Skip to main content

codetether_agent/tui/ui/chat_view/
lines.rs

1//! Cached chat-line buffer builder.
2//!
3//! [`build_chat_lines`] returns a width-keyed cached vector of [`Line`]s,
4//! delegating to [`append_entries`] and [`push_streaming_preview`].
5
6use ratatui::text::Line;
7
8use crate::tui::app::state::App;
9use crate::tui::color_palette::ColorPalette;
10use crate::tui::message_formatter::MessageFormatter;
11use crate::tui::ui::tool_panel::build_render_entries;
12
13use super::empty::push_empty_placeholder;
14use super::entries::append_entries;
15use super::streaming::push_streaming_preview;
16
17/// Build (or return cached) chat lines for the current width.
18///
19/// Uses [`AppState::get_or_build_message_lines`] as cache key.
20///
21/// # Examples
22///
23/// ```rust,no_run
24/// # use codetether_agent::tui::ui::chat_view::lines::build_chat_lines;
25/// # fn d(a:&mut codetether_agent::tui::app::state::App){ let p=codetether_agent::tui::color_palette::ColorPalette::marketing(); let f=codetether_agent::tui::message_formatter::MessageFormatter::new(76); let l=build_chat_lines(a,80,80,&f,&p); }
26/// ```
27pub fn build_chat_lines(
28    app: &mut App,
29    max_width: usize,
30    content_width: usize,
31    formatter: &MessageFormatter,
32    palette: &ColorPalette,
33) -> Vec<Line<'static>> {
34    if let Some(cached) = app.state.get_or_build_message_lines(max_width) {
35        return cached;
36    }
37    let separator_width = content_width.saturating_sub(2).min(60);
38    let panel_width = content_width.saturating_sub(4);
39
40    // Fast path: messages / width / processing unchanged — only streaming_text
41    // differs. Reuse the frozen prefix and rebuild just the streaming suffix.
42    if let Some(mut lines) = app.state.clone_frozen_prefix(max_width) {
43        let frozen_len = lines.len();
44        push_streaming_preview(&mut lines, &app.state, separator_width, formatter);
45        app.state
46            .store_message_lines_with_frozen(lines.clone(), max_width, frozen_len);
47        return lines;
48    }
49
50    let mut lines = Vec::new();
51    let entries = build_render_entries(&app.state.messages);
52    if entries.is_empty() {
53        push_empty_placeholder(&mut lines);
54        app.state.set_tool_preview_max_scroll(0);
55    } else {
56        let result = append_entries(
57            &mut lines,
58            &entries,
59            separator_width,
60            panel_width,
61            app.state.tool_preview_scroll,
62            formatter,
63            palette,
64        );
65        app.state
66            .set_tool_preview_max_scroll(result.tool_preview_max_scroll);
67    }
68    let frozen_len = lines.len();
69    push_streaming_preview(&mut lines, &app.state, separator_width, formatter);
70    app.state
71        .store_message_lines_with_frozen(lines.clone(), max_width, frozen_len);
72    lines
73}