mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
feat(tui/limits): refresh usage header and timing copy
This commit is contained in:
@@ -10,24 +10,68 @@ pub fn format_elapsed(start_time: Instant) -> String {
|
||||
/// Convert a [`std::time::Duration`] into a human-readable, compact string.
|
||||
///
|
||||
/// Formatting rules:
|
||||
/// * < 1 s -> "{milli}ms"
|
||||
/// * < 60 s -> "{sec:.2}s" (two decimal places)
|
||||
/// * >= 60 s -> "{min}m {sec:02}s"
|
||||
/// * < 1 s -> "{milli}ms"
|
||||
/// * < 60 s -> "{sec:.2}s" (two decimal places)
|
||||
/// * < 60 m -> "{min}m {sec:02}s"
|
||||
/// * < 24 h -> "{hour}h {minute:02}m" (rounded to the nearest minute)
|
||||
/// * >= 24 h -> "{day}d {hour:02}h" (rounded to the nearest hour)
|
||||
pub fn format_duration(duration: Duration) -> String {
|
||||
let millis = duration.as_millis() as i64;
|
||||
format_elapsed_millis(millis)
|
||||
let millis = duration.as_millis();
|
||||
if millis < 1_000 {
|
||||
return format!("{millis}ms");
|
||||
}
|
||||
|
||||
let secs = duration.as_secs();
|
||||
if secs < 60 {
|
||||
return format!("{:.2}s", duration.as_secs_f64());
|
||||
}
|
||||
|
||||
if secs < 3_600 {
|
||||
return format_minutes_seconds(duration);
|
||||
}
|
||||
|
||||
if secs < 86_400 {
|
||||
return format_hours_minutes(duration);
|
||||
}
|
||||
|
||||
format_days_hours(duration)
|
||||
}
|
||||
|
||||
fn format_elapsed_millis(millis: i64) -> String {
|
||||
if millis < 1000 {
|
||||
format!("{millis}ms")
|
||||
} else if millis < 60_000 {
|
||||
format!("{:.2}s", millis as f64 / 1000.0)
|
||||
} else {
|
||||
let minutes = millis / 60_000;
|
||||
let seconds = (millis % 60_000) / 1000;
|
||||
format!("{minutes}m {seconds:02}s")
|
||||
fn format_minutes_seconds(duration: Duration) -> String {
|
||||
let total_seconds = duration.as_secs();
|
||||
let minutes = total_seconds / 60;
|
||||
let seconds = total_seconds % 60;
|
||||
format!("{minutes}m {seconds:02}s")
|
||||
}
|
||||
|
||||
fn format_hours_minutes(duration: Duration) -> String {
|
||||
let total_hours_f = duration.as_secs_f64() / 3_600.0;
|
||||
let mut hours = total_hours_f.floor() as u64;
|
||||
let mut minutes = ((total_hours_f - hours as f64) * 60.0).round() as u64;
|
||||
|
||||
if minutes == 60 {
|
||||
minutes = 0;
|
||||
hours += 1;
|
||||
}
|
||||
|
||||
if hours >= 24 {
|
||||
return format_days_hours(duration);
|
||||
}
|
||||
|
||||
format!("{hours}h {minutes:02}m")
|
||||
}
|
||||
|
||||
fn format_days_hours(duration: Duration) -> String {
|
||||
let total_hours_f = duration.as_secs_f64() / 3_600.0;
|
||||
let mut days = (total_hours_f / 24.0).floor() as u64;
|
||||
let mut hours = (total_hours_f - days as f64 * 24.0).round() as u64;
|
||||
|
||||
if hours == 24 {
|
||||
hours = 0;
|
||||
days += 1;
|
||||
}
|
||||
|
||||
format!("{days}d {hours:02}h")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -67,12 +111,24 @@ mod tests {
|
||||
assert_eq!(format_duration(dur_exact), "1m 00s");
|
||||
|
||||
let dur_long = Duration::from_millis(3_601_000);
|
||||
assert_eq!(format_duration(dur_long), "60m 01s");
|
||||
assert_eq!(format_duration(dur_long), "1h 00m");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_one_hour_has_space() {
|
||||
let dur_hour = Duration::from_millis(3_600_000);
|
||||
assert_eq!(format_duration(dur_hour), "60m 00s");
|
||||
assert_eq!(format_duration(dur_hour), "1h 00m");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_hours_rounds_minutes() {
|
||||
let dur = Duration::from_secs(4 * 3_600 + 58 * 60 + 40);
|
||||
assert_eq!(format_duration(dur), "4h 59m");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_days_rounds_hours() {
|
||||
let dur = Duration::from_secs(2 * 86_400 + 11 * 3_600 + 45 * 60);
|
||||
assert_eq!(format_duration(dur), "2d 12h");
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ expression: visual
|
||||
---
|
||||
[magenta]/limits[/]
|
||||
|
||||
[bold]Rate limit usage snapshot[/]
|
||||
[dim] Tip: run `/limits` right after Codex replies for freshest numbers.[/]
|
||||
[bold]Usage Limits[/]
|
||||
Real usage data is not available yet.
|
||||
[dim] Send a message to Codex, then run /limits again.[/]
|
||||
|
||||
+1
-2
@@ -4,8 +4,7 @@ expression: visual
|
||||
---
|
||||
[magenta]/limits[/]
|
||||
|
||||
[bold]Rate limit usage snapshot[/]
|
||||
[dim] Tip: run `/limits` right after Codex replies for freshest numbers.[/]
|
||||
[bold]Usage Limits[/]
|
||||
• Hourly limit[dim] (≈5 hours window)[/]: [dark-gray+bold]30.0% used[/]
|
||||
• Weekly limit[dim] (≈1 week window)[/]: [dark-gray+bold]60.0% used[/]
|
||||
[green] Within current limits[/]
|
||||
|
||||
+1
-2
@@ -4,8 +4,7 @@ expression: visual
|
||||
---
|
||||
[magenta]/limits[/]
|
||||
|
||||
[bold]Rate limit usage snapshot[/]
|
||||
[dim] Tip: run `/limits` right after Codex replies for freshest numbers.[/]
|
||||
[bold]Usage Limits[/]
|
||||
• Hourly limit[dim] (≈5 hours window)[/]: [dark-gray+bold]0.0% used[/]
|
||||
• Weekly limit[dim] (≈1 week window)[/]: [dark-gray+bold]20.0% used[/]
|
||||
[green] Within current limits[/]
|
||||
|
||||
+1
-2
@@ -4,8 +4,7 @@ expression: visual
|
||||
---
|
||||
[magenta]/limits[/]
|
||||
|
||||
[bold]Rate limit usage snapshot[/]
|
||||
[dim] Tip: run `/limits` right after Codex replies for freshest numbers.[/]
|
||||
[bold]Usage Limits[/]
|
||||
• Hourly limit[dim] (≈5 hours window)[/]: [dark-gray+bold]20.0% used[/]
|
||||
• Weekly limit[dim] (≈1 week window)[/]: [dark-gray+bold]20.0% used[/]
|
||||
[green] Within current limits[/]
|
||||
|
||||
+1
-2
@@ -4,8 +4,7 @@ expression: visual
|
||||
---
|
||||
[magenta]/limits[/]
|
||||
|
||||
[bold]Rate limit usage snapshot[/]
|
||||
[dim] Tip: run `/limits` right after Codex replies for freshest numbers.[/]
|
||||
[bold]Usage Limits[/]
|
||||
• Hourly limit[dim] (≈5 hours window)[/]: [dark-gray+bold]98.0% used[/]
|
||||
• Weekly limit[dim] (≈1 week window)[/]: [dark-gray+bold]0.0% used[/]
|
||||
[green] Within current limits[/]
|
||||
|
||||
@@ -181,7 +181,7 @@ fn build_summary_lines(
|
||||
let mut lines: Vec<Line<'static>> = vec![
|
||||
"/limits".magenta().into(),
|
||||
"".into(),
|
||||
vec![" Tip: run `/limits` right after Codex replies for freshest numbers.".dim()].into(),
|
||||
vec!["Usage Limits".bold()].into(),
|
||||
build_usage_line(
|
||||
" • Hourly limit",
|
||||
&metrics.hourly_window_label,
|
||||
@@ -336,12 +336,12 @@ fn build_reset_line(
|
||||
let last = last_reset?;
|
||||
let now = Utc::now();
|
||||
let since = now.signed_duration_since(last).to_std().ok()?;
|
||||
let mut text = format!(" • {label} window reset ~{} ago", format_duration(since));
|
||||
let mut text = format!(" • {label} window started ~{} ago", format_duration(since));
|
||||
|
||||
let next = last + chrono::Duration::minutes(window_minutes as i64);
|
||||
if let Ok(until) = next.signed_duration_since(now).to_std() {
|
||||
if until.as_secs() > 0 {
|
||||
text.push_str(&format!(" (next ≈ {})", format_duration(until)));
|
||||
text.push_str(&format!(" (resets in ≈ {})", format_duration(until)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user