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}