1use std::rc::Rc;
2use std::sync::Arc;
3
4use acp_thread::{AgentModelIcon, AgentModelInfo, AgentModelSelector};
5use fs::Fs;
6use gpui::{Entity, FocusHandle};
7use picker::popover_menu::PickerPopoverMenu;
8use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*};
9
10use crate::ui::ModelSelectorTooltip;
11use crate::{ModelSelector, model_selector::acp_model_selector};
12
13pub struct ModelSelectorPopover {
14 selector: Entity<ModelSelector>,
15 menu_handle: PopoverMenuHandle<ModelSelector>,
16}
17
18impl ModelSelectorPopover {
19 pub(crate) fn new(
20 selector: Rc<dyn AgentModelSelector>,
21 agent_server: Rc<dyn agent_servers::AgentServer>,
22 fs: Arc<dyn Fs>,
23 menu_handle: PopoverMenuHandle<ModelSelector>,
24 focus_handle: FocusHandle,
25 window: &mut Window,
26 cx: &mut Context<Self>,
27 ) -> Self {
28 Self {
29 selector: cx.new(move |cx| {
30 acp_model_selector(selector, agent_server, fs, focus_handle.clone(), window, cx)
31 }),
32 menu_handle,
33 }
34 }
35
36 pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
37 self.menu_handle.toggle(window, cx);
38 }
39
40 pub fn active_model<'a>(&self, cx: &'a App) -> Option<&'a AgentModelInfo> {
41 self.selector.read(cx).delegate.active_model()
42 }
43
44 pub fn cycle_favorite_models(&self, window: &mut Window, cx: &mut Context<Self>) {
45 self.selector.update(cx, |selector, cx| {
46 selector.delegate.cycle_favorite_models(window, cx);
47 });
48 }
49}
50
51impl Render for ModelSelectorPopover {
52 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
53 let selector = self.selector.read(cx);
54 let model = selector.delegate.active_model();
55 let model_name = model
56 .as_ref()
57 .map(|model| model.name.clone())
58 .unwrap_or_else(|| SharedString::from("Select a Model"));
59
60 let model_icon = model.as_ref().and_then(|model| model.icon.clone());
61
62 let (color, icon) = if self.menu_handle.is_deployed() {
63 (Color::Accent, IconName::ChevronUp)
64 } else {
65 (Color::Muted, IconName::ChevronDown)
66 };
67
68 let show_cycle_row = selector.delegate.favorites_count() > 1;
69
70 let tooltip = Tooltip::element({
71 move |_, _cx| {
72 ModelSelectorTooltip::new()
73 .show_cycle_row(show_cycle_row)
74 .into_any_element()
75 }
76 });
77
78 PickerPopoverMenu::new(
79 self.selector.clone(),
80 ButtonLike::new("active-model")
81 .selected_style(ButtonStyle::Tinted(TintColor::Accent))
82 .when_some(model_icon, |this, icon| {
83 this.child(
84 match icon {
85 AgentModelIcon::Path(path) => Icon::from_external_svg(path),
86 AgentModelIcon::Named(icon_name) => Icon::new(icon_name),
87 }
88 .color(color)
89 .size(IconSize::XSmall),
90 )
91 })
92 .child(
93 Label::new(model_name)
94 .color(color)
95 .size(LabelSize::Small)
96 .ml_0p5(),
97 )
98 .child(Icon::new(icon).color(Color::Muted).size(IconSize::XSmall)),
99 tooltip,
100 gpui::Corner::BottomRight,
101 cx,
102 )
103 .with_handle(self.menu_handle.clone())
104 .render(window, cx)
105 }
106}