From 168077fe4234c1de4b494dd8a884d063b70d75ba Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 9 Mar 2026 15:30:45 -0700 Subject: [PATCH] Add "Toggle Agent Mode" action that enables / disables the multi-workspace sidebar Co-authored-by: cameron Co-authored-by: Eric Holk --- .../src/platform_title_bar.rs | 11 +++++ crates/sidebar/src/sidebar.rs | 3 ++ crates/title_bar/src/title_bar.rs | 8 ++++ crates/workspace/src/multi_workspace.rs | 41 ++++++++++++++++++- crates/zed/src/zed/app_menus.rs | 2 + crates/zed_actions/src/lib.rs | 2 + 6 files changed, 65 insertions(+), 2 deletions(-) diff --git a/crates/platform_title_bar/src/platform_title_bar.rs b/crates/platform_title_bar/src/platform_title_bar.rs index 7053fe89e7fdc6ece9ad50fdd8facaf31dba3086..146fe2ce4cb8c6064af0073e770ae3fc0f903392 100644 --- a/crates/platform_title_bar/src/platform_title_bar.rs +++ b/crates/platform_title_bar/src/platform_title_bar.rs @@ -33,6 +33,7 @@ pub struct PlatformTitleBar { system_window_tabs: Entity, workspace_sidebar_open: bool, sidebar_has_notifications: bool, + is_singleton: bool, } impl PlatformTitleBar { @@ -48,6 +49,7 @@ impl PlatformTitleBar { system_window_tabs, workspace_sidebar_open: false, sidebar_has_notifications: false, + is_singleton: false, } } @@ -96,6 +98,15 @@ impl PlatformTitleBar { cx.notify(); } + pub fn is_singleton(&self) -> bool { + self.is_singleton + } + + pub fn set_singleton(&mut self, is_singleton: bool, cx: &mut Context) { + self.is_singleton = is_singleton; + cx.notify(); + } + pub fn is_multi_workspace_enabled(cx: &App) -> bool { cx.has_flag::() && !DisableAiSettings::get_global(cx).disable_ai } diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index 1e50a75e2841fb471b2d630b71c2df59200c5bea..a163d48a595383980f0169b6cb5096d144bc15ba 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -218,6 +218,9 @@ impl Sidebar { MultiWorkspaceEvent::WorkspaceRemoved(_) => { this.update_entries(cx); } + MultiWorkspaceEvent::SingletonModeChanged => { + cx.notify(); + } }, ) .detach(); diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 3566d6210769c09a8a6de1706cb258ff2b119ce9..a67e88bb72ceaa3b3f4a84e7fc5dab81ccc42d33 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -371,18 +371,22 @@ impl TitleBar { let is_open = multi_workspace.read(cx).is_sidebar_open(); let has_notifications = multi_workspace.read(cx).sidebar_has_notifications(cx); + let is_singleton = multi_workspace.read(cx).is_singleton(); platform_titlebar.update(cx, |titlebar, cx| { titlebar.set_workspace_sidebar_open(is_open, cx); titlebar.set_sidebar_has_notifications(has_notifications, cx); + titlebar.set_singleton(is_singleton, cx); }); let platform_titlebar = platform_titlebar.clone(); let subscription = cx.observe(&multi_workspace, move |mw, cx| { let is_open = mw.read(cx).is_sidebar_open(); let has_notifications = mw.read(cx).sidebar_has_notifications(cx); + let is_singleton = mw.read(cx).is_singleton(); platform_titlebar.update(cx, |titlebar, cx| { titlebar.set_workspace_sidebar_open(is_open, cx); titlebar.set_sidebar_has_notifications(has_notifications, cx); + titlebar.set_singleton(is_singleton, cx); }); }); @@ -692,6 +696,10 @@ impl TitleBar { return None; } + if self.platform_titlebar.read(cx).is_singleton() { + return None; + } + let is_sidebar_open = self.platform_titlebar.read(cx).is_workspace_sidebar_open(); if is_sidebar_open { diff --git a/crates/workspace/src/multi_workspace.rs b/crates/workspace/src/multi_workspace.rs index 3f5981178fe118f41196538e1a22960bd55644d0..3ea2a9a7934e01dbb3b8651398daa780bff3c390 100644 --- a/crates/workspace/src/multi_workspace.rs +++ b/crates/workspace/src/multi_workspace.rs @@ -39,6 +39,7 @@ pub enum MultiWorkspaceEvent { ActiveWorkspaceChanged, WorkspaceAdded(Entity), WorkspaceRemoved(EntityId), + SingletonModeChanged, } pub enum SidebarEvent { @@ -108,6 +109,7 @@ pub struct MultiWorkspace { active_workspace_index: usize, sidebar: Option>, sidebar_open: bool, + is_singleton: bool, _sidebar_subscription: Option, pending_removal_tasks: Vec>, _serialize_task: Option>, @@ -144,6 +146,7 @@ impl MultiWorkspace { active_workspace_index: 0, sidebar: None, sidebar_open: false, + is_singleton: false, _sidebar_subscription: None, pending_removal_tasks: Vec::new(), _serialize_task: None, @@ -191,8 +194,29 @@ impl MultiWorkspace { cx.has_flag::() && !DisableAiSettings::get_global(cx).disable_ai } + pub fn is_singleton(&self) -> bool { + self.is_singleton + } + + pub fn set_singleton( + &mut self, + is_singleton: bool, + window: &mut Window, + cx: &mut Context, + ) { + if self.is_singleton == is_singleton { + return; + } + self.is_singleton = is_singleton; + if is_singleton && self.sidebar_open { + self.close_sidebar(window, cx); + } + cx.emit(MultiWorkspaceEvent::SingletonModeChanged); + cx.notify(); + } + pub fn toggle_sidebar(&mut self, window: &mut Window, cx: &mut Context) { - if !self.multi_workspace_enabled(cx) { + if !self.multi_workspace_enabled(cx) || self.is_singleton { return; } @@ -207,7 +231,7 @@ impl MultiWorkspace { } pub fn focus_sidebar(&mut self, window: &mut Window, cx: &mut Context) { - if !self.multi_workspace_enabled(cx) { + if !self.multi_workspace_enabled(cx) || self.is_singleton { return; } @@ -233,6 +257,9 @@ impl MultiWorkspace { } pub fn open_sidebar(&mut self, cx: &mut Context) { + if self.is_singleton { + return; + } self.sidebar_open = true; for workspace in &self.workspaces { workspace.update(cx, |workspace, cx| { @@ -773,6 +800,16 @@ impl Render for MultiWorkspace { this.activate_previous_workspace(window, cx); }, )) + .on_action(cx.listener( + |this: &mut Self, _: &zed_actions::agent::ToggleAgentMode, window, cx| { + if this.is_singleton { + this.set_singleton(false, window, cx); + this.open_sidebar(cx); + } else { + this.set_singleton(true, window, cx); + } + }, + )) .when(self.multi_workspace_enabled(cx), |this| { this.on_action(cx.listener( |this: &mut Self, _: &ToggleWorkspaceSidebar, window, cx| { diff --git a/crates/zed/src/zed/app_menus.rs b/crates/zed/src/zed/app_menus.rs index f73d703557f8f73ad380c0b7a2cb995b29f92cf1..82a5136bd57ba45291473ccc74b58d2f65543f6a 100644 --- a/crates/zed/src/zed/app_menus.rs +++ b/crates/zed/src/zed/app_menus.rs @@ -47,6 +47,8 @@ pub fn app_menus(cx: &mut App) -> Vec { MenuItem::separator(), MenuItem::action("Diagnostics", diagnostics::Deploy), MenuItem::separator(), + MenuItem::action("Toggle Agent Mode", zed_actions::agent::ToggleAgentMode), + MenuItem::separator(), ]; if ReleaseChannel::try_global(cx) == Some(ReleaseChannel::Dev) { diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index ae785bb4a0c792dd7f55d8850e8c05ce6327c108..4d0c97d26749ae96cff71b0339a31c8aca15bb6a 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -456,6 +456,8 @@ pub mod agent { ResetAgentZoom, /// Pastes clipboard content without any formatting. PasteRaw, + /// Toggles the agent singleton mode for the current window. + ToggleAgentMode, ] );