Skip to content

Fix minimal tab drag edge hit testing - #126

Merged
austinywang merged 1 commit into
mainfrom
issue-4289-minimal-tab-drag-followup
May 20, 2026
Merged

austinywang merged 1 commit into
mainfrom
issue-4289-minimal-tab-drag-followup

Conversation

@austinywang

@austinywang austinywang commented May 20, 2026 •

Copy link
Copy Markdown

Summary

  • Keep tab hit ownership across the near-titlebar edge in shared-backdrop/minimal tab bars so tab drags do not fall through to app-window dragging.
  • Clear the shared-backdrop active tab fill so the selected tab does not retain a hover-like gray highlight.
  • Add runtime tests covering edge hit ownership and shared-backdrop active background color.

Verification

  • Not run locally; this repository uses CI for tests.

View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Summary by cubic

Fixes tab drag hit testing so minimal/shared-backdrop tabs keep hit ownership near the titlebar edge and don’t fall through to window dragging. Also clears the active tab fill in shared-backdrop mode to remove the hover-like gray highlight.

  • Bug Fixes
    • Keep tab-lane hit ownership across the near-titlebar edge in minimal/shared-backdrop mode.
    • Return .clear for shared-backdrop active tab background in TabBarColors.
    • Add tests for edge hit behavior and active tab background alpha.

Written for commit c39e2ce. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Fixed active tab background color rendering to ensure proper visual appearance in certain display modes.
  • Refactor

    • Improved the tab interaction hit detection system for more reliable and accurate click and drag handling across various user interactions.
  • Tests

    • Added comprehensive test coverage for active tab appearance rendering verification and tab interaction hit-region behavior validation.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 20, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d7dd40a-d688-4e40-ab7b-150a91efa353

📥 Commits

Reviewing files that changed from the base of the PR and between 78de1d7 and c39e2ce.

📒 Files selected for processing (3)
  • Sources/Bonsplit/Internal/Styling/TabBarColors.swift
  • Sources/Bonsplit/Internal/Views/TabBarView.swift
  • Tests/BonsplitTests/BonsplitTests.swift

📝 Walkthrough

Walkthrough

This PR refines the Bonsplit tab bar with two coordinated improvements: shared-backdrop mode now uses fully transparent active tab backgrounds, and tab hit-testing tolerances are centralized and refactored to use lane-based vertical gating plus horizontal x-range checks for more precise interaction bounds.

Changes

Tab Bar Styling and Hit-Testing

Layer / File(s) Summary
Shared backdrop active tab styling
Sources/Bonsplit/Internal/Styling/TabBarColors.swift, Tests/BonsplitTests/BonsplitTests.swift
activeTabBackground(for:) now returns .clear for shared-backdrop mode instead of computing an overlay. Test verifies the rendered alpha is near zero.
Tab hit-testing tolerance refactoring and validation
Sources/Bonsplit/Internal/Views/TabBarView.swift, Tests/BonsplitTests/BonsplitTests.swift
New BonsplitTabItemHitTesting centralizes horizontal and vertical slop with a containsTabLaneHit helper that gates on vertical bounds then checks x coverage per tab. TabItemHitRegionView and TabBarBackgroundNSView now delegate to this centralized logic. Extended tests verify near-titlebar-edge hit behavior for tab lanes and empty chrome areas in minimal mode.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

  • cmux-dev-artifacts#1647: Touches the same tab hit-testing and drag-capture logic in minimal-mode tab bar behavior.
  • cmux-dev-artifacts#1644: The lane-based hit-testing refactor and near-titlebar-edge test assertions directly address minimal-mode drag detection needed for tab-reorder UI test validation.

Possibly related PRs

  • manaflow-ai/bonsplit#124: Both PRs modify hit-testing geometry in TabBarView.swift for tab-item region containment checks with overlapping code-path updates.

Poem

🐰 A tab bar's backdrop now gleams crystal clear,
With hit-testing lanes that boundaries endear.
No overlay tints when shared backdrops align,
Just precise slop constants—the details divine!
Tolerances tamed in one cozy enum,
Tab chrome and chrome capture: a harmonious sum. 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix minimal tab drag edge hit testing' directly addresses one of the main changes: fixing hit-test geometry at the edge of minimal tab bars to prevent drags from falling through to app-window dragging.
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.

✏️ 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-4289-minimal-tab-drag-followup

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.

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

No issues found across 3 files

Re-trigger cubic

@austinywang
austinywang merged commit f7a39b2 into main May 20, 2026
6 checks passed
@austinywang
austinywang deleted the issue-4289-minimal-tab-drag-followup branch May 20, 2026 00:06
@greptile-apps

