@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
@@ -20,6 +20,8 @@ orbital GridSimOrbital {
20
20
  generation : number = 0
21
21
  aliveCount : number = 0
22
22
  running : boolean = false
23
+ speed : number = 1
24
+ elapsedMs : number = 0
23
25
  cellShapes : [GridCellShape]
24
26
  }
25
27
 
@@ -47,20 +49,36 @@ orbital GridSimOrbital {
47
49
  padding: md
48
50
  children: [
49
51
  { type: stack, direction: vertical, gap: md, children: [
50
- { type: typography, variant: h4, content: @entity.title },
51
- { type: typography, variant: body, content: @config.taskText },
52
- { type: stack, direction: horizontal, gap: sm, children: [
53
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
54
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
55
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
56
- { type: button, action: RESET, label: "Reset", variant: ghost }
52
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
53
+ { type: stack, direction: vertical, gap: xs, children: [
54
+ { type: typography, variant: h4, content: @entity.title },
55
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
56
+ ] },
57
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
57
58
  ] },
58
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
59
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
59
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
60
+ (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" }),
61
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
62
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
63
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
64
+ { 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") },
65
+ { 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") },
66
+ { 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") },
67
+ { 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") }
68
+ ] },
69
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
70
+ ] },
71
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
72
+ { type: typography, variant: body, content: @config.taskText }
60
73
  ] }
61
74
  ]
62
75
  })
63
76
 
77
+ MODE_SELECTED -> idle when (and (not (== @config.matchMode "")) (not (== ?mode @config.matchMode)))
78
+ (set @entity.active false)
79
+ (set @entity.running false)
80
+ (render-ui main null)
81
+
64
82
  MODE_SELECTED -> idle when (== ?mode @config.matchMode)
65
83
  (set @entity.active true)
66
84
  (set @entity.title @config.title)
@@ -81,16 +99,92 @@ orbital GridSimOrbital {
81
99
  padding: md
82
100
  children: [
83
101
  { type: stack, direction: vertical, gap: md, children: [
84
- { type: typography, variant: h4, content: @entity.title },
85
- { type: typography, variant: body, content: @config.taskText },
86
- { type: stack, direction: horizontal, gap: sm, children: [
87
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
88
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
89
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
90
- { type: button, action: RESET, label: "Reset", variant: ghost }
102
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
103
+ { type: stack, direction: vertical, gap: xs, children: [
104
+ { type: typography, variant: h4, content: @entity.title },
105
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
106
+ ] },
107
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
91
108
  ] },
92
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
93
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
109
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
110
+ (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" }),
111
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
112
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
113
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
114
+ { 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") },
115
+ { 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") },
116
+ { 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") },
117
+ { 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") }
118
+ ] },
119
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
120
+ ] },
121
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
122
+ { type: typography, variant: body, content: @config.taskText }
123
+ ] }
124
+ ]
125
+ })
126
+
127
+ TOGGLE_RUN -> idle when (== @entity.active true)
128
+ (set @entity.running (not @entity.running))
129
+ (render-ui main {
130
+ type: box
131
+ padding: md
132
+ children: [
133
+ { type: stack, direction: vertical, gap: md, children: [
134
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
135
+ { type: stack, direction: vertical, gap: xs, children: [
136
+ { type: typography, variant: h4, content: @entity.title },
137
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
138
+ ] },
139
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
140
+ ] },
141
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
142
+ (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" }),
143
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
144
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
145
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
146
+ { 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") },
147
+ { 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") },
148
+ { 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") },
149
+ { 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") }
150
+ ] },
151
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
152
+ ] },
153
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
154
+ { type: typography, variant: body, content: @config.taskText }
155
+ ] }
156
+ ]
157
+ })
158
+ (emit RUN_TOGGLED { running: @entity.running })
159
+
160
+ SPEED_SET -> idle when (== @entity.active true)
161
+ (set @entity.speed ?speed)
162
+ (render-ui main {
163
+ type: box
164
+ padding: md
165
+ children: [
166
+ { type: stack, direction: vertical, gap: md, children: [
167
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
168
+ { type: stack, direction: vertical, gap: xs, children: [
169
+ { type: typography, variant: h4, content: @entity.title },
170
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
171
+ ] },
172
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
173
+ ] },
174
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
175
+ (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" }),
176
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
177
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
178
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
179
+ { 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") },
180
+ { 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") },
181
+ { 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") },
182
+ { 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") }
183
+ ] },
184
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
185
+ ] },
186
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
187
+ { type: typography, variant: body, content: @config.taskText }
94
188
  ] }
95
189
  ]
96
190
  })
