Skip to main content

codetether_agent/tui/ui/chat_view/
spinner.rs

1//! Animated spinner and elapsed-time formatting.
2//!
3//! Ported from the legacy TUI. [`current_spinner_frame`] cycles through
4//! braille-pattern glyphs every 100 ms. [`format_elapsed`] renders an
5//! [`Instant`] delta as `MmSS` or `S.s` seconds.
6
7const SPINNER: [&str; 10] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
8
9/// Return the current braille-spinner glyph based on wall-clock time.
10///
11/// # Examples
12///
13/// ```rust
14/// use codetether_agent::tui::ui::chat_view::spinner::current_spinner_frame;
15/// let frame = current_spinner_frame();
16/// assert!(frame.chars().count() >= 1);
17/// ```
18pub fn current_spinner_frame() -> &'static str {
19    let idx = (std::time::SystemTime::now()
20        .duration_since(std::time::UNIX_EPOCH)
21        .unwrap_or_default()
22        .as_millis()
23        / 100) as usize
24        % SPINNER.len();
25    SPINNER[idx]
26}
27
28/// Format the time elapsed since `started` as a human-readable string.
29///
30/// Returns ` MmSS` for durations ≥ 60 s, otherwise ` S.s`.
31///
32/// # Examples
33///
34/// ```rust
35/// use codetether_agent::tui::ui::chat_view::spinner::format_elapsed;
36/// // Instant::now() has ≈0 s elapsed, so format is " 0.xs"
37/// let s = format_elapsed(std::time::Instant::now());
38/// assert!(s.starts_with(' '));
39/// assert!(s.contains('s'));
40/// ```
41pub fn format_elapsed(started: std::time::Instant) -> String {
42    let elapsed = started.elapsed();
43    if elapsed.as_secs() >= 60 {
44        format!(" {}m{:02}s", elapsed.as_secs() / 60, elapsed.as_secs() % 60)
45    } else {
46        format!(" {:.1}s", elapsed.as_secs_f64())
47    }
48}