@classytic/labs 0.1.0 → 0.2.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 (455) hide show
  1. package/dist/_virtual/_rolldown/runtime.mjs +18 -0
  2. package/dist/biology/enzyme-rate/preset.mjs +5 -5
  3. package/dist/biology/genetic-cross/core.d.mts +7 -7
  4. package/dist/biology/genetic-cross/core.mjs +4 -4
  5. package/dist/biology/genetic-cross/grid.mjs +132 -123
  6. package/dist/biology/genetic-cross/preset.mjs +4 -4
  7. package/dist/biology/genetic-cross/sex-linked.mjs +7 -7
  8. package/dist/biology/photosynthesis-factors/preset.mjs +8 -8
  9. package/dist/biology/respiration/preset.mjs +4 -4
  10. package/dist/biology/sequence/central-dogma.mjs +5 -5
  11. package/dist/biology/sequence/core.d.mts +6 -6
  12. package/dist/biology/sequence/core.mjs +1 -1
  13. package/dist/biology/sequence/preset.mjs +6 -6
  14. package/dist/blocks/authoring.mjs +5 -5
  15. package/dist/blocks/biology.d.mts +2 -2
  16. package/dist/blocks/biology.mjs +5 -5
  17. package/dist/blocks/chem.d.mts +2 -2
  18. package/dist/blocks/chem.mjs +13 -13
  19. package/dist/blocks/circuits.d.mts +293 -4
  20. package/dist/blocks/circuits.mjs +874 -9
  21. package/dist/blocks/discrete.d.mts +30 -2
  22. package/dist/blocks/discrete.mjs +74 -4
  23. package/dist/blocks/economics.mjs +1 -1
  24. package/dist/blocks/geography.mjs +1 -1
  25. package/dist/blocks/geometry.mjs +3 -3
  26. package/dist/blocks/ict.d.mts +90 -1
  27. package/dist/blocks/ict.mjs +217 -7
  28. package/dist/blocks/index.d.mts +3064 -657
  29. package/dist/blocks/index.mjs +9 -9
  30. package/dist/blocks/lab-block.mjs +5 -5
  31. package/dist/blocks/lab-config.d.mts +1 -1
  32. package/dist/blocks/lab-config.mjs +4 -4
  33. package/dist/blocks/lab-gallery.d.mts +1 -1
  34. package/dist/blocks/lab-gallery.mjs +2 -2
  35. package/dist/blocks/language.d.mts +6 -6
  36. package/dist/blocks/language.mjs +12 -12
  37. package/dist/blocks/lesson.mjs +4 -4
  38. package/dist/blocks/math.d.mts +577 -11
  39. package/dist/blocks/math.mjs +1648 -14
  40. package/dist/blocks/ml.d.mts +45 -1
  41. package/dist/blocks/ml.mjs +80 -6
  42. package/dist/blocks/physics.d.mts +9 -4
  43. package/dist/blocks/physics.mjs +75 -28
  44. package/dist/blocks/statistics.mjs +1 -1
  45. package/dist/build/CircuitEditor.d.mts +14 -0
  46. package/dist/build/CircuitEditor.mjs +787 -0
  47. package/dist/build/CircuitPlayer.d.mts +19 -0
  48. package/dist/build/CircuitPlayer.mjs +53 -0
  49. package/dist/build/CircuitScene.d.mts +61 -0
  50. package/dist/build/CircuitScene.mjs +383 -0
  51. package/dist/build/contract.d.mts +116 -0
  52. package/dist/build/editor-ops.d.mts +65 -0
  53. package/dist/build/editor-ops.mjs +306 -0
  54. package/dist/build/editor-ui.mjs +121 -0
  55. package/dist/build/flow.d.mts +14 -0
  56. package/dist/build/flow.mjs +119 -0
  57. package/dist/build/index.d.mts +10 -0
  58. package/dist/build/index.mjs +10 -0
  59. package/dist/build/parts/ammeter.d.mts +1 -0
  60. package/dist/build/parts/ammeter.mjs +32 -0
  61. package/dist/build/parts/bulb.d.mts +1 -0
  62. package/dist/build/parts/bulb.mjs +50 -0
  63. package/dist/build/parts/capacitor.d.mts +1 -0
  64. package/dist/build/parts/capacitor.mjs +26 -0
  65. package/dist/build/parts/cell.d.mts +1 -0
  66. package/dist/build/parts/cell.mjs +40 -0
  67. package/dist/build/parts/diode.d.mts +1 -0
  68. package/dist/build/parts/diode.mjs +33 -0
  69. package/dist/build/parts/index.d.mts +5 -0
  70. package/dist/build/parts/index.mjs +43 -0
  71. package/dist/build/parts/mosfet.d.mts +1 -0
  72. package/dist/build/parts/mosfet.mjs +95 -0
  73. package/dist/build/parts/node.d.mts +1 -0
  74. package/dist/build/parts/node.mjs +32 -0
  75. package/dist/build/parts/resistor.d.mts +1 -0
  76. package/dist/build/parts/resistor.mjs +49 -0
  77. package/dist/build/parts/shared.mjs +42 -0
  78. package/dist/build/parts/switch.d.mts +1 -0
  79. package/dist/build/parts/switch.mjs +34 -0
  80. package/dist/build/parts.d.mts +1 -0
  81. package/dist/build/registry.d.mts +9 -0
  82. package/dist/build/registry.mjs +15 -0
  83. package/dist/build/solve.d.mts +8 -0
  84. package/dist/build/solve.mjs +0 -0
  85. package/dist/catalog/registry.d.mts +2 -2
  86. package/dist/catalog/templates.d.mts +1 -1
  87. package/dist/catalog/templates.mjs +6 -6
  88. package/dist/chem/battery.mjs +1 -1
  89. package/dist/chem/bohr-atom.mjs +4 -4
  90. package/dist/chem/diffusion/preset.mjs +4 -4
  91. package/dist/chem/electrochem/preset.mjs +5 -5
  92. package/dist/chem/equilibrium/preset.d.mts +1 -1
  93. package/dist/chem/equilibrium/preset.mjs +6 -6
  94. package/dist/chem/gas-box/preset.mjs +6 -6
  95. package/dist/chem/kinetics/preset.mjs +6 -6
  96. package/dist/chem/periodic-trends/preset.mjs +7 -7
  97. package/dist/chem/reaction-lab.mjs +3 -3
  98. package/dist/chem/reaction-profile.mjs +4 -4
  99. package/dist/chem/solution/dilution.mjs +6 -6
  100. package/dist/chem/solution/field.d.mts +2 -2
  101. package/dist/chem/solution/field.mjs +2 -2
  102. package/dist/chem/solution/solution-box.mjs +7 -7
  103. package/dist/chem/stoichiometry/preset.mjs +5 -5
  104. package/dist/chem/titration/preset.mjs +5 -5
  105. package/dist/circuits/ac-dc/asset.mjs +1 -1
  106. package/dist/circuits/ac-dc/preset.mjs +2 -2
  107. package/dist/circuits/brownout/index.d.mts +1 -0
  108. package/dist/circuits/brownout/preset.d.mts +22 -0
  109. package/dist/circuits/brownout/preset.mjs +399 -0
  110. package/dist/circuits/capacitor-leak/preset.mjs +13 -8
  111. package/dist/circuits/circuit/asset.mjs +71 -14
  112. package/dist/circuits/circuit/preset.mjs +23 -5
  113. package/dist/circuits/circuit-builder.mjs +60 -132
  114. package/dist/circuits/circuit-lab.mjs +60 -40
  115. package/dist/circuits/cmos-gate/index.d.mts +1 -0
  116. package/dist/circuits/cmos-gate/preset.d.mts +75 -0
  117. package/dist/circuits/cmos-gate/preset.mjs +1491 -0
  118. package/dist/circuits/diode/index.d.mts +1 -0
  119. package/dist/circuits/diode/preset.d.mts +25 -0
  120. package/dist/circuits/diode/preset.mjs +266 -0
  121. package/dist/circuits/index.d.mts +7 -1
  122. package/dist/circuits/index.mjs +7 -1
  123. package/dist/circuits/rc-charging/index.d.mts +1 -0
  124. package/dist/circuits/rc-charging/preset.d.mts +27 -0
  125. package/dist/circuits/rc-charging/preset.mjs +329 -0
  126. package/dist/circuits/semiconductor/index.d.mts +1 -0
  127. package/dist/circuits/semiconductor/preset.d.mts +102 -0
  128. package/dist/circuits/semiconductor/preset.mjs +1855 -0
  129. package/dist/circuits/transistor/index.d.mts +1 -0
  130. package/dist/circuits/transistor/preset.d.mts +27 -0
  131. package/dist/circuits/transistor/preset.mjs +329 -0
  132. package/dist/commerce/accounting/core.d.mts +1 -1
  133. package/dist/commerce/accounting/equation-balance.mjs +5 -5
  134. package/dist/commerce/accounting/journal-poster.mjs +8 -8
  135. package/dist/commerce/accounting/statement-sorter.mjs +4 -4
  136. package/dist/commerce/economics/core.d.mts +2 -2
  137. package/dist/commerce/economics/core.mjs +1 -1
  138. package/dist/commerce/economics/demand-shift-vs-move.mjs +6 -6
  139. package/dist/commerce/economics/elasticity-revenue.mjs +6 -6
  140. package/dist/commerce/economics/market-equilibrium.mjs +5 -5
  141. package/dist/core/easing.d.mts +3 -3
  142. package/dist/core/easing.mjs +2 -2
  143. package/dist/core/numeric.d.mts +3 -3
  144. package/dist/core/numeric.mjs +2 -2
  145. package/dist/core/reactive.d.mts +1 -1
  146. package/dist/core/reactive.mjs +1 -1
  147. package/dist/core/rng.d.mts +2 -2
  148. package/dist/core/rng.mjs +1 -1
  149. package/dist/core/tex.mjs +2 -2
  150. package/dist/core/timeline.d.mts +1 -1
  151. package/dist/core/timeline.mjs +1 -1
  152. package/dist/core/util.mjs +11 -1
  153. package/dist/discrete/arrangements/preset.mjs +6 -14
  154. package/dist/discrete/bayes/preset.mjs +43 -35
  155. package/dist/discrete/binomial/preset.mjs +2 -2
  156. package/dist/discrete/combination-studio/figure.d.mts +6 -0
  157. package/dist/discrete/combination-studio/figure.mjs +159 -0
  158. package/dist/discrete/combination-studio/index.d.mts +2 -0
  159. package/dist/discrete/combination-studio/preset.d.mts +44 -0
  160. package/dist/discrete/combination-studio/preset.mjs +440 -0
  161. package/dist/discrete/core/combinatorics.d.mts +23 -3
  162. package/dist/discrete/core/combinatorics.mjs +38 -3
  163. package/dist/discrete/core/pigeonhole.d.mts +1 -1
  164. package/dist/discrete/core/pigeonhole.mjs +1 -1
  165. package/dist/discrete/core/probability.d.mts +3 -3
  166. package/dist/discrete/core/probability.mjs +3 -3
  167. package/dist/discrete/core/sets.d.mts +1 -1
  168. package/dist/discrete/counting-slots/preset.mjs +8 -8
  169. package/dist/discrete/counting-tree/preset.mjs +65 -8
  170. package/dist/discrete/expected-value/preset.mjs +13 -13
  171. package/dist/discrete/hypergeometric/preset.mjs +48 -15
  172. package/dist/discrete/index.d.mts +4 -2
  173. package/dist/discrete/index.mjs +4 -2
  174. package/dist/discrete/karnaugh/preset.mjs +26 -20
  175. package/dist/discrete/lln/preset.mjs +44 -3
  176. package/dist/discrete/logic-circuit/preset.mjs +2 -2
  177. package/dist/discrete/monte-carlo/preset.d.mts +1 -1
  178. package/dist/discrete/monte-carlo/preset.mjs +14 -14
  179. package/dist/discrete/monty-hall/preset.mjs +6 -6
  180. package/dist/discrete/outcome-builder/preset.mjs +3 -3
  181. package/dist/discrete/pascal/preset.mjs +37 -7
  182. package/dist/discrete/rules.d.mts +20 -0
  183. package/dist/discrete/rules.mjs +264 -0
  184. package/dist/discrete/sample-space/preset.mjs +4 -4
  185. package/dist/discrete/selection/preset.mjs +7 -13
  186. package/dist/discrete/truth-table/preset.mjs +32 -9
  187. package/dist/discrete/venn/preset.mjs +71 -48
  188. package/dist/geography/cycle-lab/preset.mjs +17 -10
  189. package/dist/geography/cycles.d.mts +3 -3
  190. package/dist/geography/cycles.mjs +3 -3
  191. package/dist/geometry/board/preset.d.mts +2 -0
  192. package/dist/geometry/board/preset.mjs +11 -15
  193. package/dist/geometry/builder.mjs +3 -3
  194. package/dist/ict/number-systems/base-odometer.mjs +4 -4
  195. package/dist/ict/number-systems/bit-grouper.d.mts +1 -1
  196. package/dist/ict/number-systems/bit-grouper.mjs +4 -4
  197. package/dist/ict/number-systems/place-value-dial.d.mts +1 -1
  198. package/dist/ict/number-systems/place-value-dial.mjs +3 -3
  199. package/dist/ict/number-systems/wheel.d.mts +3 -3
  200. package/dist/ict/number-systems/wheel.mjs +4 -4
  201. package/dist/kit/annotate.mjs +3 -3
  202. package/dist/kit/answer-check.d.mts +2 -2
  203. package/dist/kit/answer-check.mjs +34 -6
  204. package/dist/kit/ask.d.mts +19 -0
  205. package/dist/kit/ask.mjs +46 -0
  206. package/dist/kit/bar-strip.mjs +53 -0
  207. package/dist/kit/calc.d.mts +33 -0
  208. package/dist/kit/calc.mjs +27 -0
  209. package/dist/kit/carrier-engine.mjs +147 -0
  210. package/dist/kit/clue-scene.d.mts +86 -0
  211. package/dist/kit/clue-scene.mjs +424 -0
  212. package/dist/kit/cluster.d.mts +30 -0
  213. package/dist/kit/cluster.mjs +81 -0
  214. package/dist/kit/controls.mjs +11 -5
  215. package/dist/kit/coords.d.mts +88 -0
  216. package/dist/kit/coords.mjs +192 -0
  217. package/dist/kit/data-scene.d.mts +27 -0
  218. package/dist/kit/data-scene.mjs +199 -0
  219. package/dist/kit/diagram.mjs +111 -50
  220. package/dist/kit/electronics.mjs +552 -60
  221. package/dist/kit/expr-analysis.d.mts +18 -6
  222. package/dist/kit/expr-analysis.mjs +82 -28
  223. package/dist/kit/frame.d.mts +3 -3
  224. package/dist/kit/frame.mjs +25 -6
  225. package/dist/kit/gameshow.mjs +2 -2
  226. package/dist/kit/index.d.mts +14 -2
  227. package/dist/kit/index.mjs +13 -1
  228. package/dist/kit/logic-gates.mjs +246 -47
  229. package/dist/kit/palette.mjs +26 -0
  230. package/dist/kit/pedagogy.d.mts +4 -4
  231. package/dist/kit/pedagogy.mjs +21 -17
  232. package/dist/kit/play.d.mts +1 -1
  233. package/dist/kit/play.mjs +3 -3
  234. package/dist/kit/predict.d.mts +55 -0
  235. package/dist/kit/predict.mjs +146 -0
  236. package/dist/kit/receipt.d.mts +29 -0
  237. package/dist/kit/receipt.mjs +94 -0
  238. package/dist/kit/rich.mjs +2 -2
  239. package/dist/kit/rule.d.mts +59 -0
  240. package/dist/kit/rule.mjs +215 -0
  241. package/dist/kit/scale.mjs +1 -1
  242. package/dist/kit/scene-library.d.mts +22 -0
  243. package/dist/kit/scene-library.mjs +311 -0
  244. package/dist/kit/scene-studio.d.mts +14 -0
  245. package/dist/kit/scene-studio.mjs +338 -0
  246. package/dist/kit/scenes.d.mts +31 -0
  247. package/dist/kit/scenes.mjs +569 -0
  248. package/dist/kit/slot-fill.d.mts +61 -0
  249. package/dist/kit/slot-fill.mjs +207 -0
  250. package/dist/kit/solve.d.mts +21 -0
  251. package/dist/kit/solve.mjs +75 -0
  252. package/dist/kit/space.mjs +5 -5
  253. package/dist/kit/steps.mjs +2 -2
  254. package/dist/kit/vessel.d.mts +69 -0
  255. package/dist/kit/vessel.mjs +191 -0
  256. package/dist/language/agreement/preset.mjs +2 -2
  257. package/dist/language/article-lens/preset.d.mts +1 -1
  258. package/dist/language/article-lens/preset.mjs +8 -8
  259. package/dist/language/deck.d.mts +3 -3
  260. package/dist/language/deck.mjs +1 -1
  261. package/dist/language/preposition-scene/preset.d.mts +2 -2
  262. package/dist/language/preposition-scene/preset.mjs +5 -5
  263. package/dist/language/sentence-builder/preset.mjs +5 -5
  264. package/dist/language/transform/preset.d.mts +2 -2
  265. package/dist/language/transform/preset.mjs +5 -5
  266. package/dist/language/ui.d.mts +3 -3
  267. package/dist/language/ui.mjs +4 -4
  268. package/dist/language/word-match/preset.mjs +3 -3
  269. package/dist/logic/LogicBuildLab.d.mts +22 -0
  270. package/dist/logic/LogicBuildLab.mjs +170 -0
  271. package/dist/logic/LogicEditScene.d.mts +31 -0
  272. package/dist/logic/LogicEditScene.mjs +276 -0
  273. package/dist/logic/LogicEditor.d.mts +14 -0
  274. package/dist/logic/LogicEditor.mjs +413 -0
  275. package/dist/logic/LogicScene.d.mts +32 -0
  276. package/dist/logic/LogicScene.mjs +277 -0
  277. package/dist/logic/contract.d.mts +72 -0
  278. package/dist/logic/display.d.mts +24 -0
  279. package/dist/logic/display.mjs +127 -0
  280. package/dist/logic/edit-ops.d.mts +44 -0
  281. package/dist/logic/edit-ops.mjs +177 -0
  282. package/dist/logic/evaluate.d.mts +11 -0
  283. package/dist/logic/evaluate.mjs +93 -0
  284. package/dist/logic/gates.d.mts +10 -0
  285. package/dist/logic/gates.mjs +73 -0
  286. package/dist/logic/index.d.mts +14 -0
  287. package/dist/logic/index.mjs +13 -0
  288. package/dist/logic/lab.d.mts +32 -0
  289. package/dist/logic/lab.mjs +160 -0
  290. package/dist/logic/presets.d.mts +12 -0
  291. package/dist/logic/presets.mjs +241 -0
  292. package/dist/logic/registry.d.mts +9 -0
  293. package/dist/logic/registry.mjs +15 -0
  294. package/dist/math/area-model/preset.mjs +70 -44
  295. package/dist/math/balance-algebra/preset.mjs +14 -5
  296. package/dist/math/broken-tree/index.d.mts +1 -0
  297. package/dist/math/broken-tree/preset.d.mts +26 -0
  298. package/dist/math/broken-tree/preset.mjs +435 -0
  299. package/dist/math/circle/index.d.mts +1 -0
  300. package/dist/math/circle/preset.d.mts +44 -0
  301. package/dist/math/circle/preset.mjs +198 -0
  302. package/dist/math/complex/core.d.mts +93 -0
  303. package/dist/math/complex/core.mjs +239 -0
  304. package/dist/math/complex/index.d.mts +3 -0
  305. package/dist/math/complex/preset.d.mts +31 -0
  306. package/dist/math/complex/preset.mjs +377 -0
  307. package/dist/math/complex/rules.d.mts +6 -0
  308. package/dist/math/complex/rules.mjs +121 -0
  309. package/dist/math/conic/index.d.mts +1 -0
  310. package/dist/math/conic/preset.d.mts +30 -0
  311. package/dist/math/conic/preset.mjs +334 -0
  312. package/dist/math/derivation.mjs +5 -5
  313. package/dist/math/derivative-explorer/preset.mjs +5 -5
  314. package/dist/math/domain-range/index.d.mts +1 -0
  315. package/dist/math/domain-range/preset.d.mts +33 -0
  316. package/dist/math/domain-range/preset.mjs +313 -0
  317. package/dist/math/fraction-bar/index.d.mts +1 -0
  318. package/dist/math/fraction-bar/preset.d.mts +27 -0
  319. package/dist/math/fraction-bar/preset.mjs +197 -0
  320. package/dist/math/function-machine/preset.mjs +2 -2
  321. package/dist/math/gradient-descent.mjs +6 -6
  322. package/dist/math/grapher/preset.d.mts +1 -1
  323. package/dist/math/grapher/preset.mjs +4 -4
  324. package/dist/math/harmonic-form/preset.mjs +6 -6
  325. package/dist/math/index.d.mts +27 -2
  326. package/dist/math/index.mjs +27 -2
  327. package/dist/math/integral-explorer/preset.mjs +2 -2
  328. package/dist/math/interactive/preset.mjs +3 -3
  329. package/dist/math/limit-explorer/preset.mjs +4 -4
  330. package/dist/math/linear-model/index.d.mts +1 -0
  331. package/dist/math/linear-model/preset.d.mts +53 -0
  332. package/dist/math/linear-model/preset.mjs +164 -0
  333. package/dist/math/linear-system/preset.mjs +4 -4
  334. package/dist/math/mystery-bucket/preset.mjs +6 -6
  335. package/dist/math/number-line/preset.mjs +3 -2
  336. package/dist/math/parabola/preset.mjs +4 -3
  337. package/dist/math/pattern/preset.mjs +7 -13
  338. package/dist/math/percent-bar/index.d.mts +1 -0
  339. package/dist/math/percent-bar/preset.d.mts +37 -0
  340. package/dist/math/percent-bar/preset.mjs +275 -0
  341. package/dist/math/poly/core.d.mts +50 -0
  342. package/dist/math/poly/core.mjs +268 -0
  343. package/dist/math/poly/index.d.mts +3 -0
  344. package/dist/math/poly/preset.d.mts +23 -0
  345. package/dist/math/poly/preset.mjs +141 -0
  346. package/dist/math/poly/steps.d.mts +10 -0
  347. package/dist/math/poly/steps.mjs +99 -0
  348. package/dist/math/rate-machine/index.d.mts +1 -0
  349. package/dist/math/rate-machine/preset.d.mts +35 -0
  350. package/dist/math/rate-machine/preset.mjs +281 -0
  351. package/dist/math/ratio-share/index.d.mts +1 -0
  352. package/dist/math/ratio-share/preset.d.mts +24 -0
  353. package/dist/math/ratio-share/preset.mjs +204 -0
  354. package/dist/math/receipt/index.d.mts +1 -0
  355. package/dist/math/receipt/preset.d.mts +22 -0
  356. package/dist/math/receipt/preset.mjs +137 -0
  357. package/dist/math/sequence-predict/index.d.mts +1 -0
  358. package/dist/math/sequence-predict/preset.d.mts +34 -0
  359. package/dist/math/sequence-predict/preset.mjs +130 -0
  360. package/dist/math/straight-line/index.d.mts +1 -0
  361. package/dist/math/straight-line/preset.d.mts +37 -0
  362. package/dist/math/straight-line/preset.mjs +374 -0
  363. package/dist/math/system-solve/index.d.mts +1 -0
  364. package/dist/math/system-solve/preset.d.mts +21 -0
  365. package/dist/math/system-solve/preset.mjs +227 -0
  366. package/dist/math/transform/index.d.mts +1 -0
  367. package/dist/math/transform/preset.d.mts +41 -0
  368. package/dist/math/transform/preset.mjs +343 -0
  369. package/dist/math/triangle-trig/preset.mjs +12 -9
  370. package/dist/math/trig/core.d.mts +37 -0
  371. package/dist/math/trig/core.mjs +150 -0
  372. package/dist/math/trig/diagrams.d.mts +1 -0
  373. package/dist/math/trig/diagrams.mjs +321 -0
  374. package/dist/math/trig/index.d.mts +3 -0
  375. package/dist/math/trig/preset.d.mts +25 -0
  376. package/dist/math/trig/preset.mjs +260 -0
  377. package/dist/math/trig/rules.d.mts +6 -0
  378. package/dist/math/trig/rules.mjs +159 -0
  379. package/dist/math/trig-explorer.mjs +4 -4
  380. package/dist/ml/boundary/preset.mjs +8 -3
  381. package/dist/ml/classifier/preset.mjs +31 -6
  382. package/dist/ml/kmeans/preset.mjs +4 -4
  383. package/dist/ml/knn/preset.mjs +47 -12
  384. package/dist/ml/regression/preset.mjs +36 -7
  385. package/dist/physics/atwood/preset.mjs +4 -4
  386. package/dist/physics/bullet-walls/preset.d.mts +1 -1
  387. package/dist/physics/bullet-walls/preset.mjs +7 -7
  388. package/dist/physics/carnot/preset.mjs +6 -6
  389. package/dist/physics/circular-motion/preset.mjs +36 -5
  390. package/dist/physics/collision-track/preset.mjs +35 -5
  391. package/dist/physics/efficiency/preset.d.mts +2 -2
  392. package/dist/physics/efficiency/preset.mjs +5 -5
  393. package/dist/physics/electric-field/preset.mjs +32 -23
  394. package/dist/physics/electric-flux/index.d.mts +1 -0
  395. package/dist/physics/electric-flux/preset.d.mts +29 -0
  396. package/dist/physics/electric-flux/preset.mjs +264 -0
  397. package/dist/physics/energy-skate/preset.mjs +5 -5
  398. package/dist/physics/entropy/preset.mjs +7 -7
  399. package/dist/physics/expansion/preset.mjs +13 -13
  400. package/dist/physics/exponential/asset.mjs +1 -1
  401. package/dist/physics/exponential/preset.mjs +4 -4
  402. package/dist/physics/gas-process/preset.mjs +3 -3
  403. package/dist/physics/gauss-law/index.d.mts +1 -0
  404. package/dist/physics/gauss-law/preset.d.mts +20 -0
  405. package/dist/physics/gauss-law/preset.mjs +286 -0
  406. package/dist/physics/gravitation/preset.mjs +7 -7
  407. package/dist/physics/gravity-drop.mjs +4 -4
  408. package/dist/physics/heat-transfer/preset.d.mts +1 -1
  409. package/dist/physics/heat-transfer/preset.mjs +10 -10
  410. package/dist/physics/impulse/preset.mjs +5 -5
  411. package/dist/physics/index.d.mts +4 -1
  412. package/dist/physics/index.mjs +4 -1
  413. package/dist/physics/kepler/preset.mjs +4 -4
  414. package/dist/physics/lever/preset.mjs +3 -3
  415. package/dist/physics/lorentz/preset.mjs +11 -11
  416. package/dist/physics/magnetism/preset.mjs +3 -3
  417. package/dist/physics/optics/preset.mjs +2 -2
  418. package/dist/physics/orbit-lab.mjs +5 -5
  419. package/dist/physics/projectile-lab.mjs +4 -4
  420. package/dist/physics/rain-relative/preset.mjs +5 -5
  421. package/dist/physics/ramp-forces/preset.mjs +5 -5
  422. package/dist/physics/river-boat.mjs +6 -6
  423. package/dist/physics/shm/preset.mjs +9 -9
  424. package/dist/physics/stopping-distance/preset.mjs +3 -3
  425. package/dist/physics/temperature-scales/preset.mjs +105 -116
  426. package/dist/physics/terminal-velocity/preset.mjs +5 -5
  427. package/dist/physics/thermal/preset.mjs +5 -5
  428. package/dist/physics/vector-board/preset.mjs +7 -6
  429. package/dist/physics/vector-scene.mjs +2 -2
  430. package/dist/physics/vector-types/preset.mjs +37 -3
  431. package/dist/physics/water-density/preset.mjs +12 -12
  432. package/dist/physics/waves/doppler.mjs +2 -2
  433. package/dist/physics/waves/preset.mjs +4 -4
  434. package/dist/physics/waves/ripple.mjs +6 -6
  435. package/dist/physics/waves/string.mjs +5 -5
  436. package/dist/physics/work-energy/preset.mjs +4 -4
  437. package/dist/physics/work-potential/index.d.mts +1 -0
  438. package/dist/physics/work-potential/preset.d.mts +20 -0
  439. package/dist/physics/work-potential/preset.mjs +276 -0
  440. package/dist/schemas/index.d.mts +6 -6
  441. package/dist/schemas/index.mjs +3 -3
  442. package/dist/statistics/center-spread/preset.mjs +3 -3
  443. package/dist/statistics/core/descriptive.d.mts +2 -2
  444. package/dist/statistics/core/descriptive.mjs +2 -2
  445. package/dist/statistics/core/normal.d.mts +3 -3
  446. package/dist/statistics/core/normal.mjs +3 -3
  447. package/dist/statistics/core/sequences.d.mts +1 -1
  448. package/dist/statistics/galton/preset.mjs +2 -2
  449. package/dist/statistics/histogram/preset.mjs +6 -6
  450. package/dist/statistics/normal/preset.mjs +2 -2
  451. package/dist/statistics/sampling/preset.mjs +4 -4
  452. package/dist/statistics/sequence/preset.mjs +58 -15
  453. package/dist/statistics/z-table/preset.mjs +17 -4
  454. package/package.json +11 -3
  455. package/styles.css +4 -1
@@ -0,0 +1,207 @@
1
+ 'use client';
2
+
3
+ import { useCheckpoint } from "./pedagogy.mjs";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/kit/slot-fill.tsx
8
+ /**
9
+ * SlotFill, the "tap a tile into the blank" answer interaction, the lightweight,
10
+ * mobile-first commit Brilliant uses everywhere (fill Day 1 = ?, "translate by (▢,▢)",
11
+ * "a total of ▢ items"). It beats a typed box for short numeric/word answers: nothing
12
+ * to mistype, it reads as a game, and it works one-thumbed.
13
+ *
14
+ * HEADLESS by design: `useSlotFill` owns the tray + grading + checkpoint, and the
15
+ * presentational pieces (<Blank> for one blank, <SlotTray> for the tile pool) read from
16
+ * it. That's the reuse win, the SAME engine drives a tidy row (<SlotFill>) OR blanks
17
+ * embedded inline in a sentence/expression (a receipt total, a translation vector), so
18
+ * every fill-in lesson shares one tested core instead of re-rolling tile state.
19
+ */
20
+ const norm = (v) => String(v).trim();
21
+ /** The headless engine: tile pool + per-slot grading + the shared checkpoint report. */
22
+ function useSlotFill(slots, tiles, activity, onSolved) {
23
+ const [filled, setFilled] = useState({});
24
+ const [usedTiles, setUsedTiles] = useState(/* @__PURE__ */ new Set());
25
+ const [wrong, setWrong] = useState(null);
26
+ const activeId = slots.find((s) => filled[s.id] == null)?.id ?? null;
27
+ const solved = slots.length > 0 && slots.every((s) => filled[s.id] != null && norm(filled[s.id]) === norm(s.answer));
28
+ useCheckpoint({
29
+ solved,
30
+ activity
31
+ });
32
+ const fired = useRef(false);
33
+ useEffect(() => {
34
+ if (solved && !fired.current) {
35
+ fired.current = true;
36
+ onSolved?.();
37
+ }
38
+ }, [solved, onSolved]);
39
+ const place = (tileIdx) => {
40
+ if (!activeId || usedTiles.has(tileIdx)) return;
41
+ const slot = slots.find((s) => s.id === activeId);
42
+ if (norm(tiles[tileIdx]) === norm(slot.answer)) {
43
+ setFilled((f) => ({
44
+ ...f,
45
+ [activeId]: String(tiles[tileIdx])
46
+ }));
47
+ setUsedTiles((u) => new Set(u).add(tileIdx));
48
+ setWrong(null);
49
+ } else {
50
+ setWrong({
51
+ slot: activeId,
52
+ tile: tileIdx
53
+ });
54
+ setTimeout(() => setWrong(null), 450);
55
+ }
56
+ };
57
+ const reset = () => {
58
+ setFilled({});
59
+ setUsedTiles(/* @__PURE__ */ new Set());
60
+ setWrong(null);
61
+ fired.current = false;
62
+ };
63
+ return {
64
+ filled,
65
+ activeId,
66
+ solved,
67
+ wrong,
68
+ usedTiles,
69
+ slots,
70
+ tiles,
71
+ place,
72
+ reset
73
+ };
74
+ }
75
+ /** One inline blank, drops anywhere in a sentence/expression. Reads `fill` by slot id. */
76
+ function Blank({ fill, id, width = 46 }) {
77
+ const slot = fill.slots.find((s) => s.id === id);
78
+ const v = fill.filled[id];
79
+ const isActive = id === fill.activeId;
80
+ const isWrong = fill.wrong?.slot === id;
81
+ return /* @__PURE__ */ jsxs("span", {
82
+ style: {
83
+ display: "inline-grid",
84
+ justifyItems: "center",
85
+ gap: 3,
86
+ verticalAlign: "middle"
87
+ },
88
+ children: [/* @__PURE__ */ jsx("span", {
89
+ "aria-label": v ? `filled ${v}` : isActive ? "active blank" : "blank",
90
+ style: {
91
+ minWidth: width,
92
+ height: 42,
93
+ padding: "0 8px",
94
+ borderRadius: 9,
95
+ display: "grid",
96
+ placeItems: "center",
97
+ fontSize: 17,
98
+ fontWeight: 800,
99
+ fontVariantNumeric: "tabular-nums",
100
+ background: v ? "color-mix(in oklab, var(--stage-good) 18%, transparent)" : "transparent",
101
+ border: `2px ${v ? "solid" : "dashed"} ${v ? "var(--stage-good)" : isActive ? "var(--stage-accent)" : "color-mix(in oklab, var(--stage-fg) 30%, transparent)"}`,
102
+ color: v ? "var(--stage-good)" : "var(--stage-fg)",
103
+ animation: isWrong ? "slotfill-shake 0.4s" : void 0,
104
+ transition: "border-color 0.15s, background 0.15s"
105
+ },
106
+ children: v ?? (isActive ? "▾" : "")
107
+ }), slot?.label != null && /* @__PURE__ */ jsx("span", {
108
+ style: {
109
+ fontSize: 11,
110
+ color: "var(--stage-muted)",
111
+ fontWeight: 600
112
+ },
113
+ children: slot.label
114
+ })]
115
+ });
116
+ }
117
+ /** The tile tray, tap a tile and it drops into the active blank. */
118
+ function SlotTray({ fill }) {
119
+ return /* @__PURE__ */ jsxs("div", {
120
+ style: {
121
+ display: "flex",
122
+ gap: 10,
123
+ flexWrap: "wrap",
124
+ justifyContent: "center",
125
+ padding: "10px 14px",
126
+ borderRadius: 12,
127
+ background: "color-mix(in oklab, var(--stage-fg) 6%, transparent)"
128
+ },
129
+ children: [fill.tiles.map((t, i) => {
130
+ const used = fill.usedTiles.has(i);
131
+ const isWrong = fill.wrong?.tile === i;
132
+ return /* @__PURE__ */ jsx("button", {
133
+ type: "button",
134
+ disabled: used || fill.solved,
135
+ onClick: () => fill.place(i),
136
+ "aria-label": `tile ${t}`,
137
+ style: {
138
+ minWidth: 44,
139
+ height: 44,
140
+ padding: "0 10px",
141
+ borderRadius: 10,
142
+ fontSize: 17,
143
+ fontWeight: 800,
144
+ fontVariantNumeric: "tabular-nums",
145
+ cursor: used || fill.solved ? "default" : "pointer",
146
+ background: used ? "color-mix(in oklab, var(--stage-fg) 10%, transparent)" : "var(--stage-bg)",
147
+ color: used ? "transparent" : "var(--stage-fg)",
148
+ border: `2px solid ${isWrong ? "var(--stage-warn)" : used ? "transparent" : "color-mix(in oklab, var(--stage-fg) 26%, transparent)"}`,
149
+ animation: isWrong ? "slotfill-shake 0.4s" : void 0,
150
+ transition: "transform 0.1s"
151
+ },
152
+ children: used ? "" : t
153
+ }, i);
154
+ }), /* @__PURE__ */ jsx("style", { children: `@keyframes slotfill-shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-5px)}60%{transform:translateX(5px)}}` })]
155
+ });
156
+ }
157
+ /** Row layout: labelled blanks in a bordered strip + the tray. The common case. */
158
+ function SlotFill({ slots, tiles, activity, prompt, onSolved }) {
159
+ const fill = useSlotFill(slots, tiles, activity, onSolved);
160
+ return /* @__PURE__ */ jsxs("div", {
161
+ className: "not-prose",
162
+ style: {
163
+ display: "grid",
164
+ gap: 12,
165
+ justifyItems: "center",
166
+ marginTop: 8
167
+ },
168
+ children: [
169
+ prompt && /* @__PURE__ */ jsx("p", {
170
+ style: {
171
+ margin: 0,
172
+ fontWeight: 600,
173
+ textAlign: "center"
174
+ },
175
+ children: prompt
176
+ }),
177
+ /* @__PURE__ */ jsx("div", {
178
+ style: {
179
+ display: "flex",
180
+ gap: 12,
181
+ flexWrap: "wrap",
182
+ justifyContent: "center",
183
+ padding: "10px 14px",
184
+ borderRadius: 12,
185
+ border: "1px solid color-mix(in oklab, var(--stage-fg) 14%, transparent)"
186
+ },
187
+ children: slots.map((s) => /* @__PURE__ */ jsx(Blank, {
188
+ fill,
189
+ id: s.id
190
+ }, s.id))
191
+ }),
192
+ /* @__PURE__ */ jsx(SlotTray, { fill }),
193
+ fill.solved && /* @__PURE__ */ jsx("p", {
194
+ role: "status",
195
+ style: {
196
+ margin: 0,
197
+ color: "var(--stage-good)",
198
+ fontWeight: 700
199
+ },
200
+ children: "✓ Nice, that's the pattern."
201
+ })
202
+ ]
203
+ });
204
+ }
205
+
206
+ //#endregion
207
+ export { Blank, SlotFill, SlotTray, useSlotFill };
@@ -0,0 +1,21 @@
1
+ import { Complex } from "../math/complex/core.mjs";
2
+ import { fromAst } from "../math/poly/core.mjs";
3
+ import { Node } from "@classytic/stage";
4
+
5
+ //#region src/kit/solve.d.ts
6
+ /** Coefficients [a0, a1, …] of `node` as a polynomial in `x` (the canonical poly
7
+ * engine does the extraction). null if it isn't polynomial in x. */
8
+ declare const polyCoeffs: typeof fromAst;
9
+ interface PolySolution {
10
+ coeffs: number[];
11
+ degree: number;
12
+ roots: Complex[];
13
+ realRoots: number[];
14
+ discriminant?: number;
15
+ }
16
+ /** Solve `node` = 0 for x exactly (degree 1 or 2). null if not a usable polynomial. */
17
+ declare function solvePoly(node: Node, x?: string, params?: Record<string, number>): PolySolution | null;
18
+ /** "x = 2 or x = −2" (complex when needed), or "no real solutions" framing. */
19
+ declare function solutionTex(sol: PolySolution, x?: string): string;
20
+ //#endregion
21
+ export { PolySolution, polyCoeffs, solutionTex, solvePoly };
@@ -0,0 +1,75 @@
1
+ import { toStr } from "../math/complex/core.mjs";
2
+ import { fromAst, trim } from "../math/poly/core.mjs";
3
+
4
+ //#region src/kit/solve.ts
5
+ const cleanNum = (n) => {
6
+ const r = Math.round(n * 1e9) / 1e9;
7
+ return Object.is(r, -0) ? 0 : r;
8
+ };
9
+ /** Coefficients [a0, a1, …] of `node` as a polynomial in `x` (the canonical poly
10
+ * engine does the extraction). null if it isn't polynomial in x. */
11
+ const polyCoeffs = fromAst;
12
+ /** Solve `node` = 0 for x exactly (degree 1 or 2). null if not a usable polynomial. */
13
+ function solvePoly(node, x = "x", params = {}) {
14
+ const raw = polyCoeffs(node, x, params);
15
+ if (!raw) return null;
16
+ const coeffs = trim(raw);
17
+ const degree = coeffs.length - 1;
18
+ if (degree === 1) {
19
+ const a0 = coeffs[0], a1 = coeffs[1];
20
+ const r = cleanNum(-a0 / a1);
21
+ return {
22
+ coeffs,
23
+ degree,
24
+ roots: [{
25
+ re: r,
26
+ im: 0
27
+ }],
28
+ realRoots: [r]
29
+ };
30
+ }
31
+ if (degree === 2) {
32
+ const c0 = coeffs[0], c1 = coeffs[1], c2 = coeffs[2];
33
+ const disc = c1 * c1 - 4 * c2 * c0;
34
+ const twoA = 2 * c2;
35
+ if (disc >= 0) {
36
+ const s = Math.sqrt(disc);
37
+ const real = [cleanNum((-c1 - s) / twoA), cleanNum((-c1 + s) / twoA)].sort((u, v) => u - v);
38
+ return {
39
+ coeffs,
40
+ degree,
41
+ discriminant: disc,
42
+ roots: real.map((r) => ({
43
+ re: r,
44
+ im: 0
45
+ })),
46
+ realRoots: real
47
+ };
48
+ }
49
+ const s = Math.sqrt(-disc), re = cleanNum(-c1 / twoA), im = cleanNum(s / twoA);
50
+ return {
51
+ coeffs,
52
+ degree,
53
+ discriminant: disc,
54
+ roots: [{
55
+ re,
56
+ im: -im
57
+ }, {
58
+ re,
59
+ im
60
+ }],
61
+ realRoots: []
62
+ };
63
+ }
64
+ return null;
65
+ }
66
+ /** "x = 2 or x = −2" (complex when needed), or "no real solutions" framing. */
67
+ function solutionTex(sol, x = "x") {
68
+ if (sol.roots.length === 1) return `${x} = ${toStr(sol.roots[0])}`;
69
+ const [r1, r2] = sol.roots;
70
+ if (r1 && r2 && r1.re === r2.re && r1.im === r2.im) return `${x} = ${toStr(r1)} \\text{ (repeated)}`;
71
+ return `${x} = ${toStr(r1)} \\text{ or } ${x} = ${toStr(r2)}`;
72
+ }
73
+
74
+ //#endregion
75
+ export { polyCoeffs, solutionTex, solvePoly };
@@ -6,17 +6,17 @@ import { fmt, useCoords } from "@classytic/stage";
6
6
 
