diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx index db7cf1bb0a..a03ac7cac2 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx @@ -244,7 +244,8 @@ function AutoMethod(props: AutoMethodProps) { { key: "c", cmd: () => { - const code = props.authorization.instructions.match(/[A-Z0-9]{4}-[A-Z0-9]{4,5}/)?.[0] ?? props.authorization.url + const code = + props.authorization.instructions.match(/[A-Z0-9]{4}-[A-Z0-9]{4,5}/)?.[0] ?? props.authorization.url Clipboard.copy(code) .then(() => toast.show({ message: "Copied to clipboard", variant: "info" })) .catch(toast.error) 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 71fb256a7f..898d14e979 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -60,12 +60,7 @@ import { useArgs } from "@tui/context/args" import { Flag } from "@opencode-ai/core/flag/flag" import { type WorkspaceStatus } from "../workspace-label" import { useCommandPalette } from "../../context/command-palette" -import { - useBindings, - useCommandShortcut, - useLeaderActive, - useOpencodeKeymap, -} from "../../keymap" +import { useBindings, useCommandShortcut, useLeaderActive, useOpencodeKeymap } from "../../keymap" import { useTuiConfig } from "../../context/tui-config" export type PromptProps = { @@ -890,7 +885,13 @@ export function Prompt(props: PromptProps) { target: inputTarget, enabled: (() => { cursorVersion() - return inputTarget() !== undefined && !props.disabled && store.mode === "normal" && !auto()?.visible && input?.visualCursor.offset === 0 + return ( + inputTarget() !== undefined && + !props.disabled && + store.mode === "normal" && + !auto()?.visible && + input?.visualCursor.offset === 0 + ) })(), bindings: [ { diff --git a/packages/opencode/src/cli/cmd/tui/config/legacy-keymap-transform.ts b/packages/opencode/src/cli/cmd/tui/config/legacy-keymap-transform.ts index c0c621862e..4b266a4ecc 100644 --- a/packages/opencode/src/cli/cmd/tui/config/legacy-keymap-transform.ts +++ b/packages/opencode/src/cli/cmd/tui/config/legacy-keymap-transform.ts @@ -46,7 +46,12 @@ const inputCommands = { input_select_all: "input.select.all", } as const satisfies Partial> -function add(config: SectionsConfig, section: KeymapSection, command: string, binding: BindingValue | undefined) { +function add( + config: SectionsConfig, + section: KeymapSection, + command: string, + binding: BindingValue | undefined, +) { if (binding === undefined) return config[section] ??= {} config[section][command] = binding @@ -154,7 +159,12 @@ export function create(keybinds: LegacyKeybinds): KeymapConfigInput { add(config, "dialog_select", "dialog.select.submit", keybinds["dialog.select.submit"]) add(config, "dialog_actions", "dialog.action.delete", combineBindings(keybinds.stash_delete, keybinds.session_delete)) add(config, "dialog_actions", "dialog.action.rename", keybinds.session_rename) - add(config, "dialog_actions", "dialog.action.toggle", combineBindings(keybinds["dialog.mcp.toggle"], keybinds["plugins.toggle"])) + add( + config, + "dialog_actions", + "dialog.action.toggle", + combineBindings(keybinds["dialog.mcp.toggle"], keybinds["plugins.toggle"]), + ) add(config, "model", "model.dialog.provider", keybinds.model_provider_list) add(config, "model", "model.dialog.favorite", keybinds.model_favorite_toggle) diff --git a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts index 400eb38528..74e1b696f8 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts @@ -293,7 +293,12 @@ export function keymapBindingDefaults(input: { section: string; binding: Readonl export const KeymapConfig = z .object({ leader: z.string().prefault("ctrl+x"), - leader_timeout: z.number().int().positive().prefault(KeymapLeaderTimeoutDefault).describe("Leader key timeout in milliseconds"), + leader_timeout: z + .number() + .int() + .positive() + .prefault(KeymapLeaderTimeoutDefault) + .describe("Leader key timeout in milliseconds"), sections: KeymapSections, }) .strict() diff --git a/packages/opencode/src/cli/cmd/tui/config/tui.ts b/packages/opencode/src/cli/cmd/tui/config/tui.ts index 095bc2c882..429d7e5c1c 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui.ts @@ -23,12 +23,7 @@ import * as Log from "@opencode-ai/core/util/log" import { ConfigVariable } from "@/config/variable" import { Npm } from "@opencode-ai/core/npm" import { LegacyKeymapTransform } from "./legacy-keymap-transform" -import { - KeymapSectionNames, - keymapBindingDefaults, - type KeymapInfo, - type KeymapSection, -} from "./tui-schema" +import { KeymapSectionNames, keymapBindingDefaults, type KeymapInfo, type KeymapSection } from "./tui-schema" const log = Log.create({ service: "tui.config" }) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 81df918059..9ba300ea14 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -1101,8 +1101,7 @@ export function Session() { > {revert()!.reverted.length} message reverted - {redoShortcut()} or /redo to - restore + {redoShortcut()} or /redo to restore diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx index 617ede6395..811db7e82f 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/question.tsx @@ -220,11 +220,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) { }, }, ...(confirm() - ? [ - { key: "return", cmd: () => submit() }, - { key: "escape", cmd: () => reject() }, - ...sections.question, - ] + ? [{ key: "return", cmd: () => submit() }, { key: "escape", cmd: () => reject() }, ...sections.question] : [ ...Array.from({ length: max }, (_, index) => ({ key: String(index + 1), diff --git a/packages/opencode/test/config/tui.test.ts b/packages/opencode/test/config/tui.test.ts index 5acc3d84fa..5adff22422 100644 --- a/packages/opencode/test/config/tui.test.ts +++ b/packages/opencode/test/config/tui.test.ts @@ -428,16 +428,20 @@ test("resolves semantic keymap sections", async () => { expect(config.keymap.sections.global.find((binding) => binding.cmd === "command.palette.show")?.key).toBe("alt+p") expect(config.keymap.sections.global.find((binding) => binding.cmd === "session.new")?.key).toBe("n") expect(config.keymap.sections.prompt.find((binding) => binding.cmd === "prompt.editor")?.key).toBe("ctrl+e") - expect(config.keymap.sections.autocomplete.find((binding) => binding.cmd === "prompt.autocomplete.next")?.key).toBe("ctrl+j") - expect(config.keymap.sections.dialog_actions.find((binding) => binding.cmd === "dialog.action.toggle")?.key).toBe("ctrl+t") + expect(config.keymap.sections.autocomplete.find((binding) => binding.cmd === "prompt.autocomplete.next")?.key).toBe( + "ctrl+j", + ) + expect(config.keymap.sections.dialog_actions.find((binding) => binding.cmd === "dialog.action.toggle")?.key).toBe( + "ctrl+t", + ) expect(config.keymap.sections.model.find((binding) => binding.cmd === "model.dialog.favorite")?.key).toBe("ctrl+f") expect(config.keymap.sections.plugins.find((binding) => binding.cmd === "plugin.dialog.install")?.key).toBe("shift+i") expect(config.keymap.pick("plugins", ["plugin.dialog.install"]).map((binding) => binding.cmd)).toEqual([ "plugin.dialog.install", ]) - expect( - (config.keymap.pick("plugins", ["plugin.dialog.install"])[0] as { group?: unknown } | undefined)?.group, - ).toBe("Plugins") + expect((config.keymap.pick("plugins", ["plugin.dialog.install"])[0] as { group?: unknown } | undefined)?.group).toBe( + "Plugins", + ) expect(config.keymap.omit("plugins", ["plugin.dialog.install"]).map((binding) => binding.cmd)).toEqual([]) }) @@ -477,8 +481,12 @@ test("legacy keybinds transform into semantic keymap sections", async () => { ]) expect(config.keymap.sections.global.find((binding) => binding.cmd === "command.palette.show")?.key).toBe("alt+p") expect(config.keymap.sections.prompt.find((binding) => binding.cmd === "prompt.editor")?.key).toBe("ctrl+e") - expect(config.keymap.sections.autocomplete.find((binding) => binding.cmd === "prompt.autocomplete.next")?.key).toBe("ctrl+j") - expect(config.keymap.sections.dialog_actions.find((binding) => binding.cmd === "dialog.action.toggle")?.key).toBe("ctrl+t") + expect(config.keymap.sections.autocomplete.find((binding) => binding.cmd === "prompt.autocomplete.next")?.key).toBe( + "ctrl+j", + ) + expect(config.keymap.sections.dialog_actions.find((binding) => binding.cmd === "dialog.action.toggle")?.key).toBe( + "ctrl+t", + ) expect(config.keymap.sections.model.find((binding) => binding.cmd === "model.dialog.provider")?.key).toBe("ctrl+a") expect(config.keymap.sections.model.find((binding) => binding.cmd === "model.dialog.favorite")?.key).toBe("ctrl+f") expect(config.keymap.sections.plugins.find((binding) => binding.cmd === "plugin.dialog.install")?.key).toBe("shift+i") @@ -486,9 +494,9 @@ test("legacy keybinds transform into semantic keymap sections", async () => { expect(config.keymap.pick("plugins", ["plugin.dialog.install"]).map((binding) => binding.cmd)).toEqual([ "plugin.dialog.install", ]) - expect( - (config.keymap.omit("plugins", ["plugin.dialog.install"])[0] as { group?: unknown } | undefined)?.group, - ).toBe("Plugins") + expect((config.keymap.omit("plugins", ["plugin.dialog.install"])[0] as { group?: unknown } | undefined)?.group).toBe( + "Plugins", + ) expect(config.keymap.omit("plugins", ["plugin.dialog.install"]).map((binding) => binding.cmd)).toEqual([ "plugins.list", ]) diff --git a/packages/web/src/content/docs/keybinds.mdx b/packages/web/src/content/docs/keybinds.mdx index a137aef37f..599945428e 100644 --- a/packages/web/src/content/docs/keybinds.mdx +++ b/packages/web/src/content/docs/keybinds.mdx @@ -55,11 +55,11 @@ You do not need to use a leader key, but we recommend doing so. `keymap.sections` is grouped by semantic area. Each section contains command names and the key sequence that triggers them. -| Field | Description | -| ----- | ----------- | -| `leader` | The key used by `` sequences. Defaults to `ctrl+x`. | +| Field | Description | +| ---------------- | --------------------------------------------------------------------------------------------------- | +| `leader` | The key used by `` sequences. Defaults to `ctrl+x`. | | `leader_timeout` | How long OpenCode waits for the next key after the leader key, in milliseconds. Defaults to `2000`. | -| `sections` | A map of TUI areas to command bindings. | +| `sections` | A map of TUI areas to command bindings. | --- @@ -310,7 +310,7 @@ On native Windows, the defaults for undo and terminal suspend are different for - `input.undo` defaults to `ctrl+z,ctrl+-,super+z` when it is not explicitly configured (the `ctrl+z` binding is added because Windows terminals do not support POSIX suspend). - `terminal.suspend` is disabled because native Windows terminals do not support POSIX suspend. -::: + ::: ---