frontend: „Was ist neu?" — neu in Produktion mit gelbem Strahlenkranz
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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
68abdc6625
commit
7a52277b84
@@ -68,3 +68,44 @@ export function cheapCls(n, cheapSet){
|
||||
const leaf = !pathChildren(n).some(k => cheapSet.has(k));
|
||||
return leaf ? 'cheap cheap-leaf' : 'cheap';
|
||||
}
|
||||
|
||||
/* ---------- Was ist neu? (D28) ----------
|
||||
„Neu" heißt hier bewusst nicht „Zeile hinzugefügt", sondern **neu in
|
||||
Produktion**: ein Knoten, der jetzt `[^]` trägt und es in der zuletzt
|
||||
gesehenen Fassung noch nicht tat (weil er anders stand oder fehlte). Das ist
|
||||
die Änderungsmeldung, die einen Plan-Leser wirklich interessiert.
|
||||
|
||||
Knoten-Identität ist der **Pfad der Labels** von der Wurzel, nicht die
|
||||
Zeilennummer: Umeinrücken oder Umsortieren erzeugt so keine Falschmeldungen.
|
||||
Gleichnamige Geschwister werden über einen Index unterschieden. Ein
|
||||
umbenanntes Label gilt als neuer Knoten — gewollt, der Text ist der Vertrag
|
||||
(D14). */
|
||||
function walkKeys(nodes, parentKey, fn){
|
||||
const seen = new Map();
|
||||
for(const n of nodes){
|
||||
const base = parentKey + ' > ' + n.label;
|
||||
const i = seen.get(base) || 0;
|
||||
seen.set(base, i + 1);
|
||||
const key = i ? base + '#' + i : base;
|
||||
fn(key, n);
|
||||
walkKeys(n.children, key, fn);
|
||||
}
|
||||
}
|
||||
/* key -> Status-Schlüssel ('' für neutrale Knoten) über den ganzen Baum. */
|
||||
export function statusByKey(roots){
|
||||
const map = new Map();
|
||||
walkKeys(roots, '', (key, n) => map.set(key, n.status ? n.status.key : ''));
|
||||
return map;
|
||||
}
|
||||
/* Menge der Knoten, die gegenüber `prevRoots` NEU in Produktion sind.
|
||||
`prevRoots == null` (noch keine Vergleichsfassung) ⇒ leere Menge — sonst
|
||||
leuchtete beim ersten Ansehen der ganze fertige Teil des Plans auf. */
|
||||
export function freshProdSet(prevRoots, currRoots){
|
||||
const set = new Set();
|
||||
if(!prevRoots) return set;
|
||||
const before = statusByKey(prevRoots);
|
||||
walkKeys(currRoots, '', (key, n) => {
|
||||
if(n.status && n.status.key === 'prod' && before.get(key) !== 'prod') set.add(n);
|
||||
});
|
||||
return set;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user