app
Adom Browser Extension
Public Made by Adomby adom
Your AI's hands in your real, signed-in browser. The Adom Browser Extension (abe) works with both Chrome and Edge: build, drive, and test web apps, and log past vendor walls, in the browser you already use - cookies, SSO, saved logins and captcha trust intact. The nbrowser_* bridge (pup's counterpart) runs in your actual logged-in profile.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
// record.js — SW-side control for browser recording (nbrowser_record_*).
//
// 🎥 DOCTRINE — why it's built this way (full detail: the native-browser-recording skill + README "Recording"):
// GOAL: demos must look AMAZING → full-res, HIGH FRAMERATE (20–60fps). The enemy is PAINT-THROTTLING, not the
// codec: Chrome correctly stops painting occluded/background/static windows (battery) → capture freezes/drops fps.
// (CDP screencast is NOT garbage — it's pup's browser_record_start engine, 20–30fps when the page paints.)
// The NATIVE-browser methods here are 'wgc' + 'getdisplaymedia' (real high-fps engines). Background capture of
// the user's REAL Chrome (the only browser with their logins) needs it to PAINT while occluded — which means
// launching it with the occlusion/throttle-disable flags (--disable-features=CalculateNativeWinOcclusion
// --disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling).
// Default Chrome lacks them (correctly — battery). So:
// • No login needed → record via PUP/Hydrogen (see the demo-recording skill); don't touch the user's Chrome.
// • Login needed → native Chrome: ASK+WARN, FULLY kill the whole chrome.exe process tree, relaunch with the
// user's profile + the flags, record full-fps in the BACKGROUND, then OFFER TO REVERT later (battery saver).
// ⛔ NEVER foreground the user's real Chrome to fix framerate (use the flags); NEVER leave the flags on forever.
//
// TWO methods (caller picks via args.method; README has the full pros/cons):
// • 'getdisplaymedia' (desktopCapture) — CROSS-PLATFORM (Mac/Ubuntu/Windows). High-fps (the WebRTC
// pipeline Google Meet uses). Chrome REQUIRES a one-time "choose what to share" picker per
// recording (no silent variant in any browser). Records the chosen surface in the background.
// • 'wgc' — WINDOWS-only, handled BRIDGE-SIDE via HD's Windows-Graphics-Capture recorder: no picker,
// fully background, no wake-lock, 60fps mp4. The bridge intercepts method:'wgc' before the
// extension; reaching here means HD/bridge WGC isn't wired on this machine.
//
// record_start is NON-BLOCKING: it shows the picker and returns immediately with a recordId in state
// 'awaiting_pick'. The picker can take as long as the user needs (the old blocking version timed out
// the relay request). Poll nbrowser_record_status until state=='recording', then drive, then stop.
// The MediaRecorder lives in the offscreen document, so it keeps recording even if the SW idles.
const OFFSCREEN_URL = 'src/offscreen-record.html';
const active = new Map(); // recordId -> { state, method, startedAt?, mimeType?, error? }
let seq = 0;
async function ensureOffscreen() {
const has = chrome.offscreen.hasDocument ? await chrome.offscreen.hasDocument() : false;
if (!has) await chrome.offscreen.createDocument({ url: OFFSCREEN_URL, reasons: ['USER_MEDIA'], justification: 'Record the browser to WebM for demos' });
}
export async function recordStart(tabId, args = {}) {
const method = (args.method || 'getdisplaymedia').toLowerCase();
if (method === 'wgc') {
return { ok: false, error: 'method:wgc is handled by the bridge (HD WGC recorder), not the extension',
_hint: 'the bridge intercepts method:"wgc"; reaching the extension means HD/bridge WGC is not wired here — use method:"getdisplaymedia"' };
}
// 'tabcapture' — record the CURRENT tab with NO picker. Needs activeTab, which is granted when the
// user clicks the Adom toolbar icon (that gesture). This powers the friendly "Record this tab" button.
if (method === 'tabcapture' || method === 'thistab') {
let streamId;
try {
streamId = await new Promise((res, rej) => chrome.tabCapture.getMediaStreamId({ targetTabId: Number(tabId) }, (id) =>
(chrome.runtime.lastError || !id) ? rej(new Error((chrome.runtime.lastError && chrome.runtime.lastError.message) || 'tabCapture failed')) : res(id)));
} catch (e) {
return { ok: false, error: String((e && e.message) || e), _hint: 'tabCapture needs activeTab — start it from the Adom toolbar button ("Record this tab"); that click grants it.' };
}
const recordId = 'r' + (++seq);
await ensureOffscreen();
const res = await chrome.runtime.sendMessage({ target: 'offscreen-record', action: 'start', streamId, source: 'tab', fps: Number(args.fps) || 30, audio: args.audio === true });
if (res && res.ok) { active.set(recordId, { state: 'recording', method: 'tabcapture', startedAt: Date.now(), mimeType: res.mimeType }); return { ok: true, recordId, method: 'tabcapture', state: 'recording' }; }
return { ok: false, error: (res && res.error) || 'recorder failed to start' };
}
const recordId = 'r' + (++seq);
active.set(recordId, { state: 'awaiting_pick', method: 'getdisplaymedia' });
const tab = tabId != null ? await chrome.tabs.get(Number(tabId)).catch(() => null) : null;
// Picker callback — runs whenever the user picks (or cancels). Recording starts HERE, async.
const onPick = async (streamId) => {
if (chrome.runtime.lastError || !streamId) { active.set(recordId, { state: 'cancelled', method }); return; }
try {
await ensureOffscreen();
const res = await chrome.runtime.sendMessage({ target: 'offscreen-record', action: 'start', streamId, source: 'desktop', fps: Number(args.fps) || 30, audio: args.audio === true });
if (res && res.ok) active.set(recordId, { state: 'recording', method: 'getdisplaymedia', startedAt: Date.now(), mimeType: res.mimeType });
else active.set(recordId, { state: 'error', method, error: (res && res.error) || 'recorder start failed' });
} catch (e) { active.set(recordId, { state: 'error', method, error: String((e && e.message) || e) }); }
};
try {
if (tab) chrome.desktopCapture.chooseDesktopMedia(['window', 'screen', 'tab'], tab, onPick);
else chrome.desktopCapture.chooseDesktopMedia(['window', 'screen'], onPick);
} catch (e) {
active.set(recordId, { state: 'error', method, error: String((e && e.message) || e) });
return { ok: false, error: String((e && e.message) || e) };
}
return { ok: true, recordId, method: 'getdisplaymedia', state: 'awaiting_pick',
_hint: 'a share picker is up — once the user picks, state becomes "recording". Poll nbrowser_record_status {recordId}; then drive; then nbrowser_record_stop.' };
}
export async function recordStop(args = {}) {
const recordId = args.recordId || [...active.keys()].reverse().find((id) => active.get(id).state === 'recording');
const meta = recordId ? active.get(recordId) : null;
if (!meta) return { ok: false, error: 'no recording found' };
if (meta.state !== 'recording') return { ok: false, error: `recording is in state '${meta.state}', not recording`, state: meta.state };
const res = await chrome.runtime.sendMessage({ target: 'offscreen-record', action: 'stop' });
active.delete(recordId);
if (![...active.values()].some((m) => m.state === 'recording') && chrome.offscreen.closeDocument) chrome.offscreen.closeDocument().catch(() => {});
if (!res || !res.ok) return { ok: false, error: (res && res.error) || 'recorder failed to stop' };
return { ok: true, recordId, method: meta.method, durationMs: Date.now() - meta.startedAt, mimeType: res.mimeType, sizeKB: res.sizeKB, base64: res.base64 };
}
export function recordStatus(args = {}) {
const recordId = args.recordId || [...active.keys()].pop();
const m = recordId ? active.get(recordId) : null;
if (!m) return { ok: true, recordId: recordId || null, state: 'none' };
return { ok: true, recordId, state: m.state, method: m.method, error: m.error, elapsedMs: m.startedAt ? Date.now() - m.startedAt : 0 };
}
export function recordList() {
return { ok: true, count: active.size, recordings: [...active].map(([id, m]) => ({ recordId: id, state: m.state, method: m.method, elapsedMs: m.startedAt ? Date.now() - m.startedAt : 0 })) };
}