Transcription progress UX + use WASM on mobile web
CI / test (push) Successful in 19s
CI / deploy-web (push) Successful in 35s
CI / build-apk (push) Successful in 6m43s

Two issues behind "it just shows downloaded 100% / loads forever / crashes
after a couple seconds":

1. Progress was invisible during transcription. The 'transcribing' stage
   only fired AFTER the first (slow) chunk, so the UI sat at "Loading
   model… 100%" through the entire first inference and looked frozen.
   - pipeline now emits a transcribing kickoff (progress 0) BEFORE the
     first chunk and carries chunkIndex/chunkCount on every event.
   - transcribeStore threads those through; the Library job card shows a
     spinner, "Transcribing… N%", and "part i of N" so a long file's
     progress is legible and obviously advancing.

2. Web crash on mobile. We picked WebGPU + fp16 whenever navigator.gpu
   existed, but mobile WebGPU drivers crash on sustained fp16 Whisper
   inference (transcribes a chunk, then the GPU process dies). Mobile web
   now uses cross-origin-isolated multi-threaded WASM (slower but stable);
   desktop keeps WebGPU.

Native's "loads forever" was the same missing-feedback problem — it was
grinding through chunks with no UI signal; the chunk counter now shows it.

tsc clean, 279 tests pass (pipeline progress test updated for the kickoff
event), web export OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 10:05:54 +02:00
parent e3ed03a471
commit fa12817050
5 changed files with 74 additions and 13 deletions
+13
View File
@@ -64,8 +64,21 @@ async function lib(): Promise<TransformersModule> {
const loaded = new Map<ModelId, AsrPipeline>();
let cachedBackend: Backend | undefined;
/**
* Mobile browsers expose WebGPU but their drivers are flaky for sustained ML
* inference — fp16 Whisper tends to crash the GPU process after a chunk or two.
* On mobile we deliberately use (cross-origin-isolated, multi-threaded) WASM,
* which is slower but reliable. Desktop keeps WebGPU.
*/
function isMobileWeb(): boolean {
if (typeof navigator === 'undefined') return false;
const ua = navigator.userAgent || '';
return /Android|iPhone|iPad|iPod|Mobile|Silk|Kindle/i.test(ua);
}
async function detectWebGpu(): Promise<boolean> {
try {
if (isMobileWeb()) return false;
if (typeof navigator === 'undefined' || !('gpu' in navigator)) return false;
const gpu = (navigator as { gpu?: { requestAdapter(): Promise<unknown> } }).gpu;
const adapter = await gpu?.requestAdapter();