fix: eingeklappter Knoten vertritt seinen Teilbaum auf dem günstigsten Pfad
D38 hielt fest, ein eingeklappter Zweig dürfe Pfadknoten verbergen, „die Inversion an den sichtbaren Knoten bleibt richtig". Zu kurz gedacht: Die LINIE blieb eben nicht richtig. Sie fädelt durch die cheap-leaf-Knoten im DOM; ein eingeklappter Pfadknoten ist zwar `cheap`, aber kein Blatt (seine Pfad-Kinder existieren im Modell) — also bekam er keine Station, und die Linie umging den ganzen Zweig. Im Beispiel nachgemessen: „Concept" war `node cheap folded`, verbarg zwei Pfadknoten, trug keinen Punkt; die Linie begann erst bei „PWA". Das Bild behauptete damit, in diesem Zweig sei nichts zu tun — eine Aussage über den Plan, nicht über die Ansicht. Genau die Grenze, die D38 sonst zieht. Regel: Eingeklappt überspringt cheapCls() die Blatt-Prüfung und fragt den Teilbaum (hidesCheap). Liegt darin etwas auf dem Pfad, ist der Knoten dessen tiefste noch sichtbare Station. Beim Aufklappen geben die Kinder sie zurück — gemessen 6 -> 7 -> 6 Stationen beim Hin- und Herklappen. Auch ohne eigene Pfad-Mitgliedschaft: Ein per :#… gezogenes Ziel kann unter einem +-Knoten liegen, der selbst nicht gebraucht wird (D42). Eingeklappt ist dieser der einzige sichtbare Griff auf nötige Arbeit — er wird Station und zählt als cheap, tritt also nicht zurück. Ihn auszublassen wäre die Lüge. 4 neue Tests (208 gesamt). SPEC §9, D38 (Nachtrag) und frontend/CLAUDE.md nachgezogen; die alte „hinnehmbar"-Stelle in D38 ist als falsch markiert statt gelöscht. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6463a76e1e
commit
38104a4c5d
+19
-1
@@ -168,7 +168,25 @@ export function computeCheapSet(roots){
|
||||
}
|
||||
/* CSS-Klassen für den günstigen Pfad. Leere `cheapSet` (Pfad aus) ⇒ ''.
|
||||
Endknoten (kein Kind liegt auf dem Pfad) bekommt zusätzlich 'cheap-leaf'. */
|
||||
export function cheapCls(n, cheapSet){
|
||||
/* Liegt im Teilbaum eines Knotens etwas auf dem Pfad? Gebraucht für
|
||||
eingeklappte Knoten (siehe `cheapCls`). Verworfene sind nie in `cheapSet`
|
||||
(SPEC §9), es braucht also keine eigene Filterung. */
|
||||
export function hidesCheap(n, cheapSet){
|
||||
for(const k of n.children || []){
|
||||
if(cheapSet.has(k) || hidesCheap(k, cheapSet)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function cheapCls(n, cheapSet, collapsed){
|
||||
/* Eingeklappt steht der Knoten stellvertretend für seinen ganzen Teilbaum
|
||||
(SPEC §9/D38): Liegt darin etwas auf dem Pfad, ist er dessen tiefste noch
|
||||
SICHTBARE Station — sonst überspränge die Linie den Zweig, als wäre dort
|
||||
nichts zu tun. Das gilt auch, wenn er selbst nicht gebraucht wird, sein
|
||||
Teilbaum aber schon (eine per `:#…` gezogene Alternative, D42): Er ist
|
||||
dann der einzige sichtbare Griff auf nötige Arbeit und darf deshalb auch
|
||||
nicht von der Inversion ausgeblasst werden. */
|
||||
if(collapsed) return (cheapSet.has(n) || hidesCheap(n, cheapSet)) ? 'cheap cheap-leaf' : '';
|
||||
if(!cheapSet.has(n)) return '';
|
||||
const leaf = !pathChildren(n).some(k => cheapSet.has(k));
|
||||
return leaf ? 'cheap cheap-leaf' : 'cheap';
|
||||
|
||||
@@ -21,7 +21,12 @@ import { gateOf, needsBreakdown, visibleChildren, cheapCls } from './model.js';
|
||||
gegenüber der zuletzt gesehenen Fassung (D28, `freshSet` optional) und
|
||||
optionale Knoten (`+`, SPEC §3/D29 — trägt den hohlen Kreis am Abzweig). */
|
||||
function extraCls(n, opts){
|
||||
const cheap = cheapCls(n, opts.cheapSet);
|
||||
/* Dieselbe Bedingung wie in `itemHtml`: eingeklappt ist ein Knoten nur, wenn
|
||||
er überhaupt sichtbare Kinder hat. Der Pfad braucht sie hier, weil ein
|
||||
eingeklappter Knoten seine verborgenen Pfad-Knoten vertritt (D38). */
|
||||
const collapsed = !!(opts.collapsedSet && opts.collapsedSet.has(n))
|
||||
&& visibleChildren(n, opts.showDiscarded).length > 0;
|
||||
const cheap = cheapCls(n, opts.cheapSet, collapsed);
|
||||
const fresh = opts.freshSet && opts.freshSet.has(n) ? 'fresh' : '';
|
||||
return [cheap, fresh, n.optional ? 'opt' : '', n.focus ? 'focusmark' : ''].filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user