manage_profiles_modal.rs

  1use std::sync::Arc;
  2
  3use assistant_settings::AssistantSettings;
  4use assistant_tool::ToolWorkingSet;
  5use fs::Fs;
  6use gpui::{prelude::*, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable};
  7use settings::Settings as _;
  8use ui::{prelude::*, ListItem, ListItemSpacing, Navigable, NavigableEntry};
  9use workspace::{ModalView, Workspace};
 10
 11use crate::assistant_configuration::profile_picker::{ProfilePicker, ProfilePickerDelegate};
 12use crate::assistant_configuration::tool_picker::{ToolPicker, ToolPickerDelegate};
 13use crate::{AssistantPanel, ManageProfiles};
 14
 15enum Mode {
 16    ChooseProfile(Entity<ProfilePicker>),
 17    ViewProfile(ViewProfileMode),
 18    ConfigureTools(Entity<ToolPicker>),
 19}
 20
 21#[derive(Clone)]
 22pub struct ViewProfileMode {
 23    profile_id: Arc<str>,
 24    configure_tools: NavigableEntry,
 25}
 26
 27pub struct ManageProfilesModal {
 28    fs: Arc<dyn Fs>,
 29    tools: Arc<ToolWorkingSet>,
 30    focus_handle: FocusHandle,
 31    mode: Mode,
 32}
 33
 34impl ManageProfilesModal {
 35    pub fn register(
 36        workspace: &mut Workspace,
 37        _window: Option<&mut Window>,
 38        _cx: &mut Context<Workspace>,
 39    ) {
 40        workspace.register_action(|workspace, _: &ManageProfiles, window, cx| {
 41            if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
 42                let fs = workspace.app_state().fs.clone();
 43                let thread_store = panel.read(cx).thread_store().read(cx);
 44                let tools = thread_store.tools();
 45                workspace.toggle_modal(window, cx, |window, cx| Self::new(fs, tools, window, cx))
 46            }
 47        });
 48    }
 49
 50    pub fn new(
 51        fs: Arc<dyn Fs>,
 52        tools: Arc<ToolWorkingSet>,
 53        window: &mut Window,
 54        cx: &mut Context<Self>,
 55    ) -> Self {
 56        let focus_handle = cx.focus_handle();
 57        let handle = cx.entity();
 58
 59        Self {
 60            fs,
 61            tools,
 62            focus_handle,
 63            mode: Mode::ChooseProfile(cx.new(|cx| {
 64                let delegate = ProfilePickerDelegate::new(
 65                    move |profile_id, window, cx| {
 66                        handle.update(cx, |this, cx| {
 67                            this.view_profile(profile_id.clone(), window, cx);
 68                        })
 69                    },
 70                    cx,
 71                );
 72                ProfilePicker::new(delegate, window, cx)
 73            })),
 74        }
 75    }
 76
 77    pub fn view_profile(
 78        &mut self,
 79        profile_id: Arc<str>,
 80        window: &mut Window,
 81        cx: &mut Context<Self>,
 82    ) {
 83        self.mode = Mode::ViewProfile(ViewProfileMode {
 84            profile_id,
 85            configure_tools: NavigableEntry::focusable(cx),
 86        });
 87        self.focus_handle(cx).focus(window);
 88    }
 89
 90    fn configure_tools(
 91        &mut self,
 92        profile_id: Arc<str>,
 93        window: &mut Window,
 94        cx: &mut Context<Self>,
 95    ) {
 96        let settings = AssistantSettings::get_global(cx);
 97        let Some(profile) = settings.profiles.get(&profile_id).cloned() else {
 98            return;
 99        };
100
101        self.mode = Mode::ConfigureTools(cx.new(|cx| {
102            let delegate = ToolPickerDelegate::new(
103                self.fs.clone(),
104                self.tools.clone(),
105                profile_id,
106                profile,
107                cx,
108            );
109            ToolPicker::new(delegate, window, cx)
110        }));
111        self.focus_handle(cx).focus(window);
112    }
113
114    fn confirm(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {}
115
116    fn cancel(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {}
117}
118
119impl ModalView for ManageProfilesModal {}
120
121impl Focusable for ManageProfilesModal {
122    fn focus_handle(&self, cx: &App) -> FocusHandle {
123        match &self.mode {
124            Mode::ChooseProfile(profile_picker) => profile_picker.focus_handle(cx),
125            Mode::ConfigureTools(tool_picker) => tool_picker.focus_handle(cx),
126            Mode::ViewProfile(_) => self.focus_handle.clone(),
127        }
128    }
129}
130
131impl EventEmitter<DismissEvent> for ManageProfilesModal {}
132
133impl ManageProfilesModal {
134    fn render_view_profile(
135        &mut self,
136        mode: ViewProfileMode,
137        window: &mut Window,
138        cx: &mut Context<Self>,
139    ) -> impl IntoElement {
140        Navigable::new(
141            div()
142                .track_focus(&self.focus_handle(cx))
143                .size_full()
144                .child(
145                    v_flex().child(
146                        div()
147                            .id("configure-tools")
148                            .track_focus(&mode.configure_tools.focus_handle)
149                            .on_action({
150                                let profile_id = mode.profile_id.clone();
151                                cx.listener(move |this, _: &menu::Confirm, window, cx| {
152                                    this.configure_tools(profile_id.clone(), window, cx);
153                                })
154                            })
155                            .child(
156                                ListItem::new("configure-tools")
157                                    .toggle_state(
158                                        mode.configure_tools
159                                            .focus_handle
160                                            .contains_focused(window, cx),
161                                    )
162                                    .inset(true)
163                                    .spacing(ListItemSpacing::Sparse)
164                                    .start_slot(Icon::new(IconName::Cog))
165                                    .child(Label::new("Configure Tools"))
166                                    .on_click({
167                                        let profile_id = mode.profile_id.clone();
168                                        cx.listener(move |this, _, window, cx| {
169                                            this.configure_tools(profile_id.clone(), window, cx);
170                                        })
171                                    }),
172                            ),
173                    ),
174                )
175                .into_any_element(),
176        )
177        .entry(mode.configure_tools)
178    }
179}
180
181impl Render for ManageProfilesModal {
182    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
183        div()
184            .elevation_3(cx)
185            .w(rems(34.))
186            .key_context("ManageProfilesModal")
187            .on_action(cx.listener(|this, _: &menu::Cancel, window, cx| this.cancel(window, cx)))
188            .on_action(cx.listener(|this, _: &menu::Confirm, window, cx| this.confirm(window, cx)))
189            .capture_any_mouse_down(cx.listener(|this, _, window, cx| {
190                this.focus_handle(cx).focus(window);
191            }))
192            .on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
193            .child(match &self.mode {
194                Mode::ChooseProfile(profile_picker) => profile_picker.clone().into_any_element(),
195                Mode::ViewProfile(mode) => self
196                    .render_view_profile(mode.clone(), window, cx)
197                    .into_any_element(),
198                Mode::ConfigureTools(tool_picker) => tool_picker.clone().into_any_element(),
199            })
200    }
201}