@almadar/std 16.241.0 → 16.243.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 (109) hide show
  1. package/behaviors/behaviors-registry.json +4650 -535
  2. package/behaviors/lolo/agent/atoms/std-agent-memory.lolo +38 -1
  3. package/behaviors/lolo/agent/atoms/std-agent-session.lolo +1074 -47
  4. package/behaviors/lolo/agent/atoms/std-agent-tool-loop.lolo +8 -7
  5. package/behaviors/lolo/agent/organisms/std-agent-assistant.lolo +56 -37
  6. package/behaviors/lolo/infra/atoms/std-page-open.lolo +59 -0
  7. package/behaviors/lolo/infra/atoms/std-page-search.lolo +136 -0
  8. package/behaviors/lolo/infra/atoms/std-page-treat.lolo +59 -0
  9. package/behaviors/lolo/infra/atoms/std-page-watch.lolo +88 -0
  10. package/behaviors/lolo/ui/core/atoms/std-book-reader.lolo +25 -1
  11. package/behaviors/lolo/ui/core/atoms/std-card-grid.lolo +9 -0
  12. package/behaviors/lolo/ui/core/atoms/std-doc-page.lolo +153 -0
  13. package/behaviors/lolo/ui/learning/atoms/std-learn-arrangement.lolo +124 -92
  14. package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +114 -55
  15. package/behaviors/lolo/ui/learning/atoms/std-learn-compare.lolo +40 -14
  16. package/behaviors/lolo/ui/learning/atoms/std-learn-coupled-sim.lolo +363 -123
  17. package/behaviors/lolo/ui/learning/atoms/std-learn-derivation.lolo +93 -48
  18. package/behaviors/lolo/ui/learning/atoms/std-learn-diagram.lolo +60 -28
  19. package/behaviors/lolo/ui/learning/atoms/std-learn-doser.lolo +193 -96
  20. package/behaviors/lolo/ui/learning/atoms/std-learn-estimate.lolo +75 -32
  21. package/behaviors/lolo/ui/learning/atoms/std-learn-goal-seek.lolo +142 -86
  22. package/behaviors/lolo/ui/learning/atoms/std-learn-grid-sim.lolo +266 -98
  23. package/behaviors/lolo/ui/learning/atoms/std-learn-hotspot.lolo +103 -65
  24. package/behaviors/lolo/ui/learning/atoms/std-learn-live-sim.lolo +223 -109
  25. package/behaviors/lolo/ui/learning/atoms/std-learn-measure.lolo +232 -64
  26. package/behaviors/lolo/ui/learning/atoms/std-learn-param-explorer.lolo +69 -29
  27. package/behaviors/lolo/ui/learning/atoms/std-learn-perturb.lolo +363 -123
  28. package/behaviors/lolo/ui/learning/atoms/std-learn-predict.lolo +73 -41
  29. package/behaviors/lolo/ui/learning/atoms/std-learn-sample-accumulator.lolo +96 -38
  30. package/behaviors/lolo/ui/learning/atoms/std-learn-sandbox.lolo +65 -45
  31. package/behaviors/lolo/ui/learning/atoms/std-learn-scatter-fit.lolo +243 -117
  32. package/behaviors/lolo/ui/learning/atoms/std-learn-scenario.lolo +76 -28
  33. package/behaviors/lolo/ui/learning/atoms/std-learn-sim-3d.lolo +228 -109
  34. package/behaviors/lolo/ui/learning/atoms/std-learn-speed-drill.lolo +95 -44
  35. package/behaviors/lolo/ui/learning/atoms/std-learn-stepper.lolo +199 -125
  36. package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +92 -48
  37. package/behaviors/lolo/ui/learning/atoms/std-learn-trace-player.lolo +239 -78
  38. package/behaviors/lolo/ui/learning/atoms/std-learn-transport.lolo +147 -38
  39. package/behaviors/lolo/ui/learning/atoms/std-learn-tree.lolo +123 -58
  40. package/behaviors/lolo/ui/learning/atoms/ui-algo-graph-canvas.lolo +22 -0
  41. package/behaviors/lolo/ui/learning/atoms/ui-algorithm-canvas.lolo +26 -0
  42. package/behaviors/lolo/ui/learning/atoms/ui-biology-canvas.lolo +26 -0
  43. package/behaviors/lolo/ui/learning/atoms/ui-chemistry-canvas.lolo +27 -1
  44. package/behaviors/lolo/ui/learning/atoms/ui-learning-canvas.lolo +27 -1
  45. package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +29 -9
  46. package/behaviors/lolo/ui/learning/atoms/ui-physics-canvas.lolo +26 -0
  47. package/behaviors/lolo-behavior-contracts.json +1 -1
  48. package/behaviors/registry/agent/atoms/std-agent-memory.orb +372 -2
  49. package/behaviors/registry/agent/atoms/std-agent-session.orb +4752 -348
  50. package/behaviors/registry/agent/atoms/std-agent-tool-loop.orb +54 -42
  51. package/behaviors/registry/agent/organisms/std-agent-assistant.orb +356 -122
  52. package/behaviors/registry/factory-signatures.json +1 -1
  53. package/behaviors/registry/infra/atoms/std-page-open.orb +435 -0
  54. package/behaviors/registry/infra/atoms/std-page-search.orb +885 -0
  55. package/behaviors/registry/infra/atoms/std-page-treat.orb +494 -0
  56. package/behaviors/registry/infra/atoms/std-page-watch.orb +572 -0
  57. package/behaviors/registry/ui/core/atoms/std-book-reader.orb +119 -0
  58. package/behaviors/registry/ui/core/atoms/std-card-grid.orb +61 -0
  59. package/behaviors/registry/ui/core/atoms/std-doc-page.orb +594 -0
  60. package/behaviors/registry/ui/learning/atoms/std-learn-arrangement.orb +903 -369
  61. package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +1002 -232
  62. package/behaviors/registry/ui/learning/atoms/std-learn-compare.orb +253 -74
  63. package/behaviors/registry/ui/learning/atoms/std-learn-coupled-sim.orb +3624 -921
  64. package/behaviors/registry/ui/learning/atoms/std-learn-derivation.orb +685 -144
  65. package/behaviors/registry/ui/learning/atoms/std-learn-diagram.orb +404 -52
  66. package/behaviors/registry/ui/learning/atoms/std-learn-doser.orb +1787 -210
  67. package/behaviors/registry/ui/learning/atoms/std-learn-estimate.orb +699 -71
  68. package/behaviors/registry/ui/learning/atoms/std-learn-goal-seek.orb +1102 -578
  69. package/behaviors/registry/ui/learning/atoms/std-learn-grid-sim.orb +2760 -872
  70. package/behaviors/registry/ui/learning/atoms/std-learn-hotspot.orb +689 -152
  71. package/behaviors/registry/ui/learning/atoms/std-learn-live-sim.orb +1431 -633
  72. package/behaviors/registry/ui/learning/atoms/std-learn-measure.orb +2485 -512
  73. package/behaviors/registry/ui/learning/atoms/std-learn-param-explorer.orb +501 -197
  74. package/behaviors/registry/ui/learning/atoms/std-learn-perturb.orb +3642 -928
  75. package/behaviors/registry/ui/learning/atoms/std-learn-predict.orb +705 -125
  76. package/behaviors/registry/ui/learning/atoms/std-learn-sample-accumulator.orb +624 -114
  77. package/behaviors/registry/ui/learning/atoms/std-learn-sandbox.orb +405 -153
  78. package/behaviors/registry/ui/learning/atoms/std-learn-scatter-fit.orb +1911 -611
  79. package/behaviors/registry/ui/learning/atoms/std-learn-scenario.orb +436 -128
  80. package/behaviors/registry/ui/learning/atoms/std-learn-sim-3d.orb +2482 -485
  81. package/behaviors/registry/ui/learning/atoms/std-learn-speed-drill.orb +896 -192
  82. package/behaviors/registry/ui/learning/atoms/std-learn-stepper.orb +2001 -759
  83. package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +734 -228
  84. package/behaviors/registry/ui/learning/atoms/std-learn-trace-player.orb +2501 -764
  85. package/behaviors/registry/ui/learning/atoms/std-learn-transport.orb +1591 -173
  86. package/behaviors/registry/ui/learning/atoms/std-learn-tree.orb +883 -122
  87. package/behaviors/registry/ui/learning/atoms/ui-algo-graph-canvas.orb +176 -0
  88. package/behaviors/registry/ui/learning/atoms/ui-algorithm-canvas.orb +211 -0
  89. package/behaviors/registry/ui/learning/atoms/ui-biology-canvas.orb +211 -0
  90. package/behaviors/registry/ui/learning/atoms/ui-chemistry-canvas.orb +212 -1
  91. package/behaviors/registry/ui/learning/atoms/ui-learning-canvas.orb +212 -1
  92. package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +203 -9
  93. package/behaviors/registry/ui/learning/atoms/ui-physics-canvas.orb +211 -0
  94. package/behaviors/signatures.lolo +325 -655
  95. package/dist/behaviors/exports-reader.js +16092 -9747
  96. package/dist/behaviors/functions/index.d.ts +1052 -136
  97. package/dist/behaviors/functions/index.js +16063 -9745
  98. package/dist/behaviors/index.d.ts +1 -1
  99. package/dist/behaviors/index.js +16093 -9748
  100. package/dist/behaviors/query.js +16092 -9747
  101. package/dist/behaviors-embeddings.hash.json +3 -3
  102. package/dist/behaviors-embeddings.json +45 -25
  103. package/dist/behaviors-registry.json +4650 -535
  104. package/dist/index.d.ts +1 -1
  105. package/dist/index.js +16093 -9748
  106. package/dist/knob-embeddings.hash.json +3 -3
  107. package/dist/knob-embeddings.json +607 -263
  108. package/dist/registry/factory-signatures.json +1 -1
  109. package/package.json +3 -3
@@ -1,5 +1,5 @@
1
1
  app std-learn-sim-3d "1.0.0"
2
- "Generic 3D simulation-transport (clock) instrument. Contract: the atom owns the transport and the clock but NOT the physics — Run/Pause/Reset plus a 0.5x/1x/2x/4x speed row drive a running flag and speed multiplier, and a 32ms tick advances steps and simTime by dt x speedMultiplier, emitting SIM_3D_TICK { dt, simTime } each step WITHOUT rendering; a composed sibling trait transitions directly on the SIM_3D_TICK / SIM_3D_UPDATED broadcasts (the same bare-event cascade the lab routers use), applies the domain physics, and owns the scene render — this atom renders no canvas. A caption readout shows steps, sim time, and current speed. Standalone by default; behind a lab router set matchMode and it renders only when MODE_SELECTED matches."
2
+ "Generic 3D simulation-transport (clock) instrument. Contract: the atom owns the transport and the clock but NOT the physics — a Play/Pause toggle, Reset and a SPEED_SET 0.5x/1x/2x/4x group (legacy RUN, PAUSE and SPEED_HALF/NORMAL/DOUBLE/QUAD events are still honored) drive a running flag and speed multiplier, and a 32ms tick advances steps and simTime by dt x speedMultiplier, emitting SIM_3D_TICK { dt, simTime } each step WITHOUT rendering; a composed sibling trait transitions directly on the SIM_3D_TICK / SIM_3D_UPDATED broadcasts (the same bare-event cascade the lab routers use), applies the domain physics, and owns the scene render — this atom renders no canvas. A caption readout shows steps, sim time, and current speed. Standalone by default; behind a lab router set matchMode and it renders only when MODE_SELECTED matches."
3
3
  orbital LearnSim3DOrbital {
4
4
  entity Learn3DSimScene [runtime] {
5
5
  id : string!
@@ -27,24 +27,34 @@ orbital LearnSim3DOrbital {
27
27
  padding: md
28
28
  children: [
29
29
  { type: stack, direction: vertical, gap: md, children: [
30
- { type: typography, variant: h4, content: @entity.title },
31
- { type: stack, direction: horizontal, gap: sm, children: [
32
- { type: button, action: RUN, label: "Run", variant: primary },
33
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
34
- { type: button, action: RESET, label: "Reset", variant: ghost }
30
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
31
+ { type: stack, direction: vertical, gap: xs, children: [
32
+ { type: typography, variant: h4, content: @entity.title },
33
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
34
+ ] },
35
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
35
36
  ] },
36
- { type: stack, direction: horizontal, gap: sm, children: [
37
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
38
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
39
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
40
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
41
- ] },
42
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
37
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
38
+ (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" }),
39
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
40
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
41
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
42
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
43
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
44
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
45
+ ] },
46
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
47
+ ] }
43
48
  ] }
44
49
  ]
