plushie 0.0.1 → 0.5.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.
- checksums.yaml +4 -4
- data/.yardopts +22 -0
- data/CHANGELOG.md +65 -0
- data/CONTRIBUTING.md +115 -0
- data/Gemfile +15 -0
- data/{LICENSE → LICENSE.txt} +6 -6
- data/README.md +297 -5
- data/Rakefile +21 -0
- data/Steepfile +8 -0
- data/docs/accessibility.md +489 -0
- data/docs/app-behaviour.md +614 -0
- data/docs/commands.md +882 -0
- data/docs/composition-patterns.md +1037 -0
- data/docs/dsl-internals.md +343 -0
- data/docs/effects.md +108 -0
- data/docs/events.md +653 -0
- data/docs/extensions.md +1791 -0
- data/docs/getting-started.md +237 -0
- data/docs/layout.md +266 -0
- data/docs/running.md +356 -0
- data/docs/scoped-ids.md +200 -0
- data/docs/testing.md +844 -0
- data/docs/theming.md +292 -0
- data/docs/tutorial.md +369 -0
- data/examples/README.md +163 -0
- data/examples/async_fetch.rb +71 -0
- data/examples/catalog.rb +448 -0
- data/examples/clock.rb +43 -0
- data/examples/color_picker.rb +146 -0
- data/examples/counter.rb +34 -0
- data/examples/notes.rb +219 -0
- data/examples/rate_plushie.rb +256 -0
- data/examples/shortcuts.rb +79 -0
- data/examples/todo.rb +102 -0
- data/examples/widgets/color_picker_widget.rb +133 -0
- data/examples/widgets/star_rating.rb +107 -0
- data/examples/widgets/theme_toggle.rb +95 -0
- data/lib/plushie/animation.rb +250 -0
- data/lib/plushie/app.rb +75 -0
- data/lib/plushie/binary.rb +282 -0
- data/lib/plushie/bridge.rb +163 -0
- data/lib/plushie/canvas/shape/canvas_image.rb +29 -0
- data/lib/plushie/canvas/shape/canvas_svg.rb +25 -0
- data/lib/plushie/canvas/shape/canvas_text.rb +31 -0
- data/lib/plushie/canvas/shape/circle.rb +31 -0
- data/lib/plushie/canvas/shape/clip.rb +22 -0
- data/lib/plushie/canvas/shape/dash.rb +26 -0
- data/lib/plushie/canvas/shape/drag_bounds.rb +31 -0
- data/lib/plushie/canvas/shape/group.rb +64 -0
- data/lib/plushie/canvas/shape/hit_rect.rb +25 -0
- data/lib/plushie/canvas/shape/line.rb +30 -0
- data/lib/plushie/canvas/shape/linear_gradient.rb +26 -0
- data/lib/plushie/canvas/shape/path.rb +31 -0
- data/lib/plushie/canvas/shape/rect.rb +31 -0
- data/lib/plushie/canvas/shape/shape_style.rb +30 -0
- data/lib/plushie/canvas/shape/stroke.rb +31 -0
- data/lib/plushie/canvas/shape/transform.rb +60 -0
- data/lib/plushie/canvas/shape.rb +166 -0
- data/lib/plushie/command.rb +448 -0
- data/lib/plushie/connection.rb +330 -0
- data/lib/plushie/data.rb +106 -0
- data/lib/plushie/dev_server.rb +86 -0
- data/lib/plushie/dsl/buildable.rb +48 -0
- data/lib/plushie/effects.rb +169 -0
- data/lib/plushie/encode.rb +51 -0
- data/lib/plushie/event.rb +322 -0
- data/lib/plushie/extension/build.rb +293 -0
- data/lib/plushie/extension.rb +339 -0
- data/lib/plushie/key_modifiers.rb +99 -0
- data/lib/plushie/model.rb +34 -0
- data/lib/plushie/node.rb +44 -0
- data/lib/plushie/protocol/decode.rb +638 -0
- data/lib/plushie/protocol/encode.rb +362 -0
- data/lib/plushie/protocol/keys.rb +295 -0
- data/lib/plushie/protocol/parsers.rb +66 -0
- data/lib/plushie/protocol.rb +23 -0
- data/lib/plushie/rake.rb +254 -0
- data/lib/plushie/renderer_env.rb +113 -0
- data/lib/plushie/route.rb +86 -0
- data/lib/plushie/runtime/commands.rb +216 -0
- data/lib/plushie/runtime/subscriptions.rb +134 -0
- data/lib/plushie/runtime.rb +303 -0
- data/lib/plushie/selection.rb +135 -0
- data/lib/plushie/state.rb +136 -0
- data/lib/plushie/subscription.rb +268 -0
- data/lib/plushie/test/case.rb +61 -0
- data/lib/plushie/test/event_decoder.rb +118 -0
- data/lib/plushie/test/helpers.rb +214 -0
- data/lib/plushie/test/rspec.rb +78 -0
- data/lib/plushie/test/script/runner.rb +109 -0
- data/lib/plushie/test/script.rb +129 -0
- data/lib/plushie/test/session.rb +480 -0
- data/lib/plushie/test/session_pool.rb +179 -0
- data/lib/plushie/test/snapshot.rb +99 -0
- data/lib/plushie/test.rb +73 -0
- data/lib/plushie/thread_pool.rb +83 -0
- data/lib/plushie/transport/framing.rb +73 -0
- data/lib/plushie/transport/tcp_adapter.rb +68 -0
- data/lib/plushie/tree.rb +292 -0
- data/lib/plushie/type/a11y.rb +87 -0
- data/lib/plushie/type/alignment.rb +29 -0
- data/lib/plushie/type/anchor.rb +22 -0
- data/lib/plushie/type/border.rb +89 -0
- data/lib/plushie/type/color.rb +175 -0
- data/lib/plushie/type/content_fit.rb +22 -0
- data/lib/plushie/type/direction.rb +23 -0
- data/lib/plushie/type/filter_method.rb +22 -0
- data/lib/plushie/type/font.rb +87 -0
- data/lib/plushie/type/gradient.rb +41 -0
- data/lib/plushie/type/length.rb +41 -0
- data/lib/plushie/type/padding.rb +91 -0
- data/lib/plushie/type/position.rb +22 -0
- data/lib/plushie/type/shadow.rb +79 -0
- data/lib/plushie/type/shaping.rb +22 -0
- data/lib/plushie/type/style_map.rb +116 -0
- data/lib/plushie/type/theme.rb +49 -0
- data/lib/plushie/type/wrapping.rb +22 -0
- data/lib/plushie/ui.rb +904 -0
- data/lib/plushie/undo.rb +174 -0
- data/lib/plushie/version.rb +9 -0
- data/lib/plushie/widget/build.rb +34 -0
- data/lib/plushie/widget/button.rb +61 -0
- data/lib/plushie/widget/canvas.rb +73 -0
- data/lib/plushie/widget/checkbox.rb +56 -0
- data/lib/plushie/widget/column.rb +52 -0
- data/lib/plushie/widget/combo_box.rb +69 -0
- data/lib/plushie/widget/container.rb +73 -0
- data/lib/plushie/widget/floating.rb +61 -0
- data/lib/plushie/widget/grid.rb +66 -0
- data/lib/plushie/widget/image.rb +64 -0
- data/lib/plushie/widget/keyed_column.rb +61 -0
- data/lib/plushie/widget/markdown.rb +61 -0
- data/lib/plushie/widget/mouse_area.rb +72 -0
- data/lib/plushie/widget/overlay.rb +65 -0
- data/lib/plushie/widget/pane_grid.rb +67 -0
- data/lib/plushie/widget/pick_list.rb +68 -0
- data/lib/plushie/widget/pin.rb +60 -0
- data/lib/plushie/widget/progress_bar.rb +60 -0
- data/lib/plushie/widget/qr_code.rb +58 -0
- data/lib/plushie/widget/radio.rb +67 -0
- data/lib/plushie/widget/responsive.rb +58 -0
- data/lib/plushie/widget/rich_text.rb +57 -0
- data/lib/plushie/widget/row.rb +52 -0
- data/lib/plushie/widget/rule.rb +50 -0
- data/lib/plushie/widget/scrollable.rb +73 -0
- data/lib/plushie/widget/sensor.rb +60 -0
- data/lib/plushie/widget/slider.rb +66 -0
- data/lib/plushie/widget/space.rb +48 -0
- data/lib/plushie/widget/stack.rb +60 -0
- data/lib/plushie/widget/svg.rb +61 -0
- data/lib/plushie/widget/table.rb +76 -0
- data/lib/plushie/widget/text.rb +55 -0
- data/lib/plushie/widget/text_editor.rb +68 -0
- data/lib/plushie/widget/text_input.rb +57 -0
- data/lib/plushie/widget/themer.rb +60 -0
- data/lib/plushie/widget/toggler.rb +63 -0
- data/lib/plushie/widget/tooltip.rb +67 -0
- data/lib/plushie/widget/vertical_slider.rb +65 -0
- data/lib/plushie/widget/window.rb +54 -0
- data/lib/plushie.rb +235 -5
- data/sig/plushie/animation.rbs +38 -0
- data/sig/plushie/app.rbs +29 -0
- data/sig/plushie/binary.rbs +21 -0
- data/sig/plushie/bridge.rbs +43 -0
- data/sig/plushie/canvas/shape.rbs +303 -0
- data/sig/plushie/command.rbs +111 -0
- data/sig/plushie/connection.rbs +63 -0
- data/sig/plushie/data.rbs +13 -0
- data/sig/plushie/dev_server.rbs +15 -0
- data/sig/plushie/dsl/buildable.rbs +13 -0
- data/sig/plushie/effects.rbs +31 -0
- data/sig/plushie/encode.rbs +5 -0
- data/sig/plushie/event.rbs +170 -0
- data/sig/plushie/extension.rbs +40 -0
- data/sig/plushie/key_modifiers.rbs +30 -0
- data/sig/plushie/model.rbs +11 -0
- data/sig/plushie/node.rbs +12 -0
- data/sig/plushie/protocol.rbs +82 -0
- data/sig/plushie/renderer_env.rbs +11 -0
- data/sig/plushie/route.rbs +19 -0
- data/sig/plushie/runtime.rbs +114 -0
- data/sig/plushie/selection.rbs +23 -0
- data/sig/plushie/state.rbs +23 -0
- data/sig/plushie/subscription.rbs +35 -0
- data/sig/plushie/thread_pool.rbs +19 -0
- data/sig/plushie/transport/framing.rbs +13 -0
- data/sig/plushie/tree.rbs +11 -0
- data/sig/plushie/ui.rbs +73 -0
- data/sig/plushie/undo.rbs +35 -0
- data/sig/plushie.rbs +13 -0
- metadata +244 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2f963b103814479cb494a52823c3c9ff0e5e07ba98bc306f5a1682204550869
|
|
4
|
+
data.tar.gz: 448aad5c90d158c3984bc6d8a2ebfa27f9bb6cdb2509b0bea643df35c11c39a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b91eca16110d4f22964cfc17515bc15ce4310119b61947a726f672ad655a9102a3bdc783fd959903546a7b74417188d59ee5c15b3eb7ed8f8ca4e3fb5ed876ac
|
|
7
|
+
data.tar.gz: ae52385d5fba80f69bf48c06618e848af49ec59c3b016cf61f3937543f6655b7a236d55e60e629ac2f1b15ff0f343af08108775f7ccedf3d3dbc6a8dbb164dde
|
data/.yardopts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
--markup markdown
|
|
2
|
+
--output-dir doc
|
|
3
|
+
--protected
|
|
4
|
+
--no-private
|
|
5
|
+
--hide-void-return
|
|
6
|
+
--embed-mixins
|
|
7
|
+
-
|
|
8
|
+
docs/getting-started.md
|
|
9
|
+
docs/tutorial.md
|
|
10
|
+
docs/app-behaviour.md
|
|
11
|
+
docs/layout.md
|
|
12
|
+
docs/events.md
|
|
13
|
+
docs/commands.md
|
|
14
|
+
docs/effects.md
|
|
15
|
+
docs/scoped-ids.md
|
|
16
|
+
docs/theming.md
|
|
17
|
+
docs/testing.md
|
|
18
|
+
docs/running.md
|
|
19
|
+
docs/composition-patterns.md
|
|
20
|
+
docs/accessibility.md
|
|
21
|
+
docs/extensions.md
|
|
22
|
+
docs/dsl-internals.md
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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.5.0] - 2026-03-23
|
|
8
|
+
|
|
9
|
+
Initial release. Targets plushie-renderer 0.5.0.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Elm architecture (init/update/view/subscribe) via `include Plushie::App`
|
|
14
|
+
- Immutable models via `Plushie::Model.define` (Data.define + #with)
|
|
15
|
+
- Block-based UI DSL with 39 widget types
|
|
16
|
+
- Canvas shape DSL with typed structs (Rect, Circle, Line, Text, Path, Group)
|
|
17
|
+
- Canvas Group with transforms array, clip field, and top-level
|
|
18
|
+
interactive properties (on_click, on_hover, focus_style, focusable, a11y)
|
|
19
|
+
- Canvas widget `role` and `arrow_mode` props for accessible containers
|
|
20
|
+
- Complete wire protocol encode/decode (MessagePack + JSONL)
|
|
21
|
+
- Tree diffing with incremental patch generation
|
|
22
|
+
- 72+ command constructors (async, focus, scroll, window ops, effects,
|
|
23
|
+
focus_element for canvas, etc.)
|
|
24
|
+
- Platform effects (file dialogs, clipboard, notifications)
|
|
25
|
+
- Subscription system (timers, keyboard, mouse, window events)
|
|
26
|
+
- Three transport modes: spawn, stdio, iostream
|
|
27
|
+
- Renderer lifecycle management with exponential backoff restart
|
|
28
|
+
- Error recovery: StandardError rescue in update/view with model
|
|
29
|
+
preservation and log throttling
|
|
30
|
+
- 18 property type modules with wire encoding
|
|
31
|
+
- State helpers: Animation, Route, Selection, Undo, DataQuery, State,
|
|
32
|
+
KeyModifiers
|
|
33
|
+
- Widget extension system (pure Ruby composites + native Rust-backed)
|
|
34
|
+
- Native Rust extension build pipeline via `rake plushie:build` --
|
|
35
|
+
generates Cargo workspace, validates crate paths and constructors,
|
|
36
|
+
detects type name and crate collisions, builds custom renderer binary
|
|
37
|
+
- `Plushie.configure` block for SDK-wide configuration: `binary_path`,
|
|
38
|
+
`source_path`, `build_name`, `extensions`, `extension_config`,
|
|
39
|
+
`test_backend`
|
|
40
|
+
- `extension_config` runtime configuration passed to Rust extensions
|
|
41
|
+
via the Settings wire message and `InitCtx`
|
|
42
|
+
- WASM renderer download via `rake plushie:download[wasm]`
|
|
43
|
+
- `PLUSHIE_BIN_FILE` and `PLUSHIE_WASM_DIR` env vars for overriding
|
|
44
|
+
download and build output paths
|
|
45
|
+
- `rake plushie:connect` task for stdio transport (plushie --exec)
|
|
46
|
+
- Token authentication for --exec and remote rendering
|
|
47
|
+
- `RendererEnv` to filter sensitive environment variables from renderer
|
|
48
|
+
subprocess
|
|
49
|
+
- Dev server with hot code reloading
|
|
50
|
+
- Test framework with three backends (mock, headless, windowed)
|
|
51
|
+
- Session pooling for parallel test execution
|
|
52
|
+
- Snapshot and screenshot assertion helpers
|
|
53
|
+
- .plushie script format parser and runner
|
|
54
|
+
- Minitest and RSpec integration
|
|
55
|
+
- 100% YARD documentation coverage with zero warnings
|
|
56
|
+
- RBS type signatures for all modules
|
|
57
|
+
- GitHub Actions CI workflow (Ruby 3.2 + 3.3 matrix)
|
|
58
|
+
- CONTRIBUTING.md with commit conventions and development guide
|
|
59
|
+
- Rake tasks: download, build, run, connect, inspect, script, replay,
|
|
60
|
+
preflight
|
|
61
|
+
- Binary download with SHA-256 checksum verification
|
|
62
|
+
- 9 examples: counter, clock, todo, async_fetch, notes, shortcuts,
|
|
63
|
+
color_picker, catalog, rate_plushie
|
|
64
|
+
- Extracted reusable canvas widgets: StarRating, ThemeToggle,
|
|
65
|
+
ColorPickerWidget (in examples/widgets/)
|
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
|
data/{LICENSE → LICENSE.txt}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
MIT License
|
|
1
|
+
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Daniel Hedlund
|
|
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
|
|
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
|
|
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,300 @@
|
|
|
1
|
-
#
|
|
1
|
+
# plushie
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build native desktop apps in Ruby. **Pre-1.0**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Plushie is a desktop GUI framework that allows you to write your entire
|
|
6
|
+
application in Ruby -- state, events, UI -- and get native windows
|
|
7
|
+
on Linux, macOS, and Windows. Rendering is powered by
|
|
8
|
+
[iced](https://github.com/iced-rs/iced), a cross-platform GUI library
|
|
9
|
+
for Rust, which plushie drives as a precompiled binary behind the scenes.
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
```ruby
|
|
12
|
+
class Counter
|
|
13
|
+
include Plushie::App
|
|
14
|
+
|
|
15
|
+
Model = Plushie::Model.define(:count)
|
|
16
|
+
|
|
17
|
+
def init(_opts) = Model.new(count: 0)
|
|
18
|
+
|
|
19
|
+
def update(model, event)
|
|
20
|
+
case event
|
|
21
|
+
in Event::Widget[type: :click, id: "inc"]
|
|
22
|
+
model.with(count: model.count + 1)
|
|
23
|
+
in Event::Widget[type: :click, id: "dec"]
|
|
24
|
+
model.with(count: model.count - 1)
|
|
25
|
+
else
|
|
26
|
+
model
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def view(model)
|
|
31
|
+
window("main", title: "Counter") do
|
|
32
|
+
column(padding: 16, spacing: 8) do
|
|
33
|
+
text("count", "Count: #{model.count}")
|
|
34
|
+
row(spacing: 8) do
|
|
35
|
+
button("inc", "+")
|
|
36
|
+
button("dec", "-")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Plushie.run(Counter)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is one of [8 examples](examples/) included in the repo, from a
|
|
47
|
+
minimal counter to a full widget catalog. For complete project demos,
|
|
48
|
+
including native Rust extensions, see the
|
|
49
|
+
[plushie-demos](https://github.com/plushie-ui/plushie-demos/tree/main/ruby)
|
|
50
|
+
repository.
|
|
51
|
+
|
|
52
|
+
## Getting started
|
|
53
|
+
|
|
54
|
+
Add plushie to your Gemfile:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
gem "plushie", "== 0.1.0"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Then:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bundle install
|
|
64
|
+
rake plushie:download # download precompiled renderer binary
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Requires Ruby 3.2+. The precompiled binary requires no Rust toolchain.
|
|
68
|
+
|
|
69
|
+
### Your first app
|
|
70
|
+
|
|
71
|
+
Create `lib/counter.rb`:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
require "plushie"
|
|
75
|
+
|
|
76
|
+
class Counter
|
|
77
|
+
include Plushie::App
|
|
78
|
+
|
|
79
|
+
Model = Plushie::Model.define(:count)
|
|
80
|
+
|
|
81
|
+
def init(_opts) = Model.new(count: 0)
|
|
82
|
+
|
|
83
|
+
def update(model, event)
|
|
84
|
+
case event
|
|
85
|
+
in Event::Widget[type: :click, id: "increment"]
|
|
86
|
+
model.with(count: model.count + 1)
|
|
87
|
+
in Event::Widget[type: :click, id: "decrement"]
|
|
88
|
+
model.with(count: model.count - 1)
|
|
89
|
+
else
|
|
90
|
+
model
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def view(model)
|
|
95
|
+
window("main", title: "Counter") do
|
|
96
|
+
column(padding: 16, spacing: 8) do
|
|
97
|
+
text("count", "Count: #{model.count}", size: 20)
|
|
98
|
+
row(spacing: 8) do
|
|
99
|
+
button("increment", "+")
|
|
100
|
+
button("decrement", "-")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
Plushie.run(Counter)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Run it:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
ruby lib/counter.rb
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## The Elm architecture
|
|
117
|
+
|
|
118
|
+
Plushie follows the Elm architecture. Your app implements four callbacks:
|
|
119
|
+
|
|
120
|
+
- **`init(opts)`** -- returns the initial model (any Ruby object, ideally
|
|
121
|
+
immutable via `Plushie::Model.define`).
|
|
122
|
+
- **`update(model, event)`** -- receives the current model and an event,
|
|
123
|
+
returns the new model. Pure function. Return `[model, command]` for
|
|
124
|
+
side effects.
|
|
125
|
+
- **`view(model)`** -- receives the model, returns a UI tree using the
|
|
126
|
+
block DSL. The runtime diffs trees and sends patches to the renderer.
|
|
127
|
+
- **`subscribe(model)`** (optional) -- returns active subscriptions
|
|
128
|
+
(timers, keyboard/mouse events).
|
|
129
|
+
|
|
130
|
+
## Features
|
|
131
|
+
|
|
132
|
+
- **39 built-in widget types** -- buttons, text inputs, sliders, tables,
|
|
133
|
+
markdown, canvas, pane grids, and more.
|
|
134
|
+
- **22 built-in themes** -- light, dark, dracula, nord, catppuccin,
|
|
135
|
+
tokyo night, kanagawa, and more.
|
|
136
|
+
- **Multi-window** -- declare window nodes in your widget tree; the
|
|
137
|
+
framework manages them automatically.
|
|
138
|
+
- **Platform effects** -- native file dialogs, clipboard, OS
|
|
139
|
+
notifications.
|
|
140
|
+
- **Accessibility** -- screen reader support via accesskit.
|
|
141
|
+
- **Live reload** -- `Plushie.run(MyApp, dev: true)` watches lib/ and
|
|
142
|
+
reloads on file changes. Model state is preserved.
|
|
143
|
+
- **Remote rendering** -- native desktop UI for server-side Ruby apps
|
|
144
|
+
over SSH. Your init/update/view code doesn't change.
|
|
145
|
+
- **Widget extensions** -- pure Ruby composites or native Rust-backed
|
|
146
|
+
custom widgets via `include Plushie::Extension`.
|
|
147
|
+
- **Configuration system** -- `Plushie.configure` for binary paths,
|
|
148
|
+
extensions, test backends, and extension runtime config.
|
|
149
|
+
- **WASM renderer** -- `rake plushie:download[wasm]` downloads a WASM
|
|
150
|
+
build of the renderer for browser targets.
|
|
151
|
+
|
|
152
|
+
## Documentation
|
|
153
|
+
|
|
154
|
+
**Guides:**
|
|
155
|
+
|
|
156
|
+
- [Getting started](docs/getting-started.md) -- setup, first app, rake tasks, dev mode
|
|
157
|
+
- [Tutorial: building a todo app](docs/tutorial.md) -- step-by-step walkthrough
|
|
158
|
+
- [App behaviour](docs/app-behaviour.md) -- init, update, view, subscribe callbacks
|
|
159
|
+
- [Layout](docs/layout.md) -- column, row, container, spacing, alignment
|
|
160
|
+
- [Events](docs/events.md) -- widget, keyboard, mouse, window, canvas events
|
|
161
|
+
- [Commands](docs/commands.md) -- async, timers, widget ops, effects, batching
|
|
162
|
+
- [Effects](docs/effects.md) -- file dialogs, clipboard, notifications
|
|
163
|
+
- [Scoped IDs](docs/scoped-ids.md) -- how container nesting scopes widget IDs
|
|
164
|
+
|
|
165
|
+
**Advanced:**
|
|
166
|
+
|
|
167
|
+
- [Running](docs/running.md) -- transports, remote rendering, WASM
|
|
168
|
+
- [Theming](docs/theming.md) -- built-in themes and custom styling
|
|
169
|
+
- [Testing](docs/testing.md) -- mock, headless, and windowed backends
|
|
170
|
+
- [Composition patterns](docs/composition-patterns.md) -- tabs, modals, forms, lists
|
|
171
|
+
- [Accessibility](docs/accessibility.md) -- screen reader support, roles, labels
|
|
172
|
+
- [Extensions](docs/extensions.md) -- Ruby composites and native Rust widgets
|
|
173
|
+
- [DSL internals](docs/dsl-internals.md) -- how the UI builder works under the hood
|
|
174
|
+
|
|
175
|
+
**API reference:** [rubydoc.info/gems/plushie](https://www.rubydoc.info/gems/plushie)
|
|
176
|
+
|
|
177
|
+
## Testing
|
|
178
|
+
|
|
179
|
+
All testing goes through the renderer binary. No Ruby-side mocks.
|
|
180
|
+
The mock backend runs at millisecond speed.
|
|
181
|
+
|
|
182
|
+
Add `require "plushie/test"` to your test helper:
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
# test/test_helper.rb
|
|
186
|
+
require "plushie"
|
|
187
|
+
require "plushie/test"
|
|
188
|
+
require "minitest/autorun"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Then write tests:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
class CounterTest < Plushie::Test::Case
|
|
195
|
+
app Counter
|
|
196
|
+
|
|
197
|
+
def test_clicking_increment_updates_counter
|
|
198
|
+
click("#increment")
|
|
199
|
+
assert_text "#count", "Count: 1"
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Three interchangeable backends:
|
|
205
|
+
|
|
206
|
+
- **Mock** (`PLUSHIE_TEST_BACKEND=mock`) -- millisecond tests, no display.
|
|
207
|
+
Default.
|
|
208
|
+
- **Headless** (`PLUSHIE_TEST_BACKEND=headless`) -- real rendering via
|
|
209
|
+
tiny-skia, no display server. Pixel screenshots.
|
|
210
|
+
- **Windowed** (`PLUSHIE_TEST_BACKEND=windowed`) -- real windows with GPU.
|
|
211
|
+
Needs display server (Xvfb in CI).
|
|
212
|
+
|
|
213
|
+
## How it works
|
|
214
|
+
|
|
215
|
+
Under the hood, a renderer built on iced handles window drawing and
|
|
216
|
+
platform integration. Your Ruby code sends widget trees to the
|
|
217
|
+
renderer over stdin; the renderer draws native windows and sends user
|
|
218
|
+
events back over stdout.
|
|
219
|
+
|
|
220
|
+
You don't need Rust to use plushie. The renderer is a precompiled
|
|
221
|
+
binary, similar to how your app talks to a database without you
|
|
222
|
+
writing C. If you need custom native rendering, the extension system
|
|
223
|
+
lets you write Rust for just those parts.
|
|
224
|
+
|
|
225
|
+
The same protocol works over a local pipe, an SSH connection, or any
|
|
226
|
+
bidirectional byte stream.
|
|
227
|
+
|
|
228
|
+
## State helpers
|
|
229
|
+
|
|
230
|
+
Plushie ships optional state management utilities:
|
|
231
|
+
|
|
232
|
+
- **`Plushie::Animation`** -- easing functions and interpolation
|
|
233
|
+
- **`Plushie::Route`** -- navigation stack for multi-view apps
|
|
234
|
+
- **`Plushie::Selection`** -- single, multi, and range selection
|
|
235
|
+
- **`Plushie::Undo`** -- undo/redo stacks with coalescing
|
|
236
|
+
- **`Plushie::DataQuery`** -- filter, search, sort, paginate collections
|
|
237
|
+
- **`Plushie::State`** -- path-based state with revision tracking
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
bundle exec rake # tests + linter + type check
|
|
243
|
+
bundle exec rake test # tests only
|
|
244
|
+
bundle exec rake standard # linter only
|
|
245
|
+
bundle exec rake steep # type check only
|
|
246
|
+
bundle exec rake yard # generate API docs to doc/
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development guide.
|
|
250
|
+
|
|
251
|
+
Rake tasks (add `require "plushie/rake"` to your Rakefile):
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
rake plushie:download # download precompiled binary
|
|
255
|
+
rake plushie:download[wasm] # download WASM renderer
|
|
256
|
+
rake plushie:build # build from Rust source (with extensions if configured)
|
|
257
|
+
rake plushie:run[Counter] # run an app
|
|
258
|
+
rake plushie:connect[Counter] # connect to renderer via stdio
|
|
259
|
+
rake plushie:inspect[Counter] # print UI tree as JSON
|
|
260
|
+
rake plushie:script # run .plushie test scripts
|
|
261
|
+
rake plushie:replay[path] # replay a script with real windows
|
|
262
|
+
rake plushie:preflight # run all CI checks
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Configure the SDK programmatically:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
Plushie.configure do |config|
|
|
269
|
+
config.binary_path = "/opt/plushie/bin/plushie"
|
|
270
|
+
config.extensions = [MyGauge]
|
|
271
|
+
config.test_backend = :headless
|
|
272
|
+
end
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## System requirements
|
|
276
|
+
|
|
277
|
+
The precompiled binary has no additional dependencies. To build from
|
|
278
|
+
source, install a Rust toolchain via [rustup](https://rustup.rs/) and
|
|
279
|
+
the platform-specific libraries:
|
|
280
|
+
|
|
281
|
+
- **Linux (Debian/Ubuntu):**
|
|
282
|
+
`sudo apt-get install libxkbcommon-dev libwayland-dev libx11-dev cmake fontconfig pkg-config`
|
|
283
|
+
- **Linux (Arch):**
|
|
284
|
+
`sudo pacman -S libxkbcommon wayland libx11 cmake fontconfig pkgconf`
|
|
285
|
+
- **macOS:** `xcode-select --install`
|
|
286
|
+
- **Windows:**
|
|
287
|
+
[Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
|
288
|
+
with "Desktop development with C++"
|
|
289
|
+
|
|
290
|
+
## Links
|
|
291
|
+
|
|
292
|
+
| | |
|
|
293
|
+
|---|---|
|
|
294
|
+
| Ruby SDK | [github.com/plushie-ui/plushie-ruby](https://github.com/plushie-ui/plushie-ruby) |
|
|
295
|
+
| Elixir SDK | [github.com/plushie-ui/plushie-elixir](https://github.com/plushie-ui/plushie-elixir) |
|
|
296
|
+
| Renderer | [github.com/plushie-ui/plushie](https://github.com/plushie-ui/plushie) |
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
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]
|