Compare commits

...

1 Commits

Author SHA1 Message Date
jif-oai
987aed7bc4 Rename status surfaces thread id item
Co-authored-by: Codex <noreply@openai.com>
2026-04-30 16:33:13 +01:00
14 changed files with 103 additions and 43 deletions

View File

@@ -13,7 +13,7 @@ expression: "render_lines(&view, 72)"
[x] git-branch Current Git branch (omitted when unavaila…
[ ] model-with-reasoning Current model name with reasoning level
[ ] project-name Project name (omitted when unavailable)
[ ] run-state Compact session run-state text (Ready, Wo…
[ ] run-state Compact thread run-state text (Ready, Wor
[ ] context-remaining Percentage of context window remaining (o…
[ ] context-used Percentage of context window used (omitte…

View File

@@ -10,7 +10,7 @@ expression: "render_lines(&view, 84)"
>
[x] project-name Project name (falls back to current directory name)
[x] activity Spinner while working, action-required message while blo…
[x] run-state Compact session run-state text (Ready, Working, Thinking)
[x] run-state Compact thread run-state text (Ready, Working, Thinking)
[x] thread-title Current thread title (omitted when unavailable)
[ ] app-name Codex app name
[ ] current-dir Current working directory

View File

@@ -14,7 +14,7 @@
//! - Git information (branch name)
//! - Context usage (remaining %, used %, window size)
//! - Usage limits (5-hour, weekly)
//! - Session info (thread title, ID, tokens used)
//! - Thread info (thread title, ID, tokens used)
//! - Application version
use ratatui::buffer::Buffer;
@@ -44,7 +44,7 @@ use crate::render::renderable::Renderable;
/// Some items are conditionally displayed based on availability:
/// - Git-related items only show when in a git repository
/// - Context/limit items only show when data is available from the API
/// - Session ID only shows after a session has started
/// - Thread ID only shows after a thread has started
#[derive(EnumIter, EnumString, Display, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[strum(serialize_all = "kebab_case")]
pub(crate) enum StatusLineItem {
@@ -94,7 +94,7 @@ pub(crate) enum StatusLineItem {
/// Total context window size in tokens.
ContextWindowSize,
/// Total tokens used in the current session.
/// Total tokens used in the current thread.
UsedTokens,
/// Total input tokens consumed.
@@ -103,8 +103,9 @@ pub(crate) enum StatusLineItem {
/// Total output tokens generated.
TotalOutputTokens,
/// Full session UUID.
SessionId,
/// Full thread UUID.
#[strum(to_string = "thread-id", serialize = "session-id")]
ThreadId,
/// Whether Fast mode is currently active.
FastMode,
@@ -125,7 +126,7 @@ impl StatusLineItem {
StatusLineItem::CurrentDir => "Current working directory",
StatusLineItem::ProjectRoot => "Project name (omitted when unavailable)",
StatusLineItem::GitBranch => "Current Git branch (omitted when unavailable)",
StatusLineItem::Status => "Compact session run-state text (Ready, Working, Thinking)",
StatusLineItem::Status => "Compact thread run-state text (Ready, Working, Thinking)",
StatusLineItem::ContextRemaining => {
"Percentage of context window remaining (omitted when unknown)"
}
@@ -142,12 +143,10 @@ impl StatusLineItem {
StatusLineItem::ContextWindowSize => {
"Total context window size in tokens (omitted when unknown)"
}
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::SessionId => {
"Current session identifier (omitted until session starts)"
}
StatusLineItem::UsedTokens => "Total tokens used in thread (omitted when zero)",
StatusLineItem::TotalInputTokens => "Total input tokens used in thread",
StatusLineItem::TotalOutputTokens => "Total output tokens used in thread",
StatusLineItem::ThreadId => "Current thread identifier (omitted until thread starts)",
StatusLineItem::FastMode => "Whether Fast mode is currently active",
StatusLineItem::ThreadTitle => "Current thread title (omitted when unavailable)",
StatusLineItem::TaskProgress => {
@@ -173,7 +172,7 @@ impl StatusLineItem {
StatusLineItem::UsedTokens => StatusSurfacePreviewItem::UsedTokens,
StatusLineItem::TotalInputTokens => StatusSurfacePreviewItem::TotalInputTokens,
StatusLineItem::TotalOutputTokens => StatusSurfacePreviewItem::TotalOutputTokens,
StatusLineItem::SessionId => StatusSurfacePreviewItem::SessionId,
StatusLineItem::ThreadId => StatusSurfacePreviewItem::ThreadId,
StatusLineItem::FastMode => StatusSurfacePreviewItem::FastMode,
StatusLineItem::ThreadTitle => StatusSurfacePreviewItem::ThreadTitle,
StatusLineItem::TaskProgress => StatusSurfacePreviewItem::TaskProgress,
@@ -385,6 +384,19 @@ mod tests {
);
}
#[test]
fn thread_id_is_canonical_and_accepts_session_id_legacy_id() {
assert_eq!(StatusLineItem::ThreadId.to_string(), "thread-id");
assert_eq!(
"thread-id".parse::<StatusLineItem>(),
Ok(StatusLineItem::ThreadId)
);
assert_eq!(
"session-id".parse::<StatusLineItem>(),
Ok(StatusLineItem::ThreadId)
);
}
#[test]
fn parse_status_line_items_accepts_title_only_variants() {
let items = ["run-state", "task-progress"]

View File

@@ -20,7 +20,7 @@ pub(crate) enum StatusSurfacePreviewItem {
UsedTokens,
TotalInputTokens,
TotalOutputTokens,
SessionId,
ThreadId,
FastMode,
Model,
ModelWithReasoning,
@@ -46,7 +46,7 @@ impl StatusSurfacePreviewItem {
StatusSurfacePreviewItem::UsedTokens => "0 used",
StatusSurfacePreviewItem::TotalInputTokens => "0 in",
StatusSurfacePreviewItem::TotalOutputTokens => "0 out",
StatusSurfacePreviewItem::SessionId => "550e8400-e29b-41d4",
StatusSurfacePreviewItem::ThreadId => "550e8400-e29b-41d4",
StatusSurfacePreviewItem::FastMode => "Fast on",
StatusSurfacePreviewItem::Model => "gpt-5.2-codex",
StatusSurfacePreviewItem::ModelWithReasoning => "gpt-5.2-codex medium",
@@ -72,7 +72,7 @@ impl StatusSurfacePreviewItem {
Self::UsedTokens,
Self::TotalInputTokens,
Self::TotalOutputTokens,
Self::SessionId,
Self::ThreadId,
Self::FastMode,
Self::Model,
Self::ModelWithReasoning,

View File

@@ -65,14 +65,15 @@ pub(crate) enum TerminalTitleItem {
WeeklyLimit,
/// Codex application version.
CodexVersion,
/// Total tokens used in the current session.
/// Total tokens used in the current thread.
UsedTokens,
/// Total input tokens consumed.
TotalInputTokens,
/// Total output tokens generated.
TotalOutputTokens,
/// Full session UUID.
SessionId,
/// Full thread UUID.
#[strum(to_string = "thread-id", serialize = "session-id")]
ThreadId,
/// Whether Fast mode is currently active.
FastMode,
/// Current model name.
@@ -93,9 +94,7 @@ impl TerminalTitleItem {
TerminalTitleItem::Spinner => {
"Spinner while working, action-required message while blocked."
}
TerminalTitleItem::Status => {
"Compact session run-state text (Ready, Working, Thinking)"
}
TerminalTitleItem::Status => "Compact thread run-state text (Ready, Working, Thinking)",
TerminalTitleItem::Thread => "Current thread title (omitted when unavailable)",
TerminalTitleItem::GitBranch => "Current Git branch (omitted when unavailable)",
TerminalTitleItem::ContextRemaining => {
@@ -111,11 +110,11 @@ impl TerminalTitleItem {
"Remaining usage on weekly usage limit (omitted when unavailable)"
}
TerminalTitleItem::CodexVersion => "Codex application version",
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::SessionId => {
"Current session identifier (omitted until session starts)"
TerminalTitleItem::UsedTokens => "Total tokens used in thread (omitted when zero)",
TerminalTitleItem::TotalInputTokens => "Total input tokens used in thread",
TerminalTitleItem::TotalOutputTokens => "Total output tokens used in thread",
TerminalTitleItem::ThreadId => {
"Current thread identifier (omitted until thread starts)"
}
TerminalTitleItem::FastMode => "Whether Fast mode is currently active",
TerminalTitleItem::Model => "Current model name",
@@ -145,7 +144,7 @@ impl TerminalTitleItem {
TerminalTitleItem::TotalOutputTokens => {
Some(StatusSurfacePreviewItem::TotalOutputTokens)
}
TerminalTitleItem::SessionId => Some(StatusSurfacePreviewItem::SessionId),
TerminalTitleItem::ThreadId => Some(StatusSurfacePreviewItem::ThreadId),
TerminalTitleItem::FastMode => Some(StatusSurfacePreviewItem::FastMode),
TerminalTitleItem::Model => Some(StatusSurfacePreviewItem::Model),
TerminalTitleItem::ModelWithReasoning => {
@@ -479,6 +478,19 @@ mod tests {
);
}
#[test]
fn thread_id_is_canonical_and_accepts_session_id_legacy_id() {
assert_eq!(TerminalTitleItem::ThreadId.to_string(), "thread-id");
assert_eq!(
"thread-id".parse::<TerminalTitleItem>(),
Ok(TerminalTitleItem::ThreadId)
);
assert_eq!(
"session-id".parse::<TerminalTitleItem>(),
Ok(TerminalTitleItem::ThreadId)
);
}
#[test]
fn model_with_reasoning_has_distinct_id() {
assert_eq!(
@@ -510,7 +522,7 @@ mod tests {
"used-tokens",
"total-input-tokens",
"total-output-tokens",
"session-id",
"thread-id",
"fast-mode",
]
.into_iter(),
@@ -533,7 +545,7 @@ mod tests {
TerminalTitleItem::UsedTokens,
TerminalTitleItem::TotalInputTokens,
TerminalTitleItem::TotalOutputTokens,
TerminalTitleItem::SessionId,
TerminalTitleItem::ThreadId,
TerminalTitleItem::FastMode,
])
);

View File

@@ -13,7 +13,7 @@ expression: status_line_popup_snapshot(&mut chat)
[ ] model Current model name
[ ] model-with-reasoning Current model name with reasoning level
[ ] current-dir Current working directory
[ ] run-state Compact session run-state text (Ready, Working, Thinking)
[ ] run-state Compact thread run-state text (Ready, Working, Thinking)
[ ] context-remaining Percentage of context window remaining (omitted when unknown)
my-project · feat/awesome-feature · thread title

View File

@@ -13,7 +13,7 @@ expression: status_line_popup_snapshot(&mut chat)
[ ] model Current model name
[ ] model-with-reasoning Current model name with reasoning level
[ ] current-dir Current working directory
[ ] run-state Compact session run-state text (Ready, Working, Thinking)
[ ] run-state Compact thread run-state text (Ready, Working, Thinking)
[ ] context-remaining Percentage of context window remaining (omitted when unknown)
preview-live-root · feature/live-preview-branch · Live preview thread

View File

@@ -13,7 +13,7 @@ expression: status_line_popup_snapshot(&mut chat)
[ ] model Current model name
[ ] model-with-reasoning Current model name with reasoning level
[ ] current-dir Current working directory
[ ] run-state Compact session run-state text (Ready, Working, Thinking)
[ ] run-state Compact thread run-state text (Ready, Working, Thinking)
[ ] context-remaining Percentage of context window remaining (omitted when unknown)
my-project · feature/mixed-preview · Mixed preview thread

View File

@@ -14,7 +14,7 @@ expression: terminal_title_popup_snapshot(&mut chat)
[ ] project-name Project name (falls back to current directory name)
[ ] current-dir Current working directory
[ ] activity Spinner while working, action-required message while blocked.
[ ] run-state Compact session run-state text (Ready, Working, Thinking)
[ ] run-state Compact thread run-state text (Ready, Working, Thinking)
thread title | feat/awesome-feature | Tasks 0/0
Use ↑↓ to navigate, ←→ to move, space to select, enter to confirm, esc to cancel.

View File

@@ -14,7 +14,7 @@ expression: terminal_title_popup_snapshot(&mut chat)
[ ] app-name Codex app name
[ ] current-dir Current working directory
[ ] activity Spinner while working, action-required message while blocked.
[ ] run-state Compact session run-state text (Ready, Working, Thinking)
[ ] run-state Compact thread run-state text (Ready, Working, Thinking)
preview-live-root | Live preview thread | feature/live-preview-branch | Tasks 2/5
Use ↑↓ to navigate, ←→ to move, space to select, enter to confirm, esc to cancel.

View File

@@ -13,7 +13,7 @@ expression: terminal_title_popup_snapshot(&mut chat)
[ ] app-name Codex app name
[ ] current-dir Current working directory
[ ] activity Spinner while working, action-required message while blocked.
[ ] run-state Compact session run-state text (Ready, Working, Thinking)
[ ] run-state Compact thread run-state text (Ready, Working, Thinking)
[ ] git-branch Current Git branch (omitted when unavailable)
project | Mixed preview thread | Tasks 0/0

View File

@@ -494,7 +494,7 @@ impl ChatWidget {
/// Resolves a display string for one configured status-line item.
///
/// Returning `None` means "omit this item for now", not "configuration error". Callers rely on
/// this to keep partially available status lines readable while waiting for session, token, or
/// this to keep partially available status lines readable while waiting for thread, token, or
/// git metadata.
pub(super) fn status_line_value_for_item(&mut self, item: &StatusLineItem) -> Option<String> {
match item {
@@ -558,7 +558,7 @@ impl ChatWidget {
"{} out",
format_tokens_compact(self.status_line_total_usage().output_tokens)
)),
StatusLineItem::SessionId => self.thread_id.map(|id| id.to_string()),
StatusLineItem::ThreadId => self.thread_id.map(|id| id.to_string()),
StatusLineItem::FastMode => Some(
if matches!(self.current_service_tier(), Some(ServiceTier::Fast)) {
"Fast on".to_string()
@@ -596,7 +596,7 @@ impl ChatWidget {
StatusSurfacePreviewItem::UsedTokens => StatusLineItem::UsedTokens,
StatusSurfacePreviewItem::TotalInputTokens => StatusLineItem::TotalInputTokens,
StatusSurfacePreviewItem::TotalOutputTokens => StatusLineItem::TotalOutputTokens,
StatusSurfacePreviewItem::SessionId => StatusLineItem::SessionId,
StatusSurfacePreviewItem::ThreadId => StatusLineItem::ThreadId,
StatusSurfacePreviewItem::FastMode => StatusLineItem::FastMode,
StatusSurfacePreviewItem::Model => StatusLineItem::ModelName,
StatusSurfacePreviewItem::ModelWithReasoning => StatusLineItem::ModelWithReasoning,
@@ -659,8 +659,8 @@ impl ChatWidget {
TerminalTitleItem::TotalOutputTokens => self
.status_line_value_for_item(&StatusLineItem::TotalOutputTokens)
.map(|value| Self::truncate_terminal_title_part(value, /*max_chars*/ 32)),
TerminalTitleItem::SessionId => self
.status_line_value_for_item(&StatusLineItem::SessionId)
TerminalTitleItem::ThreadId => self
.status_line_value_for_item(&StatusLineItem::ThreadId)
.map(|value| Self::truncate_terminal_title_part(value, /*max_chars*/ 32)),
TerminalTitleItem::FastMode => self
.status_line_value_for_item(&StatusLineItem::FastMode)

View File

@@ -1376,6 +1376,25 @@ async fn status_line_legacy_context_usage_renders_context_used_percent() {
);
}
#[tokio::test]
async fn status_line_thread_id_renders_and_accepts_legacy_session_id() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let thread_id = ThreadId::new();
chat.thread_id = Some(thread_id);
chat.config.tui_status_line = Some(vec!["thread-id".to_string()]);
chat.refresh_status_line();
assert_eq!(status_line_text(&chat), Some(thread_id.to_string()));
chat.config.tui_status_line = Some(vec!["session-id".to_string()]);
chat.refresh_status_line();
assert_eq!(status_line_text(&chat), Some(thread_id.to_string()));
assert!(
drain_insert_history(&mut rx).is_empty(),
"thread-id and legacy session-id should remain valid status line items"
);
}
#[tokio::test]
async fn status_line_branch_state_resets_when_git_branch_disabled() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;

View File

@@ -80,6 +80,23 @@ async fn terminal_title_action_required_respects_spinner_setting() {
assert!(!chat.should_animate_terminal_title_action_required());
}
#[tokio::test]
async fn terminal_title_thread_id_renders_and_accepts_legacy_session_id() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let thread_id = ThreadId::new();
let expected_title =
ChatWidget::truncate_terminal_title_part(thread_id.to_string(), /*max_chars*/ 32);
chat.thread_id = Some(thread_id);
chat.config.tui_terminal_title = Some(vec!["thread-id".to_string()]);
chat.refresh_terminal_title();
assert_eq!(chat.last_terminal_title, Some(expected_title.clone()));
chat.config.tui_terminal_title = Some(vec!["session-id".to_string()]);
chat.refresh_terminal_title();
assert_eq!(chat.last_terminal_title, Some(expected_title));
}
#[tokio::test]
async fn terminal_title_action_required_blinks_when_animations_are_enabled() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;