fix(preview): improve browser recording quality - #217
Conversation
Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 3958111057c10c10350dd9c20ec2a2df00f504be)
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Review follow-ups on the #8839 port. startRecording holds the per-tab lifecycle lock across executeJavaScript and capturePage, neither of which was bounded. A guest with a wedged main thread — infinite loop, modal dialog, attached debugger — never settles them, so the permit was never released and stopRecording and closeTab would block forever. closeTab claims the tab id before taking the lock, so every later close became a silent no-op and the webContents leaked for the life of the app. Both calls now time out; a timed-out measure reports the existing unavailable-size error. The encoder list also ranked av1 first. Chromium reports it supported but encodes it in software, which at the new 60 fps option drops frames and burns CPU on a machine already running agents. Hardware-encodable formats now win, av1 stays as a fallback, and the explicit bitrate the old canvas path carried is restored and scaled with frame rate. Finally, the startup-cancellation check between stream acquisition and recorder construction threw without releasing the capture, unlike every other failure branch. It now goes through cleanupFailedRecordingStart.
|
Adversarial review found three defects in the adopted code. All are upstream's, but they land here, so they're fixed on this branch. 1. Recording startup could permanently wedge a tab (serious). 2. Software AV1 at 60 fps. 3. Stream leak on a cancellation path. The Re-verified: Two things noted and deliberately not changed, for your call:
The hands-on capture caveat still stands: |
Preview browser recordings serialized JPEG screenshots over IPC at a fixed 12 fps
and re-encoded them through a canvas. The result was low, uneven frame rates —
poor evidence for exactly the UI and motion work that recordings exist to capture.
Recording now uses Electron's native tab media stream: the desktop side returns a
DesktopPreviewRecordingSource(source id plus measured dimensions), and therenderer acquires that stream and encodes with
MediaRecorder, negotiating the bestavailable codec and saving the recorder's actual output format. Frame rate is a new
client-local setting (30 or 60 fps, default 30) under Settings → Integrations →
Browser. Hidden tabs stay offscreen and still record, so multiple tabs can capture
without opening their threads; native stream acquisition is bounded to five seconds
and disposes streams that resolve late.
Adopted from upstream
pingdotgg/t3code#8839(3958111057), cherry-picked clean.The setting is client-local for the same reason the other preview defaults are: the
Chromium guest being captured belongs to the desktop app.
Verified:
vp test run --dir apps/desktop apps/desktop/src/preview/Manager.test.ts(71 passed);
vp test run --dir apps/web apps/web/src/browser/browserRecording.test.ts apps/web/src/components/settings/SettingsPanels.logic.test.ts apps/web/src/components/settings/settingsSearch.test.ts(56 passed);
vp test run --dir packages/contracts packages/contracts/src/settings.test.ts(65 passed); typecheck clean on
@t3tools/contracts,@t3tools/web,@t3tools/desktop.Not verified by making a real recording. This is a full pipeline swap with an
IPC return-type change (
startScreencastnow returns a source instead ofvoid)and new timeout and lifecycle failure modes, so it deserves a hands-on capture in
the desktop app before merge. Say the word and I'll run one.
Reviewed and integrated with Claude Opus 5 in Claude Code.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.