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.

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.