Skip to main content

codetether_agent/tui/ui/chat_view/
status.rs

1//! Bottom status-line span assembly.
2//!
3//! [`build_status_lines`] collects keybinding hints, session label,
4//! auto-apply badge, bus status, latency, token usage, and status
5//! text into one or more [`Line`]s for the status bar. When the
6//! terminal is too narrow to show everything on one row, the
7//! content is stacked across three lines.
8
9use ratatui::text::{Line, Span};
10
11use crate::tui::app::state::App;
12use crate::tui::ui::status_bar::{bus_status_badge_span, latency_badge_spans};
13
14use super::auto_apply::auto_apply_spans;
15use super::images_badge::pending_images_badge;
16use super::status_hints::{keybinding_spans, session_label_spans};
17use super::status_text::status_text_span;
18use super::token_spans::push_token_spans;
19
20/// Width below which the status bar stacks across multiple rows.
21pub const STACK_WIDTH_THRESHOLD: u16 = 180;
22
23/// Assemble the status bar as one or more lines.
24///
25/// Returns a single line when `width >= STACK_WIDTH_THRESHOLD`,
26/// otherwise stacks the content across three lines:
27/// 1. keybinding hints
28/// 2. session / auto-apply / bus / images / latency badges
29/// 3. token usage / status text
30///
31/// # Examples
32///
33/// ```rust,no_run
34/// use codetether_agent::tui::ui::chat_view::status::build_status_lines;
35/// # fn demo(app: &codetether_agent::tui::app::state::App) {
36/// let lines = build_status_lines(app, "test-session", 120);
37/// assert!(!lines.is_empty());
38/// # }
39/// ```
40pub fn build_status_lines(app: &App, session_label: &str, width: u16) -> Vec<Line<'static>> {
41    let hints = keybinding_spans();
42    let badges = badge_spans(app, session_label);
43    let metrics = metric_spans(app);
44
45    if width >= STACK_WIDTH_THRESHOLD {
46        let mut combined = hints;
47        combined.push(Span::raw(" | "));
48        combined.extend(badges);
49        combined.push(Span::raw(" | "));
50        combined.extend(metrics);
51        vec![Line::from(combined)]
52    } else {
53        vec![Line::from(hints), Line::from(badges), Line::from(metrics)]
54    }
55}
56
57/// Assemble all status-bar spans in display order as a single flat row.
58///
59/// Retained for backward compatibility; prefer [`build_status_lines`].
60///
61/// # Examples
62///
63/// ```rust,no_run
64/// use codetether_agent::tui::ui::chat_view::status::build_status_spans;
65/// # fn demo(app: &codetether_agent::tui::app::state::App) {
66/// let spans = build_status_spans(app, "test-session");
67/// assert!(!spans.is_empty());
68/// # }
69/// ```
70pub fn build_status_spans(app: &App, session_label: &str) -> Vec<Span<'static>> {
71    let mut spans = keybinding_spans();
72    spans.push(Span::raw(" | "));
73    spans.extend(badge_spans(app, session_label));
74    spans.push(Span::raw(" | "));
75    spans.extend(metric_spans(app));
76    spans
77}
78
79fn badge_spans(app: &App, session_label: &str) -> Vec<Span<'static>> {
80    let mut spans = session_label_spans(session_label);
81    spans.extend(auto_apply_spans(app));
82    spans.push(Span::raw(" | "));
83    spans.push(bus_status_badge_span(app));
84    if let Some(badge) = pending_images_badge(app) {
85        spans.push(Span::raw(" | "));
86        spans.push(badge);
87    }
88    if let Some(latency) = latency_badge_spans(app) {
89        spans.push(Span::raw(" | "));
90        spans.extend(latency);
91    }
92    spans
93}
94
95fn metric_spans(app: &App) -> Vec<Span<'static>> {
96    let mut spans = Vec::new();
97    push_token_spans(&mut spans);
98    spans.push(Span::raw(" | "));
99    spans.push(status_text_span(app));
100    spans
101}