From 17ab5f6a52e3bf68b46367ca51b7f67463ca4e30 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 13 Jan 2026 00:32:53 -0800 Subject: [PATCH] Show tab queue hint in footer (#9138) - show the Tab queue hint in the footer when a task is running with Steer enabled - drop the history queue hint and add footer snapshots --- codex-rs/tui/src/bottom_pane/chat_composer.rs | 1 + codex-rs/tui/src/bottom_pane/footer.rs | 51 +++++++++++++-- ...oter_context_only_queue_hint_disabled.snap | 5 ++ ...ooter_context_only_queue_hint_enabled.snap | 5 ++ codex-rs/tui/src/chatwidget.rs | 11 ---- ...exec_and_status_layout_vt100_snapshot.snap | 29 ++++++++- .../tui2/src/bottom_pane/chat_composer.rs | 1 + codex-rs/tui2/src/bottom_pane/footer.rs | 63 +++++++++++++++++-- ...oter_context_only_queue_hint_disabled.snap | 5 ++ ...ooter_context_only_queue_hint_enabled.snap | 5 ++ codex-rs/tui2/src/chatwidget.rs | 11 ---- ...exec_and_status_layout_vt100_snapshot.snap | 29 ++++++++- 12 files changed, 184 insertions(+), 32 deletions(-) create mode 100644 codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap create mode 100644 codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap create mode 100644 codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap create mode 100644 codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index ce277154c..de3af3e84 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -1641,6 +1641,7 @@ impl ChatComposer { esc_backtrack_hint: self.esc_backtrack_hint, use_shift_enter_hint: self.use_shift_enter_hint, is_task_running: self.is_task_running, + steer_enabled: self.steer_enabled, context_window_percent: self.context_window_percent, context_window_used_tokens: self.context_window_used_tokens, } diff --git a/codex-rs/tui/src/bottom_pane/footer.rs b/codex-rs/tui/src/bottom_pane/footer.rs index 4afd7cf36..3fb04c393 100644 --- a/codex-rs/tui/src/bottom_pane/footer.rs +++ b/codex-rs/tui/src/bottom_pane/footer.rs @@ -20,6 +20,7 @@ pub(crate) struct FooterProps { pub(crate) esc_backtrack_hint: bool, pub(crate) use_shift_enter_hint: bool, pub(crate) is_task_running: bool, + pub(crate) steer_enabled: bool, pub(crate) context_window_percent: Option, pub(crate) context_window_used_tokens: Option, } @@ -110,10 +111,18 @@ fn footer_lines(props: FooterProps) -> Vec> { shortcut_overlay_lines(state) } FooterMode::EscHint => vec![esc_hint_line(props.esc_backtrack_hint)], - FooterMode::ContextOnly => vec![context_window_line( - props.context_window_percent, - props.context_window_used_tokens, - )], + FooterMode::ContextOnly => { + let mut line = context_window_line( + props.context_window_percent, + props.context_window_used_tokens, + ); + if props.is_task_running && props.steer_enabled { + line.push_span(" · ".dim()); + line.push_span(key_hint::plain(KeyCode::Tab)); + line.push_span(" to queue message".dim()); + } + vec![line] + } } } @@ -477,6 +486,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, }, @@ -489,6 +499,7 @@ mod tests { esc_backtrack_hint: true, use_shift_enter_hint: true, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, }, @@ -501,6 +512,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, }, @@ -513,6 +525,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: true, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, }, @@ -525,6 +538,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, }, @@ -537,6 +551,7 @@ mod tests { esc_backtrack_hint: true, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, }, @@ -549,6 +564,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: true, + steer_enabled: false, context_window_percent: Some(72), context_window_used_tokens: None, }, @@ -561,9 +577,36 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: Some(123_456), }, ); + + snapshot_footer( + "footer_context_only_queue_hint_disabled", + FooterProps { + mode: FooterMode::ContextOnly, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: true, + steer_enabled: false, + context_window_percent: None, + context_window_used_tokens: None, + }, + ); + + snapshot_footer( + "footer_context_only_queue_hint_enabled", + FooterProps { + mode: FooterMode::ContextOnly, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: true, + steer_enabled: true, + context_window_percent: None, + context_window_used_tokens: None, + }, + ); } } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap new file mode 100644 index 000000000..ce36b2ada --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" 100% context left " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap new file mode 100644 index 000000000..b9733866d --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" 100% context left · tab to queue message " diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 5e2ebbfe6..6ba7cb0d4 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2152,17 +2152,6 @@ impl ChatWidget { self.add_to_history(history_cell::new_user_prompt(text)); } - // If steer is enabled and a task is running, show hint about queuing with Tab - if self.config.features.enabled(Feature::Steer) && self.bottom_pane.is_task_running() { - use crate::key_hint; - use ratatui::text::Line; - let hint_line = Line::from(vec![ - "You can queue messages by pressing ".dim(), - key_hint::plain(KeyCode::Tab).into(), - ]); - self.add_to_history(history_cell::PlainHistoryCell::new(vec![hint_line])); - } - self.needs_final_message_separator = false; } diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap index c3bdf60bd..b51d759fe 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap @@ -2,6 +2,33 @@ source: tui/src/chatwidget/tests.rs expression: term.backend().vt100().screen().contents() --- + + + + + + + + + + + + + + + + + + + + + + + + + + + • I’m going to search the repo for where “Change Approved” is rendered to update that view. @@ -14,4 +41,4 @@ expression: term.backend().vt100().screen().contents() › Summarize recent commits - 100% context left + 100% context left · tab to queue message diff --git a/codex-rs/tui2/src/bottom_pane/chat_composer.rs b/codex-rs/tui2/src/bottom_pane/chat_composer.rs index 82d2e9854..c7e8af0c2 100644 --- a/codex-rs/tui2/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui2/src/bottom_pane/chat_composer.rs @@ -1583,6 +1583,7 @@ impl ChatComposer { esc_backtrack_hint: self.esc_backtrack_hint, use_shift_enter_hint: self.use_shift_enter_hint, is_task_running: self.is_task_running, + steer_enabled: self.steer_enabled, context_window_percent: self.context_window_percent, context_window_used_tokens: self.context_window_used_tokens, transcript_scrolled: self.transcript_scrolled, diff --git a/codex-rs/tui2/src/bottom_pane/footer.rs b/codex-rs/tui2/src/bottom_pane/footer.rs index 4db04464d..c543ab6ee 100644 --- a/codex-rs/tui2/src/bottom_pane/footer.rs +++ b/codex-rs/tui2/src/bottom_pane/footer.rs @@ -21,6 +21,7 @@ pub(crate) struct FooterProps { pub(crate) esc_backtrack_hint: bool, pub(crate) use_shift_enter_hint: bool, pub(crate) is_task_running: bool, + pub(crate) steer_enabled: bool, pub(crate) context_window_percent: Option, pub(crate) context_window_used_tokens: Option, pub(crate) transcript_scrolled: bool, @@ -152,10 +153,18 @@ fn footer_lines(props: FooterProps) -> Vec> { shortcut_overlay_lines(state) } FooterMode::EscHint => vec![esc_hint_line(props.esc_backtrack_hint)], - FooterMode::ContextOnly => vec![context_window_line( - props.context_window_percent, - props.context_window_used_tokens, - )], + FooterMode::ContextOnly => { + let mut line = context_window_line( + props.context_window_percent, + props.context_window_used_tokens, + ); + if props.is_task_running && props.steer_enabled { + line.push_span(" · ".dim()); + line.push_span(key_hint::plain(KeyCode::Tab)); + line.push_span(" to queue message".dim()); + } + vec![line] + } }; apply_copy_feedback(&mut lines, props.transcript_copy_feedback); lines @@ -508,6 +517,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, @@ -525,6 +535,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: true, @@ -542,6 +553,7 @@ mod tests { esc_backtrack_hint: true, use_shift_enter_hint: true, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, @@ -559,6 +571,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, @@ -576,6 +589,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: true, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, @@ -593,6 +607,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, @@ -610,6 +625,7 @@ mod tests { esc_backtrack_hint: true, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, @@ -627,6 +643,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: true, + steer_enabled: false, context_window_percent: Some(72), context_window_used_tokens: None, transcript_scrolled: false, @@ -644,6 +661,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: Some(123_456), transcript_scrolled: false, @@ -654,6 +672,42 @@ mod tests { }, ); + snapshot_footer( + "footer_context_only_queue_hint_disabled", + FooterProps { + mode: FooterMode::ContextOnly, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: true, + steer_enabled: false, + context_window_percent: None, + context_window_used_tokens: None, + transcript_scrolled: false, + transcript_selection_active: false, + transcript_scroll_position: None, + transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')), + transcript_copy_feedback: None, + }, + ); + + snapshot_footer( + "footer_context_only_queue_hint_enabled", + FooterProps { + mode: FooterMode::ContextOnly, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: true, + steer_enabled: true, + context_window_percent: None, + context_window_used_tokens: None, + transcript_scrolled: false, + transcript_selection_active: false, + transcript_scroll_position: None, + transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')), + transcript_copy_feedback: None, + }, + ); + snapshot_footer( "footer_copy_feedback_copied", FooterProps { @@ -661,6 +715,7 @@ mod tests { esc_backtrack_hint: false, use_shift_enter_hint: false, is_task_running: false, + steer_enabled: false, context_window_percent: None, context_window_used_tokens: None, transcript_scrolled: false, diff --git a/codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap b/codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap new file mode 100644 index 000000000..09d51329a --- /dev/null +++ b/codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_disabled.snap @@ -0,0 +1,5 @@ +--- +source: tui2/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" 100% context left " diff --git a/codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap b/codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap new file mode 100644 index 000000000..6f985f20c --- /dev/null +++ b/codex-rs/tui2/src/bottom_pane/snapshots/codex_tui2__bottom_pane__footer__tests__footer_context_only_queue_hint_enabled.snap @@ -0,0 +1,5 @@ +--- +source: tui2/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" 100% context left · tab to queue message " diff --git a/codex-rs/tui2/src/chatwidget.rs b/codex-rs/tui2/src/chatwidget.rs index 225d1b024..66eebde6b 100644 --- a/codex-rs/tui2/src/chatwidget.rs +++ b/codex-rs/tui2/src/chatwidget.rs @@ -2135,17 +2135,6 @@ impl ChatWidget { self.add_to_history(history_cell::new_user_prompt(message.to_string())); } - // If steer is enabled and a task is running, show hint about queuing with Tab - if self.config.features.enabled(Feature::Steer) && self.bottom_pane.is_task_running() { - use crate::key_hint; - use ratatui::text::Line; - let hint_line = Line::from(vec![ - "You can queue messages by pressing ".dim(), - key_hint::plain(KeyCode::Tab).into(), - ]); - self.add_to_history(history_cell::PlainHistoryCell::new(vec![hint_line])); - } - self.needs_final_message_separator = false; } diff --git a/codex-rs/tui2/src/chatwidget/snapshots/codex_tui2__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap b/codex-rs/tui2/src/chatwidget/snapshots/codex_tui2__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap index a447b748b..c7d553572 100644 --- a/codex-rs/tui2/src/chatwidget/snapshots/codex_tui2__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap +++ b/codex-rs/tui2/src/chatwidget/snapshots/codex_tui2__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap @@ -2,6 +2,33 @@ source: tui2/src/chatwidget/tests.rs expression: term.backend().vt100().screen().contents() --- + + + + + + + + + + + + + + + + + + + + + + + + + + + • I’m going to search the repo for where “Change Approved” is rendered to update that view. @@ -14,4 +41,4 @@ expression: term.backend().vt100().screen().contents() › Summarize recent commits - 100% context left + 100% context left · tab to queue message