hokusai-zero 0.1.9 → 0.2.0
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/hokusai.gemspec +1 -1
- data/ui/spec/hokusai/diff_spec.rb +117 -0
- data/ui/spec/hokusai/providers_spec.rb +88 -1
- data/ui/src/hokusai/backends/raylib.rb +6 -0
- data/ui/src/hokusai/blocks/text.rb +1 -3
- data/ui/src/hokusai/diff.rb +6 -5
- data/ui/src/hokusai/meta.rb +1 -1
- data/ui/src/hokusai/mounting/loop_entry.rb +3 -8
- data/ui/src/hokusai/mounting/update_entry.rb +16 -15
- data/ui/src/hokusai/node.rb +2 -2
- data/ui/src/hokusai/node_mounter.rb +2 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e03fd33f856cbaade06505c5e962980504107726d39d6ec630b16e2d4c75b480
|
4
|
+
data.tar.gz: eb7bf1b39ea4acae4e7d7cfe4d7d70d14cd7335e65c4b74dc7c7e37b697eb52b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c85bc9f63b75685b60fc83d3e247c84cd3ee02e981608aadf810e080ec7c22043bf0d0ff5908678ed244ad40a2a0e9653d67eaefd77071b1cde15df81e2f641
|
7
|
+
data.tar.gz: 10e5e39e7185c0c3863a0958fe20085d62f2603bfe1621a5d92dbb16caff6d7183d1a8779f45a5d2e1624b99c54ea116f1dbe8318f6b78d2bc2a47ade2965d7d
|
data/hokusai.gemspec
CHANGED
@@ -0,0 +1,117 @@
|
|
1
|
+
describe Hokusai::Diff, focus: true do
|
2
|
+
def patches(before, after)
|
3
|
+
patches = []
|
4
|
+
|
5
|
+
Hokusai::Diff.new(before, after).patch do |item|
|
6
|
+
patches << item
|
7
|
+
end
|
8
|
+
|
9
|
+
patches
|
10
|
+
end
|
11
|
+
|
12
|
+
it "inserts new items" do
|
13
|
+
list = patches([], [[:key1, 1],[:key2, 2],[:key3, 3]])
|
14
|
+
|
15
|
+
expect(list.count).to eq(3)
|
16
|
+
|
17
|
+
list.each_with_index do |patch, i|
|
18
|
+
expect(patch).to be_a(Hokusai::InsertPatch)
|
19
|
+
expect(patch.target).to eq(i)
|
20
|
+
expect(patch.value).to eq(i+1)
|
21
|
+
expect(patch.delete).to be(true)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "inserts new items and deletes old items" do
|
26
|
+
list = patches(
|
27
|
+
[[:key1, 1], [:key2, 2], [:key3, 3]],
|
28
|
+
[[:key1, 1], [:key4, 4], [:key3, 3]]
|
29
|
+
)
|
30
|
+
|
31
|
+
expect(list.count).to eq(1)
|
32
|
+
|
33
|
+
# removes key2 and inserts key4 with 1 insertpatch
|
34
|
+
expect(list.first).to be_a(Hokusai::InsertPatch)
|
35
|
+
expect(list.first.target).to eq(1)
|
36
|
+
expect(list.first.value).to eq(4)
|
37
|
+
expect(list.first.delete).to be(true)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "updates items with new values on existing keys" do
|
41
|
+
list = patches(
|
42
|
+
[[:key1, 1], [:key2, 2], [:key3, 3]],
|
43
|
+
[[:key1, 1], [:key2, 4], [:key3, 3]]
|
44
|
+
)
|
45
|
+
|
46
|
+
expect(list.count).to eq(1)
|
47
|
+
|
48
|
+
# removes key2 and inserts key4 with 1 insertpatch
|
49
|
+
expect(list.first).to be_a(Hokusai::UpdatePatch)
|
50
|
+
expect(list.first.target).to eq(1)
|
51
|
+
expect(list.first.value).to eq(4)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "moves items that have the same key and no longer are in the list" do
|
55
|
+
list = patches(
|
56
|
+
[[:key1, 1], [:key2, 2], [:key3, 3]],
|
57
|
+
[[:key1, 1], [:key3, 3]]
|
58
|
+
)
|
59
|
+
|
60
|
+
expect(list.count).to eq(1)
|
61
|
+
expect(list.first).to be_a(Hokusai::MovePatch)
|
62
|
+
expect(list.first.from).to eq(2)
|
63
|
+
expect(list.first.to).to eq(1)
|
64
|
+
expect(list.first.value).to eq(3)
|
65
|
+
expect(list.first.delete).to be(true)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "deletes items that have different keys and no longer are in the list" do
|
69
|
+
list = patches(
|
70
|
+
[[:key1, 1], [:key2, 2], [:key3, 3]],
|
71
|
+
[[:key2, 2], [:key3, 3]]
|
72
|
+
)
|
73
|
+
|
74
|
+
expect(list.count).to eq(3)
|
75
|
+
# moves position 1 to 0
|
76
|
+
expect(list[0]).to be_a(Hokusai::MovePatch)
|
77
|
+
expect(list[0].from).to eq(1)
|
78
|
+
expect(list[0].to).to eq(0)
|
79
|
+
expect(list[0].delete).to be(true)
|
80
|
+
|
81
|
+
# moves position 2 to 1
|
82
|
+
expect(list[1]).to be_a(Hokusai::MovePatch)
|
83
|
+
expect(list[1].from).to eq(2)
|
84
|
+
expect(list[1].to).to eq(1)
|
85
|
+
|
86
|
+
# delete position 2
|
87
|
+
expect(list[2]).to be_a(Hokusai::DeletePatch)
|
88
|
+
expect(list[2].target).to eq(2)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "moves items around" do
|
92
|
+
# [d, a, c] [(c), e, a, b]
|
93
|
+
list = patches(
|
94
|
+
[[:key1, :d], [:key2, :a], [:key3, :c]],
|
95
|
+
[[:key3, :c], [:key4, :e], [:key2, :a], [:key5, :b]]
|
96
|
+
)
|
97
|
+
|
98
|
+
expect(list.count).to eq(3)
|
99
|
+
|
100
|
+
# Moves [key3, c] to position 0 and deletes [key1, d]
|
101
|
+
expect(list[0]).to be_a(Hokusai::MovePatch)
|
102
|
+
expect(list[0].from).to eq(2)
|
103
|
+
expect(list[0].to).to eq(0)
|
104
|
+
expect(list[0].delete).to eq(true)
|
105
|
+
|
106
|
+
# Inserts [key4, e] into position 1
|
107
|
+
expect(list[1]).to be_a(Hokusai::InsertPatch)
|
108
|
+
expect(list[1].target).to eq(1)
|
109
|
+
expect(list[1].value).to eq(:e)
|
110
|
+
expect(list[1].delete).to be(false)
|
111
|
+
|
112
|
+
expect(list[2]).to be_a(Hokusai::InsertPatch)
|
113
|
+
expect(list[2].target).to eq(3)
|
114
|
+
expect(list[2].value).to eq(:b)
|
115
|
+
expect(list[2].delete).to eq(true) # delete doesn't need to be true here...
|
116
|
+
end
|
117
|
+
end
|
@@ -27,7 +27,7 @@ describe "Providers" do
|
|
27
27
|
slot
|
28
28
|
EOF
|
29
29
|
|
30
|
-
provide :provision,
|
30
|
+
provide :provision, counter_klass.new
|
31
31
|
|
32
32
|
def state
|
33
33
|
self.class.provides[:provision]
|
@@ -212,4 +212,91 @@ describe "Providers" do
|
|
212
212
|
expect(provided.state.count).to eq(2)
|
213
213
|
end
|
214
214
|
end
|
215
|
+
|
216
|
+
describe "Loops with conditionals" do
|
217
|
+
let(:loop_if_container) do
|
218
|
+
provider_klass = provider
|
219
|
+
inject_klass = inject
|
220
|
+
|
221
|
+
c = Class.new(Hokusai::Block) do
|
222
|
+
template <<~EOF
|
223
|
+
[template]
|
224
|
+
container
|
225
|
+
provision
|
226
|
+
[for="value in values"]
|
227
|
+
vblock { :key="value" }
|
228
|
+
[if="show"]
|
229
|
+
vblock
|
230
|
+
inject
|
231
|
+
[else]
|
232
|
+
vblock
|
233
|
+
injectelse
|
234
|
+
control
|
235
|
+
EOF
|
236
|
+
|
237
|
+
uses(
|
238
|
+
container: Hokusai::Blocks::Vblock,
|
239
|
+
vblock: Hokusai::Blocks::Vblock,
|
240
|
+
control: inject_klass,
|
241
|
+
inject: inject_klass,
|
242
|
+
injectelse: inject_klass,
|
243
|
+
provision: provider_klass
|
244
|
+
)
|
245
|
+
|
246
|
+
attr_accessor :show
|
247
|
+
|
248
|
+
def initialize(**args)
|
249
|
+
@show = false
|
250
|
+
|
251
|
+
super
|
252
|
+
end
|
253
|
+
|
254
|
+
def values
|
255
|
+
[1,2,3,4,5]
|
256
|
+
end
|
257
|
+
|
258
|
+
def toggle
|
259
|
+
self.show = !show
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
c.mount
|
264
|
+
end
|
265
|
+
|
266
|
+
it "provisions extend to conditional children in loops" do
|
267
|
+
loop_if_container.toggle
|
268
|
+
loop_if_container.update
|
269
|
+
|
270
|
+
injecteds = get_blocks_by_type(loop_if_container, "inject")
|
271
|
+
provided = get_block_by_type(loop_if_container, "provision")
|
272
|
+
control = get_block_by_type(loop_if_container, "control")
|
273
|
+
|
274
|
+
injecteds.each do |injected|
|
275
|
+
expect(injected.provision.count).to eq(0)
|
276
|
+
end
|
277
|
+
|
278
|
+
injecteds.first.provision.increment
|
279
|
+
|
280
|
+
injecteds.each do |injected|
|
281
|
+
expect(injected.provision.count).to eq(1)
|
282
|
+
end
|
283
|
+
|
284
|
+
expect(provided.state.count).to eq(1)
|
285
|
+
expect(control.provision).to be(nil)
|
286
|
+
|
287
|
+
loop_if_container.toggle
|
288
|
+
loop_if_container.update
|
289
|
+
|
290
|
+
inject_elses = get_blocks_by_type(loop_if_container, "injectelse")
|
291
|
+
|
292
|
+
inject_elses.each do |inject_else|
|
293
|
+
expect(inject_else.provision).not_to be(nil)
|
294
|
+
expect(inject_else.provision.count).to eq(1)
|
295
|
+
end
|
296
|
+
|
297
|
+
inject_elses.first.provision.increment
|
298
|
+
|
299
|
+
expect(provided.state.count).to eq(2)
|
300
|
+
end
|
301
|
+
end
|
215
302
|
end
|
@@ -192,6 +192,12 @@ module Hokusai::Backends
|
|
192
192
|
last_input_hash = nil
|
193
193
|
|
194
194
|
until Raylib.WindowShouldClose
|
195
|
+
if Raylib.IsWindowFocused
|
196
|
+
Raylib.DisableEventWaiting
|
197
|
+
else
|
198
|
+
Raylib.EnableEventWaiting
|
199
|
+
end
|
200
|
+
|
195
201
|
last_width = Raylib.GetScreenWidth
|
196
202
|
last_height = Raylib.GetScreenHeight
|
197
203
|
|
@@ -206,9 +206,7 @@ class Hokusai::Blocks::Text < Hokusai::Block
|
|
206
206
|
self.last_content = content
|
207
207
|
end
|
208
208
|
|
209
|
-
height = internal_render(last_clamping, canvas)
|
210
|
-
self.last_content = last_content
|
211
|
-
|
209
|
+
height = internal_render(last_clamping, canvas)
|
212
210
|
new_height = height + padding.bottom + padding.top
|
213
211
|
new_height -= size if markdown
|
214
212
|
|
data/ui/src/hokusai/diff.rb
CHANGED
@@ -2,11 +2,12 @@ module Hokusai
|
|
2
2
|
# Represents a patch to move a loop item
|
3
3
|
# from one location to another
|
4
4
|
class MovePatch
|
5
|
-
attr_accessor :from, :to, :delete
|
5
|
+
attr_accessor :from, :to, :value, :delete
|
6
6
|
|
7
|
-
def initialize(from:, to:, delete: false)
|
7
|
+
def initialize(from:, to:, value:, delete: false)
|
8
8
|
@from = from
|
9
9
|
@to = to
|
10
|
+
@value = value
|
10
11
|
@delete = delete
|
11
12
|
end
|
12
13
|
end
|
@@ -96,12 +97,12 @@ module Hokusai
|
|
96
97
|
# move a to 2
|
97
98
|
before[bi[:index]] = [ckey, current] # before[2] = a
|
98
99
|
# update index
|
99
|
-
mapbefore[ckey] = { index: bi, value: current }
|
100
|
+
mapbefore[ckey] = { index: bi[:index], value: current }
|
100
101
|
|
101
102
|
# move c to 0
|
102
|
-
yield MovePatch.new(from: bi, to: i)
|
103
|
+
yield MovePatch.new(from: bi[:index], to: i, value: bi[:value])
|
103
104
|
else
|
104
|
-
yield MovePatch.new(from: bi, to: i, delete: true)
|
105
|
+
yield MovePatch.new(from: bi[:index], to: i, value: bi[:value], delete: true)
|
105
106
|
mapbefore[ckey] = nil
|
106
107
|
deletions += 1
|
107
108
|
# next
|
data/ui/src/hokusai/meta.rb
CHANGED
@@ -71,9 +71,7 @@ module Hokusai
|
|
71
71
|
child_block.node.meta.set_prop(ast.loop.var.to_sym, value)
|
72
72
|
child_block.node.meta.publisher.add(target)
|
73
73
|
|
74
|
-
UpdateEntry.new(child_block, block, target).register(context: ctx)
|
75
|
-
|
76
|
-
child_block.class.provides.merge!(parent.class.provides)
|
74
|
+
UpdateEntry.new(child_block, block, target).register(context: ctx, providers: mount_providers.merge(child_block.providers))
|
77
75
|
|
78
76
|
block.node.meta << child_block
|
79
77
|
|
@@ -150,9 +148,8 @@ module Hokusai
|
|
150
148
|
ctx.add_entry(ast.loop.var, patch.value)
|
151
149
|
children[patch.target].node.add_styles(target.class)
|
152
150
|
children[patch.target].node.add_props_from_block(target, context: ctx)
|
153
|
-
children[patch.target].node.meta.set_prop(ast.loop.var.to_sym, patch.value)
|
154
151
|
|
155
|
-
UpdateEntry.new(children[patch.target], uparent, utarget).register(context: ctx)
|
152
|
+
# UpdateEntry.new(children[patch.target], uparent, utarget).register(context: ctx)
|
156
153
|
when MovePatch
|
157
154
|
if patch.delete
|
158
155
|
from = children[patch.from]
|
@@ -203,12 +200,10 @@ module Hokusai
|
|
203
200
|
portal = Node.new(ast)
|
204
201
|
node = child_block_class.compile(target_ast.type, portal)
|
205
202
|
node.add_props_from_block(target, context: ctx)
|
206
|
-
child_block = NodeMounter.new(node, child_block_class).mount(context:
|
203
|
+
child_block = NodeMounter.new(node, child_block_class).mount(context: nil, providers: mount_providers.merge(ublock.providers))
|
207
204
|
child_block.node.add_styles(target.class)
|
208
205
|
child_block.node.meta.publisher.add(target)
|
209
206
|
|
210
|
-
child_block.class.provides.merge!(parent.class.provides)
|
211
|
-
|
212
207
|
if patch.delete
|
213
208
|
children[patch.target] = child_block
|
214
209
|
else
|
@@ -40,18 +40,19 @@ module Hokusai
|
|
40
40
|
unless child_present.call(child)
|
41
41
|
portal = Node.new(child, Node.new(child))
|
42
42
|
node = child_block_klass.compile("root", portal)
|
43
|
+
node.add_styles(target.class)
|
44
|
+
node.add_props_from_block(target, context: context)
|
45
|
+
node.meta.publisher.add(target)
|
46
|
+
|
43
47
|
stack = []
|
44
48
|
child.children.each_with_index do |ast, ast_index|
|
45
|
-
stack << MountEntry.new(ast_index, ast, ublock, uparent, utarget)
|
49
|
+
stack << MountEntry.new(ast_index, ast, ublock, uparent, utarget, providers: providers)
|
46
50
|
end
|
47
51
|
|
48
|
-
child_block = NodeMounter.new(node, child_block_klass, [stack]).mount(context: context, providers: providers)
|
49
|
-
UpdateEntry.new(child_block, block, target).register(context: context, providers: providers)
|
50
|
-
|
51
|
-
child_block.node.add_styles(target.class)
|
52
|
-
child_block.node.add_props_from_block(target, context: context)
|
53
|
-
child_block.node.meta.publisher.add(target)
|
52
|
+
child_block = NodeMounter.new(node, child_block_klass, [stack], previous_providers: providers).mount(context: context, providers: providers)
|
54
53
|
|
54
|
+
UpdateEntry.new(child_block, block, target).register(context: context, providers: providers)
|
55
|
+
|
55
56
|
meta.children!.insert(index, child_block)
|
56
57
|
|
57
58
|
child_block.public_send(:before_updated) if child_block.respond_to?(:before_updated)
|
@@ -66,21 +67,21 @@ module Hokusai
|
|
66
67
|
end
|
67
68
|
|
68
69
|
if child.has_else_condition? && !child.else_condition_active?
|
69
|
-
portal = Node.new(child, Node.new(child))
|
70
|
+
portal = Node.new(child.else_ast, Node.new(child))
|
70
71
|
else_child_block_klass = target.class.use(child.else_ast.type)
|
71
|
-
node = else_child_block_klass.compile(child.else_ast.type, portal)
|
72
72
|
|
73
|
+
node = else_child_block_klass.compile(child.else_ast.type, portal)
|
74
|
+
node.add_styles(utarget.class)
|
75
|
+
node.add_props_from_block(utarget, context: context)
|
76
|
+
node.meta.publisher.add(utarget)
|
77
|
+
|
73
78
|
stack = []
|
74
79
|
child.else_ast.children.each_with_index do |ast, ast_index|
|
75
|
-
stack << MountEntry.new(ast_index, ast, ublock, uparent, utarget)
|
80
|
+
stack << MountEntry.new(ast_index, ast, ublock, uparent, utarget, providers: providers)
|
76
81
|
end
|
77
82
|
|
78
|
-
child_block = NodeMounter.new(node, else_child_block_klass, [stack]).mount(context: context, providers: providers)
|
83
|
+
child_block = NodeMounter.new(node, else_child_block_klass, [stack], previous_providers: providers).mount(context: context, providers: providers)
|
79
84
|
UpdateEntry.new(child_block, block, utarget).register(context: context, providers: providers)
|
80
|
-
|
81
|
-
child_block.node.add_styles(utarget.class)
|
82
|
-
child_block.node.add_props_from_block(utarget, context: context)
|
83
|
-
child_block.node.meta.publisher.add(utarget)
|
84
85
|
meta.children!.insert(index, child_block)
|
85
86
|
|
86
87
|
child_block.public_send(:before_updated) if child_block.respond_to?(:before_updated)
|
data/ui/src/hokusai/node.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "securerandom"
|
4
4
|
require "forwardable"
|
5
5
|
require_relative "./node_mounter"
|
6
6
|
require_relative "./meta"
|
@@ -21,7 +21,7 @@ module Hokusai
|
|
21
21
|
def initialize(ast, portal = nil)
|
22
22
|
@ast = ast
|
23
23
|
@portal = portal
|
24
|
-
@uuid =
|
24
|
+
@uuid = SecureRandom.hex(6).freeze
|
25
25
|
@meta = Meta.new
|
26
26
|
end
|
27
27
|
|
@@ -7,8 +7,8 @@ module Hokusai
|
|
7
7
|
attr_accessor :primary_stack, :secondary_stack
|
8
8
|
attr_reader :root
|
9
9
|
|
10
|
-
def initialize(node, klass, secondary_stack = [], previous_target = nil)
|
11
|
-
@root = klass.new(node: node)
|
10
|
+
def initialize(node, klass, secondary_stack = [], previous_target = nil, previous_providers: {})
|
11
|
+
@root = klass.new(node: node, providers: previous_providers)
|
12
12
|
|
13
13
|
raise Hokusai::Error.new("Root #{klass} doesn't have a node. Did you remember to call `super`?") if @root.node.nil?
|
14
14
|
|
@@ -106,7 +106,6 @@ module Hokusai
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
root.public_send(:on_mounted) if root.respond_to?(:on_mounted)
|
110
109
|
root
|
111
110
|
end
|
112
111
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hokusai-zero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- skinnyjames
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -197,6 +197,7 @@ files:
|
|
197
197
|
- ui/spec/hokusai/automation/keys_transcoder_spec.rb
|
198
198
|
- ui/spec/hokusai/automation/selector_spec.rb
|
199
199
|
- ui/spec/hokusai/block_spec.rb
|
200
|
+
- ui/spec/hokusai/diff_spec.rb
|
200
201
|
- ui/spec/hokusai/directives_spec.rb
|
201
202
|
- ui/spec/hokusai/e2e/client_spec.rb
|
202
203
|
- ui/spec/hokusai/e2e/meta_spec.rb
|