@almadar/std 16.241.0 → 16.242.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 +134 -0
  8. package/behaviors/lolo/infra/atoms/std-page-treat.lolo +59 -0
  9. package/behaviors/lolo/infra/atoms/std-page-watch.lolo +86 -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 +867 -0
  55. package/behaviors/registry/infra/atoms/std-page-treat.orb +494 -0
  56. package/behaviors/registry/infra/atoms/std-page-watch.orb +554 -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
@@ -0,0 +1,153 @@
1
+ app std-doc-page "1.0.0"
2
+ "std-doc-page — one page of a documentation site: a section sidebar with the current page marked, a breadcrumb, the page title and lede, an \"on this page\" contents list, the page's own content (a render slot the call site fills, live behaviors included) and previous/next links. Supports right-to-left pages. Pick for docs sites, handbooks, reference manuals and knowledge bases."
3
+ type DocNavItem = { label : string!, href : string, items : [DocNavItem], active : boolean }
4
+ type DocCrumb = { label : string!, href : string }
5
+ type DocTocEntry = { id : string!, label : string!, level : number! }
6
+ type DocLink = { label : string!, href : string!, category : string }
7
+ orbital DocPageOrbital {
8
+
9
+ entity DocPageView [runtime] {
10
+ id : string!
11
+ }
12
+
13
+ trait DocPageRender -> @rebindable DocPageView [interaction, instance] {
14
+ initial: idle
15
+
16
+ state idle {
17
+ INIT -> idle
18
+ (render-ui main {
19
+ type: box
20
+ paddingY: lg
21
+ fullWidth: true
22
+ dir: @config.direction
23
+ children: [{
24
+ type: container
25
+ size: xl
26
+ maxWidth: xl
27
+ padding: lg
28
+ children: [{
29
+ type: split
30
+ ratio: "1:4"
31
+ gap: xl
32
+ children: [{
33
+ type: stack
34
+ direction: vertical
35
+ gap: sm
36
+ className: "lg:sticky lg:top-6 self-start"
37
+ children: [{
38
+ type: typography
39
+ content: @config.sidebarLabel
40
+ variant: label
41
+ weight: semibold
42
+ className: (if (== @config.sidebarLabel "") hidden "px-3")
43
+ }, {
44
+ type: doc-sidebar
45
+ items: @config.navItems
46
+ aria-label: @config.sidebarLabel
47
+ }]
48
+ }, {
49
+ type: stack
50
+ direction: vertical
51
+ gap: lg
52
+ className: "min-w-0"
53
+ children: [{
54
+ type: doc-breadcrumb
55
+ items: @config.crumbs
56
+ className: (if (== (array/len @config.crumbs) 0) hidden "")
57
+ }, {
58
+ type: stack
59
+ direction: vertical
60
+ gap: sm
61
+ children: [{
62
+ type: typography
63
+ content: @config.title
64
+ variant: h1
65
+ weight: bold
66
+ className: "tracking-tight text-balance"
67
+ }, {
68
+ type: typography
69
+ content: @config.lede
70
+ variant: large
71
+ color: muted
72
+ className: (if (== @config.lede "") hidden "max-w-3xl text-pretty")
73
+ }]
74
+ }, {
75
+ type: box
76
+ bg: muted
77
+ padding: md
78
+ rounded: lg
79
+ className: (if (== (array/len @config.toc) 0) hidden "")
80
+ children: [{
81
+ type: stack
82
+ direction: vertical
83
+ gap: xs
84
+ children: [{
85
+ type: typography
86
+ content: @config.tocLabel
87
+ variant: label
88
+ weight: semibold
89
+ }, {
90
+ type: doc-toc
91
+ items: @config.toc
92
+ aria-label: @config.tocLabel
93
+ }]
94
+ }]
95
+ }, @config.content, {
96
+ type: divider
97
+ }, {
98
+ type: doc-pagination
99
+ prev: (if (== (object/get @config.prev href "") "") null @config.prev)
100
+ next: (if (== (object/get @config.next href "") "") null @config.next)
101
+ }]
102
+ }]
103
+ }]
104
+ }]
105
+ })
106
+ }
107
+
108
+ config {
109
+ title : string = "Untitled page"
110
+ @label "Title"
111
+ @tier "presentation"
112
+ lede : string = ""
113
+ @label "Lede"
114
+ @description "One or two sentences under the title; empty hides it"
115
+ @tier "presentation"
116
+ content : render-ui = { type: stack, direction: vertical, gap: lg, children: [] }
117
+ @label "Content"
118
+ @description "The page body: markdown, code and live behaviors (trait references) in reading order"
119
+ @tier "presentation"
120
+ navItems : [DocNavItem] = []
121
+ @label "Sidebar"
122
+ @description "The documentation tree; mark the current page active"
123
+ @tier "presentation"
124
+ sidebarLabel : string = "Documentation"
125
+ @label "Sidebar label"
126
+ @tier "presentation"
127
+ crumbs : [DocCrumb] = []
128
+ @label "Breadcrumb"
129
+ @description "Ancestors first, the current page last without a link; empty hides it"
130
+ @tier "presentation"
131
+ toc : [DocTocEntry] = []
132
+ @label "On this page"
133
+ @description "Anchors of the page's sections (each section declares the same id); empty hides it"
134
+ @tier "presentation"
135
+ tocLabel : string = "On this page"
136
+ @label "Contents label"
137
+ @tier "presentation"
138
+ prev : DocLink = { label: "", href: "" }
139
+ @label "Previous page"
140
+ @description "Empty href hides it"
141
+ @tier "presentation"
142
+ next : DocLink = { label: "", href: "" }
143
+ @label "Next page"
144
+ @description "Empty href hides it"
145
+ @tier "presentation"
146
+ direction : "ltr" | "rtl" = "ltr"
147
+ @label "Direction"
148
+ @tier "presentation"
149
+ }
150
+ }
151
+
152
+ page "/doc" as DocPage -> DocPageRender
153
+ }
@@ -31,6 +31,11 @@ orbital LearnArrangementOrbital {
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 LearnArrangementItem = {
36
41
  id : string!
@@ -75,8 +80,8 @@ orbital LearnArrangementOrbital {
75
80
  (bx (+ @config.slotLeft (* @i @config.slotSpacing)))
76
81
  )
77
82
  [
78
- { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, color: @config.slotBorderColor, fill: @fillColor },
79
- { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), fontSize: @config.slotFontSize, align: "center", color: @config.slotTextColor }
83
+ { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, tone: @config.slotBorderColor, fillTone: @fillColor, fillStyle: "tint" },
84
+ { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), textSize: @config.slotFontSize, align: "center", tone: @config.slotTextColor }
80
85
  ]
