@@ -203,6 +203,8 @@ actions!(
         CloseActiveDock,
         /// Closes all docks.
         CloseAllDocks,
+        /// Toggles all docks.
+        ToggleAllDocks,
         /// Closes the current window.
         CloseWindow,
         /// Opens the feedback dialog.
@@ -1176,6 +1178,7 @@ pub struct Workspace {
     _items_serializer: Task<Result<()>>,
     session_id: Option<String>,
     scheduled_tasks: Vec<Task<()>>,
+    last_open_dock_positions: Vec<DockPosition>,
 }
 
 impl EventEmitter<Event> for Workspace {}
@@ -1518,6 +1521,7 @@ impl Workspace {
             session_id: Some(session_id),
 
             scheduled_tasks: Vec::new(),
+            last_open_dock_positions: Vec::new(),
         }
     }
 
@@ -2987,12 +2991,17 @@ impl Workspace {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let dock = self.dock_at_position(dock_side);
         let mut focus_center = false;
         let mut reveal_dock = false;
+
+        let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
+        let was_visible = self.is_dock_at_position_open(dock_side, cx) && !other_is_zoomed;
+        if was_visible {
+            self.save_open_dock_positions(cx);
+        }
+
+        let dock = self.dock_at_position(dock_side);
         dock.update(cx, |dock, cx| {
-            let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
-            let was_visible = dock.is_open() && !other_is_zoomed;
             dock.set_open(!was_visible, window, cx);
 
             if dock.active_panel().is_none() {
@@ -3041,7 +3050,8 @@ impl Workspace {
     }
 
     fn close_active_dock(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
-        if let Some(dock) = self.active_dock(window, cx) {
+        if let Some(dock) = self.active_dock(window, cx).cloned() {
+            self.save_open_dock_positions(cx);
             dock.update(cx, |dock, cx| {
                 dock.set_open(false, window, cx);
             });
@@ -3051,6 +3061,7 @@ impl Workspace {
     }
 
     pub fn close_all_docks(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        self.save_open_dock_positions(cx);
         for dock in self.all_docks() {
             dock.update(cx, |dock, cx| {
                 dock.set_open(false, window, cx);
@@ -3062,6 +3073,67 @@ impl Workspace {
         self.serialize_workspace(window, cx);
     }
 
+    fn get_open_dock_positions(&self, cx: &Context<Self>) -> Vec<DockPosition> {
+        self.all_docks()
+            .into_iter()
+            .filter_map(|dock| {
+                let dock_ref = dock.read(cx);
+                if dock_ref.is_open() {
+                    Some(dock_ref.position())
+                } else {
+                    None
+                }
+            })
+            .collect()
+    }
+
+    /// Saves the positions of currently open docks.
+    ///
+    /// Updates `last_open_dock_positions` with positions of all currently open
+    /// docks, to later be restored by the 'Toggle All Docks' action.
+    fn save_open_dock_positions(&mut self, cx: &mut Context<Self>) {
+        let open_dock_positions = self.get_open_dock_positions(cx);
+        if !open_dock_positions.is_empty() {
+            self.last_open_dock_positions = open_dock_positions;
+        }
+    }
+
+    /// Toggles all docks between open and closed states.
+    ///
+    /// If any docks are open, closes all and remembers their positions. If all
+    /// docks are closed, restores the last remembered dock configuration.
+    fn toggle_all_docks(
+        &mut self,
+        _: &ToggleAllDocks,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let open_dock_positions = self.get_open_dock_positions(cx);
+
+        if !open_dock_positions.is_empty() {
+            self.close_all_docks(window, cx);
+        } else if !self.last_open_dock_positions.is_empty() {
+            self.restore_last_open_docks(window, cx);
+        }
+    }
+
+    /// Reopens docks from the most recently remembered configuration.
+    ///
+    /// Opens all docks whose positions are stored in `last_open_dock_positions`
+    /// and clears the stored positions.
+    fn restore_last_open_docks(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        let positions_to_open = std::mem::take(&mut self.last_open_dock_positions);
+
+        for position in positions_to_open {
+            let dock = self.dock_at_position(position);
+            dock.update(cx, |dock, cx| dock.set_open(true, window, cx));
+        }
+
+        cx.focus_self(window);
+        cx.notify();
+        self.serialize_workspace(window, cx);
+    }
+
     /// Transfer focus to the panel of the given type.
     pub fn focus_panel<T: Panel>(
         &mut self,
@@ -5761,6 +5833,7 @@ impl Workspace {
                     workspace.close_all_docks(window, cx);
                 }),
             )
+            .on_action(cx.listener(Self::toggle_all_docks))
             .on_action(cx.listener(
                 |workspace: &mut Workspace, _: &ClearAllNotifications, _, cx| {
                     workspace.clear_all_notifications(cx);
@@ -9206,6 +9279,238 @@ mod tests {
         });
     }
 
+    #[gpui::test]
+    async fn test_toggle_all_docks(cx: &mut gpui::TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, [], cx).await;
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
+        workspace.update_in(cx, |workspace, window, cx| {
+            // Open two docks
+            let left_dock = workspace.dock_at_position(DockPosition::Left);
+            let right_dock = workspace.dock_at_position(DockPosition::Right);
+
+            left_dock.update(cx, |dock, cx| dock.set_open(true, window, cx));
+            right_dock.update(cx, |dock, cx| dock.set_open(true, window, cx));
+
+            assert!(left_dock.read(cx).is_open());
+            assert!(right_dock.read(cx).is_open());
+        });
+
+        workspace.update_in(cx, |workspace, window, cx| {
+            // Toggle all docks - should close both
+            workspace.toggle_all_docks(&ToggleAllDocks, window, cx);
+
+            let left_dock = workspace.dock_at_position(DockPosition::Left);
+            let right_dock = workspace.dock_at_position(DockPosition::Right);
+            assert!(!left_dock.read(cx).is_open());
+            assert!(!right_dock.read(cx).is_open());
+        });
+
+        workspace.update_in(cx, |workspace, window, cx| {
+            // Toggle again - should reopen both
+            workspace.toggle_all_docks(&ToggleAllDocks, window, cx);
+
+            let left_dock = workspace.dock_at_position(DockPosition::Left);
+            let right_dock = workspace.dock_at_position(DockPosition::Right);
+            assert!(left_dock.read(cx).is_open());
+            assert!(right_dock.read(cx).is_open());
+        });
+    }
+
+    #[gpui::test]
+    async fn test_toggle_all_with_manual_close(cx: &mut gpui::TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+
+        let project = Project::test(fs, [], cx).await;
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
+        workspace.update_in(cx, |workspace, window, cx| {
+            // Open two docks
+            let left_dock = workspace.dock_at_position(DockPosition::Left);
+            let right_dock = workspace.dock_at_position(DockPosition::Right);
+
+            left_dock.update(cx, |dock, cx| dock.set_open(true, window, cx));
+            right_dock.update(cx, |dock, cx| dock.set_open(true, window, cx));
+
+            assert!(left_dock.read(cx).is_open());
+            assert!(right_dock.read(cx).is_open());
+        });
+
+        workspace.update_in(cx, |workspace, window, cx| {
+            // Close them manually
+            workspace.toggle_dock(DockPosition::Left, window, cx);
+            workspace.toggle_dock(DockPosition::Right, window, cx);
+
+            let left_dock = workspace.dock_at_position(DockPosition::Left);
+            let right_dock = workspace.dock_at_position(DockPosition::Right);
+            assert!(!left_dock.read(cx).is_open());
+            assert!(!right_dock.read(cx).is_open());
+        });
+
+        workspace.update_in(cx, |workspace, window, cx| {
+            // Toggle all docks - only last closed (right dock) should reopen
+            workspace.toggle_all_docks(&ToggleAllDocks, window, cx);
+
+            let left_dock = workspace.dock_at_position(DockPosition::Left);
+            let right_dock = workspace.dock_at_position(DockPosition::Right);
+            assert!(!left_dock.read(cx).is_open());
+            assert!(right_dock.read(cx).is_open());
+        });
+    }
+
+    #[gpui::test]
+    async fn test_toggle_all_docks_after_dock_move(cx: &mut gpui::TestAppContext) {
+        init_test(cx);
+        let fs = FakeFs::new(cx.executor());
+        let project = Project::test(fs, [], cx).await;
+        let (workspace, cx) =
+            cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx));
+
+        // Open two docks (left and right) with one panel each
+        let (left_panel, right_panel) = workspace.update_in(cx, |workspace, window, cx| {
+            let left_panel = cx.new(|cx| TestPanel::new(DockPosition::Left, cx));
+            workspace.add_panel(left_panel.clone(), window, cx);
+
+            let right_panel = cx.new(|cx| TestPanel::new(DockPosition::Right, cx));
+            workspace.add_panel(right_panel.clone(), window, cx);
+
+            workspace.toggle_dock(DockPosition::Left, window, cx);
+            workspace.toggle_dock(DockPosition::Right, window, cx);
+
+            // Verify initial state
+            assert!(
+                workspace.left_dock().read(cx).is_open(),
+                "Left dock should be open"
+            );
+            assert_eq!(
+                workspace
+                    .left_dock()
+                    .read(cx)
+                    .visible_panel()
+                    .unwrap()
+                    .panel_id(),
+                left_panel.panel_id(),
+                "Left panel should be visible in left dock"
+            );
+            assert!(
+                workspace.right_dock().read(cx).is_open(),
+                "Right dock should be open"
+            );
+            assert_eq!(
+                workspace
+                    .right_dock()
+                    .read(cx)
+                    .visible_panel()
+                    .unwrap()
+                    .panel_id(),
+                right_panel.panel_id(),
+                "Right panel should be visible in right dock"
+            );
+            assert!(
+                !workspace.bottom_dock().read(cx).is_open(),
+                "Bottom dock should be closed"
+            );
+
+            (left_panel, right_panel)
+        });
+
+        // Focus the left panel and move it to the next position (bottom dock)
+        workspace.update_in(cx, |workspace, window, cx| {
+            workspace.toggle_panel_focus::<TestPanel>(window, cx); // Focus left panel
+            assert!(
+                left_panel.read(cx).focus_handle(cx).is_focused(window),
+                "Left panel should be focused"
+            );
+        });
+
+        cx.dispatch_action(MoveFocusedPanelToNextPosition);
+
+        // Verify the left panel has moved to the bottom dock, and the bottom dock is now open
+        workspace.update(cx, |workspace, cx| {
+            assert!(
+                !workspace.left_dock().read(cx).is_open(),
+                "Left dock should be closed"
+            );
+            assert!(
+                workspace.bottom_dock().read(cx).is_open(),
+                "Bottom dock should now be open"
+            );
+            assert_eq!(
+                left_panel.read(cx).position,
+                DockPosition::Bottom,
+                "Left panel should now be in the bottom dock"
+            );
+            assert_eq!(
+                workspace
+                    .bottom_dock()
+                    .read(cx)
+                    .visible_panel()
+                    .unwrap()
+                    .panel_id(),
+                left_panel.panel_id(),
+                "Left panel should be the visible panel in the bottom dock"
+            );
+        });
+
+        // Toggle all docks off
+        workspace.update_in(cx, |workspace, window, cx| {
+            workspace.toggle_all_docks(&ToggleAllDocks, window, cx);
+            assert!(
+                !workspace.left_dock().read(cx).is_open(),
+                "Left dock should be closed"
+            );
+            assert!(
+                !workspace.right_dock().read(cx).is_open(),
+                "Right dock should be closed"
+            );
+            assert!(
+                !workspace.bottom_dock().read(cx).is_open(),
+                "Bottom dock should be closed"
+            );
+        });
+
+        // Toggle all docks back on and verify positions are restored
+        workspace.update_in(cx, |workspace, window, cx| {
+            workspace.toggle_all_docks(&ToggleAllDocks, window, cx);
+            assert!(
+                !workspace.left_dock().read(cx).is_open(),
+                "Left dock should remain closed"
+            );
+            assert!(
+                workspace.right_dock().read(cx).is_open(),
+                "Right dock should remain open"
+            );
+            assert!(
+                workspace.bottom_dock().read(cx).is_open(),
+                "Bottom dock should remain open"
+            );
+            assert_eq!(
+                left_panel.read(cx).position,
+                DockPosition::Bottom,
+                "Left panel should remain in the bottom dock"
+            );
+            assert_eq!(
+                right_panel.read(cx).position,
+                DockPosition::Right,
+                "Right panel should remain in the right dock"
+            );
+            assert_eq!(
+                workspace
+                    .bottom_dock()
+                    .read(cx)
+                    .visible_panel()
+                    .unwrap()
+                    .panel_id(),
+                left_panel.panel_id(),
+                "Left panel should be the visible panel in the right dock"
+            );
+        });
+    }
+
     #[gpui::test]
     async fn test_join_pane_into_next(cx: &mut gpui::TestAppContext) {
         init_test(cx);