omarchy-ui 0.0.7-x86_64-linux → 0.0.8-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 +4 -4
- data/README.md +57 -93
- data/lib/omarchy_ui/cli.rb +51 -18
- data/lib/omarchy_ui/generator.rb +129 -3
- data/lib/omarchy_ui/project_config.rb +207 -0
- data/lib/omarchy_ui.rb +2 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bbe75336899d8f9674166011c929469db798869f88b32f45a3b61505b0713d01
|
|
4
|
+
data.tar.gz: bb5f2e4f0dfec4578992d17d5f3fdc5530d5b2c73d4aeebcfada553355077f94
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9776119537e37548ea84ca4129647a6f94b64944ae75315e776db661220aefeb4cc80527ed93a1b1c9410e0b80d2a547c77a2aae6674f770decc354bdee2a505
|
|
7
|
+
data.tar.gz: 6856581d58533845967d37daae2679cbf9601f9555aa724b9a9cbf04da0a62a8b15cd146df6d3b36551dec85ad29a13189ba1bdb01a143c2a3cfed87f46d2bb4
|
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.
|
|
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">
|
|
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">
|
|
@@ -53,8 +53,8 @@ and the Omarchy adapter.
|
|
|
53
53
|
|
|
54
54
|
| Build | Use it for | Primary surface | Delivery |
|
|
55
55
|
| --- | --- | --- | --- |
|
|
56
|
-
| Omarchy application | Full desktop tools and standalone interfaces | `app :main` | `omarchy_ui run` or `
|
|
57
|
-
| Omarchy plugin | Bar widgets, panels, and shell-integrated tools | `bar_widget` and `panel` | `omarchy_ui
|
|
56
|
+
| Omarchy application | Full desktop tools and standalone interfaces | `app :main` | `omarchy_ui new NAME`, then `run` or `bundle` |
|
|
57
|
+
| Omarchy plugin | Bar widgets, panels, and shell-integrated tools | `bar_widget` and `panel` | `omarchy_ui new NAME --plugin`, then `push` or `bundle` |
|
|
58
58
|
| Portable Zui application | The same application UI outside Omarchy | `app :main` | The platform-neutral [Zui toolchain](https://github.com/AdamMusa/zui#developer-workflow) |
|
|
59
59
|
|
|
60
60
|
## Requirements
|
|
@@ -63,10 +63,11 @@ and the Omarchy adapter.
|
|
|
63
63
|
- Ruby 3.1 or newer while installing the gem and developing applications;
|
|
64
64
|
- optional Qt modules for components that use features such as Quick 3D or Multimedia.
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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.
|
|
70
71
|
|
|
71
72
|
## Quick start
|
|
72
73
|
|
|
@@ -78,20 +79,37 @@ gem install omarchy-ui
|
|
|
78
79
|
|
|
79
80
|
The gem installs the compatible Zui version as a dependency.
|
|
80
81
|
|
|
81
|
-
### 2. Generate
|
|
82
|
+
### 2. Generate a complete project
|
|
82
83
|
|
|
83
84
|
```bash
|
|
84
85
|
omarchy_ui new Counter
|
|
85
86
|
cd counter
|
|
86
87
|
```
|
|
87
88
|
|
|
89
|
+
For an integrated Omarchy plugin instead:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
omarchy_ui new "System Status" --plugin
|
|
93
|
+
cd system-status
|
|
94
|
+
```
|
|
95
|
+
|
|
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.
|
|
99
|
+
|
|
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.
|
|
103
|
+
|
|
88
104
|
The generator creates a small, host-independent project:
|
|
89
105
|
|
|
90
106
|
```text
|
|
91
107
|
counter/
|
|
108
|
+
├── LICENSE
|
|
92
109
|
├── app.rb # reusable Zui application
|
|
93
110
|
├── components/
|
|
94
111
|
│ └── welcome.rb # application-owned Ruby UI module
|
|
112
|
+
├── config.rb # project identity and build settings
|
|
95
113
|
├── main.rb # Omarchy launcher
|
|
96
114
|
└── README.md
|
|
97
115
|
```
|
|
@@ -228,48 +246,18 @@ QML is not required.
|
|
|
228
246
|
|
|
229
247
|
## Omarchy plugins
|
|
230
248
|
|
|
231
|
-
|
|
232
|
-
at its root. Omarchy UI adds the generated service, panel, bar-widget, framework, and runtime files
|
|
233
|
-
while staging the plugin; do not commit generated copies to the application source.
|
|
249
|
+
Generate a complete plugin project with one command:
|
|
234
250
|
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
├── main.rb
|
|
239
|
-
└── assets/
|
|
251
|
+
```bash
|
|
252
|
+
omarchy_ui new "System Status" --plugin
|
|
253
|
+
cd system-status
|
|
240
254
|
```
|
|
241
255
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
```json
|
|
247
|
-
{
|
|
248
|
-
"schemaVersion": 1,
|
|
249
|
-
"id": "example.system-status",
|
|
250
|
-
"name": "System Status",
|
|
251
|
-
"version": "0.1.0",
|
|
252
|
-
"author": "Example Author",
|
|
253
|
-
"license": "MIT",
|
|
254
|
-
"description": "A Ruby-powered status panel for Omarchy.",
|
|
255
|
-
"kinds": ["service", "bar-widget", "panel"],
|
|
256
|
-
"keepLoaded": true,
|
|
257
|
-
"entryPoints": {
|
|
258
|
-
"service": "Service.qml",
|
|
259
|
-
"barWidget": "BarWidget.qml",
|
|
260
|
-
"panel": "Panel.qml"
|
|
261
|
-
},
|
|
262
|
-
"barWidget": {
|
|
263
|
-
"displayName": "System Status",
|
|
264
|
-
"description": "Open the current system status.",
|
|
265
|
-
"category": "System",
|
|
266
|
-
"allowMultiple": false,
|
|
267
|
-
"defaultSection": "right"
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
```
|
|
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.
|
|
271
259
|
|
|
272
|
-
|
|
260
|
+
The Ruby entry point uses ordinary Zui components:
|
|
273
261
|
|
|
274
262
|
```ruby
|
|
275
263
|
# main.rb
|
|
@@ -294,35 +282,29 @@ OmarchyUI.plugin do
|
|
|
294
282
|
end
|
|
295
283
|
```
|
|
296
284
|
|
|
297
|
-
`bar_widget`
|
|
298
|
-
|
|
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.
|
|
299
287
|
|
|
300
288
|
### Validate and install
|
|
301
289
|
|
|
302
290
|
```bash
|
|
303
|
-
omarchy_ui validate
|
|
304
|
-
omarchy_ui push
|
|
291
|
+
omarchy_ui validate
|
|
292
|
+
omarchy_ui push
|
|
305
293
|
```
|
|
306
294
|
|
|
307
|
-
`validate` checks
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
1. stages and validates the complete plugin;
|
|
311
|
-
2. backs up an existing installation when present;
|
|
312
|
-
3. installs it atomically under `~/.config/omarchy/plugins/<plugin-id>`;
|
|
313
|
-
4. enables the plugin; and
|
|
314
|
-
5. restarts the Omarchy shell.
|
|
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.
|
|
315
297
|
|
|
316
298
|
During development, suppress either lifecycle action when needed:
|
|
317
299
|
|
|
318
300
|
```bash
|
|
319
|
-
omarchy_ui push
|
|
320
|
-
omarchy_ui push
|
|
301
|
+
omarchy_ui push --no-enable
|
|
302
|
+
omarchy_ui push --no-restart
|
|
321
303
|
```
|
|
322
304
|
|
|
323
305
|
> [!WARNING]
|
|
324
306
|
> Omarchy plugins execute as unsandboxed user code inside the shell environment. Review the Ruby,
|
|
325
|
-
> assets,
|
|
307
|
+
> assets, generated package, and provenance before installing a third-party plugin.
|
|
326
308
|
|
|
327
309
|
## Command-line reference
|
|
328
310
|
|
|
@@ -332,9 +314,10 @@ omarchy_ui <command> [arguments]
|
|
|
332
314
|
|
|
333
315
|
| Command | Arguments | Result |
|
|
334
316
|
| --- | --- | --- |
|
|
335
|
-
| `omarchy_ui new NAME` | One
|
|
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 |
|
|
336
319
|
| `omarchy_ui run FILE` | One Ruby entry point | Runs an application through the Omarchy/Quickshell host |
|
|
337
|
-
| `omarchy_ui bundle [DIRECTORY]` | Zero or one project directory |
|
|
320
|
+
| `omarchy_ui bundle [DIRECTORY]` | Zero or one project directory | Generates the complete distributable package and prints its location |
|
|
338
321
|
| `omarchy_ui validate [DIRECTORY]` | Zero or one plugin directory | Stages the complete plugin and delegates validation to `omarchy plugin validate` |
|
|
339
322
|
| `omarchy_ui push [DIRECTORY]` | Optional `--no-enable` and `--no-restart` | Atomically installs and activates a validated plugin |
|
|
340
323
|
| `omarchy_ui version` | None | Prints the installed Omarchy UI version |
|
|
@@ -344,38 +327,19 @@ not forward arbitrary Ruby arguments.
|
|
|
344
327
|
|
|
345
328
|
## Packaging
|
|
346
329
|
|
|
347
|
-
Create
|
|
330
|
+
Create the complete distributable package from the project root:
|
|
348
331
|
|
|
349
332
|
```bash
|
|
350
333
|
omarchy_ui bundle
|
|
351
|
-
./dist/my-application/run
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
The bundle contains:
|
|
355
|
-
|
|
356
|
-
```text
|
|
357
|
-
dist/my-application/
|
|
358
|
-
├── main.rb # bundled application source
|
|
359
|
-
├── assets/ # application-owned resources
|
|
360
|
-
├── App.qml # Omarchy host entry point
|
|
361
|
-
├── Service.qml # process and surface bridge
|
|
362
|
-
├── Panel.qml # shell panel adapter
|
|
363
|
-
├── BarWidget.qml # shell bar adapter
|
|
364
|
-
├── ControlNode.qml # generated Zui protocol renderer
|
|
365
|
-
├── Components/Builtins/ # only component adapters selected by Zui
|
|
366
|
-
├── Controls/ Theme/ Fonts/ # generated renderer dependencies
|
|
367
|
-
├── zui-tree-shake.json # auditable selected-component report
|
|
368
|
-
├── Commons@ Ui@ # links to Omarchy's shell modules
|
|
369
|
-
├── omarchy-ui-runtime # embedded mruby executable
|
|
370
|
-
├── omarchy-ui-runtime.sha256
|
|
371
|
-
├── RUNTIME_PROVENANCE.md
|
|
372
|
-
└── run
|
|
373
334
|
```
|
|
374
335
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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.
|
|
379
343
|
|
|
380
344
|
## Component catalog
|
|
381
345
|
|
|
@@ -504,11 +468,11 @@ bodies are never recorded. Per-plugin logs rotate at 128 KiB under
|
|
|
504
468
|
| `Ruby file not found` | Pass an existing entry point to `omarchy_ui run`, normally `main.rb` |
|
|
505
469
|
| `Omarchy QML module not found` | Confirm the Omarchy shell installation provides `/usr/share/omarchy/shell/Commons` and `Ui` |
|
|
506
470
|
| A component reports a missing module | Install or enable the Qt module required by that declared component |
|
|
507
|
-
| Plugin validation fails | Run `omarchy_ui validate` and correct the
|
|
471
|
+
| Plugin validation fails | Run `omarchy_ui validate` and correct the reported Ruby, asset, or generated-metadata error before pushing |
|
|
508
472
|
| A pushed plugin is installed but inactive | Run `omarchy plugin enable <id>` and `omarchy restart shell` after resolving the reported shell error |
|
|
509
|
-
| The bundle destination already exists | Review or move the
|
|
473
|
+
| The bundle destination already exists | Review or move the previously generated package before bundling again |
|
|
510
474
|
|
|
511
|
-
Omarchy UI reports unsupported properties, components, events, paths,
|
|
475
|
+
Omarchy UI reports unsupported properties, components, events, paths, generated metadata, and runtime
|
|
512
476
|
resources directly. Preserve the original error when opening an issue.
|
|
513
477
|
|
|
514
478
|
## Development
|
data/lib/omarchy_ui/cli.rb
CHANGED
|
@@ -27,7 +27,7 @@ module OmarchyUI
|
|
|
27
27
|
when "validate" then validate(arguments)
|
|
28
28
|
when "version", "--version", "-v" then @out.puts(OmarchyUI::VERSION); 0
|
|
29
29
|
else
|
|
30
|
-
@err.puts("Usage: omarchy_ui <new NAME|run FILE|bundle [DIRECTORY]|push [DIRECTORY]|validate [DIRECTORY]|version>")
|
|
30
|
+
@err.puts("Usage: omarchy_ui <new NAME [--plugin]|run FILE|bundle [DIRECTORY]|push [DIRECTORY]|validate [DIRECTORY]|version>")
|
|
31
31
|
command.nil? ? 0 : 64
|
|
32
32
|
end
|
|
33
33
|
rescue Interrupt
|
|
@@ -67,14 +67,30 @@ module OmarchyUI
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def new_project(arguments)
|
|
70
|
+
plugin = !arguments.delete("--plugin").nil?
|
|
70
71
|
name = arguments.shift || raise(ArgumentError, "new requires a project name")
|
|
71
|
-
raise ArgumentError, "new accepts
|
|
72
|
+
raise ArgumentError, "new accepts a name and optional --plugin" unless arguments.empty?
|
|
72
73
|
destination = File.expand_path(slug(name))
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
kind = plugin ? :plugin : :application
|
|
75
|
+
Generator.new(path: destination, name: name, kind:, author: default_author).create
|
|
76
|
+
label = plugin ? "Omarchy-compliant plugin" : "self-contained Omarchy application"
|
|
77
|
+
@out.puts("Created #{label} in #{destination}")
|
|
75
78
|
0
|
|
76
79
|
end
|
|
77
80
|
|
|
81
|
+
def default_author
|
|
82
|
+
configured = ENV["OMARCHY_UI_AUTHOR"].to_s.strip
|
|
83
|
+
return configured unless configured.empty?
|
|
84
|
+
|
|
85
|
+
output, _error, status = Open3.capture3("git", "config", "--get", "user.name")
|
|
86
|
+
git_author = output.to_s.strip
|
|
87
|
+
return git_author if status.success? && !git_author.empty?
|
|
88
|
+
|
|
89
|
+
ENV["USER"].to_s.strip.then { |user| user.empty? ? "Omarchy UI Developer" : user }
|
|
90
|
+
rescue SystemCallError
|
|
91
|
+
"Omarchy UI Developer"
|
|
92
|
+
end
|
|
93
|
+
|
|
78
94
|
def slug(value)
|
|
79
95
|
result = value.to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-|\-\z/, "")
|
|
80
96
|
raise ArgumentError, "name must contain letters or numbers" if result.empty?
|
|
@@ -83,6 +99,10 @@ module OmarchyUI
|
|
|
83
99
|
|
|
84
100
|
def validate(arguments)
|
|
85
101
|
source = File.expand_path(arguments.shift || Dir.pwd)
|
|
102
|
+
raise ArgumentError, "validate accepts one directory" unless arguments.empty?
|
|
103
|
+
config = ProjectConfig.load(source)
|
|
104
|
+
raise ArgumentError, "validate requires a plugin project" if config&.application?
|
|
105
|
+
|
|
86
106
|
with_staged_project(source) do |staging|
|
|
87
107
|
system("omarchy", "plugin", "validate", staging) ? 0 : 1
|
|
88
108
|
end
|
|
@@ -91,15 +111,19 @@ module OmarchyUI
|
|
|
91
111
|
def bundle_project(arguments)
|
|
92
112
|
source = File.expand_path(arguments.shift || Dir.pwd)
|
|
93
113
|
raise ArgumentError, "bundle accepts one directory" unless arguments.empty?
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
config = ProjectConfig.load(source)
|
|
115
|
+
entrypoint = config&.entrypoint || "main.rb"
|
|
116
|
+
raise ArgumentError, "#{entrypoint} not found: #{source}" unless File.file?(File.join(source, entrypoint))
|
|
117
|
+
destination = File.join(source, "dist", config&.slug || File.basename(source))
|
|
96
118
|
raise ArgumentError, "bundle destination already exists: #{destination}" if File.exist?(destination)
|
|
97
119
|
FileUtils.mkdir_p(destination)
|
|
98
|
-
entries = application_entries(source)
|
|
120
|
+
entries = application_entries(source, config:)
|
|
99
121
|
FileUtils.cp_r(entries.map { |entry| File.join(source, entry) }, destination)
|
|
100
|
-
|
|
122
|
+
config.write_manifest(destination) if config&.plugin?
|
|
123
|
+
bundle_ruby_entrypoint(source, destination, entrypoint:)
|
|
101
124
|
Runtime.install_package(destination)
|
|
102
|
-
|
|
125
|
+
plugin = config ? config.plugin? : File.file?(File.join(destination, "manifest.json"))
|
|
126
|
+
if plugin
|
|
103
127
|
raise ArgumentError, "bundled plugin validation failed" unless system("omarchy", "plugin", "validate", destination)
|
|
104
128
|
@out.puts("Bundled plugin in #{destination}")
|
|
105
129
|
return 0
|
|
@@ -115,7 +139,7 @@ module OmarchyUI
|
|
|
115
139
|
app_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
116
140
|
export OMARCHY_UI_RUNTIME="$app_dir/omarchy-ui-runtime"
|
|
117
141
|
export OMARCHY_UI_PROJECT_DIR="$app_dir"
|
|
118
|
-
export OMARCHY_UI_RUBY_PROGRAM="$app_dir
|
|
142
|
+
export OMARCHY_UI_RUBY_PROGRAM="$app_dir/#{entrypoint}"
|
|
119
143
|
export QT_LOGGING_RULES="${QT_LOGGING_RULES:+$QT_LOGGING_RULES;}qt.qpa.services.warning=false"
|
|
120
144
|
exec quickshell --path "$app_dir/App.qml"
|
|
121
145
|
SH
|
|
@@ -131,8 +155,9 @@ module OmarchyUI
|
|
|
131
155
|
[ENV["QT_LOGGING_RULES"], "qt.qpa.services.warning=false"].compact.reject(&:empty?).join(";")
|
|
132
156
|
end
|
|
133
157
|
|
|
134
|
-
def application_entries(source)
|
|
158
|
+
def application_entries(source, config: ProjectConfig.load(source))
|
|
135
159
|
generated = Runtime::GENERATED_ENTRIES + Runtime::AUDIT_FILES + %w[run Commons Ui]
|
|
160
|
+
generated.concat([ProjectConfig::FILE_NAME, "manifest.json"]) if config
|
|
136
161
|
Dir.children(source).reject { |entry| %w[.git dist].include?(entry) || generated.include?(entry) }
|
|
137
162
|
end
|
|
138
163
|
|
|
@@ -140,8 +165,11 @@ module OmarchyUI
|
|
|
140
165
|
enable = !arguments.delete("--no-enable")
|
|
141
166
|
restart = !arguments.delete("--no-restart")
|
|
142
167
|
source = File.expand_path(arguments.shift || Dir.pwd)
|
|
168
|
+
raise ArgumentError, "push accepts one directory" unless arguments.empty?
|
|
169
|
+
config = ProjectConfig.load(source)
|
|
170
|
+
raise ArgumentError, "push requires a plugin project" if config&.application?
|
|
143
171
|
manifest_path = File.join(source, "manifest.json")
|
|
144
|
-
manifest = JSON.parse(File.read(manifest_path))
|
|
172
|
+
manifest = config ? config.manifest : JSON.parse(File.read(manifest_path))
|
|
145
173
|
plugin_id = manifest.fetch("id")
|
|
146
174
|
raise ArgumentError, "invalid plugin id" unless VALID_ID.match?(plugin_id)
|
|
147
175
|
plugin_root = File.expand_path("~/.config/omarchy/plugins")
|
|
@@ -184,11 +212,14 @@ module OmarchyUI
|
|
|
184
212
|
|
|
185
213
|
def stage_project(source, parent: nil, prefix: ".omarchy-ui-staging-")
|
|
186
214
|
raise ArgumentError, "project directory not found: #{source}" unless File.directory?(source)
|
|
215
|
+
config = ProjectConfig.load(source)
|
|
187
216
|
staging = parent ? Dir.mktmpdir(prefix, parent) : Dir.mktmpdir(prefix)
|
|
188
|
-
entries = application_entries(source)
|
|
217
|
+
entries = application_entries(source, config:)
|
|
189
218
|
FileUtils.cp_r(entries.map { |entry| File.join(source, entry) }, staging) unless entries.empty?
|
|
190
|
-
|
|
191
|
-
|
|
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:)
|
|
192
223
|
Runtime.install_package(staging)
|
|
193
224
|
end
|
|
194
225
|
staging
|
|
@@ -197,9 +228,11 @@ module OmarchyUI
|
|
|
197
228
|
raise
|
|
198
229
|
end
|
|
199
230
|
|
|
200
|
-
def bundle_ruby_entrypoint(source, destination)
|
|
201
|
-
|
|
202
|
-
File.
|
|
231
|
+
def bundle_ruby_entrypoint(source, destination, entrypoint: "main.rb")
|
|
232
|
+
source_entrypoint = File.join(source, entrypoint)
|
|
233
|
+
destination_entrypoint = File.join(destination, entrypoint)
|
|
234
|
+
FileUtils.mkdir_p(File.dirname(destination_entrypoint))
|
|
235
|
+
File.write(destination_entrypoint, SourceBundle.new(source_entrypoint, root: source).call)
|
|
203
236
|
end
|
|
204
237
|
|
|
205
238
|
def activate_plugin(plugin_id)
|
data/lib/omarchy_ui/generator.rb
CHANGED
|
@@ -4,9 +4,15 @@ require "fileutils"
|
|
|
4
4
|
|
|
5
5
|
module OmarchyUI
|
|
6
6
|
class Generator
|
|
7
|
-
|
|
7
|
+
PROJECT_KINDS = %i[application plugin].freeze
|
|
8
|
+
|
|
9
|
+
def initialize(path:, name: nil, kind: :application, plugin_id: nil, author: nil)
|
|
8
10
|
@path = File.expand_path(path)
|
|
9
11
|
@name = name || File.basename(@path).split(/[-_]/).map(&:capitalize).join(" ")
|
|
12
|
+
@kind = kind.to_sym
|
|
13
|
+
@plugin_id = plugin_id || "local.#{project_slug}"
|
|
14
|
+
@author = author.to_s.strip.empty? ? "Omarchy UI Developer" : author.to_s.strip
|
|
15
|
+
raise ArgumentError, "unsupported project kind: #{@kind}" unless PROJECT_KINDS.include?(@kind)
|
|
10
16
|
end
|
|
11
17
|
|
|
12
18
|
def create
|
|
@@ -14,9 +20,11 @@ module OmarchyUI
|
|
|
14
20
|
created = true
|
|
15
21
|
FileUtils.mkdir_p(File.join(@path, "components"))
|
|
16
22
|
File.write(File.join(@path, "main.rb"), main_program)
|
|
17
|
-
File.write(File.join(@path, "app.rb"), application_program)
|
|
23
|
+
File.write(File.join(@path, "app.rb"), application_program) unless plugin?
|
|
24
|
+
File.write(File.join(@path, ProjectConfig::FILE_NAME), project_config)
|
|
18
25
|
File.write(File.join(@path, "components", "welcome.rb"), welcome_component)
|
|
19
26
|
File.write(File.join(@path, "README.md"), readme)
|
|
27
|
+
File.write(File.join(@path, "LICENSE"), license)
|
|
20
28
|
@path
|
|
21
29
|
rescue StandardError
|
|
22
30
|
FileUtils.remove_entry(@path) if created && File.directory?(@path)
|
|
@@ -26,6 +34,8 @@ module OmarchyUI
|
|
|
26
34
|
private
|
|
27
35
|
|
|
28
36
|
def main_program
|
|
37
|
+
return plugin_program if plugin?
|
|
38
|
+
|
|
29
39
|
<<~RUBY
|
|
30
40
|
# frozen_string_literal: true
|
|
31
41
|
|
|
@@ -36,6 +46,33 @@ module OmarchyUI
|
|
|
36
46
|
RUBY
|
|
37
47
|
end
|
|
38
48
|
|
|
49
|
+
def plugin_program
|
|
50
|
+
<<~RUBY
|
|
51
|
+
# frozen_string_literal: true
|
|
52
|
+
|
|
53
|
+
require "omarchy_ui"
|
|
54
|
+
require_relative "components/welcome"
|
|
55
|
+
|
|
56
|
+
OmarchyUI.plugin(ui: #{constant_name}::UI) do
|
|
57
|
+
bar_widget do
|
|
58
|
+
row spacing: 7 do
|
|
59
|
+
text "#{@name}"
|
|
60
|
+
end
|
|
61
|
+
on_click { open_panel :#{surface_name} }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
panel :#{surface_name} do
|
|
65
|
+
scroll width: 560, height: 620 do
|
|
66
|
+
welcome_card(
|
|
67
|
+
title: "#{@name}",
|
|
68
|
+
message: "Your Omarchy plugin is ready. Build the interface with Ruby and Zui components."
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
RUBY
|
|
74
|
+
end
|
|
75
|
+
|
|
39
76
|
def application_program
|
|
40
77
|
<<~RUBY
|
|
41
78
|
# frozen_string_literal: true
|
|
@@ -80,6 +117,8 @@ module OmarchyUI
|
|
|
80
117
|
end
|
|
81
118
|
|
|
82
119
|
def readme
|
|
120
|
+
return plugin_readme if plugin?
|
|
121
|
+
|
|
83
122
|
<<~MARKDOWN
|
|
84
123
|
# #{@name}
|
|
85
124
|
|
|
@@ -92,10 +131,97 @@ module OmarchyUI
|
|
|
92
131
|
```
|
|
93
132
|
|
|
94
133
|
Reusable Ruby UI components live in `components/` and load with ordinary Ruby
|
|
95
|
-
`require_relative`.
|
|
134
|
+
`require_relative`. Project identity and build settings live in `config.rb`.
|
|
135
|
+
|
|
136
|
+
## Build
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
omarchy_ui bundle
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Omarchy UI generates the complete self-contained application package required by Omarchy.
|
|
143
|
+
MARKDOWN
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def plugin_readme
|
|
147
|
+
<<~MARKDOWN
|
|
148
|
+
# #{@name}
|
|
149
|
+
|
|
150
|
+
A pure Ruby Omarchy plugin built with Omarchy UI and Zui.
|
|
151
|
+
|
|
152
|
+
## Validate
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
omarchy_ui validate
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Install for development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
omarchy_ui push
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Build
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
omarchy_ui bundle
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
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.
|
|
96
173
|
MARKDOWN
|
|
97
174
|
end
|
|
98
175
|
|
|
176
|
+
def project_config
|
|
177
|
+
config = ProjectConfig.generate(
|
|
178
|
+
name: @name,
|
|
179
|
+
slug: project_slug,
|
|
180
|
+
kind: @kind,
|
|
181
|
+
author: @author,
|
|
182
|
+
plugin_id: @plugin_id
|
|
183
|
+
)
|
|
184
|
+
config
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def license
|
|
188
|
+
<<~TEXT
|
|
189
|
+
MIT License
|
|
190
|
+
|
|
191
|
+
Copyright (c) #{Time.now.year} #{@author}
|
|
192
|
+
|
|
193
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
194
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
195
|
+
in the Software without restriction, including without limitation the rights
|
|
196
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
197
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
198
|
+
furnished to do so, subject to the following conditions:
|
|
199
|
+
|
|
200
|
+
The above copyright notice and this permission notice shall be included in all
|
|
201
|
+
copies or substantial portions of the Software.
|
|
202
|
+
|
|
203
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
204
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
205
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
206
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
207
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
208
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
209
|
+
SOFTWARE.
|
|
210
|
+
TEXT
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def plugin?
|
|
214
|
+
@kind == :plugin
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def project_slug
|
|
218
|
+
File.basename(@path).downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-|\-\z/, "")
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def surface_name
|
|
222
|
+
project_slug.tr("-", "_")
|
|
223
|
+
end
|
|
224
|
+
|
|
99
225
|
def constant_name
|
|
100
226
|
@name.gsub(/[^a-zA-Z0-9]+/, " ").split.map(&:capitalize).join.then do |name|
|
|
101
227
|
name.empty? || name.match?(/\A\d/) ? "OmarchyApplication" : name
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module OmarchyUI
|
|
6
|
+
class ProjectConfig
|
|
7
|
+
FILE_NAME = "config.rb"
|
|
8
|
+
PROJECT_TYPES = %i[application plugin].freeze
|
|
9
|
+
SLUG = /\A[a-z0-9]+(?:-[a-z0-9]+)*\z/
|
|
10
|
+
SECTIONS = %i[left center right].freeze
|
|
11
|
+
|
|
12
|
+
class Builder
|
|
13
|
+
FIELDS = %i[type id name slug version author license description entrypoint].freeze
|
|
14
|
+
|
|
15
|
+
FIELDS.each do |field|
|
|
16
|
+
define_method(field) { |value| @values[field] = value }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@values = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def bar_widget(&definition)
|
|
24
|
+
widget = WidgetBuilder.new
|
|
25
|
+
widget.instance_eval(&definition)
|
|
26
|
+
@values[:bar_widget] = widget.to_h
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def build(path: FILE_NAME)
|
|
30
|
+
ProjectConfig.new(@values, path:).tap(&:validate!)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class WidgetBuilder
|
|
35
|
+
FIELDS = %i[display_name description category default_section].freeze
|
|
36
|
+
|
|
37
|
+
FIELDS.each do |field|
|
|
38
|
+
define_method(field) { |value| @values[field] = value }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def initialize
|
|
42
|
+
@values = {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_h
|
|
46
|
+
@values.dup
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attr_reader :path
|
|
51
|
+
|
|
52
|
+
def self.define(&definition)
|
|
53
|
+
raise ArgumentError, "project config requires a block" unless definition
|
|
54
|
+
|
|
55
|
+
Builder.new.tap { |builder| builder.instance_eval(&definition) }.build
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.generate(name:, slug:, kind:, author:, plugin_id: nil)
|
|
59
|
+
type = kind.to_sym
|
|
60
|
+
raise ArgumentError, "unsupported project type: #{type}" unless PROJECT_TYPES.include?(type)
|
|
61
|
+
|
|
62
|
+
lines = [
|
|
63
|
+
"# frozen_string_literal: true",
|
|
64
|
+
"",
|
|
65
|
+
"OmarchyUI.configure do",
|
|
66
|
+
" type #{type.inspect}"
|
|
67
|
+
]
|
|
68
|
+
lines << " id #{(plugin_id || "local.#{slug}").inspect}" if type == :plugin
|
|
69
|
+
lines.concat([
|
|
70
|
+
" name #{name.inspect}",
|
|
71
|
+
" slug #{slug.inspect}",
|
|
72
|
+
" version \"0.1.0\""
|
|
73
|
+
])
|
|
74
|
+
if type == :plugin
|
|
75
|
+
lines.concat([
|
|
76
|
+
" author #{author.inspect}",
|
|
77
|
+
" license \"MIT\"",
|
|
78
|
+
" description #{"A Ruby-powered #{name} plugin generated by Omarchy UI.".inspect}",
|
|
79
|
+
" entrypoint \"main.rb\"",
|
|
80
|
+
"",
|
|
81
|
+
" bar_widget do",
|
|
82
|
+
" display_name #{name.inspect}",
|
|
83
|
+
" description #{"Open #{name}.".inspect}",
|
|
84
|
+
" category \"System\"",
|
|
85
|
+
" default_section :right",
|
|
86
|
+
" end"
|
|
87
|
+
])
|
|
88
|
+
else
|
|
89
|
+
lines << " entrypoint \"main.rb\""
|
|
90
|
+
end
|
|
91
|
+
lines << "end"
|
|
92
|
+
lines.join("\n") + "\n"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.load(root)
|
|
96
|
+
path = File.join(File.expand_path(root), FILE_NAME)
|
|
97
|
+
return nil unless File.file?(path)
|
|
98
|
+
|
|
99
|
+
value = Module.new.module_eval(File.read(path), path, 1)
|
|
100
|
+
unless value.is_a?(ProjectConfig)
|
|
101
|
+
raise ArgumentError, "#{FILE_NAME} must return an OmarchyUI::ProjectConfig"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
new(value.to_h, path:).tap(&:validate!)
|
|
105
|
+
rescue SyntaxError, NameError, NoMethodError => error
|
|
106
|
+
raise ArgumentError, "invalid #{FILE_NAME}: #{error.message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def initialize(data, path:)
|
|
110
|
+
@data = data
|
|
111
|
+
@path = path
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def type
|
|
115
|
+
value = @data[:type]
|
|
116
|
+
value.respond_to?(:to_sym) ? value.to_sym : value
|
|
117
|
+
end
|
|
118
|
+
def application? = type == :application
|
|
119
|
+
def plugin? = type == :plugin
|
|
120
|
+
def name = @data.fetch(:name)
|
|
121
|
+
def slug = @data.fetch(:slug)
|
|
122
|
+
def entrypoint = @data.fetch(:entrypoint)
|
|
123
|
+
|
|
124
|
+
def to_h
|
|
125
|
+
@data.dup
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def manifest
|
|
129
|
+
raise ArgumentError, "project config describes an application, not a plugin" unless plugin?
|
|
130
|
+
|
|
131
|
+
widget = @data.fetch(:bar_widget)
|
|
132
|
+
{
|
|
133
|
+
"schemaVersion" => 1,
|
|
134
|
+
"id" => @data.fetch(:id),
|
|
135
|
+
"name" => name,
|
|
136
|
+
"version" => @data.fetch(:version),
|
|
137
|
+
"author" => @data.fetch(:author),
|
|
138
|
+
"license" => @data.fetch(:license),
|
|
139
|
+
"description" => @data.fetch(:description),
|
|
140
|
+
"kinds" => ["service", "bar-widget", "panel"],
|
|
141
|
+
"keepLoaded" => true,
|
|
142
|
+
"entryPoints" => {
|
|
143
|
+
"service" => "Service.qml",
|
|
144
|
+
"barWidget" => "BarWidget.qml",
|
|
145
|
+
"panel" => "Panel.qml"
|
|
146
|
+
},
|
|
147
|
+
"barWidget" => {
|
|
148
|
+
"displayName" => widget.fetch(:display_name),
|
|
149
|
+
"description" => widget.fetch(:description),
|
|
150
|
+
"category" => widget.fetch(:category),
|
|
151
|
+
"allowMultiple" => false,
|
|
152
|
+
"defaultSection" => widget.fetch(:default_section).to_s
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def write_manifest(destination)
|
|
158
|
+
File.write(File.join(destination, "manifest.json"), JSON.pretty_generate(manifest) + "\n")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def validate!
|
|
162
|
+
raise ArgumentError, "invalid project config" unless @data.is_a?(Hash)
|
|
163
|
+
raise ArgumentError, "invalid project type" unless PROJECT_TYPES.include?(type)
|
|
164
|
+
required_string!(:name)
|
|
165
|
+
required_string!(:slug)
|
|
166
|
+
raise ArgumentError, "invalid project slug" unless SLUG.match?(@data[:slug])
|
|
167
|
+
required_string!(:version)
|
|
168
|
+
required_string!(:entrypoint)
|
|
169
|
+
validate_entrypoint!
|
|
170
|
+
validate_plugin! if plugin?
|
|
171
|
+
self
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
def required_string!(key, object = @data)
|
|
177
|
+
value = object[key]
|
|
178
|
+
raise ArgumentError, "invalid project config field: #{key}" unless value.is_a?(String) && !value.strip.empty?
|
|
179
|
+
|
|
180
|
+
value
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def validate_entrypoint!
|
|
184
|
+
parts = entrypoint.split(/[\\\/]/)
|
|
185
|
+
if entrypoint.start_with?("/") || parts.empty? || parts.include?("..") || !entrypoint.end_with?(".rb")
|
|
186
|
+
raise ArgumentError, "invalid project entrypoint"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def validate_plugin!
|
|
191
|
+
%i[id author license description].each { |key| required_string!(key) }
|
|
192
|
+
raise ArgumentError, "invalid plugin id" unless VALID_ID.match?(@data[:id])
|
|
193
|
+
|
|
194
|
+
widget = @data[:bar_widget]
|
|
195
|
+
raise ArgumentError, "plugin project config is missing bar widget metadata" unless widget.is_a?(Hash)
|
|
196
|
+
|
|
197
|
+
%i[display_name description category].each { |key| required_string!(key, widget) }
|
|
198
|
+
section = widget[:default_section]
|
|
199
|
+
valid_section = section.respond_to?(:to_sym) && SECTIONS.include?(section.to_sym)
|
|
200
|
+
raise ArgumentError, "invalid default bar section" unless valid_section
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def self.configure(&definition)
|
|
205
|
+
ProjectConfig.define(&definition)
|
|
206
|
+
end
|
|
207
|
+
end
|
data/lib/omarchy_ui.rb
CHANGED
|
@@ -11,7 +11,7 @@ rescue LoadError
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
module OmarchyUI
|
|
14
|
-
VERSION = "0.0.
|
|
14
|
+
VERSION = "0.0.8"
|
|
15
15
|
ZUI_VERSION = Zui::VERSION
|
|
16
16
|
FRAMEWORK_ROOT = File.expand_path("..", __dir__)
|
|
17
17
|
|
|
@@ -126,5 +126,6 @@ module Zui
|
|
|
126
126
|
end
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
require_relative "omarchy_ui/project_config"
|
|
129
130
|
require_relative "omarchy_ui/generator"
|
|
130
131
|
require_relative "omarchy_ui/runtime"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omarchy-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.8
|
|
5
5
|
platform: x86_64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Adam Moussa Ali
|
|
@@ -39,6 +39,7 @@ files:
|
|
|
39
39
|
- lib/omarchy_ui.rb
|
|
40
40
|
- lib/omarchy_ui/cli.rb
|
|
41
41
|
- lib/omarchy_ui/generator.rb
|
|
42
|
+
- lib/omarchy_ui/project_config.rb
|
|
42
43
|
- lib/omarchy_ui/runtime.rb
|
|
43
44
|
- vendor/runtime/x86_64-linux/RUNTIME_PROVENANCE.md
|
|
44
45
|
- vendor/runtime/x86_64-linux/omarchy-ui-runtime
|