use std::path::PathBuf;use std::sync::Arc;use std::time::Duration; use anyhow::{Result, anyhow};use assistant_context_editor::{ AssistantPanelDelegate, ConfigurationError, ContextEditor, SlashCommandCompletionProvider, make_lsp_adapter_delegate, render_remaining_tokens,};use assistant_settings::{AssistantDockPosition, AssistantSettings};use assistant_slash_command::SlashCommandWorkingSet;use assistant_tool::ToolWorkingSet; use client::zed_urls;use editor::{Editor, EditorEvent, MultiBuffer};use fs::Fs;use gpui::{ Action, Animation, AnimationExt as _, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, KeyContext, Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,};use language::LanguageRegistry;use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};use language_model_selector::ToggleModelSelector;use project::Project;use prompt_library::{PromptLibrary, open_prompt_library};use prompt_store::PromptBuilder;use settings::{Settings, update_settings_file};use time::UtcOffset;use ui::{ Banner, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip, prelude::*,};use util::ResultExt as _;use workspace::Workspace;use workspace::dock::{DockPosition, Panel, PanelEvent};use zed_actions::agent::OpenConfiguration;use zed_actions::assistant::{OpenPromptLibrary, ToggleFocus}; use crate::active_thread::ActiveThread;use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};use crate::history_store::{HistoryEntry, HistoryStore};use crate::message_editor::MessageEditor;use crate::thread::{Thread, ThreadError, ThreadId, TokenUsageRatio};use crate::thread_history::{PastContext, PastThread, ThreadHistory};use crate::thread_store::ThreadStore;use crate::{ AgentDiff, ExpandMessageEditor, InlineAssistant, NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ThreadEvent, ToggleContextPicker,}; pub fn init(cx: &mut App) { cx.observe_new( |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| { workspace .register_action(|workspace, action: &NewThread, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { panel.update(cx, |panel, cx| panel.new_thread(action, window, cx)); workspace.focus_panel::<AssistantPanel>(window, cx); } }) .register_action(|workspace, _: &OpenHistory, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| panel.open_history(window, cx)); } }) .register_action(|workspace, _: &OpenConfiguration, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| panel.open_configuration(window, cx)); } }) .register_action(|workspace, _: &NewTextThread, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx)); } }) .register_action(|workspace, _: &OpenPromptLibrary, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| { panel.deploy_prompt_library(&OpenPromptLibrary, window, cx) }); } }) .register_action(|workspace, _: &OpenAgentDiff, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); let thread = panel.read(cx).thread.read(cx).thread().clone(); AgentDiff::deploy_in_workspace(thread, workspace, window, cx); } }) .register_action(|workspace, _: &ExpandMessageEditor, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| { panel.message_editor.update(cx, |editor, cx| { editor.expand_message_editor(&ExpandMessageEditor, window, cx); }); }); } }); }, ) .detach();} enum ActiveView { Thread { change_title_editor: Entity<Editor>, _subscriptions: Vec<gpui::Subscription>, }, PromptEditor, History, Configuration,} impl ActiveView { pub fn thread(thread: Entity<Thread>, window: &mut Window, cx: &mut App) -> Self { let summary = thread.read(cx).summary_or_default(); let editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_text(summary, window, cx); editor }); let subscriptions = vec![ window.subscribe(&editor, cx, { { let thread = thread.clone(); move |editor, event, window, cx| match event { EditorEvent::BufferEdited => { let new_summary = editor.read(cx).text(cx); thread.update(cx, |thread, cx| { thread.set_summary(new_summary, cx); }) } EditorEvent::Blurred => { if editor.read(cx).text(cx).is_empty() { let summary = thread.read(cx).summary_or_default(); editor.update(cx, |editor, cx| { editor.set_text(summary, window, cx); }); } } _ => {} } } }), window.subscribe(&thread, cx, { let editor = editor.clone(); move |thread, event, window, cx| match event { ThreadEvent::SummaryGenerated => { let summary = thread.read(cx).summary_or_default(); editor.update(cx, |editor, cx| { editor.set_text(summary, window, cx); }) } _ => {} } }), ]; Self::Thread { change_title_editor: editor, _subscriptions: subscriptions, } }} pub struct AssistantPanel { workspace: WeakEntity<Workspace>, project: Entity<Project>, fs: Arc<dyn Fs>, language_registry: Arc<LanguageRegistry>, thread_store: Entity<ThreadStore>, thread: Entity<ActiveThread>, _thread_subscription: Subscription, message_editor: Entity<MessageEditor>, context_store: Entity<assistant_context_editor::ContextStore>, context_editor: Option<Entity<ContextEditor>>, configuration: Option<Entity<AssistantConfiguration>>, configuration_subscription: Option<Subscription>, local_timezone: UtcOffset, active_view: ActiveView, previous_view: Option<ActiveView>, history_store: Entity<HistoryStore>, history: Entity<ThreadHistory>, assistant_dropdown_menu_handle: PopoverMenuHandle<ContextMenu>, width: Option<Pixels>, height: Option<Pixels>,} impl AssistantPanel { pub fn load( workspace: WeakEntity<Workspace>, prompt_builder: Arc<PromptBuilder>, cx: AsyncWindowContext, ) -> Task<Result<Entity<Self>>> { cx.spawn(async move |cx| { let tools = cx.new(|_| ToolWorkingSet::default())?; let thread_store = workspace .update(cx, |workspace, cx| { let project = workspace.project().clone(); ThreadStore::load(project, tools.clone(), prompt_builder.clone(), cx) })? .await; let slash_commands = Arc::new(SlashCommandWorkingSet::default()); let context_store = workspace .update(cx, |workspace, cx| { let project = workspace.project().clone(); assistant_context_editor::ContextStore::new( project, prompt_builder.clone(), slash_commands, cx, ) })? .await?; workspace.update_in(cx, |workspace, window, cx| { cx.new(|cx| Self::new(workspace, thread_store, context_store, window, cx)) }) }) } fn new( workspace: &Workspace, thread_store: Entity<ThreadStore>, context_store: Entity<assistant_context_editor::ContextStore>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let thread = thread_store.update(cx, |this, cx| this.create_thread(cx)); let fs = workspace.app_state().fs.clone(); let project = workspace.project(); let language_registry = project.read(cx).languages().clone(); let workspace = workspace.weak_handle(); let weak_self = cx.entity().downgrade(); let message_editor_context_store = cx.new(|_cx| { crate::context_store::ContextStore::new( project.downgrade(), Some(thread_store.downgrade()), ) }); let message_editor = cx.new(|cx| { MessageEditor::new( fs.clone(), workspace.clone(), message_editor_context_store.clone(), thread_store.downgrade(), thread.clone(), window, cx, ) }); let history_store = cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx)); cx.observe(&history_store, |_, _, cx| cx.notify()).detach(); let active_view = ActiveView::thread(thread.clone(), window, cx); let thread_subscription = cx.subscribe(&thread, |_, _, event, cx| { if let ThreadEvent::MessageAdded(_) = &event { // needed to leave empty state cx.notify(); } }); let thread = cx.new(|cx| { ActiveThread::new( thread.clone(), thread_store.clone(), language_registry.clone(), message_editor_context_store.clone(), workspace.clone(), window, cx, ) }); Self { active_view, workspace, project: project.clone(), fs: fs.clone(), language_registry, thread_store: thread_store.clone(), thread, _thread_subscription: thread_subscription, message_editor, context_store, context_editor: None, configuration: None, configuration_subscription: None, local_timezone: UtcOffset::from_whole_seconds( chrono::Local::now().offset().local_minus_utc(), ) .unwrap(), previous_view: None, history_store: history_store.clone(), history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, window, cx)), assistant_dropdown_menu_handle: PopoverMenuHandle::default(), width: None, height: None, } } pub fn toggle_focus( workspace: &mut Workspace, _: &ToggleFocus, window: &mut Window, cx: &mut Context<Workspace>, ) { if workspace .panel::<Self>(cx) .is_some_and(|panel| panel.read(cx).enabled(cx)) { workspace.toggle_panel_focus::<Self>(window, cx); } } pub(crate) fn local_timezone(&self) -> UtcOffset { self.local_timezone } pub(crate) fn thread_store(&self) -> &Entity<ThreadStore> { &self.thread_store } fn cancel( &mut self, _: &editor::actions::Cancel, _window: &mut Window, cx: &mut Context<Self>, ) { self.thread .update(cx, |thread, cx| thread.cancel_last_completion(cx)); } fn new_thread(&mut self, action: &NewThread, window: &mut Window, cx: &mut Context<Self>) { let thread = self .thread_store .update(cx, |this, cx| this.create_thread(cx)); let thread_view = ActiveView::thread(thread.clone(), window, cx); self.set_active_view(thread_view, window, cx); let message_editor_context_store = cx.new(|_cx| { crate::context_store::ContextStore::new( self.project.downgrade(), Some(self.thread_store.downgrade()), ) }); if let Some(other_thread_id) = action.from_thread_id.clone() { let other_thread_task = self .thread_store .update(cx, |this, cx| this.open_thread(&other_thread_id, cx)); cx.spawn({ let context_store = message_editor_context_store.clone(); async move |_panel, cx| { let other_thread = other_thread_task.await?; context_store.update(cx, |this, cx| { this.add_thread(other_thread, false, cx); })?; anyhow::Ok(()) } }) .detach_and_log_err(cx); } self.thread = cx.new(|cx| { ActiveThread::new( thread.clone(), self.thread_store.clone(), self.language_registry.clone(), message_editor_context_store.clone(), self.workspace.clone(), window, cx, ) }); self._thread_subscription = cx.subscribe(&thread, |_, _, event, cx| { if let ThreadEvent::MessageAdded(_) = &event { // needed to leave empty state cx.notify(); } }); self.message_editor = cx.new(|cx| { MessageEditor::new( self.fs.clone(), self.workspace.clone(), message_editor_context_store, self.thread_store.downgrade(), thread, window, cx, ) }); self.message_editor.focus_handle(cx).focus(window); } fn new_prompt_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.set_active_view(ActiveView::PromptEditor, window, cx); let context = self .context_store .update(cx, |context_store, cx| context_store.create(cx)); let lsp_adapter_delegate = make_lsp_adapter_delegate(&self.project, cx) .log_err() .flatten(); self.context_editor = Some(cx.new(|cx| { let mut editor = ContextEditor::for_context( context, self.fs.clone(), self.workspace.clone(), self.project.clone(), lsp_adapter_delegate, window, cx, ); editor.insert_default_prompt(window, cx); editor })); if let Some(context_editor) = self.context_editor.as_ref() { context_editor.focus_handle(cx).focus(window); } } fn deploy_prompt_library( &mut self, _: &OpenPromptLibrary, _window: &mut Window, cx: &mut Context<Self>, ) { open_prompt_library( self.language_registry.clone(), Box::new(PromptLibraryInlineAssist::new(self.workspace.clone())), Arc::new(|| { Box::new(SlashCommandCompletionProvider::new( Arc::new(SlashCommandWorkingSet::default()), None, None, )) }), cx, ) .detach_and_log_err(cx); } fn open_history(&mut self, window: &mut Window, cx: &mut Context<Self>) { if matches!(self.active_view, ActiveView::History) { if let Some(previous_view) = self.previous_view.take() { self.set_active_view(previous_view, window, cx); } } else { self.thread_store .update(cx, |thread_store, cx| thread_store.reload(cx)) .detach_and_log_err(cx); self.set_active_view(ActiveView::History, window, cx); } cx.notify(); } pub(crate) fn open_saved_prompt_editor( &mut self, path: PathBuf, window: &mut Window, cx: &mut Context<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_in(window, async move |this, cx| { let context = context.await?; this.update_in(cx, |this, window, cx| { let editor = cx.new(|cx| { ContextEditor::for_context(Lines 1–500 of 1732. Load more
use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; use anyhow::{Result, anyhow}; use assistant_context_editor::{ AssistantPanelDelegate, ConfigurationError, ContextEditor, SlashCommandCompletionProvider, make_lsp_adapter_delegate, render_remaining_tokens, }; use assistant_settings::{AssistantDockPosition, AssistantSettings}; use assistant_slash_command::SlashCommandWorkingSet; use assistant_tool::ToolWorkingSet; use client::zed_urls; use editor::{Editor, EditorEvent, MultiBuffer}; use fs::Fs; use gpui::{ Action, Animation, AnimationExt as _, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, KeyContext, Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between, }; use language::LanguageRegistry; use language_model::{LanguageModelProviderTosView, LanguageModelRegistry}; use language_model_selector::ToggleModelSelector; use project::Project; use prompt_library::{PromptLibrary, open_prompt_library}; use prompt_store::PromptBuilder; use settings::{Settings, update_settings_file}; use time::UtcOffset; use ui::{ Banner, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip, prelude::*, }; use util::ResultExt as _; use workspace::Workspace; use workspace::dock::{DockPosition, Panel, PanelEvent}; use zed_actions::agent::OpenConfiguration; use zed_actions::assistant::{OpenPromptLibrary, ToggleFocus}; use crate::active_thread::ActiveThread; use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent}; use crate::history_store::{HistoryEntry, HistoryStore}; use crate::message_editor::MessageEditor; use crate::thread::{Thread, ThreadError, ThreadId, TokenUsageRatio}; use crate::thread_history::{PastContext, PastThread, ThreadHistory}; use crate::thread_store::ThreadStore; use crate::{ AgentDiff, ExpandMessageEditor, InlineAssistant, NewTextThread, NewThread, OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ThreadEvent, ToggleContextPicker, }; pub fn init(cx: &mut App) { cx.observe_new( |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| { workspace .register_action(|workspace, action: &NewThread, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { panel.update(cx, |panel, cx| panel.new_thread(action, window, cx)); workspace.focus_panel::<AssistantPanel>(window, cx); } }) .register_action(|workspace, _: &OpenHistory, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| panel.open_history(window, cx)); } }) .register_action(|workspace, _: &OpenConfiguration, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| panel.open_configuration(window, cx)); } }) .register_action(|workspace, _: &NewTextThread, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| panel.new_prompt_editor(window, cx)); } }) .register_action(|workspace, _: &OpenPromptLibrary, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| { panel.deploy_prompt_library(&OpenPromptLibrary, window, cx) }); } }) .register_action(|workspace, _: &OpenAgentDiff, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); let thread = panel.read(cx).thread.read(cx).thread().clone(); AgentDiff::deploy_in_workspace(thread, workspace, window, cx); } }) .register_action(|workspace, _: &ExpandMessageEditor, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { workspace.focus_panel::<AssistantPanel>(window, cx); panel.update(cx, |panel, cx| { panel.message_editor.update(cx, |editor, cx| { editor.expand_message_editor(&ExpandMessageEditor, window, cx); }); }); } }); }, ) .detach(); } enum ActiveView { Thread { change_title_editor: Entity<Editor>, _subscriptions: Vec<gpui::Subscription>, }, PromptEditor, History, Configuration, } impl ActiveView { pub fn thread(thread: Entity<Thread>, window: &mut Window, cx: &mut App) -> Self { let summary = thread.read(cx).summary_or_default(); let editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_text(summary, window, cx); editor }); let subscriptions = vec![ window.subscribe(&editor, cx, { { let thread = thread.clone(); move |editor, event, window, cx| match event { EditorEvent::BufferEdited => { let new_summary = editor.read(cx).text(cx); thread.update(cx, |thread, cx| { thread.set_summary(new_summary, cx); }) } EditorEvent::Blurred => { if editor.read(cx).text(cx).is_empty() { let summary = thread.read(cx).summary_or_default(); editor.update(cx, |editor, cx| { editor.set_text(summary, window, cx); }); } } _ => {} } } }), window.subscribe(&thread, cx, { let editor = editor.clone(); move |thread, event, window, cx| match event { ThreadEvent::SummaryGenerated => { let summary = thread.read(cx).summary_or_default(); editor.update(cx, |editor, cx| { editor.set_text(summary, window, cx); }) } _ => {} } }), ]; Self::Thread { change_title_editor: editor, _subscriptions: subscriptions, } } } pub struct AssistantPanel { workspace: WeakEntity<Workspace>, project: Entity<Project>, fs: Arc<dyn Fs>, language_registry: Arc<LanguageRegistry>, thread_store: Entity<ThreadStore>, thread: Entity<ActiveThread>, _thread_subscription: Subscription, message_editor: Entity<MessageEditor>, context_store: Entity<assistant_context_editor::ContextStore>, context_editor: Option<Entity<ContextEditor>>, configuration: Option<Entity<AssistantConfiguration>>, configuration_subscription: Option<Subscription>, local_timezone: UtcOffset, active_view: ActiveView, previous_view: Option<ActiveView>, history_store: Entity<HistoryStore>, history: Entity<ThreadHistory>, assistant_dropdown_menu_handle: PopoverMenuHandle<ContextMenu>, width: Option<Pixels>, height: Option<Pixels>, } impl AssistantPanel { pub fn load( workspace: WeakEntity<Workspace>, prompt_builder: Arc<PromptBuilder>, cx: AsyncWindowContext, ) -> Task<Result<Entity<Self>>> { cx.spawn(async move |cx| { let tools = cx.new(|_| ToolWorkingSet::default())?; let thread_store = workspace .update(cx, |workspace, cx| { let project = workspace.project().clone(); ThreadStore::load(project, tools.clone(), prompt_builder.clone(), cx) })? .await; let slash_commands = Arc::new(SlashCommandWorkingSet::default()); let context_store = workspace .update(cx, |workspace, cx| { let project = workspace.project().clone(); assistant_context_editor::ContextStore::new( project, prompt_builder.clone(), slash_commands, cx, ) })? .await?; workspace.update_in(cx, |workspace, window, cx| { cx.new(|cx| Self::new(workspace, thread_store, context_store, window, cx)) }) }) } fn new( workspace: &Workspace, thread_store: Entity<ThreadStore>, context_store: Entity<assistant_context_editor::ContextStore>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let thread = thread_store.update(cx, |this, cx| this.create_thread(cx)); let fs = workspace.app_state().fs.clone(); let project = workspace.project(); let language_registry = project.read(cx).languages().clone(); let workspace = workspace.weak_handle(); let weak_self = cx.entity().downgrade(); let message_editor_context_store = cx.new(|_cx| { crate::context_store::ContextStore::new( project.downgrade(), Some(thread_store.downgrade()), ) }); let message_editor = cx.new(|cx| { MessageEditor::new( fs.clone(), workspace.clone(), message_editor_context_store.clone(), thread_store.downgrade(), thread.clone(), window, cx, ) }); let history_store = cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx)); cx.observe(&history_store, |_, _, cx| cx.notify()).detach(); let active_view = ActiveView::thread(thread.clone(), window, cx); let thread_subscription = cx.subscribe(&thread, |_, _, event, cx| { if let ThreadEvent::MessageAdded(_) = &event { // needed to leave empty state cx.notify(); } }); let thread = cx.new(|cx| { ActiveThread::new( thread.clone(), thread_store.clone(), language_registry.clone(), message_editor_context_store.clone(), workspace.clone(), window, cx, ) }); Self { active_view, workspace, project: project.clone(), fs: fs.clone(), language_registry, thread_store: thread_store.clone(), thread, _thread_subscription: thread_subscription, message_editor, context_store, context_editor: None, configuration: None, configuration_subscription: None, local_timezone: UtcOffset::from_whole_seconds( chrono::Local::now().offset().local_minus_utc(), ) .unwrap(), previous_view: None, history_store: history_store.clone(), history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, window, cx)), assistant_dropdown_menu_handle: PopoverMenuHandle::default(), width: None, height: None, } } pub fn toggle_focus( workspace: &mut Workspace, _: &ToggleFocus, window: &mut Window, cx: &mut Context<Workspace>, ) { if workspace .panel::<Self>(cx) .is_some_and(|panel| panel.read(cx).enabled(cx)) { workspace.toggle_panel_focus::<Self>(window, cx); } } pub(crate) fn local_timezone(&self) -> UtcOffset { self.local_timezone } pub(crate) fn thread_store(&self) -> &Entity<ThreadStore> { &self.thread_store } fn cancel( &mut self, _: &editor::actions::Cancel, _window: &mut Window, cx: &mut Context<Self>, ) { self.thread .update(cx, |thread, cx| thread.cancel_last_completion(cx)); } fn new_thread(&mut self, action: &NewThread, window: &mut Window, cx: &mut Context<Self>) { let thread = self .thread_store .update(cx, |this, cx| this.create_thread(cx)); let thread_view = ActiveView::thread(thread.clone(), window, cx); self.set_active_view(thread_view, window, cx); let message_editor_context_store = cx.new(|_cx| { crate::context_store::ContextStore::new( self.project.downgrade(), Some(self.thread_store.downgrade()), ) }); if let Some(other_thread_id) = action.from_thread_id.clone() { let other_thread_task = self .thread_store .update(cx, |this, cx| this.open_thread(&other_thread_id, cx)); cx.spawn({ let context_store = message_editor_context_store.clone(); async move |_panel, cx| { let other_thread = other_thread_task.await?; context_store.update(cx, |this, cx| { this.add_thread(other_thread, false, cx); })?; anyhow::Ok(()) } }) .detach_and_log_err(cx); } self.thread = cx.new(|cx| { ActiveThread::new( thread.clone(), self.thread_store.clone(), self.language_registry.clone(), message_editor_context_store.clone(), self.workspace.clone(), window, cx, ) }); self._thread_subscription = cx.subscribe(&thread, |_, _, event, cx| { if let ThreadEvent::MessageAdded(_) = &event { // needed to leave empty state cx.notify(); } }); self.message_editor = cx.new(|cx| { MessageEditor::new( self.fs.clone(), self.workspace.clone(), message_editor_context_store, self.thread_store.downgrade(), thread, window, cx, ) }); self.message_editor.focus_handle(cx).focus(window); } fn new_prompt_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.set_active_view(ActiveView::PromptEditor, window, cx); let context = self .context_store .update(cx, |context_store, cx| context_store.create(cx)); let lsp_adapter_delegate = make_lsp_adapter_delegate(&self.project, cx) .log_err() .flatten(); self.context_editor = Some(cx.new(|cx| { let mut editor = ContextEditor::for_context( context, self.fs.clone(), self.workspace.clone(), self.project.clone(), lsp_adapter_delegate, window, cx, ); editor.insert_default_prompt(window, cx); editor })); if let Some(context_editor) = self.context_editor.as_ref() { context_editor.focus_handle(cx).focus(window); } } fn deploy_prompt_library( &mut self, _: &OpenPromptLibrary, _window: &mut Window, cx: &mut Context<Self>, ) { open_prompt_library( self.language_registry.clone(), Box::new(PromptLibraryInlineAssist::new(self.workspace.clone())), Arc::new(|| { Box::new(SlashCommandCompletionProvider::new( Arc::new(SlashCommandWorkingSet::default()), None, None, )) }), cx, ) .detach_and_log_err(cx); } fn open_history(&mut self, window: &mut Window, cx: &mut Context<Self>) { if matches!(self.active_view, ActiveView::History) { if let Some(previous_view) = self.previous_view.take() { self.set_active_view(previous_view, window, cx); } } else { self.thread_store .update(cx, |thread_store, cx| thread_store.reload(cx)) .detach_and_log_err(cx); self.set_active_view(ActiveView::History, window, cx); } cx.notify(); } pub(crate) fn open_saved_prompt_editor( &mut self, path: PathBuf, window: &mut Window, cx: &mut Context<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_in(window, async move |this, cx| { let context = context.await?; this.update_in(cx, |this, window, cx| { let editor = cx.new(|cx| { ContextEditor::for_context(