feat: Faltmarken (>/<) — Teilbäume ein-/ausklappbar (SPEC §1/§9, D38)

Parser liest die Marke zwischen Zeichen und Statusbox (Leerraum-Regel,
`- >Achtung` bleibt Label); den Anfangszustand rechnet initialCollapsed()
in model.js: `>` klappt ein, `<` (und die Fokusmarke !!!) holt seinen
Teilbaum hervor, indem die Faltung die Pfad-Ebenen hinunterwandert —
Geschwister stehen als einzelne eingeklappte Knoten da, jede Kante bleibt
eine echte. Im Diagramm: Falt-Zeichen ▾/„▸ n" vor dem Label (n = alle
verborgenen Knoten), Klick oder ←/→ am fokussierten Knoten klappt um;
Eingriffe je Label-Pfad, nur für die Sitzung, Dokumentwechsel setzt
zurück. Eingeklappte Kinder werden nicht gerendert (Export, Messungen,
Pfadlinie automatisch konsistent), ihre Warnungen aber weiter gemeldet.
Export/Druck: „▸ n" bleibt, das ▾ offener Knoten fällt weg. Legende +
a11y in allen 9 Sprachen; SPEC-§11-Abschnitt in §1/§9 überführt;
15 neue Tests (tests/fold.test.js), Snapshots aktualisiert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-22 16:47:34 +02:00
co-authored by Claude Fable 5
parent c778e3e267
commit cbe798d746
15 changed files with 480 additions and 76 deletions
+63
View File
@@ -1771,3 +1771,66 @@ auch die Selbst-Abhängigkeit ist stumm.
dieselbe Zurückhaltung wie bei den IDs (D36): keine eigene Diagramm-
Darstellung, bis die Querverbindungen (§11) gebaut sind; der Pfeil im Tooltip
sagt die Richtung („hängt ab von“), ohne ein neues Zeichen einzuführen.
## D38 — Faltmarken gebaut: `<` wandert die Faltung hinunter, Eingriffe sind flüchtig
Die in D34 entschiedene Schreibweise (`- > [x] …`, Export folgt der Faltung)
ist umgesetzt (SPEC §1/§9). Die Bau-Entscheidungen:
**`<` holt seinen Teilbaum hervor, indem die Faltung die Pfad-Ebenen
hinunterwandert.** Die naive Lesart — `<` öffnet einfach alle Vorfahren —
machte das `>` am Vorfahren wirkungslos: Öffnen zeigt **alle** Kinder samt
Teilbäumen, nicht den einen gemeinten. Die andere naheliegende Lesart — nur
den Pfad einblenden, alles andere desselben Vorfahren verbergen — bräuchte
Knoten, die ohne ihre sichtbaren Eltern gezeichnet werden, oder Kanten, die
Ebenen überspringen: Beides log über die Struktur (dieselbe Sorte Fehler, die
D29 beim Treppen-Export korrigiert hat). Das Hinunterwandern vermeidet beides:
Der eingeklappte Vorfahr öffnet sich, seine **übrigen** Kinder werden
stattdessen eingeklappt — sichtbar ist der Pfad samt Teilbaum, die Geschwister
stehen als je ein Knoten mit „▸ n“ da, und jede gezeichnete Kante ist eine
echte. Ein `>` innerhalb des hervorgeholten Teilbaums bleibt respektiert.
Ausdrückbar ist das alles in einem schlichten Je-Knoten-Zustand
(`initialCollapsed()` in model.js, headless getestet).
**Auch die Fokusmarke `!!!` holt sich hervor** — über denselben Mechanismus
(sie geht als zweite Rettungsmarke in `initialCollapsed()` ein). Ein
Zeigefinger auf etwas Unsichtbares zeigte ins Leere; und weil Nutzer-Eingriffe
den Anfangszustand **überlagern**, kann man den Bereich danach trotzdem wieder
zuklappen — die Marke reißt ihn nicht bei jedem Neubau wieder auf.
**Interaktive Eingriffe: je Knoten, Label-Pfad-Identität, nur für die
Sitzung.** Der Baum wird bei jedem Tastendruck neu geparst — ein Eingriff muss
Neu-Renderings überleben, also hängt er nicht am Knotenobjekt, sondern am
**Label-Pfad** (dieselbe Identität wie „Was ist neu?“, D28: Umsortieren
überlebt, Umbenennen gilt als neuer Knoten). **Nicht persistiert**: Die
dauerhafte Aussage über den Anfangszustand steht im Text (D34 — „so wird
dieses Dokument eröffnet“); der localStorage-Ansichtszustand ist zudem global
über alle Dokumente (D22), ein Je-Dokument-Faltzustand wäre dort ein
Fremdkörper. Dokumentwechsel setzt zurück.
**Bedienung: Falt-Zeichen ▾/„▸ n“ vor dem Label, Pfeiltasten ←/→.** Der
einfache Klick ist der Link (§6), Alt+Klick der Sprung (D25), der lange Druck
ebenso — für das Falten blieb nur ein eigenes Klickziel oder eine weitere
Modifier-Geste; das sichtbare Zeichen ist zugleich die Auffindbarkeit (die
Lehre aus D25: eine Geste, die niemand sieht, ist keine Funktion). ←/→ am
fokussierten Knoten ist das WAI-ARIA-Baum-Idiom; Enter bleibt dem Link,
Alt+Enter dem Sprung. Der Zähler nennt **alle** verborgenen Knoten (nicht nur
die direkten Kinder) — er sagt, wie viel Plan dort zusammengefaltet liegt.
Nach dem Umklappen wird der Fokus auf denselben Knoten zurückgesetzt (der
Neubau hätte ihn sonst verschluckt und die Tastaturbedienung abgerissen).
**Faltung ist reine Ansicht — mit zwei bewussten Konsequenzen.** Warnungen
aus eingeklappten Teilbäumen werden weiter gemeldet: `renderChildren()`
überspringt nur das HTML, ein eigener Lauf (`walkFolded`) sammelt Warnungen
und zählt zugleich die verborgenen Knoten — sonst verschwände eine
`mixedGate`-Meldung je nach Faltzustand, obwohl sie den **Text** betrifft.
Und der günstigste Pfad rechnet unverändert über den ganzen Baum; seine
Spline-Linie läuft ohnehin nur durch DOM-Knoten, führt also durch die
sichtbaren Endknoten. Ein eingeklappter Zweig kann Pfadknoten verbergen —
hinnehmbar, die Inversion an den sichtbaren Knoten bleibt richtig.
**Export und Druck:** Verborgene Kinder stehen nicht im DOM — Export,
Stiel-Messung, Treppe und Pfadlinie sind damit ohne Zusatzcode konsistent
(derselbe Grund, aus dem der Renderer sie gar nicht erst erzeugt, statt sie
per CSS zu verstecken). Die Kennzeichnung „▸ n“ ist Teil des Knotentexts und
wandert von selbst in den SVG-Export; das ▾ offener Knoten wird dort und im
Druck entfernt — es ist Bedienelement, keine Aussage über den Plan.
+1
View File
@@ -164,6 +164,7 @@ Pfad, der die Wahrheit sagt.
Anfangszustand — im Diagramm wird danach unabhängig gefaltet. Schreibweise
und Export-Verhalten sind entschieden (D34-Nachtrag): `- > [x] …`,
Export/Druck folgen der sichtbaren Faltung mit Kennzeichnung.
**Umgesetzt** (SPEC §1/§9, D38).
Der gestaffelte „günstigste Pfad" aus *Kleinere Ideen* (Status-Bewusstsein,
Ausbaustufen) baut hierauf auf: Er braucht denselben Umbau der Kostenrechnung
+48 -24
View File
@@ -7,15 +7,16 @@ Syntaxänderungen werden zuerst hier dokumentiert, dann implementiert.
## 1. Zeilenformat
```
[Einrückung][Zeichen] [Statusbox] Label (Größe) URL @tag … !!! %% Kommentar
[Einrückung][Zeichen] [Faltmarke] [Statusbox] Label (Größe) URL @tag … !!! %% Kommentar
```
Alle Bestandteile außer dem Label sind optional. Die Extraktion erfolgt in
dieser Reihenfolge (wichtig für Kollisionsfreiheit):
1. Kommentar entfernen: alles ab `%%` bis Zeilenende.
2. Einrückung, Zeichen (`-` / `+` / `|` / `=`) und Statusbox `[…]` per
Zeilen-Regex; `=` nur mit folgendem Leerraum (§3).
2. Einrückung, Zeichen (`-` / `+` / `|` / `=`), Faltmarke (`>` / `<`) und
Statusbox `[…]` per Zeilen-Regex; `=` nur mit folgendem Leerraum (§3),
die Faltmarke ebenso (siehe unten).
3. URL: erstes Token, das auf `https?://\S+` passt (dadurch stören `@` in URLs nicht).
4. Größe: erstes `(XS|S|M|L|XL|XXL)`, Groß-/Kleinschreibung egal.
5. Tags: alle `@name`-Vorkommen.
@@ -24,6 +25,19 @@ dieser Reihenfolge (wichtig für Kollisionsfreiheit):
8. Fokusmarke: `!!!` als **alleinstehendes** Token (siehe unten).
9. Rest, whitespace-normalisiert = Label. Leeres Label ⇒ Zeile ignorieren.
**Faltmarke `>` / `<`** — bestimmt, wie das Dokument **eröffnet** wird:
- Steht **zwischen** Zerlegungszeichen und Statusbox (`- > [x] Backend`); bei
Wurzelknoten (ohne Zeichen) am Zeilenanfang. Erkannt nur mit **folgendem
Leerraum** — `- >Achtung` bleibt damit ein Label. Begründung der Stellung:
D34-Nachtrag.
- `>` heißt: der Teilbaum dieses Knotens ist beim Öffnen **eingeklappt**.
- `<` innerhalb eines eingeklappten Bereichs holt den **eigenen Teilbaum**
gezielt wieder hervor (Mechanik: §9).
- Die Marken bestimmen nur den **Anfangszustand** — im Diagramm wird danach
unabhängig vom Text gefaltet (§9). Sie sagen nichts über Fortschritt (§4)
oder Notwendigkeit (§3) und ändern weder Kosten noch Warnungen.
**Knoten-ID `#name`** — benennt einen Knoten im **ganzen Dokument** eindeutig;
sie ist die Adresse für Abhängigkeiten und Beschreibungsblöcke (§11).
@@ -83,7 +97,7 @@ hat etwas, das ein Cursor nicht hat: **alle** sehen dieselbe Stelle.
Referenz-Regex der Implementierung:
```
^([ \t]*)([-|+]|=(?=[ \t]))?\s*(?:\[([ ?~xX^/-])\]\s*)?(.*)$
^([ \t]*)([-|+]|=(?=[ \t]))?\s*(?:([><])(?=[ \t])\s*)?(?:\[([ ?~xX^/-])\]\s*)?(.*)$
```
Für die Knoten-ID (Schritt 6, nur der erste Treffer):
@@ -508,6 +522,36 @@ gehört und lesbar bleibt. Kein Blinken.
- Wie die Cursor-Zeile erscheint sie **nicht** im Druck und **nicht** im
Grafikexport: Sie sagt „schau jetzt hierhin", nicht „so ist der Plan".
### Ein- und ausklappbare Teilbäume (`>` / `<`, §1)
Jeder Knoten mit sichtbaren Kindern trägt ein kleines **Falt-Zeichen** vor dem
Label: **▾** offen, **„▸ n“** eingeklappt (n = Zahl der verborgenen Knoten).
Klick auf das Zeichen klappt um — der einfache Klick auf den Knoten selbst
bleibt der Link (§6); Tastatur: **←** klappt zu, **→** klappt auf am
fokussierten Knoten (WAI-ARIA-Baum-Idiom).
- **Anfangszustand aus dem Text (§1):** `>` klappt ein. `<` holt seinen
Teilbaum hervor, indem die Faltung die Pfad-Ebenen **hinunterwandert**:
Jeder eingeklappte Vorfahr öffnet sich, seine übrigen Kinder stehen
stattdessen als einzelne eingeklappte Knoten da — sichtbar ist genau der
Pfad samt Teilbaum, der Rest bleibt kompakt. Ein `>` **innerhalb** des
hervorgeholten Teilbaums bleibt respektiert.
- Auch ein **`!!!`-markierter Knoten** (§1) holt sich auf diese Weise hervor —
ein Zeigefinger auf etwas Unsichtbares zeigte ins Leere.
- Der interaktive Eingriff gilt **je Knoten** (Identität = Label-Pfad, wie bei
„Was ist neu?“) und **für die Sitzung**; er wird nicht gespeichert — die
dauerhafte Aussage steht im Text (D34). Ein Dokumentwechsel setzt zurück.
- Faltung ist **reine Ansicht**: Warnungen aus eingeklappten Teilbäumen werden
weiter gemeldet (sie gelten dem Text), und der günstigste Pfad rechnet
unverändert über den ganzen Baum — seine Linie zeigt nur die sichtbaren
Endknoten.
- **Export und Druck folgen der sichtbaren Faltung** (dieselbe Regel wie beim
„verworfene einblenden“-Filter): Verborgene Teilbäume fehlen, eingeklappte
Knoten behalten die Kennzeichnung „▸ n“ — das Bild behauptet damit keine
Vollständigkeit. Das ▾ offener Knoten ist ein Bedienelement und erscheint
weder im Export noch im Druck.
Siehe D38.
### Grafikexport des Diagramms
Das Diagramm wird aus der Live-Geometrie in ein eigenständiges SVG (nur Formen
+ Text, keine externen Ressourcen) nachgezeichnet. Zwei Icon-Schaltflächen:
@@ -646,26 +690,6 @@ ist, damit der gewählte Knoten effektiv fertig werden kann. Gemeinsam
benötigte Abhängigkeiten werden dabei **nur einmal** gezählt. Genau das macht
die Rechnung schwerer als heute — siehe D34.
### Ein- und ausklappbare Teilbäume (`>` / `<`)
- `>` an einem Knoten heißt: **ab hier eingeklappt** — sein Teilbaum ist beim
Öffnen des Dokuments zunächst verborgen.
- `<` innerhalb eines eingeklappten Bereichs holt einen einzelnen Teilbaum
gezielt wieder hervor.
- Im Diagramm wird danach **unabhängig vom Text** interaktiv auf- und
zugeklappt; die Marken im Text bestimmen nur den Anfangszustand.
- **Stellung im Zeilenformat (entschieden):** **hinter** dem Zerlegungszeichen,
vor der Statusbox — `- > [x] Backend`. Die Spalte der Zerlegungszeichen
bleibt so bündig (an ihr liest das Auge die Hierarchie ab), und ein `>` am
Zeilenanfang wäre die Markdown-Blockquote-Konvention. Wurzelknoten (ohne
Zeichen) tragen die Marke am Zeilenanfang. Begründung: D34-Nachtrag.
- **Grafikexport und Druck (entschieden):** folgen der **sichtbar
eingeklappten** Struktur — dieselbe Regel wie beim „verworfene
einblenden“-Filter (§9: „Es wird genau die sichtbare Struktur exportiert“).
Damit das Bild nicht stillschweigend Vollständigkeit behauptet, wird ein
eingeklappter Knoten **gekennzeichnet** (z. B. „▸“ oder die Anzahl der
verborgenen Kinder; genaue Form wird beim Bauen festgelegt).
### Knotenbeschreibungen
Erläuternder Text zu einem Knoten, im Diagramm als Tooltip oder Pop-up.
+5 -1
View File
@@ -91,10 +91,14 @@ entscheiden, **bevor** Code entsteht.
**benennen**.
- [ ] Querverbindungen zeichnen (eigene SVG-Ebene, optisch sekundär);
bei ausgewähltem Knoten ein-/ausgehende hervorheben.
- [ ] Faltmarken `>` / `<` parsen; interaktives Auf-/Zuklappen im Diagramm.
- [x] Faltmarken `>` / `<` parsen; interaktives Auf-/Zuklappen im Diagramm.
→ Schreibweise und Export-Verhalten entschieden (D34-Nachtrag): Marke
hinter dem Zerlegungszeichen (`- > [x] …`), Export/Druck folgen der
sichtbar eingeklappten Struktur mit Kennzeichnung am Knoten.
Umgesetzt (D38): `initialCollapsed()` (`<` wandert die Faltung die
Pfad-Ebenen hinunter, `!!!` holt sich mit hervor); Falt-Zeichen ▾/„▸ n"
+ Tastatur ←/→; Eingriffe je Label-Pfad, nur für die Sitzung; Warnungen
aus eingeklappten Teilbäumen bleiben; `tests/fold.test.js`.
- [x] XOR-Gruppe (`=`) parsen (nur mit folgendem Leerraum, Mischung →
`mixedGate`), zwei realisierte Alternativen melden.
→ Zeichen entschieden: `=` statt `x` (D34-Nachtrag). Umgesetzt (D35):
+3 -3
View File
@@ -60,9 +60,9 @@
- [^] Load a document from ?sourceUrl= (S)
- [^] Jump between diagram and text (S)
- [x] Line numbers in the text editor (XS) %% the warnings name them
- [?] Collapsible subtrees (M)
- [?] Initial state from the text: > collapses, < re-opens (S)
- [?] Fold and unfold in the diagram, independently of the text (S)
- [x] Collapsible subtrees (M)
- [x] Initial state from the text: > collapses, < re-opens (S)
- [x] Fold and unfold in the diagram, independently of the text (S)
- [ ] Open and save .werkbaum files (S)
+ [^] Show what is new since your last visit (S)
+ [?] A pastel colour per person (S)