profile_selector.rs

  1use std::sync::Arc;
  2
  3use assistant_settings::{AgentProfile, AssistantSettings};
  4use fs::Fs;
  5use gpui::{prelude::*, Action, Entity, Subscription, WeakEntity};
  6use indexmap::IndexMap;
  7use settings::{update_settings_file, Settings as _, SettingsStore};
  8use ui::{prelude::*, ContextMenu, ContextMenuEntry, PopoverMenu, Tooltip};
  9use util::ResultExt as _;
 10
 11use crate::{ManageProfiles, ThreadStore};
 12
 13pub struct ProfileSelector {
 14    profiles: IndexMap<Arc<str>, AgentProfile>,
 15    fs: Arc<dyn Fs>,
 16    thread_store: WeakEntity<ThreadStore>,
 17    _subscriptions: Vec<Subscription>,
 18}
 19
 20impl ProfileSelector {
 21    pub fn new(
 22        fs: Arc<dyn Fs>,
 23        thread_store: WeakEntity<ThreadStore>,
 24        cx: &mut Context<Self>,
 25    ) -> Self {
 26        let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
 27            this.refresh_profiles(cx);
 28        });
 29
 30        let mut this = Self {
 31            profiles: IndexMap::default(),
 32            fs,
 33            thread_store,
 34            _subscriptions: vec![settings_subscription],
 35        };
 36        this.refresh_profiles(cx);
 37
 38        this
 39    }
 40
 41    fn refresh_profiles(&mut self, cx: &mut Context<Self>) {
 42        let settings = AssistantSettings::get_global(cx);
 43
 44        self.profiles = settings.profiles.clone();
 45    }
 46
 47    fn build_context_menu(
 48        &self,
 49        window: &mut Window,
 50        cx: &mut Context<Self>,
 51    ) -> Entity<ContextMenu> {
 52        ContextMenu::build(window, cx, |mut menu, _window, cx| {
 53            let settings = AssistantSettings::get_global(cx);
 54            let icon_position = IconPosition::Start;
 55
 56            menu = menu.header("Profiles");
 57            for (profile_id, profile) in self.profiles.clone() {
 58                menu = menu.toggleable_entry(
 59                    profile.name.clone(),
 60                    profile_id == settings.default_profile,
 61                    icon_position,
 62                    None,
 63                    {
 64                        let fs = self.fs.clone();
 65                        let thread_store = self.thread_store.clone();
 66                        move |_window, cx| {
 67                            update_settings_file::<AssistantSettings>(fs.clone(), cx, {
 68                                let profile_id = profile_id.clone();
 69                                move |settings, _cx| {
 70                                    settings.set_profile(profile_id.clone());
 71                                }
 72                            });
 73
 74                            thread_store
 75                                .update(cx, |this, cx| {
 76                                    this.load_profile_by_id(&profile_id, cx);
 77                                })
 78                                .log_err();
 79                        }
 80                    },
 81                );
 82            }
 83
 84            menu = menu.separator();
 85            menu = menu.item(
 86                ContextMenuEntry::new("Configure Profiles")
 87                    .icon(IconName::Pencil)
 88                    .icon_color(Color::Muted)
 89                    .handler(move |window, cx| {
 90                        window.dispatch_action(ManageProfiles.boxed_clone(), cx);
 91                    }),
 92            );
 93
 94            menu
 95        })
 96    }
 97}
 98
 99impl Render for ProfileSelector {
100    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
101        let settings = AssistantSettings::get_global(cx);
102        let profile = settings
103            .profiles
104            .get(&settings.default_profile)
105            .map(|profile| profile.name.clone())
106            .unwrap_or_else(|| "Unknown".into());
107
108        let this = cx.entity().clone();
109        PopoverMenu::new("tool-selector")
110            .menu(move |window, cx| {
111                Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
112            })
113            .trigger_with_tooltip(
114                Button::new("profile-selector-button", profile)
115                    .style(ButtonStyle::Filled)
116                    .label_size(LabelSize::Small),
117                Tooltip::text("Change Profile"),
118            )
119            .anchor(gpui::Corner::BottomLeft)
120    }
121}