@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 LearnStepperOrbital {
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 LearnStepperStage = {
36
41
  id : string!
@@ -93,9 +98,9 @@ orbital LearnStepperOrbital {
93
98
  (visibleThreadNote (if (== @entity.activeFilter "all") @config.allSummary (object/get (array/first (array/filter @config.categorySummaries (fn cs (== @cs.category @entity.activeFilter)))) summary)))
94
99
  (axisWidth (- @config.width (+ @config.axisMarginLeft @config.axisMarginRight)))
95
100
  (baseShapes [
96
- { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, color: "#78716c", lineWidth: 2, opacity: 1 },
97
- { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), fontSize: 11, align: "left", color: "#78716c" },
98
- { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), fontSize: 11, align: "right", color: "#78716c" }
101
+ { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, tone: "axis", stroke: "normal", opacity: 1 },
102
+ { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), textSize: "xs", align: "left", tone: "label" },
103
+ { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), textSize: "xs", align: "right", tone: "label" }
99
104
  ])
100
105
  (markerShapes (array/flatten (array/map @config.stages (fn s (let (
101
106
  (isRevealed (<= @s.position @currentPosition))
@@ -111,18 +116,22 @@ orbital LearnStepperOrbital {
111
116
  )
112
117
  (if @isCurrent
113
118
  [
114
- { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), color: @catColor, fill: "transparent", lineWidth: 3, opacity: 1 },
115
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
119
+ { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), tone: @catColor, fillStyle: "outline", stroke: "bold", opacity: 1 },
120
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
116
121
  ]
117
122
  [
118
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
123
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
119
124
  ]
120
125
  )
121
126
  )))))
122
- (allShapes (array/concat @baseShapes @markerShapes))
127
+ (legendShapes (array/flatten (array/map @config.categories [
128
+ { type: circle, id: (str/concat "legend-dot-" @index), x: (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))), y: 22, radius: 5, tone: (object/get @item color), fillTone: (object/get @item color), fillStyle: "solid", opacity: 1 },
129
+ { type: text, id: (str/concat "legend-label-" @index), x: (+ (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))) 12), y: 26, text: (object/get @item label), textSize: "xs", align: "left", tone: "label" }
130
+ ])))
131
+ (allShapes (array/concat @baseShapes @markerShapes @legendShapes))
123
132
  (filterButtons (array/concat [
124
- { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "primary" "ghost"), size: sm }
125
- ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "primary" "ghost"), size: sm }))))
133
+ { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter "all") "true" "false") }
134
+ ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter (object/get @cc category)) "true" "false") }))))
126
135
  )
127
136
  (do
128
137
  (set @entity.narration @narration)
@@ -140,25 +149,35 @@ orbital LearnStepperOrbital {
140
149
  padding: md
141
150
  children: [
142
151
  { type: stack, direction: vertical, gap: md, children: [
143
- { type: typography, variant: h4, content: @entity.title },
144
- { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false, label: (str/concat "Stage " (str/concat "" (+ @entity.cursorIndex 1)) " of " (str/concat "" (array/len @config.stages))) },
145
- { type: chart-legend, items: (array/map @config.categories (fn cc { label: (object/get @cc label), color: (object/get @cc color) })), direction: horizontal },
146
- { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate },
147
- { type: stack, direction: horizontal, gap: xs, children: @entity.filterButtons },
148
- { type: stack, direction: horizontal, gap: sm, children: [
149
- { type: button, action: PREV, label: "Prev", variant: ghost, disabled: (if (<= @entity.cursorIndex -1) true false) },
150
- { type: button, action: NEXT, label: "Next", variant: primary, disabled: (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) true false) },
151
- { type: button, action: RESET_STEPPER, label: "Reset", variant: ghost }
152
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
153
+ { type: stack, direction: vertical, gap: xs, children: [
154
+ { type: typography, variant: h4, content: @entity.title },
155
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
156
+ ] },
157
+ { type: badge, label: (if (<= @entity.cursorIndex -1) "Not started" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "Complete" (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)))), variant: (if (<= @entity.cursorIndex -1) "neutral" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "success" "primary")) }
152
158
  ] },
159
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
160
+ { type: button, action: NEXT, label: "Next", icon: "chevron-right", variant: primary, disabled: (>= @entity.cursorIndex (- (array/len @config.stages) 1)) },
161
+ { type: button, action: PREV, label: "Prev", icon: "chevron-left", variant: secondary, disabled: (<= @entity.cursorIndex -1) },
162
+ { type: button, action: RESET_STEPPER, label: "Reset", icon: "rotate-ccw", variant: ghost },
163
+ { type: typography, variant: caption, color: muted, content: (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)) }
164
+ ] },
165
+ { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false },
166
+ { type: stack, direction: horizontal, align: center, gap: xs, wrap: true, role: "group", aria-label: "Category lens", children: @entity.filterButtons },
167
+ { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate, keyMap: { ArrowRight: NEXT, ArrowLeft: PREV, KeyR: RESET_STEPPER } },
153
168
  { type: card, variant: bordered, padding: md, title: (if (!= @entity.currentStageLabel "") @entity.currentStageLabel "Not Started"), children: [
154
169
  { type: typography, variant: body, content: @entity.currentStageDescription }
155
170
  ] },
