From 5fd2337465b88b57eb2a9ebbd658fb90e96ba2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Walther?= Date: Sun, 16 Aug 2026 11:20:41 +0200 Subject: [PATCH] chore: ignore build output site/, add favicon implementation plan --- .gitignore | 2 + docs/superpowers/plans/2026-08-16-favicon.md | 242 +++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-16-favicon.md diff --git a/.gitignore b/.gitignore index 47f1c33e..0b1c8d1f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .cache .vscode .venv/ +site/ + diff --git a/docs/superpowers/plans/2026-08-16-favicon.md b/docs/superpowers/plans/2026-08-16-favicon.md new file mode 100644 index 00000000..fab1ceb8 --- /dev/null +++ b/docs/superpowers/plans/2026-08-16-favicon.md @@ -0,0 +1,242 @@ +# Favicon Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Das rwcloud.ch-Wiki bekommt ein eigenes minimalistisches Favicon (Monogramm „r“ auf Slate-Squircle) und es ist in `mkdocs.yml` aktiviert, sichtbar im Browser-Tab. + +**Architecture:** Ein neues `favicon.svg` (Pfad-basiert, fonts-unabhängig) dient als Master. Da ImageMagick auf dem Build-Mac nicht installiert ist, werden die PNG-/ICO-Ableitungen per Pillow deterministisch gezeichnet (gleiche Geometrie, flache Ecken statt Béziers — bei Favicon-Grössen visuell identisch) und zu einer Multi-Size `.ico` gebündelt. `mkdocs.yml` verweist auf die neue `.ico`. Die bestehende `favicon.ico` wird ersetzt. + +**Tech Stack:** SVG (Master), Python 3 + Pillow (als dev-Dependency via `uv add --dev pillow`), MkDocs Material + +## Global Constraints + +- Farben: Untergrund Material Slate 900 `#263238`, Buchstabe Material Green 500 `#4CAF50` +- „r" ist **Pfads**, kein ``-Element (fonts-unabhängig, Rasterisierbar) +- SVG: 512×512 viewBox, keine Verläufe/Filter +- ICO enthält 16/32/48/64 px, alle mit Alpha-Kanal (RGBA) +- `mkdocs.yml` Zeile 24: `#favicon: assets/images/favicon.ico` wird aktiviert; `#logo:` bleibt kommentiert +- Keine neuen Python-Abhängigkeiten + +--- + +### Task 1: Favicon-Assets (SVG, PNG, ICO) + +**Files:** +- Create: `docs/assets/images/favicon.svg` +- Create: `docs/assets/images/icon-192.png` +- Create: `docs/assets/images/icon-512.png` +- Replace: `docs/assets/images/favicon.ico` (existiert bereits, wird überschrieben) +- Create: `scripts/render_favicon.py` (einmaliges Render-Script, bleibt als Nachvollziehbarkeit im Repo) + +**Interfaces:** +- Produziert: die drei obigen Bilddateien + ein `favicon.ico` mit Größen 16/32/48/64 +- Konsumiert von: Task 2 (`mkdocs.yml`) und dem Browser-Tab + +- [ ] **Step 1: Pillow als dev-Dependency installieren** + +Run: `uv add --dev pillow` +Expected: `Added pillow ...` und `uv.lock` + `pyproject.toml` aktualisiert. Wenn Pillow schon vorhanden ist (`uv pip show pillow` zeigt eine Version), überspringen. + +- [ ] **Step 2: `favicon.svg` schreiben** + +Erstelle `docs/assets/images/favicon.svg` mit exakt diesem Inhalt (blockiges „r" = Stemm + Arm + rechter Abfall, eine zusammenhängende Kontur): + +```xml + + + + +``` + +Erklärung: Squircle (rx=112 ≈ 22 % der Kanten), Slate 900 `#263238`; das „r" als ein Pfad: Stemm (160→224 breit, 150→400), Arm (224→380, 150→215 hoch), rechter Abfall (316→380, 150→280). Alle Strokes ≥ 64px in 512-Koordinaten = ≥ 2px in 16px. + +- [ ] **Step 3: Render-Script `scripts/render_favicon.py` anlegen** + +Erstelle `scripts/render_favicon.py` mit exakt diesem Inhalt (reines Pillow, deterministisch, gleiche Geometrie wie `favicon.svg`): + +```python +"""Rendert docs/assets/images/favicon.ico (16/32/48/64) und icon-192/icon-512.png. + +Geometrie entspricht 1:1 favicon.svg (512-Koordinatensystem, lineare Skalierung). +Voraussetzung: Pillow (im uv-Venv, dev-Dependency). +Aufruf: uv run python scripts/render_favicon.py +""" +from pathlib import Path +from PIL import Image, ImageDraw + +ROOT = Path(__file__).resolve().parent.parent +IMG = ROOT / "docs" / "assets" / "images" + +BG = (0x26, 0x32, 0x38, 255) # Material Slate 900 +FG = (0x4C, 0xAF, 0x50, 255) # Material Green 500 + +def draw(size: int) -> Image.Image: + im = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + d = ImageDraw.Draw(im) + d.rounded_rectangle([0, 0, size - 1, size - 1], radius=size // 5, fill=BG) + + def s(v512: int) -> int: + # 512er-Koordinate auf Zielgrösse skalieren (aufgerundet) + return round(v512 * size / 512) + + d.polygon( + [ + (s(160), s(400)), + (s(160), s(150)), + (s(380), s(150)), + (s(380), s(280)), + (s(316), s(280)), + (s(316), s(215)), + (s(224), s(215)), + (s(224), s(400)), + ], + fill=FG, + ) + return im + +def main() -> None: + draw(512).save(IMG / "icon-512.png") + draw(192).save(IMG / "icon-192.png") + + sizes = [16, 32, 48, 64] + images = [draw(sz) for sz in sizes] + images[-1].save( + IMG / "favicon.ico", + format="ICO", + sizes=[(im.width, im.height) for im in images], + append_images=images[:-1], + ) + print("OK:", ", ".join(p.name for p in + [IMG / "favicon.ico", IMG / "icon-192.png", IMG / "icon-512.png"])) + +if __name__ == "__main__": + main() +``` + +- [ ] **Step 4: Render-Script ausführen** + +Run: `uv run python scripts/render_favicon.py` +Expected: `OK: favicon.ico, icon-192.png, icon-512.png` + +- [ ] **Step 5: Verifikation – ICO & PNG haben die richtigen Dimensionen** + +```bash +uv run python - <<'PY' +from PIL import Image + +i = Image.open("docs/assets/images/favicon.ico") +print("ICO primary size:", i.size, "frames:", i.n_frames) +assert i.n_frames == 4, "ICO muss 4 Frames (16/32/48/64) haben" +for n in range(i.n_frames): + i.seek(n) + print(" frame", n, i.size) + +for f, name in [(192, "icon-192.png"), (512, "icon-512.png")]: + im = Image.open(f"docs/assets/images/{name}") + assert im.size == (f, f), f"{name} wrong size {im.size}" + assert im.mode == "RGBA" + print(name, im.size, im.mode) +print("ALL OK") +PY +``` + +Expected: `ICO primary size: (64, 64) frames: 4`, Frames 16/32/48/64, `icon-192.png (192, 192) RGBA`, `icon-512.png (512, 512) RGBA`, `ALL OK`. + +- [ ] **Step 6: Visuelle Prüfung** + +Öffne `docs/assets/images/icon-512.png` im macOS-Preview: +`open docs/assets/images/icon-512.png` +Erwartet: Slate-Quadrat mit abgerundeten Ecken, helles blockiges „r" mittig (Stemm links, Arm oben nach rechts, Abfall rechts). Falls das „r" falsch aussieht (z. B. umgedreht, zu klein, nicht zentriert): die `d=`-Pfad-Koordinaten in `favicon.svg` UND die Polygon-Punkte in `scripts/render_favicon.py` **zusammen** anpassen, dann Step 4 wieder. + +- [ ] **Step 7: Commit** + +- [ ] **Step 7: Commit** + +```bash +git add docs/assets/images/favicon.svg docs/assets/images/favicon.ico \ + docs/assets/images/icon-192.png docs/assets/images/icon-512.png \ + scripts/render_favicon.py +git commit -m "feat(favicon): add minimal 'r' monogram favicon + render script" +``` + +--- + +### Task 2: `mkdocs.yml` aktivieren + Build-Verifikation + +**Files:** +- Modify: `mkdocs.yml:23-24` (nur die `favicon`-Zeile entkommentieren) + +**Interfaces:** +- Konsumiert: `docs/assets/images/favicon.ico` aus Task 1 +- Produziert: ein gebauter `site/`-Ordner, dessen `index.html` das neue Favicon referenziert + +- [ ] **Step 1: `mkdocs.yml` anpassen** + +Öffne `mkdocs.yml` und ändere Zeile 24 von +` #favicon: assets/images/favicon.ico` +zu +` favicon: assets/images/favicon.ico` +Zeile 23 (`#logo:`) bleibt unverändert. + +- [ ] **Step 2: Build ausführen** + +Run: `uv run mkdocs build --clean` +Expected: `INFO - Cleaning site directory` … `INFO - Git revision ... - Done in Xs` (oder analog), exit 0. + +- [ ] **Step 3: Verifikation – Site enthält neues Favicon, HTML referenziert es** + +```bash +ls -la site/assets/images/favicon.ico site/assets/images/icon-192.png site/assets/images/icon-512.png +grep -o 'favicon[^"]*\.ico' site/index.html | head -1 +# Ersetzt: altes favicon.ico sollte durch neues ersetzt sein +cmp site/assets/images/favicon.ico docs/assets/images/favicon.ico && echo "identical" +``` + +Expected: alle drei Dateien existieren unter `site/assets/images/`, `index.html` enthält einen `favicon`-Href, `cmp` meldet `identical`. + +- [ ] **Step 4: Commit** + +```bash +git add mkdocs.yml +git commit -m "feat(favicon): enable new favicon in mkdocs.yml" +``` + +--- + +### Task 3: `mkdocs serve` starten (Nutzer-Ansicht) + +**Files:** +- Keine (nur Prozess starten) + +**Interfaces:** +- Konsumiert: Task 1 + Task 2 Ergebnis +- Sichtbar im Browser unter `http://localhost:8000` + +- [ ] **Step 1: Port 8000 freigeben (falls belegt)** + +Run: `lsof -i :8000 || true` +Expected: leer. Falls ein Prozess dort lauscht, diesen identifizieren und fragen, ob er beendet werden darf; sonst auf Port 8001 ausweichen (Step 3). + +- [ ] **Step 2: `mkdocs serve` im Hintergrund starten** + +Run (dauert kurz, bis `Serving on http://...`): +```bash +uv run mkdocs serve --dev-port 8000 2>&1 | tee /tmp/mkdocs-serve.log & +sleep 4 +grep -E "Serving on|ERROR" /tmp/mkdocs-serve.log +``` +Expected: `Serving on http://127.0.0.1:8000/` und kein `ERROR`. + +- [ ] **Step 3: Nutzer prüft Favicon** + +Nutzer öffnet im Browser: `http://localhost:8000` → Browser-Tab zeigt das neue „r"-Favicon. Falls gewünscht, Tab 4-fach zoomen, um 16px-Rendering zu prüfen. + +- [ ] **Step 4: Aufräumen (nach Freigabe)** + +Nutzer meldet „passt" → +```bash +pkill -f "mkdocs serve" || true +rm -f /tmp/mkdocs-serve.log +``` + +Beachte: `site/` ist Build-Artefakt. Es ist aktuell NICHT in `.gitignore` (nur `.DS_Store`, `.temp`, `.cache`, `.vscode`, `.venv/`). Prüfen mit `git status --porcelain | grep site/` — falls `site/` als untracked auftaucht, `site/` zu `.gitignore` hinzufügen und separat committen, bevor Task 3 startet.