agent_ui: Show the "new thread" keybinding for the currently active agent (#42939)
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.
This commit is contained in:
@@ -313,7 +313,7 @@
|
||||
"use_key_equivalents": true,
|
||||
"bindings": {
|
||||
"cmd-n": "agent::NewTextThread",
|
||||
"cmd-alt-t": "agent::NewThread"
|
||||
"cmd-alt-n": "agent::NewExternalAgentThread"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user