@react-three/fiber 10.0.0-alpha.1 → 10.0.0-canary.2b511a5

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/legacy.cjs CHANGED
@@ -5,10 +5,17 @@ const jsxRuntime = require('react/jsx-runtime');
5
5
  const React = require('react');
6
6
  const useMeasure = require('react-use-measure');
7
7
  const itsFine = require('its-fine');
8
+ const fiber = require('@react-three/fiber');
9
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
10
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
11
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
12
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
13
+ const gainmapJs = require('@monogrid/gainmap-js');
8
14
  const Tb = require('scheduler');
9
15
  const traditional = require('zustand/traditional');
10
16
  const suspendReact = require('suspend-react');
11
17
 
18
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
19
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
13
20
 
14
21
  function _interopNamespaceCompat(e) {
@@ -65,6 +72,374 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
65
72
  WebGPURenderer: WebGPURenderer
66
73
  }, [three__namespace]);
67
74
 
75
+ const primaryRegistry = /* @__PURE__ */ new Map();
76
+ const pendingSubscribers = /* @__PURE__ */ new Map();
77
+ function registerPrimary(id, renderer, store) {
78
+ if (primaryRegistry.has(id)) {
79
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
80
+ }
81
+ const entry = { renderer, store };
82
+ primaryRegistry.set(id, entry);
83
+ const subscribers = pendingSubscribers.get(id);
84
+ if (subscribers) {
85
+ subscribers.forEach((callback) => callback(entry));
86
+ pendingSubscribers.delete(id);
87
+ }
88
+ return () => {
89
+ const currentEntry = primaryRegistry.get(id);
90
+ if (currentEntry?.renderer === renderer) {
91
+ primaryRegistry.delete(id);
92
+ }
93
+ };
94
+ }
95
+ function getPrimary(id) {
96
+ return primaryRegistry.get(id);
97
+ }
98
+ function waitForPrimary(id, timeout = 5e3) {
99
+ const existing = primaryRegistry.get(id);
100
+ if (existing) {
101
+ return Promise.resolve(existing);
102
+ }
103
+ return new Promise((resolve, reject) => {
104
+ const timeoutId = setTimeout(() => {
105
+ const subscribers = pendingSubscribers.get(id);
106
+ if (subscribers) {
107
+ const index = subscribers.indexOf(callback);
108
+ if (index !== -1) subscribers.splice(index, 1);
109
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
110
+ }
111
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
112
+ }, timeout);
113
+ const callback = (entry) => {
114
+ clearTimeout(timeoutId);
115
+ resolve(entry);
116
+ };
117
+ if (!pendingSubscribers.has(id)) {
118
+ pendingSubscribers.set(id, []);
119
+ }
120
+ pendingSubscribers.get(id).push(callback);
121
+ });
122
+ }
123
+ function hasPrimary(id) {
124
+ return primaryRegistry.has(id);
125
+ }
126
+ function unregisterPrimary(id) {
127
+ primaryRegistry.delete(id);
128
+ }
129
+ function getPrimaryIds() {
130
+ return Array.from(primaryRegistry.keys());
131
+ }
132
+
133
+ const presetsObj = {
134
+ apartment: "lebombo_1k.hdr",
135
+ city: "potsdamer_platz_1k.hdr",
136
+ dawn: "kiara_1_dawn_1k.hdr",
137
+ forest: "forest_slope_1k.hdr",
138
+ lobby: "st_fagans_interior_1k.hdr",
139
+ night: "dikhololo_night_1k.hdr",
140
+ park: "rooitou_park_1k.hdr",
141
+ studio: "studio_small_03_1k.hdr",
142
+ sunset: "venice_sunset_1k.hdr",
143
+ warehouse: "empty_warehouse_01_1k.hdr"
144
+ };
145
+
146
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
147
+ const isArray = (arr) => Array.isArray(arr);
148
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
149
+ function useEnvironment({
150
+ files = defaultFiles,
151
+ path = "",
152
+ preset = void 0,
153
+ colorSpace = void 0,
154
+ extensions
155
+ } = {}) {
156
+ if (preset) {
157
+ validatePreset(preset);
158
+ files = presetsObj[preset];
159
+ path = CUBEMAP_ROOT;
160
+ }
161
+ const multiFile = isArray(files);
162
+ const { extension, isCubemap } = getExtension(files);
163
+ const loader = getLoader$1(extension);
164
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
165
+ const renderer = fiber.useThree((state) => state.renderer);
166
+ React.useLayoutEffect(() => {
167
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
168
+ function clearGainmapTexture() {
169
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
170
+ }
171
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
172
+ }, [files, renderer.domElement]);
173
+ const loaderResult = fiber.useLoader(
174
+ loader,
175
+ multiFile ? [files] : files,
176
+ (loader2) => {
177
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
178
+ loader2.setRenderer?.(renderer);
179
+ }
180
+ loader2.setPath?.(path);
181
+ if (extensions) extensions(loader2);
182
+ }
183
+ );
184
+ let texture = multiFile ? (
185
+ // @ts-ignore
186
+ loaderResult[0]
187
+ ) : loaderResult;
188
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
189
+ texture = texture.renderTarget?.texture;
190
+ }
191
+ texture.mapping = isCubemap ? three.CubeReflectionMapping : three.EquirectangularReflectionMapping;
192
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
193
+ return texture;
194
+ }
195
+ const preloadDefaultOptions = {
196
+ files: defaultFiles,
197
+ path: "",
198
+ preset: void 0,
199
+ extensions: void 0
200
+ };
201
+ useEnvironment.preload = (preloadOptions) => {
202
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
203
+ let { files, path = "" } = options;
204
+ const { preset, extensions } = options;
205
+ if (preset) {
206
+ validatePreset(preset);
207
+ files = presetsObj[preset];
208
+ path = CUBEMAP_ROOT;
209
+ }
210
+ const { extension } = getExtension(files);
211
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
212
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
213
+ }
214
+ const loader = getLoader$1(extension);
215
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
216
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
217
+ loader2.setPath?.(path);
218
+ if (extensions) extensions(loader2);
219
+ });
220
+ };
221
+ const clearDefaultOptins = {
222
+ files: defaultFiles,
223
+ preset: void 0
224
+ };
225
+ useEnvironment.clear = (clearOptions) => {
226
+ const options = { ...clearDefaultOptins, ...clearOptions };
227
+ let { files } = options;
228
+ const { preset } = options;
229
+ if (preset) {
230
+ validatePreset(preset);
231
+ files = presetsObj[preset];
232
+ }
233
+ const { extension } = getExtension(files);
234
+ const loader = getLoader$1(extension);
235
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
236
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
237
+ };
238
+ function validatePreset(preset) {
239
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
240
+ }
241
+ function getExtension(files) {
242
+ const isCubemap = isArray(files) && files.length === 6;
243
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
244
+ const firstEntry = isArray(files) ? files[0] : files;
245
+ const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
246
+ return { extension, isCubemap, isGainmap };
247
+ }
248
+ function getLoader$1(extension) {
249
+ const loader = extension === "cube" ? three.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
250
+ return loader;
251
+ }
252
+
253
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
254
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
255
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
256
+ sceneProps = {
257
+ backgroundBlurriness: 0,
258
+ backgroundIntensity: 1,
259
+ backgroundRotation: [0, 0, 0],
260
+ environmentIntensity: 1,
261
+ environmentRotation: [0, 0, 0],
262
+ ...sceneProps
263
+ };
264
+ const target = resolveScene(scene || defaultScene);
265
+ const oldbg = target.background;
266
+ const oldenv = target.environment;
267
+ const oldSceneProps = {
268
+ // @ts-ignore
269
+ backgroundBlurriness: target.backgroundBlurriness,
270
+ // @ts-ignore
271
+ backgroundIntensity: target.backgroundIntensity,
272
+ // @ts-ignore
273
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
274
+ // @ts-ignore
275
+ environmentIntensity: target.environmentIntensity,
276
+ // @ts-ignore
277
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
278
+ };
279
+ if (background !== "only") target.environment = texture;
280
+ if (background) target.background = texture;
281
+ fiber.applyProps(target, sceneProps);
282
+ return () => {
283
+ if (background !== "only") target.environment = oldenv;
284
+ if (background) target.background = oldbg;
285
+ fiber.applyProps(target, oldSceneProps);
286
+ };
287
+ }
288
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
289
+ const defaultScene = fiber.useThree((state) => state.scene);
290
+ React__namespace.useLayoutEffect(() => {
291
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
292
+ });
293
+ return null;
294
+ }
295
+ function EnvironmentCube({
296
+ background = false,
297
+ scene,
298
+ blur,
299
+ backgroundBlurriness,
300
+ backgroundIntensity,
301
+ backgroundRotation,
302
+ environmentIntensity,
303
+ environmentRotation,
304
+ ...rest
305
+ }) {
306
+ const texture = useEnvironment(rest);
307
+ const defaultScene = fiber.useThree((state) => state.scene);
308
+ React__namespace.useLayoutEffect(() => {
309
+ return setEnvProps(background, scene, defaultScene, texture, {
310
+ backgroundBlurriness: blur ?? backgroundBlurriness,
311
+ backgroundIntensity,
312
+ backgroundRotation,
313
+ environmentIntensity,
314
+ environmentRotation
315
+ });
316
+ });
317
+ React__namespace.useEffect(() => {
318
+ return () => {
319
+ texture.dispose();
320
+ };
321
+ }, [texture]);
322
+ return null;
323
+ }
324
+ function EnvironmentPortal({
325
+ children,
326
+ near = 0.1,
327
+ far = 1e3,
328
+ resolution = 256,
329
+ frames = 1,
330
+ map,
331
+ background = false,
332
+ blur,
333
+ backgroundBlurriness,
334
+ backgroundIntensity,
335
+ backgroundRotation,
336
+ environmentIntensity,
337
+ environmentRotation,
338
+ scene,
339
+ files,
340
+ path,
341
+ preset = void 0,
342
+ extensions
343
+ }) {
344
+ const gl = fiber.useThree((state) => state.gl);
345
+ const defaultScene = fiber.useThree((state) => state.scene);
346
+ const camera = React__namespace.useRef(null);
347
+ const [virtualScene] = React__namespace.useState(() => new three.Scene());
348
+ const fbo = React__namespace.useMemo(() => {
349
+ const fbo2 = new three.WebGLCubeRenderTarget(resolution);
350
+ fbo2.texture.type = three.HalfFloatType;
351
+ return fbo2;
352
+ }, [resolution]);
353
+ React__namespace.useEffect(() => {
354
+ return () => {
355
+ fbo.dispose();
356
+ };
357
+ }, [fbo]);
358
+ React__namespace.useLayoutEffect(() => {
359
+ if (frames === 1) {
360
+ const autoClear = gl.autoClear;
361
+ gl.autoClear = true;
362
+ camera.current.update(gl, virtualScene);
363
+ gl.autoClear = autoClear;
364
+ }
365
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
366
+ backgroundBlurriness: blur ?? backgroundBlurriness,
367
+ backgroundIntensity,
368
+ backgroundRotation,
369
+ environmentIntensity,
370
+ environmentRotation
371
+ });
372
+ }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
373
+ let count = 1;
374
+ fiber.useFrame(() => {
375
+ if (frames === Infinity || count < frames) {
376
+ const autoClear = gl.autoClear;
377
+ gl.autoClear = true;
378
+ camera.current.update(gl, virtualScene);
379
+ gl.autoClear = autoClear;
380
+ count++;
381
+ }
382
+ });
383
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
384
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
385
+ children,
386
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
387
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
388
+ ] }),
389
+ virtualScene
390
+ ) });
391
+ }
392
+ function EnvironmentGround(props) {
393
+ const textureDefault = useEnvironment(props);
394
+ const texture = props.map || textureDefault;
395
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
396
+ React__namespace.useEffect(() => {
397
+ return () => {
398
+ textureDefault.dispose();
399
+ };
400
+ }, [textureDefault]);
401
+ const height = props.ground?.height ?? 15;
402
+ const radius = props.ground?.radius ?? 60;
403
+ const scale = props.ground?.scale ?? 1e3;
404
+ const args = React__namespace.useMemo(
405
+ () => [texture, height, radius],
406
+ [texture, height, radius]
407
+ );
408
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
410
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
411
+ ] });
412
+ }
413
+ function EnvironmentColor({ color, scene }) {
414
+ const defaultScene = fiber.useThree((state) => state.scene);
415
+ React__namespace.useLayoutEffect(() => {
416
+ if (color === void 0) return;
417
+ const target = resolveScene(scene || defaultScene);
418
+ const oldBg = target.background;
419
+ target.background = new three.Color(color);
420
+ return () => {
421
+ target.background = oldBg;
422
+ };
423
+ });
424
+ return null;
425
+ }
426
+ function EnvironmentDualSource(props) {
427
+ const { backgroundFiles, ...envProps } = props;
428
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
429
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
430
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
431
+ ] });
432
+ }
433
+ function Environment(props) {
434
+ if (props.color && !props.files && !props.preset && !props.map) {
435
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
436
+ }
437
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
438
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
439
+ }
440
+ return props.ground ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentGround, { ...props }) : props.map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props }) : props.children ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentPortal, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props });
441
+ }
442
+
68
443
  var __defProp$2 = Object.defineProperty;
