@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
@@ -31,6 +31,11 @@ orbital LearnClassifyOrbital {
31
31
  fill : string
32
32
  lineWidth : number
33
33
  opacity : number
34
+ tone : string
35
+ fillTone : string
36
+ stroke : string
37
+ textSize : string
38
+ fillStyle : string
34
39
  }
35
40
  type LearnClassifyItemOption = {
36
41
  category : string!
@@ -117,8 +122,8 @@ orbital LearnClassifyOrbital {
117
122
  (plotBottom (- @config.height @marginY))
118
123
  (tierHeight (/ @plotHeight @categoryCount))
119
124
  (maxWidth (* @plotWidth 0.95))
120
- (minWidth (* @plotWidth 0.3))
121
- (widthStep (/ (- @maxWidth @minWidth) (- @categoryCount 1)))
125
+ (minWidth (- @maxWidth (* @config.taper (- @maxWidth (* @plotWidth 0.3)))))
126
+ (widthStep (/ (- @maxWidth @minWidth) (math/max 1 (- @categoryCount 1))))
122
127
  (centerX (/ @config.width 2))
123
128
  (revealed (!= @entity.selectedCategory ""))
124
129
  (rects (array/flatten (array/map (array/range @categoryCount) (fn i (let (
@@ -130,10 +135,10 @@ orbital LearnClassifyOrbital {
130
135
  (isCorrectCategory (== @categoryValue @entity.correctCategory))
131
136
  (isSelectedCategory (== @categoryValue @entity.selectedCategory))
132
137
  (fillColor (if (and @revealed @isCorrectCategory) @config.correctColor (if (and @revealed @isSelectedCategory) @config.incorrectColor (object/get @category color))))
133
- (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: @fillColor, fill: @fillColor })
138
+ (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: @fillColor, fillTone: @fillColor, fillStyle: "tint" })
134
139
  )
135
140
  (if (and @revealed @isSelectedCategory)
136
- [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: "#0f172a", fill: "transparent", lineWidth: 3, opacity: 1 } ]
141
+ [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: "ink", fillStyle: "outline", stroke: "bold", opacity: 1 } ]
137
142
  [ @baseRect ]
138
143
  )
139
144
  )))))
@@ -141,30 +146,34 @@ orbital LearnClassifyOrbital {
141
146
  (category (array/nth @config.categories @i))
142
147
  (yTop (- @plotBottom (* (+ @i 1) @tierHeight)))
143
148
  )
144
- { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), fontSize: 13, align: "center", color: "#0f172a" }))))
149
+ { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), textSize: "sm", align: "center", tone: "label" }))))
145
150
  (backdropShapes (array/concat @rects @labels))
146
151
  (summaryView [
147
152
  { type: typography, variant: h4, content: "Round Complete" },
148
153
  { type: stat-display, label: "Final Score", value: @entity.correctCount, max: @entity.attemptedCount },
149
154
  { type: stack, direction: vertical, gap: xs, children: (array/map @entity.categoryStats (fn s { type: typography, variant: body, content: (str/concat (object/get @s label) ": " (str/concat "" (object/get @s correct)) "/" (str/concat "" (object/get @s attempted))) })) },
150
- { type: button, action: RETRY, label: @config.retryLabel, variant: primary }
155
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: primary }
151
156
  ])
