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