diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index f5fc9ee1a2..4bb2998897 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -747,6 +747,8 @@ impl ChatComposer { .primary_hint(KeymapContext::Global, "open_transcript"), find_transcript_key: default_keymap .primary_hint(KeymapContext::Global, "find_transcript"), + focus_activity_key: default_keymap + .primary_hint(KeymapContext::Global, "focus_activity"), insert_newline_key: footer_insert_newline_key( &default_keymap.editor.insert_newline, use_shift_enter_hint, @@ -1031,6 +1033,8 @@ impl ChatComposer { keymap.primary_hint(KeymapContext::Global, "open_transcript"); self.footer.find_transcript_key = keymap.primary_hint(KeymapContext::Global, "find_transcript"); + self.footer.focus_activity_key = + keymap.primary_hint(KeymapContext::Global, "focus_activity"); self.footer.insert_newline_key = match keymap.primary_hint(KeymapContext::Editor, "insert_newline") { hint @ Some(ShortcutHint::Chord { .. }) => hint, @@ -3864,6 +3868,7 @@ impl ChatComposer { edit_previous: Some(key_hint::plain(KeyCode::Esc).into()), show_transcript: self.footer.show_transcript_key, find_transcript: self.footer.find_transcript_key, + focus_activity: self.footer.focus_activity_key, history_search: self.footer.history_search_key, reasoning_down: self.footer.reasoning_down_key, reasoning_up: self.footer.reasoning_up_key, diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs b/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs index 492f0f299e..fe4a6dc186 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs @@ -222,6 +222,7 @@ pub(super) struct FooterState { pub(super) show_warnings_key: Option, pub(super) warning_notice_area: std::cell::Cell>, pub(super) find_transcript_key: Option, + pub(super) focus_activity_key: Option, pub(super) insert_newline_key: Option, pub(super) queue_key: Option, pub(super) toggle_shortcuts_key: Option, diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_hides_visible_sparkles_without_restarting_their_deadline.snap b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_hides_visible_sparkles_without_restarting_their_deadline.snap index c150d7acf6..3016ed7189 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_hides_visible_sparkles_without_restarting_their_deadline.snap +++ b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_hides_visible_sparkles_without_restarting_their_deadline.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/chat_composer/sparkle_tests.rs -assertion_line: 452 expression: "snapshots.join(\"\\n\\n\")" --- expires during help: false @@ -16,11 +15,12 @@ shortcut help: / Commands tab Send message @ Mention files ⌥+, Less reasoning ! Shell command ⌥+. More reasoning - ctrl+j New line ctrl+c Quit - ctrl+v Paste image - ctrl+g External editor Transcript (open first) - ctrl+r Search history ctrl+t Open transcript - esc esc Edit last message f3 Find text + ctrl+j New line f4 Inspect activity + ctrl+v Paste image ctrl+c Quit + ctrl+g External editor + ctrl+r Search history Transcript (open first) + esc esc Edit last message ctrl+t Open transcript + f3 Find text pgup / pgdn Scroll ctrl+space Start selection ctrl+home / ctrl+end Top / latest @@ -50,11 +50,12 @@ shortcut help: / Commands tab Send message @ Mention files ⌥+, Less reasoning ! Shell command ⌥+. More reasoning - ctrl+j New line ctrl+c Quit - ctrl+v Paste image - ctrl+g External editor Transcript (open first) - ctrl+r Search history ctrl+t Open transcript - esc esc Edit last message f3 Find text + ctrl+j New line f4 Inspect activity + ctrl+v Paste image ctrl+c Quit + ctrl+g External editor + ctrl+r Search history Transcript (open first) + esc esc Edit last message ctrl+t Open transcript + f3 Find text pgup / pgdn Scroll ctrl+space Start selection ctrl+home / ctrl+end Top / latest diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_preserves_an_unused_sparkle_but_literal_question_marks_do_not.snap b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_preserves_an_unused_sparkle_but_literal_question_marks_do_not.snap index 7dbc122cdf..bcebcd4bb5 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_preserves_an_unused_sparkle_but_literal_question_marks_do_not.snap +++ b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__sparkle__tests__shortcut_help_preserves_an_unused_sparkle_but_literal_question_marks_do_not.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/chat_composer/sparkle_tests.rs -assertion_line: 365 expression: "snapshots.join(\"\\n\\n\")" --- shortcut help: @@ -10,11 +9,12 @@ shortcut help: / Commands tab Send message @ Mention files ⌥+, Less reasoning ! Shell command ⌥+. More reasoning - ctrl+j New line ctrl+c Quit - ctrl+v Paste image - ctrl+g External editor Transcript (open first) - ctrl+r Search history ctrl+t Open transcript - esc esc Edit last message f3 Find text + ctrl+j New line f4 Inspect activity + ctrl+v Paste image ctrl+c Quit + ctrl+g External editor + ctrl+r Search history Transcript (open first) + esc esc Edit last message ctrl+t Open transcript + f3 Find text pgup / pgdn Scroll ctrl+space Start selection ctrl+home / ctrl+end Top / latest diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__activity_shortcut_help_follows_runtime_binding.snap b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__activity_shortcut_help_follows_runtime_binding.snap new file mode 100644 index 0000000000..1d3d9c8311 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__activity_shortcut_help_follows_runtime_binding.snap @@ -0,0 +1,87 @@ +--- +source: tui/src/bottom_pane/chat_composer/status_surface_tests.rs +expression: "snapshots.join(\"\\n---\\n\")" +--- +"f4" + + + + + + + + + Keyboard shortcuts + + Compose Session Transcript (open first) + / Commands tab Send message ctrl+t Open transcript + @ Mention files ⌥+, Less reasoning f3 Find text + ! Shell command ⌥+. More reasoning pgup / pgdn Scroll + ctrl+j New line f4 Inspect activity ctrl+space Start selection + ctrl+v Paste image ctrl+c Quit ctrl+home / ctrl+end Top / latest + ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest + ctrl+r Search history + esc esc Edit last message + + /keymap customize + +› Ask Codex + + MODEL · ~/project · Context 20% used + ? / esc close +--- +"ctrl-x t" + + + + + + + + + Keyboard shortcuts + + Compose Session Transcript (open first) + / Commands tab Send message ctrl+t Open transcript + @ Mention files ⌥+, Less reasoning f3 Find text + ! Shell command ⌥+. More reasoning pgup / pgdn Scroll + ctrl+j New line ctrl+x t Inspect activity ctrl+space Start selection + ctrl+v Paste image ctrl+c Quit ctrl+home / ctrl+end Top / latest + ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest + ctrl+r Search history + esc esc Edit last message + + /keymap customize + +› Ask Codex + + MODEL · ~/project · Context 20% used + ? / esc close +--- +[] + + + + + + + + + Keyboard shortcuts + + Compose Session Transcript (open first) + / Commands tab Send message ctrl+t Open transcript + @ Mention files ⌥+, Less reasoning f3 Find text + ! Shell command ⌥+. More reasoning pgup / pgdn Scroll + ctrl+j New line ctrl+c Quit ctrl+space Start selection + ctrl+v Paste image ctrl+home / ctrl+end Top / latest + ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest + ctrl+r Search history + esc esc Edit last message + + /keymap customize + +› Ask Codex + + MODEL · ~/project · Context 20% used + ? / esc close diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_narrow.snap b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_narrow.snap index e733a7bf45..84f7c14204 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_narrow.snap +++ b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_narrow.snap @@ -26,6 +26,7 @@ expression: text tab Send message ⌥+, Less reasoning ⌥+. More reasoning + f4 Inspect activity ctrl+c Quit Transcript (open first) diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_wide.snap b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_wide.snap index 6664b64357..304ad3ed39 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_wide.snap +++ b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__status_surface__tests__shortcut_help_above_wide.snap @@ -12,13 +12,13 @@ expression: text Keyboard shortcuts - Compose Session Transcript (open first) - / Commands tab Send message ctrl+t Open transcript - @ Mention files ⌥+, Less reasoning f3 Find text - ! Shell command ⌥+. More reasoning pgup / pgdn Scroll - ctrl+j New line ctrl+c Quit ctrl+space Start selection - ctrl+v Paste image ctrl+home / ctrl+end Top / latest - ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest + Compose Session Transcript (open first) + / Commands tab Send message ctrl+t Open transcript + @ Mention files ⌥+, Less reasoning f3 Find text + ! Shell command ⌥+. More reasoning pgup / pgdn Scroll + ctrl+j New line f4 Inspect activity ctrl+space Start selection + ctrl+v Paste image ctrl+c Quit ctrl+home / ctrl+end Top / latest + ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest ctrl+r Search history esc esc Edit last message diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/status_surface_tests.rs b/codex-rs/tui/src/bottom_pane/chat_composer/status_surface_tests.rs index f2b1b0dc68..4a7bc01f8a 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer/status_surface_tests.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer/status_surface_tests.rs @@ -362,3 +362,25 @@ fn slash_popup_preserves_footer_and_selection_style() { .collect(); assert!(footer_text.contains("transcript status")); } + +#[test] +fn activity_shortcut_help_follows_runtime_binding() { + let mut snapshots = Vec::new(); + for configured in [ + serde_json::json!("f4"), + serde_json::json!("ctrl-x t"), + serde_json::json!([]), + ] { + let config = serde_json::from_value(serde_json::json!({ + "global": {"focus_activity": configured} + })) + .unwrap(); + let keymap = crate::keymap::RuntimeKeymap::from_config(&config).unwrap(); + let mut composer = composer(); + composer.set_keymap_bindings(&keymap); + composer.handle_key_event(KeyEvent::new(KeyCode::Char('?'), KeyModifiers::NONE)); + let (text, _) = render(&composer, /*width*/ 130, /*footer*/ None); + snapshots.push(format!("{configured}\n{text}")); + } + insta::assert_snapshot!(snapshots.join("\n---\n")); +} diff --git a/codex-rs/tui/src/bottom_pane/footer.rs b/codex-rs/tui/src/bottom_pane/footer.rs index e05190d795..b81f2c3151 100644 --- a/codex-rs/tui/src/bottom_pane/footer.rs +++ b/codex-rs/tui/src/bottom_pane/footer.rs @@ -120,6 +120,7 @@ pub(crate) struct FooterKeyHints { pub(crate) edit_previous: Option, pub(crate) show_transcript: Option, pub(crate) find_transcript: Option, + pub(crate) focus_activity: Option, pub(crate) history_search: Option, pub(crate) reasoning_down: Option, pub(crate) reasoning_up: Option, @@ -138,6 +139,7 @@ impl FooterKeyHints { edit_previous: Some(key_hint::plain(KeyCode::Esc).into()), show_transcript: Some(key_hint::ctrl(KeyCode::Char('t')).into()), find_transcript: Some(key_hint::plain(KeyCode::F(3)).into()), + focus_activity: Some(key_hint::plain(KeyCode::F(4)).into()), history_search: Some(key_hint::ctrl(KeyCode::Char('r')).into()), reasoning_down: Some(key_hint::alt(KeyCode::Char(',')).into()), reasoning_up: Some(key_hint::alt(KeyCode::Char('.')).into()), diff --git a/codex-rs/tui/src/bottom_pane/shortcut_overlay.rs b/codex-rs/tui/src/bottom_pane/shortcut_overlay.rs index ac6cd657c1..d38b96790a 100644 --- a/codex-rs/tui/src/bottom_pane/shortcut_overlay.rs +++ b/codex-rs/tui/src/bottom_pane/shortcut_overlay.rs @@ -75,6 +75,7 @@ pub(super) fn lines(props: &FooterProps, width: u16) -> Vec> { session.push(hints.reasoning_up, "More reasoning"); session.push(hints.toggle_voice, "Voice"); session.push(hints.agents, "Agents (empty prompt)"); + session.push(hints.focus_activity, "Inspect activity"); session.entries.push(Shortcut::new( key_hint::ctrl(KeyCode::Char('c')), if props.is_task_running { diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__agents_navigation_help.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__agents_navigation_help.snap index 23735ab827..4fd8fbc9a1 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__agents_navigation_help.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__agents_navigation_help.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/chat_composer.rs -assertion_line: 5294 expression: terminal.backend() --- " Keyboard shortcuts " @@ -10,10 +9,11 @@ expression: terminal.backend() " @ Mention files ⌥+, Less reasoning " " ! Shell command ⌥+. More reasoning " " ctrl+j New line ← Agents (empty prompt) " -" ctrl+v Paste image ctrl+c Quit " -" ctrl+g External editor " -" ctrl+r Search history Transcript (open first) " -" esc esc Edit last message ctrl+t Open transcript " +" ctrl+v Paste image f4 Inspect activity " +" ctrl+g External editor ctrl+c Quit " +" ctrl+r Search history " +" esc esc Edit last message Transcript (open first) " +" ctrl+t Open transcript " " f3 Find text " " pgup / pgdn Scroll " " ctrl+space Start selection " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_configured_key_chords.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_configured_key_chords.snap index 16b106d609..ac5dc30da6 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_configured_key_chords.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_configured_key_chords.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/chat_composer.rs -assertion_line: 5620 expression: terminal.backend() --- " Keyboard shortcuts " @@ -9,11 +8,12 @@ expression: terminal.backend() " / Commands tab Send message " " @ Mention files ⌥+, Less reasoning " " ! Shell command ⌥+. More reasoning " -" ctrl+x enter New line ctrl+c Quit " -" ctrl+v Paste image " -" ctrl+g ctrl+g External editor Transcript (open first) " -" ctrl+r Search history ctrl+t Open transcript " -" esc esc Edit last message f3 Find text " +" ctrl+x enter New line f4 Inspect activity " +" ctrl+v Paste image ctrl+c Quit " +" ctrl+g ctrl+g External editor " +" ctrl+r Search history Transcript (open first) " +" esc esc Edit last message ctrl+t Open transcript " +" f3 Find text " " pgup / pgdn Scroll " " ctrl+space Start selection " " ctrl+home / ctrl+end Top / latest " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay.snap index 4ae2e3f440..3b60d2cf47 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay.snap @@ -1,19 +1,23 @@ --- source: tui/src/bottom_pane/chat_composer.rs -assertion_line: 5294 expression: terminal.backend() --- " Keyboard shortcuts " " " -" Compose Session Transcript (open first) " -" / Commands tab Send message ctrl+t Open transcript" -" @ Mention files ⌥+, Less reasoning f3 Find text " -" ! Shell command ⌥+. More reasoning pgup / pgdn Scroll " -" shift+enter New line ctrl+c Quit ctrl+space Start selection" -" ctrl+v Paste image ctrl+home / ctrl+end Top / latest " -" ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest " -" ctrl+r Search history " -" esc Edit last message " +" Compose Session " +" / Commands tab Send message " +" @ Mention files ⌥+, Less reasoning " +" ! Shell command ⌥+. More reasoning " +" shift+enter New line f4 Inspect activity " +" ctrl+v Paste image ctrl+c Quit " +" ctrl+g External editor " +" ctrl+r Search history Transcript (open first) " +" esc Edit last message ctrl+t Open transcript " +" f3 Find text " +" pgup / pgdn Scroll " +" ctrl+space Start selection " +" ctrl+home / ctrl+end Top / latest " +" shift+⌥+, / shift+⌥+. Top / latest " " " " /keymap customize " " " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay_queue_submissions.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay_queue_submissions.snap index 12ec62a631..2c3774ef66 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay_queue_submissions.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay_queue_submissions.snap @@ -1,19 +1,23 @@ --- source: tui/src/bottom_pane/chat_composer.rs -assertion_line: 5294 expression: terminal.backend() --- " Keyboard shortcuts " " " -" Compose Session Transcript (open first) " -" / Commands tab Queue message ctrl+t Open transcript" -" @ Mention files ⌥+, Less reasoning f3 Find text " -" ! Shell command ⌥+. More reasoning pgup / pgdn Scroll " -" shift+enter New line ctrl+c Quit ctrl+space Start selection" -" ctrl+v Paste image ctrl+home / ctrl+end Top / latest " -" ctrl+g External editor shift+⌥+, / shift+⌥+. Top / latest " -" ctrl+r Search history " -" esc esc Edit last message " +" Compose Session " +" / Commands tab Queue message " +" @ Mention files ⌥+, Less reasoning " +" ! Shell command ⌥+. More reasoning " +" shift+enter New line f4 Inspect activity " +" ctrl+v Paste image ctrl+c Quit " +" ctrl+g External editor " +" ctrl+r Search history Transcript (open first) " +" esc esc Edit last message ctrl+t Open transcript " +" f3 Find text " +" pgup / pgdn Scroll " +" ctrl+space Start selection " +" ctrl+home / ctrl+end Top / latest " +" shift+⌥+, / shift+⌥+. Top / latest " " " " /keymap customize " " " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_collaboration_modes_enabled.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_collaboration_modes_enabled.snap index 0eb963406f..191f6faff5 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_collaboration_modes_enabled.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_collaboration_modes_enabled.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/footer.rs -assertion_line: 1240 expression: terminal.backend() --- " Keyboard shortcuts " @@ -11,9 +10,10 @@ expression: terminal.backend() " ! Shell command ⌥+, Less reasoning " " ctrl+j New line ⌥+. More reasoning " " ctrl+v Paste image f8 Voice " -" ctrl+g External editor ctrl+c Quit " -" ctrl+r Search history " -" esc esc Edit last message Transcript (open first) " +" ctrl+g External editor f4 Inspect activity " +" ctrl+r Search history ctrl+c Quit " +" esc esc Edit last message " +" Transcript (open first) " " ctrl+t Open transcript " " f3 Find text " " pgup / pgdn Scroll " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_running.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_running.snap index cb2a7ce0a2..96ca5ec3f8 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_running.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_running.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/footer.rs -assertion_line: 1240 expression: terminal.backend() --- " Keyboard shortcuts " @@ -10,10 +9,11 @@ expression: terminal.backend() " @ Mention files ⌥+, Less reasoning " " ! Shell command ⌥+. More reasoning " " ctrl+j New line f8 Voice " -" ctrl+v Paste image ctrl+c Interrupt " -" ctrl+g External editor " -" ctrl+r Search history Transcript (open first) " -" esc esc Edit last message ctrl+t Open transcript " +" ctrl+v Paste image f4 Inspect activity " +" ctrl+g External editor ctrl+c Interrupt " +" ctrl+r Search history " +" esc esc Edit last message Transcript (open first) " +" ctrl+t Open transcript " " f3 Find text " " pgup / pgdn Scroll " " ctrl+space Start selection " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_shift_and_esc.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_shift_and_esc.snap index fd8b6b6943..5ecba9d728 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_shift_and_esc.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_shift_and_esc.snap @@ -1,6 +1,5 @@ --- source: tui/src/bottom_pane/footer.rs -assertion_line: 1240 expression: terminal.backend() --- " Keyboard shortcuts " @@ -10,10 +9,11 @@ expression: terminal.backend() " @ Mention files ⌥+, Less reasoning " " ! Shell command ⌥+. More reasoning " " shift+enter New line f8 Voice " -" ctrl+v Paste image ctrl+c Quit " -" ctrl+g External editor " -" ctrl+r Search history Transcript (open first) " -" esc Edit last message ctrl+t Open transcript " +" ctrl+v Paste image f4 Inspect activity " +" ctrl+g External editor ctrl+c Quit " +" ctrl+r Search history " +" esc Edit last message Transcript (open first) " +" ctrl+t Open transcript " " f3 Find text " " pgup / pgdn Scroll " " ctrl+space Start selection " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__shortcut_overlay__tests__shortcut_overlay_customized_wsl.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__shortcut_overlay__tests__shortcut_overlay_customized_wsl.snap index ac2fea009e..44e3afb8fe 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__shortcut_overlay__tests__shortcut_overlay_customized_wsl.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__shortcut_overlay__tests__shortcut_overlay_customized_wsl.snap @@ -10,8 +10,9 @@ expression: terminal.backend() " ! Shell command ⌥+. More reasoning " " shift+enter New line f8 Voice " " ctrl+⌥+v Paste image ← Agents (empty prompt) " -" ctrl+x ctrl+e External editor ctrl+c Quit " -" esc Edit last message " +" ctrl+x ctrl+e External editor f4 Inspect activity " +" esc Edit last message ctrl+c Quit " +" " " Transcript (open first) " " ctrl+t Open transcript " " f3 Find text " diff --git a/codex-rs/tui/src/transcript_view/disclosure.rs b/codex-rs/tui/src/transcript_view/disclosure.rs index 2fd967e880..ccbc373a18 100644 --- a/codex-rs/tui/src/transcript_view/disclosure.rs +++ b/codex-rs/tui/src/transcript_view/disclosure.rs @@ -1,13 +1,11 @@ //! Local activity disclosure keyed by retained tool identities, independent of text selection. -//! Controls follow the activity preview; copied text and full-transcript search remain source based. +//! Controls appear only while activity is focused; copied text and full-transcript search remain source based. use crate::style::accent_color; use std::collections::HashSet; use crate::bottom_pane::TranscriptFooter; use crate::key_hint::KeyBindingListExt; -use crate::key_hint::key_label_spans; -use crate::keymap::KeymapContext; use crate::keymap::ListAction; use crate::keymap::RuntimeKeymap; use crossterm::event::KeyEvent; @@ -362,74 +360,43 @@ impl TranscriptView { } pub(super) fn disclosure_footer(&self, width: u16) -> Option { - if self.detailed || self.mode != HistoryRenderMode::Rich { + if !self.is_activity_focused() { return None; } let keymap = &self.disclosure.keymap; - let text = if self.is_activity_focused() { - let hints: Vec<_> = [ - (ListAction::MoveUp, "previous"), - (ListAction::MoveDown, "next"), - (ListAction::Accept, "details"), - (ListAction::Cancel, "back"), - ] - .into_iter() - .filter_map(|(action, label)| { - keymap.list.primary_hint(action).map(|key| { - let mut spans = key.spans(); - spans.push(format!(" {label}").dim()); - Line::from(spans) - }) + let hints: Vec<_> = [ + (ListAction::MoveUp, "previous"), + (ListAction::MoveDown, "next"), + (ListAction::Accept, "details"), + (ListAction::Cancel, "back"), + ] + .into_iter() + .filter_map(|(action, label)| { + keymap.list.primary_hint(action).map(|key| { + let mut spans = key.spans(); + spans.push(format!(" {label}").dim()); + Line::from(spans) }) - .collect(); - // Drop navigation before the details/back controls, retaining whole remapped chords. - crate::footer_hint::first_fitting_line( - (0..hints.len()).map(|start| { - let mut spans = Vec::new(); - for (index, hint) in hints[start..].iter().enumerate() { - if index > 0 { - spans.push(" · ".dim()); - } - spans.extend(hint.spans.clone()); + }) + .collect(); + // Drop navigation before the details/back controls, retaining whole remapped chords. + let text = crate::footer_hint::first_fitting_line( + (0..hints.len()).map(|start| { + let mut spans = Vec::new(); + for (index, hint) in hints[start..].iter().enumerate() { + if index > 0 { + spans.push(" · ".dim()); } - Line::from(spans) - }), - width, - ) - } else { - if !self.visible.iter().any(|row| row.layout.disclosure) { - return None; - } - let key = keymap.primary_hint(KeymapContext::Global, "focus_activity")?; - let primary = key.display_label(); - let alternatives = std::iter::once(primary.clone()) - .chain( - crate::keymap::user_bindings(&keymap.app.focus_activity) - .iter() - .map(crate::key_hint::KeyBinding::display_label) - .filter(|label| *label != primary), - ) - .take(/*n*/ 2) - .collect::>() - .join("/"); - crate::footer_hint::first_fitting_line( - [ - (alternatives.as_str(), " inspect activity"), - (alternatives.as_str(), " activity"), - (primary.as_str(), " activity"), - ] - .map(|(keys, action)| { - let mut spans = key_label_spans(keys); - spans.push(action.dim()); - Line::from(spans) - }), - width, - ) - }; + spans.extend(hint.spans.clone()); + } + Line::from(spans) + }), + width, + ); Some(TranscriptFooter { text: text.into(), cursor_column: None, - is_interactive: self.is_activity_focused(), + is_interactive: true, }) } } diff --git a/codex-rs/tui/src/transcript_view/footer.rs b/codex-rs/tui/src/transcript_view/footer.rs index c3db6f4145..a8437b5d00 100644 --- a/codex-rs/tui/src/transcript_view/footer.rs +++ b/codex-rs/tui/src/transcript_view/footer.rs @@ -63,67 +63,65 @@ impl TranscriptView { } let pending = self.is_loading_history() || self.history == TranscriptHistoryState::Failed; let can_return = self.selection.is_none() && self.can_return_to_latest(); - (pending || self.unseen_activity || can_return) - .then(|| { - let navigation = if self.can_return_to_latest() { - latest_navigation - } else { - "" - }; - let mut status = self.status_line_with_navigation(navigation, motion); - // Loading must not resize the composer or move its caret. Shorten secondary text - // before clipping either shortcut; failed loads prioritize their retry action. - if status.width() > usize::from(width) { - status = if self.is_loading_history() { - let loading = if navigation.is_empty() { - if "↑ Loading…".width() <= usize::from(width) { - "Loading…" - } else { - "…" - } - } else if "↑ Loading… · ".width() + navigation.width() <= usize::from(width) - { - "Loading… · " + (pending || self.unseen_activity || can_return).then(|| { + let navigation = if self.can_return_to_latest() { + latest_navigation + } else { + "" + }; + let mut status = self.status_line_with_navigation(navigation, motion); + // Loading must not resize the composer or move its caret. Shorten secondary text + // before clipping either shortcut; failed loads prioritize their retry action. + if status.width() > usize::from(width) { + status = if self.is_loading_history() { + let loading = if navigation.is_empty() { + if "↑ Loading…".width() <= usize::from(width) { + "Loading…" } else { - "… " - }; - let mut spans = vec!["↑ ".fg(accent_color())]; - spans.extend(shimmer_text(loading, motion)); - spans.extend(navigation_line(navigation).spans); - Line::from(spans) - } else if self.history == TranscriptHistoryState::Failed { - let mut spans = vec!["Retry: ".dim()]; - spans.extend(key_label_spans(&JumpTarget::Beginning.hint_label())); - Line::from(spans) - } else if navigation.is_empty() { - Line::from("New activity").dim() - } else if self.unseen_activity { - let mut spans = vec!["New · ".dim()]; - spans.extend(navigation_line(navigation).spans); - Line::from(spans) + "…" + } + } else if "↑ Loading… · ".width() + navigation.width() <= usize::from(width) + { + "Loading… · " } else { - navigation_line(navigation) + "… " }; - } - let retry = if self.history == TranscriptHistoryState::Failed { - format!( - "{} retry", - crate::key_hint::ctrl(KeyCode::Home).display_label() - ) + let mut spans = vec!["↑ ".fg(accent_color())]; + spans.extend(shimmer_text(loading, motion)); + spans.extend(navigation_line(navigation).spans); + Line::from(spans) + } else if self.history == TranscriptHistoryState::Failed { + let mut spans = vec!["Retry: ".dim()]; + spans.extend(key_label_spans(&JumpTarget::Beginning.hint_label())); + Line::from(spans) + } else if navigation.is_empty() { + Line::from("New activity").dim() + } else if self.unseen_activity { + let mut spans = vec!["New · ".dim()]; + spans.extend(navigation_line(navigation).spans); + Line::from(spans) } else { - navigation.to_owned() + navigation_line(navigation) }; - TranscriptFooter { - text: first_fitting_line( - [status, navigation_line(&retry), navigation_line(navigation)], - width, - ) - .into(), - cursor_column: None, - is_interactive: false, - } - }) - .or_else(|| self.disclosure_footer(width)) + } + let retry = if self.history == TranscriptHistoryState::Failed { + format!( + "{} retry", + crate::key_hint::ctrl(KeyCode::Home).display_label() + ) + } else { + navigation.to_owned() + }; + TranscriptFooter { + text: first_fitting_line( + [status, navigation_line(&retry), navigation_line(navigation)], + width, + ) + .into(), + cursor_column: None, + is_interactive: false, + } + }) } pub(crate) fn status_line_with_navigation( diff --git a/codex-rs/tui/src/transcript_view/footer_tests.rs b/codex-rs/tui/src/transcript_view/footer_tests.rs index fbbf2c2565..52a0139352 100644 --- a/codex-rs/tui/src/transcript_view/footer_tests.rs +++ b/codex-rs/tui/src/transcript_view/footer_tests.rs @@ -290,7 +290,7 @@ impl HistoryCell for ActivityCell { } #[test] -fn activity_alternative_focuses_details_and_hints_follow_configured_chords() { +fn activity_focus_keeps_controls_without_passive_hints() { let mut cells: Vec> = Vec::new(); let cell = Arc::new(ActivityCell::default()); let mut view = TranscriptView::default(); @@ -314,12 +314,20 @@ fn activity_alternative_focuses_details_and_hints_follow_configured_chords() { for width in [20, 32, 80] { hints.push(format!( "{width} columns: {}", - view.footer(width, MotionMode::Reduced).unwrap().text, + view.footer(width, MotionMode::Reduced).map_or_else( + || "".to_owned(), + |footer| footer.text.to_string() + ), )); } view.handle_key(KeyEvent::new(KeyCode::F(4), KeyModifiers::NONE), &cells); assert!(view.is_activity_focused()); hints.push(format!("focused\n{:?}", render_live(&mut view))); + for width in [20, 32, 80] { + let footer = view.footer(width, MotionMode::Reduced).unwrap(); + assert!(footer.is_interactive); + hints.push(format!("focused, {width} columns: {}", footer.text)); + } view.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE), &cells); assert!(view.disclosure.is_expanded(&["sample-activity".to_owned()])); let expanded = render_live(&mut view); @@ -352,9 +360,10 @@ fn activity_alternative_focuses_details_and_hints_follow_configured_chords() { view.set_keymap_bindings(&keymap); view.jump_to_latest(); render(&mut view, &cells); - let hint = view - .footer(/*width*/ 80, MotionMode::Reduced) - .map_or_else(|| "unbound".to_owned(), |footer| footer.text.to_string()); + let hint = view.footer(/*width*/ 80, MotionMode::Reduced).map_or_else( + || "".to_owned(), + |footer| footer.text.to_string(), + ); hints.push(format!("{configured}: {hint}")); } insta::assert_snapshot!(hints.join("\n")); diff --git a/codex-rs/tui/src/transcript_view/snapshots/codex_tui__transcript_view__footer__tests__activity_alternative_focuses_details_and_hints_follow_configured_chords.snap b/codex-rs/tui/src/transcript_view/snapshots/codex_tui__transcript_view__footer__tests__activity_focus_keeps_controls_without_passive_hints.snap similarity index 84% rename from codex-rs/tui/src/transcript_view/snapshots/codex_tui__transcript_view__footer__tests__activity_alternative_focuses_details_and_hints_follow_configured_chords.snap rename to codex-rs/tui/src/transcript_view/snapshots/codex_tui__transcript_view__footer__tests__activity_focus_keeps_controls_without_passive_hints.snap index 2c9d624e99..dabbfed8a0 100644 --- a/codex-rs/tui/src/transcript_view/snapshots/codex_tui__transcript_view__footer__tests__activity_alternative_focuses_details_and_hints_follow_configured_chords.snap +++ b/codex-rs/tui/src/transcript_view/snapshots/codex_tui__transcript_view__footer__tests__activity_focus_keeps_controls_without_passive_hints.snap @@ -16,9 +16,9 @@ Buffer { x: 0, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } -20 columns: f4 inspect activity -32 columns: f4 inspect activity -80 columns: f4 inspect activity +20 columns: +32 columns: +80 columns: focused Buffer { area: Rect { x: 0, y: 0, width: 32, height: 3 }, @@ -35,6 +35,9 @@ Buffer { x: 0, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, ] } +focused, 20 columns: esc back +focused, 32 columns: enter details · esc back +focused, 80 columns: ↑ previous · ↓ next · enter details · esc back expanded Buffer { area: Rect { x: 0, y: 0, width: 32, height: 3 }, @@ -51,6 +54,6 @@ Buffer { x: 15, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, ] } -"ctrl-x t": ctrl+x t inspect activity -["ctrl-x t","f4"]: ctrl+x t/f4 inspect activity -[]: unbound +"ctrl-x t": +["ctrl-x t","f4"]: +[]: