@classytic/stage 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 (86) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/kit/glyphs.d.mts +5 -5
  3. package/dist/assets/kit/glyphs.mjs +4 -4
  4. package/dist/builder/SceneBuilder.mjs +4 -4
  5. package/dist/builder/editor.d.mts +2 -1
  6. package/dist/builder/editor.mjs +14 -4
  7. package/dist/builder/tools.d.mts +1 -1
  8. package/dist/builder/tools.mjs +1 -1
  9. package/dist/chem/index.d.mts +5 -5
  10. package/dist/chem/index.mjs +4 -4
  11. package/dist/circuit/index.d.mts +92 -0
  12. package/dist/circuit/index.mjs +333 -0
  13. package/dist/core/clock.d.mts +1 -1
  14. package/dist/core/clock.mjs +3 -3
  15. package/dist/core/control.d.mts +1 -1
  16. package/dist/core/control.mjs +2 -2
  17. package/dist/core/coords.d.mts +4 -4
  18. package/dist/core/coords.mjs +1 -1
  19. package/dist/core/learner.d.mts +1 -1
  20. package/dist/core/learner.mjs +1 -1
  21. package/dist/core/motion.d.mts +6 -6
  22. package/dist/core/motion.mjs +6 -6
  23. package/dist/core/richText.d.mts +2 -2
  24. package/dist/core/vec.d.mts +1 -1
  25. package/dist/field/index.d.mts +11 -3
  26. package/dist/field/index.mjs +20 -3
  27. package/dist/index.d.mts +3 -1
  28. package/dist/index.mjs +3 -1
  29. package/dist/interaction/MovableDot.d.mts +17 -1
  30. package/dist/interaction/MovableDot.mjs +141 -43
  31. package/dist/interaction/index.d.mts +2 -1
  32. package/dist/interaction/index.mjs +2 -1
  33. package/dist/interaction/useDraggable.d.mts +1 -1
  34. package/dist/interaction/useDraggable.mjs +31 -1
  35. package/dist/interaction/usePressSpring.d.mts +24 -0
  36. package/dist/interaction/usePressSpring.mjs +67 -0
  37. package/dist/logic/ast.d.mts +1 -1
  38. package/dist/logic/index.mjs +8 -3
  39. package/dist/logic/minimize.d.mts +2 -2
  40. package/dist/logic/minimize.mjs +56 -18
  41. package/dist/logic/table.mjs +1 -1
  42. package/dist/math/ast.d.mts +1 -1
  43. package/dist/math/ast.mjs +1 -1
  44. package/dist/math/calculus.mjs +68 -9
  45. package/dist/math/compile.d.mts +8 -0
  46. package/dist/math/compile.mjs +40 -0
  47. package/dist/math/defs.mjs +1 -1
  48. package/dist/math/index.d.mts +4 -3
  49. package/dist/math/index.mjs +4 -3
  50. package/dist/math/parse.mjs +2 -2
  51. package/dist/math/tokenize.mjs +1 -1
  52. package/dist/primitives/CanvasLayer.mjs +3 -3
  53. package/dist/primitives/Grid.d.mts +12 -1
  54. package/dist/primitives/Grid.mjs +15 -9
  55. package/dist/primitives/Label.d.mts +1 -1
  56. package/dist/primitives/Label.mjs +1 -1
  57. package/dist/primitives/Tex.mjs +2 -2
  58. package/dist/scene/Scene.mjs +1 -1
  59. package/dist/scene/assets.d.mts +2 -2
  60. package/dist/scene/assets.mjs +2 -2
  61. package/dist/scene/commands.d.mts +10 -1
  62. package/dist/scene/commands.mjs +42 -2
  63. package/dist/scene/evaluators.mjs +2 -2
  64. package/dist/scene/index.d.mts +2 -2
  65. package/dist/scene/index.mjs +2 -2
  66. package/dist/scene/migrate.mjs +1 -1
  67. package/dist/scene/sims.mjs +1 -1
  68. package/dist/scene/types.d.mts +4 -4
  69. package/dist/sim/particles.d.mts +2 -2
  70. package/dist/sim/rate.d.mts +4 -4
  71. package/dist/sim/rate.mjs +1 -1
  72. package/dist/sim/registry.d.mts +1 -1
  73. package/dist/sim/sampler.d.mts +5 -5
  74. package/dist/sim/thermal.d.mts +3 -3
  75. package/dist/sim/thermal.mjs +2 -2
  76. package/dist/sim/types.d.mts +2 -2
  77. package/dist/sim/wave.d.mts +2 -2
  78. package/dist/sim/wave.mjs +2 -2
  79. package/dist/steps/index.d.mts +1 -1
  80. package/dist/steps/index.mjs +3 -3
  81. package/dist/thermo/index.d.mts +1 -1
  82. package/dist/thermo/index.mjs +1 -1
  83. package/dist/view/Stage.mjs +2 -2
  84. package/dist/view/useInView.mjs +1 -1
  85. package/package.json +8 -4
  86. package/styles.css +23 -0
