feat(tui): show minutes/hours in thinking timer (#3220)
What - Show compact elapsed time in the TUI status indicator: Xs, MmSSs, HhMMmSSs. - Add private helper fmt_elapsed_compact with a unit test. Why - Seconds‑only becomes hard to read during longer runs; minutes/hours improve clarity without extra noise. How - Implemented in codex-rs/tui/src/status_indicator_widget.rs only. - The helper is used when rendering the existing “Working/Thinking” timer. - No changes to codex-common::elapsed::format_duration or other crates. Scope/Impact - TUI‑only; no public API changes; minimal risk. - Snapshot tests should remain unchanged (most show “0s”). Before/After - Working (65s • Esc to interrupt) → Working (1m05s • Esc to interrupt) - Working (3723s • …) → Working (1h02m03s • …) Tests - Unit: fmt_elapsed_compact_formats_seconds_minutes_hours. - Local checks: cargo fmt --all, cargo clippy -p codex-tui -- -D warnings, cargo test -p codex-tui. Notes - Open to adjusting the exact format or moving the helper if maintainers prefer a shared location. Signed-off-by: Enrique Moreno Tent <enriquemorenotent@gmail.com>
This commit is contained in:
committed by
GitHub
parent
17a80d43c8
commit
6cfc012e9d
@@ -31,6 +31,23 @@ pub(crate) struct StatusIndicatorWidget {
|
||||
frame_requester: FrameRequester,
|
||||
}
|
||||
|
||||
// Format elapsed seconds into a compact human-friendly form used by the status line.
|
||||
// Examples: 0s, 59s, 1m00s, 59m59s, 1h00m00s, 2h03m09s
|
||||
fn fmt_elapsed_compact(elapsed_secs: u64) -> String {
|
||||
if elapsed_secs < 60 {
|
||||
return format!("{elapsed_secs}s");
|
||||
}
|
||||
if elapsed_secs < 3600 {
|
||||
let minutes = elapsed_secs / 60;
|
||||
let seconds = elapsed_secs % 60;
|
||||
return format!("{minutes}m{seconds:02}s");
|
||||
}
|
||||
let hours = elapsed_secs / 3600;
|
||||
let minutes = (elapsed_secs % 3600) / 60;
|
||||
let seconds = elapsed_secs % 60;
|
||||
format!("{hours}h{minutes:02}m{seconds:02}s")
|
||||
}
|
||||
|
||||
impl StatusIndicatorWidget {
|
||||
pub(crate) fn new(app_event_tx: AppEventSender, frame_requester: FrameRequester) -> Self {
|
||||
Self {
|
||||
@@ -136,13 +153,14 @@ impl WidgetRef for StatusIndicatorWidget {
|
||||
self.frame_requester
|
||||
.schedule_frame_in(Duration::from_millis(32));
|
||||
let elapsed = self.elapsed_seconds();
|
||||
let pretty_elapsed = fmt_elapsed_compact(elapsed);
|
||||
|
||||
// Plain rendering: no borders or padding so the live cell is visually indistinguishable from terminal scrollback.
|
||||
let mut spans = vec![" ".into()];
|
||||
spans.extend(shimmer_spans(&self.header));
|
||||
spans.extend(vec![
|
||||
" ".into(),
|
||||
format!("({elapsed}s • ").dim(),
|
||||
format!("({pretty_elapsed} • ").dim(),
|
||||
"Esc".dim().bold(),
|
||||
" to interrupt)".dim(),
|
||||
]);
|
||||
@@ -184,6 +202,22 @@ mod tests {
|
||||
use std::time::Instant;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn fmt_elapsed_compact_formats_seconds_minutes_hours() {
|
||||
assert_eq!(fmt_elapsed_compact(0), "0s");
|
||||
assert_eq!(fmt_elapsed_compact(1), "1s");
|
||||
assert_eq!(fmt_elapsed_compact(59), "59s");
|
||||
assert_eq!(fmt_elapsed_compact(60), "1m00s");
|
||||
assert_eq!(fmt_elapsed_compact(61), "1m01s");
|
||||
assert_eq!(fmt_elapsed_compact(3 * 60 + 5), "3m05s");
|
||||
assert_eq!(fmt_elapsed_compact(59 * 60 + 59), "59m59s");
|
||||
assert_eq!(fmt_elapsed_compact(3600), "1h00m00s");
|
||||
assert_eq!(fmt_elapsed_compact(3600 + 60 + 1), "1h01m01s");
|
||||
assert_eq!(fmt_elapsed_compact(25 * 3600 + 2 * 60 + 3), "25h02m03s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renders_with_working_header() {
|
||||
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
||||
|
||||
Reference in New Issue
Block a user