@almadar/std 16.174.0 → 16.176.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.
Files changed (45) hide show
  1. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +4 -9
  2. package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +132 -22
  3. package/behaviors/lolo/ui/learning/atoms/std-learn-derivation.lolo +428 -0
  4. package/behaviors/lolo/ui/learning/atoms/std-learn-doser.lolo +520 -0
  5. package/behaviors/lolo/ui/learning/atoms/std-learn-scatter-fit.lolo +805 -0
  6. package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +31 -2
  7. package/behaviors/lolo/ui/learning/atoms/std-learn-transport.lolo +186 -0
  8. package/behaviors/lolo/ui/learning/atoms/std-learn-tree.lolo +522 -0
  9. package/behaviors/lolo/ui/learning/atoms/ui-algo-graph-canvas.lolo +192 -0
  10. package/behaviors/lolo/ui/learning/atoms/ui-algorithm-canvas.lolo +111 -2
  11. package/behaviors/lolo/ui/learning/atoms/ui-biology-canvas.lolo +150 -1
  12. package/behaviors/lolo/ui/learning/atoms/ui-chemistry-canvas.lolo +132 -1
  13. package/behaviors/lolo/ui/learning/atoms/ui-learning-canvas.lolo +48 -1
  14. package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +141 -1
  15. package/behaviors/lolo/ui/learning/atoms/ui-physics-canvas.lolo +199 -1
  16. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +4 -39
  17. package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +1288 -197
  18. package/behaviors/registry/ui/learning/atoms/std-learn-derivation.orb +2512 -0
  19. package/behaviors/registry/ui/learning/atoms/std-learn-doser.orb +3678 -0
  20. package/behaviors/registry/ui/learning/atoms/std-learn-scatter-fit.orb +6539 -0
  21. package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +173 -2
  22. package/behaviors/registry/ui/learning/atoms/std-learn-transport.orb +986 -0
  23. package/behaviors/registry/ui/learning/atoms/std-learn-tree.orb +3333 -0
  24. package/behaviors/registry/ui/learning/atoms/ui-algo-graph-canvas.orb +716 -0
  25. package/behaviors/registry/ui/learning/atoms/ui-algorithm-canvas.orb +506 -1
  26. package/behaviors/registry/ui/learning/atoms/ui-biology-canvas.orb +684 -0
  27. package/behaviors/registry/ui/learning/atoms/ui-chemistry-canvas.orb +634 -0
  28. package/behaviors/registry/ui/learning/atoms/ui-learning-canvas.orb +256 -0
  29. package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +736 -12
  30. package/behaviors/registry/ui/learning/atoms/ui-physics-canvas.orb +1061 -33
  31. package/dist/behaviors/exports-reader.js +2448 -2184
  32. package/dist/behaviors/functions/index.d.ts +800 -13
  33. package/dist/behaviors/functions/index.js +2431 -2185
  34. package/dist/behaviors/index.d.ts +1 -1
  35. package/dist/behaviors/index.js +2449 -2185
  36. package/dist/behaviors/query.js +2448 -2184
  37. package/dist/behaviors-embeddings.hash.json +3 -3
  38. package/dist/behaviors-embeddings.json +318 -294
  39. package/dist/behaviors-registry.json +4391 -1298
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.js +2449 -2185
  42. package/dist/knob-embeddings.hash.json +3 -3
  43. package/dist/knob-embeddings.json +1 -1
  44. package/dist/registry/factory-signatures.json +1 -1
  45. package/package.json +2 -2