45
50
  })
46
51
  (emit SIM_3D_UPDATED { running: @entity.running, speedMultiplier: @entity.speedMultiplier, steps: @entity.steps, simTime: @entity.simTime })
47
52
 
53
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
54
+ (set @entity.active false)
55
+ (set @entity.running false)
56
+ (render-ui main null)
57
+
48
58
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
49
59
  (set @entity.active true)
50
60
  (set @entity.title @config.title)
@@ -57,19 +67,84 @@ orbital LearnSim3DOrbital {
57
67
  padding: md
58
68
  children: [
59
69
  { type: stack, direction: vertical, gap: md, children: [
60
- { type: typography, variant: h4, content: @entity.title },
61
- { type: stack, direction: horizontal, gap: sm, children: [
62
- { type: button, action: RUN, label: "Run", variant: primary },
63
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
64
- { type: button, action: RESET, label: "Reset", variant: ghost }
70
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
71
+ { type: stack, direction: vertical, gap: xs, children: [
72
+ { type: typography, variant: h4, content: @entity.title },
73
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
74
+ ] },
75
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
65
76
  ] },
66
- { type: stack, direction: horizontal, gap: sm, children: [
67
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
68
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
69
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
70
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
77
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
78
+ (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" }),
79
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
80
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
81
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
82
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
83
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
84
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
85
+ ] },
86
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
87
+ ] }
88
+ ] }
89
+ ]
90
+ })
91
+ (emit SIM_3D_UPDATED { running: @entity.running, speedMultiplier: @entity.speedMultiplier, steps: @entity.steps, simTime: @entity.simTime })
92
+
93
+ TOGGLE_RUN -> idle when (== @entity.active true)
94
+ (set @entity.running (not @entity.running))
95
+ (render-ui main {
96
+ type: box
97
+ padding: md
98
+ children: [
99
+ { type: stack, direction: vertical, gap: md, children: [
100
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
101
+ { type: stack, direction: vertical, gap: xs, children: [
102
+ { type: typography, variant: h4, content: @entity.title },
103
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
104
+ ] },
105
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
106
+ ] },
107
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
108
+ (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" }),
109
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
110
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
111
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
112
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
113
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
114
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
115
+ ] },
116
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
117
+ ] }
118
+ ] }
119
+ ]
120
+ })
121
+ (emit SIM_3D_UPDATED { running: @entity.running, speedMultiplier: @entity.speedMultiplier, steps: @entity.steps, simTime: @entity.simTime })
122
+
123
+ SPEED_SET -> idle when (== @entity.active true)
124
+ (set @entity.speedMultiplier ?speed)
125
+ (render-ui main {
126
+ type: box
127
+ padding: md
128
+ children: [
129
+ { type: stack, direction: vertical, gap: md, children: [
130
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
131
+ { type: stack, direction: vertical, gap: xs, children: [
132
+ { type: typography, variant: h4, content: @entity.title },
133
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
134
+ ] },
135
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
71
136
  ] },
72
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
137
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
138
+ (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" }),
139
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
140
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
141
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
142
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
143
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
144
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
145
+ ] },
146
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
147
+ ] }
73
148
  ] }
