Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
# APP-3656: Vertical Tabs Panel — Compact Mode + View Toggle
|
||||
|
||||
## Summary
|
||||
|
||||
Add a compact display mode for the vertical tabs panel and a settings icon button that opens a popup to switch between compact and expanded views. In compact mode, each pane is rendered as a single-line row (icon + title) instead of the current multi-line card layout.
|
||||
|
||||
## Problem
|
||||
|
||||
The current expanded pane rows show rich detail (working directory, branch, agent status, diff stats, PR badge) and occupy significant vertical space. When a user has many tabs and panes, the panel requires heavy scrolling. Users need a denser view that lets them quickly scan and switch between panes without scrolling past multiple lines of metadata per item.
|
||||
|
||||
## Goals
|
||||
|
||||
- Let users switch the vertical tabs panel between a dense compact view and the current detailed expanded view.
|
||||
- Persist the user's preference across sessions.
|
||||
- Add a control-bar icon button that opens a settings popup for toggling the view mode.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- **Group-by**: The settings popup in the Figma mocks also contains "Group panes by" options (Tab, Directory/Environment, Branch, Status). These are out of scope for this ticket.
|
||||
- **Compact group headers**: Group headers remain unchanged in both modes. Iterating on header layout is a separate concern.
|
||||
- **Search functionality**: The search input in the control bar remains inert (per APP-3648).
|
||||
|
||||
## Figma / design references
|
||||
|
||||
- Compact view: https://www.figma.com/design/CsBdBW4YoLgSAbr5eSkwV6/House-of-Agents?node-id=7079-25549&m=dev
|
||||
- Compact view (settings button active): https://www.figma.com/design/CsBdBW4YoLgSAbr5eSkwV6/House-of-Agents?node-id=7079-24535&m=dev
|
||||
- Expanded view + settings popup: https://www.figma.com/design/CsBdBW4YoLgSAbr5eSkwV6/House-of-Agents?node-id=7103-34398&m=dev
|
||||
|
||||
**Intentional deviation from mocks**: In compact mode, non-agent terminal panes display the **terminal title** (e.g. process name, user-set title) instead of the working directory shown in the mock.
|
||||
|
||||
## User experience
|
||||
|
||||
### View modes
|
||||
|
||||
The vertical tabs panel supports two view modes:
|
||||
|
||||
- **Expanded** (default, current behavior): Multi-line pane rows with full metadata (working directory, git branch, conversation status, agent badge, diff stats, PR badge). This is the existing layout, unchanged.
|
||||
- **Compact**: Single-line pane rows with just an icon and a title string. Described in detail below.
|
||||
|
||||
### Setting
|
||||
|
||||
A new user setting (`VerticalTabsViewMode`) controls which mode is active. It is a synced setting (cloud-persisted) with two variants: `Compact` and `Expanded`. Default is `Expanded`.
|
||||
|
||||
### Settings icon button
|
||||
|
||||
A new icon button is added to the existing control bar in the vertical tabs panel header, to the left of the existing Configs button and Plus button.
|
||||
|
||||
- **Icon**: `settings-04` (a sliders/filter icon, per the Figma mock).
|
||||
- **Size**: 16×16 icon in a 20×20 hit target with 2px padding, matching other icon buttons in the control bar.
|
||||
- **Behavior**: Clicking the button toggles the settings popup open/closed.
|
||||
- **Active state**: When the popup is open, the button has a highlighted background (`fg_overlay_3`), matching the active state shown in mock 2.
|
||||
- **Tooltip**: On hover (when popup is closed), show "View options".
|
||||
|
||||
### Settings popup
|
||||
|
||||
The popup appears anchored below the settings icon button.
|
||||
|
||||
- **Contents**: A segmented control with exactly two segments:
|
||||
- **Left segment**: Hamburger/list icon (`menu-01`), representing compact mode.
|
||||
- **Right segment**: Grid icon (`grid-01`), representing expanded mode.
|
||||
- The segment corresponding to the current view mode is visually selected (highlighted background).
|
||||
- Clicking a segment immediately switches the view mode setting and the panel re-renders.
|
||||
- **Dismiss**: The popup closes when the user clicks outside it, presses Escape, or clicks the settings icon button again.
|
||||
- **Positioning**: Anchored to the bottom edge of the settings button, left-aligned with it. If the popup would clip outside the window, reposition within window bounds.
|
||||
- **Style**: Rounded container with border (`neutral_4`), subtle background, and drop shadow, matching the existing option menu pattern.
|
||||
|
||||
### Compact pane rows
|
||||
|
||||
In compact mode, each pane row within a tab group is a single-line element:
|
||||
|
||||
```
|
||||
[icon 16×16] [4px gap] [title text, 12px, single line, ellipsis truncation]
|
||||
```
|
||||
|
||||
With horizontal padding (12px left and right) and vertical padding (8px top and bottom) per row. Rows have 4px corner radius, consistent with the expanded layout.
|
||||
|
||||
#### Per-pane-type content
|
||||
|
||||
**Terminal pane (non-agent)**:
|
||||
- Icon: Terminal icon (same as expanded tertiary line).
|
||||
- Title: The terminal title from the shell (e.g. the running process name or user-set title). This intentionally differs from the mock, which shows the working directory.
|
||||
|
||||
**Terminal pane (agent session)**:
|
||||
- Icon: Conversation status icon (the colored status badge — running, stopped, completed, etc.) at 16×16.
|
||||
- Title: The conversation display title (e.g. "Refactor the button component to use..."). Truncated with ellipsis if it overflows.
|
||||
|
||||
**Terminal pane (ambient agent)**:
|
||||
- Icon: `OzCloud` icon.
|
||||
- Title: The conversation display title if available, otherwise the terminal title.
|
||||
|
||||
**Code pane**:
|
||||
- Icon: Code file icon (language-specific if available, falling back to generic code icon).
|
||||
- Title: The file name/path as shown in the pane title.
|
||||
|
||||
**Other panes** (Notebook, Workflow, Settings, Rules, Plan, MCP Server, etc.):
|
||||
- Icon: The type-specific icon (same icon used in the expanded view's kind badge).
|
||||
- Title: The pane configuration title.
|
||||
|
||||
#### Split panes (multiple visible panes in one tab group)
|
||||
|
||||
Each visible pane in a tab group gets its own single-line compact row, same as in expanded mode. There is no combined-row or "(+N)" treatment — the group collapse/expand chevron and the pane count label in the group header already communicate how many panes exist.
|
||||
|
||||
#### Row interactions
|
||||
|
||||
All interactions are identical to the expanded view:
|
||||
- **Click**: Focus the pane (`WorkspaceAction::FocusPane`).
|
||||
- **Right-click**: Open the tab right-click context menu.
|
||||
- **Hover**: Highlight with `fg_overlay_1` background (or `fg_overlay_2` if selected). Cursor changes to pointing hand.
|
||||
- **Selected state**: The focused pane in the active tab has a `fg_overlay_2` background and a 1px `fg_overlay_2` border.
|
||||
- **Drag**: Pane rows are not individually draggable (only entire tab groups are draggable, unchanged).
|
||||
|
||||
#### Row indicators
|
||||
|
||||
- **Unsaved changes indicator**: For code panes with unsaved changes, show a small filled circle icon (`circle-filled`, 16×16) to the right of the title text, same as the expanded view's badge. This is right-aligned in the row.
|
||||
|
||||
#### Tab color support
|
||||
|
||||
Per-pane and per-group tab colors work the same as in expanded mode. In compact mode, the color applies as a background tint on the pane row (at `TAB_COLOR_OPACITY` / `TAB_COLOR_HOVER_OPACITY`).
|
||||
|
||||
### Group headers
|
||||
|
||||
Group headers are unchanged between compact and expanded modes. They continue to show:
|
||||
- Left: Group title (uppercase, 10px, sub-text color)
|
||||
- Right: Pane count label + collapse/expand chevron
|
||||
|
||||
The collapse/expand behavior for groups works the same in both modes. Collapsing a group hides all its pane rows (whether compact or expanded).
|
||||
|
||||
### Transitions
|
||||
|
||||
Switching between compact and expanded mode re-renders all pane rows immediately. No animation is required. The scroll position should be preserved as closely as possible (the scroll state handle is shared).
|
||||
|
||||
## Success criteria
|
||||
|
||||
1. A `VerticalTabsViewMode` setting with `Compact` and `Expanded` variants is persisted as a synced cloud setting.
|
||||
2. The settings icon button appears in the control bar between the search input and the Configs button.
|
||||
3. Clicking the settings icon button opens a popup with a two-segment control (compact/expanded). Clicking a segment switches the mode immediately.
|
||||
4. The popup closes on outside click, Escape, or re-clicking the settings button.
|
||||
5. In compact mode, every pane type renders as a single-line row with the correct icon and title per the rules above.
|
||||
6. Non-agent terminal panes in compact mode show the terminal title (not the working directory).
|
||||
7. Agent terminal panes in compact mode show the conversation status icon and conversation title.
|
||||
8. Code panes show the unsaved-changes circle indicator when applicable.
|
||||
9. Tab-color tinting works correctly on compact rows.
|
||||
10. Group headers, collapse/expand, drag-to-reorder tabs, and right-click context menus all work unchanged in compact mode.
|
||||
11. Switching modes preserves the scroll position and does not reset collapsed/expanded group states.
|
||||
12. The setting defaults to `Expanded`, matching current behavior — no user-visible change until the user explicitly switches.
|
||||
|
||||
## Validation
|
||||
|
||||
- **Visual inspection**: Toggle between compact and expanded modes. Verify that compact rows are single-line, icons are correct per pane type, and text truncates with ellipsis.
|
||||
- **Terminal title vs pwd**: Open a non-agent terminal, set a custom title or run a process, switch to compact mode, and verify the terminal title (not the pwd) is shown.
|
||||
- **Agent panes**: Start an agent conversation, switch to compact mode, verify the status icon and conversation title are shown.
|
||||
- **Unsaved code indicator**: Open a code file, make an unsaved edit, switch to compact mode, and verify the filled circle indicator appears.
|
||||
- **Tab colors**: Assign a tab color, switch to compact mode, verify the color tint is visible on the compact row.
|
||||
- **Settings popup**: Click the settings icon, verify the popup appears anchored below it with the correct segment selected. Click the other segment, verify the mode switches. Click outside, verify the popup closes.
|
||||
- **Persistence**: Switch to compact mode, quit and relaunch, verify the panel opens in compact mode.
|
||||
- **Narrow panel**: Resize the panel to minimum width (200px) in compact mode. Verify rows truncate gracefully and the control bar remains usable.
|
||||
- **Group collapse**: Collapse a group in compact mode, switch to expanded, verify it remains collapsed.
|
||||
|
||||
## Open questions
|
||||
|
||||
None — all resolved:
|
||||
|
||||
1. ~~Should the compact view hide the group pane count label?~~ No. Keep it unchanged.
|
||||
2. ~~Keyboard shortcut to toggle compact/expanded?~~ Out of scope for now; to be added later.
|
||||
3. ~~Where does the compact/expanded toggle live when group-by is added?~~ It stays inside the same popup, per the Figma mock.
|
||||
@@ -0,0 +1,293 @@
|
||||
# APP-3656: Tech Spec — Compact Mode + View Toggle
|
||||
|
||||
## Problem
|
||||
|
||||
The vertical tabs panel currently renders every pane as a multi-line card (2–4 lines each). With many tabs/panes the panel requires heavy scrolling. The product spec (APP-3656 PRODUCT.md) defines a compact single-line rendering mode and a control-bar settings popup to toggle between compact and expanded views. This spec translates that behavior into concrete implementation changes.
|
||||
|
||||
## Relevant code
|
||||
|
||||
- `app/src/workspace/view/vertical_tabs.rs` — all rendering for the vertical tabs panel; `VerticalTabsPanelState`, `render_control_bar`, `render_pane_row`, `render_terminal_row_content`, `TypedPane`, `PaneProps`
|
||||
- `app/src/workspace/tab_settings.rs` — `TabSettings` group and `define_settings_group!` / `implement_setting_for_enum!` macros for persisted settings
|
||||
- `app/src/workspace/action.rs` — `WorkspaceAction` enum for dispatching UI events
|
||||
- `app/src/workspace/view.rs` — `Workspace` struct fields, `show_new_session_dropdown_menu` pattern for popup overlays
|
||||
- `ui/src/ui_components/segmented_control.rs` — `SegmentedControl<T>` view, `RenderableOptionConfig`, `SegmentedControlEvent`
|
||||
- `warp_core/src/ui/icons.rs` — `Icon` enum and SVG path mappings
|
||||
- `app/src/ai/conversation_status_ui.rs` — `render_status_element` for agent status badges
|
||||
- `app/src/terminal/view/tab_metadata.rs` — `terminal_title_from_shell()`, `display_working_directory()`, `selected_conversation_display_title()`
|
||||
- `app/src/terminal/view/pane_impl.rs (926-973)` — `is_ambient_agent_session()`, `selected_conversation_status()`, `selected_conversation_display_title()`
|
||||
|
||||
## Current state
|
||||
|
||||
The panel layout is:
|
||||
|
||||
```
|
||||
Resizable (drag-right edge)
|
||||
└─ Container (background + right border)
|
||||
└─ Flex::column
|
||||
├─ render_control_bar (search input + new-tab split button)
|
||||
└─ Shrinkable(ClippedScrollable::vertical(tab groups))
|
||||
```
|
||||
|
||||
`render_pane_row` dispatches to either `render_terminal_row_content` (for terminal panes, producing 3 lines: primary dir+branch, secondary agent/title, tertiary kind+badges) or an inline multi-line layout for non-terminal panes (title, subtitle, kind badge row). There is no concept of a view mode — every row is always expanded.
|
||||
|
||||
The `SegmentedControl<T>` view is fully reusable. It takes a vec of options, a render config callback, and emits `SegmentedControlEvent::OptionSelected(T)` on segment click. Icon-only segments are supported via `RenderableOptionConfig` with `label: None` and a populated `icon_path`.
|
||||
|
||||
## Proposed changes
|
||||
|
||||
### 1. Add `VerticalTabsViewMode` setting
|
||||
|
||||
In `tab_settings.rs`, add an enum and wire it into `TabSettings`:
|
||||
|
||||
```rust
|
||||
#[derive(Default, Debug, serde::Serialize, serde::Deserialize, PartialEq, Copy, Clone)]
|
||||
pub enum VerticalTabsViewMode {
|
||||
Compact,
|
||||
#[default]
|
||||
Expanded,
|
||||
}
|
||||
```
|
||||
|
||||
Register with `implement_setting_for_enum!` using `SyncToCloud::Globally(RespectUserSyncSetting::Yes)` and hierarchy `"appearance.tabs"`. Add `vertical_tabs_view_mode: VerticalTabsViewMode` to the `define_settings_group!` block.
|
||||
|
||||
### 2. Add icon variants
|
||||
|
||||
The segmented control needs two icons not currently in the `Icon` enum:
|
||||
|
||||
- `Menu` already exists (`layout-left.svg`) but maps to the sidebar icon, not a hamburger/list icon. Verify `bundled/svg/` for a `menu-01.svg` or similar. If absent, add `ListMenu` → `bundled/svg/list-menu.svg` (or the closest available SVG). The Figma mock's `menu-01` icon is a standard 3-line hamburger.
|
||||
- `Grid` already exists → `bundled/svg/grid.svg`. Should work for the expanded segment.
|
||||
- For the settings button, `Settings` → `bundled/svg/settings.svg` already exists. Check whether the Figma `settings-04` sliders icon matches visually; if not, add a `Settings04` variant mapping to a new SVG.
|
||||
|
||||
If new SVGs are needed, add them to `resources/bundled/svg/` and extend the `Icon` enum + `From<Icon> for &'static str` match.
|
||||
|
||||
### 3. Add state to `VerticalTabsPanelState`
|
||||
|
||||
```rust
|
||||
pub(super) struct VerticalTabsPanelState {
|
||||
// ... existing fields ...
|
||||
settings_button_mouse_state: MouseStateHandle,
|
||||
settings_popup_mouse_state: MouseStateHandle,
|
||||
show_settings_popup: bool,
|
||||
}
|
||||
```
|
||||
|
||||
Initialize all with `Default::default()` / `false`.
|
||||
|
||||
### 4. Add `WorkspaceAction` variants
|
||||
|
||||
```rust
|
||||
pub enum WorkspaceAction {
|
||||
// ... existing ...
|
||||
ToggleVerticalTabsSettingsPopup,
|
||||
SetVerticalTabsViewMode(VerticalTabsViewMode),
|
||||
}
|
||||
```
|
||||
|
||||
`ToggleVerticalTabsSettingsPopup` toggles `show_settings_popup` on the panel state and calls `ctx.notify()`.
|
||||
|
||||
`SetVerticalTabsViewMode` writes the new value through `TabSettings` (same as other setting mutations). Both actions should be listed in the `should_save_app_state_on_action` match as `false` (no workspace state save needed).
|
||||
|
||||
### 5. Update `render_control_bar`
|
||||
|
||||
Insert the settings button between the search bar and the new-tab button. Layout becomes:
|
||||
|
||||
```
|
||||
Flex::row [search_bar (Shrinkable)] [settings_button] [new_tab_button]
|
||||
```
|
||||
|
||||
The settings button:
|
||||
- Uses `Hoverable` wrapping an icon button with `WarpIcon::Settings` (or `Settings04`) at 16×16 in a 20×20 container with 2px padding.
|
||||
- Background is `fg_overlay_3` when `state.show_settings_popup` is true, `fg_overlay_2` on hover, transparent otherwise.
|
||||
- `on_click` dispatches `WorkspaceAction::ToggleVerticalTabsSettingsPopup`.
|
||||
- Wrap in a `Stack` to position a tooltip ("View options") as an overlay when hovered and popup is closed.
|
||||
- Wrap the whole button in a `SavePosition` with ID `"vertical_tabs_settings_button"` for popup anchoring.
|
||||
|
||||
### 6. Render the settings popup
|
||||
|
||||
Inside `render_vertical_tabs_panel`, after building the panel content `Flex::column`, wrap the result in a `Stack`. When `state.show_settings_popup` is true, add a positioned overlay child:
|
||||
|
||||
```rust
|
||||
if state.show_settings_popup {
|
||||
let popup = render_settings_popup(state, app);
|
||||
stack.add_positioned_overlay_child(
|
||||
popup,
|
||||
OffsetPositioning::offset_from_parent(
|
||||
vec2f(0., 4.),
|
||||
ParentOffsetBounds::WindowByPosition,
|
||||
ParentAnchor::BottomLeft,
|
||||
ChildAnchor::TopLeft,
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Alternative (simpler)**: Rather than making the popup a positioned overlay on the Stack, render it inline as an absolutely-positioned element anchored to the settings button's `SavePosition`. Both approaches work; use whichever is more consistent with existing popup patterns in the file.
|
||||
|
||||
`render_settings_popup` builds a `Container` styled as a popover (border `neutral_4`, background with subtle overlay, corner radius 6px, drop shadow). Contents:
|
||||
|
||||
```
|
||||
Container (popup styling)
|
||||
└─ Padding(16px horizontal, 8px vertical)
|
||||
└─ SegmentedControl (rendered inline via ChildView)
|
||||
```
|
||||
|
||||
The `SegmentedControl<VerticalTabsViewMode>` is **not** stored as a `ViewHandle` — it is too lightweight for that. Instead, build the segmented control UI manually using the same pattern: two `Hoverable` icon buttons inside a rounded container, with the active segment highlighted. This avoids needing a persistent `ViewHandle` on `VerticalTabsPanelState` and keeps the popup self-contained.
|
||||
|
||||
Concretely, `render_settings_popup` returns:
|
||||
|
||||
```rust
|
||||
fn render_settings_popup(state: &VerticalTabsPanelState, app: &AppContext) -> Box<dyn Element> {
|
||||
let current_mode = *TabSettings::as_ref(app).vertical_tabs_view_mode;
|
||||
// Build two icon buttons (compact / expanded), highlight the active one
|
||||
// on_click dispatches WorkspaceAction::SetVerticalTabsViewMode(...)
|
||||
}
|
||||
```
|
||||
|
||||
**Dismiss handling**: The popup should close on:
|
||||
- Outside click: Wrap the popup's overlay in a dismiss layer (same pattern as existing menus — e.g., a transparent full-window Hoverable behind the popup that dispatches close on click).
|
||||
- Escape: Add a keybinding handler or check focus loss.
|
||||
- Re-click on settings button: Already handled by `ToggleVerticalTabsSettingsPopup`.
|
||||
|
||||
### 7. Add `render_compact_pane_row`
|
||||
|
||||
New function in `vertical_tabs.rs`:
|
||||
|
||||
```rust
|
||||
fn render_compact_pane_row(props: PaneProps<'_>, app: &AppContext) -> Box<dyn Element>
|
||||
```
|
||||
|
||||
This function shares the same `PaneProps`, `Hoverable` wrapper, click handler, background/border logic, and cursor as `render_pane_row`. The difference is only the content layout.
|
||||
|
||||
**Approach**: Extract the shared interaction wrapper into a helper, then call either compact or expanded content rendering inside it. Specifically:
|
||||
|
||||
```rust
|
||||
fn render_pane_row_wrapper(
|
||||
props: PaneProps<'_>,
|
||||
is_compact: bool,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
// ... Hoverable + click + right-click + cursor + background logic (unchanged) ...
|
||||
let content = if is_compact {
|
||||
render_compact_content(&props, app)
|
||||
} else {
|
||||
render_expanded_content(&props, app) // current logic extracted
|
||||
};
|
||||
// ... container with padding, border, corner radius ...
|
||||
}
|
||||
```
|
||||
|
||||
**Compact content layout**:
|
||||
|
||||
```
|
||||
Flex::row (CrossAxisAlignment::Center, spacing 4px)
|
||||
├─ [icon 16×16] // type-dependent
|
||||
├─ [title text, 12px] // Shrinkable, single line, ellipsis
|
||||
└─ [indicator 16×16]? // optional, right-aligned (unsaved circle for code panes)
|
||||
```
|
||||
|
||||
**Compact padding**: 8px vertical, 12px horizontal (vs 12px uniform in expanded). This matches the Figma mock's `py-8 px-12`.
|
||||
|
||||
**Per-type icon and title logic**:
|
||||
|
||||
For terminal panes, read from `TerminalView`:
|
||||
|
||||
```rust
|
||||
let (icon, title) = if let Some(view_handle) = terminal_view_handle.as_ref() {
|
||||
let tv: &TerminalView = view_handle.as_ref(app);
|
||||
let conversation_title = tv.selected_conversation_display_title(app);
|
||||
let conversation_status = tv.selected_conversation_status(app);
|
||||
let is_ambient = tv.is_ambient_agent_session(app);
|
||||
|
||||
if let Some(conv_title) = conversation_title {
|
||||
// Agent session: status icon + conversation title
|
||||
let icon_element = if let Some(status) = conversation_status {
|
||||
render_status_element(&status, 12., appearance)
|
||||
} else if is_ambient {
|
||||
WarpIcon::OzCloud icon element
|
||||
} else {
|
||||
WarpIcon::Oz icon element
|
||||
};
|
||||
(icon_element, conv_title)
|
||||
} else {
|
||||
// Non-agent terminal: terminal icon + terminal title (NOT pwd)
|
||||
let terminal_title = tv.terminal_title_from_shell();
|
||||
(WarpIcon::Terminal icon element, terminal_title)
|
||||
}
|
||||
} else {
|
||||
// Non-terminal pane: type icon + pane title (already in props.title)
|
||||
(typed.icon() icon element, props.title.clone())
|
||||
};
|
||||
```
|
||||
|
||||
For the unsaved indicator (code panes only): append a `CircleFilled` icon (16×16, sub-text color) to the right of the row when `typed.badge(app).is_some()`.
|
||||
|
||||
### 8. Integrate view mode into `render_pane_row` call sites
|
||||
|
||||
In `render_tab_group`, where `render_pane_row(pane_props, app)` is called inside the rows loop, read the current view mode and branch:
|
||||
|
||||
```rust
|
||||
let view_mode = *TabSettings::as_ref(app).vertical_tabs_view_mode;
|
||||
let row = match view_mode {
|
||||
VerticalTabsViewMode::Compact => render_compact_pane_row(pane_props, app),
|
||||
VerticalTabsViewMode::Expanded => render_pane_row(pane_props, app),
|
||||
};
|
||||
rows.add_child(row);
|
||||
```
|
||||
|
||||
Or use the combined `render_pane_row_wrapper` approach from section 7.
|
||||
|
||||
### 9. Handle `WorkspaceAction` in `view.rs`
|
||||
|
||||
In the `WorkspaceAction` match in `handle_action`:
|
||||
|
||||
```rust
|
||||
WorkspaceAction::ToggleVerticalTabsSettingsPopup => {
|
||||
self.vertical_tabs_panel.show_settings_popup =
|
||||
!self.vertical_tabs_panel.show_settings_popup;
|
||||
ctx.notify();
|
||||
}
|
||||
WorkspaceAction::SetVerticalTabsViewMode(mode) => {
|
||||
TabSettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
settings.vertical_tabs_view_mode.set_value(mode, ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
```
|
||||
|
||||
## End-to-end flow
|
||||
|
||||
1. User clicks the settings icon button in the vertical tabs control bar.
|
||||
2. `ToggleVerticalTabsSettingsPopup` is dispatched → `show_settings_popup` flips to `true` → panel re-renders.
|
||||
3. The panel's `Stack` now includes the popup overlay anchored below the button.
|
||||
4. User clicks the compact segment → `SetVerticalTabsViewMode(Compact)` dispatches → `TabSettings` writes the new value → settings sync triggers → panel re-renders.
|
||||
5. `render_tab_group` reads the updated `VerticalTabsViewMode` and calls `render_compact_pane_row` for each pane.
|
||||
6. Each compact row renders: icon + title in a single line.
|
||||
7. Popup auto-closes because the click dismisses it (or the user clicks outside / presses Escape).
|
||||
8. On next launch, the setting is loaded from the synced settings store, and the panel renders in compact mode immediately.
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
**Icon availability**: The Figma mock references `settings-04`, `menu-01`, and `grid-01` icons. The existing `Icon::Menu` maps to `layout-left.svg` (sidebar icon), not a hamburger. Mitigation: audit `resources/bundled/svg/` for matching SVGs; add new variants if needed. If exact icons are unavailable, use the closest existing ones (`Settings`, `Menu`, `Grid`) and iterate visually.
|
||||
|
||||
**Segmented control as ViewHandle**: Creating a `ViewHandle<SegmentedControl<VerticalTabsViewMode>>` requires storing it on `VerticalTabsPanelState` and wiring up subscriptions. This adds complexity for a two-button toggle. Mitigation: build the toggle inline as two `Hoverable` icon buttons inside a styled container, dispatching `WorkspaceAction::SetVerticalTabsViewMode` directly. This avoids the `ViewHandle` lifecycle overhead.
|
||||
|
||||
**Popup dismiss on outside click**: The existing popup patterns in the workspace (e.g., `show_new_session_dropdown_menu`) rely on `Menu` views that handle their own focus/dismiss. Our popup is simpler (no menu items). Mitigation: render a full-window transparent `Hoverable` behind the popup that dispatches `ToggleVerticalTabsSettingsPopup` on click. This is the same "click-away backdrop" pattern used elsewhere.
|
||||
|
||||
**Compact row height consistency**: Different pane types produce different icon heights (status badges have padding/background, plain icons don't). Mitigation: use `ConstrainedBox` to fix all icons to 16×16 and fix the row height via `ConstrainedBox::with_height` or consistent padding.
|
||||
|
||||
## Testing and validation
|
||||
|
||||
- **Visual verification**: Build and run with `cargo run`, open several tabs with different pane types (terminal, code, agent, settings, notebook), toggle between compact and expanded. Verify single-line rendering and correct icons per pane type.
|
||||
- **Setting persistence**: Switch to compact, restart (`cargo run` again), verify the panel starts in compact mode.
|
||||
- **Popup behavior**: Click the settings icon, verify popup appears below it. Click outside, verify it closes. Click the icon again, verify it toggles.
|
||||
- **Edge cases**:
|
||||
- Empty tab group → "No tabs open" message should display in both modes.
|
||||
- Collapsed groups → remain collapsed across mode switches.
|
||||
- Minimum panel width (200px) → compact rows truncate gracefully.
|
||||
- Tab colors → tint renders correctly on compact rows.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- **Group-by options**: The popup will eventually include "Group panes by" options (Tab, Directory/Environment, Branch, Status) above the segmented control, per the Figma mock. The popup container is designed to accommodate additional content above the toggle.
|
||||
- **Keyboard shortcut**: A keybinding to toggle compact/expanded mode can be added later as a new action binding.
|
||||
- **Search functionality**: The search input remains inert. When implemented, it should filter in both compact and expanded modes.
|
||||
- **Icon audit**: Once the feature is visually reviewed, confirm the chosen icons match the Figma mock and swap SVGs if needed.
|
||||
Reference in New Issue
Block a user