dist/ the Tauri shell serves — the manifest, icons, and service worker are inert inside the shell, so one build covers all three.
Quick Start
1
Build the bundle
dist/ — the page, stylesheet, manifest, icons, the entry app.js plus its eager chunks, and a sw.js whose cache name is a hash of exactly those bytes.2
Serve dist/
3
Install it
The browser shows an Install app prompt because the manifest and service worker are present. Installed, it opens standalone in portrait — no browser chrome — using the icons from
manifest.webmanifest.4
Reload offline
Cut the network and reload. The shell still renders: the service worker serves the last
index.html and the precached assets from its versioned cache.The service worker does not background-sync or push-notify, and it never precaches lazy chunks — those are cached on first use. A feature works offline once you have used it online.
The Manifest
app/manifest.webmanifest is what turns the tab into an installable app. Every URL is relative (./) so a project site served from a subpath resolves correctly.
Icons are read from the Tauri icon set at build time, so the manifest cannot name an icon the shell does not have — a missing one fails the build, not a phone’s home screen.
How Offline Works
The service worker picks a strategy from the request, never touching cross-origin traffic.Only
GET requests are handled. A same-origin file that is not precached is a lazy chunk; the build leaves those out on purpose so a browser does not download ~1.4 MB it may never use.How Redeploys Work
A new build is a new cache, and the old one is deleted on activate — so a redeploy never pins a user to a stale app.- The cache name is
praisonai-mobile-<buildId>, wherebuildIdis the first 16 hex of a sha256 over the precached bytes. - On
activate, everypraisonai-mobile-*cache that is not the current one is deleted, thenclients.claim()runs. - Navigation is always network-first, so a new
index.htmlsurfaces on the first online visit rather than waiting for a cache to expire.
Which Engine the Web Picks
A browser tab has a server to talk to, so the web defaults to the remote engine.defaultEngineIdFor only decides the very first launch; a persisted engineId always wins. The web picks remote-http because a browser has no reason to fetch ~1.4 MB of engine before first paint. See Engines → First-Launch Default.
Content Security Policy
The browser sees only the<meta> CSP in index.html; Tauri appends its own, so inside the shell both apply and the effective policy is their intersection.
Because Tauri appends rather than replaces, the
ipc: entries are listed in the browser CSP too: a browser ignores a scheme it does not know, and without them the shell’s intersection would block Android’s IPC channel with nothing but a console line to say so.Every URL Is Relative
GitHub Pages serves project sites from a subpath (e.g./PraisonAI/), so every URL in the app is relative.
An absolute /app.js or /manifest.webmanifest would resolve to the wrong site. The boot proof (tools/web-boot.test.mjs) serves dist/ under /PraisonAI/ to pin exactly this — the manifest is served as application/manifest+json, icons return 200 as image/png, and the service worker activates scoped to the subpath.
Deploying to GitHub Pages
Themobile-web.yml workflow builds, boots the app in a real browser, and deploys — but only main deploys, and only after one manual repo setting.
Choosing the Web Build vs. Tauri
Both ship from the same bundle; pick by what the target needs.Best Practices
Never use absolute URLs
Never use absolute URLs
The app is designed to live under a subpath on GitHub Pages. An absolute
/app.js resolves to the wrong site. Keep start_url, scope, links, icons, and scripts ./-relative.Never precache lazy chunks
Never precache lazy chunks
Splitting exists so a browser does not pay ~1.4 MB it may never use. Precaching lazy chunks undoes that. The service worker caches each one on first use into the same versioned cache instead.
Never register the service worker inside Tauri
Never register the service worker inside Tauri
register-sw.js skips registration when __TAURI_INTERNALS__ is present, on non-http(s) origins, and where serviceWorker is absent. Inside the shell the app IS the offline copy; a worker caching custom-protocol responses would put a second copy between the shell and its assets.Let a failed registration degrade, not break
Let a failed registration degrade, not break
If registration fails, the app still boots from the network.
register-sw.js logs a warning rather than throwing — a degraded page, not a broken one.Trust the content-hash cache name
Trust the content-hash cache name
The cache name is a sha256 of the precached bytes, so two builds of one version differ the moment a source file does.
activate deletes any older praisonai-mobile-* cache, so a redeploy never serves a mismatched pair of old and new chunks.Related
Engines
Why the web defaults to
remote-http, and the first-launch default.Native Shell
The Tauri shell’s CSP, insets, and back-gesture arbitration.
Overview
The three targets and the retained chat screen.
Shipping to Stores
The App Store and Play submission path for the Tauri builds.

