Fix language model selector (#26138)

This PR fixes the language model selector.

I tried to piece together the state prior to #25697 (the state it was in
at 11838cf89e) while retaining unrelated
changes that happened since then.

Release Notes:

- Fixed an issue where language models would not be authenticated until
after the model selector was opened (Preview only).
This commit is contained in:
Marshall Bowers 2025-03-05 12:48:10 -05:00 committed by GitHub
parent ad9c508a72
commit 66784c0b3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 289 additions and 200 deletions

View file

@ -1,8 +1,8 @@
use std::{rc::Rc, sync::Arc};
use std::sync::Arc;
use feature_flags::ZedPro;
use gpui::{
action_with_deprecated_aliases, Action, AnyElement, App, Corner, DismissEvent, Entity,
action_with_deprecated_aliases, Action, AnyElement, AnyView, App, Corner, DismissEvent, Entity,
EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity,
};
use language_model::{
@ -10,10 +10,7 @@ use language_model::{
};
use picker::{Picker, PickerDelegate};
use proto::Plan;
use ui::{
prelude::*, ButtonLike, IconButtonShape, ListItem, ListItemSpacing, PopoverMenu,
PopoverMenuHandle, Tooltip,
};
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
use workspace::ShowConfiguration;
action_with_deprecated_aliases!(
@ -31,7 +28,6 @@ pub struct LanguageModelSelector {
/// The task used to update the picker's matches when there is a change to
/// the language model registry.
update_matches_task: Option<Task<()>>,
popover_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
_authenticate_all_providers_task: Task<()>,
_subscriptions: Vec<Subscription>,
}
@ -63,7 +59,6 @@ impl LanguageModelSelector {
LanguageModelSelector {
picker,
update_matches_task: None,
popover_menu_handle: PopoverMenuHandle::default(),
_authenticate_all_providers_task: Self::authenticate_all_providers(cx),
_subscriptions: vec![cx.subscribe_in(
&LanguageModelRegistry::global(cx),
@ -73,15 +68,6 @@ impl LanguageModelSelector {
}
}
pub fn toggle_model_selector(
&mut self,
_: &ToggleModelSelector,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.popover_menu_handle.toggle(window, cx);
}
fn handle_language_model_registry_event(
&mut self,
_registry: &Entity<LanguageModelRegistry>,
@ -201,6 +187,65 @@ impl Render for LanguageModelSelector {
}
}
#[derive(IntoElement)]
pub struct LanguageModelSelectorPopoverMenu<T, TT>
where
T: PopoverTrigger + ButtonCommon,
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
{
language_model_selector: Entity<LanguageModelSelector>,
trigger: T,
tooltip: TT,
handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
anchor: Corner,
}
impl<T, TT> LanguageModelSelectorPopoverMenu<T, TT>
where
T: PopoverTrigger + ButtonCommon,
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
{
pub fn new(
language_model_selector: Entity<LanguageModelSelector>,
trigger: T,
tooltip: TT,
anchor: Corner,
) -> Self {
Self {
language_model_selector,
trigger,
tooltip,
handle: None,
anchor,
}
}
pub fn with_handle(mut self, handle: PopoverMenuHandle<LanguageModelSelector>) -> Self {
self.handle = Some(handle);
self
}
}
impl<T, TT> RenderOnce for LanguageModelSelectorPopoverMenu<T, TT>
where
T: PopoverTrigger + ButtonCommon,
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
{
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let language_model_selector = self.language_model_selector.clone();
PopoverMenu::new("model-switcher")
.menu(move |_window, _cx| Some(language_model_selector.clone()))
.trigger_with_tooltip(self.trigger, self.tooltip)
.anchor(self.anchor)
.when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
.offset(gpui::Point {
x: px(0.0),
y: px(-2.0),
})
}
}
#[derive(Clone)]
struct ModelInfo {
model: Arc<dyn LanguageModel>,
@ -482,114 +527,3 @@ impl PickerDelegate for LanguageModelPickerDelegate {
)
}
}
pub fn inline_language_model_selector(
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &App) + 'static,
) -> PopoverMenu<LanguageModelSelector> {
let on_model_changed = Rc::new(on_model_changed);
PopoverMenu::new("popover-button")
.menu(move |window, cx| {
Some(cx.new(|cx| {
LanguageModelSelector::new(
{
let on_model_changed = on_model_changed.clone();
move |model, cx| {
on_model_changed(model, cx);
}
},
window,
cx,
)
}))
})
.trigger_with_tooltip(
IconButton::new("context", IconName::SettingsAlt)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(Color::Muted),
move |window, cx| {
Tooltip::with_meta(
format!(
"Using {}",
LanguageModelRegistry::read_global(cx)
.active_model()
.map(|model| model.name().0)
.unwrap_or_else(|| "No model selected".into()),
),
None,
"Change Model",
window,
cx,
)
},
)
.anchor(gpui::Corner::TopRight)
.offset(gpui::Point {
x: px(0.0),
y: px(-2.0),
})
}
pub fn assistant_language_model_selector(
keybinding_target: FocusHandle,
menu_handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
cx: &App,
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &App) + 'static,
) -> PopoverMenu<LanguageModelSelector> {
let active_model = LanguageModelRegistry::read_global(cx).active_model();
let model_name = match active_model {
Some(model) => model.name().0,
_ => SharedString::from("No model selected"),
};
let on_model_changed = Rc::new(on_model_changed);
PopoverMenu::new("popover-button")
.menu(move |window, cx| {
Some(cx.new(|cx| {
LanguageModelSelector::new(
{
let on_model_changed = on_model_changed.clone();
move |model, cx| {
on_model_changed(model, cx);
}
},
window,
cx,
)
}))
})
.trigger_with_tooltip(
ButtonLike::new("active-model")
.style(ButtonStyle::Subtle)
.child(
h_flex()
.gap_0p5()
.child(
Label::new(model_name)
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(
Icon::new(IconName::ChevronDown)
.color(Color::Muted)
.size(IconSize::XSmall),
),
),
move |window, cx| {
Tooltip::for_action_in(
"Change Model",
&ToggleModelSelector,
&keybinding_target,
window,
cx,
)
},
)
.anchor(Corner::BottomRight)
.when_some(menu_handle, |el, handle| el.with_handle(handle))
.offset(gpui::Point {
x: px(0.0),
y: px(-2.0),
})
}