156
- { type: stack, direction: horizontal, gap: xs, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
157
- { type: typography, variant: caption, content: @entity.visibleThreadNote }
171
+ { type: stack, direction: horizontal, gap: xs, wrap: true, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
172
+ { type: typography, variant: caption, color: muted, content: @entity.visibleThreadNote }
158
173
  ] }
159
174
  ]
160
175
  })
161
176
 
177
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
178
+ (set @entity.active false)
179
+ (render-ui main null)
180
+
162
181
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
163
182
  (set @entity.active true)
164
183
  (set @entity.title @config.title)
@@ -179,9 +198,9 @@ orbital LearnStepperOrbital {
179
198
  (visibleThreadNote (if (== @entity.activeFilter "all") @config.allSummary (object/get (array/first (array/filter @config.categorySummaries (fn cs (== @cs.category @entity.activeFilter)))) summary)))
180
199
  (axisWidth (- @config.width (+ @config.axisMarginLeft @config.axisMarginRight)))
181
200
  (baseShapes [
182
- { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, color: "#78716c", lineWidth: 2, opacity: 1 },
183
- { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), fontSize: 11, align: "left", color: "#78716c" },
184
- { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), fontSize: 11, align: "right", color: "#78716c" }
201
+ { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, tone: "axis", stroke: "normal", opacity: 1 },
202
+ { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), textSize: "xs", align: "left", tone: "label" },
203
+ { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), textSize: "xs", align: "right", tone: "label" }
185
204
  ])
186
205
  (markerShapes (array/flatten (array/map @config.stages (fn s (let (
187
206
  (isRevealed (<= @s.position @currentPosition))
@@ -197,18 +216,22 @@ orbital LearnStepperOrbital {
197
216
  )
198
217
  (if @isCurrent
199
218
  [
200
- { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), color: @catColor, fill: "transparent", lineWidth: 3, opacity: 1 },
201
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
219
+ { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), tone: @catColor, fillStyle: "outline", stroke: "bold", opacity: 1 },
220
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
202
221
  ]
203
222
  [
204
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
223
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
205
224
  ]
206
225
  )
207
226
  )))))
208
- (allShapes (array/concat @baseShapes @markerShapes))
227
+ (legendShapes (array/flatten (array/map @config.categories [
228
+ { type: circle, id: (str/concat "legend-dot-" @index), x: (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))), y: 22, radius: 5, tone: (object/get @item color), fillTone: (object/get @item color), fillStyle: "solid", opacity: 1 },
229
+ { type: text, id: (str/concat "legend-label-" @index), x: (+ (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))) 12), y: 26, text: (object/get @item label), textSize: "xs", align: "left", tone: "label" }
230
+ ])))
231
+ (allShapes (array/concat @baseShapes @markerShapes @legendShapes))
209
232
  (filterButtons (array/concat [
210
- { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "primary" "ghost"), size: sm }
211
- ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "primary" "ghost"), size: sm }))))
233
+ { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter "all") "true" "false") }
234
+ ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter (object/get @cc category)) "true" "false") }))))
212
235
  )
