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
endReturn 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. SeePlushie.Commandfor 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)}
endSee 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
endSee 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.
Summary
Callbacks
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.
Called once at startup. Returns the initial model, optionally with commands for loading initial data or configuring the UI.
Called once at startup to provide application-level settings to the renderer.
Called after every update. Returns the list of active subscriptions.
The runtime diffs this list and starts or stops subscriptions as needed.
Default: [].
Called on every event. Returns the next model, optionally with commands.
Called after every update. Returns a window node or a list of window nodes.
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.
Types
@type command() :: Plushie.Command.t() | [Plushie.Command.t()]
@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.
@type model() :: term()
@type window_view() :: Plushie.Widget.Window.t() | map() | [Plushie.Widget.Window.t() | map()] | nil
Callbacks
@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.
Called once at startup. Returns the initial model, optionally with commands for loading initial data or configuring the UI.
@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 pixelsfonts: list of font file paths to load at startup
Theming
theme: built-in theme atom (e.g.:dark,:nord),:systemto follow the OS light/dark preference, or a custom palette map. SeePlushie.Type.Theme.
Rendering
antialiasing: booleanvsync: boolean (defaulttrue). Synchronizes frame presentation with the display refresh rate. Disabling can improve performance on some platforms.scale_factor: number (default1.0). Multiplier on top of OS DPI scaling. Per-window overrides via thescale_factorwindow 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-subscriptionmax_rateand per-widgetevent_rateoverride this default.
Native widgets
widget_config: map of namespace -> config for native widgets. Each key matches a widget'snamespace()on the Rust side. The config is passed toPlushieWidget::init()viaInitCtx.config. Also configurable viaconfig :plushie, :widget_config.required_widgets: list of native widget type names this app requires the renderer to have registered. The renderer emits arequired_widgets_missingdiagnostic on the Settings handshake for any names it does not recognize (non-fatal; the app keeps running).
Default: %{} (renderer uses its own defaults).
@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: [].
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.
@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.
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}}
endDefault: empty config (the renderer uses its own defaults).