@lovo/matter-react 0.4.0 → 0.5.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,19 @@
1
1
  # @lovo/matter-react
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 35274c3: Rename ambiguous `@lovo/matter-react` public exports to clearer names (BREAKING, pre-1.0):
8
+
9
+ - `useOverlayPass` → `usePostProcessPass` (and the paired type `OverlayTransform` → `PostProcessTransform`)
10
+ - `useStaticHint` → `useStaticSceneHint`
11
+ - `MonitorAnchor` (type) → `ShaderMonitorAnchor`
12
+
13
+ Migration: update imports and call sites to the new names. Behavior is unchanged.
14
+
15
+ ## 0.4.1
16
+
3
17
  ## 0.4.0
4
18
 
5
19
  ### Minor Changes
@@ -21,13 +35,13 @@
21
35
  **New: `useOverlayPass(transform, deps)` hook**
22
36
 
23
37
  ```ts
24
- import { useOverlayPass, useAnimatableUniform } from "@lovo/matter-react";
38
+ import { useAnimatableUniform, useOverlayPass } from '@lovo/matter-react';
25
39
 
26
40
  export function MyOverlay({ intensity }) {
27
41
  const intensityU = useAnimatableUniform(intensity);
28
42
  useOverlayPass(
29
43
  (input) => input.mul(intensityU), // takes upstream pixel, returns modified pixel
30
- [intensityU]
44
+ [intensityU],
31
45
  );
32
46
  return null;
33
47
  }
package/README.md CHANGED
@@ -38,7 +38,7 @@ export default function Hero() {
38
38
 
39
39
  ## Getting components
40
40
 
41
- Polished drop-in components (`<LinearGradient>`, `<Aurora>`, `<DotField>`, `<NoiseField>`, `<MeshGradient>`, `<Waves>`) ship via the shadcn-style copy-paste CLI. Install it once:
41
+ Polished drop-in components (`<LinearGradient>`, `<Aurora>`, `<DotField>`, `<SimplexNoise>`, `<MeshGradient>`, `<Waves>`) ship via the shadcn-style copy-paste CLI. Install it once:
42
42
 
43
43
  ```bash
44
44
  npm install -D @lovo/matter-cli
package/dist/index.cjs CHANGED
@@ -25,12 +25,12 @@ __export(index_exports, {
25
25
  ShaderScene: () => ShaderScene,
26
26
  useAnimatableUniform: () => useAnimatableUniform,
27
27
  useCursor: () => useCursor,
28
- useOverlayPass: () => useOverlayPass,
28
+ usePostProcessPass: () => usePostProcessPass,
29
29
  useResize: () => useResize,
30
30
  useScroll: () => useScroll,
31
31
  useShaderContext: () => useShaderContext,
32
32
  useShaderMaterial: () => useShaderMaterial,
33
- useStaticHint: () => useStaticHint
33
+ useStaticSceneHint: () => useStaticSceneHint
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
@@ -73,29 +73,29 @@ var baseStyle = {
73
73
  whiteSpace: "pre"
74
74
  };
75
75
  function ShaderMonitor({ anchor = "top-right" }) {
76
- const ctx = (0, import_react3.useContext)(ShaderContext);
76
+ const shaderContext = (0, import_react3.useContext)(ShaderContext);
77
77
  const [stats, setStats] = (0, import_react3.useState)({ fps: 0, ticks: 0, frames: 0 });
78
78
  const ticksRef = (0, import_react3.useRef)(0);
79
79
  const fpsAccumRef = (0, import_react3.useRef)({ frames: 0, lastSampleAt: 0, fps: 0 });
80
80
  (0, import_react3.useEffect)(() => {
81
- if (!ctx) return;
82
- const client = (tick) => {
81
+ if (!shaderContext) return;
82
+ const schedulerTickHandler = (tick) => {
83
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;
84
+ const fpsAccumulator = fpsAccumRef.current;
85
+ fpsAccumulator.frames += 1;
86
+ if (fpsAccumulator.lastSampleAt === 0) fpsAccumulator.lastSampleAt = tick.now;
87
+ const deltaTimeSinceLastSample = tick.now - fpsAccumulator.lastSampleAt;
88
+ if (deltaTimeSinceLastSample >= 500) {
89
+ fpsAccumulator.fps = Math.round(fpsAccumulator.frames * 1e3 / deltaTimeSinceLastSample);
90
+ fpsAccumulator.frames = 0;
91
+ fpsAccumulator.lastSampleAt = tick.now;
92
92
  }
93
- setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames });
93
+ setStats({ fps: fpsAccumulator.fps, ticks: ticksRef.current, frames: fpsAccumulator.frames });
94
94
  };
95
- ctx.scheduler.add(client);
96
- return () => ctx.scheduler.remove(client);
97
- }, [ctx]);
98
- if (!ctx) {
95
+ shaderContext.scheduler.add(schedulerTickHandler);
96
+ return () => shaderContext.scheduler.remove(schedulerTickHandler);
97
+ }, [shaderContext]);
98
+ if (!shaderContext) {
99
99
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
100
100
  }
101
101
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
@@ -112,8 +112,8 @@ function ShaderMonitor({ anchor = "top-right" }) {
112
112
  }
113
113
 
114
114
  // src/components/shader-scene/shader-scene.tsx
115
- var import_matter = require("@lovo/matter");
116
115
  var import_react4 = require("react");
116
+ var import_matter = require("@lovo/matter");
117
117
  var import_three = require("three");
118
118
  var import_tsl = require("three/tsl");
119
119
  var import_webgpu = require("three/webgpu");
@@ -128,7 +128,7 @@ var defaultStyle = {
128
128
  function ShaderScene(props) {
129
129
  const { children, fallback, className, style, maxDPR } = props;
130
130
  const canvasRef = (0, import_react4.useRef)(null);
131
- const [ctx, setCtx] = (0, import_react4.useState)(null);
131
+ const [shaderContext, setShaderContext] = (0, import_react4.useState)(null);
132
132
  const [error, setError] = (0, import_react4.useState)(null);
133
133
  (0, import_react4.useEffect)(() => {
134
134
  const canvas = canvasRef.current;
@@ -150,10 +150,10 @@ function ShaderScene(props) {
150
150
  const overlays = /* @__PURE__ */ new Map();
151
151
  const basePass = (0, import_tsl.pass)(scene, camera);
152
152
  const rebuildOutputNode = () => {
153
- const seed = basePass;
153
+ const basePassNode = basePass;
154
154
  postProcessing.outputNode = Array.from(overlays.values()).reduce(
155
- (node, transform) => transform(node),
156
- seed
155
+ (currentPipeline, transform) => transform(currentPipeline),
156
+ basePassNode
157
157
  );
158
158
  postProcessing.needsUpdate = true;
159
159
  };
@@ -190,12 +190,12 @@ function ShaderScene(props) {
190
190
  scheduler.dispose();
191
191
  renderer.dispose();
192
192
  };
193
- setCtx({ renderer, scene, camera, scheduler, registerOverlay });
194
- } catch (err) {
193
+ setShaderContext({ renderer, scene, camera, scheduler, registerOverlay });
194
+ } catch (caughtError) {
195
195
  if (cancelled) return;
196
- const e = err instanceof Error ? err : new Error(String(err));
197
- console.error("[ShaderScene] renderer init failed:", e);
198
- setError(e);
196
+ const normalizedError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
197
+ console.error("[ShaderScene] renderer init failed:", normalizedError);
198
+ setError(normalizedError);
199
199
  }
200
200
  };
201
201
  void setup();
@@ -203,7 +203,7 @@ function ShaderScene(props) {
203
203
  cancelled = true;
204
204
  cleanup?.();
205
205
  cleanup = null;
206
- setCtx(null);
206
+ setShaderContext(null);
207
207
  };
208
208
  }, [maxDPR]);
209
209
  let content;
@@ -231,8 +231,8 @@ function ShaderScene(props) {
231
231
  ]
232
232
  }
233
233
  );
234
- } else if (ctx) {
235
- content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShaderContext.Provider, { value: ctx, children });
234
+ } else if (shaderContext) {
235
+ content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShaderContext.Provider, { value: shaderContext, children });
236
236
  } else {
237
237
  content = fallback ?? null;
238
238
  }
