# 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.

Source: https://caique.interlace.tools/docs/api/keys

<!-- Generated by scripts/api-reference.ts from the built dist/*.d.ts. Do not edit; run `npx tsx scripts/api-reference.ts`. -->

```ts
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.

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

| Parameter | Type |
| :-- | :-- |
| `keymap` | `Keymap<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).

```ts
function canonical(spec: string): string;
```

| Parameter | Type |
| :-- | :-- |
| `spec` | `string` |

**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.

```ts
function canReadKeys(input: InputStream): boolean;
```

| Parameter | Type |
| :-- | :-- |
| `input` | `InputStream` |

**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.

```ts
function decode(chunk: string): KeyPress[];
```

| Parameter | Type |
| :-- | :-- |
| `chunk` | `string` |

**Returns** `KeyPress[]`

### match

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

```ts
function match<Action extends string>(keymap: Keymap<Action>, key: KeyPress): Action | undefined;
```

| Parameter | Type |
| :-- | :-- |
| `keymap` | `Keymap<Action>` |
| `key` | `KeyPress` |

**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.

```ts
function readKeys(input: KeyInput, onKey: (key: KeyPress) => void, onExit?: Registrar): () => void;
```

| Parameter | Type |
| :-- | :-- |
| `input` | `KeyInput` |
| `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.

```ts
function specOf(key: KeyPress): string | undefined;
```

| Parameter | Type |
| :-- | :-- |
| `key` | `KeyPress` |

**Returns** `string \| undefined`

## Classes

### KeysError

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

```ts
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.

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

### KeyPress

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

```ts
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.

```ts
type KeyInput = NodeJS.ReadableStream & InputStream;
```

### Keymap

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

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

### KeysErrorCode

```ts
type KeysErrorCode = 'E_KEY_SPEC' | 'E_NOT_A_TERMINAL';
```
