omarchy-ui 0.0.8-x86_64-linux → 0.0.10-x86_64-linux

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbe75336899d8f9674166011c929469db798869f88b32f45a3b61505b0713d01
4
- data.tar.gz: bb5f2e4f0dfec4578992d17d5f3fdc5530d5b2c73d4aeebcfada553355077f94
3
+ metadata.gz: ba33f1f0b7fead5be0f82726faeeb3815b94386f679732ee4945d840374a1023
4
+ data.tar.gz: bc7bfb798a46491d22085e3f81cbde27da3b9f14149b1ec63bd3e36b182f83c7
5
5
  SHA512:
6
- metadata.gz: 9776119537e37548ea84ca4129647a6f94b64944ae75315e776db661220aefeb4cc80527ed93a1b1c9410e0b80d2a547c77a2aae6674f770decc354bdee2a505
7
- data.tar.gz: 6856581d58533845967d37daae2679cbf9601f9555aa724b9a9cbf04da0a62a8b15cd146df6d3b36551dec85ad29a13189ba1bdb01a143c2a3cfed87f46d2bb4
6
+ metadata.gz: c76b00784fdf4a0371269d79d570e10ece613faf94449f2e40025a7a5a7a45b53080b259a7a68cedc7e21c89cc03e628cc3e3301c04db806145db19cc2b0d6ed
7
+ data.tar.gz: f4511e5523b4a033be1d80682305d99e0502721186cf8330d3c4d10e332bf0e85233d96f1b59840c54f7679e34abe4b8018b170357170b1bff6e1e38f4f697e6
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- <img alt="Omarchy UI 0.0.8" src="https://img.shields.io/badge/Omarchy_UI-0.0.8-b7ff5a?style=flat-square&labelColor=111711">
13
+ <img alt="Omarchy UI 0.0.10" src="https://img.shields.io/badge/Omarchy_UI-0.0.10-b7ff5a?style=flat-square&labelColor=111711">
14
14
  <img alt="Ruby 3.1 or newer" src="https://img.shields.io/badge/Ruby-3.1%2B-cc342d?style=flat-square&logo=ruby&logoColor=white">
15
15
  <img alt="Zui 0.0.10" src="https://img.shields.io/badge/Zui-0.0.10-7ee14b?style=flat-square&labelColor=111711">
16
16
  <img alt="241 components" src="https://img.shields.io/badge/components-241-75d943?style=flat-square&labelColor=111711">
@@ -46,8 +46,8 @@ and the Omarchy adapter.
46
46
  </table>
47
47
 
48
48
  > [!IMPORTANT]
49
- > Omarchy UI currently targets **Omarchy on x86-64 Linux** and is marked experimental. The reusable
50
- > Zui application module can also run with Zui's native host on Linux, macOS, and Windows.
49
+ > Omarchy UI currently targets **Omarchy on x86-64 Linux**. Reusable Zui application modules can
50
+ > also run through Zui's native host on Linux, macOS, and Windows.
51
51
 
52
52
  ## Choose a workflow
53
53
 
@@ -59,15 +59,16 @@ and the Omarchy adapter.
59
59
 
60
60
  ## Requirements
61
61
 
62
- - Omarchy on x86-64 Linux;
63
- - Ruby 3.1 or newer while installing the gem and developing applications;
64
- - optional Qt modules for components that use features such as Quick 3D or Multimedia.
62
+ - Omarchy on x86-64 Linux
63
+ - Ruby 3.1 or newer for installation and development
64
+ - Qt 6 development tools, CMake, Ninja, and a C++ compiler for `bundle`, `validate`, and `push`
65
+ - Optional Qt modules for components that use features such as Quick 3D or Multimedia
65
66
 