@@ -102,16 +196,27 @@ orbital GridSimOrbital {
102
196
  padding: md
103
197
  children: [
104
198
  { type: stack, direction: vertical, gap: md, children: [
105
- { type: typography, variant: h4, content: @entity.title },
106
- { type: typography, variant: body, content: @config.taskText },
107
- { type: stack, direction: horizontal, gap: sm, children: [
108
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
109
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
110
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
111
- { type: button, action: RESET, label: "Reset", variant: ghost }
199
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
200
+ { type: stack, direction: vertical, gap: xs, children: [
201
+ { type: typography, variant: h4, content: @entity.title },
202
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
203
+ ] },
204
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
112
205
  ] },
113
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
114
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
206
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
207
+ (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" }),
208
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
209
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
210
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
211
+ { 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") },
212
+ { 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") },
213
+ { 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") },
214
+ { 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") }
215
+ ] },
216
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
217
+ ] },
218
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
219
+ { type: typography, variant: body, content: @config.taskText }
115
220
  ] }
116
221
  ]
117
222
  })
@@ -124,16 +229,27 @@ orbital GridSimOrbital {
124
229
  padding: md
125
230
  children: [
126
231
  { type: stack, direction: vertical, gap: md, children: [
127
- { type: typography, variant: h4, content: @entity.title },
128
- { type: typography, variant: body, content: @config.taskText },
129
- { type: stack, direction: horizontal, gap: sm, children: [
130
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
131
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
132
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
133
- { type: button, action: RESET, label: "Reset", variant: ghost }
232
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
233
+ { type: stack, direction: vertical, gap: xs, children: [
234
+ { type: typography, variant: h4, content: @entity.title },
235
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
236
+ ] },
237
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
238
+ ] },
239
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
240
+ (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" }),
241
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
242
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
243
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
244
+ { 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") },
245
+ { 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") },
246
+ { 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") },
247
+ { 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") }
248
+ ] },
249
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
134
250
  ] },
135
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
136
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
251
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
252
+ { type: typography, variant: body, content: @config.taskText }
137
253
  ] }
138
254
  ]
139
255
  })
@@ -178,16 +294,27 @@ orbital GridSimOrbital {
178
294
  padding: md
179
295
  children: [
180
296
  { type: stack, direction: vertical, gap: md, children: [
181
- { type: typography, variant: h4, content: @entity.title },
182
- { type: typography, variant: body, content: @config.taskText },
183
- { type: stack, direction: horizontal, gap: sm, children: [
184
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
185
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
186
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
187
- { type: button, action: RESET, label: "Reset", variant: ghost }
297
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
298
+ { type: stack, direction: vertical, gap: xs, children: [
299
+ { type: typography, variant: h4, content: @entity.title },
300
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
301
+ ] },
302
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
188
303
  ] },
189
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
190
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
304
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
305
+ (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" }),
306
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
307
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
308
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
309
+ { 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") },
310
+ { 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") },
311
+ { 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") },
312
+ { 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") }
313
+ ] },
314
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
315
+ ] },
316
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
317
+ { type: typography, variant: body, content: @config.taskText }
191
318
  ] }
192
319
  ]
193
320
  })
