@react-three/fiber 10.0.0-canary.f81281f → 10.0.0-canary.fbc659b

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.
Files changed (39) hide show
  1. package/dist/extension.cjs +8 -7
  2. package/dist/extension.d.cts +10 -47
  3. package/dist/extension.d.mts +10 -47
  4. package/dist/extension.d.ts +10 -47
  5. package/dist/extension.mjs +1 -1
  6. package/dist/index.cjs +14 -15
  7. package/dist/index.d.cts +13 -13
  8. package/dist/index.d.mts +13 -13
  9. package/dist/index.d.ts +13 -13
  10. package/dist/index.mjs +4 -5
  11. package/dist/legacy.cjs +37 -15
  12. package/dist/legacy.d.cts +10 -7
  13. package/dist/legacy.d.mts +10 -7
  14. package/dist/legacy.d.ts +10 -7
  15. package/dist/legacy.mjs +13 -5
  16. package/dist/shared/fiber.BdIbKbh7.mjs +16563 -0
  17. package/dist/shared/{fiber.C_v1rkNg.mjs → fiber.Biz9olEe.mjs} +214 -62
  18. package/dist/shared/fiber.C6hitYDY.d.cts +48 -0
  19. package/dist/shared/{fiber.Bw8YnGg5.d.cts → fiber.C_MFx7We.d.cts} +555 -19
  20. package/dist/shared/{fiber.Bw8YnGg5.d.mts → fiber.C_MFx7We.d.mts} +555 -19
  21. package/dist/shared/{fiber.Bw8YnGg5.d.ts → fiber.C_MFx7We.d.ts} +555 -19
  22. package/dist/shared/{fiber.DAg-9ieU.d.ts → fiber.CtivZ3IY.d.ts} +190 -548
  23. package/dist/shared/{fiber.Ddgoyqql.d.mts → fiber.CvlK0a1Z.d.mts} +190 -548
  24. package/dist/shared/fiber.D4fD6H74.d.mts +48 -0
  25. package/dist/shared/{fiber.B6zNSmwj.cjs → fiber.DfiCufha.cjs} +222 -60
  26. package/dist/shared/{fiber.DiPB625B.d.cts → fiber.VTTQNnlj.d.cts} +190 -548
  27. package/dist/shared/fiber.YgTUGbja.d.ts +48 -0
  28. package/dist/shared/fiber.nDnBf1np.cjs +16661 -0
  29. package/dist/webgpu/index.cjs +13 -13
  30. package/dist/webgpu/index.d.cts +12 -7
  31. package/dist/webgpu/index.d.mts +12 -7
  32. package/dist/webgpu/index.d.ts +12 -7
  33. package/dist/webgpu/index.mjs +4 -4
  34. package/package.json +8 -8
  35. package/readme.md +1 -1
  36. package/dist/chunks/webgl.cjs +0 -27
  37. package/dist/chunks/webgl.mjs +0 -11
  38. package/dist/shared/fiber.BGL2rpse.cjs +0 -15129
  39. package/dist/shared/fiber.Ch4JrXS2.mjs +0 -15031
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { useContext } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
- import { useFiber, useContextBridge, traverseFiber } from 'its-fine';
4
+ import { useFiber, useContextBridge, useActivityBridge as useActivityBridge$1, traverseFiber } from 'its-fine';
5
5
  import { getScheduler } from '@pmndrs/scheduler';
6
6
 
7
7
  var __defProp = Object.defineProperty;
@@ -13,16 +13,18 @@ function useMutableCallback(fn) {
13
13
  useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
14
14
  return ref;
15
15
  }
16
+ const useActivityBridge = React["Activity"] ? useActivityBridge$1 : () => React.Fragment;
16
17
  function useBridge() {
17
18
  const fiber = useFiber();
18
19
  const ContextBridge = useContextBridge();
20
+ const ActivityBridge = useActivityBridge();
19
21
  return React.useMemo(
20
22
  () => ({ children }) => {
21
23
  const strict = !!traverseFiber(fiber, true, (node) => node.type === React.StrictMode);
22
24
  const Root = strict ? React.StrictMode : React.Fragment;
23
- return /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsx(ContextBridge, { children }) });
25
+ return /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsx(ActivityBridge, { children: /* @__PURE__ */ jsx(ContextBridge, { children }) }) });
24
26
  },
25
- [fiber, ContextBridge]
27
+ [fiber, ContextBridge, ActivityBridge]
26
28
  );
27
29
  }
