Files
2026-06-28 23:33:35 +02:00

8.3 KiB
Raw Permalink Blame History

NOVA Control Fix-Plan — 26 Gates

Basis: index.html (461 Zeilen, eine Datei, Vanilla JS, offline) Ziel: Alle 26 binären Gates schließen. P0+P1 vollständig, P2 nach Spec.


Architektur-Entscheidungen

  • Eine Datei bleibt — kein Splitting in CSS/JS
  • Event-Delegation statt pro-Render-Listener (fix P1.7)
  • State-getriebenes Rendering: rv() rendert NUR den aktiven View, kein Voll-Neuaufbau
  • CSS-Variablen für Dark Mode (existiert bereits via :root, braucht [data-theme="dark"]-Block)

P0 — Blocker (3 Gates)

P0.1 — Datei-Dialog öffnet NICHT beim Laden

File: index.html Change: Zeile document.getElementById('imp').click(); löschen Verify: Seite laden → kein Datei-Dialog. Manueller Import per Command-Palette funktioniert.

P0.2 — Command-Palette mit ≥8 Befehlen

File: index.html Change:

  1. rp() erweitern auf ≥8 Commands: Board, Streaks, Insights, Neues Task, Import, Export, Reset/Demo, 500 Tasks laden
  2. ld() am Ende der Init aufrufen → rp() ausführen
  3. Tasks als anspringbare Einträge in inipal() dynamisch hinzufügen Verify: Cmd+K → Liste zeigt ≥8 Einträge. Pfeiltasten navigieren, Enter führt aus.

P0.3 — Dark Mode (realer Farbwechsel + Persistenz)

File: index.html Change:

  1. [data-theme="dark"] CSS-Block mit min --bg, --surface, --text, --border
  2. prefers-color-scheme: dark Media Query in at() korrekt verdrahtet
  3. System-Modus reagiert auf matchMedia('(prefers-color-scheme:dark)').addEventListener('change', ...)
  4. at() validieren: data-theme auf <html> korrekt gesetzt? CSS-Selektoren existent? Verify: 🌙 → Hintergrund dunkel, Text hell. Reload → bleibt. System-Umschaltung in OS-Einstellungen → reagiert live.

P1 — Kern-Features (7 Gates)

P1.1 — Drag & Drop (Maus), volle Implementierung

File: index.html Change: HTML5 DnD API:

  • draggable="true" auf .task
  • dragstart: dataTransfer.setData('text/plain', taskId)
  • dragover: e.preventDefault() + Drop-Indikator (gestrichelte Linie/Platzhalter)
  • drop: Task verschieben (Spalte + Position), st updaten, sv(), rv()
  • Intra-Spalte (Reihenfolge) + Inter-Spalte (Statuswechsel) Verify: Task mit Maus von Backlog → Aktiv ziehen. Position innerhalb Spalte ändern. Persistiert nach Reload.

P1.2 — Tastatur-DnD

File: index.html Change: Bei fokussiertem Task:

  • Ctrl+Alt+← → Spalte nach links (Status rückwärts)
  • Ctrl+Alt+→ → Spalte nach rechts (Status vorwärts)
  • Ctrl+Alt+↑/↓ → Position in Spalte ändern
  • aria-live="polite" Region für Status-Ansagen Verify: Task per Tastatur durch alle Spalten bewegen. Screenreader-kompatibel.

P1.3 — Feld-Schema-Drift behoben

File: index.html Change:

  1. op()-Create: dud (Zeile ~260 im Original-Fix-Brief-Kontext)
  2. ldDemo(): prüfen ob schon d verwendet wird → ja, aber op()-Create schreibt du
  3. Task-Render im Board: liest d → bleibt korrekt
  4. Chart: liest df für Abschlussdatum → ok
  5. dt (created) und df (finished) konsistent halten Verify: Neues Task mit Fälligkeitsdatum → nach Reload noch da. Keine du-Felder im localStorage.

P1.4 — Streak-Berechnung

File: index.html Change:

  1. Pro Habit: aktuelle Serie + längste Serie berechnen
  2. Algorithmus: lokale Kalendertage, Wert > 0 = erfüllt
  3. Lücke (Tag = 0) bricht aktuelle Serie
  4. Heute noch nicht erfüllt → bricht NICHT (gestern zählt weiter)
  5. Anzeige im Streak-View: "Aktuell: X Tage | Best: Y Tage" Verify: Serie bricht korrekt. Gestern 1, heute 0 → Serie läuft weiter. Gestern 0, heute 1 → Serie = 1.

P1.5 — Export → verlustfreier Round-Trip

File: index.html Change:

  1. exportData(): JSON-Blob mit {version: 1, tasks, habits, theme} → Download
  2. Command-Palette-Eintrag: "Export"
  3. Import validiert version-Feld + tasks als Array
  4. Round-Trip-Test: Export → Import → gleicher Zustand Verify: 40 Tasks exportieren, Seite löschen, importieren → 40 Tasks zurück, gleiche Daten.

P1.6 — Zweiter Chart + Trend-Granularität

