fix(tui): prevent keybind dispatch during modal prompts
Tab key during QuestionPrompt/PermissionPrompt triggered agent_cycle because CommandProvider useKeyboard handler lacked guards for active modal state. Root cause: opentui fires all global keyboard handlers in mount order (parent first), so parent handler processed Tab before child could preventDefault. Fix: suspend command keybinds while QuestionPrompt or PermissionPrompt is active (matching existing autocomplete.tsx pattern), and add defaultPrevented guard as defense-in-depth. Clamp suspendCount to >= 0 to prevent mismatched enable/disable calls.pull/17141/head
parent
7b0def4b81
commit
11916aeea1
|
|
@ -60,6 +60,7 @@ function init() {
|
|||
useKeyboard((evt) => {
|
||||
if (suspended()) return
|
||||
if (dialog.stack.length > 0) return
|
||||
if (evt.defaultPrevented) return
|
||||
for (const option of entries()) {
|
||||
if (!isEnabled(option)) continue
|
||||
if (option.keybind && keybind.match(option.keybind, evt)) {
|
||||
|
|
@ -93,7 +94,7 @@ function init() {
|
|||
})
|
||||
},
|
||||
keybinds(enabled: boolean) {
|
||||
setSuspendCount((count) => count + (enabled ? -1 : 1))
|
||||
setSuspendCount((count) => Math.max(0, count + (enabled ? -1 : 1)))
|
||||
},
|
||||
suspended,
|
||||
show() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { createStore } from "solid-js/store"
|
||||
import { createMemo, For, Match, Show, Switch } from "solid-js"
|
||||
import { createMemo, For, Match, onCleanup, onMount, Show, Switch } from "solid-js"
|
||||
import { Portal, useKeyboard, useRenderer, useTerminalDimensions, type JSX } from "@opentui/solid"
|
||||
import type { TextareaRenderable } from "@opentui/core"
|
||||
import { useKeybind } from "../../context/keybind"
|
||||
|
|
@ -16,6 +16,7 @@ import { Locale } from "@/util/locale"
|
|||
import { Global } from "@/global"
|
||||
import { useDialog } from "../../ui/dialog"
|
||||
import { useTuiConfig } from "../../context/tui-config"
|
||||
import { useCommandDialog } from "../../component/dialog-command"
|
||||
|
||||
type PermissionStage = "permission" | "always" | "reject"
|
||||
|
||||
|
|
@ -129,10 +130,14 @@ function TextBody(props: { title: string; description?: string; icon?: string })
|
|||
export function PermissionPrompt(props: { request: PermissionRequest }) {
|
||||
const sdk = useSDK()
|
||||
const sync = useSync()
|
||||
const command = useCommandDialog()
|
||||
const [store, setStore] = createStore({
|
||||
stage: "permission" as PermissionStage,
|
||||
})
|
||||
|
||||
onMount(() => command.keybinds(false))
|
||||
onCleanup(() => command.keybinds(true))
|
||||
|
||||
const session = createMemo(() => sync.data.session.find((s) => s.id === props.request.sessionID))
|
||||
|
||||
const input = createMemo(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { createStore } from "solid-js/store"
|
||||
import { createMemo, createSignal, For, Show } from "solid-js"
|
||||
import { createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js"
|
||||
import { useKeyboard } from "@opentui/solid"
|
||||
import type { TextareaRenderable } from "@opentui/core"
|
||||
import { useKeybind } from "../../context/keybind"
|
||||
|
|
@ -9,12 +9,17 @@ import { useSDK } from "../../context/sdk"
|
|||
import { SplitBorder } from "../../component/border"
|
||||
import { useTextareaKeybindings } from "../../component/textarea-keybindings"
|
||||
import { useDialog } from "../../ui/dialog"
|
||||
import { useCommandDialog } from "../../component/dialog-command"
|
||||
|
||||
export function QuestionPrompt(props: { request: QuestionRequest }) {
|
||||
const sdk = useSDK()
|
||||
const { theme } = useTheme()
|
||||
const keybind = useKeybind()
|
||||
const bindings = useTextareaKeybindings()
|
||||
const command = useCommandDialog()
|
||||
|
||||
onMount(() => command.keybinds(false))
|
||||
onCleanup(() => command.keybinds(true))
|
||||
|
||||
const questions = createMemo(() => props.request.questions)
|
||||
const single = createMemo(() => questions().length === 1 && questions()[0]?.multiple !== true)
|
||||
|
|
|
|||
Loading…
Reference in New Issue