From 7a52277b84b416bf33019dc7a1e890d1f431323c Mon Sep 17 00:00:00 2001 From: mhoennig Date: Mon, 27 Jul 2026 15:18:20 +0200 Subject: [PATCH] =?UTF-8?q?frontend:=20=E2=80=9EWas=20ist=20neu=3F"=20?= =?UTF-8?q?=E2=80=94=20neu=20in=20Produktion=20mit=20gelbem=20Strahlenkran?= =?UTF-8?q?z?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dokumente von außen (mitgeliefert D27, ?sourceUrl= D23) ändern sich, ohne dass der Betrachter es merkt. Sie zeigen jetzt, was sich seit seinem letzten Besuch getan hat. - „Neu" heißt bewusst NICHT „Zeile hinzugefügt", sondern **neu in Produktion**: ein Knoten trägt jetzt [^] und tat es in der zuletzt gesehenen Fassung nicht. Ein Zeilendiff meldete vor allem Rauschen; die Nachricht, die zählt, ist was live gegangen ist. Nebeneffekt: es leuchtet eine Handvoll Knoten, nicht dreißig. - Basis ist die zuletzt GESEHENE Fassung je Dokument (`werkbaum-seen`), nicht die letzte Auslieferung — wer Fassungen überspringt, sieht alles seither. Fortgeschrieben wird erst beim Bestätigen, sonst wäre die Meldung nach einem Neuladen weg, bevor sie jemand bemerkt. Beim Erstkontakt leuchtet nichts. - Knoten-Identität ist der Label-Pfad, nicht die Zeilennummer: Umeinrücken und Umsortieren erzeugen keine Falschmeldungen; gleichnamige Geschwister per Index; Umbenennen gilt als neuer Knoten (der Text ist der Vertrag, D14). - Darstellung: gelber Strahlenkranz nach außen, kein Blinken (WCAG 2.2.2/2.3.1; Blinken zöge dauerhaft den Blick statt einmal zu melden). Außen, weil die Füllung dem Status gehört (SPEC §4). Zusammen mit der Cursor-Zeile: Tinte innen, Gelb außen. Nicht im Druck und nicht im Grafikexport — die Markierung hängt am persönlichen Besuchsstand. - Knopf im Diagramm-Kopf nur, wenn es etwas gibt; nennt die Anzahl, Klick bestätigt. Kein Dauer-Umschalter. - Bei selbst bearbeitetem mitgeliefertem Dokument wird nichts hervorgehoben — dort fehlt die saubere Vergleichsbasis (D27). Fehler beim Bauen, der zuerst durchrutschte: Die Menge wurde beim Laden aus einem eigenen Parse-Durchlauf berechnet. Der Zähler stimmte, aber kein Knoten leuchtete — `Set.has()` prüft Objektidentität, und die gerenderten Knoten kamen aus einem anderen Parse. `render()` bildet die Menge jetzt bei jedem Durchlauf aus den gerade geparsten Wurzeln; vorgehalten wird nur der geparste Basisbaum. Verifiziert: 9 neue Modelltests (Statuswechsel, neuer [^]-Knoten, nicht-[^] ignoriert, unverändert ignoriert, Erstkontakt leer, Umsortieren/Tabs ohne Falschmeldung, gleiches Label unter verschiedenen Eltern, gleichnamige Geschwister, verworfene). Im Browser: Erstkontakt setzt nur die Basis (Knopf versteckt); zurückgedrehte Basis lässt genau die erwarteten Knoten leuchten (Zähler 3, dann 1); Bestätigen räumt auf und überlebt das Neuladen; ohne Bestätigen bleibt die Meldung über ein Neuladen stehen. Vitest 46/46. Co-Authored-By: Claude Opus 4.8 --- docs/DECISIONS.md | 52 ++++++++++++++++++++++ docs/SPEC.md | 11 +++++ frontend/CLAUDE.md | 8 ++++ frontend/index.html | 4 ++ frontend/src/app.js | 85 ++++++++++++++++++++++++++++++++++-- frontend/src/model.js | 41 +++++++++++++++++ frontend/src/render.js | 13 +++++- frontend/src/style.css | 17 ++++++++ frontend/tests/fresh.test.js | 67 ++++++++++++++++++++++++++++ 9 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 frontend/tests/fresh.test.js diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index f2a4794..22dae68 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -665,3 +665,55 @@ Das fiel sofort auf, als der Plan um den Abschnitt „gemeinsam arbeiten" Der Altwert `'1'` aus der ersten Fassung sagt nichts über den Textstand; dort wird bewusst nichts überschrieben, nur der Merker ersetzt. Wer aus dieser kurzen Zwischenfassung kommt, holt den aktuellen Stand über den Reset. + +## D28 — „Was ist neu?": neu in Produktion, gelber Strahlenkranz +Dokumente von außen (mitgeliefert, D27; per `?sourceUrl=`, D23) ändern sich, +ohne dass der Betrachter es merkt. Sie zeigen deshalb, was sich seit seinem +letzten Besuch getan hat. + +**„Neu" heißt: neu in Produktion.** Nicht „Zeile hinzugefügt". Ein Zeilendiff +meldet vor allem Rauschen — jede neu notierte Idee, jede Umformulierung. Die +Nachricht, die einen Plan-Leser wirklich angeht, ist: *was ist tatsächlich +live gegangen*. Also gilt ein Knoten als neu, wenn er **jetzt `[^]`** trägt und +es in der zuletzt gesehenen Fassung **nicht** tat (weil er anders stand oder +noch fehlte). Das macht die Hervorhebung zugleich sparsam: In einem großen Plan +leuchten typischerweise eine Handvoll Knoten, nicht dreißig. + +**Basis ist die zuletzt GESEHENE Fassung, nicht die letzte Auslieferung.** +`werkbaum-seen` hält je Dokument-id den Text, den der Betrachter zuletzt +bestätigt hat. Wer drei Fassungen übersprungen hat, sieht alles seither. Die +Basis wird **erst beim Bestätigen** fortgeschrieben — schriebe man sie beim +Laden fort, wäre die Meldung nach einem Neuladen verschwunden, bevor sie jemand +bemerkt hat. Beim **Erstkontakt** leuchtet nichts (sonst strahlte beim ersten +Ansehen der gesamte fertige Teil des Plans auf); es wird nur die Basis gesetzt. + +**Knoten-Identität ist der Label-Pfad**, nicht die Zeilennummer: Umeinrücken und +Umsortieren erzeugen so keine Falschmeldungen. Gleichnamige Geschwister werden +über einen Index unterschieden; ein umbenanntes Label gilt als neuer Knoten — +gewollt, der Text ist der Vertrag (D14). + +**Darstellung: gelber Strahlenkranz nach außen.** Bewusst **kein Blinken** (vom +Nutzer erwogen): WCAG 2.2.2 verlangt, dass blinkende Inhalte abschaltbar sind, +2.3.1 begrenzt Flackern wegen des Anfallsrisikos — und vor allem zöge Blinken +*dauerhaft* den Blick, statt einmal zu melden. Der Schein liegt **außen**, weil +die Knotenfüllung dem Status gehört (SPEC §4) und lesbar bleiben muss. Ein +Knoten, der zugleich die Cursor-Zeile ist (D25), bekommt beides: Tinte innen, +Gelb außen. Im **Druck und im Grafikexport erscheint der Kranz nicht** — er +hängt an *deinem* letzten Besuch, ein Export damit hieße für jeden Betrachter +etwas anderes. + +Ein **Knopf im Diagramm-Kopf** erscheint nur, wenn es etwas gibt, nennt die +Anzahl und bestätigt per Klick („gesehen"). Kein Dauer-Umschalter: Die Meldung +soll verschwinden, wenn sie ihren Zweck erfüllt hat. + +**Zurückhaltung bei bearbeitetem Text.** Hat der Nutzer das mitgelieferte +Dokument geändert, wird es nicht mehr nachgezogen (D27) — dann gibt es keine +saubere Vergleichsbasis, und es wird nichts hervorgehoben. + +**Stolperfalle (beim Bauen hineingelaufen):** Die Menge der neuen Knoten muss aus +**denselben Knotenobjekten** gebildet werden, die gerade gerendert werden. Zuerst +wurde sie beim Laden aus einem eigenen Parse-Durchlauf berechnet — der Zähler +stimmte, aber kein einziger Knoten leuchtete, weil `Set.has()` auf +Objektidentität prüft und die gerenderten Knoten aus einem anderen Parse kamen. +`render()` bildet die Menge daher bei jedem Durchlauf neu; vorgehalten wird nur +der **geparste Basisbaum**. diff --git a/docs/SPEC.md b/docs/SPEC.md index 5105a0c..799b0fb 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -235,6 +235,17 @@ Doppelklick stellt die Vorgabe wieder her; die Aufteilung bleibt für beide Ausrichtungen getrennt erhalten. Die Legende belegt höchstens 85 % des Panels, damit das Textfeld nie ganz verschwindet. Siehe D26. +### Was ist neu? (Dokumente von außen) +Bei Dokumenten, die von außen kommen (mitgeliefert oder per `?sourceUrl=`), wird +gezeigt, was sich seit dem letzten Besuch getan hat. **„Neu" heißt: neu in +Produktion** — ein Knoten trägt jetzt `[^]` und tat es in der zuletzt gesehenen +Fassung nicht. Solche Knoten bekommen einen **gelben Strahlenkranz** nach außen +(die Füllung bleibt die Statusfarbe aus §4). Ein Knopf im Diagramm-Kopf nennt +die Anzahl und bestätigt per Klick; danach ist die aktuelle Fassung die neue +Vergleichsbasis. Beim ersten Ansehen eines Dokuments leuchtet nichts. Der Kranz +erscheint weder im Druck noch im Grafikexport — er hängt am persönlichen +Besuchsstand. Siehe D28. + ### Sprung zwischen Diagramm und Text Jeder Knoten kennt seine Zeilennummer im Notationstext; beide Richtungen sind verknüpft (siehe D25): diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index 6b7b339..164cb04 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -174,6 +174,14 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der `werkbaum-ui` gesichert. Die 85-%-Obergrenze steht **zusätzlich** als `max-width`/`max-height` im CSS — die gespeicherte px-Größe würde den Editor sonst erdrücken, wenn das Panel später schrumpft. +- „Was ist neu?" (D28): `freshProdSet(prevRoots, currRoots)` in `model.js` liefert + die Knoten, die **neu `[^]`** sind (Identität = Label-Pfad, nicht Zeile). + `render()` bildet die Menge **bei jedem Durchlauf neu** aus den gerade + geparsten `roots` — eine vorab berechnete Menge stammte aus einem anderen + Parse-Durchlauf und träfe per Objektidentität nie zu (genau dieser Fehler ist + passiert: Zähler stimmte, nichts leuchtete). Vorgehalten wird nur + `freshPrevRoots` (Basis, einmal geparst). Basis je Dokument in `werkbaum-seen`, + fortgeschrieben **erst beim Bestätigen** über `#freshBtn`. - Kleiner Bildschirm: `body.mobile` (per `matchMedia`, ≤ 640 px) stapelt Diagramm/Editor mit **stufenlosem** Splitter (kein Snap/Collapse wie auf Desktop): der Gutter-Drag ruft `setMobileDrow()` (klemmt `--drow` zwischen den diff --git a/frontend/index.html b/frontend/index.html index 741a626..bd81fbd 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -112,6 +112,10 @@ +