81
86
  )))))
82
87
  )
@@ -87,23 +92,30 @@ orbital LearnArrangementOrbital {
87
92
  padding: md
88
93
  children: [
89
94
  { type: stack, direction: vertical, gap: md, children: [
90
- { type: typography, variant: h4, content: @entity.title },
91
- { type: typography, variant: body, content: "Tap items below to build the sequence in order, then press Check." },
92
- { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate },
93
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: sm })) },
94
- { type: stack, direction: horizontal, gap: sm, children: [
95
- { type: button, action: UNDO, label: @config.undoLabel, variant: ghost, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
96
- { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
97
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost }
95
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
96
+ { type: stack, direction: vertical, gap: xs, children: [
97
+ { type: typography, variant: h4, content: @entity.title },
98
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
99
+ ] },
100
+ { type: badge, label: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "Correct" (str/concat @entity.correctCount " / " @entity.totalItems " correct")) (str/concat (array/len @entity.placedIds) " / " @entity.totalItems " placed")), variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "error") "neutral") }
98
101
  ] },
99
- { type: stack, direction: horizontal, gap: sm, children: [
100
- { type: badge, variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "danger") "neutral"), label: (if @entity.checked @entity.verdict "Not checked yet") },
101
- { type: stat-display, label: "Score", value: @entity.correctCount, max: @entity.totalItems }
102
- ] }
102
+ { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: CHECK, Backspace: UNDO, KeyR: RETRY } },
103
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: lg, disabled: @entity.checked })) },
104
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
105
+ { type: button, action: CHECK, label: @config.checkLabel, icon: "check", variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
106
+ { type: button, action: UNDO, label: @config.undoLabel, icon: "chevron-left", variant: secondary, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
107
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost },
108
+ { type: typography, variant: caption, color: muted, content: (str/concat "Placed " (array/len @entity.placedIds) " of " @entity.totalItems) }
109
+ ] },
110
+ { type: typography, variant: body, content: (if @entity.checked @entity.verdict "Tap items to build the sequence in order, then press Check.") }
103
111
  ] }
104
112
  ]
105
113
  })
106
114
 
115
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
116
+ (set @entity.active false)
117
+ (render-ui main null)
118
+
107
119
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
108
120
  (set @entity.active true)