@@ -268,8 +268,8 @@ function useAnimatableUniform(value) {
268
268
  }
269
269
 
270
270
  // src/hooks/use-cursor/use-cursor.ts
271
- var import_matter2 = require("@lovo/matter");
272
271
  var import_react7 = require("react");
272
+ var import_matter2 = require("@lovo/matter");
273
273
 
274
274
  // src/hooks/use-shader-context/use-shader-context.ts
275
275
  var import_react6 = require("react");
@@ -283,81 +283,91 @@ var STUB_SIGNAL = {
283
283
  on: () => () => void 0
284
284
  };
285
285
  function useCursor(opts = {}) {
286
- const ctx = useShaderContext();
286
+ const shaderContext = useShaderContext();
287
287
  const [input, setInput] = (0, import_react7.useState)(null);
288
288
  (0, import_react7.useEffect)(() => {
289
- const canvas = ctx?.renderer.three.domElement;
290
- const elementOpt = opts.element ?? (canvas instanceof HTMLElement ? canvas : void 0);
291
- const fresh = new import_matter2.CursorInput({ ...opts, element: elementOpt });
292
- setInput(fresh);
289
+ const canvas = shaderContext?.renderer.three.domElement;
290
+ const resolvedElement = opts.element ?? (canvas instanceof HTMLElement ? canvas : void 0);
291
+ const newCursorInput = new import_matter2.CursorInput({ ...opts, element: resolvedElement });
292
+ setInput(newCursorInput);
293
293
  let detach = null;
294
- if (ctx?.scheduler) {
295
- const client = ({ delta }) => fresh.tick(delta);
296
- ctx.scheduler.add(client);
297
- detach = () => ctx.scheduler.remove(client);
294
+ if (shaderContext?.scheduler) {
295
+ const schedulerTickHandler = ({ delta }) => newCursorInput.tick(delta);
296
+ shaderContext.scheduler.add(schedulerTickHandler);
297
+ detach = () => shaderContext.scheduler.remove(schedulerTickHandler);
298
298
  } else {
299
- let raf = null;
299
+ let animationFrameId = null;
300
300
  let lastNow = performance.now();
301
301
  const loop = (now) => {
302
302
  const delta = (now - lastNow) / 1e3;
303
303
  lastNow = now;
304
- fresh.tick(delta);
305
- raf = requestAnimationFrame(loop);
304
+ newCursorInput.tick(delta);
305
+ animationFrameId = requestAnimationFrame(loop);
306
306
  };
307
- raf = requestAnimationFrame(loop);
307
+ animationFrameId = requestAnimationFrame(loop);
308
308
  detach = () => {
309
- if (raf !== null) cancelAnimationFrame(raf);
309
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
310
310
  };
311
311
  }
312
312
  return () => {
313
313
  detach();
314
- fresh.dispose();
314
+ newCursorInput.dispose();
315
315
  setInput(null);
316
316
  };
317
- }, [ctx]);
317
+ }, [shaderContext]);
318
318
  return input ?? STUB_SIGNAL;
319
319
  }
320
320
 
321
321
  // src/hooks/use-overlay-pass/use-overlay-pass.ts
322
322
  var import_react8 = require("react");
323
- function useOverlayPass(transform, deps) {
324
- const ctx = useShaderContext();
323
+ function usePostProcessPass(transform, deps) {
324
+ const shaderContext = useShaderContext();
325
325
  (0, import_react8.useEffect)(() => {
326
- if (!ctx) return;
327
- const unregister = ctx.registerOverlay(transform);
326
+ if (!shaderContext) return;
327
+ const unregister = shaderContext.registerOverlay(transform);
328
328
  return unregister;
329
- }, [ctx, ...deps]);
329
+ }, [shaderContext, ...deps]);
330
330
  }
331
331
 
332
332
  // src/hooks/use-resize/use-resize.ts
333
333
  var import_react9 = require("react");
334
+
335
+ // src/internal/create-signal.ts
336
+ function createSignal(getValue) {
337
+ const listeners = /* @__PURE__ */ new Set();
338
+ return {
339
+ listeners,
340
+ signal: {
341
+ get: getValue,
342
+ on: (_event, listener) => {
343
+ listeners.add(listener);
344
+ return () => {
345
+ listeners.delete(listener);
346
+ };
347
+ }
348
+ }
349
+ };
350
+ }
351
+
352
+ // src/hooks/use-resize/use-resize.ts
334
353
  var STUB_SIGNAL2 = {
335
354
  get: () => [0, 0, 1],
336
355
  on: () => () => void 0
337
356
  };
338
357
  function useResize() {
339
- const ctx = useShaderContext();
358
+ const shaderContext = useShaderContext();
340
359
  const [signal, setSignal] = (0, import_react9.useState)(null);
341
360
  (0, import_react9.useEffect)(() => {
342
- if (!ctx) return void 0;
343
- const canvas = ctx.renderer.three.domElement;
361
+ if (!shaderContext) return void 0;
362
+ const canvas = shaderContext.renderer.three.domElement;
344
363
  if (!(canvas instanceof HTMLCanvasElement)) return void 0;
345
364
  let value = [
346
365
  canvas.clientWidth,
347
366
  canvas.clientHeight,
348
367
  typeof window !== "undefined" ? window.devicePixelRatio : 1
349
368
  ];
350
- const listeners = /* @__PURE__ */ new Set();
351
- const fresh = {
352
- get: () => value,
353
- on: (_event, cb) => {
354
- listeners.add(cb);
355
- return () => {
356
- listeners.delete(cb);
357
- };
358
- }
359
- };
360
- setSignal(fresh);
369
+ const { signal: newSignal, listeners } = createSignal(() => value);
370
+ setSignal(newSignal);
361
371
  const emit = () => {
362
372
  const next = [
363
373
  canvas.clientWidth,
@@ -366,35 +376,37 @@ function useResize() {
366
376
  ];
367
377
  if (next[0] === value[0] && next[1] === value[1] && next[2] === value[2]) return;
368
378
  value = next;
369
- for (const cb of listeners) cb(next);
379
+ for (const listener of listeners) listener(next);
370
380
  };
371
381
  const observer = new ResizeObserver(emit);
372
382
  observer.observe(canvas);
373
- let mql = null;
374
- let mqlHandler = null;
383
+ let mediaQueryList = null;
384
+ let mediaQueryListener = null;
375
385
  const setupDprWatch = () => {
376
386
  if (typeof window === "undefined") return;
377
387
  const dpr = window.devicePixelRatio;
378
- const next = window.matchMedia(`(resolution: ${dpr}dppx)`);
379
- const handler = () => {
388
+ const nextMediaQueryList = window.matchMedia(`(resolution: ${dpr}dppx)`);
389
+ const nextMediaQueryListener = () => {
380
390
  emit();
381
- if (mql && mqlHandler) mql.removeEventListener("change", mqlHandler);
391
+ if (mediaQueryList && mediaQueryListener)
392
+ mediaQueryList.removeEventListener("change", mediaQueryListener);
382
393
  setupDprWatch();
383
394
  };
384
- next.addEventListener("change", handler);
385
- mql = next;
386
- mqlHandler = handler;
395
+ nextMediaQueryList.addEventListener("change", nextMediaQueryListener);
396
+ mediaQueryList = nextMediaQueryList;
397
+ mediaQueryListener = nextMediaQueryListener;
387
398
  };
388
399
  setupDprWatch();
389
400
  return () => {
390
401
  observer.disconnect();
391
- if (mql && mqlHandler) mql.removeEventListener("change", mqlHandler);
392
- mql = null;
393
- mqlHandler = null;
402
+ if (mediaQueryList && mediaQueryListener)
403
+ mediaQueryList.removeEventListener("change", mediaQueryListener);
404
+ mediaQueryList = null;
405
+ mediaQueryListener = null;
394
406
  listeners.clear();
395
407
  setSignal(null);
396
408
  };
397
- }, [ctx]);
409
+ }, [shaderContext]);
398
410
  return signal ?? STUB_SIGNAL2;
399
411
  }
400
412
 
