@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,855 +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
-
784
- // const LinearEncoding = 3000
785
- const sRGBEncoding = 3001;
786
- const SRGBColorSpace = 'srgb';
787
- const LinearSRGBColorSpace = 'srgb-linear';
788
-
789
- // https://github.com/mrdoob/three.js/pull/27042
790
- // https://github.com/mrdoob/three.js/pull/22748
791
- const colorMaps = ['map', 'emissiveMap', 'sheenTintMap',
792
- // <r134
793
- 'sheenColorMap', 'specularTintMap',
794
- // <r134
795
- 'specularColorMap', 'envMap'];
796
- const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
797
-
798
- // This function applies a set of changes to the instance
799
- function applyProps(object, props) {
800
- const instance = object.__r3f;
801
- const rootState = instance && findInitialRoot(instance).getState();
802
- const prevHandlers = instance == null ? void 0 : instance.eventCount;
803
- for (const prop in props) {
804
- let value = props[prop];
805
-
806
- // Don't mutate reserved keys
807
- if (RESERVED_PROPS.includes(prop)) continue;
808
-
809
- // Deal with pointer events, including removing them if undefined
810
- if (instance && EVENT_REGEX.test(prop)) {
811
- if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
812
- instance.eventCount = Object.keys(instance.handlers).length;
813
- }
814
-
815
- // Ignore setting undefined props
816
- // https://github.com/pmndrs/react-three-fiber/issues/274
817
- if (value === undefined) continue;
818
- let {
819
- root,
820
- key,
821
- target
822
- } = resolve(object, prop);
823
-
824
- // Alias (output)encoding => (output)colorSpace (since r152)
825
- // https://github.com/pmndrs/react-three-fiber/pull/2829
826
- if (hasColorSpace(root)) {
827
- if (key === 'encoding') {
828
- key = 'colorSpace';
829
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
830
- } else if (key === 'outputEncoding') {
831
- key = 'outputColorSpace';
832
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
833
- }
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);
834
401
  }
835
-
836
- // Copy if properties match signatures
837
- 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) {
838
404
  target.copy(value);
839
405
  }
840
- // Layers have no copy function, we must therefore copy the mask property
841
- else if (target instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) {
842
- target.mask = value.mask;
843
- }
844
406
  // Set array types
845
- else if (target != null && target.set && Array.isArray(value)) {
846
- 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);
847
409
  }
848
410
  // Set literal types
849
- else if (target != null && target.set && typeof value !== 'object') {
850
- const isColor = target instanceof THREE__namespace.Color;
411
+ else if (target && typeof target.set === 'function' && typeof value === 'number') {
851
412
  // Allow setting array scalars
852
- if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
413
+ if (typeof target.setScalar === 'function') target.setScalar(value);
853
414
  // Otherwise just set single value
854
415
  else target.set(value);
855
-
856
- // Emulate THREE.ColorManagement for older three.js versions
857
- // https://github.com/pmndrs/react-three-fiber/issues/344
858
- if (!getColorManagement() && !(rootState != null && rootState.linear) && isColor) target.convertSRGBToLinear();
859
416
  }
860
417
  // Else, just overwrite the value
861
418
  else {
419
+ var _root$key;
862
420
  root[key] = value;
863
421
 
864
422
  // Auto-convert sRGB texture parameters for built-in materials
865
423
  // https://github.com/pmndrs/react-three-fiber/issues/344
866
424
  // https://github.com/mrdoob/three.js/pull/25857
867
- 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 &&
868
426
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
869
427
  root[key].format === THREE__namespace.RGBAFormat && root[key].type === THREE__namespace.UnsignedByteType) {
870
428
  // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
871
- if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
429
+ root[key].colorSpace = THREE__namespace.SRGBColorSpace;
872
430
  }
873
431
  }
874
432
  }
875
433
 
876
434
  // Register event handlers
877
- 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;
878
437
  // Pre-emptively remove the instance from the interaction manager
879
- const index = rootState.internal.interaction.indexOf(instance.object);
438
+ const index = rootState.internal.interaction.indexOf(object);
880
439
  if (index > -1) rootState.internal.interaction.splice(index, 1);
881
440
  // Add the instance to the interaction manager only when it has handlers
882
- if (instance.eventCount && instance.object.raycast !== null && instance.object instanceof THREE__namespace.Object3D) {
883
- rootState.internal.interaction.push(instance.object);
441
+ if (instance.eventCount && object.raycast !== null) {
442
+ rootState.internal.interaction.push(object);
884
443
  }
885
444
  }
886
445
 
887
446
  // Auto-attach geometries and materials
888
447
  if (instance && instance.props.attach === undefined) {
889
- 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';
890
449
  }
891
450
 
892
451
  // Instance was updated, request a frame
@@ -1058,7 +617,19 @@ function createEvents(store) {
1058
617
  stopped: false
1059
618
  };
1060
619
  for (const hit of intersections) {
1061
- 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
+ }
1062
633
  if (state) {
1063
634
  const {
1064
635
  raycaster,
@@ -1319,7 +890,7 @@ function createEvents(store) {
1319
890
  }
1320
891
 
1321
892
  const isRenderer = def => !!(def != null && def.render);
1322
- const context = /*#__PURE__*/React__namespace.createContext(null);
893
+ const context = /* @__PURE__ */React__namespace.createContext(null);
1323
894
  const createStore = (invalidate, advance) => {
1324
895
  const rootStore = traditional.createWithEqualityFn((set, get) => {
1325
896
  const position = new THREE__namespace.Vector3();
@@ -1333,7 +904,7 @@ const createStore = (invalidate, advance) => {
1333
904
  left
1334
905
  } = size;
1335
906
  const aspect = width / height;
1336
- if (target instanceof THREE__namespace.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
907
+ if (target.isVector3) tempTarget.copy(target);else tempTarget.set(...target);
1337
908
  const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1338
909
  if (isOrthographicCamera(camera)) {
1339
910
  return {
@@ -1535,161 +1106,576 @@ const createStore = (invalidate, advance) => {
1535
1106
  oldDpr = viewport.dpr;
1536
1107
  // Update camera & renderer
1537
1108
  updateCamera(camera, size);
1538
- gl.setPixelRatio(viewport.dpr);
1109
+ if (viewport.dpr > 0) gl.setPixelRatio(viewport.dpr);
1539
1110
  const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1540
1111
  gl.setSize(size.width, size.height, updateStyle);
1541
1112
  }
1542
1113
 
1543
- // Update viewport once the camera changes
1544
- if (camera !== oldCamera) {
1545
- oldCamera = camera;
1546
- // Update viewport
1547
- set(state => ({
1548
- viewport: {
1549
- ...state.viewport,
1550
- ...state.viewport.getCurrentViewport(camera)
1551
- }
1552
- }));
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' && process.env.NODE_ENV !== '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;
1553
1312
  }
1554
- });
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
+ }
1555
1329
 
1556
- // Invalidate on any change
1557
- 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;
1558
1337
 
1559
- // Return root state
1560
- return rootStore;
1561
- };
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)];
1562
1343
 
1563
- /**
1564
- * Exposes an object's {@link Instance}.
1565
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
1566
- *
1567
- * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1568
- */
1569
- function useInstanceHandle(ref) {
1570
- const instance = React__namespace.useRef(null);
1571
- React__namespace.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1572
- 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);
1573
1377
  }
1378
+ function appendChild(parent, child) {
1379
+ if (!child) return;
1574
1380
 
1575
- /**
1576
- * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1577
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1578
- */
1579
- function useStore() {
1580
- const store = React__namespace.useContext(context);
1581
- if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1582
- return store;
1381
+ // Link instances
1382
+ child.parent = parent;
1383
+ parent.children.push(child);
1384
+
1385
+ // Attach tree once complete
1386
+ handleContainerEffects(parent, child);
1583
1387
  }
1388
+ function insertBefore(parent, child, beforeChild) {
1389
+ if (!child || !beforeChild) return;
1584
1390
 
1585
- /**
1586
- * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1587
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1588
- */
1589
- function useThree(selector = state => state, equalityFn) {
1590
- 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);
1591
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
+ };
1592
1408
 
1593
- /**
1594
- * Executes a callback before render in a shared frame loop.
1595
- * Can order effects with render priority or manually render with a positive priority.
1596
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1597
- */
1598
- function useFrame(callback, renderPriority = 0) {
1599
- const store = useStore();
1600
- const subscribe = store.getState().internal.subscribe;
1601
- // Memoize ref
1602
- const ref = useMutableCallback(callback);
1603
- // Subscribe on mount, unsubscribe on unmount
1604
- useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1605
- 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
+ }
1606
1414
  }
1415
+ function removeChild(parent, child, dispose) {
1416
+ if (!child) return;
1607
1417
 
1608
- /**
1609
- * Returns a node graph of an object with named nodes & materials.
1610
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1611
- */
1612
- function useGraph(object) {
1613
- 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);
1614
1456
  }
1615
- const memoizedLoaders = new WeakMap();
1616
- const isConstructor = value => {
1617
- var _value$prototype;
1618
- return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1619
- };
1620
- function loadingFn(extensions, onProgress) {
1621
- return async function (Proto, ...input) {
1622
- 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)];
1623
1509
 
1624
- // Construct and cache loader if constructor was passed
1625
- if (isConstructor(Proto)) {
1626
- loader = memoizedLoaders.get(Proto);
1627
- if (!loader) {
1628
- loader = new Proto();
1629
- 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);
1630
1521
  }
1631
- } else {
1632
- 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);
1633
1532
  }
1533
+ }
1534
+ reconstructed.length = 0;
1535
+ }
1634
1536
 
1635
- // Apply loader extensions
1636
- 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;
1637
1541
 
1638
- // Go through the urls and load them
1639
- 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}`))))));
1640
- };
1641
- }
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;
1642
1577
 
1643
- /**
1644
- * Synchronously loads and caches assets with a three loader.
1645
- *
1646
- * Note: this hook's caller must be wrapped with `React.Suspense`
1647
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1648
- */
1649
- function useLoader(loader, input, extensions, onProgress) {
1650
- // Use suspense to load async assets
1651
- const keys = Array.isArray(input) ? input : [input];
1652
- const results = suspendReact.suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1653
- equal: is.equ
1654
- });
1655
- // Return the object(s)
1656
- return Array.isArray(input) ? results : results[0];
1657
- }
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;
1658
1587
 
1659
- /**
1660
- * Preloads an asset into cache as a side-effect.
1661
- */
1662
- useLoader.preload = function (loader, input, extensions) {
1663
- const keys = Array.isArray(input) ? input : [input];
1664
- return suspendReact.preload(loadingFn(extensions), [loader, ...keys]);
1665
- };
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
+ }
1666
1601
 
1667
- /**
1668
- * Removes a loaded asset from cache.
1669
- */
1670
- useLoader.clear = function (loader, input) {
1671
- const keys = Array.isArray(input) ? input : [input];
1672
- return suspendReact.clear([loader, ...keys]);
1673
- };
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
+ });
1674
1671
 
1675
1672
  const _roots = new Map();
1676
1673
  const shallowLoose = {
1677
1674
  objects: 'shallow',
1678
1675
  strict: false
1679
1676
  };
1680
- const createRendererInstance = (gl, canvas) => {
1681
- const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1682
- if (isRenderer(customRenderer)) return customRenderer;
1683
- return new THREE__namespace.WebGLRenderer({
1684
- powerPreference: 'high-performance',
1685
- canvas: canvas,
1686
- antialias: true,
1687
- alpha: true,
1688
- ...gl
1689
- });
1690
- };
1691
1677
  function computeInitialSize(canvas, size) {
1692
- if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1678
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1693
1679
  const {
1694
1680
  width,
1695
1681
  height,
@@ -1765,10 +1751,13 @@ function createRoot(canvas) {
1765
1751
 
1766
1752
  // Locals
1767
1753
  let onCreated;
1768
- let configured = false;
1769
1754
  let lastCamera;
1755
+ let configured = false;
1756
+ let pending = null;
1770
1757
  return {
1771
- configure(props = {}) {
1758
+ async configure(props = {}) {
1759
+ let resolve;
1760
+ pending = new Promise(_resolve => resolve = _resolve);
1772
1761
  let {
1773
1762
  gl: glConfig,
1774
1763
  size: propsSize,
@@ -1791,9 +1780,26 @@ function createRoot(canvas) {
1791
1780
 
1792
1781
  // Set up renderer (one time only!)
1793
1782
  let gl = state.gl;
1794
- if (!state.gl) state.set({
1795
- gl: gl = createRendererInstance(glConfig, canvas)
1796
- });
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
+ }
1797
1803
 
1798
1804
  // Set up raycaster (one time only!)
1799
1805
  let raycaster = state.raycaster;
@@ -1819,7 +1825,7 @@ function createRoot(canvas) {
1819
1825
  // Create default camera, don't overwrite any user-set state
1820
1826
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
1821
1827
  lastCamera = cameraOptions;
1822
- const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1828
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1823
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);
1824
1830
  if (!isCamera) {
1825
1831
  camera.position.z = 5;
@@ -1849,7 +1855,7 @@ function createRoot(canvas) {
1849
1855
  // Set up scene (one time only!)
1850
1856
  if (!state.scene) {
1851
1857
  let scene;
1852
- if (sceneOptions instanceof THREE__namespace.Scene) {
1858
+ if (sceneOptions != null && sceneOptions.isScene) {
1853
1859
  scene = sceneOptions;
1854
1860
  prepare(scene, store, '', {});
1855
1861
  } else {
@@ -1862,8 +1868,34 @@ function createRoot(canvas) {
1862
1868
  });
1863
1869
  }
1864
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
+
1865
1896
  // Set up XR (one time only!)
1866
1897
  if (!state.xr) {
1898
+ var _gl$xr;
1867
1899
  // Handle frame behavior in WebXR
1868
1900
  const handleXRFrame = (timestamp, frame) => {
1869
1901
  const state = store.getState();
@@ -1894,7 +1926,7 @@ function createRoot(canvas) {
1894
1926
  };
1895
1927
 
1896
1928
  // Subscribe to WebXR session events
1897
- if (gl.xr) xr.connect();
1929
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1898
1930
  state.set({
1899
1931
  xr
1900
1932
  });
@@ -1921,22 +1953,12 @@ function createRoot(canvas) {
1921
1953
  }
1922
1954
  if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1923
1955
  }
1924
-
1925
- // Safely set color management if available.
1926
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1927
- const ColorManagement = getColorManagement();
1928
- if (ColorManagement) {
1929
- if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1930
- }
1956
+ THREE__namespace.ColorManagement.enabled = !legacy;
1931
1957
 
1932
1958
  // Set color space and tonemapping preferences
1933
1959
  if (!configured) {
1934
- const LinearEncoding = 3000;
1935
- const sRGBEncoding = 3001;
1936
- applyProps(gl, {
1937
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1938
- toneMapping: flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping
1939
- });
1960
+ gl.outputColorSpace = linear ? THREE__namespace.LinearSRGBColorSpace : THREE__namespace.SRGBColorSpace;
1961
+ gl.toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1940
1962
  }
1941
1963
 
1942
1964
  // Update color management state
@@ -1952,45 +1974,24 @@ function createRoot(canvas) {
1952
1974
 
1953
1975
  // Set gl props
1954
1976
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
1955
- // Store events internally
1956
- if (events && !state.events.handlers) state.set({
1957
- events: events(store)
1958
- });
1959
- // Check size, allow it to take on container bounds initially
1960
- const size = computeInitialSize(canvas, propsSize);
1961
- if (!is.equ(size, state.size, shallowLoose)) {
1962
- state.setSize(size.width, size.height, size.top, size.left);
1963
- }
1964
- // Check pixelratio
1965
- if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1966
- // Check frameloop
1967
- if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1968
- // Check pointer missed
1969
- if (!state.onPointerMissed) state.set({
1970
- onPointerMissed
1971
- });
1972
- // Check performance
1973
- if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1974
- performance: {
1975
- ...state.performance,
1976
- ...performance
1977
- }
1978
- }));
1979
1977
 
1980
1978
  // Set locals
1981
1979
  onCreated = onCreatedCallback;
1982
1980
  configured = true;
1981
+ resolve();
1983
1982
  return this;
1984
1983
  },
1985
1984
  render(children) {
1986
1985
  // The root has to be configured before it can be rendered
1987
- if (!configured) this.configure();
1988
- reconciler.updateContainer( /*#__PURE__*/jsxRuntime.jsx(Provider, {
1989
- store: store,
1990
- children: children,
1991
- onCreated: onCreated,
1992
- rootElement: canvas
1993
- }), 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
+ });
1994
1995
  return store;
1995
1996
  },
1996
1997
  unmount() {
@@ -1998,12 +1999,6 @@ function createRoot(canvas) {
1998
1999
  }
1999
2000
  };
2000
2001
  }
2001
- function render(children, canvas, config) {
2002
- console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
2003
- const root = createRoot(canvas);
2004
- root.configure(config);
2005
- return root.render(children);
2006
- }
2007
2002
  function Provider({
2008
2003
  store,
2009
2004
  children,
@@ -2141,18 +2136,17 @@ function Portal({
2141
2136
  return store;
2142
2137
  // eslint-disable-next-line react-hooks/exhaustive-deps
2143
2138
  }, [previousRoot, container]);
2144
- return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2145
- children: reconciler.createPortal( /*#__PURE__*/jsxRuntime.jsx(context.Provider, {
2146
- value: usePortalStore,
2147
- children: children
2148
- }), usePortalStore, null)
2149
- });
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
+ );
2150
2149
  }
2151
- reconciler.injectIntoDevTools({
2152
- bundleType: 0 ,
2153
- rendererPackageName: '@react-three/fiber',
2154
- version: React__namespace.version
2155
- });
2156
2150
 
2157
2151
  function createSubs(callback, subs) {
2158
2152
  const sub = {
@@ -2251,7 +2245,7 @@ function loop(timestamp) {
2251
2245
  repeat += update(timestamp, state);
2252
2246
  }
2253
2247
  }
2254
- useFrameInProgress = true;
2248
+ useFrameInProgress = false;
2255
2249
 
2256
2250
  // Run after-effects
2257
2251
  flushGlobalEffects('after', timestamp);
@@ -2414,7 +2408,6 @@ exports.getRootState = getRootState;
2414
2408
  exports.invalidate = invalidate;
2415
2409
  exports.isRef = isRef;
2416
2410
  exports.reconciler = reconciler;
2417
- exports.render = render;
2418
2411
  exports.threeTypes = threeTypes;
2419
2412
  exports.unmountComponentAtNode = unmountComponentAtNode;
2420
2413
  exports.useBridge = useBridge;