28
30
  function Block({ set }) {
@@ -47,6 +49,23 @@ const ErrorBoundary = /* @__PURE__ */ (() => {
47
49
  }
48
50
  }, __publicField(_a, "getDerivedStateFromError", () => ({ error: true })), _a;
49
51
  })();
52
+ const noop = () => {
53
+ };
54
+ function Gate({ promise, onSettled }) {
55
+ React.use(promise);
56
+ useIsomorphicLayoutEffect(onSettled, [onSettled]);
57
+ return null;
58
+ }
59
+ function useGate() {
60
+ const [promise, setPromise] = React.useState(null);
61
+ const onSettled = React.useCallback(() => setPromise(null), []);
62
+ const waitFor = React.useCallback(
63
+ (next) => setPromise((current) => current ?? Promise.resolve(next).then(noop, noop)),
64
+ []
65
+ );
66
+ const gate = promise ? /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Gate, { promise, onSettled }) }) : null;
67
+ return [gate, waitFor];
68
+ }
50
69
 
51
70
  const shownNotices = /* @__PURE__ */ new Set();
52
71
  function notifyDepreciated({ heading, body, link }) {
@@ -104,10 +123,160 @@ function notifyAlpha({ message, link }) {
104
123
  console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
105
124
  }
106
125
  }
126
+ function isDevelopment() {
127
+ try {
128
+ return process.env.NODE_ENV !== "production";
129
+ } catch {
130
+ return true;
131
+ }
132
+ }
133
+
134
+ const INTERNAL_LIB_MARKERS = [
135
+ "zustand",
136
+ "setState",
137
+ "Object.assign",
138
+ "@pmndrs/scheduler",
139
+ "Scheduler.tickRoot",
140
+ "Scheduler.stepJob"
141
+ ];
142
+ function isInternalRendererAccess(stack, callerDepth = 2) {
143
+ if (!stack) return false;
144
+ const caller = stack.split("\n")[callerDepth] ?? "";
145
+ return INTERNAL_LIB_MARKERS.some((m) => caller.includes(m));
146
+ }
147
+
148
+ const isAccessorDescriptor = (descriptor) => !!descriptor && (descriptor.get !== void 0 || descriptor.set !== void 0);
149
+ const isAccessor = (object, key) => isAccessorDescriptor(Object.getOwnPropertyDescriptor(object, key));
150
+ function cloneState(...sources) {
151
+ const target = {};
152
+ for (const source of sources) {
153
+ if (source) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
154
+ }
155
+ return target;
156
+ }
157
+ function preserveAccessors(rawSet, get) {
158
+ return ((partial, replace) => {
159
+ const previous = get();
160
+ const patch = typeof partial === "function" ? partial(previous) : partial;
161
+ if (replace || !previous || patch == null || Object.is(patch, previous)) {
162
+ return rawSet(patch, replace);
163
+ }
164
+ const next = cloneState(previous);
165
+ const viaSetter = [];
166
+ for (const key of Reflect.ownKeys(patch)) {
167
+ const descriptor = Object.getOwnPropertyDescriptor(patch, key);
168
+ if (!descriptor.enumerable) continue;
169
+ if (isAccessorDescriptor(descriptor)) {
170
+ Object.defineProperty(next, key, descriptor);
171
+ } else if (isAccessor(next, key)) {
172
+ viaSetter.push(key);
173
+ } else {
174
+ Object.defineProperty(next, key, {
175
+ value: descriptor.value,
176
+ writable: true,
177
+ enumerable: true,
178
+ configurable: true
179
+ });
180
+ }
181
+ }
182
+ for (const key of viaSetter) next[key] = patch[key];
183
+ rawSet(next, true);
184
+ });
185
+ }
186
+ const keepAccessors = (initializer) => (rawSet, get, api) => {
187
+ const set = preserveAccessors(rawSet, get);
188
+ api.setState = set;
189
+ return initializer(set, get, api);
190
+ };
191
+ let glNoticeShown = false;
192
+ function defineRendererAccessors(state) {
193
+ Object.defineProperty(state, "gl", {
194
+ get() {
195
+ const renderer = this.internal?.actualRenderer;
196
+ if (!glNoticeShown && renderer && !this.isLegacy) {
197
+ const stack = new Error().stack || "";
198
+ if (!isInternalRendererAccess(stack)) {
199
+ glNoticeShown = true;
200
+ const cleanedStack = stack.split("\n").slice(2).join("\n") || "Stack trace unavailable";
201
+ notifyDepreciated({
202
+ heading: "Accessing state.gl in WebGPU mode",
203
+ body: "Please use state.renderer instead. state.gl is deprecated and will be removed in future versions.\n\nFor backwards compatibility, state.gl currently maps to state.renderer, but this may cause issues with libraries expecting WebGLRenderer.\n\nAccessed from:\n" + cleanedStack
204
+ });
205
+ }
206
+ }
207
+ return renderer;
208
+ },
209
+ set(value) {
210
+ this.internal.actualRenderer = value;
211
+ },
212
+ enumerable: true,
213
+ configurable: true
214
+ });
215
+ Object.defineProperty(state, "renderer", {
216
+ get() {
217
+ return this.internal?.actualRenderer;
218
+ },
219
+ set(value) {
220
+ this.internal.actualRenderer = value;
221
+ },
222
+ enumerable: true,
223
+ configurable: true
224
+ });
225
+ }
107
226
 
