feat(phase 3j pwa): manifest + service worker + icons → installable PWA

- web/static/manifest.webmanifest: name/short_name/start_url=/dashboard/
  display=standalone/theme_color/background_color + three icons (192, 512,
  512-maskable with ~12% safe-zone padding)
- web/static/sw.js: minimal SW — install caches /static/* shell assets,
  fetch is network-first with cache fallback on GETs only, skips /mcp/
  and non-GETs entirely. CACHE_NAME versioned for clean activate-time
  prune.
- cmd/icongen: stdlib-only generator that produces the three PNG icons
  from a stylised "p" monogram. Run once at brand-change, commit output.
- web.init() registers .webmanifest → application/manifest+json with
  mime.AddExtensionType so Chrome accepts the manifest at all
- layout.tmpl + login.tmpl: manifest link, apple-touch-icon, theme-color,
  apple-mobile-web-app-* metas, inline SW-register on load (silent on
  failure — older browsers still work)
- design.md gets §"PWA install (Phase 3j)"; CLAUDE.md "Out of scope"
  drops the Phase-3j line and adds push/background-sync as the
  remaining Otto-PWA territory
- 4 new tests cover manifest MIME, sw.js delivery, all 3 icons, layout
  meta tags
This commit is contained in:
mAi
2026-05-15 19:32:48 +02:00
parent 91c807da1d
commit 1d5db0fe7b
12 changed files with 389 additions and 2 deletions

View File

@@ -442,11 +442,40 @@ Pure CSS + minimal template tweaks make every projax page legible and tappable o
**Out of scope:**
- Native PWA install (manifest.json + service worker) — Phase 3j candidate.
- Push notifications (Otto-PWA's domain).
- Dark mode toggle.
- Right-to-left layout.
## PWA install (Phase 3j)
projax is now installable as a real PWA — iOS Safari "Add to Home Screen" produces a standalone-launching icon and Android Chrome offers the same install flow. Scope is "install affordance," NOT full offline mode.
**Manifest** (`/static/manifest.webmanifest`, served with `application/manifest+json` via `mime.AddExtensionType` in `web.init`):
- `name` / `short_name`: "projax"
- `start_url`: `/dashboard` (the daily-driver surface m wants on tap)
- `scope`: `/`
- `display`: `standalone`
- `theme_color` / `background_color`: `#1a1a1a` (matches login-page palette)
- `icons`: 192×192, 512×512, and a 512×512 maskable variant with ~12% safe-zone padding
**Icons** (`/static/icon-{192,512,maskable}.png`): stdlib-only generation via `cmd/icongen``go run ./cmd/icongen` rewrites them from the source design. Stylised "p" monogram on a dark ground with an accent stripe. Re-run when the brand or palette changes; the PNGs are checked in.
**Service worker** (`/static/sw.js`):
- On `install`: pre-cache `/static/style.css` + manifest + icons.
- On `activate`: prune stale caches from earlier `CACHE_NAME` versions.
- On `fetch` (GET only): network-first with cache fallback on failure. Successful GETs are stashed for the next offline render.
- Explicitly skipped: any non-GET request, anything under `/mcp/`.
This is deliberately the smallest correct service worker. Mutations (CalDAV/Gitea writeback, MCP) require the network; m airplane-modes → he sees the last cached read view but can't change anything until reconnected.
**Layout meta**: `theme-color`, `apple-mobile-web-app-capable=yes` (iOS treats as standalone), `apple-mobile-web-app-status-bar-style=black-translucent`, `apple-mobile-web-app-title=projax`, plus the `<link rel="manifest">` + `<link rel="apple-touch-icon">` chain. Login template carries the same set so a first-time install from `/login` works the same as from `/dashboard`.
**Tests**: `TestManifestServedWithCorrectMIME`, `TestServiceWorkerServed`, `TestIconsServed`, `TestLayoutHasManifestAndAppleTouchIcon`.
**Out of scope for 3j**: push notifications, background sync, full offline write mode, splash-screen tuning.
## 9. Phase-1 deliverable checklist
- [ ] `projax.items` + `projax.item_links` migrations in `db/migrations/`