codetether_agent/tui/ui/chat_view/
streaming.rs1use std::cell::RefCell;
9
10use ratatui::{
11 style::{Color, Modifier, Style},
12 text::{Line, Span},
13};
14
15use crate::tui::app::state::AppState;
16use crate::tui::message_formatter::MessageFormatter;
17use crate::tui::ui::status_bar::format_timestamp;
18
19const STREAM_REPARSE_THRESHOLD: usize = 48;
22
23thread_local! {
24 static STREAM_PARSE_CACHE: RefCell<Option<(usize, Vec<Line<'static>>)>> =
27 const { RefCell::new(None) };
28}
29
30pub fn push_streaming_preview(
39 lines: &mut Vec<Line<'static>>,
40 state: &AppState,
41 separator_width: usize,
42 formatter: &MessageFormatter,
43) {
44 if !state.processing || state.streaming_text.is_empty() {
45 return;
46 }
47 lines.push(Line::from(Span::styled(
48 "─".repeat(separator_width.min(40)),
49 Style::default().fg(Color::DarkGray).dim(),
50 )));
51 lines.push(Line::from(vec![
52 Span::styled(
53 format!("[{}] ", format_timestamp(std::time::SystemTime::now())),
54 Style::default().fg(Color::DarkGray).dim(),
55 ),
56 Span::styled("◆ ", Style::default().fg(Color::Cyan).bold()),
57 Span::styled("assistant", Style::default().fg(Color::Cyan).bold()),
58 Span::styled(
59 " (streaming…)",
60 Style::default()
61 .fg(Color::DarkGray)
62 .add_modifier(Modifier::DIM),
63 ),
64 super::elapsed_badge::elapsed_badge(state),
65 ]));
66 let formatted = cached_format(&state.streaming_text, formatter);
67 for line in formatted {
68 let mut spans = vec![Span::styled(" ", Style::default().fg(Color::Cyan))];
69 spans.extend(line.spans);
70 lines.push(Line::from(spans));
71 }
72}
73
74fn cached_format(text: &str, formatter: &MessageFormatter) -> Vec<Line<'static>> {
75 STREAM_PARSE_CACHE.with(|cell| {
76 let cur_len = text.len();
77 if let Some((parsed_len, ref lines)) = *cell.borrow()
78 && cur_len >= parsed_len
79 && cur_len - parsed_len < STREAM_REPARSE_THRESHOLD
80 {
81 return lines.clone();
82 }
83 let formatted = formatter.format_content(text, "assistant");
84 *cell.borrow_mut() = Some((cur_len, formatted.clone()));
85 formatted
86 })
87}
88
89pub fn reset_stream_parse_cache() {
93 STREAM_PARSE_CACHE.with(|cell| *cell.borrow_mut() = None);
94}