213
236
  (do
214
237
  (set @entity.narration @narration)
@@ -226,21 +249,27 @@ orbital LearnStepperOrbital {
226
249
  padding: md
227
250
  children: [
228
251
  { type: stack, direction: vertical, gap: md, children: [
229
- { type: typography, variant: h4, content: @entity.title },
230
- { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false, label: (str/concat "Stage " (str/concat "" (+ @entity.cursorIndex 1)) " of " (str/concat "" (array/len @config.stages))) },
231
- { type: chart-legend, items: (array/map @config.categories (fn cc { label: (object/get @cc label), color: (object/get @cc color) })), direction: horizontal },
232
- { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate },
233
- { type: stack, direction: horizontal, gap: xs, children: @entity.filterButtons },
234
- { type: stack, direction: horizontal, gap: sm, children: [
235
- { type: button, action: PREV, label: "Prev", variant: ghost, disabled: (if (<= @entity.cursorIndex -1) true false) },
236
- { type: button, action: NEXT, label: "Next", variant: primary, disabled: (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) true false) },
237
- { type: button, action: RESET_STEPPER, label: "Reset", variant: ghost }
252
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
253
+ { type: stack, direction: vertical, gap: xs, children: [
254
+ { type: typography, variant: h4, content: @entity.title },
255
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
256
+ ] },
257
+ { type: badge, label: (if (<= @entity.cursorIndex -1) "Not started" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "Complete" (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)))), variant: (if (<= @entity.cursorIndex -1) "neutral" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "success" "primary")) }
238
258
  ] },
259
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
260
+ { type: button, action: NEXT, label: "Next", icon: "chevron-right", variant: primary, disabled: (>= @entity.cursorIndex (- (array/len @config.stages) 1)) },
261
+ { type: button, action: PREV, label: "Prev", icon: "chevron-left", variant: secondary, disabled: (<= @entity.cursorIndex -1) },
262
+ { type: button, action: RESET_STEPPER, label: "Reset", icon: "rotate-ccw", variant: ghost },
263
+ { type: typography, variant: caption, color: muted, content: (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)) }
264
+ ] },
265
+ { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false },
266
+ { type: stack, direction: horizontal, align: center, gap: xs, wrap: true, role: "group", aria-label: "Category lens", children: @entity.filterButtons },
267
+ { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate, keyMap: { ArrowRight: NEXT, ArrowLeft: PREV, KeyR: RESET_STEPPER } },
239
268
  { type: card, variant: bordered, padding: md, title: (if (!= @entity.currentStageLabel "") @entity.currentStageLabel "Not Started"), children: [
240
269
  { type: typography, variant: body, content: @entity.currentStageDescription }
241
270
  ] },
242
- { type: stack, direction: horizontal, gap: xs, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
243
- { type: typography, variant: caption, content: @entity.visibleThreadNote }
271
+ { type: stack, direction: horizontal, gap: xs, wrap: true, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
272
+ { type: typography, variant: caption, color: muted, content: @entity.visibleThreadNote }
244
273
  ] }
245
274
  ]
246
275
  })
@@ -262,9 +291,9 @@ orbital LearnStepperOrbital {
262
291
  (visibleThreadNote (if (== @entity.activeFilter "all") @config.allSummary (object/get (array/first (array/filter @config.categorySummaries (fn cs (== @cs.category @entity.activeFilter)))) summary)))
263
292
  (axisWidth (- @config.width (+ @config.axisMarginLeft @config.axisMarginRight)))
264
293
  (baseShapes [
265
- { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, color: "#78716c", lineWidth: 2, opacity: 1 },
266
- { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), fontSize: 11, align: "left", color: "#78716c" },
267
- { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), fontSize: 11, align: "right", color: "#78716c" }
294
+ { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, tone: "axis", stroke: "normal", opacity: 1 },
295
+ { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), textSize: "xs", align: "left", tone: "label" },
296
+ { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), textSize: "xs", align: "right", tone: "label" }
268
297
  ])
269
298
  (markerShapes (array/flatten (array/map @config.stages (fn s (let (
270
299
  (isRevealed (<= @s.position @currentPosition))
@@ -280,18 +309,22 @@ orbital LearnStepperOrbital {
280
309
  )
281
310
  (if @isCurrent
282
311
  [
283
- { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), color: @catColor, fill: "transparent", lineWidth: 3, opacity: 1 },
284
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
312
+ { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), tone: @catColor, fillStyle: "outline", stroke: "bold", opacity: 1 },
313
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
285
314
  ]
286
315
  [
287
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
316
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
288
317
  ]
289
318
  )
290
319
  )))))
291
- (allShapes (array/concat @baseShapes @markerShapes))
320
+ (legendShapes (array/flatten (array/map @config.categories [
321
+ { type: circle, id: (str/concat "legend-dot-" @index), x: (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))), y: 22, radius: 5, tone: (object/get @item color), fillTone: (object/get @item color), fillStyle: "solid", opacity: 1 },
322
+ { type: text, id: (str/concat "legend-label-" @index), x: (+ (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))) 12), y: 26, text: (object/get @item label), textSize: "xs", align: "left", tone: "label" }
323
+ ])))
324
+ (allShapes (array/concat @baseShapes @markerShapes @legendShapes))
292
325
  (filterButtons (array/concat [
293
- { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "primary" "ghost"), size: sm }
294
- ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "primary" "ghost"), size: sm }))))
326
+ { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter "all") "true" "false") }
327
+ ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter (object/get @cc category)) "true" "false") }))))
295
328
  )
