/** * JPEG frame pump for the camera bubble overlay. * * Owned by the popover (see `recorder.ts` top-of-file comment for the * WebKit capture-exclusion rationale — the popover is the only page that * can hold the camera, so it's also the page that emits frames to the * bubble overlay window). The pump reads from a given `MediaStream`, draws * each frame into an offscreen canvas, encodes JPEG, and emits * `clips:bubble-frame` events over Tauri IPC. * * The pump runs for the FULL camera session — pre-record preview AND * recording. The recorder does NOT start its own pump; it only consumes * the video track via MediaRecorder. That way a single pump instance * survives the preview → recording transition without any frame-drop * handoff, which was the source of the "bubble goes black when recording * starts" bug. * * Returns a stop fn that cancels the scheduler and releases the hidden * video + canvas elements. The caller owns the MediaStream lifecycle. * * ## Performance notes (updates-121) * * Previous versions ran at 10 FPS / 144px during recording and STILL * dropped frames to single-digits because the popover's main thread is * saturated during recording: MediaRecorder encoding video + audio + * continuous chunk `fetch()` uploads to localhost + Tauri IPC emit + * canvas.drawImage + canvas.toDataURL + getDisplayMedia + getUserMedia * stream internals. The pump competes with all of that for the one JS * main thread the webview has. * * Web research findings (April 2026, WKWebView on macOS): * - OffscreenCanvas + transferControlToOffscreen in a Worker is only * reliable for WebGL contexts on Safari 17+; the 2D context path in * a Worker has been historically unreliable in WebKit (three.js issue * 16782, Safari Tech Preview thread 704724). Risky for production. * - MediaStreamTrackProcessor is Tech Preview in Safari 18+, and only * fully shipped in Safari 26+. Can't rely on it. * - createImageBitmap from a