66
- Omarchy UI generates a complete Omarchy-compatible application or plugin package from the
67
- developer's Ruby and assets. It owns the Omarchy metadata, entry points, adapter, validation,
68
- tree-shaken Zui QML, and embedded mruby runtime. The finished package does not need Ruby or framework
69
- gems. A component fails with an explicit error when its required Qt module or resource is unavailable;
70
- Omarchy UI never substitutes a different component.
67
+ Omarchy UI turns Ruby and assets into an Omarchy-compatible application or plugin. It owns the
68
+ metadata, entry points, adapter, validation, tree-shaken and AOT-compiled Zui QML, and embedded mruby
69
+ runtime. Omarchy UI invokes the build tools; projects do not contain CMake files or distribution
70
+ scripts. The finished package does not require Ruby or framework gems. Components report a clear
71
+ error when a required Qt module or resource is unavailable.
71
72
 
72
73
  ## Quick start
73
74
 
@@ -79,7 +80,7 @@ gem install omarchy-ui
79
80
 
80
81
  The gem installs the compatible Zui version as a dependency.
81
82
 
82
- ### 2. Generate a complete project
83
+ ### 2. Generate a project
83
84
 
84
85
  ```bash
85
86
  omarchy_ui new Counter
@@ -93,13 +94,12 @@ omarchy_ui new "System Status" --plugin
93
94
  cd system-status
94
95
  ```
95
96
 
96
- Both commands create a ready-to-run Ruby project. Omarchy UI generates the complete compliant
97
- application or plugin package; developers do not write QML, shell entry points, or an Omarchy
98
- manifest by hand.
97
+ Both commands create a ready-to-run Ruby project. Developers write the interface and behavior;
98
+ Omarchy UI generates the Omarchy package without application-owned QML, shell entry points, or
99
+ handwritten manifests.
99
100
 
100
- Each project includes a Ruby-native `config.rb` containing its type, identity, release version,
101
- entry point, and—for plugins—public marketplace metadata. `bundle`, `validate`, and `push` read this
102
- single configuration when assembling the complete package.
101
+ Each project includes a Ruby-native `config.rb` with its type, identity, version, entry point, and
102
+ plugin metadata. `bundle`, `validate`, and `push` use this single configuration.
103
103
 
104
104
  The generator creates a small, host-independent project:
105
105
 
@@ -120,8 +120,8 @@ counter/
120
120
  omarchy_ui run main.rb
121
121
  ```
122
122
 
123
- `run` bundles the project's Ruby entry point, asks Zui to tree-shake the QML runtime, prepares the Omarchy host,
124
- then launches it through Quickshell.
123
+ `run` bundles the project's Ruby entry point, asks Zui to tree-shake the QML runtime, prepares the
124
+ Omarchy host, and launches it through Quickshell.
125
125
 
126
126
  ## Application model
127
127
 
@@ -246,16 +246,15 @@ QML is not required.
246
246
 
247
247
  ## Omarchy plugins
248
248
 
249
- Generate a complete plugin project with one command:
249
+ Generate a plugin project with one command:
250
250
 
251
251
  ```bash
252
252
  omarchy_ui new "System Status" --plugin
253
253
  cd system-status
254
254
  ```
255
255
 
256
- The generated project already satisfies Omarchy's structural requirements. Developers work in Ruby
257
- and add their own assets; Omarchy UI owns the required plugin metadata, shell surfaces, native host,
258
- tree-shaken Zui package, validation, and install lifecycle.
256
+ Developers work in Ruby and add their own assets. Omarchy UI supplies the plugin metadata, shell
257
+ surfaces, native host, tree-shaken Zui package, validation, and installation lifecycle.
259
258
 
260
259
  The Ruby entry point uses ordinary Zui components:
261
260
 
@@ -282,8 +281,8 @@ OmarchyUI.plugin do
282
281
  end
283
282
  ```
284
283
 
285
- `bar_widget` and `panel :status` are the complete developer-facing surface declarations. Omarchy UI
286
- maps them to the required Omarchy shell entry points when it builds or stages the plugin.
284
+ `bar_widget` and `panel :status` are the developer-facing surface declarations. Omarchy UI maps them
285
+ to the required shell entry points when it builds or stages the plugin.
287
286
 
288
287
  ### Validate and install
289
288
 
@@ -292,8 +291,21 @@ omarchy_ui validate
292
291
  omarchy_ui push
