# `Plushie.Command`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.6.0/lib/plushie/command.ex#L1)

Commands describe side effects that `update/2` wants the runtime to perform.

They are plain data -- inspectable, testable, serializable. The runtime
interprets them after `update/2` returns. Nothing executes inside `update`.

## Categories

- **Basic**: `none/0`, `done/2`, `async/2`, `stream/2`, `cancel/1`, `send_after/2`, `exit/0`
- **Focus**: `focus/1`, `focus_next/0`, `focus_previous/0`
- **Text**: `select_all/1`, `move_cursor_to_front/1`, `move_cursor_to_end/1`,
  `move_cursor_to/2`, `select_range/3`
- **Scroll**: `scroll_to/2`, `snap_to/3`, `snap_to_end/1`, `scroll_by/3`
- **Window ops**: `close_window/1`, `resize_window/3`, `move_window/3`,
  `maximize_window/2`, `minimize_window/2`, `set_window_mode/2`,
  `toggle_maximize/1`, `toggle_decorations/1`, `gain_focus/1`,
  `set_window_level/2`, `drag_window/1`, `drag_resize_window/2`,
  `request_user_attention/2`, `screenshot/2`, `set_resizable/2`,
  `set_min_size/3`, `set_max_size/3`, `enable_mouse_passthrough/1`,
  `disable_mouse_passthrough/1`, `show_system_menu/1`, `set_icon/4`,
  `set_resize_increments/3`, `allow_automatic_tabbing/1`.
- **Window queries**: `get_window_size/2`, `get_window_position/2`,
  `is_maximized/2`, `is_minimized/2`, `get_mode/2`, `get_scale_factor/2`,
  `raw_id/2`, `monitor_size/2`
- **System ops**: `allow_automatic_tabbing/1`
- **System queries**: `get_system_theme/1`, `get_system_info/1`
- **PaneGrid ops**: `pane_split/4`, `pane_close/2`, `pane_swap/3`,
  `pane_maximize/2`, `pane_restore/1`
- **Image ops**: `create_image/2`, `create_image/4`, `update_image/2`,
  `update_image/4`, `delete_image/1`, `list_images/1`, `clear_images/0`
- **Queries**: `tree_hash/1`, `find_focused/1`
- **Font**: `load_font/1`
- **Accessibility**: `announce/1`
- **Native widget**: `widget_command/3`, `widget_commands/1`
- **Test/Headless**: `advance_frame/1`
- **Batch**: `batch/1`

## Result delivery

Commands deliver results back to `update/2` through three mechanisms:

- **Async/Stream**: `async/2` delivers `%Plushie.Event.AsyncEvent{tag: tag, result: result}`.
  `stream/2` delivers `%Plushie.Event.StreamEvent{tag: tag, value: value}` for each chunk.
- **Window and system queries**: `get_window_size/2`, `get_mode/2`, etc. deliver
  `%Plushie.Event.SystemEvent{}` structs through `update/2`. The `type` field identifies the
  query kind, `tag` holds the stringified event tag, and `data` holds the result payload.
  For example, `get_system_theme(:my_tag)` delivers
  `%SystemEvent{type: :system_theme, tag: "my_tag", data: "dark"}`.
- **Platform effects**: `Plushie.Effect` functions deliver
  `%Plushie.Event.EffectEvent{tag: tag, result: result}`. The `tag` matches the
  atom you provided when creating the effect command. Timeouts deliver the
  same struct with `result: {:error, :timeout}`. See `Plushie.Effect`.

## Usage

    def update(model, %Plushie.Event.WidgetEvent{type: :click, id: "save"}) do
      cmd = Plushie.Command.async(fn -> save(model) end, :save_result)
      {model, cmd}
    end

    def update(model, %Plushie.Event.AsyncEvent{tag: :save_result, result: :ok}), do: %{model | saved: true}

Multiple commands can be issued at once via `batch/1`:

    cmd = Plushie.Command.batch([
      Plushie.Command.focus("name_input"),
      Plushie.Command.send_after(5000, :auto_save)
    ])

# `event_tag`

```elixir
@type event_tag() :: atom()
```

Tag atom used to identify async results in `update/2`.

# `t`

```elixir
@type t() :: %Plushie.Command{payload: map(), type: atom()}
```

A command to be dispatched by the runtime.

Always a `%Command{}` struct. `batch/1` wraps multiple commands into a
single struct with `type: :batch`. The runtime normalizes bare lists
internally, but the public type is always a struct.

# `widget_id`

```elixir
@type widget_id() :: String.t()
```

Stable string identifier for a widget node in the UI tree.

# `window_id`

