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

Behaviour for Plushie apps following the Elm architecture.

Implement this behaviour to define a desktop application. The runtime calls
`init/1` once at startup, then repeatedly calls `update/2` and `view/1` as
events arrive from the renderer and subscriptions.

## Minimal example

    defmodule MyApp do
      use Plushie.App

      alias Plushie.Event.WidgetEvent

      def init(_opts), do: %{count: 0}

      def update(model, %WidgetEvent{type: :click, id: "increment"}),
        do: %{model | count: model.count + 1}

      def update(model, _event), do: model

      def view(model) do
        import Plushie.UI

        window "counter", title: "Counter" do
          column do
            text("count", "Count: #{model.count}")
            button("increment", "Increment")
          end
        end
      end
    end

## Return values

`init/1` and `update/2` both accept two return shapes:

- Bare model: no side effects needed.
- `{model, command}` or `{model, [command]}`: schedule async work, widget
  operations, window management, or timers. See `Plushie.Command` for the
  full command API.

## Commands

Return a `{model, command}` tuple from `update/2` or `init/1` to schedule
side effects:

    def init(_opts) do
      model = %{data: nil, loading: true}
      {model, Command.task(fn -> load_initial_data() end, :loaded)}
    end

    def update(model, %WidgetEvent{type: :click, id: "save"}) do
      {model, Command.task(fn -> save_to_disk(model) end, :save_result)}
    end

See `Plushie.Command` for the full command API and result delivery.

## Subscriptions

Override `subscribe/1` to receive ongoing events. The runtime diffs the
subscription list after each update and starts or stops subscriptions as
needed.

    def subscribe(model) do
      subs = [Plushie.Subscription.on_key(:key_events)]

      if model.auto_refresh do
        [Plushie.Subscription.every(5000, :refresh) | subs]
      else
        subs
      end
    end

See `Plushie.Subscription` for all subscription types.

## Error handling

Exceptions in `update/2` and `view/1` are caught by the runtime and logged;
the model reverts to its pre-exception state. You do not need to wrap your
callbacks in try/rescue. After 100 consecutive errors, the runtime suppresses
further log messages to prevent log flooding.

## Optional callbacks

`subscribe/1`, `handle_renderer_exit/2`, `window_config/1`, and `settings/0`
are optional. `use Plushie.App` provides default implementations that can be
overridden.

# `command`

```elixir
@type command() :: Plushie.Command.t() | [Plushie.Command.t()]
```

# `event`

```elixir
@type event() :: Plushie.Event.t() | term()
```

Events delivered to `update/2`.

Most events are `Plushie.Event` structs from the renderer and
subscriptions. The `term()` case covers bare values delivered by
`Command.send_after/2` and `Command.dispatch/2`.

# `model`

```elixir
@type model() :: term()
```

# `window_view`

```elixir
@type window_view() ::
  Plushie.Widget.Window.t() | map() | [Plushie.Widget.Window.t() | map()] | nil
```

# `handle_renderer_exit`
*optional* 

```elixir
@callback handle_renderer_exit(model(), reason :: Plushie.RendererExitError.t()) ::
  model()
```

Called when the renderer process exits unexpectedly. Receives a
`%Plushie.RendererExitError{}` struct with `:type`, `:message`, and `:details`.
Return the model to use when the renderer restarts.
Default: return model unchanged.

# `init`

```elixir
@callback init(opts :: keyword()) :: model() | {model(), command()}
```

Called once at startup. Returns the initial model, optionally with
commands for loading initial data or configuring the UI.

# `settings`
*optional* 

```elixir
@callback settings() :: map()
```

Called once at startup to provide application-level settings to the
renderer.

## Typography

- `default_font`: font specification map (same format as font props)
- `default_text_size`: base text size in pixels
- `fonts`: list of font file paths to load at startup

## Theming

- `theme`: built-in theme atom (e.g. `:dark`, `:nord`), `:system`
  to follow the OS light/dark preference, or a custom palette map.
  See `Plushie.Type.Theme`.

## Rendering

- `antialiasing`: boolean
- `vsync`: boolean (default `true`). Synchronizes frame presentation
  with the display refresh rate. Disabling can improve performance on
  some platforms.
- `scale_factor`: number (default `1.0`). Multiplier on top of OS
  DPI scaling. Per-window overrides via the `scale_factor` window prop.

## Performance

- `default_event_rate`: integer, max events per second for coalescable
  event types (mouse moves, scroll, slider drags, etc.). Omit for
  unlimited. Set to 60 for most apps, lower for dashboards or remote
  rendering. Per-subscription `max_rate` and per-widget `event_rate`
  override this default.

## Native widgets

- `widget_config`: map of namespace -> config for native widgets.
  Each key matches a widget's `namespace()` on the Rust side. The
  config is passed to `PlushieWidget::init()` via `InitCtx.config`.
  Also configurable via `config :plushie, :widget_config`.
- `required_widgets`: list of native widget type names this app
  requires the renderer to have registered. The renderer emits a
  `required_widgets_missing` diagnostic on the Settings handshake
  for any names it does not recognize (non-fatal; the app keeps
  running).

Default: `%{}` (renderer uses its own defaults).

# `subscribe`
*optional* 

```elixir
@callback subscribe(model()) :: [Plushie.Subscription.t()]
```

Called after every update. Returns the list of active subscriptions.
The runtime diffs this list and starts or stops subscriptions as needed.
Default: `[]`.

# `update`

```elixir
@callback update(model(), event()) :: model() | {model(), command()}
```

Called on every event. Returns the next model, optionally with commands.

Events are `Plushie.Event` structs (widget interactions, key presses,
window lifecycle, async results, timer ticks, etc.) or bare terms from
`Command.send_after/2`. Include a catch-all clause for unhandled events.

# `view`

```elixir
@callback view(model()) :: window_view()
```

Called after every update. Returns a window node or a list of window nodes.

The top level must be explicit windows. Single-window apps return one
`window(...)` node. Multi-window apps return a list of `window(...)` nodes.
`nil` or `[]` is allowed when the app intentionally has no open windows.

# `window_config`
*optional* 

```elixir
@callback window_config(model()) :: map()
```

Called on runtime startup to configure window properties. Also called
on renderer restart whenever windows need re-opening. Per-window props
set in the view tree override these defaults.

## Supported keys

`title`, `size`, `width`, `height`, `position`, `min_size`, `max_size`,
`maximized`, `fullscreen`, `visible`, `resizable`, `closeable`,
`minimizable`, `decorations`, `transparent`, `blur`, `level`,
`exit_on_close_request`.

See `Plushie.Widget.Window` for details on each property.

## Example

    def window_config(_model) do
      %{title: "My App", width: 800, height: 600, min_size: {400, 300}}
    end

Default: empty config (the renderer uses its own defaults).

---

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