Description
Adds "Merge into tab N: " entries to the tab context menu. Choosing one moves every visible terminal pane of the source tab into the chosen tab as right-hand splits, keeps the running sessions alive, and closes the emptied source tab. It works in both vertical and horizontal tab layouts.
Today the only way to combine two existing tabs is to create a throwaway split in one, drag its pane header onto the other tab, and clean up the leftover pane. A single-pane tab has no pane header, so there is nothing to drag, and a tab-row drag only reorders tabs.
Behavior:
- A tab is offered as a merge source only when all of its panes are visible terminal panes. Eligibility is checked for the whole tab before any pane moves, so a tab is never half-merged.
- Tabs that track child-agent panes are excluded.
- Merging a tab into itself is a no-op.
- The feature is gated behind the default-off
MergeTabIntoTabflag (merge_tab_into_tabcargo feature), so release behavior is unchanged.
Not in this PR: dragging a tab onto a pane, cross-window merges, preserving the source tab's split layout, and an always-visible pane header setting.
Linked Issue
Refs #8959
- The linked issue is labeled
ready-to-specorready-to-implement. - Screenshots or a short video of the implementation are included below.
#8959 currently carries needs-info. ready-to-implement was removed on 2026-07-07 while maintainers asked for more user-story detail, and it hasn't been restored. This PR is a draft so maintainers can accept the direction, ask for a different one, or close it. I'm happy to turn it into a spec PR if that's preferred. Context and open questions for the maintainers are in this comment on #8959.
Testing
Automated:
- Unit tests in
app/src/pane_group/mod_tests.rsandapp/src/workspace/view_tests.rscover merge ordering, session identity surviving the move, the source tab closing, rejection of ineligible tabs with nothing moved, merging into itself, tab-group pruning, no undo entry, and the flag being inert when off. Three of them open the real context menu (horizontal tabs, the vertical-tabs kebab menu, and the vertical pane-row menu) and select a merge entry. - Integration tests in
crates/integration(test_merge_tab_into_tab_from_vertical_tab_context_menu,test_merge_tab_into_tab_from_horizontal_tab_context_menu,test_merge_tab_into_tab_keeps_sessions_alive) run in a real window.
cargo test -p warp --lib -- <merge filters> 15 passed; 0 failed
cargo test -p integration --test integration 3 passed; 0 failed
cargo clippy -p warp_features -p warp -p integration --all-targets --tests -- -D warnings clean
./script/format --check cleanManual: built with ./script/run --features merge_tab_into_tab, opened two tabs each running a live loop, and merged one into the other from the context menu. Both loops kept running with unbroken timestamps. Vertical tabs were exercised by hand. Horizontal tabs are covered by the integration test only.
- I have manually tested my changes locally with
./script/run
Media
Before: the tab menu has no merge entry
Right-clicking a tab offers no way to combine it with another open tab.
Before: dragging a tab into another tab does nothing
Dragging a tab row into another tab's terminal area leaves the two tabs separate, with no split.
After: the tab menu offers "Merge into tab"
With the flag on, the tab context menu lists one "Merge into tab N: " row per other tab. <img width="1000" alt="Tab context menu with a Merge into tab entry" src="https://cdn.trylle.com/media/images/e0d7d6c6641032a4d1203ef4efb9ed53?key=AWmKqEC0hA3alDxA1iI6mn8dcPNRlaIYbK%2BImdpQLXyC1uptT4DwepvRd2JdW%2FCTgZ1zOyXnK86jCUT5OF50GA3TnrBHv%2FKBgmjJ0MaK5FH%2F5QAuPCKJw1Sm%2BowQMb5wSvsWTaZ81TlUqhjX3ocfAPJO6mGWabvxxziah7KirxLZkBEcuxKTJSO7LCbOsw82hYDVj%2F11urRBLbBNjC7%2BkufYVJ2dLQ%3D%3D"/>
After: the merged tab keeps both sessions running
Both terminals are now splits of one tab, and their timestamps continue without a restart.
After: merging two live tabs from the menu
The full flow: right-click a tab, choose the merge entry, and both running sessions end up side by side.
Known limitations
- The destination list is flat rows in the context menu, one per other tab, so a window with many tabs gets a long menu. A nested submenu was tried first. Clicking a submenu parent row closes its sidecar and the sidecar doesn't reopen while the pointer stays on the row. That is existing behavior of "Move to group" too, and is filed as #16609.
- Moving the last pane out of a tab goes through the shared
remove_pane_for_movepath, which logs tworeport_error!events ("Unable to locate a panel to activate after close" and "Pane not found"). The existing child-agent re-adopt path logs the same events. Filed as #16628. - On the macOS OSS build I used,
DragTabsToWindowsis off, so a tab-row drag into a terminal area is a no-op there. I haven't compared behavior on a build with that feature enabled.
CHANGELOG-NONE
Activity
5@cla-bot check
The cla-bot has been summoned, and re-checked this pull request!
This pull request is a draft
Merging is unavailable until it is marked ready for review.
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @nickvirden on file. In order for us to review and merge your code, each contributor must visit https://cla.warp.dev to read and agree to our CLA. Once you have done so, please comment
@cla-bot checkto trigger another check.