@@ -7,7 +7,7 @@ use assistant_context_editor::{
make_lsp_adapter_delegate, AssistantContext, AssistantPanelDelegate, ContextEditor,
ContextEditorToolbarItem, ContextEditorToolbarItemEvent, ContextHistory, ContextId,
ContextStore, ContextStoreEvent, InsertDraggedFiles, SlashCommandCompletionProvider,
- ToggleModelSelector, DEFAULT_TAB_TITLE,
+ DEFAULT_TAB_TITLE,
};
use assistant_settings::{AssistantDockPosition, AssistantSettings};
use assistant_slash_command::SlashCommandWorkingSet;
@@ -21,7 +21,6 @@ use gpui::{
};
use language::LanguageRegistry;
use language_model::{LanguageModelProviderId, LanguageModelRegistry, ZED_CLOUD_PROVIDER_ID};
-use language_model_selector::LanguageModelSelector;
use project::Project;
use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};
use search::{buffer_search::DivRegistrar, BufferSearchBar};
@@ -29,7 +28,7 @@ use settings::{update_settings_file, Settings};
use smol::stream::StreamExt;
use std::{ops::ControlFlow, path::PathBuf, sync::Arc};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
-use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
+use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
use util::{maybe, ResultExt};
use workspace::DraggedTab;
use workspace::{
@@ -77,7 +76,6 @@ pub struct AssistantPanel {
languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>,
subscriptions: Vec<Subscription>,
- model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
model_summary_editor: Entity<Editor>,
authenticate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,
configuration_subscription: Option<Subscription>,
@@ -119,17 +117,9 @@ impl AssistantPanel {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
- let model_selector_menu_handle = PopoverMenuHandle::default();
let model_summary_editor = cx.new(|cx| Editor::single_line(window, cx));
- let context_editor_toolbar = cx.new(|cx| {
- ContextEditorToolbarItem::new(
- workspace,
- model_selector_menu_handle.clone(),
- model_summary_editor.clone(),
- window,
- cx,
- )
- });
+ let context_editor_toolbar =
+ cx.new(|_| ContextEditorToolbarItem::new(model_summary_editor.clone()));
let pane = cx.new(|cx| {
let mut pane = Pane::new(
@@ -331,7 +321,6 @@ impl AssistantPanel {
languages: workspace.app_state().languages.clone(),
fs: workspace.app_state().fs.clone(),
subscriptions,
- model_selector_menu_handle,
model_summary_editor,
authenticate_provider_task: None,
configuration_subscription: None,
@@ -1054,15 +1043,6 @@ impl AssistantPanel {
.detach_and_log_err(cx);
}
- fn toggle_model_selector(
- &mut self,
- _: &ToggleModelSelector,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) {
- self.model_selector_menu_handle.toggle(window, cx);
- }
-
pub(crate) fn active_context_editor(&self, cx: &App) -> Option<Entity<ContextEditor>> {
self.pane
.read(cx)
@@ -1229,7 +1209,6 @@ impl Render for AssistantPanel {
}))
.on_action(cx.listener(AssistantPanel::deploy_history))
.on_action(cx.listener(AssistantPanel::deploy_prompt_library))
- .on_action(cx.listener(AssistantPanel::toggle_model_selector))
.child(registrar.size_full().child(self.pane.clone()))
.into_any_element()
}
@@ -193,6 +193,8 @@ pub struct ContextEditor {
// the file is opened. In order to keep the worktree alive for the duration of the
// context editor, we keep a reference here.
dragged_file_worktrees: Vec<Entity<Worktree>>,
+ language_model_selector: Entity<LanguageModelSelector>,
+ language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
}
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
@@ -238,6 +240,22 @@ impl ContextEditor {
cx.subscribe_in(&editor, window, Self::handle_editor_search_event),
];
+ let fs_clone = fs.clone();
+ let language_model_selector = cx.new(|cx| {
+ LanguageModelSelector::new(
+ move |model, cx| {
+ update_settings_file::<AssistantSettings>(
+ fs_clone.clone(),
+ cx,
+ move |settings, _| settings.set_model(model.clone()),
+ );
+ },
+ window,
+ cx,
+ )
+ });
+
+ let language_model_selector_menu_handle = PopoverMenuHandle::default();
let sections = context.read(cx).slash_command_output_sections().to_vec();
let patch_ranges = context.read(cx).patch_ranges().collect::<Vec<_>>();
let slash_commands = context.read(cx).slash_commands().clone();
@@ -262,6 +280,8 @@ impl ContextEditor {
show_accept_terms: false,
slash_menu_handle: Default::default(),
dragged_file_worktrees: Vec::new(),
+ language_model_selector,
+ language_model_selector_menu_handle,
};
this.update_message_headers(cx);
this.update_image_blocks(cx);
@@ -2355,13 +2375,62 @@ impl ContextEditor {
slash_command_picker::SlashCommandSelector::new(
self.slash_commands.clone(),
cx.entity().downgrade(),
- Button::new("trigger", "Add Context")
- .icon(IconName::Plus)
+ IconButton::new("trigger", IconName::Plus)
.icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .icon_position(IconPosition::Start),
- Tooltip::text("Type / to insert via keyboard"),
+ .icon_color(Color::Muted),
+ move |window, cx| {
+ Tooltip::with_meta(
+ "Add Context",
+ None,
+ "Type / to insert via keyboard",
+ window,
+ cx,
+ )
+ },
+ )
+ }
+
+ fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
+ let active_model = LanguageModelRegistry::read_global(cx).active_model();
+ let focus_handle = self.editor().focus_handle(cx).clone();
+ let model_name = match active_model {
+ Some(model) => model.name().0,
+ None => SharedString::from("No model selected"),
+ };
+
+ LanguageModelSelectorPopoverMenu::new(
+ self.language_model_selector.clone(),
+ ButtonLike::new("active-model")
+ .style(ButtonStyle::Subtle)
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(
+ div().max_w_32().child(
+ Label::new(model_name)
+ .size(LabelSize::Small)
+ .color(Color::Muted)
+ .text_ellipsis()
+ .into_any_element(),
+ ),
+ )
+ .child(
+ Icon::new(IconName::ChevronDown)
+ .color(Color::Muted)
+ .size(IconSize::XSmall),
+ ),
+ ),
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Change Model",
+ &ToggleModelSelector,
+ &focus_handle,
+ window,
+ cx,
+ )
+ },
)
+ .with_handle(self.language_model_selector_menu_handle.clone())
}
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
@@ -2800,6 +2869,7 @@ impl EventEmitter<SearchEvent> for ContextEditor {}
impl Render for ContextEditor {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let provider = LanguageModelRegistry::read_global(cx).active_provider();
+
let accept_terms = if self.show_accept_terms {
provider.as_ref().and_then(|provider| {
provider.render_accept_terms(LanguageModelProviderTosView::PromptEditorPopup, cx)
@@ -2852,7 +2922,17 @@ impl Render for ContextEditor {
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
- .child(h_flex().gap_1().child(self.render_inject_context_menu(cx)))
+ .child(
+ h_flex()
+ .gap_1()
+ .child(self.render_inject_context_menu(cx))
+ .child(ui::Divider::vertical())
+ .child(
+ div()
+ .pl_0p5()
+ .child(self.render_language_model_selector(cx)),
+ ),
+ )
.child(
h_flex()
.w_full()
@@ -3163,36 +3243,13 @@ impl FollowableItem for ContextEditor {
pub struct ContextEditorToolbarItem {
active_context_editor: Option<WeakEntity<ContextEditor>>,
model_summary_editor: Entity<Editor>,
- language_model_selector: Entity<LanguageModelSelector>,
- language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
}
impl ContextEditorToolbarItem {
- pub fn new(
- workspace: &Workspace,
- model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
- model_summary_editor: Entity<Editor>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> Self {
+ pub fn new(model_summary_editor: Entity<Editor>) -> Self {
Self {
active_context_editor: None,
model_summary_editor,
- language_model_selector: cx.new(|cx| {
- let fs = workspace.app_state().fs.clone();
- LanguageModelSelector::new(
- move |model, cx| {
- update_settings_file::<AssistantSettings>(
- fs.clone(),
- cx,
- move |settings, _| settings.set_model(model.clone()),
- );
- },
- window,
- cx,
- )
- }),
- language_model_selector_menu_handle: model_selector_menu_handle,
}
}
@@ -3263,8 +3320,7 @@ impl Render for ContextEditorToolbarItem {
})),
),
);
- let active_provider = LanguageModelRegistry::read_global(cx).active_provider();
- let active_model = LanguageModelRegistry::read_global(cx).active_model();
+
let right_side = h_flex()
.gap_2()
// TODO display this in a nicer way, once we have a design for it.
@@ -3280,56 +3336,6 @@ impl Render for ContextEditorToolbarItem {
// scan_items_remaining
// .map(|remaining_items| format!("Files to scan: {}", remaining_items))
// })
- .child(
- LanguageModelSelectorPopoverMenu::new(
- self.language_model_selector.clone(),
- ButtonLike::new("active-model")
- .style(ButtonStyle::Subtle)
- .child(
- h_flex()
- .w_full()
- .gap_0p5()
- .child(
- div()
- .overflow_x_hidden()
- .flex_grow()
- .whitespace_nowrap()
- .child(match (active_provider, active_model) {
- (Some(provider), Some(model)) => h_flex()
- .gap_1()
- .child(
- Icon::new(
- model
- .icon()
- .unwrap_or_else(|| provider.icon()),
- )
- .color(Color::Muted)
- .size(IconSize::XSmall),
- )
- .child(
- Label::new(model.name().0)
- .size(LabelSize::Small)
- .color(Color::Muted),
- )
- .into_any_element(),
- _ => Label::new("No model selected")
- .size(LabelSize::Small)
- .color(Color::Muted)
- .into_any_element(),
- }),
- )
- .child(
- Icon::new(IconName::ChevronDown)
- .color(Color::Muted)
- .size(IconSize::XSmall),
- ),
- ),
- move |window, cx| {
- Tooltip::for_action("Change Model", &ToggleModelSelector, window, cx)
- },
- )
- .with_handle(self.language_model_selector_menu_handle.clone()),
- )
.children(self.render_remaining_tokens(cx));
h_flex()