Commit Graph
285 Commits
Author SHA1 Message Date
mhoennigandClaude Opus 4.8 e160015fc7 frontend: Mitgeliefertes Dokument heißt „Werkbaum" (Tippfehler korrigiert)
„Werkbank" war ein Vertipper, gemeint war der Projektname.

- WERKBAUM_NAME auf „Werkbaum" gesetzt.
- `loadDocs()` zieht den Namen bei bereits angelegten Dokumenten nach — aber
  nur, solange er unverändert der ausgelieferte „Werkbank" ist. Eine eigene
  Umbenennung des Nutzers bleibt stehen; ein fremdes Dokument, das zufällig
  „Werkbank" heißt, wird nicht angefasst (die Regel greift über die
  reservierte id, nicht über den Namen).

Verifiziert im Browser: Bestandszustand mit drei Dokumenten (Example,
id=werkbaum/„Werkbank", eigenes d9/„Werkbank") -> nach dem Laden ist nur
id=werkbaum umbenannt, d9 bleibt unberührt; frischer Zustand zeigt „Example"
und „Werkbaum" im Wähler, Wechsel rendert 75 Knoten ohne Warnung.
Vitest 37/37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 14:10:06 +02:00
mhoennigandClaude Opus 4.8 6fb45ff37a frontend: „Werkbank" — Werkbaums eigener Plan als mitgeliefertes Dokument
Der interessanteste Beispielbaum war nur über einen ?sourceUrl=-Link
erreichbar. Er steht jetzt direkt im Dokument-Wähler.

- Eine Quelle, keine Kopie: `?raw`-Import aus
  docs/examples/example-werkbaum.werkbaum — derselben Datei, die auch der
  ?sourceUrl=-Link lädt. Vite bettet sie in die eine Ausgabedatei ein (D19),
  nichts wird nachgeladen (D20). Die Beispieldatei ist damit Build-Eingabe.
- `seedShippedDocs()` legt das Dokument genau EINMAL an — auch für
  Bestandsnutzer mit vorhandener Dokumentenliste — und merkt sich das in
  `werkbaum-seeded`. Ohne Merker gäbe es nur schlechte Alternativen: entweder
  bekämen bestehende Nutzer es nie, oder ein bewusst gelöschtes Dokument
  kehrte bei jedem Laden zurück. Das aktive Dokument bleibt unverändert.
- Fester, nicht lokalisierter Name wie „Example" (D22). Der Reset setzt jetzt
  beide mitgelieferten Dokumente zurück; eigene bleiben unangetastet.

Verifiziert im Browser: frischer Zustand -> Example + Werkbank, aktiv bleibt
Example; Wechsel auf Werkbank rendert 75 Knoten ohne Warnung; Bestandsnutzer
mit eigener Liste bekommt Werkbank ergänzt, ohne aus seinem Dokument gerissen
zu werden; nach Löschen kehrt es nicht zurück. Build bleibt eine einzelne
Datei (361 kB), Vitest 37/37.

Offen (in D27 notiert): der Text wird nach dem Anlegen nicht mehr
aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 14:06:58 +02:00
mhoennigandClaude Opus 4.8 6888762ac8 frontend: Legende scrollbar + Splitter zwischen Editor und Legende
Die Legende wurde abgeschnitten statt zu scrollen — obwohl `.hint` seit jeher
`flex:1 1 auto; min-height:0; overflow:auto` trägt.

Ursache: Chrome legt den Inhalt eines <details> in das Pseudo-Element
`::details-content`. Damit war `.hint` kein Flex-Kind von `.agenda` mehr, die
Begrenzung griff nicht und der Kasten wuchs auf seine Inhaltshöhe (gemessen
585 px in einem 282 px hohen Container) und wurde geclippt. Eine Regel für
`::details-content` wirkte im Test, ist aber Chrome-eigen — Firefox und Safari
kennen das Pseudo-Element nicht.

- Die Legende ist deshalb kein <details> mehr, sondern `div.agenda` +
  `button.agenda-summary`; der Zustand hängt an der Klasse `open`
  (`aria-expanded` am Button, `#legendBtn` und Splitter-Sichtbarkeit über
  `setAgendaOpen()` synchron). `.hint` ist wieder echtes Flex-Kind und scrollt
  in allen Browsern.
- Neuer Splitter `#hintGutter` im selben Idiom wie der große (Pointer-Capture,
  Doppelklick setzt zurück): horizontal nebeneinander -> Breite `--hcol`,
  gestapelt (side/mobil) -> Höhe `--hrow`. Beide getrennt gehalten und in
  `werkbaum-ui` persistiert (samt Auf-/Zu-Zustand), damit ein Moduswechsel die
  jeweils andere Aufteilung nicht zerstört. Grenzen 90 px bis 85 %.
- Die 85-%-Obergrenze steht zusätzlich als max-width/max-height im CSS: die
  gespeicherte Größe ist ein fester px-Wert und würde den Editor sonst auf 0
  drücken, wenn das Panel später schrumpft (großer Splitter, Drehung,
  Bildschirmtastatur). Verifiziert mit --hrow:9999px -> gekappt bei 85 %.
- Nebenbei: der große Splitter war als einziges Bedienelement nicht lokalisiert
  (fest deutsche title/aria-label). Beide teilen sich nun `gutterTooltip`,
  dazu `gutterAria`/`hintGutterAria` — 9 Sprachen.