@@ -28,14 +28,14 @@ interface ParticlesState {
28
28
  py: number[];
29
29
  vx: number[];
30
30
  vy: number[];
31
- /** 0 or 1 two populations (for diffusion / colour). */
31
+ /** 0 or 1, two populations (for diffusion / colour). */
32
32
  group: number[];
33
33
  tSec: number;
34
34
  /** Mean kinetic energy ∝ temperature. */
35
35
  temp: number;
36
36
  /** Smoothed pressure (wall-collision momentum per unit time per unit perimeter). */
37
37
  pressure: number;
38
- /** Mixing index 0 (separated) → 1 (uniform) fraction-based, for diffusion. */
38
+ /** Mixing index 0 (separated) → 1 (uniform), fraction-based, for diffusion. */
39
39
  mixed: number;
40
40
  }
41
41
  declare const ParticlesCore: SimCore<ParticlesState, ParticlesParams>;
@@ -8,7 +8,7 @@ interface RateParams {
8
8
  value0?: number;
9
9
  /** Asymptote the value relaxes toward. Default 0 (pure decay). */
10
10
  target?: number;
11
- /** Time constant τ (seconds) the e-folding time. Default 1. */
11
+ /** Time constant τ (seconds), the e-folding time. Default 1. */
12
12
  tau?: number;
13
13
  /** Stop after this many seconds (essentially relaxed) so it doesn't run forever. Default ∞. */
14
14
  maxTime?: number;
@@ -21,15 +21,15 @@ interface RateState {
21
21
  tau: number;
22
22
  /** Seconds elapsed on this core's timeline. */
23
23
  tSec: number;
24
- /** Rolling history of `value`, oldest-first for the curve. */
24
+ /** Rolling history of `value`, oldest-first, for the curve. */
25
25
  samples: number[];
26
26
  cap: number;
27
27
  maxTime: number;
28
- /** True once tSec ≥ maxTime the runtime stops the loop. */
28
+ /** True once tSec ≥ maxTime, the runtime stops the loop. */
29
29
  done: boolean;
30
30
  }
31
31
  declare const RateCore: SimCore<RateState, RateParams>;
32
- /** τ from a half-life (and back) t½ = τ·ln2. */
32
+ /** τ from a half-life (and back), t½ = τ·ln2. */
33
33
  declare const halfLifeToTau: (halfLife: number) => number;
34
34
  declare const tauToHalfLife: (tau: number) => number;
35
35
  //#endregion
package/dist/sim/rate.mjs CHANGED
@@ -31,7 +31,7 @@ const RateCore = {
31
31
  };
32
32
  }
33
33
  };
34
- /** τ from a half-life (and back) t½ = τ·ln2. */
34
+ /** τ from a half-life (and back), t½ = τ·ln2. */
35
35
  const halfLifeToTau = (halfLife) => halfLife / Math.LN2;
