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)
}
}