74
149
  ]
75
150
  })
@@ -82,19 +157,24 @@ orbital LearnSim3DOrbital {
82
157
  padding: md
83
158
  children: [
84
159
  { type: stack, direction: vertical, gap: md, children: [
85
- { type: typography, variant: h4, content: @entity.title },
86
- { type: stack, direction: horizontal, gap: sm, children: [
87
- { type: button, action: RUN, label: "Run", variant: primary },
88
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
89
- { type: button, action: RESET, label: "Reset", variant: ghost }
160
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
161
+ { type: stack, direction: vertical, gap: xs, children: [
162
+ { type: typography, variant: h4, content: @entity.title },
163
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
164
+ ] },
165
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
90
166
  ] },
91
- { type: stack, direction: horizontal, gap: sm, children: [
92
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
93
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
94
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
95
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
96
- ] },
97
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
167
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
168
+ (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" }),
169
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
170
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
171
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
172
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
173
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
174
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
175
+ ] },
176
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
177
+ ] }
98
178
  ] }
99
179
  ]
100
180
  })
@@ -107,19 +187,24 @@ orbital LearnSim3DOrbital {
107
187
  padding: md
108
188
  children: [
109
189
  { type: stack, direction: vertical, gap: md, children: [
110
- { type: typography, variant: h4, content: @entity.title },
111
- { type: stack, direction: horizontal, gap: sm, children: [
112
- { type: button, action: RUN, label: "Run", variant: primary },
113
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
114
- { type: button, action: RESET, label: "Reset", variant: ghost }
115
- ] },
116
- { type: stack, direction: horizontal, gap: sm, children: [
117
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
118
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
119
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
120
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
190
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
191
+ { type: stack, direction: vertical, gap: xs, children: [
192
+ { type: typography, variant: h4, content: @entity.title },
193
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
194
+ ] },
195
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
121
196
  ] },
122
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
197
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
198
+ (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" }),
199
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
200
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
201
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
202
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
203
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
204
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
205
+ ] },
206
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
207
+ ] }
123
208
  ] }
