Skip to content

Fix minimal mode workspace and pane tab clicks#2650

Open
austinywang wants to merge 7 commits intomainfrom
issue-2633-minimal-mode-tab-switching
Open

Fix minimal mode workspace and pane tab clicks#2650
austinywang wants to merge 7 commits intomainfrom
issue-2633-minimal-mode-tab-switching

Conversation

@austinywang
Copy link
Copy Markdown
Contributor

@austinywang austinywang commented Apr 6, 2026

Summary

  • restore minimal-mode workspace and pane tab clicks by keeping titlebar-band hits on the real tab chrome instead of the window-drag path or portal hosts
  • teach browser and terminal portal hosts to pass pointer hits through when the point lands on Bonsplit tab-bar regions, including the sibling-tree cases minimal mode creates in the titlebar band
  • add regression coverage for Bonsplit tab-bar background detection in both portal hosts and update vendor/bonsplit to the merged support in manafow-ai/bonsplit#97

Fixes #2633

Root Cause

Minimal mode renders both workspace and pane tabs into the titlebar band. On the affected setup, those clicks could be claimed before the tab views saw them: either by AppKit window-drag behavior or by the browser/terminal portal hosts that sit above the SwiftUI and AppKit tab chrome.

There was also a pane-tab-specific failure mode. In minimal mode, the visible tab strip can live outside the immediate sibling container bounds that the portal hosts would normally reason about, so pointer hits in that area were not being recognized as Bonsplit tab-bar hits.

Implementation

  • set MainWindowHostingView.mouseDownCanMoveWindow = false so titlebar-band SwiftUI tab chrome remains clickable in minimal mode
  • add pass-through checks in WindowBrowserHostView and WindowTerminalHostView for titlebar-band hits plus Bonsplit tab-bar registry/background detection
  • expose DEBUG-only test seams and add browser/terminal regression tests for direct tab-bar hits and sibling-tree minimal-mode cases
  • bump vendor/bonsplit to the merged tab-hit support from manafow-ai/bonsplit#97

Validation

  • rebuilt a tagged Debug app locally with ./scripts/reload.sh --tag issue-2633-pr-cleanup
  • did not run the local test suite because this repo's policy explicitly forbids local test runs

Note

Medium Risk
Touches low-level AppKit hit-testing and window-drag behavior; mistakes could break click/drag routing or cursor behavior across the window chrome and portals.

Overview
Fixes minimal-mode click routing so workspace/pane tab interactions aren’t intercepted by window dragging or portal host views.

MainWindowHostingView is made non-draggable (mouseDownCanMoveWindow = false), and both WindowBrowserHostView and WindowTerminalHostView now pass mouse down/up events through to underlying Bonsplit tab-bar regions (via BonsplitTabBarHitRegionRegistry plus a fallback scan for TabBarBackgroundNSView, including sibling trees used when tabs render into the titlebar band). New unit tests cover tab-bar background hit detection and sibling-tree edge cases.

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

Summary by CodeRabbit

  • Bug Fixes

    • Improved pointer event handling to ensure mouse clicks and interactions properly reach workspace tabs, pane UI, and titlebar elements instead of inadvertently triggering window movement or drag behavior.
  • Tests

    • Added test coverage for pointer hit-detection logic in tab bar and UI element interactions.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 6, 2026

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 16, 2026 2:16am

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai bot commented Apr 6, 2026

This review could not be run because your cubic account has exceeded the monthly review limit. If you need help restoring access, please contact contact@cubic.dev.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

Changes extend portal host view hit-testing to pass through pointer events to Bonsplit pane tab bars and window titlebar, preventing the portal from claiming those interactions. Adds tab-bar background detection helpers with view-hierarchy traversal. Includes comprehensive test coverage for the new detection logic. Updates vendored Bonsplit dependency.

Changes