@@ -409,23 +421,14 @@ function useScroll() {
409
421
  (0, import_react10.useEffect)(() => {
410
422
  if (typeof window === "undefined") return void 0;
411
423
  const compute = () => {
412
- const y = window.scrollY;
424
+ const scrollYPosition = window.scrollY;
413
425
  const max = Math.max(document.documentElement.scrollHeight - window.innerHeight, 1);
414
- const progress = Math.max(0, Math.min(1, y / max));
415
- return [y, progress];
426
+ const progress = Math.max(0, Math.min(1, scrollYPosition / max));
427
+ return [scrollYPosition, progress];
416
428
  };
417
429
  let value = compute();
418
- const listeners = /* @__PURE__ */ new Set();
419
- const fresh = {
420
- get: () => value,
421
- on: (_event, cb) => {
422
- listeners.add(cb);
423
- return () => {
424
- listeners.delete(cb);
425
- };
426
- }
427
- };
428
- setSignal(fresh);
430
+ const { signal: newSignal, listeners } = createSignal(() => value);
431
+ setSignal(newSignal);
429
432
  let rafPending = false;
430
433
  const onScroll = () => {
431
434
  if (rafPending) return;
@@ -435,7 +438,7 @@ function useScroll() {
435
438
  const next = compute();
436
439
  if (next[0] === value[0] && next[1] === value[1]) return;
437
440
  value = next;
438
- for (const cb of listeners) cb(next);
441
+ for (const listener of listeners) listener(next);
439
442
  });
440
443
  };
441
444
  window.addEventListener("scroll", onScroll, { passive: true });
@@ -453,9 +456,9 @@ var import_react11 = require("react");
453
456
  var import_webgpu2 = require("three/webgpu");
454
457
  function useShaderMaterial(build) {
455
458
  const material = (0, import_react11.useMemo)(() => {
456
- const m = new import_webgpu2.MeshBasicNodeMaterial();
457
- m.colorNode = build();
458
- return m;
459
+ const nodeMaterial = new import_webgpu2.MeshBasicNodeMaterial();
460
+ nodeMaterial.colorNode = build();
461
+ return nodeMaterial;
459
462
  }, [build]);
460
463
  (0, import_react11.useEffect)(() => {
461
464
  return () => material.dispose();
@@ -465,13 +468,12 @@ function useShaderMaterial(build) {
465
468
 
466
469
  // src/hooks/use-static-hint/use-static-hint.ts
467
470
  var import_react12 = require("react");
468
- function useStaticHint(hint) {
469
- const ctx = useShaderContext();
471
+ function useStaticSceneHint(isStatic) {
472
+ const shaderContext = useShaderContext();
470
473
  (0, import_react12.useEffect)(() => {
471
- if (!ctx) return;
472
- ctx.scheduler.setIdle(hint);
473
- return () => ctx.scheduler.setIdle(false);
474
- }, [ctx, hint]);
474
+ if (!shaderContext) return;
475
+ return shaderContext.scheduler.setIdle(isStatic);
476
+ }, [shaderContext, isStatic]);
475
477
  }
476
478
  // Annotate the CommonJS export names for ESM import in node:
477
479
  0 && (module.exports = {
@@ -480,11 +482,11 @@ function useStaticHint(hint) {
480
482
  ShaderScene,
481
483
  useAnimatableUniform,
482
484
  useCursor,
483
- useOverlayPass,
485
+ usePostProcessPass,
484
486
  useResize,
485
487
  useScroll,
486
488
  useShaderContext,
487
489
  useShaderMaterial,
488
- useStaticHint
490
+ useStaticSceneHint
489
491
  });
490
492
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/fallback-boundary/fallback-boundary.tsx","../src/components/shader-monitor/shader-monitor.tsx","../src/context/shader-context.ts","../src/components/shader-scene/shader-scene.tsx","../src/hooks/use-animatable-uniform/use-animatable-uniform.ts","../src/hooks/use-cursor/use-cursor.ts","../src/hooks/use-shader-context/use-shader-context.ts","../src/hooks/use-overlay-pass/use-overlay-pass.ts","../src/hooks/use-resize/use-resize.ts","../src/hooks/use-scroll/use-scroll.ts","../src/hooks/use-shader-material/use-shader-material.ts","../src/hooks/use-static-hint/use-static-hint.ts"],"sourcesContent":["// @lovo/matter-react — React binding for Matter.\n\nexport * from './components/index.js'\nexport * from './hooks/index.js'\n\nexport type { ShaderContextValue, OverlayTransform } from './context/shader-context.js'\n","'use client'\n\nimport { type ReactNode, useEffect, useState } from 'react'\n\nexport interface FallbackBoundaryProps {\n /** Rendered until WebGPU/WebGL is available on the client. */\n fallback?: ReactNode\n children: ReactNode\n}\n\n/**\n * Render `fallback` until the component mounts on the client. Gates the\n * children behind client-only mounting so SSR/no-WebGPU users see a\n * sensible static placeholder rather than a flash of nothing.\n */\nexport function FallbackBoundary({ fallback, children }: FallbackBoundaryProps) {\n const [mounted, setMounted] = useState(false)\n\n useEffect(() => {\n setMounted(true)\n }, [])\n\n return <>{mounted ? children : (fallback ?? null)}</>\n}\n","'use client'\n\nimport { type CSSProperties, useContext, useEffect, useRef, useState } from 'react'\n\nimport { ShaderContext } from '../../context/shader-context.js'\n\nexport type MonitorAnchor = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\nconst anchorStyle: Record<MonitorAnchor, CSSProperties> = {\n 'top-left': { top: 8, left: 8 },\n 'top-right': { top: 8, right: 8 },\n 'bottom-left': { bottom: 8, left: 8 },\n 'bottom-right': { bottom: 8, right: 8 },\n}\n\nconst baseStyle: CSSProperties = {\n position: 'absolute',\n zIndex: 10,\n padding: '6px 8px',\n borderRadius: 6,\n background: 'rgba(0, 0, 0, 0.6)',\n color: '#fff',\n font: '11px ui-monospace, monospace',\n lineHeight: 1.4,\n pointerEvents: 'none',\n whiteSpace: 'pre',\n}\n\nexport interface ShaderMonitorProps {\n anchor?: MonitorAnchor\n}\n\n/**\n * Dev-only overlay that displays the current scene's FPS, tick count, and\n * paused/idle state. Reads from the surrounding `<ShaderScene>` via context\n * and subscribes to its scheduler. Renders nothing useful if mounted outside\n * a scene.\n */\nexport function ShaderMonitor({ anchor = 'top-right' }: ShaderMonitorProps) {\n const ctx = useContext(ShaderContext)\n const [stats, setStats] = useState({ fps: 0, ticks: 0, frames: 0 })\n const ticksRef = useRef(0)\n const fpsAccumRef = useRef({ frames: 0, lastSampleAt: 0, fps: 0 })\n\n useEffect(() => {\n if (!ctx) return\n const client = (tick: { now: number }) => {\n ticksRef.current += 1\n const acc = fpsAccumRef.current\n\n acc.frames += 1\n if (acc.lastSampleAt === 0) acc.lastSampleAt = tick.now\n const dt = tick.now - acc.lastSampleAt\n\n if (dt >= 500) {\n acc.fps = Math.round((acc.frames * 1000) / dt)\n acc.frames = 0\n acc.lastSampleAt = tick.now\n }\n setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames })\n }\n\n ctx.scheduler.add(client)\n\n return () => ctx.scheduler.remove(client)\n }, [ctx])\n\n if (!ctx) {\n return (\n <div data-testid=\"matter-monitor\" style={{ ...baseStyle, ...anchorStyle[anchor] }}>\n no scene\n </div>\n )\n }\n\n return (\n <div data-testid=\"matter-monitor\" style={{ ...baseStyle, ...anchorStyle[anchor] }}>\n <span data-testid=\"matter-monitor-fps\">fps: {stats.fps || '—'}</span>\n {'\\n'}\n <span data-testid=\"matter-monitor-ticks\">ticks: {stats.ticks}</span>\n </div>\n )\n}\n","import type { FrameScheduler, GpuRenderer } from '@lovo/matter'\nimport { createContext } from 'react'\nimport type { Camera, Scene } from 'three'\nimport type { ShaderNodeObject } from 'three/tsl'\nimport type { Node } from 'three/webgpu'\n\nexport type OverlayTransform = (input: ShaderNodeObject<Node>) => ShaderNodeObject<Node>\n\nexport interface ShaderContextValue {\n renderer: GpuRenderer\n scene: Scene\n camera: Camera\n scheduler: FrameScheduler\n registerOverlay: (transform: OverlayTransform) => () => void\n}\n\nexport const ShaderContext = createContext<ShaderContextValue | null>(null)\n","'use client'\n\nimport {\n createIntersectionWatcher,\n createRenderer,\n createVisibilityWatcher,\n FrameScheduler,\n} from '@lovo/matter'\nimport { type CSSProperties, type ReactNode, useEffect, useRef, useState } from 'react'\nimport { OrthographicCamera, Scene } from 'three'\nimport { pass } from 'three/tsl'\nimport type { ShaderNodeObject } from 'three/tsl'\nimport { PostProcessing } from 'three/webgpu'\nimport type { Node } from 'three/webgpu'\n\nimport {\n type OverlayTransform,\n ShaderContext,\n type ShaderContextValue,\n} from '../../context/shader-context.js'\n\nexport interface ShaderSceneProps {\n children?: ReactNode\n /** Rendered server-side and during WebGPU init. Default: empty. */\n fallback?: ReactNode\n className?: string\n style?: CSSProperties\n /** Cap on devicePixelRatio. Default: 2. */\n maxDPR?: number\n}\n\nconst defaultStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n display: 'block',\n width: '100%',\n height: '100%',\n}\n\n/**\n * Owns a canvas, a Three.js renderer (WebGPU + WebGL2 fallback), an\n * orthographic camera covering the canvas, an empty Scene, and a\n * FrameScheduler. Children consume these via useShaderContext().\n */\nexport function ShaderScene(props: ShaderSceneProps) {\n const { children, fallback, className, style, maxDPR } = props\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const [ctx, setCtx] = useState<ShaderContextValue | null>(null)\n const [error, setError] = useState<Error | null>(null)\n\n useEffect(() => {\n const canvas = canvasRef.current\n\n if (!canvas) return\n\n let cancelled = false\n let cleanup: (() => void) | null = null\n\n const setup = async () => {\n try {\n const renderer = await createRenderer(canvas, { maxDPR })\n\n if (cancelled) {\n renderer.dispose()\n\n return\n }\n const scene = new Scene()\n const camera = new OrthographicCamera(-1, 1, 1, -1, 0.1, 10)\n\n camera.position.z = 1\n const postProcessing = new PostProcessing(renderer.three)\n const scheduler = new FrameScheduler()\n\n const overlays = new Map<symbol, OverlayTransform>()\n\n // Allocate the base PassNode once per setup so rebuilds reuse the same\n // node identity instead of churning a fresh one (and a fresh render\n // target binding) on every register/unregister.\n const basePass = pass(scene, camera)\n\n const rebuildOutputNode = () => {\n // ShaderNodeObject<PassNode> isn't structurally assignable to\n // ShaderNodeObject<Node> (invariant generic methods); a runtime\n // PassNode is still a Node, so the cast at the reduce seed boundary\n // is safe. See CLAUDE.md gotcha #5.\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n const seed = basePass as unknown as ShaderNodeObject<Node>\n\n postProcessing.outputNode = Array.from(overlays.values()).reduce(\n (node, transform) => transform(node),\n seed,\n )\n postProcessing.needsUpdate = true\n }\n\n rebuildOutputNode() // initial: just basePass, no overlays\n\n const registerOverlay = (transform: OverlayTransform): (() => void) => {\n const key = Symbol('overlay')\n\n overlays.set(key, transform)\n rebuildOutputNode()\n\n return () => {\n overlays.delete(key)\n rebuildOutputNode()\n }\n }\n\n scheduler.add(() => postProcessing.render())\n scheduler.start()\n\n const visibility = createVisibilityWatcher()\n const intersection = createIntersectionWatcher(canvas)\n\n const updatePauseState = () => {\n const shouldRun = visibility.isVisible() && intersection.isInView()\n\n if (shouldRun) scheduler.resume()\n else scheduler.pause()\n }\n\n updatePauseState()\n\n const unsubVisibility = visibility.subscribe(updatePauseState)\n const unsubIntersection = intersection.subscribe(updatePauseState)\n\n const onResize = () => renderer.resize()\n\n window.addEventListener('resize', onResize)\n\n cleanup = () => {\n unsubVisibility()\n unsubIntersection()\n visibility.dispose()\n intersection.dispose()\n window.removeEventListener('resize', onResize)\n scheduler.dispose()\n renderer.dispose()\n }\n\n setCtx({ renderer, scene, camera, scheduler, registerOverlay })\n } catch (err) {\n if (cancelled) return\n const e = err instanceof Error ? err : new Error(String(err))\n\n console.error('[ShaderScene] renderer init failed:', e)\n setError(e)\n }\n }\n\n void setup()\n\n return () => {\n cancelled = true\n cleanup?.()\n cleanup = null\n setCtx(null)\n }\n }, [maxDPR])\n\n let content: ReactNode\n\n if (error) {\n content = (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '1rem',\n color: '#fff',\n background: 'rgba(120, 30, 30, 0.85)',\n font: '0.85rem ui-monospace, monospace',\n whiteSpace: 'pre-wrap',\n textAlign: 'center',\n }}\n >\n ShaderScene init failed:\n {'\\n'}\n {error.message}\n </div>\n )\n } else if (ctx) {\n content = <ShaderContext.Provider value={ctx}>{children}</ShaderContext.Provider>\n } else {\n content = fallback ?? null\n }\n\n return (\n <div className={className} style={{ ...defaultStyle, ...style }}>\n <canvas ref={canvasRef} style={{ width: '100%', height: '100%', display: 'block' }} />\n {content}\n </div>\n )\n}\n","'use client'\n\nimport { useEffect, useMemo } from 'react'\nimport { uniform } from 'three/tsl'\nimport type { ShaderNodeObject } from 'three/tsl'\nimport type { Node } from 'three/webgpu'\n\nexport interface AnimatableSignal<T> {\n get(): T\n on(event: 'change', cb: (value: T) => void): () => void\n}\n\nexport type AnimatableProp<T> = T | AnimatableSignal<T>\n\nconst isSignal = <T>(value: AnimatableProp<T>): value is AnimatableSignal<T> => {\n if (typeof value !== 'object' || value === null) return false\n\n return (\n 'get' in value &&\n typeof value.get === 'function' &&\n 'on' in value &&\n typeof value.on === 'function'\n )\n}\n\n/**\n * Bind an AnimatableProp<T> to a TSL uniform. Plain values create a\n * static uniform that updates only when the prop changes (React render\n * path). Signals subscribe via .on('change') and write into the uniform\n * imperatively without re-rendering.\n *\n * Returns a chainable TSL node with `.value: T` exposed for callers that\n * need to read the current uniform value imperatively (e.g., to compute\n * derived JS-side math). The runtime object is a UniformNode<T>; we\n * present it as `ShaderNodeObject<Node> & { value: T }` because TSL's\n * generic invariance blocks the more precise type from flowing through\n * downstream consumers like OverlayTransform.\n */\nexport function useAnimatableUniform<T>(\n value: AnimatableProp<T>,\n): ShaderNodeObject<Node> & { value: T } {\n // Create the uniform once with the initial value; subsequent updates flow\n // through the effect below (either via signal subscription or direct write).\n const uniformNode = useMemo(() => {\n const initial = isSignal(value) ? value.get() : value\n\n return uniform(initial)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n useEffect(() => {\n if (isSignal(value)) {\n const unsub = value.on('change', (next) => {\n uniformNode.value = next\n })\n\n return unsub\n }\n uniformNode.value = value\n\n return undefined\n }, [value, uniformNode])\n\n // TSL's ShaderNodeObject<UniformNode<T>> isn't structurally assignable to\n // ShaderNodeObject<Node> because the ShaderNodeObject proxy has invariant\n // generic methods (label/etc.). Consumers chain `.mul()`/`.add()` which\n // work on either at runtime; the narrower return type is fine to widen.\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n return uniformNode as unknown as ShaderNodeObject<Node> & { value: T }\n}\n","'use client'\n\nimport { CursorInput, type CursorInputOptions, type Vec2 } from '@lovo/matter'\nimport { useEffect, useState } from 'react'\n\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js'\n\nexport interface CursorSignal {\n /** Current smoothed cursor position (Vec2 in 0..1 viewport space). */\n get(): Vec2\n /** Subscribe to change events. Returns unsubscribe. */\n on(event: 'change', cb: (value: Vec2) => void): () => void\n}\n\n// Inert stub returned on the first render before the lifecycle effect\n// has created the real CursorInput. Calling .on returns an unsub no-op.\nconst STUB_SIGNAL: CursorSignal = {\n get: () => [0.5, 0.5] as const,\n on: () => () => undefined,\n}\n\n/**\n * React wrapper for CursorInput. Auto-attaches to the parent <ShaderScene>'s\n * scheduler if available; otherwise creates a free-running rAF tick.\n *\n * Lifecycle is in a single effect so React 19 Strict Mode's intentional\n * mount→unmount→mount cycle creates a *fresh* CursorInput per real mount\n * instead of disposing a long-lived one (which would silently break the\n * window mousemove listener and the smoothing tick).\n */\nexport function useCursor(opts: CursorInputOptions = {}): CursorSignal {\n const ctx = useShaderContext()\n const [input, setInput] = useState<CursorInput | null>(null)\n\n useEffect(() => {\n // Plumb the parent <ShaderScene>'s canvas as the cursor's normalization\n // element. Without this, cursor coords are viewport-normalized — fine for\n // a full-page scene but visibly offset when the canvas sits inside a\n // smaller wrapper (e.g., 70vh hero). DotField's cell tiling makes the\n // mismatch obvious; LinearGradient mostly gets away with it. Caller can\n // override by passing `opts.element` explicitly.\n const canvas = ctx?.renderer.three.domElement\n const elementOpt = opts.element ?? (canvas instanceof HTMLElement ? canvas : undefined)\n const fresh = new CursorInput({ ...opts, element: elementOpt })\n\n setInput(fresh)\n\n let detach: (() => void) | null = null\n\n if (ctx?.scheduler) {\n const client = ({ delta }: { delta: number }) => fresh.tick(delta)\n\n ctx.scheduler.add(client)\n detach = () => ctx.scheduler.remove(client)\n } else {\n let raf: number | null = null\n let lastNow = performance.now()\n const loop = (now: number) => {\n const delta = (now - lastNow) / 1000\n\n lastNow = now\n fresh.tick(delta)\n raf = requestAnimationFrame(loop)\n }\n\n raf = requestAnimationFrame(loop)\n detach = () => {\n if (raf !== null) cancelAnimationFrame(raf)\n }\n }\n\n return () => {\n detach()\n fresh.dispose()\n setInput(null)\n }\n // We intentionally only re-create on ctx change, not opts (which is a\n // fresh object literal each render). Smoothing tweaks during dev are\n // applied by remounting the parent component.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ctx])\n\n return input ?? STUB_SIGNAL\n}\n","import { useContext } from 'react'\n\nimport { ShaderContext, type ShaderContextValue } from '../../context/shader-context.js'\n\n/**\n * Read the shader scene context. Returns null when called outside a\n * <ShaderScene>; useShaderMaterial and similar hooks check this.\n */\nexport function useShaderContext(): ShaderContextValue | null {\n return useContext(ShaderContext)\n}\n","'use client'\n\nimport { type DependencyList, useEffect } from 'react'\n\nimport type { OverlayTransform } from '../../context/shader-context.js'\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js'\n\n/**\n * Register a TSL transform as an overlay pass on the parent <ShaderScene>.\n *\n * The transform takes the \"color so far\" — base scene + any earlier\n * overlays as a TSL vec4 node — and returns a modified vec4. Registration\n * happens on mount; unregistration on unmount. The hook re-registers\n * whenever any value in `deps` changes (useEffect semantics): use this\n * for structural changes (e.g., a `mode: 'additive' | 'subtractive'`\n * toggle) that swap the transform function itself. Uniforms captured\n * inside the transform mutate in place, so uniform value changes do\n * NOT need to be in deps.\n *\n * When called outside a <ShaderScene> provider, this hook is a no-op.\n * Matches the existing useShaderContext convention.\n */\nexport function useOverlayPass(transform: OverlayTransform, deps: DependencyList): void {\n const ctx = useShaderContext()\n\n useEffect(() => {\n if (!ctx) return\n const unregister = ctx.registerOverlay(transform)\n\n return unregister\n // The transform captures the latest values via the deps array; we re-register\n // when deps change. ctx is included so a remounted ShaderScene re-attaches.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ctx, ...deps])\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js'\n\nexport type ResizeValue = readonly [width: number, height: number, dpr: number]\n\nexport interface ResizeSignal {\n /** Current size in CSS pixels + devicePixelRatio. */\n get(): ResizeValue\n on(event: 'change', cb: (value: ResizeValue) => void): () => void\n}\n\n// Inert stub returned on the first render before the lifecycle effect has\n// observed the canvas. Subscribing to it returns a no-op unsubscribe.\nconst STUB_SIGNAL: ResizeSignal = {\n get: () => [0, 0, 1] as const,\n on: () => () => undefined,\n}\n\n/**\n * Track the parent <ShaderScene>'s canvas size + DPR. Exposes an AnimatableSignal\n * that components can pass into a TSL uniform to make pixel-aware effects\n * (e.g., DotField's pixel-spacing math).\n *\n * Strict-Mode-safe: lifecycle is in one effect, so React 19's intentional\n * mount→unmount→mount cycle creates a fresh ResizeObserver per real mount\n * (CLAUDE.md gotcha #14).\n *\n * Falls back to the stub signal until the parent context is ready.\n */\nexport function useResize(): ResizeSignal {\n const ctx = useShaderContext()\n const [signal, setSignal] = useState<ResizeSignal | null>(null)\n\n useEffect(() => {\n if (!ctx) return undefined\n\n const canvas = ctx.renderer.three.domElement\n\n if (!(canvas instanceof HTMLCanvasElement)) return undefined\n\n let value: ResizeValue = [\n canvas.clientWidth,\n canvas.clientHeight,\n typeof window !== 'undefined' ? window.devicePixelRatio : 1,\n ]\n const listeners = new Set<(v: ResizeValue) => void>()\n const fresh: ResizeSignal = {\n get: () => value,\n on: (_event, cb) => {\n listeners.add(cb)\n\n return () => {\n listeners.delete(cb)\n }\n },\n }\n\n setSignal(fresh)\n\n const emit = () => {\n const next: ResizeValue = [\n canvas.clientWidth,\n canvas.clientHeight,\n typeof window !== 'undefined' ? window.devicePixelRatio : 1,\n ]\n\n if (next[0] === value[0] && next[1] === value[1] && next[2] === value[2]) return\n value = next\n for (const cb of listeners) cb(next)\n }\n\n const observer = new ResizeObserver(emit)\n\n observer.observe(canvas)\n\n // Cross-browser DPR-change watch. matchMedia(`(resolution: <dpr>dppx)`)\n // matches at the *current* DPR; when the user zooms the page the query\n // stops matching, fires `change`, and we re-arm the watch at the new DPR.\n // We track the current MQL + handler so we can fully detach in cleanup\n // (the handler is captured by the listener — passing a fresh closure to\n // removeEventListener wouldn't actually unregister it).\n let mql: MediaQueryList | null = null\n let mqlHandler: (() => void) | null = null\n const setupDprWatch = () => {\n if (typeof window === 'undefined') return\n const dpr = window.devicePixelRatio\n const next = window.matchMedia(`(resolution: ${dpr}dppx)`)\n const handler = () => {\n emit()\n if (mql && mqlHandler) mql.removeEventListener('change', mqlHandler)\n setupDprWatch()\n }\n\n next.addEventListener('change', handler)\n mql = next\n mqlHandler = handler\n }\n\n setupDprWatch()\n\n return () => {\n observer.disconnect()\n if (mql && mqlHandler) mql.removeEventListener('change', mqlHandler)\n mql = null\n mqlHandler = null\n listeners.clear()\n setSignal(null)\n }\n }, [ctx])\n\n return signal ?? STUB_SIGNAL\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\n\nexport type ScrollValue = readonly [scrollY: number, progress: number]\n\nexport interface ScrollSignal {\n /** Current scroll Y (px) and normalized progress in [0,1]. */\n get(): ScrollValue\n on(event: 'change', cb: (value: ScrollValue) => void): () => void\n}\n\n// Inert stub returned during SSR + on the first client render before the\n// lifecycle effect attaches. Subscribing to it returns a no-op unsubscribe.\nconst STUB_SIGNAL: ScrollSignal = {\n get: () => [0, 0] as const,\n on: () => () => undefined,\n}\n\n/**\n * Track window scroll position. Exposes an AnimatableSignal of `[scrollY, progress]`\n * where `progress` is `scrollY / max(documentHeight - innerHeight, 1)` clamped\n * to [0, 1]. Listener is rAF-throttled and `passive: true` so it never blocks\n * scrolling.\n *\n * No v1 Tier 1 component consumes this hook; it ships so users can pass\n * `inputs={{ scroll: useScroll() }}` to any Matter component.\n *\n * Strict-Mode-safe: lifecycle is in one effect, so React 19's intentional\n * mount→unmount→mount cycle in dev creates a fresh listener pair per real\n * mount and tears down cleanly on each pseudo-unmount (CLAUDE.md gotcha #14).\n *\n * **Known limitation (v1):** `progress` is computed against whichever\n * `documentHeight` was current when the last scroll fired. If the page grows\n * after mount (async content, font load reflow, expanding panels) without\n * the user scrolling, the denominator goes stale. A future ResizeObserver/\n * MutationObserver pass would close the gap; deferred until a v1 component\n * consumes scroll input.\n */\nexport function useScroll(): ScrollSignal {\n const [signal, setSignal] = useState<ScrollSignal | null>(null)\n\n useEffect(() => {\n if (typeof window === 'undefined') return undefined\n\n const compute = (): ScrollValue => {\n const y = window.scrollY\n // For pages shorter than the viewport, `documentHeight - innerHeight` is\n // <= 0; clamp to 1 to avoid div-by-zero. Progress stays at 0 in that\n // case because scrollY is also 0.\n const max = Math.max(document.documentElement.scrollHeight - window.innerHeight, 1)\n const progress = Math.max(0, Math.min(1, y / max))\n\n return [y, progress]\n }\n\n let value: ScrollValue = compute()\n const listeners = new Set<(v: ScrollValue) => void>()\n const fresh: ScrollSignal = {\n get: () => value,\n on: (_event, cb) => {\n listeners.add(cb)\n\n return () => {\n listeners.delete(cb)\n }\n },\n }\n\n setSignal(fresh)\n\n let rafPending = false\n const onScroll = () => {\n if (rafPending) return\n rafPending = true\n requestAnimationFrame(() => {\n rafPending = false\n const next = compute()\n\n if (next[0] === value[0] && next[1] === value[1]) return\n value = next\n for (const cb of listeners) cb(next)\n })\n }\n\n window.addEventListener('scroll', onScroll, { passive: true })\n\n return () => {\n window.removeEventListener('scroll', onScroll)\n listeners.clear()\n setSignal(null)\n }\n }, [])\n\n return signal ?? STUB_SIGNAL\n}\n","'use client'\n\nimport { useEffect, useMemo } from 'react'\nimport type { ShaderNodeObject } from 'three/tsl'\nimport { MeshBasicNodeMaterial } from 'three/webgpu'\nimport type { Node } from 'three/webgpu'\n\n/** A TSL fragment that produces a color. Accept any Node or TSL-wrapped node. */\nexport type ColorTSL = Node | ShaderNodeObject<Node>\n\n/**\n * Bind a TSL color expression to a NodeMaterial. Returns the material;\n * caller is responsible for adding it to a mesh and disposing when done.\n *\n * The TSL fragment is computed once via `useMemo` and re-applied if the\n * factory function changes. For dynamic uniforms, mutate `.value` on the\n * uniform nodes — don't recreate the TSL fragment per render.\n */\nexport function useShaderMaterial(build: () => ColorTSL): MeshBasicNodeMaterial {\n const material = useMemo(() => {\n const m = new MeshBasicNodeMaterial()\n\n m.colorNode = build()\n\n return m\n }, [build])\n\n useEffect(() => {\n return () => material.dispose()\n }, [material])\n\n return material\n}\n","'use client'\n\nimport { useEffect } from 'react'\n\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js'\n\n/**\n * Opt a component out of the rAF loop while it has no dynamic uniforms.\n *\n * When `hint` is true, the scheduler runs one final flush tick (so any\n * uniform changes since the last frame are rendered) and then halts the\n * rAF loop until either `hint` becomes false or another component in the\n * same scene calls `scheduler.requestRender()`.\n *\n * Use for components whose animation is fully derived from props that don't\n * include `time`, e.g. `<LinearGradient speed={0}>` with no `interactive`.\n */\nexport function useStaticHint(hint: boolean): void {\n const ctx = useShaderContext()\n\n useEffect(() => {\n if (!ctx) return\n ctx.scheduler.setIdle(hint)\n\n return () => ctx.scheduler.setIdle(false)\n }, [ctx, hint])\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_tsl","import_matter","import_react","import_react","import_react","import_react","STUB_SIGNAL","import_react","STUB_SIGNAL","import_react","import_webgpu","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/fallback-boundary/fallback-boundary.tsx","../src/components/shader-monitor/shader-monitor.tsx","../src/context/shader-context.ts","../src/components/shader-scene/shader-scene.tsx","../src/hooks/use-animatable-uniform/use-animatable-uniform.ts","../src/hooks/use-cursor/use-cursor.ts","../src/hooks/use-shader-context/use-shader-context.ts","../src/hooks/use-overlay-pass/use-overlay-pass.ts","../src/hooks/use-resize/use-resize.ts","../src/internal/create-signal.ts","../src/hooks/use-scroll/use-scroll.ts","../src/hooks/use-shader-material/use-shader-material.ts","../src/hooks/use-static-hint/use-static-hint.ts"],"sourcesContent":["// @lovo/matter-react — React binding for Matter.\n\nexport * from './components/index.js';\nexport * from './hooks/index.js';\n\nexport type { ShaderContextValue, PostProcessTransform } from './context/shader-context.js';\n","'use client';\n\nimport { type ReactNode, useEffect, useState } from 'react';\n\nexport interface FallbackBoundaryProps {\n fallback?: ReactNode;\n children: ReactNode;\n}\n\nexport function FallbackBoundary({ fallback, children }: FallbackBoundaryProps) {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n return <>{mounted ? children : (fallback ?? null)}</>;\n}\n","'use client';\n\nimport { type CSSProperties, useContext, useEffect, useRef, useState } from 'react';\n\nimport { ShaderContext } from '../../context/shader-context.js';\n\nexport type ShaderMonitorAnchor = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\nconst anchorStyle: Record<ShaderMonitorAnchor, CSSProperties> = {\n 'top-left': { top: 8, left: 8 },\n 'top-right': { top: 8, right: 8 },\n 'bottom-left': { bottom: 8, left: 8 },\n 'bottom-right': { bottom: 8, right: 8 },\n};\n\nconst baseStyle: CSSProperties = {\n position: 'absolute',\n zIndex: 10,\n padding: '6px 8px',\n borderRadius: 6,\n background: 'rgba(0, 0, 0, 0.6)',\n color: '#fff',\n font: '11px ui-monospace, monospace',\n lineHeight: 1.4,\n pointerEvents: 'none',\n whiteSpace: 'pre',\n};\n\nexport interface ShaderMonitorProps {\n anchor?: ShaderMonitorAnchor;\n}\n\nexport function ShaderMonitor({ anchor = 'top-right' }: ShaderMonitorProps) {\n const shaderContext = useContext(ShaderContext);\n const [stats, setStats] = useState({ fps: 0, ticks: 0, frames: 0 });\n const ticksRef = useRef(0);\n const fpsAccumRef = useRef({ frames: 0, lastSampleAt: 0, fps: 0 });\n\n useEffect(() => {\n if (!shaderContext) return;\n const schedulerTickHandler = (tick: { now: number }) => {\n ticksRef.current += 1;\n const fpsAccumulator = fpsAccumRef.current;\n\n fpsAccumulator.frames += 1;\n if (fpsAccumulator.lastSampleAt === 0) fpsAccumulator.lastSampleAt = tick.now;\n const deltaTimeSinceLastSample = tick.now - fpsAccumulator.lastSampleAt;\n\n if (deltaTimeSinceLastSample >= 500) {\n fpsAccumulator.fps = Math.round((fpsAccumulator.frames * 1000) / deltaTimeSinceLastSample);\n fpsAccumulator.frames = 0;\n fpsAccumulator.lastSampleAt = tick.now;\n }\n setStats({ fps: fpsAccumulator.fps, ticks: ticksRef.current, frames: fpsAccumulator.frames });\n };\n\n shaderContext.scheduler.add(schedulerTickHandler);\n\n return () => shaderContext.scheduler.remove(schedulerTickHandler);\n }, [shaderContext]);\n\n if (!shaderContext) {\n return (\n <div data-testid=\"matter-monitor\" style={{ ...baseStyle, ...anchorStyle[anchor] }}>\n no scene\n </div>\n );\n }\n\n return (\n <div data-testid=\"matter-monitor\" style={{ ...baseStyle, ...anchorStyle[anchor] }}>\n <span data-testid=\"matter-monitor-fps\">fps: {stats.fps || '—'}</span>\n {'\\n'}\n <span data-testid=\"matter-monitor-ticks\">ticks: {stats.ticks}</span>\n </div>\n );\n}\n","import { createContext } from 'react';\n\nimport type { FrameScheduler, GpuRenderer } from '@lovo/matter';\nimport type { Camera, Scene } from 'three';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nexport type PostProcessTransform = (input: ShaderNodeObject<Node>) => ShaderNodeObject<Node>;\n\nexport interface ShaderContextValue {\n renderer: GpuRenderer;\n scene: Scene;\n camera: Camera;\n scheduler: FrameScheduler;\n registerOverlay: (transform: PostProcessTransform) => () => void;\n}\n\nexport const ShaderContext = createContext<ShaderContextValue | null>(null);\n","'use client';\n\nimport { type CSSProperties, type ReactNode, useEffect, useRef, useState } from 'react';\n\nimport {\n createIntersectionWatcher,\n createRenderer,\n createVisibilityWatcher,\n FrameScheduler,\n} from '@lovo/matter';\nimport { OrthographicCamera, Scene } from 'three';\nimport { pass } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport { PostProcessing } from 'three/webgpu';\nimport type { Node } from 'three/webgpu';\n\nimport {\n type PostProcessTransform,\n ShaderContext,\n type ShaderContextValue,\n} from '../../context/shader-context.js';\n\nexport interface ShaderSceneProps {\n children?: ReactNode;\n fallback?: ReactNode;\n className?: string;\n style?: CSSProperties;\n maxDPR?: number;\n}\n\nconst defaultStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n display: 'block',\n width: '100%',\n height: '100%',\n};\n\nexport function ShaderScene(props: ShaderSceneProps) {\n const { children, fallback, className, style, maxDPR } = props;\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const [shaderContext, setShaderContext] = useState<ShaderContextValue | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n\n if (!canvas) return;\n\n let cancelled = false;\n let cleanup: (() => void) | null = null;\n\n const setup = async () => {\n try {\n const renderer = await createRenderer(canvas, { maxDPR });\n\n if (cancelled) {\n renderer.dispose();\n\n return;\n }\n const scene = new Scene();\n const camera = new OrthographicCamera(-1, 1, 1, -1, 0.1, 10);\n\n camera.position.z = 1;\n const postProcessing = new PostProcessing(renderer.three);\n const scheduler = new FrameScheduler();\n\n const overlays = new Map<symbol, PostProcessTransform>();\n\n const basePass = pass(scene, camera);\n\n const rebuildOutputNode = () => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n const basePassNode = basePass as unknown as ShaderNodeObject<Node>;\n\n postProcessing.outputNode = Array.from(overlays.values()).reduce(\n (currentPipeline, transform) => transform(currentPipeline),\n basePassNode,\n );\n postProcessing.needsUpdate = true;\n };\n\n rebuildOutputNode(); // initial: just basePass, no overlays\n\n const registerOverlay = (transform: PostProcessTransform): (() => void) => {\n const key = Symbol('overlay');\n\n overlays.set(key, transform);\n rebuildOutputNode();\n\n return () => {\n overlays.delete(key);\n rebuildOutputNode();\n };\n };\n\n scheduler.add(() => postProcessing.render());\n scheduler.start();\n\n const visibility = createVisibilityWatcher();\n const intersection = createIntersectionWatcher(canvas);\n\n const updatePauseState = () => {\n const shouldRun = visibility.isVisible() && intersection.isInView();\n\n if (shouldRun) scheduler.resume();\n else scheduler.pause();\n };\n\n updatePauseState();\n\n const unsubVisibility = visibility.subscribe(updatePauseState);\n const unsubIntersection = intersection.subscribe(updatePauseState);\n\n const onResize = () => renderer.resize();\n\n window.addEventListener('resize', onResize);\n\n cleanup = () => {\n unsubVisibility();\n unsubIntersection();\n visibility.dispose();\n intersection.dispose();\n window.removeEventListener('resize', onResize);\n scheduler.dispose();\n renderer.dispose();\n };\n\n setShaderContext({ renderer, scene, camera, scheduler, registerOverlay });\n } catch (caughtError) {\n if (cancelled) return;\n const normalizedError =\n caughtError instanceof Error ? caughtError : new Error(String(caughtError));\n\n console.error('[ShaderScene] renderer init failed:', normalizedError);\n setError(normalizedError);\n }\n };\n\n void setup();\n\n return () => {\n cancelled = true;\n cleanup?.();\n cleanup = null;\n setShaderContext(null);\n };\n }, [maxDPR]);\n\n let content: ReactNode;\n\n if (error) {\n content = (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '1rem',\n color: '#fff',\n background: 'rgba(120, 30, 30, 0.85)',\n font: '0.85rem ui-monospace, monospace',\n whiteSpace: 'pre-wrap',\n textAlign: 'center',\n }}\n >\n ShaderScene init failed:\n {'\\n'}\n {error.message}\n </div>\n );\n } else if (shaderContext) {\n content = <ShaderContext.Provider value={shaderContext}>{children}</ShaderContext.Provider>;\n } else {\n content = fallback ?? null;\n }\n\n return (\n <div className={className} style={{ ...defaultStyle, ...style }}>\n <canvas ref={canvasRef} style={{ width: '100%', height: '100%', display: 'block' }} />\n {content}\n </div>\n );\n}\n","'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport { uniform } from 'three/tsl';\nimport type { ShaderNodeObject } from 'three/tsl';\nimport type { Node } from 'three/webgpu';\n\nexport interface AnimatableSignal<T> {\n get(): T;\n on(event: 'change', cb: (value: T) => void): () => void;\n}\n\nexport type AnimatableProp<T> = T | AnimatableSignal<T>;\n\nconst isSignal = <T>(value: AnimatableProp<T>): value is AnimatableSignal<T> => {\n if (typeof value !== 'object' || value === null) return false;\n\n return (\n 'get' in value &&\n typeof value.get === 'function' &&\n 'on' in value &&\n typeof value.on === 'function'\n );\n};\n\nexport function useAnimatableUniform<T>(\n value: AnimatableProp<T>,\n): ShaderNodeObject<Node> & { value: T } {\n const uniformNode = useMemo(() => {\n const initial = isSignal(value) ? value.get() : value;\n\n return uniform(initial);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (isSignal(value)) {\n const unsub = value.on('change', (next) => {\n uniformNode.value = next;\n });\n\n return unsub;\n }\n uniformNode.value = value;\n\n return undefined;\n }, [value, uniformNode]);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n return uniformNode as unknown as ShaderNodeObject<Node> & { value: T };\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { CursorInput, type CursorInputOptions, type Vector2 } from '@lovo/matter';\n\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js';\n\nexport interface CursorSignal {\n get(): Vector2;\n on(event: 'change', cb: (value: Vector2) => void): () => void;\n}\n\nconst STUB_SIGNAL: CursorSignal = {\n get: () => [0.5, 0.5] as const,\n on: () => () => undefined,\n};\n\nexport function useCursor(opts: CursorInputOptions = {}): CursorSignal {\n const shaderContext = useShaderContext();\n const [input, setInput] = useState<CursorInput | null>(null);\n\n useEffect(() => {\n const canvas = shaderContext?.renderer.three.domElement;\n const resolvedElement = opts.element ?? (canvas instanceof HTMLElement ? canvas : undefined);\n const newCursorInput = new CursorInput({ ...opts, element: resolvedElement });\n\n setInput(newCursorInput);\n\n let detach: (() => void) | null = null;\n\n if (shaderContext?.scheduler) {\n const schedulerTickHandler = ({ delta }: { delta: number }) => newCursorInput.tick(delta);\n\n shaderContext.scheduler.add(schedulerTickHandler);\n detach = () => shaderContext.scheduler.remove(schedulerTickHandler);\n } else {\n let animationFrameId: number | null = null;\n let lastNow = performance.now();\n const loop = (now: number) => {\n const delta = (now - lastNow) / 1000;\n\n lastNow = now;\n newCursorInput.tick(delta);\n animationFrameId = requestAnimationFrame(loop);\n };\n\n animationFrameId = requestAnimationFrame(loop);\n detach = () => {\n if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);\n };\n }\n\n return () => {\n detach();\n newCursorInput.dispose();\n setInput(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shaderContext]);\n\n return input ?? STUB_SIGNAL;\n}\n","import { useContext } from 'react';\n\nimport { ShaderContext, type ShaderContextValue } from '../../context/shader-context.js';\n\nexport function useShaderContext(): ShaderContextValue | null {\n return useContext(ShaderContext);\n}\n","'use client';\n\nimport { type DependencyList, useEffect } from 'react';\n\nimport type { PostProcessTransform } from '../../context/shader-context.js';\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js';\n\nexport function usePostProcessPass(transform: PostProcessTransform, deps: DependencyList): void {\n const shaderContext = useShaderContext();\n\n useEffect(() => {\n if (!shaderContext) return;\n const unregister = shaderContext.registerOverlay(transform);\n\n return unregister;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shaderContext, ...deps]);\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { createSignal } from '../../internal/create-signal.js';\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js';\n\nexport type ResizeValue = readonly [width: number, height: number, dpr: number];\n\nexport interface ResizeSignal {\n get(): ResizeValue;\n on(event: 'change', cb: (value: ResizeValue) => void): () => void;\n}\n\nconst STUB_SIGNAL: ResizeSignal = {\n get: () => [0, 0, 1] as const,\n on: () => () => undefined,\n};\n\nexport function useResize(): ResizeSignal {\n const shaderContext = useShaderContext();\n const [signal, setSignal] = useState<ResizeSignal | null>(null);\n\n useEffect(() => {\n if (!shaderContext) return undefined;\n\n const canvas = shaderContext.renderer.three.domElement;\n\n if (!(canvas instanceof HTMLCanvasElement)) return undefined;\n\n let value: ResizeValue = [\n canvas.clientWidth,\n canvas.clientHeight,\n typeof window !== 'undefined' ? window.devicePixelRatio : 1,\n ];\n const { signal: newSignal, listeners } = createSignal<ResizeValue>(() => value);\n\n setSignal(newSignal);\n\n const emit = () => {\n const next: ResizeValue = [\n canvas.clientWidth,\n canvas.clientHeight,\n typeof window !== 'undefined' ? window.devicePixelRatio : 1,\n ];\n\n if (next[0] === value[0] && next[1] === value[1] && next[2] === value[2]) return;\n value = next;\n for (const listener of listeners) listener(next);\n };\n\n const observer = new ResizeObserver(emit);\n\n observer.observe(canvas);\n\n let mediaQueryList: MediaQueryList | null = null;\n let mediaQueryListener: (() => void) | null = null;\n const setupDprWatch = () => {\n if (typeof window === 'undefined') return;\n const dpr = window.devicePixelRatio;\n const nextMediaQueryList = window.matchMedia(`(resolution: ${dpr}dppx)`);\n const nextMediaQueryListener = () => {\n emit();\n if (mediaQueryList && mediaQueryListener)\n mediaQueryList.removeEventListener('change', mediaQueryListener);\n setupDprWatch();\n };\n\n nextMediaQueryList.addEventListener('change', nextMediaQueryListener);\n mediaQueryList = nextMediaQueryList;\n mediaQueryListener = nextMediaQueryListener;\n };\n\n setupDprWatch();\n\n return () => {\n observer.disconnect();\n if (mediaQueryList && mediaQueryListener)\n mediaQueryList.removeEventListener('change', mediaQueryListener);\n mediaQueryList = null;\n mediaQueryListener = null;\n listeners.clear();\n setSignal(null);\n };\n }, [shaderContext]);\n\n return signal ?? STUB_SIGNAL;\n}\n","'use client';\n\nexport function createSignal<T>(getValue: () => T): {\n signal: { get(): T; on(event: string, listener: (v: T) => void): () => void };\n listeners: Set<(v: T) => void>;\n} {\n const listeners = new Set<(v: T) => void>();\n\n return {\n listeners,\n signal: {\n get: getValue,\n on: (_event, listener) => {\n listeners.add(listener);\n\n return () => {\n listeners.delete(listener);\n };\n },\n },\n };\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { createSignal } from '../../internal/create-signal.js';\n\nexport type ScrollValue = readonly [scrollY: number, progress: number];\n\nexport interface ScrollSignal {\n get(): ScrollValue;\n on(event: 'change', cb: (value: ScrollValue) => void): () => void;\n}\n\nconst STUB_SIGNAL: ScrollSignal = {\n get: () => [0, 0] as const,\n on: () => () => undefined,\n};\n\nexport function useScroll(): ScrollSignal {\n const [signal, setSignal] = useState<ScrollSignal | null>(null);\n\n useEffect(() => {\n if (typeof window === 'undefined') return undefined;\n\n const compute = (): ScrollValue => {\n const scrollYPosition = window.scrollY;\n\n const max = Math.max(document.documentElement.scrollHeight - window.innerHeight, 1);\n const progress = Math.max(0, Math.min(1, scrollYPosition / max));\n\n return [scrollYPosition, progress];\n };\n\n let value: ScrollValue = compute();\n const { signal: newSignal, listeners } = createSignal<ScrollValue>(() => value);\n\n setSignal(newSignal);\n\n let rafPending = false;\n const onScroll = () => {\n if (rafPending) return;\n rafPending = true;\n requestAnimationFrame(() => {\n rafPending = false;\n const next = compute();\n\n if (next[0] === value[0] && next[1] === value[1]) return;\n value = next;\n for (const listener of listeners) listener(next);\n });\n };\n\n window.addEventListener('scroll', onScroll, { passive: true });\n\n return () => {\n window.removeEventListener('scroll', onScroll);\n listeners.clear();\n setSignal(null);\n };\n }, []);\n\n return signal ?? STUB_SIGNAL;\n}\n","'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport type { ShaderNodeObject } from 'three/tsl';\nimport { MeshBasicNodeMaterial } from 'three/webgpu';\nimport type { Node } from 'three/webgpu';\n\nexport type ColorTSL = Node | ShaderNodeObject<Node>;\n\nexport function useShaderMaterial(build: () => ColorTSL): MeshBasicNodeMaterial {\n const material = useMemo(() => {\n const nodeMaterial = new MeshBasicNodeMaterial();\n\n nodeMaterial.colorNode = build();\n\n return nodeMaterial;\n }, [build]);\n\n useEffect(() => {\n return () => material.dispose();\n }, [material]);\n\n return material;\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport { useShaderContext } from '../use-shader-context/use-shader-context.js';\n\nexport function useStaticSceneHint(isStatic: boolean): void {\n const shaderContext = useShaderContext();\n\n useEffect(() => {\n if (!shaderContext) return;\n\n return shaderContext.scheduler.setIdle(isStatic);\n }, [shaderContext, isStatic]);\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_tsl","import_react","import_matter","import_react","import_react","import_react","STUB_SIGNAL","import_react","STUB_SIGNAL","import_react","import_webgpu","import_react"]}