tui-pages 0.7.2

Core for TUI apps with multiple pages
Documentation
# Input Pipeline

The input pipeline turns a `KeyEvent` into one of: an action to run, plain text
for a field, a "waiting for the next key" state, or a cancellation. You rarely
touch it directly — `handle_key` runs it for you — but you do need to know the
binding syntax and how text input and chord sequences work.

## Binding syntax

Bindings are plain strings. Modifiers are joined with `+`; a multi-key sequence
is space-separated.

| Write | Means |
|-------|-------|
| `tab`, `enter`, `esc`, `space` | named keys |
| `up` `down` `left` `right` `home` `end` `pageup` `pagedown` | navigation keys |
| `backspace` `delete` `insert` | editing keys |
| `f1``f12` | function keys |
| `a`, `:`, `?` | single characters |
| `ctrl+s` | Ctrl + S |
| `alt+x` | Alt + X |
| `shift+tab` | Shift + Tab (normalized to BackTab) |
| `ctrl+shift+p` | combined modifiers |
| `g h` | press `g`, then `h` (a sequence) |

Modifier names are case-insensitive and have short forms: `ctrl`/`control`/`c`,
`alt`/`meta`/`m`, `shift`/`s`. So `ctrl+s` and `c+s` are the same binding.

> This is the **string** format — `ctrl+`, not the `C-` you may have seen
> elsewhere. The examples all use this form.

## Binding keys to actions

On the builder, `.bind(mode, binding, action)` registers a key in a mode:

```rust
TuiPages::builder(View::Home)
    .bind(modes::GENERAL, "tab", Action::FocusNext)
    .bind(modes::GENERAL, "enter", Action::Select)
    .bind(modes::GENERAL, "g h", Action::GotoHome)   // sequence
    .bind(modes::GLOBAL,  "ctrl+c", Action::Quit)    // works everywhere
```

A key only fires if its mode is in the current page's `PageSpec::modes`.
`GLOBAL` is always active.

## Multi-key sequences

Bind a space-separated sequence and the runtime waits for the rest after the
first key:

```rust
.bind(modes::GENERAL, "g h", Action::GotoHome)
.bind(modes::GENERAL, "g n", Action::GotoNotes)
.bind(modes::GENERAL, "ctrl+x ctrl+c", Action::ForceQuit)
```

While waiting, `handle_key` returns `TuiPagesStatus::Waiting(hints)` — those
hints are the candidate continuations, which you can show in a status bar. If
the next key doesn't match, or the timeout (`input_timeout_ms`, default 1000ms)
elapses, the sequence cancels.

## Text input

By default every key is looked up as a binding. When a page sets
`accepts_text_input(true)`, plain character keys that aren't bound instead flow
through as text:

```rust
PageSpec::new()
    .accepts_text_input(true)
    .modes(vec![modes::INSERT])
```

Now `handle_key` returns `TuiPagesStatus::TextHandled` for ordinary typing, and
your handler's `handle_text(chord, ctx, state)` gets the `KeyChord`. Bound keys
(like `esc` to leave the field) still fire as actions.

## What `handle_key` returns

`handle_key` returns a `TuiPagesOutput` whose `status` is one of:

```rust
pub enum TuiPagesStatus<A> {
    ActionHandled,            // a binding matched; your handler ran
    TextHandled,             // text flowed to handle_text
    Waiting(Vec<InputHint<A>>), // mid-sequence; show the hints
    Cancelled,               // sequence expired or didn't match
    CommandIncomplete(_),    // from submit_command — see Commands
    CommandUnknown,
    CommandEmpty,
}
```

and a `quit_requested: bool` set when an effect asked to quit. Most loops only
check `quit_requested` and stash `Waiting` hints for display:

```rust
let output = tui.handle_key(key, state)?;
match output.status {
    TuiPagesStatus::Waiting(hints) => waiting = hints,
    _ => waiting.clear(),
}
if output.quit_requested {
    return Ok(());
}
```

## Parsing bindings yourself

If you load keybindings from a config file, the parse functions are public:

```rust
use tui_pages::{parse_binding, try_parse_binding, parse_key, try_parse_key};

// Lenient: drops tokens it can't parse. Good for trusted, in-code strings.
let chords: Vec<KeyChord> = parse_binding("ctrl+x z");

// Strict: reports the first bad token. Use for user-editable config.
match try_parse_binding("ctrl+shft+x") {
    Ok(chords) => { /* ... */ }
    Err(e) => eprintln!("bad binding: {e}"),  // UnknownKey("ctrl+shft+x")
}
```

`parse_binding` returns a *sequence* (`Vec<KeyChord>`); `parse_key` parses a
single chord. To build a chord from a live event, use
`KeyChord::from_event(&key_event)`.