diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-02 02:45:53 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-02 02:45:53 +0300 |
| commit | 1257c1e5cbb6487448ab10f2b6d153af7059e65e (patch) | |
| tree | f63e9f5bcf32d6ceb1a01c14efd180d9b65eec17 /src | |
| parent | cc527d45fe4821cb11c484372a9f25c51c02e078 (diff) | |
fix: preserve toasts during retained renders
Diffstat (limited to 'src')
| -rw-r--r-- | src/server/headless.rs | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs index 75195441..30ee2439 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -2514,6 +2514,7 @@ impl HeadlessServer { && self.app.state.selection.is_none() && self.app.state.copy_mode.is_none() && self.app.state.context_menu.is_none() + && self.app.state.toast.is_none() && !self.app.full_redraw_pending } @@ -3391,6 +3392,19 @@ mod tests { } } + fn frame_text(frame: &FrameData) -> String { + frame + .cells + .chunks(usize::from(frame.width)) + .map(|row| { + row.iter() + .map(|cell| cell.symbol.as_str()) + .collect::<String>() + }) + .collect::<Vec<_>>() + .join("\n") + } + fn read_server_shutdown_reason(bytes: Vec<u8>) -> Option<String> { match read_server_message(bytes) { ServerMessage::ServerShutdown { reason } => reason, @@ -5557,6 +5571,47 @@ next_tab = "" } #[tokio::test] + async fn retained_pty_update_declines_while_toast_is_visible() { + let (mut server, client_rx, pane_id) = retained_test_server(b"aaaa"); + server.app.state.toast = Some(crate::app::state::ToastNotification { + kind: crate::app::state::ToastKind::NeedsAttention, + title: "pi needs attention".to_owned(), + context: "background ยท 2".to_owned(), + target: None, + }); + server.render_and_stream(); + let initial = read_server_frame( + client_rx + .recv_timeout(Duration::from_millis(100)) + .expect("initial frame"), + ); + assert!( + frame_text(&initial).contains("pi needs attention"), + "expected initial full frame to include toast text" + ); + + let toast_row = server.app.state.view.toast_hit_area.y; + let inner_rect = server.app.state.view.pane_infos[0].inner_rect; + let pane_row = toast_row + .checked_sub(inner_rect.y) + .expect("toast should overlap the pane") + + 1; + assert!(pane_row <= inner_rect.height); + let runtime = server + .app + .state + .runtime_for_pane_in_workspace(&server.app.terminal_runtimes, 0, pane_id) + .expect("runtime"); + runtime.test_process_pty_bytes(format!("\x1b[{pane_row};1Hzzzz").as_bytes()); + + assert!(!server.render_retained_pty_update_and_stream()); + assert!( + client_rx.recv_timeout(Duration::from_millis(50)).is_err(), + "retained path should not stream a frame that can overwrite toast cells" + ); + } + + #[tokio::test] async fn retained_pty_update_matches_full_render_frame() { let initial = b"\x1b[6 qleft \xe4\xb8\xad"; let update = b"\r\x1b[44mZ\x1b[0m"; |
