Skip to content

fix(preview): apply viewport changes when the panel is hidden - #7303

Open
gbarros-dev wants to merge 6 commits into
pingdotgg:mainfrom
gbarros-dev:fix/preview-hidden-viewport
Open

fix(preview): apply viewport changes when the panel is hidden#7303
gbarros-dev wants to merge 6 commits into
pingdotgg:mainfrom
gbarros-dev:fix/preview-hidden-viewport

Conversation

@gbarros-dev

@gbarros-dev gbarros-dev commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

preview_resize only updated the CSS/React chrome. If the browser panel was hidden, the guest never changed size and wait timed out.

Resize now persists the setting, then applies a CDP device-metrics override so the guest viewport changes even when the tab is not visible.

Fixes #3712.

Split out of closed #7127. Land after #7236 if both are touching preview hosts in the same week; this branch is based on current main and does not include #7236.

Tests: vp test run packages/contracts/src/preview.test.ts apps/web/src/components/preview/previewGuestViewport.test.ts

Implemented with Grok 4.6 through Grok CLI.


Note

Medium Risk
Changes preview guest sizing via CDP and splits human vs automation control paths, but scope is localized to preview IPC/manager with rollback on automation failure and tests for the new behavior.

Overview
Fixes preview resize when the browser panel is hidden: persisted viewport settings now drive a CDP device-metrics override on the guest webContents, not only CSS/React layout.

Desktop: PreviewManager adds setViewport (toolbar/restore—CDP only, no agent badge) and automationSetViewport (same CDP calls under agent control with a recorded resize action). Mobile emulation uses shortest-side < 768px. New IPC channels and bridge methods wire both paths.

Web: previewGuestViewport maps PreviewViewportSetting to { width, height } or { clear: true } for fill mode. HostedBrowserWebview reapplies after webview registration and when the effective viewport or hasWebContents changes. PreviewAutomationHosts persists resize, then applies the override; failures roll back server state and guest metrics.

Contracts add DesktopPreviewAutomationSetViewportInputSchema; unit tests cover mapping, schema, and human setViewport without agent control.

Reviewed by Cursor Bugbot for commit a202af0. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Apply guest viewport CDP override when the preview panel is hidden

  • Adds setViewport and automationSetViewport to PreviewManager in Manager.ts, sending Emulation.setDeviceMetricsOverride or Emulation.clearDeviceMetricsOverride via the Chrome DevTools Protocol without requiring the panel to be visible.
  • Exposes both methods through new IPC channels (desktop:preview-set-viewport, desktop:preview-automation-set-viewport) and the desktopBridge.preview API in preload.ts.
  • HostedBrowserWebview.tsx now re-applies the viewport override via applyPreviewGuestViewport on registration and whenever the effective viewport setting changes.
  • Automation-driven resizes in PreviewAutomationHosts.tsx also apply the guest override and roll back both server and guest state on failure.
  • The mobile flag in the CDP override is computed as true when the shortest dimension is less than 768px.

Macroscope summarized a202af0.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e94d9920-4d12-4c96-8245-0bce8a787f16

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 17, 2026
Comment thread apps/web/src/components/preview/PreviewAutomationHosts.tsx

@macroscopeapp macroscopeapp Bot left a comment

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.

Reviewed the web-side changes for UI/behavior consistency. Two concerns about the new guest viewport override and how it interacts with the existing viewport-readiness contract; both are in apps/web/src/components/preview.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/preview/PreviewView.tsx Outdated
Comment thread apps/web/src/components/preview/PreviewAutomationHosts.tsx Outdated
Comment thread apps/desktop/src/preview/Manager.ts
Comment thread apps/web/src/components/preview/PreviewAutomationHosts.tsx
Comment thread apps/desktop/src/preview/Manager.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new CDP viewport control capability via new IPC methods. An unresolved Medium-severity finding identifies a race condition where async viewport override calls may complete out of order, potentially leaving the guest viewport in a stale state.

You can customize Macroscope's approvability policy. Learn more.

@gbarros-dev
gbarros-dev force-pushed the fix/preview-hidden-viewport branch from 6a56ea4 to 441e9f9 Compare August 17, 2026 08:37

@macroscopeapp macroscopeapp Bot left a comment

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.

Reviewed the web-side viewport override wiring (PreviewView.tsx, PreviewAutomationHosts.tsx, previewGuestViewport.ts). Two issues in the new PreviewView effect affect visible preview chrome; details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/preview/PreviewView.tsx Outdated
Comment thread apps/web/src/components/preview/PreviewView.tsx Outdated
Comment thread apps/desktop/src/preview/Manager.ts
Comment thread apps/desktop/src/preview/Manager.ts

@macroscopeapp macroscopeapp Bot left a comment

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.

One finding: the new guest viewport override is synced only from the committed viewport, so it desyncs from the <webview> box during a resize-handle drag.

Posted via Macroscope — UI Consistency

Comment on lines +251 to +258
useEffect(() => {
if (!runtimeTabId || !desktopOverlay?.hasWebContents) return;
void applyPreviewGuestViewport(
previewBridge?.setViewport,
runtimeTabId,
viewportRef.current,
).catch(() => undefined);
}, [desktopOverlay?.hasWebContents, runtimeTabId, viewportOverrideKey]);

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.

