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

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

Kino provides several built-in kinos. The Kino.Shorts module provides a facade to access and use most of the Kinos in this project, although you can also use their modules directly as listed in the sidebar.

For 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.

Kino also provides facilities to aid debugging, such as Kino.Process and a custom dbg() implementation that integrates with Livebook.

Custom 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. You can customize how your own data structures are rendered by implementing the Kino.Render protocol.

You can also implement your own kinos by writing custom JavaScript, 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

Summary

Functions

Renders a kino that periodically calls the given function to render a new result.

Same as listen/2, except each event is processed concurrently.

Returns the directories that contain .beam files for modules defined in the notebook.

Configures Kino.

Inspects the given term as cell output.

Interrupts evaluation with the given message.

Starts a process that consumes a stream with fun without blocking execution.

Returns a special value that results in no visible output.

Recompiles dependencies.

Renders the given term as cell output.

Starts a process under the Kino supervisor.

Similar to start_child/2 but returns the new pid or raises an error.

Terminates a child started with start_child/1.

Returns a temporary directory that gets removed when the runtime terminates.

Types

@type nothing() :: :"do not show this result in output"

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

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} - to continue

  • :halt - to no longer schedule callback evaluation

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

async_listen(stream_or_interval_ms, fun)

View Source
@spec async_listen(Enumerable.t() | pos_integer(), (term() -> any())) :: pid()

Same as listen/2, except each event is processed concurrently.

@spec beam_paths() :: [String.t()]

Returns the directories that contain .beam files for modules defined in the notebook.

@spec configure(keyword()) :: :ok

Configures Kino.

The supported options are:

  • :inspect

They are discussed individually in the sections below.

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

interrupt!(variant, message)

View Source
@spec interrupt!(:normal | :error, String.t()) :: no_return()

Interrupts evaluation with the given message.

This function raises a specific error to let Livebook known that evaluation should be stopped. The error message and a Continue button are shown to the user, who can then attempt to resolve the source of the interrupt before resuming execution.

Do not use interrupt inside listeners

Since interrupt!/2 aborts the execution, it cannot be used inside Kino.listen/2 or other event listeners. In such cases, you can use Kino.Frame and render any messages directly within the frame, using Kino.Text or Kino.Markdown.

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}")
Link to this function

listen(stream_or_interval_ms, fun)

View Source
@spec listen(Enumerable.t() | pos_integer(), (term() -> any())) :: pid()

Starts a process that consumes a stream with fun without blocking execution.

It returns the PID of the started process. The process can be terminated with Kino.terminate_child/1.

Note that events are processed by fun sequentially. If you want to process them concurrently, use async_listen/2.

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)
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)
) :: pid()
when state: term()

A stateful version of listen/2.

The callback should return either of:

  • {:cont, state} - to continue

  • :halt - to stop listening

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

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()
@spec recompile() :: :ok

Recompiles dependencies.

Once you have installed dependencies with Mix.install/1, this will recompile any outdated path dependencies declared during the install.

Reproducibility

Keep in mind that recompiling dependency modules is not going to mark any cells as stale. This means that the given notebook state may no longer be reproducible. This function is meant as a utility when prototyping alongside a Mix project.

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

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 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.

On creation, many kinos use start_child/1 underneath, which means that you cannot use functions such as Kino.DataTable.new/1 in GenServer.init/1. If you need to do that, you must either create the kinos beforehand and pass in the GenServer argument, or create them in GenServer.handle_continue/2.

Link to this function

start_child!(child_spec)

View Source
@spec start_child!(Supervisor.child_spec() | {module(), term()} | module()) :: pid()

Similar to start_child/2 but returns the new pid or raises an error.

Link to this function

terminate_child(pid)

View Source (since 0.9.1)
@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.

@spec tmp_dir() :: String.t() | nil

Returns a temporary directory that gets removed when the runtime terminates.