From 30b7178f6717301ece536b7653cc1e4ca17471de Mon Sep 17 00:00:00 2001 From: Bennet Bo Fenner Date: Mon, 16 Mar 2026 17:33:12 +0100 Subject: [PATCH] agent_ui: Rename `ConnectionView` to `ConversationView` (#51684) We now share connections across multiple "ConnectionView"s the naming does not make sense anymore Release Notes: - N/A --- crates/agent_ui/src/agent_panel.rs | 211 ++++--- crates/agent_ui/src/agent_ui.rs | 4 +- ...onnection_view.rs => conversation_view.rs} | 571 ++++++++++-------- .../thread_view.rs | 10 +- crates/agent_ui/src/message_editor.rs | 2 +- crates/agent_ui/src/sidebar.rs | 2 +- crates/agent_ui/src/test_support.rs | 2 +- crates/agent_ui/src/thread_history_view.rs | 20 +- crates/zed/src/visual_test_runner.rs | 4 +- 9 files changed, 454 insertions(+), 372 deletions(-) rename crates/agent_ui/src/{connection_view.rs => conversation_view.rs} (92%) rename crates/agent_ui/src/{connection_view => conversation_view}/thread_view.rs (99%) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index ccf9e481ef48095de76587c915962eef458a77e1..403c11c00b24f9de7c8c1b56a3c8ac02a3bdb77f 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -31,12 +31,12 @@ use zed_actions::agent::{ use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal, HoldForDefault}; use crate::{ - AddContextServer, AgentDiffPane, ConnectionView, CopyThreadToClipboard, CycleStartThreadIn, + AddContextServer, AgentDiffPane, ConversationView, CopyThreadToClipboard, CycleStartThreadIn, Follow, InlineAssistant, LoadThreadFromClipboard, NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell, ResetTrialUpsell, StartThreadIn, ToggleNavigationMenu, ToggleNewThreadMenu, ToggleOptionsMenu, agent_configuration::{AgentConfiguration, AssistantConfigurationEvent}, - connection_view::{AcpThreadViewEvent, ThreadView}, + conversation_view::{AcpThreadViewEvent, ThreadView}, slash_command::SlashCommandCompletionProvider, text_thread_editor::{AgentPanelDelegate, TextThreadEditor, make_lsp_adapter_delegate}, ui::EndTrialUpsell, @@ -275,9 +275,9 @@ pub fn init(cx: &mut App) { .register_action(|workspace, _: &OpenAgentDiff, window, cx| { let thread = workspace .panel::(cx) - .and_then(|panel| panel.read(cx).active_connection_view().cloned()) - .and_then(|thread_view| { - thread_view + .and_then(|panel| panel.read(cx).active_conversation().cloned()) + .and_then(|conversation| { + conversation .read(cx) .active_thread() .map(|r| r.read(cx).thread.clone()) @@ -624,7 +624,7 @@ enum History { enum ActiveView { Uninitialized, AgentThread { - server_view: Entity, + conversation_view: Entity, }, TextThread { text_thread_editor: Entity, @@ -878,7 +878,7 @@ pub struct AgentPanel { focus_handle: FocusHandle, active_view: ActiveView, previous_view: Option, - background_threads: HashMap>, + background_threads: HashMap>, new_thread_menu_handle: PopoverMenuHandle, start_thread_in_menu_handle: PopoverMenuHandle, agent_panel_menu_handle: PopoverMenuHandle, @@ -1334,9 +1334,11 @@ impl AgentPanel { .unwrap_or(false) } - pub fn active_connection_view(&self) -> Option<&Entity> { + pub fn active_conversation(&self) -> Option<&Entity> { match &self.active_view { - ActiveView::AgentThread { server_view, .. } => Some(server_view), + ActiveView::AgentThread { + conversation_view, .. + } => Some(conversation_view), ActiveView::Uninitialized | ActiveView::TextThread { .. } | ActiveView::History { .. } @@ -1556,11 +1558,11 @@ impl AgentPanel { } fn expand_message_editor(&mut self, window: &mut Window, cx: &mut Context) { - let Some(thread_view) = self.active_connection_view() else { + let Some(conversation_view) = self.active_conversation() else { return; }; - let Some(active_thread) = thread_view.read(cx).active_thread().cloned() else { + let Some(active_thread) = conversation_view.read(cx).active_thread().cloned() else { return; }; @@ -1886,8 +1888,8 @@ impl AgentPanel { cx: &mut Context, ) { if let Some(workspace) = self.workspace.upgrade() - && let Some(thread_view) = self.active_connection_view() - && let Some(active_thread) = thread_view.read(cx).active_thread().cloned() + && let Some(conversation_view) = self.active_conversation() + && let Some(active_thread) = conversation_view.read(cx).active_thread().cloned() { active_thread.update(cx, |thread, cx| { thread @@ -2077,21 +2079,23 @@ impl AgentPanel { } } - pub fn as_active_server_view(&self) -> Option<&Entity> { + pub fn active_conversation_view(&self) -> Option<&Entity> { match &self.active_view { - ActiveView::AgentThread { server_view } => Some(server_view), + ActiveView::AgentThread { conversation_view } => Some(conversation_view), _ => None, } } - pub fn as_active_thread_view(&self, cx: &App) -> Option> { - let server_view = self.as_active_server_view()?; + pub fn active_thread_view(&self, cx: &App) -> Option> { + let server_view = self.active_conversation_view()?; server_view.read(cx).active_thread().cloned() } pub fn active_agent_thread(&self, cx: &App) -> Option> { match &self.active_view { - ActiveView::AgentThread { server_view, .. } => server_view + ActiveView::AgentThread { + conversation_view, .. + } => conversation_view .read(cx) .active_thread() .map(|r| r.read(cx).thread.clone()), @@ -2109,7 +2113,7 @@ impl AgentPanel { pub fn parent_threads(&self, cx: &App) -> Vec> { let mut views = Vec::new(); - if let Some(server_view) = self.as_active_server_view() { + if let Some(server_view) = self.active_conversation_view() { if let Some(thread_view) = server_view.read(cx).parent_thread(cx) { views.push(thread_view); } @@ -2125,11 +2129,11 @@ impl AgentPanel { } fn retain_running_thread(&mut self, old_view: ActiveView, cx: &mut Context) { - let ActiveView::AgentThread { server_view } = old_view else { + let ActiveView::AgentThread { conversation_view } = old_view else { return; }; - let Some(thread_view) = server_view.read(cx).parent_thread(cx) else { + let Some(thread_view) = conversation_view.read(cx).parent_thread(cx) else { return; }; @@ -2143,14 +2147,15 @@ impl AgentPanel { return; } - self.background_threads.insert(session_id, server_view); + self.background_threads + .insert(session_id, conversation_view); } pub(crate) fn active_native_agent_thread(&self, cx: &App) -> Option> { match &self.active_view { - ActiveView::AgentThread { server_view, .. } => { - server_view.read(cx).as_native_thread(cx) - } + ActiveView::AgentThread { + conversation_view, .. + } => conversation_view.read(cx).as_native_thread(cx), _ => None, } } @@ -2206,24 +2211,26 @@ impl AgentPanel { // Re-subscribe whenever the ConnectionView changes, since the inner // ThreadView may have been replaced (e.g. navigating between threads). self._active_view_observation = match &self.active_view { - ActiveView::AgentThread { server_view } => { + ActiveView::AgentThread { conversation_view } => { self._thread_view_subscription = - Self::subscribe_to_active_thread_view(server_view, window, cx); - let focus_handle = server_view.focus_handle(cx); + Self::subscribe_to_active_thread_view(conversation_view, window, cx); + let focus_handle = conversation_view.focus_handle(cx); self._active_thread_focus_subscription = Some(cx.on_focus_in(&focus_handle, window, |_this, _window, cx| { cx.emit(AgentPanelEvent::ThreadFocused); cx.notify(); })); - Some( - cx.observe_in(server_view, window, |this, server_view, window, cx| { + Some(cx.observe_in( + conversation_view, + window, + |this, server_view, window, cx| { this._thread_view_subscription = Self::subscribe_to_active_thread_view(&server_view, window, cx); cx.emit(AgentPanelEvent::ActiveViewChanged); this.serialize(cx); cx.notify(); - }), - ) + }, + )) } _ => { self._thread_view_subscription = None; @@ -2347,7 +2354,7 @@ impl AgentPanel { } fn subscribe_to_active_thread_view( - server_view: &Entity, + server_view: &Entity, window: &mut Window, cx: &mut Context, ) -> Option { @@ -2521,13 +2528,18 @@ impl AgentPanel { window: &mut Window, cx: &mut Context, ) { - if let Some(server_view) = self.background_threads.remove(&session_id) { - self.set_active_view(ActiveView::AgentThread { server_view }, focus, window, cx); + if let Some(conversation_view) = self.background_threads.remove(&session_id) { + self.set_active_view( + ActiveView::AgentThread { conversation_view }, + focus, + window, + cx, + ); return; } - if let ActiveView::AgentThread { server_view } = &self.active_view { - if server_view + if let ActiveView::AgentThread { conversation_view } = &self.active_view { + if conversation_view .read(cx) .active_thread() .map(|t| t.read(cx).id.clone()) @@ -2538,8 +2550,8 @@ impl AgentPanel { } } - if let Some(ActiveView::AgentThread { server_view }) = &self.previous_view { - if server_view + if let Some(ActiveView::AgentThread { conversation_view }) = &self.previous_view { + if conversation_view .read(cx) .active_thread() .map(|t| t.read(cx).id.clone()) @@ -2590,8 +2602,8 @@ impl AgentPanel { let connection_store = self.connection_store.clone(); - let server_view = cx.new(|cx| { - crate::ConnectionView::new( + let conversation_view = cx.new(|cx| { + crate::ConversationView::new( server, connection_store, ext_agent, @@ -2608,9 +2620,9 @@ impl AgentPanel { ) }); - cx.observe(&server_view, |this, server_view, cx| { + cx.observe(&conversation_view, |this, server_view, cx| { let is_active = this - .as_active_server_view() + .active_conversation_view() .is_some_and(|active| active.entity_id() == server_view.entity_id()); if is_active { cx.emit(AgentPanelEvent::ActiveViewChanged); @@ -2622,7 +2634,12 @@ impl AgentPanel { }) .detach(); - self.set_active_view(ActiveView::AgentThread { server_view }, focus, window, cx); + self.set_active_view( + ActiveView::AgentThread { conversation_view }, + focus, + window, + cx, + ); } fn active_thread_has_messages(&self, cx: &App) -> bool { @@ -3140,7 +3157,9 @@ impl Focusable for AgentPanel { fn focus_handle(&self, cx: &App) -> FocusHandle { match &self.active_view { ActiveView::Uninitialized => self.focus_handle.clone(), - ActiveView::AgentThread { server_view, .. } => server_view.focus_handle(cx), + ActiveView::AgentThread { + conversation_view, .. + } => conversation_view.focus_handle(cx), ActiveView::History { history: kind } => match kind { History::AgentThreads { view } => view.read(cx).focus_handle(cx), History::TextThreads => self.text_thread_history.focus_handle(cx), @@ -3269,8 +3288,8 @@ impl AgentPanel { const LOADING_SUMMARY_PLACEHOLDER: &str = "Loading Summary…"; let content = match &self.active_view { - ActiveView::AgentThread { server_view } => { - let server_view_ref = server_view.read(cx); + ActiveView::AgentThread { conversation_view } => { + let server_view_ref = conversation_view.read(cx); let is_generating_title = server_view_ref.as_native_thread(cx).is_some() && server_view_ref.parent_thread(cx).map_or(false, |tv| { tv.read(cx).thread.read(cx).has_provisional_title() @@ -3296,18 +3315,18 @@ impl AgentPanel { div() .w_full() .on_action({ - let thread_view = server_view.downgrade(); + let conversation_view = conversation_view.downgrade(); move |_: &menu::Confirm, window, cx| { - if let Some(thread_view) = thread_view.upgrade() { - thread_view.focus_handle(cx).focus(window, cx); + if let Some(conversation_view) = conversation_view.upgrade() { + conversation_view.focus_handle(cx).focus(window, cx); } } }) .on_action({ - let thread_view = server_view.downgrade(); + let conversation_view = conversation_view.downgrade(); move |_: &editor::actions::Cancel, window, cx| { - if let Some(thread_view) = thread_view.upgrade() { - thread_view.focus_handle(cx).focus(window, cx); + if let Some(conversation_view) = conversation_view.upgrade() { + conversation_view.focus_handle(cx).focus(window, cx); } } }) @@ -3315,7 +3334,7 @@ impl AgentPanel { .into_any_element() } } else { - Label::new(server_view.read(cx).title(cx)) + Label::new(conversation_view.read(cx).title(cx)) .color(Color::Muted) .truncate() .into_any_element() @@ -3400,9 +3419,9 @@ impl AgentPanel { .into_any() } - fn handle_regenerate_thread_title(thread_view: Entity, cx: &mut App) { - thread_view.update(cx, |thread_view, cx| { - if let Some(thread) = thread_view.as_native_thread(cx) { + fn handle_regenerate_thread_title(conversation_view: Entity, cx: &mut App) { + conversation_view.update(cx, |conversation_view, cx| { + if let Some(thread) = conversation_view.as_native_thread(cx) { thread.update(cx, |thread, cx| { thread.generate_title(cx); }); @@ -3450,18 +3469,20 @@ impl AgentPanel { _ => false, }; - let thread_view = match &self.active_view { - ActiveView::AgentThread { server_view } => Some(server_view.clone()), + let conversation_view = match &self.active_view { + ActiveView::AgentThread { conversation_view } => Some(conversation_view.clone()), _ => None, }; let thread_with_messages = match &self.active_view { - ActiveView::AgentThread { server_view } => { - server_view.read(cx).has_user_submitted_prompt(cx) + ActiveView::AgentThread { conversation_view } => { + conversation_view.read(cx).has_user_submitted_prompt(cx) } _ => false, }; let has_auth_methods = match &self.active_view { - ActiveView::AgentThread { server_view } => server_view.read(cx).has_auth_methods(), + ActiveView::AgentThread { conversation_view } => { + conversation_view.read(cx).has_auth_methods() + } _ => false, }; @@ -3505,13 +3526,13 @@ impl AgentPanel { .separator(); } - if let Some(thread_view) = thread_view.as_ref() { + if let Some(conversation_view) = conversation_view.as_ref() { menu = menu .entry("Regenerate Thread Title", None, { - let thread_view = thread_view.clone(); + let conversation_view = conversation_view.clone(); move |_, cx| { Self::handle_regenerate_thread_title( - thread_view.clone(), + conversation_view.clone(), cx, ); } @@ -3888,7 +3909,9 @@ impl AgentPanel { }; let active_thread = match &self.active_view { - ActiveView::AgentThread { server_view } => server_view.read(cx).as_native_thread(cx), + ActiveView::AgentThread { conversation_view } => { + conversation_view.read(cx).as_native_thread(cx) + } ActiveView::Uninitialized | ActiveView::TextThread { .. } | ActiveView::History { .. } @@ -4208,7 +4231,7 @@ impl AgentPanel { }; let is_thread_loading = self - .active_connection_view() + .active_conversation() .map(|thread| thread.read(cx).is_loading()) .unwrap_or(false); @@ -4533,13 +4556,11 @@ impl AgentPanel { ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => { false } - ActiveView::AgentThread { server_view, .. } - if server_view.read(cx).as_native_thread(cx).is_none() => - { - false - } - ActiveView::AgentThread { server_view } => { - let history_is_empty = server_view + ActiveView::AgentThread { + conversation_view, .. + } if conversation_view.read(cx).as_native_thread(cx).is_none() => false, + ActiveView::AgentThread { conversation_view } => { + let history_is_empty = conversation_view .read(cx) .history() .is_none_or(|h| h.read(cx).is_empty()); @@ -4804,9 +4825,9 @@ impl AgentPanel { cx: &mut Context, ) { match &self.active_view { - ActiveView::AgentThread { server_view } => { - server_view.update(cx, |thread_view, cx| { - thread_view.insert_dragged_files(paths, added_worktrees, window, cx); + ActiveView::AgentThread { conversation_view } => { + conversation_view.update(cx, |conversation_view, cx| { + conversation_view.insert_dragged_files(paths, added_worktrees, window, cx); }); } ActiveView::TextThread { @@ -4905,8 +4926,10 @@ impl Render for AgentPanel { .on_action(cx.listener(Self::reset_font_size)) .on_action(cx.listener(Self::toggle_zoom)) .on_action(cx.listener(|this, _: &ReauthenticateAgent, window, cx| { - if let Some(thread_view) = this.active_connection_view() { - thread_view.update(cx, |thread_view, cx| thread_view.reauthenticate(window, cx)) + if let Some(conversation_view) = this.active_conversation() { + conversation_view.update(cx, |conversation_view, cx| { + conversation_view.reauthenticate(window, cx) + }) } })) .child(self.render_toolbar(window, cx)) @@ -4924,8 +4947,10 @@ impl Render for AgentPanel { match &self.active_view { ActiveView::Uninitialized => parent, - ActiveView::AgentThread { server_view, .. } => parent - .child(server_view.clone()) + ActiveView::AgentThread { + conversation_view, .. + } => parent + .child(conversation_view.clone()) .child(self.render_drag_target(cx)), ActiveView::History { history: kind } => match kind { History::AgentThreads { view } => parent.child(view.clone()), @@ -5130,9 +5155,9 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate { // Wait to create a new context until the workspace is no longer // being updated. cx.defer_in(window, move |panel, window, cx| { - if let Some(thread_view) = panel.active_connection_view() { - thread_view.update(cx, |thread_view, cx| { - thread_view.insert_selections(window, cx); + if let Some(conversation_view) = panel.active_conversation() { + conversation_view.update(cx, |conversation_view, cx| { + conversation_view.insert_selections(window, cx); }); } else if let Some(text_thread_editor) = panel.active_text_thread_editor() { let snapshot = buffer.read(cx).snapshot(cx); @@ -5168,9 +5193,9 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate { // Wait to create a new context until the workspace is no longer // being updated. cx.defer_in(window, move |panel, window, cx| { - if let Some(thread_view) = panel.active_connection_view() { - thread_view.update(cx, |thread_view, cx| { - thread_view.insert_terminal_text(text, window, cx); + if let Some(conversation_view) = panel.active_conversation() { + conversation_view.update(cx, |conversation_view, cx| { + conversation_view.insert_terminal_text(text, window, cx); }); } else if let Some(text_thread_editor) = panel.active_text_thread_editor() { text_thread_editor.update(cx, |text_thread_editor, cx| { @@ -5233,8 +5258,8 @@ impl AgentPanel { /// /// This is a test-only accessor that exposes the private `active_thread_view()` /// method for test assertions. Not compiled into production builds. - pub fn active_thread_view_for_tests(&self) -> Option<&Entity> { - self.active_connection_view() + pub fn active_thread_view_for_tests(&self) -> Option<&Entity> { + self.active_conversation() } /// Sets the start_thread_in value directly, bypassing validation. @@ -5296,7 +5321,7 @@ impl AgentPanel { #[cfg(test)] mod tests { use super::*; - use crate::connection_view::tests::{StubAgentServer, init_test}; + use crate::conversation_view::tests::{StubAgentServer, init_test}; use crate::test_support::{active_session_id, open_thread_with_connection, send_message}; use acp_thread::{StubAgentConnection, ThreadStatus}; use assistant_text_thread::TextThreadStore; @@ -5419,7 +5444,7 @@ mod tests { "workspace A agent type should be restored" ); assert!( - panel.active_connection_view().is_some(), + panel.active_conversation().is_some(), "workspace A should have its active thread restored" ); }); @@ -5439,7 +5464,7 @@ mod tests { "workspace B agent type should be restored" ); assert!( - panel.active_connection_view().is_none(), + panel.active_conversation().is_none(), "workspace B should have no active thread" ); }); @@ -5856,7 +5881,7 @@ mod tests { send_message(&panel, &mut cx); let weak_view_a = panel.read_with(&cx, |panel, _cx| { - panel.active_connection_view().unwrap().downgrade() + panel.active_conversation().unwrap().downgrade() }); // Thread A should be idle (auto-completed via set_next_prompt_updates). diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index e0ab9a707b7e7ab5fed6a0a27d4c253c08445dfa..dde70f15e8084144d9beb1d4fb9563cf12fb942e 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -8,9 +8,9 @@ mod branch_names; mod buffer_codegen; mod completion_provider; mod config_options; -pub(crate) mod connection_view; mod context; mod context_server_configuration; +pub(crate) mod conversation_view; mod entry_view_state; mod external_source_prompt; mod favorite_models; @@ -71,7 +71,7 @@ pub use crate::agent_panel::{ use crate::agent_registry_ui::AgentRegistryPage; pub use crate::inline_assistant::InlineAssistant; pub use agent_diff::{AgentDiffPane, AgentDiffToolbar}; -pub(crate) use connection_view::ConnectionView; +pub(crate) use conversation_view::ConversationView; pub use external_source_prompt::ExternalSourcePrompt; pub(crate) use mode_selector::ModeSelector; pub(crate) use model_selector::ModelSelector; diff --git a/crates/agent_ui/src/connection_view.rs b/crates/agent_ui/src/conversation_view.rs similarity index 92% rename from crates/agent_ui/src/connection_view.rs rename to crates/agent_ui/src/conversation_view.rs index 42d4fe4e4ef6b69e53d951e0007e564d5cc614a4..c9a6c7334d22cc3159c4fc6dde02fe57c8676ae9 100644 --- a/crates/agent_ui/src/connection_view.rs +++ b/crates/agent_ui/src/conversation_view.rs @@ -306,9 +306,9 @@ pub enum AcpServerViewEvent { ActiveThreadChanged, } -impl EventEmitter for ConnectionView {} +impl EventEmitter for ConversationView {} -pub struct ConnectionView { +pub struct ConversationView { agent: Rc, connection_store: Entity, connection_key: Agent, @@ -325,7 +325,7 @@ pub struct ConnectionView { _subscriptions: Vec, } -impl ConnectionView { +impl ConversationView { pub fn has_auth_methods(&self) -> bool { self.as_connected().map_or(false, |connected| { !connected.connection.auth_methods().is_empty() @@ -478,7 +478,7 @@ impl ConnectedServerState { } } -impl ConnectionView { +impl ConversationView { pub fn new( agent: Rc, connection_store: Entity, @@ -2313,7 +2313,7 @@ impl ConnectionView { fn render_markdown(&self, markdown: Entity, style: MarkdownStyle) -> MarkdownElement { let workspace = self.workspace.clone(); MarkdownElement::new(markdown, style).on_url_click(move |text, window, cx| { - crate::connection_view::thread_view::open_link(text, &workspace, window, cx); + crate::conversation_view::thread_view::open_link(text, &workspace, window, cx); }) } @@ -2643,7 +2643,7 @@ fn placeholder_text(agent_name: &str, has_commands: bool) -> String { } } -impl Focusable for ConnectionView { +impl Focusable for ConversationView { fn focus_handle(&self, cx: &App) -> FocusHandle { match self.active_thread() { Some(thread) => thread.read(cx).focus_handle(cx), @@ -2653,7 +2653,7 @@ impl Focusable for ConnectionView { } #[cfg(any(test, feature = "test-support"))] -impl ConnectionView { +impl ConversationView { /// Expands a tool call so its content is visible. /// This is primarily useful for visual testing. pub fn expand_tool_call(&mut self, tool_call_id: acp::ToolCallId, cx: &mut Context) { @@ -2666,7 +2666,7 @@ impl ConnectionView { } } -impl Render for ConnectionView { +impl Render for ConversationView { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { self.sync_queued_message_editors(window, cx); let v2_flag = cx.has_flag::(); @@ -2786,9 +2786,10 @@ pub(crate) mod tests { async fn test_drop(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, _cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; - let weak_view = thread_view.downgrade(); - drop(thread_view); + let (conversation_view, _cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; + let weak_view = conversation_view.downgrade(); + drop(conversation_view); assert!(!weak_view.is_upgradable()); } @@ -2801,14 +2802,14 @@ pub(crate) mod tests { }; let initial_content = AgentInitialContent::FromExternalSource(prompt); - let (thread_view, cx) = setup_thread_view_with_initial_content( + let (conversation_view, cx) = setup_conversation_view_with_initial_content( StubAgentServer::default_response(), initial_content, cx, ) .await; - active_thread(&thread_view, cx).read_with(cx, |view, cx| { + active_thread(&conversation_view, cx).read_with(cx, |view, cx| { assert!(view.show_external_source_prompt_warning); assert_eq!(view.thread.read(cx).entries().len(), 0); assert_eq!(view.message_editor.read(cx).text(cx), "Write me a script"); @@ -2824,17 +2825,18 @@ pub(crate) mod tests { }; let initial_content = AgentInitialContent::FromExternalSource(prompt); - let (thread_view, cx) = setup_thread_view_with_initial_content( + let (conversation_view, cx) = setup_conversation_view_with_initial_content( StubAgentServer::default_response(), initial_content, cx, ) .await; - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); - active_thread(&thread_view, cx).read_with(cx, |view, cx| { + active_thread(&conversation_view, cx).read_with(cx, |view, cx| { assert!(!view.show_external_source_prompt_warning); assert_eq!(view.message_editor.read(cx).text(cx), ""); assert_eq!(view.thread.read(cx).entries().len(), 2); @@ -2845,16 +2847,18 @@ pub(crate) mod tests { async fn test_notification_for_stop_event(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); cx.deactivate_window(); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -2869,17 +2873,18 @@ pub(crate) mod tests { async fn test_notification_for_error(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(SaboteurAgentConnection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(SaboteurAgentConnection), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); cx.deactivate_window(); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -2907,9 +2912,9 @@ pub(crate) mod tests { let connection_store = cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx))); - let thread_view = cx.update(|window, cx| { + let conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(StubAgentServer::default_response()), connection_store, Agent::Custom { id: "Test".into() }, @@ -2931,7 +2936,7 @@ pub(crate) mod tests { cx.run_until_parked(); let history = cx.update(|_window, cx| { - thread_view + conversation_view .read(cx) .history() .expect("Missing history") @@ -2939,7 +2944,7 @@ pub(crate) mod tests { }); // Initially empty because StubAgentConnection.session_list() returns None - active_thread(&thread_view, cx).read_with(cx, |view, _cx| { + active_thread(&conversation_view, cx).read_with(cx, |view, _cx| { assert_eq!(view.recent_history_entries.len(), 0); }); @@ -2951,7 +2956,7 @@ pub(crate) mod tests { }); cx.run_until_parked(); - active_thread(&thread_view, cx).read_with(cx, |view, _cx| { + active_thread(&conversation_view, cx).read_with(cx, |view, _cx| { assert_eq!(view.recent_history_entries.len(), 1); assert_eq!( view.recent_history_entries[0].session_id, @@ -2967,7 +2972,7 @@ pub(crate) mod tests { }); cx.run_until_parked(); - active_thread(&thread_view, cx).read_with(cx, |view, _cx| { + active_thread(&conversation_view, cx).read_with(cx, |view, _cx| { assert_eq!(view.recent_history_entries.len(), 1); assert_eq!( view.recent_history_entries[0].session_id, @@ -2981,7 +2986,7 @@ pub(crate) mod tests { init_test(cx); let session = AgentSessionInfo::new(SessionId::new("history-session")); - let (thread_view, history, cx) = setup_thread_view_with_history( + let (conversation_view, history, cx) = setup_thread_view_with_history( StubAgentServer::new(SessionHistoryConnection::new(vec![session.clone()])), cx, ) @@ -2994,7 +2999,7 @@ pub(crate) mod tests { ); }); - active_thread(&thread_view, cx).read_with(cx, |view, _cx| { + active_thread(&conversation_view, cx).read_with(cx, |view, _cx| { assert_eq!(view.recent_history_entries.len(), 1); assert_eq!( view.recent_history_entries[0].session_id, @@ -3017,9 +3022,9 @@ pub(crate) mod tests { let connection_store = cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx))); - let thread_view = cx.update(|window, cx| { + let conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(StubAgentServer::new(ResumeOnlyAgentConnection)), connection_store, Agent::Custom { id: "Test".into() }, @@ -3039,7 +3044,7 @@ pub(crate) mod tests { cx.run_until_parked(); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { let state = view.active_thread().unwrap(); assert!(state.read(cx).resumed_without_history); assert_eq!(state.read(cx).list_state.item_count(), 0); @@ -3072,9 +3077,9 @@ pub(crate) mod tests { let connection_store = cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx))); - let _thread_view = cx.update(|window, cx| { + let _conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(StubAgentServer::new(connection)), connection_store, Agent::Custom { id: "Test".into() }, @@ -3105,20 +3110,21 @@ pub(crate) mod tests { async fn test_refusal_handling(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(RefusalAgentConnection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(RefusalAgentConnection), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Do something harmful", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Check that the refusal error is set - thread_view.read_with(cx, |thread_view, cx| { + conversation_view.read_with(cx, |thread_view, cx| { let state = thread_view.active_thread().unwrap(); assert!( matches!(state.read(cx).thread_error, Some(ThreadError::Refusal)), @@ -3131,9 +3137,9 @@ pub(crate) mod tests { async fn test_connect_failure_transitions_to_load_error(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(FailingAgentServer, cx).await; + let (conversation_view, cx) = setup_conversation_view(FailingAgentServer, cx).await; - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { let title = view.title(cx); assert_eq!( title.as_ref(), @@ -3167,11 +3173,12 @@ pub(crate) mod tests { init_test(cx); let connection = AuthGatedAgentConnection::new(); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; // When new_session returns AuthRequired, the server should transition // to Connected + Unauthenticated rather than getting stuck in Loading. - thread_view.read_with(cx, |view, _cx| { + conversation_view.read_with(cx, |view, _cx| { let connected = view .as_connected() .expect("Should be in Connected state even though auth is required"); @@ -3189,7 +3196,7 @@ pub(crate) mod tests { ); }); - thread_view.read_with(cx, |view, _cx| { + conversation_view.read_with(cx, |view, _cx| { assert!( view.active_thread().is_none(), "active_thread() should be None when unauthenticated without a session" @@ -3199,7 +3206,7 @@ pub(crate) mod tests { // Authenticate using the real authenticate flow on ConnectionView. // This calls connection.authenticate(), which flips the internal flag, // then on success triggers reset() -> new_session() which now succeeds. - thread_view.update_in(cx, |view, window, cx| { + conversation_view.update_in(cx, |view, window, cx| { view.authenticate( acp::AuthMethodId::new(AuthGatedAgentConnection::AUTH_METHOD_ID), window, @@ -3209,7 +3216,7 @@ pub(crate) mod tests { cx.run_until_parked(); // After auth, the server should have an active thread in the Ok state. - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { let connected = view .as_connected() .expect("Should still be in Connected state after auth"); @@ -3254,16 +3261,18 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); cx.deactivate_window(); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -3278,11 +3287,12 @@ pub(crate) mod tests { async fn test_notification_when_panel_hidden(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; - add_to_workspace(thread_view.clone(), cx); + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); @@ -3292,7 +3302,8 @@ pub(crate) mod tests { // Note: In the test environment, the panel is not actually added to the dock, // so is_agent_panel_hidden will return true - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -3309,9 +3320,10 @@ pub(crate) mod tests { async fn test_notification_still_works_when_window_inactive(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); @@ -3319,7 +3331,8 @@ pub(crate) mod tests { // Deactivate window - should show notification regardless of setting cx.deactivate_window(); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -3387,9 +3400,9 @@ pub(crate) mod tests { cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project1.clone(), cx))); let agent = StubAgentServer::default_response(); - let thread_view = cx.update(|window, cx| { + let conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(agent), connection_store, Agent::Custom { id: "Test".into() }, @@ -3408,7 +3421,7 @@ pub(crate) mod tests { }); cx.run_until_parked(); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); @@ -3435,7 +3448,8 @@ pub(crate) mod tests { // Window is active, agent panel is visible in workspace1, but workspace1 // is in the background. The notification should show because the user // can't actually see the agent panel. - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -3482,16 +3496,18 @@ pub(crate) mod tests { ); }); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); // Window is active - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -3508,18 +3524,20 @@ pub(crate) mod tests { async fn test_notification_closed_when_thread_view_dropped(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; - let weak_view = thread_view.downgrade(); + let weak_view = conversation_view.downgrade(); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); cx.deactivate_window(); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -3532,7 +3550,7 @@ pub(crate) mod tests { ); // Drop the thread view (simulating navigation to a new thread) - drop(thread_view); + drop(conversation_view); drop(message_editor); // Trigger an update to flush effects, which will call release_dropped_entities cx.update(|_window, _cx| {}); @@ -3553,45 +3571,49 @@ pub(crate) mod tests { ); } - async fn setup_thread_view( + async fn setup_conversation_view( agent: impl AgentServer + 'static, cx: &mut TestAppContext, - ) -> (Entity, &mut VisualTestContext) { - let (thread_view, _history, cx) = - setup_thread_view_with_history_and_initial_content(agent, None, cx).await; - (thread_view, cx) + ) -> (Entity, &mut VisualTestContext) { + let (conversation_view, _history, cx) = + setup_conversation_view_with_history_and_initial_content(agent, None, cx).await; + (conversation_view, cx) } async fn setup_thread_view_with_history( agent: impl AgentServer + 'static, cx: &mut TestAppContext, ) -> ( - Entity, + Entity, Entity, &mut VisualTestContext, ) { - let (thread_view, history, cx) = - setup_thread_view_with_history_and_initial_content(agent, None, cx).await; - (thread_view, history.expect("Missing history"), cx) + let (conversation_view, history, cx) = + setup_conversation_view_with_history_and_initial_content(agent, None, cx).await; + (conversation_view, history.expect("Missing history"), cx) } - async fn setup_thread_view_with_initial_content( + async fn setup_conversation_view_with_initial_content( agent: impl AgentServer + 'static, initial_content: AgentInitialContent, cx: &mut TestAppContext, - ) -> (Entity, &mut VisualTestContext) { - let (thread_view, _history, cx) = - setup_thread_view_with_history_and_initial_content(agent, Some(initial_content), cx) - .await; - (thread_view, cx) + ) -> (Entity, &mut VisualTestContext) { + let (conversation_view, _history, cx) = + setup_conversation_view_with_history_and_initial_content( + agent, + Some(initial_content), + cx, + ) + .await; + (conversation_view, cx) } - async fn setup_thread_view_with_history_and_initial_content( + async fn setup_conversation_view_with_history_and_initial_content( agent: impl AgentServer + 'static, initial_content: Option, cx: &mut TestAppContext, ) -> ( - Entity, + Entity, Option>, &mut VisualTestContext, ) { @@ -3607,9 +3629,9 @@ pub(crate) mod tests { let agent_key = Agent::Custom { id: "Test".into() }; - let thread_view = cx.update(|window, cx| { + let conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(agent), connection_store.clone(), agent_key.clone(), @@ -3635,16 +3657,17 @@ pub(crate) mod tests { .and_then(|e| e.read(cx).history().cloned()) }); - (thread_view, history, cx) + (conversation_view, history, cx) } - fn add_to_workspace(thread_view: Entity, cx: &mut VisualTestContext) { - let workspace = thread_view.read_with(cx, |thread_view, _cx| thread_view.workspace.clone()); + fn add_to_workspace(conversation_view: Entity, cx: &mut VisualTestContext) { + let workspace = + conversation_view.read_with(cx, |thread_view, _cx| thread_view.workspace.clone()); workspace .update_in(cx, |workspace, window, cx| { workspace.add_item_to_active_pane( - Box::new(cx.new(|_| ThreadViewItem(thread_view.clone()))), + Box::new(cx.new(|_| ThreadViewItem(conversation_view.clone()))), None, true, window, @@ -3654,7 +3677,7 @@ pub(crate) mod tests { .unwrap(); } - struct ThreadViewItem(Entity); + struct ThreadViewItem(Entity); impl Item for ThreadViewItem { type Event = (); @@ -4330,11 +4353,11 @@ pub(crate) mod tests { } fn active_thread( - thread_view: &Entity, + conversation_view: &Entity, cx: &TestAppContext, ) -> Entity { cx.read(|cx| { - thread_view + conversation_view .read(cx) .active_thread() .expect("No active thread") @@ -4343,10 +4366,10 @@ pub(crate) mod tests { } fn message_editor( - thread_view: &Entity, + conversation_view: &Entity, cx: &TestAppContext, ) -> Entity { - let thread = active_thread(thread_view, cx); + let thread = active_thread(conversation_view, cx); cx.read(|cx| thread.read(cx).message_editor.clone()) } @@ -4373,9 +4396,9 @@ pub(crate) mod tests { cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx))); let connection = Rc::new(StubAgentConnection::new()); - let thread_view = cx.update(|window, cx| { + let conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(StubAgentServer::new(connection.as_ref().clone())), connection_store, Agent::Custom { id: "Test".into() }, @@ -4395,7 +4418,7 @@ pub(crate) mod tests { cx.run_until_parked(); - let thread = thread_view + let thread = conversation_view .read_with(cx, |view, cx| { view.active_thread().map(|r| r.read(cx).thread.clone()) }) @@ -4421,7 +4444,7 @@ pub(crate) mod tests { assert_eq!(thread.entries().len(), 2); }); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { let entry_view_state = view .active_thread() .map(|active| active.read(cx).entry_view_state.clone()) @@ -4462,7 +4485,7 @@ pub(crate) mod tests { user_message.id.clone().unwrap() }); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { let entry_view_state = view .active_thread() .unwrap() @@ -4501,7 +4524,7 @@ pub(crate) mod tests { assert_eq!(thread.entries().len(), 2); }); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { let active = view.active_thread().unwrap(); active .read(cx) @@ -4534,10 +4557,10 @@ pub(crate) mod tests { acp::ContentChunk::new("Response 1".into()), )]); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; - let thread = thread_view + let thread = conversation_view .read_with(cx, |view, cx| { view.active_thread().map(|r| r.read(cx).thread.clone()) }) @@ -4560,12 +4583,12 @@ pub(crate) mod tests { cx.run_until_parked(); // Move somewhere else first so we're not trivially already on the last user prompt. - active_thread(&thread_view, cx).update(cx, |view, cx| { + active_thread(&conversation_view, cx).update(cx, |view, cx| { view.scroll_to_top(cx); }); cx.run_until_parked(); - active_thread(&thread_view, cx).update(cx, |view, cx| { + active_thread(&conversation_view, cx).update(cx, |view, cx| { view.scroll_to_most_recent_user_prompt(cx); let scroll_top = view.list_state.logical_scroll_top(); // Entries layout is: [User1, Assistant1, User2, Assistant2] @@ -4579,10 +4602,11 @@ pub(crate) mod tests { ) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; // With no entries, scrolling should be a no-op and must not panic. - active_thread(&thread_view, cx).update(cx, |view, cx| { + active_thread(&conversation_view, cx).update(cx, |view, cx| { view.scroll_to_most_recent_user_prompt(cx); let scroll_top = view.list_state.logical_scroll_top(); assert_eq!(scroll_top.item_ix, 0); @@ -4599,18 +4623,20 @@ pub(crate) mod tests { acp::ContentChunk::new("Response".into()), )]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Original message to edit", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); - let user_message_editor = thread_view.read_with(cx, |view, cx| { + let user_message_editor = conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4631,7 +4657,7 @@ pub(crate) mod tests { // Focus cx.focus(&user_message_editor); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4649,7 +4675,7 @@ pub(crate) mod tests { window.dispatch_action(Box::new(editor::actions::Cancel), cx); }); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4668,16 +4694,17 @@ pub(crate) mod tests { let connection = StubAgentConnection::new(); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("", window, cx); }); let thread = cx.read(|cx| { - thread_view + conversation_view .read(cx) .active_thread() .unwrap() @@ -4687,7 +4714,7 @@ pub(crate) mod tests { }); let entries_before = cx.read(|cx| thread.read(cx).entries().len()); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| { + active_thread(&conversation_view, cx).update_in(cx, |view, window, cx| { view.send(window, cx); }); cx.run_until_parked(); @@ -4709,19 +4736,20 @@ pub(crate) mod tests { acp::ContentChunk::new("Response".into()), )]); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Original message to edit", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); - let user_message_editor = thread_view.read_with(cx, |view, cx| { + let user_message_editor = conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4769,7 +4797,7 @@ pub(crate) mod tests { cx.run_until_parked(); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4812,19 +4840,20 @@ pub(crate) mod tests { let connection = StubAgentConnection::new(); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Original message to edit", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); - let (user_message_editor, session_id) = thread_view.read_with(cx, |view, cx| { + let (user_message_editor, session_id) = conversation_view.read_with(cx, |view, cx| { let thread = view.active_thread().unwrap().read(cx).thread.read(cx); assert_eq!(thread.entries().len(), 1); @@ -4846,7 +4875,7 @@ pub(crate) mod tests { // Focus cx.focus(&user_message_editor); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4859,7 +4888,7 @@ pub(crate) mod tests { editor.set_text("Edited message content", window, cx); }); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4877,7 +4906,7 @@ pub(crate) mod tests { connection.end_turn(session_id, acp::StopReason::EndTurn); }); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4891,7 +4920,7 @@ pub(crate) mod tests { cx.update(|window, cx| { assert!(user_message_editor.focus_handle(cx).is_focused(window)); assert_eq!( - thread_view + conversation_view .read(cx) .active_thread() .and_then(|active| active.read(cx).editing_message), @@ -4905,7 +4934,7 @@ pub(crate) mod tests { } struct GeneratingThreadSetup { - thread_view: Entity, + conversation_view: Entity, thread: Entity, message_editor: Entity, } @@ -4915,17 +4944,18 @@ pub(crate) mod tests { ) -> (GeneratingThreadSetup, &mut VisualTestContext) { let connection = StubAgentConnection::new(); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); - let (thread, session_id) = thread_view.read_with(cx, |view, cx| { + let (thread, session_id) = conversation_view.read_with(cx, |view, cx| { let thread = view .active_thread() .as_ref() @@ -4956,7 +4986,7 @@ pub(crate) mod tests { ( GeneratingThreadSetup { - thread_view, + conversation_view, thread, message_editor, }, @@ -4971,13 +5001,13 @@ pub(crate) mod tests { let (setup, cx) = setup_generating_thread(cx).await; let focus_handle = setup - .thread_view + .conversation_view .read_with(cx, |view, cx| view.focus_handle(cx)); cx.update(|window, cx| { window.focus(&focus_handle, cx); }); - setup.thread_view.update_in(cx, |_, window, cx| { + setup.conversation_view.update_in(cx, |_, window, cx| { window.dispatch_action(menu::Cancel.boxed_clone(), cx); }); @@ -5016,11 +5046,11 @@ pub(crate) mod tests { async fn test_escape_when_idle_is_noop(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(StubAgentConnection::new()), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(StubAgentConnection::new()), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let thread = thread_view.read_with(cx, |view, cx| { + let thread = conversation_view.read_with(cx, |view, cx| { view.active_thread().unwrap().read(cx).thread.clone() }); @@ -5028,12 +5058,12 @@ pub(crate) mod tests { assert_eq!(thread.status(), ThreadStatus::Idle); }); - let focus_handle = thread_view.read_with(cx, |view, _cx| view.focus_handle.clone()); + let focus_handle = conversation_view.read_with(cx, |view, _cx| view.focus_handle.clone()); cx.update(|window, cx| { window.focus(&focus_handle, cx); }); - thread_view.update_in(cx, |_, window, cx| { + conversation_view.update_in(cx, |_, window, cx| { window.dispatch_action(menu::Cancel.boxed_clone(), cx); }); @@ -5050,17 +5080,18 @@ pub(crate) mod tests { let connection = StubAgentConnection::new(); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Message 1", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); - let (thread, session_id) = thread_view.read_with(cx, |view, cx| { + let (thread, session_id) = conversation_view.read_with(cx, |view, cx| { let thread = view.active_thread().unwrap().read(cx).thread.clone(); (thread.clone(), thread.read(cx).session_id().clone()) @@ -5099,7 +5130,7 @@ pub(crate) mod tests { message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Message 2", window, cx); }); - active_thread(&thread_view, cx) + active_thread(&conversation_view, cx) .update_in(cx, |view, window, cx| view.interrupt_and_send(window, cx)); cx.update(|_, cx| { @@ -5181,18 +5212,20 @@ pub(crate) mod tests { acp::ContentChunk::new("Response".into()), )]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Original message to edit", window, cx) }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); - let user_message_editor = thread_view.read_with(cx, |thread_view, cx| { - thread_view + let user_message_editor = conversation_view.read_with(cx, |conversation_view, cx| { + conversation_view .active_thread() .map(|active| &active.read(cx).entry_view_state) .as_ref() @@ -5206,7 +5239,7 @@ pub(crate) mod tests { }); cx.focus(&user_message_editor); - thread_view.read_with(cx, |view, cx| { + conversation_view.read_with(cx, |view, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -5222,8 +5255,11 @@ pub(crate) mod tests { // Create a simple buffer with some text so we can create a selection // that will then be added to the message being edited. - let (workspace, project) = thread_view.read_with(cx, |thread_view, _cx| { - (thread_view.workspace.clone(), thread_view.project.clone()) + let (workspace, project) = conversation_view.read_with(cx, |conversation_view, _cx| { + ( + conversation_view.workspace.clone(), + conversation_view.project.clone(), + ) }); let buffer = project.update(cx, |project, cx| { project.create_local_buffer("let a = 10 + 10;", None, false, cx) @@ -5245,7 +5281,7 @@ pub(crate) mod tests { }) .unwrap(); - thread_view.update_in(cx, |view, window, cx| { + conversation_view.update_in(cx, |view, window, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -5271,18 +5307,22 @@ pub(crate) mod tests { acp::ContentChunk::new("Response".into()), )]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Can you review this snippet ", window, cx) }); // Create a simple buffer with some text so we can create a selection // that will then be added to the message being edited. - let (workspace, project) = thread_view.read_with(cx, |thread_view, _cx| { - (thread_view.workspace.clone(), thread_view.project.clone()) + let (workspace, project) = conversation_view.read_with(cx, |conversation_view, _cx| { + ( + conversation_view.workspace.clone(), + conversation_view.project.clone(), + ) }); let buffer = project.update(cx, |project, cx| { project.create_local_buffer("let a = 10 + 10;", None, false, cx) @@ -5304,7 +5344,7 @@ pub(crate) mod tests { }) .unwrap(); - thread_view.update_in(cx, |view, window, cx| { + conversation_view.update_in(cx, |view, window, cx| { assert_eq!( view.active_thread() .and_then(|active| active.read(cx).editing_message), @@ -5343,7 +5383,8 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; // Disable notifications to avoid popup windows cx.update(|_window, cx| { @@ -5356,18 +5397,19 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Run cargo build", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Verify the tool call is in WaitingForConfirmation state with the expected options - thread_view.read_with(cx, |thread_view, cx| { - let thread = thread_view + conversation_view.read_with(cx, |conversation_view, cx| { + let thread = conversation_view .active_thread() .expect("Thread should exist") .read(cx) @@ -5451,7 +5493,8 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; // Disable notifications cx.update(|_window, cx| { @@ -5464,18 +5507,19 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Edit the main file", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Verify the options - thread_view.read_with(cx, |thread_view, cx| { - let thread = thread_view + conversation_view.read_with(cx, |conversation_view, cx| { + let thread = conversation_view .active_thread() .expect("Thread should exist") .read(cx) @@ -5539,7 +5583,8 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; // Disable notifications cx.update(|_window, cx| { @@ -5552,18 +5597,19 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Fetch the docs", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Verify the options - thread_view.read_with(cx, |thread_view, cx| { - let thread = thread_view + conversation_view.read_with(cx, |conversation_view, cx| { + let thread = conversation_view .active_thread() .expect("Thread should exist") .read(cx) @@ -5630,7 +5676,8 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; // Disable notifications cx.update(|_window, cx| { @@ -5643,18 +5690,19 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Run the deploy script", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Verify only 2 options (no pattern button when command doesn't match pattern) - thread_view.read_with(cx, |thread_view, cx| { - let thread = thread_view + conversation_view.read_with(cx, |conversation_view, cx| { + let thread = conversation_view .active_thread() .expect("Thread should exist") .read(cx) @@ -5729,8 +5777,9 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); cx.update(|_window, cx| { AgentSettings::override_global( @@ -5742,18 +5791,19 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Run tests", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Verify tool call is waiting for confirmation - thread_view.read_with(cx, |thread_view, cx| { - let tool_call = thread_view.pending_tool_call(cx); + conversation_view.read_with(cx, |conversation_view, cx| { + let tool_call = conversation_view.pending_tool_call(cx); assert!( tool_call.is_some(), "Expected a tool call waiting for confirmation" @@ -5761,7 +5811,7 @@ pub(crate) mod tests { }); // Dispatch the AuthorizeToolCall action (simulating dropdown menu selection) - thread_view.update_in(cx, |_, window, cx| { + conversation_view.update_in(cx, |_, window, cx| { window.dispatch_action( crate::AuthorizeToolCall { tool_call_id: "action-test-1".to_string(), @@ -5776,8 +5826,8 @@ pub(crate) mod tests { cx.run_until_parked(); // Verify tool call is no longer waiting for confirmation (was authorized) - thread_view.read_with(cx, |thread_view, cx| { - let tool_call = thread_view.pending_tool_call(cx); + conversation_view.read_with(cx, |conversation_view, cx| { + let tool_call = conversation_view.pending_tool_call(cx); assert!( tool_call.is_none(), "Tool call should no longer be waiting for confirmation after AuthorizeToolCall action" @@ -5805,8 +5855,9 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); cx.update(|_window, cx| { AgentSettings::override_global( @@ -5818,12 +5869,13 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Install dependencies", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); @@ -5844,7 +5896,7 @@ pub(crate) mod tests { }; // Dispatch action with the pattern option (simulating "Always allow `npm` commands") - thread_view.update_in(cx, |_, window, cx| { + conversation_view.update_in(cx, |_, window, cx| { window.dispatch_action( crate::AuthorizeToolCall { tool_call_id: "pattern-action-test-1".to_string(), @@ -5859,8 +5911,8 @@ pub(crate) mod tests { cx.run_until_parked(); // Verify tool call was authorized - thread_view.read_with(cx, |thread_view, cx| { - let tool_call = thread_view.pending_tool_call(cx); + conversation_view.read_with(cx, |conversation_view, cx| { + let tool_call = conversation_view.pending_tool_call(cx); assert!( tool_call.is_none(), "Tool call should be authorized after selecting pattern option" @@ -5888,8 +5940,9 @@ pub(crate) mod tests { connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]); - let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection), cx).await; + add_to_workspace(conversation_view.clone(), cx); cx.update(|_window, cx| { AgentSettings::override_global( @@ -5901,26 +5954,27 @@ pub(crate) mod tests { ); }); - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Push changes", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); cx.run_until_parked(); // Use default granularity (last option = "Only this time") // Simulate clicking the Deny button - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| { + active_thread(&conversation_view, cx).update_in(cx, |view, window, cx| { view.reject_once(&RejectOnce, window, cx) }); cx.run_until_parked(); // Verify tool call was rejected (no longer waiting for confirmation) - thread_view.read_with(cx, |thread_view, cx| { - let tool_call = thread_view.pending_tool_call(cx); + conversation_view.read_with(cx, |conversation_view, cx| { + let tool_call = conversation_view.pending_tool_call(cx); assert!( tool_call.is_none(), "Tool call should be rejected after Deny" @@ -5986,10 +6040,11 @@ pub(crate) mod tests { async fn test_manually_editing_title_updates_acp_thread_title(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; - add_to_workspace(thread_view.clone(), cx); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; + add_to_workspace(conversation_view.clone(), cx); - let active = active_thread(&thread_view, cx); + let active = active_thread(&conversation_view, cx); let title_editor = cx.read(|cx| active.read(cx).title_editor.clone()); let thread = cx.read(|cx| active.read(cx).thread.clone()); @@ -5997,7 +6052,7 @@ pub(crate) mod tests { assert!(!editor.read_only(cx)); }); - cx.focus(&thread_view); + cx.focus(&conversation_view); cx.focus(&title_editor); cx.dispatch_action(editor::actions::DeleteLine); @@ -6017,10 +6072,10 @@ pub(crate) mod tests { async fn test_title_editor_is_read_only_when_set_title_unsupported(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(ResumeOnlyAgentConnection), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(ResumeOnlyAgentConnection), cx).await; - let active = active_thread(&thread_view, cx); + let active = active_thread(&conversation_view, cx); let title_editor = cx.read(|cx| active.read(cx).title_editor.clone()); title_editor.read_with(cx, |editor, cx| { @@ -6037,16 +6092,17 @@ pub(crate) mod tests { let connection = StubAgentConnection::new(); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; - let message_editor = message_editor(&thread_view, cx); + let message_editor = message_editor(&conversation_view, cx); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Some prompt", window, cx); }); - active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx)); + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); - let session_id = thread_view.read_with(cx, |view, cx| { + let session_id = conversation_view.read_with(cx, |view, cx| { view.active_thread() .unwrap() .read(cx) @@ -6064,8 +6120,8 @@ pub(crate) mod tests { cx.run_until_parked(); - thread_view.read_with(cx, |thread_view, cx| { - let state = thread_view.active_thread().unwrap(); + conversation_view.read_with(cx, |conversation_view, cx| { + let state = conversation_view.active_thread().unwrap(); let error = &state.read(cx).thread_error; match error { Some(ThreadError::Other { message, .. }) => { @@ -6356,11 +6412,11 @@ pub(crate) mod tests { async fn test_move_queued_message_to_empty_main_editor(cx: &mut TestAppContext) { init_test(cx); - let (connection_view, cx) = - setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; // Add a plain-text message to the queue directly. - active_thread(&connection_view, cx).update_in(cx, |thread, window, cx| { + active_thread(&conversation_view, cx).update_in(cx, |thread, window, cx| { thread.add_to_queue( vec![acp::ContentBlock::Text(acp::TextContent::new( "queued message".to_string(), @@ -6377,12 +6433,12 @@ pub(crate) mod tests { cx.run_until_parked(); // Queue should now be empty. - let queue_len = active_thread(&connection_view, cx) + let queue_len = active_thread(&conversation_view, cx) .read_with(cx, |thread, _cx| thread.local_queued_messages.len()); assert_eq!(queue_len, 0, "Queue should be empty after move"); // Main editor should contain the queued message text. - let text = message_editor(&connection_view, cx).update(cx, |editor, cx| editor.text(cx)); + let text = message_editor(&conversation_view, cx).update(cx, |editor, cx| editor.text(cx)); assert_eq!( text, "queued message", "Main editor should contain the moved queued message" @@ -6393,11 +6449,11 @@ pub(crate) mod tests { async fn test_move_queued_message_to_non_empty_main_editor(cx: &mut TestAppContext) { init_test(cx); - let (connection_view, cx) = - setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; // Seed the main editor with existing content. - message_editor(&connection_view, cx).update_in(cx, |editor, window, cx| { + message_editor(&conversation_view, cx).update_in(cx, |editor, window, cx| { editor.set_message( vec![acp::ContentBlock::Text(acp::TextContent::new( "existing content".to_string(), @@ -6408,7 +6464,7 @@ pub(crate) mod tests { }); // Add a plain-text message to the queue. - active_thread(&connection_view, cx).update_in(cx, |thread, window, cx| { + active_thread(&conversation_view, cx).update_in(cx, |thread, window, cx| { thread.add_to_queue( vec![acp::ContentBlock::Text(acp::TextContent::new( "queued message".to_string(), @@ -6422,12 +6478,12 @@ pub(crate) mod tests { cx.run_until_parked(); // Queue should now be empty. - let queue_len = active_thread(&connection_view, cx) + let queue_len = active_thread(&conversation_view, cx) .read_with(cx, |thread, _cx| thread.local_queued_messages.len()); assert_eq!(queue_len, 0, "Queue should be empty after move"); // Main editor should contain existing content + separator + queued content. - let text = message_editor(&connection_view, cx).update(cx, |editor, cx| editor.text(cx)); + let text = message_editor(&conversation_view, cx).update(cx, |editor, cx| editor.text(cx)); assert_eq!( text, "existing content\n\nqueued message", "Main editor should have existing content and queued message separated by two newlines" @@ -6449,9 +6505,9 @@ pub(crate) mod tests { cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx))); // StubAgentConnection defaults to supports_close_session() -> false - let thread_view = cx.update(|window, cx| { + let conversation_view = cx.update(|window, cx| { cx.new(|cx| { - ConnectionView::new( + ConversationView::new( Rc::new(StubAgentServer::default_response()), connection_store, Agent::Custom { id: "Test".into() }, @@ -6471,7 +6527,7 @@ pub(crate) mod tests { cx.run_until_parked(); - thread_view.read_with(cx, |view, _cx| { + conversation_view.read_with(cx, |view, _cx| { let connected = view.as_connected().expect("Should be connected"); assert!( !connected.threads.is_empty(), @@ -6483,7 +6539,7 @@ pub(crate) mod tests { ); }); - thread_view + conversation_view .update(cx, |view, cx| { view.as_connected() .expect("Should be connected") @@ -6496,12 +6552,12 @@ pub(crate) mod tests { async fn test_close_all_sessions_calls_close_when_supported(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = - setup_thread_view(StubAgentServer::new(CloseCapableConnection::new()), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(CloseCapableConnection::new()), cx).await; cx.run_until_parked(); - let close_capable = thread_view.read_with(cx, |view, _cx| { + let close_capable = conversation_view.read_with(cx, |view, _cx| { let connected = view.as_connected().expect("Should be connected"); assert!( !connected.threads.is_empty(), @@ -6519,7 +6575,7 @@ pub(crate) mod tests { .expect("Should be CloseCapableConnection") }); - thread_view + conversation_view .update(cx, |view, cx| { view.as_connected() .expect("Should be connected") @@ -6538,11 +6594,12 @@ pub(crate) mod tests { async fn test_close_session_returns_error_when_unsupported(cx: &mut TestAppContext) { init_test(cx); - let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await; + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::default_response(), cx).await; cx.run_until_parked(); - let result = thread_view + let result = conversation_view .update(cx, |view, cx| { let connected = view.as_connected().expect("Should be connected"); assert!( diff --git a/crates/agent_ui/src/connection_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs similarity index 99% rename from crates/agent_ui/src/connection_view/thread_view.rs rename to crates/agent_ui/src/conversation_view/thread_view.rs index ed2a062c0aed13c60c8ea15193bb598764a31806..3bee1499d983da06e345cae23c3deba5973b22e6 100644 --- a/crates/agent_ui/src/connection_view/thread_view.rs +++ b/crates/agent_ui/src/conversation_view/thread_view.rs @@ -167,7 +167,7 @@ pub struct ThreadView { pub parent_id: Option, pub thread: Entity, pub(crate) conversation: Entity, - pub server_view: WeakEntity, + pub server_view: WeakEntity, pub agent_icon: IconName, pub agent_icon_from_external_svg: Option, pub agent_id: AgentId, @@ -256,7 +256,7 @@ impl ThreadView { parent_id: Option, thread: Entity, conversation: Entity, - server_view: WeakEntity, + server_view: WeakEntity, agent_icon: IconName, agent_icon_from_external_svg: Option, agent_id: AgentId, @@ -882,7 +882,7 @@ impl ThreadView { let agent_id = self.agent_id.clone(); let server_view = self.server_view.clone(); move |window, cx| { - ConnectionView::handle_auth_required( + ConversationView::handle_auth_required( server_view.clone(), AuthRequired::new(), agent_id, @@ -1755,7 +1755,7 @@ impl ThreadView { pub fn sync_thread( &mut self, project: Entity, - server_view: Entity, + server_view: Entity, window: &mut Window, cx: &mut Context, ) { @@ -7318,7 +7318,7 @@ impl ThreadView { } let connection = this.thread.read(cx).connection().clone(); window.defer(cx, |window, cx| { - ConnectionView::handle_auth_required( + ConversationView::handle_auth_required( server_view, AuthRequired::new(), agent_name, diff --git a/crates/agent_ui/src/message_editor.rs b/crates/agent_ui/src/message_editor.rs index 6c62bd2f81ded87ae3b2aec4ac23473bc3324b8c..fd625db07b0c34cdf90a9913f574d38df32e97f8 100644 --- a/crates/agent_ui/src/message_editor.rs +++ b/crates/agent_ui/src/message_editor.rs @@ -1680,7 +1680,7 @@ mod tests { use crate::completion_provider::{PromptCompletionProviderDelegate, PromptContextType}; use crate::{ - connection_view::tests::init_test, + conversation_view::tests::init_test, message_editor::{Mention, MessageEditor, parse_mention_links}, }; diff --git a/crates/agent_ui/src/sidebar.rs b/crates/agent_ui/src/sidebar.rs index b2634a807fe0a0588536e822f2ec06c6d2099c09..87f4efeb0cf145f635b9af9d6923ec53a8e38ff5 100644 --- a/crates/agent_ui/src/sidebar.rs +++ b/crates/agent_ui/src/sidebar.rs @@ -486,7 +486,7 @@ impl Sidebar { self.focused_thread = active_workspace .as_ref() .and_then(|ws| ws.read(cx).panel::(cx)) - .and_then(|panel| panel.read(cx).active_connection_view().cloned()) + .and_then(|panel| panel.read(cx).active_conversation().cloned()) .and_then(|cv| cv.read(cx).parent_id(cx)); let mut threads_by_paths: HashMap> = HashMap::new(); diff --git a/crates/agent_ui/src/test_support.rs b/crates/agent_ui/src/test_support.rs index 7b986d045dddbf25fbe940a3b783c4f145781e8b..66c8c447a827e7f36c3098b4835026836ef8ccd8 100644 --- a/crates/agent_ui/src/test_support.rs +++ b/crates/agent_ui/src/test_support.rs @@ -82,7 +82,7 @@ pub fn open_thread_with_connection( } pub fn send_message(panel: &Entity, cx: &mut VisualTestContext) { - let thread_view = panel.read_with(cx, |panel, cx| panel.as_active_thread_view(cx).unwrap()); + let thread_view = panel.read_with(cx, |panel, cx| panel.active_thread_view(cx).unwrap()); let message_editor = thread_view.read_with(cx, |view, _cx| view.message_editor.clone()); message_editor.update_in(cx, |editor, window, cx| { editor.set_text("Hello", window, cx); diff --git a/crates/agent_ui/src/thread_history_view.rs b/crates/agent_ui/src/thread_history_view.rs index 6961f78884d2fb5fb95830d91dad940ca9dc48e9..bfb01d74d534934cbe731bd80403d04f4e454457 100644 --- a/crates/agent_ui/src/thread_history_view.rs +++ b/crates/agent_ui/src/thread_history_view.rs @@ -1,5 +1,5 @@ use crate::thread_history::ThreadHistory; -use crate::{AgentPanel, ConnectionView, RemoveHistory, RemoveSelectedThread}; +use crate::{AgentPanel, ConversationView, RemoveHistory, RemoveSelectedThread}; use acp_thread::AgentSessionInfo; use chrono::{Datelike as _, Local, NaiveDate, TimeDelta, Utc}; use editor::{Editor, EditorEvent}; @@ -640,7 +640,7 @@ impl Render for ThreadHistoryView { #[derive(IntoElement)] pub struct HistoryEntryElement { entry: AgentSessionInfo, - thread_view: WeakEntity, + conversation_view: WeakEntity, selected: bool, hovered: bool, supports_delete: bool, @@ -648,10 +648,10 @@ pub struct HistoryEntryElement { } impl HistoryEntryElement { - pub fn new(entry: AgentSessionInfo, thread_view: WeakEntity) -> Self { + pub fn new(entry: AgentSessionInfo, conversation_view: WeakEntity) -> Self { Self { entry, - thread_view, + conversation_view, selected: false, hovered: false, supports_delete: false, @@ -725,13 +725,13 @@ impl RenderOnce for HistoryEntryElement { Tooltip::for_action("Delete", &RemoveSelectedThread, cx) }) .on_click({ - let thread_view = self.thread_view.clone(); + let conversation_view = self.conversation_view.clone(); let session_id = self.entry.session_id.clone(); move |_event, _window, cx| { - if let Some(thread_view) = thread_view.upgrade() { - thread_view.update(cx, |thread_view, cx| { - thread_view.delete_history_entry(&session_id, cx); + if let Some(conversation_view) = conversation_view.upgrade() { + conversation_view.update(cx, |conversation_view, cx| { + conversation_view.delete_history_entry(&session_id, cx); }); } } @@ -741,11 +741,11 @@ impl RenderOnce for HistoryEntryElement { None }) .on_click({ - let thread_view = self.thread_view.clone(); + let conversation_view = self.conversation_view.clone(); let entry = self.entry; move |_event, window, cx| { - if let Some(workspace) = thread_view + if let Some(workspace) = conversation_view .upgrade() .and_then(|view| view.read(cx).workspace().upgrade()) { diff --git a/crates/zed/src/visual_test_runner.rs b/crates/zed/src/visual_test_runner.rs index 310632648053036162491a54d346eb5c98f13994..197a7c6003737c486bc6adfb7190a1f23dbcf94b 100644 --- a/crates/zed/src/visual_test_runner.rs +++ b/crates/zed/src/visual_test_runner.rs @@ -3459,7 +3459,7 @@ edition = "2021" // Insert a message into the active thread's message editor and submit. let thread_view = cx - .read(|cx| panel.read(cx).as_active_thread_view(cx)) + .read(|cx| panel.read(cx).active_thread_view(cx)) .ok_or_else(|| anyhow::anyhow!("No active thread view"))?; cx.update_window(workspace_window.into(), |_, window, cx| { @@ -3528,7 +3528,7 @@ edition = "2021" new_workspace.read(cx).panel::(cx) })?; if let Some(new_panel) = new_panel { - let new_thread_view = cx.read(|cx| new_panel.read(cx).as_active_thread_view(cx)); + let new_thread_view = cx.read(|cx| new_panel.read(cx).active_thread_view(cx)); if let Some(new_thread_view) = new_thread_view { cx.update_window(workspace_window.into(), |_, window, cx| { let message_editor = new_thread_view.read(cx).message_editor.clone();