296
329
  (do
297
330
  (set @entity.narration @narration)
@@ -309,21 +342,27 @@ orbital LearnStepperOrbital {
309
342
  padding: md
310
343
  children: [
311
344
  { type: stack, direction: vertical, gap: md, children: [
312
- { type: typography, variant: h4, content: @entity.title },
313
- { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false, label: (str/concat "Stage " (str/concat "" (+ @entity.cursorIndex 1)) " of " (str/concat "" (array/len @config.stages))) },
314
- { type: chart-legend, items: (array/map @config.categories (fn cc { label: (object/get @cc label), color: (object/get @cc color) })), direction: horizontal },
315
- { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate },
316
- { type: stack, direction: horizontal, gap: xs, children: @entity.filterButtons },
317
- { type: stack, direction: horizontal, gap: sm, children: [
318
- { type: button, action: PREV, label: "Prev", variant: ghost, disabled: (if (<= @entity.cursorIndex -1) true false) },
319
- { type: button, action: NEXT, label: "Next", variant: primary, disabled: (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) true false) },
320
- { type: button, action: RESET_STEPPER, label: "Reset", variant: ghost }
345
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
346
+ { type: stack, direction: vertical, gap: xs, children: [
347
+ { type: typography, variant: h4, content: @entity.title },
348
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
349
+ ] },
350
+ { type: badge, label: (if (<= @entity.cursorIndex -1) "Not started" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "Complete" (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)))), variant: (if (<= @entity.cursorIndex -1) "neutral" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "success" "primary")) }
351
+ ] },
352
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
353
+ { type: button, action: NEXT, label: "Next", icon: "chevron-right", variant: primary, disabled: (>= @entity.cursorIndex (- (array/len @config.stages) 1)) },
354
+ { type: button, action: PREV, label: "Prev", icon: "chevron-left", variant: secondary, disabled: (<= @entity.cursorIndex -1) },
355
+ { type: button, action: RESET_STEPPER, label: "Reset", icon: "rotate-ccw", variant: ghost },
356
+ { type: typography, variant: caption, color: muted, content: (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)) }
321
357
  ] },
358
+ { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false },
359
+ { type: stack, direction: horizontal, align: center, gap: xs, wrap: true, role: "group", aria-label: "Category lens", children: @entity.filterButtons },
360
+ { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate, keyMap: { ArrowRight: NEXT, ArrowLeft: PREV, KeyR: RESET_STEPPER } },
322
361
  { type: card, variant: bordered, padding: md, title: (if (!= @entity.currentStageLabel "") @entity.currentStageLabel "Not Started"), children: [
323
362
  { type: typography, variant: body, content: @entity.currentStageDescription }
324
363
  ] },
325
- { type: stack, direction: horizontal, gap: xs, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
326
- { type: typography, variant: caption, content: @entity.visibleThreadNote }
364
+ { type: stack, direction: horizontal, gap: xs, wrap: true, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
365
+ { type: typography, variant: caption, color: muted, content: @entity.visibleThreadNote }
327
366
  ] }
328
367
  ]
329
368
  })
@@ -346,9 +385,9 @@ orbital LearnStepperOrbital {
346
385
  (visibleThreadNote (if (== @entity.activeFilter "all") @config.allSummary (object/get (array/first (array/filter @config.categorySummaries (fn cs (== @cs.category @entity.activeFilter)))) summary)))
347
386
  (axisWidth (- @config.width (+ @config.axisMarginLeft @config.axisMarginRight)))
348
387
  (baseShapes [
349
- { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, color: "#78716c", lineWidth: 2, opacity: 1 },
350
- { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), fontSize: 11, align: "left", color: "#78716c" },
351
- { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), fontSize: 11, align: "right", color: "#78716c" }
388
+ { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, tone: "axis", stroke: "normal", opacity: 1 },
389
+ { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), textSize: "xs", align: "left", tone: "label" },
390
+ { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), textSize: "xs", align: "right", tone: "label" }
352
391
  ])
353
392
  (markerShapes (array/flatten (array/map @config.stages (fn s (let (
354
393
  (isRevealed (<= @s.position @currentPosition))
@@ -364,18 +403,22 @@ orbital LearnStepperOrbital {
364
403
  )
365
404
  (if @isCurrent
366
405
  [
367
- { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), color: @catColor, fill: "transparent", lineWidth: 3, opacity: 1 },
368
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
406
+ { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), tone: @catColor, fillStyle: "outline", stroke: "bold", opacity: 1 },
407
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
369
408
  ]
370
409
  [
371
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
410
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
372
411
  ]
373
412
  )
374
413
  )))))
