# `StatifierUI.Kino`
[🔗](https://github.com/riddler/statifier-ui/blob/v0.11.0/lib/statifier_ui/kino.ex#L2)

The Livebook inspector: `inspect/3` composes the four panes -
configuration diagram, datamodel explorer, event injection, event
log - over one shared `StatifierUI.Trace.Subscriber`, attached with
catch-up (statifier ADR-0049), inside one `Kino.Layout`.

Compiled only when the optional `:kino` dependency is present
(ADR-0004); a host without it gets a stub whose `inspect/3` raises
with instructions. Nothing else in this package touches Kino.

## The scrubber (sui-3gg)

Four buttons above the diagram - **First**, **Prev**, **Next**,
**Live** - move the diagram and the datamodel explorer from the live
tip to any macrostep in the log and back. Selecting a macrostep draws
the configuration that macrostep settled in, shows the datamodel as it
stood at the end of that macrostep, opens its entry in the event log,
and marks that entry `- selected`; the note above the diagram says
which point is on screen. `StatifierUI.Inspector` decides all of it, so the
whole behaviour is testable without Kino - this module only wires
buttons to `Updater.scrub/2`.

Buttons rather than a click target on the log entry itself: the log
pane is `Kino.Markdown`, which renders text and cannot carry a click
handler back to the runtime. A directly clickable entry is the
ADR-0008 renderer's to give, not Mermaid-and-Markdown's.

## Lifecycle

Every process this module starts - the subscriber and the updater -
goes through `Kino.start_child/1`, so re-evaluating the cell
terminates them with it. The session notices the dead subscriber
through its own monitor and drops it from its subscriber set: that is
the clean detach, with nothing to unsubscribe by hand. The session
itself is *not* owned here - it keeps running across cell
re-evaluations, which is exactly what makes catch-up worth having.

## Catch-up needs `record: true`

Start the session with `record: true` (and `trace: true`) for the
inspector to reconstruct everything it missed. Without it the
subscriber falls back to live delivery and the status header labels
the panes **Live-only** - a partial stream is never presented as
whole. Replay cost grows with run length (it re-runs the recording in
the attaching process), so expect cell evaluation on a very long-lived
session to take correspondingly longer.

# `inspect`

```elixir
@spec inspect(pid(), StatifierUI.Fixtures.t() | nil, keyword()) :: Kino.Layout.t()
```

Builds the inspector for `session` and returns the composed
`Kino.Layout` for the cell to render.

`fixtures` is a `t:StatifierUI.Fixtures.t/0` (or `nil`): it feeds the
injection palette's per-event buttons. `opts`:

  * `:source` - the SCXML text, forwarded to the subscriber for the
    `session.start` manifest.
  * `:capacity` - the subscriber's buffer capacity (default 1000).

# `inspect_trace`

```elixir
@spec inspect_trace(
  Path.t() | [StatifierUI.Trace.Message.t()],
  StatifierUI.Fixtures.t() | nil,
  keyword()
) :: Kino.Layout.t() | Kino.Markdown.t()
```

Reopens a saved trace: the third leg of
`StatifierUI.Trace.Capture`'s record / save / reload.

    StatifierUI.Kino.inspect_trace("execution.jsonl")

`trace` is a file path (read through
`StatifierUI.Trace.Capture.load/1`) or an already-loaded
`t:StatifierUI.Trace.Message.t/0` list. `fixtures` is accepted for
symmetry with `inspect/3` and is currently unused - the palette it
feeds is an injection affordance, and there is nothing to inject into.

`opts`:

  * `:machine` - the compiled `%Statifier.Machine{}` to draw against.
    Optional: without it the machine is recompiled from the SCXML the
    trace carries in its own `session.start` message, which is what
    `StatifierUI.Trace.Capture.record/3`'s `:source` option puts
    there. A trace captured without `:source` and reopened without
    `:machine` has no chart to draw and returns a
    `Kino.Markdown` saying so rather than a diagram it cannot draw.

## Stepping it (sui-2uz)

The layout carries the same **|< First / < Prev / Next > / Live**
scrubber `inspect/3` has, plus a **jump to** select listing every
macrostep by number and event, and a datamodel diff pane saying what
the selected macrostep changed. A persisted stream has no tip that
moves, so "Live" here means the end of the recording.

Stepping a recording asks the engine for nothing: every configuration
shown was stamped by the engine into the file, and a trace read back
through `StatifierUI.Trace.Capture.load/1` got its contents from
statifier ADR-0034 replay re-driving the core at capture time. The
controls move a *selection* over messages already in hand.

## What this is still not

**There is no injection form.** Injection drives a session, and a file
has none. The `fixtures` argument stays accepted and unused for that
reason.

# `palette_panel`

```elixir
@spec palette_panel(
  StatifierUI.Fixtures.Bundle.discovery(),
  keyword()
) :: Kino.Markdown.t()
```

Renders every bundle a palette discovery found, one panel after another,
as a single `Kino.Markdown` widget.

`discovery` is what `StatifierUI.Fixtures.Bundle.discover/2` or
`discover_dir/2` returned.

# `test_panel`

```elixir
@spec test_panel(
  StatifierUI.Fixtures.Bundle.t(),
  keyword()
) :: Kino.Markdown.t()
```

Renders one fragment's fixture bundle as its "test this step" panel - its
truth table plus its expectation results (sui-13q) - as a
`Kino.Markdown` widget.

Like `truth_table/2`, independent of `inspect/3` and of any session: a
bundle is a fact about one palette entry's examples, so this needs no
running chart. `opts` go to
`StatifierUI.Fixtures.Bundle.Markdown.render/2`, which forwards each one
to the layer that owns it.

# `truth_table`

```elixir
@spec truth_table(
  StatifierUI.Fixtures.t(),
  keyword()
) :: Kino.Markdown.t()
```

Renders `fixtures`' truth table - every expression evaluated across every
dataset (ADR-0006) - as a `Kino.Markdown` widget.

Independent of `inspect/3` and of any session: a truth table is a fact
about expressions and datasets, so this needs no running chart and no
Phoenix. `opts` are split by owner - `:expressions`, `:datasets`,
`:functions`, and `:providers` go to `StatifierUI.TruthTable.build/2`,
and the rest to `StatifierUI.TruthTable.Markdown.render/2`.

---

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