use std::path::PathBuf;use std::sync::Arc; use anyhow::{anyhow, Result};use assistant_context_editor::{ make_lsp_adapter_delegate, render_remaining_tokens, AssistantPanelDelegate, ConfigurationError, ContextEditor, SlashCommandCompletionProvider,};use assistant_settings::{AssistantDockPosition, AssistantSettings};use assistant_slash_command::SlashCommandWorkingSet;use assistant_tool::ToolWorkingSet; use client::zed_urls;use editor::Editor;use fs::Fs;use gpui::{ prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity,};use language::LanguageRegistry;use language_model::{LanguageModelProviderTosView, LanguageModelRegistry};use project::Project;use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary};use settings::{update_settings_file, Settings};use time::UtcOffset;use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip};use util::ResultExt as _;use workspace::dock::{DockPosition, Panel, PanelEvent};use workspace::Workspace;use zed_actions::assistant::{DeployPromptLibrary, 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};use crate::thread_history::{PastContext, PastThread, ThreadHistory};use crate::thread_store::ThreadStore;use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory}; pub fn init(cx: &mut App) { cx.observe_new( |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| { workspace .register_action(|workspace, _: &NewThread, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { panel.update(cx, |panel, cx| panel.new_thread(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, _: &NewPromptEditor, 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, _: &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)); } }); }, ) .detach();} enum ActiveView { Thread, PromptEditor, History, Configuration,} pub struct AssistantPanel { workspace: WeakEntity<Workspace>, project: Entity<Project>, fs: Arc<dyn Fs>, language_registry: Arc<LanguageRegistry>, thread_store: Entity<ThreadStore>, thread: Entity<ActiveThread>, message_editor: Entity<MessageEditor>, context_store: Entity<assistant_context_editor::ContextStore>, context_editor: Option<Entity<ContextEditor>>, configuration: Option<Entity<AssistantConfiguration>>, configuration_subscription: Option<Subscription>, tools: Arc<ToolWorkingSet>, local_timezone: UtcOffset, active_view: ActiveView, history_store: Entity<HistoryStore>, history: Entity<ThreadHistory>, new_item_context_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(|mut cx| async move { let tools = Arc::new(ToolWorkingSet::default()); log::info!("[assistant2-debug] initializing ThreadStore"); let thread_store = workspace.update(&mut cx, |workspace, cx| { let project = workspace.project().clone(); ThreadStore::new(project, tools.clone(), cx) })??; log::info!("[assistant2-debug] finished initializing ThreadStore"); let slash_commands = Arc::new(SlashCommandWorkingSet::default()); log::info!("[assistant2-debug] initializing ContextStore"); let context_store = workspace .update(&mut cx, |workspace, cx| { let project = workspace.project().clone(); assistant_context_editor::ContextStore::new( project, prompt_builder.clone(), slash_commands, cx, ) })? .await?; log::info!("[assistant2-debug] finished initializing ContextStore"); workspace.update_in(&mut cx, |workspace, window, cx| { cx.new(|cx| Self::new(workspace, thread_store, context_store, tools, window, cx)) }) }) } fn new( workspace: &Workspace, thread_store: Entity<ThreadStore>, context_store: Entity<assistant_context_editor::ContextStore>, tools: Arc<ToolWorkingSet>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { log::info!("[assistant2-debug] AssistantPanel::new"); let thread = thread_store.update(cx, |this, cx| this.create_thread(cx)); let fs = workspace.app_state().fs.clone(); let project = workspace.project().clone(); let language_registry = project.read(cx).languages().clone(); let workspace = workspace.weak_handle(); let weak_self = cx.entity().downgrade(); let message_editor = cx.new(|cx| { MessageEditor::new( fs.clone(), workspace.clone(), thread_store.downgrade(), thread.clone(), window, cx, ) }); let history_store = cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx)); Self { active_view: ActiveView::Thread, workspace: workspace.clone(), project, fs: fs.clone(), language_registry: language_registry.clone(), thread_store: thread_store.clone(), thread: cx.new(|cx| { ActiveThread::new( thread.clone(), thread_store.clone(), workspace, language_registry, tools.clone(), window, cx, ) }), message_editor, context_store, context_editor: None, configuration: None, configuration_subscription: None, tools, local_timezone: UtcOffset::from_whole_seconds( chrono::Local::now().offset().local_minus_utc(), ) .unwrap(), history_store: history_store.clone(), history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, cx)), new_item_context_menu_handle: PopoverMenuHandle::default(), width: None, height: None, } } pub fn toggle_focus( workspace: &mut Workspace, _: &ToggleFocus, window: &mut Window, cx: &mut Context<Workspace>, ) { let settings = AssistantSettings::get_global(cx); if !settings.enabled { return; } 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, window: &mut Window, cx: &mut Context<Self>) { let thread = self .thread_store .update(cx, |this, cx| this.create_thread(cx)); self.active_view = ActiveView::Thread; self.thread = cx.new(|cx| { ActiveThread::new( thread.clone(), self.thread_store.clone(), self.workspace.clone(), self.language_registry.clone(), self.tools.clone(), window, cx, ) }); self.message_editor = cx.new(|cx| { MessageEditor::new( self.fs.clone(), self.workspace.clone(), 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.active_view = ActiveView::PromptEditor; 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, _: &DeployPromptLibrary, _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>) { self.thread_store .update(cx, |thread_store, cx| thread_store.reload(cx)) .detach_and_log_err(cx); self.active_view = ActiveView::History; self.history.focus_handle(cx).focus(window); 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, |this, mut cx| async move { let context = context.await?; this.update_in(&mut cx, |this, window, cx| { let editor = cx.new(|cx| { ContextEditor::for_context( context, fs, workspace, project, lsp_adapter_delegate, window, cx, ) }); this.active_view = ActiveView::PromptEditor; this.context_editor = Some(editor); anyhow::Ok(()) })??; Ok(()) }) } pub(crate) fn open_thread( &mut self, thread_id: &ThreadId, window: &mut Window, cx: &mut Context<Self>, ) -> Task<Result<()>> { let open_thread_task = self .thread_store .update(cx, |this, cx| this.open_thread(thread_id, cx)); cx.spawn_in(window, |this, mut cx| async move { let thread = open_thread_task.await?; this.update_in(&mut cx, |this, window, cx| { this.active_view = ActiveView::Thread; this.thread = cx.new(|cx| { ActiveThread::new( thread.clone(), this.thread_store.clone(), this.workspace.clone(), this.language_registry.clone(), this.tools.clone(), window, cx, ) }); this.message_editor = cx.new(|cx| { MessageEditor::new( this.fs.clone(), this.workspace.clone(), this.thread_store.downgrade(), thread, window, cx, ) }); this.message_editor.focus_handle(cx).focus(window); }) }) } pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.active_view = ActiveView::Configuration; self.configuration = Some(cx.new(|cx| AssistantConfiguration::new(window, cx))); if let Some(configuration) = self.configuration.as_ref() { self.configuration_subscription = Some(cx.subscribe_in( configuration, window, Self::handle_assistant_configuration_event, )); configuration.focus_handle(cx).focus(window); } } fn handle_assistant_configuration_event( &mut self, _entity: &Entity<AssistantConfiguration>, event: &AssistantConfigurationEvent, window: &mut Window, cx: &mut Context<Self>, ) { match event { AssistantConfigurationEvent::NewThread(provider) => { if LanguageModelRegistry::read_global(cx) .active_provider() .map_or(true, |active_provider| { active_provider.id() != provider.id() }) { if let Some(model) = provider.default_model(cx) { update_settings_file::<AssistantSettings>( self.fs.clone(), cx, move |settings, _| settings.set_model(model), ); } } self.new_thread(window, cx); } } } pub(crate) fn active_thread(&self, cx: &App) -> Entity<Thread> { self.thread.read(cx).thread().clone() } pub(crate) fn delete_thread(&mut self, thread_id: &ThreadId, cx: &mut Context<Self>) { self.thread_store .update(cx, |this, cx| this.delete_thread(thread_id, cx)) .detach_and_log_err(cx); } pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> { self.context_editor.clone() } pub(crate) fn delete_context(&mut self, path: PathBuf, cx: &mut Context<Self>) { self.context_store .update(cx, |this, cx| this.delete_local_context(path, cx)) .detach_and_log_err(cx); }} impl Focusable for AssistantPanel { fn focus_handle(&self, cx: &App) -> FocusHandle { match self.active_view { ActiveView::Thread => self.message_editor.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::Configuration => { if let Some(configuration) = self.configuration.as_ref() { configuration.focus_handle(cx) } else { cx.focus_handle() } } } }} impl EventEmitter<PanelEvent> for AssistantPanel {} impl Panel for AssistantPanel { fn persistent_name() -> &'static str { "AssistantPanel2" } fn position(&self, _window: &Window, cx: &App) -> DockPosition { match AssistantSettings::get_global(cx).dock {Lines 1–500 of 1111. Load more
use std::path::PathBuf; use std::sync::Arc; use anyhow::{anyhow, Result}; use assistant_context_editor::{ make_lsp_adapter_delegate, render_remaining_tokens, AssistantPanelDelegate, ConfigurationError, ContextEditor, SlashCommandCompletionProvider, }; use assistant_settings::{AssistantDockPosition, AssistantSettings}; use assistant_slash_command::SlashCommandWorkingSet; use assistant_tool::ToolWorkingSet; use client::zed_urls; use editor::Editor; use fs::Fs; use gpui::{ prelude::*, Action, AnyElement, App, AsyncWindowContext, Corner, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, Pixels, Subscription, Task, UpdateGlobal, WeakEntity, }; use language::LanguageRegistry; use language_model::{LanguageModelProviderTosView, LanguageModelRegistry}; use project::Project; use prompt_library::{open_prompt_library, PromptBuilder, PromptLibrary}; use settings::{update_settings_file, Settings}; use time::UtcOffset; use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip}; use util::ResultExt as _; use workspace::dock::{DockPosition, Panel, PanelEvent}; use workspace::Workspace; use zed_actions::assistant::{DeployPromptLibrary, 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}; use crate::thread_history::{PastContext, PastThread, ThreadHistory}; use crate::thread_store::ThreadStore; use crate::{InlineAssistant, NewPromptEditor, NewThread, OpenConfiguration, OpenHistory}; pub fn init(cx: &mut App) { cx.observe_new( |workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| { workspace .register_action(|workspace, _: &NewThread, window, cx| { if let Some(panel) = workspace.panel::<AssistantPanel>(cx) { panel.update(cx, |panel, cx| panel.new_thread(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, _: &NewPromptEditor, 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, _: &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)); } }); }, ) .detach(); } enum ActiveView { Thread, PromptEditor, History, Configuration, } pub struct AssistantPanel { workspace: WeakEntity<Workspace>, project: Entity<Project>, fs: Arc<dyn Fs>, language_registry: Arc<LanguageRegistry>, thread_store: Entity<ThreadStore>, thread: Entity<ActiveThread>, message_editor: Entity<MessageEditor>, context_store: Entity<assistant_context_editor::ContextStore>, context_editor: Option<Entity<ContextEditor>>, configuration: Option<Entity<AssistantConfiguration>>, configuration_subscription: Option<Subscription>, tools: Arc<ToolWorkingSet>, local_timezone: UtcOffset, active_view: ActiveView, history_store: Entity<HistoryStore>, history: Entity<ThreadHistory>, new_item_context_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(|mut cx| async move { let tools = Arc::new(ToolWorkingSet::default()); log::info!("[assistant2-debug] initializing ThreadStore"); let thread_store = workspace.update(&mut cx, |workspace, cx| { let project = workspace.project().clone(); ThreadStore::new(project, tools.clone(), cx) })??; log::info!("[assistant2-debug] finished initializing ThreadStore"); let slash_commands = Arc::new(SlashCommandWorkingSet::default()); log::info!("[assistant2-debug] initializing ContextStore"); let context_store = workspace .update(&mut cx, |workspace, cx| { let project = workspace.project().clone(); assistant_context_editor::ContextStore::new( project, prompt_builder.clone(), slash_commands, cx, ) })? .await?; log::info!("[assistant2-debug] finished initializing ContextStore"); workspace.update_in(&mut cx, |workspace, window, cx| { cx.new(|cx| Self::new(workspace, thread_store, context_store, tools, window, cx)) }) }) } fn new( workspace: &Workspace, thread_store: Entity<ThreadStore>, context_store: Entity<assistant_context_editor::ContextStore>, tools: Arc<ToolWorkingSet>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { log::info!("[assistant2-debug] AssistantPanel::new"); let thread = thread_store.update(cx, |this, cx| this.create_thread(cx)); let fs = workspace.app_state().fs.clone(); let project = workspace.project().clone(); let language_registry = project.read(cx).languages().clone(); let workspace = workspace.weak_handle(); let weak_self = cx.entity().downgrade(); let message_editor = cx.new(|cx| { MessageEditor::new( fs.clone(), workspace.clone(), thread_store.downgrade(), thread.clone(), window, cx, ) }); let history_store = cx.new(|cx| HistoryStore::new(thread_store.clone(), context_store.clone(), cx)); Self { active_view: ActiveView::Thread, workspace: workspace.clone(), project, fs: fs.clone(), language_registry: language_registry.clone(), thread_store: thread_store.clone(), thread: cx.new(|cx| { ActiveThread::new( thread.clone(), thread_store.clone(), workspace, language_registry, tools.clone(), window, cx, ) }), message_editor, context_store, context_editor: None, configuration: None, configuration_subscription: None, tools, local_timezone: UtcOffset::from_whole_seconds( chrono::Local::now().offset().local_minus_utc(), ) .unwrap(), history_store: history_store.clone(), history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, cx)), new_item_context_menu_handle: PopoverMenuHandle::default(), width: None, height: None, } } pub fn toggle_focus( workspace: &mut Workspace, _: &ToggleFocus, window: &mut Window, cx: &mut Context<Workspace>, ) { let settings = AssistantSettings::get_global(cx); if !settings.enabled { return; } 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, window: &mut Window, cx: &mut Context<Self>) { let thread = self .thread_store .update(cx, |this, cx| this.create_thread(cx)); self.active_view = ActiveView::Thread; self.thread = cx.new(|cx| { ActiveThread::new( thread.clone(), self.thread_store.clone(), self.workspace.clone(), self.language_registry.clone(), self.tools.clone(), window, cx, ) }); self.message_editor = cx.new(|cx| { MessageEditor::new( self.fs.clone(), self.workspace.clone(), 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.active_view = ActiveView::PromptEditor; 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, _: &DeployPromptLibrary, _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>) { self.thread_store .update(cx, |thread_store, cx| thread_store.reload(cx)) .detach_and_log_err(cx); self.active_view = ActiveView::History; self.history.focus_handle(cx).focus(window); 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, |this, mut cx| async move { let context = context.await?; this.update_in(&mut cx, |this, window, cx| { let editor = cx.new(|cx| { ContextEditor::for_context( context, fs, workspace, project, lsp_adapter_delegate, window, cx, ) }); this.active_view = ActiveView::PromptEditor; this.context_editor = Some(editor); anyhow::Ok(()) })??; Ok(()) }) } pub(crate) fn open_thread( &mut self, thread_id: &ThreadId, window: &mut Window, cx: &mut Context<Self>, ) -> Task<Result<()>> { let open_thread_task = self .thread_store .update(cx, |this, cx| this.open_thread(thread_id, cx)); cx.spawn_in(window, |this, mut cx| async move { let thread = open_thread_task.await?; this.update_in(&mut cx, |this, window, cx| { this.active_view = ActiveView::Thread; this.thread = cx.new(|cx| { ActiveThread::new( thread.clone(), this.thread_store.clone(), this.workspace.clone(), this.language_registry.clone(), this.tools.clone(), window, cx, ) }); this.message_editor = cx.new(|cx| { MessageEditor::new( this.fs.clone(), this.workspace.clone(), this.thread_store.downgrade(), thread, window, cx, ) }); this.message_editor.focus_handle(cx).focus(window); }) }) } pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.active_view = ActiveView::Configuration; self.configuration = Some(cx.new(|cx| AssistantConfiguration::new(window, cx))); if let Some(configuration) = self.configuration.as_ref() { self.configuration_subscription = Some(cx.subscribe_in( configuration, window, Self::handle_assistant_configuration_event, )); configuration.focus_handle(cx).focus(window); } } fn handle_assistant_configuration_event( &mut self, _entity: &Entity<AssistantConfiguration>, event: &AssistantConfigurationEvent, window: &mut Window, cx: &mut Context<Self>, ) { match event { AssistantConfigurationEvent::NewThread(provider) => { if LanguageModelRegistry::read_global(cx) .active_provider() .map_or(true, |active_provider| { active_provider.id() != provider.id() }) { if let Some(model) = provider.default_model(cx) { update_settings_file::<AssistantSettings>( self.fs.clone(), cx, move |settings, _| settings.set_model(model), ); } } self.new_thread(window, cx); } } } pub(crate) fn active_thread(&self, cx: &App) -> Entity<Thread> { self.thread.read(cx).thread().clone() } pub(crate) fn delete_thread(&mut self, thread_id: &ThreadId, cx: &mut Context<Self>) { self.thread_store .update(cx, |this, cx| this.delete_thread(thread_id, cx)) .detach_and_log_err(cx); } pub(crate) fn active_context_editor(&self) -> Option<Entity<ContextEditor>> { self.context_editor.clone() } pub(crate) fn delete_context(&mut self, path: PathBuf, cx: &mut Context<Self>) { self.context_store .update(cx, |this, cx| this.delete_local_context(path, cx)) .detach_and_log_err(cx); } } impl Focusable for AssistantPanel { fn focus_handle(&self, cx: &App) -> FocusHandle { match self.active_view { ActiveView::Thread => self.message_editor.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::Configuration => { if let Some(configuration) = self.configuration.as_ref() { configuration.focus_handle(cx) } else { cx.focus_handle() } } } } } impl EventEmitter<PanelEvent> for AssistantPanel {} impl Panel for AssistantPanel { fn persistent_name() -> &'static str { "AssistantPanel2" } fn position(&self, _window: &Window, cx: &App) -> DockPosition { match AssistantSettings::get_global(cx).dock {