6.3 KiB
Manual offline verification (M3)
The service worker, precache injection, and production serving are covered by
unit tests and curl smoke tests; actual offline behavior (SW lifecycle, Cache
Storage, airplane mode) can only be exercised in a real browser. This is that
procedure. Re-run it after any change to src/pwa/*, scripts/build.ts, or
the serve paths in src/index.ts.
Prerequisites
- Chrome or Edge (WebGPU + full PWA support; Firefox works for the WASM/offline parts but not WebGPU as of mid-2026).
- Production build served locally:
Service workers require a secure context;
bun run build bun run start # NODE_ENV=production bun src/index.ts → http://localhost:3000http://localhostqualifies.
1. First visit — install + precache
- Open
http://localhost:3000/in a fresh profile (or after DevTools → Application → Storage → "Clear site data"). - DevTools → Application → Service Workers:
/sw.jsshould be activated and running. No reload prompt should appear on this first install (register.tsonly reloads after the user accepts the update toast). - Application → Cache Storage: expect
whisper-shell-<version>— the precached app shell:/, the hashedchunk-*.js/chunk-*.css,/inference.worker.js,/manifest.webmanifest(+ a hashed copy),/icons/icon-192.png,/icons/icon-512.png(+ a hashed 192 copy). The<version>matches the "cache version" printed bybun run build.whisper-runtime-<version>appears only after step 2 below (it holds/ort/*fetched during model load).
- The ModelPicker status line should read "The app is cached for offline use. Load a model once to make it available offline too."
- Installability: the browser should offer an install icon in the address bar (manifest + SW + icons). Optional: install and confirm standalone display.
Result: Passed
2. Load a model once (online)
- Select a model (tiny.en is fastest) and let it load fully.
- Application → Cache Storage now also contains:
transformers-cache— Whisper weights (.onnx, config/tokenizer JSON), written by Transformers.js itself, never touched by our SW (D-005).whisper-runtime-<version>—/ort/ort-wasm-simd-threaded*.{wasm,mjs}(onnxruntime-web loads one variant per backend, e.g. the.jsepbuild for WebGPU; the worker pointswasmPathsat/ort/so nothing hits the jsdelivr CDN).
- The model now shows the "available offline" badge in ModelPicker, and the status line switches to "Offline ready — …".
Result: Works with WebGPU, however there was a WASM problem: When trying to run Whisper Tiny with Backend = WASM (CPU):
Can't create a session. ERROR_CODE: 1, ERROR_MESSAGE: qdq_actions.cc:137
TransposeDQWeightsForMatMulNBits Missing required scale:
model.decoder.embed_tokens.weight_merged_0_scale for node:
model.decoder.embed_tokens.weight_transposed_DequantizeLinear
Diagnosed & fixed (2026-07-10, D-014): a level-2 graph-optimizer bug in
the onnxruntime-web build bundled with Transformers.js 4.2, triggered by the
8-bit decoders' tied embed_tokens weight — not a model or cache problem.
The worker now caps WASM sessions at graphOptimizationLevel: "basic"
(src/worker/inference.worker.ts), verified against the same ort-web build
outside the browser. Re-run this step with Backend = WASM to confirm in a
real browser.
3. Go offline and transcribe
- DevTools → Network → throttling Offline (or OS airplane mode — stronger,
also proves nothing relies on localhost). Optionally also stop
bun run startto prove the app survives our server being down. - Reload the page. The app shell must render normally (served from
whisper-shell-*, cache-first). Also try a deep link likehttp://localhost:3000/foo— the SW falls back to the cached shell for navigations. - Load the previously-downloaded model. It must become ready with no
network (weights from
transformers-cache, ort runtime fromwhisper-runtime-*). The DevTools Network tab should show every request answered by the service worker / cache, zero failed fetches escaping to the network. - Drop a local audio file and confirm a complete transcription offline.
- Still offline, confirm a not-downloaded model fails gracefully (error surfaced, app usable) rather than hanging.
Result: Works.
4. Update flow (toast)
- Back online, with the old tab still open: make any visible change (e.g.
edit a string in
src/App.tsx), thenbun run buildand restartbun run start. The cache version insw.jschanges. - Reload the tab once so the browser re-checks
/sw.js(servedCache-Control: no-cache). When the new SW reaches "installed/waiting", the toast "A new version of Whisper Web is available." appears. - "Later" dismisses it; the old version keeps running.
- "Reload" posts
SKIP_WAITING, the new SW activates, and the page reloads itself once. After activation, Cache Storage must contain only the newwhisper-shell-*/whisper-runtime-*pair — old versions deleted — whiletransformers-cacheis left intact (model still "available offline").
Result: Works
5. Transcript translation offline (D-WS4b)
- Online: transcribe anything, open Translate transcript…, pick a
direction, click Translate. The hint said "Downloads a translation
model once (~113 MB)"; the status shows "Downloading the translation
model… N%", then "Translating… n of m sentences", then a second card
"Translation (English|Danish)" with
.txt/.srt/.txt+tsexports named<file>.<en|da>.<ext>. - Cache Storage →
transformers-cachenow holdsXenova/opus-mt-…files;whisper-shell-*holds/translate.worker.js. The hint reads "Translation model downloaded — works offline". - Go offline (DevTools → Network → Offline), reload, transcribe with a cached Whisper model, translate again in the same direction: it must work with no network request. The other (never downloaded) direction must fail with "You appear to be offline…" and a Retry button, not hang.
Result: pending (needs a real browser)
Recording the result
When this passes in a real browser, flip the [~] offline-verification item
in WorkBreakdown.md M3 to [x] and note browser + version here.
- Verified on: browser/version, date