From b0aa7b2cddc57a55a1c0285a1ea82b59ac9783bd Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Thu, 7 May 2026 21:41:13 +0300 Subject: fix: preserve osc8 hyperlinks in client rendering fixes #73 --- src/server/headless.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'src/server/headless.rs') diff --git a/src/server/headless.rs b/src/server/headless.rs index 11b9d606..5fc7a544 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -368,6 +368,27 @@ fn render_virtual( (buffer, cursor) } +fn visible_hyperlinks(app_state: &AppState) -> Vec<((u16, u16), String, String)> { + let Some(ws_idx) = app_state.active else { + return Vec::new(); + }; + let Some(tab) = app_state + .workspaces + .get(ws_idx) + .and_then(crate::workspace::Workspace::active_tab) + else { + return Vec::new(); + }; + + let mut links = Vec::new(); + for info in &app_state.view.pane_infos { + if let Some(runtime) = tab.runtimes.get(&info.id) { + links.extend(runtime.visible_hyperlinks(info.inner_rect)); + } + } + links +} + fn focused_terminal_cursor(app_state: &AppState) -> Option { if app_state.mode != Mode::Terminal { return None; @@ -1120,6 +1141,13 @@ impl HeadlessServer { fn frame_server_message(msg: &ServerMessage) -> Result, protocol::FramingError> { let mut framed = Vec::new(); protocol::write_message(&mut framed, msg)?; + let payload_len = framed.len().saturating_sub(4); + if payload_len > MAX_FRAME_SIZE { + return Err(protocol::FramingError::Oversized { + claimed: payload_len, + max: MAX_FRAME_SIZE, + }); + } Ok(framed) } @@ -1568,7 +1596,9 @@ impl HeadlessServer { for (client_id, (cols, rows), is_foreground) in render_targets { let area = Rect::new(0, 0, cols, rows); let (buffer, cursor) = render_virtual(&mut self.app.state, area, is_foreground); - let frame = FrameData::from_ratatui_buffer(&buffer, cursor); + let hyperlinks = visible_hyperlinks(&self.app.state); + let frame = + FrameData::from_ratatui_buffer_with_hyperlinks(&buffer, cursor, &hyperlinks); let Some(client) = self.clients.get_mut(&client_id) else { continue; @@ -1584,6 +1614,14 @@ impl HeadlessServer { let message = ServerMessage::Frame(frame.clone()); let serialized = match Self::frame_server_message(&message) { Ok(framed) => framed, + Err(protocol::FramingError::Oversized { claimed, max }) => { + warn!( + client_id, + claimed, max, "skipping oversized frame for client" + ); + client.last_frame = Some(frame); + continue; + } Err(err) => { warn!(client_id, err = %err, "failed to serialize frame for client"); broken_clients.push(client_id); -- cgit v1.2.3