model_selector_popover.rs

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