# `Kino.JS.Live`
[🔗](https://github.com/livebook-dev/kino/blob/v0.19.0/lib/kino/js/live.ex#L1)

Introduces state and event-driven capabilities to JavaScript
powered kinos.

Make sure to read the introduction to JavaScript kinos in
`Kino.JS` for more context.

Similarly to static kinos, live kinos involve a custom JavaScript
code running in the browser. In fact, this part of the API is the
same. In addition, each live kino has a server process running on
the Elixir side, responsible for maintaining state and able to
communicate with the JavaScript side at any time. Again, to illustrate
the ideas we start with a minimal example.

## Example

We will follow up on our `KinoDocs.HTML` example by adding support
for replacing the content on demand.

    defmodule KinoDocs.LiveHTML do
      use Kino.JS
      use Kino.JS.Live

      def new(html) do
        Kino.JS.Live.new(__MODULE__, html)
      end

      def replace(kino, html) do
        Kino.JS.Live.cast(kino, {:replace, html})
      end

      @impl true
      def init(html, ctx) do
        {:ok, assign(ctx, html: html)}
      end

      @impl true
      def handle_connect(ctx) do
        {:ok, ctx.assigns.html, ctx}
      end

      @impl true
      def handle_cast({:replace, html}, ctx) do
        broadcast_event(ctx, "replace", html)
        {:noreply, assign(ctx, html: html)}
      end

      asset "main.js" do
        """
        export function init(ctx, html) {
          ctx.root.innerHTML = html;

          ctx.handleEvent("replace", (html) => {
            ctx.root.innerHTML = html;
          });
        }
        """
      end
    end

Just as before we define a module, this time calling it
`KinoDocs.LiveHTML` for clarity. Note many similarities to
the previous version, we still call `use Kino.JS`, define
the `main.js` file and define the `new(html)` function for
creating a kino instance. As a matter of fact, the initial
result of `KinoDocs.LiveHTML.new(html)` will render exactly
the same as our previous `KinoDocs.HTML.new(html)`.

As for the new bits, we added `use Kino.JS.Live` to define
a live kino server. We use `Kino.JS.Live.new/2` for creating
the kino instance and we implement a few `GenServer`-like
callbacks.

Once the kino server is started with `Kino.JS.Live.new/2`,
the `c:init/2` callback is called with the initial argument.
In this case we store the given `html` in server state.

Whenever the kino is rendered on a new client, the `c:handle_connect/1`
callback is called and it builds the initial data for the
client. In this case, we always return the stored `html`.
This initial data is then passed to the JavaScript `init`
function. Keep in mind that while the server is initialized
once, connect may happen at any point, as the users join/refresh
the page.

Finally, the whole point of our example is the ability to
replace the HTML content directly from the Elixir side and
for this purpose we added the public `replace(kino, html)`
function. Underneath the function uses `cast/2` to message
our server and the message is handled with `c:handle_cast/2`.
In this case we store the new `html` in the server state and
broadcast an event with the new value. On the client side,
we subscribe to those events with `ctx.handleEvent(event, callback)`
to update the page accordingly.

## Event handlers

You must eventually register JavaScript handlers for all events
that the client may receive. However, the registration can be
deferred, if the initialization is asynchronous. For example,
the following is perfectly fine:

```js
export function init(ctx, data) {
  fetch(data.someUrl).then((resp) => {
    ctx.handleEvent("update", (payload) => {
      // ...
    });
  });
}
```

Or alternatively:

```js
export async function init(ctx, data) {
  const response = await fetch(data.someUrl);

  ctx.handleEvent("update", (payload) => {
    // ...
  });
}
```

In such case all incoming events are buffered and dispatched once
the handler is registered.

## Binary payloads

The client-server communication supports binary data, both on
initialization and on custom events. On the server side, a binary
payload has the form of `{:binary, info, binary}`, where `info`
is regular JSON-serializable data that can be sent alongside
the plain binary.

On the client side, a binary payload is represented as `[info, buffer]`,
where `info` is the additional data and `buffer` is the binary
as `ArrayBuffer`.

The following example showcases how to send and receive events
with binary payloads.

    defmodule KinoDocs.Binary do
      use Kino.JS
      use Kino.JS.Live

      def new() do
        Kino.JS.Live.new(__MODULE__, nil)
      end

      @impl true
      def handle_connect(ctx) do
        payload = {:binary, %{message: "hello"}, <<1, 2>>}
        {:ok, payload, ctx}
      end

      @impl true
      def handle_event("ping", {:binary, _info, binary}, ctx) do
        reply_payload = {:binary, %{message: "pong"}, <<1, 2, binary::binary>>}
        broadcast_event(ctx, "pong", reply_payload)
        {:noreply, ctx}
      end

      asset "main.js" do
        """
        export function init(ctx, payload) {
          console.log("initial data", payload);

          ctx.handleEvent("pong", ([info, buffer]) => {
            console.log("event data", [info, buffer])
          });

          const buffer = new ArrayBuffer(2);
          const bytes = new Uint8Array(buffer);
          bytes[0] = 4;
          bytes[1] = 250;
          ctx.pushEvent("ping", [{ message: "ping" }, buffer]);
        }
        """
      end
    end

# `from`

```elixir
@type from() :: GenServer.from()
```

# `payload`

```elixir
@type payload() :: term() | {:binary, info :: term(), binary()}
```

# `t`

```elixir
@opaque t()
```

# `handle_call`
*optional* 

```elixir
@callback handle_call(msg :: term(), from(), ctx :: Kino.JS.Live.Context.t()) ::
  {:noreply, ctx :: Kino.JS.Live.Context.t()}
  | {:reply, term(), ctx :: Kino.JS.Live.Context.t()}
```

Invoked to handle synchronous `call/3` messages.

See `c:GenServer.handle_call/3` for more details.

# `handle_cast`
*optional* 

```elixir
@callback handle_cast(msg :: term(), ctx :: Kino.JS.Live.Context.t()) ::
  {:noreply, ctx :: Kino.JS.Live.Context.t()}
```

Invoked to handle asynchronous `cast/2` messages.

See `c:GenServer.handle_cast/2` for more details.

# `handle_connect`

```elixir
@callback handle_connect(ctx :: Kino.JS.Live.Context.t()) ::
  {:ok, payload(), ctx :: Kino.JS.Live.Context.t()}
```

Invoked whenever a new client connects to the server.

The returned data is passed to the JavaScript `init` function
of the connecting client.

# `handle_event`
*optional* 

```elixir
@callback handle_event(event :: String.t(), payload(), ctx :: Kino.JS.Live.Context.t()) ::
  {:noreply, ctx :: Kino.JS.Live.Context.t()}
```

Invoked to handle client events.

# `handle_info`
*optional* 

```elixir
@callback handle_info(msg :: term(), ctx :: Kino.JS.Live.Context.t()) ::
  {:noreply, ctx :: Kino.JS.Live.Context.t()}
```

Invoked to handle all other messages.

See `c:GenServer.handle_info/2` for more details.

# `init`
*optional* 

```elixir
@callback init(arg :: term(), ctx :: Kino.JS.Live.Context.t()) ::
  {:ok, ctx :: Kino.JS.Live.Context.t()}
  | {:ok, ctx :: Kino.JS.Live.Context.t(), opts :: keyword()}
```

Invoked when the server is started.

See `c:GenServer.init/1` for more details.

If you want to create other kinos on initialization, see the
limitations described in `Kino.start_child/1`.

# `terminate`
*optional* 

```elixir
@callback terminate(reason, ctx :: Kino.JS.Live.Context.t()) :: term()
when reason: :normal | :shutdown | {:shutdown, term()} | term()
```

Invoked when the server is about to exit.

See `c:GenServer.terminate/2` for more details.

# `call`

```elixir
@spec call(t(), term(), timeout()) :: term()
```

Makes a synchronous call to the kino server and waits
for its reply.

See `GenServer.call/3` for more details.

# `cast`

```elixir
@spec cast(t(), term()) :: :ok
```

Sends an asynchronous request to the kino server.

See `GenServer.cast/2` for more details.

# `monitor`

```elixir
@spec monitor(t()) :: reference()
```

Starts monitoring the kino server from the calling process.

Refer to `Process.monitor/1` for more details.

# `new`

```elixir
@spec new(module(), term(), keyword()) :: t()
```

Instantiates a live JavaScript kino defined by `module`.

The given `init_arg` is passed to the `init/2` callback when
the underlying kino process is started.

## Options

  * `:export` - a function called to export the given kino to Markdown.
    This works the same as `Kino.JS.new/3`, except the function
    receives `t:Kino.JS.Live.Context.t/0` as an argument

# `reply`

```elixir
@spec reply(from(), term()) :: :ok
```

Replies to the kino server.

This function can be used to explicitly send a reply to the kino server
that called `call/3` when the reply cannot be specified in the return
value of `handle_call/3`.

See `GenServer.reply/2` for more details.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
