@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
@@ -9,6 +9,7 @@ orbital LearnTransportOrbital {
9
9
  steps : int = 0
10
10
  elapsedMs : number = 0
11
11
  done : boolean = false
12
+ speed : number = 1
12
13
  }
13
14
 
14
15
  trait LearnTransportSim -> @rebindable LearnTransportScene [interaction, instance] {
@@ -25,14 +26,26 @@ orbital LearnTransportOrbital {
25
26
  type: box
26
27
  padding: md
27
28
  children: [
28
- { type: stack, direction: vertical, gap: sm, children: [
29
- { type: typography, variant: h4, content: @entity.title },
30
- { type: stack, direction: horizontal, gap: sm, children: [
31
- { type: button, action: STEP, label: "Step", variant: primary },
32
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
33
- { type: button, action: RESET, label: "Reset", variant: ghost }
29
+ { type: stack, direction: vertical, gap: md, children: [
30
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
31
+ { type: stack, direction: vertical, gap: xs, children: [
32
+ { type: typography, variant: h4, content: @entity.title },
33
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
34
+ ] },
35
+ { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
34
36
  ] },
35
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
37
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
38
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: @entity.done }),
39
+ { type: button, action: STEP, label: "Step", icon: "step-forward", variant: secondary, disabled: (or @entity.running @entity.done) },
40
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
41
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
42
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
43
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
44
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
45
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
46
+ ] },
47
+ { type: typography, variant: caption, color: muted, content: (if (> @config.totalSteps 0) (str/concat "Step " @entity.steps " of " @config.totalSteps) (str/concat "Step " @entity.steps)) }
48
+ ] }
36
49
  ] }
37
50
  ]
38
51
  })
@@ -48,33 +61,62 @@ orbital LearnTransportOrbital {
48
61
  type: box
49
62
  padding: md
50
63
  children: [
51
- { type: stack, direction: vertical, gap: sm, children: [
52
- { type: typography, variant: h4, content: @entity.title },
53
- { type: stack, direction: horizontal, gap: sm, children: [
54
- { type: button, action: STEP, label: "Step", variant: primary },
55
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
56
- { type: button, action: RESET, label: "Reset", variant: ghost }
64
+ { type: stack, direction: vertical, gap: md, children: [
65
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
66
+ { type: stack, direction: vertical, gap: xs, children: [
67
+ { type: typography, variant: h4, content: @entity.title },
68
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
69
+ ] },
70
+ { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
57
71
  ] },
58
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
72
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
73
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: @entity.done }),
74
+ { type: button, action: STEP, label: "Step", icon: "step-forward", variant: secondary, disabled: (or @entity.running @entity.done) },
75
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
76
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
77
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
78
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
79
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
80
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
81
+ ] },
82
+ { type: typography, variant: caption, color: muted, content: (if (> @config.totalSteps 0) (str/concat "Step " @entity.steps " of " @config.totalSteps) (str/concat "Step " @entity.steps)) }
83
+ ] }
59
84
  ] }
60
85
  ]
61
86
  })
62
87
  (emit LEARN_UPDATED { running: @entity.running, steps: @entity.steps })
63
88
 
89
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
90
+ (set @entity.active false)
91
+ (set @entity.running false)
92
+ (render-ui main null)
93
+
64
94
  STEP -> idle when (and (== @entity.active true) (not (== @entity.done true)))
65
95
  (set @entity.steps (+ @entity.steps 1))
