View Source Kino (Kino v0.8.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.

kino-tree

Kino.Tree

Kino.Tree displays arbitrarily nested data structure as a tree view.

data = Process.info(self())
Kino.Tree.new(data)

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 as kino_vega_lite, kino_ecto

  • namespace all modules under KinoExample, not Kino.Example. Note that official packages maintained by the Livebook team expose public APIs under Kino., because they are essentially direct extensions of Kino 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.

Configures Kino.

Inspects the given term as cell output.

Asynchronously consumes a stream with fun.

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

Link to this function

animate(stream_or_interval_ms, fun)

View Source
@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)
Link to this function

animate(stream_or_interval_ms, state, fun)

View Source
@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.

Link to this function

inspect(term, opts \\ [])

View Source
@spec inspect(
  term(),
  keyword()
) :: term()

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.

Link to this function

listen(stream_or_interval_ms, fun)

View Source
@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)
Link to this function

listen(stream_or_interval_ms, state, fun)

View Source
@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.

@spec render(term()) :: term()

Renders the given term as cell output.

This effectively allows any Livebook cell to have multiple evaluation results.

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 call start_child/1 inside callbacks such as GenServer.init/1 or Kino.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. See Kino.JS.Live.init/2 for more details.