@almadar/std 16.241.0 → 16.242.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 +134 -0
  8. package/behaviors/lolo/infra/atoms/std-page-treat.lolo +59 -0
  9. package/behaviors/lolo/infra/atoms/std-page-watch.lolo +86 -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 +867 -0
  55. package/behaviors/registry/infra/atoms/std-page-treat.orb +494 -0
  56. package/behaviors/registry/infra/atoms/std-page-watch.orb +554 -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
@@ -23,6 +23,7 @@ orbital MeasureOrbital {
23
23
  t : number = 0
24
24
  value : number = 0
25
25
  running : boolean = false
26
+ speed : number = 1
26
27
  traceSamples : [MeasureSample]
27
28
  traceCurves : [MeasureCurve]
28
29
  points : [MeasurePoint]
@@ -50,21 +51,39 @@ orbital MeasureOrbital {
50
51
  padding: md
51
52
  children: [
52
53
  { type: stack, direction: vertical, gap: md, children: [
53
- { type: typography, variant: h4, content: @entity.title },
54
- { type: typography, variant: body, content: @config.taskText },
55
- { type: stack, direction: horizontal, gap: sm, children: [
56
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
57
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
58
- { type: button, action: PROBE, label: @config.probeLabel, variant: primary, disabled: (if (> @entity.t 0) false true) },
59
- { type: button, action: RESET, label: "Reset", variant: ghost }
54
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
55
+ { type: stack, direction: vertical, gap: xs, children: [
56
+ { type: typography, variant: h4, content: @entity.title },
57
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
58
+ ] },
59
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
60
60
  ] },
61
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false },
62
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) "s · " @config.valueLabel " = " (math/round @entity.value 3)) },
63
- { type: typography, variant: caption, content: (str/concat "Samples recorded: " (str/concat "" @entity.sampleCount) " · mean " @config.valueLabel ": " (math/round @entity.meanValue 3)) }
61
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
62
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
63
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
64
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
65
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
66
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
67
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
68
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
69
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
70
+ ] },
71
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
72
+ ] },
73
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
74
+ { type: stack, direction: vertical, gap: xs, children: [
75
+ { type: typography, variant: body, content: @config.taskText },
76
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
77
+ ] }
64
78
  ] }
65
79
  ]
66
80
  })
67
81
 
82
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
83
+ (set @entity.active false)
84
+ (set @entity.running false)
85
+ (render-ui main null)
86
+
68
87
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
69
88
  (set @entity.active true)
70
89
  (set @entity.title @config.title)
@@ -81,17 +100,101 @@ orbital MeasureOrbital {
81
100
  padding: md
82
101
  children: [
83
102
  { type: stack, direction: vertical, gap: md, children: [
84
- { type: typography, variant: h4, content: @entity.title },
85
- { type: typography, variant: body, content: @config.taskText },
86
- { type: stack, direction: horizontal, gap: sm, children: [
87
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
88
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
89
- { type: button, action: PROBE, label: @config.probeLabel, variant: primary, disabled: (if (> @entity.t 0) false true) },
90
- { type: button, action: RESET, label: "Reset", variant: ghost }
103
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
104
+ { type: stack, direction: vertical, gap: xs, children: [
105
+ { type: typography, variant: h4, content: @entity.title },
106
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
107
+ ] },
108
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
109
+ ] },
110
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
111
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
112
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
113
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
114
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
115
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
116
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
117
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
118
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
119
+ ] },
120
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
121
+ ] },
122
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
123
+ { type: stack, direction: vertical, gap: xs, children: [
124
+ { type: typography, variant: body, content: @config.taskText },
125
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
126
+ ] }
127
+ ] }
128
+ ]
129
+ })
130
+
131
+ TOGGLE_RUN -> idle when (and (== @entity.active true) (or (== @entity.running true) (< @entity.t @config.duration)))
132
+ (set @entity.running (not @entity.running))
133
+ (render-ui main {
134
+ type: box
135
+ padding: md
136
+ children: [
137
+ { type: stack, direction: vertical, gap: md, children: [
138
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
139
+ { type: stack, direction: vertical, gap: xs, children: [
140
+ { type: typography, variant: h4, content: @entity.title },
141
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
142
+ ] },
143
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
144
+ ] },
145
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
146
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
147
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
148
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
149
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
150
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
151
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
152
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
153
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
154
+ ] },
155
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
156
+ ] },
157
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
158
+ { type: stack, direction: vertical, gap: xs, children: [
159
+ { type: typography, variant: body, content: @config.taskText },
160
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
161
+ ] }
162
+ ] }
163
+ ]
164
+ })
165
+ (emit RUN_TOGGLED { running: @entity.running })
166
+
167
+ SPEED_SET -> idle when (== @entity.active true)
168
+ (set @entity.speed ?speed)
169
+ (render-ui main {
170
+ type: box
171
+ padding: md
172
+ children: [
173
+ { type: stack, direction: vertical, gap: md, children: [
174
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
175
+ { type: stack, direction: vertical, gap: xs, children: [
176
+ { type: typography, variant: h4, content: @entity.title },
177
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
178
+ ] },
179
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
180
+ ] },
181
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
182
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
183
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
184
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
185
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
186
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
187
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
188
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
189
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
190
+ ] },
191
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
91
192
  ] },