Cohort / File(s) Summary
Window Initialization
Sources/AppDelegate.swift
Override mouseDownCanMoveWindow in MainWindowHostingView to return false, preventing the SwiftUI hosting view from initiating window-drag behavior and allowing pointer interactions to reach underlying UI.
Portal Hit-Testing Logic
Sources/BrowserWindowPortal.swift, Sources/TerminalWindowPortal.swift
Extended hitTest(_:) methods to add pass-through checks for Bonsplit pane tab bars (both files) and window titlebar (Terminal only). Introduced recursive helper functions to detect tab-bar background views within the view tree and sibling hierarchy. Added DEBUG-scoped test wrappers for background detection. Terminal portal also adjusted import structure to unconditionally import ObjectiveC.
Portal Hit-Testing Tests
cmuxTests/BrowserPanelTests.swift, cmuxTests/TerminalAndGhosttyTests.swift
Added FakeTabBarBackgroundNSView helper class and three test methods per file validating tab-bar background detection: within view tree, in sibling subtree, and regression scenario with bounds outside immediate sibling container.
Vendored Dependencies
vendor/bonsplit
Updated submodule pointer to new commit revision.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • #2379: Directly modifies the same portal hit-testing code paths (WindowTerminalHostView/WindowBrowserHostView.hitTest) with related pass-through and divider interaction helpers.
  • #1950: Updates hit-testing and pass-through logic in the same portal host views, adjusting sidebar-resizer and divider interaction routing.
  • #1717: Introduces the same test files (BrowserPanelTests.swift, TerminalAndGhosttyTests.swift) with matching hit-testing test helper patterns.

Poem

🐰 A rabbit hops through portals clear,
Where tabs now answer, far and near!
No draggy windows block the way—
Hit-tests pass through to save the day! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The code changes comprehensively address all objectives from issue #2633: preventing window-drag behavior on tab chrome, implementing tab-bar hit-region detection for both portal hosts, handling sibling-tree cases in minimal mode, and adding regression test coverage.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to fixing tab interaction issues in minimal mode. Changes include AppDelegate pointer-interactivity fix, portal host hit-test logic, test coverage, and bonsplit submodule update—all aligned with the linked issue.
Description check ✅ Passed PR description comprehensively covers all required template sections: detailed summary of changes, root cause analysis, implementation details, and validation steps performed.
Title check ✅ Passed The title 'Fix minimal mode workspace and pane tab clicks' directly addresses the main objective of the PR: restoring tab click functionality in minimal mode for both workspace and pane tabs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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-2633-minimal-mode-tab-switching

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 6, 2026

Greptile Summary

This PR fixes workspace tab interactions in minimal mode on Intel by overriding mouseDownCanMoveWindow to false on MainWindowHostingView. On Intel, AppKit's default behavior could intercept mouse-down events in the titlebar band as window-drag candidates before SwiftUI received them; since dragging is already handled exclusively through WindowDragHandleView's explicit drag path, this one-line change is safe and consistent with the established mouseDownCanMoveWindow = false pattern used throughout the codebase (NonDraggableHostingView, TitlebarLeadingInsetPassthroughView, DraggableFolderNSView, etc.).

Confidence Score: 5/5

Safe to merge — minimal one-line fix consistent with established codebase patterns; existing drag infrastructure is unaffected

The change is a single property override matching the existing pattern in NonDraggableHostingView, TitlebarLeadingInsetPassthroughView, DraggableFolderNSView, and others. Window dragging is already handled exclusively through WindowDragHandleView with explicit performDrag calls, and the window is created with isMovable = false and isMovableByWindowBackground = false. No P1 or P0 findings.

No files require special attention

Important Files Changed

Filename Overview
Sources/AppDelegate.swift Adds mouseDownCanMoveWindow = false to MainWindowHostingView, preventing AppKit from intercepting titlebar-band mouse-down events as window-drag candidates before SwiftUI receives them

Sequence Diagram

sequenceDiagram
    participant U as User (Intel)
    participant AK as AppKit
    participant MWHV as MainWindowHostingView
    participant DV as DraggableView (WindowDragHandle)
    participant SUI as SwiftUI WorkspaceTab

    Note over MWHV: mouseDownCanMoveWindow = false (new)
    U->>AK: leftMouseDown on titlebar band
    AK->>MWHV: mouseDownCanMoveWindow?
    MWHV-->>AK: false — skip window drag
    AK->>SUI: event propagates to SwiftUI
    SUI-->>U: tab hover/click/right-click works

    Note over DV: Explicit drag path (unchanged)
    U->>AK: leftMouseDown on empty titlebar
    AK->>DV: hitTest → captures hit
    DV->>AK: window.performDrag(with: event)
    AK-->>U: window drag works
Loading

