agent_ui: Show the "new thread" keybinding for the currently active agent (#42939)

Danilo Leal created

This PR's goal is to improve discoverability of how Zed "remembers" the
currently selected agent when hitting `cmd-n` (or `ctrl-n`). Hitting
that binding starts a new thread with whatever agent is currently
selected.

In the example below, I am in a Claude Code thread and if I hit `cmd-n`,
a new, fresh CC thread will be started:

<img width="500" height="822" alt="Screenshot 2025-11-18 at 1  13@2x"
src="https://github.com/user-attachments/assets/d3acd1aa-459d-4078-9b62-bbac3b8c1600"
/>


Release Notes:

- agent: Improved discoverability of the `cmd-n` keybinding to create a
new thread with the currently selected agent.

Change summary

assets/keymaps/default-macos.json  |  2 
crates/agent_ui/src/agent_panel.rs | 50 ++++++++++++++++++++++++++-----
2 files changed, 42 insertions(+), 10 deletions(-)

Detailed changes

assets/keymaps/default-macos.json 🔗

@@ -313,7 +313,7 @@
     "use_key_equivalents": true,
     "bindings": {
       "cmd-n": "agent::NewTextThread",
-      "cmd-alt-t": "agent::NewThread"
+      "cmd-alt-n": "agent::NewExternalAgentThread"
     }
   },
   {

crates/agent_ui/src/agent_panel.rs 🔗

@@ -1892,6 +1892,9 @@ impl AgentPanel {
             .anchor(Corner::TopRight)
             .with_handle(self.new_thread_menu_handle.clone())
             .menu({
+                let selected_agent = self.selected_agent.clone();
+                let is_agent_selected = move |agent_type: AgentType| selected_agent == agent_type;
+
                 let workspace = self.workspace.clone();
                 let is_via_collab = workspace
                     .update(cx, |workspace, cx| {
@@ -1929,7 +1932,9 @@ impl AgentPanel {
                             })
                             .item(
                                 ContextMenuEntry::new("Zed Agent")
-                                    .action(NewThread.boxed_clone())
+                                    .when(is_agent_selected(AgentType::NativeAgent) | is_agent_selected(AgentType::TextThread) , |this| {
+                                        this.action(Box::new(NewExternalAgentThread { agent: None }))
+                                    })
                                     .icon(IconName::ZedAgent)
                                     .icon_color(Color::Muted)
                                     .handler({
@@ -1955,9 +1960,9 @@ impl AgentPanel {
                             )
                             .item(
                                 ContextMenuEntry::new("Text Thread")
+                                    .action(NewTextThread.boxed_clone())
                                     .icon(IconName::TextThread)
                                     .icon_color(Color::Muted)
-                                    .action(NewTextThread.boxed_clone())
                                     .handler({
                                         let workspace = workspace.clone();
                                         move |window, cx| {
@@ -1983,6 +1988,9 @@ impl AgentPanel {
                             .header("External Agents")
                             .item(
                                 ContextMenuEntry::new("Claude Code")
+                                    .when(is_agent_selected(AgentType::ClaudeCode), |this| {
+                                        this.action(Box::new(NewExternalAgentThread { agent: None }))
+                                    })
                                     .icon(IconName::AiClaude)
                                     .disabled(is_via_collab)
                                     .icon_color(Color::Muted)
@@ -2009,6 +2017,9 @@ impl AgentPanel {
                             )
                             .item(
                                 ContextMenuEntry::new("Codex CLI")
+                                    .when(is_agent_selected(AgentType::Codex), |this| {
+                                        this.action(Box::new(NewExternalAgentThread { agent: None }))
+                                    })
                                     .icon(IconName::AiOpenAi)
                                     .disabled(is_via_collab)
                                     .icon_color(Color::Muted)
@@ -2035,6 +2046,9 @@ impl AgentPanel {
                             )
                             .item(
                                 ContextMenuEntry::new("Gemini CLI")
+                                    .when(is_agent_selected(AgentType::Gemini), |this| {
+                                        this.action(Box::new(NewExternalAgentThread { agent: None }))
+                                    })
                                     .icon(IconName::AiGemini)
                                     .icon_color(Color::Muted)
                                     .disabled(is_via_collab)
@@ -2060,8 +2074,8 @@ impl AgentPanel {
                                     }),
                             )
                             .map(|mut menu| {
-                                let agent_server_store_read = agent_server_store.read(cx);
-                                let agent_names = agent_server_store_read
+                                let agent_server_store = agent_server_store.read(cx);
+                                let agent_names = agent_server_store
                                     .external_agents()
                                     .filter(|name| {
                                         name.0 != GEMINI_NAME
@@ -2070,21 +2084,38 @@ impl AgentPanel {
                                     })
                                     .cloned()
                                     .collect::<Vec<_>>();
+
                                 let custom_settings = cx
                                     .global::<SettingsStore>()
                                     .get::<AllAgentServersSettings>(None)
                                     .custom
                                     .clone();
+
                                 for agent_name in agent_names {
-                                    let icon_path = agent_server_store_read.agent_icon(&agent_name);
-                                    let mut entry =
-                                        ContextMenuEntry::new(format!("{}", agent_name));
+                                    let icon_path = agent_server_store.agent_icon(&agent_name);
+
+                                    let mut entry = ContextMenuEntry::new(agent_name.clone());
+
+                                    let command = custom_settings
+                                        .get(&agent_name.0)
+                                        .map(|settings| settings.command.clone())
+                                        .unwrap_or(placeholder_command());
+
                                     if let Some(icon_path) = icon_path {
                                         entry = entry.custom_icon_svg(icon_path);
                                     } else {
                                         entry = entry.icon(IconName::Terminal);
                                     }
                                     entry = entry
+                                        .when(
+                                            is_agent_selected(AgentType::Custom {
+                                                name: agent_name.0.clone(),
+                                                command: command.clone(),
+                                            }),
+                                            |this| {
+                                                this.action(Box::new(NewExternalAgentThread { agent: None }))
+                                            },
+                                        )
                                         .icon_color(Color::Muted)
                                         .disabled(is_via_collab)
                                         .handler({
@@ -2124,6 +2155,7 @@ impl AgentPanel {
                                                 }
                                             }
                                         });
+
                                     menu = menu.item(entry);
                                 }
 
@@ -2156,7 +2188,7 @@ impl AgentPanel {
             .id("selected_agent_icon")
             .when_some(selected_agent_custom_icon, |this, icon_path| {
                 let label = selected_agent_label.clone();
-                this.px(DynamicSpacing::Base02.rems(cx))
+                this.px_1()
                     .child(Icon::from_external_svg(icon_path).color(Color::Muted))
                     .tooltip(move |_window, cx| {
                         Tooltip::with_meta(label.clone(), None, "Selected Agent", cx)
@@ -2165,7 +2197,7 @@ impl AgentPanel {
             .when(!has_custom_icon, |this| {
                 this.when_some(self.selected_agent.icon(), |this, icon| {
                     let label = selected_agent_label.clone();
-                    this.px(DynamicSpacing::Base02.rems(cx))
+                    this.px_1()
                         .child(Icon::new(icon).color(Color::Muted))
                         .tooltip(move |_window, cx| {
                             Tooltip::with_meta(label.clone(), None, "Selected Agent", cx)