152
157
  (itemView [
153
158
  { type: card, variant: bordered, padding: md, children: (array/concat [
154
159
  { type: typography, variant: body, content: @entity.itemText }
155
160
  ] (if (and @config.showHint (!= @entity.itemHint "")) [ { type: typography, variant: caption, color: muted, content: @entity.itemHint } ] [])) },
156
- { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate },
157
- { type: stack, direction: horizontal, gap: xs, children: (let (
161
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: TOGGLE_RUN, ArrowRight: ADVANCE, KeyR: RETRY } },
162
+ { type: stack, direction: vertical, gap: sm, children: (let (
158
163
  (itemOptions (object/get (array/nth @config.items @entity.itemIndex) options []))
164
+ (locked (!= @entity.selectedCategory ""))
159
165
  ) (if (> (array/len @itemOptions) 0)
160
- (array/map @itemOptions (fn o { type: button, action: PICK, actionPayload: { category: (object/get @o category) }, label: (object/get @o label), variant: (if (== @entity.selectedCategory (object/get @o category)) "primary" "secondary"), size: sm }))
161
- (array/map (array/reverse @config.categories) (fn c { type: button, action: PICK, actionPayload: { category: (object/get @c value) }, label: (object/get @c label), variant: (if (== @entity.selectedCategory (object/get @c value)) "primary" "secondary"), size: sm })))) },
162
- { type: stack, direction: horizontal, gap: xs, children: (array/concat (if (!= @entity.verdict "") [ { type: badge, variant: (if (== @entity.verdict "correct") "success" "danger"), label: (if (== @entity.verdict "correct") "Correct" "Incorrect") } ] []) [ { type: typography, variant: caption, content: @entity.feedback } ]) },
163
- { type: stack, direction: horizontal, gap: sm, children: [
164
- { type: button, action: ADVANCE, label: @config.advanceLabel, variant: primary, disabled: (if (== @entity.selectedCategory "") true false) },
165
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Auto"), variant: ghost }
166
- ] },
167
- { type: stat-display, label: (if (> @config.passScore 0) (str/concat "Score — pass at " @config.passScore) "Score"), value: (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) @entity.score @entity.correctCount), max: (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (if (> @config.scoreCap 0) @config.scoreCap (* @entity.totalItems @config.pointsPerCorrect)) @entity.attemptedCount) }
166
+ (array/map @itemOptions (fn o { type: button, action: PICK, actionPayload: { category: (object/get @o category) }, label: (object/get @o label), size: lg, disabled: @locked, variant: (if (not @locked) "secondary" (if (== (object/get @o category) @entity.correctCategory) "success" (if (== (object/get @o category) @entity.selectedCategory) "danger" "ghost"))) }))
167
+ (array/map (array/reverse @config.categories) (fn c { type: button, action: PICK, actionPayload: { category: (object/get @c value) }, label: (object/get @c label), size: lg, disabled: @locked, variant: (if (not @locked) "secondary" (if (== (object/get @c value) @entity.correctCategory) "success" (if (== (object/get @c value) @entity.selectedCategory) "danger" "ghost"))) })))) },
168
+ (if (== @entity.verdict "") null { type: stack, direction: vertical, gap: xs, children: [
169
+ { type: badge, variant: (if (== @entity.verdict "correct") "success" "error"), label: (if (== @entity.verdict "correct") "Correct" "Incorrect") },
170
+ { type: typography, variant: body, content: @entity.feedback }
171
+ ] }),
172
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
173
+ { type: button, action: ADVANCE, label: @config.advanceLabel, icon: "chevron-right", variant: primary, disabled: (== @entity.selectedCategory "") },
174
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: ghost, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Auto", icon: "play", variant: ghost, aria-pressed: "false" }),
175
+ (if (> @config.passScore 0) { type: typography, variant: caption, color: muted, content: (str/concat "Pass at " @config.passScore) } null)
176
+ ] }
168
177
  ])
169
178
  )
170
179
  (do
@@ -177,13 +186,24 @@ orbital LearnClassifyOrbital {
177
186
  padding: md
178
187
  children: [
179
188
  { type: stack, direction: vertical, gap: md, children: [
180
- { type: typography, variant: h4, content: @entity.title },
189
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
190
+ { type: stack, direction: vertical, gap: xs, children: [
191
+ { type: typography, variant: h4, content: @entity.title },
192
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
193
+ ] },
194
+ { type: badge, label: (if @entity.roundComplete "Round complete" (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (str/concat "Score " @entity.score) (str/concat @entity.correctCount " / " @entity.attemptedCount " correct"))), variant: (if @entity.roundComplete "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "incorrect") "error" "neutral"))) }
195
+ ] },
181
196
  { type: progress-bar, value: (+ @entity.itemIndex 1), max: @entity.totalItems, showPercentage: false, label: (str/concat "Item " (str/concat "" (+ @entity.itemIndex 1)) " of " (str/concat "" @entity.totalItems)) },
182
197
  { type: stack, direction: vertical, gap: md, children: @entity.roundView }
183
198
  ] }
184
199
  ]
185
200
  })
186
201
 
202
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
203
+ (set @entity.active false)
204
+ (set @entity.running false)
205
+ (render-ui main null)
206
+
187
207
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
188
208
  (set @entity.active true)
189
209
  (set @entity.title @config.title)
@@ -218,8 +238,8 @@ orbital LearnClassifyOrbital {
218
238
  (plotBottom (- @config.height @marginY))
219
239
  (tierHeight (/ @plotHeight @categoryCount))
220
240
  (maxWidth (* @plotWidth 0.95))
221
- (minWidth (* @plotWidth 0.3))
222
- (widthStep (/ (- @maxWidth @minWidth) (- @categoryCount 1)))
241
+ (minWidth (- @maxWidth (* @config.taper (- @maxWidth (* @plotWidth 0.3)))))
242
+ (widthStep (/ (- @maxWidth @minWidth) (math/max 1 (- @categoryCount 1))))
223
243
  (centerX (/ @config.width 2))
224
244
  (revealed (!= @entity.selectedCategory ""))
225
245
  (rects (array/flatten (array/map (array/range @categoryCount) (fn i (let (
@@ -231,10 +251,10 @@ orbital LearnClassifyOrbital {
231
251
  (isCorrectCategory (== @categoryValue @entity.correctCategory))
232
252
  (isSelectedCategory (== @categoryValue @entity.selectedCategory))
233
253
  (fillColor (if (and @revealed @isCorrectCategory) @config.correctColor (if (and @revealed @isSelectedCategory) @config.incorrectColor (object/get @category color))))
234
- (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: @fillColor, fill: @fillColor })
254
+ (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: @fillColor, fillTone: @fillColor, fillStyle: "tint" })
235
255
  )
236
256
  (if (and @revealed @isSelectedCategory)
237
- [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: "#0f172a", fill: "transparent", lineWidth: 3, opacity: 1 } ]
257
+ [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: "ink", fillStyle: "outline", stroke: "bold", opacity: 1 } ]
238
258
  [ @baseRect ]
239
259
  )
240
260
  )))))