375
- (allShapes (array/concat @baseShapes @markerShapes))
414
+ (legendShapes (array/flatten (array/map @config.categories [
415
+ { type: circle, id: (str/concat "legend-dot-" @index), x: (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))), y: 22, radius: 5, tone: (object/get @item color), fillTone: (object/get @item color), fillStyle: "solid", opacity: 1 },
416
+ { type: text, id: (str/concat "legend-label-" @index), x: (+ (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))) 12), y: 26, text: (object/get @item label), textSize: "xs", align: "left", tone: "label" }
417
+ ])))
418
+ (allShapes (array/concat @baseShapes @markerShapes @legendShapes))
376
419
  (filterButtons (array/concat [
377
- { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "primary" "ghost"), size: sm }
378
- ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "primary" "ghost"), size: sm }))))
420
+ { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter "all") "true" "false") }
421
+ ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter (object/get @cc category)) "true" "false") }))))
379
422
  )
380
423
  (do
381
424
  (set @entity.narration @narration)
@@ -393,21 +436,27 @@ orbital LearnStepperOrbital {
393
436
  padding: md
394
437
  children: [
395
438
  { type: stack, direction: vertical, gap: md, children: [
396
- { type: typography, variant: h4, content: @entity.title },
397
- { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false, label: (str/concat "Stage " (str/concat "" (+ @entity.cursorIndex 1)) " of " (str/concat "" (array/len @config.stages))) },
398
- { type: chart-legend, items: (array/map @config.categories (fn cc { label: (object/get @cc label), color: (object/get @cc color) })), direction: horizontal },
399
- { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate },
400
- { type: stack, direction: horizontal, gap: xs, children: @entity.filterButtons },
401
- { type: stack, direction: horizontal, gap: sm, children: [
402
- { type: button, action: PREV, label: "Prev", variant: ghost, disabled: (if (<= @entity.cursorIndex -1) true false) },
403
- { type: button, action: NEXT, label: "Next", variant: primary, disabled: (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) true false) },
404
- { type: button, action: RESET_STEPPER, label: "Reset", variant: ghost }
439
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
440
+ { type: stack, direction: vertical, gap: xs, children: [
441
+ { type: typography, variant: h4, content: @entity.title },
442
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
443
+ ] },
444
+ { type: badge, label: (if (<= @entity.cursorIndex -1) "Not started" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "Complete" (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)))), variant: (if (<= @entity.cursorIndex -1) "neutral" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "success" "primary")) }
405
445
  ] },
446
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
447
+ { type: button, action: NEXT, label: "Next", icon: "chevron-right", variant: primary, disabled: (>= @entity.cursorIndex (- (array/len @config.stages) 1)) },
448
+ { type: button, action: PREV, label: "Prev", icon: "chevron-left", variant: secondary, disabled: (<= @entity.cursorIndex -1) },
449
+ { type: button, action: RESET_STEPPER, label: "Reset", icon: "rotate-ccw", variant: ghost },
450
+ { type: typography, variant: caption, color: muted, content: (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)) }
451
+ ] },
452
+ { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false },
453
+ { type: stack, direction: horizontal, align: center, gap: xs, wrap: true, role: "group", aria-label: "Category lens", children: @entity.filterButtons },
454
+ { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate, keyMap: { ArrowRight: NEXT, ArrowLeft: PREV, KeyR: RESET_STEPPER } },
406
455
  { type: card, variant: bordered, padding: md, title: (if (!= @entity.currentStageLabel "") @entity.currentStageLabel "Not Started"), children: [
407
456
  { type: typography, variant: body, content: @entity.currentStageDescription }
408
457
  ] },
409
- { type: stack, direction: horizontal, gap: xs, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
410
- { type: typography, variant: caption, content: @entity.visibleThreadNote }
458
+ { type: stack, direction: horizontal, gap: xs, wrap: true, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
459
+ { type: typography, variant: caption, color: muted, content: @entity.visibleThreadNote }
411
460
  ] }
412
461
  ]
413
462
  })
@@ -430,9 +479,9 @@ orbital LearnStepperOrbital {
430
479
  (visibleThreadNote (if (== @entity.activeFilter "all") @config.allSummary (object/get (array/first (array/filter @config.categorySummaries (fn cs (== @cs.category @entity.activeFilter)))) summary)))
431
480
  (axisWidth (- @config.width (+ @config.axisMarginLeft @config.axisMarginRight)))
432
481
  (baseShapes [
433
- { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, color: "#78716c", lineWidth: 2, opacity: 1 },
434
- { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), fontSize: 11, align: "left", color: "#78716c" },
435
- { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), fontSize: 11, align: "right", color: "#78716c" }
482
+ { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, tone: "axis", stroke: "normal", opacity: 1 },
483
+ { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), textSize: "xs", align: "left", tone: "label" },
484
+ { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), textSize: "xs", align: "right", tone: "label" }
436
485
  ])
