TermUI.Elm behaviour (TermUI v0.2.0)

View Source

The Elm Architecture implementation for TermUI components.

This module provides the core callbacks for implementing components using The Elm Architecture pattern: update/2 for state changes and view/1 for rendering.

The Pattern

  1. Events arrive from terminal input
  2. event_to_msg/2 converts events to component-specific messages
  3. update/2 transforms state based on messages, returns new state + commands
  4. view/1 renders current state to a render tree
  5. Commands execute asynchronously, sending result messages back

Usage

defmodule Counter do
  use TermUI.Elm

  def init(_opts), do: %{count: 0}

  def event_to_msg(%Event.Key{key: :up}, _state), do: {:msg, :increment}
  def event_to_msg(%Event.Key{key: :down}, _state), do: {:msg, :decrement}
  def event_to_msg(_, _), do: :ignore

  def update(:increment, state), do: {%{state | count: state.count + 1}, []}
  def update(:decrement, state), do: {%{state | count: state.count - 1}, []}

  def view(state) do
    text("Count: #{state.count}")
  end
end

Summary

Callbacks

Converts an event to a component-specific message.

Initializes component state from options.

Updates component state based on a message.

Renders the current state to a render tree.

Functions

Normalizes update result to standard form.

Validates that an update function is pure (best effort).

Types

command()

@type command() :: term()

event_to_msg_result()

@type event_to_msg_result() :: {:msg, msg()} | :ignore | :propagate

msg()

@type msg() :: TermUI.Message.t()

render_tree()

@type render_tree() :: term()

state()

@type state() :: term()

update_result()

@type update_result() :: {state(), [command()]} | {state()} | :noreply

Callbacks

event_to_msg(t, state)

@callback event_to_msg(TermUI.Event.t(), state()) :: event_to_msg_result()

Converts an event to a component-specific message.

This callback transforms raw terminal events into domain-specific messages that have semantic meaning for the component.

Parameters

  • event - The terminal event (Key, Mouse, Resize, etc.)
  • state - Current component state

Returns

  • {:msg, message} - Event converted to a message for update
  • :ignore - Event not handled by this component
  • :propagate - Pass event to parent component

init(opts)

(optional)
@callback init(opts :: keyword()) :: state()

Initializes component state from options.

Called once when the component is created.

Parameters

  • opts - Options passed to the component

Returns

Initial state for the component.

update(msg, state)

@callback update(msg(), state()) :: update_result()

Updates component state based on a message.

This is the core logic of the component. It receives the current state and a message, and returns the new state plus any commands to execute.

Update functions must be pure—no side effects, no external calls. Side effects are performed through commands returned in the result.

Parameters

  • msg - The message to handle
  • state - Current component state

Returns

  • {new_state, commands} - New state and commands to execute
  • {new_state} - Shorthand for {new_state, []}
  • :noreply - Keep state unchanged, no commands

Examples

def update(:increment, state) do
  {%{state | count: state.count + 1}, []}
end

def update({:fetch_data, url}, state) do
  cmd = Command.http_get(url, {:data_loaded, :response})
  {%{state | loading: true}, [cmd]}
end

def update(:noop, _state), do: :noreply

view(state)

@callback view(state()) :: render_tree()

Renders the current state to a render tree.

View functions must be pure—given the same state, they always produce the same output. View functions should be fast since they run every frame.

Parameters

  • state - Current component state

Returns

A render tree structure that will be processed into terminal output.

Examples

def view(state) do
  box(border: true) do
    text("Count: #{state.count}")
  end
end

Functions

normalize_update_result(arg1, old_state)

@spec normalize_update_result(update_result(), state()) :: {state(), [command()]}

Normalizes update result to standard form.

Converts shorthand forms to the full {state, commands} tuple.

validate_update_purity(module)

@spec validate_update_purity(module()) :: :ok | {:warnings, [String.t()]}

Validates that an update function is pure (best effort).

Returns warnings if the update function appears to have side effects. This is a heuristic check, not a guarantee.