@lovo/matter-react 0.4.1 → 0.6.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.6.0
4
+
5
+ ## 0.5.0
6
+
7
+ ### Minor Changes
8
+
9
+ - 35274c3: Rename ambiguous `@lovo/matter-react` public exports to clearer names (BREAKING, pre-1.0):
10
+
11
+ - `useOverlayPass` → `usePostProcessPass` (and the paired type `OverlayTransform` → `PostProcessTransform`)
12
+ - `useStaticHint` → `useStaticSceneHint`
13
+ - `MonitorAnchor` (type) → `ShaderMonitorAnchor`
14
+
15
+ Migration: update imports and call sites to the new names. Behavior is unchanged.
16
+
3
17
  ## 0.4.1
4
18
 
5
19
  ## 0.4.0
@@ -23,13 +37,13 @@
23
37
  **New: `useOverlayPass(transform, deps)` hook**
24
38
 
25
39
  ```ts
26
- import { useOverlayPass, useAnimatableUniform } from "@lovo/matter-react";
40
+ import { useAnimatableUniform, useOverlayPass } from '@lovo/matter-react';
27
41
 
28
42
  export function MyOverlay({ intensity }) {
29
43
  const intensityU = useAnimatableUniform(intensity);
30
44
  useOverlayPass(
31
45
  (input) => input.mul(intensityU), // takes upstream pixel, returns modified pixel
32
- [intensityU]
46
+ [intensityU],
33
47
  );
34
48
  return null;
35
49
  }
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,13 @@ __export(index_exports, {
25
25
  ShaderScene: () => ShaderScene,
26
26
  useAnimatableUniform: () => useAnimatableUniform,
27
27
  useCursor: () => useCursor,
28
- useOverlayPass: () => useOverlayPass,
28
+ useDisplayGamut: () => useDisplayGamut,
29
+ usePostProcessPass: () => usePostProcessPass,
29
30
  useResize: () => useResize,
30
31
  useScroll: () => useScroll,
31
32
  useShaderContext: () => useShaderContext,
32
33
  useShaderMaterial: () => useShaderMaterial,
33
- useStaticHint: () => useStaticHint
34
+ useStaticSceneHint: () => useStaticSceneHint
34
35
  });
35
36
  module.exports = __toCommonJS(index_exports);
36
37
 
@@ -73,29 +74,29 @@ var baseStyle = {
73
74
  whiteSpace: "pre"
74
75
  };
75
76
  function ShaderMonitor({ anchor = "top-right" }) {
76
- const ctx = (0, import_react3.useContext)(ShaderContext);
77
+ const shaderContext = (0, import_react3.useContext)(ShaderContext);
77
78
  const [stats, setStats] = (0, import_react3.useState)({ fps: 0, ticks: 0, frames: 0 });
78
79
  const ticksRef = (0, import_react3.useRef)(0);
79
80
  const fpsAccumRef = (0, import_react3.useRef)({ frames: 0, lastSampleAt: 0, fps: 0 });
80
81
  (0, import_react3.useEffect)(() => {
81
- if (!ctx) return;
82
- const client = (tick) => {
82
+ if (!shaderContext) return;
83
+ const schedulerTickHandler = (tick) => {
83
84
  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;
85
+ const fpsAccumulator = fpsAccumRef.current;
86
+ fpsAccumulator.frames += 1;
87
+ if (fpsAccumulator.lastSampleAt === 0) fpsAccumulator.lastSampleAt = tick.now;
88
+ const deltaTimeSinceLastSample = tick.now - fpsAccumulator.lastSampleAt;
89
+ if (deltaTimeSinceLastSample >= 500) {
90
+ fpsAccumulator.fps = Math.round(fpsAccumulator.frames * 1e3 / deltaTimeSinceLastSample);
91
+ fpsAccumulator.frames = 0;
92
+ fpsAccumulator.lastSampleAt = tick.now;
92
93
  }
93
- setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames });
94
+ setStats({ fps: fpsAccumulator.fps, ticks: ticksRef.current, frames: fpsAccumulator.frames });
94
95
  };
95
- ctx.scheduler.add(client);
96
- return () => ctx.scheduler.remove(client);
97
- }, [ctx]);
98
- if (!ctx) {
96
+ shaderContext.scheduler.add(schedulerTickHandler);
97
+ return () => shaderContext.scheduler.remove(schedulerTickHandler);
98
+ }, [shaderContext]);
99
+ if (!shaderContext) {
99
100
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
100
101
  }
101
102
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
@@ -112,11 +113,44 @@ function ShaderMonitor({ anchor = "top-right" }) {
112
113
  }
113
114
 
114
115
  // src/components/shader-scene/shader-scene.tsx
116
+ var import_react5 = require("react");
115
117
  var import_matter = require("@lovo/matter");
116
- var import_react4 = require("react");
117
118
  var import_three = require("three");
118
119
  var import_tsl = require("three/tsl");
119
120
  var import_webgpu = require("three/webgpu");
121
+
122
+ // src/hooks/use-display-gamut/use-display-gamut.ts
123
+ var import_react4 = require("react");
124
+ var P3_QUERY = "(color-gamut: p3)";
125
+ function detectGamut() {
126
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
127
+ return "srgb";
128
+ }
129
+ return window.matchMedia(P3_QUERY).matches ? "p3" : "srgb";
130
+ }
131
+ function useDisplayGamut(preference) {
132
+ const [resolved, setResolved] = (0, import_react4.useState)(
133
+ () => preference === "auto" ? detectGamut() : preference
134
+ );
135
+ (0, import_react4.useEffect)(() => {
136
+ if (preference !== "auto") {
137
+ setResolved(preference);
138
+ return;
139
+ }
140
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
141
+ setResolved("srgb");
142
+ return;
143
+ }
144
+ const mediaQuery = window.matchMedia(P3_QUERY);
145
+ const update = () => setResolved(mediaQuery.matches ? "p3" : "srgb");
146
+ update();
147
+ mediaQuery.addEventListener("change", update);
148
+ return () => mediaQuery.removeEventListener("change", update);
149
+ }, [preference]);
150
+ return resolved;
151
+ }
152
+
153
+ // src/components/shader-scene/shader-scene.tsx
120
154
  var import_jsx_runtime3 = require("react/jsx-runtime");
121
155
  var defaultStyle = {
122
156
  position: "absolute",
@@ -126,18 +160,21 @@ var defaultStyle = {
126
160
  height: "100%"
127
161
  };
128
162
  function ShaderScene(props) {
129
- const { children, fallback, className, style, maxDPR } = props;
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)(() => {
163
+ const { children, fallback, className, style, maxDPR, gamut = "auto" } = props;
164
+ const resolvedGamut = useDisplayGamut(gamut);
165
+ const canvasRef = (0, import_react5.useRef)(null);
166
+ const [shaderContext, setShaderContext] = (0, import_react5.useState)(null);
167
+ const [error, setError] = (0, import_react5.useState)(null);
168
+ const [firstFramePainted, setFirstFramePainted] = (0, import_react5.useState)(false);
169
+ (0, import_react5.useEffect)(() => {
134
170
  const canvas = canvasRef.current;
135
171
  if (!canvas) return;
136
172
  let cancelled = false;
137
173
  let cleanup = null;
174
+ let firstPaintRaf = null;
138
175
  const setup = async () => {
139
176
  try {
140
- const renderer = await (0, import_matter.createRenderer)(canvas, { maxDPR });
177
+ const renderer = await (0, import_matter.createRenderer)(canvas, { maxDPR, gamut: resolvedGamut });
141
178
  if (cancelled) {
142
179
  renderer.dispose();
143
180
  return;
@@ -148,12 +185,11 @@ function ShaderScene(props) {
148
185
  const postProcessing = new import_webgpu.PostProcessing(renderer.three);
149
186
  const scheduler = new import_matter.FrameScheduler();
150
187
  const overlays = /* @__PURE__ */ new Map();
151
- const basePass = (0, import_tsl.pass)(scene, camera);
188
+ const basePassNode = (0, import_tsl.vec4)((0, import_tsl.pass)(scene, camera));
152
189
  const rebuildOutputNode = () => {
153
- const seed = basePass;
154
190
  postProcessing.outputNode = Array.from(overlays.values()).reduce(
155
- (node, transform) => transform(node),
156
- seed
191
+ (currentPipeline, transform) => transform(currentPipeline),
192
+ basePassNode
157
193
  );
158
194
  postProcessing.needsUpdate = true;
159
195
  };
@@ -167,7 +203,18 @@ function ShaderScene(props) {
167
203
  rebuildOutputNode();
168
204
  };
169
205
  };
170
- scheduler.add(() => postProcessing.render());
206
+ let firstPaintSignaled = false;
207
+ const renderFrame = () => {
208
+ postProcessing.render();
209
+ if (!firstPaintSignaled && (scene.children.length > 0 || overlays.size > 0)) {
210
+ firstPaintSignaled = true;
211
+ firstPaintRaf = requestAnimationFrame(() => {
212
+ firstPaintRaf = null;
213
+ if (!cancelled) setFirstFramePainted(true);
214
+ });
215
+ }
216
+ };
217
+ scheduler.add(renderFrame);
171
218
  scheduler.start();
172
219
  const visibility = (0, import_matter.createVisibilityWatcher)();
173
220
  const intersection = (0, import_matter.createIntersectionWatcher)(canvas);
@@ -179,33 +226,38 @@ function ShaderScene(props) {
179
226
  updatePauseState();
180
227
  const unsubVisibility = visibility.subscribe(updatePauseState);
181
228
  const unsubIntersection = intersection.subscribe(updatePauseState);
182
- const onResize = () => renderer.resize();
183
- window.addEventListener("resize", onResize);
229
+ const resizeObserver = new ResizeObserver(() => renderer.resize());
230
+ resizeObserver.observe(canvas);
184
231
  cleanup = () => {
185
232
  unsubVisibility();
186
233
  unsubIntersection();
187
234
  visibility.dispose();
188
235
  intersection.dispose();
189
- window.removeEventListener("resize", onResize);
236
+ resizeObserver.disconnect();
190
237
  scheduler.dispose();
191
238
  renderer.dispose();
192
239
  };
193
- setCtx({ renderer, scene, camera, scheduler, registerOverlay });
194
- } catch (err) {
240
+ setShaderContext({ renderer, scene, camera, scheduler, registerOverlay });
241
+ } catch (caughtError) {
195
242
  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);
243
+ const normalizedError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
244
+ console.error("[ShaderScene] renderer init failed:", normalizedError);
245
+ setError(normalizedError);
199
246
  }
200
247
  };
201
248
  void setup();
202
249
  return () => {
203
250
  cancelled = true;
251
+ if (firstPaintRaf !== null) {
252
+ cancelAnimationFrame(firstPaintRaf);
253
+ firstPaintRaf = null;
254
+ }
204
255
  cleanup?.();
205
256
  cleanup = null;
206
- setCtx(null);
257
+ setShaderContext(null);
258
+ setFirstFramePainted(false);
207
259
  };
208
- }, [maxDPR]);
260
+ }, [maxDPR, resolvedGamut]);
209
261
  let content;
210
262
  if (error) {
211
263
  content = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
@@ -231,10 +283,11 @@ function ShaderScene(props) {
231
283
  ]
232
284
  }
233
285
  );
234
- } else if (ctx) {
235
- content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShaderContext.Provider, { value: ctx, children });
236
286
  } else {
237
- content = fallback ?? null;
287
+ content = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
288
+ shaderContext && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShaderContext.Provider, { value: shaderContext, children }),
289
+ !firstFramePainted && (fallback ?? null)
290
+ ] });
238
291
  }
239
292
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: { ...defaultStyle, ...style }, children: [
240
293
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("canvas", { ref: canvasRef, style: { width: "100%", height: "100%", display: "block" } }),
@@ -243,18 +296,18 @@ function ShaderScene(props) {
243
296
  }
244
297
 
245
298
  // src/hooks/use-animatable-uniform/use-animatable-uniform.ts
246
- var import_react5 = require("react");
299
+ var import_react6 = require("react");
247
300
  var import_tsl2 = require("three/tsl");
248
301
  var isSignal = (value) => {
249
302
  if (typeof value !== "object" || value === null) return false;
250
303
  return "get" in value && typeof value.get === "function" && "on" in value && typeof value.on === "function";
251
304
  };
252
305
  function useAnimatableUniform(value) {
253
- const uniformNode = (0, import_react5.useMemo)(() => {
306
+ const uniformNode = (0, import_react6.useMemo)(() => {
254
307
  const initial = isSignal(value) ? value.get() : value;
255
308
  return (0, import_tsl2.uniform)(initial);
256
309
  }, []);
257
- (0, import_react5.useEffect)(() => {
310
+ (0, import_react6.useEffect)(() => {
258
311
  if (isSignal(value)) {
259
312
  const unsub = value.on("change", (next) => {
260
313
  uniformNode.value = next;
@@ -268,13 +321,13 @@ function useAnimatableUniform(value) {
268
321
  }
269
322
 
270
323
  // src/hooks/use-cursor/use-cursor.ts
324
+ var import_react8 = require("react");
271
325
  var import_matter2 = require("@lovo/matter");
272
- var import_react7 = require("react");
273
326
 
274
327
  // src/hooks/use-shader-context/use-shader-context.ts
275
- var import_react6 = require("react");
328
+ var import_react7 = require("react");
276
329
  function useShaderContext() {
277
- return (0, import_react6.useContext)(ShaderContext);
330
+ return (0, import_react7.useContext)(ShaderContext);
278
331
  }
279
332
 
280
333
  // src/hooks/use-cursor/use-cursor.ts
@@ -283,81 +336,91 @@ var STUB_SIGNAL = {
283
336
  on: () => () => void 0
284
337
  };
285
338
  function useCursor(opts = {}) {
286
- const ctx = useShaderContext();
287
- const [input, setInput] = (0, import_react7.useState)(null);
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);
339
+ const shaderContext = useShaderContext();
340
+ const [input, setInput] = (0, import_react8.useState)(null);
341
+ (0, import_react8.useEffect)(() => {
342
+ const canvas = shaderContext?.renderer.three.domElement;
343
+ const resolvedElement = opts.element ?? (canvas instanceof HTMLElement ? canvas : void 0);
344
+ const newCursorInput = new import_matter2.CursorInput({ ...opts, element: resolvedElement });
345
+ setInput(newCursorInput);
293
346
  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);
347
+ if (shaderContext?.scheduler) {
348
+ const schedulerTickHandler = ({ delta }) => newCursorInput.tick(delta);
349
+ shaderContext.scheduler.add(schedulerTickHandler);
350
+ detach = () => shaderContext.scheduler.remove(schedulerTickHandler);
298
351
  } else {
299
- let raf = null;
352
+ let animationFrameId = null;
300
353
  let lastNow = performance.now();
301
354
  const loop = (now) => {
302
355
  const delta = (now - lastNow) / 1e3;
303
356
  lastNow = now;
304
- fresh.tick(delta);
305
- raf = requestAnimationFrame(loop);
357
+ newCursorInput.tick(delta);
358
+ animationFrameId = requestAnimationFrame(loop);
306
359
  };
307
- raf = requestAnimationFrame(loop);
360
+ animationFrameId = requestAnimationFrame(loop);
308
361
  detach = () => {
309
- if (raf !== null) cancelAnimationFrame(raf);
362
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
310
363
  };
311
364
  }
312
365
  return () => {
313
366
  detach();
314
- fresh.dispose();
367
+ newCursorInput.dispose();
315
368
  setInput(null);
316
369
  };
317
- }, [ctx]);
370
+ }, [shaderContext]);
318
371
  return input ?? STUB_SIGNAL;
319
372
  }
320
373
 
321
374
  // 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);
375
+ var import_react9 = require("react");
376
+ function usePostProcessPass(transform, deps) {
377
+ const shaderContext = useShaderContext();
378
+ (0, import_react9.useEffect)(() => {
379
+ if (!shaderContext) return;
380
+ const unregister = shaderContext.registerOverlay(transform);
328
381
  return unregister;
329
- }, [ctx, ...deps]);
382
+ }, [shaderContext, ...deps]);
383
+ }
384
+
385
+ // src/hooks/use-resize/use-resize.ts
386
+ var import_react10 = require("react");
387
+
388
+ // src/internal/create-signal.ts
389
+ function createSignal(getValue) {
390
+ const listeners = /* @__PURE__ */ new Set();
391
+ return {
392
+ listeners,
393
+ signal: {
394
+ get: getValue,
395
+ on: (_event, listener) => {
396
+ listeners.add(listener);
397
+ return () => {
398
+ listeners.delete(listener);
399
+ };
400
+ }
401
+ }
402
+ };
330
403
  }
331
404
 
332
405
  // src/hooks/use-resize/use-resize.ts
333
- var import_react9 = require("react");
334
406
  var STUB_SIGNAL2 = {
335
407
  get: () => [0, 0, 1],
336
408
  on: () => () => void 0
337
409
  };
338
410
  function useResize() {
339
- const ctx = useShaderContext();
340
- const [signal, setSignal] = (0, import_react9.useState)(null);
341
- (0, import_react9.useEffect)(() => {
342
- if (!ctx) return void 0;
343
- const canvas = ctx.renderer.three.domElement;
411
+ const shaderContext = useShaderContext();
412
+ const [signal, setSignal] = (0, import_react10.useState)(null);
413
+ (0, import_react10.useEffect)(() => {
414
+ if (!shaderContext) return void 0;
415
+ const canvas = shaderContext.renderer.three.domElement;
344
416
  if (!(canvas instanceof HTMLCanvasElement)) return void 0;
345
417
  let value = [
346
418
  canvas.clientWidth,
347
419
  canvas.clientHeight,
348
420
  typeof window !== "undefined" ? window.devicePixelRatio : 1
349
421
  ];
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);
422
+ const { signal: newSignal, listeners } = createSignal(() => value);
423
+ setSignal(newSignal);
361
424
  const emit = () => {
362
425
  const next = [
363
426
  canvas.clientWidth,
@@ -366,66 +429,59 @@ function useResize() {
366
429
  ];
367
430
  if (next[0] === value[0] && next[1] === value[1] && next[2] === value[2]) return;
368
431
  value = next;
369
- for (const cb of listeners) cb(next);
432
+ for (const listener of listeners) listener(next);
370
433
  };
371
434
  const observer = new ResizeObserver(emit);
372
435
  observer.observe(canvas);
373
- let mql = null;
374
- let mqlHandler = null;
436
+ let mediaQueryList = null;
437
+ let mediaQueryListener = null;
375
438
  const setupDprWatch = () => {
376
439
  if (typeof window === "undefined") return;
377
440
  const dpr = window.devicePixelRatio;
378
- const next = window.matchMedia(`(resolution: ${dpr}dppx)`);
379
- const handler = () => {
441
+ const nextMediaQueryList = window.matchMedia(`(resolution: ${dpr}dppx)`);
442
+ const nextMediaQueryListener = () => {
380
443
  emit();
381
- if (mql && mqlHandler) mql.removeEventListener("change", mqlHandler);
444
+ if (mediaQueryList && mediaQueryListener)
445
+ mediaQueryList.removeEventListener("change", mediaQueryListener);
382
446
  setupDprWatch();
383
447
  };
384
- next.addEventListener("change", handler);
385
- mql = next;
386
- mqlHandler = handler;
448
+ nextMediaQueryList.addEventListener("change", nextMediaQueryListener);
449
+ mediaQueryList = nextMediaQueryList;
450
+ mediaQueryListener = nextMediaQueryListener;
387
451
  };
388
452
  setupDprWatch();
389
453
  return () => {
390
454
  observer.disconnect();
391
- if (mql && mqlHandler) mql.removeEventListener("change", mqlHandler);
392
- mql = null;
393
- mqlHandler = null;
455
+ if (mediaQueryList && mediaQueryListener)
456
+ mediaQueryList.removeEventListener("change", mediaQueryListener);
457
+ mediaQueryList = null;
458
+ mediaQueryListener = null;
394
459
  listeners.clear();
395
460
  setSignal(null);
396
461
  };
397
- }, [ctx]);
462
+ }, [shaderContext]);
398
463
  return signal ?? STUB_SIGNAL2;
399
464
  }
400
465
 
401
466
  // src/hooks/use-scroll/use-scroll.ts
402
- var import_react10 = require("react");
467
+ var import_react11 = require("react");
403
468
  var STUB_SIGNAL3 = {
404
469
  get: () => [0, 0],
405
470
  on: () => () => void 0
406
471
  };
407
472
  function useScroll() {
408
- const [signal, setSignal] = (0, import_react10.useState)(null);
409
- (0, import_react10.useEffect)(() => {
473
+ const [signal, setSignal] = (0, import_react11.useState)(null);
474
+ (0, import_react11.useEffect)(() => {
410
475
  if (typeof window === "undefined") return void 0;
411
476
  const compute = () => {
412
- const y = window.scrollY;
477
+ const scrollYPosition = window.scrollY;
413
478
  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];
479
+ const progress = Math.max(0, Math.min(1, scrollYPosition / max));
480
+ return [scrollYPosition, progress];
416
481
  };
417
482
  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);
483
+ const { signal: newSignal, listeners } = createSignal(() => value);
484
+ setSignal(newSignal);
429
485
  let rafPending = false;
430
486
  const onScroll = () => {
431
487
  if (rafPending) return;
@@ -435,7 +491,7 @@ function useScroll() {
435
491
  const next = compute();
436
492
  if (next[0] === value[0] && next[1] === value[1]) return;
437
493
  value = next;
438
- for (const cb of listeners) cb(next);
494
+ for (const listener of listeners) listener(next);
439
495
  });
440
496
  };
441
497
  window.addEventListener("scroll", onScroll, { passive: true });
@@ -449,29 +505,28 @@ function useScroll() {
449
505
  }
450
506
 
451
507
  // src/hooks/use-shader-material/use-shader-material.ts
452
- var import_react11 = require("react");
508
+ var import_react12 = require("react");
453
509
  var import_webgpu2 = require("three/webgpu");
454
510
  function useShaderMaterial(build) {
455
- const material = (0, import_react11.useMemo)(() => {
456
- const m = new import_webgpu2.MeshBasicNodeMaterial();
457
- m.colorNode = build();
458
- return m;
511
+ const material = (0, import_react12.useMemo)(() => {
512
+ const nodeMaterial = new import_webgpu2.MeshBasicNodeMaterial();
513
+ nodeMaterial.colorNode = build();
514
+ return nodeMaterial;
459
515
  }, [build]);
460
- (0, import_react11.useEffect)(() => {
516
+ (0, import_react12.useEffect)(() => {
461
517
  return () => material.dispose();
462
518
  }, [material]);
463
519
  return material;
464
520
  }
465
521
 
466
522
  // src/hooks/use-static-hint/use-static-hint.ts
467
- var import_react12 = require("react");
468
- function useStaticHint(hint) {
469
- const ctx = useShaderContext();
470
- (0, import_react12.useEffect)(() => {
471
- if (!ctx) return;
472
- ctx.scheduler.setIdle(hint);
473
- return () => ctx.scheduler.setIdle(false);
474
- }, [ctx, hint]);
523
+ var import_react13 = require("react");
524
+ function useStaticSceneHint(isStatic) {
525
+ const shaderContext = useShaderContext();
526
+ (0, import_react13.useEffect)(() => {
527
+ if (!shaderContext) return;
528
+ return shaderContext.scheduler.setIdle(isStatic);
529
+ }, [shaderContext, isStatic]);
475
530
  }
476
531
  // Annotate the CommonJS export names for ESM import in node:
477
532
  0 && (module.exports = {
@@ -480,11 +535,12 @@ function useStaticHint(hint) {
480
535
  ShaderScene,
481
536
  useAnimatableUniform,
482
537
  useCursor,
483
- useOverlayPass,
538
+ useDisplayGamut,
539
+ usePostProcessPass,
484
540
  useResize,
485
541
  useScroll,
486
542
  useShaderContext,
487
543
  useShaderMaterial,
488
- useStaticHint
544
+ useStaticSceneHint
489
545
  });
490
546
  //# sourceMappingURL=index.cjs.map