Skip to content

Fix Cloud focus convergence on middle-click - #16513

Open
teamleaderleo wants to merge 4 commits into
mainfrom
fix/cloud-middle-click-focus
Open

teamleaderleo wants to merge 4 commits into
mainfrom
fix/cloud-middle-click-focus

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A Cloud manual-mirror pane beside a local pane could receive a middle-click while Bonsplit still reported the local pane as focused. The visible focus ring and keyboard ownership then stayed on the local pane. Middle-click now activates the containing workspace pane before the portal callback runs, clears stale reparent-focus suppression, and forwards the event to the clicked terminal. A mixed local and Cloud regression covers the focus handoff.

Testing

  • swiftc -parse Sources/GhosttyNSView+PointerFocusActivation.swift Sources/GhosttyTerminalView.swift cmuxTests/SurfacePaneFactoryFocusTests.swift
  • python3 scripts/verify-local.py --affected mf/main (3/3)
  • git diff --check

The synthetic regression uses a CGEvent bridged to NSEvent with the middle-button field set, so it exercises otherMouseDown. Native app compilation and live dogfood were not run on the MacBook Air.

Changelog

Fixed: Middle-clicking a Cloud terminal beside a local pane moves the focus ring and keyboard ownership to the clicked pane.

Demo Video

  • No live capture; the regression test covers the mixed-pane interaction.

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • UI, settings, menu, schema, help-text or user-facing docs change: localization audited; no new user-facing strings
  • Reviewed with a subagent before merge, and all bot and human review comments resolved

Summary by CodeRabbit

  • Bug Fixes
    • Middle-clicking in a terminal pane now activates that pane and directs terminal input to it, even after focus has shifted to another panel. This helps ensure the pane you interact with receives subsequent keyboard input.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

📝 Walkthrough

Middle-click handling now activates the containing workspace pane and clears reparent-focus suppression before requesting pointer focus. The focus test checks that a Cloud terminal receives panel focus and terminal-input ownership after a middle-click.

Changes: Middle-click focus

Layer / File(s) Summary
Middle-click focus handling
Sources/GhosttyTerminalView.swift, cmuxTests/SurfacePaneFactoryFocusTests.swift
The handler activates the containing pane and clears surface reparent-focus suppression before pointer-focus handling. The test dispatches a synthetic middle-click and checks panel focus and terminal-input ownership.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~7 minutes

Change: Bug fix

Suggested reviewers: austinywang

Merge Risk: ⚪ Minimal · up to f21b6

The middle-click change updates pane focus and terminal-input ownership. No concrete merge-blocking risk is established.

Pre-merge checks · Passed 24 · Inconclusive 1

