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::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}