@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
@@ -7,12 +7,13 @@ orbital HotspotOrbital {
7
7
  x : number!
8
8
  y : number!
9
9
  radius : number
10
- color : string!
11
- fill : string
10
+ tone : string
11
+ fillTone : string
12
+ fillStyle : string
13
+ stroke : string
14
+ textSize : string
12
15
  text : string
13
- fontSize : number
14
16
  align : "left" | "center" | "right"
15
- lineWidth : number
16
17
  }
17
18
  type HotspotShape = {
18
19
  id : string!
@@ -53,8 +54,8 @@ orbital HotspotOrbital {
53
54
  INIT -> idle when (== @config.matchMode "")
54
55
  (let (
55
56
  (first (array/nth @config.prompts 0))
56
- (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), color: (object/get @s color "#3b82f6"), fill: (object/get @s color "#3b82f6") })))
57
- (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), fontSize: 12, align: "center", color: "#0f172a" })))
57
+ (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), tone: (object/get @s color "series-1"), fillTone: (object/get @s color "series-1"), fillStyle: "solid" })))
58
+ (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), textSize: "xs", align: "center", tone: "label" })))
58
59
  )
59
60
  (do
60
61
  (set @entity.active true)
@@ -74,32 +75,42 @@ orbital HotspotOrbital {
74
75
  { type: card, variant: bordered, padding: md, children: [
75
76
  { type: typography, variant: body, content: @entity.promptText }
76
77
  ] },
77
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE" },
78
+ { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE", keyMap: { Space: NEXT, ArrowRight: NEXT, KeyR: RETRY } },
78
79
  { type: stack, direction: horizontal, gap: xs, children: [
79
- { type: typography, variant: caption, content: @entity.feedback }
80
+ { type: typography, variant: body, content: @entity.feedback }
80
81
  ] },
81
- { type: stack, direction: horizontal, gap: sm, children: [
82
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if @entity.found false true) }
83
- ] },
84
- { type: stat-display, label: "First-try score", value: @entity.score, max: @entity.totalPrompts }
82
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
83
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if @entity.found false true) },
84
+ { type: typography, variant: caption, color: muted, content: (str/concat "First-try " @entity.score " of " @entity.totalPrompts) }
85
+ ] }
85
86
  ])))
86
87
  (render-ui main {
87
88
  type: box
88
89
  padding: md
89
90
  children: [
90
91
  { type: stack, direction: vertical, gap: md, children: [
91
- { type: typography, variant: h4, content: @entity.title },
92
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
93
+ { type: stack, direction: vertical, gap: xs, children: [
94
+ { type: typography, variant: h4, content: @entity.title },
95
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
96
+ ] },
97
+ { type: badge, label: (if @entity.done "Round complete" (if @entity.found "Correct" (if (> @entity.tries 0) "Try again" (str/concat "Score " @entity.score " / " @entity.totalPrompts)))), variant: (if @entity.done "primary" (if @entity.found "success" (if (> @entity.tries 0) "error" "neutral"))) }
98
+ ] },
92
99
  { type: progress-bar, value: (+ @entity.promptIndex 1), max: @entity.totalPrompts, showPercentage: false, label: (str/concat "Prompt " (str/concat "" (+ @entity.promptIndex 1)) " of " (str/concat "" @entity.totalPrompts)) },
93
100
  { type: stack, direction: vertical, gap: md, children: @entity.view }
94
101
  ] }
95
102
  ]
96
103
  })
97
104
 
105
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
106
+ (set @entity.active false)
107
+ (render-ui main null)
108
+
98
109
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
99
110
  (let (
100
111
  (first (array/nth @config.prompts 0))
101
- (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), color: (object/get @s color "#3b82f6"), fill: (object/get @s color "#3b82f6") })))
102
- (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), fontSize: 12, align: "center", color: "#0f172a" })))
112
+ (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), tone: (object/get @s color "series-1"), fillTone: (object/get @s color "series-1"), fillStyle: "solid" })))
113
+ (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), textSize: "xs", align: "center", tone: "label" })))
103
114
  )
