Skip to content

Support minimum browser viewport emulation - #6919

Closed
austinywang wants to merge 28 commits into
mainfrom
issue-6137-
Closed

austinywang wants to merge 28 commits into
mainfrom
issue-6137-

Conversation

@austinywang

@austinywang austinywang commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #6137

Summary

  • add per-browser-surface minimum CSS viewport size backed by WKWebView magnification
  • persist the viewport floor in session snapshots and reapply it across WebView replacement and pane geometry updates
  • enable browser.viewport.set and update browser automation docs

Tests

  • python3 -m py_compile tests_v2/test_browser_api_unsupported_matrix.py tests_v2/test_browser_cli_agent_port.py
  • ./scripts/lint-pbxproj-test-wiring.sh
  • python3 scripts/swift_file_length_budget.py
  • python3 scripts/check-package-resolved-policy.py
  • python3 scripts/check-workspace-package-groups.py --check

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

  • Linked issue
  • Docs updated
  • Tests updated
  • Localization updated
  • CI is the validation path; local app builds were not run for closeout.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds minimum CSS viewport emulation for browser panes using WKWebView magnification, controlled by browser.viewport.set, with zoom-aware scaling, pane-limit validation, and session persistence. The API runs on the main actor, returns handled/changed with magnification, and reliably reapplies across layout and hosting changes.

  • New Features

    • Minimum per-surface CSS viewport via magnification (0.1–1.0), independent of page zoom; recalculates on zoom; saved in session snapshots and restored; reapplied on bind, portal attach/resync, representable updates, and local‑inline hosting.
    • browser.viewport.set: width/height are integer CSS px in 0–100000; 0 clears; returns not_ready until pane layout; rejects sizes beyond the pane’s emulation range with invalid_params and max_width/max_height; response includes width, height, handled, changed, and magnification; enforced main-actor dispatch. CLI/docs updated; errors localized.
  • Bug Fixes

    • Context‑menu link capture scales hit‑tests by both pageZoom and magnification to avoid opening the wrong link.
    • Stabilized browser history waits and provisional back‑navigation using page titles and fallback logic.
    • Stabilized shell PATH tests by exporting PATH and isolating edits via CMUX_TEST_PATH.

Written for commit 37ab68b. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added browser.viewport.set to enforce a minimum CSS viewport size by emulating the viewport via WebKit magnification.
    • Browser sessions now persist and restore the configured minimum viewport size (width/height).
  • Bug Fixes
    • Fixed context-menu link fallback hit-testing when both page zoom and viewport magnification are active.
  • Documentation
    • Updated CLI and command references to clarify viewport emulation semantics and valid width/height ranges, including 0 0 clearing.
  • Tests
    • Expanded automated coverage for minimum-viewport sizing, magnification updates across page zoom, and snapshot round-tripping/idempotency.

@vercel

vercel Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 5, 2026 2:52am
cmux-staging Building Building Preview, Comment Jul 5, 2026 2:52am

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds minimum CSS viewport sizing for browser panels, persists it in session snapshots, reapplies it during panel updates, exposes browser.viewport.set, updates coordinate mapping for magnified views, and adjusts one shell PATH integration test.

Changes

Browser viewport minimum sizing

