ExRatatui.App behaviour (ExRatatui v0.5.1)

Copy Markdown View Source

A behaviour for building supervised TUI applications.

Provides a LiveView-inspired callback interface for terminal apps that can be placed in OTP supervision trees.

Usage

defmodule MyTUI do
  use ExRatatui.App

  @impl true
  def mount(_opts) do
    {:ok, %{count: 0}}
  end

  @impl true
  def render(state, frame) do
    alias ExRatatui.Widgets.Paragraph
    alias ExRatatui.Layout.Rect

    widget = %Paragraph{text: "Count: #{state.count}"}
    rect = %Rect{x: 0, y: 0, width: frame.width, height: frame.height}
    [{widget, rect}]
  end

  @impl true
  def handle_event(%ExRatatui.Event.Key{code: "q"}, state) do
    {:stop, state}
  end

  def handle_event(_event, state) do
    {:noreply, state}
  end
end

Then add to your supervision tree:

children = [{MyTUI, []}]
Supervisor.start_link(children, strategy: :one_for_one)

Options

Options are passed through start_link/1 and forwarded to mount/1:

  • :name - process registration name (defaults to the module name, pass nil to skip registration)
  • :poll_interval - event polling interval in milliseconds (default: 16, which gives ~60fps). The poll runs on the BEAM's DirtyIo scheduler so it never blocks normal processes. Lower values increase responsiveness but use more CPU; higher values reduce CPU but add input latency.
  • :test_mode - {width, height} tuple to use a headless test terminal instead of the real terminal. Enables async: true tests without a TTY.

Callbacks

  • mount/1 — Called once on startup with options. Return {:ok, initial_state} or {:error, reason} to abort startup.
  • render/2 — Called after every state change. Receives state and a %ExRatatui.Frame{} with terminal dimensions. Return a list of {widget, rect} tuples.
  • handle_event/2 — Called when a terminal event arrives. Return {:noreply, new_state} or {:stop, state}.
  • handle_info/2 — Called for non-terminal messages (e.g., PubSub). Optional; default implementation returns {:noreply, state}.
  • terminate/2 — Called when the TUI is shutting down. Receives the exit reason and final state. Optional; default is a no-op. Use this to stop the VM with System.stop(0) in standalone apps.

Summary

Types

state()

@type state() :: term()

Callbacks

handle_event(arg1, state)

@callback handle_event(
  ExRatatui.Event.Key.t()
  | ExRatatui.Event.Mouse.t()
  | ExRatatui.Event.Resize.t(),
  state()
) :: {:noreply, state()} | {:stop, state()}

handle_info(msg, state)

(optional)
@callback handle_info(msg :: term(), state()) :: {:noreply, state()} | {:stop, state()}

mount(opts)

@callback mount(opts :: keyword()) :: {:ok, state()} | {:error, reason :: term()}

render(state, t)

@callback render(state(), ExRatatui.Frame.t()) :: [
  {ExRatatui.widget(), ExRatatui.Layout.Rect.t()}
]

terminate(reason, state)

(optional)
@callback terminate(reason :: term(), state()) :: term()