@lovo/matter-react 0.2.0 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @lovo/matter-react
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1c69220: Rename public API symbols to domain-accurate names.
8
+
9
+ New primary names: `FrameScheduler`, `GpuRenderer`, `GpuBackend` (`@lovo/matter`); `ShaderScene`, `ShaderSceneProps`, `ShaderContext`, `ShaderContextValue`, `useShaderContext`, `ShaderMonitor`, `ShaderMonitorProps`, `AnimatableSignal` (`@lovo/matter-react`).
10
+
11
+ Old names (`MatterScheduler`, `MatterRenderer`, `MatterBackend`, `MatterScene`, `MatterSceneProps`, `MatterContext`, `MatterContextValue`, `useMatterContext`, `MatterMonitor`, `MatterMonitorProps`, `MatterSignal`, `MatterBackend`) are deprecated with `@deprecated` JSDoc and continue to work. They will be removed no earlier than 0.5.0.
12
+
13
+ **Migration:** Replace old names with new in your imports and JSX. A one-pass find-and-replace is sufficient — no behavioral changes.
14
+
15
+ ## 0.3.0
16
+
17
+ ### Minor Changes
18
+
19
+ - c4cbb52: Add the overlay-component category. `MatterScene` now drives its render via `three/webgpu`'s `PostProcessing` pipeline so child components can register chained TSL transforms instead of each owning their own material draw.
20
+
21
+ **New: `useOverlayPass(transform, deps)` hook**
22
+
23
+ ```ts
24
+ import { useOverlayPass, useAnimatableUniform } from "@lovo/matter-react";
25
+
26
+ export function MyOverlay({ intensity }) {
27
+ const intensityU = useAnimatableUniform(intensity);
28
+ useOverlayPass(
29
+ (input) => input.mul(intensityU), // takes upstream pixel, returns modified pixel
30
+ [intensityU]
31
+ );
32
+ return null;
33
+ }
34
+ ```
35
+
36
+ Mount the component inside any `<MatterScene>` and it composes onto the pipeline; multiple overlays chain in mount order. Uniforms captured inside `transform` update in place and don't need to be in `deps` — only put structural changes (mode toggles, etc.) in `deps` so the transform gets re-registered.
37
+
38
+ **Registry-side ships (delivered via `@lovo/matter-cli` copy-paste):**
39
+
40
+ - `<FilmGrain>` — additive or subtractive grain overlay.
41
+ - `<Vignette>` — radial edge darkening, aspect-corrected so the mask is a circle on widescreen.
42
+ - **Breaking:** `<MeshGradient>` no longer accepts `grain` / `grainSpeed` props. Stack `<FilmGrain />` as a sibling inside `<MatterScene>` instead. Existing copies pulled before this release keep working; new pulls / CLI refreshes pick up the new shape. The MeshGradient docs page has the new pattern.
43
+
44
+ ### Patch Changes
45
+
46
+ - Updated dependencies [3856367]
47
+ - @lovo/matter@0.3.0
48
+
3
49
  ## 0.2.0
4
50
 
5
51
  ### Minor Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React binding for **Matter** — shader components on WebGPU + Three.js TSL.
4
4
 
