# `StatifierUI.EventLog.Labels`
[🔗](https://github.com/riddler/statifier-ui/blob/v0.11.0/lib/statifier_ui/event_log/labels.ex#L1)

Turns the wire format's integer indexes into strings a human can read,
using only the `session.start` tables already on the stream
(`lib/statifier_ui/trace/manifest.ex:120-211`).

A `t()` is a flat lookup built once, from `from_manifest/1` or
`from_log/1`, and then consulted many times as an `EventLog` renders.
Every resolver takes the `t()` first so a renderer can partially apply
it (`&Labels.state(labels, &1)`) across a list of indexes.

## The late-attach degradation

`empty/0` is not a stub - it is the late-attach case
(`StatifierUI.Trace.Subscriber` emits no `session.start` when it joins a
running session that already has one). With no manifest tables at all,
every resolver falls back to its bare `"#<index>"` form, which is why
every resolver's "not found" branch and `empty/0`'s "always not found"
case are the same code path rather than two.

## Origins and owners

`origin/2` and `owner/2` dispatch on the `"kind"` field of an origin or
owner object (`docs/wire-format.md:625-660`) and delegate to the four
index resolvers above. Both are total over the documented shapes: an
unrecognized `"kind"` renders the kind string itself rather than
raising, matching the format's additive-field rule - a client should
degrade gracefully in front of a newer producer, not crash.

# `t`

```elixir
@type t() :: %StatifierUI.EventLog.Labels{
  contents: %{optional(non_neg_integer()) =&gt; map()},
  data: %{optional(non_neg_integer()) =&gt; map()},
  states: %{optional(non_neg_integer()) =&gt; map()},
  transitions: %{optional(non_neg_integer()) =&gt; map()}
}
```

# `content`

```elixir
@spec content(t(), non_neg_integer()) :: String.t()
```

Resolves a content `index` to `"<kind>@<start_line>:<start_column>"`.
An unknown index renders `"#<index>"`.

# `data`

```elixir
@spec data(t(), non_neg_integer()) :: String.t()
```

Resolves a `<data>` `index` to its `"id"`, otherwise `"#<index>"`.

# `empty`

```elixir
@spec empty() :: t()
```

The late-attach case: no tables at all, so every index resolves to its bare `"#<index>"` form.

# `from_log`

```elixir
@spec from_log(StatifierUI.EventLog.t()) :: t()
```

Finds the first `session.start` in `log`'s `session_messages` and
delegates to `from_manifest/1`. Returns `empty/0` when the log carries
none - the late-attach case.

# `from_manifest`

```elixir
@spec from_manifest(StatifierUI.Trace.Message.t()) :: t()
```

Builds a `t()` from a `session.start` message's payload, keying each
table by its own index field (`"index"`, `"t_index"`, `"c_index"`,
`"d_index"`).

# `origin`

```elixir
@spec origin(t(), map()) :: String.t()
```

Resolves an origin object (`docs/wire-format.md:625-645`) by dispatching
on its `"kind"`. An unrecognized `"kind"` renders the kind string
itself.

# `owner`

```elixir
@spec owner(t(), map()) :: String.t()
```

Resolves an owner object (`docs/wire-format.md:646-660`) by dispatching
on its `"kind"`. An unrecognized `"kind"` renders the kind string
itself.

# `state`

```elixir
@spec state(t(), non_neg_integer()) :: String.t()
```

Resolves a state `index` to its `"id"` when present, `"<scxml>"` for
the synthesized root, or a bare `"#<index>"` for an anonymous state or
an index this `t()` does not know about.

# `states`

```elixir
@spec states(t(), [non_neg_integer()]) :: String.t()
```

Resolves each of `indexes` through `state/2`, in list order, joined with `", "`.

# `transition`

```elixir
@spec transition(t(), non_neg_integer()) :: String.t()
```

Resolves a transition `index` to `"<events>: <source> -> <targets>"`.

`events` is an array of arrays of strings - one dot-split token list
per whitespace-separated event descriptor - so inner lists join with
`"."` and the outer list joins with `" "`; an empty `events` list
renders `"(eventless)"`. Targets join with `", "`; an empty target
list renders `"(targetless)"`. An unknown index renders `"#<index>"`.

---

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