assistant: Improve the language model selector (#25125)

This PR includes change such as:

- Ensures the popover width is fixed/not dancing around
- Ensures the popover is not obscuring the trigger in the buffer and
terminal inline assistant scenarios
- Removes ellipsis from the trigger button label
- Ensures the scrollbar doesn't hide the check icon

| Terminal | Prompt Editor | Buffer |
|--------|--------|--------|
| ![Screenshot 2025-02-18 at 8 43
46 PM](https://github.com/user-attachments/assets/9cdfbaf1-f27e-4f48-877e-9cf61767ecee)
| ![Screenshot 2025-02-18 at 8 43
49 PM](https://github.com/user-attachments/assets/7abf9be2-bd2a-43d7-9a5d-d665e7e9fda3)
| ![Screenshot 2025-02-18 at 8 43
52 PM](https://github.com/user-attachments/assets/017bbdb3-185a-4bf6-9005-018ecafef9dd)
|

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-02-18 21:01:09 -03:00 committed by GitHub
parent a6006afdd7
commit 2627a5fdbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 30 additions and 20 deletions

View file

@ -1610,6 +1610,7 @@ impl Render for PromptEditor {
cx, cx,
) )
}, },
gpui::Corner::TopRight,
)) ))
.map(|el| { .map(|el| {
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else { let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {

View file

@ -662,6 +662,7 @@ impl Render for PromptEditor {
cx, cx,
) )
}, },
gpui::Corner::TopRight,
)) ))
.children( .children(
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status { if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {

View file

@ -61,13 +61,9 @@ impl Render for AssistantModelSelector {
h_flex() h_flex()
.gap_0p5() .gap_0p5()
.child( .child(
div().max_w_32().child( Label::new(model_name)
Label::new(model_name) .size(LabelSize::Small)
.size(LabelSize::Small) .color(Color::Muted),
.color(Color::Muted)
.text_ellipsis()
.into_any_element(),
),
) )
.child( .child(
Icon::new(IconName::ChevronDown) Icon::new(IconName::ChevronDown)
@ -84,6 +80,7 @@ impl Render for AssistantModelSelector {
cx, cx,
) )
}, },
gpui::Corner::BottomRight,
) )
.with_handle(self.menu_handle.clone()) .with_handle(self.menu_handle.clone())
} }

View file

@ -2404,13 +2404,9 @@ impl ContextEditor {
h_flex() h_flex()
.gap_0p5() .gap_0p5()
.child( .child(
div().max_w_32().child( Label::new(model_name)
Label::new(model_name) .size(LabelSize::Small)
.size(LabelSize::Small) .color(Color::Muted),
.color(Color::Muted)
.text_ellipsis()
.into_any_element(),
),
) )
.child( .child(
Icon::new(IconName::ChevronDown) Icon::new(IconName::ChevronDown)
@ -2427,6 +2423,7 @@ impl ContextEditor {
cx, cx,
) )
}, },
gpui::Corner::BottomLeft,
) )
.with_handle(self.language_model_selector_menu_handle.clone()) .with_handle(self.language_model_selector_menu_handle.clone())
} }

View file

@ -2,8 +2,8 @@ use std::sync::Arc;
use feature_flags::ZedPro; use feature_flags::ZedPro;
use gpui::{ use gpui::{
Action, AnyElement, AnyView, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Action, AnyElement, AnyView, App, Corner, DismissEvent, Entity, EventEmitter, FocusHandle,
Subscription, Task, WeakEntity, Focusable, Subscription, Task, WeakEntity,
}; };
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry}; use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};
@ -37,12 +37,13 @@ impl LanguageModelSelector {
on_model_changed: on_model_changed.clone(), on_model_changed: on_model_changed.clone(),
all_models: all_models.clone(), all_models: all_models.clone(),
filtered_models: all_models, filtered_models: all_models,
selected_index: 0, selected_index: Self::get_active_model_index(cx),
}; };
let picker = cx.new(|cx| { let picker = cx.new(|cx| {
Picker::uniform_list(delegate, window, cx) Picker::uniform_list(delegate, window, cx)
.show_scrollbar(true) .show_scrollbar(true)
.width(rems(20.))
.max_height(Some(rems(20.).into())) .max_height(Some(rems(20.).into()))
}); });
@ -100,6 +101,16 @@ impl LanguageModelSelector {
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
fn get_active_model_index(cx: &App) -> usize {
let active_model = LanguageModelRegistry::read_global(cx).active_model();
Self::all_models(cx)
.iter()
.position(|model_info| {
Some(model_info.model.id()) == active_model.as_ref().map(|model| model.id())
})
.unwrap_or(0)
}
} }
impl EventEmitter<DismissEvent> for LanguageModelSelector {} impl EventEmitter<DismissEvent> for LanguageModelSelector {}
@ -126,6 +137,7 @@ where
trigger: T, trigger: T,
tooltip: TT, tooltip: TT,
handle: Option<PopoverMenuHandle<LanguageModelSelector>>, handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
anchor: Corner,
} }
impl<T, TT> LanguageModelSelectorPopoverMenu<T, TT> impl<T, TT> LanguageModelSelectorPopoverMenu<T, TT>
@ -137,12 +149,14 @@ where
language_model_selector: Entity<LanguageModelSelector>, language_model_selector: Entity<LanguageModelSelector>,
trigger: T, trigger: T,
tooltip: TT, tooltip: TT,
anchor: Corner,
) -> Self { ) -> Self {
Self { Self {
language_model_selector, language_model_selector,
trigger, trigger,
tooltip, tooltip,
handle: None, handle: None,
anchor,
} }
} }
@ -163,7 +177,7 @@ where
PopoverMenu::new("model-switcher") PopoverMenu::new("model-switcher")
.menu(move |_window, _cx| Some(language_model_selector.clone())) .menu(move |_window, _cx| Some(language_model_selector.clone()))
.trigger_with_tooltip(self.trigger, self.tooltip) .trigger_with_tooltip(self.trigger, self.tooltip)
.anchor(gpui::Corner::BottomRight) .anchor(self.anchor)
.when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle)) .when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
.offset(gpui::Point { .offset(gpui::Point {
x: px(0.0), x: px(0.0),
@ -360,7 +374,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
.items_center() .items_center()
.gap_1p5() .gap_1p5()
.pl_0p5() .pl_0p5()
.min_w(px(240.)) .w(px(240.))
.child( .child(
div().max_w_40().child( div().max_w_40().child(
Label::new(model_info.model.name().0.clone()).text_ellipsis(), Label::new(model_info.model.name().0.clone()).text_ellipsis(),
@ -387,7 +401,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
}), }),
), ),
) )
.end_slot(div().when(is_selected, |this| { .end_slot(div().pr_3().when(is_selected, |this| {
this.child( this.child(
Icon::new(IconName::Check) Icon::new(IconName::Check)
.color(Color::Accent) .color(Color::Accent)