diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 5bbc93c390..be29106089 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -225,7 +225,8 @@ "bindings": { "cmd-n": "assistant2::NewThread", "cmd-shift-h": "assistant2::OpenHistory", - "cmd-shift-m": "assistant2::ToggleModelSelector" + "cmd-shift-m": "assistant2::ToggleModelSelector", + "cmd-shift-a": "assistant2::ToggleContextPicker" } }, { @@ -618,6 +619,7 @@ "context": "PromptEditor", "use_key_equivalents": true, "bindings": { + "cmd-shift-a": "assistant2::ToggleContextPicker", "ctrl-[": "assistant::CyclePreviousInlineAssist", "ctrl-]": "assistant::CycleNextInlineAssist" } diff --git a/crates/assistant2/src/assistant.rs b/crates/assistant2/src/assistant.rs index 64f1416fa4..298360c6f3 100644 --- a/crates/assistant2/src/assistant.rs +++ b/crates/assistant2/src/assistant.rs @@ -34,6 +34,7 @@ actions!( [ ToggleFocus, NewThread, + ToggleContextPicker, ToggleModelSelector, OpenHistory, Chat, diff --git a/crates/assistant2/src/context_strip.rs b/crates/assistant2/src/context_strip.rs index fa0f7cab8a..0cdd858c61 100644 --- a/crates/assistant2/src/context_strip.rs +++ b/crates/assistant2/src/context_strip.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use gpui::{Model, View, WeakModel, WeakView}; +use gpui::{FocusHandle, Model, View, WeakModel, WeakView}; use ui::{prelude::*, PopoverMenu, PopoverMenuHandle, Tooltip}; use workspace::Workspace; @@ -8,11 +8,13 @@ use crate::context_picker::ContextPicker; use crate::context_store::ContextStore; use crate::thread_store::ThreadStore; use crate::ui::ContextPill; +use crate::ToggleContextPicker; pub struct ContextStrip { context_store: Model, context_picker: View, - pub(crate) context_picker_handle: PopoverMenuHandle, + context_picker_menu_handle: PopoverMenuHandle, + focus_handle: FocusHandle, } impl ContextStrip { @@ -20,6 +22,8 @@ impl ContextStrip { context_store: Model, workspace: WeakView, thread_store: Option>, + focus_handle: FocusHandle, + context_picker_menu_handle: PopoverMenuHandle, cx: &mut ViewContext, ) -> Self { Self { @@ -32,7 +36,8 @@ impl ContextStrip { cx, ) }), - context_picker_handle: PopoverMenuHandle::default(), + context_picker_menu_handle, + focus_handle, } } } @@ -41,6 +46,7 @@ impl Render for ContextStrip { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let context = self.context_store.read(cx).context(); let context_picker = self.context_picker.clone(); + let focus_handle = self.focus_handle.clone(); h_flex() .flex_wrap() @@ -51,7 +57,15 @@ impl Render for ContextStrip { .trigger( IconButton::new("add-context", IconName::Plus) .icon_size(IconSize::Small) - .style(ui::ButtonStyle::Filled), + .style(ui::ButtonStyle::Filled) + .tooltip(move |cx| { + Tooltip::for_action_in( + "Add Context", + &ToggleContextPicker, + &focus_handle, + cx, + ) + }), ) .attach(gpui::AnchorCorner::TopLeft) .anchor(gpui::AnchorCorner::BottomLeft) @@ -59,7 +73,7 @@ impl Render for ContextStrip { x: px(0.0), y: px(-16.0), }) - .with_handle(self.context_picker_handle.clone()), + .with_handle(self.context_picker_menu_handle.clone()), ) .children(context.iter().map(|context| { ContextPill::new(context.clone()).on_remove({ diff --git a/crates/assistant2/src/inline_assistant.rs b/crates/assistant2/src/inline_assistant.rs index 4f260518ae..9ecf166c51 100644 --- a/crates/assistant2/src/inline_assistant.rs +++ b/crates/assistant2/src/inline_assistant.rs @@ -1,8 +1,8 @@ use crate::context::attach_context_to_message; +use crate::context_picker::ContextPicker; use crate::context_store::ContextStore; use crate::context_strip::ContextStrip; use crate::thread_store::ThreadStore; -use crate::AssistantPanel; use crate::{ assistant_settings::AssistantSettings, prompts::PromptBuilder, @@ -10,6 +10,7 @@ use crate::{ terminal_inline_assistant::TerminalInlineAssistant, CycleNextInlineAssist, CyclePreviousInlineAssist, ToggleInlineAssist, }; +use crate::{AssistantPanel, ToggleContextPicker}; use anyhow::{Context as _, Result}; use client::{telemetry::Telemetry, ErrorExt}; use collections::{hash_map, HashMap, HashSet, VecDeque}; @@ -60,7 +61,9 @@ use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase}; use terminal_view::{terminal_panel::TerminalPanel, TerminalView}; use text::{OffsetRangeExt, ToPoint as _}; use theme::ThemeSettings; -use ui::{prelude::*, CheckboxWithLabel, IconButtonShape, KeyBinding, Popover, Tooltip}; +use ui::{ + prelude::*, CheckboxWithLabel, IconButtonShape, KeyBinding, Popover, PopoverMenuHandle, Tooltip, +}; use util::{RangeExt, ResultExt}; use workspace::{dock::Panel, ShowConfiguration}; use workspace::{notifications::NotificationId, ItemHandle, Toast, Workspace}; @@ -1486,6 +1489,7 @@ struct PromptEditor { id: InlineAssistId, editor: View, context_strip: View, + context_picker_menu_handle: PopoverMenuHandle, language_model_selector: View, edited_since_done: bool, gutter_dimensions: Arc>, @@ -1607,6 +1611,7 @@ impl Render for PromptEditor { .bg(cx.theme().colors().editor_background) .block_mouse_down() .cursor(CursorStyle::Arrow) + .on_action(cx.listener(Self::toggle_context_picker)) .on_action(cx.listener(Self::confirm)) .on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::move_up)) @@ -1747,13 +1752,22 @@ impl PromptEditor { editor.set_placeholder_text(Self::placeholder_text(codegen.read(cx)), cx); editor }); + let context_picker_menu_handle = PopoverMenuHandle::default(); let mut this = Self { id, - editor: prompt_editor, + editor: prompt_editor.clone(), context_strip: cx.new_view(|cx| { - ContextStrip::new(context_store, workspace.clone(), thread_store.clone(), cx) + ContextStrip::new( + context_store, + workspace.clone(), + thread_store.clone(), + prompt_editor.focus_handle(cx), + context_picker_menu_handle.clone(), + cx, + ) }), + context_picker_menu_handle, language_model_selector: cx.new_view(|cx| { let fs = fs.clone(); LanguageModelSelector::new( @@ -1911,6 +1925,10 @@ impl PromptEditor { } } + fn toggle_context_picker(&mut self, _: &ToggleContextPicker, cx: &mut ViewContext) { + self.context_picker_menu_handle.toggle(cx); + } + fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext) { match self.codegen.read(cx).status(cx) { CodegenStatus::Idle | CodegenStatus::Done | CodegenStatus::Error(_) => { diff --git a/crates/assistant2/src/message_editor.rs b/crates/assistant2/src/message_editor.rs index 64a40f5b95..a01af8323c 100644 --- a/crates/assistant2/src/message_editor.rs +++ b/crates/assistant2/src/message_editor.rs @@ -14,17 +14,19 @@ use ui::{ use workspace::Workspace; use crate::assistant_settings::AssistantSettings; +use crate::context_picker::ContextPicker; use crate::context_store::ContextStore; use crate::context_strip::ContextStrip; use crate::thread::{RequestKind, Thread}; use crate::thread_store::ThreadStore; -use crate::{Chat, ToggleModelSelector}; +use crate::{Chat, ToggleContextPicker, ToggleModelSelector}; pub struct MessageEditor { thread: Model, editor: View, context_store: Model, context_strip: View, + context_picker_menu_handle: PopoverMenuHandle, language_model_selector: View, language_model_selector_menu_handle: PopoverMenuHandle, use_tools: bool, @@ -39,25 +41,31 @@ impl MessageEditor { cx: &mut ViewContext, ) -> Self { let context_store = cx.new_model(|_cx| ContextStore::new()); + let context_picker_menu_handle = PopoverMenuHandle::default(); + + let editor = cx.new_view(|cx| { + let mut editor = Editor::auto_height(80, cx); + editor.set_placeholder_text("Ask anything, @ to add context", cx); + editor.set_show_indent_guides(false, cx); + + editor + }); Self { thread, - editor: cx.new_view(|cx| { - let mut editor = Editor::auto_height(80, cx); - editor.set_placeholder_text("Ask anything, @ to add context", cx); - editor.set_show_indent_guides(false, cx); - - editor - }), + editor: editor.clone(), context_store: context_store.clone(), context_strip: cx.new_view(|cx| { ContextStrip::new( context_store, workspace.clone(), Some(thread_store.clone()), + editor.focus_handle(cx), + context_picker_menu_handle.clone(), cx, ) }), + context_picker_menu_handle, language_model_selector: cx.new_view(|cx| { let fs = fs.clone(); LanguageModelSelector::new( @@ -80,6 +88,10 @@ impl MessageEditor { self.language_model_selector_menu_handle.toggle(cx); } + fn toggle_context_picker(&mut self, _: &ToggleContextPicker, cx: &mut ViewContext) { + self.context_picker_menu_handle.toggle(cx); + } + fn chat(&mut self, _: &Chat, cx: &mut ViewContext) { self.send_to_model(RequestKind::Chat, cx); } @@ -193,6 +205,7 @@ impl Render for MessageEditor { .key_context("MessageEditor") .on_action(cx.listener(Self::chat)) .on_action(cx.listener(Self::toggle_model_selector)) + .on_action(cx.listener(Self::toggle_context_picker)) .size_full() .gap_2() .p_2() diff --git a/crates/assistant2/src/terminal_inline_assistant.rs b/crates/assistant2/src/terminal_inline_assistant.rs index 48851bf82c..95b2cf7ddb 100644 --- a/crates/assistant2/src/terminal_inline_assistant.rs +++ b/crates/assistant2/src/terminal_inline_assistant.rs @@ -1,9 +1,11 @@ use crate::assistant_settings::AssistantSettings; use crate::context::attach_context_to_message; +use crate::context_picker::ContextPicker; use crate::context_store::ContextStore; use crate::context_strip::ContextStrip; use crate::prompts::PromptBuilder; use crate::thread_store::ThreadStore; +use crate::ToggleContextPicker; use anyhow::{Context as _, Result}; use client::telemetry::Telemetry; use collections::{HashMap, VecDeque}; @@ -29,7 +31,7 @@ use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase}; use terminal::Terminal; use terminal_view::TerminalView; use theme::ThemeSettings; -use ui::{prelude::*, text_for_action, IconButtonShape, Tooltip}; +use ui::{prelude::*, text_for_action, IconButtonShape, PopoverMenuHandle, Tooltip}; use util::ResultExt; use workspace::{notifications::NotificationId, Toast, Workspace}; @@ -460,6 +462,7 @@ struct PromptEditor { height_in_lines: u8, editor: View, context_strip: View, + context_picker_menu_handle: PopoverMenuHandle, language_model_selector: View, edited_since_done: bool, prompt_history: VecDeque, @@ -580,7 +583,9 @@ impl Render for PromptEditor { .size_full() .child( h_flex() + .key_context("PromptEditor") .bg(cx.theme().colors().editor_background) + .on_action(cx.listener(Self::toggle_context_picker)) .on_action(cx.listener(Self::confirm)) .on_action(cx.listener(Self::secondary_confirm)) .on_action(cx.listener(Self::cancel)) @@ -674,14 +679,23 @@ impl PromptEditor { editor.set_placeholder_text(Self::placeholder_text(cx), cx); editor }); + let context_picker_menu_handle = PopoverMenuHandle::default(); let mut this = Self { id, height_in_lines: 1, - editor: prompt_editor, + editor: prompt_editor.clone(), context_strip: cx.new_view(|cx| { - ContextStrip::new(context_store, workspace.clone(), thread_store.clone(), cx) + ContextStrip::new( + context_store, + workspace.clone(), + thread_store.clone(), + prompt_editor.focus_handle(cx), + context_picker_menu_handle.clone(), + cx, + ) }), + context_picker_menu_handle, language_model_selector: cx.new_view(|cx| { let fs = fs.clone(); LanguageModelSelector::new( @@ -790,6 +804,10 @@ impl PromptEditor { } } + fn toggle_context_picker(&mut self, _: &ToggleContextPicker, cx: &mut ViewContext) { + self.context_picker_menu_handle.toggle(cx); + } + fn cancel(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext) { match &self.codegen.read(cx).status { CodegenStatus::Idle | CodegenStatus::Done | CodegenStatus::Error(_) => {