66
96
  (render-ui main {
67
97
  type: box
68
98
  padding: md
69
99
  children: [
70
- { type: stack, direction: vertical, gap: sm, children: [
71
- { type: typography, variant: h4, content: @entity.title },
72
- { type: stack, direction: horizontal, gap: sm, children: [
73
- { type: button, action: STEP, label: "Step", variant: primary },
74
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
75
- { type: button, action: RESET, label: "Reset", variant: ghost }
100
+ { type: stack, direction: vertical, gap: md, children: [
101
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
102
+ { type: stack, direction: vertical, gap: xs, children: [
103
+ { type: typography, variant: h4, content: @entity.title },
104
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
105
+ ] },
106
+ { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
76
107
  ] },
77
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
108
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
109
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: @entity.done }),
110
+ { type: button, action: STEP, label: "Step", icon: "step-forward", variant: secondary, disabled: (or @entity.running @entity.done) },
111
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
112
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
113
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
114
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
115
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
116
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
117
+ ] },
118
+ { type: typography, variant: caption, color: muted, content: (if (> @config.totalSteps 0) (str/concat "Step " @entity.steps " of " @config.totalSteps) (str/concat "Step " @entity.steps)) }
119
+ ] }
78
120
  ] }
79
121
  ]
80
122
  })
@@ -86,14 +128,26 @@ orbital LearnTransportOrbital {
86
128
  type: box
87
129
  padding: md
88
130
  children: [
89
- { type: stack, direction: vertical, gap: sm, children: [
90
- { type: typography, variant: h4, content: @entity.title },
91
- { type: stack, direction: horizontal, gap: sm, children: [
92
- { type: button, action: STEP, label: "Step", variant: primary },
93
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
94
- { type: button, action: RESET, label: "Reset", variant: ghost }
131
+ { type: stack, direction: vertical, gap: md, children: [
132
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
133
+ { type: stack, direction: vertical, gap: xs, children: [
134
+ { type: typography, variant: h4, content: @entity.title },
135
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
136
+ ] },
137
+ { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
95
138
  ] },
96
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
139
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
140
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: @entity.done }),
141
+ { type: button, action: STEP, label: "Step", icon: "step-forward", variant: secondary, disabled: (or @entity.running @entity.done) },
142
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
143
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
144
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
145
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
146
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
147
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
148
+ ] },
149
+ { type: typography, variant: caption, color: muted, content: (if (> @config.totalSteps 0) (str/concat "Step " @entity.steps " of " @config.totalSteps) (str/concat "Step " @entity.steps)) }
150
+ ] }
97
151
  ] }
98
152
  ]
99
153
  })
@@ -107,25 +161,67 @@ orbital LearnTransportOrbital {
107
161
  type: box
108
162
  padding: md
109
163
  children: [
110
- { type: stack, direction: vertical, gap: sm, children: [
111
- { type: typography, variant: h4, content: @entity.title },
112
- { type: stack, direction: horizontal, gap: sm, children: [
113
- { type: button, action: STEP, label: "Step", variant: primary },
114
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
115
- { type: button, action: RESET, label: "Reset", variant: ghost }
164
+ { type: stack, direction: vertical, gap: md, children: [
165
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
166
+ { type: stack, direction: vertical, gap: xs, children: [
167
+ { type: typography, variant: h4, content: @entity.title },
168
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
169
+ ] },
170
+ { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
116
171
  ] },
117
- { type: typography, variant: caption, content: (str/concat "Steps: " @entity.steps (if @entity.done " — done" (if @entity.running " — auto-stepping" " — paused"))) }
172
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
173
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: @entity.done }),
174
+ { type: button, action: STEP, label: "Step", icon: "step-forward", variant: secondary, disabled: (or @entity.running @entity.done) },
175
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
176
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
177
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
178
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
179
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
180
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
181
+ ] },
182
+ { type: typography, variant: caption, color: muted, content: (if (> @config.totalSteps 0) (str/concat "Step " @entity.steps " of " @config.totalSteps) (str/concat "Step " @entity.steps)) }
183
+ ] }
118
184
  ] }
119
185
  ]
120
186
  })
121
187
  (emit LEARN_RESET { })
