plushie/extension

Extension system for custom widget types.

Plushie supports two kinds of extensions:

Native widget extensions

A native widget is defined by creating an ExtensionDef and registering it. The widget’s Rust crate handles rendering and event emission; the Gleam side provides the typed builder API.

import plushie/extension
import plushie/node.{type Node}
import plushie/prop/color
import plushie/prop/length

// Define the extension
pub const gauge_def = extension.ExtensionDef(
  kind: "gauge",
  rust_crate: "native/my_gauge",
  rust_constructor: "my_gauge::GaugeExtension::new()",
  props: [
    extension.NumberProp("value"),
    extension.NumberProp("min"),
    extension.NumberProp("max"),
    extension.ColorProp("color"),
    extension.LengthProp("width"),
  ],
  commands: [
    extension.CommandDef("set_value", [extension.NumberParam("value")]),
  ],
)

// Build a gauge widget
pub fn gauge(id: String, value: Float, opts: List(GaugeOpt)) -> Node {
  extension.build(gauge_def, id, [
    #("value", node.FloatVal(value)),
    ..gauge_opts_to_props(opts)
  ])
}

// Send a command to a gauge
pub fn set_gauge_value(node_id: String, value: Float) -> Command(msg) {
  extension.command(gauge_def, node_id, "set_value", [
    #("value", node.FloatVal(value)),
  ])
}

Composite widget extensions

Composite widgets are simpler – they’re just functions that return Node trees. No registration or Rust code needed.

import plushie/node.{type Node}
import plushie/ui
import plushie/prop/padding

// A labeled input composite widget
pub fn labeled_input(
  id: String,
  label: String,
  value: String,
  children: List(Node),
) -> Node {
  ui.column(id, [ui.spacing(4)], [
    ui.text_(id <> "-label", label),
    ui.text_input(id <> "-input", value, []),
    ..children
  ])
}

Types

Command definition for an extension widget.

pub type CommandDef {
  CommandDef(name: String, params: List(ParamDef))
}

Constructors

  • CommandDef(name: String, params: List(ParamDef))

Definition of a native widget extension.

Describes the Rust crate, constructor, props, and commands that a native widget supports. Used at compile time to configure the plushie binary build and at runtime to construct nodes and commands.

pub type ExtensionDef {
  ExtensionDef(
    kind: String,
    rust_crate: String,
    rust_constructor: String,
    props: List(PropDef),
    commands: List(CommandDef),
  )
}

Constructors

  • ExtensionDef(
      kind: String,
      rust_crate: String,
      rust_constructor: String,
      props: List(PropDef),
      commands: List(CommandDef),
    )

    Arguments

    kind

    Widget kind string (e.g., “gauge”). Must match the Rust crate’s registered widget type name.

    rust_crate

    Path to the Rust crate relative to the project root (e.g., “native/my_gauge”).

    rust_constructor

    Rust expression to construct the extension instance (e.g., “my_gauge::GaugeExtension::new()”).

    props

    Declared properties with their types, for documentation and build tooling validation.

    commands

    Declared commands that can be sent to this widget type.

Parameter definition for extension commands.

pub type ParamDef {
  NumberParam(name: String)
  StringParam(name: String)
  BooleanParam(name: String)
}

Constructors

  • NumberParam(name: String)
  • StringParam(name: String)
  • BooleanParam(name: String)

Property definition for an extension widget.

pub type PropDef {
  NumberProp(name: String)
  StringProp(name: String)
  BooleanProp(name: String)
  ColorProp(name: String)
  LengthProp(name: String)
  PaddingProp(name: String)
  AlignmentProp(name: String)
  FontProp(name: String)
  StyleProp(name: String)
  MapProp(name: String)
  AnyProp(name: String)
  ListProp(name: String, inner: String)
}

Constructors

  • NumberProp(name: String)
  • StringProp(name: String)
  • BooleanProp(name: String)
  • ColorProp(name: String)
  • LengthProp(name: String)
  • PaddingProp(name: String)
  • AlignmentProp(name: String)
  • FontProp(name: String)
  • StyleProp(name: String)
  • MapProp(name: String)
  • AnyProp(name: String)
  • ListProp(name: String, inner: String)

Values

pub fn build(
  def: ExtensionDef,
  id: String,
  props: List(#(String, node.PropValue)),
) -> node.Node

Build a node for a native extension widget.

Creates a Node with the extension’s kind and the given props. Props are passed as key-value pairs already encoded to PropValue.

pub fn build_container(
  def: ExtensionDef,
  id: String,
  props: List(#(String, node.PropValue)),
  children: List(node.Node),
) -> node.Node

Build a container node for a native extension widget with children.

pub fn command(
  def: ExtensionDef,
  node_id: String,
  op: String,
  payload: List(#(String, node.PropValue)),
) -> command.Command(msg)

Create an extension command targeting a specific widget instance.

The command is sent via the wire protocol’s extension_command message type and delivered to the Rust widget by node ID.

pub fn command_names(def: ExtensionDef) -> List(String)

Get the command definition names from an extension definition.

pub fn commands(
  def: ExtensionDef,
  cmds: List(#(String, String, List(#(String, node.PropValue)))),
) -> command.Command(msg)

Create a batch of extension commands.

pub fn prop_names(def: ExtensionDef) -> List(String)

Get the prop definition names from an extension definition.

pub fn validate(def: ExtensionDef) -> Result(Nil, List(String))

Validate an extension definition at runtime.

Returns Ok(Nil) when valid, or Error(errors) with a list of human-readable validation failure messages.

Checks performed:

  • kind must be non-empty
  • No duplicate prop names
  • No reserved prop names (id, type, children, a11y)
Search Document