293
292
  ```
294
293
 
295
- `validate` generates and checks the complete plugin without mutating the Ruby project. `push` safely
296
- builds, validates, installs, enables, and reloads the plugin for development.
294
+ `validate` generates and checks the plugin without modifying the Ruby project. `push` builds,
295
+ validates, installs, enables, and reloads it for development.
296
+
297
+ To publish, target a separate public distribution repository. Omarchy UI rebuilds the thin package,
298
+ enforces the marketplace contract, pushes it, and prints the complete submission for review:
299
+
300
+ ```bash
301
+ omarchy_ui publish --repo YOUR_GITHUB_NAME/PLUGIN_REPOSITORY \
302
+ --category System --tags system,quickshell --create
303
+ ```
304
+
305
+ After reviewing the printed title, metadata, and five marketplace attestations, rerun the same
306
+ command with `--submit`. It creates a submission or refreshes an existing open one. This two-step
307
+ flow prevents accidental replacement of the source repository and ensures the ownership statement
308
+ is reviewed before it is posted.
297
309
 
298
310
  During development, suppress either lifecycle action when needed:
299
311
 
@@ -302,10 +314,6 @@ omarchy_ui push --no-enable
302
314
  omarchy_ui push --no-restart
303
315
  ```
304
316
 
305
- > [!WARNING]
306
- > Omarchy plugins execute as unsandboxed user code inside the shell environment. Review the Ruby,
307
- > assets, generated package, and provenance before installing a third-party plugin.
308
-
309
317
  ## Command-line reference
310
318
 
311
319
  ```text
@@ -314,11 +322,12 @@ omarchy_ui <command> [arguments]
314
322
 
315
323
  | Command | Arguments | Result |
316
324
  | --- | --- | --- |
317
- | `omarchy_ui new NAME` | One project name | Creates a complete Omarchy application project |
318
- | `omarchy_ui new NAME --plugin` | One project name and `--plugin` | Creates a complete Omarchy-compliant plugin project |
325
+ | `omarchy_ui new NAME` | One project name | Creates an Omarchy application project |
326
+ | `omarchy_ui new NAME --plugin --github USER` | One project name, `--plugin`, and optional GitHub user | Creates a marketplace-ready Omarchy plugin project |
319
327
  | `omarchy_ui run FILE` | One Ruby entry point | Runs an application through the Omarchy/Quickshell host |
320
- | `omarchy_ui bundle [DIRECTORY]` | Zero or one project directory | Generates the complete distributable package and prints its location |
321
- | `omarchy_ui validate [DIRECTORY]` | Zero or one plugin directory | Stages the complete plugin and delegates validation to `omarchy plugin validate` |
328
+ | `omarchy_ui bundle [DIRECTORY]` | Zero or one project directory | Generates the distributable package and prints its location |
329
+ | `omarchy_ui publish [DIRECTORY]` | Required `--repo`, `--category`, `--tags`; optional `--create`, `--submit` | Builds and pushes a thin distribution repository, then previews or submits its marketplace issue |
330
+ | `omarchy_ui validate [DIRECTORY]` | Zero or one plugin directory | Stages the plugin and delegates validation to `omarchy plugin validate` |
322
331
  | `omarchy_ui push [DIRECTORY]` | Optional `--no-enable` and `--no-restart` | Atomically installs and activates a validated plugin |
323
332
  | `omarchy_ui version` | None | Prints the installed Omarchy UI version |
324
333
 
@@ -327,19 +336,29 @@ not forward arbitrary Ruby arguments.
327
336
 
328
337
  ## Packaging
329
338
 
330
- Create the complete distributable package from the project root:
339
+ Create the distributable package from the project root:
331
340
 
332
341
  ```bash
333
342
  omarchy_ui bundle
