@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
@@ -24,6 +24,7 @@ orbital SpeedDrillOrbital {
24
24
  options : [string]
25
25
  correctIndex : number = 0
26
26
  feedback : string
27
+ pickedIndex : number = -1
27
28
  view : [node]
28
29
  }
29
30
 
@@ -43,6 +44,7 @@ orbital SpeedDrillOrbital {
43
44
  (set @entity.remaining (* @config.timeLimit 10))
44
45
  (set @entity.running true)
45
46
  (set @entity.verdict "")
47
+ (set @entity.pickedIndex -1)
46
48
  (set @entity.score 0)
47
49
  (set @entity.streak 0)
48
50
  (set @entity.bestStreak 0)
@@ -61,23 +63,37 @@ orbital SpeedDrillOrbital {
61
63
  padding: md
62
64
  children: [
63
65
  { type: stack, direction: vertical, gap: md, children: [
64
- { type: typography, variant: h4, content: @entity.title },
66
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
67
+ { type: stack, direction: vertical, gap: xs, children: [
68
+ { type: typography, variant: h4, content: @entity.title },
69
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
70
+ ] },
71
+ { type: badge, label: (if @entity.done "Round complete" (str/concat @entity.score " pts · streak " @entity.streak)), variant: (if @entity.done "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "") "neutral" "error"))) }
72
+ ] },
65
73
  { 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)) },
66
- { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
74
+ { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), variant: (if (< @entity.remaining (* @config.timeLimit 3)) "warning" "primary"), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
67
75
  { type: stack, direction: vertical, gap: md, children: @entity.view },
68
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
69
- { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), variant: secondary, size: sm, disabled: (if @entity.running false true) })) },
70
- { type: typography, variant: caption, content: @entity.feedback },
71
- { type: stack, direction: horizontal, gap: sm, children: [
72
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
73
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost, disabled: (if @entity.done false true) }
74
- ] },
75
- { type: stat-display, label: "Score", value: @entity.score, max: 0 },
76
- { type: typography, variant: caption, content: (str/concat "Streak: " (str/concat "" @entity.streak) " · best: " (str/concat "" @entity.bestStreak)) }
76
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
77
+ { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), size: lg, disabled: (not @entity.running), variant: (if @entity.running "secondary" (if (== @i @entity.correctIndex) "success" (if (== @i @entity.pickedIndex) "danger" "ghost"))) })) },
78
+ (if (or (== @entity.feedback "") @entity.done) null { type: stack, direction: vertical, gap: xs, children: [
79
+ { type: badge, variant: (if (== @entity.verdict "correct") "success" "error"), label: (if (== @entity.verdict "correct") "Correct" (if (== @entity.verdict "timeout") "Time's up" "Incorrect")) },
80
+ { type: typography, variant: body, content: @entity.feedback }
81
+ ] }),
82
+ (if @entity.done { type: typography, variant: body, content: @entity.feedback } null),
83
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
84
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
85
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost, disabled: (if @entity.done false true) },
86
+ { type: typography, variant: caption, color: muted, content: (str/concat "Best streak " @entity.bestStreak) }
87
+ ] }
77
88
  ] }
78
89
  ]
79
90
  })
80
91
 
