From 511419fb85da60a99ba4bbc7f9bf9602e132a880 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Tue, 7 Apr 2026 19:05:16 -1000 Subject: [PATCH] feat: add screenshot --base64 for inline image return Returns data:image/png;base64,... instead of writing to disk. Cap at 10MB. Works with all screenshot modes (element, clip, viewport). Eliminates the two-step screenshot+file-serve dance for remote agents. Co-Authored-By: Claude Opus 4.6 (1M context) --- browse/src/meta-commands.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/browse/src/meta-commands.ts b/browse/src/meta-commands.ts index 1eaea71c..34b00463 100644 --- a/browse/src/meta-commands.ts +++ b/browse/src/meta-commands.ts @@ -112,17 +112,20 @@ export async function handleMetaCommand( // ─── Visual ──────────────────────────────────────── case 'screenshot': { - // Parse priority: flags (--viewport, --clip) → selector (@ref, CSS) → output path + // Parse priority: flags (--viewport, --clip, --base64) → selector (@ref, CSS) → output path const page = bm.getPage(); let outputPath = `${TEMP_DIR}/browse-screenshot.png`; let clipRect: { x: number; y: number; width: number; height: number } | undefined; let targetSelector: string | undefined; let viewportOnly = false; + let base64Mode = false; const remaining: string[] = []; for (let i = 0; i < args.length; i++) { if (args[i] === '--viewport') { viewportOnly = true; + } else if (args[i] === '--base64') { + base64Mode = true; } else if (args[i] === '--clip') { const coords = args[++i]; if (!coords) throw new Error('Usage: screenshot --clip x,y,w,h [path]'); @@ -159,6 +162,24 @@ export async function handleMetaCommand( throw new Error('Cannot use --viewport with --clip — choose one'); } + // --base64 mode: capture to buffer instead of disk + if (base64Mode) { + let buffer: Buffer; + if (targetSelector) { + const resolved = await bm.resolveRef(targetSelector); + const locator = 'locator' in resolved ? resolved.locator : page.locator(resolved.selector); + buffer = await locator.screenshot({ timeout: 5000 }); + } else if (clipRect) { + buffer = await page.screenshot({ clip: clipRect }); + } else { + buffer = await page.screenshot({ fullPage: !viewportOnly }); + } + if (buffer.length > 10 * 1024 * 1024) { + throw new Error('Screenshot too large for --base64 (>10MB). Use disk path instead.'); + } + return `data:image/png;base64,${buffer.toString('base64')}`; + } + if (targetSelector) { const resolved = await bm.resolveRef(targetSelector); const locator = 'locator' in resolved ? resolved.locator : page.locator(resolved.selector);