agent: Show provider icon in model selectors (#30595)

Artem Loenko , Danilo Leal , and Danilo Leal created

I often switch between models, and I believe many people do. Currently,
it is difficult to determine which provider offers the selected model
because the same models are available from different providers. I
propose a simple change to the selector so that users can distinguish
between providers from the model they have chosen.

As a side note, I would actually prefer to have a text label with the
provider’s name next to the model name in the selector. However, I
understand that this is too opinionated and takes up too much space.

| Before | After |
| ------ | ------ |
|
![before_inline_assist](https://github.com/user-attachments/assets/35617ba5-e8d4-4dab-a997-f7286f73f2bf)
|
![after_inline_assist](https://github.com/user-attachments/assets/c37c81b4-73e4-49e2-955d-b8543b2855ad)
|
|
![before_text_thread](https://github.com/user-attachments/assets/af90303b-12d6-402c-90a5-8b36cd97396e)
|
![after_text_thread](https://github.com/user-attachments/assets/bca5b423-f12b-4eaf-a82e-424d09b7f447)
|
|
![before_thread](https://github.com/user-attachments/assets/0946775f-1d52-437b-a217-9708ee2e789a)
|
![after_thread](https://github.com/user-attachments/assets/f5e53968-9020-446f-9d5e-653ae9fdea3e)
|

Release Notes:

- The model selector has been improved with a provider icon, making it
easier to distinguish between providers.

---------

Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/agent_ui/src/agent_model_selector.rs | 31 +++++++++++++++++-----
crates/agent_ui/src/text_thread_editor.rs   | 19 ++++++++++++-
2 files changed, 40 insertions(+), 10 deletions(-)

Detailed changes

crates/agent_ui/src/agent_model_selector.rs 🔗

@@ -11,7 +11,7 @@ use language_model::{ConfiguredModel, LanguageModelRegistry};
 use picker::popover_menu::PickerPopoverMenu;
 use settings::update_settings_file;
 use std::sync::Arc;
-use ui::{PopoverMenuHandle, Tooltip, prelude::*};
+use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
 
 pub struct AgentModelSelector {
     selector: Entity<LanguageModelSelector>,
@@ -94,20 +94,35 @@ impl Render for AgentModelSelector {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let model = self.selector.read(cx).delegate.active_model(cx);
         let model_name = model
+            .as_ref()
             .map(|model| model.model.name().0)
             .unwrap_or_else(|| SharedString::from("No model selected"));
+        let provider_icon = model
+            .as_ref()
+            .map(|model| model.provider.icon())
+            .unwrap_or_else(|| IconName::Ai);
 
         let focus_handle = self.focus_handle.clone();
 
         PickerPopoverMenu::new(
             self.selector.clone(),
-            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),
+            ButtonLike::new("active-model")
+                .child(
+                    Icon::new(provider_icon)
+                        .color(Color::Muted)
+                        .size(IconSize::XSmall),
+                )
+                .child(
+                    Label::new(model_name)
+                        .color(Color::Muted)
+                        .size(LabelSize::Small)
+                        .ml_0p5(),
+                )
+                .child(
+                    Icon::new(IconName::ChevronDown)
+                        .color(Color::Muted)
+                        .size(IconSize::XSmall),
+                ),
             move |window, cx| {
                 Tooltip::for_action_in(
                     "Change Model",

crates/agent_ui/src/text_thread_editor.rs 🔗

@@ -2120,12 +2120,21 @@ impl TextThreadEditor {
         let active_model = LanguageModelRegistry::read_global(cx)
             .default_model()
             .map(|default| default.model);
-        let focus_handle = self.editor().focus_handle(cx).clone();
         let model_name = match active_model {
             Some(model) => model.name().0,
             None => SharedString::from("No model selected"),
         };
 
+        let active_provider = LanguageModelRegistry::read_global(cx)
+            .default_model()
+            .map(|default| default.provider);
+        let provider_icon = match active_provider {
+            Some(provider) => provider.icon(),
+            None => IconName::Ai,
+        };
+
+        let focus_handle = self.editor().focus_handle(cx).clone();
+
         PickerPopoverMenu::new(
             self.language_model_selector.clone(),
             ButtonLike::new("active-model")
@@ -2133,10 +2142,16 @@ impl TextThreadEditor {
                 .child(
                     h_flex()
                         .gap_0p5()
+                        .child(
+                            Icon::new(provider_icon)
+                                .color(Color::Muted)
+                                .size(IconSize::XSmall),
+                        )
                         .child(
                             Label::new(model_name)
+                                .color(Color::Muted)
                                 .size(LabelSize::Small)
-                                .color(Color::Muted),
+                                .ml_0p5(),
                         )
                         .child(
                             Icon::new(IconName::ChevronDown)