@react-three/fiber 9.0.0-rc.1 → 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,852 +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: () => false,
410
- trackSchedulerEvent: () => {},
411
- resolveEventType: () => null,
412
- resolveEventTimeStamp: () => -1.1,
413
- requestPostPaintCallback() {},
414
- maySuspendCommit: () => false,
415
- preloadInstance: () => true,
416
- // true indicates already loaded
417
- startSuspendingCommit() {},
418
- suspendInstance() {},
419
- waitForCommitToBeReady: () => null,
420
- NotPendingTransition: null,
421
- setCurrentUpdatePriority(newPriority) {
422
- currentUpdatePriority = newPriority;
423
- },
424
- getCurrentUpdatePriority() {
425
- return currentUpdatePriority;
426
- },
427
- resolveUpdatePriority() {
428
- var _window$event;
429
- if (currentUpdatePriority !== constants.NoEventPriority) return currentUpdatePriority;
430
- switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
431
- case 'click':
432
- case 'contextmenu':
433
- case 'dblclick':
434
- case 'pointercancel':
435
- case 'pointerdown':
436
- case 'pointerup':
437
- return constants.DiscreteEventPriority;
438
- case 'pointermove':
439
- case 'pointerout':
440
- case 'pointerover':
441
- case 'pointerenter':
442
- case 'pointerleave':
443
- case 'wheel':
444
- return constants.ContinuousEventPriority;
445
- default:
446
- return constants.DefaultEventPriority;
447
- }
448
- },
449
- resetFormInstance() {}
450
- });
451
-
452
- var _window$document, _window$navigator;
453
- /**
454
- * Returns the instance's initial (outmost) root.
455
- */
456
- function findInitialRoot(instance) {
457
- let root = instance.root;
458
- while (root.getState().previousRoot) root = root.getState().previousRoot;
459
- return root;
460
- }
461
-
462
- /**
463
- * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
464
- */
465
- const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
466
- /**
467
- * The current THREE.ColorManagement instance, if present.
468
- */
469
- const getColorManagement = () => {
470
- var _ColorManagement;
471
- return (_ColorManagement = catalogue.ColorManagement) != null ? _ColorManagement : null;
472
- };
473
- /**
474
- * Safely flush async effects when testing, simulating a legacy root.
475
- */
476
- const act = React__namespace.act;
477
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
478
- const isRef = obj => obj && obj.hasOwnProperty('current');
479
-
480
- /**
481
- * An SSR-friendly useLayoutEffect.
482
- *
483
- * React currently throws a warning when using useLayoutEffect on the server.
484
- * To get around it, we can conditionally useEffect on the server (no-op) and
485
- * useLayoutEffect elsewhere.
486
- *
487
- * @see https://github.com/facebook/react/issues/14927
488
- */
489
- 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;
490
- function useMutableCallback(fn) {
491
- const ref = React__namespace.useRef(fn);
492
- useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
493
- return ref;
494
- }
495
- /**
496
- * Bridges renderer Context and StrictMode from a primary renderer.
497
- */
498
- function useBridge() {
499
- const fiber = itsFine.useFiber();
500
- const ContextBridge = itsFine.useContextBridge();
501
- return React__namespace.useMemo(() => ({
502
- children
503
- }) => {
504
- const strict = !!itsFine.traverseFiber(fiber, true, node => node.type === React__namespace.StrictMode);
505
- const Root = strict ? React__namespace.StrictMode : React__namespace.Fragment;
506
- return /*#__PURE__*/jsxRuntime.jsx(Root, {
507
- children: /*#__PURE__*/jsxRuntime.jsx(ContextBridge, {
508
- children: children
509
- })
510
- });
511
- }, [fiber, ContextBridge]);
512
- }
513
- function Block({
514
- set
515
- }) {
516
- useIsomorphicLayoutEffect(() => {
517
- set(new Promise(() => null));
518
- return () => set(false);
519
- }, [set]);
520
- return null;
521
- }
522
- class ErrorBoundary extends React__namespace.Component {
523
- constructor(...args) {
524
- super(...args);
525
- this.state = {
526
- error: false
527
- };
528
- }
529
- componentDidCatch(err) {
530
- this.props.set(err);
531
- }
532
- render() {
533
- return this.state.error ? null : this.props.children;
534
- }
535
- }
536
- ErrorBoundary.getDerivedStateFromError = () => ({
537
- error: true
538
- });
539
- function calculateDpr(dpr) {
540
- var _window$devicePixelRa;
541
- // Err on the side of progress by assuming 2x dpr if we can't detect it
542
- // This will happen in workers where window is defined but dpr isn't.
543
- const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
544
- return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
545
- }
546
-
547
- /**
548
- * Returns instance root state
549
- */
550
- function getRootState(obj) {
551
- var _r3f;
552
- return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
553
- }
554
- // A collection of compare functions
555
- const is = {
556
- obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
557
- fun: a => typeof a === 'function',
558
- str: a => typeof a === 'string',
559
- num: a => typeof a === 'number',
560
- boo: a => typeof a === 'boolean',
561
- und: a => a === void 0,
562
- arr: a => Array.isArray(a),
563
- equ(a, b, {
564
- arrays = 'shallow',
565
- objects = 'reference',
566
- strict = true
567
- } = {}) {
568
- // Wrong type or one of the two undefined, doesn't match
569
- if (typeof a !== typeof b || !!a !== !!b) return false;
570
- // Atomic, just compare a against b
571
- if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
572
- const isObj = is.obj(a);
573
- if (isObj && objects === 'reference') return a === b;
574
- const isArr = is.arr(a);
575
- if (isArr && arrays === 'reference') return a === b;
576
- // Array or Object, shallow compare first to see if it's a match
577
- if ((isArr || isObj) && a === b) return true;
578
- // Last resort, go through keys
579
- let i;
580
- // Check if a has all the keys of b
581
- for (i in a) if (!(i in b)) return false;
582
- // Check if values between keys match
583
- if (isObj && arrays === 'shallow' && objects === 'shallow') {
584
- for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
585
- strict,
586
- objects: 'reference'
587
- })) return false;
588
- } else {
589
- for (i in strict ? b : a) if (a[i] !== b[i]) return false;
590
- }
591
- // If i is undefined
592
- if (is.und(i)) {
593
- // If both arrays are empty we consider them equal
594
- if (isArr && a.length === 0 && b.length === 0) return true;
595
- // If both objects are empty we consider them equal
596
- if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
597
- // Otherwise match them by value
598
- if (a !== b) return false;
599
- }
600
- return true;
601
- }
602
- };
603
-
604
- // Collects nodes and materials from a THREE.Object3D
605
- function buildGraph(object) {
606
- const data = {
607
- nodes: {},
608
- materials: {}
609
- };
610
- if (object) {
611
- object.traverse(obj => {
612
- if (obj.name) data.nodes[obj.name] = obj;
613
- if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
614
- });
615
- }
616
- return data;
617
- }
618
- // Disposes an object and all its properties
619
- function dispose(obj) {
620
- if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
621
- for (const p in obj) {
622
- const prop = obj[p];
623
- if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
624
- }
625
- }
626
- const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
627
-
628
- // Gets only instance props from reconciler fibers
629
- function getInstanceProps(queue) {
630
- const props = {};
631
- for (const key in queue) {
632
- if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
633
- }
634
- return props;
635
- }
636
-
637
- // Each object in the scene carries a small LocalState descriptor
638
- function prepare(target, root, type, props) {
639
- const object = target;
640
-
641
- // Create instance descriptor
642
- let instance = object == null ? void 0 : object.__r3f;
643
- if (!instance) {
644
- instance = {
645
- root,
646
- type,
647
- parent: null,
648
- children: [],
649
- props: getInstanceProps(props),
650
- object,
651
- eventCount: 0,
652
- handlers: {},
653
- isHidden: false
654
- };
655
- if (object) {
656
- object.__r3f = instance;
657
- if (type) applyProps(object, instance.props);
658
- }
659
- }
660
- return instance;
661
- }
662
- function resolve(root, key) {
663
- var _target;
664
- let target = root[key];
665
- if (!key.includes('-')) return {
666
- root,
667
- key,
668
- target
669
- };
670
-
671
- // Resolve pierced target
672
- const chain = key.split('-');
673
- target = chain.reduce((acc, key) => acc[key], root);
674
- key = chain.pop();
675
-
676
- // Switch root if atomic
677
- if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
678
- return {
679
- root,
680
- key,
681
- target
682
- };
683
- }
684
-
685
- // Checks if a dash-cased string ends with an integer
686
- const INDEX_REGEX = /-\d+$/;
687
- function attach(parent, child) {
688
- if (is.str(child.props.attach)) {
689
- // If attaching into an array (foo-0), create one
690
- if (INDEX_REGEX.test(child.props.attach)) {
691
- const index = child.props.attach.replace(INDEX_REGEX, '');
692
- const {
693
- root,
694
- key
695
- } = resolve(parent.object, index);
696
- if (!Array.isArray(root[key])) root[key] = [];
697
- }
698
- const {
699
- root,
700
- key
701
- } = resolve(parent.object, child.props.attach);
702
- child.previousAttach = root[key];
703
- root[key] = child.object;
704
- } else if (is.fun(child.props.attach)) {
705
- child.previousAttach = child.props.attach(parent.object, child.object);
706
- }
707
- }
708
- function detach(parent, child) {
709
- if (is.str(child.props.attach)) {
710
- const {
711
- root,
712
- key
713
- } = resolve(parent.object, child.props.attach);
714
- const previous = child.previousAttach;
715
- // When the previous value was undefined, it means the value was never set to begin with
716
- if (previous === undefined) delete root[key];
717
- // Otherwise set the previous value
718
- else root[key] = previous;
719
- } else {
720
- child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
721
- }
722
- delete child.previousAttach;
723
- }
724
- const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
725
- // Instance props
726
- 'args', 'dispose', 'attach', 'object', 'onUpdate',
727
- // Behavior flags
728
- 'dispose'];
729
- const MEMOIZED_PROTOTYPES = new Map();
730
-
731
- // This function prepares a set of changes to be applied to the instance
732
- function diffProps(instance, newProps) {
733
- const changedProps = {};
734
-
735
- // Sort through props
736
- for (const prop in newProps) {
737
- // Skip reserved keys
738
- if (RESERVED_PROPS.includes(prop)) continue;
739
- // Skip if props match
740
- if (is.equ(newProps[prop], instance.props[prop])) continue;
741
-
742
- // Props changed, add them
743
- changedProps[prop] = newProps[prop];
744
-
745
- // Reset pierced props
746
- for (const other in newProps) {
747
- if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
748
- }
749
- }
750
-
751
- // Reset removed props for HMR
752
- for (const prop in instance.props) {
753
- if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
754
- const {
755
- root,
756
- key
757
- } = resolve(instance.object, prop);
758
-
759
- // https://github.com/mrdoob/three.js/issues/21209
760
- // HMR/fast-refresh relies on the ability to cancel out props, but threejs
761
- // has no means to do this. Hence we curate a small collection of value-classes
762
- // with their respective constructor/set arguments
763
- // For removed props, try to set default values, if possible
764
- if (root.constructor && root.constructor.length === 0) {
765
- // create a blank slate of the instance and copy the particular parameter.
766
- let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
767
- if (!ctor) {
768
- ctor = new root.constructor();
769
- MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
770
- }
771
- changedProps[key] = ctor[key];
772
- } else {
773
- // instance does not have constructor, just set it to 0
774
- changedProps[key] = 0;
775
- }
776
- }
777
- return changedProps;
778
- }
779
- typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';
780
-
781
- // const LinearEncoding = 3000
782
- const sRGBEncoding = 3001;
783
- const SRGBColorSpace = 'srgb';
784
- const LinearSRGBColorSpace = 'srgb-linear';
785
-
786
- // https://github.com/mrdoob/three.js/pull/27042
787
- // https://github.com/mrdoob/three.js/pull/22748
788
- const colorMaps = ['map', 'emissiveMap', 'sheenTintMap',
789
- // <r134
790
- 'sheenColorMap', 'specularTintMap',
791
- // <r134
792
- 'specularColorMap', 'envMap'];
793
- const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
794
-
795
- // This function applies a set of changes to the instance
796
- function applyProps(object, props) {
797
- const instance = object.__r3f;
798
- const rootState = instance && findInitialRoot(instance).getState();
799
- const prevHandlers = instance == null ? void 0 : instance.eventCount;
800
- for (const prop in props) {
801
- let value = props[prop];
802
-
803
- // Don't mutate reserved keys
804
- if (RESERVED_PROPS.includes(prop)) continue;
805
-
806
- // Deal with pointer events, including removing them if undefined
807
- if (instance && EVENT_REGEX.test(prop)) {
808
- if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
809
- instance.eventCount = Object.keys(instance.handlers).length;
810
- }
811
-
812
- // Ignore setting undefined props
813
- // https://github.com/pmndrs/react-three-fiber/issues/274
814
- if (value === undefined) continue;
815
- let {
816
- root,
817
- key,
818
- target
819
- } = resolve(object, prop);
820
-
821
- // Alias (output)encoding => (output)colorSpace (since r152)
822
- // https://github.com/pmndrs/react-three-fiber/pull/2829
823
- if (hasColorSpace(root)) {
824
- if (key === 'encoding') {
825
- key = 'colorSpace';
826
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
827
- } else if (key === 'outputEncoding') {
828
- key = 'outputColorSpace';
829
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
830
- }
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);
831
401
  }
832
-
833
- // Copy if properties match signatures
834
- 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) {
835
404
  target.copy(value);
836
405
  }
