# `Plushie.Bridge`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/bridge.ex#L1)

Bridge to the plushie renderer process.

Manages the connection to the renderer, buffers partial JSONL lines (JSON
mode) or receives length-prefixed frames (MessagePack mode), and forwards
decoded events to the runtime process.

## Transport modes

Transport I/O is delegated to modules implementing `Plushie.Transport`.
Controlled by the `:transport` option:

- `:spawn` (default) - spawns the renderer binary as a child process
  using an Erlang Port (`Plushie.Transport.Port`).

- `:stdio` - reads/writes the BEAM's own stdin/stdout. Used when the
  renderer spawns the Elixir process (`Plushie.Transport.Port`).

- `{:iostream, pid}` - sends and receives protocol messages via an
  external process (`Plushie.Transport.IOStream`). See that module for
  the adapter protocol.

## Wire formats

Controlled by the `:format` option:

- `:json` - JSONL over stdio. Opt-in for debugging and observability.
- `:msgpack` (default) - MessagePack with 4-byte length-prefixed framing.

On unexpected exit the bridge applies exponential back-off and attempts
to restart the renderer up to `max_restarts` times. If the limit is
exhausted the GenServer stops with `{:max_restarts_reached, reason}`.

During restart the runtime rebuilds renderer-owned state by re-sending
settings, a full snapshot, subscriptions, and windows. Transient commands
that cannot be rebuilt from that state are held until the runtime finishes
resync, then sent in order.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `restart_renderer`

```elixir
@spec restart_renderer(bridge :: GenServer.server()) :: :ok
```

Restarts the renderer process intentionally (e.g. after a Rust rebuild).

Unlike crash recovery, this does not count against the restart limit
and does not use exponential backoff. The existing renderer is closed
cleanly before opening the new one. The runtime receives
`:renderer_restarted` and re-syncs as usual.

# `screenshot`

```elixir
@spec screenshot(
  bridge :: GenServer.server(),
  name :: String.t(),
  opts :: keyword(),
  timeout :: timeout()
) :: map()
```

Captures a renderer screenshot and returns the raw response map.

Width and height are optional positive integers. The call blocks until the
renderer replies with `screenshot_response`.

# `send_advance_frame`

```elixir
@spec send_advance_frame(bridge :: GenServer.server(), timestamp :: non_neg_integer()) ::
  :ok
```

Sends an advance_frame message to the renderer (headless/test mode).

# `send_command`

```elixir
@spec send_command(
  bridge :: GenServer.server(),
  id :: String.t(),
  family :: String.t(),
  value :: term()
) :: :ok
```

Sends a widget-targeted command to the renderer.

# `send_commands`

```elixir
@spec send_commands(
  bridge :: GenServer.server(),
  commands :: [{String.t(), String.t(), term()}]
) :: :ok
```

Sends a batch of widget-targeted commands to the renderer.

# `send_effect`

```elixir
@spec send_effect(
  bridge :: GenServer.server(),
  id :: String.t(),
  kind :: String.t(),
  payload :: map()
) :: :ok
```

Sends an effect request to the renderer.

# `send_image_op`

```elixir
@spec send_image_op(bridge :: GenServer.server(), op :: String.t(), payload :: map()) ::
  :ok
```

Sends an image operation (create/update/delete) to the renderer.

# `send_interact`

```elixir
@spec send_interact(
  bridge :: GenServer.server(),
  id :: String.t(),
  action :: String.t(),
  selector :: map(),
  payload :: map()
) :: :ok
```

Sends an interact request to the renderer.

The renderer will process the interaction against its widget tree and
respond with `interact_step` / `interact_response` messages. These are
forwarded to the runtime as `{:interact_step, id, events}` and
`{:interact_response, id, events}`.

## Parameters

- `id` - unique request identifier, used to correlate responses.
- `action` - the interaction verb. One of: `"click"`, `"toggle"`,
  `"select"`, `"type_text"`, `"submit"`, `"press"`, `"release"`,
  `"type_key"`, `"slide"`, `"paste"`, `"scroll"`, `"move_to"`,
  `"sort"`, `"canvas_press"`, `"canvas_release"`, `"canvas_move"`,
  `"pane_focus_cycle"`.
- `selector` - a map identifying the target widget. Keys are
  optional and include `"by"` (e.g. `"id"`, `"text"`, `"role"`,
  `"label"`, `"focused"`) and `"value"` (the lookup value). An
  empty map targets the focused widget or the window root.
