model_selector_popover.rs

  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}