omarchy-ui 0.0.2-x86_64-linux → 0.0.3-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 +10 -20
- data/lib/omarchy_ui/application.rb +40 -1
- data/lib/omarchy_ui/builder.rb +21 -4
- data/lib/omarchy_ui/cli.rb +13 -6
- data/lib/omarchy_ui/project.rb +1 -1
- data/lib/omarchy_ui.rb +1 -1
- data/vendor/runtime/x86_64-linux/omarchy-ui-runtime +0 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f53f63774323d862684e519c9884aed320d675b9391d228354def2b7768f0e4
|
|
4
|
+
data.tar.gz: 0ed0b927c051f03f6418f737769b51bc16c80114a70e362254daf23832f72196
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20c6694b5a1641f360402835d44b2eabefd164a373e28f94b878d0e41e1351cb3c40a67db4a19e2d7ff0df5298d1a6ff0ed8316ea7aa89ffe2073ffef2f7dfd0
|
|
7
|
+
data.tar.gz: 00457faae9811da4e493d35d77b7c0ed514200c16acd51174841e5e3a172f9687a891d44f18c5dca60ad1be1ea47398a4b7128160d66dfd8c37c1ad796dde9df
|
data/README.md
CHANGED
|
@@ -25,29 +25,28 @@ end
|
|
|
25
25
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
|
-
On Omarchy x86-64, install the gem
|
|
28
|
+
On Omarchy x86-64, install the gem:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
gem install omarchy-ui
|
|
32
|
-
omarchy_ui new "My App"
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
The gem contains everything required to
|
|
34
|
+
The gem contains everything required to launch and bundle an application. Developers do
|
|
36
35
|
not need to install a separate runtime or copy framework files. Bundled applications require only
|
|
37
36
|
an Omarchy computer to run.
|
|
38
37
|
|
|
39
|
-
##
|
|
38
|
+
## Start an application
|
|
40
39
|
|
|
41
40
|
```bash
|
|
42
|
-
omarchy_ui new
|
|
43
|
-
cd
|
|
41
|
+
omarchy_ui new MyApp
|
|
42
|
+
cd myapp
|
|
44
43
|
omarchy_ui launch main.rb
|
|
45
44
|
```
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
A new standalone project contains only application-owned files:
|
|
48
47
|
|
|
49
48
|
```text
|
|
50
|
-
|
|
49
|
+
myapp/
|
|
51
50
|
├── Components/
|
|
52
51
|
│ └── Welcome.qml
|
|
53
52
|
├── README.md
|
|
@@ -63,10 +62,10 @@ From an application directory:
|
|
|
63
62
|
|
|
64
63
|
```bash
|
|
65
64
|
omarchy_ui bundle
|
|
66
|
-
./dist/
|
|
65
|
+
./dist/myapp/run
|
|
67
66
|
```
|
|
68
67
|
|
|
69
|
-
`bundle` creates a self-contained application under `dist/<project-name>/`.
|
|
68
|
+
`bundle` creates a self-contained application under `dist/<project-name>/`. Its `run`
|
|
70
69
|
launcher works on another Omarchy computer without Ruby or the `omarchy-ui` gem.
|
|
71
70
|
|
|
72
71
|
An Omarchy Shell plugin is a separate packaging mode. It requires `manifest.json` so the shell
|
|
@@ -306,15 +305,6 @@ Component names, files, properties, events, IDs, effects, values, message sizes,
|
|
|
306
305
|
limits are validated. Commands use argv arrays without a shell. Applications and plugins run with
|
|
307
306
|
the current user's permissions, so review third-party code before installing it.
|
|
308
307
|
|
|
309
|
-
## Omarchy Phone example
|
|
310
|
-
|
|
311
|
-
`examples/omarchy-phone` demonstrates reactive controls, background discovery, safe commands,
|
|
312
|
-
ADB pairing and connection, scrcpy launching, iPhone discovery, and UxPlay AirPlay mirroring.
|
|
313
|
-
|
|
314
|
-
```bash
|
|
315
|
-
omarchy_ui launch examples/omarchy-phone/main.rb
|
|
316
|
-
```
|
|
317
|
-
|
|
318
308
|
## Development and verification
|
|
319
309
|
|
|
320
310
|
```bash
|
|
@@ -324,7 +314,7 @@ ruby script/benchmark.rb
|
|
|
324
314
|
```
|
|
325
315
|
|
|
326
316
|
The suite covers state, bindings, repeated structures, event persistence, component schemas,
|
|
327
|
-
animation tracks and sequences, tasks, command safety, standalone
|
|
317
|
+
animation tracks and sequences, tasks, command safety, standalone projects, packaging,
|
|
328
318
|
manifests, component contracts, linting, and the phone backend.
|
|
329
319
|
|
|
330
320
|
## License
|
|
@@ -190,16 +190,50 @@ module OmarchyUI
|
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
def reconcile_structure(structure)
|
|
193
|
+
previous_children = structure.last_children
|
|
193
194
|
structure.node.children.dup.each { |child| unregister_subtree(child) }
|
|
194
195
|
structure.node.children.clear
|
|
195
196
|
@builder.rebuild(structure.node, &structure.renderer)
|
|
196
197
|
children = structure.node.children.map(&:to_h)
|
|
197
198
|
return if children == structure.last_children
|
|
199
|
+
if patchable_trees?(previous_children, children)
|
|
200
|
+
emit_property_patches(previous_children, children)
|
|
201
|
+
structure.last_children = children
|
|
202
|
+
return
|
|
203
|
+
end
|
|
198
204
|
structure.last_children = children
|
|
199
205
|
emit("v" => PROTOCOL_VERSION, "type" => "patch", "op" => "replace_children",
|
|
200
206
|
"id" => structure.node.id, "children" => children)
|
|
201
207
|
end
|
|
202
208
|
|
|
209
|
+
def patchable_trees?(previous, current)
|
|
210
|
+
return false unless previous.length == current.length
|
|
211
|
+
previous.zip(current).all? do |before, after|
|
|
212
|
+
before["id"] == after["id"] && before["type"] == after["type"] &&
|
|
213
|
+
before.fetch("events", []) == after.fetch("events", []) &&
|
|
214
|
+
before.fetch("props", {}).keys.sort == after.fetch("props", {}).keys.sort &&
|
|
215
|
+
patchable_trees?(before.fetch("children", []), after.fetch("children", []))
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def emit_property_patches(previous, current)
|
|
220
|
+
previous.zip(current).each do |before, after|
|
|
221
|
+
before.fetch("props", {}).each do |property, old_value|
|
|
222
|
+
value = after.fetch("props", {}).fetch(property)
|
|
223
|
+
next if value == old_value
|
|
224
|
+
next if echoed_input_patch?(after.fetch("id"), property, value)
|
|
225
|
+
emit("v" => PROTOCOL_VERSION, "type" => "patch", "op" => "set",
|
|
226
|
+
"id" => after.fetch("id"), "property" => property, "value" => value)
|
|
227
|
+
end
|
|
228
|
+
emit_property_patches(before.fetch("children", []), after.fetch("children", []))
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def echoed_input_patch?(control_id, property, value)
|
|
233
|
+
@active_event && @active_event["event"] == "input" && @active_event["id"] == control_id &&
|
|
234
|
+
%w[text value].include?(property) && @active_event.dig("payload", "value") == value
|
|
235
|
+
end
|
|
236
|
+
|
|
203
237
|
def unregister_subtree(node)
|
|
204
238
|
node.children.each { |child| unregister_subtree(child) }
|
|
205
239
|
@nodes.delete(node.id)
|
|
@@ -212,7 +246,12 @@ module OmarchyUI
|
|
|
212
246
|
key = [message.fetch("id"), message.fetch("event")]
|
|
213
247
|
handler = @handlers[key]
|
|
214
248
|
raise ProtocolError, "unknown event target: #{key.join('/')}" unless handler
|
|
215
|
-
|
|
249
|
+
begin
|
|
250
|
+
@active_event = message
|
|
251
|
+
@builder.instance_exec(message["payload"] || {}, &handler)
|
|
252
|
+
ensure
|
|
253
|
+
@active_event = nil
|
|
254
|
+
end
|
|
216
255
|
acknowledgement = {
|
|
217
256
|
"v" => PROTOCOL_VERSION, "type" => "ack", "seq" => message["seq"],
|
|
218
257
|
"id" => message.fetch("id"), "event" => message.fetch("event")
|
data/lib/omarchy_ui/builder.rb
CHANGED
|
@@ -16,11 +16,12 @@ module OmarchyUI
|
|
|
16
16
|
def initialize(application)
|
|
17
17
|
@application = application
|
|
18
18
|
@stack = []
|
|
19
|
+
@dynamic_scopes = []
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def component(type, id: nil, **props, &block)
|
|
22
23
|
definition = @application.components.fetch(type)
|
|
23
|
-
node = @application.build_node(type, explicit_id: id, props:)
|
|
24
|
+
node = @application.build_node(type, explicit_id: id || scoped_id(type), props:)
|
|
24
25
|
append(node)
|
|
25
26
|
if block
|
|
26
27
|
raise ArgumentError, "#{type} is not a container" unless definition.container
|
|
@@ -64,7 +65,8 @@ module OmarchyUI
|
|
|
64
65
|
definition = @application.components.fetch(type)
|
|
65
66
|
raise ArgumentError, "dynamic component must be a container: #{type}" unless definition.container
|
|
66
67
|
|
|
67
|
-
node = component(type, id:, **props
|
|
68
|
+
node = component(type, id:, **props)
|
|
69
|
+
within_dynamic(node, &renderer)
|
|
68
70
|
@application.register_structure(node, renderer)
|
|
69
71
|
node
|
|
70
72
|
end
|
|
@@ -166,7 +168,7 @@ module OmarchyUI
|
|
|
166
168
|
def every(seconds, immediate: false, &block) = @application.schedule(:every, interval: seconds, immediate:, &block)
|
|
167
169
|
def async(&block) = @application.schedule(:async, &block)
|
|
168
170
|
def run_command(argv, **options) = Command.run(argv, **options)
|
|
169
|
-
def rebuild(node, &renderer) =
|
|
171
|
+
def rebuild(node, &renderer) = within_dynamic(node, &renderer)
|
|
170
172
|
def open_panel(name) = @application.emit_effect("open_panel", "surface" => name.to_s)
|
|
171
173
|
|
|
172
174
|
def close_panel(name = nil)
|
|
@@ -175,6 +177,20 @@ module OmarchyUI
|
|
|
175
177
|
|
|
176
178
|
private
|
|
177
179
|
|
|
180
|
+
def scoped_id(type)
|
|
181
|
+
return nil if @dynamic_scopes.empty?
|
|
182
|
+
scope = @dynamic_scopes.last
|
|
183
|
+
scope[:sequence] += 1
|
|
184
|
+
"#{scope.fetch(:id)}.#{type}.#{scope.fetch(:sequence)}"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def within_dynamic(node, &block)
|
|
188
|
+
@dynamic_scopes.push({ id: node.id, sequence: 0 })
|
|
189
|
+
within(node, &block)
|
|
190
|
+
ensure
|
|
191
|
+
@dynamic_scopes.pop
|
|
192
|
+
end
|
|
193
|
+
|
|
178
194
|
def surface(name, id:, options: {}, &block)
|
|
179
195
|
raise ArgumentError, "surface requires a block" unless block
|
|
180
196
|
node = @application.build_node(:container, explicit_id: id)
|
|
@@ -192,7 +208,8 @@ module OmarchyUI
|
|
|
192
208
|
def input_component(type, property, value, id:, props:, handler: nil)
|
|
193
209
|
props = props.merge(property => value) unless value.equal?(UNSET)
|
|
194
210
|
node = component(type, id:, **props)
|
|
195
|
-
|
|
211
|
+
event = type == :text_field ? :input : :change
|
|
212
|
+
@application.register_handler(node.id, event, handler) if handler
|
|
196
213
|
node
|
|
197
214
|
end
|
|
198
215
|
|
data/lib/omarchy_ui/cli.rb
CHANGED
|
@@ -103,12 +103,15 @@ module OmarchyUI
|
|
|
103
103
|
destination = File.join(source, "dist", File.basename(source))
|
|
104
104
|
raise ArgumentError, "bundle destination already exists: #{destination}" if File.exist?(destination)
|
|
105
105
|
FileUtils.mkdir_p(destination)
|
|
106
|
-
entries =
|
|
106
|
+
entries = application_entries(source)
|
|
107
107
|
FileUtils.cp_r(entries.map { |entry| File.join(source, entry) }, destination)
|
|
108
108
|
Project.install_runtime(destination)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
if File.file?(File.join(destination, "manifest.json"))
|
|
110
|
+
raise ArgumentError, "bundled plugin validation failed" unless system("omarchy", "plugin", "validate", destination)
|
|
111
|
+
@out.puts("Bundled plugin in #{destination}")
|
|
112
|
+
return 0
|
|
113
|
+
end
|
|
114
|
+
|
|
112
115
|
%w[Commons Ui].each do |module_name|
|
|
113
116
|
FileUtils.ln_s(File.join("/usr/share/omarchy/shell", module_name), File.join(destination, module_name))
|
|
114
117
|
end
|
|
@@ -135,11 +138,15 @@ module OmarchyUI
|
|
|
135
138
|
[ENV["QT_LOGGING_RULES"], "qt.qpa.services.warning=false"].compact.reject(&:empty?).join(";")
|
|
136
139
|
end
|
|
137
140
|
|
|
141
|
+
def application_entries(source)
|
|
142
|
+
generated = Project::RUNTIME_FILES + %w[omarchy-ui-runtime run Commons Ui]
|
|
143
|
+
Dir.children(source).reject { |entry| %w[.git dist].include?(entry) || generated.include?(entry) }
|
|
144
|
+
end
|
|
145
|
+
|
|
138
146
|
def push(arguments)
|
|
139
147
|
enable = !arguments.delete("--no-enable")
|
|
140
148
|
restart = !arguments.delete("--no-restart")
|
|
141
149
|
source = File.expand_path(arguments.shift || Dir.pwd)
|
|
142
|
-
Runtime.install_shared
|
|
143
150
|
manifest_path = File.join(source, "manifest.json")
|
|
144
151
|
manifest = JSON.parse(File.read(manifest_path))
|
|
145
152
|
plugin_id = manifest.fetch("id")
|
|
@@ -185,7 +192,7 @@ module OmarchyUI
|
|
|
185
192
|
def stage_project(source, parent: nil, prefix: ".omarchy-ui-staging-")
|
|
186
193
|
raise ArgumentError, "project directory not found: #{source}" unless File.directory?(source)
|
|
187
194
|
staging = parent ? Dir.mktmpdir(prefix, parent) : Dir.mktmpdir(prefix)
|
|
188
|
-
entries =
|
|
195
|
+
entries = application_entries(source)
|
|
189
196
|
FileUtils.cp_r(entries.map { |entry| File.join(source, entry) }, staging) unless entries.empty?
|
|
190
197
|
Project.install_runtime(staging) if File.file?(File.join(staging, "main.rb"))
|
|
191
198
|
staging
|
data/lib/omarchy_ui/project.rb
CHANGED
|
@@ -28,7 +28,7 @@ module OmarchyUI
|
|
|
28
28
|
def self.install_runtime(path, framework_root: FRAMEWORK_ROOT)
|
|
29
29
|
RUNTIME_FILES.each do |file|
|
|
30
30
|
destination = File.join(path, file)
|
|
31
|
-
FileUtils.cp(File.join(framework_root, file), destination)
|
|
31
|
+
FileUtils.cp(File.join(framework_root, file), destination)
|
|
32
32
|
end
|
|
33
33
|
bundled_runtime = File.join(framework_root, "vendor", "runtime", "x86_64-linux", "omarchy-ui-runtime")
|
|
34
34
|
if File.file?(bundled_runtime)
|
data/lib/omarchy_ui.rb
CHANGED
|
Binary file
|