feat(floating-terminal): consolidate tab model — drag-to-reorder + drag-to-split#6469
Draft
wolfiesch wants to merge 5 commits into
Draft
feat(floating-terminal): consolidate tab model — drag-to-reorder + drag-to-split#6469wolfiesch wants to merge 5 commits into
wolfiesch wants to merge 5 commits into
Conversation
The floating terminal reused the shared TabBar/SortableTab but rendered the strip bare, without the DndContext/sensors the main workspace gets from TabGroupSplitLayout. dnd-kit's useSortable never activated, so tabs could not be dragged to reorder. Wrap the floating tab strip in TabDragProvider -> DndContext -> DragOverlay driven by the existing useTabDragSplit hook (keyed to the floating worktree). Reordering flows through the shared reorderUnifiedTabs/tabOrder model with no new state. Broaden the titlebar no-drag selector to [data-tab-id] so dragging browser/markdown tabs reorders instead of starting a window move. Also guard moveTabToNewPaneColumn for the floating worktree: the floating panel renders a single group with no split layout, so a "Move Tab to Split" would orphan the tab into a group that never renders (pre-existing latent bug for terminal/browser/markdown tabs).
- Normalize the titlebar no-drag target so SVG icons and text nodes
nested inside a [data-tab-id] tab root yield the pointer to dnd-kit
instead of starting a floating-window move. Add SVG- and text-node
regression tests asserting setPointerCapture is not called.
- Enforce the floating-worktree no-split invariant at the store
boundary: dropUnifiedTab now rejects a split drop for the floating
worktree before creating any group/layout, complementing the existing
tab-move-to-pane-column UI guard. Add a direct store test.
- Strengthen FloatingTerminalPanel drag wiring tests via an inspectable
hoisted useTabDragSplit mock: assert the hook is called with
{ worktreeId, enabled }, DndContext receives the exact sensors/handler
references, the drag root uses setDragRootNode as ref, TabBar receives
the exact hoveredTabInsertion, plus a closed-panel enabled:false case.
- Reword comments that claimed the disabled state registers no sensors;
the hook uses an impossible activation distance to prevent drag
activation while hidden.
Scale the shared tab drag-split system out to the floating terminal so a dragged tab can be dropped onto a pane edge to split the floating workspace, with the same live hover preview used in the main workspace. - Render TabGroupSplitLayout in FloatingTerminalPanel and remove the earlier no-split invariant from both the tab-move-to-pane-column UI guard and the dropUnifiedTab store boundary; the floating worktree now seeds layoutByWorktree and routes its tab strip, panes, and drag wiring through the shared split layout. - Rewrite the floating panel tests to assert delegation to TabGroupSplitLayout instead of reaching into TabBar internals, and move PTY-exit / hidden-visibility coverage into the new TerminalPaneOverlayLayer.test.tsx and TabGroupSplitLayout.test.ts (dnd-wiring enabled/disabled, exact sensor/handler passthrough). - Drop the stale !src/preload/api-types.d.ts gitignore allow-list entry left over from stablyai#1197 (api-types.d.ts -> type-checked api-types.ts), so the accidental tsc emit stays ignored.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the floating terminal's tab handling to parity with the main workspace by consolidating it onto the shared unified-tab model:
The floating panel now renders the shared
TabGroupSplitLayout(tab strip, panes, reorder/drag wiring) instead of owning a bespoke single-group surface. The earlier no-split invariant was removed from both thetab-move-to-pane-columnUI guard and thedropUnifiedTabstore boundary, and the floating worktree seedslayoutByWorktree.Screenshots
Testing
pnpm lintpnpm typecheckpnpm test(affected: 7 files, 173 tests green)pnpm buildTabGroupSplitLayoutrather than TabBar internals; PTY-exit / hidden-visibility coverage moved toTerminalPaneOverlayLayer.test.tsx; dnd-wiring (enabled/disabled, exact sensor/handler passthrough) covered inTabGroupSplitLayout.test.tsTargeted gates run locally:
typecheck:webclean,oxlintclean on all changed files, full affected suite 173/173.AI Review Report
Reviewed the drag-split consolidation for: (1) correctness of removing the no-split invariant at both the UI guard and store boundary — confirmed the floating worktree now seeds
layoutByWorktreeso the split layout has a tree to paint and tabs aren't orphaned; (2) test quality — confirmed panel tests assert delegation (component boundary) rather than reaching into TabBar internals, reducing brittleness; (3) the untrackedsrc/preload/api-types.d.tswas identified as an accidental tsc emit of the type-checkedapi-types.ts(source-of-truth since #1197) and is not committed — the stale!src/preload/api-types.d.tsallow-list entry was removed from.gitignoreto prevent recurrence.Cross-platform: changes are renderer-only (React/zustand/dnd-kit) with no shell, path, or Electron-main behavior touched; no OS-specific code paths introduced. Drag activation uses pointer sensors already in use across platforms.
Security Audit
No new input handling, command execution, path handling, auth, secrets, or IPC surfaces. Changes are confined to renderer tab-model state and drag wiring. The
.gitignorechange keeps a generated.d.tsemit out of source control (no secret exposure). No dependency changes.Notes
.gitignore: dropped the stale!src/preload/api-types.d.tswhitelist (leftover from fix(preload): collapse index.d.ts into type-checked api-types.ts #1197's.d.ts->.tsmigration) so the accidental tsc emit stays ignored.