109
121
  (set @entity.title @config.title)
@@ -124,8 +136,8 @@ orbital LearnArrangementOrbital {
124
136
  (bx (+ @config.slotLeft (* @i @config.slotSpacing)))
125
137
  )
126
138
  [
127
- { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, color: @config.slotBorderColor, fill: @fillColor },
128
- { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), fontSize: @config.slotFontSize, align: "center", color: @config.slotTextColor }
139
+ { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, tone: @config.slotBorderColor, fillTone: @fillColor, fillStyle: "tint" },
140
+ { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), textSize: @config.slotFontSize, align: "center", tone: @config.slotTextColor }
129
141
  ]
130
142
  )))))
131
143
  )
@@ -136,19 +148,22 @@ orbital LearnArrangementOrbital {
136
148
  padding: md
137
149
  children: [
138
150
  { type: stack, direction: vertical, gap: md, children: [
139
- { type: typography, variant: h4, content: @entity.title },
140
- { type: typography, variant: body, content: "Tap items below to build the sequence in order, then press Check." },
141
- { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate },
142
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: sm })) },
143
- { type: stack, direction: horizontal, gap: sm, children: [
144
- { type: button, action: UNDO, label: @config.undoLabel, variant: ghost, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
145
- { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
146
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost }
151
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
152
+ { type: stack, direction: vertical, gap: xs, children: [
153
+ { type: typography, variant: h4, content: @entity.title },
154
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
155
+ ] },
156
+ { type: badge, label: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "Correct" (str/concat @entity.correctCount " / " @entity.totalItems " correct")) (str/concat (array/len @entity.placedIds) " / " @entity.totalItems " placed")), variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "error") "neutral") }
147
157
  ] },
148
- { type: stack, direction: horizontal, gap: sm, children: [
149
- { type: badge, variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "danger") "neutral"), label: (if @entity.checked @entity.verdict "Not checked yet") },
150
- { type: stat-display, label: "Score", value: @entity.correctCount, max: @entity.totalItems }
151
- ] }
158
+ { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: CHECK, Backspace: UNDO, KeyR: RETRY } },
159
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: lg, disabled: @entity.checked })) },
160
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
161
+ { type: button, action: CHECK, label: @config.checkLabel, icon: "check", variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
162
+ { type: button, action: UNDO, label: @config.undoLabel, icon: "chevron-left", variant: secondary, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
163
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost },
164
+ { type: typography, variant: caption, color: muted, content: (str/concat "Placed " (array/len @entity.placedIds) " of " @entity.totalItems) }
165
+ ] },
166
+ { type: typography, variant: body, content: (if @entity.checked @entity.verdict "Tap items to build the sequence in order, then press Check.") }
152
167
  ] }
153
168
  ]
154
169
  })
@@ -170,8 +185,8 @@ orbital LearnArrangementOrbital {
170
185
  (bx (+ @config.slotLeft (* @i @config.slotSpacing)))
171
186
  )
172
187
  [
173
- { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, color: @config.slotBorderColor, fill: @fillColor },
174
- { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), fontSize: @config.slotFontSize, align: "center", color: @config.slotTextColor }
188
+ { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, tone: @config.slotBorderColor, fillTone: @fillColor, fillStyle: "tint" },
189
+ { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), textSize: @config.slotFontSize, align: "center", tone: @config.slotTextColor }
175
190
  ]
176
191
  )))))
177
192
  )
@@ -182,19 +197,22 @@ orbital LearnArrangementOrbital {
182
197
  padding: md
183
198
  children: [
184
199
  { type: stack, direction: vertical, gap: md, children: [
185
- { type: typography, variant: h4, content: @entity.title },
186
- { type: typography, variant: body, content: "Tap items below to build the sequence in order, then press Check." },
187
- { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate },
188
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: sm })) },
189
- { type: stack, direction: horizontal, gap: sm, children: [
190
- { type: button, action: UNDO, label: @config.undoLabel, variant: ghost, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
191
- { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
192
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost }
200
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
201
+ { type: stack, direction: vertical, gap: xs, children: [
202
+ { type: typography, variant: h4, content: @entity.title },
203
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
204
+ ] },
205
+ { type: badge, label: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "Correct" (str/concat @entity.correctCount " / " @entity.totalItems " correct")) (str/concat (array/len @entity.placedIds) " / " @entity.totalItems " placed")), variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "error") "neutral") }
206
+ ] },
207
+ { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: CHECK, Backspace: UNDO, KeyR: RETRY } },
208
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: lg, disabled: @entity.checked })) },
209
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
210
+ { type: button, action: CHECK, label: @config.checkLabel, icon: "check", variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
211
+ { type: button, action: UNDO, label: @config.undoLabel, icon: "chevron-left", variant: secondary, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
212
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost },
213
+ { type: typography, variant: caption, color: muted, content: (str/concat "Placed " (array/len @entity.placedIds) " of " @entity.totalItems) }
193
214
  ] },
