diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-15 03:58:07 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-15 04:02:14 +0300 |
| commit | 2c7c8beb079c5fd58a2e53efd2058df050ad9f55 (patch) | |
| tree | 6756f671dc728d915b1d6ebececa8cf7f70b66fb /src/server/headless.rs | |
| parent | 88370e1516182ab519d22a12fa82d5780a40fee6 (diff) | |
feat: add floating popup panes
refs #1125
Diffstat (limited to 'src/server/headless.rs')
| -rw-r--r-- | src/server/headless.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs index 720b43f2..9c21aa1b 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -3462,6 +3462,7 @@ impl HeadlessServer { fn retained_pty_update_allowed_by_app_state(&self) -> bool { self.app.state.mode == app::Mode::Terminal + && self.app.state.popup_pane.is_none() && self.app.state.selection.is_none() && self.app.state.copy_mode.is_none() && self.app.state.context_menu.is_none() @@ -7801,6 +7802,136 @@ next_tab = "" } #[tokio::test] + async fn retained_pty_update_declines_while_popup_is_visible() { + let (mut server, client_rx, _) = retained_test_server(b"tiled"); + let popup_runtime = + crate::terminal::TerminalRuntime::test_with_screen_bytes(40, 12, b"popup-aaaa"); + let (_, terminal_id) = server.app.install_test_popup_runtime(popup_runtime); + + server.render_and_stream(); + let initial = read_server_frame( + client_rx + .recv_timeout(Duration::from_millis(100)) + .expect("initial popup frame"), + ); + assert!(frame_text(&initial).contains("popup-aaaa")); + server + .app + .terminal_runtimes + .get(&terminal_id) + .unwrap() + .test_process_pty_bytes(b"\rZ"); + + assert!(!server.render_retained_pty_update_and_stream()); + server.render_and_stream(); + let updated = read_server_frame( + client_rx + .recv_timeout(Duration::from_millis(100)) + .expect("full popup fallback frame"), + ); + assert!(frame_text(&updated).contains("Zopup-aaaa")); + } + + #[tokio::test] + async fn popup_forces_host_mouse_capture_for_headless_client() { + let mut server = test_headless_server(); + let (client_tx, client_control_rx, _client_rx) = test_client_writer(); + server.clients.insert( + 1, + ClientConnection::new( + (80, 24), + crate::kitty_graphics::HostCellSize::default(), + crate::terminal_theme::TerminalTheme::default(), + None, + 1, + RenderEncoding::SemanticFrame, + Some(client_tx), + ), + ); + server.app.state.mouse_capture = false; + let popup_runtime = + crate::terminal::TerminalRuntime::test_with_screen_bytes(40, 12, b"popup"); + server.app.install_test_popup_runtime(popup_runtime); + + server.stream_host_mouse_capture_mode(); + + assert!(matches!( + read_server_message( + client_control_rx + .recv_timeout(Duration::from_millis(100)) + .expect("mouse capture message") + ), + ServerMessage::MouseCapture { enabled: true } + )); + } + + #[tokio::test] + async fn virtual_render_uses_popup_cursor() { + let (mut server, _client_rx, _) = retained_test_server(b"\x1b[2;2H"); + let popup_runtime = + crate::terminal::TerminalRuntime::test_with_screen_bytes(40, 12, b"\x1b[4;5H"); + let (_, terminal_id) = server.app.install_test_popup_runtime(popup_runtime); + + let (_, cursor) = crate::server::render_stream::render_virtual_with_runtime_registry( + &mut server.app.state, + &server.app.terminal_runtimes, + ratatui::layout::Rect::new(0, 0, 80, 24), + true, + crate::kitty_graphics::HostCellSize::default(), + ); + let (_, inner) = + crate::ui::popup_pane_rects(&server.app.state, server.app.state.view.terminal_area) + .unwrap(); + let expected = server + .app + .terminal_runtimes + .get(&terminal_id) + .unwrap() + .cursor_state(inner, true) + .unwrap(); + + assert_eq!( + cursor, + Some(crate::protocol::CursorState { + x: expected.x, + y: expected.y, + visible: expected.visible, + shape: expected.shape, + }) + ); + } + + #[tokio::test] + async fn virtual_render_does_not_resize_directly_attached_popup() { + let (mut server, _client_rx, _) = retained_test_server(b"tiled"); + let popup_runtime = crate::terminal::TerminalRuntime::test_with_screen_bytes(50, 13, b""); + let (_, terminal_id) = server.app.install_test_popup_runtime(popup_runtime); + server + .app + .state + .direct_attach_resize_locks + .insert(terminal_id.clone()); + + let _ = crate::server::render_stream::render_virtual_with_runtime_registry( + &mut server.app.state, + &server.app.terminal_runtimes, + ratatui::layout::Rect::new(0, 0, 80, 24), + true, + crate::kitty_graphics::HostCellSize::default(), + ); + + assert_eq!( + server + .app + .terminal_runtimes + .get(&terminal_id) + .unwrap() + .current_size(), + (13, 50) + ); + } + + #[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 { |
