Drafter (drafter v0.2.10)

Copy Markdown View Source

An Elixir Terminal User Interface framework.

Drafter provides a complete TUI framework with:

  • Widget-based UI components
  • Event-driven architecture
  • Flexible layout system
  • Self-implemented drawing primitives
  • Minimal dependencies

defmodule MyApp do

  use Drafter.App

  def mount(_props) do
    %{counter: 0}
  end

  def render(state) do
    Drafter.container([
      Drafter.label("Counter: \\#{state.counter}"),
      Drafter.button("Click me!", on_click: :increment)
    ])
  end

  def handle_event(:increment, state) do
    {:ok, %{state | counter: state.counter + 1}}
  end
end

Drafter.run(MyApp)

Summary

Functions

Programmatically activate (press) a widget by its ID.

Create a button widget

Create a container widget

Returns the currently active skin atom.

Create a digits widget for displaying large numbers

Get the full state of a widget by its ID.

Get the current value of a widget by its ID.

Create a grid widget for layouts

Create a horizontal layout container

Create a label widget

Create a markdown widget

Create a placeholder widget

Query all widgets matching CSS-like selector.

Query a single widget by CSS-like selector.

Create a rule (divider line) widget

Start a TUI application.

Run an app module in an isolated or shared session using pre-started session services.

Send an app event to the active app loop.

Set an interval timer

Switch the active rendering skin.

Switches the active theme by name.

Set a one-time timeout timer

Stop all animations for a widget.

Stop an animation by its reference.

Validate a widget's value. Sends :validate event to widget, triggering its validators.

Create a vertical layout container

Functions

activate_widget(widget_id)

@spec activate_widget(atom()) :: term()

Programmatically activate (press) a widget by its ID.

animate(widget_id, property, end_value, opts \\ [])

@spec animate(atom(), atom(), any(), keyword()) :: reference()

Animate a widget property.

Properties

  • :opacity - Opacity (0.0 to 1.0)
  • :background - Background color (RGB tuple)
  • :color - Foreground color (RGB tuple)
  • :offset_x - X offset in cells
  • :offset_y - Y offset in cells

Options

  • :duration - Animation duration in ms (default: 300)
  • :easing - Easing function (default: :ease_out)
  • :on_complete - Callback when animation finishes

Easing Functions

:linear, :ease, :ease_in, :ease_out, :ease_in_out, :ease_in_quad, :ease_out_quad, :ease_in_out_quad, :ease_in_cubic, :ease_out_cubic, :ease_in_out_cubic, :ease_in_elastic, :ease_out_elastic, :ease_in_bounce, :ease_out_bounce, :ease_in_out_bounce, :ease_in_back, :ease_out_back

Examples

Drafter.animate(:my_button, :opacity, 0.5, duration: 500)
Drafter.animate(:my_label, :background, {255, 0, 0}, duration: 1000, easing: :ease_out)

button(text, opts \\ [])

@spec button(
  String.t(),
  keyword()
) :: {Drafter.Widget.Button, map()}

Create a button widget

container(children, opts \\ [])

@spec container(
  [{module(), map()}],
  keyword()
) :: {Drafter.Widget.Container, map()}

Create a container widget

current_skin()

@spec current_skin() :: atom()

Returns the currently active skin atom.

digits(text, opts \\ [])

@spec digits(
  String.t(),
  keyword()
) :: {Drafter.Widget.Digits, map()}

Create a digits widget for displaying large numbers

focus(widget_id)

@spec focus(term()) :: :ok

footer(text \\ "Press 'q' to quit", opts \\ [])

@spec footer(
  String.t(),
  keyword()
) :: {Drafter.Widget.Footer, map()}

Create a footer widget

get_widget_state(widget_id)

@spec get_widget_state(atom()) :: struct() | nil

Get the full state of a widget by its ID.

Returns the complete widget state struct, useful for accessing multiple fields or widget-specific data.

Returns nil if widget not found.

get_widget_value(widget_id)

@spec get_widget_value(atom()) :: term() | nil

Get the current value of a widget by its ID.

