agent_model_selector.rs

  1use crate::{
  2    ModelUsageContext,
  3    language_model_selector::{LanguageModelSelector, language_model_selector},
  4};
  5use fs::Fs;
  6use gpui::{Entity, FocusHandle, SharedString};
  7use picker::popover_menu::PickerPopoverMenu;
  8use settings::update_settings_file;
  9use std::sync::Arc;
 10use ui::{ButtonLike, PopoverMenuHandle, TintColor, Tooltip, prelude::*};
 11use zed_actions::agent::ToggleModelSelector;
 12
 13pub struct AgentModelSelector {
 14    selector: Entity<LanguageModelSelector>,
 15    menu_handle: PopoverMenuHandle<LanguageModelSelector>,
 16    focus_handle: FocusHandle,
 17}
 18
 19impl AgentModelSelector {
 20    pub(crate) fn new(
 21        fs: Arc<dyn Fs>,
 22        menu_handle: PopoverMenuHandle<LanguageModelSelector>,
 23        focus_handle: FocusHandle,
 24        model_usage_context: ModelUsageContext,
 25        window: &mut Window,
 26        cx: &mut Context<Self>,
 27    ) -> Self {
 28        Self {
 29            selector: cx.new(move |cx| {
 30                let fs = fs.clone();
 31                language_model_selector(
 32                    {
 33                        let model_context = model_usage_context.clone();
 34                        move |cx| model_context.configured_model(cx)
 35                    },
 36                    move |model, cx| {
 37                        let provider = model.provider_id().0.to_string();
 38                        let model_id = model.id().0.to_string();
 39                        match &model_usage_context {
 40                            ModelUsageContext::InlineAssistant => {
 41                                update_settings_file(fs.clone(), cx, move |settings, _cx| {
 42                                    settings
 43                                        .agent
 44                                        .get_or_insert_default()
 45                                        .set_inline_assistant_model(provider.clone(), model_id);
 46                                });
 47                            }
 48                        }
 49                    },
 50                    true, // Use popover styles for picker
 51                    window,
 52                    cx,
 53                )
 54            }),
 55            menu_handle,
 56            focus_handle,
 57        }
 58    }
 59
 60    pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
 61        self.menu_handle.toggle(window, cx);
 62    }
 63}
 64
 65impl Render for AgentModelSelector {
 66    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 67        let model = self.selector.read(cx).delegate.active_model(cx);
 68        let model_name = model
 69            .as_ref()
 70            .map(|model| model.model.name().0)
 71            .unwrap_or_else(|| SharedString::from("Select a Model"));
 72
 73        let provider_icon = model.as_ref().map(|model| model.provider.icon());
 74        let color = if self.menu_handle.is_deployed() {
 75            Color::Accent
 76        } else {
 77            Color::Muted
 78        };
 79
 80        let focus_handle = self.focus_handle.clone();
 81
 82        PickerPopoverMenu::new(
 83            self.selector.clone(),
 84            ButtonLike::new("active-model")
 85                .when_some(provider_icon, |this, icon| {
 86                    this.child(Icon::new(icon).color(color).size(IconSize::XSmall))
 87                })
 88                .selected_style(ButtonStyle::Tinted(TintColor::Accent))
 89                .child(
 90                    Label::new(model_name)
 91                        .color(color)
 92                        .size(LabelSize::Small)
 93                        .ml_0p5(),
 94                )
 95                .child(
 96                    Icon::new(IconName::ChevronDown)
 97                        .color(color)
 98                        .size(IconSize::XSmall),
 99                ),
100            move |_window, cx| {
101                Tooltip::for_action_in("Change Model", &ToggleModelSelector, &focus_handle, cx)
102            },
103            gpui::Corner::TopRight,
104            cx,
105        )
106        .with_handle(self.menu_handle.clone())
107        .offset(gpui::Point {
108            x: px(0.0),
109            y: px(2.0),
110        })
111        .render(window, cx)
112    }
113}