@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
@@ -21,6 +21,7 @@ orbital CoupledSimOrbital {
21
21
  coefA : number = 0
22
22
  coefB : number = 0
23
23
  running : boolean = false
24
+ speed : number = 1
24
25
  pSamples : [CoupledSample]
25
26
  qSamples : [CoupledSample]
26
27
  curves : [CoupledCurve]
@@ -50,29 +51,50 @@ orbital CoupledSimOrbital {
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: 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 "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
60
+ ] },
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: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
59
64
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
60
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
65
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
66
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
67
+ { 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") },
68
+ { 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") },
69
+ { 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") },
70
+ { 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") }
71
+ ] },
72
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
61
73
  ] },
62
- { type: stack, direction: vertical, gap: xs, children: [
63
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
64
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
74
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
75
+ { type: stack, direction: vertical, gap: sm, children: [
76
+ { type: stack, direction: vertical, gap: xs, children: [
77
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
78
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
79
+ ] },
80
+ { type: stack, direction: vertical, gap: xs, children: [
81
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
82
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
83
+ ] }
65
84
  ] },
66
85
  { type: stack, direction: vertical, gap: xs, children: [
67
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
68
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
69
- ] },
70
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
71
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
86
+ { type: typography, variant: body, content: @config.taskText },
87
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
88
+ ] }
72
89
  ] }
73
90
  ]
74
91
  })
75
92
 
93
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
94
+ (set @entity.active false)
95
+ (set @entity.running false)
96
+ (render-ui main null)
97
+
76
98
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
77
99
  (set @entity.active true)
78
100
  (set @entity.title @config.title)
@@ -93,25 +115,134 @@ orbital CoupledSimOrbital {
93
115
  padding: md
94
116
  children: [
95
117
  { type: stack, direction: vertical, gap: md, children: [
96
- { type: typography, variant: h4, content: @entity.title },
97
- { type: typography, variant: body, content: @config.taskText },
98
- { type: stack, direction: horizontal, gap: sm, children: [
99
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
100
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
101
- { type: button, action: RESET, label: "Reset", variant: ghost },
118
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
119
+ { type: stack, direction: vertical, gap: xs, children: [
120
+ { type: typography, variant: h4, content: @entity.title },
121
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
122
+ ] },
123
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
124
+ ] },
125
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
126
+ (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) }),
127
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
102
128
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
103
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
129
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
130
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
131
+ { 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") },
132
+ { 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") },
133
+ { 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") },
134
+ { 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") }
135
+ ] },
136
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
137
+ ] },
138
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
139
+ { type: stack, direction: vertical, gap: sm, children: [
140
+ { type: stack, direction: vertical, gap: xs, children: [
141
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
142
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
143
+ ] },
144
+ { type: stack, direction: vertical, gap: xs, children: [
145
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
146
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
147
+ ] }
104
148
  ] },
105
149
  { type: stack, direction: vertical, gap: xs, children: [
106
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
107
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
150
+ { type: typography, variant: body, content: @config.taskText },
151
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
152
+ ] }
153
+ ] }
154
+ ]
155
+ })
156
+
157
+ TOGGLE_RUN -> idle when (and (== @entity.active true) (or (== @entity.running true) (< @entity.t @config.duration)))
158
+ (set @entity.running (not @entity.running))
159
+ (render-ui main {
160
+ type: box
161
+ padding: md
162
+ children: [
163
+ { type: stack, direction: vertical, gap: md, children: [
164
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
165
+ { type: stack, direction: vertical, gap: xs, children: [
166
+ { type: typography, variant: h4, content: @entity.title },
167
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
168
+ ] },
169
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
170
+ ] },
171
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
172
+ (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) }),
173
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
174
+ { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
175
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
176
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
177
+ { 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") },
178
+ { 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") },
179
+ { 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") },
180
+ { 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") }
181
+ ] },
182
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
183
+ ] },
184
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
185
+ { type: stack, direction: vertical, gap: sm, children: [
186
+ { type: stack, direction: vertical, gap: xs, children: [
187
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
188
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
189
+ ] },
190
+ { type: stack, direction: vertical, gap: xs, children: [
191
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
192
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
193
+ ] }
108
194
  ] },
109
195
  { type: stack, direction: vertical, gap: xs, children: [
110
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
111
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
196
+ { type: typography, variant: body, content: @config.taskText },
197
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
198
+ ] }
199
+ ] }
200
+ ]
201
+ })
202
+ (emit RUN_TOGGLED { running: @entity.running })
203
+
204
+ SPEED_SET -> idle when (== @entity.active true)
205
+ (set @entity.speed ?speed)
206
+ (render-ui main {
207
+ type: box
208
+ padding: md
209
+ children: [
210
+ { type: stack, direction: vertical, gap: md, children: [
211
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
212
+ { type: stack, direction: vertical, gap: xs, children: [
213
+ { type: typography, variant: h4, content: @entity.title },
214
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
215
+ ] },
216
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
217
+ ] },
218
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
219
+ (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) }),
220
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
221
+ { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
222
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
223
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
224
+ { 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") },
225
+ { 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") },
226
+ { 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") },
227
+ { 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") }
228
+ ] },
229
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
230
+ ] },
231
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
232
+ { type: stack, direction: vertical, gap: sm, children: [
233
+ { type: stack, direction: vertical, gap: xs, children: [
234
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
235
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
236
+ ] },
237
+ { type: stack, direction: vertical, gap: xs, children: [
238
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
239
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
240
+ ] }
112
241
  ] },
113
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
114
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
242
+ { type: stack, direction: vertical, gap: xs, children: [
243
+ { type: typography, variant: body, content: @config.taskText },
244
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
245
+ ] }
115
246
  ] }
