frontend: optionale Abzweige gestrichelt + Stiel trifft die Knotenmitte
Zwei Nachbesserungen aus dem ersten Blick auf `+` (D29). **Gestrichelter Abzweig.** Der hohle Kreis allein war zu leise. Der Einwand gegen einen dritten Linienstil bleibt richtig, greift aber nicht so weit wie gedacht: Gestrichelt wird NUR der Abzweig zum Knoten (nicht die Sammelleiste — die gehört der ganzen Gruppe) und zwar in TINTE, während die any-of-Linien gestrichelt in Grau sind. Entscheidend ist, dass beide sich nie am selben Verteiler treffen können: | darf nach SPEC §3 nicht mit -/+ gemischt werden. Der Kreis bleibt zusätzlich — er sagt, WELCHER Knoten gemeint ist. Umgesetzt an den vorhandenen Pseudoelementen, ohne neue Zeichenebene: im Fächer der senkrechte Stiel (border-left von ::after, beim letzten Kind border-right von ::before), gestapelt der waagerechte Ast (border-top von ::before); die jeweils andere Kante ist die Leiste und bleibt durchgezogen. Dafür braucht auch das <li> die Klasse `opt` — den Abzweig zeichnet es, nicht der Knoten. Der SVG-Export zieht mit (dash || isOpt). **Stiel trifft die Knotenmitte auch waagerecht (`--stem-x`).** Dabei fiel ein älterer Fehler auf, gemeldet als „die Linie zu Wahl trifft den Knoten nicht": Im horizontalen Fächer lief der Stiel zu einem Knoten mit any-of-Kindern neben dem Knoten vorbei (gemessen 13,4 px). Dieselbe Verwechslung, die D10 senkrecht schon behoben hat, nur in der anderen Achse — der Stiel saß bei 50 % der ZELLE, und das ist nur die Knotenmitte, solange der Knoten darin zentriert steht. `li.has-or` ist aber flex-start (der Knoten steht links, damit die any-of-Sammelleiste unter ihm aufsetzt), und die Zelle ist so breit wie der Teilbaum. Rein in CSS nicht lösbar: gebraucht wird die Knotenbreite, und kein Selektor macht sie einer Elternregel zugänglich (Anchor Positioning ist Chrome-only). `alignStems()` misst deshalb nach jedem render() und in applyLayout() die Knotenmitte der betroffenen Zellen und setzt `--stem-x`; die Pseudoelemente rechnen über left:var(--stem-x, 50%) bzw. right:calc(100% - var(--stem-x, 50%)). Der Rückfallwert 50 % hält alle übrigen Zellen ohne Messung richtig, die transponierten Modi setzen left/right ohnehin fest. Messwerte werden wie in drawCheapPath() durch `zoom` zurückgerechnet. Verifiziert: Vitest 60/60 (2 neue Tests: `opt` auch am <li> neben dem Gate der eigenen Kinder; <li> ohne Attribut, wenn weder Kinder noch optional). Im Browser gemessen: Abweichung Stiel↔Knotenmitte bei „Wahl" 13,4 px → 0,0 px, alle übrigen Zellen unverändert 0,0 px ohne gesetzte Variable. Angesehen in horizontal und kompakt: gestrichelter Ast in Tinte zum Kreis, Sammelleiste durchgezogen, deutlich unterscheidbar vom gestrichelt-grauen any-of-Ast daneben. SVG-Export gerendert geprüft: 2 von 9 Tinte-Linien gestrichelt, beide Kreise am Auftreffpunkt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
aa3e087ee6
commit
091e8849d8
+27
-4
@@ -570,12 +570,19 @@
|
||||
content:'';position:absolute;top:0;left:50%;height:14px;border-left:2px solid var(--line);
|
||||
}
|
||||
ul.and>li{padding:16px 12px 0}
|
||||
/* Der Stiel liegt bei `--stem-x` — normalerweise 50 % des <li>, was die
|
||||
Knotenmitte ist, solange der Knoten im <li> zentriert steht. Bei
|
||||
`li.has-or` ist er das NICHT (linksbündig, damit die any-of-Leiste unter
|
||||
ihm aufsetzt) und der Teilbaum macht das <li> breiter als den Knoten: der
|
||||
Stiel liefe daneben vorbei. `alignStems()` in app.js misst dort die
|
||||
Knotenmitte und setzt `--stem-x` — D10 in der Waagerechten. */
|
||||
ul.and>li::before,
|
||||
ul.and>li::after{
|
||||
content:'';position:absolute;top:0;right:50%;
|
||||
border-top:2px solid var(--line);width:50%;height:16px;
|
||||
content:'';position:absolute;top:0;
|
||||
left:0;right:calc(100% - var(--stem-x, 50%));
|
||||
border-top:2px solid var(--line);height:16px;
|
||||
}
|
||||
ul.and>li::after{right:auto;left:50%;border-left:2px solid var(--line)}
|
||||
ul.and>li::after{left:var(--stem-x, 50%);right:0;border-left:2px solid var(--line)}
|
||||
ul.and>li:only-child{padding-top:14px}
|
||||
ul.and>li:only-child::before,ul.and>li:only-child::after{border-top:none}
|
||||
ul.and>li:only-child::after{border-left:2px solid var(--line);height:14px}
|
||||
@@ -631,7 +638,23 @@
|
||||
font-weight:400;
|
||||
}
|
||||
|
||||
/* --- OPTIONAL (`+`, SPEC §3/D29): hohler Kreis am Abzweig ---
|
||||
/* --- OPTIONAL (`+`, SPEC §3/D29): gestrichelter Abzweig + hohler Kreis ---
|
||||
Gestrichelt wird NUR der Abzweig zum Knoten, nicht die Sammelleiste — die
|
||||
gehört der ganzen Geschwistergruppe. Im Fächer ist der Abzweig der
|
||||
senkrechte Stiel (border-left von ::after, beim letzten Kind border-right
|
||||
von ::before), in den gestapelten Anordnungen der waagerechte Ast
|
||||
(border-top von ::before); die Leiste ist jeweils die andere Kante.
|
||||
Gestrichelt in TINTE — any-of ist gestrichelt in Grau (D15), die Farbe
|
||||
hält beide auseinander, der Kreis unten bestätigt es. */
|
||||
ul.and>li.opt::after{border-left-style:dashed}
|
||||
ul.and>li.opt:last-child::before{border-right-style:dashed}
|
||||
.tree.vertical ul.and>li.opt::before,
|
||||
.tree.kompakt ul.and>li.opt::before,
|
||||
.tree:not(.vertical):not(.kompakt) ul.or>li.has-and>ul.and>li.opt::before{
|
||||
border-top-style:dashed;
|
||||
}
|
||||
|
||||
/* Hohler Kreis am Abzweig ---
|
||||
Kein dritter Linienstil: gestrichelt gehört any-of, und im kompakten Modus
|
||||
codiert allein der Stil das Gate (D15). Stattdessen die FODA-Konvention —
|
||||
gefüllter Punkt = erforderlich (hier: gar keiner), hohler Punkt = optional.
|
||||
|
||||
Reference in New Issue
Block a user