@react-three/fiber 10.0.0-canary.164c7be → 10.0.0-canary.1be335a

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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as webgpu from 'three/webgpu';
2
- import { RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, WebGLCubeRenderTarget, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
3
- import { WebGLRenderTarget, WebGLRenderer } from 'three';
2
+ import { CubeRenderTarget, RenderTarget, CubeReflectionMapping, EquirectangularReflectionMapping, CubeTextureLoader, Scene, HalfFloatType, Color, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, ShaderMaterial, Uniform, SRGBColorSpace, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, CanvasTarget, Raycaster, OrthographicCamera, PerspectiveCamera, PCFShadowMap, VSMShadowMap, BasicShadowMap, ACESFilmicToneMapping, WebGPURenderer } from 'three/webgpu';
3
+ import { WebGLCubeRenderTarget, WebGLRenderTarget, WebGLRenderer } from 'three';
4
4
  import { Inspector } from 'three/addons/inspector/Inspector.js';
5
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
6
  import * as React from 'react';
@@ -13,7 +13,7 @@ import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
13
13
  import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
14
14
  import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
15
15
  import { GainMapLoader } from '@monogrid/gainmap-js';
16
- import Tb, { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
16
+ import Tb, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
17
17
  import { createWithEqualityFn } from 'zustand/traditional';
18
18
  import { getScheduler } from '@pmndrs/scheduler';
19
19
  export { Scheduler, getScheduler } from '@pmndrs/scheduler';
@@ -36,10 +36,13 @@ const R3F_BUILD_WEBGPU = true;
36
36
 
37
37
  const THREE = /*#__PURE__*/_mergeNamespaces({
38
38
  __proto__: null,
39
+ CubeRenderTarget: CubeRenderTarget,
40
+ CubeRenderTargetCompat: CubeRenderTarget,
39
41
  Inspector: Inspector,
40
42
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
41
43
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
42
44
  RenderTargetCompat: RenderTarget,
45
+ WebGLCubeRenderTarget: WebGLCubeRenderTarget,
43
46
  WebGLRenderTarget: WebGLRenderTarget,
44
47
  WebGLRenderer: WebGLRenderer
45
48
  }, [webgpu]);
@@ -313,15 +316,16 @@ function EnvironmentPortal({
313
316
  preset = void 0,
314
317
  extensions
315
318
  }) {
316
- const gl = useThree$1((state) => state.gl);
319
+ const renderer = useThree$1((state) => state.renderer);
320
+ const isLegacy = useThree$1((state) => state.isLegacy);
317
321
  const defaultScene = useThree$1((state) => state.scene);
318
322
  const camera = React.useRef(null);
319
323
  const [virtualScene] = React.useState(() => new Scene());
320
324
  const fbo = React.useMemo(() => {
321
- const fbo2 = new WebGLCubeRenderTarget(resolution);
325
+ const fbo2 = isLegacy ? new WebGLCubeRenderTarget(resolution) : new CubeRenderTarget(resolution);
322
326
  fbo2.texture.type = HalfFloatType;
323
327
  return fbo2;
324
- }, [resolution]);
328
+ }, [resolution, isLegacy]);
325
329
  React.useEffect(() => {
326
330
  return () => {
327
331
  fbo.dispose();
@@ -329,10 +333,10 @@ function EnvironmentPortal({
329
333
  }, [fbo]);
330
334
  React.useLayoutEffect(() => {
331
335
  if (frames === 1) {
332
- const autoClear = gl.autoClear;
333
- gl.autoClear = true;
334
- camera.current.update(gl, virtualScene);
335
- gl.autoClear = autoClear;
336
+ const autoClear = renderer.autoClear;
337
+ renderer.autoClear = true;
338
+ camera.current.update(renderer, virtualScene);
339
+ renderer.autoClear = autoClear;
336
340
  }
337
341
  return setEnvProps(background, scene, defaultScene, fbo.texture, {
338
342
  backgroundBlurriness: blur ?? backgroundBlurriness,
@@ -349,7 +353,7 @@ function EnvironmentPortal({
349
353
  defaultScene,
350
354
  background,
351
355
  frames,
352
- gl,
356
+ renderer,
353
357
  blur,
354
358
  backgroundBlurriness,
355
359
  backgroundIntensity,
@@ -360,10 +364,10 @@ function EnvironmentPortal({
360
364
  let count = 1;
361
365
  useFrame$1(() => {
362
366
  if (frames === Infinity || count < frames) {
363
- const autoClear = gl.autoClear;
364
- gl.autoClear = true;
365
- camera.current.update(gl, virtualScene);
366
- gl.autoClear = autoClear;
367
+ const autoClear = renderer.autoClear;
368
+ renderer.autoClear = true;
369
+ camera.current.update(renderer, virtualScene);
370
+ renderer.autoClear = autoClear;
367
371
  count++;
368
372
  }
369
373
  });
@@ -606,8 +610,7 @@ function prepare(target, root, type, props) {
606
610
  object,
607
611
  eventCount: 0,
608
612
  handlers: {},
609
- isHidden: false,
610
- deferredRefs: []
613
+ isHidden: false
611
614
  };
612
615
  if (object) object.__r3f = instance;
613
616
  }
@@ -764,6 +767,17 @@ function unregisterVisibility(store, object) {
764
767
  internal.visibilityRegistry.delete(object.uuid);
765
768
  internal.occlusionCache.delete(object);
766
769
  }
770
+ function updateVisibilityHandlers(store, object, handlers) {
771
+ const { internal } = store.getState();
772
+ const entry = internal.visibilityRegistry.get(object.uuid);
773
+ if (!entry) return false;
774
+ entry.handlers = handlers;
775
+ if ((handlers.onOccluded || handlers.onVisible) && !internal.occlusionEnabled) {
776
+ object.occlusionTest = true;
777
+ enableOcclusion(store);
778
+ }
779
+ return true;
780
+ }
767
781
  function checkVisibility(state) {
768
782
  const { internal, camera } = state;
769
783
  const registry = internal.visibilityRegistry;
@@ -924,9 +938,9 @@ function diffProps(instance, newProps) {
924
938
  const { root, key } = resolve(instance.object, prop);
925
939
  if (root.constructor && root.constructor.length === 0) {
926
940
  const ctor = getMemoizedPrototype(root);
927
- if (!is.und(ctor)) changedProps[key] = ctor[key];
941
+ if (!is.und(ctor)) changedProps[prop] = ctor[key];
928
942
  } else {
929
- changedProps[key] = 0;
943
+ changedProps[prop] = 0;
930
944
  }
931
945
  }
932
946
  return changedProps;
@@ -951,10 +965,7 @@ function applyProps(object, props) {
951
965
  continue;
952
966
  }
953
967
  if (value === void 0) continue;
954
- if (isFromRef(value)) {
955
- instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
956
- continue;
957
- }
968
+ if (isFromRef(value)) continue;
958
969
  if (isOnce(value)) {
959
970
  if (instance?.appliedOnce?.has(prop)) continue;
960
971
  if (instance) {
@@ -990,6 +1001,19 @@ function applyProps(object, props) {
990
1001
  } else if (isVectorLike(target) && is.num(value)) {
991
1002
  if ("setScalar" in target && typeof target.setScalar === "function") target.setScalar(value);
992
1003
  else target.set(value);
1004
+ } else if (root instanceof ShaderMaterial && key === "uniforms" && is.obj(value)) {
1005
+ if (!is.obj(root.uniforms)) root.uniforms = {};
1006
+ const uniforms = root.uniforms;
1007
+ const nextUniforms = value;
1008
+ for (const name in nextUniforms) {
1009
+ const uniform = nextUniforms[name];
1010
+ const targetUniform = uniforms[name];
1011
+ if (targetUniform) Object.assign(targetUniform, uniform);
1012
+ else {
1013
+ const nextUniform = uniform instanceof Uniform ? uniform.clone() : new Uniform(uniform.value);
1014
+ uniforms[name] = nextUniform;
1015
+ }
1016
+ }
993
1017
  } else {
994
1018
  root[key] = value;
995
1019
  if (key.endsWith("Node") && root.isMaterial) {
@@ -1008,6 +1032,9 @@ function applyProps(object, props) {
1008
1032
  if (instance.eventCount && object2.raycast !== null) {
1009
1033
  rootState.internal.interaction.push(object2);
1010
1034
  }
1035
+ }
1036
+ if (instance?.parent && rootState?.internal && instance.object?.isObject3D) {
1037
+ const object2 = instance.object;
1011
1038
  const root = findInitialRoot(instance);
1012
1039
  const visibilityHandlers = {
1013
1040
  onFramed: instance.handlers.onFramed,
@@ -1015,7 +1042,9 @@ function applyProps(object, props) {
1015
1042
  onVisible: instance.handlers.onVisible
1016
1043
  };
1017
1044
  if (hasVisibilityHandlers(visibilityHandlers)) {
1018
- registerVisibility(root, object2, visibilityHandlers);
1045
+ if (!updateVisibilityHandlers(root, object2, visibilityHandlers)) {
1046
+ registerVisibility(root, object2, visibilityHandlers);
1047
+ }
1019
1048
  } else {
1020
1049
  unregisterVisibility(root, object2);
1021
1050
  }
@@ -1543,6 +1572,7 @@ function createPointerEvents(store) {
1543
1572
  set((state) => ({ events: { ...state.events, connected: void 0 } }));
1544
1573
  }
1545
1574
  },
1575
+ // PREVIEW (A8): the XR pointer registration API may change before stable.
1546
1576
  registerPointer: (config) => {
1547
1577
  const pointerId = nextXRPointerId++;
1548
1578
  xrPointers.set(pointerId, config);
@@ -1629,6 +1659,13 @@ function notifyAlpha({ message, link }) {
1629
1659
  }
1630
1660
  }
1631
1661
 
1662
+ const INTERNAL_LIB_MARKERS = ["zustand", "setState", "Object.assign"];
1663
+ function isInternalRendererAccess(stack, callerDepth = 2) {
1664
+ if (!stack) return false;
1665
+ const caller = stack.split("\n")[callerDepth] ?? "";
1666
+ return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
1667
+ }
1668
+
1632
1669
  const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1633
1670
  const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
1634
1671
  const createStore = (invalidate, advance) => {
@@ -1761,6 +1798,7 @@ const createStore = (invalidate, advance) => {
1761
1798
  }),
1762
1799
  setFrameloop: (frameloop = "always") => {
1763
1800
  set(() => ({ frameloop }));
1801
+ getScheduler().frameloop = frameloop;
1764
1802
  },
1765
1803
  setError: (error) => set(() => ({ error })),
1766
1804
  error: null,
@@ -1828,8 +1866,7 @@ const createStore = (invalidate, advance) => {
1828
1866
  const currentState = rootStore.getState();
1829
1867
  if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1830
1868
  const stack = new Error().stack || "";
1831
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1832
- if (!isInternalAccess) {
1869
+ if (!isInternalRendererAccess(stack)) {
1833
1870
  const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1834
1871
  notifyDepreciated({
1835
1872
  heading: "Accessing state.gl in WebGPU mode",
@@ -1942,19 +1979,25 @@ function loadingFn(extensions, onProgress) {
1942
1979
  );
1943
1980
  };
1944
1981
  }
1945
- function useLoader(loader, input, extensions, onProgress) {
1946
- const keys = Array.isArray(input) ? input : [input];
1982
+ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1983
+ const inputs = Array.isArray(input) ? input : [input];
1984
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
1947
1985
  const fn = loadingFn(extensions, onProgress);
1948
- const results = keys.map((key) => suspend(fn, [loader, key], { equal: is.equ }));
1986
+ const results = cacheKeys.map(
1987
+ (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1988
+ );
1949
1989
  return Array.isArray(input) ? results : results[0];
1950
1990
  }
1951
- useLoader.preload = function(loader, input, extensions, onProgress) {
1952
- const keys = Array.isArray(input) ? input : [input];
1953
- keys.forEach((key) => preload(loadingFn(extensions, onProgress), [loader, key]));
1991
+ useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1992
+ const inputs = Array.isArray(input) ? input : [input];
1993
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
1994
+ cacheKeys.forEach(
1995
+ (key, index) => preload(() => loadingFn(extensions, onProgress)(loader, inputs[index]), [loader, key])
1996
+ );
1954
1997
  };
1955
- useLoader.clear = function(loader, input) {
1956
- const keys = Array.isArray(input) ? input : [input];
1957
- keys.forEach((key) => clear([loader, key]));
1998
+ useLoader.clear = function(loader, input, cacheKey) {
1999
+ const cacheKeys = Array.isArray(cacheKey ?? input) ? cacheKey ?? input : [cacheKey ?? input];
2000
+ cacheKeys.forEach((key) => clear([loader, key]));
1958
2001
  };
1959
2002
  useLoader.loader = getLoader;
1960
2003
 
@@ -2376,7 +2419,7 @@ function advance(timestamp) {
2376
2419
  getScheduler().step(timestamp);
2377
2420
  }
2378
2421
 
2379
- const version = "10.0.0-alpha.2";
2422
+ const version = "10.0.0-alpha.3";
2380
2423
  const packageData = {
2381
2424
  version: version};
2382
2425
 
@@ -13632,7 +13675,7 @@ function n0() {
13632
13675
  var t0 = n0();
13633
13676
  const r0 = Xb(t0);
13634
13677
 
13635
- const t = 1, o = 8, r = 32, e = 2;
13678
+ const t = 1, o = 8, r = 32, e = 2, i$1 = 268435456;
13636
13679
 
13637
13680
  function createReconciler(config) {
13638
13681
  const reconciler2 = r0(config);
@@ -13797,7 +13840,15 @@ function setFiberRef(fiber, publicInstance) {
13797
13840
  }
13798
13841
  }
13799
13842
  const reconstructed = [];
13800
- function swapInstances() {
13843
+ function flushReconstructedInstances() {
13844
+ if (reconstructed.length === 0) return;
13845
+ try {
13846
+ swapReconstructedInstances();
13847
+ } finally {
13848
+ reconstructed.length = 0;
13849
+ }
13850
+ }
13851
+ function swapReconstructedInstances() {
13801
13852
  for (const [instance] of reconstructed) {
13802
13853
  const parent = instance.parent;
13803
13854
  if (parent) {
@@ -13815,6 +13866,7 @@ function swapInstances() {
13815
13866
  }
13816
13867
  }
13817
13868
  if (instance.isHidden) unhideInstance(instance);
13869
+ if (isObject3D(instance.object)) removeInteractivity(findInitialRoot(instance), instance.object);
13818
13870
  if (instance.object.__r3f) delete instance.object.__r3f;
13819
13871
  if (instance.type !== "primitive") disposeOnIdle(instance.object);
13820
13872
  }
@@ -13827,6 +13879,8 @@ function swapInstances() {
13827
13879
  instance.object.__r3f = instance;
13828
13880
  setFiberRef(fiber, instance.object);
13829
13881
  delete instance.appliedOnce;
13882
+ instance.eventCount = 0;
13883
+ instance.handlers = {};
13830
13884
  applyProps(instance.object, instance.props);
13831
13885
  if (instance.props.attach) {
13832
13886
  attach(parent, instance);
@@ -13843,14 +13897,114 @@ function swapInstances() {
13843
13897
  invalidateInstance(instance);
13844
13898
  }
13845
13899
  }
13846
- reconstructed.length = 0;
13847
13900
  }
13848
13901
  const handleTextInstance = () => {
13849
13902
  };
13850
13903
  const NO_CONTEXT = {};
13851
13904
  let currentUpdatePriority = NoEventPriority;
13852
- const NoFlags = 0;
13853
- const Update = 4;
13905
+ function getEventPriority(type) {
13906
+ switch (type) {
13907
+ case "beforetoggle":
13908
+ case "cancel":
13909
+ case "click":
13910
+ case "close":
13911
+ case "contextmenu":
13912
+ case "copy":
13913
+ case "cut":
13914
+ case "auxclick":
13915
+ case "dblclick":
13916
+ case "dragend":
13917
+ case "dragstart":
13918
+ case "drop":
13919
+ case "focusin":
13920
+ case "focusout":
13921
+ case "input":
13922
+ case "invalid":
13923
+ case "keydown":
13924
+ case "keypress":
13925
+ case "keyup":
13926
+ case "mousedown":
13927
+ case "mouseup":
13928
+ case "paste":
13929
+ case "pause":
13930
+ case "play":
13931
+ case "pointercancel":
13932
+ case "pointerdown":
13933
+ case "pointerup":
13934
+ case "ratechange":
13935
+ case "reset":
13936
+ case "resize":
13937
+ case "seeked":
13938
+ case "submit":
13939
+ case "touchcancel":
13940
+ case "touchend":
13941
+ case "touchstart":
13942
+ case "volumechange":
13943
+ case "change":
13944
+ case "selectionchange":
13945
+ case "compositionstart":
13946
+ case "compositionend":
13947
+ case "compositionupdate":
13948
+ case "beforeinput":
13949
+ case "blur":
13950
+ case "fullscreenchange":
13951
+ case "focus":
13952
+ case "hashchange":
13953
+ case "popstate":
13954
+ case "select":
13955
+ case "selectstart":
13956
+ return e;
13957
+ case "drag":
13958
+ case "dragenter":
13959
+ case "dragexit":
13960
+ case "dragleave":
13961
+ case "dragover":
13962
+ case "mousemove":
13963
+ case "mouseout":
13964
+ case "mouseover":
13965
+ case "pointermove":
13966
+ case "pointerout":
13967
+ case "pointerover":
13968
+ case "scroll":
13969
+ case "touchmove":
13970
+ case "wheel":
13971
+ case "mouseenter":
13972
+ case "mouseleave":
13973
+ case "pointerenter":
13974
+ case "pointerleave":
13975
+ return o;
13976
+ case "message": {
13977
+ switch (unstable_getCurrentPriorityLevel()) {
13978
+ case unstable_ImmediatePriority:
13979
+ return e;
13980
+ case unstable_UserBlockingPriority:
13981
+ return o;
13982
+ case unstable_NormalPriority:
13983
+ case unstable_LowPriority:
13984
+ return r;
13985
+ case unstable_IdlePriority:
13986
+ return i$1;
13987
+ default:
13988
+ return r;
13989
+ }
13990
+ }
13991
+ default:
13992
+ return r;
13993
+ }
13994
+ }
13995
+ function scheduleMicrotask(callback) {
13996
+ if (typeof queueMicrotask === "function") {
13997
+ queueMicrotask(callback);
13998
+ } else if (typeof Promise !== "undefined") {
13999
+ Promise.resolve().then(callback).catch((error) => {
14000
+ setTimeout(() => {
14001
+ throw error;
14002
+ });
14003
+ });
14004
+ } else {
14005
+ setTimeout(callback);
14006
+ }
14007
+ }
13854
14008
  const reconciler = /* @__PURE__ */ createReconciler({
13855
14009
  isPrimaryRenderer: false,
13856
14010
  warnsIfNotActing: false,
@@ -13889,16 +14043,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
13889
14043
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
13890
14044
  else if (newProps.args?.some((value, index) => value !== oldProps.args?.[index])) reconstruct = true;
13891
14045
  if (reconstruct) {
13892
- reconstructed.push([instance, { ...newProps }, fiber]);
14046
+ reconstructed.push([instance, getInstanceProps(newProps), fiber]);
13893
14047
  } else {
13894
14048
  const changedProps = diffProps(instance, newProps);
13895
- if (Object.keys(changedProps).length) {
13896
- Object.assign(instance.props, changedProps);
13897
- applyProps(instance.object, changedProps);
13898
- }
14049
+ const attach2 = instance.props.attach;
14050
+ instance.props = getInstanceProps(newProps);
14051
+ if (attach2 !== void 0) instance.props.attach = attach2;
14052
+ else delete instance.props.attach;
14053
+ if (Object.keys(changedProps).length) applyProps(instance.object, changedProps);
13899
14054
  }
13900
- const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
13901
- if (isTailSibling) swapInstances();
13902
14055
  },
13903
14056
  finalizeInitialChildren: (instance) => {
13904
14057
  for (const prop in instance.props) {
@@ -13923,8 +14076,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13923
14076
  const target = container.getState().internal.container ?? container.getState().scene;
13924
14077
  return prepare(target, container, "", {});
13925
14078
  },
13926
- resetAfterCommit: () => {
13927
- },
14079
+ // Reconstructed instances are swapped once all mutations are committed,
14080
+ // before layout effects run so refs point to the new objects
14081
+ resetAfterCommit: flushReconstructedInstances,
13928
14082
  shouldSetTextContent: () => false,
13929
14083
  clearContainer: () => false,
13930
14084
  hideInstance,
@@ -13932,6 +14086,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13932
14086
  createTextInstance: handleTextInstance,
13933
14087
  hideTextInstance: handleTextInstance,
13934
14088
  unhideTextInstance: handleTextInstance,
14089
+ // Mirrors react-dom, which flushes discrete work in microtasks
14090
+ supportsMicrotasks: true,
14091
+ scheduleMicrotask,
13935
14092
  scheduleTimeout: typeof setTimeout === "function" ? setTimeout : void 0,
13936
14093
  cancelTimeout: typeof clearTimeout === "function" ? clearTimeout : void 0,
13937
14094
  noTimeout: -1,
@@ -13972,28 +14129,9 @@ const reconciler = /* @__PURE__ */ createReconciler({
13972
14129
  },
13973
14130
  resolveUpdatePriority() {
13974
14131
  if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
13975
- switch (typeof window !== "undefined" && window.event?.type) {
13976
- case "click":
13977
- case "contextmenu":
13978
- case "dblclick":
13979
- case "dragenter":
13980
- case "dragleave":
13981
- case "drop":
13982
- case "pointercancel":
13983
- case "pointerdown":
13984
- case "pointerup":
13985
- return e;
13986
- case "dragover":
13987
- case "pointermove":
13988
- case "pointerout":
13989
- case "pointerover":
13990
- case "pointerenter":
13991
- case "pointerleave":
13992
- case "wheel":
13993
- return o;
13994
- default:
13995
- return r;
13996
- }
14132
+ const eventType = typeof window !== "undefined" ? window.event?.type : void 0;
14133
+ if (eventType === void 0) return r;
14134
+ return getEventPriority(eventType);
13997
14135
  },
13998
14136
  resetFormInstance() {
13999
14137
  },
@@ -14042,11 +14180,15 @@ const reconciler = /* @__PURE__ */ createReconciler({
14042
14180
  cloneMutableTextInstance(textInstance) {
14043
14181
  return textInstance;
14044
14182
  },
14183
+ // React 19.2 added these for root-level view transitions. They only run for hosts that opt into
14184
+ // view transitions by returning a container from cloneRootViewTransitionContainer; R3F renders
14185
+ // into a WebGL/WebGPU canvas, where cloning the root has no meaning, so we never opt in and
14186
+ // React never calls them. They stay as no-ops rather than throws: the host config is shared by
14187
+ // every root, and a throw here would turn a React-internal probe into a hard crash for users.
14188
+ // Guarded by a test asserting a full render + unmount never reaches them.
14045
14189
  cloneRootViewTransitionContainer(_rootContainer) {
14046
- throw new Error("Not implemented.");
14047
14190
  },
14048
14191
  removeRootViewTransitionClone(_rootContainer, _clone) {
14049
- throw new Error("Not implemented.");
14050
14192
  },
14051
14193
  // https://github.com/facebook/react/pull/32465
14052
14194
  createFragmentInstance: (_fiber) => null,
@@ -14131,6 +14273,7 @@ function createRoot(canvas) {
14131
14273
  const lastConfiguredProps = {};
14132
14274
  let configured = false;
14133
14275
  let pending = null;
14276
+ let rendererSetup = null;
14134
14277
  return {
14135
14278
  async configure(props = {}) {
14136
14279
  let resolve;
@@ -14161,7 +14304,7 @@ function createRoot(canvas) {
14161
14304
  forceEven
14162
14305
  } = props;
14163
14306
  const textureColorSpace = is.obj(glConfig) && !is.fun(glConfig) && !isRenderer(glConfig) && glConfig.textureColorSpace || is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && rendererConfig.textureColorSpace || SRGBColorSpace;
14164
- const state = store.getState();
14307
+ let state = store.getState();
14165
14308
  const defaultGLProps = {
14166
14309
  canvas,
14167
14310
  powerPreference: "high-performance",
@@ -14189,56 +14332,68 @@ function createRoot(canvas) {
14189
14332
  "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
14190
14333
  );
14191
14334
  }
14192
- if (wantsGL && !state.internal.actualRenderer) {
14193
- renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
14194
- state.internal.actualRenderer = renderer;
14195
- state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14196
- } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
14197
- const primary = await waitForPrimary(primaryCanvas);
14198
- renderer = primary.renderer;
14199
- state.internal.actualRenderer = renderer;
14200
- const canvasTarget = new CanvasTarget(canvas);
14201
- primary.store.setState((prev) => ({
14202
- internal: { ...prev.internal, isMultiCanvas: true }
14203
- }));
14204
- state.set((prev) => ({
14205
- webGPUSupported: primary.store.getState().webGPUSupported,
14206
- renderer,
14207
- primaryStore: primary.store,
14208
- internal: {
14209
- ...prev.internal,
14210
- canvasTarget,
14211
- isMultiCanvas: true,
14212
- isSecondary: true,
14213
- targetId: primaryCanvas
14214
- }
14215
- }));
14216
- } else if (!wantsGL && !state.internal.actualRenderer) {
14217
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14218
- if (!renderer.hasInitialized?.()) {
14219
- const size2 = computeInitialSize(canvas, propsSize);
14220
- if (size2.width > 0 && size2.height > 0) {
14221
- const pixelRatio = calculateDpr(dpr);
14222
- canvas.width = size2.width * pixelRatio;
14223
- canvas.height = size2.height * pixelRatio;
14224
- }
14225
- await renderer.init();
14226
- }
14227
- const backend = renderer.backend;
14228
- const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14229
- state.internal.actualRenderer = renderer;
14230
- state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14231
- if (canvasId && !state.internal.isSecondary) {
14232
- const canvasTarget = new CanvasTarget(canvas);
14233
- const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14234
- state.set((prev) => ({
14235
- internal: {
14236
- ...prev.internal,
14237
- canvasTarget,
14238
- unregisterPrimary
14335
+ if (!state.internal.actualRenderer) {
14336
+ if (!rendererSetup) {
14337
+ rendererSetup = (async () => {
14338
+ if (wantsGL) {
14339
+ renderer = await resolveRenderer(glConfig, defaultGLProps, WebGLRenderer);
14340
+ state.internal.actualRenderer = renderer;
14341
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14342
+ } else if (!wantsGL && primaryCanvas) {
14343
+ const primary = await waitForPrimary(primaryCanvas);
14344
+ renderer = primary.renderer;
14345
+ state.internal.actualRenderer = renderer;
14346
+ const canvasTarget = new CanvasTarget(canvas);
14347
+ primary.store.setState((prev) => ({
14348
+ internal: { ...prev.internal, isMultiCanvas: true }
14349
+ }));
14350
+ state.set((prev) => ({
14351
+ webGPUSupported: primary.store.getState().webGPUSupported,
14352
+ renderer,
14353
+ primaryStore: primary.store,
14354
+ internal: {
14355
+ ...prev.internal,
14356
+ canvasTarget,
14357
+ isMultiCanvas: true,
14358
+ isSecondary: true,
14359
+ targetId: primaryCanvas
14360
+ }
14361
+ }));
14362
+ } else if (!wantsGL) {
14363
+ renderer = await resolveRenderer(rendererConfig, defaultGPUProps, WebGPURenderer);
14364
+ if (!renderer.hasInitialized?.()) {
14365
+ const size2 = computeInitialSize(canvas, propsSize);
14366
+ if (size2.width > 0 && size2.height > 0) {
14367
+ const pixelRatio = calculateDpr(dpr);
14368
+ canvas.width = size2.width * pixelRatio;
14369
+ canvas.height = size2.height * pixelRatio;
14370
+ }
14371
+ await renderer.init();
14372
+ }
14373
+ const backend = renderer.backend;
14374
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14375
+ state.internal.actualRenderer = renderer;
14376
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14377
+ if (canvasId && !state.internal.isSecondary) {
14378
+ const canvasTarget = new CanvasTarget(canvas);
14379
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
14380
+ state.set((prev) => ({
14381
+ internal: {
14382
+ ...prev.internal,
14383
+ canvasTarget,
14384
+ unregisterPrimary
14385
+ }
14386
+ }));
14387
+ }
14239
14388
  }
14240
- }));
14389
+ })().catch((err) => {
14390
+ rendererSetup = null;
14391
+ throw err;
14392
+ });
14241
14393
  }
14394
+ await rendererSetup;
14395
+ state = store.getState();
14396
+ renderer = state.internal.actualRenderer;
14242
14397
  }
14243
14398
  let raycaster = state.raycaster;
14244
14399
  if (!raycaster) state.set({ raycaster: raycaster = new Raycaster() });
@@ -14588,7 +14743,8 @@ function unmountComponentAtNode(canvas, callback) {
14588
14743
  dispose(state.scene);
14589
14744
  _roots.delete(canvas);
14590
14745
  if (callback) callback(canvas);
14591
- } catch {
14746
+ } catch (error) {
14747
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
14592
14748
  }
14593
14749
  }, 500);
14594
14750
  }
@@ -14651,6 +14807,7 @@ function PortalInner({ state = {}, children, container }) {
14651
14807
  viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), resolvedSize);
14652
14808
  if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14653
14809
  }
14810
+ const set = injectState.set;
14654
14811
  return {
14655
14812
  // The intersect consists of the previous root state
14656
14813
  ...rootState,
@@ -14669,7 +14826,7 @@ function PortalInner({ state = {}, children, container }) {
14669
14826
  size: resolvedSize,
14670
14827
  viewport: { ...rootState.viewport, ...viewport },
14671
14828
  // Layers are allowed to override events
14672
- setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14829
+ setEvents: (events2) => set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
14673
14830
  // Container for child attachment - the portalScene (injected or container itself)
14674
14831
  internal: { ...rootState.internal, ...injectState.internal, container: portalScene }
14675
14832
  };
@@ -14728,14 +14885,49 @@ function parseBackground(background) {
14728
14885
  return null;
14729
14886
  }
14730
14887
 
14888
+ function parseRendererConfig(rendererProp) {
14889
+ const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
14890
+ if (isRendererConfig) {
14891
+ const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
14892
+ return {
14893
+ primaryCanvas: pc,
14894
+ scheduler: sc,
14895
+ renderer: Object.keys(rest).length > 0 ? rest : rendererProp
14896
+ };
14897
+ }
14898
+ return { primaryCanvas: void 0, scheduler: void 0, renderer: rendererProp };
14899
+ }
14900
+
14901
+ const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
14902
+ const registries = /* @__PURE__ */ new WeakMap();
14903
+ function getKindRegistry(store, kind) {
14904
+ let registry = registries.get(store);
14905
+ if (!registry) {
14906
+ registry = {
14907
+ nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14908
+ uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14909
+ buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
14910
+ gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
14911
+ };
14912
+ registries.set(store, registry);
14913
+ }
14914
+ return registry[kind];
14915
+ }
14916
+ function invalidateResource(store, kind, scopeKey) {
14917
+ const registry = getKindRegistry(store, kind);
14918
+ {
14919
+ registry.valid.clear();
14920
+ registry.staged.clear();
14921
+ }
14922
+ }
14923
+ function invalidateAllResources(store) {
14924
+ for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
14925
+ }
14926
+
14731
14927
  function clearHmrCaches(store) {
14732
- store.setState((state) => ({
14733
- nodes: {},
14734
- uniforms: {},
14735
- buffers: {},
14736
- gpuStorage: {},
14737
- _hmrVersion: state._hmrVersion + 1
14738
- }));
14928
+ const primary = store.getState().primaryStore ?? store;
14929
+ invalidateAllResources(primary);
14930
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
14739
14931
  }
14740
14932
 
14741
14933
  function CanvasImpl({
@@ -14771,18 +14963,7 @@ function CanvasImpl({
14771
14963
  forceEven,
14772
14964
  ...props
14773
14965
  }) {
14774
- const isRendererConfig = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp);
14775
- let primaryCanvas;
14776
- let scheduler;
14777
- let renderer;
14778
- if (isRendererConfig) {
14779
- const { primaryCanvas: pc, scheduler: sc, ...rest } = rendererProp;
14780
- primaryCanvas = pc;
14781
- scheduler = sc;
14782
- renderer = Object.keys(rest).length > 0 ? rest : rendererProp;
14783
- } else {
14784
- renderer = rendererProp;
14785
- }
14966
+ const { primaryCanvas, scheduler, renderer } = parseRendererConfig(rendererProp);
14786
14967
  React.useMemo(() => extend(THREE), []);
14787
14968
  const Bridge = useBridge();
14788
14969
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
@@ -14827,6 +15008,7 @@ function CanvasImpl({
14827
15008
  const handleDropMissed = useMutableCallback(onDropMissed);
14828
15009
  const [block, setBlock] = React.useState(false);
14829
15010
  const [error, setError] = React.useState(false);
15011
+ const [fallbackVisible, setFallbackVisible] = React.useState(false);
14830
15012
  if (block) throw block;
14831
15013
  if (error) throw error;
14832
15014
  const root = React.useRef(null);
@@ -14834,6 +15016,7 @@ function CanvasImpl({
14834
15016
  const unsubscribeErrorRef = React.useRef(null);
14835
15017
  useIsomorphicLayoutEffect(() => {
14836
15018
  effectActiveRef.current = true;
15019
+ if (fallbackVisible) return;
14837
15020
  const canvas = canvasRef.current;
14838
15021
  if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
14839
15022
  if (!root.current) {
@@ -14854,7 +15037,7 @@ function CanvasImpl({
14854
15037
  }
14855
15038
  async function run() {
14856
15039
  if (!effectActiveRef.current || !root.current) return;
14857
- await root.current.configure({
15040
+ const configured = await root.current.configure({
14858
15041
  id,
14859
15042
  primaryCanvas,
14860
15043
  scheduler,
@@ -14895,8 +15078,20 @@ function CanvasImpl({
14895
15078
  }
14896
15079
  onCreated?.(state);
14897
15080
  }
14898
- });
14899
- if (!effectActiveRef.current || !root.current) return;
15081
+ }).then(
15082
+ () => true,
15083
+ // Renderer setup failed (e.g. no WebGL/WebGPU support). The `fallback` prop lives
15084
+ // inside <canvas>, which browsers don't display, so surface it as visible DOM
15085
+ // instead; with no fallback, rethrow to an external error boundary. (#3757)
15086
+ (setupError) => {
15087
+ if (effectActiveRef.current) {
15088
+ if (fallback != null) setFallbackVisible(true);
15089
+ else setError(setupError);
15090
+ }
15091
+ return false;
15092
+ }
15093
+ );
15094
+ if (!configured || !effectActiveRef.current || !root.current) return;
14900
15095
  root.current.render(
14901
15096
  /* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxs(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: [
14902
15097
  backgroundProps && /* @__PURE__ */ jsx(Environment, { ...backgroundProps }),
@@ -14959,7 +15154,11 @@ function CanvasImpl({
14959
15154
  ...style
14960
15155
  },
14961
15156
  ...props,
14962
- children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
15157
+ children: fallbackVisible ? (
15158
+ // Renderer setup failed: render the fallback as visible DOM. Inside <canvas> (below)
15159
+ // it exists in the tree but browsers never paint it, which is the whole bug (#3757).
15160
+ fallback
15161
+ ) : /* @__PURE__ */ jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
14963
15162
  "canvas",
14964
15163
  {
14965
15164
  ref: canvasRef,