92
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false },
93
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) "s · " @config.valueLabel " = " (math/round @entity.value 3)) },
94
- { type: typography, variant: caption, content: (str/concat "Samples recorded: " (str/concat "" @entity.sampleCount) " · mean " @config.valueLabel ": " (math/round @entity.meanValue 3)) }
193
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
194
+ { type: stack, direction: vertical, gap: xs, children: [
195
+ { type: typography, variant: body, content: @config.taskText },
196
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
197
+ ] }
95
198
  ] }
96
199
  ]
97
200
  })
@@ -103,17 +206,30 @@ orbital MeasureOrbital {
103
206
  padding: md
104
207
  children: [
105
208
  { type: stack, direction: vertical, gap: md, children: [
106
- { type: typography, variant: h4, content: @entity.title },
107
- { type: typography, variant: body, content: @config.taskText },
108
- { type: stack, direction: horizontal, gap: sm, children: [
109
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
110
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
111
- { type: button, action: PROBE, label: @config.probeLabel, variant: primary, disabled: (if (> @entity.t 0) false true) },
112
- { type: button, action: RESET, label: "Reset", variant: ghost }
209
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
210
+ { type: stack, direction: vertical, gap: xs, children: [
211
+ { type: typography, variant: h4, content: @entity.title },
212
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
213
+ ] },
214
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
113
215
  ] },
114
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false },
115
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) "s · " @config.valueLabel " = " (math/round @entity.value 3)) },
116
- { type: typography, variant: caption, content: (str/concat "Samples recorded: " (str/concat "" @entity.sampleCount) " · mean " @config.valueLabel ": " (math/round @entity.meanValue 3)) }
216
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
217
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
218
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
219
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
220
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
221
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
222
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
223
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
224
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
225
+ ] },
226
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
227
+ ] },
228
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
229
+ { type: stack, direction: vertical, gap: xs, children: [
230
+ { type: typography, variant: body, content: @config.taskText },
231
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
232
+ ] }
117
233
  ] }
118
234
  ]
119
235
  })
@@ -126,17 +242,30 @@ orbital MeasureOrbital {
126
242
  padding: md
127
243
  children: [
128
244
  { type: stack, direction: vertical, gap: md, children: [
129
- { type: typography, variant: h4, content: @entity.title },
130
- { type: typography, variant: body, content: @config.taskText },
131
- { type: stack, direction: horizontal, gap: sm, children: [
132
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
133
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
134
- { type: button, action: PROBE, label: @config.probeLabel, variant: primary, disabled: (if (> @entity.t 0) false true) },
135
- { type: button, action: RESET, label: "Reset", variant: ghost }
245
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
246
+ { type: stack, direction: vertical, gap: xs, children: [
247
+ { type: typography, variant: h4, content: @entity.title },
248
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
249
+ ] },
250
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
251
+ ] },
252
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
253
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
254
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
255
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
256
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
257
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
258
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
259
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
260
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
261
+ ] },
262
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
136
263
  ] },
137
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false },
138
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) "s · " @config.valueLabel " = " (math/round @entity.value 3)) },
139
- { type: typography, variant: caption, content: (str/concat "Samples recorded: " (str/concat "" @entity.sampleCount) " · mean " @config.valueLabel ": " (math/round @entity.meanValue 3)) }
264
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
265
+ { type: stack, direction: vertical, gap: xs, children: [
266
+ { type: typography, variant: body, content: @config.taskText },
267
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
268
+ ] }
140
269
  ] }
