@almadar/std 16.173.0 → 16.174.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 (203) hide show
  1. package/behaviors/lolo/infra/atoms/std-cross-reference.lolo +2 -4
  2. package/behaviors/lolo/ui/core/atoms/std-board.lolo +5 -9
  3. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +8 -1
  4. package/behaviors/lolo/ui/core/atoms/std-esign-flow.lolo +2 -2
  5. package/behaviors/lolo/ui/core/atoms/std-event-log.lolo +3 -7
  6. package/behaviors/lolo/ui/core/atoms/std-form-advanced.lolo +1 -1
  7. package/behaviors/lolo/ui/core/atoms/std-image-upload-multi.lolo +1 -1
  8. package/behaviors/lolo/ui/core/atoms/std-list.lolo +3 -3
  9. package/behaviors/lolo/ui/core/atoms/std-modal.lolo +2 -2
  10. package/behaviors/lolo/ui/core/atoms/std-multi-party-flow.lolo +2 -4
  11. package/behaviors/lolo/ui/core/atoms/std-rating-review.lolo +5 -13
  12. package/behaviors/lolo/ui/core/atoms/std-recurrence.lolo +3 -7
  13. package/behaviors/lolo/ui/core/atoms/std-saved-search.lolo +1 -1
  14. package/behaviors/lolo/ui/core/atoms/std-signature-capture.lolo +2 -2
  15. package/behaviors/lolo/ui/core/atoms/std-step-flow.lolo +2 -4
  16. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +1 -1
  17. package/behaviors/lolo/ui/core/atoms/std-thread.lolo +2 -2
  18. package/behaviors/lolo/ui/core/atoms/std-vote.lolo +2 -2
  19. package/behaviors/lolo/ui/core/atoms/std-wizard.lolo +3 -7
  20. package/behaviors/lolo/ui/core/atoms/ui-atlas-image.lolo +1 -1
  21. package/behaviors/lolo/ui/core/atoms/ui-atlas-panel.lolo +1 -1
  22. package/behaviors/lolo/ui/core/atoms/ui-center.lolo +1 -1
  23. package/behaviors/lolo/ui/core/atoms/ui-hstack.lolo +1 -1
  24. package/behaviors/lolo/ui/core/atoms/ui-icon.lolo +1 -1
  25. package/behaviors/lolo/ui/core/atoms/ui-stack.lolo +1 -1
  26. package/behaviors/lolo/ui/core/atoms/ui-typography.lolo +1 -1
  27. package/behaviors/lolo/ui/core/atoms/ui-vstack.lolo +1 -1
  28. package/behaviors/lolo/ui/core/molecules/ui-content-renderer.lolo +1 -1
  29. package/behaviors/lolo/ui/core/molecules/ui-form-layout.lolo +1 -1
  30. package/behaviors/lolo/ui/core/molecules/ui-grid.lolo +1 -1
  31. package/behaviors/lolo/ui/core/organisms/ui-chat-bar.lolo +1 -1
  32. package/behaviors/lolo/ui/core/organisms/ui-dashboard-grid.lolo +1 -1
  33. package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +1 -1
  34. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +1 -1
  35. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +1 -1
  36. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +1 -1
  37. package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +9 -3
  38. package/behaviors/lolo/ui/core/organisms/ui-form.lolo +9 -3
  39. package/behaviors/lolo/ui/core/organisms/ui-master-detail.lolo +1 -1
  40. package/behaviors/lolo/ui/core/organisms/ui-media-gallery.lolo +1 -1
  41. package/behaviors/lolo/ui/core/organisms/ui-segment-renderer.lolo +7 -1
  42. package/behaviors/lolo/ui/core/organisms/ui-stat-card.lolo +1 -1
  43. package/behaviors/lolo/ui/core/organisms/ui-subagent-trace-panel.lolo +1 -1
  44. package/behaviors/lolo/ui/game/atoms/ui-action-tile.lolo +1 -1
  45. package/behaviors/lolo/ui/game/atoms/ui-draw-shape-layer.lolo +41 -9
  46. package/behaviors/lolo/ui/game/atoms/ui-draw-shape.lolo +40 -8
  47. package/behaviors/lolo/ui/game/atoms/ui-draw-sprite-layer.lolo +13 -3
  48. package/behaviors/lolo/ui/game/atoms/ui-draw-sprite.lolo +12 -2
  49. package/behaviors/lolo/ui/game/atoms/ui-draw-text-layer.lolo +3 -3
  50. package/behaviors/lolo/ui/game/atoms/ui-draw-text.lolo +2 -2
  51. package/behaviors/lolo/ui/game/organisms/std-tactics-board-3d.lolo +32 -2
  52. package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +1 -1
  53. package/behaviors/lolo/ui/learning/atoms/std-learn-compare.lolo +397 -0
  54. package/behaviors/lolo/ui/learning/atoms/std-learn-coupled-sim.lolo +537 -0
  55. package/behaviors/lolo/ui/learning/atoms/std-learn-estimate.lolo +419 -0
  56. package/behaviors/lolo/ui/learning/atoms/std-learn-goal-seek.lolo +591 -0
  57. package/behaviors/lolo/ui/learning/atoms/std-learn-grid-sim.lolo +425 -0
  58. package/behaviors/lolo/ui/learning/atoms/std-learn-hotspot.lolo +398 -0
  59. package/behaviors/lolo/ui/learning/atoms/std-learn-measure.lolo +325 -0
  60. package/behaviors/lolo/ui/learning/atoms/std-learn-perturb.lolo +494 -0
  61. package/behaviors/lolo/ui/learning/atoms/std-learn-predict.lolo +408 -0
  62. package/behaviors/lolo/ui/learning/atoms/std-learn-scenario.lolo +307 -0
  63. package/behaviors/lolo/ui/learning/atoms/std-learn-sim-3d.lolo +318 -0
  64. package/behaviors/lolo/ui/learning/atoms/std-learn-speed-drill.lolo +336 -0
  65. package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +380 -0
  66. package/behaviors/lolo/ui/learning/atoms/ui-biology-canvas.lolo +43 -4
  67. package/behaviors/lolo/ui/learning/atoms/ui-chemistry-canvas.lolo +41 -4
  68. package/behaviors/lolo/ui/learning/atoms/ui-physics-canvas.lolo +43 -4
  69. package/behaviors/lolo/ui/marketing/organisms/ui-book-viewer.lolo +1 -1
  70. package/behaviors/lolo/ui/marketing/organisms/ui-case-study-organism.lolo +1 -1
  71. package/behaviors/lolo/ui/marketing/organisms/ui-feature-grid-organism.lolo +1 -1
  72. package/behaviors/lolo/ui/marketing/organisms/ui-hero-organism.lolo +1 -1
  73. package/behaviors/lolo/ui/marketing/organisms/ui-pricing-organism.lolo +1 -1
  74. package/behaviors/lolo/ui/marketing/organisms/ui-showcase-organism.lolo +1 -1
  75. package/behaviors/lolo/ui/marketing/organisms/ui-stats-organism.lolo +1 -1
  76. package/behaviors/lolo/ui/marketing/organisms/ui-step-flow-organism.lolo +1 -1
  77. package/behaviors/lolo/ui/marketing/organisms/ui-team-organism.lolo +1 -1
  78. package/behaviors/registry/agent/atoms/std-agent-session.orb +22 -1
  79. package/behaviors/registry/agent/atoms/std-agent-tool-call.orb +38 -2
  80. package/behaviors/registry/agent/atoms/std-agent-tool-loop.orb +38 -2
  81. package/behaviors/registry/agent/organisms/std-agent-rabit.orb +23 -1
  82. package/behaviors/registry/infra/atoms/std-approval-gate.orb +8 -16
  83. package/behaviors/registry/infra/atoms/std-cache-aside.orb +31 -62
  84. package/behaviors/registry/infra/atoms/std-circuit-breaker.orb +16 -32
  85. package/behaviors/registry/infra/atoms/std-cross-reference.orb +32 -4
  86. package/behaviors/registry/infra/atoms/std-data-erasure.orb +8 -16
  87. package/behaviors/registry/infra/atoms/std-mod-queue.orb +8 -16
  88. package/behaviors/registry/infra/atoms/std-rate-limiter.orb +14 -28
  89. package/behaviors/registry/infra/atoms/std-row-access-control.orb +30 -2
  90. package/behaviors/registry/ui/core/atoms/std-billable-hour.orb +1 -2
  91. package/behaviors/registry/ui/core/atoms/std-board.orb +19 -5
  92. package/behaviors/registry/ui/core/atoms/std-browse.orb +29 -7
  93. package/behaviors/registry/ui/core/atoms/std-dashboard-grid-embedded.orb +1 -2
  94. package/behaviors/registry/ui/core/atoms/std-esign-flow.orb +3 -5
  95. package/behaviors/registry/ui/core/atoms/std-esign-request.orb +3 -6
  96. package/behaviors/registry/ui/core/atoms/std-event-log.orb +17 -3
  97. package/behaviors/registry/ui/core/atoms/std-export.orb +2 -4
  98. package/behaviors/registry/ui/core/atoms/std-form-advanced.orb +1 -1
  99. package/behaviors/registry/ui/core/atoms/std-gallery.orb +9 -18
  100. package/behaviors/registry/ui/core/atoms/std-graphs.orb +15 -1
  101. package/behaviors/registry/ui/core/atoms/std-import.orb +2 -4
  102. package/behaviors/registry/ui/core/atoms/std-list.orb +4 -6
  103. package/behaviors/registry/ui/core/atoms/std-modal.orb +2 -2
  104. package/behaviors/registry/ui/core/atoms/std-multi-party-flow.orb +16 -2
  105. package/behaviors/registry/ui/core/atoms/std-pagination.orb +15 -1
  106. package/behaviors/registry/ui/core/atoms/std-rating-review.orb +19 -5
  107. package/behaviors/registry/ui/core/atoms/std-recurrence.orb +17 -3
  108. package/behaviors/registry/ui/core/atoms/std-rich-editor.orb +15 -1
  109. package/behaviors/registry/ui/core/atoms/std-scatter.orb +15 -1
  110. package/behaviors/registry/ui/core/atoms/std-signature-capture.orb +2 -2
  111. package/behaviors/registry/ui/core/atoms/std-stats.orb +15 -1
  112. package/behaviors/registry/ui/core/atoms/std-step-flow.orb +16 -2
  113. package/behaviors/registry/ui/core/atoms/std-thread.orb +2 -2
  114. package/behaviors/registry/ui/core/atoms/std-vote.orb +2 -2
  115. package/behaviors/registry/ui/core/atoms/std-wizard.orb +17 -3
  116. package/behaviors/registry/ui/core/atoms/ui-atlas-image.orb +4 -1
  117. package/behaviors/registry/ui/core/atoms/ui-atlas-panel.orb +4 -1
  118. package/behaviors/registry/ui/core/atoms/ui-center.orb +4 -1
  119. package/behaviors/registry/ui/core/atoms/ui-hstack.orb +4 -1
  120. package/behaviors/registry/ui/core/atoms/ui-icon.orb +4 -1
  121. package/behaviors/registry/ui/core/atoms/ui-stack.orb +4 -1
  122. package/behaviors/registry/ui/core/atoms/ui-typography.orb +4 -1
  123. package/behaviors/registry/ui/core/atoms/ui-vstack.orb +4 -1
  124. package/behaviors/registry/ui/core/molecules/ui-accordion.orb +3 -3
  125. package/behaviors/registry/ui/core/molecules/ui-content-renderer.orb +4 -1
  126. package/behaviors/registry/ui/core/molecules/ui-form-layout.orb +4 -1
  127. package/behaviors/registry/ui/core/molecules/ui-grid.orb +4 -1
  128. package/behaviors/registry/ui/core/molecules/ui-tabs.orb +2 -2
  129. package/behaviors/registry/ui/core/molecules/ui-wizard-container.orb +1 -1
  130. package/behaviors/registry/ui/core/organisms/ui-chat-bar.orb +4 -1
  131. package/behaviors/registry/ui/core/organisms/ui-dashboard-grid.orb +5 -2
  132. package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +5 -2
  133. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +4 -1
  134. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +4 -1
  135. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +4 -1
  136. package/behaviors/registry/ui/core/organisms/ui-form-section.orb +34 -2
  137. package/behaviors/registry/ui/core/organisms/ui-form.orb +34 -2
  138. package/behaviors/registry/ui/core/organisms/ui-master-detail.orb +4 -1
  139. package/behaviors/registry/ui/core/organisms/ui-media-gallery.orb +4 -1
  140. package/behaviors/registry/ui/core/organisms/ui-segment-renderer.orb +24 -2
  141. package/behaviors/registry/ui/core/organisms/ui-stat-card.orb +4 -1
  142. package/behaviors/registry/ui/core/organisms/ui-subagent-trace-panel.orb +4 -1
  143. package/behaviors/registry/ui/core/organisms/ui-tabbed-container.orb +1 -1
  144. package/behaviors/registry/ui/core/organisms/ui-ui-slot-renderer.orb +1 -1
  145. package/behaviors/registry/ui/game/atoms/std-fx-particles.orb +35 -1
  146. package/behaviors/registry/ui/game/atoms/ui-action-tile.orb +4 -1
  147. package/behaviors/registry/ui/game/atoms/ui-draw-shape-layer.orb +183 -13
  148. package/behaviors/registry/ui/game/atoms/ui-draw-shape.orb +104 -9
  149. package/behaviors/registry/ui/game/atoms/ui-draw-sprite-layer.orb +56 -2
  150. package/behaviors/registry/ui/game/atoms/ui-draw-sprite.orb +36 -4
  151. package/behaviors/registry/ui/game/atoms/ui-draw-text-layer.orb +21 -2
  152. package/behaviors/registry/ui/game/atoms/ui-draw-text.orb +19 -4
  153. package/behaviors/registry/ui/game/organisms/std-tactics-board-3d.orb +173 -2
  154. package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +1 -1
  155. package/behaviors/registry/ui/learning/atoms/std-learn-compare.orb +2814 -0
  156. package/behaviors/registry/ui/learning/atoms/std-learn-coupled-sim.orb +3183 -0
  157. package/behaviors/registry/ui/learning/atoms/std-learn-estimate.orb +2394 -0
  158. package/behaviors/registry/ui/learning/atoms/std-learn-goal-seek.orb +3951 -0
  159. package/behaviors/registry/ui/learning/atoms/std-learn-grid-sim.orb +3458 -0
  160. package/behaviors/registry/ui/learning/atoms/std-learn-hotspot.orb +2646 -0
  161. package/behaviors/registry/ui/learning/atoms/std-learn-measure.orb +1979 -0
  162. package/behaviors/registry/ui/learning/atoms/std-learn-perturb.orb +2994 -0
  163. package/behaviors/registry/ui/learning/atoms/std-learn-predict.orb +2682 -0
  164. package/behaviors/registry/ui/learning/atoms/std-learn-scenario.orb +1842 -0
  165. package/behaviors/registry/ui/learning/atoms/std-learn-sim-3d.orb +1648 -0
  166. package/behaviors/registry/ui/learning/atoms/std-learn-speed-drill.orb +2307 -0
  167. package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +2947 -0
  168. package/behaviors/registry/ui/learning/atoms/ui-algorithm-canvas.orb +173 -4
  169. package/behaviors/registry/ui/learning/atoms/ui-biology-canvas.orb +267 -7
  170. package/behaviors/registry/ui/learning/atoms/ui-chemistry-canvas.orb +263 -8
  171. package/behaviors/registry/ui/learning/atoms/ui-learning-canvas.orb +116 -1
  172. package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +200 -4
  173. package/behaviors/registry/ui/learning/atoms/ui-physics-canvas.orb +264 -7
  174. package/behaviors/registry/ui/marketing/atoms/std-marketing-article.orb +1 -2
  175. package/behaviors/registry/ui/marketing/atoms/std-marketing-footer.orb +1 -2
  176. package/behaviors/registry/ui/marketing/atoms/std-marketing-split.orb +1 -2
  177. package/behaviors/registry/ui/marketing/atoms/std-marketing-stats.orb +1 -2
  178. package/behaviors/registry/ui/marketing/atoms/std-marketing-steps.orb +1 -2
  179. package/behaviors/registry/ui/marketing/atoms/std-marketing-team.orb +1 -2
  180. package/behaviors/registry/ui/marketing/organisms/ui-book-viewer.orb +4 -1
  181. package/behaviors/registry/ui/marketing/organisms/ui-case-study-organism.orb +4 -1
  182. package/behaviors/registry/ui/marketing/organisms/ui-feature-grid-organism.orb +4 -1
  183. package/behaviors/registry/ui/marketing/organisms/ui-hero-organism.orb +4 -1
  184. package/behaviors/registry/ui/marketing/organisms/ui-pricing-organism.orb +4 -1
  185. package/behaviors/registry/ui/marketing/organisms/ui-showcase-organism.orb +4 -1
  186. package/behaviors/registry/ui/marketing/organisms/ui-stats-organism.orb +4 -1
  187. package/behaviors/registry/ui/marketing/organisms/ui-step-flow-organism.orb +4 -1
  188. package/behaviors/registry/ui/marketing/organisms/ui-team-organism.orb +4 -1
  189. package/dist/behaviors/exports-reader.js +3440 -2189
  190. package/dist/behaviors/functions/index.d.ts +1751 -154
  191. package/dist/behaviors/functions/index.js +3402 -2190
  192. package/dist/behaviors/index.d.ts +1 -1
  193. package/dist/behaviors/index.js +3441 -2190
  194. package/dist/behaviors/query.js +3440 -2189
  195. package/dist/behaviors-embeddings.hash.json +3 -3
  196. package/dist/behaviors-embeddings.json +230 -178
  197. package/dist/behaviors-registry.json +4397 -171
  198. package/dist/index.d.ts +1 -1
  199. package/dist/index.js +3441 -2190
  200. package/dist/knob-embeddings.hash.json +3 -3
  201. package/dist/knob-embeddings.json +1 -1
  202. package/dist/registry/factory-signatures.json +1 -1
  203. package/package.json +2 -2
