model_selector_popover.rs

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