334
343
  ```
335
344
 
336
- Omarchy UI prints the generated package location. It automatically assembles the bundled Ruby and
337
- assets, Omarchy metadata and entry points, native host, checksummed runtime provenance, and only the
338
- Zui QML components used by the project. Application packages receive a direct launcher; plugin
339
- packages are validated against Omarchy's requirements before the command succeeds. Omarchy UI pins
340
- Zui 0.0.10 so generated QML exactly matches the Zui core embedded in the deterministic mruby runtime.
341
- The source `config.rb` remains the authoritative build configuration; plugin manifests are generated
342
- from it in the distributable package and are never maintained separately by the developer.
345
+ Omarchy UI assembles one bundled Ruby program and required assets, native runtime, manifest, and only
346
+ the Zui components used by the project. It AOT-compiles the generated QML into a content-addressed Qt
347
+ module, then discards the generated component, control, theme, and font source tree. Applications
348
+ receive a direct launcher; plugins must pass Omarchy validation before the command succeeds. Omarchy
349
+ UI pins Zui 0.0.10 so the compiled UI matches the Zui core embedded in the deterministic mruby
350
+ runtime.
351
+
352
+ Current Omarchy entry points resolve by filename, so a package retains only the required generated
353
+ loader shims. Applications keep `App.qml`; standard plugins keep `Service.qml`, `Panel.qml`, and
354
+ `BarWidget.qml`. Each shim contains only an import and a compiled type instance. The application
355
+ interface is authored in Ruby and stored in native `.so` artifacts. Distributable packages do not
356
+ include generated QML source, build reports, audit/provenance folders, checksums, tests, or CI files.
357
+ They also omit `config.rb` and Ruby files already folded into `main.rb`.
358
+ Compiled packages should be built against the Qt version shipped by their target Omarchy release.
359
+
360
+ The source `config.rb` is the build configuration. Omarchy UI generates plugin manifests from it;
361
+ developers never maintain a separate manifest.
343
362
 
344
363
  ## Component catalog
345
364
 
@@ -406,10 +425,12 @@ Zui DSL · state · events · bindings · scheduler
406
425
  │
407
426
  │ versioned JSON render, patch, event, and effect protocol
408
427
  ▼
409
- Omarchy Service.qml process bridge ───── embedded mruby runtime
428
+ Omarchy process bridge ───────────────── embedded mruby runtime
410
429
  │
411
430
  ▼
412
- Zui ControlNode + native component catalog
431
+ Tree-shaken Zui host + component catalog
432
+ │
433
+ │ bundle: Qt AOT module + required loader shims
413
434
  │
414
435
  ▼
415
436
  App window · Omarchy panel · Omarchy bar widget
@@ -427,8 +448,9 @@ The dependency direction is deliberate:
427
448
  - **Applications own** Ruby source, assets, state, behavior, and reusable UI modules.
428
449
 
429
450
  At bundle, validation, and push time, Omarchy UI asks Zui to analyze the bundled Ruby syntax and
430
- generate only the required component adapters plus their renderer dependencies. The four Omarchy
431
- host files then replace Zui's generic desktop host. Read the full [QML ownership and adapter
451
+ generate only the required component adapters plus their renderer dependencies. Omarchy UI overlays
452
+ its host, compiles the complete generated QML graph into a content-addressed Qt module, and emits the
453
+ minimal loader files required by Omarchy. Read the full [QML ownership and adapter
432
454
  boundary](docs/qml-support.md).
433
455
 
434
456
  ## Runtime integrity
data/Service.qml CHANGED
@@ -499,4 +499,27 @@ Item {
499
499
  repeat: false
500
500
  onTriggered: root.startRuby()
501
501
  }
502
+
503
+ // A desktop surface is rendered by the same long-lived service that owns
504
+ // the Ruby runtime. Its DesktopStage adapter creates the layer-shell window;
505
+ // plugins keep all scene structure, state, and interaction in Ruby.
506
+ ControlNode {
507
+ id: desktopRenderer
508
+ visible: root.ready && root.rootId("desktop") !== ""
509
+ bridge: root
510
+ surfaceName: "desktop"
511
+ controlId: root.rootId(surfaceName)
512
+ }
513
+
514
+ IpcHandler {
515
+ target: root.manifest && root.manifest.id ? String(root.manifest.id) : "omarchy-ui"
516
+
517
+ function playPause(): string {
518
+ var desktopId = root.rootId("desktop")
519
+ if (desktopId === "") return "not-ready"
520
+ return root.sendEvent("desktop", desktopId, "shortcut", { name: "play_pause" })
521
+ ? "ok"
522
+ : "not-ready"
523
+ }
524
+ }
502
525
  }
@@ -23,11 +23,12 @@ module OmarchyUI
23
23
  when "run" then run_file(arguments)
24
24
  when "new" then new_project(arguments)
25
25
  when "bundle" then bundle_project(arguments)
26
+ when "publish" then publish(arguments)
26
27
  when "push" then push(arguments)
27
28
  when "validate" then validate(arguments)
28
29
  when "version", "--version", "-v" then @out.puts(OmarchyUI::VERSION); 0
29
30
  else
30
- @err.puts("Usage: omarchy_ui <new NAME [--plugin]|run FILE|bundle [DIRECTORY]|push [DIRECTORY]|validate [DIRECTORY]|version>")
31
+ @err.puts("Usage: omarchy_ui <new NAME [--plugin] [--github USER]|run FILE|bundle [DIRECTORY]|publish [DIRECTORY] --repo OWNER/REPO --category CATEGORY --tags TAGS [--create] [--submit]|push [DIRECTORY]|validate [DIRECTORY]|version>")
31
32
  command.nil? ? 0 : 64
32
33
  end
33
34
  rescue Interrupt
@@ -68,11 +69,14 @@ module OmarchyUI
68
69
 
69
70
  def new_project(arguments)
70
71
  plugin = !arguments.delete("--plugin").nil?
72
+ github_user = extract_option!(arguments, "--github")
71
73
  name = arguments.shift || raise(ArgumentError, "new requires a project name")
72
- raise ArgumentError, "new accepts a name and optional --plugin" unless arguments.empty?
74
+ raise ArgumentError, "new accepts a name, --plugin, and --github USER" unless arguments.empty?
75
+ raise ArgumentError, "--github is only valid with --plugin" if github_user && !plugin
73
76
  destination = File.expand_path(slug(name))
74
77
  kind = plugin ? :plugin : :application
75
- Generator.new(path: destination, name: name, kind:, author: default_author).create
78
+ github_user ||= default_github_user if plugin
79
+ Generator.new(path: destination, name: name, kind:, author: default_author, github_user:).create
76
80
  label = plugin ? "Omarchy-compliant plugin" : "self-contained Omarchy application"
77
81
  @out.puts("Created #{label} in #{destination}")
78
82
  0
@@ -91,6 +95,30 @@ module OmarchyUI
91
95
  "Omarchy UI Developer"
92
96
  end
93
97
 
98
+ def default_github_user
99
+ configured = ENV["OMARCHY_UI_GITHUB_USER"].to_s.strip
100
+ return configured unless configured.empty?
101
+
102
+ output, _error, status = Open3.capture3("gh", "api", "user", "--jq", ".login")
103
+ login = output.to_s.strip
104
+ return login if status.success? && !login.empty?
105
+
106
+ raise ArgumentError,
107
+ "plugin generation needs --github USER or OMARCHY_UI_GITHUB_USER (gh auth login can provide it automatically)"
108
+ rescue Errno::ENOENT
109
+ raise ArgumentError, "plugin generation needs --github USER or OMARCHY_UI_GITHUB_USER"
110
+ end
111
+
112
+ def extract_option!(arguments, name)
113
+ index = arguments.index(name)
114
+ return nil unless index
115
+
116
+ value = arguments[index + 1]
117
+ raise ArgumentError, "#{name} requires a value" if value.nil? || value.start_with?("--")
118
+ arguments.slice!(index, 2)
119
+ value
120
+ end
121
+
94
122
  def slug(value)
95
123
  result = value.to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-|\-\z/, "")
96
124
  raise ArgumentError, "name must contain letters or numbers" if result.empty?
@@ -114,16 +142,21 @@ module OmarchyUI
114
142
  config = ProjectConfig.load(source)
115
143
  entrypoint = config&.entrypoint || "main.rb"
116
144
  raise ArgumentError, "#{entrypoint} not found: #{source}" unless File.file?(File.join(source, entrypoint))
145
+ precompiled = config.nil? && compiled_package?(source)
117
146
  destination = File.join(source, "dist", config&.slug || File.basename(source))
118
147
  raise ArgumentError, "bundle destination already exists: #{destination}" if File.exist?(destination)
119
148
  FileUtils.mkdir_p(destination)
120
- entries = application_entries(source, config:)
149
+ entries = precompiled ? package_entries(source) : application_entries(source, config:)
121
150
  FileUtils.cp_r(entries.map { |entry| File.join(source, entry) }, destination)
122
- config.write_manifest(destination) if config&.plugin?
123
- bundle_ruby_entrypoint(source, destination, entrypoint:)
124
- Runtime.install_package(destination)
151
+ unless precompiled
152
+ config.write_manifest(destination) if config&.plugin?
153
+ bundle_ruby_entrypoint(source, destination, entrypoint:)
154
+ Runtime.install_package(destination, compiled: true)
155
+ end
156
+ prune_bundled_ruby_sources(destination, entrypoint:)
125
157
  plugin = config ? config.plugin? : File.file?(File.join(destination, "manifest.json"))
126
158
  if plugin
159
+ PluginPackage.validate!(destination)
127
160
  raise ArgumentError, "bundled plugin validation failed" unless system("omarchy", "plugin", "validate", destination)
128
161
  @out.puts("Bundled plugin in #{destination}")
129
162
  return 0
@@ -140,6 +173,8 @@ module OmarchyUI
140
173
  export OMARCHY_UI_RUNTIME="$app_dir/omarchy-ui-runtime"
141
174
  export OMARCHY_UI_PROJECT_DIR="$app_dir"
142
175
  export OMARCHY_UI_RUBY_PROGRAM="$app_dir/#{entrypoint}"
176
+ export QML2_IMPORT_PATH="$app_dir${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}"
177
+ export QML_IMPORT_PATH="$app_dir${QML_IMPORT_PATH:+:$QML_IMPORT_PATH}"
143
178
  export QT_LOGGING_RULES="${QT_LOGGING_RULES:+$QT_LOGGING_RULES;}qt.qpa.services.warning=false"
144
179
  exec quickshell --path "$app_dir/App.qml"
145
180
  SH
@@ -156,9 +191,54 @@ module OmarchyUI
156
191
  end
157
192
 
158
193
  def application_entries(source, config: ProjectConfig.load(source))
159
- generated = Runtime::GENERATED_ENTRIES + Runtime::AUDIT_FILES + %w[run Commons Ui]
194
+ generated = Runtime::GENERATED_ENTRIES + %w[run Commons Ui]
160
195
  generated.concat([ProjectConfig::FILE_NAME, "manifest.json"]) if config
161
- Dir.children(source).reject { |entry| %w[.git dist].include?(entry) || generated.include?(entry) }
196
+ Dir.children(source).reject do |entry|
197
+ (PluginPackage::DEVELOPMENT_ENTRIES + PluginPackage::SOURCE_CONTROL_ENTRIES).include?(entry) ||
198
+ generated.include?(entry)
199
+ end
200
+ end
201
+
202
+ def package_entries(source)
203
+ excluded = PluginPackage::DEVELOPMENT_ENTRIES + PluginPackage::SOURCE_CONTROL_ENTRIES +
204
+ Runtime::LEGACY_METADATA_FILES
205
+ Dir.children(source).reject { |entry| excluded.include?(entry) }
206
+ end
207
+
208
+ def compiled_package?(source)
209
+ compiled_root = File.join(source, QmlCompiler::COMPILED_ROOT, "Bundles")
210
+ shims = QmlCompiler::ENTRY_TYPES.keys.any? { |entry| File.file?(File.join(source, entry)) }
211
+ return false unless File.directory?(compiled_root) && shims
212
+
213
+ QmlCompiler.verify!(source)
214
+ true
215
+ end
216
+
217
+ def publish(arguments)
218
+ repository = extract_option!(arguments, "--repo")
219
+ category = extract_option!(arguments, "--category")
220
+ tags = extract_option!(arguments, "--tags").to_s.split(",")
221
+ create = !arguments.delete("--create").nil?
222
+ submit = !arguments.delete("--submit").nil?
223
+ source = File.expand_path(arguments.shift || Dir.pwd)
224
+ raise ArgumentError, "publish accepts one directory and publish options" unless arguments.empty?
225
+ config = ProjectConfig.load(source)
226
+ raise ArgumentError, "publish requires an Omarchy UI plugin project" unless config&.plugin?
227
+
228
+ with_staged_project(source) do |staging|
229
+ raise ArgumentError, "staged plugin validation failed" unless system("omarchy", "plugin", "validate", staging)
230
+ PluginPublisher.publish!(
231
+ package: staging,
232
+ source:,
233
+ repository:,
234
+ category:,
235
+ tags:,
236
+ create:,
237
+ submit:,
238
+ out: @out
239
+ )
240
+ end
241
+ 0
162
242
  end
163
243
 
164
244
  def push(arguments)
@@ -213,15 +293,20 @@ module OmarchyUI
213
293
  def stage_project(source, parent: nil, prefix: ".omarchy-ui-staging-")
214
294
  raise ArgumentError, "project directory not found: #{source}" unless File.directory?(source)
215
295
  config = ProjectConfig.load(source)
296
+ precompiled = config.nil? && compiled_package?(source)
216
297
  staging = parent ? Dir.mktmpdir(prefix, parent) : Dir.mktmpdir(prefix)
217
- entries = application_entries(source, config:)
298
+ entries = precompiled ? package_entries(source) : application_entries(source, config:)
218
299
  FileUtils.cp_r(entries.map { |entry| File.join(source, entry) }, staging) unless entries.empty?
219
- config.write_manifest(staging) if config&.plugin?
220
- entrypoint = config&.entrypoint || "main.rb"
221
- if File.file?(File.join(staging, entrypoint))
222
- bundle_ruby_entrypoint(source, staging, entrypoint:)
223
- Runtime.install_package(staging)
300
+ unless precompiled
301
+ config.write_manifest(staging) if config&.plugin?
302
+ entrypoint = config&.entrypoint || "main.rb"
303
+ if File.file?(File.join(staging, entrypoint))
304
+ bundle_ruby_entrypoint(source, staging, entrypoint:)
305
+ Runtime.install_package(staging, compiled: true)
306
+ end
224
307
  end
308
+ prune_bundled_ruby_sources(staging, entrypoint: "main.rb") if File.file?(File.join(staging, "manifest.json"))
309
+ PluginPackage.validate!(staging) if File.file?(File.join(staging, "manifest.json"))
225
310
  staging
226
311
  rescue StandardError
227
312
  FileUtils.remove_entry(staging) if staging && File.exist?(staging)
@@ -235,6 +320,17 @@ module OmarchyUI
235
320
  File.write(destination_entrypoint, SourceBundle.new(source_entrypoint, root: source).call)
236
321
  end
237
322
 
323
+ def prune_bundled_ruby_sources(destination, entrypoint: "main.rb")
324
+ kept = File.expand_path(entrypoint, destination)
325
+ Dir.glob(File.join(destination, "**", "*.rb"), File::FNM_DOTMATCH).each do |path|
326
+ FileUtils.rm_f(path) unless File.expand_path(path) == kept
327
+ end
328
+ directories = Dir.glob(File.join(destination, "**", "*"), File::FNM_DOTMATCH)
329
+ .select { |path| File.directory?(path) && !File.symlink?(path) }
330
+ .sort_by { |path| -path.count(File::SEPARATOR) }
331
+ directories.each { |path| Dir.rmdir(path) if Dir.empty?(path) }
332
+ end
333
+
238
334
  def activate_plugin(plugin_id)
239
335
  return if system("omarchy", "plugin", "enable", plugin_id)
240
336
  system("omarchy-shell", "shell", "rescanPlugins")
@@ -5,12 +5,17 @@ require "fileutils"
5
5
  module OmarchyUI
6
6
  class Generator
7
7
  PROJECT_KINDS = %i[application plugin].freeze
8
+ GITHUB_USER = /\A(?!-)[A-Za-z0-9-]{1,39}(?<!-)\z/
8
9
 
9
- def initialize(path:, name: nil, kind: :application, plugin_id: nil, author: nil)
10
+ def initialize(path:, name: nil, kind: :application, plugin_id: nil, author: nil, github_user: nil)
10
11
  @path = File.expand_path(path)
11
12
  @name = name || File.basename(@path).split(/[-_]/).map(&:capitalize).join(" ")
12
13
  @kind = kind.to_sym
13
- @plugin_id = plugin_id || "local.#{project_slug}"
14
+ @github_user = github_user.to_s.strip
15
+ if plugin? && !GITHUB_USER.match?(@github_user)
16
+ raise ArgumentError, "plugin generation needs a valid GitHub username"
17
+ end
18
+ @plugin_id = plugin_id || "io.github.#{@github_user.downcase}.#{project_slug}"
14
19
  @author = author.to_s.strip.empty? ? "Omarchy UI Developer" : author.to_s.strip
15
20
  raise ArgumentError, "unsupported project kind: #{@kind}" unless PROJECT_KINDS.include?(@kind)
16
21
  end
@@ -124,6 +129,9 @@ module OmarchyUI
124
129
 
125
130
  A pure Ruby desktop application built with Zui and the Omarchy UI host.
126
131
 
132
+ The interface and behavior are authored entirely in Ruby. Omarchy UI generates QML only
133
+ as a temporary build input, compiles it, and ships no handwritten QML source.
134
+
127
135
  ## Run
128
136
 
129
137
  ```bash