188
+
189
+ SPEED_SET -> idle when (== @entity.active true)
190
+ (set @entity.speed ?speed)
191
+ (render-ui main {
192
+ type: box
193
+ padding: md
194
+ children: [
195
+ { type: stack, direction: vertical, gap: md, children: [
196
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
197
+ { type: stack, direction: vertical, gap: xs, children: [
198
+ { type: typography, variant: h4, content: @entity.title },
199
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
200
+ ] },
201
+ { type: badge, label: (if @entity.done "Done" (if @entity.running "Running" "Paused")), variant: (if @entity.done "success" (if @entity.running "primary" "neutral")) }
202
+ ] },
203
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
204
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: primary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Play", icon: "play", variant: primary, aria-pressed: "false", disabled: @entity.done }),
205
+ { type: button, action: STEP, label: "Step", icon: "step-forward", variant: secondary, disabled: (or @entity.running @entity.done) },
206
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
207
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
208
+ { type: button, action: SPEED_SET, actionPayload: { speed: 0.5 }, label: "0.5×", size: sm, variant: (if (== @entity.speed 0.5) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 0.5) "true" "false") },
209
+ { type: button, action: SPEED_SET, actionPayload: { speed: 1 }, label: "1×", size: sm, variant: (if (== @entity.speed 1) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 1) "true" "false") },
210
+ { type: button, action: SPEED_SET, actionPayload: { speed: 2 }, label: "2×", size: sm, variant: (if (== @entity.speed 2) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 2) "true" "false") },
211
+ { type: button, action: SPEED_SET, actionPayload: { speed: 4 }, label: "4×", size: sm, variant: (if (== @entity.speed 4) "secondary" "ghost"), aria-pressed: (if (== @entity.speed 4) "true" "false") }
212
+ ] },
213
+ { type: typography, variant: caption, color: muted, content: (if (> @config.totalSteps 0) (str/concat "Step " @entity.steps " of " @config.totalSteps) (str/concat "Step " @entity.steps)) }
214
+ ] }
215
+ ] }
216
+ ]
217
+ })
122
218
  }
123
219
 
