From 8db954a492299f9ff14f6ee478f506bebcbd73fa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 22 Jan 2026 11:18:13 +0100 Subject: [PATCH] agent_ui: Defer spawning agents until the agent panel has been opened (#47355) Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/agent_ui/src/acp/thread_view.rs | 13 +- crates/agent_ui/src/agent_panel.rs | 138 ++++++-------------- crates/agent_ui_v2/src/agent_thread_pane.rs | 1 - 3 files changed, 41 insertions(+), 111 deletions(-) diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 98b2825cb33395b249e69c155a602b9b5815abaf..4b994b61a8885f7f24708f90da190221adedc958 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -413,7 +413,6 @@ impl AcpThreadView { thread_store: Option>, prompt_store: Option>, history: Entity, - track_load_event: bool, window: &mut Window, cx: &mut Context, ) -> Self { @@ -559,7 +558,6 @@ impl AcpThreadView { resume_thread.clone(), workspace.clone(), project.clone(), - track_load_event, window, cx, ), @@ -570,7 +568,7 @@ impl AcpThreadView { profile_selector: None, notifications: Vec::new(), notification_subscriptions: HashMap::default(), - list_state: list_state, + list_state, thread_retry_status: None, thread_error: None, thread_error_markdown: None, @@ -630,7 +628,6 @@ impl AcpThreadView { self.resume_thread_metadata.clone(), self.workspace.clone(), self.project.clone(), - true, window, cx, ); @@ -651,7 +648,6 @@ impl AcpThreadView { resume_thread: Option, workspace: WeakEntity, project: Entity, - track_load_event: bool, window: &mut Window, cx: &mut Context, ) -> ThreadState { @@ -713,9 +709,7 @@ impl AcpThreadView { } }; - if track_load_event { - telemetry::event!("Agent Thread Started", agent = connection.telemetry_id()); - } + telemetry::event!("Agent Thread Started", agent = connection.telemetry_id()); let result = if let Some(resume) = resume_thread.clone() { cx.update(|_, cx| { @@ -8703,7 +8697,6 @@ pub(crate) mod tests { Some(thread_store), None, history.clone(), - false, window, cx, ) @@ -8995,7 +8988,6 @@ pub(crate) mod tests { Some(thread_store), None, history, - false, window, cx, ) @@ -9288,7 +9280,6 @@ pub(crate) mod tests { Some(thread_store.clone()), None, history, - false, window, cx, ) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 2991bff04ffe46f1be75517ccf9e6e6d702dfd96..dc3b32cb7faf4b5608c6b9fe515d4bf56938b534 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -9,9 +9,7 @@ use project::{ agent_server_store::{CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME}, }; use serde::{Deserialize, Serialize}; -use settings::{ - DefaultAgentView as DefaultView, LanguageModelProviderSetting, LanguageModelSelection, -}; +use settings::{LanguageModelProviderSetting, LanguageModelSelection}; use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent}; @@ -219,6 +217,7 @@ enum HistoryKind { } enum ActiveView { + Uninitialized, ExternalAgentThread { thread_view: Entity, }, @@ -291,43 +290,14 @@ impl From for AgentType { impl ActiveView { pub fn which_font_size_used(&self) -> WhichFontSize { match self { - ActiveView::ExternalAgentThread { .. } | ActiveView::History { .. } => { - WhichFontSize::AgentFont - } + ActiveView::Uninitialized + | ActiveView::ExternalAgentThread { .. } + | ActiveView::History { .. } => WhichFontSize::AgentFont, ActiveView::TextThread { .. } => WhichFontSize::BufferFont, ActiveView::Configuration => WhichFontSize::None, } } - fn native_agent( - fs: Arc, - prompt_store: Option>, - thread_store: Entity, - project: Entity, - workspace: WeakEntity, - history: Entity, - window: &mut Window, - cx: &mut App, - ) -> Self { - let thread_view = cx.new(|cx| { - crate::acp::AcpThreadView::new( - ExternalAgent::NativeAgent.server(fs, thread_store.clone()), - None, - None, - workspace, - project, - Some(thread_store), - prompt_store, - history, - false, - window, - cx, - ) - }); - - Self::ExternalAgentThread { thread_view } - } - pub fn text_thread( text_thread_editor: Entity, language_registry: Arc, @@ -418,7 +388,6 @@ impl ActiveView { pub struct AgentPanel { workspace: WeakEntity, - loading: bool, user_store: Entity, project: Entity, fs: Arc, @@ -431,6 +400,7 @@ pub struct AgentPanel { context_server_registry: Entity, configuration: Option>, configuration_subscription: Option, + focus_handle: FocusHandle, active_view: ActiveView, previous_view: Option, new_thread_menu_handle: PopoverMenuHandle, @@ -504,22 +474,15 @@ impl AgentPanel { let panel = cx.new(|cx| Self::new(workspace, text_thread_store, prompt_store, window, cx)); - panel.as_mut(cx).loading = true; if let Some(serialized_panel) = serialized_panel { panel.update(cx, |panel, cx| { panel.width = serialized_panel.width.map(|w| w.round()); if let Some(selected_agent) = serialized_panel.selected_agent { - panel.selected_agent = selected_agent.clone(); - panel.new_agent_thread(selected_agent, window, cx); + panel.selected_agent = selected_agent; } cx.notify(); }); - } else { - panel.update(cx, |panel, cx| { - panel.new_agent_thread(AgentType::NativeAgent, window, cx); - }); } - panel.as_mut(cx).loading = false; panel })?; @@ -570,37 +533,7 @@ impl AgentPanel { ) .detach(); - let panel_type = AgentSettings::get_global(cx).default_view; - let active_view = match panel_type { - DefaultView::Thread => ActiveView::native_agent( - fs.clone(), - prompt_store.clone(), - thread_store.clone(), - project.clone(), - workspace.clone(), - acp_history.clone(), - window, - cx, - ), - DefaultView::TextThread => { - let context = text_thread_store.update(cx, |store, cx| store.create(cx)); - let lsp_adapter_delegate = make_lsp_adapter_delegate(&project.clone(), cx).unwrap(); - let text_thread_editor = cx.new(|cx| { - let mut editor = TextThreadEditor::for_text_thread( - context, - fs.clone(), - workspace.clone(), - project.clone(), - lsp_adapter_delegate, - window, - cx, - ); - editor.insert_default_prompt(window, cx); - editor - }); - ActiveView::text_thread(text_thread_editor, language_registry.clone(), window, cx) - } - }; + let active_view = ActiveView::Uninitialized; let weak_panel = cx.entity().downgrade(); @@ -680,6 +613,7 @@ impl AgentPanel { prompt_store, configuration: None, configuration_subscription: None, + focus_handle: cx.focus_handle(), context_server_registry, previous_view: None, new_thread_menu_handle: PopoverMenuHandle::default(), @@ -696,7 +630,6 @@ impl AgentPanel { text_thread_history, thread_store, selected_agent: AgentType::default(), - loading: false, show_trust_workspace_message: false, }; @@ -772,7 +705,8 @@ impl AgentPanel { pub(crate) fn active_thread_view(&self) -> Option<&Entity> { match &self.active_view { ActiveView::ExternalAgentThread { thread_view, .. } => Some(thread_view), - ActiveView::TextThread { .. } + ActiveView::Uninitialized + | ActiveView::TextThread { .. } | ActiveView::History { .. } | ActiveView::Configuration => None, } @@ -868,7 +802,6 @@ impl AgentPanel { agent: crate::ExternalAgent, } - let loading = self.loading; let thread_store = self.thread_store.clone(); cx.spawn_in(window, async move |this, cx| { @@ -918,7 +851,6 @@ impl AgentPanel { summarize_thread, workspace, project, - loading, ext_agent, window, cx, @@ -1064,7 +996,9 @@ impl AgentPanel { } => { text_thread_editor.focus_handle(cx).focus(window, cx); } - ActiveView::History { .. } | ActiveView::Configuration => {} + ActiveView::Uninitialized + | ActiveView::History { .. } + | ActiveView::Configuration => {} } } cx.notify(); @@ -1234,7 +1168,8 @@ impl AgentPanel { }) .detach_and_log_err(cx); } - ActiveView::TextThread { .. } + ActiveView::Uninitialized + | ActiveView::TextThread { .. } | ActiveView::History { .. } | ActiveView::Configuration => {} } @@ -1314,6 +1249,7 @@ impl AgentPanel { window: &mut Window, cx: &mut Context, ) { + let current_is_uninitialized = matches!(self.active_view, ActiveView::Uninitialized); let current_is_history = matches!(self.active_view, ActiveView::History { .. }); let new_is_history = matches!(new_view, ActiveView::History { .. }); @@ -1323,13 +1259,7 @@ impl AgentPanel { let current_is_special = current_is_history || current_is_config; let new_is_special = new_is_history || new_is_config; - match &new_view { - ActiveView::TextThread { .. } => {} - ActiveView::ExternalAgentThread { .. } => {} - ActiveView::History { .. } | ActiveView::Configuration => {} - } - - if current_is_special && !new_is_special { + if current_is_uninitialized || (current_is_special && !new_is_special) { self.active_view = new_view; } else if !current_is_special && new_is_special { self.previous_view = Some(std::mem::replace(&mut self.active_view, new_view)); @@ -1539,7 +1469,6 @@ impl AgentPanel { summarize_thread: Option, workspace: WeakEntity, project: Entity, - loading: bool, ext_agent: ExternalAgent, window: &mut Window, cx: &mut Context, @@ -1565,7 +1494,6 @@ impl AgentPanel { thread_store, self.prompt_store.clone(), self.acp_history.clone(), - !loading, window, cx, ) @@ -1573,7 +1501,7 @@ impl AgentPanel { self.set_active_view( ActiveView::ExternalAgentThread { thread_view }, - !loading, + true, window, cx, ); @@ -1583,6 +1511,7 @@ impl AgentPanel { impl Focusable for AgentPanel { fn focus_handle(&self, cx: &App) -> FocusHandle { match &self.active_view { + ActiveView::Uninitialized => self.focus_handle.clone(), ActiveView::ExternalAgentThread { thread_view, .. } => thread_view.focus_handle(cx), ActiveView::History { kind } => match kind { HistoryKind::AgentThreads => self.acp_history.focus_handle(cx), @@ -1595,7 +1524,7 @@ impl Focusable for AgentPanel { if let Some(configuration) = self.configuration.as_ref() { configuration.focus_handle(cx) } else { - cx.focus_handle() + self.focus_handle.clone() } } } @@ -1653,7 +1582,12 @@ impl Panel for AgentPanel { cx.notify(); } - fn set_active(&mut self, _active: bool, _window: &mut Window, _cx: &mut Context) {} + fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut Context) { + if active && matches!(self.active_view, ActiveView::Uninitialized) { + let selected_agent = self.selected_agent.clone(); + self.new_agent_thread(selected_agent, window, cx); + } + } fn remote_id() -> Option { Some(proto::PanelId::AssistantPanel) @@ -1806,6 +1740,7 @@ impl AgentPanel { Label::new(title).truncate().into_any_element() } ActiveView::Configuration => Label::new("Settings").truncate().into_any_element(), + ActiveView::Uninitialized => Label::new("Agent").truncate().into_any_element(), }; h_flex() @@ -2043,7 +1978,8 @@ impl AgentPanel { ActiveView::ExternalAgentThread { thread_view } => { thread_view.read(cx).as_native_thread(cx) } - ActiveView::TextThread { .. } + ActiveView::Uninitialized + | ActiveView::TextThread { .. } | ActiveView::History { .. } | ActiveView::Configuration => None, }; @@ -2430,7 +2366,8 @@ impl AgentPanel { return false; } } - ActiveView::ExternalAgentThread { .. } + ActiveView::Uninitialized + | ActiveView::ExternalAgentThread { .. } | ActiveView::History { .. } | ActiveView::Configuration => return false, } @@ -2461,7 +2398,9 @@ impl AgentPanel { } match &self.active_view { - ActiveView::History { .. } | ActiveView::Configuration => false, + ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => { + false + } ActiveView::ExternalAgentThread { thread_view, .. } if thread_view.read(cx).as_native_thread(cx).is_none() => { @@ -2726,7 +2665,7 @@ impl AgentPanel { ); }); } - ActiveView::History { .. } | ActiveView::Configuration => {} + ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => {} } } @@ -2768,7 +2707,7 @@ impl AgentPanel { match &self.active_view { ActiveView::ExternalAgentThread { .. } => key_context.add("acp_thread"), ActiveView::TextThread { .. } => key_context.add("text_thread"), - ActiveView::History { .. } | ActiveView::Configuration => {} + ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => {} } key_context } @@ -2817,6 +2756,7 @@ impl Render for AgentPanel { .children(self.render_workspace_trust_message(cx)) .children(self.render_onboarding(window, cx)) .map(|parent| match &self.active_view { + ActiveView::Uninitialized => parent, ActiveView::ExternalAgentThread { thread_view, .. } => parent .child(thread_view.clone()) .child(self.render_drag_target(cx)), @@ -3035,7 +2975,7 @@ impl AgentPanel { }; self._external_thread( - server, None, None, workspace, project, false, ext_agent, window, cx, + server, None, None, workspace, project, ext_agent, window, cx, ); } diff --git a/crates/agent_ui_v2/src/agent_thread_pane.rs b/crates/agent_ui_v2/src/agent_thread_pane.rs index c4d644af6f0af9815270d14735aeb33ef4b62a95..747ee09415c1819dcb7dae3e8b27ee411e9a222a 100644 --- a/crates/agent_ui_v2/src/agent_thread_pane.rs +++ b/crates/agent_ui_v2/src/agent_thread_pane.rs @@ -121,7 +121,6 @@ impl AgentThreadPane { Some(thread_store), prompt_store, history, - true, window, cx, )