Verifiziert im Browser: Legende scrollt (247 px Fenster, 607 px Inhalt);
Ziehen in beiden Ausrichtungen inkl. Unter-/Obergrenze und Doppelklick-Reset;
`--hcol` und `--hrow` überleben den Moduswechsel; nach Neuladen sind
Auf-Zustand, Breite und beide Variablen wiederhergestellt; auf Mobil ist die
Summary-Zeile aus und der Kopf-Knopf steuert. Vitest 37/37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 14:02:56 +02:00
mhoennigandClaude Opus 4.8 4061362922 frontend: Sprung holt keine Bildschirmtastatur mehr herauf
Nach dem Sprung erschien auf Touch-Geräten sofort die virtuelle Tastatur und
nahm den halben Bildschirm — auch bei angeschlossener Bluetooth-Tastatur.

Das ist kein Fehler der App: Eine Webseite erfährt nichts über verbundene
Tastaturen, sie fordert nur Fokus an; alles Weitere entscheidet das OS (Android
hat dafür den Schalter „Bildschirmtastatur anzeigen" unter Physische Tastatur,
vielerorts an). Statt darauf zu verweisen, fordert der Sprung sie erst gar
nicht an:

- `jumpToLine()` setzt vor dem Fokussieren `inputmode="none"` — das unterdrückt
  nur die VIRTUELLE Tastatur, Hardware-Tastaturen tippen unverändert weiter.
- Die Sperre fällt, sobald der Nutzer das Textfeld selbst antippt
  (`pointerdown` läuft vor dem Fokus). Der Sprung ist damit „hinschauen", der
  erste Tipp ins Textfeld „bearbeiten".
- `newDoc()` hebt die Sperre ausdrücklich auf — bei einem neuen, leeren
  Dokument ist Tippen gemeint.

Verifiziert im Browser: Sprung setzt inputmode=none bei Fokus im Textfeld und
korrekter Zeilenmarkierung; Feld bleibt editierbar (nicht readOnly/disabled);
eigenes pointerdown entfernt das Attribut; erneuter Sprung setzt es wieder;
„Neues Dokument" fokussiert ohne Sperre. Vitest 37/37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 13:51:29 +02:00
mhoennigandClaude Opus 4.8 bbdecce75a frontend: Touch-Langdruck springt beim Loslassen (Fokus blieb sonst nicht)
Auf echten Touch-Geräten flackerte die Markierung im Editor nach dem langen
Druck nur kurz auf und der Fokus fiel sofort wieder heraus.

Ursache: Der Sprung lief im 500-ms-Timer, und `focus()` aus einem
Timer-Callback gilt in mobilen Browsern nicht als Nutzergeste — ein Textfeld
darf sich so nicht selbst fokussieren (sonst könnte jede Seite ungefragt die
Bildschirmtastatur aufziehen). `touchend` ist eine Nutzergeste.

- Der Timer setzt jetzt nur noch die Rückmeldung: der Zielknoten bekommt die
  Klasse `armed` mit demselben Petrol-Ring wie unter dem Alt-Zeiger.
  `jumpToLine()` läuft im touchend-Handler; Wischen bricht weiterhin ab.
- Zweiter Beitrag zum selben Symptom: die native Textauswahl bzw. das
  Link-Callout des Browsers startet bei derselben Druckdauer und riss die
  Auswahl an sich. Drei Schichten dagegen, weil keine allein überall wirkt:
  contextmenu-preventDefault während des Drucks (überall), `user-select:none`
  an .node für grobe Zeiger (@media hover:none and pointer:coarse) und
  `-webkit-touch-callout:none` (nur iOS; Chrome kennt es nicht mehr). Die
  frühere Regel `a.node{-webkit-touch-callout:none}` geht darin auf.

Verifiziert (synthetische TouchEvents): während des Drucks nur `armed` +
Petrol-Ring, Auswahl noch unverändert, Fokus noch nicht im Textfeld; beim
Loslassen Sprung, Fokus TEXTAREA, Klick unterdrückt; Wischen und kurzer Druck
springen nicht und lassen den Klick durch. Ring greift auch unterhalb einer
any-of-Gruppe. Vitest 37/37.

Einschränkung: Weder die Nutzergesten-Regel noch die nativen Langdruck-Gesten
lassen sich synthetisch auslösen — der eigentliche Beweis ist ein Test auf
echter Hardware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 13:41:48 +02:00
mhoennigandClaude Opus 4.8 c496850b88 frontend: Alt-Sprung auffindbar machen (Cursor-Rückmeldung + Legenden-Zeile)
Eine Modifier-Geste, die niemand kennt, ist keine Funktion. Zwei Maßnahmen:

- Solange Alt gedrückt ist, trägt #out die Klasse `alt`: alle Knoten zeigen
  `cursor:alias`, der Knoten unter dem Zeiger einen Petrol-Ring. Das wirkt
  auch auf verlinkten Knoten, wo der einfache Klick dem Link gehört und es
  sonst gar keine Rückmeldung gäbe. Der `blur`-Handler ist Pflicht — bei
  Alt+Tab kommt kein keyup mehr an, der Modus bliebe hängen.
- Die aufklappbare Legende endet mit einer abgesetzten Bedienungs-Zeile
  (`hint_jump` in 9 Sprachen, `.hint-op` mit gestricheltem Trenner), damit
  sie ihre Rolle als Notations-Legende behält.

Verworfen (in D25 samt Begründung festgehalten): einfacher Klick springt auf
Knoten ohne URL (~97 % der Knoten der Beispieldateien) — beiläufig auffindbar,
aber dieselbe Geste täte je nach Knoten Verschiedenes; sowie ein einmaliger
Hinweis nach dem ersten Sprung.