194
- { type: stack, direction: horizontal, gap: sm, children: [
195
- { type: badge, variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "danger") "neutral"), label: (if @entity.checked @entity.verdict "Not checked yet") },
196
- { type: stat-display, label: "Score", value: @entity.correctCount, max: @entity.totalItems }
197
- ] }
215
+ { type: typography, variant: body, content: (if @entity.checked @entity.verdict "Tap items to build the sequence in order, then press Check.") }
198
216
  ] }
199
217
  ]
200
218
  })
@@ -216,8 +234,8 @@ orbital LearnArrangementOrbital {
216
234
  (bx (+ @config.slotLeft (* @i @config.slotSpacing)))
217
235
  )
218
236
  [
219
- { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, color: @config.slotBorderColor, fill: @fillColor },
220
- { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), fontSize: @config.slotFontSize, align: "center", color: @config.slotTextColor }
237
+ { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, tone: @config.slotBorderColor, fillTone: @fillColor, fillStyle: "tint" },
238
+ { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), textSize: @config.slotFontSize, align: "center", tone: @config.slotTextColor }
221
239
  ]
222
240
  )))))
223
241
  )
@@ -228,19 +246,22 @@ orbital LearnArrangementOrbital {
228
246
  padding: md
229
247
  children: [
230
248
  { type: stack, direction: vertical, gap: md, children: [
231
- { type: typography, variant: h4, content: @entity.title },
232
- { type: typography, variant: body, content: "Tap items below to build the sequence in order, then press Check." },
233
- { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate },
234
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: sm })) },
235
- { type: stack, direction: horizontal, gap: sm, children: [
236
- { type: button, action: UNDO, label: @config.undoLabel, variant: ghost, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
237
- { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
238
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost }
249
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
250
+ { type: stack, direction: vertical, gap: xs, children: [
251
+ { type: typography, variant: h4, content: @entity.title },
252
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
253
+ ] },
254
+ { type: badge, label: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "Correct" (str/concat @entity.correctCount " / " @entity.totalItems " correct")) (str/concat (array/len @entity.placedIds) " / " @entity.totalItems " placed")), variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "error") "neutral") }
239
255
  ] },
240
- { type: stack, direction: horizontal, gap: sm, children: [
241
- { type: badge, variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "danger") "neutral"), label: (if @entity.checked @entity.verdict "Not checked yet") },
242
- { type: stat-display, label: "Score", value: @entity.correctCount, max: @entity.totalItems }
243
- ] }
256
+ { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: CHECK, Backspace: UNDO, KeyR: RETRY } },
257
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: lg, disabled: @entity.checked })) },
258
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
259
+ { type: button, action: CHECK, label: @config.checkLabel, icon: "check", variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
260
+ { type: button, action: UNDO, label: @config.undoLabel, icon: "chevron-left", variant: secondary, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
261
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost },
262
+ { type: typography, variant: caption, color: muted, content: (str/concat "Placed " (array/len @entity.placedIds) " of " @entity.totalItems) }
263
+ ] },
264
+ { type: typography, variant: body, content: (if @entity.checked @entity.verdict "Tap items to build the sequence in order, then press Check.") }
244
265
  ] }
245
266
  ]
246
267
  })
@@ -272,8 +293,8 @@ orbital LearnArrangementOrbital {
272
293
  (bx (+ @config.slotLeft (* @i @config.slotSpacing)))
273
294
  )
274
295
  [
275
- { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, color: @config.slotBorderColor, fill: @fillColor },
276
- { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), fontSize: @config.slotFontSize, align: "center", color: @config.slotTextColor }
296
+ { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, tone: @config.slotBorderColor, fillTone: @fillColor, fillStyle: "tint" },
297
+ { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), textSize: @config.slotFontSize, align: "center", tone: @config.slotTextColor }
277
298
  ]
278
299
  )))))
279
300
  )
