diff --git a/crates/agent_ui/src/sidebar.rs b/crates/agent_ui/src/sidebar.rs index aed642ccc9987569fb3681ab93bb2c8fe6de2674..2586e3691278095ee177745e13c01b6e3d531145 100644 --- a/crates/agent_ui/src/sidebar.rs +++ b/crates/agent_ui/src/sidebar.rs @@ -243,6 +243,7 @@ pub struct Sidebar { selection: Option, focused_thread: Option, active_entry_index: Option, + hovered_thread_index: Option, collapsed_groups: HashSet, expanded_groups: HashMap, view: SidebarView, @@ -345,6 +346,7 @@ impl Sidebar { selection: None, focused_thread: None, active_entry_index: None, + hovered_thread_index: None, collapsed_groups: HashSet::new(), expanded_groups: HashMap::new(), view: SidebarView::default(), @@ -1582,11 +1584,23 @@ impl Sidebar { } } + fn delete_thread(&mut self, session_id: &acp::SessionId, cx: &mut Context) { + let Some(thread_store) = ThreadStore::try_global(cx) else { + return; + }; + self.hovered_thread_index = None; + thread_store.update(cx, |store, cx| { + store + .delete_thread(session_id.clone(), cx) + .detach_and_log_err(cx); + }); + } + fn render_thread( &self, ix: usize, thread: &ThreadEntry, - is_selected: bool, + is_focused: bool, docked_right: bool, cx: &mut Context, ) -> AnyElement { @@ -1602,6 +1616,11 @@ impl Sidebar { let session_info = thread.session_info.clone(); let thread_workspace = thread.workspace.clone(); + let is_hovered = self.hovered_thread_index == Some(ix); + let is_selected = self.focused_thread.as_ref() == Some(&session_info.session_id); + let can_delete = thread.agent == Agent::NativeAgent; + let session_id_for_delete = thread.session_info.session_id.clone(); + let id = SharedString::from(format!("thread-entry-{}", ix)); let timestamp = thread @@ -1648,9 +1667,32 @@ impl Sidebar { .when(thread.diff_stats.lines_removed > 0, |this| { this.removed(thread.diff_stats.lines_removed as usize) }) - .selected(self.focused_thread.as_ref() == Some(&session_info.session_id)) - .focused(is_selected) + .selected(is_selected) + .focused(is_focused) .docked_right(docked_right) + .hovered(is_hovered) + .on_hover(cx.listener(move |this, is_hovered: &bool, _window, cx| { + if *is_hovered { + this.hovered_thread_index = Some(ix); + } else if this.hovered_thread_index == Some(ix) { + this.hovered_thread_index = None; + } + cx.notify(); + })) + .when((is_hovered || is_selected) && can_delete, |this| { + this.action_slot( + IconButton::new("delete-thread", IconName::Trash) + .icon_size(IconSize::Small) + .icon_color(Color::Muted) + .tooltip(Tooltip::text("Delete Thread")) + .on_click({ + let session_id = session_id_for_delete.clone(); + cx.listener(move |this, _, _window, cx| { + this.delete_thread(&session_id, cx); + }) + }), + ) + }) .on_click({ let agent = thread.agent.clone(); cx.listener(move |this, _, window, cx| { diff --git a/crates/ui/src/components/ai/thread_item.rs b/crates/ui/src/components/ai/thread_item.rs index 35aa3487a39c69795545b646666840743cfd8526..51ed3d4e01b5dd469a29d3b969a10be2f3d88c12 100644 --- a/crates/ui/src/components/ai/thread_item.rs +++ b/crates/ui/src/components/ai/thread_item.rs @@ -283,8 +283,20 @@ impl RenderOnce for ThreadItem { .when_some(self.tooltip, |this, tooltip| this.tooltip(tooltip)), ) .child(gradient_overlay) - .when(self.hovered, |this| { - this.when_some(self.action_slot, |this, slot| this.child(slot)) + .when(self.hovered || self.selected, |this| { + this.when_some(self.action_slot, |this, slot| { + let overlay = GradientFade::new( + base_bg, + color.element_hover, + color.element_active, + ) + .width(px(64.0)) + .right(px(6.)) + .gradient_stop(0.75) + .group_name("thread-item"); + + this.child(h_flex().relative().child(overlay).child(slot)) + }) }), ) .when_some(self.worktree, |this, worktree| { @@ -337,7 +349,7 @@ impl RenderOnce for ThreadItem { .when(has_diff_stats, |this| { this.child( DiffStat::new(diff_stat_id, added_count, removed_count) - .tooltip("Unreviewed changes"), + .tooltip("Unreviewed Changes"), ) }) .when(has_diff_stats && has_timestamp, |this| {