Skip to content

Fix horizontal tab insertion positions - #202

Merged
austinywang merged 4 commits into
mainfrom
issue-9746-horizontal-tab-insert
Aug 7, 2026
Merged

austinywang merged 4 commits into
mainfrom
issue-9746-horizontal-tab-insert

Conversation

@austinywang

@austinywang austinywang commented Aug 7, 2026 •

Copy link
Copy Markdown

Summary

  • replace competing per-tab drop delegates with one pane-local AppKit drop destination
  • resolve every insertion slot from live tab-frame midpoints, including translated split panes
  • route same-pane and cross-pane mutations through the shared controller APIs
  • retain file-drop behavior and suppress no-op reorder indicators

Regression coverage

The first commit adds failing pointer-to-index regression tests. The second commit adds the implementation plus behavior coverage for middle insertion, cross-pane insertion, drag pasteboard publication races, inactive workspaces, and translated AppKit hit testing.

The review follow-up rejects stale pasteboard-only transfers that cannot complete and validates the linear model/visual frame-order contract.

Testing

  • arch -arm64 "$(xcrun --find swift)" test
  • 216 XCTest cases and 7 Swift Testing cases passed
  • no new warnings observed

Related cmux issue: manaflow-ai/cmux#9746

Summary by CodeRabbit

  • New Features

    • Added native drag-and-drop support across the full tab strip, including trailing empty space.
    • Supports tab transfers between panes, tab reordering, and file drops.
    • Provides accurate insertion indicators and handles empty tab strips.
  • Bug Fixes

    • Prevents no-op moves and invalid drops.
    • Improves tab order updates and drop-target detection.
  • Tests

    • Added coverage for tab reordering, cross-pane transfers, file drops, hit testing, and insertion positions.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR replaces SwiftUI tab drop handling with a native AppKit destination. It adds centralized tab and file drop handling, geometry-based insertion resolution, reorder notification changes, SwiftUI integration, and updated native drop coverage.

Changes

Tab-bar drag and drop

Layer / File(s) Summary
Drop index and reorder contracts
Sources/Bonsplit/Internal/Views/TabDropIndexResolver.swift, Sources/Bonsplit/Public/BonsplitController.swift, Tests/BonsplitTests/TabDropIndexResolverTests.swift
The resolver calculates insertion indices from tab geometry. Same-pane moves use reorderTab, which reports only actual order changes.
Tab and file drop handling
Sources/Bonsplit/Internal/Views/TabBarDropHandler.swift, Tests/BonsplitTests/TabBarDropHandlerTests.swift
TabBarDropHandler validates drag state and payloads, handles tab and file drops, supports same-pane and cross-pane moves, and clears drag state.
Native destination integration
Sources/Bonsplit/Internal/Views/TabBarDropDestinationNSView.swift, Sources/Bonsplit/Internal/Views/TabBarDropDestinationView.swift, Sources/Bonsplit/Internal/Views/TabBarView.swift, Tests/BonsplitTests/BonsplitTests.swift
The AppKit destination captures supported drags across the tab strip, updates the drop indicator, resolves insertion positions, performs drops, and replaces the previous SwiftUI drop wiring.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DragSource
  participant TabBarDropDestinationNSView
  participant TabBarDropHandler
  participant BonsplitController
  DragSource->>TabBarDropDestinationNSView: Send tab or file drag
  TabBarDropDestinationNSView->>TabBarDropHandler: Validate payload and insertion index
  TabBarDropHandler->>BonsplitController: Reorder or move tab
  TabBarDropDestinationNSView-->>DragSource: Return drag operation and clear indicator
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.45% 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 clearly and concisely describes the PR's main change to horizontal tab insertion positions.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-9746-horizontal-tab-insert

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.

@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: 1

🤖 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 `@Sources/Bonsplit/Internal/Views/TabBarDropHandler.swift`:
- Around line 41-45: Update the hasTabTransfer branch in TabBarDropHandler to
return .move only when the transfer source pane differs from the destination
pane and onExternalTabDrop is non-nil; otherwise return an empty result. Add a
regression test covering a same-process transfer with no live drag state and no
external handler, asserting it is rejected.
🪄 Autofix

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 Plus

Run ID: 672e3528-41ff-4b04-beb9-f4f36747facd

📥 Commits

Reviewing files that changed from the base of the PR and between ca2e592 and 7d2aa11.

