/status followup (#4304)

- Render `send a message to load usage data` in the beginning of the
session
- Render `data not available yet` if received no rate limits 
- nit case
- Deleted stall snapshots that were moved to
`codex-rs/tui/src/status/snapshots`
This commit is contained in:
Ahmed Ibrahim
2025-09-26 11:16:54 -07:00
committed by GitHub
parent d3f6f6629b
commit 1fba99ed85
12 changed files with 82 additions and 70 deletions

View File

@@ -559,10 +559,6 @@ fn parse_rate_limit_snapshot(headers: &HeaderMap) -> Option<RateLimitSnapshot> {
"x-codex-secondary-reset-after-seconds", "x-codex-secondary-reset-after-seconds",
); );
if primary.is_none() && secondary.is_none() {
return None;
}
Some(RateLimitSnapshot { primary, secondary }) Some(RateLimitSnapshot { primary, secondary })
} }

View File

@@ -1,19 +0,0 @@
---
source: tui/src/status.rs
expression: sanitized
---
/status
╭──────────────────────────────────────────────────────────────────╮
│ >_ OpenAI Codex (v0.0.0) │
│ │
│ Model : gpt-5-codex (reasoning high, summaries detailed) │
│ Directory : /workspace/tests │
│ Approval : on-request │
│ Sandbox : workspace-write │
│ Agents.md : <none> │
│ │
│ Token Usage : 1.9K total (1K input + 900 output) │
│ 5h limit : [███████████████░░░░░] 72% used · resets 03:14 │
│ Weekly limit : [█████████░░░░░░░░░░░] 45% used · resets 03:24 │
╰──────────────────────────────────────────────────────────────────╯

View File

@@ -1,19 +0,0 @@
---
source: tui/src/status.rs
expression: sanitized
---
/status
╭────────────────────────────────────────────╮
│ >_ OpenAI Codex (v0.0.0) │
│ │
│ Model : gpt-5-codex (reasoning high │
│ Directory : /workspace/tests │
│ Approval : on-request │
│ Sandbox : read-only │
│ Agents.md : <none> │
│ │
│ Token Usage : 1.9K total (1K input + 900 │
│ 5h limit : [███████████████░░░░░] 72% │
│ · resets 03:14 │
╰────────────────────────────────────────────╯

View File

@@ -23,7 +23,6 @@ use super::helpers::compose_agents_summary;
use super::helpers::compose_model_display; use super::helpers::compose_model_display;
use super::helpers::format_directory_display; use super::helpers::format_directory_display;
use super::helpers::format_tokens_compact; use super::helpers::format_tokens_compact;
use super::rate_limits::RESET_BULLET;
use super::rate_limits::RateLimitSnapshotDisplay; use super::rate_limits::RateLimitSnapshotDisplay;
use super::rate_limits::StatusRateLimitData; use super::rate_limits::StatusRateLimitData;
use super::rate_limits::compose_rate_limit_data; use super::rate_limits::compose_rate_limit_data;
@@ -149,8 +148,7 @@ impl StatusHistoryCell {
let base_line = Line::from(base_spans.clone()); let base_line = Line::from(base_spans.clone());
if let Some(resets_at) = row.resets_at.as_ref() { if let Some(resets_at) = row.resets_at.as_ref() {
let resets_span = let resets_span = Span::from(format!("(resets {resets_at})")).dim();
Span::from(format!("{RESET_BULLET} resets {resets_at}")).dim();
let mut inline_spans = base_spans.clone(); let mut inline_spans = base_spans.clone();
inline_spans.push(Span::from(" ").dim()); inline_spans.push(Span::from(" ").dim());
inline_spans.push(resets_span.clone()); inline_spans.push(resets_span.clone());
@@ -171,7 +169,10 @@ impl StatusHistoryCell {
lines lines
} }
StatusRateLimitData::Missing => { StatusRateLimitData::Missing => {
vec![formatter.line("Limits", vec![Span::from("data not available yet").dim()])] vec![formatter.line(
"Limits",
vec![Span::from("send a message to load usage data").dim()],
)]
} }
} }
} }
@@ -233,7 +234,7 @@ impl HistoryCell for StatusHistoryCell {
if self.session_id.is_some() { if self.session_id.is_some() {
push_label(&mut labels, &mut seen, "Session"); push_label(&mut labels, &mut seen, "Session");
} }
push_label(&mut labels, &mut seen, "Token Usage"); push_label(&mut labels, &mut seen, "Token usage");
self.collect_rate_limit_labels(&mut seen, &mut labels); self.collect_rate_limit_labels(&mut seen, &mut labels);
let formatter = FieldFormatter::from_labels(labels.iter().map(String::as_str)); let formatter = FieldFormatter::from_labels(labels.iter().map(String::as_str));
@@ -263,7 +264,7 @@ impl HistoryCell for StatusHistoryCell {
} }
lines.push(Line::from(Vec::<Span<'static>>::new())); lines.push(Line::from(Vec::<Span<'static>>::new()));
lines.push(formatter.line("Token Usage", self.token_usage_spans())); lines.push(formatter.line("Token usage", self.token_usage_spans()));
lines.extend(self.rate_limit_lines(available_inner_width, &formatter)); lines.extend(self.rate_limit_lines(available_inner_width, &formatter));