Verifiziert im Browser: ohne Alt Cursor auto bzw. pointer (Link), mit Alt
überall `alias`, nach Loslassen zurück, nach blur ohne keyup (Alt+Tab)
zurückgesetzt; Legenden-Zeile sichtbar und abgesetzt. Vitest 37/37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 13:26:43 +02:00
mhoennigandClaude Opus 4.8 1f6988b047 frontend: Sprung zwischen Diagramm und Text (beide Richtungen)
Knoten und Notationszeile sind nun verknüpft. Der Parser hängte die
Zeilennummer ohnehin schon an jeden Knoten — sie wandert jetzt als data-line
ins Markup.

- Diagramm -> Text: Alt+Klick markiert die ganze Zeile im Editor (Fokus,
  in Sicht gescrollt); Tastatur Alt+Enter, Touch langer Druck (500 ms,
  Wischen bricht ab). Ein zugeklapptes Editor-Panel wird zuerst geöffnet.
- Text -> Diagramm: der Knoten der Cursor-Zeile bekommt weißen Halo +
  Tinte-Ring und wird beim Zeilenwechsel ins Bild gescrollt.
- Alt statt einfachem Klick, weil ein Knoten mit URL als <a> den ganzen
  Kasten belegt (SPEC §6 bleibt unverändert). Der Handler ruft
  preventDefault() — sonst lädt Alt+Klick auf einen Link das Ziel herunter.
  Neuer Tooltip-Hinweis an jedem Knoten (i18n `jumpHint`, 9 Sprachen).
- Gescrollt wird über einen Spiegel-div, nicht über Zeilenhöhe x n: lange
  Zeilen brechen weich um, die naive Rechnung lag im Test bei 60 Zeilen um
  bis zu 525 px daneben.

Beim Verifizieren gefunden und behoben: `ul.or .node{box-shadow:none}` ist
spezifischer als `.node.current` und schluckte den Ring überall unterhalb
einer any-of-Gruppe — die Regel braucht den #out-Präfix.

Verifiziert im Browser (Alt+Klick auf <a>- und div-Knoten, Klick aufs
Größen-Badge, Klick/Enter ohne Alt unverändert, Alt+Enter, kurzer/langer
Druck und Wischen auf Mobil, Panel-Aufklappen aus dem minimierten Zustand,
Zeile nach Sprung immer im sichtbaren Band, Kommentar-/verworfene Zeilen
heben nichts hervor). Vitest 37/37 (3 neue data-line-Tests, Snapshots
aktualisiert). Dokumentiert als D25 + SPEC §9 + frontend/CLAUDE.md + README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 13:14:10 +02:00
mhoennigandClaude Opus 4.8 cdf843e2e3 docs: Eigene Endung .werkbaum + fünf Beispieldateien unter docs/examples/
Mit nur einem Beispiel ließ sich das Umschalten zwischen Dokumenten (D22)
nicht zeigen — jeder ?sourceUrl=-Link legt genau ein Dokument an (D23), erst
mehrere nacheinander geöffnete füllen den Wähler.

- Neue Endung `.werkbaum` (D24, SPEC §12): macht Notationstexte zuordenbar
  (spätere Editor-Zuordnung, Syntax-Highlighting, Öffnen/Speichern). Konvention,
  kein Vertrag — `?sourceUrl=` wertet weder Endung noch Content-Type aus, `.txt`
  bleibt gültig. `.gitattributes` führt `*.werkbaum` als Text (LF).
- docs/example-plan.txt -> docs/examples/example-plan-0.werkbaum (Inhalt bis
  auf die Kopfzeile unverändert); README-Links nachgezogen.
- Neu, je einen anderen Aspekt zeigend:
  example-plan-1 (Küche: kein Software-Plan, alternativenlastig — günstigster
  Pfad), example-plan-2 (Konferenz: breiter Fächer, viele Beteiligte),
  example-plan-3 (drei Wurzeln = drei Bäume nebeneinander, dazu ein Knoten
  ohne Größe für das implizite-M-Badge),
  example-werkbaum (Werkbaum selbst: Bestand + mögliche Weiterentwicklung,
  destilliert aus ROADMAP/TASKS/DECISIONS).
- README.md / README.de.md: Tabelle mit den fünf Live-Links samt Hinweis, dass
  sie nacheinander geöffnet den Dokument-Wähler füllen.

Verifiziert: alle fünf parsen headless ohne Warnungen und ohne gemischte Gates
(19/26/43/28/79 Knoten, 3 Wurzeln in Beispiel 3); Vitest 34/34 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 12:43:43 +02:00
mhoennigandClaude Opus 4.8 4d0fb9edd7 docs: Beispiel-Link auf den latest build (stabile Instanz hat sourceUrl noch nicht)
Der Live-Beispiel-Link zeigte auf werkbaum.javagil.de — die stabile Instanz
wird manuell deployt (scripts/deploy-prod.sh) und kennt ?sourceUrl= noch nicht
(verifiziert: 0 Treffer im ausgelieferten HTML). Der Parameter waere dort
stillschweigend ignoriert worden. Der Link zeigt nun auf die automatisch aus
main deployte Pages-Instanz, die das Feature hat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:41:28 +02:00
mhoennigandClaude Opus 4.8 ca98f0a055 docs: Beispiel-Plan als ladbare Textdatei + ?sourceUrl=-Abschnitt im README
Für ?sourceUrl= (D23) fehlte bislang eine ladbare Beispieldatei — im Repo lagen
nur Markdown-Dokumente (rendern als Chaos) und VERSION.

- docs/example-plan.txt: englischer Beispielplan, der die Notation vorführt —
  alle acht Status (inkl. [!] mit Warndreieck und einer verworfenen
  Alternative), beide Gates, Größen, Tags, URLs, Kommentare sowie zwei
  Geister-Knoten für die Untergliederungspflicht ab M.
- README.md / README.de.md: neuer Abschnitt „Loading a diagram from a URL" /
  „Diagramm aus einer URL laden" mit klickbarem Live-Beispiel, dem Hinweis auf
  URL-als-Dokumentname und der CORS-Einschränkung.

Verifiziert: Die Datei parst ohne Warnungen (18 Knoten, 7 sichtbare Status,
1 Warndreieck, 2 Geister-Knoten).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:40:11 +02:00
mhoennigandClaude Opus 4.8 1557a7ab0d frontend: Notationstext per ?sourceUrl= von einer URL laden
Der Editor kann den Text aus einer externen Textdatei beziehen:
  …/index.html?sourceUrl=https://example.org/plan.txt

- Das Geladene wird als eigenes Dokument (D22) geführt, dessen Name die URL
  ist; die id leitet sich aus der URL ab, derselbe Link aktualisiert also
  dieses Dokument, statt bei jedem Aufruf ein neues anzulegen. Eigene
  Dokumente bleiben unberührt. Die volle URL steht zusätzlich im Tooltip
  (der Name wird in der Titelzeile elidiert).
- Ist der Parameter gesetzt, wird bei jedem Laden neu geholt — die URL ist
  die Quelle der Wahrheit.
- Nur http(s) (relative Angaben werden aufgelöst), credentials:'omit';
  file:/data:/javascript: werden abgewiesen. Fremder Text ist ungefährlich:
  Labels werden escaped, der Parser erkennt als Link nur https?:// (SPEC §1).
- Fehler (CORS, 404, Netz) landen als zeilenlose Warnung `sourceLoad` im
  Warnbereich und nennen CORS ausdrücklich als wahrscheinliche Ursache; der
  bisherige Stand bleibt stehen. i18n in allen 9 Sprachen.
- Nebenbei: checkForUpdates() baut die Cache-Buster-URL nun über
  URL/searchParams — mit vorhandenem Query-String hätte '?t=' ein zweites
  '?' erzeugt.

Verifiziert headless (jsdom gegen den gebauten Bundle: Erfolg, CORS-Fehler,
404, ohne Parameter, verbotenes Schema) und im Browser gegen
raw.githubusercontent.com. Dokumentiert als D23 + SPEC §9 + frontend/CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:23:40 +02:00
mhoennigandClaude Opus 4.8 64e4e60b53 frontend: Dokument-Wähler — Beispiel englisch, Inline-Umbenennen, gezielter Reset
Behebt drei Punkte am Mehrfach-Dokument-Feature (D22):

