Add compaction counts to TUI status line and title

This commit is contained in:
canvrno-oai
2026-07-08 14:55:10 -07:00
parent d5e7342080
commit f5b69436b5
12 changed files with 102 additions and 10 deletions
+1
View File
@@ -139,6 +139,7 @@ fn terminal_title_item_id(item: &str) -> Option<&'static str> {
"used-tokens" => Some("used-tokens"),
"total-input-tokens" => Some("total-input-tokens"),
"total-output-tokens" => Some("total-output-tokens"),
"compactions" => Some("compactions"),
"thread-id" | "session-id" => Some("thread-id"),
"fast-mode" => Some("fast-mode"),
"model" | "model-name" => Some("model"),
+16 -4
View File
@@ -1125,22 +1125,34 @@ async fn replayed_interrupted_turn_restores_queued_input_to_composer() {
async fn token_usage_update_refreshes_status_line_with_runtime_context_window() {
let mut app = make_test_app().await;
app.chat_widget.setup_status_line(
vec![crate::bottom_pane::StatusLineItem::ContextWindowSize],
vec![
crate::bottom_pane::StatusLineItem::ContextWindowSize,
crate::bottom_pane::StatusLineItem::Compactions,
],
/*use_theme_colors*/ true,
);
app.chat_widget
.setup_terminal_title(vec![crate::bottom_pane::TerminalTitleItem::Compactions]);
assert_eq!(app.chat_widget.status_line_text(), None);
assert_eq!(
app.chat_widget.status_line_text(),
Some("0 compactions".into())
);
app.handle_thread_event_now(ThreadBufferedEvent::Notification(token_usage_notification(
ThreadId::new(),
"turn-1",
Some(950_000),
/*window_number*/ 0,
/*window_number*/ 3,
)));
assert_eq!(
app.chat_widget.status_line_text(),
Some("950K window".into())
Some("950K window · 3 compactions".into())
);
assert_eq!(
app.chat_widget.last_terminal_title,
Some("3 compactions".to_string())
);
}
@@ -17,6 +17,7 @@
//! - Context usage (remaining %, used %, window size)
//! - Usage limits (primary, secondary)
//! - Session info (thread title, thread ID, tokens used)
//! - Context compaction count
//! - Application version
use ratatui::buffer::Buffer;
@@ -124,6 +125,9 @@ pub(crate) enum StatusLineItem {
/// Total output tokens generated.
TotalOutputTokens,
/// Number of context compactions in the current thread.
Compactions,
/// Full thread UUID.
#[strum(to_string = "thread-id", serialize = "session-id")]
SessionId,
@@ -182,6 +186,7 @@ impl StatusLineItem {
StatusLineItem::UsedTokens => "Total tokens used in session (omitted when zero)",
StatusLineItem::TotalInputTokens => "Total input tokens used in session",
StatusLineItem::TotalOutputTokens => "Total output tokens used in session",
StatusLineItem::Compactions => "Number of context compactions in the current thread",
StatusLineItem::SessionId => "Current thread identifier (omitted until thread starts)",
StatusLineItem::FastMode => "Whether Fast mode is currently active",
StatusLineItem::RawOutput => "Whether raw scrollback mode is active",
@@ -219,6 +224,7 @@ impl StatusLineItem {
StatusLineItem::UsedTokens => StatusSurfacePreviewItem::UsedTokens,
StatusLineItem::TotalInputTokens => StatusSurfacePreviewItem::TotalInputTokens,
StatusLineItem::TotalOutputTokens => StatusSurfacePreviewItem::TotalOutputTokens,
StatusLineItem::Compactions => StatusSurfacePreviewItem::Compactions,
StatusLineItem::SessionId => StatusSurfacePreviewItem::SessionId,
StatusLineItem::FastMode => StatusSurfacePreviewItem::FastMode,
StatusLineItem::RawOutput => StatusSurfacePreviewItem::RawOutput,
@@ -43,7 +43,8 @@ impl StatusLineAccent {
| StatusLineItem::ContextWindowSize
| StatusLineItem::UsedTokens
| StatusLineItem::TotalInputTokens
| StatusLineItem::TotalOutputTokens => Self::Usage,
| StatusLineItem::TotalOutputTokens
| StatusLineItem::Compactions => Self::Usage,
StatusLineItem::FiveHourLimit | StatusLineItem::WeeklyLimit => Self::Limit,
StatusLineItem::CodexVersion | StatusLineItem::SessionId => Self::Metadata,
StatusLineItem::FastMode | StatusLineItem::RawOutput => Self::Mode,
@@ -27,6 +27,7 @@ pub(crate) enum StatusSurfacePreviewItem {
UsedTokens,
TotalInputTokens,
TotalOutputTokens,
Compactions,
SessionId,
FastMode,
RawOutput,
@@ -60,6 +61,7 @@ impl StatusSurfacePreviewItem {
StatusSurfacePreviewItem::UsedTokens => "0 used",
StatusSurfacePreviewItem::TotalInputTokens => "0 in",
StatusSurfacePreviewItem::TotalOutputTokens => "0 out",
StatusSurfacePreviewItem::Compactions => "0 compactions",
StatusSurfacePreviewItem::SessionId => "550e8400-e29b-41d4",
StatusSurfacePreviewItem::FastMode => "Fast on",
StatusSurfacePreviewItem::RawOutput => "raw output",
@@ -93,6 +95,7 @@ impl StatusSurfacePreviewItem {
Self::UsedTokens,
Self::TotalInputTokens,
Self::TotalOutputTokens,
Self::Compactions,
Self::SessionId,
Self::FastMode,
Self::RawOutput,
@@ -72,6 +72,8 @@ pub(crate) enum TerminalTitleItem {
TotalInputTokens,
/// Total output tokens generated.
TotalOutputTokens,
/// Number of context compactions in the current thread.
Compactions,
/// Full thread UUID.
#[strum(to_string = "thread-id", serialize = "session-id")]
SessionId,
@@ -118,6 +120,7 @@ impl TerminalTitleItem {
TerminalTitleItem::UsedTokens => "Total tokens used in session (omitted when zero)",
TerminalTitleItem::TotalInputTokens => "Total input tokens used in session",
TerminalTitleItem::TotalOutputTokens => "Total output tokens used in session",
TerminalTitleItem::Compactions => "Number of context compactions in the current thread",
TerminalTitleItem::SessionId => {
"Current thread identifier (omitted until thread starts)"
}
@@ -150,6 +153,7 @@ impl TerminalTitleItem {
TerminalTitleItem::TotalOutputTokens => {
Some(StatusSurfacePreviewItem::TotalOutputTokens)
}
TerminalTitleItem::Compactions => Some(StatusSurfacePreviewItem::Compactions),
TerminalTitleItem::SessionId => Some(StatusSurfacePreviewItem::SessionId),
TerminalTitleItem::FastMode => Some(StatusSurfacePreviewItem::FastMode),
TerminalTitleItem::Model => Some(StatusSurfacePreviewItem::Model),
-1
View File
@@ -551,7 +551,6 @@ pub(crate) struct ChatWidget {
runtime_model_provider_base_url: Option<String>,
pub(crate) remote_connection: Option<RemoteConnectionStatus>,
token_info: Option<TokenUsageInfo>,
#[cfg_attr(not(test), allow(dead_code))]
window_number: u64,
rate_limit_snapshots_by_limit_id: BTreeMap<String, RateLimitSnapshotDisplay>,
refreshing_status_outputs: Vec<(u64, StatusHistoryHandle)>,
@@ -12,9 +12,9 @@ expression: status_line_popup_snapshot(&mut chat)
[x] project-name Project name (omitted when unavailable)
[x] git-branch Current Git branch (omitted when unavailable)
[x] thread-title Current thread title, or thread identifier when unnamed
[x] compactions Number of context compactions in the current thread
[ ] model Current model name
[ ] model-with-reasoning Current model name with reasoning level
[ ] reasoning Current reasoning level
preview-live-root · feature/live-preview-branch · Live preview thread
preview-live-root · feature/live-preview-branch · Live preview thread · 3 compactions
Press space to toggle; ←/→ to move; enter to confirm and close; esc to close
@@ -2,5 +2,5 @@
source: tui/src/chatwidget/tests/status_surface_previews.rs
expression: snapshot
---
status line: preview-live-root · feature/live-preview-branch · Live preview thread
terminal title: preview-live-root | Live preview thread | feature/live-preview-branch | Tasks 2/5
status line: preview-live-root · feature/live-preview-branch · Live preview thread · 3 compactions
terminal title: preview-live-root | Live preview thread | feature/live-preview-branch | Tasks 2/5 | 3 compactions
@@ -0,0 +1,20 @@
---
source: tui/src/chatwidget/tests/status_surface_previews.rs
expression: terminal_title_popup_snapshot(&mut chat)
---
Configure Terminal Title
Select which items to display in the terminal title.
Type to search
>
› [x] app-name Codex app name
[x] model Current model name
[x] reasoning Current reasoning level
[x] fast-mode Whether Fast mode is currently active
[x] compactions Number of context compactions in the current thread
[ ] project-name Project name (falls back to current directory name)
[ ] current-dir Current working directory
[ ] activity Spinner while working, action-required message while blocked.
codex | gpt-5.5 | default | Fast off | 3 compactions
Press space to toggle; ←/→ to move; enter to confirm and close; esc to close
@@ -727,6 +727,7 @@ impl ChatWidget {
"{} out",
format_tokens_compact(self.status_line_total_usage().output_tokens)
)),
StatusLineItem::Compactions => Some(format!("{} compactions", self.window_number)),
StatusLineItem::SessionId => self.thread_id.map(|id| id.to_string()),
StatusLineItem::FastMode => Some(
if self.current_service_tier() == Some(ServiceTier::Fast.request_value()) {
@@ -785,6 +786,7 @@ impl ChatWidget {
StatusSurfacePreviewItem::UsedTokens => StatusLineItem::UsedTokens,
StatusSurfacePreviewItem::TotalInputTokens => StatusLineItem::TotalInputTokens,
StatusSurfacePreviewItem::TotalOutputTokens => StatusLineItem::TotalOutputTokens,
StatusSurfacePreviewItem::Compactions => StatusLineItem::Compactions,
StatusSurfacePreviewItem::SessionId => StatusLineItem::SessionId,
StatusSurfacePreviewItem::FastMode => StatusLineItem::FastMode,
StatusSurfacePreviewItem::RawOutput => StatusLineItem::RawOutput,
@@ -843,6 +845,9 @@ impl ChatWidget {
TerminalTitleItem::TotalOutputTokens => self
.status_line_value_for_item(StatusLineItem::TotalOutputTokens)
.map(|value| Self::truncate_terminal_title_part(value, /*max_chars*/ 32)),
TerminalTitleItem::Compactions => self
.status_line_value_for_item(StatusLineItem::Compactions)
.map(|value| Self::truncate_terminal_title_part(value, /*max_chars*/ 32)),
TerminalTitleItem::SessionId => self
.status_line_value_for_item(StatusLineItem::SessionId)
.map(|value| Self::truncate_terminal_title_part(value, /*max_chars*/ 32)),
@@ -89,6 +89,13 @@ async fn status_surface_preview_lines_live_only_snapshot() {
chat.status_line_branch = Some("feature/live-preview-branch".to_string());
chat.thread_name = Some("Live preview thread".to_string());
chat.transcript.last_plan_progress = Some((2, 5));
handle_token_count_with_window_number(
&mut chat,
Some(make_token_info(
/*total_tokens*/ 1, /*context_window*/ 100,
)),
/*window_number*/ 3,
);
let snapshot = combined_preview_snapshot(
&mut chat,
@@ -96,12 +103,14 @@ async fn status_surface_preview_lines_live_only_snapshot() {
StatusLineItem::ProjectRoot,
StatusLineItem::GitBranch,
StatusLineItem::ThreadTitle,
StatusLineItem::Compactions,
],
&[
TerminalTitleItem::Project,
TerminalTitleItem::Thread,
TerminalTitleItem::GitBranch,
TerminalTitleItem::TaskProgress,
TerminalTitleItem::Compactions,
],
);
@@ -114,10 +123,18 @@ async fn status_line_setup_popup_live_only_snapshot() {
cache_project_root(&mut chat, "preview-live-root");
chat.status_line_branch = Some("feature/live-preview-branch".to_string());
chat.thread_name = Some("Live preview thread".to_string());
handle_token_count_with_window_number(
&mut chat,
Some(make_token_info(
/*total_tokens*/ 1, /*context_window*/ 100,
)),
/*window_number*/ 3,
);
chat.config.tui_status_line = Some(vec![
"project-name".to_string(),
"git-branch".to_string(),
"thread-title".to_string(),
"compactions".to_string(),
]);
assert_chatwidget_snapshot!(
@@ -326,6 +343,30 @@ async fn terminal_title_setup_popup_live_only_snapshot() {
);
}
#[tokio::test]
async fn terminal_title_setup_popup_compactions_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
handle_token_count_with_window_number(
&mut chat,
Some(make_token_info(
/*total_tokens*/ 1, /*context_window*/ 100,
)),
/*window_number*/ 3,
);
chat.config.tui_terminal_title = Some(vec![
"app-name".to_string(),
"model".to_string(),
"reasoning".to_string(),
"fast-mode".to_string(),
"compactions".to_string(),
]);
assert_chatwidget_snapshot!(
"terminal_title_setup_popup_compactions",
terminal_title_popup_snapshot(&mut chat)
);
}
#[tokio::test]
async fn terminal_title_setup_popup_hardcoded_only_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;