assistant2: Add prompt editor history (#23439)
This PR adds the prompt editor history to Assistant2. <img width="1309" alt="Screenshot 2025-01-21 at 9 02 07 PM" src="https://github.com/user-attachments/assets/d79936fe-1c23-425f-b99d-43f85afd0c39" /> Release Notes: - N/A
This commit is contained in:
parent
be407e27f9
commit
7516b8c8b7
3 changed files with 163 additions and 32 deletions
|
@ -1342,7 +1342,6 @@ impl AssistantPanelDelegate for ConcreteAssistantPanelDelegate {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Activate the panel
|
|
||||||
if !panel.focus_handle(cx).contains_focused(cx) {
|
if !panel.focus_handle(cx).contains_focused(cx) {
|
||||||
workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ actions!(
|
||||||
ToggleModelSelector,
|
ToggleModelSelector,
|
||||||
RemoveAllContext,
|
RemoveAllContext,
|
||||||
OpenHistory,
|
OpenHistory,
|
||||||
|
OpenPromptEditorHistory,
|
||||||
RemoveSelectedThread,
|
RemoveSelectedThread,
|
||||||
Chat,
|
Chat,
|
||||||
ChatMode,
|
ChatMode,
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_context_editor::{make_lsp_adapter_delegate, ContextEditor};
|
use assistant_context_editor::{
|
||||||
|
make_lsp_adapter_delegate, AssistantPanelDelegate, ContextEditor, ContextHistory,
|
||||||
|
};
|
||||||
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||||
use assistant_slash_command::SlashCommandWorkingSet;
|
use assistant_slash_command::SlashCommandWorkingSet;
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
|
@ -13,6 +16,7 @@ use gpui::{
|
||||||
WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
|
use project::Project;
|
||||||
use prompt_library::PromptBuilder;
|
use prompt_library::PromptBuilder;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
|
@ -26,9 +30,10 @@ use crate::message_editor::MessageEditor;
|
||||||
use crate::thread::{Thread, ThreadError, ThreadId};
|
use crate::thread::{Thread, ThreadError, ThreadId};
|
||||||
use crate::thread_history::{PastThread, ThreadHistory};
|
use crate::thread_history::{PastThread, ThreadHistory};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{NewPromptEditor, NewThread, OpenHistory, ToggleFocus};
|
use crate::{NewPromptEditor, NewThread, OpenHistory, OpenPromptEditorHistory, ToggleFocus};
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
<dyn AssistantPanelDelegate>::set_global(Arc::new(ConcreteAssistantPanelDelegate), cx);
|
||||||
cx.observe_new_views(
|
cx.observe_new_views(
|
||||||
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
|
|workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
|
||||||
workspace
|
workspace
|
||||||
|
@ -50,7 +55,13 @@ pub fn init(cx: &mut AppContext) {
|
||||||
.register_action(|workspace, _: &NewPromptEditor, cx| {
|
.register_action(|workspace, _: &NewPromptEditor, cx| {
|
||||||
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||||
workspace.focus_panel::<AssistantPanel>(cx);
|
workspace.focus_panel::<AssistantPanel>(cx);
|
||||||
panel.update(cx, |panel, cx| panel.new_prompt_editor(workspace, cx));
|
panel.update(cx, |panel, cx| panel.new_prompt_editor(cx));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.register_action(|workspace, _: &OpenPromptEditorHistory, cx| {
|
||||||
|
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||||
|
workspace.focus_panel::<AssistantPanel>(cx);
|
||||||
|
panel.update(cx, |panel, cx| panel.open_prompt_editor_history(cx));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -62,10 +73,12 @@ enum ActiveView {
|
||||||
Thread,
|
Thread,
|
||||||
PromptEditor,
|
PromptEditor,
|
||||||
History,
|
History,
|
||||||
|
PromptEditorHistory,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AssistantPanel {
|
pub struct AssistantPanel {
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
|
project: Model<Project>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
thread_store: Model<ThreadStore>,
|
thread_store: Model<ThreadStore>,
|
||||||
|
@ -73,11 +86,13 @@ pub struct AssistantPanel {
|
||||||
message_editor: View<MessageEditor>,
|
message_editor: View<MessageEditor>,
|
||||||
context_store: Model<assistant_context_editor::ContextStore>,
|
context_store: Model<assistant_context_editor::ContextStore>,
|
||||||
context_editor: Option<View<ContextEditor>>,
|
context_editor: Option<View<ContextEditor>>,
|
||||||
|
context_history: Option<View<ContextHistory>>,
|
||||||
tools: Arc<ToolWorkingSet>,
|
tools: Arc<ToolWorkingSet>,
|
||||||
local_timezone: UtcOffset,
|
local_timezone: UtcOffset,
|
||||||
active_view: ActiveView,
|
active_view: ActiveView,
|
||||||
history: View<ThreadHistory>,
|
history: View<ThreadHistory>,
|
||||||
new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
|
open_history_context_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
width: Option<Pixels>,
|
width: Option<Pixels>,
|
||||||
height: Option<Pixels>,
|
height: Option<Pixels>,
|
||||||
}
|
}
|
||||||
|
@ -126,7 +141,8 @@ impl AssistantPanel {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));
|
let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));
|
||||||
let fs = workspace.app_state().fs.clone();
|
let fs = workspace.app_state().fs.clone();
|
||||||
let language_registry = workspace.project().read(cx).languages().clone();
|
let project = workspace.project().clone();
|
||||||
|
let language_registry = project.read(cx).languages().clone();
|
||||||
let workspace = workspace.weak_handle();
|
let workspace = workspace.weak_handle();
|
||||||
let weak_self = cx.view().downgrade();
|
let weak_self = cx.view().downgrade();
|
||||||
|
|
||||||
|
@ -143,6 +159,7 @@ impl AssistantPanel {
|
||||||
Self {
|
Self {
|
||||||
active_view: ActiveView::Thread,
|
active_view: ActiveView::Thread,
|
||||||
workspace: workspace.clone(),
|
workspace: workspace.clone(),
|
||||||
|
project,
|
||||||
fs: fs.clone(),
|
fs: fs.clone(),
|
||||||
language_registry: language_registry.clone(),
|
language_registry: language_registry.clone(),
|
||||||
thread_store: thread_store.clone(),
|
thread_store: thread_store.clone(),
|
||||||
|
@ -158,6 +175,7 @@ impl AssistantPanel {
|
||||||
message_editor,
|
message_editor,
|
||||||
context_store,
|
context_store,
|
||||||
context_editor: None,
|
context_editor: None,
|
||||||
|
context_history: None,
|
||||||
tools,
|
tools,
|
||||||
local_timezone: UtcOffset::from_whole_seconds(
|
local_timezone: UtcOffset::from_whole_seconds(
|
||||||
chrono::Local::now().offset().local_minus_utc(),
|
chrono::Local::now().offset().local_minus_utc(),
|
||||||
|
@ -165,6 +183,7 @@ impl AssistantPanel {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
history: cx.new_view(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
|
history: cx.new_view(|cx| ThreadHistory::new(weak_self, thread_store, cx)),
|
||||||
new_item_context_menu_handle: PopoverMenuHandle::default(),
|
new_item_context_menu_handle: PopoverMenuHandle::default(),
|
||||||
|
open_history_context_menu_handle: PopoverMenuHandle::default(),
|
||||||
width: None,
|
width: None,
|
||||||
height: None,
|
height: None,
|
||||||
}
|
}
|
||||||
|
@ -210,21 +229,22 @@ impl AssistantPanel {
|
||||||
self.message_editor.focus_handle(cx).focus(cx);
|
self.message_editor.focus_handle(cx).focus(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_prompt_editor(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
fn new_prompt_editor(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
self.active_view = ActiveView::PromptEditor;
|
self.active_view = ActiveView::PromptEditor;
|
||||||
|
|
||||||
let project = workspace.project().clone();
|
|
||||||
let context = self
|
let context = self
|
||||||
.context_store
|
.context_store
|
||||||
.update(cx, |context_store, cx| context_store.create(cx));
|
.update(cx, |context_store, cx| context_store.create(cx));
|
||||||
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
|
let lsp_adapter_delegate = make_lsp_adapter_delegate(&self.project, cx)
|
||||||
|
.log_err()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
self.context_editor = Some(cx.new_view(|cx| {
|
self.context_editor = Some(cx.new_view(|cx| {
|
||||||
ContextEditor::for_context(
|
ContextEditor::for_context(
|
||||||
context,
|
context,
|
||||||
self.fs.clone(),
|
self.fs.clone(),
|
||||||
self.workspace.clone(),
|
self.workspace.clone(),
|
||||||
project,
|
self.project.clone(),
|
||||||
lsp_adapter_delegate,
|
lsp_adapter_delegate,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -241,6 +261,60 @@ impl AssistantPanel {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_prompt_editor_history(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
self.active_view = ActiveView::PromptEditorHistory;
|
||||||
|
self.context_history = Some(cx.new_view(|cx| {
|
||||||
|
ContextHistory::new(
|
||||||
|
self.project.clone(),
|
||||||
|
self.context_store.clone(),
|
||||||
|
self.workspace.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
|
if let Some(context_history) = self.context_history.as_ref() {
|
||||||
|
context_history.focus_handle(cx).focus(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_saved_prompt_editor(
|
||||||
|
&mut self,
|
||||||
|
path: PathBuf,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let context = self
|
||||||
|
.context_store
|
||||||
|
.update(cx, |store, cx| store.open_local_context(path.clone(), cx));
|
||||||
|
let fs = self.fs.clone();
|
||||||
|
let project = self.project.clone();
|
||||||
|
let workspace = self.workspace.clone();
|
||||||
|
|
||||||
|
let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err().flatten();
|
||||||
|
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
let context = context.await?;
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
let editor = cx.new_view(|cx| {
|
||||||
|
ContextEditor::for_context(
|
||||||
|
context,
|
||||||
|
fs,
|
||||||
|
workspace,
|
||||||
|
project,
|
||||||
|
lsp_adapter_delegate,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
this.active_view = ActiveView::PromptEditor;
|
||||||
|
this.context_editor = Some(editor);
|
||||||
|
|
||||||
|
anyhow::Ok(())
|
||||||
|
})??;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn open_thread(&mut self, thread_id: &ThreadId, cx: &mut ViewContext<Self>) {
|
pub(crate) fn open_thread(&mut self, thread_id: &ThreadId, cx: &mut ViewContext<Self>) {
|
||||||
let Some(thread) = self
|
let Some(thread) = self
|
||||||
.thread_store
|
.thread_store
|
||||||
|
@ -285,8 +359,21 @@ impl FocusableView for AssistantPanel {
|
||||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||||
match self.active_view {
|
match self.active_view {
|
||||||
ActiveView::Thread => self.message_editor.focus_handle(cx),
|
ActiveView::Thread => self.message_editor.focus_handle(cx),
|
||||||
ActiveView::PromptEditor => self.context_editor.as_ref().unwrap().focus_handle(cx),
|
|
||||||
ActiveView::History => self.history.focus_handle(cx),
|
ActiveView::History => self.history.focus_handle(cx),
|
||||||
|
ActiveView::PromptEditor => {
|
||||||
|
if let Some(context_editor) = self.context_editor.as_ref() {
|
||||||
|
context_editor.focus_handle(cx)
|
||||||
|
} else {
|
||||||
|
cx.focus_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveView::PromptEditorHistory => {
|
||||||
|
if let Some(context_history) = self.context_history.as_ref() {
|
||||||
|
context_history.focus_handle(cx)
|
||||||
|
} else {
|
||||||
|
cx.focus_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,8 +456,6 @@ impl Panel for AssistantPanel {
|
||||||
|
|
||||||
impl AssistantPanel {
|
impl AssistantPanel {
|
||||||
fn render_toolbar(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_toolbar(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let focus_handle = self.focus_handle(cx);
|
|
||||||
|
|
||||||
let thread = self.thread.read(cx);
|
let thread = self.thread.read(cx);
|
||||||
|
|
||||||
let title = match self.active_view {
|
let title = match self.active_view {
|
||||||
|
@ -390,7 +475,8 @@ impl AssistantPanel {
|
||||||
SharedString::from(context_editor.read(cx).title(cx).to_string())
|
SharedString::from(context_editor.read(cx).title(cx).to_string())
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
||||||
ActiveView::History => "History".into(),
|
ActiveView::History => "History / Thread".into(),
|
||||||
|
ActiveView::PromptEditorHistory => "History / Prompt Editor".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
|
@ -412,7 +498,7 @@ impl AssistantPanel {
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.gap(DynamicSpacing::Base02.rems(cx))
|
.gap(DynamicSpacing::Base02.rems(cx))
|
||||||
.child(
|
.child(
|
||||||
PopoverMenu::new("assistant-toolbar-popover-menu")
|
PopoverMenu::new("assistant-toolbar-new-popover-menu")
|
||||||
.trigger(
|
.trigger(
|
||||||
IconButton::new("new", IconName::Plus)
|
IconButton::new("new", IconName::Plus)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -429,22 +515,23 @@ impl AssistantPanel {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
IconButton::new("open-history", IconName::HistoryRerun)
|
PopoverMenu::new("assistant-toolbar-history-popover-menu")
|
||||||
.icon_size(IconSize::Small)
|
.trigger(
|
||||||
.style(ButtonStyle::Subtle)
|
IconButton::new("open-history", IconName::HistoryRerun)
|
||||||
.tooltip({
|
.icon_size(IconSize::Small)
|
||||||
let focus_handle = focus_handle.clone();
|
.style(ButtonStyle::Subtle)
|
||||||
move |cx| {
|
.tooltip(|cx| Tooltip::text("History…", cx)),
|
||||||
Tooltip::for_action_in(
|
)
|
||||||
"Open History",
|
.anchor(Corner::TopRight)
|
||||||
&OpenHistory,
|
.with_handle(self.open_history_context_menu_handle.clone())
|
||||||
&focus_handle,
|
.menu(move |cx| {
|
||||||
cx,
|
Some(ContextMenu::build(cx, |menu, _| {
|
||||||
)
|
menu.action("Thread History", OpenHistory.boxed_clone())
|
||||||
}
|
.action(
|
||||||
})
|
"Prompt Editor History",
|
||||||
.on_click(move |_event, cx| {
|
OpenPromptEditorHistory.boxed_clone(),
|
||||||
cx.dispatch_action(OpenHistory.boxed_clone());
|
)
|
||||||
|
}))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
@ -704,8 +791,52 @@ impl Render for AssistantPanel {
|
||||||
.child(self.message_editor.clone()),
|
.child(self.message_editor.clone()),
|
||||||
)
|
)
|
||||||
.children(self.render_last_error(cx)),
|
.children(self.render_last_error(cx)),
|
||||||
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
|
||||||
ActiveView::History => parent.child(self.history.clone()),
|
ActiveView::History => parent.child(self.history.clone()),
|
||||||
|
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
||||||
|
ActiveView::PromptEditorHistory => parent.children(self.context_history.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ConcreteAssistantPanelDelegate;
|
||||||
|
|
||||||
|
impl AssistantPanelDelegate for ConcreteAssistantPanelDelegate {
|
||||||
|
fn active_context_editor(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Option<View<ContextEditor>> {
|
||||||
|
let panel = workspace.panel::<AssistantPanel>(cx)?;
|
||||||
|
panel.update(cx, |panel, _cx| panel.context_editor.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_saved_context(
|
||||||
|
&self,
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
path: std::path::PathBuf,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
||||||
|
return Task::ready(Err(anyhow!("Assistant panel not found")));
|
||||||
|
};
|
||||||
|
|
||||||
|
panel.update(cx, |panel, cx| panel.open_saved_prompt_editor(path, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_remote_context(
|
||||||
|
&self,
|
||||||
|
_workspace: &mut Workspace,
|
||||||
|
_context_id: assistant_context_editor::ContextId,
|
||||||
|
_cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Task<Result<View<ContextEditor>>> {
|
||||||
|
Task::ready(Err(anyhow!("opening remote context not implemented")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quote_selection(
|
||||||
|
&self,
|
||||||
|
_workspace: &mut Workspace,
|
||||||
|
_creases: Vec<(String, String)>,
|
||||||
|
_cx: &mut ViewContext<Workspace>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue