@almadar/std 16.173.0 → 16.175.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 (220) hide show
  1. package/behaviors/lolo/infra/atoms/std-cross-reference.lolo +2 -4
  2. package/behaviors/lolo/ui/core/atoms/std-board.lolo +5 -9
  3. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +8 -1
  4. package/behaviors/lolo/ui/core/atoms/std-esign-flow.lolo +2 -2
  5. package/behaviors/lolo/ui/core/atoms/std-event-log.lolo +3 -7
  6. package/behaviors/lolo/ui/core/atoms/std-form-advanced.lolo +1 -1
  7. package/behaviors/lolo/ui/core/atoms/std-image-upload-multi.lolo +1 -1
  8. package/behaviors/lolo/ui/core/atoms/std-list.lolo +3 -3
  9. package/behaviors/lolo/ui/core/atoms/std-modal.lolo +2 -2
  10. package/behaviors/lolo/ui/core/atoms/std-multi-party-flow.lolo +2 -4
  11. package/behaviors/lolo/ui/core/atoms/std-rating-review.lolo +5 -13
  12. package/behaviors/lolo/ui/core/atoms/std-recurrence.lolo +3 -7
  13. package/behaviors/lolo/ui/core/atoms/std-saved-search.lolo +1 -1
  14. package/behaviors/lolo/ui/core/atoms/std-signature-capture.lolo +2 -2
  15. package/behaviors/lolo/ui/core/atoms/std-step-flow.lolo +2 -4
  16. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +1 -1
  17. package/behaviors/lolo/ui/core/atoms/std-thread.lolo +2 -2
  18. package/behaviors/lolo/ui/core/atoms/std-vote.lolo +2 -2
  19. package/behaviors/lolo/ui/core/atoms/std-wizard.lolo +3 -7
  20. package/behaviors/lolo/ui/core/atoms/ui-atlas-image.lolo +1 -1
  21. package/behaviors/lolo/ui/core/atoms/ui-atlas-panel.lolo +1 -1
  22. package/behaviors/lolo/ui/core/atoms/ui-center.lolo +1 -1
  23. package/behaviors/lolo/ui/core/atoms/ui-hstack.lolo +1 -1
  24. package/behaviors/lolo/ui/core/atoms/ui-icon.lolo +1 -1
  25. package/behaviors/lolo/ui/core/atoms/ui-stack.lolo +1 -1
  26. package/behaviors/lolo/ui/core/atoms/ui-typography.lolo +1 -1
  27. package/behaviors/lolo/ui/core/atoms/ui-vstack.lolo +1 -1
  28. package/behaviors/lolo/ui/core/molecules/ui-content-renderer.lolo +1 -1
  29. package/behaviors/lolo/ui/core/molecules/ui-form-layout.lolo +1 -1
  30. package/behaviors/lolo/ui/core/molecules/ui-grid.lolo +1 -1
  31. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +4 -9
  32. package/behaviors/lolo/ui/core/organisms/ui-chat-bar.lolo +1 -1
  33. package/behaviors/lolo/ui/core/organisms/ui-dashboard-grid.lolo +1 -1
  34. package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +1 -1
  35. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +1 -1
  36. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +1 -1
  37. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +1 -1
  38. package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +9 -3
  39. package/behaviors/lolo/ui/core/organisms/ui-form.lolo +9 -3
  40. package/behaviors/lolo/ui/core/organisms/ui-master-detail.lolo +1 -1
  41. package/behaviors/lolo/ui/core/organisms/ui-media-gallery.lolo +1 -1
  42. package/behaviors/lolo/ui/core/organisms/ui-segment-renderer.lolo +7 -1
  43. package/behaviors/lolo/ui/core/organisms/ui-stat-card.lolo +1 -1
  44. package/behaviors/lolo/ui/core/organisms/ui-subagent-trace-panel.lolo +1 -1
  45. package/behaviors/lolo/ui/game/atoms/ui-action-tile.lolo +1 -1
  46. package/behaviors/lolo/ui/game/atoms/ui-draw-shape-layer.lolo +41 -9
  47. package/behaviors/lolo/ui/game/atoms/ui-draw-shape.lolo +40 -8
  48. package/behaviors/lolo/ui/game/atoms/ui-draw-sprite-layer.lolo +13 -3
  49. package/behaviors/lolo/ui/game/atoms/ui-draw-sprite.lolo +12 -2
  50. package/behaviors/lolo/ui/game/atoms/ui-draw-text-layer.lolo +3 -3
  51. package/behaviors/lolo/ui/game/atoms/ui-draw-text.lolo +2 -2
  52. package/behaviors/lolo/ui/game/organisms/std-tactics-board-3d.lolo +32 -2
  53. package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +133 -23
  54. package/behaviors/lolo/ui/learning/atoms/std-learn-compare.lolo +397 -0
  55. package/behaviors/lolo/ui/learning/atoms/std-learn-coupled-sim.lolo +537 -0
  56. package/behaviors/lolo/ui/learning/atoms/std-learn-derivation.lolo +428 -0
  57. package/behaviors/lolo/ui/learning/atoms/std-learn-doser.lolo +520 -0
  58. package/behaviors/lolo/ui/learning/atoms/std-learn-estimate.lolo +419 -0
  59. package/behaviors/lolo/ui/learning/atoms/std-learn-goal-seek.lolo +591 -0
  60. package/behaviors/lolo/ui/learning/atoms/std-learn-grid-sim.lolo +425 -0
  61. package/behaviors/lolo/ui/learning/atoms/std-learn-hotspot.lolo +398 -0
  62. package/behaviors/lolo/ui/learning/atoms/std-learn-measure.lolo +325 -0
  63. package/behaviors/lolo/ui/learning/atoms/std-learn-perturb.lolo +494 -0
  64. package/behaviors/lolo/ui/learning/atoms/std-learn-predict.lolo +408 -0
  65. package/behaviors/lolo/ui/learning/atoms/std-learn-scatter-fit.lolo +805 -0
  66. package/behaviors/lolo/ui/learning/atoms/std-learn-scenario.lolo +307 -0
  67. package/behaviors/lolo/ui/learning/atoms/std-learn-sim-3d.lolo +318 -0
  68. package/behaviors/lolo/ui/learning/atoms/std-learn-speed-drill.lolo +336 -0
  69. package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +409 -0
  70. package/behaviors/lolo/ui/learning/atoms/std-learn-transport.lolo +186 -0
  71. package/behaviors/lolo/ui/learning/atoms/std-learn-tree.lolo +522 -0
  72. package/behaviors/lolo/ui/learning/atoms/ui-algo-graph-canvas.lolo +192 -0
  73. package/behaviors/lolo/ui/learning/atoms/ui-algorithm-canvas.lolo +111 -2
  74. package/behaviors/lolo/ui/learning/atoms/ui-biology-canvas.lolo +193 -5
  75. package/behaviors/lolo/ui/learning/atoms/ui-chemistry-canvas.lolo +173 -5
  76. package/behaviors/lolo/ui/learning/atoms/ui-learning-canvas.lolo +48 -1
  77. package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +141 -1
  78. package/behaviors/lolo/ui/learning/atoms/ui-physics-canvas.lolo +242 -5
  79. package/behaviors/lolo/ui/marketing/organisms/ui-book-viewer.lolo +1 -1
  80. package/behaviors/lolo/ui/marketing/organisms/ui-case-study-organism.lolo +1 -1
  81. package/behaviors/lolo/ui/marketing/organisms/ui-feature-grid-organism.lolo +1 -1
  82. package/behaviors/lolo/ui/marketing/organisms/ui-hero-organism.lolo +1 -1
  83. package/behaviors/lolo/ui/marketing/organisms/ui-pricing-organism.lolo +1 -1
  84. package/behaviors/lolo/ui/marketing/organisms/ui-showcase-organism.lolo +1 -1
  85. package/behaviors/lolo/ui/marketing/organisms/ui-stats-organism.lolo +1 -1
  86. package/behaviors/lolo/ui/marketing/organisms/ui-step-flow-organism.lolo +1 -1
  87. package/behaviors/lolo/ui/marketing/organisms/ui-team-organism.lolo +1 -1
  88. package/behaviors/registry/agent/atoms/std-agent-session.orb +22 -1
  89. package/behaviors/registry/agent/atoms/std-agent-tool-call.orb +38 -2
  90. package/behaviors/registry/agent/atoms/std-agent-tool-loop.orb +38 -2
  91. package/behaviors/registry/agent/organisms/std-agent-rabit.orb +23 -1
  92. package/behaviors/registry/infra/atoms/std-approval-gate.orb +8 -16
  93. package/behaviors/registry/infra/atoms/std-cache-aside.orb +31 -62
  94. package/behaviors/registry/infra/atoms/std-circuit-breaker.orb +16 -32
  95. package/behaviors/registry/infra/atoms/std-cross-reference.orb +32 -4
  96. package/behaviors/registry/infra/atoms/std-data-erasure.orb +8 -16
  97. package/behaviors/registry/infra/atoms/std-mod-queue.orb +8 -16
  98. package/behaviors/registry/infra/atoms/std-rate-limiter.orb +14 -28
  99. package/behaviors/registry/infra/atoms/std-row-access-control.orb +30 -2
  100. package/behaviors/registry/ui/core/atoms/std-billable-hour.orb +1 -2
  101. package/behaviors/registry/ui/core/atoms/std-board.orb +19 -5
  102. package/behaviors/registry/ui/core/atoms/std-browse.orb +29 -7
  103. package/behaviors/registry/ui/core/atoms/std-dashboard-grid-embedded.orb +1 -2
  104. package/behaviors/registry/ui/core/atoms/std-esign-flow.orb +3 -5
  105. package/behaviors/registry/ui/core/atoms/std-esign-request.orb +3 -6
  106. package/behaviors/registry/ui/core/atoms/std-event-log.orb +17 -3
  107. package/behaviors/registry/ui/core/atoms/std-export.orb +2 -4
  108. package/behaviors/registry/ui/core/atoms/std-form-advanced.orb +1 -1
  109. package/behaviors/registry/ui/core/atoms/std-gallery.orb +9 -18
  110. package/behaviors/registry/ui/core/atoms/std-graphs.orb +15 -1
  111. package/behaviors/registry/ui/core/atoms/std-import.orb +2 -4
  112. package/behaviors/registry/ui/core/atoms/std-list.orb +4 -6
  113. package/behaviors/registry/ui/core/atoms/std-modal.orb +2 -2
  114. package/behaviors/registry/ui/core/atoms/std-multi-party-flow.orb +16 -2
  115. package/behaviors/registry/ui/core/atoms/std-pagination.orb +15 -1
  116. package/behaviors/registry/ui/core/atoms/std-rating-review.orb +19 -5
  117. package/behaviors/registry/ui/core/atoms/std-recurrence.orb +17 -3
  118. package/behaviors/registry/ui/core/atoms/std-rich-editor.orb +15 -1
  119. package/behaviors/registry/ui/core/atoms/std-scatter.orb +15 -1
  120. package/behaviors/registry/ui/core/atoms/std-signature-capture.orb +2 -2
  121. package/behaviors/registry/ui/core/atoms/std-stats.orb +15 -1
  122. package/behaviors/registry/ui/core/atoms/std-step-flow.orb +16 -2
  123. package/behaviors/registry/ui/core/atoms/std-thread.orb +2 -2
  124. package/behaviors/registry/ui/core/atoms/std-vote.orb +2 -2
  125. package/behaviors/registry/ui/core/atoms/std-wizard.orb +17 -3
  126. package/behaviors/registry/ui/core/atoms/ui-atlas-image.orb +4 -1
  127. package/behaviors/registry/ui/core/atoms/ui-atlas-panel.orb +4 -1
  128. package/behaviors/registry/ui/core/atoms/ui-center.orb +4 -1
  129. package/behaviors/registry/ui/core/atoms/ui-hstack.orb +4 -1
  130. package/behaviors/registry/ui/core/atoms/ui-icon.orb +4 -1
  131. package/behaviors/registry/ui/core/atoms/ui-stack.orb +4 -1
  132. package/behaviors/registry/ui/core/atoms/ui-typography.orb +4 -1
  133. package/behaviors/registry/ui/core/atoms/ui-vstack.orb +4 -1
  134. package/behaviors/registry/ui/core/molecules/ui-accordion.orb +3 -3
  135. package/behaviors/registry/ui/core/molecules/ui-content-renderer.orb +4 -1
  136. package/behaviors/registry/ui/core/molecules/ui-form-layout.orb +4 -1
  137. package/behaviors/registry/ui/core/molecules/ui-grid.orb +4 -1
  138. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +4 -39
  139. package/behaviors/registry/ui/core/molecules/ui-tabs.orb +2 -2
  140. package/behaviors/registry/ui/core/molecules/ui-wizard-container.orb +1 -1
  141. package/behaviors/registry/ui/core/organisms/ui-chat-bar.orb +4 -1
  142. package/behaviors/registry/ui/core/organisms/ui-dashboard-grid.orb +5 -2
  143. package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +5 -2
  144. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +4 -1
  145. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +4 -1
  146. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +4 -1
  147. package/behaviors/registry/ui/core/organisms/ui-form-section.orb +34 -2
  148. package/behaviors/registry/ui/core/organisms/ui-form.orb +34 -2
  149. package/behaviors/registry/ui/core/organisms/ui-master-detail.orb +4 -1
  150. package/behaviors/registry/ui/core/organisms/ui-media-gallery.orb +4 -1
  151. package/behaviors/registry/ui/core/organisms/ui-segment-renderer.orb +24 -2
  152. package/behaviors/registry/ui/core/organisms/ui-stat-card.orb +4 -1
  153. package/behaviors/registry/ui/core/organisms/ui-subagent-trace-panel.orb +4 -1
  154. package/behaviors/registry/ui/core/organisms/ui-tabbed-container.orb +1 -1
  155. package/behaviors/registry/ui/core/organisms/ui-ui-slot-renderer.orb +1 -1
  156. package/behaviors/registry/ui/game/atoms/std-fx-particles.orb +35 -1
  157. package/behaviors/registry/ui/game/atoms/ui-action-tile.orb +4 -1
  158. package/behaviors/registry/ui/game/atoms/ui-draw-shape-layer.orb +183 -13
  159. package/behaviors/registry/ui/game/atoms/ui-draw-shape.orb +104 -9
  160. package/behaviors/registry/ui/game/atoms/ui-draw-sprite-layer.orb +56 -2
  161. package/behaviors/registry/ui/game/atoms/ui-draw-sprite.orb +36 -4
  162. package/behaviors/registry/ui/game/atoms/ui-draw-text-layer.orb +21 -2
  163. package/behaviors/registry/ui/game/atoms/ui-draw-text.orb +19 -4
  164. package/behaviors/registry/ui/game/organisms/std-tactics-board-3d.orb +173 -2
  165. package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +1288 -197
  166. package/behaviors/registry/ui/learning/atoms/std-learn-compare.orb +2814 -0
  167. package/behaviors/registry/ui/learning/atoms/std-learn-coupled-sim.orb +3183 -0
  168. package/behaviors/registry/ui/learning/atoms/std-learn-derivation.orb +2512 -0
  169. package/behaviors/registry/ui/learning/atoms/std-learn-doser.orb +3678 -0
  170. package/behaviors/registry/ui/learning/atoms/std-learn-estimate.orb +2394 -0
  171. package/behaviors/registry/ui/learning/atoms/std-learn-goal-seek.orb +3951 -0
  172. package/behaviors/registry/ui/learning/atoms/std-learn-grid-sim.orb +3458 -0
  173. package/behaviors/registry/ui/learning/atoms/std-learn-hotspot.orb +2646 -0
  174. package/behaviors/registry/ui/learning/atoms/std-learn-measure.orb +1979 -0
  175. package/behaviors/registry/ui/learning/atoms/std-learn-perturb.orb +2994 -0
  176. package/behaviors/registry/ui/learning/atoms/std-learn-predict.orb +2682 -0
  177. package/behaviors/registry/ui/learning/atoms/std-learn-scatter-fit.orb +6539 -0
  178. package/behaviors/registry/ui/learning/atoms/std-learn-scenario.orb +1842 -0
  179. package/behaviors/registry/ui/learning/atoms/std-learn-sim-3d.orb +1648 -0
  180. package/behaviors/registry/ui/learning/atoms/std-learn-speed-drill.orb +2307 -0
  181. package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +3118 -0
  182. package/behaviors/registry/ui/learning/atoms/std-learn-transport.orb +986 -0
  183. package/behaviors/registry/ui/learning/atoms/std-learn-tree.orb +3333 -0
  184. package/behaviors/registry/ui/learning/atoms/ui-algo-graph-canvas.orb +716 -0
  185. package/behaviors/registry/ui/learning/atoms/ui-algorithm-canvas.orb +679 -5
  186. package/behaviors/registry/ui/learning/atoms/ui-biology-canvas.orb +962 -18
  187. package/behaviors/registry/ui/learning/atoms/ui-chemistry-canvas.orb +919 -30
  188. package/behaviors/registry/ui/learning/atoms/ui-learning-canvas.orb +372 -1
  189. package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +936 -16
  190. package/behaviors/registry/ui/learning/atoms/ui-physics-canvas.orb +1328 -43
  191. package/behaviors/registry/ui/marketing/atoms/std-marketing-article.orb +1 -2
  192. package/behaviors/registry/ui/marketing/atoms/std-marketing-footer.orb +1 -2
  193. package/behaviors/registry/ui/marketing/atoms/std-marketing-split.orb +1 -2
  194. package/behaviors/registry/ui/marketing/atoms/std-marketing-stats.orb +1 -2
  195. package/behaviors/registry/ui/marketing/atoms/std-marketing-steps.orb +1 -2
  196. package/behaviors/registry/ui/marketing/atoms/std-marketing-team.orb +1 -2
  197. package/behaviors/registry/ui/marketing/organisms/ui-book-viewer.orb +4 -1
  198. package/behaviors/registry/ui/marketing/organisms/ui-case-study-organism.orb +4 -1
  199. package/behaviors/registry/ui/marketing/organisms/ui-feature-grid-organism.orb +4 -1
  200. package/behaviors/registry/ui/marketing/organisms/ui-hero-organism.orb +4 -1
  201. package/behaviors/registry/ui/marketing/organisms/ui-pricing-organism.orb +4 -1
  202. package/behaviors/registry/ui/marketing/organisms/ui-showcase-organism.orb +4 -1
  203. package/behaviors/registry/ui/marketing/organisms/ui-stats-organism.orb +4 -1
  204. package/behaviors/registry/ui/marketing/organisms/ui-step-flow-organism.orb +4 -1
  205. package/behaviors/registry/ui/marketing/organisms/ui-team-organism.orb +4 -1
  206. package/dist/behaviors/exports-reader.js +3704 -2189
  207. package/dist/behaviors/functions/index.d.ts +2638 -254
  208. package/dist/behaviors/functions/index.js +3648 -2190
  209. package/dist/behaviors/index.d.ts +1 -1
  210. package/dist/behaviors/index.js +3705 -2190
  211. package/dist/behaviors/query.js +3704 -2189
  212. package/dist/behaviors-embeddings.hash.json +3 -3
  213. package/dist/behaviors-embeddings.json +289 -213
  214. package/dist/behaviors-registry.json +7900 -587
  215. package/dist/index.d.ts +1 -1
  216. package/dist/index.js +3705 -2190
  217. package/dist/knob-embeddings.hash.json +3 -3
  218. package/dist/knob-embeddings.json +1 -1
  219. package/dist/registry/factory-signatures.json +1 -1
  220. package/package.json +2 -2