837
- // Layers have no copy function, we must therefore copy the mask property
838
- else if (target instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) {
839
- target.mask = value.mask;
840
- }
841
406
  // Set array types
842
- else if (target != null && target.set && Array.isArray(value)) {
843
- 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);
844
409
  }
845
410
  // Set literal types
846
- else if (target != null && target.set && typeof value !== 'object') {
847
- const isColor = target instanceof THREE__namespace.Color;
411
+ else if (target && typeof target.set === 'function' && typeof value === 'number') {
848
412
  // Allow setting array scalars
849
- if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
413
+ if (typeof target.setScalar === 'function') target.setScalar(value);
850
414
  // Otherwise just set single value
851
415
  else target.set(value);
852
-
853
- // Emulate THREE.ColorManagement for older three.js versions
854
- // https://github.com/pmndrs/react-three-fiber/issues/344
855
- if (!getColorManagement() && !(rootState != null && rootState.linear) && isColor) target.convertSRGBToLinear();
856
416
  }
857
417
  // Else, just overwrite the value
858
418
  else {
419
+ var _root$key;
859
420
  root[key] = value;
860
421
 
861
422
  // Auto-convert sRGB texture parameters for built-in materials
862
423
  // https://github.com/pmndrs/react-three-fiber/issues/344
863
424
  // https://github.com/mrdoob/three.js/pull/25857
864
- 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 &&
865
426
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
866
427
  root[key].format === THREE__namespace.RGBAFormat && root[key].type === THREE__namespace.UnsignedByteType) {
867
428
  // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
868
- if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
429
+ root[key].colorSpace = THREE__namespace.SRGBColorSpace;
869
430
  }
870
431
  }