5
- This package wraps the engine ([`@lovo/matter`](https://www.npmjs.com/package/@lovo/matter)) with React-friendly primitives: a shared `<MatterScene>` canvas, a `useShaderMaterial` hook for `@react-three/fiber` integration, and input hooks (`useCursor`, `useScroll`).
5
+ This package wraps the engine ([`@lovo/matter`](https://www.npmjs.com/package/@lovo/matter)) with React-friendly primitives: a shared `<ShaderScene>` canvas, a `useShaderMaterial` hook for `@react-three/fiber` integration, and input hooks (`useCursor`, `useScroll`).
6
6
 
7
7
  ## Install
8
8
 
@@ -17,21 +17,21 @@ npm install @lovo/matter @lovo/matter-react react three
17
17
  Matter components work in three configurations:
18
18
 
19
19
  1. **Drop-in** — each component manages its own canvas. Simplest path; one canvas per effect.
20
- 2. **Shared scene** — wrap multiple Matter components in a single `<MatterScene>` to share one canvas (faster, layered effects).
20
+ 2. **Shared scene** — wrap multiple Matter components in a single `<ShaderScene>` to share one canvas (faster, layered effects).
21
21
  3. **Inside `@react-three/fiber`** — use `useShaderMaterial` directly inside a r3f `<Canvas>` you already own.
22
22
 
23
23
  ## Minimal usage (Mode 2: shared scene)
24
24
 
25
25
  ```tsx
26
- import { MatterScene } from '@lovo/matter-react'
26
+ import { ShaderScene } from '@lovo/matter-react'
27
27
  // LinearGradient is copy-pasted into your project via @lovo/matter-cli
28
28
  import { LinearGradient } from '@/components/matter/linear-gradient'
29
29
 
30
30
  export default function Hero() {
31
31
  return (
32
- <MatterScene>
32
+ <ShaderScene>
33
33
  <LinearGradient colors={['#0b0c2a', '#1d1f57', '#7d2dff']} angle={120} />
34
- </MatterScene>
34
+ </ShaderScene>
35
35
  )
36
36
  }
37
37
  ```
@@ -52,6 +52,10 @@ The component lands in `src/components/matter/linear-gradient.tsx` and is yours
52
52
 
53
53
  <https://github.com/lovo-hq/matter>
54
54
 
55
+ ## Migration from 0.3.x
56
+
57
+ `MatterScene`, `MatterMonitor`, `useMatterContext`, and related types have been renamed to `ShaderScene`, `ShaderMonitor`, `useShaderContext`, `ShaderContextValue`, etc. The old names are deprecated and still work — remove them at your leisure before 0.5.0.
58
+
55
59
  ## License
56
60
 
57
61
  MIT — see [LICENSE](./LICENSE).
package/dist/index.cjs CHANGED
@@ -21,29 +21,103 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  FallbackBoundary: () => FallbackBoundary,
24
- MatterMonitor: () => MatterMonitor,
25
- MatterScene: () => MatterScene,
24
+ ShaderMonitor: () => ShaderMonitor,
25
+ ShaderScene: () => ShaderScene,
26
26
  useAnimatableUniform: () => useAnimatableUniform,
27
27
  useCursor: () => useCursor,
28
- useMatterContext: () => useMatterContext,
28
+ useOverlayPass: () => useOverlayPass,
29
29
  useResize: () => useResize,
30
30
  useScroll: () => useScroll,
31
+ useShaderContext: () => useShaderContext,
31
32
  useShaderMaterial: () => useShaderMaterial,
32
33
  useStaticHint: () => useStaticHint
33
34
  });
34
35
  module.exports = __toCommonJS(index_exports);
35
36
 
36
- // src/MatterScene.tsx
37
+ // src/components/fallback-boundary/fallback-boundary.tsx
38
+ var import_react = require("react");
39
+ var import_jsx_runtime = require("react/jsx-runtime");
40
+ function FallbackBoundary({ fallback, children }) {
41
+ const [mounted, setMounted] = (0, import_react.useState)(false);
42
+ (0, import_react.useEffect)(() => {
43
+ setMounted(true);
44
+ }, []);
45
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: mounted ? children : fallback ?? null });
46
+ }
47
+
48
+ // src/components/shader-monitor/shader-monitor.tsx
49
+ var import_react3 = require("react");
50
+
51
+ // src/context/shader-context.ts
37
52
  var import_react2 = require("react");
38
- var import_three = require("three");
39
- var import_matter = require("@lovo/matter");
53
+ var ShaderContext = (0, import_react2.createContext)(null);
40
54
 