Layer / File(s) Summary
Viewport state and snapshot data
Sources/Panels/BrowserPanel.swift, Sources/SessionPersistence.swift, Sources/Workspace.swift, cmuxTests/BrowserPanelSessionRestoreTests.swift
BrowserPanel stores minimum viewport state, session snapshots persist minimumViewportWidth and minimumViewportHeight, Workspace captures them, and restore and round-trip tests cover the fields.
Minimum viewport enforcement
Sources/Panels/BrowserPanel.swift, Sources/Panels/BrowserPanelView.swift, cmuxTests/BrowserWebContentProcessTests.swift
BrowserPanel computes and reapplies minimum viewport magnification, updates page-zoom handling, and the web content tests cover viewport expansion and maximum reachable size calculations. Lifecycle hooks in BrowserPanelView reapply the minimum viewport size after panel updates.
Command dispatch and API contracts
Sources/TerminalController.swift, Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandExecutionPolicyTests.swift, docs/cli-contract.md, skills/cmux-browser/SKILL.md, skills/cmux-browser/references/commands.md, Resources/Localizable.xcstrings, tests_v2/test_browser_api_unsupported_matrix.py, tests_v2/test_browser_cli_agent_port.py
TerminalController handles browser.viewport.set on the main actor, the worker router rejects it, the execution-policy test classifies it as main-actor-only, and the browser viewport docs and parity tests update the supported/error contract.
Magnification-aware context menu capture
Sources/Panels/CmuxWebView+ContextMenuLinkCapture.swift, cmuxTests/CmuxWebViewContextMenuLinkCaptureTests.swift
Context-menu link hit testing now divides CSS viewport coordinates by pageZoom * magnification, and the test covers the magnified coordinate case.

Shell PATH preservation

Layer / File(s) Summary
PATH setup before sourcing scripts
tests/test_claude_wrapper_user_binary_resolution.py
The shell-integration test stores the expected PATH separately, resets the runtime PATH to a minimal value, and exports the saved PATH in the bash and zsh command strings before sourcing the integration scripts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#5766: Both PRs modify Sources/TerminalController.swift’s v2 browser action dispatching to add new browser.* handlers.
  • manaflow-ai/cmux#6508: Both PRs change the BrowserPanel session-restore path.
  • manaflow-ai/cmux#5780: Both PRs modify the same context-menu coordinate mapping logic in CmuxWebView+ContextMenuLinkCapture.swift.

Suggested reviewers

  • lawrencecchen

Poem

A rabbit hopped through pane and glass,
and made the tiny viewport vast.
With magnified paws and a whisker twirl,
the desktop layout kept its curl.
🐇✨

