ExRatatui (ExRatatui v0.7.1)

Copy Markdown View Source

Elixir bindings for the Rust ratatui terminal UI library.

This module provides the core API for building terminal UIs: initializing the terminal, drawing widgets, and polling for events — all via Rust NIFs that run on the BEAM's DirtyIo scheduler.

Quick start

ExRatatui.run(fn terminal ->
  {w, h} = ExRatatui.terminal_size()
  paragraph = %ExRatatui.Widgets.Paragraph{text: "Hello!"}
  rect = %ExRatatui.Layout.Rect{x: 0, y: 0, width: w, height: h}

  ExRatatui.draw(terminal, [{paragraph, rect}])
  ExRatatui.poll_event(60_000)
end)

Core functions

  • run/1 — initialize the terminal, run a function, restore on exit
  • draw/2 — render a list of {widget, rect} tuples in a single frame
  • poll_event/1 — non-blocking event polling (keyboard, mouse, resize)
  • terminal_size/0 — current terminal dimensions

OTP apps

For supervised TUI applications, see ExRatatui.App — a behaviour with LiveView-inspired callbacks (mount/1, render/2, handle_event/2).

Running over SSH

To serve a TUI to remote clients without a local terminal, see ExRatatui.SSH.Daemon and the SSH transport guide.

Widgets

See ExRatatui.Widgets.Paragraph, ExRatatui.Widgets.Block, ExRatatui.Widgets.List, ExRatatui.Widgets.Table, ExRatatui.Widgets.Gauge, ExRatatui.Widgets.LineGauge, ExRatatui.Widgets.Tabs, ExRatatui.Widgets.Scrollbar, ExRatatui.Widgets.Checkbox, ExRatatui.Widgets.TextInput, ExRatatui.Widgets.Clear, ExRatatui.Widgets.Markdown, ExRatatui.Widgets.Textarea, ExRatatui.Widgets.Throbber, ExRatatui.Widgets.Popup, and ExRatatui.Widgets.WidgetList.

Testing

Use init_test_terminal/2 and get_buffer_content/1 for headless rendering verification in CI — no TTY required.

Summary

Functions

Draws a list of {widget, rect} tuples to the terminal in a single frame.

Returns the test terminal's buffer contents as a string.

Initializes a headless test terminal with the given dimensions.

Polls for terminal events with a timeout (default 250ms).

Runs a TUI application.

Returns the current terminal size as {width, height}.

Returns the current cursor position from the TextInput state.

Returns the current text value from the TextInput state.

Forwards a key event to the TextInput state.

Creates a new TextInput state.

Sets the text value on the TextInput state.

Returns the cursor position as {row, col} from the Textarea state.

Returns the current text from the Textarea as a string (lines joined with \n).

Forwards a key event with modifiers to the Textarea state.

Returns the number of lines in the Textarea state.

Creates a new Textarea state.

Sets the text value on the Textarea state.

Types

Functions

draw(terminal_ref, widgets)

@spec draw(terminal_ref(), [{widget(), ExRatatui.Layout.Rect.t()}]) ::
  :ok | {:error, term()}

Draws a list of {widget, rect} tuples to the terminal in a single frame.

Each tuple pairs a widget struct (e.g. %Paragraph{}, %Table{}) with a %Rect{} that defines where to render it. Returns :ok on success or {:error, reason} on failure.

Examples

iex> terminal = ExRatatui.init_test_terminal(40, 10)
iex> paragraph = %ExRatatui.Widgets.Paragraph{text: "Hello!"}
iex> rect = %ExRatatui.Layout.Rect{x: 0, y: 0, width: 40, height: 10}
iex> ExRatatui.draw(terminal, [{paragraph, rect}])
:ok

get_buffer_content(terminal_ref)

@spec get_buffer_content(terminal_ref()) :: String.t() | {:error, term()}

Returns the test terminal's buffer contents as a string.

Each line is trimmed of trailing whitespace and joined with newlines. Only works with a test terminal reference from init_test_terminal/2.

Examples

iex> terminal = ExRatatui.init_test_terminal(20, 3)
iex> paragraph = %ExRatatui.Widgets.Paragraph{text: "Hi there"}
iex> rect = %ExRatatui.Layout.Rect{x: 0, y: 0, width: 20, height: 3}
iex> ExRatatui.draw(terminal, [{paragraph, rect}])
iex> ExRatatui.get_buffer_content(terminal) =~ "Hi there"
true

init_test_terminal(width, height)

@spec init_test_terminal(non_neg_integer(), non_neg_integer()) ::
  terminal_ref() | {:error, term()}

Initializes a headless test terminal with the given dimensions.

Takes width (columns) and height (rows) for the virtual terminal. Uses ratatui's TestBackend — no real terminal needed. Useful for testing rendering output without a TTY. Returns a terminal reference.

Examples

iex> terminal = ExRatatui.init_test_terminal(40, 10)
iex> is_reference(terminal)
true