871
432
  }
872
433
 
873
434
  // Register event handlers
874
- 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;
875
437
  // Pre-emptively remove the instance from the interaction manager
876
- const index = rootState.internal.interaction.indexOf(instance.object);
438
+ const index = rootState.internal.interaction.indexOf(object);
877
439
  if (index > -1) rootState.internal.interaction.splice(index, 1);
878
440
  // Add the instance to the interaction manager only when it has handlers
879
- if (instance.eventCount && instance.object.raycast !== null && instance.object instanceof THREE__namespace.Object3D) {
880
- rootState.internal.interaction.push(instance.object);
441
+ if (instance.eventCount && object.raycast !== null) {
442
+ rootState.internal.interaction.push(object);
881
443
  }
882
444
  }
883
445
 
884
446
  // Auto-attach geometries and materials
885
447
  if (instance && instance.props.attach === undefined) {
886
- 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';
887
449
  }
888
450
 
889
451
  // Instance was updated, request a frame
@@ -1055,7 +617,19 @@ function createEvents(store) {
1055
617
  stopped: false
1056
618
  };
1057
619
  for (const hit of intersections) {
1058
- 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
+ }
1059
633
  if (state) {
1060
634
  const {
1061
635
  raycaster,
@@ -1316,7 +890,7 @@ function createEvents(store) {
1316
890
  }
1317
891
 
1318
892
  const isRenderer = def => !!(def != null && def.render);
1319
- const context = /*#__PURE__*/React__namespace.createContext(null);
893
+ const context = /* @__PURE__ */React__namespace.createContext(null);
1320
894
  const createStore = (invalidate, advance) => {
1321
895
  const rootStore = traditional.createWithEqualityFn((set, get) => {
1322
896
  const position = new THREE__namespace.Vector3();
@@ -1330,7 +904,7 @@ const createStore = (invalidate, advance) => {
1330
904
  left
1331
905
  } = size;
1332
906
  const aspect = width / height;
1333
- if (target instanceof THREE__namespace.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
907
+ if (target.isVector3) tempTarget.copy(target);else tempTarget.set(...target);
1334
908
  const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1335
909
  if (isOrthographicCamera(camera)) {
1336
910
  return {
@@ -1532,161 +1106,576 @@ const createStore = (invalidate, advance) => {
1532
1106
  oldDpr = viewport.dpr;
1533
1107
  // Update camera & renderer
1534
1108
  updateCamera(camera, size);
1535
- gl.setPixelRatio(viewport.dpr);
1109
+ if (viewport.dpr > 0) gl.setPixelRatio(viewport.dpr);
1536
1110
  const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1537
1111
  gl.setSize(size.width, size.height, updateStyle);
1538
1112
  }
1539
-
1540
- // Update viewport once the camera changes
1541
- if (camera !== oldCamera) {
1542
- oldCamera = camera;
1543
- // Update viewport
1544
- set(state => ({
1545
- viewport: {
1546
- ...state.viewport,
1547
- ...state.viewport.getCurrentViewport(camera)
1548
- }
1549
- }));
1113
+
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;
1312
+ }
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;
1550
1324
  }
1551
- });
1325
+ instance.isHidden = false;
1326
+ invalidateInstance(instance);
1327
+ }
1328
+ }
1552
1329
 
1553
- // Invalidate on any change
1554
- 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;
1555
1337
 
1556
- // Return root state
1557
- return rootStore;
1558
- };
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)];
1559
1343
 
1560
- /**
1561
- * Exposes an object's {@link Instance}.
1562
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
1563
- *
1564
- * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1565
- */
1566
- function useInstanceHandle(ref) {
1567
- const instance = React__namespace.useRef(null);
1568
- React__namespace.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1569
- 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);
1570
1377
  }
1378
+ function appendChild(parent, child) {
1379
+ if (!child) return;
1571
1380
 
1572
- /**
1573
- * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1574
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1575
- */
1576
- function useStore() {
1577
- const store = React__namespace.useContext(context);
1578
- if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1579
- return store;
1381
+ // Link instances
1382
+ child.parent = parent;
1383
+ parent.children.push(child);
1384
+
1385
+ // Attach tree once complete
1386
+ handleContainerEffects(parent, child);
1580
1387
  }
1388
+ function insertBefore(parent, child, beforeChild) {
1389
+ if (!child || !beforeChild) return;
1581
1390
 
1582
- /**
1583
- * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1584
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1585
- */
1586
- function useThree(selector = state => state, equalityFn) {
1587
- 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);
1588
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
+ };
1589
1408
 
1590
- /**
1591
- * Executes a callback before render in a shared frame loop.
1592
- * Can order effects with render priority or manually render with a positive priority.
1593
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1594
- */
1595
- function useFrame(callback, renderPriority = 0) {
1596
- const store = useStore();
1597
- const subscribe = store.getState().internal.subscribe;
1598
- // Memoize ref
1599
- const ref = useMutableCallback(callback);
1600
- // Subscribe on mount, unsubscribe on unmount
1601
- useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1602
- 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
+ }
1603
1414
  }