141
270
  ]
142
271
  })
@@ -151,17 +280,30 @@ orbital MeasureOrbital {
151
280
  padding: md
152
281
  children: [
153
282
  { type: stack, direction: vertical, gap: md, children: [
154
- { type: typography, variant: h4, content: @entity.title },
155
- { type: typography, variant: body, content: @config.taskText },
156
- { type: stack, direction: horizontal, gap: sm, children: [
157
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
158
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
159
- { type: button, action: PROBE, label: @config.probeLabel, variant: primary, disabled: (if (> @entity.t 0) false true) },
160
- { 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.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
289
+ ] },
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", disabled: (>= @entity.t @config.duration) }),
292
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
293
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
294
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
295
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
296
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
297
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
298
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
299
+ ] },
300
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
161
301
  ] },
162
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false },
163
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) "s · " @config.valueLabel " = " (math/round @entity.value 3)) },
164
- { type: typography, variant: caption, content: (str/concat "Samples recorded: " (str/concat "" @entity.sampleCount) " · mean " @config.valueLabel ": " (math/round @entity.meanValue 3)) }
302
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
303
+ { type: stack, direction: vertical, gap: xs, children: [
304
+ { type: typography, variant: body, content: @config.taskText },
305
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
306
+ ] }
165
307
  ] }
166
308
  ]
167
309
  })
@@ -181,17 +323,30 @@ orbital MeasureOrbital {
181
323
  padding: md
182
324
  children: [
183
325
  { type: stack, direction: vertical, gap: md, children: [
184
- { type: typography, variant: h4, content: @entity.title },
185
- { type: typography, variant: body, content: @config.taskText },
186
- { type: stack, direction: horizontal, gap: sm, children: [
187
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
188
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
189
- { type: button, action: PROBE, label: @config.probeLabel, variant: primary, disabled: (if (> @entity.t 0) false true) },
190
- { type: button, action: RESET, label: "Reset", variant: ghost }
326
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
327
+ { type: stack, direction: vertical, gap: xs, children: [
328
+ { type: typography, variant: h4, content: @entity.title },
329
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
330
+ ] },
331
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Measuring…" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
191
332
  ] },
192
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false },
193
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) "s · " @config.valueLabel " = " (math/round @entity.value 3)) },
194
- { type: typography, variant: caption, content: (str/concat "Samples recorded: " (str/concat "" @entity.sampleCount) " · mean " @config.valueLabel ": " (math/round @entity.meanValue 3)) }
333
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
334
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: (>= @entity.t @config.duration) }),
335
+ { type: button, action: PROBE, label: @config.probeLabel, icon: "plus", variant: secondary, disabled: (<= @entity.t 0) },
336
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
337
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
338
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
339
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
340
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
341
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
342
+ ] },
343
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration " s") }
344
+ ] },
345
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: @config.valueMin, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.traceCurves, points: @entity.points, vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, Enter: PROBE, KeyR: RESET } },
346
+ { type: stack, direction: vertical, gap: xs, children: [
347
+ { type: typography, variant: body, content: @config.taskText },
348
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.valueLabel " = " (math/round @entity.value 3) " · " @entity.sampleCount " recorded · mean " (math/round @entity.meanValue 3)) }
349
+ ] }
195
350
  ] }
196
351
  ]
197
352
  })
