Skip to content

Commit 8aa5361

Browse files
agent_ui: Add support for deleting thread history (zed-industries#43370)
This PR adds support for deleting your entire thread history. This is inspired by a Zed user from the meetup in Amsterdam, he was missing this feature. **Demo** https://github.com/user-attachments/assets/5a195007-1094-4ec6-902a-1b83db5ec508 Release Notes: - AI: Add support for deleting your entire thread history --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
1 parent 6a311ca commit 8aa5361

File tree

5 files changed

+127
-11
lines changed

5 files changed

+127
-11
lines changed

crates/agent/src/db.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,4 +424,20 @@ impl ThreadsDatabase {
424424
Ok(())
425425
})
426426
}
427+
428+
pub fn delete_threads(&self) -> Task<Result<()>> {
429+
let connection = self.connection.clone();
430+
431+
self.executor.spawn(async move {
432+
let connection = connection.lock();
433+
434+
let mut delete = connection.exec_bound::<()>(indoc! {"
435+
DELETE FROM threads
436+
"})?;
437+
438+
delete(())?;
439+
440+
Ok(())
441+
})
442+
}
427443
}

crates/agent/src/history_store.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,15 @@ impl HistoryStore {
188188
})
189189
}
190190

191+
pub fn delete_threads(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
192+
let database_future = ThreadsDatabase::connect(cx);
193+
cx.spawn(async move |this, cx| {
194+
let database = database_future.await.map_err(|err| anyhow!(err))?;
195+
database.delete_threads().await?;
196+
this.update(cx, |this, cx| this.reload(cx))
197+
})
198+
}
199+
191200
pub fn delete_text_thread(
192201
&mut self,
193202
path: Arc<Path>,

crates/agent_ui/src/acp/thread_history.rs

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::acp::AcpThreadView;
2-
use crate::{AgentPanel, RemoveSelectedThread};
2+
use crate::{AgentPanel, RemoveHistory, RemoveSelectedThread};
33
use agent::{HistoryEntry, HistoryStore};
44
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta};
55
use editor::{Editor, EditorEvent};
@@ -12,7 +12,7 @@ use std::{fmt::Display, ops::Range};
1212
use text::Bias;
1313
use time::{OffsetDateTime, UtcOffset};
1414
use ui::{
15-
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tooltip, WithScrollbar,
15+
HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Tab, Tooltip, WithScrollbar,
1616
prelude::*,
1717
};
1818

@@ -25,6 +25,7 @@ pub struct AcpThreadHistory {
2525
search_query: SharedString,
2626
visible_items: Vec<ListItemType>,
2727
local_timezone: UtcOffset,
28+
confirming_delete_history: bool,
2829
_update_task: Task<()>,
2930
_subscriptions: Vec<gpui::Subscription>,
3031
}
@@ -98,6 +99,7 @@ impl AcpThreadHistory {
9899
)
99100
.unwrap(),
100101
search_query: SharedString::default(),
102+
confirming_delete_history: false,
101103
_subscriptions: vec![search_editor_subscription, history_store_subscription],
102104
_update_task: Task::ready(()),
103105
};
@@ -331,6 +333,24 @@ impl AcpThreadHistory {
331333
task.detach_and_log_err(cx);
332334
}
333335