41
- // src/matter-context.ts
42
- var import_react = require("react");
43
- var MatterContext = (0, import_react.createContext)(null);
55
+ // src/components/shader-monitor/shader-monitor.tsx
56
+ var import_jsx_runtime2 = require("react/jsx-runtime");
57
+ var anchorStyle = {
58
+ "top-left": { top: 8, left: 8 },
59
+ "top-right": { top: 8, right: 8 },
60
+ "bottom-left": { bottom: 8, left: 8 },
61
+ "bottom-right": { bottom: 8, right: 8 }
62
+ };
63
+ var baseStyle = {
64
+ position: "absolute",
65
+ zIndex: 10,
66
+ padding: "6px 8px",
67
+ borderRadius: 6,
68
+ background: "rgba(0, 0, 0, 0.6)",
69
+ color: "#fff",
70
+ font: "11px ui-monospace, monospace",
71
+ lineHeight: 1.4,
72
+ pointerEvents: "none",
73
+ whiteSpace: "pre"
74
+ };
75
+ function ShaderMonitor({ anchor = "top-right" }) {
76
+ const ctx = (0, import_react3.useContext)(ShaderContext);
77
+ const [stats, setStats] = (0, import_react3.useState)({ fps: 0, ticks: 0, frames: 0 });
78
+ const ticksRef = (0, import_react3.useRef)(0);
79
+ const fpsAccumRef = (0, import_react3.useRef)({ frames: 0, lastSampleAt: 0, fps: 0 });
80
+ (0, import_react3.useEffect)(() => {
81
+ if (!ctx) return;
82
+ const client = (tick) => {
83
+ ticksRef.current += 1;
84
+ const acc = fpsAccumRef.current;
85
+ acc.frames += 1;
86
+ if (acc.lastSampleAt === 0) acc.lastSampleAt = tick.now;
87
+ const dt = tick.now - acc.lastSampleAt;
88
+ if (dt >= 500) {
89
+ acc.fps = Math.round(acc.frames * 1e3 / dt);
90
+ acc.frames = 0;
91
+ acc.lastSampleAt = tick.now;
92
+ }
93
+ setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames });
94
+ };
95
+ ctx.scheduler.add(client);
96
+ return () => ctx.scheduler.remove(client);
97
+ }, [ctx]);
98
+ if (!ctx) {
99
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
100
+ }
101
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
102
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { "data-testid": "matter-monitor-fps", children: [
103
+ "fps: ",
104
+ stats.fps || "\u2014"
105
+ ] }),
106
+ "\n",
107
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { "data-testid": "matter-monitor-ticks", children: [
108
+ "ticks: ",
109
+ stats.ticks
110
+ ] })
111
+ ] });
112
+ }
44
113
 