36
36
  const tauToHalfLife = (tau) => tau * Math.LN2;
37
37
 
@@ -1,7 +1,7 @@
1
1
  import { SimCore } from "./types.mjs";
2
2
 
3
3
  //#region src/sim/registry.d.ts
4
- /** A core with its specifics erased the runtime reads state fields by name. */
4
+ /** A core with its specifics erased, the runtime reads state fields by name. */
5
5
  type AnySim = SimCore<Record<string, unknown>, Record<string, unknown>>;
6
6
  declare function registerSim<S extends Record<string, unknown>, P>(name: string, core: SimCore<S, P>): void;
7
7
  declare function getSim(name: string): AnySim | undefined;
@@ -8,7 +8,7 @@ interface SamplerParams {
8
8
  perStep?: number;
9
9
  /** PRNG seed. Default 1. */
10
10
  seed?: number;
11
- /** Stop drawing once `n` reaches this a converged sim shouldn't run forever. Default ∞. */
11
+ /** Stop drawing once `n` reaches this, a converged sim shouldn't run forever. Default ∞. */
12
12
  maxDraws?: number;
13
13
  /** History ring-buffer capacity for the headline estimate p[0]. Default 240. */
14
14
  trace?: number;
@@ -20,7 +20,7 @@ interface SamplerState {
20
20
  * Current PRNG state (advances every draw). Deliberately NOT named `seed`: the
21
21
  * Scene runtime merges the SimDecl `params` (which carries the initial `seed`)
22
22
  * onto the state each frame, so an evolving field sharing a param name would be
23
- * clobbered back to its initial value freezing the RNG. Keep evolving state
23
+ * clobbered back to its initial value, freezing the RNG. Keep evolving state
24
24
  * fields disjoint from param names.
25
25
  */
26
26
  rng: number;
@@ -30,16 +30,16 @@ interface SamplerState {
30
30
  counts: number[];
31
31
  /** Running frequency estimate per outcome (counts / n). */
32
32
  p: number[];
33
- /** True (normalized) probabilities for convergence comparison. */
33
+ /** True (normalized) probabilities, for convergence comparison. */
34
34
  p0: number[];
35
35
  /** Index of the most recent draw (-1 before the first). */
36
36
  last: number;
37
- /** Rolling history of p[0], oldest-first for an LLN convergence plot. */
37
+ /** Rolling history of p[0], oldest-first, for an LLN convergence plot. */
38
38
  samples: number[];
39
39
  cap: number;
40
40
  /** Draw cap; step() becomes a no-op once n reaches it. */
41
41
  maxDraws: number;
42
- /** True once n ≥ maxDraws the runtime stops the loop. */
42
+ /** True once n ≥ maxDraws, the runtime stops the loop. */
43
43
  done: boolean;
44
44
  }
45
45
  declare const SamplerCore: SimCore<SamplerState, SamplerParams>;
@@ -21,10 +21,10 @@ interface Substance {
21
21
  /** Liquid colour token (for the skin). */
22
22
  color: string;
23
23
  }
24
- /** Real water the canonical example (note: latent vaporisation ≫ everything,
24
+ /** Real water, the canonical example (note: latent vaporisation ≫ everything,
25
25
  * so the boiling plateau dwarfs the rest, which is exactly the lesson). */
26
26
  declare const WATER: Substance;
27
- /** Ethanol lower boiling point, smaller latent heats; a contrasting curve. */
27
+ /** Ethanol, lower boiling point, smaller latent heats; a contrasting curve. */
28
28
  declare const ETHANOL: Substance;
29
29
  type Phase = 'solid' | 'melting' | 'liquid' | 'boiling' | 'gas';
30
30
  /** One run of the energy ladder. `kind:'heat'` is a sloped temperature rise
@@ -58,7 +58,7 @@ interface ThermalState {
58
58
  powerW: number;
59
59
  tStart: number;
60
60
  tMax: number;
61
- /** The full energy ladder the heating curve, as data. */
61
+ /** The full energy ladder, the heating curve, as data. */
62
62
  segs: ThermalSeg[];
63
63
  /** Total heat to walk the whole ladder, J. */
64
64
  totalJ: number;
@@ -1,6 +1,6 @@
1
1
  //#region src/sim/thermal.ts
2
2
  const clamp01 = (v) => Math.max(0, Math.min(1, v));
3
- /** Real water the canonical example (note: latent vaporisation ≫ everything,
3
+ /** Real water, the canonical example (note: latent vaporisation ≫ everything,
4
4
  * so the boiling plateau dwarfs the rest, which is exactly the lesson). */
5
5
  const WATER = {
6
6
  name: "Water",
@@ -16,7 +16,7 @@ const WATER = {
16
16
  tBoil: 100,
17
17
  color: "var(--stage-accent, #3b82f6)"
18
18
  };
19
- /** Ethanol lower boiling point, smaller latent heats; a contrasting curve. */
19
+ /** Ethanol, lower boiling point, smaller latent heats; a contrasting curve. */
20
20
  const ETHANOL = {
21
21
  name: "Ethanol",
22
22
  solidName: "solid",
@@ -1,12 +1,12 @@
1
1
  //#region src/sim/types.d.ts
2
2
  /**
3
- * Sim cores the "physics engine" half of a lab: pure, time-stepped simulators
3
+ * Sim cores, the "physics engine" half of a lab: pure, time-stepped simulators
4
4
  * with NO React and NO pixels. A core is `reset(params) → state`, then
5
5
  * `step(state, dt) → state` once per frame. Because a core is a pure function of
6
6
  * (params, elapsed time), a lab built on one is deterministic: it replays
7
7
  * identically under a Remotion fixed-frame clock (→ video) and is unit-testable
8
8
  * with no DOM. Rendering (glyphs) READS the returned state; drawing never lives
9
- * in here. Live controls write their target fields onto `state` between steps
9
+ * in here. Live controls write their target fields onto `state` between steps ,
10
10
  * so a core's mutable inputs (e.g. `volts`, `mode`) live IN its state, which is
11
11
  * also why those fields are readable by name from a binding.
12
12
  */
@@ -22,9 +22,9 @@ interface WaveState {
22
22
  tSec: number;
23
23
  /** Instantaneous signed voltage; sign = current direction. */
24
24
  v: number;
25
- /** ∫v·dt accumulated charge displacement; what flow position (electrons / water) reads. */
25
+ /** ∫v·dt, accumulated charge displacement; what flow position (electrons / water) reads. */
26
26
  charge: number;
27
- /** Rolling history of `v`, oldest-first, length ≤ `cap` for the live trace. */
27
+ /** Rolling history of `v`, oldest-first, length ≤ `cap`, for the live trace. */
28
28
  samples: number[];
29
29
  /** Fixed capacity of `samples`. */
30
30
  cap: number;
package/dist/sim/wave.mjs CHANGED
@@ -2,9 +2,9 @@ import { oscillate } from "../core/motion.mjs";
2
2
 
3
3
  //#region src/sim/wave.ts
4
4
  /**
5
- * `wave` the AC/DC signal source. One core drives a whole scene: a DC source
5
+ * `wave`, the AC/DC signal source. One core drives a whole scene: a DC source
6
6
  * holds a steady voltage; an AC source oscillates. Either way it exposes the
7
- * instantaneous voltage `v` (signed sign IS current direction) plus a rolling
7
+ * instantaneous voltage `v` (signed, sign IS current direction) plus a rolling
8
8
  * `samples` buffer for a live scrolling trace. The SAME `v` can skin many ways at
9
9
  * once (a lamp's brightness, electrons in a wire, water sloshing in a pipe), which
10
10
  * is the point: sim ≠ render, so one signal feeds every visual metaphor in sync.
@@ -1,7 +1,7 @@
1
1
  import { ReactNode } from "react";
2
2
 
3
3
  //#region src/steps/index.d.ts
4
- /** One step in the sequence identity + gating only. Its TITLE, PROSE, and the
4
+ /** One step in the sequence, identity + gating only. Its TITLE, PROSE, and the
5
5
  * interactive itself live in the authored Section block, not here. */
6
6
  interface StepSpec {
7
7
  id: string;
@@ -5,14 +5,14 @@ import { createContext, createElement, useContext, useMemo, useRef, useSyncExter
5
5
 
6
6
  //#region src/steps/index.ts
7
7
  /**
8
- * Step progress + gating the runtime that turns an ordered sequence of lesson
8
+ * Step progress + gating, the runtime that turns an ordered sequence of lesson
9
9
  * STEPS into "unlock the next when this one is solved" + aggregated completion.
10
10
  *
11
- * ONE SOURCE OF TRUTH for the step sequence is the authored content itself the
11
+ * ONE SOURCE OF TRUTH for the step sequence is the authored content itself, the
12
12
  * @classytic/cms-ui `Section`/`Step` blocks a creator orders in the editor
13
13
  * (serialized in the MDX). This engine deliberately does NOT model an authored
14
14
  * "LessonDoc": that would be a SECOND, competing definition of the same steps
15
- * (and creators never hand-write one they build Section blocks). The host's
15
+ * (and creators never hand-write one, they build Section blocks). The host's
16
16
  * Section player derives a `StepSpec[]` from the section order, wraps each
17
17
  * section's interactive in `<StepScope id>`, and reads `useStepProgress` to gate
18
18
  * reveal. Each lab's `useLearner().report` flows into step completion AND
@@ -1,6 +1,6 @@
1
1
  //#region src/thermo/index.d.ts
2
2
  /**
3
- * `thermo` ideal-gas thermodynamic processes, as a PURE kernel (like `field`:
3
+ * `thermo`, ideal-gas thermodynamic processes, as a PURE kernel (like `field`:
4
4
  * no React, no pixels, no time-stepping). A lab drives it with a slider/progress
5
5
  * fraction; the kernel returns the full P–V path of a quasi-static process plus the
6
6
  * first-law bookkeeping along it (work, heat, internal energy, entropy).
@@ -1,6 +1,6 @@
1
1
  //#region src/thermo/index.ts
2
2
  /**
3
- * `thermo` ideal-gas thermodynamic processes, as a PURE kernel (like `field`:
3
+ * `thermo`, ideal-gas thermodynamic processes, as a PURE kernel (like `field`:
4
4
  * no React, no pixels, no time-stepping). A lab drives it with a slider/progress
5
5
  * fraction; the kernel returns the full P–V path of a quasi-static process plus the
6
6
  * first-law bookkeeping along it (work, heat, internal energy, entropy).
@@ -8,10 +8,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/view/Stage.tsx
10
10
  /**
11
- * <Stage> the SVG root. SSR-safe: builds a CoordinateSystem from explicit
11
+ * <Stage>, the SVG root. SSR-safe: builds a CoordinateSystem from explicit
12
12
  * width/height/aspectRatio (or a default) so children render on first paint;
13
13
  * after mount a ResizeObserver swaps in the measured width. There is ONE content
14
- * group with no transform every primitive self-positions via coords.toPx().
14
+ * group with no transform, every primitive self-positions via coords.toPx().
15
15
  * A pixel-space overlay group hosts selection halos/handles (builder phase).
16
16
  */
17
17
  const DEFAULT_VIEW = {
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from "react";
4
4
 
5
5
  //#region src/view/useInView.ts
6
6
  /**
7
- * useInView pause work when a lab scrolls off-screen. Sim labs drive an
7
+ * useInView, pause work when a lab scrolls off-screen. Sim labs drive an
8
8
  * unbounded requestAnimationFrame loop; without this, every sim on a page runs
9
9
  * forever even when nobody can see it (and a gallery of labs pins the CPU). Gate
10
10
  * `useFrameLoop({ running: inView })` on this. SSR-safe; if IntersectionObserver
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/stage",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Domain-neutral visual primitive engine for the web. Declarative SVG diagram/math primitives, draggable handles, a math↔pixel coordinate system, a motion core + energy effect defs, theming, pure sim cores, and a small kit of neutral glyph primitives. Reactive, web-native, agent-drivable. The render foundation under @classytic/labs (subject-matter glyphs live there); canvas + 3D subpaths later.",
5
5
  "author": "Classytic",
6
6
  "license": "MIT",
@@ -60,6 +60,10 @@
60
60
  "types": "./dist/field/index.d.mts",
61
61
  "default": "./dist/field/index.mjs"
62
62
  },
63
+ "./circuit": {
64
+ "types": "./dist/circuit/index.d.mts",
65
+ "default": "./dist/circuit/index.mjs"
66
+ },
63
67
  "./thermo": {
64
68
  "types": "./dist/thermo/index.d.mts",
65
69
  "default": "./dist/thermo/index.mjs"
@@ -68,9 +72,9 @@
68
72
  "types": "./dist/chem/index.d.mts",
69
73
  "default": "./dist/chem/index.mjs"
70
74
  },
71
- "./lesson": {
72
- "types": "./dist/lesson/index.d.mts",
73
- "default": "./dist/lesson/index.mjs"
75
+ "./steps": {
76
+ "types": "./dist/steps/index.d.mts",
77
+ "default": "./dist/steps/index.mjs"
74
78
  },
75
79
  "./styles.css": "./styles.css",
76
80
  "./package.json": "./package.json"
package/styles.css CHANGED
@@ -49,6 +49,19 @@
49
49
  --stage-wire: var(--stage-metal);
50
50
  --stage-live: var(--stage-good);
51
51
  --stage-charge: var(--stage-accent);
52
+
53
+ /* Categorical series palette: 8 distinct, theme-aware hues for group / series
54
+ colours (K-map loops, distributions, selection groups, arrangement letters).
55
+ Labs import these via kit/palette instead of hardcoding hex arrays that
56
+ ignore dark mode. cat-1/2/3 align with accent / accent-2 / good. */
57
+ --stage-cat-1: oklch(0.59 0.20 261);
58
+ --stage-cat-2: oklch(0.63 0.17 45);
59
+ --stage-cat-3: oklch(0.58 0.16 150);
60
+ --stage-cat-4: oklch(0.55 0.20 300);
61
+ --stage-cat-5: oklch(0.62 0.12 200);
62
+ --stage-cat-6: oklch(0.60 0.21 18);
63
+ --stage-cat-7: oklch(0.70 0.15 92);
64
+ --stage-cat-8: oklch(0.60 0.20 332);
52
65
  }
53
66
 
54
67
  .dark {
@@ -67,4 +80,14 @@
67
80
  --stage-pan: oklch(0.83 0.02 255);
68
81
  --stage-weight-2: var(--brand-2, oklch(0.74 0.15 50));
69
82
  --stage-liquid: oklch(0.7 0.13 220 / 0.6);
83
+
84
+ /* Brighter categorical hues for the dark card surface. */
85
+ --stage-cat-1: oklch(0.72 0.17 261);
86
+ --stage-cat-2: oklch(0.78 0.15 52);
87
+ --stage-cat-3: oklch(0.72 0.17 150);
88
+ --stage-cat-4: oklch(0.71 0.18 300);
89
+ --stage-cat-5: oklch(0.76 0.12 200);
90
+ --stage-cat-6: oklch(0.73 0.18 22);
91
+ --stage-cat-7: oklch(0.83 0.16 92);
92
+ --stage-cat-8: oklch(0.75 0.18 332);
70
93
  }