fix(ui): keep spinner patches behind sidebar chrome

refs #2534
This commit is contained in:
Jonathan Liebig
2026-09-15 03:37:40 +02:00
parent 0b24a21779
commit f3272b5a32
5 changed files with 45 additions and 1 deletions
+2 -1
View File
@@ -54,7 +54,8 @@ impl ClientShellState {
.iter()
.map(|spinner| {
let mut cell = spinner.cell.clone();
cell.symbol = (*symbol).to_owned();
cell.symbol.clear();
cell.symbol.push_str(symbol);
crate::protocol::PaneSurfacePatchRow {
x: spinner.x,
y: spinner.y,
+3
View File
@@ -286,6 +286,9 @@ pub(super) fn render_shell_sidebar(
} else {
render_sidebar(buffer, area, snapshot, config, state, hits);
}
let sidebar_toggle = hits.sidebar_toggle;
hits.animated_status_cells
.retain(|cell| !super::contains(sidebar_toggle, (cell.x, cell.y)));
}
pub(super) fn record_animated_status_cells(
+1
View File
@@ -1922,6 +1922,7 @@ impl ClientShellState {
fn spinner_eligible(&self) -> bool {
self.config.status_indicators == crate::config::StatusIndicatorStyle::Animated
&& self.mode != ClientShellMode::Navigate
&& self.endpoint_status(&self.active_endpoint_id) == Some(ClientEndpointStatus::Online)
&& self
.last_composed_size
.is_some_and(|(cols, rows)| self.layout(cols, rows).sidebar.width > 0)
@@ -719,6 +719,7 @@ fn animated_status_advances_only_on_its_desktop_deadline() {
state.chrome_drag = None;
state.endpoints[0].status = ClientEndpointStatus::Reconnecting;
assert!(state.compose_spinner_patch(106, 30).is_none());
assert!(!state.tick_spinner(deadline + std::time::Duration::from_millis(80)));
state.endpoints[0].status = ClientEndpointStatus::Online;
state.config.status_indicators = crate::config::StatusIndicatorStyle::Dots;
+38
View File
@@ -139,6 +139,44 @@ fn collapsed_wide_machine_initial_records_the_spinner_cell() {
.any(|spinner| spinner.x == rect.x + 2 && spinner.y == rect.y));
}
#[test]
fn sidebar_toggle_is_not_retained_as_a_spinner_cell() {
let (mut state, _) = state_with_scrollable_agents();
state.config.status_indicators = crate::config::StatusIndicatorStyle::Animated;
state.sidebar_collapsed = true;
for endpoint in &mut state.endpoints {
if let Some(snapshot) = endpoint.snapshot.as_mut() {
for agent in &mut snapshot.agents {
agent.agent_status = AgentStatus::Working;
}
}
}
let first = state.compose(100, 8).expect("collapsed endpoint sidebar");
let toggle = state.hits.sidebar_toggle;
assert!(state
.hits
.endpoint_agents
.iter()
.any(|(rect, _, _)| contains(*rect, (toggle.x, toggle.y))));
assert!(!state
.hits
.animated_status_cells
.iter()
.any(|cell| contains(toggle, (cell.x, cell.y))));
let now = std::time::Instant::now();
state.timer_delay(now);
let deadline = state.next_spinner_frame.expect("spinner deadline");
assert!(state.tick_spinner(deadline));
let patch = state
.compose_spinner_patch(100, 8)
.expect("visible spinner patch");
let patched = apply_composed_surface_patch(&first, patch).expect("applicable spinner patch");
let recomposed = state.compose(100, 8).expect("advanced full frame");
assert_eq!(patched, recomposed);
}
#[test]
fn switching_machines_preserves_aggregate_agent_scroll_and_visible_rows() {
let (mut state, remote) = state_with_scrollable_agents();