diff --git a/src/client/shell/endpoint_sidebar.rs b/src/client/shell/endpoint_sidebar.rs index a5080985..8a5316cf 100644 --- a/src/client/shell/endpoint_sidebar.rs +++ b/src/client/shell/endpoint_sidebar.rs @@ -342,7 +342,9 @@ pub(super) fn render_expanded( _ => 0, }) .collect::>(); - if std::mem::take(state.reveal_navigation_workspace) { + let reveal_navigation = !body.is_empty() && std::mem::take(state.reveal_navigation_workspace); + let reveal_focus = !body.is_empty() && std::mem::take(state.reveal_focused_workspace); + if reveal_navigation || reveal_focus { let selected_row = rows.iter().position(|row| match row { Row::Workspace { endpoint, entry } => { let endpoint = &state.endpoints[*endpoint]; @@ -351,9 +353,17 @@ pub(super) fn render_expanded( .as_deref() .and_then(|snapshot| snapshot.workspaces.get(entry.index)) .is_some_and(|workspace| { - state.selected_workspace_id.is_some_and(|target| { - target.matches(&endpoint.endpoint_id, &workspace.workspace_id) - }) + if reveal_navigation { + state.selected_workspace_id.is_some_and(|target| { + target.matches(&endpoint.endpoint_id, &workspace.workspace_id) + }) + } else { + &endpoint.endpoint_id == state.active_endpoint_id + && active_snapshot.is_some_and(|snapshot| { + snapshot.focused_workspace_id.as_deref() + == Some(workspace.workspace_id.as_str()) + }) + } }) } Row::Endpoint(_) => false, diff --git a/src/client/shell/tests/endpoints.rs b/src/client/shell/tests/endpoints.rs index a1c1abbf..6807c84f 100644 --- a/src/client/shell/tests/endpoints.rs +++ b/src/client/shell/tests/endpoints.rs @@ -632,6 +632,75 @@ fn saved_machine_preserves_endpoint_scoped_worktree_collapses() { .any(|hit| { hit.endpoint_id == remote_id && hit.workspace_id == "remote_ws_2" })); } +#[test] +fn expanded_machine_sidebar_reveals_newly_focused_workspace() { + let (mut state, remote_id) = state_with_remote(); + let mut initial = snapshot(); + let template = initial.workspaces[0].clone(); + initial.workspaces = (1..=12) + .map(|number| ClientShellWorkspace { + workspace_id: format!("ws_{number}"), + number, + label: format!("space-{number}"), + focused: number == 1, + ..template.clone() + }) + .collect(); + // Reuse workspace IDs across machines so revealing must be endpoint-scoped. + let mut remote = initial.clone(); + remote.boot_id = "remote-boot".into(); + remote.workspaces.push(ClientShellWorkspace { + workspace_id: "ws_13".into(), + number: 13, + focused: false, + ..template.clone() + }); + state.set_endpoint_snapshot(&remote_id, Box::new(remote)); + state.set_snapshot(Box::new(initial)); + state.compose(106, 20).expect("full machines sidebar"); + assert!(state.hits.workspace_max_scroll > 0); + + let mut update = state.snapshot.as_deref().expect("snapshot").clone(); + update.revision = 2; + update.workspaces.push(ClientShellWorkspace { + workspace_id: "ws_13".into(), + number: 13, + label: "new-space".into(), + ..template + }); + update.focused_workspace_id = Some("ws_13".into()); + for workspace in &mut update.workspaces { + workspace.focused = workspace.workspace_id == "ws_13"; + } + state.set_snapshot(Box::new(update)); + let mut updated_surface = surface(); + updated_surface.projection_revision = 2; + state.set_pane_surface(updated_surface); + state.compose(106, 2).expect("zero-height workspace body"); + assert!(state.reveal_focused_workspace); + state.compose(106, 20).expect("new workspace revealed"); + assert!(state + .hits + .workspaces + .iter() + .any(|hit| { hit.endpoint_id == ClientEndpointId::Local && hit.workspace_id == "ws_13" })); + + state.workspace_scroll = 0; + state.compose(106, 20).expect("manual scroll"); + assert_eq!(state.workspace_scroll, 0); + assert!(!state + .hits + .workspaces + .iter() + .any(|hit| { hit.endpoint_id == ClientEndpointId::Local && hit.workspace_id == "ws_13" })); + let unchanged = state.snapshot.as_deref().expect("snapshot").clone(); + state.set_snapshot(Box::new(unchanged)); + state + .compose(106, 20) + .expect("unchanged focus preserves scroll"); + assert_eq!(state.workspace_scroll, 0); +} + #[test] fn expanded_machine_sidebar_applies_space_row_gap_within_each_machine() { let (mut state, remote_id) = state_with_remote();