@classytic/stage 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 (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/dist/assets/index.d.mts +3 -0
  4. package/dist/assets/index.mjs +5 -0
  5. package/dist/assets/kit/glyphs.d.mts +61 -0
  6. package/dist/assets/kit/glyphs.mjs +256 -0
  7. package/dist/assets/kit/index.d.mts +2 -0
  8. package/dist/assets/kit/index.mjs +4 -0
  9. package/dist/assets/kit/svg-defs.d.mts +6 -0
  10. package/dist/assets/kit/svg-defs.mjs +137 -0
  11. package/dist/builder/Palette.d.mts +30 -0
  12. package/dist/builder/Palette.mjs +100 -0
  13. package/dist/builder/SceneBuilder.d.mts +26 -0
  14. package/dist/builder/SceneBuilder.mjs +340 -0
  15. package/dist/builder/controls.d.mts +18 -0
  16. package/dist/builder/controls.mjs +29 -0
  17. package/dist/builder/editor.d.mts +46 -0
  18. package/dist/builder/editor.mjs +130 -0
  19. package/dist/builder/icons.d.mts +14 -0
  20. package/dist/builder/icons.mjs +143 -0
  21. package/dist/builder/index.d.mts +6 -0
  22. package/dist/builder/index.mjs +7 -0
  23. package/dist/builder/tools.d.mts +47 -0
  24. package/dist/builder/tools.mjs +303 -0
  25. package/dist/chem/index.d.mts +118 -0
  26. package/dist/chem/index.mjs +131 -0
  27. package/dist/core/clock.d.mts +41 -0
  28. package/dist/core/clock.mjs +62 -0
  29. package/dist/core/context.d.mts +13 -0
  30. package/dist/core/context.mjs +29 -0
  31. package/dist/core/control.d.mts +58 -0
  32. package/dist/core/control.mjs +113 -0
  33. package/dist/core/coords.d.mts +51 -0
  34. package/dist/core/coords.mjs +44 -0
  35. package/dist/core/index.d.mts +8 -0
  36. package/dist/core/index.mjs +9 -0
  37. package/dist/core/learner.d.mts +44 -0
  38. package/dist/core/learner.mjs +23 -0
  39. package/dist/core/motion.d.mts +73 -0
  40. package/dist/core/motion.mjs +103 -0
  41. package/dist/core/richText.d.mts +28 -0
  42. package/dist/core/richText.mjs +52 -0
  43. package/dist/core/vec.d.mts +47 -0
  44. package/dist/core/vec.mjs +144 -0
  45. package/dist/field/index.d.mts +58 -0
  46. package/dist/field/index.mjs +156 -0
  47. package/dist/index.d.mts +54 -0
  48. package/dist/index.mjs +55 -0
  49. package/dist/interaction/MovableDot.d.mts +33 -0
  50. package/dist/interaction/MovableDot.mjs +66 -0
  51. package/dist/interaction/hitTest.d.mts +16 -0
  52. package/dist/interaction/hitTest.mjs +39 -0
  53. package/dist/interaction/index.d.mts +5 -0
  54. package/dist/interaction/index.mjs +6 -0
  55. package/dist/interaction/useDraggable.d.mts +51 -0
  56. package/dist/interaction/useDraggable.mjs +116 -0
  57. package/dist/interaction/useScreenToMath.d.mts +12 -0
  58. package/dist/interaction/useScreenToMath.mjs +25 -0
  59. package/dist/logic/ast.d.mts +29 -0
  60. package/dist/logic/ast.mjs +37 -0
  61. package/dist/logic/index.d.mts +25 -0
  62. package/dist/logic/index.mjs +36 -0
  63. package/dist/logic/latex.d.mts +6 -0
  64. package/dist/logic/latex.mjs +33 -0
  65. package/dist/logic/minimize.d.mts +37 -0
  66. package/dist/logic/minimize.mjs +175 -0
  67. package/dist/logic/parse.d.mts +6 -0
  68. package/dist/logic/parse.mjs +68 -0
  69. package/dist/logic/table.d.mts +24 -0
  70. package/dist/logic/table.mjs +65 -0
  71. package/dist/logic/tokenize.mjs +258 -0
  72. package/dist/math/ast.d.mts +35 -0
  73. package/dist/math/ast.mjs +94 -0
  74. package/dist/math/calculus.d.mts +9 -0
  75. package/dist/math/calculus.mjs +188 -0
  76. package/dist/math/defs.mjs +51 -0
  77. package/dist/math/index.d.mts +26 -0
  78. package/dist/math/index.mjs +42 -0
  79. package/dist/math/latex.d.mts +7 -0
  80. package/dist/math/latex.mjs +115 -0
  81. package/dist/math/parse.d.mts +6 -0
  82. package/dist/math/parse.mjs +91 -0
  83. package/dist/math/tokenize.mjs +112 -0
  84. package/dist/primitives/CanvasLayer.d.mts +26 -0
  85. package/dist/primitives/CanvasLayer.mjs +87 -0
  86. package/dist/primitives/Dot.d.mts +27 -0
  87. package/dist/primitives/Dot.mjs +40 -0
  88. package/dist/primitives/Grid.d.mts +30 -0
  89. package/dist/primitives/Grid.mjs +122 -0
  90. package/dist/primitives/Label.d.mts +33 -0
  91. package/dist/primitives/Label.mjs +45 -0
  92. package/dist/primitives/Lines.d.mts +50 -0
  93. package/dist/primitives/Lines.mjs +82 -0
  94. package/dist/primitives/Plot.d.mts +50 -0
  95. package/dist/primitives/Plot.mjs +87 -0
  96. package/dist/primitives/Shapes.d.mts +76 -0
  97. package/dist/primitives/Shapes.mjs +79 -0
  98. package/dist/primitives/Tex.d.mts +27 -0
  99. package/dist/primitives/Tex.mjs +54 -0
  100. package/dist/primitives/index.d.mts +10 -0
  101. package/dist/primitives/index.mjs +10 -0
  102. package/dist/primitives/props.d.mts +15 -0
  103. package/dist/scene/Scene.d.mts +31 -0
  104. package/dist/scene/Scene.mjs +168 -0
  105. package/dist/scene/assets.d.mts +37 -0
  106. package/dist/scene/assets.mjs +31 -0
  107. package/dist/scene/commands.d.mts +47 -0
  108. package/dist/scene/commands.mjs +162 -0
  109. package/dist/scene/evaluators.d.mts +17 -0
  110. package/dist/scene/evaluators.mjs +210 -0
  111. package/dist/scene/index.d.mts +13 -0
  112. package/dist/scene/index.mjs +13 -0
  113. package/dist/scene/migrate.d.mts +11 -0
  114. package/dist/scene/migrate.mjs +35 -0
  115. package/dist/scene/render.d.mts +14 -0
  116. package/dist/scene/render.mjs +126 -0
  117. package/dist/scene/resolve.d.mts +12 -0
  118. package/dist/scene/resolve.mjs +95 -0
  119. package/dist/scene/schema.d.mts +9 -0
  120. package/dist/scene/schema.mjs +51 -0
  121. package/dist/scene/sims.d.mts +18 -0
  122. package/dist/scene/sims.mjs +54 -0
  123. package/dist/scene/store.d.mts +22 -0
  124. package/dist/scene/store.mjs +37 -0
  125. package/dist/scene/types.d.mts +228 -0
  126. package/dist/scene/types.mjs +27 -0
  127. package/dist/sim/equilibrium.d.mts +36 -0
  128. package/dist/sim/equilibrium.mjs +54 -0
  129. package/dist/sim/index.d.mts +9 -0
  130. package/dist/sim/index.mjs +9 -0
  131. package/dist/sim/particles.d.mts +43 -0
  132. package/dist/sim/particles.mjs +136 -0
  133. package/dist/sim/rate.d.mts +36 -0
  134. package/dist/sim/rate.mjs +39 -0
  135. package/dist/sim/registry.d.mts +10 -0
  136. package/dist/sim/registry.mjs +27 -0
  137. package/dist/sim/sampler.d.mts +47 -0
  138. package/dist/sim/sampler.mjs +79 -0
  139. package/dist/sim/thermal.d.mts +78 -0
  140. package/dist/sim/thermal.mjs +172 -0
  141. package/dist/sim/types.d.mts +24 -0
  142. package/dist/sim/wave.d.mts +34 -0
  143. package/dist/sim/wave.mjs +61 -0
  144. package/dist/steps/index.d.mts +62 -0
  145. package/dist/steps/index.mjs +133 -0
  146. package/dist/thermo/index.d.mts +82 -0
  147. package/dist/thermo/index.mjs +133 -0
  148. package/dist/view/Stage.d.mts +39 -0
  149. package/dist/view/Stage.mjs +91 -0
  150. package/dist/view/index.d.mts +4 -0
  151. package/dist/view/index.mjs +5 -0
  152. package/dist/view/useElementSize.d.mts +13 -0
  153. package/dist/view/useElementSize.mjs +40 -0
  154. package/dist/view/useInView.d.mts +9 -0
  155. package/dist/view/useInView.mjs +33 -0
  156. package/package.json +141 -0
  157. package/styles.css +70 -0
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+
5
+ //#region src/view/useElementSize.ts
6
+ /**
7
+ * SSR-safe size observer. Returns `null` before mount so callers fall back to
8
+ * explicit/default dimensions and render REAL content on first paint (no
9
+ * blank-until-JS). After mount, a ResizeObserver reports the measured box.
10
+ */
11
+ function useElementSize() {
12
+ const ref = useRef(null);
13
+ const [size, setSize] = useState(null);
14
+ useEffect(() => {
15
+ const el = ref.current;
16
+ if (!el || typeof ResizeObserver === "undefined") return;
17
+ const ro = new ResizeObserver((entries) => {
18
+ const e = entries[0];
19
+ if (!e) return;
20
+ setSize({
21
+ width: e.contentRect.width,
22
+ height: e.contentRect.height
23
+ });
24
+ });
25
+ ro.observe(el);
26
+ const r = el.getBoundingClientRect();
27
+ if (r.width > 0) setSize({
28
+ width: r.width,
29
+ height: r.height
30
+ });
31
+ return () => ro.disconnect();
32
+ }, []);
33
+ return {
34
+ ref,
35
+ size
36
+ };
37
+ }
38
+
39
+ //#endregion
40
+ export { useElementSize };
@@ -0,0 +1,9 @@
1
+ import { RefObject } from "react";
2
+
3
+ //#region src/view/useInView.d.ts
4
+ declare function useInView<T extends Element = HTMLDivElement>(rootMargin?: string): {
5
+ ref: RefObject<T | null>;
6
+ inView: boolean;
7
+ };
8
+ //#endregion
9
+ export { useInView };
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+
5
+ //#region src/view/useInView.ts
6
+ /**
7
+ * useInView — pause work when a lab scrolls off-screen. Sim labs drive an
8
+ * unbounded requestAnimationFrame loop; without this, every sim on a page runs
9
+ * forever even when nobody can see it (and a gallery of labs pins the CPU). Gate
10
+ * `useFrameLoop({ running: inView })` on this. SSR-safe; if IntersectionObserver
11
+ * is unavailable it assumes visible (never silently freezes a lab).
12
+ */
13
+ function useInView(rootMargin = "160px") {
14
+ const ref = useRef(null);
15
+ const [inView, setInView] = useState(false);
16
+ useEffect(() => {
17
+ const el = ref.current;
18
+ if (!el || typeof IntersectionObserver === "undefined") {
19
+ setInView(true);
20
+ return;
21
+ }
22
+ const io = new IntersectionObserver((entries) => setInView(entries.some((e) => e.isIntersecting)), { rootMargin });
23
+ io.observe(el);
24
+ return () => io.disconnect();
25
+ }, [rootMargin]);
26
+ return {
27
+ ref,
28
+ inView
29
+ };
30
+ }
31
+
32
+ //#endregion
33
+ export { useInView };
package/package.json ADDED
@@ -0,0 +1,141 @@
1
+ {
2
+ "name": "@classytic/stage",
3
+ "version": "0.1.0",
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
+ "author": "Classytic",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "sideEffects": [
9
+ "**/*.css",
10
+ "**/assets/**"
11
+ ],
12
+ "engines": {
13
+ "node": ">=22"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.mts",
21
+ "default": "./dist/index.mjs"
22
+ },
23
+ "./core": {
24
+ "types": "./dist/core/index.d.mts",
25
+ "default": "./dist/core/index.mjs"
26
+ },
27
+ "./primitives": {
28
+ "types": "./dist/primitives/index.d.mts",
29
+ "default": "./dist/primitives/index.mjs"
30
+ },
31
+ "./interaction": {
32
+ "types": "./dist/interaction/index.d.mts",
33
+ "default": "./dist/interaction/index.mjs"
34
+ },
35
+ "./view": {
36
+ "types": "./dist/view/index.d.mts",
37
+ "default": "./dist/view/index.mjs"
38
+ },
39
+ "./scene": {
40
+ "types": "./dist/scene/index.d.mts",
41
+ "default": "./dist/scene/index.mjs"
42
+ },
43
+ "./builder": {
44
+ "types": "./dist/builder/index.d.mts",
45
+ "default": "./dist/builder/index.mjs"
46
+ },
47
+ "./assets": {
48
+ "types": "./dist/assets/index.d.mts",
49
+ "default": "./dist/assets/index.mjs"
50
+ },
51
+ "./math": {
52
+ "types": "./dist/math/index.d.mts",
53
+ "default": "./dist/math/index.mjs"
54
+ },
55
+ "./sim": {
56
+ "types": "./dist/sim/index.d.mts",
57
+ "default": "./dist/sim/index.mjs"
58
+ },
59
+ "./field": {
60
+ "types": "./dist/field/index.d.mts",
61
+ "default": "./dist/field/index.mjs"
62
+ },
63
+ "./thermo": {
64
+ "types": "./dist/thermo/index.d.mts",
65
+ "default": "./dist/thermo/index.mjs"
66
+ },
67
+ "./chem": {
68
+ "types": "./dist/chem/index.d.mts",
69
+ "default": "./dist/chem/index.mjs"
70
+ },
71
+ "./lesson": {
72
+ "types": "./dist/lesson/index.d.mts",
73
+ "default": "./dist/lesson/index.mjs"
74
+ },
75
+ "./styles.css": "./styles.css",
76
+ "./package.json": "./package.json"
77
+ },
78
+ "main": "./dist/index.mjs",
79
+ "module": "./dist/index.mjs",
80
+ "types": "./dist/index.d.mts",
81
+ "files": [
82
+ "dist",
83
+ "styles.css",
84
+ "LICENSE",
85
+ "README.md"
86
+ ],
87
+ "scripts": {
88
+ "build": "tsdown",
89
+ "dev": "tsdown --watch",
90
+ "typecheck": "tsc --noEmit",
91
+ "sync": "node scripts/sync-stage.mjs",
92
+ "pretest": "npm run build",
93
+ "test": "vitest run",
94
+ "verify": "npm run typecheck && npm test",
95
+ "push": "classytic-push",
96
+ "changeset": "changeset",
97
+ "version-packages": "changeset version",
98
+ "release": "npm run build && changeset publish",
99
+ "prepublishOnly": "npm run typecheck && npm run build && npm test",
100
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
101
+ },
102
+ "peerDependencies": {
103
+ "katex": ">=0.16",
104
+ "react": ">=19",
105
+ "react-dom": ">=19"
106
+ },
107
+ "peerDependenciesMeta": {
108
+ "katex": {
109
+ "optional": true
110
+ }
111
+ },
112
+ "devDependencies": {
113
+ "@changesets/cli": "^2.27.9",
114
+ "@classytic/dev-tools": "^0.2.0",
115
+ "@testing-library/react": "^16.3.2",
116
+ "@types/katex": "^0.16.7",
117
+ "@types/node": "^22.0.0",
118
+ "@types/react": "^19.2.0",
119
+ "@types/react-dom": "^19.2.0",
120
+ "happy-dom": "^20.0.0",
121
+ "katex": "^0.16.11",
122
+ "react": "^19.2.0",
123
+ "react-dom": "^19.2.0",
124
+ "tsdown": "^0.22.0",
125
+ "typescript": "^5.7.0",
126
+ "vitest": "^4.1.0"
127
+ },
128
+ "keywords": [
129
+ "react",
130
+ "svg",
131
+ "visualization",
132
+ "interactive",
133
+ "geometry",
134
+ "diagram",
135
+ "coordinate-system",
136
+ "draggable",
137
+ "math",
138
+ "engine",
139
+ "primitives"
140
+ ]
141
+ }
package/styles.css ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @classytic/stage — theme tokens.
3
+ *
4
+ * Import once in your app's global CSS:
5
+ * @import "@classytic/stage/styles.css";
6
+ *
7
+ * THEME-AWARE: the drawing surface + neutrals bind to the host's shadcn tokens
8
+ * (`--card`, `--card-foreground`, `--foreground`, `--muted-foreground`), so a
9
+ * stage adopts the app's LIGHT or DARK theme automatically — no fixed dark
10
+ * canvas. Domain colours (metal/pan/sheen/grid) have no shadcn equivalent, so
11
+ * they carry tuned light values in `:root` and brighter dark values in `.dark`
12
+ * (same split globals.css uses). Override any token in your own `:root`/`.dark`
13
+ * to retheme every stage at once. (Each value keeps a literal fallback so a
14
+ * non-shadcn host still renders.)
15
+ */
16
+
17
+ @source "./dist/**/*.mjs";
18
+
19
+ :root {
20
+ /* Surface + neutrals follow the host theme (light here). */
21
+ --stage-bg: var(--card, oklch(1 0 0));
22
+ --stage-fg: var(--card-foreground, oklch(0.20 0.02 260));
23
+ --stage-muted: var(--muted-foreground, oklch(0.55 0.02 260));
24
+ --stage-grid: color-mix(in oklab, var(--foreground, oklch(0.2 0.02 260)) 14%, transparent);
25
+ --stage-axis: color-mix(in oklab, var(--foreground, oklch(0.2 0.02 260)) 42%, transparent);
26
+
27
+ /* Accents — host brand where one exists; the rest tuned to read on a LIGHT
28
+ surface (lower lightness / more chroma than the dark-mode values below). */
29
+ --stage-accent: var(--primary, oklch(0.59 0.22 261));
30
+ --stage-accent-2: var(--brand-2, oklch(0.62 0.18 48));
31
+ --stage-good: var(--success, oklch(0.58 0.16 150));
32
+ --stage-warn: var(--warning, oklch(0.68 0.15 72));
33
+ --stage-danger: var(--destructive, oklch(0.62 0.24 25));
34
+
35
+ /* Asset kit. metal/pan are domain greys → a mid grey that reads on white.
36
+ The unknown (weight) tracks the brand. --stage-sheen is the top-edge
37
+ highlight so components never hardcode #fff. */
38
+ --stage-metal: oklch(0.62 0.02 255);
39
+ --stage-pan: oklch(0.66 0.02 255);
40
+ --stage-weight: var(--primary, oklch(0.59 0.22 261));
41
+ --stage-weight-2: var(--brand-2, oklch(0.62 0.18 48));
42
+ --stage-sheen: oklch(1 0 0);
43
+ --stage-liquid: oklch(0.6 0.13 220 / 0.55);
44
+ --stage-handle-ring: color-mix(in oklab, var(--primary, oklch(0.59 0.22 261)) 40%, transparent);
45
+
46
+ /* Electronics — semantic aliases so circuit symbols never hardcode a colour.
47
+ Override these alone to retheme every circuit lab. wire = leads/conductors,
48
+ live = energised current path, charge = capacitor field / stored charge. */
49
+ --stage-wire: var(--stage-metal);
50
+ --stage-live: var(--stage-good);
51
+ --stage-charge: var(--stage-accent);
52
+ }
53
+
54
+ .dark {
55
+ /* Brighter neutrals + accents so glyphs pop on the dark card surface. */
56
+ --stage-bg: var(--card, oklch(0.20 0.02 260));
57
+ --stage-fg: var(--card-foreground, oklch(0.92 0.01 255));
58
+ --stage-muted: var(--muted-foreground, oklch(0.70 0.02 260));
59
+ --stage-grid: color-mix(in oklab, var(--foreground, oklch(0.95 0.01 255)) 18%, transparent);
60
+ --stage-axis: color-mix(in oklab, var(--foreground, oklch(0.95 0.01 255)) 45%, transparent);
61
+
62
+ --stage-accent-2: var(--brand-2, oklch(0.78 0.16 50));
63
+ --stage-good: var(--success, oklch(0.72 0.17 150));
64
+ --stage-warn: var(--warning, oklch(0.82 0.16 85));
65
+
66
+ --stage-metal: oklch(0.85 0.018 255);
67
+ --stage-pan: oklch(0.83 0.02 255);
68
+ --stage-weight-2: var(--brand-2, oklch(0.74 0.15 50));
69
+ --stage-liquid: oklch(0.7 0.13 220 / 0.6);
70
+ }