/** * KHAELOR * File: src/tui/commands.ts * Description: The single command registry — one source powers keys, slash commands, and the universal palette (TUI_DESIGN §4.2). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import type { PaletteItem } from "./composer/palette.js"; export interface CommandDef { /** Stable id, e.g. `diff.show`. */ id: string; /** Palette title, e.g. `View diff`. */ title: string; /** Slash form, e.g. `/diff`. */ slash?: string; /** Bound key, shown in the universal palette, e.g. `d`. */ key?: string; /** Short description for the slash palette. */ description?: string; run(): void; } /** * All bindings are declared here with name/title/key, so every palette lists * them and a future rebinding config gets them for free — users never need * to memorize commands. */ export class CommandRegistry { private readonly commands = new Map(); register(def: CommandDef): void { this.commands.set(def.id, def); } find(id: string): CommandDef | null { return this.commands.get(id) ?? null; } bySlash(slash: string): CommandDef | null { for (const def of this.commands.values()) { if (def.slash === slash) return def; } return null; } list(): CommandDef[] { return [...this.commands.values()]; } /** Slash-palette items: `/name description`. */ slashItems(): PaletteItem[] { return this.list() .filter((d) => d.slash !== undefined) .map((d) => { const item: PaletteItem = { id: d.id, label: d.slash as string }; if (d.description !== undefined) item.detail = d.description; return item; }); } /** Universal-palette items: `Title /slash key`. */ paletteItems(): PaletteItem[] { return this.list().map((d) => { const item: PaletteItem = { id: d.id, label: d.title }; if (d.slash !== undefined) item.detail = d.slash; if (d.key !== undefined) item.keyHint = d.key; return item; }); } }