8.0 KiB
TECH — Don't auto-add indexed repos to Directory tab colors
See also: PRODUCT.md in this directory.
Linear: APP-4115.
Problem
Two coupled code changes are needed to deliver the product behavior:
- Stop the
CodebaseIndexManager-driven subscription that auto-populatesTabSettings.directory_tab_colorswith newly indexed paths. - Make the
Add directory colorcontrol conditional: render the existing searchableFilterableDropdownwhen candidate repos exist, and render a plainActionButtonthat opens the folder picker when the candidate set is empty.
Relevant code
app/src/workspace/view.rs:2775-2790— the subscription that callsDirectoryTabColors::merge_new_pathson everyCodebaseIndexManagerEvent::SyncStateUpdated.app/src/workspace/tab_settings.rs:168-243—DirectoryTabColor/DirectoryTabColors, includingwith_colorandcolor_for_directory.app/src/settings_view/appearance_page.rs:1371-1378— construction ofDirectoryColorAddPicker.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_pathhelper used in the visible list.
Current state
- On startup and on every indexing sync-state update, the workspace view reads all
CodebaseIndexManagerpaths and callsDirectoryTabColors::merge_new_paths, which inserts each canonical path intodirectory_tab_colorsasUnassignedif not already present. DirectoryColorAddPickeralready wrapsFilterableDropdownand computes the correct candidate set for the searchable known-repo flow.- The X button on each row dispatches
RemoveDefaultDirectoryTabColor, which writesDirectoryTabColor::Suppressedrather 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.
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 dispatchDirectoryColorAddPickerAction::AddNewDirectory. - Creates the existing
FilterableDropdown, keeps its current searchable behavior, and preserves the pinned+ Add directory…footer. - Subscribes to
CodebaseIndexManager,PersistedWorkspace, andTabSettingsso 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 todropdown.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_itemsis true, renderChildView::new(&self.dropdown). - Otherwise, render
ChildView::new(&self.button).handle_action: Select(path)emitsDirectoryColorAddPickerEvent::Selected(path)and letsFilterableDropdownclose itself after dispatch.AddNewDirectorycloses the dropdown if needed and emitsDirectoryColorAddPickerEvent::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_colordoes, - dedupes by canonical key,
- filters out entries already present with a non-
Suppressedcolor, - keeps
Suppressedentries 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_pagecontinues to construct the picker and subscribe to its events.handle_directory_color_add_picker_eventcontinues to mapSelected(path)toadd_directory_tab_color_path(path, ctx)andRequestAddFromFilePickertoopen_directory_tab_color_folder_picker(ctx).- The existing rebuild of
directory_tab_color_delete_buttonsandcolor_picker_dot_statesinhandle_tab_settings_eventremains 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
Suppressedkey, selecting it transitions that entry back toUnassignedthroughwith_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
DirectoryColorAddPickerrenders the searchable dropdown.- The user opens it, filters if needed, and either selects a row or clicks the pinned footer.
- Row selection emits
Selected(path). - Footer click emits
RequestAddFromFilePicker.
No candidate repos exist
DirectoryColorAddPickerrenders the plainAdd directory colorbutton.- Clicking the button emits
RequestAddFromFilePickerimmediately. - The native folder picker opens, and on success the selected path is added through the existing appearance-page helper.
Indexing sync state updates
CodebaseIndexManagerEvent::SyncStateUpdatedfires.directory_tab_colorsis not modified.DirectoryColorAddPickerrefreshes 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.
Suppressedsemantics become less obvious. Keep the existingSuppressedwrite path unchanged so prefix-shadowing behavior remains stable.- Stale dropdown state when candidates disappear. Close the dropdown in
refresh_itemswhenever the candidate list becomes empty.
Testing and validation
- Unit tests on
compute_candidate_pathscovering dedupe,Suppressed, missing-path filtering, worktree inclusion, and sort order. - Regression test confirming that
CodebaseIndexManagerEvent::SyncStateUpdatedno longer mutatesdirectory_tab_colors. - Manual validation of both control states:
- Dropdown shown when candidates exist.
- Fallback button shown when no candidates exist.
- Dropdown row selection adds an
Unassignedentry. - Dropdown footer opens the folder picker.
- Fallback button opens the folder picker.
cargo checkandcargo fmt --check.verify-ui-change-in-cloudfor the final UI check.
Follow-ups
None.