```elixir
@type window_id() :: String.t()
```

Stable string identifier for a window node in the UI tree.

# `advance_frame`

```elixir
@spec advance_frame(timestamp :: non_neg_integer()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Advance the animation clock by one frame in headless/test mode.

Sends an `advance_frame` message to the renderer with the given
`timestamp` (monotonic milliseconds). If `on_animation_frame` is
subscribed, the renderer emits an `animation_frame` event back.

This is a test/headless-only command. In normal daemon mode the
renderer drives animation frames from the display vsync.

## Example

    Plushie.Command.advance_frame(16)

# `allow_automatic_tabbing`

```elixir
@spec allow_automatic_tabbing(enabled :: boolean()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Sets whether the system can automatically organize windows into tabs.

This is a macOS-specific setting. On other platforms it is a no-op.
See: https://developer.apple.com/documentation/appkit/nswindow/1646657-allowsautomaticwindowtabbing

# `announce`

```elixir
@spec announce(text :: String.t()) :: %Plushie.Command{payload: term(), type: term()}
```

Triggers a screen reader announcement without a visible widget.

The text is immediately announced by assistive technology as a
live-region assertion. Useful for status updates, error messages,
and other dynamic content that should be announced but doesn't
need to be visually displayed.

## Example

    Command.announce("File saved successfully")
    Command.announce("3 search results found")

# `async`

```elixir
@spec async(fun :: fun(), event_tag :: atom()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Run `fun` asynchronously in a Task. When it returns, the runtime dispatches
`%Plushie.Event.AsyncEvent{tag: event_tag, result: result}` through `update/2`.

Only one task per tag can be active. If a task with the same tag is
already running, it is killed and replaced. Use unique tags if you
need concurrent tasks.

# `batch`

```elixir
@spec batch(commands :: t() | [t()]) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Issue multiple commands. Commands in the batch execute sequentially
in list order, with state threaded through each.

Accepts a single command, a list of commands, or a nested list -- anything
`List.wrap/1` can normalize.

# `cancel`

```elixir
@spec cancel(event_tag :: atom()) :: %Plushie.Command{payload: term(), type: term()}
```

Cancel a running async or stream command by its tag.

If the task has already completed, this is a no-op. The runtime tracks
running tasks by their event tag and terminates the associated process.

## Example

    Command.cancel(:file_import)

# `clear_images`

```elixir
@spec clear_images() :: %Plushie.Command{payload: term(), type: term()}
```

Clears all in-memory images.

# `close_window`

```elixir
@spec close_window(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Close the window identified by `window_id`.

# `create_image`

```elixir
@spec create_image(handle :: String.t(), data :: binary()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Creates an in-memory image from encoded PNG/JPEG bytes.

The raw binary is stored as-is in the command payload. The protocol layer
handles format-specific encoding (native binary for msgpack, base64 for JSON).

# `create_image`

```elixir
@spec create_image(
  handle :: String.t(),
  width :: pos_integer(),
  height :: pos_integer(),
  pixels :: binary()
) :: %Plushie.Command{payload: term(), type: term()}
```

Creates an in-memory image from raw RGBA pixel data.

The raw binary is stored as-is in the command payload. The protocol layer
handles format-specific encoding (native binary for msgpack, base64 for JSON).

# `delete_image`

```elixir
@spec delete_image(handle :: String.t()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Deletes an in-memory image by handle name.

# `disable_mouse_passthrough`

```elixir
@spec disable_mouse_passthrough(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Disable mouse passthrough on a window.

# `done`

```elixir
@spec done(value :: term(), msg_fn :: (term() -&gt; term())) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Wraps an already-resolved value in a command. The runtime immediately
dispatches `msg_fn.(value)` through `update/2` without spawning a task.

Useful for lifting a pure value into the command pipeline.

# `drag_resize_window`

```elixir
@spec drag_resize_window(window_id :: window_id(), direction :: atom() | String.t()) ::
  %Plushie.Command{payload: term(), type: term()}
```

Start drag-resizing the window from the given edge/corner direction.

# `drag_window`

```elixir
@spec drag_window(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Start dragging the window.

# `enable_mouse_passthrough`

```elixir
@spec enable_mouse_passthrough(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Enable mouse passthrough on a window (clicks pass through to windows below).

# `exit`

```elixir
@spec exit() :: %Plushie.Command{payload: term(), type: term()}
```

Exit the application.

# `find_focused`

```elixir
@spec find_focused(tag :: atom()) :: %Plushie.Command{payload: term(), type: term()}
```

Queries which widget currently has focus.

The result arrives in `update/2` as
`%SystemEvent{type: :find_focused, tag: tag, data: %{"focused" => "..." | nil}}`.

Note: if no widget is focused, the `"focused"` field may be `nil`.

# `focus`

```elixir
@spec focus(widget_id :: widget_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Focus the widget identified by `widget_id`.

Supports window-qualified paths: `"main#email"` targets widget
`"email"` in window `"main"`.

# `focus_element`

```elixir
@spec focus_element(canvas_id :: widget_id(), element_id :: String.t()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Focus a specific interactive element within a canvas. Supports `"window#canvas"`.

# `focus_next`

```elixir
@spec focus_next() :: %Plushie.Command{payload: term(), type: term()}
```

Move focus to the next focusable widget.

# `focus_previous`

```elixir
@spec focus_previous() :: %Plushie.Command{payload: term(), type: term()}
```

Move focus to the previous focusable widget.

# `gain_focus`

```elixir
@spec gain_focus(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Give focus to a window.

# `get_mode`

```elixir
@spec get_mode(window_id :: window_id(), tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query the current window mode (windowed, fullscreen, hidden).

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: mode}`.

# `get_scale_factor`

```elixir
@spec get_scale_factor(window_id :: window_id(), tag :: event_tag()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Query the window's current scale factor (DPI scaling).

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: factor}`.

# `get_system_info`

```elixir
@spec get_system_info(tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query system information (OS, CPU, memory, graphics).

The result arrives in `update/2` as
`%Plushie.Event.SystemEvent{type: :system_info, tag: tag, data: info}` where `tag`
is the stringified event tag and `info` is a map with keys:
`"system_name"`, `"system_kernel"`, `"system_version"`,
`"system_short_version"`, `"cpu_brand"`, `"cpu_cores"`, `"memory_total"`,
`"memory_used"`, `"graphics_backend"`, `"graphics_adapter"`.

System info is always available (the `sysinfo` iced feature is enabled
unconditionally).

## Example

    def update(model, %Plushie.Event.WidgetEvent{type: :click, id: "sys_info"}) do
      {model, Plushie.Command.get_system_info(:sys_info)}
    end

    def update(model, %Plushie.Event.SystemEvent{type: :system_info, tag: "sys_info", data: info}) do
      %{model | system: info}
    end

# `get_system_theme`

```elixir
@spec get_system_theme(tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query the current system theme (light/dark mode).

The result arrives in `update/2` as
`%Plushie.Event.SystemEvent{type: :system_theme, tag: tag, data: mode}` where
`tag` is the stringified event tag and `mode` is `"light"`, `"dark"`, or
`"none"` (when no system preference is detected). Returns `"none"` on
Linux systems without a desktop environment. Apps should provide a theme
fallback.

## Example

    def update(model, %Plushie.Event.WidgetEvent{type: :click, id: "check_theme"}) do
      {model, Plushie.Command.get_system_theme(:theme_result)}
    end

    def update(model, %Plushie.Event.SystemEvent{type: :system_theme, tag: "theme_result", data: mode}) do
      %{model | theme_mode: mode}
    end

# `get_window_position`

```elixir
@spec get_window_position(window_id :: window_id(), tag :: event_tag()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Query the position of a window.

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: data}`
where `data` is `%{x: x, y: y}` or `nil` if unavailable.

# `get_window_size`

```elixir
@spec get_window_size(window_id :: window_id(), tag :: event_tag()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Query the size of a window.

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: data}`
where `data` is `%{width: width, height: height}`.

# `is_maximized`

```elixir
@spec is_maximized(window_id :: window_id(), tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query whether a window is maximized.

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: boolean}`.

# `is_minimized`

```elixir
@spec is_minimized(window_id :: window_id(), tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query whether a window is minimized.

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: boolean}`.

# `list_images`

```elixir
@spec list_images(tag :: atom()) :: %Plushie.Command{payload: term(), type: term()}
```

Lists all in-memory image handles.

The result arrives in `update/2` as
`%SystemEvent{type: :image_list, tag: tag, data: %{"handles" => [...]}}`.

# `load_font`

```elixir
@spec load_font(data :: binary()) :: %Plushie.Command{payload: term(), type: term()}
```

Loads a font at runtime from binary data.

The font data should be the raw bytes of a TrueType (.ttf) or OpenType
(.otf) font file. Once loaded, the font can be referenced by name in
widget `font` props.

## Example

    font_data = File.read!("path/to/CustomFont.ttf")
    Plushie.Command.load_font(font_data)

# `maximize_window`

```elixir
@spec maximize_window(window_id :: window_id(), maximized :: boolean()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Maximize or restore a window.

# `minimize_window`

```elixir
@spec minimize_window(window_id :: window_id(), minimized :: boolean()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Minimize or restore a window.

# `monitor_size`

```elixir
@spec monitor_size(window_id :: window_id(), tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query the monitor size for the display containing a window.

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: data}`
where `data` is `%{width: width, height: height}` or `nil` if the
monitor cannot be determined.

# `move_cursor_to`

```elixir
@spec move_cursor_to(widget_id :: widget_id(), position :: non_neg_integer()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Move the text cursor to a specific position. Supports `"window#path"`.

# `move_cursor_to_end`

```elixir
@spec move_cursor_to_end(widget_id :: widget_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Move the text cursor to the end of the input. Supports `"window#path"`.

# `move_cursor_to_front`

```elixir
@spec move_cursor_to_front(widget_id :: widget_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Move the text cursor to the front of the input. Supports `"window#path"`.

# `move_window`

```elixir
@spec move_window(window_id :: window_id(), x :: number(), y :: number()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Move a window to the given position.

# `none`

```elixir
@spec none() :: %Plushie.Command{payload: term(), type: term()}
```

A no-op command. Returned implicitly when `update/2` returns a bare model.

# `pane_close`

```elixir
@spec pane_close(pane_grid_id :: widget_id(), pane_id :: term()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Close a pane in the pane grid.

# `pane_maximize`

```elixir
@spec pane_maximize(pane_grid_id :: widget_id(), pane_id :: term()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Maximize a pane in the pane grid.

# `pane_restore`

```elixir
@spec pane_restore(pane_grid_id :: widget_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Restore all panes from maximized state.

# `pane_split`

```elixir
@spec pane_split(
  pane_grid_id :: widget_id(),
  pane_id :: term(),
  axis :: atom() | String.t(),
  new_pane_id :: term()
) :: %Plushie.Command{payload: term(), type: term()}
```

Split a pane in the pane grid along the given axis.

# `pane_swap`

```elixir
@spec pane_swap(pane_grid_id :: widget_id(), pane_a :: term(), pane_b :: term()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Swap two panes in the pane grid.

# `raw_id`

```elixir
@spec raw_id(window_id :: window_id(), tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Query the raw platform window ID (e.g. X11 window ID, HWND).

Result arrives as `%Plushie.Event.SystemEvent{tag: tag, data: platform_id}`.

# `request_user_attention`

```elixir
@spec request_user_attention(window_id :: window_id(), urgency :: atom() | nil) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Request user attention for a window. Urgency can be :informational or :critical.

# `resize_window`

```elixir
@spec resize_window(window_id :: window_id(), width :: number(), height :: number()) ::
  %Plushie.Command{payload: term(), type: term()}
```

Resize a window to the given dimensions.

# `screenshot`

```elixir
@spec screenshot(window_id :: window_id(), tag :: event_tag()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Take a screenshot of a window. Result arrives as a tagged event.

# `scroll_by`

```elixir
@spec scroll_by(widget_id :: widget_id(), x :: float(), y :: float()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Scroll the widget by a relative offset. Supports `"window#path"`.

# `scroll_to`

```elixir
@spec scroll_to(widget_id :: widget_id(), offset :: term()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Scroll the widget identified by `widget_id` to `offset`.

Supports window-qualified paths: `"main#list"`.

# `select_all`

```elixir
@spec select_all(widget_id :: widget_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Select all text in the widget identified by `widget_id`.

Supports window-qualified paths: `"main#email"`.

# `select_range`

```elixir
@spec select_range(
  widget_id :: widget_id(),
  start_pos :: non_neg_integer(),
  end_pos :: non_neg_integer()
) :: %Plushie.Command{payload: term(), type: term()}
```

Select a range of text in the input. Supports `"window#path"`.

# `send_after`

```elixir
@spec send_after(delay_ms :: non_neg_integer(), event :: term()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Send `event` through `update/2` after `delay_ms` milliseconds.

If a timer with the same event term is already pending, the previous
timer is canceled and replaced. This prevents duplicate deliveries
when `send_after` is called repeatedly for the same event.

# `set_icon`

```elixir
@spec set_icon(
  window_id :: window_id(),
  rgba_data :: binary(),
  width :: pos_integer(),
  height :: pos_integer()
) :: %Plushie.Command{payload: term(), type: term()}
```

Sets the window icon from raw RGBA pixel data.

The `rgba_data` must be a binary of `width * height * 4` bytes (one byte
each for R, G, B, A per pixel, row-major). The raw binary is stored as-is
in the command payload. The protocol layer handles format-specific encoding
(native binary for msgpack via Msgpax.Bin, base64 for JSON).

## Example

    icon_data = File.read!("icon_32x32.rgba")
    Plushie.Command.set_icon("main", icon_data, 32, 32)

# `set_max_size`

```elixir
@spec set_max_size(window_id :: window_id(), width :: number(), height :: number()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Set the maximum size of a window.

# `set_min_size`

```elixir
@spec set_min_size(window_id :: window_id(), width :: number(), height :: number()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Set the minimum size of a window.

# `set_resizable`

```elixir
@spec set_resizable(window_id :: window_id(), resizable :: boolean()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Set whether a window is resizable.

# `set_resize_increments`

```elixir
@spec set_resize_increments(
  window_id :: window_id(),
  width :: number() | nil,
  height :: number() | nil
) :: %Plushie.Command{payload: term(), type: term()}
```

Sets the resize increment size for a window.

When set, the window will only resize in multiples of the given width and
height. Pass `nil` for both to clear the constraint. Useful for terminal
emulators and grid-aligned apps.

# `set_window_level`

```elixir
@spec set_window_level(window_id :: window_id(), level :: atom() | String.t()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Set window stacking level (:normal, :always_on_top, :always_on_bottom).

On Wayland, window stacking is compositor-controlled and this command may
be silently ignored.

# `set_window_mode`

```elixir
@spec set_window_mode(window_id :: window_id(), mode :: atom() | String.t()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Set window mode (windowed, fullscreen, etc.).

# `show_system_menu`

```elixir
@spec show_system_menu(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Show the system menu for a window.

# `snap_to`

```elixir
@spec snap_to(widget_id :: widget_id(), x :: float(), y :: float()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Snap the scrollable widget to an absolute offset. Supports `"window#path"`.

# `snap_to_end`

```elixir
@spec snap_to_end(widget_id :: widget_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Snap the scrollable widget to the end of its content. Supports `"window#path"`.

# `stream`

```elixir
@spec stream(fun :: (fun() -&gt; term()), event_tag :: atom()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Run `fun` as a streaming async task. The function receives an `emit` callback
that sends intermediate results to `update/2` as
`%Plushie.Event.StreamEvent{tag: event_tag, value: value}`. The function's final
return value is delivered as `%Plushie.Event.AsyncEvent{tag: event_tag, result: result}`.

Only one task per tag can be active. If a task with the same tag is
already running, it is killed and replaced. Use unique tags if you
need concurrent streams.

This is sugar over spawning a process manually. You can achieve the same
thing with bare `Task` and `send/2` if you prefer direct Elixir patterns.

## Example

    Command.stream(fn emit ->
      for chunk <- File.stream!("big.csv") do
        emit.({:chunk, process(chunk)})
      end
      :done
    end, :file_import)

# `toggle_decorations`

```elixir
@spec toggle_decorations(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Toggle window decorations (title bar, borders).

# `toggle_maximize`

```elixir
@spec toggle_maximize(window_id :: window_id()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Toggle window maximized state.

# `tree_hash`

```elixir
@spec tree_hash(tag :: atom()) :: %Plushie.Command{payload: term(), type: term()}
```

Computes a SHA-256 hash of the renderer's current tree state.

The result arrives in `update/2` as
`%SystemEvent{type: :tree_hash, tag: tag, data: %{"hash" => "..."}}`.

# `update_image`

```elixir
@spec update_image(handle :: String.t(), data :: binary()) :: %Plushie.Command{
  payload: term(),
  type: term()
}
```

Updates an existing in-memory image with new encoded PNG/JPEG bytes.

The raw binary is stored as-is in the command payload. The protocol layer
handles format-specific encoding (native binary for msgpack, base64 for JSON).

# `update_image`

```elixir
@spec update_image(
  handle :: String.t(),
  width :: pos_integer(),
  height :: pos_integer(),
  pixels :: binary()
) :: %Plushie.Command{payload: term(), type: term()}
```

Updates an existing in-memory image with new raw RGBA pixel data.

The raw binary is stored as-is in the command payload. The protocol layer
handles format-specific encoding (native binary for msgpack, base64 for JSON).

# `widget_command`

```elixir
@spec widget_command(node_id :: String.t(), op :: String.t(), payload :: map()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Send a command to a native widget.

Widget commands bypass the normal tree update / diff / patch cycle and
are delivered directly to the target native widget on the Rust side.

# `widget_commands`

```elixir
@spec widget_commands(commands :: [{String.t(), String.t(), map()}]) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Send a batch of widget commands (processed in one cycle).

Each command in the list is a `{node_id, op, payload}` tuple.

---

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