Support minimum browser viewport emulation - #6919
austinywang wants to merge 28 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds minimum CSS viewport sizing for browser panels, persists it in session snapshots, reapplies it during panel updates, exposes ChangesBrowser viewport minimum sizing
Shell PATH preservation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 23 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (23 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
# Conflicts: # .github/swift-file-length-budget.tsv
Greptile SummaryThis PR implements minimum CSS viewport emulation for browser panes using
Confidence Score: 5/5Safe to merge — all viewport state mutations happen on the main actor, WKWebView access is guarded by bounds checks, session fields are opt-in with decodeIfPresent, and the existing socket-worker route has a preconditionFailure sentinel. The command handler validates inputs before touching any WebKit state, the policy test proves main-actor routing, magnification math is unit-tested at the static level, session persistence is backward-compatible, and the hit-test coordinate fix is covered by a targeted test. No blocking issues were identified across routing, isolation, persistence, or localization. No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client as Automation Client
participant TC as TerminalController (main actor)
participant BP as BrowserPanel
participant WV as WKWebView
Client->>TC: "browser.viewport.set {width, height}"
TC->>TC: validate width/height (strict int, 0-100000)
TC->>BP: maximumReachableMinimumViewportSize()
BP->>WV: read bounds, pageZoom
WV-->>BP: bounds, pageZoom
alt "layout not ready (bounds <= 1)"
BP-->>TC: nil
TC-->>Client: err: not_ready (reason: layout_unavailable)
else size exceeds pane limit
BP-->>TC: CGSize(maxW, maxH)
TC-->>Client: err: invalid_params (max_width, max_height)
else valid
BP-->>TC: CGSize(maxW, maxH)
TC->>BP: setMinimumViewportSize(width, height)
BP->>BP: normalise dimensions, compare to stored
BP->>BP: applyMinimumViewportSize()
BP->>BP: minimumViewportMagnification(bounds, size, pageZoom)
BP->>WV: setMagnification(scale, centeredAt: .zero)
WV-->>BP: done
BP-->>TC: changed bool
TC->>BP: currentMinimumViewportSize()
TC-->>Client: "ok {handled, changed, width, height, magnification}"
end
Note over BP,WV: Re-applied on: bindWebView, pageZoom change, portal attach/resync, representable.update, local-inline pin, session restore
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client as Automation Client
participant TC as TerminalController (main actor)
participant BP as BrowserPanel
participant WV as WKWebView
Client->>TC: "browser.viewport.set {width, height}"
TC->>TC: validate width/height (strict int, 0-100000)
TC->>BP: maximumReachableMinimumViewportSize()
BP->>WV: read bounds, pageZoom
WV-->>BP: bounds, pageZoom
alt "layout not ready (bounds <= 1)"
BP-->>TC: nil
TC-->>Client: err: not_ready (reason: layout_unavailable)
else size exceeds pane limit
BP-->>TC: CGSize(maxW, maxH)
TC-->>Client: err: invalid_params (max_width, max_height)
else valid
BP-->>TC: CGSize(maxW, maxH)
TC->>BP: setMinimumViewportSize(width, height)
BP->>BP: normalise dimensions, compare to stored
BP->>BP: applyMinimumViewportSize()
BP->>BP: minimumViewportMagnification(bounds, size, pageZoom)
BP->>WV: setMagnification(scale, centeredAt: .zero)
WV-->>BP: done
BP-->>TC: changed bool
TC->>BP: currentMinimumViewportSize()
TC-->>Client: "ok {handled, changed, width, height, magnification}"
end
Note over BP,WV: Re-applied on: bindWebView, pageZoom change, portal attach/resync, representable.update, local-inline pin, session restore
Reviews (18): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/TerminalController.swift (1)
9575-9588: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winReturn
handled: truefor successful idempotent sets.
setMinimumViewportSizereturnsfalsewhen the stored size and magnification are already current, so repeating the same validbrowser.viewport.setcan report"handled": falsedespite being successfully handled. Keep no-op/change status separate from the command-handled contract.Proposed fix
- let handled = browserPanel.setMinimumViewportSize( + let changed = browserPanel.setMinimumViewportSize( width: requestedWidth, height: requestedHeight ) let storedSize = browserPanel.currentMinimumViewportSize() return .ok(v2BrowserActionPayload( @@ tabManager: tabManager, extra: [ - "handled": handled, + "handled": true, + "changed": changed, "width": Int((storedSize?.width ?? 0).rounded()), "height": Int((storedSize?.height ?? 0).rounded()), "magnification": Double(browserPanel.webView.magnification)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/TerminalController.swift` around lines 9575 - 9588, The `browser.viewport.set` response is conflating “no-op” with “not handled” in the `TerminalController` flow around `setMinimumViewportSize` and `v2BrowserActionPayload`. Update the logic so a successful idempotent call still returns handled as true, while preserving any separate indication of whether the viewport actually changed; use the existing `browserPanel.setMinimumViewportSize`, `browserPanel.currentMinimumViewportSize()`, and payload construction to keep the command-handled contract distinct from change detection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@Sources/TerminalController.swift`:
- Around line 9575-9588: The `browser.viewport.set` response is conflating
“no-op” with “not handled” in the `TerminalController` flow around
`setMinimumViewportSize` and `v2BrowserActionPayload`. Update the logic so a
successful idempotent call still returns handled as true, while preserving any
separate indication of whether the viewport actually changed; use the existing
`browserPanel.setMinimumViewportSize`,
`browserPanel.currentMinimumViewportSize()`, and payload construction to keep
the command-handled contract distinct from change detection.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: cd6b5e06-8ed8-410e-88cc-cd5742ba0c94
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (2)
Resources/Localizable.xcstringsSources/TerminalController.swift
# Conflicts: # .github/swift-file-length-budget.tsv
|
Addressed CodeRabbit's idempotent |
# Conflicts: # tests/test_claude_wrapper_user_binary_resolution.py
# Conflicts: # .github/swift-file-length-budget.tsv
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@skills/cmux-browser/references/commands.md`:
- Line 76: The cmux browser viewport command reference in commands.md is missing
the updated bounds contract, so the doc still implies any width/height is valid.
Update the `cmux browser <surface> viewport <width> <height>` entry to match the
wording from `docs/cli-contract.md`, including the `0...100000` range and the
current-pane emulation limits, so the `viewport` command documentation stays
consistent with the actual CLI/API behavior.
In `@Sources/TerminalController.swift`:
- Around line 9558-9583: The viewport validation in the browser viewport set
path is too strict when `browserPanel.maximumReachableMinimumViewportSize()` is
nil, causing `invalid_params` to reject a requested minimum viewport before
layout exists. Update the `browser.viewport.set` handling in
`TerminalController` so it only enforces the `maximumReachable...` bounds when
that geometry is available; if it is nil, keep the requested
`minimumViewportSize` on `BrowserPanel` and allow the existing reapply logic to
enforce it once the web view has bounds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 35d9ebdd-95c0-4404-bd0b-25fed2ae8525
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (8)
Sources/Panels/BrowserPanel.swiftSources/TerminalController.swiftcmuxTests/BrowserWebContentProcessTests.swiftdocs/cli-contract.mdskills/cmux-browser/references/commands.mdtests/test_claude_wrapper_user_binary_resolution.pytests_v2/test_browser_api_unsupported_matrix.pytests_v2/test_browser_cli_agent_port.py
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/TerminalController.swift (1)
9537-9544: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winReject fractional viewport dimensions at parse time.
v2Doublelets this endpoint accept non-integral sizes, but the payload immediately rounds them back toInts. A request like{"width": 0.4, "height": 900}would persist a positive floor while reportingwidth: 0, so clients cannot round-trip the new API and can be told emulation is cleared when it is not. Parse these as strict integers (or normalize before storing/echoing) so the request/response contract stays consistent. Based on learnings, v2 JSON handlers here should usev2StrictInt(...)when strict integer parsing matters.Suggested fix
- guard let width = v2Double(params, "width"), - let height = v2Double(params, "height"), - width.isFinite, - height.isFinite, + guard let width = v2StrictInt(params, "width"), + let height = v2StrictInt(params, "height"), width >= 0, height >= 0, - width <= Double(BrowserPanel.maximumMinimumViewportDimension), - height <= Double(BrowserPanel.maximumMinimumViewportDimension) else { + width <= Int(BrowserPanel.maximumMinimumViewportDimension), + height <= Int(BrowserPanel.maximumMinimumViewportDimension) else { return .err( code: "invalid_params", message: String( @@ - let requestedWidth = CGFloat(width) - let requestedHeight = CGFloat(height) + let requestedWidth = CGFloat(width) + let requestedHeight = CGFloat(height)Also applies to: 9595-9598
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/TerminalController.swift` around lines 9537 - 9544, The viewport size parsing in TerminalController’s v2 JSON handlers is too permissive because v2Double accepts fractional values that are later rounded to Ints, breaking request/response consistency. Update the width/height parsing in the viewport-related handling code to use v2StrictInt (or otherwise normalize before storing and echoing) so only integral dimensions are accepted and the returned values match what is actually applied.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@Sources/TerminalController.swift`:
- Around line 9537-9544: The viewport size parsing in TerminalController’s v2
JSON handlers is too permissive because v2Double accepts fractional values that
are later rounded to Ints, breaking request/response consistency. Update the
width/height parsing in the viewport-related handling code to use v2StrictInt
(or otherwise normalize before storing and echoing) so only integral dimensions
are accepted and the returned values match what is actually applied.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 06ebc8d6-0e51-4ff9-ada8-2bddd7c4db4f
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (8)
Resources/Localizable.xcstringsSources/TerminalController.swiftcmuxTests/BrowserConfigTests.swiftcmuxTests/BrowserWebContentProcessTests.swiftdocs/cli-contract.mdskills/cmux-browser/references/commands.mdtests_v2/test_browser_api_unsupported_matrix.pytests_v2/test_browser_cli_agent_port.py
# Conflicts: # .github/swift-file-length-budget.tsv
# Conflicts: # .github/swift-file-length-budget.tsv # Resources/Localizable.xcstrings
|
CodeRabbit follow-up:
|
# Conflicts: # .github/swift-file-length-budget.tsv
Fixes #6137
Summary
Tests
Note: local cmux dev builds and xcodebuild were not run per task constraint.
Demo Video
Not applicable; this is API/automation behavior covered by tests and CI.
Review Trigger
Please review the browser viewport emulation, session persistence, socket command contract, and zoom/magnification handling. The PATH test change is a test-fixture stabilization so the existing wrapper test uses the intended PATH while sourcing shell integration scripts.
Checklist
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Adds minimum CSS viewport emulation for browser panes using
WKWebViewmagnification, controlled bybrowser.viewport.set, with zoom-aware scaling, pane-limit validation, and session persistence. The API runs on the main actor, returnshandled/changedwithmagnification, and reliably reapplies across layout and hosting changes.New Features
browser.viewport.set: width/height are integer CSS px in 0–100000;0clears; returnsnot_readyuntil pane layout; rejects sizes beyond the pane’s emulation range withinvalid_paramsandmax_width/max_height; response includeswidth,height,handled,changed, andmagnification; enforced main-actor dispatch. CLI/docs updated; errors localized.Bug Fixes
pageZoomand magnification to avoid opening the wrong link.PATHand isolating edits viaCMUX_TEST_PATH.Written for commit 37ab68b. Summary will update on new commits.
Summary by CodeRabbit
browser.viewport.setto enforce a minimum CSS viewport size by emulating the viewport via WebKit magnification.0 0clearing.