Plushie (Plushie v0.7.0)

Copy Markdown View Source

Native desktop GUIs from Elixir, powered by iced.

Quick start

{:ok, pid} = Plushie.start_link(MyApp)

Dev mode (live code reloading)

# In config/dev.exs:
config :plushie, code_reloader: true

# Or as a start_link option:
{:ok, pid} = Plushie.start_link(MyApp, code_reloader: true)

Under a supervisor

children = [
  {Plushie, app: MyApp}
]

Options

  • :app: (required) the app module implementing Plushie.App
  • :app_opts: opts forwarded to app.init/1 (default: [])
  • :binary: path to the plushie binary (default: auto-resolved)
  • :name: supervisor registration name (default: Plushie)
  • :daemon: if true, keep running after the last window closes
                  (default: `false`). In daemon mode, `all_windows_closed`
                  is delivered to `update/2` instead of triggering shutdown.
  • :code_reloader: enable dev-mode live reloading. false (default),
                  `true`, or a keyword list of reloader options
                  (`:debounce_ms`, `:rebuild_artifacts`). Can also be set
                  via `config :plushie, code_reloader: true`.
  • :transport: :spawn (default, spawns the renderer as a child
                  process), `:stdio` (reads/writes the BEAM's own
                  stdin/stdout, for use with `plushie --exec`), or
                  `{:iostream, pid}` (custom transport via iostream
                  adapter, see `Plushie.Bridge` for the protocol)
  • :format: wire format, :msgpack (default) or :json
  • :log_level: plushie binary log level (:off, :error, :warning, :info, :debug).
                  Default: `:error`.
  • :renderer_args: extra CLI args passed to the renderer process
  • :heartbeat_interval: maximum time (ms) between renderer messages
                  before the bridge considers it unresponsive and restarts
                  the renderer. `nil` disables the watchdog. Default: `30_000`.

When :transport is :stdio or {:iostream, pid}, the :binary option is ignored (no renderer subprocess is spawned).

Telemetry

Plushie emits :telemetry events for observability. Spans include both start and stop (or exception) suffixes automatically.

Spans

Spans are emitted via :telemetry.span/3. Each produces [:plushie, <name>, :start] and [:plushie, <name>, :stop] events (or :exception on failure).

  • [:plushie, :view] - calls app.view(model). Metadata: %{app: module()}.
  • [:plushie, :normalize] - normalizes the raw view tree into canonical wire format, including widget rendering and memo caching. Metadata: %{app: module()}.
  • [:plushie, :diff] - diffs old and new trees to produce patch operations. Metadata: %{app: module()}.
  • [:plushie, :update] - calls app.update(model, event). Metadata: %{app: module(), event: Plushie.Event.t()}.
  • [:plushie, :commands] - executes commands returned by update/2 or init/1. Metadata: %{count: non_neg_integer()}.
  • [:plushie, :subscriptions, :sync] - diffs and synchronizes active subscriptions. Metadata: %{}.

Single events

Single events are emitted via :telemetry.execute/3.

Runtime

  • [:plushie, :runtime, :view_error] - view/1 raised or threw. Measurements: %{count: 1}. Metadata: %{app: module()}.
  • [:plushie, :runtime, :update_error] - update/2 raised or threw. Measurements: %{count: 1}. Metadata: %{app: module(), event: term()}.
  • [:plushie, :runtime, :effect_timeout] - a pending effect request timed out. Measurements: %{count: 1}. Metadata: %{id: term()}.
  • [:plushie, :runtime, :ticks_drained] - coalesced multiple pending ticks into one cycle. Measurements: %{count: integer()}. Metadata: %{tag: atom()}.

Tree

  • [:plushie, :memo, :hit] - memo cache hit during normalization. Measurements: %{count: 1}. Metadata: %{id: String.t()}.
  • [:plushie, :memo, :miss] - memo cache miss during normalization. Measurements: %{count: 1}. Metadata: %{id: String.t()}.
  • [:plushie, :widget_cache, :hit] - widget view cache hit. Measurements: %{count: 1}. Metadata: %{id: String.t(), module: module()}.
  • [:plushie, :widget_cache, :miss] - widget view cache miss. Measurements: %{count: 1}. Metadata: %{id: String.t(), module: module()}.

Bridge

  • [:plushie, :bridge, :send] - frame sent to the renderer. Measurements: %{byte_size: non_neg_integer()}.
  • [:plushie, :bridge, :receive] - frame received from the renderer. Measurements: %{byte_size: non_neg_integer()}.
  • [:plushie, :bridge, :restart] - renderer process restarted. Measurements: %{count: pos_integer()} (cumulative restart count).
  • [:plushie, :bridge, :protocol_error] - failed to decode a renderer frame. Metadata: %{reason: term(), format: atom()}.
  • [:plushie, :bridge, :max_restarts_reached] - renderer exceeded the maximum restart limit. Metadata: %{reason: term(), max_restarts: integer()}.

Summary

Functions

Returns the registered name of the bridge for the given instance.

Child spec for embedding Plushie under an existing supervisor.

Returns the registered name of the runtime for the given instance.

Starts a Plushie application under a supervisor linked to the calling process.

Stops a running Plushie supervisor.

Types

transport()

@type transport() :: :spawn | :stdio | {:iostream, pid()}

Functions

bridge_for(name \\ __MODULE__)

@spec bridge_for(name :: atom()) :: atom()

Returns the registered name of the bridge for the given instance.

child_spec(init_arg)

@spec child_spec(keyword()) :: Supervisor.child_spec()

Child spec for embedding Plushie under an existing supervisor.

Example

children = [
  {Plushie, app: MyApp, name: :my_app_gui}
]

runtime_for(name \\ __MODULE__)

@spec runtime_for(name :: atom()) :: atom()

Returns the registered name of the runtime for the given instance.

start_link(app_module, opts \\ [])

@spec start_link(
  module(),
  keyword()
) :: Supervisor.on_start()

Starts a Plushie application under a supervisor linked to the calling process.

Returns {:ok, pid} on success.

stop(pid_or_name \\ __MODULE__)

@spec stop(pid() | atom()) :: :ok

Stops a running Plushie supervisor.

Accepts a pid or the instance name passed as :name to start_link/2 (defaults to Plushie, matching the default registration).