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