Skip to content

Fix first-attempt Dock pane drops - #7536

Merged
austinywang merged 18 commits into
mainfrom
issue-7529-sidebar-dock-drop-unfocused
Jul 8, 2026
Merged

austinywang merged 18 commits into
mainfrom
issue-7529-sidebar-dock-drop-unfocused

Conversation

@austinywang

@austinywang austinywang commented Jul 7, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #7529

Summary

  • route terminal portal drop mouse-up through the same accepted drop-target session that produced hover/drop-zone feedback
  • cover unfocused main-to-Dock and Dock-to-main pane drops through the real pane drop target and shared Dock move paths
  • keep external Finder file URL drops out of the terminal portal mouse-up pass-through path while preserving file-preview/pane/sidebar drag routing
  • keep browser raw file URL drops in WebKit; browser pane drops continue through the normal pane drop target path

Testing

  • python3 scripts/swift_file_length_budget.py
  • ./scripts/lint-pbxproj-test-wiring.sh
  • scripts/check-pbxproj.sh
  • git diff --check
  • ./scripts/reload.sh --tag issue-7529-dock-drop after the final pushed commit
  • Not run locally: unit-test suites, XCUITests, simulators, or bare xcodebuild per the issue plan; CI owns heavy test execution.

Demo Video

  • Not applicable for this routing/state fix. A tagged Debug build is provided for local dogfood.

Review Trigger

  • Addressed CodeRabbit/Cursor feedback on pointer-up drag pasteboard routing and raw file URL drop handling; review bots are rerunning on the latest pushed commit.

