# `Ascii.ExRatatui`
[🔗](https://github.com/houllette/ascii_art/blob/main/lib/ascii/ex_ratatui.ex#L2)

Frames for [ExRatatui](https://hexdocs.pm/ex_ratatui) terminal UIs: as
ratatui text, as a `Paragraph`, or placed in an area of the screen.

    def render(state, frame) do
      area = %ExRatatui.Layout.Rect{x: 0, y: 0, width: frame.width, height: frame.height}
      [Ascii.ExRatatui.place(state.art, area, title: "donut")]
    end

A `%Ascii.Frame{}` is an `ExRatatui.Widget` too, drawn from the top
left of its rect with the defaults below, so `[{frame, rect}]` draws it
as it is.

For a ready-made app that plays pieces full screen, locally or over SSH,
see `Ascii.ExRatatui.Viewer`; the [ExRatatui guide](ex_ratatui.md)
has dashboards and testing.

## Options

Every function here takes:

  * `:mode` - how colour is drawn: `:truecolor` (the default), the
    nearest of xterm's 256 colours (`:ansi256`), for terminals without
    24-bit colour, or `:mono`, in the terminal's own colours. As for
    `Ascii.Render.ANSI`.
  * `:ground` - paint a coloured piece's ground behind it (default
    `true`).

and `paragraph/2` and `place/3`:

  * `:title` - a title, on a border around the picture.
  * `:border` - a border without a title (default `false`).

Only available when `ex_ratatui` is a dependency.

# `option`

```elixir
@type option() ::
  {:mode, Ascii.Render.ANSI.mode()}
  | {:ground, boolean()}
  | {:title, String.t()}
  | {:border, boolean()}
```

See the module docs.

# `event`

```elixir
@spec event(
  event :: ExRatatui.Event.Key.t() | ExRatatui.Event.Mouse.t() | term(),
  placed ::
    {ExRatatui.Widgets.Paragraph.t(), ExRatatui.Layout.Rect.t()}
    | ExRatatui.Layout.Rect.t()
) :: Ascii.event() | nil
```

An ExRatatui event as one for a piece (`t:Ascii.event/0`), or `nil`:

  * a key pressed or held, by its code (`"up"`, `"enter"`, `"a"`), unless
    ctrl, alt or super is held with it;
  * a left click inside the picture, as the frame's cell clicked.
    `placed` says where the picture is: what `place/3` returned (its
    border, centring and scroll allowed for), or the rect whose top
    left cell is the frame's (as `[{frame, rect}]` draws it).

Mouse events need `mouse_capture: true` (see `ExRatatui.run/2`).

# `handle_event`

```elixir
@spec handle_event(
  anim :: Ascii.Animation.t(),
  event :: term(),
  placed ::
    {ExRatatui.Widgets.Paragraph.t(), ExRatatui.Layout.Rect.t()}
    | ExRatatui.Layout.Rect.t()
) :: {:ok, Ascii.Animation.t()} | :ignore
```

An ExRatatui event for a playing piece (see `event/2`), taken up as
`Ascii.handle_event/2` takes it: `{:ok, anim}`, or `:ignore` for an
event that is not one for a piece, or that it had no use for.

    def handle_event(event, state) do
      case Ascii.ExRatatui.handle_event(state.tree, event, state.placed) do
        {:ok, tree} -> {:noreply, %{state | tree: tree}}
        :ignore -> {:noreply, state}
      end
    end

# `lines`

```elixir
@spec lines(frame :: Ascii.Frame.t(), opts :: [option()]) :: [ExRatatui.Text.Line.t()]
```

The frame as ratatui lines, one a row: a span for each run of one ink,
on the ground. Spaces show no ink, so they join the run they are in.
A frame without colours is one plain span a line.

# `paragraph`

```elixir
@spec paragraph(frame :: Ascii.Frame.t(), opts :: [option()]) ::
  ExRatatui.Widgets.Paragraph.t()
```

The frame as a `Paragraph`, in a bordered `Block` if given a `:title` or
`border: true`. Its rect wants the frame's `cols` and `rows`, and two
more of each for a border; `place/3` works that out.

# `place`

```elixir
@spec place(
  frame :: Ascii.Frame.t(),
  area :: ExRatatui.Layout.Rect.t(),
  opts :: [option()]
) ::
  {ExRatatui.Widgets.Paragraph.t(), ExRatatui.Layout.Rect.t()}
```

The frame as a `Paragraph` and the rect to draw it in: its own size,
centred in `area`. A box is wide enough for its title, the picture in
the middle of it. Too big for the area, it fills it and shows its
middle.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