116
247
  ]
117
248
  })
@@ -123,25 +254,41 @@ orbital CoupledSimOrbital {
123
254
  padding: md
124
255
  children: [
125
256
  { type: stack, direction: vertical, gap: md, children: [
126
- { type: typography, variant: h4, content: @entity.title },
127
- { type: typography, variant: body, content: @config.taskText },
128
- { type: stack, direction: horizontal, gap: sm, children: [
129
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
130
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
131
- { type: button, action: RESET, label: "Reset", variant: ghost },
257
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
258
+ { type: stack, direction: vertical, gap: xs, children: [
259
+ { type: typography, variant: h4, content: @entity.title },
260
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
261
+ ] },
262
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
263
+ ] },
264
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
265
+ (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) }),
266
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
132
267
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
133
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
268
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
269
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
270
+ { 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") },
271
+ { 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") },
272
+ { 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") },
273
+ { 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") }
274
+ ] },
275
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
134
276
  ] },
135
- { type: stack, direction: vertical, gap: xs, children: [
136
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
137
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
277
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
278
+ { type: stack, direction: vertical, gap: sm, children: [
279
+ { type: stack, direction: vertical, gap: xs, children: [
280
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
281
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
282
+ ] },
283
+ { type: stack, direction: vertical, gap: xs, children: [
284
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
285
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
286
+ ] }
138
287
  ] },
139
288
  { type: stack, direction: vertical, gap: xs, children: [
140
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
141
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
142
- ] },
143
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
144
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
289
+ { type: typography, variant: body, content: @config.taskText },
290
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
291
+ ] }
145
292
  ] }
146
293
  ]
147
294
  })
@@ -154,25 +301,41 @@ orbital CoupledSimOrbital {
154
301
  padding: md
155
302
  children: [
156
303
  { type: stack, direction: vertical, gap: md, children: [
157
- { type: typography, variant: h4, content: @entity.title },
158
- { type: typography, variant: body, content: @config.taskText },
159
- { type: stack, direction: horizontal, gap: sm, children: [
160
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
161
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
162
- { type: button, action: RESET, label: "Reset", variant: ghost },
304
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
305
+ { type: stack, direction: vertical, gap: xs, children: [
306
+ { type: typography, variant: h4, content: @entity.title },
307
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
308
+ ] },
309
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
310
+ ] },
311
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
312
+ (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) }),
313
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
163
314
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
164
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
315
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
316
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
317
+ { 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") },
318
+ { 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") },
319
+ { 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") },
320
+ { 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") }
321
+ ] },
322
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
165
323
  ] },
166
- { type: stack, direction: vertical, gap: xs, children: [
167
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
168
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
324
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
325
+ { type: stack, direction: vertical, gap: sm, children: [
326
+ { type: stack, direction: vertical, gap: xs, children: [
327
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
328
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
329
+ ] },
330
+ { type: stack, direction: vertical, gap: xs, children: [
331
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
332
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
333
+ ] }
169
334
  ] },
170
335
  { type: stack, direction: vertical, gap: xs, children: [
171
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
172
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
173
- ] },
174
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
175
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
336
+ { type: typography, variant: body, content: @config.taskText },
337
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
338
+ ] }
176
339
  ] }
177
340
  ]
178
341
  })
@@ -196,25 +359,41 @@ orbital CoupledSimOrbital {
196
359
  padding: md
197
360
  children: [
198
361
  { type: stack, direction: vertical, gap: md, children: [
199
- { type: typography, variant: h4, content: @entity.title },
200
- { type: typography, variant: body, content: @config.taskText },
201
- { type: stack, direction: horizontal, gap: sm, children: [
202
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
203
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
204
- { type: button, action: RESET, label: "Reset", variant: ghost },
362
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
363
+ { type: stack, direction: vertical, gap: xs, children: [
364
+ { type: typography, variant: h4, content: @entity.title },
365
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
366
+ ] },
367
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
368
+ ] },
369
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
370
+ (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) }),
371
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
205
372
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
206
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
373
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
374
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
375
+ { 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") },
376
+ { 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") },
377
+ { 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") },
378
+ { 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") }
379
+ ] },
380
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
207
381
  ] },
208
- { type: stack, direction: vertical, gap: xs, children: [
209
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
210
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
382
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
383
+ { type: stack, direction: vertical, gap: sm, children: [
384
+ { type: stack, direction: vertical, gap: xs, children: [
385
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
386
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
387
+ ] },
388
+ { type: stack, direction: vertical, gap: xs, children: [
389
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
390
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
391
+ ] }
211
392
  ] },
212
393
  { type: stack, direction: vertical, gap: xs, children: [
213
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
214
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
215
- ] },
216
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
217
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
394
+ { type: typography, variant: body, content: @config.taskText },
395
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
396
+ ] }
218
397
  ] }
219
398
  ]
220
399
  })
@@ -227,25 +406,41 @@ orbital CoupledSimOrbital {
227
406
  padding: md
228
407
  children: [
229
408
  { type: stack, direction: vertical, gap: md, children: [
230
- { type: typography, variant: h4, content: @entity.title },
231
- { type: typography, variant: body, content: @config.taskText },
232
- { type: stack, direction: horizontal, gap: sm, children: [
233
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
234
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
235
- { type: button, action: RESET, label: "Reset", variant: ghost },
409
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
410
+ { type: stack, direction: vertical, gap: xs, children: [
411
+ { type: typography, variant: h4, content: @entity.title },
412
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
413
+ ] },
414
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
415
+ ] },
416
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
417
+ (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) }),
418
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
236
419
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
237
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
420
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
421
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
422
+ { 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") },
423
+ { 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") },
424
+ { 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") },
425
+ { 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") }
426
+ ] },
427
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
238
428
  ] },
239
- { type: stack, direction: vertical, gap: xs, children: [
240
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
241
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
429
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
430
+ { type: stack, direction: vertical, gap: sm, children: [
431
+ { type: stack, direction: vertical, gap: xs, children: [
432
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
433
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
434
+ ] },
435
+ { type: stack, direction: vertical, gap: xs, children: [
436
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
437
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
438
+ ] }
242
439
  ] },
243
440
  { type: stack, direction: vertical, gap: xs, children: [
244
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
245
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
246
- ] },
247
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
248
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
441
+ { type: typography, variant: body, content: @config.taskText },
442
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
443
+ ] }
249
444
  ] }
