Skip to content

feat: add PWA manifest and offline service worker for public pages - #85

Open
ydw1904 wants to merge 1 commit into
OpenMouse-Project:mainfrom
ydw1904:feat/offline-pwa-public-pages
Open

feat: add PWA manifest and offline service worker for public pages#85
ydw1904 wants to merge 1 commit into
OpenMouse-Project:mainfrom
ydw1904:feat/offline-pwa-public-pages

Conversation

@ydw1904

@ydw1904 ydw1904 commented Aug 12, 2026

Copy link
Copy Markdown

What

Adds the PWA foundation — a web app manifest and a service worker — so the marketing page
(/) and the interface preview (/demo.html) are installable and load without a connection.

Approach

A small build plugin, build/pwa-vite-plugin.ts, emits sw.js at build time with the hashed
asset list injected. It mirrors the existing build/sites-vite-plugin.ts rather than pulling in
vite-plugin-pwa and its Workbox dependency tree, which keeps the repo at zero runtime
dependencies and two devDependencies
.

Caching strategy:

Request Strategy Why
Navigations Network-first, cache fallback Content stays fresh while the site is under active development
/assets/* Cache-first Content-hashed, so immutable
Google Fonts Stale-while-revalidate, separate cache Avoids vendoring font binaries and the OFL attribution question

The licensed control app is deliberately excluded

This is a security property, not an optimization — please don't widen the precache list later.

functions/_middleware.js validates the om_license_session cookie against Supabase on every
request to a protected route, returns 404 for /protected-assets/* without a valid session,
and sends Cache-Control: private, no-store. Caching the control app would give a user with an
expired or revoked license permanent offline access to it — exactly what the gate exists to
prevent.

So /control-app, /protected-assets/* and /control are absent from the precache list and
are additionally forced to the network by the worker's BYPASS list.

This does not close the "Offline access" roadmap item

That item names the control panel specifically:

Use the control panel with supported mice even when an internet connection isn't available.

Delivering that requires deciding how a license validates with no server to check against, which
is a product decision rather than a build-tooling one. I've opened a separate issue laying out
the options.

Testing

  • npm run build passes; generated sw.js passes node --check
  • All 15 precached URLs return 200 against vite preview — worth checking explicitly, since
    cache.addAll() rejects atomically and a single 404 would silently break the whole install
  • Verified no gated path appears in the generated precache list

To check offline behaviour locally: npm run build && npx vite preview, then DevTools →
Application → Service Workers → tick Offline and reload.

Makes the marketing page and interface preview installable and available
without a connection. A small build plugin emits sw.js with the hashed
asset list injected, mirroring build/sites-vite-plugin.ts rather than
adding vite-plugin-pwa and its Workbox tree.

Caching: navigations are network-first with cache fallback so the site
stays fresh during development, content-hashed assets are cache-first,
and Google Fonts are stale-while-revalidate in a separate cache.

The licensed control app is deliberately excluded. functions/_middleware.js
validates the session cookie on every request to /control-app and
/protected-assets/*, so caching either would let a revoked license keep
working offline. Those paths are absent from the precache list and are
forced to the network by the worker's BYPASS list -- this is a security
property, not an optimization.

This does not close the "Offline access" roadmap item, which names the
control panel specifically. Making that work offline requires deciding
how a license validates with no server to check against; opening a
separate issue for that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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