gleamtea
gleamtea — a delightful terminal UI framework for the BEAM, Gleam edition.
These are typed Gleam bindings to the Erlang beamtea
library: Bubble Tea / The Elm Architecture on prim_tty.
A program is three functions:
init() -> #(model, Cmd(msg))— the starting model and an initial commandupdate(msg, model) -> #(model, Cmd(msg))— fold a message into the modelview(model) -> String— render the model to the full screen
Wire them together with program and run with start.
The neat part: Gleam’s custom types compile to exactly the Erlang terms
beamtea already speaks. Quit becomes the atom quit, Tick(1000, msg)
becomes {tick, 1000, Msg}, and an incoming keypress {key, up} matches
the Key(Up) pattern — so the binding is (almost) all types and no glue.
Types
A command: a side-effect for the runtime to run after an update.
Each variant compiles directly to the Erlang term beamtea’s runtime interprets, so there is no marshalling cost.
pub type Cmd(msg) {
None
Quit
Msg(msg)
Tick(Int, msg)
Every(Int, msg)
Task(fn() -> msg)
Batch(List(Cmd(msg)))
Seq(List(Cmd(msg)))
}
Constructors
-
NoneDo nothing.
-
QuitQuit the program after this update.
-
Msg(msg)Deliver
msgback toupdateas soon as possible. -
Tick(Int, msg)Deliver
msgonce, after the given number of milliseconds. -
Every(Int, msg)Deliver
msgrepeatedly, every N milliseconds, until the program quits. -
Task(fn() -> msg)Run a function asynchronously; its return value is delivered to
update. -
Batch(List(Cmd(msg)))Run several commands.
-
Seq(List(Cmd(msg)))Run several commands in sequence.
A message delivered to your update. Wrap your own messages in
User; the runtime delivers keyboard and resize events as
Key and Resize.
pub type Event(user) {
Key(key.Key)
Resize(#(Int, Int))
User(user)
}
Constructors
-
Key(key.Key)A keypress. See
gleamtea/key. -
Resize(#(Int, Int))The terminal was resized to
#(columns, rows). -
User(user)
Where the runtime positions your whole view within the terminal. Center
and TopCenter re-flow automatically on resize, so a small view can fill
or centre the screen without any manual sizing. For finer control, compose
with gleamtea/layout inside your view instead.
pub type Layout {
TopLeft
Center
TopCenter
Fill
}
Constructors
-
TopLeftDraw the view at the top-left corner (the default).
-
CenterCentre the view horizontally and vertically.
-
TopCenterCentre the view horizontally, pinned to the top.
-
FillWrap the view in a bordered panel that fills the whole terminal.
Runtime options.
pub type Options {
Options(alt_screen: Bool, catch_ctrl_c: Bool, layout: Layout)
}
Constructors
-
Options(alt_screen: Bool, catch_ctrl_c: Bool, layout: Layout)Arguments
- alt_screen
-
Use the alternate screen buffer (like
vim/less). DefaultTrue. - catch_ctrl_c
-
Quit on Ctrl-C instead of passing it to
update. DefaultTrue. - layout
-
How to position the whole view within the terminal. Default
TopLeft.
Values
pub fn default_options() -> Options
The default options: alternate screen on, Ctrl-C quits, top-left layout.
pub fn program(
init init: fn() -> #(model, Cmd(msg)),
update update: fn(msg, model) -> #(model, Cmd(msg)),
view view: fn(model) -> String,
) -> Program(model, msg)
Assemble a program from its three functions.
pub fn start(
program: Program(model, msg),
) -> Result(model, dynamic.Dynamic)
Start a program and block until it quits.
Returns Ok(final_model) on a clean exit, or Error(reason) if the
program could not start (e.g. stdout is not a terminal) or crashed.
Run gleamtea programs so they own the terminal — use bin/gleamtea-run,
which launches the VM with -noshell -noinput and puts the tty in raw
mode. Starting from an interactive gleam run/shell will fight the shell
for control of the terminal.
pub fn start_with(
program: Program(model, msg),
options: Options,
) -> Result(model, dynamic.Dynamic)
Start a program with explicit Options.
pub fn window_size() -> #(Int, Int)
The current terminal size as #(columns, rows).
Callable from init and view (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 as Resize(#(cols, rows)) if you prefer to store it. Returns #(80, 24) outside a running
program.