Reviews (1): Last reviewed commit: "Fix minimal mode workspace tab clicks" | Re-trigger Greptile

austinywang and others added 3 commits April 7, 2026 03:11
Pulls in manaflow-ai/bonsplit#91, which adds
NonDraggableHostingView/NonDraggableHostingController and overrides
mouseDownCanMoveWindow=false on the pane container chain so AppKit
no longer treats split-pane tab clicks as window-drag intents in
minimal mode (where the window has no titlebar drag region).

The cmux app already overrides mouseDownCanMoveWindow=false on its
top-level MainWindowHostingView, but Bonsplit's split layout creates
its own nested NSHostingController instances that were never covered
by that override — making split-pane tabs completely unclickable in
minimal mode while terminal surfaces (which use a separate portal
hosting path) kept working.
Pulls in manaflow-ai/bonsplit#92 which makes ThemedSplitView refuse
window-drag promotion. Fixes left pane tab clicks in horizontal splits
when minimal mode (no titlebar drag region) caused AppKit to consume
the mouseUp before SwiftUI's tap gesture fired.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
Sources/BrowserWindowPortal.swift (1)

1257-1323: Extract the Bonsplit background scan before these copies drift.

This recursive TabBarBackgroundNSView detection is now effectively duplicated in Sources/TerminalWindowPortal.swift. A shared helper would make future hit-test fixes much less likely to land in only one portal.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/BrowserWindowPortal.swift` around lines 1257 - 1323, The duplicated
recursive detection logic for TabBarBackgroundNSView (functions
hasBonsplitTabBarBackground and hasUnderlyingBonsplitTabBarBackground, plus
their DEBUG testing wrappers hasBonsplitTabBarBackgroundForTesting and
hasUnderlyingBonsplitTabBarBackgroundForTesting) should be extracted into a
shared helper (e.g., a new PortalHitTest or WindowPortalUtils type) and the
copies in Sources/BrowserWindowPortal.swift and
Sources/TerminalWindowPortal.swift replaced to call that shared API; move the
recursive implementation and any DEBUG-accessible wrappers into the new helper,
keep the same function signatures (or provide thin forwarding functions) so
callers (BrowserWindowPortal and TerminalWindowPortal) only delegate to the
shared hasBonsplitTabBarBackground(...) and
hasUnderlyingBonsplitTabBarBackground(...) helpers to avoid drift.
scripts/build-intel-debug-artifact.sh (1)

370-397: Embedded metadata lacks tarball SHA (intentional but worth noting).

The metadata embedded in the app bundle (line 391) is written before the tarball exists, so tarballSHA256 is empty. The external metadata file (line 424) gets the actual SHA. This is fine for debugging provenance, but if someone extracts and inspects the embedded cmux-dev-build-metadata.json, the missing SHA could be confusing.

Consider adding a comment near line 391 noting this is intentional, or using a placeholder like "pending" instead of empty string to make the intent clearer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build-intel-debug-artifact.sh` around lines 370 - 397, The embedded
metadata currently writes an empty tarball SHA (the last argument to
write_metadata_json is ""), which is confusing; update the call to
write_metadata_json (symbol: write_metadata_json, METADATA_PATH,
PACKAGED_APP_PATH, cmux-dev-build-metadata.json) to pass a clear placeholder
like "pending" instead of "" for the tarball SHA, and/or add a one-line comment
above the call explaining that the real tarball SHA is computed later and the
embedded metadata intentionally contains a placeholder.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/build-ghostty-cli-helper.sh`:
- Around line 110-112: The case pattern using v[0-9]*.[0-9]*.[0-9]* allows
suffixes like -rc1; replace the glob-based check with a strict regex match
against exact_tag to require MAJOR.MINOR.PATCH only. Concretely, in the block
that currently uses case "$exact_tag" and the v[0-9]*.[0-9]*.[0-9]*) branch,
change to an if [[ "$exact_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && return 0 (or
equivalent) so only pure release tags like v1.2.3 pass and tags with suffixes
(e.g., -rc1) do not.

In `@Sources/BrowserWindowPortal.swift`:
- Around line 701-728: The switch in shouldPassThroughToPaneTabBar currently
only allows mouse down/up types so hover and pointer updates are blocked; update
the eventType whitelist in shouldPassThroughToPaneTabBar to also include
.mouseMoved, .mouseEntered, .mouseExited, and .cursorUpdate (and optionally the
dragged variants if desired) so move/enter/exit/cursor updates are treated like
clicks and can pass through to the pane tab bar; keep the rest of the logic
(windowPoint conversion, BonsplitTabBarHitRegionRegistry check and debug
logging) unchanged.

In `@Sources/TerminalWindowPortal.swift`:
- Around line 223-250: The method shouldPassThroughToPaneTabBar currently only
allows mouse down/up events to pass through, which still swallows hover and
cursor events; update the switch on eventType in shouldPassThroughToPaneTabBar
to also allow hover-related event types (at minimum .mouseMoved, .mouseEntered,
.mouseExited, and .cursorUpdate, and optionally
.leftMouseDragged/.rightMouseDragged if relevant) to fall through to the
passthrough logic so registry checks
(BonsplitTabBarHitRegionRegistry.containsWindowPoint) and
Self.hasUnderlyingBonsplitTabBarBackground are used for those events too; keep
the existing registryHit/result computation and debug logging unchanged.

In `@vendor/bonsplit`:
- Line 1: The parent repo's submodule pointer in vendor/bonsplit references
commit 8316cbef which is not present on the remote main branch; open the
vendor/bonsplit repository, ensure commit 8316cbef is present locally, push that
commit to origin/main (or rebase/merge it onto origin/main) so origin/main
contains 8316cbef, then return to the parent repo and update the submodule
pointer (in vendor/bonsplit) to the now-pushed commit and commit that change to
the parent repository.

---

Nitpick comments:
In `@scripts/build-intel-debug-artifact.sh`:
- Around line 370-397: The embedded metadata currently writes an empty tarball
SHA (the last argument to write_metadata_json is ""), which is confusing; update
the call to write_metadata_json (symbol: write_metadata_json, METADATA_PATH,
PACKAGED_APP_PATH, cmux-dev-build-metadata.json) to pass a clear placeholder
like "pending" instead of "" for the tarball SHA, and/or add a one-line comment
above the call explaining that the real tarball SHA is computed later and the
embedded metadata intentionally contains a placeholder.

In `@Sources/BrowserWindowPortal.swift`:
- Around line 1257-1323: The duplicated recursive detection logic for
TabBarBackgroundNSView (functions hasBonsplitTabBarBackground and
hasUnderlyingBonsplitTabBarBackground, plus their DEBUG testing wrappers
hasBonsplitTabBarBackgroundForTesting and
hasUnderlyingBonsplitTabBarBackgroundForTesting) should be extracted into a
shared helper (e.g., a new PortalHitTest or WindowPortalUtils type) and the
copies in Sources/BrowserWindowPortal.swift and
Sources/TerminalWindowPortal.swift replaced to call that shared API; move the
recursive implementation and any DEBUG-accessible wrappers into the new helper,
keep the same function signatures (or provide thin forwarding functions) so
callers (BrowserWindowPortal and TerminalWindowPortal) only delegate to the
shared hasBonsplitTabBarBackground(...) and
hasUnderlyingBonsplitTabBarBackground(...) helpers to avoid drift.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 19ddf4a1-e380-4ead-b7e2-e6b9d7fb7177

📥 Commits

Reviewing files that changed from the base of the PR and between 8f44462 and e2e96fe.

📒 Files selected for processing (10)
  • Sources/AppDelegate.swift
  • Sources/BrowserWindowPortal.swift
  • Sources/TerminalWindowPortal.swift
  • cmuxTests/BrowserPanelTests.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
  • scripts/build-ghostty-cli-helper.sh
  • scripts/build-intel-debug-artifact.sh
  • scripts/clang-xcodebuild-wrapper.sh
  • scripts/reload.sh
  • vendor/bonsplit
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/AppDelegate.swift

Comment thread scripts/build-ghostty-cli-helper.sh Outdated
Comment on lines +110 to +112
case "$exact_tag" in
tip|v[0-9]*.[0-9]*.[0-9]*)
return 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

for tag in tip v1.2.3 v1.2.3-rc1 v1.2.3+meta xcframework-deadbeef; do
  case "$tag" in
    tip|v[0-9]*.[0-9]*.[0-9]*) old="match" ;;
    *) old="no-match" ;;
  esac

  if [[ "$tag" == "tip" || "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    new="match"
  else
    new="no-match"
  fi

  printf "%-22s old=%-8s new=%s\n" "$tag" "$old" "$new"
done

Repository: manaflow-ai/cmux

Length of output: 298


🏁 Script executed:

cd manaflow-ai/cmux && sed -n '100,125p' scripts/build-ghostty-cli-helper.sh

Repository: manaflow-ai/cmux

Length of output: 126


🏁 Script executed:

fd -t f "build-ghostty-cli-helper.sh" 2>/dev/null | head -5

Repository: manaflow-ai/cmux

Length of output: 95


🏁 Script executed:

sed -n '103,125p' scripts/build-ghostty-cli-helper.sh

Repository: manaflow-ai/cmux

Length of output: 844


Tighten release-tag matching to avoid false positives.

On Line 111, v[0-9]*.[0-9]*.[0-9]* will also match non-release tags like v1.2.3-rc1, so the override path is skipped when it shouldn't be.

Proposed fix
-  case "$exact_tag" in
-    tip|v[0-9]*.[0-9]*.[0-9]*)
-      return 0
-      ;;
-  esac
+  if [[ "$exact_tag" == "tip" ]]; then
+    return 0
+  fi
+
+  if [[ "$exact_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+    return 0
+  fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/build-ghostty-cli-helper.sh` around lines 110 - 112, The case pattern
using v[0-9]*.[0-9]*.[0-9]* allows suffixes like -rc1; replace the glob-based
check with a strict regex match against exact_tag to require MAJOR.MINOR.PATCH
only. Concretely, in the block that currently uses case "$exact_tag" and the
v[0-9]*.[0-9]*.[0-9]*) branch, change to an if [[ "$exact_tag" =~
^v[0-9]+\.[0-9]+\.[0-9]+$ ]] && return 0 (or equivalent) so only pure release
tags like v1.2.3 pass and tags with suffixes (e.g., -rc1) do not.