View File

@@ -162,7 +162,7 @@ pub(crate) fn format_reset_timestamp(dt: DateTime<Local>, captured_at: DateTime<
if dt.date_naive() == captured_at.date_naive() { if dt.date_naive() == captured_at.date_naive() {
time time
} else { } else {
format!("{} ({time})", dt.format("%-d %b")) format!("{time} on {}", dt.format("%-d %b"))
} }
} }

View File

@@ -11,7 +11,6 @@ use std::convert::TryFrom;
const STATUS_LIMIT_BAR_SEGMENTS: usize = 20; const STATUS_LIMIT_BAR_SEGMENTS: usize = 20;
const STATUS_LIMIT_BAR_FILLED: &str = ""; const STATUS_LIMIT_BAR_FILLED: &str = "";
const STATUS_LIMIT_BAR_EMPTY: &str = ""; const STATUS_LIMIT_BAR_EMPTY: &str = "";
pub(crate) const RESET_BULLET: &str = "·";
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct StatusRateLimitRow { pub(crate) struct StatusRateLimitRow {
@@ -105,7 +104,7 @@ pub(crate) fn compose_rate_limit_data(
} }
if rows.is_empty() { if rows.is_empty() {
StatusRateLimitData::Missing StatusRateLimitData::Available(vec![])
} else { } else {
StatusRateLimitData::Available(rows) StatusRateLimitData::Available(rows)
} }

View File

@@ -4,15 +4,15 @@ expression: sanitized
--- ---
/status /status
╭──────────────────────────────────────────────────────────────────────────╮ ╭──────────────────────────────────────────────────────────────────────────
│ >_ OpenAI Codex (v0.0.0) │ │ >_ OpenAI Codex (v0.0.0)
│ │
│ Model: gpt-5-codex (reasoning none, summaries auto) │ │ Model: gpt-5-codex (reasoning none, summaries auto)
│ Directory: [[workspace]] │ │ Directory: [[workspace]]
│ Approval: on-request │ │ Approval: on-request
│ Sandbox: read-only │ │ Sandbox: read-only
│ Agents.md: <none> │ │ Agents.md: <none>
│ │
│ Token Usage: 1.2K total (800 input + 400 output) │ │ Token usage: 1.2K total (800 input + 400 output)
│ Monthly limit: [██░░░░░░░░░░░░░░░░░░] 12% used · resets 7 May (07:08) │ │ Monthly limit: [██░░░░░░░░░░░░░░░░░░] 12% used (resets 07:08 on 7 May) │
╰──────────────────────────────────────────────────────────────────────────╯ ╰──────────────────────────────────────────────────────────────────────────

View File

@@ -13,7 +13,7 @@ expression: sanitized
│ Sandbox: workspace-write │ │ Sandbox: workspace-write │
│ Agents.md: <none> │ │ Agents.md: <none> │
│ │ │ │
│ Token Usage: 1.9K total (1K input + 900 output) │ │ Token usage: 1.9K total (1K input + 900 output) │
│ 5h limit: [███████████████░░░░░] 72% used · resets 03:14 │ │ 5h limit: [███████████████░░░░░] 72% used (resets 03:14)
│ Weekly limit: [█████████░░░░░░░░░░░] 45% used · resets 03:24 │ │ Weekly limit: [█████████░░░░░░░░░░░] 45% used (resets 03:24)
╰───────────────────────────────────────────────────────────────────╯ ╰───────────────────────────────────────────────────────────────────╯

View File

@@ -0,0 +1,18 @@
---
source: tui/src/status/tests.rs
expression: sanitized
---
/status
╭──────────────────────────────────────────────────────────────╮
│ >_ OpenAI Codex (v0.0.0) │
│ │
│ Model: gpt-5-codex (reasoning none, summaries auto) │
│ Directory: [[workspace]] │
│ Approval: on-request │
│ Sandbox: read-only │
│ Agents.md: <none> │
│ │
│ Token usage: 750 total (500 input + 250 output) │
│ Limits: data not available yet │
╰──────────────────────────────────────────────────────────────╯

View File

@@ -13,6 +13,6 @@ expression: sanitized
│ Sandbox: read-only │ │ Sandbox: read-only │
│ Agents.md: <none> │ │ Agents.md: <none> │
│ │ │ │
│ Token Usage: 750 total (500 input + 250 output) │ │ Token usage: 750 total (500 input + 250 output) │
│ Limits: data not available yet │ Limits: send a message to load usage data
╰──────────────────────────────────────────────────────────────╯ ╰──────────────────────────────────────────────────────────────╯

View File

@@ -13,7 +13,7 @@ expression: sanitized
│ Sandbox: read-only │ │ Sandbox: read-only │
│ Agents.md: <none> │ │ Agents.md: <none> │
│ │ │ │
│ Token Usage: 1.9K total (1K input + 90 │ │ Token usage: 1.9K total (1K input + 90 │
│ 5h limit: [███████████████░░░░░] 72% │ │ 5h limit: [███████████████░░░░░] 72% │
· resets 03:14 │ (resets 03:14)
╰────────────────────────────────────────────╯ ╰────────────────────────────────────────────╯

View File

@@ -248,3 +248,39 @@ fn status_snapshot_shows_missing_limits_message() {
let sanitized = sanitize_directory(rendered_lines).join("\n"); let sanitized = sanitize_directory(rendered_lines).join("\n");
assert_snapshot!(sanitized); assert_snapshot!(sanitized);
} }
#[test]
fn status_snapshot_shows_empty_limits_message() {
let temp_home = TempDir::new().expect("temp home");
let mut config = test_config(&temp_home);
config.model = "gpt-5-codex".to_string();
config.cwd = PathBuf::from("/workspace/tests");
let usage = TokenUsage {
input_tokens: 500,
cached_input_tokens: 0,
output_tokens: 250,
reasoning_output_tokens: 0,
total_tokens: 750,
};
let snapshot = RateLimitSnapshot {
primary: None,
secondary: None,
};
let captured_at = chrono::Local
.with_ymd_and_hms(2024, 6, 7, 8, 9, 10)
.single()
.expect("timestamp");
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let composite = new_status_output(&config, &usage, &None, Some(&rate_display));
let mut rendered_lines = render_lines(&composite.display_lines(80));
if cfg!(windows) {
for line in &mut rendered_lines {
*line = line.replace('\\', "/");
}
}
let sanitized = sanitize_directory(rendered_lines).join("\n");
assert_snapshot!(sanitized);
}