feat: Querverbindungen folgen der Faltung + Schalter; Falt-Knopf mit vier Voreinstellungen (D75)

- Abhängigkeits-Kanten enden am nächsten sichtbaren Vorfahren, für Quelle
  wie Ziel; zusammengefallene Kanten entfallen bzw. werden dedupliziert
  (data-sub-ids/data-sub-deps am eingeklappten Vertreter, SPEC §9)
- Umschalter für die Querverbindungen neben dem Günstigster-Pfad-Knopf,
  Voreinstellung an, persistiert; Export und Druck folgen ihm
- Der Falt-Knopf schaltet durch: M und kleiner zu → alles abseits des
  Pfads zu → alles zu → alles offen; Icon/Tooltip zeigen den nächsten
  Schritt, Handfaltung setzt die Position zurück (presetFoldSet, model.js)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-25 17:19:32 +02:00
co-authored by Claude Fable 5
parent 69c9be7a6d
commit 33443016ce
10 changed files with 452 additions and 80 deletions
+3
View File
@@ -19,6 +19,9 @@ reverse.
## 2026-08-25
- The fold button cycles through four presets now: size M and smaller, everything off the cheapest path, everything, all open
- A button next to the cheapest-path toggle switches the dependency cross links on and off
- Dependency links follow the folding: an edge to a hidden node now ends at its first visible ancestor, for source and target alike
- In Brave, the file notice names the flag that enables the File System Access API by hand
- Fix: the file notice and the legend blamed "Chromium", but Brave is Chromium without the File System Access API — they now name the feature and example browsers
- Browsers without the File System Access API (Firefox, Safari) explain themselves once: files open as copies and saving downloads a new file
+84
View File
@@ -5660,3 +5660,87 @@ in den Übersetzungen — eine Stelle statt neun, an denen sie vertippt sein
kann. **Nachgemessen** im Dev-Server (Stufe-1-Pfad erzwungen, Brave per
`navigator.brave`-Stub zur Laufzeit): mit Stub trägt das Banner die zweite
Zeile samt Mono-Code der Adresse, ohne Stub fehlt beides.
## D75 — Querverbindungen folgen der Faltung und bekommen einen Schalter; der Falt-Knopf schaltet vier Voreinstellungen durch
Drei zusammenhängende Nutzerwünsche an derselben Stelle des Diagramms, in
einem Zug gebaut.
**1. Abhängigkeits-Kanten enden am nächsten sichtbaren Vorfahren.** D41 ließ
Kanten zu eingeklappten Knoten schlicht entfallen — gerade in einem dicht
gefalteten Plan verschwand damit die Aussage „dieser Zweig braucht jenen"
genau dann, wenn man sie am nötigsten hat. Die Regel gab es längst: **Der
eingeklappte Knoten vertritt seinen Teilbaum** — für die Pfad-Station, den
„▸ n"-Zähler und die Cursor-Zeile (D38-Nachträge). Jetzt gilt sie auch für
die Querverbindungen, für **Quelle wie Ziel** (Nutzer-Vorgabe). Fallen beide
Endpunkte in denselben sichtbaren Knoten, entfällt die Kante (sie sagte
nichts mehr); mehrere so zusammengefallene Kanten desselben Paars werden
**eine**. Kanten zu ausgeblendeten **verworfenen** Knoten entfallen
weiterhin — dieselbe Grenze wie bei der Cursor-Zeile (D38-Nachtrag 4):
Faltung ist Ansicht, der Verworfen-Filter ist eine Aussage über den Plan.
**Umgesetzt im Renderer, nicht in einer App-Nebenrechnung:** `walkFolded()`
läuft ohnehin durch jeden verborgenen Teilbaum (Warnungen, „▸ n") und sammelt
jetzt dessen IDs und Abhängigkeiten mit; der eingeklappte Knoten trägt sie
als `data-sub-ids`/`data-sub-deps` — getrennt von den eigenen Attributen,
damit die Bedeutung ablesbar bleibt, und headless testbar (Hausregel
D54-Nachtrag 3). `depEdges()` in app.js löst dann nur noch auf: Die
Sub-IDs stehen in DFS- und damit Dokumentreihenfolge, „erste Vergabe
gewinnt" (D36) gilt so auch über die Faltgrenze hinweg. Der Grafikexport
nutzt dasselbe `depEdges()` und folgt ohne Zusatzcode.
**2. Ein Umschalter für die Querverbindungen**, neben dem
Günstigster-Pfad-Knopf (Nutzer-Vorgabe): Voreinstellung an, persistiert in
`werkbaum-ui` wie die Nachbarn (D22). Export und Druck folgen ihm wie den
übrigen Ansichts-Filtern (die D38/D44/D56-Linie: das Bild zeigt, was
sichtbar ist). Der Klick zeichnet nur die Overlays neu statt zu rendern —
am Baum ändert sich nichts.
**3. Der Falt-Knopf wird ein Durchschalter mit vier Voreinstellungen**
(Nutzer-Vorgabe, Reihenfolge wie gewünscht): **(1)** ab Größe M abwärts zu
(die D44-Regel, unverändert), **(2)** alles zu, durch dessen Teilbaum der
günstigste Pfad nicht läuft — weder der Knoten selbst noch ein Unterknoten
liegt darauf; sichtbar bleibt genau der Pfad, alles Übrige steht als je ein
eingeklappter Knoten da —, **(3)** alles zu, **(4)** alles offen, dann
wieder von vorn. Jede Stufe beschreibt einen **vollständigen** Faltzustand
(die D44-Eigenschaft bleibt: zweimal Drücken derselben Stufe ergäbe
dasselbe), geschrieben wird über denselben Weg wie bisher — ein
Undo-Schritt je Stufe, beim Pad trägt die Sitzungs-Überlagerung.
- **Der Knopf zeigt den NÄCHSTEN Schritt** (Icon per `data-next`, Tooltip
aus vier neuen i18n-Schlüsseln × 9 Sprachen; `foldSmallTooltip` entfällt).
Das ist die D17-Logik des Bereichs-Umschalters: Ein Knopf, der den
Zustand zeigt, den man vor sich hat, sagt nichts — einer, der das Ziel
zeigt, sagt, was passiert. Mit vier Stufen ist er zudem kein Umschalter
mehr, `aria-pressed` entfällt.
- **Die Reihum-Position wird nicht gemerkt, sondern geprüft** — die
D44-Fortschreibung für vier Stufen: `render()` rechnet nach, ob der Baum
noch die zuletzt hergestellte Stufe beschreibt (`presetFoldSet` in
model.js, headless getestet); wenn nicht — Handfaltung, Textänderung,
Dokumentwechsel —, beginnt der nächste Druck wieder bei 1. Ein reines
Ablesen ohne Position (D44) trägt bei vier Stufen nicht mehr: Ein voll
offener Baum kann zugleich Stufe 4 und einer leeren Stufe 1 entsprechen —
die Mehrdeutigkeit ist den Stufen inhärent, die geprüfte Position löst
sie deterministisch.
- **Stufe 2 rechnet den Pfad auch bei ausgeschaltetem Pfad-Umschalter**
die Voreinstellung fragt nach dem Pfad, nicht nach seiner Anzeige; die
Rechnung ist dieselbe, die bei eingeschaltetem Pfad ohnehin je Tastendruck
läuft (D42).
**Preis in der Kopfzeile, benannt:** Der Querverbindungs-Knopf ist das elfte
Element. Auf dem Telefon (375 px) bricht die Zeile damit regulär in zwei
Reihen à 78 px — genau der Umbruch, den D50/D56 unterhalb von 440 px
vorsehen; am Schreibtisch bleibt sie einreihig (gemessen 44 px bei 800 px).
**Nachgemessen** im Browser an einem Wegwerf-Dokument (10 Knoten, 3 Kanten,
über die echte UI angelegt und gelöscht): Mittel eingeklappt → Kante endet
am Vertreter (`data-sub-deps="z1"`), Ziel-Seite ebenso; beide Quellen unter
einem Vorfahren eingeklappt → **eine** Kante statt zwei (3 → 2); Wurzel zu →
0 Kanten. Durchschalter: small → path → closed → open → wieder small, je mit
korrekter Faltmenge und Tooltip; Handfaltung danach setzt auf small zurück;
`path` faltet bei ausgeschaltetem Pfad-Umschalter identisch; Undo nimmt eine
Stufe in einem Zug zurück (4 Marken → 1 → 4). Umschalter: aus → 0 Overlays
und 0 Kanten im exportierten SVG, an → 3/3; `depLinks` überlebt den Reload.
480 → 487 Tests, davon 7 neue in `tests/fold.test.js` (presetFoldSet-Modi
inkl. des per `:#…` gezogenen Ziels unter einer Zugabe; `data-sub-*` in
Dokumentreihenfolge, dedupliziert, nie an offenen Knoten, nie für
ausgeblendete verworfene).
+41 -18
View File
@@ -1083,19 +1083,32 @@ fokussierten Knoten (WAI-ARIA-Baum-Idiom).
weil eine Fokusmarke `!!!` ihren Knoten immer wieder hervorholt): Dann wird
lieber nichts geschrieben, als einen Text zu hinterlassen, der etwas anderes
sagt als das Bild.
- **Für den ganzen Baum** gibt es im Diagramm-Kopf einen **Umschalter**:
gedrückt ist alles **ab Größe M abwärts** zugeklappt, nicht gedrückt ist der
ganze Baum **offen**. Betroffen ist jeder Knoten mit Kindern, dessen
**angegebene** Größe (§5) `M` oder kleiner ist — offen bleiben `L`, `XL`,
`XXL`. Ein Knoten **ohne** Größenangabe wird nicht zugeklappt: Der günstigste
Pfad rechnet fehlende Größen zwar als `M` (unten), das ist aber eine
Kostenannahme und keine Aussage des Autors.
- Beide Stellungen beschreiben einen **vollständigen** Faltzustand — was der
Umschalter nicht zuklappt, ist danach offen. Sein Zustand wird **nicht
gemerkt, sondern am Baum abgelesen**: Wer danach einen Knoten von Hand
umklappt, sieht ihn von selbst herausspringen. Geschrieben wird wie beim
einzelnen Umklappen — ein einziger Rückgängig-Schritt für den ganzen
Vorgang. Siehe D44.
- **Für den ganzen Baum** gibt es im Diagramm-Kopf einen **Durchschalter**:
Jeder Druck stellt die **nächste** von vier Voreinstellungen her, reihum —
1. **ab Größe M abwärts zugeklappt**: jeder Knoten mit Kindern, dessen
**angegebene** Größe (§5) `M` oder kleiner ist — offen bleiben `L`,
`XL`, `XXL`. Ein Knoten **ohne** Größenangabe wird nicht zugeklappt:
Der günstigste Pfad schätzt fehlende Größen zwar (unten), das ist aber
eine Kostenannahme und keine Aussage des Autors.
2. **alles abseits des günstigsten Pfads zugeklappt**: jeder Knoten, durch
dessen Teilbaum der günstigste Pfad (§9) **nicht** läuft — weder er
selbst noch ein Unterknoten liegt auf ihm. Der Pfad bleibt vollständig
sichtbar, alles Übrige steht als je ein eingeklappter Knoten da.
Gerechnet wird dafür auch bei **ausgeschaltetem** Pfad-Umschalter — die
Voreinstellung fragt nach dem Pfad, nicht nach seiner Anzeige.
3. **alles zugeklappt**: jeder Knoten mit Kindern.
4. **alles offen**.
Der Knopf zeigt als Icon und Tooltip den **nächsten** Schritt — also was
Drücken tun wird, nicht was gerade gilt: Den Zustand hat man ohnehin vor
sich (dieselbe Logik wie beim Bereichs-Umschalter auf kleinem Bildschirm).
- Jede Voreinstellung beschreibt einen **vollständigen** Faltzustand — was
sie nicht zuklappt, ist danach offen. Die Reihum-Position wird **nicht
gemerkt, sondern am Baum geprüft**: Beschreibt der Baum nicht mehr den
zuletzt hergestellten Schritt (jemand hat von Hand umgeklappt oder den
Text geändert), beginnt der nächste Druck wieder bei 1. Geschrieben wird
wie beim einzelnen Umklappen — ein einziger Rückgängig-Schritt je
Voreinstellung. Siehe D44, D75.
- 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.
@@ -1139,11 +1152,21 @@ Strichen, kein gefülltes Dreieck). Der Baum trägt weiterhin die Hauptaussage.
- Die Kanten des **ausgewählten** Knotens — Tastaturfokus im Diagramm, sonst
der Knoten der Cursor-Zeile — sind in Tinte hervorgehoben, ein- wie
ausgehende; sie liegen über den übrigen Kanten, aber unter jedem Knoten.
- Kanten zu gerade nicht sichtbaren Knoten (eingeklappt §9, verworfen
ausgeblendet §4) entfallen; bei doppelter ID zielt die Kante auf die erste
Vergabe (D36/D39).
- Die Basis-Kanten erscheinen auch im **Grafikexport** und im **Druck**; die
Hervorhebung ist Interaktion und erscheint nicht. Siehe D41.
- **Eingeklappte Knoten vertreten ihre Teilbäume auch hier** (§9, Falten):
Liegt ein Endpunkt — Quelle wie Ziel — in einem eingeklappten Teilbaum,
endet die Kante am nächsten **sichtbaren Vorfahren**. Fallen beide
Endpunkte in denselben sichtbaren Knoten zusammen, entfällt die Kante (sie
sagte nichts mehr); mehrere so zusammengefallene Kanten desselben Paars
werden zu **einer**. Kanten zu **ausgeblendeten verworfenen** Knoten (§4)
entfallen weiterhin — der Verworfen-Filter ist eine Aussage über den Plan,
keine Ansicht. Bei doppelter ID zielt die Kante auf die erste Vergabe
(D36/D39).
- **Ein Umschalter im Diagramm-Kopf** (neben dem günstigsten Pfad) blendet
die Querverbindungen ein und aus; Voreinstellung **an**, Zustand gemerkt.
- Die Basis-Kanten erscheinen auch im **Grafikexport** und im **Druck**,
sofern der Umschalter an ist (beide folgen ihm wie den übrigen
Ansichts-Filtern); die Hervorhebung ist Interaktion und erscheint nicht.
Siehe D41, D75.
### Grafikexport des Diagramms
Das Diagramm wird aus der Live-Geometrie in ein eigenständiges SVG (nur Formen
+18 -4
View File
@@ -42,6 +42,8 @@
- [^] #ed.deplink: Cross links for dependencies (M)
- [^] #ed.deplink.calm: Visually secondary — the tree carries the message (S)
- [^] #ed.deplink.focus: Highlight what a selected node needs and feeds (S)
- [x] #ed.deplink.fold: A hidden endpoint falls to its visible ancestor (S) %% source and target alike, see D75
- [x] #ed.deplink.toggle: A switch hides the cross links (XS)
- [^] #ed.path: Cheapest path (XL)
- [^] #ed.path.cost: Cost model, a size prices its subtree (S)
- [^] #ed.path.est: Missing size estimated from the sub-packages (S) %% see D66
@@ -83,7 +85,7 @@
- [^] #ed.fold.marks: Initial state from the text: > collapses, < re-opens (S)
- [^] #ed.fold.click: Fold and unfold in the diagram (S)
- [^] #ed.fold.write: Folding writes the mark back into the text (S) %% one undo step
- [^] #ed.fold.preset: One switch folds everything from size M downward (S) %% see D44
- [x] #ed.fold.preset: The fold button cycles through four presets (S) %% M-and-smaller, off-path, all, none — see D44/D75
- [^] #ed.snaps: Earlier states of a document, every ten minutes (S) %% only when something changed
- [^] #ed.snaps.manual: Save a state by hand, before a larger change (XS) %% ten minutes is the wrong beat for that moment
- [^] #ed.files: Open and save .werkbaum files (S)
@@ -397,6 +399,16 @@
The selected node — keyboard focus in the diagram, otherwise the node of the
caret line — pulls its incoming and outgoing edges forward in ink.
#ed.deplink.fold
When an endpoint sits in a folded subtree, the edge ends at the nearest
visible ancestor instead of vanishing — the collapsed node stands in for
its subtree here too, for source and target alike. Edges whose two ends
fall into the same visible node disappear, duplicates merge into one.
#ed.deplink.toggle
A toggle next to the cheapest-path button hides the cross links entirely;
export and print follow it like the other view filters.
#ed.path
Highlight the cheapest way through the tree: with and, everything; with or,
the cheapest alternative. This is the feature the whole notation exists for.
@@ -590,9 +602,11 @@
restores exactly what you were looking at.
#ed.fold.preset
One switch puts the whole tree at working height: everything of size M and
smaller closed, L and above open. Size rather than depth — depth only says
how finely somebody happened to write it down.
One button cycles the whole tree through four complete fold states: size M
and smaller closed, everything off the cheapest path closed, everything
closed, everything open. Icon and tooltip show the next step; a manual fold
restarts the cycle. Size rather than depth — depth only says how finely
somebody happened to write it down.
#ed.snaps
A safety net against mishaps: the text is put aside every ten minutes, but