Detailed changes
@@ -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::<AgentPanel>(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::<AgentPanel>(cx);
}
}
}
@@ -822,7 +822,6 @@ pub struct AgentPanel {
agent_navigation_menu: Option<Entity<ContextMenu>>,
_extension_subscription: Option<Subscription>,
width: Option<Pixels>,
- height: Option<Pixels>,
zoomed: bool,
pending_serialization: Option<Task<Result<()>>>,
onboarding: Entity<AgentPanelOnboarding>,
@@ -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<Workspace>, cx: &App) -> bool {
- let workspace_read = workspace.read(cx);
-
- workspace_read
- .drawer::<AgentPanel>()
- .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::<Self>()
}
pub fn active_connection_view(&self) -> Option<&Entity<ConnectionView>> {
@@ -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>) {
- 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::<AgentPanel>(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"
@@ -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!(
@@ -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::<crate::AgentPanel>(window, cx);
@@ -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<MultiWorkspace>,
persistence_key: Option<u64>,
is_open: bool,
@@ -254,7 +254,7 @@ pub struct Sidebar {
_subscriptions: Vec<gpui::Subscription>,
}
-impl Sidebar {
+impl ThreadsPanel {
pub fn load(
_workspace: WeakEntity<Workspace>,
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<PanelEvent> for Sidebar {}
+impl EventEmitter<PanelEvent> 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<Self>) -> 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<MultiWorkspace>,
cx: &mut gpui::VisualTestContext,
- ) -> Entity<Sidebar> {
+ ) -> Entity<ThreadsPanel> {
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<MultiWorkspace>,
cx: &mut gpui::VisualTestContext,
- ) -> (Entity<Sidebar>, Entity<AgentPanel>) {
+ ) -> (Entity<ThreadsPanel>, Entity<AgentPanel>) {
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::<AgentPanel>() {
- dock.activate_panel(panel_ix, window, cx);
- }
- dock.set_open(true, window, cx);
- });
+ workspace.focus_drawer::<AgentPanel>(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<Sidebar>, cx: &mut gpui::VisualTestContext) {
+ fn open_and_focus_sidebar(sidebar: &Entity<ThreadsPanel>, 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>,
+ sidebar: &Entity<ThreadsPanel>,
cx: &mut gpui::VisualTestContext,
) -> Vec<String> {
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<Sidebar>, query: &str, cx: &mut gpui::VisualTestContext) {
+ fn type_in_search(
+ sidebar: &Entity<ThreadsPanel>,
+ 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| {
@@ -7017,6 +7017,34 @@ impl Workspace {
cx.notify();
}
+ fn drawer_mut<T: 'static>(&mut self) -> Option<(Entity<T>, &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<T: 'static>(&self) -> Option<(Entity<T>, &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<T: 'static>(&self) -> Option<Entity<T>> {
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<Self>,
) -> Option<Entity<T>> {
- if let Some(drawer) = self.left_drawer.as_mut() {
- if let Some(view) = drawer.view.clone().downcast::<T>().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::<T>().ok() {
- drawer.open = true;
- return Some(view);
- }
+ if let Some((view, drawer)) = self.drawer_mut::<T>() {
+ 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<Self>,
) -> bool {
- if let Some(drawer) = self.drawer::<T>() {
- if drawer.focus_handle(cx).contains_focused(window, cx) {
+ if let Some((view, drawer)) = self.drawer_mut::<T>() {
+ 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<T: Focusable>(&mut self, cx: &mut Context<Self>) {
- if let Some(left) = self.left_drawer.as_mut() {
- if left.view.clone().downcast::<T>().is_ok() {
- left.open = true;
+ pub fn toggle_drawer<T: Focusable>(&mut self, cx: &mut Context<Self>) -> bool {
+ if let Some((_, drawer)) = self.drawer_mut::<T>() {
+ if drawer.open {
+ drawer.open = false;
cx.notify();
- return;
- }
- }
- if let Some(right) = self.right_drawer.as_mut() {
- if right.view.clone().downcast::<T>().is_ok() {
- right.open = true;
+ false
+ } else {
+ drawer.open = true;
cx.notify();
- return;
+ true
}
+ } else {
+ false
+ }
+ }
+
+ pub fn open_drawer<T: Focusable>(&mut self, cx: &mut Context<Self>) {
+ if let Some((_, drawer)) = self.drawer_mut::<T>() {
+ drawer.open = true;
+ cx.notify();
}
}
pub fn close_drawer<T: Focusable>(&mut self, cx: &mut Context<Self>) {
- if let Some(left) = self.left_drawer.as_mut() {
- if left.view.clone().downcast::<T>().is_ok() {
- left.open = false;
- cx.notify();
- return;
- }
+ if let Some((_, drawer)) = self.drawer_mut::<T>() {
+ drawer.open = false;
+ cx.notify();
}
- if let Some(right) = self.right_drawer.as_mut() {
- if right.view.clone().downcast::<T>().is_ok() {
- right.open = false;
- cx.notify();
- return;
- }
+ }
+
+ pub fn drawer_is_open<T: 'static>(&self) -> bool {
+ if let Some((_, drawer)) = self.drawer_ref::<T>() {
+ 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<Self>) {
- if let Some(drawer) = &mut self.left_drawer {
- drawer.open = true;
- cx.notify();
- }
- }
-
- pub fn open_right_drawer(&mut self, cx: &mut Context<Self>) {
- if let Some(drawer) = &mut self.right_drawer {
- drawer.open = true;
- cx.notify();
- }
- }
-
- pub fn close_left_drawer(&mut self, cx: &mut Context<Self>) {
- if let Some(drawer) = &mut self.left_drawer {
- drawer.open = false;
- cx.notify();
- }
- }
-
- pub fn close_right_drawer(&mut self, cx: &mut Context<Self>) {
- if let Some(drawer) = &mut self.right_drawer {
- drawer.open = false;
- cx.notify();
- }
- }
-
- pub fn toggle_left_drawer(&mut self, cx: &mut Context<Self>) {
- if let Some(drawer) = &mut self.left_drawer {
- drawer.open = !drawer.open;
- cx.notify();
- }
- }
-
- pub fn toggle_right_drawer(&mut self, cx: &mut Context<Self>) {
- 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>) {
self.left_drawer = None;
cx.notify();
@@ -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::<DisableAiSettings>(None)
.disable_ai
|| cfg!(test);
- let existing_panel = workspace.panel::<agent_ui::sidebar::Sidebar>(cx);
+ let existing_panel = workspace.panel::<agent_ui::ThreadsPanel>(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::<DisableAiSettings>(None)
.disable_ai;
- let have_panel = workspace.panel::<agent_ui::sidebar::Sidebar>(cx).is_some();
+ let have_panel = workspace.panel::<agent_ui::ThreadsPanel>(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>| {
workspace
- .toggle_panel_focus::<agent_ui::sidebar::Sidebar>(window, cx);
+ .toggle_panel_focus::<agent_ui::ThreadsPanel>(window, cx);
},
)
.register_action({