diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index daaab43ac5..c56105243d 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -1120,6 +1120,16 @@ impl ChatComposer { .collect() } + #[cfg(test)] + pub(crate) fn status_line_text(&self) -> Option { + self.status_line_value.as_ref().map(|line| { + line.spans + .iter() + .map(|span| span.content.as_ref()) + .collect::() + }) + } + pub(crate) fn local_images(&self) -> Vec { self.attached_images .iter() diff --git a/codex-rs/tui/src/bottom_pane/mod.rs b/codex-rs/tui/src/bottom_pane/mod.rs index 24a4c959ef..62142ed227 100644 --- a/codex-rs/tui/src/bottom_pane/mod.rs +++ b/codex-rs/tui/src/bottom_pane/mod.rs @@ -690,6 +690,11 @@ impl BottomPane { self.status.is_some() } + #[cfg(test)] + pub(crate) fn status_line_text(&self) -> Option { + self.composer.status_line_text() + } + pub(crate) fn show_esc_backtrack_hint(&mut self) { self.esc_backtrack_hint = true; self.composer.set_esc_backtrack_hint(true); diff --git a/codex-rs/tui/src/bottom_pane/status_line_setup.rs b/codex-rs/tui/src/bottom_pane/status_line_setup.rs index 12fd573b91..076ce50909 100644 --- a/codex-rs/tui/src/bottom_pane/status_line_setup.rs +++ b/codex-rs/tui/src/bottom_pane/status_line_setup.rs @@ -92,6 +92,9 @@ pub(crate) enum StatusLineItem { /// Full session UUID. SessionId, + + /// Whether Fast mode is currently active. + FastMode, } impl StatusLineItem { @@ -125,6 +128,7 @@ impl StatusLineItem { StatusLineItem::SessionId => { "Current session identifier (omitted until session starts)" } + StatusLineItem::FastMode => "Whether Fast mode is currently active", } } } diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index ae35225b6b..c7f4bd1046 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -5348,6 +5348,13 @@ impl ChatWidget { format_tokens_compact(self.status_line_total_usage().output_tokens) )), StatusLineItem::SessionId => self.thread_id.map(|id| id.to_string()), + StatusLineItem::FastMode => Some( + if matches!(self.config.service_tier, Some(ServiceTier::Fast)) { + "Fast on".to_string() + } else { + "Fast off".to_string() + }, + ), } } diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_fast_mode_footer.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_fast_mode_footer.snap new file mode 100644 index 0000000000..ad644699c4 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_line_fast_mode_footer.snap @@ -0,0 +1,9 @@ +--- +source: tui/src/chatwidget/tests.rs +expression: terminal.backend() +--- +" " +" " +"› Ask Codex to do anything " +" " +" Fast on " diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index a9819fb188..7a99793e65 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -1951,6 +1951,10 @@ fn lines_to_single_string(lines: &[ratatui::text::Line<'static>]) -> String { s } +fn status_line_text(chat: &ChatWidget) -> Option { + chat.bottom_pane.status_line_text() +} + fn make_token_info(total_tokens: i64, context_window: i64) -> TokenUsageInfo { fn usage(total_tokens: i64) -> TokenUsage { TokenUsage { @@ -9282,6 +9286,39 @@ async fn status_line_branch_refreshes_after_interrupt() { assert!(chat.status_line_branch_pending); } +#[tokio::test] +async fn status_line_fast_mode_renders_on_and_off() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; + chat.config.tui_status_line = Some(vec!["fast-mode".to_string()]); + + chat.refresh_status_line(); + assert_eq!(status_line_text(&chat), Some("Fast off".to_string())); + + chat.set_service_tier(Some(ServiceTier::Fast)); + chat.refresh_status_line(); + assert_eq!(status_line_text(&chat), Some("Fast on".to_string())); +} + +#[tokio::test] +async fn status_line_fast_mode_footer_snapshot() { + use ratatui::Terminal; + use ratatui::backend::TestBackend; + + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; + chat.show_welcome_banner = false; + chat.config.tui_status_line = Some(vec!["fast-mode".to_string()]); + chat.set_service_tier(Some(ServiceTier::Fast)); + chat.refresh_status_line(); + + let width = 80; + let height = chat.desired_height(width); + let mut terminal = Terminal::new(TestBackend::new(width, height)).expect("create terminal"); + terminal + .draw(|f| chat.render(f.area(), f.buffer_mut())) + .expect("draw fast-mode footer"); + assert_snapshot!("status_line_fast_mode_footer", terminal.backend()); +} + #[tokio::test] async fn stream_recovery_restores_previous_status_header() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;