From 4f878221333403caac0862560c9df36a680ad14e Mon Sep 17 00:00:00 2001 From: shibang Date: Fri, 19 Dec 2025 00:03:42 +1300 Subject: [PATCH] gpui: Persist window bounds and display when detaching a workspace session (#45201) Closes #41246 #45092 Release Notes: - N/A **Root Cause**: Empty local workspaces returned `DetachFromSession` from `serialize_workspace_location()`, and the `DetachFromSession` handler only cleared the session_id **without saving window bounds**. **Fix Applied**: Modified the `DetachFromSession` handler to save window bounds via `set_window_open_status()`: ```rust WorkspaceLocation::DetachFromSession => { let window_bounds = SerializedWindowBounds(window.window_bounds()); let display = window.display(cx).and_then(|d| d.uuid().ok()); window.spawn(cx, async move |_| { persistence::DB .set_window_open_status(database_id, window_bounds, display.unwrap_or_default()) .await.log_err(); persistence::DB.set_session_id(database_id, None).await.log_err(); }) } ``` **Recording**: https://github.com/user-attachments/assets/2b6564d4-4e1b-40fe-943b-147296340aa7 --- crates/workspace/src/persistence.rs | 49 +++++++++++++++++++++++++++++ crates/workspace/src/workspace.rs | 24 ++++++++++---- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index cf5bdf2ab0059f10f2fb44e2069c8c0baf24d72b..094d03494e726677dc43235d96fc62c076673bf5 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -3296,4 +3296,53 @@ mod tests { assert_eq!(workspace.center_group, new_workspace.center_group); } + + #[gpui::test] + async fn test_empty_workspace_window_bounds() { + zlog::init_test(); + + let db = WorkspaceDb::open_test_db("test_empty_workspace_window_bounds").await; + let id = db.next_id().await.unwrap(); + + // Create a workspace with empty paths (empty workspace) + let empty_paths: &[&str] = &[]; + let display_uuid = Uuid::new_v4(); + let window_bounds = SerializedWindowBounds(WindowBounds::Windowed(Bounds { + origin: point(px(100.0), px(200.0)), + size: size(px(800.0), px(600.0)), + })); + + let workspace = SerializedWorkspace { + id, + paths: PathList::new(empty_paths), + location: SerializedWorkspaceLocation::Local, + center_group: Default::default(), + window_bounds: None, + display: None, + docks: Default::default(), + breakpoints: Default::default(), + centered_layout: false, + session_id: None, + window_id: None, + user_toolchains: Default::default(), + }; + + // Save the workspace (this creates the record with empty paths) + db.save_workspace(workspace.clone()).await; + + // Save window bounds separately (as the actual code does via set_window_open_status) + db.set_window_open_status(id, window_bounds, display_uuid) + .await + .unwrap(); + + // Retrieve it using empty paths + let retrieved = db.workspace_for_roots(empty_paths).unwrap(); + + // Verify window bounds were persisted + assert_eq!(retrieved.id, id); + assert!(retrieved.window_bounds.is_some()); + assert_eq!(retrieved.window_bounds.unwrap().0, window_bounds.0); + assert!(retrieved.display.is_some()); + assert_eq!(retrieved.display.unwrap(), display_uuid); + } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a412b74600158f83d250da021a9f06b627ea98ac..0c5c9ffa5d0bfb1f70ce6a861b0209f321222fc0 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -5665,12 +5665,24 @@ impl Workspace { persistence::DB.save_workspace(serialized_workspace).await; }) } - WorkspaceLocation::DetachFromSession => window.spawn(cx, async move |_| { - persistence::DB - .set_session_id(database_id, None) - .await - .log_err(); - }), + WorkspaceLocation::DetachFromSession => { + let window_bounds = SerializedWindowBounds(window.window_bounds()); + let display = window.display(cx).and_then(|d| d.uuid().ok()); + window.spawn(cx, async move |_| { + persistence::DB + .set_window_open_status( + database_id, + window_bounds, + display.unwrap_or_default(), + ) + .await + .log_err(); + persistence::DB + .set_session_id(database_id, None) + .await + .log_err(); + }) + } WorkspaceLocation::None => Task::ready(()), } }