Quickstart
This page registers one command and dispatches it from two surfaces in a web page: a button and a key chord.
Register a command
ts
import { OK, Registry, ok } from '@ufo-soft/caelus'
import { keybindings } from '@ufo-soft/caelus/keybindings'
let count = 0
const registry = new Registry()
registry.register({
name: 'increment',
command: { description: 'Add one', parameters: [] },
handler: () => ok(++count),
})A registration has three parts. The name is how every surface calls the command. The command describes it: a description and its parameters, here none. The handler does the work and returns an outcome: ok with data, or failed with a reason.
Dispatch it from two surfaces
ts
// A button per command, built from the registry
for (const { name, command } of registry.all()) {
const button = document.createElement('button')
button.textContent = name
button.title = command.description
button.onclick = () => registry.invoke(name, {})
document.body.append(button)
}
// A key chord for the same command
keybindings(registry, { 'alt+up': 'increment' }).attach()The buttons come from registry.all(), so every registered command gets one with no per-command code. The keymap maps the chord alt+up to the command's name. Both call registry.invoke with the name and the args.
Read the outcome
ts
const outcome = await registry.invoke('increment', {})
if (outcome.kind === OK) console.log(outcome.data)txt
1invoke always returns a promise of an outcome. Check kind before reading data: a failed outcome has a reason instead.
Next
- Commands: parameters, handlers, outcomes, and the registry in full.
- Keybindings, CLI, MCP: the surfaces.