- `payload` - action-specific data. Examples:
  - `%{text: "hello"}` for `"type_text"` / `"paste"`
  - `%{value: "option"}` for `"select"`
  - `%{value: 0.5}` for `"slide"`
  - `%{key: "Enter", modifiers: %{}}` for `"press"` / `"release"` / `"type_key"`
  - `%{x: 10, y: 20, button: "left"}` for `"canvas_press"` / `"canvas_release"`
  - `%{x: 10, y: 20}` for `"canvas_move"` / `"move_to"`
  - `%{delta_x: 0, delta_y: -3}` for `"scroll"`
  - `%{column: "name", direction: "asc"}` for `"sort"`
  - `%{}` for `"click"`, `"toggle"`, `"submit"`, `"pane_focus_cycle"`

# `send_load_font`

```elixir
@spec send_load_font(
  bridge :: GenServer.server(),
  family :: String.t(),
  data :: binary()
) :: :ok
```

Sends a font-load message to the renderer.

# `send_patch`

```elixir
@spec send_patch(bridge :: GenServer.server(), ops :: [map()]) :: :ok
```

Sends a patch (list of diff ops) to the renderer.

# `send_register_effect_stub`

```elixir
@spec send_register_effect_stub(
  bridge :: GenServer.server(),
  kind :: String.t(),
  response :: term()
) :: :ok
```

Registers an effect stub with the renderer.

# `send_settings`

```elixir
@spec send_settings(bridge :: GenServer.server(), settings :: map()) :: :ok
```

Sends application-level settings to the renderer.

# `send_snapshot`

```elixir
@spec send_snapshot(bridge :: GenServer.server(), tree :: map()) :: :ok
```

Sends an encoded snapshot of `tree` to the renderer.

# `send_subscribe`

```elixir
@spec send_subscribe(
  bridge :: GenServer.server(),
  kind :: String.t(),
  tag :: String.t(),
  max_rate :: non_neg_integer() | nil,
  window_id :: String.t() | nil
) :: :ok
```

Subscribes to a renderer-side event source.

# `send_system_op`

```elixir
@spec send_system_op(
  bridge :: GenServer.server(),
  op :: String.t(),
  settings :: map()
) :: :ok
```

Sends a system-wide operation to the renderer.

# `send_system_query`

```elixir
@spec send_system_query(
  bridge :: GenServer.server(),
  op :: String.t(),
  settings :: map()
) :: :ok
```

Sends a system-wide query to the renderer.

# `send_unregister_effect_stub`

```elixir
@spec send_unregister_effect_stub(bridge :: GenServer.server(), kind :: String.t()) ::
  :ok
```

Removes a previously registered effect stub.

# `send_unsubscribe`

```elixir
@spec send_unsubscribe(
  bridge :: GenServer.server(),
  kind :: String.t(),
  tag :: String.t() | nil
) :: :ok
```

Unsubscribes from a renderer-side event source.

# `send_widget_op`

```elixir
@spec send_widget_op(bridge :: GenServer.server(), op :: String.t(), payload :: map()) ::
  :ok
```

Sends a widget operation to the renderer.

# `send_window_op`

```elixir
@spec send_window_op(
  bridge :: GenServer.server(),
  op :: String.t(),
  window_id :: String.t(),
  settings :: map()
) :: :ok
```

Sends a window lifecycle operation to the renderer.

# `start_link`

```elixir
@spec start_link(opts :: keyword()) :: GenServer.on_start()
```

Starts the bridge linked to the calling process.

Required opts:
  - `:runtime`       - pid to receive `{:renderer_event, event}` messages

Required for `:spawn` transport (default):
  - `:renderer_path` - filesystem path to the plushie binary

Optional opts:
  - `:name`          - registration name passed to `GenServer.start_link/3`
  - `:transport`     - `:spawn` (default, spawns renderer as child process),
                       `:stdio` (reads/writes the BEAM's own stdin/stdout),
                       or `{:iostream, pid}` (custom transport via iostream adapter)
  - `:format`        - wire format, `:msgpack` (default) or `:json`
  - `:log_level`     - renderer log level (`:off`, `:error`, `:warning`, `:info`, `:debug`).
                       Default: `:error`. Ignored when `RUST_LOG` is set in the environment.
  - `:renderer_args` - extra CLI args prepended to the renderer command (e.g. `["--headless"]`)
  - `:max_restarts`  - max restart attempts before giving up (default: 5)
  - `:restart_delay` - base delay in ms for exponential back-off (default: 100)
  - `:heartbeat_interval` - maximum time (ms) between renderer messages before
                       the bridge considers the renderer unresponsive and triggers
                       a restart. `nil` disables the watchdog. Default: `30_000`.

# `stop`

```elixir
@spec stop(bridge :: GenServer.server()) :: :ok
```

Stops the bridge GenServer.

---

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