69
444
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
70
445
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -244,7 +619,8 @@ function prepare(target, root, type, props) {
244
619
  object,
245
620
  eventCount: 0,
246
621
  handlers: {},
247
- isHidden: false
622
+ isHidden: false,
623
+ deferredRefs: []
248
624
  };
249
625
  if (object) object.__r3f = instance;
250
626
  }
@@ -293,7 +669,7 @@ function createOcclusionObserverNode(store, uniform) {
293
669
  let occlusionSetupPromise = null;
294
670
  function enableOcclusion(store) {
295
671
  const state = store.getState();
296
- const { internal, renderer, rootScene } = state;
672
+ const { internal, renderer } = state;
297
673
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
298
674
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
299
675
  if (!hasOcclusionSupport) {
@@ -456,6 +832,22 @@ function hasVisibilityHandlers(handlers) {
456
832
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
457
833
  }
458
834
 
835
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
836
+ function fromRef(ref) {
837
+ return { [FROM_REF]: ref };
838
+ }
839
+ function isFromRef(value) {
840
+ return value !== null && typeof value === "object" && FROM_REF in value;
841
+ }
842
+
843
+ const ONCE = Symbol.for("@react-three/fiber.once");
844
+ function once(...args) {
845
+ return { [ONCE]: args.length ? args : true };
846
+ }
847
+ function isOnce(value) {
848
+ return value !== null && typeof value === "object" && ONCE in value;
849
+ }
850
+
459
851
  const RESERVED_PROPS = [
460
852
  "children",
461
853
  "key",
@@ -526,7 +918,7 @@ function getMemoizedPrototype(root) {
526
918
  ctor = new root.constructor();
527
919
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
528
920
  }
529
- } catch (e) {
921
+ } catch {
530
922
  }
531
923
  return ctor;
532
924
  }
@@ -557,7 +949,7 @@ function applyProps(object, props) {
557
949
  const rootState = instance && findInitialRoot(instance).getState();
558
950
  const prevHandlers = instance?.eventCount;
559
951
  for (const prop in props) {
560
- let value = props[prop];
952
+ const value = props[prop];
561
953
  if (RESERVED_PROPS.includes(prop)) continue;
562
954
  if (instance && EVENT_REGEX.test(prop)) {
563
955
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -572,6 +964,25 @@ function applyProps(object, props) {
572
964
  continue;
573
965
  }
574
966
  if (value === void 0) continue;
967
+ if (isFromRef(value)) {
968
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
969
+ continue;
970
+ }
971
+ if (isOnce(value)) {
972
+ if (instance?.appliedOnce?.has(prop)) continue;
973
+ if (instance) {
974
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
975
+ instance.appliedOnce.add(prop);
976
+ }
977
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
978
+ const args = value[ONCE];
979
+ if (typeof targetRoot[targetKey] === "function") {
980
+ targetRoot[targetKey](...args === true ? [] : args);
981
+ } else if (args !== true && args.length > 0) {
982
+ targetRoot[targetKey] = args[0];
983
+ }
984
+ continue;
985
+ }
575
986
  let { root, key, target } = resolve(object, prop);
576
987
  if (target === void 0 && (typeof root !== "object" || root === null)) {
577
988
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -594,7 +1005,7 @@ function applyProps(object, props) {
594
1005
  else target.set(value);
595
1006
  } else {
596
1007
  root[key] = value;
597
- if (rootState && !rootState.linear && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
1008
+ if (rootState && rootState.renderer?.outputColorSpace === three.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
598
1009
  root[key].format === three.RGBAFormat && root[key].type === three.UnsignedByteType) {
599
1010
  root[key].colorSpace = rootState.textureColorSpace;
600
1011
  }
@@ -709,13 +1120,14 @@ function createEvents(store) {
709
1120
  for (const hit of hits) {
710
1121
  let eventObject = hit.object;
711
1122
  while (eventObject) {
712
- if (eventObject.__r3f?.eventCount)
1123
+ if (eventObject.__r3f?.eventCount) {
713
1124
  intersections.push({ ...hit, eventObject });
1125
+ }
714
1126
  eventObject = eventObject.parent;
715
1127
  }
716
1128
  }
717
1129
  if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
718
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1130
+ for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
719
1131
  if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
720
1132
  }
721
1133
  }
@@ -745,12 +1157,12 @@ function createEvents(store) {
745
1157
  releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
746
1158
  }
747
1159
  };
748
- let extractEventProps = {};
749
- for (let prop in event) {
750
- let property = event[prop];
1160
+ const extractEventProps = {};
1161
+ for (const prop in event) {
1162
+ const property = event[prop];
751
1163
  if (typeof property !== "function") extractEventProps[prop] = property;
752
1164
  }
753
- let raycastEvent = {
1165
+ const raycastEvent = {
754
1166
  ...hit,
755
1167
  ...extractEventProps,
756
1168
  pointer,
@@ -950,7 +1362,7 @@ function createPointerEvents(store) {
950
1362
  return {
951
1363
  priority: 1,
952
1364
  enabled: true,
953
- compute(event, state, previous) {
1365
+ compute(event, state) {
954
1366
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
955
1367
  state.raycaster.setFromCamera(state.pointer, state.camera);
956
1368
  },
@@ -1048,300 +1460,26 @@ function notifyAlpha({ message, link }) {
1048
1460
  }
1049
1461
  }
1050
1462
 
1051
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1052
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1053
- const createStore = (invalidate, advance) => {
1054
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1055
- const position = new three.Vector3();
1056
- const defaultTarget = new three.Vector3();
1057
- const tempTarget = new three.Vector3();
1058
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1059
- const { width, height, top, left } = size;
1060
- const aspect = width / height;
1061
- if (target.isVector3) tempTarget.copy(target);
1062
- else tempTarget.set(...target);
1063
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1064
- if (isOrthographicCamera(camera)) {
1065
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1066
- } else {
1067
- const fov = camera.fov * Math.PI / 180;
1068
- const h = 2 * Math.tan(fov / 2) * distance;
1069
- const w = h * (width / height);
1070
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1071
- }
1072
- }
1073
- let performanceTimeout = void 0;
1074
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1075
- const pointer = new three.Vector2();
1076
- const rootState = {
1077
- set,
1078
- get,
1079
- // Mock objects that have to be configured
1080
- gl: null,
1081
- renderer: null,
1082
- camera: null,
1083
- frustum: new three.Frustum(),
1084
- autoUpdateFrustum: true,
1085
- raycaster: null,
1086
- events: { priority: 1, enabled: true, connected: false },
1087
- scene: null,
1088
- rootScene: null,
1089
- xr: null,
1090
- inspector: null,
1091
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1092
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1093
- legacy: false,
1094
- linear: false,
1095
- flat: false,
1096
- textureColorSpace: "srgb",
1097
- isLegacy: false,
1098
- webGPUSupported: false,
1099
- isNative: false,
1100
- controls: null,
1101
- pointer,
1102
- mouse: pointer,
1103
- frameloop: "always",
1104
- onPointerMissed: void 0,
1105
- onDragOverMissed: void 0,
1106
- onDropMissed: void 0,
1107
- performance: {
1108
- current: 1,
1109
- min: 0.5,
1110
- max: 1,
1111
- debounce: 200,
1112
- regress: () => {
1113
- const state2 = get();
1114
- if (performanceTimeout) clearTimeout(performanceTimeout);
1115
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1116
- performanceTimeout = setTimeout(
1117
- () => setPerformanceCurrent(get().performance.max),
1118
- state2.performance.debounce
1119
- );
1120
- }
1121
- },
1122
- size: { width: 0, height: 0, top: 0, left: 0 },
1123
- viewport: {
1124
- initialDpr: 0,
1125
- dpr: 0,
1126
- width: 0,
1127
- height: 0,
1128
- top: 0,
1129
- left: 0,
1130
- aspect: 0,
1131
- distance: 0,
1132
- factor: 0,
1133
- getCurrentViewport
1134
- },
1135
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1136
- setSize: (width, height, top = 0, left = 0) => {
1137
- const camera = get().camera;
1138
- const size = { width, height, top, left };
1139
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1140
- },
1141
- setDpr: (dpr) => set((state2) => {
1142
- const resolved = calculateDpr(dpr);
1143
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1144
- }),
1145
- setFrameloop: (frameloop = "always") => {
1146
- set(() => ({ frameloop }));
1147
- },
1148
- setError: (error) => set(() => ({ error })),
1149
- error: null,
1150
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1151
- uniforms: {},
1152
- nodes: {},
1153
- textures: /* @__PURE__ */ new Map(),
1154
- postProcessing: null,
1155
- passes: {},
1156
- previousRoot: void 0,
1157
- internal: {
1158
- // Events
1159
- interaction: [],
1160
- hovered: /* @__PURE__ */ new Map(),
1161
- subscribers: [],
1162
- initialClick: [0, 0],
1163
- initialHits: [],
1164
- capturedMap: /* @__PURE__ */ new Map(),
1165
- lastEvent: React__namespace.createRef(),
1166
- // Visibility tracking (onFramed, onOccluded, onVisible)
1167
- visibilityRegistry: /* @__PURE__ */ new Map(),
1168
- // Occlusion system (WebGPU only)
1169
- occlusionEnabled: false,
1170
- occlusionObserver: null,
1171
- occlusionCache: /* @__PURE__ */ new Map(),
1172
- helperGroup: null,
1173
- // Updates
1174
- active: false,
1175
- frames: 0,
1176
- priority: 0,
1177
- subscribe: (ref, priority, store) => {
1178
- const internal = get().internal;
1179
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1180
- internal.subscribers.push({ ref, priority, store });
1181
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1182
- return () => {
1183
- const internal2 = get().internal;
1184
- if (internal2?.subscribers) {
1185
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1186
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1187
- }
1188
- };
1189
- },
1190
- // Renderer Storage (single source of truth)
1191
- actualRenderer: null,
1192
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1193
- scheduler: null
1194
- }
1195
- };
1196
- return rootState;
1197
- });
1198
- const state = rootStore.getState();
1199
- Object.defineProperty(state, "gl", {
1200
- get() {
1201
- const currentState = rootStore.getState();
1202
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1203
- const stack = new Error().stack || "";
1204
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1205
- if (!isInternalAccess) {
1206
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1207
- notifyDepreciated({
1208
- heading: "Accessing state.gl in WebGPU mode",
1209
- body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
1210
- });
1211
- }
1212
- }
1213
- return currentState.internal.actualRenderer;
1214
- },
1215
- set(value) {
1216
- rootStore.getState().internal.actualRenderer = value;
1217
- },
1218
- enumerable: true,
1219
- configurable: true
1220
- });
1221
- Object.defineProperty(state, "renderer", {
1222
- get() {
1223
- return rootStore.getState().internal.actualRenderer;
1224
- },
1225
- set(value) {
1226
- rootStore.getState().internal.actualRenderer = value;
1227
- },
1228
- enumerable: true,
1229
- configurable: true
1230
- });
1231
- let oldScene = state.scene;
1232
- rootStore.subscribe(() => {
1233
- const currentState = rootStore.getState();
1234
- const { scene, rootScene, set } = currentState;
1235
- if (scene !== oldScene) {
1236
- oldScene = scene;
1237
- if (scene?.isScene && scene !== rootScene) {
1238
- set({ rootScene: scene });
1239
- }
1240
- }
1241
- });
1242
- let oldSize = state.size;
1243
- let oldDpr = state.viewport.dpr;
1244
- let oldCamera = state.camera;
1245
- rootStore.subscribe(() => {
1246
- const { camera, size, viewport, set, internal } = rootStore.getState();
1247
- const actualRenderer = internal.actualRenderer;
1248
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1249
- oldSize = size;
1250
- oldDpr = viewport.dpr;
1251
- updateCamera(camera, size);
1252
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1253
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1254
- actualRenderer.setSize(size.width, size.height, updateStyle);
1255
- }
1256
- if (camera !== oldCamera) {
1257
- oldCamera = camera;
1258
- const { rootScene } = rootStore.getState();
1259
- if (camera && rootScene && !camera.parent) {
1260
- rootScene.add(camera);
1261
- }
1262
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1263
- const currentState = rootStore.getState();
1264
- if (currentState.autoUpdateFrustum && camera) {
1265
- updateFrustum(camera, currentState.frustum);
1266
- }
1267
- }
1268
- });
1269
- rootStore.subscribe((state2) => invalidate(state2));
1270
- return rootStore;
1271
- };
1272
-
1273
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1274
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1275
- function getLoader(Proto) {
1276
- if (isConstructor$1(Proto)) {
1277
- let loader = memoizedLoaders.get(Proto);
1278
- if (!loader) {
1279
- loader = new Proto();
1280
- memoizedLoaders.set(Proto, loader);
1281
- }
1282
- return loader;
1283
- }
1284
- return Proto;
1285
- }
1286
- function loadingFn(extensions, onProgress) {
1287
- return function(Proto, input) {
1288
- const loader = getLoader(Proto);
1289
- if (extensions) extensions(loader);
1290
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1291
- return loader.loadAsync(input, onProgress).then((data) => {
1292
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1293
- return data;
1294
- });
1295
- }
1296
- return new Promise(
1297
- (res, reject) => loader.load(
1298
- input,
1299
- (data) => {
1300
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1301
- res(data);
1302
- },
1303
- onProgress,
1304
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1305
- )
1306
- );
1307
- };
1308
- }
1309
- function useLoader(loader, input, extensions, onProgress) {
1310
- const keys = Array.isArray(input) ? input : [input];
1311
- const fn = loadingFn(extensions, onProgress);
1312
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1313
- return Array.isArray(input) ? results : results[0];
1314
- }
1315
- useLoader.preload = function(loader, input, extensions, onProgress) {
1316
- const keys = Array.isArray(input) ? input : [input];
1317
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1318
- };
1319
- useLoader.clear = function(loader, input) {
1320
- const keys = Array.isArray(input) ? input : [input];
1321
- keys.forEach((key) => suspendReact.clear([loader, key]));
1322
- };
1323
- useLoader.loader = getLoader;
1324
-
1325
- var __defProp$1 = Object.defineProperty;
1326
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1327
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1328
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1329
- class PhaseGraph {
1330
- constructor() {
1331
- /** Ordered list of phase nodes */
1332
- __publicField$1(this, "phases", []);
1333
- /** Quick lookup by name */
1334
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1335
- /** Cached ordered names (invalidated on changes) */
1336
- __publicField$1(this, "orderedNamesCache", null);
1337
- this.initializeDefaultPhases();
1338
- }
1339
- //* Initialization --------------------------------
1340
- initializeDefaultPhases() {
1341
- for (const name of DEFAULT_PHASES) {
1342
- const node = { name, isAutoGenerated: false };
1343
- this.phases.push(node);
1344
- this.phaseMap.set(name, node);
1463
+ var __defProp$1 = Object.defineProperty;
1464
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1465
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1466
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1467
+ class PhaseGraph {
1468
+ constructor() {
1469
+ /** Ordered list of phase nodes */
1470
+ __publicField$1(this, "phases", []);
1471
+ /** Quick lookup by name */
1472
+ __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1473
+ /** Cached ordered names (invalidated on changes) */
1474
+ __publicField$1(this, "orderedNamesCache", null);
1475
+ this.initializeDefaultPhases();
1476
+ }
1477
+ //* Initialization --------------------------------
1478
+ initializeDefaultPhases() {
1479
+ for (const name of DEFAULT_PHASES) {
1480
+ const node = { name, isAutoGenerated: false };
1481
+ this.phases.push(node);
1482
+ this.phaseMap.set(name, node);
1345
1483
  }
1346
1484
  this.invalidateCache();
1347
1485
  }
@@ -1360,8 +1498,9 @@ class PhaseGraph {
1360
1498
  const node = { name, isAutoGenerated: false };
1361
1499
  let insertIndex = this.phases.length;
1362
1500
  const targetIndex = this.getPhaseIndex(before ?? after);
1363
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1364
- else {
1501
+ if (targetIndex !== -1) {
1502
+ insertIndex = before ? targetIndex : targetIndex + 1;
1503
+ } else {
1365
1504
  const constraintType = before ? "before" : "after";
1366
1505
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1367
1506
  }
@@ -2226,132 +2365,445 @@ const _Scheduler = class _Scheduler {
2226
2365
  console.error(`[Scheduler] Error in global job "${job.id}":`, error);
2227
2366
  }
2228
2367
  }
2229
- }
2230
- /**
2231
- * Execute all jobs for a single root in sorted order.
2232
- * Rebuilds sorted job list if needed, then dispatches each job.
2233
- * Errors are caught and propagated via triggerError.
2234
- * @param {RootEntry} root - The root entry to tick
2235
- * @param {number} timestamp - RAF timestamp in milliseconds
2236
- * @param {number} delta - Time since last frame in seconds
2237
- * @returns {void}
2238
- * @private
2239
- */
2240
- tickRoot(root, timestamp, delta) {
2241
- if (root.needsRebuild) {
2242
- root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2243
- root.needsRebuild = false;
2244
- }
2245
- const providedState = root.getState?.() ?? {};
2246
- const frameState = {
2247
- ...providedState,
2248
- time: timestamp,
2249
- delta,
2250
- elapsed: this.loopState.elapsedTime / 1e3,
2251
- // Convert ms to seconds
2252
- frame: this.loopState.frameCount
2253
- };
2254
- for (const job of root.sortedJobs) {
2255
- if (!shouldRun(job, timestamp)) continue;
2256
- try {
2257
- job.callback(frameState, delta);
2258
- } catch (error) {
2259
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2260
- this.triggerError(error instanceof Error ? error : new Error(String(error)));
2368
+ }
2369
+ /**
2370
+ * Execute all jobs for a single root in sorted order.
2371
+ * Rebuilds sorted job list if needed, then dispatches each job.
2372
+ * Errors are caught and propagated via triggerError.
2373
+ * @param {RootEntry} root - The root entry to tick
2374
+ * @param {number} timestamp - RAF timestamp in milliseconds
2375
+ * @param {number} delta - Time since last frame in seconds
2376
+ * @returns {void}
2377
+ * @private
2378
+ */
2379
+ tickRoot(root, timestamp, delta) {
2380
+ if (root.needsRebuild) {
2381
+ root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2382
+ root.needsRebuild = false;
2383
+ }
2384
+ const providedState = root.getState?.() ?? {};
2385
+ const frameState = {
2386
+ ...providedState,
2387
+ time: timestamp,
2388
+ delta,
2389
+ elapsed: this.loopState.elapsedTime / 1e3,
2390
+ // Convert ms to seconds
2391
+ frame: this.loopState.frameCount
2392
+ };
2393
+ for (const job of root.sortedJobs) {
2394
+ if (!shouldRun(job, timestamp)) continue;
2395
+ try {
2396
+ job.callback(frameState, delta);
2397
+ } catch (error) {
2398
+ console.error(`[Scheduler] Error in job "${job.id}":`, error);
2399
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
2400
+ }
2401
+ }
2402
+ }
2403
+ //* Debug & Inspection Methods ================================
2404
+ /**
2405
+ * Get the total number of registered jobs across all roots.
2406
+ * Includes both per-root jobs and global before/after jobs.
2407
+ * @returns {number} Total job count
2408
+ */
2409
+ getJobCount() {
2410
+ let count = 0;
2411
+ for (const root of this.roots.values()) {
2412
+ count += root.jobs.size;
2413
+ }
2414
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2415
+ }
2416
+ /**
2417
+ * Get all registered job IDs across all roots.
2418
+ * Includes both per-root jobs and global before/after jobs.
2419
+ * @returns {string[]} Array of all job IDs
2420
+ */
2421
+ getJobIds() {
2422
+ const ids = [];
2423
+ for (const root of this.roots.values()) {
2424
+ ids.push(...root.jobs.keys());
2425
+ }
2426
+ ids.push(...this.globalBeforeJobs.keys());
2427
+ ids.push(...this.globalAfterJobs.keys());
2428
+ return ids;
2429
+ }
2430
+ /**
2431
+ * Get the number of registered roots (Canvas instances).
2432
+ * @returns {number} Number of registered roots
2433
+ */
2434
+ getRootCount() {
2435
+ return this.roots.size;
2436
+ }
2437
+ /**
2438
+ * Check if any user (non-system) jobs are registered in a specific phase.
2439
+ * Used by the default render job to know if a user has taken over rendering.
2440
+ *
2441
+ * @param phase The phase to check
2442
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2443
+ * @returns true if any user jobs exist in the phase
2444
+ */
2445
+ hasUserJobsInPhase(phase, rootId) {
2446
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2447
+ return rootsToCheck.some((root) => {
2448
+ if (!root) return false;
2449
+ for (const job of root.jobs.values()) {
2450
+ if (job.phase === phase && !job.system && job.enabled) return true;
2451
+ }
2452
+ return false;
2453
+ });
2454
+ }
2455
+ //* Utility Methods ================================
2456
+ /**
2457
+ * Generate a unique root ID for automatic root registration.
2458
+ * @returns {string} A unique root ID in the format 'root_N'
2459
+ */
2460
+ generateRootId() {
2461
+ return `root_${this.nextRootIndex++}`;
2462
+ }
2463
+ /**
2464
+ * Generate a unique job ID.
2465
+ * @returns {string} A unique job ID in the format 'job_N'
2466
+ * @private
2467
+ */
2468
+ generateJobId() {
2469
+ return `job_${this.nextJobIndex}`;
2470
+ }
2471
+ /**
2472
+ * Normalize before/after constraints to a Set.
2473
+ * Handles undefined, single string, or array inputs.
2474
+ * @param {string | string[] | undefined} value - The constraint value(s)
2475
+ * @returns {Set<string>} Normalized Set of constraint strings
2476
+ * @private
2477
+ */
2478
+ normalizeConstraints(value) {
2479
+ if (!value) return /* @__PURE__ */ new Set();
2480
+ if (Array.isArray(value)) return new Set(value);
2481
+ return /* @__PURE__ */ new Set([value]);
2482
+ }
2483
+ };
2484
+ //* Static State & Methods (Singleton Usage) ================================
2485
+ //* Cross-Bundle Singleton Key ==============================
2486
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2487
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2488
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2489
+ let Scheduler = _Scheduler;
2490
+ const getScheduler = () => Scheduler.get();
2491
+ if (hmrData) {
2492
+ hmrData.accept?.();
2493
+ }
2494
+
2495
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2496
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2497
+ const createStore = (invalidate, advance) => {
2498
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2499
+ const position = new three.Vector3();
2500
+ const defaultTarget = new three.Vector3();
2501
+ const tempTarget = new three.Vector3();
2502
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2503
+ const { width, height, top, left } = size;
2504
+ const aspect = width / height;
2505
+ if (target.isVector3) tempTarget.copy(target);
2506
+ else tempTarget.set(...target);
2507
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2508
+ if (isOrthographicCamera(camera)) {
2509
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2510
+ } else {
2511
+ const fov = camera.fov * Math.PI / 180;
2512
+ const h = 2 * Math.tan(fov / 2) * distance;
2513
+ const w = h * (width / height);
2514
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2515
+ }
2516
+ }
2517
+ let performanceTimeout = void 0;
2518
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2519
+ const pointer = new three.Vector2();
2520
+ const rootState = {
2521
+ set,
2522
+ get,
2523
+ // Mock objects that have to be configured
2524
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2525
+ primaryStore: null,
2526
+ gl: null,
2527
+ renderer: null,
2528
+ camera: null,
2529
+ frustum: new three.Frustum(),
2530
+ autoUpdateFrustum: true,
2531
+ raycaster: null,
2532
+ events: { priority: 1, enabled: true, connected: false },
2533
+ scene: null,
2534
+ rootScene: null,
2535
+ xr: null,
2536
+ inspector: null,
2537
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2538
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2539
+ textureColorSpace: three.SRGBColorSpace,
2540
+ isLegacy: false,
2541
+ webGPUSupported: false,
2542
+ isNative: false,
2543
+ controls: null,
2544
+ pointer,
2545
+ mouse: pointer,
2546
+ frameloop: "always",
2547
+ onPointerMissed: void 0,
2548
+ onDragOverMissed: void 0,
2549
+ onDropMissed: void 0,
2550
+ performance: {
2551
+ current: 1,
2552
+ min: 0.5,
2553
+ max: 1,
2554
+ debounce: 200,
2555
+ regress: () => {
2556
+ const state2 = get();
2557
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2558
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2559
+ performanceTimeout = setTimeout(
2560
+ () => setPerformanceCurrent(get().performance.max),
2561
+ state2.performance.debounce
2562
+ );
2563
+ }
2564
+ },
2565
+ size: { width: 0, height: 0, top: 0, left: 0 },
2566
+ viewport: {
2567
+ initialDpr: 0,
2568
+ dpr: 0,
2569
+ width: 0,
2570
+ height: 0,
2571
+ top: 0,
2572
+ left: 0,
2573
+ aspect: 0,
2574
+ distance: 0,
2575
+ factor: 0,
2576
+ getCurrentViewport
2577
+ },
2578
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2579
+ setSize: (width, height, top, left) => {
2580
+ const state2 = get();
2581
+ if (width === void 0) {
2582
+ set({ _sizeImperative: false });
2583
+ if (state2._sizeProps) {
2584
+ const { width: propW, height: propH } = state2._sizeProps;
2585
+ if (propW !== void 0 || propH !== void 0) {
2586
+ const currentSize = state2.size;
2587
+ const newSize = {
2588
+ width: propW ?? currentSize.width,
2589
+ height: propH ?? currentSize.height,
2590
+ top: currentSize.top,
2591
+ left: currentSize.left
2592
+ };
2593
+ set((s) => ({
2594
+ size: newSize,
2595
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2596
+ }));
2597
+ getScheduler().invalidate();
2598
+ }
2599
+ }
2600
+ return;
2601
+ }
2602
+ const w = width;
2603
+ const h = height ?? width;
2604
+ const t = top ?? state2.size.top;
2605
+ const l = left ?? state2.size.left;
2606
+ const size = { width: w, height: h, top: t, left: l };
2607
+ set((s) => ({
2608
+ size,
2609
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2610
+ _sizeImperative: true
2611
+ }));
2612
+ getScheduler().invalidate();
2613
+ },
2614
+ setDpr: (dpr) => set((state2) => {
2615
+ const resolved = calculateDpr(dpr);
2616
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2617
+ }),
2618
+ setFrameloop: (frameloop = "always") => {
2619
+ set(() => ({ frameloop }));
2620
+ },
2621
+ setError: (error) => set(() => ({ error })),
2622
+ error: null,
2623
+ //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2624
+ uniforms: {},
2625
+ nodes: {},
2626
+ textures: /* @__PURE__ */ new Map(),
2627
+ postProcessing: null,
2628
+ passes: {},
2629
+ _hmrVersion: 0,
2630
+ _sizeImperative: false,
2631
+ _sizeProps: null,
2632
+ previousRoot: void 0,
2633
+ internal: {
2634
+ // Events
2635
+ interaction: [],
2636
+ hovered: /* @__PURE__ */ new Map(),
2637
+ subscribers: [],
2638
+ initialClick: [0, 0],
2639
+ initialHits: [],
2640
+ capturedMap: /* @__PURE__ */ new Map(),
2641
+ lastEvent: React__namespace.createRef(),
2642
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2643
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2644
+ // Occlusion system (WebGPU only)
2645
+ occlusionEnabled: false,
2646
+ occlusionObserver: null,
2647
+ occlusionCache: /* @__PURE__ */ new Map(),
2648
+ helperGroup: null,
2649
+ // Updates
2650
+ active: false,
2651
+ frames: 0,
2652
+ priority: 0,
2653
+ subscribe: (ref, priority, store) => {
2654
+ const internal = get().internal;
2655
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2656
+ internal.subscribers.push({ ref, priority, store });
2657
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2658
+ return () => {
2659
+ const internal2 = get().internal;
2660
+ if (internal2?.subscribers) {
2661
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2662
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2663
+ }
2664
+ };
2665
+ },
2666
+ // Renderer Storage (single source of truth)
2667
+ actualRenderer: null,
2668
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2669
+ scheduler: null
2670
+ }
2671
+ };
2672
+ return rootState;
2673
+ });
2674
+ const state = rootStore.getState();
2675
+ Object.defineProperty(state, "gl", {
2676
+ get() {
2677
+ const currentState = rootStore.getState();
2678
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2679
+ const stack = new Error().stack || "";
2680
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2681
+ if (!isInternalAccess) {
2682
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2683
+ notifyDepreciated({
2684
+ heading: "Accessing state.gl in WebGPU mode",
2685
+ body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
2686
+ });
2687
+ }
2688
+ }
2689
+ return currentState.internal.actualRenderer;
2690
+ },
2691
+ set(value) {
2692
+ rootStore.getState().internal.actualRenderer = value;
2693
+ },
2694
+ enumerable: true,
2695
+ configurable: true
2696
+ });
2697
+ Object.defineProperty(state, "renderer", {
2698
+ get() {
2699
+ return rootStore.getState().internal.actualRenderer;
2700
+ },
2701
+ set(value) {
2702
+ rootStore.getState().internal.actualRenderer = value;
2703
+ },
2704
+ enumerable: true,
2705
+ configurable: true
2706
+ });
2707
+ let oldScene = state.scene;
2708
+ rootStore.subscribe(() => {
2709
+ const currentState = rootStore.getState();
2710
+ const { scene, rootScene, set } = currentState;
2711
+ if (scene !== oldScene) {
2712
+ oldScene = scene;
2713
+ if (scene?.isScene && scene !== rootScene) {
2714
+ set({ rootScene: scene });
2715
+ }
2716
+ }
2717
+ });
2718
+ let oldSize = state.size;
2719
+ let oldDpr = state.viewport.dpr;
2720
+ let oldCamera = state.camera;
2721
+ rootStore.subscribe(() => {
2722
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2723
+ const actualRenderer = internal.actualRenderer;
2724
+ const canvasTarget = internal.canvasTarget;
2725
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2726
+ oldSize = size;
2727
+ oldDpr = viewport.dpr;
2728
+ updateCamera(camera, size);
2729
+ if (canvasTarget) {
2730
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2731
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2732
+ canvasTarget.setSize(size.width, size.height, updateStyle);
2733
+ } else {
2734
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2735
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2736
+ actualRenderer.setSize(size.width, size.height, updateStyle);
2737
+ }
2738
+ }
2739
+ if (camera !== oldCamera) {
2740
+ oldCamera = camera;
2741
+ const { rootScene } = rootStore.getState();
2742
+ if (camera && rootScene && !camera.parent) {
2743
+ rootScene.add(camera);
2744
+ }
2745
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2746
+ const currentState = rootStore.getState();
2747
+ if (currentState.autoUpdateFrustum && camera) {
2748
+ updateFrustum(camera, currentState.frustum);
2261
2749
  }
2262
2750
  }
2263
- }
2264
- //* Debug & Inspection Methods ================================
2265
- /**
2266
- * Get the total number of registered jobs across all roots.
2267
- * Includes both per-root jobs and global before/after jobs.
2268
- * @returns {number} Total job count
2269
- */
2270
- getJobCount() {
2271
- let count = 0;
2272
- for (const root of this.roots.values()) {
2273
- count += root.jobs.size;
2751
+ });
2752
+ rootStore.subscribe((state2) => invalidate(state2));
2753
+ return rootStore;
2754
+ };
2755
+
2756
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2757
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2758
+ function getLoader(Proto) {
2759
+ if (isConstructor$1(Proto)) {
2760
+ let loader = memoizedLoaders.get(Proto);
2761
+ if (!loader) {
2762
+ loader = new Proto();
2763
+ memoizedLoaders.set(Proto, loader);
2274
2764
  }
2275
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2765
+ return loader;
2276
2766
  }
2277
- /**
2278
- * Get all registered job IDs across all roots.
2279
- * Includes both per-root jobs and global before/after jobs.
2280
- * @returns {string[]} Array of all job IDs
2281
- */
2282
- getJobIds() {
2283
- const ids = [];
2284
- for (const root of this.roots.values()) {
2285
- ids.push(...root.jobs.keys());
2767
+ return Proto;
2768
+ }
2769
+ function loadingFn(extensions, onProgress) {
2770
+ return function(Proto, input) {
2771
+ const loader = getLoader(Proto);
2772
+ if (extensions) extensions(loader);
2773
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2774
+ return loader.loadAsync(input, onProgress).then((data) => {
2775
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2776
+ return data;
2777
+ });
2286
2778
  }
2287
- ids.push(...this.globalBeforeJobs.keys());
2288
- ids.push(...this.globalAfterJobs.keys());
2289
- return ids;
2290
- }
2291
- /**
2292
- * Get the number of registered roots (Canvas instances).
2293
- * @returns {number} Number of registered roots
2294
- */
2295
- getRootCount() {
2296
- return this.roots.size;
2297
- }
2298
- /**
2299
- * Check if any user (non-system) jobs are registered in a specific phase.
2300
- * Used by the default render job to know if a user has taken over rendering.
2301
- *
2302
- * @param phase The phase to check
2303
- * @param rootId Optional root ID to check (checks all roots if not provided)
2304
- * @returns true if any user jobs exist in the phase
2305
- */
2306
- hasUserJobsInPhase(phase, rootId) {
2307
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2308
- return rootsToCheck.some((root) => {
2309
- if (!root) return false;
2310
- for (const job of root.jobs.values()) {
2311
- if (job.phase === phase && !job.system && job.enabled) return true;
2312
- }
2313
- return false;
2314
- });
2315
- }
2316
- //* Utility Methods ================================
2317
- /**
2318
- * Generate a unique root ID for automatic root registration.
2319
- * @returns {string} A unique root ID in the format 'root_N'
2320
- */
2321
- generateRootId() {
2322
- return `root_${this.nextRootIndex++}`;
2323
- }
2324
- /**
2325
- * Generate a unique job ID.
2326
- * @returns {string} A unique job ID in the format 'job_N'
2327
- * @private
2328
- */
2329
- generateJobId() {
2330
- return `job_${this.nextJobIndex}`;
2331
- }
2332
- /**
2333
- * Normalize before/after constraints to a Set.
2334
- * Handles undefined, single string, or array inputs.
2335
- * @param {string | string[] | undefined} value - The constraint value(s)
2336
- * @returns {Set<string>} Normalized Set of constraint strings
2337
- * @private
2338
- */
2339
- normalizeConstraints(value) {
2340
- if (!value) return /* @__PURE__ */ new Set();
2341
- if (Array.isArray(value)) return new Set(value);
2342
- return /* @__PURE__ */ new Set([value]);
2343
- }
2344
- };
2345
- //* Static State & Methods (Singleton Usage) ================================
2346
- //* Cross-Bundle Singleton Key ==============================
2347
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2348
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2349
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2350
- let Scheduler = _Scheduler;
2351
- const getScheduler = () => Scheduler.get();
2352
- if (hmrData) {
2353
- hmrData.accept?.();
2779
+ return new Promise(
2780
+ (res, reject) => loader.load(
2781
+ input,
2782
+ (data) => {
2783
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2784
+ res(data);
2785
+ },
2786
+ onProgress,
2787
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2788
+ )
2789
+ );
2790
+ };
2791
+ }
2792
+ function useLoader(loader, input, extensions, onProgress) {
2793
+ const keys = Array.isArray(input) ? input : [input];
2794
+ const fn = loadingFn(extensions, onProgress);
2795
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2796
+ return Array.isArray(input) ? results : results[0];
2354
2797
  }
2798
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2799
+ const keys = Array.isArray(input) ? input : [input];
2800
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
2801
+ };
2802
+ useLoader.clear = function(loader, input) {
2803
+ const keys = Array.isArray(input) ? input : [input];
2804
+ keys.forEach((key) => suspendReact.clear([loader, key]));
2805
+ };
2806
+ useLoader.loader = getLoader;
2355
2807
 
2356
2808
  function useFrame(callback, priorityOrOptions) {
2357
2809
  const store = React__namespace.useContext(context);
@@ -2532,6 +2984,9 @@ function useTexture(input, optionsOrOnLoad) {
2532
2984
  const textureCache = useThree((state) => state.textures);
2533
2985
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2534
2986
  const { onLoad, cache = false } = options;
2987
+ const onLoadRef = React.useRef(onLoad);
2988
+ onLoadRef.current = onLoad;
2989
+ const onLoadCalledForRef = React.useRef(null);
2535
2990
  const urls = React.useMemo(() => getUrls(input), [input]);
2536
2991
  const cachedResult = React.useMemo(() => {
2537
2992
  if (!cache) return null;
@@ -2542,9 +2997,13 @@ function useTexture(input, optionsOrOnLoad) {
2542
2997
  three.TextureLoader,
2543
2998
  IsObject(input) ? Object.values(input) : input
2544
2999
  );
3000
+ const inputKey = urls.join("\0");
2545
3001
  React.useLayoutEffect(() => {
2546
- if (!cachedResult) onLoad?.(loadedTextures);
2547
- }, [onLoad, cachedResult, loadedTextures]);
3002
+ if (cachedResult) return;
3003
+ if (onLoadCalledForRef.current === inputKey) return;
3004
+ onLoadCalledForRef.current = inputKey;
3005
+ onLoadRef.current?.(loadedTextures);
3006
+ }, [cachedResult, loadedTextures, inputKey]);
2548
3007
  React.useEffect(() => {
2549
3008
  if (cachedResult) return;
2550
3009
  if ("initTexture" in renderer) {
@@ -2711,14 +3170,31 @@ function useTextures() {
2711
3170
  }, [store]);
2712
3171
  }
2713
3172
 
2714
- function useRenderTarget(width, height, options) {
3173
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2715
3174
  const isLegacy = useThree((s) => s.isLegacy);
2716
3175
  const size = useThree((s) => s.size);
3176
+ let width;
3177
+ let height;
3178
+ let opts;
3179
+ if (typeof widthOrOptions === "object") {
3180
+ opts = widthOrOptions;
3181
+ } else if (typeof widthOrOptions === "number") {
3182
+ width = widthOrOptions;
3183
+ if (typeof heightOrOptions === "object") {
3184
+ height = widthOrOptions;
3185
+ opts = heightOrOptions;
3186
+ } else if (typeof heightOrOptions === "number") {
3187
+ height = heightOrOptions;
3188
+ opts = options;
3189
+ } else {
3190
+ height = widthOrOptions;
3191
+ }
3192
+ }
2717
3193
  return React.useMemo(() => {
2718
3194
  const w = width ?? size.width;
2719
3195
  const h = height ?? size.height;
2720
- return new three.WebGLRenderTarget(w, h, options);
2721
- }, [width, height, size.width, size.height, options, isLegacy]);
3196
+ return new three.WebGLRenderTarget(w, h, opts);
3197
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2722
3198
  }
2723
3199
 
2724
3200
  function useStore() {
@@ -2768,28 +3244,18 @@ function addTail(callback) {
2768
3244
  function invalidate(state, frames = 1, stackFrames = false) {
2769
3245
  getScheduler().invalidate(frames, stackFrames);
2770
3246
  }
2771
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3247
+ function advance(timestamp) {
2772
3248
  getScheduler().step(timestamp);
2773
3249
  }
2774
3250
 
2775
- const version = "10.0.0-alpha.1";
3251
+ const version = "10.0.0-alpha.2";
2776
3252
  const packageData = {
2777
3253
  version: version};
2778
3254
 
2779
3255
  function Xb(Tt) {
2780
3256
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2781
3257
  }
2782
- var Rm = { exports: {} }, Og = { exports: {} };
2783
- /**
2784
- * @license React
2785
- * react-reconciler.production.js
2786
- *
2787
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2788
- *
2789
- * This source code is licensed under the MIT license found in the
2790
- * LICENSE file in the root directory of this source tree.
2791
- */
2792
- var _b;
3258
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2793
3259
  function Kb() {
2794
3260
  return _b || (_b = 1, (function(Tt) {
2795
3261
  Tt.exports = function(m) {
@@ -3861,7 +4327,6 @@ Error generating stack: ` + l.message + `
3861
4327
  if (J === cl || J === jc) throw J;
3862
4328
  var Ge = Yn(29, J, null, P.mode);
3863
4329
  return Ge.lanes = H, Ge.return = P, Ge;
3864
- } finally {
3865
4330
  }
3866
4331
  };
3867
4332
  }
@@ -4515,7 +4980,6 @@ Error generating stack: ` + l.message + `
4515
4980
  var h = r.lastRenderedState, y = d(h, a);
4516
4981
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4517
4982
  } catch {
4518
- } finally {
4519
4983
  }
4520
4984
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4521
4985
  }
@@ -6936,10 +7400,7 @@ Error generating stack: ` + l.message + `
6936
7400
  function vr(t, r) {
6937
7401
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6938
7402
  }
6939
- var ie = {}, Fm = React__default, tt = Tb__default, 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");
6940
- var gc = Symbol.for("react.activity");
6941
- var $r = Symbol.for("react.memo_cache_sentinel");
6942
- 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;
7403
+ var ie = {}, Fm = React__default, tt = Tb__default, 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;
6943
7404
  m.cloneMutableInstance;
6944
7405
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6945
7406
  m.cloneMutableTextInstance;
@@ -7308,17 +7769,7 @@ No matching component was found for:
7308
7769
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
7309
7770
  })(Og)), Og.exports;
7310
7771
  }
7311
- var Mg = { exports: {} };
7312
- /**
7313
- * @license React
7314
- * react-reconciler.development.js
7315
- *
7316
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7317
- *
7318
- * This source code is licensed under the MIT license found in the
7319
- * LICENSE file in the root directory of this source tree.
7320
- */
7321
- var Rb;
7772
+ var Mg = { exports: {} }, Rb;
7322
7773
  function e0() {
7323
7774
  return Rb || (Rb = 1, (function(Tt) {
7324
7775
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -13085,10 +13536,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
13085
13536
  function Ic() {
13086
13537
  return di;
13087
13538
  }
13088
- var le = {}, qm = React__default, St = Tb__default, 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");
13089
- var Ds = Symbol.for("react.activity");
13090
- var Bh = Symbol.for("react.memo_cache_sentinel");
13091
- 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;
13539
+ var le = {}, qm = React__default, St = Tb__default, 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;
13092
13540
  m.cloneMutableInstance;
13093
13541
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
13094
13542
  m.cloneMutableTextInstance;
@@ -14056,15 +14504,6 @@ function n0() {
14056
14504
  var t0 = n0();
14057
14505
  const r0 = Xb(t0);
14058
14506
 
14059
- /**
14060
- * @license React
14061
- * react-reconciler-constants.production.js
14062
- *
14063
- * Copyright (c) Meta Platforms, Inc. and affiliates.
14064
- *
14065
- * This source code is licensed under the MIT license found in the
14066
- * LICENSE file in the root directory of this source tree.
14067
- */
14068
14507
  const t = 1, o = 8, r = 32, e = 2;
14069
14508
 
14070
14509
  function createReconciler(config) {
@@ -14091,10 +14530,11 @@ function extend(objects) {
14091
14530
  function validateInstance(type, props) {
14092
14531
  const name = toPascalCase(type);
14093
14532
  const target = catalogue[name];
14094
- if (type !== "primitive" && !target)
14533
+ if (type !== "primitive" && !target) {
14095
14534
  throw new Error(
14096
14535
  `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`
14097
14536
  );
14537
+ }
14098
14538
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
14099
14539
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
14100
14540
  }
@@ -14258,6 +14698,7 @@ function swapInstances() {
14258
14698
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14259
14699
  instance.object.__r3f = instance;
14260
14700
  setFiberRef(fiber, instance.object);
14701
+ delete instance.appliedOnce;
14261
14702
  applyProps(instance.object, instance.props);
14262
14703
  if (instance.props.attach) {
14263
14704
  attach(parent, instance);
@@ -14331,8 +14772,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14331
14772
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14332
14773
  if (isTailSibling) swapInstances();
14333
14774
  },
14334
- finalizeInitialChildren: () => false,
14335
- commitMount() {
14775
+ finalizeInitialChildren: (instance) => {
14776
+ for (const prop in instance.props) {
14777
+ if (isFromRef(instance.props[prop])) return true;
14778
+ }
14779
+ return false;
14780
+ },
14781
+ commitMount(instance) {
14782
+ const resolved = {};
14783
+ for (const prop in instance.props) {
14784
+ const value = instance.props[prop];
14785
+ if (isFromRef(value)) {
14786
+ const ref = value[FROM_REF];
14787
+ if (ref.current != null) resolved[prop] = ref.current;
14788
+ }
14789
+ }
14790
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14336
14791
  },
14337
14792
  getPublicInstance: (instance) => instance?.object,
14338
14793
  prepareForCommit: () => null,
@@ -14545,14 +15000,17 @@ function createRoot(canvas) {
14545
15000
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14546
15001
  let onCreated;
14547
15002
  let lastCamera;
14548
- let lastConfiguredProps = {};
15003
+ const lastConfiguredProps = {};
14549
15004
  let configured = false;
14550
15005
  let pending = null;
14551
15006
  return {
14552
15007
  async configure(props = {}) {
14553
15008
  let resolve;
14554
15009
  pending = new Promise((_resolve) => resolve = _resolve);
14555
- let {
15010
+ const {
15011
+ id: canvasId,
15012
+ primaryCanvas,
15013
+ scheduler: schedulerConfig,
14556
15014
  gl: glConfig,
14557
15015
  renderer: rendererConfig,
14558
15016
  size: propsSize,
@@ -14560,10 +15018,7 @@ function createRoot(canvas) {
14560
15018
  events,
14561
15019
  onCreated: onCreatedCallback,
14562
15020
  shadows = false,
14563
- linear = false,
14564
- flat = false,
14565
15021
  textureColorSpace = three.SRGBColorSpace,
14566
- legacy = false,
14567
15022
  orthographic = false,
14568
15023
  frameloop = "always",
14569
15024
  dpr = [1, 2],
@@ -14574,9 +15029,11 @@ function createRoot(canvas) {
14574
15029
  onDragOverMissed,
14575
15030
  onDropMissed,
14576
15031
  autoUpdateFrustum = true,
14577
- occlusion = false
15032
+ occlusion = false,
15033
+ _sizeProps,
15034
+ forceEven
14578
15035
  } = props;
14579
- let state = store.getState();
15036
+ const state = store.getState();
14580
15037
  const defaultGLProps = {
14581
15038
  canvas,
14582
15039
  powerPreference: "high-performance",
@@ -14588,22 +15045,34 @@ function createRoot(canvas) {
14588
15045
  "WebGPURenderer (renderer prop) is not available in this build. Use @react-three/fiber or @react-three/fiber/webgpu instead."
14589
15046
  );
14590
15047
  }
14591
- (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
15048
+ const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU);
14592
15049
  if (glConfig && rendererConfig) {
14593
15050
  throw new Error("Cannot use both gl and renderer props at the same time");
14594
15051
  }
14595
15052
  let renderer = state.internal.actualRenderer;
15053
+ if (primaryCanvas && !R3F_BUILD_WEBGPU) {
15054
+ throw new Error(
15055
+ "The `primaryCanvas` prop for multi-canvas rendering is only available with WebGPU. Use @react-three/fiber/webgpu instead."
15056
+ );
15057
+ }
15058
+ if (primaryCanvas && wantsGL) {
15059
+ throw new Error(
15060
+ "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
15061
+ );
15062
+ }
14596
15063
  if (!state.internal.actualRenderer) {
14597
15064
  renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14598
15065
  state.internal.actualRenderer = renderer;
14599
- state.set({ isLegacy: true, gl: renderer, renderer });
15066
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14600
15067
  }
14601
15068
  let raycaster = state.raycaster;
14602
15069
  if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
14603
15070
  const { params, ...options } = raycastOptions || {};
14604
15071
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14605
- if (!is.equ(params, raycaster.params, shallowLoose))
15072
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14606
15073
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
15074
+ }
15075
+ let tempCamera = state.camera;
14607
15076
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14608
15077
  lastCamera = cameraOptions;
14609
15078
  const isCamera = cameraOptions?.isCamera;
@@ -14623,6 +15092,7 @@ function createRoot(canvas) {
14623
15092
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14624
15093
  }
14625
15094
  state.set({ camera });
15095
+ tempCamera = camera;
14626
15096
  raycaster.camera = camera;
14627
15097
  }
14628
15098
  if (!state.scene) {
@@ -14640,7 +15110,7 @@ function createRoot(canvas) {
14640
15110
  rootScene: scene,
14641
15111
  internal: { ...prev.internal, container: scene }
14642
15112
  }));
14643
- const camera = state.camera;
15113
+ const camera = tempCamera;
14644
15114
  if (camera && !camera.parent) scene.add(camera);
14645
15115
  }
14646
15116
  if (events && !state.events.handlers) {
@@ -14654,9 +15124,17 @@ function createRoot(canvas) {
14654
15124
  wasEnabled = enabled;
14655
15125
  });
14656
15126
  }
15127
+ if (_sizeProps !== void 0) {
15128
+ state.set({ _sizeProps });
15129
+ }
15130
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15131
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15132
+ }
14657
15133
  const size = computeInitialSize(canvas, propsSize);
14658
- if (!is.equ(size, state.size, shallowLoose)) {
15134
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15135
+ const wasImperative = state._sizeImperative;
14659
15136
  state.setSize(size.width, size.height, size.top, size.left);
15137
+ if (!wasImperative) state.set({ _sizeImperative: false });
14660
15138
  }
14661
15139
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14662
15140
  state.setDpr(dpr);
@@ -14681,7 +15159,7 @@ function createRoot(canvas) {
14681
15159
  const handleXRFrame = (timestamp, frame) => {
14682
15160
  const state2 = store.getState();
14683
15161
  if (state2.frameloop === "never") return;
14684
- advance(timestamp, true);
15162
+ advance(timestamp);
14685
15163
  };
14686
15164
  const actualRenderer = state.internal.actualRenderer;
14687
15165
  const handleSessionChange = () => {
@@ -14693,16 +15171,16 @@ function createRoot(canvas) {
14693
15171
  };
14694
15172
  const xr = {
14695
15173
  connect() {
14696
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14697
- const actualRenderer2 = renderer2 || gl;
14698
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14699
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15174
+ const { gl, renderer: renderer2 } = store.getState();
15175
+ const xrManager = (renderer2 || gl).xr;
15176
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15177
+ xrManager.addEventListener("sessionend", handleSessionChange);
14700
15178
  },
14701
15179
  disconnect() {
14702
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14703
- const actualRenderer2 = renderer2 || gl;
14704
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14705
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15180
+ const { gl, renderer: renderer2 } = store.getState();
15181
+ const xrManager = (renderer2 || gl).xr;
15182
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15183
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14706
15184
  }
14707
15185
  };
14708
15186
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14726,35 +15204,21 @@ function createRoot(canvas) {
14726
15204
  } else if (is.obj(shadows)) {
14727
15205
  Object.assign(renderer.shadowMap, shadows);
14728
15206
  }
14729
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
15207
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14730
15208
  renderer.shadowMap.needsUpdate = true;
14731
- }
14732
- {
14733
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14734
- const linearChanged = linear !== lastConfiguredProps.linear;
14735
- const flatChanged = flat !== lastConfiguredProps.flat;
14736
- if (legacyChanged) {
14737
- three.ColorManagement.enabled = !legacy;
14738
- lastConfiguredProps.legacy = legacy;
14739
15209
  }
14740
- if (!configured || linearChanged) {
14741
- renderer.outputColorSpace = linear ? three.LinearSRGBColorSpace : three.SRGBColorSpace;
14742
- lastConfiguredProps.linear = linear;
14743
- }
14744
- if (!configured || flatChanged) {
14745
- renderer.toneMapping = flat ? three.NoToneMapping : three.ACESFilmicToneMapping;
14746
- lastConfiguredProps.flat = flat;
14747
- }
14748
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14749
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14750
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15210
+ }
15211
+ if (!configured) {
15212
+ renderer.outputColorSpace = three.SRGBColorSpace;
15213
+ renderer.toneMapping = three.ACESFilmicToneMapping;
14751
15214
  }
14752
15215
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14753
15216
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14754
15217
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14755
15218
  }
14756
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
15219
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14757
15220
  applyProps(renderer, glConfig);
15221
+ }
14758
15222
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14759
15223
  const currentRenderer = state.renderer;
14760
15224
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
@@ -14764,11 +15228,26 @@ function createRoot(canvas) {
14764
15228
  const scheduler = getScheduler();
14765
15229
  const rootId = state.internal.rootId;
14766
15230
  if (!rootId) {
14767
- const newRootId = scheduler.generateRootId();
15231
+ const newRootId = canvasId || scheduler.generateRootId();
14768
15232
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14769
15233
  getState: () => store.getState(),
14770
15234
  onError: (err) => store.getState().setError(err)
14771
15235
  });
15236
+ const unregisterCanvasTarget = scheduler.register(
15237
+ () => {
15238
+ const state2 = store.getState();
15239
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15240
+ const renderer2 = state2.internal.actualRenderer;
15241
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15242
+ }
15243
+ },
15244
+ {
15245
+ id: `${newRootId}_canvasTarget`,
15246
+ rootId: newRootId,
15247
+ phase: "start",
15248
+ system: true
15249
+ }
15250
+ );
14772
15251
  const unregisterFrustum = scheduler.register(
14773
15252
  () => {
14774
15253
  const state2 = store.getState();
@@ -14810,11 +15289,15 @@ function createRoot(canvas) {
14810
15289
  }
14811
15290
  },
14812
15291
  {
14813
- id: `${newRootId}_render`,
15292
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15293
+ id: canvasId || `${newRootId}_render`,
14814
15294
  rootId: newRootId,
14815
15295
  phase: "render",
14816
- system: true
15296
+ system: true,
14817
15297
  // Internal flag: this is a system job, not user-controlled
15298
+ // Apply scheduler config for render ordering and rate limiting
15299
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15300
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14818
15301
  }
14819
15302
  );
14820
15303
  state.set((state2) => ({
@@ -14823,6 +15306,7 @@ function createRoot(canvas) {
14823
15306
  rootId: newRootId,
14824
15307
  unregisterRoot: () => {
14825
15308
  unregisterRoot();
15309
+ unregisterCanvasTarget();
14826
15310
  unregisterFrustum();
14827
15311
  unregisterVisibility();
14828
15312
  unregisterRender();
@@ -14881,15 +15365,24 @@ function unmountComponentAtNode(canvas, callback) {
14881
15365
  const renderer = state.internal.actualRenderer;
14882
15366
  const unregisterRoot = state.internal.unregisterRoot;
14883
15367
  if (unregisterRoot) unregisterRoot();
15368
+ const unregisterPrimary = state.internal.unregisterPrimary;
15369
+ if (unregisterPrimary) unregisterPrimary();
15370
+ const canvasTarget = state.internal.canvasTarget;
15371
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14884
15372
  state.events.disconnect?.();
14885
15373
  cleanupHelperGroup(root.store);
14886
- renderer?.renderLists?.dispose?.();
14887
- renderer?.forceContextLoss?.();
14888
- if (renderer?.xr) state.xr.disconnect();
15374
+ if (state.isLegacy && renderer) {
15375
+ ;
15376
+ renderer.renderLists?.dispose?.();
15377
+ renderer.forceContextLoss?.();
15378
+ }
15379
+ if (!state.internal.isSecondary) {
15380
+ if (renderer?.xr) state.xr.disconnect();
15381
+ }
14889
15382
  dispose(state.scene);
14890
15383
  _roots.delete(canvas);
14891
15384
  if (callback) callback(canvas);
14892
- } catch (e) {
15385
+ } catch {
14893
15386
  }
14894
15387
  }, 500);
14895
15388
  }
@@ -14897,36 +15390,34 @@ function unmountComponentAtNode(canvas, callback) {
14897
15390
  }
14898
15391
  }
14899
15392
  function createPortal(children, container, state) {
14900
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15393
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14901
15394
  }
14902
- function PortalWrapper({ children, container, state }) {
15395
+ function Portal({ children, container, state }) {
14903
15396
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14904
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15397
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14905
15398
  if (isRef(container)) return container.current ?? null;
14906
15399
  return container;
14907
15400
  });
15401
+ const setResolvedContainer = React.useCallback(
15402
+ (newContainer) => {
15403
+ if (!newContainer || newContainer === resolvedContainer) return;
15404
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15405
+ },
15406
+ [resolvedContainer, _setResolvedContainer, isRef]
15407
+ );
14908
15408
  React.useMemo(() => {
14909
- if (isRef(container)) {
14910
- const current = container.current;
14911
- if (!current) {
14912
- queueMicrotask(() => {
14913
- const updated = container.current;
14914
- if (updated && updated !== resolvedContainer) {
14915
- setResolvedContainer(updated);
14916
- }
14917
- });
14918
- } else if (current !== resolvedContainer) {
14919
- setResolvedContainer(current);
14920
- }
14921
- } else if (container !== resolvedContainer) {
14922
- setResolvedContainer(container);
15409
+ if (isRef(container) && !container.current) {
15410
+ return queueMicrotask(() => {
15411
+ setResolvedContainer(container.current);
15412
+ });
14923
15413
  }
14924
- }, [container, resolvedContainer, isRef]);
15414
+ setResolvedContainer(container);
15415
+ }, [container, isRef, setResolvedContainer]);
14925
15416
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14926
15417
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14927
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15418
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14928
15419
  }
14929
- function Portal({ state = {}, children, container }) {
15420
+ function PortalInner({ state = {}, children, container }) {
14930
15421
  const { events, size, injectScene = true, ...rest } = state;
14931
15422
  const previousRoot = useStore();
14932
15423
  const [raycaster] = React.useState(() => new three.Raycaster());
@@ -14947,11 +15438,12 @@ function Portal({ state = {}, children, container }) {
14947
15438
  };
14948
15439
  }, [portalScene, container, injectScene]);
14949
15440
  const inject = useMutableCallback((rootState, injectState) => {
15441
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14950
15442
  let viewport = void 0;
14951
- if (injectState.camera && size) {
15443
+ if (injectState.camera && (size || injectState.size)) {
14952
15444
  const camera = injectState.camera;
14953
- viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(), size);
14954
- if (camera !== rootState.camera) updateCamera(camera, size);
15445
+ viewport = rootState.viewport.getCurrentViewport(camera, new three.Vector3(), resolvedSize);
15446
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14955
15447
  }
14956
15448
  return {
14957
15449
  // The intersect consists of the previous root state
@@ -14968,7 +15460,7 @@ function Portal({ state = {}, children, container }) {
14968
15460
  previousRoot,
14969
15461
  // Events, size and viewport can be overridden by the inject layer
14970
15462
  events: { ...rootState.events, ...injectState.events, ...events },
14971
- size: { ...rootState.size, ...size },
15463
+ size: resolvedSize,
14972
15464
  viewport: { ...rootState.viewport, ...viewport },
14973
15465
  // Layers are allowed to override events
14974
15466
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -15002,15 +15494,13 @@ function CanvasImpl({
15002
15494
  fallback,
15003
15495
  resize,
15004
15496
  style,
15497
+ id,
15005
15498
  gl,
15006
- renderer,
15499
+ renderer: rendererProp,
15007
15500
  events = createPointerEvents,
15008
15501
  eventSource,
15009
15502
  eventPrefix,
15010
15503
  shadows,
15011
- linear,
15012
- flat,
15013
- legacy,
15014
15504
  orthographic,
15015
15505
  frameloop,
15016
15506
  dpr,
@@ -15022,10 +15512,46 @@ function CanvasImpl({
15022
15512
  onDragOverMissed,
15023
15513
  onDropMissed,
15024
15514
  onCreated,
15515
+ hmr,
15516
+ width,
15517
+ height,
15518
+ background,
15519
+ forceEven,
15025
15520
  ...props
15026
15521
  }) {
15522
+ const { primaryCanvas, scheduler, ...rendererConfig } = typeof rendererProp === "object" && rendererProp !== null && !("render" in rendererProp) && ("primaryCanvas" in rendererProp || "scheduler" in rendererProp) ? rendererProp : { primaryCanvas: void 0, scheduler: void 0 };
15523
+ const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15027
15524
  React__namespace.useMemo(() => extend(THREE), []);
15028
15525
  const Bridge = useBridge();
15526
+ const backgroundProps = React__namespace.useMemo(() => {
15527
+ if (!background) return null;
15528
+ if (typeof background === "object" && !background.isColor) {
15529
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15530
+ return {
15531
+ ...rest,
15532
+ preset,
15533
+ files: envMap || files,
15534
+ backgroundFiles: backgroundMap,
15535
+ background: true
15536
+ };
15537
+ }
15538
+ if (typeof background === "number") {
15539
+ return { color: background, background: true };
15540
+ }
15541
+ if (typeof background === "string") {
15542
+ if (background in presetsObj) {
15543
+ return { preset: background, background: true };
15544
+ }
15545
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15546
+ return { files: background, background: true };
15547
+ }
15548
+ return { color: background, background: true };
15549
+ }
15550
+ if (background.isColor) {
15551
+ return { color: background, background: true };
15552
+ }
15553
+ return null;
15554
+ }, [background]);
15029
15555
  const hasInitialSizeRef = React__namespace.useRef(false);
15030
15556
  const measureConfig = React__namespace.useMemo(() => {
15031
15557
  if (!hasInitialSizeRef.current) {
@@ -15042,7 +15568,21 @@ function CanvasImpl({
15042
15568
  };
15043
15569
  }, [resize, hasInitialSizeRef.current]);
15044
15570
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15045
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
15571
+ const effectiveSize = React__namespace.useMemo(() => {
15572
+ let w = width ?? containerRect.width;
15573
+ let h = height ?? containerRect.height;
15574
+ if (forceEven) {
15575
+ w = Math.ceil(w / 2) * 2;
15576
+ h = Math.ceil(h / 2) * 2;
15577
+ }
15578
+ return {
15579
+ width: w,
15580
+ height: h,
15581
+ top: containerRect.top,
15582
+ left: containerRect.left
15583
+ };
15584
+ }, [width, height, containerRect, forceEven]);
15585
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15046
15586
  hasInitialSizeRef.current = true;
15047
15587
  }
15048
15588
  const canvasRef = React__namespace.useRef(null);
@@ -15061,7 +15601,7 @@ function CanvasImpl({
15061
15601
  useIsomorphicLayoutEffect(() => {
15062
15602
  effectActiveRef.current = true;
15063
15603
  const canvas = canvasRef.current;
15064
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
15604
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15065
15605
  if (!root.current) {
15066
15606
  root.current = createRoot(canvas);
15067
15607
  notifyAlpha({
@@ -15081,21 +15621,24 @@ function CanvasImpl({
15081
15621
  async function run() {
15082
15622
  if (!effectActiveRef.current || !root.current) return;
15083
15623
  await root.current.configure({
15624
+ id,
15625
+ primaryCanvas,
15626
+ scheduler,
15084
15627
  gl,
15085
15628
  renderer,
15086
15629
  scene,
15087
15630
  events,
15088
15631
  shadows,
15089
- linear,
15090
- flat,
15091
- legacy,
15092
15632
  orthographic,
15093
15633
  frameloop,
15094
15634
  dpr,
15095
15635
  performance,
15096
15636
  raycaster,
15097
15637
  camera,
15098
- size: containerRect,
15638
+ size: effectiveSize,
15639
+ // Store size props for reset functionality
15640
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15641
+ forceEven,
15099
15642
  // Pass mutable reference to onPointerMissed so it's free to update
15100
15643
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15101
15644
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15119,7 +15662,10 @@ function CanvasImpl({
15119
15662
  });
15120
15663
  if (!effectActiveRef.current || !root.current) return;
15121
15664
  root.current.render(
15122
- /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: children ?? null }) }) })
15665
+ /* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: [
15666
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15667
+ children ?? null
15668
+ ] }) }) })
15123
15669
  );
15124
15670
  }
15125
15671
  run();
@@ -15141,6 +15687,35 @@ function CanvasImpl({
15141
15687
  };
15142
15688
  }
15143
15689
  }, []);
15690
+ React__namespace.useEffect(() => {
15691
+ if (hmr === false) return;
15692
+ const canvas = canvasRef.current;
15693
+ if (!canvas) return;
15694
+ const handleHMR = () => {
15695
+ queueMicrotask(() => {
15696
+ const rootEntry = _roots.get(canvas);
15697
+ if (rootEntry?.store) {
15698
+ rootEntry.store.setState((state) => ({
15699
+ nodes: {},
15700
+ uniforms: {},
15701
+ _hmrVersion: state._hmrVersion + 1
15702
+ }));
15703
+ }
15704
+ });
15705
+ };
15706
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('legacy.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15707
+ const hot = undefined;
15708
+ hot.on("vite:afterUpdate", handleHMR);
15709
+ return () => hot.dispose?.(() => {
15710
+ });
15711
+ }
15712
+ if (typeof module !== "undefined" && module.hot) {
15713
+ const hot = module.hot;
15714
+ hot.addStatusHandler((status) => {
15715
+ if (status === "idle") handleHMR();
15716
+ });
15717
+ }
15718
+ }, [hmr]);
15144
15719
  const pointerEvents = eventSource ? "none" : "auto";
15145
15720
  return /* @__PURE__ */ jsxRuntime.jsx(
15146
15721
  "div",
@@ -15155,7 +15730,7 @@ function CanvasImpl({
15155
15730
  ...style
15156
15731
  },
15157
15732
  ...props,
15158
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15733
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "r3f-canvas-container", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, id, className: "r3f-canvas", style: { display: "block" }, children: fallback }) })
15159
15734
  }
15160
15735
  );
15161
15736
  }
@@ -15167,8 +15742,15 @@ extend(THREE);
15167
15742
 
15168
15743
  exports.Block = Block;
15169
15744
  exports.Canvas = Canvas;
15745
+ exports.Environment = Environment;
15746
+ exports.EnvironmentCube = EnvironmentCube;
15747
+ exports.EnvironmentMap = EnvironmentMap;
15748
+ exports.EnvironmentPortal = EnvironmentPortal;
15170
15749
  exports.ErrorBoundary = ErrorBoundary;
15750
+ exports.FROM_REF = FROM_REF;
15171
15751
  exports.IsObject = IsObject;
15752
+ exports.ONCE = ONCE;
15753
+ exports.Portal = Portal;
15172
15754
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15173
15755
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15174
15756
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15198,30 +15780,41 @@ exports.events = createPointerEvents;
15198
15780
  exports.extend = extend;
15199
15781
  exports.findInitialRoot = findInitialRoot;
15200
15782
  exports.flushSync = flushSync;
15783
+ exports.fromRef = fromRef;
15201
15784
  exports.getInstanceProps = getInstanceProps;
15785
+ exports.getPrimary = getPrimary;
15786
+ exports.getPrimaryIds = getPrimaryIds;
15202
15787
  exports.getRootState = getRootState;
15203
15788
  exports.getScheduler = getScheduler;
15204
15789
  exports.getUuidPrefix = getUuidPrefix;
15205
15790
  exports.hasConstructor = hasConstructor;
15791
+ exports.hasPrimary = hasPrimary;
15206
15792
  exports.invalidate = invalidate;
15207
15793
  exports.invalidateInstance = invalidateInstance;
15208
15794
  exports.is = is;
15209
15795
  exports.isColorRepresentation = isColorRepresentation;
15210
15796
  exports.isCopyable = isCopyable;
15797
+ exports.isFromRef = isFromRef;
15211
15798
  exports.isObject3D = isObject3D;
15799
+ exports.isOnce = isOnce;
15212
15800
  exports.isOrthographicCamera = isOrthographicCamera;
15213
15801
  exports.isRef = isRef;
15214
15802
  exports.isRenderer = isRenderer;
15215
15803
  exports.isTexture = isTexture;
15216
15804
  exports.isVectorLike = isVectorLike;
15805
+ exports.once = once;
15217
15806
  exports.prepare = prepare;
15807
+ exports.presetsObj = presetsObj;
15218
15808
  exports.reconciler = reconciler;
15809
+ exports.registerPrimary = registerPrimary;
15219
15810
  exports.removeInteractivity = removeInteractivity;
15220
15811
  exports.resolve = resolve;
15221
15812
  exports.unmountComponentAtNode = unmountComponentAtNode;
15813
+ exports.unregisterPrimary = unregisterPrimary;
15222
15814
  exports.updateCamera = updateCamera;
15223
15815
  exports.updateFrustum = updateFrustum;
15224
15816
  exports.useBridge = useBridge;
15817
+ exports.useEnvironment = useEnvironment;
15225
15818
  exports.useFrame = useFrame;
15226
15819
  exports.useGraph = useGraph;
15227
15820
  exports.useInstanceHandle = useInstanceHandle;
@@ -15233,3 +15826,4 @@ exports.useStore = useStore;
15233
15826
  exports.useTexture = useTexture;
15234
15827
  exports.useTextures = useTextures;
15235
15828
  exports.useThree = useThree;
15829
+ exports.waitForPrimary = waitForPrimary;