📒 Files selected for processing (10)
  • Sources/Bonsplit/Internal/Views/TabBarDropDestinationNSView.swift
  • Sources/Bonsplit/Internal/Views/TabBarDropDestinationView.swift
  • Sources/Bonsplit/Internal/Views/TabBarDropHandler.swift
  • Sources/Bonsplit/Internal/Views/TabBarView.swift
  • Sources/Bonsplit/Internal/Views/TabDropIndexResolver.swift
  • Sources/Bonsplit/Public/BonsplitController.swift
  • Tests/BonsplitTests/BonsplitTests.swift
  • Tests/BonsplitTests/TabBarDropHandlerTests.swift
  • Tests/BonsplitTests/TabDropDelegateSamePaneFallbackTests.swift
  • Tests/BonsplitTests/TabDropIndexResolverTests.swift
💤 Files with no reviewable changes (1)
  • Tests/BonsplitTests/TabDropDelegateSamePaneFallbackTests.swift

Comment thread Sources/Bonsplit/Internal/Views/TabBarDropHandler.swift Outdated
@austinywang

Copy link
Copy Markdown
Author

Review follow-up:

  • The actionable fallback finding is fixed in 4da56ab and its thread is resolved.
  • The docstring item is a non-blocking coverage warning over internal AppKit override/private helper methods. The new internal types and the resolver ordering contracts are documented; this change adds no public API, so I am intentionally not adding boilerplate comments that merely restate lifecycle method names.

Validation: 216 XCTest cases plus 7 Swift Testing cases pass on arm64 Swift, with no warnings observed.

…-tab-insert

# Conflicts:
#	Sources/Bonsplit/Internal/Views/TabBarView.swift
@austinywang
austinywang merged commit d3d329b into main Aug 7, 2026
5 of 6 checks passed

@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/Bonsplit/Internal/Views/TabBarView.swift (1)

1017-1028: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep accepted targets visible in full-width tab mode.

When isFullWidthTabMode is true, Sources/Bonsplit/Internal/Views/TabBarDropDestinationNSView.swift resolves the right half of the selected tab to selectedIndex + 1 because it uses the full pane.tabs.count with the selected tab’s frame. TabBarView stores that index, but it omits dropZoneAfterTabs in full-width mode and only renders the leading indicator for the visible tab. A valid cross-pane drop can therefore have no insertion indicator. Render an after-selected indicator for this slot, or reject the slot in full-width mode. Add a regression test for both sides of a full-width tab.

Also applies to: 1157-1157

🤖 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/Bonsplit/Internal/Views/TabBarView.swift` around lines 1017 - 1028,
Update TabBarView’s full-width tab rendering to handle the accepted target index
dropTargetIndex == selectedIndex + 1 by displaying the corresponding
after-selected insertion indicator, or explicitly reject that slot before
storing it. Preserve valid indicators for both sides of the selected tab and add
regression coverage for drops on each side in full-width mode.
🤖 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/Bonsplit/Internal/Views/TabBarView.swift`:
- Around line 1017-1028: Update TabBarView’s full-width tab rendering to handle
the accepted target index dropTargetIndex == selectedIndex + 1 by displaying the
corresponding after-selected insertion indicator, or explicitly reject that slot
before storing it. Preserve valid indicators for both sides of the selected tab
and add regression coverage for drops on each side in full-width mode.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 835dfbab-b300-4b0c-8eea-e412f1450fb7

📥 Commits

Reviewing files that changed from the base of the PR and between 7d2aa11 and b11fe3c.

📒 Files selected for processing (7)
  • Sources/Bonsplit/Internal/Views/TabBarDropDestinationNSView.swift
  • Sources/Bonsplit/Internal/Views/TabBarDropHandler.swift
  • Sources/Bonsplit/Internal/Views/TabBarView.swift
  • Sources/Bonsplit/Internal/Views/TabDropIndexResolver.swift
  • Tests/BonsplitTests/BonsplitTests.swift
  • Tests/BonsplitTests/TabBarDropHandlerTests.swift
  • Tests/BonsplitTests/TabDropIndexResolverTests.swift
🚧 Files skipped from review as they are similar to previous changes (4)
  • Sources/Bonsplit/Internal/Views/TabBarDropHandler.swift
  • Tests/BonsplitTests/TabDropIndexResolverTests.swift
  • Tests/BonsplitTests/TabBarDropHandlerTests.swift
  • Tests/BonsplitTests/BonsplitTests.swift

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