45
- // src/MatterScene.tsx
46
- var import_jsx_runtime = require("react/jsx-runtime");
114
+ // src/components/shader-scene/shader-scene.tsx
115
+ var import_matter = require("@lovo/matter");
116
+ var import_react4 = require("react");
117
+ var import_three = require("three");
118
+ var import_tsl = require("three/tsl");
119
+ var import_webgpu = require("three/webgpu");
120
+ var import_jsx_runtime3 = require("react/jsx-runtime");
47
121
  var defaultStyle = {
48
122
  position: "absolute",
49
123
  inset: 0,
@@ -51,12 +125,12 @@ var defaultStyle = {
51
125
  width: "100%",
52
126
  height: "100%"
53
127
  };
54
- function MatterScene(props) {
128
+ function ShaderScene(props) {
55
129
  const { children, fallback, className, style, maxDPR } = props;
56
- const canvasRef = (0, import_react2.useRef)(null);
57
- const [ctx, setCtx] = (0, import_react2.useState)(null);
58
- const [error, setError] = (0, import_react2.useState)(null);
59
- (0, import_react2.useEffect)(() => {
130
+ const canvasRef = (0, import_react4.useRef)(null);
131
+ const [ctx, setCtx] = (0, import_react4.useState)(null);
132
+ const [error, setError] = (0, import_react4.useState)(null);
133
+ (0, import_react4.useEffect)(() => {
60
134
  const canvas = canvasRef.current;
61
135
  if (!canvas) return;
62
136
  let cancelled = false;
@@ -71,8 +145,29 @@ function MatterScene(props) {
71
145
  const scene = new import_three.Scene();
72
146
  const camera = new import_three.OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
73
147
  camera.position.z = 1;
74
- const scheduler = new import_matter.MatterScheduler();
75
- scheduler.add(() => renderer.three.render(scene, camera));
148
+ const postProcessing = new import_webgpu.PostProcessing(renderer.three);
149
+ const scheduler = new import_matter.FrameScheduler();
150
+ const overlays = /* @__PURE__ */ new Map();
151
+ const basePass = (0, import_tsl.pass)(scene, camera);
152
+ const rebuildOutputNode = () => {
153
+ const seed = basePass;
154
+ postProcessing.outputNode = Array.from(overlays.values()).reduce(
155
+ (node, transform) => transform(node),
156
+ seed
157
+ );
158
+ postProcessing.needsUpdate = true;
159
+ };
160
+ rebuildOutputNode();
161
+ const registerOverlay = (transform) => {
162
+ const key = /* @__PURE__ */ Symbol("overlay");
163
+ overlays.set(key, transform);
164
+ rebuildOutputNode();
165
+ return () => {
166
+ overlays.delete(key);
167
+ rebuildOutputNode();
168
+ };
169
+ };
170
+ scheduler.add(() => postProcessing.render());
76
171
  scheduler.start();
77
172
  const visibility = (0, import_matter.createVisibilityWatcher)();
78
173
  const intersection = (0, import_matter.createIntersectionWatcher)(canvas);
@@ -95,11 +190,11 @@ function MatterScene(props) {
95
190
  scheduler.dispose();
96
191
  renderer.dispose();
97
192
  };
98
- setCtx({ renderer, scene, camera, scheduler });
193
+ setCtx({ renderer, scene, camera, scheduler, registerOverlay });
99
194
  } catch (err) {
100
195
  if (cancelled) return;
101
196
  const e = err instanceof Error ? err : new Error(String(err));
102
- console.error("[MatterScene] renderer init failed:", e);
197
+ console.error("[ShaderScene] renderer init failed:", e);
103
198
  setError(e);
104
199
  }
105
200
  };
@@ -111,9 +206,9 @@ function MatterScene(props) {
111
206
  setCtx(null);
112
207
  };
113
208
  }, [maxDPR]);
114
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...defaultStyle, ...style }, children: [
115
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("canvas", { ref: canvasRef, style: { width: "100%", height: "100%", display: "block" } }),
116
- error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
209
+ let content;
210
+ if (error) {
211
+ content = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
117
212
  "div",
118
213
  {
119
214
  style: {
@@ -130,47 +225,67 @@ function MatterScene(props) {
130
225
  textAlign: "center"
131
226
  },
132
227
  children: [
133
- "MatterScene init failed:",
228
+ "ShaderScene init failed:",
134
229
  "\n",
135
230
  error.message
136
231
  ]
137
232
  }
138
- ) : ctx ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MatterContext.Provider, { value: ctx, children }) : fallback ?? null
233
+ );
234
+ } else if (ctx) {
235
+ content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShaderContext.Provider, { value: ctx, children });
236
+ } else {
237
+ content = fallback ?? null;
238
+ }
239
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: { ...defaultStyle, ...style }, children: [
240
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("canvas", { ref: canvasRef, style: { width: "100%", height: "100%", display: "block" } }),
241
+ content
139
242
  ] });
140
243
  }
141
244
 