greptile-apps Bot commented May 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes tab drag hit testing in minimal/shared-backdrop tab bars so that drags along the near-titlebar edge are claimed by the tab rather than falling through to app-window dragging. It also clears the active-tab fill in shared-backdrop mode so the selected tab no longer shows a hover-like gray highlight.

  • TabBarView.swift: Introduces BonsplitTabItemHitTesting with a shared containsTabLaneHit helper that expands the hit region by verticalSlop (6 pt) above the tab bar bounds; individual TabItemHitRegionView hit regions are also widened from dy: -2 to dy: -6.
  • TabBarColors.swift: activeTabBackground(for:) now returns .clear for usesSharedBackdrop, replacing the per-appearance tinted overlay that was visually indistinguishable from a hover state.
  • Tests: Adds testSharedBackdropActiveTabBackgroundIsClear and extends the minimal-mode hit-ownership test to cover both the in-tab and near-titlebar-edge cases.

Confidence Score: 4/5

Safe to merge with low risk; the core hit-testing logic is straightforward and the new tests cover the intended scenario.

The containsTabLaneHit function expands the accepted y-range symmetrically, adding 6 pt of slop below the tab bar bottom edge as well as above it. Whether this matters depends on whether the drag-zone view bounds extend below the visual tab bar — the test only verifies the upward direction. The rest of the changes look correct and well-targeted.

Sources/Bonsplit/Internal/Views/TabBarView.swift — specifically the bounds expansion in containsTabLaneHit and the matching dy change in TabItemHitRegionView.

Important Files Changed

Filename Overview
Sources/Bonsplit/Internal/Views/TabBarView.swift Introduces BonsplitTabItemHitTesting with containsTabLaneHit for unified edge hit-testing; expands vertical slop symmetrically (top and bottom) which may over-extend below the tab bar.
Sources/Bonsplit/Internal/Styling/TabBarColors.swift Replaces computed overlay fill with .clear for shared-backdrop active tab; straightforward and correct change with matching test coverage.
Tests/BonsplitTests/BonsplitTests.swift Adds tests for shared-backdrop active background transparency and near-titlebar-edge tab hit ownership; force-unwrap in color test could cause a crash instead of a clean failure.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Mouse event at point P] --> B{shouldCaptureHit\nbounds.contains P?}
    B -- No --> C[return false\nno window drag capture]
    B -- Yes --> D{BonsplitTabItemHitRegionRegistry\ncontainsWindowPoint}
    D -- true --> C
    D -- false --> E{hitRegion type}
    E -- entireBounds --> F[return true\ncapture for window drag]
    E -- trailingEmptyChrome --> G{P.x < trailingLimit\nand not in tab lane?}
    G -- Yes --> F
    G -- No --> C

    subgraph containsTabLaneHit
        H[bounds.insetBy dx:0 dy:-6\nexpands plus-minus 6pt vertically] --> I{expanded bounds\ncontains P?}
        I -- No --> J[return false]
        I -- Yes --> K{P.x within any\ntabFrame plus-minus horizontalSlop?}
        K -- Yes --> L[return true: tab hit]
        K -- No --> J
    end

    D --> containsTabLaneHit
Loading

Reviews (1): Last reviewed commit: "Fix minimal tab drag edge hit testing" | Re-trigger Greptile

Comment on lines +109 to +115
guard bounds.insetBy(dx: 0, dy: -verticalSlop).contains(localPoint) else {
return false
}
return tabFrames.contains { frame in
localPoint.x >= frame.minX - horizontalSlop
&& localPoint.x <= frame.maxX + horizontalSlop
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Symmetric vertical expansion hits both edges

bounds.insetBy(dx: 0, dy: -verticalSlop) expands the accepted y-range by verticalSlop (6 pt) on both the top edge (toward the titlebar — the intended fix) and the bottom edge (toward app content). Any point within a tab's x-lane that lies up to 6 pt below the tab bar's bottom edge will be claimed as a tab hit. The old per-tab insetBy(dx: -2, dy: -2) also over-extended downward, but only by 2 pt. The new code triples that slop, and shouldCaptureHit only guards this with bounds.contains(point) on the drag-zone view, so if that view's bounds extend past the tab bar's visual bottom (e.g. it fills the full content view), the downward expansion starts suppressing window-drag events for touches in the app content area. The test only verifies the upward direction.

Comment on lines +1277 to +1280
let active = NSColor(TabBarColors.activeTabBackground(for: appearance)).usingColorSpace(.sRGB)!

var alpha: CGFloat = 1
active.getRed(nil, green: nil, blue: nil, alpha: &alpha)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Force-unwrapping usingColorSpace(.sRGB) will crash the test rather than fail it cleanly if the color-space conversion ever returns nil. Color.clear resolves to NSColor.clear (calibrated gray-space), and while conversion to sRGB works today, a guarded call makes the failure mode explicit and the intent clearer.

Suggested change
let active = NSColor(TabBarColors.activeTabBackground(for: appearance)).usingColorSpace(.sRGB)!
var alpha: CGFloat = 1
active.getRed(nil, green: nil, blue: nil, alpha: &alpha)
let active = try XCTUnwrap(
NSColor(TabBarColors.activeTabBackground(for: appearance)).usingColorSpace(.sRGB),
"Could not convert activeTabBackground color to sRGB"
)
var alpha: CGFloat = 1
active.getRed(nil, green: nil, blue: nil, alpha: &alpha)

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.

1 participant