v0.7.0
[0.7.0] - 2026-05-08
This release introduces the Rust application SDK (plushie crate),
the widget SDK (plushie-widget-sdk), the cargo-plushie tool, and
moves the workspace to a crate-split layout. Everything below is
relative to 0.6.1.
Breaking changes
close_windowmoved fromwidget_optowindow_op. Hosts that
sentwidget_opwithop: "close_window"must switch to
window_opwithop: "close"and awindow_idfield. The renderer
no longer handles the old widget_op form.- Workspace restructured into per-role crates. The former single
renderer crate is split acrossplushie-core,plushie-renderer-engine,
plushie-renderer-lib,plushie-renderer(native binary),
plushie-renderer-wasm,plushie-widget-sdk, andplushie(app SDK).
Workspace crates now live undercrates/. The pure renderer state
engine (Core), retained tree, and wire codec live in
plushie-renderer-engine; widget authors do not depend on it. Propsrepresentation unified. ThePropsenum (Typed vs Wire
variants) collapses to a singlePropMap. SDK builders, prop
helpers, and widget renderers now operate on one representation.
Callers that matched onProps::TypedorProps::Wireneed to
migrate to the unified API.plushie-widget-sdkis optional. It is gated by thedirect
feature on theplushiecrate. Wire-only consumers get a slimmer
build. App authors using built-in widgets see no change; widget
authors must depend onplushie-widget-sdkexplicitly.PlushieRenderertrait is sealed. External implementations are
no longer permitted. All supported backends ship with the crate.- Scripting
interactpayload format. TypedKey,KeyPress,
MouseButton,InteractAction, andEffectKindreplace the
stringly-typed forms. Thecmd/commandmodifier is now resolved
to the platform-appropriate physical modifier by the renderer. - Canonical renderer env whitelist. The renderer subprocess inherits
only an explicit list of environment variables (names and prefixes).
Custom vars must match a documented prefix or be passed via a
supported hook. Seedocs/reference/wire-protocol.md. - Diagnostic wire format. Diagnostics emitted by the renderer use
the typedDiagnosticenum with structured variants instead of
free-form strings. Host SDKs that pattern-match diagnostic text
must migrate to the variant form. - Widget-command wire format unified. Per-widget command variants
(PaneGridOp, etc.) are replaced by a singlewidget_command
envelope with typed payloads. Extension authors that hand-built
command messages must move to the#[derive(WidgetCommand)]path. WidgetExtensiontrait removed. Replaced byPlushieWidget,
WidgetRegistry, and factory-based dispatch. The term "extension"
is retired throughout the API in favour of "widget".[patch.crates-io]replaced by.cargo/config.tomlpath
overrides for theplushie-icedfork during development. Consumer
projects vendoring the fork need to update their override mechanism.
Deprecated
plushie-widget-sdk::JsonProps. The type alias remains available
for compatibility, but widget authors should use&Propsdirectly.
Added
plushieRust application SDK. Elm-styleApptrait with
init/update/view/subscriptions, direct and wire runners,
typed commands, subscription lifecycle diffing, effect dispatch,
and composite-widget support.plushie-widget-sdkfor custom widget authors.PlushieWidget
trait,WidgetRegistry,CanvasEngine,#[derive(PlushieWidget)],
#[derive(WidgetCommand)],#[derive(WidgetEvent)],
#[derive(WidgetProps)],widget!function-like macro,
BUILTIN_TYPE_NAMES, widget-scoped subscriptions, typed config
helpers, panic-isolated entry points, and test helpers.cargo-plushiebuild tool.build(including--wasmvia
wasm-pack),download(renderer binaries),run(with--watch
hot-reload via cargo-watch),new-widget,init,doctor, and
wasmsubcommands. Reads[package.metadata.plushie]and
[package.metadata.plushie.widget]for project and widget
configuration.- Dev-mode hot-reload.
dev::watch_rendererplus the--watch
orchestrator incargo-plushie run. The wire runner swaps the
renderer subprocess on rebuild without losing session state. A
RebuildingOverlayis injected into the view tree with interactive
dismiss, auto-dismiss, and event interception. plushie::cli::run::<A>()easy-path entrypoint with the
--plushie-*reserved flag prefix for framework-owned options.plushie::automationmodule.TestSession(direct) and
WidgetTestSession(widget-scoped) for integration-style testing,
.plushiescript format (header + instructions),Selectorwith
tree search, typed assertions (AssertModel,run_with_model_debug,
resolved_a11ysurfacing of inferred a11y).automation::cli
primitivesscript,replay, andinspect. TheBackendenum
(Mock,Headless,Windowed) routes scripts through the
appropriate runner, including a real renderer subprocess for
Windowedreplay.run_connect+ renderer-spawned-us mode. The SDK can attach to
a renderer launched externally via thePLUSHIE_SOCKETenv var,
using a newSocketAdapterand splitBridgetransport
(Subprocess+Socket).- Four-step renderer binary discovery.
cargo-plushiepath,
PLUSHIE_RENDERERenv, workspacetarget/probe, andPATH
lookup, with an advisory architecture check. - Multi-window lifecycle sync in the wire runtime, at parity with
the Elixir SDK. - Wire bridge auto-restart with heartbeat watchdog. Transient
renderer failures no longer terminate the app. - Protocol version handshake. The SDK validates the renderer's
hellomessage protocol version and honours the negotiated codec. - Typed
Diagnosticenum inplushie-corewith structured
variants. Widget SDK and renderer-lib emit typed diagnostics at all
former log-warn sites. RENDERER_VERSIONconst exported fromplushie-renderer-lib,
deduplicated withPROTOCOL_VERSION.- Typed
Errorenum on theplushiecrate and a wire-renderer
exit hook so app code sees a typed failure surface. plushie-core-macrosproc-macro crate.PlushieEnumderive
(used for FontWeight, FontStyle, FontStretch, and many other
enums), wire-encode/decode generation, and thewidget!
function-like macro for built-in widget descriptions.- Typed core domain types.
Color(hex parsing with short-form
expansion),Angle(dual-storage, degrees on the wire),Padding
(per-side and axis constructors),Length,Theme/CustomTheme
with shade overrides,A11ywith merge semantics,PathCommand,
PointerKind,MouseButton(with Back/Forward),ArrowMode,
SortOrder,ErrorCorrection,ValueRange(renamed fromRange),
EventType,OutgoingMessage, andScopedId. - Composable
TreeTransformwalker in core. Normalize, widget
expansion, and animation scan now share a single walk. - Generic
Animatable<T>types and type-safe animation on
widget-builder setters. Angle-bearing APIs and Image/SVG rotation
flow throughAnglewith animation support. - Widget-scoped subscriptions.
SubscribeCtx, dispatch helpers,
and native renderer wiring let widget authors manage their own
subscription lifecycles. - Accessibility inference. Widget-level
infer_a11ymerges with
explicit host-suppliedA11y. SDK builders set a11y defaults on
the tree so tests can assert. Scoped refs rewrite automatically,
implicit radio groups are populated, and pick_list gains an
infer_a11yfallback. Amissing_accessible_namediagnostic
flags unlabelled widgets.Command::focus_next_withinand
focus_previous_withinscope keyboard navigation. - Automation backend dispatch.
.plushiescripts with
backend: windowedspawn a realplushie-renderersubprocess so
the run can be watched. [profile.dist]for shipping artifacts and Sigstore signing of
release binaries.- CI matrix expanded to macOS (including darwin-x86_64) and
Windows, with nightlycargo auditandcargo denyworkflows. - Workspace-level
[workspace.dependencies]pins shared crates
in one place. - Comprehensive rustdoc pass with
#![deny(missing_docs)]on
plushie,plushie-core, andplushie-widget-sdk, plus
Panics/Errors sections and workspace clippy lints. - docs.rs metadata on every crate; README badges, versioning
policy, and a pull-request template.
Changed
plushie::rununified across direct and wire modes; same API, mode
selected by features and environment.EffectHandlerreturnsFutureinstead of an icedTask.- Codec state is per-App (
EventSink,WriterSink) rather than
global;Codec::get_global()and the last of the global singletons
are removed. TestSessionergonomics pass: richer event-bridge polish,
subscription grouping, assertion helpers, multi-finger touch,
multi-window coverage, async delivery contract forErr/Cancel/
panic, and end-to-endrun_wireintegration.parking_lot::Mutexon hot paths (direct-mode event queue,
renderer sink).- Nextest CI profile tuned;
just test-examplesruns inline tests in
simple examples, added to preflight. - Canvas shape hashing is direct instead of going through
Debug
strings; per-event allocations trimmed on hot paths;
scope-string buffer threaded through normalize with a fast path
when widget expansion is a no-op. memo()actually memoizes at normalize time.- Msgpack depth pre-check and invalid UTF-8 rejection share a single
codec-boundary guard. - Protocol envelope unified:
_opmessages nested under a payload
envelope; effect-stub acks renamed to*_register_ack.
Fixed
- Widget-state invariant violations surface as panics (were silent).
Bridge::receivereuses a singleBufReaderacross calls.Propsequality treats null-valued keys as absent; tree-diff
round-trips null props correctly.- Cooperative
SendAfterdelay and async panic guard in the wire
runner. Length::wire_decoderejects off-canonical shapes.- Coalesce compatibility check includes the
Accumulatefield list. - Scripting cursor and scroll events normalize to
f32. - Pointer-area registration and derived
type_namescorrected in
the widget SDK. - Toggle inverts current state;
Displayshows the window; interact
processes all pending events. cargo-plushieoutput polish and rustdoc link hygiene.
Security
- Renderer subprocess env whitelist. Only an explicit set of
variables and prefixes cross the process boundary. - Canonical widget-id rules enforced in the Rust SDK.
- Tree-depth cap enforced centrally in the walker; duplicate-id
collection short-circuits past a sane cap. - Msgpack depth pre-check and UTF-8 validation at the codec
boundary. - SVG decode bounded by a pinned
usvgversion and a wall-clock
pre-parse guard. - Inline startup fonts counted against the process-wide font load
cap. - Unix socket creation hardened (
bind_unix), with a safer auto
socket directory. - WASM settings validated before wiring up the event sink.
- Pathological viewport dimensions rejected in
.plushie
automation files. - Oversized text prop content truncated at the renderer boundary.
- Numeric prop ranges emit a warning when out of sane bounds.
- Native writer channel has a backpressure timeout and
diagnostic. - TOCTOU and pass-through trust boundaries documented on platform
effects.
Full Changelog: v0.6.1...v0.7.0