250
445
  ]
251
446
  })
@@ -258,25 +453,41 @@ orbital CoupledSimOrbital {
258
453
  padding: md
259
454
  children: [
260
455
  { type: stack, direction: vertical, gap: md, children: [
261
- { type: typography, variant: h4, content: @entity.title },
262
- { type: typography, variant: body, content: @config.taskText },
263
- { type: stack, direction: horizontal, gap: sm, children: [
264
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
265
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
266
- { type: button, action: RESET, label: "Reset", variant: ghost },
456
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
457
+ { type: stack, direction: vertical, gap: xs, children: [
458
+ { type: typography, variant: h4, content: @entity.title },
459
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
460
+ ] },
461
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
462
+ ] },
463
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
464
+ (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) }),
465
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
267
466
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
268
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
467
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
468
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
469
+ { 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") },
470
+ { 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") },
471
+ { 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") },
472
+ { 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") }
473
+ ] },
474
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
269
475
  ] },
270
- { type: stack, direction: vertical, gap: xs, children: [
271
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
272
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
476
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
477
+ { type: stack, direction: vertical, gap: sm, children: [
478
+ { type: stack, direction: vertical, gap: xs, children: [
479
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
480
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
481
+ ] },
482
+ { type: stack, direction: vertical, gap: xs, children: [
483
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
484
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
485
+ ] }
273
486
  ] },
274
487
  { type: stack, direction: vertical, gap: xs, children: [
275
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
276
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
277
- ] },
278
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
279
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
488
+ { type: typography, variant: body, content: @config.taskText },
489
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
490
+ ] }
280
491
  ] }
281
492
  ]
282
493
  })
@@ -294,25 +505,41 @@ orbital CoupledSimOrbital {
294
505
  padding: md
295
506
  children: [
296
507
  { type: stack, direction: vertical, gap: md, children: [
297
- { type: typography, variant: h4, content: @entity.title },
298
- { type: typography, variant: body, content: @config.taskText },
299
- { type: stack, direction: horizontal, gap: sm, children: [
300
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if (and (== @entity.running false) (< @entity.t @config.duration)) false true) },
301
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
302
- { type: button, action: RESET, label: "Reset", variant: ghost },
508
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
509
+ { type: stack, direction: vertical, gap: xs, children: [
510
+ { type: typography, variant: h4, content: @entity.title },
511
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
512
+ ] },
513
+ { type: badge, label: (if (>= @entity.t @config.duration) "Done" (if @entity.running "Running" "Paused")), variant: (if (>= @entity.t @config.duration) "success" (if @entity.running "primary" "neutral")) }
514
+ ] },
515
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
516
+ (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) }),
517
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
303
518
  { type: button, action: SHOCK, actionPayload: { delta: @config.shockSize }, label: @config.shockUpLabel, variant: secondary, size: sm },
304
- { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm }
519
+ { type: button, action: SHOCK, actionPayload: { delta: (- 0 @config.shockSize) }, label: @config.shockDownLabel, variant: secondary, size: sm },
520
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
521
+ { 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") },
522
+ { 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") },
523
+ { 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") },
524
+ { 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") }
525
+ ] },
526
+ { type: typography, variant: caption, color: muted, content: (str/concat "t = " (math/round @entity.t 1) " of " @config.duration) }
305
527
  ] },