iex> terminal = ExRatatui.init_test_terminal(40, 10)
iex> alias ExRatatui.Widgets.Paragraph
iex> alias ExRatatui.Layout.Rect
iex> :ok = ExRatatui.draw(terminal, [{%Paragraph{text: "Hello!"}, %Rect{x: 0, y: 0, width: 40, height: 10}}])
iex> ExRatatui.get_buffer_content(terminal) =~ "Hello!"
true

poll_event(timeout_ms \\ 250)

@spec poll_event(non_neg_integer()) :: ExRatatui.Event.t() | nil | {:error, term()}

Polls for terminal events with a timeout (default 250ms).

Returns an Event.Key, Event.Mouse, Event.Resize struct, nil if no event within the timeout, or {:error, reason} on failure.

run(fun)

@spec run((terminal_ref() -> term())) :: term() | {:error, term()}

Runs a TUI application.

Initializes the terminal, calls fun with the terminal reference, and ensures terminal cleanup on exit.

ExRatatui.run(fn terminal ->
  # your TUI loop here
end)

terminal_size()

@spec terminal_size() :: {non_neg_integer(), non_neg_integer()} | {:error, term()}

Returns the current terminal size as {width, height}.

Returns {:error, reason} if the terminal size cannot be determined.

text_input_cursor(state_ref)

@spec text_input_cursor(reference()) :: non_neg_integer()

Returns the current cursor position from the TextInput state.

Examples

iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_cursor(state)
0
iex> ExRatatui.text_input_handle_key(state, "a")
:ok
iex> ExRatatui.text_input_cursor(state)
1

text_input_get_value(state_ref)

@spec text_input_get_value(reference()) :: String.t()

Returns the current text value from the TextInput state.

Examples

iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_get_value(state)
""

text_input_handle_key(state_ref, key_code)

@spec text_input_handle_key(reference(), String.t()) :: :ok

Forwards a key event to the TextInput state.

Pass the code field from an ExRatatui.Event.Key struct. Supports printable characters, "backspace", "delete", "left", "right", "home", and "end".

Examples

iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_handle_key(state, "h")
:ok
iex> ExRatatui.text_input_handle_key(state, "i")
:ok
iex> ExRatatui.text_input_get_value(state)
"hi"

text_input_new()

@spec text_input_new() :: reference()

Creates a new TextInput state.

Returns a reference to the Rust-side state (ResourceArc). Pass this reference as the :state field of %ExRatatui.Widgets.TextInput{}.

Examples

iex> state = ExRatatui.text_input_new()
iex> is_reference(state)
true

text_input_set_value(state_ref, value)

@spec text_input_set_value(reference(), String.t()) :: :ok

Sets the text value on the TextInput state.

The cursor is moved to the end of the new value.

Examples

iex> state = ExRatatui.text_input_new()
iex> ExRatatui.text_input_set_value(state, "hello")
:ok
iex> ExRatatui.text_input_get_value(state)
"hello"

textarea_cursor(state_ref)

@spec textarea_cursor(reference()) :: {non_neg_integer(), non_neg_integer()}

Returns the cursor position as {row, col} from the Textarea state.

Examples

iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_cursor(state)
{0, 0}

textarea_get_value(state_ref)

@spec textarea_get_value(reference()) :: String.t()

Returns the current text from the Textarea as a string (lines joined with \n).

Examples

iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_get_value(state)
""

textarea_handle_key(state_ref, key_code, modifiers \\ [])

@spec textarea_handle_key(reference(), String.t(), [String.t()]) :: :ok

Forwards a key event with modifiers to the Textarea state.

modifiers is a list of active modifier strings (e.g. ["ctrl"]), defaults to []. The textarea supports Emacs-style shortcuts (Ctrl+Z undo, Ctrl+Y redo, etc.).

Examples

iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_handle_key(state, "h", [])
:ok
iex> ExRatatui.textarea_handle_key(state, "i", [])
:ok
iex> ExRatatui.textarea_get_value(state)
"hi"

textarea_line_count(state_ref)

@spec textarea_line_count(reference()) :: non_neg_integer()

Returns the number of lines in the Textarea state.

Examples

iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_line_count(state)
1

textarea_new()

@spec textarea_new() :: reference()

Creates a new Textarea state.

Returns a reference to the Rust-side state (ResourceArc). Pass this reference as the :state field of %ExRatatui.Widgets.Textarea{}.

Examples

iex> state = ExRatatui.textarea_new()
iex> is_reference(state)
true

textarea_set_value(state_ref, value)

@spec textarea_set_value(reference(), String.t()) :: :ok

Sets the text value on the Textarea state.

Examples

iex> state = ExRatatui.textarea_new()
iex> ExRatatui.textarea_set_value(state, "hello\nworld")
:ok
iex> ExRatatui.textarea_get_value(state)
"hello\nworld"