# `Plushie.Command`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/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` (widget command), `focus_next/0`, `focus_previous/0` (widget ops)
- **Text**: `select_all/1`, `move_cursor_to_front/1`, `move_cursor_to_end/1`,
  `move_cursor_to/2`, `select_range/3` (see `Command.Text`)
- **Scroll**: `scroll_to/3`, `snap_to/3`, `snap_to_end/1`, `scroll_by/3`
  (see `Command.Scroll`)
- **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`, `focus_window/1`,
  `set_window_level/2`, `drag_window/1`, `drag_resize_window/2`,
  `request_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` (see `Command.Window`)
- **Window queries**: `window_size/2`, `window_position/2`,
  `is_maximized/2`, `is_minimized/2`, `window_mode/2`, `scale_factor/2`,
  `raw_id/2`, `monitor_size/2` (see `Command.WindowQuery`)
- **System ops**: `allow_automatic_tabbing/1`
- **System queries**: `system_theme/1`, `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_rgba/4`,
  `update_image/2`, `update_image_rgba/4`, `delete_image/1`,
  `list_images/1`, `clear_images/0` (see `Command.Image`)
- **Queries**: `tree_hash/1`, `find_focused/1`
- **Font**: `load_font/2`
- **Accessibility**: `announce/1`
- **Widget commands**: `widget_command/3`, `widget_batch/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**: `window_size/2`, `window_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 `value` holds the result payload.
  For example, `system_theme(:my_tag)` delivers
  `%SystemEvent{type: :system_theme, tag: "my_tag", value: "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.task(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.

# `__using__`
*macro* 

Standalone DSL for declaring typed command functions.

Use this in any module to generate command builder functions from
typed declarations. The same `command` macro used in native widgets
works here, producing functions that return `%Command{}` structs.

## Example

    defmodule Plushie.Command.Text do
      use Plushie.Command

      command :select_all
      command :move_cursor_to, value: :integer
      command :select_range, fields: [start_pos: :integer, end_pos: :integer]
    end

Generates:

    def select_all(widget_id) when is_binary(widget_id)
    def move_cursor_to(widget_id, value) when is_binary(widget_id) and is_integer(value)
    def select_range(widget_id, start_pos, end_pos) when is_binary(widget_id) and ...

# `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(), politeness :: :polite | :assertive) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Triggers a screen reader announcement without a visible widget.

The text is announced by assistive technology as a live-region
update. The default politeness is `:polite` which is the correct
choice for most toast-style feedback (saves, confirmations,
counts). Pass `:assertive` to interrupt the user's current
announcement for urgent context.

## Example

    Command.announce("File saved successfully")
    Command.announce("3 search results found", :polite)
    Command.announce("Connection lost", :assertive)

# `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`

# `close_window`

# `create_image`

# `create_image_rgba`

# `delete_image`

# `disable_mouse_passthrough`

# `dispatch`

```elixir
@spec dispatch(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.

The mapper receives only the value, not the model. Do not capture the
current model in the closure: it will be stale by the time the event
is processed. The mapper should produce an event struct that `update/2`
handles by reading the current model at that point.

# `drag_resize_window`

# `drag_window`

# `enable_mouse_passthrough`

# `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, value: %{"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 or canvas element identified by `widget_id`.

Supports scoped paths for canvas elements: `"canvas/element"`
focuses the element within the canvas. Supports window-qualified
paths: `"main#email"` or `"main#canvas/element"`.

# `focus_next`

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

Move focus to the next focusable widget.

# `focus_next_within`

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

Move focus to the next focusable widget within the subtree rooted at
`scope`. Only widgets that are descendants of the scope widget are
considered; focus wraps at the subtree boundary.

Useful for menus, pane grids, and any other keyboard container that
wants a bounded Tab cycle without leaking focus to siblings.

## Example

    Command.focus_next_within("main#menu")

# `focus_previous`

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

Move focus to the previous focusable widget.

# `focus_previous_within`

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

Move focus to the previous focusable widget within the subtree rooted
at `scope`. See `focus_next_within/1` for semantics.

# `focus_window`

# `is_maximized`

# `is_minimized`

# `list_images`

# `load_font`

```elixir
@spec load_font(family :: String.t(), 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 `family`
in widget `font` props.

## Example

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

# `maximize_window`

# `minimize_window`

# `monitor_size`

# `move_cursor_to`

# `move_cursor_to_end`

# `move_cursor_to_front`

# `move_window`

# `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 :: String.t()) ::
  %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 :: String.t()) ::
  %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 :: String.t(),
  axis :: atom() | String.t(),
  new_pane_id :: String.t()
) :: %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 :: String.t(),
  pane_b :: String.t()
) ::
  %Plushie.Command{payload: term(), type: term()}
```

Swap two panes in the pane grid.

# `raw_id`

# `request_attention`

# `resize_window`

# `scale_factor`

# `screenshot`

# `scroll_by`

# `scroll_to`

# `select_all`

# `select_range`

# `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`

# `set_max_size`

# `set_min_size`

# `set_resizable`

# `set_resize_increments`

# `set_window_level`

# `set_window_mode`

# `show_system_menu`

# `snap_to`

# `snap_to_end`

# `stream`

```elixir
@spec stream(fun :: ((term() -&gt; :ok) -&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)

# `system_info`

```elixir
@spec 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, value: 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.system_info(:sys_info)}
    end

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

# `system_theme`

```elixir
@spec 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, value: 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.system_theme(:theme_result)}
    end

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

# `task`

```elixir
@spec task(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.

# `toggle_decorations`

# `toggle_maximize`

# `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, value: %{"hash" => "..."}}`.

# `update_image`

# `update_image_rgba`

# `widget_batch`

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

Send a batch of widget commands processed atomically in one cycle.

Each command in the list is a `{id, family, value}` tuple. All commands
are applied before any resulting events are emitted.

# `widget_command`

```elixir
@spec widget_command(id :: String.t(), family :: String.t(), value :: term()) ::
  %Plushie.Command{
    payload: term(),
    type: term()
  }
```

Send a command to a widget by ID.

Commands use the unified wire format matching events:

    {"type": "command", "id": "gauge", "family": "set_value", "value": 72.0}

The `value` defaults to nil for commands with no payload (e.g. reset).
The `family` string identifies the operation. For native widgets, it
maps to the Rust widget's `handle_widget_op` dispatch. For built-in
widgets, the renderer handles it directly.

# `window_mode`

# `window_position`

# `window_size`

---

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