From 3d23c87ff20fdcb782f39d41bbd9daad8333bbbe Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 13 Mar 2026 13:29:58 -0700 Subject: [PATCH] Remove Panel impl from AgentPanel Co-authored-by: Max Brunsfeld --- .../manage_profiles_modal.rs | 2 +- crates/agent_ui/src/agent_panel.rs | 221 ++++++------------ crates/agent_ui/src/completion_provider.rs | 2 +- crates/agent_ui/src/connection_view.rs | 4 +- .../src/connection_view/thread_view.rs | 6 +- crates/agent_ui/src/inline_assistant.rs | 6 +- crates/agent_ui/src/sidebar.rs | 18 +- crates/agent_ui/src/thread_history_view.rs | 2 +- .../agent_ui/src/ui/acp_onboarding_modal.rs | 4 +- .../src/ui/claude_agent_onboarding_modal.rs | 4 +- crates/agent_ui/src/ui/mention_crease.rs | 2 +- crates/workspace/src/dock.rs | 7 +- crates/workspace/src/workspace.rs | 104 +++++++++ crates/zed/src/main.rs | 6 +- crates/zed/src/zed.rs | 59 +++-- 15 files changed, 232 insertions(+), 215 deletions(-) diff --git a/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs b/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs index 744c92a7f7739c9fda2664de45d536769e802986..946f8eafad6d2a41142d365cbec7f561813b6be3 100644 --- a/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs +++ b/crates/agent_ui/src/agent_configuration/manage_profiles_modal.rs @@ -121,7 +121,7 @@ impl ManageProfilesModal { _cx: &mut Context, ) { workspace.register_action(|workspace, action: &ManageProfiles, window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { let fs = workspace.app_state().fs.clone(); let active_model = panel .read(cx) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 0a855f8610fe4b07367dddf877d2856192f77a51..1c16a79e9286158b039d29c683d2e9767b8a4522 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -86,7 +86,7 @@ use util::{ResultExt as _, debug_panic}; use workspace::{ CollaboratorId, DraggedSelection, DraggedTab, OpenResult, ToggleZoom, ToolbarItemView, Workspace, WorkspaceId, - dock::{DockPosition, Panel, PanelEvent}, + dock::{DockPosition, PanelEvent}, }; use zed_actions::{ DecreaseBufferFontSize, IncreaseBufferFontSize, ResetBufferFontSize, @@ -151,50 +151,50 @@ pub fn init(cx: &mut App) { |workspace: &mut Workspace, _window, _cx: &mut Context| { workspace .register_action(|workspace, action: &NewThread, window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| panel.new_thread(action, window, cx)); - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); } }) .register_action( |workspace, action: &NewNativeAgentThreadFromSummary, window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_native_agent_thread_from_summary(action, window, cx) }); - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); } }, ) .register_action(|workspace, _: &ExpandMessageEditor, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| panel.expand_message_editor(window, cx)); } }) .register_action(|workspace, _: &OpenHistory, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| panel.open_history(window, cx)); } }) .register_action(|workspace, _: &OpenSettings, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| panel.open_configuration(window, cx)); } }) .register_action(|workspace, _: &NewTextThread, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.new_text_thread(window, cx); }); } }) .register_action(|workspace, action: &NewExternalAgentThread, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.external_thread( action.agent.clone(), @@ -210,8 +210,8 @@ pub fn init(cx: &mut App) { } }) .register_action(|workspace, action: &OpenRulesLibrary, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.deploy_rules_library(action, window, cx) }); @@ -222,7 +222,7 @@ pub fn init(cx: &mut App) { }) .register_action(|workspace, _: &OpenAgentDiff, window, cx| { let thread = workspace - .panel::(cx) + .drawer::() .and_then(|panel| panel.read(cx).active_connection_view().cloned()) .and_then(|thread_view| { thread_view @@ -236,24 +236,24 @@ pub fn init(cx: &mut App) { } }) .register_action(|workspace, _: &ToggleNavigationMenu, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.toggle_navigation_menu(&ToggleNavigationMenu, window, cx); }); } }) .register_action(|workspace, _: &ToggleOptionsMenu, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.toggle_options_menu(&ToggleOptionsMenu, window, cx); }); } }) .register_action(|workspace, _: &ToggleNewThreadMenu, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.toggle_new_thread_menu(&ToggleNewThreadMenu, window, cx); }); @@ -272,7 +272,7 @@ pub fn init(cx: &mut App) { window.refresh(); }) .register_action(|workspace, _: &ResetTrialUpsell, _window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, _| { panel .on_boarding_upsell_dismissed @@ -285,29 +285,29 @@ pub fn init(cx: &mut App) { TrialEndUpsell::set_dismissed(false, cx); }) .register_action(|workspace, _: &ResetAgentZoom, window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.reset_agent_zoom(window, cx); }); } }) .register_action(|workspace, _: &CopyThreadToClipboard, window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.copy_thread_to_clipboard(window, cx); }); } }) .register_action(|workspace, _: &LoadThreadFromClipboard, window, cx| { - if let Some(panel) = workspace.panel::(cx) { - workspace.focus_panel::(window, cx); + if let Some(panel) = workspace.drawer::() { + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.load_thread_from_clipboard(window, cx); }); } }) .register_action(|workspace, action: &ReviewBranchDiff, window, cx| { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; @@ -332,7 +332,7 @@ pub fn init(cx: &mut App) { )), ]; - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.external_thread( @@ -352,13 +352,13 @@ pub fn init(cx: &mut App) { }) .register_action( |workspace, action: &ResolveConflictsWithAgent, window, cx| { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; let content_blocks = build_conflict_resolution_prompt(&action.conflicts); - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.external_thread( @@ -379,14 +379,14 @@ pub fn init(cx: &mut App) { ) .register_action( |workspace, action: &ResolveConflictedFilesWithAgent, window, cx| { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; let content_blocks = build_conflicted_files_resolution_prompt(&action.conflicted_file_paths); - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); panel.update(cx, |panel, cx| { panel.external_thread( @@ -406,21 +406,21 @@ pub fn init(cx: &mut App) { }, ) .register_action(|workspace, action: &StartThreadIn, _window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.set_start_thread_in(action, cx); }); } }) .register_action(|workspace, _: &CycleStartThreadIn, _window, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.cycle_start_thread_in(cx); }); } }) .register_action(|workspace, _: &ToggleAgentDrawer, _window, cx| { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; let panel_view: AnyView = panel.into(); @@ -839,6 +839,10 @@ pub struct AgentPanel { } impl AgentPanel { + fn enabled(&self, cx: &App) -> bool { + AgentSettings::get_global(cx).enabled(cx) + } + fn serialize(&mut self, cx: &mut App) { let Some(workspace_id) = self.workspace_id else { return; @@ -1186,10 +1190,10 @@ impl AgentPanel { cx: &mut Context, ) { if workspace - .panel::(cx) + .drawer::() .is_some_and(|panel| panel.read(cx).enabled(cx)) { - workspace.toggle_panel_focus::(window, cx); + workspace.toggle_drawer_focus::(window, cx); } } @@ -1200,11 +1204,11 @@ impl AgentPanel { cx: &mut Context, ) { if workspace - .panel::(cx) + .drawer::() .is_some_and(|panel| panel.read(cx).enabled(cx)) { - if !workspace.toggle_panel_focus::(window, cx) { - workspace.close_panel::(window, cx); + if !workspace.toggle_drawer_focus::(window, cx) { + workspace.close_drawer::(cx); } } } @@ -1249,7 +1253,7 @@ impl AgentPanel { let workspace_read = workspace.read(cx); workspace_read - .panel::(cx) + .drawer::() .map(|panel| { let panel_id = Entity::entity_id(&panel); @@ -3033,8 +3037,8 @@ impl AgentPanel { .detach(); } - workspace.focus_panel::(window, cx); - if let Some(panel) = workspace.panel::(cx) { + workspace.focus_drawer::(window, cx); + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.external_thread( None, @@ -3062,6 +3066,15 @@ impl AgentPanel { anyhow::Ok(()) } + + fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool { + self.zoomed + } + + fn set_zoomed(&mut self, zoomed: bool, _window: &mut Window, cx: &mut Context) { + self.zoomed = zoomed; + cx.notify(); + } } impl Focusable for AgentPanel { @@ -3100,98 +3113,6 @@ pub enum AgentPanelEvent { impl EventEmitter for AgentPanel {} impl EventEmitter for AgentPanel {} -impl Panel for AgentPanel { - fn persistent_name() -> &'static str { - "AgentPanel" - } - - fn panel_key() -> &'static str { - AGENT_PANEL_KEY - } - - fn position(&self, _window: &Window, cx: &App) -> DockPosition { - agent_panel_dock_position(cx) - } - - fn position_is_valid(&self, position: DockPosition) -> bool { - position != DockPosition::Bottom - } - - fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context) { - settings::update_settings_file(self.fs.clone(), cx, move |settings, _| { - settings - .agent - .get_or_insert_default() - .set_dock(position.into()); - }); - } - - fn size(&self, window: &Window, cx: &App) -> Pixels { - let settings = AgentSettings::get_global(cx); - match self.position(window, cx) { - DockPosition::Left | DockPosition::Right => { - self.width.unwrap_or(settings.default_width) - } - DockPosition::Bottom => self.height.unwrap_or(settings.default_height), - } - } - - fn set_size(&mut self, size: Option, window: &mut Window, cx: &mut Context) { - match self.position(window, cx) { - DockPosition::Left | DockPosition::Right => self.width = size, - DockPosition::Bottom => self.height = size, - } - self.serialize(cx); - cx.notify(); - } - - fn set_active(&mut self, active: bool, window: &mut Window, cx: &mut Context) { - if active - && matches!(self.active_view, ActiveView::Uninitialized) - && !matches!( - self.worktree_creation_status, - Some(WorktreeCreationStatus::Creating) - ) - { - let selected_agent_type = self.selected_agent_type.clone(); - self.new_agent_thread_inner(selected_agent_type, false, window, cx); - } - } - - fn remote_id() -> Option { - Some(proto::PanelId::AssistantPanel) - } - - fn icon(&self, _window: &Window, cx: &App) -> Option { - (self.enabled(cx) && AgentSettings::get_global(cx).button).then_some(IconName::ZedAssistant) - } - - fn icon_tooltip(&self, _window: &Window, _cx: &App) -> &'static str { - "Agent Panel" - } - - fn toggle_action(&self) -> Box { - Box::new(ToggleAgentDrawer) - } - - fn activation_priority(&self) -> u32 { - 3 - } - - fn enabled(&self, cx: &App) -> bool { - AgentSettings::get_global(cx).enabled(cx) - } - - fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool { - self.zoomed - } - - fn set_zoomed(&mut self, zoomed: bool, _window: &mut Window, cx: &mut Context) { - self.zoomed = zoomed; - cx.notify(); - } -} - impl AgentPanel { fn render_title_view(&self, _window: &mut Window, cx: &Context) -> AnyElement { const LOADING_SUMMARY_PLACEHOLDER: &str = "Loading Summary…"; @@ -3764,7 +3685,7 @@ impl AgentPanel { if let Some(workspace) = workspace.upgrade() { workspace.update(cx, |workspace, cx| { if let Some(panel) = - workspace.panel::(cx) + workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_agent_thread( @@ -3790,7 +3711,7 @@ impl AgentPanel { if let Some(workspace) = workspace.upgrade() { workspace.update(cx, |workspace, cx| { if let Some(panel) = - workspace.panel::(cx) + workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_agent_thread( @@ -3878,7 +3799,7 @@ impl AgentPanel { if let Some(workspace) = workspace.upgrade() { workspace.update(cx, |workspace, cx| { if let Some(panel) = - workspace.panel::(cx) + workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_agent_thread( @@ -3974,7 +3895,7 @@ impl AgentPanel { if let Some(workspace) = workspace.upgrade() { workspace.update(cx, |workspace, cx| { if let Some(panel) = - workspace.panel::(cx) + workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_agent_thread( @@ -4789,7 +4710,7 @@ impl rules_library::InlineAssistDelegate for PromptLibraryInlineAssist { let Some(workspace) = self.workspace.upgrade() else { return; }; - let Some(panel) = workspace.read(cx).panel::(cx) else { + let Some(panel) = workspace.read(cx).drawer::() else { return; }; let Some(history) = panel @@ -4825,7 +4746,7 @@ impl rules_library::InlineAssistDelegate for PromptLibraryInlineAssist { window: &mut Window, cx: &mut Context, ) -> bool { - workspace.focus_panel::(window, cx).is_some() + workspace.focus_drawer::(window, cx).is_some() } } @@ -4838,7 +4759,7 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate { _window: &mut Window, cx: &mut Context, ) -> Option> { - let panel = workspace.panel::(cx)?; + let panel = workspace.drawer::()?; panel.read(cx).active_text_thread_editor() } @@ -4849,7 +4770,7 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate { window: &mut Window, cx: &mut Context, ) -> Task> { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return Task::ready(Err(anyhow!("Agent panel not found"))); }; @@ -4876,12 +4797,12 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate { window: &mut Window, cx: &mut Context, ) { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; if !panel.focus_handle(cx).contains_focused(window, cx) { - workspace.toggle_panel_focus::(window, cx); + workspace.toggle_drawer_focus::(window, cx); } panel.update(cx, |_, cx| { @@ -4914,12 +4835,12 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate { window: &mut Window, cx: &mut Context, ) { - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; if !panel.focus_handle(cx).contains_focused(window, cx) { - workspace.toggle_panel_focus::(window, cx); + workspace.toggle_drawer_focus::(window, cx); } panel.update(cx, |_, cx| { diff --git a/crates/agent_ui/src/completion_provider.rs b/crates/agent_ui/src/completion_provider.rs index d8c45755413ffb14433e3eeb4309e869de195a75..02a7c89347baeb7642a7c7c69de133a256b5a837 100644 --- a/crates/agent_ui/src/completion_provider.rs +++ b/crates/agent_ui/src/completion_provider.rs @@ -1096,7 +1096,7 @@ impl PromptCompletionProvider { let project = workspace.project().read(cx); let include_root_name = workspace.visible_worktrees(cx).count() > 1; - if let Some(agent_panel) = workspace.panel::(cx) + if let Some(agent_panel) = workspace.drawer::() && let Some(thread) = agent_panel.read(cx).active_agent_thread(cx) { let thread = thread.read(cx); diff --git a/crates/agent_ui/src/connection_view.rs b/crates/agent_ui/src/connection_view.rs index bea9c93ced96e79122b5fdf1afd4bb6e2f52407e..2e03fcd55ecb73ec8fc60a03af2c93b29aa9e7a6 100644 --- a/crates/agent_ui/src/connection_view.rs +++ b/crates/agent_ui/src/connection_view.rs @@ -2465,7 +2465,7 @@ impl ConnectionView { if let Some(workspace) = workspace_handle.upgrade() { multi_workspace.activate(workspace.clone(), cx); workspace.update(cx, |workspace, cx| { - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); }); } }) @@ -3493,7 +3493,7 @@ pub(crate) mod tests { workspace.add_panel(panel, window, cx); // Open the dock and activate the agent panel so it's visible - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); }); cx.run_until_parked(); diff --git a/crates/agent_ui/src/connection_view/thread_view.rs b/crates/agent_ui/src/connection_view/thread_view.rs index 35df60b567de86762a9af330013df0fab35f3f01..999ab5397936b4fcee9c314bfcf2851159d9adec 100644 --- a/crates/agent_ui/src/connection_view/thread_view.rs +++ b/crates/agent_ui/src/connection_view/thread_view.rs @@ -783,7 +783,7 @@ impl ThreadView { && self .workspace .upgrade() - .and_then(|workspace| workspace.read(cx).panel::(cx)) + .and_then(|workspace| workspace.read(cx).drawer::()) .is_some_and(|panel| { panel.read(cx).start_thread_in() == &StartThreadIn::NewWorktree }); @@ -8000,14 +8000,14 @@ pub(crate) fn open_link( } MentionUri::Selection { abs_path: None, .. } => {} MentionUri::Thread { id, name } => { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.open_thread(id, None, Some(name.into()), window, cx) }); } } MentionUri::TextThread { path, .. } => { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel .open_saved_text_thread(path.as_path().into(), window, cx) diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index 1fc66f6079fa146440a1f5a594d9f160e4580ab2..2cd3d0bd1c973a8e0dd96a4e557bed6e5f80b624 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -259,7 +259,7 @@ impl InlineAssistant { let Some(inline_assist_target) = Self::resolve_inline_assist_target( workspace, - workspace.panel::(cx), + workspace.drawer::(), window, cx, ) else { @@ -271,7 +271,7 @@ impl InlineAssistant { model_registry.configuration_error(model_registry.inline_assistant_model(), cx) }; - let Some(agent_panel) = workspace.panel::(cx) else { + let Some(agent_panel) = workspace.drawer::() else { return; }; let agent_panel = agent_panel.read(cx); @@ -1974,7 +1974,7 @@ impl CodeActionProvider for AssistantCodeActionProvider { let (thread_store, history) = cx.update(|_window, cx| { let panel = workspace .read(cx) - .panel::(cx) + .drawer::() .context("missing agent panel")? .read(cx); diff --git a/crates/agent_ui/src/sidebar.rs b/crates/agent_ui/src/sidebar.rs index cb865910f9ebafb051b71030ce26d7a34e98ecd1..3b767f685380807b58a3f4b1f719045595007a6e 100644 --- a/crates/agent_ui/src/sidebar.rs +++ b/crates/agent_ui/src/sidebar.rs @@ -428,7 +428,7 @@ impl Sidebar { ) .detach(); - if let Some(agent_panel) = workspace.read(cx).panel::(cx) { + if let Some(agent_panel) = workspace.read(cx).drawer::() { self.subscribe_to_agent_panel(&agent_panel, window, cx); } } @@ -457,7 +457,7 @@ impl Sidebar { workspace: &Entity, cx: &App, ) -> Vec { - let Some(agent_panel) = workspace.read(cx).panel::(cx) else { + let Some(agent_panel) = workspace.read(cx).drawer::() else { return Vec::new(); }; let agent_panel_ref = agent_panel.read(cx); @@ -511,7 +511,7 @@ impl Sidebar { self.focused_thread = active_workspace .as_ref() - .and_then(|ws| ws.read(cx).panel::(cx)) + .and_then(|ws| ws.read(cx).drawer::()) .and_then(|panel| panel.read(cx).active_connection_view().cloned()) .and_then(|cv| cv.read(cx).parent_id(cx)); @@ -1441,10 +1441,10 @@ impl Sidebar { }); workspace.update(cx, |workspace, cx| { - workspace.open_panel::(window, cx); + workspace.open_drawer::(cx); }); - if let Some(agent_panel) = workspace.read(cx).panel::(cx) { + if let Some(agent_panel) = workspace.read(cx).drawer::() { agent_panel.update(cx, |panel, cx| { panel.load_agent_thread( agent, @@ -1766,12 +1766,12 @@ impl Sidebar { }); workspace.update(cx, |workspace, cx| { - if let Some(agent_panel) = workspace.panel::(cx) { + if let Some(agent_panel) = workspace.drawer::() { agent_panel.update(cx, |panel, cx| { panel.new_thread(&NewThread, window, cx); }); } - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); }); } @@ -1871,7 +1871,7 @@ impl Sidebar { return; }; - let Some(agent_panel) = active_workspace.read(cx).panel::(cx) else { + let Some(agent_panel) = active_workspace.read(cx).drawer::() else { return; }; @@ -4091,7 +4091,7 @@ mod tests { workspace_a.read_with(cx, |workspace, cx| { assert!( - workspace.panel::(cx).is_some(), + workspace.drawer::().is_some(), "Agent panel should exist" ); let dock = workspace.right_dock().read(cx); diff --git a/crates/agent_ui/src/thread_history_view.rs b/crates/agent_ui/src/thread_history_view.rs index 092169efbf57f2947f2532e4a599e7b4935dc539..ba692dde746bfa43c0b4ccdd7cebac4b616151ea 100644 --- a/crates/agent_ui/src/thread_history_view.rs +++ b/crates/agent_ui/src/thread_history_view.rs @@ -749,7 +749,7 @@ impl RenderOnce for HistoryEntryElement { .upgrade() .and_then(|view| view.read(cx).workspace().upgrade()) { - if let Some(panel) = workspace.read(cx).panel::(cx) { + if let Some(panel) = workspace.read(cx).drawer::() { panel.update(cx, |panel, cx| { if let Some(agent) = panel.selected_agent() { panel.load_agent_thread( diff --git a/crates/agent_ui/src/ui/acp_onboarding_modal.rs b/crates/agent_ui/src/ui/acp_onboarding_modal.rs index ee214e07ffb526f1c4ef89cc9301b4ea7e8d6ebf..0534010ae59a4eb50afb21345a20d6c23d296cbb 100644 --- a/crates/agent_ui/src/ui/acp_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/acp_onboarding_modal.rs @@ -33,9 +33,9 @@ impl AcpOnboardingModal { fn open_panel(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { self.workspace.update(cx, |workspace, cx| { - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_agent_thread( AgentType::Custom { diff --git a/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs b/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs index 3a9010b0a155873e658946b4155f09f8867e498a..74888868d6e2526db118203386cb0ff7846ff105 100644 --- a/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs +++ b/crates/agent_ui/src/ui/claude_agent_onboarding_modal.rs @@ -33,9 +33,9 @@ impl ClaudeCodeOnboardingModal { fn open_panel(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { self.workspace.update(cx, |workspace, cx| { - workspace.focus_panel::(window, cx); + workspace.focus_drawer::(window, cx); - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.new_agent_thread( AgentType::Custom { diff --git a/crates/agent_ui/src/ui/mention_crease.rs b/crates/agent_ui/src/ui/mention_crease.rs index b70b77e6ca603aba8fd55706918ffb3543e2a734..c54f7cb1c15d13a167b5bc831ce5646275f3fffd 100644 --- a/crates/agent_ui/src/ui/mention_crease.rs +++ b/crates/agent_ui/src/ui/mention_crease.rs @@ -273,7 +273,7 @@ fn open_thread( ) { use crate::AgentPanel; - let Some(panel) = workspace.panel::(cx) else { + let Some(panel) = workspace.drawer::() else { return; }; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 62c63a664dae08fbda3291dd9b63d2c94a518efb..9a1605b506a068780c238574ddbaa8cdfdd40d5f 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -14,7 +14,6 @@ use settings::SettingsStore; use std::sync::Arc; use ui::{ContextMenu, Divider, DividerColor, IconButton, Tooltip, h_flex}; use ui::{prelude::*, right_click_menu}; -use util::ResultExt as _; pub(crate) const RESIZE_HANDLE_SIZE: Pixels = px(6.); @@ -41,6 +40,7 @@ pub trait Panel: Focusable + EventEmitter + Render + Sized { fn icon_label(&self, _window: &Window, _: &App) -> Option { None } + fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool { false } @@ -78,7 +78,6 @@ pub trait PanelHandle: Send + Sync { fn icon(&self, window: &Window, cx: &App) -> Option; fn icon_tooltip(&self, window: &Window, cx: &App) -> &'static str; fn toggle_action(&self, window: &Window, cx: &App) -> Box; - fn icon_label(&self, window: &Window, cx: &App) -> Option; fn panel_focus_handle(&self, cx: &App) -> FocusHandle; fn to_any(&self) -> AnyView; fn activation_priority(&self, cx: &App) -> u32; @@ -168,10 +167,6 @@ where self.read(cx).toggle_action() } - fn icon_label(&self, window: &Window, cx: &App) -> Option { - self.read(cx).icon_label(window, cx) - } - fn to_any(&self) -> AnyView { self.clone().into() } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a2594e116441ef583ef7e3d54965f835cd712d64..bfd30e29d8c172d10f4897def8f505b7d13edcab 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -7017,6 +7017,110 @@ impl Workspace { cx.notify(); } + pub fn drawer(&self) -> Option> { + if let Some(left) = self.left_drawer.as_ref() { + if let Some(drawer) = left.view.clone().downcast().ok() { + return Some(drawer); + } + } + if let Some(right) = self.right_drawer.as_ref() { + if let Some(drawer) = right.view.clone().downcast().ok() { + return Some(drawer); + } + } + None + } + + pub fn focus_drawer( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> Option> { + if let Some(drawer) = self.left_drawer.as_mut() { + if let Some(view) = drawer.view.clone().downcast::().ok() { + drawer.open = true; + view.focus_handle(cx).focus(window, cx); + return Some(view); + } + } + if let Some(drawer) = self.right_drawer.as_mut() { + if let Some(view) = drawer.view.clone().downcast::().ok() { + drawer.open = true; + return Some(view); + } + } + None + } + + pub fn toggle_drawer_focus( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> bool { + if let Some(drawer) = self.drawer::() { + if drawer.focus_handle(cx).contains_focused(window, cx) { + // todo! focus the center? + false + } else { + drawer.focus_handle(cx).focus(window, cx); + true + } + } else { + false + } + } + + pub fn open_drawer(&mut self, cx: &mut Context) { + if let Some(left) = self.left_drawer.as_mut() { + if left.view.clone().downcast::().is_ok() { + left.open = true; + cx.notify(); + return; + } + } + if let Some(right) = self.right_drawer.as_mut() { + if right.view.clone().downcast::().is_ok() { + right.open = true; + cx.notify(); + return; + } + } + } + + pub fn close_drawer(&mut self, cx: &mut Context) { + if let Some(left) = self.left_drawer.as_mut() { + if left.view.clone().downcast::().is_ok() { + left.open = false; + cx.notify(); + return; + } + } + if let Some(right) = self.right_drawer.as_mut() { + if right.view.clone().downcast::().is_ok() { + right.open = false; + cx.notify(); + return; + } + } + } + + pub fn remove_drawer(&mut self, cx: &mut Context) { + if let Some(left) = self.left_drawer.as_mut() { + if left.view.clone().downcast::().is_ok() { + self.left_drawer = None; + cx.notify(); + return; + } + } + if let Some(right) = self.right_drawer.as_mut() { + if right.view.clone().downcast::().is_ok() { + self.right_drawer = None; + cx.notify(); + return; + } + } + } + pub fn left_drawer_view(&self) -> Option<&AnyView> { self.left_drawer.as_ref().map(|d| &d.view) } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f98d51061630fefba33f7703eac68670cde67502..4e7920314a54739a9d621886a6de611dd0c8b35d 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -923,7 +923,7 @@ fn handle_open_request(request: OpenRequest, app_state: Arc, cx: &mut multi_workspace.update(cx, |multi_workspace, window, cx| { multi_workspace.workspace().update(cx, |workspace, cx| { - if let Some(panel) = workspace.focus_panel::(window, cx) { + if let Some(panel) = workspace.focus_drawer::(window, cx) { panel.update(cx, |panel, cx| { panel.new_agent_thread_with_external_source_prompt( external_source_prompt, @@ -951,7 +951,7 @@ fn handle_open_request(request: OpenRequest, app_state: Arc, cx: &mut workspace.update(cx, |workspace, cx| { let client = workspace.project().read(cx).client(); let thread_store: Option> = workspace - .panel::(cx) + .drawer::() .map(|panel| panel.read(cx).thread_store().clone()); anyhow::Ok((client, thread_store)) }) @@ -989,7 +989,7 @@ fn handle_open_request(request: OpenRequest, app_state: Arc, cx: &mut multi_workspace.update(cx, |_, window, cx| { workspace.update(cx, |workspace, cx| { - if let Some(panel) = workspace.panel::(cx) { + if let Some(panel) = workspace.drawer::() { panel.update(cx, |panel, cx| { panel.open_thread( session_id, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index dd446041fd4e38dcdbcf70dad159ebc842d67648..ac5ffcc767c3626744ae7f0d33f3569e54a6fcf1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -657,36 +657,50 @@ fn initialize_panels( }) } -fn setup_or_teardown_ai_panel( +fn setup_or_teardown_ai_panels( workspace: &mut Workspace, + prompt_builder: Arc, window: &mut Window, cx: &mut Context, - load_panel: impl FnOnce( - WeakEntity, - AsyncWindowContext, - ) -> Task>> - + 'static, ) -> Task> { let disable_ai = SettingsStore::global(cx) .get::(None) .disable_ai || cfg!(test); - let existing_panel = workspace.panel::

(cx); + let existing_panel = workspace.panel::(cx); match (disable_ai, existing_panel) { (false, None) => cx.spawn_in(window, async move |workspace, cx| { - let panel = load_panel(workspace.clone(), cx.clone()).await?; + let agent_drawer = + agent_ui::AgentPanel::load(workspace.clone(), prompt_builder.clone(), cx.clone()) + .await?; + let sidebar_panel = + agent_ui::sidebar::Sidebar::load(workspace.clone(), cx.clone()).await?; workspace.update_in(cx, |workspace, window, cx| { let disable_ai = SettingsStore::global(cx) .get::(None) .disable_ai; - let have_panel = workspace.panel::

(cx).is_some(); + let have_panel = workspace.panel::(cx).is_some(); if !disable_ai && !have_panel { - workspace.add_panel(panel, window, cx); + let position = + workspace::dock::PanelHandle::position(&sidebar_panel, window, cx); + workspace.add_panel(sidebar_panel, window, cx); + match position { + workspace::dock::DockPosition::Left => { + workspace.set_left_drawer(agent_drawer.into(), cx) + } + workspace::dock::DockPosition::Right => { + workspace.set_right_drawer(agent_drawer.into(), cx) + } + workspace::dock::DockPosition::Bottom => { + unreachable!("drawers cannot go on the bottom") + } + } } }) }), (true, Some(existing_panel)) => { - workspace.remove_panel::

(&existing_panel, window, cx); + workspace.remove_panel(&existing_panel, window, cx); + workspace.remove_drawer::(cx); Task::ready(Ok(())) } _ => Task::ready(Ok(())), @@ -700,18 +714,7 @@ async fn initialize_agent_panel( ) -> anyhow::Result<()> { workspace_handle .update_in(&mut cx, |workspace, window, cx| { - let prompt_builder = prompt_builder.clone(); - setup_or_teardown_ai_panel(workspace, window, cx, move |workspace, cx| { - agent_ui::AgentPanel::load(workspace, prompt_builder, cx) - }) - })? - .await?; - - workspace_handle - .update_in(&mut cx, |workspace, window, cx| { - setup_or_teardown_ai_panel(workspace, window, cx, |workspace, cx| { - agent_ui::sidebar::Sidebar::load(workspace, cx) - }) + setup_or_teardown_ai_panels(workspace, prompt_builder.clone(), window, cx) })? .await?; @@ -719,14 +722,8 @@ async fn initialize_agent_panel( let prompt_builder = prompt_builder.clone(); cx.observe_global_in::(window, move |workspace, window, cx| { let prompt_builder = prompt_builder.clone(); - setup_or_teardown_ai_panel(workspace, window, cx, move |workspace, cx| { - agent_ui::AgentPanel::load(workspace, prompt_builder, cx) - }) - .detach_and_log_err(cx); - setup_or_teardown_ai_panel(workspace, window, cx, |workspace, cx| { - agent_ui::sidebar::Sidebar::load(workspace, cx) - }) - .detach_and_log_err(cx); + setup_or_teardown_ai_panels(workspace, prompt_builder.clone(), window, cx) + .detach_and_log_err(cx); }) .detach();