Skip to content

Commit

Permalink
Revamp Explorer section for Kino (#879)
Browse files Browse the repository at this point in the history
Pong and custom Kinos chapters are still pending.
  • Loading branch information
josevalim authored Jan 17, 2022
1 parent fe0bf66 commit 4d79706
Show file tree
Hide file tree
Showing 11 changed files with 385 additions and 253 deletions.
29 changes: 18 additions & 11 deletions lib/livebook/notebook/explore.ex
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,25 @@ defmodule Livebook.Notebook.Explore do
# cover_url: "/images/axon.png"
# }
# },
%{
path: Path.join(__DIR__, "explore/vm_introspection.livemd"),
details: %{
description: "Extract and visualize information about a remote running node.",
cover_url: "/images/vm_introspection.png"
}
},
%{
ref: :kino_intro,
path: Path.join(__DIR__, "explore/kino/intro_to_kino.livemd")
},
%{
ref: :kino_vm_introspection,
path: Path.join(__DIR__, "explore/kino/vm_introspection.livemd")
},
%{
ref: :kino_chat_app,
path: Path.join(__DIR__, "explore/kino/chat_app.livemd")
},
%{
ref: :kino_pong,
path: Path.join(__DIR__, "explore/kino/pong.livemd")
},
%{
ref: :kino_custom_widgets,
path: Path.join(__DIR__, "explore/kino/creating_custom_widgets.livemd")
ref: :kino_custom_kinos,
path: Path.join(__DIR__, "explore/kino/custom_kinos.livemd")
}
]

Expand Down Expand Up @@ -202,9 +203,15 @@ defmodule Livebook.Notebook.Explore do
%{
title: "Interactions with Kino",
description:
"Kino is an Elixir package that allows for displaying and controlling rich, interactieve widgets in Livebook. Learn how to make your notebooks more engaging with inputs, plots, tables, and much more!",
"Kino is an Elixir package for displaying and controlling rich, interactive widgets in Livebook. Learn how to make your notebooks more engaging with inputs, plots, tables, and much more!",
cover_url: "/images/kino.png",
notebook_refs: [:kino_intro, :kino_pong, :kino_custom_widgets]
notebook_refs: [
:kino_intro,
:kino_vm_introspection,
:kino_chat_app,
:kino_pong,
:kino_custom_kinos
]
}
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,35 +194,11 @@ Date.from_iso8601("2020-02-30")

