Beide READMEs behaupteten noch, die stabile Instanz könne `?sourceUrl=` erst „ab ihrem nächsten Produktions-Deploy". Der ist gelaufen (1.1.2), also ist der Satz falsch. Statt ihn umzuformulieren fällt er weg — er war ohnehin nur die Entschuldigung dafür, dass die sechs Beispiel-Links je README auf den latest build zeigten, weil `?sourceUrl=` auf prod fehlte. Jetzt zeigen sie auf werkbaum.javagil.de, also dorthin, wohin der Kopf des README ohnehin als Erstes weist. Verloren geht dabei nichts: Dass es die Pages-Instanz als jeweils neuesten Build gibt, steht unverändert in Zeile 9. Stichprobe: einer der umgestellten Links liefert HTTP 200, und die Rohdatei sendet `access-control-allow-origin: *` samt `text/plain` — der Link funktioniert also nicht nur formal.
13 KiB
Werkbaum
English · Deutsch
▶ Live ausprobieren: https://werkbaum.javagil.de (stabil) · https://mhoennig.github.io/werkbaum/ (jeweils neuester Build 🚧)
Eine textuelle, Markdown-artige Notation für Projektstrukturpläne (Work Breakdown Structure) mit Und/Oder-Zerlegung — und ein Live-Editor, der sie als Diagramm rendert.
[~] Werkbaum (XL) https://wiki.example.de/relaunch
- [~] Dokumentenspeicher
| [x] Textdatei mit Copy+Paste im Frontend (S)
- [x] Parser
- [x] Texteingabefeld im Frontend
| [ ] Backend
- [~] Darstellung/Rendern (XL)
- [/] H (S) @anna
- [ ] CMS-Anbindung (M)
| [ ] WordPress
| [?] Headless CMS
- = Pflicht-Teilpaket (all of, im Diagramm nebeneinander) ·
| = Alternative (any of, untereinander) · […] = Status ·
(M) = T-Shirt-Aufwand · @name = Zuständigkeit · %% = Kommentar.
Nutzung
Den gehosteten Editor öffnen — links Text bearbeiten, rechts entsteht das Diagramm live. Toggles: transponierte (schmale) Darstellung, verworfene Elemente einblenden.
Zwischen Diagramm und Text springen
Diagramm und Editor sind in beide Richtungen verknüpft:
- Alt+Klick auf einen Knoten (Tastatur: Alt+Enter, Touch: langer Druck) markiert seine Zeile im Texteditor — ein zugeklapptes Editor-Panel öffnet sich dabei zuerst.
- Cursor im Text bewegen hebt den zugehörigen Knoten im Diagramm hervor.
Der einfache Klick behält seine bisherige Bedeutung: Ein Knoten mit URL öffnet sie weiterhin.
Diagramm aus einer URL laden
Der Editor kann seinen Notationstext über den Query-Parameter sourceUrl aus
einer externen Textdatei beziehen — praktisch zum Teilen eines Plans oder wenn
die Quelle in Git oder einem Wiki gepflegt wird:
https://werkbaum.javagil.de/?sourceUrl=https://raw.githubusercontent.com/mhoennig/werkbaum/main/docs/examples/example-plan-0.werkbaum
Fertige Beispiele in docs/examples/ — nacheinander
öffnen: Jedes wird ein eigenes Dokument, danach lässt sich in der
Editor-Titelzeile zwischen allen umschalten:
| Beispiel | Zeigt |
|---|---|
| ▶ 0 · Online-Shop-Relaunch | ein Softwareprojekt mit allen acht Status |
| ▶ 1 · Neue Küche | ein Plan ohne Software, viele Alternativen — gut für den Günstigster-Pfad-Schalter |
| ▶ 2 · Community-Konferenz | ein breiter Plan mit vielen Beteiligten; horizontal vs. kompakt vergleichen |
| ▶ 3 · Drei Arbeitsstränge | mehrere Wurzeln = mehrere Bäume nebeneinander |
| ▶ Werkbaum selbst | Bestand und mögliche Weiterentwicklung |
Der geladene Text wird als eigenes Dokument geführt, dessen Name die URL ist — eigene Dokumente bleiben unberührt, und derselbe Link aktualisiert dieses eine Dokument, statt Kopien anzuhäufen. Die URL ist die Quelle der Wahrheit: Sie wird bei jedem Laden neu geholt, lokale Änderungen daran überleben ein Neuladen also nicht.
Notationsdateien tragen die Endung .werkbaum (UTF-8; siehe docs/SPEC.md
§12 und D24) — Konvention, kein Vertrag: sourceUrl liest jede per http(s)
erreichbare Textdatei, unabhängig von Endung und Content-Type.
Einschränkung — CORS: Der Browser lädt fremde Hosts nur, wenn sie
Access-Control-Allow-Origin senden. raw.githubusercontent.com und
GitLab-Raw-Links tun das; ein beliebiger Webserver oft nicht. Scheitert das
Laden, bleibt der bisherige Stand stehen und eine Warnung nennt die Ursache.
Zugelassen sind nur http/https.
Zusammen an einem Plan arbeiten (Etherpad)
Für Zusammenarbeit in Echtzeit braucht Werkbaum kein eigenes Backend — es leiht sich ein Etherpad. Angegeben wird die Pad-Adresse, wie sie im Browser steht (ohne Export-Pfad, den hängt Werkbaum selbst an):
https://werkbaum.javagil.de/?etherpad=https://pad.hostsharing.net/p/mein-plan
Alle bearbeiten den Notationstext im Pad; ein Neu-laden-Knopf neben dem Pad-Knopf holt den aktuellen Stand. Das Zusammenführen gleichzeitiger Änderungen macht Etherpad — genau darum geht es.
Bewusst kein Abrufen im Hintergrund: Etherpad drosselt den Export (serienmäßig 10 Abrufe je 90 s und IP), ein Takt gewinnt dagegen nicht — er erzeugt die Drosselung. Nachgemessen: Nach Überschreiten des Budgets hält der Server die Verbindung rund zwei Minuten ohne Antwort offen und liefert dann in 0,4 s. Der Knopf greift gut mit „Was ist neu?" zusammen: drücken, und was seither in Produktion ging, leuchtet auf.
Weil das Pad die Schreibfläche ist, ist das Textfeld hier schreibgeschützt; ein Knopf in der Editor-Titelzeile öffnet das Pad im neuen Tab. Ohne den Schutz verschwände getippter Text beim nächsten Abruf.
Getestet gegen Etherpad: Der Klartext-Export sendet
Access-Control-Allow-Origin: *, und die Notation kommt byte-identisch
zurück — führende Leerzeichen, -/+/|, Statusboxen und %% überleben
Etherpads Speichermodell, das - wird nicht zur Aufzählung umgedeutet.
Das Pad lässt sich auch im Editor-Panel einbetten: Ein Wähler in der Titelzeile schaltet reihum zwischen Pad und Text (durch einen Splitter frei geteilt), nur Pad und nur Text. Der schmal gezogene Textspiegel behält seinen Zweck — der Sprung zwischen Diagramm und Text arbeitet auf ihm, und in nur Pad holt ein Sprung ihn selbst zurück. Der Rahmen wird nur geladen, solange er sichtbar ist, denn ein geladenes Pad verbindet sich und zeigt dich in dessen Anwesenden-Liste.
Der gemeinsame Zeigefinger: Schreib !!! in eine Zeile, und dieser Knoten
wird hervorgehoben und ins Bild geholt — bei allen, die auf das Pad schauen.
Das kann ein Cursor nicht. Erkannt nur als alleinstehendes Token, Achtung!!!
bleibt also ein gewöhnliches Label. Die Marke bleibt stehen, bis jemand sie
löscht.
Bedenke: Der Plantext liegt damit auf fremder Infrastruktur, und ein Pad ist
für jeden lesbar, der die Adresse kennt. Im eingebetteten Rahmen wird Etherpads
Autoren-Cookie (SameSite=Lax) nicht mitgesendet — man gilt bei jedem Laden als
neuer Autor, was nur serverseitig zu beheben ist (cookie.sameSite: "None").
Aus demselben Grund ist der eingebettete Rahmen vor allem zum Mitlesen gut:
Sollte das Bearbeiten darin einmal aufhören zu funktionieren, den Ansichts-Wähler
einmal durchschalten (das lädt den Rahmen neu) oder über „im Pad bearbeiten" im
eigenen Tab weiterarbeiten, wo das Cookie gilt. Siehe docs/DECISIONS.md D31
und D32.
Lokal ausführen
Die Editor-Quelle liegt jetzt als ES-Module unter frontend/src/, gebündelt mit
Vite (siehe docs/DECISIONS.md D19). Da Browser
ES-Modul-Importe über file:// blocken, funktioniert das direkte Öffnen von
frontend/index.html nicht mehr — stattdessen:
cd frontend
npm install # einmalig
npm run dev # Dev-Server unter http://localhost:8137
npm test # Vitest-Unit-Tests
npm run build # -> frontend/dist/index.html (eine self-contained Datei)
Die gebaute dist/index.html inlint JS, CSS und Favicon — diese Datei öffnet
also standalone per file:// und ist zugleich das, was deployt wird.
Build-Hinweis & eigene Produktions-Installation
Nicht-produktive Builds tragen hinter dem Titel einen kleinen Hinweis (Symbol + Tooltip), damit klar ist, dass es nicht die stabile Instanz ist:
- Dev-Server (
npm run dev) → 🔧 „Vorschau – lokaler Entwicklungsstand" - Default-Build (
npm run build, u. a. der GitHub-Pages-Deploy) → 🚧 „Aktueller Entwicklungsstand (latest build) – kann noch Fehler enthalten"
Für die eigene produktive Installation wird der Hinweis abgeschaltet:
cd frontend
npm ci # oder: npm install
npm run build:prod # -> frontend/dist/index.html OHNE Hinweis
build:prod läuft im Vite-Modus prod; frontend/.env.prod setzt dabei
VITE_BUILD_BADGE=none, wodurch der Badge-Code komplett wegoptimiert wird (er
steht dann nicht einmal mehr im Quelltext der Ausgabe). Die entstandene
dist/index.html legst du standalone auf deinen Webspace/Server (file://-
tauglich). Steuerung im Detail: app.js (mountBuildBadge), docs/DECISIONS.md
D16.
Zwei Dinge, die sonst nur der Pages-Workflow erledigt und die du im Eigenbetrieb
selbst geradeziehst: der Footer-Link MIT-License zeigt relativ auf
../LICENSE (lege die Datei eine Ebene über index.html ab oder passe den Link
an), und die Versionsnummer bleibt der Quelltext-Platzhalter 1.0 (der
Workflow ersetzt ihn sonst aus VERSION + Commit-Zahl).
Bequemer: scripts/deploy-prod.sh. Das Skript macht genau den obigen
Prod-Build und die beiden Nacharbeiten (LICENSE danebenlegen + Link
geradeziehen, Footer-Version + Commit-Link wie beim Pages-Workflow) und spiegelt
das Ergebnis per rsync/SSH auf einen Server:
scripts/deploy-prod.sh mih00@mih00.hostsharing.net:~/doms/werkbaum.javagil.de/htdocs-ssl
Das Ziel ist entweder dieses Argument oder — ohne Argument — die Variable
DEPLOY_TARGET aus der git-ignorierten Datei .env (Vorlage:
.env.example, einmal kopieren und den Pfad eintragen). Ein
Argument hat Vorrang.
Ohne -y zeigt es zuerst eine --dry-run-Vorschau und fragt nach. rsync --delete sorgt dafür, dass am Ziel nichts Altes stehen bleibt — das
Zielverzeichnis gilt also als exklusiv für Werkbaum (eine laufende Let's-Encrypt-
Challenge unter .well-known/ wird per --filter=protect ausgenommen, web-
taugliche Rechte 755/644 werden erzwungen). (Hostsharing: eine direkt
aufgeschaltete Domain liefert aus …/htdocs-ssl/; als Subdomain unter einer
anderen Domain läge das Web-Verzeichnis in …/subs-ssl/<name>/.)
Projektdokumente
frontend/— Editor ·backend/— Kotlin/Spring (Gerüst folgt, siehe backend/README.md)docs/SPEC.md— verbindliche Sprachdefinitiondocs/DECISIONS.md— Design-Entscheidungen mit Begründungdocs/ROADMAP.md— Mermaid-Plugin, Taiga-Integration, Tenzudocs/TASKS.md— offene Aufgaben (Checkboxen)docs/brand/BRAND.md— Logo, Wortbild, Anwendungsregelndocs/design/— Design-Herleitung der MarkeCLAUDE.md— Projektkontext für Claude Code
Deployment
Der Editor wird per GitHub Actions als statische Seite auf GitHub Pages
veröffentlicht (Workflow: .github/workflows/pages.yml). Ausgelöst bei jedem
Push auf main sowie manuell (workflow_dispatch).
Der Workflow richtet Node ein, führt npm ci, npm test (Vitest) und
npm run build (Vite) aus und veröffentlicht die gebündelte
frontend/dist/index.html als index.html an der Wurzel-URL, dazu LICENSE
für den MIT-Link im Footer. Das Favicon ist im Build bereits inline, es muss also
nichts weiter kopiert werden; nur der Laufzeit-Link ../LICENSE wird auf der
Kopie geradegezogen. Ein fehlschlagender Test blockiert das Deployment.
backend/ und die übrigen docs/ werden nicht veröffentlicht.
Beim Zusammenstellen setzt der Workflow zudem die Versionsnummer im Footer:
Major.Minor stammt aus der Datei VERSION (per bewusstem „Bump-Commit"
gepflegt), die Micro-Stelle aus der Zahl der Commits seit diesem letzten
Bump — sie steigt also mit jedem Commit und beginnt nach einem Bump wieder bei
0 (Werkbaum 1.0.0, 1.0.1, … dann VERSION auf 1.1 bumpen → 1.1.0). Es
wird nichts ins Repo zurückgeschrieben. Im Footer verlinkt der Name Werkbaum
die Repo-Startseite, die Versionsnummer genau den zugehörigen Commit
(…/commit/<sha>). Lokal geöffnet zeigt der Editor den Platzhalter aus dem
Quelltext (Werkbaum 1.0).
Einmalige Einrichtung: In den Repo-Settings unter Pages als Source „GitHub Actions" wählen. Das Repo muss dafür öffentlich sein (GitHub Pages via Actions ist für private Repos nur mit kostenpflichtigem Plan verfügbar).
Lizenz
MIT — siehe LICENSE. © 2026 Michael Hönnig. Die mitgelieferten
IBM-Plex-Schriften stehen unter der SIL Open Font License 1.1 (siehe
LICENSE und frontend/src/fonts/OFL.txt).