@@ -201,7 +356,7 @@ orbital MeasureOrbital {
201
356
  ticks {
202
357
  measureTick every 100ms
203
358
  when (and (== @entity.active true) (== @entity.running true))
204
- (set @entity.t (math/min @config.duration (+ @entity.t 0.1)))
359
+ (set @entity.t (math/min @config.duration (+ @entity.t (* 0.1 @entity.speed))))
205
360
  (set @entity.value (if (== @config.signalKind "sine")
206
361
  (* @config.amplitude (math/sin (* @config.rate @entity.t)))
207
362
  (if (== @config.signalKind "growth")
@@ -218,6 +373,15 @@ orbital MeasureOrbital {
218
373
  }
219
374
  @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
220
375
  @tier "domain"
376
+ TOGGLE_RUN {
377
+ }
378
+ @description "Learner pressed the Play/Pause toggle — starts or freezes the signal clock."
379
+ @tier "domain"
380
+ SPEED_SET {
381
+ speed : number
382
+ }
383
+ @description "The learner picked a playback speed; the signal clock advances that many times faster."
384
+ @tier "presentation"
221
385
  RUN {
222
386
  }
223
387
  @description "Learner started (or resumed) the signal's evolution; the trace draws live until Pause or the configured duration."
@@ -265,7 +429,11 @@ orbital MeasureOrbital {
265
429
  @tier "presentation"
266
430
  title : string = "Measurement Lab"
267
431
  @label "Title"
268
- @description "Title shown above the experiment."
432
+ @description "The instrument's name, the header's one hero."
433
+ @tier "presentation"
434
+ summary : string = ""
435
+ @label "Concept"
436
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
269
437
  @tier "presentation"
270
438
  taskText : string = "Run the signal, then press Probe to record measurements. Watch the running mean settle."
271
439
  @label "Task Text"
@@ -306,10 +474,10 @@ orbital MeasureOrbital {
306
474
  probeLabel : string = "Probe"
307
475
  @label "Probe Label"
308
476
  @tier "presentation"
309
- signalColor : string = "#94a3b8"
477
+ signalColor : string = "ink"
310
478
  @label "Signal Color"
311
479
  @tier "presentation"
312
- probeColor : string = "#dc2626"
480
+ probeColor : string = "highlight"
313
481
  @label "Probe Color"
314
482
  @tier "presentation"
315
483
  width : number = 600
@@ -75,21 +75,33 @@ orbital ParamExplorerOrbital {
75
75
  padding: md
76
76
  children: [
77
77
  { type: stack, direction: vertical, gap: md, children: [
78
- { type: typography, variant: h4, content: @entity.title },
79
- { type: stack, direction: vertical, gap: xs, children: [
80
- { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
81
- { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
78
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
79
+ { type: stack, direction: vertical, gap: xs, children: [
80
+ { type: typography, variant: h4, content: @entity.title },
81
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
82
+ ] },
83
+ { type: badge, label: (str/concat "y = " (if (== @entity.curveKind "power") "A·xᴮ" (if (== @entity.curveKind "linear") "A + B·x" "A·sin(B·x)"))), variant: neutral }
82
84
  ] },
85
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
86
+ { type: stack, direction: vertical, gap: sm, children: [
83
87
  { type: stack, direction: vertical, gap: xs, children: [
84
- { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
85
- { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
88
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
89
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
90
+ ] },
91
+ { type: stack, direction: vertical, gap: xs, children: [
92
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
93
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
94
+ ] }
86
95
  ] },
87
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
88
96
  { type: typography, variant: body, content: (str/concat @entity.paramALabel "=" (math/round @entity.paramA 2) ", " @entity.paramBLabel "=" (math/round @entity.paramB 2) " — at x=" (math/round @entity.xMid 2) ", y=" (math/round @entity.yMid 3)) }
89
97
  ] }
90
98
  ]
91
99
  })
92
100
 
101
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
102
+ (set @entity.active false)
103
+ (render-ui main null)
104
+
93
105
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
94
106
  (let (
95
107
  (A @config.paramADefault)
@@ -134,16 +146,24 @@ orbital ParamExplorerOrbital {
134
146
  padding: md
135
147
  children: [
136
148
  { type: stack, direction: vertical, gap: md, children: [
137
- { type: typography, variant: h4, content: @entity.title },
138
- { type: stack, direction: vertical, gap: xs, children: [
139
- { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
140
- { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
149
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
150
+ { type: stack, direction: vertical, gap: xs, children: [
151
+ { type: typography, variant: h4, content: @entity.title },
152
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
153
+ ] },
154
+ { type: badge, label: (str/concat "y = " (if (== @entity.curveKind "power") "A·xᴮ" (if (== @entity.curveKind "linear") "A + B·x" "A·sin(B·x)"))), variant: neutral }
141
155
  ] },
156
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
157
+ { type: stack, direction: vertical, gap: sm, children: [
142
158
  { type: stack, direction: vertical, gap: xs, children: [
143
- { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
144
- { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
159
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
160
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
161
+ ] },
162
+ { type: stack, direction: vertical, gap: xs, children: [
163
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
164
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
165
+ ] }
145
166
  ] },
146
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
147
167
  { type: typography, variant: body, content: (str/concat @entity.paramALabel "=" (math/round @entity.paramA 2) ", " @entity.paramBLabel "=" (math/round @entity.paramB 2) " — at x=" (math/round @entity.xMid 2) ", y=" (math/round @entity.yMid 3)) }
148
168
  ] }
149
169
  ]
@@ -185,16 +205,24 @@ orbital ParamExplorerOrbital {
185
205
  padding: md
186
206
  children: [
187
207
  { type: stack, direction: vertical, gap: md, children: [
188
- { type: typography, variant: h4, content: @entity.title },
189
- { type: stack, direction: vertical, gap: xs, children: [
190
- { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
191
- { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
208
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
209
+ { type: stack, direction: vertical, gap: xs, children: [
210
+ { type: typography, variant: h4, content: @entity.title },
211
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
212
+ ] },
213
+ { type: badge, label: (str/concat "y = " (if (== @entity.curveKind "power") "A·xᴮ" (if (== @entity.curveKind "linear") "A + B·x" "A·sin(B·x)"))), variant: neutral }
192
214
  ] },
215
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
216
+ { type: stack, direction: vertical, gap: sm, children: [
193
217
  { type: stack, direction: vertical, gap: xs, children: [
194
- { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
195
- { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
218
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
219
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
220
+ ] },
221
+ { type: stack, direction: vertical, gap: xs, children: [
222
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
223
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
224
+ ] }
196
225
  ] },
197
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
198
226
  { type: typography, variant: body, content: (str/concat @entity.paramALabel "=" (math/round @entity.paramA 2) ", " @entity.paramBLabel "=" (math/round @entity.paramB 2) " — at x=" (math/round @entity.xMid 2) ", y=" (math/round @entity.yMid 3)) }
199
227
  ] }
200
228
  ]
@@ -237,16 +265,24 @@ orbital ParamExplorerOrbital {
237
265
  padding: md
238
266
  children: [
239
267
  { type: stack, direction: vertical, gap: md, children: [
240
- { type: typography, variant: h4, content: @entity.title },
241
- { type: stack, direction: vertical, gap: xs, children: [
242
- { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
243
- { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
268
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
269
+ { type: stack, direction: vertical, gap: xs, children: [
270
+ { type: typography, variant: h4, content: @entity.title },
271
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
272
+ ] },
273
+ { type: badge, label: (str/concat "y = " (if (== @entity.curveKind "power") "A·xᴮ" (if (== @entity.curveKind "linear") "A + B·x" "A·sin(B·x)"))), variant: neutral }
244
274
  ] },
275
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
276
+ { type: stack, direction: vertical, gap: sm, children: [
245
277
  { type: stack, direction: vertical, gap: xs, children: [
246
- { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
247
- { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
278
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
279
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
280
+ ] },
281
+ { type: stack, direction: vertical, gap: xs, children: [
282
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
283
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
284
+ ] }
248
285
  ] },
249
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: @config.interactive, animate: @config.animate },
250
286
  { type: typography, variant: body, content: (str/concat @entity.paramALabel "=" (math/round @entity.paramA 2) ", " @entity.paramBLabel "=" (math/round @entity.paramB 2) " — at x=" (math/round @entity.xMid 2) ", y=" (math/round @entity.yMid 3)) }
251
287
  ] }
252
288
  ]
@@ -291,6 +327,10 @@ orbital ParamExplorerOrbital {
291
327
  @label "Title"
292
328
  @description "Title shown above the canvas."
293
329
  @tier "presentation"
330
+ summary : string = ""
331
+ @label "Concept"
332
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
333
+ @tier "presentation"
294
334
  curveKind : string = "power"
295
335
  @label "Curve Kind"
296
336
  @description "Curve family the two parameters drive: power (y = A·xᴮ), linear (y = A + B·x), or sine (y = A·sin(B·x))."
@@ -344,7 +384,7 @@ orbital ParamExplorerOrbital {
344
384
  @label "X Max"
345
385
  @description "Right edge of the sampled x range."
346
386
  @tier "presentation"
347
- color : string = "#3b82f6"
387
+ color : string = "series-2"
348
388
  @label "Curve Color"
349
389
  @tier "presentation"
350
390
  width : number = 600