Skip to content

feat(web): make TaskForge an installable PWA#3

Merged
emreycolakoglu merged 1 commit into
mainfrom
feat/installable-pwa
Jul 17, 2026
Merged

feat(web): make TaskForge an installable PWA#3
emreycolakoglu merged 1 commit into
mainfrom
feat/installable-pwa

Conversation

@emreycolakoglu

Copy link
Copy Markdown
Owner

What

Makes the web app installable — "Install app" on desktop/Android, "Add to Home Screen" on iOS. Adds a web app manifest, a generated icon set, and a service worker via vite-plugin-pwa.

Why these choices

Manifest lives in src/pwa/manifest.ts, not beside the vite config, so tsc and vitest both see it. manifest.test.ts asserts Chrome's installability rules and that every icon src resolves to a real file — renaming the art without regenerating icons breaks install silently: the app still builds and loads, it just stops offering "Install".

Icons are generated, not committed by hand. Source art is assets/pwa/*.svg; pnpm --filter @taskforge/web icons rasterizes to public/icons/*.png using puppeteer, already a devDep for scripts/screenshots.mjs. PNG rather than SVG because iOS home-screen icons and Android launcher tiles need raster. The maskable icon is separate and full-bleed — the launcher crops to its own shape and supplies the rounding, so reusing the rounded-corner art would round already-rounded corners. apple-touch-icon renders from the maskable art because iOS composites alpha against black.

The service worker precaches the app shell only — no runtimeCaching for /api. Every route is session-gated and boards are shared between users, so caching API responses would park another user's task data in Cache Storage, where logout doesn't reach it. /api and /ws are denylisted from the navigation fallback; without that the SW answers XHRs and the socket handshake with the HTML shell.

Verification

Driven against the built app, through both vite preview and the NestJS useStaticAssets path used in production:

  • service worker registers and activates
  • /manifest.webmanifest → 200 application/manifest+json, parses (not swallowed by the SPA fallback)
  • all four icons → 200 image/png
  • GET /api/auth/status returns JSON, not the HTML shell
  • sw.js served max-age=0, so updates propagate

vitest run: 133 passed (14 files). tsc --noEmit: 3 errors, the same 3 pre-existing ones AGENTS.md documents — count unchanged.

Notes

  • The PWA only exists in production builds; vite dev emits no manifest or SW, so install isn't offered on :5173.
  • Install requires a secure context. Deploying the container behind plain HTTP means no install prompt regardless of the manifest.

🤖 Generated with Claude Code

Adds a web app manifest, generated icon set, and a service worker so the app
can be installed to the home screen / desktop.

- Manifest lives in src/pwa/manifest.ts so tsc and vitest see it; its test
  guards Chrome's installability rules and that each icon src exists on disk,
  since a stale icon path breaks install silently.
- Icons rasterize from assets/pwa/*.svg via puppeteer (already a devDep).
  Separate maskable art: full-bleed, glyph inside the 80% safe zone. iOS
  apple-touch-icon renders from it too, as iOS composites alpha on black.
- Service worker precaches the app shell only. No runtimeCaching for /api:
  routes are session-gated and boards are shared, so cached responses would
  outlive logout in Cache Storage. /api and /ws are denylisted from the
  navigation fallback.

Verified against the built app: SW activates, manifest parses, all icons
resolve, and /api returns JSON rather than the HTML shell — through both
vite preview and the NestJS static-asset path used in production.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@emreycolakoglu
emreycolakoglu merged commit a9929e2 into main Jul 17, 2026
1 check passed
@emreycolakoglu
emreycolakoglu deleted the feat/installable-pwa branch July 17, 2026 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant