@react-three/fiber 10.0.0-alpha.0 → 10.0.0-alpha.2

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.
@@ -1,5 +1,5 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, WebGPURenderer, Color, Vector4, PostProcessing } from 'three/webgpu';
2
+ import { RenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, WebGPURenderer, Color, Vector4, PostProcessing } from 'three/webgpu';
3
3
  import { Inspector } from 'three/addons/inspector/Inspector.js';
4
4
  import { jsx, Fragment } from 'react/jsx-runtime';
5
5
  import * as React from 'react';
@@ -34,18 +34,21 @@ const WebGLRenderer = class WebGLRenderer2 {
34
34
  );
35
35
  }
36
36
  };
37
+ const WebGLRenderTarget = null;
37
38
 
38
39
  const THREE = /*#__PURE__*/_mergeNamespaces({
39
40
  __proto__: null,
40
41
  Inspector: Inspector,
41
42
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
42
43
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
44
+ RenderTargetCompat: RenderTarget,
45
+ WebGLRenderTarget: WebGLRenderTarget,
43
46
  WebGLRenderer: WebGLRenderer
44
47
  }, [webgpu]);
45
48
 
46
- var __defProp$2 = Object.defineProperty;
47
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
48
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
49
+ var __defProp$3 = Object.defineProperty;
50
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
51
+ var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
49
52
  const act = React["act"];
50
53
  const useIsomorphicLayoutEffect = /* @__PURE__ */ (() => typeof window !== "undefined" && (window.document?.createElement || window.navigator?.product === "ReactNative"))() ? React.useLayoutEffect : React.useEffect;
51
54
  function useMutableCallback(fn) {
@@ -77,7 +80,7 @@ const ErrorBoundary = /* @__PURE__ */ (() => {
77
80
  return _a = class extends React.Component {
78
81
  constructor() {
79
82
  super(...arguments);
80
- __publicField$2(this, "state", { error: false });
83
+ __publicField$3(this, "state", { error: false });
81
84
  }
82
85
  componentDidCatch(err) {
83
86
  this.props.set(err);
@@ -85,7 +88,7 @@ const ErrorBoundary = /* @__PURE__ */ (() => {
85
88
  render() {
86
89
  return this.state.error ? null : this.props.children;
87
90
  }
88
- }, __publicField$2(_a, "getDerivedStateFromError", () => ({ error: true })), _a;
91
+ }, __publicField$3(_a, "getDerivedStateFromError", () => ({ error: true })), _a;
89
92
  })();
90
93
 
91
94
  const is = {
@@ -148,6 +151,13 @@ function updateCamera(camera, size) {
148
151
  }
149
152
  camera.updateProjectionMatrix();
150
153
  }
154
+ const frustumMatrix = new Matrix4();
155
+ function updateFrustum(camera, frustum) {
156
+ const target = frustum ?? new Frustum();
157
+ frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
158
+ target.setFromProjectionMatrix(frustumMatrix);
159
+ return target;
160
+ }
151
161
 
152
162
  const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
153
163
  function findInitialRoot(instance) {
@@ -228,6 +238,205 @@ function invalidateInstance(instance) {
228
238
  if (state && state.internal.frames === 0) state.invalidate();
229
239
  }
230
240
 
241
+ const tempFrustum = new Frustum();
242
+ let hasWarnedWebGL = false;
243
+ let tslModule = null;
244
+ async function loadTSL() {
245
+ if (tslModule) return tslModule;
246
+ try {
247
+ const tsl = await import('three/tsl');
248
+ tslModule = { uniform: tsl.uniform, nodeObject: tsl.nodeObject };
249
+ return tslModule;
250
+ } catch {
251
+ return null;
252
+ }
253
+ }
254
+ function createOcclusionObserverNode(store, uniform) {
255
+ const node = new Node("float");
256
+ node.updateType = NodeUpdateType.OBJECT;
257
+ node.update = function(frame) {
258
+ const { internal } = store.getState();
259
+ const registry = internal.visibilityRegistry;
260
+ const cache = internal.occlusionCache;
261
+ for (const entry of registry.values()) {
262
+ const { object, handlers } = entry;
263
+ if (handlers.onOccluded || handlers.onVisible) {
264
+ const isOccluded = frame.renderer.isOccluded(object);
265
+ cache.set(object, isOccluded);
266
+ }
267
+ }
268
+ };
269
+ node.setup = function() {
270
+ return uniform(0);
271
+ };
272
+ return node;
273
+ }
274
+ let occlusionSetupPromise = null;
275
+ function enableOcclusion(store) {
276
+ const state = store.getState();
277
+ const { internal, renderer, rootScene } = state;
278
+ if (internal.occlusionEnabled || occlusionSetupPromise) return;
279
+ const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
280
+ if (!hasOcclusionSupport) {
281
+ if (!hasWarnedWebGL) {
282
+ console.warn(
283
+ "[R3F] Warning: onOccluded/onVisible occlusion queries require WebGPU renderer. Occlusion events will not fire on WebGL."
284
+ );
285
+ hasWarnedWebGL = true;
286
+ }
287
+ return;
288
+ }
289
+ occlusionSetupPromise = setupOcclusion(store);
290
+ }
291
+ async function setupOcclusion(store) {
292
+ const state = store.getState();
293
+ const { internal, rootScene, set } = state;
294
+ const tsl = await loadTSL();
295
+ if (!tsl) {
296
+ console.warn("[R3F] Warning: TSL module not available. Occlusion queries disabled.");
297
+ occlusionSetupPromise = null;
298
+ return;
299
+ }
300
+ const { uniform, nodeObject } = tsl;
301
+ let helperGroup = internal.helperGroup;
302
+ if (!helperGroup) {
303
+ helperGroup = new Group();
304
+ helperGroup.name = "__r3fInternal";
305
+ helperGroup.__r3fInternal = true;
306
+ rootScene.add(helperGroup);
307
+ }
308
+ const geometry = new BoxGeometry(1, 1, 1);
309
+ const material = new MeshBasicNodeMaterial({
310
+ transparent: true,
311
+ opacity: 0
312
+ });
313
+ const observerNode = nodeObject(createOcclusionObserverNode(store, uniform));
314
+ material.colorNode = observerNode;
315
+ material.needsUpdate = true;
316
+ const mesh = new Mesh(geometry, material);
317
+ mesh.name = "__r3fOcclusionObserver";
318
+ mesh.scale.setScalar(1e-4);
319
+ mesh.frustumCulled = false;
320
+ mesh.__r3fInternal = true;
321
+ helperGroup.add(mesh);
322
+ set((state2) => ({
323
+ internal: {
324
+ ...state2.internal,
325
+ helperGroup,
326
+ occlusionObserver: mesh,
327
+ occlusionEnabled: true
328
+ }
329
+ }));
330
+ occlusionSetupPromise = null;
331
+ }
332
+ function disableOcclusion(store) {
333
+ const { internal, set } = store.getState();
334
+ if (!internal.occlusionEnabled) return;
335
+ if (internal.occlusionObserver) {
336
+ internal.occlusionObserver.removeFromParent();
337
+ internal.occlusionObserver.geometry.dispose();
338
+ internal.occlusionObserver.material.dispose();
339
+ }
340
+ internal.occlusionCache.clear();
341
+ set((state) => ({
342
+ internal: {
343
+ ...state.internal,
344
+ occlusionObserver: null,
345
+ occlusionEnabled: false
346
+ }
347
+ }));
348
+ }
349
+ function cleanupHelperGroup(store) {
350
+ const { internal, set } = store.getState();
351
+ disableOcclusion(store);
352
+ if (internal.helperGroup) {
353
+ internal.helperGroup.removeFromParent();
354
+ set((state) => ({
355
+ internal: {
356
+ ...state.internal,
357
+ helperGroup: null
358
+ }
359
+ }));
360
+ }
361
+ }
362
+ function registerVisibility(store, object, handlers) {
363
+ const { internal } = store.getState();
364
+ const registry = internal.visibilityRegistry;
365
+ const entry = {
366
+ object,
367
+ handlers,
368
+ lastFramedState: null,
369
+ lastOccludedState: null,
370
+ lastVisibleState: null
371
+ };
372
+ registry.set(object.uuid, entry);
373
+ if (handlers.onOccluded || handlers.onVisible) {
374
+ object.occlusionTest = true;
375
+ if (!internal.occlusionEnabled) {
376
+ enableOcclusion(store);
377
+ }
378
+ }
379
+ }
380
+ function unregisterVisibility(store, object) {
381
+ const { internal } = store.getState();
382
+ internal.visibilityRegistry.delete(object.uuid);
383
+ internal.occlusionCache.delete(object);
384
+ }
385
+ function checkVisibility(state) {
386
+ const { internal, camera } = state;
387
+ const registry = internal.visibilityRegistry;
388
+ if (registry.size === 0) return;
389
+ updateFrustum(camera, tempFrustum);
390
+ for (const entry of registry.values()) {
391
+ const { object, handlers, lastFramedState, lastOccludedState, lastVisibleState } = entry;
392
+ let inFrustum = null;
393
+ const computeFrustum = () => {
394
+ if (inFrustum === null) {
395
+ if (object.geometry?.boundingSphere === null) {
396
+ object.geometry?.computeBoundingSphere();
397
+ }
398
+ inFrustum = tempFrustum.intersectsObject(object);
399
+ }
400
+ return inFrustum;
401
+ };
402
+ if (handlers.onFramed) {
403
+ const currentInFrustum = computeFrustum();
404
+ if (currentInFrustum !== lastFramedState) {
405
+ entry.lastFramedState = currentInFrustum;
406
+ handlers.onFramed(currentInFrustum);
407
+ }
408
+ }
409
+ let currentOcclusion = null;
410
+ if (handlers.onOccluded && internal.occlusionEnabled) {
411
+ currentOcclusion = internal.occlusionCache.get(object) ?? null;
412
+ if (currentOcclusion !== null && currentOcclusion !== lastOccludedState) {
413
+ entry.lastOccludedState = currentOcclusion;
414
+ handlers.onOccluded(currentOcclusion);
415
+ }
416
+ }
417
+ if (handlers.onVisible) {
418
+ const currentInFrustum = computeFrustum();
419
+ if (!handlers.onFramed && currentInFrustum !== lastFramedState) {
420
+ entry.lastFramedState = currentInFrustum;
421
+ }
422
+ let isOccluded = currentOcclusion;
423
+ if (isOccluded === null && internal.occlusionEnabled) {
424
+ isOccluded = internal.occlusionCache.get(object) ?? null;
425
+ }
426
+ if (isOccluded === null) isOccluded = false;
427
+ const isVisible = currentInFrustum && !isOccluded && object.visible;
428
+ if (isVisible !== lastVisibleState) {
429
+ entry.lastVisibleState = isVisible;
430
+ handlers.onVisible(isVisible);
431
+ }
432
+ }
433
+ }
434
+ }
435
+ function hasVisibilityHandlers(handlers) {
436
+ if (!handlers) return false;
437
+ return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
438
+ }
439
+
231
440
  const RESERVED_PROPS = [
232
441
  "children",
233
442
  "key",
@@ -242,6 +451,7 @@ const RESERVED_PROPS = [
242
451
  "dispose"
243
452
  ];
244
453
  const EVENT_REGEX = /^on(Pointer|Drag|Drop|Click|DoubleClick|ContextMenu|Wheel)/;
454
+ const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
245
455
  const INDEX_REGEX = /-\d+$/;
246
456
  const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
247
457
  const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
@@ -328,7 +538,7 @@ function applyProps(object, props) {
328
538
  const rootState = instance && findInitialRoot(instance).getState();
329
539
  const prevHandlers = instance?.eventCount;
330
540
  for (const prop in props) {
331
- let value = props[prop];
541
+ const value = props[prop];
332
542
  if (RESERVED_PROPS.includes(prop)) continue;
333
543
  if (instance && EVENT_REGEX.test(prop)) {
334
544
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -336,6 +546,12 @@ function applyProps(object, props) {
336
546
  instance.eventCount = Object.keys(instance.handlers).length;
337
547
  continue;
338
548
  }
549
+ if (instance && VISIBILITY_EVENT_REGEX.test(prop)) {
550
+ if (typeof value === "function") instance.handlers[prop] = value;
551
+ else delete instance.handlers[prop];
552
+ instance.eventCount = Object.keys(instance.handlers).length;
553
+ continue;
554
+ }
339
555
  if (value === void 0) continue;
340
556
  let { root, key, target } = resolve(object, prop);
341
557
  if (target === void 0 && (typeof root !== "object" || root === null)) {
@@ -372,6 +588,17 @@ function applyProps(object, props) {
372
588
  if (instance.eventCount && object2.raycast !== null) {
373
589
  rootState.internal.interaction.push(object2);
374
590
  }
591
+ const root = findInitialRoot(instance);
592
+ const visibilityHandlers = {
593
+ onFramed: instance.handlers.onFramed,
594
+ onOccluded: instance.handlers.onOccluded,
595
+ onVisible: instance.handlers.onVisible
596
+ };
597
+ if (hasVisibilityHandlers(visibilityHandlers)) {
598
+ registerVisibility(root, object2, visibilityHandlers);
599
+ } else {
600
+ unregisterVisibility(root, object2);
601
+ }
375
602
  }
376
603
  if (instance && instance.props.attach === void 0) {
377
604
  if (instance.object.isBufferGeometry) instance.props.attach = "geometry";
@@ -406,6 +633,7 @@ function removeInteractivity(store, object) {
406
633
  internal.capturedMap.forEach((captures, pointerId) => {
407
634
  releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
408
635
  });
636
+ unregisterVisibility(store, object);
409
637
  }
410
638
  function createEvents(store) {
411
639
  function calculateDistance(event) {
@@ -462,13 +690,14 @@ function createEvents(store) {
462
690
  for (const hit of hits) {
463
691
  let eventObject = hit.object;
464
692
  while (eventObject) {
465
- if (eventObject.__r3f?.eventCount)
693
+ if (eventObject.__r3f?.eventCount) {
466
694
  intersections.push({ ...hit, eventObject });
695
+ }
467
696
  eventObject = eventObject.parent;
468
697
  }
469
698
  }
470
699
  if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
471
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
700
+ for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
472
701
  if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
473
702
  }
474
703
  }
@@ -498,12 +727,12 @@ function createEvents(store) {
498
727
  releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
499
728
  }
500
729
  };
501
- let extractEventProps = {};
502
- for (let prop in event) {
503
- let property = event[prop];
730
+ const extractEventProps = {};
731
+ for (const prop in event) {
732
+ const property = event[prop];
504
733
  if (typeof property !== "function") extractEventProps[prop] = property;
505
734
  }
506
- let raycastEvent = {
735
+ const raycastEvent = {
507
736
  ...hit,
508
737
  ...extractEventProps,
509
738
  pointer,
@@ -788,8 +1017,21 @@ function formatLocation(url, line) {
788
1017
  const file = clean.split("/").pop() ?? clean;
789
1018
  return `${file}:${line}`;
790
1019
  }
1020
+ function notifyAlpha({ message, link }) {
1021
+ if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_ALPHA_WARNINGS !== "true") {
1022
+ return;
1023
+ }
1024
+ if (shownNotices.has(message)) return;
1025
+ shownNotices.add(message);
1026
+ const boxStyle = "background: #6366f1; color: #ffffff; padding: 6px 10px; border-radius: 4px; font-weight: 500;";
1027
+ console.log(`%c\u{1F52C} ${message}`, boxStyle);
1028
+ {
1029
+ console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
1030
+ }
1031
+ }
791
1032
 
792
- const context = /* @__PURE__ */ React.createContext(null);
1033
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1034
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
793
1035
  const createStore = (invalidate, advance) => {
794
1036
  const rootStore = createWithEqualityFn((set, get) => {
795
1037
  const position = new Vector3();
@@ -820,6 +1062,8 @@ const createStore = (invalidate, advance) => {
820
1062
  gl: null,
821
1063
  renderer: null,
822
1064
  camera: null,
1065
+ frustum: new Frustum(),
1066
+ autoUpdateFrustum: true,
823
1067
  raycaster: null,
824
1068
  events: { priority: 1, enabled: true, connected: false },
825
1069
  scene: null,
@@ -871,10 +1115,38 @@ const createStore = (invalidate, advance) => {
871
1115
  getCurrentViewport
872
1116
  },
873
1117
  setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
874
- setSize: (width, height, top = 0, left = 0) => {
875
- const camera = get().camera;
876
- const size = { width, height, top, left };
877
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1118
+ setSize: (width, height, top, left) => {
1119
+ const state2 = get();
1120
+ if (width === void 0) {
1121
+ set({ _sizeImperative: false });
1122
+ if (state2._sizeProps) {
1123
+ const { width: propW, height: propH } = state2._sizeProps;
1124
+ if (propW !== void 0 || propH !== void 0) {
1125
+ const currentSize = state2.size;
1126
+ const newSize = {
1127
+ width: propW ?? currentSize.width,
1128
+ height: propH ?? currentSize.height,
1129
+ top: currentSize.top,
1130
+ left: currentSize.left
1131
+ };
1132
+ set((s) => ({
1133
+ size: newSize,
1134
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
1135
+ }));
1136
+ }
1137
+ }
1138
+ return;
1139
+ }
1140
+ const w = width;
1141
+ const h = height ?? width;
1142
+ const t = top ?? state2.size.top;
1143
+ const l = left ?? state2.size.left;
1144
+ const size = { width: w, height: h, top: t, left: l };
1145
+ set((s) => ({
1146
+ size,
1147
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
1148
+ _sizeImperative: true
1149
+ }));
878
1150
  },
879
1151
  setDpr: (dpr) => set((state2) => {
880
1152
  const resolved = calculateDpr(dpr);
@@ -891,6 +1163,9 @@ const createStore = (invalidate, advance) => {
891
1163
  textures: /* @__PURE__ */ new Map(),
892
1164
  postProcessing: null,
893
1165
  passes: {},
1166
+ _hmrVersion: 0,
1167
+ _sizeImperative: false,
1168
+ _sizeProps: null,
894
1169
  previousRoot: void 0,
895
1170
  internal: {
896
1171
  // Events
@@ -901,6 +1176,13 @@ const createStore = (invalidate, advance) => {
901
1176
  initialHits: [],
902
1177
  capturedMap: /* @__PURE__ */ new Map(),
903
1178
  lastEvent: React.createRef(),
1179
+ // Visibility tracking (onFramed, onOccluded, onVisible)
1180
+ visibilityRegistry: /* @__PURE__ */ new Map(),
1181
+ // Occlusion system (WebGPU only)
1182
+ occlusionEnabled: false,
1183
+ occlusionObserver: null,
1184
+ occlusionCache: /* @__PURE__ */ new Map(),
1185
+ helperGroup: null,
904
1186
  // Updates
905
1187
  active: false,
906
1188
  frames: 0,
@@ -986,7 +1268,15 @@ const createStore = (invalidate, advance) => {
986
1268
  }
987
1269
  if (camera !== oldCamera) {
988
1270
  oldCamera = camera;
1271
+ const { rootScene } = rootStore.getState();
1272
+ if (camera && rootScene && !camera.parent) {
1273
+ rootScene.add(camera);
1274
+ }
989
1275
  set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1276
+ const currentState = rootStore.getState();
1277
+ if (currentState.autoUpdateFrustum && camera) {
1278
+ updateFrustum(camera, currentState.frustum);
1279
+ }
990
1280
  }
991
1281
  });
992
1282
  rootStore.subscribe((state2) => invalidate(state2));
@@ -1045,18 +1335,18 @@ useLoader.clear = function(loader, input) {
1045
1335
  };
1046
1336
  useLoader.loader = getLoader;
1047
1337
 
1048
- var __defProp$1 = Object.defineProperty;
1049
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1050
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1338
+ var __defProp$2 = Object.defineProperty;
1339
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1340
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1051
1341
  const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1052
1342
  class PhaseGraph {
1053
1343
  constructor() {
1054
1344
  /** Ordered list of phase nodes */
1055
- __publicField$1(this, "phases", []);
1345
+ __publicField$2(this, "phases", []);
1056
1346
  /** Quick lookup by name */
1057
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1347
+ __publicField$2(this, "phaseMap", /* @__PURE__ */ new Map());
1058
1348
  /** Cached ordered names (invalidated on changes) */
1059
- __publicField$1(this, "orderedNamesCache", null);
1349
+ __publicField$2(this, "orderedNamesCache", null);
1060
1350
  this.initializeDefaultPhases();
1061
1351
  }
1062
1352
  //* Initialization --------------------------------
@@ -1083,8 +1373,9 @@ class PhaseGraph {
1083
1373
  const node = { name, isAutoGenerated: false };
1084
1374
  let insertIndex = this.phases.length;
1085
1375
  const targetIndex = this.getPhaseIndex(before ?? after);
1086
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1087
- else {
1376
+ if (targetIndex !== -1) {
1377
+ insertIndex = before ? targetIndex : targetIndex + 1;
1378
+ } else {
1088
1379
  const constraintType = before ? "before" : "after";
1089
1380
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1090
1381
  }
@@ -1312,9 +1603,9 @@ function resetJobTiming(job) {
1312
1603
  job.lastRun = void 0;
1313
1604
  }
1314
1605
 
1315
- var __defProp = Object.defineProperty;
1316
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1317
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1606
+ var __defProp$1 = Object.defineProperty;
1607
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1608
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1318
1609
  const hmrData = (() => {
1319
1610
  if (typeof process !== "undefined" && process.env.NODE_ENV === "test") return void 0;
1320
1611
  if (typeof import_meta_hot !== "undefined") return import_meta_hot;
@@ -1328,9 +1619,9 @@ const _Scheduler = class _Scheduler {
1328
1619
  //* Constructor ================================
1329
1620
  constructor() {
1330
1621
  //* Critical State ================================
1331
- __publicField(this, "roots", /* @__PURE__ */ new Map());
1332
- __publicField(this, "phaseGraph");
1333
- __publicField(this, "loopState", {
1622
+ __publicField$1(this, "roots", /* @__PURE__ */ new Map());
1623
+ __publicField$1(this, "phaseGraph");
1624
+ __publicField$1(this, "loopState", {
1334
1625
  running: false,
1335
1626
  rafHandle: null,
1336
1627
  lastTime: null,
@@ -1339,17 +1630,21 @@ const _Scheduler = class _Scheduler {
1339
1630
  elapsedTime: 0,
1340
1631
  createdAt: performance.now()
1341
1632
  });
1342
- __publicField(this, "stoppedTime", 0);
1633
+ __publicField$1(this, "stoppedTime", 0);
1343
1634
  //* Private State ================================
1344
- __publicField(this, "nextRootIndex", 0);
1345
- __publicField(this, "globalBeforeJobs", /* @__PURE__ */ new Map());
1346
- __publicField(this, "globalAfterJobs", /* @__PURE__ */ new Map());
1347
- __publicField(this, "nextGlobalIndex", 0);
1348
- __publicField(this, "idleCallbacks", /* @__PURE__ */ new Set());
1349
- __publicField(this, "nextJobIndex", 0);
1350
- __publicField(this, "jobStateListeners", /* @__PURE__ */ new Map());
1351
- __publicField(this, "pendingFrames", 0);
1352
- __publicField(this, "_frameloop", "always");
1635
+ __publicField$1(this, "nextRootIndex", 0);
1636
+ __publicField$1(this, "globalBeforeJobs", /* @__PURE__ */ new Map());
1637
+ __publicField$1(this, "globalAfterJobs", /* @__PURE__ */ new Map());
1638
+ __publicField$1(this, "nextGlobalIndex", 0);
1639
+ __publicField$1(this, "idleCallbacks", /* @__PURE__ */ new Set());
1640
+ __publicField$1(this, "nextJobIndex", 0);
1641
+ __publicField$1(this, "jobStateListeners", /* @__PURE__ */ new Map());
1642
+ __publicField$1(this, "pendingFrames", 0);
1643
+ __publicField$1(this, "_frameloop", "always");
1644
+ //* Independent Mode & Error Handling State ================================
1645
+ __publicField$1(this, "_independent", false);
1646
+ __publicField$1(this, "errorHandler", null);
1647
+ __publicField$1(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
1353
1648
  //* Core Loop Execution Methods ================================
1354
1649
  /**
1355
1650
  * Main RAF loop callback.
@@ -1358,7 +1653,7 @@ const _Scheduler = class _Scheduler {
1358
1653
  * @returns {void}
1359
1654
  * @private
1360
1655
  */
1361
- __publicField(this, "loop", (timestamp) => {
1656
+ __publicField$1(this, "loop", (timestamp) => {
1362
1657
  if (!this.loopState.running) return;
1363
1658
  this.executeFrame(timestamp);
1364
1659
  if (this._frameloop === "demand") {
@@ -1372,6 +1667,12 @@ const _Scheduler = class _Scheduler {
1372
1667
  });
1373
1668
  this.phaseGraph = new PhaseGraph();
1374
1669
  }
1670
+ static get instance() {
1671
+ return globalThis[_Scheduler.INSTANCE_KEY] ?? null;
1672
+ }
1673
+ static set instance(value) {
1674
+ globalThis[_Scheduler.INSTANCE_KEY] = value;
1675
+ }
1375
1676
  /**
1376
1677
  * Get the global scheduler instance (creates if doesn't exist).
1377
1678
  * Uses HMR data to preserve instance across hot reloads.
@@ -1420,29 +1721,43 @@ const _Scheduler = class _Scheduler {
1420
1721
  get isRunning() {
1421
1722
  return this.loopState.running;
1422
1723
  }
1724
+ get isReady() {
1725
+ return this.roots.size > 0;
1726
+ }
1727
+ get independent() {
1728
+ return this._independent;
1729
+ }
1730
+ set independent(value) {
1731
+ this._independent = value;
1732
+ if (value) this.ensureDefaultRoot();
1733
+ }
1423
1734
  //* Root Management Methods ================================
1424
1735
  /**
1425
1736
  * Register a root (Canvas) with the scheduler.
1426
1737
  * The first root to register starts the RAF loop (if frameloop='always').
1427
1738
  * @param {string} id - Unique identifier for this root
1428
- * @param {() => RootState} getState - Function to get the root's current state
1739
+ * @param {RootOptions} [options] - Optional configuration with getState and onError callbacks
1429
1740
  * @returns {() => void} Unsubscribe function to remove this root
1430
1741
  */
1431
- registerRoot(id, getState) {
1742
+ registerRoot(id, options = {}) {
1432
1743
  if (this.roots.has(id)) {
1433
1744
  console.warn(`[Scheduler] Root "${id}" already registered`);
1434
1745
  return () => this.unregisterRoot(id);
1435
1746
  }
1436
1747
  const entry = {
1437
1748
  id,
1438
- getState,
1749
+ getState: options.getState ?? (() => ({})),
1439
1750
  jobs: /* @__PURE__ */ new Map(),
1440
1751
  sortedJobs: [],
1441
1752
  needsRebuild: false
1442
1753
  };
1754
+ if (options.onError) {
1755
+ this.errorHandler = options.onError;
1756
+ }
1443
1757
  this.roots.set(id, entry);
1444
- if (this.roots.size === 1 && this._frameloop === "always") {
1445
- this.start();
1758
+ if (this.roots.size === 1) {
1759
+ this.notifyRootReady();
1760
+ if (this._frameloop === "always") this.start();
1446
1761
  }
1447
1762
  return () => this.unregisterRoot(id);
1448
1763
  }
@@ -1462,7 +1777,60 @@ const _Scheduler = class _Scheduler {
1462
1777
  this.roots.delete(id);
1463
1778
  if (this.roots.size === 0) {
1464
1779
  this.stop();
1780
+ this.errorHandler = null;
1781
+ }
1782
+ }
1783
+ /**
1784
+ * Subscribe to be notified when a root becomes available.
1785
+ * Fires immediately if a root already exists.
1786
+ * @param {() => void} callback - Function called when first root registers
1787
+ * @returns {() => void} Unsubscribe function
1788
+ */
1789
+ onRootReady(callback) {
1790
+ if (this.roots.size > 0) {
1791
+ callback();
1792
+ return () => {
1793
+ };
1465
1794
  }
1795
+ this.rootReadyCallbacks.add(callback);
1796
+ return () => this.rootReadyCallbacks.delete(callback);
1797
+ }
1798
+ /**
1799
+ * Notify all registered root-ready callbacks.
1800
+ * Called when the first root registers.
1801
+ * @returns {void}
1802
+ * @private
1803
+ */
1804
+ notifyRootReady() {
1805
+ for (const cb of this.rootReadyCallbacks) {
1806
+ try {
1807
+ cb();
1808
+ } catch (error) {
1809
+ console.error("[Scheduler] Error in root-ready callback:", error);
1810
+ }
1811
+ }
1812
+ this.rootReadyCallbacks.clear();
1813
+ }
1814
+ /**
1815
+ * Ensure a default root exists for independent mode.
1816
+ * Creates a minimal root with no state provider.
1817
+ * @returns {void}
1818
+ * @private
1819
+ */
1820
+ ensureDefaultRoot() {
1821
+ if (!this.roots.has("__default__")) {
1822
+ this.registerRoot("__default__");
1823
+ }
1824
+ }
1825
+ /**
1826
+ * Trigger error handling for job errors.
1827
+ * Uses the bound error handler if available, otherwise logs to console.
1828
+ * @param {Error} error - The error to handle
1829
+ * @returns {void}
1830
+ */
1831
+ triggerError(error) {
1832
+ if (this.errorHandler) this.errorHandler(error);
1833
+ else console.error("[Scheduler]", error);
1466
1834
  }
1467
1835
  //* Phase Management Methods ================================
1468
1836
  /**
@@ -1822,9 +2190,9 @@ const _Scheduler = class _Scheduler {
1822
2190
  const deltaMs = this.loopState.lastTime !== null ? now - this.loopState.lastTime : 0;
1823
2191
  const delta = deltaMs / 1e3;
1824
2192
  const elapsed = now - this.loopState.createdAt;
1825
- const rootState = root.getState();
2193
+ const providedState = root.getState?.() ?? {};
1826
2194
  const frameState = {
1827
- ...rootState,
2195
+ ...providedState,
1828
2196
  time: now,
1829
2197
  delta,
1830
2198
  elapsed,
@@ -1834,6 +2202,7 @@ const _Scheduler = class _Scheduler {
1834
2202
  job.callback(frameState, delta);
1835
2203
  } catch (error) {
1836
2204
  console.error(`[Scheduler] Error in job "${job.id}":`, error);
2205
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
1837
2206
  }
1838
2207
  }
1839
2208
  /**
@@ -1875,7 +2244,7 @@ const _Scheduler = class _Scheduler {
1875
2244
  /**
1876
2245
  * Execute all jobs for a single root in sorted order.
1877
2246
  * Rebuilds sorted job list if needed, then dispatches each job.
1878
- * Errors are caught and propagated to the root's error boundary.
2247
+ * Errors are caught and propagated via triggerError.
1879
2248
  * @param {RootEntry} root - The root entry to tick
1880
2249
  * @param {number} timestamp - RAF timestamp in milliseconds
1881
2250
  * @param {number} delta - Time since last frame in seconds
@@ -1887,10 +2256,9 @@ const _Scheduler = class _Scheduler {
1887
2256
  root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
1888
2257
  root.needsRebuild = false;
1889
2258
  }
1890
- const rootState = root.getState();
1891
- if (!rootState) return;
2259
+ const providedState = root.getState?.() ?? {};
1892
2260
  const frameState = {
1893
- ...rootState,
2261
+ ...providedState,
1894
2262
  time: timestamp,
1895
2263
  delta,
1896
2264
  elapsed: this.loopState.elapsedTime / 1e3,
@@ -1903,7 +2271,7 @@ const _Scheduler = class _Scheduler {
1903
2271
  job.callback(frameState, delta);
1904
2272
  } catch (error) {
1905
2273
  console.error(`[Scheduler] Error in job "${job.id}":`, error);
1906
- rootState.setError(error instanceof Error ? error : new Error(String(error)));
2274
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
1907
2275
  }
1908
2276
  }
1909
2277
  }
@@ -1988,8 +2356,11 @@ const _Scheduler = class _Scheduler {
1988
2356
  return /* @__PURE__ */ new Set([value]);
1989
2357
  }
1990
2358
  };
1991
- //* Static State & Methods (Singlton Usage) ================================
1992
- __publicField(_Scheduler, "instance", null);
2359
+ //* Static State & Methods (Singleton Usage) ================================
2360
+ //* Cross-Bundle Singleton Key ==============================
2361
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2362
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2363
+ __publicField$1(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
1993
2364
  let Scheduler = _Scheduler;
1994
2365
  const getScheduler = () => Scheduler.get();
1995
2366
  if (hmrData) {
@@ -1997,11 +2368,9 @@ if (hmrData) {
1997
2368
  }
1998
2369
 
1999
2370
  function useFrame(callback, priorityOrOptions) {
2000
- const store = useStore();
2001
- const getRootId = React.useCallback(() => {
2002
- const state = store.getState();
2003
- return state.internal.rootId;
2004
- }, [store]);
2371
+ const store = React.useContext(context);
2372
+ const isInsideCanvas = store !== null;
2373
+ const scheduler = getScheduler();
2005
2374
  const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
2006
2375
  id: priorityOrOptions.id,
2007
2376
  phase: priorityOrOptions.phase,
@@ -2021,55 +2390,71 @@ function useFrame(callback, priorityOrOptions) {
2021
2390
  const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
2022
2391
  useIsomorphicLayoutEffect(() => {
2023
2392
  if (!callback) return;
2024
- const scheduler = getScheduler();
2025
- const rootId = getRootId();
2026
- const state = store.getState();
2027
- if (isLegacyPriority) {
2028
- state.internal.priority++;
2029
- let parentRoot = state.previousRoot;
2030
- while (parentRoot) {
2031
- const parentState = parentRoot.getState();
2032
- if (parentState?.internal) parentState.internal.priority++;
2033
- parentRoot = parentState?.previousRoot;
2034
- }
2035
- notifyDepreciated({
2036
- heading: "useFrame with numeric priority is deprecated",
2037
- body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
2038
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
2039
- });
2040
- }
2041
- const wrappedCallback = (frameState, delta) => {
2042
- const localState = store.getState();
2043
- const mergedState = {
2044
- ...localState,
2045
- time: frameState.time,
2046
- delta: frameState.delta,
2047
- elapsed: frameState.elapsed,
2048
- frame: frameState.frame
2049
- };
2050
- callbackRef.current?.(mergedState, delta);
2051
- };
2052
- const unregister = scheduler.register(wrappedCallback, {
2053
- id,
2054
- rootId,
2055
- ...options
2056
- });
2057
- return () => {
2058
- unregister();
2393
+ if (isInsideCanvas) {
2394
+ const state = store.getState();
2395
+ const rootId = state.internal.rootId;
2059
2396
  if (isLegacyPriority) {
2060
- const currentState = store.getState();
2061
- if (currentState.internal) {
2062
- currentState.internal.priority--;
2063
- let parentRoot = currentState.previousRoot;
2064
- while (parentRoot) {
2065
- const parentState = parentRoot.getState();
2066
- if (parentState?.internal) parentState.internal.priority--;
2067
- parentRoot = parentState?.previousRoot;
2397
+ state.internal.priority++;
2398
+ let parentRoot = state.previousRoot;
2399
+ while (parentRoot) {
2400
+ const parentState = parentRoot.getState();
2401
+ if (parentState?.internal) parentState.internal.priority++;
2402
+ parentRoot = parentState?.previousRoot;
2403
+ }
2404
+ notifyDepreciated({
2405
+ heading: "useFrame with numeric priority is deprecated",
2406
+ body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
2407
+ link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
2408
+ });
2409
+ }
2410
+ const wrappedCallback = (frameState, delta) => {
2411
+ const localState = store.getState();
2412
+ const mergedState = {
2413
+ ...localState,
2414
+ time: frameState.time,
2415
+ delta: frameState.delta,
2416
+ elapsed: frameState.elapsed,
2417
+ frame: frameState.frame
2418
+ };
2419
+ callbackRef.current?.(mergedState, delta);
2420
+ };
2421
+ const unregister = scheduler.register(wrappedCallback, {
2422
+ id,
2423
+ rootId,
2424
+ ...options
2425
+ });
2426
+ return () => {
2427
+ unregister();
2428
+ if (isLegacyPriority) {
2429
+ const currentState = store.getState();
2430
+ if (currentState.internal) {
2431
+ currentState.internal.priority--;
2432
+ let parentRoot = currentState.previousRoot;
2433
+ while (parentRoot) {
2434
+ const parentState = parentRoot.getState();
2435
+ if (parentState?.internal) parentState.internal.priority--;
2436
+ parentRoot = parentState?.previousRoot;
2437
+ }
2068
2438
  }
2069
2439
  }
2440
+ };
2441
+ } else {
2442
+ const registerOutside = () => {
2443
+ return scheduler.register((state, delta) => callbackRef.current?.(state, delta), { id, ...options });
2444
+ };
2445
+ if (scheduler.independent || scheduler.isReady) {
2446
+ return registerOutside();
2070
2447
  }
2071
- };
2072
- }, [store, id, optionsKey, isLegacyPriority]);
2448
+ let unregisterJob = null;
2449
+ const unsubReady = scheduler.onRootReady(() => {
2450
+ unregisterJob = registerOutside();
2451
+ });
2452
+ return () => {
2453
+ unsubReady();
2454
+ unregisterJob?.();
2455
+ };
2456
+ }
2457
+ }, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
2073
2458
  const isPaused = React.useSyncExternalStore(
2074
2459
  // Subscribe function
2075
2460
  React.useCallback(
@@ -2084,7 +2469,7 @@ function useFrame(callback, priorityOrOptions) {
2084
2469
  React.useCallback(() => false, [])
2085
2470
  );
2086
2471
  const controls = React.useMemo(() => {
2087
- const scheduler = getScheduler();
2472
+ const scheduler2 = getScheduler();
2088
2473
  return {
2089
2474
  /** The job's unique ID */
2090
2475
  id,
@@ -2092,7 +2477,7 @@ function useFrame(callback, priorityOrOptions) {
2092
2477
  * Access to the global scheduler for frame loop control.
2093
2478
  * Use for controlling the entire frame loop, adding phases, etc.
2094
2479
  */
2095
- scheduler,
2480
+ scheduler: scheduler2,
2096
2481
  /**
2097
2482
  * Manually step this job only.
2098
2483
  * Bypasses FPS limiting - always runs.
@@ -2340,6 +2725,16 @@ function useTextures() {
2340
2725
  }, [store]);
2341
2726
  }
2342
2727
 
2728
+ function useRenderTarget(width, height, options) {
2729
+ const isLegacy = useThree((s) => s.isLegacy);
2730
+ const size = useThree((s) => s.size);
2731
+ return useMemo(() => {
2732
+ const w = width ?? size.width;
2733
+ const h = height ?? size.height;
2734
+ return new RenderTarget(w, h, options);
2735
+ }, [width, height, size.width, size.height, options, isLegacy]);
2736
+ }
2737
+
2343
2738
  function useStore() {
2344
2739
  const store = useContext(context);
2345
2740
  if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
@@ -2391,24 +2786,14 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
2391
2786
  getScheduler().step(timestamp);
2392
2787
  }
2393
2788
 
2394
- const version = "10.0.0-alpha.0";
2789
+ const version = "10.0.0-alpha.2";
2395
2790
  const packageData = {
2396
2791
  version: version};
2397
2792
 
2398
2793
  function Xb(Tt) {
2399
2794
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2400
2795
  }
2401
- var Rm = { exports: {} }, Og = { exports: {} };
2402
- /**
2403
- * @license React
2404
- * react-reconciler.production.js
2405
- *
2406
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2407
- *
2408
- * This source code is licensed under the MIT license found in the
2409
- * LICENSE file in the root directory of this source tree.
2410
- */
2411
- var _b;
2796
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2412
2797
  function Kb() {
2413
2798
  return _b || (_b = 1, (function(Tt) {
2414
2799
  Tt.exports = function(m) {
@@ -3480,7 +3865,6 @@ Error generating stack: ` + l.message + `
3480
3865
  if (J === cl || J === jc) throw J;
3481
3866
  var Ge = Yn(29, J, null, P.mode);
3482
3867
  return Ge.lanes = H, Ge.return = P, Ge;
3483
- } finally {
3484
3868
  }
3485
3869
  };
3486
3870
  }
@@ -4134,7 +4518,6 @@ Error generating stack: ` + l.message + `
4134
4518
  var h = r.lastRenderedState, y = d(h, a);
4135
4519
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4136
4520
  } catch {
4137
- } finally {
4138
4521
  }
4139
4522
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4140
4523
  }
@@ -6555,10 +6938,7 @@ Error generating stack: ` + l.message + `
6555
6938
  function vr(t, r) {
6556
6939
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6557
6940
  }
6558
- var ie = {}, Fm = React__default, tt = Tb, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy");
6559
- var gc = Symbol.for("react.activity");
6560
- var $r = Symbol.for("react.memo_cache_sentinel");
6561
- var Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
6941
+ var ie = {}, Fm = React__default, tt = Tb, Lt = Object.assign, hc = Symbol.for("react.element"), zs = Symbol.for("react.transitional.element"), sa = Symbol.for("react.portal"), $a = Symbol.for("react.fragment"), kf = Symbol.for("react.strict_mode"), Cs = Symbol.for("react.profiler"), mc = Symbol.for("react.consumer"), Io = Symbol.for("react.context"), Zi = Symbol.for("react.forward_ref"), Va = Symbol.for("react.suspense"), Te = Symbol.for("react.suspense_list"), wf = Symbol.for("react.memo"), ua = Symbol.for("react.lazy"), gc = Symbol.for("react.activity"), $r = Symbol.for("react.memo_cache_sentinel"), Pf = Symbol.iterator, xf = Symbol.for("react.client.reference"), ca = Array.isArray, M = Fm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Yp = m.rendererVersion, zf = m.rendererPackageName, Cf = m.extraDevToolsConfig, Ts = m.getPublicInstance, Hm = m.getRootHostContext, Xp = m.getChildHostContext, Am = m.prepareForCommit, _s = m.resetAfterCommit, Vr = m.createInstance;
6562
6942
  m.cloneMutableInstance;
6563
6943
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6564
6944
  m.cloneMutableTextInstance;
@@ -6927,17 +7307,7 @@ No matching component was found for:
6927
7307
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
6928
7308
  })(Og)), Og.exports;
6929
7309
  }
6930
- var Mg = { exports: {} };
6931
- /**
6932
- * @license React
6933
- * react-reconciler.development.js
6934
- *
6935
- * Copyright (c) Meta Platforms, Inc. and affiliates.
6936
- *
6937
- * This source code is licensed under the MIT license found in the
6938
- * LICENSE file in the root directory of this source tree.
6939
- */
6940
- var Rb;
7310
+ var Mg = { exports: {} }, Rb;
6941
7311
  function e0() {
6942
7312
  return Rb || (Rb = 1, (function(Tt) {
6943
7313
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -12704,10 +13074,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
12704
13074
  function Ic() {
12705
13075
  return di;
12706
13076
  }
12707
- var le = {}, qm = React__default, St = Tb, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy");
12708
- var Ds = Symbol.for("react.activity");
12709
- var Bh = Symbol.for("react.memo_cache_sentinel");
12710
- var ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
13077
+ var le = {}, qm = React__default, St = Tb, ze = Object.assign, Uh = Symbol.for("react.element"), Ho = Symbol.for("react.transitional.element"), Ao = Symbol.for("react.portal"), ol = Symbol.for("react.fragment"), Lc = Symbol.for("react.strict_mode"), Uf = Symbol.for("react.profiler"), ei = Symbol.for("react.consumer"), on = Symbol.for("react.context"), jn = Symbol.for("react.forward_ref"), Nc = Symbol.for("react.suspense"), Bf = Symbol.for("react.suspense_list"), al = Symbol.for("react.memo"), kt = Symbol.for("react.lazy"), Ds = Symbol.for("react.activity"), Bh = Symbol.for("react.memo_cache_sentinel"), ni = Symbol.iterator, il = Symbol.for("react.client.reference"), fn = Array.isArray, x = qm.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Jt = m.rendererVersion, Zt = m.rendererPackageName, jo = m.extraDevToolsConfig, ot = m.getPublicInstance, Zr = m.getRootHostContext, Dn = m.getChildHostContext, Ws = m.prepareForCommit, pa = m.resetAfterCommit, Fc = m.createInstance;
12711
13078
  m.cloneMutableInstance;
12712
13079
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
12713
13080
  m.cloneMutableTextInstance;
@@ -13675,15 +14042,6 @@ function n0() {
13675
14042
  var t0 = n0();
13676
14043
  const r0 = Xb(t0);
13677
14044
 
13678
- /**
13679
- * @license React
13680
- * react-reconciler-constants.production.js
13681
- *
13682
- * Copyright (c) Meta Platforms, Inc. and affiliates.
13683
- *
13684
- * This source code is licensed under the MIT license found in the
13685
- * LICENSE file in the root directory of this source tree.
13686
- */
13687
14045
  const t = 1, o = 8, r = 32, e = 2;
13688
14046
 
13689
14047
  function createReconciler(config) {
@@ -13692,7 +14050,8 @@ function createReconciler(config) {
13692
14050
  return reconciler2;
13693
14051
  }
13694
14052
  const NoEventPriority = 0;
13695
- const catalogue = {};
14053
+ const R3F_CATALOGUE = Symbol.for("@react-three/fiber.catalogue");
14054
+ const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
13696
14055
  const PREFIX_REGEX = /^three(?=[A-Z])/;
13697
14056
  const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
13698
14057
  let i = 0;
@@ -13709,10 +14068,11 @@ function extend(objects) {
13709
14068
  function validateInstance(type, props) {
13710
14069
  const name = toPascalCase(type);
13711
14070
  const target = catalogue[name];
13712
- if (type !== "primitive" && !target)
14071
+ if (type !== "primitive" && !target) {
13713
14072
  throw new Error(
13714
14073
  `R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`
13715
14074
  );
14075
+ }
13716
14076
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
13717
14077
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
13718
14078
  }
@@ -14163,14 +14523,14 @@ function createRoot(canvas) {
14163
14523
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14164
14524
  let onCreated;
14165
14525
  let lastCamera;
14166
- let lastConfiguredProps = {};
14526
+ const lastConfiguredProps = {};
14167
14527
  let configured = false;
14168
14528
  let pending = null;
14169
14529
  return {
14170
14530
  async configure(props = {}) {
14171
14531
  let resolve;
14172
14532
  pending = new Promise((_resolve) => resolve = _resolve);
14173
- let {
14533
+ const {
14174
14534
  gl: glConfig,
14175
14535
  renderer: rendererConfig,
14176
14536
  size: propsSize,
@@ -14190,9 +14550,12 @@ function createRoot(canvas) {
14190
14550
  camera: cameraOptions,
14191
14551
  onPointerMissed,
14192
14552
  onDragOverMissed,
14193
- onDropMissed
14553
+ onDropMissed,
14554
+ autoUpdateFrustum = true,
14555
+ occlusion = false,
14556
+ _sizeProps
14194
14557
  } = props;
14195
- let state = store.getState();
14558
+ const state = store.getState();
14196
14559
  const defaultGPUProps = {
14197
14560
  canvas
14198
14561
  };
@@ -14207,7 +14570,9 @@ function createRoot(canvas) {
14207
14570
  let renderer = state.internal.actualRenderer;
14208
14571
  if (!state.internal.actualRenderer) {
14209
14572
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14210
- await renderer.init();
14573
+ if (!renderer.hasInitialized?.()) {
14574
+ await renderer.init();
14575
+ }
14211
14576
  const backend = renderer.backend;
14212
14577
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14213
14578
  state.internal.actualRenderer = renderer;
@@ -14217,8 +14582,9 @@ function createRoot(canvas) {
14217
14582
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
14218
14583
  const { params, ...options } = raycastOptions || {};
14219
14584
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14220
- if (!is.equ(params, raycaster.params, shallowLoose))
14585
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14221
14586
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
14587
+ }
14222
14588
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14223
14589
  lastCamera = cameraOptions;
14224
14590
  const isCamera = cameraOptions?.isCamera;
@@ -14255,6 +14621,8 @@ function createRoot(canvas) {
14255
14621
  rootScene: scene,
14256
14622
  internal: { ...prev.internal, container: scene }
14257
14623
  }));
14624
+ const camera = state.camera;
14625
+ if (camera && !camera.parent) scene.add(camera);
14258
14626
  }
14259
14627
  if (events && !state.events.handlers) {
14260
14628
  state.set({ events: events(store) });
@@ -14267,9 +14635,14 @@ function createRoot(canvas) {
14267
14635
  wasEnabled = enabled;
14268
14636
  });
14269
14637
  }
14638
+ if (_sizeProps !== void 0) {
14639
+ state.set({ _sizeProps });
14640
+ }
14270
14641
  const size = computeInitialSize(canvas, propsSize);
14271
- if (!is.equ(size, state.size, shallowLoose)) {
14642
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
14643
+ const wasImperative = state._sizeImperative;
14272
14644
  state.setSize(size.width, size.height, size.top, size.left);
14645
+ if (!wasImperative) state.set({ _sizeImperative: false });
14273
14646
  }
14274
14647
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14275
14648
  state.setDpr(dpr);
@@ -14282,6 +14655,10 @@ function createRoot(canvas) {
14282
14655
  if (!state.onPointerMissed) state.set({ onPointerMissed });
14283
14656
  if (!state.onDragOverMissed) state.set({ onDragOverMissed });
14284
14657
  if (!state.onDropMissed) state.set({ onDropMissed });
14658
+ if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
14659
+ if (occlusion && !state.internal.occlusionEnabled) {
14660
+ enableOcclusion(store);
14661
+ }
14285
14662
  if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
14286
14663
  state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
14287
14664
  lastConfiguredProps.performance = performance;
@@ -14335,15 +14712,17 @@ function createRoot(canvas) {
14335
14712
  } else if (is.obj(shadows)) {
14336
14713
  Object.assign(renderer.shadowMap, shadows);
14337
14714
  }
14338
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
14715
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14339
14716
  renderer.shadowMap.needsUpdate = true;
14717
+ }
14340
14718
  }
14341
14719
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14342
14720
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14343
14721
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14344
14722
  }
14345
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
14723
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14346
14724
  applyProps(renderer, glConfig);
14725
+ }
14347
14726
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14348
14727
  const currentRenderer = state.renderer;
14349
14728
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
@@ -14354,7 +14733,37 @@ function createRoot(canvas) {
14354
14733
  const rootId = state.internal.rootId;
14355
14734
  if (!rootId) {
14356
14735
  const newRootId = scheduler.generateRootId();
14357
- const unregisterRoot = scheduler.registerRoot(newRootId, () => store.getState());
14736
+ const unregisterRoot = scheduler.registerRoot(newRootId, {
14737
+ getState: () => store.getState(),
14738
+ onError: (err) => store.getState().setError(err)
14739
+ });
14740
+ const unregisterFrustum = scheduler.register(
14741
+ () => {
14742
+ const state2 = store.getState();
14743
+ if (state2.autoUpdateFrustum && state2.camera) {
14744
+ updateFrustum(state2.camera, state2.frustum);
14745
+ }
14746
+ },
14747
+ {
14748
+ id: `${newRootId}_frustum`,
14749
+ rootId: newRootId,
14750
+ phase: "preRender",
14751
+ system: true
14752
+ }
14753
+ );
14754
+ const unregisterVisibility = scheduler.register(
14755
+ () => {
14756
+ const state2 = store.getState();
14757
+ checkVisibility(state2);
14758
+ },
14759
+ {
14760
+ id: `${newRootId}_visibility`,
14761
+ rootId: newRootId,
14762
+ phase: "preRender",
14763
+ system: true,
14764
+ after: `${newRootId}_frustum`
14765
+ }
14766
+ );
14358
14767
  const unregisterRender = scheduler.register(
14359
14768
  () => {
14360
14769
  const state2 = store.getState();
@@ -14382,6 +14791,8 @@ function createRoot(canvas) {
14382
14791
  rootId: newRootId,
14383
14792
  unregisterRoot: () => {
14384
14793
  unregisterRoot();
14794
+ unregisterFrustum();
14795
+ unregisterVisibility();
14385
14796
  unregisterRender();
14386
14797
  },
14387
14798
  scheduler
@@ -14439,6 +14850,7 @@ function unmountComponentAtNode(canvas, callback) {
14439
14850
  const unregisterRoot = state.internal.unregisterRoot;
14440
14851
  if (unregisterRoot) unregisterRoot();
14441
14852
  state.events.disconnect?.();
14853
+ cleanupHelperGroup(root.store);
14442
14854
  renderer?.renderLists?.dispose?.();
14443
14855
  renderer?.forceContextLoss?.();
14444
14856
  if (renderer?.xr) state.xr.disconnect();
@@ -14578,6 +14990,9 @@ function CanvasImpl({
14578
14990
  onDragOverMissed,
14579
14991
  onDropMissed,
14580
14992
  onCreated,
14993
+ hmr,
14994
+ width,
14995
+ height,
14581
14996
  ...props
14582
14997
  }) {
14583
14998
  React.useMemo(() => extend(THREE), []);
@@ -14598,7 +15013,16 @@ function CanvasImpl({
14598
15013
  };
14599
15014
  }, [resize, hasInitialSizeRef.current]);
14600
15015
  const [containerRef, containerRect] = useMeasure(measureConfig);
14601
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
15016
+ const effectiveSize = React.useMemo(
15017
+ () => ({
15018
+ width: width ?? containerRect.width,
15019
+ height: height ?? containerRect.height,
15020
+ top: containerRect.top,
15021
+ left: containerRect.left
15022
+ }),
15023
+ [width, height, containerRect]
15024
+ );
15025
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
14602
15026
  hasInitialSizeRef.current = true;
14603
15027
  }
14604
15028
  const canvasRef = React.useRef(null);
@@ -14617,8 +15041,23 @@ function CanvasImpl({
14617
15041
  useIsomorphicLayoutEffect(() => {
14618
15042
  effectActiveRef.current = true;
14619
15043
  const canvas = canvasRef.current;
14620
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
14621
- if (!root.current) root.current = createRoot(canvas);
15044
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15045
+ if (!root.current) {
15046
+ root.current = createRoot(canvas);
15047
+ notifyAlpha({
15048
+ message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
15049
+ link: "https://github.com/pmndrs/react-three-fiber/discussions"
15050
+ });
15051
+ const rootEntry = _roots.get(canvas);
15052
+ if (rootEntry?.store) {
15053
+ if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
15054
+ unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
15055
+ if (state.error && effectActiveRef.current) {
15056
+ setError(state.error);
15057
+ }
15058
+ });
15059
+ }
15060
+ }
14622
15061
  async function run() {
14623
15062
  if (!effectActiveRef.current || !root.current) return;
14624
15063
  await root.current.configure({
@@ -14636,7 +15075,9 @@ function CanvasImpl({
14636
15075
  performance,
14637
15076
  raycaster,
14638
15077
  camera,
14639
- size: containerRect,
15078
+ size: effectiveSize,
15079
+ // Store size props for reset functionality
15080
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
14640
15081
  // Pass mutable reference to onPointerMissed so it's free to update
14641
15082
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
14642
15083
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -14659,15 +15100,9 @@ function CanvasImpl({
14659
15100
  }
14660
15101
  });
14661
15102
  if (!effectActiveRef.current || !root.current) return;
14662
- const store = root.current.render(
15103
+ root.current.render(
14663
15104
  /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsx(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: children ?? null }) }) })
14664
15105
  );
14665
- if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
14666
- unsubscribeErrorRef.current = store.subscribe((state) => {
14667
- if (state.error && effectActiveRef.current) {
14668
- setError(state.error);
14669
- }
14670
- });
14671
15106
  }
14672
15107
  run();
14673
15108
  }
@@ -14688,6 +15123,33 @@ function CanvasImpl({
14688
15123
  };
14689
15124
  }
14690
15125
  }, []);
15126
+ React.useEffect(() => {
15127
+ if (hmr === false) return;
15128
+ const canvas = canvasRef.current;
15129
+ if (!canvas) return;
15130
+ const handleHMR = () => {
15131
+ const rootEntry = _roots.get(canvas);
15132
+ if (rootEntry?.store) {
15133
+ rootEntry.store.setState((state) => ({
15134
+ nodes: {},
15135
+ uniforms: {},
15136
+ _hmrVersion: state._hmrVersion + 1
15137
+ }));
15138
+ }
15139
+ };
15140
+ if (typeof import.meta !== "undefined" && import.meta.hot) {
15141
+ const hot = import.meta.hot;
15142
+ hot.on("vite:afterUpdate", handleHMR);
15143
+ return () => hot.dispose?.(() => {
15144
+ });
15145
+ }
15146
+ if (typeof module !== "undefined" && module.hot) {
15147
+ const hot = module.hot;
15148
+ hot.addStatusHandler((status) => {
15149
+ if (status === "idle") handleHMR();
15150
+ });
15151
+ }
15152
+ }, [hmr]);
14691
15153
  const pointerEvents = eventSource ? "none" : "auto";
14692
15154
  return /* @__PURE__ */ jsx(
14693
15155
  "div",
@@ -14710,6 +15172,72 @@ function Canvas(props) {
14710
15172
  return /* @__PURE__ */ jsx(FiberProvider, { children: /* @__PURE__ */ jsx(CanvasImpl, { ...props }) });
14711
15173
  }
14712
15174
 
15175
+ var __defProp = Object.defineProperty;
15176
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15177
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
15178
+ var _a;
15179
+ const INTERNAL_DATA = Symbol("ScopedStore.data");
15180
+ _a = INTERNAL_DATA;
15181
+ const _ScopedStore = class _ScopedStore {
15182
+ constructor(data) {
15183
+ __publicField(this, _a);
15184
+ this[INTERNAL_DATA] = data;
15185
+ return new Proxy(this, {
15186
+ get(target, prop, receiver) {
15187
+ if (typeof prop === "string") {
15188
+ if (prop === "scope" || prop === "has" || prop === "keys") {
15189
+ return Reflect.get(target, prop, receiver);
15190
+ }
15191
+ return target[INTERNAL_DATA][prop];
15192
+ }
15193
+ return Reflect.get(target, prop, receiver);
15194
+ },
15195
+ has(target, prop) {
15196
+ return typeof prop === "string" ? prop in target[INTERNAL_DATA] : Reflect.has(target, prop);
15197
+ },
15198
+ ownKeys(target) {
15199
+ return Reflect.ownKeys(target[INTERNAL_DATA]);
15200
+ },
15201
+ getOwnPropertyDescriptor(target, prop) {
15202
+ if (typeof prop === "string" && prop in target[INTERNAL_DATA]) {
15203
+ return {
15204
+ configurable: true,
15205
+ enumerable: true,
15206
+ value: target[INTERNAL_DATA][prop]
15207
+ };
15208
+ }
15209
+ return void 0;
15210
+ }
15211
+ });
15212
+ }
15213
+ /**
15214
+ * Access a nested scope by key.
15215
+ * If the key doesn't exist or isn't a scope object, returns an empty ScopedStore.
15216
+ */
15217
+ scope(key) {
15218
+ const data = this[INTERNAL_DATA][key];
15219
+ return new _ScopedStore(
15220
+ data && typeof data === "object" ? data : {}
15221
+ );
15222
+ }
15223
+ /**
15224
+ * Check if a key exists in the store.
15225
+ */
15226
+ has(key) {
15227
+ return key in this[INTERNAL_DATA];
15228
+ }
15229
+ /**
15230
+ * Get all keys in the store.
15231
+ */
15232
+ keys() {
15233
+ return Object.keys(this[INTERNAL_DATA]);
15234
+ }
15235
+ };
15236
+ let ScopedStore = _ScopedStore;
15237
+ function createScopedStore(data) {
15238
+ return new ScopedStore(data);
15239
+ }
15240
+
14713
15241
  function addTexture(set, key, value) {
14714
15242
  set((state) => {
14715
15243
  const newMap = new Map(state.textures);
@@ -14788,21 +15316,87 @@ function useCompareMemoize(value, deep) {
14788
15316
  const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
14789
15317
  function useUniforms(creatorOrScope, scope) {
14790
15318
  const store = useStore();
15319
+ const removeUniforms2 = useCallback(
15320
+ (names, targetScope) => {
15321
+ const nameArray = Array.isArray(names) ? names : [names];
15322
+ store.setState((state) => {
15323
+ if (targetScope) {
15324
+ const currentScope = { ...state.uniforms[targetScope] };
15325
+ for (const name of nameArray) delete currentScope[name];
15326
+ return { uniforms: { ...state.uniforms, [targetScope]: currentScope } };
15327
+ }
15328
+ const uniforms2 = { ...state.uniforms };
15329
+ for (const name of nameArray) if (isUniformNode$1(uniforms2[name])) delete uniforms2[name];
15330
+ return { uniforms: uniforms2 };
15331
+ });
15332
+ },
15333
+ [store]
15334
+ );
15335
+ const clearUniforms = useCallback(
15336
+ (targetScope) => {
15337
+ store.setState((state) => {
15338
+ if (targetScope && targetScope !== "root") {
15339
+ const { [targetScope]: _, ...rest } = state.uniforms;
15340
+ return { uniforms: rest };
15341
+ }
15342
+ if (targetScope === "root") {
15343
+ const uniforms2 = {};
15344
+ for (const [key, value] of Object.entries(state.uniforms)) {
15345
+ if (!isUniformNode$1(value)) uniforms2[key] = value;
15346
+ }
15347
+ return { uniforms: uniforms2 };
15348
+ }
15349
+ return { uniforms: {} };
15350
+ });
15351
+ },
15352
+ [store]
15353
+ );
15354
+ const rebuildUniforms = useCallback(
15355
+ (targetScope) => {
15356
+ store.setState((state) => {
15357
+ let newUniforms = state.uniforms;
15358
+ if (targetScope && targetScope !== "root") {
15359
+ const { [targetScope]: _, ...rest } = state.uniforms;
15360
+ newUniforms = rest;
15361
+ } else if (targetScope === "root") {
15362
+ newUniforms = {};
15363
+ for (const [key, value] of Object.entries(state.uniforms)) {
15364
+ if (!isUniformNode$1(value)) newUniforms[key] = value;
15365
+ }
15366
+ } else {
15367
+ newUniforms = {};
15368
+ }
15369
+ return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15370
+ });
15371
+ },
15372
+ [store]
15373
+ );
14791
15374
  const inputForMemoization = useMemo(() => {
14792
- return is.fun(creatorOrScope) ? creatorOrScope(store.getState()) : creatorOrScope;
15375
+ if (is.fun(creatorOrScope)) {
15376
+ const state = store.getState();
15377
+ const wrappedState = {
15378
+ ...state,
15379
+ uniforms: createScopedStore(state.uniforms),
15380
+ nodes: createScopedStore(state.nodes)
15381
+ };
15382
+ return creatorOrScope(wrappedState);
15383
+ }
15384
+ return creatorOrScope;
14793
15385
  }, [creatorOrScope, store]);
14794
15386
  const memoizedInput = useCompareMemoize(inputForMemoization);
14795
- return useMemo(() => {
14796
- const state = store.getState();
14797
- const set = store.setState;
15387
+ const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
15388
+ const storeUniforms = useThree((s) => s.uniforms);
15389
+ const uniforms = useMemo(() => {
14798
15390
  if (memoizedInput === void 0) {
14799
- return state.uniforms;
15391
+ return storeUniforms;
14800
15392
  }
14801
15393
  if (typeof memoizedInput === "string") {
14802
- const scopeData = state.uniforms[memoizedInput];
15394
+ const scopeData = storeUniforms[memoizedInput];
14803
15395
  if (scopeData && !isUniformNode$1(scopeData)) return scopeData;
14804
15396
  return {};
14805
15397
  }
15398
+ const state = store.getState();
15399
+ const set = store.setState;
14806
15400
  if (typeof memoizedInput !== "object" || memoizedInput === null) {
14807
15401
  throw new Error("Invalid uniform input");
14808
15402
  }
@@ -14838,44 +15432,49 @@ function useUniforms(creatorOrScope, scope) {
14838
15432
  set((s) => ({
14839
15433
  uniforms: {
14840
15434
  ...s.uniforms,
14841
- [scope]: {
14842
- ...s.uniforms[scope],
14843
- ...result
14844
- }
15435
+ [scope]: { ...s.uniforms[scope], ...result }
14845
15436
  }
14846
15437
  }));
14847
15438
  } else {
14848
- set((s) => ({
14849
- uniforms: {
14850
- ...s.uniforms,
14851
- ...result
14852
- }
14853
- }));
15439
+ set((s) => ({ uniforms: { ...s.uniforms, ...result } }));
14854
15440
  }
14855
15441
  }
14856
15442
  return result;
14857
- }, [store, memoizedInput, scope]);
15443
+ }, [
15444
+ store,
15445
+ memoizedInput,
15446
+ scope,
15447
+ // Only include storeUniforms in deps for reader modes to enable reactivity
15448
+ isReader ? storeUniforms : null
15449
+ ]);
15450
+ return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms, rebuildUniforms };
15451
+ }
15452
+ function rebuildAllUniforms(store, scope) {
15453
+ store.setState((state) => {
15454
+ let newUniforms = state.uniforms;
15455
+ if (scope && scope !== "root") {
15456
+ const { [scope]: _, ...rest } = state.uniforms;
15457
+ newUniforms = rest;
15458
+ } else if (scope === "root") {
15459
+ newUniforms = {};
15460
+ for (const [key, value] of Object.entries(state.uniforms)) {
15461
+ if (!isUniformNode$1(value)) newUniforms[key] = value;
15462
+ }
15463
+ } else {
15464
+ newUniforms = {};
15465
+ }
15466
+ return { uniforms: newUniforms, _hmrVersion: state._hmrVersion + 1 };
15467
+ });
14858
15468
  }
14859
15469
  function removeUniforms(set, names, scope) {
14860
15470
  set((state) => {
14861
15471
  if (scope) {
14862
15472
  const currentScope = { ...state.uniforms[scope] };
14863
- for (const name of names) {
14864
- delete currentScope[name];
14865
- }
14866
- return {
14867
- uniforms: {
14868
- ...state.uniforms,
14869
- [scope]: currentScope
14870
- }
14871
- };
15473
+ for (const name of names) delete currentScope[name];
15474
+ return { uniforms: { ...state.uniforms, [scope]: currentScope } };
14872
15475
  }
14873
15476
  const uniforms = { ...state.uniforms };
14874
- for (const name of names) {
14875
- if (isUniformNode$1(uniforms[name])) {
14876
- delete uniforms[name];
14877
- }
14878
- }
15477
+ for (const name of names) if (isUniformNode$1(uniforms[name])) delete uniforms[name];
14879
15478
  return { uniforms };
14880
15479
  });
14881
15480
  }
@@ -14889,9 +15488,7 @@ function clearRootUniforms(set) {
14889
15488
  set((state) => {
14890
15489
  const uniforms = {};
14891
15490
  for (const [key, value] of Object.entries(state.uniforms)) {
14892
- if (!isUniformNode$1(value)) {
14893
- uniforms[key] = value;
14894
- }
15491
+ if (!isUniformNode$1(value)) uniforms[key] = value;
14895
15492
  }
14896
15493
  return { uniforms };
14897
15494
  });
@@ -14966,21 +15563,82 @@ function useUniform(name, value) {
14966
15563
  const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
14967
15564
  function useNodes(creatorOrScope, scope) {
14968
15565
  const store = useStore();
14969
- return useMemo(() => {
14970
- const state = store.getState();
14971
- const set = store.setState;
15566
+ const removeNodes2 = useCallback(
15567
+ (names, targetScope) => {
15568
+ const nameArray = Array.isArray(names) ? names : [names];
15569
+ store.setState((state) => {
15570
+ if (targetScope) {
15571
+ const currentScope = { ...state.nodes[targetScope] };
15572
+ for (const name of nameArray) delete currentScope[name];
15573
+ return { nodes: { ...state.nodes, [targetScope]: currentScope } };
15574
+ }
15575
+ const nodes2 = { ...state.nodes };
15576
+ for (const name of nameArray) if (isTSLNode(nodes2[name])) delete nodes2[name];
15577
+ return { nodes: nodes2 };
15578
+ });
15579
+ },
15580
+ [store]
15581
+ );
15582
+ const clearNodes = useCallback(
15583
+ (targetScope) => {
15584
+ store.setState((state) => {
15585
+ if (targetScope && targetScope !== "root") {
15586
+ const { [targetScope]: _, ...rest } = state.nodes;
15587
+ return { nodes: rest };
15588
+ }
15589
+ if (targetScope === "root") {
15590
+ const nodes2 = {};
15591
+ for (const [key, value] of Object.entries(state.nodes)) {
15592
+ if (!isTSLNode(value)) nodes2[key] = value;
15593
+ }
15594
+ return { nodes: nodes2 };
15595
+ }
15596
+ return { nodes: {} };
15597
+ });
15598
+ },
15599
+ [store]
15600
+ );
15601
+ const rebuildNodes = useCallback(
15602
+ (targetScope) => {
15603
+ store.setState((state) => {
15604
+ let newNodes = state.nodes;
15605
+ if (targetScope && targetScope !== "root") {
15606
+ const { [targetScope]: _, ...rest } = state.nodes;
15607
+ newNodes = rest;
15608
+ } else if (targetScope === "root") {
15609
+ newNodes = {};
15610
+ for (const [key, value] of Object.entries(state.nodes)) {
15611
+ if (!isTSLNode(value)) newNodes[key] = value;
15612
+ }
15613
+ } else {
15614
+ newNodes = {};
15615
+ }
15616
+ return { nodes: newNodes, _hmrVersion: state._hmrVersion + 1 };
15617
+ });
15618
+ },
15619
+ [store]
15620
+ );
15621
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
15622
+ const storeNodes = useThree((s) => s.nodes);
15623
+ const hmrVersion = useThree((s) => s._hmrVersion);
15624
+ const nodes = useMemo(() => {
14972
15625
  if (creatorOrScope === void 0) {
14973
- return state.nodes;
15626
+ return storeNodes;
14974
15627
  }
14975
15628
  if (typeof creatorOrScope === "string") {
14976
- const scopeData = state.nodes[creatorOrScope];
14977
- if (scopeData && !isTSLNode(scopeData)) {
14978
- return scopeData;
14979
- }
15629
+ const scopeData = storeNodes[creatorOrScope];
15630
+ if (scopeData && !isTSLNode(scopeData)) return scopeData;
14980
15631
  return {};
14981
15632
  }
15633
+ const state = store.getState();
15634
+ const set = store.setState;
14982
15635
  const creator = creatorOrScope;
14983
- const created = creator(state);
15636
+ const wrappedState = {
15637
+ ...state,
15638
+ uniforms: createScopedStore(state.uniforms),
15639
+ nodes: createScopedStore(state.nodes)
15640
+ };
15641
+ const created = creator(wrappedState);
14984
15642
  const result = {};
14985
15643
  let hasNewNodes = false;
14986
15644
  if (scope) {
@@ -14989,9 +15647,7 @@ function useNodes(creatorOrScope, scope) {
14989
15647
  if (currentScope[name]) {
14990
15648
  result[name] = currentScope[name];
14991
15649
  } else {
14992
- if (typeof node.label === "function") {
14993
- node.setName(`${scope}.${name}`);
14994
- }
15650
+ if (typeof node.label === "function") node.setName(`${scope}.${name}`);
14995
15651
  result[name] = node;
14996
15652
  hasNewNodes = true;
14997
15653
  }
@@ -15000,10 +15656,7 @@ function useNodes(creatorOrScope, scope) {
15000
15656
  set((s) => ({
15001
15657
  nodes: {
15002
15658
  ...s.nodes,
15003
- [scope]: {
15004
- ...s.nodes[scope],
15005
- ...result
15006
- }
15659
+ [scope]: { ...s.nodes[scope], ...result }
15007
15660
  }
15008
15661
  }));
15009
15662
  }
@@ -15014,44 +15667,52 @@ function useNodes(creatorOrScope, scope) {
15014
15667
  if (existing && isTSLNode(existing)) {
15015
15668
  result[name] = existing;
15016
15669
  } else {
15017
- if (typeof node.label === "function") {
15018
- node.setName(name);
15019
- }
15670
+ if (typeof node.label === "function") node.setName(name);
15020
15671
  result[name] = node;
15021
15672
  hasNewNodes = true;
15022
15673
  }
15023
15674
  }
15024
15675
  if (hasNewNodes) {
15025
- set((s) => ({
15026
- nodes: {
15027
- ...s.nodes,
15028
- ...result
15029
- }
15030
- }));
15676
+ set((s) => ({ nodes: { ...s.nodes, ...result } }));
15031
15677
  }
15032
15678
  return result;
15033
- }, [store, typeof creatorOrScope === "string" ? creatorOrScope : scope]);
15679
+ }, [
15680
+ store,
15681
+ typeof creatorOrScope === "string" ? creatorOrScope : scope,
15682
+ // Only include storeNodes in deps for reader modes to enable reactivity
15683
+ // Creator mode intentionally excludes it to avoid re-running creator on unrelated changes
15684
+ isReader ? storeNodes : null,
15685
+ // Include hmrVersion for creator modes to allow rebuildNodes() to bust the cache
15686
+ isReader ? null : hmrVersion
15687
+ ]);
15688
+ return { ...nodes, removeNodes: removeNodes2, clearNodes, rebuildNodes };
15689
+ }
15690
+ function rebuildAllNodes(store, scope) {
15691
+ store.setState((state) => {
15692
+ let newNodes = state.nodes;
15693
+ if (scope && scope !== "root") {
15694
+ const { [scope]: _, ...rest } = state.nodes;
15695
+ newNodes = rest;
15696
+ } else if (scope === "root") {
15697
+ newNodes = {};
15698
+ for (const [key, value] of Object.entries(state.nodes)) {
15699
+ if (!isTSLNode(value)) newNodes[key] = value;
15700
+ }
15701
+ } else {
15702
+ newNodes = {};
15703
+ }
15704
+ return { nodes: newNodes, _hmrVersion: state._hmrVersion + 1 };
15705
+ });
15034
15706
  }
15035
15707
  function removeNodes(set, names, scope) {
15036
15708
  set((state) => {
15037
15709
  if (scope) {
15038
15710
  const currentScope = { ...state.nodes[scope] };
15039
- for (const name of names) {
15040
- delete currentScope[name];
15041
- }
15042
- return {
15043
- nodes: {
15044
- ...state.nodes,
15045
- [scope]: currentScope
15046
- }
15047
- };
15711
+ for (const name of names) delete currentScope[name];
15712
+ return { nodes: { ...state.nodes, [scope]: currentScope } };
15048
15713
  }
15049
15714
  const nodes = { ...state.nodes };
15050
- for (const name of names) {
15051
- if (isTSLNode(nodes[name])) {
15052
- delete nodes[name];
15053
- }
15054
- }
15715
+ for (const name of names) if (isTSLNode(nodes[name])) delete nodes[name];
15055
15716
  return { nodes };
15056
15717
  });
15057
15718
  }
@@ -15065,9 +15726,7 @@ function clearRootNodes(set) {
15065
15726
  set((state) => {
15066
15727
  const nodes = {};
15067
15728
  for (const [key, value] of Object.entries(state.nodes)) {
15068
- if (!isTSLNode(value)) {
15069
- nodes[key] = value;
15070
- }
15729
+ if (!isTSLNode(value)) nodes[key] = value;
15071
15730
  }
15072
15731
  return { nodes };
15073
15732
  });
@@ -15079,7 +15738,12 @@ function useLocalNodes(creator) {
15079
15738
  const textures = useThree((s) => s.textures);
15080
15739
  return useMemo(() => {
15081
15740
  const state = store.getState();
15082
- return creator(state);
15741
+ const wrappedState = {
15742
+ ...state,
15743
+ uniforms: createScopedStore(state.uniforms),
15744
+ nodes: createScopedStore(state.nodes)
15745
+ };
15746
+ return creator(wrappedState);
15083
15747
  }, [store, creator, uniforms, nodes, textures]);
15084
15748
  }
15085
15749
 
@@ -15093,6 +15757,10 @@ function usePostProcessing(mainCB, setupCB) {
15093
15757
  mainCBRef.current = mainCB;
15094
15758
  setupCBRef.current = setupCB;
15095
15759
  const [rebuildVersion, setRebuildVersion] = useState(0);
15760
+ useEffect(() => {
15761
+ callbacksRanRef.current = false;
15762
+ scenePassCacheRef.current = null;
15763
+ }, []);
15096
15764
  const clearPasses = useCallback(() => {
15097
15765
  store.setState({ passes: {} });
15098
15766
  }, [store]);
@@ -15174,4 +15842,4 @@ function usePostProcessing(mainCB, setupCB) {
15174
15842
 
15175
15843
  extend(THREE);
15176
15844
 
15177
- export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, updateCamera, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, usePostProcessing, useStore, useTexture, useTextures, useThree, useUniform, useUniforms };
15845
+ export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, clearNodeScope, clearRootNodes, clearRootUniforms, clearScope, context, createEvents, createPointerEvents, createPortal, createRoot, createScopedStore, createStore, createTextureOperations, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, rebuildAllNodes, rebuildAllUniforms, reconciler, removeInteractivity, removeNodes, removeUniforms, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useLocalNodes, useMutableCallback, useNodes, usePostProcessing, useRenderTarget, useStore, useTexture, useTextures, useThree, useUniform, useUniforms };