Quickstart
Semantic command graph, projections and wire parsing
This creates a runtime, contributes one command to the palette and slash surfaces, reads the snapshot, and runs the command:
import { createWhistle } from "@fungi.computer/whistle";
const whistle = createWhistle();
const lease = whistle.contribute({ sourceId: "editor", namespace: "editor", commands: [ { id: "save", title: "Save file", category: "File", projections: { palette: {}, slash: { aliases: ["save"] }, bindings: { values: ["mod+s"] }, }, handler: () => ({ saved: true }), }, ],});
const snapshot = whistle.snapshot();console.log(snapshot.palette.map((row) => row.command)); // [{ namespace: "editor", id: "save" }]console.log(snapshot.slash.map((row) => row.alias)); // ["save"]
const outcome = await whistle.execute( { namespace: "editor", id: "save" }, { origin: "palette" },);if (outcome.status === "handled") console.log(outcome.result); // { saved: true }
lease.dispose();whistle.close();A command is named by its typed key, { namespace, id }. Every projection row
carries that key as command, so a palette click, a key press and a slash
command all call the same execute with it. A row’s commandId is an opaque
render key Whistle mints: use it for React keys or dedupe, never build or parse
it. Namespaces may contain colons (an App surface such as
installation:inkcap); command ids may not.
Bind a browser keyboard
Section titled “Bind a browser keyboard”npm install @opentui/keymap@0.5.10import { createWhistle } from "@fungi.computer/whistle";import { bindHtmlKeymap } from "@fungi.computer/whistle/html";
const whistle = createWhistle();whistle.contribute({ sourceId: "browser", namespace: "browser", commands: [ { id: "save", title: "Save", category: "File", projections: { bindings: { values: ["mod+s"] } }, handler: () => ({ saved: true }), }, ],});const keyboard = bindHtmlKeymap(document.body, whistle, "keyboard");// Contributions and availability updates refresh the projected key layer.keyboard.dispose();whistle.close();dispose removes the Whistle layer and subscription, and can be called twice.
The underlying HTML keymap host follows the document’s lifetime; this helper
does not remove its DOM listeners. Commands still execute through Whistle.