View Source Kino (Kino v0.7.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.
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-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
.
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.
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
.
Configures Kino.
Inspects the given term as cell output.
Asynchronously consumes a stream with fun
.
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.
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 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.
@spec listen(Enumerable.t() | pos_integer(), (term() -> any())) :: :ok
Asynchronously consumes a stream with fun
.
examples
Examples
This function is primarily useful to consume Kino.Control
events:
button = Kino.Control.button("Greet")
button
|> Kino.Control.stream()
|> Kino.listen(fn event -> IO.inspect(event) end)
Or in the tagged version:
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")
button
|> Kino.Control.stream()
|> Kino.listen(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.
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.
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.