Skip to content

fix(screenshot): capture beyond viewport for off-screen clips#2271

Draft
filip-michalsky wants to merge 1 commit into
mainfrom
fm/stg-2335-screenshot-beyond-viewport
Draft

fix(screenshot): capture beyond viewport for off-screen clips#2271
filip-michalsky wants to merge 1 commit into
mainfrom
fm/stg-2335-screenshot-beyond-viewport

Conversation

@filip-michalsky

@filip-michalsky filip-michalsky commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

CDP Page.captureScreenshot only renders the visible viewport unless captureBeyondViewport is set. Stagehand enabled it only for fullPage, so a clip below the fold (or an element scrolled off-screen) came back blank — the "any screenshot taken beyond the viewport will turn up blank" behavior seen in STG-2335.

Auto-enable captureBeyondViewport whenever a clip falls outside the current viewport (mirroring Playwright); in-viewport clips are unchanged. Adds pure helpers (clipFitsViewport / shouldCaptureBeyondViewport) with unit coverage.

why

page.screenshot({ clip }) returned a blank image whenever the clip fell outside the current viewport — a clip below the fold, or an element scrolled off-screen. This is the "any screenshot taken beyond the viewport will turn up blank" behavior reported in STG-2335 (PermitFlow).

Root cause: CDP Page.captureScreenshot only renders the visible viewport unless captureBeyondViewport is set. Stagehand enabled it only for fullPage, so non-fullPage clips outside the viewport rendered blank. Playwright avoids this by enabling captureBeyondViewport when the clip doesn't fit the viewport.

what changed

  • screenshotUtils.ts: added pure helpers getViewportMetrics, clipFitsViewport, and shouldCaptureBeyondViewport.
  • page.ts: computes captureBeyondViewport (true for fullPage, or any clip outside the current viewport) and passes it to the frame capture.
  • frame.ts: honors an explicit captureBeyondViewport, falling back to fullPage so existing behavior is unchanged.
  • Added unit coverage (screenshot-capture-beyond-viewport.test.ts) and a changeset.

In-viewport clips are unchanged (captureBeyondViewport stays false), so no behavior change for visible clips.

test plan

  • Unit — 10 tests covering the decision logic: in-viewport vs below-the-fold clips, scroll offset, sub-pixel tolerance, and the unmeasurable-viewport fallback.

  • Live Chrome repro — below-the-fold clip (y=1449, fold at 711, content height 2556):

    Path Result
    old (captureBeyondViewport:false) ❌ 1306 bytes — blank
    fixed page.screenshot({clip}) ✅ 40995 bytes — has content
  • Typecheck clean (only pre-existing Protocol.WebMCP errors remain).

CDP Page.captureScreenshot only renders the visible viewport unless
captureBeyondViewport is set. Stagehand enabled it only for fullPage, so a
clip below the fold (or an element scrolled off-screen) came back blank —
the "any screenshot taken beyond the viewport will turn up blank" behavior
seen in STG-2335.

Auto-enable captureBeyondViewport whenever a clip falls outside the current
viewport (mirroring Playwright); in-viewport clips are unchanged. Adds pure
helpers (clipFitsViewport / shouldCaptureBeyondViewport) with unit coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 885d206

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-evals Patch
@browserbasehq/stagehand-server-v3 Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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.

1 participant