306
- { type: stack, direction: vertical, gap: xs, children: [
307
- { type: typography, variant: caption, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
308
- { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
528
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false, keyMap: { Space: TOGGLE_RUN, KeyR: RESET } },
529
+ { type: stack, direction: vertical, gap: sm, children: [
530
+ { type: stack, direction: vertical, gap: xs, children: [
531
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefALabel ": " (math/round @entity.coefA 3)) },
532
+ { type: range-slider, min: @config.coefAMin, max: @config.coefAMax, step: @config.coefAStep, value: @entity.coefA, showTooltip: true, action: "SET_A" }
533
+ ] },
534
+ { type: stack, direction: vertical, gap: xs, children: [
535
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
536
+ { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
537
+ ] }
309
538
  ] },
310
539
  { type: stack, direction: vertical, gap: xs, children: [
311
- { type: typography, variant: caption, content: (str/concat @config.coefBLabel ": " (math/round @entity.coefB 3)) },
312
- { type: range-slider, min: @config.coefBMin, max: @config.coefBMax, step: @config.coefBStep, value: @entity.coefB, showTooltip: true, action: "SET_B" }
313
- ] },
314
- { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: 0, xMax: @config.duration, yMin: 0, yMax: @config.valueMax, showAxes: true, showGrid: true, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
315
- { type: typography, variant: body, content: (str/concat "t = " (math/round @entity.t 1) " · " @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
540
+ { type: typography, variant: body, content: @config.taskText },
541
+ { type: typography, variant: caption, color: muted, content: (str/concat @config.pLabel " = " (math/round @entity.p 1) " · " @config.qLabel " = " (math/round @entity.q 1)) }
542
+ ] }
316
543
  ] }
317
544
  ]
318
545
  })
@@ -323,7 +550,7 @@ orbital CoupledSimOrbital {
323
550
  coupledTick every 100ms
324
551
  when (and (== @entity.active true) (and (== @entity.running true) (< @entity.t @config.duration)))
325
552
  (let (
326
- (dt (* 0.1 @config.timeScale))
553
+ (dt (* 0.1 (* @config.timeScale @entity.speed)))
327
554
  (a @entity.coefA)
328
555
  (b @entity.coefB)
329
556
  (P @entity.p)
@@ -342,7 +569,7 @@ orbital CoupledSimOrbital {
342
569
  (newQ (math/max 0 (+ @Q (* @dQ @dt))))
343
570
  )
344
571
  (do
345
- (set @entity.t (math/min @config.duration (+ @entity.t 0.1)))
572
+ (set @entity.t (math/min @config.duration (+ @entity.t (* 0.1 @entity.speed))))
346
573
  (set @entity.p @newP)
347
574
  (set @entity.q @newQ)
348
575
  (set @entity.pSamples (array/append @entity.pSamples { x: @entity.t, y: @newP }))
@@ -360,6 +587,15 @@ orbital CoupledSimOrbital {
360
587
  }
361
588
  @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
362
589
  @tier "domain"
590
+ TOGGLE_RUN {
591
+ }
592
+ @description "Learner pressed the Play/Pause toggle — starts or freezes the coupled evolution."
593
+ @tier "domain"
594
+ SPEED_SET {
595
+ speed : number
596
+ }
597
+ @description "The learner picked a playback speed; the system evolves that many times faster."
598
+ @tier "presentation"
363
599
  RUN {
364
600
  }
365
601
  @description "Learner started (or resumed) the coupled evolution; the two curves draw live until Pause or the configured duration."
@@ -424,7 +660,11 @@ orbital CoupledSimOrbital {
424
660
  @tier "presentation"
425
661
  title : string = "Coupled System"
426
662
  @label "Title"
427
- @description "Title shown above the simulation."
663
+ @description "The instrument's name, the header's one hero."
664
+ @tier "presentation"
665
+ summary : string = ""
666
+ @label "Concept"
667
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
428
668
  @tier "presentation"
429
669
  taskText : string = "Run the system, retune the rates, and shock it — watch how the two quantities push and pull each other."
430
670
  @label "Task Text"
@@ -514,10 +754,10 @@ orbital CoupledSimOrbital {
514
754
  gridStep : number = 50
515
755
  @label "Grid Step"
516
756
  @tier "presentation"
517
- pColor : string = "#16a34a"
757
+ pColor : string = "series-1"
518
758
  @label "P Color"
519
759
  @tier "presentation"
520
- qColor : string = "#dc2626"
760
+ qColor : string = "series-2"
521
761
  @label "Q Color"
522
762
  @tier "presentation"
523
763
  autoRun : boolean = true