From 04c2ca7aede27e87bcb361e764769bf09a1b0a15 Mon Sep 17 00:00:00 2001 From: qiaozongming Date: Thu, 18 Jun 2026 19:06:18 +0800 Subject: [PATCH] fix(tui): preserve command keybinds while prompt suggestion is visible --- .../cli/cmd/tui/component/dialog-command.tsx | 24 ++++++++++++++----- .../cli/cmd/tui/component/prompt/index.tsx | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx index 36ef66d50..4acd466a0 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx @@ -46,6 +46,7 @@ function init() { const root = getOwner() const [registrations, setRegistrations] = createSignal[]>([]) const [suspendCount, setSuspendCount] = createSignal(0) + const [inputSuspendCount, setInputSuspendCount] = createSignal(0) const dialog = useDialog() const keybind = useKeybind() const lang = useLanguage() @@ -80,16 +81,20 @@ function init() { category: lang.t("tui.command.palette.suggested"), })), ) - const suspended = () => suspendCount() > 0 + const suspended = () => suspendCount() > 0 || inputSuspendCount() > 0 + const fullySuspended = () => suspendCount() > 0 + const inputSuspended = () => inputSuspendCount() > 0 const isTextEditingKey = (evt: Parameters[1]) => Object.keys(keybind.all).some( (name) => (name.startsWith("input_") || name === "history_previous" || name === "history_next") && keybind.match(name, evt), ) + const isSuspendedInputKey = (evt: Parameters[1]) => + evt.name === "tab" || evt.name === "escape" useKeyboard((evt) => { - if (suspended()) return + if (fullySuspended()) return if (dialog.stack.length > 0) return if (evt.defaultPrevented) return const textInputFocused = isEditBufferRenderable(renderer.currentFocusedRenderable) @@ -98,6 +103,7 @@ function init() { if (!isEnabled(option)) continue if (textEditingKey && !option.keybind?.startsWith("input_")) continue if (option.keybind && keybind.match(option.keybind, evt)) { + if (inputSuspended() && isSuspendedInputKey(evt)) return evt.preventDefault() option.onSelect?.(dialog) return @@ -131,10 +137,16 @@ function init() { ] }) }, - keybinds(enabled: boolean) { - setSuspendCount((count) => count + (enabled ? -1 : 1)) + keybinds(enabled: boolean, mode: "all" | "input" = "all") { + const update = (count: number) => count + (enabled ? -1 : 1) + if (mode === "input") setInputSuspendCount(update) + else setSuspendCount(update) + }, + suspended(mode?: "all" | "input") { + if (mode === "all") return fullySuspended() + if (mode === "input") return inputSuspended() + return suspended() }, - suspended, show() { dialog.replace(() => ) }, @@ -184,7 +196,7 @@ export function CommandProvider(props: ParentProps) { const keybind = useKeybind() useKeyboard((evt) => { - if (value.suspended()) return + if (value.suspended("all")) return if (dialog.stack.length > 0) return if (evt.defaultPrevented) return if (keybind.match("command_list", evt)) { 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 de82676a0..8ded8de69 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -453,8 +453,8 @@ export function Prompt(props: PromptProps) { // session change, status leaving idle). createEffect(() => { if (!ghost()) return - command.keybinds(false) - onCleanup(() => command.keybinds(true)) + command.keybinds(false, "input") + onCleanup(() => command.keybinds(true, "input")) }) const usage = createMemo(() => {