@@ -2,10 +2,11 @@ mod assistant_settings;
mod attachments;
mod completion_provider;
mod saved_conversation;
-mod saved_conversation_picker;
+mod saved_conversations;
mod tools;
pub mod ui;
+use crate::saved_conversation::SavedConversationMetadata;
use crate::ui::UserOrAssistant;
use ::ui::{div, prelude::*, Color, Tooltip, ViewContext};
use anyhow::{Context, Result};
@@ -29,7 +30,7 @@ use language::{language_settings::SoftWrap, LanguageRegistry};
use open_ai::{FunctionContent, ToolCall, ToolCallContent};
use rich_text::RichText;
use saved_conversation::{SavedAssistantMessagePart, SavedChatMessage, SavedConversation};
-use saved_conversation_picker::SavedConversationPicker;
+use saved_conversations::SavedConversations;
use semantic_index::{CloudEmbeddingProvider, ProjectIndex, ProjectIndexDebugView, SemanticIndex};
use serde::{Deserialize, Serialize};
use settings::Settings;
@@ -61,15 +62,7 @@ pub enum SubmitMode {
Codebase,
}
-gpui::actions!(
- assistant2,
- [
- Cancel,
- ToggleFocus,
- DebugProjectIndex,
- ToggleSavedConversations
- ]
-);
+gpui::actions!(assistant2, [Cancel, ToggleFocus, DebugProjectIndex,]);
gpui::impl_actions!(assistant2, [Submit]);
pub fn init(client: Arc<Client>, cx: &mut AppContext) {
@@ -109,8 +102,6 @@ pub fn init(client: Arc<Client>, cx: &mut AppContext) {
},
)
.detach();
- cx.observe_new_views(SavedConversationPicker::register)
- .detach();
}
pub fn enabled(cx: &AppContext) -> bool {
@@ -262,6 +253,8 @@ pub struct AssistantChat {
fs: Arc<dyn Fs>,
language_registry: Arc<LanguageRegistry>,
composer_editor: View<Editor>,
+ saved_conversations: View<SavedConversations>,
+ saved_conversations_open: bool,
project_index_button: View<ProjectIndexButton>,
active_file_button: Option<View<ActiveFileButton>>,
user_store: Model<UserStore>,
@@ -317,6 +310,24 @@ impl AssistantChat {
_ => None,
};
+ let saved_conversations = cx.new_view(|cx| SavedConversations::new(cx));
+ cx.spawn({
+ let fs = fs.clone();
+ let saved_conversations = saved_conversations.downgrade();
+ |_assistant_chat, mut cx| async move {
+ let saved_conversation_metadata = SavedConversationMetadata::list(fs).await?;
+
+ cx.update(|cx| {
+ saved_conversations.update(cx, |this, cx| {
+ this.init(saved_conversation_metadata, cx);
+ })
+ })??;
+
+ anyhow::Ok(())
+ }
+ })
+ .detach_and_log_err(cx);
+
Self {
model,
messages: Vec::new(),
@@ -326,6 +337,8 @@ impl AssistantChat {
editor.set_placeholder_text("Send a message…", cx);
editor
}),
+ saved_conversations,
+ saved_conversations_open: false,
list_state,
user_store,
fs,
@@ -357,6 +370,10 @@ impl AssistantChat {
})
}
+ fn toggle_saved_conversations(&mut self) {
+ self.saved_conversations_open = !self.saved_conversations_open;
+ }
+
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
// If we're currently editing a message, cancel the edit.
if let Some(editing_message) = self.editing_message.take() {
@@ -1017,18 +1034,18 @@ impl Render for AssistantChat {
.h(header_height)
.p(Spacing::Small.rems(cx))
.child(
- IconButton::new("open-saved-conversations", IconName::ChevronLeft)
- .on_click(|_event, cx| {
- cx.dispatch_action(Box::new(ToggleSavedConversations))
- })
- .tooltip(move |cx| {
- Tooltip::with_meta(
- "Switch Conversations",
- Some(&ToggleSavedConversations),
- "UI will change, temporary.",
- cx,
- )
- }),
+ IconButton::new(
+ "toggle-saved-conversations",
+ if self.saved_conversations_open {
+ IconName::ChevronRight
+ } else {
+ IconName::ChevronLeft
+ },
+ )
+ .on_click(cx.listener(|this, _event, _cx| {
+ this.toggle_saved_conversations();
+ }))
+ .tooltip(move |cx| Tooltip::text("Switch Conversations", cx)),
)
.child(
h_flex()
@@ -1052,6 +1069,15 @@ impl Render for AssistantChat {
),
),
)
+ .when(self.saved_conversations_open, |element| {
+ element.child(
+ h_flex()
+ .absolute()
+ .top(header_height)
+ .w_full()
+ .child(self.saved_conversations.clone()),
+ )
+ })
.child(Composer::new(
self.composer_editor.clone(),
self.project_index_button.clone(),
@@ -5,65 +5,56 @@ use gpui::{AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, V
use picker::{Picker, PickerDelegate};
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use util::ResultExt;
-use workspace::{ModalView, Workspace};
use crate::saved_conversation::SavedConversationMetadata;
-use crate::ToggleSavedConversations;
-pub struct SavedConversationPicker {
- picker: View<Picker<SavedConversationPickerDelegate>>,
+pub struct SavedConversations {
+ focus_handle: FocusHandle,
+ picker: Option<View<Picker<SavedConversationPickerDelegate>>>,
}
-impl EventEmitter<DismissEvent> for SavedConversationPicker {}
+impl EventEmitter<DismissEvent> for SavedConversations {}
-impl ModalView for SavedConversationPicker {}
-
-impl FocusableView for SavedConversationPicker {
+impl FocusableView for SavedConversations {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
- self.picker.focus_handle(cx)
+ if let Some(picker) = self.picker.as_ref() {
+ picker.focus_handle(cx)
+ } else {
+ self.focus_handle.clone()
+ }
}
}
-impl SavedConversationPicker {
- pub fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
- workspace.register_action(|workspace, _: &ToggleSavedConversations, cx| {
- let fs = workspace.project().read(cx).fs().clone();
-
- cx.spawn(|workspace, mut cx| async move {
- let saved_conversations = SavedConversationMetadata::list(fs).await?;
-
- cx.update(|cx| {
- workspace.update(cx, |workspace, cx| {
- workspace.toggle_modal(cx, move |cx| {
- let delegate = SavedConversationPickerDelegate::new(
- cx.view().downgrade(),
- saved_conversations,
- );
- Self::new(delegate, cx)
- });
- })
- })??;
-
- anyhow::Ok(())
- })
- .detach_and_log_err(cx);
- });
+impl SavedConversations {
+ pub fn new(cx: &mut ViewContext<Self>) -> Self {
+ Self {
+ focus_handle: cx.focus_handle(),
+ picker: None,
+ }
}
- pub fn new(delegate: SavedConversationPickerDelegate, cx: &mut ViewContext<Self>) -> Self {
- let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
- Self { picker }
+ pub fn init(
+ &mut self,
+ saved_conversations: Vec<SavedConversationMetadata>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ let delegate =
+ SavedConversationPickerDelegate::new(cx.view().downgrade(), saved_conversations);
+ self.picker = Some(cx.new_view(|cx| Picker::uniform_list(delegate, cx).modal(false)));
}
}
-impl Render for SavedConversationPicker {
- fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
- v_flex().w(rems(34.)).child(self.picker.clone())
+impl Render for SavedConversations {
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+ v_flex()
+ .w_full()
+ .bg(cx.theme().colors().panel_background)
+ .children(self.picker.clone())
}
}
pub struct SavedConversationPickerDelegate {
- view: WeakView<SavedConversationPicker>,
+ view: WeakView<SavedConversations>,
saved_conversations: Vec<SavedConversationMetadata>,
selected_index: usize,
matches: Vec<StringMatch>,
@@ -71,7 +62,7 @@ pub struct SavedConversationPickerDelegate {
impl SavedConversationPickerDelegate {
pub fn new(
- weak_view: WeakView<SavedConversationPicker>,
+ weak_view: WeakView<SavedConversations>,
saved_conversations: Vec<SavedConversationMetadata>,
) -> Self {
let matches = saved_conversations
@@ -194,7 +185,6 @@ impl PickerDelegate for SavedConversationPickerDelegate {
Some(
ListItem::new(ix)
- .inset(true)
.spacing(ListItemSpacing::Sparse)
.selected(selected)
.child(HighlightedLabel::new(