@@ -242,19 +262,19 @@ orbital LearnClassifyOrbital {
242
262
  (category (array/nth @config.categories @i))
243
263
  (yTop (- @plotBottom (* (+ @i 1) @tierHeight)))
244
264
  )
245
- { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), fontSize: 13, align: "center", color: "#0f172a" }))))
265
+ { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), textSize: "sm", align: "center", tone: "label" }))))
246
266
  (backdropShapes (array/concat @rects @labels))
247
267
  (summaryView [
248
268
  { type: typography, variant: h4, content: "Round Complete" },
249
269
  { type: stat-display, label: "Final Score", value: @entity.correctCount, max: @entity.attemptedCount },
250
270
  { type: stack, direction: vertical, gap: xs, children: (array/map @entity.categoryStats (fn s { type: typography, variant: body, content: (str/concat (object/get @s label) ": " (str/concat "" (object/get @s correct)) "/" (str/concat "" (object/get @s attempted))) })) },
251
- { type: button, action: RETRY, label: @config.retryLabel, variant: primary }
271
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: primary }
252
272
  ])
253
273
  (itemView [
254
274
  { type: card, variant: bordered, padding: md, children: (array/concat [
255
275
  { type: typography, variant: body, content: @entity.itemText }
256
276
  ] (if (and @config.showHint (!= @entity.itemHint "")) [ { type: typography, variant: caption, color: muted, content: @entity.itemHint } ] [])) },
257
- { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate },
277
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: TOGGLE_RUN, ArrowRight: ADVANCE, KeyR: RETRY } },
258
278
  { type: stack, direction: horizontal, gap: xs, children: (let (
259
279
  (itemOptions (object/get (array/nth @config.items @entity.itemIndex) options []))
260
280
  ) (if (> (array/len @itemOptions) 0)
@@ -278,7 +298,13 @@ orbital LearnClassifyOrbital {
278
298
  padding: md
279
299
  children: [
280
300
  { type: stack, direction: vertical, gap: md, children: [
281
- { type: typography, variant: h4, content: @entity.title },
301
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
302
+ { type: stack, direction: vertical, gap: xs, children: [
303
+ { type: typography, variant: h4, content: @entity.title },
304
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
305
+ ] },
306
+ { type: badge, label: (if @entity.roundComplete "Round complete" (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (str/concat "Score " @entity.score) (str/concat @entity.correctCount " / " @entity.attemptedCount " correct"))), variant: (if @entity.roundComplete "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "incorrect") "error" "neutral"))) }
307
+ ] },
282
308
  { type: progress-bar, value: (+ @entity.itemIndex 1), max: @entity.totalItems, showPercentage: false, label: (str/concat "Item " (str/concat "" (+ @entity.itemIndex 1)) " of " (str/concat "" @entity.totalItems)) },
283
309
  { type: stack, direction: vertical, gap: md, children: @entity.roundView }
284
310
  ] }
@@ -313,8 +339,8 @@ orbital LearnClassifyOrbital {
313
339
  (plotBottom (- @config.height @marginY))
314
340
  (tierHeight (/ @plotHeight @categoryCount))
315
341
  (maxWidth (* @plotWidth 0.95))
316
- (minWidth (* @plotWidth 0.3))
317
- (widthStep (/ (- @maxWidth @minWidth) (- @categoryCount 1)))
342
+ (minWidth (- @maxWidth (* @config.taper (- @maxWidth (* @plotWidth 0.3)))))
343
+ (widthStep (/ (- @maxWidth @minWidth) (math/max 1 (- @categoryCount 1))))
318
344
  (centerX (/ @config.width 2))
319
345
  (revealed (!= @entity.selectedCategory ""))
320
346
  (rects (array/flatten (array/map (array/range @categoryCount) (fn i (let (
@@ -326,10 +352,10 @@ orbital LearnClassifyOrbital {
326
352
  (isCorrectCategory (== @categoryValue @entity.correctCategory))
327
353
  (isSelectedCategory (== @categoryValue @entity.selectedCategory))
328
354
  (fillColor (if (and @revealed @isCorrectCategory) @config.correctColor (if (and @revealed @isSelectedCategory) @config.incorrectColor (object/get @category color))))
329
- (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: @fillColor, fill: @fillColor })
355
+ (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: @fillColor, fillTone: @fillColor, fillStyle: "tint" })
330
356
  )
331
357
  (if (and @revealed @isSelectedCategory)
332
- [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: "#0f172a", fill: "transparent", lineWidth: 3, opacity: 1 } ]
358
+ [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: "ink", fillStyle: "outline", stroke: "bold", opacity: 1 } ]
333
359
  [ @baseRect ]
334
360
  )
335
361
  )))))
@@ -337,19 +363,19 @@ orbital LearnClassifyOrbital {
337
363
  (category (array/nth @config.categories @i))
338
364
  (yTop (- @plotBottom (* (+ @i 1) @tierHeight)))
339
365
  )
340
- { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), fontSize: 13, align: "center", color: "#0f172a" }))))
366
+ { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), textSize: "sm", align: "center", tone: "label" }))))
341
367
  (backdropShapes (array/concat @rects @labels))
