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                    window,
 51                    cx,
 52                )
 53            }),
 54            menu_handle,
 55            focus_handle,
 56        }
 57    }
 58
 59    pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
 60        self.menu_handle.toggle(window, cx);
 61    }
 62}
 63
 64impl Render for AgentModelSelector {
 65    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 66        let model = self.selector.read(cx).delegate.active_model(cx);
 67        let model_name = model
 68            .as_ref()
 69            .map(|model| model.model.name().0)
 70            .unwrap_or_else(|| SharedString::from("Select a Model"));
 71
 72        let provider_icon = model.as_ref().map(|model| model.provider.icon());
 73        let color = if self.menu_handle.is_deployed() {
 74            Color::Accent
 75        } else {
 76            Color::Muted
 77        };
 78
 79        let focus_handle = self.focus_handle.clone();
 80
 81        PickerPopoverMenu::new(
 82            self.selector.clone(),
 83            ButtonLike::new("active-model")
 84                .when_some(provider_icon, |this, icon| {
 85                    this.child(Icon::new(icon).color(color).size(IconSize::XSmall))
 86                })
 87                .selected_style(ButtonStyle::Tinted(TintColor::Accent))
 88                .child(
 89                    Label::new(model_name)
 90                        .color(color)
 91                        .size(LabelSize::Small)
 92                        .ml_0p5(),
 93                )
 94                .child(
 95                    Icon::new(IconName::ChevronDown)
 96                        .color(color)
 97                        .size(IconSize::XSmall),
 98                ),
 99            move |_window, cx| {
100                Tooltip::for_action_in("Change Model", &ToggleModelSelector, &focus_handle, cx)
101            },
102            gpui::Corner::TopRight,
103            cx,
104        )
105        .with_handle(self.menu_handle.clone())
106        .offset(gpui::Point {
107            x: px(0.0),
108            y: px(2.0),
109        })
110        .render(window, cx)
111    }
112}