104
115
  (do
105
116
  (set @entity.active true)
@@ -119,21 +130,27 @@ orbital HotspotOrbital {
119
130
  { type: card, variant: bordered, padding: md, children: [
120
131
  { type: typography, variant: body, content: @entity.promptText }
121
132
  ] },
122
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE" },
133
+ { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE", keyMap: { Space: NEXT, ArrowRight: NEXT, KeyR: RETRY } },
123
134
  { type: stack, direction: horizontal, gap: xs, children: [
124
- { type: typography, variant: caption, content: @entity.feedback }
125
- ] },
126
- { type: stack, direction: horizontal, gap: sm, children: [
127
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if @entity.found false true) }
135
+ { type: typography, variant: body, content: @entity.feedback }
128
136
  ] },
129
- { type: stat-display, label: "First-try score", value: @entity.score, max: @entity.totalPrompts }
137
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
138
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if @entity.found false true) },
139
+ { type: typography, variant: caption, color: muted, content: (str/concat "First-try " @entity.score " of " @entity.totalPrompts) }
140
+ ] }
130
141
  ])))
131
142
  (render-ui main {
132
143
  type: box
133
144
  padding: md
134
145
  children: [
135
146
  { type: stack, direction: vertical, gap: md, children: [
136
- { type: typography, variant: h4, content: @entity.title },
147
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
148
+ { type: stack, direction: vertical, gap: xs, children: [
149
+ { type: typography, variant: h4, content: @entity.title },
150
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
151
+ ] },
152
+ { type: badge, label: (if @entity.done "Round complete" (if @entity.found "Correct" (if (> @entity.tries 0) "Try again" (str/concat "Score " @entity.score " / " @entity.totalPrompts)))), variant: (if @entity.done "primary" (if @entity.found "success" (if (> @entity.tries 0) "error" "neutral"))) }
153
+ ] },
137
154
  { type: progress-bar, value: (+ @entity.promptIndex 1), max: @entity.totalPrompts, showPercentage: false, label: (str/concat "Prompt " (str/concat "" (+ @entity.promptIndex 1)) " of " (str/concat "" @entity.totalPrompts)) },
138
155
  { type: stack, direction: vertical, gap: md, children: @entity.view }
139
156
  ] }
@@ -147,10 +164,10 @@ orbital HotspotOrbital {
147
164
  (tapped (array/find @config.shapes (fn s (== (object/get @s id) @tappedId))))
148
165
  (tappedLabel (object/get @tapped label ""))
149
166
  (target (array/find @config.shapes (fn s (== (object/get @s id) @entity.targetId))))
150
- (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), color: (object/get @s color "#3b82f6"), fill: (object/get @s color "#3b82f6") })))
151
- (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), fontSize: 12, align: "center", color: "#0f172a" })))
167
+ (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), tone: (object/get @s color "series-1"), fillTone: (object/get @s color "series-1"), fillStyle: "solid" })))
168
+ (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), textSize: "xs", align: "center", tone: "label" })))
152
169
  (ring (if @hit
153
- [ { type: circle, id: "hit-ring", x: (object/get @target x), y: (object/get @target y), radius: (+ (object/get @target radius 22) 6), color: @config.hitColor, fill: "transparent", lineWidth: 4 } ]
170
+ [ { type: circle, id: "hit-ring", x: (object/get @target x), y: (object/get @target y), radius: (+ (object/get @target radius 22) 6), tone: @config.hitColor, fillStyle: "outline", stroke: "bold" } ]
154
171
  []))
155
172
  (newFound (if @hit true false))
156
173
  (newScore (if (and @hit (== @entity.tries 0)) (+ @entity.score 1) @entity.score))
