mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
feat(tui): add configurable Vim keymap and startup default
Expose Vim bindings through the TUI keymap config and add a startup toggle so Vim mode can be enabled by default. Wire runtime keymap handling for Vim actions, add `tui.vim_mode_default` to config parsing/runtime config, apply the default in ChatWidget startup paths, and update docs/default keymap/schema accordingly.
This commit is contained in:
committed by
Felipe Coury
parent
d49e77a7c2
commit
cb3e5baa73
@@ -2236,7 +2236,8 @@
|
||||
"open_transcript": null,
|
||||
"queue": null,
|
||||
"submit": null,
|
||||
"toggle_shortcuts": null
|
||||
"toggle_shortcuts": null,
|
||||
"toggle_vim_mode": null
|
||||
},
|
||||
"list": {
|
||||
"accept": null,
|
||||
@@ -2270,7 +2271,45 @@
|
||||
"scroll_down": null,
|
||||
"scroll_up": null
|
||||
},
|
||||
"preset": "latest"
|
||||
"preset": "latest",
|
||||
"vim_normal": {
|
||||
"append_after_cursor": null,
|
||||
"append_line_end": null,
|
||||
"cancel_operator": null,
|
||||
"delete_char": null,
|
||||
"delete_to_line_end": null,
|
||||
"enter_insert": null,
|
||||
"insert_line_start": null,
|
||||
"move_down": null,
|
||||
"move_left": null,
|
||||
"move_line_end": null,
|
||||
"move_line_start": null,
|
||||
"move_right": null,
|
||||
"move_up": null,
|
||||
"move_word_backward": null,
|
||||
"move_word_end": null,
|
||||
"move_word_forward": null,
|
||||
"open_line_above": null,
|
||||
"open_line_below": null,
|
||||
"paste_after": null,
|
||||
"start_delete_operator": null,
|
||||
"start_yank_operator": null,
|
||||
"yank_line": null
|
||||
},
|
||||
"vim_operator": {
|
||||
"cancel": null,
|
||||
"delete_line": null,
|
||||
"motion_down": null,
|
||||
"motion_left": null,
|
||||
"motion_line_end": null,
|
||||
"motion_line_start": null,
|
||||
"motion_right": null,
|
||||
"motion_up": null,
|
||||
"motion_word_backward": null,
|
||||
"motion_word_end": null,
|
||||
"motion_word_forward": null,
|
||||
"yank_line": null
|
||||
}
|
||||
},
|
||||
"description": "Keybinding overrides for the TUI.\n\nThis supports rebinding selected actions globally and by context. Context bindings take precedence over `global` bindings."
|
||||
},
|
||||
@@ -2335,6 +2374,11 @@
|
||||
"default": null,
|
||||
"description": "Syntax highlighting theme name (kebab-case).\n\nWhen set, overrides automatic light/dark theme detection. Use `/theme` in the TUI or see `$CODEX_HOME/themes` for custom themes.",
|
||||
"type": "string"
|
||||
},
|
||||
"vim_mode_default": {
|
||||
"default": false,
|
||||
"description": "Start the composer in Vim mode (`Normal`) by default. Defaults to `false`.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -2642,6 +2686,14 @@
|
||||
}
|
||||
],
|
||||
"description": "Toggle the composer shortcut overlay."
|
||||
},
|
||||
"toggle_vim_mode": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Toggle Vim mode for the composer input."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -2726,7 +2778,8 @@
|
||||
"open_transcript": null,
|
||||
"queue": null,
|
||||
"submit": null,
|
||||
"toggle_shortcuts": null
|
||||
"toggle_shortcuts": null,
|
||||
"toggle_vim_mode": null
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@@ -2789,6 +2842,58 @@
|
||||
}
|
||||
],
|
||||
"default": "latest"
|
||||
},
|
||||
"vim_normal": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiVimNormalKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"append_after_cursor": null,
|
||||
"append_line_end": null,
|
||||
"cancel_operator": null,
|
||||
"delete_char": null,
|
||||
"delete_to_line_end": null,
|
||||
"enter_insert": null,
|
||||
"insert_line_start": null,
|
||||
"move_down": null,
|
||||
"move_left": null,
|
||||
"move_line_end": null,
|
||||
"move_line_start": null,
|
||||
"move_right": null,
|
||||
"move_up": null,
|
||||
"move_word_backward": null,
|
||||
"move_word_end": null,
|
||||
"move_word_forward": null,
|
||||
"open_line_above": null,
|
||||
"open_line_below": null,
|
||||
"paste_after": null,
|
||||
"start_delete_operator": null,
|
||||
"start_yank_operator": null,
|
||||
"yank_line": null
|
||||
}
|
||||
},
|
||||
"vim_operator": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiVimOperatorKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"cancel": null,
|
||||
"delete_line": null,
|
||||
"motion_down": null,
|
||||
"motion_left": null,
|
||||
"motion_line_end": null,
|
||||
"motion_line_start": null,
|
||||
"motion_right": null,
|
||||
"motion_up": null,
|
||||
"motion_word_backward": null,
|
||||
"motion_word_end": null,
|
||||
"motion_word_forward": null,
|
||||
"yank_line": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -3041,6 +3146,122 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiVimNormalKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Vim normal-mode keybindings for modal editing inside text areas.",
|
||||
"properties": {
|
||||
"append_after_cursor": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"append_line_end": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"cancel_operator": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"delete_char": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"delete_to_line_end": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"enter_insert": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"insert_line_start": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_down": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_left": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_line_end": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_line_start": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_right": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_up": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_word_backward": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_word_end": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"move_word_forward": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"open_line_above": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"open_line_below": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"paste_after": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"start_delete_operator": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"start_yank_operator": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"yank_line": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiVimOperatorKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Vim operator-pending keybindings for modal editing inside text areas.",
|
||||
"properties": {
|
||||
"cancel": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"delete_line": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_down": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_left": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_line_end": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_line_start": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_right": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_up": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_word_backward": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_word_end": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"motion_word_forward": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
},
|
||||
"yank_line": {
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"UriBasedFileOpener": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -543,6 +543,35 @@ async fn runtime_config_defaults_model_availability_nux() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tui_vim_mode_default_defaults_to_false() {
|
||||
let toml = r#"
|
||||
[tui]
|
||||
"#;
|
||||
let parsed: ConfigToml = toml::from_str(toml).expect("deserialize empty [tui] table");
|
||||
assert!(
|
||||
!parsed
|
||||
.tui
|
||||
.expect("config should include tui section")
|
||||
.vim_mode_default
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tui_vim_mode_default_true() {
|
||||
let toml = r#"
|
||||
[tui]
|
||||
vim_mode_default = true
|
||||
"#;
|
||||
let parsed: ConfigToml = toml::from_str(toml).expect("deserialize vim_mode_default=true");
|
||||
assert!(
|
||||
parsed
|
||||
.tui
|
||||
.expect("config should include tui section")
|
||||
.vim_mode_default
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_toml_deserializes_permission_profiles() {
|
||||
let toml = r#"
|
||||
@@ -1425,6 +1454,7 @@ fn tui_config_missing_notifications_field_defaults_to_enabled() {
|
||||
notification_settings: TuiNotificationSettings::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
vim_mode_default: false,
|
||||
alternate_screen: AltScreenMode::Auto,
|
||||
status_line: None,
|
||||
terminal_title: None,
|
||||
@@ -5269,6 +5299,7 @@ async fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
tui_notifications: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
tui_vim_mode_default: false,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(true),
|
||||
feedback_enabled: true,
|
||||
@@ -5465,6 +5496,7 @@ async fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
tui_notifications: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
tui_vim_mode_default: false,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(true),
|
||||
feedback_enabled: true,
|
||||
@@ -5615,6 +5647,7 @@ async fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
tui_notifications: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
tui_vim_mode_default: false,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(false),
|
||||
feedback_enabled: true,
|
||||
@@ -5750,6 +5783,7 @@ async fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
tui_notifications: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
tui_vim_mode_default: false,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(true),
|
||||
feedback_enabled: true,
|
||||
|
||||
@@ -356,6 +356,9 @@ pub struct Config {
|
||||
/// Persisted startup availability NUX state for model tooltips.
|
||||
pub model_availability_nux: ModelAvailabilityNuxConfig,
|
||||
|
||||
/// Start the composer in Vim mode (`Normal`) by default.
|
||||
pub tui_vim_mode_default: bool,
|
||||
|
||||
/// Start the TUI in the specified collaboration mode (plan/default).
|
||||
|
||||
/// Controls whether the TUI uses the terminal's alternate screen buffer.
|
||||
@@ -2470,6 +2473,11 @@ impl Config {
|
||||
.as_ref()
|
||||
.map(|t| t.model_availability_nux.clone())
|
||||
.unwrap_or_default(),
|
||||
tui_vim_mode_default: cfg
|
||||
.tui
|
||||
.as_ref()
|
||||
.map(|t| t.vim_mode_default)
|
||||
.unwrap_or(false),
|
||||
tui_alternate_screen: cfg
|
||||
.tui
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user