feat(extension): complete the recorder — in-page UI, control bar, screenshot mode, hardening - #6
Merged
Merged
Conversation
stream-recorder gains an optional micStream muxed as AAC-LC into the screen fMP4 (cross-track-offset keeps A/V aligned; drops to video-only where AAC encoding is unavailable) and pause()/resume() that stop encoding while shifting later chunk timestamps back by the wall-clock pause, so the muxed timeline stays gapless. Timestamps are clamped per track to stay monotonic. The webcam recorder pauses via MediaRecorder.pause()/resume().
- abort() now releases the server-side multipart via POST /api/r/abort (idempotent, best-effort) instead of only clearing local buffers - a finalize lost to the network falls back to the no-auth GET /api/r/state probe: state === ready means the idempotent finalize already applied - queued parts wait out offline windows and resume on the online signal; a request that dies mid-flight stays fatal (desktop parity) - 429/413/401 map to actionable copy via friendlyUploadError; checkAuth probes GET /api/r/auth/check so a revoked token is caught early
Textual drift guard (PLAN.md Decision 5): extracts the shared type declarations from both files, normalises comments/whitespace, and fails on any mismatch — without a cross-app import that would violate the boundary rule.
The recorder runs as restartable sessions over one set of acquired streams: restart discards the upload (server abort included) and re-inits without re-showing the picker; delete aborts and stops the capture. Pause drives the engine's gapless pause on both tracks and the 30-min cap only counts active time. Without a camera the mic no longer drops — it rides the screen fMP4 as the engine's AAC track. RecordingStatus carries startedAt/paused timing so any surface can derive the live clock, and the active-upload crash marker is relayed to the SW (offscreen documents cannot touch chrome.storage).
Declared content script (http/https) rendering a draggable shadow-DOM bar — stop, countdown timer, pause/resume, restart, delete — from the session- storage recording status, so it re-mounts after navigation with a continuous timer. Buttons disable while the tail upload saves.
… mode The recorder UI now opens as an extension iframe floating top-right over a blurred backdrop instead of an anchored action popup (default_popup is stripped via a build:manifestGenerated hook; restricted pages fall back to a standalone popup window). The iframe stays opaque and is sized to the panel through SW-relayed height reports — iframe transparency renders as an opaque white canvas when the host page's color-scheme differs, and window.postMessage proved unreliable on some sites. Panel redesign: home/video/screenshot tabs, device rows with on/off pills and device selects, live mic level meter, recording-limit caption, Effects/Blur stubs and a More menu, brand icon. Opening it without a camera+mic grant injects an invisible grant frame that asks for both devices in one native prompt; on allow both toggle on and the bubble appears (a dismissed prompt is not treated as a block). Screenshot mode captures the visible tab (overlay hidden for the frame) and posts the PNG to the forked /api/s/upload domain. host_permissions now cover the API origin (localhost too in dev builds) so extension-context fetches skip CORS/Private-Network-Access — Brave otherwise blocks extension frames from reaching a localhost dev server. The SW also sweeps crashed uploads on startup and opens the recording URL on success.
The extension still had placeholder icon PNGs — gen-brand-icons.py never emitted them. Regenerated 16/32/48/128 from the canonical master and taught the script the extension output so future regens stay in sync.
The viewer built imageUrl from lib/site's process.env-only R2_PUBLIC_BASE_URL, which never sees the .dev.vars media-proxy override — every screenshot uploaded against next dev rendered a broken image pointing at the prod CDN. publicScreenshotUrlFor now resolves binding env → process.env → CDN fallback, mirroring the recording domain's publicUrlFor. Prod output is unchanged.
Status header, superseded auth decision (externally_connectable tab sign-in), phase 2/3 + screenshot-mode results, the in-page recorder surface, and the hardening notes now match the implementation.
Comments now describe the behaviour on its own terms instead of citing other products. Comment-only change; no behaviour or binary output is affected.
sardorml
force-pushed
the
feat/extension-complete
branch
from
July 16, 2026 15:23
e078b0c to
f84ffc7
Compare
sardorml
marked this pull request as ready for review
August 2, 2026 14:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the browser extension: in-page recorder UI, full recording controls, screenshot mode, and the robustness gaps from PLAN.md. Ten commits, each a self-contained unit.
What's new
Recorder surface
Recording controls
Screenshot mode
/api/s/uploaddomain; opens the view link.Hardening
POST /api/r/aborton failure/delete/restart + an SW startup sweep for crashed uploads (marker relayed to the SW — offscreen docs can't touchchrome.storage)./api/r/stateprobe; part pumps wait out offline windows; 429/413/401 map to friendly copy;auth/checkprobe on open; wire-drift test pins the forked types againstapps/web.host_permissionsadded so extension-context fetches skip CORS/PNA (Brave blocks localhost dev fetches without them).Web fix (dev-only symptom)
process.envonly, so dev uploads pointed at the prod CDN and rendered broken. Now resolves through the binding env like the recording domain.Cleanup
Notes for review
http/https) adds the "read and change data on all websites" install warning — required for the navigation-surviving control bar.window.postMessagesilently failed on some sites.CAPTUREFLOW_EXTENSION_IDin wrangler vars before the prod auth handshake can pass.Verification
pnpm typecheck(all 7 projects), extension tests 47/47, engine tests 14/14, web tests 26/26,wxt buildclean, prettier clean.