From fb459feb5aa37d54b07358908cacf77f7cc5d037 Mon Sep 17 00:00:00 2001 From: Felipe Coury Date: Sun, 19 Apr 2026 00:27:40 -0300 Subject: [PATCH] feat(tui): add common keymap picker category Add a curated Common tab to `/keymap` for the shortcuts most likely to be rebound, while keeping All as the initial view. Demote onboarding from the top-level picker categories without removing its keymap actions from All or search. --- codex-rs/tui/src/keymap_setup.rs | 60 +++++++++++++++++++ codex-rs/tui/src/keymap_setup/picker.rs | 55 +++++++++++++++-- ...p__tests__keymap_picker_first_actions.snap | 2 +- ...ap_setup__tests__keymap_picker_narrow.snap | 4 +- ...ymap_setup__tests__keymap_picker_wide.snap | 2 +- 5 files changed, 113 insertions(+), 10 deletions(-) diff --git a/codex-rs/tui/src/keymap_setup.rs b/codex-rs/tui/src/keymap_setup.rs index d8f14f2e84..69e67cceab 100644 --- a/codex-rs/tui/src/keymap_setup.rs +++ b/codex-rs/tui/src/keymap_setup.rs @@ -717,6 +717,7 @@ fn key_parts_to_config_key_spec( #[cfg(test)] mod tests { use super::picker::KEYMAP_ALL_TAB_ID; + use super::picker::KEYMAP_COMMON_TAB_ID; use super::picker::KEYMAP_CUSTOM_TAB_ID; use super::picker::KEYMAP_UNBOUND_TAB_ID; use super::*; @@ -849,6 +850,65 @@ mod tests { })); } + #[test] + fn picker_common_tab_lists_curated_actions() { + let runtime = RuntimeKeymap::defaults(); + let params = build_keymap_picker_params(&runtime, &TuiKeymap::default()); + let common_tab = selection_tab(¶ms, KEYMAP_COMMON_TAB_ID); + let actions = common_tab + .items + .iter() + .map(|item| { + item.search_value + .as_deref() + .unwrap_or_default() + .split_whitespace() + .take(2) + .collect::>() + .join(".") + }) + .collect::>(); + + assert_eq!( + actions, + vec![ + "Composer.submit", + "Editor.insert_newline", + "Composer.queue", + "Global.open_external_editor", + "Global.copy", + "Global.toggle_vim_mode", + "Editor.delete_backward_word", + "Editor.delete_forward_word", + "Editor.move_word_left", + "Editor.move_word_right", + "Global.open_transcript", + "Pager.close", + "Pager.page_up", + "Pager.page_down", + "Approval.open_fullscreen", + "Approval.approve", + "Approval.approve_for_session", + "Approval.decline", + "Approval.cancel", + ] + ); + } + + #[test] + fn picker_keeps_onboarding_actions_searchable_without_dedicated_tab() { + let runtime = RuntimeKeymap::defaults(); + let params = build_keymap_picker_params(&runtime, &TuiKeymap::default()); + let all_tab = selection_tab(¶ms, KEYMAP_ALL_TAB_ID); + + assert!(params.tabs.iter().all(|tab| tab.label != "Onboarding")); + assert!(all_tab.items.iter().any(|item| { + item.search_value + .as_deref() + .is_some_and(|search_value| search_value.contains("Onboarding quit")) + })); + } + #[test] fn picker_content_snapshot() { let runtime = RuntimeKeymap::defaults(); diff --git a/codex-rs/tui/src/keymap_setup/picker.rs b/codex-rs/tui/src/keymap_setup/picker.rs index abe170720d..500bbbed63 100644 --- a/codex-rs/tui/src/keymap_setup/picker.rs +++ b/codex-rs/tui/src/keymap_setup/picker.rs @@ -24,6 +24,7 @@ use super::has_custom_binding; const KEYMAP_PICKER_VIEW_ID: &str = "keymap-picker"; pub(super) const KEYMAP_ALL_TAB_ID: &str = "all-shortcuts"; +pub(super) const KEYMAP_COMMON_TAB_ID: &str = "common-shortcuts"; pub(super) const KEYMAP_CUSTOM_TAB_ID: &str = "custom-shortcuts"; pub(super) const KEYMAP_UNBOUND_TAB_ID: &str = "unbound-shortcuts"; const KEYMAP_CONTEXT_LABEL_WIDTH: usize = 12; @@ -53,6 +54,28 @@ struct KeymapContextTab { contexts: &'static [&'static str], } +const KEYMAP_COMMON_ACTIONS: &[(&str, &str)] = &[ + ("composer", "submit"), + ("editor", "insert_newline"), + ("composer", "queue"), + ("global", "open_external_editor"), + ("global", "copy"), + ("global", "toggle_vim_mode"), + ("editor", "delete_backward_word"), + ("editor", "delete_forward_word"), + ("editor", "move_word_left"), + ("editor", "move_word_right"), + ("global", "open_transcript"), + ("pager", "close"), + ("pager", "page_up"), + ("pager", "page_down"), + ("approval", "open_fullscreen"), + ("approval", "approve"), + ("approval", "approve_for_session"), + ("approval", "decline"), + ("approval", "cancel"), +]; + const KEYMAP_CONTEXT_TABS: &[KeymapContextTab] = &[ KeymapContextTab { id: "app-shortcuts", @@ -90,12 +113,6 @@ const KEYMAP_CONTEXT_TABS: &[KeymapContextTab] = &[ description: "Approval prompt shortcuts.", contexts: &["approval"], }, - KeymapContextTab { - id: "onboarding-shortcuts", - label: "Onboarding", - description: "Onboarding flow shortcuts.", - contexts: &["onboarding"], - }, ]; pub(crate) fn build_keymap_picker_params( @@ -126,6 +143,22 @@ pub(crate) fn build_keymap_picker_params( ), }); + let common_rows = keymap_common_rows(&rows); + let common_count = common_rows.len(); + tabs.push(SelectionTab { + id: KEYMAP_COMMON_TAB_ID.to_string(), + label: "Common".to_string(), + header: keymap_header( + "Frequently customized shortcuts.".to_string(), + action_count_line(common_count), + ), + items: keymap_selection_items( + common_rows, + "No common shortcuts", + "No common shortcut actions are available.", + ), + }); + let custom_rows = rows .iter() .filter(|row| row.custom_binding) @@ -223,6 +256,16 @@ fn build_keymap_rows( .collect() } +fn keymap_common_rows(rows: &[KeymapActionRow]) -> Vec<&KeymapActionRow> { + KEYMAP_COMMON_ACTIONS + .iter() + .filter_map(|(context, action)| { + rows.iter() + .find(|row| row.context == *context && row.action == *action) + }) + .collect() +} + fn keymap_selection_items<'a>( rows: impl IntoIterator, empty_name: &str, diff --git a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_first_actions.snap b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_first_actions.snap index 5cc754da67..4ca23ad5f3 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_first_actions.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__keymap_setup__tests__keymap_picker_first_actions.snap @@ -3,6 +3,7 @@ source: tui/src/keymap_setup.rs expression: snapshot --- tab: All (91 selectable) +tab: Common (19 selectable) tab: Customized (0) (0 selectable) tab: Unbound (1) (1 selectable) tab: App (6 selectable) @@ -11,7 +12,6 @@ tab: Editor (16 selectable) tab: Vim (34 selectable) tab: Navigation (17 selectable) tab: Approval (6 selectable) -tab: Onboarding (9 selectable) Open Transcript | ctrl-t · Default | Global open_transcript Open Transcript Open the transcript overlay. ctrl-t Default Open External Editor | ctrl-g · Default | Global open_external_editor Open External Editor Open the current draft in an external editor. ctrl-g Default Copy | ctrl-o · Default | Global copy Copy Copy the last agent response to the clipboard. ctrl-o Default 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 9cb9662d60..7519836b9a 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 @@ -7,8 +7,8 @@ expression: "render_picker(params, 78)" All configurable shortcuts. 91 actions, 0 customized, 1 unbound. - [All] Customized (0) Unbound (1) App Composer Editor Vim Navigation - Approval Onboarding + [All] Common Customized (0) Unbound (1) App Composer Editor Vim + Navigation Approval Type to search shortcuts › [D] Global Open Transcript Current ctrl-t · Default … 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 36549b6c0b..ccf83b5f52 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 @@ -7,7 +7,7 @@ expression: "render_picker(params, 120)" All configurable shortcuts. 91 actions, 0 customized, 1 unbound. - [All] Customized (0) Unbound (1) App Composer Editor Vim Navigation Approval Onboarding + [All] Common Customized (0) Unbound (1) App Composer Editor Vim Navigation Approval Type to search shortcuts › [D] Global Open Transcript Current ctrl-t · Default keymap. Open the transcript overlay.