124
209
  ]
125
210
  })
@@ -135,19 +220,24 @@ orbital LearnSim3DOrbital {
135
220
  padding: md
136
221
  children: [
137
222
  { type: stack, direction: vertical, gap: md, children: [
138
- { type: typography, variant: h4, content: @entity.title },
139
- { type: stack, direction: horizontal, gap: sm, children: [
140
- { type: button, action: RUN, label: "Run", variant: primary },
141
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
142
- { type: button, action: RESET, label: "Reset", variant: ghost }
223
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
224
+ { type: stack, direction: vertical, gap: xs, children: [
225
+ { type: typography, variant: h4, content: @entity.title },
226
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
227
+ ] },
228
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
143
229
  ] },
144
- { type: stack, direction: horizontal, gap: sm, children: [
145
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
146
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
147
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
148
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
149
- ] },
150
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
230
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
231
+ (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" }),
232
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
233
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
234
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
235
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
236
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
237
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
238
+ ] },
239
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
240
+ ] }
151
241
  ] }
152
242
  ]
153
243
  })
@@ -160,19 +250,24 @@ orbital LearnSim3DOrbital {
160
250
  padding: md
161
251
  children: [
162
252
  { type: stack, direction: vertical, gap: md, children: [
163
- { type: typography, variant: h4, content: @entity.title },
164
- { type: stack, direction: horizontal, gap: sm, children: [
165
- { type: button, action: RUN, label: "Run", variant: primary },
166
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
167
- { type: button, action: RESET, label: "Reset", variant: ghost }
168
- ] },
169
- { type: stack, direction: horizontal, gap: sm, children: [
170
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
171
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
172
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
173
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
253
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
254
+ { type: stack, direction: vertical, gap: xs, children: [
255
+ { type: typography, variant: h4, content: @entity.title },
256
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
257
+ ] },
258
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
174
259
  ] },
175
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
260
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
261
+ (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" }),
262
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
263
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
264
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
265
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
266
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
267
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
268
+ ] },
269
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
270
+ ] }
176
271
  ] }
177
272
  ]
178
273
  })
@@ -185,19 +280,24 @@ orbital LearnSim3DOrbital {
185
280
  padding: md
186
281
  children: [
187
282
  { type: stack, direction: vertical, gap: md, children: [
188
- { type: typography, variant: h4, content: @entity.title },
189
- { type: stack, direction: horizontal, gap: sm, children: [
190
- { type: button, action: RUN, label: "Run", variant: primary },
191
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
192
- { type: button, action: RESET, label: "Reset", variant: ghost }
283
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
284
+ { type: stack, direction: vertical, gap: xs, children: [
285
+ { type: typography, variant: h4, content: @entity.title },
286
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
287
+ ] },
288
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
193
289
  ] },
194
- { type: stack, direction: horizontal, gap: sm, children: [
195
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
196
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
197
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
198
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
199
- ] },
200
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
290
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
291
+ (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" }),
292
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
293
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
294
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
295
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
296
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
297
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
298
+ ] },
299
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
300
+ ] }
201
301
  ] }
