1use crate::{
2 ModelUsageContext,
3 language_model_selector::{LanguageModelSelector, language_model_selector},
4 ui::ModelSelectorTooltip,
5};
6use fs::Fs;
7use gpui::{Entity, FocusHandle, SharedString};
8use language_model::IconOrSvg;
9use picker::popover_menu::PickerPopoverMenu;
10use settings::update_settings_file;
11use std::sync::Arc;
12use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*};
13
14pub struct AgentModelSelector {
15 selector: Entity<LanguageModelSelector>,
16 menu_handle: PopoverMenuHandle<LanguageModelSelector>,
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 language_model_selector(
31 {
32 let model_context = model_usage_context.clone();
33 move |cx| model_context.configured_model(cx)
34 },
35 {
36 let fs = fs.clone();
37 move |model, cx| {
38 let provider = model.provider_id().0.to_string();
39 let model_id = model.id().0.to_string();
40 match &model_usage_context {
41 ModelUsageContext::InlineAssistant => {
42 update_settings_file(fs.clone(), cx, move |settings, _cx| {
43 settings
44 .agent
45 .get_or_insert_default()
46 .set_inline_assistant_model(provider.clone(), model_id);
47 });
48 }
49 }
50 }
51 },
52 {
53 let fs = fs.clone();
54 move |model, should_be_favorite, cx| {
55 crate::favorite_models::toggle_in_settings(
56 model,
57 should_be_favorite,
58 fs.clone(),
59 cx,
60 );
61 }
62 },
63 true, // Use popover styles for picker
64 focus_handle.clone(),
65 window,
66 cx,
67 )
68 }),
69 menu_handle,
70 }
71 }
72
73 pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
74 self.menu_handle.toggle(window, cx);
75 }
76
77 pub fn active_model(&self, cx: &App) -> Option<language_model::ConfiguredModel> {
78 self.selector.read(cx).delegate.active_model(cx)
79 }
80
81 pub fn cycle_favorite_models(&self, window: &mut Window, cx: &mut Context<Self>) {
82 self.selector.update(cx, |selector, cx| {
83 selector.delegate.cycle_favorite_models(window, cx);
84 });
85 }
86}
87
88impl Render for AgentModelSelector {
89 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
90 let model = self.selector.read(cx).delegate.active_model(cx);
91 let model_name = model
92 .as_ref()
93 .map(|model| model.model.name().0)
94 .unwrap_or_else(|| SharedString::from("Select a Model"));
95
96 let provider_icon = model.as_ref().map(|model| model.provider.icon());
97 let color = if self.menu_handle.is_deployed() {
98 Color::Accent
99 } else {
100 Color::Muted
101 };
102
103 let show_cycle_row = self.selector.read(cx).delegate.favorites_count() > 1;
104
105 let tooltip = Tooltip::element({
106 move |_, _cx| {
107 ModelSelectorTooltip::new()
108 .show_cycle_row(show_cycle_row)
109 .into_any_element()
110 }
111 });
112
113 PickerPopoverMenu::new(
114 self.selector.clone(),
115 ButtonLike::new("active-model")
116 .when_some(provider_icon, |this, icon| {
117 this.child(
118 match icon {
119 IconOrSvg::Svg(path) => Icon::from_external_svg(path),
120 IconOrSvg::Icon(name) => Icon::new(name),
121 }
122 .color(color)
123 .size(IconSize::XSmall),
124 )
125 })
126 .selected_style(ButtonStyle::Tinted(TintColor::Accent))
127 .child(
128 Label::new(model_name)
129 .color(color)
130 .size(LabelSize::Small)
131 .ml_0p5(),
132 )
133 .child(
134 Icon::new(IconName::ChevronDown)
135 .color(color)
136 .size(IconSize::XSmall),
137 ),
138 tooltip,
139 gpui::Corner::TopRight,
140 cx,
141 )
142 .with_handle(self.menu_handle.clone())
143 .offset(gpui::Point {
144 x: px(0.0),
145 y: px(2.0),
146 })
147 .render(window, cx)
148 }
149}