8.3 KiB
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:
rp()erweitern auf ≥8 Commands: Board, Streaks, Insights, Neues Task, Import, Export, Reset/Demo, 500 Tasks ladenld()am Ende der Init aufrufen →rp()ausführen- 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:
[data-theme="dark"]CSS-Block mit min--bg,--surface,--text,--borderprefers-color-scheme: darkMedia Query inat()korrekt verdrahtet- System-Modus reagiert auf
matchMedia('(prefers-color-scheme:dark)').addEventListener('change', ...) at()validieren:data-themeauf<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.taskdragstart:dataTransfer.setData('text/plain', taskId)dragover:e.preventDefault()+ Drop-Indikator (gestrichelte Linie/Platzhalter)drop: Task verschieben (Spalte + Position),stupdaten,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:
op()-Create:du→d(Zeile ~260 im Original-Fix-Brief-Kontext)ldDemo(): prüfen ob schondverwendet wird → ja, aberop()-Create schreibtdu- Task-Render im Board: liest
d→ bleibt korrekt - Chart: liest
dffür Abschlussdatum → ok dt(created) unddf(finished) konsistent halten Verify: Neues Task mit Fälligkeitsdatum → nach Reload noch da. Keinedu-Felder im localStorage.
P1.4 — Streak-Berechnung
File: index.html
Change:
- Pro Habit: aktuelle Serie + längste Serie berechnen
- Algorithmus: lokale Kalendertage, Wert > 0 = erfüllt
- Lücke (Tag = 0) bricht aktuelle Serie
- Heute noch nicht erfüllt → bricht NICHT (gestern zählt weiter)
- 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:
exportData(): JSON-Blob mit{version: 1, tasks, habits, theme}→ Download- Command-Palette-Eintrag: "Export"
- Import validiert
version-Feld +tasksals Array - 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:
<canvas id="chart2">unter dem Trend-Chart- Verteilungs-Chart: Donut nach Priorität (Hoch/Mittel/Tief)
- Trend-Chart auf pro Woche umstellen (nicht pro Monat)
- Datenbasis für Trend: Abschlussdatum (
df) - 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:
rb(): KEINEinnerHTML-Neusetzung mehr — nur Board-Grid updaten- Toolbar (Suche, Prioritätsfilter) wird einmalig im HTML gerendert, nicht pro
rb() - Event-Delegation:
click/keydownauf#board-sectioneinmalig im Init binden - Suche filtert per CSS-Klasse
display:nonestatt 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: autoNUR auf.board, nichtbodyVerify: 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:
- Modal: Fokusbare Elemente sammeln, Tab/Shift+Tab zyklisch
- Palette: dito
- ESC schließt + Fokus zurück auf Auslöser
- 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:
- Navigation setzt
location.hash = '#/' + view window.addEventListener('hashchange', ...)schaltet View- Beim Laden:
if (location.hash)→ View aus Hash setzen - Default:
#/boardVerify: URL#/insights→ Insights-View. Reload → bleibt. Browser-Zurück funktioniert.
P2.7 — Stress-Aktion "500 Tasks laden"
File: index.html
Change:
gen500(): 500 Tasks generieren (Batch-Insert, kein 500×pushin Render-Loop)- Command-Palette-Eintrag "500 Tasks laden"
- Performance:
requestAnimationFramefü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:
ld():nv_tvalidieren →Array.isArray()? JSON.parse-Fehler? → Fallback[]- Import: Jeden Task auf
id,tl,stprüfen → fehlerhafte Einträge → Fehlermeldung ohne Crash - 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.1–P0.3 | ~15 min |
| Phase 2: Kern | P1.1–P1.7 | ~90 min |
| Phase 3: Politur | P2.1–P2.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.