342
368
  (summaryView [
343
369
  { type: typography, variant: h4, content: "Round Complete" },
344
370
  { type: stat-display, label: "Final Score", value: @entity.correctCount, max: @entity.attemptedCount },
345
371
  { type: stack, direction: vertical, gap: xs, children: (array/map @entity.categoryStats (fn s { type: typography, variant: body, content: (str/concat (object/get @s label) ": " (str/concat "" (object/get @s correct)) "/" (str/concat "" (object/get @s attempted))) })) },
346
- { type: button, action: RETRY, label: @config.retryLabel, variant: primary }
372
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: primary }
347
373
  ])
348
374
  (itemView [
349
375
  { type: card, variant: bordered, padding: md, children: (array/concat [
350
376
  { type: typography, variant: body, content: @entity.itemText }
351
377
  ] (if (and @config.showHint (!= @entity.itemHint "")) [ { type: typography, variant: caption, color: muted, content: @entity.itemHint } ] [])) },
352
- { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate },
378
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: TOGGLE_RUN, ArrowRight: ADVANCE, KeyR: RETRY } },
353
379
  { type: stack, direction: horizontal, gap: xs, children: (let (
354
380
  (itemOptions (object/get (array/nth @config.items @entity.itemIndex) options []))
355
381
  ) (if (> (array/len @itemOptions) 0)
@@ -373,7 +399,13 @@ orbital LearnClassifyOrbital {
373
399
  padding: md
374
400
  children: [
375
401
  { type: stack, direction: vertical, gap: md, children: [
376
- { type: typography, variant: h4, content: @entity.title },
402
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
403
+ { type: stack, direction: vertical, gap: xs, children: [
404
+ { type: typography, variant: h4, content: @entity.title },
405
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
406
+ ] },
407
+ { type: badge, label: (if @entity.roundComplete "Round complete" (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (str/concat "Score " @entity.score) (str/concat @entity.correctCount " / " @entity.attemptedCount " correct"))), variant: (if @entity.roundComplete "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "incorrect") "error" "neutral"))) }
408
+ ] },
377
409
  { type: progress-bar, value: (+ @entity.itemIndex 1), max: @entity.totalItems, showPercentage: false, label: (str/concat "Item " (str/concat "" (+ @entity.itemIndex 1)) " of " (str/concat "" @entity.totalItems)) },
378
410
  { type: stack, direction: vertical, gap: md, children: @entity.roundView }
379
411
  ] }
@@ -419,8 +451,8 @@ orbital LearnClassifyOrbital {
419
451
  (plotBottom (- @config.height @marginY))
420
452
  (tierHeight (/ @plotHeight @categoryCount))
421
453
  (maxWidth (* @plotWidth 0.95))
422
- (minWidth (* @plotWidth 0.3))
423
- (widthStep (/ (- @maxWidth @minWidth) (- @categoryCount 1)))
454
+ (minWidth (- @maxWidth (* @config.taper (- @maxWidth (* @plotWidth 0.3)))))
455
+ (widthStep (/ (- @maxWidth @minWidth) (math/max 1 (- @categoryCount 1))))
424
456
  (centerX (/ @config.width 2))
425
457
  (revealed (!= @entity.selectedCategory ""))
426
458
  (rects (array/flatten (array/map (array/range @categoryCount) (fn i (let (
@@ -432,10 +464,10 @@ orbital LearnClassifyOrbital {
432
464
  (isCorrectCategory (== @categoryValue @entity.correctCategory))
433
465
  (isSelectedCategory (== @categoryValue @entity.selectedCategory))
434
466
  (fillColor (if (and @revealed @isCorrectCategory) @config.correctColor (if (and @revealed @isSelectedCategory) @config.incorrectColor (object/get @category color))))
435
- (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: @fillColor, fill: @fillColor })
467
+ (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: @fillColor, fillTone: @fillColor, fillStyle: "tint" })
436
468
  )
437
469
  (if (and @revealed @isSelectedCategory)
438
- [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: "#0f172a", fill: "transparent", lineWidth: 3, opacity: 1 } ]
470
+ [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: "ink", fillStyle: "outline", stroke: "bold", opacity: 1 } ]
439
471
  [ @baseRect ]
440
472
  )
441
473
  )))))
@@ -443,19 +475,19 @@ orbital LearnClassifyOrbital {
443
475
  (category (array/nth @config.categories @i))
444
476
  (yTop (- @plotBottom (* (+ @i 1) @tierHeight)))
445
477
  )
446
- { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), fontSize: 13, align: "center", color: "#0f172a" }))))
478
+ { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), textSize: "sm", align: "center", tone: "label" }))))
447
479
  (backdropShapes (array/concat @rects @labels))
