From 3a8fdb74c692c584bab01a4ed88906817eb11727 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Sun, 15 Mar 2026 16:28:02 -0700 Subject: [PATCH] Rename Sidebar -> ThreadsPanel, get some agent_ui tests passing --- crates/agent_ui/src/agent_panel.rs | 38 ++--- crates/agent_ui/src/agent_ui.rs | 3 +- crates/agent_ui/src/connection_view.rs | 2 +- .../src/{sidebar.rs => threads_panel.rs} | 45 +++--- crates/workspace/src/workspace.rs | 144 ++++++++---------- crates/zed/src/zed.rs | 15 +- 6 files changed, 105 insertions(+), 142 deletions(-) rename crates/agent_ui/src/{sidebar.rs => threads_panel.rs} (99%) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 1c16a79e9286158b039d29c683d2e9767b8a4522..4526a87e19489b588ce22875803a2403d642591c 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -430,14 +430,14 @@ pub fn init(cx: &mut App) { if workspace.right_drawer_view().is_none() { workspace.set_right_drawer(panel_view, cx); } else { - workspace.toggle_right_drawer(cx); + workspace.toggle_drawer::(cx); } } DockPosition::Left | DockPosition::Bottom => { if workspace.left_drawer_view().is_none() { workspace.set_left_drawer(panel_view, cx); } else { - workspace.toggle_left_drawer(cx); + workspace.toggle_drawer::(cx); } } } @@ -822,7 +822,6 @@ pub struct AgentPanel { agent_navigation_menu: Option>, _extension_subscription: Option, width: Option, - height: Option, zoomed: bool, pending_serialization: Option>>, onboarding: Entity, @@ -1159,7 +1158,6 @@ impl AgentPanel { agent_navigation_menu: None, _extension_subscription: extension_subscription, width: None, - height: None, zoomed: false, pending_serialization: None, onboarding, @@ -1250,20 +1248,7 @@ impl AgentPanel { } pub fn is_visible(workspace: &Entity, cx: &App) -> bool { - let workspace_read = workspace.read(cx); - - workspace_read - .drawer::() - .map(|panel| { - let panel_id = Entity::entity_id(&panel); - - workspace_read.all_docks().iter().any(|dock| { - dock.read(cx) - .visible_panel() - .is_some_and(|visible_panel| visible_panel.panel_id() == panel_id) - }) - }) - .unwrap_or(false) + workspace.read(cx).drawer_is_open::() } pub fn active_connection_view(&self) -> Option<&Entity> { @@ -3070,11 +3055,6 @@ impl AgentPanel { 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 { @@ -5159,7 +5139,7 @@ mod tests { let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx)); let panel = cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx)); - workspace.add_panel(panel, window, cx); + workspace.set_left_drawer(panel.clone().into(), cx); }); cx.run_until_parked(); @@ -5671,7 +5651,7 @@ mod tests { let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx)); let panel = cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx)); - workspace.add_panel(panel.clone(), window, cx); + workspace.set_left_drawer(panel.clone().into(), cx); panel }); @@ -5781,7 +5761,7 @@ mod tests { let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx)); let panel = cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx)); - workspace.add_panel(panel.clone(), window, cx); + workspace.set_left_drawer(panel.clone().into(), cx); panel }); @@ -5866,17 +5846,17 @@ mod tests { let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx)); let panel = cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx)); - workspace.add_panel(panel.clone(), window, cx); + workspace.set_left_drawer(panel.clone().into(), cx); + workspace.focus_drawer::(window, cx); panel }); cx.run_until_parked(); // Simulate worktree creation in progress and reset to Uninitialized - panel.update_in(cx, |panel, window, cx| { + panel.update(cx, |panel, _cx| { panel.worktree_creation_status = Some(WorktreeCreationStatus::Creating); panel.active_view = ActiveView::Uninitialized; - Panel::set_active(panel, true, window, cx); assert!( matches!(panel.active_view, ActiveView::Uninitialized), "set_active should not create a thread while worktree is being created" diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index db0cf873418ea38f8d5771c13b281528218fb94e..5672ef5fe55ac1324be8c48d8d75e7ed9865e2dd 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -23,7 +23,6 @@ mod mode_selector; mod model_selector; mod model_selector_popover; mod profile_selector; -pub mod sidebar; mod slash_command; mod slash_command_picker; mod terminal_codegen; @@ -35,6 +34,7 @@ mod text_thread_history; mod thread_history; mod thread_history_view; mod threads_archive_view; +mod threads_panel; mod ui; use std::rc::Rc; @@ -78,6 +78,7 @@ pub(crate) use model_selector_popover::ModelSelectorPopover; pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor}; pub(crate) use thread_history::ThreadHistory; pub(crate) use thread_history_view::*; +pub use threads_panel::ThreadsPanel; use zed_actions; actions!( diff --git a/crates/agent_ui/src/connection_view.rs b/crates/agent_ui/src/connection_view.rs index 2e03fcd55ecb73ec8fc60a03af2c93b29aa9e7a6..4798b4579ba6f05531845f7757e1e36085884a3e 100644 --- a/crates/agent_ui/src/connection_view.rs +++ b/crates/agent_ui/src/connection_view.rs @@ -3490,7 +3490,7 @@ pub(crate) mod tests { cx.new(|cx| TextThreadStore::fake(workspace.project().clone(), cx)); let panel = cx.new(|cx| crate::AgentPanel::new(workspace, text_thread_store, None, window, cx)); - workspace.add_panel(panel, window, cx); + workspace.set_left_drawer(panel.clone().into(), cx); // Open the dock and activate the agent panel so it's visible workspace.focus_drawer::(window, cx); diff --git a/crates/agent_ui/src/sidebar.rs b/crates/agent_ui/src/threads_panel.rs similarity index 99% rename from crates/agent_ui/src/sidebar.rs rename to crates/agent_ui/src/threads_panel.rs index b36164ffde09cff4bac84427b84830d3ff0e4af2..aa562538ad6addaaf53305a82b9e3e68cf833b53 100644 --- a/crates/agent_ui/src/sidebar.rs +++ b/crates/agent_ui/src/threads_panel.rs @@ -10,9 +10,9 @@ use db::kvp::KEY_VALUE_STORE; use editor::Editor; use feature_flags::{AgentV2FeatureFlag, FeatureFlagViewExt as _}; use gpui::{ - Action, AnyElement, App, AsyncWindowContext, Context, Entity, EventEmitter, FocusHandle, - Focusable, ListState, Pixels, Render, SharedString, Task, WeakEntity, Window, actions, list, - prelude::*, px, + AnyElement, App, AsyncWindowContext, Context, Entity, EventEmitter, FocusHandle, Focusable, + ListState, Pixels, Render, SharedString, Task, WeakEntity, Window, actions, list, prelude::*, + px, }; use menu::{Cancel, Confirm, SelectFirst, SelectLast, SelectNext, SelectPrevious}; use project::Event as ProjectEvent; @@ -232,7 +232,7 @@ fn workspace_label_from_path_list(path_list: &PathList) -> SharedString { } } -pub struct Sidebar { +pub struct ThreadsPanel { multi_workspace: WeakEntity, persistence_key: Option, is_open: bool, @@ -254,7 +254,7 @@ pub struct Sidebar { _subscriptions: Vec, } -impl Sidebar { +impl ThreadsPanel { pub fn load( _workspace: WeakEntity, mut cx: AsyncWindowContext, @@ -1856,7 +1856,7 @@ impl Sidebar { } } -impl Sidebar { +impl ThreadsPanel { pub fn is_open(&self) -> bool { self.is_open } @@ -1984,15 +1984,15 @@ impl Sidebar { } } -impl Focusable for Sidebar { +impl Focusable for ThreadsPanel { fn focus_handle(&self, cx: &App) -> FocusHandle { self.filter_editor.focus_handle(cx) } } -impl EventEmitter for Sidebar {} +impl EventEmitter for ThreadsPanel {} -impl Panel for Sidebar { +impl Panel for ThreadsPanel { fn persistent_name() -> &'static str { "ThreadsSidebar" } @@ -2056,7 +2056,7 @@ impl Panel for Sidebar { } } -impl Render for Sidebar { +impl Render for ThreadsPanel { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let ui_font = theme::setup_ui_font(window, cx); let docked_right = AgentSettings::get_global(cx).dock == settings::DockPosition::Right; @@ -2170,7 +2170,7 @@ mod tests { fn setup_sidebar( multi_workspace: &Entity, cx: &mut gpui::VisualTestContext, - ) -> Entity { + ) -> Entity { let (sidebar, _panel) = setup_sidebar_with_agent_panel(multi_workspace, cx); sidebar } @@ -2178,22 +2178,17 @@ mod tests { fn setup_sidebar_with_agent_panel( multi_workspace: &Entity, cx: &mut gpui::VisualTestContext, - ) -> (Entity, Entity) { + ) -> (Entity, Entity) { let workspace = multi_workspace.read_with(cx, |mw, _cx| mw.workspace().clone()); let project = workspace.read_with(cx, |ws, _cx| ws.project().clone()); let panel = add_agent_panel(&workspace, &project, cx); workspace.update_in(cx, |workspace, window, cx| { - workspace.right_dock().update(cx, |dock, cx| { - if let Some(panel_ix) = dock.panel_index_for_type::() { - dock.activate_panel(panel_ix, window, cx); - } - dock.set_open(true, window, cx); - }); + workspace.focus_drawer::(window, cx); }); cx.run_until_parked(); let multi_workspace_entity = multi_workspace.clone(); let sidebar = workspace.update_in(cx, |_, window, cx| { - cx.new(|cx| Sidebar::new(multi_workspace_entity, window, cx)) + cx.new(|cx| ThreadsPanel::new(multi_workspace_entity, window, cx)) }); (sidebar, panel) } @@ -2242,7 +2237,7 @@ mod tests { cx.run_until_parked(); } - fn open_and_focus_sidebar(sidebar: &Entity, cx: &mut gpui::VisualTestContext) { + fn open_and_focus_sidebar(sidebar: &Entity, cx: &mut gpui::VisualTestContext) { cx.run_until_parked(); sidebar.update_in(cx, |sidebar, window, cx| { sidebar.set_open(true, cx); @@ -2252,7 +2247,7 @@ mod tests { } fn visible_entries_as_strings( - sidebar: &Entity, + sidebar: &Entity, cx: &mut gpui::VisualTestContext, ) -> Vec { sidebar.read_with(cx, |sidebar, _cx| { @@ -3154,7 +3149,7 @@ mod tests { workspace.update_in(cx, |workspace, window, cx| { let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx)); let panel = cx.new(|cx| AgentPanel::test_new(workspace, text_thread_store, window, cx)); - workspace.add_panel(panel.clone(), window, cx); + workspace.set_left_drawer(panel.clone().into(), cx); panel }) } @@ -3266,7 +3261,11 @@ mod tests { ); } - fn type_in_search(sidebar: &Entity, query: &str, cx: &mut gpui::VisualTestContext) { + fn type_in_search( + sidebar: &Entity, + query: &str, + cx: &mut gpui::VisualTestContext, + ) { sidebar.update_in(cx, |sidebar, window, cx| { window.focus(&sidebar.filter_editor.focus_handle(cx), cx); sidebar.filter_editor.update(cx, |editor, cx| { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index bfd30e29d8c172d10f4897def8f505b7d13edcab..57cd92f129256585de277865e60ce45166cbbd39 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -7017,6 +7017,34 @@ impl Workspace { cx.notify(); } + fn drawer_mut(&mut self) -> Option<(Entity, &mut Drawer)> { + if let Some(left) = self.left_drawer.as_mut() { + if let Some(drawer) = left.view.clone().downcast().ok() { + return Some((drawer, left)); + } + } + if let Some(right) = self.right_drawer.as_mut() { + if let Some(drawer) = right.view.clone().downcast().ok() { + return Some((drawer, right)); + } + } + None + } + + fn drawer_ref(&self) -> Option<(Entity, &Drawer)> { + if let Some(left) = self.left_drawer.as_ref() { + if let Some(drawer) = left.view.clone().downcast().ok() { + return Some((drawer, left)); + } + } + if let Some(right) = self.right_drawer.as_ref() { + if let Some(drawer) = right.view.clone().downcast().ok() { + return Some((drawer, right)); + } + } + None + } + pub fn drawer(&self) -> Option> { if let Some(left) = self.left_drawer.as_ref() { if let Some(drawer) = left.view.clone().downcast().ok() { @@ -7036,18 +7064,10 @@ impl Workspace { 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); - } + if let Some((view, drawer)) = self.drawer_mut::() { + drawer.open = true; + view.focus_handle(cx).focus(window, cx); + return Some(view); } None } @@ -7057,12 +7077,14 @@ impl Workspace { window: &mut Window, cx: &mut Context, ) -> bool { - if let Some(drawer) = self.drawer::() { - if drawer.focus_handle(cx).contains_focused(window, cx) { + if let Some((view, drawer)) = self.drawer_mut::() { + if view.focus_handle(cx).contains_focused(window, cx) { // todo! focus the center? false } else { - drawer.focus_handle(cx).focus(window, cx); + drawer.open = true; + view.focus_handle(cx).focus(window, cx); + cx.notify(); true } } else { @@ -7070,37 +7092,41 @@ impl Workspace { } } - 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; + pub fn toggle_drawer(&mut self, cx: &mut Context) -> bool { + if let Some((_, drawer)) = self.drawer_mut::() { + if drawer.open { + drawer.open = false; cx.notify(); - return; - } - } - if let Some(right) = self.right_drawer.as_mut() { - if right.view.clone().downcast::().is_ok() { - right.open = true; + false + } else { + drawer.open = true; cx.notify(); - return; + true } + } else { + false + } + } + + pub fn open_drawer(&mut self, cx: &mut Context) { + if let Some((_, drawer)) = self.drawer_mut::() { + drawer.open = true; + cx.notify(); } } 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((_, drawer)) = self.drawer_mut::() { + drawer.open = false; + cx.notify(); } - if let Some(right) = self.right_drawer.as_mut() { - if right.view.clone().downcast::().is_ok() { - right.open = false; - cx.notify(); - return; - } + } + + pub fn drawer_is_open(&self) -> bool { + if let Some((_, drawer)) = self.drawer_ref::() { + drawer.open + } else { + false } } @@ -7137,48 +7163,6 @@ impl Workspace { self.right_drawer.as_ref().is_some_and(|d| d.open) } - pub fn open_left_drawer(&mut self, cx: &mut Context) { - if let Some(drawer) = &mut self.left_drawer { - drawer.open = true; - cx.notify(); - } - } - - pub fn open_right_drawer(&mut self, cx: &mut Context) { - if let Some(drawer) = &mut self.right_drawer { - drawer.open = true; - cx.notify(); - } - } - - pub fn close_left_drawer(&mut self, cx: &mut Context) { - if let Some(drawer) = &mut self.left_drawer { - drawer.open = false; - cx.notify(); - } - } - - pub fn close_right_drawer(&mut self, cx: &mut Context) { - if let Some(drawer) = &mut self.right_drawer { - drawer.open = false; - cx.notify(); - } - } - - pub fn toggle_left_drawer(&mut self, cx: &mut Context) { - if let Some(drawer) = &mut self.left_drawer { - drawer.open = !drawer.open; - cx.notify(); - } - } - - pub fn toggle_right_drawer(&mut self, cx: &mut Context) { - if let Some(drawer) = &mut self.right_drawer { - drawer.open = !drawer.open; - cx.notify(); - } - } - pub fn remove_left_drawer(&mut self, cx: &mut Context) { self.left_drawer = None; cx.notify(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index ac5ffcc767c3626744ae7f0d33f3569e54a6fcf1..b63d79e22ded76675ec1325b8d2047e6f3854527 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -88,9 +88,9 @@ use workspace::notifications::{ }; use workspace::{ - AppState, MultiWorkspace, NewFile, NewWindow, OpenLog, Panel, Toast, Workspace, - WorkspaceSettings, create_and_open_local_file, - notifications::simple_message_notification::MessageNotification, open_new, + AppState, MultiWorkspace, NewFile, NewWindow, OpenLog, Toast, Workspace, WorkspaceSettings, + create_and_open_local_file, notifications::simple_message_notification::MessageNotification, + open_new, }; use workspace::{ CloseIntent, CloseProject, CloseWindow, NotificationFrame, RestoreBanner, @@ -667,19 +667,18 @@ fn setup_or_teardown_ai_panels( .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 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?; + let sidebar_panel = agent_ui::ThreadsPanel::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 { let position = workspace::dock::PanelHandle::position(&sidebar_panel, window, cx); @@ -1048,7 +1047,7 @@ fn register_actions( window: &mut Window, cx: &mut Context| { workspace - .toggle_panel_focus::(window, cx); + .toggle_panel_focus::(window, cx); }, ) .register_action({