@@ -162,28 +179,27 @@ orbital HotspotOrbital {
162
179
  { type: card, variant: bordered, padding: md, children: [
163
180
  { type: typography, variant: body, content: @entity.promptText }
164
181
  ] },
165
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @newCanvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE" },
182
+ { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @newCanvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE", keyMap: { Space: NEXT, ArrowRight: NEXT, KeyR: RETRY } },
166
183
  { type: stack, direction: horizontal, gap: xs, children: [
167
- { type: badge, variant: success, label: "Found" },
168
- { type: typography, variant: caption, content: @newFeedback }
169
- ] },
170
- { type: stack, direction: horizontal, gap: sm, children: [
171
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: false }
184
+ { type: typography, variant: body, content: @newFeedback }
172
185
  ] },
173
- { type: stat-display, label: "First-try score", value: @newScore, max: @entity.totalPrompts }
186
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
187
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: false },
188
+ { type: typography, variant: caption, color: muted, content: (str/concat "First-try " @newScore " of " @entity.totalPrompts) }
189
+ ] }
174
190
  ])
175
191
  (notFoundView [
176
192
  { type: card, variant: bordered, padding: md, children: [
177
193
  { type: typography, variant: body, content: @entity.promptText }
178
194
  ] },
179
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @newCanvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE" },
195
+ { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @newCanvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE", keyMap: { Space: NEXT, ArrowRight: NEXT, KeyR: RETRY } },
180
196
  { type: stack, direction: horizontal, gap: xs, children: [
181
- { type: typography, variant: caption, content: @newFeedback }
182
- ] },
183
- { type: stack, direction: horizontal, gap: sm, children: [
184
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: true }
197
+ { type: typography, variant: body, content: @newFeedback }
185
198
  ] },
186
- { type: stat-display, label: "First-try score", value: @newScore, max: @entity.totalPrompts }
199
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
200
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: true },
201
+ { type: typography, variant: caption, color: muted, content: (str/concat "First-try " @newScore " of " @entity.totalPrompts) }
202
+ ] }
187
203
  ])
188
204
  )
189
205
  (do
@@ -198,7 +214,13 @@ orbital HotspotOrbital {
198
214
  padding: md
199
215
  children: [
200
216
  { type: stack, direction: vertical, gap: md, children: [
201
- { type: typography, variant: h4, content: @entity.title },
217
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
218
+ { type: stack, direction: vertical, gap: xs, children: [
219
+ { type: typography, variant: h4, content: @entity.title },
220
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
221
+ ] },
222
+ { type: badge, label: (if @entity.done "Round complete" (if @entity.found "Correct" (if (> @entity.tries 0) "Try again" (str/concat "Score " @entity.score " / " @entity.totalPrompts)))), variant: (if @entity.done "primary" (if @entity.found "success" (if (> @entity.tries 0) "error" "neutral"))) }
223
+ ] },
202
224
  { type: progress-bar, value: (+ @entity.promptIndex 1), max: @entity.totalPrompts, showPercentage: false, label: (str/concat "Prompt " (str/concat "" (+ @entity.promptIndex 1)) " of " (str/concat "" @entity.totalPrompts)) },
203
225
  { type: stack, direction: vertical, gap: md, children: @entity.view }
204
226
  ] }
@@ -212,8 +234,8 @@ orbital HotspotOrbital {
212
234
  (nextIndex (+ @entity.promptIndex 1))
213
235
  (finished (>= @nextIndex (array/len @config.prompts)))
214
236
  (next (array/nth @config.prompts (math/min @nextIndex (- (array/len @config.prompts) 1))))
215
- (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), color: (object/get @s color "#3b82f6"), fill: (object/get @s color "#3b82f6") })))
216
- (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), fontSize: 12, align: "center", color: "#0f172a" })))
237
+ (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), tone: (object/get @s color "series-1"), fillTone: (object/get @s color "series-1"), fillStyle: "solid" })))
238
+ (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), textSize: "xs", align: "center", tone: "label" })))
217
239
  )