- Beispiel-Dokument heißt jetzt fest „Example" (englisch, nicht mehr
  lokalisiert „Beispiel") und trägt die reservierte id `example`. Alt-Zustände
  (zufällige id, „Beispiel") werden beim Laden adoptiert, sofern der Text noch
  unverändert ist (=== INITIAL) — echte Inhalte nie.
- Umbenennen läuft inline im Menü (Textfeld: Enter bestätigt, Esc bricht ab,
  Blur bestätigt) statt über window.prompt, das in manchen Browser-Kontexten
  unterdrückt ist („Umbenennen funktioniert nicht").
- Reset (Debug-Knopf, non-prod) setzt nur noch das Beispiel-Dokument auf
  INITIAL/„Example" zurück und verwirft Ansichts-/Sprach-/Update-Flags; alle
  anderen Dokumente bleiben erhalten (vorher wurden pauschal alle werkbaum-*-
  Schlüssel gelöscht).

i18n-Keys docDefaultName/docRenamePrompt entfallen (nicht mehr gebraucht).
D22 + frontend/CLAUDE.md nachgezogen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 12:01:43 +02:00
mhoennigandClaude Opus 4.8 36d79b82c4 frontend: Mehrere umschaltbare Dokumente (Wähler in der Editor-Titelzeile)
Der Editor kann jetzt mehrere Notationstexte halten und zwischen ihnen
umschalten. Der Name des aktiven Dokuments ersetzt die feste Beschriftung
„Struktur (Text)" und öffnet als Dropdown den Wähler mit Wechseln, Neu,
Umbenennen und Löschen.

- Datenmodell (noch kein Backend): Dokumente als [{id,name,text}] in
  localStorage (werkbaum-docs), aktives per id in werkbaum-active; der
  aktive Text bleibt zusätzlich in werkbaum-src gespiegelt.
- Migration verlustfrei: fehlt werkbaum-docs, wird der bestehende Einzeltext
  (oder INITIAL) in ein Dokument gepackt. Reset und Löschen des letzten
  Dokuments säen das Beispiel neu.
- Jedes Dokument ist nur Text + Name (kein Strukturformat, D14) —
  vorwärtskompatibel zum geplanten Backend (D13). Ansichts-State (Modus,
  Zoom, Aufteilung) bleibt global.
- i18n in allen 9 Sprachen; Doc-Namen bleiben Nutzerdaten (nicht übersetzt).
- Dokumentiert als ADR D22 (DECISIONS.md) + frontend/CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 11:48:31 +02:00
mhoennigandClaude Opus 4.8 5a0a06b185 frontend: Beispiel-PSP durchgehend englisch (Web+Nativ → Web+Native)
Letzter deutscher Rest im INITIAL-Beispiel entfernt, damit das
Standard-Diagramm ein breiteres Publikum anspricht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 11:08:26 +02:00
mhoennigandClaude Opus 4.8 6aee82949b ci: Build-Node von 20 (EOL) auf 24 (aktueller Active LTS) heben
Node 20 ist seit ~April 2026 End-of-Life. Node 24 "Krypton" ist der aktuelle
Active LTS und passt zur node24-Laufzeit der Actions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 11:01:39 +02:00
mhoennigandClaude Opus 4.8 c2181e3252 ci: Actions auf node24-Majors heben (Node-20-Deprecation)
GitHub erzwingt node24 für node20-Actions (Deprecation vom 2025-09-19). Die fünf
Pages-Actions auf ihren jeweils ersten node24-Major gehoben:
checkout v4->v5, setup-node v4->v5, configure-pages v5->v6,
upload-pages-artifact v3->v5, deploy-pages v4->v5.

Der Build-`node-version: 20` (für npm ci/test/build) ist davon unberührt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 11:00:12 +02:00
mhoennigandClaude Opus 4.8 17d354059e frontend: Update-Erkennung über vollen Content-Hash statt 300-Zeichen-Fenster
Die Erkennung hashte nur die ersten + letzten 300 Zeichen der Seite. Da der
Editor eine self-contained Datei ist (D19), liegen der gesamte App-Code UND die
Footer-Version als inline-Bundle in der Mitte — außerhalb des abgetasteten
Fensters. Anfang/Ende sind Build-übergreifend identisch, daher wurde eine neue
Version nie erkannt (nur die ETag änderte sich -> "Metadaten geändert, aber
Inhalt gleich").

- hashContent() (cyrb53) über den GESAMTEN Text als alleinige Wahrheit.
- ETag/Last-Modified-Vergleich entfernt: GitHub Pages liefert je Cache-Knoten
  wechselnde ETags für identischen Inhalt und löste damit die irreführende
  Metadaten-Meldung aus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:52:16 +02:00
mhoennigandClaude Opus 4.8 29e144eddf frontend: UI-Default-Sprache aus der Browsersprache (Fallback DE)
Ohne gespeicherte werkbaum-lang wählt detectLang() die erste vom Browser
gemeldete Sprache (navigator.languages), für die eine Übersetzung existiert
(Primär-Subtag; de-AT->de, zh-Hans->zh), sonst Deutsch. Greift auch nach dem
Reset. Deutsch bleibt Quellsprache. Als D21 dokumentiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:15:23 +02:00
mhoennigandClaude Opus 4.8 095dbfb63e frontend: High-Risk-Warndreieck + Prod-Gating der Test-Hilfen
- [!]-Knoten trägt jetzt ein Warndreieck (⚠) an der oberen linken Ecke,
  zusätzlich zur orangen Färbung — im Diagramm, im SVG/PNG-Export und
  barrierefrei (aria-hidden, Status steckt im aria-label). Tooltip in allen
  9 Sprachen.
- SPEC §4: [!]/highrisk-Status nachgetragen (war beim ersten Hinzufügen
  übersprungen), inkl. Warndreieck-Kennzeichnung.
- Beispiel-PSP: Backend als High-Risk ([!]) markiert.
- Debug-Panel + Reset-Button nur außerhalb des Prod-Builds (isProdBuild via
  VITE_BUILD_BADGE); im build:prod eliminiert esbuild den Zweig als toten
  Code. Update-Check + Banner bleiben in allen Builds aktiv.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:00:51 +02:00
mhoennigandClaude Haiku 4.5 90b5deb45b fix: Move reset button to header next to fullscreen
Reset-Button gehört in den Header neben dem Fullscreen-Button,
nicht ins Debug-Panel.

Changes:
- Reset-Button (🔄) oben rechts neben Fullscreen-Toggle
- Benutzt gleiche Styling-Klasse (fsbtn) für Konsistenz
- Title: \"App auf Defaults zurücksetzen (für Testing)\"
- Debug-Panel vereinfacht (nur Log-Text, kein Button mehr)

Intuitivere Platzierung für Testing-Feature.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 09:22:22 +02:00
mhoennigandClaude Haiku 4.5 f284e36458 improvement: Add reset button to debug panel
Reset-Button gehört ins Debug-Panel (Testing-Feature):

Changes:
- Debug-Panel erweitert mit flexiblem Layout (flex-column)
- 🔄 Reset-Button hinzugefügt (klickbar, hover-Effekt)
- ✕ Close-Button für Panel (zum Schließen)
- Bessere Styling: halbtransparente Buttons, klare Hover-States

Reset ist nur während Testing nötig — gehört ins Debug-Panel,
nicht in die Haupt-UI. Das 📦-Icon im Footer bleibt für
verfügbare Updates.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:57:30 +02:00
mhoennigandClaude Haiku 4.5 0a8bab24fb refactor: Move update notification icon to footer
Statt Reset-Button im Footer:
- 📦-Symbol rechts neben Version (nur wenn Update verfügbar)
- Klick zeigt die Update-Benachrichtigung wieder an
- Hilft, wenn man den Banner auf \"Später\" verschoben hat

Features:
- Icon nur sichtbar wenn werkbaum-update-available Flag gesetzt
- Hover-Effekt: Icon wird größer (scale 1.2)
- Tooltip erklärt Funktion
- Auto-Update nach Benachrichtigung

Reset-Button entfernt (Nutzer wünscht sich das oben neben Fullscreen,
wenn überhaupt benötigt).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:47:36 +02:00
mhoennigandClaude Haiku 4.5 ceabc34bbe improvement: Make reset button more visible in footer
Reset-Button war zu unauffällig (nur 🔄-Symbol).

Jetzt prominenter:
- Label: \"🔄 Reset\" statt nur 🔄
- Border: dünne Linie um den Button
- Hover-Effekt: Farbe ändert sich zu Petrol (#0F766E)
- Cursor zeigt es ist klickbar

Button sitzt im Footer zwischen Versionsnummer und Links.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:44:35 +02:00
mhoennigandClaude Haiku 4.5 b94f58fcd5 improvement: Clarify update detection logging
Besseres Logging zum Verständnis von Cache-Verhalten:

- \"✓ Alles aktuell\" = Content + Headers gleich
- \"⚙ Metadaten geändert, aber Inhalt gleich\" = Headers unterschiedlich,
  aber HTML-Inhalt identisch (nur Build-Metadaten/ETag geändert)
- \" NEUE VERSION ERKANNT!\" = echter neuer Inhalt entdeckt

Hilft zu verstehen, ob GitHub Pages cacht oder ob wirklich
eine neue Version deployed wurde.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:37:39 +02:00
mhoennigandClaude Haiku 4.5 3bc8f984a6 fix: Simplify update detection – back to GET with timestamp
HEAD-Request hat auf GitHub Pages zu Netzwerkfehlern geführt.
Vereinfachter Ansatz:

- Zurück zu einfachem GET-Request (statt HEAD)
- Cache-Busting via Timestamp: location.href + '?t=' + Date.now()
- ETag/Last-Modified Vergleich + Content-Hash Fallback
- Besseres Error-Logging: unterscheidet "Netzwerk/CORS" von anderen Fehlern

Robuster auf instabilen Netzwerk-Bedingungen (Smartphone).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:32:06 +02:00
mhoennigandClaude Haiku 4.5 e7db4d53d5 feat: Add reset-to-defaults button for testing
Neuer Button im Footer (🔄) zum Zurücksetzen aller Einstellungen:

Features:
- Löscht alle werkbaum-* localStorage-Einträge (Editor, Einstellungen, Update-Flags)
- Confirm-Dialog mit Warnung (Editor-Inhalt geht verloren)
- Reagiert auf alle Bildschirmgrößen
- Nach Confirm: kurze Verzögerung (Logging sichtbar), dann auto-reload

Hilfreich zum Testen:
- Update-Detection testen (Editor-Inhalt neu setzen)
- High-Risk-Status sehen (mit Reset auf Defaults und neuem Beispiel)
- Alle Einstellungen auf Default zurückbringen

Der Button sitzt unauffällig im Footer als 🔄-Symbol.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:25:06 +02:00
mhoennigandClaude Haiku 4.5 d5ef3ff800 fix: Clear update flag before reload when user clicks "Jetzt laden"
Problem: Nach Klick auf "Jetzt laden" wird die Seite neu geladen,
aber das werkbaum-update-available Flag bleibt in localStorage.
Beim Laden der neuen Version wird der alte Check noch vorhanden,
also wird der Banner wieder angezeigt (auch wenn die neue Version
schon da ist).

Lösung: Vor window.location.reload() das Flag löschen.
Damit nach dem Reload die neue Version sauber geladen wird.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:13:14 +02:00
mhoennigandClaude Haiku 4.5 e9519ed7ae fix: Improve update detection with ETag/Last-Modified headers
Robustere Update-Detection für unzuverlässige Netzwerk-Bedingungen:

Neuer Ansatz (statt nur Content-Hash):
1. HEAD-Request prüft ETag + Last-Modified Headers
   - Effizient: Kein großer HTML-Download nötig
   - Verlässlich: Standard HTTP-Cache-Header
2. Wenn Headers gleich → "✓ Alles aktuell" (schnell raus)
3. Nur bei Header-Änderung → volles HTML fetchen + Content-Hash-Vergleich
4. Speichert ETag + Last-Modified für nächste Prüfung

Besseres Logging:
- " NEUE VERSION ERKANNT!" (highlight)
- "⚠ " + Fehlerdetails (was genau fehlgeschlagen ist)
- "✓ Erste Prüfung – Hash gespeichert"
- "✓ Headers änderten sich, aber Content gleich"

Das verhindert:
- Falsche "alles aktuell" wenn Fetch fehlschlägt
- Mehrfaches Fetchen der großen HTML
- "Failed to Fetch" auf unstabilen Verbindungen

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 08:08:24 +02:00
mhoennigandClaude Haiku 4.5 68322b13d9 feat: Add High Risk status [!] for uncertain effort estimation
Neuer Status-Code [!] für Items mit unbekannter Größe/Aufwand:

Parser:
- STATUS_BY_CODE erweitert: '!' -> {key:'highrisk', name:'High Risk – Aufwand unklar'}

Style:
- .node.st-highrisk: Pastellorange (#FFE5CC) mit orange Rahmen (#F97316)
- .chip.st-highrisk: Gleiche Farben für Agenda-Beispiele

i18n (alle 9 Sprachen):
- DE: "High Risk"
- EN: "high risk"
- ES: "alto riesgo"
- FR: "risque élevé"
- PL: "wysokie ryzyko"
- RU: "высокий риск"
- HI: "उच्च जोखिम"
- ZH: "高风险"
- JA: "高リスク"

Tests:
- parser.test.js aktualisiert: 8 Status-Codes (waren 7)

Damit können Items mit ungeklärtem Aufwand im Lean-Pathfinding-Prozess
gekennzeichnet werden – wichtig für Risk-Flagging in Phase 1.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 07:52:02 +02:00
mhoennigandClaude Haiku 4.5 0454f0f50a fix: Add visible debug panel for update detection
Neue Debug-Features für Fehlersuche ohne Dev-Konsole:

- Update-Check alle 15 Sekunden (statt 60, für schnelleres Feedback beim Test)
- Logging in localStorage mit Zeitstempel
- Sichtbares Debug-Panel unten rechts (schwarz, Petrol-Rand)
  * Zeigt die letzten 6 Update-Check-Einträge
  * Klick zum Schließen
  * Hilft ohne Browser Dev-Tools zu sehen, was passiert

Logging zeigt:
-  Neue Version erkannt!
- ✓ Alles aktuell
- ⚠ Fehler: ... (falls Fetch fehlschlägt)

So kann Update-Detection auch auf Smartphone (ohne Dev-Konsole) debugged werden.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 07:40:31 +02:00
mhoennigandClaude Haiku 4.5 ae207789ef docs: Update subtitle short form – concise for mobile
Kurzversionen angepasst für kleine Bildschirme:
- DE: „PSP-Editor mit Lean Pathfinding"
- EN: „WBS editor with Lean Pathfinding"
- ES: „Editor EDT con Lean Pathfinding"
- FR: „Éditeur WBS avec Lean Pathfinding"
- PL: „Edytor WBS z Lean Pathfinding"
- RU: „Редактор СДР с Lean Pathfinding"
- HI: „WBS संपादक Lean Pathfinding के साथ"
- ZH: „WBS 编辑器和 Lean Pathfinding"
- JA: „WBS エディター & Lean Pathfinding"

Testcommit für Update-Detection.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 07:28:26 +02:00
mhoennigandClaude Haiku 4.5 a9232ecbe7 fix: Vereinfache Update-Detection – Client-seitig statt Service Worker
Service Worker mit Blob-URL funktionierte nicht zuverlässig (blob: Protocol
nicht registrierbar in einigen Browsern). Neue Lösung:

- Client-seitige Prüfung statt Service Worker
- Alle 60 Sekunden fetch() der index.html (mit Cache-Busting via Timestamp)
- Einfacher Hash-Check (erste + letzte 200 Zeichen des Content)
- localStorage speichert Hash zum Vergleichen
- Update-Flag wird gesetzt wenn sich Hash ändert
- Benachrichtigung nur wenn User aktiv ist (Page Visibility API)
- Erste Prüfung nach 3 Sekunden, dann alle 60 Sekunden

Viel zuverlässiger als der frühere SW-Ansatz. Funktioniert überall ohne Browser-Limit.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 07:22:12 +02:00
mhoennigandClaude Haiku 4.5 475b4d6fef docs: Update subtitle – PSP & Lean Pathfinding prominence
Texte unter dem Titel angepasst, um Work Breakdown Structure (PSP/WBS) und
Lean Pathfinding gleich prominent zu zeigen. Feature-Tree nur erwähnt, wenn
Platz ist. Kurzversionen auf Mobile: „PSP · Lean Pathfinding Editor".

Alle 9 Sprachen angepasst (DE/EN/ES/FR/PL/RU/HI/ZH/JA).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 07:05:02 +02:00
mhoennigandClaude Haiku 4.5 cad7a8bb6d frontend: Update-Detection überarbeitet - nur anzeigen wenn User aktiv ist
- Service Worker prüft im Hintergrund auf neue Versionen (jede 5 Minuten)
- Update-Status wird in localStorage gespeichert
- Benachrichtigung erscheint nur wenn:
  * User zur App zurückkommt (Page Visibility API)
  * Oder beim initialen Laden wenn Update verfügbar
- "Später" Button zum Dismissieren, "Jetzt laden" für Reload
- Keine Browser-Permission nötig
- localStorage-Daten bleiben beim Reload erhalten
- Pragmatisch: User sieht Updates nur wenn er arbeitet

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 06:48:16 +02:00
mhoennigandClaude Haiku 4.5 a9d2cf3054 frontend: Service Worker für Update-Detection hinzufügen
- Service Worker prüft periodisch auf neue Versionen (jede Minute)
- Bei verfügbarem Update: Benachrichtigung oben auf der Seite
- "Jetzt laden" Button triggert page reload
- Benachrichtigung verschwindet nach 30s oder bei Klick
- Service Worker als Blob registriert (Single-File-App kompatibel)
- localStorage-Daten bleiben erhalten beim Reload

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 06:37:30 +02:00
mhoennigandClaude Haiku 4.5 1c92c66c18 frontend: Fix duplicate variable declarations in ResizeObserver scope
- Rename variables to avoid conflicts with existing declarations
- Wrap ResizeObserver code in IIFE for local scope isolation
- Fixes build error: 'editorPanel' and 'legendBtn' already declared

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 06:25:08 +02:00
mhoennigandClaude Haiku 4.5 7e30a3217a frontend: Editor-Panel Copy & Agenda Buttons nur sichtbar wenn Panel offen genug ist
- ResizeObserver überwacht .editor-body Höhe
- Copy und Agenda Buttons werden versteckt wenn Body-Höhe < Head-Höhe
- Schwellwert: Buttons nur sichtbar wenn mindestens Titelzeilenhöhe Inhalt vorhanden ist

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 06:09:20 +02:00
mhoennigandClaude Haiku 4.5 a4ea1f760a frontend: Konsistente Höhe für Zoom-Controls in collapsed/expanded State
- Feste Höhe von 28px für .zoomctl (collapsed und expanded)
- Padding für .zoomToggle angepasst auf 4px 8px (gleich wie andere Buttons)
- SVG-Größe auf 16x16 optimiert

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 05:52:47 +02:00
mhoennigandClaude Haiku 4.5 640cb95c27 frontend: Zoom-Controls mit Auto-Collapse-Funktion
- Zoom-Controls collapse automatisch nach 3 Sekunden Inaktivität
- Toggle-Button mit Lupe+Plus-Symbol zum Expandieren der Controls
- Position weiter nach rechts-unten verschoben (6px)
- Event-Delegation für zuverlässige Click-Erfassung
- Lazy-Loading der DOM-Elemente für robuste Initialisierung

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-22 21:16:03 +02:00
mhoennig 154e3e92e2 docs: LEAN-PATHFINDING.md hinzugefügt (Feature-Roadmap)
Erstellt eine detaillierte Roadmap für Lean Pathfinding: Abhängigkeits-Tracking, Velocity-basierte Kostenschätzung, Kritischer Pfad, Optimierungs-Tools, KI-Features und Integrationen.
2026-07-22 11:59:18 +02:00
mhoennig a334c60fd1 frontend: Add privacy link with multilingual support
Introduced a "Privacy" link in the footer, localized for supported languages. Default to German for DE UI, and English for all others.
2026-07-22 11:20:38 +02:00
mhoennig a92a87f135 frontend: kleinere UI-Bereinigungen (Code & i18n)
- Redundante Rückgabewerte in `overlaySvg()` entfernt
- `aria-labelledby` des Editors ersetzt durch `aria-label` für klarere i18n-Texte
2026-07-22 11:07:42 +02:00
mhoennig 6f9854db52 frontend: Add ARIA enhancements for better accessibility
Attached the `aria-labelledby` attribute to the editor textarea and added an ID to the editor title. Corrected the relative path for the script reference.
2026-07-22 10:51:25 +02:00
mhoennig ae8c32786d frontend: Cleanup mobile viewport height logic for better compatibility
Removed redundant CSS fallbacks for mobile viewport height and replaced them with a streamlined `@supports` block to handle `dvh`-capable browsers. Clarified comments to reduce warnings and improve maintainability.
2026-07-22 10:44:56 +02:00
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 0b6438c54d frontend: Footer-Credit "Vibe-Coded" -> "Built with Claude Code"
"Claude Code" bleibt als Produktname unveraendert. Der Credit ist hartkodiert
(kein data-i18n) und damit wie zuvor sprachunabhaengig englisch — keine
Uebersetzungen betroffen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:47:40 +02:00
mhoennigandClaude Opus 4.8 350b0db7ee scripts/deploy-prod: Ziel aus Wurzel-.env statt scripts/deploy.env
DEPLOY_TARGET wird jetzt aus der .env im Repo-Wurzelordner gelesen (die von der
bestehenden .gitignore-Regel ohnehin ignoriert wird; Vite liest sie nicht, da
dessen Env unter frontend/ liegt). scripts/deploy.env(.example) entfaellt; neue
Vorlage .env.example im Wurzelordner. Argument hat weiterhin Vorrang, ~ bleibt
fuer die Remote-Seite erhalten (kein source).

.gitignore: eigene scripts/deploy.env-Zeile zurueckgenommen, Kommentar an der
.env-Regel ergaenzt. README (de/en) entsprechend aktualisiert.

Lokal verifiziert: Fallback liest aus Wurzel-.env, .env ignoriert, .env.example
getrackt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 07:15:32 +02:00
mhoennigandClaude Opus 4.8 6b83af7d73 scripts/deploy-prod: Ziel aus git-ignorierter scripts/deploy.env (Fallback)
Ohne Ziel-Argument liest das Skript DEPLOY_TARGET aus scripts/deploy.env
(git-ignoriert; Vorlage scripts/deploy.env.example ist eingecheckt). Argument
hat weiterhin Vorrang.

Der Wert wird bewusst NICHT via `source` gelesen, weil bash bei `host:~/pfad`
die Tilde nach dem ':' lokal expandieren wuerde; stattdessen roh ausgelesen,
Whitespace/CR + umgebende Quotes gestrippt, das ~ bleibt fuer die Remote-Seite
erhalten. Lokal verifiziert: Fallback greift, ~ bleibt in unquoted/single-/
double-quoted erhalten (kein lokales $HOME), Quotes entfernt.

.gitignore: scripts/deploy.env ergaenzt (die vorhandene .env-Regel greift wegen
des abweichenden Dateinamens nicht). README (de/en) aktualisiert; Beispielpfad
auf die htdocs-ssl-Direktaufschaltung umgestellt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 07:05:40 +02:00
mhoennigandClaude Opus 4.8 ecf767cfb5 scripts/deploy-prod: ACME-Challenge vor --delete schuetzen
rsync --filter='protect /.well-known/***' bewahrt am Ziel liegende
.well-known-Dateien (Let's-Encrypt-Challenge waehrend einer Erneuerung) vor dem
--delete, obwohl sie nicht in der Quelle liegen. Verhindert die Race Condition,
dass ein Deploy eine laufende Zertifikatserneuerung abraeumt. Bedient
Hostsharing die Challenge ausserhalb des Docroots, ist es ein No-op.

Lokal verifiziert: .well-known/acme-challenge/testtoken ueberlebt den Deploy,
eine sonstige Alt-Datei wird weiterhin per --delete entfernt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 06:58:14 +02:00
mhoennigandClaude Opus 4.8 483bd89d25 scripts/deploy-prod: web-taugliche Rechte erzwingen (--chmod=D755,F644)
mktemp -d legt das Staging-Verzeichnis mit 0700 an; rsync -a uebertrug diesen
Modus auf das Ziel-Verzeichnis, sodass der Webserver es nicht betreten konnte
(Apache 403 "Server unable to read htaccess file, denying access to be safe" —
die .htaccess-Suche selbst ist normal, der Zugriff scheiterte an den Rechten).

Fix: rsync mit --chmod=D755,F644, unabhaengig von lokalen mktemp-/umask-Rechten.
Lokal verifiziert: Ziel wird 755, Dateien 644 (auch wenn das Ziel vorher 700 war).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 06:31:35 +02:00