Files
werkbaum/frontend/index.html
T
mhoennigandClaude Fable 5 ecc1b632f6 feat: von Station zu Station des günstigsten Pfads (D47)
Mit dem status-bewussten Pfad (D46) zeigt das Diagramm die offene Front — im
mitgelieferten Plan 24 Stationen, verteilt über einen Baum von über 20 000 px
Breite. Sie zu sehen war damit gelöst, sie abzugehen nicht. Der neue Knopf im
Diagramm-Kopf holt sie der Reihe nach in die Mitte: erster Druck die erste
(was als Nächstes dran ist), jeder weitere die nächste, nach der letzten
wieder die erste.

Der Sprung fasst nur das Diagramm an — zentrieren, hervorheben, Tastaturfokus,
also buchstäblich die Behandlung des ausdrücklichen Alt+Klicks (D25) samt Puls
und hervorgehobenen Abhängigkeits-Kanten. In den Text führt von dort
Alt+Enter, dieselbe Geste wie überall.

Ohne eigenen Zustand: Fortgesetzt wird am gerade hervorgehobenen Knoten. Ein
gemerkter Index wäre schlechter — der Baum wird bei jedem Tastendruck neu
gebaut, die Stationsliste ändert sich unter ihm. Verborgen, solange es keine
Station gibt (Pfad aus, oder alles erledigt); der Tooltip nennt die Anzahl.
i18n `leanNextTooltip` in neun Sprachen.

Die Mobil-Kopfzeile trug den neunten Knopf nicht mehr — D17-Nachtrag 5 hatte
sie gerade erst auf acht zurechtgemessen. Nachgemessen: bei 375 px reicht es
nach Lücke 8->6 px und Innenabstand 10->8 px weiterhin (345 px Inhalt, 359 px
Platz, 14 px Luft); bei 320 px passen neun Fingerziele arithmetisch nicht, und
unter 29 px zu gehen hat D17-Nachtrag 5 abgelehnt. Die Zeile schiebt dort
deshalb (`overflow-x:auto`, Balken ausgeblendet) — 41 px Schiebeweg, Kopfhöhe
unverändert 49 px, alle neun Elemente in voller Größe erreichbar.

Dabei ein älterer, stiller Fehler gefunden: Bei Platzmangel schrumpfte der
Modus-Wähler — das einzige Element ohne feste Größe — auf einen 2-px-Strich
zusammen, während sein Icon 38 px breit darüber hinausragte; er sah aus wie
ein Trennstrich und war unbenutzbar. `flex:0 0 auto` an allen Kindern der
Zeile stellt das ab.

236 Tests grün. SPEC §9 und D47 halten es fest; der Plan bekommt den Knoten
als `[x]` (noch nicht deployt), frontend/CLAUDE.md die Kopfzeilen-Stolperfalle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 13:22:52 +02:00

