@almadar/std 16.243.0 → 16.244.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 (140) hide show
  1. package/behaviors/behaviors-registry.json +4470 -513
  2. package/behaviors/lolo/agent/atoms/std-agent-context.lolo +24 -4
  3. package/behaviors/lolo/agent/atoms/std-agent-memory.lolo +20 -5
  4. package/behaviors/lolo/agent/atoms/std-agent-proposal.lolo +550 -0
  5. package/behaviors/lolo/agent/atoms/std-agent-session.lolo +284 -25
  6. package/behaviors/lolo/agent/atoms/std-agent-suggestions.lolo +841 -0
  7. package/behaviors/lolo/agent/atoms/std-agent-tool-loop.lolo +58 -8
  8. package/behaviors/lolo/agent/organisms/std-agent-assistant.lolo +1 -0
  9. package/behaviors/lolo/infra/atoms/std-page-treat.lolo +28 -1
  10. package/behaviors/lolo/ui/avl/atoms/ui-avl-state-machine.lolo +22 -0
  11. package/behaviors/lolo/ui/avl/atoms/ui-avl-transition-explainer.lolo +175 -0
  12. package/behaviors/lolo/ui/avl/atoms/ui-module-card.lolo +22 -0
  13. package/behaviors/lolo/ui/core/atoms/std-app-layout.lolo +23 -4
  14. package/behaviors/lolo/ui/core/atoms/std-hero.lolo +72 -33
  15. package/behaviors/lolo/ui/core/molecules/ui-agent-chat-feed.lolo +1 -1
  16. package/behaviors/lolo/ui/core/molecules/ui-markdown-content.lolo +54 -0
  17. package/behaviors/lolo/ui/core/molecules/ui-upload-drop-zone.lolo +5 -0
  18. package/behaviors/lolo/ui/core/organisms/ui-segment-renderer.lolo +127 -3
  19. package/behaviors/lolo/ui/core/organisms/ui-state-machine-view.lolo +22 -0
  20. package/behaviors/lolo/ui/core/templates/ui-dashboard-layout.lolo +2 -2
  21. package/behaviors/lolo/ui/learning/atoms/std-learn-arrangement.lolo +87 -17
  22. package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +172 -37
  23. package/behaviors/lolo/ui/learning/atoms/std-learn-coupled-sim.lolo +183 -70
  24. package/behaviors/lolo/ui/learning/atoms/std-learn-derivation.lolo +78 -13
  25. package/behaviors/lolo/ui/learning/atoms/std-learn-diagram.lolo +58 -2
  26. package/behaviors/lolo/ui/learning/atoms/std-learn-doser.lolo +107 -12
  27. package/behaviors/lolo/ui/learning/atoms/std-learn-estimate.lolo +83 -10
  28. package/behaviors/lolo/ui/learning/atoms/std-learn-goal-seek.lolo +130 -9
  29. package/behaviors/lolo/ui/learning/atoms/std-learn-grid-sim.lolo +143 -59
  30. package/behaviors/lolo/ui/learning/atoms/std-learn-hotspot.lolo +96 -22
  31. package/behaviors/lolo/ui/learning/atoms/std-learn-live-sim.lolo +66 -5
  32. package/behaviors/lolo/ui/learning/atoms/std-learn-predict.lolo +84 -6
  33. package/behaviors/lolo/ui/learning/atoms/std-learn-sandbox.lolo +80 -14
  34. package/behaviors/lolo/ui/learning/atoms/std-learn-scatter-fit.lolo +99 -5
  35. package/behaviors/lolo/ui/learning/atoms/std-learn-scenario.lolo +81 -7
  36. package/behaviors/lolo/ui/learning/atoms/std-learn-speed-drill.lolo +83 -7
  37. package/behaviors/lolo/ui/learning/atoms/std-learn-stepper.lolo +224 -99
  38. package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +63 -2
  39. package/behaviors/lolo/ui/learning/atoms/std-learn-trace-player.lolo +99 -38
  40. package/behaviors/lolo/ui/learning/atoms/std-learn-transport.lolo +7 -7
  41. package/behaviors/lolo/ui/learning/atoms/std-learn-tree.lolo +69 -4
  42. package/behaviors/lolo-behavior-contracts.json +1 -1
  43. package/behaviors/registry/agent/atoms/std-agent-context.orb +205 -6
  44. package/behaviors/registry/agent/atoms/std-agent-memory.orb +146 -4
  45. package/behaviors/registry/agent/atoms/std-agent-proposal.orb +1972 -0
  46. package/behaviors/registry/agent/atoms/std-agent-session.orb +2021 -597
  47. package/behaviors/registry/agent/atoms/std-agent-suggestions.orb +5079 -0
  48. package/behaviors/registry/agent/atoms/std-agent-tool-loop.orb +287 -5
  49. package/behaviors/registry/agent/organisms/std-agent-assistant.orb +7 -0
  50. package/behaviors/registry/factory-signatures.json +1 -1
  51. package/behaviors/registry/infra/atoms/std-page-treat.orb +327 -2
  52. package/behaviors/registry/ui/avl/atoms/ui-avl-state-machine.orb +134 -0
  53. package/behaviors/registry/ui/avl/atoms/ui-avl-transition-explainer.orb +678 -0
  54. package/behaviors/registry/ui/avl/atoms/ui-module-card.orb +122 -0
  55. package/behaviors/registry/ui/core/atoms/std-app-layout.orb +73 -4
  56. package/behaviors/registry/ui/core/atoms/std-hero.orb +174 -97
  57. package/behaviors/registry/ui/core/molecules/ui-markdown-content.orb +204 -1
  58. package/behaviors/registry/ui/core/molecules/ui-upload-drop-zone.orb +12 -0
  59. package/behaviors/registry/ui/core/organisms/ui-segment-renderer.orb +683 -30
  60. package/behaviors/registry/ui/learning/atoms/std-learn-arrangement.orb +748 -21
  61. package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +1429 -41
  62. package/behaviors/registry/ui/learning/atoms/std-learn-coupled-sim.orb +1031 -185
  63. package/behaviors/registry/ui/learning/atoms/std-learn-derivation.orb +807 -132
  64. package/behaviors/registry/ui/learning/atoms/std-learn-diagram.orb +553 -6
  65. package/behaviors/registry/ui/learning/atoms/std-learn-doser.orb +987 -21
  66. package/behaviors/registry/ui/learning/atoms/std-learn-estimate.orb +582 -16
  67. package/behaviors/registry/ui/learning/atoms/std-learn-goal-seek.orb +954 -13
  68. package/behaviors/registry/ui/learning/atoms/std-learn-grid-sim.orb +828 -88
  69. package/behaviors/registry/ui/learning/atoms/std-learn-hotspot.orb +730 -28
  70. package/behaviors/registry/ui/learning/atoms/std-learn-live-sim.orb +479 -9
  71. package/behaviors/registry/ui/learning/atoms/std-learn-predict.orb +797 -46
  72. package/behaviors/registry/ui/learning/atoms/std-learn-sandbox.orb +757 -58
  73. package/behaviors/registry/ui/learning/atoms/std-learn-scatter-fit.orb +1176 -164
  74. package/behaviors/registry/ui/learning/atoms/std-learn-scenario.orb +599 -12
  75. package/behaviors/registry/ui/learning/atoms/std-learn-speed-drill.orb +832 -150
  76. package/behaviors/registry/ui/learning/atoms/std-learn-stepper.orb +1561 -127
  77. package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +655 -6
  78. package/behaviors/registry/ui/learning/atoms/std-learn-trace-player.orb +634 -43
  79. package/behaviors/registry/ui/learning/atoms/std-learn-transport.orb +85 -31
  80. package/behaviors/registry/ui/learning/atoms/std-learn-tree.orb +425 -9
  81. package/canonical-operators.json +45 -2
  82. package/dist/behaviors/exports-reader.js +16667 -7868
  83. package/dist/behaviors/functions/index.d.ts +711 -32
  84. package/dist/behaviors/functions/index.js +16651 -7869
  85. package/dist/behaviors/index.d.ts +1 -1
  86. package/dist/behaviors/index.js +16668 -7869
  87. package/dist/behaviors/query.js +16667 -7868
  88. package/dist/behaviors-embeddings.hash.json +3 -3
  89. package/dist/behaviors-embeddings.json +77 -53
  90. package/dist/behaviors-registry.json +4470 -513
  91. package/dist/index.d.ts +3 -3
  92. package/dist/index.js +16101 -7275
  93. package/dist/knob-embeddings.hash.json +3 -3
  94. package/dist/knob-embeddings.json +1309 -685
  95. package/dist/modules/anim.d.ts +1 -1
  96. package/dist/modules/array.d.ts +1 -1
  97. package/dist/modules/async.d.ts +1 -1
  98. package/dist/modules/behavior.d.ts +1 -1
  99. package/dist/modules/browser.d.ts +1 -1
  100. package/dist/modules/composition.d.ts +1 -1
  101. package/dist/modules/contract.d.ts +1 -1
  102. package/dist/modules/core.d.ts +1 -1
  103. package/dist/modules/core.js +29 -2
  104. package/dist/modules/data.d.ts +1 -1
  105. package/dist/modules/ease.d.ts +1 -1
  106. package/dist/modules/format.d.ts +1 -1
  107. package/dist/modules/geo.d.ts +1 -1
  108. package/dist/modules/graph.d.ts +1 -1
  109. package/dist/modules/grid.d.ts +1 -1
  110. package/dist/modules/i18n.d.ts +1 -1
  111. package/dist/modules/index.d.ts +1 -1
  112. package/dist/modules/index.js +29 -2
  113. package/dist/modules/integration.d.ts +1 -1
  114. package/dist/modules/json.d.ts +1 -1
  115. package/dist/modules/llm.d.ts +1 -1
  116. package/dist/modules/math.d.ts +1 -1
  117. package/dist/modules/memory.d.ts +1 -1
  118. package/dist/modules/nn.d.ts +1 -1
  119. package/dist/modules/noise.d.ts +1 -1
  120. package/dist/modules/object.d.ts +1 -1
  121. package/dist/modules/os.d.ts +1 -1
  122. package/dist/modules/path.d.ts +1 -1
  123. package/dist/modules/prob.d.ts +1 -1
  124. package/dist/modules/session.d.ts +1 -1
  125. package/dist/modules/str.d.ts +1 -1
  126. package/dist/modules/tensor.d.ts +1 -1
  127. package/dist/modules/time.d.ts +1 -1
  128. package/dist/modules/trace.d.ts +1 -1
  129. package/dist/modules/train.d.ts +1 -1
  130. package/dist/modules/validate.d.ts +1 -1
  131. package/dist/modules/vector.d.ts +1 -1
  132. package/dist/modules/workspace.d.ts +1 -1
  133. package/dist/registry/factory-signatures.json +1 -1
  134. package/dist/registry.d.ts +2 -2
  135. package/dist/registry.js +29 -2
  136. package/dist/{types-Cdb321uM.d.ts → types-Y-yYWEOX.d.ts} +1 -1
  137. package/i18n/ar.json +3 -1
  138. package/i18n/en.json +2 -0
  139. package/i18n/sl.json +3 -1
  140. package/package.json +3 -3
