Commit Graph
63 Commits
Author SHA1 Message Date
mhoennig 1cf60e2a75 frontend: Integrate self-hosted IBM Plex fonts, replace Google Fonts
IBM Plex Sans and Mono are now embedded locally (`woff2`) under `src/fonts`, licensed under SIL OFL 1.1. Removed external `<link>` to Google Fonts for enhanced privacy and compliance. Updated `style.css` with `@font-face` and unicode ranges for `latin` and `latin-ext` subsets. Added license details to documentation.
2026-07-22 10:33:09 +02:00
mhoennigandClaude Opus 4.8 0f27bab905 frontend/ci: Build-Hinweis in die App-Quelle, env-gesteuert; Prod-Build ohne
Der Latest-Build-Hinweis wandert vom Workflow-sed in die App (app.js,
mountBuildBadge), damit ihn auch der Dev-Server zeigt — ein Post-Build-sed
erreicht den Dev-Server nicht. Logik umgekehrt: Hinweis ist der Normalfall,
nur die produktive Installation schaltet ihn ab.

Drei Zustaende ueber Vite-Env VITE_BUILD_BADGE (Auswertung in app.js):
- Dev-Server (import.meta.env.DEV) -> 🔧 "Vorschau/lokaler Entwicklungsstand"
- Default `npm run build` (Env ungesetzt) -> 🚧 "latest build"; der Pages-
  Deploy nutzt den Default und traegt den Hinweis dadurch automatisch (die
  sed-Injektion entfaellt).
- `npm run build:prod` (Vite-Modus prod, .env.prod: VITE_BUILD_BADGE=none)
  -> KEIN Badge; esbuild eliminiert den Zweig als toten Code.

Verifiziert zur Laufzeit (Dev + `vite preview` auf beiden Builds): 🔧 im Dev,
🚧 im Default-Build, gar nichts im Prod-Build (Titel sauber "Werkbaum").
34 Vitest-Tests gruen, Workflow-YAML/Shell gueltig.

Doku: D16 fortgeschrieben (quellbasiert/env statt sed, Begruendung); README
(de/en) mit Prod-Build-Anleitung inkl. LICENSE-/Versions-Caveats; frontend/
CLAUDE.md; .claude/launch.json bekommt einen frontend-dist-Preview (vite
preview, Port 8138) zum Verifizieren gebauter Dateien.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 05:56:59 +02:00
mhoennigandClaude Opus 4.8 0a8b3659ea frontend: Fehlertolerantes Parsen — unbekannte Statuszeichen melden
TASKS Phase 2 (Phase 2 abgeschlossen). Der Parser erfasst die Statusbox jetzt
als beliebiges Einzelzeichen `\[([^\]])\]` und validiert gegen STATUS_BY_CODE.
Unbekannte Codes (z. B. [z]) werden nicht mehr still ins Label geschluckt,
sondern als {type:'unknownStatus', line, code} in parse().warnings gemeldet;
der Knoten bleibt neutral, Folgezeilen unberührt. Mehrzeichige Klammern ([xyz])
sind keine Statusbox und bleiben im Label (kein Fehlalarm).

render() in app.js führt Parser- und Renderer-Warnungen zusammen (nach Zeile
sortiert) und zeigt sie via formatWarning. Neuer i18n-Key unknownStatusWarn in
allen 9 Sprachen.