@@ -0,0 +1,591 @@
1
+ app std-learn-goal-seek "1.0.0"
2
+ "Generic goal-seek (inverse-problem) instrument — a config-declared target curve is drawn from a hidden parameter pair, and the learner tunes their own two parameters with sliders until their curve matches it, then presses Check to be graded against an RMS-distance tolerance. The curve family (power y = A·xᴮ, linear y = A + B·x, or sine y = A·sin(B·x)), the hidden target parameters, the tolerance, and every label are config-declared, so the same mechanism serves any inverse problem: fit a demand curve, match a projectile arc, tune a growth rate, recover a wave's amplitude and frequency. A live distance readout (optionally hidden for a harder variant) plays warmer/colder. Standalone by default; behind a lab router set matchMode and it renders only when MODE_SELECTED matches."
3
+ orbital GoalSeekOrbital {
4
+ type GoalSeekCurve = {
5
+ label : string
6
+ color : string
7
+ samples : [GoalSeekCurveSample]!
8
+ }
9
+ type GoalSeekCurveSample = {
10
+ x : number!
11
+ y : number!
12
+ }
13
+
14
+ entity GoalSeekScene [runtime] {
15
+ id : string!
16
+ active : boolean = false
17
+ title : string
18
+ curveKind : string
19
+ paramA : number
20
+ paramB : number
21
+ paramALabel : string
22
+ paramBLabel : string
23
+ targetSamples : [GoalSeekCurveSample]
24
+ curves : [GoalSeekCurve]
25
+ distance : number = 0
26
+ attempts : number = 0
27
+ solved : boolean = false
28
+ feedback : string
29
+ resultRow : [node]
30
+ yMin : number
31
+ yMax : number
32
+ }
33
+
34
+ trait GoalSeekSim -> @rebindable GoalSeekScene [interaction, instance] {
35
+ initial: idle
36
+
37
+ state idle {
38
+ INIT -> idle when (== @config.matchMode "")
39
+ (let (
40
+ (kind @config.curveKind)
41
+ (span (- @config.xMax @config.xMin))
42
+ (tA @config.targetA)
43
+ (tB @config.targetB)
44
+ (A @config.paramADefault)
45
+ (B @config.paramBDefault)
46
+ (targetSamples (array/map (array/range 0 81) (fn (i)
47
+ (let (
48
+ (x (+ @config.xMin (* @i (/ @span 80))))
49
+ (yVal (if (== @kind "power")
50
+ (* @tA (math/pow @x @tB))
51
+ (if (== @kind "linear")
52
+ (+ @tA (* @tB @x))
53
+ (* @tA (math/sin (* @tB @x))))))
54
+ )
55
+ { x: @x, y: @yVal }))))
56
+ (samples (array/map (array/range 0 81) (fn (i)
57
+ (let (
58
+ (x (+ @config.xMin (* @i (/ @span 80))))
59
+ (yVal (if (== @kind "power")
60
+ (* @A (math/pow @x @B))
61
+ (if (== @kind "linear")
62
+ (+ @A (* @B @x))
63
+ (* @A (math/sin (* @B @x))))))
64
+ )
65
+ { x: @x, y: @yVal }))))
66
+ (dist (math/sqrt (/ (array/sum (array/map (array/range 0 81) (fn (i)
67
+ (let (
68
+ (dY (- (object/get (array/nth @samples @i) y) (object/get (array/nth @targetSamples @i) y)))
69
+ )
70
+ (* @dY @dY))))) 81)))
71
+ (allSamples (array/concat @samples @targetSamples))
72
+ (yMinRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/min @acc (object/get @s y)))))
73
+ (yMaxRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/max @acc (object/get @s y)))))
74
+ (yPad (* 0.1 (math/max 0.001 (- @yMaxRaw @yMinRaw))))
75
+ )
76
+ (do
77
+ (set @entity.active true)
78
+ (set @entity.title @config.title)
79
+ (set @entity.curveKind @kind)
80
+ (set @entity.paramA @A)
81
+ (set @entity.paramB @B)
82
+ (set @entity.paramALabel @config.paramALabel)
83
+ (set @entity.paramBLabel @config.paramBLabel)
84
+ (set @entity.targetSamples @targetSamples)
85
+ (set @entity.distance @dist)
86
+ (set @entity.attempts 0)
87
+ (set @entity.solved false)
88
+ (set @entity.feedback "")
89
+ (set @entity.yMin (- @yMinRaw @yPad))
90
+ (set @entity.yMax (+ @yMaxRaw @yPad))
91
+ (set @entity.curves [
92
+ { label: @config.targetLabel, color: @config.targetColor, samples: @targetSamples },
93
+ { label: @config.learnerLabel, color: @config.learnerColor, samples: @samples }
94
+ ])))
95
+ (set @entity.resultRow (array/concat (if @entity.solved [ { type: badge, variant: success, label: "Solved" } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]))
96
+ (render-ui main {
97
+ type: box
98
+ padding: md
99
+ children: [
100
+ { type: stack, direction: vertical, gap: md, children: [
101
+ { type: typography, variant: h4, content: @entity.title },
102
+ { type: typography, variant: body, content: @config.goalText },
103
+ { type: stack, direction: vertical, gap: xs, children: [
104
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
105
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
106
+ ] },
107
+ { type: stack, direction: vertical, gap: xs, children: [
108
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
109
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
110
+ ] },
111
+ { 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: false, animate: false },
112
+ { type: typography, variant: body, content: (if @config.showLiveDistance (str/concat "Distance from target: " (math/round @entity.distance 3) " · attempts: " (str/concat "" @entity.attempts)) (str/concat "Attempts: " (str/concat "" @entity.attempts))) },
113
+ { type: stack, direction: horizontal, gap: sm, children: [
114
+ { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if @entity.solved true false) },
115
+ { type: button, action: RESET, label: "Reset", variant: ghost }
116
+ ] },
117
+ { type: stack, direction: horizontal, gap: xs, children: @entity.resultRow }
118
+ ] }
119
+ ]
120
+ })
121
+
122
+ MODE_SELECTED -> idle when (== ?mode @config.matchMode)
123
+ (let (
124
+ (kind @config.curveKind)
125
+ (span (- @config.xMax @config.xMin))
126
+ (tA @config.targetA)
127
+ (tB @config.targetB)
128
+ (A @config.paramADefault)
129
+ (B @config.paramBDefault)
130
+ (targetSamples (array/map (array/range 0 81) (fn (i)
131
+ (let (
132
+ (x (+ @config.xMin (* @i (/ @span 80))))
133
+ (yVal (if (== @kind "power")
134
+ (* @tA (math/pow @x @tB))
135
+ (if (== @kind "linear")
136
+ (+ @tA (* @tB @x))
137
+ (* @tA (math/sin (* @tB @x))))))
138
+ )
139
+ { x: @x, y: @yVal }))))
140
+ (samples (array/map (array/range 0 81) (fn (i)
141
+ (let (
142
+ (x (+ @config.xMin (* @i (/ @span 80))))
143
+ (yVal (if (== @kind "power")
144
+ (* @A (math/pow @x @B))
145
+ (if (== @kind "linear")
146
+ (+ @A (* @B @x))
147
+ (* @A (math/sin (* @B @x))))))
148
+ )
149
+ { x: @x, y: @yVal }))))
150
+ (dist (math/sqrt (/ (array/sum (array/map (array/range 0 81) (fn (i)
151
+ (let (
152
+ (dY (- (object/get (array/nth @samples @i) y) (object/get (array/nth @targetSamples @i) y)))
153
+ )
154
+ (* @dY @dY))))) 81)))
155
+ (allSamples (array/concat @samples @targetSamples))
156
+ (yMinRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/min @acc (object/get @s y)))))
157
+ (yMaxRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/max @acc (object/get @s y)))))
158
+ (yPad (* 0.1 (math/max 0.001 (- @yMaxRaw @yMinRaw))))
159
+ )
160
+ (do
161
+ (set @entity.active true)
162
+ (set @entity.title @config.title)
163
+ (set @entity.curveKind @kind)
164
+ (set @entity.paramA @A)
165
+ (set @entity.paramB @B)
166
+ (set @entity.paramALabel @config.paramALabel)
167
+ (set @entity.paramBLabel @config.paramBLabel)
168
+ (set @entity.targetSamples @targetSamples)
169
+ (set @entity.distance @dist)
170
+ (set @entity.attempts 0)
171
+ (set @entity.solved false)
172
+ (set @entity.feedback "")
173
+ (set @entity.yMin (- @yMinRaw @yPad))
174
+ (set @entity.yMax (+ @yMaxRaw @yPad))
175
+ (set @entity.curves [
176
+ { label: @config.targetLabel, color: @config.targetColor, samples: @targetSamples },
177
+ { label: @config.learnerLabel, color: @config.learnerColor, samples: @samples }
178
+ ])))
179
+ (set @entity.resultRow (array/concat (if @entity.solved [ { type: badge, variant: success, label: "Solved" } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]))
180
+ (render-ui main {
181
+ type: box
182
+ padding: md
183
+ children: [
184
+ { type: stack, direction: vertical, gap: md, children: [
185
+ { type: typography, variant: h4, content: @entity.title },
186
+ { type: typography, variant: body, content: @config.goalText },
187
+ { type: stack, direction: vertical, gap: xs, children: [
188
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
189
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
190
+ ] },
191
+ { type: stack, direction: vertical, gap: xs, children: [
192
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
193
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
194
+ ] },
195
+ { 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: false, animate: false },
196
+ { type: typography, variant: body, content: (if @config.showLiveDistance (str/concat "Distance from target: " (math/round @entity.distance 3) " · attempts: " (str/concat "" @entity.attempts)) (str/concat "Attempts: " (str/concat "" @entity.attempts))) },
197
+ { type: stack, direction: horizontal, gap: sm, children: [
198
+ { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if @entity.solved true false) },
199
+ { type: button, action: RESET, label: "Reset", variant: ghost }
200
+ ] },
201
+ { type: stack, direction: horizontal, gap: xs, children: @entity.resultRow }
202
+ ] }
203
+ ]
204
+ })
205
+
206
+ SET_PARAM_A -> idle when (and (== @entity.active true) (== @entity.solved false))
207
+ (let (
208
+ (A (math/clamp ?value @config.paramAMin @config.paramAMax))
209
+ (B @entity.paramB)
210
+ (kind @entity.curveKind)
211
+ (span (- @config.xMax @config.xMin))
212
+ (samples (array/map (array/range 0 81) (fn (i)
213
+ (let (
214
+ (x (+ @config.xMin (* @i (/ @span 80))))
215
+ (yVal (if (== @kind "power")
216
+ (* @A (math/pow @x @B))
217
+ (if (== @kind "linear")
218
+ (+ @A (* @B @x))
219
+ (* @A (math/sin (* @B @x))))))
220
+ )
221
+ { x: @x, y: @yVal }))))
222
+ (dist (math/sqrt (/ (array/sum (array/map (array/range 0 81) (fn (i)
223
+ (let (
224
+ (dY (- (object/get (array/nth @samples @i) y) (object/get (array/nth @entity.targetSamples @i) y)))
225
+ )
226
+ (* @dY @dY))))) 81)))
227
+ (allSamples (array/concat @samples @entity.targetSamples))
228
+ (yMinRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/min @acc (object/get @s y)))))
229
+ (yMaxRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/max @acc (object/get @s y)))))
230
+ (yPad (* 0.1 (math/max 0.001 (- @yMaxRaw @yMinRaw))))
231
+ )
232
+ (do
233
+ (set @entity.paramA @A)
234
+ (set @entity.distance @dist)
235
+ (set @entity.yMin (- @yMinRaw @yPad))
236
+ (set @entity.yMax (+ @yMaxRaw @yPad))
237
+ (set @entity.curves [
238
+ { label: @config.targetLabel, color: @config.targetColor, samples: @entity.targetSamples },
239
+ { label: @config.learnerLabel, color: @config.learnerColor, samples: @samples }
240
+ ])))
241
+ (set @entity.resultRow (array/concat (if @entity.solved [ { type: badge, variant: success, label: "Solved" } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]))
242
+ (render-ui main {
243
+ type: box
244
+ padding: md
245
+ children: [
246
+ { type: stack, direction: vertical, gap: md, children: [
247
+ { type: typography, variant: h4, content: @entity.title },
248
+ { type: typography, variant: body, content: @config.goalText },
249
+ { type: stack, direction: vertical, gap: xs, children: [
250
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
251
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
252
+ ] },
253
+ { type: stack, direction: vertical, gap: xs, children: [
254
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
255
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
256
+ ] },
257
+ { 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: false, animate: false },
258
+ { type: typography, variant: body, content: (if @config.showLiveDistance (str/concat "Distance from target: " (math/round @entity.distance 3) " · attempts: " (str/concat "" @entity.attempts)) (str/concat "Attempts: " (str/concat "" @entity.attempts))) },
259
+ { type: stack, direction: horizontal, gap: sm, children: [
260
+ { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if @entity.solved true false) },
261
+ { type: button, action: RESET, label: "Reset", variant: ghost }
262
+ ] },
263
+ { type: stack, direction: horizontal, gap: xs, children: @entity.resultRow }
264
+ ] }
265
+ ]
266
+ })
267
+ (emit PARAMS_CHANGED { paramA: @entity.paramA, paramB: @entity.paramB, distance: @entity.distance })
268
+
269
+ SET_PARAM_B -> idle when (and (== @entity.active true) (== @entity.solved false))
270
+ (let (
271
+ (A @entity.paramA)
272
+ (B (math/clamp ?value @config.paramBMin @config.paramBMax))
273
+ (kind @entity.curveKind)
274
+ (span (- @config.xMax @config.xMin))
275
+ (samples (array/map (array/range 0 81) (fn (i)
276
+ (let (
277
+ (x (+ @config.xMin (* @i (/ @span 80))))
278
+ (yVal (if (== @kind "power")
279
+ (* @A (math/pow @x @B))
280
+ (if (== @kind "linear")
281
+ (+ @A (* @B @x))
282
+ (* @A (math/sin (* @B @x))))))
283
+ )
284
+ { x: @x, y: @yVal }))))
285
+ (dist (math/sqrt (/ (array/sum (array/map (array/range 0 81) (fn (i)
286
+ (let (
287
+ (dY (- (object/get (array/nth @samples @i) y) (object/get (array/nth @entity.targetSamples @i) y)))
288
+ )
289
+ (* @dY @dY))))) 81)))
290
+ (allSamples (array/concat @samples @entity.targetSamples))
291
+ (yMinRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/min @acc (object/get @s y)))))
292
+ (yMaxRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/max @acc (object/get @s y)))))
293
+ (yPad (* 0.1 (math/max 0.001 (- @yMaxRaw @yMinRaw))))
294
+ )
295
+ (do
296
+ (set @entity.paramB @B)
297
+ (set @entity.distance @dist)
298
+ (set @entity.yMin (- @yMinRaw @yPad))
299
+ (set @entity.yMax (+ @yMaxRaw @yPad))
300
+ (set @entity.curves [
301
+ { label: @config.targetLabel, color: @config.targetColor, samples: @entity.targetSamples },
302
+ { label: @config.learnerLabel, color: @config.learnerColor, samples: @samples }
303
+ ])))
304
+ (set @entity.resultRow (array/concat (if @entity.solved [ { type: badge, variant: success, label: "Solved" } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]))
305
+ (render-ui main {
306
+ type: box
307
+ padding: md
308
+ children: [
309
+ { type: stack, direction: vertical, gap: md, children: [
310
+ { type: typography, variant: h4, content: @entity.title },
311
+ { type: typography, variant: body, content: @config.goalText },
312
+ { type: stack, direction: vertical, gap: xs, children: [
313
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
314
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
315
+ ] },
316
+ { type: stack, direction: vertical, gap: xs, children: [
317
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
318
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
319
+ ] },
320
+ { 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: false, animate: false },
321
+ { type: typography, variant: body, content: (if @config.showLiveDistance (str/concat "Distance from target: " (math/round @entity.distance 3) " · attempts: " (str/concat "" @entity.attempts)) (str/concat "Attempts: " (str/concat "" @entity.attempts))) },
322
+ { type: stack, direction: horizontal, gap: sm, children: [
323
+ { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if @entity.solved true false) },
324
+ { type: button, action: RESET, label: "Reset", variant: ghost }
325
+ ] },
326
+ { type: stack, direction: horizontal, gap: xs, children: @entity.resultRow }
327
+ ] }
328
+ ]
329
+ })
330
+ (emit PARAMS_CHANGED { paramA: @entity.paramA, paramB: @entity.paramB, distance: @entity.distance })
331
+
332
+ CHECK -> idle when (and (== @entity.active true) (== @entity.solved false))
333
+ (set @entity.attempts (+ @entity.attempts 1))
334
+ (set @entity.solved (if (<= @entity.distance @config.tolerance) true false))
335
+ (set @entity.feedback (if (<= @entity.distance @config.tolerance)
336
+ (str/concat "Matched! Distance " (math/round @entity.distance 3) " is within tolerance " (math/round @config.tolerance 3) ".")
337
+ (str/concat "Not yet — distance " (math/round @entity.distance 3) " is above tolerance " (math/round @config.tolerance 3) ". Keep tuning.")))
338
+ (set @entity.resultRow (array/concat (if @entity.solved [ { type: badge, variant: success, label: "Solved" } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]))
339
+ (render-ui main {
340
+ type: box
341
+ padding: md
342
+ children: [
343
+ { type: stack, direction: vertical, gap: md, children: [
344
+ { type: typography, variant: h4, content: @entity.title },
345
+ { type: typography, variant: body, content: @config.goalText },
346
+ { type: stack, direction: vertical, gap: xs, children: [
347
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
348
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
349
+ ] },
350
+ { type: stack, direction: vertical, gap: xs, children: [
351
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
352
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
353
+ ] },
354
+ { 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: false, animate: false },
355
+ { type: typography, variant: body, content: (if @config.showLiveDistance (str/concat "Distance from target: " (math/round @entity.distance 3) " · attempts: " (str/concat "" @entity.attempts)) (str/concat "Attempts: " (str/concat "" @entity.attempts))) },
356
+ { type: stack, direction: horizontal, gap: sm, children: [
357
+ { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if @entity.solved true false) },
358
+ { type: button, action: RESET, label: "Reset", variant: ghost }
359
+ ] },
360
+ { type: stack, direction: horizontal, gap: xs, children: @entity.resultRow }
361
+ ] }
362
+ ]
363
+ })
364
+ (emit GOAL_CHECKED { matched: @entity.solved, distance: @entity.distance, attempts: @entity.attempts })
365
+
366
+ RESET -> idle when (== @entity.active true)
367
+ (let (
368
+ (A @config.paramADefault)
369
+ (B @config.paramBDefault)
370
+ (kind @entity.curveKind)
371
+ (span (- @config.xMax @config.xMin))
372
+ (samples (array/map (array/range 0 81) (fn (i)
373
+ (let (
374
+ (x (+ @config.xMin (* @i (/ @span 80))))
375
+ (yVal (if (== @kind "power")
376
+ (* @A (math/pow @x @B))
377
+ (if (== @kind "linear")
378
+ (+ @A (* @B @x))
379
+ (* @A (math/sin (* @B @x))))))
380
+ )
381
+ { x: @x, y: @yVal }))))
382
+ (dist (math/sqrt (/ (array/sum (array/map (array/range 0 81) (fn (i)
383
+ (let (
384
+ (dY (- (object/get (array/nth @samples @i) y) (object/get (array/nth @entity.targetSamples @i) y)))
385
+ )
386
+ (* @dY @dY))))) 81)))
387
+ (allSamples (array/concat @samples @entity.targetSamples))
388
+ (yMinRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/min @acc (object/get @s y)))))
389
+ (yMaxRaw (array/reduce @allSamples (object/get (array/nth @allSamples 0) y) (fn (acc s) (math/max @acc (object/get @s y)))))
390
+ (yPad (* 0.1 (math/max 0.001 (- @yMaxRaw @yMinRaw))))
391
+ )
392
+ (do
393
+ (set @entity.paramA @A)
394
+ (set @entity.paramB @B)
395
+ (set @entity.distance @dist)
396
+ (set @entity.attempts 0)
397
+ (set @entity.solved false)
398
+ (set @entity.feedback "")
399
+ (set @entity.yMin (- @yMinRaw @yPad))
400
+ (set @entity.yMax (+ @yMaxRaw @yPad))
401
+ (set @entity.curves [
402
+ { label: @config.targetLabel, color: @config.targetColor, samples: @entity.targetSamples },
403
+ { label: @config.learnerLabel, color: @config.learnerColor, samples: @samples }
404
+ ])))
405
+ (set @entity.resultRow (array/concat (if @entity.solved [ { type: badge, variant: success, label: "Solved" } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]))
406
+ (render-ui main {
407
+ type: box
408
+ padding: md
409
+ children: [
410
+ { type: stack, direction: vertical, gap: md, children: [
411
+ { type: typography, variant: h4, content: @entity.title },
412
+ { type: typography, variant: body, content: @config.goalText },
413
+ { type: stack, direction: vertical, gap: xs, children: [
414
+ { type: typography, variant: caption, content: (str/concat @entity.paramALabel ": " (math/round @entity.paramA 2)) },
415
+ { type: range-slider, min: @config.paramAMin, max: @config.paramAMax, step: @config.paramAStep, value: @entity.paramA, showTooltip: true, action: "SET_PARAM_A" }
416
+ ] },
417
+ { type: stack, direction: vertical, gap: xs, children: [
418
+ { type: typography, variant: caption, content: (str/concat @entity.paramBLabel ": " (math/round @entity.paramB 2)) },
419
+ { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.paramB, showTooltip: true, action: "SET_PARAM_B" }
420
+ ] },
421
+ { 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: false, animate: false },
422
+ { type: typography, variant: body, content: (if @config.showLiveDistance (str/concat "Distance from target: " (math/round @entity.distance 3) " · attempts: " (str/concat "" @entity.attempts)) (str/concat "Attempts: " (str/concat "" @entity.attempts))) },
423
+ { type: stack, direction: horizontal, gap: sm, children: [
424
+ { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if @entity.solved true false) },
425
+ { type: button, action: RESET, label: "Reset", variant: ghost }
426
+ ] },
427
+ { type: stack, direction: horizontal, gap: xs, children: @entity.resultRow }
428
+ ] }
429
+ ]
430
+ })
431
+ (emit GOAL_RESET { attempts: 0 })
432
+ }
433
+
434
+ listens {
435
+ MODE_SELECTED {
436
+ mode : string
437
+ }
438
+ @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
439
+ @tier "domain"
440
+ SET_PARAM_A {
441
+ value : number
442
+ }
443
+ @description "Learner dragged the first parameter's slider — value is the new A; the learner curve and its distance to the target recompute immediately."
444
+ @tier "domain"
445
+ SET_PARAM_B {
446
+ value : number
447
+ }
448
+ @description "Learner dragged the second parameter's slider — value is the new B; the learner curve and its distance to the target recompute immediately."
449
+ @tier "domain"
450
+ CHECK {
451
+ }
452
+ @description "Learner asked to be graded — the current RMS distance is compared against the tolerance and the attempt is counted."
453
+ @tier "domain"
454
+ RESET {
455
+ }
456
+ @description "Learner reset the parameters to their defaults and cleared the attempt count; the target stays the same."
457
+ @tier "domain"
458
+ }
459
+
460
+ emits {
461
+ PARAMS_CHANGED -> external {
462
+ paramA : number
463
+ paramB : number
464
+ distance : number
465
+ }
466
+ @description "The learner's parameter values and current distance after a slider change — a composed listener (readout, coach) can subscribe; a harmless no-op when the atom runs standalone."
467
+ @tier "domain"
468
+ GOAL_CHECKED -> external {
469
+ matched : boolean
470
+ distance : number
471
+ attempts : number
472
+ }
473
+ @description "The verdict of a Check press: whether the learner's curve is within tolerance of the target, the RMS distance, and how many attempts it took."
474
+ @synonyms "goal seek, inverse problem, curve fitting, match the target, tune to fit"
475
+ @tier "domain"
476
+ GOAL_RESET -> external {
477
+ attempts : number
478
+ }
479
+ @description "The learner reset the instrument back to the default parameters."
480
+ @tier "domain"
481
+ }
482
+
483
+ config {
484
+ matchMode : string = ""
485
+ @label "Match Mode"
486
+ @description "When composed behind a lab router, the MODE_SELECTED mode value this instrument responds to. Empty string means standalone — render immediately on INIT."
487
+ @tier "presentation"
488
+ title : string = "Goal Seek"
489
+ @label "Title"
490
+ @description "Title shown above the canvas."
491
+ @tier "presentation"
492
+ goalText : string = "Tune the two sliders until your curve matches the target curve, then press Check."
493
+ @label "Goal Text"
494
+ @description "One-sentence statement of the inverse problem the learner must solve."
495
+ @tier "domain"
496
+ curveKind : string = "sine"
497
+ @label "Curve Kind"
498
+ @description "Curve family both curves are drawn from: power (y = A·xᴮ), linear (y = A + B·x), or sine (y = A·sin(B·x))."
499
+ @synonyms "curve family, power law, linear function, sine wave, function type"
500
+ @tier "domain"
501
+ targetA : number = 2.5
502
+ @label "Target A"
503
+ @description "Hidden first parameter of the target curve the learner must recover."
504
+ @tier "domain"
505
+ targetB : number = 1.5
506
+ @label "Target B"
507
+ @description "Hidden second parameter of the target curve the learner must recover."
508
+ @tier "domain"
509
+ tolerance : number = 0.35
510
+ @label "Tolerance"
511
+ @description "Maximum RMS distance between learner and target curves that still counts as a match."
512
+ @tier "domain"
513
+ showLiveDistance : boolean = true
514
+ @label "Show Live Distance"
515
+ @description "When true the RMS distance updates live as the learner tunes (warmer/colder); when false only attempts are shown, for a harder variant."
516
+ @tier "domain"
517
+ paramALabel : string = "A"
518
+ @label "Parameter A Label"
519
+ @description "Display label for the first parameter."
520
+ @tier "presentation"
521
+ paramAMin : number = -5
522
+ @label "Parameter A Min"
523
+ @tier "domain"
524
+ paramAMax : number = 5
525
+ @label "Parameter A Max"
526
+ @tier "domain"
527
+ paramAStep : number = 0.1
528
+ @label "Parameter A Step"
529
+ @tier "presentation"
530
+ paramADefault : number = 1
531
+ @label "Parameter A Default"
532
+ @description "Starting value of the learner's first parameter."
533
+ @tier "domain"
534
+ paramBLabel : string = "B"
535
+ @label "Parameter B Label"
536
+ @description "Display label for the second parameter."
537
+ @tier "presentation"
538
+ paramBMin : number = -5
539
+ @label "Parameter B Min"
540
+ @tier "domain"
541
+ paramBMax : number = 5
542
+ @label "Parameter B Max"
543
+ @tier "domain"
544
+ paramBStep : number = 0.1
545
+ @label "Parameter B Step"
546
+ @tier "presentation"
547
+ paramBDefault : number = 1
548
+ @label "Parameter B Default"
549
+ @description "Starting value of the learner's second parameter."
550
+ @tier "domain"
551
+ targetLabel : string = "Target"
552
+ @label "Target Label"
553
+ @tier "presentation"
554
+ learnerLabel : string = "Yours"
555
+ @label "Learner Label"
556
+ @tier "presentation"
557
+ targetColor : string = "#94a3b8"
558
+ @label "Target Color"
559
+ @tier "presentation"
560
+ learnerColor : string = "#3b82f6"
561
+ @label "Learner Color"
562
+ @tier "presentation"
563
+ checkLabel : string = "Check"
564
+ @label "Check Label"
565
+ @tier "presentation"
566
+ xMin : number = 0
567
+ @label "X Min"
568
+ @tier "presentation"
569
+ xMax : number = 10
570
+ @label "X Max"
571
+ @tier "presentation"
572
+ width : number = 600
573
+ @label "Width"
574
+ @tier "presentation"
575
+ height : number = 400
576
+ @label "Height"
577
+ @tier "presentation"
578
+ showAxes : boolean = true
579
+ @label "Show Axes"
580
+ @tier "presentation"
581
+ showGrid : boolean = true
582
+ @label "Show Grid"
583
+ @tier "presentation"
584
+ gridStep : number = 1
585
+ @label "Grid Step"
586
+ @tier "presentation"
587
+ }
588
+ }
589
+
590
+ page "/learn-goal-seek" as GoalSeekPage -> GoalSeekSim
591
+ }