diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index aedea488..e8aca68f 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -559,10 +559,6 @@ fn parse_rate_limit_snapshot(headers: &HeaderMap) -> Option { "x-codex-secondary-reset-after-seconds", ); - if primary.is_none() && secondary.is_none() { - return None; - } - Some(RateLimitSnapshot { primary, secondary }) } diff --git a/codex-rs/tui/src/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap b/codex-rs/tui/src/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap deleted file mode 100644 index 1615305f..00000000 --- a/codex-rs/tui/src/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap +++ /dev/null @@ -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 : │ -│ │ -│ Token Usage : 1.9K total (1K input + 900 output) │ -│ 5h limit : [███████████████░░░░░] 72% used · resets 03:14 │ -│ Weekly limit : [█████████░░░░░░░░░░░] 45% used · resets 03:24 │ -╰──────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/codex-rs/tui/src/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap deleted file mode 100644 index 44cad152..00000000 --- a/codex-rs/tui/src/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ /dev/null @@ -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 : │ -│ │ -│ Token Usage : 1.9K total (1K input + 900 │ -│ 5h limit : [███████████████░░░░░] 72% │ -│ · resets 03:14 │ -╰────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/card.rs b/codex-rs/tui/src/status/card.rs index 8f7188d5..d983ba27 100644 --- a/codex-rs/tui/src/status/card.rs +++ b/codex-rs/tui/src/status/card.rs @@ -23,7 +23,6 @@ use super::helpers::compose_agents_summary; use super::helpers::compose_model_display; use super::helpers::format_directory_display; use super::helpers::format_tokens_compact; -use super::rate_limits::RESET_BULLET; use super::rate_limits::RateLimitSnapshotDisplay; use super::rate_limits::StatusRateLimitData; use super::rate_limits::compose_rate_limit_data; @@ -149,8 +148,7 @@ impl StatusHistoryCell { let base_line = Line::from(base_spans.clone()); if let Some(resets_at) = row.resets_at.as_ref() { - let resets_span = - Span::from(format!("{RESET_BULLET} resets {resets_at}")).dim(); + let resets_span = Span::from(format!("(resets {resets_at})")).dim(); let mut inline_spans = base_spans.clone(); inline_spans.push(Span::from(" ").dim()); inline_spans.push(resets_span.clone()); @@ -171,7 +169,10 @@ impl StatusHistoryCell { lines } 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() { 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); let formatter = FieldFormatter::from_labels(labels.iter().map(String::as_str)); @@ -263,7 +264,7 @@ impl HistoryCell for StatusHistoryCell { } lines.push(Line::from(Vec::>::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)); diff --git a/codex-rs/tui/src/status/helpers.rs b/codex-rs/tui/src/status/helpers.rs index 46185e1c..43dfd820 100644 --- a/codex-rs/tui/src/status/helpers.rs +++ b/codex-rs/tui/src/status/helpers.rs @@ -162,7 +162,7 @@ pub(crate) fn format_reset_timestamp(dt: DateTime, captured_at: DateTime< if dt.date_naive() == captured_at.date_naive() { time } else { - format!("{} ({time})", dt.format("%-d %b")) + format!("{time} on {}", dt.format("%-d %b")) } } diff --git a/codex-rs/tui/src/status/rate_limits.rs b/codex-rs/tui/src/status/rate_limits.rs index 8da3a1c6..875001ef 100644 --- a/codex-rs/tui/src/status/rate_limits.rs +++ b/codex-rs/tui/src/status/rate_limits.rs @@ -11,7 +11,6 @@ use std::convert::TryFrom; const STATUS_LIMIT_BAR_SEGMENTS: usize = 20; const STATUS_LIMIT_BAR_FILLED: &str = "█"; const STATUS_LIMIT_BAR_EMPTY: &str = "░"; -pub(crate) const RESET_BULLET: &str = "·"; #[derive(Debug, Clone)] pub(crate) struct StatusRateLimitRow { @@ -105,7 +104,7 @@ pub(crate) fn compose_rate_limit_data( } if rows.is_empty() { - StatusRateLimitData::Missing + StatusRateLimitData::Available(vec![]) } else { StatusRateLimitData::Available(rows) } diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap index 25fd9c8a..67ed88cc 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_monthly_limit.snap @@ -4,15 +4,15 @@ 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: │ -│ │ -│ Token Usage: 1.2K total (800 input + 400 output) │ -│ Monthly limit: [██░░░░░░░░░░░░░░░░░░] 12% used · resets 7 May (07:08) │ -╰──────────────────────────────────────────────────────────────────────────╯ +╭───────────────────────────────────────────────────────────────────────────╮ +│ >_ OpenAI Codex (v0.0.0) │ +│ │ +│ Model: gpt-5-codex (reasoning none, summaries auto) │ +│ Directory: [[workspace]] │ +│ Approval: on-request │ +│ Sandbox: read-only │ +│ Agents.md: │ +│ │ +│ Token usage: 1.2K total (800 input + 400 output) │ +│ Monthly limit: [██░░░░░░░░░░░░░░░░░░] 12% used (resets 07:08 on 7 May) │ +╰───────────────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap index fc929e04..c445d116 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_reasoning_details.snap @@ -13,7 +13,7 @@ expression: sanitized │ Sandbox: workspace-write │ │ Agents.md: │ │ │ -│ Token Usage: 1.9K total (1K input + 900 output) │ -│ 5h limit: [███████████████░░░░░] 72% used · resets 03:14 │ -│ Weekly limit: [█████████░░░░░░░░░░░] 45% used · resets 03:24 │ +│ Token usage: 1.9K total (1K input + 900 output) │ +│ 5h limit: [███████████████░░░░░] 72% used (resets 03:14) │ +│ Weekly limit: [█████████░░░░░░░░░░░] 45% used (resets 03:24) │ ╰───────────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap new file mode 100644 index 00000000..2766f070 --- /dev/null +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_empty_limits_message.snap @@ -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: │ +│ │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Limits: data not available yet │ +╰──────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap index eabcc2a4..21627522 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_shows_missing_limits_message.snap @@ -13,6 +13,6 @@ expression: sanitized │ Sandbox: read-only │ │ Agents.md: │ │ │ -│ Token Usage: 750 total (500 input + 250 output) │ -│ Limits: data not available yet │ +│ Token usage: 750 total (500 input + 250 output) │ +│ Limits: send a message to load usage data │ ╰──────────────────────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap index 9f7e1a45..a943c852 100644 --- a/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap +++ b/codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_truncates_in_narrow_terminal.snap @@ -13,7 +13,7 @@ expression: sanitized │ Sandbox: read-only │ │ Agents.md: │ │ │ -│ Token Usage: 1.9K total (1K input + 90 │ +│ Token usage: 1.9K total (1K input + 90 │ │ 5h limit: [███████████████░░░░░] 72% │ -│ · resets 03:14 │ +│ (resets 03:14) │ ╰────────────────────────────────────────────╯ diff --git a/codex-rs/tui/src/status/tests.rs b/codex-rs/tui/src/status/tests.rs index d583a5e5..b095819d 100644 --- a/codex-rs/tui/src/status/tests.rs +++ b/codex-rs/tui/src/status/tests.rs @@ -248,3 +248,39 @@ fn status_snapshot_shows_missing_limits_message() { let sanitized = sanitize_directory(rendered_lines).join("\n"); 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); +}