142
- // src/useMatterContext.ts
143
- var import_react3 = require("react");
144
- function useMatterContext() {
145
- return (0, import_react3.useContext)(MatterContext);
245
+ // src/hooks/use-animatable-uniform/use-animatable-uniform.ts
246
+ var import_react5 = require("react");
247
+ var import_tsl2 = require("three/tsl");
248
+ var isSignal = (value) => {
249
+ if (typeof value !== "object" || value === null) return false;
250
+ return "get" in value && typeof value.get === "function" && "on" in value && typeof value.on === "function";
251
+ };
252
+ function useAnimatableUniform(value) {
253
+ const uniformNode = (0, import_react5.useMemo)(() => {
254
+ const initial = isSignal(value) ? value.get() : value;
255
+ return (0, import_tsl2.uniform)(initial);
256
+ }, []);
257
+ (0, import_react5.useEffect)(() => {
258
+ if (isSignal(value)) {
259
+ const unsub = value.on("change", (next) => {
260
+ uniformNode.value = next;
261
+ });
262
+ return unsub;
263
+ }
264
+ uniformNode.value = value;
265
+ return void 0;
266
+ }, [value, uniformNode]);
267
+ return uniformNode;
146
268
  }
147
269
 
148
- // src/useShaderMaterial.ts
149
- var import_react4 = require("react");
150
- var import_webgpu = require("three/webgpu");
151
- function useShaderMaterial(build) {
152
- const material = (0, import_react4.useMemo)(() => {
153
- const m = new import_webgpu.MeshBasicNodeMaterial();
154
- m.colorNode = build();
155
- return m;
156
- }, [build]);
157
- (0, import_react4.useEffect)(() => {
158
- return () => material.dispose();
159
- }, [material]);
160
- return material;
270
+ // src/hooks/use-cursor/use-cursor.ts
271
+ var import_matter2 = require("@lovo/matter");
272
+ var import_react7 = require("react");
273
+
274
+ // src/hooks/use-shader-context/use-shader-context.ts
275
+ var import_react6 = require("react");
276
+ function useShaderContext() {
277
+ return (0, import_react6.useContext)(ShaderContext);
161
278
  }
162
279
 
163
- // src/useCursor.ts
164
- var import_react5 = require("react");
165
- var import_matter2 = require("@lovo/matter");
280
+ // src/hooks/use-cursor/use-cursor.ts
166
281
  var STUB_SIGNAL = {
167
282
  get: () => [0.5, 0.5],
168
283
  on: () => () => void 0
169
284
  };
170
285
  function useCursor(opts = {}) {
171
- const ctx = useMatterContext();
172
- const [input, setInput] = (0, import_react5.useState)(null);
173
- (0, import_react5.useEffect)(() => {
286
+ const ctx = useShaderContext();
287
+ const [input, setInput] = (0, import_react7.useState)(null);
288
+ (0, import_react7.useEffect)(() => {
174
289
  const canvas = ctx?.renderer.three.domElement;
175
290
  const elementOpt = opts.element ?? (canvas instanceof HTMLElement ? canvas : void 0);
176
291
  const fresh = new import_matter2.CursorInput({ ...opts, element: elementOpt });
@@ -195,7 +310,7 @@ function useCursor(opts = {}) {
195
310
  };
196
311
  }
197
312
  return () => {
198
- detach?.();
313
+ detach();
199
314
  fresh.dispose();
200
315
  setInput(null);
201
316
  };
@@ -203,16 +318,27 @@ function useCursor(opts = {}) {
203
318
  return input ?? STUB_SIGNAL;
204
319
  }
205
320
 
206
- // src/useResize.ts
207
- var import_react6 = require("react");
321
+ // src/hooks/use-overlay-pass/use-overlay-pass.ts
322
+ var import_react8 = require("react");
323
+ function useOverlayPass(transform, deps) {
324
+ const ctx = useShaderContext();
325
+ (0, import_react8.useEffect)(() => {
326
+ if (!ctx) return;
327
+ const unregister = ctx.registerOverlay(transform);
328
+ return unregister;
329
+ }, [ctx, ...deps]);
330
+ }
331
+
332
+ // src/hooks/use-resize/use-resize.ts
333
+ var import_react9 = require("react");
208
334
  var STUB_SIGNAL2 = {
209
335
  get: () => [0, 0, 1],
210
336
  on: () => () => void 0
211
337
  };
212
338
  function useResize() {
213
- const ctx = useMatterContext();
214
- const [signal, setSignal] = (0, import_react6.useState)(null);
215
- (0, import_react6.useEffect)(() => {
339
+ const ctx = useShaderContext();
340
+ const [signal, setSignal] = (0, import_react9.useState)(null);
341
+ (0, import_react9.useEffect)(() => {
216
342
  if (!ctx) return void 0;
217
343
  const canvas = ctx.renderer.three.domElement;
218
344
  if (!(canvas instanceof HTMLCanvasElement)) return void 0;
@@ -272,15 +398,15 @@ function useResize() {
272
398
  return signal ?? STUB_SIGNAL2;
273
399
  }
274
400
 
275
- // src/useScroll.ts
276
- var import_react7 = require("react");
401
+ // src/hooks/use-scroll/use-scroll.ts
402
+ var import_react10 = require("react");
277
403
  var STUB_SIGNAL3 = {
278
404
  get: () => [0, 0],
279
405
  on: () => () => void 0
280
406
  };
281
407
  function useScroll() {
282
- const [signal, setSignal] = (0, import_react7.useState)(null);
283
- (0, import_react7.useEffect)(() => {
408
+ const [signal, setSignal] = (0, import_react10.useState)(null);
409
+ (0, import_react10.useEffect)(() => {
284
410
  if (typeof window === "undefined") return void 0;
285
411
  const compute = () => {
286
412
  const y = window.scrollY;
@@ -322,123 +448,42 @@ function useScroll() {
322
448
  return signal ?? STUB_SIGNAL3;
323
449
  }
324
450
 
325
- // src/useAnimatableUniform.ts
326
- var import_react8 = require("react");
327
- var import_tsl = require("three/tsl");
328
- var isSignal = (value) => {
329
- return typeof value === "object" && value !== null && typeof value.get === "function" && typeof value.on === "function";
330
- };
331
- function useAnimatableUniform(value) {
332
- const uniformNode = (0, import_react8.useMemo)(() => {
333
- const initial = isSignal(value) ? value.get() : value;
334
- return (0, import_tsl.uniform)(initial);
335
- }, []);
336
- (0, import_react8.useEffect)(() => {
337
- if (isSignal(value)) {
338
- const unsub = value.on("change", (next) => {
339
- ;
340
- uniformNode.value = next;
341
- });
342
- return unsub;
343
- }
344
- ;
345
- uniformNode.value = value;
346
- return void 0;
347
- }, [value, uniformNode]);
348
- return uniformNode;
349
- }
350
-
351
- // src/FallbackBoundary.tsx
352
- var import_react9 = require("react");
353
- var import_jsx_runtime2 = require("react/jsx-runtime");
354
- function FallbackBoundary({ fallback, children }) {
355
- const [mounted, setMounted] = (0, import_react9.useState)(false);
356
- (0, import_react9.useEffect)(() => {
357
- setMounted(true);
358
- }, []);
359
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: mounted ? children : fallback ?? null });
451
+ // src/hooks/use-shader-material/use-shader-material.ts
452
+ var import_react11 = require("react");
453
+ var import_webgpu2 = require("three/webgpu");
454
+ function useShaderMaterial(build) {
455
+ const material = (0, import_react11.useMemo)(() => {
456
+ const m = new import_webgpu2.MeshBasicNodeMaterial();
457
+ m.colorNode = build();
458
+ return m;
459
+ }, [build]);
460
+ (0, import_react11.useEffect)(() => {
461
+ return () => material.dispose();
462
+ }, [material]);
463
+ return material;
360
464
  }
361
465
 
362
- // src/useStaticHint.ts
363
- var import_react10 = require("react");
466
+ // src/hooks/use-static-hint/use-static-hint.ts
467
+ var import_react12 = require("react");
364
468
  function useStaticHint(hint) {
365
- const ctx = useMatterContext();
366
- (0, import_react10.useEffect)(() => {
469
+ const ctx = useShaderContext();
470
+ (0, import_react12.useEffect)(() => {
367
471
  if (!ctx) return;
368
472
  ctx.scheduler.setIdle(hint);
369
473
  return () => ctx.scheduler.setIdle(false);
370
474
  }, [ctx, hint]);
371
475
  }
372
-
373
- // src/MatterMonitor.tsx
374
- var import_react11 = require("react");
375
- var import_jsx_runtime3 = require("react/jsx-runtime");
376
- var anchorStyle = {
377
- "top-left": { top: 8, left: 8 },
378
- "top-right": { top: 8, right: 8 },
379
- "bottom-left": { bottom: 8, left: 8 },
380
- "bottom-right": { bottom: 8, right: 8 }
381
- };
382
- var baseStyle = {
383
- position: "absolute",
384
- zIndex: 10,
385
- padding: "6px 8px",
386
- borderRadius: 6,
387
- background: "rgba(0, 0, 0, 0.6)",
388
- color: "#fff",
389
- font: "11px ui-monospace, monospace",
390
- lineHeight: 1.4,
391
- pointerEvents: "none",
392
- whiteSpace: "pre"
393
- };
394
- function MatterMonitor({ anchor = "top-right" }) {
395
- const ctx = (0, import_react11.useContext)(MatterContext);
396
- const [stats, setStats] = (0, import_react11.useState)({ fps: 0, ticks: 0, frames: 0 });
397
- const ticksRef = (0, import_react11.useRef)(0);
398
- const fpsAccumRef = (0, import_react11.useRef)({ frames: 0, lastSampleAt: 0, fps: 0 });
399
- (0, import_react11.useEffect)(() => {
400
- if (!ctx) return;
401
- const client = (tick) => {
402
- ticksRef.current += 1;
403
- const acc = fpsAccumRef.current;
404
- acc.frames += 1;
405
- if (acc.lastSampleAt === 0) acc.lastSampleAt = tick.now;
406
- const dt = tick.now - acc.lastSampleAt;
407
- if (dt >= 500) {
408
- acc.fps = Math.round(acc.frames * 1e3 / dt);
409
- acc.frames = 0;
410
- acc.lastSampleAt = tick.now;
411
- }
412
- setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames });
413
- };
414
- ctx.scheduler.add(client);
415
- return () => ctx.scheduler.remove(client);
416
- }, [ctx]);
417
- if (!ctx) {
418
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
419
- }
420
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
421
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-testid": "matter-monitor-fps", children: [
422
- "fps: ",
423
- stats.fps || "\u2014"
424
- ] }),
425
- "\n",
426
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-testid": "matter-monitor-ticks", children: [
427
- "ticks: ",
428
- stats.ticks
429
- ] })
430
- ] });
431
- }
432
476
  // Annotate the CommonJS export names for ESM import in node:
433
477
  0 && (module.exports = {
434
478
  FallbackBoundary,
435
- MatterMonitor,
436
- MatterScene,
479
+ ShaderMonitor,
480
+ ShaderScene,
437
481
  useAnimatableUniform,
438
482
  useCursor,
439
- useMatterContext,
483
+ useOverlayPass,
440
484
  useResize,
441
485
  useScroll,
486
+ useShaderContext,
442
487
  useShaderMaterial,
443
488
  useStaticHint
444
489
  });