plushie/extension
Extension system for custom widget types.
Plushie supports two kinds of extensions:
-
Native widgets: backed by a Rust crate implementing the
WidgetExtensiontrait. The crate is compiled into the plushie binary, and the widget communicates via the standard wire protocol. -
Composite widgets: pure Gleam widgets that compose built-in widgets into reusable components. They produce standard Node trees and require no Rust code.
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
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:
kindmust be non-empty- No duplicate prop names
- No reserved prop names (id, type, children, a11y)