From 591ffb1aaef4fb83afac02a2dd97c0f1b05c6647 Mon Sep 17 00:00:00 2001 From: Ian MacLeod Date: Wed, 23 Sep 2026 17:41:16 +0000 Subject: [PATCH] Prefer Shift-arrow hints for queued messages and questions (#47618) ## Why Queued-message edit hints could vary by terminal instead of showing the first configured shortcut. Use the keymap consistently for queued messages and async question navigation. ## What changed - Make `Shift+Left` the primary shortcut for advancing through questions and editing the last queued message, and `Shift+Right` for navigating back toward the composer. Keep `Alt+Up` and `Alt+Down` as alternatives. - Remove terminal-specific hint selection and derive hints from the configured keymap, including custom chords and unbound actions. - Update question footers, queued-input previews, and keymap picker snapshots to reflect the preferred shortcuts. ## Testing Extend tests to cover both default shortcut pairs, configured shortcut ordering, custom chord hints, and hiding the queued-message edit hint when the action is unbound. GitOrigin-RevId: e0f60860ef1e81c71ca0fc32369b8cbd12582b47 --- ...d_replay__misalignment_replayed_input.snap | 2 +- ...app__tests__disconnect__offline_draft.snap | 2 +- .../src/bottom_pane/async_questions/mod.rs | 2 - .../src/bottom_pane/async_questions/render.rs | 2 +- ...estions__tests__question_capped_other.snap | 3 +- ...tests__question_inline_history_search.snap | 2 +- ...uestions__tests__question_named_other.snap | 3 +- ...ests__question_options_width_boundary.snap | 2 +- ...ests__question_remapped_choice_footer.snap | 2 +- ..._tests__question_wrapped_named_option.snap | 2 +- ...stions__tests__question_wrapped_other.snap | 2 +- ...sts__question_wrapped_selected_option.snap | 2 +- .../async_questions/state_tests.rs | 3 +- codex-rs/tui/src/bottom_pane/mod.rs | 16 +- .../src/bottom_pane/pending_input_preview.rs | 8 +- codex-rs/tui/src/bottom_pane/questions.rs | 1 - ...view__tests__render_many_line_message.snap | 6 +- ...ests__render_more_than_three_messages.snap | 6 +- ...ut_preview__tests__render_one_message.snap | 6 +- ..._pending_steers_above_queued_messages.snap | 6 +- ...t_preview__tests__render_two_messages.snap | 6 +- ...review__tests__render_wrapped_message.snap | 6 +- ...n_tests__question_collapsed_countdown.snap | 2 +- ...s_visible_when_status_hidden_snapshot.snap | 2 +- ...__status_and_queued_messages_snapshot.snap | 2 +- ..._details_and_queued_messages_snapshot.snap | 2 +- codex-rs/tui/src/chatwidget.rs | 64 ------ codex-rs/tui/src/chatwidget/constructor.rs | 9 - codex-rs/tui/src/chatwidget/keymap_picker.rs | 6 - ...ts__hidden_shell_paste_queued_preview.snap | 2 +- ...tests__image_preparation_disconnected.snap | 2 +- ..._tests__image_preparation_in_progress.snap | 2 +- codex-rs/tui/src/chatwidget/tests.rs | 3 - .../chatwidget/tests/composer_submission.rs | 198 ++++-------------- codex-rs/tui/src/chatwidget/tests/helpers.rs | 38 ---- .../src/chatwidget/tests/questions_tests.rs | 100 +++++++-- ...c_question_answered_on_another_client.snap | 3 +- ...s__questions_tests__freeform_question.snap | 2 +- ...tions_tests__last_question_with_queue.snap | 2 +- ...ests__questions_tests__named_question.snap | 2 +- ...__questions_tests__oversized_question.snap | 2 +- ...tions_tests__question_queue_hint_Left.snap | 2 +- ...__questions_tests__question_vim_other.snap | 4 +- ..._questions_tests__question_vim_single.snap | 4 +- ...ests__questions_with_status_and_queue.snap | 2 +- ...ns_tests__queued_async_question_reply.snap | 3 +- ...ests__single_question_working_spacing.snap | 2 +- codex-rs/tui/src/keymap.rs | 8 +- codex-rs/tui/src/keymap_setup/actions.rs | 2 +- ...ap_setup__tests__keymap_picker_custom.snap | 4 +- ...ests__keymap_picker_fast_mode_enabled.snap | 4 +- ...up__tests__keymap_picker_global_chord.snap | 4 +- ...ap_setup__tests__keymap_picker_narrow.snap | 4 +- ...ymap_setup__tests__keymap_picker_wide.snap | 4 +- ...__keymap_question_action_descriptions.snap | 6 +- ...setup__tests__keymap_question_actions.snap | 4 +- 56 files changed, 202 insertions(+), 388 deletions(-) diff --git a/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__buffered_replay__misalignment_replayed_input.snap b/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__buffered_replay__misalignment_replayed_input.snap index 62c4ad734a..0b0bc95459 100644 --- a/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__buffered_replay__misalignment_replayed_input.snap +++ b/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__buffered_replay__misalignment_replayed_input.snap @@ -4,7 +4,7 @@ expression: "normalize_snapshot_paths(render_bottom_popup(&app.chat_widget, 80)) --- • Queued follow-up inputs ↳ keep queued - ⌥+↑ edit last queued message + shift+← edit last queued message › keep draft diff --git a/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__disconnect__offline_draft.snap b/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__disconnect__offline_draft.snap index 6313aaf5c7..a7f5173a83 100644 --- a/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__disconnect__offline_draft.snap +++ b/codex-rs/tui/src/app/tests/snapshots/codex_tui__app__tests__disconnect__offline_draft.snap @@ -7,7 +7,7 @@ expression: "format!(\"{}\\n{}\",\nlines_to_single_string(&app.transcript_cells. • Queued follow-up inputs ↳ queued before disconnect - ⌥+↑ edit last queued message + shift+← edit last queued message › keep this draft! diff --git a/codex-rs/tui/src/bottom_pane/async_questions/mod.rs b/codex-rs/tui/src/bottom_pane/async_questions/mod.rs index b0ff6f4d75..824fc2d7d3 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/mod.rs +++ b/codex-rs/tui/src/bottom_pane/async_questions/mod.rs @@ -67,7 +67,6 @@ pub(crate) struct AsyncQuestions { pub(crate) delivery_enabled: bool, pub(crate) submission: Option, visible_options: std::cell::Cell<(usize, usize)>, - pub(crate) next_hint: Option, keymap: RuntimeKeymap, // Ignore autorepeat from the number key that opened Other. other_selector: Option, @@ -103,7 +102,6 @@ impl AsyncQuestions { delivery_enabled: true, submission: None, visible_options: std::cell::Cell::new((0, 0)), - next_hint: None, keymap, other_selector: None, composer, diff --git a/codex-rs/tui/src/bottom_pane/async_questions/render.rs b/codex-rs/tui/src/bottom_pane/async_questions/render.rs index e712579da6..e13c8c64ea 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/render.rs +++ b/codex-rs/tui/src/bottom_pane/async_questions/render.rs @@ -209,7 +209,7 @@ impl AsyncQuestions { None }; if let Some(label) = next - && let Some(key) = self.next_hint + && let Some(key) = chat_hint("edit_queued_message") { tips.push(crate::footer_hint::shortcut(&key.display_label(), label)); } diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_capped_other.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_capped_other.snap index 09e5783d7b..4ed880ac8b 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_capped_other.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_capped_other.snap @@ -16,4 +16,5 @@ expression: buffer_text(&buffer) long draft - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip + shift+→ main prompt diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_inline_history_search.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_inline_history_search.snap index 6b2f5d253a..6d2565db54 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_inline_history_search.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_inline_history_search.snap @@ -12,4 +12,4 @@ expression: buffer_text(&buffer) reverse-i-search: e enter accept · esc enter submit ctrl+] skip option 13/13 - ⌥+↓ prev question + shift+→ prev question diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_named_other.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_named_other.snap index 20706d1bfe..6bf8f6aa91 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_named_other.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_named_other.snap @@ -9,4 +9,5 @@ expression: buffer_text(&buffer) › 1. Other 2. Other (write an answer) - enter submit ctrl+] skip ⌥+↓ prev question + enter submit ctrl+] skip + shift+→ prev question diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_options_width_boundary.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_options_width_boundary.snap index b141809437..e5de46ab14 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_options_width_boundary.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_options_width_boundary.snap @@ -14,7 +14,7 @@ Full height: enter submit ctrl+] skip - ⌥+↓ main prompt + shift+→ main prompt Clipped: diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_remapped_choice_footer.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_remapped_choice_footer.snap index 9f8bb1483e..8e143ef143 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_remapped_choice_footer.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_remapped_choice_footer.snap @@ -12,4 +12,4 @@ expression: buffer_text(&buffer) rows f9 submit ctrl+] skip option 1/13 - ⌥+↓ prev question + shift+→ prev question diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_named_option.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_named_option.snap index eaeaae1bdd..6216fcead5 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_named_option.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_named_option.snap @@ -11,4 +11,4 @@ expression: buffer_text(&buffer) 2. Other enter submit ctrl+] skip - ⌥+↓ main prompt + shift+→ main prompt diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_other.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_other.snap index d99153d310..4ab15014dd 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_other.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_other.snap @@ -13,4 +13,4 @@ expression: buffer_text(&buffer) rows enter submit ctrl+] skip - ⌥+↓ main prompt + shift+→ main prompt diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_selected_option.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_selected_option.snap index 48245c4716..d6786b87ed 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_selected_option.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_selected_option.snap @@ -10,4 +10,4 @@ expression: buffer_text(&clipped) multiple rows enter submit ctrl+] skip - option 2/3 ⌥+↓ main prompt + option 2/3 shift+→ main prompt diff --git a/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs b/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs index d98d412d1a..55f15cb8de 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs +++ b/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs @@ -17,7 +17,6 @@ fn editor() -> AsyncQuestions { /*disable_paste_burst*/ true, RuntimeKeymap::defaults(), ); - editor.next_hint = Some(crate::key_hint::alt(KeyCode::Up).into()); editor.append( "message", &[ @@ -446,7 +445,7 @@ fn existing_cross_context_keymaps_load_without_misleading_submit_hints() { let mut editor = editor(); editor.navigate(/*forward*/ true); editor.set_keymap(&keymap); - insta::allow_duplicates! { insta::assert_snapshot!(editor.footer_lines(/*width*/ 100, /*option_tip*/ None)[0].to_string(), @"ctrl+] skip ⌥+↓ prev question"); } + insta::allow_duplicates! { insta::assert_snapshot!(editor.footer_lines(/*width*/ 100, /*option_tip*/ None)[0].to_string(), @"ctrl+] skip shift+→ prev question"); } editor.handle_key_event(KeyEvent::from(KeyCode::F(12))); assert!(editor.submission.is_none()); } diff --git a/codex-rs/tui/src/bottom_pane/mod.rs b/codex-rs/tui/src/bottom_pane/mod.rs index 3f16d118e1..443e699388 100644 --- a/codex-rs/tui/src/bottom_pane/mod.rs +++ b/codex-rs/tui/src/bottom_pane/mod.rs @@ -510,6 +510,9 @@ impl BottomPane { if let Some(questions) = &mut self.questions { questions.set_keymap(keymap); } + // Show the first shortcut from the same keymap ChatWidget uses to handle queued edits. + self.pending_input_preview + .set_edit_binding(keymap.primary_hint(KeymapContext::Chat, "edit_queued_message")); let interrupt_binding = keymap.primary_hint(KeymapContext::Chat, "interrupt_turn"); self.pending_input_preview .set_interrupt_binding(interrupt_binding); @@ -605,19 +608,6 @@ impl BottomPane { self.request_redraw(); } - /// Update the key hint shown next to queued messages so it matches the - /// binding that `ChatWidget` actually listens for. - pub(crate) fn set_queued_message_edit_binding( - &mut self, - binding: Option, - ) { - self.pending_input_preview.set_edit_binding(binding); - if let Some(questions) = &mut self.questions { - questions.next_hint = binding; - } - self.request_redraw(); - } - pub(crate) fn set_vim_enabled(&mut self, enabled: bool) { self.composer.set_vim_enabled(enabled); if let Some(questions) = &mut self.questions { diff --git a/codex-rs/tui/src/bottom_pane/pending_input_preview.rs b/codex-rs/tui/src/bottom_pane/pending_input_preview.rs index 93328d52a6..e687ea1162 100644 --- a/codex-rs/tui/src/bottom_pane/pending_input_preview.rs +++ b/codex-rs/tui/src/bottom_pane/pending_input_preview.rs @@ -17,15 +17,13 @@ use crate::wrapping::adaptive_wrap_lines; /// steers explain that they will be submitted after the next tool/result /// boundary unless the user invokes the interrupt binding to send them /// immediately. The edit hint at the bottom only appears when there are actual -/// queued user inputs to pop back into the composer. Because some terminals -/// intercept certain modifier-key combinations, the displayed binding is +/// queued user inputs to pop back into the composer. The displayed binding is /// configurable via [`set_edit_binding`](Self::set_edit_binding). pub(crate) struct PendingInputPreview { pub pending_steers: Vec, pub rejected_steers: Vec, pub queued_messages: Vec, - /// Key combination rendered in the hint line. Defaults to Alt+Up but may - /// be overridden for terminals where that chord is unavailable. + /// Key combination rendered in the hint line. Defaults to Shift+Left. pub(super) edit_binding: Option, /// Key combination rendered for immediately interrupting and sending steers. interrupt_binding: Option, @@ -44,7 +42,7 @@ impl PendingInputPreview { pending_steers: Vec::new(), rejected_steers: Vec::new(), queued_messages: Vec::new(), - edit_binding: Some(key_hint::alt(KeyCode::Up).into()), + edit_binding: Some(key_hint::shift(KeyCode::Left).into()), interrupt_binding: Some(key_hint::plain(KeyCode::Esc).into()), } } diff --git a/codex-rs/tui/src/bottom_pane/questions.rs b/codex-rs/tui/src/bottom_pane/questions.rs index a507e7cb33..0251efa896 100644 --- a/codex-rs/tui/src/bottom_pane/questions.rs +++ b/codex-rs/tui/src/bottom_pane/questions.rs @@ -30,7 +30,6 @@ impl BottomPane { self.keymap.clone(), ); questions.set_vim_enabled(self.composer.is_vim_enabled()); - questions.next_hint = self.pending_input_preview.edit_binding; Box::new(questions) }) } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_many_line_message.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_many_line_message.snap index 5b1fa9576f..05e29dff55 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_many_line_message.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_many_line_message.snap @@ -10,7 +10,7 @@ Buffer { " a message ", " with many ", " … ", - " ⌥+↑ edit last queued message ", + " shift+← edit last queued message ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, @@ -25,7 +25,7 @@ Buffer { x: 0, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, x: 5, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 4, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD, - x: 7, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, - x: 32, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 11, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 36, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_more_than_three_messages.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_more_than_three_messages.snap index 55001e84c1..8b8bba6114 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_more_than_three_messages.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_more_than_three_messages.snap @@ -10,7 +10,7 @@ Buffer { " ↳ This is another message ", " ↳ This is a third message ", " ↳ This is a fourth message ", - " ⌥+↑ edit last queued message ", + " shift+← edit last queued message ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, @@ -28,7 +28,7 @@ Buffer { x: 4, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, x: 28, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 4, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD, - x: 7, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, - x: 32, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 11, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 36, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_one_message.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_one_message.snap index c13d75009f..887f9d20a6 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_one_message.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_one_message.snap @@ -7,7 +7,7 @@ Buffer { content: [ "• Queued follow-up inputs ", " ↳ Hello, world! ", - " ⌥+↑ edit last queued message ", + " shift+← edit last queued message ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, @@ -16,7 +16,7 @@ Buffer { x: 4, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, x: 17, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 4, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD, - x: 7, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, - x: 32, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 11, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 36, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_pending_steers_above_queued_messages.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_pending_steers_above_queued_messages.snap index b86b009111..59ee6e1787 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_pending_steers_above_queued_messages.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_pending_steers_above_queued_messages.snap @@ -15,7 +15,7 @@ Buffer { " ", "• Queued follow-up inputs ", " ↳ Queued follow-up question ", - " ⌥+↑ edit last queued message ", + " shift+← edit last queued message ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, @@ -38,7 +38,7 @@ Buffer { x: 4, y: 9, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, x: 29, y: 9, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 4, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD, - x: 7, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, - x: 32, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 11, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 36, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_two_messages.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_two_messages.snap index 6b5d65e161..07a1a1e341 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_two_messages.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_two_messages.snap @@ -8,7 +8,7 @@ Buffer { "• Queued follow-up inputs ", " ↳ Hello, world! ", " ↳ This is another message ", - " ⌥+↑ edit last queued message ", + " shift+← edit last queued message ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, @@ -20,7 +20,7 @@ Buffer { x: 4, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, x: 27, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 4, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD, - x: 7, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, - x: 32, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 11, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 36, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_wrapped_message.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_wrapped_message.snap index 546e1cdc05..3a88045764 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_wrapped_message.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_wrapped_message.snap @@ -9,7 +9,7 @@ Buffer { " ↳ This is a longer message that should", " be wrapped ", " ↳ This is another message ", - " ⌥+↑ edit last queued message ", + " shift+← edit last queued message ", ], styles: [ x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, @@ -23,7 +23,7 @@ Buffer { x: 4, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, x: 27, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, x: 4, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD, - x: 7, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, - x: 32, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 11, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 36, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__question_tests__question_collapsed_countdown.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__question_tests__question_collapsed_countdown.snap index fca5693129..91cb130fdd 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__question_tests__question_collapsed_countdown.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__question_tests__question_collapsed_countdown.snap @@ -3,4 +3,4 @@ source: tui/src/bottom_pane/questions_tests.rs expression: "pane.question_summary(now).unwrap().iter().map(ToString::to_string).collect::>().join(\"\\n\")" --- ? 2 questions · 15s - ⌥+↑ to answer + shift+← to answer diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__queued_messages_visible_when_status_hidden_snapshot.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__queued_messages_visible_when_status_hidden_snapshot.snap index 55b21ed7ac..ad1ea121c6 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__queued_messages_visible_when_status_hidden_snapshot.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__queued_messages_visible_when_status_hidden_snapshot.snap @@ -4,7 +4,7 @@ expression: "render_snapshot(&pane, area)" --- • Queued follow-up inputs ↳ Queued follow-up question - ⌥+↑ edit last queued message + shift+← edit last queued message › Ask Codex to do anything diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_queued_messages_snapshot.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_queued_messages_snapshot.snap index cae3680319..877ccd7b9f 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_queued_messages_snapshot.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_queued_messages_snapshot.snap @@ -6,7 +6,7 @@ expression: "render_snapshot(&pane, area)" • Queued follow-up inputs ↳ Queued follow-up question - ⌥+↑ edit last queued message + shift+← edit last queued message › Ask Codex to do anything diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_with_details_and_queued_messages_snapshot.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_with_details_and_queued_messages_snapshot.snap index 114c209aef..fd6db64388 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_with_details_and_queued_messages_snapshot.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_with_details_and_queued_messages_snapshot.snap @@ -8,7 +8,7 @@ expression: "render_snapshot(&pane, area)" • Queued follow-up inputs ↳ Queued follow-up question - ⌥+↑ edit last queued message + shift+← edit last queued message › Ask Codex to do anything diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 984a9582cc..c06ccbb941 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -150,10 +150,6 @@ use codex_protocol::plan_tool::StepStatus as UpdatePlanItemStatus; use codex_protocol::request_permissions::RequestPermissionsEvent; use codex_protocol::user_input::ByteRange; use codex_protocol::user_input::TextElement; -use codex_terminal_detection::Multiplexer; -use codex_terminal_detection::TerminalInfo; -use codex_terminal_detection::TerminalName; -use codex_terminal_detection::terminal_info; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; use crossterm::event::KeyCode; @@ -188,62 +184,6 @@ const TUI_STUB_MESSAGE: &str = "Not available in TUI yet."; const PARENT_OWNED_INPUT_MESSAGE: &str = "This sub-agent is controlled by its parent. Direct input is disabled."; -/// Choose the keybinding used to edit the most-recently queued message. -/// -/// Apple Terminal, Warp, and VSCode integrated terminals intercept or silently -/// swallow Alt+Up, and tmux does not reliably pass that chord through. We fall -/// back to Shift+Left for those environments while keeping the more discoverable -/// Alt+Up everywhere else. -/// -/// The match is exhaustive so that adding a new `TerminalName` variant forces -/// an explicit decision about which binding that terminal should use. -fn queued_message_edit_binding_for_terminal(terminal_info: TerminalInfo) -> KeyBinding { - if matches!( - terminal_info.multiplexer.as_ref(), - Some(Multiplexer::Tmux { .. }) - ) { - return key_hint::shift(KeyCode::Left); - } - - match terminal_info.name { - TerminalName::AppleTerminal | TerminalName::WarpTerminal | TerminalName::VsCode => { - key_hint::shift(KeyCode::Left) - } - TerminalName::Ghostty - | TerminalName::Iterm2 - | TerminalName::WezTerm - | TerminalName::Kitty - | TerminalName::Alacritty - | TerminalName::Konsole - | TerminalName::GnomeTerminal - | TerminalName::Vte - | TerminalName::WindowsTerminal - | TerminalName::Dumb - | TerminalName::Unknown => key_hint::alt(KeyCode::Up), - } -} - -fn queued_message_edit_hint_binding( - keymap: &RuntimeKeymap, - terminal_info: TerminalInfo, -) -> Option { - let configured = keymap.primary_hint(crate::keymap::KeymapContext::Chat, "edit_queued_message"); - if matches!( - configured, - Some(crate::key_hint::ShortcutHint::Chord { .. }) - ) { - return configured; - } - - let terminal_binding = queued_message_edit_binding_for_terminal(terminal_info); - keymap - .chat - .edit_queued_message - .contains(&terminal_binding) - .then_some(crate::key_hint::ShortcutHint::Single(terminal_binding)) - .or(configured) -} - fn normalize_thread_name(name: &str) -> Option { let trimmed = name.trim(); (!trimmed.is_empty()).then(|| trimmed.to_string()) @@ -744,10 +684,6 @@ pub(crate) struct ChatWidget { /// Main chat-surface bindings resolved from `tui.keymap.chat`. chat_keymap: ChatKeymap, permission_shortcut_pending: bool, - /// Keybinding to show for popping the most-recently queued message back - /// into the composer. This may differ from the first configured binding - /// when the default set includes a terminal-specific fallback. - queued_message_edit_hint_binding: Option, // Pending notification to show when unfocused on next Draw pending_notification: Option, /// When `Some`, the user has pressed a quit shortcut and the second press diff --git a/codex-rs/tui/src/chatwidget/constructor.rs b/codex-rs/tui/src/chatwidget/constructor.rs index 1c8c63ec78..ad28b084db 100644 --- a/codex-rs/tui/src/chatwidget/constructor.rs +++ b/codex-rs/tui/src/chatwidget/constructor.rs @@ -72,7 +72,6 @@ impl ChatWidget { &header_model, &model_catalog.try_list_models().unwrap_or_default(), ); - let current_terminal_info = terminal_info(); let runtime_keymap = RuntimeKeymap::from_config(&local_settings.tui.keymap).ok(); let default_keymap = RuntimeKeymap::defaults(); let copy_last_response_binding = runtime_keymap @@ -83,10 +82,6 @@ impl ChatWidget { .as_ref() .map(|keymap| keymap.chat.clone()) .unwrap_or_else(|| default_keymap.chat.clone()); - let queued_message_edit_hint_binding = queued_message_edit_hint_binding( - runtime_keymap.as_ref().unwrap_or(&default_keymap), - current_terminal_info, - ); let pet_http_client = codex_http_client::RouteAwareClientPool::new( config.http_client_factory(), codex_http_client::ClientRouteClass::Other, @@ -240,7 +235,6 @@ impl ChatWidget { safety_buffering_source: UserMessageSource::Prompt, chat_keymap, permission_shortcut_pending: false, - queued_message_edit_hint_binding, show_welcome_banner: is_first_run, startup_tooltip_override, suppress_session_configured_redraw: false, @@ -309,9 +303,6 @@ impl ChatWidget { .bottom_pane .set_voice_command_enabled(/*enabled*/ false); widget.sync_mentions_v2_enabled(); - widget - .bottom_pane - .set_queued_message_edit_binding(widget.queued_message_edit_hint_binding); widget.update_collaboration_mode_indicator(); widget diff --git a/codex-rs/tui/src/chatwidget/keymap_picker.rs b/codex-rs/tui/src/chatwidget/keymap_picker.rs index 6d482f2fd5..1db2956369 100644 --- a/codex-rs/tui/src/chatwidget/keymap_picker.rs +++ b/codex-rs/tui/src/chatwidget/keymap_picker.rs @@ -12,10 +12,8 @@ //! appear to accept a remap while some handlers still respond to the old keys. use codex_config::types::TuiKeymap; -use codex_terminal_detection::terminal_info; use super::ChatWidget; -use super::queued_message_edit_hint_binding; use crate::app_event::KeymapCaptureMode; use crate::app_event::KeymapEditIntent; use crate::keymap::RuntimeKeymap; @@ -173,10 +171,6 @@ impl ChatWidget { self.local_settings.tui.keymap = keymap_config; self.copy_last_response_binding = runtime_keymap.app.copy.clone(); self.chat_keymap = runtime_keymap.chat.clone(); - self.queued_message_edit_hint_binding = - queued_message_edit_hint_binding(runtime_keymap, terminal_info()); - self.bottom_pane - .set_queued_message_edit_binding(self.queued_message_edit_hint_binding); self.bottom_pane.set_keymap_bindings(runtime_keymap); self.update_realtime_footer(); self.request_redraw(); diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_shell_paste_queued_preview.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_shell_paste_queued_preview.snap index aa68492600..5eee3ce8c2 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_shell_paste_queued_preview.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__hidden_shell_paste_queued_preview.snap @@ -6,7 +6,7 @@ expression: "normalize_snapshot_paths(render_bottom_popup(&chat, 80))" • Queued follow-up inputs ↳ [Pasted Content 1006 chars] - ⌥+↑ edit last queued message + shift+← edit last queued message › Ask Codex to do anything diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_disconnected.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_disconnected.snap index 2295e42b63..b51aeb4daf 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_disconnected.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_disconnected.snap @@ -6,7 +6,7 @@ expression: "normalize_snapshot_paths(render_bottom_popup(&chat, 80)).replace(' • Queued follow-up inputs ↳ x - ⌥+↑ edit last queued message + shift+← edit last queued message › describe diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_in_progress.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_in_progress.snap index 95cd946f14..ff6ac12a4c 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_in_progress.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__image_preparation_in_progress.snap @@ -4,7 +4,7 @@ expression: "normalize_snapshot_paths(render_bottom_popup(&chat, 80))" --- • Queued follow-up inputs ↳ Preparing images: describe - ⌥+↑ edit last queued message + shift+← edit last queued message › x diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index c0ec4ad648..b1594c9829 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -159,9 +159,6 @@ pub(super) use codex_protocol::plan_tool::StepStatus; pub(super) use codex_protocol::plan_tool::UpdatePlanArgs; pub(super) use codex_protocol::request_permissions::RequestPermissionProfile; pub(super) use codex_protocol::user_input::TextElement; -pub(super) use codex_terminal_detection::Multiplexer; -pub(super) use codex_terminal_detection::TerminalInfo; -pub(super) use codex_terminal_detection::TerminalName; pub(super) use codex_utils_absolute_path::AbsolutePathBuf; pub(super) use codex_utils_approval_presets::builtin_approval_presets; pub(super) use codex_utils_path_uri::LegacyAppPathString; diff --git a/codex-rs/tui/src/chatwidget/tests/composer_submission.rs b/codex-rs/tui/src/chatwidget/tests/composer_submission.rs index 7302a40670..a5c107a4de 100644 --- a/codex-rs/tui/src/chatwidget/tests/composer_submission.rs +++ b/codex-rs/tui/src/chatwidget/tests/composer_submission.rs @@ -1924,54 +1924,42 @@ async fn restore_thread_input_state_applies_running_state_policy() { } #[tokio::test] -async fn alt_up_edits_most_recent_queued_message() { - let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; - chat.chat_keymap.edit_queued_message = vec![crate::key_hint::alt(KeyCode::Up)]; - chat.queued_message_edit_hint_binding = Some(crate::key_hint::alt(KeyCode::Up).into()); - chat.bottom_pane - .set_queued_message_edit_binding(chat.queued_message_edit_hint_binding); +async fn default_shortcuts_edit_most_recent_queued_message() { + for key in [ + KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT), + KeyEvent::new(KeyCode::Up, KeyModifiers::ALT), + ] { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + chat.bottom_pane.set_task_running(/*running*/ true); + for message in ["first queued", "second queued"] { + chat.input_queue + .queued_user_messages + .push_back(UserMessage::from(message.to_string()).into()); + } + chat.refresh_pending_input_preview(); - // Simulate a running task so messages would normally be queued. - chat.bottom_pane.set_task_running(/*running*/ true); + chat.handle_key_event(key); - // Seed two queued messages. - chat.input_queue - .queued_user_messages - .push_back(UserMessage::from("first queued".to_string()).into()); - chat.input_queue - .queued_user_messages - .push_back(UserMessage::from("second queued".to_string()).into()); - chat.refresh_pending_input_preview(); - - // Press Alt+Up to edit the most recent (last) queued message. - chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::ALT)); - - // Composer should now contain the last queued message. - assert_eq!( - chat.bottom_pane.composer_text(), - "second queued".to_string() - ); - // And the queue should now contain only the remaining (older) item. - assert_eq!(chat.input_queue.queued_user_messages.len(), 1); - assert_eq!( - chat.input_queue.queued_user_messages.front().unwrap().text, - "first queued" - ); + assert_eq!(chat.bottom_pane.composer_text(), "second queued"); + assert_eq!(chat.queued_user_message_texts(), vec!["first queued"]); + } } #[tokio::test] async fn unbound_queued_message_edit_does_not_fall_back_to_alt_up() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; - chat.chat_keymap.edit_queued_message = Vec::new(); - chat.queued_message_edit_hint_binding = None; - chat.bottom_pane - .set_queued_message_edit_binding(chat.queued_message_edit_hint_binding); + let mut config = codex_config::types::TuiKeymap::default(); + config.chat.edit_queued_message = Some(codex_config::types::KeybindingsSpec::Many(Vec::new())); + let keymap = RuntimeKeymap::from_config(&config).expect("valid unbound queued edit"); + chat.apply_keymap_update(config, &keymap); chat.bottom_pane.set_task_running(/*running*/ true); chat.input_queue .queued_user_messages .push_back(UserMessage::from("queued".to_string()).into()); chat.refresh_pending_input_preview(); + assert!(!render_bottom_popup(&chat, /*width*/ 100).contains("edit last queued message")); + chat.handle_key_event(KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT)); chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::ALT)); assert!(chat.bottom_pane.composer_text().is_empty()); @@ -1979,141 +1967,31 @@ async fn unbound_queued_message_edit_does_not_fall_back_to_alt_up() { } #[tokio::test] -async fn shift_left_edits_most_recent_queued_message_in_apple_terminal() { - assert_shift_left_edits_most_recent_queued_message_for_terminal(TerminalInfo { - name: TerminalName::AppleTerminal, - term_program: None, - version: None, - term: None, - multiplexer: None, - }) - .await; -} - -#[tokio::test] -async fn shift_left_edits_most_recent_queued_message_in_warp_terminal() { - assert_shift_left_edits_most_recent_queued_message_for_terminal(TerminalInfo { - name: TerminalName::WarpTerminal, - term_program: None, - version: None, - term: None, - multiplexer: None, - }) - .await; -} - -#[tokio::test] -async fn shift_left_edits_most_recent_queued_message_in_vscode_terminal() { - assert_shift_left_edits_most_recent_queued_message_for_terminal(TerminalInfo { - name: TerminalName::VsCode, - term_program: None, - version: None, - term: None, - multiplexer: None, - }) - .await; -} - -#[tokio::test] -async fn shift_left_edits_most_recent_queued_message_in_tmux() { - assert_shift_left_edits_most_recent_queued_message_for_terminal(TerminalInfo { - name: TerminalName::Iterm2, - term_program: None, - version: None, - term: None, - multiplexer: Some(Multiplexer::Tmux { version: None }), - }) - .await; -} - -#[test] -fn queued_message_edit_hint_displays_configured_chords() { +async fn queued_message_edit_hint_displays_configured_chords() { use codex_config::types::KeybindingSpec; use codex_config::types::KeybindingsSpec; use codex_config::types::TuiKeymap; - let terminal_info = || TerminalInfo { - name: TerminalName::Iterm2, - term_program: None, - version: None, - term: None, - multiplexer: None, - }; + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; let mut config = TuiKeymap::default(); config.chat.edit_queued_message = Some(KeybindingsSpec::One(KeybindingSpec( "ctrl-x up".to_string(), ))); let keymap = RuntimeKeymap::from_config(&config).expect("valid queued edit chord"); + chat.apply_keymap_update(config, &keymap); + chat.input_queue + .queued_user_messages + .push_back(UserMessage::from("queued".to_string()).into()); + chat.refresh_pending_input_preview(); - assert_eq!( - queued_message_edit_hint_binding(&keymap, terminal_info()), - Some(crate::key_hint::ShortcutHint::Chord { - prefix: crate::key_hint::ctrl(KeyCode::Char('x')), - completion: crate::key_hint::plain(KeyCode::Up), - }) - ); - - let default_keymap = RuntimeKeymap::defaults(); - assert_eq!( - queued_message_edit_hint_binding(&default_keymap, terminal_info()), - Some(crate::key_hint::ShortcutHint::Single(crate::key_hint::alt( - KeyCode::Up, - ))) - ); -} - -#[test] -fn queued_message_edit_binding_mapping_covers_special_terminals_and_tmux() { - assert_eq!( - queued_message_edit_binding_for_terminal(TerminalInfo { - name: TerminalName::AppleTerminal, - term_program: None, - version: None, - term: None, - multiplexer: None, - }), - crate::key_hint::shift(KeyCode::Left) - ); - assert_eq!( - queued_message_edit_binding_for_terminal(TerminalInfo { - name: TerminalName::WarpTerminal, - term_program: None, - version: None, - term: None, - multiplexer: None, - }), - crate::key_hint::shift(KeyCode::Left) - ); - assert_eq!( - queued_message_edit_binding_for_terminal(TerminalInfo { - name: TerminalName::VsCode, - term_program: None, - version: None, - term: None, - multiplexer: None, - }), - crate::key_hint::shift(KeyCode::Left) - ); - assert_eq!( - queued_message_edit_binding_for_terminal(TerminalInfo { - name: TerminalName::Iterm2, - term_program: None, - version: None, - term: None, - multiplexer: Some(Multiplexer::Tmux { version: None }), - }), - crate::key_hint::shift(KeyCode::Left) - ); - assert_eq!( - queued_message_edit_binding_for_terminal(TerminalInfo { - name: TerminalName::Iterm2, - term_program: None, - version: None, - term: None, - multiplexer: None, - }), - crate::key_hint::alt(KeyCode::Up) - ); + let hint = crate::key_hint::ShortcutHint::Chord { + prefix: crate::key_hint::ctrl(KeyCode::Char('x')), + completion: crate::key_hint::plain(KeyCode::Up), + }; + assert!(render_bottom_popup(&chat, /*width*/ 100).contains(&format!( + "{} edit last queued message", + hint.display_label() + ))); } /// Pressing Up to recall the most recent history entry and immediately queuing diff --git a/codex-rs/tui/src/chatwidget/tests/helpers.rs b/codex-rs/tui/src/chatwidget/tests/helpers.rs index 0584d97daf..cf9e79648f 100644 --- a/codex-rs/tui/src/chatwidget/tests/helpers.rs +++ b/codex-rs/tui/src/chatwidget/tests/helpers.rs @@ -2,7 +2,6 @@ use super::*; use codex_app_server_protocol::ImageGenerationItem; use codex_app_server_protocol::PluginAvailability; use codex_utils_absolute_path::test_support::PathExt; -use pretty_assertions::assert_eq; pub(super) async fn test_config() -> Config { // Start from the built-in defaults so tests do not inherit host/system config. @@ -1311,43 +1310,6 @@ pub(super) fn get_available_model(chat: &ChatWidget, model: &str) -> ModelPreset .unwrap_or_else(|| panic!("{model} preset not found")) } -pub(super) async fn assert_shift_left_edits_most_recent_queued_message_for_terminal( - terminal_info: TerminalInfo, -) { - let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; - chat.queued_message_edit_hint_binding = - Some(queued_message_edit_binding_for_terminal(terminal_info).into()); - chat.bottom_pane - .set_queued_message_edit_binding(chat.queued_message_edit_hint_binding); - - // Simulate a running task so messages would normally be queued. - chat.bottom_pane.set_task_running(/*running*/ true); - - // Seed two queued messages. - chat.input_queue - .queued_user_messages - .push_back(UserMessage::from("first queued".to_string()).into()); - chat.input_queue - .queued_user_messages - .push_back(UserMessage::from("second queued".to_string()).into()); - chat.refresh_pending_input_preview(); - - // Press Shift+Left to edit the most recent (last) queued message. - chat.handle_key_event(KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT)); - - // Composer should now contain the last queued message. - assert_eq!( - chat.bottom_pane.composer_text(), - "second queued".to_string() - ); - // And the queue should now contain only the remaining (older) item. - assert_eq!(chat.input_queue.queued_user_messages.len(), 1); - assert_eq!( - chat.input_queue.queued_user_messages.front().unwrap().text, - "first queued" - ); -} - pub(super) fn render_bottom_first_row(chat: &ChatWidget, width: u16) -> String { let height = chat.desired_height(width); let area = Rect::new(0, 0, width, height); diff --git a/codex-rs/tui/src/chatwidget/tests/questions_tests.rs b/codex-rs/tui/src/chatwidget/tests/questions_tests.rs index fcb5a6cbb0..a7cd7a2d2e 100644 --- a/codex-rs/tui/src/chatwidget/tests/questions_tests.rs +++ b/codex-rs/tui/src/chatwidget/tests/questions_tests.rs @@ -865,21 +865,93 @@ async fn questions_and_queued_messages_share_the_resolved_shortcut() { .queued_user_messages .push_back(UserMessage::from("queued".to_string()).into()); chat.refresh_pending_input_preview(); - for binding in [key_hint::shift(KeyCode::Left), key_hint::alt(KeyCode::Up)] { - chat.bottom_pane - .set_queued_message_edit_binding(Some(binding.into())); - let hint = binding.display_label(); - assert!(render_bottom_popup(&chat, /*width*/ 100).contains(&hint)); - chat.add_async_questions(&hint, &questions()); - assert!(render_bottom_popup(&chat, /*width*/ 100).contains(&format!("{hint} to answer"))); - let (key, modifiers) = binding.parts(); - chat.handle_key_event(KeyEvent::new(key, modifiers)); - insta::assert_snapshot!( - format!("question_queue_hint_{}", key), - render_bottom_popup(&chat, /*width*/ 100) - ); - chat.handle_key_event(KeyEvent::from(KeyCode::Esc)); + let forward_hint = key_hint::shift(KeyCode::Left).display_label(); + let backward_hint = key_hint::shift(KeyCode::Right).display_label(); + assert!( + render_bottom_popup(&chat, /*width*/ 100) + .contains(&format!("{forward_hint} edit last queued message")) + ); + chat.add_async_questions("message", &questions()); + assert!( + render_bottom_popup(&chat, /*width*/ 100).contains(&format!("{forward_hint} to answer")) + ); + + for (forward, backward) in [ + ( + KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT), + KeyEvent::new(KeyCode::Right, KeyModifiers::SHIFT), + ), + ( + KeyEvent::new(KeyCode::Up, KeyModifiers::ALT), + KeyEvent::new(KeyCode::Down, KeyModifiers::ALT), + ), + ] { + chat.handle_key_event(forward); + let rendered = render_bottom_popup(&chat, /*width*/ 100); + assert!(rendered.contains(&format!("{backward_hint} main prompt"))); + assert!(rendered.contains(&format!("{forward_hint} next question"))); + if forward.code == KeyCode::Left { + insta::assert_snapshot!( + "question_queue_hint_Left", + render_bottom_popup(&chat, /*width*/ 100) + ); + } + chat.handle_key_event(forward); + let rendered = render_bottom_popup(&chat, /*width*/ 100); + assert!(rendered.contains(&format!("{backward_hint} prev question"))); + assert!(rendered.contains(&format!("{forward_hint} queued messages"))); + chat.handle_key_event(backward); + assert!(render_bottom_popup(&chat, /*width*/ 100).contains("Which way?")); + chat.handle_key_event(backward); + assert!(!chat.bottom_pane.questions.as_ref().unwrap().expanded); } + + let config = toml::from_str( + r#" +[chat] +edit_queued_message = ["alt-up", "shift-left"] +prompt_stack_back = ["alt-down", "shift-right"] +"#, + ) + .unwrap(); + let keymap = RuntimeKeymap::from_config(&config).expect("valid alternate shortcuts"); + chat.apply_keymap_update(config, &keymap); + chat.add_async_questions("remapped", &questions()); + let forward_hint = key_hint::alt(KeyCode::Up).display_label(); + assert!( + render_bottom_popup(&chat, /*width*/ 100).contains(&format!("{forward_hint} to answer")) + ); + chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::ALT)); + insta::assert_snapshot!( + "question_queue_hint_Up", + render_bottom_popup(&chat, /*width*/ 100) + ); + + let config = toml::from_str( + r#" +[chat] +edit_queued_message = ["ctrl-x up", "shift-left"] +prompt_stack_back = ["ctrl-x down", "shift-right"] +"#, + ) + .unwrap(); + let keymap = RuntimeKeymap::from_config(&config).expect("valid configured chords"); + chat.apply_keymap_update(config, &keymap); + let hint = |completion| { + crate::key_hint::ShortcutHint::Chord { + prefix: key_hint::ctrl(KeyCode::Char('x')), + completion: key_hint::plain(completion), + } + .display_label() + }; + let rendered = render_bottom_popup(&chat, /*width*/ 100); + assert!(rendered.contains(&format!("{} main prompt", hint(KeyCode::Down)))); + assert!(rendered.contains(&format!("{} next question", hint(KeyCode::Up)))); + chat.handle_key_event(KeyEvent::new(KeyCode::Right, KeyModifiers::SHIFT)); + assert!( + render_bottom_popup(&chat, /*width*/ 100) + .contains(&format!("{} to answer", hint(KeyCode::Up))) + ); } #[tokio::test] diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__async_question_answered_on_another_client.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__async_question_answered_on_another_client.snap index e2206d6079..dbff57f466 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__async_question_answered_on_another_client.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__async_question_answered_on_another_client.snap @@ -1,6 +1,5 @@ --- source: tui/src/chatwidget/tests/questions_tests.rs -assertion_line: 937 expression: "render_bottom_popup(&chat, 80)" --- • Queued follow-up inputs @@ -9,4 +8,4 @@ expression: "render_bottom_popup(&chat, 80)" My draft for the second question - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__freeform_question.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__freeform_question.snap index e1c6e93803..d5152c26f4 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__freeform_question.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__freeform_question.snap @@ -10,4 +10,4 @@ expression: "render_bottom_popup(&chat, 80)" Type your answer - enter submit ctrl+] skip ⌥+↓ main prompt ⌥+↑ next question + enter submit ctrl+] skip shift+→ main prompt shift+← next question diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__last_question_with_queue.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__last_question_with_queue.snap index e905f75705..6d5f9e50fc 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__last_question_with_queue.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__last_question_with_queue.snap @@ -12,4 +12,4 @@ expression: "render_bottom_popup(&chat, 80)" › 1. Next 2. Other - enter submit ctrl+] skip ⌥+↓ prev question ⌥+↑ queued messages + enter submit ctrl+] skip shift+→ prev question shift+← queued messages diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__named_question.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__named_question.snap index 80904e34cf..482c920dec 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__named_question.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__named_question.snap @@ -14,4 +14,4 @@ expression: "render_bottom_popup(&chat, 80)" xxxxxxxxxxxxxxxx but do not deploy 2. Other - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__oversized_question.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__oversized_question.snap index 90a3db2310..caca3916c9 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__oversized_question.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__oversized_question.snap @@ -8,4 +8,4 @@ expression: "render_bottom_popup(&chat, 80)" Type your answer - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_queue_hint_Left.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_queue_hint_Left.snap index 156892cad4..886508e1aa 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_queue_hint_Left.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_queue_hint_Left.snap @@ -11,4 +11,4 @@ expression: "render_bottom_popup(&chat, 100)" Type your answer - enter submit ctrl+] skip ⌥+↓ main prompt shift+← next question + enter submit ctrl+] skip shift+→ main prompt shift+← next question diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_other.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_other.snap index a8d5be4d7c..308cf5b03a 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_other.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_other.snap @@ -11,7 +11,7 @@ NORMAL › 2. alpha beta alpha gamma Vim: Normal - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt SEARCH • Queued follow-up inputs @@ -23,4 +23,4 @@ SEARCH alpha gamma /gamma Vim: Normal - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_single.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_single.snap index bc7b8bf14b..9d4e9db68f 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_single.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__question_vim_single.snap @@ -10,7 +10,7 @@ NORMAL alpha beta alpha gamma Vim: Normal - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt SEARCH • Queued follow-up inputs @@ -21,4 +21,4 @@ SEARCH alpha gamma /gamma Vim: Normal - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__questions_with_status_and_queue.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__questions_with_status_and_queue.snap index 6ce74d9a83..825487980e 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__questions_with_status_and_queue.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__questions_with_status_and_queue.snap @@ -11,4 +11,4 @@ OPEN ONLY QUESTIONS • Queued follow-up inputs ? 2 questions - ⌥+↑ to answer + shift+← to answer diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__queued_async_question_reply.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__queued_async_question_reply.snap index ba162e47e1..349dfffaa7 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__queued_async_question_reply.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__queued_async_question_reply.snap @@ -1,6 +1,5 @@ --- source: tui/src/chatwidget/tests/questions_tests.rs -assertion_line: 51 expression: "render_bottom_popup(&chat, 80)" --- • Queued follow-up inputs @@ -12,4 +11,4 @@ expression: "render_bottom_popup(&chat, 80)" Type your answer - enter submit ctrl+] skip ⌥+↓ main prompt ⌥+↑ queued messages + enter submit ctrl+] skip shift+→ main prompt shift+← queued messages diff --git a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__single_question_working_spacing.snap b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__single_question_working_spacing.snap index be181f5135..0944e4e969 100644 --- a/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__single_question_working_spacing.snap +++ b/codex-rs/tui/src/chatwidget/tests/snapshots/codex_tui__chatwidget__tests__questions_tests__single_question_working_spacing.snap @@ -10,4 +10,4 @@ expression: rendered A typed answer - enter submit ctrl+] skip ⌥+↓ main prompt + enter submit ctrl+] skip shift+→ main prompt diff --git a/codex-rs/tui/src/keymap.rs b/codex-rs/tui/src/keymap.rs index 0bd2417914..2ff06d2296 100644 --- a/codex-rs/tui/src/keymap.rs +++ b/codex-rs/tui/src/keymap.rs @@ -142,7 +142,7 @@ pub(crate) struct ChatKeymap { pub(crate) previous_permission_mode: Vec, /// Switch to the next available permission mode. pub(crate) next_permission_mode: Vec, - /// Move up through async questions, then edit the most recently queued message. + /// Move forward through async questions, then edit the most recently queued message. pub(crate) edit_queued_message: Vec, /// Move back through async questions toward the composer. pub(crate) prompt_stack_back: Vec, @@ -1672,8 +1672,8 @@ impl RuntimeKeymap { ], previous_permission_mode: default_bindings![], next_permission_mode: default_bindings![], - edit_queued_message: default_bindings![alt(KeyCode::Up), shift(KeyCode::Left)], - prompt_stack_back: default_bindings![alt(KeyCode::Down), shift(KeyCode::Right)], + edit_queued_message: default_bindings![shift(KeyCode::Left), alt(KeyCode::Up)], + prompt_stack_back: default_bindings![shift(KeyCode::Right), alt(KeyCode::Down)], skip_question: default_bindings![ctrl(KeyCode::Char(']'))], }, composer: ComposerKeymap { @@ -2944,7 +2944,7 @@ mod tests { ); assert_eq!( runtime.chat.edit_queued_message, - vec![key_hint::alt(KeyCode::Up), key_hint::shift(KeyCode::Left)] + vec![key_hint::shift(KeyCode::Left), key_hint::alt(KeyCode::Up)] ); assert_eq!( runtime.composer.history_search_previous, diff --git a/codex-rs/tui/src/keymap_setup/actions.rs b/codex-rs/tui/src/keymap_setup/actions.rs index 1f733ca976..6359ecac8a 100644 --- a/codex-rs/tui/src/keymap_setup/actions.rs +++ b/codex-rs/tui/src/keymap_setup/actions.rs @@ -102,7 +102,7 @@ pub(super) const KEYMAP_ACTIONS: &[KeymapActionDescriptor] = &[ action("chat", "Chat", "increase_reasoning_effort", "Increase reasoning effort."), action("chat", "Chat", "previous_permission_mode", "Switch to the previous available permission mode."), action("chat", "Chat", "next_permission_mode", "Switch to the next available permission mode."), - action("chat", "Chat", "edit_queued_message", "Move up through questions, then edit the last queued message."), + action("chat", "Chat", "edit_queued_message", "Move forward through questions, then edit the last queued message."), action("chat", "Chat", "prompt_stack_back", "Move back through questions toward the composer."), action("chat", "Chat", "skip_question", "Skip the focused question."), action("chat", "Chat", "toggle_voice", "Start or stop a voice conversation."), diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_custom.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_custom.snap index 6297777d7d..07cd8e07f1 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_custom.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_custom.snap @@ -26,8 +26,8 @@ expression: "render_picker(params, 120)" Chat Increase Reasoning Effort alt-., shift-up Chat - Previous Permission Mode unbound Chat - Next Permission Mode unbound - Chat Edit Queued Message alt-up, shift-left - Chat Prompt Stack Back alt-down, shift-right + Chat Edit Queued Message shift-left, alt-up + Chat Prompt Stack Back shift-right, alt-down Chat Skip Question ctrl-] Chat Toggle Voice f8 Chat Toggle Voice Mute ctrl-x diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_fast_mode_enabled.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_fast_mode_enabled.snap index 068133af8b..9ed6743cbd 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_fast_mode_enabled.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_fast_mode_enabled.snap @@ -27,8 +27,8 @@ expression: "render_picker(params, 120)" Chat Increase Reasoning Effort alt-., shift-up Chat - Previous Permission Mode unbound Chat - Next Permission Mode unbound - Chat Edit Queued Message alt-up, shift-left - Chat Prompt Stack Back alt-down, shift-right + Chat Edit Queued Message shift-left, alt-up + Chat Prompt Stack Back shift-right, alt-down Chat Skip Question ctrl-] Chat Toggle Voice f8 Chat Toggle Voice Mute ctrl-x diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_global_chord.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_global_chord.snap index d994c06c26..e5e27f4066 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_global_chord.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_global_chord.snap @@ -26,8 +26,8 @@ expression: "render_picker(params, 120)" Chat Increase Reasoning Effort alt-., shift-up Chat - Previous Permission Mode unbound Chat - Next Permission Mode unbound - Chat Edit Queued Message alt-up, shift-left - Chat Prompt Stack Back alt-down, shift-right + Chat Edit Queued Message shift-left, alt-up + Chat Prompt Stack Back shift-right, alt-down Chat Skip Question ctrl-] Chat Toggle Voice f8 Chat - Toggle Voice Mute unbound diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_narrow.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_narrow.snap index 13557c6377..32f1127699 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_narrow.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_narrow.snap @@ -26,8 +26,8 @@ expression: "render_picker(params, 78)" Chat Increase Reasoning Effort alt-., shift-up Chat - Previous Permission Mode unbound Chat - Next Permission Mode unbound - Chat Edit Queued Message alt-up, shift-left - Chat Prompt Stack Back alt-down, shift-right + Chat Edit Queued Message shift-left, alt-up + Chat Prompt Stack Back shift-right, alt-down Chat Skip Question ctrl-] Chat Toggle Voice f8 Chat Toggle Voice Mute ctrl-x diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_wide.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_wide.snap index e6a30e77b5..a3ecd77419 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_wide.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_wide.snap @@ -26,8 +26,8 @@ expression: "render_picker(params, 120)" Chat Increase Reasoning Effort alt-., shift-up Chat - Previous Permission Mode unbound Chat - Next Permission Mode unbound - Chat Edit Queued Message alt-up, shift-left - Chat Prompt Stack Back alt-down, shift-right + Chat Edit Queued Message shift-left, alt-up + Chat Prompt Stack Back shift-right, alt-down Chat Skip Question ctrl-] Chat Toggle Voice f8 Chat Toggle Voice Mute ctrl-x diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_action_descriptions.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_action_descriptions.snap index 22c1280c45..ee640450cf 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_action_descriptions.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_action_descriptions.snap @@ -5,14 +5,14 @@ expression: descriptions Edit Shortcut Edit Queued Message · Chat - Current alt-up, shift-left · Default keymap + Current shift-left, alt-up · Default keymap Config `tui.keymap.chat.edit_queued_message` - Move up through questions, then edit the last queued message. + Move forward through questions, then edit the last queued message. Edit Shortcut Prompt Stack Back · Chat - Current alt-down, shift-right · Default keymap + Current shift-right, alt-down · Default keymap Config `tui.keymap.chat.prompt_stack_back` Move back through questions toward the composer. diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_actions.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_actions.snap index d158d4a8f2..459e314dc2 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_actions.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_question_actions.snap @@ -26,8 +26,8 @@ expression: "render_picker(params, 120)" Chat Increase Reasoning Effort alt-., shift-up Chat - Previous Permission Mode unbound Chat - Next Permission Mode unbound - Chat Edit Queued Message alt-up, shift-left - Chat Prompt Stack Back alt-down, shift-right + Chat Edit Queued Message shift-left, alt-up + Chat Prompt Stack Back shift-right, alt-down › Chat Skip Question ctrl-] Chat Toggle Voice f8 Chat Toggle Voice Mute ctrl-x