Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+365
View File
@@ -0,0 +1,365 @@
# APP-3875: Vertical Tabs v2 — Summary Tab Item Mode
## Summary
Add a new `Summary` tab item mode for vertical tabs when `View as = Tabs`.
This is a follow-up to APP-3828. `Focused session` remains the default tab item for `View as = Tabs`, while `Summary` provides a tab-level overview of the work inside the tab. In Summary mode, each tab renders as a fixed expanded-style summary card with:
- a primary line derived from conversation / command labels across the tab
- a second line derived from working directories across the tab
- branch lines derived from the tab's unique branch contexts, with diff stats and PR chips shown on the branch line rather than on pane rows
## Problem
The current `View as = Tabs` mode is still fundamentally pane-scoped. It reduces each tab to a single representative row, but that row is just the focused pane rendered with the existing pane-row UI.
That works when the user only cares about the current focused pane, but it breaks down when a tab contains multiple active panes or multiple related workflows. In that case, the representative row hides the rest of the tab's work:
- the row title only describes the focused pane
- the working directory only describes the focused pane
- diff stats and PR chips only describe the focused pane's branch context
- the tab gives no concise overview of the other work happening inside it
Users need a second Tabs-mode representation that answers "what is inside this tab?" rather than only "which pane in this tab is focused right now?"
## Goals
- Add a new `Tab item` section directly under `View as` in the vertical tabs settings popup.
- Make `Tab item` available only when `View as = Tabs`.
- Keep `Focused session` as the default tab item mode for `View as = Tabs`.
- Add `Summary` as a second tab item mode.
- Make `Summary` render a tab-level summary card rather than a focused-pane row.
- Make `Summary` hide `Density` and the other focused-pane row controls that do not apply to the summary card.
- Define the summary card in terms of:
- work labels on the first line
- working directories on the second line
- branch lines below, with diff stats and PR chips keyed to the branch context
- Keep the summary card stable and predictable rather than heuristic-heavy.
- Preserve the user's previous focused-session display settings when they temporarily switch into and out of `Summary`.
## Non-goals
- Adding `Summary` to `View as = Panes`.
- Adding a compact-density summary layout.
- Making branch lines or chips independently clickable.
- Replacing branch lines with pane-preview lines.
- Introducing fuzzy matching, semantic merging, or aggressive rewriting of work labels.
- Redesigning tab headers, drag-and-drop, rename behavior, or the existing tab-level hover sidecar.
- Reworking the existing `Focused session` behavior beyond adding the new `Tab item` control next to it.
## Figma / design references
- Popup exploration: https://www.figma.com/design/CsBdBW4YoLgSAbr5eSkwV6/House-of-Agents?node-id=7628-124093&t=LSuxL7FNk3EXOfvJ-0
- Summary card exploration: https://www.figma.com/design/CsBdBW4YoLgSAbr5eSkwV6/House-of-Agents?node-id=7633-127452&t=LSuxL7FNk3EXOfvJ-0
### Intentional deviations from the exploratory mocks
- `Summary` is a Tabs-only `Tab item` mode, not a density option.
- When `Summary` is selected, `Density` is hidden rather than adapted.
- The lower lines in Summary mode are branch lines, not pane-preview lines.
- Branch rows are grouped by branch context rather than rendered once per pane.
## User experience
### Popup structure
The vertical tabs settings popup keeps `View as` as its top section:
- `Panes`
- `Tabs`
When `View as = Panes`:
- the `Tab item` section is hidden
- the existing pane-oriented controls continue to behave as they do today
When `View as = Tabs`:
- a `Tab item` section appears directly under `View as`
- the `Tab item` section has exactly two options:
- `Focused session`
- `Summary`
`Focused session` is the default tab item mode for `View as = Tabs`.
### Popup state transitions
- Clicking `Focused session` or `Summary` updates the panel immediately.
- The popup remains open after changing `Tab item`.
- Switching from `Focused session` to `Summary` hides:
- `Density`
- `Pane title as`
- `Additional metadata`
- `Show`
- Switching back from `Summary` to `Focused session` restores those sections exactly as they were before; Summary does not overwrite the stored values for those controls.
- Switching from `Tabs` back to `Panes` hides `Tab item` entirely.
- If the user later returns to `View as = Tabs`, Warp restores the last selected `Tab item` mode.
### Focused session behavior
When `View as = Tabs` and `Tab item = Focused session`, behavior remains the same as APP-3828:
- each tab renders one representative row
- the representative row is derived from the focused pane in that tab
- `Density`, `Pane title as`, `Additional metadata`, and `Show` continue to affect that row exactly as they do today
This mode remains the default Tabs behavior.
### Summary behavior
When `View as = Tabs` and `Tab item = Summary`, each tab renders a fixed expanded-style summary card instead of a focused-pane row.
The summary card represents the tab as a whole, not any specific pane.
Summary mode does not expose density variants. It always uses the Summary card layout described below.
### Summary card structure
Each summary card contains up to four regions, in this order:
1. primary line
2. working-directory line
3. up to three visible branch lines
4. optional overflow line (`+ N more`)
The card may omit regions that have no data. For example:
- a tab with no branch context has no branch lines
- a tab with no working-directory data has no working-directory line
The absence of a later region does not insert placeholder text or empty rows.
### Summary card icon
Summary mode renders the pane circle icon as a tab-level pane-kind summary instead of using only the focused pane's icon.
- If all visible panes in the tab are the same pane kind, render one icon for that kind.
- Terminal panes are distinguished by their semantic terminal icon treatment, so plain terminals, Oz/ambient-agent terminals, and CLI-agent terminals do not all collapse to the generic terminal icon.
- If the tab has two or more pane kinds, render two icons using the same stacked arrangement as the current agent/status icon treatment:
- the oldest pane kind is the main icon
- the second-oldest distinct pane kind is the smaller secondary icon in the bottom-right
- Choose pane kinds by sorting the tab's visible panes by pane creation order, then taking the first two distinct pane kinds.
- If the oldest pane is closed, recompute from the remaining visible panes.
- Ignore any additional pane kinds beyond the first two distinct kinds for the icon treatment.
### Primary line
The primary line summarizes the work happening in the tab.
It is derived from work labels gathered across the tab's visible panes in tab order.
#### Work-label sources
For terminal-like panes, the work label should prefer conversation / command-oriented text, using the same general precedence as the current focused-session labeling:
- CLI agent display title, if present
- conversation display title, if present
- terminal title, if it is meaningful
- last completed command, if present
- otherwise the terminal fallback label such as `New session`
For non-terminal panes, the work label falls back to the pane title or pane-type label when no conversation / command label exists.
#### Normalization rules
Work-label normalization is intentionally conservative:
- trim leading and trailing whitespace
- collapse repeated internal whitespace
- drop empty labels
- dedupe exact-equivalent normalized labels while preserving the first-seen display text
Summary mode must not:
- semantically rewrite labels
- fuzzy-match different labels
- merge distinct labels like `cargo test` and `cargo + code review`
- treat different agent names as interchangeable
#### Rendering rules
- Render the first four unique work labels in first-seen order.
- Join them with ` • `.
- If more than four unique labels remain, append ` + N more`.
- The line is single-line and truncates visually when it does not fit.
Examples:
- `Claude • Oz • cargo • code review`
- `Claude • cargo • code review + 2 more`
### Working-directory line
The second line summarizes where the work in the tab is happening.
- Gather unique working directories exposed by panes in first-seen order.
- Render them as a list separated by ` • `.
- The line is single-line and truncates visually when it does not fit.
- If no pane exposes working-directory data, omit the line.
Warp should not try to pick one "most representative" directory in Summary mode. The card should show the stable list of unique working directories instead.
Examples:
- `~/warp-internal`
- `~/warp-internal • ~/warp-server • ~/warp-terraform`
### Branch lines
The lower lines in Summary mode are branch lines, not pane-preview lines.
Each branch line represents a unique branch context present in the tab and is the place where diff stats and PR chips appear in Summary mode.
#### Branch grouping
Branch lines are coalesced by unique repository + branch context.
That means panes from different repositories do not collapse into one branch line merely because they share the same branch name, such as `main`.
#### Branch-line ordering
- Branch lines are ordered by first appearance in the tab's visible pane order.
- Render at most three branch lines.
- If more than three unique branch contexts exist, render an overflow line below them:
- `+ N more`
#### Branch-line contents
Each visible branch line shows:
- the branch label on the left
- diff stats on the right, if available for that branch context
- a PR chip on the right, if available for that branch context
A branch line may show:
- just the branch label
- the branch label plus diff stats
- the branch label plus PR chip
- the branch label plus both diff stats and PR chip
If a pane contributes no branch context, it does not create a branch line.
#### Coalesced metadata behavior
Within one coalesced repository + branch group:
- diff stats are shown once for the group
- the PR chip is shown once for the group
If multiple panes in the same coalesced group expose the same logical branch metadata, Warp should simply coalesce them into that one rendered line rather than treating them as separate rows.
### Summary-card interactions
In v1, the summary card remains a single tab-level target.
- clicking the card activates the tab
- clicking the card focuses the tab's active pane
- branch lines are informational only
- diff stats and PR chips inside Summary mode are informational only
Summary mode does not introduce child-level click targets for branches or chips.
### Selection, hover, and tab-level behavior
Summary mode keeps the existing tab-level interaction model:
- the active tab retains active/selected styling
- hover styling remains tab-level
- tab header behavior remains unchanged
- rename behavior remains unchanged
- drag-and-drop behavior remains unchanged
- close behavior remains unchanged
- the existing tab-level hover sidecar behavior remains unchanged
### Single-pane tabs
A single-pane tab can still render a summary card.
In that case, the summary card may contain:
- one primary work label
- one working directory
- one branch line
Summary mode should still look intentional for single-pane tabs, even though it provides less aggregation benefit there than for multi-pane tabs.
### Mixed tabs and missing data
Summary mode must handle mixed-content tabs gracefully.
Examples:
- A tab may include panes that contribute work labels but no branch context.
- A tab may include panes that contribute branch context but no working-directory data.
- A tab may include only one visible branch line and no working-directory line.
Warp must not insert placeholder copy such as `No branch` or `No directory`.
### Search behavior
When `View as = Tabs` and `Tab item = Summary`, search/filtering operates on the tab summary data rather than on a focused-pane row.
Search matching includes the full underlying summary content for the tab, including:
- all normalized work labels, including labels not visible because of `+ N more`
- all working-directory values gathered for the tab
- all coalesced branch labels, including branch groups not visible because of the branch overflow line
- PR labels / identifiers present in the summary card
- diff-stat text shown for branch lines
This means a tab can match the search query even if the matching branch or work label is currently hidden behind an overflow line.
## Success criteria
1. The vertical tabs popup shows a `Tab item` section directly under `View as` when `View as = Tabs`.
2. The `Tab item` section is hidden when `View as = Panes`.
3. `Tab item` has exactly two options: `Focused session` and `Summary`.
4. `Focused session` remains the default Tabs-mode representation.
5. Selecting `Summary` updates the panel immediately and leaves the popup open.
6. When `Summary` is selected, `Density`, `Pane title as`, `Additional metadata`, and `Show` are hidden.
7. Returning from `Summary` to `Focused session` restores the previously selected values for those hidden controls.
8. In Summary mode, each tab renders one summary card rather than one focused-pane row.
9. The summary card's first line is derived from work labels across the tab, not just the focused pane.
10. Work-label deduplication is conservative and exact-equivalent only after whitespace normalization.
11. The primary line uses ` • ` between visible work labels.
12. The working-directory line shows the unique working directories in stable first-seen order rather than choosing one heuristic "best" directory.
13. The working-directory line uses ` • ` between visible directories.
14. The Summary pane circle renders one pane-kind icon for homogeneous tabs.
15. The Summary pane circle renders two pane-kind icons for heterogeneous tabs, selected from the two oldest distinct pane kinds by pane creation order, with the secondary icon in the same bottom-right placement as the existing agent/status composite icon.
16. The lower rows in Summary mode are branch lines, not pane previews.
17. Branch lines are coalesced by repository + branch context, so two repositories on `main` do not collapse into one row.
18. Each branch line can show branch label, diff stats, and PR chip in one row.
19. Summary mode shows at most three visible branch lines, followed by `+ N more` when additional branch contexts exist.
20. Branch lines and chips in Summary mode are informational only and do not create new click targets.
21. Tabs with no working-directory data or no branch data still render sensible summary cards without placeholder text.
22. Search in Summary mode matches the full summary dataset for the tab, including values hidden behind overflow lines.
23. Switching away from Summary does not erase the user's focused-session density or pane-row display preferences.
24. Existing tab header, selection, hover, close, rename, drag, and sidecar behavior remain unchanged.
## Validation
- Open the vertical tabs popup, switch to `View as = Tabs`, and verify a `Tab item` section appears directly beneath `View as`.
- Verify `Focused session` is selected by default in Tabs mode.
- Switch between `Focused session` and `Summary` and verify the panel updates immediately without closing the popup.
- While `Summary` is selected, verify `Density`, `Pane title as`, `Additional metadata`, and `Show` are hidden.
- Switch back to `Focused session` and verify those controls return with their previous values intact.
- Create a tab with multiple terminal/agent panes and verify the Summary primary line contains multiple work labels rather than just the active pane's label.
- Verify exact-duplicate work labels are deduped, while distinct labels remain distinct.
- Create a tab spanning multiple working directories and verify the second line shows the unique directories in first-seen order.
- Verify the primary and working-directory lines use ` • ` separators between visible values.
- Create a homogeneous tab and verify Summary renders one pane-kind icon.
- Create a heterogeneous tab and verify Summary renders two pane-kind icons selected from the two oldest distinct pane kinds, with the oldest as the main icon and the second-oldest distinct kind as the bottom-right secondary icon.
- Create a tab with multiple panes on the same repository + branch and verify they produce one branch line rather than multiple pane-preview rows.
- Create a tab with two different repositories both on `main` and verify they render as separate branch lines.
- Verify diff stats and PR chips appear on the branch line rather than being keyed to a focused pane row.
- Create more than three unique branch contexts in one tab and verify only three branch lines are shown, followed by `+ N more`.
- Verify tabs with no branch data omit the branch section entirely.
- Verify tabs with no working-directory data omit the working-directory line entirely.
- Click a Summary card and verify Warp activates the tab and focuses its active pane.
- Verify clicking on branch lines or chips does not trigger separate actions.
- Search for a work label, directory, hidden-overflow branch, PR number, and diff-stat text, and verify the correct tab still matches in Summary mode.
- Verify tab headers, drag-and-drop, rename, close, and hover-sidecar behavior are unchanged while Summary mode is selected.
## Open questions
None.
+475
View File
@@ -0,0 +1,475 @@
# APP-3875: Tech Spec — Summary Tab Item Mode for Vertical Tabs
## Problem
APP-3875 adds a second tab-level representation for vertical tabs when `View as = Tabs`.
Today, Tabs mode is implemented by reusing the existing pane-row UI for the tab's focused pane. That keeps the implementation simple, but it makes the tab item fundamentally pane-scoped:
- render logic is driven by a single representative `PaneId`
- search logic is driven by the same representative pane's search fragments
- diff stats and PR chips are rendered as pane-level terminal metadata
The new `Summary` mode needs a different unit of representation:
- the card must summarize the whole tab, not one pane
- the lower rows are branch lines, not pane previews
- search needs to match hidden summary content, not just visible rows
- the popup must hide the focused-session controls when Summary is selected, without losing the underlying settings
Technically, this means we need to introduce a Tabs-only item mode and a tab-level aggregation model while preserving the existing focused-session behavior and reusing as much of the current selection / hover / rename / drag infrastructure as possible.
## Relevant code
- `specs/APP-3875/PRODUCT.md` — agreed user-facing behavior for Summary mode
- `specs/APP-3828/PRODUCT.md` — current `View as = Tabs` focused-session behavior this feature extends
- `app/src/workspace/tab_settings.rs (171-279)` — current synced vertical-tabs settings (`VerticalTabsViewMode`, `VerticalTabsDisplayGranularity`, `VerticalTabsPrimaryInfo`, `VerticalTabsCompactSubtitle`)
- `app/src/workspace/action.rs (234-241)` — existing vertical-tabs setting actions
- `app/src/workspace/action.rs (624-848)` — `should_save_app_state_on_action` coverage for vertical-tabs popup actions
- `app/src/workspace/action_tests.rs (1-50)` — current tests for vertical-tabs action persistence behavior
- `app/src/workspace/view.rs (18224-18337)` — workspace-side action handling for the vertical-tabs popup settings
- `app/src/workspace/view/vertical_tabs.rs (231-327)` — shared row wrapper behavior (`render_pane_row_element`) for click, hover, selection styling, and inline rename
- `app/src/workspace/view/vertical_tabs.rs (428-669)` — `VerticalTabsPanelState`, popup-local mouse state, and `matching_tab_indices`
- `app/src/workspace/view/vertical_tabs.rs (1048-1314)` — main vertical-tabs render path (`render_groups`, `render_tab_group`), including the current flat Tabs-mode list
- `app/src/workspace/view/vertical_tabs.rs (1982-2180)` — `PaneProps`, search-fragment generation, and terminal primary-line helpers
- `app/src/workspace/view/vertical_tabs.rs (2155-2781)` — terminal metadata helpers and current clickable diff/PR badge rendering
- `app/src/workspace/view/vertical_tabs.rs (2398-2492)` — `render_title_override` and inline rename editor behavior
- `app/src/workspace/view/vertical_tabs.rs (2890-3319)` — `render_settings_popup`
- `app/src/workspace/view/vertical_tabs.rs (4087-4369)` — compact row rendering and the current vertical-tabs unit-test module hookup
- `app/src/workspace/view/vertical_tabs_tests.rs (1-259)` — existing pure helper tests for vertical-tabs behavior
- `app/src/terminal/view/tab_metadata.rs (24-119)` — terminal metadata accessors used by the current focused-session rows (`display_working_directory`, `current_git_branch`, `current_pull_request_url`, `current_diff_line_changes`)
- `app/src/terminal/view.rs:2753` — `TerminalView::current_repo_path`, needed to distinguish same-named branches across repositories
## Current state
### Settings and popup model
The vertical-tabs popup currently has two orthogonal axes:
- `View as` → `Panes` vs `Tabs`
- `Density` → `Compact` vs `Expanded`
It also exposes focused-pane controls:
- `Pane title as`
- `Additional metadata` (compact only)
- `Show` (expanded only)
There is no Tabs-only concept of "how should a tab item be represented?" The popup assumes that every rendered item is still a pane row.
### Tabs mode is already a flat list
Current `View as = Tabs` behavior is implemented as a flat list, not a grouped header + body UI:
- `uses_outer_group_container` returns `false` for `VerticalTabsDisplayGranularity::Tabs`
- `render_groups` adds spacing between flat tab items in Tabs mode
- `render_tab_group` skips the outer group header/container path when `uses_outer_group_container` is false
This matches the mock and is the right foundation for Summary mode. We do not need to invent a new container hierarchy for this feature.
### Rendering and search are pane-derived
The current Tabs-mode implementation still relies on a representative pane:
- `pane_ids_for_display_granularity(...)` returns one representative `PaneId` in Tabs mode
- `matching_tab_indices(...)` searches by building `PaneProps` for that representative pane
- the search branch in `render_groups(...)` also matches representative-pane `PaneProps`
- `render_tab_group(...)` iterates the selected pane IDs and renders either `render_compact_pane_row(...)` or `render_pane_row(...)`
This means render and search both inherit the limitations of pane-scoped data.
### Pane metadata is available in the right places, but not yet aggregated
The required summary inputs already exist in the codebase, mostly on `TerminalView`:
- work-label inputs:
- conversation display title
- CLI agent display title
- terminal title
- last completed command
- directory input:
- `display_working_directory(...)`
- branch metadata:
- `current_git_branch(...)`
- `current_diff_line_changes(...)`
- `current_pull_request_url(...)`
- `current_repo_path(...)`
However, these values are only consumed today for a single pane at a time. There is no tab-level aggregation model, no stable dedupe / coalescing logic, and no summary-specific search fragments.
### Diff and PR badges are interactive today
The existing helpers:
- `render_terminal_diff_stats_badge(...)`
- `render_terminal_pull_request_badge(...)`
wrap the visual chip content in `Hoverable` and dispatch actions on click. That matches focused-session rows, but it does not match Summary mode, where branch lines and chips are informational only.
## Proposed changes
### 1. Add a synced Tabs-only item-mode setting
Add a new enum in `app/src/workspace/tab_settings.rs`:
```rust
#[derive(Default, Debug, serde::Serialize, serde::Deserialize, PartialEq, Copy, Clone)]
pub enum VerticalTabsTabItemMode {
#[default]
FocusedSession,
Summary,
}
```
Register it in `TabSettings` with the same sync behavior as the existing vertical-tabs popup settings:
- `SupportedPlatforms::ALL`
- `SyncToCloud::Globally(RespectUserSyncSetting::Yes)`
- `hierarchy: "appearance.tabs"`
This setting is only used when `vertical_tabs_display_granularity == Tabs`, but it should still be persisted independently so Warp can restore the user's last chosen Tabs-mode representation.
### 2. Add a workspace action for the new setting
Add `WorkspaceAction::SetVerticalTabsTabItemMode(VerticalTabsTabItemMode)` beside the existing vertical-tabs popup actions.
Handle it in `Workspace::handle_action` exactly like the other vertical-tabs setting writes:
- write to `settings.vertical_tabs_tab_item_mode`
- call `ctx.notify()`
Update `should_save_app_state_on_action` and `action_tests.rs` so the new action is explicitly marked as not requiring workspace-state persistence.
### 3. Extend popup-local state and restructure `render_settings_popup`
Add two mouse states to `VerticalTabsPanelState`:
- `focused_session_option_mouse_state`
- `summary_option_mouse_state`
Keep the existing density and focused-session option mouse states unchanged so Summary mode can temporarily hide, rather than replace, those controls.
Update `render_settings_popup(...)` so the popup hierarchy becomes:
1. `View as`
2. `Tab item` (only when `View as = Tabs`)
3. `Density` and the focused-session controls (only when `View as = Tabs && Tab item = FocusedSession`, or when `View as = Panes`)
Concretely:
- if `View as = Panes`, do not render `Tab item`
- if `View as = Tabs`, render a `Tab item` section directly under `View as`
- if `Tab item = Summary`, do not render:
- `Density`
- `Pane title as`
- `Additional metadata`
- `Show`
This preserves the stored values of those controls while making the Summary-mode popup match the product behavior.
### 4. Introduce a tab-level Summary aggregation model
Add a new pure aggregation layer in `vertical_tabs.rs` for Summary mode rather than pushing summary derivation directly into the renderer.
Suggested data shape:
```rust
struct VerticalTabsSummaryData {
primary_labels: Vec<String>,
working_directories: Vec<String>,
branch_entries: Vec<VerticalTabsSummaryBranchEntry>,
}
struct VerticalTabsSummaryBranchEntry {
repo_path: PathBuf,
branch_name: String,
diff_stats: Option<GitLineChanges>,
pull_request_label: Option<String>,
}
```
The exact field names can vary, but the design should keep two properties:
1. the full aggregated data is available for search, even when some items are visually hidden behind overflow
2. ordering is explicit and stable; do not rely on `HashMap` iteration order for rendering
Implementation guidance:
- preserve first-seen order with `Vec` plus a set / index map keyed by normalized value
- use one aggregation pass over the tab's `visible_pane_ids()`
- build Summary data from the full tab, not just the representative pane
Summary mode also needs a small tab-level icon selection helper:
- map each visible pane to a stable pane-kind enum used only for Summary icon rendering
- distinguish terminal-backed agent sessions from plain terminals so Oz/ambient-agent and CLI-agent terminal panes render their semantic agent icons rather than the generic terminal icon
- sort candidates by pane creation order, using the pane view `EntityId` as the current stable creation-order key
- render `Single(kind)` if all visible panes share the same kind
- render `Pair { primary, secondary }` for heterogeneous tabs, where `primary` is the oldest pane kind and `secondary` is the second-oldest distinct pane kind
- position the secondary icon using the same bottom-right anchor, offset, and cutout-ring sizing as the existing agent/status composite icon
- recompute from the current visible panes each render so closing the oldest pane naturally changes the selected icons
### 5. Reuse existing pane-derived helpers instead of re-implementing label logic
Summary mode should reuse the same data precedence rules the focused-session code already uses wherever possible.
For work labels:
- reuse `terminal_primary_line_data(...)` for terminal panes
- reuse `PaneProps::new(...)` / `displayed_title()` for non-terminal panes where practical
For working-directory contributions:
- terminal panes use `TerminalView::display_working_directory(...)`
- code panes can contribute the already-derived parent-directory subtitle that `PaneProps::new(...)` computes
- other panes should only contribute if they already expose a meaningful directory-like value; otherwise they contribute nothing
This avoids creating a second set of heuristics that drift from the current row behavior.
### 6. Coalesce branch lines by repository + branch
Summary branch lines must be keyed by repository + branch, not branch name alone.
Implementation plan:
- only panes that can expose both `current_repo_path()` and `current_git_branch()` contribute a branch line
- use `(repo_path, branch_name)` as the grouping key
- preserve first-seen order by recording the first time each key appears while scanning visible panes
- store diff stats and PR label once per coalesced entry
This keeps same-named branches in different repositories distinct and matches the product spec's branch-line behavior.
For v1, if multiple panes in the same coalesced group expose the same logical metadata, simply keep the first observed non-empty values for:
- diff stats
- PR label
That is sufficient because the product semantics treat these as logically branch-scoped. We do not need a more complex merge strategy.
### 7. Add summary-specific normalization and search helpers
Add pure helpers for:
- work-label normalization
- work-label deduplication with first-seen display preservation
- summary search-fragment generation
Search fragments for Summary mode should include the full, untruncated data set:
- all work labels
- all working directories
- all branch names
- all PR labels
- diff-stat text for all coalesced branch entries
This is intentionally different from the render path, which only shows a prefix and then `+ N more`.
### 8. Keep row-level interaction, selection, hover, and rename by reusing the existing wrapper
Do not build Summary mode as an entirely separate interaction surface.
Instead, keep a representative `PaneProps` for the tab's focused pane and use it to drive the existing shared row wrapper:
- `render_pane_row_element(...)` for click, hover, selection background, and detail-sidecar targeting
- `render_title_override(...)` for custom tab title and inline rename editor behavior
The Summary renderer should therefore have the shape:
- build `SummaryData` from the full tab
- build representative `PaneProps` from the focused pane
- render Summary content inside the existing wrapper using the representative `PaneProps`
This gives Summary mode the current Tabs-mode behavior "for free":
- click focuses the tab's active pane
- hover uses the existing tab-level styling
- double-click rename continues to work in the same place
- detail-sidecar hover targeting remains tab-scoped via `VerticalTabsDetailTarget::Tab`
### 9. Add a dedicated Summary renderer
Add a new renderer in `vertical_tabs.rs`, for example:
```rust
fn render_summary_tab_item(
representative_props: PaneProps<'_>,
summary: &VerticalTabsSummaryData,
app: &AppContext,
) -> Box<dyn Element>
```
This renderer should:
- render a fixed expanded-style card
- render a Summary-specific pane-kind icon rather than the focused pane's icon
- render the primary line, working-directory line, visible branch lines, and optional overflow line
- cap visible work labels and visible branch entries independently
- omit empty sections instead of rendering placeholders
Because Summary mode is informational-only at the branch level, the branch lines should not introduce nested click handlers or badge hover handlers.
### 10. Split badge visuals from badge interactivity
Refactor the current terminal diff / PR badge helpers so the visual badge content can be reused without the focused-session click behavior.
Suggested direction:
- keep existing interactive helpers for focused-session rows
- extract passive visual helpers for:
- diff-stats badge content
- PR badge content
- Summary mode uses the passive versions in branch lines
This avoids duplicating the chip visuals while keeping Summary mode intentionally non-interactive.
### 11. Branch render and search logic based on mode, not only granularity
Current render and search logic branch only on `VerticalTabsDisplayGranularity`.
Update the Tabs-mode path so it branches on both:
- display granularity
- tab item mode
Suggested structure:
- `Panes` → existing pane-based render and search
- `Tabs + FocusedSession` → existing representative-pane render and search
- `Tabs + Summary` → summary render and summary search
This can be implemented with a small resolved-mode helper rather than scattering nested conditionals throughout the file.
### 12. Add pure tests for the new aggregation behavior
Extend `vertical_tabs_tests.rs` with pure helper tests covering:
- work-label normalization
- exact-equivalent dedupe while preserving first-seen display text
- working-directory dedupe and stable ordering
- branch coalescing by repository + branch
- same branch name in different repos stays distinct
- overflow-count behavior for primary labels and branch lines
- summary search fragments include hidden-overflow values
Keep these helpers pure so they do not require a full `AppContext` or UI harness.
### 13. Keep focused-session behavior untouched
Do not refactor the existing focused-session row renderers beyond what is needed to:
- add the new setting/action
- hide irrelevant popup sections in Summary mode
- share badge visuals where necessary
Focused-session Tabs mode is already shipped behavior from APP-3828. The safest implementation is to keep that path intact and add Summary as a parallel path.
## End-to-end flow
```mermaid
flowchart TD
A[User opens vertical-tabs popup] --> B[Selects View as = Tabs]
B --> C[Selects Tab item = Summary]
C --> D[WorkspaceAction::SetVerticalTabsTabItemMode]
D --> E[TabSettings.vertical_tabs_tab_item_mode updated]
E --> F[render_settings_popup hides Density and pane-row controls]
E --> G[render_groups resolves Tabs + Summary mode]
G --> H[Aggregate full-tab SummaryData from visible panes]
H --> I[Build representative PaneProps from focused pane]
I --> J[render_summary_tab_item inside existing row wrapper]
H --> K[summary_search_fragments]
K --> L[matching_tab_indices and render_groups search path]
```
At runtime, Summary mode therefore follows the same top-level control flow as the current popup settings, but diverges at render/search time into a tab-level aggregation path.
## Risks and mitigations
### Risk: render and search drift apart
If Summary render data and Summary search fragments are built in separate ad hoc code paths, the panel will render one thing and search another.
Mitigation:
- use one `VerticalTabsSummaryData` aggregation path
- derive both render output and search fragments from that shared data
### Risk: same-named branches collapse incorrectly
If branch grouping keys only use the branch name, two different repos on `main` will merge incorrectly.
Mitigation:
- group by `(repo_path, branch_name)`
- add a unit test for two repos on `main`
### Risk: custom tab rename behavior regresses in Summary mode
Current Tabs-mode rows support inline rename because the row wrapper and `render_title_override(...)` are wired to tab-level rename state.
Mitigation:
- keep representative `PaneProps`
- route Summary primary-line rendering through the same title-override / rename-editor path
### Risk: Summary data is unavailable for some non-terminal panes
Branch metadata is readily available on `TerminalView`, but not every pane type exposes repo / branch context today.
Mitigation:
- let non-terminal panes contribute work labels and directories where available
- omit branch contributions from panes that do not expose branch context
- render sparse summary cards without placeholder text
### Risk: passive Summary chips drift visually from focused-session chips
If Summary mode re-implements diff/PR chips from scratch, the appearance can drift from the current vertical-tabs styling.
Mitigation:
- extract shared visual badge helpers
- keep only the event-handling wrapper different between focused-session and Summary mode
## Testing and validation
### Unit tests
- `app/src/workspace/action_tests.rs`
- add coverage for `SetVerticalTabsTabItemMode(...)`
- `app/src/workspace/view/vertical_tabs_tests.rs`
- label normalization and dedupe
- directory dedupe and stable ordering
- branch coalescing by repo + branch
- same branch name in different repos
- overflow counting
- summary search fragments include hidden entries
- Summary pane-kind icon selection for homogeneous tabs, heterogeneous tabs, and oldest-pane removal
### Manual / UI validation
- verify `Tab item` appears only when `View as = Tabs`
- verify `Summary` hides `Density`, `Pane title as`, `Additional metadata`, and `Show`
- verify switching back to `Focused session` restores the prior focused-session settings
- verify a multi-pane tab renders a summary card instead of a focused-pane row
- verify homogeneous Summary tabs render one pane-kind icon
- verify heterogeneous Summary tabs render the two oldest distinct pane-kind icons with the secondary icon in the bottom-right
- verify branch lines are grouped by repo + branch and carry diff stats / PR chips
- verify branch lines and chips are not independently clickable
- verify clicking the card still focuses the active pane
- verify search matches hidden overflow data
- verify double-click rename still works in Summary mode
### Broader validation
When implementation lands, run the same validation expected for other vertical-tabs work:
- targeted unit tests
- relevant workspace / vertical-tabs test suites
- visual verification of the new Summary mode in a real app session
## Follow-ups
- Add interactive branch-line affordances (for example, opening code review from a branch line) if product later wants child-level actions
- Consider a compact Summary representation if product later decides Summary should coexist with `Density`
- Expand branch-context contribution beyond terminal panes if more pane types gain stable repo / branch metadata
- Consolidate focused-session and Summary badge rendering around a single shared visual badge API once both paths exist