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 4babe4d36..771cd292c 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx @@ -11,17 +11,22 @@ import { } from "solid-js" import { useKeyboard } from "@opentui/solid" import { useKeybind } from "@tui/context/keybind" +import type { KeybindsConfig } from "@opencode-ai/sdk" type Context = ReturnType const ctx = createContext() +export type CommandOption = DialogSelectOption & { + keybind?: keyof KeybindsConfig +} + function init() { - const [registrations, setRegistrations] = createSignal[]>([]) + const [registrations, setRegistrations] = createSignal[]>([]) const dialog = useDialog() + const keybind = useKeybind() const options = createMemo(() => { return registrations().flatMap((x) => x()) }) - const keybind = useKeybind() useKeyboard((evt) => { for (const option of options()) { @@ -42,7 +47,7 @@ function init() { } } }, - register(cb: () => DialogSelectOption[]) { + register(cb: () => CommandOption[]) { const results = createMemo(cb) setRegistrations((arr) => [results, ...arr]) onCleanup(() => { @@ -78,6 +83,12 @@ export function CommandProvider(props: ParentProps) { return {props.children} } -function DialogCommand(props: { options: DialogSelectOption[] }) { - return +function DialogCommand(props: { options: CommandOption[] }) { + const keybind = useKeybind() + return ( + ({ ...x, footer: x.keybind ? keybind.print(x.keybind) : undefined }))} + /> + ) } diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 32e00882b..86a70619e 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -4,6 +4,7 @@ import { useSync } from "@tui/context/sync" import { map, pipe, flatMap, entries, filter, isDeepEqual } from "remeda" import { DialogSelect } from "@tui/ui/dialog-select" import { useDialog } from "@tui/ui/dialog" +import { Keybind } from "@/util/keybind" export function DialogModel() { const local = useLocal() diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 8c64671f5..d50c21cad 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -4,6 +4,7 @@ import { useRoute } from "@tui/context/route" import { useSync } from "@tui/context/sync" import { createMemo, onMount } from "solid-js" import { Locale } from "@/util/locale" +import { Keybind } from "@/util/keybind" export function DialogSessionList() { const dialog = useDialog() @@ -45,6 +46,13 @@ export function DialogSessionList() { }) dialog.clear() }} + keybind={[ + { + keybind: Keybind.parse("del")[0], + title: "delete", + onTrigger: (option) => {}, + }, + ]} /> ) } diff --git a/packages/opencode/src/cli/cmd/tui/context/keybind.tsx b/packages/opencode/src/cli/cmd/tui/context/keybind.tsx index 3a11eab08..4d801e248 100644 --- a/packages/opencode/src/cli/cmd/tui/context/keybind.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/keybind.tsx @@ -76,16 +76,19 @@ export const { use: useKeybind, provider: KeybindProvider } = createSimpleContex get leader() { return store.leader }, - match(key: keyof KeybindsConfig, evt: ParsedKey) { - const keybind = keybinds()[key] - if (!keybind) return false - const parsed: Keybind.Info = { + parse(evt: ParsedKey) { + return { ctrl: evt.ctrl, name: evt.name, shift: false, leader: store.leader, option: evt.option, } + }, + match(key: keyof KeybindsConfig, evt: ParsedKey) { + const keybind = keybinds()[key] + if (!keybind) return false + const parsed: Keybind.Info = result.parse(evt) for (const key of keybind) { if (Keybind.match(key, parsed)) { return true diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx index 839157fc8..4bced5680 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx @@ -10,7 +10,7 @@ export function DialogMessage(props: { messageID: string; sessionID: string }) { return ( { title: string options: DialogSelectOption[] onFilter?: (query: string) => void onSelect?: (option: DialogSelectOption) => void + keybind?: { + keybind: Keybind.Info + title: string + onTrigger: (option: DialogSelectOption) => void + }[] limit?: number current?: T } @@ -22,7 +27,6 @@ export interface DialogSelectProps { export interface DialogSelectOption { title: string value: T - keybind?: keyof KeybindsConfig description?: string footer?: string category?: string @@ -99,6 +103,7 @@ export function DialogSelect(props: DialogSelectProps) { } } + const keybind = useKeybind() useKeyboard((evt) => { if (evt.name === "up") move(-1) if (evt.name === "down") move(1) @@ -109,10 +114,15 @@ export function DialogSelect(props: DialogSelectProps) { if (option.onSelect) option.onSelect(dialog) props.onSelect?.(option) } + + for (const item of props.keybind ?? []) { + if (Keybind.match(item.keybind, keybind.parse(evt))) { + item.onTrigger(selected()) + } + } }) let scroll: ScrollBoxRenderable - const keybind = useKeybind() return ( @@ -179,7 +189,7 @@ export function DialogSelect(props: DialogSelectProps) { >