7
7
  //#region src/kit/space.tsx
8
8
  /**
9
- * Space glyph kit — Earth, Sun and a satellite, drawn in the stage's pixel frame
9
+ * Space glyph kit, Earth, Sun and a satellite, drawn in the stage's pixel frame
10
10
  * (project the world centre, then size everything off the px radius so the glyph
11
11
  * is crisp at any zoom). Kurzgesagt-style: radial body gradient + a rim shadow
12
12
  * for spherical depth + a top-left specular highlight, the technique borrowed from
13
13
  * my-video's EarthIcon. Gradient ids are made unique per instance so multiple
14
14
  * glyphs on one Stage don't collide.
15
15
  *
16
- * These are decorative (pointer-events:none) — drop a draggable MovableDot on top
16
+ * These are decorative (pointer-events:none), drop a draggable MovableDot on top
17
17
  * if the body needs to be grabbed.
18
18
  */
19
- /** Earth — blue oceans, a few green landmasses, ice caps, rim + shine + atmosphere. */
19
+ /** Earth, blue oceans, a few green landmasses, ice caps, rim + shine + atmosphere. */
20
20
  function EarthGlyph({ center, r, atmosphere = true }) {
21
21
  const c = useCoords();
22
22
  const [cx, cy] = c.toPx(center.x, center.y);
@@ -157,7 +157,7 @@ function EarthGlyph({ center, r, atmosphere = true }) {
157
157
  ]
158
158
  });