448
480
  (summaryView [
449
481
  { type: typography, variant: h4, content: "Round Complete" },
450
482
  { type: stat-display, label: "Final Score", value: @entity.correctCount, max: @entity.attemptedCount },
451
483
  { type: stack, direction: vertical, gap: xs, children: (array/map @entity.categoryStats (fn s { type: typography, variant: body, content: (str/concat (object/get @s label) ": " (str/concat "" (object/get @s correct)) "/" (str/concat "" (object/get @s attempted))) })) },
452
- { type: button, action: RETRY, label: @config.retryLabel, variant: primary }
484
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: primary }
453
485
  ])
454
486
  (itemView [
455
487
  { type: card, variant: bordered, padding: md, children: (array/concat [
456
488
  { type: typography, variant: body, content: @entity.itemText }
457
489
  ] (if (and @config.showHint (!= @entity.itemHint "")) [ { type: typography, variant: caption, color: muted, content: @entity.itemHint } ] [])) },
458
- { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate },
490
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: TOGGLE_RUN, ArrowRight: ADVANCE, KeyR: RETRY } },
459
491
  { type: stack, direction: horizontal, gap: xs, children: (let (
460
492
  (itemOptions (object/get (array/nth @config.items @entity.itemIndex) options []))
461
493
  ) (if (> (array/len @itemOptions) 0)
@@ -479,7 +511,13 @@ orbital LearnClassifyOrbital {
479
511
  padding: md
480
512
  children: [
481
513
  { type: stack, direction: vertical, gap: md, children: [
482
- { type: typography, variant: h4, content: @entity.title },
514
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
515
+ { type: stack, direction: vertical, gap: xs, children: [
516
+ { type: typography, variant: h4, content: @entity.title },
517
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
518
+ ] },
519
+ { type: badge, label: (if @entity.roundComplete "Round complete" (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (str/concat "Score " @entity.score) (str/concat @entity.correctCount " / " @entity.attemptedCount " correct"))), variant: (if @entity.roundComplete "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "incorrect") "error" "neutral"))) }
520
+ ] },
483
521
  { type: progress-bar, value: (+ @entity.itemIndex 1), max: @entity.totalItems, showPercentage: false, label: (str/concat "Item " (str/concat "" (+ @entity.itemIndex 1)) " of " (str/concat "" @entity.totalItems)) },
484
522
  { type: stack, direction: vertical, gap: md, children: @entity.roundView }
485
523
  ] }
@@ -519,8 +557,8 @@ orbital LearnClassifyOrbital {
519
557
  (plotBottom (- @config.height @marginY))
520
558
  (tierHeight (/ @plotHeight @categoryCount))
521
559
  (maxWidth (* @plotWidth 0.95))
522
- (minWidth (* @plotWidth 0.3))
523
- (widthStep (/ (- @maxWidth @minWidth) (- @categoryCount 1)))
560
+ (minWidth (- @maxWidth (* @config.taper (- @maxWidth (* @plotWidth 0.3)))))
561
+ (widthStep (/ (- @maxWidth @minWidth) (math/max 1 (- @categoryCount 1))))
524
562
  (centerX (/ @config.width 2))
525
563
  (revealed (!= @entity.selectedCategory ""))
526
564
  (rects (array/flatten (array/map (array/range @categoryCount) (fn i (let (
@@ -532,10 +570,10 @@ orbital LearnClassifyOrbital {
532
570
  (isCorrectCategory (== @categoryValue @entity.correctCategory))
533
571
  (isSelectedCategory (== @categoryValue @entity.selectedCategory))
534
572
  (fillColor (if (and @revealed @isCorrectCategory) @config.correctColor (if (and @revealed @isSelectedCategory) @config.incorrectColor (object/get @category color))))
535
- (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: @fillColor, fill: @fillColor })
573
+ (baseRect { type: rect, id: (str/concat "category-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: @fillColor, fillTone: @fillColor, fillStyle: "tint" })
536
574
  )
537
575
  (if (and @revealed @isSelectedCategory)
538
- [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, color: "#0f172a", fill: "transparent", lineWidth: 3, opacity: 1 } ]
576
+ [ @baseRect, { type: rect, id: (str/concat "category-pick-ring-" @i), x: @x, y: @yTop, width: @w, height: @tierHeight, tone: "ink", fillStyle: "outline", stroke: "bold", opacity: 1 } ]
539
577
  [ @baseRect ]
540
578
  )
541
579
  )))))
@@ -543,19 +581,19 @@ orbital LearnClassifyOrbital {
543
581
  (category (array/nth @config.categories @i))
544
582
  (yTop (- @plotBottom (* (+ @i 1) @tierHeight)))
545
583
  )
546
- { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), fontSize: 13, align: "center", color: "#0f172a" }))))
584
+ { type: text, id: (str/concat "category-label-" @i), x: @centerX, y: (+ @yTop (/ @tierHeight 2) 5), text: (object/get @category label), textSize: "sm", align: "center", tone: "label" }))))
547
585
  (backdropShapes (array/concat @rects @labels))
548
586
  (summaryView [
549
587
  { type: typography, variant: h4, content: "Round Complete" },
550
588
  { type: stat-display, label: "Final Score", value: @entity.correctCount, max: @entity.attemptedCount },
551
589
  { type: stack, direction: vertical, gap: xs, children: (array/map @entity.categoryStats (fn s { type: typography, variant: body, content: (str/concat (object/get @s label) ": " (str/concat "" (object/get @s correct)) "/" (str/concat "" (object/get @s attempted))) })) },
552
- { type: button, action: RETRY, label: @config.retryLabel, variant: primary }
590
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: primary }
553
591
  ])
554
592
  (itemView [
555
593
  { type: card, variant: bordered, padding: md, children: (array/concat [
556
594
  { type: typography, variant: body, content: @entity.itemText }
557
595
  ] (if (and @config.showHint (!= @entity.itemHint "")) [ { type: typography, variant: caption, color: muted, content: @entity.itemHint } ] [])) },
558
- { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate },
596
+ { type: biology-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, nodes: [], edges: [], shapes: @backdropShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: TOGGLE_RUN, ArrowRight: ADVANCE, KeyR: RETRY } },
559
597
  { type: stack, direction: horizontal, gap: xs, children: (let (
560
598
  (itemOptions (object/get (array/nth @config.items @entity.itemIndex) options []))
561
599
  ) (if (> (array/len @itemOptions) 0)
@@ -579,7 +617,13 @@ orbital LearnClassifyOrbital {
579
617
  padding: md
580
618
  children: [
581
619
  { type: stack, direction: vertical, gap: md, children: [
582
- { type: typography, variant: h4, content: @entity.title },
620
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
621
+ { type: stack, direction: vertical, gap: xs, children: [
622
+ { type: typography, variant: h4, content: @entity.title },
623
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
624
+ ] },
625
+ { type: badge, label: (if @entity.roundComplete "Round complete" (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (str/concat "Score " @entity.score) (str/concat @entity.correctCount " / " @entity.attemptedCount " correct"))), variant: (if @entity.roundComplete "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "incorrect") "error" "neutral"))) }
626
+ ] },
583
627
  { type: progress-bar, value: (+ @entity.itemIndex 1), max: @entity.totalItems, showPercentage: false, label: (str/concat "Item " (str/concat "" (+ @entity.itemIndex 1)) " of " (str/concat "" @entity.totalItems)) },
584
628
  { type: stack, direction: vertical, gap: md, children: @entity.roundView }
585
629
  ] }
@@ -595,7 +639,13 @@ orbital LearnClassifyOrbital {
595
639
  padding: md
596
640
  children: [
597
641
  { type: stack, direction: vertical, gap: md, children: [
598
- { type: typography, variant: h4, content: @entity.title },
642
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
643
+ { type: stack, direction: vertical, gap: xs, children: [
644
+ { type: typography, variant: h4, content: @entity.title },
645
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
646
+ ] },
647
+ { type: badge, label: (if @entity.roundComplete "Round complete" (if (or (!= @config.pointsPerCorrect 1) (> @config.penaltyPerMiss 0)) (str/concat "Score " @entity.score) (str/concat @entity.correctCount " / " @entity.attemptedCount " correct"))), variant: (if @entity.roundComplete "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "incorrect") "error" "neutral"))) }
648
+ ] },
599
649
  { type: progress-bar, value: (+ @entity.itemIndex 1), max: @entity.totalItems, showPercentage: false, label: (str/concat "Item " (str/concat "" (+ @entity.itemIndex 1)) " of " (str/concat "" @entity.totalItems)) },
600
650
  { type: stack, direction: vertical, gap: md, children: @entity.roundView }
601
651
  ] }
@@ -669,6 +719,10 @@ orbital LearnClassifyOrbital {
669
719
  @label "Title"
670
720
  @description "Title shown above the instrument."
671
721
  @tier "presentation"
722
+ summary : string = ""
723
+ @label "Concept"
724
+ @description "One line under the title saying what is being classified (keep it under ~65 characters). Empty shows no line."
725
+ @tier "presentation"
672
726
  width : number = 640
673
727
  @label "Width"
674
728
  @description "Canvas width in pixels."
@@ -677,8 +731,13 @@ orbital LearnClassifyOrbital {
677
731
  @label "Height"
678
732
  @description "Canvas height in pixels."
679
733
  @tier "presentation"
680
- backgroundColor : string = "#f8fafc"
734
+ taper : number = 0
735
+ @label "Taper"
736
+ @description "How far the category rungs narrow toward the top: 0 = equal-width rungs (no implied order), 1 = a full pyramid for hierarchies (needs, levels). Long category names need 0."
737
+ @tier "presentation"
738
+ backgroundColor : string = ""
681
739
  @label "Background Color"
740
+ @description "Canvas ground (tone name, token or colour); empty uses the theme's drawing ground."
682
741
  @tier "presentation"
683
742
  interactive : boolean = false
684
743
  @label "Interactive"
@@ -697,18 +756,18 @@ orbital LearnClassifyOrbital {
697
756
  @synonyms "item, prompt, scenario, situation, case, example, question"
698
757
  @tier "domain"
699
758
  categories : [LearnClassifyOption] = [
700
- { value: "a", label: "Category A", color: "#94a3b8" },
701
- { value: "b", label: "Category B", color: "#64748b" }
759
+ { value: "a", label: "Category A", color: "series-1" },
760
+ { value: "b", label: "Category B", color: "series-2" }
702
761
  ]
703
762
  @label "Categories"
704
763
  @description "Ordered bottom-to-top roster of category buckets (value/label/color) used to render the stacked backdrop and the pick buttons; order determines the backdrop's stacking, widest at the bottom to narrowest at the top."
705
764
  @synonyms "category, bucket, class, tier, level, classification, roster"
706
765
  @tier "domain"
707
- correctColor : string = "#16a34a"
766
+ correctColor : string = "success"
708
767
  @label "Correct Highlight Color"
709
768
  @description "Fill color for the correct category once the learner has picked."
710
769
  @tier "presentation"
711
- incorrectColor : string = "#dc2626"
770
+ incorrectColor : string = "error"
712
771
  @label "Incorrect Highlight Color"
713
772
  @description "Fill color for the learner's picked category when it does not match the correct category."
714
773
  @tier "presentation"
@@ -92,8 +92,14 @@ orbital CompareOrbital {
92
92
  padding: md
93
93
  children: [
94
94
  { type: stack, direction: vertical, gap: md, children: [
95
- { type: typography, variant: h4, content: @entity.title },
96
- { type: typography, variant: body, content: @config.compareText },
95
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
96
+ { type: stack, direction: vertical, gap: xs, children: [
97
+ { type: typography, variant: h4, content: @entity.title },
98
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
99
+ ] },
100
+ { type: badge, label: (str/concat "Δ " (math/round @entity.delta 3)), variant: neutral }
101
+ ] },
102
+ { 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 },
97
103
  { type: stack, direction: horizontal, gap: lg, children: [
98
104
  { type: stack, direction: vertical, gap: xs, children: [
99
105
  { type: typography, variant: caption, content: (str/concat @config.leftLabel " — " @config.paramALabel ": " (math/round @entity.leftA 2)) },
@@ -108,12 +114,16 @@ orbital CompareOrbital {
108
114
  { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.rightB, showTooltip: true, action: "SET_PARAM", actionPayload: { side: "right", which: "B" } }
109
115
  ] }
110
116
  ] },
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: (str/concat "At x=" (math/round @entity.xMid 2) ": " @config.leftLabel " = " (math/round @entity.leftMid 3) ", " @config.rightLabel " = " (math/round @entity.rightMid 3) ", Δ = " (math/round @entity.delta 3)) }
117
+ { type: typography, variant: body, content: @config.compareText },
118
+ { type: typography, variant: caption, color: muted, content: (str/concat "At x=" (math/round @entity.xMid 2) ": " @config.leftLabel " = " (math/round @entity.leftMid 3) ", " @config.rightLabel " = " (math/round @entity.rightMid 3) ", Δ = " (math/round @entity.delta 3)) }
113
119
  ] }
114
120
  ]
115
121
  })
116
122
 
123
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
124
+ (set @entity.active false)
125
+ (render-ui main null)
126
+
117
127
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
118
128
  (let (
119
129
  (kind @config.curveKind)
@@ -173,8 +183,14 @@ orbital CompareOrbital {
173
183
  padding: md
174
184
  children: [
175
185
  { type: stack, direction: vertical, gap: md, children: [
176
- { type: typography, variant: h4, content: @entity.title },
177
- { type: typography, variant: body, content: @config.compareText },
186
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
187
+ { type: stack, direction: vertical, gap: xs, children: [
188
+ { type: typography, variant: h4, content: @entity.title },
189
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
190
+ ] },
191
+ { type: badge, label: (str/concat "Δ " (math/round @entity.delta 3)), variant: neutral }
192
+ ] },
193
+ { 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 },
178
194
  { type: stack, direction: horizontal, gap: lg, children: [
179
195
  { type: stack, direction: vertical, gap: xs, children: [
180
196
  { type: typography, variant: caption, content: (str/concat @config.leftLabel " — " @config.paramALabel ": " (math/round @entity.leftA 2)) },
@@ -189,8 +205,8 @@ orbital CompareOrbital {
189
205
  { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.rightB, showTooltip: true, action: "SET_PARAM", actionPayload: { side: "right", which: "B" } }
190
206
  ] }
191
207
  ] },
192
- { 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 },
193
- { type: typography, variant: body, content: (str/concat "At x=" (math/round @entity.xMid 2) ": " @config.leftLabel " = " (math/round @entity.leftMid 3) ", " @config.rightLabel " = " (math/round @entity.rightMid 3) ", Δ = " (math/round @entity.delta 3)) }
208
+ { type: typography, variant: body, content: @config.compareText },
209
+ { type: typography, variant: caption, color: muted, content: (str/concat "At x=" (math/round @entity.xMid 2) ": " @config.leftLabel " = " (math/round @entity.leftMid 3) ", " @config.rightLabel " = " (math/round @entity.rightMid 3) ", Δ = " (math/round @entity.delta 3)) }
194
210
  ] }
195
211
  ]
196
212
  })
@@ -249,8 +265,14 @@ orbital CompareOrbital {
249
265
  padding: md
250
266
  children: [
251
267
  { type: stack, direction: vertical, gap: md, children: [
252
- { type: typography, variant: h4, content: @entity.title },
253
- { type: typography, variant: body, content: @config.compareText },
268
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
269
+ { type: stack, direction: vertical, gap: xs, children: [
270
+ { type: typography, variant: h4, content: @entity.title },
271
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
272
+ ] },
273
+ { type: badge, label: (str/concat "Δ " (math/round @entity.delta 3)), variant: neutral }
274
+ ] },
275
+ { type: math-canvas, width: @config.width, height: @config.height, title: "", xMin: @config.xMin, xMax: @config.xMax, yMin: @entity.yMin, yMax: @entity.yMax, showAxes: @config.showAxes, showGrid: @config.showGrid, gridStep: @config.gridStep, curves: @entity.curves, points: [], vectors: [], shapes: [], interactive: false, animate: false },
254
276
  { type: stack, direction: horizontal, gap: lg, children: [
255
277
  { type: stack, direction: vertical, gap: xs, children: [
256
278
  { type: typography, variant: caption, content: (str/concat @config.leftLabel " — " @config.paramALabel ": " (math/round @entity.leftA 2)) },
@@ -265,8 +287,8 @@ orbital CompareOrbital {
265
287
  { type: range-slider, min: @config.paramBMin, max: @config.paramBMax, step: @config.paramBStep, value: @entity.rightB, showTooltip: true, action: "SET_PARAM", actionPayload: { side: "right", which: "B" } }
266
288
  ] }
267
289
  ] },
268
- { 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 },
269
- { type: typography, variant: body, content: (str/concat "At x=" (math/round @entity.xMid 2) ": " @config.leftLabel " = " (math/round @entity.leftMid 3) ", " @config.rightLabel " = " (math/round @entity.rightMid 3) ", Δ = " (math/round @entity.delta 3)) }
290
+ { type: typography, variant: body, content: @config.compareText },
291
+ { type: typography, variant: caption, color: muted, content: (str/concat "At x=" (math/round @entity.xMid 2) ": " @config.leftLabel " = " (math/round @entity.leftMid 3) ", " @config.rightLabel " = " (math/round @entity.rightMid 3) ", Δ = " (math/round @entity.delta 3)) }
270
292
  ] }
271
293
  ]
272
294
  })
@@ -310,6 +332,10 @@ orbital CompareOrbital {
310
332
  @label "Title"
311
333
  @description "Title shown above the canvas."
312
334
  @tier "presentation"
335
+ summary : string = ""
336
+ @label "Concept"
337
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
338
+ @tier "presentation"
313
339
  compareText : string = "Tune each side's parameters and watch how the two curves diverge."
314
340
  @label "Compare Text"
315
341
  @description "One-sentence statement of what is being contrasted."
@@ -363,10 +389,10 @@ orbital CompareOrbital {
363
389
  rightBDefault : number = 1.5
364
390
  @label "Right B Default"
365
391
  @tier "domain"
366
- leftColor : string = "#3b82f6"
392
+ leftColor : string = "series-1"
367
393
  @label "Left Color"
368
394
  @tier "presentation"
369
- rightColor : string = "#f59e0b"
395
+ rightColor : string = "series-2"
370
396
  @label "Right Color"
371
397
  @tier "presentation"
372
398
  xMin : number = 0