import { createMemo } from "solid-js" import { useSync } from "@tui/context/sync" import { Keybind } from "@/util/keybind" import { pipe, mapValues } from "remeda" import type { KeybindsConfig } from "@opencode-ai/sdk/v2" import type { ParsedKey, Renderable } from "@opentui/core" import { createStore } from "solid-js/store" import { useKeyboard, useRenderer } from "@opentui/solid" import { createSimpleContext } from "./helper" export const { use: useKeybind, provider: KeybindProvider } = createSimpleContext({ name: "Keybind", init: () => { const sync = useSync() const keybinds = createMemo(() => { return pipe( sync.data.config.keybinds ?? {}, mapValues((value) => Keybind.parse(value)), ) }) const [store, setStore] = createStore({ leader: false, }) const renderer = useRenderer() let focus: Renderable | null let timeout: NodeJS.Timeout function leader(active: boolean) { if (active) { setStore("leader", true) focus = renderer.currentFocusedRenderable focus?.blur() if (timeout) clearTimeout(timeout) timeout = setTimeout(() => { if (!store.leader) return leader(false) if (focus) { focus.focus() } }, 2000) return } if (!active) { if (focus && !renderer.currentFocusedRenderable) { focus.focus() } setStore("leader", false) } } useKeyboard(async (evt) => { if (!store.leader && result.match("leader", evt)) { leader(true) return } if (store.leader && evt.name) { setImmediate(() => { if (focus && renderer.currentFocusedRenderable === focus) { focus.focus() } leader(false) }) } }) const result = { get all() { return keybinds() }, get leader() { return store.leader }, parse(evt: ParsedKey): Keybind.Info { // Handle special case for Ctrl+Underscore (represented as \x1F) if (evt.name === "\x1F") { return Keybind.fromParsedKey({ ...evt, name: "_", ctrl: true }, store.leader) } return Keybind.fromParsedKey(evt, store.leader) }, 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 } } }, print(key: keyof KeybindsConfig) { const first = keybinds()[key]?.at(0) if (!first) return "" const result = Keybind.toString(first) return result.replace("", Keybind.toString(keybinds().leader![0]!)) }, } return result }, })