Detailed changes
@@ -197,6 +197,11 @@ pub trait AgentModelSelector: 'static {
fn watch(&self, _cx: &mut App) -> Option<watch::Receiver<()>> {
None
}
+
+ /// Returns whether the model picker should render a footer.
+ fn should_render_footer(&self) -> bool {
+ false
+ }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -961,6 +961,10 @@ impl acp_thread::AgentModelSelector for NativeAgentModelSelector {
fn watch(&self, cx: &mut App) -> Option<watch::Receiver<()>> {
Some(self.connection.0.read(cx).models.watch())
}
+
+ fn should_render_footer(&self) -> bool {
+ true
+ }
}
impl acp_thread::AgentConnection for NativeAgentConnection {
@@ -7,14 +7,17 @@ use collections::IndexMap;
use fs::Fs;
use futures::FutureExt;
use fuzzy::{StringMatchCandidate, match_strings};
-use gpui::{AsyncWindowContext, BackgroundExecutor, DismissEvent, Task, WeakEntity};
+use gpui::{
+ Action, AsyncWindowContext, BackgroundExecutor, DismissEvent, FocusHandle, Task, WeakEntity,
+};
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
use ui::{
- DocumentationAside, DocumentationEdge, DocumentationSide, IntoElement, ListItem,
+ DocumentationAside, DocumentationEdge, DocumentationSide, IntoElement, KeyBinding, ListItem,
ListItemSpacing, prelude::*,
};
use util::ResultExt;
+use zed_actions::agent::OpenSettings;
use crate::ui::HoldForDefault;
@@ -24,10 +27,12 @@ pub fn acp_model_selector(
selector: Rc<dyn AgentModelSelector>,
agent_server: Rc<dyn AgentServer>,
fs: Arc<dyn Fs>,
+ focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<AcpModelSelector>,
) -> AcpModelSelector {
- let delegate = AcpModelPickerDelegate::new(selector, agent_server, fs, window, cx);
+ let delegate =
+ AcpModelPickerDelegate::new(selector, agent_server, fs, focus_handle, window, cx);
Picker::list(delegate, window, cx)
.show_scrollbar(true)
.width(rems(20.))
@@ -49,6 +54,7 @@ pub struct AcpModelPickerDelegate {
selected_description: Option<(usize, SharedString, bool)>,
selected_model: Option<AgentModelInfo>,
_refresh_models_task: Task<()>,
+ focus_handle: FocusHandle,
}
impl AcpModelPickerDelegate {
@@ -56,6 +62,7 @@ impl AcpModelPickerDelegate {
selector: Rc<dyn AgentModelSelector>,
agent_server: Rc<dyn AgentServer>,
fs: Arc<dyn Fs>,
+ focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<AcpModelSelector>,
) -> Self {
@@ -104,6 +111,7 @@ impl AcpModelPickerDelegate {
selected_index: 0,
selected_description: None,
_refresh_models_task: refresh_models_task,
+ focus_handle,
}
}
@@ -331,6 +339,39 @@ impl PickerDelegate for AcpModelPickerDelegate {
)
})
}
+
+ fn render_footer(
+ &self,
+ _window: &mut Window,
+ cx: &mut Context<Picker<Self>>,
+ ) -> Option<AnyElement> {
+ let focus_handle = self.focus_handle.clone();
+
+ if !self.selector.should_render_footer() {
+ return None;
+ }
+
+ Some(
+ h_flex()
+ .w_full()
+ .p_1p5()
+ .border_t_1()
+ .border_color(cx.theme().colors().border_variant)
+ .child(
+ Button::new("configure", "Configure")
+ .full_width()
+ .style(ButtonStyle::Outlined)
+ .key_binding(
+ KeyBinding::for_action_in(&OpenSettings, &focus_handle, cx)
+ .map(|kb| kb.size(rems_from_px(12.))),
+ )
+ .on_click(|_, window, cx| {
+ window.dispatch_action(OpenSettings.boxed_clone(), cx);
+ }),
+ )
+ .into_any(),
+ )
+ }
}
fn info_list_to_picker_entries(
@@ -30,8 +30,18 @@ impl AcpModelSelectorPopover {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
+ let focus_handle_clone = focus_handle.clone();
Self {
- selector: cx.new(move |cx| acp_model_selector(selector, agent_server, fs, window, cx)),
+ selector: cx.new(move |cx| {
+ acp_model_selector(
+ selector,
+ agent_server,
+ fs,
+ focus_handle_clone.clone(),
+ window,
+ cx,
+ )
+ }),
menu_handle,
focus_handle,
}
@@ -253,6 +253,7 @@ impl ManageProfilesModal {
});
},
false, // Do not use popover styles for the model picker
+ self.focus_handle.clone(),
window,
cx,
)
@@ -25,6 +25,8 @@ impl AgentModelSelector {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
+ let focus_handle_clone = focus_handle.clone();
+
Self {
selector: cx.new(move |cx| {
let fs = fs.clone();
@@ -48,6 +50,7 @@ impl AgentModelSelector {
}
},
true, // Use popover styles for picker
+ focus_handle_clone,
window,
cx,
)
@@ -2,14 +2,17 @@ use std::{cmp::Reverse, sync::Arc};
use collections::IndexMap;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
-use gpui::{Action, AnyElement, App, BackgroundExecutor, DismissEvent, Subscription, Task};
+use gpui::{
+ Action, AnyElement, App, BackgroundExecutor, DismissEvent, FocusHandle, Subscription, Task,
+};
use language_model::{
AuthenticateError, ConfiguredModel, LanguageModel, LanguageModelProviderId,
LanguageModelRegistry,
};
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
-use ui::{ListItem, ListItemSpacing, prelude::*};
+use ui::{KeyBinding, ListItem, ListItemSpacing, prelude::*};
+use zed_actions::agent::OpenSettings;
type OnModelChanged = Arc<dyn Fn(Arc<dyn LanguageModel>, &mut App) + 'static>;
type GetActiveModel = Arc<dyn Fn(&App) -> Option<ConfiguredModel> + 'static>;
@@ -20,6 +23,7 @@ pub fn language_model_selector(
get_active_model: impl Fn(&App) -> Option<ConfiguredModel> + 'static,
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &mut App) + 'static,
popover_styles: bool,
+ focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<LanguageModelSelector>,
) -> LanguageModelSelector {
@@ -27,6 +31,7 @@ pub fn language_model_selector(
get_active_model,
on_model_changed,
popover_styles,
+ focus_handle,
window,
cx,
);
@@ -88,6 +93,7 @@ pub struct LanguageModelPickerDelegate {
_authenticate_all_providers_task: Task<()>,
_subscriptions: Vec<Subscription>,
popover_styles: bool,
+ focus_handle: FocusHandle,
}
impl LanguageModelPickerDelegate {
@@ -95,6 +101,7 @@ impl LanguageModelPickerDelegate {
get_active_model: impl Fn(&App) -> Option<ConfiguredModel> + 'static,
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &mut App) + 'static,
popover_styles: bool,
+ focus_handle: FocusHandle,
window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Self {
@@ -128,6 +135,7 @@ impl LanguageModelPickerDelegate {
},
)],
popover_styles,
+ focus_handle,
}
}
@@ -521,6 +529,8 @@ impl PickerDelegate for LanguageModelPickerDelegate {
_window: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Option<gpui::AnyElement> {
+ let focus_handle = self.focus_handle.clone();
+
if !self.popover_styles {
return None;
}
@@ -528,22 +538,19 @@ impl PickerDelegate for LanguageModelPickerDelegate {
Some(
h_flex()
.w_full()
+ .p_1p5()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
- .p_1()
- .gap_4()
- .justify_between()
.child(
Button::new("configure", "Configure")
- .icon(IconName::Settings)
- .icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .icon_position(IconPosition::Start)
+ .full_width()
+ .style(ButtonStyle::Outlined)
+ .key_binding(
+ KeyBinding::for_action_in(&OpenSettings, &focus_handle, cx)
+ .map(|kb| kb.size(rems_from_px(12.))),
+ )
.on_click(|_, window, cx| {
- window.dispatch_action(
- zed_actions::agent::OpenSettings.boxed_clone(),
- cx,
- );
+ window.dispatch_action(OpenSettings.boxed_clone(), cx);
}),
)
.into_any(),
@@ -280,6 +280,8 @@ impl TextThreadEditor {
.thought_process_output_sections()
.to_vec();
let slash_commands = text_thread.read(cx).slash_commands().clone();
+ let focus_handle = editor.read(cx).focus_handle(cx);
+
let mut this = Self {
text_thread,
slash_commands,
@@ -315,6 +317,7 @@ impl TextThreadEditor {
});
},
true, // Use popover styles for picker
+ focus_handle,
window,
cx,
)