108
227
  const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
109
228
  const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
110
229
 
230
+ const R3F_EXTENSIONS = Symbol.for("@react-three/fiber.extensions");
231
+ function getRegistry() {
232
+ return globalThis[R3F_EXTENSIONS] ?? (globalThis[R3F_EXTENSIONS] = {
233
+ extensions: /* @__PURE__ */ new Map(),
234
+ roots: /* @__PURE__ */ new Set(),
235
+ applied: /* @__PURE__ */ new WeakMap()
236
+ });
237
+ }
238
+ function setupRoot(registry, store, extension) {
239
+ let applied = registry.applied.get(store);
240
+ if (!applied) registry.applied.set(store, applied = /* @__PURE__ */ new Map());
241
+ if (applied.has(extension.name)) return;
242
+ applied.set(extension.name, extension);
243
+ const patch = extension.setup?.(store);
244
+ if (patch) store.setState(patch);
245
+ }
246
+ function registerRootExtension(extension) {
247
+ const registry = getRegistry();
248
+ registry.extensions.set(extension.name, extension);
249
+ for (const store of registry.roots) setupRoot(registry, store, extension);
250
+ return () => {
251
+ if (registry.extensions.get(extension.name) === extension) registry.extensions.delete(extension.name);
252
+ };
253
+ }
254
+ function setRenderOverride(store, render) {
255
+ store.getState().internal.renderOverride = render;
256
+ }
257
+ function attachRootExtensions(store) {
258
+ const registry = getRegistry();
259
+ registry.roots.add(store);
260
+ for (const extension of registry.extensions.values()) setupRoot(registry, store, extension);
261
+ }
262
+ function current(registry, setUpBy) {
263
+ return registry.extensions.get(setUpBy.name) ?? setUpBy;
264
+ }
265
+ function detachRootExtensions(store) {
266
+ const registry = getRegistry();
267
+ if (!registry.roots.delete(store)) return;
268
+ const applied = registry.applied.get(store);
269
+ registry.applied.delete(store);
270
+ if (!applied) return;
271
+ for (const setUpBy of applied.values()) current(registry, setUpBy).dispose?.(store);
272
+ }
273
+ function notifyRootExtensionsHmr(store) {
274
+ const registry = getRegistry();
275
+ const applied = registry.applied.get(store);
276
+ if (!applied) return;
277
+ for (const setUpBy of applied.values()) current(registry, setUpBy).hmr?.(store);
278
+ }
279
+
111
280
  function useStore() {
112
281
  const store = useContext(context);
113
282
  if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
@@ -134,12 +303,20 @@ function useFrame(callback, priorityOrOptions) {
134
303
  const options = React.useMemo(() => {
135
304
  return typeof priorityOrOptions === "number" ? { priority: priorityOrOptions } : priorityOrOptions ?? {};
136
305
  }, [optionsKey]);
137
- const reactId = React.useId();
138
- const id = options.id ?? reactId;
306
+ const [generatedId] = React.useState(() => scheduler.generateJobId());
307
+ const id = options.id ?? generatedId;
139
308
  const callbackRef = useMutableCallback(callback);
140
309
  const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
310
+ const isLegacyOrdering = typeof priorityOrOptions === "number" && priorityOrOptions < 0;
141
311
  useIsomorphicLayoutEffect(() => {
142
312
  if (!callback) return;
313
+ if (isLegacyOrdering) {
314
+ notifyDepreciated({
315
+ heading: "useFrame numeric priority order is reversed in v10",
316
+ body: "useFrame(callback, number) is deprecated and its ordering direction is reversed from v9: v9 ran lower numbers first, v10 maps the number to { priority } and runs higher numbers first, so a negative priority now runs after default-priority jobs.\n\nUse { before, after } to name the dependency, or { priority } with the v10 direction (higher = earlier).",
317
+ link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
318
+ });
319
+ }
143
320
  if (isInsideCanvas) {
144
321
  const state = store.getState();
145
322
  const rootId = state.internal.rootId;
@@ -153,19 +330,17 @@ function useFrame(callback, priorityOrOptions) {
153
330
  }
154
331
  notifyDepreciated({
155
332
  heading: "useFrame with numeric priority is deprecated",
156
- body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
333
+ body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within a phase, use: useFrame(callback, { priority: number }) \u2014 higher runs first, the reverse of v9',
157
334
  link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
158
335
  });
159
336
  }
160
337
  const wrappedCallback = (frameState, delta) => {
161
- const localState = store.getState();
162
- const mergedState = {
163
- ...localState,
164
- time: frameState.time,
165
- delta: frameState.delta,
166
- elapsed: frameState.elapsed,
167
- frame: frameState.frame
168
- };
338
+ const mergedState = cloneState(store.getState());
339
+ mergedState.time = frameState.time;
340
+ mergedState.delta = frameState.delta;
341
+ mergedState.elapsed = frameState.elapsed;
342
+ mergedState.frame = frameState.frame;
343
+ mergedState.overstep = frameState.overstep;
169
344
  callbackRef.current?.(mergedState, delta);
170
345
  };
171
346
  const unregister = scheduler.register(wrappedCallback, {
@@ -198,7 +373,7 @@ function useFrame(callback, priorityOrOptions) {
198
373
  { id, ...options }
199
374
  );
200
375
  }
201
- }, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
376
+ }, [store, scheduler, id, optionsKey, isLegacyPriority, isLegacyOrdering, isInsideCanvas]);
202
377
  const isPaused = React.useSyncExternalStore(
203
378
  // Subscribe function
204
379
  React.useCallback(
@@ -276,54 +451,31 @@ function useFrame(callback, priorityOrOptions) {
276
451
  return controls;
277
452
  }
278
453
 
279
- const R3F_EXTENSIONS = Symbol.for("@react-three/fiber.extensions");
280
- function getRegistry() {
281
- return globalThis[R3F_EXTENSIONS] ?? (globalThis[R3F_EXTENSIONS] = {
282
- extensions: /* @__PURE__ */ new Map(),
283
- roots: /* @__PURE__ */ new Set(),
284
- applied: /* @__PURE__ */ new WeakMap()
285
- });
286
- }
287
- function setupRoot(registry, store, extension) {
288
- let applied = registry.applied.get(store);
289
- if (!applied) registry.applied.set(store, applied = /* @__PURE__ */ new Map());
290
- if (applied.has(extension.name)) return;
291
- applied.set(extension.name, extension);
292
- const patch = extension.setup?.(store);
293
- if (patch) store.setState(patch);
294
- }
295
- function registerRootExtension(extension) {
296
- const registry = getRegistry();
297
- registry.extensions.set(extension.name, extension);
298
- for (const store of registry.roots) setupRoot(registry, store, extension);
299
- return () => {
300
- if (registry.extensions.get(extension.name) === extension) registry.extensions.delete(extension.name);
301
- };
302
- }
303
- function setRenderOverride(store, render) {
304
- store.getState().internal.renderOverride = render;
305
- }
306
- function attachRootExtensions(store) {
307
- const registry = getRegistry();
308
- registry.roots.add(store);
309
- for (const extension of registry.extensions.values()) setupRoot(registry, store, extension);
310
- }
311
- function current(registry, setUpBy) {
312
- return registry.extensions.get(setUpBy.name) ?? setUpBy;
454
+ const R3F_STAGED_TEXTURES = Symbol.for("@react-three/fiber.stagedTextures");
455
+ const globalStaging = globalThis;
456
+ const staging = globalStaging[R3F_STAGED_TEXTURES] ?? (globalStaging[R3F_STAGED_TEXTURES] = /* @__PURE__ */ new WeakMap());
457
+ function stageTextures(store, entries) {
458
+ const registered = store.getState().textures;
459
+ let staged = staging.get(store);
460
+ for (const [url, texture] of entries) {
461
+ if (registered.has(url)) continue;
462
+ if (!staged) staging.set(store, staged = /* @__PURE__ */ new Map());
463
+ staged.set(url, texture);
464
+ }
313
465
  }
314
- function detachRootExtensions(store) {
315
- const registry = getRegistry();
316
- if (!registry.roots.delete(store)) return;
317
- const applied = registry.applied.get(store);
318
- registry.applied.delete(store);
319
- if (!applied) return;
320
- for (const setUpBy of applied.values()) current(registry, setUpBy).dispose?.(store);
466
+ function unstageTextures(store, urls) {
467
+ const staged = staging.get(store);
468
+ if (!staged) return;
469
+ for (const url of urls) staged.delete(url);
470
+ if (staged.size === 0) staging.delete(store);
321
471
  }
322
- function notifyRootExtensionsHmr(store) {
323
- const registry = getRegistry();
324
- const applied = registry.applied.get(store);
325
- if (!applied) return;
326
- for (const setUpBy of applied.values()) current(registry, setUpBy).hmr?.(store);
472
+ function getTextureView(store) {
473
+ const registered = store.getState().textures;
474
+ const staged = staging.get(store);
475
+ if (!staged) return registered;
476
+ const view = new Map(registered);
477
+ for (const [url, texture] of staged) if (!view.has(url)) view.set(url, texture);
478
+ return view;
327
479
  }
328
480
 
329
- export { Block as B, ErrorBoundary as E, useThree as a, useFrame as b, context as c, useIsomorphicLayoutEffect as d, useMutableCallback as e, useBridge as f, detachRootExtensions as g, attachRootExtensions as h, notifyAlpha as i, notifyRootExtensionsHmr as j, notifyDepreciated as n, registerRootExtension as r, setRenderOverride as s, useStore as u };
481
+ export { Block as B, ErrorBoundary as E, useThree as a, useFrame as b, context as c, useIsomorphicLayoutEffect as d, useMutableCallback as e, useBridge as f, getTextureView as g, defineRendererAccessors as h, isDevelopment as i, detachRootExtensions as j, attachRootExtensions as k, unstageTextures as l, stageTextures as m, notifyDepreciated as n, isAccessor as o, preserveAccessors as p, cloneState as q, registerRootExtension as r, setRenderOverride as s, keepAccessors as t, useStore as u, useGate as v, notifyAlpha as w, notifyRootExtensionsHmr as x };
@@ -0,0 +1,48 @@
1
+ import { aO as FrameNextCallback } from './fiber.C_MFx7We.cjs';
2
+ import { UseFrameNextOptions, FrameNextControls } from '@pmndrs/scheduler';
3
+
4
+ /**
5
+ * Frame hook with phase-based ordering, priority, and FPS throttling.
6
+ *
7
+ * Always delivers full RootState — works both inside and outside Canvas context:
8
+ * - Inside Canvas: Full RootState (renderer, scene, camera, etc.)
9
+ * - Outside Canvas: The job registers immediately but the callback is held until a
10
+ * Canvas adopts it, then runs with full RootState. For hostless / standalone
11
+ * frame loops with no Canvas, use `@pmndrs/scheduler` directly.
12
+ *
13
+ * Returns a controls object for manual stepping, pausing, and resuming.
14
+ *
15
+ * @param callback - Function called each frame with (state, delta). Optional if you only need scheduler access.
16
+ * @param priorityOrOptions - Either a priority number (backwards compat) or options object
17
+ * @returns Controls object with step(), stepAll(), invalidate(), pause(), resume(), isPaused, id, rootId, scheduler
18
+ *
19
+ * @example
20
+ * // Simple priority (backwards compat)
21
+ * useFrame((state, delta) => { ... }, 5)
22
+ *
23
+ * @example
24
+ * // With phase-based ordering
25
+ * useFrame((state, delta) => { ... }, { phase: 'physics' })
26
+ *
27
+ * @example
28
+ * // Outside Canvas - callback waits for a Canvas, then always has full state
29
+ * function UI() {
30
+ * useFrame((state, delta) => { syncUI(state.camera) });
31
+ * return <button>...</button>;
32
+ * }
33
+ *
34
+ * @example
35
+ * // Hostless / standalone loop (no Canvas) - use @pmndrs/scheduler directly
36
+ * import { getScheduler } from '@pmndrs/scheduler'
37
+ * getScheduler().register((state, delta) => { updateGame(delta) })
38
+ *
39
+ * @example
40
+ * // Scheduler-only access (no callback)
41
+ * const { scheduler } = useFrame()
42
+ * scheduler.pauseJob('some-job-id')
43
+ *
44
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
45
+ */
46
+ declare function useFrame(callback?: FrameNextCallback, priorityOrOptions?: number | UseFrameNextOptions): FrameNextControls;
47
+
48
+ export { useFrame as u };