437
486
  (markerShapes (array/flatten (array/map @config.stages (fn s (let (
438
487
  (isRevealed (<= @s.position @currentPosition))
@@ -448,18 +497,22 @@ orbital LearnStepperOrbital {
448
497
  )
449
498
  (if @isCurrent
450
499
  [
451
- { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), color: @catColor, fill: "transparent", lineWidth: 3, opacity: 1 },
452
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
500
+ { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), tone: @catColor, fillStyle: "outline", stroke: "bold", opacity: 1 },
501
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
453
502
  ]
454
503
  [
455
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
504
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
456
505
  ]
457
506
  )
458
507
  )))))
459
- (allShapes (array/concat @baseShapes @markerShapes))
508
+ (legendShapes (array/flatten (array/map @config.categories [
509
+ { type: circle, id: (str/concat "legend-dot-" @index), x: (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))), y: 22, radius: 5, tone: (object/get @item color), fillTone: (object/get @item color), fillStyle: "solid", opacity: 1 },
510
+ { type: text, id: (str/concat "legend-label-" @index), x: (+ (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))) 12), y: 26, text: (object/get @item label), textSize: "xs", align: "left", tone: "label" }
511
+ ])))
512
+ (allShapes (array/concat @baseShapes @markerShapes @legendShapes))
460
513
  (filterButtons (array/concat [
461
- { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "primary" "ghost"), size: sm }
462
- ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "primary" "ghost"), size: sm }))))
514
+ { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter "all") "true" "false") }
515
+ ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter (object/get @cc category)) "true" "false") }))))
463
516
  )
464
517
  (do
465
518
  (set @entity.narration @narration)
@@ -477,21 +530,27 @@ orbital LearnStepperOrbital {
477
530
  padding: md
478
531
  children: [
479
532
  { type: stack, direction: vertical, gap: md, children: [
480
- { type: typography, variant: h4, content: @entity.title },
481
- { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false, label: (str/concat "Stage " (str/concat "" (+ @entity.cursorIndex 1)) " of " (str/concat "" (array/len @config.stages))) },
482
- { type: chart-legend, items: (array/map @config.categories (fn cc { label: (object/get @cc label), color: (object/get @cc color) })), direction: horizontal },
483
- { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate },
484
- { type: stack, direction: horizontal, gap: xs, children: @entity.filterButtons },
485
- { type: stack, direction: horizontal, gap: sm, children: [
486
- { type: button, action: PREV, label: "Prev", variant: ghost, disabled: (if (<= @entity.cursorIndex -1) true false) },
487
- { type: button, action: NEXT, label: "Next", variant: primary, disabled: (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) true false) },
488
- { type: button, action: RESET_STEPPER, label: "Reset", variant: ghost }
533
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
534
+ { type: stack, direction: vertical, gap: xs, children: [
535
+ { type: typography, variant: h4, content: @entity.title },
536
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
537
+ ] },
538
+ { type: badge, label: (if (<= @entity.cursorIndex -1) "Not started" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "Complete" (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)))), variant: (if (<= @entity.cursorIndex -1) "neutral" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "success" "primary")) }
539
+ ] },
540
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
541
+ { type: button, action: NEXT, label: "Next", icon: "chevron-right", variant: primary, disabled: (>= @entity.cursorIndex (- (array/len @config.stages) 1)) },
542
+ { type: button, action: PREV, label: "Prev", icon: "chevron-left", variant: secondary, disabled: (<= @entity.cursorIndex -1) },
543
+ { type: button, action: RESET_STEPPER, label: "Reset", icon: "rotate-ccw", variant: ghost },
544
+ { type: typography, variant: caption, color: muted, content: (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)) }
489
545
  ] },
546
+ { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false },
547
+ { type: stack, direction: horizontal, align: center, gap: xs, wrap: true, role: "group", aria-label: "Category lens", children: @entity.filterButtons },
548
+ { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate, keyMap: { ArrowRight: NEXT, ArrowLeft: PREV, KeyR: RESET_STEPPER } },
490
549
  { type: card, variant: bordered, padding: md, title: (if (!= @entity.currentStageLabel "") @entity.currentStageLabel "Not Started"), children: [
491
550
  { type: typography, variant: body, content: @entity.currentStageDescription }
492
551
  ] },
493
- { type: stack, direction: horizontal, gap: xs, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
494
- { type: typography, variant: caption, content: @entity.visibleThreadNote }
552
+ { type: stack, direction: horizontal, gap: xs, wrap: true, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
553
+ { type: typography, variant: caption, color: muted, content: @entity.visibleThreadNote }
495
554
  ] }
496
555
  ]
497
556
  })
