Input Pipeline - tui-pages

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

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

    #![allow(unused)]
    fn main() {
    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:

    #![allow(unused)]
    fn main() {
    .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:

    #![allow(unused)]
    fn main() {
    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:

    #![allow(unused)]
    fn main() {
    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:

    #![allow(unused)]
    fn main() {
    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:

    #![allow(unused)]
    fn main() {
    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)`.