docs: Alicia-Arbeitsanweisung (26 Gates Schritt-für-Schritt)
This commit is contained in:
@@ -0,0 +1,165 @@
|
|||||||
|
# ALICIA.md — NOVA Control Reparatur
|
||||||
|
|
||||||
|
**Auftrag:** Behebe 26 binäre Gates in `index.html`. Jedes Gate ist ein klares Ja/Nein-Kriterium — erst erfüllt, wenn nachweisbar wahr.
|
||||||
|
|
||||||
|
**Repo:** `git@git.datenhimmel.work:danielkrause/nova-control.git`
|
||||||
|
**Branch:** `master`
|
||||||
|
**Datei:** Nur `index.html` — eine Datei, kein Framework, keine Libs, kein CDN, offline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Arbeitsweise
|
||||||
|
|
||||||
|
1. **Gate für Gate.** Nicht mehrere auf einmal. Ein Gate → implementieren → testen → commit.
|
||||||
|
2. **Vor jedem Commit:** `index.html` im Browser öffnen und das Gate manuell verifizieren.
|
||||||
|
3. **Commit-Message:** `fix: P0.1 Datei-Dialog entfernt` o.ä.
|
||||||
|
4. **Push nach jeder Phase** (P0, P1, P2) — nicht nach jedem einzelnen Gate.
|
||||||
|
|
||||||
|
## Reihenfolge (P0 → P1 → P2)
|
||||||
|
|
||||||
|
### P0 — BLOCKER (zuerst, weil App sonst beim Start kaputt)
|
||||||
|
|
||||||
|
**P0.1 — Datei-Dialog**
|
||||||
|
- Zeile mit `document.getElementById('imp').click();` **löschen**
|
||||||
|
- Test: Seite laden → KEIN Datei-Dialog
|
||||||
|
|
||||||
|
**P0.2 — Command-Palette**
|
||||||
|
- `rp()` wird nie aufgerufen → `cmds` bleibt `[]`
|
||||||
|
- `rp()` auf ≥8 Befehle erweitern: Board, Streaks, Insights, Neues Task, Import, Export, Reset/Demo, 500 Tasks
|
||||||
|
- `ld()` ruft am Ende `rp()` auf, dann `inipal()` beim Öffnen der Palette
|
||||||
|
- Tasks dynamisch als anspringbare Einträge in die Liste
|
||||||
|
- Test: Cmd+K → Liste zeigt ≥8 Einträge
|
||||||
|
|
||||||
|
**P0.3 — Dark Mode**
|
||||||
|
- `:root` hat nur Hell-Werte, `[data-theme="dark"]`-Block fehlt
|
||||||
|
- CSS: `[data-theme="dark"]` mit `--bg`, `--surface`, `--text`, `--border`
|
||||||
|
- `at()`: `matchMedia('(prefers-color-scheme:dark)').addEventListener('change', ...)` für Live-Reaktion
|
||||||
|
- `localStorage` für Persistenz (existiert via `sv()`)
|
||||||
|
- Test: 🌙 klicken → Hintergrund dunkel. Reload → bleibt dunkel.
|
||||||
|
|
||||||
|
### P1 — KERN-FEATURES
|
||||||
|
|
||||||
|
**P1.1 — Maus-Drag & Drop**
|
||||||
|
- HTML5 DnD API: `draggable="true"` auf `.task`
|
||||||
|
- `dragstart`/`dragover`/`drop` Events
|
||||||
|
- Drop-Indikator (gestrichelter Rahmen oder Platzhalter)
|
||||||
|
- Inter-Spalte (Status wechselt) + Intra-Spalte (Reihenfolge)
|
||||||
|
- Nach Drop: `sv()` + `rv()`
|
||||||
|
- Test: Task von Backlog nach Aktiv ziehen. Nach Reload noch da.
|
||||||
|
|
||||||
|
**P1.2 — Tastatur-DnD**
|
||||||
|
- Fokussiertes Task: Ctrl+Alt+Pfeiltasten
|
||||||
|
- ←→: Spalte wechseln | ↑↓: Position in Spalte
|
||||||
|
- `<div aria-live="polite">` für Status-Ansagen
|
||||||
|
- Test: Nur Tastatur, Task durch alle 4 Spalten bewegen.
|
||||||
|
|
||||||
|
**P1.3 — Feld-Schema**
|
||||||
|
- `op()`-Create speichert Fälligkeit als `du`, Board liest `d`
|
||||||
|
- `du` → `d` vereinheitlichen (in Create + Edit)
|
||||||
|
- `ldDemo()` und Render prüfen: benutzen die `d`?
|
||||||
|
- Test: Task mit Datum anlegen → Reload → Datum noch da.
|
||||||
|
|
||||||
|
**P1.4 — Streak-Berechnung**
|
||||||
|
- Pro Habit: aktuelle Serie + längste Serie
|
||||||
|
- Regeln: Wert > 0 = erfüllt. Tag mit 0 = Bruch. Heute noch nicht erfüllt = kein Bruch.
|
||||||
|
- Anzeige im Streak-View
|
||||||
|
- Test: Serie bricht bei 0, läuft weiter wenn heute noch kein Wert.
|
||||||
|
|
||||||
|
**P1.5 — Export**
|
||||||
|
- `exportData()`: `{version:1, tasks, habits, theme}` als JSON-Blob → Download
|
||||||
|
- Command "Export" in Palette
|
||||||
|
- Import validiert `version` + `tasks` Array
|
||||||
|
- Test: Export → localStorage löschen → Import → identischer Zustand.
|
||||||
|
|
||||||
|
**P1.6 — Zweiter Chart + Trend-Wochen**
|
||||||
|
- `<canvas id="chart2">` für Donut (Prioritätsverteilung)
|
||||||
|
- Trend-Chart: Monate → Wochen umstellen
|
||||||
|
- Datenbasis Trend: `df` (Abschlussdatum)
|
||||||
|
- Legende, Labels, Empty-State
|
||||||
|
- Test: Insights zeigt 2 Charts. Trend in Wochen.
|
||||||
|
|
||||||
|
**P1.7 — Listener-Leak + Fokusverlust**
|
||||||
|
- `rb()`: KEIN `innerHTML` = Toolbar + alles neu
|
||||||
|
- Toolbar (Suche + Filter) EINMAL im HTML, nicht pro `rb()`
|
||||||
|
- Event-Delegation auf `#board-section` EINMAL binden
|
||||||
|
- Suche: `display:none` statt DOM-Neuaufbau
|
||||||
|
- Test: 10× Views wechseln → Modal öffnet 1×. Tippen in Suche → Fokus bleibt.
|
||||||
|
|
||||||
|
### P2 — POLITUR
|
||||||
|
|
||||||
|
**P2.1 — Heatmap 26 Wochen**
|
||||||
|
- Spalten=Wochen, Zeilen=Mo..So, 26 Wochen zurück
|
||||||
|
- Hover-Tooltip mit Datum + Wert
|
||||||
|
- Test: Kein 28-Tage-Streifen mehr, Wochen-Grid sichtbar.
|
||||||
|
|
||||||
|
**P2.2 — Responsive**
|
||||||
|
- `@media (max-width: 768px)`: Layout einspaltig
|
||||||
|
- `@media (max-width: 480px)`: Board untereinander
|
||||||
|
- `overflow-x: auto` NUR auf `.board`, nicht `body`
|
||||||
|
- Test: 360px Breite → kein Body-Scroll.
|
||||||
|
|
||||||
|
**P2.3 — prefers-reduced-motion**
|
||||||
|
- `@media (prefers-reduced-motion: reduce) { ... }`
|
||||||
|
- Animationen deaktivieren (Toast, Drag-Transitions)
|
||||||
|
- Test: OS-Einstellung "Reduzierte Bewegung" → keine Animationen.
|
||||||
|
|
||||||
|
**P2.4 — Focus-Trap**
|
||||||
|
- Modal + Palette: Tab/Shift+Tab zyklisch im Dialog
|
||||||
|
- ESC schließt + Fokus zurück auf Auslöser
|
||||||
|
- Test: Modal offen → Tab kreist. ESC → Fokus auf Button davor.
|
||||||
|
|
||||||
|
**P2.5 — Palette-Tastatur**
|
||||||
|
- Keydown auf `#pal-q` (Eingabefeld) statt `#pal-list`
|
||||||
|
- ↑/↓ navigieren Liste, Enter führt aus
|
||||||
|
- Fokus bleibt im Eingabefeld
|
||||||
|
- Test: Pfeiltasten bei Fokus im Suchfeld.
|
||||||
|
|
||||||
|
**P2.6 — Hash-Routing**
|
||||||
|
- Navigation setzt `location.hash = '#/' + view`
|
||||||
|
- `hashchange`-Event schaltet View
|
||||||
|
- Beim Laden: View aus Hash setzen
|
||||||
|
- Test: `#/insights` → Insights-View. Reload bleibt. Zurück-Button.
|
||||||
|
|
||||||
|
**P2.7 — 500 Tasks**
|
||||||
|
- `gen500()`: 500 Tasks generieren
|
||||||
|
- Command in Palette
|
||||||
|
- Performance: Batch-Rendering, kein 500× Einzel-Push
|
||||||
|
- Test: 500 Tasks → Board flüssig, Suche filtert sofort.
|
||||||
|
|
||||||
|
**P2.8 — Robustheit**
|
||||||
|
- `ld()`: `nv_t` validieren → `Array.isArray()` + try/catch → Fallback `[]`
|
||||||
|
- Import: Jeden Task auf `id`, `tl`, `st` prüfen
|
||||||
|
- Fehlerhafte Einträge → Meldung, bestehende Daten bleiben
|
||||||
|
- Test: Kaputtes JSON importieren → Fehlermeldung, keine Daten verloren.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git-Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@git.datenhimmel.work:danielkrause/nova-control.git
|
||||||
|
cd nova-control
|
||||||
|
|
||||||
|
# Gate bearbeiten...
|
||||||
|
git add index.html
|
||||||
|
git commit -m "fix: P0.1 Datei-Dialog entfernt"
|
||||||
|
|
||||||
|
# Nach jeder Phase:
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Nach Abschluss:
|
||||||
|
git tag v1.0 # alle 26 Gates geschlossen
|
||||||
|
git push --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Constraints (unveränderlich)
|
||||||
|
|
||||||
|
- ❌ Keine zweite Datei (kein .css, kein .js)
|
||||||
|
- ❌ Keine externen Libs/CDNs (kein jQuery, kein React, kein Bootstrap)
|
||||||
|
- ❌ Kein Build-Step, kein npm
|
||||||
|
- ✅ CSS `<style>` im `<head>`
|
||||||
|
- ✅ JS `<script>` am Ende von `<body>`
|
||||||
|
- ✅ `localStorage` für Persistenz
|
||||||
|
- ✅ Eine HTML-Datei, die im Browser per `file://` läuft
|
||||||
Reference in New Issue
Block a user