Verifiziert: mehrere unbekannte Codes -> zeilensortierte, lokalisierte
Warnungen (DE/EN), Knoten neutral, keine Konsolenfehler. tests/parser.test.js
+5 (34 Tests grün). SPEC §4 + CLAUDE ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 21:16:34 +02:00
mhoennigandClaude Opus 4.8 15699c321c frontend: Barrierefreiheit — aria-Labels, Fokusreihenfolge, Live-Region
TASKS Phase 2. Jeder Diagramm-Knoten bekommt in render.js (nodeAria) einen
sprechenden, lokalisierten aria-label: Label + Status + Aufwand (inkl.
„(angenommen)" beim impliziten M) + Zuständige + Link. Die rein visuellen
Badges (Größe, Tags, ↗) sind aria-hidden, damit der Screenreader nicht „M",
„anna", „↗" kryptisch doppelt vorliest. Neue a11y*-i18n-Keys in allen 9
Sprachen.

Alle Knoten sind fokussierbar (tabindex=0 bzw. der Link) in Dokument-/Lese-
reihenfolge; sichtbarer :focus-visible-Rahmen (Petrol). #warn ist eine
Live-Region (role=status, aria-live=polite) — neue Warnungen werden angesagt.

Verifiziert: aria-label folgt dem Sprachwechsel (DE/EN), Badges aria-hidden,
keine visuelle Regression, keine Konsolenfehler. Renderer-Snapshots
aktualisiert (29 Tests grün). SPEC §9 (Barrierefreiheit) + CLAUDE ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 21:08:41 +02:00
mhoennigandClaude Opus 4.8 5905712aa5 frontend: Renderer + Modell headless auslagern (src/model.js, src/render.js)
TASKS Phase 1. Baum-/Kostenlogik -> model.js (gateOf, needsBreakdown,
visibleChildren, computeCheapSet, cheapCls), HTML-Erzeugung -> render.js
(renderTreeHtml). Beide sind headless: UI-State (verworfene einblenden,
günstigster Pfad) kommt als Parameter herein statt aus Globals; nur cheapPathOn
bleibt UI-State in app.js. render() in app.js ist jetzt die dünne DOM-Anbindung.

Snapshot-Tests (tests/render.test.js): Grundzustand, günstigster Pfad,
verworfene einblenden, Geister-Knoten („Untergliederung fehlt"), plus die
Zusicherung, dass der Renderer nie eine Modus-Klasse erzeugt (Modus =
CSS-Container-Klasse in app.js -> ein Snapshot deckt alle drei Modi). 24 Tests
grün. Byte-identisches Rendering im Browser verifiziert (18 Knoten, 6 Blätter,
3 Geister, Toggles ok, keine Konsolenfehler).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:58:47 +02:00
mhoennigandClaude Opus 4.8 78d1000f61 frontend: Projektgerüst mit Vite — Single-HTML in ES-Module + Bündelung
Erster Schritt der Modularisierung (ROADMAP „tragfähige Codebasis",
TASKS Phase 1). Der bisher einzelne index.html-Prototyp wird zur Vite-App:

- CSS -> src/style.css, gesamtes JS -> src/app.js (noch als ein Modul;
  Parser/Renderer werden in den folgenden Checkboxen herausgelöst).
- index.html ist der Vite-Entry (<script type="module" src="/src/app.js">).
- npm run build (vite + vite-plugin-singlefile) inlint JS+CSS+Favicon (als
  data:-URI) in EINE self-contained dist/index.html -> bleibt file://-tauglich.
- Vitest als Testrunner eingerichtet (tests/, passWithNoTests bis Schritt 2/3).
- Pages-Workflow: Node + npm ci + npm test + npm run build, deployt dist/;
  Favicon-sed entfällt (inline), nur ../LICENSE + Footer-Version bleiben.
- .claude/launch.json auf Vite-Dev-Server (Port 8137) umgestellt.

Entscheidung als D19 dokumentiert (weicht D11 „kein Build-Zwang" bewusst auf,
hält aber D16 file://-Versprechen über die gebündelte Datei). README (de/en)
und frontend/CLAUDE.md um Dev-/Build-/Test-Workflow ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:47:23 +02:00
mhoennigandClaude Opus 4.8 ad57707dbc frontend: Mobiler Splitter stufenlos + Titelzeilen-Tap maximiert
Auf dem Smartphone hatte der Splitter zwei Fehler: nach dem ersten Ziehen
rastete er in eine feste Aufteilung und ließ sich nicht mehr bewegen (die
Titelzeile im Snap-Rand fing jeden weiteren Zug ab), und das Tippen auf eine
Titelzeile klappte das Panel nicht mehr aus (der Handler verlangte den
collapsed-Zustand, den ein freier Zug aufhob).

Mobil jetzt kontinuierlich statt diskret:
- Grid-Zeilen-Minima --pmin-d/--pmin-e = gemessene Kopfhöhen (syncPanelMins),
  dadurch bleiben beide Titelzeilen immer stehen (kein Snap/Collapse).
- Gutter-Drag ruft setMobileDrow() (klemmt --drow frei) — jederzeit beweglich.
- Tipp auf eine Titelzeile maximiert dieses Panel (das andere = Titelzeile).
- applyLayout ruft auf Mobil kein applySplit (behält --drow bei Moduswechsel);
  seg-Klick stoppt Propagation, damit er die Titelzeile nicht mit-maximiert.
- Default: Diagramm maximiert. Desktop behält das diskrete Modell + Buttons.

SPEC §9, DECISIONS D17 und frontend/CLAUDE.md nachgeführt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:24:05 +02:00
mhoennigandClaude Opus 4.8 8e8c44bf9d frontend: Günstigster-Pfad-Linie mit Stationspunkten + Nesting-Fix
- gestrichelte, geschwungene Petrol-Linie (Katmull-Rom) durch die Endknoten
  des Pfads; kräftig hinter den Knoten, abgetönte Kopie davor
- blasse, große Stationspunkte nur an echten Blättern (U-Bahn-Plan) — macht
  durchquerte Fremd-/Zwischenknoten eindeutig als "nicht Endknoten" erkennbar
- Linie + Punkte auch im SVG/PNG-Export (hinten/vorne-Schichtung)
- Overlays liegen in #out, erben dessen CSS-zoom (Punkte /zoom umgerechnet);
  Neuzeichnung nach render() und applyLayout()
- Beispiel erweitert: Frontend (M) -> PWA | Web+Nativ (Web[/]+Android+iOS)
- Layout-Fix: all-of unter einer any-of-Alternative wird nur horizontal schmal
  transponiert gestapelt (ul.or>li.has-and>ul.and), sonst schob der breite
  Fächer Alternativknoten und Elternbaum weit nach rechts
- Geist-Knoten-Label "Untergliederung fehlt" -> "…" (alle 9 Sprachen), die
  Erklärung bleibt im Tooltip; der lange Text verbreiterte gestapelte Zellen
- SPEC §5/§9 und DECISIONS D18 nachgeführt, Notiz in frontend/CLAUDE.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 18:52:45 +02:00
mhoennigandClaude Opus 4.8 c8f8160eca frontend: Günstigster-Pfad-Hervorhebung per Inversion + implizites M
- Umschalter im Diagramm-Kopf (Default an, Zustand in werkbaum-ui persistiert)
- nötige Knoten: all-of ⇒ alle Kinder, any-of ⇒ günstigste Alternative
  (kleinste rekursive Kosten; Gleichstand ⇒ erste); verworfene zählen nie mit
- Darstellung per Inversion: nicht benötigte Knoten treten blass/entsättigt
  zurück (kein Zusatzrahmen an den ohnehin dichten Knoten-Ecken)
- fehlende T-Shirt-Größe wird für die Kostenschätzung als M gewertet;
  invertiertes M-Badge (weiß mit Petrol-Rand) macht die Annahme sichtbar
- neue Tooltips i18n (DE-Quelle + 8 Übersetzungen, 50 Keys × 9)
- SPEC §9 (Günstigster Pfad) und DECISIONS D18 mitgeführt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 16:47:29 +02:00
mhoennigandClaude Opus 4.8 506bdccd2e frontend: Ein-Bereich-Ansicht + kompakte Legende/Sprachwahl auf Mobil
Auf kleinem Bildschirm (body.mobile per matchMedia, <=640px):
- Immer nur ein Bereich gross (Diagramm ODER Editor); der andere schrumpft
  auf seine Titelzeile und dient als Umschalter (Antippen holt ihn nach
  vorn). Splitter und Min/Normal/Max-Buttons entfallen.
- Legende von der Bereichs-Umschaltung entkoppelt: eigener Umschalter
  (#legendBtn) im Editor-Kopf statt der dauerhaften "AGENDA"-Zeile, damit
  sie zugeklappt keinen Platz kostet (neuer i18n-Key legendTooltip, 9 Spr.).
- Sprachwahl schlank: nur EN + aktive Sprache + "…" (klappt alle auf); die
  aktive Sprache wird auch als "extra" eingeblendet, Auto-Aufklappen entfaellt.
- Default Vollbild auf Mobil (nur ohne gespeicherte Nutzerwahl).

Verifiziert (headless, 375px): body.mobile gesetzt, Diagramm-Default +
Editor als Titelzeile, Umschalten per Tap, Legenden-Toggle, Sprachleiste
DE+EN+…/Aufklappen zeigt alle 9/aktives Extra bleibt sichtbar, Vollbild an,
keine Konsolenfehler; Desktop unveraendert (beide Panels, 4 Sprachen,
Legendenbutton verborgen). i18n konsistent (47 Keys x 9).

SPEC §9 (Kleiner Bildschirm) + DECISIONS D17 + frontend/CLAUDE.md ergaenzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 14:14:31 +02:00
mhoennigandClaude Opus 4.8 7c39da7b44 frontend: GUI-Zustand und Editortext im Browser speichern
Ohne Backend bleiben Einstellungen und Text ueber ein Neuladen erhalten
(localStorage). Gespeichert: Editortext (werkbaum-src) und GUI-Zustand
(werkbaum-ui: Modus, verworfene, Split-Zustand inkl. freier Drag-Groesse
--col/--drow, Zoom, Vollbild); Sprache bleibt in werkbaum-lang.

saveUI()/saveSrc() haengen an allen Zustandsaenderungen (Layout, Toggle,
Fenster-Buttons, Splitter-Drag, Zoom, Vollbild, Texteingabe);
restoreState() stellt beim Laden alles wieder her und unterdrueckt
waehrenddessen ueber restoring=true das Zurueckschreiben. Fehlender Text
faellt auf das Beispiel (INITIAL) zurueck, ein leerer bleibt leer.

Verifiziert (headless): Text, Modus, verworfene, Zoom, Vollbild,
Custom-Split (--col) und Minimier-Zustand ueberstehen ein Reload;
keine Konsolenfehler. Keine SPEC-Aenderung (Notation unberuehrt);
localStorage-Schluessel in frontend/CLAUDE.md dokumentiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 13:42:27 +02:00
Michael HoennigandClaude Opus 4.8 20b9d89f88 frontend: Darstellung horizontal/vertikal/kompakt + graue Any-of-Linien
Der bisherige „vertikal"-Toggle wird zur dreistufigen Auswahl
„horizontal / vertikal / kompakt", die zugleich das Seitenlayout wählt:

- horizontal: Diagramm oben, Editor unten (je volle Breite), Agenda
  rechts im Editor aufklappbar.
- vertikal: Editor links, Diagramm rechts; „all of" tritt rechts aus.
- kompakt: wie vertikal, aber „all of" läuft nach unten (spart Breite);
  Gate nur noch über den Linienstil codiert.

Zusätzlich wird das Petrol im Diagramm aufgegeben: any-of-Linien
(Sammelleiste und Abzweige) sowie der Rahmen der Alternative-Knoten sind
in allen Modi durchgehend gestrichelt bzw. grau (#6B7A8C). all-of bleibt
durchgezogen in Tinte. Petrol nur noch für UI-Akzente und das Logo.

Doku nachgezogen: SPEC §9 (Linienführung, drei Modi), DECISIONS D15,
frontend/CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 10:30:56 +02:00
Michael Hoennig ded894a4da initial, vibe-coded version, mostly frontend 2026-07-21 09:55:30 +02:00