diff options
| author | Sebastian Herrlinger <[email protected]> | 2025-12-11 17:20:04 +0100 |
|---|---|---|
| committer | Sebastian Herrlinger <[email protected]> | 2025-12-11 17:23:47 +0100 |
| commit | e271852bc3c6e3e4ee04a2245a7bc2c8f8b7f30a (patch) | |
| tree | fe60fb286b13ccbdd3433a50793e3456b4120757 /packages | |
| parent | 4e02704f17be406a7cec8b5a815582a08a2043cf (diff) | |
| download | opencode-e271852bc3c6e3e4ee04a2245a7bc2c8f8b7f30a.tar.gz opencode-e271852bc3c6e3e4ee04a2245a7bc2c8f8b7f30a.zip | |
allow custom mappings for all textarea actions via config
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/opencode/package.json | 4 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx | 72 | ||||
| -rw-r--r-- | packages/opencode/src/config/config.ts | 63 | ||||
| -rw-r--r-- | packages/web/src/content/docs/keybinds.mdx | 40 |
4 files changed, 157 insertions, 22 deletions
diff --git a/packages/opencode/package.json b/packages/opencode/package.json index ec7365c42..ab08a043f 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -71,8 +71,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.2", - "@opentui/core": "0.1.60", - "@opentui/solid": "0.1.60", + "@opentui/core": "0.0.0-20251211-4403a69a", + "@opentui/solid": "0.0.0-20251211-4403a69a", "@parcel/watcher": "2.5.1", "@pierre/precision-diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index fce9917c2..a5b6051ed 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -46,6 +46,60 @@ export type PromptRef = { const PLACEHOLDERS = ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"] +const TEXTAREA_ACTIONS = [ + "submit", + "newline", + "move-left", + "move-right", + "move-up", + "move-down", + "select-left", + "select-right", + "select-up", + "select-down", + "line-home", + "line-end", + "select-line-home", + "select-line-end", + "visual-line-home", + "visual-line-end", + "select-visual-line-home", + "select-visual-line-end", + "buffer-home", + "buffer-end", + "select-buffer-home", + "select-buffer-end", + "delete-line", + "delete-to-line-end", + "delete-to-line-start", + "backspace", + "delete", + "undo", + "redo", + "word-forward", + "word-backward", + "select-word-forward", + "select-word-backward", + "delete-word-forward", + "delete-word-backward", +] as const + +function mapTextareaKeybindings( + keybinds: Record<string, { ctrl: boolean; meta: boolean; shift: boolean; leader: boolean; name: string }[]>, + action: (typeof TEXTAREA_ACTIONS)[number], +): KeyBinding[] { + const configKey = `input_${action.replace(/-/g, "_")}` + const bindings = keybinds[configKey] + if (!bindings) return [] + return bindings.map((binding) => ({ + name: binding.name, + ctrl: binding.ctrl || undefined, + meta: binding.meta || undefined, + shift: binding.shift || undefined, + action, + })) +} + export function Prompt(props: PromptProps) { let input: TextareaRenderable let anchor: BoxRenderable @@ -76,26 +130,12 @@ export function Prompt(props: PromptProps) { } const textareaKeybindings = createMemo(() => { - const newlineBindings = keybind.all.input_newline || [] - const submitBindings = keybind.all.input_submit || [] + const keybinds = keybind.all return [ { name: "return", action: "submit" }, { name: "return", meta: true, action: "newline" }, - ...newlineBindings.map((binding) => ({ - name: binding.name, - ctrl: binding.ctrl || undefined, - meta: binding.meta || undefined, - shift: binding.shift || undefined, - action: "newline" as const, - })), - ...submitBindings.map((binding) => ({ - name: binding.name, - ctrl: binding.ctrl || undefined, - meta: binding.meta || undefined, - shift: binding.shift || undefined, - action: "submit" as const, - })), + ...TEXTAREA_ACTIONS.flatMap((action) => mapTextareaKeybindings(keybinds, action)), ] satisfies KeyBinding[] }) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 34e3eea79..85f5b4409 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -466,7 +466,68 @@ export namespace Config { input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"), input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"), input_submit: z.string().optional().default("return").describe("Submit input"), - input_newline: z.string().optional().default("shift+return,ctrl+j").describe("Insert newline in input"), + input_newline: z + .string() + .optional() + .default("shift+return,ctrl+return,alt+return,ctrl+j") + .describe("Insert newline in input"), + input_move_left: z.string().optional().default("left,ctrl+b").describe("Move cursor left in input"), + input_move_right: z.string().optional().default("right,ctrl+f").describe("Move cursor right in input"), + input_move_up: z.string().optional().default("up").describe("Move cursor up in input"), + input_move_down: z.string().optional().default("down").describe("Move cursor down in input"), + input_select_left: z.string().optional().default("shift+left").describe("Select left in input"), + input_select_right: z.string().optional().default("shift+right").describe("Select right in input"), + input_select_up: z.string().optional().default("shift+up").describe("Select up in input"), + input_select_down: z.string().optional().default("shift+down").describe("Select down in input"), + input_line_home: z.string().optional().default("ctrl+a").describe("Move to start of line in input"), + input_line_end: z.string().optional().default("ctrl+e").describe("Move to end of line in input"), + input_select_line_home: z.string().optional().default("none").describe("Select to start of line in input"), + input_select_line_end: z.string().optional().default("none").describe("Select to end of line in input"), + input_visual_line_home: z.string().optional().default("none").describe("Move to start of visual line in input"), + input_visual_line_end: z.string().optional().default("none").describe("Move to end of visual line in input"), + input_select_visual_line_home: z + .string() + .optional() + .default("none") + .describe("Select to start of visual line in input"), + input_select_visual_line_end: z + .string() + .optional() + .default("none") + .describe("Select to end of visual line in input"), + input_buffer_home: z.string().optional().default("home").describe("Move to start of buffer in input"), + input_buffer_end: z.string().optional().default("end").describe("Move to end of buffer in input"), + input_select_buffer_home: z + .string() + .optional() + .default("shift+home") + .describe("Select to start of buffer in input"), + input_select_buffer_end: z.string().optional().default("shift+end").describe("Select to end of buffer in input"), + input_delete_line: z.string().optional().default("alt+d").describe("Delete line in input"), + input_delete_to_line_end: z.string().optional().default("ctrl+k").describe("Delete to end of line in input"), + input_delete_to_line_start: z.string().optional().default("ctrl+u").describe("Delete to start of line in input"), + input_backspace: z.string().optional().default("backspace,shift+backspace").describe("Backspace in input"), + input_delete: z.string().optional().default("delete,shift+delete").describe("Delete character in input"), + input_undo: z.string().optional().default("ctrl+-,cmd+z").describe("Undo in input"), + input_redo: z.string().optional().default("ctrl+.,cmd+shift+z").describe("Redo in input"), + input_word_forward: z.string().optional().default("alt+f,alt+right").describe("Move word forward in input"), + input_word_backward: z.string().optional().default("alt+b,alt+left").describe("Move word backward in input"), + input_select_word_forward: z + .string() + .optional() + .default("alt+shift+f,alt+shift+right") + .describe("Select word forward in input"), + input_select_word_backward: z + .string() + .optional() + .default("alt+shift+b,alt+shift+left") + .describe("Select word backward in input"), + input_delete_word_forward: z.string().optional().default("ctrl+d").describe("Delete word forward in input"), + input_delete_word_backward: z + .string() + .optional() + .default("ctrl+w,alt+backspace") + .describe("Delete word backward in input"), history_previous: z.string().optional().default("up").describe("Previous history item"), history_next: z.string().optional().default("down").describe("Next history item"), session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"), diff --git a/packages/web/src/content/docs/keybinds.mdx b/packages/web/src/content/docs/keybinds.mdx index 36b51c68d..67e53c455 100644 --- a/packages/web/src/content/docs/keybinds.mdx +++ b/packages/web/src/content/docs/keybinds.mdx @@ -46,10 +46,44 @@ OpenCode has a list of keybinds that you can customize through the OpenCode conf "agent_cycle_reverse": "shift+tab", "input_clear": "ctrl+c", "input_paste": "ctrl+v", - "input_submit": "enter", - "input_newline": "shift+enter,ctrl+j", + "input_submit": "return", + "input_newline": "shift+return,ctrl+return,alt+return,ctrl+j", + "input_move_left": "left,ctrl+b", + "input_move_right": "right,ctrl+f", + "input_move_up": "up", + "input_move_down": "down", + "input_select_left": "shift+left", + "input_select_right": "shift+right", + "input_select_up": "shift+up", + "input_select_down": "shift+down", + "input_line_home": "ctrl+a", + "input_line_end": "ctrl+e", + "input_select_line_home": "none", + "input_select_line_end": "none", + "input_visual_line_home": "none", + "input_visual_line_end": "none", + "input_select_visual_line_home": "none", + "input_select_visual_line_end": "none", + "input_buffer_home": "home", + "input_buffer_end": "end", + "input_select_buffer_home": "shift+home", + "input_select_buffer_end": "shift+end", + "input_delete_line": "alt+d", + "input_delete_to_line_end": "ctrl+k", + "input_delete_to_line_start": "ctrl+u", + "input_backspace": "backspace,shift+backspace", + "input_delete": "delete,shift+delete", + "input_undo": "ctrl+-,cmd+z", + "input_redo": "ctrl+.,cmd+shift+z", + "input_word_forward": "alt+f,alt+right", + "input_word_backward": "alt+b,alt+left", + "input_select_word_forward": "alt+shift+f,alt+shift+right", + "input_select_word_backward": "alt+shift+b,alt+shift+left", + "input_delete_word_forward": "ctrl+d", + "input_delete_word_backward": "ctrl+w,alt+backspace", "history_previous": "up", - "history_next": "down" + "history_next": "down", + "terminal_suspend": "ctrl+z" } } ``` |