@@ -284,19 +305,22 @@ orbital LearnArrangementOrbital {
284
305
  padding: md
285
306
  children: [
286
307
  { type: stack, direction: vertical, gap: md, children: [
287
- { type: typography, variant: h4, content: @entity.title },
288
- { type: typography, variant: body, content: "Tap items below to build the sequence in order, then press Check." },
289
- { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate },
290
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: sm })) },
291
- { type: stack, direction: horizontal, gap: sm, children: [
292
- { type: button, action: UNDO, label: @config.undoLabel, variant: ghost, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
293
- { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
294
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost }
308
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
309
+ { type: stack, direction: vertical, gap: xs, children: [
310
+ { type: typography, variant: h4, content: @entity.title },
311
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
312
+ ] },
313
+ { type: badge, label: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "Correct" (str/concat @entity.correctCount " / " @entity.totalItems " correct")) (str/concat (array/len @entity.placedIds) " / " @entity.totalItems " placed")), variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "error") "neutral") }
314
+ ] },
315
+ { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: CHECK, Backspace: UNDO, KeyR: RETRY } },
316
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: lg, disabled: @entity.checked })) },
317
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
318
+ { type: button, action: CHECK, label: @config.checkLabel, icon: "check", variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
319
+ { type: button, action: UNDO, label: @config.undoLabel, icon: "chevron-left", variant: secondary, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
320
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost },
321
+ { type: typography, variant: caption, color: muted, content: (str/concat "Placed " (array/len @entity.placedIds) " of " @entity.totalItems) }
295
322
  ] },
296
- { type: stack, direction: horizontal, gap: sm, children: [
297
- { type: badge, variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "danger") "neutral"), label: (if @entity.checked @entity.verdict "Not checked yet") },
298
- { type: stat-display, label: "Score", value: @entity.correctCount, max: @entity.totalItems }
299
- ] }
323
+ { type: typography, variant: body, content: (if @entity.checked @entity.verdict "Tap items to build the sequence in order, then press Check.") }
300
324
  ] }
301
325
  ]
302
326
  })
@@ -320,8 +344,8 @@ orbital LearnArrangementOrbital {
320
344
  (bx (+ @config.slotLeft (* @i @config.slotSpacing)))
321
345
  )
322
346
  [
323
- { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, color: @config.slotBorderColor, fill: @fillColor },
324
- { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), fontSize: @config.slotFontSize, align: "center", color: @config.slotTextColor }
347
+ { type: "rect", id: (str/concat "slot-" @i), x: @bx, y: @config.slotY, width: @config.slotWidth, height: @config.slotHeight, tone: @config.slotBorderColor, fillTone: @fillColor, fillStyle: "tint" },
348
+ { type: "text", id: (str/concat "slot-label-" @i), x: (+ @bx (/ @config.slotWidth 2)), y: (+ @config.slotY (/ @config.slotHeight 2) 5), text: (if @filled @label (str/concat "" (+ @i 1))), textSize: @config.slotFontSize, align: "center", tone: @config.slotTextColor }
325
349
  ]
326
350
  )))))
327
351
  )
@@ -332,19 +356,22 @@ orbital LearnArrangementOrbital {
332
356
  padding: md
333
357
  children: [
334
358
  { type: stack, direction: vertical, gap: md, children: [
335
- { type: typography, variant: h4, content: @entity.title },
336
- { type: typography, variant: body, content: "Tap items below to build the sequence in order, then press Check." },
337
- { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate },
338
- { type: stack, direction: horizontal, gap: xs, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: sm })) },
339
- { type: stack, direction: horizontal, gap: sm, children: [
340
- { type: button, action: UNDO, label: @config.undoLabel, variant: ghost, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
341
- { type: button, action: CHECK, label: @config.checkLabel, variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
342
- { type: button, action: RETRY, label: @config.retryLabel, variant: ghost }
359
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
360
+ { type: stack, direction: vertical, gap: xs, children: [
361
+ { type: typography, variant: h4, content: @entity.title },
362
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
363
+ ] },
364
+ { type: badge, label: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "Correct" (str/concat @entity.correctCount " / " @entity.totalItems " correct")) (str/concat (array/len @entity.placedIds) " / " @entity.totalItems " placed")), variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "error") "neutral") }
343
365
  ] },