@@ -0,0 +1,522 @@
1
+ app std-learn-tree "1.0.0"
2
+ "Generic ordered-tree builder instrument — inserts values one by one from a config-declared sequence, each value descends left/right by comparison against the current node and attaches as a leaf; the tree renders on algo-graph-canvas (layout: tree, auto-positioned from the parent/child edges), settled nodes green, the descent cursor red, the comparison trail amber. Traverse sweeps the tree to the chosen order (inorder/preorder/levelorder) one node per press or on auto-run, narrating the visit order so far. Vocabulary, sample values, colors, and labels are entirely config-declared, so the same mechanism serves any ordered-tree domain. Standalone by default; behind a lab router set matchMode and it renders only when MODE_SELECTED matches."
3
+ orbital LearnTreeOrbital {
4
+ type LearnTreeNode = {
5
+ id : string!
6
+ label : string
7
+ state : "unvisited" | "frontier" | "current" | "visited" | "goal" | "path"
8
+ }
9
+ type LearnTreeEdge = {
10
+ from : string!
11
+ to : string!
12
+ directed : boolean
13
+ label : string
14
+ state : "default" | "tree" | "relaxed" | "candidate" | "path"
15
+ }
16
+ type LearnTreeEntry = {
17
+ id : string!
18
+ value : number!
19
+ rank : number!
20
+ path : string
21
+ levelKey : number
22
+ }
23
+
24
+ entity LearnTreeScene [runtime] {
25
+ id : string!
26
+ active : boolean = false
27
+ title : string
28
+ nodes : [LearnTreeNode]
29
+ edges : [LearnTreeEdge]
30
+ entries : [LearnTreeEntry]
31
+ cursor : number = 0
32
+ pending : number = 0
33
+ descending : boolean = false
34
+ trail : [string]
35
+ curId : string = ""
36
+ curVal : number = 0
37
+ curRank : number = 0
38
+ curRankOffset : number = 0
39
+ depth : number = 0
40
+ maxDepth : number = 0
41
+ count : number = 0
42
+ traversing : boolean = false
43
+ traversalOrder : [string]
44
+ sweptIds : [string]
45
+ done : boolean = false
46
+ running : boolean = false
47
+ narration : string
48
+ }
49
+
50
+ trait LearnTreeSim -> @rebindable LearnTreeScene [interaction, instance] {
51
+ initial: idle
52
+
53
+ state idle {
54
+ INIT -> idle when (== @config.matchMode "")
55
+ (set @entity.active true)
56
+ (set @entity.title @config.title)
57
+ (set @entity.nodes [])
58
+ (set @entity.edges [])
59
+ (set @entity.entries [])
60
+ (set @entity.cursor 0)
61
+ (set @entity.descending false)
62
+ (set @entity.trail [])
63
+ (set @entity.depth 0)
64
+ (set @entity.maxDepth 0)
65
+ (set @entity.count 0)
66
+ (set @entity.traversing false)
67
+ (set @entity.traversalOrder [])
68
+ (set @entity.sweptIds [])
69
+ (set @entity.done false)
70
+ (set @entity.running @config.autoRun)
71
+ (set @entity.narration "Press Insert to add the next value; keep pressing to watch it descend.")
72
+ (render-ui main {
73
+ type: box
74
+ padding: md
75
+ children: [
76
+ { type: stack, direction: vertical, gap: md, children: [
77
+ { type: typography, variant: h4, content: @entity.title },
78
+ { type: stack, direction: horizontal, gap: sm, children: [
79
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
80
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
81
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
82
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
83
+ { type: button, action: RESET, label: "Reset", variant: ghost }
84
+ ] },
85
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
86
+ { type: typography, variant: body, content: @entity.narration }
87
+ ] }
88
+ ]
89
+ })
90
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
91
+
92
+ MODE_SELECTED -> idle when (== ?mode @config.matchMode)
93
+ (set @entity.active true)
94
+ (set @entity.title @config.title)
95
+ (set @entity.nodes [])
96
+ (set @entity.edges [])
97
+ (set @entity.entries [])
98
+ (set @entity.cursor 0)
99
+ (set @entity.descending false)
100
+ (set @entity.trail [])
101
+ (set @entity.depth 0)
102
+ (set @entity.maxDepth 0)
103
+ (set @entity.count 0)
104
+ (set @entity.traversing false)
105
+ (set @entity.traversalOrder [])
106
+ (set @entity.sweptIds [])
107
+ (set @entity.done false)
108
+ (set @entity.running @config.autoRun)
109
+ (set @entity.narration "Press Insert to add the next value; keep pressing to watch it descend.")
110
+ (render-ui main {
111
+ type: box
112
+ padding: md
113
+ children: [
114
+ { type: stack, direction: vertical, gap: md, children: [
115
+ { type: typography, variant: h4, content: @entity.title },
116
+ { type: stack, direction: horizontal, gap: sm, children: [
117
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
118
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
119
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
120
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
121
+ { type: button, action: RESET, label: "Reset", variant: ghost }
122
+ ] },
123
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
124
+ { type: typography, variant: body, content: @entity.narration }
125
+ ] }
126
+ ]
127
+ })
128
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
129
+
130
+ INSERT_VALUE -> idle when (and (== @entity.active true) (or @entity.descending (< @entity.cursor (array/len @config.values))))
131
+ (set @entity.traversing false)
132
+ (set @entity.traversalOrder [])
133
+ (set @entity.sweptIds [])
134
+ (set @entity.done false)
135
+ (if (not @entity.descending)
136
+ (let (
137
+ (v (array/nth @config.values @entity.cursor))
138
+ )
139
+ (if (== @entity.count 0)
140
+ (do
141
+ (set @entity.pending @v)
142
+ (set @entity.nodes (array/append @entity.nodes { id: "n0", label: (str/concat "" @v), state: "visited" }))
143
+ (set @entity.entries (array/append @entity.entries { id: "n0", value: @v, rank: 300, path: "", levelKey: 300 }))
144
+ (set @entity.count 1)
145
+ (set @entity.cursor 1)
146
+ (set @entity.narration (str/concat "insert " (str/concat "" @v) " — tree empty, becomes root" (if @config.balanced " · height 1 for 1 node" ""))))
147
+ (do
148
+ (set @entity.pending @v)
149
+ (set @entity.descending true)
150
+ (set @entity.trail [])
151
+ (set @entity.curId (object/get (array/first @entity.entries) id))
152
+ (set @entity.curVal (object/get (array/first @entity.entries) value))
153
+ (set @entity.curRank (object/get (array/first @entity.entries) rank))
154
+ (set @entity.curRankOffset 150)
155
+ (set @entity.depth 0)
156
+ (set @entity.cursor (+ @entity.cursor 1))
157
+ (set @entity.narration (str/concat "insert " (str/concat "" @v) (if @config.showComparisons " — compare at the root" ""))))))
158
+ (let (
159
+ (side (if (< @entity.pending @entity.curVal) "L" "R"))
160
+ (child (array/find @entity.edges (fn e (and (== (object/get @e from) @entity.curId) (== (object/get @e label) @side)))))
161
+ (newRank (if (== @side "L") (- @entity.curRank @entity.curRankOffset) (+ @entity.curRank @entity.curRankOffset)))
162
+ (newId (str/concat "n" (str/concat "" @entity.count)))
163
+ (newDepth (+ @entity.depth 1))
164
+ (newCount (+ @entity.count 1))
165
+ (newMax (math/max @entity.maxDepth @newDepth))
166
+ (cmpText (str/concat (str/concat "" @entity.pending) (if (== @side "L") " < " " >= ") (str/concat "" @entity.curVal)))
167
+ )
168
+ (if (== @child null)
169
+ (do
170
+ (set @entity.narration (str/concat (if @config.showComparisons (str/concat @cmpText " — ") "") "attach " (str/concat "" @entity.pending) " as a leaf" (if @config.balanced (str/concat " · height " (str/concat "" (+ @newMax 1)) " for " (str/concat "" @newCount) " nodes") "")))
171
+ (set @entity.nodes (array/append @entity.nodes { id: @newId, label: (str/concat "" @entity.pending), state: "visited" }))
172
+ (set @entity.edges (array/append @entity.edges { from: @entity.curId, to: @newId, directed: true, label: @side, state: "tree" }))
173
+ (set @entity.entries (array/append @entity.entries { id: @newId, value: @entity.pending, rank: @newRank, path: (str/concat (object/get (array/find @entity.entries (fn t (== (object/get @t id) @entity.curId))) path) @side), levelKey: (+ (* @newDepth 10000) @newRank) }))
174
+ (set @entity.count @newCount)
175
+ (set @entity.maxDepth @newMax)
176
+ (set @entity.descending false)
177
+ (set @entity.trail []))
178
+ (do
179
+ (set @entity.narration (str/concat (if @config.showComparisons (str/concat @cmpText " — ") "") "descend " (if (== @side "L") "left" "right")))
180
+ (set @entity.trail (array/append @entity.trail @entity.curId))
181
+ (set @entity.curId (object/get @child to))
182
+ (set @entity.curVal (object/get (array/find @entity.entries (fn t (== (object/get @t id) (object/get @child to)))) value))
183
+ (set @entity.curRank (object/get (array/find @entity.entries (fn t (== (object/get @t id) (object/get @child to)))) rank))
184
+ (set @entity.curRankOffset (/ @entity.curRankOffset 2))
185
+ (set @entity.depth @newDepth)))))
186
+ (set @entity.nodes (array/map @entity.nodes (fn n
187
+ (object/merge @n {
188
+ state: (if (and (== @entity.descending true) (== (object/get @n id) @entity.curId)) "current"
189
+ (if (and (== @entity.descending true) (array/includes @entity.trail (object/get @n id))) "frontier"
190
+ "visited"))
191
+ }))))
192
+ (set @entity.edges (array/map @entity.edges (fn e
193
+ (object/merge @e {
194
+ state: (if (and (== @entity.descending true) (== (object/get @e from) @entity.curId)) "candidate" "tree")
195
+ }))))
196
+ (render-ui main {
197
+ type: box
198
+ padding: md
199
+ children: [
200
+ { type: stack, direction: vertical, gap: md, children: [
201
+ { type: typography, variant: h4, content: @entity.title },
202
+ { type: stack, direction: horizontal, gap: sm, children: [
203
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
204
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
205
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
206
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
207
+ { type: button, action: RESET, label: "Reset", variant: ghost }
208
+ ] },
209
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
210
+ { type: typography, variant: body, content: @entity.narration }
211
+ ] }
212
+ ]
213
+ })
214
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
215
+
216
+ DELETE_VALUE -> idle when (and (== @entity.active true) (> @entity.count 0))
217
+ (let (
218
+ (lastId (object/get (array/last @entity.entries) id))
219
+ (lastVal (object/get (array/last @entity.entries) value))
220
+ )
221
+ (do
222
+ (set @entity.nodes (array/filter @entity.nodes (fn n (!= (object/get @n id) @lastId))))
223
+ (set @entity.edges (array/filter @entity.edges (fn e (and (!= (object/get @e to) @lastId) (!= (object/get @e from) @lastId)))))
224
+ (set @entity.entries (array/filter @entity.entries (fn t (!= (object/get @t id) @lastId))))
225
+ (set @entity.count (- @entity.count 1))
226
+ (set @entity.cursor (math/max (- @entity.cursor 1) 0))
227
+ (set @entity.descending false)
228
+ (set @entity.trail [])
229
+ (set @entity.traversing false)
230
+ (set @entity.traversalOrder [])
231
+ (set @entity.sweptIds [])
232
+ (set @entity.done false)
233
+ (set @entity.narration (str/concat "remove " (str/concat "" @lastVal) " — the most recently inserted value (undo); press Insert to re-insert"))))
234
+ (set @entity.nodes (array/map @entity.nodes (fn n (object/merge @n { state: "visited" }))))
235
+ (set @entity.edges (array/map @entity.edges (fn e (object/merge @e { state: "tree" }))))
236
+ (render-ui main {
237
+ type: box
238
+ padding: md
239
+ children: [
240
+ { type: stack, direction: vertical, gap: md, children: [
241
+ { type: typography, variant: h4, content: @entity.title },
242
+ { type: stack, direction: horizontal, gap: sm, children: [
243
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
244
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
245
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
246
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
247
+ { type: button, action: RESET, label: "Reset", variant: ghost }
248
+ ] },
249
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
250
+ { type: typography, variant: body, content: @entity.narration }
251
+ ] }
252
+ ]
253
+ })
254
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
255
+
256
+ RUN_TRAVERSAL -> idle when (and (== @entity.active true) (> @entity.count 0))
257
+ (if (or (== @entity.traversing false) (== @entity.done true))
258
+ (let (
259
+ (order (if (== @config.traversalKind "preorder")
260
+ (array/map (array/sort @entity.entries "path" "asc") (fn t (object/get @t id)))
261
+ (if (== @config.traversalKind "levelorder")
262
+ (array/map (array/sort @entity.entries "levelKey" "asc") (fn t (object/get @t id)))
263
+ (array/map (array/sort @entity.entries "value" "asc") (fn t (object/get @t id))))))
264
+ (firstId (array/first @order))
265
+ )
266
+ (let (
267
+ (firstVal (object/get (array/find @entity.entries (fn t (== (object/get @t id) @firstId))) value))
268
+ )
269
+ (do
270
+ (set @entity.traversalOrder @order)
271
+ (set @entity.sweptIds [@firstId])
272
+ (set @entity.traversing true)
273
+ (set @entity.done (== (array/len @order) 1))
274
+ (set @entity.narration (str/concat @config.traversalKind " traversal — visiting order so far: " (str/concat "" @firstVal))))))
275
+ (let (
276
+ (nextId (array/nth @entity.traversalOrder (array/len @entity.sweptIds)))
277
+ )
278
+ (let (
279
+ (newSwept (array/append @entity.sweptIds @nextId))
280
+ )
281
+ (do
282
+ (set @entity.sweptIds @newSwept)
283
+ (set @entity.done (>= (array/len @newSwept) (array/len @entity.traversalOrder)))
284
+ (set @entity.narration (str/concat @config.traversalKind " traversal — visiting order so far: " (str/join (array/map @newSwept (fn sid (str/concat "" (object/get (array/find @entity.entries (fn t (== (object/get @t id) @sid))) value)))) ", ")))))))
285
+ (set @entity.nodes (array/map @entity.nodes (fn n
286
+ (object/merge @n {
287
+ state: (if (and (== @entity.traversing true) (array/includes @entity.sweptIds (object/get @n id))) "path" "visited")
288
+ }))))
289
+ (set @entity.edges (array/map @entity.edges (fn e (object/merge @e { state: "tree" }))))
290
+ (render-ui main {
291
+ type: box
292
+ padding: md
293
+ children: [
294
+ { type: stack, direction: vertical, gap: md, children: [
295
+ { type: typography, variant: h4, content: @entity.title },
296
+ { type: stack, direction: horizontal, gap: sm, children: [
297
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
298
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
299
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
300
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
301
+ { type: button, action: RESET, label: "Reset", variant: ghost }
302
+ ] },
303
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
304
+ { type: typography, variant: body, content: @entity.narration }
305
+ ] }
306
+ ]
307
+ })
308
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
309
+
310
+ TOGGLE_RUN -> idle when (== @entity.active true)
311
+ (set @entity.running (not @entity.running))
312
+ (set @entity.narration (if @entity.running "Auto-stepping the traversal — tick sweeps one node at a time." "Paused — step the traversal manually with Traverse."))
313
+ (render-ui main {
314
+ type: box
315
+ padding: md
316
+ children: [
317
+ { type: stack, direction: vertical, gap: md, children: [
318
+ { type: typography, variant: h4, content: @entity.title },
319
+ { type: stack, direction: horizontal, gap: sm, children: [
320
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
321
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
322
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
323
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
324
+ { type: button, action: RESET, label: "Reset", variant: ghost }
325
+ ] },
326
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
327
+ { type: typography, variant: body, content: @entity.narration }
328
+ ] }
329
+ ]
330
+ })
331
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
332
+
333
+ RESET -> idle when (== @entity.active true)
334
+ (set @entity.nodes [])
335
+ (set @entity.edges [])
336
+ (set @entity.entries [])
337
+ (set @entity.cursor 0)
338
+ (set @entity.descending false)
339
+ (set @entity.trail [])
340
+ (set @entity.depth 0)
341
+ (set @entity.maxDepth 0)
342
+ (set @entity.count 0)
343
+ (set @entity.traversing false)
344
+ (set @entity.traversalOrder [])
345
+ (set @entity.sweptIds [])
346
+ (set @entity.done false)
347
+ (set @entity.running @config.autoRun)
348
+ (set @entity.narration "Press Insert to add the next value; keep pressing to watch it descend.")
349
+ (render-ui main {
350
+ type: box
351
+ padding: md
352
+ children: [
353
+ { type: stack, direction: vertical, gap: md, children: [
354
+ { type: typography, variant: h4, content: @entity.title },
355
+ { type: stack, direction: horizontal, gap: sm, children: [
356
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
357
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
358
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
359
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
360
+ { type: button, action: RESET, label: "Reset", variant: ghost }
361
+ ] },
362
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
363
+ { type: typography, variant: body, content: @entity.narration }
364
+ ] }
365
+ ]
366
+ })
367
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
368
+ }
369
+
370
+ ticks {
371
+ treeTick every 500ms
372
+ when (and (== @entity.active true) (and (== @entity.running true) (not (== @entity.done true))))
373
+ (if (or (== @entity.traversing false) (== @entity.done true))
374
+ (let (
375
+ (order (if (== @config.traversalKind "preorder")
376
+ (array/map (array/sort @entity.entries "path" "asc") (fn t (object/get @t id)))
377
+ (if (== @config.traversalKind "levelorder")
378
+ (array/map (array/sort @entity.entries "levelKey" "asc") (fn t (object/get @t id)))
379
+ (array/map (array/sort @entity.entries "value" "asc") (fn t (object/get @t id))))))
380
+ (firstId (array/first @order))
381
+ )
382
+ (let (
383
+ (firstVal (object/get (array/find @entity.entries (fn t (== (object/get @t id) @firstId))) value))
384
+ )
385
+ (do
386
+ (set @entity.traversalOrder @order)
387
+ (set @entity.sweptIds [@firstId])
388
+ (set @entity.traversing true)
389
+ (set @entity.done (== (array/len @order) 1))
390
+ (set @entity.narration (str/concat @config.traversalKind " traversal — visiting order so far: " (str/concat "" @firstVal))))))
391
+ (let (
392
+ (nextId (array/nth @entity.traversalOrder (array/len @entity.sweptIds)))
393
+ )
394
+ (let (
395
+ (newSwept (array/append @entity.sweptIds @nextId))
396
+ )
397
+ (do
398
+ (set @entity.sweptIds @newSwept)
399
+ (set @entity.done (>= (array/len @newSwept) (array/len @entity.traversalOrder)))
400
+ (set @entity.narration (str/concat @config.traversalKind " traversal — visiting order so far: " (str/join (array/map @newSwept (fn sid (str/concat "" (object/get (array/find @entity.entries (fn t (== (object/get @t id) @sid))) value)))) ", ")))))))
401
+ (set @entity.nodes (array/map @entity.nodes (fn n
402
+ (object/merge @n {
403
+ state: (if (and (== @entity.traversing true) (array/includes @entity.sweptIds (object/get @n id))) "path" "visited")
404
+ }))))
405
+ (set @entity.edges (array/map @entity.edges (fn e (object/merge @e { state: "tree" }))))
406
+ (render-ui main {
407
+ type: box
408
+ padding: md
409
+ children: [
410
+ { type: stack, direction: vertical, gap: md, children: [
411
+ { type: typography, variant: h4, content: @entity.title },
412
+ { type: stack, direction: horizontal, gap: sm, children: [
413
+ { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
414
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
415
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
416
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
417
+ { type: button, action: RESET, label: "Reset", variant: ghost }
418
+ ] },
419
+ { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
420
+ { type: typography, variant: body, content: @entity.narration }
421
+ ] }
422
+ ]
423
+ })
424
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
425
+ }
426
+
427
+ listens {
428
+ MODE_SELECTED {
429
+ mode : string
430
+ }
431
+ @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
432
+ @tier "domain"
433
+ INSERT_VALUE {
434
+ }
435
+ @description "Insert the next config-declared value: descends one comparison step per press from the root, coloring the descent cursor and comparison trail, until it attaches as a leaf. Pressing again while a descent is in flight continues that same descent."
436
+ @synonyms "insert, add, next value, descend, attach"
437
+ @tier "domain"
438
+ DELETE_VALUE {
439
+ }
440
+ @description "Undo the most recently inserted value — removes its node/edge/entry and rewinds the insert cursor by one so the same value can be re-inserted."
441
+ @synonyms "delete, remove, undo, rewind"
442
+ @tier "domain"
443
+ RUN_TRAVERSAL {
444
+ }
445
+ @description "Advance the traversal sweep one node at a time in the configured order (inorder/preorder/levelorder); pressing after the sweep completes starts a fresh sweep."
446
+ @synonyms "traverse, sweep, visit, walk, in-order, pre-order, level-order"
447
+ @tier "domain"
448
+ TOGGLE_RUN {
449
+ }
450
+ @description "Toggle auto-stepping: while running, the tick sweeps the traversal one node forward every step. Has no effect on Insert/Delete, which stay manual."
451
+ @synonyms "run, pause, auto, auto-step"
452
+ @tier "domain"
453
+ RESET {
454
+ }
455
+ @description "Clear the tree back to empty and restore the insert cursor and running flag from config, ready to insert from the first value again."
456
+ @synonyms "reset, clear, restart, start over"
457
+ @tier "domain"
458
+ }
459
+
460
+ emits {
461
+ TREE_UPDATED -> external {
462
+ nodes : [LearnTreeNode]
463
+ edges : [LearnTreeEdge]
464
+ count : number
465
+ narration : string
466
+ }
467
+ @description "The tree after each insert/delete/traversal step — a composed listener (readout, grader) can subscribe; a harmless no-op when the atom runs standalone."
468
+ @synonyms "tree update, insert result, traversal result, node count"
469
+ @tier "domain"
470
+ }
471
+
472
+ config {
473
+ matchMode : string = ""
474
+ @label "Match Mode"
475
+ @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
476
+ @tier "presentation"
477
+ autoRun : boolean = false
478
+ @label "Auto Run"
479
+ @description "When true, the tick auto-steps the traversal sweep started by Traverse; toggle live with the Run/Pause button. Has no effect on Insert/Delete, which stay manual."
480
+ @synonyms "auto run, auto step, auto play"
481
+ @tier "domain"
482
+ values : [number] = [50, 30, 70, 20, 40, 60, 80]
483
+ @label "Values"
484
+ @description "The values Insert feeds into the tree, one per descent, in order."
485
+ @synonyms "keys, values to insert, dataset, sequence"
486
+ @tier "domain"
487
+ traversalKind : string = "inorder"
488
+ @label "Traversal Kind"
489
+ @description "Which sweep Traverse replays — inorder (sorted order, left to right), preorder (root before children), or levelorder (breadth first, top to bottom)."
490
+ @synonyms "traversal, in-order, pre-order, level-order, sweep"
491
+ @tier "domain"
492
+ showComparisons : boolean = true
493
+ @label "Show Comparisons"
494
+ @description "Include each left/right comparison in the narration while a value descends; turn off for a terse attach-only narration."
495
+ @synonyms "comparisons, narration detail"
496
+ @tier "presentation"
497
+ balanced : boolean = false
498
+ @label "Balance Readout"
499
+ @description "When true, each insert's narration reports the tree's current height against its node count so the learner can judge balance; no auto-rotation is performed."
500
+ @synonyms "balance, height, balance readout"
501
+ @tier "domain"
502
+ title : string = "Ordered Tree"
503
+ @label "Title"
504
+ @description "Title shown above the canvas."
505
+ @tier "presentation"
506
+ width : number = 600
507
+ @label "Width"
508
+ @description "Canvas width in pixels."
509
+ @tier "presentation"
510
+ height : number = 400
511
+ @label "Height"
512
+ @description "Canvas height in pixels."
513
+ @tier "presentation"
514
+ backgroundColor : string = "#f8fafc"
515
+ @label "Background Color"
516
+ @description "Canvas background color."
517
+ @tier "presentation"
518
+ }
519
+ }
520
+
521
+ page "/learn-tree" as LearnTreePage -> LearnTreeSim
522
+ }