218
240
  (do
219
241
  (set @entity.promptIndex (if @finished @entity.promptIndex @nextIndex))
@@ -229,20 +251,20 @@ orbital HotspotOrbital {
229
251
  [
230
252
  { type: typography, variant: h4, content: "Round Complete" },
231
253
  { type: stat-display, label: "First-try score", value: @entity.score, max: @entity.totalPrompts },
232
- { type: button, action: RETRY, label: @config.retryLabel, variant: primary }
254
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: primary }
233
255
  ]
234
256
  [
235
257
  { type: card, variant: bordered, padding: md, children: [
236
258
  { type: typography, variant: body, content: @entity.promptText }
237
259
  ] },
238
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE" },
260
+ { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE", keyMap: { Space: NEXT, ArrowRight: NEXT, KeyR: RETRY } },
239
261
  { type: stack, direction: horizontal, gap: xs, children: [
240
- { type: typography, variant: caption, content: @entity.feedback }
241
- ] },
242
- { type: stack, direction: horizontal, gap: sm, children: [
243
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if @entity.found false true) }
262
+ { type: typography, variant: body, content: @entity.feedback }
244
263
  ] },
245
- { type: stat-display, label: "First-try score", value: @entity.score, max: @entity.totalPrompts }
264
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
265
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if @entity.found false true) },
266
+ { type: typography, variant: caption, color: muted, content: (str/concat "First-try " @entity.score " of " @entity.totalPrompts) }
267
+ ] }
246
268
  ])))
247
269
  )
248
270
  (render-ui main {
@@ -250,7 +272,13 @@ orbital HotspotOrbital {
250
272
  padding: md
251
273
  children: [
252
274
  { type: stack, direction: vertical, gap: md, children: [
253
- { type: typography, variant: h4, content: @entity.title },
275
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
276
+ { type: stack, direction: vertical, gap: xs, children: [
277
+ { type: typography, variant: h4, content: @entity.title },
278
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
279
+ ] },
280
+ { type: badge, label: (if @entity.done "Round complete" (if @entity.found "Correct" (if (> @entity.tries 0) "Try again" (str/concat "Score " @entity.score " / " @entity.totalPrompts)))), variant: (if @entity.done "primary" (if @entity.found "success" (if (> @entity.tries 0) "error" "neutral"))) }
281
+ ] },
254
282
  { type: progress-bar, value: (+ @entity.promptIndex 1), max: @entity.totalPrompts, showPercentage: false, label: (str/concat "Prompt " (str/concat "" (+ @entity.promptIndex 1)) " of " (str/concat "" @entity.totalPrompts)) },
255
283
  { type: stack, direction: vertical, gap: md, children: @entity.view }
256
284
  ] }
@@ -261,8 +289,8 @@ orbital HotspotOrbital {
261
289
  RETRY -> idle when (and (== @entity.active true) (== @entity.done true))
262
290
  (let (
263
291
  (first (array/nth @config.prompts 0))
264
- (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), color: (object/get @s color "#3b82f6"), fill: (object/get @s color "#3b82f6") })))
265
- (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), fontSize: 12, align: "center", color: "#0f172a" })))
292
+ (circles (array/map @config.shapes (fn s { type: circle, id: (object/get @s id), x: (object/get @s x), y: (object/get @s y), radius: (object/get @s radius 22), tone: (object/get @s color "series-1"), fillTone: (object/get @s color "series-1"), fillStyle: "solid" })))
293
+ (labels (array/map @config.shapes (fn s { type: text, id: (str/concat "label-" (object/get @s id)), x: (object/get @s x), y: (+ (object/get @s y) (object/get @s radius 22) 14), text: (object/get @s label), textSize: "xs", align: "center", tone: "label" })))
266
294
  )
267
295
  (do
268
296
  (set @entity.promptIndex 0)
@@ -279,21 +307,27 @@ orbital HotspotOrbital {
279
307
  { type: card, variant: bordered, padding: md, children: [
280
308
  { type: typography, variant: body, content: @entity.promptText }
281
309
  ] },
282
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE" },
310
+ { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.canvasShapes, interactive: true, animate: false, onShapeClick: "TAP_SHAPE", keyMap: { Space: NEXT, ArrowRight: NEXT, KeyR: RETRY } },
283
311
  { type: stack, direction: horizontal, gap: xs, children: [
284
- { type: typography, variant: caption, content: @entity.feedback }
312
+ { type: typography, variant: body, content: @entity.feedback }
285
313
  ] },
286
- { type: stack, direction: horizontal, gap: sm, children: [
287
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if @entity.found false true) }
288
- ] },
289
- { type: stat-display, label: "First-try score", value: @entity.score, max: @entity.totalPrompts }
314
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
315
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if @entity.found false true) },
316
+ { type: typography, variant: caption, color: muted, content: (str/concat "First-try " @entity.score " of " @entity.totalPrompts) }
317
+ ] }
290
318
  ])))
