Add spinner animation to TUI status indicator (#1917)
## Summary - add a pulsing dot loader before the shimmering `Working` label in the status indicator widget and include a small test asserting the spinner character is rendered - also fix a small bug in the ran command header by adding a space between the ⚡ and `Ran command` https://github.com/user-attachments/assets/6768c9d2-e094-49cb-ad51-44bcac10aa6f ## Testing - `just fmt` - `just fix` *(failed: E0658 `let` expressions in core/src/client.rs)* - `cargo test --all-features` *(failed: E0658 `let` expressions in core/src/client.rs)* ------ https://chatgpt.com/codex/tasks/task_i_68941bffdb948322b0f4190bc9dbe7f6 --------- Co-authored-by: aibrahim-oai <aibrahim@openai.com>
This commit is contained in:
@@ -262,7 +262,7 @@ impl HistoryCell {
|
|||||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||||
let command_escaped = strip_bash_lc_and_escape(&command);
|
let command_escaped = strip_bash_lc_and_escape(&command);
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
"⚡Ran command ".magenta(),
|
"⚡ Ran command ".magenta(),
|
||||||
command_escaped.into(),
|
command_escaped.into(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
@@ -556,7 +556,7 @@ impl HistoryCell {
|
|||||||
let mut header: Vec<Span> = Vec::new();
|
let mut header: Vec<Span> = Vec::new();
|
||||||
header.push(Span::raw("📋"));
|
header.push(Span::raw("📋"));
|
||||||
header.push(Span::styled(
|
header.push(Span::styled(
|
||||||
"Updated",
|
" Updated",
|
||||||
Style::default().add_modifier(Modifier::BOLD).magenta(),
|
Style::default().add_modifier(Modifier::BOLD).magenta(),
|
||||||
));
|
));
|
||||||
header.push(Span::raw(" to do list ["));
|
header.push(Span::raw(" to do list ["));
|
||||||
|
|||||||
@@ -213,9 +213,20 @@ impl WidgetRef for StatusIndicatorWidget {
|
|||||||
// Plain rendering: no borders or padding so the live cell is visually indistinguishable from terminal scrollback.
|
// Plain rendering: no borders or padding so the live cell is visually indistinguishable from terminal scrollback.
|
||||||
let inner_width = area.width as usize;
|
let inner_width = area.width as usize;
|
||||||
|
|
||||||
// Compose a single status line like: "▌ Working (Xs • Ctrl c to interrupt) <logs>"
|
|
||||||
let mut spans: Vec<Span<'static>> = Vec::new();
|
let mut spans: Vec<Span<'static>> = Vec::new();
|
||||||
spans.push(Span::styled("▌ ", Style::default().fg(Color::Cyan)));
|
spans.push(Span::styled("▌ ", Style::default().fg(Color::Cyan)));
|
||||||
|
|
||||||
|
// Simple dim spinner to the left of the header.
|
||||||
|
let spinner_frames = ['·', '•', '●', '•'];
|
||||||
|
const SPINNER_SLOWDOWN: usize = 2;
|
||||||
|
let spinner_ch = spinner_frames[(idx / SPINNER_SLOWDOWN) % spinner_frames.len()];
|
||||||
|
spans.push(Span::styled(
|
||||||
|
spinner_ch.to_string(),
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
));
|
||||||
|
spans.push(Span::raw(" "));
|
||||||
|
|
||||||
|
// Space after header
|
||||||
// Animated header after the left bar
|
// Animated header after the left bar
|
||||||
spans.extend(animated_spans);
|
spans.extend(animated_spans);
|
||||||
// Space between header and bracket block
|
// Space between header and bracket block
|
||||||
@@ -324,4 +335,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert!(row.contains("Working"), "expected Working header: {row:?}");
|
assert!(row.contains("Working"), "expected Working header: {row:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spinner_is_rendered() {
|
||||||
|
let (tx_raw, _rx) = channel::<AppEvent>();
|
||||||
|
let tx = AppEventSender::new(tx_raw);
|
||||||
|
let mut w = StatusIndicatorWidget::new(tx);
|
||||||
|
w.restart_with_text("Hello".to_string());
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(120));
|
||||||
|
|
||||||
|
let area = ratatui::layout::Rect::new(0, 0, 30, 1);
|
||||||
|
let mut buf = ratatui::buffer::Buffer::empty(area);
|
||||||
|
w.render_ref(area, &mut buf);
|
||||||
|
|
||||||
|
let ch = buf[(2, 0)].symbol().chars().next().unwrap_or(' ');
|
||||||
|
assert!(
|
||||||
|
matches!(ch, '·' | '•' | '●'),
|
||||||
|
"expected spinner char at col 2: {ch:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user