1use crate::{
2 ModelUsageContext,
3 language_model_selector::{LanguageModelSelector, language_model_selector},
4};
5use fs::Fs;
6use gpui::{Entity, FocusHandle, SharedString};
7use picker::popover_menu::PickerPopoverMenu;
8use settings::update_settings_file;
9use std::sync::Arc;
10use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*};
11use zed_actions::agent::ToggleModelSelector;
12
13pub struct AgentModelSelector {
14 selector: Entity<LanguageModelSelector>,
15 menu_handle: PopoverMenuHandle<LanguageModelSelector>,
16 focus_handle: FocusHandle,
17}
18
19impl AgentModelSelector {
20 pub(crate) fn new(
21 fs: Arc<dyn Fs>,
22 menu_handle: PopoverMenuHandle<LanguageModelSelector>,
23 focus_handle: FocusHandle,
24 model_usage_context: ModelUsageContext,
25 window: &mut Window,
26 cx: &mut Context<Self>,
27 ) -> Self {
28 Self {
29 selector: cx.new(move |cx| {
30 let fs = fs.clone();
31 language_model_selector(
32 {
33 let model_context = model_usage_context.clone();
34 move |cx| model_context.configured_model(cx)
35 },
36 move |model, cx| {
37 let provider = model.provider_id().0.to_string();
38 let model_id = model.id().0.to_string();
39 match &model_usage_context {
40 ModelUsageContext::InlineAssistant => {
41 update_settings_file(fs.clone(), cx, move |settings, _cx| {
42 settings
43 .agent
44 .get_or_insert_default()
45 .set_inline_assistant_model(provider.clone(), model_id);
46 });
47 }
48 }
49 },
50 true, // Use popover styles for picker
51 window,
52 cx,
53 )
54 }),
55 menu_handle,
56 focus_handle,
57 }
58 }
59
60 pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
61 self.menu_handle.toggle(window, cx);
62 }
63}
64
65impl Render for AgentModelSelector {
66 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
67 let model = self.selector.read(cx).delegate.active_model(cx);
68 let model_name = model
69 .as_ref()
70 .map(|model| model.model.name().0)
71 .unwrap_or_else(|| SharedString::from("Select a Model"));
72
73 let provider_icon = model.as_ref().map(|model| model.provider.icon());
74 let color = if self.menu_handle.is_deployed() {
75 Color::Accent
76 } else {
77 Color::Muted
78 };
79
80 let focus_handle = self.focus_handle.clone();
81
82 PickerPopoverMenu::new(
83 self.selector.clone(),
84 ButtonLike::new("active-model")
85 .when_some(provider_icon, |this, icon| {
86 this.child(Icon::new(icon).color(color).size(IconSize::XSmall))
87 })
88 .selected_style(ButtonStyle::Tinted(TintColor::Accent))
89 .child(
90 Label::new(model_name)
91 .color(color)
92 .size(LabelSize::Small)
93 .ml_0p5(),
94 )
95 .child(
96 Icon::new(IconName::ChevronDown)
97 .color(color)
98 .size(IconSize::XSmall),
99 ),
100 move |_window, cx| {
101 Tooltip::for_action_in("Change Model", &ToggleModelSelector, &focus_handle, cx)
102 },
103 gpui::Corner::TopRight,
104 cx,
105 )
106 .with_handle(self.menu_handle.clone())
107 .offset(gpui::Point {
108 x: px(0.0),
109 y: px(2.0),
110 })
111 .render(window, cx)
112 }
113}