🚥 Pre-merge checks | ✅ 23 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning tests/test_claude_wrapper_user_binary_resolution.py changes shell PATH handling, which is unrelated to minimum browser viewport emulation. Move the PATH/shell integration test changes into a separate PR or justify them as necessary for this viewport feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 8.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (23 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement opt-in minimum viewport emulation, persistence, reapplication, and browser.viewport.set as requested in #6137.
Cmux Swift Actor Isolation ✅ Passed PASS: New viewport state and socket handling stay within @MainActor BrowserPanel/Workspace/TerminalController; the snapshot remains plain Sendable Codable, and tests are @MainActor.
Cmux Swift Blocking Runtime ✅ Passed The new viewport code is immediate and main-actor routed; no new waits, sleeps, locks, or sync dispatch were added in the changed production Swift.
Cmux Browser Automation Off-Main ✅ Passed PASS: browser.viewport.set is a synchronous UI-mutating main-actor command; wait-based browser methods stay on the socket worker, and tests assert it remains main actor.
Cmux Expensive Synchronous Load ✅ Passed Changes only mutate viewport state and session snapshot fields; no RestorableAgentSessionIndex.load()/agent-history JSON load was added to main-actor or socket paths.
Cmux Cache Substitution Correctness ✅ Passed PASS: snapshot reads panel-owned minimumViewportSize, updated event-driven by set/restore; older snapshots decode nil via decodeIfPresent, so no stale/cold cache swap.
Cmux No Hacky Sleeps ✅ Passed Only new sleeps are in test-only retry helpers; production/runtime code adds no fixed delays or polling, and the rule allows test scaffolding.
Cmux Algorithmic Complexity ✅ Passed Added viewport code is constant-time per pane; new collection ops are only fixed-size method lists and per-panel snapshot fields.
Cmux Swift Concurrency ✅ Passed The PR adds viewport/session logic and UI-bound callbacks only; no new background queues, Combine state, completion-handler APIs, or fire-and-forget Tasks were introduced.
Cmux Swift @Concurrent ✅ Passed No changed Swift file adds @concurrent or nonisolated async; the new viewport APIs are synchronous/main-actor, and the async tests are @MainActor UI-bound.
Cmux Swift File And Package Boundaries ✅ Passed PASS: Changes are small, localized viewport/AppKit glue in pre-existing oversized app files; no new oversized file or >250-line growth, and tests cover the feature.
Cmux Swiftpm Lockfiles ✅ Passed PR touches source/docs/tests only; no Package.swift, Package.resolved, .gitignore, or Xcode package-reference files changed, and package-local .gitignore files don't ignore Package.resolved.
Cmux Swift Logging ✅ Passed No added or changed production logging in the new viewport paths; the only NSLog hits are preexisting/unrelated or DEBUG-gated.
Cmux User-Facing Error Privacy ✅ Passed New viewport errors are generic and avoid vendor/secret/upstream details; docs/tests only add allowed references.
Cmux Full Internationalization ✅ Passed All new user-facing Swift strings use String(localized:), and the new viewport xcstrings keys have translations for all 20 locales; docs/tests are allowed.
Cmux Swiftui State Layout ✅ Passed No new @Observable/@published or layout-driving GeometryReader were added; viewport state is plain private storage, and applyMinimumViewportSize is only called from AppKit bridge lifecycle hooks.
Cmux Architecture Rethink ✅ Passed PASS: BrowserPanel owns the viewport floor; BrowserPanelView only re-applies it on bridge callbacks, and browser.viewport.set is a main-actor bridge with worker precondition—not a symptom patch.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed Touched diff only adds browser viewport/session logic; no new NSWindow/NSPanel/WindowGroup or cmux.* identifier changes, so the auxiliary-window shortcut rule isn't triggered.
Cmux Source Artifacts ✅ Passed All changed paths are source, tests, docs, or a localization catalog; none are temp/build/log/screenshot artifacts per the rule.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No new production test/debug seam was introduced; the only ForTesting symbol in Sources/Panels/CmuxWebView+ContextMenuLinkCapture.swift preexisted and the new viewport APIs are used by production c...
Cmux No Ambient Global State ✅ Passed PASS: The PR adds instance methods and stored properties on existing types; no new file-scope funcs, mutable globals, or singletons were introduced.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding minimum browser viewport emulation.
Description check ✅ Passed The description covers the required sections with summary, testing, demo video note, review trigger, and checklist items.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-6137-

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.

@greptile-apps

greptile-apps Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements minimum CSS viewport emulation for browser panes using WKWebView.magnification, controlled via the new browser.viewport.set socket command. The viewport floor is calculated per-frame (accounting for pageZoom), persisted in session snapshots, and re-applied across WebView replacement, pane layout changes, portal attach/resync, and local-inline hosting. A long-standing context-menu hit-test bug is also fixed by incorporating magnification into the coordinate scale factor alongside pageZoom.

  • New browser.viewport.set command: validates integer width/height (0–100000), returns not_ready when layout is unavailable, returns invalid_params with max_width/max_height when the requested size exceeds the pane's emulation range, and reports handled, changed, and magnification in the success response. Routing is confirmed mainActor by the policy test, and a preconditionFailure guard in the socket-worker path enforces the invariant at runtime.
  • Session persistence: minimumViewportWidth/minimumViewportHeight added as optional Double? fields with decodeIfPresent, preserving backward compatibility with existing sessions.
  • Test stabilization: browser history wait helpers now require both URL and page title to match before proceeding, eliminating races in provisional-back-navigation tests; PATH test fixes export PATH before sourcing shell integration scripts.

Confidence Score: 5/5

Safe 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

Filename Overview
Sources/Panels/BrowserPanel.swift Adds viewport floor state, magnification calculation helpers, and re-application hooks; logic is well-guarded (finite scale, epsilon check, nil fast-path) and return-value semantics are consistent with docs.
Sources/TerminalController.swift Implements v2BrowserViewportSet on the main actor with strict-int validation, layout-readiness guard (not_ready), capacity check (invalid_params), and a preconditionFailure sentinel in the socket-worker router; policy test confirms main-actor routing.
Sources/Panels/BrowserPanelView.swift Six applyMinimumViewportSize call sites in updateNSView (portal bind, geometry resync, local-inline pin, representable update); all are guarded by an epsilon check inside the method so they are no-ops when magnification is already correct.
Sources/Panels/CmuxWebView+ContextMenuLinkCapture.swift Fixes cssViewportPoint to divide by zoom x magnification instead of zoom alone, preventing hit-test skew when viewport magnification is active; test coverage added.
Sources/SessionPersistence.swift Adds minimumViewportWidth/minimumViewportHeight as optional Double? with decodeIfPresent; fully backward-compatible with existing session files.
Sources/Workspace.swift Captures currentMinimumViewportSize() when building the browser session snapshot; straightforward snapshot-field addition.
Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandExecutionPolicyTests.swift Adds browser.viewport.set to the main-actor policy assertion list, proving it is never routed to the socket worker.
Resources/Localizable.xcstrings Three new localized error strings (invalidSize, exceedsPaneLimit, layoutNotReady) translated into all 19 supported locales.

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
Loading
%%{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
Loading

Reviews (18): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

@blacksmith-sh

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Return handled: true for successful idempotent sets.

setMinimumViewportSize returns false when the stored size and magnification are already current, so repeating the same valid browser.viewport.set can report "handled": false despite 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

📥 Commits

Reviewing files that changed from the base of the PR and between 30ce5f1 and 8f52fb0.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (2)
  • Resources/Localizable.xcstrings
  • Sources/TerminalController.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit's idempotent browser.viewport.set finding in 3dff2c3: successful calls now always return handled: true, with changed reporting whether viewport state or magnification actually changed. Added API and CLI parity assertions for the repeated-call path.

cmux added 2 commits June 26, 2026 17:20
# Conflicts:
#	tests/test_claude_wrapper_user_binary_resolution.py
# Conflicts:
#	.github/swift-file-length-budget.tsv
Comment thread Sources/TerminalController.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 538cfea and 541fef9.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (8)
  • Sources/Panels/BrowserPanel.swift
  • Sources/TerminalController.swift
  • cmuxTests/BrowserWebContentProcessTests.swift
  • docs/cli-contract.md
  • skills/cmux-browser/references/commands.md
  • tests/test_claude_wrapper_user_binary_resolution.py
  • tests_v2/test_browser_api_unsupported_matrix.py
  • tests_v2/test_browser_cli_agent_port.py

Comment thread skills/cmux-browser/references/commands.md Outdated
Comment thread Sources/TerminalController.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Reject fractional viewport dimensions at parse time.

v2Double lets this endpoint accept non-integral sizes, but the payload immediately rounds them back to Ints. A request like {"width": 0.4, "height": 900} would persist a positive floor while reporting width: 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 use v2StrictInt(...) 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

📥 Commits

Reviewing files that changed from the base of the PR and between 541fef9 and aee8431.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (8)
  • Resources/Localizable.xcstrings
  • Sources/TerminalController.swift
  • cmuxTests/BrowserConfigTests.swift
  • cmuxTests/BrowserWebContentProcessTests.swift
  • docs/cli-contract.md
  • skills/cmux-browser/references/commands.md
  • tests_v2/test_browser_api_unsupported_matrix.py
  • tests_v2/test_browser_cli_agent_port.py

@austinywang

Copy link
Copy Markdown
Contributor Author

CodeRabbit follow-up:

  • Out of Scope Changes: the tests/test_claude_wrapper_user_binary_resolution.py PATH adjustment is retained as test-fixture stabilization for this branch. It preserves the intended PATH while sourcing shell integration scripts so the existing wrapper test exercises the right behavior in CI; it does not change product behavior.
  • Docstring Coverage: this is a repo-wide baseline warning from the bot, not a new actionable defect introduced by this PR. The changed user-facing API contract is documented in docs/cli-contract.md and skills/cmux-browser/references/commands.md.

# Conflicts:
#	.github/swift-file-length-budget.tsv
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 37ab68bf Deployed Jul 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Browser surfaces tie CSS viewport to pane width, responsive sites lose desktop layout in any narrow pane

3 participants