Checklist

  • Regression tests added in their own file and wired into cmuxTests
  • Drop mouse-up routing is gated by an accepted active drop session, not stale pasteboard state
  • Raw external file URL mouse-up drops do not use the terminal portal pass-through path
  • No .github/*.tsv edits and no submodule edits
  • Swift file length budget passes

Summary by CodeRabbit

  • Bug Fixes
    • Drag-and-drop portal routing now aligns pointer-up behavior with hover/drag, with capture and pass-through gated by active drop-drag state and pasteboard types.
    • Unfocused Dock/pane drops now consistently dock and undock surfaces in both directions.
    • Dock and browser-pane live-surface transfers are more accurately validated per destination; Dock moves no longer leave the source workspace broken—an empty replacement panel is created when needed.
  • Tests
    • Added/expanded automated coverage for terminal and browser portal routing (including restricted file URL negative cases) and unfocused Dock/pane docking side effects.

Note

Medium Risk
Touches drag hit-testing, Dock surface moves, and workspace empty-state preservation; wrong gating could break drops or leave workspaces in an inconsistent state, but behavior is heavily regression-tested.

Overview
Fixes first-attempt Dock pane drops (including unfocused windows) by tracking an active pane drop session on WindowTerminalHostView and routing terminal portal mouse-up through the same path as hover, gated on that session (and sidebar reorder drags) instead of stale pasteboard state. Raw Finder file URL mouse-up no longer uses terminal portal pass-through; tab/file-preview/sidebar payloads still do when a drop was accepted.

Dock moves use shared canMoveSurfaceIntoDock (blocks remote tmux mirror panels); moving the last main panel into the same workspace/window Dock is allowed and preserves the emptied workspace via a replacement terminal instead of tearing it down. Browser/terminal pane targets call the same eligibility check for Dock routing.

Adds DockPaneDropUnfocusedRoutingTests and updates window Dock lifecycle expectations.

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

@vercel

vercel Bot commented Jul 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled Jul 8, 2026 6:47am
cmux-staging Building Building Preview, Comment Jul 8, 2026 6:47am

@coderabbitai

coderabbitai Bot commented Jul 7, 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

Pointer-up drag routing now consults active pane-drop state, pane drop targets maintain that state during drag lifecycles, Dock moves can preserve an emptied source workspace, and new tests cover unfocused Dock drop flows and the external-drop path.

Changes

Pointer-up routing and Dock drop handling

Layer / File(s) Summary
Pointer-up routing decisions
Sources/WindowInputRoutingContext.swift, Sources/TerminalWindowPortal.swift, Sources/DragOverlayRoutingPolicy.swift
allowsTerminalPortalDragRouting includes pointer-up events, terminal hit testing consults active pane-drop state, and terminal-portal pass-through now branches on event kind and active-drop state.
Pane drop session and registration
Sources/PaneDropRoutingSession.swift, Sources/PaneDropRoutingRegistration.swift, Sources/PaneDropRoutingSupport.swift, Sources/TerminalPaneDropTargetView.swift
Active drag sequence tracking, host-view lookup, and pane-drop routing updates and clears are wired through drag-enter, drag-update, drag-exit, and view-removal paths.
Dock move preservation
Sources/AppDelegate+DockSurfaceMove.swift, Sources/Workspace.swift
Moves into a Dock now allow the previously blocked same-workspace and same-window cases, and empty-source cleanup can preserve the source workspace by creating a replacement panel and detaching kept-open remote tmux state.
Routing and dock drop tests
cmuxTests/DockPaneDropUnfocusedRoutingTests.swift, cmuxTests/WindowDockLifecycleTests.swift, cmux.xcodeproj/project.pbxproj
A new unfocused Dock drop suite covers routing, hover, and move behavior; the Dock lifecycle test switches to the external-drop path; and the project file registers the new sources.

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

Sequence Diagram(s)

sequenceDiagram
  participant WindowInputRoutingContext
  participant WindowTerminalHostView
  participant PaneDropRoutingSession
  participant DragOverlayRoutingPolicy

  WindowTerminalHostView->>WindowInputRoutingContext: allowsTerminalPortalDragRouting(.pointerUp)
  WindowInputRoutingContext-->>WindowTerminalHostView: true
  WindowTerminalHostView->>PaneDropRoutingSession: hasActiveDropDrag
  PaneDropRoutingSession-->>WindowTerminalHostView: active or inactive
  WindowTerminalHostView->>DragOverlayRoutingPolicy: shouldPassThroughTerminalPortalHitTesting(..., hasActiveDropDrag)
  DragOverlayRoutingPolicy-->>WindowTerminalHostView: pass through or block
Loading

Possibly related PRs

  • manaflow-ai/cmux#3299: Also changes terminal-portal drag routing and hit-testing behavior in Sources/TerminalWindowPortal.swift and the overlay pass-through path.
  • manaflow-ai/cmux#3539: Touches the same pane drop routing and fileURL-related drop handling paths around overlay hit testing.
  • manaflow-ai/cmux#7379: Related remote-tmux mirror lifecycle cleanup and detach behavior in Workspace.
🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also includes broader browser file-URL, remote tmux, and workspace lifecycle changes beyond fixing first-attempt Dock pane drops. Split the broader routing and workspace updates into separate PRs, or clearly tie them to #7529 with explicit rationale and acceptance criteria.
✅ Passed checks (24 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address #7529 by routing unfocused pane drops through the accepted drop session so the first drop succeeds.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cmux Swift Actor Isolation ✅ Passed New routing/session state is @MainActor, UI access stays on AppKit/main-actor types, and the only @unchecked Sendable/shared-state debt is pre-existing or test-only.
Cmux Swift Blocking Runtime ✅ Passed Changed production Swift adds MainActor routing/session state only; no new waits, sleeps, sync dispatch, or locks appear in the touched hunks. Existing Workspace asyncAfter/waits are pre-existing.
Cmux Browser Automation Off-Main ✅ Passed PR only changes pane-drop UI/Dock routing; it does not touch TerminalController.swift or ControlCommandExecutionPolicy.swift, so the browser socket-automation rule isn’t implicated.
Cmux Expensive Synchronous Load ✅ Passed No new sync agent-history load landed on an interactive path; the only RestorableAgentSessionIndex.load() call pre-existed unchanged, and the diff is routing-only.
Cmux Cache Substitution Correctness ✅ Passed The changes only add transient drag-routing state and event-driven updates; no fresh persistence/history/snapshot read was replaced by a stale cache.
Cmux No Hacky Sleeps ✅ Passed Diff only touches Swift/test files; no TS/JS/shell/build-runtime changes or sleep/wait additions were found.
Cmux Algorithmic Complexity ✅ Passed New routing uses bounded drag metadata and O(1) session/set checks; no new nested scans or per-target rescans over scalable workspace/pane collections appear in prod code.
Cmux Swift Concurrency ✅ Passed The diff only adds synchronous dock-routing guards and test assertion changes; it does not introduce DispatchQueue, Combine, completion handlers, or fire-and-forget Tasks.
Cmux Swift @Concurrent ✅ Passed No changed nonisolated async or @concurrent misuse found; the new async tests are @MainActor UI-bound and the helper they await is actor-serialized by design.
Cmux Swift File And Package Boundaries ✅ Passed Only small AppKit drag-routing glue/tests were added; no new oversized production file or >250-line growth, and the 2k-line portal file was touched incidentally.
Cmux Swiftpm Lockfiles ✅ Passed PASS: The PR only adds Swift source/test files to project.pbxproj; no .gitignore or Package.resolved files changed, and no SwiftPM package-reference diff appears.
Cmux Swift Logging ✅ Passed No added or modified runtime logging in the PR diff; touched Swift files added no print/debugPrint/dump/NSLog/Logger changes, and existing Workspace NSLog lines were unchanged.
Cmux User-Facing Error Privacy ✅ Passed The touched changes are routing/state logic and tests only; I found no new or modified user-facing error/alert text exposing vendor, internal, or secret details.
Cmux Full Internationalization ✅ Passed No new user-facing production text or locale resources were added; changes are logic-only plus tests/comments/debug logs.
Cmux Swiftui State Layout ✅ Passed Changed code is AppKit drag/drop routing and bridge views; no new SwiftUI state/layout patterns were introduced, and the lone ObservableObject is test-only scaffolding.
Cmux Architecture Rethink ✅ Passed PASS: The PR adds a small, single-owner drag session/registration bridge and shared routing paths; no sleeps, polling, locks, observers, or duplicate lifecycle owners introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR only changes drag/drop routing and Dock/workspace lifecycle; it adds no standalone NSWindow/NSPanel/WindowGroup code and doesn’t touch cmuxAuxiliaryWindowIdentifiers.
Cmux Source Artifacts ✅ Passed All changed paths are source/test/config files (Swift, pbxproj); none are logs, screenshots, temp dirs, caches, or other artifact paths.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Changed Sources files only add production drag-routing helpers; no new #if DEBUG/test-only names or widened test seams in the diff.
Cmux No Ambient Global State ✅ Passed No new ambient global state: routing lives on WindowTerminalHostView/PaneDropRoutingSession, and added helpers are instance methods or private extensions, not global API.
Title check ✅ Passed The title is concise and accurately summarizes the PR's main change: fixing first-attempt Dock pane drops.
Description check ✅ Passed The description covers summary, testing, demo note, review trigger, and checklist, with enough detail for the template.
✨ 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-7529-sidebar-dock-drop-unfocused

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 Jul 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes first-attempt Dock pane drops by sharing drag state between hover and mouse-up routing. The main changes are:

  • Active pane drop sessions for terminal portal pointer-up routing.
  • Safer pasteboard filtering for raw file URL drops.
  • Shared Dock move eligibility checks for terminal and browser pane drops.
  • Source workspace preservation when its last panel moves into its own Dock.
  • Regression tests and Xcode project wiring for the Dock drop paths.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
Sources/AppDelegate+DockSurfaceMove.swift Allows same-Dock last-panel moves while preserving the source workspace with a replacement terminal.
Sources/PaneDropRoutingSession.swift Adds main-actor tracking for active pane drop drag sequence numbers.
Sources/PaneDropRoutingRegistration.swift Registers and clears pane drop sessions against the owning terminal host view.
Sources/TerminalPaneDropTargetView.swift Connects terminal pane drag lifecycle callbacks to Dock routing and active drop-session state.
Sources/TerminalWindowPortal.swift Routes terminal portal mouse-up events only when an active pane drop or sidebar drag is present.
Sources/DragOverlayRoutingPolicy.swift Separates pointer-drag and pointer-up pasteboard rules for terminal portal routing.
Sources/BrowserPaneDropTargetView.swift Uses the shared Dock move eligibility check for browser pane Dock drops.
Sources/Workspace.swift Centralizes remote tmux mirror detach cleanup behind an idempotent helper.
cmux.xcodeproj/project.pbxproj Wires the new routing files and Dock pane drop tests into the Xcode project.
cmuxTests/DockPaneDropUnfocusedRoutingTests.swift Adds tests for unfocused Dock pane drop routing and active drop-session cleanup.
cmuxTests/WindowDockLifecycleTests.swift Adds coverage for moving the last main panel into the window Dock.

Reviews (9): Last reviewed commit: "Reject remote tmux Dock transfer for #75..." | Re-trigger Greptile

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Sources/DragOverlayRoutingPolicy.swift (2)

379-396: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

.pointerUp terminal routing is missing the sidebar-reorder check present in .pointerDrag.

For .pointerDrag, shouldPassThroughTerminalPortalHitTesting delegates to shouldPassThroughPortalHitTesting, which includes hasSidebarReorder. The new .pointerUp branch is hand-rolled as hasBonsplitTabTransfer(pasteboardTypes) || hasFileDropPayload(pasteboardTypes) and omits the sidebar-tab-reorder check entirely. Per the PR objective ("allowing pane, file-preview, and sidebar drag payloads to reach SwiftUI drop targets at drop time"), sidebar drag payloads dropped on a terminal portal would fail to route correctly on mouse-up, unlike during drag/hover.

🐛 Proposed fix to include sidebar-reorder payloads in the drop-time path
         case .pointerUp:
-            return hasBonsplitTabTransfer(pasteboardTypes) || hasFileDropPayload(pasteboardTypes)
+            return hasBonsplitTabTransfer(pasteboardTypes)
+                || hasSidebarTabReorder(pasteboardTypes)
+                || hasFileDropPayload(pasteboardTypes)
🤖 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/DragOverlayRoutingPolicy.swift` around lines 379 - 396, The
.pointerUp path in shouldPassThroughTerminalPortalHitTesting is missing the
sidebar reorder payload check that .pointerDrag already gets through
shouldPassThroughPortalHitTesting. Update the .pointerUp branch in
DragOverlayRoutingPolicy to include the same sidebar-reorder condition alongside
hasBonsplitTabTransfer and hasFileDropPayload, so drop-time routing matches the
drag/hover behavior for sidebar-tab drags.

363-374: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate .pointerDrag / .pointerUp case bodies — merge to avoid divergence.

The two cases are byte-for-byte identical. Beyond DRY, keeping them separate is exactly what let the sibling shouldPassThroughTerminalPortalHitTesting function's .pointerUp branch silently diverge and drop the sidebar-reorder check (see other comment). Merging the cases here removes one such divergence opportunity.

♻️ Proposed consolidation
         switch routingContext.eventKind {
-        case .pointerDrag:
-            return hasTabTransfer
-                || hasFilePreviewTransfer(pasteboardTypes)
-                || hasSidebarReorder
-        case .pointerUp:
-            return hasTabTransfer
-                || hasFilePreviewTransfer(pasteboardTypes)
-                || hasSidebarReorder
+        case .pointerDrag, .pointerUp:
+            return hasTabTransfer
+                || hasFilePreviewTransfer(pasteboardTypes)
+                || hasSidebarReorder
         case .pointerHover:
             return hasTabTransfer || hasSidebarReorder
🤖 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/DragOverlayRoutingPolicy.swift` around lines 363 - 374, Merge the
identical `.pointerDrag` and `.pointerUp` branches in the routing switch so they
share one body, and keep the combined logic in the same decision point inside
the event-kind handling. Use the surrounding `routingContext.eventKind` switch
in `DragOverlayRoutingPolicy` to consolidate the `hasTabTransfer ||
hasFilePreviewTransfer(pasteboardTypes) || hasSidebarReorder` check, reducing
the chance of future divergence between drag and up handling.
🤖 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/WindowInputRoutingContext.swift`:
- Around line 93-104: The drag-routing logic currently treats `.pointerUp` as
eligible in `allowsBrowserPortalDragRouting` and
`allowsTerminalPortalDragRouting`, which lets
`TerminalWindowPortal.performHitTest`, `BrowserWindowPortal.hitTest`, and
`DragOverlayRoutingPolicy` read drag pasteboard types during ordinary mouse-up
events. Update `WindowInputRoutingContext` so `.pointerUp` is not routed through
the drag-pasteboard path unless there is an explicit active-drag signal, either
by removing `.pointerUp` from the drag-routing checks or by adding a separate
guard used by the hit-test/routing methods before touching `NSPasteboard(name:
.drag).types`.

---

Outside diff comments:
In `@Sources/DragOverlayRoutingPolicy.swift`:
- Around line 379-396: The .pointerUp path in
shouldPassThroughTerminalPortalHitTesting is missing the sidebar reorder payload
check that .pointerDrag already gets through shouldPassThroughPortalHitTesting.
Update the .pointerUp branch in DragOverlayRoutingPolicy to include the same
sidebar-reorder condition alongside hasBonsplitTabTransfer and
hasFileDropPayload, so drop-time routing matches the drag/hover behavior for
sidebar-tab drags.
- Around line 363-374: Merge the identical `.pointerDrag` and `.pointerUp`
branches in the routing switch so they share one body, and keep the combined
logic in the same decision point inside the event-kind handling. Use the
surrounding `routingContext.eventKind` switch in `DragOverlayRoutingPolicy` to
consolidate the `hasTabTransfer || hasFilePreviewTransfer(pasteboardTypes) ||
hasSidebarReorder` check, reducing the chance of future divergence between drag
and up handling.
🪄 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: d30e8e80-d6f9-4f02-b933-5e772858283d

📥 Commits

Reviewing files that changed from the base of the PR and between 0484cac and 847ee17.

📒 Files selected for processing (4)
  • Sources/DragOverlayRoutingPolicy.swift
  • Sources/WindowInputRoutingContext.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/DockPaneDropUnfocusedRoutingTests.swift

Comment thread Sources/WindowInputRoutingContext.swift
@austinywang
austinywang force-pushed the issue-7529-sidebar-dock-drop-unfocused branch from 847ee17 to 11d0900 Compare July 7, 2026 22:56
Comment thread Sources/DragOverlayRoutingPolicy.swift Outdated
Comment thread Sources/PaneDropRoutingSupport.swift Outdated

@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/PaneDropRoutingSupport.swift`:
- Around line 53-81: PaneDropRoutingSession can remain active if
PaneDropTargetView is torn down mid-drag because draggingExited(_:) and
performDragOperation(_:) may never run. Add a view-lifecycle cleanup path in
PaneDropTargetView such as willMove(toSuperview:), viewDidMoveToWindow, or
deinit that calls PaneDropRoutingSession.clearActiveDropDrag(nil) or otherwise
removes the current sequence, so the active drop state is always reset even when
the view disappears.
🪄 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: 9d29f088-6b75-4133-92ec-cf983611a431

📥 Commits

Reviewing files that changed from the base of the PR and between 847ee17 and 44f74d0.

📒 Files selected for processing (7)
  • Sources/DragOverlayRoutingPolicy.swift
  • Sources/PaneDropRoutingSupport.swift
  • Sources/TerminalPaneDropTargetView.swift
  • Sources/TerminalWindowPortal.swift
  • Sources/WindowInputRoutingContext.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/DockPaneDropUnfocusedRoutingTests.swift

Comment thread Sources/PaneDropRoutingSupport.swift
Comment thread Sources/TerminalPaneDropTargetView.swift
@austinywang
austinywang enabled auto-merge July 8, 2026 03:06

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

Cursor Bugbot has reviewed your changes using default effort 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 002b2d9. Configure here.

guard sourceWorkspace.panels.isEmpty else { return }
sourceWorkspace.detachRemoteTmuxMirrorKeptOpenLocallyIfNeeded()
_ = sourceWorkspace.createReplacementTerminalPanel()
sourceWorkspace.scheduleTerminalGeometryReconcile()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Preserve workspace skips focus reconcile

Medium Severity

When the last main panel is moved into its own Dock and the source workspace is preserved via preserveEmptySourceWorkspaceAfterSurfaceMove, a replacement terminal is created but the workspace never focuses or selects that tab and does not schedule focus reconciliation, unlike other empty-workspace replacement paths in Workspace.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 002b2d9. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a314102. The replacement helper now centralizes replacement tab selection, and the Dock-preserve caller opts out of AppKit focus reassertion so the destination Dock keeps keyboard ownership.

— Claude Code

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/TerminalPaneDropTargetView.swift (1)

115-146: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fail closed for ineligible Dock drops
At Sources/TerminalPaneDropTargetView.swift:115-146, canMoveSurfaceIntoDock(...) == false falls through to workspace.performPortalPaneDrop(...), and that path will resolve the Dock pane ID against the workspace by falling back to the focused/first pane. Reject here instead so non-movable Dock drops can’t be routed to the wrong workspace pane.

🤖 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/TerminalPaneDropTargetView.swift` around lines 115 - 146, The
Dock-drop handling in TerminalPaneDropTargetView should fail closed when
canMoveSurfaceIntoDock(sourceTabId:destinationDock:) is false instead of falling
through to workspace routing. Update the drop decision around the existing
PaneDragTransfer.decode and dock.performPortalPaneDrop path so ineligible Dock
drops are rejected early, preventing the fallback workspace pane resolution from
sending the drop to the wrong pane.

Source: Path instructions

🤖 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/Workspace.swift`:
- Around line 3466-3473: The remote-tmux session-ended path in
handleRemoteTmuxSessionEndedKeepingWorkspaceOpenIfNeeded() creates a replacement
terminal panel but does not perform the same post-close focus and selection
handling used in the didCloseTab empty-workspace path. After
createReplacementTerminalPanel() is called, mirror the existing empty-workspace
logic by focusing the pane, selecting the newly created tab, and applying tab
selection so the workspace ends on an active terminal instead of an unselected
pane.

---

Outside diff comments:
In `@Sources/TerminalPaneDropTargetView.swift`:
- Around line 115-146: The Dock-drop handling in TerminalPaneDropTargetView
should fail closed when canMoveSurfaceIntoDock(sourceTabId:destinationDock:) is
false instead of falling through to workspace routing. Update the drop decision
around the existing PaneDragTransfer.decode and dock.performPortalPaneDrop path
so ineligible Dock drops are rejected early, preventing the fallback workspace
pane resolution from sending the drop to the wrong pane.
🪄 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: 488ff58f-70de-4e5f-a2dd-4f8c570092ed

📥 Commits

Reviewing files that changed from the base of the PR and between d7723d1 and 002b2d9.

📒 Files selected for processing (8)
  • Sources/AppDelegate+DockSurfaceMove.swift
  • Sources/BrowserPaneDropTargetView.swift
  • Sources/TerminalPaneDropTargetView.swift
  • Sources/TerminalWindowPortal.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/DockPaneDropUnfocusedRoutingTests.swift
  • cmuxTests/WindowDockLifecycleTests.swift

Comment thread Sources/Workspace.swift

This branch was previously deployed

1 inactive deployment
Preview – cmux — 002b2d91 Deployed Jul 8, 2026 by vercel[bot]
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.

Dragging a pane into the sidebar Dock fails on first attempt when Dock is unfocused (hover highlight shows, drop no-ops)

1 participant