Skip to content

Stage 1: messaging, state, permissions, offscreen scaffold#27

Open
juvirez wants to merge 1 commit into
v2from
claude/v2-stage-1-messaging
Open

Stage 1: messaging, state, permissions, offscreen scaffold#27
juvirez wants to merge 1 commit into
v2from
claude/v2-stage-1-messaging

Conversation

@juvirez

@juvirez juvirez commented May 26, 2026

Copy link
Copy Markdown
Owner

Cross-context plumbing for the rewrite. No actual recording yet — Stages 2–5 will add tabCapture+MediaRecorder in the offscreen document, CDP-based HAR/console, and the zip assembly. This PR makes the START/STOP flow round-trip cleanly between popup, service worker, and offscreen.

What's in this PR

New shared code

  • src/types/messages.tsRequest discriminated union (START, STOP, GET_STATUS) + Status response type
  • src/lib/messaging.ts — typed sendMessage<R>() and onMessage(handler) wrappers (async response via return true)
  • src/lib/state.tsgetState/setState/clearState over chrome.storage.session, so recording state survives service-worker suspension
  • src/lib/permissions.tsoriginPattern(url) to derive *://example.com/*, plus requestRecorderPermissions(origin) / hasRecorderPermissions(origin)

Service worker (src/background/index.ts)

  • Handles START: writes session state, ensures offscreen document exists, starts chrome.alarms badge tick, sets badge text
  • Handles STOP: clears state and alarm, clears badge, closes offscreen document
  • Handles GET_STATUS: returns recording state for the popup
  • Listens to chrome.tabs.onRemoved: if the recording tab is closed, cancels the session

Offscreen scaffold

  • src/offscreen/index.html + src/offscreen/index.ts — empty document that just logs on load. Wired through vite.config.ts rollup input so CRXJS bundles it. Stage 2 will add the MediaRecorder here.

Popup (src/popup/App.tsx)

  • Three render states: idle (with optional "permissions denied, try again" caption), recording, unsupported (for chrome:// etc.)
  • On Start click: derives origin pattern from active tab URL and calls chrome.permissions.request({ permissions: ['debugger', 'tabCapture'], origins: [pattern] }). The user sees one dialog scoped to the current site rather than "all websites". If denied, the popup stays open with a retry caption.
  • Closes itself with window.close() after dispatching START/STOP so the action badge takes over as the live status indicator.

Test plan

  • pnpm install && pnpm build succeed; offscreen ends up at dist/src/offscreen/index.html and the service worker loader at dist/service-worker-loader.js
  • tsc -b passes with no errors
  • Manual in chrome://extensions (Load unpacked → dist/):
    • Popup opens, shows "Start recording" on a regular http(s) page
    • Popup shows the unsupported caption on chrome://extensions
    • Click Start on a real page → single Chrome dialog for debugger + tabCapture + the specific origin only
    • Deny → popup shows the retry caption, no crash
    • Allow → popup closes, action badge shows 0:01, 0:02, … ticking every second
    • Reopen popup → shows "Stop recording"
    • Click Stop → badge clears, popup closes
    • Start, then close the tab → badge clears automatically, state cleaned up

What's not in this PR

  • Stage 2: tabCapture stream → MediaRecorder in offscreen → blob back to SW
  • Stage 3: chrome.debugger.attach + Network domain → HAR via chrome-har npm (fixes the cached-resource timing bug)
  • Stage 4: console + exceptions via Runtime.consoleAPICalled / Runtime.exceptionThrown
  • Stage 5: screenshot + meta.json
  • Stage 6: zip assembly + download from offscreen via <a download> (so no downloads permission needed)
  • Stage 7: 10-minute cap, debugger onDetach cancellation, SW restart recovery
  • Stage 8: ESLint flat config, GitHub Actions CI, Web Store release workflow

https://claude.ai/code/session_017HbCsooFaMcc15JCHrbbj3


Generated by Claude Code

Lay down the cross-context plumbing for the rewrite: typed messages
between popup/service worker/offscreen, session storage for recording
state, helpers for the per-origin permission request, badge timer via
chrome.alarms, and an offscreen document scaffold that the service
worker creates on start and closes on stop.

The popup now has the real two-state UI (idle / recording) plus an
"unsupported page" state for chrome:// and similar URLs. On the first
Start click it requests debugger + tabCapture for the current
tab's origin only (e.g. *://example.com/*) via
chrome.permissions.request, so the user sees a single dialog scoped
to the site rather than "all websites" at install time.

No actual recording yet — that's Stages 2-5. End to end the START /
STOP flow now works: badge ticks, offscreen document spawns and
closes, state survives popup close, and tab closure mid-record is
detected via chrome.tabs.onRemoved and triggers cleanup.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants