@react-three/fiber 9.0.0-rc.0 → 9.0.0-rc.10

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.
@@ -4,11 +4,11 @@ var THREE = require('three');
4
4
  var React = require('react');
5
5
  var constants = require('react-reconciler/constants');
6
6
  var traditional = require('zustand/traditional');
7
+ var Reconciler = require('react-reconciler');
8
+ var scheduler = require('scheduler');
7
9
  var suspendReact = require('suspend-react');
8
10
  var jsxRuntime = require('react/jsx-runtime');
9
11
  var itsFine = require('its-fine');
10
- var Reconciler = require('react-reconciler');
11
- var scheduler = require('scheduler');
12
12
 
13
13
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
14
14
 
@@ -38,856 +38,414 @@ var threeTypes = /*#__PURE__*/Object.freeze({
38
38
  __proto__: null
39
39
  });
40
40
 
41
- // TODO: upstream to DefinitelyTyped for React 19
42
- // https://github.com/facebook/react/issues/28956
43
-
44
- const createReconciler = Reconciler__default["default"];
45
-
46
- // TODO: handle constructor overloads
47
- // https://github.com/pmndrs/react-three-fiber/pull/2931
48
- // https://github.com/microsoft/TypeScript/issues/37079
49
-
50
- const catalogue = {};
51
- let i = 0;
52
- const isConstructor$1 = object => typeof object === 'function';
53
- function extend(objects) {
54
- if (isConstructor$1(objects)) {
55
- const Component = `${i++}`;
56
- catalogue[Component] = objects;
57
- return Component;
58
- } else {
59
- Object.assign(catalogue, objects);
60
- }
41
+ /**
42
+ * Returns the instance's initial (outmost) root.
43
+ */
44
+ function findInitialRoot(instance) {
45
+ let root = instance.root;
46
+ while (root.getState().previousRoot) root = root.getState().previousRoot;
47
+ return root;
61
48
  }
62
- function validateInstance(type, props) {
63
- // Get target from catalogue
64
- const name = `${type[0].toUpperCase()}${type.slice(1)}`;
65
- const target = catalogue[name];
66
-
67
- // Validate element target
68
- if (type !== 'primitive' && !target) throw new Error(`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`);
49
+ /**
50
+ * Safely flush async effects when testing, simulating a legacy root.
51
+ */
52
+ const act = React__namespace.act;
53
+ const isOrthographicCamera = def => def && def.isOrthographicCamera;
54
+ const isRef = obj => obj && obj.hasOwnProperty('current');
55
+ const isColorRepresentation = value => value != null && (typeof value === 'string' || typeof value === 'number' || value.isColor);
69
56
 
70
- // Validate primitives
71
- if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
57
+ /**
58
+ * An SSR-friendly useLayoutEffect.
59
+ *
60
+ * React currently throws a warning when using useLayoutEffect on the server.
61
+ * To get around it, we can conditionally useEffect on the server (no-op) and
62
+ * useLayoutEffect elsewhere.
63
+ *
64
+ * @see https://github.com/facebook/react/issues/14927
65
+ */
66
+ const useIsomorphicLayoutEffect = /* @__PURE__ */((_window$document, _window$navigator) => typeof window !== 'undefined' && (((_window$document = window.document) == null ? void 0 : _window$document.createElement) || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative'))() ? React__namespace.useLayoutEffect : React__namespace.useEffect;
67
+ function useMutableCallback(fn) {
68
+ const ref = React__namespace.useRef(fn);
69
+ useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
70
+ return ref;
71
+ }
72
+ /**
73
+ * Bridges renderer Context and StrictMode from a primary renderer.
74
+ */
75
+ function useBridge() {
76
+ const fiber = itsFine.useFiber();
77
+ const ContextBridge = itsFine.useContextBridge();
78
+ return React__namespace.useMemo(() => ({
79
+ children
80
+ }) => {
81
+ const strict = !!itsFine.traverseFiber(fiber, true, node => node.type === React__namespace.StrictMode);
82
+ const Root = strict ? React__namespace.StrictMode : React__namespace.Fragment;
83
+ return /*#__PURE__*/jsxRuntime.jsx(Root, {
84
+ children: /*#__PURE__*/jsxRuntime.jsx(ContextBridge, {
85
+ children: children
86
+ })
87
+ });
88
+ }, [fiber, ContextBridge]);
89
+ }
90
+ function Block({
91
+ set
92
+ }) {
93
+ useIsomorphicLayoutEffect(() => {
94
+ set(new Promise(() => null));
95
+ return () => set(false);
96
+ }, [set]);
97
+ return null;
98
+ }
72
99
 
73
- // Throw if an object or literal was passed for args
74
- if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
100
+ // NOTE: static members get down-level transpiled to mutations which break tree-shaking
101
+ const ErrorBoundary = /* @__PURE__ */(_ErrorBoundary => (_ErrorBoundary = class ErrorBoundary extends React__namespace.Component {
102
+ constructor(...args) {
103
+ super(...args);
104
+ this.state = {
105
+ error: false
106
+ };
107
+ }
108
+ componentDidCatch(err) {
109
+ this.props.set(err);
110
+ }
111
+ render() {
112
+ return this.state.error ? null : this.props.children;
113
+ }
114
+ }, _ErrorBoundary.getDerivedStateFromError = () => ({
115
+ error: true
116
+ }), _ErrorBoundary))();
117
+ function calculateDpr(dpr) {
118
+ var _window$devicePixelRa;
119
+ // Err on the side of progress by assuming 2x dpr if we can't detect it
120
+ // This will happen in workers where window is defined but dpr isn't.
121
+ const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
122
+ return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
75
123
  }
76
- function createInstance(type, props, root) {
77
- var _props$object;
78
- validateInstance(type, props);
79
124
 
80
- // Regenerate the R3F instance for primitives to simulate a new object
81
- if (type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
82
- return prepare(props.object, root, type, props);
125
+ /**
126
+ * Returns instance root state
127
+ */
128
+ function getRootState(obj) {
129
+ var _r3f;
130
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
83
131
  }
84
- function hideInstance(instance) {
85
- if (!instance.isHidden) {
86
- var _instance$parent;
87
- if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
88
- detach(instance.parent, instance);
89
- } else if (isObject3D(instance.object)) {
90
- instance.object.visible = false;
132
+ // A collection of compare functions
133
+ const is = {
134
+ obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
135
+ fun: a => typeof a === 'function',
136
+ str: a => typeof a === 'string',
137
+ num: a => typeof a === 'number',
138
+ boo: a => typeof a === 'boolean',
139
+ und: a => a === void 0,
140
+ nul: a => a === null,
141
+ arr: a => Array.isArray(a),
142
+ equ(a, b, {
143
+ arrays = 'shallow',
144
+ objects = 'reference',
145
+ strict = true
146
+ } = {}) {
147
+ // Wrong type or one of the two undefined, doesn't match
148
+ if (typeof a !== typeof b || !!a !== !!b) return false;
149
+ // Atomic, just compare a against b
150
+ if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
151
+ const isObj = is.obj(a);
152
+ if (isObj && objects === 'reference') return a === b;
153
+ const isArr = is.arr(a);
154
+ if (isArr && arrays === 'reference') return a === b;
155
+ // Array or Object, shallow compare first to see if it's a match
156
+ if ((isArr || isObj) && a === b) return true;
157
+ // Last resort, go through keys
158
+ let i;
159
+ // Check if a has all the keys of b
160
+ for (i in a) if (!(i in b)) return false;
161
+ // Check if values between keys match
162
+ if (isObj && arrays === 'shallow' && objects === 'shallow') {
163
+ for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
164
+ strict,
165
+ objects: 'reference'
166
+ })) return false;
167
+ } else {
168
+ for (i in strict ? b : a) if (a[i] !== b[i]) return false;
91
169
  }
92
- instance.isHidden = true;
93
- invalidateInstance(instance);
170
+ // If i is undefined
171
+ if (is.und(i)) {
172
+ // If both arrays are empty we consider them equal
173
+ if (isArr && a.length === 0 && b.length === 0) return true;
174
+ // If both objects are empty we consider them equal
175
+ if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
176
+ // Otherwise match them by value
177
+ if (a !== b) return false;
178
+ }
179
+ return true;
180
+ }
181
+ };
182
+
183
+ // Collects nodes and materials from a THREE.Object3D
184
+ function buildGraph(object) {
185
+ const data = {
186
+ nodes: {},
187
+ materials: {}
188
+ };
189
+ if (object) {
190
+ object.traverse(obj => {
191
+ if (obj.name) data.nodes[obj.name] = obj;
192
+ if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
193
+ });
94
194
  }
195
+ return data;
95
196
  }
96
- function unhideInstance(instance) {
97
- if (instance.isHidden) {
98
- var _instance$parent2;
99
- if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
100
- attach(instance.parent, instance);
101
- } else if (isObject3D(instance.object) && instance.props.visible !== false) {
102
- instance.object.visible = true;
103
- }
104
- instance.isHidden = false;
105
- invalidateInstance(instance);
197
+ // Disposes an object and all its properties
198
+ function dispose(obj) {
199
+ if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
200
+ for (const p in obj) {
201
+ const prop = obj[p];
202
+ if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
106
203
  }
107
204
  }
205
+ const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
108
206
 
109
- // https://github.com/facebook/react/issues/20271
110
- // This will make sure events and attach are only handled once when trees are complete
111
- function handleContainerEffects(parent, child, beforeChild) {
112
- // Bail if tree isn't mounted or parent is not a container.
113
- // This ensures that the tree is finalized and React won't discard results to Suspense
114
- const state = child.root.getState();
115
- if (!parent.parent && parent.object !== state.scene) return;
116
-
117
- // Create & link object on first run
118
- if (!child.object) {
119
- var _child$props$object, _child$props$args;
120
- // Get target from catalogue
121
- const name = `${child.type[0].toUpperCase()}${child.type.slice(1)}`;
122
- const target = catalogue[name];
207
+ // Gets only instance props from reconciler fibers
208
+ function getInstanceProps(queue) {
209
+ const props = {};
210
+ for (const key in queue) {
211
+ if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
212
+ }
213
+ return props;
214
+ }
123
215
 
124
- // Create object
125
- child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
126
- child.object.__r3f = child;
216
+ // Each object in the scene carries a small LocalState descriptor
217
+ function prepare(target, root, type, props) {
218
+ const object = target;
127
219
 
128
- // Set initial props
129
- applyProps(child.object, child.props);
220
+ // Create instance descriptor
221
+ let instance = object == null ? void 0 : object.__r3f;
222
+ if (!instance) {
223
+ instance = {
224
+ root,
225
+ type,
226
+ parent: null,
227
+ children: [],
228
+ props: getInstanceProps(props),
229
+ object,
230
+ eventCount: 0,
231
+ handlers: {},
232
+ isHidden: false
233
+ };
234
+ if (object) object.__r3f = instance;
130
235
  }
236
+ return instance;
237
+ }
238
+ function resolve(root, key) {
239
+ var _target;
240
+ let target = root[key];
241
+ if (!key.includes('-')) return {
242
+ root,
243
+ key,
244
+ target
245
+ };
131
246
 
132
- // Append instance
133
- if (child.props.attach) {
134
- attach(parent, child);
135
- } else if (isObject3D(child.object) && isObject3D(parent.object)) {
136
- const childIndex = parent.object.children.indexOf(beforeChild == null ? void 0 : beforeChild.object);
137
- if (beforeChild && childIndex !== -1) {
138
- child.object.parent = parent.object;
139
- parent.object.children.splice(childIndex, 0, child.object);
140
- child.object.dispatchEvent({
141
- type: 'added'
142
- });
143
- parent.object.dispatchEvent({
144
- type: 'childadded',
145
- child: child.object
146
- });
147
- } else {
148
- parent.object.add(child.object);
149
- }
150
- }
151
-
152
- // Link subtree
153
- for (const childInstance of child.children) handleContainerEffects(child, childInstance);
247
+ // Resolve pierced target
248
+ const chain = key.split('-');
249
+ target = chain.reduce((acc, key) => acc[key], root);
250
+ key = chain.pop();
154
251
 
155
- // Tree was updated, request a frame
156
- invalidateInstance(child);
252
+ // Switch root if atomic
253
+ if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
254
+ return {
255
+ root,
256
+ key,
257
+ target
258
+ };
157
259
  }
158
- function appendChild(parent, child) {
159
- if (!child) return;
160
-
161
- // Link instances
162
- child.parent = parent;
163
- parent.children.push(child);
164
260
 
165
- // Attach tree once complete
166
- handleContainerEffects(parent, child);
261
+ // Checks if a dash-cased string ends with an integer
262
+ const INDEX_REGEX = /-\d+$/;
263
+ function attach(parent, child) {
264
+ if (is.str(child.props.attach)) {
265
+ // If attaching into an array (foo-0), create one
266
+ if (INDEX_REGEX.test(child.props.attach)) {
267
+ const index = child.props.attach.replace(INDEX_REGEX, '');
268
+ const {
269
+ root,
270
+ key
271
+ } = resolve(parent.object, index);
272
+ if (!Array.isArray(root[key])) root[key] = [];
273
+ }
274
+ const {
275
+ root,
276
+ key
277
+ } = resolve(parent.object, child.props.attach);
278
+ child.previousAttach = root[key];
279
+ root[key] = child.object;
280
+ } else if (is.fun(child.props.attach)) {
281
+ child.previousAttach = child.props.attach(parent.object, child.object);
282
+ }
167
283
  }
168
- function insertBefore(parent, child, beforeChild) {
169
- if (!child || !beforeChild) return;
170
-
171
- // Link instances
172
- child.parent = parent;
173
- const childIndex = parent.children.indexOf(beforeChild);
174
- if (childIndex !== -1) parent.children.splice(childIndex, 0, child);else parent.children.push(child);
175
-
176
- // Attach tree once complete
177
- handleContainerEffects(parent, child, beforeChild);
284
+ function detach(parent, child) {
285
+ if (is.str(child.props.attach)) {
286
+ const {
287
+ root,
288
+ key
289
+ } = resolve(parent.object, child.props.attach);
290
+ const previous = child.previousAttach;
291
+ // When the previous value was undefined, it means the value was never set to begin with
292
+ if (previous === undefined) delete root[key];
293
+ // Otherwise set the previous value
294
+ else root[key] = previous;
295
+ } else {
296
+ child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
297
+ }
298
+ delete child.previousAttach;
178
299
  }
179
- function disposeOnIdle(object) {
180
- if (typeof object.dispose === 'function') {
181
- const handleDispose = () => {
182
- try {
183
- object.dispose();
184
- } catch {
185
- // no-op
186
- }
187
- };
188
-
189
- // In a testing environment, cleanup immediately
190
- if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose();
191
- // Otherwise, using a real GPU so schedule cleanup to prevent stalls
192
- else scheduler.unstable_scheduleCallback(scheduler.unstable_IdlePriority, handleDispose);
300
+ const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
301
+ // Instance props
302
+ 'args', 'dispose', 'attach', 'object', 'onUpdate',
303
+ // Behavior flags
304
+ 'dispose'];
305
+ const MEMOIZED_PROTOTYPES = new Map();
306
+ function getMemoizedPrototype(root) {
307
+ let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
308
+ try {
309
+ if (!ctor) {
310
+ ctor = new root.constructor();
311
+ MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
312
+ }
313
+ } catch (e) {
314
+ // ...
193
315
  }
316
+ return ctor;
194
317
  }
195
- function removeChild(parent, child, dispose) {
196
- if (!child) return;
197
318
 
198
- // Unlink instances
199
- child.parent = null;
200
- const childIndex = parent.children.indexOf(child);
201
- if (childIndex !== -1) parent.children.splice(childIndex, 1);
319
+ // This function prepares a set of changes to be applied to the instance
320
+ function diffProps(instance, newProps) {
321
+ const changedProps = {};
202
322
 
203
- // Eagerly tear down tree
204
- if (child.props.attach) {
205
- detach(parent, child);
206
- } else if (isObject3D(child.object) && isObject3D(parent.object)) {
207
- parent.object.remove(child.object);
208
- removeInteractivity(findInitialRoot(child), child.object);
209
- }
323
+ // Sort through props
324
+ for (const prop in newProps) {
325
+ // Skip reserved keys
326
+ if (RESERVED_PROPS.includes(prop)) continue;
327
+ // Skip if props match
328
+ if (is.equ(newProps[prop], instance.props[prop])) continue;
210
329
 
211
- // Allow objects to bail out of unmount disposal with dispose={null}
212
- const shouldDispose = child.props.dispose !== null && dispose !== false;
330
+ // Props changed, add them
331
+ changedProps[prop] = newProps[prop];
213
332
 
214
- // Recursively remove instance children
215
- for (let i = child.children.length - 1; i >= 0; i--) {
216
- const node = child.children[i];
217
- removeChild(child, node, shouldDispose);
333
+ // Reset pierced props
334
+ for (const other in newProps) {
335
+ if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
336
+ }
218
337
  }
219
- child.children.length = 0;
220
338
 
221
- // Unlink instance object
222
- delete child.object.__r3f;
223
-
224
- // Dispose object whenever the reconciler feels like it.
225
- // Never dispose of primitives because their state may be kept outside of React!
226
- // In order for an object to be able to dispose it
227
- // - has a dispose method
228
- // - cannot be a <primitive object={...} />
229
- // - cannot be a THREE.Scene, because three has broken its own API
230
- if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {
231
- disposeOnIdle(child.object);
232
- }
339
+ // Reset removed props for HMR
340
+ for (const prop in instance.props) {
341
+ if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
342
+ const {
343
+ root,
344
+ key
345
+ } = resolve(instance.object, prop);
233
346
 
234
- // Tree was updated, request a frame for top-level instance
235
- if (dispose === undefined) invalidateInstance(child);
236
- }
237
- function setFiberRef(fiber, publicInstance) {
238
- for (const _fiber of [fiber, fiber.alternate]) {
239
- if (_fiber !== null) {
240
- if (typeof _fiber.ref === 'function') {
241
- _fiber.refCleanup == null ? void 0 : _fiber.refCleanup();
242
- const cleanup = _fiber.ref(publicInstance);
243
- if (typeof cleanup === 'function') _fiber.refCleanup = cleanup;
244
- } else if (_fiber.ref) {
245
- _fiber.ref.current = publicInstance;
246
- }
347
+ // https://github.com/mrdoob/three.js/issues/21209
348
+ // HMR/fast-refresh relies on the ability to cancel out props, but threejs
349
+ // has no means to do this. Hence we curate a small collection of value-classes
350
+ // with their respective constructor/set arguments
351
+ // For removed props, try to set default values, if possible
352
+ if (root.constructor && root.constructor.length === 0) {
353
+ // create a blank slate of the instance and copy the particular parameter.
354
+ const ctor = getMemoizedPrototype(root);
355
+ if (!is.und(ctor)) changedProps[key] = ctor[key];
356
+ } else {
357
+ // instance does not have constructor, just set it to 0
358
+ changedProps[key] = 0;
247
359
  }
248
360
  }
361
+ return changedProps;
249
362
  }
250
- const reconstructed = [];
251
- function swapInstances() {
252
- // Detach instance
253
- for (const [instance] of reconstructed) {
254
- const parent = instance.parent;
255
- if (parent) {
256
- if (instance.props.attach) {
257
- detach(parent, instance);
258
- } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
259
- parent.object.remove(instance.object);
260
- }
261
- for (const child of instance.children) {
262
- if (child.props.attach) {
263
- detach(instance, child);
264
- } else if (isObject3D(child.object) && isObject3D(instance.object)) {
265
- instance.object.remove(child.object);
266
- }
267
- }
268
- }
269
363
 
270
- // If the old instance is hidden, we need to unhide it.
271
- // React assumes it can discard instances since they're pure for DOM.
272
- // This isn't true for us since our lifetimes are impure and longliving.
273
- // So, we manually check if an instance was hidden and unhide it.
274
- if (instance.isHidden) unhideInstance(instance);
364
+ // https://github.com/mrdoob/three.js/pull/27042
365
+ // https://github.com/mrdoob/three.js/pull/22748
366
+ const colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap'];
367
+ const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
368
+ // This function applies a set of changes to the instance
369
+ function applyProps(object, props) {
370
+ var _instance$object;
371
+ const instance = object.__r3f;
372
+ const rootState = instance && findInitialRoot(instance).getState();
373
+ const prevHandlers = instance == null ? void 0 : instance.eventCount;
374
+ for (const prop in props) {
375
+ let value = props[prop];
275
376
 
276
- // Dispose of old object if able
277
- if (instance.object.__r3f) delete instance.object.__r3f;
278
- if (instance.type !== 'primitive') disposeOnIdle(instance.object);
279
- }
377
+ // Don't mutate reserved keys
378
+ if (RESERVED_PROPS.includes(prop)) continue;
280
379
 
281
- // Update instance
282
- for (const [instance, props, fiber] of reconstructed) {
283
- instance.props = props;
284
- const parent = instance.parent;
285
- if (parent) {
286
- var _instance$props$objec, _instance$props$args;
287
- // Get target from catalogue
288
- const name = `${instance.type[0].toUpperCase()}${instance.type.slice(1)}`;
289
- const target = catalogue[name];
380
+ // Deal with pointer events, including removing them if undefined
381
+ if (instance && EVENT_REGEX.test(prop)) {
382
+ if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
383
+ instance.eventCount = Object.keys(instance.handlers).length;
384
+ continue;
385
+ }
290
386
 
291
- // Create object
292
- instance.object = (_instance$props$objec = instance.props.object) != null ? _instance$props$objec : new target(...((_instance$props$args = instance.props.args) != null ? _instance$props$args : []));
293
- instance.object.__r3f = instance;
294
- setFiberRef(fiber, instance.object);
387
+ // Ignore setting undefined props
388
+ // https://github.com/pmndrs/react-three-fiber/issues/274
389
+ if (value === undefined) continue;
390
+ let {
391
+ root,
392
+ key,
393
+ target
394
+ } = resolve(object, prop);
295
395
 
296
- // Set initial props
297
- applyProps(instance.object, instance.props);
298
- if (instance.props.attach) {
299
- attach(parent, instance);
300
- } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
301
- parent.object.add(instance.object);
302
- }
303
- for (const child of instance.children) {
304
- if (child.props.attach) {
305
- attach(instance, child);
306
- } else if (isObject3D(child.object) && isObject3D(instance.object)) {
307
- instance.object.add(child.object);
308
- }
309
- }
310
-
311
- // Tree was updated, request a frame
312
- invalidateInstance(instance);
313
- }
314
- }
315
- reconstructed.length = 0;
316
- }
317
-
318
- // Don't handle text instances, make it no-op
319
- const handleTextInstance = () => {};
320
- const NO_CONTEXT = {};
321
- let currentUpdatePriority = constants.NoEventPriority;
322
-
323
- // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js
324
- const NoFlags = 0;
325
- const Update = 4;
326
- const reconciler = createReconciler({
327
- isPrimaryRenderer: false,
328
- warnsIfNotActing: false,
329
- supportsMutation: true,
330
- supportsPersistence: false,
331
- supportsHydration: false,
332
- createInstance,
333
- removeChild,
334
- appendChild,
335
- appendInitialChild: appendChild,
336
- insertBefore,
337
- appendChildToContainer(container, child) {
338
- const scene = container.getState().scene.__r3f;
339
- if (!child || !scene) return;
340
- appendChild(scene, child);
341
- },
342
- removeChildFromContainer(container, child) {
343
- const scene = container.getState().scene.__r3f;
344
- if (!child || !scene) return;
345
- removeChild(scene, child);
346
- },
347
- insertInContainerBefore(container, child, beforeChild) {
348
- const scene = container.getState().scene.__r3f;
349
- if (!child || !beforeChild || !scene) return;
350
- insertBefore(scene, child, beforeChild);
351
- },
352
- getRootHostContext: () => NO_CONTEXT,
353
- getChildHostContext: () => NO_CONTEXT,
354
- commitUpdate(instance, type, oldProps, newProps, fiber) {
355
- var _newProps$args, _oldProps$args, _newProps$args2;
356
- validateInstance(type, newProps);
357
- let reconstruct = false;
358
-
359
- // Reconstruct primitives if object prop changes
360
- if (instance.type === 'primitive' && oldProps.object !== newProps.object) reconstruct = true;
361
- // Reconstruct instance if args were added or removed
362
- else if (((_newProps$args = newProps.args) == null ? void 0 : _newProps$args.length) !== ((_oldProps$args = oldProps.args) == null ? void 0 : _oldProps$args.length)) reconstruct = true;
363
- // Reconstruct instance if args were changed
364
- else if ((_newProps$args2 = newProps.args) != null && _newProps$args2.some((value, index) => {
365
- var _oldProps$args2;
366
- return value !== ((_oldProps$args2 = oldProps.args) == null ? void 0 : _oldProps$args2[index]);
367
- })) reconstruct = true;
368
-
369
- // Reconstruct when args or <primitive object={...} have changes
370
- if (reconstruct) {
371
- reconstructed.push([instance, {
372
- ...newProps
373
- }, fiber]);
374
- } else {
375
- // Create a diff-set, flag if there are any changes
376
- const changedProps = diffProps(instance, newProps);
377
- if (Object.keys(changedProps).length) {
378
- Object.assign(instance.props, changedProps);
379
- applyProps(instance.object, changedProps);
380
- }
381
- }
382
-
383
- // Flush reconstructed siblings when we hit the last updated child in a sequence
384
- const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
385
- if (isTailSibling) swapInstances();
386
- },
387
- finalizeInitialChildren: () => false,
388
- commitMount() {},
389
- getPublicInstance: instance => instance == null ? void 0 : instance.object,
390
- prepareForCommit: () => null,
391
- preparePortalMount: container => prepare(container.getState().scene, container, '', {}),
392
- resetAfterCommit: () => {},
393
- shouldSetTextContent: () => false,
394
- clearContainer: () => false,
395
- hideInstance,
396
- unhideInstance,
397
- createTextInstance: handleTextInstance,
398
- hideTextInstance: handleTextInstance,
399
- unhideTextInstance: handleTextInstance,
400
- scheduleTimeout: typeof setTimeout === 'function' ? setTimeout : undefined,
401
- cancelTimeout: typeof clearTimeout === 'function' ? clearTimeout : undefined,
402
- noTimeout: -1,
403
- getInstanceFromNode: () => null,
404
- beforeActiveInstanceBlur() {},
405
- afterActiveInstanceBlur() {},
406
- detachDeletedInstance() {},
407
- prepareScopeUpdate() {},
408
- getInstanceFromScope: () => null,
409
- shouldAttemptEagerTransition() {
410
- return false;
411
- },
412
- requestPostPaintCallback() {},
413
- maySuspendCommit() {
414
- return false;
415
- },
416
- preloadInstance() {
417
- return true; // true indicates already loaded
418
- },
419
- startSuspendingCommit() {},
420
- suspendInstance() {},
421
- waitForCommitToBeReady() {
422
- return null;
423
- },
424
- NotPendingTransition: null,
425
- setCurrentUpdatePriority(newPriority) {
426
- currentUpdatePriority = newPriority;
427
- },
428
- getCurrentUpdatePriority() {
429
- return currentUpdatePriority;
430
- },
431
- resolveUpdatePriority() {
432
- var _window$event;
433
- if (currentUpdatePriority !== constants.NoEventPriority) return currentUpdatePriority;
434
- switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
435
- case 'click':
436
- case 'contextmenu':
437
- case 'dblclick':
438
- case 'pointercancel':
439
- case 'pointerdown':
440
- case 'pointerup':
441
- return constants.DiscreteEventPriority;
442
- case 'pointermove':
443
- case 'pointerout':
444
- case 'pointerover':
445
- case 'pointerenter':
446
- case 'pointerleave':
447
- case 'wheel':
448
- return constants.ContinuousEventPriority;
449
- default:
450
- return constants.DefaultEventPriority;
451
- }
452
- },
453
- resetFormInstance() {}
454
- });
455
-
456
- var _window$document, _window$navigator;
457
- /**
458
- * Returns the instance's initial (outmost) root.
459
- */
460
- function findInitialRoot(instance) {
461
- let root = instance.root;
462
- while (root.getState().previousRoot) root = root.getState().previousRoot;
463
- return root;
464
- }
465
-
466
- /**
467
- * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
468
- */
469
- const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
470
- /**
471
- * The current THREE.ColorManagement instance, if present.
472
- */
473
- const getColorManagement = () => {
474
- var _ColorManagement;
475
- return (_ColorManagement = catalogue.ColorManagement) != null ? _ColorManagement : null;
476
- };
477
- /**
478
- * Safely flush async effects when testing, simulating a legacy root.
479
- */
480
- const act = React__namespace.act;
481
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
482
- const isRef = obj => obj && obj.hasOwnProperty('current');
483
-
484
- /**
485
- * An SSR-friendly useLayoutEffect.
486
- *
487
- * React currently throws a warning when using useLayoutEffect on the server.
488
- * To get around it, we can conditionally useEffect on the server (no-op) and
489
- * useLayoutEffect elsewhere.
490
- *
491
- * @see https://github.com/facebook/react/issues/14927
492
- */
493
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' && ((_window$document = window.document) != null && _window$document.createElement || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative') ? React__namespace.useLayoutEffect : React__namespace.useEffect;
494
- function useMutableCallback(fn) {
495
- const ref = React__namespace.useRef(fn);
496
- useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
497
- return ref;
498
- }
499
- /**
500
- * Bridges renderer Context and StrictMode from a primary renderer.
501
- */
502
- function useBridge() {
503
- const fiber = itsFine.useFiber();
504
- const ContextBridge = itsFine.useContextBridge();
505
- return React__namespace.useMemo(() => ({
506
- children
507
- }) => {
508
- const strict = !!itsFine.traverseFiber(fiber, true, node => node.type === React__namespace.StrictMode);
509
- const Root = strict ? React__namespace.StrictMode : React__namespace.Fragment;
510
- return /*#__PURE__*/jsxRuntime.jsx(Root, {
511
- children: /*#__PURE__*/jsxRuntime.jsx(ContextBridge, {
512
- children: children
513
- })
514
- });
515
- }, [fiber, ContextBridge]);
516
- }
517
- function Block({
518
- set
519
- }) {
520
- useIsomorphicLayoutEffect(() => {
521
- set(new Promise(() => null));
522
- return () => set(false);
523
- }, [set]);
524
- return null;
525
- }
526
- class ErrorBoundary extends React__namespace.Component {
527
- constructor(...args) {
528
- super(...args);
529
- this.state = {
530
- error: false
531
- };
532
- }
533
- componentDidCatch(err) {
534
- this.props.set(err);
535
- }
536
- render() {
537
- return this.state.error ? null : this.props.children;
538
- }
539
- }
540
- ErrorBoundary.getDerivedStateFromError = () => ({
541
- error: true
542
- });
543
- function calculateDpr(dpr) {
544
- var _window$devicePixelRa;
545
- // Err on the side of progress by assuming 2x dpr if we can't detect it
546
- // This will happen in workers where window is defined but dpr isn't.
547
- const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
548
- return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
549
- }
550
-
551
- /**
552
- * Returns instance root state
553
- */
554
- function getRootState(obj) {
555
- var _r3f;
556
- return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
557
- }
558
- // A collection of compare functions
559
- const is = {
560
- obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
561
- fun: a => typeof a === 'function',
562
- str: a => typeof a === 'string',
563
- num: a => typeof a === 'number',
564
- boo: a => typeof a === 'boolean',
565
- und: a => a === void 0,
566
- arr: a => Array.isArray(a),
567
- equ(a, b, {
568
- arrays = 'shallow',
569
- objects = 'reference',
570
- strict = true
571
- } = {}) {
572
- // Wrong type or one of the two undefined, doesn't match
573
- if (typeof a !== typeof b || !!a !== !!b) return false;
574
- // Atomic, just compare a against b
575
- if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
576
- const isObj = is.obj(a);
577
- if (isObj && objects === 'reference') return a === b;
578
- const isArr = is.arr(a);
579
- if (isArr && arrays === 'reference') return a === b;
580
- // Array or Object, shallow compare first to see if it's a match
581
- if ((isArr || isObj) && a === b) return true;
582
- // Last resort, go through keys
583
- let i;
584
- // Check if a has all the keys of b
585
- for (i in a) if (!(i in b)) return false;
586
- // Check if values between keys match
587
- if (isObj && arrays === 'shallow' && objects === 'shallow') {
588
- for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
589
- strict,
590
- objects: 'reference'
591
- })) return false;
592
- } else {
593
- for (i in strict ? b : a) if (a[i] !== b[i]) return false;
594
- }
595
- // If i is undefined
596
- if (is.und(i)) {
597
- // If both arrays are empty we consider them equal
598
- if (isArr && a.length === 0 && b.length === 0) return true;
599
- // If both objects are empty we consider them equal
600
- if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
601
- // Otherwise match them by value
602
- if (a !== b) return false;
603
- }
604
- return true;
605
- }
606
- };
607
-
608
- // Collects nodes and materials from a THREE.Object3D
609
- function buildGraph(object) {
610
- const data = {
611
- nodes: {},
612
- materials: {}
613
- };
614
- if (object) {
615
- object.traverse(obj => {
616
- if (obj.name) data.nodes[obj.name] = obj;
617
- if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
618
- });
619
- }
620
- return data;
621
- }
622
- // Disposes an object and all its properties
623
- function dispose(obj) {
624
- if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
625
- for (const p in obj) {
626
- const prop = obj[p];
627
- if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
628
- }
629
- }
630
- const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
631
-
632
- // Gets only instance props from reconciler fibers
633
- function getInstanceProps(queue) {
634
- const props = {};
635
- for (const key in queue) {
636
- if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
637
- }
638
- return props;
639
- }
640
-
641
- // Each object in the scene carries a small LocalState descriptor
642
- function prepare(target, root, type, props) {
643
- const object = target;
644
-
645
- // Create instance descriptor
646
- let instance = object == null ? void 0 : object.__r3f;
647
- if (!instance) {
648
- instance = {
649
- root,
650
- type,
651
- parent: null,
652
- children: [],
653
- props: getInstanceProps(props),
654
- object,
655
- eventCount: 0,
656
- handlers: {},
657
- isHidden: false
658
- };
659
- if (object) {
660
- object.__r3f = instance;
661
- if (type) applyProps(object, instance.props);
662
- }
663
- }
664
- return instance;
665
- }
666
- function resolve(root, key) {
667
- var _target;
668
- let target = root[key];
669
- if (!key.includes('-')) return {
670
- root,
671
- key,
672
- target
673
- };
674
-
675
- // Resolve pierced target
676
- const chain = key.split('-');
677
- target = chain.reduce((acc, key) => acc[key], root);
678
- key = chain.pop();
679
-
680
- // Switch root if atomic
681
- if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
682
- return {
683
- root,
684
- key,
685
- target
686
- };
687
- }
688
-
689
- // Checks if a dash-cased string ends with an integer
690
- const INDEX_REGEX = /-\d+$/;
691
- function attach(parent, child) {
692
- if (is.str(child.props.attach)) {
693
- // If attaching into an array (foo-0), create one
694
- if (INDEX_REGEX.test(child.props.attach)) {
695
- const index = child.props.attach.replace(INDEX_REGEX, '');
696
- const {
697
- root,
698
- key
699
- } = resolve(parent.object, index);
700
- if (!Array.isArray(root[key])) root[key] = [];
701
- }
702
- const {
703
- root,
704
- key
705
- } = resolve(parent.object, child.props.attach);
706
- child.previousAttach = root[key];
707
- root[key] = child.object;
708
- } else if (is.fun(child.props.attach)) {
709
- child.previousAttach = child.props.attach(parent.object, child.object);
710
- }
711
- }
712
- function detach(parent, child) {
713
- if (is.str(child.props.attach)) {
714
- const {
715
- root,
716
- key
717
- } = resolve(parent.object, child.props.attach);
718
- const previous = child.previousAttach;
719
- // When the previous value was undefined, it means the value was never set to begin with
720
- if (previous === undefined) delete root[key];
721
- // Otherwise set the previous value
722
- else root[key] = previous;
723
- } else {
724
- child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
725
- }
726
- delete child.previousAttach;
727
- }
728
- const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
729
- // Instance props
730
- 'args', 'dispose', 'attach', 'object', 'onUpdate',
731
- // Behavior flags
732
- 'dispose'];
733
- const MEMOIZED_PROTOTYPES = new Map();
734
-
735
- // This function prepares a set of changes to be applied to the instance
736
- function diffProps(instance, newProps) {
737
- const changedProps = {};
738
-
739
- // Sort through props
740
- for (const prop in newProps) {
741
- // Skip reserved keys
742
- if (RESERVED_PROPS.includes(prop)) continue;
743
- // Skip if props match
744
- if (is.equ(newProps[prop], instance.props[prop])) continue;
745
-
746
- // Props changed, add them
747
- changedProps[prop] = newProps[prop];
748
-
749
- // Reset pierced props
750
- for (const other in newProps) {
751
- if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
752
- }
753
- }
754
-
755
- // Reset removed props for HMR
756
- for (const prop in instance.props) {
757
- if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
758
- const {
759
- root,
760
- key
761
- } = resolve(instance.object, prop);
762
-
763
- // https://github.com/mrdoob/three.js/issues/21209
764
- // HMR/fast-refresh relies on the ability to cancel out props, but threejs
765
- // has no means to do this. Hence we curate a small collection of value-classes
766
- // with their respective constructor/set arguments
767
- // For removed props, try to set default values, if possible
768
- if (root.constructor && root.constructor.length === 0) {
769
- // create a blank slate of the instance and copy the particular parameter.
770
- let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
771
- if (!ctor) {
772
- ctor = new root.constructor();
773
- MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
774
- }
775
- changedProps[key] = ctor[key];
776
- } else {
777
- // instance does not have constructor, just set it to 0
778
- changedProps[key] = 0;
779
- }
780
- }
781
- return changedProps;
782
- }
783
- typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';
784
-
785
- // const LinearEncoding = 3000
786
- const sRGBEncoding = 3001;
787
- const SRGBColorSpace = 'srgb';
788
- const LinearSRGBColorSpace = 'srgb-linear';
789
-
790
- // https://github.com/mrdoob/three.js/pull/27042
791
- // https://github.com/mrdoob/three.js/pull/22748
792
- const colorMaps = ['map', 'emissiveMap', 'sheenTintMap',
793
- // <r134
794
- 'sheenColorMap', 'specularTintMap',
795
- // <r134
796
- 'specularColorMap', 'envMap'];
797
- const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
798
-
799
- // This function applies a set of changes to the instance
800
- function applyProps(object, props) {
801
- const instance = object.__r3f;
802
- const rootState = instance && findInitialRoot(instance).getState();
803
- const prevHandlers = instance == null ? void 0 : instance.eventCount;
804
- for (const prop in props) {
805
- let value = props[prop];
806
-
807
- // Don't mutate reserved keys
808
- if (RESERVED_PROPS.includes(prop)) continue;
809
-
810
- // Deal with pointer events, including removing them if undefined
811
- if (instance && EVENT_REGEX.test(prop)) {
812
- if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
813
- instance.eventCount = Object.keys(instance.handlers).length;
814
- }
815
-
816
- // Ignore setting undefined props
817
- // https://github.com/pmndrs/react-three-fiber/issues/274
818
- if (value === undefined) continue;
819
- let {
820
- root,
821
- key,
822
- target
823
- } = resolve(object, prop);
824
-
825
- // Alias (output)encoding => (output)colorSpace (since r152)
826
- // https://github.com/pmndrs/react-three-fiber/pull/2829
827
- if (hasColorSpace(root)) {
828
- if (key === 'encoding') {
829
- key = 'colorSpace';
830
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
831
- } else if (key === 'outputEncoding') {
832
- key = 'outputColorSpace';
833
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
834
- }
396
+ // Layers must be written to the mask property
397
+ if (target instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) {
398
+ target.mask = value.mask;
399
+ } else if (target instanceof THREE__namespace.Color && isColorRepresentation(value)) {
400
+ target.set(value);
835
401
  }
836
-
837
- // Copy if properties match signatures
838
- if (typeof (target == null ? void 0 : target.copy) === 'function' && target.copy === value.copy) {
402
+ // Copy if properties match signatures and implement math interface (likely read-only)
403
+ else if (target && typeof target.set === 'function' && typeof target.copy === 'function' && value != null && value.constructor && target.constructor === value.constructor) {
839
404
  target.copy(value);
840
405
  }
841
- // Layers have no copy function, we must therefore copy the mask property
842
- else if (target instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) {
843
- target.mask = value.mask;
844
- }
845
406
  // Set array types
846
- else if (target != null && target.set && Array.isArray(value)) {
847
- if (target.fromArray) target.fromArray(value);else target.set(...value);
407
+ else if (target && typeof target.set === 'function' && Array.isArray(value)) {
408
+ if (typeof target.fromArray === 'function') target.fromArray(value);else target.set(...value);
848
409
  }
849
410
  // Set literal types
850
- else if (target != null && target.set && typeof value !== 'object') {
851
- const isColor = target instanceof THREE__namespace.Color;
411
+ else if (target && typeof target.set === 'function' && typeof value === 'number') {
852
412
  // Allow setting array scalars
853
- if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
413
+ if (typeof target.setScalar === 'function') target.setScalar(value);
854
414
  // Otherwise just set single value
855
415
  else target.set(value);
856
-
857
- // Emulate THREE.ColorManagement for older three.js versions
858
- // https://github.com/pmndrs/react-three-fiber/issues/344
859
- if (!getColorManagement() && !(rootState != null && rootState.linear) && isColor) target.convertSRGBToLinear();
860
416
  }
861
417
  // Else, just overwrite the value
862
418
  else {
419
+ var _root$key;
863
420
  root[key] = value;
864
421
 
865
422
  // Auto-convert sRGB texture parameters for built-in materials
866
423
  // https://github.com/pmndrs/react-three-fiber/issues/344
867
424
  // https://github.com/mrdoob/three.js/pull/25857
868
- if (rootState && !rootState.linear && colorMaps.includes(key) && root[key] instanceof THREE__namespace.Texture &&
425
+ if (rootState && !rootState.linear && colorMaps.includes(key) && (_root$key = root[key]) != null && _root$key.isTexture &&
869
426
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
870
427
  root[key].format === THREE__namespace.RGBAFormat && root[key].type === THREE__namespace.UnsignedByteType) {
871
428
  // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
872
- if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
429
+ root[key].colorSpace = THREE__namespace.SRGBColorSpace;
873
430
  }
874
431
  }
875
432
  }
876
433
 
877
434
  // Register event handlers
878
- if (instance != null && instance.parent && rootState != null && rootState.internal && instance.object instanceof THREE__namespace.Object3D && prevHandlers !== instance.eventCount) {
435
+ if (instance != null && instance.parent && rootState != null && rootState.internal && (_instance$object = instance.object) != null && _instance$object.isObject3D && prevHandlers !== instance.eventCount) {
436
+ const object = instance.object;
879
437
  // Pre-emptively remove the instance from the interaction manager
880
- const index = rootState.internal.interaction.indexOf(instance.object);
438
+ const index = rootState.internal.interaction.indexOf(object);
881
439
  if (index > -1) rootState.internal.interaction.splice(index, 1);
882
440
  // Add the instance to the interaction manager only when it has handlers
883
- if (instance.eventCount && instance.object.raycast !== null && instance.object instanceof THREE__namespace.Object3D) {
884
- rootState.internal.interaction.push(instance.object);
441
+ if (instance.eventCount && object.raycast !== null) {
442
+ rootState.internal.interaction.push(object);
885
443
  }
886
444
  }
887
445
 
888
446
  // Auto-attach geometries and materials
889
447
  if (instance && instance.props.attach === undefined) {
890
- if (instance.object instanceof THREE__namespace.BufferGeometry) instance.props.attach = 'geometry';else if (instance.object instanceof THREE__namespace.Material) instance.props.attach = 'material';
448
+ if (instance.object.isBufferGeometry) instance.props.attach = 'geometry';else if (instance.object.isMaterial) instance.props.attach = 'material';
891
449
  }
892
450
 
893
451
  // Instance was updated, request a frame
@@ -1059,7 +617,19 @@ function createEvents(store) {
1059
617
  stopped: false
1060
618
  };
1061
619
  for (const hit of intersections) {
1062
- const state = getRootState(hit.object);
620
+ let state = getRootState(hit.object);
621
+
622
+ // If the object is not managed by R3F, it might be parented to an element which is.
623
+ // Traverse upwards until we find a managed parent and use its state instead.
624
+ if (!state) {
625
+ hit.object.traverseAncestors(obj => {
626
+ const parentState = getRootState(obj);
627
+ if (parentState) {
628
+ state = parentState;
629
+ return false;
630
+ }
631
+ });
632
+ }
1063
633
  if (state) {
1064
634
  const {
1065
635
  raycaster,
@@ -1320,7 +890,7 @@ function createEvents(store) {
1320
890
  }
1321
891
 
1322
892
  const isRenderer = def => !!(def != null && def.render);
1323
- const context = /*#__PURE__*/React__namespace.createContext(null);
893
+ const context = /* @__PURE__ */React__namespace.createContext(null);
1324
894
  const createStore = (invalidate, advance) => {
1325
895
  const rootStore = traditional.createWithEqualityFn((set, get) => {
1326
896
  const position = new THREE__namespace.Vector3();
@@ -1334,7 +904,7 @@ const createStore = (invalidate, advance) => {
1334
904
  left
1335
905
  } = size;
1336
906
  const aspect = width / height;
1337
- if (target instanceof THREE__namespace.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
907
+ if (target.isVector3) tempTarget.copy(target);else tempTarget.set(...target);
1338
908
  const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1339
909
  if (isOrthographicCamera(camera)) {
1340
910
  return {
@@ -1536,161 +1106,576 @@ const createStore = (invalidate, advance) => {
1536
1106
  oldDpr = viewport.dpr;
1537
1107
  // Update camera & renderer
1538
1108
  updateCamera(camera, size);
1539
- gl.setPixelRatio(viewport.dpr);
1109
+ if (viewport.dpr > 0) gl.setPixelRatio(viewport.dpr);
1540
1110
  const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1541
1111
  gl.setSize(size.width, size.height, updateStyle);
1542
1112
  }
1543
1113
 
1544
- // Update viewport once the camera changes
1545
- if (camera !== oldCamera) {
1546
- oldCamera = camera;
1547
- // Update viewport
1548
- set(state => ({
1549
- viewport: {
1550
- ...state.viewport,
1551
- ...state.viewport.getCurrentViewport(camera)
1552
- }
1553
- }));
1114
+ // Update viewport once the camera changes
1115
+ if (camera !== oldCamera) {
1116
+ oldCamera = camera;
1117
+ // Update viewport
1118
+ set(state => ({
1119
+ viewport: {
1120
+ ...state.viewport,
1121
+ ...state.viewport.getCurrentViewport(camera)
1122
+ }
1123
+ }));
1124
+ }
1125
+ });
1126
+
1127
+ // Invalidate on any change
1128
+ rootStore.subscribe(state => invalidate(state));
1129
+
1130
+ // Return root state
1131
+ return rootStore;
1132
+ };
1133
+
1134
+ /**
1135
+ * Exposes an object's {@link Instance}.
1136
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
1137
+ *
1138
+ * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1139
+ */
1140
+ function useInstanceHandle(ref) {
1141
+ const instance = React__namespace.useRef(null);
1142
+ React__namespace.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1143
+ return instance;
1144
+ }
1145
+
1146
+ /**
1147
+ * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1148
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1149
+ */
1150
+ function useStore() {
1151
+ const store = React__namespace.useContext(context);
1152
+ if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1153
+ return store;
1154
+ }
1155
+
1156
+ /**
1157
+ * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1158
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1159
+ */
1160
+ function useThree(selector = state => state, equalityFn) {
1161
+ return useStore()(selector, equalityFn);
1162
+ }
1163
+
1164
+ /**
1165
+ * Executes a callback before render in a shared frame loop.
1166
+ * Can order effects with render priority or manually render with a positive priority.
1167
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1168
+ */
1169
+ function useFrame(callback, renderPriority = 0) {
1170
+ const store = useStore();
1171
+ const subscribe = store.getState().internal.subscribe;
1172
+ // Memoize ref
1173
+ const ref = useMutableCallback(callback);
1174
+ // Subscribe on mount, unsubscribe on unmount
1175
+ useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1176
+ return null;
1177
+ }
1178
+
1179
+ /**
1180
+ * Returns a node graph of an object with named nodes & materials.
1181
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1182
+ */
1183
+ function useGraph(object) {
1184
+ return React__namespace.useMemo(() => buildGraph(object), [object]);
1185
+ }
1186
+ const memoizedLoaders = new WeakMap();
1187
+ const isConstructor$1 = value => {
1188
+ var _value$prototype;
1189
+ return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1190
+ };
1191
+ function loadingFn(extensions, onProgress) {
1192
+ return function (Proto, ...input) {
1193
+ let loader;
1194
+
1195
+ // Construct and cache loader if constructor was passed
1196
+ if (isConstructor$1(Proto)) {
1197
+ loader = memoizedLoaders.get(Proto);
1198
+ if (!loader) {
1199
+ loader = new Proto();
1200
+ memoizedLoaders.set(Proto, loader);
1201
+ }
1202
+ } else {
1203
+ loader = Proto;
1204
+ }
1205
+
1206
+ // Apply loader extensions
1207
+ if (extensions) extensions(loader);
1208
+
1209
+ // Go through the urls and load them
1210
+ return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => {
1211
+ if (isObject3D(data == null ? void 0 : data.scene)) Object.assign(data, buildGraph(data.scene));
1212
+ res(data);
1213
+ }, onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
1214
+ };
1215
+ }
1216
+
1217
+ /**
1218
+ * Synchronously loads and caches assets with a three loader.
1219
+ *
1220
+ * Note: this hook's caller must be wrapped with `React.Suspense`
1221
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1222
+ */
1223
+ function useLoader(loader, input, extensions, onProgress) {
1224
+ // Use suspense to load async assets
1225
+ const keys = Array.isArray(input) ? input : [input];
1226
+ const results = suspendReact.suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1227
+ equal: is.equ
1228
+ });
1229
+ // Return the object(s)
1230
+ return Array.isArray(input) ? results : results[0];
1231
+ }
1232
+
1233
+ /**
1234
+ * Preloads an asset into cache as a side-effect.
1235
+ */
1236
+ useLoader.preload = function (loader, input, extensions) {
1237
+ const keys = Array.isArray(input) ? input : [input];
1238
+ return suspendReact.preload(loadingFn(extensions), [loader, ...keys]);
1239
+ };
1240
+
1241
+ /**
1242
+ * Removes a loaded asset from cache.
1243
+ */
1244
+ useLoader.clear = function (loader, input) {
1245
+ const keys = Array.isArray(input) ? input : [input];
1246
+ return suspendReact.clear([loader, ...keys]);
1247
+ };
1248
+
1249
+ // TODO: upstream to DefinitelyTyped for React 19
1250
+ // https://github.com/facebook/react/issues/28956
1251
+
1252
+ function createReconciler(config) {
1253
+ const reconciler = Reconciler__default["default"](config);
1254
+ reconciler.injectIntoDevTools({
1255
+ bundleType: typeof process !== 'undefined' && "production" !== 'production' ? 1 : 0,
1256
+ rendererPackageName: '@react-three/fiber',
1257
+ version: React__namespace.version
1258
+ });
1259
+ return reconciler;
1260
+ }
1261
+ const NoEventPriority = 0;
1262
+
1263
+ // TODO: handle constructor overloads
1264
+ // https://github.com/pmndrs/react-three-fiber/pull/2931
1265
+ // https://github.com/microsoft/TypeScript/issues/37079
1266
+
1267
+ const catalogue = {};
1268
+ const PREFIX_REGEX = /^three(?=[A-Z])/;
1269
+ const toPascalCase = type => `${type[0].toUpperCase()}${type.slice(1)}`;
1270
+ let i = 0;
1271
+ const isConstructor = object => typeof object === 'function';
1272
+ function extend(objects) {
1273
+ if (isConstructor(objects)) {
1274
+ const Component = `${i++}`;
1275
+ catalogue[Component] = objects;
1276
+ return Component;
1277
+ } else {
1278
+ Object.assign(catalogue, objects);
1279
+ }
1280
+ }
1281
+ function validateInstance(type, props) {
1282
+ // Get target from catalogue
1283
+ const name = `${type[0].toUpperCase()}${type.slice(1)}`;
1284
+ const target = catalogue[name];
1285
+
1286
+ // Validate element target
1287
+ if (type !== 'primitive' && !target) throw new Error(`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`);
1288
+
1289
+ // Validate primitives
1290
+ if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
1291
+
1292
+ // Throw if an object or literal was passed for args
1293
+ if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
1294
+ }
1295
+ function createInstance(type, props, root) {
1296
+ var _props$object;
1297
+ // Remove three* prefix from elements
1298
+ type = type.replace(PREFIX_REGEX, '');
1299
+ validateInstance(type, props);
1300
+
1301
+ // Regenerate the R3F instance for primitives to simulate a new object
1302
+ if (type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
1303
+ return prepare(props.object, root, type, props);
1304
+ }
1305
+ function hideInstance(instance) {
1306
+ if (!instance.isHidden) {
1307
+ var _instance$parent;
1308
+ if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
1309
+ detach(instance.parent, instance);
1310
+ } else if (isObject3D(instance.object)) {
1311
+ instance.object.visible = false;
1554
1312
  }
1555
- });
1313
+ instance.isHidden = true;
1314
+ invalidateInstance(instance);
1315
+ }
1316
+ }
1317
+ function unhideInstance(instance) {
1318
+ if (instance.isHidden) {
1319
+ var _instance$parent2;
1320
+ if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
1321
+ attach(instance.parent, instance);
1322
+ } else if (isObject3D(instance.object) && instance.props.visible !== false) {
1323
+ instance.object.visible = true;
1324
+ }
1325
+ instance.isHidden = false;
1326
+ invalidateInstance(instance);
1327
+ }
1328
+ }
1556
1329
 
1557
- // Invalidate on any change
1558
- rootStore.subscribe(state => invalidate(state));
1330
+ // https://github.com/facebook/react/issues/20271
1331
+ // This will make sure events and attach are only handled once when trees are complete
1332
+ function handleContainerEffects(parent, child, beforeChild) {
1333
+ // Bail if tree isn't mounted or parent is not a container.
1334
+ // This ensures that the tree is finalized and React won't discard results to Suspense
1335
+ const state = child.root.getState();
1336
+ if (!parent.parent && parent.object !== state.scene) return;
1559
1337
 
1560
- // Return root state
1561
- return rootStore;
1562
- };
1338
+ // Create & link object on first run
1339
+ if (!child.object) {
1340
+ var _child$props$object, _child$props$args;
1341
+ // Get target from catalogue
1342
+ const target = catalogue[toPascalCase(child.type)];
1563
1343
 
1564
- /**
1565
- * Exposes an object's {@link Instance}.
1566
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
1567
- *
1568
- * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1569
- */
1570
- function useInstanceHandle(ref) {
1571
- const instance = React__namespace.useRef(null);
1572
- React__namespace.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1573
- return instance;
1344
+ // Create object
1345
+ child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
1346
+ child.object.__r3f = child;
1347
+ }
1348
+
1349
+ // Set initial props
1350
+ applyProps(child.object, child.props);
1351
+
1352
+ // Append instance
1353
+ if (child.props.attach) {
1354
+ attach(parent, child);
1355
+ } else if (isObject3D(child.object) && isObject3D(parent.object)) {
1356
+ const childIndex = parent.object.children.indexOf(beforeChild == null ? void 0 : beforeChild.object);
1357
+ if (beforeChild && childIndex !== -1) {
1358
+ child.object.parent = parent.object;
1359
+ parent.object.children.splice(childIndex, 0, child.object);
1360
+ child.object.dispatchEvent({
1361
+ type: 'added'
1362
+ });
1363
+ parent.object.dispatchEvent({
1364
+ type: 'childadded',
1365
+ child: child.object
1366
+ });
1367
+ } else {
1368
+ parent.object.add(child.object);
1369
+ }
1370
+ }
1371
+
1372
+ // Link subtree
1373
+ for (const childInstance of child.children) handleContainerEffects(child, childInstance);
1374
+
1375
+ // Tree was updated, request a frame
1376
+ invalidateInstance(child);
1574
1377
  }
1378
+ function appendChild(parent, child) {
1379
+ if (!child) return;
1575
1380
 
1576
- /**
1577
- * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1578
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1579
- */
1580
- function useStore() {
1581
- const store = React__namespace.useContext(context);
1582
- if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1583
- return store;
1381
+ // Link instances
1382
+ child.parent = parent;
1383
+ parent.children.push(child);
1384
+
1385
+ // Attach tree once complete
1386
+ handleContainerEffects(parent, child);
1584
1387
  }
1388
+ function insertBefore(parent, child, beforeChild) {
1389
+ if (!child || !beforeChild) return;
1585
1390
 
1586
- /**
1587
- * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1588
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1589
- */
1590
- function useThree(selector = state => state, equalityFn) {
1591
- return useStore()(selector, equalityFn);
1391
+ // Link instances
1392
+ child.parent = parent;
1393
+ const childIndex = parent.children.indexOf(beforeChild);
1394
+ if (childIndex !== -1) parent.children.splice(childIndex, 0, child);else parent.children.push(child);
1395
+
1396
+ // Attach tree once complete
1397
+ handleContainerEffects(parent, child, beforeChild);
1592
1398
  }
1399
+ function disposeOnIdle(object) {
1400
+ if (typeof object.dispose === 'function') {
1401
+ const handleDispose = () => {
1402
+ try {
1403
+ object.dispose();
1404
+ } catch {
1405
+ // no-op
1406
+ }
1407
+ };
1593
1408
 
1594
- /**
1595
- * Executes a callback before render in a shared frame loop.
1596
- * Can order effects with render priority or manually render with a positive priority.
1597
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1598
- */
1599
- function useFrame(callback, renderPriority = 0) {
1600
- const store = useStore();
1601
- const subscribe = store.getState().internal.subscribe;
1602
- // Memoize ref
1603
- const ref = useMutableCallback(callback);
1604
- // Subscribe on mount, unsubscribe on unmount
1605
- useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1606
- return null;
1409
+ // In a testing environment, cleanup immediately
1410
+ if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose();
1411
+ // Otherwise, using a real GPU so schedule cleanup to prevent stalls
1412
+ else scheduler.unstable_scheduleCallback(scheduler.unstable_IdlePriority, handleDispose);
1413
+ }
1607
1414
  }
1415
+ function removeChild(parent, child, dispose) {
1416
+ if (!child) return;
1608
1417
 
1609
- /**
1610
- * Returns a node graph of an object with named nodes & materials.
1611
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1612
- */
1613
- function useGraph(object) {
1614
- return React__namespace.useMemo(() => buildGraph(object), [object]);
1418
+ // Unlink instances
1419
+ child.parent = null;
1420
+ const childIndex = parent.children.indexOf(child);
1421
+ if (childIndex !== -1) parent.children.splice(childIndex, 1);
1422
+
1423
+ // Eagerly tear down tree
1424
+ if (child.props.attach) {
1425
+ detach(parent, child);
1426
+ } else if (isObject3D(child.object) && isObject3D(parent.object)) {
1427
+ parent.object.remove(child.object);
1428
+ removeInteractivity(findInitialRoot(child), child.object);
1429
+ }
1430
+
1431
+ // Allow objects to bail out of unmount disposal with dispose={null}
1432
+ const shouldDispose = child.props.dispose !== null && dispose !== false;
1433
+
1434
+ // Recursively remove instance children
1435
+ for (let i = child.children.length - 1; i >= 0; i--) {
1436
+ const node = child.children[i];
1437
+ removeChild(child, node, shouldDispose);
1438
+ }
1439
+ child.children.length = 0;
1440
+
1441
+ // Unlink instance object
1442
+ delete child.object.__r3f;
1443
+
1444
+ // Dispose object whenever the reconciler feels like it.
1445
+ // Never dispose of primitives because their state may be kept outside of React!
1446
+ // In order for an object to be able to dispose it
1447
+ // - has a dispose method
1448
+ // - cannot be a <primitive object={...} />
1449
+ // - cannot be a THREE.Scene, because three has broken its own API
1450
+ if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {
1451
+ disposeOnIdle(child.object);
1452
+ }
1453
+
1454
+ // Tree was updated, request a frame for top-level instance
1455
+ if (dispose === undefined) invalidateInstance(child);
1615
1456
  }
1616
- const memoizedLoaders = new WeakMap();
1617
- const isConstructor = value => {
1618
- var _value$prototype;
1619
- return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1620
- };
1621
- function loadingFn(extensions, onProgress) {
1622
- return async function (Proto, ...input) {
1623
- let loader;
1457
+ function setFiberRef(fiber, publicInstance) {
1458
+ for (const _fiber of [fiber, fiber.alternate]) {
1459
+ if (_fiber !== null) {
1460
+ if (typeof _fiber.ref === 'function') {
1461
+ _fiber.refCleanup == null ? void 0 : _fiber.refCleanup();
1462
+ const cleanup = _fiber.ref(publicInstance);
1463
+ if (typeof cleanup === 'function') _fiber.refCleanup = cleanup;
1464
+ } else if (_fiber.ref) {
1465
+ _fiber.ref.current = publicInstance;
1466
+ }
1467
+ }
1468
+ }
1469
+ }
1470
+ const reconstructed = [];
1471
+ function swapInstances() {
1472
+ // Detach instance
1473
+ for (const [instance] of reconstructed) {
1474
+ const parent = instance.parent;
1475
+ if (parent) {
1476
+ if (instance.props.attach) {
1477
+ detach(parent, instance);
1478
+ } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
1479
+ parent.object.remove(instance.object);
1480
+ }
1481
+ for (const child of instance.children) {
1482
+ if (child.props.attach) {
1483
+ detach(instance, child);
1484
+ } else if (isObject3D(child.object) && isObject3D(instance.object)) {
1485
+ instance.object.remove(child.object);
1486
+ }
1487
+ }
1488
+ }
1489
+
1490
+ // If the old instance is hidden, we need to unhide it.
1491
+ // React assumes it can discard instances since they're pure for DOM.
1492
+ // This isn't true for us since our lifetimes are impure and longliving.
1493
+ // So, we manually check if an instance was hidden and unhide it.
1494
+ if (instance.isHidden) unhideInstance(instance);
1495
+
1496
+ // Dispose of old object if able
1497
+ if (instance.object.__r3f) delete instance.object.__r3f;
1498
+ if (instance.type !== 'primitive') disposeOnIdle(instance.object);
1499
+ }
1500
+
1501
+ // Update instance
1502
+ for (const [instance, props, fiber] of reconstructed) {
1503
+ instance.props = props;
1504
+ const parent = instance.parent;
1505
+ if (parent) {
1506
+ var _instance$props$objec, _instance$props$args;
1507
+ // Get target from catalogue
1508
+ const target = catalogue[toPascalCase(instance.type)];
1624
1509
 
1625
- // Construct and cache loader if constructor was passed
1626
- if (isConstructor(Proto)) {
1627
- loader = memoizedLoaders.get(Proto);
1628
- if (!loader) {
1629
- loader = new Proto();
1630
- memoizedLoaders.set(Proto, loader);
1510
+ // Create object
1511
+ instance.object = (_instance$props$objec = instance.props.object) != null ? _instance$props$objec : new target(...((_instance$props$args = instance.props.args) != null ? _instance$props$args : []));
1512
+ instance.object.__r3f = instance;
1513
+ setFiberRef(fiber, instance.object);
1514
+
1515
+ // Set initial props
1516
+ applyProps(instance.object, instance.props);
1517
+ if (instance.props.attach) {
1518
+ attach(parent, instance);
1519
+ } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
1520
+ parent.object.add(instance.object);
1631
1521
  }
1632
- } else {
1633
- loader = Proto;
1522
+ for (const child of instance.children) {
1523
+ if (child.props.attach) {
1524
+ attach(instance, child);
1525
+ } else if (isObject3D(child.object) && isObject3D(instance.object)) {
1526
+ instance.object.add(child.object);
1527
+ }
1528
+ }
1529
+
1530
+ // Tree was updated, request a frame
1531
+ invalidateInstance(instance);
1634
1532
  }
1533
+ }
1534
+ reconstructed.length = 0;
1535
+ }
1635
1536
 
1636
- // Apply loader extensions
1637
- if (extensions) extensions(loader);
1537
+ // Don't handle text instances, make it no-op
1538
+ const handleTextInstance = () => {};
1539
+ const NO_CONTEXT = {};
1540
+ let currentUpdatePriority = NoEventPriority;
1638
1541
 
1639
- // Go through the urls and load them
1640
- return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => res(isObject3D(data == null ? void 0 : data.scene) ? Object.assign(data, buildGraph(data.scene)) : data), onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
1641
- };
1642
- }
1542
+ // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js
1543
+ const NoFlags = 0;
1544
+ const Update = 4;
1545
+ const reconciler = /* @__PURE__ */createReconciler({
1546
+ isPrimaryRenderer: false,
1547
+ warnsIfNotActing: false,
1548
+ supportsMutation: true,
1549
+ supportsPersistence: false,
1550
+ supportsHydration: false,
1551
+ createInstance,
1552
+ removeChild,
1553
+ appendChild,
1554
+ appendInitialChild: appendChild,
1555
+ insertBefore,
1556
+ appendChildToContainer(container, child) {
1557
+ const scene = container.getState().scene.__r3f;
1558
+ if (!child || !scene) return;
1559
+ appendChild(scene, child);
1560
+ },
1561
+ removeChildFromContainer(container, child) {
1562
+ const scene = container.getState().scene.__r3f;
1563
+ if (!child || !scene) return;
1564
+ removeChild(scene, child);
1565
+ },
1566
+ insertInContainerBefore(container, child, beforeChild) {
1567
+ const scene = container.getState().scene.__r3f;
1568
+ if (!child || !beforeChild || !scene) return;
1569
+ insertBefore(scene, child, beforeChild);
1570
+ },
1571
+ getRootHostContext: () => NO_CONTEXT,
1572
+ getChildHostContext: () => NO_CONTEXT,
1573
+ commitUpdate(instance, type, oldProps, newProps, fiber) {
1574
+ var _newProps$args, _oldProps$args, _newProps$args2;
1575
+ validateInstance(type, newProps);
1576
+ let reconstruct = false;
1643
1577
 
1644
- /**
1645
- * Synchronously loads and caches assets with a three loader.
1646
- *
1647
- * Note: this hook's caller must be wrapped with `React.Suspense`
1648
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1649
- */
1650
- function useLoader(loader, input, extensions, onProgress) {
1651
- // Use suspense to load async assets
1652
- const keys = Array.isArray(input) ? input : [input];
1653
- const results = suspendReact.suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1654
- equal: is.equ
1655
- });
1656
- // Return the object(s)
1657
- return Array.isArray(input) ? results : results[0];
1658
- }
1578
+ // Reconstruct primitives if object prop changes
1579
+ if (instance.type === 'primitive' && oldProps.object !== newProps.object) reconstruct = true;
1580
+ // Reconstruct instance if args were added or removed
1581
+ else if (((_newProps$args = newProps.args) == null ? void 0 : _newProps$args.length) !== ((_oldProps$args = oldProps.args) == null ? void 0 : _oldProps$args.length)) reconstruct = true;
1582
+ // Reconstruct instance if args were changed
1583
+ else if ((_newProps$args2 = newProps.args) != null && _newProps$args2.some((value, index) => {
1584
+ var _oldProps$args2;
1585
+ return value !== ((_oldProps$args2 = oldProps.args) == null ? void 0 : _oldProps$args2[index]);
1586
+ })) reconstruct = true;
1659
1587
 
1660
- /**
1661
- * Preloads an asset into cache as a side-effect.
1662
- */
1663
- useLoader.preload = function (loader, input, extensions) {
1664
- const keys = Array.isArray(input) ? input : [input];
1665
- return suspendReact.preload(loadingFn(extensions), [loader, ...keys]);
1666
- };
1588
+ // Reconstruct when args or <primitive object={...} have changes
1589
+ if (reconstruct) {
1590
+ reconstructed.push([instance, {
1591
+ ...newProps
1592
+ }, fiber]);
1593
+ } else {
1594
+ // Create a diff-set, flag if there are any changes
1595
+ const changedProps = diffProps(instance, newProps);
1596
+ if (Object.keys(changedProps).length) {
1597
+ Object.assign(instance.props, changedProps);
1598
+ applyProps(instance.object, changedProps);
1599
+ }
1600
+ }
1667
1601
 
1668
- /**
1669
- * Removes a loaded asset from cache.
1670
- */
1671
- useLoader.clear = function (loader, input) {
1672
- const keys = Array.isArray(input) ? input : [input];
1673
- return suspendReact.clear([loader, ...keys]);
1674
- };
1602
+ // Flush reconstructed siblings when we hit the last updated child in a sequence
1603
+ const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
1604
+ if (isTailSibling) swapInstances();
1605
+ },
1606
+ finalizeInitialChildren: () => false,
1607
+ commitMount() {},
1608
+ getPublicInstance: instance => instance == null ? void 0 : instance.object,
1609
+ prepareForCommit: () => null,
1610
+ preparePortalMount: container => prepare(container.getState().scene, container, '', {}),
1611
+ resetAfterCommit: () => {},
1612
+ shouldSetTextContent: () => false,
1613
+ clearContainer: () => false,
1614
+ hideInstance,
1615
+ unhideInstance,
1616
+ createTextInstance: handleTextInstance,
1617
+ hideTextInstance: handleTextInstance,
1618
+ unhideTextInstance: handleTextInstance,
1619
+ scheduleTimeout: typeof setTimeout === 'function' ? setTimeout : undefined,
1620
+ cancelTimeout: typeof clearTimeout === 'function' ? clearTimeout : undefined,
1621
+ noTimeout: -1,
1622
+ getInstanceFromNode: () => null,
1623
+ beforeActiveInstanceBlur() {},
1624
+ afterActiveInstanceBlur() {},
1625
+ detachDeletedInstance() {},
1626
+ prepareScopeUpdate() {},
1627
+ getInstanceFromScope: () => null,
1628
+ shouldAttemptEagerTransition: () => false,
1629
+ trackSchedulerEvent: () => {},
1630
+ resolveEventType: () => null,
1631
+ resolveEventTimeStamp: () => -1.1,
1632
+ requestPostPaintCallback() {},
1633
+ maySuspendCommit: () => false,
1634
+ preloadInstance: () => true,
1635
+ // true indicates already loaded
1636
+ startSuspendingCommit() {},
1637
+ suspendInstance() {},
1638
+ waitForCommitToBeReady: () => null,
1639
+ NotPendingTransition: null,
1640
+ HostTransitionContext: /* @__PURE__ */React__namespace.createContext(null),
1641
+ setCurrentUpdatePriority(newPriority) {
1642
+ currentUpdatePriority = newPriority;
1643
+ },
1644
+ getCurrentUpdatePriority() {
1645
+ return currentUpdatePriority;
1646
+ },
1647
+ resolveUpdatePriority() {
1648
+ var _window$event;
1649
+ if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
1650
+ switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
1651
+ case 'click':
1652
+ case 'contextmenu':
1653
+ case 'dblclick':
1654
+ case 'pointercancel':
1655
+ case 'pointerdown':
1656
+ case 'pointerup':
1657
+ return constants.DiscreteEventPriority;
1658
+ case 'pointermove':
1659
+ case 'pointerout':
1660
+ case 'pointerover':
1661
+ case 'pointerenter':
1662
+ case 'pointerleave':
1663
+ case 'wheel':
1664
+ return constants.ContinuousEventPriority;
1665
+ default:
1666
+ return constants.DefaultEventPriority;
1667
+ }
1668
+ },
1669
+ resetFormInstance() {}
1670
+ });
1675
1671
 
1676
1672
  const _roots = new Map();
1677
1673
  const shallowLoose = {
1678
1674
  objects: 'shallow',
1679
1675
  strict: false
1680
1676
  };
1681
- const createRendererInstance = (gl, canvas) => {
1682
- const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1683
- if (isRenderer(customRenderer)) return customRenderer;
1684
- return new THREE__namespace.WebGLRenderer({
1685
- powerPreference: 'high-performance',
1686
- canvas: canvas,
1687
- antialias: true,
1688
- alpha: true,
1689
- ...gl
1690
- });
1691
- };
1692
1677
  function computeInitialSize(canvas, size) {
1693
- if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1678
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1694
1679
  const {
1695
1680
  width,
1696
1681
  height,
@@ -1766,10 +1751,13 @@ function createRoot(canvas) {
1766
1751
 
1767
1752
  // Locals
1768
1753
  let onCreated;
1769
- let configured = false;
1770
1754
  let lastCamera;
1755
+ let configured = false;
1756
+ let pending = null;
1771
1757
  return {
1772
- configure(props = {}) {
1758
+ async configure(props = {}) {
1759
+ let resolve;
1760
+ pending = new Promise(_resolve => resolve = _resolve);
1773
1761
  let {
1774
1762
  gl: glConfig,
1775
1763
  size: propsSize,
@@ -1792,9 +1780,26 @@ function createRoot(canvas) {
1792
1780
 
1793
1781
  // Set up renderer (one time only!)
1794
1782
  let gl = state.gl;
1795
- if (!state.gl) state.set({
1796
- gl: gl = createRendererInstance(glConfig, canvas)
1797
- });
1783
+ if (!state.gl) {
1784
+ const defaultProps = {
1785
+ canvas: canvas,
1786
+ powerPreference: 'high-performance',
1787
+ antialias: true,
1788
+ alpha: true
1789
+ };
1790
+ const customRenderer = typeof glConfig === 'function' ? await glConfig(defaultProps) : glConfig;
1791
+ if (isRenderer(customRenderer)) {
1792
+ gl = customRenderer;
1793
+ } else {
1794
+ gl = new THREE__namespace.WebGLRenderer({
1795
+ ...defaultProps,
1796
+ ...glConfig
1797
+ });
1798
+ }
1799
+ state.set({
1800
+ gl
1801
+ });
1802
+ }
1798
1803
 
1799
1804
  // Set up raycaster (one time only!)
1800
1805
  let raycaster = state.raycaster;
@@ -1820,7 +1825,7 @@ function createRoot(canvas) {
1820
1825
  // Create default camera, don't overwrite any user-set state
1821
1826
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
1822
1827
  lastCamera = cameraOptions;
1823
- const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1828
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1824
1829
  const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
1825
1830
  if (!isCamera) {
1826
1831
  camera.position.z = 5;
@@ -1850,7 +1855,7 @@ function createRoot(canvas) {
1850
1855
  // Set up scene (one time only!)
1851
1856
  if (!state.scene) {
1852
1857
  let scene;
1853
- if (sceneOptions instanceof THREE__namespace.Scene) {
1858
+ if (sceneOptions != null && sceneOptions.isScene) {
1854
1859
  scene = sceneOptions;
1855
1860
  prepare(scene, store, '', {});
1856
1861
  } else {
@@ -1863,8 +1868,34 @@ function createRoot(canvas) {
1863
1868
  });
1864
1869
  }
1865
1870
 
1871
+ // Store events internally
1872
+ if (events && !state.events.handlers) state.set({
1873
+ events: events(store)
1874
+ });
1875
+ // Check size, allow it to take on container bounds initially
1876
+ const size = computeInitialSize(canvas, propsSize);
1877
+ if (!is.equ(size, state.size, shallowLoose)) {
1878
+ state.setSize(size.width, size.height, size.top, size.left);
1879
+ }
1880
+ // Check pixelratio
1881
+ if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1882
+ // Check frameloop
1883
+ if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1884
+ // Check pointer missed
1885
+ if (!state.onPointerMissed) state.set({
1886
+ onPointerMissed
1887
+ });
1888
+ // Check performance
1889
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1890
+ performance: {
1891
+ ...state.performance,
1892
+ ...performance
1893
+ }
1894
+ }));
1895
+
1866
1896
  // Set up XR (one time only!)
1867
1897
  if (!state.xr) {
1898
+ var _gl$xr;
1868
1899
  // Handle frame behavior in WebXR
1869
1900
  const handleXRFrame = (timestamp, frame) => {
1870
1901
  const state = store.getState();
@@ -1895,7 +1926,7 @@ function createRoot(canvas) {
1895
1926
  };
1896
1927
 
1897
1928
  // Subscribe to WebXR session events
1898
- if (gl.xr) xr.connect();
1929
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1899
1930
  state.set({
1900
1931
  xr
1901
1932
  });
@@ -1922,22 +1953,12 @@ function createRoot(canvas) {
1922
1953
  }
1923
1954
  if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1924
1955
  }
1925
-
1926
- // Safely set color management if available.
1927
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1928
- const ColorManagement = getColorManagement();
1929
- if (ColorManagement) {
1930
- if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1931
- }
1956
+ THREE__namespace.ColorManagement.enabled = !legacy;
1932
1957
 
1933
1958
  // Set color space and tonemapping preferences
1934
1959
  if (!configured) {
1935
- const LinearEncoding = 3000;
1936
- const sRGBEncoding = 3001;
1937
- applyProps(gl, {
1938
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1939
- toneMapping: flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping
1940
- });
1960
+ gl.outputColorSpace = linear ? THREE__namespace.LinearSRGBColorSpace : THREE__namespace.SRGBColorSpace;
1961
+ gl.toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1941
1962
  }
1942
1963
 
1943
1964
  // Update color management state
@@ -1953,45 +1974,24 @@ function createRoot(canvas) {
1953
1974
 
1954
1975
  // Set gl props
1955
1976
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
1956
- // Store events internally
1957
- if (events && !state.events.handlers) state.set({
1958
- events: events(store)
1959
- });
1960
- // Check size, allow it to take on container bounds initially
1961
- const size = computeInitialSize(canvas, propsSize);
1962
- if (!is.equ(size, state.size, shallowLoose)) {
1963
- state.setSize(size.width, size.height, size.top, size.left);
1964
- }
1965
- // Check pixelratio
1966
- if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1967
- // Check frameloop
1968
- if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1969
- // Check pointer missed
1970
- if (!state.onPointerMissed) state.set({
1971
- onPointerMissed
1972
- });
1973
- // Check performance
1974
- if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1975
- performance: {
1976
- ...state.performance,
1977
- ...performance
1978
- }
1979
- }));
1980
1977
 
1981
1978
  // Set locals
1982
1979
  onCreated = onCreatedCallback;
1983
1980
  configured = true;
1981
+ resolve();
1984
1982
  return this;
1985
1983
  },
1986
1984
  render(children) {
1987
1985
  // The root has to be configured before it can be rendered
1988
- if (!configured) this.configure();
1989
- reconciler.updateContainer( /*#__PURE__*/jsxRuntime.jsx(Provider, {
1990
- store: store,
1991
- children: children,
1992
- onCreated: onCreated,
1993
- rootElement: canvas
1994
- }), fiber, null, () => undefined);
1986
+ if (!configured && !pending) this.configure();
1987
+ pending.then(() => {
1988
+ reconciler.updateContainer( /*#__PURE__*/jsxRuntime.jsx(Provider, {
1989
+ store: store,
1990
+ children: children,
1991
+ onCreated: onCreated,
1992
+ rootElement: canvas
1993
+ }), fiber, null, () => undefined);
1994
+ });
1995
1995
  return store;
1996
1996
  },
1997
1997
  unmount() {
@@ -1999,12 +1999,6 @@ function createRoot(canvas) {
1999
1999
  }
2000
2000
  };
2001
2001
  }
2002
- function render(children, canvas, config) {
2003
- console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
2004
- const root = createRoot(canvas);
2005
- root.configure(config);
2006
- return root.render(children);
2007
- }
2008
2002
  function Provider({
2009
2003
  store,
2010
2004
  children,
@@ -2142,18 +2136,17 @@ function Portal({
2142
2136
  return store;
2143
2137
  // eslint-disable-next-line react-hooks/exhaustive-deps
2144
2138
  }, [previousRoot, container]);
2145
- return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2146
- children: reconciler.createPortal( /*#__PURE__*/jsxRuntime.jsx(context.Provider, {
2147
- value: usePortalStore,
2148
- children: children
2149
- }), usePortalStore, null)
2150
- });
2139
+ return (
2140
+ /*#__PURE__*/
2141
+ // @ts-ignore, reconciler types are not maintained
2142
+ jsxRuntime.jsx(jsxRuntime.Fragment, {
2143
+ children: reconciler.createPortal( /*#__PURE__*/jsxRuntime.jsx(context.Provider, {
2144
+ value: usePortalStore,
2145
+ children: children
2146
+ }), usePortalStore, null)
2147
+ })
2148
+ );
2151
2149
  }
2152
- reconciler.injectIntoDevTools({
2153
- bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
2154
- rendererPackageName: '@react-three/fiber',
2155
- version: React__namespace.version
2156
- });
2157
2150
 
2158
2151
  function createSubs(callback, subs) {
2159
2152
  const sub = {
@@ -2252,7 +2245,7 @@ function loop(timestamp) {
2252
2245
  repeat += update(timestamp, state);
2253
2246
  }
2254
2247
  }
2255
- useFrameInProgress = true;
2248
+ useFrameInProgress = false;
2256
2249
 
2257
2250
  // Run after-effects
2258
2251
  flushGlobalEffects('after', timestamp);
@@ -2415,7 +2408,6 @@ exports.getRootState = getRootState;
2415
2408
  exports.invalidate = invalidate;
2416
2409
  exports.isRef = isRef;
2417
2410
  exports.reconciler = reconciler;
2418
- exports.render = render;
2419
2411
  exports.threeTypes = threeTypes;
2420
2412
  exports.unmountComponentAtNode = unmountComponentAtNode;
2421
2413
  exports.useBridge = useBridge;