diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 7515805..ba39be1 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -619,3 +619,37 @@ Grenzen: mindestens 90 px, höchstens 85 % — die Obergrenze steht **zusätzlic als `max-width`/`max-height` im CSS**, weil die gespeicherte Größe ein fester px-Wert ist: schrumpft das Panel später (Zug am großen Splitter, Drehung, Bildschirmtastatur), würde der Editor sonst auf 0 gedrückt. + +## D27 — „Werkbank": Werkbaums eigener Plan als mitgeliefertes Dokument +Neben dem Beispiel (D22) liegt ein zweites mitgeliefertes Dokument im Wähler: +**„Werkbank"** — Werkbaum selbst, mit Werkbaum geplant (Bestand + mögliche +Weiterentwicklung). Damit ist der interessanteste Beispielbaum ohne Umweg über +einen `?sourceUrl=`-Link erreichbar; zugleich dient er als lebende +Projektübersicht. + +**Eine Quelle, keine Kopie.** Der Text wird per `?raw`-Import aus +`docs/examples/example-werkbaum.werkbaum` gezogen — derselben Datei, die auch +der `?sourceUrl=`-Link lädt. Vite bettet sie beim Build in die eine Ausgabedatei +ein (D19), es wird nichts nachgeladen (D20). Eine zweite, abgetippte Fassung im +Quelltext würde unweigerlich auseinanderlaufen. (Nebenwirkung: die Beispieldatei +ist damit **Build-Eingabe** — Umbenennen bricht den Build. `vite.config.js` +erlaubt den Zugriff außerhalb des Roots bereits über `server.fs.allow: ['..']`, +eingeführt für das Favicon.) + +**Genau einmal angelegt.** `seedShippedDocs()` fügt das Dokument auch +Bestandsnutzern hinzu, die schon eine Dokumentenliste haben, und merkt sich das +in `werkbaum-seeded`. Ohne diesen 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 beim Anlegen +unverändert — niemand wird aus seinem Text gerissen. + +**Fester Name, nicht lokalisiert** — wie „Example" (D22): Dokumentnamen sind +Nutzerdaten. Der Reset (D22) setzt jetzt **beide** mitgelieferten Dokumente auf +ihren Auslieferungsstand zurück; eigene Dokumente bleiben weiterhin unangetastet. + +**Offen:** Der Text wird nach dem Anlegen nicht mehr aktualisiert. Erscheint +später eine neuere Fassung des Plans, sieht sie nur, wer das Dokument löscht und +zurücksetzt oder den `?sourceUrl=`-Link öffnet. Ein Ausbau könnte in +`werkbaum-seeded` statt '1' eine Versionsnummer ablegen und ein **unverändertes** +Dokument nachziehen (bearbeitete nie) — dieselbe Adoptions-Regel wie beim +Beispiel in D22. diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index 760b7c6..b628d0e 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -106,6 +106,13 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der `werkbaum-ui`/`werkbaum-lang`/Update-Flags — **andere Dokumente bleiben stehen** (nicht mehr pauschal alle `werkbaum-*` löschen!). Das letzte gelöschte Dokument wird als Beispiel neu gesät. +- Mitgeliefertes Dokument „Werkbank" (D27): `app.js` importiert + `../../docs/examples/example-werkbaum.werkbaum?raw` — die Beispieldatei ist + damit **Build-Eingabe**, Umbenennen/Verschieben bricht den Build (Zugriff + außerhalb des Roots erlaubt `server.fs.allow:['..']`). `seedShippedDocs()` legt + es **einmalig** an (auch für Bestandsnutzer) und merkt sich das in + `werkbaum-seeded`; ohne den Merker käme ein gelöschtes Dokument bei jedem Laden + zurück. `resetToDefaults()` setzt beide mitgelieferten Dokumente zurück. - Umbenennen ist **inline** (kein `window.prompt` — in manchen Browser-Kontexten unterdrückt): `renameDoc()` setzt `renamingId`, `renderDocMenu()` rendert dann ein `` (Enter = `commitRename`, Esc = `cancelRename`, diff --git a/frontend/src/app.js b/frontend/src/app.js index dcfc855..f234c2f 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -3,6 +3,11 @@ import { parse } from './parser.js'; import { computeCheapSet } from './model.js'; import { esc, renderTreeHtml } from './render.js'; import { formatWarning } from './warnings.js'; +/* Werkbaum, mit Werkbaum geplant — als mitgeliefertes Dokument „Werkbank" (D27). + Dieselbe Datei, die auch per ?sourceUrl= geladen werden kann; `?raw` bettet + sie beim Build in die eine Ausgabedatei ein (D19), es wird nichts nachgeladen + (D20). Quelle bleibt docs/examples/ — keine Kopie, die auseinanderläuft. */ +import WERKBAUM_DOC from '../../docs/examples/example-werkbaum.werkbaum?raw'; const INITIAL = `%% Project structure – Sprint 14 [~] Website relaunch (XL) https://wiki.example.com/relaunch @@ -1296,6 +1301,7 @@ fsBtn.addEventListener('click', () => { erhalten. Sprache liegt weiterhin in 'werkbaum-lang'. */ const LS_UI = 'werkbaum-ui', LS_SRC = 'werkbaum-src'; const LS_DOCS = 'werkbaum-docs', LS_ACTIVE = 'werkbaum-active'; +const LS_SEEDED = 'werkbaum-seeded'; /* mitgelieferte Dokumente schon angelegt? */ let restoring = false; /* unterdrückt Speichern während des Wiederherstellens */ let hadStoredUI = false; /* gab es beim Laden schon gespeicherte GUI-Einstellungen? */ @@ -1310,6 +1316,9 @@ let hadStoredUI = false; /* gab es beim Laden schon gespeicherte GUI-Einstellun unabhängig von der UI-Sprache englisch — wie der Beispieltext selbst (breiteres Publikum). */ const EXAMPLE_ID = 'example', EXAMPLE_NAME = 'Example'; +/* Zweites mitgeliefertes Dokument: Werkbaum selbst (D27). Fester Name wie beim + Beispiel — Dokumentnamen sind Nutzerdaten und werden nicht übersetzt (D22). */ +const WERKBAUM_ID = 'werkbaum', WERKBAUM_NAME = 'Werkbank'; let docs = []; /* [{id, name, text}] */ let activeId = null; function uid(){ return 'd' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7); } @@ -1329,6 +1338,20 @@ function persistDocs(){ if(d) localStorage.setItem(LS_SRC, d.text); /* Spiegel für Fallback/Migration */ }catch(_){} } +/* „Werkbank" (D27) genau EINMAL anlegen — auch für Bestandsnutzer, die schon + eine Dokumentenliste haben. Der Merker `LS_SEEDED` sorgt dafür, dass ein + bewusst gelöschtes Dokument nicht bei jedem Laden zurückkehrt; ein späterer + Ausbau kann dort eine Versionsnummer statt '1' ablegen. */ +function seedShippedDocs(){ + let seeded = null; + try{ seeded = localStorage.getItem(LS_SEEDED); }catch(_){} + if(seeded) return; + if(!docs.some(d => d.id === WERKBAUM_ID)){ + docs.push({ id: WERKBAUM_ID, name: WERKBAUM_NAME, text: WERKBAUM_DOC }); + } + try{ localStorage.setItem(LS_SEEDED, '1'); }catch(_){} +} + /* Aus dem localStorage laden; bei fehlender Dokumentenliste den bestehenden Einzeltext (oder INITIAL) als erstes Dokument migrieren. */ function loadDocs(){ @@ -1352,6 +1375,7 @@ function loadDocs(){ docs[0].id = EXAMPLE_ID; docs[0].name = EXAMPLE_NAME; } + seedShippedDocs(); activeId = docs.some(d => d.id === a) ? a : docs[0].id; } function saveSrc(){ @@ -2023,14 +2047,20 @@ function resetToDefaults(){ ['werkbaum-ui','werkbaum-lang','werkbaum-html-hash','werkbaum-update-available','werkbaum-update-log'] .forEach(k => { try{ localStorage.removeItem(k); }catch(_){} }); - /* Nur das Beispiel-Dokument auf den Ausgangstext zurücksetzen; existiert es - nicht (mehr), wird es neu angelegt. Andere Dokumente bleiben unberührt. */ - const ex = docs.find(d => d.id === EXAMPLE_ID); - if(ex){ ex.text = INITIAL; ex.name = EXAMPLE_NAME; } - else { docs.unshift({ id: EXAMPLE_ID, name: EXAMPLE_NAME, text: INITIAL }); } + /* Nur die MITGELIEFERTEN Dokumente auf ihren Ausgangstext zurücksetzen; + existiert eines nicht (mehr), wird es neu angelegt. Eigene Dokumente des + Nutzers bleiben unberührt (D22/D27). */ + const reseed = (id, name, text, vorn) => { + const d = docs.find(x => x.id === id); + if(d){ d.text = text; d.name = name; } + else if(vorn) docs.unshift({ id, name, text }); + else docs.push({ id, name, text }); + }; + reseed(EXAMPLE_ID, EXAMPLE_NAME, INITIAL, true); + reseed(WERKBAUM_ID, WERKBAUM_NAME, WERKBAUM_DOC, false); activeId = EXAMPLE_ID; persistDocs(); - logUpdate('🔄 Beispiel-Dokument und Einstellungen zurückgesetzt'); + logUpdate('🔄 Mitgelieferte Dokumente und Einstellungen zurückgesetzt'); /* Kurze Verzögerung, damit Logging sichtbar wird, dann reload */ setTimeout(() => {