@classytic/labs 0.1.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 (547) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/dist/biology/enzyme-rate/index.d.mts +1 -0
  4. package/dist/biology/enzyme-rate/preset.d.mts +25 -0
  5. package/dist/biology/enzyme-rate/preset.mjs +256 -0
  6. package/dist/biology/genetic-cross/core.d.mts +64 -0
  7. package/dist/biology/genetic-cross/core.mjs +147 -0
  8. package/dist/biology/genetic-cross/grid.mjs +239 -0
  9. package/dist/biology/genetic-cross/index.d.mts +3 -0
  10. package/dist/biology/genetic-cross/preset.d.mts +26 -0
  11. package/dist/biology/genetic-cross/preset.mjs +184 -0
  12. package/dist/biology/genetic-cross/sex-linked.d.mts +27 -0
  13. package/dist/biology/genetic-cross/sex-linked.mjs +226 -0
  14. package/dist/biology/index.d.mts +11 -0
  15. package/dist/biology/index.mjs +12 -0
  16. package/dist/biology/photosynthesis-factors/index.d.mts +1 -0
  17. package/dist/biology/photosynthesis-factors/preset.d.mts +25 -0
  18. package/dist/biology/photosynthesis-factors/preset.mjs +270 -0
  19. package/dist/biology/punnett-cross/index.d.mts +1 -0
  20. package/dist/biology/punnett-cross/preset.d.mts +27 -0
  21. package/dist/biology/punnett-cross/preset.mjs +21 -0
  22. package/dist/biology/respiration/index.d.mts +1 -0
  23. package/dist/biology/respiration/preset.d.mts +17 -0
  24. package/dist/biology/respiration/preset.mjs +179 -0
  25. package/dist/biology/sequence/central-dogma.d.mts +17 -0
  26. package/dist/biology/sequence/central-dogma.mjs +339 -0
  27. package/dist/biology/sequence/core.d.mts +38 -0
  28. package/dist/biology/sequence/core.mjs +178 -0
  29. package/dist/biology/sequence/index.d.mts +3 -0
  30. package/dist/biology/sequence/preset.d.mts +20 -0
  31. package/dist/biology/sequence/preset.mjs +235 -0
  32. package/dist/blocks/accounting.d.mts +142 -0
  33. package/dist/blocks/accounting.mjs +446 -0
  34. package/dist/blocks/authoring.d.mts +12 -0
  35. package/dist/blocks/authoring.mjs +705 -0
  36. package/dist/blocks/biology.d.mts +197 -0
  37. package/dist/blocks/biology.mjs +513 -0
  38. package/dist/blocks/chem.d.mts +128 -0
  39. package/dist/blocks/chem.mjs +486 -0
  40. package/dist/blocks/circuits.d.mts +86 -0
  41. package/dist/blocks/circuits.mjs +348 -0
  42. package/dist/blocks/discrete.d.mts +62 -0
  43. package/dist/blocks/discrete.mjs +314 -0
  44. package/dist/blocks/economics.d.mts +56 -0
  45. package/dist/blocks/economics.mjs +203 -0
  46. package/dist/blocks/geography.d.mts +58 -0
  47. package/dist/blocks/geography.mjs +115 -0
  48. package/dist/blocks/geometry.d.mts +28 -0
  49. package/dist/blocks/geometry.mjs +160 -0
  50. package/dist/blocks/ict.d.mts +65 -0
  51. package/dist/blocks/ict.mjs +197 -0
  52. package/dist/blocks/index.d.mts +4555 -0
  53. package/dist/blocks/index.mjs +120 -0
  54. package/dist/blocks/lab-block.d.mts +16 -0
  55. package/dist/blocks/lab-block.mjs +74 -0
  56. package/dist/blocks/lab-config.d.mts +23 -0
  57. package/dist/blocks/lab-config.mjs +200 -0
  58. package/dist/blocks/lab-gallery.d.mts +30 -0
  59. package/dist/blocks/lab-gallery.mjs +208 -0
  60. package/dist/blocks/language.d.mts +349 -0
  61. package/dist/blocks/language.mjs +750 -0
  62. package/dist/blocks/lesson.d.mts +43 -0
  63. package/dist/blocks/lesson.mjs +140 -0
  64. package/dist/blocks/math.d.mts +411 -0
  65. package/dist/blocks/math.mjs +1536 -0
  66. package/dist/blocks/ml.d.mts +84 -0
  67. package/dist/blocks/ml.mjs +146 -0
  68. package/dist/blocks/physics.d.mts +259 -0
  69. package/dist/blocks/physics.mjs +868 -0
  70. package/dist/blocks/statistics.d.mts +29 -0
  71. package/dist/blocks/statistics.mjs +123 -0
  72. package/dist/catalog/index.d.mts +3 -0
  73. package/dist/catalog/index.mjs +8 -0
  74. package/dist/catalog/registry.d.mts +46 -0
  75. package/dist/catalog/registry.mjs +56 -0
  76. package/dist/catalog/templates.d.mts +11 -0
  77. package/dist/catalog/templates.mjs +141 -0
  78. package/dist/chem/battery.d.mts +16 -0
  79. package/dist/chem/battery.mjs +311 -0
  80. package/dist/chem/bohr-atom.d.mts +16 -0
  81. package/dist/chem/bohr-atom.mjs +212 -0
  82. package/dist/chem/diffusion/asset.mjs +76 -0
  83. package/dist/chem/diffusion/index.d.mts +1 -0
  84. package/dist/chem/diffusion/preset.d.mts +15 -0
  85. package/dist/chem/diffusion/preset.mjs +188 -0
  86. package/dist/chem/electrochem/index.d.mts +1 -0
  87. package/dist/chem/electrochem/preset.d.mts +23 -0
  88. package/dist/chem/electrochem/preset.mjs +402 -0
  89. package/dist/chem/equilibrium/index.d.mts +1 -0
  90. package/dist/chem/equilibrium/preset.d.mts +36 -0
  91. package/dist/chem/equilibrium/preset.mjs +416 -0
  92. package/dist/chem/gas-box/index.d.mts +1 -0
  93. package/dist/chem/gas-box/preset.d.mts +27 -0
  94. package/dist/chem/gas-box/preset.mjs +395 -0
  95. package/dist/chem/index.d.mts +16 -0
  96. package/dist/chem/index.mjs +17 -0
  97. package/dist/chem/kinetics/index.d.mts +1 -0
  98. package/dist/chem/kinetics/preset.d.mts +31 -0
  99. package/dist/chem/kinetics/preset.mjs +478 -0
  100. package/dist/chem/periodic-trends/index.d.mts +1 -0
  101. package/dist/chem/periodic-trends/preset.d.mts +21 -0
  102. package/dist/chem/periodic-trends/preset.mjs +831 -0
  103. package/dist/chem/reaction-lab.d.mts +18 -0
  104. package/dist/chem/reaction-lab.mjs +165 -0
  105. package/dist/chem/reaction-profile.d.mts +21 -0
  106. package/dist/chem/reaction-profile.mjs +287 -0
  107. package/dist/chem/solution/dilution.d.mts +27 -0
  108. package/dist/chem/solution/dilution.mjs +199 -0
  109. package/dist/chem/solution/field.d.mts +30 -0
  110. package/dist/chem/solution/field.mjs +145 -0
  111. package/dist/chem/solution/index.d.mts +3 -0
  112. package/dist/chem/solution/solution-box.d.mts +28 -0
  113. package/dist/chem/solution/solution-box.mjs +140 -0
  114. package/dist/chem/stoichiometry/index.d.mts +1 -0
  115. package/dist/chem/stoichiometry/preset.d.mts +29 -0
  116. package/dist/chem/stoichiometry/preset.mjs +392 -0
  117. package/dist/chem/titration/index.d.mts +1 -0
  118. package/dist/chem/titration/preset.d.mts +29 -0
  119. package/dist/chem/titration/preset.mjs +436 -0
  120. package/dist/circuits/ac-dc/asset.mjs +295 -0
  121. package/dist/circuits/ac-dc/index.d.mts +1 -0
  122. package/dist/circuits/ac-dc/preset.d.mts +16 -0
  123. package/dist/circuits/ac-dc/preset.mjs +134 -0
  124. package/dist/circuits/capacitor-leak/index.d.mts +1 -0
  125. package/dist/circuits/capacitor-leak/preset.d.mts +32 -0
  126. package/dist/circuits/capacitor-leak/preset.mjs +271 -0
  127. package/dist/circuits/circuit/asset.d.mts +6 -0
  128. package/dist/circuits/circuit/asset.mjs +367 -0
  129. package/dist/circuits/circuit/index.d.mts +2 -0
  130. package/dist/circuits/circuit/preset.d.mts +46 -0
  131. package/dist/circuits/circuit/preset.mjs +235 -0
  132. package/dist/circuits/circuit-builder.d.mts +30 -0
  133. package/dist/circuits/circuit-builder.mjs +412 -0
  134. package/dist/circuits/circuit-lab.d.mts +20 -0
  135. package/dist/circuits/circuit-lab.mjs +400 -0
  136. package/dist/circuits/index.d.mts +7 -0
  137. package/dist/circuits/index.mjs +8 -0
  138. package/dist/commerce/accounting/core.d.mts +48 -0
  139. package/dist/commerce/accounting/core.mjs +57 -0
  140. package/dist/commerce/accounting/equation-balance.d.mts +35 -0
  141. package/dist/commerce/accounting/equation-balance.mjs +292 -0
  142. package/dist/commerce/accounting/index.d.mts +4 -0
  143. package/dist/commerce/accounting/journal-poster.d.mts +29 -0
  144. package/dist/commerce/accounting/journal-poster.mjs +472 -0
  145. package/dist/commerce/accounting/statement-sorter.d.mts +25 -0
  146. package/dist/commerce/accounting/statement-sorter.mjs +304 -0
  147. package/dist/commerce/economics/core.d.mts +33 -0
  148. package/dist/commerce/economics/core.mjs +26 -0
  149. package/dist/commerce/economics/demand-shift-vs-move.d.mts +35 -0
  150. package/dist/commerce/economics/demand-shift-vs-move.mjs +324 -0
  151. package/dist/commerce/economics/elasticity-revenue.d.mts +31 -0
  152. package/dist/commerce/economics/elasticity-revenue.mjs +253 -0
  153. package/dist/commerce/economics/index.d.mts +4 -0
  154. package/dist/commerce/economics/market-equilibrium.d.mts +33 -0
  155. package/dist/commerce/economics/market-equilibrium.mjs +340 -0
  156. package/dist/commerce/index.d.mts +9 -0
  157. package/dist/commerce/index.mjs +10 -0
  158. package/dist/core/easing.d.mts +28 -0
  159. package/dist/core/easing.mjs +66 -0
  160. package/dist/core/index.d.mts +10 -0
  161. package/dist/core/index.mjs +11 -0
  162. package/dist/core/numeric.d.mts +37 -0
  163. package/dist/core/numeric.mjs +75 -0
  164. package/dist/core/reactive.d.mts +24 -0
  165. package/dist/core/reactive.mjs +51 -0
  166. package/dist/core/rng.d.mts +21 -0
  167. package/dist/core/rng.mjs +39 -0
  168. package/dist/core/tex.d.mts +14 -0
  169. package/dist/core/tex.mjs +44 -0
  170. package/dist/core/timeline.d.mts +34 -0
  171. package/dist/core/timeline.mjs +77 -0
  172. package/dist/core/util.d.mts +19 -0
  173. package/dist/core/util.mjs +31 -0
  174. package/dist/discrete/arrangements/preset.d.mts +28 -0
  175. package/dist/discrete/arrangements/preset.mjs +304 -0
  176. package/dist/discrete/bayes/preset.d.mts +33 -0
  177. package/dist/discrete/bayes/preset.mjs +476 -0
  178. package/dist/discrete/binomial/preset.d.mts +25 -0
  179. package/dist/discrete/binomial/preset.mjs +333 -0
  180. package/dist/discrete/core/combinatorics.d.mts +23 -0
  181. package/dist/discrete/core/combinatorics.mjs +57 -0
  182. package/dist/discrete/core/inclusionExclusion.d.mts +19 -0
  183. package/dist/discrete/core/inclusionExclusion.mjs +30 -0
  184. package/dist/discrete/core/pigeonhole.d.mts +15 -0
  185. package/dist/discrete/core/pigeonhole.mjs +24 -0
  186. package/dist/discrete/core/probability.d.mts +29 -0
  187. package/dist/discrete/core/probability.mjs +43 -0
  188. package/dist/discrete/core/sets.d.mts +21 -0
  189. package/dist/discrete/core/sets.mjs +45 -0
  190. package/dist/discrete/counting-slots/preset.d.mts +30 -0
  191. package/dist/discrete/counting-slots/preset.mjs +553 -0
  192. package/dist/discrete/counting-tree/index.d.mts +1 -0
  193. package/dist/discrete/counting-tree/preset.d.mts +44 -0
  194. package/dist/discrete/counting-tree/preset.mjs +364 -0
  195. package/dist/discrete/expected-value/preset.d.mts +28 -0
  196. package/dist/discrete/expected-value/preset.mjs +352 -0
  197. package/dist/discrete/hypergeometric/preset.d.mts +25 -0
  198. package/dist/discrete/hypergeometric/preset.mjs +330 -0
  199. package/dist/discrete/index.d.mts +26 -0
  200. package/dist/discrete/index.mjs +27 -0
  201. package/dist/discrete/karnaugh/index.d.mts +1 -0
  202. package/dist/discrete/karnaugh/preset.d.mts +30 -0
  203. package/dist/discrete/karnaugh/preset.mjs +452 -0
  204. package/dist/discrete/lln/asset.mjs +206 -0
  205. package/dist/discrete/lln/index.d.mts +1 -0
  206. package/dist/discrete/lln/preset.d.mts +17 -0
  207. package/dist/discrete/lln/preset.mjs +152 -0
  208. package/dist/discrete/logic-circuit/index.d.mts +1 -0
  209. package/dist/discrete/logic-circuit/preset.d.mts +47 -0
  210. package/dist/discrete/logic-circuit/preset.mjs +307 -0
  211. package/dist/discrete/monte-carlo/index.d.mts +1 -0
  212. package/dist/discrete/monte-carlo/preset.d.mts +64 -0
  213. package/dist/discrete/monte-carlo/preset.mjs +446 -0
  214. package/dist/discrete/monty-hall/index.d.mts +1 -0
  215. package/dist/discrete/monty-hall/preset.d.mts +23 -0
  216. package/dist/discrete/monty-hall/preset.mjs +491 -0
  217. package/dist/discrete/outcome-builder/preset.d.mts +24 -0
  218. package/dist/discrete/outcome-builder/preset.mjs +283 -0
  219. package/dist/discrete/pascal/preset.d.mts +24 -0
  220. package/dist/discrete/pascal/preset.mjs +372 -0
  221. package/dist/discrete/sample-space/index.d.mts +1 -0
  222. package/dist/discrete/sample-space/preset.d.mts +42 -0
  223. package/dist/discrete/sample-space/preset.mjs +395 -0
  224. package/dist/discrete/selection/preset.d.mts +33 -0
  225. package/dist/discrete/selection/preset.mjs +406 -0
  226. package/dist/discrete/truth-table/index.d.mts +1 -0
  227. package/dist/discrete/truth-table/preset.d.mts +30 -0
  228. package/dist/discrete/truth-table/preset.mjs +456 -0
  229. package/dist/discrete/venn/index.d.mts +1 -0
  230. package/dist/discrete/venn/preset.d.mts +32 -0
  231. package/dist/discrete/venn/preset.mjs +461 -0
  232. package/dist/geography/cycle-lab/index.d.mts +1 -0
  233. package/dist/geography/cycle-lab/preset.d.mts +25 -0
  234. package/dist/geography/cycle-lab/preset.mjs +254 -0
  235. package/dist/geography/cycles.d.mts +21 -0
  236. package/dist/geography/cycles.mjs +205 -0
  237. package/dist/geography/index.d.mts +4 -0
  238. package/dist/geography/index.mjs +5 -0
  239. package/dist/geometry/board/index.d.mts +1 -0
  240. package/dist/geometry/board/preset.d.mts +71 -0
  241. package/dist/geometry/board/preset.mjs +148 -0
  242. package/dist/geometry/builder.d.mts +18 -0
  243. package/dist/geometry/builder.mjs +332 -0
  244. package/dist/geometry/index.d.mts +4 -0
  245. package/dist/geometry/index.mjs +5 -0
  246. package/dist/geometry/intersecting-circles.d.mts +17 -0
  247. package/dist/geometry/intersecting-circles.mjs +104 -0
  248. package/dist/ict/index.d.mts +5 -0
  249. package/dist/ict/index.mjs +6 -0
  250. package/dist/ict/number-systems/base-odometer.d.mts +34 -0
  251. package/dist/ict/number-systems/base-odometer.mjs +160 -0
  252. package/dist/ict/number-systems/bit-grouper.d.mts +32 -0
  253. package/dist/ict/number-systems/bit-grouper.mjs +190 -0
  254. package/dist/ict/number-systems/index.d.mts +4 -0
  255. package/dist/ict/number-systems/place-value-dial.d.mts +29 -0
  256. package/dist/ict/number-systems/place-value-dial.mjs +153 -0
  257. package/dist/ict/number-systems/wheel.d.mts +73 -0
  258. package/dist/ict/number-systems/wheel.mjs +279 -0
  259. package/dist/index.d.mts +10 -0
  260. package/dist/index.mjs +11 -0
  261. package/dist/kit/anim.mjs +52 -0
  262. package/dist/kit/annotate.mjs +116 -0
  263. package/dist/kit/answer-check.d.mts +38 -0
  264. package/dist/kit/answer-check.mjs +65 -0
  265. package/dist/kit/asset-util.mjs +15 -0
  266. package/dist/kit/controls.mjs +77 -0
  267. package/dist/kit/cycle.d.mts +44 -0
  268. package/dist/kit/cycle.mjs +188 -0
  269. package/dist/kit/diagram.mjs +176 -0
  270. package/dist/kit/electronics.mjs +357 -0
  271. package/dist/kit/expr-analysis.d.mts +37 -0
  272. package/dist/kit/expr-analysis.mjs +111 -0
  273. package/dist/kit/frame.d.mts +70 -0
  274. package/dist/kit/frame.mjs +205 -0
  275. package/dist/kit/freq-tree.mjs +99 -0
  276. package/dist/kit/gameshow.mjs +315 -0
  277. package/dist/kit/index.d.mts +5 -0
  278. package/dist/kit/index.mjs +6 -0
  279. package/dist/kit/logic-gates.d.mts +6 -0
  280. package/dist/kit/logic-gates.mjs +210 -0
  281. package/dist/kit/molecules.mjs +178 -0
  282. package/dist/kit/pedagogy.d.mts +126 -0
  283. package/dist/kit/pedagogy.mjs +331 -0
  284. package/dist/kit/play.d.mts +21 -0
  285. package/dist/kit/play.mjs +52 -0
  286. package/dist/kit/probability.mjs +141 -0
  287. package/dist/kit/proportion.mjs +112 -0
  288. package/dist/kit/reaction.mjs +102 -0
  289. package/dist/kit/rich.d.mts +10 -0
  290. package/dist/kit/rich.mjs +21 -0
  291. package/dist/kit/scale.mjs +230 -0
  292. package/dist/kit/space.mjs +326 -0
  293. package/dist/kit/steps.d.mts +24 -0
  294. package/dist/kit/steps.mjs +57 -0
  295. package/dist/kit/thermal.mjs +289 -0
  296. package/dist/language/agreement/index.d.mts +1 -0
  297. package/dist/language/agreement/preset.d.mts +27 -0
  298. package/dist/language/agreement/preset.mjs +113 -0
  299. package/dist/language/article-lens/index.d.mts +1 -0
  300. package/dist/language/article-lens/preset.d.mts +31 -0
  301. package/dist/language/article-lens/preset.mjs +125 -0
  302. package/dist/language/deck.d.mts +45 -0
  303. package/dist/language/deck.mjs +61 -0
  304. package/dist/language/icon.d.mts +40 -0
  305. package/dist/language/icon.mjs +73 -0
  306. package/dist/language/index.d.mts +10 -0
  307. package/dist/language/index.mjs +11 -0
  308. package/dist/language/preposition-scene/index.d.mts +1 -0
  309. package/dist/language/preposition-scene/preset.d.mts +34 -0
  310. package/dist/language/preposition-scene/preset.mjs +234 -0
  311. package/dist/language/sentence-builder/index.d.mts +1 -0
  312. package/dist/language/sentence-builder/preset.d.mts +32 -0
  313. package/dist/language/sentence-builder/preset.mjs +122 -0
  314. package/dist/language/transform/index.d.mts +1 -0
  315. package/dist/language/transform/preset.d.mts +31 -0
  316. package/dist/language/transform/preset.mjs +137 -0
  317. package/dist/language/ui.d.mts +53 -0
  318. package/dist/language/ui.mjs +88 -0
  319. package/dist/language/word-match/index.d.mts +1 -0
  320. package/dist/language/word-match/preset.d.mts +22 -0
  321. package/dist/language/word-match/preset.mjs +148 -0
  322. package/dist/math/area-model/asset.d.mts +6 -0
  323. package/dist/math/area-model/asset.mjs +270 -0
  324. package/dist/math/area-model/index.d.mts +2 -0
  325. package/dist/math/area-model/preset.d.mts +29 -0
  326. package/dist/math/area-model/preset.mjs +243 -0
  327. package/dist/math/balance-algebra/asset.d.mts +6 -0
  328. package/dist/math/balance-algebra/asset.mjs +150 -0
  329. package/dist/math/balance-algebra/index.d.mts +2 -0
  330. package/dist/math/balance-algebra/preset.d.mts +30 -0
  331. package/dist/math/balance-algebra/preset.mjs +228 -0
  332. package/dist/math/derivation.d.mts +22 -0
  333. package/dist/math/derivation.mjs +113 -0
  334. package/dist/math/derivative-explorer/index.d.mts +1 -0
  335. package/dist/math/derivative-explorer/preset.d.mts +19 -0
  336. package/dist/math/derivative-explorer/preset.mjs +227 -0
  337. package/dist/math/function-machine/index.d.mts +1 -0
  338. package/dist/math/function-machine/preset.d.mts +21 -0
  339. package/dist/math/function-machine/preset.mjs +177 -0
  340. package/dist/math/gradient-descent.d.mts +23 -0
  341. package/dist/math/gradient-descent.mjs +473 -0
  342. package/dist/math/grapher/index.d.mts +1 -0
  343. package/dist/math/grapher/preset.d.mts +40 -0
  344. package/dist/math/grapher/preset.mjs +313 -0
  345. package/dist/math/harmonic-form/index.d.mts +1 -0
  346. package/dist/math/harmonic-form/preset.d.mts +21 -0
  347. package/dist/math/harmonic-form/preset.mjs +387 -0
  348. package/dist/math/index.d.mts +25 -0
  349. package/dist/math/index.mjs +26 -0
  350. package/dist/math/integral-explorer/index.d.mts +1 -0
  351. package/dist/math/integral-explorer/preset.d.mts +23 -0
  352. package/dist/math/integral-explorer/preset.mjs +217 -0
  353. package/dist/math/interactive/index.d.mts +1 -0
  354. package/dist/math/interactive/preset.d.mts +72 -0
  355. package/dist/math/interactive/preset.mjs +202 -0
  356. package/dist/math/limit-explorer/index.d.mts +1 -0
  357. package/dist/math/limit-explorer/preset.d.mts +19 -0
  358. package/dist/math/limit-explorer/preset.mjs +216 -0
  359. package/dist/math/linear-system/index.d.mts +1 -0
  360. package/dist/math/linear-system/preset.d.mts +28 -0
  361. package/dist/math/linear-system/preset.mjs +164 -0
  362. package/dist/math/mystery-bucket/asset.d.mts +6 -0
  363. package/dist/math/mystery-bucket/asset.mjs +145 -0
  364. package/dist/math/mystery-bucket/index.d.mts +2 -0
  365. package/dist/math/mystery-bucket/preset.d.mts +36 -0
  366. package/dist/math/mystery-bucket/preset.mjs +116 -0
  367. package/dist/math/number-line/index.d.mts +1 -0
  368. package/dist/math/number-line/preset.d.mts +24 -0
  369. package/dist/math/number-line/preset.mjs +142 -0
  370. package/dist/math/parabola/index.d.mts +1 -0
  371. package/dist/math/parabola/preset.d.mts +13 -0
  372. package/dist/math/parabola/preset.mjs +79 -0
  373. package/dist/math/pattern/asset.d.mts +6 -0
  374. package/dist/math/pattern/asset.mjs +102 -0
  375. package/dist/math/pattern/index.d.mts +2 -0
  376. package/dist/math/pattern/preset.d.mts +32 -0
  377. package/dist/math/pattern/preset.mjs +294 -0
  378. package/dist/math/triangle-trig/index.d.mts +1 -0
  379. package/dist/math/triangle-trig/preset.d.mts +47 -0
  380. package/dist/math/triangle-trig/preset.mjs +245 -0
  381. package/dist/math/trig-explorer.d.mts +18 -0
  382. package/dist/math/trig-explorer.mjs +209 -0
  383. package/dist/ml/boundary/index.d.mts +1 -0
  384. package/dist/ml/boundary/preset.d.mts +24 -0
  385. package/dist/ml/boundary/preset.mjs +385 -0
  386. package/dist/ml/classifier/index.d.mts +1 -0
  387. package/dist/ml/classifier/preset.d.mts +25 -0
  388. package/dist/ml/classifier/preset.mjs +315 -0
  389. package/dist/ml/index.d.mts +6 -0
  390. package/dist/ml/index.mjs +7 -0
  391. package/dist/ml/kmeans/index.d.mts +1 -0
  392. package/dist/ml/kmeans/preset.d.mts +33 -0
  393. package/dist/ml/kmeans/preset.mjs +236 -0
  394. package/dist/ml/knn/index.d.mts +1 -0
  395. package/dist/ml/knn/preset.d.mts +28 -0
  396. package/dist/ml/knn/preset.mjs +279 -0
  397. package/dist/ml/regression/index.d.mts +1 -0
  398. package/dist/ml/regression/preset.d.mts +32 -0
  399. package/dist/ml/regression/preset.mjs +394 -0
  400. package/dist/physics/atwood/index.d.mts +1 -0
  401. package/dist/physics/atwood/preset.d.mts +23 -0
  402. package/dist/physics/atwood/preset.mjs +312 -0
  403. package/dist/physics/bullet-walls/index.d.mts +1 -0
  404. package/dist/physics/bullet-walls/preset.d.mts +28 -0
  405. package/dist/physics/bullet-walls/preset.mjs +373 -0
  406. package/dist/physics/carnot/index.d.mts +1 -0
  407. package/dist/physics/carnot/preset.d.mts +27 -0
  408. package/dist/physics/carnot/preset.mjs +427 -0
  409. package/dist/physics/circular-motion/index.d.mts +1 -0
  410. package/dist/physics/circular-motion/preset.d.mts +25 -0
  411. package/dist/physics/circular-motion/preset.mjs +293 -0
  412. package/dist/physics/collision-track/index.d.mts +1 -0
  413. package/dist/physics/collision-track/preset.d.mts +27 -0
  414. package/dist/physics/collision-track/preset.mjs +352 -0
  415. package/dist/physics/efficiency/index.d.mts +1 -0
  416. package/dist/physics/efficiency/preset.d.mts +33 -0
  417. package/dist/physics/efficiency/preset.mjs +343 -0
  418. package/dist/physics/electric-field/index.d.mts +1 -0
  419. package/dist/physics/electric-field/preset.d.mts +15 -0
  420. package/dist/physics/electric-field/preset.mjs +306 -0
  421. package/dist/physics/energy-skate/index.d.mts +1 -0
  422. package/dist/physics/energy-skate/preset.d.mts +25 -0
  423. package/dist/physics/energy-skate/preset.mjs +254 -0
  424. package/dist/physics/entropy/index.d.mts +1 -0
  425. package/dist/physics/entropy/preset.d.mts +18 -0
  426. package/dist/physics/entropy/preset.mjs +416 -0
  427. package/dist/physics/expansion/index.d.mts +1 -0
  428. package/dist/physics/expansion/preset.d.mts +26 -0
  429. package/dist/physics/expansion/preset.mjs +609 -0
  430. package/dist/physics/exponential/asset.mjs +221 -0
  431. package/dist/physics/exponential/index.d.mts +1 -0
  432. package/dist/physics/exponential/preset.d.mts +17 -0
  433. package/dist/physics/exponential/preset.mjs +182 -0
  434. package/dist/physics/gas-process/index.d.mts +1 -0
  435. package/dist/physics/gas-process/preset.d.mts +30 -0
  436. package/dist/physics/gas-process/preset.mjs +384 -0
  437. package/dist/physics/gravitation/index.d.mts +1 -0
  438. package/dist/physics/gravitation/preset.d.mts +24 -0
  439. package/dist/physics/gravitation/preset.mjs +345 -0
  440. package/dist/physics/gravity-drop.d.mts +9 -0
  441. package/dist/physics/gravity-drop.mjs +187 -0
  442. package/dist/physics/heat-transfer/index.d.mts +1 -0
  443. package/dist/physics/heat-transfer/preset.d.mts +27 -0
  444. package/dist/physics/heat-transfer/preset.mjs +626 -0
  445. package/dist/physics/impulse/index.d.mts +1 -0
  446. package/dist/physics/impulse/preset.d.mts +29 -0
  447. package/dist/physics/impulse/preset.mjs +453 -0
  448. package/dist/physics/index.d.mts +43 -0
  449. package/dist/physics/index.mjs +44 -0
  450. package/dist/physics/kepler/index.d.mts +1 -0
  451. package/dist/physics/kepler/preset.d.mts +27 -0
  452. package/dist/physics/kepler/preset.mjs +257 -0
  453. package/dist/physics/lever/asset.d.mts +6 -0
  454. package/dist/physics/lever/asset.mjs +220 -0
  455. package/dist/physics/lever/index.d.mts +2 -0
  456. package/dist/physics/lever/preset.d.mts +31 -0
  457. package/dist/physics/lever/preset.mjs +261 -0
  458. package/dist/physics/lorentz/index.d.mts +1 -0
  459. package/dist/physics/lorentz/preset.d.mts +29 -0
  460. package/dist/physics/lorentz/preset.mjs +367 -0
  461. package/dist/physics/magnetism/index.d.mts +1 -0
  462. package/dist/physics/magnetism/preset.d.mts +15 -0
  463. package/dist/physics/magnetism/preset.mjs +334 -0
  464. package/dist/physics/optics/asset.d.mts +6 -0
  465. package/dist/physics/optics/asset.mjs +234 -0
  466. package/dist/physics/optics/index.d.mts +2 -0
  467. package/dist/physics/optics/preset.d.mts +14 -0
  468. package/dist/physics/optics/preset.mjs +202 -0
  469. package/dist/physics/orbit-lab.d.mts +6 -0
  470. package/dist/physics/orbit-lab.mjs +222 -0
  471. package/dist/physics/projectile-lab.d.mts +10 -0
  472. package/dist/physics/projectile-lab.mjs +354 -0
  473. package/dist/physics/rain-relative/index.d.mts +1 -0
  474. package/dist/physics/rain-relative/preset.d.mts +19 -0
  475. package/dist/physics/rain-relative/preset.mjs +358 -0
  476. package/dist/physics/ramp-forces/index.d.mts +1 -0
  477. package/dist/physics/ramp-forces/preset.d.mts +36 -0
  478. package/dist/physics/ramp-forces/preset.mjs +550 -0
  479. package/dist/physics/river-boat.d.mts +22 -0
  480. package/dist/physics/river-boat.mjs +498 -0
  481. package/dist/physics/shm/index.d.mts +1 -0
  482. package/dist/physics/shm/preset.d.mts +33 -0
  483. package/dist/physics/shm/preset.mjs +554 -0
  484. package/dist/physics/stopping-distance/index.d.mts +1 -0
  485. package/dist/physics/stopping-distance/preset.d.mts +29 -0
  486. package/dist/physics/stopping-distance/preset.mjs +662 -0
  487. package/dist/physics/temperature-scales/index.d.mts +1 -0
  488. package/dist/physics/temperature-scales/preset.d.mts +15 -0
  489. package/dist/physics/temperature-scales/preset.mjs +353 -0
  490. package/dist/physics/terminal-velocity/index.d.mts +1 -0
  491. package/dist/physics/terminal-velocity/preset.d.mts +26 -0
  492. package/dist/physics/terminal-velocity/preset.mjs +404 -0
  493. package/dist/physics/thermal/index.d.mts +1 -0
  494. package/dist/physics/thermal/preset.d.mts +44 -0
  495. package/dist/physics/thermal/preset.mjs +373 -0
  496. package/dist/physics/vector-board/index.d.mts +1 -0
  497. package/dist/physics/vector-board/preset.d.mts +65 -0
  498. package/dist/physics/vector-board/preset.mjs +273 -0
  499. package/dist/physics/vector-board/view.d.mts +49 -0
  500. package/dist/physics/vector-board/view.mjs +58 -0
  501. package/dist/physics/vector-scene.d.mts +41 -0
  502. package/dist/physics/vector-scene.mjs +211 -0
  503. package/dist/physics/vector-types/index.d.mts +1 -0
  504. package/dist/physics/vector-types/preset.d.mts +27 -0
  505. package/dist/physics/vector-types/preset.mjs +192 -0
  506. package/dist/physics/water-density/index.d.mts +1 -0
  507. package/dist/physics/water-density/preset.d.mts +18 -0
  508. package/dist/physics/water-density/preset.mjs +439 -0
  509. package/dist/physics/waves/core.mjs +33 -0
  510. package/dist/physics/waves/doppler.d.mts +23 -0
  511. package/dist/physics/waves/doppler.mjs +260 -0
  512. package/dist/physics/waves/index.d.mts +4 -0
  513. package/dist/physics/waves/preset.d.mts +30 -0
  514. package/dist/physics/waves/preset.mjs +488 -0
  515. package/dist/physics/waves/ripple.d.mts +26 -0
  516. package/dist/physics/waves/ripple.mjs +234 -0
  517. package/dist/physics/waves/string.d.mts +29 -0
  518. package/dist/physics/waves/string.mjs +289 -0
  519. package/dist/physics/work-energy/index.d.mts +1 -0
  520. package/dist/physics/work-energy/preset.d.mts +17 -0
  521. package/dist/physics/work-energy/preset.mjs +341 -0
  522. package/dist/schemas/index.d.mts +235 -0
  523. package/dist/schemas/index.mjs +145 -0
  524. package/dist/statistics/center-spread/preset.d.mts +34 -0
  525. package/dist/statistics/center-spread/preset.mjs +323 -0
  526. package/dist/statistics/core/descriptive.d.mts +35 -0
  527. package/dist/statistics/core/descriptive.mjs +64 -0
  528. package/dist/statistics/core/normal.d.mts +21 -0
  529. package/dist/statistics/core/normal.mjs +37 -0
  530. package/dist/statistics/core/sequences.d.mts +27 -0
  531. package/dist/statistics/core/sequences.mjs +35 -0
  532. package/dist/statistics/galton/preset.d.mts +27 -0
  533. package/dist/statistics/galton/preset.mjs +331 -0
  534. package/dist/statistics/histogram/preset.d.mts +27 -0
  535. package/dist/statistics/histogram/preset.mjs +385 -0
  536. package/dist/statistics/index.d.mts +11 -0
  537. package/dist/statistics/index.mjs +12 -0
  538. package/dist/statistics/normal/preset.d.mts +30 -0
  539. package/dist/statistics/normal/preset.mjs +400 -0
  540. package/dist/statistics/sampling/preset.d.mts +32 -0
  541. package/dist/statistics/sampling/preset.mjs +406 -0
  542. package/dist/statistics/sequence/preset.d.mts +28 -0
  543. package/dist/statistics/sequence/preset.mjs +302 -0
  544. package/dist/statistics/z-table/preset.d.mts +28 -0
  545. package/dist/statistics/z-table/preset.mjs +348 -0
  546. package/package.json +266 -0
  547. package/styles.css +267 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Classytic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @classytic/labs
