View Source Kino (Kino v0.10.0)
Client-driven interactive widgets for Livebook.
Kino is the library used by Livebook to render rich and interactive outputs directly from your Elixir code.
getting-started
Getting started
Livebook is distributed with a set of interactive tutorials and examples, including some that specifically focus on Kino. If you're just getting started, going through these is highly recommended.
You can access these notebooks by starting Livebook and clicking on "Learn" in the sidebar.
built-in-kinos
Built-in kinos
Kino renders any data structure that implements the Kino.Render
protocol, falling back to the Kernel.inspect/2
representation
whenever an implementation is not available. The data structures
supported by Kino out of the box are:
kino-datatable
Kino.DataTable
Kino.DataTable
implements a data table output for user-provided
tabular data:
data = [
%{id: 1, name: "Elixir", website: "https://elixir-lang.org"},
%{id: 2, name: "Erlang", website: "https://www.erlang.org"}
]
Kino.DataTable.new(data)
kino-ets
Kino.ETS
Kino.ETS
implements a data table output for ETS tables in the
system:
tid = :ets.new(:users, [:set, :public])
Kino.ETS.new(tid)
kino-image
Kino.Image
Kino.Image
wraps binary image content and can be used to render
raw images of any given format:
content = File.read!("/path/to/image.jpeg")
Kino.Image.new(content, "image/jpeg")
kino-markdown
Kino.Markdown
Kino.Markdown
renders Markdown content, in case you need richer text:
Kino.Markdown.new("""
# Example
A regular Markdown file.
## Code
```elixir
"Elixir" |> String.graphemes() |> Enum.frequencies()
```
## Table
| ID | Name | Website |
| -- | ------ | ----------------------- |
| 1 | Elixir | https://elixir-lang.org |
| 2 | Erlang | https://www.erlang.org |
""")
kino-text
Kino.Text
Kino.Text
renders plain text content. It is similar to Kino.Markdown
,
however doesn't interpret any markup.
Kino.Text.new("Hello!")
kino-mermaid
Kino.Mermaid
Kino.Mermaid
renders Mermaid graphs:
Kino.Mermaid.new("""
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
""")
kino-frame
Kino.Frame
Kino.Frame
is a placeholder for static outputs that can
be dynamically updated.
frame = Kino.Frame.new() |> Kino.render()
for i <- 1..100 do
Kino.Frame.render(frame, i)
Process.sleep(50)
end
Also see Kino.animate/3
.
kino-tree
Kino.Tree
Kino.Tree
displays arbitrarily nested data structure as a
tree view.
data = Process.info(self())
Kino.Tree.new(data)
kino-html
Kino.HTML
Kino.HTML
displays arbitrary static HTML.
Kino.HTML.new("""
<h3>Look!</h3>
<p>I wrote this HTML from <strong>Kino</strong>!</p>
""")
user-interactions
User interactions
Kino.Input
and Kino.Control
provide a set of widgets for
entering data and capturing user events. See the respective
module documentation for examples.
all-others
All others
All other data structures are rendered as text using Elixir's
Kernel.inspect/2
.
custom-kinos
Custom kinos
Kino makes it possible to define custom JavaScript powered
kinos, see Kino.JS
and Kino.JS.Live
for more details.
Packaging
When publishing custom kinos and smart cells, please consider the following guidelines:
prefix package name with
kino_
, usually followed by the name of the integration, such askino_vega_lite
,kino_ecto
namespace all modules under
KinoExample
, notKino.Example
. Note that official packages maintained by the Livebook team expose public APIs underKino.
, because they are essentially direct extensions ofKino
and we make sure no conflicting modules exist. Unofficial packages should follow the usual Elixir conventions with respect to module names
Link to this section Summary
Functions
Renders a kino that periodically calls the given function to render a new result.
A stateful version of animate/2
.
Same as listen/2
, except each event is processed concurrently.
Configures Kino.
Inspects the given term as cell output.
Interrupts evaluation with the given message.
Consumes a stream with fun
without blocking execution.
A stateful version of listen/2
.
Returns a special value that results in no visible output.
Renders the given term as cell output.
Starts a process under the Kino supervisor.
Terminates a child started with start_child/1
.
Link to this section Types
@type nothing() :: :"do not show this result in output"
Link to this section Functions
@spec animate(Enumerable.t() | pos_integer(), (term() -> any())) :: nothing()
Renders a kino that periodically calls the given function to render a new result.
The callback receives a stream element and should return a term to be rendered.
This function uses Kino.Frame
as the underlying kino.
It returns nothing (a non-printable result).
examples
Examples
An animation is created by turning a stream of values into subsequent animation frames:
Stream.interval(100)
|> Stream.take(100)
|> Kino.animate(fn i ->
Kino.Markdown.new("**Iteration: `#{i}`**")
end)
Alternatively an integer may be passed as a shorthand for
Stream.interval/1
:
# Render new Markdown every 100ms
Kino.animate(100, fn i ->
Kino.Markdown.new("**Iteration: `#{i}`**")
end)
@spec animate( Enumerable.t() | pos_integer(), state, (term(), state -> {:cont, term(), state} | :halt) ) :: nothing() when state: term()
A stateful version of animate/2
.
The callback receives a stream element and the accumulated state and it should return either of:
{:cont, term_to_render, state}
- the continue:halt
- to no longer schedule callback evaluation
examples
Examples
This function is primarily useful to consume Kino.Control
events:
button = Kino.Control.button("Click")
button
|> Kino.Control.stream()
|> Kino.animate(0, fn _event, counter ->
new_counter = counter + 1
md = Kino.Markdown.new("**Clicks: `#{new_counter}`**")
{:cont, md, new_counter}
end)
@spec async_listen(Enumerable.t() | pos_integer(), (term() -> any())) :: :ok
Same as listen/2
, except each event is processed concurrently.
@spec configure(keyword()) :: :ok
Configures Kino.
The supported options are:
:inspect
They are discussed individually in the sections below.
inspect
Inspect
A keyword list containing inspect options used for printing usual evaluation results. Defaults to pretty formatting with a limit of 50 entries.
To show more entries, you configure a higher limit:
Kino.configure(inspect: [limit: 200])
You can also show all entries by setting the limit to :infinity
,
but keep in mind that for large data structures it is memory-expensive
and is not an advised configuration in this case. Instead prefer
the use of IO.inspect/2
with :infinity
limit when needed.
See Inspect.Opts
for the full list of options.
Inspects the given term as cell output.
This works essentially the same as IO.inspect/2
, except it
always produces colored text and respects the configuration
set with configure/1
.
Opposite to render/1
, it does not attempt to render the given
term as a kino.
Interrupts evaluation with the given message.
This function raises a specific error to let Livebook known that evaluation should be stopped. The error message is shown to the user and they can retry evaluation with a button click, supposedly after they resolve the interrupt reason.
examples
Examples
text =
Kino.Input.text("Input")
|> Kino.render()
|> Kino.Input.read()
if text == "" do
Kino.interrupt!(:error, "Input required")
end
# This will not be run if the `interrupt!` is called above
Kino.Markdown.new("**You entered:** #{text}")
@spec listen(Enumerable.t() | pos_integer(), (term() -> any())) :: :ok
Consumes a stream with fun
without blocking execution.
Note that events are processed by fun
sequentially. If you want
to process them concurrently, use async_listen/2
.
examples
Examples
This function is primarily useful to consume Kino.Control
events:
Kino.Control.button("Greet")
|> Kino.listen(fn event -> IO.inspect(event) end)
You can also merge multiple controls into a single stream. For example, in order to merge them and tag each with a distinct event:
button = Kino.Control.button("Hello")
input = Kino.Input.checkbox("Check")
stream = Kino.Control.tagged_stream([hello: button, check: input])
Kino.listen(stream, fn
{:hello, event} -> ...
{:check, event} -> ...
end)
Any other stream works as well:
Stream.interval(100)
|> Stream.take(10)
|> Kino.listen(fn i -> IO.puts("Ping #{i}") end)
Finally, an integer may be passed as a shorthand for Stream.interval/1
:
Kino.listen(100, fn i -> IO.puts("Ping #{i}") end)
@spec listen( Enumerable.t() | pos_integer(), state, (term(), state -> {:cont, state} | :halt) ) :: :ok when state: term()
A stateful version of listen/2
.
The callback should return either of:
{:cont, state}
- the continue:halt
- to stop listening
examples
Examples
button = Kino.Control.button("Click")
Kino.listen(button, 0, fn _event, counter ->
new_counter = counter + 1
IO.puts("Clicks: #{new_counter}")
{:cont, new_counter}
end)
@spec nothing() :: nothing()
Returns a special value that results in no visible output.
examples
Examples
This is especially handy when you wish to suppress the default output of a cell. For instance, a cell containing this would normally result in verbose response output:
resp = Req.get!("https://example.org")
That output can be suppressed by appending a call to nothing/0
:
resp = Req.get!("https://example.org")
Kino.nothing()
Renders the given term as cell output.
This effectively allows any Livebook cell to have multiple evaluation results.
@spec start_child(Supervisor.child_spec() | {module(), term()} | module()) :: DynamicSupervisor.on_start_child()
Starts a process under the Kino supervisor.
The process is automatically terminated when the current process terminates or the current cell reevaluates.
If you want to terminate the started process, use
terminate_child/1
. If you terminate the process manually,
the Kino supervisor might restart it if the child's :restart
strategy says so.
Nested start
It is not possible to use
start_child/1
while initializing another process started this way. In other words, you generally cannot callstart_child/1
inside callbacks such asGenServer.init/1
orKino.JS.Live.init/2
. If you do that, starting the process will block forever.Note that creating many kinos uses
start_child/1
underneath, hence the same restriction applies to starting those. SeeKino.JS.Live.init/2
for more details.
@spec terminate_child(pid()) :: :ok | {:error, :not_found}
Terminates a child started with start_child/1
.
Returns :ok
if the child was found and terminated, or
{:error, :not_found}
if the child was not found.