The override is keyed on the committed viewport, but the <webview> box follows the uncommitted drag: useBrowserViewportResize sets dragViewport on every pointermove, effectiveViewport/layout feed the element width/height (and data-preview-css-*) in HostedBrowserWebview, and the commit only happens on pointerup (or after the 150 ms keyboard-resize debounce).

Because the guest is now pinned by Emulation.setDeviceMetricsOverride to the previously committed size, the page stops reflowing while the frame is being dragged and snaps only at commit — previously the guest CSS viewport was the element box, so responsive breakpoints updated live under the drag badge. For the duration of the drag the badge and data-preview-css-width/height also disagree with what the guest reports.

Suggest owning the sync where the drag state is known — e.g. apply applyPreviewGuestViewport from effectiveViewport in HostedBrowserWebview/useBrowserViewportResize, keyed on browserViewportSettingKey(effectiveViewport) and throttled to a frame — so intermediate drag sizes reach the guest and the committed value remains the final write.

Posted via Macroscope — UI Consistency

@macroscopeapp macroscopeapp Bot left a comment

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.

One finding on the new guest-viewport application path. See the inline comment on apps/web/src/browser/HostedBrowserWebview.tsx.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/browser/HostedBrowserWebview.tsx Outdated
Comment thread apps/web/src/browser/HostedBrowserWebview.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

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.

One ownership finding on the new guest-viewport override plumbing. The rest of the web-side changes (shared previewGuestViewport helper, automation rollback path, webview-owner effect) look consistent with the existing preview surface contracts.

Posted via Macroscope — UI Consistency

Comment on lines +247 to +258
const viewportOverrideKey =
viewport._tag === "fill" ? "fill" : `${viewport._tag}:${viewport.width}x${viewport.height}`;
const viewportRef = useRef(viewport);
viewportRef.current = viewport;
useEffect(() => {
if (!runtimeTabId || !desktopOverlay?.hasWebContents) return;
void applyPreviewGuestViewport(
previewBridge?.setViewport,
runtimeTabId,
viewportRef.current,
).catch(() => undefined);
}, [desktopOverlay?.hasWebContents, runtimeTabId, viewportOverrideKey]);

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.

HostedBrowserWebview already owns this override for every tab: it applies previewBridge.setViewport right after registerWebview, and re-applies from an effect keyed on browserViewportSettingKey(effectiveViewport) + hasWebContents for the same runtimeTabId (and ElectronBrowserHost mounts it app-wide for every session). This effect duplicates that work for the active tab, and it pushes the committed viewport while the webview owner pushes the drag-live effectiveViewport — so any re-run here (e.g. hasWebContents flipping after a crash-recovery re-register) races the owner and can momentarily pin the guest to a stale size during a resize drag or a pending keyboard resize.

Suggest dropping this effect (plus the now-unused viewportOverrideKey/viewportRef) and leaving the guest override with the webview owner, so the behavior has a single owner.

Posted via Macroscope — UI Consistency

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Removed the PreviewView effect. HostedBrowserWebview is the only guest-override owner.

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.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

preview_resize only updated the CSS/React chrome. If the browser panel
was hidden, the guest never changed size and wait timed out.

Resize now persists the setting, then applies a CDP device-metrics
override so the guest viewport changes even when the tab is not visible.
A size-only ready check could resolve resize before React committed the
webview geometry. A failed CDP apply also left the persisted snapshot
on the new size.

Resize now waits for the existing ready gates, rolls the snapshot back
when the override fails, and re-applies the committed setting when the
guest attaches.
The restore effect depended on the viewport object, so every preview
event re-sent CDP and flashed the agent-controlling badge. Toolbar
resizes now use a setViewport path that does not take agent control,
and the effect keys only on the viewport size string.
Width-only mobile detection treated 844x390 phones as desktop. The
guest also stayed pinned to the last committed size while the frame
was dragged.

mobile now uses the shortest side. HostedBrowserWebview applies a
frame-throttled CDP override from the effective (including drag)
viewport.
The HostedBrowserWebview override ran one frame after mount, before
registerWebview finished, then swallowed the failure. Crash recovery
and mini-player never retried.

The override now runs after a successful register, and drag updates
wait for hasWebContents.
PreviewView reapplied the committed size after the webview owner already
set the live override. That raced drags and keyboard resizes. The
active-tab effect is gone; HostedBrowserWebview is the single owner.
@gbarros-dev
gbarros-dev force-pushed the fix/preview-hidden-viewport branch from 6b94fa7 to a202af0 Compare August 17, 2026 18:42

@cursor cursor Bot left a comment

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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a202af0. Configure here.

);
});
return () => window.cancelAnimationFrame(frame);
}, [guestViewportKey, hasWebContents, runtimeTabId]);

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.

Drag resize CDP race

Medium Severity

The guest viewport effect keys off effectiveViewport, including in-progress drag/keyboard sizes, and fires async setViewport IPC without cancelling in-flight calls. Cleanup only cancels the pending animation frame, so an older override can finish after a newer one and leave the guest stuck on a stale size.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a202af0. Configure here.

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: preview_resize times out and leaves viewport state internally inconsistent

1 participant