use std::cell::RefCell;
use ratatui::{
style::{Color, Modifier, Style},
text::{Line, Span},
};
use crate::tui::app::state::AppState;
use crate::tui::message_formatter::MessageFormatter;
use crate::tui::ui::status_bar::format_timestamp;
const STREAM_REPARSE_THRESHOLD: usize = 48;
thread_local! {
static STREAM_PARSE_CACHE: RefCell<Option<(usize, Vec<Line<'static>>)>> =
const { RefCell::new(None) };
}
pub fn push_streaming_preview(
lines: &mut Vec<Line<'static>>,
state: &AppState,
separator_width: usize,
formatter: &MessageFormatter,
) {
if !state.processing || state.streaming_text.is_empty() {
return;
}
lines.push(Line::from(Span::styled(
"─".repeat(separator_width.min(40)),
Style::default().fg(Color::DarkGray).dim(),
)));
lines.push(Line::from(vec![
Span::styled(
format!("[{}] ", format_timestamp(std::time::SystemTime::now())),
Style::default().fg(Color::DarkGray).dim(),
),
Span::styled("◆ ", Style::default().fg(Color::Cyan).bold()),
Span::styled("assistant", Style::default().fg(Color::Cyan).bold()),
Span::styled(
" (streaming…)",
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::DIM),
),
]));
let formatted = cached_format(&state.streaming_text, formatter);
for line in formatted {
let mut spans = vec![Span::styled(" ", Style::default().fg(Color::Cyan))];
spans.extend(line.spans);
lines.push(Line::from(spans));
}
}
fn cached_format(text: &str, formatter: &MessageFormatter) -> Vec<Line<'static>> {
STREAM_PARSE_CACHE.with(|cell| {
let cur_len = text.len();
if let Some((parsed_len, ref lines)) = *cell.borrow()
&& cur_len >= parsed_len
&& cur_len - parsed_len < STREAM_REPARSE_THRESHOLD
{
return lines.clone();
}
let formatted = formatter.format_content(text, "assistant");
*cell.borrow_mut() = Some((cur_len, formatted.clone()));
formatted
})
}
pub fn reset_stream_parse_cache() {
STREAM_PARSE_CACHE.with(|cell| *cell.borrow_mut() = None);
}