291
319
  (render-ui main {
292
320
  type: box
293
321
  padding: md
294
322
  children: [
295
323
  { type: stack, direction: vertical, gap: md, children: [
296
- { type: typography, variant: h4, content: @entity.title },
324
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
325
+ { type: stack, direction: vertical, gap: xs, children: [
326
+ { type: typography, variant: h4, content: @entity.title },
327
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
328
+ ] },
329
+ { type: badge, label: (if @entity.done "Round complete" (if @entity.found "Correct" (if (> @entity.tries 0) "Try again" (str/concat "Score " @entity.score " / " @entity.totalPrompts)))), variant: (if @entity.done "primary" (if @entity.found "success" (if (> @entity.tries 0) "error" "neutral"))) }
330
+ ] },
297
331
  { type: progress-bar, value: (+ @entity.promptIndex 1), max: @entity.totalPrompts, showPercentage: false, label: (str/concat "Prompt " (str/concat "" (+ @entity.promptIndex 1)) " of " (str/concat "" @entity.totalPrompts)) },
298
332
  { type: stack, direction: vertical, gap: md, children: @entity.view }
299
333
  ] }
@@ -362,12 +396,16 @@ orbital HotspotOrbital {
362
396
  @label "Title"
363
397
  @description "Title shown above the canvas."
364
398
  @tier "presentation"
399
+ summary : string = ""
400
+ @label "Concept"
401
+ @description "One line under the title saying what is being identified (keep it under ~65 characters). Empty shows no line."
402
+ @tier "presentation"
365
403
  shapes : [HotspotShape] = [
366
- { id: "nucleus", label: "Nucleus", x: 300, y: 170, radius: 34, color: "#8b5cf6" },
367
- { id: "membrane", label: "Membrane", x: 130, y: 120, radius: 26, color: "#3b82f6" },
368
- { id: "mitochondrion", label: "Mitochondrion", x: 460, y: 120, radius: 24, color: "#f59e0b" },
369
- { id: "ribosome", label: "Ribosome", x: 200, y: 280, radius: 18, color: "#16a34a" },
370
- { id: "vacuole", label: "Vacuole", x: 420, y: 280, radius: 28, color: "#06b6d4" }
404
+ { id: "nucleus", label: "Nucleus", x: 300, y: 170, radius: 34, color: "series-1" },
405
+ { id: "membrane", label: "Membrane", x: 130, y: 120, radius: 26, color: "series-2" },
406
+ { id: "mitochondrion", label: "Mitochondrion", x: 460, y: 120, radius: 24, color: "series-3" },
407
+ { id: "ribosome", label: "Ribosome", x: 200, y: 280, radius: 18, color: "series-4" },
408
+ { id: "vacuole", label: "Vacuole", x: 420, y: 280, radius: 28, color: "series-5" }
371
409
  ]
372
410
  @label "Hotspots"
373
411
  @description "The labeled parts of the scene; each carries an id, display label, canvas position, radius, and color. Realizations declare their own subject's parts here."
@@ -380,7 +418,7 @@ orbital HotspotOrbital {
380
418
  @label "Prompts"
381
419
  @description "The question roster; each prompt names a target hotspot id and an explanation revealed on a hit."
382
420
  @tier "domain"
383
- hitColor : string = "#16a34a"
421
+ hitColor : string = "success"
384
422
  @label "Hit Ring Color"
385
423
  @description "Color of the highlight ring drawn around the target once found."
386
424
  @tier "presentation"
@@ -396,7 +434,7 @@ orbital HotspotOrbital {
396
434
  height : number = 380
397
435
  @label "Height"
398
436
  @tier "presentation"
399
- backgroundColor : string = "#f8fafc"
437
+ backgroundColor : string = ""
400
438
  @label "Background Color"
401
439
  @tier "presentation"
402
440
  }