@@ -74,7 +74,7 @@ orbital LearnTour3DOrbital {
74
74
  initial: idle
75
75
 
76
76
  state idle {
77
- INIT -> idle when (== @config.matchMode "")
77
+ INIT -> idle when (and (== @config.matchMode "") (== @config.rosterSource "config"))
78
78
  (set @entity.active true)
79
79
  (set @entity.title @config.title)
80
80
  (set @entity.stations @config.stations)
@@ -123,7 +123,7 @@ orbital LearnTour3DOrbital {
123
123
  (set @entity.active false)
124
124
  (render-ui main null)
125
125
 
126
- MODE_SELECTED -> idle when (== ?mode @config.matchMode)
126
+ MODE_SELECTED -> idle when (and (== ?mode @config.matchMode) (== @config.rosterSource "config"))
127
127
  (set @entity.active true)
128
128
  (set @entity.title @config.title)
129
129
  (set @entity.stations @config.stations)
@@ -168,6 +168,55 @@ orbital LearnTour3DOrbital {
168
168
  })
169
169
  (emit TOUR_3D_UPDATED { id: @entity.selectedId, name: (object/get (array/nth @entity.stations 0) name), caption: (object/get (array/nth @entity.stations 0) caption), visited: (array/len @entity.visitedIds), total: (array/len @entity.stations), tourComplete: false })
170
170
 
171
+ ROSTER_LOADED -> idle when (and (== @config.rosterSource "event") (and (== ?mode @config.matchMode) (> (array/len ?stations) 0)))
172
+ (set @entity.active true)
173
+ (set @entity.title @config.title)
174
+ (set @entity.stations ?stations)
175
+ (set @entity.backdrop ?backdrop)
176
+ (set @entity.edges ?edges)
177
+ (set @entity.selectedIndex 0)
178
+ (set @entity.selectedId (object/get (array/nth @entity.stations 0) id))
179
+ (set @entity.visitedIds [(object/get (array/nth @entity.stations 0) id)])
180
+ (set @entity.tourComplete false)
181
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations 0) name) " — " (object/get (array/nth @entity.stations 0) caption)))
182
+ (render-ui main {
183
+ type: box
184
+ padding: md
185
+ children: [
186
+ { type: stack, direction: vertical, gap: md, children: [
187
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
188
+ { type: stack, direction: vertical, gap: xs, children: [
189
+ { type: typography, variant: h4, content: @entity.title },
190
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
191
+ ] },
192
+ { type: badge, label: (if @entity.tourComplete "Complete" (str/concat "Station " (str/concat "" (+ @entity.selectedIndex 1)) " of " (str/concat "" (array/len @entity.stations)))), variant: (if @entity.tourComplete "success" "neutral") }
193
+ ] },
194
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
195
+ { type: button, action: TOUR, label: @config.tourLabel, icon: "chevron-right", variant: primary },
196
+ { type: button, action: RESET, label: @config.resetLabel, icon: "rotate-ccw", variant: ghost },
197
+ { type: typography, variant: caption, color: muted, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
198
+ ] },
199
+ { type: biology-canvas, keyMap: { Space: TOUR, ArrowRight: TOUR, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
200
+ mode: "3d",
201
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
202
+ target: { x: (object/get (array/nth @entity.stations @entity.selectedIndex) x), y: (object/get (array/nth @entity.stations @entity.selectedIndex) y), z: (object/get (array/nth @entity.stations @entity.selectedIndex) z) } },
203
+ lighting: @config.lighting,
204
+ post: @config.post,
205
+ nodes: (array/map (array/concat @entity.backdrop @entity.stations) (fn n { id: (object/get @n id), x: (object/get @n x), y: (object/get @n y), z: (object/get @n z), radius: (if (== (object/get @n id) @entity.selectedId) (* (object/get @n radius) 1.4) (object/get @n radius)), color: (object/get @n color), label: (object/get @n name), shape: (object/get @n shape), opacity: (object/get @n opacity) })),
206
+ edges: @entity.edges,
207
+ interactive: true,
208
+ onShapeClick: "SELECT_STATION"
209
+ },
210
+ { type: typography, variant: body, content: @entity.narration }
211
+ ] }
212
+ ]
213
+ })
214
+ (emit TOUR_3D_UPDATED { id: @entity.selectedId, name: (object/get (array/nth @entity.stations 0) name), caption: (object/get (array/nth @entity.stations 0) caption), visited: (array/len @entity.visitedIds), total: (array/len @entity.stations), tourComplete: false })
215
+
216
+ ROSTER_LOADED -> idle when (and (== @config.rosterSource "event") (not (== ?mode @config.matchMode)))
217
+ (set @entity.active false)
218
+ (render-ui main null)
219
+
171
220
  TOUR -> idle when (== @entity.active true)
