diff --git a/assets/icons/knockouts/archive_bg.svg b/assets/icons/knockouts/archive_bg.svg new file mode 100644 index 0000000000000000000000000000000000000000..1954d14b1ee16adf605e2cfe31309838d2448f7a --- /dev/null +++ b/assets/icons/knockouts/archive_bg.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/knockouts/archive_fg.svg b/assets/icons/knockouts/archive_fg.svg new file mode 100644 index 0000000000000000000000000000000000000000..74d1238c5399105ba83046c63f4505b0d1fec877 --- /dev/null +++ b/assets/icons/knockouts/archive_fg.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index c331c82046e3925f154c3b1fb2f55580392f2475..15a4a6f64d5e1a12686d43ddc01dee14df80a7f4 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -720,7 +720,7 @@ "right": "menu::SelectChild", "enter": "menu::Confirm", "ctrl-f": "agents_sidebar::FocusSidebarFilter", - "ctrl-g": "agents_sidebar::ToggleArchive", + "ctrl-g": "agents_sidebar::ViewAllThreads", "shift-backspace": "agent::RemoveSelectedThread", "ctrl-tab": "agents_sidebar::ToggleThreadSwitcher", "ctrl-shift-tab": ["agents_sidebar::ToggleThreadSwitcher", { "select_last": true }], diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 21b260116ae5f8a58fcdb2871a3b4e8e6c692e4d..4121b376b5a142e298bc655ac8c925507aaeae6e 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -783,7 +783,7 @@ "right": "menu::SelectChild", "enter": "menu::Confirm", "cmd-f": "agents_sidebar::FocusSidebarFilter", - "cmd-g": "agents_sidebar::ToggleArchive", + "cmd-g": "agents_sidebar::ViewAllThreads", "shift-backspace": "agent::RemoveSelectedThread", "ctrl-tab": "agents_sidebar::ToggleThreadSwitcher", "ctrl-shift-tab": ["agents_sidebar::ToggleThreadSwitcher", { "select_last": true }], diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json index d3e185a6936266b6fde722d5622d6ac68d9dbf98..2098f18ff0f2e264b6891f412a29540972629a92 100644 --- a/assets/keymaps/default-windows.json +++ b/assets/keymaps/default-windows.json @@ -720,7 +720,7 @@ "right": "menu::SelectChild", "enter": "menu::Confirm", "ctrl-f": "agents_sidebar::FocusSidebarFilter", - "ctrl-g": "agents_sidebar::ToggleArchive", + "ctrl-g": "agents_sidebar::ViewAllThreads", "shift-backspace": "agent::RemoveSelectedThread", "ctrl-tab": "agents_sidebar::ToggleThreadSwitcher", "ctrl-shift-tab": ["agents_sidebar::ToggleThreadSwitcher", { "select_last": true }], diff --git a/crates/agent_ui/src/threads_archive_view.rs b/crates/agent_ui/src/threads_archive_view.rs index e9101b1553837d7ba82dc0d1550fe11c51591a37..aa082a0c23e524c142426bde171a7ed28aa32cf7 100644 --- a/crates/agent_ui/src/threads_archive_view.rs +++ b/crates/agent_ui/src/threads_archive_view.rs @@ -30,7 +30,7 @@ use picker::{ use project::{AgentId, AgentServerStore}; use settings::Settings as _; use theme::ActiveTheme; -use ui::{AgentThreadStatus, ThreadItem}; +use ui::{AgentThreadStatus, IconDecoration, IconDecorationKind, Tab, ThreadItem}; use ui::{ Divider, KeyBinding, ListItem, ListItemSpacing, ListSubHeader, Tooltip, WithScrollbar, prelude::*, utils::platform_title_bar_height, @@ -116,7 +116,7 @@ fn fuzzy_match_positions(query: &str, text: &str) -> Option> { pub enum ThreadsArchiveViewEvent { Close, - Unarchive { thread: ThreadMetadata }, + Activate { thread: ThreadMetadata }, CancelRestore { thread_id: ThreadId }, } @@ -140,6 +140,7 @@ pub struct ThreadsArchiveView { archived_thread_ids: HashSet, archived_branch_names: HashMap>, _load_branch_names_task: Task<()>, + show_archived_only: bool, } impl ThreadsArchiveView { @@ -154,7 +155,7 @@ impl ThreadsArchiveView { let filter_editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); - editor.set_placeholder_text("Search archive…", window, cx); + editor.set_placeholder_text("Search threads…", window, cx); editor }); @@ -213,6 +214,7 @@ impl ThreadsArchiveView { archived_thread_ids: HashSet::default(), archived_branch_names: HashMap::default(), _load_branch_names_task: Task::ready(()), + show_archived_only: false, }; this.update_items(cx); @@ -251,9 +253,11 @@ impl ThreadsArchiveView { } fn update_items(&mut self, cx: &mut Context) { + let show_archived_only = self.show_archived_only; let sessions = ThreadMetadataStore::global(cx) .read(cx) - .archived_entries() + .entries() + .filter(|t| !show_archived_only || t.archived) .sorted_by_cached_key(|t| t.created_at.unwrap_or(t.updated_at)) .rev() .cloned() @@ -380,6 +384,11 @@ impl ThreadsArchiveView { }); } + fn archive_thread(&mut self, thread_id: ThreadId, cx: &mut Context) { + self.preserve_selection_on_next_update = true; + ThreadMetadataStore::global(cx).update(cx, |store, cx| store.archive(thread_id, None, cx)); + } + fn unarchive_thread( &mut self, thread: ThreadMetadata, @@ -398,7 +407,7 @@ impl ThreadsArchiveView { self.mark_restoring(&thread.thread_id, cx); self.selection = None; self.reset_filter_editor_text(window, cx); - cx.emit(ThreadsArchiveViewEvent::Unarchive { thread }); + cx.emit(ThreadsArchiveViewEvent::Activate { thread }); } fn show_project_picker_for_thread( @@ -580,6 +589,8 @@ impl ThreadsArchiveView { let is_restoring = self.restoring.contains(&thread.thread_id); + let is_archived = thread.archived; + let branch_names_for_thread: HashMap = self .archived_branch_names .get(&thread.thread_id) @@ -589,18 +600,37 @@ impl ThreadsArchiveView { .collect() }) .unwrap_or_default(); + let worktrees = worktree_info_from_thread_paths( &thread.worktree_paths, &branch_names_for_thread, ); + let color = cx.theme().colors(); + let knockout_color = color + .title_bar_background + .blend(color.panel_background.opacity(0.25)); + let archived_decoration = + IconDecoration::new(IconDecorationKind::Archive, knockout_color, cx) + .color(color.icon_disabled) + .position(gpui::Point { + x: px(-3.), + y: px(-3.5), + }); + let base = ThreadItem::new(id, thread.display_title()) .icon(icon) + .when(is_archived, |this| { + this.icon_color(Color::Muted) + .title_label_color(Color::Muted) + .icon_decoration(archived_decoration) + }) .when_some(icon_from_external_svg, |this, svg| { this.custom_icon_from_external_svg(svg) }) .timestamp(timestamp) .highlight_positions(highlight_positions.clone()) + .project_paths(thread.folder_paths().paths_owned()) .worktrees(worktrees) .focused(is_focused) .hovered(is_hovered) @@ -633,7 +663,7 @@ impl ThreadsArchiveView { ) .tooltip(Tooltip::text("Restoring…")) .into_any_element() - } else { + } else if is_archived { base.action_slot( IconButton::new("delete-thread", IconName::Trash) .icon_size(IconSize::Small) @@ -664,7 +694,31 @@ impl ThreadsArchiveView { }) }), ) - .tooltip(move |_, cx| Tooltip::for_action("Restore Thread", &menu::Confirm, cx)) + .tooltip(move |_, cx| { + Tooltip::for_action("Open Archived Thread", &menu::Confirm, cx) + }) + .on_click({ + let thread = thread.clone(); + cx.listener(move |this, _, window, cx| { + this.unarchive_thread(thread.clone(), window, cx); + }) + }) + .into_any_element() + } else { + base.action_slot( + IconButton::new("archive-thread", IconName::Archive) + .icon_size(IconSize::Small) + .icon_color(Color::Muted) + .tooltip(Tooltip::text("Archive Thread")) + .on_click({ + let thread_id = thread.thread_id; + cx.listener(move |this, _, _, cx| { + this.archive_thread(thread_id, cx); + cx.stop_propagation(); + }) + }), + ) + .tooltip(move |_, cx| Tooltip::for_action("Open Thread", &menu::Confirm, cx)) .on_click({ let thread = thread.clone(); cx.listener(move |this, _, window, cx| { @@ -807,6 +861,54 @@ impl ThreadsArchiveView { ) }) } + + fn render_toolbar(&self, cx: &mut Context) -> impl IntoElement { + let entry_count = self + .items + .iter() + .filter(|item| matches!(item, ArchiveListItem::Entry { .. })) + .count(); + + let count_label = if entry_count == 1 { + if self.show_archived_only { + "1 archived thread".to_string() + } else { + "1 thread".to_string() + } + } else if self.show_archived_only { + format!("{} archived threads", entry_count) + } else { + format!("{} threads", entry_count) + }; + + h_flex() + .mt_px() + .pl_2p5() + .pr_1p5() + .h(Tab::content_height(cx)) + .justify_between() + .border_b_1() + .border_color(cx.theme().colors().border) + .child( + Label::new(count_label) + .size(LabelSize::Small) + .color(Color::Muted), + ) + .child( + IconButton::new("toggle-archived-only", IconName::ListFilter) + .icon_size(IconSize::Small) + .toggle_state(self.show_archived_only) + .tooltip(Tooltip::text(if self.show_archived_only { + "Show All Threads" + } else { + "Show Archived Only" + })) + .on_click(cx.listener(|this, _, _, cx| { + this.show_archived_only = !this.show_archived_only; + this.update_items(cx); + })), + ) + } } pub fn format_history_entry_timestamp(entry_time: DateTime) -> String { @@ -847,7 +949,7 @@ impl Render for ThreadsArchiveView { let message = if has_query { "No threads match your search." } else { - "No archived or hidden threads yet." + "No threads yet." }; v_flex() @@ -889,6 +991,7 @@ impl Render for ThreadsArchiveView { .on_action(cx.listener(Self::remove_selected_thread)) .size_full() .child(self.render_header(window, cx)) + .when(!has_query, |this| this.child(self.render_toolbar(cx))) .child(content) } } @@ -1025,7 +1128,7 @@ impl ProjectPickerDelegate { .update(cx, |view, cx| { view.selection = None; view.reset_filter_editor_text(window, cx); - cx.emit(ThreadsArchiveViewEvent::Unarchive { + cx.emit(ThreadsArchiveViewEvent::Activate { thread: self.thread.clone(), }); }) diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index 1c3b8ba018fd968556d4f37d9f39598214f8cc04..60a714d06929ccda12d77a03052046e9b972e35d 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -68,7 +68,7 @@ gpui::actions!( /// Creates a new thread in the currently selected or active project group. NewThreadInGroup, /// Toggles between the thread list and the archive view. - ToggleArchive, + ViewAllThreads, ] ); @@ -2490,7 +2490,7 @@ impl Sidebar { }) } - fn activate_archived_thread( + fn open_thread_from_archive( &mut self, metadata: ThreadMetadata, window: &mut Window, @@ -2533,9 +2533,13 @@ impl Sidebar { } let store = ThreadMetadataStore::global(cx); - let task = store - .read(cx) - .get_archived_worktrees_for_thread(thread_id, cx); + let task = if metadata.archived { + store + .read(cx) + .get_archived_worktrees_for_thread(thread_id, cx) + } else { + Task::ready(Ok(Vec::new())) + }; let path_list = metadata.folder_paths().clone(); let restore_task = cx.spawn_in(window, async move |this, cx| { @@ -2545,8 +2549,10 @@ impl Sidebar { if archived_worktrees.is_empty() { this.update_in(cx, |this, window, cx| { this.restoring_tasks.remove(&thread_id); - ThreadMetadataStore::global(cx) - .update(cx, |store, cx| store.unarchive(thread_id, cx)); + if metadata.archived { + ThreadMetadataStore::global(cx) + .update(cx, |store, cx| store.unarchive(thread_id, cx)); + } if let Some(workspace) = this.find_current_workspace_for_path_list( &path_list, @@ -4389,7 +4395,7 @@ impl Sidebar { this.child( IconButton::new("thread-import", IconName::ThreadImport) .icon_size(IconSize::Small) - .tooltip(Tooltip::text("Import ACP Threads")) + .tooltip(Tooltip::text("Import External Agent Threads")) .on_click(cx.listener(|this, _, window, cx| { this.show_archive(window, cx); this.show_thread_import_modal(window, cx); @@ -4401,10 +4407,10 @@ impl Sidebar { .icon_size(IconSize::Small) .toggle_state(is_archive) .tooltip(move |_, cx| { - Tooltip::for_action("Toggle Archived Threads", &ToggleArchive, cx) + Tooltip::for_action("View All Threads", &ViewAllThreads, cx) }) .on_click(cx.listener(|this, _, window, cx| { - this.toggle_archive(&ToggleArchive, window, cx); + this.toggle_archive(&ViewAllThreads, window, cx); })), ) .child(self.render_recent_projects_button(cx)); @@ -4522,7 +4528,7 @@ impl Sidebar { ) } - fn toggle_archive(&mut self, _: &ToggleArchive, window: &mut Window, cx: &mut Context) { + fn toggle_archive(&mut self, _: &ViewAllThreads, window: &mut Window, cx: &mut Context) { match &self.view { SidebarView::ThreadList => { let side = match self.side(cx) { @@ -4574,8 +4580,8 @@ impl Sidebar { ThreadsArchiveViewEvent::Close => { this.show_thread_list(window, cx); } - ThreadsArchiveViewEvent::Unarchive { thread } => { - this.activate_archived_thread(thread.clone(), window, cx); + ThreadsArchiveViewEvent::Activate { thread } => { + this.open_thread_from_archive(thread.clone(), window, cx); } ThreadsArchiveViewEvent::CancelRestore { thread_id } => { this.restoring_tasks.remove(thread_id); diff --git a/crates/sidebar/src/sidebar_tests.rs b/crates/sidebar/src/sidebar_tests.rs index a174d7f316e195076854fe2f80d94512c2f81177..2fa51f90fe6b3602e72d9fdc3efc6588cd7087e8 100644 --- a/crates/sidebar/src/sidebar_tests.rs +++ b/crates/sidebar/src/sidebar_tests.rs @@ -4114,7 +4114,7 @@ async fn test_activate_archived_thread_with_saved_paths_activates_matching_works // Call activate_archived_thread – should resolve saved paths and // switch to the workspace for project-b. sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(session_id.clone()), @@ -4182,7 +4182,7 @@ async fn test_activate_archived_thread_cwd_fallback_with_matching_workspace( // No thread saved to the store – cwd is the only path hint. sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(acp::SessionId::new(Arc::from("unknown-session"))), @@ -4248,7 +4248,7 @@ async fn test_activate_archived_thread_no_paths_no_cwd_uses_active_workspace( // No saved thread, no cwd – should fall back to the active workspace. sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(acp::SessionId::new(Arc::from("no-context-session"))), @@ -4304,7 +4304,7 @@ async fn test_activate_archived_thread_saved_paths_opens_new_workspace(cx: &mut ); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(session_id.clone()), @@ -4359,7 +4359,7 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window(cx: &m let session_id = acp::SessionId::new(Arc::from("archived-cross-window")); sidebar.update_in(cx_a, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(session_id.clone()), @@ -4439,7 +4439,7 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window_with_t let session_id = acp::SessionId::new(Arc::from("archived-cross-window-with-sidebar")); sidebar_a.update_in(cx_a, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(session_id.clone()), @@ -4522,7 +4522,7 @@ async fn test_activate_archived_thread_prefers_current_window_for_matching_paths let session_id = acp::SessionId::new(Arc::from("archived-current-window")); sidebar_a.update_in(cx_a, |sidebar, window, cx| { - sidebar.activate_archived_thread( + sidebar.open_thread_from_archive( ThreadMetadata { thread_id: ThreadId::new(), session_id: Some(session_id.clone()), @@ -5330,7 +5330,7 @@ async fn test_restore_worktree_thread_uses_main_repo_project_group_key(cx: &mut // provisional ProjectGroupKey to find a matching workspace. let metadata = cx.update(|_window, cx| store.read(cx).entry(thread_id).unwrap().clone()); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); @@ -6151,7 +6151,7 @@ async fn test_unarchive_only_shows_restored_thread(cx: &mut TestAppContext) { // Unarchive it — the draft should be replaced by the restored thread. sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); @@ -6241,7 +6241,7 @@ async fn test_unarchive_first_thread_in_group_does_not_create_spurious_draft( }); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); @@ -6325,7 +6325,7 @@ async fn test_unarchive_into_new_workspace_does_not_create_duplicate_real_thread }); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); @@ -6467,7 +6467,7 @@ async fn test_unarchive_into_existing_workspace_replaces_draft(cx: &mut TestAppC }); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); @@ -6552,7 +6552,7 @@ async fn test_unarchive_into_inactive_existing_workspace_does_not_leave_active_d }); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); let panel_b_before_settle = workspace_b.read_with(cx, |workspace, cx| { @@ -6693,7 +6693,7 @@ async fn test_unarchive_after_removing_parent_project_group_restores_real_thread ); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(archived_metadata.clone(), window, cx); + sidebar.open_thread_from_archive(archived_metadata.clone(), window, cx); }); cx.run_until_parked(); @@ -6805,7 +6805,7 @@ async fn test_unarchive_does_not_create_duplicate_real_thread_metadata(cx: &mut }); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); @@ -7396,7 +7396,7 @@ async fn test_unarchive_linked_worktree_thread_into_project_group_shows_only_res }); sidebar.update_in(cx, |sidebar, window, cx| { - sidebar.activate_archived_thread(metadata, window, cx); + sidebar.open_thread_from_archive(metadata, window, cx); }); cx.run_until_parked(); diff --git a/crates/ui/src/components/ai/thread_item.rs b/crates/ui/src/components/ai/thread_item.rs index 7440c8391190d302f629e044bc3fbf187f6baf2f..159e5bb48422a5102ee5ad3e9edde54007be62ec 100644 --- a/crates/ui/src/components/ai/thread_item.rs +++ b/crates/ui/src/components/ai/thread_item.rs @@ -1,4 +1,7 @@ -use crate::{CommonAnimationExt, DiffStat, GradientFade, HighlightedLabel, Tooltip, prelude::*}; +use crate::{ + CommonAnimationExt, DecoratedIcon, DiffStat, GradientFade, HighlightedLabel, IconDecoration, + Tooltip, prelude::*, +}; use gpui::{ Animation, AnimationExt, AnyView, ClickEvent, Hsla, MouseButton, SharedString, @@ -39,6 +42,7 @@ pub struct ThreadItem { icon_color: Option, icon_visible: bool, custom_icon_from_external_svg: Option, + icon_decoration: Option, title: SharedString, title_label_color: Option, title_generating: bool, @@ -71,6 +75,7 @@ impl ThreadItem { icon_color: None, icon_visible: true, custom_icon_from_external_svg: None, + icon_decoration: None, title: title.into(), title_label_color: None, title_generating: false, @@ -117,6 +122,11 @@ impl ThreadItem { self } + pub fn icon_decoration(mut self, decoration: IconDecoration) -> Self { + self.icon_decoration = Some(decoration); + self + } + pub fn custom_icon_from_external_svg(mut self, svg: impl Into) -> Self { self.custom_icon_from_external_svg = Some(svg.into()); self @@ -326,6 +336,10 @@ impl RenderOnce for ThreadItem { icon.tooltip(Tooltip::text(tooltip)) }) .into_any_element() + } else if let Some(decoration) = self.icon_decoration { + icon_container() + .child(DecoratedIcon::new(agent_icon, Some(decoration))) + .into_any_element() } else { icon_container().child(agent_icon).into_any_element() }; @@ -553,35 +567,41 @@ impl RenderOnce for ThreadItem { .min_w_0() .gap_1p5() .child(icon_container()) // Icon Spacing - .child( - h_flex() - .min_w_0() - .flex_shrink() - .overflow_hidden() - .gap_1p5() - .when_some(self.project_name, |this, name| { - this.child( - Label::new(name) - .size(LabelSize::Small) - .color(Color::Muted), - ) - }) - .when( - has_project_name && (has_project_paths || has_worktree), - |this| this.child(dot_separator()), + .when( + has_project_name || has_project_paths || has_worktree, + |this| { + this.child( + h_flex() + .min_w_0() + .flex_shrink() + .overflow_hidden() + .gap_1p5() + .when_some(self.project_name, |this, name| { + this.child( + Label::new(name) + .size(LabelSize::Small) + .color(Color::Muted), + ) + }) + .when( + has_project_name + && (has_project_paths || has_worktree), + |this| this.child(dot_separator()), + ) + .when_some(project_paths, |this, paths| { + this.child( + Label::new(paths) + .size(LabelSize::Small) + .color(Color::Muted) + .into_any_element(), + ) + }) + .when(has_project_paths && has_worktree, |this| { + this.child(dot_separator()) + }) + .children(worktree_labels), ) - .when_some(project_paths, |this, paths| { - this.child( - Label::new(paths) - .size(LabelSize::Small) - .color(Color::Muted) - .into_any_element(), - ) - }) - .when(has_project_paths && has_worktree, |this| { - this.child(dot_separator()) - }) - .children(worktree_labels), + }, ) .when( (has_project_name || has_project_paths || has_worktree) diff --git a/crates/ui/src/components/icon/icon_decoration.rs b/crates/ui/src/components/icon/icon_decoration.rs index 423f6d73a68e8ee3aea550129e2a6220a8a699a6..4515b8fa44f50d21ba5ca4274689324fbbde2bb8 100644 --- a/crates/ui/src/components/icon/icon_decoration.rs +++ b/crates/ui/src/components/icon/icon_decoration.rs @@ -18,6 +18,8 @@ pub enum KnockoutIconName { DotBg, TriangleFg, TriangleBg, + ArchiveFg, + ArchiveBg, } impl KnockoutIconName { @@ -33,6 +35,7 @@ pub enum IconDecorationKind { X, Dot, Triangle, + Archive, } impl IconDecorationKind { @@ -41,6 +44,7 @@ impl IconDecorationKind { Self::X => KnockoutIconName::XFg, Self::Dot => KnockoutIconName::DotFg, Self::Triangle => KnockoutIconName::TriangleFg, + Self::Archive => KnockoutIconName::ArchiveFg, } } @@ -49,6 +53,7 @@ impl IconDecorationKind { Self::X => KnockoutIconName::XBg, Self::Dot => KnockoutIconName::DotBg, Self::Triangle => KnockoutIconName::TriangleBg, + Self::Archive => KnockoutIconName::ArchiveBg, } } } @@ -163,7 +168,7 @@ impl RenderOnce for IconDecoration { .absolute() .bottom(self.position.y) .right(self.position.x) - .child(foreground) .child(background) + .child(foreground) } }