From ffb3996dbfd2f90998b9a978050985598a9bc8ae Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Tue, 7 Apr 2026 19:03:58 -1000 Subject: [PATCH] feat: add scroll --times N for automated repeated scrolling Extends the scroll command with --times N flag for infinite feed scraping. Scrolls N times with configurable --wait delay (default 1000ms) between each scroll for content loading. Usage: scroll --times 10 scroll --times 5 --wait 2000 scroll --times 3 .feed-container Composable with scrape: scroll to load content, then scrape images. Co-Authored-By: Claude Opus 4.6 (1M context) --- browse/src/write-commands.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/browse/src/write-commands.ts b/browse/src/write-commands.ts index 4f7d0d31..14064f53 100644 --- a/browse/src/write-commands.ts +++ b/browse/src/write-commands.ts @@ -269,7 +269,32 @@ export async function handleWriteCommand( } case 'scroll': { - const selector = args[0]; + // Parse --times N and --wait ms flags + const timesIdx = args.indexOf('--times'); + const times = timesIdx >= 0 ? parseInt(args[timesIdx + 1], 10) || 1 : 0; + const waitIdx = args.indexOf('--wait'); + const waitMs = waitIdx >= 0 ? parseInt(args[waitIdx + 1], 10) || 1000 : 1000; + const selector = args.find(a => !a.startsWith('--') && args.indexOf(a) !== timesIdx + 1 && args.indexOf(a) !== waitIdx + 1); + + if (times > 0) { + // Repeated scroll mode + for (let i = 0; i < times; i++) { + if (selector) { + const resolved = await bm.resolveRef(selector); + if ('locator' in resolved) { + await resolved.locator.scrollIntoViewIfNeeded({ timeout: 5000 }); + } else { + await target.locator(resolved.selector).scrollIntoViewIfNeeded({ timeout: 5000 }); + } + } else { + await target.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); + } + if (i < times - 1) await new Promise(r => setTimeout(r, waitMs)); + } + return `Scrolled ${times} times${selector ? ` (${selector})` : ''} with ${waitMs}ms delay`; + } + + // Single scroll (original behavior) if (selector) { const resolved = await bm.resolveRef(selector); if ('locator' in resolved) {