TermUI.Elm behaviour (TermUI v1.0.0)
View SourceThe 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
- Events arrive from terminal input
- event_to_msg/2 converts events to component-specific messages
- update/2 transforms state based on messages, returns new state + commands
- view/1 renders current state to a render tree
- Commands execute asynchronously, sending result messages back
TermUI.Runtime runs one Elm root module and owns its state. Stateful widgets
are explicit state machines embedded in that root state; the runtime does not
automatically start a process or route focus for each widget.
Usage
defmodule Counter do
use TermUI.Elm
alias TermUI.Event
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 init result to standard form.
Normalizes update result to standard form.
Validates that an update function is pure (best effort).
Types
@type command() :: term()
@type event_to_msg_result() :: {:msg, msg()} | :ignore | :propagate
@type msg() :: TermUI.Message.t()
@type render_tree() :: term()
@type state() :: term()
Callbacks
@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- Leave the event unhandled. The 1.0 single-root runtime has no parent to receive it, so this currently has the same practical effect as:ignore.
@callback init(opts :: keyword()) :: init_result()
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.
@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.
Keep update functions pure when practical—state transitions remain easier to
test when side effects are represented by returned commands. The runtime does
not enforce purity, and application-owned processes may instead report
results through TermUI.Runtime.send_message/3 or the optional root
handle_info/2 extension.
Parameters
msg- The message to handlestate- 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({:load_file, path}, state) do
cmd = Command.file_read(path, :file_loaded)
{%{state | loading: true}, [cmd]}
end
def update(:noop, _state), do: :noreply
@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 because the runtime invokes them whenever the application is dirty and needs a render.
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
@spec normalize_init_result(init_result()) :: {state(), [command()]}
Normalizes init result to standard form.
Supports plain state as well as state with startup commands.
@spec normalize_update_result(update_result(), state()) :: {state(), [command()]}
Normalizes update result to standard form.
Converts shorthand forms to the full {state, commands} tuple.
Validates that an update function is pure (best effort).
This 1.0 compatibility hook does not perform static or runtime analysis and
currently always returns :ok. Purity is a recommended application design,
not a runtime-enforced property.