2
+
3
+ **Authorable, domain-organized interactive learning labs — built on the
4
+ [`@classytic/stage`](../stage) engine.** Labs are React components composed from the
5
+ stage primitives — SVG `<Stage>` (accessible, themeable, agent-drivable) plus a zero-dep
6
+ `<CanvasLayer>` for high-element animations — with HTML controls around them, tokenized
7
+ and validated by Zod prop schemas. Some labs are backed by a portable JSON `SceneDoc`
8
+ (typed dependency graph + resolver + command/undo); many are direct primitive
9
+ compositions. Per-domain subpaths so
10
+ you only ship what you import.
11
+
12
+ The product isn't a fixed widget catalogue — it's a small, sharp engine + reusable
13
+ **lab families** (templates) a teacher composes and configures. Adding a "new lab" =
14
+ registering one family, not new plumbing.
15
+
16
+ ```bash
17
+ npm i @classytic/labs @classytic/stage
18
+ # required peers: react, react-dom
19
+ # optional peers: katex (pretty math), zod (template params + /blocks), lucide-react,
20
+ # @classytic/cms-ui + @classytic/cms (for /blocks authoring)
21
+ ```
22
+
23
+ ```css
24
+ /* in your global CSS, after `@import "tailwindcss";` */
25
+ @import "@classytic/stage/styles.css"; /* engine tokens + asset kit */
26
+ @import "@classytic/labs/styles.css"; /* control-UI kit + lab tokens */
27
+ ```
28
+
29
+ ```ts
30
+ // once, if you use any lab that renders equations (DerivativeExplorer, Grapher, …)
31
+ import "katex/dist/katex.min.css";
32
+ ```
33
+
34
+ ## Use a lab
35
+
36
+ ```tsx
37
+ import { Grapher, DerivativeExplorer, BalanceAlgebraLab } from '@classytic/labs/math';
38
+ import { LeverBalanceLab } from '@classytic/labs/physics';
39
+ import { CircuitNetworkLab } from '@classytic/labs/circuits';
40
+
41
+ // type any equation(s) + expose learner-draggable param sliders:
42
+ <Grapher equations={['a*sin(b*x)']} params={[{ name: 'a', min: 0, max: 3, value: 1 }]} />
43
+
44
+ // drag a point; the secant becomes the EXACT tangent (symbolic f′), shown in KaTeX:
45
+ <DerivativeExplorer equation="x^2" />
46
+
47
+ // solve 2x + 1 = 7 by balancing the scale:
48
+ <BalanceAlgebraLab coef={2} addend={1} rhs={7} answer={3} />
49
+ ```
50
+
51
+ ## The lab-template registry (`@classytic/labs/catalog`)
52
+
53
+ Families are listable/instantiable units — the basis for a lab "marketplace" and the
54
+ authoring kit. Params are validated against the family's zod schema before a scene is built.
55
+
56
+ ```ts
57
+ import { listLabTemplates, instantiateTemplate } from '@classytic/labs/catalog';
58
+
59
+ listLabTemplates(); // [{ id:'balance-algebra', category:'algebra', … }, …]
60
+ const r = instantiateTemplate('balance-algebra', { coef: 3, addend: 1, rhs: 10, answer: 3 });
61
+ if (r.ok) renderScene(r.doc); // r.doc is a portable SceneDoc
62
+ else console.warn(r.error, r.issues); // typed validation errors — no broken scene
63
+ ```
64
+
65
+ Built-in families: `balance-algebra`, `area-model`, `growing-pattern`, `balance-lever`,
66
+ `optics`, `circuit` (more as domains migrate).
67
+
68
+ ## Authoring
69
+
70
+ Labs are authored with the stage **SceneBuilder** in `authoring` mode (place points,
71
+ sliders, labels; live scalar controls; a pedagogy/`LabMeta` editor) and surfaced in a
72
+ host (e.g. Brihot/Mentora) via the `@classytic/cms-ui` `defineBlock` contract. The lab's
73
+ completion/score events flow to the host's learner seam (xAPI) via `useLearner().report`.
74
+
75
+ ## Subpaths
76
+
77
+ Domain packs (lab components): `@classytic/labs/{math, physics, chem, circuits, geometry,
78
+ language, ict, commerce, biology, geography, discrete, statistics, ml}`. Shared:
79
+ `@classytic/labs/{core, catalog, schemas}`. Editor blocks: `@classytic/labs/blocks` (all)
80
+ or per-domain `@classytic/labs/blocks/{math, physics, chem, circuits, geometry, ict,
81
+ language, accounting, economics, biology, geography, ml, discrete, statistics}`. Import
82
+ only what you need (tree-shaken at the subpath boundary).
83
+
84
+ ## Rendering model (all on @classytic/stage)
85
+
86
+ **Every lab renders on the stage engine — the legacy canvas engine is gone.** Two backends:
87
+
88
+ - **SVG `<Stage>`** (the default — accessible, themeable, SceneDoc-portable): all of math,
89
+ the physics vector/diagram labs (`VectorScene`, `RiverBoat`, `ProjectileLab`,
90
+ `GravityDrop`), chemistry (`BohrAtom`, `ReactionProfile`, `ReactionLab`, `Battery`),
91
+ circuits (`CircuitLab`, `CircuitBuilder`, `CircuitNetworkLab`), and geometry
92
+ (`GeometryBoard`, `GeometryBuilder`, `IntersectingCircles`).
93
+ - **`<CanvasLayer>`** (stage's zero-dependency raw-Canvas2D escape hatch — HiDPI, shares the
94
+ engine coords + clock) for the genuine high-element-count labs: `GradientDescent` (per-cell
95
+ heatmap) and `OrbitLab` (fading 260-point trail). This is the deliberate alternative to a
96
+ heavy GPU dependency; nothing here needs WebGL.
97
+
98
+ `@classytic/labs/core` is now just a small domain-neutral toolkit on top of stage (lazy-KaTeX
99
+ `<Tex>`, numerical calculus, easing/keyframes, a tiny reactive value, number utils) — the
100
+ coordinate system, primitives, resolver, clock, control surface, and learner seam all live in
101
+ `@classytic/stage`.
102
+
103
+ ## License
104
+
105
+ MIT
@@ -0,0 +1 @@
1
+ import { EnzymeRateLab, EnzymeRateProps } from "./preset.mjs";
@@ -0,0 +1,25 @@
1
+ import { ReactNode } from "react";
2
+
3
+ //#region src/biology/enzyme-rate/preset.d.ts
4
+ interface EnzymeRateProps {
5
+ factor?: 'temperature' | 'pH';
6
+ optimum?: number;
7
+ factorMin?: number;
8
+ factorMax?: number;
9
+ title?: string;
10
+ prompt?: string;
11
+ height?: number;
12
+ objectives?: string[];
13
+ }
14
+ declare function EnzymeRateLab({
15
+ factor,
16
+ optimum,
17
+ factorMin,
18
+ factorMax,
19
+ title,
20
+ prompt,
21
+ height,
22
+ objectives
23
+ }: EnzymeRateProps): ReactNode;
24
+ //#endregion
25
+ export { EnzymeRateLab, EnzymeRateProps };
@@ -0,0 +1,256 @@
1
+ 'use client';
2
+
3
+ import { clamp } from "../../core/util.mjs";
4
+ import { Slider, StatusPill } from "../../kit/controls.mjs";
5
+ import { Callout, ControlBar, Field, LabFrame, LiveRegion } from "../../kit/frame.mjs";
6
+ import { useCheckpoint } from "../../kit/pedagogy.mjs";
7
+ import { useRef, useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { Axes, Dot, Label, Polygon, Segment, Stage } from "@classytic/stage";
10
+
11
+ //#region src/biology/enzyme-rate/preset.tsx
12
+ /**
13
+ * EnzymeRateLab — the optimum, then the cliff: enzymes denature.
14
+ *
15
+ * Drag temperature: the rate climbs to an optimum (more kinetic energy → more
16
+ * successful enzyme–substrate collisions) then CRASHES as the lock-and-key active
17
+ * site is mangled — and crucially, cooling back down does NOT restore it
18
+ * (thermal denaturation is irreversible). The bell is BUILT from plotted dots as
19
+ * you sweep, never pre-drawn. pH mode shows the same optimum but reversibly; a
20
+ * "fresh enzyme" reset is the only way back from a denatured run.
21
+ *
22
+ * Reuses core/util + kit/controls; tokenized; reduced-motion safe (no autoplay —
23
+ * the learner drags). The collision-theory vs denaturation split → a MathDerivation.
24
+ */
25
+ const CLIFF = 16;
26
+ /** A lock-and-key enzyme glyph; `denat` 0 (intact) → 1 (mangled active site). */
27
+ function Enzyme({ denat }) {
28
+ const d = clamp(denat, 0, 1);
29
+ const body = d > .05 ? "var(--stage-danger)" : "var(--stage-accent)";
30
+ const edge = `color-mix(in oklab, ${body} 60%, black)`;
31
+ const splay = d * 14;
32
+ const notch = `M ${40 - splay} 26 L 52 ${48 + d * 8} L 64 ${30 - splay * .5} L 76 ${48 + d * 6} L ${88 + splay} 26`;
33
+ const keySeated = d < .25;
34
+ return /* @__PURE__ */ jsxs("svg", {
35
+ width: 150,
36
+ height: 120,
37
+ viewBox: "0 0 130 120",
38
+ "aria-hidden": true,
39
+ children: [
40
+ /* @__PURE__ */ jsx("path", {
41
+ d: "M 18 30 Q 14 70 30 96 Q 64 110 98 96 Q 114 70 110 30 Z",
42
+ fill: `color-mix(in oklab, ${body} 22%, var(--stage-bg))`,
43
+ stroke: edge,
44
+ strokeWidth: 2
45
+ }),
46
+ /* @__PURE__ */ jsx("path", {
47
+ d: notch,
48
+ fill: "var(--stage-bg)",
49
+ stroke: edge,
50
+ strokeWidth: 2.5,
51
+ strokeLinejoin: "round"
52
+ }),
53
+ /* @__PURE__ */ jsx("g", {
54
+ transform: keySeated ? "translate(52 8)" : `translate(58 -8) rotate(${18 + d * 14} 12 14)`,
55
+ children: /* @__PURE__ */ jsx("path", {
56
+ d: "M 0 18 L 12 0 L 24 18 Z",
57
+ fill: keySeated ? "var(--stage-good)" : "var(--stage-warn)",
58
+ stroke: "color-mix(in oklab, var(--stage-fg) 40%, transparent)",
59
+ strokeWidth: 1
60
+ })
61
+ }),
62
+ /* @__PURE__ */ jsx("text", {
63
+ x: 64,
64
+ y: 114,
65
+ fill: "var(--stage-muted)",
66
+ fontSize: 10,
67
+ fontWeight: 700,
68
+ textAnchor: "middle",
69
+ children: d > .4 ? "DENATURED" : keySeated ? "substrate fits" : "binding…"
70
+ })
71
+ ]
72
+ });
73
+ }
74
+ function EnzymeRateLab({ factor = "temperature", optimum = 40, factorMin = 0, factorMax = 80, title = "The optimum, then the cliff — enzymes denature", prompt = "Drag the temperature. Past the optimum the enzyme denatures — and cooling back down won’t fix it.", height = 240, objectives }) {
75
+ const reversible = factor === "pH";
76
+ const [f, setF] = useState(reversible ? optimum - 2 : factorMin + 5);
77
+ const peak = useRef(f);
78
+ const pts = useRef(/* @__PURE__ */ new Map());
79
+ const [, setTick] = useState(0);
80
+ useCheckpoint({
81
+ solved: Math.abs(f - optimum) < 1.5,
82
+ activity: "enzyme-rate"
83
+ });
84
+ const denatFrom = (x) => clamp((x - optimum) / CLIFF, 0, 1);
85
+ const denat = reversible ? denatFrom(f) : denatFrom(peak.current);
86
+ const rate = clamp(clamp(f / optimum, 0, 1) * (1 - denat), 0, 1);
87
+ pts.current.set(Math.round(f), rate);
88
+ const onF = (v) => {
89
+ setF(v);
90
+ if (!reversible && v > peak.current) peak.current = v;
91
+ setTick((t) => t + 1 & 65535);
92
+ };
93
+ const reset = () => {
94
+ pts.current = /* @__PURE__ */ new Map();
95
+ peak.current = factorMin + 5;
96
+ setF(factorMin + 5);
97
+ setTick((t) => t + 1 & 65535);
98
+ };
99
+ const view = {
100
+ xMin: factorMin - (factorMax - factorMin) * .06,
101
+ xMax: factorMax + 1,
102
+ yMin: -.12,
103
+ yMax: 1.12
104
+ };
105
+ const unit = reversible ? "" : " °C";
106
+ const points = [...pts.current.entries()].sort((a, b) => a[0] - b[0]);
107
+ const state = denat > .5 ? "denatured" : Math.abs(f - optimum) < 2 ? "optimum" : f < optimum ? "climbing" : "falling";
108
+ const figure = /* @__PURE__ */ jsxs("div", {
109
+ style: {
110
+ display: "flex",
111
+ gap: 12,
112
+ flexWrap: "wrap",
113
+ alignItems: "center",
114
+ borderRadius: 14,
115
+ background: "var(--stage-bg)",
116
+ border: "1px solid var(--stage-grid)",
117
+ padding: 12
118
+ },
119
+ children: [/* @__PURE__ */ jsx(Enzyme, { denat }), /* @__PURE__ */ jsx("div", {
120
+ style: {
121
+ flex: 1,
122
+ minWidth: 240
123
+ },
124
+ children: /* @__PURE__ */ jsxs(Stage, {
125
+ view,
126
+ height,
127
+ preserveAspect: false,
128
+ ariaLabel: `Enzyme rate vs ${factor}; ${state}`,
129
+ children: [
130
+ /* @__PURE__ */ jsx(Axes, { ticks: false }),
131
+ /* @__PURE__ */ jsx(Label, {
132
+ x: (factorMin + factorMax) / 2,
133
+ y: -.08,
134
+ text: reversible ? "pH →" : "temperature →",
135
+ color: "var(--stage-muted)",
136
+ size: 11
137
+ }),
138
+ /* @__PURE__ */ jsx(Label, {
139
+ x: factorMin,
140
+ y: .55,
141
+ text: "rate",
142
+ color: "var(--stage-muted)",
143
+ size: 11,
144
+ dx: -6
145
+ }),
146
+ !reversible && /* @__PURE__ */ jsx(Polygon, {
147
+ points: [
148
+ {
149
+ x: optimum + CLIFF,
150
+ y: 0
151
+ },
152
+ {
153
+ x: factorMax,
154
+ y: 0
155
+ },
156
+ {
157
+ x: factorMax,
158
+ y: 1.1
159
+ },
160
+ {
161
+ x: optimum + CLIFF,
162
+ y: 1.1
163
+ }
164
+ ],
165
+ color: "var(--stage-danger)",
166
+ fill: "var(--stage-danger)",
167
+ fillOpacity: .08,
168
+ weight: 0
169
+ }),
170
+ /* @__PURE__ */ jsx(Segment, {
171
+ from: {
172
+ x: optimum,
173
+ y: 0
174
+ },
175
+ to: {
176
+ x: optimum,
177
+ y: 1.1
178
+ },
179
+ color: "var(--stage-good)",
180
+ weight: 1,
181
+ dashed: true,
182
+ opacity: .5
183
+ }),
184
+ /* @__PURE__ */ jsx(Label, {
185
+ x: optimum,
186
+ y: 1.1,
187
+ text: "optimum",
188
+ color: "var(--stage-good)",
189
+ size: 10,
190
+ dy: -4
191
+ }),
192
+ points.map(([x, y]) => /* @__PURE__ */ jsx(Dot, {
193
+ x,
194
+ y,
195
+ r: 3,
196
+ color: "var(--stage-accent)",
197
+ opacity: .85
198
+ }, x)),
199
+ /* @__PURE__ */ jsx(Dot, {
200
+ x: f,
201
+ y: rate,
202
+ r: 6,
203
+ color: denat > .5 ? "var(--stage-danger)" : "var(--stage-good)"
204
+ })
205
+ ]
206
+ })
207
+ })]
208
+ });
209
+ return /* @__PURE__ */ jsx(LabFrame, {
210
+ title,
211
+ prompt,
212
+ objectives,
213
+ aside: /* @__PURE__ */ jsxs(Callout, {
214
+ tone: state === "optimum" ? "result" : "info",
215
+ children: [/* @__PURE__ */ jsxs("div", {
216
+ style: {
217
+ fontVariantNumeric: "tabular-nums",
218
+ fontWeight: 600
219
+ },
220
+ children: [
221
+ reversible ? "pH" : "temperature",
222
+ " ",
223
+ f.toFixed(0),
224
+ unit,
225
+ " · rate ",
226
+ (rate * 100).toFixed(0)
227
+ ]
228
+ }), /* @__PURE__ */ jsx(StatusPill, {
229
+ ok: state === "optimum",
230
+ children: state === "optimum" ? "at the optimum ✓" : state === "denatured" ? "denatured — no recovery" : state === "falling" ? reversible ? "past optimum" : "denaturing…" : "climbing (more collisions)"
231
+ })]
232
+ }),
233
+ controls: /* @__PURE__ */ jsxs(ControlBar, { children: [/* @__PURE__ */ jsx(Field, {
234
+ label: reversible ? "pH" : "temperature",
235
+ value: `${f.toFixed(0)}${unit}`,
236
+ children: /* @__PURE__ */ jsx(Slider, {
237
+ value: f,
238
+ min: factorMin,
239
+ max: factorMax,
240
+ step: 1,
241
+ onChange: onF,
242
+ ariaLabel: reversible ? "pH" : "temperature"
243
+ })
244
+ }), /* @__PURE__ */ jsx("button", {
245
+ type: "button",
246
+ className: "lab-chip",
247
+ onClick: reset,
248
+ children: "↺ fresh enzyme"
249
+ })] }),
250
+ footer: /* @__PURE__ */ jsx(LiveRegion, { children: `${reversible ? "pH" : "Temperature"} ${f.toFixed(0)}, rate ${(rate * 100).toFixed(0)}. ${state}.` }),
251
+ children: figure
252
+ });
253
+ }
254
+
255
+ //#endregion
256
+ export { EnzymeRateLab };
@@ -0,0 +1,64 @@
1
+ //#region src/biology/genetic-cross/core.d.ts
2
+ /**
3
+ * Genetics core — a SERIALIZABLE cross model + the rule that turns a genotype into
4
+ * a phenotype. Authored as data (so agents can declare it via JSON), resolved into
5
+ * pure functions for the lab. One model covers simple dominance, multiple alleles,
6
+ * codominance and incomplete dominance:
7
+ *
8
+ * • each allele has a dominance `rank` + the `trait` it expresses
9
+ * • phenotype = the trait(s) of the highest-rank allele(s) present; two equal
10
+ * top-rank alleles with different traits express together (CODOMINANCE → "AB")
11
+ * • an optional `blends` entry overrides a specific heterozygote
12
+ * (INCOMPLETE DOMINANCE → red × white = pink)
13
+ *
14
+ * No reinvented numerics — this is just the genetics rule; UI lives in the preset.
15
+ */
16
+ interface AlleleSpec {
17
+ symbol: string;
18
+ rank: number;
19
+ trait: string;
20
+ }
21
+ interface BlendSpec {
22
+ pair: [string, string];
23
+ label: string;
24
+ color?: string;
25
+ }
26
+ interface CrossModelSpec {
27
+ trait?: string;
28
+ alleles: AlleleSpec[];
29
+ blends?: BlendSpec[];
30
+ colors?: Record<string, string>;
31
+ }
32
+ interface Phenotype {
33
+ label: string;
34
+ color: string;
35
+ }
36
+ interface ResolvedModel {
37
+ trait: string;
38
+ alleles: AlleleSpec[];
39
+ /** order a genotype's two alleles by their declared position (stable display). */
40
+ norm: (a: string, b: string) => [string, string];
41
+ phenotypeOf: (a: string, b: string) => Phenotype;
42
+ phenotypeColor: (label: string) => string;
43
+ /** every possible genotype as a normalised [a,b] pair (for parent pickers). */
44
+ genotypes: [string, string][];
45
+ /** true if `a` masks `b` here — a dominant over a present lower-rank allele. */
46
+ masks: (a: string, b: string) => boolean;
47
+ }
48
+ declare function resolveModel(spec: CrossModelSpec): ResolvedModel;
49
+ /** Simple dominant/recessive — the classic monohybrid (also backs PunnettCross). */
50
+ declare const monohybridSpec: (letter?: string, dominant?: string, recessive?: string) => CrossModelSpec;
51
+ /** ABO blood groups — multiple alleles + codominance (Aᴬ Aᴮ → AB). */
52
+ declare const BLOOD_TYPE_SPEC: CrossModelSpec;
53
+ /** Incomplete dominance — red × white = pink (a blended heterozygote). */
54
+ declare const INCOMPLETE_SPEC: CrossModelSpec;
55
+ declare const CROSS_PRESETS: {
56
+ readonly monohybrid: CrossModelSpec;
57
+ readonly 'blood-type': CrossModelSpec;
58
+ readonly incomplete: CrossModelSpec;
59
+ };
60
+ type CrossPresetKey = keyof typeof CROSS_PRESETS;
61
+ /** Two independent genes — the classic dihybrid 9:3:3:1 (height × seed colour). */
62
+ declare const DIHYBRID_LOCI: CrossModelSpec[];
63
+ //#endregion
64
+ export { AlleleSpec, BLOOD_TYPE_SPEC, BlendSpec, CROSS_PRESETS, CrossModelSpec, CrossPresetKey, DIHYBRID_LOCI, INCOMPLETE_SPEC, Phenotype, ResolvedModel, monohybridSpec, resolveModel };
@@ -0,0 +1,147 @@
1
+ //#region src/biology/genetic-cross/core.ts
2
+ const PALETTE = [
3
+ "var(--stage-accent)",
4
+ "var(--stage-accent-2)",
5
+ "var(--stage-good)",
6
+ "var(--stage-warn)",
7
+ "var(--stage-danger)",
8
+ "var(--stage-muted)"
9
+ ];
10
+ const blendKey = (x, y) => [x, y].sort().join("|");
11
+ function resolveModel(spec) {
12
+ const order = new Map(spec.alleles.map((a, i) => [a.symbol, i]));
13
+ const rank = new Map(spec.alleles.map((a) => [a.symbol, a.rank]));
14
+ const trait = new Map(spec.alleles.map((a) => [a.symbol, a.trait]));
15
+ const blends = new Map((spec.blends ?? []).map((b) => [blendKey(b.pair[0], b.pair[1]), b]));
16
+ const labelOfPair = (a, b) => {
17
+ const bl = blends.get(blendKey(a, b));
18
+ if (bl) return bl.label;
19
+ const maxR = Math.max(rank.get(a) ?? 0, rank.get(b) ?? 0);
20
+ const tops = [a, b].filter((s) => (rank.get(s) ?? 0) === maxR);
21
+ const traits = [...new Set(tops.map((s) => trait.get(s) ?? s))];
22
+ return traits.length === 1 ? traits[0] : traits.join("");
23
+ };
24
+ const syms = spec.alleles.map((a) => a.symbol);
25
+ const genotypes = [];
26
+ const seen = [];
27
+ for (let i = 0; i < syms.length; i++) for (let j = i; j < syms.length; j++) {
28
+ genotypes.push([syms[i], syms[j]]);
29
+ const l = labelOfPair(syms[i], syms[j]);
30
+ if (!seen.includes(l)) seen.push(l);
31
+ }
32
+ const colorMap = new Map(seen.map((l, idx) => [l, spec.colors?.[l] ?? PALETTE[idx % PALETTE.length]]));
33
+ for (const b of spec.blends ?? []) if (b.color) colorMap.set(b.label, b.color);
34
+ const phenotypeColor = (label) => colorMap.get(label) ?? PALETTE[0];
35
+ return {
36
+ trait: spec.trait ?? "phenotype",
37
+ alleles: spec.alleles,
38
+ norm: (a, b) => (order.get(a) ?? 0) <= (order.get(b) ?? 0) ? [a, b] : [b, a],
39
+ phenotypeOf: (a, b) => ({
40
+ label: labelOfPair(a, b),
41
+ color: phenotypeColor(labelOfPair(a, b))
42
+ }),
43
+ phenotypeColor,
44
+ genotypes,
45
+ masks: (a, b) => !blends.has(blendKey(a, b)) && (rank.get(a) ?? 0) > (rank.get(b) ?? 0)
46
+ };
47
+ }
48
+ /** Simple dominant/recessive — the classic monohybrid (also backs PunnettCross). */
49
+ const monohybridSpec = (letter = "A", dominant = "tall", recessive = "short") => ({
50
+ trait: "trait",
51
+ alleles: [{
52
+ symbol: letter.toUpperCase(),
53
+ rank: 2,
54
+ trait: dominant
55
+ }, {
56
+ symbol: letter.toLowerCase(),
57
+ rank: 1,
58
+ trait: recessive
59
+ }],
60
+ colors: {
61
+ [dominant]: "var(--stage-accent)",
62
+ [recessive]: "var(--stage-muted)"
63
+ }
64
+ });
65
+ /** ABO blood groups — multiple alleles + codominance (Aᴬ Aᴮ → AB). */
66
+ const BLOOD_TYPE_SPEC = {
67
+ trait: "blood type",
68
+ alleles: [
69
+ {
70
+ symbol: "A",
71
+ rank: 2,
72
+ trait: "A"
73
+ },
74
+ {
75
+ symbol: "B",
76
+ rank: 2,
77
+ trait: "B"
78
+ },
79
+ {
80
+ symbol: "O",
81
+ rank: 1,
82
+ trait: "O"
83
+ }
84
+ ],
85
+ colors: {
86
+ A: "var(--stage-accent)",
87
+ B: "var(--stage-accent-2)",
88
+ AB: "var(--stage-good)",
89
+ O: "var(--stage-muted)"
90
+ }
91
+ };
92
+ /** Incomplete dominance — red × white = pink (a blended heterozygote). */
93
+ const INCOMPLETE_SPEC = {
94
+ trait: "flower colour",
95
+ alleles: [{
96
+ symbol: "R",
97
+ rank: 1,
98
+ trait: "red"
99
+ }, {
100
+ symbol: "W",
101
+ rank: 1,
102
+ trait: "white"
103
+ }],
104
+ blends: [{
105
+ pair: ["R", "W"],
106
+ label: "pink",
107
+ color: "var(--stage-accent)"
108
+ }],
109
+ colors: {
110
+ red: "var(--stage-danger)",
111
+ white: "color-mix(in oklab, var(--stage-fg) 82%, var(--stage-bg))"
112
+ }
113
+ };
114
+ const CROSS_PRESETS = {
115
+ monohybrid: monohybridSpec(),
116
+ "blood-type": BLOOD_TYPE_SPEC,
117
+ incomplete: INCOMPLETE_SPEC
118
+ };
119
+ /** Two independent genes — the classic dihybrid 9:3:3:1 (height × seed colour). */
120
+ const DIHYBRID_LOCI = [monohybridSpec("A", "tall", "short"), monohybridSpec("B", "yellow", "green")];
121
+ /** every distinct phenotype label a single-locus model can show (stable order). */
122
+ function phenotypeLabels(m) {
123
+ const seen = [];
124
+ for (const [a, b] of m.genotypes) {
125
+ const l = m.phenotypeOf(a, b).label;
126
+ if (!seen.includes(l)) seen.push(l);
127
+ }
128
+ return seen;
129
+ }
130
+ const COMBO_PALETTE = [
131
+ "var(--stage-good)",
132
+ "var(--stage-accent)",
133
+ "var(--stage-accent-2)",
134
+ "var(--stage-warn)",
135
+ "var(--stage-danger)",
136
+ "var(--stage-muted)"
137
+ ];
138
+ /** stable colour map for the combined phenotype labels of a multi-locus cross. */
139
+ function comboColorMap(models) {
140
+ const perLocus = models.map(phenotypeLabels);
141
+ let combos = [""];
142
+ for (const labels of perLocus) combos = combos.flatMap((c) => labels.map((l) => c ? `${c} ${l}` : l));
143
+ return new Map(combos.map((c, i) => [c, COMBO_PALETTE[i % COMBO_PALETTE.length]]));
144
+ }
145
+
146
+ //#endregion
147
+ export { BLOOD_TYPE_SPEC, CROSS_PRESETS, DIHYBRID_LOCI, INCOMPLETE_SPEC, comboColorMap, monohybridSpec, resolveModel };