231 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Werkbaum PSP &amp; Lean Pathfinding Editor (Work Breakdown Structure)</title>
<meta name="description" content="Werkbaum ist ein textbasierter Editor für PSP/Lean Pathfinding und Projektstrukturpläne (WBS): Schreibe die Struktur als Text, das And/Or-Diagramm entsteht live. Auch für Feature-Trees &amp; Requirements.">
<link rel="icon" type="image/svg+xml" href="../docs/brand/favicon.svg">
</head>
<body>
<header>
<div class="header-row">
<h1 style="display:flex;align-items:center;gap:11px">
<svg width="34" height="34" viewBox="0 0 64 64" aria-hidden="true">
<rect x="22" y="5" width="20" height="12" rx="3.5" fill="#243447"/>
<path d="M32 17 V22 M14 22 H50 M14 22 V27 M50 22 V27" fill="none" stroke="#243447" stroke-width="3"/>
<rect x="4" y="27" width="20" height="12" rx="3.5" fill="#fff" stroke="#243447" stroke-width="3"/>
<rect x="40" y="27" width="20" height="12" rx="3.5" fill="#fff" stroke="#243447" stroke-width="3"/>
<path d="M50 39 V46" fill="none" stroke="#0F766E" stroke-width="3" stroke-dasharray="4 4"/>
<rect x="40" y="46" width="20" height="12" rx="3.5" fill="#fff" stroke="#0F766E" stroke-width="3"/>
</svg>
<span data-i18n-title="brandTooltip" title="„Werkbaum“ bedeutet so viel wie Werk-Baum — der Baum des Projektstrukturplans (WBS).">Werk<span style="color:var(--or)">baum</span></span>
</h1>
<div class="header-tools">
<button type="button" id="fsToggle" class="fsbtn" aria-pressed="false" data-i18n-title="fullscreenTooltip" data-i18n-aria="fullscreenTooltip" title="Vollbild Panels nutzen die ganze Fensterbreite" aria-label="Vollbild Panels nutzen die ganze Fensterbreite">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
</button>
<div class="langsel" role="radiogroup" aria-label="Sprache / Language">
<button type="button" data-lang="de" title="Deutsch">DE</button>
<button type="button" data-lang="en" title="English">EN</button>
<button type="button" data-lang="es" title="Español">ES</button>
<button type="button" data-lang="fr" title="Français">FR</button>
<button type="button" class="lang-extra" data-lang="pl" title="Polski">PL</button>
<button type="button" class="lang-extra" data-lang="ru" title="Русский">RU</button>
<button type="button" class="lang-extra" data-lang="hi" title="हिन्दी">HI</button>
<button type="button" class="lang-extra" data-lang="zh" title="中文"></button>
<button type="button" class="lang-extra" data-lang="ja" title="日本語"></button>
<button type="button" id="langMore" class="langmore" aria-expanded="false" data-i18n-title="langMore" data-i18n-aria="langMore" title="weitere Sprachen" aria-label="weitere Sprachen"></button>
</div>
</div>
</div>
<p><span class="sub-full" data-i18n="subtitle">Werkbaum Ein Text-basierter WBS/Projektstrukturplan/Feature-Tree/Requirements-Baum Diagramm-Editor.</span><span class="sub-short" data-i18n="subtitleShort">WBS, Feature- &amp; Requirements-Bäume als Text</span></p>
</header>
<div class="app" id="app">
<div class="panel editor">
<div class="panel-head">
<!-- Gegenstück zum Knopf im Diagramm-Kopf (D17-Nachtrag). Der
Dokumenten-Wähler steht direkt daneben und ist damit genau dann zu
sehen, wenn der Text vorn ist — er gehört zu diesem Bereich. -->
<button type="button" class="copybtn panetoggle" id="paneToDiagram" data-i18n-title="paneToDiagram" data-i18n-aria="paneToDiagram" title="Zum Diagramm wechseln" aria-label="Zum Diagramm wechseln">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="3" width="6" height="4" rx="1"/><rect x="2.5" y="17" width="6" height="4" rx="1"/><rect x="15.5" y="17" width="6" height="4" rx="1"/><path d="M12 7V13M5.5 13H18.5M5.5 13V17M18.5 13V17"/></svg>
</button>
<span class="doctrigger" id="docTrigger" role="button" tabindex="0" aria-haspopup="menu" aria-expanded="false" data-i18n-title="docSwitchTooltip" data-i18n-aria="docSwitchTooltip" title="Dokument wählen oder verwalten" aria-label="Dokument wählen oder verwalten">
<span class="docname" id="docName"></span>
<svg class="doc-caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
</span>
<div class="docmenu" id="docMenu" role="menu" data-i18n-aria="docMenuAria" aria-label="Dokumente" hidden>
<div class="docmenu-list" id="docList"></div>
<div class="docmenu-actions">
<button type="button" class="docact" id="docNew" data-i18n="docNew">Neues Dokument</button>
<button type="button" class="docact" id="docRename" data-i18n="docRename">Umbenennen</button>
<button type="button" class="docact" id="docRestore" data-i18n="docRestore" hidden>Original wiederherstellen</button>
<button type="button" class="docact docdel" id="docDelete" data-i18n="docDelete">Löschen</button>
</div>
</div>
<button type="button" class="copybtn padview" id="padViewBtn" data-view="text" hidden>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect class="frame" x="2.5" y="5" width="19" height="14" rx="2"/><line class="ic-both" x1="12" y1="5" x2="12" y2="19"/><rect class="ic-pad" x="3.5" y="6" width="8" height="12" rx="1" fill="currentColor" stroke="none"/><rect class="ic-text" x="12.5" y="6" width="8" height="12" rx="1" fill="currentColor" stroke="none"/></svg>
</button>
<a class="copybtn padlink" id="padLink" href="#" target="_blank" rel="noopener" hidden>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9.5" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
</a>
<button type="button" class="copybtn padrefresh" id="padRefresh" hidden>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 3v6h-6"/></svg>
</button>
<button type="button" class="copybtn" id="copy" data-i18n-title="copyTooltip" data-i18n-aria="copyTooltip" title="Text in die Zwischenablage kopieren" aria-label="Text in die Zwischenablage kopieren">
<svg class="ic-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<svg class="ic-done" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
</button>
<button type="button" class="copybtn legendbtn" id="legendBtn" data-i18n-title="legendTooltip" data-i18n-aria="legendTooltip" title="Legende ein-/ausblenden" aria-label="Legende ein-/ausblenden">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
</button>
<div class="winbtns" aria-label="Editor">
<button type="button" class="winbtn" data-state="b" data-i18n-title="minimize" title="minimieren"></button>
<button type="button" class="winbtn" data-state="normal" data-i18n-title="normal" title="normal"></button>
<button type="button" class="winbtn" data-state="a" data-i18n-title="maximize" title="maximieren"></button>
</div>
</div>
<div class="editor-body">
<!-- Pad-Rahmen + interner Spiegel teilen sich einen eigenen Bereich, damit die
Legenden-Aufteilung (D26) unberührt bleibt. Reihenfolge: Pad, Splitter,
Spiegel — gezogen wird von der Spiegelseite aus, wie bei der Legende. -->
<div class="src-area pv-text" id="srcArea">
<iframe class="padframe" id="padFrame" title="Etherpad" hidden></iframe>
<div class="pad-gutter" id="padGutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="padGutterAria" title="Ziehen zum Verschieben, Doppelklick setzt zurück" aria-label="Pad und Texteditor größenverändern" hidden></div>
<!-- Zeilennummern + Textfeld sind eine Spalte: der Splitter oben teilt den
Rahmen gegen diese Spalte, nicht gegen das nackte Textfeld. -->
<div class="src-wrap" id="srcWrap">
<div class="lineno" id="lineNos" aria-hidden="true"><div class="lineno-inner"></div></div>
<textarea id="src" spellcheck="false" data-i18n-aria="editorTitle" aria-label="Struktur (Text)"></textarea>
</div>
</div>
<div class="hint-gutter" id="hintGutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="hintGutterAria" title="Ziehen zum Verschieben, Doppelklick setzt zurück" aria-label="Editor und Legende größenverändern" hidden></div>
<div class="agenda" id="agenda">
<button type="button" class="agenda-summary" id="agendaToggle" aria-expanded="false" aria-controls="hint" data-i18n="agenda">Agenda</button>
<div class="hint" id="hint">
Einrückung (2 Leerzeichen oder Tab) definiert die Hierarchie.<br>
<code>-</code>&nbsp; Teilpaket, alle erforderlich <em>(all of)</em><br>
<code>+</code>&nbsp; Zugabe, nicht erforderlich <em>(optional)</em><br>
<code>!!!</code>&nbsp; hierhin schauen (gemeinsamer Zeigefinger)<br>
<code class="or-code">|</code>&nbsp; Alternative, eine wählen <em>(any of)</em><br>
<code class="or-code">=</code>&nbsp; Alternative, genau eine <em>(xor)</em><br>
Zeile ohne Zeichen = Wurzelknoten. <code class="or-code">|</code>, <code class="or-code">=</code> und <code>-</code>/<code>+</code> nicht mischen.<br>
Status als Kästchen nach dem Zeichen, z.&nbsp;B. <code>- [~] Frontend</code>:
<div class="chips">
<span class="chip st-idee">[?] Idee</span>
<span class="chip st-geplant">[&nbsp;] geplant</span>
<span class="chip st-arbeit">[~] in Arbeit</span>
<span class="chip st-durchstich">[/] Durchstich</span>
<span class="chip st-fertig">[x] fertig</span>
<span class="chip st-prod">[^] in Produktion</span>
<span class="chip st-verworfen">[-] verworfen</span>
</div>
Aufwand als T-Shirt-Größe in Klammern, Link einfach als URL anhängen:<br>
<code>- [ ] Backend (L) https://…</code><br>
Ab <code>(M)</code> gilt: weiter untergliedern — fehlt die Untergliederung, erscheint ein Platzhalter im Diagramm.<br>
Kommentare mit <code>%%</code> — als ganze Zeile oder am Zeilenende.
Personen mit <code>@name</code> — erscheinen unten rechts am Knoten.
Knoten-ID mit <code>#name:</code> vor dem Titel — erscheint im Tooltip des Knotens.
Abhängigkeiten mit <code>:#name,#name</code> — erscheinen im Tooltip.
Die Knotenfarbe zeigt den effektiven Status (mit Abhängigkeiten); ist der eigene weiter, steht er als Marke unten links.
Beschreibungen: <code>"</code> Zeile unter dem Knoten; Langtext hinter <code>---</code> als eingerückter <code>#id</code>-Block — beides im Tooltip (”).
Falten: <code>- [x] &gt;</code> startet eingeklappt, <code>&lt;</code> holt hervor; ▾/▸ am Knoten klappt um (Tastatur: ←/→).
</div>
</div>
</div>
</div>
<div class="gutter" id="gutter" role="separator" data-i18n-title="gutterTooltip" data-i18n-aria="gutterAria" aria-label="Bereiche größenverändern" title="Ziehen zum Verschieben, Doppelklick setzt zurück"></div>
<div class="panel right">
<div class="panel-head">
<!-- Nur auf kleinem Bildschirm (D17-Nachtrag): dort ist immer genau ein
Bereich zu sehen, und dieser Knopf schaltet auf den anderen um. Er
zeigt das Ziel, nicht den Zustand — der Zustand ist der Bereich, den
man gerade vor sich hat. -->
<button type="button" class="copybtn panetoggle" id="paneToText" data-i18n-title="paneToText" data-i18n-aria="paneToText" title="Zum Text wechseln" aria-label="Zum Text wechseln">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 10h16M4 14h10M4 18h13"/></svg>
</button>
<span data-i18n="diagramTitle">Diagramm</span>
<button type="button" class="copybtn togglebtn" id="showc" aria-pressed="false" data-i18n-title="discardedTooltip" data-i18n-aria="discardedTooltip" title="Verworfene Knoten samt Teilbaum ein-/ausblenden" aria-label="Verworfene Knoten samt Teilbaum ein-/ausblenden">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="6" width="18" height="12" rx="2" stroke-dasharray="3 2.5"/><path d="M7 12h10"/></svg>
</button>
<button type="button" class="copybtn togglebtn" id="cheapBtn" aria-pressed="true" data-i18n-title="cheapTooltip" data-i18n-aria="cheapTooltip" title="Günstigsten Pfad hervorheben nicht benötigte Alternativen treten zurück" aria-label="Günstigsten Pfad hervorheben nicht benötigte Alternativen treten zurück">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="5" cy="6" r="2.5"/><circle cx="19" cy="18" r="2.5"/><path d="M7.5 6H14a3 3 0 0 1 0 6H10a3 3 0 0 0 0 6h6.5"/></svg>
</button>
<!-- Von Station zu Station des günstigsten Pfads (SPEC §9, D47). Kein
Umschalter, sondern eine Bewegung: jeder Druck holt die nächste noch
offene Station in die Mitte. Verborgen, solange es keine gibt (wie
der „Was ist neu?"-Knopf daneben). -->
<button type="button" class="copybtn" id="leanNextBtn" title="Zur nächsten Station des günstigsten Pfads" aria-label="Zur nächsten Station des günstigsten Pfads" hidden>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 12h5" stroke-dasharray="2.5 2.5"/><circle cx="11" cy="12" r="3"/><path d="M15 12h6M18.5 9.5l2.5 2.5-2.5 2.5"/></svg>
</button>
<button type="button" class="copybtn freshbtn" id="freshBtn" title="Neu in Produktion" aria-label="Neu in Produktion" hidden>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg>
<span class="freshcount" id="freshCount" aria-hidden="true">0</span>
</button>
<!-- Faltung für den ganzen Baum (SPEC §9, D44): ein Umschalter wie die
beiden Nachbarn. Gedrückt = alles ab Größe M abwärts ist zugeklappt. -->
<button type="button" class="copybtn togglebtn" id="foldSmallBtn" aria-pressed="false" data-i18n-title="foldSmallTooltip" data-i18n-aria="foldSmallTooltip" title="Knoten der Größe M und kleiner zuklappen erneut drücken klappt alle wieder auf" aria-label="Knoten der Größe M und kleiner zuklappen erneut drücken klappt alle wieder auf">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21V3M8 8l4 4 4-4M8 16l4-4 4 4"/></svg>
</button>
<div class="seg" role="radiogroup" aria-label="Darstellung">
<label data-i18n-title="modeHorizontal" title="Horizontal Organigramm, Diagramm über dem Editor">
<input type="radio" name="layout" value="horizontal" checked data-i18n-aria="modeHorizontal" aria-label="Horizontal Organigramm">
<span><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="3" width="6" height="4" rx="1"/><rect x="2.5" y="17" width="6" height="4" rx="1"/><rect x="15.5" y="17" width="6" height="4" rx="1"/><path d="M12 7V13M5.5 13H18.5M5.5 13V17M18.5 13V17"/></svg></span>
</label>
<label data-i18n-title="modeKompakt" title="Kompakt alles nach unten, platzsparend">
<input type="radio" name="layout" value="kompakt" data-i18n-aria="modeKompakt" aria-label="Kompakt alles nach unten">
<span><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="6" height="4" rx="1"/><path d="M6 7V20"/><path d="M6 11H10M6 17H10"/><rect x="10" y="9" width="9" height="4" rx="1"/><rect x="10" y="15" width="9" height="4" rx="1"/></svg></span>
</label>
<label data-i18n-title="modeVertikal" title="Vertikal Baum nach rechts, Diagramm neben dem Editor">
<input type="radio" name="layout" value="vertikal" data-i18n-aria="modeVertikal" aria-label="Vertikal Baum nach rechts">
<span><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="9.5" width="5" height="5" rx="1"/><rect x="16" y="3" width="6" height="4" rx="1"/><rect x="16" y="17" width="6" height="4" rx="1"/><path d="M7.5 12H12M12 5V19M12 5H16M12 19H16"/></svg></span>
</label>
</div>
<button type="button" class="copybtn" id="copyDiagram" data-i18n-title="copyDiagramTooltip" data-i18n-aria="copyDiagramTooltip" title="Diagramm als PNG-Bild in die Zwischenablage kopieren" aria-label="Diagramm als PNG-Bild in die Zwischenablage kopieren">
<svg class="ic-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<svg class="ic-done" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
</button>
<div class="dlgroup" id="dlGroup">
<button type="button" class="copybtn dltrigger" id="dlTrigger" aria-haspopup="true" aria-expanded="false" data-i18n-title="downloadMenu" data-i18n-aria="downloadMenu" title="Diagramm herunterladen (SVG/PNG)" aria-label="Diagramm herunterladen (SVG/PNG)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12M8 11l4 4 4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg>
</button>
<div class="dlmenu">
<button type="button" class="copybtn dlbtn" id="dlDiagram" data-i18n-title="downloadDiagramTooltip" data-i18n-aria="downloadDiagramTooltip" title="Diagramm als SVG-Datei herunterladen" aria-label="Diagramm als SVG-Datei herunterladen">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12M8 11l4 4 4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg><span class="dl-label">SVG</span>
</button>
<button type="button" class="copybtn dlbtn" id="dlDiagramPng" data-i18n-title="downloadPngTooltip" data-i18n-aria="downloadPngTooltip" title="Diagramm als PNG-Datei herunterladen" aria-label="Diagramm als PNG-Datei herunterladen">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12M8 11l4 4 4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg><span class="dl-label">PNG</span>
</button>
</div>
</div>
<div class="winbtns" aria-label="Diagramm">
<button type="button" class="winbtn" data-state="a" data-i18n-title="minimize" title="minimieren"></button>
<button type="button" class="winbtn" data-state="normal" data-i18n-title="normal" title="normal"></button>
<button type="button" class="winbtn" data-state="b" data-i18n-title="maximize" title="maximieren"></button>
</div>
</div>
<div class="diagram"><ul class="tree" id="out"></ul></div>
<div class="warnings" id="warn" role="status" aria-live="polite"></div>
<div class="zoomctl" data-i18n-aria="zoomAria" aria-label="Zoom (Strg/Cmd + Mausrad)">
<button type="button" id="zoomToggle" class="zoomToggle" data-i18n-title="zoomTooltip" title="Zoom anzeigen" aria-label="Zoom anzeigen">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="10" cy="10" r="6"/><path d="m15 15 4 4M10 7v6M7 10h6"/></svg>
</button>
<div class="zoomcontrols">
<button type="button" id="zoomOut" data-i18n-title="zoomOut" title="verkleinern"></button>
<button type="button" id="zoomReset" data-i18n-title="zoomReset" title="zurücksetzen">100&nbsp;%</button>
<button type="button" id="zoomIn" data-i18n-title="zoomIn" title="vergrößern">+</button>
</div>
</div>
</div>
</div>
<footer class="site-footer"><a href="https://github.com/mhoennig/werkbaum" target="_blank" rel="noopener">Werkbaum</a>&nbsp;<a class="ver" href="https://github.com/mhoennig/werkbaum/commit/main" target="_blank" rel="noopener">1.0</a> · <a href="llms.md" target="_blank" rel="noopener" title="Werkbaum-Notation für KI-Agenten · Werkbaum notation for AI agents (Markdown)">llms.md</a> © 2026 <a href="https://michael.hoennig.de" target="_blank" rel="noopener">Michael Hönnig</a> · <span class="vibe" title="Fable 5 and Opus 4.8">Built with Claude Code</span> <a href="../LICENSE" target="_blank" rel="noopener">License</a> · <a href="https://michael.hoennig.de/imprint.html" target="_blank" rel="noopener" data-i18n="imprint">Impressum</a> · <a id="privacyLink" href="https://michael.hoennig.de/datenschutzerklaerung.html" target="_blank" rel="noopener" data-i18n="privacy">Datenschutz</a></footer>
<script type="module" src="./src/app.js"></script>
</body>
</html>