caique
API reference

caique/keys

Every export of caique/keys, with its signature and doc comment: decode, canonical, specOf, bindings, match, canReadKeys and 2 more, plus 5 types.

import { decode, canonical, specOf, … } from 'caique/keys';

Functions

bindings

Every binding in a keymap, in the order it was written, with each key in canonical form.

This is what a hint line is generated from. A spec that names no key, or two specs that name the same key ('G' and 'shift+g'), are refused here: a keymap that cannot be read one way is a hint line that can lie.

function bindings<Action extends string>(keymap: Keymap<Action>): Binding<Action>[];
ParameterType
keymapKeymap<Action>

Returns Binding<Action>[]

canonical

The one spelling of a key spec, or a KeysError naming what is wrong with it.

Modifiers are ctrl, meta (or alt) and shift, in any order and any case. A named key is case-insensitive (PageUp). A single uppercase letter on its own is that letter with shift (G is shift+g, which is what the terminal sends for it); after a modifier its case is ignored (Ctrl+C is ctrl+c — a terminal cannot tell the two apart anyway).

function canonical(spec: string): string;
ParameterType
specstring

Returns string

canReadKeys

Whether keys can be read from this input at all: a terminal that can be put into raw mode. A pipe, a file and /dev/null cannot, and no amount of waiting changes that.

function canReadKeys(input: InputStream): boolean;
ParameterType
inputInputStream

Returns boolean

decode

Every key in one chunk of input, in order — what one data event from a raw terminal means.

The chunk goes through node's own decoder. The one thing node will not say synchronously is a chunk that ends in Escape: it waits for the rest of a sequence, then reports escape. A chunk decoded on its own has no rest, so that report is made here, as node would make it. A partial sequence node is still holding (ESC [) is reported as nothing, which is also what node does with it.

function decode(chunk: string): KeyPress[];
ParameterType
chunkstring

Returns KeyPress[]

match

The action a key press is bound to in this keymap, or undefined.

function match<Action extends string>(keymap: Keymap<Action>, key: KeyPress): Action | undefined;
ParameterType
keymapKeymap<Action>
keyKeyPress

Returns Action \| undefined

readKeys

Read key presses from a terminal until the returned function is called.

Raw mode is taken once, here, and given back once, when the reader stops — or by the exit path, on a signal or a crash, through onExit (closeout's exit hook by default). If the input was already raw, somebody else owns that and it is left raw.

Throws E_NOT_A_TERMINAL at once, before listening to anything, when input cannot be read a key at a time (controlroom R7): the caller reads lines, or takes the flag, instead.

function readKeys(input: KeyInput, onKey: (key: KeyPress) => void, onExit?: Registrar): () => void;
ParameterType
inputKeyInput
onKey(key: KeyPress) => void
onExit (optional)Registrar

Returns () => void

specOf

A key press in a keymap's spelling, or undefined for a sequence nothing can bind.

function specOf(key: KeyPress): string | undefined;
ParameterType
keyKeyPress

Returns string \| undefined

Classes

KeysError

A refusal in the family's shape: what is wrong, and what to do about it.

class KeysError extends Error {
    readonly code: KeysErrorCode;
    readonly fix: string;
    constructor(code: KeysErrorCode, message: string, fix: string);
}

Interfaces

Binding

One entry of a keymap, with its key in the one spelling match() compares.

interface Binding<Action extends string = string> {
    key: string;
    action: Action;
}

KeyPress

One key press, as every consumer of this module sees it.

interface KeyPress {
    /**
     * `up`, `down`, `left`, `right`, `tab`, `enter`, `escape`, `backspace`, `delete`, `home`,
     * `end`, `pageup`, `pagedown`, `space`, a lowercase letter, any other printable character
     * as itself (`/`, `?`, `é`), or one of node's own names (`f1`, `insert`, `paste-start`).
     * Empty for a sequence nothing names, which no keymap can bind.
     */
    name: string;
    ctrl: boolean;
    meta: boolean;
    shift: boolean;
    /** The characters the terminal sent for it. */
    sequence: string;
}

Types

KeyInput

A readable terminal stream: process.stdin, or a test's double of one.

type KeyInput = NodeJS.ReadableStream & InputStream;

Keymap

Key spec → action name. Plain data: it can be written in JSON and diffed.

type Keymap<Action extends string = string> = Readonly<Record<string, Action>>;

KeysErrorCode

type KeysErrorCode = 'E_KEY_SPEC' | 'E_NOT_A_TERMINAL';

On this page