Comment on lines +701 to +728
private func shouldPassThroughToPaneTabBar(
at point: NSPoint,
eventType: NSEvent.EventType?
) -> Bool {
switch eventType {
case .leftMouseDown, .leftMouseUp,
.rightMouseDown, .rightMouseUp,
.otherMouseDown, .otherMouseUp:
break
default:
return false
}

let windowPoint = convert(point, to: nil)
let registryHit = window.map {
BonsplitTabBarHitRegionRegistry.containsWindowPoint(windowPoint, in: $0)
} ?? false
let result = registryHit || Self.hasUnderlyingBonsplitTabBarBackground(at: windowPoint, below: self)
#if DEBUG
if eventType == .leftMouseDown {
dlog(
"portal.brwsr.passThroughTabBar wp=\(Int(windowPoint.x)),\(Int(windowPoint.y)) " +
"registry=\(registryHit ? 1 : 0) result=\(result ? 1 : 0)"
)
}
#endif
return result
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hover still won’t reach pane tabs.

Because Line 705 only whitelists mouse down/up events, split-pane and left-pane tab bars under the browser portal still won’t receive move/enter/exit/cursor updates. Click/right-click will work again, but hover highlight and pointer-state changes stay intercepted by the host.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/BrowserWindowPortal.swift` around lines 701 - 728, The switch in
shouldPassThroughToPaneTabBar currently only allows mouse down/up types so hover
and pointer updates are blocked; update the eventType whitelist in
shouldPassThroughToPaneTabBar to also include .mouseMoved, .mouseEntered,
.mouseExited, and .cursorUpdate (and optionally the dragged variants if desired)
so move/enter/exit/cursor updates are treated like clicks and can pass through
to the pane tab bar; keep the rest of the logic (windowPoint conversion,
BonsplitTabBarHitRegionRegistry check and debug logging) unchanged.

Comment on lines +223 to +250
private func shouldPassThroughToPaneTabBar(
at point: NSPoint,
eventType: NSEvent.EventType?
) -> Bool {
switch eventType {
case .leftMouseDown, .leftMouseUp,
.rightMouseDown, .rightMouseUp,
.otherMouseDown, .otherMouseUp:
break
default:
return false
}

let windowPoint = convert(point, to: nil)
let registryHit = window.map {
BonsplitTabBarHitRegionRegistry.containsWindowPoint(windowPoint, in: $0)
} ?? false
let result = registryHit || Self.hasUnderlyingBonsplitTabBarBackground(at: windowPoint, below: self)
#if DEBUG
if eventType == .leftMouseDown {
dlog(
"portal.term.passThroughTabBar wp=\(Int(windowPoint.x)),\(Int(windowPoint.y)) " +
"registry=\(registryHit ? 1 : 0) result=\(result ? 1 : 0)"
)
}
#endif
return result
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Pane-tab hover is still blocked here.

Line 227 only passes mouse down/up through to the underlying Bonsplit tab bar. That restores click/right-click, but hover/enter/exit/cursor updates for split-pane and left-pane tabs will still get swallowed by the terminal portal host.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalWindowPortal.swift` around lines 223 - 250, The method
shouldPassThroughToPaneTabBar currently only allows mouse down/up events to pass
through, which still swallows hover and cursor events; update the switch on
eventType in shouldPassThroughToPaneTabBar to also allow hover-related event
types (at minimum .mouseMoved, .mouseEntered, .mouseExited, and .cursorUpdate,
and optionally .leftMouseDragged/.rightMouseDragged if relevant) to fall through
to the passthrough logic so registry checks
(BonsplitTabBarHitRegionRegistry.containsWindowPoint) and
Self.hasUnderlyingBonsplitTabBarBackground are used for those events too; keep
the existing registryHit/result computation and debug logging unchanged.

Comment thread vendor/bonsplit
@@ -1 +1 @@
Subproject commit 098d9fa00e2b1d4712f1a46b818ee7d53d4aa31f
Subproject commit 8316cbef7d66f3a98c6051241861abef04f95466
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify bonsplit submodule commit is ancestor of origin/main
cd vendor/bonsplit
git fetch origin main
if git merge-base --is-ancestor HEAD origin/main; then
  echo "✅ Submodule commit is on origin/main"
else
  echo "❌ Submodule commit is NOT on origin/main — push before merging parent PR"
  exit 1
fi

Repository: manaflow-ai/cmux

Length of output: 238


Submodule commit must be pushed to origin/main before merging.

The submodule pointer targets commit 8316cbef, but this commit has not been pushed to the remote main branch of vendor/bonsplit. Per project guidelines, submodule commits must be pushed to their remote main branch before updating the parent repository pointer.

Push the submodule commit to origin/main in the vendor/bonsplit repository, then update the parent pointer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vendor/bonsplit` at line 1, The parent repo's submodule pointer in
vendor/bonsplit references commit 8316cbef which is not present on the remote
main branch; open the vendor/bonsplit repository, ensure commit 8316cbef is
present locally, push that commit to origin/main (or rebase/merge it onto
origin/main) so origin/main contains 8316cbef, then return to the parent repo
and update the submodule pointer (in vendor/bonsplit) to the now-pushed commit
and commit that change to the parent repository.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 10 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/build-ghostty-cli-helper.sh">

<violation number="1" location="scripts/build-ghostty-cli-helper.sh:110">
P2: The release-tag matcher is too permissive and can misclassify non-semver tags as valid release tags, skipping the version override and potentially reintroducing build panics.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread scripts/build-ghostty-cli-helper.sh Outdated
Copy link
Copy Markdown

@cursor cursor bot left a comment

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 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 e2e96fe. Configure here.

) -> Bool {
hasUnderlyingBonsplitTabBarBackground(at: windowPoint, below: portalHost)
}
#endif
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicated tab bar detection logic across portal classes

Low Severity

hasBonsplitTabBarBackground, hasUnderlyingBonsplitTabBarBackground, shouldPassThroughToPaneTabBar, and their testing wrappers are copy-pasted across WindowTerminalHostView and WindowBrowserHostView with identical logic (only debug log prefixes differ). A bug fix applied to one will likely be missed in the other. Since these are static methods operating on plain NSView hierarchies with no dependency on the host class, they could live in a shared extension or utility.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e2e96fe. Configure here.

…-2633-minimal-mode-tab-switching

# Conflicts:
#	scripts/reload.sh
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/AppDelegate.swift`:
- Around line 10330-10334: The click hit-testing is using event.locationInWindow
directly which is in window coordinates; update the clickInspectMonitor handler
(the closure assigned to clickInspectMonitor) to convert the point into the
contentView's coordinate system before calling hitTest: obtain
window.contentView, call its convert(_:from:) with event.locationInWindow and
from: nil (or use convert(event.locationInWindow, from: window) pattern used
elsewhere), then pass that converted point to hitView.hitTest(_: ) (and to any
subsequent hit-testing/logging) so the hit chain is computed in the view's own
coordinate space.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9663601-0fdf-416f-837a-d45e49f8798e

📥 Commits

Reviewing files that changed from the base of the PR and between e2e96fe and 19898e9.

📒 Files selected for processing (2)
  • Sources/AppDelegate.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
✅ Files skipped from review due to trivial changes (1)
  • cmuxTests/TerminalAndGhosttyTests.swift

Comment thread Sources/AppDelegate.swift Outdated
Comment on lines +10330 to +10334
clickInspectMonitor = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseDown]) { event in
guard let window = event.window else { return event }
let windowPoint = event.locationInWindow
let hitView = window.contentView?.hitTest(windowPoint)
let hitClass = hitView.map { String(describing: type(of: $0)) } ?? "nil"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the code at the specified lines
sed -n '10325,10340p' Sources/AppDelegate.swift

Repository: manaflow-ai/cmux

Length of output: 828


🏁 Script executed:

# Also check the context around this function
sed -n '10320,10370p' Sources/AppDelegate.swift | head -60

Repository: manaflow-ai/cmux

Length of output: 2219


🏁 Script executed:

# Search for other hitTest usage in the codebase to see common patterns
rg 'hitTest\(' Sources/ -A 2 -B 2

Repository: manaflow-ai/cmux

Length of output: 16222


🌐 Web query:

NSView hitTest coordinate system AppKit macOS

💡 Result:

In AppKit on macOS, the NSView hitTest(:) method takes an NSPoint parameter that is expressed in the coordinate system of the view's superview, not the receiver's own coordinate system. This method finds the deepest descendant view (including itself) containing that point or returns nil if outside. It is typically invoked by NSWindow for mouse event routing and ignores hidden views. When overriding, convert points between coordinate systems using methods like convert(:from:) if needed.

Citations:


Fix coordinate-space mismatch in click hit-testing.

Line 10333 passes event.locationInWindow directly to hitTest(_:), but the method expects coordinates in the view's own coordinate system. All other hitTest calls in the codebase (AppDelegate, ContentView, BrowserPanelView, etc.) convert the point first using view.convert(event.locationInWindow, from: nil). Without conversion, the hit chain logged will be incorrect.

Suggested fix
     private func installClickInspectMonitor() {
         guard clickInspectMonitor == nil else { return }
         clickInspectMonitor = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseDown]) { event in
             guard let window = event.window else { return event }
             let windowPoint = event.locationInWindow
-            let hitView = window.contentView?.hitTest(windowPoint)
+            let hitView = window.contentView.flatMap { contentView in
+                let contentPoint = contentView.convert(windowPoint, from: nil)
+                return contentView.hitTest(contentPoint)
+            }
             let hitClass = hitView.map { String(describing: type(of: $0)) } ?? "nil"
             var ancestorClasses: [String] = []
             var current: NSView? = hitView
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/AppDelegate.swift` around lines 10330 - 10334, The click hit-testing
is using event.locationInWindow directly which is in window coordinates; update
the clickInspectMonitor handler (the closure assigned to clickInspectMonitor) to
convert the point into the contentView's coordinate system before calling
hitTest: obtain window.contentView, call its convert(_:from:) with
event.locationInWindow and from: nil (or use convert(event.locationInWindow,
from: window) pattern used elsewhere), then pass that converted point to
hitView.hitTest(_: ) (and to any subsequent hit-testing/logging) so the hit
chain is computed in the view's own coordinate space.

@austinywang austinywang changed the title Fix minimal mode workspace tab clicks on Intel Fix minimal mode workspace and pane tab clicks Apr 16, 2026
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/reload.sh">

<violation number="1">
P2: `xcodebuild` is now invoked without the clang wrapper env, removing the compiler-probe workaround path.</violation>

<violation number="2">
P1: Post-build copying of `ghostty/zig-out/bin/ghostty` can overwrite the Xcode-produced helper with a stale or wrong-arch binary.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Minimal Mode breaks Switching Tabs

1 participant