172
221
  (if (and (not @entity.tourComplete) (== (if (array/includes @entity.visitedIds (object/get (array/nth @entity.stations (math/mod (+ @entity.selectedIndex 1) (array/len @entity.stations))) id)) (array/len @entity.visitedIds) (+ (array/len @entity.visitedIds) 1)) (array/len @entity.stations))) (emit LEARN_ROUND_COMPLETE { score: (array/len @entity.stations), total: (array/len @entity.stations) }) null)
173
222
  (set @entity.selectedIndex (math/mod (+ @entity.selectedIndex 1) (array/len @entity.stations)))
@@ -348,6 +397,14 @@ orbital LearnTour3DOrbital {
348
397
  }
349
398
  @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
350
399
  @tier "domain"
400
+ ROSTER_LOADED {
401
+ mode : string!
402
+ stations : [Learn3DStation]!
403
+ edges : [Learn3DEdge]!
404
+ backdrop : [Learn3DStation]!
405
+ }
406
+ @description "Runtime content feed, honoured only when rosterSource is event — mode names the instrument it is for (the matchMode it carries, blank when standalone) and stations, edges and backdrop are the scene roster; a matching instrument re-initialises from it exactly as INIT does from config, any other instrument hides."
407
+ @tier "domain"
351
408
  SELECT_STATION {
352
409
  payload : Learn3DClick
353
410
  }