File: index.html Change:

  1. <canvas id="chart2"> unter dem Trend-Chart
  2. Verteilungs-Chart: Donut nach Priorität (Hoch/Mittel/Tief)
  3. Trend-Chart auf pro Woche umstellen (nicht pro Monat)
  4. Datenbasis für Trend: Abschlussdatum (df)
  5. Beide Charts mit Achsen/Labels/Legende, Empty-State bei 0 Daten Verify: Insights zeigt zwei Charts. Trend zeigt Wochen-Balken. Donut zeigt Prio-Verteilung.

P1.7 — Listener-Leak + Suchfeld-Fokusverlust

File: index.html Change:

  1. rb(): KEINE innerHTML-Neusetzung mehr — nur Board-Grid updaten
  2. Toolbar (Suche, Prioritätsfilter) wird einmalig im HTML gerendert, nicht pro rb()
  3. Event-Delegation: click/keydown auf #board-section einmalig im Init binden
  4. Suche filtert per CSS-Klasse display:none statt Neuaufbau → kein Fokusverlust Verify: Tippen in Suche → Fokus bleibt. 10× zwischen Views wechseln → Klick öffnet Modal genau 1×.

P2 — Spec-Konformität & Politur (10 Gates)

P2.1 — Heatmap: 26-Wochen-Grid

File: index.html Change: rs() → Heatmap als Wochen-Grid (Spalten=Wochen, Zeilen=Mo..So), 26 Wochen zurück. Hover-Tooltip: <div class="heatmap-tip"> mit Datum + Wert. Verify: Kein 28-Tage-Streifen mehr. Wochen-Grid sichtbar. Tooltip zeigt korrektes Datum.

P2.2 — Responsive / kein Horizontal-Scroll

File: index.html Change: Media Queries:

  • @media (max-width: 768px): Layout einspaltig, Sidebar kollabiert
  • @media (max-width: 480px): Board-Spalten untereinander
  • Board bei schmalen Screens: overflow-x: auto NUR auf .board, nicht body Verify: 360px Breite → kein horizontaler Body-Scroll. Board scrollt innerhalb.

P2.3 — prefers-reduced-motion

File: index.html Change:

@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  .task { transition: none; }
}

Verify: OS-Einstellung "Reduzierte Bewegung" → keine Animationen. Toast erscheint sofort.

P2.4 — Focus-Trap (Modal + Palette)

File: index.html Change:

  1. Modal: Fokusbare Elemente sammeln, Tab/Shift+Tab zyklisch
  2. Palette: dito
  3. ESC schließt + Fokus zurück auf Auslöser
  4. Palette: clp() setzt Fokus zurück (aktuell nicht) Verify: Modal offen → Tab zyklisch. ESC → Fokus auf vorherigem Element.

P2.5 — Palette-Tastatur korrekt

File: index.html Change: Keydown-Handler auf #pal-q (Eingabefeld) statt #pal-list:

  • ↑/↓ navigieren Liste (Index-Update + visuelles Highlight)
  • Enter führt markierten Befehl aus
  • Fokus bleibt im Eingabefeld Verify: Pfeiltasten navigieren bei Fokus im Suchfeld. Enter führt aus.

P2.6 — Hash-Routing

File: index.html Change:

  1. Navigation setzt location.hash = '#/' + view
  2. window.addEventListener('hashchange', ...) schaltet View
  3. Beim Laden: if (location.hash) → View aus Hash setzen
  4. Default: #/board Verify: URL #/insights → Insights-View. Reload → bleibt. Browser-Zurück funktioniert.

P2.7 — Stress-Aktion "500 Tasks laden"

File: index.html Change:

  1. gen500(): 500 Tasks generieren (Batch-Insert, kein 500× push in Render-Loop)
  2. Command-Palette-Eintrag "500 Tasks laden"
  3. Performance: requestAnimationFrame für Batch-Rendering, keine 500 Listener Verify: 500 Tasks → Board flüssig, Suche filtert in Echtzeit, Drag&Drop ruckelfrei.

P2.8 — Robustheit

File: index.html Change:

  1. ld(): nv_t validieren → Array.isArray()? JSON.parse-Fehler? → Fallback []
  2. Import: Jeden Task auf id, tl, st prüfen → fehlerhafte Einträge → Fehlermeldung ohne Crash
  3. Kein Datenverlust am bestehenden Zustand bei fehlerhaftem Import Verify: Kaputtes JSON importieren → klare Fehlermeldung, bestehende Tasks unverändert. nv_t = "nicht ein array" → App startet mit Seed-Daten.

Phasen-Plan

Phase Gates Aufwand
Phase 1: Blocker P0.1P0.3 ~15 min
Phase 2: Kern P1.1P1.7 ~90 min
Phase 3: Politur P2.1P2.8 ~45 min
Phase 4: Gate-Check Alle 26 ~15 min

Gesamt: ~2.5h, 26 binäre Gates.


Nach Freigabe: Implementierung in 4 Phasen, nach jeder Phase Commit + Gate-Check.