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
+74
View File
@@ -0,0 +1,74 @@
# Don't auto-add indexed repos to Directory tab colors
Linear: [APP-4115](https://linear.app/warpdotdev/issue/APP-4115/dont-auto-add-indexed-repos-including-worktrees-to-directory-tab)
## Summary
The Directory tab colors list in Settings > Appearance should stop auto-populating itself from the set of indexed codebases. Instead, the user explicitly chooses which directories to color, assisted by a searchable `Add directory color` dropdown when known repos are available. If there are no candidate repos left to show, the control falls back to a plain `Add directory color` button that opens the native folder picker directly.
## Problem
Today, every time `CodebaseIndexManager` emits a sync-state update, Warp merges all currently-indexed codebase paths into the user's `appearance.tabs.directory_tab_colors` setting as `Unassigned` entries. This produces two user-visible problems reported in `#feedback-app`:
1. Worktrees under paths like `~/.warp-dev/worktrees/warp-internal/...` get indexed as codebases and then silently added to the colors list, flooding the settings panel with entries the user never wanted to manage.
2. The Directory tab colors list grows to reflect indexing state rather than intentional user configuration, making the settings panel noisy and hard to use.
The relevant subscription lives in `app/src/workspace/view.rs:2775-2790`. The existing folder-picker button lives in `app/src/settings_view/appearance_page.rs:4632-4671`.
## Goals
- Stop auto-adding any indexed codebase (including worktrees) to `directory_tab_colors`.
- Make known repos one click to add when candidates are available.
- Preserve a direct folder-picker path when there are no known repos left to offer.
## Non-goals
- Migrating or cleaning up entries that were already auto-added on prior app versions. Users remove clutter manually via the existing per-row X button.
- Changing how `color_for_directory` resolves the active tab color for any working directory. Longest-prefix matching is unchanged.
- Changing the set of ANSI color dots, the Suppressed state, or the overall layout of the Directory tab colors settings card.
- Hiding worktrees from the candidate set. Worktrees remain selectable so users can intentionally color them.
- Introducing a new setting to toggle the auto-add behavior.
## Figma / design references
Figma: none provided. Keep the searchable dropdown behavior aligned with the `New worktree config` sidecar in `app/src/workspace/view.rs:8143-8301`. Keep the fallback button aligned with the prior `Add directory color` button treatment in `app/src/settings_view/appearance_page.rs:4674-4720`.
## User experience
### Removal of auto-add
- Newly indexed repos (including worktrees) never appear in `appearance.tabs.directory_tab_colors` on their own.
- Existing indexed or ambiently-detected repos are not added when the user opens Settings, switches tabs, starts new sessions, or triggers reindexing.
- Previously auto-added entries remain in the setting until the user removes them via the X button. The visible list and X-button behavior in `appearance_page.rs:4674-4815` is unchanged.
### `Add directory color` control
The header control is conditional:
- If candidate repos exist, render the searchable `Add directory color` dropdown.
- If no candidate repos exist, render a plain `Add directory color` action button with the leading plus icon.
Candidate set:
- The candidate set is the union of indexed codebase paths from `CodebaseIndexManager::get_codebase_paths` and persisted workspace paths from `PersistedWorkspace::workspaces()`.
- Paths are deduped by canonical form.
- Linked worktrees are kept in the list.
- Entries whose canonical path is already a key in `directory_tab_colors` with a color other than `Suppressed` are filtered out.
- Entries keyed as `Suppressed` remain selectable so users can undo a prior removal.
- Entries whose path no longer exists on disk are filtered out.
- Remaining entries are sorted alphabetically by canonical path, matching the visible colors list.
When candidates exist:
- Each dropdown row shows the user-friendly path, using the same `user_friendly_path` helper used by the list below.
- The dropdown search field filters rows by case-insensitive substring match on the full path.
- A pinned footer labeled `+ Add directory…` remains visible at the bottom of the dropdown.
- Selecting a row adds the corresponding canonical path to `directory_tab_colors` with `DirectoryTabColor::Unassigned`.
- Clicking the pinned footer closes the dropdown and opens the native folder picker.
When no candidates exist:
- Clicking the fallback button opens the native folder picker directly.
- If the user cancels the folder picker, nothing changes.
Interaction details and invariants:
- Opening the dropdown or clicking the fallback button never mutates `directory_tab_colors` by itself. Mutation only happens on selecting a row or completing the folder picker.
- The visible colors list below the header is rendered exactly as it is today: same row layout, same color dots, same X button, same alphabetical sort order.
- The per-row color picker and X button continue to read/write through the existing `SetDefaultDirectoryTabColor` and `RemoveDefaultDirectoryTabColor` actions.
- `RemoveDefaultDirectoryTabColor` continues to persist `Suppressed` rather than deleting the key.
## Success criteria
- Starting Warp fresh, opening and closing repos, triggering codebase indexing, or creating worktrees does **not** add any entries to `appearance.tabs.directory_tab_colors` in the TOML settings file.
- When candidate repos exist, the `Add directory color` control is the searchable dropdown.
- The dropdown lists indexed codebases and persisted workspaces that are not already keyed in `directory_tab_colors` with a non-`Suppressed` color. Entries are deduped by canonical path.
- Worktrees (paths whose repository has an external gitdir) are included in the candidate set and can be added.
- Selecting a dropdown row adds that path to `directory_tab_colors` as `Unassigned`. The row appears in the visible colors list below immediately.
- The pinned `+ Add directory…` footer remains visible at the bottom of the dropdown and opens the native folder picker.
- When every known repo is already present (with a non-`Suppressed` color), the control falls back to the plain `Add directory color` button and clicking it opens the native folder picker directly.
- Entries that were auto-added by the old behavior on prior app versions are not removed, altered, or reordered by this change.
- Longest-prefix matching in `DirectoryTabColors::color_for_directory` returns the same color for any given working directory that it returned before this change, given the same persisted configuration.
## Validation
- Unit tests on a helper that computes the candidate set, covering dedupe across indexed and persisted sources, filtering out keys present with non-`Suppressed` colors, keeping `Suppressed` keys, filtering out missing paths, and worktree inclusion.
- Unit test confirming that handling a `CodebaseIndexManagerEvent::SyncStateUpdated` does not mutate `directory_tab_colors`.
- Manual validation:
1. With several worktrees under `~/.warp-dev/worktrees/warp-internal/...` indexed, confirm none appear in the Directory tab colors list on a fresh profile.
2. Confirm that when candidate repos exist, `Add directory color` renders as the searchable dropdown and its list contains those worktrees.
3. Select a repo from the dropdown, confirm it appears in the list below with the `Unassigned` dot selected.
4. With all known repos added, confirm the control falls back to the plain button and clicking it opens the native folder picker.
5. Pick a folder via either the dropdown footer or the fallback button, confirm it is added to the list just like the prior file-picker flow.
- Visual check via `verify-ui-change-in-cloud` that the dropdown renders correctly when candidates exist and that the plain fallback button appears when there are none.
## Open questions
- None at spec creation.
+101
View File
@@ -0,0 +1,101 @@
# TECH — Don't auto-add indexed repos to Directory tab colors
See also: `PRODUCT.md` in this directory.
Linear: [APP-4115](https://linear.app/warpdotdev/issue/APP-4115/dont-auto-add-indexed-repos-including-worktrees-to-directory-tab).
## Problem
Two coupled code changes are needed to deliver the product behavior:
1. Stop the `CodebaseIndexManager`-driven subscription that auto-populates `TabSettings.directory_tab_colors` with newly indexed paths.
2. Make the `Add directory color` control conditional: render the existing searchable `FilterableDropdown` when candidate repos exist, and render a plain `ActionButton` that opens the folder picker when the candidate set is empty.
## Relevant code
- `app/src/workspace/view.rs:2775-2790` — the subscription that calls `DirectoryTabColors::merge_new_paths` on every `CodebaseIndexManagerEvent::SyncStateUpdated`.
- `app/src/workspace/tab_settings.rs:168-243``DirectoryTabColor` / `DirectoryTabColors`, including `with_color` and `color_for_directory`.
- `app/src/settings_view/appearance_page.rs:1371-1378` — construction of `DirectoryColorAddPicker`.
- `app/src/settings_view/appearance_page.rs:2416-2425``handle_directory_color_add_picker_event`.
- `app/src/settings_view/appearance_page.rs:4632-4671` — helper functions for adding a directory tab color directly and opening the folder picker.
- `app/src/settings_view/appearance_page.rs:4674-4815``DirectoryTabColorsWidget`, delete buttons, and rendering of the colors list.
- `app/src/settings_view/directory_color_add_picker.rs` — candidate computation, conditional rendering, and footer/button event emission.
- `app/src/view_components/filterable_dropdown.rs` — existing searchable dropdown behavior and pinned footer support.
- `app/src/ai/persisted_workspace.rs:714-721``PersistedWorkspace::workspaces()` iterator.
- `crates/ai/src/index/full_source_code_embedding/manager.rs:556-558``get_codebase_paths`.
- `crates/warp_util/src/path.rs:85-114``user_friendly_path` helper used in the visible list.
## Current state
- On startup and on every indexing sync-state update, the workspace view reads all `CodebaseIndexManager` paths and calls `DirectoryTabColors::merge_new_paths`, which inserts each canonical path into `directory_tab_colors` as `Unassigned` if not already present.
- `DirectoryColorAddPicker` already wraps `FilterableDropdown` and computes the correct candidate set for the searchable known-repo flow.
- The X button on each row dispatches `RemoveDefaultDirectoryTabColor`, which writes `DirectoryTabColor::Suppressed` rather than deleting the key.
## Proposed changes
### 1. Remove the auto-add subscription
Delete the entire `if FeatureFlag::DirectoryTabColors.is_enabled() { ctx.subscribe_to_model(&CodebaseIndexManager::handle(ctx), …) }` block at `app/src/workspace/view.rs:2775-2790`. No replacement subscription is needed.
Delete `DirectoryTabColors::merge_new_paths` and any tests or helpers whose only purpose was validating the old auto-add behavior.
### 2. Keep `FilterableDropdown` and add a fallback button
Keep `app/src/settings_view/directory_color_add_picker.rs` centered on the existing `FilterableDropdown` implementation. Add a second child view for the plain button and track whether the dropdown currently has any candidate rows.
```rust path=null start=null
pub struct DirectoryColorAddPicker {
button: ViewHandle<ActionButton>,
dropdown: ViewHandle<FilterableDropdown<DirectoryColorAddPickerAction>>,
footer_mouse_state: MouseStateHandle,
has_dropdown_items: bool,
}
```
`DirectoryColorAddPicker::new`:
- Creates the fallback `ActionButton::new("Add directory color", SecondaryTheme).with_icon(Icon::Plus)` and wires it to dispatch `DirectoryColorAddPickerAction::AddNewDirectory`.
- Creates the existing `FilterableDropdown`, keeps its current searchable behavior, and preserves the pinned `+ Add directory…` footer.
- Subscribes to `CodebaseIndexManager`, `PersistedWorkspace`, and `TabSettings` so the candidate list refreshes when any source changes.
`refresh_items`:
- Computes the candidate set using the existing pure helper.
- Converts candidates into `DropdownItem<DirectoryColorAddPickerAction>` values and passes them to `dropdown.set_items(items, ctx)`.
- Sets `has_dropdown_items = !items.is_empty()`.
- If the list becomes empty, closes the dropdown so the view can switch cleanly to the fallback button.
`render`:
- If `has_dropdown_items` is true, render `ChildView::new(&self.dropdown)`.
- Otherwise, render `ChildView::new(&self.button)`.
`handle_action`:
- `Select(path)` emits `DirectoryColorAddPickerEvent::Selected(path)` and lets `FilterableDropdown` close itself after dispatch.
- `AddNewDirectory` closes the dropdown if needed and emits `DirectoryColorAddPickerEvent::RequestAddFromFilePicker`.
### 3. Candidate-set helper
Keep `compute_candidate_paths` as the pure helper that:
- unions indexed and persisted paths,
- canonicalizes keys the same way `DirectoryTabColors::with_color` does,
- dedupes by canonical key,
- filters out entries already present with a non-`Suppressed` color,
- keeps `Suppressed` entries re-addable,
- filters out missing paths,
- sorts by canonical key.
### 4. Appearance page wiring
No appearance-page architectural change is needed beyond the existing `DirectoryColorAddPicker` integration:
- `build_page` continues to construct the picker and subscribe to its events.
- `handle_directory_color_add_picker_event` continues to map `Selected(path)` to `add_directory_tab_color_path(path, ctx)` and `RequestAddFromFilePicker` to `open_directory_tab_color_folder_picker(ctx)`.
- The existing rebuild of `directory_tab_color_delete_buttons` and `color_picker_dot_states` in `handle_tab_settings_event` remains the source of truth for the visible list.
### 5. `Suppressed` behavior
No special-case behavior is needed beyond the existing helper and appearance-page add path:
- If a candidate row corresponds to a `Suppressed` key, selecting it transitions that entry back to `Unassigned` through `with_color(path, DirectoryTabColor::Unassigned)`.
- If the user removes a row and it still exists in the indexed/persisted candidate set, it will reappear in the dropdown.
## End-to-end flow
### Candidate repos exist
1. `DirectoryColorAddPicker` renders the searchable dropdown.
2. The user opens it, filters if needed, and either selects a row or clicks the pinned footer.
3. Row selection emits `Selected(path)`.
4. Footer click emits `RequestAddFromFilePicker`.
### No candidate repos exist
1. `DirectoryColorAddPicker` renders the plain `Add directory color` button.
2. Clicking the button emits `RequestAddFromFilePicker` immediately.
3. The native folder picker opens, and on success the selected path is added through the existing appearance-page helper.
### Indexing sync state updates
1. `CodebaseIndexManagerEvent::SyncStateUpdated` fires.
2. `directory_tab_colors` is not modified.
3. `DirectoryColorAddPicker` refreshes its candidate list and may switch between dropdown and button depending on whether any rows remain.
## Risks and mitigations
- **The control changes shape when the candidate count crosses zero.** This is intentional. The dropdown only adds value when there are known repos to show.
- **`Suppressed` semantics become less obvious.** Keep the existing `Suppressed` write path unchanged so prefix-shadowing behavior remains stable.
- **Stale dropdown state when candidates disappear.** Close the dropdown in `refresh_items` whenever the candidate list becomes empty.
## Testing and validation
- Unit tests on `compute_candidate_paths` covering dedupe, `Suppressed`, missing-path filtering, worktree inclusion, and sort order.
- Regression test confirming that `CodebaseIndexManagerEvent::SyncStateUpdated` no longer mutates `directory_tab_colors`.
- Manual validation of both control states:
1. Dropdown shown when candidates exist.
2. Fallback button shown when no candidates exist.
3. Dropdown row selection adds an `Unassigned` entry.
4. Dropdown footer opens the folder picker.
5. Fallback button opens the folder picker.
- `cargo check` and `cargo fmt --check`.
- `verify-ui-change-in-cloud` for the final UI check.
## Follow-ups
None.