92
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
93
+ (set @entity.active false)
94
+ (set @entity.running false)
95
+ (render-ui main null)
96
+
81
97
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
82
98
  (let (
83
99
  (first (array/nth @config.items 0))
@@ -90,6 +106,7 @@ orbital SpeedDrillOrbital {
90
106
  (set @entity.remaining (* @config.timeLimit 10))
91
107
  (set @entity.running true)
92
108
  (set @entity.verdict "")
109
+ (set @entity.pickedIndex -1)
93
110
  (set @entity.score 0)
94
111
  (set @entity.streak 0)
95
112
  (set @entity.bestStreak 0)
@@ -108,19 +125,28 @@ orbital SpeedDrillOrbital {
108
125
  padding: md
109
126
  children: [
110
127
  { type: stack, direction: vertical, gap: md, children: [
111
- { type: typography, variant: h4, content: @entity.title },
128
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
129
+ { type: stack, direction: vertical, gap: xs, children: [
130
+ { type: typography, variant: h4, content: @entity.title },
131
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
132
+ ] },
133
+ { type: badge, label: (if @entity.done "Round complete" (str/concat @entity.score " pts · streak " @entity.streak)), variant: (if @entity.done "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "") "neutral" "error"))) }
134
+ ] },
112
135
  { 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)) },
113
- { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
136
+ { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), variant: (if (< @entity.remaining (* @config.timeLimit 3)) "warning" "primary"), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
114
137
  { type: stack, direction: vertical, gap: md, children: @entity.view },
115
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
116
- { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), variant: secondary, size: sm, disabled: (if @entity.running false true) })) },
117
- { type: typography, variant: caption, content: @entity.feedback },
118
- { type: stack, direction: horizontal, gap: sm, children: [
119
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
120
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost, disabled: (if @entity.done false true) }
121
- ] },
122
- { type: stat-display, label: "Score", value: @entity.score, max: 0 },
123
- { type: typography, variant: caption, content: (str/concat "Streak: " (str/concat "" @entity.streak) " · best: " (str/concat "" @entity.bestStreak)) }
138
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
139
+ { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), size: lg, disabled: (not @entity.running), variant: (if @entity.running "secondary" (if (== @i @entity.correctIndex) "success" (if (== @i @entity.pickedIndex) "danger" "ghost"))) })) },
140
+ (if (or (== @entity.feedback "") @entity.done) null { type: stack, direction: vertical, gap: xs, children: [
141
+ { type: badge, variant: (if (== @entity.verdict "correct") "success" "error"), label: (if (== @entity.verdict "correct") "Correct" (if (== @entity.verdict "timeout") "Time's up" "Incorrect")) },
142
+ { type: typography, variant: body, content: @entity.feedback }
143
+ ] }),
144
+ (if @entity.done { type: typography, variant: body, content: @entity.feedback } null),
145
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
146
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
147
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost, disabled: (if @entity.done false true) },
148
+ { type: typography, variant: caption, color: muted, content: (str/concat "Best streak " @entity.bestStreak) }
149
+ ] }
124
150
  ] }
125
151
  ]
126
152
  })
@@ -134,6 +160,7 @@ orbital SpeedDrillOrbital {
134
160
  )
135
161
  (do
136
162
  (set @entity.running false)
163
+ (set @entity.pickedIndex ?index)
137
164
  (set @entity.verdict (if @correct "correct" "wrong"))
138
165
  (set @entity.score (+ @entity.score @points))
139
166
  (set @entity.streak @newStreak)
@@ -156,6 +183,7 @@ orbital SpeedDrillOrbital {
156
183
  (set @entity.remaining (if @finished 0 (* @config.timeLimit 10)))
157
184
  (set @entity.running (if @finished false true))
158
185
  (set @entity.verdict "")
186
+ (set @entity.pickedIndex -1)
159
187
  (set @entity.promptText (if @finished @entity.promptText (object/get @next prompt)))
160
188
  (set @entity.options (if @finished [] (object/get @next options)))
161
189
  (set @entity.correctIndex (if @finished @entity.correctIndex (object/get @next correctIndex)))
@@ -176,19 +204,28 @@ orbital SpeedDrillOrbital {
176
204
  padding: md
177
205
  children: [
178
206
  { type: stack, direction: vertical, gap: md, children: [
179
- { type: typography, variant: h4, content: @entity.title },
207
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
208
+ { type: stack, direction: vertical, gap: xs, children: [
209
+ { type: typography, variant: h4, content: @entity.title },
210
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
211
+ ] },
212
+ { type: badge, label: (if @entity.done "Round complete" (str/concat @entity.score " pts · streak " @entity.streak)), variant: (if @entity.done "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "") "neutral" "error"))) }
213
+ ] },
180
214
  { 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)) },
181
- { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
215
+ { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), variant: (if (< @entity.remaining (* @config.timeLimit 3)) "warning" "primary"), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
182
216
  { type: stack, direction: vertical, gap: md, children: @entity.view },
183
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
184
- { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), variant: secondary, size: sm, disabled: (if @entity.running false true) })) },
185
- { type: typography, variant: caption, content: @entity.feedback },
186
- { type: stack, direction: horizontal, gap: sm, children: [
187
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
188
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost, disabled: (if @entity.done false true) }
189
- ] },
190
- { type: stat-display, label: "Score", value: @entity.score, max: 0 },
191
- { type: typography, variant: caption, content: (str/concat "Streak: " (str/concat "" @entity.streak) " · best: " (str/concat "" @entity.bestStreak)) }
217
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
218
+ { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), size: lg, disabled: (not @entity.running), variant: (if @entity.running "secondary" (if (== @i @entity.correctIndex) "success" (if (== @i @entity.pickedIndex) "danger" "ghost"))) })) },
219
+ (if (or (== @entity.feedback "") @entity.done) null { type: stack, direction: vertical, gap: xs, children: [
220
+ { type: badge, variant: (if (== @entity.verdict "correct") "success" "error"), label: (if (== @entity.verdict "correct") "Correct" (if (== @entity.verdict "timeout") "Time's up" "Incorrect")) },
221
+ { type: typography, variant: body, content: @entity.feedback }
222
+ ] }),
223
+ (if @entity.done { type: typography, variant: body, content: @entity.feedback } null),
224
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
225
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
226
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost, disabled: (if @entity.done false true) },
227
+ { type: typography, variant: caption, color: muted, content: (str/concat "Best streak " @entity.bestStreak) }
228
+ ] }
192
229
  ] }
