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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7,10 +7,17 @@ const jsxRuntime = require('react/jsx-runtime');
7
7
  const React = require('react');
8
8
  const useMeasure = require('react-use-measure');
9
9
  const itsFine = require('its-fine');
10
+ const fiber = require('@react-three/fiber');
11
+ const GroundedSkybox_js = require('three/examples/jsm/objects/GroundedSkybox.js');
12
+ const HDRLoader_js = require('three/examples/jsm/loaders/HDRLoader.js');
13
+ const EXRLoader_js = require('three/examples/jsm/loaders/EXRLoader.js');
14
+ const UltraHDRLoader_js = require('three/examples/jsm/loaders/UltraHDRLoader.js');
15
+ const gainmapJs = require('@monogrid/gainmap-js');
10
16
  const Tb = require('scheduler');
11
17
  const traditional = require('zustand/traditional');
12
18
  const suspendReact = require('suspend-react');
13
19
 
20
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
21
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
15
22
 
16
23
  function _interopNamespaceCompat(e) {
@@ -56,6 +63,374 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
56
63
  WebGLRenderer: three.WebGLRenderer
57
64
  }, [webgpu__namespace]);
58
65
 
66
+ const primaryRegistry = /* @__PURE__ */ new Map();
67
+ const pendingSubscribers = /* @__PURE__ */ new Map();
68
+ function registerPrimary(id, renderer, store) {
69
+ if (primaryRegistry.has(id)) {
70
+ console.warn(`Canvas with id="${id}" already registered. Overwriting.`);
71
+ }
72
+ const entry = { renderer, store };
73
+ primaryRegistry.set(id, entry);
74
+ const subscribers = pendingSubscribers.get(id);
75
+ if (subscribers) {
76
+ subscribers.forEach((callback) => callback(entry));
77
+ pendingSubscribers.delete(id);
78
+ }
79
+ return () => {
80
+ const currentEntry = primaryRegistry.get(id);
81
+ if (currentEntry?.renderer === renderer) {
82
+ primaryRegistry.delete(id);
83
+ }
84
+ };
85
+ }
86
+ function getPrimary(id) {
87
+ return primaryRegistry.get(id);
88
+ }
89
+ function waitForPrimary(id, timeout = 5e3) {
90
+ const existing = primaryRegistry.get(id);
91
+ if (existing) {
92
+ return Promise.resolve(existing);
93
+ }
94
+ return new Promise((resolve, reject) => {
95
+ const timeoutId = setTimeout(() => {
96
+ const subscribers = pendingSubscribers.get(id);
97
+ if (subscribers) {
98
+ const index = subscribers.indexOf(callback);
99
+ if (index !== -1) subscribers.splice(index, 1);
100
+ if (subscribers.length === 0) pendingSubscribers.delete(id);
101
+ }
102
+ reject(new Error(`Timeout waiting for canvas with id="${id}". Make sure a <Canvas id="${id}"> is mounted.`));
103
+ }, timeout);
104
+ const callback = (entry) => {
105
+ clearTimeout(timeoutId);
106
+ resolve(entry);
107
+ };
108
+ if (!pendingSubscribers.has(id)) {
109
+ pendingSubscribers.set(id, []);
110
+ }
111
+ pendingSubscribers.get(id).push(callback);
112
+ });
113
+ }
114
+ function hasPrimary(id) {
115
+ return primaryRegistry.has(id);
116
+ }
117
+ function unregisterPrimary(id) {
118
+ primaryRegistry.delete(id);
119
+ }
120
+ function getPrimaryIds() {
121
+ return Array.from(primaryRegistry.keys());
122
+ }
123
+
124
+ const presetsObj = {
125
+ apartment: "lebombo_1k.hdr",
126
+ city: "potsdamer_platz_1k.hdr",
127
+ dawn: "kiara_1_dawn_1k.hdr",
128
+ forest: "forest_slope_1k.hdr",
129
+ lobby: "st_fagans_interior_1k.hdr",
130
+ night: "dikhololo_night_1k.hdr",
131
+ park: "rooitou_park_1k.hdr",
132
+ studio: "studio_small_03_1k.hdr",
133
+ sunset: "venice_sunset_1k.hdr",
134
+ warehouse: "empty_warehouse_01_1k.hdr"
135
+ };
136
+
137
+ const CUBEMAP_ROOT = "https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/";
138
+ const isArray = (arr) => Array.isArray(arr);
139
+ const defaultFiles = ["/px.png", "/nx.png", "/py.png", "/ny.png", "/pz.png", "/nz.png"];
140
+ function useEnvironment({
141
+ files = defaultFiles,
142
+ path = "",
143
+ preset = void 0,
144
+ colorSpace = void 0,
145
+ extensions
146
+ } = {}) {
147
+ if (preset) {
148
+ validatePreset(preset);
149
+ files = presetsObj[preset];
150
+ path = CUBEMAP_ROOT;
151
+ }
152
+ const multiFile = isArray(files);
153
+ const { extension, isCubemap } = getExtension(files);
154
+ const loader = getLoader$1(extension);
155
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
156
+ const renderer = fiber.useThree((state) => state.renderer);
157
+ React.useLayoutEffect(() => {
158
+ if (extension !== "webp" && extension !== "jpg" && extension !== "jpeg") return;
159
+ function clearGainmapTexture() {
160
+ fiber.useLoader.clear(loader, multiFile ? [files] : files);
161
+ }
162
+ renderer.domElement.addEventListener("webglcontextlost", clearGainmapTexture, { once: true });
163
+ }, [files, renderer.domElement]);
164
+ const loaderResult = fiber.useLoader(
165
+ loader,
166
+ multiFile ? [files] : files,
167
+ (loader2) => {
168
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
169
+ loader2.setRenderer?.(renderer);
170
+ }
171
+ loader2.setPath?.(path);
172
+ if (extensions) extensions(loader2);
173
+ }
174
+ );
175
+ let texture = multiFile ? (
176
+ // @ts-ignore
177
+ loaderResult[0]
178
+ ) : loaderResult;
179
+ if (extension === "jpg" || extension === "jpeg" || extension === "webp") {
180
+ texture = texture.renderTarget?.texture;
181
+ }
182
+ texture.mapping = isCubemap ? webgpu.CubeReflectionMapping : webgpu.EquirectangularReflectionMapping;
183
+ texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
184
+ return texture;
185
+ }
186
+ const preloadDefaultOptions = {
187
+ files: defaultFiles,
188
+ path: "",
189
+ preset: void 0,
190
+ extensions: void 0
191
+ };
192
+ useEnvironment.preload = (preloadOptions) => {
193
+ const options = { ...preloadDefaultOptions, ...preloadOptions };
194
+ let { files, path = "" } = options;
195
+ const { preset, extensions } = options;
196
+ if (preset) {
197
+ validatePreset(preset);
198
+ files = presetsObj[preset];
199
+ path = CUBEMAP_ROOT;
200
+ }
201
+ const { extension } = getExtension(files);
202
+ if (extension === "webp" || extension === "jpg" || extension === "jpeg") {
203
+ throw new Error("useEnvironment: Preloading gainmaps is not supported");
204
+ }
205
+ const loader = getLoader$1(extension);
206
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
207
+ fiber.useLoader.preload(loader, isArray(files) ? [files] : files, (loader2) => {
208
+ loader2.setPath?.(path);
209
+ if (extensions) extensions(loader2);
210
+ });
211
+ };
212
+ const clearDefaultOptins = {
213
+ files: defaultFiles,
214
+ preset: void 0
215
+ };
216
+ useEnvironment.clear = (clearOptions) => {
217
+ const options = { ...clearDefaultOptins, ...clearOptions };
218
+ let { files } = options;
219
+ const { preset } = options;
220
+ if (preset) {
221
+ validatePreset(preset);
222
+ files = presetsObj[preset];
223
+ }
224
+ const { extension } = getExtension(files);
225
+ const loader = getLoader$1(extension);
226
+ if (!loader) throw new Error("useEnvironment: Unrecognized file extension: " + files);
227
+ fiber.useLoader.clear(loader, isArray(files) ? [files] : files);
228
+ };
229
+ function validatePreset(preset) {
230
+ if (!(preset in presetsObj)) throw new Error("Preset must be one of: " + Object.keys(presetsObj).join(", "));
231
+ }
232
+ function getExtension(files) {
233
+ const isCubemap = isArray(files) && files.length === 6;
234
+ const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
235
+ const firstEntry = isArray(files) ? files[0] : files;
236
+ 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();
237
+ return { extension, isCubemap, isGainmap };
238
+ }
239
+ function getLoader$1(extension) {
240
+ const loader = extension === "cube" ? webgpu.CubeTextureLoader : extension === "hdr" ? HDRLoader_js.HDRLoader : extension === "exr" ? EXRLoader_js.EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader_js.UltraHDRLoader : extension === "webp" ? gainmapJs.GainMapLoader : null;
241
+ return loader;
242
+ }
243
+
244
+ const isRef$1 = (obj) => obj.current && obj.current.isScene;
245
+ const resolveScene = (scene) => isRef$1(scene) ? scene.current : scene;
246
+ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {}) {
247
+ sceneProps = {
248
+ backgroundBlurriness: 0,
249
+ backgroundIntensity: 1,
250
+ backgroundRotation: [0, 0, 0],
251
+ environmentIntensity: 1,
252
+ environmentRotation: [0, 0, 0],
253
+ ...sceneProps
254
+ };
255
+ const target = resolveScene(scene || defaultScene);
256
+ const oldbg = target.background;
257
+ const oldenv = target.environment;
258
+ const oldSceneProps = {
259
+ // @ts-ignore
260
+ backgroundBlurriness: target.backgroundBlurriness,
261
+ // @ts-ignore
262
+ backgroundIntensity: target.backgroundIntensity,
263
+ // @ts-ignore
264
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
265
+ // @ts-ignore
266
+ environmentIntensity: target.environmentIntensity,
267
+ // @ts-ignore
268
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
269
+ };
270
+ if (background !== "only") target.environment = texture;
271
+ if (background) target.background = texture;
272
+ fiber.applyProps(target, sceneProps);
273
+ return () => {
274
+ if (background !== "only") target.environment = oldenv;
275
+ if (background) target.background = oldbg;
276
+ fiber.applyProps(target, oldSceneProps);
277
+ };
278
+ }
279
+ function EnvironmentMap({ scene, background = false, map, ...config }) {
280
+ const defaultScene = fiber.useThree((state) => state.scene);
281
+ React__namespace.useLayoutEffect(() => {
282
+ if (map) return setEnvProps(background, scene, defaultScene, map, config);
283
+ });
284
+ return null;
285
+ }
286
+ function EnvironmentCube({
287
+ background = false,
288
+ scene,
289
+ blur,
290
+ backgroundBlurriness,
291
+ backgroundIntensity,
292
+ backgroundRotation,
293
+ environmentIntensity,
294
+ environmentRotation,
295
+ ...rest
296
+ }) {
297
+ const texture = useEnvironment(rest);
298
+ const defaultScene = fiber.useThree((state) => state.scene);
299
+ React__namespace.useLayoutEffect(() => {
300
+ return setEnvProps(background, scene, defaultScene, texture, {
301
+ backgroundBlurriness: blur ?? backgroundBlurriness,
302
+ backgroundIntensity,
303
+ backgroundRotation,
304
+ environmentIntensity,
305
+ environmentRotation
306
+ });
307
+ });
308
+ React__namespace.useEffect(() => {
309
+ return () => {
310
+ texture.dispose();
311
+ };
312
+ }, [texture]);
313
+ return null;
314
+ }
315
+ function EnvironmentPortal({
316
+ children,
317
+ near = 0.1,
318
+ far = 1e3,
319
+ resolution = 256,
320
+ frames = 1,
321
+ map,
322
+ background = false,
323
+ blur,
324
+ backgroundBlurriness,
325
+ backgroundIntensity,
326
+ backgroundRotation,
327
+ environmentIntensity,
328
+ environmentRotation,
329
+ scene,
330
+ files,
331
+ path,
332
+ preset = void 0,
333
+ extensions
334
+ }) {
335
+ const gl = fiber.useThree((state) => state.gl);
336
+ const defaultScene = fiber.useThree((state) => state.scene);
337
+ const camera = React__namespace.useRef(null);
338
+ const [virtualScene] = React__namespace.useState(() => new webgpu.Scene());
339
+ const fbo = React__namespace.useMemo(() => {
340
+ const fbo2 = new webgpu.WebGLCubeRenderTarget(resolution);
341
+ fbo2.texture.type = webgpu.HalfFloatType;
342
+ return fbo2;
343
+ }, [resolution]);
344
+ React__namespace.useEffect(() => {
345
+ return () => {
346
+ fbo.dispose();
347
+ };
348
+ }, [fbo]);
349
+ React__namespace.useLayoutEffect(() => {
350
+ if (frames === 1) {
351
+ const autoClear = gl.autoClear;
352
+ gl.autoClear = true;
353
+ camera.current.update(gl, virtualScene);
354
+ gl.autoClear = autoClear;
355
+ }
356
+ return setEnvProps(background, scene, defaultScene, fbo.texture, {
357
+ backgroundBlurriness: blur ?? backgroundBlurriness,
358
+ backgroundIntensity,
359
+ backgroundRotation,
360
+ environmentIntensity,
361
+ environmentRotation
362
+ });
363
+ }, [children, virtualScene, fbo.texture, scene, defaultScene, background, frames, gl]);
364
+ let count = 1;
365
+ fiber.useFrame(() => {
366
+ if (frames === Infinity || count < frames) {
367
+ const autoClear = gl.autoClear;
368
+ gl.autoClear = true;
369
+ camera.current.update(gl, virtualScene);
370
+ gl.autoClear = autoClear;
371
+ count++;
372
+ }
373
+ });
374
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(
375
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
376
+ children,
377
+ /* @__PURE__ */ jsxRuntime.jsx("cubeCamera", { ref: camera, args: [near, far, fbo] }),
378
+ files || preset ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { background: true, files, preset, path, extensions }) : map ? /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { background: true, map, extensions }) : null
379
+ ] }),
380
+ virtualScene
381
+ ) });
382
+ }
383
+ function EnvironmentGround(props) {
384
+ const textureDefault = useEnvironment(props);
385
+ const texture = props.map || textureDefault;
386
+ React__namespace.useMemo(() => fiber.extend({ GroundProjectedEnvImpl: GroundedSkybox_js.GroundedSkybox }), []);
387
+ React__namespace.useEffect(() => {
388
+ return () => {
389
+ textureDefault.dispose();
390
+ };
391
+ }, [textureDefault]);
392
+ const height = props.ground?.height ?? 15;
393
+ const radius = props.ground?.radius ?? 60;
394
+ const scale = props.ground?.scale ?? 1e3;
395
+ const args = React__namespace.useMemo(
396
+ () => [texture, height, radius],
397
+ [texture, height, radius]
398
+ );
399
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
400
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentMap, { ...props, map: texture }),
401
+ /* @__PURE__ */ jsxRuntime.jsx("groundProjectedEnvImpl", { args, scale })
402
+ ] });
403
+ }
404
+ function EnvironmentColor({ color, scene }) {
405
+ const defaultScene = fiber.useThree((state) => state.scene);
406
+ React__namespace.useLayoutEffect(() => {
407
+ if (color === void 0) return;
408
+ const target = resolveScene(scene || defaultScene);
409
+ const oldBg = target.background;
410
+ target.background = new webgpu.Color(color);
411
+ return () => {
412
+ target.background = oldBg;
413
+ };
414
+ });
415
+ return null;
416
+ }
417
+ function EnvironmentDualSource(props) {
418
+ const { backgroundFiles, ...envProps } = props;
419
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
420
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...envProps, background: false }),
421
+ /* @__PURE__ */ jsxRuntime.jsx(EnvironmentCube, { ...props, files: backgroundFiles, background: "only" })
422
+ ] });
423
+ }
424
+ function Environment(props) {
425
+ if (props.color && !props.files && !props.preset && !props.map) {
426
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentColor, { ...props });
427
+ }
428
+ if (props.backgroundFiles && props.backgroundFiles !== props.files) {
429
+ return /* @__PURE__ */ jsxRuntime.jsx(EnvironmentDualSource, { ...props });
430
+ }
431
+ 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 });
432
+ }
433
+
59
434
  var __defProp$2 = Object.defineProperty;
60
435
  var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
61
436
  var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -235,7 +610,8 @@ function prepare(target, root, type, props) {
235
610
  object,
236
611
  eventCount: 0,
237
612
  handlers: {},
238
- isHidden: false
613
+ isHidden: false,
614
+ deferredRefs: []
239
615
  };
240
616
  if (object) object.__r3f = instance;
241
617
  }
@@ -284,7 +660,7 @@ function createOcclusionObserverNode(store, uniform) {
284
660
  let occlusionSetupPromise = null;
285
661
  function enableOcclusion(store) {
286
662
  const state = store.getState();
287
- const { internal, renderer, rootScene } = state;
663
+ const { internal, renderer } = state;
288
664
  if (internal.occlusionEnabled || occlusionSetupPromise) return;
289
665
  const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
290
666
  if (!hasOcclusionSupport) {
@@ -447,6 +823,22 @@ function hasVisibilityHandlers(handlers) {
447
823
  return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
448
824
  }
449
825
 
826
+ const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
827
+ function fromRef(ref) {
828
+ return { [FROM_REF]: ref };
829
+ }
830
+ function isFromRef(value) {
831
+ return value !== null && typeof value === "object" && FROM_REF in value;
832
+ }
833
+
834
+ const ONCE = Symbol.for("@react-three/fiber.once");
835
+ function once(...args) {
836
+ return { [ONCE]: args.length ? args : true };
837
+ }
838
+ function isOnce(value) {
839
+ return value !== null && typeof value === "object" && ONCE in value;
840
+ }
841
+
450
842
  const RESERVED_PROPS = [
451
843
  "children",
452
844
  "key",
@@ -517,7 +909,7 @@ function getMemoizedPrototype(root) {
517
909
  ctor = new root.constructor();
518
910
  MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
519
911
  }
520
- } catch (e) {
912
+ } catch {
521
913
  }
522
914
  return ctor;
523
915
  }
@@ -548,7 +940,7 @@ function applyProps(object, props) {
548
940
  const rootState = instance && findInitialRoot(instance).getState();
549
941
  const prevHandlers = instance?.eventCount;
550
942
  for (const prop in props) {
551
- let value = props[prop];
943
+ const value = props[prop];
552
944
  if (RESERVED_PROPS.includes(prop)) continue;
553
945
  if (instance && EVENT_REGEX.test(prop)) {
554
946
  if (typeof value === "function") instance.handlers[prop] = value;
@@ -563,6 +955,25 @@ function applyProps(object, props) {
563
955
  continue;
564
956
  }
565
957
  if (value === void 0) continue;
958
+ if (isFromRef(value)) {
959
+ instance?.deferredRefs?.push({ prop, ref: value[FROM_REF] });
960
+ continue;
961
+ }
962
+ if (isOnce(value)) {
963
+ if (instance?.appliedOnce?.has(prop)) continue;
964
+ if (instance) {
965
+ instance.appliedOnce ?? (instance.appliedOnce = /* @__PURE__ */ new Set());
966
+ instance.appliedOnce.add(prop);
967
+ }
968
+ const { root: targetRoot, key: targetKey } = resolve(object, prop);
969
+ const args = value[ONCE];
970
+ if (typeof targetRoot[targetKey] === "function") {
971
+ targetRoot[targetKey](...args === true ? [] : args);
972
+ } else if (args !== true && args.length > 0) {
973
+ targetRoot[targetKey] = args[0];
974
+ }
975
+ continue;
976
+ }
566
977
  let { root, key, target } = resolve(object, prop);
567
978
  if (target === void 0 && (typeof root !== "object" || root === null)) {
568
979
  throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
@@ -585,7 +996,7 @@ function applyProps(object, props) {
585
996
  else target.set(value);
586
997
  } else {
587
998
  root[key] = value;
588
- 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
999
+ if (rootState && rootState.renderer?.outputColorSpace === webgpu.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
589
1000
  root[key].format === webgpu.RGBAFormat && root[key].type === webgpu.UnsignedByteType) {
590
1001
  root[key].colorSpace = rootState.textureColorSpace;
591
1002
  }
@@ -700,13 +1111,14 @@ function createEvents(store) {
700
1111
  for (const hit of hits) {
701
1112
  let eventObject = hit.object;
702
1113
  while (eventObject) {
703
- if (eventObject.__r3f?.eventCount)
1114
+ if (eventObject.__r3f?.eventCount) {
704
1115
  intersections.push({ ...hit, eventObject });
1116
+ }
705
1117
  eventObject = eventObject.parent;
706
1118
  }
707
1119
  }
708
1120
  if ("pointerId" in event && state.internal.capturedMap.has(event.pointerId)) {
709
- for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1121
+ for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
710
1122
  if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
711
1123
  }
712
1124
  }
@@ -736,12 +1148,12 @@ function createEvents(store) {
736
1148
  releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
737
1149
  }
738
1150
  };
739
- let extractEventProps = {};
740
- for (let prop in event) {
741
- let property = event[prop];
1151
+ const extractEventProps = {};
1152
+ for (const prop in event) {
1153
+ const property = event[prop];
742
1154
  if (typeof property !== "function") extractEventProps[prop] = property;
743
1155
  }
744
- let raycastEvent = {
1156
+ const raycastEvent = {
745
1157
  ...hit,
746
1158
  ...extractEventProps,
747
1159
  pointer,
@@ -941,7 +1353,7 @@ function createPointerEvents(store) {
941
1353
  return {
942
1354
  priority: 1,
943
1355
  enabled: true,
944
- compute(event, state, previous) {
1356
+ compute(event, state) {
945
1357
  state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
946
1358
  state.raycaster.setFromCamera(state.pointer, state.camera);
947
1359
  },
@@ -1039,300 +1451,26 @@ function notifyAlpha({ message, link }) {
1039
1451
  }
1040
1452
  }
1041
1453
 
1042
- const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
1043
- const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
1044
- const createStore = (invalidate, advance) => {
1045
- const rootStore = traditional.createWithEqualityFn((set, get) => {
1046
- const position = new webgpu.Vector3();
1047
- const defaultTarget = new webgpu.Vector3();
1048
- const tempTarget = new webgpu.Vector3();
1049
- function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1050
- const { width, height, top, left } = size;
1051
- const aspect = width / height;
1052
- if (target.isVector3) tempTarget.copy(target);
1053
- else tempTarget.set(...target);
1054
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1055
- if (isOrthographicCamera(camera)) {
1056
- return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
1057
- } else {
1058
- const fov = camera.fov * Math.PI / 180;
1059
- const h = 2 * Math.tan(fov / 2) * distance;
1060
- const w = h * (width / height);
1061
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1062
- }
1063
- }
1064
- let performanceTimeout = void 0;
1065
- const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
1066
- const pointer = new webgpu.Vector2();
1067
- const rootState = {
1068
- set,
1069
- get,
1070
- // Mock objects that have to be configured
1071
- gl: null,
1072
- renderer: null,
1073
- camera: null,
1074
- frustum: new webgpu.Frustum(),
1075
- autoUpdateFrustum: true,
1076
- raycaster: null,
1077
- events: { priority: 1, enabled: true, connected: false },
1078
- scene: null,
1079
- rootScene: null,
1080
- xr: null,
1081
- inspector: null,
1082
- invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
1083
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1084
- legacy: false,
1085
- linear: false,
1086
- flat: false,
1087
- textureColorSpace: "srgb",
1088
- isLegacy: false,
1089
- webGPUSupported: false,
1090
- isNative: false,
1091
- controls: null,
1092
- pointer,
1093
- mouse: pointer,
1094
- frameloop: "always",
1095
- onPointerMissed: void 0,
1096
- onDragOverMissed: void 0,
1097
- onDropMissed: void 0,
1098
- performance: {
1099
- current: 1,
1100
- min: 0.5,
1101
- max: 1,
1102
- debounce: 200,
1103
- regress: () => {
1104
- const state2 = get();
1105
- if (performanceTimeout) clearTimeout(performanceTimeout);
1106
- if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
1107
- performanceTimeout = setTimeout(
1108
- () => setPerformanceCurrent(get().performance.max),
1109
- state2.performance.debounce
1110
- );
1111
- }
1112
- },
1113
- size: { width: 0, height: 0, top: 0, left: 0 },
1114
- viewport: {
1115
- initialDpr: 0,
1116
- dpr: 0,
1117
- width: 0,
1118
- height: 0,
1119
- top: 0,
1120
- left: 0,
1121
- aspect: 0,
1122
- distance: 0,
1123
- factor: 0,
1124
- getCurrentViewport
1125
- },
1126
- setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
1127
- setSize: (width, height, top = 0, left = 0) => {
1128
- const camera = get().camera;
1129
- const size = { width, height, top, left };
1130
- set((state2) => ({ size, viewport: { ...state2.viewport, ...getCurrentViewport(camera, defaultTarget, size) } }));
1131
- },
1132
- setDpr: (dpr) => set((state2) => {
1133
- const resolved = calculateDpr(dpr);
1134
- return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
1135
- }),
1136
- setFrameloop: (frameloop = "always") => {
1137
- set(() => ({ frameloop }));
1138
- },
1139
- setError: (error) => set(() => ({ error })),
1140
- error: null,
1141
- //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
1142
- uniforms: {},
1143
- nodes: {},
1144
- textures: /* @__PURE__ */ new Map(),
1145
- postProcessing: null,
1146
- passes: {},
1147
- previousRoot: void 0,
1148
- internal: {
1149
- // Events
1150
- interaction: [],
1151
- hovered: /* @__PURE__ */ new Map(),
1152
- subscribers: [],
1153
- initialClick: [0, 0],
1154
- initialHits: [],
1155
- capturedMap: /* @__PURE__ */ new Map(),
1156
- lastEvent: React__namespace.createRef(),
1157
- // Visibility tracking (onFramed, onOccluded, onVisible)
1158
- visibilityRegistry: /* @__PURE__ */ new Map(),
1159
- // Occlusion system (WebGPU only)
1160
- occlusionEnabled: false,
1161
- occlusionObserver: null,
1162
- occlusionCache: /* @__PURE__ */ new Map(),
1163
- helperGroup: null,
1164
- // Updates
1165
- active: false,
1166
- frames: 0,
1167
- priority: 0,
1168
- subscribe: (ref, priority, store) => {
1169
- const internal = get().internal;
1170
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1171
- internal.subscribers.push({ ref, priority, store });
1172
- internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1173
- return () => {
1174
- const internal2 = get().internal;
1175
- if (internal2?.subscribers) {
1176
- internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
1177
- internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
1178
- }
1179
- };
1180
- },
1181
- // Renderer Storage (single source of truth)
1182
- actualRenderer: null,
1183
- // Scheduler for useFrameNext (initialized in renderer.tsx)
1184
- scheduler: null
1185
- }
1186
- };
1187
- return rootState;
1188
- });
1189
- const state = rootStore.getState();
1190
- Object.defineProperty(state, "gl", {
1191
- get() {
1192
- const currentState = rootStore.getState();
1193
- if (!currentState.isLegacy && currentState.internal.actualRenderer) {
1194
- const stack = new Error().stack || "";
1195
- const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
1196
- if (!isInternalAccess) {
1197
- const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
1198
- notifyDepreciated({
1199
- heading: "Accessing state.gl in WebGPU mode",
1200
- 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
1201
- });
1202
- }
1203
- }
1204
- return currentState.internal.actualRenderer;
1205
- },
1206
- set(value) {
1207
- rootStore.getState().internal.actualRenderer = value;
1208
- },
1209
- enumerable: true,
1210
- configurable: true
1211
- });
1212
- Object.defineProperty(state, "renderer", {
1213
- get() {
1214
- return rootStore.getState().internal.actualRenderer;
1215
- },
1216
- set(value) {
1217
- rootStore.getState().internal.actualRenderer = value;
1218
- },
1219
- enumerable: true,
1220
- configurable: true
1221
- });
1222
- let oldScene = state.scene;
1223
- rootStore.subscribe(() => {
1224
- const currentState = rootStore.getState();
1225
- const { scene, rootScene, set } = currentState;
1226
- if (scene !== oldScene) {
1227
- oldScene = scene;
1228
- if (scene?.isScene && scene !== rootScene) {
1229
- set({ rootScene: scene });
1230
- }
1231
- }
1232
- });
1233
- let oldSize = state.size;
1234
- let oldDpr = state.viewport.dpr;
1235
- let oldCamera = state.camera;
1236
- rootStore.subscribe(() => {
1237
- const { camera, size, viewport, set, internal } = rootStore.getState();
1238
- const actualRenderer = internal.actualRenderer;
1239
- if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1240
- oldSize = size;
1241
- oldDpr = viewport.dpr;
1242
- updateCamera(camera, size);
1243
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1244
- const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
1245
- actualRenderer.setSize(size.width, size.height, updateStyle);
1246
- }
1247
- if (camera !== oldCamera) {
1248
- oldCamera = camera;
1249
- const { rootScene } = rootStore.getState();
1250
- if (camera && rootScene && !camera.parent) {
1251
- rootScene.add(camera);
1252
- }
1253
- set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
1254
- const currentState = rootStore.getState();
1255
- if (currentState.autoUpdateFrustum && camera) {
1256
- updateFrustum(camera, currentState.frustum);
1257
- }
1258
- }
1259
- });
1260
- rootStore.subscribe((state2) => invalidate(state2));
1261
- return rootStore;
1262
- };
1263
-
1264
- const memoizedLoaders = /* @__PURE__ */ new WeakMap();
1265
- const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
1266
- function getLoader(Proto) {
1267
- if (isConstructor$1(Proto)) {
1268
- let loader = memoizedLoaders.get(Proto);
1269
- if (!loader) {
1270
- loader = new Proto();
1271
- memoizedLoaders.set(Proto, loader);
1272
- }
1273
- return loader;
1274
- }
1275
- return Proto;
1276
- }
1277
- function loadingFn(extensions, onProgress) {
1278
- return function(Proto, input) {
1279
- const loader = getLoader(Proto);
1280
- if (extensions) extensions(loader);
1281
- if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
1282
- return loader.loadAsync(input, onProgress).then((data) => {
1283
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1284
- return data;
1285
- });
1286
- }
1287
- return new Promise(
1288
- (res, reject) => loader.load(
1289
- input,
1290
- (data) => {
1291
- if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
1292
- res(data);
1293
- },
1294
- onProgress,
1295
- (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
1296
- )
1297
- );
1298
- };
1299
- }
1300
- function useLoader(loader, input, extensions, onProgress) {
1301
- const keys = Array.isArray(input) ? input : [input];
1302
- const fn = loadingFn(extensions, onProgress);
1303
- const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
1304
- return Array.isArray(input) ? results : results[0];
1305
- }
1306
- useLoader.preload = function(loader, input, extensions, onProgress) {
1307
- const keys = Array.isArray(input) ? input : [input];
1308
- keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
1309
- };
1310
- useLoader.clear = function(loader, input) {
1311
- const keys = Array.isArray(input) ? input : [input];
1312
- keys.forEach((key) => suspendReact.clear([loader, key]));
1313
- };
1314
- useLoader.loader = getLoader;
1315
-
1316
- var __defProp$1 = Object.defineProperty;
1317
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1318
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1319
- const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1320
- class PhaseGraph {
1321
- constructor() {
1322
- /** Ordered list of phase nodes */
1323
- __publicField$1(this, "phases", []);
1324
- /** Quick lookup by name */
1325
- __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1326
- /** Cached ordered names (invalidated on changes) */
1327
- __publicField$1(this, "orderedNamesCache", null);
1328
- this.initializeDefaultPhases();
1329
- }
1330
- //* Initialization --------------------------------
1331
- initializeDefaultPhases() {
1332
- for (const name of DEFAULT_PHASES) {
1333
- const node = { name, isAutoGenerated: false };
1334
- this.phases.push(node);
1335
- this.phaseMap.set(name, node);
1454
+ var __defProp$1 = Object.defineProperty;
1455
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1456
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1457
+ const DEFAULT_PHASES = ["start", "input", "physics", "update", "render", "finish"];
1458
+ class PhaseGraph {
1459
+ constructor() {
1460
+ /** Ordered list of phase nodes */
1461
+ __publicField$1(this, "phases", []);
1462
+ /** Quick lookup by name */
1463
+ __publicField$1(this, "phaseMap", /* @__PURE__ */ new Map());
1464
+ /** Cached ordered names (invalidated on changes) */
1465
+ __publicField$1(this, "orderedNamesCache", null);
1466
+ this.initializeDefaultPhases();
1467
+ }
1468
+ //* Initialization --------------------------------
1469
+ initializeDefaultPhases() {
1470
+ for (const name of DEFAULT_PHASES) {
1471
+ const node = { name, isAutoGenerated: false };
1472
+ this.phases.push(node);
1473
+ this.phaseMap.set(name, node);
1336
1474
  }
1337
1475
  this.invalidateCache();
1338
1476
  }
@@ -1351,8 +1489,9 @@ class PhaseGraph {
1351
1489
  const node = { name, isAutoGenerated: false };
1352
1490
  let insertIndex = this.phases.length;
1353
1491
  const targetIndex = this.getPhaseIndex(before ?? after);
1354
- if (targetIndex !== -1) insertIndex = before ? targetIndex : targetIndex + 1;
1355
- else {
1492
+ if (targetIndex !== -1) {
1493
+ insertIndex = before ? targetIndex : targetIndex + 1;
1494
+ } else {
1356
1495
  const constraintType = before ? "before" : "after";
1357
1496
  console.warn(`[useFrame] Phase "${before ?? after}" not found for '${constraintType}' constraint`);
1358
1497
  }
@@ -2233,116 +2372,429 @@ const _Scheduler = class _Scheduler {
2233
2372
  root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
2234
2373
  root.needsRebuild = false;
2235
2374
  }
2236
- const providedState = root.getState?.() ?? {};
2237
- const frameState = {
2238
- ...providedState,
2239
- time: timestamp,
2240
- delta,
2241
- elapsed: this.loopState.elapsedTime / 1e3,
2242
- // Convert ms to seconds
2243
- frame: this.loopState.frameCount
2244
- };
2245
- for (const job of root.sortedJobs) {
2246
- if (!shouldRun(job, timestamp)) continue;
2247
- try {
2248
- job.callback(frameState, delta);
2249
- } catch (error) {
2250
- console.error(`[Scheduler] Error in job "${job.id}":`, error);
2251
- this.triggerError(error instanceof Error ? error : new Error(String(error)));
2375
+ const providedState = root.getState?.() ?? {};
2376
+ const frameState = {
2377
+ ...providedState,
2378
+ time: timestamp,
2379
+ delta,
2380
+ elapsed: this.loopState.elapsedTime / 1e3,
2381
+ // Convert ms to seconds
2382
+ frame: this.loopState.frameCount
2383
+ };
2384
+ for (const job of root.sortedJobs) {
2385
+ if (!shouldRun(job, timestamp)) continue;
2386
+ try {
2387
+ job.callback(frameState, delta);
2388
+ } catch (error) {
2389
+ console.error(`[Scheduler] Error in job "${job.id}":`, error);
2390
+ this.triggerError(error instanceof Error ? error : new Error(String(error)));
2391
+ }
2392
+ }
2393
+ }
2394
+ //* Debug & Inspection Methods ================================
2395
+ /**
2396
+ * Get the total number of registered jobs across all roots.
2397
+ * Includes both per-root jobs and global before/after jobs.
2398
+ * @returns {number} Total job count
2399
+ */
2400
+ getJobCount() {
2401
+ let count = 0;
2402
+ for (const root of this.roots.values()) {
2403
+ count += root.jobs.size;
2404
+ }
2405
+ return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2406
+ }
2407
+ /**
2408
+ * Get all registered job IDs across all roots.
2409
+ * Includes both per-root jobs and global before/after jobs.
2410
+ * @returns {string[]} Array of all job IDs
2411
+ */
2412
+ getJobIds() {
2413
+ const ids = [];
2414
+ for (const root of this.roots.values()) {
2415
+ ids.push(...root.jobs.keys());
2416
+ }
2417
+ ids.push(...this.globalBeforeJobs.keys());
2418
+ ids.push(...this.globalAfterJobs.keys());
2419
+ return ids;
2420
+ }
2421
+ /**
2422
+ * Get the number of registered roots (Canvas instances).
2423
+ * @returns {number} Number of registered roots
2424
+ */
2425
+ getRootCount() {
2426
+ return this.roots.size;
2427
+ }
2428
+ /**
2429
+ * Check if any user (non-system) jobs are registered in a specific phase.
2430
+ * Used by the default render job to know if a user has taken over rendering.
2431
+ *
2432
+ * @param phase The phase to check
2433
+ * @param rootId Optional root ID to check (checks all roots if not provided)
2434
+ * @returns true if any user jobs exist in the phase
2435
+ */
2436
+ hasUserJobsInPhase(phase, rootId) {
2437
+ const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2438
+ return rootsToCheck.some((root) => {
2439
+ if (!root) return false;
2440
+ for (const job of root.jobs.values()) {
2441
+ if (job.phase === phase && !job.system && job.enabled) return true;
2442
+ }
2443
+ return false;
2444
+ });
2445
+ }
2446
+ //* Utility Methods ================================
2447
+ /**
2448
+ * Generate a unique root ID for automatic root registration.
2449
+ * @returns {string} A unique root ID in the format 'root_N'
2450
+ */
2451
+ generateRootId() {
2452
+ return `root_${this.nextRootIndex++}`;
2453
+ }
2454
+ /**
2455
+ * Generate a unique job ID.
2456
+ * @returns {string} A unique job ID in the format 'job_N'
2457
+ * @private
2458
+ */
2459
+ generateJobId() {
2460
+ return `job_${this.nextJobIndex}`;
2461
+ }
2462
+ /**
2463
+ * Normalize before/after constraints to a Set.
2464
+ * Handles undefined, single string, or array inputs.
2465
+ * @param {string | string[] | undefined} value - The constraint value(s)
2466
+ * @returns {Set<string>} Normalized Set of constraint strings
2467
+ * @private
2468
+ */
2469
+ normalizeConstraints(value) {
2470
+ if (!value) return /* @__PURE__ */ new Set();
2471
+ if (Array.isArray(value)) return new Set(value);
2472
+ return /* @__PURE__ */ new Set([value]);
2473
+ }
2474
+ };
2475
+ //* Static State & Methods (Singleton Usage) ================================
2476
+ //* Cross-Bundle Singleton Key ==============================
2477
+ // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2478
+ // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2479
+ __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2480
+ let Scheduler = _Scheduler;
2481
+ const getScheduler = () => Scheduler.get();
2482
+ if (hmrData) {
2483
+ hmrData.accept?.();
2484
+ }
2485
+
2486
+ const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
2487
+ const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
2488
+ const createStore = (invalidate, advance) => {
2489
+ const rootStore = traditional.createWithEqualityFn((set, get) => {
2490
+ const position = new webgpu.Vector3();
2491
+ const defaultTarget = new webgpu.Vector3();
2492
+ const tempTarget = new webgpu.Vector3();
2493
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
2494
+ const { width, height, top, left } = size;
2495
+ const aspect = width / height;
2496
+ if (target.isVector3) tempTarget.copy(target);
2497
+ else tempTarget.set(...target);
2498
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
2499
+ if (isOrthographicCamera(camera)) {
2500
+ return { width: width / camera.zoom, height: height / camera.zoom, top, left, factor: 1, distance, aspect };
2501
+ } else {
2502
+ const fov = camera.fov * Math.PI / 180;
2503
+ const h = 2 * Math.tan(fov / 2) * distance;
2504
+ const w = h * (width / height);
2505
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
2506
+ }
2507
+ }
2508
+ let performanceTimeout = void 0;
2509
+ const setPerformanceCurrent = (current) => set((state2) => ({ performance: { ...state2.performance, current } }));
2510
+ const pointer = new webgpu.Vector2();
2511
+ const rootState = {
2512
+ set,
2513
+ get,
2514
+ // Mock objects that have to be configured
2515
+ // primaryStore is set after store creation (self-reference for primary, primary's store for secondary)
2516
+ primaryStore: null,
2517
+ gl: null,
2518
+ renderer: null,
2519
+ camera: null,
2520
+ frustum: new webgpu.Frustum(),
2521
+ autoUpdateFrustum: true,
2522
+ raycaster: null,
2523
+ events: { priority: 1, enabled: true, connected: false },
2524
+ scene: null,
2525
+ rootScene: null,
2526
+ xr: null,
2527
+ inspector: null,
2528
+ invalidate: (frames = 1, stackFrames = false) => invalidate(get(), frames, stackFrames),
2529
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
2530
+ textureColorSpace: webgpu.SRGBColorSpace,
2531
+ isLegacy: false,
2532
+ webGPUSupported: false,
2533
+ isNative: false,
2534
+ controls: null,
2535
+ pointer,
2536
+ mouse: pointer,
2537
+ frameloop: "always",
2538
+ onPointerMissed: void 0,
2539
+ onDragOverMissed: void 0,
2540
+ onDropMissed: void 0,
2541
+ performance: {
2542
+ current: 1,
2543
+ min: 0.5,
2544
+ max: 1,
2545
+ debounce: 200,
2546
+ regress: () => {
2547
+ const state2 = get();
2548
+ if (performanceTimeout) clearTimeout(performanceTimeout);
2549
+ if (state2.performance.current !== state2.performance.min) setPerformanceCurrent(state2.performance.min);
2550
+ performanceTimeout = setTimeout(
2551
+ () => setPerformanceCurrent(get().performance.max),
2552
+ state2.performance.debounce
2553
+ );
2554
+ }
2555
+ },
2556
+ size: { width: 0, height: 0, top: 0, left: 0 },
2557
+ viewport: {
2558
+ initialDpr: 0,
2559
+ dpr: 0,
2560
+ width: 0,
2561
+ height: 0,
2562
+ top: 0,
2563
+ left: 0,
2564
+ aspect: 0,
2565
+ distance: 0,
2566
+ factor: 0,
2567
+ getCurrentViewport
2568
+ },
2569
+ setEvents: (events) => set((state2) => ({ ...state2, events: { ...state2.events, ...events } })),
2570
+ setSize: (width, height, top, left) => {
2571
+ const state2 = get();
2572
+ if (width === void 0) {
2573
+ set({ _sizeImperative: false });
2574
+ if (state2._sizeProps) {
2575
+ const { width: propW, height: propH } = state2._sizeProps;
2576
+ if (propW !== void 0 || propH !== void 0) {
2577
+ const currentSize = state2.size;
2578
+ const newSize = {
2579
+ width: propW ?? currentSize.width,
2580
+ height: propH ?? currentSize.height,
2581
+ top: currentSize.top,
2582
+ left: currentSize.left
2583
+ };
2584
+ set((s) => ({
2585
+ size: newSize,
2586
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
2587
+ }));
2588
+ getScheduler().invalidate();
2589
+ }
2590
+ }
2591
+ return;
2592
+ }
2593
+ const w = width;
2594
+ const h = height ?? width;
2595
+ const t = top ?? state2.size.top;
2596
+ const l = left ?? state2.size.left;
2597
+ const size = { width: w, height: h, top: t, left: l };
2598
+ set((s) => ({
2599
+ size,
2600
+ viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
2601
+ _sizeImperative: true
2602
+ }));
2603
+ getScheduler().invalidate();
2604
+ },
2605
+ setDpr: (dpr) => set((state2) => {
2606
+ const resolved = calculateDpr(dpr);
2607
+ return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
2608
+ }),
2609
+ setFrameloop: (frameloop = "always") => {
2610
+ set(() => ({ frameloop }));
2611
+ },
2612
+ setError: (error) => set(() => ({ error })),
2613
+ error: null,
2614
+ //* TSL State (managed via hooks: useUniforms, useNodes, useTextures, usePostProcessing) ==============================
2615
+ uniforms: {},
2616
+ nodes: {},
2617
+ textures: /* @__PURE__ */ new Map(),
2618
+ postProcessing: null,
2619
+ passes: {},
2620
+ _hmrVersion: 0,
2621
+ _sizeImperative: false,
2622
+ _sizeProps: null,
2623
+ previousRoot: void 0,
2624
+ internal: {
2625
+ // Events
2626
+ interaction: [],
2627
+ hovered: /* @__PURE__ */ new Map(),
2628
+ subscribers: [],
2629
+ initialClick: [0, 0],
2630
+ initialHits: [],
2631
+ capturedMap: /* @__PURE__ */ new Map(),
2632
+ lastEvent: React__namespace.createRef(),
2633
+ // Visibility tracking (onFramed, onOccluded, onVisible)
2634
+ visibilityRegistry: /* @__PURE__ */ new Map(),
2635
+ // Occlusion system (WebGPU only)
2636
+ occlusionEnabled: false,
2637
+ occlusionObserver: null,
2638
+ occlusionCache: /* @__PURE__ */ new Map(),
2639
+ helperGroup: null,
2640
+ // Updates
2641
+ active: false,
2642
+ frames: 0,
2643
+ priority: 0,
2644
+ subscribe: (ref, priority, store) => {
2645
+ const internal = get().internal;
2646
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
2647
+ internal.subscribers.push({ ref, priority, store });
2648
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
2649
+ return () => {
2650
+ const internal2 = get().internal;
2651
+ if (internal2?.subscribers) {
2652
+ internal2.priority = internal2.priority - (priority > 0 ? 1 : 0);
2653
+ internal2.subscribers = internal2.subscribers.filter((s) => s.ref !== ref);
2654
+ }
2655
+ };
2656
+ },
2657
+ // Renderer Storage (single source of truth)
2658
+ actualRenderer: null,
2659
+ // Scheduler for useFrameNext (initialized in renderer.tsx)
2660
+ scheduler: null
2661
+ }
2662
+ };
2663
+ return rootState;
2664
+ });
2665
+ const state = rootStore.getState();
2666
+ Object.defineProperty(state, "gl", {
2667
+ get() {
2668
+ const currentState = rootStore.getState();
2669
+ if (!currentState.isLegacy && currentState.internal.actualRenderer) {
2670
+ const stack = new Error().stack || "";
2671
+ const isInternalAccess = stack.includes("zustand") || stack.includes("setState") || stack.includes("Object.assign") || stack.includes("react-three-fiber/packages/fiber/src/core");
2672
+ if (!isInternalAccess) {
2673
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
2674
+ notifyDepreciated({
2675
+ heading: "Accessing state.gl in WebGPU mode",
2676
+ 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
2677
+ });
2678
+ }
2679
+ }
2680
+ return currentState.internal.actualRenderer;
2681
+ },
2682
+ set(value) {
2683
+ rootStore.getState().internal.actualRenderer = value;
2684
+ },
2685
+ enumerable: true,
2686
+ configurable: true
2687
+ });
2688
+ Object.defineProperty(state, "renderer", {
2689
+ get() {
2690
+ return rootStore.getState().internal.actualRenderer;
2691
+ },
2692
+ set(value) {
2693
+ rootStore.getState().internal.actualRenderer = value;
2694
+ },
2695
+ enumerable: true,
2696
+ configurable: true
2697
+ });
2698
+ let oldScene = state.scene;
2699
+ rootStore.subscribe(() => {
2700
+ const currentState = rootStore.getState();
2701
+ const { scene, rootScene, set } = currentState;
2702
+ if (scene !== oldScene) {
2703
+ oldScene = scene;
2704
+ if (scene?.isScene && scene !== rootScene) {
2705
+ set({ rootScene: scene });
2706
+ }
2707
+ }
2708
+ });
2709
+ let oldSize = state.size;
2710
+ let oldDpr = state.viewport.dpr;
2711
+ let oldCamera = state.camera;
2712
+ rootStore.subscribe(() => {
2713
+ const { camera, size, viewport, set, internal } = rootStore.getState();
2714
+ const actualRenderer = internal.actualRenderer;
2715
+ const canvasTarget = internal.canvasTarget;
2716
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
2717
+ oldSize = size;
2718
+ oldDpr = viewport.dpr;
2719
+ updateCamera(camera, size);
2720
+ if (canvasTarget) {
2721
+ if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
2722
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && canvasTarget.domElement instanceof HTMLCanvasElement;
2723
+ canvasTarget.setSize(size.width, size.height, updateStyle);
2724
+ } else {
2725
+ if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
2726
+ const updateStyle = typeof HTMLCanvasElement !== "undefined" && actualRenderer.domElement instanceof HTMLCanvasElement;
2727
+ actualRenderer.setSize(size.width, size.height, updateStyle);
2728
+ }
2729
+ }
2730
+ if (camera !== oldCamera) {
2731
+ oldCamera = camera;
2732
+ const { rootScene } = rootStore.getState();
2733
+ if (camera && rootScene && !camera.parent) {
2734
+ rootScene.add(camera);
2735
+ }
2736
+ set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
2737
+ const currentState = rootStore.getState();
2738
+ if (currentState.autoUpdateFrustum && camera) {
2739
+ updateFrustum(camera, currentState.frustum);
2252
2740
  }
2253
2741
  }
2254
- }
2255
- //* Debug & Inspection Methods ================================
2256
- /**
2257
- * Get the total number of registered jobs across all roots.
2258
- * Includes both per-root jobs and global before/after jobs.
2259
- * @returns {number} Total job count
2260
- */
2261
- getJobCount() {
2262
- let count = 0;
2263
- for (const root of this.roots.values()) {
2264
- count += root.jobs.size;
2742
+ });
2743
+ rootStore.subscribe((state2) => invalidate(state2));
2744
+ return rootStore;
2745
+ };
2746
+
2747
+ const memoizedLoaders = /* @__PURE__ */ new WeakMap();
2748
+ const isConstructor$1 = (value) => typeof value === "function" && value?.prototype?.constructor === value;
2749
+ function getLoader(Proto) {
2750
+ if (isConstructor$1(Proto)) {
2751
+ let loader = memoizedLoaders.get(Proto);
2752
+ if (!loader) {
2753
+ loader = new Proto();
2754
+ memoizedLoaders.set(Proto, loader);
2265
2755
  }
2266
- return count + this.globalBeforeJobs.size + this.globalAfterJobs.size;
2756
+ return loader;
2267
2757
  }
2268
- /**
2269
- * Get all registered job IDs across all roots.
2270
- * Includes both per-root jobs and global before/after jobs.
2271
- * @returns {string[]} Array of all job IDs
2272
- */
2273
- getJobIds() {
2274
- const ids = [];
2275
- for (const root of this.roots.values()) {
2276
- ids.push(...root.jobs.keys());
2758
+ return Proto;
2759
+ }
2760
+ function loadingFn(extensions, onProgress) {
2761
+ return function(Proto, input) {
2762
+ const loader = getLoader(Proto);
2763
+ if (extensions) extensions(loader);
2764
+ if ("loadAsync" in loader && typeof loader.loadAsync === "function") {
2765
+ return loader.loadAsync(input, onProgress).then((data) => {
2766
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2767
+ return data;
2768
+ });
2277
2769
  }
2278
- ids.push(...this.globalBeforeJobs.keys());
2279
- ids.push(...this.globalAfterJobs.keys());
2280
- return ids;
2281
- }
2282
- /**
2283
- * Get the number of registered roots (Canvas instances).
2284
- * @returns {number} Number of registered roots
2285
- */
2286
- getRootCount() {
2287
- return this.roots.size;
2288
- }
2289
- /**
2290
- * Check if any user (non-system) jobs are registered in a specific phase.
2291
- * Used by the default render job to know if a user has taken over rendering.
2292
- *
2293
- * @param phase The phase to check
2294
- * @param rootId Optional root ID to check (checks all roots if not provided)
2295
- * @returns true if any user jobs exist in the phase
2296
- */
2297
- hasUserJobsInPhase(phase, rootId) {
2298
- const rootsToCheck = rootId ? [this.roots.get(rootId)].filter(Boolean) : Array.from(this.roots.values());
2299
- return rootsToCheck.some((root) => {
2300
- if (!root) return false;
2301
- for (const job of root.jobs.values()) {
2302
- if (job.phase === phase && !job.system && job.enabled) return true;
2303
- }
2304
- return false;
2305
- });
2306
- }
2307
- //* Utility Methods ================================
2308
- /**
2309
- * Generate a unique root ID for automatic root registration.
2310
- * @returns {string} A unique root ID in the format 'root_N'
2311
- */
2312
- generateRootId() {
2313
- return `root_${this.nextRootIndex++}`;
2314
- }
2315
- /**
2316
- * Generate a unique job ID.
2317
- * @returns {string} A unique job ID in the format 'job_N'
2318
- * @private
2319
- */
2320
- generateJobId() {
2321
- return `job_${this.nextJobIndex}`;
2322
- }
2323
- /**
2324
- * Normalize before/after constraints to a Set.
2325
- * Handles undefined, single string, or array inputs.
2326
- * @param {string | string[] | undefined} value - The constraint value(s)
2327
- * @returns {Set<string>} Normalized Set of constraint strings
2328
- * @private
2329
- */
2330
- normalizeConstraints(value) {
2331
- if (!value) return /* @__PURE__ */ new Set();
2332
- if (Array.isArray(value)) return new Set(value);
2333
- return /* @__PURE__ */ new Set([value]);
2334
- }
2335
- };
2336
- //* Static State & Methods (Singleton Usage) ================================
2337
- //* Cross-Bundle Singleton Key ==============================
2338
- // Use Symbol.for() to ensure scheduler is shared across bundle boundaries
2339
- // This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
2340
- __publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
2341
- let Scheduler = _Scheduler;
2342
- const getScheduler = () => Scheduler.get();
2343
- if (hmrData) {
2344
- hmrData.accept?.();
2770
+ return new Promise(
2771
+ (res, reject) => loader.load(
2772
+ input,
2773
+ (data) => {
2774
+ if (isObject3D(data?.scene)) Object.assign(data, buildGraph(data.scene));
2775
+ res(data);
2776
+ },
2777
+ onProgress,
2778
+ (error) => reject(new Error(`Could not load ${input}: ${error?.message}`))
2779
+ )
2780
+ );
2781
+ };
2782
+ }
2783
+ function useLoader(loader, input, extensions, onProgress) {
2784
+ const keys = Array.isArray(input) ? input : [input];
2785
+ const fn = loadingFn(extensions, onProgress);
2786
+ const results = keys.map((key) => suspendReact.suspend(fn, [loader, key], { equal: is.equ }));
2787
+ return Array.isArray(input) ? results : results[0];
2345
2788
  }
2789
+ useLoader.preload = function(loader, input, extensions, onProgress) {
2790
+ const keys = Array.isArray(input) ? input : [input];
2791
+ keys.forEach((key) => suspendReact.preload(loadingFn(extensions, onProgress), [loader, key]));
2792
+ };
2793
+ useLoader.clear = function(loader, input) {
2794
+ const keys = Array.isArray(input) ? input : [input];
2795
+ keys.forEach((key) => suspendReact.clear([loader, key]));
2796
+ };
2797
+ useLoader.loader = getLoader;
2346
2798
 
2347
2799
  function useFrame(callback, priorityOrOptions) {
2348
2800
  const store = React__namespace.useContext(context);
@@ -2523,6 +2975,9 @@ function useTexture(input, optionsOrOnLoad) {
2523
2975
  const textureCache = useThree((state) => state.textures);
2524
2976
  const options = typeof optionsOrOnLoad === "function" ? { onLoad: optionsOrOnLoad } : optionsOrOnLoad ?? {};
2525
2977
  const { onLoad, cache = false } = options;
2978
+ const onLoadRef = React.useRef(onLoad);
2979
+ onLoadRef.current = onLoad;
2980
+ const onLoadCalledForRef = React.useRef(null);
2526
2981
  const urls = React.useMemo(() => getUrls(input), [input]);
2527
2982
  const cachedResult = React.useMemo(() => {
2528
2983
  if (!cache) return null;
@@ -2533,9 +2988,13 @@ function useTexture(input, optionsOrOnLoad) {
2533
2988
  webgpu.TextureLoader,
2534
2989
  IsObject(input) ? Object.values(input) : input
2535
2990
  );
2991
+ const inputKey = urls.join("\0");
2536
2992
  React.useLayoutEffect(() => {
2537
- if (!cachedResult) onLoad?.(loadedTextures);
2538
- }, [onLoad, cachedResult, loadedTextures]);
2993
+ if (cachedResult) return;
2994
+ if (onLoadCalledForRef.current === inputKey) return;
2995
+ onLoadCalledForRef.current = inputKey;
2996
+ onLoadRef.current?.(loadedTextures);
2997
+ }, [cachedResult, loadedTextures, inputKey]);
2539
2998
  React.useEffect(() => {
2540
2999
  if (cachedResult) return;
2541
3000
  if ("initTexture" in renderer) {
@@ -2702,16 +3161,33 @@ function useTextures() {
2702
3161
  }, [store]);
2703
3162
  }
2704
3163
 
2705
- function useRenderTarget(width, height, options) {
3164
+ function useRenderTarget(widthOrOptions, heightOrOptions, options) {
2706
3165
  const isLegacy = useThree((s) => s.isLegacy);
2707
3166
  const size = useThree((s) => s.size);
3167
+ let width;
3168
+ let height;
3169
+ let opts;
3170
+ if (typeof widthOrOptions === "object") {
3171
+ opts = widthOrOptions;
3172
+ } else if (typeof widthOrOptions === "number") {
3173
+ width = widthOrOptions;
3174
+ if (typeof heightOrOptions === "object") {
3175
+ height = widthOrOptions;
3176
+ opts = heightOrOptions;
3177
+ } else if (typeof heightOrOptions === "number") {
3178
+ height = heightOrOptions;
3179
+ opts = options;
3180
+ } else {
3181
+ height = widthOrOptions;
3182
+ }
3183
+ }
2708
3184
  return React.useMemo(() => {
2709
3185
  const w = width ?? size.width;
2710
3186
  const h = height ?? size.height;
2711
3187
  {
2712
- return isLegacy ? new three.WebGLRenderTarget(w, h, options) : new webgpu.RenderTarget(w, h, options);
3188
+ return isLegacy ? new three.WebGLRenderTarget(w, h, opts) : new webgpu.RenderTarget(w, h, opts);
2713
3189
  }
2714
- }, [width, height, size.width, size.height, options, isLegacy]);
3190
+ }, [width, height, size.width, size.height, opts, isLegacy]);
2715
3191
  }
2716
3192
 
2717
3193
  function useStore() {
@@ -2761,28 +3237,18 @@ function addTail(callback) {
2761
3237
  function invalidate(state, frames = 1, stackFrames = false) {
2762
3238
  getScheduler().invalidate(frames, stackFrames);
2763
3239
  }
2764
- function advance(timestamp, runGlobalEffects = true, state, frame) {
3240
+ function advance(timestamp) {
2765
3241
  getScheduler().step(timestamp);
2766
3242
  }
2767
3243
 
2768
- const version = "10.0.0-alpha.1";
3244
+ const version = "10.0.0-alpha.2";
2769
3245
  const packageData = {
2770
3246
  version: version};
2771
3247
 
2772
3248
  function Xb(Tt) {
2773
3249
  return Tt && Tt.__esModule && Object.prototype.hasOwnProperty.call(Tt, "default") ? Tt.default : Tt;
2774
3250
  }
2775
- var Rm = { exports: {} }, Og = { exports: {} };
2776
- /**
2777
- * @license React
2778
- * react-reconciler.production.js
2779
- *
2780
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2781
- *
2782
- * This source code is licensed under the MIT license found in the
2783
- * LICENSE file in the root directory of this source tree.
2784
- */
2785
- var _b;
3251
+ var Rm = { exports: {} }, Og = { exports: {} }, _b;
2786
3252
  function Kb() {
2787
3253
  return _b || (_b = 1, (function(Tt) {
2788
3254
  Tt.exports = function(m) {
@@ -3854,7 +4320,6 @@ Error generating stack: ` + l.message + `
3854
4320
  if (J === cl || J === jc) throw J;
3855
4321
  var Ge = Yn(29, J, null, P.mode);
3856
4322
  return Ge.lanes = H, Ge.return = P, Ge;
3857
- } finally {
3858
4323
  }
3859
4324
  };
3860
4325
  }
@@ -4508,7 +4973,6 @@ Error generating stack: ` + l.message + `
4508
4973
  var h = r.lastRenderedState, y = d(h, a);
4509
4974
  if (c.hasEagerState = true, c.eagerState = y, jn(y, h)) return go(t, r, c, 0), Ne === null && Bn(), false;
4510
4975
  } catch {
4511
- } finally {
4512
4976
  }
4513
4977
  if (a = yo(t, r, c, l), a !== null) return nt(a, t, l), ns(a, r, l), true;
4514
4978
  }
@@ -6929,10 +7393,7 @@ Error generating stack: ` + l.message + `
6929
7393
  function vr(t, r) {
6930
7394
  Sf(t, r), (t = t.alternate) && Sf(t, r);
6931
7395
  }
6932
- 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");
6933
- var gc = Symbol.for("react.activity");
6934
- var $r = Symbol.for("react.memo_cache_sentinel");
6935
- 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;
7396
+ 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;
6936
7397
  m.cloneMutableInstance;
6937
7398
  var yc = m.appendInitialChild, Kp = m.finalizeInitialChildren, Rs = m.shouldSetTextContent, bc = m.createTextInstance;
6938
7399
  m.cloneMutableTextInstance;
@@ -7301,17 +7762,7 @@ No matching component was found for:
7301
7762
  }, Tt.exports.default = Tt.exports, Object.defineProperty(Tt.exports, "__esModule", { value: true });
7302
7763
  })(Og)), Og.exports;
7303
7764
  }
7304
- var Mg = { exports: {} };
7305
- /**
7306
- * @license React
7307
- * react-reconciler.development.js
7308
- *
7309
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7310
- *
7311
- * This source code is licensed under the MIT license found in the
7312
- * LICENSE file in the root directory of this source tree.
7313
- */
7314
- var Rb;
7765
+ var Mg = { exports: {} }, Rb;
7315
7766
  function e0() {
7316
7767
  return Rb || (Rb = 1, (function(Tt) {
7317
7768
  process.env.NODE_ENV !== "production" && (Tt.exports = function(m) {
@@ -13078,10 +13529,7 @@ Check the render method of %s.`, G(di) || "Unknown")), i = zo(n), i.payload = {
13078
13529
  function Ic() {
13079
13530
  return di;
13080
13531
  }
13081
- 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");
13082
- var Ds = Symbol.for("react.activity");
13083
- var Bh = Symbol.for("react.memo_cache_sentinel");
13084
- 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;
13532
+ 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;
13085
13533
  m.cloneMutableInstance;
13086
13534
  var bn = m.appendInitialChild, Ue = m.finalizeInitialChildren, ue = m.shouldSetTextContent, Do = m.createTextInstance;
13087
13535
  m.cloneMutableTextInstance;
@@ -14049,15 +14497,6 @@ function n0() {
14049
14497
  var t0 = n0();
14050
14498
  const r0 = Xb(t0);
14051
14499
 
14052
- /**
14053
- * @license React
14054
- * react-reconciler-constants.production.js
14055
- *
14056
- * Copyright (c) Meta Platforms, Inc. and affiliates.
14057
- *
14058
- * This source code is licensed under the MIT license found in the
14059
- * LICENSE file in the root directory of this source tree.
14060
- */
14061
14500
  const t = 1, o = 8, r = 32, e = 2;
14062
14501
 
14063
14502
  function createReconciler(config) {
@@ -14084,10 +14523,11 @@ function extend(objects) {
14084
14523
  function validateInstance(type, props) {
14085
14524
  const name = toPascalCase(type);
14086
14525
  const target = catalogue[name];
14087
- if (type !== "primitive" && !target)
14526
+ if (type !== "primitive" && !target) {
14088
14527
  throw new Error(
14089
14528
  `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`
14090
14529
  );
14530
+ }
14091
14531
  if (type === "primitive" && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
14092
14532
  if (props.args !== void 0 && !Array.isArray(props.args)) throw new Error("R3F: The args prop must be an array!");
14093
14533
  }
@@ -14251,6 +14691,7 @@ function swapInstances() {
14251
14691
  instance.object = instance.props.object ?? new target(...instance.props.args ?? []);
14252
14692
  instance.object.__r3f = instance;
14253
14693
  setFiberRef(fiber, instance.object);
14694
+ delete instance.appliedOnce;
14254
14695
  applyProps(instance.object, instance.props);
14255
14696
  if (instance.props.attach) {
14256
14697
  attach(parent, instance);
@@ -14324,8 +14765,22 @@ const reconciler = /* @__PURE__ */ createReconciler({
14324
14765
  const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
14325
14766
  if (isTailSibling) swapInstances();
14326
14767
  },
14327
- finalizeInitialChildren: () => false,
14328
- commitMount() {
14768
+ finalizeInitialChildren: (instance) => {
14769
+ for (const prop in instance.props) {
14770
+ if (isFromRef(instance.props[prop])) return true;
14771
+ }
14772
+ return false;
14773
+ },
14774
+ commitMount(instance) {
14775
+ const resolved = {};
14776
+ for (const prop in instance.props) {
14777
+ const value = instance.props[prop];
14778
+ if (isFromRef(value)) {
14779
+ const ref = value[FROM_REF];
14780
+ if (ref.current != null) resolved[prop] = ref.current;
14781
+ }
14782
+ }
14783
+ if (Object.keys(resolved).length) applyProps(instance.object, resolved);
14329
14784
  },
14330
14785
  getPublicInstance: (instance) => instance?.object,
14331
14786
  prepareForCommit: () => null,
@@ -14538,14 +14993,17 @@ function createRoot(canvas) {
14538
14993
  if (!prevRoot) _roots.set(canvas, { fiber, store });
14539
14994
  let onCreated;
14540
14995
  let lastCamera;
14541
- let lastConfiguredProps = {};
14996
+ const lastConfiguredProps = {};
14542
14997
  let configured = false;
14543
14998
  let pending = null;
14544
14999
  return {
14545
15000
  async configure(props = {}) {
14546
15001
  let resolve;
14547
15002
  pending = new Promise((_resolve) => resolve = _resolve);
14548
- let {
15003
+ const {
15004
+ id: canvasId,
15005
+ primaryCanvas,
15006
+ scheduler: schedulerConfig,
14549
15007
  gl: glConfig,
14550
15008
  renderer: rendererConfig,
14551
15009
  size: propsSize,
@@ -14553,10 +15011,7 @@ function createRoot(canvas) {
14553
15011
  events,
14554
15012
  onCreated: onCreatedCallback,
14555
15013
  shadows = false,
14556
- linear = false,
14557
- flat = false,
14558
15014
  textureColorSpace = webgpu.SRGBColorSpace,
14559
- legacy = false,
14560
15015
  orthographic = false,
14561
15016
  frameloop = "always",
14562
15017
  dpr = [1, 2],
@@ -14567,9 +15022,11 @@ function createRoot(canvas) {
14567
15022
  onDragOverMissed,
14568
15023
  onDropMissed,
14569
15024
  autoUpdateFrustum = true,
14570
- occlusion = false
15025
+ occlusion = false,
15026
+ _sizeProps,
15027
+ forceEven
14571
15028
  } = props;
14572
- let state = store.getState();
15029
+ const state = store.getState();
14573
15030
  const defaultGLProps = {
14574
15031
  canvas,
14575
15032
  powerPreference: "high-performance",
@@ -14577,7 +15034,8 @@ function createRoot(canvas) {
14577
15034
  alpha: true
14578
15035
  };
14579
15036
  const defaultGPUProps = {
14580
- canvas
15037
+ canvas,
15038
+ antialias: true
14581
15039
  };
14582
15040
  const wantsGL = (state.isLegacy || glConfig || !R3F_BUILD_WEBGPU || !rendererConfig);
14583
15041
  if (glConfig && rendererConfig) {
@@ -14591,10 +15049,35 @@ function createRoot(canvas) {
14591
15049
  });
14592
15050
  }
14593
15051
  let renderer = state.internal.actualRenderer;
15052
+ if (primaryCanvas && wantsGL) {
15053
+ throw new Error(
15054
+ "The `primaryCanvas` prop for multi-canvas rendering cannot be used with WebGL. Remove the `gl` prop or use WebGPU."
15055
+ );
15056
+ }
14594
15057
  if (wantsGL && !state.internal.actualRenderer) {
14595
15058
  renderer = await resolveRenderer(glConfig, defaultGLProps, three.WebGLRenderer);
14596
15059
  state.internal.actualRenderer = renderer;
14597
- state.set({ isLegacy: true, gl: renderer, renderer });
15060
+ state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
15061
+ } else if (!wantsGL && primaryCanvas && !state.internal.actualRenderer) {
15062
+ const primary = await waitForPrimary(primaryCanvas);
15063
+ renderer = primary.renderer;
15064
+ state.internal.actualRenderer = renderer;
15065
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15066
+ primary.store.setState((prev) => ({
15067
+ internal: { ...prev.internal, isMultiCanvas: true }
15068
+ }));
15069
+ state.set((prev) => ({
15070
+ webGPUSupported: primary.store.getState().webGPUSupported,
15071
+ renderer,
15072
+ primaryStore: primary.store,
15073
+ internal: {
15074
+ ...prev.internal,
15075
+ canvasTarget,
15076
+ isMultiCanvas: true,
15077
+ isSecondary: true,
15078
+ targetId: primaryCanvas
15079
+ }
15080
+ }));
14598
15081
  } else if (!wantsGL && !state.internal.actualRenderer) {
14599
15082
  renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
14600
15083
  if (!renderer.hasInitialized?.()) {
@@ -14603,14 +15086,27 @@ function createRoot(canvas) {
14603
15086
  const backend = renderer.backend;
14604
15087
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14605
15088
  state.internal.actualRenderer = renderer;
14606
- state.set({ webGPUSupported: isWebGPUBackend, renderer });
15089
+ state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
15090
+ if (canvasId && !state.internal.isSecondary) {
15091
+ const canvasTarget = new webgpu.CanvasTarget(canvas);
15092
+ const unregisterPrimary = registerPrimary(canvasId, renderer, store);
15093
+ state.set((prev) => ({
15094
+ internal: {
15095
+ ...prev.internal,
15096
+ canvasTarget,
15097
+ unregisterPrimary
15098
+ }
15099
+ }));
15100
+ }
14607
15101
  }
14608
15102
  let raycaster = state.raycaster;
14609
15103
  if (!raycaster) state.set({ raycaster: raycaster = new webgpu.Raycaster() });
14610
15104
  const { params, ...options } = raycastOptions || {};
14611
15105
  if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options });
14612
- if (!is.equ(params, raycaster.params, shallowLoose))
15106
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
14613
15107
  applyProps(raycaster, { params: { ...raycaster.params, ...params } });
15108
+ }
15109
+ let tempCamera = state.camera;
14614
15110
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
14615
15111
  lastCamera = cameraOptions;
14616
15112
  const isCamera = cameraOptions?.isCamera;
@@ -14630,6 +15126,7 @@ function createRoot(canvas) {
14630
15126
  if (!state.camera && !cameraOptions?.rotation) camera.lookAt(0, 0, 0);
14631
15127
  }
14632
15128
  state.set({ camera });
15129
+ tempCamera = camera;
14633
15130
  raycaster.camera = camera;
14634
15131
  }
14635
15132
  if (!state.scene) {
@@ -14647,7 +15144,7 @@ function createRoot(canvas) {
14647
15144
  rootScene: scene,
14648
15145
  internal: { ...prev.internal, container: scene }
14649
15146
  }));
14650
- const camera = state.camera;
15147
+ const camera = tempCamera;
14651
15148
  if (camera && !camera.parent) scene.add(camera);
14652
15149
  }
14653
15150
  if (events && !state.events.handlers) {
@@ -14661,9 +15158,17 @@ function createRoot(canvas) {
14661
15158
  wasEnabled = enabled;
14662
15159
  });
14663
15160
  }
15161
+ if (_sizeProps !== void 0) {
15162
+ state.set({ _sizeProps });
15163
+ }
15164
+ if (forceEven !== void 0 && state.internal.forceEven !== forceEven) {
15165
+ state.set((prev) => ({ internal: { ...prev.internal, forceEven } }));
15166
+ }
14664
15167
  const size = computeInitialSize(canvas, propsSize);
14665
- if (!is.equ(size, state.size, shallowLoose)) {
15168
+ if (!state._sizeImperative && !is.equ(size, state.size, shallowLoose)) {
15169
+ const wasImperative = state._sizeImperative;
14666
15170
  state.setSize(size.width, size.height, size.top, size.left);
15171
+ if (!wasImperative) state.set({ _sizeImperative: false });
14667
15172
  }
14668
15173
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
14669
15174
  state.setDpr(dpr);
@@ -14688,7 +15193,7 @@ function createRoot(canvas) {
14688
15193
  const handleXRFrame = (timestamp, frame) => {
14689
15194
  const state2 = store.getState();
14690
15195
  if (state2.frameloop === "never") return;
14691
- advance(timestamp, true);
15196
+ advance(timestamp);
14692
15197
  };
14693
15198
  const actualRenderer = state.internal.actualRenderer;
14694
15199
  const handleSessionChange = () => {
@@ -14700,16 +15205,16 @@ function createRoot(canvas) {
14700
15205
  };
14701
15206
  const xr = {
14702
15207
  connect() {
14703
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14704
- const actualRenderer2 = renderer2 || gl;
14705
- actualRenderer2.xr.addEventListener("sessionstart", handleSessionChange);
14706
- actualRenderer2.xr.addEventListener("sessionend", handleSessionChange);
15208
+ const { gl, renderer: renderer2 } = store.getState();
15209
+ const xrManager = (renderer2 || gl).xr;
15210
+ xrManager.addEventListener("sessionstart", handleSessionChange);
15211
+ xrManager.addEventListener("sessionend", handleSessionChange);
14707
15212
  },
14708
15213
  disconnect() {
14709
- const { gl, renderer: renderer2, isLegacy } = store.getState();
14710
- const actualRenderer2 = renderer2 || gl;
14711
- actualRenderer2.xr.removeEventListener("sessionstart", handleSessionChange);
14712
- actualRenderer2.xr.removeEventListener("sessionend", handleSessionChange);
15214
+ const { gl, renderer: renderer2 } = store.getState();
15215
+ const xrManager = (renderer2 || gl).xr;
15216
+ xrManager.removeEventListener("sessionstart", handleSessionChange);
15217
+ xrManager.removeEventListener("sessionend", handleSessionChange);
14713
15218
  }
14714
15219
  };
14715
15220
  if (typeof renderer.xr?.addEventListener === "function") xr.connect();
@@ -14733,43 +15238,21 @@ function createRoot(canvas) {
14733
15238
  } else if (is.obj(shadows)) {
14734
15239
  Object.assign(renderer.shadowMap, shadows);
14735
15240
  }
14736
- if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type)
15241
+ if (oldEnabled !== renderer.shadowMap.enabled || oldType !== renderer.shadowMap.type) {
14737
15242
  renderer.shadowMap.needsUpdate = true;
14738
- }
14739
- {
14740
- const legacyChanged = legacy !== lastConfiguredProps.legacy;
14741
- const linearChanged = linear !== lastConfiguredProps.linear;
14742
- const flatChanged = flat !== lastConfiguredProps.flat;
14743
- if (legacyChanged) {
14744
- if (legacy)
14745
- notifyDepreciated({
14746
- heading: "Legacy Color Management",
14747
- body: "Legacy color management is deprecated and will be removed in a future version.",
14748
- link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
14749
- });
14750
- }
14751
- if (legacyChanged) {
14752
- webgpu.ColorManagement.enabled = !legacy;
14753
- lastConfiguredProps.legacy = legacy;
14754
15243
  }
14755
- if (!configured || linearChanged) {
14756
- renderer.outputColorSpace = linear ? webgpu.LinearSRGBColorSpace : webgpu.SRGBColorSpace;
14757
- lastConfiguredProps.linear = linear;
14758
- }
14759
- if (!configured || flatChanged) {
14760
- renderer.toneMapping = flat ? webgpu.NoToneMapping : webgpu.ACESFilmicToneMapping;
14761
- lastConfiguredProps.flat = flat;
14762
- }
14763
- if (legacyChanged && state.legacy !== legacy) state.set(() => ({ legacy }));
14764
- if (linearChanged && state.linear !== linear) state.set(() => ({ linear }));
14765
- if (flatChanged && state.flat !== flat) state.set(() => ({ flat }));
15244
+ }
15245
+ if (!configured) {
15246
+ renderer.outputColorSpace = webgpu.SRGBColorSpace;
15247
+ renderer.toneMapping = webgpu.ACESFilmicToneMapping;
14766
15248
  }
14767
15249
  if (textureColorSpace !== lastConfiguredProps.textureColorSpace) {
14768
15250
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
14769
15251
  lastConfiguredProps.textureColorSpace = textureColorSpace;
14770
15252
  }
14771
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose))
15253
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
14772
15254
  applyProps(renderer, glConfig);
15255
+ }
14773
15256
  if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
14774
15257
  const currentRenderer = state.renderer;
14775
15258
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
@@ -14779,11 +15262,26 @@ function createRoot(canvas) {
14779
15262
  const scheduler = getScheduler();
14780
15263
  const rootId = state.internal.rootId;
14781
15264
  if (!rootId) {
14782
- const newRootId = scheduler.generateRootId();
15265
+ const newRootId = canvasId || scheduler.generateRootId();
14783
15266
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14784
15267
  getState: () => store.getState(),
14785
15268
  onError: (err) => store.getState().setError(err)
14786
15269
  });
15270
+ const unregisterCanvasTarget = scheduler.register(
15271
+ () => {
15272
+ const state2 = store.getState();
15273
+ if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
15274
+ const renderer2 = state2.internal.actualRenderer;
15275
+ renderer2.setCanvasTarget(state2.internal.canvasTarget);
15276
+ }
15277
+ },
15278
+ {
15279
+ id: `${newRootId}_canvasTarget`,
15280
+ rootId: newRootId,
15281
+ phase: "start",
15282
+ system: true
15283
+ }
15284
+ );
14787
15285
  const unregisterFrustum = scheduler.register(
14788
15286
  () => {
14789
15287
  const state2 = store.getState();
@@ -14825,11 +15323,15 @@ function createRoot(canvas) {
14825
15323
  }
14826
15324
  },
14827
15325
  {
14828
- id: `${newRootId}_render`,
15326
+ // Use canvas ID directly as job ID if available, otherwise use generated rootId
15327
+ id: canvasId || `${newRootId}_render`,
14829
15328
  rootId: newRootId,
14830
15329
  phase: "render",
14831
- system: true
15330
+ system: true,
14832
15331
  // Internal flag: this is a system job, not user-controlled
15332
+ // Apply scheduler config for render ordering and rate limiting
15333
+ ...schedulerConfig?.after && { after: schedulerConfig.after },
15334
+ ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14833
15335
  }
14834
15336
  );
14835
15337
  state.set((state2) => ({
@@ -14838,6 +15340,7 @@ function createRoot(canvas) {
14838
15340
  rootId: newRootId,
14839
15341
  unregisterRoot: () => {
14840
15342
  unregisterRoot();
15343
+ unregisterCanvasTarget();
14841
15344
  unregisterFrustum();
14842
15345
  unregisterVisibility();
14843
15346
  unregisterRender();
@@ -14896,15 +15399,24 @@ function unmountComponentAtNode(canvas, callback) {
14896
15399
  const renderer = state.internal.actualRenderer;
14897
15400
  const unregisterRoot = state.internal.unregisterRoot;
14898
15401
  if (unregisterRoot) unregisterRoot();
15402
+ const unregisterPrimary = state.internal.unregisterPrimary;
15403
+ if (unregisterPrimary) unregisterPrimary();
15404
+ const canvasTarget = state.internal.canvasTarget;
15405
+ if (canvasTarget?.dispose) canvasTarget.dispose();
14899
15406
  state.events.disconnect?.();
14900
15407
  cleanupHelperGroup(root.store);
14901
- renderer?.renderLists?.dispose?.();
14902
- renderer?.forceContextLoss?.();
14903
- if (renderer?.xr) state.xr.disconnect();
15408
+ if (state.isLegacy && renderer) {
15409
+ ;
15410
+ renderer.renderLists?.dispose?.();
15411
+ renderer.forceContextLoss?.();
15412
+ }
15413
+ if (!state.internal.isSecondary) {
15414
+ if (renderer?.xr) state.xr.disconnect();
15415
+ }
14904
15416
  dispose(state.scene);
14905
15417
  _roots.delete(canvas);
14906
15418
  if (callback) callback(canvas);
14907
- } catch (e) {
15419
+ } catch {
14908
15420
  }
14909
15421
  }, 500);
14910
15422
  }
@@ -14912,36 +15424,34 @@ function unmountComponentAtNode(canvas, callback) {
14912
15424
  }
14913
15425
  }
14914
15426
  function createPortal(children, container, state) {
14915
- return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { children, container, state });
15427
+ return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container, state });
14916
15428
  }
14917
- function PortalWrapper({ children, container, state }) {
15429
+ function Portal({ children, container, state }) {
14918
15430
  const isRef = React.useCallback((obj) => obj && "current" in obj, []);
14919
- const [resolvedContainer, setResolvedContainer] = React.useState(() => {
15431
+ const [resolvedContainer, _setResolvedContainer] = React.useState(() => {
14920
15432
  if (isRef(container)) return container.current ?? null;
14921
15433
  return container;
14922
15434
  });
15435
+ const setResolvedContainer = React.useCallback(
15436
+ (newContainer) => {
15437
+ if (!newContainer || newContainer === resolvedContainer) return;
15438
+ _setResolvedContainer(isRef(newContainer) ? newContainer.current : newContainer);
15439
+ },
15440
+ [resolvedContainer, _setResolvedContainer, isRef]
15441
+ );
14923
15442
  React.useMemo(() => {
14924
- if (isRef(container)) {
14925
- const current = container.current;
14926
- if (!current) {
14927
- queueMicrotask(() => {
14928
- const updated = container.current;
14929
- if (updated && updated !== resolvedContainer) {
14930
- setResolvedContainer(updated);
14931
- }
14932
- });
14933
- } else if (current !== resolvedContainer) {
14934
- setResolvedContainer(current);
14935
- }
14936
- } else if (container !== resolvedContainer) {
14937
- setResolvedContainer(container);
15443
+ if (isRef(container) && !container.current) {
15444
+ return queueMicrotask(() => {
15445
+ setResolvedContainer(container.current);
15446
+ });
14938
15447
  }
14939
- }, [container, resolvedContainer, isRef]);
15448
+ setResolvedContainer(container);
15449
+ }, [container, isRef, setResolvedContainer]);
14940
15450
  if (!resolvedContainer) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
14941
15451
  const portalKey = resolvedContainer.uuid ?? `portal-${resolvedContainer.id ?? "unknown"}`;
14942
- return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children, container: resolvedContainer, state }, portalKey);
15452
+ return /* @__PURE__ */ jsxRuntime.jsx(PortalInner, { children, container: resolvedContainer, state }, portalKey);
14943
15453
  }
14944
- function Portal({ state = {}, children, container }) {
15454
+ function PortalInner({ state = {}, children, container }) {
14945
15455
  const { events, size, injectScene = true, ...rest } = state;
14946
15456
  const previousRoot = useStore();
14947
15457
  const [raycaster] = React.useState(() => new webgpu.Raycaster());
@@ -14962,11 +15472,12 @@ function Portal({ state = {}, children, container }) {
14962
15472
  };
14963
15473
  }, [portalScene, container, injectScene]);
14964
15474
  const inject = useMutableCallback((rootState, injectState) => {
15475
+ const resolvedSize = { ...rootState.size, ...injectState.size, ...size };
14965
15476
  let viewport = void 0;
14966
- if (injectState.camera && size) {
15477
+ if (injectState.camera && (size || injectState.size)) {
14967
15478
  const camera = injectState.camera;
14968
- viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), size);
14969
- if (camera !== rootState.camera) updateCamera(camera, size);
15479
+ viewport = rootState.viewport.getCurrentViewport(camera, new webgpu.Vector3(), resolvedSize);
15480
+ if (camera !== rootState.camera) updateCamera(camera, resolvedSize);
14970
15481
  }
14971
15482
  return {
14972
15483
  // The intersect consists of the previous root state
@@ -14983,7 +15494,7 @@ function Portal({ state = {}, children, container }) {
14983
15494
  previousRoot,
14984
15495
  // Events, size and viewport can be overridden by the inject layer
14985
15496
  events: { ...rootState.events, ...injectState.events, ...events },
14986
- size: { ...rootState.size, ...size },
15497
+ size: resolvedSize,
14987
15498
  viewport: { ...rootState.viewport, ...viewport },
14988
15499
  // Layers are allowed to override events
14989
15500
  setEvents: (events2) => injectState.set((state2) => ({ ...state2, events: { ...state2.events, ...events2 } })),
@@ -15017,15 +15528,13 @@ function CanvasImpl({
15017
15528
  fallback,
15018
15529
  resize,
15019
15530
  style,
15531
+ id,
15020
15532
  gl,
15021
- renderer,
15533
+ renderer: rendererProp,
15022
15534
  events = createPointerEvents,
15023
15535
  eventSource,
15024
15536
  eventPrefix,
15025
15537
  shadows,
15026
- linear,
15027
- flat,
15028
- legacy,
15029
15538
  orthographic,
15030
15539
  frameloop,
15031
15540
  dpr,
@@ -15037,10 +15546,46 @@ function CanvasImpl({
15037
15546
  onDragOverMissed,
15038
15547
  onDropMissed,
15039
15548
  onCreated,
15549
+ hmr,
15550
+ width,
15551
+ height,
15552
+ background,
15553
+ forceEven,
15040
15554
  ...props
15041
15555
  }) {
15556
+ 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 };
15557
+ const renderer = Object.keys(rendererConfig).length > 0 ? rendererConfig : rendererProp;
15042
15558
  React__namespace.useMemo(() => extend(THREE), []);
15043
15559
  const Bridge = useBridge();
15560
+ const backgroundProps = React__namespace.useMemo(() => {
15561
+ if (!background) return null;
15562
+ if (typeof background === "object" && !background.isColor) {
15563
+ const { backgroundMap, envMap, files, preset, ...rest } = background;
15564
+ return {
15565
+ ...rest,
15566
+ preset,
15567
+ files: envMap || files,
15568
+ backgroundFiles: backgroundMap,
15569
+ background: true
15570
+ };
15571
+ }
15572
+ if (typeof background === "number") {
15573
+ return { color: background, background: true };
15574
+ }
15575
+ if (typeof background === "string") {
15576
+ if (background in presetsObj) {
15577
+ return { preset: background, background: true };
15578
+ }
15579
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)|\\.(hdr|exr|jpg|jpeg|png|webp|gif)$/i.test(background)) {
15580
+ return { files: background, background: true };
15581
+ }
15582
+ return { color: background, background: true };
15583
+ }
15584
+ if (background.isColor) {
15585
+ return { color: background, background: true };
15586
+ }
15587
+ return null;
15588
+ }, [background]);
15044
15589
  const hasInitialSizeRef = React__namespace.useRef(false);
15045
15590
  const measureConfig = React__namespace.useMemo(() => {
15046
15591
  if (!hasInitialSizeRef.current) {
@@ -15057,7 +15602,21 @@ function CanvasImpl({
15057
15602
  };
15058
15603
  }, [resize, hasInitialSizeRef.current]);
15059
15604
  const [containerRef, containerRect] = useMeasure__default(measureConfig);
15060
- if (!hasInitialSizeRef.current && containerRect.width > 0 && containerRect.height > 0) {
15605
+ const effectiveSize = React__namespace.useMemo(() => {
15606
+ let w = width ?? containerRect.width;
15607
+ let h = height ?? containerRect.height;
15608
+ if (forceEven) {
15609
+ w = Math.ceil(w / 2) * 2;
15610
+ h = Math.ceil(h / 2) * 2;
15611
+ }
15612
+ return {
15613
+ width: w,
15614
+ height: h,
15615
+ top: containerRect.top,
15616
+ left: containerRect.left
15617
+ };
15618
+ }, [width, height, containerRect, forceEven]);
15619
+ if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15061
15620
  hasInitialSizeRef.current = true;
15062
15621
  }
15063
15622
  const canvasRef = React__namespace.useRef(null);
@@ -15076,7 +15635,7 @@ function CanvasImpl({
15076
15635
  useIsomorphicLayoutEffect(() => {
15077
15636
  effectActiveRef.current = true;
15078
15637
  const canvas = canvasRef.current;
15079
- if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
15638
+ if (effectiveSize.width > 0 && effectiveSize.height > 0 && canvas) {
15080
15639
  if (!root.current) {
15081
15640
  root.current = createRoot(canvas);
15082
15641
  notifyAlpha({
@@ -15096,21 +15655,24 @@ function CanvasImpl({
15096
15655
  async function run() {
15097
15656
  if (!effectActiveRef.current || !root.current) return;
15098
15657
  await root.current.configure({
15658
+ id,
15659
+ primaryCanvas,
15660
+ scheduler,
15099
15661
  gl,
15100
15662
  renderer,
15101
15663
  scene,
15102
15664
  events,
15103
15665
  shadows,
15104
- linear,
15105
- flat,
15106
- legacy,
15107
15666
  orthographic,
15108
15667
  frameloop,
15109
15668
  dpr,
15110
15669
  performance,
15111
15670
  raycaster,
15112
15671
  camera,
15113
- size: containerRect,
15672
+ size: effectiveSize,
15673
+ // Store size props for reset functionality
15674
+ _sizeProps: width !== void 0 || height !== void 0 ? { width, height } : null,
15675
+ forceEven,
15114
15676
  // Pass mutable reference to onPointerMissed so it's free to update
15115
15677
  onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
15116
15678
  onDragOverMissed: (...args) => handleDragOverMissed.current?.(...args),
@@ -15134,7 +15696,10 @@ function CanvasImpl({
15134
15696
  });
15135
15697
  if (!effectActiveRef.current || !root.current) return;
15136
15698
  root.current.render(
15137
- /* @__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 }) }) })
15699
+ /* @__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: [
15700
+ backgroundProps && /* @__PURE__ */ jsxRuntime.jsx(Environment, { ...backgroundProps }),
15701
+ children ?? null
15702
+ ] }) }) })
15138
15703
  );
15139
15704
  }
15140
15705
  run();
@@ -15156,6 +15721,35 @@ function CanvasImpl({
15156
15721
  };
15157
15722
  }
15158
15723
  }, []);
15724
+ React__namespace.useEffect(() => {
15725
+ if (hmr === false) return;
15726
+ const canvas = canvasRef.current;
15727
+ if (!canvas) return;
15728
+ const handleHMR = () => {
15729
+ queueMicrotask(() => {
15730
+ const rootEntry = _roots.get(canvas);
15731
+ if (rootEntry?.store) {
15732
+ rootEntry.store.setState((state) => ({
15733
+ nodes: {},
15734
+ uniforms: {},
15735
+ _hmrVersion: state._hmrVersion + 1
15736
+ }));
15737
+ }
15738
+ });
15739
+ };
15740
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined) {
15741
+ const hot = undefined;
15742
+ hot.on("vite:afterUpdate", handleHMR);
15743
+ return () => hot.dispose?.(() => {
15744
+ });
15745
+ }
15746
+ if (typeof module !== "undefined" && module.hot) {
15747
+ const hot = module.hot;
15748
+ hot.addStatusHandler((status) => {
15749
+ if (status === "idle") handleHMR();
15750
+ });
15751
+ }
15752
+ }, [hmr]);
15159
15753
  const pointerEvents = eventSource ? "none" : "auto";
15160
15754
  return /* @__PURE__ */ jsxRuntime.jsx(
15161
15755
  "div",
@@ -15170,7 +15764,7 @@ function CanvasImpl({
15170
15764
  ...style
15171
15765
  },
15172
15766
  ...props,
15173
- 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 }) })
15767
+ 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 }) })
15174
15768
  }
15175
15769
  );
15176
15770
  }
@@ -15182,8 +15776,15 @@ extend(THREE);
15182
15776
 
15183
15777
  exports.Block = Block;
15184
15778
  exports.Canvas = Canvas;
15779
+ exports.Environment = Environment;
15780
+ exports.EnvironmentCube = EnvironmentCube;
15781
+ exports.EnvironmentMap = EnvironmentMap;
15782
+ exports.EnvironmentPortal = EnvironmentPortal;
15185
15783
  exports.ErrorBoundary = ErrorBoundary;
15784
+ exports.FROM_REF = FROM_REF;
15186
15785
  exports.IsObject = IsObject;
15786
+ exports.ONCE = ONCE;
15787
+ exports.Portal = Portal;
15187
15788
  exports.R3F_BUILD_LEGACY = R3F_BUILD_LEGACY;
15188
15789
  exports.R3F_BUILD_WEBGPU = R3F_BUILD_WEBGPU;
15189
15790
  exports.REACT_INTERNAL_PROPS = REACT_INTERNAL_PROPS;
@@ -15213,30 +15814,41 @@ exports.events = createPointerEvents;
15213
15814
  exports.extend = extend;
15214
15815
  exports.findInitialRoot = findInitialRoot;
15215
15816
  exports.flushSync = flushSync;
15817
+ exports.fromRef = fromRef;
15216
15818
  exports.getInstanceProps = getInstanceProps;
15819
+ exports.getPrimary = getPrimary;
15820
+ exports.getPrimaryIds = getPrimaryIds;
15217
15821
  exports.getRootState = getRootState;
15218
15822
  exports.getScheduler = getScheduler;
15219
15823
  exports.getUuidPrefix = getUuidPrefix;
15220
15824
  exports.hasConstructor = hasConstructor;
15825
+ exports.hasPrimary = hasPrimary;
15221
15826
  exports.invalidate = invalidate;
15222
15827
  exports.invalidateInstance = invalidateInstance;
15223
15828
  exports.is = is;
15224
15829
  exports.isColorRepresentation = isColorRepresentation;
15225
15830
  exports.isCopyable = isCopyable;
15831
+ exports.isFromRef = isFromRef;
15226
15832
  exports.isObject3D = isObject3D;
15833
+ exports.isOnce = isOnce;
15227
15834
  exports.isOrthographicCamera = isOrthographicCamera;
15228
15835
  exports.isRef = isRef;
15229
15836
  exports.isRenderer = isRenderer;
15230
15837
  exports.isTexture = isTexture;
15231
15838
  exports.isVectorLike = isVectorLike;
15839
+ exports.once = once;
15232
15840
  exports.prepare = prepare;
15841
+ exports.presetsObj = presetsObj;
15233
15842
  exports.reconciler = reconciler;
15843
+ exports.registerPrimary = registerPrimary;
15234
15844
  exports.removeInteractivity = removeInteractivity;
15235
15845
  exports.resolve = resolve;
15236
15846
  exports.unmountComponentAtNode = unmountComponentAtNode;
15847
+ exports.unregisterPrimary = unregisterPrimary;
15237
15848
  exports.updateCamera = updateCamera;
15238
15849
  exports.updateFrustum = updateFrustum;
15239
15850
  exports.useBridge = useBridge;
15851
+ exports.useEnvironment = useEnvironment;
15240
15852
  exports.useFrame = useFrame;
15241
15853
  exports.useGraph = useGraph;
15242
15854
  exports.useInstanceHandle = useInstanceHandle;
@@ -15248,3 +15860,4 @@ exports.useStore = useStore;
15248
15860
  exports.useTexture = useTexture;
15249
15861
  exports.useTextures = useTextures;
15250
15862
  exports.useThree = useThree;
15863
+ exports.waitForPrimary = waitForPrimary;