@@ -515,9 +574,9 @@ orbital LearnStepperOrbital {
515
574
  (visibleThreadNote (if (== @entity.activeFilter "all") @config.allSummary (object/get (array/first (array/filter @config.categorySummaries (fn cs (== @cs.category @entity.activeFilter)))) summary)))
516
575
  (axisWidth (- @config.width (+ @config.axisMarginLeft @config.axisMarginRight)))
517
576
  (baseShapes [
518
- { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, color: "#78716c", lineWidth: 2, opacity: 1 },
519
- { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), fontSize: 11, align: "left", color: "#78716c" },
520
- { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), fontSize: 11, align: "right", color: "#78716c" }
577
+ { type: line, id: "axis", x1: @config.axisMarginLeft, y1: @config.axisY, x2: (- @config.width @config.axisMarginRight), y2: @config.axisY, tone: "axis", stroke: "normal", opacity: 1 },
578
+ { type: text, id: "minPositionLabel", x: @config.axisMarginLeft, y: (+ @config.axisY 26), text: (str/concat "" @config.minPosition), textSize: "xs", align: "left", tone: "label" },
579
+ { type: text, id: "maxPositionLabel", x: (- @config.width @config.axisMarginRight), y: (+ @config.axisY 26), text: (str/concat "" @config.maxPosition), textSize: "xs", align: "right", tone: "label" }
521
580
  ])
522
581
  (markerShapes (array/flatten (array/map @config.stages (fn s (let (
523
582
  (isRevealed (<= @s.position @currentPosition))
@@ -533,18 +592,22 @@ orbital LearnStepperOrbital {
533
592
  )
534
593
  (if @isCurrent
535
594
  [
536
- { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), color: @catColor, fill: "transparent", lineWidth: 3, opacity: 1 },
537
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
595
+ { type: circle, id: (str/concat "ring-" @s.position), x: @xPos, y: @config.axisY, radius: (+ @rad 5), tone: @catColor, fillStyle: "outline", stroke: "bold", opacity: 1 },
596
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
538
597
  ]
539
598
  [
540
- { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, color: @col, fill: @col, opacity: @finalOpacity }
599
+ { type: circle, id: (str/concat "marker-" @s.position), x: @xPos, y: @config.axisY, radius: @rad, tone: @col, fillTone: @col, fillStyle: "solid", opacity: @finalOpacity }
541
600
  ]
542
601
  )
543
602
  )))))
544
- (allShapes (array/concat @baseShapes @markerShapes))
603
+ (legendShapes (array/flatten (array/map @config.categories [
604
+ { type: circle, id: (str/concat "legend-dot-" @index), x: (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))), y: 22, radius: 5, tone: (object/get @item color), fillTone: (object/get @item color), fillStyle: "solid", opacity: 1 },
605
+ { type: text, id: (str/concat "legend-label-" @index), x: (+ (+ @config.axisMarginLeft (* @index (/ @axisWidth (array/len @config.categories)))) 12), y: 26, text: (object/get @item label), textSize: "xs", align: "left", tone: "label" }
606
+ ])))
607
+ (allShapes (array/concat @baseShapes @markerShapes @legendShapes))
545
608
  (filterButtons (array/concat [
546
- { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "primary" "ghost"), size: sm }
547
- ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "primary" "ghost"), size: sm }))))
609
+ { type: button, action: FILTER_CATEGORY, actionPayload: { category: "all" }, label: @config.allLabel, variant: (if (== @entity.activeFilter "all") "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter "all") "true" "false") }
610
+ ] (array/map @config.categories (fn cc { type: button, action: FILTER_CATEGORY, actionPayload: { category: (object/get @cc category) }, label: (object/get @cc label), variant: (if (== @entity.activeFilter (object/get @cc category)) "secondary" "ghost"), size: sm, aria-pressed: (if (== @entity.activeFilter (object/get @cc category)) "true" "false") }))))
548
611
  )
