All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[0.6.0] - 2026-04-02
Breaking
Unified pointer events. 14 device/widget-specific event types replaced with 8 generic types:
:press,:release,:move,:scroll,:enter,:exit,:double_click,:resize. All carrypointertype (:mouse/:touch/:pen),modifiersstate, and optionalfingerID for touch. Removed types:canvas_press,canvas_release,canvas_move,canvas_scroll,mouse_right_press,mouse_right_release,mouse_middle_press,mouse_middle_release,mouse_move,mouse_scroll,mouse_enter,mouse_exit,mouse_double_click,sensor_resize.Canvas element events unified.
canvas_element_enter/leave/focused/blurred/drag/drag_end/key_press/key_releaseandcanvas_element_clickreplaced with standard types using scoped IDs. Canvas elements look like regular widgets from the SDK's perspective.MouseEventandTouchEventremoved. Subscription pointer events are now delivered asWidgetEventstructs withidset to the window ID andscopeof[].mouse_arearenamed topointer_area. The widget, DSL macro, and wire type all use the new name.Window ID in scope chain. Window IDs are appended to the end of the scope list. Pattern matching with
| _at the end of scope naturally ignores the window for single-window apps.:scrollvs:scrolled.:scrollis pointer wheel input (with coordinates and deltas).:scrolledis scrollable container viewport state change. Previously both used:scroll.:start/:endalignment aliases removed. Use:left/:right/:top/:bottom/:center.Subscription functions renamed.
on_mouse_move->on_pointer_move,on_mouse_button->on_pointer_button,on_mouse_scroll->on_pointer_scroll,on_touch->on_pointer_touch.Canvas auto-consumption removed. Canvas background pointer events now reach
update/2when opted in viaon_press/on_move/etc.Renderer binary version bumped to 0.6.0.
Added
Device awareness on pointer events.
Plushie.Type.Pointermodule withpointer_type(:mouse/:touch/:pen) andbuttontypes. Every pointer event includes pointer type, modifier state, and finger ID for touch.Window-qualified selector syntax.
"main#form/save"targets a widget in a specific window. Works in test selectors and commands (Command.focus,Command.scroll_to, etc.).Widget state re-render. When a widget's
handle_event/2returns{:update_state, new_state}, the view is immediately re-rendered. Previously required an unrelated event to trigger the re-render.Mock canvas element click.
click("#canvas-id/element-id")works in mock mode tests by detecting scoped IDs and verifying element existence.Mock sequential click fix. Sequential clicks on different widgets now work reliably in mock mode (synthetic event path replaces fragile focus+space approach).
Coalescing for pointer events.
:move(Replace),:scroll(Accumulate deltas),:scrolled(Replace),:resize(Replace).
Fixed
- Widget re-render on state change with window sync and error revert.
- Pre-existing decoder bugs:
transition_completemissing from specs and missing scope extraction,sortdata shape mismatch,pane_focus_cyclespec/decoder mismatch. plushie.buildpatches vendored iced subcrates when local source checkout exists. Handles file read errors in Cargo.toml parsing.
Changed
- Documentation overhaul. README rewritten,
docs/README.mdindex added for hexdocs,CONTRIBUTING.mdcreated. All em-dashes replaced with single dashes. Comprehensive docs for pointer events, device awareness, canvas touch, modifier patterns, window scope, and selector syntax.
[0.5.0] - 2026-03-23
Breaking
- Renderer binary renamed from
plushietoplushie-renderer. The binary resolution chain, download task, and build task all use the new name. Thebin/plushie-renderersymlink replaces the oldbin/plushie. - WASM files renamed from
plushie_wasm.*toplushie_renderer_wasm.*. Update any HTML script tags that reference the old names.
Added
--bin-file PATHand--wasm-dir PATHoptions onmix plushie.build, matching the existing options onmix plushie.download. Override output locations for both stock and extension builds.- Canvas group redesign -- groups now support
roleandarrow_modeprops for accessible tree/list patterns in canvas. - Block-form options on Canvas widget --
roleandarrow_modecan be set via the DSL block form. - Focus ring support --
focus_ring_radiuson canvas groups, focus ring padding on interactive canvas widgets. - Expanded test helpers -- additional utility functions for test sessions.
- Demo project links in docs -- extensions guide, testing guide, running guide, getting-started guide, and examples README all link to the plushie-demos repo.
Changed
- Renderer binary version bumped to 0.5.1.
- Download URL updated from
plushie-ui/plushietoplushie-ui/plushie-rendererreleases. - Rust crate references updated throughout for the
plushie-rendererworkspace split (plushie-ext,plushie-core,plushie-renderer-lib). - Canvas element terminology updated across docs and code.
Fixed
- Protocol dispatch warning in
Canvas.build/1. - Credo string literal warnings in Canvas doc comments.
- Star rating
focus_styleuses correct stroke object format. - Button style map removed from RatePlushie (uses default theme).
- Review form theme contrast in RatePlushie.
- Canvas scope walker wraps transform/clip directives as metadata.
- Heading text colors restored for contrast.
- Vestigial
interactivefield removed from leaf shape structs.
[0.4.0] - 2026-03-22
Breaking
- Project renamed from toddy to plushie. All module names (
Toddy.*->Plushie.*), config keys (:toddy->:plushie), environment variables (TODDY_*->PLUSHIE_*), and mix tasks (toddy.*->plushie.*) have changed. - Canvas shapes are now typed structs instead of plain maps. Builder
functions (
rect,circle,line,text,path,image,svg,stroke,linear_gradient) return struct instances (%Rect{},%Circle{}, etc.) withPlushie.Encodeprotocol implementations. Code that pattern-matched on%{type: "rect"}must use%Rect{}. import Plushie.Canvas.Shapeno longer needed forgroup,layer, orinteractivein canvas blocks. Useimport Plushie.UIinstead. Canvastext,image, andsvgcalls are automatically resolved inside canvas/layer/group blocks.- Test backend renamed:
:pooled_mock->:mock,Plushie.Test.Backend.Pooled->Plushie.Test.Backend.MockRenderer. The env var value changes:PLUSHIE_TEST_BACKEND=mock(waspooled_mock).Plushie.Test.MockBridgerenamed toPlushie.Test.InternalMockBridge(@moduledoc false). Padding.encode/1renamed toPadding.cast/1(normalization, not wire encoding).
Added
- Block-form options for all widgets. Every leaf widget and canvas
shape supports a do-block syntax for declaring options:
button "save", "Save" do style :primary padding %{top: 10, bottom: 10} end - Container inline props. Container widgets (column, row, container,
etc.) accept option declarations directly in their do-blocks, mixed
with children:
column do spacing 8 padding 16 text("Hello") end - Nested do-blocks for struct-typed options. Options like
padding,a11y,border,shadow, andstylesupport nested do-blocks that construct typed structs:container "card" do border do width 1 color "#ddd" rounded 8 end shadow do color "#0000001a" offset_y 2 blur_radius 8 end text("Content") end interactivedirective with id-first syntax, keyword form, block form, and pipe form for canvas shape interactivity.Plushie.DSL.Buildablebehaviour -- formal contract for types participating in the DSL block-form pattern (from_opts/1,__field_keys__/0,__field_types__/0).- Compile-time validation everywhere. All widget block forms validate option keys at compile time. Using an option that doesn't belong to the current widget produces a helpful error. Canvas blocks validate every call against its context (canvas/layer/group).
- Context-aware
canvas_scopewalker validates and rewrites calls inside canvas blocks. Wrong-aritytext/image/svgcalls, widget macros, and misplaced shapes produce compile-time errors. - Context-aware
container_scopewalker validates container options. Using an option on the wrong container lists which containers support it. - New value structs --
ShapeStyle(hover/pressed overrides),DragBounds,HitRect,Dash, plusPaddingandFontconverted from utility types to proper structs. - Extension DSL integration -- extension widgets automatically
generate
Buildablecallbacks and option metadata frompropdeclarations. - Tree normalizer leak detection -- shape structs and DSL metadata tuples in the widget tree produce clear error messages.
- Event coalescing --
max_rateon subscriptions,event_rateon widgets, host-side pending coalesce buffer for mouse moves and sensor resizes. - Three transport modes --
:spawn(default),:stdio(forplushie --exec), and{:iostream, pid}(for SSH/TCP/custom). - Canvas interactive shapes -- renderer-side hit testing with click,
hover, drag, focus events via the
interactivefield on shapes. docs/dsl-internals.md-- maintainer guide for the DSL architecture, Buildable behaviour, and scope walkers.--wasmflag formix plushie.downloadandmix plushie.build.bin/plushiesymlink created bymix plushie.downloadfor stable path references without the platform-specific name.mix plushie.connectreplacesmix plushie.stdio. Connects to the renderer via Unix socket or TCP instead of stdin/stdout. Token auth via Settings message.- Doc-sync tests linking doc code blocks to test functions via HTML comment markers.
Changed
Downloaded binaries moved from
priv/bin/to_build/plushie/bin/. Build artifacts belong in_build/wheremix cleanremoves them.mix plushie.stdiorenamed tomix plushie.connect. The old stdin/stdout transport is still available as a fallback whenPLUSHIE_SOCKETis not set.Plushie.UIis now the single macro/DSL layer. All shape macros (rect,circle,group,layer, etc.), path commands, transforms, clips, and gradients are available viaimport Plushie.UI.Plushie.Canvas.Shapeis now a pure-function module (no macros). Import it directly only for helper functions outside canvas blocks.All
Encodeprotocol implementations moved to their respective struct module files.Plushie.Encodecontains only the protocol definition and primitive implementations.Widget struct field types tightened to reference specific type modules (e.g.,
Plushie.Type.Padding.t()instead ofterm()).Canvas widget type annotations use
canvas_shape()union type.@widget_callsderived from component lists instead of manually maintained.Doc code examples use
use Plushie.Appinstead of@behaviour Plushie.App(the latter misses default implementations of optional callbacks likewindow_config/1).
[0.3.0] - 2026-03-19
Initial public release.
Added
- Elm architecture --
init/1,update/2,view/1, optionalsubscribe/1callbacks via thePlushie.Appbehaviour. - 38 built-in widget types -- layout (column, row, container, scrollable, stack, grid, pane_grid), display (text, rich_text, markdown, image, svg, progress_bar, qr_code, rule, canvas), input (button, text_input, text_editor, checkbox, radio, toggler, slider, vertical_slider, pick_list, combo_box, table), and wrappers (tooltip, pointer_area, sensor, overlay, responsive, themer, keyed_column, space, floating, pin, window).
- 22 built-in themes -- light, dark, dracula, nord, solarized,
gruvbox, catppuccin, tokyo night, kanagawa, moonfly, nightfly,
oxocarbon, ferra. Custom palettes and per-widget style overrides
via
Plushie.Type.StyleMap. - Multi-window -- declare window nodes in the widget tree; the framework manages open/close/update automatically.
- Platform effects -- native file dialogs, clipboard (text, HTML, primary selection), OS notifications.
- Accessibility -- screen reader support via accesskit on all platforms. A11y props on all widgets.
- Commands -- async work, streaming, timers, widget ops (focus, scroll, select), window management (25+ operations), image management, platform effects, extension commands.
- Subscriptions -- timers, keyboard, mouse, touch, IME, window lifecycle, animation frames, system theme changes.
- 16 typed event structs -- Widget, Key, Mouse, Touch, Ime, Window, Canvas, PointerArea, Pane, Sensor, Effect, System, Timer, Async, Stream, Modifiers.
- Scoped widget IDs -- containers namespace children's IDs automatically. Pattern match on local ID or scope chain.
- Three-backend test framework -- mocked (fast, no display), headless (real rendering via tiny-skia, screenshots), windowed (real GPU windows). Same API across all three.
- Extension system -- pure Elixir composite widgets or Rust-backed
native widgets via
Plushie.Extensionmacro DSL. - Live reload -- file watching in dev mode, enabled by default
via
mix plushie.gui. State preserved across reloads. - Daemon mode --
Plushie.start_link(MyApp, daemon: true)keeps the process running after the last window closes. - Precompiled binaries --
mix plushie.downloadfetches platform-specific binaries with mandatory SHA256 verification. - Build from source --
mix plushie.buildcompiles the plushie binary, with optional extension workspace generation. - State helpers --
Plushie.State(revision tracking),Plushie.Undo(undo/redo),Plushie.Selection(single/multi/range),Plushie.Route(navigation),Plushie.Data(query pipeline),Plushie.Animation(easing functions). - Canvas drawing -- shape primitives (rect, circle, arc, path, text, image) with layers, gradients, opacity, and caching.
- 8 example apps -- Counter, Todo, Notes, Clock, Shortcuts, AsyncFetch, ColorPicker, Catalog.