@@ -388,6 +445,10 @@ orbital LearnTour3DOrbital {
388
445
  @label "Match Mode"
389
446
  @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
390
447
  @tier "presentation"
448
+ rosterSource : "config" | "event" = "config"
449
+ @label "Roster Source"
450
+ @description "Where the stations, edges and backdrop come from: config reads those knobs at INIT; event waits for ROSTER_LOADED and takes them from its payload."
451
+ @tier "domain"
391
452
  title : string = "3D Guided Tour"
392
453
  @label "Title"
393
454
  @description "Title shown above the canvas."
@@ -17,6 +17,7 @@ orbital LearnTracePlayerOrbital {
17
17
  id : string!
18
18
  active : boolean = false
19
19
  title : string
20
+ frames : [TraceFrame]
20
21
  frameIndex : number = 0
21
22
  running : boolean = false
22
23
  speed : number = 1
@@ -31,16 +32,17 @@ orbital LearnTracePlayerOrbital {
31
32
  initial: idle
32
33
 
33
34
  state idle {
34
- INIT -> idle when (== @config.matchMode "")
35
+ INIT -> idle when (and (== @config.matchMode "") (== @config.rosterSource "config"))
35
36
  (set @entity.active true)
36
37
  (set @entity.title @config.title)
38
+ (set @entity.frames @config.frames)
37
39
  (set @entity.frameIndex 0)
38
40
  (set @entity.running false)
39
41
  (set @entity.statusNote "Paused")
40
- (set @entity.frameLabel (object/get (array/nth @config.frames @entity.frameIndex) label))
41
- (set @entity.frameCaption (object/get (array/nth @config.frames @entity.frameIndex) caption))
42
- (set @entity.bars (array/map (object/get (array/nth @config.frames @entity.frameIndex) values)
43
- { value: @item, color: (if (array/includes (object/get (array/nth @config.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
42
+ (set @entity.frameLabel (object/get (array/nth @entity.frames @entity.frameIndex) label))
43
+ (set @entity.frameCaption (object/get (array/nth @entity.frames @entity.frameIndex) caption))
44
+ (set @entity.bars (array/map (object/get (array/nth @entity.frames @entity.frameIndex) values)
45
+ { value: @item, color: (if (array/includes (object/get (array/nth @entity.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
44
46
  (render-ui main {
45
47
  type: box
46
48
  padding: md
@@ -62,9 +64,9 @@ orbital LearnTracePlayerOrbital {
62
64
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
63
65
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
64
66
  ] },
65
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
67
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
66
68
  ] },
67
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
69
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
68
70
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
69
71
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
70
72
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -78,16 +80,17 @@ orbital LearnTracePlayerOrbital {
78
80
  (set @entity.running false)
79
81
  (render-ui main null)
80
82
 
81
- MODE_SELECTED -> idle when (== ?mode @config.matchMode)
83
+ MODE_SELECTED -> idle when (and (== ?mode @config.matchMode) (== @config.rosterSource "config"))
82
84
  (set @entity.active true)
83
85
  (set @entity.title @config.title)
86
+ (set @entity.frames @config.frames)
84
87
  (set @entity.frameIndex 0)
85
88
  (set @entity.running false)
86
89
  (set @entity.statusNote "Paused")
87
- (set @entity.frameLabel (object/get (array/nth @config.frames @entity.frameIndex) label))
88
- (set @entity.frameCaption (object/get (array/nth @config.frames @entity.frameIndex) caption))
89
- (set @entity.bars (array/map (object/get (array/nth @config.frames @entity.frameIndex) values)
90
- { value: @item, color: (if (array/includes (object/get (array/nth @config.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
90
+ (set @entity.frameLabel (object/get (array/nth @entity.frames @entity.frameIndex) label))
91
+ (set @entity.frameCaption (object/get (array/nth @entity.frames @entity.frameIndex) caption))
92
+ (set @entity.bars (array/map (object/get (array/nth @entity.frames @entity.frameIndex) values)
93
+ { value: @item, color: (if (array/includes (object/get (array/nth @entity.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
91
94
  (render-ui main {
92
95
  type: box
93
96
  padding: md
@@ -109,9 +112,9 @@ orbital LearnTracePlayerOrbital {
109
112
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
110
113
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
111
114
  ] },
112
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
115
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
113
116
  ] },
114
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
117
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
115
118
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
116
119
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
117
120
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -120,6 +123,54 @@ orbital LearnTracePlayerOrbital {
120
123
  ]
121
124
  })
122
125
 
126
+ ROSTER_LOADED -> idle when (and (== @config.rosterSource "event") (and (== ?mode @config.matchMode) (> (array/len ?frames) 0)))
127
+ (set @entity.frames ?frames)
128
+ (set @entity.active true)
129
+ (set @entity.title @config.title)
130
+ (set @entity.frameIndex 0)
131
+ (set @entity.running false)
132
+ (set @entity.statusNote "Paused")
133
+ (set @entity.frameLabel (object/get (array/nth ?frames @entity.frameIndex) label))
134
+ (set @entity.frameCaption (object/get (array/nth ?frames @entity.frameIndex) caption))
135
+ (set @entity.bars (array/map (object/get (array/nth ?frames @entity.frameIndex) values)
136
+ { value: @item, color: (if (array/includes (object/get (array/nth ?frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
137
+ (render-ui main {
138
+ type: box
139
+ padding: md
140
+ children: [
141
+ { type: stack, direction: vertical, gap: md, children: [
142
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
143
+ { type: stack, direction: vertical, gap: xs, children: [
144
+ { type: typography, variant: h4, content: @entity.title },
145
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
146
+ ] },
147
+ { type: badge, label: @entity.statusNote, variant: (if (== @entity.statusNote "Finished") "success" (if @entity.running "primary" "neutral")) }
148
+ ] },
149
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
150
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (== @entity.statusNote "Finished") }),
151
+ { type: button, action: RESTART, label: "Restart", icon: "rotate-ccw", variant: ghost },
152
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
153
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
154
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
155
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
156
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
157
+ ] },
158
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len ?frames)) }
159
+ ] },
160
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len ?frames), showPercentage: false },
161
+ { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
162
+ { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
163
+ { type: typography, variant: body, content: @entity.frameCaption }
164
+ ] }
165
+ ] }
166
+ ]
167
+ })
168
+
169
+ ROSTER_LOADED -> idle when (and (== @config.rosterSource "event") (not (== ?mode @config.matchMode)))
170
+ (set @entity.active false)
171
+ (set @entity.running false)
172
+ (render-ui main null)
173
+
123
174
  TOGGLE_RUN -> idle when (== @entity.active true)
124
175
  (set @entity.running (not @entity.running))
125
176
  (set @entity.statusNote (if @entity.running "Playing" "Paused"))
@@ -144,9 +195,9 @@ orbital LearnTracePlayerOrbital {
144
195
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
145
196
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
146
197
  ] },
147
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
198
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
148
199
  ] },
149
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
200
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
150
201
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
151
202
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
152
203
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -178,9 +229,9 @@ orbital LearnTracePlayerOrbital {
178
229
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
179
230
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
180
231
  ] },
181
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
232
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
182
233
  ] },
183
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
234
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
184
235
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
185
236
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
186
237
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -213,9 +264,9 @@ orbital LearnTracePlayerOrbital {
213
264
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
214
265
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
215
266
  ] },
216
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
267
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
217
268
  ] },
218
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
269
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
219
270
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
220
271
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
221
272
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -249,9 +300,9 @@ orbital LearnTracePlayerOrbital {
249
300
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
250
301
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
251
302
  ] },
252
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
303
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
253
304
  ] },
254
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
305
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
255
306
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
256
307
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
257
308
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -265,10 +316,10 @@ orbital LearnTracePlayerOrbital {
265
316
  (set @entity.frameIndex 0)
266
317
  (set @entity.running false)
267
318
  (set @entity.statusNote "Paused")
268
- (set @entity.frameLabel (object/get (array/nth @config.frames @entity.frameIndex) label))
269
- (set @entity.frameCaption (object/get (array/nth @config.frames @entity.frameIndex) caption))
270
- (set @entity.bars (array/map (object/get (array/nth @config.frames @entity.frameIndex) values)
271
- { value: @item, color: (if (array/includes (object/get (array/nth @config.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
319
+ (set @entity.frameLabel (object/get (array/nth @entity.frames @entity.frameIndex) label))
320
+ (set @entity.frameCaption (object/get (array/nth @entity.frames @entity.frameIndex) caption))
321
+ (set @entity.bars (array/map (object/get (array/nth @entity.frames @entity.frameIndex) values)
322
+ { value: @item, color: (if (array/includes (object/get (array/nth @entity.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
272
323
  (render-ui main {
273
324
  type: box
274
325
  padding: md
@@ -290,9 +341,9 @@ orbital LearnTracePlayerOrbital {
290
341
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
291
342
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
292
343
  ] },
293
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
344
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
294
345
  ] },
295
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
346
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
296
347
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
297
348
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
298
349
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -310,13 +361,13 @@ orbital LearnTracePlayerOrbital {
310
361
  (when (>= (* @entity.elapsedMs @entity.speed) @config.tickMs)
311
362
  (do
312
363
  (set @entity.elapsedMs 0)
313
- (set @entity.running (if (and (>= @entity.frameIndex (- (array/len @config.frames) 1)) (not @config.loop)) false @entity.running))
314
- (set @entity.frameIndex (if (>= @entity.frameIndex (- (array/len @config.frames) 1)) (if @config.loop 0 @entity.frameIndex) (+ @entity.frameIndex 1)))
315
- (set @entity.frameLabel (object/get (array/nth @config.frames @entity.frameIndex) label))
316
- (set @entity.frameCaption (object/get (array/nth @config.frames @entity.frameIndex) caption))
317
- (set @entity.bars (array/map (object/get (array/nth @config.frames @entity.frameIndex) values)
318
- { value: @item, color: (if (array/includes (object/get (array/nth @config.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
319
- (set @entity.statusNote (if @entity.running "Playing" (if (>= (+ @entity.frameIndex 1) (array/len @config.frames)) "Finished" "Paused")))
364
+ (set @entity.running (if (and (>= @entity.frameIndex (- (array/len @entity.frames) 1)) (not @config.loop)) false @entity.running))
365
+ (set @entity.frameIndex (if (>= @entity.frameIndex (- (array/len @entity.frames) 1)) (if @config.loop 0 @entity.frameIndex) (+ @entity.frameIndex 1)))
366
+ (set @entity.frameLabel (object/get (array/nth @entity.frames @entity.frameIndex) label))
367
+ (set @entity.frameCaption (object/get (array/nth @entity.frames @entity.frameIndex) caption))
368
+ (set @entity.bars (array/map (object/get (array/nth @entity.frames @entity.frameIndex) values)
369
+ { value: @item, color: (if (array/includes (object/get (array/nth @entity.frames @entity.frameIndex) highlights) @index) @config.highlightColor @config.baseColor), label: (str/concat "" @item) }))
370
+ (set @entity.statusNote (if @entity.running "Playing" (if (>= (+ @entity.frameIndex 1) (array/len @entity.frames)) "Finished" "Paused")))
320
371
  (render-ui main {
321
372
  type: box
322
373
  padding: md
@@ -338,9 +389,9 @@ orbital LearnTracePlayerOrbital {
338
389
  { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
339
390
  { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
340
391
  ] },
341
- { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @config.frames)) }
392
+ { type: typography, variant: caption, color: muted, content: (str/concat "Frame " (+ @entity.frameIndex 1) " of " (array/len @entity.frames)) }
342
393
  ] },
343
- { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @config.frames), showPercentage: false },
394
+ { type: progress-bar, value: (+ @entity.frameIndex 1), max: (array/len @entity.frames), showPercentage: false },
344
395
  { type: algorithm-canvas, keyMap: { Space: TOGGLE_RUN, KeyR: RESTART }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, bars: @entity.bars },
345
396
  { type: card, variant: bordered, padding: md, title: @entity.frameLabel, children: [
346
397
  { type: typography, variant: body, content: @entity.frameCaption }
@@ -349,7 +400,7 @@ orbital LearnTracePlayerOrbital {
349
400
  ]
350
401
  })
351
402
  (emit FRAME_ADVANCED { frameIndex: @entity.frameIndex, frameLabel: @entity.frameLabel })
352
- (if (and (not @config.loop) (not @entity.running) (>= (+ @entity.frameIndex 1) (array/len @config.frames))) (emit TRACE_DONE { totalFrames: (array/len @config.frames) }) null)
403
+ (if (and (not @config.loop) (not @entity.running) (>= (+ @entity.frameIndex 1) (array/len @entity.frames))) (emit TRACE_DONE { totalFrames: (array/len @entity.frames) }) null)
353
404
  ))
354
405
  }
355
406
 
@@ -359,6 +410,12 @@ orbital LearnTracePlayerOrbital {
359
410
  }
360
411
  @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
361
412
  @tier "domain"
413
+ ROSTER_LOADED {
414
+ mode : string!
415
+ frames : [TraceFrame]!
416
+ }
417
+ @description "Runtime content feed, honoured only when rosterSource is event — mode names the instrument it is for (the matchMode it carries, blank when standalone) and frames is the frame roster; a matching instrument re-initialises from it exactly as INIT does from config, any other instrument hides."
418
+ @tier "domain"
362
419
  TOGGLE_RUN {
363
420
  }
364
421
  @description "Learner pressed the Play/Pause toggle — starts or freezes the frame cursor."
@@ -403,6 +460,10 @@ orbital LearnTracePlayerOrbital {
403
460
  @label "Match Mode"
404
461
  @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
405
462
  @tier "presentation"
463
+ rosterSource : "config" | "event" = "config"
464
+ @label "Roster Source"
465
+ @description "Where the frame roster comes from: config reads the frames knob at INIT; event waits for ROSTER_LOADED and takes the roster from its payload."
466
+ @tier "domain"
406
467
  title : string = "Trace Player"
407
468
  @label "Title"
408
469
  @description "The instrument's name, the header's one hero."
@@ -29,7 +29,7 @@ orbital LearnTransportOrbital {
29
29
  { type: stack, direction: vertical, gap: md, children: [
30
30
  { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
31
31
  { type: stack, direction: vertical, gap: xs, children: [
32
- { type: typography, variant: h4, content: @entity.title },
32
+ (if (== @entity.title "") null { type: typography, variant: h4, content: @entity.title }),
33
33
  (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
34
34
  ] },
35
35
  { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
@@ -64,7 +64,7 @@ orbital LearnTransportOrbital {
64
64
  { type: stack, direction: vertical, gap: md, children: [
65
65
  { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
66
66
  { type: stack, direction: vertical, gap: xs, children: [
67
- { type: typography, variant: h4, content: @entity.title },
67
+ (if (== @entity.title "") null { type: typography, variant: h4, content: @entity.title }),
68
68
  (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
69
69
  ] },
70
70
  { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
@@ -100,7 +100,7 @@ orbital LearnTransportOrbital {
100
100
  { type: stack, direction: vertical, gap: md, children: [
101
101
  { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
102
102
  { type: stack, direction: vertical, gap: xs, children: [
103
- { type: typography, variant: h4, content: @entity.title },
103
+ (if (== @entity.title "") null { type: typography, variant: h4, content: @entity.title }),
104
104
  (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
105
105
  ] },
106
106
  { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
@@ -131,7 +131,7 @@ orbital LearnTransportOrbital {
131
131
  { type: stack, direction: vertical, gap: md, children: [
132
132
  { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
133
133
  { type: stack, direction: vertical, gap: xs, children: [
134
- { type: typography, variant: h4, content: @entity.title },
134
+ (if (== @entity.title "") null { type: typography, variant: h4, content: @entity.title }),
135
135
  (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
136
136
  ] },
137
137
  { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
@@ -164,7 +164,7 @@ orbital LearnTransportOrbital {
164
164
  { type: stack, direction: vertical, gap: md, children: [
165
165
  { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
166
166
  { type: stack, direction: vertical, gap: xs, children: [
167
- { type: typography, variant: h4, content: @entity.title },
167
+ (if (== @entity.title "") null { type: typography, variant: h4, content: @entity.title }),
168
168
  (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
169
169
  ] },
170
170
  { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
@@ -195,7 +195,7 @@ orbital LearnTransportOrbital {
195
195
  { type: stack, direction: vertical, gap: md, children: [
196
196
  { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
197
197
  { type: stack, direction: vertical, gap: xs, children: [
198
- { type: typography, variant: h4, content: @entity.title },
198
+ (if (== @entity.title "") null { type: typography, variant: h4, content: @entity.title }),
199
199
  (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
200
200
  ] },
201
201
  { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
@@ -270,7 +270,7 @@ orbital LearnTransportOrbital {
270
270
  @tier "presentation"
271
271
  title : string = "Learning Instrument"
272
272
  @label "Title"
273
- @description "The instrument's name, the header's one hero."
273
+ @description "The instrument's name, the header's one hero. Empty shows no title."
274
274
  @tier "presentation"
275
275
  summary : string = ""
276
276
  @label "Concept"
@@ -25,6 +25,7 @@ orbital LearnTreeOrbital {
25
25
  id : string!
26
26
  active : boolean = false
27
27
  title : string
28
+ values : [number]
28
29
  nodes : [LearnTreeNode]
29
30
  edges : [LearnTreeEdge]
30
31
  entries : [LearnTreeEntry]
@@ -51,9 +52,10 @@ orbital LearnTreeOrbital {
51
52
  initial: idle
52
53
 
53
54
  state idle {
54
- INIT -> idle when (== @config.matchMode "")
55
+ INIT -> idle when (and (== @config.matchMode "") (== @config.rosterSource "config"))
55
56
  (set @entity.active true)
56
57
  (set @entity.title @config.title)
58
+ (set @entity.values @config.values)
57
59
  (set @entity.nodes [])
58
60
  (set @entity.edges [])
59
61
  (set @entity.entries [])
@@ -101,9 +103,10 @@ orbital LearnTreeOrbital {
101
103
  (set @entity.running false)
102
104
  (render-ui main null)
103
105
 
104
- MODE_SELECTED -> idle when (== ?mode @config.matchMode)
106
+ MODE_SELECTED -> idle when (and (== ?mode @config.matchMode) (== @config.rosterSource "config"))
105
107
  (set @entity.active true)
106
108
  (set @entity.title @config.title)
109
+ (set @entity.values @config.values)
107
110
  (set @entity.nodes [])
108
111
  (set @entity.edges [])
109
112
  (set @entity.entries [])
@@ -146,14 +149,66 @@ orbital LearnTreeOrbital {
146
149
  })
147
150
  (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
148
151
 
149
- INSERT_VALUE -> idle when (and (== @entity.active true) (or @entity.descending (< @entity.cursor (array/len @config.values))))
152
+ ROSTER_LOADED -> idle when (and (== @config.rosterSource "event") (and (== ?mode @config.matchMode) (> (array/len ?values) 0)))
153
+ (set @entity.active true)
154
+ (set @entity.title @config.title)
155
+ (set @entity.values ?values)
156
+ (set @entity.nodes [])
157
+ (set @entity.edges [])
158
+ (set @entity.entries [])
159
+ (set @entity.cursor 0)
160
+ (set @entity.descending false)
161
+ (set @entity.trail [])
162
+ (set @entity.depth 0)
163
+ (set @entity.maxDepth 0)
164
+ (set @entity.count 0)
165
+ (set @entity.traversing false)
166
+ (set @entity.traversalOrder [])
167
+ (set @entity.sweptIds [])
168
+ (set @entity.done false)
169
+ (set @entity.running @config.autoRun)
170
+ (set @entity.narration "Press Insert to add the next value; keep pressing to watch it descend.")
171
+ (render-ui main {
172
+ type: box
173
+ padding: md
174
+ children: [
175
+ { type: stack, direction: vertical, gap: md, children: [
176
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
177
+ { type: stack, direction: vertical, gap: xs, children: [
178
+ { type: typography, variant: h4, content: @entity.title },
179
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
180
+ ] },
181
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
182
+ ] },
183
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
184
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
185
+ { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
186
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
187
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
188
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
189
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
190
+ ] },
191
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
192
+ { type: typography, variant: body, content: @entity.narration }
193
+ ] }
194
+ ]
195
+ })
196
+ (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
197
+
198
+ ROSTER_LOADED -> idle when (and (== @config.rosterSource "event") (not (== ?mode @config.matchMode)))
199
+ (set @entity.active false)
200
+ (set @entity.running false)
201
+ (render-ui main null)
202
+
203
+
204
+ INSERT_VALUE -> idle when (and (== @entity.active true) (or @entity.descending (< @entity.cursor (array/len @entity.values))))
150
205
  (set @entity.traversing false)
151
206
  (set @entity.traversalOrder [])
152
207
  (set @entity.sweptIds [])
153
208
  (set @entity.done false)
154
209
  (if (not @entity.descending)
155
210
  (let (
156
- (v (array/nth @config.values @entity.cursor))
211
+ (v (array/nth @entity.values @entity.cursor))
157
212
  )
158
213
  (if (== @entity.count 0)
159
214
  (do
@@ -491,6 +546,12 @@ orbital LearnTreeOrbital {
491
546
  }
492
547
  @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
493
548
  @tier "domain"
549
+ ROSTER_LOADED {
550
+ mode : string!
551
+ values : [number]!
552
+ }
553
+ @description "Runtime content feed, honoured only when rosterSource is event — mode names the instrument it is for (the matchMode it carries, blank when standalone) and values is the insertion sequence; a matching instrument re-initialises from it exactly as INIT does from config, any other instrument hides."
554
+ @tier "domain"
494
555
  INSERT_VALUE {
495
556
  }
496
557
  @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."
@@ -535,6 +596,10 @@ orbital LearnTreeOrbital {
535
596
  @label "Match Mode"
536
597
  @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
537
598
  @tier "presentation"
599
+ rosterSource : "config" | "event" = "config"
600
+ @label "Roster Source"
601
+ @description "Where the insertion values come from: config reads the values knob at INIT; event waits for ROSTER_LOADED and takes them from its payload."
602
+ @tier "domain"
538
603
  autoRun : boolean = false
539
604
  @label "Auto Run"
540
605
  @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."