Failed Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage Inconclusive Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks (24 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: fixing Cloud focus behavior for middle-click.
Description check Passed The description includes a complete summary, testing details, changelog entry, proof statement, and relevant checklist items. The Demo Video heading serves the Proof requirement, and omitted checklist…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Cmux Cloud Persistent Session And Early Input Passed The reviewed diff adds only middle-click focus convergence and a regression test. The changed handler reuses existing focus activation, reparent-suppression clearing, explicit-input, and terminal mous…
Cmux Swift Actor Isolation Passed The production diff only adds existing focus calls inside GhosttyNSView.otherMouseDown(with:), an AppKit UI callback. It adds no model, protocol, Sendable reference type, logger, helper, or backgr…
Cmux Swift Blocking Runtime Passed The production diff adds no blocking or timing-based synchronization. Sources/GhosttyTerminalView.swift only calls focus activation and suppression-clear methods in otherMouseDown; the added lines…
Cmux Browser Automation Off-Main Passed PASS: The PR changes only Sources/GhosttyTerminalView.swift and cmuxTests/SurfacePaneFactoryFocusTests.swift. The changed code handles middle-click focus activation and adds a regression test. It …
Cmux Expensive Synchronous Load Passed PASS. The only production change is in GhosttyNSView.otherMouseDown(with:): it calls activateContainerFocusFromPointerDown() and clears reparent-focus suppression before handling the middle button…
Cmux Cache Substitution Correctness Passed PASS. The production diff only adds middle-click pane activation, reparent-focus suppression clearing, and explicit input handling in GhosttyNSView.otherMouseDown(with:). It does not replace an auth…
Cmux No Hacky Sleeps Passed PASS: The pull request changes only Swift production code and Swift tests. It adds no TypeScript, JavaScript, shell, or build/runtime-script sleep, timer, polling, delayed dispatch, or wall-clock wait…
Cmux Algorithmic Complexity Passed The production diff adds only constant-time control flow and existing method calls in GhosttyNSView.otherMouseDown. It adds no collection traversal, nested scan, repeated sort/filter, join, or batch…
Cmux Swift Concurrency Passed PASS. The PR adds synchronous focus calls in the AppKit otherMouseDown(with:) override and a test-only CGEvent/NSEvent middle-click helper. The authoritative diff adds no background Dispatch queue, …
Cmux Swift @Concurrent Passed The diff adds only synchronous focus handling in GhosttyNSView.otherMouseDown and a synchronous @MainActor test helper. It introduces no nonisolated async work, no @concurrent annotation, and …
Cmux Swift Package Boundaries Passed PASS: The production diff adds middle-click handling inside GhosttyNSView.otherMouseDown(with:). It uses NSEvent, window, terminalSurface, Ghostty mouse APIs, workspace focus activation, and h…
Cmux Swiftpm Lockfiles Passed PASS: The authoritative PR diff changes only Sources/GhosttyTerminalView.swift and cmuxTests/SurfacePaneFactoryFocusTests.swift. It contains no Package.swift, Package.resolved, `cmux.xcodeproj…
Cmux Swift Logging Passed PASS: The reviewed diff adds focus-handling code and a test helper only. It adds no print, debugPrint, dump, NSLog, file/stdout diagnostics, Logger declarations, or sensitive logging. The chan…
Cmux User-Facing Error Privacy Passed The pull request changes focus handling in Sources/GhosttyTerminalView.swift and adds a regression test. It adds no user-facing errors, alerts, command output, API error bodies, or recovery copy. Th…
Cmux Full Internationalization Passed PASS: The diff changes pointer-focus behavior and adds a regression test. It adds no user-facing Swift text, app metadata, string-catalog entry, web UI text, API response, rendered markdown, or change…
Cmux Swiftui State Layout Passed PASS. The diff changes GhosttyNSView.otherMouseDown(with:), an AppKit bridge view, and adds AppKit regression-test logic. It introduces no ObservableObject, @Published, @Observable, `GeometryR…
Cmux Architecture Rethink Passed PASS. The diff adds a small AppKit middle-click correctness path. It has no sleeps, delayed dispatch, polling, locks, observers, new mutable state, or side channels. `activateContainerFocusFromPointer…
Cmux Swift Auxiliary Window Close Shortcuts Passed The PR changes only middle-click handling in Sources/GhosttyTerminalView.swift and a test fixture/helper in cmuxTests/SurfacePaneFactoryFocusTests.swift. It adds or materially changes no standalon…
Cmux Source Artifacts Passed The PR changes only Sources/GhosttyTerminalView.swift and cmuxTests/SurfacePaneFactoryFocusTests.swift. The diff adds hand-written Swift product logic and a focused regression test, including a sy…
Cmux No Test Or Debug Seam In Production Source Passed The only production change is in Sources/GhosttyTerminalView.swift. It adds normal middle-click focus behavior and calls existing production helpers. It does not add a #if DEBUG test-only member, …
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. (1 skipped: 1 too large.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.

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.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread cmuxTests/SurfacePaneFactoryFocusTests.swift
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Yours to fix: compile error in cmuxTests/SurfacePaneFactoryFocusTests.swift:900 call to main actor-isolated instance method 'createMainWindow(initialWorkspaceTi... (a file this PR changes) (+2 more in that file). Also failing here, not on main: compile error in cmuxTests/CmuxSocketEventMapperTests.swift:251 cannot find 'CMUXCLI' in scope, compile error in cmuxTests/CodeRouterHandoffArmGrantStoreTests.swift:378 extra argument 'maxReplacements' in call (+1 more in that file).

CI failed on f21b6e29c2 (run 38349974735 attempt 3): 2 code, 2 unknown.

Job Verdict Why
CI fast guards code a guard step failed
macos / macOS compile admission code a compile error
guards / workflow-guard-tests / quality-determinism unknown no known signature; failed step: Validate socket capability parity
guards / workflow-guard-tests / preflight unknown no known signature; failed step: Validate macOS runner guards

Failures:

  • new in this PR compile error in cmuxTests/CmuxSocketEventMapperTests.swift:251 cannot find 'CMUXCLI' in scope (job)
  • new in this PR compile error in cmuxTests/CodeRouterHandoffArmGrantStoreTests.swift:378 extra argument 'maxReplacements' in call (job)
  • new in this PR compile error in cmuxTests/CodeRouterHandoffArmGrantStoreTests.swift:437 extra argument 'maxReplacements' in call (job)
  • yours compile error in cmuxTests/SurfacePaneFactoryFocusTests.swift:900 call to main actor-isolated instance method 'createMainWindow(initialWorkspaceTi... (job)
  • yours compile error in cmuxTests/SurfacePaneFactoryFocusTests.swift:913 call to main actor-isolated instance method 'setContainerFrame' in a synchronous... (job)
  • yours compile error in cmuxTests/SurfacePaneFactoryFocusTests.swift:913 main actor-isolated property 'bonsplitController' can not be referenced from a n... (job)
Matched log lines
CI fast guards: FAIL    9.3s  workflow-guard-tests / ci: Run canonical CMUX CI guard profile
macos / macOS compile admission: /tmp/cmux-ci/src/cmuxTests/CmuxSocketEventMapperTests.swift:251:17: error: cannot find 'CMUXCLI' in scope

Not re-run automatically: CI fast guards, macos / macOS compile admission, guards / workflow-guard-tests / quality-determinism, guards / workflow-guard-tests / preflight are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Dogfood tours of f21b6e29

modifier-clicks-tour at f21b6e29: not run

skipped: CI left no app build for this head (its compile failed or was cancelled)

Tours are picked by the paths globs in dogfood/scenarios/*.json; a Dogfood-tours: a, b line in the description picks them instead (none turns this off). Look at every frame before merging: a green tour only means no step failed.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Fixed the CI compile error in 73ba38a: AppKit exposes NSEvent.mouseEvent(with: ...) for synthetic events, so the regression now constructs .otherMouseDown through that API. swiftc -parse over the changed source and test plus git diff --check pass.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Follow-up in 5ad497b: the synthetic middle-click now uses a CGEvent bridged to NSEvent and explicitly sets the button-number field to 2, so the test exercises GhosttyNSView.otherMouseDown instead of relying on the generic AppKit constructor.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Updated the PR description to the repository template, including the concrete behavior, testing evidence, changelog, demo note, and checklist.

@teamleaderleo
teamleaderleo force-pushed the fix/cloud-middle-click-focus branch from 95a3a8b to f21b6e2 Compare October 13, 2026 04:50
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Rebased onto current main at f21b6e2; scoped diff check is clean. Parked while hosted CI reruns for exact-head attribution.

@blacksmith-sh

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards failed on f21b6e29c2 (https://github.com/manaflow-ai/cmux/actions/runs/38349974735). It does not block the merge; a red guard merged into main breaks it for every open PR.

Run canonical CMUX CI guard profile

GitHub-hosted runner label: .github/workflows/coderouter-publish-npm.yml:19:    runs-on: ubuntu-latest # github-hosted-required: npm provenance needs a GitHub-hosted runner
GitHub-hosted runner label: .github/workflows/coderouter-publish-pypi.yml:19:    runs-on: ubuntu-latest # github-hosted-required: PyPI trusted publishing needs a GitHub-hosted runner

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Run canonical CMUX CI guard profile'

Validate signing secrets are read only in protected environments

Test: python3 tests/test_ci_signing_secrets_need_release_environment.py

Signing secrets must be read only inside a protected environment:
  - coderouter-release.yml build: reads APPLE_CERTIFICATE_BASE64 without a protected environment; declare environment: one of ['release', 'release-next']
  - coderouter-release.yml build: reads APPLE_CERTIFICATE_PASSWORD without a protected environment; declare environment: one of ['release', 'release-next']

Fix: the assertion above names what the guard expects; change the tree to match it.
Reproduce in seconds, no build: scripts/ci/guards-local.sh --step 'Validate signing secrets are read only in protected environments'

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally. This comment is updated in place on each push.

@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.

🔇 Additional comments (3)
cmuxTests/SurfacePaneFactoryFocusTests.swift (2)

152-163: LGTM!


880-891: LGTM!

Sources/GhosttyTerminalView.swift-9534-9541 (1)

9534-9541: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Extract the shared pointer-down activation to one action path.

focusFromPointerDown() has the same two calls: activateContainerFocusFromPointerDown() and clearReparentFocusSuppressionForPointerFocus(). otherMouseDown now repeats them inline. The two paths can drift apart. otherMouseDown could reuse the shared helper, but focusFromPointerDown() also calls makeFirstResponder. Extract only the activation part into one private method. Call that method from both focusFromPointerDown() and otherMouseDown.

As per coding guidelines: "The same behavior wired separately through multiple surfaces instead of one shared action path."

Proposed refactor
+    private func activatePaneFocusFromPointerDown() {
+        guard let terminalSurface else { return }
+        activateContainerFocusFromPointerDown()
+        terminalSurface.hostedView.clearReparentFocusSuppressionForPointerFocus()
+    }
+
     func focusFromPointerDown() {
-        if let terminalSurface {
-            activateContainerFocusFromPointerDown()
-            terminalSurface.hostedView.clearReparentFocusSuppressionForPointerFocus()
-        }
+        activatePaneFocusFromPointerDown()
-        if let terminalSurface {
-            activateContainerFocusFromPointerDown()
-            terminalSurface.hostedView.clearReparentFocusSuppressionForPointerFocus()
-        }
+        activatePaneFocusFromPointerDown()

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 5cd97c62-e867-428c-b14e-5c80f4893da2
📥 Commits

Reviewing files that changed from the base of the PR and between a077b73 and f21b6e2.

📒 Files selected for processing (2)
  • Sources/GhosttyTerminalView.swift
  • cmuxTests/SurfacePaneFactoryFocusTests.swift

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Closing in the 10-13 stale sweep: parked stale/conflicting work has no approved classic merge path. Reopen if still wanted.

@cursor

cursor Bot commented Oct 13, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@teamleaderleo teamleaderleo added the needs a call Finished and held for a team design or product decision (see #13742 and the gallery in #15427) label Oct 13, 2026
@blacksmith-sh

This comment has been minimized.

@blacksmith-sh

blacksmith-sh Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
GitHub-hosted runner label: .github/workflows/
coderouter-publish-npm.yml:19: runs-on: ubuntu-latest # github-hosted-required: npm
provenance needs a GitHub-hosted runner
View Logs
GitHub-hosted runner label: .github/workflows/
coderouter-publish-pypi.yml:19: runs-on: ubuntu-latest # github-hosted-required: PyP
I trusted publishing needs a GitHub-hosted runner
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

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

needs a call Finished and held for a team design or product decision (see #13742 and the gallery in #15427)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants