mirror of
https://github.com/openai/codex.git
synced 2026-05-02 02:17:22 +00:00
feat(tui): add configurable keymap support (#18593)
## Why The TUI currently handles keyboard shortcuts as hard-coded event matches spread across app, composer, pager, list, approval, and navigation code. That makes shortcuts hard to customize, makes displayed hints easy to drift from actual behavior, and makes future keymap work riskier because there is no central action inventory. This PR adds the foundation for configurable, action-based keymaps without adding the interactive remapping UI yet. Onboarding intentionally stays on fixed startup shortcuts because users cannot reasonably configure keymaps before completing onboarding. This is PR1 in the keymap stack: - PR1: #18593: configurable keymap foundation - PR2: #18594: `/keymap` picker and guided remapping UI - PR3: #18595: Vim composer mode and the remap option ## Design Notes The new model resolves named actions into concrete runtime bindings once from config, then passes those bindings to the UI surfaces that handle input or render shortcut hints. The main concepts are: - **Context**: a scope where an action is active, such as `global`, `chat`, `composer`, `editor`, `pager`, `list`, or `approval`. - **Action**: a named operation inside a context, such as `global.open_transcript`, `composer.submit`, or `pager.close`. - **Binding**: one or more single-key shortcuts assigned to an action, written as config strings such as `ctrl-t`, `alt-backspace`, or `page-down`. Multi-step sequences such as `ctrl-x ctrl-s`, `g g`, or leader-key flows are not part of this PR. - **Resolution order**: context-specific config wins first, supported global fallbacks come next, and built-in defaults fill in anything unset. - **Explicit unbinding**: an empty array removes an action binding in that scope and does not fall through to a fallback binding. - **Conflict validation**: a resolved keymap rejects duplicate active bindings inside the same scope so one keypress cannot dispatch two actions. ## What Changed - Added `TuiKeymap` config support under `[tui.keymap]`, including typed contexts/actions, key alias normalization, generated schema coverage, and user-facing config errors. - Added `RuntimeKeymap` resolution in `codex-rs/tui/src/keymap.rs`, including fallback precedence, built-in defaults, explicit unbinding, and per-context conflict validation. - Rewired existing TUI handlers to consume resolved keymap actions instead of directly matching hard-coded keys in each component. - Updated key hint rendering and footer/pager/list surfaces so displayed shortcuts follow the resolved keymap. - Kept onboarding shortcuts fixed in `codex-rs/tui/src/onboarding/keys.rs` instead of exposing them through `[tui.keymap]`. ## Validation The branch includes focused coverage for config parsing, key normalization, runtime fallback resolution, explicit unbinding, duplicate-key conflict validation, default keymap consistency, onboarding startup key behavior, and UI hint snapshots affected by resolved key bindings.
This commit is contained in:
@@ -967,6 +967,20 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"KeybindingsSpec": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
],
|
||||
"description": "One action binding value in config.\n\nThis accepts either:\n\n1. A single key spec string (`\"ctrl-a\"`). 2. A list of key spec strings (`[\"ctrl-a\", \"alt-a\"]`).\n\nAn empty list explicitly unbinds the action in that scope. Because an explicit empty list is still a configured value, runtime resolution must not fall through to global or built-in defaults for that action."
|
||||
},
|
||||
"MarketplaceConfig": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -2224,6 +2238,83 @@
|
||||
"description": "Enable animations (welcome screen, shimmer effects, spinners). Defaults to `true`.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"keymap": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"approval": {
|
||||
"approve": null,
|
||||
"approve_for_prefix": null,
|
||||
"approve_for_session": null,
|
||||
"cancel": null,
|
||||
"decline": null,
|
||||
"deny": null,
|
||||
"open_fullscreen": null,
|
||||
"open_thread": null
|
||||
},
|
||||
"chat": {
|
||||
"decrease_reasoning_effort": null,
|
||||
"edit_queued_message": null,
|
||||
"increase_reasoning_effort": null
|
||||
},
|
||||
"composer": {
|
||||
"history_search_next": null,
|
||||
"history_search_previous": null,
|
||||
"queue": null,
|
||||
"submit": null,
|
||||
"toggle_shortcuts": null
|
||||
},
|
||||
"editor": {
|
||||
"delete_backward": null,
|
||||
"delete_backward_word": null,
|
||||
"delete_forward": null,
|
||||
"delete_forward_word": null,
|
||||
"insert_newline": null,
|
||||
"kill_line_end": null,
|
||||
"kill_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_left": null,
|
||||
"move_word_right": null,
|
||||
"yank": null
|
||||
},
|
||||
"global": {
|
||||
"clear_terminal": null,
|
||||
"copy": null,
|
||||
"open_external_editor": null,
|
||||
"open_transcript": null,
|
||||
"queue": null,
|
||||
"submit": null,
|
||||
"toggle_shortcuts": null
|
||||
},
|
||||
"list": {
|
||||
"accept": null,
|
||||
"cancel": null,
|
||||
"move_down": null,
|
||||
"move_up": null
|
||||
},
|
||||
"pager": {
|
||||
"close": null,
|
||||
"close_transcript": null,
|
||||
"half_page_down": null,
|
||||
"half_page_up": null,
|
||||
"jump_bottom": null,
|
||||
"jump_top": null,
|
||||
"page_down": null,
|
||||
"page_up": null,
|
||||
"scroll_down": null,
|
||||
"scroll_up": null
|
||||
}
|
||||
},
|
||||
"description": "Keybinding overrides for the TUI.\n\nThis supports rebinding selected actions globally and by context. Context bindings take precedence over `global` bindings."
|
||||
},
|
||||
"model_availability_nux": {
|
||||
"allOf": [
|
||||
{
|
||||
@@ -2296,6 +2387,602 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiApprovalKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Approval overlay keybindings.",
|
||||
"properties": {
|
||||
"approve": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Approve the primary option."
|
||||
},
|
||||
"approve_for_prefix": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Approve with exec-policy prefix when that option exists."
|
||||
},
|
||||
"approve_for_session": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Approve for session when that option exists."
|
||||
},
|
||||
"cancel": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Cancel an elicitation request."
|
||||
},
|
||||
"decline": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Decline and provide corrective guidance."
|
||||
},
|
||||
"deny": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Deny without providing follow-up guidance."
|
||||
},
|
||||
"open_fullscreen": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Open the full-screen approval details view."
|
||||
},
|
||||
"open_thread": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Open the thread that requested approval when shown from another thread."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiChatKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Chat context keybindings.",
|
||||
"properties": {
|
||||
"decrease_reasoning_effort": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Decrease the active reasoning effort."
|
||||
},
|
||||
"edit_queued_message": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Edit the most recently queued message."
|
||||
},
|
||||
"increase_reasoning_effort": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Increase the active reasoning effort."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiComposerKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Composer context keybindings. These override corresponding `global` actions.",
|
||||
"properties": {
|
||||
"history_search_next": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move to the next match in reverse history search."
|
||||
},
|
||||
"history_search_previous": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Open reverse history search or move to the previous match."
|
||||
},
|
||||
"queue": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Queue the current composer draft while a task is running."
|
||||
},
|
||||
"submit": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Submit the current composer draft."
|
||||
},
|
||||
"toggle_shortcuts": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Toggle the composer shortcut overlay."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiEditorKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Editor context keybindings for text editing inside text areas.",
|
||||
"properties": {
|
||||
"delete_backward": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Delete one grapheme to the left."
|
||||
},
|
||||
"delete_backward_word": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Delete the previous word."
|
||||
},
|
||||
"delete_forward": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Delete one grapheme to the right."
|
||||
},
|
||||
"delete_forward_word": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Delete the next word."
|
||||
},
|
||||
"insert_newline": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Insert a newline in the editor."
|
||||
},
|
||||
"kill_line_end": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Kill text from cursor to line end."
|
||||
},
|
||||
"kill_line_start": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Kill text from cursor to line start."
|
||||
},
|
||||
"move_down": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor down one visual line."
|
||||
},
|
||||
"move_left": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor left by one grapheme."
|
||||
},
|
||||
"move_line_end": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor to end of line."
|
||||
},
|
||||
"move_line_start": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor to beginning of line."
|
||||
},
|
||||
"move_right": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor right by one grapheme."
|
||||
},
|
||||
"move_up": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor up one visual line."
|
||||
},
|
||||
"move_word_left": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor to beginning of previous word."
|
||||
},
|
||||
"move_word_right": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move cursor to end of next word."
|
||||
},
|
||||
"yank": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Yank the kill buffer."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiGlobalKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Global keybindings. These are used when a context does not define an override.",
|
||||
"properties": {
|
||||
"clear_terminal": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Clear the terminal UI."
|
||||
},
|
||||
"copy": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Copy the last agent response to the clipboard."
|
||||
},
|
||||
"open_external_editor": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Open the external editor for the current draft."
|
||||
},
|
||||
"open_transcript": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Open the transcript overlay."
|
||||
},
|
||||
"queue": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Queue the current composer draft while a task is running."
|
||||
},
|
||||
"submit": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Submit the current composer draft."
|
||||
},
|
||||
"toggle_shortcuts": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Toggle the composer shortcut overlay."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Raw keymap configuration from `[tui.keymap]`.\n\nEach context contains action-level overrides. Missing actions inherit from built-in defaults, and selected chat/composer actions can fall back through `global` during runtime resolution.\n\nThis type is intentionally a persistence shape, not the structure used by input handlers. Runtime consumers should resolve it into `RuntimeKeymap` first so precedence, empty-list unbinding, and duplicate-key validation are applied consistently.",
|
||||
"properties": {
|
||||
"approval": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiApprovalKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"approve": null,
|
||||
"approve_for_prefix": null,
|
||||
"approve_for_session": null,
|
||||
"cancel": null,
|
||||
"decline": null,
|
||||
"deny": null,
|
||||
"open_fullscreen": null,
|
||||
"open_thread": null
|
||||
}
|
||||
},
|
||||
"chat": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiChatKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"decrease_reasoning_effort": null,
|
||||
"edit_queued_message": null,
|
||||
"increase_reasoning_effort": null
|
||||
}
|
||||
},
|
||||
"composer": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiComposerKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"history_search_next": null,
|
||||
"history_search_previous": null,
|
||||
"queue": null,
|
||||
"submit": null,
|
||||
"toggle_shortcuts": null
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiEditorKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"delete_backward": null,
|
||||
"delete_backward_word": null,
|
||||
"delete_forward": null,
|
||||
"delete_forward_word": null,
|
||||
"insert_newline": null,
|
||||
"kill_line_end": null,
|
||||
"kill_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_left": null,
|
||||
"move_word_right": null,
|
||||
"yank": null
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiGlobalKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"clear_terminal": null,
|
||||
"copy": null,
|
||||
"open_external_editor": null,
|
||||
"open_transcript": null,
|
||||
"queue": null,
|
||||
"submit": null,
|
||||
"toggle_shortcuts": null
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiListKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"accept": null,
|
||||
"cancel": null,
|
||||
"move_down": null,
|
||||
"move_up": null
|
||||
}
|
||||
},
|
||||
"pager": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/TuiPagerKeymap"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"close": null,
|
||||
"close_transcript": null,
|
||||
"half_page_down": null,
|
||||
"half_page_up": null,
|
||||
"jump_bottom": null,
|
||||
"jump_top": null,
|
||||
"page_down": null,
|
||||
"page_up": null,
|
||||
"scroll_down": null,
|
||||
"scroll_up": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiListKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "List selection context keybindings for popup-style selectable lists.",
|
||||
"properties": {
|
||||
"accept": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Accept current selection."
|
||||
},
|
||||
"cancel": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Cancel and close selection view."
|
||||
},
|
||||
"move_down": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move list selection down."
|
||||
},
|
||||
"move_up": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Move list selection up."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TuiPagerKeymap": {
|
||||
"additionalProperties": false,
|
||||
"description": "Pager context keybindings for transcript and static overlays.",
|
||||
"properties": {
|
||||
"close": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Close the pager overlay."
|
||||
},
|
||||
"close_transcript": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Close the transcript overlay via its dedicated toggle key."
|
||||
},
|
||||
"half_page_down": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Scroll down by half a page."
|
||||
},
|
||||
"half_page_up": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Scroll up by half a page."
|
||||
},
|
||||
"jump_bottom": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Jump to the end."
|
||||
},
|
||||
"jump_top": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Jump to the beginning."
|
||||
},
|
||||
"page_down": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Scroll down by one page."
|
||||
},
|
||||
"page_up": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Scroll up by one page."
|
||||
},
|
||||
"scroll_down": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Scroll down by one row."
|
||||
},
|
||||
"scroll_up": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Scroll up by one row."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"UriBasedFileOpener": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user