workspace: Persist dock state for empty workspaces (#45966)

Closes #4568

Release Notes:

- Fixed project-less workspaces not persisting their dock state

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
Pedro Paulo Magno 2026-01-22 13:35:59 -03:00 committed by GitHub
parent 5e9ca9cf77
commit d6bd3661e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 67 additions and 3 deletions

View file

@ -281,6 +281,25 @@ impl From<WindowBoundsJson> for WindowBounds {
}
}
const DEFAULT_DOCK_STATE_KEY: &str = "default_dock_state";
pub fn read_default_dock_state() -> Option<DockStructure> {
let json_str = KEY_VALUE_STORE
.read_kvp(DEFAULT_DOCK_STATE_KEY)
.log_err()
.flatten()?;
serde_json::from_str::<DockStructure>(&json_str).ok()
}
pub async fn write_default_dock_state(docks: DockStructure) -> anyhow::Result<()> {
let json_str = serde_json::to_string(&docks)?;
KEY_VALUE_STORE
.write_kvp(DEFAULT_DOCK_STATE_KEY.to_string(), json_str)
.await?;
Ok(())
}
#[derive(Debug)]
pub struct Breakpoint {
pub position: u32,

View file

@ -15,6 +15,7 @@ use gpui::{AsyncWindowContext, Entity, WeakEntity};
use language::{Toolchain, ToolchainScope};
use project::{Project, debugger::breakpoint_store::SourceBreakpoint};
use remote::RemoteConnectionOptions;
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
@ -64,7 +65,7 @@ pub(crate) struct SerializedWorkspace {
pub(crate) window_id: Option<u64>,
}
#[derive(Debug, PartialEq, Clone, Default)]
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
pub struct DockStructure {
pub(crate) left: DockData,
pub(crate) right: DockData,
@ -114,7 +115,7 @@ impl Bind for DockStructure {
}
}
#[derive(Debug, PartialEq, Clone, Default)]
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
pub struct DockData {
pub(crate) visible: bool,
pub(crate) active_panel: Option<String>,

View file

@ -1823,6 +1823,15 @@ impl Workspace {
};
notify_if_database_failed(window, cx);
// Check if this is an empty workspace (no paths to open)
// An empty workspace is one where project_paths is empty
let is_empty_workspace = project_paths.is_empty();
// Check if serialized workspace has paths before it's moved
let serialized_workspace_has_paths = serialized_workspace
.as_ref()
.map(|ws| !ws.paths.is_empty())
.unwrap_or(false);
let opened_items = window
.update(cx, |_workspace, window, cx| {
open_items(serialized_workspace, project_paths, window, cx)
@ -1830,6 +1839,32 @@ impl Workspace {
.await
.unwrap_or_default();
// Restore default dock state for empty workspaces
// Only restore if:
// 1. This is an empty workspace (no paths), AND
// 2. The serialized workspace either doesn't exist or has no paths
if is_empty_workspace && !serialized_workspace_has_paths {
if let Some(default_docks) = persistence::read_default_dock_state() {
window
.update(cx, |workspace, window, cx| {
for (dock, serialized_dock) in [
(&mut workspace.right_dock, default_docks.right),
(&mut workspace.left_dock, default_docks.left),
(&mut workspace.bottom_dock, default_docks.bottom),
]
.iter_mut()
{
dock.update(cx, |dock, cx| {
dock.serialized_dock = Some(serialized_dock.clone());
dock.restore_state(window, cx);
});
}
cx.notify();
})
.log_err();
}
}
window
.update(cx, |workspace, window, cx| {
window.activate_window();
@ -5858,6 +5893,8 @@ impl Workspace {
WorkspaceLocation::DetachFromSession => {
let window_bounds = SerializedWindowBounds(window.window_bounds());
let display = window.display(cx).and_then(|d| d.uuid().ok());
// Save dock state for empty local workspaces
let docks = build_serialized_docks(self, window, cx);
window.spawn(cx, async move |_| {
persistence::DB
.set_window_open_status(
@ -5871,9 +5908,16 @@ impl Workspace {
.set_session_id(database_id, None)
.await
.log_err();
persistence::write_default_dock_state(docks).await.log_err();
})
}
WorkspaceLocation::None => {
// Save dock state for empty non-local workspaces
let docks = build_serialized_docks(self, window, cx);
window.spawn(cx, async move |_| {
persistence::write_default_dock_state(docks).await.log_err();
})
}
WorkspaceLocation::None => Task::ready(()),
}
}