Run an ExRatatui.App inside a Livebook notebook via xterm.js.
Kino.ExRatatui is a byte-stream transport that pipes the runtime
server's rendered ANSI through an xterm.js iframe and forwards
keypresses + resize events back.
Example
defmodule Counter do
use ExRatatui.App
alias ExRatatui.Event.Key
alias ExRatatui.Layout.Rect
alias ExRatatui.Widgets.Paragraph
def mount(_), do: {:ok, %{n: 0}}
def render(state, frame) do
[{%Paragraph{text: "Count: #{state.n}"},
%Rect{x: 0, y: 0, width: frame.width, height: frame.height}}]
end
def handle_event(%Key{code: "+"}, s), do: {:noreply, %{s | n: s.n + 1}}
def handle_event(%Key{code: "q"}, s), do: {:stop, s}
def handle_event(_, s), do: {:noreply, s}
end
Kino.ExRatatui.new(Counter)Options
The second argument is a keyword list passed straight to
ExRatatui.App.mount/1. Use it for any per-instance configuration
your App reads from its mount opts. The keys :mod, :name, and
:transport are reserved by the runtime and silently overwritten.
Lifecycle
Each new/2 call spawns a fresh Kino.JS.Live server. The runtime
server and ExRatatui.Session are created lazily on the first
"resize" event from the iframe so we always start at the correct
cell dimensions reported by xterm.js's FitAddon. Re-evaluating the
cell (or closing the notebook) tears the runtime server down and
starts a new one — no state is preserved across re-evals.
How it plugs into ExRatatui
Implements ExRatatui.Transport as a byte-stream transport, using
ExRatatui.Transport.start_server/1 to boot the runtime and
ExRatatui.Transport.ByteStream to pump input + resize events. See
the Custom Transports
guide for the
reference shape.
Summary
Functions
Renders a one-shot static frame of widgets and returns a
non-interactive Kino.JS widget that paints it once.
Builds a new live kino that hosts mod (an ExRatatui.App).
Functions
@spec frame( [{ExRatatui.widget(), ExRatatui.Layout.Rect.t()}], keyword() ) :: Kino.JS.t()
Renders a one-shot static frame of widgets and returns a
non-interactive Kino.JS widget that paints it once.
Useful for documentation, screenshots in notebooks, or
Kino.Layout.grid([frame_a, frame_b, frame_c]) side-by-side
comparisons. There is no event loop, no resize handling, and no
runtime server — just an ExRatatui.Session rendered once and
written to xterm.js.
Options
:cols— terminal width in cells. Defaults to80.:rows— terminal height in cells. Defaults to24.
Examples
iex> alias ExRatatui.Layout.Rect
iex> alias ExRatatui.Widgets.{Block, Paragraph}
iex> kino = Kino.ExRatatui.frame(
...> [
...> {%Paragraph{
...> text: "Hello from a static frame!",
...> block: %Block{title: "demo"}
...> },
...> %Rect{x: 0, y: 0, width: 40, height: 5}}
...> ],
...> cols: 40,
...> rows: 5
...> )
iex> kino.module
Kino.ExRatatui
@spec new( module(), keyword() ) :: Kino.JS.Live.t()
Builds a new live kino that hosts mod (an ExRatatui.App).
mount_opts is forwarded verbatim to ExRatatui.App.mount/1, so an
App can do per-instance setup the same way it would over SSH.
Examples
Kino.ExRatatui.new(Counter)
Kino.ExRatatui.new(Counter, start: 10, theme: :dark)