124
220
  ticks {
125
221
  transportTick every 32ms
126
222
  when (and (== @entity.active true) (and (== @entity.running true) (not (== @entity.done true))))
127
223
  (set @entity.elapsedMs (+ @entity.elapsedMs 32))
128
- (when (>= @entity.elapsedMs @config.stepIntervalMs)
224
+ (when (>= (* @entity.elapsedMs @entity.speed) @config.stepIntervalMs)
129
225
  (do
130
226
  (set @entity.elapsedMs 0)
131
227
  (set @entity.steps (+ @entity.steps 1))
@@ -134,10 +230,15 @@ orbital LearnTransportOrbital {
134
230
  }
135
231
 
136
232
  listens {
233
+ SPEED_SET {
234
+ speed : number
235
+ }
236
+ @description "The learner picked a playback speed; the clock steps that many times faster."
237
+ @tier "presentation"
137
238
  MODE_SELECTED {
138
239
  mode : string
139
240
  }
140
- @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
241
+ @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, and stands down (stops its clock, goes inactive) when another mode is chosen."
141
242
  @tier "domain"
142
243
  }
143
244
 
@@ -169,7 +270,15 @@ orbital LearnTransportOrbital {
169
270
  @tier "presentation"
170
271
  title : string = "Learning Instrument"
171
272
  @label "Title"
172
- @description "Title shown above the transport controls."
273
+ @description "The instrument's name, the header's one hero."
274
+ @tier "presentation"
275
+ summary : string = ""
276
+ @label "Concept"
277
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
278
+ @tier "presentation"
279
+ totalSteps : int = 0
280
+ @label "Total Steps"
281
+ @description "When the walk has a known length, the step counter reads 'Step N of M'. 0 means open-ended."
173
282
  @tier "presentation"
174
283
  autoRun : boolean = false
175
284
  @label "Auto Run"
@@ -74,21 +74,33 @@ orbital LearnTreeOrbital {
74
74
  padding: md
75
75
  children: [
76
76
  { type: stack, direction: vertical, gap: md, children: [
77
- { type: typography, variant: h4, content: @entity.title },
78
- { type: stack, direction: horizontal, gap: sm, children: [
79
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
77
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
78
+ { type: stack, direction: vertical, gap: xs, children: [
79
+ { type: typography, variant: h4, content: @entity.title },
80
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
81
+ ] },
82
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
83
+ ] },
84
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
85
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
80
86
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
81
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
82
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
83
- { type: button, action: RESET, label: "Reset", variant: ghost }
87
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
88
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
89
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
90
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
84
91
  ] },
85
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
92
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
86
93
  { type: typography, variant: body, content: @entity.narration }
87
94
  ] }
88
95
  ]
89
96
  })
90
97
  (emit TREE_UPDATED { nodes: @entity.nodes, edges: @entity.edges, count: @entity.count, narration: @entity.narration })
91
98
 
99
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
100
+ (set @entity.active false)
101
+ (set @entity.running false)
102
+ (render-ui main null)
103
+
92
104
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
93
105
  (set @entity.active true)
94
106
  (set @entity.title @config.title)
@@ -112,15 +124,22 @@ orbital LearnTreeOrbital {
112
124
  padding: md
113
125
  children: [
114
126
  { type: stack, direction: vertical, gap: md, children: [
115
- { type: typography, variant: h4, content: @entity.title },
116
- { type: stack, direction: horizontal, gap: sm, children: [
117
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
127
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
128
+ { type: stack, direction: vertical, gap: xs, children: [
129
+ { type: typography, variant: h4, content: @entity.title },
130
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
131
+ ] },
132
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
133
+ ] },
134
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
135
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
118
136
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
119
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
120
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
121
- { type: button, action: RESET, label: "Reset", variant: ghost }
137
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
138
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
139
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
140
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
122
141
  ] },
123
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
142
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
124
143
  { type: typography, variant: body, content: @entity.narration }
125
144
  ] }
126
145
  ]
@@ -198,15 +217,22 @@ orbital LearnTreeOrbital {
198
217
  padding: md
199
218
  children: [
200
219
  { type: stack, direction: vertical, gap: md, children: [
201
- { type: typography, variant: h4, content: @entity.title },
202
- { type: stack, direction: horizontal, gap: sm, children: [
203
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
220
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
221
+ { type: stack, direction: vertical, gap: xs, children: [
222
+ { type: typography, variant: h4, content: @entity.title },
223
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
224
+ ] },
225
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
226
+ ] },
227
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
228
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
204
229
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
205
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
206
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
207
- { type: button, action: RESET, label: "Reset", variant: ghost }
230
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
231
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
232
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
233
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
208
234
  ] },
209
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
235
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
210
236
  { type: typography, variant: body, content: @entity.narration }
211
237
  ] }
212
238
  ]
@@ -238,15 +264,22 @@ orbital LearnTreeOrbital {
238
264
  padding: md
239
265
  children: [
240
266
  { type: stack, direction: vertical, gap: md, children: [
241
- { type: typography, variant: h4, content: @entity.title },
242
- { type: stack, direction: horizontal, gap: sm, children: [
243
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
267
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
268
+ { type: stack, direction: vertical, gap: xs, children: [
269
+ { type: typography, variant: h4, content: @entity.title },
270
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
271
+ ] },
272
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
273
+ ] },
274
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
275
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
244
276
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
245
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
246
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
247
- { type: button, action: RESET, label: "Reset", variant: ghost }
277
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
278
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
279
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
280
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
248
281
  ] },
249
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
282
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
250
283
  { type: typography, variant: body, content: @entity.narration }
251
284
  ] }
252
285
  ]
@@ -292,15 +325,22 @@ orbital LearnTreeOrbital {
292
325
  padding: md
293
326
  children: [
294
327
  { type: stack, direction: vertical, gap: md, children: [
295
- { type: typography, variant: h4, content: @entity.title },
296
- { type: stack, direction: horizontal, gap: sm, children: [
297
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
328
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
329
+ { type: stack, direction: vertical, gap: xs, children: [
330
+ { type: typography, variant: h4, content: @entity.title },
331
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
332
+ ] },
333
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
334
+ ] },
335
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
336
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
298
337
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
299
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
300
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
301
- { type: button, action: RESET, label: "Reset", variant: ghost }
338
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
339
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
340
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
341
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
302
342
  ] },
303
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
343
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
304
344
  { type: typography, variant: body, content: @entity.narration }
305
345
  ] }
