mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Remove context status-line meter (#17420)
Addresses #17313 Problem: The visual context meter in the status line was confusing and continued to draw negative feedback, and context reporting should remain an explicit opt-in rather than part of the default footer. Solution: Remove the visual meter, restore opt-in context remaining/used percentage items that explicitly say "Context", keep existing context-usage configs working as a hidden alias, and update the setup text and snapshots.
This commit is contained in:
committed by
GitHub
Unverified
parent
0393a485ed
commit
470510174b
@@ -324,8 +324,7 @@ pub struct Config {
|
||||
|
||||
/// Ordered list of status line item identifiers for the TUI.
|
||||
///
|
||||
/// When unset, the TUI defaults to: `model-with-reasoning`, `context-remaining`, and
|
||||
/// `current-dir`.
|
||||
/// When unset, the TUI defaults to: `model-with-reasoning` and `current-dir`.
|
||||
pub tui_status_line: Option<Vec<String>>,
|
||||
|
||||
/// Ordered list of terminal title item identifiers for the TUI.
|
||||
|
||||
+2
-2
@@ -13,9 +13,9 @@ expression: "render_lines(&view, 72)"
|
||||
[x] git-branch Current Git branch (omitted when unavaila…
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] project-root Project root directory (omitted when unav…
|
||||
[ ] context-usage Visual meter of context window usage (omi…
|
||||
[ ] context-remaining Percentage of context window remaining (o…
|
||||
[ ] context-used Percentage of context window used (omitte…
|
||||
[ ] five-hour-limit Remaining usage on 5-hour usage limit (om…
|
||||
[ ] weekly-limit Remaining usage on weekly usage limit (om…
|
||||
|
||||
gpt-5-codex · ~/codex-rs · jif/statusline-preview
|
||||
Use ↑↓ to navigate, ←→ to move, space to select, enter to confirm, esc
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! - Model information (name, reasoning level)
|
||||
//! - Directory paths (current dir, project root)
|
||||
//! - Git information (branch name)
|
||||
//! - Context usage (meter, window size)
|
||||
//! - Context usage (remaining %, used %, window size)
|
||||
//! - Usage limits (5-hour, weekly)
|
||||
//! - Session info (thread title, ID, tokens used)
|
||||
//! - Application version
|
||||
@@ -22,6 +22,7 @@ use ratatui::layout::Rect;
|
||||
use ratatui::text::Line;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::Display;
|
||||
use strum_macros::EnumIter;
|
||||
use strum_macros::EnumString;
|
||||
@@ -62,15 +63,14 @@ pub(crate) enum StatusLineItem {
|
||||
/// Current git branch name (if in a repository).
|
||||
GitBranch,
|
||||
|
||||
/// Visual meter of context window usage.
|
||||
/// Percentage of context window remaining.
|
||||
ContextRemaining,
|
||||
|
||||
/// Percentage of context window used.
|
||||
///
|
||||
/// Also accepts legacy `context-remaining` and `context-used` config values.
|
||||
#[strum(
|
||||
to_string = "context-usage",
|
||||
serialize = "context-remaining",
|
||||
serialize = "context-used"
|
||||
)]
|
||||
ContextUsage,
|
||||
/// Also accepts the legacy `context-usage` config value.
|
||||
#[strum(to_string = "context-used", serialize = "context-usage")]
|
||||
ContextUsed,
|
||||
|
||||
/// Remaining usage on the 5-hour rate limit.
|
||||
FiveHourLimit,
|
||||
@@ -112,8 +112,11 @@ impl StatusLineItem {
|
||||
StatusLineItem::CurrentDir => "Current working directory",
|
||||
StatusLineItem::ProjectRoot => "Project root directory (omitted when unavailable)",
|
||||
StatusLineItem::GitBranch => "Current Git branch (omitted when unavailable)",
|
||||
StatusLineItem::ContextUsage => {
|
||||
"Visual meter of context window usage (omitted when unknown)"
|
||||
StatusLineItem::ContextRemaining => {
|
||||
"Percentage of context window remaining (omitted when unknown)"
|
||||
}
|
||||
StatusLineItem::ContextUsed => {
|
||||
"Percentage of context window used (omitted when unknown)"
|
||||
}
|
||||
StatusLineItem::FiveHourLimit => {
|
||||
"Remaining usage on 5-hour usage limit (omitted when unavailable)"
|
||||
@@ -137,25 +140,6 @@ impl StatusLineItem {
|
||||
}
|
||||
}
|
||||
|
||||
const SELECTABLE_STATUS_LINE_ITEMS: &[StatusLineItem] = &[
|
||||
StatusLineItem::ModelName,
|
||||
StatusLineItem::ModelWithReasoning,
|
||||
StatusLineItem::CurrentDir,
|
||||
StatusLineItem::ProjectRoot,
|
||||
StatusLineItem::GitBranch,
|
||||
StatusLineItem::ContextUsage,
|
||||
StatusLineItem::FiveHourLimit,
|
||||
StatusLineItem::WeeklyLimit,
|
||||
StatusLineItem::CodexVersion,
|
||||
StatusLineItem::ContextWindowSize,
|
||||
StatusLineItem::UsedTokens,
|
||||
StatusLineItem::TotalInputTokens,
|
||||
StatusLineItem::TotalOutputTokens,
|
||||
StatusLineItem::SessionId,
|
||||
StatusLineItem::FastMode,
|
||||
StatusLineItem::ThreadTitle,
|
||||
];
|
||||
|
||||
/// Runtime values used to preview the current status-line selection.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub(crate) struct StatusLinePreviewData {
|
||||
@@ -232,7 +216,7 @@ impl StatusLineSetupView {
|
||||
}
|
||||
}
|
||||
|
||||
for item in SELECTABLE_STATUS_LINE_ITEMS.iter().cloned() {
|
||||
for item in StatusLineItem::iter() {
|
||||
let item_id = item.to_string();
|
||||
if used_ids.contains(&item_id) {
|
||||
continue;
|
||||
@@ -317,19 +301,27 @@ mod tests {
|
||||
use crate::app_event::AppEvent;
|
||||
|
||||
#[test]
|
||||
fn context_usage_is_canonical_and_accepts_legacy_ids() {
|
||||
assert_eq!(StatusLineItem::ContextUsage.to_string(), "context-usage");
|
||||
assert_eq!(
|
||||
"context-usage".parse::<StatusLineItem>(),
|
||||
Ok(StatusLineItem::ContextUsage)
|
||||
);
|
||||
assert_eq!(
|
||||
"context-remaining".parse::<StatusLineItem>(),
|
||||
Ok(StatusLineItem::ContextUsage)
|
||||
);
|
||||
fn context_used_accepts_context_usage_legacy_id() {
|
||||
assert_eq!(StatusLineItem::ContextUsed.to_string(), "context-used");
|
||||
assert_eq!(
|
||||
"context-used".parse::<StatusLineItem>(),
|
||||
Ok(StatusLineItem::ContextUsage)
|
||||
Ok(StatusLineItem::ContextUsed)
|
||||
);
|
||||
assert_eq!(
|
||||
"context-usage".parse::<StatusLineItem>(),
|
||||
Ok(StatusLineItem::ContextUsed)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_remaining_is_separate_selectable_id() {
|
||||
assert_eq!(
|
||||
"context-remaining".parse::<StatusLineItem>(),
|
||||
Ok(StatusLineItem::ContextRemaining)
|
||||
);
|
||||
assert_eq!(
|
||||
StatusLineItem::ContextRemaining.to_string(),
|
||||
"context-remaining"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ expression: normalized_backend_snapshot(terminal.backend())
|
||||
" "
|
||||
"› Ask Codex to do anything "
|
||||
" "
|
||||
" gpt-5.4 xhigh fast · Context [ ] · /tmp/project "
|
||||
" gpt-5.4 xhigh fast · Context 0% used · /tmp/project "
|
||||
|
||||
@@ -450,9 +450,12 @@ impl ChatWidget {
|
||||
Some(format!("{} used", format_tokens_compact(total)))
|
||||
}
|
||||
}
|
||||
StatusLineItem::ContextUsage => self
|
||||
StatusLineItem::ContextRemaining => self
|
||||
.status_line_context_remaining_percent()
|
||||
.map(|remaining| format!("Context {remaining}% left")),
|
||||
StatusLineItem::ContextUsed => self
|
||||
.status_line_context_used_percent()
|
||||
.map(format_context_used_meter),
|
||||
.map(|used| format!("Context {used}% used")),
|
||||
StatusLineItem::FiveHourLimit => {
|
||||
let window = self
|
||||
.rate_limit_snapshots_by_limit_id
|
||||
@@ -661,61 +664,3 @@ where
|
||||
}
|
||||
(items, invalid)
|
||||
}
|
||||
|
||||
fn format_context_used_meter(used_percent: i64) -> String {
|
||||
const METER_WIDTH: usize = 5;
|
||||
const EIGHTHS_PER_CELL: i64 = 8;
|
||||
const PARTIAL_BLOCKS: [&str; 8] = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
|
||||
|
||||
let used_percent = used_percent.clamp(0, 100);
|
||||
let total_eighths = (used_percent * METER_WIDTH as i64 * EIGHTHS_PER_CELL + 50) / 100;
|
||||
let filled_cells = (total_eighths / EIGHTHS_PER_CELL) as usize;
|
||||
let partial_eighths = (total_eighths % EIGHTHS_PER_CELL) as usize;
|
||||
|
||||
let mut meter = String::with_capacity(METER_WIDTH);
|
||||
meter.push_str(&"█".repeat(filled_cells));
|
||||
meter.push_str(PARTIAL_BLOCKS[partial_eighths]);
|
||||
|
||||
let occupied_cells = filled_cells + usize::from(partial_eighths > 0);
|
||||
meter.push_str(&" ".repeat(METER_WIDTH.saturating_sub(occupied_cells)));
|
||||
|
||||
format!("Context [{meter}]")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::format_context_used_meter;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn context_meter_uses_five_cells_with_partial_blocks() {
|
||||
assert_eq!(
|
||||
format_context_used_meter(/*used_percent*/ 100),
|
||||
"Context [█████]"
|
||||
);
|
||||
assert_eq!(
|
||||
format_context_used_meter(/*used_percent*/ 50),
|
||||
"Context [██▌ ]"
|
||||
);
|
||||
assert_eq!(
|
||||
format_context_used_meter(/*used_percent*/ 10),
|
||||
"Context [▌ ]"
|
||||
);
|
||||
assert_eq!(
|
||||
format_context_used_meter(/*used_percent*/ 0),
|
||||
"Context [ ]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_meter_clamps_out_of_range_values() {
|
||||
assert_eq!(
|
||||
format_context_used_meter(/*used_percent*/ 125),
|
||||
"Context [█████]"
|
||||
);
|
||||
assert_eq!(
|
||||
format_context_used_meter(/*used_percent*/ -1),
|
||||
"Context [ ]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,32 +867,50 @@ async fn status_line_invalid_items_warn_once() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_line_legacy_context_used_renders_context_meter() {
|
||||
async fn status_line_context_used_renders_labeled_percent() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.config.tui_status_line = Some(vec!["context-used".to_string()]);
|
||||
|
||||
chat.refresh_status_line();
|
||||
|
||||
assert_eq!(status_line_text(&chat), Some("Context [ ]".to_string()));
|
||||
assert_eq!(status_line_text(&chat), Some("Context 0% used".to_string()));
|
||||
assert!(
|
||||
drain_insert_history(&mut rx).is_empty(),
|
||||
"legacy context-used should remain a valid status line item"
|
||||
"context-used should remain a valid status line item"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_line_legacy_context_remaining_renders_context_meter() {
|
||||
async fn status_line_context_remaining_renders_labeled_percent() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.config.tui_status_line = Some(vec!["context-remaining".to_string()]);
|
||||
|
||||
chat.refresh_status_line();
|
||||
|
||||
assert_eq!(status_line_text(&chat), Some("Context [ ]".to_string()));
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some("Context 100% left".to_string())
|
||||
);
|
||||
assert!(
|
||||
drain_insert_history(&mut rx).is_empty(),
|
||||
"legacy context-remaining should remain a valid status line item"
|
||||
"context-remaining should remain a valid status line item"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_line_legacy_context_usage_renders_context_used_percent() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.config.tui_status_line = Some(vec!["context-usage".to_string()]);
|
||||
|
||||
chat.refresh_status_line();
|
||||
|
||||
assert_eq!(status_line_text(&chat), Some("Context 0% used".to_string()));
|
||||
assert!(
|
||||
drain_insert_history(&mut rx).is_empty(),
|
||||
"legacy context-usage should remain a valid status line item"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -995,7 +1013,7 @@ async fn status_line_model_with_reasoning_includes_fast_for_fast_capable_models(
|
||||
chat.config.cwd = test_project_path().abs();
|
||||
chat.config.tui_status_line = Some(vec![
|
||||
"model-with-reasoning".to_string(),
|
||||
"context-usage".to_string(),
|
||||
"context-used".to_string(),
|
||||
"current-dir".to_string(),
|
||||
]);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
|
||||
@@ -1008,7 +1026,7 @@ async fn status_line_model_with_reasoning_includes_fast_for_fast_capable_models(
|
||||
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some(format!("gpt-5.4 xhigh fast · Context [ ] · {test_cwd}"))
|
||||
Some(format!("gpt-5.4 xhigh fast · Context 0% used · {test_cwd}"))
|
||||
);
|
||||
|
||||
chat.set_model("gpt-5.3-codex");
|
||||
@@ -1017,7 +1035,7 @@ async fn status_line_model_with_reasoning_includes_fast_for_fast_capable_models(
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some(format!(
|
||||
"gpt-5.3-codex xhigh · Context [ ] · {test_cwd}"
|
||||
"gpt-5.3-codex xhigh · Context 0% used · {test_cwd}"
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1141,7 +1159,7 @@ async fn status_line_model_with_reasoning_fast_footer_snapshot() {
|
||||
chat.config.cwd = test_project_path().abs();
|
||||
chat.config.tui_status_line = Some(vec![
|
||||
"model-with-reasoning".to_string(),
|
||||
"context-usage".to_string(),
|
||||
"context-used".to_string(),
|
||||
"current-dir".to_string(),
|
||||
]);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
|
||||
|
||||
Reference in New Issue
Block a user