aboutsummaryrefslogtreecommitdiff
path: root/src/server/headless.rs
diff options
context:
space:
mode:
authorakbash <akbash@herdr.dev>2026-07-06 23:59:59 +0300
committerGitHub <noreply@github.com>2026-07-06 23:59:59 +0300
commit4f22e9a73ba4d9029468e078235c5f5ce629d179 (patch)
tree49b56a29305192c8270aa43e87bb5f1d86d00233 /src/server/headless.rs
parent56edcbcba83b8ff7f6f69ee4231f163e97df3346 (diff)
fix: preserve wrapped pane links (#1099)
refs #1098 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: Can Celik <ogulcancelik@gmail.com>
Diffstat (limited to 'src/server/headless.rs')
-rw-r--r--src/server/headless.rs43
1 files changed, 43 insertions, 0 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs
index e84dfc59..04320ff2 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -163,6 +163,13 @@ fn dirty_patch_intersects_hyperlinks(
false
}
+fn dirty_patch_contains_plain_url_candidate(patch: &crate::pane::TerminalDirtyPatch) -> bool {
+ patch.rows.iter().any(|(_, row_cells)| {
+ let row: String = row_cells.iter().map(|cell| cell.symbol.as_str()).collect();
+ !crate::app::actions::visible_url_spans(&row).is_empty()
+ })
+}
+
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
@@ -3196,6 +3203,9 @@ impl HeadlessServer {
if dirty_patch_intersects_hyperlinks(&frame, info.inner_rect, &patch) {
retained_fallback!("hyperlink_intersection");
}
+ if dirty_patch_contains_plain_url_candidate(&patch) {
+ retained_fallback!("plain_url_candidate");
+ }
if !apply_terminal_dirty_patch(&mut frame, info.inner_rect, patch) {
retained_fallback!("patch_apply_failed");
}
@@ -3372,6 +3382,7 @@ impl HeadlessServer {
let hyperlinks = crate::server::render_stream::visible_hyperlinks(
&self.app.state,
&self.app.terminal_runtimes,
+ Some(&buffer),
);
crate::render_prof::duration_since(
"full_render.visible_hyperlinks",
@@ -7513,6 +7524,38 @@ next_tab = ""
}
#[tokio::test]
+ async fn retained_pty_update_declines_when_dirty_row_creates_plain_url() {
+ let (mut server, client_rx, pane_id) = retained_test_server(b"plain");
+ server.render_and_stream();
+ let _ = client_rx
+ .recv_timeout(Duration::from_millis(100))
+ .expect("initial frame");
+
+ let runtime = server
+ .app
+ .state
+ .runtime_for_pane_in_workspace(&server.app.terminal_runtimes, 0, pane_id)
+ .expect("runtime");
+ runtime.test_process_pty_bytes(b"\rhttps://example.com/new");
+
+ assert!(!server.render_retained_pty_update_and_stream());
+ assert!(client_rx.recv_timeout(Duration::from_millis(50)).is_err());
+
+ server.render_and_stream();
+ let full = read_server_frame(
+ client_rx
+ .recv_timeout(Duration::from_millis(100))
+ .expect("full frame after plain URL"),
+ );
+ assert!(
+ full.hyperlinks
+ .iter()
+ .any(|uri| uri == "https://example.com/new"),
+ "full render should synthesize plain URL hyperlink metadata"
+ );
+ }
+
+ #[tokio::test]
async fn retained_pty_update_allows_kitty_enabled_empty_graphics_cache() {
let (mut server, client_rx, pane_id) = retained_test_server(b"aaaa");
server.app.state.kitty_graphics_enabled = true;