feat: Labels brechen balanciert um, ID-Zeile über dem Titel, Falt-Chip — D64

Lange Knotentitel brechen bei ~40 Zeichen um, gleichmäßig auf die Zeilen
verteilt — vom Renderer (wrapLabel, headless getestet) statt text-wrap:balance,
damit der Kasten auf die längste balancierte Zeile schrumpft. Der
23-px-Abzweig ankert an der ersten Zeile; der Optional-Kreis sitzt fest auf
der Abzweighöhe. Der Grafikexport misst die gerenderten Zeilen (labelLines)
und gibt je Zeile ein <text> aus. Dazu: die eingeblendete ID (D56) steht in
einer eigenen Zeile über dem Titel, und das Falt-Zeichen ist ein gerahmter
19×16-px-Chip mit gedeckelter Höhe. Plan: #ed.render.wrap, #ed.render M→L
(D62-Prüfung schlug korrekt an).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-24 17:45:11 +02:00
co-authored by Claude Fable 5
parent 1debb7d9da
commit 52232f2c20
10 changed files with 318 additions and 32 deletions
+34 -7
View File
@@ -684,7 +684,18 @@
border-radius:var(--r);
padding:7px 14px;
font-size:.88rem;font-weight:500;line-height:1.3;
white-space:nowrap;
/* Lange Labels brechen um, statt den Baum in die Breite zu treiben (D64).
Die Umbrüche setzt der RENDERER (wrapLabel: ~40 Zeichen je Zeile,
gleichmäßig verteilt) als `\n` — pre-line macht sie sichtbar, und der
Kasten schrumpft auf die längste balancierte Zeile. `max-width` +
`overflow-wrap` sind nur der Rückhalt für ein einzelnes Wort über der
Grenze. Zentriert (Organigramm-Konvention). Die feste Zeilenhöhe
bleibt — der 23-px-Abzweig (§9) trifft bei mehrzeiligen Knoten
deterministisch die Mitte der ERSTEN Zeile. */
white-space:pre-line;
max-width:40ch;
text-align:center;
overflow-wrap:break-word;
box-shadow:0 1px 2px rgba(36,52,71,.08);
}
a.node{text-decoration:none;cursor:pointer}
@@ -692,15 +703,23 @@
/* Falt-Zeichen (SPEC §9, D38): ▾ offen / „▸ n" eingeklappt, Klickziel fürs
Umklappen. Gedeckt in Grau — es ist Bedienelement, keine Statusaussage;
beim Zeigen färbt es sich zur Tinte. */
/* Als gerahmter Chip statt bloßem Glyph (D64): das nackte ▾ war ~10px klein
und kaum zu treffen. Die Höhe ist bewusst gedeckelt (feste line-height +
kein vertikales Padding): Der Chip darf die Zeilenbox des Knotens NICHT
über die festen 18,3px heben, sonst wandert die 23-px-Abzweig-Geometrie
(§9) um Halbpixel. */
.node .fold{
cursor:pointer;color:var(--muted);
font-size:.72em;margin-right:6px;
font-size:.75em;margin-right:6px;
display:inline-block;padding:0 6px;line-height:14px;
border:1px solid rgba(36,52,71,.28);border-radius:5px;
background:rgba(36,52,71,.04);
user-select:none;-webkit-user-select:none;
}
.node .fold:hover{color:var(--line)}
.node .fold:hover{color:var(--line);border-color:var(--line);background:rgba(36,52,71,.09)}
/* Der dunkle Wurzelknoten braucht ein helleres Zeichen als das Grau. */
.root-node .fold{color:rgba(255,255,255,.75)}
.root-node .fold:hover{color:#fff}
.root-node .fold{color:rgba(255,255,255,.85);border-color:rgba(255,255,255,.45);background:rgba(255,255,255,.08)}
.root-node .fold:hover{color:#fff;border-color:#fff}
/* ”-Marke (SPEC §9, D40): der Knoten trägt eine Beschreibung — im Tooltip
nachlesbar. Spiegelt das "-Zeichen der Notation; gedeckt, kein Klickziel. */
.node .desc-mark{color:var(--muted);margin-left:4px;font-weight:600}
@@ -1165,7 +1184,12 @@
Abzweig kommt von links auf halber Knotenhöhe. */
.node.opt::before{
content:'';position:absolute;
left:-5px;top:50%;transform:translateY(-50%);
/* 18px statt 50%: Der gestapelte Abzweig sitzt fest bei 23px im <li>
(= 5px Listenabstand + halbe EINZEILIGE Knotenhöhe). Seit Knoten
umbrechen (D64), ist 50% bei mehrzeiligen nicht mehr diese Höhe —
der Kreis muss auf dem Abzweig sitzen, nicht auf der Knotenmitte.
Für einzeilige Knoten sind 18px und 50% dasselbe. */
left:-5px;top:18px;transform:translateY(-50%);
width:10px;height:10px;border-radius:50%;
background:var(--card);border:2px solid var(--line);
box-sizing:border-box;
@@ -1177,8 +1201,11 @@
/* …ausgenommen der gestapelte all-of-Teilbaum unter einer any-of-Gruppe
(siehe oben): dort kommt der Abzweig wieder von links. */
.tree:not(.vertical):not(.kompakt) ul.or>li.has-and>ul.and>li>.node.opt::before{
left:-5px;top:50%;transform:translateY(-50%);
left:-5px;top:18px;transform:translateY(-50%);
}
/* Vertikal zentrierte all-of-Zwischenknoten (unten): dort trifft der Abzweig
wirklich die Knotenmitte (50%) — der Kreis geht mit. */
.tree.vertical li.has-and>ul.and>li.has-and>.node.opt::before{top:50%}
/* --- TREPPE: aufeinanderfolgende optionale Endknoten (D29, Nachtrag 3) ---
Nur im horizontalen Fächer, wo jedes Geschwister sonst eine eigene Spalte