diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index a19bc77dcc..8a46e6c234 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -268,6 +268,14 @@ "ctrl-alt-t": "agent::NewThread" } }, + { + "context": "AgentPanel && acp_thread", + "use_key_equivalents": true, + "bindings": { + "ctrl-n": "agent::NewAcpThread", + "ctrl-alt-t": "agent::NewThread" + } + }, { "context": "MessageEditor > Editor", "bindings": { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 875658c5a0..cb1cf572fb 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -309,6 +309,14 @@ "cmd-alt-t": "agent::NewThread" } }, + { + "context": "AgentPanel && acp_thread", + "use_key_equivalents": true, + "bindings": { + "cmd-n": "agent::NewAcpThread", + "cmd-alt-t": "agent::NewThread" + } + }, { "context": "MessageEditor > Editor", "use_key_equivalents": true, diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index e726dd6640..9dce0abacf 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -7,7 +7,7 @@ use std::time::Duration; use db::kvp::{Dismissable, KEY_VALUE_STORE}; use serde::{Deserialize, Serialize}; -use crate::NewGeminiThread; +use crate::NewAcpThread; use crate::language_model_selector::ToggleModelSelector; use crate::{ AddContextServer, AgentDiffPane, ContinueThread, ContinueWithBurnMode, @@ -112,7 +112,7 @@ pub fn init(cx: &mut App) { panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx)); } }) - .register_action(|workspace, _: &NewGeminiThread, window, cx| { + .register_action(|workspace, _: &NewAcpThread, window, cx| { if let Some(panel) = workspace.panel::(cx) { workspace.focus_panel::(window, cx); panel.update(cx, |panel, cx| panel.new_gemini_thread(window, cx)); @@ -436,7 +436,8 @@ pub struct AgentPanel { history_store: Entity, history: Entity, hovered_recent_history_item: Option, - assistant_dropdown_menu_handle: PopoverMenuHandle, + new_thread_menu_handle: PopoverMenuHandle, + agent_panel_menu_handle: PopoverMenuHandle, assistant_navigation_menu_handle: PopoverMenuHandle, assistant_navigation_menu: Option>, width: Option, @@ -700,7 +701,8 @@ impl AgentPanel { history_store: history_store.clone(), history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, window, cx)), hovered_recent_history_item: None, - assistant_dropdown_menu_handle: PopoverMenuHandle::default(), + new_thread_menu_handle: PopoverMenuHandle::default(), + agent_panel_menu_handle: PopoverMenuHandle::default(), assistant_navigation_menu_handle: PopoverMenuHandle::default(), assistant_navigation_menu: None, width: None, @@ -1094,7 +1096,7 @@ impl AgentPanel { window: &mut Window, cx: &mut Context, ) { - self.assistant_dropdown_menu_handle.toggle(window, cx); + self.agent_panel_menu_handle.toggle(window, cx); } pub fn increase_font_size( @@ -1790,7 +1792,45 @@ impl AgentPanel { | ActiveView::Configuration => None, }; - let agent_extra_menu = PopoverMenu::new("agent-options-menu") + let new_thread_menu = PopoverMenu::new("new_thread_menu") + .trigger_with_tooltip( + IconButton::new("new_thread_menu_btn", IconName::Plus).icon_size(IconSize::Small), + Tooltip::text("New Thread…"), + ) + .anchor(Corner::TopRight) + .with_handle(self.new_thread_menu_handle.clone()) + .menu(move |window, cx| { + let active_thread = active_thread.clone(); + Some(ContextMenu::build(window, cx, |mut menu, _window, cx| { + menu = menu + .when(cx.has_flag::(), |this| { + this.header("Zed Agent") + }) + .action("New Thread", NewThread::default().boxed_clone()) + .action("New Text Thread", NewTextThread.boxed_clone()) + .when_some(active_thread, |this, active_thread| { + let thread = active_thread.read(cx); + if !thread.is_empty() { + this.action( + "New From Summary", + Box::new(NewThread { + from_thread_id: Some(thread.id().clone()), + }), + ) + } else { + this + } + }) + .when(cx.has_flag::(), |this| { + this.separator() + .header("External Agents") + .action("New Gemini Thread", NewAcpThread.boxed_clone()) + }); + menu + })) + }); + + let agent_panel_menu = PopoverMenu::new("agent-options-menu") .trigger_with_tooltip( IconButton::new("agent-options-menu", IconName::Ellipsis) .icon_size(IconSize::Small), @@ -1808,44 +1848,9 @@ impl AgentPanel { }, ) .anchor(Corner::TopRight) - .with_handle(self.assistant_dropdown_menu_handle.clone()) + .with_handle(self.agent_panel_menu_handle.clone()) .menu(move |window, cx| { - let active_thread = active_thread.clone(); - Some(ContextMenu::build(window, cx, |mut menu, _window, cx| { - menu = menu - .action("New Thread", NewThread::default().boxed_clone()) - .action("New Text Thread", NewTextThread.boxed_clone()) - .when(cx.has_flag::(), |this| { - this.action("New Gemini Thread", NewGeminiThread.boxed_clone()) - }) - .when_some(active_thread, |this, active_thread| { - let thread = active_thread.read(cx); - if !thread.is_empty() { - this.action( - "New From Summary", - Box::new(NewThread { - from_thread_id: Some(thread.id().clone()), - }), - ) - } else { - this - } - }) - .separator(); - - menu = menu - .header("MCP Servers") - .action( - "View Server Extensions", - Box::new(zed_actions::Extensions { - category_filter: Some( - zed_actions::ExtensionCategoryFilter::ContextServers, - ), - }), - ) - .action("Add Custom Server…", Box::new(AddContextServer)) - .separator(); - + Some(ContextMenu::build(window, cx, |mut menu, _window, _| { if let Some(usage) = usage { menu = menu .header_with_link("Prompt Usage", "Manage", account_url.clone()) @@ -1883,6 +1888,19 @@ impl AgentPanel { .separator() } + menu = menu + .header("MCP Servers") + .action( + "View Server Extensions", + Box::new(zed_actions::Extensions { + category_filter: Some( + zed_actions::ExtensionCategoryFilter::ContextServers, + ), + }), + ) + .action("Add Custom Server…", Box::new(AddContextServer)) + .separator(); + menu = menu .action("Rules…", Box::new(OpenRulesLibrary::default())) .action("Settings", Box::new(OpenConfiguration)) @@ -1924,27 +1942,8 @@ impl AgentPanel { .px(DynamicSpacing::Base08.rems(cx)) .border_l_1() .border_color(cx.theme().colors().border) - .child( - IconButton::new("new", IconName::Plus) - .icon_size(IconSize::Small) - .style(ButtonStyle::Subtle) - .tooltip(move |window, cx| { - Tooltip::for_action_in( - "New Thread", - &NewThread::default(), - &focus_handle, - window, - cx, - ) - }) - .on_click(move |_event, window, cx| { - window.dispatch_action( - NewThread::default().boxed_clone(), - cx, - ); - }), - ) - .child(agent_extra_menu), + .child(new_thread_menu) + .child(agent_panel_menu), ), ) } @@ -3054,8 +3053,10 @@ impl AgentPanel { fn key_context(&self) -> KeyContext { let mut key_context = KeyContext::new_with_defaults(); key_context.add("AgentPanel"); - if matches!(self.active_view, ActiveView::TextThread { .. }) { - key_context.add("prompt_editor"); + match &self.active_view { + ActiveView::AcpThread { .. } => key_context.add("acp_thread"), + ActiveView::TextThread { .. } => key_context.add("prompt_editor"), + ActiveView::Thread { .. } | ActiveView::History | ActiveView::Configuration => {} } key_context } diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index 10912cc055..3170ec4a26 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -57,8 +57,8 @@ actions!( [ /// Creates a new text-based conversation thread. NewTextThread, - /// Creates a new Gemini CLI-based conversation thread. - NewGeminiThread, + /// Creates a new external agent conversation thread. + NewAcpThread, /// Toggles the context picker interface for adding files, symbols, or other context. ToggleContextPicker, /// Toggles the navigation menu for switching between threads and views.