crates/agent_ui/src/agent_panel.rs 🔗
@@ -73,8 +73,9 @@ use search::{BufferSearchBar, buffer_search};
use settings::{Settings, update_settings_file};
use theme::ThemeSettings;
use ui::{
- Button, Callout, ContextMenu, ContextMenuEntry, DocumentationSide, KeyBinding, PopoverMenu,
- PopoverMenuHandle, SpinnerLabel, Tab, Tooltip, prelude::*, utils::WithRemSize,
+ Button, ButtonLike, Callout, ContextMenu, ContextMenuEntry, DocumentationSide, KeyBinding,
+ PopoverMenu, PopoverMenuHandle, SpinnerLabel, Tab, TintColor, Tooltip, prelude::*,
+ utils::WithRemSize,
};
use util::ResultExt as _;
use workspace::{
@@ -416,17 +417,10 @@ impl From<ExternalAgent> for AgentType {
impl StartThreadIn {
fn label(&self) -> SharedString {
match self {
- Self::LocalProject => "Local Project".into(),
+ Self::LocalProject => "Current Project".into(),
Self::NewWorktree => "New Worktree".into(),
}
}
-
- fn icon(&self) -> IconName {
- match self {
- Self::LocalProject => IconName::Screen,
- Self::NewWorktree => IconName::GitBranchPlus,
- }
- }
}
#[derive(Clone, Debug)]
@@ -3114,12 +3108,11 @@ impl AgentPanel {
};
let trigger_button = Button::new("thread-target-trigger", trigger_label)
- .label_size(LabelSize::Small)
- .color(Color::Muted)
.icon(icon)
.icon_size(IconSize::XSmall)
.icon_position(IconPosition::End)
.icon_color(Color::Muted)
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent))
.disabled(is_creating);
let dock_position = AgentSettings::get_global(cx).dock;
@@ -3132,21 +3125,16 @@ impl AgentPanel {
PopoverMenu::new("thread-target-selector")
.trigger(trigger_button)
- .anchor(gpui::Corner::BottomRight)
- .with_handle(self.start_thread_in_menu_handle.clone())
.menu(move |window, cx| {
- let current_target = current_target;
- Some(ContextMenu::build(window, cx, move |menu, _window, _cx| {
- let is_local_selected = current_target == StartThreadIn::LocalProject;
- let is_new_worktree_selected = current_target == StartThreadIn::NewWorktree;
+ let is_local_selected = current_target == StartThreadIn::LocalProject;
+ let is_new_worktree_selected = current_target == StartThreadIn::NewWorktree;
+ Some(ContextMenu::build(window, cx, move |menu, _window, _cx| {
let new_worktree_disabled = !has_git_repo || is_via_collab;
menu.header("Start Thread In…")
.item(
- ContextMenuEntry::new("Local Project")
- .icon(StartThreadIn::LocalProject.icon())
- .icon_color(Color::Muted)
+ ContextMenuEntry::new("Current Project")
.toggleable(IconPosition::End, is_local_selected)
.handler(|window, cx| {
window
@@ -3155,8 +3143,6 @@ impl AgentPanel {
)
.item({
let entry = ContextMenuEntry::new("New Worktree")
- .icon(StartThreadIn::NewWorktree.icon())
- .icon_color(Color::Muted)
.toggleable(IconPosition::End, is_new_worktree_selected)
.disabled(new_worktree_disabled)
.handler(|window, cx| {
@@ -3182,6 +3168,12 @@ impl AgentPanel {
})
}))
})
+ .with_handle(self.start_thread_in_menu_handle.clone())
+ .anchor(Corner::TopLeft)
+ .offset(gpui::Point {
+ x: px(1.0),
+ y: px(1.0),
+ })
}
fn render_toolbar(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
@@ -3209,77 +3201,179 @@ impl AgentPanel {
| ActiveView::Configuration => None,
};
- let new_thread_menu = PopoverMenu::new("new_thread_menu")
- .trigger_with_tooltip(
- IconButton::new("new_thread_menu_btn", IconName::Plus).icon_size(IconSize::Small),
- {
- let focus_handle = focus_handle.clone();
- move |_window, cx| {
- Tooltip::for_action_in(
- "New Thread…",
- &ToggleNewThreadMenu,
- &focus_handle,
- cx,
+ let new_thread_menu_builder: Rc<
+ dyn Fn(&mut Window, &mut App) -> Option<Entity<ContextMenu>>,
+ > = {
+ 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| {
+ workspace.project().read(cx).is_via_collab()
+ })
+ .unwrap_or_default();
+
+ let focus_handle = focus_handle.clone();
+ let agent_server_store = agent_server_store;
+
+ Rc::new(move |window, cx| {
+ telemetry::event!("New Thread Clicked");
+
+ let active_thread = active_thread.clone();
+ Some(ContextMenu::build(window, cx, |menu, _window, cx| {
+ menu.context(focus_handle.clone())
+ .when_some(active_thread, |this, active_thread| {
+ let thread = active_thread.read(cx);
+
+ if !thread.is_empty() {
+ let session_id = thread.id().clone();
+ this.item(
+ ContextMenuEntry::new("New From Summary")
+ .icon(IconName::ThreadFromSummary)
+ .icon_color(Color::Muted)
+ .handler(move |window, cx| {
+ window.dispatch_action(
+ Box::new(NewNativeAgentThreadFromSummary {
+ from_session_id: session_id.clone(),
+ }),
+ cx,
+ );
+ }),
+ )
+ } else {
+ this
+ }
+ })
+ .item(
+ ContextMenuEntry::new("Zed Agent")
+ .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({
+ let workspace = workspace.clone();
+ move |window, cx| {
+ if let Some(workspace) = workspace.upgrade() {
+ workspace.update(cx, |workspace, cx| {
+ if let Some(panel) =
+ workspace.panel::<AgentPanel>(cx)
+ {
+ panel.update(cx, |panel, cx| {
+ panel.new_agent_thread(
+ AgentType::NativeAgent,
+ window,
+ cx,
+ );
+ });
+ }
+ });
+ }
+ }
+ }),
)
- }
- },
- )
- .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;
+ .item(
+ ContextMenuEntry::new("Text Thread")
+ .action(NewTextThread.boxed_clone())
+ .icon(IconName::TextThread)
+ .icon_color(Color::Muted)
+ .handler({
+ let workspace = workspace.clone();
+ move |window, cx| {
+ if let Some(workspace) = workspace.upgrade() {
+ workspace.update(cx, |workspace, cx| {
+ if let Some(panel) =
+ workspace.panel::<AgentPanel>(cx)
+ {
+ panel.update(cx, |panel, cx| {
+ panel.new_agent_thread(
+ AgentType::TextThread,
+ window,
+ cx,
+ );
+ });
+ }
+ });
+ }
+ }
+ }),
+ )
+ .separator()
+ .header("External Agents")
+ .map(|mut menu| {
+ let agent_server_store = agent_server_store.read(cx);
+ let registry_store =
+ project::AgentRegistryStore::try_global(cx);
+ let registry_store_ref =
+ registry_store.as_ref().map(|s| s.read(cx));
+
+ struct AgentMenuItem {
+ id: ExternalAgentServerName,
+ display_name: SharedString,
+ }
- let workspace = self.workspace.clone();
- let is_via_collab = workspace
- .update(cx, |workspace, cx| {
- workspace.project().read(cx).is_via_collab()
- })
- .unwrap_or_default();
+ let agent_items = agent_server_store
+ .external_agents()
+ .map(|name| {
+ let display_name = agent_server_store
+ .agent_display_name(name)
+ .or_else(|| {
+ registry_store_ref
+ .as_ref()
+ .and_then(|store| store.agent(name.0.as_ref()))
+ .map(|a| a.name().clone())
+ })
+ .unwrap_or_else(|| name.0.clone());
+ AgentMenuItem {
+ id: name.clone(),
+ display_name,
+ }
+ })
+ .sorted_unstable_by_key(|e| e.display_name.to_lowercase())
+ .collect::<Vec<_>>();
- move |window, cx| {
- telemetry::event!("New Thread Clicked");
-
- let active_thread = active_thread.clone();
- Some(ContextMenu::build(window, cx, |menu, _window, cx| {
- menu.context(focus_handle.clone())
- .when_some(active_thread, |this, active_thread| {
- let thread = active_thread.read(cx);
-
- if !thread.is_empty() {
- let session_id = thread.id().clone();
- this.item(
- ContextMenuEntry::new("New From Summary")
- .icon(IconName::ThreadFromSummary)
- .icon_color(Color::Muted)
- .handler(move |window, cx| {
- window.dispatch_action(
- Box::new(NewNativeAgentThreadFromSummary {
- from_session_id: session_id.clone(),
- }),
- cx,
- );
- }),
- )
+ for item in &agent_items {
+ let mut entry =
+ ContextMenuEntry::new(item.display_name.clone());
+
+ let icon_path = agent_server_store
+ .agent_icon(&item.id)
+ .or_else(|| {
+ registry_store_ref
+ .as_ref()
+ .and_then(|store| store.agent(item.id.0.as_str()))
+ .and_then(|a| a.icon_path().cloned())
+ });
+
+ if let Some(icon_path) = icon_path {
+ entry = entry.custom_icon_svg(icon_path);
} else {
- this
+ entry = entry.icon(IconName::Sparkle);
}
- })
- .item(
- ContextMenuEntry::new("Zed Agent")
+
+ entry = entry
.when(
- is_agent_selected(AgentType::NativeAgent)
- | is_agent_selected(AgentType::TextThread),
+ is_agent_selected(AgentType::Custom {
+ name: item.id.0.clone(),
+ }),
|this| {
- this.action(Box::new(NewExternalAgentThread {
- agent: None,
- }))
+ this.action(Box::new(
+ NewExternalAgentThread { agent: None },
+ ))
},
)
- .icon(IconName::ZedAgent)
.icon_color(Color::Muted)
+ .disabled(is_via_collab)
.handler({
let workspace = workspace.clone();
+ let agent_id = item.id.clone();
move |window, cx| {
if let Some(workspace) = workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
@@ -3288,7 +3382,9 @@ impl AgentPanel {
{
panel.update(cx, |panel, cx| {
panel.new_agent_thread(
- AgentType::NativeAgent,
+ AgentType::Custom {
+ name: agent_id.0.clone(),
+ },
window,
cx,
);
@@ -3297,16 +3393,84 @@ impl AgentPanel {
});
}
}
- }),
- )
- .item(
- ContextMenuEntry::new("Text Thread")
- .action(NewTextThread.boxed_clone())
- .icon(IconName::TextThread)
+ });
+
+ menu = menu.item(entry);
+ }
+
+ menu
+ })
+ .separator()
+ .map(|mut menu| {
+ let agent_server_store = agent_server_store.read(cx);
+ let registry_store =
+ project::AgentRegistryStore::try_global(cx);
+ let registry_store_ref =
+ registry_store.as_ref().map(|s| s.read(cx));
+
+ let previous_built_in_ids: &[ExternalAgentServerName] =
+ &[CLAUDE_AGENT_NAME.into(), CODEX_NAME.into(), GEMINI_NAME.into()];
+
+ let promoted_items = previous_built_in_ids
+ .iter()
+ .filter(|id| {
+ !agent_server_store.external_agents.contains_key(*id)
+ })
+ .filter_map(|name| {
+ let display_name = registry_store_ref
+ .as_ref()
+ .and_then(|store| store.agent(name.0.as_ref()))
+ .map(|a| a.name().clone())?;
+ Some((name.clone(), display_name))
+ })
+ .sorted_unstable_by_key(|(_, display_name)| display_name.to_lowercase())
+ .collect::<Vec<_>>();
+
+ for (agent_id, display_name) in &promoted_items {
+ let mut entry =
+ ContextMenuEntry::new(display_name.clone());
+
+ let icon_path = registry_store_ref
+ .as_ref()
+ .and_then(|store| store.agent(agent_id.0.as_str()))
+ .and_then(|a| a.icon_path().cloned());
+
+ if let Some(icon_path) = icon_path {
+ entry = entry.custom_icon_svg(icon_path);
+ } else {
+ entry = entry.icon(IconName::Sparkle);
+ }
+
+ entry = entry
.icon_color(Color::Muted)
+ .disabled(is_via_collab)
.handler({
let workspace = workspace.clone();
+ let agent_id = agent_id.clone();
move |window, cx| {
+ let fs = <dyn fs::Fs>::global(cx);
+ let agent_id_string =
+ agent_id.to_string();
+ settings::update_settings_file(
+ fs,
+ cx,
+ move |settings, _| {
+ let agent_servers = settings
+ .agent_servers
+ .get_or_insert_default();
+ agent_servers.entry(agent_id_string).or_insert_with(|| {
+ settings::CustomAgentServerSettings::Registry {
+ default_mode: None,
+ default_model: None,
+ env: Default::default(),
+ favorite_models: Vec::new(),
+ default_config_options: Default::default(),
+ favorite_config_option_values: Default::default(),
+ }
+ });
+ },
+ );
+
if let Some(workspace) = workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
if let Some(panel) =
@@ -3314,7 +3478,9 @@ impl AgentPanel {
{
panel.update(cx, |panel, cx| {
panel.new_agent_thread(
- AgentType::TextThread,
+ AgentType::Custom {
+ name: agent_id.0.clone(),
+ },
window,
cx,
);
@@ -3323,215 +3489,29 @@ impl AgentPanel {
});
}
}
- }),
- )
- .separator()
- .header("External Agents")
- .map(|mut menu| {
- let agent_server_store = agent_server_store.read(cx);
- let registry_store =
- project::AgentRegistryStore::try_global(cx);
- let registry_store_ref =
- registry_store.as_ref().map(|s| s.read(cx));
-
- struct AgentMenuItem {
- id: ExternalAgentServerName,
- display_name: SharedString,
- }
-
- let agent_items = agent_server_store
- .external_agents()
- .map(|name| {
- let display_name = agent_server_store
- .agent_display_name(name)
- .or_else(|| {
- registry_store_ref
- .as_ref()
- .and_then(|store| store.agent(name.0.as_ref()))
- .map(|a| a.name().clone())
- })
- .unwrap_or_else(|| name.0.clone());
- AgentMenuItem {
- id: name.clone(),
- display_name,
- }
- })
- .sorted_unstable_by_key(|e| e.display_name.to_lowercase())
- .collect::<Vec<_>>();
-
- for item in &agent_items {
- let mut entry =
- ContextMenuEntry::new(item.display_name.clone());
+ });
- let icon_path = agent_server_store
- .agent_icon(&item.id)
- .or_else(|| {
- registry_store_ref
- .as_ref()
- .and_then(|store| store.agent(item.id.0.as_str()))
- .and_then(|a| a.icon_path().cloned())
- });
-
- if let Some(icon_path) = icon_path {
- entry = entry.custom_icon_svg(icon_path);
- } else {
- entry = entry.icon(IconName::Sparkle);
- }
+ menu = menu.item(entry);
+ }
- entry = entry
- .when(
- is_agent_selected(AgentType::Custom {
- name: item.id.0.clone(),
- }),
- |this| {
- this.action(Box::new(
- NewExternalAgentThread { agent: None },
- ))
- },
+ menu
+ })
+ .item(
+ ContextMenuEntry::new("Add More Agents")
+ .icon(IconName::Plus)
+ .icon_color(Color::Muted)
+ .handler({
+ move |window, cx| {
+ window.dispatch_action(
+ Box::new(zed_actions::AcpRegistry),
+ cx,
)
- .icon_color(Color::Muted)
- .disabled(is_via_collab)
- .handler({
- let workspace = workspace.clone();
- let agent_id = item.id.clone();
- move |window, cx| {
- if let Some(workspace) = workspace.upgrade() {
- workspace.update(cx, |workspace, cx| {
- if let Some(panel) =
- workspace.panel::<AgentPanel>(cx)
- {
- panel.update(cx, |panel, cx| {
- panel.new_agent_thread(
- AgentType::Custom {
- name: agent_id.0.clone(),
- },
- window,
- cx,
- );
- });
- }
- });
- }
- }
- });
-
- menu = menu.item(entry);
- }
-
- menu
- })
- .separator()
- .map(|mut menu| {
- let agent_server_store = agent_server_store.read(cx);
- let registry_store =
- project::AgentRegistryStore::try_global(cx);
- let registry_store_ref =
- registry_store.as_ref().map(|s| s.read(cx));
-
- let previous_built_in_ids: &[ExternalAgentServerName] =
- &[CLAUDE_AGENT_NAME.into(), CODEX_NAME.into(), GEMINI_NAME.into()];
-
- let promoted_items = previous_built_in_ids
- .iter()
- .filter(|id| {
- !agent_server_store.external_agents.contains_key(*id)
- })
- .filter_map(|name| {
- let display_name = registry_store_ref
- .as_ref()
- .and_then(|store| store.agent(name.0.as_ref()))
- .map(|a| a.name().clone())?;
- Some((name.clone(), display_name))
- })
- .sorted_unstable_by_key(|(_, display_name)| display_name.to_lowercase())
- .collect::<Vec<_>>();
-
- for (agent_id, display_name) in &promoted_items {
- let mut entry =
- ContextMenuEntry::new(display_name.clone());
-
- let icon_path = registry_store_ref
- .as_ref()
- .and_then(|store| store.agent(agent_id.0.as_str()))
- .and_then(|a| a.icon_path().cloned());
-
- if let Some(icon_path) = icon_path {
- entry = entry.custom_icon_svg(icon_path);
- } else {
- entry = entry.icon(IconName::Sparkle);
}
-
- entry = entry
- .icon_color(Color::Muted)
- .disabled(is_via_collab)
- .handler({
- let workspace = workspace.clone();
- let agent_id = agent_id.clone();
- move |window, cx| {
- let fs = <dyn fs::Fs>::global(cx);
- let agent_id_string =
- agent_id.to_string();
- settings::update_settings_file(
- fs,
- cx,
- move |settings, _| {
- let agent_servers = settings
- .agent_servers
- .get_or_insert_default();
- agent_servers.entry(agent_id_string).or_insert_with(|| {
- settings::CustomAgentServerSettings::Registry {
- default_mode: None,
- default_model: None,
- env: Default::default(),
- favorite_models: Vec::new(),
- default_config_options: Default::default(),
- favorite_config_option_values: Default::default(),
- }
- });
- },
- );
-
- if let Some(workspace) = workspace.upgrade() {
- workspace.update(cx, |workspace, cx| {
- if let Some(panel) =
- workspace.panel::<AgentPanel>(cx)
- {
- panel.update(cx, |panel, cx| {
- panel.new_agent_thread(
- AgentType::Custom {
- name: agent_id.0.clone(),
- },
- window,
- cx,
- );
- });
- }
- });
- }
- }
- });
-
- menu = menu.item(entry);
- }
-
- menu
- })
- .item(
- ContextMenuEntry::new("Add More Agents")
- .icon(IconName::Plus)
- .icon_color(Color::Muted)
- .handler({
- move |window, cx| {
- window.dispatch_action(
- Box::new(zed_actions::AcpRegistry),
- cx,
- )
- }
- }),
- )
- }))
- }
- });
+ }),
+ )
+ }))
+ })
+ };
let is_thread_loading = self
.active_connection_view()
@@ -3539,6 +3519,9 @@ impl AgentPanel {
.unwrap_or(false);
let has_custom_icon = selected_agent_custom_icon.is_some();
+ let selected_agent_custom_icon_for_button = selected_agent_custom_icon.clone();
+ let selected_agent_builtin_icon = self.selected_agent.icon();
+ let selected_agent_label_for_tooltip = selected_agent_label.clone();
let selected_agent = div()
.id("selected_agent_icon")
@@ -3552,7 +3535,12 @@ impl AgentPanel {
})
})
.tooltip(move |_, cx| {
- Tooltip::with_meta(selected_agent_label.clone(), None, "Selected Agent", cx)
+ Tooltip::with_meta(
+ selected_agent_label_for_tooltip.clone(),
+ None,
+ "Selected Agent",
+ cx,
+ )
});
let selected_agent = if is_thread_loading {
@@ -3571,50 +3559,160 @@ impl AgentPanel {
let show_history_menu = self.history_kind_for_selected_agent(cx).is_some();
let has_v2_flag = cx.has_flag::<AgentV2FeatureFlag>();
+ let is_empty_state = !self.active_thread_has_messages(cx);
- h_flex()
- .id("agent-panel-toolbar")
- .h(Tab::container_height(cx))
- .max_w_full()
- .flex_none()
- .justify_between()
- .gap_2()
- .bg(cx.theme().colors().tab_bar_background)
- .border_b_1()
- .border_color(cx.theme().colors().border)
- .child(
- h_flex()
- .size_full()
- .gap(DynamicSpacing::Base04.rems(cx))
- .pl(DynamicSpacing::Base04.rems(cx))
- .child(match &self.active_view {
- ActiveView::History { .. } | ActiveView::Configuration => {
- self.render_toolbar_back_button(cx).into_any_element()
+ let is_in_history_or_config = matches!(
+ &self.active_view,
+ ActiveView::History { .. } | ActiveView::Configuration
+ );
+
+ let use_v2_empty_toolbar = has_v2_flag && is_empty_state && !is_in_history_or_config;
+
+ if use_v2_empty_toolbar {
+ let (chevron_icon, icon_color, label_color) =
+ if self.new_thread_menu_handle.is_deployed() {
+ (IconName::ChevronUp, Color::Accent, Color::Accent)
+ } else {
+ (IconName::ChevronDown, Color::Muted, Color::Default)
+ };
+
+ let agent_icon_element: AnyElement =
+ if let Some(icon_path) = selected_agent_custom_icon_for_button {
+ Icon::from_external_svg(icon_path)
+ .size(IconSize::Small)
+ .color(icon_color)
+ .into_any_element()
+ } else {
+ let icon_name = selected_agent_builtin_icon.unwrap_or(IconName::ZedAgent);
+ Icon::new(icon_name)
+ .size(IconSize::Small)
+ .color(icon_color)
+ .into_any_element()
+ };
+
+ let agent_selector_button = ButtonLike::new("agent-selector-trigger")
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent))
+ .child(
+ h_flex()
+ .gap_1()
+ .child(agent_icon_element)
+ .child(Label::new(selected_agent_label).color(label_color))
+ .child(
+ Icon::new(chevron_icon)
+ .color(icon_color)
+ .size(IconSize::XSmall),
+ ),
+ );
+
+ let agent_selector_menu = PopoverMenu::new("new_thread_menu")
+ .trigger(agent_selector_button)
+ .menu({
+ let builder = new_thread_menu_builder.clone();
+ move |window, cx| builder(window, cx)
+ })
+ .with_handle(self.new_thread_menu_handle.clone())
+ .anchor(Corner::TopLeft)
+ .offset(gpui::Point {
+ x: px(1.0),
+ y: px(1.0),
+ });
+
+ h_flex()
+ .id("agent-panel-toolbar")
+ .h(Tab::container_height(cx))
+ .max_w_full()
+ .flex_none()
+ .justify_between()
+ .gap_2()
+ .bg(cx.theme().colors().tab_bar_background)
+ .border_b_1()
+ .border_color(cx.theme().colors().border)
+ .child(
+ h_flex()
+ .size_full()
+ .gap(DynamicSpacing::Base04.rems(cx))
+ .pl(DynamicSpacing::Base04.rems(cx))
+ .child(selected_agent)
+ .child(agent_selector_menu)
+ .child(self.render_start_thread_in_selector(cx)),
+ )
+ .child(
+ h_flex()
+ .flex_none()
+ .gap(DynamicSpacing::Base02.rems(cx))
+ .pl(DynamicSpacing::Base04.rems(cx))
+ .pr(DynamicSpacing::Base06.rems(cx))
+ .when(show_history_menu, |this| {
+ this.child(self.render_recent_entries_menu(
+ IconName::MenuAltTemp,
+ Corner::TopRight,
+ cx,
+ ))
+ })
+ .child(self.render_panel_options_menu(window, cx)),
+ )
+ .into_any_element()
+ } else {
+ let new_thread_menu = PopoverMenu::new("new_thread_menu")
+ .trigger_with_tooltip(
+ IconButton::new("new_thread_menu_btn", IconName::Plus)
+ .icon_size(IconSize::Small),
+ {
+ move |_window, cx| {
+ Tooltip::for_action_in(
+ "New Thread\u{2026}",
+ &ToggleNewThreadMenu,
+ &focus_handle,
+ cx,
+ )
}
- _ => selected_agent.into_any_element(),
- })
- .child(self.render_title_view(window, cx)),
- )
- .child(
- h_flex()
- .flex_none()
- .gap(DynamicSpacing::Base02.rems(cx))
- .pl(DynamicSpacing::Base04.rems(cx))
- .pr(DynamicSpacing::Base06.rems(cx))
- .when(
- has_v2_flag && !self.active_thread_has_messages(cx),
- |this| this.child(self.render_start_thread_in_selector(cx)),
- )
- .child(new_thread_menu)
- .when(show_history_menu, |this| {
- this.child(self.render_recent_entries_menu(
- IconName::MenuAltTemp,
- Corner::TopRight,
- cx,
- ))
- })
- .child(self.render_panel_options_menu(window, cx)),
- )
+ },
+ )
+ .anchor(Corner::TopRight)
+ .with_handle(self.new_thread_menu_handle.clone())
+ .menu(move |window, cx| new_thread_menu_builder(window, cx));
+
+ h_flex()
+ .id("agent-panel-toolbar")
+ .h(Tab::container_height(cx))
+ .max_w_full()
+ .flex_none()
+ .justify_between()
+ .gap_2()
+ .bg(cx.theme().colors().tab_bar_background)
+ .border_b_1()
+ .border_color(cx.theme().colors().border)
+ .child(
+ h_flex()
+ .size_full()
+ .gap(DynamicSpacing::Base04.rems(cx))
+ .pl(DynamicSpacing::Base04.rems(cx))
+ .child(match &self.active_view {
+ ActiveView::History { .. } | ActiveView::Configuration => {
+ self.render_toolbar_back_button(cx).into_any_element()
+ }
+ _ => selected_agent.into_any_element(),
+ })
+ .child(self.render_title_view(window, cx)),
+ )
+ .child(
+ h_flex()
+ .flex_none()
+ .gap(DynamicSpacing::Base02.rems(cx))
+ .pl(DynamicSpacing::Base04.rems(cx))
+ .pr(DynamicSpacing::Base06.rems(cx))
+ .child(new_thread_menu)
+ .when(show_history_menu, |this| {
+ this.child(self.render_recent_entries_menu(
+ IconName::MenuAltTemp,
+ Corner::TopRight,
+ cx,
+ ))
+ })
+ .child(self.render_panel_options_menu(window, cx)),
+ )
+ .into_any_element()
+ }
}
fn render_worktree_creation_status(&self, cx: &mut Context<Self>) -> Option<AnyElement> {