1415
+ function removeChild(parent, child, dispose) {
1416
+ if (!child) return;
1604
1417
 
1605
- /**
1606
- * Returns a node graph of an object with named nodes & materials.
1607
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1608
- */
1609
- function useGraph(object) {
1610
- 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);
1611
1456
  }
1612
- const memoizedLoaders = new WeakMap();
1613
- const isConstructor = value => {
1614
- var _value$prototype;
1615
- return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1616
- };
1617
- function loadingFn(extensions, onProgress) {
1618
- return async function (Proto, ...input) {
1619
- 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
+ }
1620
1489
 
1621
- // Construct and cache loader if constructor was passed
1622
- if (isConstructor(Proto)) {
1623
- loader = memoizedLoaders.get(Proto);
1624
- if (!loader) {
1625
- loader = new Proto();
1626
- memoizedLoaders.set(Proto, loader);
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)];
1509
+
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);
1627
1521
  }
1628
- } else {
1629
- 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);
1630
1532
  }
1533
+ }
1534
+ reconstructed.length = 0;
1535
+ }
1631
1536
 
1632
- // Apply loader extensions
1633
- 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;
1634
1541
 
1635
- // Go through the urls and load them
1636
- 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}`))))));
1637
- };
1638
- }
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;
1639
1577
 
1640
- /**
1641
- * Synchronously loads and caches assets with a three loader.
1642
- *
1643
- * Note: this hook's caller must be wrapped with `React.Suspense`
1644
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1645
- */
1646
- function useLoader(loader, input, extensions, onProgress) {
1647
- // Use suspense to load async assets
1648
- const keys = Array.isArray(input) ? input : [input];
1649
- const results = suspendReact.suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1650
- equal: is.equ
1651
- });
1652
- // Return the object(s)
1653
- return Array.isArray(input) ? results : results[0];
1654
- }
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;
1655
1587
 
1656
- /**
1657
- * Preloads an asset into cache as a side-effect.
1658
- */
1659
- useLoader.preload = function (loader, input, extensions) {
1660
- const keys = Array.isArray(input) ? input : [input];
1661
- return suspendReact.preload(loadingFn(extensions), [loader, ...keys]);
1662
- };
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
+ }
1663
1601
 
1664
- /**
1665
- * Removes a loaded asset from cache.
1666
- */
1667
- useLoader.clear = function (loader, input) {
1668
- const keys = Array.isArray(input) ? input : [input];
1669
- return suspendReact.clear([loader, ...keys]);
1670
- };
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
+ });
1671
1671
 
1672
1672
  const _roots = new Map();
1673
1673
  const shallowLoose = {
1674
1674
  objects: 'shallow',
1675
1675
  strict: false
1676
1676
  };
1677
- const createRendererInstance = (gl, canvas) => {
1678
- const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1679
- if (isRenderer(customRenderer)) return customRenderer;
1680
- return new THREE__namespace.WebGLRenderer({
1681
- powerPreference: 'high-performance',
1682
- canvas: canvas,
1683
- antialias: true,
1684
- alpha: true,
1685
- ...gl
1686
- });
1687
- };
1688
1677
  function computeInitialSize(canvas, size) {
1689
- if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1678
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1690
1679
  const {
1691
1680
  width,
1692
1681
  height,
@@ -1762,10 +1751,13 @@ function createRoot(canvas) {
1762
1751
 
1763
1752
  // Locals
1764
1753
  let onCreated;
1765
- let configured = false;
1766
1754
  let lastCamera;
1755
+ let configured = false;
1756
+ let pending = null;
1767
1757
  return {
1768
- configure(props = {}) {
1758
+ async configure(props = {}) {
1759
+ let resolve;
1760
+ pending = new Promise(_resolve => resolve = _resolve);
1769
1761
  let {
1770
1762
  gl: glConfig,
1771
1763
  size: propsSize,
@@ -1788,9 +1780,26 @@ function createRoot(canvas) {
1788
1780
 
1789
1781
  // Set up renderer (one time only!)
1790
1782
  let gl = state.gl;
1791
- if (!state.gl) state.set({
1792
- gl: gl = createRendererInstance(glConfig, canvas)
1793
- });
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
+ }
1794
1803
 
1795
1804
  // Set up raycaster (one time only!)
1796
1805
  let raycaster = state.raycaster;
@@ -1816,7 +1825,7 @@ function createRoot(canvas) {
1816
1825
  // Create default camera, don't overwrite any user-set state
1817
1826
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
1818
1827
  lastCamera = cameraOptions;
1819
- const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1828
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1820
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);
1821
1830
  if (!isCamera) {
1822
1831
  camera.position.z = 5;
@@ -1846,7 +1855,7 @@ function createRoot(canvas) {
1846
1855
  // Set up scene (one time only!)
1847
1856
  if (!state.scene) {
1848
1857
  let scene;
1849
- if (sceneOptions instanceof THREE__namespace.Scene) {
1858
+ if (sceneOptions != null && sceneOptions.isScene) {
1850
1859
  scene = sceneOptions;
1851
1860
  prepare(scene, store, '', {});
1852
1861
  } else {
@@ -1859,8 +1868,34 @@ function createRoot(canvas) {
1859
1868
  });
1860
1869
  }
1861
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
+
1862
1896
  // Set up XR (one time only!)
1863
1897
  if (!state.xr) {
1898
+ var _gl$xr;
1864
1899
  // Handle frame behavior in WebXR
1865
1900
  const handleXRFrame = (timestamp, frame) => {
1866
1901
  const state = store.getState();
@@ -1891,7 +1926,7 @@ function createRoot(canvas) {
1891
1926
  };
1892
1927
 
1893
1928
  // Subscribe to WebXR session events
1894
- if (gl.xr) xr.connect();
1929
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1895
1930
  state.set({
1896
1931
  xr
1897
1932
  });
@@ -1918,22 +1953,12 @@ function createRoot(canvas) {
1918
1953
  }
1919
1954
  if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1920
1955
  }
1921
-
1922
- // Safely set color management if available.
1923
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1924
- const ColorManagement = getColorManagement();
1925
- if (ColorManagement) {
1926
- if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1927
- }
1956
+ THREE__namespace.ColorManagement.enabled = !legacy;
1928
1957
 
1929
1958
  // Set color space and tonemapping preferences
1930
1959
  if (!configured) {
1931
- const LinearEncoding = 3000;
1932
- const sRGBEncoding = 3001;
1933
- applyProps(gl, {
1934
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1935
- toneMapping: flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping
1936
- });
1960
+ gl.outputColorSpace = linear ? THREE__namespace.LinearSRGBColorSpace : THREE__namespace.SRGBColorSpace;
1961
+ gl.toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1937
1962
  }
1938
1963
 
1939
1964
  // Update color management state
@@ -1949,45 +1974,24 @@ function createRoot(canvas) {
1949
1974
 
1950
1975
  // Set gl props
1951
1976
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
1952
- // Store events internally
1953
- if (events && !state.events.handlers) state.set({
1954
- events: events(store)
1955
- });
1956
- // Check size, allow it to take on container bounds initially
1957
- const size = computeInitialSize(canvas, propsSize);
1958
- if (!is.equ(size, state.size, shallowLoose)) {
1959
- state.setSize(size.width, size.height, size.top, size.left);
1960
- }
1961
- // Check pixelratio
1962
- if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1963
- // Check frameloop
1964
- if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1965
- // Check pointer missed
1966
- if (!state.onPointerMissed) state.set({
1967
- onPointerMissed
1968
- });
1969
- // Check performance
1970
- if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1971
- performance: {
1972
- ...state.performance,
1973
- ...performance
1974
- }
1975
- }));
1976
1977
 
1977
1978
  // Set locals
1978
1979
  onCreated = onCreatedCallback;
1979
1980
  configured = true;
1981
+ resolve();
1980
1982
  return this;
1981
1983
  },
1982
1984
  render(children) {
1983
1985
  // The root has to be configured before it can be rendered
1984
- if (!configured) this.configure();
1985
- reconciler.updateContainer( /*#__PURE__*/jsxRuntime.jsx(Provider, {
1986
- store: store,
1987
- children: children,
1988
- onCreated: onCreated,
1989
- rootElement: canvas
1990
- }), 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
+ });
1991
1995
  return store;
1992
1996
  },
1993
1997
  unmount() {
@@ -1995,12 +1999,6 @@ function createRoot(canvas) {
1995
1999
  }
1996
2000
  };
1997
2001
  }
1998
- function render(children, canvas, config) {
1999
- console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
2000
- const root = createRoot(canvas);
2001
- root.configure(config);
2002
- return root.render(children);
2003
- }
2004
2002
  function Provider({
2005
2003
  store,
2006
2004
  children,
@@ -2138,18 +2136,17 @@ function Portal({
2138
2136
  return store;
2139
2137
  // eslint-disable-next-line react-hooks/exhaustive-deps
2140
2138
  }, [previousRoot, container]);
2141
- return /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
2142
- children: reconciler.createPortal( /*#__PURE__*/jsxRuntime.jsx(context.Provider, {
2143
- value: usePortalStore,
2144
- children: children
2145
- }), usePortalStore, null)
2146
- });
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
+ );
2147
2149
  }
2148
- reconciler.injectIntoDevTools({
2149
- bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
2150
- rendererPackageName: '@react-three/fiber',
2151
- version: React__namespace.version
2152
- });
2153
2150
 
2154
2151
  function createSubs(callback, subs) {
2155
2152
  const sub = {
@@ -2248,7 +2245,7 @@ function loop(timestamp) {
2248
2245
  repeat += update(timestamp, state);
2249
2246
  }
2250
2247
  }
2251
- useFrameInProgress = true;
2248
+ useFrameInProgress = false;
2252
2249
 
2253
2250
  // Run after-effects
2254
2251
  flushGlobalEffects('after', timestamp);
@@ -2411,7 +2408,6 @@ exports.getRootState = getRootState;
2411
2408
  exports.invalidate = invalidate;
2412
2409
  exports.isRef = isRef;
2413
2410
  exports.reconciler = reconciler;
2414
- exports.render = render;
2415
2411
  exports.threeTypes = threeTypes;
2416
2412
  exports.unmountComponentAtNode = unmountComponentAtNode;
2417
2413
  exports.useBridge = useBridge;