export interface KeyboardShortcut { key: string meta?: boolean ctrl?: boolean shift?: boolean alt?: boolean } export type Resolvable = T | (() => T) export function resolveResolvable(value: Resolvable): T { return typeof value === "function" ? (value as () => T)() : value } export interface Command { id: string label: Resolvable description: Resolvable keywords?: Resolvable shortcut?: KeyboardShortcut disabled?: Resolvable action: () => void | Promise category?: Resolvable } export function createCommandRegistry() { const commands = new Map() function register(command: Command) { commands.set(command.id, command) } function unregister(id: string) { commands.delete(id) } function get(id: string) { return commands.get(id) } function getAll() { return Array.from(commands.values()) } function execute(id: string) { const command = commands.get(id) if (command) { return command.action() } } function search(query: string) { if (!query) return getAll() const lowerQuery = query.toLowerCase() return getAll().filter((cmd) => { const label = resolveResolvable(cmd.label) const description = resolveResolvable(cmd.description) const keywords = cmd.keywords ? resolveResolvable(cmd.keywords) : undefined const category = cmd.category ? resolveResolvable(cmd.category) : undefined const labelMatch = label.toLowerCase().includes(lowerQuery) const descMatch = description.toLowerCase().includes(lowerQuery) const keywordMatch = keywords?.some((k) => k.toLowerCase().includes(lowerQuery)) const categoryMatch = category?.toLowerCase().includes(lowerQuery) return labelMatch || descMatch || keywordMatch || categoryMatch }) } return { register, unregister, get, getAll, execute, search, } } export type CommandRegistry = ReturnType