tuika 0.6.0

The application framework for Rust terminal UIs — flexbox layout, overlays, focus, keymap, components, and safe ratatui interoperability.
Documentation
# tuika keymap

A host-agnostic binding engine: declare an application's key bindings once, as
data, and resolve incoming key presses to named commands. It sits beside the
[component gallery](components.md) and [terminal features](features.md) as a
third kind of tuika primitive — not a widget and not an escape sequence, but the
routing layer between input and behavior.

It follows the **register → dispatch → query** shape of
[OpenTUI's keymap](https://opentui.com/docs/keymap/overview/), adapted to
idiomatic Rust and to tuika's own [`Key`] events. Because it consumes the
already-translated `Key` — never a terminal type — the whole engine runs and
unit-tests without a PTY.
[API](https://docs.rs/tuika/latest/tuika/keymap/index.html)

## The model

Four types, smallest to largest:

- **`Chord`** — one key press plus its modifiers (`ctrl+r`, `alt+shift+tab`,
  `?`, `space`, the literal `ctrl++`). Parse it from a string with
  `Chord::parse`, or build it from a live event with `Chord::from_key`.
- **`KeySequence`** — one *or more* chords typed in order, written
  space-separated (`g g`, `ctrl+x s`). A single chord is a one-element sequence,
  so every binding is a sequence underneath.
- **`Layer`** — a named, prioritized group of bindings. A layer may be *gated*
  on runtime data (`when("mode", "search")`) so it is active only in a given
  application mode; an ungated layer is always active.
- **`Keymap<C>`** — the engine. It owns the layers, a small runtime-data store
  used to gate them, and the pending multi-stroke state. `C` is your command
  type — usually an enum.

## Defining bindings

Bindings are built declaratively and resolve to a command value of your choosing:

```rust
use tuika::keymap::{Keymap, Layer};

#[derive(Clone, Debug, PartialEq)]
enum Action { Search, Quit, Top, Close }

let mut keymap = Keymap::new()
    .layer(
        Layer::new("global")
            .bind_labeled("ctrl+r", "search", Action::Search)
            .bind("ctrl+d", Action::Quit)
            // A two-stroke sequence, vim-style.
            .bind("g g", Action::Top),
    );
```

`bind` (and `bind_labeled`, which also carries a help string) panics on a
malformed spec, because bindings are authored as static literals — a bad spec is
a programmer error caught on first run, like a malformed regex literal. For
config-sourced specs, use `Chord::parse` / `Layer::try_bind`, which return a
`Result`.

## Dispatching

Feed each translated `Key` to `dispatch`. It returns one of three outcomes:

```rust
use tuika::event::{Key, KeyCode};
use tuika::keymap::Dispatch;

let ctrl_r = Key { code: KeyCode::Char('r'), ctrl: true, alt: false, shift: false };
match keymap.dispatch(ctrl_r) {
    Dispatch::Command(action) => { /* run it */ }
    Dispatch::Pending         => { /* a multi-stroke sequence is mid-flight */ }
    Dispatch::Unmatched       => { /* no binding — handle the key yourself */ }
}
```

- **Single-stroke** bindings resolve immediately.
- **Multi-stroke** sequences accumulate: the first `g` of `g g` returns
  `Pending`, the second returns `Command(Top)`.
- An **exact** match wins immediately even when it is also the prefix of a
  longer binding — a bound `g` fires without waiting to see whether `g g`
  follows, so author overlapping bindings with that in mind.
- A sequence that **dead-ends** is dropped, and its final stroke is retried on
  its own so it can begin a fresh sequence. `Keymap::reset` abandons a pending
  sequence explicitly (e.g. on a focus change or a timeout tick — the engine
  keeps no clock of its own).

A key that no active binding matches returns `Unmatched`, so the host stays in
control of everything the keymap does not claim (typing into a text field,
say).

### Modifier normalization

Matching is on a normalized chord, so a binding matches the event a terminal
actually delivers. For a character key, Shift is folded into the character
itself (`?` arrives as `Char('?')`, not `Shift`+`Char('/')`), so the `shift`
flag is dropped for characters and kept meaningful only for non-character keys
(`Shift`+`Enter`). `Shift`+`Tab` folds to the distinct `BackTab` key terminals
report. Both the string parser and `Chord::from_key` apply the same rules, so a
parsed binding and a live event agree.

## Modes: layers gated on runtime data

`set_data` drives which layers are active, mirroring an application's modes. A
layer's `when` clauses must all match the current data for it to be active;
changing data recomputes activation and clears any now-impossible pending
sequence.

```rust
let mut keymap = Keymap::new()
    .layer(Layer::new("global").bind("ctrl+d", Action::Quit))
    .layer(
        Layer::new("panel")
            .when("mode", "panel")
            .bind("q", Action::Close),
    );

// `q` does nothing until the app enters panel mode…
keymap.set_data("mode", "panel");
// …now it closes the panel, while ctrl+d still quits in every mode.
```

When two active layers bind the same sequence, the higher `priority` wins, so an
overlay layer can shadow a global binding while it is up.

## Querying for help

`hints()` lists the currently-active bindings — key label, optional help text,
and command — ready to feed a [`KeyHints`](components.md) row or a help overlay.
Because activation is data-driven, the hints always reflect the current mode:

```rust
for hint in keymap.hints() {
    println!("{:<8} {}", hint.keys, hint.label.as_deref().unwrap_or(""));
    // e.g.  "Ctrl+R   search"
}
```

## Testing

The engine never touches the terminal, so behavior is tested by feeding
synthetic `Key`s and asserting the `Dispatch` outcome — no raw mode, no PTY:

```rust
let mut keymap = Keymap::new().layer(Layer::new("nav").bind("g g", Action::Top));
let g = Key::new(KeyCode::Char('g'));
assert_eq!(keymap.dispatch(g), Dispatch::Pending);
assert_eq!(keymap.dispatch(g), Dispatch::Command(Action::Top));
```

[`Key`]: https://docs.rs/tuika/latest/tuika/event/struct.Key.html