# `Dala.Ui.Renderer`
[🔗](https://github.com/manhvu/dala/blob/main/lib/dala/ui/renderer.ex#L1)

Serializes a component tree to a binary command stream and passes it to the
platform NIF in a single call. Compose (Android) and SwiftUI (iOS) handle
diffing and rendering internally.

## Node format

    %{
      type: :column,
      props: %{padding: :space_md, background: :surface},
      children: [
        %{type: :text,   props: %{text: "Hello", text_size: :xl, text_color: :on_surface}, children: []},
        %{type: :button, props: %{text: "Tap", on_tap: self()},    children: []}
      ]
    }

## Token resolution

Atom values for color props, spacing props, radius props, and text sizes are
resolved at render time through the active `Dala.Theme` and the base palette.

**Color props** (`:background`, `:text_color`, `:border_color`, `:color`,
`:placeholder_color`): resolved via theme semantic tokens first, then the
base palette. E.g. `:primary` → theme's primary → `:blue_500` → `0xFF2196F3`.

**Spacing props** (`:padding`, `:padding_top`, etc., `:gap`): accept spacing
tokens (`:space_xs`, `:space_sm`, `:space_md`, `:space_lg`, `:space_xl`)
that are scaled by `theme.space_scale`.

**Radius props** (`:corner_radius`): accept `:radius_sm`, `:radius_md`,
`:radius_lg`, `:radius_pill` from the active theme.

**Border** (currently honored on `:box` only): set both `:border_color`
(a color token like `:primary` or `:border`) and `:border_width` (an
integer pt/dp value, e.g. `1`). When width is 0 or color is unset, no
border draws.

**Text size props** (`:text_size`, `:font_size`): token atoms (`:xl`, `:lg`,
etc.) are multiplied by `theme.type_scale`.

## Component defaults

When a component's props omit styling keys, the renderer injects sensible
defaults from the active theme. Explicit props always win over defaults.

    # Gets primary background, white text, medium radius automatically:
    %{type: :button, props: %{text: "Save", on_tap: {self(), :save}}, children: []}

## Style structs

A `%Dala.Ui.Style{}` value under the `:style` key is merged into the node's
own props before serialisation. Inline props override style values.

## Platform blocks

Props scoped to one platform are silently ignored on the other:

    props: %{padding: 12, ios: %{padding: 20}}
    # iOS sees padding: 20; Android sees padding: 12

## Injecting a mock NIF

    Dala.Ui.Renderer.render(tree, :android, MockNIF)

# `colors`

```elixir
@spec colors() :: %{required(atom()) =&gt; non_neg_integer()}
```

Return the full color palette map (token → ARGB integer).

# `compute_layout_hash`

```elixir
@spec compute_layout_hash(Dala.Node.t()) :: non_neg_integer()
```

Compute the layout hash for a node. Delegates to `Dala.Node.compute_layout_hash/1`.

# `encode_event`

```elixir
@spec encode_event(
  String.t() | atom(),
  non_neg_integer(),
  non_neg_integer(),
  binary()
) :: binary()
```

Encode an EVENT command

# `encode_frame`

Encode patches to binary frame format for the native side (v3)

# `encode_patch_node`

```elixir
@spec encode_patch_node(String.t() | atom(), non_neg_integer(), map()) :: binary()
```

Encode a PATCH_NODE command with field mask

# `encode_register_string`

```elixir
@spec encode_register_string(non_neg_integer(), binary()) :: binary()
```

Encode a REGISTER_STRING command

# `encode_set_style`

```elixir
@spec encode_set_style(String.t() | atom(), map()) :: binary()
```

Encode a SET_STYLE command for style-only updates

# `encode_set_text`

```elixir
@spec encode_set_text(String.t() | atom(), binary()) :: binary()
```

Encode a SET_TEXT command

# `encode_tree`

Encode a full Dala.Node tree to binary format (v3)

# `encode_tree_with_taps`

Encode tree with tap handles for render_fast

# `hash_id`

```elixir
@spec hash_id(String.t() | atom()) :: non_neg_integer()
```

Hash a node ID to a stable u64. Delegates to `Dala.Node.stable_id/1`.

# `render`

```elixir
@spec render(map(), atom(), module() | atom(), atom()) ::
  {:ok, :binary_tree} | {:error, term()}
```

Render a component tree for the given platform.

Loads the active `Dala.Theme`, clears the tap registry, serialises the tree
to a binary command stream, and calls `set_root_binary/1` on the NIF.
Returns `{:ok, :binary_tree}`.

`transition` is an atom (`:push`, `:pop`, `:reset`, `:none`) for the nav
animation. Defaults to `:none` (instant swap).

# `render_fast`

```elixir
@spec render_fast(Dala.Node.t() | map(), atom(), module(), atom()) ::
  {:ok, :binary_tree}
```

Fast render path for simple updates.

Optimized version that batches tap registrations with the binary encoding.
Otherwise identical to `render/4` — loads the active theme, serialises the
tree to binary, and calls `set_root_binary/1` on the NIF.

# `render_patches`

```elixir
@spec render_patches(
  Dala.Node.t() | map() | nil,
  Dala.Node.t() | map(),
  atom(),
  module(),
  atom()
) :: {:ok, [Dala.Ui.Diff.patch()]}
```

Render using incremental patches instead of full tree.

Compares `old_tree` with `new_tree`, computes the diff, and sends
only the patches to native. Falls back to full render on first call
(when `old_tree` is nil).

`new_tree` can be either a map (from Dala.Ui.Widgets functions) or a `Dala.Node` struct.
If it's a map, it will be converted to a `Dala.Node` first.

Returns `{:ok, patches}` where patches is the list of patch tuples.

# `text_sizes`

```elixir
@spec text_sizes() :: %{required(atom()) =&gt; float()}
```

Return the text-size scale map (token → float sp).

---

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