202
302
  ]
203
303
  })
@@ -210,19 +310,24 @@ orbital LearnSim3DOrbital {
210
310
  padding: md
211
311
  children: [
212
312
  { type: stack, direction: vertical, gap: md, children: [
213
- { type: typography, variant: h4, content: @entity.title },
214
- { type: stack, direction: horizontal, gap: sm, children: [
215
- { type: button, action: RUN, label: "Run", variant: primary },
216
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
217
- { type: button, action: RESET, label: "Reset", variant: ghost }
218
- ] },
219
- { type: stack, direction: horizontal, gap: sm, children: [
220
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
221
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
222
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
223
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
313
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
314
+ { type: stack, direction: vertical, gap: xs, children: [
315
+ { type: typography, variant: h4, content: @entity.title },
316
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
317
+ ] },
318
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
224
319
  ] },
225
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
320
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
321
+ (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" }),
322
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
323
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
324
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
325
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
326
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
327
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
328
+ ] },
329
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
330
+ ] }
226
331
  ] }
227
332
  ]
228
333
  })
@@ -235,19 +340,24 @@ orbital LearnSim3DOrbital {
235
340
  padding: md
236
341
  children: [
237
342
  { type: stack, direction: vertical, gap: md, children: [
238
- { type: typography, variant: h4, content: @entity.title },
239
- { type: stack, direction: horizontal, gap: sm, children: [
240
- { type: button, action: RUN, label: "Run", variant: primary },
241
- { type: button, action: PAUSE, label: "Pause", variant: secondary },
242
- { type: button, action: RESET, label: "Reset", variant: ghost }
343
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
344
+ { type: stack, direction: vertical, gap: xs, children: [
345
+ { type: typography, variant: h4, content: @entity.title },
346
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
347
+ ] },
348
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
243
349
  ] },
244
- { type: stack, direction: horizontal, gap: sm, children: [
245
- { type: button, action: SPEED_HALF, label: "0.5x", variant: ghost },
246
- { type: button, action: SPEED_NORMAL, label: "1x", variant: ghost },
247
- { type: button, action: SPEED_DOUBLE, label: "2x", variant: ghost },
248
- { type: button, action: SPEED_QUAD, label: "4x", variant: ghost }
249
- ] },
250
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps " — sim time: " (math/round @entity.simTime 1) " s — speed: " @entity.speedMultiplier "x") }
350
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
351
+ (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" }),
352
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
353
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
354
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speedMultiplier 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 0.5) "true" "false") },
355
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speedMultiplier 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 1) "true" "false") },
356
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speedMultiplier 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 2) "true" "false") },
357
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speedMultiplier 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speedMultiplier 4) "true" "false") }
358
+ ] },
359
+ { type: typography, variant: caption, color: muted, content: (str/concat "Step " @entity.steps " · " (math/round @entity.simTime 1) " s") }
360
+ ] }
251
361
  ] }
252
362
  ]
253
363
  })
@@ -268,6 +378,11 @@ orbital LearnSim3DOrbital {
268
378
  }
269
379
 
270
380
  listens {
381
+ SPEED_SET {
382
+ speed : number
383
+ }
384
+ @description "The learner picked a playback speed; the clock advances that many times faster."
385
+ @tier "presentation"
271
386
  MODE_SELECTED {
272
387
  mode : string
273
388
  }
@@ -301,7 +416,11 @@ orbital LearnSim3DOrbital {
301
416
  @tier "presentation"
302
417
  title : string = "3D Simulation"
303
418
  @label "Title"
304
- @description "Title shown above the transport controls."
419
+ @description "The instrument's name, the header's one hero."
420
+ @tier "presentation"
421
+ summary : string = ""
422
+ @label "Concept"
423
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
305
424
  @tier "presentation"
306
425
  dt : number = 0.02
307
426
  @label "Timestep"