mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 17:54:23 +01:00
108 lines
2.6 KiB
TypeScript
108 lines
2.6 KiB
TypeScript
import { useDialog } from "@tui/ui/dialog"
|
|
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
|
|
import {
|
|
createContext,
|
|
createMemo,
|
|
createSignal,
|
|
onCleanup,
|
|
useContext,
|
|
type Accessor,
|
|
type ParentProps,
|
|
} from "solid-js"
|
|
import { useKeyboard } from "@opentui/solid"
|
|
import { useKeybind } from "@tui/context/keybind"
|
|
import type { KeybindsConfig } from "@opencode-ai/sdk"
|
|
|
|
type Context = ReturnType<typeof init>
|
|
const ctx = createContext<Context>()
|
|
|
|
export type CommandOption = DialogSelectOption & {
|
|
keybind?: keyof KeybindsConfig
|
|
}
|
|
|
|
function init() {
|
|
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
|
|
const dialog = useDialog()
|
|
const keybind = useKeybind()
|
|
const options = createMemo(() => {
|
|
return registrations().flatMap((x) => x())
|
|
})
|
|
|
|
let keybinds = true
|
|
useKeyboard((evt) => {
|
|
if (!keybinds) return
|
|
for (const option of options()) {
|
|
if (option.keybind && keybind.match(option.keybind, evt)) {
|
|
evt.preventDefault()
|
|
option.onSelect?.(dialog)
|
|
return
|
|
}
|
|
}
|
|
})
|
|
|
|
const result = {
|
|
trigger(name: string, source?: "prompt") {
|
|
for (const option of options()) {
|
|
if (option.value === name) {
|
|
option.onSelect?.(dialog, source)
|
|
return
|
|
}
|
|
}
|
|
},
|
|
keybinds(enabled: boolean) {
|
|
keybinds = enabled
|
|
},
|
|
show() {
|
|
dialog.replace(() => <DialogCommand options={options()} />)
|
|
},
|
|
register(cb: () => CommandOption[]) {
|
|
const results = createMemo(cb)
|
|
setRegistrations((arr) => [results, ...arr])
|
|
onCleanup(() => {
|
|
setRegistrations((arr) => arr.filter((x) => x !== results))
|
|
})
|
|
},
|
|
get options() {
|
|
return options()
|
|
},
|
|
}
|
|
return result
|
|
}
|
|
|
|
export function useCommandDialog() {
|
|
const value = useContext(ctx)
|
|
if (!value) {
|
|
throw new Error("useCommandDialog must be used within a CommandProvider")
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function CommandProvider(props: ParentProps) {
|
|
const value = init()
|
|
const dialog = useDialog()
|
|
const keybind = useKeybind()
|
|
|
|
useKeyboard((evt) => {
|
|
if (keybind.match("command_list", evt) && dialog.stack.length === 0) {
|
|
evt.preventDefault()
|
|
dialog.replace(() => <DialogCommand options={value.options} />)
|
|
return
|
|
}
|
|
})
|
|
|
|
return <ctx.Provider value={value}>{props.children}</ctx.Provider>
|
|
}
|
|
|
|
function DialogCommand(props: { options: CommandOption[] }) {
|
|
const keybind = useKeybind()
|
|
return (
|
|
<DialogSelect
|
|
title="Commands"
|
|
options={props.options.map((x) => ({
|
|
...x,
|
|
footer: x.keybind ? keybind.print(x.keybind) : undefined,
|
|
}))}
|
|
/>
|
|
)
|
|
}
|