@@ -209,16 +336,27 @@ orbital GridSimOrbital {
209
336
  padding: md
210
337
  children: [
211
338
  { type: stack, direction: vertical, gap: md, children: [
212
- { type: typography, variant: h4, content: @entity.title },
213
- { type: typography, variant: body, content: @config.taskText },
214
- { type: stack, direction: horizontal, gap: sm, children: [
215
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
216
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
217
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
218
- { type: button, action: RESET, label: "Reset", variant: ghost }
339
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
340
+ { type: stack, direction: vertical, gap: xs, children: [
341
+ { type: typography, variant: h4, content: @entity.title },
342
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
343
+ ] },
344
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
345
+ ] },
346
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
347
+ (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" }),
348
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
349
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
350
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
351
+ { 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") },
352
+ { 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") },
353
+ { 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") },
354
+ { 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") }
355
+ ] },
356
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
219
357
  ] },
220
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
221
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
358
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
359
+ { type: typography, variant: body, content: @config.taskText }
222
360
  ] }
223
361
  ]
224
362
  })
@@ -242,16 +380,27 @@ orbital GridSimOrbital {
242
380
  padding: md
243
381
  children: [
244
382
  { type: stack, direction: vertical, gap: md, children: [
245
- { type: typography, variant: h4, content: @entity.title },
246
- { type: typography, variant: body, content: @config.taskText },
247
- { type: stack, direction: horizontal, gap: sm, children: [
248
- { type: button, action: RUN, label: "Run", variant: primary, disabled: (if @entity.running true false) },
249
- { type: button, action: PAUSE, label: "Pause", variant: secondary, disabled: (if @entity.running false true) },
250
- { type: button, action: STEP, label: @config.stepLabel, variant: secondary, disabled: (if @entity.running true false) },
251
- { type: button, action: RESET, label: "Reset", variant: ghost }
383
+ { type: stack, direction: horizontal, justify: between, align: start, gap: md, children: [
384
+ { type: stack, direction: vertical, gap: xs, children: [
385
+ { type: typography, variant: h4, content: @entity.title },
386
+ (if (== @config.summary "") null { type: typography, variant: body2, color: muted, content: @config.summary })
387
+ ] },
388
+ { type: badge, label: (if @entity.running "Running" "Paused"), variant: (if @entity.running "primary" "neutral") }
252
389
  ] },
253
- { type: learning-canvas, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
254
- { type: typography, variant: body, content: (str/concat "Generation " (str/concat "" @entity.generation) " · active cells: " (str/concat "" @entity.aliveCount)) }
390
+ { type: stack, direction: horizontal, align: center, gap: sm, wrap: true, children: [
391
+ (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" }),
392
+ { type: button, action: STEP, label: @config.stepLabel, icon: "step-forward", variant: secondary, disabled: @entity.running },
393
+ { type: button, action: RESET, label: "Reset", icon: "rotate-ccw", variant: ghost },
394
+ { type: stack, direction: horizontal, align: center, gap: xs, role: "group", aria-label: "Speed", children: [
395
+ { 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") },
396
+ { 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") },
397
+ { 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") },
398
+ { 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") }
399
+ ] },
400
+ { type: typography, variant: caption, color: muted, content: (str/concat "Generation " @entity.generation " · " @entity.aliveCount " active") }
401
+ ] },
402
+ { type: learning-canvas, keyMap: { Space: TOGGLE_RUN, ArrowRight: STEP, KeyR: RESET }, width: @config.width, height: @config.height, backgroundColor: @config.backgroundColor, shapes: @entity.cellShapes, interactive: true, animate: false, onShapeClick: "TAP_CELL" },
403
+ { type: typography, variant: body, content: @config.taskText }
255
404
  ] }
256
405
  ]
257
406
  })
@@ -259,41 +408,46 @@ orbital GridSimOrbital {
259
408
  }
260
409
 
261
410
  ticks {
262
- gridTick every 300ms
411
+ gridTick every 75ms
263
412
  when (and (== @entity.active true) (== @entity.running true))
264
- (set @entity.cells (array/map (array/range 0 (* @config.rows @config.cols)) (fn (i)
413
+ (set @entity.elapsedMs (+ @entity.elapsedMs 75))
414
+ (when (>= (* @entity.elapsedMs @entity.speed) 300)
415
+ (do
416
+ (set @entity.elapsedMs 0)
417
+ (set @entity.cells (array/map (array/range 0 (* @config.rows @config.cols)) (fn (i)
418
+ (let (
419
+ (r (math/floor (/ @i @config.cols)))
420
+ (c (math/mod @i @config.cols))
421
+ (v (array/nth @entity.cells @i))
422
+ (up (if (> @r 0) (array/nth @entity.cells (- @i @config.cols)) 0))
423
+ (down (if (< @r (- @config.rows 1)) (array/nth @entity.cells (+ @i @config.cols)) 0))
424
+ (left (if (> @c 0) (array/nth @entity.cells (- @i 1)) 0))
425
+ (right (if (< @c (- @config.cols 1)) (array/nth @entity.cells (+ @i 1)) 0))
426
+ (ul (if (and (> @r 0) (> @c 0)) (array/nth @entity.cells (- (- @i @config.cols) 1)) 0))
427
+ (ur (if (and (> @r 0) (< @c (- @config.cols 1))) (array/nth @entity.cells (+ (- @i @config.cols) 1)) 0))
428
+ (dl (if (and (< @r (- @config.rows 1)) (> @c 0)) (array/nth @entity.cells (- (+ @i @config.cols) 1)) 0))
429
+ (dr (if (and (< @r (- @config.rows 1)) (< @c (- @config.cols 1))) (array/nth @entity.cells (+ @i @config.cols 1)) 0))
430
+ (n4 (+ @up @down @left @right))
431
+ (n8 (+ @n4 @ul @ur @dl @dr))
432
+ )
433
+ (if (== @config.rule "life")
434
+ (if (if (> @v 0.5)
435
+ (if (== (math/round @n8 0) 2) true (== (math/round @n8 0) 3))
436
+ (== (math/round @n8 0) 3)) 1 0)
437
+ (if (== @config.rule "spread")
438
+ (if (if (> @v 0.5) true (> @n4 0.5)) 1 0)
439
+ (math/max 0 (* @config.decay (+ @v (* @config.diffuseRate (- (/ @n4 4) @v)))))))))))
440
+ (set @entity.generation (+ @entity.generation 1))
441
+ (set @entity.aliveCount (array/len (array/filter @entity.cells (fn v (> @v 0.05)))))
442
+ (set @entity.cellShapes (array/map (array/range 0 (* @config.rows @config.cols)) (fn (i)
265
443
  (let (
266
444
  (r (math/floor (/ @i @config.cols)))
267
445
  (c (math/mod @i @config.cols))
268
446
  (v (array/nth @entity.cells @i))
269
- (up (if (> @r 0) (array/nth @entity.cells (- @i @config.cols)) 0))
270
- (down (if (< @r (- @config.rows 1)) (array/nth @entity.cells (+ @i @config.cols)) 0))
271
- (left (if (> @c 0) (array/nth @entity.cells (- @i 1)) 0))
272
- (right (if (< @c (- @config.cols 1)) (array/nth @entity.cells (+ @i 1)) 0))
273
- (ul (if (and (> @r 0) (> @c 0)) (array/nth @entity.cells (- (- @i @config.cols) 1)) 0))
274
- (ur (if (and (> @r 0) (< @c (- @config.cols 1))) (array/nth @entity.cells (+ (- @i @config.cols) 1)) 0))
275
- (dl (if (and (< @r (- @config.rows 1)) (> @c 0)) (array/nth @entity.cells (- (+ @i @config.cols) 1)) 0))
276
- (dr (if (and (< @r (- @config.rows 1)) (< @c (- @config.cols 1))) (array/nth @entity.cells (+ @i @config.cols 1)) 0))
277
- (n4 (+ @up @down @left @right))
278
- (n8 (+ @n4 @ul @ur @dl @dr))
447
+ (cellColor (if (> @v 0.66) @config.onColor (if (> @v 0.33) @config.midColor (if (> @v 0.05) @config.lowColor @config.offColor))))
279
448
  )
280
- (if (== @config.rule "life")
281
- (if (if (> @v 0.5)
282
- (if (== (math/round @n8 0) 2) true (== (math/round @n8 0) 3))
283
- (== (math/round @n8 0) 3)) 1 0)
284
- (if (== @config.rule "spread")
285
- (if (if (> @v 0.5) true (> @n4 0.5)) 1 0)
286
- (math/max 0 (* @config.decay (+ @v (* @config.diffuseRate (- (/ @n4 4) @v)))))))))))
287
- (set @entity.generation (+ @entity.generation 1))
288
- (set @entity.aliveCount (array/len (array/filter @entity.cells (fn v (> @v 0.05)))))
289
- (set @entity.cellShapes (array/map (array/range 0 (* @config.rows @config.cols)) (fn (i)
290
- (let (
291
- (r (math/floor (/ @i @config.cols)))
292
- (c (math/mod @i @config.cols))
293
- (v (array/nth @entity.cells @i))
294
- (cellColor (if (> @v 0.66) @config.onColor (if (> @v 0.33) @config.midColor (if (> @v 0.05) @config.lowColor @config.offColor))))
295
- )
296
- { type: rect, id: (str/concat "cell-" @i), x: (+ 10 (* @c @config.cellSize)), y: (+ 10 (* @r @config.cellSize)), width: (- @config.cellSize 2), height: (- @config.cellSize 2), color: @cellColor, fill: @cellColor }))))
449
+ { type: rect, id: (str/concat "cell-" @i), x: (+ 10 (* @c @config.cellSize)), y: (+ 10 (* @r @config.cellSize)), width: (- @config.cellSize 2), height: (- @config.cellSize 2), color: @cellColor, fill: @cellColor }))))
450
+ ))
297
451
  }
298
452
 
299
453
  listens {
@@ -302,6 +456,15 @@ orbital GridSimOrbital {
302
456
  }
303
457
  @description "Lab router broadcast naming the active mode; this instrument renders when the mode equals matchMode, otherwise renders null."
304
458
  @tier "domain"
459
+ TOGGLE_RUN {
460
+ }
461
+ @description "Learner pressed the Play/Pause toggle — starts or freezes the evolution."
462
+ @tier "domain"
463
+ SPEED_SET {
464
+ speed : number
465
+ }
466
+ @description "The learner picked a playback speed; generations advance that many times faster."
467
+ @tier "presentation"
305
468
  RUN {
306
469
  }
307
470
  @description "Learner started the evolution; the grid advances one generation per tick until Pause."
@@ -358,7 +521,11 @@ orbital GridSimOrbital {
358
521
  @tier "presentation"
359
522
  title : string = "Grid World"
360
523
  @label "Title"
361
- @description "Title shown above the grid."
524
+ @description "The instrument's name, the header's one hero."
525
+ @tier "presentation"
526
+ summary : string = ""
527
+ @label "Concept"
528
+ @description "One line under the title saying what the learner is looking at (keep it under ~65 characters). Empty shows no line."
362
529
  @tier "presentation"
363
530
  taskText : string = "Tap cells to seed the world, then Run — or Step one generation at a time to see the rule at work."
364
531
  @label "Task Text"
@@ -390,20 +557,21 @@ orbital GridSimOrbital {
390
557
  cellSize : number = 26
391
558
  @label "Cell Size"
392
559
  @tier "presentation"
393
- onColor : string = "#1d4ed8"
560
+ onColor : string = "series-2"
394
561
  @label "On Color"
395
562
  @tier "presentation"
396
- midColor : string = "#60a5fa"
563
+ midColor : string = "series-3"
397
564
  @label "Mid Color"
398
565
  @tier "presentation"
399
- lowColor : string = "#bfdbfe"
566
+ lowColor : string = "muted"
400
567
  @label "Low Color"
401
568
  @tier "presentation"
402
- offColor : string = "#e2e8f0"
569
+ offColor : string = "fill"
403
570
  @label "Off Color"
404
571
  @tier "presentation"
405
- backgroundColor : string = "#f8fafc"
572
+ backgroundColor : string = ""
406
573
  @label "Background Color"
574
+ @description "Canvas ground override; empty uses the theme's drawing ground."
407
575
  @tier "presentation"
408
576
  stepLabel : string = "Step"
409
577
  @label "Step Label"