344
- { type: stack, direction: horizontal, gap: sm, children: [
345
- { type: badge, variant: (if @entity.checked (if (== @entity.correctCount @entity.totalItems) "success" "danger") "neutral"), label: (if @entity.checked @entity.verdict "Not checked yet") },
346
- { type: stat-display, label: "Score", value: @entity.correctCount, max: @entity.totalItems }
347
- ] }
366
+ { type: math-canvas, width: @config.width, height: @config.height, xMin: 0, xMax: @config.width, yMin: 0, yMax: @config.height, showAxes: false, showGrid: false, gridStep: 1, curves: [], points: [], vectors: [], shapes: @entity.slotShapes, interactive: @config.interactive, animate: @config.animate, keyMap: { Space: CHECK, Backspace: UNDO, KeyR: RETRY } },
367
+ { type: stack, direction: vertical, gap: sm, children: (array/map (array/filter @config.items (fn it (not (array/includes @entity.placedIds (object/get @it id))))) (fn it { type: button, action: PLACE, actionPayload: { itemId: (object/get @it id) }, label: (if (!= (object/get @it hint "") "") (str/concat (object/get @it label) " — " (object/get @it hint "")) (object/get @it label)), variant: secondary, size: lg, disabled: @entity.checked })) },
368
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
369
+ { type: button, action: CHECK, label: @config.checkLabel, icon: "check", variant: primary, disabled: (if (>= (array/len @entity.placedIds) @entity.totalItems) false true) },
370
+ { type: button, action: UNDO, label: @config.undoLabel, icon: "chevron-left", variant: secondary, disabled: (if (> (array/len @entity.placedIds) 0) false true) },
371
+ { type: button, action: RETRY, label: @config.retryLabel, icon: "rotate-ccw", variant: ghost },
372
+ { type: typography, variant: caption, color: muted, content: (str/concat "Placed " (array/len @entity.placedIds) " of " @entity.totalItems) }
373
+ ] },
374
+ { type: typography, variant: body, content: (if @entity.checked @entity.verdict "Tap items to build the sequence in order, then press Check.") }
348
375
  ] }
349
376
  ]
350
377
  })
@@ -407,6 +434,10 @@ orbital LearnArrangementOrbital {
407
434
  @label "Title"
408
435
  @description "Title shown above the instrument."
409
436
  @tier "presentation"
437
+ summary : string = ""
438
+ @label "Concept"
439
+ @description "One line under the title saying what is being ordered (keep it under ~65 characters). Empty shows no line."
440
+ @tier "presentation"
410
441
  width : number = 640
411
442
  @label "Width"
412
443
  @description "Canvas width in pixels."
@@ -434,19 +465,19 @@ orbital LearnArrangementOrbital {
434
465
  @description "Roster of items to order; each carries its label, its zero-based correct position in the finished sequence, and an optional hint shown beneath its button while unplaced."
435
466
  @synonyms "item, step, stage, sequence, order, ranking, procedure"
436
467
  @tier "domain"
437
- correctColor : string = "#16a34a"
468
+ correctColor : string = "success"
438
469
  @label "Correct Slot Color"
439
470
  @description "Fill color for a filled slot whose item matches its correct position, once graded."
440
471
  @tier "presentation"
441
- incorrectColor : string = "#dc2626"
472
+ incorrectColor : string = "error"
442
473
  @label "Incorrect Slot Color"
443
474
  @description "Fill color for a filled slot whose item does not match its correct position, once graded."
444
475
  @tier "presentation"
445
- pendingColor : string = "#94a3b8"
476
+ pendingColor : string = "series-2"
446
477
  @label "Pending Slot Color"
447
478
  @description "Fill color for a filled slot before grading."
448
479
  @tier "presentation"
449
- emptySlotColor : string = "#e2e8f0"
480
+ emptySlotColor : string = "fill"
450
481
  @label "Empty Slot Color"
451
482
  @description "Fill color for a slot with no item placed yet."
452
483
  @tier "presentation"
@@ -465,13 +496,14 @@ orbital LearnArrangementOrbital {
465
496
  slotY : number = 70
466
497
  @label "Slot Row Y (px)"
467
498
  @tier "presentation"
468
- slotFontSize : number = 13
469
- @label "Slot Font Size"
499
+ slotFontSize : string = "sm"
500
+ @label "Slot Text Size"
501
+ @description "Slot label size role: xs, sm, base or lg."
470
502
  @tier "presentation"
471
- slotBorderColor : string = "#334155"
503
+ slotBorderColor : string = "ink"
472
504
  @label "Slot Border Color"
473
505
  @tier "presentation"
474
- slotTextColor : string = "#0f172a"
506
+ slotTextColor : string = "label"
475
507
  @label "Slot Text Color"
476
508
  @tier "presentation"
477
509
  undoLabel : string = "Undo"