Add Fast mode status-line indicator (#13670)

Addresses feature request #13660

Adds new option to `/statusline` so the status line can display "fast
on" or "fast off"

Summary
- introduce a `FastMode` status-line item so `/statusline` can render
explicit `Fast on`/`Fast off` text for the service tier
- wire the item into the picker metadata and resolve its string from
`ChatWidget` without adding any unrelated `thread-name` logic or storage
changes
- ensure the refresh paths keep the cached footer in sync when the
service tier (fast mode) changes

Testing
- Manually tested

Here's what it looks like when enabled:

<img width="366" height="75" alt="image"
src="https://github.com/user-attachments/assets/7f992d2b-6dab-49ed-aa43-ad496f56f193"
/>
This commit is contained in:
Eric Traut
2026-03-07 00:42:08 -07:00
committed by GitHub
Unverified
parent 4b4f61d379
commit 8df4d9b3b2
6 changed files with 72 additions and 0 deletions
@@ -1120,6 +1120,16 @@ impl ChatComposer {
.collect()
}
#[cfg(test)]
pub(crate) fn status_line_text(&self) -> Option<String> {
self.status_line_value.as_ref().map(|line| {
line.spans
.iter()
.map(|span| span.content.as_ref())
.collect::<String>()
})
}
pub(crate) fn local_images(&self) -> Vec<LocalImageAttachment> {
self.attached_images
.iter()
+5
View File
@@ -690,6 +690,11 @@ impl BottomPane {
self.status.is_some()
}
#[cfg(test)]
pub(crate) fn status_line_text(&self) -> Option<String> {
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);
@@ -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",
}
}
}
+7
View File
@@ -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()
},
),
}
}
@@ -0,0 +1,9 @@
---
source: tui/src/chatwidget/tests.rs
expression: terminal.backend()
---
" "
" "
" Ask Codex to do anything "
" "
" Fast on "
+37
View File
@@ -1951,6 +1951,10 @@ fn lines_to_single_string(lines: &[ratatui::text::Line<'static>]) -> String {
s
}
fn status_line_text(chat: &ChatWidget) -> Option<String> {
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;