@@ -0,0 +1,409 @@
1
+ app std-learn-tour-3d "1.0.0"
2
+ "Generic guided-tour-over-3D-stations instrument. Contract: a config-declared roster of stations (id, name, caption, x, y, z, radius, color, shape, opacity) plus optional connector edges (from, to, color, label), rendered as an orbit-rotatable volumetric scene on a biology-canvas in 3D mode; pressing Tour advances the selection to the next station (mod the roster length) and clicking a shape selects its station — either way the camera retargets onto the selected station's x/y/z, the station enlarges, a name — caption narration renders, and a progress readout tracks visited stations until every station has been toured. A `backdrop` knob renders non-station scene geometry under the stations (painted, never toured). SYNC_STATIONS hot-swaps the roster mid-flight (a composed preset coordinator emits it with new stations/edges/backdrop; the tour adopts them and restarts progress at the first new station) — the single-writer entry point for dynamic rosters. Composed of a biology-canvas in 3D mode plus a tour state machine. Standalone by default; behind a lab router set matchMode and it renders only when MODE_SELECTED matches."
3
+ orbital LearnTour3DOrbital {
4
+ type SceneLight = {
5
+ intensity : number
6
+ color : string
7
+ position : [number]
8
+ }
9
+ type SceneHemiLight = {
10
+ intensity : number
11
+ color : string
12
+ groundColor : string
13
+ }
14
+ type ScenePointLight = {
15
+ intensity : number
16
+ color : string
17
+ position : [number]!
18
+ distance : number
19
+ decay : number
20
+ }
21
+ type Lighting3D = {
22
+ ambient : SceneLight
23
+ directional : SceneLight
24
+ hemisphere : SceneHemiLight
25
+ points : [ScenePointLight]
26
+ environment : "room" | "none"
27
+ toneMapping : "aces" | "none"
28
+ }
29
+ type Post3D = {
30
+ bloom : { intensity : number threshold : number smoothing : number }
31
+ vignette : { offset : number darkness : number }
32
+ }
33
+ type Learn3DStation = {
34
+ id : string!
35
+ name : string!
36
+ caption : string
37
+ x : number!
38
+ y : number!
39
+ z : number!
40
+ radius : number
41
+ color : string
42
+ shape : string
43
+ opacity : number
44
+ }
45
+
46
+ type Learn3DEdge = {
47
+ from : string!
48
+ to : string!
49
+ color : string
50
+ label : string
51
+ }
52
+
53
+ type Learn3DClick = {
54
+ id : string
55
+ type : string
56
+ index : number!
57
+ }
58
+
59
+ entity Learn3DTourScene [runtime] {
60
+ id : string!
61
+ active : boolean = false
62
+ title : string
63
+ stations : [Learn3DStation]
64
+ backdrop : [Learn3DStation] = []
65
+ edges : [Learn3DEdge]
66
+ selectedIndex : number = 0
67
+ selectedId : string = ""
68
+ visitedIds : [string] = []
69
+ tourComplete : boolean = false
70
+ narration : string = ""
71
+ }
72
+
73
+ trait Learn3DTourSim -> @rebindable Learn3DTourScene [interaction, instance] {
74
+ initial: idle
75
+
76
+ state idle {
77
+ INIT -> idle when (== @config.matchMode "")
78
+ (set @entity.active true)
79
+ (set @entity.title @config.title)
80
+ (set @entity.stations @config.stations)
81
+ (set @entity.backdrop @config.backdrop)
82
+ (set @entity.edges @config.edges)
83
+ (set @entity.selectedIndex 0)
84
+ (set @entity.selectedId (object/get (array/nth @entity.stations 0) id))
85
+ (set @entity.visitedIds [(object/get (array/nth @entity.stations 0) id)])
86
+ (set @entity.tourComplete false)
87
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations 0) name) " — " (object/get (array/nth @entity.stations 0) caption)))
88
+ (render-ui main {
89
+ type: box
90
+ padding: md
91
+ children: [
92
+ { type: stack, direction: vertical, gap: md, children: [
93
+ { type: typography, variant: h4, content: @entity.title },
94
+ { type: stack, direction: horizontal, gap: sm, children: [
95
+ { type: button, action: TOUR, label: @config.tourLabel, variant: primary },
96
+ { type: button, action: RESET, label: @config.resetLabel, variant: ghost }
97
+ ] },
98
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
99
+ mode: "3d",
100
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
101
+ 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) } },
102
+ lighting: @config.lighting,
103
+ post: @config.post,
104
+ 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) })),
105
+ edges: @entity.edges,
106
+ interactive: true,
107
+ onShapeClick: "SELECT_STATION"
108
+ },
109
+ { type: typography, variant: body, content: @entity.narration },
110
+ { type: typography, variant: caption, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
111
+ ] }
112
+ ]
113
+ })
114
+ (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 })
115
+
116
+ MODE_SELECTED -> idle when (== ?mode @config.matchMode)
117
+ (set @entity.active true)
118
+ (set @entity.title @config.title)
119
+ (set @entity.stations @config.stations)
120
+ (set @entity.backdrop @config.backdrop)
121
+ (set @entity.edges @config.edges)
122
+ (set @entity.selectedIndex 0)
123
+ (set @entity.selectedId (object/get (array/nth @entity.stations 0) id))
124
+ (set @entity.visitedIds [(object/get (array/nth @entity.stations 0) id)])
125
+ (set @entity.tourComplete false)
126
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations 0) name) " — " (object/get (array/nth @entity.stations 0) caption)))
127
+ (render-ui main {
128
+ type: box
129
+ padding: md
130
+ children: [
131
+ { type: stack, direction: vertical, gap: md, children: [
132
+ { type: typography, variant: h4, content: @entity.title },
133
+ { type: stack, direction: horizontal, gap: sm, children: [
134
+ { type: button, action: TOUR, label: @config.tourLabel, variant: primary },
135
+ { type: button, action: RESET, label: @config.resetLabel, variant: ghost }
136
+ ] },
137
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
138
+ mode: "3d",
139
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
140
+ 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) } },
141
+ lighting: @config.lighting,
142
+ post: @config.post,
143
+ 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) })),
144
+ edges: @entity.edges,
145
+ interactive: true,
146
+ onShapeClick: "SELECT_STATION"
147
+ },
148
+ { type: typography, variant: body, content: @entity.narration },
149
+ { type: typography, variant: caption, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
150
+ ] }
151
+ ]
152
+ })
153
+ (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 })
154
+
155
+ TOUR -> idle when (== @entity.active true)
156
+ (set @entity.selectedIndex (math/mod (+ @entity.selectedIndex 1) (array/len @entity.stations)))
157
+ (set @entity.selectedId (object/get (array/nth @entity.stations @entity.selectedIndex) id))
158
+ (set @entity.visitedIds (if (array/includes @entity.visitedIds @entity.selectedId) @entity.visitedIds (array/append @entity.visitedIds @entity.selectedId)))
159
+ (set @entity.tourComplete (== (array/len @entity.visitedIds) (array/len @entity.stations)))
160
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations @entity.selectedIndex) name) " — " (object/get (array/nth @entity.stations @entity.selectedIndex) caption)))
161
+ (render-ui main {
162
+ type: box
163
+ padding: md
164
+ children: [
165
+ { type: stack, direction: vertical, gap: md, children: [
166
+ { type: typography, variant: h4, content: @entity.title },
167
+ { type: stack, direction: horizontal, gap: sm, children: [
168
+ { type: button, action: TOUR, label: @config.tourLabel, variant: primary },
169
+ { type: button, action: RESET, label: @config.resetLabel, variant: ghost }
170
+ ] },
171
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
172
+ mode: "3d",
173
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
174
+ 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) } },
175
+ lighting: @config.lighting,
176
+ post: @config.post,
177
+ 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) })),
178
+ edges: @entity.edges,
179
+ interactive: true,
180
+ onShapeClick: "SELECT_STATION"
181
+ },
182
+ { type: typography, variant: body, content: @entity.narration },
183
+ { type: typography, variant: caption, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
184
+ ] }
185
+ ]
186
+ })
187
+ (emit TOUR_3D_UPDATED { id: @entity.selectedId, name: (object/get (array/nth @entity.stations @entity.selectedIndex) name), caption: (object/get (array/nth @entity.stations @entity.selectedIndex) caption), visited: (array/len @entity.visitedIds), total: (array/len @entity.stations), tourComplete: @entity.tourComplete })
188
+
189
+ SELECT_STATION -> idle when (== @entity.active true)
190
+ (let (
191
+ (clickedId (object/get ?payload id))
192
+ (found (array/findIndex @entity.stations (fn n (== (object/get @n id) @clickedId))))
193
+ (idx (if (>= @found 0) @found @entity.selectedIndex))
194
+ )
195
+ (do
196
+ (set @entity.selectedIndex @idx)
197
+ (set @entity.selectedId (object/get (array/nth @entity.stations @idx) id))
198
+ (set @entity.visitedIds (if (array/includes @entity.visitedIds @entity.selectedId) @entity.visitedIds (array/append @entity.visitedIds @entity.selectedId)))
199
+ (set @entity.tourComplete (== (array/len @entity.visitedIds) (array/len @entity.stations)))
200
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations @idx) name) " — " (object/get (array/nth @entity.stations @idx) caption)))
201
+ (render-ui main {
202
+ type: box
203
+ padding: md
204
+ children: [
205
+ { type: stack, direction: vertical, gap: md, children: [
206
+ { type: typography, variant: h4, content: @entity.title },
207
+ { type: stack, direction: horizontal, gap: sm, children: [
208
+ { type: button, action: TOUR, label: @config.tourLabel, variant: primary },
209
+ { type: button, action: RESET, label: @config.resetLabel, variant: ghost }
210
+ ] },
211
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
212
+ mode: "3d",
213
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
214
+ 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) } },
215
+ lighting: @config.lighting,
216
+ post: @config.post,
217
+ 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) })),
218
+ edges: @entity.edges,
219
+ interactive: true,
220
+ onShapeClick: "SELECT_STATION"
221
+ },
222
+ { type: typography, variant: body, content: @entity.narration },
223
+ { type: typography, variant: caption, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
224
+ ] }
225
+ ]
226
+ })
227
+ (emit TOUR_3D_UPDATED { id: @entity.selectedId, name: (object/get (array/nth @entity.stations @idx) name), caption: (object/get (array/nth @entity.stations @idx) caption), visited: (array/len @entity.visitedIds), total: (array/len @entity.stations), tourComplete: @entity.tourComplete })
228
+ )
229
+ )
230
+
231
+ RESET -> idle when (== @entity.active true)
232
+ (set @entity.selectedIndex 0)
233
+ (set @entity.selectedId (object/get (array/nth @entity.stations 0) id))
234
+ (set @entity.visitedIds [(object/get (array/nth @entity.stations 0) id)])
235
+ (set @entity.tourComplete false)
236
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations 0) name) " — " (object/get (array/nth @entity.stations 0) caption)))
237
+ (render-ui main {
238
+ type: box
239
+ padding: md
240
+ children: [
241
+ { type: stack, direction: vertical, gap: md, children: [
242
+ { type: typography, variant: h4, content: @entity.title },
243
+ { type: stack, direction: horizontal, gap: sm, children: [
244
+ { type: button, action: TOUR, label: @config.tourLabel, variant: primary },
245
+ { type: button, action: RESET, label: @config.resetLabel, variant: ghost }
246
+ ] },
247
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
248
+ mode: "3d",
249
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
250
+ 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) } },
251
+ lighting: @config.lighting,
252
+ post: @config.post,
253
+ 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) })),
254
+ edges: @entity.edges,
255
+ interactive: true,
256
+ onShapeClick: "SELECT_STATION"
257
+ },
258
+ { type: typography, variant: body, content: @entity.narration },
259
+ { type: typography, variant: caption, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
260
+ ] }
261
+ ]
262
+ })
263
+ (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 })
264
+
265
+ SYNC_STATIONS -> idle when (== @entity.active true)
266
+ (set @entity.stations ?stations)
267
+ (set @entity.backdrop ?backdrop)
268
+ (set @entity.edges ?edges)
269
+ (set @entity.selectedIndex 0)
270
+ (set @entity.selectedId (object/get (array/nth @entity.stations 0) id))
271
+ (set @entity.visitedIds [(object/get (array/nth @entity.stations 0) id)])
272
+ (set @entity.tourComplete false)
273
+ (set @entity.narration (str/concat (object/get (array/nth @entity.stations 0) name) " — " (object/get (array/nth @entity.stations 0) caption)))
274
+ (render-ui main {
275
+ type: box
276
+ padding: md
277
+ children: [
278
+ { type: stack, direction: vertical, gap: md, children: [
279
+ { type: typography, variant: h4, content: @entity.title },
280
+ { type: stack, direction: horizontal, gap: sm, children: [
281
+ { type: button, action: TOUR, label: @config.tourLabel, variant: primary },
282
+ { type: button, action: RESET, label: @config.resetLabel, variant: ghost }
283
+ ] },
284
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor,
285
+ mode: "3d",
286
+ camera: { mode: "perspective", zoom: @config.zoom, fov: @config.fov, azimuth: @config.azimuth,
287
+ 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) } },
288
+ lighting: @config.lighting,
289
+ post: @config.post,
290
+ 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) })),
291
+ edges: @entity.edges,
292
+ interactive: true,
293
+ onShapeClick: "SELECT_STATION"
294
+ },
295
+ { type: typography, variant: body, content: @entity.narration },
296
+ { type: typography, variant: caption, content: (str/concat "Explored " (str/concat "" (array/len @entity.visitedIds)) " of " (str/concat "" (array/len @entity.stations)) (if @entity.tourComplete " — tour complete!" "")) }
297
+ ] }
298
+ ]
299
+ })
300
+ (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 })
301
+ }
302
+
303
+ listens {
304
+ MODE_SELECTED {
305
+ mode : string
306
+ }
307
+ @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
308
+ @tier "domain"
309
+ SELECT_STATION {
310
+ payload : Learn3DClick
311
+ }
312
+ @description "Canvas shape-click from the 3D scene (biology-canvas onShapeClick) carrying the clicked station's id, type, and roster index."
313
+ @tier "domain"
314
+ SYNC_STATIONS {
315
+ stations : [Learn3DStation]
316
+ edges : [Learn3DEdge]
317
+ backdrop : [Learn3DStation]
318
+ }
319
+ @description "Roster hot-swap from a composed preset coordinator — the tour adopts the new stations/edges/backdrop and restarts progress at the first new station; the single-writer entry point for dynamic rosters."
320
+ @tier "domain"
321
+ }
322
+
323
+ emits {
324
+ TOUR_3D_UPDATED -> external {
325
+ id : string
326
+ name : string
327
+ caption : string
328
+ visited : number
329
+ total : number
330
+ tourComplete : boolean
331
+ }
332
+ @description "Fires on INIT/MODE_SELECTED/Tour/click/Reset with the currently selected station's id, name, and caption, plus visited/total progress and whether every station has now been visited — a composed listener (readout, quiz) can subscribe; a harmless no-op when the atom runs standalone."
333
+ @synonyms "select, choose, station, explore, tour, flythrough, pick"
334
+ @tier "domain"
335
+ }
336
+
337
+ config {
338
+ matchMode : string = ""
339
+ @label "Match Mode"
340
+ @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
341
+ @tier "presentation"
342
+ title : string = "3D Guided Tour"
343
+ @label "Title"
344
+ @description "Title shown above the canvas."
345
+ @tier "presentation"
346
+ stations : [Learn3DStation] = [
347
+ { id: "alpha", name: "Station Alpha", caption: "The first stop on the tour — the camera retargets here and the narration reads its caption.", x: 0, y: 0, z: 0, radius: 1, color: "#2563eb", shape: sphere, opacity: 1 },
348
+ { id: "beta", name: "Station Beta", caption: "The second stop — press Tour again or click a shape to advance the selection.", x: 3, y: 1, z: -1, radius: 0.8, color: "#7c3aed", shape: sphere, opacity: 1 },
349
+ { id: "gamma", name: "Station Gamma", caption: "The final stop — once every station has been visited the tour marks itself complete.", x: -3, y: -1, z: 1, radius: 0.8, color: "#0891b2", shape: sphere, opacity: 1 }
350
+ ]
351
+ @label "Stations"
352
+ @description "The station roster in didactic tour order — each with scene position, mesh shape, and a caption. Tour and clicks enlarge the selected station, retarget the camera onto its x/y/z, and narrate its caption."
353
+ @synonyms "station, stop, waypoint, tour, landmark, 3d"
354
+ @tier "domain"
355
+ edges : [Learn3DEdge] = []
356
+ @label "Edges"
357
+ @description "Optional connector lines drawn between stations, by id."
358
+ @synonyms "edge, link, connector, tether"
359
+ @tier "domain"
360
+ backdrop : [Learn3DStation] = []
361
+ @label "Backdrop Nodes"
362
+ @description "Non-station scene geometry rendered under the tour stations (painted first, stations on top) — painted but never tour stops and never highlight-selected; SYNC_STATIONS swaps it together with the roster, so a composed preset coordinator can re-skin the whole scene per preset."
363
+ @synonyms "backdrop, scenery, geometry, context nodes, 3d"
364
+ @tier "domain"
365
+ tourLabel : string = "Tour"
366
+ @label "Tour Button Label"
367
+ @description "Label text for the button that advances the tour to the next station."
368
+ @tier "presentation"
369
+ resetLabel : string = "Reset"
370
+ @label "Reset Button Label"
371
+ @description "Label text for the button that returns the tour to the first station."
372
+ @tier "presentation"
373
+ zoom : number = 1
374
+ @label "Zoom"
375
+ @description "Camera zoom factor for the 3D scene."
376
+ @tier "presentation"
377
+ fov : number = 50
378
+ @label "Field of View"
379
+ @description "Perspective camera field of view in degrees."
380
+ @tier "presentation"
381
+ azimuth : number = 0.6
382
+ @label "Azimuth"
383
+ @description "Camera orbit azimuth in radians; the learner can orbit freely on top of this."
384
+ @tier "presentation"
385
+ lighting : Lighting3D = {}
386
+ @label "Lighting"
387
+ @description "3D scene light rig as data; empty object uses the standard rig."
388
+ @tier "presentation"
389
+ post : Post3D = {}
390
+ @label "Post Processing"
391
+ @description "3D post-processing stack (bloom/vignette) as data; empty object disables it."
392
+ @tier "presentation"
393
+ width : number = 600
394
+ @label "Width"
395
+ @description "Canvas width in pixels."
396
+ @tier "presentation"
397
+ height : number = 400
398
+ @label "Height"
399
+ @description "Canvas height in pixels."
400
+ @tier "presentation"
401
+ backgroundColor : string = "#0f172a"
402
+ @label "Background Color"
403
+ @description "Canvas background color — a dark stage makes translucent shells and station colors read in 3D."
404
+ @tier "presentation"
405
+ }
406
+ }
407
+
408
+ page "/learn-tour-3d" as Learn3DTourPage -> Learn3DTourSim
409
+ }
@@ -0,0 +1,186 @@
1
+ app std-learn-transport "1.0.0"
2
+ "Generic 2D step-transport (clock) instrument — the substrate twin of std-learn-sim-3d for discrete-step instruments. Contract: the atom owns the Step/Run-Pause/Reset transport and the clock but NOT the subject computation — STEP advances one step manually, TOGGLE_RUN engages a 32ms tick that accumulates elapsed time and fires one step every stepIntervalMs, RESET zeroes the counters and restores autoRun. Every step (button or clock) broadcasts LEARN_STEP; a composed sibling trait transitions directly on the LEARN_STEP / LEARN_RESET / LEARN_UPDATED broadcasts (the same bare-event cascade the lab routers use), applies the domain computation, halts the clock by setting the shared entity's done flag (the sibling is done's ONLY writer — it must also clear it on LEARN_RESET), and owns the scene render — this atom renders only its control strip and step caption. Standalone by default; behind a lab router set matchMode and it renders only when MODE_SELECTED matches."
3
+ orbital LearnTransportOrbital {
4
+ entity LearnTransportScene [runtime, shared] {
5
+ id : string!
6
+ active : boolean = false
7
+ title : string
8
+ running : boolean = false
9
+ steps : int = 0
10
+ elapsedMs : number = 0
11
+ done : boolean = false
12
+ }
13
+
14
+ trait LearnTransportSim -> @rebindable LearnTransportScene [interaction, instance] {
15
+ initial: idle
16
+
17
+ state idle {
18
+ INIT -> idle when (== @config.matchMode "")
19
+ (set @entity.active true)
20
+ (set @entity.title @config.title)
21
+ (set @entity.running @config.autoRun)
22
+ (set @entity.steps 0)
23
+ (set @entity.elapsedMs 0)
24
+ (render-ui main {
25
+ type: box
26
+ padding: md
27
+ children: [
28
+ { type: stack, direction: vertical, gap: sm, children: [
29
+ { type: typography, variant: h4, content: @entity.title },
30
+ { type: stack, direction: horizontal, gap: sm, children: [
31
+ { type: button, action: STEP, label: "Step", variant: primary },
32
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
33
+ { type: button, action: RESET, label: "Reset", variant: ghost }
34
+ ] },
35
+ { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
36
+ ] }
37
+ ]
38
+ })
39
+ (emit LEARN_UPDATED { running: @entity.running, steps: @entity.steps })
40
+
41
+ MODE_SELECTED -> idle when (== ?mode @config.matchMode)
42
+ (set @entity.active true)
43
+ (set @entity.title @config.title)
44
+ (set @entity.running @config.autoRun)
45
+ (set @entity.steps 0)
46
+ (set @entity.elapsedMs 0)
47
+ (render-ui main {
48
+ type: box
49
+ padding: md
50
+ children: [
51
+ { type: stack, direction: vertical, gap: sm, children: [
52
+ { type: typography, variant: h4, content: @entity.title },
53
+ { type: stack, direction: horizontal, gap: sm, children: [
54
+ { type: button, action: STEP, label: "Step", variant: primary },
55
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
56
+ { type: button, action: RESET, label: "Reset", variant: ghost }
57
+ ] },
58
+ { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
59
+ ] }
60
+ ]
61
+ })
62
+ (emit LEARN_UPDATED { running: @entity.running, steps: @entity.steps })
63
+
64
+ STEP -> idle when (and (== @entity.active true) (not (== @entity.done true)))
65
+ (set @entity.steps (+ @entity.steps 1))
66
+ (render-ui main {
67
+ type: box
68
+ padding: md
69
+ children: [
70
+ { type: stack, direction: vertical, gap: sm, children: [
71
+ { type: typography, variant: h4, content: @entity.title },
72
+ { type: stack, direction: horizontal, gap: sm, children: [
73
+ { type: button, action: STEP, label: "Step", variant: primary },
74
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
75
+ { type: button, action: RESET, label: "Reset", variant: ghost }
76
+ ] },
77
+ { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
78
+ ] }
79
+ ]
80
+ })
81
+ (emit LEARN_STEP { steps: @entity.steps })
82
+
83
+ TOGGLE_RUN -> idle when (== @entity.active true)
84
+ (set @entity.running (not @entity.running))
85
+ (render-ui main {
86
+ type: box
87
+ padding: md
88
+ children: [
89
+ { type: stack, direction: vertical, gap: sm, children: [
90
+ { type: typography, variant: h4, content: @entity.title },
91
+ { type: stack, direction: horizontal, gap: sm, children: [
92
+ { type: button, action: STEP, label: "Step", variant: primary },
93
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
94
+ { type: button, action: RESET, label: "Reset", variant: ghost }
95
+ ] },
96
+ { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
97
+ ] }
98
+ ]
99
+ })
100
+ (emit LEARN_UPDATED { running: @entity.running, steps: @entity.steps })
101
+
102
+ RESET -> idle when (== @entity.active true)
103
+ (set @entity.running @config.autoRun)
104
+ (set @entity.steps 0)
105
+ (set @entity.elapsedMs 0)
106
+ (render-ui main {
107
+ type: box
108
+ padding: md
109
+ children: [
110
+ { type: stack, direction: vertical, gap: sm, children: [
111
+ { type: typography, variant: h4, content: @entity.title },
112
+ { type: stack, direction: horizontal, gap: sm, children: [
113
+ { type: button, action: STEP, label: "Step", variant: primary },
114
+ { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
115
+ { type: button, action: RESET, label: "Reset", variant: ghost }
116
+ ] },
117
+ { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
118
+ ] }
119
+ ]
120
+ })
121
+ (emit LEARN_RESET { })
122
+ }
123
+
124
+ ticks {
125
+ transportTick every 32ms
126
+ when (and (== @entity.active true) (and (== @entity.running true) (not (== @entity.done true))))
127
+ (set @entity.elapsedMs (+ @entity.elapsedMs 32))
128
+ (when (>= @entity.elapsedMs @config.stepIntervalMs)
129
+ (do
130
+ (set @entity.elapsedMs 0)
131
+ (set @entity.steps (+ @entity.steps 1))
132
+ (emit LEARN_STEP { steps: @entity.steps })
133
+ ))
134
+ }
135
+
136
+ listens {
137
+ MODE_SELECTED {
138
+ mode : string
139
+ }
140
+ @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
141
+ @tier "domain"
142
+ }
143
+
144
+ emits {
145
+ LEARN_STEP -> external {
146
+ steps : int
147
+ }
148
+ @description "Fires once per domain step — from the Step button or every stepIntervalMs of running clock — with the step counter; the composed subject trait transitions on it and applies one step of the domain computation. A harmless no-op when the atom runs standalone."
149
+ @synonyms "step, advance, tick, iterate, next"
150
+ @tier "domain"
151
+ LEARN_RESET -> external {
152
+ }
153
+ @description "Fires on Reset after the transport counters re-zero; the composed subject trait reseeds its domain state from config on it. A harmless no-op when the atom runs standalone."
154
+ @synonyms "reset, reseed, restart, clear"
155
+ @tier "domain"
156
+ LEARN_UPDATED -> external {
157
+ running : boolean
158
+ steps : int
159
+ }
160
+ @description "Fires on INIT/MODE_SELECTED/Run-Pause toggle with the transport state — running flag and step counter — so the composed subject trait can repaint its shell (Run/Pause label, step readout). A harmless no-op when the atom runs standalone."
161
+ @synonyms "run, pause, transport, running state"
162
+ @tier "domain"
163
+ }
164
+
165
+ config {
166
+ matchMode : string = ""
167
+ @label "Match Mode"
168
+ @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
169
+ @tier "presentation"
170
+ title : string = "Learning Instrument"
171
+ @label "Title"
172
+ @description "Title shown above the transport controls."
173
+ @tier "presentation"
174
+ autoRun : boolean = false
175
+ @label "Auto Run"
176
+ @description "When true the clock starts stepping on INIT; the Reset arm restores this value."
177
+ @tier "domain"
178
+ stepIntervalMs : number = 500
179
+ @label "Step Interval (ms)"
180
+ @description "Milliseconds of running clock per domain step — the 32ms base tick accumulates elapsed time and fires LEARN_STEP each time this interval elapses. 300-900 suits narrated steppers; 32 fires every frame for continuous sims."
181
+ @tier "domain"
182
+ }
183
+ }
184
+
185
+ page "/learn-transport" as LearnTransportPage -> LearnTransportSim
186
+ }