cmux-next: the drop ring stays gone after a drop and lands where it shows (cx-ohle)

Merged
TE
4d ago
+183-6

After a tab drop, the drop ring could come back and stay on screen. The outline ring fades itself out on hide, but the layout's frame loop kept applying the overlay. The next layout animation after a drop (a new column scrolling the strip, a split settling) drew the ring again at its last target, and nothing hid it again. A hidden ring that animates itself is no longer updated, so it stays gone until the next drag shows it.

The drop also now lands where the ring shows on the layout's own drag paths (NSDraggingDestination and debug.drop_highlight). endTabDrag re-resolved the target at mouse-up without the pane the drag empties, so the split room check could pick a different target than the ring previewed. It now uses the same pane as the last preview. The app's tab drags (TabDragSession) already committed the previewed proposal and are unchanged.

I didn't retune the hitbox bands in DropZoneGeometry (edge bands 28% of the pane, 28 to 180 pt, 12 pt hysteresis). Without a recording of a wrong drop, I had no evidence to retune against.

Verification

scripts/cmux-next/drop-ring-e2e.py drives a tagged app through real mouse events (debug.mouse). It drags a tab to the right edge of its pane, releases, and then opens a new column, which animates the layout. Throughout, it reads the ring's drawn opacity from the new read-only debug.drop_highlight {"report": true}. Both runs used the script at d8e0096da68c, on fleet Debug builds launched on capture mini cua-3:

  • Red, build ohlered-v1 at 42d52312ee29 (probe and test, no fix): FAIL the drop ring is drawn after the drop: 32 samples, first {"ring_opacity": 1, "showing": false, ...}. The window still shows the old ring over the right half.
  • Green, build ohlegrn-v1 at 39171c43cfe1 (fix): PASS the ring is gone after the drop and the pane split.

The endTabDrag change isn't exercised by that script, because app drags don't go through it. I checked it by reading the code.

Localization: no user-facing strings changed.

Changelog

  • Fixed: the tab drop outline no longer reappears and stays after a drop.

Captures

  • Native before/after (archive): cx-ohle drop ring. Before, the ring comes back after the drop and stays; after, it is gone once the tab lands.

🤖 Generated with Claude Code


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


Summary by cubic

Fixes the tab drop ring reappearing and staying visible after a drop, and makes the drop land where the ring shows.

  • A hidden outline ring is no longer updated by the layout's frames, so it stays gone until the next drag starts.
  • endTabDrag now decides the drop with the same pane the preview used, so the split lands where the ring showed.
  • Adds a read-only debug.drop_highlight report and a drop-ring-e2e.py script that verifies the fixes with real mouse events.

[!NOTE]
Low Risk
Targeted UI overlay and tab-drop resolution fixes with a new debug report path and E2E script; no auth, data, or broad API surface changes.

Overview
Fixes cx-ohle: the tab drop outline ring no longer reappears and sticks after release when layout keeps animating (e.g. post-split or new column).

Drop highlight: For self-fading outline styles (drivesOwnMotion), DropHighlightView.apply() stops calling renderer.update() once the highlight is hidden, so later layout frame ticks cannot redraw the ring at its last target.

Drop commit: In-process tab drags now remember the removing pane from preview through endTabDrag, so the committed drop uses the same split-room decision as the ring instead of re-hit-testing without that pane at mouse-up.

Debug / QA: debug.drop_highlight adds report: true (and layout exposes dropHighlightShowing / dropRingOpacity). New scripts/cmux-next/drop-ring-e2e.py drives a real tab drag and fails if the ring is drawn again after drop or the pane did not split.

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