549
612
  (do
550
613
  (set @entity.narration @narration)
@@ -562,21 +625,27 @@ orbital LearnStepperOrbital {
562
625
  padding: md
563
626
  children: [
564
627
  { type: stack, direction: vertical, gap: md, children: [
565
- { type: typography, variant: h4, content: @entity.title },
566
- { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false, label: (str/concat "Stage " (str/concat "" (+ @entity.cursorIndex 1)) " of " (str/concat "" (array/len @config.stages))) },
567
- { type: chart-legend, items: (array/map @config.categories (fn cc { label: (object/get @cc label), color: (object/get @cc color) })), direction: horizontal },
568
- { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate },
569
- { type: stack, direction: horizontal, gap: xs, children: @entity.filterButtons },
570
- { type: stack, direction: horizontal, gap: sm, children: [
571
- { type: button, action: PREV, label: "Prev", variant: ghost, disabled: (if (<= @entity.cursorIndex -1) true false) },
572
- { type: button, action: NEXT, label: "Next", variant: primary, disabled: (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) true false) },
573
- { type: button, action: RESET_STEPPER, label: "Reset", variant: ghost }
628
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
629
+ { type: stack, direction: vertical, gap: xs, children: [
630
+ { type: typography, variant: h4, content: @entity.title },
631
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
632
+ ] },
633
+ { type: badge, label: (if (<= @entity.cursorIndex -1) "Not started" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "Complete" (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)))), variant: (if (<= @entity.cursorIndex -1) "neutral" (if (>= @entity.cursorIndex (- (array/len @config.stages) 1)) "success" "primary")) }
574
634
  ] },
635
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
636
+ { type: button, action: NEXT, label: "Next", icon: "chevron-right", variant: primary, disabled: (>= @entity.cursorIndex (- (array/len @config.stages) 1)) },
637
+ { type: button, action: PREV, label: "Prev", icon: "chevron-left", variant: secondary, disabled: (<= @entity.cursorIndex -1) },
638
+ { type: button, action: RESET_STEPPER, label: "Reset", icon: "rotate-ccw", variant: ghost },
639
+ { type: typography, variant: caption, color: muted, content: (str/concat "Stage " (+ @entity.cursorIndex 1) " of " (array/len @config.stages)) }
640
+ ] },
641
+ { type: progress-bar, value: (+ @entity.cursorIndex 1), max: (array/len @config.stages), showPercentage: false },
642
+ { type: stack, direction: horizontal, align: center, gap: xs, wrap: true, role: "group", aria-label: "Category lens", children: @entity.filterButtons },
643
+ { type: math-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.shapes, interactive: @config.interactive, animate: @config.animate, keyMap: { ArrowRight: NEXT, ArrowLeft: PREV, KeyR: RESET_STEPPER } },
575
644
  { type: card, variant: bordered, padding: md, title: (if (!= @entity.currentStageLabel "") @entity.currentStageLabel "Not Started"), children: [
576
645
  { type: typography, variant: body, content: @entity.currentStageDescription }
577
646
  ] },
578
- { type: stack, direction: horizontal, gap: xs, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
579
- { type: typography, variant: caption, content: @entity.visibleThreadNote }
647
+ { type: stack, direction: horizontal, gap: xs, wrap: true, children: (array/map @entity.tallyItems (fn t { type: badge, variant: neutral, label: (str/concat (object/get @t label) ": " (str/concat "" (object/get @t count))) })) },
648
+ { type: typography, variant: caption, color: muted, content: @entity.visibleThreadNote }
580
649
  ] }
581
650
  ]
582
651
  })
@@ -627,7 +696,11 @@ orbital LearnStepperOrbital {
627
696
  @tier "presentation"
628
697
  title : string = "Stepper"
629
698
  @label "Title"
630
- @description "Title shown above the instrument."
699
+ @description "The instrument's name, the header's one hero."
700
+ @tier "presentation"
701
+ summary : string = ""
702
+ @label "Concept"
703
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
631
704
  @tier "presentation"
632
705
  width : number = 640
633
706
  @label "Width"
@@ -637,8 +710,9 @@ orbital LearnStepperOrbital {
637
710
  @label "Height"
638
711
  @description "Canvas height in pixels."
639
712
  @tier "presentation"
640
- backgroundColor : string = "#f8fafc"
713
+ backgroundColor : string = ""
641
714
  @label "Background Color"
715
+ @description "Canvas ground override; empty uses the theme's drawing ground."
642
716
  @tier "presentation"
643
717
  interactive : boolean = false
644
718
  @label "Interactive"
@@ -658,8 +732,8 @@ orbital LearnStepperOrbital {
658
732
  @synonyms "stage roster, sequence, timeline, cycle, phase, event roster, ordered stages"
659
733
  @tier "domain"
660
734
  categories : [LearnStepperCategory] = [
661
- { category: "a", color: "#7c3aed", label: "Category A" },
662
- { category: "b", color: "#15803d", label: "Category B" }
735
+ { category: "a", color: "series-1", label: "Category A" },
736
+ { category: "b", color: "series-2", label: "Category B" }
663
737
  ]
664
738
  @label "Categories"
665
739
  @description "Marker color and button label per stage category, used for the axis dots, the legend, the FILTER_CATEGORY buttons, and the running per-category tally."
@@ -714,7 +788,7 @@ orbital LearnStepperOrbital {
714
788
  @label "Marker Highlight Radius"
715
789
  @description "Radius of the current step's marker, larger than an ordinary stage marker."
716
790
  @tier "presentation"
717
- unreachedColor : string = "#d6d3d1"
791
+ unreachedColor : string = "muted"
718
792
  @label "Unreached Marker Color"
719
793
  @description "Color of stage markers the learner has not yet stepped to."
720
794
  @tier "presentation"