Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user