chore: ignore build output site/, add favicon implementation plan
This commit is contained in:
1 parent
13d15a95a7
commit
5fd2337465
2 files changed
+244
No files matched your search
@@ -3,3 +3,5 @@
|
||||
.cache
|
||||
.vscode
|
||||
.venv/
|
||||
site/
|
||||
|
||||
@@ -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 `<text>`-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
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<rect x="0" y="0" width="512" height="512" rx="112" fill="#263238"/>
|
||||
<path fill="#4CAF50" d="M160 400 L160 150 L380 150 L380 280 L316 280 L316 215 L224 215 L224 400 Z"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
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.
|
||||
Reference in new issue
Block a user