agent: Add design adjustments to message editor (#29891)

Danilo Leal created

- Removed unused `MessageBubbleDashed` icon
- Polished `Crosshair` icon SVG
- Added dropdown toggle keybinding to the profile selector tooltip
- Repositioned buttons at the message editor footer
- Updated buttons to use `Button` instead of `ButtonLike`

Release Notes:

- N/A

Change summary

assets/icons/crosshair.svg                   |  8 ++
assets/icons/message_bubble_dashed.svg       |  1 
crates/agent/src/assistant_model_selector.rs | 26 ++-----
crates/agent/src/message_editor.rs           |  8 +
crates/agent/src/profile_selector.rs         | 69 +++++++++++----------
crates/icons/src/icons.rs                    |  1 
6 files changed, 56 insertions(+), 57 deletions(-)

Detailed changes

assets/icons/crosshair.svg 🔗

@@ -1 +1,7 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-crosshair-icon lucide-crosshair"><circle cx="12" cy="12" r="10"/><line x1="22" x2="18" y1="12" y2="12"/><line x1="6" x2="2" y1="12" y2="12"/><line x1="12" x2="12" y1="6" y2="2"/><line x1="12" x2="12" y1="22" y2="18"/></svg>
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 12C9.76142 12 12 9.76142 12 7C12 4.23858 9.76142 2 7 2C4.23858 2 2 4.23858 2 7C2 9.76142 4.23858 12 7 12Z" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.5 7H9" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M4.5 7H2" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M7 4.5V2" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M7 11.5V9" stroke="black" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

assets/icons/message_bubble_dashed.svg 🔗

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle-dashed-icon lucide-message-circle-dashed"><path d="M13.5 3.1c-.5 0-1-.1-1.5-.1s-1 .1-1.5.1"/><path d="M19.3 6.8a10.45 10.45 0 0 0-2.1-2.1"/><path d="M20.9 13.5c.1-.5.1-1 .1-1.5s-.1-1-.1-1.5"/><path d="M17.2 19.3a10.45 10.45 0 0 0 2.1-2.1"/><path d="M10.5 20.9c.5.1 1 .1 1.5.1s1-.1 1.5-.1"/><path d="M3.5 17.5 2 22l4.5-1.5"/><path d="M3.1 10.5c0 .5-.1 1-.1 1.5s.1 1 .1 1.5"/><path d="M6.8 4.7a10.45 10.45 0 0 0-2.1 2.1"/></svg>

crates/agent/src/assistant_model_selector.rs 🔗

@@ -9,7 +9,7 @@ use language_model_selector::{
 };
 use settings::update_settings_file;
 use std::sync::Arc;
-use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
+use ui::{PopoverMenuHandle, Tooltip, prelude::*};
 
 #[derive(Clone)]
 pub enum ModelType {
@@ -110,23 +110,13 @@ impl Render for AssistantModelSelector {
 
         LanguageModelSelectorPopoverMenu::new(
             self.selector.clone(),
-            ButtonLike::new("active-model")
-                .style(ButtonStyle::Subtle)
-                .child(
-                    h_flex()
-                        .gap_0p5()
-                        .child(
-                            Label::new(model_name)
-                                .size(LabelSize::Small)
-                                .color(Color::Muted)
-                                .ml_1(),
-                        )
-                        .child(
-                            Icon::new(IconName::ChevronDown)
-                                .color(Color::Muted)
-                                .size(IconSize::XSmall),
-                        ),
-                ),
+            Button::new("active-model", model_name)
+                .label_size(LabelSize::Small)
+                .color(Color::Muted)
+                .icon(IconName::ChevronDown)
+                .icon_size(IconSize::XSmall)
+                .icon_position(IconPosition::End)
+                .icon_color(Color::Muted),
             move |window, cx| {
                 Tooltip::for_action_in(
                     "Change Model",

crates/agent/src/message_editor.rs 🔗

@@ -200,7 +200,8 @@ impl MessageEditor {
             model_selector,
             edits_expanded: false,
             editor_is_expanded: false,
-            profile_selector: cx.new(|cx| ProfileSelector::new(fs, thread_store, cx)),
+            profile_selector: cx
+                .new(|cx| ProfileSelector::new(fs, thread_store, editor.focus_handle(cx), cx)),
             last_estimated_token_count: None,
             update_token_count_task: None,
             _subscriptions: subscriptions,
@@ -463,6 +464,7 @@ impl MessageEditor {
                 workspace.is_being_followed(CollaboratorId::Agent)
             })
             .unwrap_or(false);
+
         IconButton::new("follow-agent", IconName::Crosshair)
             .icon_size(IconSize::Small)
             .icon_color(Color::Muted)
@@ -638,7 +640,7 @@ impl MessageEditor {
                                 h_flex()
                                     .gap_1()
                                     .child(self.render_follow_toggle(cx))
-                                    .child(self.profile_selector.clone()),
+                                    .children(self.render_max_mode_toggle(cx)),
                             )
                             .child(
                                 h_flex()
@@ -662,7 +664,7 @@ impl MessageEditor {
                                             }),
                                         )
                                     })
-                                    .children(self.render_max_mode_toggle(cx))
+                                    .child(self.profile_selector.clone())
                                     .child(self.model_selector.clone())
                                     .map({
                                         let focus_handle = focus_handle.clone();

crates/agent/src/profile_selector.rs 🔗

@@ -4,21 +4,20 @@ use assistant_settings::{
     AgentProfile, AgentProfileId, AssistantSettings, GroupedAgentProfiles, builtin_profiles,
 };
 use fs::Fs;
-use gpui::{Action, Entity, Subscription, WeakEntity, prelude::*};
+use gpui::{Action, Entity, FocusHandle, Subscription, WeakEntity, prelude::*};
 use language_model::LanguageModelRegistry;
 use settings::{Settings as _, SettingsStore, update_settings_file};
-use ui::{
-    ButtonLike, ContextMenu, ContextMenuEntry, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*,
-};
+use ui::{ContextMenu, ContextMenuEntry, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
 use util::ResultExt as _;
 
-use crate::{ManageProfiles, ThreadStore};
+use crate::{ManageProfiles, ThreadStore, ToggleProfileSelector};
 
 pub struct ProfileSelector {
     profiles: GroupedAgentProfiles,
     fs: Arc<dyn Fs>,
     thread_store: WeakEntity<ThreadStore>,
     menu_handle: PopoverMenuHandle<ContextMenu>,
+    focus_handle: FocusHandle,
     _subscriptions: Vec<Subscription>,
 }
 
@@ -26,6 +25,7 @@ impl ProfileSelector {
     pub fn new(
         fs: Arc<dyn Fs>,
         thread_store: WeakEntity<ThreadStore>,
+        focus_handle: FocusHandle,
         cx: &mut Context<Self>,
     ) -> Self {
         let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
@@ -37,6 +37,7 @@ impl ProfileSelector {
             fs,
             thread_store,
             menu_handle: PopoverMenuHandle::default(),
+            focus_handle,
             _subscriptions: vec![settings_subscription],
         }
     }
@@ -143,39 +144,41 @@ impl Render for ProfileSelector {
             .map_or(false, |default| default.model.supports_tools());
 
         let this = cx.entity().clone();
+        let focus_handle = self.focus_handle.clone();
+
+        let trigger_button = if supports_tools {
+            Button::new("profile-selector-model", selected_profile)
+                .label_size(LabelSize::Small)
+                .color(Color::Muted)
+                .icon(IconName::ChevronDown)
+                .icon_size(IconSize::XSmall)
+                .icon_position(IconPosition::End)
+                .icon_color(Color::Muted)
+        } else {
+            Button::new("tools-not-supported-button", "No Tools")
+                .disabled(true)
+                .label_size(LabelSize::Small)
+                .color(Color::Muted)
+                .tooltip(Tooltip::text("The current model does not support tools."))
+        };
 
         PopoverMenu::new("profile-selector")
-            .menu(move |window, cx| {
-                Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
-            })
-            .trigger(if supports_tools {
-                ButtonLike::new("profile-selector-button").child(
-                    h_flex()
-                        .gap_1()
-                        .child(
-                            Label::new(selected_profile)
-                                .size(LabelSize::Small)
-                                .color(Color::Muted),
-                        )
-                        .child(
-                            Icon::new(IconName::ChevronDown)
-                                .size(IconSize::XSmall)
-                                .color(Color::Muted),
-                        ),
-                )
-            } else {
-                ButtonLike::new("tools-not-supported-button")
-                    .disabled(true)
-                    .child(
-                        h_flex().gap_1().child(
-                            Label::new("No Tools")
-                                .size(LabelSize::Small)
-                                .color(Color::Muted),
-                        ),
+            .trigger_with_tooltip(trigger_button, {
+                let focus_handle = focus_handle.clone();
+                move |window, cx| {
+                    Tooltip::for_action_in(
+                        "Toggle Profile Menu",
+                        &ToggleProfileSelector,
+                        &focus_handle,
+                        window,
+                        cx,
                     )
-                    .tooltip(Tooltip::text("The current model does not support tools."))
+                }
             })
             .anchor(gpui::Corner::BottomRight)
             .with_handle(self.menu_handle.clone())
+            .menu(move |window, cx| {
+                Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
+            })
     }
 }

crates/icons/src/icons.rs 🔗

@@ -160,7 +160,6 @@ pub enum IconName {
     Maximize,
     Menu,
     MenuAlt,
-    MessageBubbleDashed,
     MessageBubbles,
     Mic,
     MicMute,