193
230
  ]
194
231
  })
@@ -203,6 +240,7 @@ orbital SpeedDrillOrbital {
203
240
  (set @entity.remaining (* @config.timeLimit 10))
204
241
  (set @entity.running true)
205
242
  (set @entity.verdict "")
243
+ (set @entity.pickedIndex -1)
206
244
  (set @entity.score 0)
207
245
  (set @entity.streak 0)
208
246
  (set @entity.bestStreak 0)
@@ -221,19 +259,28 @@ orbital SpeedDrillOrbital {
221
259
  padding: md
222
260
  children: [
223
261
  { type: stack, direction: vertical, gap: md, children: [
224
- { type: typography, variant: h4, content: @entity.title },
262
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
263
+ { type: stack, direction: vertical, gap: xs, children: [
264
+ { type: typography, variant: h4, content: @entity.title },
265
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
266
+ ] },
267
+ { type: badge, label: (if @entity.done "Round complete" (str/concat @entity.score " pts · streak " @entity.streak)), variant: (if @entity.done "primary" (if (== @entity.verdict "correct") "success" (if (== @entity.verdict "") "neutral" "error"))) }
268
+ ] },
225
269
  { 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)) },
226
- { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
270
+ { type: progress-bar, value: @entity.remaining, max: (* @config.timeLimit 10), variant: (if (< @entity.remaining (* @config.timeLimit 3)) "warning" "primary"), showPercentage: false, label: (str/concat "Time left: " (math/round (/ @entity.remaining 10) 1) "s") },
227
271
  { type: stack, direction: vertical, gap: md, children: @entity.view },
228
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
229
- { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), variant: secondary, size: sm, disabled: (if @entity.running false true) })) },
230
- { type: typography, variant: caption, content: @entity.feedback },
231
- { type: stack, direction: horizontal, gap: sm, children: [
232
- { type: button, action: NEXT, label: @config.nextLabel, variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
233
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost, disabled: (if @entity.done false true) }
234
- ] },
235
- { type: stat-display, label: "Score", value: @entity.score, max: 0 },
236
- { type: typography, variant: caption, content: (str/concat "Streak: " (str/concat "" @entity.streak) " · best: " (str/concat "" @entity.bestStreak)) }
272
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/range 0 (array/len @entity.options)) (fn (i)
273
+ { type: button, action: ANSWER, actionPayload: { index: @i }, label: (array/nth @entity.options @i), size: lg, disabled: (not @entity.running), variant: (if @entity.running "secondary" (if (== @i @entity.correctIndex) "success" (if (== @i @entity.pickedIndex) "danger" "ghost"))) })) },
274
+ (if (or (== @entity.feedback "") @entity.done) null { type: stack, direction: vertical, gap: xs, children: [
275
+ { type: badge, variant: (if (== @entity.verdict "correct") "success" "error"), label: (if (== @entity.verdict "correct") "Correct" (if (== @entity.verdict "timeout") "Time's up" "Incorrect")) },
276
+ { type: typography, variant: body, content: @entity.feedback }
277
+ ] }),
278
+ (if @entity.done { type: typography, variant: body, content: @entity.feedback } null),
279
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
280
+ { type: button, action: NEXT, label: @config.nextLabel, icon: "chevron-right", variant: primary, disabled: (if (and (== @entity.running false) (and (!= @entity.verdict "") (== @entity.done false))) false true) },
281
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost, disabled: (if @entity.done false true) },
282
+ { type: typography, variant: caption, color: muted, content: (str/concat "Best streak " @entity.bestStreak) }
283
+ ] }
237
284
  ] }
238
285
  ]
239
286
  })
@@ -311,6 +358,10 @@ orbital SpeedDrillOrbital {
311
358
  @label "Title"
312
359
  @description "Title shown above the drill."
313
360
  @tier "presentation"
361
+ summary : string = ""
362
+ @label "Concept"
363
+ @description "One line under the title saying what is being drilled (keep it under ~65 characters). Empty shows no line."
364
+ @tier "presentation"
314
365
  items : [SpeedDrillItem] = [
315
366
  { prompt: "7 × 8", options: ["54", "56", "63", "48"], correctIndex: 1 },
316
367
  { prompt: "9 × 6", options: ["54", "56", "45", "63"], correctIndex: 0 },