159
159
  }
160
- /** Sun — glowing core with a corona of rays. */
160
+ /** Sun, glowing core with a corona of rays. */
161
161
  function SunGlyph({ center, r }) {
162
162
  const c = useCoords();
163
163
  const [cx, cy] = c.toPx(center.x, center.y);
@@ -245,7 +245,7 @@ function SunGlyph({ center, r }) {
245
245
  ]
246
246
  });
247
247
  }
248
- /** Satellite — a body with two solar-panel wings and a dish. */
248
+ /** Satellite, a body with two solar-panel wings and a dish. */
249
249
  function SatelliteGlyph({ center, size, tilt = -.4 }) {
250
250
  const c = useCoords();
251
251
  const [cx, cy] = c.toPx(center.x, center.y);
@@ -5,10 +5,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
5
5
 
6
6
  //#region src/kit/steps.tsx
7
7
  /**
8
- * In-lab stepper — teach ONE idea at a time (Brilliant-style), instead of dumping
8
+ * In-lab stepper, teach ONE idea at a time (Brilliant-style), instead of dumping
9
9
  * a dense screen. A lab declares N steps and reveals layers/captions as the learner
10
10
  * hits Continue. Pure UI state; pair with `<StepNav>` (Back / dots / Continue).
11
- * (Distinct from stage's cross-block StepProgress — this is within a single lab.)
11
+ * (Distinct from stage's cross-block StepProgress, this is within a single lab.)
12
12
  */
13
13
  function useSteps(total) {
14
14
  const [step, setStep] = useState(0);
@@ -0,0 +1,69 @@
1
+ import { ReactNode } from "react";
2
+
3
+ //#region src/kit/vessel.d.ts
4
+ type GuessTone = 'idle' | 'ok' | 'no';
5
+ interface VesselGlyphProps {
6
+ /** top-left x,y and size of the vessel bounding box (px). */
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ /** TRUE liquid level, 0..1 of the inner height. */
12
+ fillFrac: number;
13
+ /** liquid colour token. */
14
+ color?: string;
15
+ /** discrete objects piled at the bottom (count). */
16
+ objects?: number;
17
+ /** object colour token. */
18
+ objectColor?: string;
19
+ /** optional learner-reading dashed line, 0..1 of the inner height. */
20
+ guessFrac?: number;
21
+ /** colour the reading line by correctness. */
22
+ guessTone?: GuessTone;
23
+ }
24
+ /** The pure <g> vessel, glass + liquid + objects + optional reading line. */
25
+ declare function VesselGlyph({
26
+ x,
27
+ y,
28
+ w,
29
+ h,
30
+ fillFrac,
31
+ color,
32
+ objects,
33
+ objectColor,
34
+ guessFrac,
35
+ guessTone
36
+ }: VesselGlyphProps): ReactNode;
37
+ interface VesselProps {
38
+ width?: number;
39
+ height?: number;
40
+ /** TRUE liquid level, 0..1. */
41
+ fillFrac: number;
42
+ guessFrac?: number;
43
+ guessTone?: GuessTone;
44
+ objects?: number;
45
+ liquidColor?: string;
46
+ objectColor?: string;
47
+ label?: string;
48
+ /** draw a 0..max scale up the right side; pass the axis max + unit for readouts. */
49
+ scaleMax?: number;
50
+ scaleStep?: number;
51
+ unit?: string;
52
+ }
53
+ /** Self-contained vessel (its own <svg>), drops in beside a graph as the concrete twin. */
54
+ declare function Vessel({
55
+ width,
56
+ height,
57
+ fillFrac,
58
+ guessFrac,
59
+ guessTone,
60
+ objects,
61
+ liquidColor,
62
+ objectColor,
63
+ label,
64
+ scaleMax,
65
+ scaleStep,
66
+ unit
67
+ }: VesselProps): ReactNode;
68
+ //#endregion
69
+ export { GuessTone, Vessel, VesselGlyph, VesselGlyphProps, VesselProps };
@@ -0,0 +1,191 @@
1
+ 'use client';
2
+
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+
5
+ //#region src/kit/vessel.tsx
6
+ const METAL = "var(--stage-metal, #8a8a8a)";
7
+ const GLASS = "color-mix(in oklab, var(--stage-fg) 26%, transparent)";
8
+ const FG = "var(--stage-fg, #222)";
9
+ const MUTED = "var(--stage-muted, #777)";
10
+ const TONE_COLOR = {
11
+ idle: "var(--stage-accent)",
12
+ ok: "var(--stage-good)",
13
+ no: "var(--stage-warn)"
14
+ };
15
+ /** The pure <g> vessel, glass + liquid + objects + optional reading line. */
16
+ function VesselGlyph({ x, y, w, h, fillFrac, color = "var(--stage-accent)", objects = 0, objectColor = "#e85aa6", guessFrac, guessTone = "idle" }) {
17
+ const lipH = 6;
18
+ const wall = 3;
19
+ const innerTop = y + lipH;
20
+ const innerBot = y + h - 3;
21
+ const innerH = innerBot - innerTop;
22
+ const lx = x + wall;
23
+ const rx = x + w - wall;
24
+ const innerW = rx - lx;
25
+ const liq = Math.max(0, Math.min(1, fillFrac));
26
+ const liquidTop = innerBot - liq * innerH;
27
+ const cubes = [];
28
+ if (objects > 0) {
29
+ const r = Math.max(5, Math.min(11, innerW / 5.5));
30
+ const perRow = Math.max(1, Math.floor((innerW - 4) / (r * 2)));
31
+ for (let i = 0; i < objects; i++) {
32
+ const row = Math.floor(i / perRow);
33
+ const col = i % perRow;
34
+ const rowW = Math.min(perRow, objects - row * perRow) * r * 2;
35
+ const cxp = (lx + rx) / 2 - rowW / 2 + r + col * r * 2 + row % 2 * (r * .5);
36
+ const cyp = innerBot - r - row * r * 1.7;
37
+ cubes.push(/* @__PURE__ */ jsxs("g", {
38
+ style: { animation: `vessel-drop 0.45s cubic-bezier(0.3,1.3,0.6,1) ${i * .08}s backwards` },
39
+ children: [/* @__PURE__ */ jsx("circle", {
40
+ cx: cxp,
41
+ cy: cyp,
42
+ r,
43
+ fill: objectColor,
44
+ opacity: .92
45
+ }), /* @__PURE__ */ jsx("circle", {
46
+ cx: cxp - r * .32,
47
+ cy: cyp - r * .32,
48
+ r: r * .3,
49
+ fill: "#fff",
50
+ opacity: .55
51
+ })]
52
+ }, `o${i}`));
53
+ }
54
+ }
55
+ const gColor = TONE_COLOR[guessTone];
56
+ const guessY = guessFrac != null ? innerBot - Math.max(0, Math.min(1, guessFrac)) * innerH : null;
57
+ return /* @__PURE__ */ jsxs("g", { children: [
58
+ /* @__PURE__ */ jsx("rect", {
59
+ x: lx,
60
+ y: liquidTop,
61
+ width: innerW,
62
+ height: Math.max(0, innerBot - liquidTop),
63
+ fill: color,
64
+ fillOpacity: .4,
65
+ style: { transition: "y 0.5s ease-out, height 0.5s ease-out" }
66
+ }),
67
+ liq > .02 && /* @__PURE__ */ jsx("ellipse", {
68
+ cx: (lx + rx) / 2,
69
+ cy: liquidTop,
70
+ rx: innerW / 2,
71
+ ry: 3.5,
72
+ fill: color,
73
+ fillOpacity: .7,
74
+ style: { transition: "cy 0.5s ease-out" }
75
+ }),
76
+ cubes,
77
+ /* @__PURE__ */ jsx("path", {
78
+ d: `M ${x} ${y} L ${lx} ${innerBot} L ${rx} ${innerBot} L ${x + w} ${y}`,
79
+ fill: "none",
80
+ stroke: GLASS,
81
+ strokeWidth: wall,
82
+ strokeLinejoin: "round",
83
+ strokeLinecap: "round"
84
+ }),
85
+ /* @__PURE__ */ jsx("line", {
86
+ x1: x - 3,
87
+ y1: y,
88
+ x2: x + w + 3,
89
+ y2: y,
90
+ stroke: METAL,
91
+ strokeWidth: 3,
92
+ strokeLinecap: "round"
93
+ }),
94
+ guessY != null && /* @__PURE__ */ jsxs("g", {
95
+ style: { transition: "transform 0.12s ease-out" },
96
+ children: [/* @__PURE__ */ jsx("line", {
97
+ x1: x - 6,
98
+ y1: guessY,
99
+ x2: x + w + 6,
100
+ y2: guessY,
101
+ stroke: gColor,
102
+ strokeWidth: 2.5,
103
+ strokeDasharray: "6 4"
104
+ }), /* @__PURE__ */ jsx("circle", {
105
+ cx: x + w + 6,
106
+ cy: guessY,
107
+ r: 3.5,
108
+ fill: gColor
109
+ })]
110
+ })
111
+ ] });
112
+ }
113
+ /** Self-contained vessel (its own <svg>), drops in beside a graph as the concrete twin. */
114
+ function Vessel({ width = 150, height = 300, fillFrac, guessFrac, guessTone = "idle", objects = 0, liquidColor = "var(--stage-accent)", objectColor = "#e85aa6", label, scaleMax, scaleStep, unit }) {
115
+ const padL = scaleMax != null ? 4 : 8;
116
+ const padR = scaleMax != null ? 34 : 8;
117
+ const padTop = 10;
118
+ const padBot = label ? 26 : 10;
119
+ const vx = padL;
120
+ const vw = width - padL - padR;
121
+ const vy = padTop;
122
+ const vh = height - padTop - padBot;
123
+ const innerTop = 16;
124
+ const innerBot = vy + vh - 3;
125
+ const innerH = innerBot - innerTop;
126
+ const ticks = [];
127
+ if (scaleMax != null) {
128
+ const step = scaleStep ?? scaleMax / 4;
129
+ for (let v = 0; v <= scaleMax + 1e-6; v += step) {
130
+ const ty = innerBot - v / scaleMax * innerH;
131
+ ticks.push(/* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
132
+ x1: vx + vw,
133
+ y1: ty,
134
+ x2: vx + vw + 5,
135
+ y2: ty,
136
+ stroke: MUTED,
137
+ strokeWidth: 1.5
138
+ }), /* @__PURE__ */ jsx("text", {
139
+ x: vx + vw + 8,
140
+ y: ty,
141
+ fontSize: 10,
142
+ fill: MUTED,
143
+ dominantBaseline: "middle",
144
+ children: v
145
+ })] }, `t${v}`));
146
+ }
147
+ }
148
+ return /* @__PURE__ */ jsxs("svg", {
149
+ width,
150
+ height,
151
+ viewBox: `0 0 ${width} ${height}`,
152
+ role: "img",
153
+ "aria-label": label ? `${label}: liquid at ${(fillFrac * 100).toFixed(0)}% of full` : "vessel",
154
+ children: [
155
+ ticks,
156
+ /* @__PURE__ */ jsx(VesselGlyph, {
157
+ x: vx,
158
+ y: vy,
159
+ w: vw,
160
+ h: vh,
161
+ fillFrac,
162
+ color: liquidColor,
163
+ objects,
164
+ objectColor,
165
+ guessFrac,
166
+ guessTone
167
+ }),
168
+ unit && scaleMax != null && /* @__PURE__ */ jsx("text", {
169
+ x: vx + vw + 8,
170
+ y: 12,
171
+ fontSize: 9,
172
+ fill: MUTED,
173
+ dominantBaseline: "hanging",
174
+ children: unit
175
+ }),
176
+ label && /* @__PURE__ */ jsx("text", {
177
+ x: width / 2,
178
+ y: height - 8,
179
+ fontSize: 12,
180
+ fontWeight: 700,
181
+ fill: FG,
182
+ textAnchor: "middle",
183
+ children: label
184
+ }),
185
+ /* @__PURE__ */ jsx("style", { children: `@keyframes vessel-drop{from{transform:translateY(-34px);opacity:0}to{transform:translateY(0);opacity:1}}` })
186
+ ]
187
+ });
188
+ }
189
+
190
+ //#endregion
191
+ export { Vessel, VesselGlyph };
@@ -8,7 +8,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/language/agreement/preset.tsx
10
10
  /**
11
- * AgreementLab — make the verb agree with its subject.
11
+ * AgreementLab, make the verb agree with its subject.
12
12
  *
13
13
  * Bangla drops the present copula ("He doctor") and has no 3rd-person -s
14
14
  * ("She go"), so subject–verb agreement is a core English error. The learner
@@ -90,7 +90,7 @@ function AgreementLab({ items, title = "Make them agree", prompt = "Pick the ver
90
90
  className: "lang-why",
91
91
  "data-state": correct ? "ok" : "no",
92
92
  "aria-live": "polite",
93
- children: correct ? item.note ?? `${item.subject} ${item.correct}${item.tail ? " " + item.tail : ""}.` : "Not quite — try another."
93
+ children: correct ? item.note ?? `${item.subject} ${item.correct}${item.tail ? " " + item.tail : ""}.` : "Not quite, try another."
94
94
  })
95
95
  ]
96
96
  });
@@ -1,7 +1,7 @@
1
1
  import { ReactNode } from "react";
2
2
 
3
3
  //#region src/language/article-lens/preset.d.ts
4
- type Article = 'a' | 'an' | 'the' | '—';
4
+ type Article = 'a' | 'an' | 'the' | ', ';
5
5
  interface ArticleItem {
6
6
  /** Text before the blank. */
7
7
  before: string;