assistant2: Add keybinding for profile selector (#27674)

This PR adds a keybinding to toggle the profile selector.

Defaults to `Cmd-I` on macOS and `ctrl-I` on Linux/Windows.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-03-28 13:19:37 -04:00 committed by GitHub
parent 55d934a3be
commit c8fb95cd1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 8 deletions

View file

@ -618,6 +618,7 @@
"ctrl-n": "assistant2::NewThread", "ctrl-n": "assistant2::NewThread",
"new": "assistant2::NewThread", "new": "assistant2::NewThread",
"ctrl-shift-h": "assistant2::OpenHistory", "ctrl-shift-h": "assistant2::OpenHistory",
"ctrl-i": "assistant2::ToggleProfileSelector",
"ctrl-alt-/": "assistant::ToggleModelSelector", "ctrl-alt-/": "assistant::ToggleModelSelector",
"ctrl-shift-a": "assistant2::ToggleContextPicker", "ctrl-shift-a": "assistant2::ToggleContextPicker",
"ctrl-e": "assistant2::ChatMode", "ctrl-e": "assistant2::ChatMode",
@ -635,7 +636,8 @@
{ {
"context": "MessageEditor > Editor", "context": "MessageEditor > Editor",
"bindings": { "bindings": {
"enter": "assistant2::Chat" "enter": "assistant2::Chat",
"ctrl-i": "assistant2::ToggleProfileSelector"
} }
}, },
{ {

View file

@ -269,6 +269,7 @@
"cmd-n": "assistant2::NewThread", "cmd-n": "assistant2::NewThread",
"cmd-alt-p": "assistant2::NewPromptEditor", "cmd-alt-p": "assistant2::NewPromptEditor",
"cmd-shift-h": "assistant2::OpenHistory", "cmd-shift-h": "assistant2::OpenHistory",
"cmd-i": "assistant2::ToggleProfileSelector",
"cmd-alt-/": "assistant::ToggleModelSelector", "cmd-alt-/": "assistant::ToggleModelSelector",
"cmd-shift-a": "assistant2::ToggleContextPicker", "cmd-shift-a": "assistant2::ToggleContextPicker",
"cmd-e": "assistant2::ChatMode", "cmd-e": "assistant2::ChatMode",
@ -288,6 +289,7 @@
"use_key_equivalents": true, "use_key_equivalents": true,
"bindings": { "bindings": {
"enter": "assistant2::Chat", "enter": "assistant2::Chat",
"cmd-i": "assistant2::ToggleProfileSelector",
"cmd-g d": "git::Diff", "cmd-g d": "git::Diff",
"shift-escape": "git::ExpandCommitEditor" "shift-escape": "git::ExpandCommitEditor"
} }

View file

@ -44,6 +44,7 @@ actions!(
NewThread, NewThread,
NewPromptEditor, NewPromptEditor,
ToggleContextPicker, ToggleContextPicker,
ToggleProfileSelector,
RemoveAllContext, RemoveAllContext,
OpenHistory, OpenHistory,
OpenConfiguration, OpenConfiguration,

View file

@ -29,7 +29,9 @@ use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind};
use crate::profile_selector::ProfileSelector; use crate::profile_selector::ProfileSelector;
use crate::thread::{RequestKind, Thread}; use crate::thread::{RequestKind, Thread};
use crate::thread_store::ThreadStore; use crate::thread_store::ThreadStore;
use crate::{Chat, ChatMode, RemoveAllContext, ThreadEvent, ToggleContextPicker}; use crate::{
Chat, ChatMode, RemoveAllContext, ThreadEvent, ToggleContextPicker, ToggleProfileSelector,
};
pub struct MessageEditor { pub struct MessageEditor {
thread: Entity<Thread>, thread: Entity<Thread>,
@ -135,7 +137,8 @@ impl MessageEditor {
cx, cx,
) )
}), }),
profile_selector: cx.new(|cx| ProfileSelector::new(fs, thread_store, cx)), profile_selector: cx
.new(|cx| ProfileSelector::new(fs, thread_store, editor.focus_handle(cx), cx)),
_subscriptions: subscriptions, _subscriptions: subscriptions,
} }
} }
@ -561,6 +564,9 @@ impl Render for MessageEditor {
v_flex() v_flex()
.key_context("MessageEditor") .key_context("MessageEditor")
.on_action(cx.listener(Self::chat)) .on_action(cx.listener(Self::chat))
.on_action(cx.listener(|this, _: &ToggleProfileSelector, window, cx| {
this.profile_selector.read(cx).menu_handle().toggle(window, cx);
}))
.on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| { .on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
this.model_selector this.model_selector
.update(cx, |model_selector, cx| model_selector.toggle(window, cx)); .update(cx, |model_selector, cx| model_selector.toggle(window, cx));

View file

@ -2,18 +2,20 @@ use std::sync::Arc;
use assistant_settings::{AgentProfile, AssistantSettings}; use assistant_settings::{AgentProfile, AssistantSettings};
use fs::Fs; use fs::Fs;
use gpui::{prelude::*, Action, Entity, Subscription, WeakEntity}; use gpui::{prelude::*, Action, Entity, FocusHandle, Subscription, WeakEntity};
use indexmap::IndexMap; use indexmap::IndexMap;
use settings::{update_settings_file, Settings as _, SettingsStore}; use settings::{update_settings_file, Settings as _, SettingsStore};
use ui::{prelude::*, ContextMenu, ContextMenuEntry, PopoverMenu, Tooltip}; use ui::{prelude::*, ContextMenu, ContextMenuEntry, PopoverMenu, PopoverMenuHandle, Tooltip};
use util::ResultExt as _; use util::ResultExt as _;
use crate::{ManageProfiles, ThreadStore}; use crate::{ManageProfiles, ThreadStore, ToggleProfileSelector};
pub struct ProfileSelector { pub struct ProfileSelector {
profiles: IndexMap<Arc<str>, AgentProfile>, profiles: IndexMap<Arc<str>, AgentProfile>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
thread_store: WeakEntity<ThreadStore>, thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
menu_handle: PopoverMenuHandle<ContextMenu>,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
@ -21,6 +23,7 @@ impl ProfileSelector {
pub fn new( pub fn new(
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
thread_store: WeakEntity<ThreadStore>, thread_store: WeakEntity<ThreadStore>,
focus_handle: FocusHandle,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Self { ) -> Self {
let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| { let settings_subscription = cx.observe_global::<SettingsStore>(move |this, cx| {
@ -31,6 +34,8 @@ impl ProfileSelector {
profiles: IndexMap::default(), profiles: IndexMap::default(),
fs, fs,
thread_store, thread_store,
focus_handle,
menu_handle: PopoverMenuHandle::default(),
_subscriptions: vec![settings_subscription], _subscriptions: vec![settings_subscription],
}; };
this.refresh_profiles(cx); this.refresh_profiles(cx);
@ -38,6 +43,10 @@ impl ProfileSelector {
this this
} }
pub fn menu_handle(&self) -> PopoverMenuHandle<ContextMenu> {
self.menu_handle.clone()
}
fn refresh_profiles(&mut self, cx: &mut Context<Self>) { fn refresh_profiles(&mut self, cx: &mut Context<Self>) {
let settings = AssistantSettings::get_global(cx); let settings = AssistantSettings::get_global(cx);
@ -106,7 +115,8 @@ impl Render for ProfileSelector {
.unwrap_or_else(|| "Unknown".into()); .unwrap_or_else(|| "Unknown".into());
let this = cx.entity().clone(); let this = cx.entity().clone();
PopoverMenu::new("tool-selector") let focus_handle = self.focus_handle.clone();
PopoverMenu::new("profile-selector")
.menu(move |window, cx| { .menu(move |window, cx| {
Some(this.update(cx, |this, cx| this.build_context_menu(window, cx))) Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
}) })
@ -114,8 +124,17 @@ impl Render for ProfileSelector {
Button::new("profile-selector-button", profile) Button::new("profile-selector-button", profile)
.style(ButtonStyle::Filled) .style(ButtonStyle::Filled)
.label_size(LabelSize::Small), .label_size(LabelSize::Small),
Tooltip::text("Change Profile"), move |window, cx| {
Tooltip::for_action_in(
"Change Profile",
&ToggleProfileSelector,
&focus_handle,
window,
cx,
)
},
) )
.anchor(gpui::Corner::BottomLeft) .anchor(gpui::Corner::BottomLeft)
.with_handle(self.menu_handle.clone())
} }
} }