Native desktop applications in pure Ruby
Build reactive Linux, macOS, and Windows interfaces with one Ruby API, a native Qt renderer, and no application-owned QML or browser runtime.
Start · Platforms · Components · Reactivity · Architecture · Distribution · Examples · Documentation
Zui is a desktop UI framework for Ruby. Application code owns the interface, state, behavior, and assets; Zui owns the rendering protocol, native host, platform-neutral QML implementation, and complete component catalog. The result is a normal desktop application—not a web page inside a window.
| One application language Compose UI, state, bindings, events, commands, timers, and application logic in Ruby. |
Native desktop renderer Render through Qt Quick, Controls, Multimedia, GPU effects, Shapes, and optional 3D modules. |
Private verified runtime Install a checksummed native client without changing system Qt or shell configuration. |
| 241 named components Use specific controls with validated properties and events instead of a generic markup escape hatch. |
Reactive by default Update only changed properties and publish multi-value transactions as atomic patch batches. |
Portable source Run the same application on Linux, macOS, Windows, or through an environment adapter such as Omarchy UI. |
Nova Pour is one of the complete applications included in the showcase catalog.
Quick start
Install Ruby 3.1 or newer and the gem. Zui downloads its version-matched native client and small mruby bundle runtime only when you explicitly configure it:
gem install zui
zui doctor --fix
zui new telemetry-console
cd telemetry-console
zui run main.rb
That is the complete development setup. You do not need a Qt SDK, CMake, a C++ compiler, or a system-wide Qt installation.
zui doctor --fix downloads the native client and lite mruby runtime for the installed Zui
version, verifies their SHA-256 checksums and manifests, and activates them atomically in the user
cache. It does not modify shell startup files or global Qt environment variables.
Your first application
require "zui"
module Counter
def self.run
Zui.app do
state :count, 0
app :main, title: "Counter", width: 640, height: 420 do
container padding: 24 do
column spacing: 16 do
label "Counter"
text { "Count: #{state.count}" }
"Increment" do
state.count += 1
end
end
end
end
end
end
end
Counter.run
The source is ordinary Ruby. Zui validates the declared tree, sends it through a versioned JSON protocol, and renders it with native Qt components. Application QML is neither required nor accepted through the runtime protocol.
Reactive by design
State, bindings, animation, scheduled work, and events live in the same builder context:
Zui.app do
state :status, "online"
state :uptime, 0
app :main, title: "Service Monitor", width: 720, height: 480 do
card padding: 24 do
column spacing: 12 do
indicator = badge state.status
bind(indicator, :value) { state.status.upcase }
bind(indicator, :foreground) { state.status == "online" ? "#9cff57" : "#ff6f7d" }
text { "Uptime: #{state.uptime}s" }
"Reconnect" do
transaction do
state.status = "online"
state.uptime = 0
end
end
end
end
end
every(1) { state.uptime += 1 }
end
statedefines application-owned values.- Value components can take a block for their primary reactive property.
bindconnects any declared property to application state.transactionpublishes related changes together.after,every, andasyncschedule application work.animateand animation components use native render-side transitions.dynamicrebuilds data-dependent child structures without rebuilding the whole application.
Reusable UI modules are scoped to one application builder, so domain components do not leak into other applications:
module TelemetryConsole
module UI
def dashboard
card { text "System online" }
end
end
def self.build
Zui::Application.new(ui: UI) do
app(:main, title: "Telemetry Console") { dashboard }
end
end
end
Platform support
Zui uses the same Ruby API, protocol, renderer, catalog, and application source on every supported desktop target. Each release is gated by CI that builds, packages, installs, and launches the matching native client.
| Operating system | Architecture | Native client | zui bundle output |
Release status |
|---|---|---|---|---|
| Linux | x86-64 | zui-client-linux-x86_64 |
Portable application directory | Supported and CI verified |
| macOS | Apple Silicon | zui-client-macos-arm64 |
Standard .app bundle |
Supported and CI verified |
| macOS | Intel x86-64 | zui-client-macos-x86_64 |
Standard .app bundle |
Supported and CI verified |
| Windows | x86-64 | zui-client-windows-x86_64 |
Portable application directory | Supported and CI verified |
Application bundles do not require Ruby on the destination. The default --lite mode embeds Zui's
versioned mruby runtime. --full embeds a private CRuby plus only the non-Zui gems resolved by the
project's Gemfile.lock.
Unsupported architectures fail explicitly during configuration. Zui never silently compiles Qt, uses a system Qt installation, or downloads an asset for a different platform. See the complete platform and bundle layouts.
Component catalog
Zui 0.0.9 registers 241 built-in components. Every entry has a named Ruby builder method, property and event schema, native QML renderer, reactive patch support, and contract tests.
| Category | Count | Representative components |
|---|---|---|
| Foundation and layout | 28 | container, grid_layout, scroll, split_view, application_window |
| Display, content, and media | 24 | text, image, markdown, video, model_view_3d |
| Buttons and input | 36 | button, text_field, slider, date_picker, multi_select |
| Navigation and structure | 20 | tabs, drawer, stack_view, breadcrumb, key_catcher |
| Menus, dialogs, and feedback | 19 | dialog, popup, toast, progress_ring, bottom_sheet |
| Data and collections | 22 | list_view, grid_view, table_view, tree_view, calendar |
| Charts and visualization | 16 | line_chart, candlestick_chart, heatmap, gauge, legend |
| Drawing and interaction | 16 | canvas, shape, shader_effect, drag_area, particle_system |
| Animation, state, and timing | 32 | animation, transition, timer, state_group, spring_animation |
| Effects | 7 | multi_effect, blur, drop_shadow, colorize, glow |
| Multimedia and capture | 12 | media_player, camera, audio_input, video_output, screen_capture |
| Models and utilities | 9 | list_model, settings, clipboard, standard_paths |
Zui does not silently replace unavailable components with screenshots, generic controls, or application-specific fallbacks. A declared component either renders as that component or reports an explicit error.
- Browse the complete source-backed component reference.
- Review the component coverage matrix.
Command-line workflow
| Command | Purpose |
|---|---|
zui new NAME |
Generate a pure-Ruby application, Gemfile, distribution config, and reusable UI module |
zui doctor |
Report Ruby, platform, native-client, run, and bundle readiness without changing anything |
zui doctor --fix |
Download, verify, and install the missing native client and lite mruby runtime |
zui configure |
Perform the same explicit runtime installation directly |
zui run FILE |
Launch a Ruby entry point through the private native client |
zui bundle [DIRECTORY] |
Build the default standalone --lite bundle with embedded mruby |
zui bundle --full [DIRECTORY] |
Embed private CRuby and only the gems locked by the project |
zui bundle --name NAME --output PATH |
Override the generated product name and destination |
zui bundle --no-tree-shake |
Retain the complete component and Qt feature catalog for metaprogrammed applications |
zui bundle --dist [DIRECTORY] |
Build release installers from the required project-root config.rb |
zui version |
Print the installed framework version |
Zui has no separate validation command. Ruby, DSL, schema, resource, protocol, and renderer errors are reported by the operation that encounters them.
How Zui runs
┌─────────────────────────────────────────────────────────────────────┐
│ Ruby application │
│ UI modules · state · bindings · events · commands · assets │
└──────────────────────────────┬──────────────────────────────────────┘
│ versioned JSON protocol
┌──────────────────────────────▼──────────────────────────────────────┐
│ Zui native client │
│ process transport · schema validation · lifecycle · error boundary │
└──────────────────────────────┬──────────────────────────────────────┘
│ declared component tree + patches
┌──────────────────────────────▼──────────────────────────────────────┐
│ Platform-neutral renderer │
│ ControlNode · theme · controls · 241-component QML catalog │
└──────────────────────────────┬──────────────────────────────────────┘
│
Qt Quick · Controls · Multimedia · GPU · 3D
The Ruby process owns application logic. The native client owns the Qt event loop and graphics runtime. The renderer applies validated trees and bounded reactive patch batches across the process boundary.
The client intentionally excludes browser-engine payloads. Zui is desktop-only and does not use HTML, CSS, JavaScript, WebView, Electron, or Qt WebEngine to render application interfaces.
Native client integrity
The RubyGem contains the Ruby framework and QML catalog, but not a platform client. Native clients are built by the release matrix and attached to the matching GitHub tag.
During setup, Zui verifies:
- The release asset name matches the detected platform and architecture.
- The downloaded archive matches its published SHA-256 checksum.
- Every archive path is safe before extraction.
client.jsonhas the expected format, Zui version, platform, executable, and bundle capability.- Activation completes atomically in the versioned user cache.
The private client includes the host executable, linked Qt libraries, QML modules, plugins, and
translations required by the catalog. zui run exposes those paths only to its child process.
Ship an application
zui bundle
# or
zui bundle path/to/application --name "Telemetry Console"
Every distribution combines four deliberately separate payloads:
application Ruby source and assets
+ Zui Ruby/QML framework runtime
+ selected private Ruby runtime (`mruby` or `cruby`)
+ configured native Qt/QML client
| Platform | Generated shape | Suitable next step |
|---|---|---|
| Linux | Self-contained directory with run and desktop entry |
AppImage, Flatpak, Snap, .deb, .rpm, or Arch package |
| macOS | Standard .app directory |
Sign, notarize, and distribute with the application's release identity |
| Windows | Self-contained directory with run.cmd |
MSIX, MSI, WiX, Inno Setup, or another installer |
No system Ruby or Qt installation is used by the finished bundle. Signing, notarization, installer format, store submission, and application identity remain the release owner's responsibility.
--lite is the default. It compiles the project's local Ruby source into one mruby-compatible
program and rejects external gem require calls with a --full hint. Use --full for ordinary
CRuby behavior or third-party gems. Full bundles require a locked project Gemfile; run
bundle install after changing dependencies. Both modes are built for the current target OS and
architecture, and both use the same project-specific QML/native tree-shaking pass.
zui bundle statically analyzes every production Ruby source file, including code in conditional
branches, and retains only the referenced Zui adapters, QML modules, plugins, and native library
dependency closure. Test, spec, vendor, temporary, and previous distribution directories do not
affect the result. The selected components and byte savings are recorded in zui-bundle.json.
Ruby can compute method names at runtime, so applications that invoke components through
metaprogramming must declare those possible types in .zui-bundle.json:
{
"components": ["camera", "video_output"]
}
Use --no-tree-shake only when the possible component set cannot be declared.
Native installers
zui bundle --dist creates the same tree-shaken application bundle and then packages it for the
current operating system:
| Host platform | Distribution artifacts |
|---|---|
| Linux | .deb and .rpm |
| macOS | .dmg containing the .app and an Applications shortcut |
| Windows | Inno Setup .exe installer |
Release packaging requires a config.rb file in the project root. It is executable Ruby using a
validated Zui DSL:
Zui::Dist.configure do
name "Telemetry Console"
identifier "com.example.telemetry-console"
version "1.0.0"
publisher "Example Company <dev@example.com>"
description "A native telemetry dashboard."
license "MIT"
homepage "https://example.com/telemetry-console"
icon linux: "assets/icon.png",
macos: "assets/icon.icns",
windows: "assets/icon.ico"
categories "Utility", "Development"
end
Only the current platform's icon is required when packaging: PNG or SVG on Linux, ICNS on macOS,
and ICO on Windows. Paths must remain inside the project. --output DIRECTORY selects the artifact
directory, and existing artifacts are never overwritten. Linux RPM creation requires rpmbuild
(rpm-build or rpm-tools), macOS uses the system hdiutil, and Windows requires Inno Setup 6's
ISCC.exe on PATH.
The installers carry the application, selected Ruby runtime, Zui, Qt, and selected native dependencies. They do not declare or require system Ruby. Code signing, Apple notarization, and Windows Authenticode signing remain release-owner steps.
Showcase applications
The repository includes complete Ruby applications rather than isolated visual snippets:
| Application | What it demonstrates |
|---|---|
| Avatar Runner | Keyboard focus, pointer input, timed physics, Canvas drawing, and atomic state patches |
| Nova Pour | Image loading, filtering, cart state, dialogs, bindings, and order simulation |
| Tesla Drive Dashboard | Vehicle simulation, image stacks, Canvas maps, local audio, telemetry, and animation |
| Lumen Forge | Compiled shaders, pointer uniforms, GPU effects, charts, and timers |
| Cardiac Health Monitor | Medical imagery, ECG visualization, gauges, heatmaps, particles, and live state |
| Orbital Weather Console | Image-backed weather scenes, forecasts, gauges, charts, and simulation |
| Quantum Market Terminal | Portfolio state, trading simulation, charts, allocation controls, and transactions |
| Smart Home Energy | Room imagery, lighting, device state, energy telemetry, and home simulation |
| Cinematic Music Studio | Local and remote media, playback state, seeking, playlists, and audio controls |
Run any showcase through the normal client:
zui run examples/avatar_runner/main.rb
zui run examples/tesla_drive_dashboard/main.rb
zui bundle examples/nova_pour
See the complete showcase index.
Omarchy integration
Zui is platform-neutral and has no runtime dependency on Quickshell or Omarchy. The separate
omarchy-ui adapter adds shell applications, bar widgets,
panels, plugin lifecycle, theme integration, and Omarchy packaging while reusing the same Ruby DSL,
protocol, and component catalog.
The dependency direction remains one-way: desktop-environment adapters depend on Zui; Zui never contains environment-specific branches.
Develop Zui itself
Clone the repository and run the complete local suite:
git clone https://github.com/AdamMusa/zui.git
cd zui
scripts/test
The suite covers the Ruby API, state engine, protocol, CLI, distributions, examples, QML contracts, QML linting when available, the C++ host build, and an offscreen runtime smoke test.
Build and install the gem directly:
gem build zui.gemspec
gem install ./zui-*.gem
Release CI additionally builds the pinned mruby runtime, audits both release archives, installs the
built gem, repairs a fresh cache, and launches real --lite and --full bundles on every supported
target.
Troubleshooting
| Symptom | Resolution |
|---|---|
Client: not configured |
Run zui doctor --fix once for the installed Zui version |
Lite runtime: not configured |
Run zui doctor --fix to install the checksummed mruby archive |
| Platform or architecture is unsupported | Use one of the release-gated targets above or add a verified native-client runner and artifact |
--lite rejects a gem require |
Use --full and lock the gem in the project Gemfile.lock |
| A component reports a resource or module error | Check the declared asset path and run zui doctor; Zui will not substitute another component |
| Audio, camera, or capture is unavailable | Confirm OS permissions, devices, codecs, and platform media services |
| The native client looks stale after a framework update | Run zui doctor --fix; client caches are isolated by Zui version |
Documentation and support
| Resource | Purpose |
|---|---|
| Official documentation | Guides, concepts, and searchable API documentation |
| Component reference | Properties, events, container behavior, and validated Ruby examples for all 241 components |
| Platform support | Native-client setup, target matrix, and OS-specific bundle layouts |
| Component coverage | Complete built-in catalog checklist |
| GitHub Releases | Versioned native clients and SHA-256 checksums |
| RubyGems | Published framework versions |
| Issue tracker | Focused bug reports and feature proposals |
License
Zui is available under the MIT License. Bundled third-party fonts and runtime dependencies retain their respective licenses; see third-party notices.
Build the interface in Ruby. Let Zui carry it to the desktop.