Skip to content

fix(tab-split): drop editor tabs onto browser-tab panes#1006

Open
brennanb2025 wants to merge 4 commits intomainfrom
brennanb2025/tab-split-on-browser-fix
Open

fix(tab-split): drop editor tabs onto browser-tab panes#1006
brennanb2025 wants to merge 4 commits intomainfrom
brennanb2025/tab-split-on-browser-fix

Conversation

@brennanb2025
Copy link
Copy Markdown
Contributor

Summary

  • Fix silent failure when dragging an editor/terminal tab onto a split over a browser tab. Electron <webview> guests run in a separate Chromium process and capture pointerup off the host renderer's event loop, so dnd-kit's onDragEnd never fired even though the blue drop overlay appeared.
  • New webview-drag-passthrough.ts owns a registry of live webview elements and toggles pointer-events: none on them for the duration of an active dnd-kit tab drag. useTabDragSplit flips the toggle via a useEffect keyed on activeDrag, and cleanup restores input even if the hook unmounts mid-drag. HTML5-drag listeners in BrowserPane are untouched — they still handle drag-out of images/text.
  • Make the passthrough registry survive Vite HMR by stashing the Set on window (webviews are registered once on creation, not on every mount, so a fresh module instance would otherwise lose them). Mirrors the existing DRAG_LISTENER_KEY pattern in BrowserPane.tsx.

Test plan

  • Drag an editor tab over a browser-tab pane; the blue split overlay appears and dropping actually splits the pane.
  • Drag a terminal tab over a browser-tab pane; same behavior.
  • Dragging images/text out of a browser webview still works (HTML5 drag path unaffected).
  • Edit webview-drag-passthrough.ts with the dev server running; after HMR, tab-drag passthrough still works without a full reload.
  • Cancel a drag mid-flight (Esc or drop outside); webview regains pointer input.

Root cause: Electron `<webview>` guests run in a separate Chromium process
and capture pointerup off the host renderer's event loop. During a dnd-kit
tab drag, the drag-over overlay appeared (blue rectangle), but pointerup
landed in the guest instead of document, so onDragEnd never fired and the
drop silently did nothing.

Flip `pointer-events: none` on every registered webview for the duration
of an active tab drag so the host's PointerSensor sees the release.
HTML5-drag listeners in BrowserPane keep their original purpose (drag-out
of images/text); dnd-kit pointer drags now toggle the same passthrough
via a useEffect keyed on activeDrag, and cleanup restores input even if
the hook unmounts mid-drag.

- New `webview-drag-passthrough.ts` owns the webview set + toggle helper
  so useTabDragSplit can call it without importing BrowserPane.
- BrowserPane registers each created webview and unregisters on destroy.
Vite HMR reloads `webview-drag-passthrough.ts` in isolation, replacing the
module-level `registry` Set with a fresh empty one. But the live `<webview>`
elements are registered once from BrowserPane's "create new webview" branch
(not on every mount), so after HMR the reloaded module has no record of
them and tab-drag pointer passthrough silently stops working until a full
reload.

Stash the Set on `window` under a well-known key so the reloaded module
instance re-adopts existing registrations. Mirrors the DRAG_LISTENER_KEY
pattern already used in BrowserPane.tsx. Falls back to a module-local Set
in non-DOM contexts (tests).
The prior pointer-passthrough fix made drops onto a browser pane *work*
(pointerup reached the host and onDragEnd fired), but the blue
`.tab-drop-overlay` hitbox was still invisible during the drag. Electron
`<webview>` guests paint in their own GPU compositor layer above normal
DOM regardless of CSS z-index, so the overlay element (z-index 9999)
rendered at the correct coords but was occluded by the webview's
composite.

Flip `visibility: hidden` alongside `pointer-events: none` while a tab
drag is active, restored on drag end. Using `visibility` (not `display:
none`) preserves layout so the overlay's computed rect still matches
the pane body.

Verified in a running dev build: pre-fix the overlay DOM appeared at the
expected rect but no blue rendered; post-fix the hitbox is visible and
the drop still completes correctly.
@nwparker nwparker added the size/s Small PR (≤150 added lines, ≤10 files) label Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s Small PR (≤150 added lines, ≤10 files)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants