zui 0.0.6 → 0.0.7
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/ControlNode.qml +2 -909
- data/Desktop.qml +3 -0
- data/README.md +383 -66
- data/Service.qml +14 -2
- data/lib/zui/cli.rb +34 -2
- data/lib/zui/dist_config.rb +180 -0
- data/lib/zui/dist_packager.rb +361 -0
- data/lib/zui/distribution.rb +74 -12
- data/lib/zui/generator.rb +38 -0
- data/lib/zui/tree_shaker.rb +527 -0
- data/lib/zui.rb +4 -1
- metadata +4 -1
data/Desktop.qml
CHANGED
|
@@ -64,11 +64,14 @@ QQC.ApplicationWindow {
|
|
|
64
64
|
}
|
|
65
65
|
Text {
|
|
66
66
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
67
|
+
width: Math.max(0, Math.min(680, window.width - 48))
|
|
67
68
|
text: service.lastError !== "" ? service.lastError
|
|
68
69
|
: (Fonts.failed ? "Zui could not load its bundled fonts" : "Starting Zui…")
|
|
69
70
|
color: Color.foreground
|
|
70
71
|
font.family: Style.font.family
|
|
71
72
|
font.pixelSize: Style.font.body
|
|
73
|
+
horizontalAlignment: Text.AlignHCenter
|
|
74
|
+
wrapMode: Text.Wrap
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
|
data/README.md
CHANGED
|
@@ -1,24 +1,80 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/readme-hero.svg" width="100%" alt="Zui — native desktop applications in pure Ruby">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
events, bindings, animation, media, GPU effects, 3D, and application logic. Applications are
|
|
5
|
-
Ruby and assets—there is no application UI language to learn alongside Ruby.
|
|
5
|
+
<h1 align="center">Native desktop applications in pure Ruby</h1>
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
Build reactive Linux, macOS, and Windows interfaces with one Ruby API, a native Qt renderer,
|
|
9
|
+
and no application-owned QML or browser runtime.
|
|
10
|
+
</p>
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://rubygems.org/gems/zui"><img alt="RubyGem" src="https://img.shields.io/gem/v/zui?style=flat-square&color=9cff57&labelColor=111711"></a>
|
|
14
|
+
<a href="https://github.com/AdamMusa/zui/actions/workflows/platforms.yml"><img alt="Native platforms" src="https://github.com/AdamMusa/zui/actions/workflows/platforms.yml/badge.svg"></a>
|
|
15
|
+
<img alt="Ruby 3.1 or newer" src="https://img.shields.io/badge/Ruby-%E2%89%A5%203.1-cc342d?style=flat-square&labelColor=111711">
|
|
16
|
+
<img alt="241 components" src="https://img.shields.io/badge/components-241-46e8ff?style=flat-square&labelColor=111711">
|
|
17
|
+
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-a79cff?style=flat-square&labelColor=111711"></a>
|
|
18
|
+
</p>
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="#quick-start">Start</a> ·
|
|
22
|
+
<a href="#platform-support">Platforms</a> ·
|
|
23
|
+
<a href="#component-catalog">Components</a> ·
|
|
24
|
+
<a href="#reactive-by-design">Reactivity</a> ·
|
|
25
|
+
<a href="#how-zui-runs">Architecture</a> ·
|
|
26
|
+
<a href="#ship-an-application">Distribution</a> ·
|
|
27
|
+
<a href="#showcase-applications">Examples</a> ·
|
|
28
|
+
<a href="https://zui.alkimist.dev">Documentation</a>
|
|
29
|
+
</p>
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
theme and controls, and the full built-in component catalog. It has no runtime dependency on
|
|
19
|
-
Quickshell or an Omarchy installation.
|
|
31
|
+
---
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
Zui is a desktop UI framework for Ruby. Application code owns the interface, state, behavior,
|
|
34
|
+
and assets; Zui owns the rendering protocol, native host, platform-neutral QML implementation,
|
|
35
|
+
and complete component catalog. The result is a normal desktop application—not a web page inside
|
|
36
|
+
a window.
|
|
37
|
+
|
|
38
|
+
<table>
|
|
39
|
+
<tr>
|
|
40
|
+
<td width="33%"><strong>One application language</strong><br>Compose UI, state, bindings, events, commands, timers, and application logic in Ruby.</td>
|
|
41
|
+
<td width="33%"><strong>Native desktop renderer</strong><br>Render through Qt Quick, Controls, Multimedia, GPU effects, Shapes, and optional 3D modules.</td>
|
|
42
|
+
<td width="33%"><strong>Private verified runtime</strong><br>Install a checksummed native client without changing system Qt or shell configuration.</td>
|
|
43
|
+
</tr>
|
|
44
|
+
<tr>
|
|
45
|
+
<td><strong>241 named components</strong><br>Use specific controls with validated properties and events instead of a generic markup escape hatch.</td>
|
|
46
|
+
<td><strong>Reactive by default</strong><br>Update only changed properties and publish multi-value transactions as atomic patch batches.</td>
|
|
47
|
+
<td><strong>Portable source</strong><br>Run the same application on Linux, macOS, Windows, or through an environment adapter such as Omarchy UI.</td>
|
|
48
|
+
</tr>
|
|
49
|
+
</table>
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="examples/nova_pour/preview.png" width="880" alt="Nova Pour, a complete pure-Ruby Zui desktop application">
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<p align="center"><sub>Nova Pour is one of the complete applications included in the showcase catalog.</sub></p>
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
Install Ruby 3.1 or newer and the gem. Zui downloads its version-matched native client only when
|
|
60
|
+
you explicitly configure it:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
gem install zui
|
|
64
|
+
zui doctor --fix
|
|
65
|
+
zui new telemetry-console
|
|
66
|
+
cd telemetry-console
|
|
67
|
+
zui run main.rb
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
That is the complete development setup. You do not need a Qt SDK, CMake, a C++ compiler, or a
|
|
71
|
+
system-wide Qt installation.
|
|
72
|
+
|
|
73
|
+
`zui doctor --fix` downloads the native client for the installed Zui version, verifies its
|
|
74
|
+
SHA-256 checksum and manifest, and activates it atomically in the user cache. It does not modify
|
|
75
|
+
shell startup files or global Qt environment variables.
|
|
76
|
+
|
|
77
|
+
## Your first application
|
|
22
78
|
|
|
23
79
|
```ruby
|
|
24
80
|
require "zui"
|
|
@@ -46,51 +102,52 @@ end
|
|
|
46
102
|
Counter.run
|
|
47
103
|
```
|
|
48
104
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
gem install zui
|
|
53
|
-
zui doctor --fix
|
|
54
|
-
zui new telemetry-console
|
|
55
|
-
cd telemetry-console
|
|
56
|
-
zui run main.rb
|
|
57
|
-
zui bundle
|
|
58
|
-
```
|
|
105
|
+
The source is ordinary Ruby. Zui validates the declared tree, sends it through a versioned JSON
|
|
106
|
+
protocol, and renders it with native Qt components. Application QML is neither required nor
|
|
107
|
+
accepted through the runtime protocol.
|
|
59
108
|
|
|
60
|
-
|
|
109
|
+
## Reactive by design
|
|
61
110
|
|
|
62
|
-
|
|
63
|
-
gem build zui.gemspec
|
|
64
|
-
gem install ./zui-0.0.6.gem
|
|
65
|
-
```
|
|
111
|
+
State, bindings, animation, scheduled work, and events live in the same builder context:
|
|
66
112
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
host and its Qt/QML engine libraries—not the developer's application, Ruby source, or the Zui
|
|
72
|
-
Ruby/QML framework. Setup never changes the global shell environment; `zui run` supplies the
|
|
73
|
-
client paths only to the child application.
|
|
113
|
+
```ruby
|
|
114
|
+
Zui.app do
|
|
115
|
+
state :status, "online"
|
|
116
|
+
state :uptime, 0
|
|
74
117
|
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
app :main, title: "Service Monitor", width: 720, height: 480 do
|
|
119
|
+
card padding: 24 do
|
|
120
|
+
column spacing: 12 do
|
|
121
|
+
indicator = badge state.status
|
|
122
|
+
bind(indicator, :value) { state.status.upcase }
|
|
123
|
+
bind(indicator, :foreground) { state.status == "online" ? "#9cff57" : "#ff6f7d" }
|
|
77
124
|
|
|
78
|
-
|
|
79
|
-
a standard `.app` on macOS. The package combines the application's Ruby/assets, the Zui framework
|
|
80
|
-
runtime and catalog, and a private copy of the configured native Qt/QML client. No system Qt
|
|
81
|
-
installation is used by the finished bundle. The current bundle format expects Ruby 3.1 or newer
|
|
82
|
-
on the destination and supports `ZUI_RUBY` when an installer provides a private Ruby executable.
|
|
125
|
+
text { "Uptime: #{state.uptime}s" }
|
|
83
126
|
|
|
84
|
-
|
|
127
|
+
button "Reconnect" do
|
|
128
|
+
transaction do
|
|
129
|
+
state.status = "online"
|
|
130
|
+
state.uptime = 0
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
85
136
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
that encounters them.
|
|
137
|
+
every(1) { state.uptime += 1 }
|
|
138
|
+
end
|
|
139
|
+
```
|
|
90
140
|
|
|
91
|
-
|
|
141
|
+
- `state` defines application-owned values.
|
|
142
|
+
- Value components can take a block for their primary reactive property.
|
|
143
|
+
- `bind` connects any declared property to application state.
|
|
144
|
+
- `transaction` publishes related changes together.
|
|
145
|
+
- `after`, `every`, and `async` schedule application work.
|
|
146
|
+
- `animate` and animation components use native render-side transitions.
|
|
147
|
+
- `dynamic` rebuilds data-dependent child structures without rebuilding the whole application.
|
|
92
148
|
|
|
93
|
-
|
|
149
|
+
Reusable UI modules are scoped to one application builder, so domain components do not leak into
|
|
150
|
+
other applications:
|
|
94
151
|
|
|
95
152
|
```ruby
|
|
96
153
|
module TelemetryConsole
|
|
@@ -105,30 +162,290 @@ module TelemetryConsole
|
|
|
105
162
|
app(:main, title: "Telemetry Console") { dashboard }
|
|
106
163
|
end
|
|
107
164
|
end
|
|
108
|
-
|
|
109
|
-
def self.run = build.run
|
|
110
165
|
end
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Platform support
|
|
169
|
+
|
|
170
|
+
Zui uses the same Ruby API, protocol, renderer, catalog, and application source on every supported
|
|
171
|
+
desktop target. Each release is gated by CI that builds, packages, installs, and launches the
|
|
172
|
+
matching native client.
|
|
173
|
+
|
|
174
|
+
| Operating system | Architecture | Native client | `zui bundle` output | Release status |
|
|
175
|
+
| --- | --- | --- | --- | --- |
|
|
176
|
+
| Linux | x86-64 | `zui-client-linux-x86_64` | Portable application directory | Supported and CI verified |
|
|
177
|
+
| macOS | Apple Silicon | `zui-client-macos-arm64` | Standard `.app` bundle | Supported and CI verified |
|
|
178
|
+
| macOS | Intel x86-64 | `zui-client-macos-x86_64` | Standard `.app` bundle | Supported and CI verified |
|
|
179
|
+
| Windows | x86-64 | `zui-client-windows-x86_64` | Portable application directory | Supported and CI verified |
|
|
180
|
+
|
|
181
|
+
The application bundle expects Ruby 3.1 or newer on the destination. Its launcher prefers
|
|
182
|
+
`ZUI_RUBY`, then the Ruby executable that created the bundle when that path is still available,
|
|
183
|
+
and finally a `ruby` executable on `PATH`. Installers that carry a private Ruby can point
|
|
184
|
+
`ZUI_RUBY` at that executable.
|
|
111
185
|
|
|
112
|
-
|
|
186
|
+
Unsupported architectures fail explicitly during configuration. Zui never silently compiles Qt,
|
|
187
|
+
uses a system Qt installation, or downloads an asset for a different platform. See the complete
|
|
188
|
+
[platform and bundle layouts](docs/platforms.md).
|
|
189
|
+
|
|
190
|
+
## Component catalog
|
|
191
|
+
|
|
192
|
+
Zui 0.0.7 registers **241 built-in components**. Every entry has a named Ruby builder method,
|
|
193
|
+
property and event schema, native QML renderer, reactive patch support, and contract tests.
|
|
194
|
+
|
|
195
|
+
| Category | Count | Representative components |
|
|
196
|
+
| --- | ---: | --- |
|
|
197
|
+
| Foundation and layout | 28 | `container`, `grid_layout`, `scroll`, `split_view`, `application_window` |
|
|
198
|
+
| Display, content, and media | 24 | `text`, `image`, `markdown`, `video`, `model_view_3d` |
|
|
199
|
+
| Buttons and input | 36 | `button`, `text_field`, `slider`, `date_picker`, `multi_select` |
|
|
200
|
+
| Navigation and structure | 20 | `tabs`, `drawer`, `stack_view`, `breadcrumb`, `key_catcher` |
|
|
201
|
+
| Menus, dialogs, and feedback | 19 | `dialog`, `popup`, `toast`, `progress_ring`, `bottom_sheet` |
|
|
202
|
+
| Data and collections | 22 | `list_view`, `grid_view`, `table_view`, `tree_view`, `calendar` |
|
|
203
|
+
| Charts and visualization | 16 | `line_chart`, `candlestick_chart`, `heatmap`, `gauge`, `legend` |
|
|
204
|
+
| Drawing and interaction | 16 | `canvas`, `shape`, `shader_effect`, `drag_area`, `particle_system` |
|
|
205
|
+
| Animation, state, and timing | 32 | `animation`, `transition`, `timer`, `state_group`, `spring_animation` |
|
|
206
|
+
| Effects | 7 | `multi_effect`, `blur`, `drop_shadow`, `colorize`, `glow` |
|
|
207
|
+
| Multimedia and capture | 12 | `media_player`, `camera`, `audio_input`, `video_output`, `screen_capture` |
|
|
208
|
+
| Models and utilities | 9 | `list_model`, `settings`, `clipboard`, `standard_paths` |
|
|
209
|
+
|
|
210
|
+
Zui does not silently replace unavailable components with screenshots, generic controls, or
|
|
211
|
+
application-specific fallbacks. A declared component either renders as that component or reports
|
|
212
|
+
an explicit error.
|
|
213
|
+
|
|
214
|
+
- Browse the [complete source-backed component reference](https://zui.alkimist.dev/components).
|
|
215
|
+
- Review the [component coverage matrix](docs/component-coverage.md).
|
|
216
|
+
|
|
217
|
+
## Command-line workflow
|
|
218
|
+
|
|
219
|
+
| Command | Purpose |
|
|
220
|
+
| --- | --- |
|
|
221
|
+
| `zui new NAME` | Generate a pure-Ruby application with a reusable UI module and tests |
|
|
222
|
+
| `zui doctor` | Report Ruby, platform, native-client, run, and bundle readiness without changing anything |
|
|
223
|
+
| `zui doctor --fix` | Download, verify, and install the missing versioned native client |
|
|
224
|
+
| `zui configure` | Perform the same explicit native-client installation directly |
|
|
225
|
+
| `zui run FILE` | Launch a Ruby entry point through the private native client |
|
|
226
|
+
| `zui bundle [DIRECTORY]` | Tree-shake and assemble the application, Zui framework, and native runtime for the current OS |
|
|
227
|
+
| `zui bundle --name NAME --output PATH` | Override the generated product name and destination |
|
|
228
|
+
| `zui bundle --no-tree-shake` | Retain the complete component and Qt feature catalog for metaprogrammed applications |
|
|
229
|
+
| `zui bundle --dist [DIRECTORY]` | Build release installers from the required project-root `config.rb` |
|
|
230
|
+
| `zui version` | Print the installed framework version |
|
|
231
|
+
|
|
232
|
+
Zui has no separate validation command. Ruby, DSL, schema, resource, protocol, and renderer errors
|
|
233
|
+
are reported by the operation that encounters them.
|
|
234
|
+
|
|
235
|
+
## How Zui runs
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
239
|
+
│ Ruby application │
|
|
240
|
+
│ UI modules · state · bindings · events · commands · assets │
|
|
241
|
+
└──────────────────────────────┬──────────────────────────────────────┘
|
|
242
|
+
│ versioned JSON protocol
|
|
243
|
+
┌──────────────────────────────▼──────────────────────────────────────┐
|
|
244
|
+
│ Zui native client │
|
|
245
|
+
│ process transport · schema validation · lifecycle · error boundary │
|
|
246
|
+
└──────────────────────────────┬──────────────────────────────────────┘
|
|
247
|
+
│ declared component tree + patches
|
|
248
|
+
┌──────────────────────────────▼──────────────────────────────────────┐
|
|
249
|
+
│ Platform-neutral renderer │
|
|
250
|
+
│ ControlNode · theme · controls · 241-component QML catalog │
|
|
251
|
+
└──────────────────────────────┬──────────────────────────────────────┘
|
|
252
|
+
│
|
|
253
|
+
Qt Quick · Controls · Multimedia · GPU · 3D
|
|
113
254
|
```
|
|
114
255
|
|
|
115
|
-
|
|
116
|
-
|
|
256
|
+
The Ruby process owns application logic. The native client owns the Qt event loop and graphics
|
|
257
|
+
runtime. The renderer applies validated trees and bounded reactive patch batches across the
|
|
258
|
+
process boundary.
|
|
259
|
+
|
|
260
|
+
The client intentionally excludes browser-engine payloads. Zui is desktop-only and does not use
|
|
261
|
+
HTML, CSS, JavaScript, WebView, Electron, or Qt WebEngine to render application interfaces.
|
|
262
|
+
|
|
263
|
+
## Native client integrity
|
|
117
264
|
|
|
118
|
-
|
|
265
|
+
The RubyGem contains the Ruby framework and QML catalog, but not a platform client. Native clients
|
|
266
|
+
are built by the release matrix and attached to the matching GitHub tag.
|
|
267
|
+
|
|
268
|
+
During setup, Zui verifies:
|
|
269
|
+
|
|
270
|
+
1. The release asset name matches the detected platform and architecture.
|
|
271
|
+
2. The downloaded archive matches its published SHA-256 checksum.
|
|
272
|
+
3. Every archive path is safe before extraction.
|
|
273
|
+
4. `client.json` has the expected format, Zui version, platform, executable, and bundle capability.
|
|
274
|
+
5. Activation completes atomically in the versioned user cache.
|
|
275
|
+
|
|
276
|
+
The private client includes the host executable, linked Qt libraries, QML modules, plugins, and
|
|
277
|
+
translations required by the catalog. `zui run` exposes those paths only to its child process.
|
|
278
|
+
|
|
279
|
+
## Ship an application
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
zui bundle
|
|
283
|
+
# or
|
|
284
|
+
zui bundle path/to/application --name "Telemetry Console"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Every distribution combines three deliberately separate payloads:
|
|
119
288
|
|
|
120
289
|
```text
|
|
121
|
-
Ruby
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
290
|
+
application Ruby source and assets
|
|
291
|
+
+ Zui Ruby/QML framework runtime
|
|
292
|
+
+ configured native Qt/QML client
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
| Platform | Generated shape | Suitable next step |
|
|
296
|
+
| --- | --- | --- |
|
|
297
|
+
| Linux | Self-contained directory with `run` and desktop entry | AppImage, Flatpak, Snap, `.deb`, `.rpm`, or Arch package |
|
|
298
|
+
| macOS | Standard `.app` directory | Sign, notarize, and distribute with the application's release identity |
|
|
299
|
+
| Windows | Self-contained directory with `run.cmd` | MSIX, MSI, WiX, Inno Setup, or another installer |
|
|
300
|
+
|
|
301
|
+
No system Qt installation is used by the finished bundle. Signing, notarization, installer format,
|
|
302
|
+
store submission, and application identity remain the release owner's responsibility.
|
|
303
|
+
|
|
304
|
+
`zui bundle` statically analyzes every production Ruby source file, including code in conditional
|
|
305
|
+
branches, and retains only the referenced Zui adapters, QML modules, plugins, and native library
|
|
306
|
+
dependency closure. Test, spec, vendor, temporary, and previous distribution directories do not
|
|
307
|
+
affect the result. The selected components and byte savings are recorded in `zui-bundle.json`.
|
|
308
|
+
|
|
309
|
+
Ruby can compute method names at runtime, so applications that invoke components through
|
|
310
|
+
metaprogramming must declare those possible types in `.zui-bundle.json`:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"components": ["camera", "video_output"]
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Use `--no-tree-shake` only when the possible component set cannot be declared.
|
|
319
|
+
|
|
320
|
+
### Native installers
|
|
321
|
+
|
|
322
|
+
`zui bundle --dist` creates the same tree-shaken application bundle and then packages it for the
|
|
323
|
+
current operating system:
|
|
324
|
+
|
|
325
|
+
| Host platform | Distribution artifacts |
|
|
326
|
+
|---|---|
|
|
327
|
+
| Linux | `.deb` and `.rpm` |
|
|
328
|
+
| macOS | `.dmg` containing the `.app` and an Applications shortcut |
|
|
329
|
+
| Windows | Inno Setup `.exe` installer |
|
|
330
|
+
|
|
331
|
+
Release packaging requires a `config.rb` file in the project root. It is executable Ruby using a
|
|
332
|
+
validated Zui DSL:
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
Zui::Dist.configure do
|
|
336
|
+
name "Telemetry Console"
|
|
337
|
+
identifier "com.example.telemetry-console"
|
|
338
|
+
version "1.0.0"
|
|
339
|
+
publisher "Example Company <dev@example.com>"
|
|
340
|
+
description "A native telemetry dashboard."
|
|
341
|
+
license "MIT"
|
|
342
|
+
homepage "https://example.com/telemetry-console"
|
|
343
|
+
|
|
344
|
+
icon linux: "assets/icon.png",
|
|
345
|
+
macos: "assets/icon.icns",
|
|
346
|
+
windows: "assets/icon.ico"
|
|
347
|
+
categories "Utility", "Development"
|
|
348
|
+
end
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Only the current platform's icon is required when packaging: PNG or SVG on Linux, ICNS on macOS,
|
|
352
|
+
and ICO on Windows. Paths must remain inside the project. `--output DIRECTORY` selects the artifact
|
|
353
|
+
directory, and existing artifacts are never overwritten. Linux RPM creation requires `rpmbuild`
|
|
354
|
+
(`rpm-build` or `rpm-tools`), macOS uses the system `hdiutil`, and Windows requires Inno Setup 6's
|
|
355
|
+
`ISCC.exe` on `PATH`.
|
|
356
|
+
|
|
357
|
+
The installers carry the application, Zui, Qt, and the selected native dependencies. Like the
|
|
358
|
+
directory bundle, Ruby 3.1 or newer is currently required on the destination; DEB and RPM metadata
|
|
359
|
+
declare that dependency. Code signing, Apple notarization, and Windows Authenticode signing remain
|
|
360
|
+
release-owner steps.
|
|
361
|
+
|
|
362
|
+
## Showcase applications
|
|
363
|
+
|
|
364
|
+
The repository includes complete Ruby applications rather than isolated visual snippets:
|
|
365
|
+
|
|
366
|
+
| Application | What it demonstrates |
|
|
367
|
+
| --- | --- |
|
|
368
|
+
| [Avatar Runner](examples/avatar_runner/) | Keyboard focus, pointer input, timed physics, Canvas drawing, and atomic state patches |
|
|
369
|
+
| [Nova Pour](examples/nova_pour/) | Image loading, filtering, cart state, dialogs, bindings, and order simulation |
|
|
370
|
+
| [Tesla Drive Dashboard](examples/tesla_drive_dashboard/) | Vehicle simulation, image stacks, Canvas maps, local audio, telemetry, and animation |
|
|
371
|
+
| [Lumen Forge](examples/lumen_forge/) | Compiled shaders, pointer uniforms, GPU effects, charts, and timers |
|
|
372
|
+
| [Cardiac Health Monitor](examples/cardiac_health_monitor/) | Medical imagery, ECG visualization, gauges, heatmaps, particles, and live state |
|
|
373
|
+
| [Orbital Weather Console](examples/orbital_weather_console/) | Image-backed weather scenes, forecasts, gauges, charts, and simulation |
|
|
374
|
+
| [Quantum Market Terminal](examples/quantum_market_terminal/) | Portfolio state, trading simulation, charts, allocation controls, and transactions |
|
|
375
|
+
| [Smart Home Energy](examples/smart_home_energy/) | Room imagery, lighting, device state, energy telemetry, and home simulation |
|
|
376
|
+
| [Cinematic Music Studio](examples/cinematic_music_studio/) | Local and remote media, playback state, seeking, playlists, and audio controls |
|
|
377
|
+
|
|
378
|
+
Run any showcase through the normal client:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
zui run examples/avatar_runner/main.rb
|
|
382
|
+
zui run examples/tesla_drive_dashboard/main.rb
|
|
383
|
+
zui bundle examples/nova_pour
|
|
127
384
|
```
|
|
128
385
|
|
|
129
|
-
|
|
130
|
-
|
|
386
|
+
See the [complete showcase index](examples/).
|
|
387
|
+
|
|
388
|
+
## Omarchy integration
|
|
389
|
+
|
|
390
|
+
Zui is platform-neutral and has no runtime dependency on Quickshell or Omarchy. The separate
|
|
391
|
+
[`omarchy-ui`](https://github.com/AdamMusa/omarchy-ui) adapter adds shell applications, bar widgets,
|
|
392
|
+
panels, plugin lifecycle, theme integration, and Omarchy packaging while reusing the same Ruby DSL,
|
|
393
|
+
protocol, and component catalog.
|
|
394
|
+
|
|
395
|
+
The dependency direction remains one-way: desktop-environment adapters depend on Zui; Zui never
|
|
396
|
+
contains environment-specific branches.
|
|
397
|
+
|
|
398
|
+
## Develop Zui itself
|
|
399
|
+
|
|
400
|
+
Clone the repository and run the complete local suite:
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
git clone https://github.com/AdamMusa/zui.git
|
|
404
|
+
cd zui
|
|
405
|
+
scripts/test
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The suite covers the Ruby API, state engine, protocol, CLI, distributions, examples, QML contracts,
|
|
409
|
+
QML linting when available, the C++ host build, and an offscreen runtime smoke test.
|
|
410
|
+
|
|
411
|
+
Build and install the gem directly:
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
gem build zui.gemspec
|
|
415
|
+
gem install ./zui-0.0.7.gem
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Release CI additionally constructs and audits a relocatable native client, installs the built gem,
|
|
419
|
+
repairs a fresh client cache, and launches a real application on every supported target.
|
|
420
|
+
|
|
421
|
+
## Troubleshooting
|
|
422
|
+
|
|
423
|
+
| Symptom | Resolution |
|
|
424
|
+
| --- | --- |
|
|
425
|
+
| `Client: not configured` | Run `zui doctor --fix` once for the installed Zui version |
|
|
426
|
+
| Platform or architecture is unsupported | Use one of the release-gated targets above or add a verified native-client runner and artifact |
|
|
427
|
+
| A bundle cannot find Ruby | Install Ruby 3.1+ or point `ZUI_RUBY` at an application-private Ruby executable |
|
|
428
|
+
| A component reports a resource or module error | Check the declared asset path and run `zui doctor`; Zui will not substitute another component |
|
|
429
|
+
| Audio, camera, or capture is unavailable | Confirm OS permissions, devices, codecs, and platform media services |
|
|
430
|
+
| The native client looks stale after a framework update | Run `zui doctor --fix`; client caches are isolated by Zui version |
|
|
431
|
+
|
|
432
|
+
## Documentation and support
|
|
433
|
+
|
|
434
|
+
| Resource | Purpose |
|
|
435
|
+
| --- | --- |
|
|
436
|
+
| [Official documentation](https://zui.alkimist.dev) | Guides, concepts, and searchable API documentation |
|
|
437
|
+
| [Component reference](https://zui.alkimist.dev/components) | Properties, events, container behavior, and validated Ruby examples for all 241 components |
|
|
438
|
+
| [Platform support](docs/platforms.md) | Native-client setup, target matrix, and OS-specific bundle layouts |
|
|
439
|
+
| [Component coverage](docs/component-coverage.md) | Complete built-in catalog checklist |
|
|
440
|
+
| [GitHub Releases](https://github.com/AdamMusa/zui/releases) | Versioned native clients and SHA-256 checksums |
|
|
441
|
+
| [RubyGems](https://rubygems.org/gems/zui) | Published framework versions |
|
|
442
|
+
| [Issue tracker](https://github.com/AdamMusa/zui/issues) | Focused bug reports and feature proposals |
|
|
131
443
|
|
|
132
444
|
## License
|
|
133
445
|
|
|
134
|
-
MIT
|
|
446
|
+
Zui is available under the [MIT License](LICENSE). Bundled third-party fonts and runtime
|
|
447
|
+
dependencies retain their respective licenses; see [third-party notices](THIRD_PARTY_NOTICES.md).
|
|
448
|
+
|
|
449
|
+
<p align="center">
|
|
450
|
+
<strong>Build the interface in Ruby. Let Zui carry it to the desktop.</strong>
|
|
451
|
+
</p>
|
data/Service.qml
CHANGED
|
@@ -13,6 +13,7 @@ Item {
|
|
|
13
13
|
property bool ready: false
|
|
14
14
|
property bool stopping: false
|
|
15
15
|
property string lastError: ""
|
|
16
|
+
property string runtimeDiagnostics: ""
|
|
16
17
|
property var surfaces: ({})
|
|
17
18
|
property var surfaceOptions: ({})
|
|
18
19
|
property var nodeIndex: ({})
|
|
@@ -411,6 +412,7 @@ Item {
|
|
|
411
412
|
|
|
412
413
|
function startRuby() {
|
|
413
414
|
if (stopping || projectDir === "" || program === "" || transport.running) return
|
|
415
|
+
runtimeDiagnostics = ""
|
|
414
416
|
transport.start(runtimeExecutable, program, projectDir, rubyLoadPath)
|
|
415
417
|
}
|
|
416
418
|
|
|
@@ -429,14 +431,24 @@ Item {
|
|
|
429
431
|
function onLineReceived(line) { root.handleLine(line) }
|
|
430
432
|
function onErrorLineReceived(line) {
|
|
431
433
|
var message = String(line || "").trim()
|
|
432
|
-
if (message
|
|
434
|
+
if (message === "") return
|
|
435
|
+
console.warn("zui ruby:", message)
|
|
436
|
+
var separator = root.runtimeDiagnostics === "" ? "" : "\n"
|
|
437
|
+
root.runtimeDiagnostics = (root.runtimeDiagnostics + separator + message).slice(0, 512)
|
|
438
|
+
Qt.callLater(function() {
|
|
439
|
+
if (!root.ready && !root.transport.running && root.runtimeDiagnostics !== "")
|
|
440
|
+
root.lastError = root.runtimeDiagnostics
|
|
441
|
+
})
|
|
433
442
|
}
|
|
434
443
|
function onExited(exitCode) {
|
|
435
444
|
root.ready = false
|
|
436
445
|
if (root.stopping || root.projectDir === "") return
|
|
437
|
-
|
|
446
|
+
var summary = exitCode === 0
|
|
438
447
|
? "Zui runtime stopped"
|
|
439
448
|
: "Zui runtime crashed (exit " + exitCode + ")"
|
|
449
|
+
root.lastError = root.runtimeDiagnostics === ""
|
|
450
|
+
? summary
|
|
451
|
+
: summary + "\n\n" + root.runtimeDiagnostics
|
|
440
452
|
restartTimer.interval = root.restartDelayMs
|
|
441
453
|
restartTimer.start()
|
|
442
454
|
root.restartDelayMs = Math.min(30000, root.restartDelayMs * 2)
|
data/lib/zui/cli.rb
CHANGED
|
@@ -5,7 +5,7 @@ require "rbconfig"
|
|
|
5
5
|
|
|
6
6
|
module Zui
|
|
7
7
|
class CLI
|
|
8
|
-
USAGE = "Usage: zui <new NAME|configure|doctor [--fix]|run FILE|bundle [DIRECTORY]|version>"
|
|
8
|
+
USAGE = "Usage: zui <new NAME|configure|doctor [--fix]|run FILE|bundle [--dist] [--no-tree-shake] [DIRECTORY]|version>"
|
|
9
9
|
|
|
10
10
|
def self.run(arguments, out: $stdout, err: $stderr)
|
|
11
11
|
new(out:, err:).run(arguments.dup)
|
|
@@ -56,13 +56,34 @@ module Zui
|
|
|
56
56
|
def bundle_project(arguments)
|
|
57
57
|
name = option_value(arguments, "--name")
|
|
58
58
|
destination = option_value(arguments, "--output")
|
|
59
|
+
create_installers = !arguments.delete("--dist").nil?
|
|
60
|
+
tree_shake = arguments.delete("--no-tree-shake").nil?
|
|
59
61
|
source = File.expand_path(arguments.shift || Dir.pwd)
|
|
60
62
|
raise ArgumentError, "bundle accepts one directory" unless arguments.empty?
|
|
61
|
-
|
|
63
|
+
|
|
64
|
+
if create_installers
|
|
65
|
+
raise ArgumentError, "--name cannot be used with --dist; set name in config.rb" if name
|
|
66
|
+
|
|
67
|
+
packager = DistPackager.new(tree_shake:)
|
|
68
|
+
paths = packager.package(source, output: destination)
|
|
69
|
+
paths.each { |path| @out.puts("Created distribution artifact #{path}") }
|
|
70
|
+
report_tree_shaking(packager.tree_shake_report)
|
|
71
|
+
return 0
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
distribution = Distribution.new(tree_shake:)
|
|
75
|
+
path = distribution.bundle(source, name:, destination:)
|
|
62
76
|
@out.puts("Bundled #{Platform.current.os} application in #{path}")
|
|
77
|
+
report_tree_shaking(distribution.tree_shake_report)
|
|
63
78
|
0
|
|
64
79
|
end
|
|
65
80
|
|
|
81
|
+
def report_tree_shaking(report)
|
|
82
|
+
return unless report
|
|
83
|
+
|
|
84
|
+
@out.puts("Tree-shaken runtime: #{report.components.length} components, #{format_bytes(report.saved_bytes)} removed")
|
|
85
|
+
end
|
|
86
|
+
|
|
66
87
|
def doctor(arguments)
|
|
67
88
|
fix = arguments.delete("--fix")
|
|
68
89
|
raise ArgumentError, "doctor accepts only --fix" unless arguments.empty?
|
|
@@ -117,5 +138,16 @@ module Zui
|
|
|
117
138
|
raise ArgumentError, "name must contain letters or numbers" if result.empty?
|
|
118
139
|
result
|
|
119
140
|
end
|
|
141
|
+
|
|
142
|
+
def format_bytes(bytes)
|
|
143
|
+
units = %w[B KB MB GB]
|
|
144
|
+
value = bytes.to_f
|
|
145
|
+
unit = units.shift
|
|
146
|
+
while value >= 1024 && !units.empty?
|
|
147
|
+
value /= 1024
|
|
148
|
+
unit = units.shift
|
|
149
|
+
end
|
|
150
|
+
value >= 10 || unit == "B" ? "#{value.round} #{unit}" : format("%.1f %s", value, unit)
|
|
151
|
+
end
|
|
120
152
|
end
|
|
121
153
|
end
|