# Progressive web app Mina Habits has an installable production build with a public offline app shell. Its manifest supplies a stable app identity and root scope, standalone display, 192px and 512px icons, a separate maskable icon, app colors and descriptions. The HTML includes an Apple touch icon, mobile metadata, theme color and safe-area support. Browser chrome follows the selected light or dark theme. Install from **Install app** in the footer. Supporting browsers show their native install prompt. Other browsers receive instructions for Add to Home Screen or Add to Dock. Installed windows hide the install action. ## Offline behavior After one successful online visit and service-worker installation, the application shell, fonts, icons and lazy browser chunks are available offline. Opening or reloading the app offline shows a reconnect screen instead of the browser error page. Nested routes also resolve correctly. Accounts retry on reconnection and periodically while a failed connection is visible, including when the browser incorrectly reports that it is online. An account request has a 10-second timeout; page navigations fall back to the cached shell after four seconds. **Account access, habit changes, sign-in, exports and Discord actions require a connection.** Existing dashboard content can remain visible in an open window, but no account responses are persisted for offline use and no mutations are queued or replayed. Failed saves must be retried by the user. This is an offline fallback, not offline habit editing or background synchronization. All `/api` traffic, including OAuth callback navigations, bypasses the service worker. API responses have `Cache-Control: no-store`. Only explicitly public build assets enter Cache Storage; server JavaScript, source maps, account data, exports and third-party responses are excluded. Clearing site data removes the public offline files; it does not remove account records from the server. ## Build and updates `bun run build` runs `scripts/build-pwa.ts` after Bun emits the browser and server bundles. It derives a release identifier from the worker source and public asset contents, copies a version-specific public HTML shell, and emits `dist/pwa/sw.js` with an explicit precache list. A failed precache download prevents installation of that release. The stable `/sw.js` route is served as JavaScript with `Cache-Control: no-cache` and root service-worker scope. Updates wait until existing windows close or the user chooses **Update app**, then **Reload and update**. The confirmation tells the user to save unfinished changes. Only the accepting window reloads; other windows retain their drafts and receive their own update action. Old static caches remain available for their lazy chunks. A subsequent activation with no open app windows removes obsolete Mina Habits caches, leaving unrelated caches untouched. Checking for updates on return to the app, reconnection and hourly supports long-lived installed windows. The source manifest is `src/pwa/manifest.webmanifest`. Bun fingerprints its HTML link; an explicit `/manifest.webmanifest` endpoint serves the same document. The manifest's stable `id` keeps app identity independent of its asset filename. Install icons reuse `docs/design/mina-habits-icon.png`; regenerate their exact sizes with `bun scripts/pwa-icons.ts`. The maskable derivative adds opaque padding to keep the monogram inside the central safe zone. Existing favicon and Apple icons are preserved. ## Serving requirements Use the normal production build/start or Docker workflow at the root of an HTTPS origin. Local testing may use `http://localhost` or `http://127.0.0.1`. The current production configuration already requires an HTTPS origin outside loopback and uses Caddy for TLS. No new deployment environment variables are required. Service-worker registration is deliberately disabled in development/HMR mode; run the production smoke check to verify caching. Deploy the entire build atomically, including `dist/pwa`, rather than uploading individual browser files. Do not apply immutable HTTP caching to `/sw.js` or add a proxy cache for `/api`. PWA installation support and its interface vary by browser. Push notifications, background sync and app-store packaging are optional capabilities, not installation requirements. This implementation follows the current [MDN installability requirements](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable) and the documented [service-worker lifecycle](https://web.dev/articles/service-worker-lifecycle). ## Verification ```sh bun run typecheck bun test bun x playwright install chromium bun run test:pwa bun run test:smoke ``` `test:pwa` copies the production build into a temporary directory, starts it with a disposable SQLite database and no Discord credentials, then runs Chromium. It verifies manifest parsing and Chrome installability errors, icon dimensions, MIME/cache headers, first installation, offline startup and nested/lazy routes, reconnection, responsive layouts, theme color, failed-update recovery, a two-window update with an unsaved draft, API-cache exclusion, sign-out and mobile install instructions. Screenshots are written to `.cache/pwa/`. Temporary data and the server are removed when the check finishes. Worker unit tests cover routing, installation failure and cache cleanup. Before a public release, also exercise installation and standalone relaunch on a physical iPhone/iPad and Android device over the production HTTPS origin. Desktop Chromium checks and mobile emulation do not establish native OS installation or Safari behavior. The implementation does not claim a universal PWA certification.