diff --git a/crates/agent_ui/src/conversation_view.rs b/crates/agent_ui/src/conversation_view.rs index 8a8b7803078aa7c5886251fede5f19155155f4ba..b703d00c4b333de257bf224835a63453ab897440 100644 --- a/crates/agent_ui/src/conversation_view.rs +++ b/crates/agent_ui/src/conversation_view.rs @@ -75,7 +75,7 @@ use crate::agent_diff::AgentDiff; use crate::entry_view_state::{EntryViewEvent, ViewEvent}; use crate::message_editor::{MessageEditor, MessageEditorEvent}; use crate::profile_selector::{ProfileProvider, ProfileSelector}; -use crate::thread_metadata_store::ThreadMetadataStore; +use crate::thread_metadata_store::SidebarThreadMetadataStore; use crate::ui::{AgentNotification, AgentNotificationEvent}; use crate::{ Agent, AgentDiffPane, AgentInitialContent, AgentPanel, AllowAlways, AllowOnce, @@ -2615,7 +2615,7 @@ impl ConversationView { let task = history.update(cx, |history, cx| history.delete_session(&session_id, cx)); task.detach_and_log_err(cx); - if let Some(store) = ThreadMetadataStore::try_global(cx) { + if let Some(store) = SidebarThreadMetadataStore::try_global(cx) { store .update(cx, |store, cx| store.delete(session_id.clone(), cx)) .detach_and_log_err(cx); @@ -4291,7 +4291,7 @@ pub(crate) mod tests { cx.update(|cx| { let settings_store = SettingsStore::test(cx); cx.set_global(settings_store); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); theme::init(theme::LoadThemes::JustBase, cx); editor::init(cx); agent_panel::init(cx); diff --git a/crates/agent_ui/src/thread_metadata_store.rs b/crates/agent_ui/src/thread_metadata_store.rs index c3b5e9818b497b5b190abdd714d9ff8d5bb3d2a6..fdc466d9bfc6130f21e6fe040db654d06fae11f9 100644 --- a/crates/agent_ui/src/thread_metadata_store.rs +++ b/crates/agent_ui/src/thread_metadata_store.rs @@ -20,7 +20,7 @@ use ui::{App, Context, SharedString}; use workspace::PathList; pub fn init(cx: &mut App) { - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); if cx.has_flag::() { migrate_thread_metadata(cx); @@ -37,7 +37,7 @@ pub fn init(cx: &mut App) { /// /// TODO: Remove this after N weeks of shipping the sidebar fn migrate_thread_metadata(cx: &mut App) { - ThreadMetadataStore::global(cx).update(cx, |store, cx| { + SidebarThreadMetadataStore::global(cx).update(cx, |store, cx| { let list = store.list(cx); cx.spawn(async move |this, cx| { let Ok(list) = list.await else { @@ -68,7 +68,7 @@ fn migrate_thread_metadata(cx: &mut App) { }); } -struct GlobalThreadMetadataStore(Entity); +struct GlobalThreadMetadataStore(Entity); impl Global for GlobalThreadMetadataStore {} /// Lightweight metadata for any thread (native or ACP), enough to populate @@ -140,12 +140,16 @@ impl ThreadMetadata { } } -pub struct ThreadMetadataStore { +/// The store holds all metadata needed to show threads in the sidebar. +/// Effectively, all threads stored in here are "non-archived". +/// +/// Automatically listens to AcpThread events and updates metadata if it has changed. +pub struct SidebarThreadMetadataStore { db: ThreadMetadataDb, session_subscriptions: HashMap, } -impl ThreadMetadataStore { +impl SidebarThreadMetadataStore { #[cfg(not(any(test, feature = "test-support")))] pub fn init_global(cx: &mut App) { if cx.has_global::() { @@ -290,7 +294,7 @@ impl ThreadMetadataStore { } } -impl Global for ThreadMetadataStore {} +impl Global for SidebarThreadMetadataStore {} struct ThreadMetadataDb(ThreadSafeConnection); @@ -468,12 +472,12 @@ mod tests { async fn test_migrate_thread_metadata(cx: &mut TestAppContext) { cx.update(|cx| { ThreadStore::init_global(cx); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); }); // Verify the list is empty before migration let metadata_list = cx.update(|cx| { - let store = ThreadMetadataStore::global(cx); + let store = SidebarThreadMetadataStore::global(cx); store.read(cx).list(cx) }); @@ -520,7 +524,7 @@ mod tests { // Verify the metadata was migrated let metadata_list = cx.update(|cx| { - let store = ThreadMetadataStore::global(cx); + let store = SidebarThreadMetadataStore::global(cx); store.read(cx).list(cx) }); @@ -546,7 +550,7 @@ mod tests { async fn test_migrate_thread_metadata_skips_when_data_exists(cx: &mut TestAppContext) { cx.update(|cx| { ThreadStore::init_global(cx); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); }); // Pre-populate the metadata store with existing data @@ -560,7 +564,7 @@ mod tests { }; cx.update(|cx| { - let store = ThreadMetadataStore::global(cx); + let store = SidebarThreadMetadataStore::global(cx); store.update(cx, |store, cx| { store.save(existing_metadata, cx).detach(); }); @@ -592,7 +596,7 @@ mod tests { // Verify only the existing metadata is present (migration was skipped) let metadata_list = cx.update(|cx| { - let store = ThreadMetadataStore::global(cx); + let store = SidebarThreadMetadataStore::global(cx); store.read(cx).list(cx) }); @@ -608,7 +612,7 @@ mod tests { cx.set_global(settings_store); cx.update_flags(true, vec!["agent-v2".to_string()]); ThreadStore::init_global(cx); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); }); let fs = FakeFs::new(cx.executor()); @@ -666,7 +670,7 @@ mod tests { // List all metadata from the store. let metadata_list = cx.update(|cx| { - let store = ThreadMetadataStore::global(cx); + let store = SidebarThreadMetadataStore::global(cx); store.read(cx).list(cx) }); diff --git a/crates/agent_ui/src/threads_archive_view.rs b/crates/agent_ui/src/threads_archive_view.rs index 5300eea3080071d53d654f87dc90debddcfcad16..7786486b08f2a4e44f7abc0d77f51851192c8593 100644 --- a/crates/agent_ui/src/threads_archive_view.rs +++ b/crates/agent_ui/src/threads_archive_view.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::{ Agent, RemoveSelectedThread, agent_connection_store::AgentConnectionStore, - thread_history::ThreadHistory, thread_metadata_store::ThreadMetadataStore, + thread_history::ThreadHistory, thread_metadata_store::SidebarThreadMetadataStore, }; use acp_thread::AgentSessionInfo; use agent::ThreadStore; @@ -247,9 +247,7 @@ impl ThreadsArchiveView { let today = Local::now().naive_local().date(); self._update_items_task.take(); - let unarchived_ids_task = ThreadMetadataStore::global(cx) - .read(cx) - .list_sidebar_ids(cx); + let unarchived_ids_task = SidebarThreadMetadataStore::global(cx).read(cx).list_ids(cx); self._update_items_task = Some(cx.spawn(async move |this, cx| { let unarchived_session_ids = unarchived_ids_task.await.unwrap_or_default(); diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index 6d0eaea56a749ebe22033bc22eb4978fbd5649d1..f90f94ae7b0e5698e203cd58b8597208e8ab689d 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -1,7 +1,7 @@ use acp_thread::ThreadStatus; use action_log::DiffStats; use agent_client_protocol::{self as acp}; -use agent_ui::thread_metadata_store::{ThreadMetadata, ThreadMetadataStore}; +use agent_ui::thread_metadata_store::{SidebarThreadMetadataStore, ThreadMetadata}; use agent_ui::threads_archive_view::{ ThreadsArchiveView, ThreadsArchiveViewEvent, format_history_entry_timestamp, }; @@ -300,9 +300,12 @@ impl Sidebar { }) .detach(); - cx.observe(&ThreadMetadataStore::global(cx), |this, _store, cx| { - this.list_threads(cx); - }) + cx.observe( + &SidebarThreadMetadataStore::global(cx), + |this, _store, cx| { + this.list_threads(cx); + }, + ) .detach(); cx.observe_flag::(window, |_is_enabled, this, _window, cx| { @@ -1006,7 +1009,7 @@ impl Sidebar { } fn list_threads(&mut self, cx: &mut Context) { - let list_task = ThreadMetadataStore::global(cx).read(cx).list(cx); + let list_task = SidebarThreadMetadataStore::global(cx).read(cx).list(cx); self._list_threads_task = Some(cx.spawn(async move |this, cx| { let Some(thread_entries) = list_task.await.log_err() else { return; @@ -1835,7 +1838,7 @@ impl Sidebar { cx: &mut Context, ) { // Eagerly save thread metadata so that the sidebar is updated immediately - ThreadMetadataStore::global(cx) + SidebarThreadMetadataStore::global(cx) .update(cx, |store, cx| { store.save( ThreadMetadata::from_session_info(agent.id(), &session_info), @@ -2107,7 +2110,7 @@ impl Sidebar { } } - ThreadMetadataStore::global(cx) + SidebarThreadMetadataStore::global(cx) .update(cx, |store, cx| store.delete(session_id.clone(), cx)) .detach_and_log_err(cx); } @@ -2840,7 +2843,7 @@ mod tests { editor::init(cx); cx.update_flags(false, vec!["agent-v2".into()]); ThreadStore::init_global(cx); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); language_model::LanguageModelRegistry::test(cx); prompt_store::init(cx); }); @@ -2944,7 +2947,7 @@ mod tests { folder_paths: path_list, }; let task = cx.update(|cx| { - ThreadMetadataStore::global(cx).update(cx, |store, cx| store.save(metadata, cx)) + SidebarThreadMetadataStore::global(cx).update(cx, |store, cx| store.save(metadata, cx)) }); task.await.unwrap(); } @@ -3901,7 +3904,7 @@ mod tests { cx.update(|cx| { cx.update_flags(false, vec!["agent-v2".into()]); ThreadStore::init_global(cx); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); language_model::LanguageModelRegistry::test(cx); prompt_store::init(cx); }); @@ -5200,7 +5203,7 @@ mod tests { cx.update(|cx| { cx.update_flags(false, vec!["agent-v2".into()]); ThreadStore::init_global(cx); - ThreadMetadataStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); language_model::LanguageModelRegistry::test(cx); prompt_store::init(cx); });