Now, what happens if we want our code to behave differently depending
if the date is valid or not? We can use `case` to pattern match on
the different tuples. This is also a good opportunity to use Livebook's
inputs to pass different values to our code. To render inputs, we need to
install the [Kino](https://github.com/livebook-dev/kino) library:
the different tuples:

```elixir
Mix.install(
[
{:kino, github: "livebook-dev/kino"}
],
consolidate_protocols: false
)
```

> Note: the `consolidate_protocols: false` option is not usually
> given, but it will be handy when we discuss protocols later
> in this notebook.
Kino allows our code notebooks to control Livebook itself. Let's render
an input by evaluating the cell below:

```elixir
date_input = Kino.Input.text("Date")
```

Now we can read its value and parse it:

```elixir
# Read the date input, which returns something like "2020-02-30"
input = Kino.Input.read(date_input)
# Give a random date as input
input = "2020-02-30"

# And then match on the return value
case Date.from_iso8601(input) do
Expand All @@ -234,12 +210,11 @@ case Date.from_iso8601(input) do
end
```

Now try adding a date to the input above, such as `2020-02-30` and
reevaluate the cell accordingly. In this example, we are using `case`
to pattern match on the different outcomes of the `Date.from_iso8601`
function. We say the `case` above has two clauses, one matching on
`{:ok, date}` and another on `{:error, reason}`. Try changing the input
and re-executing the cell to see how the outcome changes.
In this example, we are using `case` to pattern match on the different
outcomes of the `Date.from_iso8601`. We say the `case` above has two
clauses, one matching on `{:ok, date}` and another on `{:error, reason}`.
Now try changing the `input` variable above and reevaluate the cell
accordingly. What happens when you give it an invalid date?

Finally, we can also pattern match on maps. This is used to extract the
values for the given keys:
Expand Down
12 changes: 7 additions & 5 deletions lib/livebook/notebook/explore/elixir_and_livebook.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ and more.
If you are not familiar with Elixir, there is a fast paced
introduction to the language in the [Distributed portals with
Elixir](/explore/notebooks/distributed-portals-with-elixir)
notebook.
notebook. For a more structured introduction to the language,
see [Elixir's Getting Started guide](https://elixir-lang.org/getting-started/introduction.html)
and [the many learning resources available](https://elixir-lang.org/learning.html).

Let's move on.
Let's move forward.

## Autocompletion

Expand Down Expand Up @@ -70,10 +72,10 @@ data = [
Kino.DataTable.new(data)
```

See the [Interactions with Kino](/explore/notebooks/intro-to-kino) notebook
to learn all the ways you can interact with Livebook from Kino.
There is much more to `Kino` and we have [a series of Kino guides
in the Explore section to teach you more](/explore).

It is a good idea to specify versions of the installed packages,
Note that it is a good idea to specify versions of the installed packages,
so that the notebook is easily reproducible later on. The install
command goes beyond simply installing dependencies, it also caches
them, consolidates protocols, and more. Check
Expand Down
42 changes: 30 additions & 12 deletions lib/livebook/notebook/explore/intro_to_livebook.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -86,37 +86,55 @@ Process.sleep(300_000)
Having this cell running, feel free to insert another Elixir cell
in the section below and see it evaluates immediately.

## Notebook files
## Saving notebooks

By default notebooks are kept in memory, which is fine for interactive hacking,
but oftentimes you will want to save your work for later. Fortunately, notebooks
can be persisted by clicking on the "Disk" icon (<i class="ri-livebook-save"></i>)
in the bottom-right corner and selecting the file location.

Notebooks are stored in **live markdown** format, which is essentially the markdown you know,
Notebooks are stored in **live markdown** format, which is the Markdown you know,
with just a few assumptions on how particular elements are represented. Thanks to this
approach you can easily keep notebooks under version control and get readable diffs.
You can also easily preview those files, reuse for blog posts, and even edit in a text editor.

## Math
## Stepping up your workflow

Once you start using notebooks more, it's gonna be beneficial
to optimise how you move around. Livebook leverages the concept of
**navigation**/**insert** modes and offers many shortcuts for common operations.
Make sure to check out the shortcuts by clicking the "Keyboard" icon
(<i class="ri-livebook-shortcuts"></i>) in the sidebar or
by pressing <kbd>?</kbd>.

## Markdown extensions

Livebook also include supports for Math expressions and Mermaid diagrams.

Livebook uses $\TeX$ syntax for math.
It supports both inline math like $e^{\pi i} + 1 = 0$, as well as display math:
### Math expressions

Livebook uses $\TeX$ syntax for math inside your Markdown cells.
It supports both inline math, like $e^{\pi i} + 1 = 0$, as well as display math:

$$
S(x) = \frac{1}{1 + e^{-x}} = \frac{e^{x}}{e^{x} + 1}
$$

You can explore all supported expressions [here](https://katex.org/docs/supported.html).

## Stepping up your workflow
### Mermaid diagrams

Once you start using notebooks more, it's gonna be beneficial
to optimise how you move around. Livebook leverages the concept of
**navigation**/**insert** modes and offers many shortcuts for common operations.
Make sure to check out the shortcuts by clicking the "Keyboard" icon
(<i class="ri-livebook-shortcuts"></i>) in the sidebar or
by pressing <kbd>?</kbd>.
[Mermaid](https://mermaid-js.github.io/) is a library for creating diagrams
and visualizations using text and code. You can define those diagrams in
your Markdown cells via ```` ```mermaid ```` blocks. Let's see an example:

```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```

## Next steps

Expand Down
14 changes: 9 additions & 5 deletions lib/livebook/notebook/explore/intro_to_vega_lite.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

## Setup

To render graphs in Livebook, we need the
[`vega_lite`](https://github.com/elixir-nx/vega_lite) package
for defining our graph specification and
[`kino`](https://github.com/elixir-nx/kino). We won't use Kino
directly, but it is required to render VegaLite:
We need two libraries for plotting in Livebook:

* The [`vega_lite`](https://github.com/elixir-nx/vega_lite)
package allows us to define our graph specifications

* The [`kino`](https://github.com/elixir-nx/kino) package
renders our specifications

Let's install them:

```elixir
Mix.install([
Expand Down
153 changes: 153 additions & 0 deletions lib/livebook/notebook/explore/kino/chat_app.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Building a chat app with Kino.Control

## Setup

In this guide, we will build a chat application using
[`kino`](https://github.com/livebook-dev/kino). Let's
install it and get started:

```elixir
Mix.install([
{:kino, github: "livebook-dev/kino"}
])
```

## Kino.Control

In our [introduction to Kino](/explore/notebooks/intro_to_kino),
we learned about inputs and several different outputs, such as
tables, frames, and more. In particular, we learned how to use
inputs to capture values directly into our notebooks:

```elixir
name = Kino.Input.text("Your name")
```

and use them to print something back:

```elixir
IO.puts("Hello, #{Kino.Input.read(name)}!")
```

Inputs have one special power: they are shared across all users
accessing the notebook. For example, if you copy and paste the
URL of this notebook into another tab, the input will share the
same value. This plays into Livebook's strengths of being an
interactive and collaborative tool.

Sometimes, however, you don't want the input to be shared.
You want each different user to get their own inputs and perform
individual actions. That's exactly how
[`Kino.Control`](https://hexdocs.pm/kino/Kino.Control.html) works.
Each control is specific to each user on the page. You then receive
each user interaction as a message.

## The button control

The simplest control is `Kino.Control.button/1`. Let's give it a try:

```elixir
click_me = Kino.Control.button("Click me!")
```

Execute the cell above and the button will be rendered. You can click
it, but nothing will happen. Luckily, we can subscribe to the button
events:

```elixir
Kino.Control.subscribe(click_me, :click_me)
```

Now that we have subscribed, every time the button is clicked, we will
receive a message tagged with `:click_me`. Let's print all messages
in our inbox:

```elixir
Process.info(self(), :messages)
```

Now execute the cell above, click the button a couple times, and
re-execute the cell above. For each click, there is a new message in
our inbox. There are several ways we can consume this message.
Let's see a different one in the next example.

<!-- livebook:{"branch_parent_index":0} -->

## The form control

Whenever we want to submit multiple inputs at once, we can use
`Kino.Control.form/2`.

```elixir
inputs = [
first_name: Kino.Input.text("First name"),
last_name: Kino.Input.text("Last name")
]

form = Kino.Control.form(inputs, submit: "Greet")
```

Execute the cell above and you will see a form rendered.
You can now fill in the form and press the submit button.
Each submission will trigger a new event. Let's consume
them as a stream. Elixir streams are lazy collections that
are consumed as they happen:

```elixir
for event <- Kino.Control.stream(form) do
IO.inspect(event)
end
```

Now, as you submit the form, you should see a new event
printed. However, there is a downside: we are now stuck
inside this infinite loop of events. Luckily, we started
this particular section as a branched section, which means
the main execution flow will not be interrupted. But it
is something you should keep in mind in the future. You
can also stop it by pressing the "Stop" button above the
Elixir cell.

<!-- livebook:{"branch_parent_index":0} -->

## The chat application

We are now equipped with all knowledge necessary to build
our chat application. First, we will need a frame. Every
time a new message is received, we will append it to the
frame:

```elixir
frame = Kino.Frame.new()
```

Now we need a form with the user name and their message:

```elixir
inputs = [
name: Kino.Input.text("Name"),
message: Kino.Input.text("Message")
]

form = Kino.Control.form(inputs, submit: "Send", reset_on_submit: [:message])
```

Notice we used a new option, called `:reset_on_submit`,
that automatically clears the input once submitted.
Finally, let's stream the form events and post each
message to the frame:

```elixir
for %{data: %{name: name, message: message}} <- Kino.Control.stream(form) do
content = Kino.Markdown.new("**#{name}**: #{message}")
Kino.Frame.append(frame, content)
end
```

Execute the cell above and your chat app should be
fully operational. Open up this same notebook across
on different tabs and each different user can post
their messages.

In the next guide we will go one step further and
[develop a multiplayer pong game](/explore/notebooks/pong)!
Loading

0 comments on commit 4d79706

Please sign in to comment.