Returns the primary "value" of the widget:

  • TextInput/TextArea: the text string
  • Checkbox: boolean (checked?)
  • Switch: boolean (enabled?)
  • RadioSet: the selected option ID
  • SelectionList: list of selected option IDs
  • OptionList: the selected option ID
  • Collapsible: boolean (expanded?)
  • TabbedContent: the active tab index
  • DataTable: list of selected row indices
  • Tree: list of selected node IDs

Returns nil if widget not found.

grid(children, opts \\ [])

@spec grid(
  [{module(), map()}],
  keyword()
) :: {Drafter.Widget.Grid, map()}

Create a grid widget for layouts

horizontal(children, opts \\ [])

@spec horizontal(
  [{module(), map()}],
  keyword()
) :: {Drafter.Widget.Container, map()}

Create a horizontal layout container

label(text, opts \\ [])

@spec label(
  String.t(),
  keyword()
) :: {Drafter.Widget.Label, map()}

Create a label widget

markdown(content, opts \\ [])

@spec markdown(
  String.t(),
  keyword()
) :: {Drafter.Widget.Markdown, map()}

Create a markdown widget

placeholder(text, opts \\ [])

@spec placeholder(
  String.t(),
  keyword()
) :: {Drafter.Widget.Placeholder, map()}

Create a placeholder widget

query_all(selector)

@spec query_all(String.t()) :: [atom()]

Query all widgets matching CSS-like selector.

Returns list of widget_ids.

query_one(selector)

@spec query_one(String.t()) :: atom() | nil

Query a single widget by CSS-like selector.

Selector examples:

  • "Button" - first Button widget
  • "#submit" - widget with id :submit
  • ".primary" - widget with class :primary
  • "Button.primary" - Button with class :primary

Returns widget_id or nil if not found.

rule(opts \\ [])

@spec rule(keyword()) :: {Drafter.Widget.Rule, map()}

Create a rule (divider line) widget

run(app_module, opts \\ [])

@spec run(
  module(),
  keyword()
) :: :ok

Start a TUI application.

Options:

  • :scroll_optimization - true (default) uses a fast render path during scroll gestures (render_hierarchy from ETS) and defers a full render_app until 150 ms after the last scroll event. Set to false to disable and trigger a full render_app on every scroll tick — maximum freshness at the cost of higher CPU during scroll.
  • :syntax_highlighting - true to enable tree-sitter syntax highlighting.

run_session(app_module, session_ctx, opts \\ [])

@spec run_session(module(), map(), keyword()) :: :ok | {:error, term()}

Run an app module in an isolated or shared session using pre-started session services.

session_ctx must contain: event_manager, compositor, screen_manager, theme_manager, event_handler as pid values.

Options:

  • :mode - :isolated (default) or :shared
  • :shared_state - pid of SharedState server when mode is :shared
  • All other opts are passed as mount props to the app module

send_app_event(event, data)

@spec send_app_event(atom(), term()) :: term()

Send an app event to the active app loop.

Used by modal screens and sub-screens to communicate results back to the parent application via handle_event/3.

set_interval(value, unit_or_id)

@spec set_interval(pos_integer(), atom()) :: :ok

Set an interval timer

set_skin(skin)

@spec set_skin(atom()) :: :ok

Switch the active rendering skin.

The skin controls which characters Drafter.CharacterSet returns for every widget render. Built-in skins: :graphical (default), :wireframe, :ascii.

Can be called from inside handle_event/2 or on_timer/2 — takes effect on the next frame.

set_theme(theme_name)

@spec set_theme(String.t()) :: :ok

Switches the active theme by name.

set_timeout(timeout_ms, timer_id)

@spec set_timeout(pos_integer(), atom()) :: :ok

Set a one-time timeout timer

stop_all_animations(widget_id)

@spec stop_all_animations(atom()) :: :ok

Stop all animations for a widget.

stop_animation(animation_ref)

@spec stop_animation(reference()) :: :ok

Stop an animation by its reference.

validate_widget(widget_id)

@spec validate_widget(atom()) :: :ok | {:error, String.t()}

Validate a widget's value. Sends :validate event to widget, triggering its validators.

vertical(children, opts \\ [])

@spec vertical(
  [{module(), map()}],
  keyword()
) :: {Drafter.Widget.Container, map()}

Create a vertical layout container