From f4609c04eb7b908f7565f6e89c54d968452a8cf0 Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Wed, 15 Oct 2025 19:50:44 -0300 Subject: [PATCH] agent: Improve pickers and their triggers styles in the panel (#40284) Making all triggers have the same style when the picker is open (including changing the icon when the picker opens on top of the trigger). Also removed the footer from the ACP model selector given there's nothing to consider when that's the case; users can only configure LLM providers when using Zed's built-in agent. Release Notes: - N/A --- crates/agent_ui/src/acp/mode_selector.rs | 9 ++++- crates/agent_ui/src/acp/model_selector.rs | 38 ++----------------- .../src/acp/model_selector_popover.rs | 14 +++---- crates/agent_ui/src/agent_model_selector.rs | 20 +++++++--- crates/agent_ui/src/profile_selector.rs | 8 +++- crates/agent_ui/src/text_thread_editor.rs | 27 +++++++------ crates/picker/src/popover_menu.rs | 21 ++++++---- 7 files changed, 65 insertions(+), 72 deletions(-) diff --git a/crates/agent_ui/src/acp/mode_selector.rs b/crates/agent_ui/src/acp/mode_selector.rs index 410874126665b7d622c7cf45e81596dce7f96823..dda33bd17dbdd166d95ab4554b882fd440a067a5 100644 --- a/crates/agent_ui/src/acp/mode_selector.rs +++ b/crates/agent_ui/src/acp/mode_selector.rs @@ -174,11 +174,16 @@ impl Render for ModeSelector { let this = cx.entity(); + let icon = if self.menu_handle.is_deployed() { + IconName::ChevronUp + } else { + IconName::ChevronDown + }; + let trigger_button = Button::new("mode-selector-trigger", current_mode_name) .label_size(LabelSize::Small) - .style(ButtonStyle::Subtle) .color(Color::Muted) - .icon(IconName::ChevronDown) + .icon(icon) .icon_size(IconSize::XSmall) .icon_position(IconPosition::End) .icon_color(Color::Muted) diff --git a/crates/agent_ui/src/acp/model_selector.rs b/crates/agent_ui/src/acp/model_selector.rs index 381bdb01edec49e222c9bd9b3a97ce9ba21a9789..45fec558720fc5e88548f6dd7bc24fe624a908f5 100644 --- a/crates/agent_ui/src/acp/model_selector.rs +++ b/crates/agent_ui/src/acp/model_selector.rs @@ -5,12 +5,12 @@ use anyhow::Result; use collections::IndexMap; use futures::FutureExt; use fuzzy::{StringMatchCandidate, match_strings}; -use gpui::{Action, AsyncWindowContext, BackgroundExecutor, DismissEvent, Task, WeakEntity}; +use gpui::{AsyncWindowContext, BackgroundExecutor, DismissEvent, Task, WeakEntity}; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate}; use ui::{ - AnyElement, App, Context, DocumentationAside, DocumentationEdge, DocumentationSide, - IntoElement, ListItem, ListItemSpacing, SharedString, Window, prelude::*, rems, + DocumentationAside, DocumentationEdge, DocumentationSide, IntoElement, ListItem, + ListItemSpacing, prelude::*, }; use util::ResultExt; @@ -278,36 +278,6 @@ impl PickerDelegate for AcpModelPickerDelegate { } } - fn render_footer( - &self, - _: &mut Window, - cx: &mut Context>, - ) -> Option { - Some( - h_flex() - .w_full() - .border_t_1() - .border_color(cx.theme().colors().border_variant) - .p_1() - .gap_4() - .justify_between() - .child( - Button::new("configure", "Configure") - .icon(IconName::Settings) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .on_click(|_, window, cx| { - window.dispatch_action( - zed_actions::agent::OpenSettings.boxed_clone(), - cx, - ); - }), - ) - .into_any(), - ) - } - fn documentation_aside( &self, _window: &mut Window, @@ -317,7 +287,7 @@ impl PickerDelegate for AcpModelPickerDelegate { let description = description.clone(); DocumentationAside::new( DocumentationSide::Left, - DocumentationEdge::Bottom, + DocumentationEdge::Top, Rc::new(move |_| Label::new(description.clone()).into_any_element()), ) }) diff --git a/crates/agent_ui/src/acp/model_selector_popover.rs b/crates/agent_ui/src/acp/model_selector_popover.rs index 55f530c81b1cead74fd4ec4f6cc29ececcf2bf7e..474125d69d3173db82510ab80261b93474e0386c 100644 --- a/crates/agent_ui/src/acp/model_selector_popover.rs +++ b/crates/agent_ui/src/acp/model_selector_popover.rs @@ -57,30 +57,26 @@ impl Render for AcpModelSelectorPopover { let focus_handle = self.focus_handle.clone(); - let color = if self.menu_handle.is_deployed() { - Color::Accent + let (color, icon) = if self.menu_handle.is_deployed() { + (Color::Accent, IconName::ChevronUp) } else { - Color::Muted + (Color::Muted, IconName::ChevronDown) }; PickerPopoverMenu::new( self.selector.clone(), ButtonLike::new("active-model") + .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .when_some(model_icon, |this, icon| { this.child(Icon::new(icon).color(color).size(IconSize::XSmall)) }) - .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .child( Label::new(model_name) .color(color) .size(LabelSize::Small) .ml_0p5(), ) - .child( - Icon::new(IconName::ChevronDown) - .color(Color::Muted) - .size(IconSize::XSmall), - ), + .child(Icon::new(icon).color(Color::Muted).size(IconSize::XSmall)), move |window, cx| { Tooltip::for_action_in( "Change Model", diff --git a/crates/agent_ui/src/agent_model_selector.rs b/crates/agent_ui/src/agent_model_selector.rs index fe25cadc3c1df785c89318882a246e2209cb42e6..c368ee73b32154550304898938372a278a8b1bba 100644 --- a/crates/agent_ui/src/agent_model_selector.rs +++ b/crates/agent_ui/src/agent_model_selector.rs @@ -7,7 +7,7 @@ use gpui::{Entity, FocusHandle, SharedString}; use picker::popover_menu::PickerPopoverMenu; use settings::update_settings_file; use std::sync::Arc; -use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*}; +use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*}; use zed_actions::agent::ToggleModelSelector; pub struct AgentModelSelector { @@ -70,6 +70,11 @@ impl Render for AgentModelSelector { .unwrap_or_else(|| SharedString::from("Select a Model")); let provider_icon = model.as_ref().map(|model| model.provider.icon()); + let color = if self.menu_handle.is_deployed() { + Color::Accent + } else { + Color::Muted + }; let focus_handle = self.focus_handle.clone(); @@ -77,17 +82,18 @@ impl Render for AgentModelSelector { self.selector.clone(), ButtonLike::new("active-model") .when_some(provider_icon, |this, icon| { - this.child(Icon::new(icon).color(Color::Muted).size(IconSize::XSmall)) + this.child(Icon::new(icon).color(color).size(IconSize::XSmall)) }) + .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .child( Label::new(model_name) - .color(Color::Muted) + .color(color) .size(LabelSize::Small) .ml_0p5(), ) .child( Icon::new(IconName::ChevronDown) - .color(Color::Muted) + .color(color) .size(IconSize::XSmall), ), move |window, cx| { @@ -99,10 +105,14 @@ impl Render for AgentModelSelector { cx, ) }, - gpui::Corner::BottomRight, + gpui::Corner::TopRight, cx, ) .with_handle(self.menu_handle.clone()) + .offset(gpui::Point { + x: px(0.0), + y: px(2.0), + }) .render(window, cx) } } diff --git a/crates/agent_ui/src/profile_selector.rs b/crates/agent_ui/src/profile_selector.rs index d52b2436d68c4b43cd7e7ec73b27007e34a43153..ef9e1e691753a4a005bd1bc91b60a75c7716132f 100644 --- a/crates/agent_ui/src/profile_selector.rs +++ b/crates/agent_ui/src/profile_selector.rs @@ -144,10 +144,16 @@ impl Render for ProfileSelector { .unwrap_or_else(|| "Unknown".into()); let focus_handle = self.focus_handle.clone(); + let icon = if self.picker_handle.is_deployed() { + IconName::ChevronUp + } else { + IconName::ChevronDown + }; + let trigger_button = Button::new("profile-selector", selected_profile) .label_size(LabelSize::Small) .color(Color::Muted) - .icon(IconName::ChevronDown) + .icon(icon) .icon_size(IconSize::XSmall) .icon_position(IconPosition::End) .icon_color(Color::Muted) diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index e1265e923ef06b0bad945d3751dfbf24e0f1ee00..5e4ecf3db171e0fb7b9d465482e2858e04dbe0d1 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/crates/agent_ui/src/text_thread_editor.rs @@ -1977,7 +1977,9 @@ impl TextThreadEditor { cx.entity().downgrade(), IconButton::new("trigger", IconName::Plus) .icon_size(IconSize::Small) - .icon_color(Color::Muted), + .icon_color(Color::Muted) + .selected_icon_color(Color::Accent) + .selected_style(ButtonStyle::Filled), move |window, cx| { Tooltip::with_meta( "Add Context", @@ -2052,30 +2054,27 @@ impl TextThreadEditor { }; let focus_handle = self.editor().focus_handle(cx); + let (color, icon) = if self.language_model_selector_menu_handle.is_deployed() { + (Color::Accent, IconName::ChevronUp) + } else { + (Color::Muted, IconName::ChevronDown) + }; PickerPopoverMenu::new( self.language_model_selector.clone(), ButtonLike::new("active-model") - .style(ButtonStyle::Subtle) + .selected_style(ButtonStyle::Tinted(TintColor::Accent)) .child( h_flex() .gap_0p5() - .child( - Icon::new(provider_icon) - .color(Color::Muted) - .size(IconSize::XSmall), - ) + .child(Icon::new(provider_icon).color(color).size(IconSize::XSmall)) .child( Label::new(model_name) - .color(Color::Muted) + .color(color) .size(LabelSize::Small) .ml_0p5(), ) - .child( - Icon::new(IconName::ChevronDown) - .color(Color::Muted) - .size(IconSize::XSmall), - ), + .child(Icon::new(icon).color(color).size(IconSize::XSmall)), ), move |window, cx| { Tooltip::for_action_in( @@ -2086,7 +2085,7 @@ impl TextThreadEditor { cx, ) }, - gpui::Corner::BottomLeft, + gpui::Corner::BottomRight, cx, ) .with_handle(self.language_model_selector_menu_handle.clone()) diff --git a/crates/picker/src/popover_menu.rs b/crates/picker/src/popover_menu.rs index baf0918fd6c8e20211d04a150af9220cb2d66839..42eedb2492149aa56de527e38fcf4f2b0e4da608 100644 --- a/crates/picker/src/popover_menu.rs +++ b/crates/picker/src/popover_menu.rs @@ -1,9 +1,9 @@ use gpui::{ - AnyView, Corner, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, + AnyView, Corner, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Point, + Subscription, }; use ui::{ - App, ButtonCommon, FluentBuilder as _, IntoElement, PopoverMenu, PopoverMenuHandle, - PopoverTrigger, RenderOnce, Window, px, + FluentBuilder as _, IntoElement, PopoverMenu, PopoverMenuHandle, PopoverTrigger, prelude::*, }; use crate::{Picker, PickerDelegate}; @@ -19,6 +19,7 @@ where tooltip: TT, handle: Option>>, anchor: Corner, + offset: Option>, _subscriptions: Vec, } @@ -43,6 +44,10 @@ where trigger, tooltip, handle: None, + offset: Some(Point { + x: px(0.0), + y: px(-2.0), + }), anchor, } } @@ -51,6 +56,11 @@ where self.handle = Some(handle); self } + + pub fn offset(mut self, offset: Point) -> Self { + self.offset = Some(offset); + self + } } impl EventEmitter for PickerPopoverMenu @@ -86,9 +96,6 @@ where .trigger_with_tooltip(self.trigger, self.tooltip) .anchor(self.anchor) .when_some(self.handle, |menu, handle| menu.with_handle(handle)) - .offset(gpui::Point { - x: px(0.0), - y: px(-2.0), - }) + .when_some(self.offset, |menu, offset| menu.offset(offset)) } }