# Canvas

Enable first-class canvas support with one feature:

```toml
tui-pages = { version = "...", features = ["canvas"] }
```

The `canvas` feature intentionally enables the full canvas surface at once:
form editor, ratatui rendering, suggestions, validation, computed fields,
cursor style helpers, textarea, textinput, keymap handling, and crossterm input
session helpers.

## App Action Shape

Use a wrapper action and implement `From<CanvasAction>`:

```rust
use tui_pages::canvas::CanvasAction;

enum Action {
    Canvas(CanvasAction),
    Quit,
}

impl From<CanvasAction> for Action {
    fn from(action: CanvasAction) -> Self {
        Action::Canvas(action)
    }
}
```

Then install standard bindings and typed text routing:

```rust
let app = TuiPages::builder(View::Editor)
    .page_fn(page_spec)
    .handler(Handler)
    .canvas_defaults()
    .build();
```

`PageSpec::canvas_editor(&editor)` or `PageSpec::canvas_mode(mode)` keeps the
active input mode stack aligned with the focused canvas widget.

For custom wiring, `.canvas_keymaps()` installs only the standard NORMAL,
INSERT, and SELECT keymaps, while `.canvas_text_actions()` installs only the
typed-character mapper that turns plain text into `CanvasAction::InsertChar`.
For lower-level standalone textarea and textinput wiring, route raw key events
through the helpers below.

If you want `tui-pages` to own the canvas key dispatch directly, implement
`CanvasWidgetState` for your app state and use the widget builders:

```rust
impl tui_pages::canvas::CanvasWidgetState for State {
    fn canvas_form_editor(
        &mut self,
        id: usize,
    ) -> Option<&mut dyn tui_pages::canvas::CanvasFormEditorHost> {
        match id {
            0 => Some(&mut self.editor),
            _ => None,
        }
    }

    fn canvas_textarea(
        &mut self,
        focus_index: usize,
    ) -> Option<&mut dyn tui_pages::canvas::CanvasTextAreaHost> {
        match focus_index {
            0 => Some(&mut self.body),
            _ => None,
        }
    }

    fn canvas_textarea_entered(&mut self, focus_index: usize) -> Option<&mut bool> {
        match focus_index {
            0 => Some(&mut self.body_entered),
            _ => None,
        }
    }

    fn canvas_textinput_suggestion_suffix(
        &mut self,
        focus_index: usize,
        text: &str,
    ) -> Option<String> {
        match (focus_index, text) {
            (1, "adm") => Some("in".to_string()),
            _ => None,
        }
    }
}

let app = TuiPages::builder(View::Editor)
    .page_fn(page_spec)
    .handler(Handler)
    .canvas_form_editor(0)
    .canvas_textarea_widget(0)
    .build();
```

`canvas_form_editor` dispatches canvas actions into the `FormEditor` and returns
focus effects at editor boundaries, so your application action enum does not
need a `Canvas(CanvasAction)` variant. The textarea and textinput builders treat
their focused widget as one top-level stop until Enter sets the supplied
`entered` flag. Once entered, NORMAL-mode keys drive the widget's modal editor,
INSERT-mode keys go directly to the text widget, Esc exits edit mode and then
the widget, and boundary/submit keys hand focus back to `tui-pages`.
For textinput widgets, `canvas_textinput_suggestion_suffix` is optional; when it
returns `Some(suffix)`, the widget shows that suffix inline and accepts it with
Tab.

## FormEditor Handoff

The app owns editor state. In the action handler, dispatch canvas actions and
return focus effects when the editor reaches a boundary:

```rust
match tui_pages::dispatch_canvas_action(&mut state.editor, action) {
    CanvasDispatchOutcome::Focus(intent) => {
        ActionOutcome::effect(TuiEffect::Focus(intent))
    }
    CanvasDispatchOutcome::Applied(result) => {
        // handle messages, validation errors, persistence flags, recompute, etc.
        ActionOutcome::none()
    }
}
```

`ExitCanvasForward` and `ExitCanvasBackward` are handled by the normal
`FocusManager`, so fields can sit beside buttons, sections, modals, buffers, and
panes without custom focus code.

## Textarea And Textinput

For top-level textarea and textinput widgets, route raw key events through the
typed helpers:

```rust
let outcome = tui_pages::dispatch_canvas_text_area_key(&mut state.notes, key);
let outcome = tui_pages::dispatch_canvas_text_input_key(&mut state.command, key);
```

These return `Handled`, `Submitted`, `NotHandled`, or a focus intent. `Tab` and
vertical boundary keys map to the same forward/backward canvas exits used by
`FormEditor`.

## Rendering

Canvas renderers are re-exported from `tui_pages::canvas`. The common
form-editor path is:

```rust
tui_pages::render_canvas_with_suggestions_default(
    frame,
    frame.area(),
    form_area,
    &state.editor,
);
```

This draws the editor and, when suggestions are active, anchors the dropdown to
the active input rectangle returned by the renderer. Textarea and textinput use
the re-exported stateful widgets and expose `cursor(area, block)` helpers; set
the ratatui cursor from those values after rendering the focused widget.

## Cursor Style

Cursor style is opt-in. Call `update_cursor_style_for_editor(&editor)` or
`update_cursor_style_for_mode(mode)` from your event/render loop when you want
terminal cursor shape updates. `tui-pages` does not mutate terminal cursor style
unless the host calls these helpers.

## Example

See `examples/canvas/minimal` for the smallest form editor setup,
`examples/canvas/textarea` for direct textinput/textarea dispatch, and
`examples/canvas/full` for form editor, suggestions, validation, computed
fields, cursor/session helpers, textarea, textinput, and canvas focus handoff
together.
