Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
# APP-3825: Vertical Tabs — Drag Panes Between Tabs
|
||||
|
||||
## Summary
|
||||
|
||||
Add the vertical-tabs equivalent of Warp's existing horizontal-tab pane-drag flow. When vertical tabs are enabled, a user should be able to drag a pane header over a different tab in the vertical tabs panel, have that tab become the active drag destination, and then drop the pane into the target tab using the same in-tab drop-target and relayout UX that already exists for horizontal tabs.
|
||||
|
||||
The feature should also preserve the existing "promote to a new tab" behavior: dragging between tabs should create a new tab at that position instead of targeting an existing tab.
|
||||
|
||||
## Problem
|
||||
|
||||
In horizontal tabs mode, users can move a pane from one tab to another by dragging the pane header to the top tab strip, hovering the destination tab so it becomes active, and then dropping the pane into a new location inside that tab. That workflow is currently missing in vertical tabs mode, even though the same pane-drag and in-tab relayout concepts already exist elsewhere in the product.
|
||||
|
||||
This creates an inconsistent drag-and-drop model between horizontal and vertical tabs. Users who opt into vertical tabs lose an existing pane-management workflow and must fall back to indirect alternatives like creating a new tab first or rearranging panes after the fact.
|
||||
|
||||
## Goals
|
||||
|
||||
- Restore parity with the existing horizontal-tab pane-drag workflow when vertical tabs are enabled.
|
||||
- Let users target an existing tab from the vertical tabs panel and then place the pane within that tab using the existing pane drop overlays and relayout rules.
|
||||
- Let users create a new tab by dropping between vertical tab groups, matching the current horizontal-tab "drop between tabs" behavior.
|
||||
- Make the targeting behavior work regardless of whether the vertical tabs panel is in compact or expanded mode.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Auto-opening the vertical tabs panel if it is currently closed.
|
||||
- Redesigning the in-tab pane drop overlays or changing how pane placement inside a tab works.
|
||||
- Changing any of the existing special-case pane-drop rules inside the target tab (for example, code-pane merge behavior when tabbed editor view is preferred).
|
||||
- Adding new keyboard interactions for this drag flow.
|
||||
- Extending this ticket to editor file-tab dragging; this spec is only for dragging pane headers.
|
||||
|
||||
## Figma / design references
|
||||
|
||||
Figma: none provided
|
||||
|
||||
Design intent should match the current horizontal-tab pane-drag behavior as closely as possible.
|
||||
|
||||
## User experience
|
||||
|
||||
### Availability
|
||||
|
||||
This behavior applies when:
|
||||
|
||||
- vertical tabs are enabled, and
|
||||
- the vertical tabs panel is visible, and
|
||||
- the user is dragging a pane by its pane header.
|
||||
|
||||
If the vertical tabs panel is closed, this ticket does not introduce a new auto-open or alternate targeting path.
|
||||
|
||||
### Valid tab targets in the vertical tabs panel
|
||||
|
||||
Each visible vertical tab group represents one workspace tab and must be targetable during a pane drag.
|
||||
|
||||
In practice:
|
||||
|
||||
- In **expanded** mode, hovering any visible part of a tab group counts as hovering that tab, including its pane rows and its optional custom-title header.
|
||||
- In **compact** mode, the same rule applies to the compact rendering of that tab group.
|
||||
- The user does not need a custom tab title/header in order to target a tab. Tabs that render only pane rows must still be targetable.
|
||||
|
||||
Hover-only panel chrome must not interfere with drag targeting:
|
||||
|
||||
- Drag-target feedback takes precedence over hover-only action affordances like the kebab/close button belt.
|
||||
- Those controls must not prevent the user from targeting the underlying tab group while a pane drag is in progress.
|
||||
|
||||
### Hovering an existing tab
|
||||
|
||||
When the user drags a pane header over a different tab group in the vertical tabs panel, Warp should treat that tab group the same way the horizontal tab strip treats a hovered destination tab today.
|
||||
|
||||
That means:
|
||||
|
||||
- The hovered tab group is shown as the active drag target.
|
||||
- The workspace switches to that tab as the drag destination.
|
||||
- Once that tab is active, the user can continue moving the cursor into the workspace content area and see the existing pane relayout drop targets for that tab.
|
||||
|
||||
This should feel like a direct vertical-tabs analogue of the current horizontal behavior, not like a separate drag mode.
|
||||
|
||||
### Dropping into the target tab
|
||||
|
||||
After the destination tab becomes active, dropping the pane inside the workspace should reuse the existing in-tab pane drop behavior unchanged.
|
||||
|
||||
Specifically:
|
||||
|
||||
- The same drop-target affordances that already appear when rearranging panes within a tab should appear for the dragged pane in the destination tab.
|
||||
- The same placement outcomes should apply as they do today in horizontal tabs mode.
|
||||
- Any existing special-case behavior in the destination tab remains unchanged. For example, if a destination tab's current rules would merge a dragged code pane into an existing code-pane/editor setup instead of allowing arbitrary free placement, vertical tabs should preserve that same result rather than inventing a new one.
|
||||
|
||||
### Dragging between tabs to create a new tab
|
||||
|
||||
The vertical tabs panel must also support the vertical analogue of "drop between tabs."
|
||||
|
||||
When the dragged pane is positioned between two visible tab groups, Warp should show an insertion indicator between those groups. Dropping there creates a new workspace tab containing the dragged pane at that position.
|
||||
|
||||
The same applies after the last visible tab group:
|
||||
|
||||
- Hovering below the final tab group shows an insertion indicator at the end of the list.
|
||||
- Dropping there creates a new tab at the end.
|
||||
|
||||
This should match the semantics of the current horizontal-tab strip:
|
||||
|
||||
- **Over a tab** targets that existing tab.
|
||||
- **Between tabs** creates a new tab at that position.
|
||||
|
||||
### Visual feedback
|
||||
|
||||
During a pane drag in vertical tabs mode, the panel should provide clear, mutually exclusive feedback:
|
||||
|
||||
- **Over an existing tab**: that tab group is highlighted as the current destination tab.
|
||||
- **Between tab groups**: show an insertion indicator between groups.
|
||||
- **No valid tab target**: clear any tab-target highlight or insertion indicator.
|
||||
|
||||
At no point should both an existing-tab highlight and a between-tabs insertion indicator be shown at the same time.
|
||||
|
||||
### Cancellation and reversibility
|
||||
|
||||
The cross-tab drag flow must remain reversible until the drop is committed.
|
||||
|
||||
- Moving off a target tab group removes that target state.
|
||||
- Moving from one tab group to another updates the target accordingly.
|
||||
- Aborting the drag or dropping outside any valid destination leaves the tab/pane layout unchanged.
|
||||
|
||||
### No new behavior outside the intended scope
|
||||
|
||||
This ticket does not change how users reorder vertical tabs by dragging the tab groups themselves. It only adds parity for dragging a pane header from one tab into another tab or into a new tab position.
|
||||
|
||||
## Success criteria
|
||||
|
||||
1. In vertical tabs mode with the panel open, dragging a pane header over a different visible tab group makes that tab group the active drag destination.
|
||||
2. When a non-active tab group becomes the drag destination, Warp switches the workspace to that tab so the user can place the pane inside it.
|
||||
3. After switching to the destination tab, the existing pane relayout/drop-target UX appears in the workspace and can be used to place the pane.
|
||||
4. The placement outcomes inside the destination tab match the existing horizontal-tabs flow; no new placement rules are introduced.
|
||||
5. Dragging between two visible tab groups shows an insertion indicator and dropping there creates a new tab containing the pane at that position.
|
||||
6. Dragging below the last visible tab group shows an end-of-list insertion indicator and dropping there creates a new tab at the end.
|
||||
7. The behavior works in both compact and expanded vertical tabs panel modes.
|
||||
8. Tabs without a custom title/header are still targetable via their rendered tab-group body.
|
||||
9. Hover-only controls in the vertical tabs panel do not block or replace drag-target feedback.
|
||||
10. Cancelling the drag, or dropping outside a valid destination, leaves the tab/pane layout unchanged and clears any temporary targeting UI.
|
||||
11. Existing vertical-tab reordering behavior is unchanged.
|
||||
12. Existing within-tab special cases, including code-pane merge behavior where applicable, remain unchanged.
|
||||
|
||||
## Validation
|
||||
|
||||
- **Existing-tab transfer**: In vertical tabs mode, create two tabs with multiple panes. Drag a pane header from tab A over tab B in the vertical tabs panel. Verify tab B becomes active and the pane can be dropped into a new split location using the normal in-tab drop overlays.
|
||||
- **Compact mode**: Repeat the same flow with the vertical tabs panel in compact mode.
|
||||
- **Expanded mode**: Repeat the same flow with the panel in expanded mode.
|
||||
- **No custom header**: Verify the drag works for a destination tab that has no custom tab title and therefore renders without a separate custom header row.
|
||||
- **New-tab insertion**: Drag a pane between two tab groups and verify an insertion indicator appears. Drop and confirm a new tab is created at that exact position containing the dragged pane.
|
||||
- **End insertion**: Drag below the last tab group and verify dropping creates a new final tab.
|
||||
- **Cancel path**: Start a cross-tab pane drag, hover a target so it highlights, then cancel or drop outside any valid target. Verify no pane move is committed and temporary highlighting clears.
|
||||
- **Special-case regression check**: Use a scenario where the existing horizontal-tabs flow has a special outcome inside the target tab (for example, a code-pane/editor merge case). Verify vertical tabs preserves the same behavior.
|
||||
- **Tab reordering regression**: Verify that dragging vertical tab groups themselves still reorders tabs exactly as before.
|
||||
|
||||
## Open questions
|
||||
|
||||
None.
|
||||
@@ -0,0 +1,276 @@
|
||||
# APP-3825: Tech Spec — Vertical Tabs Pane Drag Parity
|
||||
|
||||
## Problem
|
||||
|
||||
The pane-drag pipeline already supports moving a pane onto the horizontal tab strip and then reusing the existing in-tab relayout flow inside the destination tab. Vertical tabs do not participate in that pipeline today, even though the downstream workspace and pane-group logic is already generic once it receives a `TabBarHoverIndex`.
|
||||
|
||||
The missing piece is the vertical tabs panel itself:
|
||||
|
||||
- it renders tab groups as draggable items for tab reordering,
|
||||
- but it does not expose any drop targets for pane-header drags,
|
||||
- it does not render insertion indicators from `Workspace.hovered_tab_index`,
|
||||
- and its hover-only action button belt can overlap the tab-group surface during a drag.
|
||||
|
||||
As a result, pane-header drags over vertical tabs fall through to `PaneDragDropLocation::Other` instead of producing the existing `OverTab` / `BeforeTab` flow.
|
||||
|
||||
## Relevant code
|
||||
|
||||
- `app/src/workspace/view/vertical_tabs.rs (710-929)` — vertical tabs panel rendering; `render_vertical_tabs_panel`, `render_groups`, `render_tab_group`
|
||||
- `app/src/workspace/view/vertical_tabs.rs (968-1083)` — each vertical tab group is currently only a `Draggable` + `SavePosition` for reordering
|
||||
- `app/src/workspace/view.rs (15291-15328)` — horizontal tab bar wrapper adds a workspace-level `DropTarget` with `TabBarDropTargetData { AfterTabIndex(..) }`
|
||||
- `app/src/tab.rs (1366-1382)` — each horizontal tab is wrapped in `DropTarget::new(..., TabBarDropTargetData { TabIndex(..) })`
|
||||
- `app/src/pane_group/pane/view/header/mod.rs (321-352)` — pane-header drag hover classification currently derives `TabBarHoverIndex` from `TabBarLocation` + geometry
|
||||
- `app/src/pane_group/pane/view/header/mod.rs (939-1088)` — `render_pane_header_draggable`; pane drags only accept `PaneDropTargetData` and `TabBarDropTargetData`
|
||||
- `app/src/pane_group/mod.rs (544-561)` — workspace-facing events: `DroppedOnTabBar`, `SwitchTabFocusAndMovePane`, `UpdateHoveredTabIndex`, `ClearHoveredTabIndex`
|
||||
- `app/src/pane_group/mod.rs (1098-1153)` — `handle_pane_view_event`; `OverTab` hides/moves panes into the target tab, `BeforeTab` preserves “new tab” behavior
|
||||
- `app/src/workspace/view.rs (11630-11889)` — workspace handling for cross-tab drag/drop and existing special cases like code-pane merge behavior
|
||||
- `app/src/workspace/mod.rs (1447-1461)` — `TabBarDropTargetData` and `TabBarLocation`
|
||||
- `app/src/workspace/view.rs (14883-14891)` — existing horizontal insertion indicator renderer
|
||||
|
||||
## Current state
|
||||
|
||||
### Horizontal tabs already provide the full drag channel
|
||||
|
||||
The horizontal tab strip exposes `TabBarDropTargetData` in two places:
|
||||
|
||||
- each concrete tab via `TabBarLocation::TabIndex(i)`, and
|
||||
- the strip container via `TabBarLocation::AfterTabIndex(tab_count)`.
|
||||
|
||||
Pane-header drags and editor-tab drags both recognize that data. In the pane-header path, `PaneHeader::calculate_tab_focus_hover_index` interprets the dragged rect relative to the target tab’s bounds and converts it into:
|
||||
|
||||
- `TabBarHoverIndex::OverTab(i)` when the user is targeting an existing tab
|
||||
- `TabBarHoverIndex::BeforeTab(i)` when the user is inserting between tabs
|
||||
|
||||
From there, the rest of the flow is already shared:
|
||||
|
||||
- `PaneGroup::handle_pane_view_event` emits `SwitchTabFocusAndMovePane` for `OverTab` and hides the dragged pane for `BeforeTab`
|
||||
- `Workspace` stores `hovered_tab_index`, switches active tabs when needed, and on drop either
|
||||
- adds a new tab from the moved pane for `BeforeTab`, or
|
||||
- reuses the target tab’s existing placement rules for `OverTab`
|
||||
|
||||
### Vertical tabs only support tab reordering today
|
||||
|
||||
`render_tab_group` in `vertical_tabs.rs` currently builds each tab group as:
|
||||
|
||||
- a hoverable group surface,
|
||||
- an optional overlay belt with kebab/close actions,
|
||||
- a `Draggable` used for vertical tab reordering,
|
||||
- and a `SavePosition(tab_position_id(tab_index))` used by the reorder math in `Workspace::calculate_updated_tab_index_vertical`.
|
||||
|
||||
There is no `DropTarget` around the group and no dedicated insertion targets between groups or after the final group.
|
||||
|
||||
Because of that, dragging a pane header over the vertical tabs panel never produces a workspace-tab target. The drag is not accepted by the panel, so the pane-header code falls back to `PaneDragDropLocation::Other`.
|
||||
|
||||
### `hovered_tab_index` is only rendered in the horizontal tab strip
|
||||
|
||||
`Workspace.hovered_tab_index` already drives two pieces of feedback in the horizontal strip:
|
||||
|
||||
- highlighted tab styling for `OverTab`
|
||||
- insertion bars for `BeforeTab`
|
||||
|
||||
The vertical tabs panel does not currently read or render that state at all.
|
||||
|
||||
### Overlay controls can interfere with tab-group hit-testing
|
||||
|
||||
The vertical tabs group action belt is shown from hover state and rendered as a positioned overlay on top of the group. During a pane drag, that overlay can visually and spatially compete with the tab-group surface unless we explicitly suppress it or ensure the drop target sits above it in hit-testing.
|
||||
|
||||
## Proposed changes
|
||||
|
||||
### 1. Add explicit vertical-tabs pane drop targets
|
||||
|
||||
Introduce a new workspace-level drop target data type for pane-header drags in vertical tabs. Keep it separate from `TabBarDropTargetData` so this ticket stays scoped to pane headers and does not implicitly expand to editor file-tab dragging.
|
||||
|
||||
Proposed shape in `app/src/workspace/mod.rs`:
|
||||
|
||||
```rust
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub struct VerticalTabsPaneDropTargetData {
|
||||
pub tab_bar_location: TabBarLocation,
|
||||
pub tab_hover_index: TabBarHoverIndex,
|
||||
}
|
||||
```
|
||||
|
||||
This reuses the existing `TabBarLocation` / `TabBarHoverIndex` semantics instead of inventing a parallel enum.
|
||||
|
||||
### 2. Teach pane-header draggables to accept the new vertical target data
|
||||
|
||||
Update `render_pane_header_draggable` in `app/src/pane_group/pane/view/header/mod.rs` to accept three drop-target kinds:
|
||||
|
||||
- `PaneDropTargetData`
|
||||
- `TabBarDropTargetData` (existing horizontal strip path)
|
||||
- `VerticalTabsPaneDropTargetData` (new vertical tabs path)
|
||||
|
||||
To keep the existing horizontal logic intact, extend the pane-header drag action so it can optionally carry a precomputed `TabBarHoverIndex`:
|
||||
|
||||
```rust
|
||||
PaneHeaderDragged {
|
||||
origin: ActionOrigin,
|
||||
drag_location: PaneDragDropLocation,
|
||||
drag_position: RectF,
|
||||
explicit_tab_hover_index: Option<TabBarHoverIndex>,
|
||||
}
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
- horizontal tab-strip drags keep sending `None` and continue to use `calculate_tab_focus_hover_index`
|
||||
- vertical tabs send `Some(...)` and bypass geometry inference
|
||||
|
||||
That lets vertical tabs use explicit “over tab” vs “between tabs” zones instead of trying to reinterpret a large tab-group card with the horizontal strip’s x-axis heuristics.
|
||||
|
||||
### 3. Wrap vertical tab groups in `OverTab` drop targets
|
||||
|
||||
In `render_groups` / `render_tab_group`:
|
||||
|
||||
- keep the current `Draggable + SavePosition` structure for tab reordering
|
||||
- wrap each rendered tab-group element in `DropTarget::new(..., VerticalTabsPaneDropTargetData { ... OverTab(tab_index) ... })`
|
||||
|
||||
The `SavePosition(tab_position_id(tab_index))` should remain attached to the group element used for tab reordering, not to any insertion spacer. That preserves the current reorder math in `calculate_updated_tab_index_vertical`.
|
||||
|
||||
This gives the entire visible tab-group surface an explicit “existing tab” meaning, which matches the product spec for both compact and expanded modes.
|
||||
|
||||
### 4. Add explicit insertion targets between groups and after the final group
|
||||
|
||||
Render small, dedicated insertion targets in the vertical list rather than deriving `BeforeTab` from pointer position inside a tab-group card.
|
||||
|
||||
Concretely, `render_groups` should render:
|
||||
|
||||
- a leading insertion target for `BeforeTab(0)` for full parity with the horizontal strip
|
||||
- one insertion target before each subsequent tab group
|
||||
- one trailing insertion target after the final group for `BeforeTab(tab_count)`
|
||||
|
||||
Each insertion target is a thin block wrapped in `DropTarget::new(..., VerticalTabsPaneDropTargetData { tab_hover_index: BeforeTab(i), ... })`.
|
||||
|
||||
This has two advantages:
|
||||
|
||||
- it matches the product behavior more closely than splitting a tab-group card into vertical thirds
|
||||
- it avoids rewriting the shared pane-group/workspace drag logic, since that logic already understands `BeforeTab`
|
||||
|
||||
The trailing target should have enough height to remain practically hittable even when the list fills the panel.
|
||||
|
||||
### 5. Render vertical drag feedback from `Workspace.hovered_tab_index`
|
||||
|
||||
Add vertical-tabs equivalents of the horizontal tab-strip feedback in `vertical_tabs.rs`:
|
||||
|
||||
- when `hovered_tab_index == OverTab(i)`, the corresponding tab group renders as the active drag target
|
||||
- when `hovered_tab_index == BeforeTab(i)`, render an insertion indicator before that group
|
||||
- when `hovered_tab_index == BeforeTab(tab_count)`, render the insertion indicator at the end of the list
|
||||
|
||||
Implementation-wise:
|
||||
|
||||
- thread `workspace.hovered_tab_index` into `render_groups` / `render_tab_group`
|
||||
- add a helper like `render_vertical_tab_hover_indicator`
|
||||
- include drag-target highlighting in the group background logic, separate from normal hover and active-tab styling
|
||||
|
||||
The current horizontal `render_tab_hover_indicator` is a narrow vertical bar. Vertical tabs should use a horizontal divider-style accent that visually reads as “insert here” in the list layout.
|
||||
|
||||
### 6. Suppress the vertical tab-group action belt during pane drags
|
||||
|
||||
While any pane is being dragged, do not render the floating kebab/close action belt in `render_tab_group`.
|
||||
|
||||
This is the simplest way to avoid overlay interference with drag targeting and keeps the panel visually focused on drop feedback instead of hover controls.
|
||||
|
||||
Use the existing `PaneGroup::any_pane_being_dragged(app)` signal from the active pane group, similar to how the workspace already uses it for tab-bar visibility and keymap context.
|
||||
|
||||
### 7. Leave pane-group and workspace drop behavior unchanged
|
||||
|
||||
Do not redesign the downstream move/drop handlers. The current event chain is already the right abstraction boundary:
|
||||
|
||||
- `PaneGroup::handle_pane_view_event`
|
||||
- `Workspace`’s `SwitchTabFocusAndMovePane`
|
||||
- `Workspace`’s `DroppedOnTabBar`
|
||||
|
||||
That code already preserves:
|
||||
|
||||
- hidden-pane staging while hovering `OverTab`
|
||||
- “promote to new tab” behavior for `BeforeTab`
|
||||
- existing target-tab placement rules
|
||||
- special cases like code-pane merge behavior under tabbed editor view
|
||||
|
||||
This ticket should only change how vertical tabs produce `TabBarHoverIndex`, not what happens after that.
|
||||
|
||||
## End-to-end flow
|
||||
|
||||
1. The user starts dragging a pane header.
|
||||
2. `render_pane_header_draggable` enters drag mode and accepts either pane drop targets, horizontal tab-strip targets, or the new vertical-tabs pane targets.
|
||||
3. The user moves over the vertical tabs panel:
|
||||
- over a tab group → the drop target provides `OverTab(i)`
|
||||
- over an insertion zone → the drop target provides `BeforeTab(i)`
|
||||
4. The pane-header view emits `DraggedOverTabBar` with that `TabBarHoverIndex`.
|
||||
5. `PaneGroup::handle_pane_view_event` reuses the existing behavior:
|
||||
- `OverTab(i)` → stage the pane in the destination tab via `SwitchTabFocusAndMovePane`
|
||||
- `BeforeTab(i)` → hide the pane for move and preserve “create a new tab on drop”
|
||||
6. `Workspace` updates `hovered_tab_index`, which now drives vertical-panel drag feedback.
|
||||
7. If the target is `OverTab(i)` and a tab switch is needed, `Workspace` activates the target tab and adds the pane as hidden in that tab’s pane group.
|
||||
8. The user moves into the workspace content area; the existing pane relayout drop targets appear and the pane can be placed using the current within-tab logic.
|
||||
9. On drop:
|
||||
- `BeforeTab(i)` → existing `DroppedOnTabBar` logic creates a new tab at `i`
|
||||
- `OverTab(i)` → existing target-tab placement logic runs unchanged
|
||||
10. Cancelling or leaving valid targets clears `hovered_tab_index` and reverts hidden-pane staging, as it does today.
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
### Risk: accidental scope expansion to editor file-tab dragging
|
||||
|
||||
If we reused `TabBarDropTargetData` directly in vertical tabs, editor file tabs would likely start targeting vertical tabs too, because `code/view.rs` already recognizes that type.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- use a separate `VerticalTabsPaneDropTargetData`
|
||||
- only add it to the pane-header draggable acceptance path
|
||||
|
||||
### Risk: overlay hit-testing blocks the drop target
|
||||
|
||||
The action button belt is rendered as an overlay on the group and can interfere with drag targeting.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- suppress the overlay while any pane drag is active
|
||||
|
||||
### Risk: breaking vertical tab reordering
|
||||
|
||||
Adding wrappers around the group could change the bounds used by `calculate_updated_tab_index_vertical`.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- keep `SavePosition(tab_position_id(..))` attached to the group element itself
|
||||
- do not include insertion-target spacers in that saved position
|
||||
|
||||
### Risk: feedback mismatch between target state and actual drop behavior
|
||||
|
||||
If the vertical panel renders `OverTab` / `BeforeTab` differently from what the workspace eventually does, the interaction will feel inconsistent.
|
||||
|
||||
Mitigation:
|
||||
|
||||
- reuse `TabBarHoverIndex` end to end
|
||||
- keep workspace and pane-group drop handlers unchanged
|
||||
|
||||
## Testing and validation
|
||||
|
||||
### Manual validation
|
||||
|
||||
Use the scenarios in `specs/APP-3825/PRODUCT.md`:
|
||||
|
||||
- drag a pane from one tab to another in expanded mode
|
||||
- repeat in compact mode
|
||||
- verify tabs without custom headers are targetable
|
||||
- verify insertion between groups and after the last group creates a new tab
|
||||
- verify cancel / drop-outside clears temporary state
|
||||
- verify code-pane merge behavior still matches the horizontal flow
|
||||
- verify vertical tab reordering still works
|
||||
|
||||
### Automated validation
|
||||
|
||||
This feature is mostly WarpUI drag/drop hit-testing, so manual validation is the primary check. Add lightweight automated coverage where it is cheap and reliable:
|
||||
|
||||
- if a pure helper is introduced for vertical drag-target rendering decisions, cover `OverTab` / `BeforeTab` cases in `app/src/workspace/view/vertical_tabs_tests.rs`
|
||||
- add or update workspace tests only if the new helper can be exercised without full UI drag simulation
|
||||
|
||||
No new persistence, networking, or model migration coverage is needed.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- If we later want vertical tabs to support editor file-tab dragging as well, we can either:
|
||||
- teach `code/view.rs` to accept `VerticalTabsPaneDropTargetData`, or
|
||||
- unify the horizontal and vertical tab-target metadata behind a shared explicit-hover-index type
|
||||
- If the vertical insertion indicator and the horizontal tab-strip indicator should share styling, we can extract a small shared helper after this behavior is stable
|
||||
Reference in New Issue
Block a user