plushie 0.0.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +22 -0
  3. data/AGENTS.md +595 -0
  4. data/CHANGELOG.md +209 -0
  5. data/CLAUDE.md +1 -0
  6. data/CONTRIBUTING.md +115 -0
  7. data/Gemfile +15 -0
  8. data/{LICENSE → LICENSE.txt} +6 -6
  9. data/README.md +163 -5
  10. data/Rakefile +21 -0
  11. data/Steepfile +52 -0
  12. data/docs/README.md +56 -0
  13. data/docs/guides/01-introduction.md +266 -0
  14. data/docs/guides/02-getting-started.md +472 -0
  15. data/docs/guides/03-your-first-app.md +516 -0
  16. data/docs/guides/04-the-development-loop.md +499 -0
  17. data/docs/guides/05-events.md +548 -0
  18. data/docs/guides/06-lists-and-inputs.md +605 -0
  19. data/docs/guides/07-layout.md +530 -0
  20. data/docs/guides/08-styling.md +599 -0
  21. data/docs/guides/09-animation.md +516 -0
  22. data/docs/guides/10-subscriptions.md +470 -0
  23. data/docs/guides/11-async-and-effects.md +531 -0
  24. data/docs/guides/12-canvas.md +583 -0
  25. data/docs/guides/13-custom-widgets.md +705 -0
  26. data/docs/guides/14-state-management.md +600 -0
  27. data/docs/guides/15-testing.md +649 -0
  28. data/docs/guides/16-shared-state.md +567 -0
  29. data/docs/guides/17-packaging.md +549 -0
  30. data/docs/reference/accessibility.md +420 -0
  31. data/docs/reference/animation.md +482 -0
  32. data/docs/reference/app-lifecycle.md +504 -0
  33. data/docs/reference/built-in-widgets.md +702 -0
  34. data/docs/reference/canvas.md +589 -0
  35. data/docs/reference/commands.md +376 -0
  36. data/docs/reference/composition-patterns.md +816 -0
  37. data/docs/reference/configuration.md +302 -0
  38. data/docs/reference/custom-types.md +203 -0
  39. data/docs/reference/custom-widgets.md +681 -0
  40. data/docs/reference/dsl.md +500 -0
  41. data/docs/reference/events.md +596 -0
  42. data/docs/reference/native-extension.md +636 -0
  43. data/docs/reference/rake-tasks.md +440 -0
  44. data/docs/reference/scoped-ids.md +336 -0
  45. data/docs/reference/subscriptions.md +248 -0
  46. data/docs/reference/testing.md +448 -0
  47. data/docs/reference/themes-and-styling.md +511 -0
  48. data/docs/reference/versioning.md +174 -0
  49. data/docs/reference/windows-and-layout.md +692 -0
  50. data/docs/reference/wire-protocol.md +328 -0
  51. data/docs/stewardship/README.md +109 -0
  52. data/docs/stewardship/concurrency-shape.md +208 -0
  53. data/docs/stewardship/dsl-discipline.md +204 -0
  54. data/docs/stewardship/elm-invariants.md +196 -0
  55. data/docs/stewardship/goals-and-non-goals.md +95 -0
  56. data/docs/stewardship/performance-bar.md +157 -0
  57. data/docs/stewardship/posture.md +118 -0
  58. data/docs/stewardship/resilience.md +159 -0
  59. data/docs/stewardship/roadmap/README.md +20 -0
  60. data/docs/stewardship/simplicity.md +182 -0
  61. data/docs/stewardship/test-discipline.md +154 -0
  62. data/docs/stewardship/triage.md +153 -0
  63. data/docs/stewardship/trust-model.md +112 -0
  64. data/examples/README.md +163 -0
  65. data/examples/async_fetch.rb +71 -0
  66. data/examples/clock.rb +43 -0
  67. data/examples/color_picker.rb +96 -0
  68. data/examples/counter.rb +34 -0
  69. data/examples/notes.rb +219 -0
  70. data/examples/rate_plushie.rb +288 -0
  71. data/examples/shortcuts.rb +79 -0
  72. data/examples/todo.rb +102 -0
  73. data/examples/widgets/color_picker_widget.rb +360 -0
  74. data/examples/widgets/star_rating.rb +153 -0
  75. data/examples/widgets/theme_toggle.rb +141 -0
  76. data/lib/plushie/animation/sequence.rb +58 -0
  77. data/lib/plushie/animation/spring.rb +88 -0
  78. data/lib/plushie/animation/transition.rb +86 -0
  79. data/lib/plushie/animation/tween.rb +264 -0
  80. data/lib/plushie/animation.rb +30 -0
  81. data/lib/plushie/app.rb +82 -0
  82. data/lib/plushie/binary.rb +317 -0
  83. data/lib/plushie/bounded_queue.rb +32 -0
  84. data/lib/plushie/bridge.rb +277 -0
  85. data/lib/plushie/canvas/shape/canvas_image.rb +29 -0
  86. data/lib/plushie/canvas/shape/canvas_svg.rb +25 -0
  87. data/lib/plushie/canvas/shape/canvas_text.rb +31 -0
  88. data/lib/plushie/canvas/shape/circle.rb +31 -0
  89. data/lib/plushie/canvas/shape/clip.rb +22 -0
  90. data/lib/plushie/canvas/shape/dash.rb +26 -0
  91. data/lib/plushie/canvas/shape/drag_bounds.rb +31 -0
  92. data/lib/plushie/canvas/shape/group.rb +80 -0
  93. data/lib/plushie/canvas/shape/hit_rect.rb +25 -0
  94. data/lib/plushie/canvas/shape/line.rb +30 -0
  95. data/lib/plushie/canvas/shape/linear_gradient.rb +26 -0
  96. data/lib/plushie/canvas/shape/path.rb +31 -0
  97. data/lib/plushie/canvas/shape/rect.rb +36 -0
  98. data/lib/plushie/canvas/shape/shape_style.rb +30 -0
  99. data/lib/plushie/canvas/shape/stroke.rb +31 -0
  100. data/lib/plushie/canvas/shape/transform.rb +62 -0
  101. data/lib/plushie/canvas/shape.rb +183 -0
  102. data/lib/plushie/canvas_widget.rb +508 -0
  103. data/lib/plushie/cargo_plushie.rb +121 -0
  104. data/lib/plushie/command/image.rb +78 -0
  105. data/lib/plushie/command/scroll.rb +51 -0
  106. data/lib/plushie/command/text.rb +55 -0
  107. data/lib/plushie/command/window.rb +146 -0
  108. data/lib/plushie/command/window_query.rb +54 -0
  109. data/lib/plushie/command.rb +345 -0
  110. data/lib/plushie/connection.rb +383 -0
  111. data/lib/plushie/data.rb +106 -0
  112. data/lib/plushie/dev_server.rb +113 -0
  113. data/lib/plushie/dsl/buildable.rb +48 -0
  114. data/lib/plushie/effect.rb +170 -0
  115. data/lib/plushie/encode.rb +71 -0
  116. data/lib/plushie/event/diagnostic.rb +221 -0
  117. data/lib/plushie/event/specs.rb +211 -0
  118. data/lib/plushie/event.rb +413 -0
  119. data/lib/plushie/key_modifiers.rb +99 -0
  120. data/lib/plushie/model.rb +34 -0
  121. data/lib/plushie/node.rb +48 -0
  122. data/lib/plushie/protocol/decode.rb +1053 -0
  123. data/lib/plushie/protocol/encode.rb +460 -0
  124. data/lib/plushie/protocol/keys.rb +295 -0
  125. data/lib/plushie/protocol/parsers.rb +66 -0
  126. data/lib/plushie/protocol.rb +23 -0
  127. data/lib/plushie/rake.rb +261 -0
  128. data/lib/plushie/renderer_env.rb +122 -0
  129. data/lib/plushie/renderer_exit.rb +41 -0
  130. data/lib/plushie/route.rb +86 -0
  131. data/lib/plushie/runtime/commands.rb +327 -0
  132. data/lib/plushie/runtime/subscriptions.rb +151 -0
  133. data/lib/plushie/runtime/windows.rb +178 -0
  134. data/lib/plushie/runtime.rb +1306 -0
  135. data/lib/plushie/selection.rb +135 -0
  136. data/lib/plushie/state.rb +136 -0
  137. data/lib/plushie/subscription.rb +279 -0
  138. data/lib/plushie/test/case.rb +62 -0
  139. data/lib/plushie/test/helpers.rb +374 -0
  140. data/lib/plushie/test/rspec.rb +78 -0
  141. data/lib/plushie/test/script/runner.rb +109 -0
  142. data/lib/plushie/test/script.rb +129 -0
  143. data/lib/plushie/test/session.rb +694 -0
  144. data/lib/plushie/test/session_pool.rb +195 -0
  145. data/lib/plushie/test/snapshot.rb +117 -0
  146. data/lib/plushie/test.rb +72 -0
  147. data/lib/plushie/thread_pool.rb +83 -0
  148. data/lib/plushie/timer_scheduler.rb +103 -0
  149. data/lib/plushie/transport/framing.rb +112 -0
  150. data/lib/plushie/transport/tcp_adapter.rb +75 -0
  151. data/lib/plushie/tree/diff.rb +208 -0
  152. data/lib/plushie/tree/search.rb +98 -0
  153. data/lib/plushie/tree.rb +806 -0
  154. data/lib/plushie/type/a11y.rb +102 -0
  155. data/lib/plushie/type/alignment.rb +29 -0
  156. data/lib/plushie/type/anchor.rb +22 -0
  157. data/lib/plushie/type/border.rb +104 -0
  158. data/lib/plushie/type/color.rb +175 -0
  159. data/lib/plushie/type/content_fit.rb +22 -0
  160. data/lib/plushie/type/direction.rb +23 -0
  161. data/lib/plushie/type/filter_method.rb +22 -0
  162. data/lib/plushie/type/font.rb +83 -0
  163. data/lib/plushie/type/gradient.rb +78 -0
  164. data/lib/plushie/type/length.rb +41 -0
  165. data/lib/plushie/type/line_height.rb +42 -0
  166. data/lib/plushie/type/padding.rb +98 -0
  167. data/lib/plushie/type/position.rb +22 -0
  168. data/lib/plushie/type/shadow.rb +79 -0
  169. data/lib/plushie/type/shaping.rb +22 -0
  170. data/lib/plushie/type/style_map.rb +116 -0
  171. data/lib/plushie/type/theme.rb +120 -0
  172. data/lib/plushie/type/wrapping.rb +22 -0
  173. data/lib/plushie/ui.rb +970 -0
  174. data/lib/plushie/undo.rb +208 -0
  175. data/lib/plushie/version.rb +12 -0
  176. data/lib/plushie/widget/build.rb +95 -0
  177. data/lib/plushie/widget/button.rb +18 -0
  178. data/lib/plushie/widget/canvas.rb +43 -0
  179. data/lib/plushie/widget/checkbox.rb +37 -0
  180. data/lib/plushie/widget/column.rb +16 -0
  181. data/lib/plushie/widget/combo_box.rb +41 -0
  182. data/lib/plushie/widget/container.rb +35 -0
  183. data/lib/plushie/widget/floating.rb +24 -0
  184. data/lib/plushie/widget/grid.rb +28 -0
  185. data/lib/plushie/widget/image.rb +37 -0
  186. data/lib/plushie/widget/keyed_column.rb +24 -0
  187. data/lib/plushie/widget/markdown.rb +32 -0
  188. data/lib/plushie/widget/native_build.rb +333 -0
  189. data/lib/plushie/widget/overlay.rb +28 -0
  190. data/lib/plushie/widget/pane_grid.rb +30 -0
  191. data/lib/plushie/widget/pick_list.rb +40 -0
  192. data/lib/plushie/widget/pin.rb +23 -0
  193. data/lib/plushie/widget/pointer_area.rb +38 -0
  194. data/lib/plushie/widget/progress_bar.rb +29 -0
  195. data/lib/plushie/widget/qr_code.rb +29 -0
  196. data/lib/plushie/widget/radio.rb +36 -0
  197. data/lib/plushie/widget/responsive.rb +21 -0
  198. data/lib/plushie/widget/rich_text.rb +90 -0
  199. data/lib/plushie/widget/row.rb +16 -0
  200. data/lib/plushie/widget/rule.rb +24 -0
  201. data/lib/plushie/widget/scrollable.rb +34 -0
  202. data/lib/plushie/widget/sensor.rb +22 -0
  203. data/lib/plushie/widget/slider.rb +35 -0
  204. data/lib/plushie/widget/space.rb +20 -0
  205. data/lib/plushie/widget/stack.rb +23 -0
  206. data/lib/plushie/widget/svg.rb +32 -0
  207. data/lib/plushie/widget/table.rb +84 -0
  208. data/lib/plushie/widget/text.rb +18 -0
  209. data/lib/plushie/widget/text_editor.rb +42 -0
  210. data/lib/plushie/widget/text_input.rb +25 -0
  211. data/lib/plushie/widget/themer.rb +21 -0
  212. data/lib/plushie/widget/toggler.rb +35 -0
  213. data/lib/plushie/widget/tooltip.rb +29 -0
  214. data/lib/plushie/widget/vertical_slider.rb +33 -0
  215. data/lib/plushie/widget/window.rb +24 -0
  216. data/lib/plushie/widget.rb +760 -0
  217. data/lib/plushie/widget_set.rb +82 -0
  218. data/lib/plushie.rb +263 -5
  219. data/sig/base64.rbs +6 -0
  220. data/sig/msgpack.rbs +13 -0
  221. data/sig/plushie/animation.rbs +79 -0
  222. data/sig/plushie/app.rbs +29 -0
  223. data/sig/plushie/binary.rbs +21 -0
  224. data/sig/plushie/bounded_queue.rbs +13 -0
  225. data/sig/plushie/bridge.rbs +53 -0
  226. data/sig/plushie/canvas/shape.rbs +304 -0
  227. data/sig/plushie/canvas_widget.rbs +55 -0
  228. data/sig/plushie/cargo_plushie.rbs +11 -0
  229. data/sig/plushie/command/image.rbs +11 -0
  230. data/sig/plushie/command/scroll.rbs +10 -0
  231. data/sig/plushie/command/text.rbs +11 -0
  232. data/sig/plushie/command/window.rbs +29 -0
  233. data/sig/plushie/command/window_query.rbs +14 -0
  234. data/sig/plushie/command.rbs +101 -0
  235. data/sig/plushie/connection.rbs +63 -0
  236. data/sig/plushie/data.rbs +13 -0
  237. data/sig/plushie/dev_server.rbs +15 -0
  238. data/sig/plushie/dsl/buildable.rbs +13 -0
  239. data/sig/plushie/effect.rbs +31 -0
  240. data/sig/plushie/encode.rbs +10 -0
  241. data/sig/plushie/event.rbs +254 -0
  242. data/sig/plushie/key_modifiers.rbs +31 -0
  243. data/sig/plushie/model.rbs +11 -0
  244. data/sig/plushie/node.rbs +19 -0
  245. data/sig/plushie/protocol.rbs +134 -0
  246. data/sig/plushie/renderer_env.rbs +11 -0
  247. data/sig/plushie/route.rbs +19 -0
  248. data/sig/plushie/runtime/windows.rbs +32 -0
  249. data/sig/plushie/runtime.rbs +208 -0
  250. data/sig/plushie/selection.rbs +23 -0
  251. data/sig/plushie/state.rbs +23 -0
  252. data/sig/plushie/subscription.rbs +39 -0
  253. data/sig/plushie/thread_pool.rbs +19 -0
  254. data/sig/plushie/timer_scheduler.rbs +19 -0
  255. data/sig/plushie/transport/framing.rbs +13 -0
  256. data/sig/plushie/tree/diff.rbs +13 -0
  257. data/sig/plushie/tree/search.rbs +12 -0
  258. data/sig/plushie/tree.rbs +52 -0
  259. data/sig/plushie/type/a11y.rbs +44 -0
  260. data/sig/plushie/type/font.rbs +23 -0
  261. data/sig/plushie/type/gradient.rbs +9 -0
  262. data/sig/plushie/type/line_height.rbs +10 -0
  263. data/sig/plushie/ui.rbs +81 -0
  264. data/sig/plushie/undo.rbs +43 -0
  265. data/sig/plushie/widget/build.rbs +18 -0
  266. data/sig/plushie/widget_dsl.rbs +47 -0
  267. data/sig/plushie/widget_set.rbs +5 -0
  268. data/sig/plushie.rbs +29 -0
  269. metadata +322 -10
