beamtea behaviour (beamtea v0.1.2)

View Source

beamtea — a delightful terminal UI framework for the BEAM, built on prim_tty and inspired by Bubble Tea and The Elm Architecture.

A program is three pure functions:

  • init(Flags) -> {Model, Cmd} — the starting model and an initial command
  • update(Msg, Model) -> {Model, Cmd} — fold a message into the model
  • view(Model) -> iodata() — render the model to the full screen

Supply them either as a module implementing the beamtea behaviour or as a map #{init => F, update => F, view => F}, then call start/1.

Messages your update/2 will receive from the runtime:

  • {key, Key} — a keypress (see beamtea_key)
  • {resize, {Cols, Rows}} — the terminal was resized

plus any message produced by the commands you return.

Summary

Functions

Run several commands.

Deliver Msg repeatedly, every Ms milliseconds, until the program quits. (0.1.0 timers cannot be individually cancelled.)

Deliver Msg back to update/2 as soon as possible.

A command that does nothing.

Normalize a program (module or map) into the internal record form.

Ask the runtime to quit after this update.

Run several commands (ordering is not otherwise significant in 0.1.0).

Start a program and block until it quits.

Run Fun asynchronously; its return value is delivered to update/2.

Deliver Msg once, after Ms milliseconds.

The current terminal size {Cols, Rows}. Callable from init/1 and view/1 (they run in the runtime process), so a view can size its content to the terminal without threading the size through the model. The size also arrives in update/2 as {resize, {Cols, Rows}} if you prefer to store it. Returns {80, 24} if called outside a running program.

Types

cmd/0

-type cmd() ::
          none | ok | quit |
          {msg, msg()} |
          {tick, non_neg_integer(), msg()} |
          {every, pos_integer(), msg()} |
          {task, fun(() -> msg())} |
          fun(() -> msg()) |
          {batch, [cmd()]} |
          {seq, [cmd()]} |
          [cmd()].

model/0

-type model() :: term().

msg/0

-type msg() :: {key, beamtea_key:key()} | {resize, {pos_integer(), pos_integer()}} | term().

program/0

-type program() ::
          module() |
          #{init := fun((term()) -> {model(), cmd()}),
            update := fun((msg(), model()) -> {model(), cmd()}),
            view := fun((model()) -> iodata())}.

Callbacks

init/1

-callback init(Flags :: term()) -> {model(), cmd()}.

update/2

-callback update(msg(), model()) -> {model(), cmd()}.

view/1

-callback view(model()) -> iodata().

Functions

batch(Cmds)

-spec batch([cmd()]) -> cmd().

Run several commands.

every(Ms, Msg)

-spec every(pos_integer(), msg()) -> cmd().

Deliver Msg repeatedly, every Ms milliseconds, until the program quits. (0.1.0 timers cannot be individually cancelled.)

msg(Msg)

-spec msg(msg()) -> cmd().

Deliver Msg back to update/2 as soon as possible.

none()

-spec none() -> cmd().

A command that does nothing.

normalize(Mod)

-spec normalize(program()) ->
                   #beamtea_prog{init :: fun((term()) -> {term(), term()}),
                                 update :: fun((term(), term()) -> {term(), term()}),
                                 view :: fun((term()) -> iodata())}.

Normalize a program (module or map) into the internal record form.

quit()

-spec quit() -> cmd().

Ask the runtime to quit after this update.

sequence(Cmds)

-spec sequence([cmd()]) -> cmd().

Run several commands (ordering is not otherwise significant in 0.1.0).

start(Program)

-spec start(program()) -> {ok, model()} | {error, term()}.

Equivalent to start(Program, undefined, #{}).

start(Program, Flags)

-spec start(program(), term()) -> {ok, model()} | {error, term()}.

Equivalent to start(Program, Flags, #{}).

start(Program, Flags, Opts)

-spec start(program(), term(), map()) -> {ok, model()} | {error, term()}.

Start a program and block until it quits.

Returns {ok, FinalModel} on a clean exit, or {error, Reason} if the program could not start (e.g. not_a_terminal when stdout is not a tty) or crashed.

Run beamtea programs so they own the terminal — use bin/beamtea-run or start the VM with stty -isig and erl +Bi -noshell -noinput. Launching from an interactive rebar3 shell will conflict with the shell's own tty handling and route Ctrl-C to the BEAM break handler. See the README.

Opts understands:

  • alt_screen => boolean() (default true) — use the alternate screen buffer
  • catch_ctrl_c => boolean() (default true) — quit on Ctrl-C instead of passing it to update/2
  • layout => top_left | center | {place, Opts} (default top_left) — position the whole view within the terminal, so a small view can fill/centre the screen. See beamtea_layout. center and {place, ...} re-flow automatically on resize.

task(Fun)

-spec task(fun(() -> msg())) -> cmd().

Run Fun asynchronously; its return value is delivered to update/2.

tick(Ms, Msg)

-spec tick(non_neg_integer(), msg()) -> cmd().

Deliver Msg once, after Ms milliseconds.

window_size()

-spec window_size() -> {pos_integer(), pos_integer()}.

The current terminal size {Cols, Rows}. Callable from init/1 and view/1 (they run in the runtime process), so a view can size its content to the terminal without threading the size through the model. The size also arrives in update/2 as {resize, {Cols, Rows}} if you prefer to store it. Returns {80, 24} if called outside a running program.