@@ -139,7 +147,10 @@ module OmarchyUI
139
147
  omarchy_ui bundle
140
148
  ```
141
149
 
142
- Omarchy UI generates the complete self-contained application package required by Omarchy.
150
+ Omarchy UI tree-shakes and compiles the UI, then generates the complete self-contained
151
+ application package required by Omarchy. The package contains the bundled Ruby program,
152
+ native runtime, compiled Qt module, and one generated `App.qml` loader shim—no build reports
153
+ or generated QML source tree.
143
154
  MARKDOWN
144
155
  end
145
156
 
@@ -149,6 +160,26 @@ module OmarchyUI
149
160
 
150
161
  A pure Ruby Omarchy plugin built with Omarchy UI and Zui.
151
162
 
163
+ The interface and behavior are authored entirely in Ruby. Omarchy UI generates QML only
164
+ as a temporary build input, compiles it, and ships no handwritten QML source.
165
+
166
+ ## Install
167
+
168
+ ```bash
169
+ omarchy plugin add https://github.com/#{@github_user}/#{project_slug}.git --enable
170
+ ```
171
+
172
+ ## Remove
173
+
174
+ ```bash
175
+ omarchy plugin remove #{@plugin_id}
176
+ ```
177
+
178
+ ## Dependencies
179
+
180
+ - Omarchy Quattro on x86-64 Linux
181
+ - No additional runtime dependency in the generated starter
182
+
152
183
  ## Validate
153
184
 
154
185
  ```bash
@@ -168,8 +199,11 @@ module OmarchyUI
168
199
  ```
169
200
 
170
201
  Write the plugin interface and behavior in Ruby under `main.rb` and `components/`.
171
- Public plugin identity and release metadata live in `config.rb`. Omarchy UI uses it to
172
- generate and validate the complete Omarchy-compatible plugin package.
202
+ Public plugin identity and release metadata live in `config.rb`. Omarchy UI tree-shakes and
203
+ compiles the UI, then generates and validates the complete Omarchy-compatible plugin package.
204
+ The package contains the bundled Ruby program, native runtime, compiled Qt module, manifest,
205
+ and only the generated loader shims named by that manifest—no build reports, audit folder,
206
+ tests, or generated QML source tree.
173
207
  MARKDOWN
174
208
  end
175
209