data/CHANGELOG.md ADDED
@@ -0,0 +1,209 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
+
7
+ ## [0.6.0] - 2026-05-09
8
+
9
+ Targets plushie-renderer 0.7.1.
10
+
11
+ ### Breaking changes
12
+
13
+ - Environment variable `PLUSHIE_SOURCE_PATH` renamed to
14
+ `PLUSHIE_RUST_SOURCE_PATH`. Update shell profiles and CI configs.
15
+ - Constant `Plushie::BINARY_VERSION` renamed to
16
+ `Plushie::PLUSHIE_RUST_VERSION` to match the plushie-rust release
17
+ identifier. Anything referencing the old name needs updating.
18
+ - `Command.async` renamed to `Command.task` for cross-SDK consistency
19
+ (`async` is reserved in Python and Rust). `Command.done` renamed to
20
+ `Command.dispatch` to reflect the "send through update" semantic.
21
+ `Command.widget_commands` renamed to `Command.widget_batch` to match
22
+ the Elixir SDK and disambiguate from the `Plushie::Command` type.
23
+ - Grid widget `columns` prop renamed to `num_columns`.
24
+ - `Command.Image.create_image` and `update_image` no longer accept
25
+ `pixels:`, `width:`, `height:` keyword arguments for raw RGBA data.
26
+ Use the new dedicated `create_image_rgba(handle, width, height, pixels)`
27
+ and `update_image_rgba(handle, width, height, pixels)` constructors
28
+ instead. The blob-data form of `create_image` and `update_image`
29
+ (passing raw binary data directly) is unchanged.
30
+ - Table widget `:selected` and `:striped` props removed; they were
31
+ dead code with no renderer-side effect.
32
+
33
+ ### Added
34
+
35
+ - `Plushie::CargoPlushie.resolve` helper that locates a usable
36
+ cargo-plushie via `PLUSHIE_RUST_SOURCE_PATH`, falling back to a
37
+ version-matched binary on `PATH`, otherwise raising with
38
+ `cargo install cargo-plushie --version <version> --locked`
39
+ guidance.
40
+ - `docs/versioning.md` documenting the `PLUSHIE_RUST_VERSION` pin and
41
+ SDK-vs-plushie-rust versioning rules.
42
+ - `Event::SessionError` and `Event::SessionClosed` typed variants.
43
+ `SessionError` includes a `code` field carrying the numeric error code
44
+ from the renderer.
45
+ - `Event::Diagnostic` typed variants: `crash`, `update_panicked`,
46
+ `renderer_error`, `protocol_error`, and `unknown`. Previously all
47
+ diagnostics arrived as the generic `Diagnostic` struct; each kind
48
+ now has its own Data class with kind-specific fields.
49
+ - `Plushie::BufferOverflowError` and `Plushie::ProtocolVersionMismatchError`
50
+ typed error classes raised on the matching renderer-side conditions.
51
+ - `Event::Effect` typed per-kind result variants. Effect callbacks now
52
+ receive a typed struct rather than a raw Hash.
53
+ - `RichText::Span` typed Data class for inline text spans.
54
+ - `link_click` events decoded as a typed `:link_click` variant on
55
+ `Event::Widget`.
56
+ - Touch release events carry a `lost` flag when the pointer left the
57
+ window before the release.
58
+ - `Rule` widget `thickness` prop as a direction-agnostic alternative to
59
+ the existing `width`/`height` split.
60
+ - SDK-side event coalescing: high-frequency events declared coalesable
61
+ are deduplicated in the bounded queue before reaching `update`.
62
+ - `Command.dispatch` chain depth is now capped; dispatching beyond the
63
+ limit raises rather than looping indefinitely.
64
+ - `Binary.resolve` falls back to a locally built renderer binary under
65
+ `../plushie-rust/target/{release,debug}/plushie-renderer` when
66
+ `PLUSHIE_RUST_SOURCE_PATH` is set and no explicit path is configured.
67
+ - `rake plushie:preflight` rebuilds the renderer binary from the local
68
+ plushie-rust checkout when `PLUSHIE_RUST_SOURCE_PATH` is set.
69
+ - Negative padding, border width, and border radius values are now
70
+ rejected at build time with an `ArgumentError`.
71
+
72
+ ### Fixed
73
+
74
+ - Subscription `key_press` and `key_release` events now read the
75
+ structured key payload (`key`, `modified_key`, `physical_key`,
76
+ `location`, `text`, `repeat`) from the `value` field. The previous
77
+ fallback to top-level message fields read modifiers from the wrong
78
+ location when value was non-Hash and would silently misread future
79
+ shape changes.
80
+ - `animation_frame` and `theme_changed` no longer carry dead fallback
81
+ reads alongside the canonical `value` access; the fallbacks could
82
+ never fire against the real renderer and masked the intended source
83
+ field.
84
+ - `ime_preedit` and `ime_commit` now raise `ArgumentError` when the
85
+ `value` payload is missing or non-Hash, surfacing wire-shape drift
86
+ instead of producing an `Event::Ime` with nil text and cursor.
87
+ - Concurrency bugs in the runtime, bridge, and session pool: a race in
88
+ the timer scheduler, a missing mutex on effect-kind state, and a
89
+ session-pool drain condition that could deadlock under session churn.
90
+ - Cancelled async threads are now properly released; previously a
91
+ cancelled task could hold its thread until the pool was torn down.
92
+ - Effect cancellation events are dispatched to `update` during SDK
93
+ shutdown so apps can clean up transient state.
94
+ - `image_list` and `image_clear` now route through the typed `image_op`
95
+ wire channel rather than a generic command envelope.
96
+ - `default_font` is always encoded as a `{ family: ... }` object; the
97
+ previous path emitted a bare string that the renderer rejected.
98
+ - `window_opened` position fields are now read from top-level `x`/`y`
99
+ keys as the protocol specifies; the previous path read from a nested
100
+ `position` map that does not exist.
101
+ - Effect tag supersession correctly clears the effect-kinds index when
102
+ a new registration replaces an existing one under the same tag.
103
+ - Unknown event families tolerate a missing `window_id` field instead
104
+ of raising a `KeyError`.
105
+ - Wayland-specific environment variables are now forwarded to the
106
+ renderer subprocess.
107
+ - `Plushie.configure { |c| c.log_level = ... }` is now honoured; the
108
+ previous path set the logger level only on the initial connection,
109
+ which was overwritten on restart.
110
+ - Renderer build lookup no longer raises on a missing `_build` directory;
111
+ it falls through to the next resolution step.
112
+ - Runtime event queue is bounded; a stalled app no longer causes
113
+ unbounded memory growth under high-frequency event sources.
114
+ - Widget state callbacks are no longer inherited across unrelated widget
115
+ subclasses defined with `Widget.define`.
116
+ - Effects are cancelled and their callbacks suppressed when the SDK
117
+ shuts down.
118
+ - Timeout error messages include the action and selector for easier
119
+ diagnosis in test output.
120
+ - Widget set override names are validated to prevent accidental
121
+ shadowing of built-in DSL methods.
122
+ - Renderer exit details are sanitised before appearing in error messages
123
+ and logs.
124
+
125
+ ### Changed
126
+
127
+ - Native widget builds now delegate workspace generation and
128
+ `cargo build` to [cargo-plushie](https://crates.io/crates/cargo-plushie).
129
+ The Ruby SDK writes a minimal virtual app manifest under
130
+ `_build/plushie-renderer-spec/` and shells out to
131
+ `cargo plushie build`. Widget discovery, [patch.crates-io] forwarding,
132
+ collision checks, and constructor validation now live in
133
+ cargo-plushie and are shared across host SDKs.
134
+ - Native widget crates must now declare
135
+ `[package.metadata.plushie.widget] { type_name, constructor }` in
136
+ their Cargo.toml. cargo-plushie uses that table for discovery.
137
+ - Renderer subprocess environment is now filtered to variables with a
138
+ `PLUSHIE_` prefix plus a small display-server allowlist. Previously
139
+ the full shell environment was forwarded.
140
+ - Per-timer threads replaced with a single `TimerScheduler` thread
141
+ using deadline-based `IO.select`. Timer count no longer scales the
142
+ thread count.
143
+ - Renderer restart backoff parameters, nil-view handling, and frozen
144
+ overlay semantics aligned with the Elixir and other SDKs.
145
+
146
+ ### Removed
147
+
148
+ - The checked-in `native/plushie/Cargo.lock` stash. cargo-plushie
149
+ manages the scratch workspace's lock file.
150
+
151
+ ## [0.5.0] - 2026-03-23
152
+
153
+ Initial release. Targets plushie-renderer 0.5.0.
154
+
155
+ ### Added
156
+
157
+ - Elm architecture (init/update/view/subscribe) via `include Plushie::App`
158
+ - Immutable models via `Plushie::Model.define` (Data.define + #with)
159
+ - Block-based UI DSL with 39 widget types
160
+ - Canvas shape DSL with typed structs (Rect, Circle, Line, Text, Path, Group)
161
+ - Canvas Group with transforms array, clip field, and top-level
162
+ interactive properties (on_click, on_hover, focus_style, focusable, a11y)
163
+ - Canvas widget `role` and `arrow_mode` props for accessible containers
164
+ - Complete wire protocol encode/decode (MessagePack + JSONL)
165
+ - Tree diffing with incremental patch generation
166
+ - 72+ command constructors (async, focus, scroll, window ops, effects,
167
+ focus_element for canvas, etc.)
168
+ - Platform effects (file dialogs, clipboard, notifications)
169
+ - Subscription system (timers, keyboard, mouse, window events)
170
+ - Three transport modes: spawn, stdio, iostream
171
+ - Renderer lifecycle management with exponential backoff restart
172
+ - Error recovery: StandardError rescue in update/view with model
173
+ preservation and log throttling
174
+ - 18 property type modules with wire encoding
175
+ - State helpers: Animation, Route, Selection, Undo, DataQuery, State,
176
+ KeyModifiers
177
+ - Widget extension system (pure Ruby composites + native Rust-backed)
178
+ - Native Rust extension build pipeline via `rake plushie:build` --
179
+ generates Cargo workspace, validates crate paths and constructors,
180
+ detects type name and crate collisions, builds custom renderer binary
181
+ - `Plushie.configure` block for SDK-wide configuration: `binary_path`,
182
+ `source_path`, `build_name`, `widgets`, `widget_config`,
183
+ `test_backend`
184
+ - `widget_config` runtime configuration passed to native widgets
185
+ via the Settings wire message and `InitCtx`
186
+ - WASM renderer download via `rake plushie:download[wasm]`
187
+ - `PLUSHIE_BIN_FILE` and `PLUSHIE_WASM_DIR` env vars for overriding
188
+ download and build output paths
189
+ - `rake plushie:connect` task for stdio transport (plushie --exec)
190
+ - Token authentication for --exec and remote rendering
191
+ - `RendererEnv` to filter sensitive environment variables from renderer
192
+ subprocess
193
+ - Dev server with hot code reloading
194
+ - Test framework with three backends (mock, headless, windowed)
195
+ - Session pooling for parallel test execution
196
+ - Snapshot and screenshot assertion helpers
197
+ - .plushie script format parser and runner
198
+ - Minitest and RSpec integration
199
+ - 100% YARD documentation coverage with zero warnings
200
+ - RBS type signatures for all modules
201
+ - GitHub Actions CI workflow (Ruby 3.2 + 3.3 + 4.0 matrix)
202
+ - CONTRIBUTING.md with commit conventions and development guide
203
+ - Rake tasks: download, build, run, connect, inspect, script, replay,
204
+ preflight
205
+ - Binary download with SHA-256 checksum verification
206
+ - 9 examples: counter, clock, todo, async_fetch, notes, shortcuts,
207
+ color_picker, catalog, rate_plushie
208
+ - Extracted reusable canvas widgets: StarRating, ThemeToggle,
209
+ ColorPickerWidget (in examples/widgets/)
data/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ AGENTS.md
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,115 @@
1
+ # Contributing to plushie-ruby
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/plushie-ui/plushie-ruby.git
7
+ cd plushie-ruby
8
+ bundle install
9
+ rake plushie:download # precompiled renderer binary
10
+ ```
11
+
12
+ Requires Ruby 3.2+. No Rust toolchain needed unless building from
13
+ source or writing native extensions.
14
+
15
+ ## Running checks
16
+
17
+ ```bash
18
+ bundle exec rake # tests + linter + type check
19
+ bundle exec rake test # tests only
20
+ bundle exec rake standard # linter only
21
+ bundle exec rake steep # type check only
22
+ bundle exec rake yard # generate API docs to doc/
23
+ rake plushie:preflight # pre-push gate (standard, test, steep, yard)
24
+ ```
25
+
26
+ ### Test backends
27
+
28
+ Tests run against the renderer binary. Three interchangeable backends:
29
+
30
+ ```bash
31
+ bundle exec rake test # mock (default, fastest)
32
+ PLUSHIE_TEST_BACKEND=headless bundle exec rake test # real rendering, no display
33
+ PLUSHIE_TEST_BACKEND=windowed bundle exec rake test # real windows (needs display)
34
+ ```
35
+
36
+ Mock is fast enough for TDD loops. CI runs both mock and headless.
37
+
38
+ ## Commits
39
+
40
+ Run `bundle exec rake` before committing. CI runs the same checks.
41
+
42
+ ### Message format
43
+
44
+ Use imperative mood. Describe what changed and why, not how.
45
+
46
+ ```
47
+ feat: add on_resize subscription for window resize events
48
+
49
+ The renderer already emits resize events but the SDK had no
50
+ subscription type for them. Apps had to poll window dimensions
51
+ on a timer, which was wasteful and laggy.
52
+ ```
53
+
54
+ Prefix with a category when it clarifies intent:
55
+
56
+ | Prefix | Use for |
57
+ |------------|--------------------------------------------|
58
+ | `feat:` | new user-facing functionality |
59
+ | `fix:` | bug fix |
60
+ | `docs:` | documentation only |
61
+ | `test:` | test additions or corrections |
62
+ | `refactor:`| restructuring without behaviour change |
63
+ | `chore:` | deps, CI, tooling, release prep |
64
+
65
+ # Pull requests
66
+
67
+ - One logical change per PR. If a refactor enables a feature, consider
68
+ splitting them unless the refactor is small and tightly coupled.
69
+ - PR title follows the same format as commit messages.
70
+ - Include a brief description of what and why. If there's a visual
71
+ change, a screenshot or before/after helps.
72
+ - All CI checks must pass.
73
+
74
+ ## Code style
75
+
76
+ [Standard](https://github.com/standardrb/standard) handles formatting
77
+ and linting. No configuration to argue about.
78
+
79
+ Beyond what Standard enforces:
80
+
81
+ - **Let the code speak.** Only comment when intent isn't obvious from
82
+ the code itself.
83
+ - **Prefer real implementations over mocks in tests.** The mock backend
84
+ is already fast; mocking Ruby internals hides real bugs.
85
+ - **Tests are documentation.** Write them so the next person
86
+ understands the behaviour, not just that it "passes".
87
+ - **ID is always the first argument** for widget builders.
88
+
89
+ ## Type signatures
90
+
91
+ RBS signatures live in `sig/`. When adding or changing public API
92
+ methods, update the corresponding `.rbs` file. Run `bundle exec rake
93
+ steep` to verify.
94
+
95
+ ## Documentation
96
+
97
+ Public API methods use YARD-style doc comments (`@param`, `@return`,
98
+ `@example`). Guides live in `docs/` as Markdown. Generate and browse
99
+ locally:
100
+
101
+ ```bash
102
+ bundle exec rake yard
103
+ open doc/index.html
104
+ ```
105
+
106
+ ## Architecture overview
107
+
108
+ See the [project layout and architecture](README.md#how-it-works) in
109
+ the README, or the detailed guide docs:
110
+
111
+ - [Getting started](docs/getting-started.md)
112
+ - [App behaviour](docs/app-behaviour.md)
113
+ - [Events](docs/events.md)
114
+ - [Commands](docs/commands.md)
115
+ - [Testing](docs/testing.md)
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "irb"
8
+ gem "rake", "~> 13.0"
9
+ gem "minitest", "~> 5.16"
10
+ gem "standard", "~> 1.3"
11
+ gem "steep", require: false
12
+ gem "yard", require: false
13
+
14
+ # Optional: hot reload in dev mode (Plushie.run(MyApp, dev: true))
15
+ gem "listen", "~> 3.0", require: false
@@ -1,6 +1,6 @@
1
- MIT License
1
+ The MIT License (MIT)
2
2
 
3
- Copyright (c) 2026 Daniel Hedlund <daniel@digitree.org>
3
+ Copyright (c) 2026 Daniel Hedlund
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,166 @@
1
- # Plushie for Ruby
1
+ # plushie
2
2
 
3
- Native GUI toolkit for Ruby, powered by native rendering via Iced.
3
+ Build native desktop apps in Ruby. **[Pre-1.0](#status)**
4
4
 
5
- This is a placeholder release to reserve the package name. The first
6
- functional release is coming soon.
5
+ Write your entire application in Ruby (state, events, UI) and get
6
+ native windows on Linux, macOS, and Windows. Available on
7
+ [RubyGems](https://rubygems.org/gems/plushie) as `plushie`. The
8
+ [renderer](https://github.com/plushie-ui/plushie-rust) is built on
9
+ [Iced](https://github.com/iced-rs/iced) and ships as a precompiled
10
+ binary, no Rust toolchain required.
7
11
 
8
- Source: <https://github.com/plushie-ui/plushie>
12
+ SDKs are also available for
13
+ [Elixir](https://github.com/plushie-ui/plushie-elixir),
14
+ [Gleam](https://github.com/plushie-ui/plushie-gleam),
15
+ [Python](https://github.com/plushie-ui/plushie-python), and
16
+ [TypeScript](https://github.com/plushie-ui/plushie-typescript).
17
+
18
+ ## Quick start
19
+
20
+ ```ruby
21
+ class Counter
22
+ include Plushie::App
23
+
24
+ Model = Plushie::Model.define(:count)
25
+
26
+ def init(_opts) = Model.new(count: 0)
27
+
28
+ def update(model, event)
29
+ case event
30
+ in Event::Widget[type: :click, id: "inc"]
31
+ model.with(count: model.count + 1)
32
+ in Event::Widget[type: :click, id: "dec"]
33
+ model.with(count: model.count - 1)
34
+ else
35
+ model
36
+ end
37
+ end
38
+
39
+ def view(model)
40
+ window("main", title: "Counter") do
41
+ column(padding: 16, spacing: 8) do
42
+ text("count", "Count: #{model.count}")
43
+ row(spacing: 8) do
44
+ button("inc", "+")
45
+ button("dec", "-")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ Plushie.run(Counter)
53
+ ```
54
+
55
+ Add plushie to your Gemfile and download the renderer:
56
+
57
+ ```bash
58
+ # Gemfile
59
+ gem "plushie", "== 0.1.0"
60
+ ```
61
+
62
+ ```bash
63
+ bundle install
64
+ rake plushie:download # download precompiled renderer binary
65
+ ruby lib/counter.rb
66
+ ```
67
+
68
+ Requires Ruby 3.2+. The repo includes
69
+ [several other examples](examples/) you can try. For multi-file
70
+ projects (custom widgets, native Rust extensions, real project
71
+ scaffolding), see the
72
+ [plushie-demos](https://github.com/plushie-ui/plushie-demos/tree/main/ruby)
73
+ repo.
74
+
75
+ To add Plushie to your own project, see the
76
+ [getting started guide](docs/getting-started.md), or browse the
77
+ [docs](docs/) for all guides and references.
78
+
79
+ ## How it works
80
+
81
+ Your Ruby application and the renderer run as two OS processes
82
+ that exchange messages. Think of it like talking to a database,
83
+ except the database is a GPU-accelerated GUI toolkit. The SDK builds
84
+ UI trees and handles events; the renderer draws native windows and
85
+ captures input.
86
+
87
+ The SDK diffs each new tree against the previous one and sends only
88
+ the changes. If the renderer crashes, Plushie restarts it and
89
+ re-syncs your state. If your code raises, the SDK reverts to the
90
+ last good state. Neither process can take the other down.
91
+
92
+ The same protocol works over a local pipe, an SSH connection, or
93
+ any bidirectional byte stream. Your code doesn't need to change.
94
+
95
+ ## Features
96
+
97
+ - **Elm architecture** - init, update, view. State lives in Ruby,
98
+ pure functions, predictable updates
99
+ - **Block DSL** - nested Ruby blocks build the widget tree with
100
+ natural indentation, no templates or markup
101
+ - **Built-in widgets** - layout, input, display, and interactive
102
+ widgets out of the box
103
+ - **Canvas** - shapes, paths, gradients, transforms, and
104
+ interactive elements for custom 2D drawing
105
+ - **Themes** - dark, light, nord, catppuccin, tokyo night, and
106
+ more, with custom palettes and per-widget style overrides
107
+ - **Animation** - renderer-side transitions, springs, and
108
+ sequences with no wire traffic per frame
109
+ - **Multi-window** - declare windows in your view; the framework
110
+ manages the rest
111
+ - **Platform effects** - native file dialogs, clipboard, OS
112
+ notifications
113
+ - **Accessibility** - keyboard navigation, screen readers, and
114
+ focus management via [AccessKit](https://accesskit.dev)
115
+ - **Custom widgets** - compose existing widgets in pure Ruby,
116
+ draw on the canvas, or extend with native Rust
117
+ - **Hot reload** - `Plushie.run(MyApp, dev: true)` watches lib/
118
+ and reloads on file changes with full state preservation
119
+ - **Remote rendering** - app on a server or embedded device,
120
+ renderer on a display machine over SSH or any byte stream
121
+ - **Fault-tolerant** - renderer crashes auto-recover; app
122
+ exceptions are caught and state reverted
123
+ - **Configuration system** - `Plushie.configure` for binary paths,
124
+ extensions, test backends, and widget runtime config
125
+ - **WASM renderer** - `rake plushie:download[wasm]` for browser
126
+ targets
127
+
128
+ ## Testing and automation
129
+
130
+ Tests run through the real renderer binary, not mocks. Interact like
131
+ a user: click, type, find elements, assert on text. All backends
132
+ support concurrent test execution to keep your suite fast as it
133
+ grows. Three interchangeable backends:
134
+
135
+ - **Mock** - millisecond tests, no display server
136
+ - **Headless** - real rendering via
137
+ [tiny-skia](https://github.com/linebender/tiny-skia), supports
138
+ screenshots for pixel regression in CI
139
+ - **Windowed** - real windows with GPU rendering, platform effects,
140
+ real input
141
+
142
+ ```ruby
143
+ class CounterTest < Plushie::Test::Case
144
+ app Counter
145
+
146
+ def test_clicking_increment_updates_counter
147
+ click("#inc")
148
+ assert_text "#count", "Count: 1"
149
+ end
150
+ end
151
+ ```
152
+
153
+ The same interaction API is available outside Minitest via
154
+ `Plushie::Automation::Session`. Attach to any running app and drive
155
+ it programmatically. Agent-friendly by design.
156
+
157
+ ## Status
158
+
159
+ Pre-1.0. The core works (built-in widgets, event system, themes,
160
+ multi-window, testing framework, accessibility) but the API is
161
+ still evolving. Pin to an exact version and read the
162
+ [CHANGELOG](CHANGELOG.md) when upgrading.
163
+
164
+ ## License
165
+
166
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "standard/rake"
9
+ require "plushie/rake"
10
+
11
+ desc "Run Steep type checker"
12
+ task :steep do
13
+ sh "bundle exec steep check"
14
+ end
15
+
16
+ desc "Generate YARD documentation"
17
+ task :yard do
18
+ sh "bundle exec yard doc"
19
+ end
20
+
21
+ task default: %i[test standard steep]
data/Steepfile ADDED
@@ -0,0 +1,52 @@
1
+ # Steepfile
2
+ #
3
+ # Files listed here are type-checked against their RBS declarations.
4
+ # Files with heavy metaprogramming (widget.rb define_method closures,
5
+ # Widget.define class_eval blocks) are excluded because Steep cannot
6
+ # analyze their dynamic method generation.
7
+ target :lib do
8
+ signature "sig"
9
+
10
+ # Core modules
11
+ check "lib/plushie/app.rb"
12
+ check "lib/plushie/encode.rb"
13
+ check "lib/plushie/event/specs.rb"
14
+ check "lib/plushie/key_modifiers.rb"
15
+ check "lib/plushie/route.rb"
16
+ check "lib/plushie/selection.rb"
17
+ check "lib/plushie/undo.rb"
18
+ check "lib/plushie/version.rb"
19
+
20
+ # Excluded from checking (RBS declarations retained for consumers):
21
+ #
22
+ # - model.rb: Extensions#with is defined inside a Data.define block;
23
+ # Steep resolves `self` to the enclosing module, not the Data class.
24
+ # - node.rb: Node = Data.define block; same self-resolution issue.
25
+ # - subscription.rb: Sub = Data.define block; same self-resolution issue.
26
+ # - event.rb: Widget/Key/Ime/Window/Modifiers/CommandError/System are
27
+ # all Data.define with block overrides; same self-resolution issue.
28
+
29
+ # Tree (normalization, search, diff)
30
+ check "lib/plushie/tree.rb"
31
+ check "lib/plushie/tree/search.rb"
32
+ check "lib/plushie/tree/diff.rb"
33
+
34
+ # Protocol (decode is the most type-critical)
35
+ check "lib/plushie/protocol.rb"
36
+ check "lib/plushie/protocol/decode.rb"
37
+
38
+ # Runtime
39
+ check "lib/plushie/runtime.rb"
40
+ check "lib/plushie/runtime/commands.rb"
41
+ check "lib/plushie/animation.rb"
42
+
43
+ # Widget build helpers
44
+ check "lib/plushie/widget/build.rb"
45
+
46
+ # Type modules
47
+ check "lib/plushie/type/line_height.rb"
48
+
49
+ library "json"
50
+ library "logger"
51
+ library "securerandom"
52
+ end
data/docs/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # Documentation
2
+
3
+ ## Guides
4
+
5
+ Sequential chapters that build on each other. Start here if you're
6
+ new to Plushie.
7
+
8
+ 1. [Introduction](guides/01-introduction.md) - what Plushie is and how it works
9
+ 2. [Getting Started](guides/02-getting-started.md) - installation, binary setup, first run
10
+ 3. [Your First App](guides/03-your-first-app.md) - building a counter with the Elm architecture
11
+ 4. [The Development Loop](guides/04-the-development-loop.md) - hot reload, IRB, debugging
12
+ 5. [Events](guides/05-events.md) - widget events, keyboard, pointer, pattern matching
13
+ 6. [Lists and Inputs](guides/06-lists-and-inputs.md) - dynamic lists, text inputs, forms
14
+ 7. [Layout](guides/07-layout.md) - rows, columns, containers, responsive sizing
15
+ 8. [Styling](guides/08-styling.md) - themes, colors, fonts, per-widget style overrides
16
+ 9. [Animation and Transitions](guides/09-animation.md) - transitions, springs, tweens, easing
17
+ 10. [Subscriptions](guides/10-subscriptions.md) - timers, global key/pointer events, window events
18
+ 11. [Async and Effects](guides/11-async-and-effects.md) - tasks, streams, platform effects
19
+ 12. [Canvas](guides/12-canvas.md) - shapes, layers, transforms, interactive elements
20
+ 13. [Custom Widgets](guides/13-custom-widgets.md) - composing widgets, canvas widgets, native Rust widgets
21
+ 14. [State Management](guides/14-state-management.md) - routing, undo/redo, selection, data pipelines
22
+ 15. [Testing](guides/15-testing.md) - test framework, backends, selectors, screenshots
23
+ 16. [Shared State](guides/16-shared-state.md) - multi-session apps over SSH
24
+ 17. [Packaging](guides/17-packaging.md) - publishing a gem and shipping a native renderer
25
+
26
+ ## Reference
27
+
28
+ Lookup material organized by topic. Each page is self-contained.
29
+
30
+ - [Accessibility](reference/accessibility.md) - AccessKit integration, roles, labels, keyboard navigation
31
+ - [Animation](reference/animation.md) - transitions, springs, sequences, easing curves, animatable props
32
+ - [App Lifecycle](reference/app-lifecycle.md) - init/update/view callbacks, startup, renderer restart
33
+ - [Built-in Widgets](reference/built-in-widgets.md) - every widget with props, events, and examples
34
+ - [Canvas](reference/canvas.md) - shapes, layers, groups, transforms, clips, gradients
35
+ - [Commands and Effects](reference/commands.md) - async, focus, scroll, window ops, platform effects
36
+ - [Composition Patterns](reference/composition-patterns.md) - reusable components, overlays, navigation, state-helper integration
37
+ - [Configuration](reference/configuration.md) - Plushie.configure, environment variables, runtime options
38
+ - [Custom Types](reference/custom-types.md) - shared prop-type catalog
39
+ - [Custom Widgets](reference/custom-widgets.md) - Widget.define, behavioral widgets, canvas widgets, native crates
40
+ - [DSL](reference/dsl.md) - block DSL mechanics, context stack, auto-IDs, memo caching
41
+ - [Events](reference/events.md) - every event class, widget event taxonomy, pattern-matching cookbook
42
+ - [Native Extensions](reference/native-extension.md) - authoring a Rust widget crate and shipping it alongside a Ruby gem
43
+ - [Rake Tasks](reference/rake-tasks.md) - plushie:download, plushie:build, plushie:run, preflight
44
+ - [Scoped IDs](reference/scoped-ids.md) - ID scoping rules, scope matching, command paths
45
+ - [Subscriptions](reference/subscriptions.md) - timer, keyboard, pointer, window, catch-all subscriptions
46
+ - [Testing](reference/testing.md) - Plushie::Test::Case, RSpec helpers, backends, snapshots, scripts
47
+ - [Themes and Styling](reference/themes-and-styling.md) - built-in themes, custom palettes, style maps, gradients
48
+ - [Versioning](reference/versioning.md) - SDK version, PLUSHIE_RUST_VERSION pinning, Ruby version floor
49
+ - [Windows and Layout](reference/windows-and-layout.md) - window props, layout containers, Length/Padding/Alignment/Border/Shadow
50
+ - [Wire Protocol](reference/wire-protocol.md) - MessagePack/JSONL framing, handshake, session multiplexing
51
+
52
+ ## Other resources
53
+
54
+ - [Examples](https://github.com/plushie-ui/plushie-ruby/tree/main/examples) - example apps included in the repo
55
+ - [Changelog](../CHANGELOG.md) - version history and migration notes
56
+ - [Demo apps](https://github.com/plushie-ui/plushie-demos/tree/main/ruby) - multi-file projects with custom widgets and real scaffolding