diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 9db8fdb..d14c2e5 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -39,6 +39,9 @@ jobs: # als ES-Module unter frontend/src/, Vite bündelt sie zu EINER # self-contained frontend/dist/index.html (JS+CSS+Favicon inline). Tests # laufen zuerst — schlägt Vitest fehl, wird nicht deployt. + # Hinweis: der Default-`npm run build` trägt den 🚧-„latest build"-Hinweis + # hinter dem Titel automatisch in sich (app.js/mountBuildBadge, D16) — der + # Pages-Deploy ist bewusst der latest build. Nur `build:prod` ließe ihn weg. - name: Node einrichten uses: actions/setup-node@v4 with: @@ -75,16 +78,8 @@ jobs: BUILD_VERSION="${MAJORMINOR}.${MICRO}" COMMIT_URL="https://github.com/mhoennig/werkbaum/commit/$(git rev-parse HEAD)" echo "Footer-Version: ${BUILD_VERSION} -> ${COMMIT_URL}" - # Latest-Build-Hinweis (Symbol + Tooltip) hinter dem Titel: kennzeichnet - # NUR diese Pages-Veröffentlichung als Entwicklungsstand. Wird bewusst - # erst hier auf der Site-Kopie injiziert (wie Version/LICENSE), damit die - # Quelle und jede andere Instanz (schlichter `npm run build`) ihn nicht - # tragen. Literale UTF-8-Zeichen (kein `#`/`&`, sonst kollidiert der - # sed-Delimiter); Anker `` ist im Bundle eindeutig. - BADGE='🚧' mkdir -p site sed -e 's#\.\./LICENSE#LICENSE#g' \ - -e "s##${BADGE}#" \ -e "s#\(]*>\)[0-9.]\+#\1${BUILD_VERSION}#" \ frontend/dist/index.html > site/index.html diff --git a/README.de.md b/README.de.md index 12afaba..caac64d 100644 --- a/README.de.md +++ b/README.de.md @@ -56,6 +56,36 @@ 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: + +```bash +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). + ## Projektdokumente - `frontend/` — Editor · `backend/` — Kotlin/Spring (Gerüst folgt, siehe backend/README.md) diff --git a/README.md b/README.md index 269f35c..4d3585c 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,35 @@ npm run build # -> frontend/dist/index.html (single self-contained file) The built `dist/index.html` inlines all JS, CSS and the favicon, so **that** file does open standalone via `file://` and is what gets deployed. +### Build hint & your own production install + +Non-production builds carry a small hint next to the title (symbol + tooltip) so +it's clear this is **not** the stable instance: + +- **Dev server** (`npm run dev`) → 🔧 "Preview – local development build" +- **Default build** (`npm run build`, incl. the GitHub Pages deploy) → 🚧 + "Latest build – may still be buggy" + +For **your own production install** the hint is switched off: + +```bash +cd frontend +npm ci # or: npm install +npm run build:prod # -> frontend/dist/index.html WITHOUT the hint +``` + +`build:prod` runs Vite in mode `prod`; `frontend/.env.prod` sets +`VITE_BUILD_BADGE=none`, so the badge code is tree-shaken away entirely (it isn't +even present in the output source). Drop the resulting `dist/index.html` onto your +web space/server (standalone, `file://`-capable). Details: `app.js` +(`mountBuildBadge`), `docs/DECISIONS.md` D16. + +Two things otherwise handled only by the Pages workflow, to fix up yourself when +self-hosting: the footer **MIT-License** link is relative to `../LICENSE` (place +that file one level above `index.html`, or adjust the link), and the **version +number** stays the source placeholder `1.0` (the workflow otherwise replaces it +from `VERSION` + commit count). + ## Project documents - `frontend/` — editor · `backend/` — Kotlin/Spring (scaffold to follow, see backend/README.md) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 60dce9b..90b777f 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -135,18 +135,29 @@ zählt der flache CI-Klon nur einen Commit. `1.0` bleibt die Version beim lokale Links**: „Werkbaum" → Repo-Startseite, die Versionsnummer (``) → exakt der deployte Commit (`…/commit/`, im Build via `git rev-parse HEAD`). -**Latest-Build-Hinweis:** Der Pages-Deploy ist der jeweils frischeste -Entwicklungsstand und kann Fehler enthalten; die *eigentliche* (stabile) Instanz -läuft woanders. Damit nur diese Veröffentlichung als „latest build" markiert ist, -injiziert der Workflow — nach demselben Muster wie Version/LICENSE, also **nur auf -der Site-Kopie** — hinter dem Titel (``, im Bundle eindeutiger Anker) ein -kleines Symbol mit Tooltip (`🚧`, zweisprachiger -`title`). Die Quelle bleibt unberührt: ein schlichter `npm run build` (z. B. für -die stabile Instanz) erzeugt die Datei **ohne** Hinweis. Bewusst nicht als -i18n-UI-Text im `I18N`-Objekt, weil es kein Produkt-Feature ist, sondern -Deploy-Metainformation genau dieser Pipeline (D14: die Quelle nicht um -Deploy-Spezifika erweitern). Umgesetzt mit literalen UTF-8-Zeichen im -`sed`-Replacement (kein `#`/`&`, sonst kollidiert der Delimiter). +**Build-Hinweis (Vorschau / „latest build"):** Nicht-produktive Builds tragen +hinter dem Titel ein kleines Symbol samt Tooltip, damit klar ist, dass es nicht +die *eigentliche* (stabile) Instanz ist. Drei Zustände, gesteuert per Vite-Env +`VITE_BUILD_BADGE` (Auswertung in `app.js`, `mountBuildBadge`): + +- **Dev-Server** (`import.meta.env.DEV`) → 🔧 „Vorschau – lokaler Entwicklungsstand". +- **Default-Build** `npm run build` (Env ungesetzt) → 🚧 „latest build …". Der + GitHub-Pages-Deploy nutzt genau diesen Default und trägt den Hinweis dadurch + automatisch — **keine** `sed`-Injektion mehr nötig. +- **Produktions-Build** `npm run build:prod` (Vite-Modus `prod`, `frontend/.env.prod` + setzt `VITE_BUILD_BADGE=none`) → **kein** Badge; esbuild eliminiert den Zweig + als toten Code, das Symbol steht dann nicht einmal mehr im Ausgabe-Quelltext. + +Damit trägt einzig die echte produktive Installation keinen Hinweis. **Warum in +die App-Quelle statt per Workflow-`sed` (frühere Lösung):** Nur so sieht der +Dev-Server den Hinweis ebenfalls — ein Post-Build-`sed` erreicht den Dev-Server +nicht, der die Quelle direkt ausliefert. Die Umkehrung „Hinweis ist der +Normalfall, Prod schaltet ab" passt zudem zur Anforderung (nur Prod bleibt sauber) +und macht den Default sicher: wer den Prod-Schritt vergisst, veröffentlicht einen +sichtbar als Entwicklungsstand markierten Build, nicht versehentlich einen als +stabil wirkenden. Bewusst **kein** i18n-UI-Text im `I18N`-Objekt (kein +Produkt-Feature, sondern Build-Metainformation; D14) — der `title` ist knapp +zweisprachig (DE · EN). (Nummerierung: D15 war bereits für den kompakten Modus vergeben, daher D16.) diff --git a/docs/TASKS.md b/docs/TASKS.md index 5b6ba81..2d04c57 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -52,7 +52,7 @@ Abhaken beim Erledigen; neue Aufgaben unten anfügen. - [x] GitHub-Pages-Workflow angelegt (`.github/workflows/pages.yml`, siehe docs/DECISIONS.md D16). -## Phase 3 — Integrationen (siehe docs/ROADMAP.md) +## Phase 3 — Integrationen (siehe [ROADMAP](ROADMAP.md)) - [ ] Backend-Gerüst per Spring Initializr in `backend/` anlegen (Kotlin, Gradle Kotlin DSL, JDK 21; Konventionen: backend/CLAUDE.md). - [ ] SVG-Renderer (Layout-Engine) als gemeinsame Basis für Export und diff --git a/frontend/.env.prod b/frontend/.env.prod new file mode 100644 index 0000000..67dad8c --- /dev/null +++ b/frontend/.env.prod @@ -0,0 +1,6 @@ +# Produktions-Build (`npm run build:prod`, Vite-Modus „prod"). +# Schaltet den Build-Hinweis hinter dem Titel ab — nur die echte produktive +# Installation läuft ohne das 🔧/🚧-Symbol. Alle anderen Builds (Dev-Server und +# der Default-`npm run build`, u. a. der GitHub-Pages-Deploy) zeigen ihn. +# Siehe app.js (mountBuildBadge) und docs/DECISIONS.md D16. +VITE_BUILD_BADGE=none diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index fc7f18d..17f2488 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -15,8 +15,13 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der in **eine** self-contained `dist/index.html` — die bleibt `file://`-tauglich (D16) und ist die Deploy-Artefakt-Quelle (Pages-Workflow, siehe README). - `npm --prefix frontend test` — Vitest (`tests/**/*.test.js`). -- `node_modules/` und `dist/` sind ge-`.gitignore`-t; `package-lock.json` ist - eingecheckt (der Workflow nutzt `npm ci`). +- `npm --prefix frontend run build:prod` — Produktions-Build **ohne** den + Build-Hinweis hinter dem Titel (Vite-Modus `prod`, `.env.prod` setzt + `VITE_BUILD_BADGE=none`). Nur die echte produktive Installation nutzt diesen + Weg; Dev-Server (🔧) und Default-`build` (🚧, u. a. Pages-Deploy) zeigen den + Hinweis. Logik: `mountBuildBadge()` in `app.js` (D16). +- `node_modules/` und `dist/` sind ge-`.gitignore`-t; `.env.prod` und + `package-lock.json` sind eingecheckt (der Workflow nutzt `npm ci`). ## Konventionen - Vanilla HTML/CSS/JS, ES-Module; keine Frameworks. Testwerkzeug: Vitest. diff --git a/frontend/package.json b/frontend/package.json index 84a74c3..46a8053 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,6 +7,7 @@ "scripts": { "dev": "vite", "build": "vite build", + "build:prod": "vite build --mode prod", "preview": "vite preview", "test": "vitest run", "test:watch": "vitest" diff --git a/frontend/src/app.js b/frontend/src/app.js index f1e6bd5..b8f886d 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -1103,3 +1103,36 @@ let startLang = 'de'; try{ startLang = localStorage.getItem('werkbaum-lang') || 'de'; }catch(_){} applyLang(I18N[startLang] ? startLang : 'de'); /* setzt Texte + rendert */ applyMobile(); /* Mobil-Verhalten (nach Sprache/Restore) anwenden */ + +/* ---------- Build-Hinweis (Vorschau/Dev + „latest build") ---------- + Kennzeichnet einen nicht-produktiven Build mit einem kleinen Symbol samt + Tooltip hinter dem Titel. Gesteuert per Vite-Env `VITE_BUILD_BADGE`; ohne + Vorgabe entscheidet der Modus: + Dev-Server (`npm run dev`) -> import.meta.env.DEV -> 'dev' (🔧 Vorschau) + Default-Build (`npm run build`) -> Voreinstellung -> 'latest' (🚧) + `npm run build:prod` -> .env.prod: none -> KEIN Badge + So trägt einzig die echte Produktions-Installation keinen Hinweis. Bewusst + kein I18N-Text (Deploy-Metainfo, nicht Produkt-Feature; D14/D16) — der + Tooltip ist knapp zweisprachig (DE · EN). */ +function mountBuildBadge(){ + const kind = import.meta.env.VITE_BUILD_BADGE + || (import.meta.env.DEV ? 'dev' : 'latest'); + if(kind === 'none') return; + const BADGES = { + dev: {icon:'🔧', label:'Preview build (local dev)', + title:'Vorschau – lokaler Entwicklungsstand · Preview (local dev build)'}, + latest: {icon:'🚧', label:'Latest build – may be buggy', + title:'Aktueller Entwicklungsstand (latest build) – kann noch Fehler enthalten · Latest development build – may still be buggy'}, + }; + const b = BADGES[kind]; + const h1 = document.querySelector('header h1'); + if(!b || !h1) return; + const el = document.createElement('span'); + el.className = 'build-badge'; + el.setAttribute('role', 'img'); + el.setAttribute('aria-label', b.label); + el.title = b.title; + el.textContent = b.icon; + h1.appendChild(el); +} +mountBuildBadge(); diff --git a/frontend/src/style.css b/frontend/src/style.css index 828cf44..86b40f5 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -50,6 +50,8 @@ body.fullscreen .app, body.fullscreen .site-footer{max-width:none} h1{font-size:1.25rem;font-weight:600;letter-spacing:-.01em} + /* Build-Hinweis hinter dem Titel (nur nicht-produktive Builds, siehe app.js) */ + .build-badge{margin-left:.45em;font-size:.58em;line-height:1;vertical-align:middle;cursor:help;user-select:none;text-decoration:none} header p{margin-top:4px;color:var(--muted);font-size:.9rem} .sub-short{display:none} /* Kurzfassung nur auf kleinem Bildschirm */ .langsel{