# `Coelho.Kino`
[🔗](https://github.com/nseaSeb/coelho/blob/main/lib/coelho/kino.ex#L2)

A Coelho editor in a Livebook cell.

    editor = Coelho.Kino.new()

It is the same editor a LiveView renders — the same component, the same
hook, the same schema on both sides — so what a notebook shows is what an
application gets. Read what was typed, as the document that would be
stored:

    Coelho.Kino.read(editor)

React to every change:

    Kino.listen(editor, fn %{type: :change, document: document} ->
      IO.inspect(Coelho.to_text(document))
    end)

Every change is validated on the server before it is kept, exactly as a
form submission would be: a document the schema refuses is never stored,
and the editor that sent it is put back to the last valid one.

Requires `kino` and `phoenix_live_view`, both optional dependencies of
Coelho:

    Mix.install([:coelho, :kino, :phoenix_live_view])

The editor has no uploads: a LiveView's upload channel is what carries
them, and a notebook has none.

## One writer at a time

Each change carries the whole document, and the last one to arrive is
what is kept. Two people typing in the same editor — two tabs on one
session, or a collaborator in Livebook — overwrite each other, and the
other editors are not told of a change until they reconnect. Writing
together is collaboration proper, which Coelho does not do yet.

# `event`

```elixir
@type event() ::
  %{type: :change, document: map()}
  | %{type: :hook, event: String.t(), payload: term()}
```

An event emitted to `Kino.listen/2` and `Kino.Control.stream/1`.

# `new`

```elixir
@spec new(keyword()) :: Kino.JS.Live.t()
```

A new editor.

## Options

  * `:schema` — the `Coelho.Schema` to edit under. `Coelho.Schema.default/0`
    when omitted
  * `:value` — the document to start from, validated against the schema;
    an empty document when omitted

Any other option is an attribute of `Coelho.LiveView.coelho_editor/1` —
`:toolbar`, `:labels`, `:placeholder`, `:maxlength` — passed through as
given. `:suggest` passes through too, but only opens and closes the list:
the queries arrive as `:hook` events, and nothing here inserts a choice.

Raises `ArgumentError` for a `:value` the schema refuses.

# `read`

```elixir
@spec read(Kino.JS.Live.t()) :: map()
```

The document in the editor, as it would be stored.

# `set`

```elixir
@spec set(Kino.JS.Live.t(), map()) :: :ok | {:error, [Coelho.Document.Error.t()]}
```

Replaces the document in the editor.

Validated like everything else: `{:error, errors}` leaves the editor as it
was.

---

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