336+
fn remove_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
337+
self.history_store.update(cx, |store, cx| {
338+
store.delete_threads(cx).detach_and_log_err(cx)
339+
});
340+
self.confirming_delete_history = false;
341+
cx.notify();
342+
}
343+
344+
fn prompt_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
345+
self.confirming_delete_history = true;
346+
cx.notify();
347+
}
348+
349+
fn cancel_delete_history(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
350+
self.confirming_delete_history = false;
351+
cx.notify();
352+
}
353+
334354
fn render_list_items(
335355
&mut self,
336356
range: Range<usize>,
@@ -447,6 +467,8 @@ impl Focusable for AcpThreadHistory {
447467

448468
impl Render for AcpThreadHistory {
449469
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
470+
let has_no_history = self.history_store.read(cx).is_empty(cx);
471+
450472
v_flex()
451473
.key_context("ThreadHistory")
452474
.size_full()
@@ -457,9 +479,12 @@ impl Render for AcpThreadHistory {
457479
.on_action(cx.listener(Self::select_last))
458480
.on_action(cx.listener(Self::confirm))
459481
.on_action(cx.listener(Self::remove_selected_thread))
482+
.on_action(cx.listener(|this, _: &RemoveHistory, window, cx| {
483+
this.remove_history(window, cx);
484+
}))
460485
.child(
461486
h_flex()
462-
.h(px(41.)) // Match the toolbar perfectly
487+
.h(Tab::container_height(cx))
463488
.w_full()
464489
.py_1()
465490
.px_2()
@@ -481,7 +506,7 @@ impl Render for AcpThreadHistory {
481506
.overflow_hidden()
482507
.flex_grow();
483508

484-
if self.history_store.read(cx).is_empty(cx) {
509+
if has_no_history {
485510
view.justify_center().items_center().child(
486511
Label::new("You don't have any past threads yet.")
487512
.size(LabelSize::Small)
@@ -512,6 +537,68 @@ impl Render for AcpThreadHistory {
512537
)
513538
}
514539
})
540+
.when(!has_no_history, |this| {
541+
this.child(
542+
h_flex()
543+
.p_2()
544+
.border_t_1()
545+
.border_color(cx.theme().colors().border_variant)
546+
.when(!self.confirming_delete_history, |this| {
547+
this.child(
548+
Button::new("delete_history", "Delete All History")
549+
.full_width()
550+
.style(ButtonStyle::Outlined)
551+
.label_size(LabelSize::Small)
552+
.on_click(cx.listener(|this, _, window, cx| {
553+
this.prompt_delete_history(window, cx);
554+
})),
555+
)
556+
})
557+
.when(self.confirming_delete_history, |this| {
558+
this.w_full()
559+
.gap_2()
560+
.flex_wrap()
561+
.justify_between()
562+
.child(
563+
h_flex()
564+
.flex_wrap()
565+
.gap_1()
566+
.child(
567+
Label::new("Delete all threads?")
568+
.size(LabelSize::Small),
569+
)
570+
.child(
571+
Label::new("You won't be able to recover them later.")
572+
.size(LabelSize::Small)
573+
.color(Color::Muted),
574+
),
575+
)
576+
.child(
577+
h_flex()
578+
.gap_1()
579+
.child(
580+
Button::new("cancel_delete", "Cancel")
581+
.label_size(LabelSize::Small)
582+
.on_click(cx.listener(|this, _, window, cx| {
583+
this.cancel_delete_history(window, cx);
584+
})),
585+
)
586+
.child(
587+
Button::new("confirm_delete", "Delete")
588+
.style(ButtonStyle::Tinted(ui::TintColor::Error))
589+
.color(Color::Error)
590+
.label_size(LabelSize::Small)
591+
.on_click(cx.listener(|_, _, window, cx| {
592+
window.dispatch_action(
593+
Box::new(RemoveHistory),
594+
cx,
595+
);
596+
})),
597+
),
598+
)
599+
}),
600+
)
601+
})
515602
}
516603
}
517604

crates/agent_ui/src/agent_panel.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
2020
use crate::ManageProfiles;
2121
use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal};
2222
use crate::{
23-
AddContextServer, AgentDiffPane, DeleteRecentlyOpenThread, Follow, InlineAssistant,
24-
NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory,
25-
ResetTrialEndUpsell, ResetTrialUpsell, ToggleNavigationMenu, ToggleNewThreadMenu,
26-
ToggleOptionsMenu,
23+
AddContextServer, AgentDiffPane, Follow, InlineAssistant, NewTextThread, NewThread,
24+
OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell, ResetTrialUpsell,
25+
ToggleNavigationMenu, ToggleNewThreadMenu, ToggleOptionsMenu,
2726
acp::AcpThreadView,
2827
agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
2928
slash_command::SlashCommandCompletionProvider,
@@ -614,11 +613,14 @@ impl AgentPanel {
614613
if let Some(panel) = panel.upgrade() {
615614
menu = Self::populate_recently_opened_menu_section(menu, panel, cx);
616615
}
617-
menu.action("View All", Box::new(OpenHistory))
618-
.end_slot_action(DeleteRecentlyOpenThread.boxed_clone())
616+
617+
menu = menu
618+
.action("View All", Box::new(OpenHistory))
619619
.fixed_width(px(320.).into())
620620
.keep_open_on_confirm(false)
621-
.key_context("NavigationMenu")
621+
.key_context("NavigationMenu");
622+
623+
menu
622624
});
623625
weak_panel
624626
.update(cx, |panel, cx| {

crates/agent_ui/src/agent_ui.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ actions!(
6969
CycleModeSelector,
7070
/// Expands the message editor to full size.
7171
ExpandMessageEditor,
72+
/// Removes all thread history.
73+
RemoveHistory,
7274
/// Opens the conversation history view.
7375
OpenHistory,
7476
/// Adds a context server to the configuration.

0 commit comments

Comments
 (0)