306
346
  ]
@@ -315,15 +355,22 @@ orbital LearnTreeOrbital {
315
355
  padding: md
316
356
  children: [
317
357
  { type: stack, direction: vertical, gap: md, children: [
318
- { type: typography, variant: h4, content: @entity.title },
319
- { type: stack, direction: horizontal, gap: sm, children: [
320
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
358
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
359
+ { type: stack, direction: vertical, gap: xs, children: [
360
+ { type: typography, variant: h4, content: @entity.title },
361
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
362
+ ] },
363
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
364
+ ] },
365
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
366
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
321
367
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
322
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
323
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
324
- { type: button, action: RESET, label: "Reset", variant: ghost }
368
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
369
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
370
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
371
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
325
372
  ] },
326
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
373
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
327
374
  { type: typography, variant: body, content: @entity.narration }
328
375
  ] }
329
376
  ]
@@ -351,15 +398,22 @@ orbital LearnTreeOrbital {
351
398
  padding: md
352
399
  children: [
353
400
  { type: stack, direction: vertical, gap: md, children: [
354
- { type: typography, variant: h4, content: @entity.title },
355
- { type: stack, direction: horizontal, gap: sm, children: [
356
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
401
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
402
+ { type: stack, direction: vertical, gap: xs, children: [
403
+ { type: typography, variant: h4, content: @entity.title },
404
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
405
+ ] },
406
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
407
+ ] },
408
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
409
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
357
410
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
358
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
359
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
360
- { type: button, action: RESET, label: "Reset", variant: ghost }
411
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
412
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
413
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
414
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
361
415
  ] },
362
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
416
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
363
417
  { type: typography, variant: body, content: @entity.narration }
364
418
  ] }
365
419
  ]
@@ -408,15 +462,22 @@ orbital LearnTreeOrbital {
408
462
  padding: md
409
463
  children: [
410
464
  { type: stack, direction: vertical, gap: md, children: [
411
- { type: typography, variant: h4, content: @entity.title },
412
- { type: stack, direction: horizontal, gap: sm, children: [
413
- { type: button, action: INSERT_VALUE, label: "Insert", variant: primary },
465
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
466
+ { type: stack, direction: vertical, gap: xs, children: [
467
+ { type: typography, variant: h4, content: @entity.title },
468
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
469
+ ] },
470
+ { type: badge, label: (if @entity.traversing "Traversing" (if @entity.running "Auto-stepping" "Building")), variant: (if @entity.traversing "success" (if @entity.running "primary" "neutral")) }
471
+ ] },
472
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
473
+ { type: button, action: INSERT_VALUE, label: "Insert", icon: "plus", variant: primary },
414
474
  { type: button, action: DELETE_VALUE, label: "Delete", variant: secondary },
415
- { type: button, action: RUN_TRAVERSAL, label: "Traverse", variant: secondary },
416
- { type: button, action: TOGGLE_RUN, label: (if @entity.running "Pause" "Run"), variant: secondary },
417
- { type: button, action: RESET, label: "Reset", variant: ghost }
475
+ { type: button, action: RUN_TRAVERSAL, label: "Traverse", icon: "step-forward", variant: secondary },
476
+ (if @entity.running { type: button, action: TOGGLE_RUN, label: "Pause", icon: "pause", variant: secondary, aria-pressed: "true" } { type: button, action: TOGGLE_RUN, label: "Run", icon: "play", variant: secondary, aria-pressed: "false" }),
477
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
478
+ { type: typography, variant: caption, color: muted, content: (str/concat (str/concat "" @entity.count) " nodes") }
418
479
  ] },
419
- { type: algo-graph-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
480
+ { type: algo-graph-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: RUN_TRAVERSAL, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, layout: tree, root: "n0", nodes: @entity.nodes, edges: @entity.edges },
420
481
  { type: typography, variant: body, content: @entity.narration }
421
482
  ] }
422
483
  ]
