Skip to content

fix(server): desktop-drawn preview snapshots match the page's scale and viewport - #16728

Open
ScottN-PV wants to merge 1 commit into
pingdotgg:mainfrom
ScottN-PV:fix/16690-snapshot-dpr
Open

ScottN-PV wants to merge 1 commit into
pingdotgg:mainfrom
ScottN-PV:fix/16690-snapshot-dpr

Conversation

@ScottN-PV

Copy link
Copy Markdown
Contributor

Problem

preview_snapshot assumes a 2x render scale and falls back to a 1280×800 viewport when Playwright has no viewport size, as with desktop-drawn tabs. It asks Chromium for a clip at scale 0.5 over that viewport, and Chromium applies the scale to the display's real pixels. A headless tab runs at 2x in a viewport Playwright set, so its PNG is 1280×800. A tab the desktop app draws keeps the display's scale and the viewport its panel gives it:

  • At 150% scaling the PNG is 960×600 while the result reports 1280×800.
  • When the desktop page's viewport differs from 1280×800, the fixed clip extends beyond it or omits part of it, and the reported size describes neither.

Change

For a tab the desktop draws, the snapshot reads devicePixelRatio, innerWidth, and innerHeight from the page. It divides out the zoom the desktop applied (the server publishes that zoom, and the desktop sets it on the webview), then uses the display scale and the viewport for the clip. The reported size is now read from the PNG itself. Headless tabs keep the fixed 2x and Playwright's viewport, so their captures are unchanged, including zoomed ones.

Scope and approval

Closes #16690. The triage comment confirms the bug on main and names this approach as option (a): read the page's real devicePixelRatio for desktop-drawn tabs and use it in the scale and reported-size math. Option (b), forcing 2x emulation on the visible webview for each capture, would repaint the page the user is watching.

Verification

An agent ran every check. No person tested or reviewed the change. The user resized and zoomed the dev app's window and sent the agent's prompt on request.

Automated (Linux, apps/server):

  • vp test run src/preview: 134 passed.
  • New in ServerBrowser.test.ts, for a desktop-drawn tab: a page at ratio 1.5 and 1280×800 is clipped at scale 2/3 and reported as 1280×800; a page at 1067×667 is clipped to 1067×667; a page at 200% zoom reports ratio 3 and is still clipped at scale 2/3. All three fail against main's ServerBrowser.ts and ServerBrowserPage.ts. A headless tab keeps clip scale 0.5 and never reads the page's ratio, on both.
  • New in ServerBrowserPage.test.ts: real Chromium launched at 1x, 1.5x, and 2x with the matching render scale gives a 1280×800 PNG reported as 1280×800. Another passes a viewport one pixel off the page's real 539×939, the rounding a zoomed desktop page can produce, and checks the reported size is still the PNG's 539×939. It passes on main too, which has no viewport input. With main's fixed 2x the same setups give 640×400, 960×600, and 1280×800, all reported as 1280×800.
  • apps/server typecheck passes. Lint and format pass on the changed files.

Live, agent-operated: the desktop app from this branch (vp run dev:desktop) on Windows 11 at 150% scaling, preview_snapshot on a local page with fine text and one-pixel lines.

Page viewport main This branch
1280×800, panel maximized 960×600 1280×800 (at bbc11231b)
1067×667, app window zoomed out not run 1280×800, whole page
539×939, narrow panel not run 809×1409, reported 809×1409

main, 1280×800 viewport, 960×600:
main: 960x600 for a 1280x800 viewport

This branch at bbc11231b, which handled this case the same way, 1280×800 viewport, 1280×800:
This branch: 1280x800 for a 1280x800 viewport

This branch, 1067×667 viewport, 1280×800:
This branch: 1280x800 covering a 1067x667 viewport

This branch, 539×939 viewport, 809×1409:
This branch: 809x1409 for a 539x939 viewport

