ExRatatui (ExRatatui v0.5.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).

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.

Returns :ok on success or {:error, reason} on failure.

ExRatatui.draw(terminal, [
  {%ExRatatui.Widgets.Paragraph{text: "Hello!"}, rect}
])

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.

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.

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.

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"