@@ -503,6 +564,10 @@ orbital LearnTreeOrbital {
503
564
  @label "Title"
504
565
  @description "Title shown above the canvas."
505
566
  @tier "presentation"
567
+ summary : string = ""
568
+ @label "Concept"
569
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
570
+ @tier "presentation"
506
571
  width : number = 600
507
572
  @label "Width"
508
573
  @description "Canvas width in pixels."
@@ -511,9 +576,9 @@ orbital LearnTreeOrbital {
511
576
  @label "Height"
512
577
  @description "Canvas height in pixels."
513
578
  @tier "presentation"
514
- backgroundColor : string = "#f8fafc"
579
+ backgroundColor : string = ""
515
580
  @label "Background Color"
516
- @description "Canvas background color."
581
+ @description "Canvas ground. Empty uses the theme drawing ground."
517
582
  @tier "presentation"
518
583
  }
519
584
  }
@@ -26,6 +26,13 @@ orbital AlgoGraphCanvasOrbital {
26
26
  color : string
27
27
  }
28
28
  type AlgoGraphCanvasShapesItem = {
29
+ tone : "ink" | "label" | "guide" | "axis" | "grid" | "highlight" | "fill" | "series-1" | "series-2" | "series-3" | "series-4" | "series-5" | "series-6" | "series-7" | "series-8" | "primary" | "accent" | "success" | "warning" | "error" | "info" | "muted"
30
+ fillTone : "ink" | "label" | "guide" | "axis" | "grid" | "highlight" | "fill" | "series-1" | "series-2" | "series-3" | "series-4" | "series-5" | "series-6" | "series-7" | "series-8" | "primary" | "accent" | "success" | "warning" | "error" | "info" | "muted"
31
+ stroke : "thin" | "normal" | "bold"
32
+ textSize : "xs" | "sm" | "base" | "lg" | "xl" | "2xl"
33
+ font : "body" | "display" | "mono"
34
+ textCase : "theme" | "verbatim"
35
+ fillStyle : "solid" | "tint" | "outline" | "hatch"
29
36
  type : "line" | "arrow" | "circle" | "ellipse" | "rect" | "polygon" | "path" | "text" | "axis" | "grid" | "venn-region"!
30
37
  id : string
31
38
  x : number
@@ -94,6 +101,9 @@ orbital AlgoGraphCanvasOrbital {
94
101
  height: @config.height
95
102
  title: @config.title
96
103
  backgroundColor: @config.backgroundColor
104
+ series: @config.series
105
+ keyMap: @config.keyMap
106
+ keyUpMap: @config.keyUpMap
97
107
  nodes: @config.nodes
98
108
  edges: @config.edges
99
109
  layout: @config.layout
@@ -160,6 +170,18 @@ orbital AlgoGraphCanvasOrbital {
160
170
  @label "Background Color"
161
171
  @description "backgroundColor prop"
162
172
  @tier "presentation"
173
+ series : [string] = []
174
+ @label "Series"
175
+ @description "Overrides the theme's categorical palette (`series-1…N`) for this canvas — real-world conventions like CPK atom colors or resistor bands. Unset, the theme's `--color-series-*` apply."
176
+ @tier "presentation"
177
+ keyMap : Map string string = { item: "INIT" }
178
+ @label "Key Map"
179
+ @description "Maps a keydown `e.code` (optionally `Mod+`/`Shift+`/`Alt+` prefixed) to a SEMANTIC event emitted as `UI:{event}` — e.g. `{ Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }`; keystrokes inside inputs never route."
180
+ @tier "presentation"
181
+ keyUpMap : Map string string = { item: "INIT" }
182
+ @label "Key Up Map"
183
+ @description "Maps a keyup `e.code` to a semantic event emitted as `UI:{event}`."
184
+ @tier "presentation"
163
185
  nodes : [AlgoGraphCanvasNodesItem] = []
164
186
  @label "Nodes"
165
187
  @description "nodes prop"