All but the narrow capture are soft because the webview was drawn smaller than the page (#9872). This PR changes size and coverage, not that.

Not checked: page zoom on a real desktop tab (no zoom control was found for server tabs; covered by the test above), 100% and 250% display scaling, macOS, and recordings, which capture at full scale. The clip origin still uses CSS page offsets, as on main, so a zoomed desktop tab that is scrolled would capture from a point off by the zoom factor.

Model: Claude Opus 5.5, Claude Fable 5.1 (review), GPT-6-Astra (review). Harness: Claude Code, Codex.

…cale and viewport

Snapshots assumed every tab renders at 2x in a 1280x800 viewport. A tab
the desktop draws keeps the display's scale and its panel's viewport, so
at 150% scaling the PNG came out 960x600 while the result reported
1280x800, and a viewport of another size was clipped wrongly. For those
tabs the snapshot now reads devicePixelRatio and the viewport from the
page and divides out the zoom the desktop applied. The reported size is
read from the PNG header. Headless tabs capture as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 7, 2026
"({ ratio: devicePixelRatio, width: innerWidth, height: innerHeight })",
)) as { readonly ratio: number; readonly width: number; readonly height: number };
return {
renderScale: page.ratio / tab.zoomFactor,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High preview/ServerBrowser.ts:1288

A snapshot requested immediately after a server-side zoom change can be cropped or incorrectly scaled: tab.zoomFactor is updated before the desktop applies setZoomFactor, so this calculation combines the old devicePixelRatio and viewport with the new zoom. Derive the zoom from acknowledged page state or wait for the desktop to apply it before calculating snapshot dimensions.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/preview/ServerBrowser.ts around line 1288:

A snapshot requested immediately after a server-side zoom change can be cropped or incorrectly scaled: `tab.zoomFactor` is updated before the desktop applies `setZoomFactor`, so this calculation combines the old `devicePixelRatio` and viewport with the new zoom. Derive the zoom from acknowledged page state or wait for the desktop to apply it before calculating snapshot dimensions.

@macroscopeapp

macroscopeapp Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Would Approve

Macroscope's review found this PR approvable — This is a focused server-side preview snapshot bug fix that derives desktop captures from the page’s actual scale and viewport while preserving headless behavior, with targeted tests covering the new cases. An unresolved High-severity finding identifies a possible zoom-update race that could still produce incorrectly scaled or cropped snapshots.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 062e2608-f709-4f82-8233-558f57a3dfb8
📥 Commits

Reviewing files that changed from the base of the PR and between 10f39eb and ff9ee0d.

📒 Files selected for processing (4)
  • apps/server/src/preview/ServerBrowser.test.ts
  • apps/server/src/preview/ServerBrowser.ts
  • apps/server/src/preview/ServerBrowserPage.test.ts
  • apps/server/src/preview/ServerBrowserPage.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Desktop-rendered snapshots now use page metrics and tab zoom to set capture scale and viewport dimensions. Snapshot metadata uses dimensions read from the PNG when available. Headless snapshots retain their fixed render scale.

Changes

Preview snapshot sizing

Layer / File(s) Summary
Calculate per-tab rendering parameters
apps/server/src/preview/ServerBrowser.ts, apps/server/src/preview/ServerBrowser.test.ts
Desktop snapshots derive capture scale and viewport dimensions from the page’s device-pixel ratio, inner dimensions, and tab zoom. Headless snapshots retain the fixed render scale. Tests cover desktop and headless capture parameters.
Capture viewport and report image dimensions
apps/server/src/preview/ServerBrowserPage.ts, apps/server/src/preview/ServerBrowserPage.test.ts
Snapshot capture accepts a viewport for clipping. Screenshot metadata uses PNG dimensions when available and falls back to the existing calculation. Tests check output and reported dimensions.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: maria-rcks

Merge Risk: ⚪ Minimal · up to ff9ee

No concrete issue remains that blocks merging. Real desktop-tab page zoom has not been validated, so its snapshot sizing remains worth checking.

Security Architecture Review

Security architecture risk: 🔵 Low · up to ff9ee

The change adds no new browser actions or access rights. Its exposure is limited to existing preview captures, but the effect of abnormal page-reported sizes has not been verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed values influence capture of the already-selected preview tab, not tab selection or authority over another session. Resource effects beyond that tab cannot be bounded from the inspected source alone.

Trust Boundaries and Controls

  • observed — The new desktop path treats page-evaluated metrics as capture geometry through a TypeScript assertion, without runtime validation in the inspected path. The capture scale calculation constrains width but establishes no height or total-area limit. Effective browser-side limits remain unverified.

Resilience and Maintainability Implications

  • observed — The existing control queue rejects mismatched agents and queued work whose ownership epoch changed. In-flight work is drained rather than forcibly cancelled, and the caller invalidates references after a control-generation change; this behavior predates the PR.

Hardening Proposals

  • proposed — Consider validating page-derived metrics as finite, positive values and bounding capture height or total area before issuing the screenshot request. This would make resource containment explicit; it is not evidence of a verified resource-exhaustion vulnerability.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: desktop-drawn preview snapshots now match the page’s scale and viewport.
Description check ✅ Passed The description covers the problem, change, scope and approval context, and focused verification. It also states limitations and unchecked cases.
Linked Issues check ✅ Passed [#16690] For desktop-drawn tabs, the change reads the page DPR and viewport, adjusts for desktop-applied zoom, and uses those values for the capture clip. The reported dimensions come from the PNG. Te…
Out of Scope Changes check ✅ Passed The changes to snapshot capture and its tests support [#16690]. The reviewed summary shows no unrelated changes.
Approvability ✅ Passed This is a focused snapshot bug fix. The diff changes snapshot sizing and zoom tracking in apps/server/src/preview/ServerBrowser.ts and apps/server/src/preview/ServerBrowserPage.ts, with tests in t…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: preview_snapshot of a desktop-drawn tab is sized by the display DPR (960×600 at 150% scaling)

1 participant