@react-three-dom/core 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Object3D, Scene, Camera, WebGLRenderer, Intersection, Vector3 } from 'three';
2
2
 
3
- declare const version = "0.3.0";
3
+ declare const version = "0.4.0";
4
4
 
5
5
  /**
6
6
  * Enable or disable debug logging globally.
@@ -261,6 +261,17 @@ interface R3FDOM {
261
261
  setInspectMode(on: boolean): void;
262
262
  /** Returns true if inspect mode is currently active. */
263
263
  getInspectMode(): boolean;
264
+ /**
265
+ * Manually register a Three.js object (and materialize its mirror DOM node).
266
+ * Useful in `manual` mode or to add specific objects in `auto` mode that
267
+ * were excluded by a filter.
268
+ */
269
+ r3fRegister(obj: Object3D): void;
270
+ /**
271
+ * Unregister a previously registered object and its descendants.
272
+ * Removes the mirror DOM node and store entry.
273
+ */
274
+ r3fUnregister(obj: Object3D): void;
264
275
  /**
265
276
  * Manually sweep orphaned objects from the store.
266
277
  * Removes objects that are no longer in any tracked scene graph.
@@ -400,6 +411,8 @@ declare class ObjectStore {
400
411
  /**
401
412
  * Unregister an entire subtree (object + all descendants).
402
413
  */
414
+ /** Mark a root as tracked without traversing/registering its children. */
415
+ addTrackedRoot(root: Object3D): void;
403
416
  unregisterTree(root: Object3D): void;
404
417
  /**
405
418
  * Refresh dynamic Tier 1 fields from the live Three.js object.
@@ -1010,7 +1023,7 @@ declare class InspectController {
1010
1023
  updateCamera(camera: Camera): void;
1011
1024
  /** Activate inspect mode — creates overlay on top of canvas. */
1012
1025
  enable(): void;
1013
- /** Deactivate inspect mode — removes overlay and clears hover state. */
1026
+ /** Deactivate inspect mode — removes overlay and clears all inspect state. */
1014
1027
  disable(): void;
1015
1028
  /** Disable and release all resources. */
1016
1029
  dispose(): void;
@@ -1044,6 +1057,19 @@ interface ThreeDomProps {
1044
1057
  * Default: true when canvasId is omitted, false otherwise.
1045
1058
  */
1046
1059
  primary?: boolean;
1060
+ /**
1061
+ * Registration mode:
1062
+ * - "auto" (default): traverses and registers all objects in the scene.
1063
+ * - "manual": nothing is auto-registered. Use `useR3FRegister` hook or
1064
+ * `__R3F_DOM__.r3fRegister()` to add objects explicitly.
1065
+ */
1066
+ mode?: 'auto' | 'manual';
1067
+ /**
1068
+ * Filter function for auto mode. Only objects that pass the filter are
1069
+ * registered. Ignored in manual mode.
1070
+ * Example: `filter={(obj) => !!obj.userData.testId}`
1071
+ */
1072
+ filter?: (obj: Object3D) => boolean;
1047
1073
  /** CSS selector or HTMLElement for the mirror DOM root. Default: "#three-dom-root" */
1048
1074
  root?: string | HTMLElement;
1049
1075
  /** Objects to process per amortized batch per frame. Default: 500 */
@@ -1073,7 +1099,7 @@ declare function getHighlighter(canvasId?: string): Highlighter | null;
1073
1099
  declare function getInspectController(canvasId?: string): InspectController | null;
1074
1100
  /** List all active canvas IDs. */
1075
1101
  declare function getCanvasIds(): string[];
1076
- declare function ThreeDom({ canvasId, primary, root, batchSize, syncBudgetMs, maxDomNodes, initialDepth, enabled, debug, inspect: inspectProp, }?: ThreeDomProps): null;
1102
+ declare function ThreeDom({ canvasId, primary, root, mode, filter, batchSize, syncBudgetMs, maxDomNodes, initialDepth, enabled, debug, inspect: inspectProp, }?: ThreeDomProps): null;
1077
1103
 
1078
1104
  /**
1079
1105
  * Patch Object3D.prototype.add and Object3D.prototype.remove to intercept
@@ -1083,7 +1109,7 @@ declare function ThreeDom({ canvasId, primary, root, batchSize, syncBudgetMs, ma
1083
1109
  * @param mirror - The DomMirror managing the parallel DOM tree
1084
1110
  * @returns A cleanup function that unregisters this store/mirror pair
1085
1111
  */
1086
- declare function patchObject3D(store: ObjectStore, mirror: DomMirror): () => void;
1112
+ declare function patchObject3D(store: ObjectStore, mirror: DomMirror, instanceKey?: string): () => void;
1087
1113
  /**
1088
1114
  * Restore the original Object3D.prototype.add and remove methods.
1089
1115
  * Called automatically when the last store/mirror pair is removed.
@@ -1766,4 +1792,36 @@ declare function circlePath(center: {
1766
1792
  */
1767
1793
  declare function resolveObject(idOrUuid: string): Object3D;
1768
1794
 
1769
- export { type CameraState, type CanvasSize, type Click3DOptions, type Click3DResult, DomMirror, type Drag3DOptions, type Drag3DResult, type DragOptions, type DrawPathOptions, type DrawPathResult, type DrawPoint, type GeometryInspection, Highlighter, type HighlighterOptions, type Hover3DOptions, type Hover3DResult, InspectController, type InspectOptions, MANAGED_ATTRIBUTES, type MaterialInspection, type ObjectInspection, type ObjectMetadata, ObjectStore, type PointerMiss3DOptions, type ProjectionResult, type R3FDOM, RaycastAccelerator, type RaycastResult, type SceneSnapshot, type ScreenDelta, type ScreenPoint, type SelectionListener, SelectionManager, type SnapshotNode, type StoreEvent, type StoreEventType, type StoreListener, TAG_MAP, ThreeDom, type ThreeDomProps, ThreeElement, type ThreeTagName, type Wheel3DOptions, type Wheel3DResult, type WheelOptions, type WorldDelta, applyAttributes, circlePath, click3D, computeAttributes, contextMenu3D, createFlatSnapshot, createSnapshot, curvePath, dispatchClick, dispatchContextMenu, dispatchDoubleClick, dispatchDrag, dispatchHover, dispatchPointerMiss, dispatchUnhover, dispatchWheel, doubleClick3D, drag3D, drawPath, enableDebug, ensureCustomElements, getCanvasIds, getHighlighter, getInspectController, getMirror, getSelectionManager, getStore, getTagForType, hover3D, isDebugEnabled, isInFrustum, isPatched, linePath, patchObject3D, pointerMiss3D, previewDragWorldDelta, projectAllSamplePoints, projectToScreen, r3fLog, rectPath, resolveObject, restoreObject3D, screenDeltaToWorld, unhover3D, verifyRaycastHit, verifyRaycastHitMultiPoint, version, wheel3D };
1795
+ /**
1796
+ * Registers a Three.js object with the react-three-dom bridge on mount and
1797
+ * unregisters it on unmount. Works in both `auto` and `manual` mode.
1798
+ *
1799
+ * - In `manual` mode this is the primary way to opt objects into the mirror DOM.
1800
+ * - In `auto` mode it can force-register objects excluded by a `filter`.
1801
+ * - Handles `ref.current` identity changes automatically.
1802
+ * - If the bridge isn't ready on mount, retries each frame until available.
1803
+ * - Supports multi-canvas via the optional `canvasId` parameter.
1804
+ *
1805
+ * @param ref - Ref to the Three.js object to register
1806
+ * @param canvasId - Optional canvas ID for multi-canvas setups. When omitted,
1807
+ * uses the primary bridge (`window.__R3F_DOM__`).
1808
+ *
1809
+ * @example
1810
+ * ```tsx
1811
+ * function Wall({ geometry }) {
1812
+ * const ref = useRef<Mesh>(null!);
1813
+ * useR3FRegister(ref);
1814
+ * return <mesh ref={ref} geometry={geometry} userData={{ testId: 'wall' }} />;
1815
+ * }
1816
+ *
1817
+ * // Multi-canvas
1818
+ * function Overlay() {
1819
+ * const ref = useRef<Mesh>(null!);
1820
+ * useR3FRegister(ref, 'overlay');
1821
+ * return <mesh ref={ref} />;
1822
+ * }
1823
+ * ```
1824
+ */
1825
+ declare function useR3FRegister(ref: React.RefObject<Object3D | null>, canvasId?: string): void;
1826
+
1827
+ export { type CameraState, type CanvasSize, type Click3DOptions, type Click3DResult, DomMirror, type Drag3DOptions, type Drag3DResult, type DragOptions, type DrawPathOptions, type DrawPathResult, type DrawPoint, type GeometryInspection, Highlighter, type HighlighterOptions, type Hover3DOptions, type Hover3DResult, InspectController, type InspectOptions, MANAGED_ATTRIBUTES, type MaterialInspection, type ObjectInspection, type ObjectMetadata, ObjectStore, type PointerMiss3DOptions, type ProjectionResult, type R3FDOM, RaycastAccelerator, type RaycastResult, type SceneSnapshot, type ScreenDelta, type ScreenPoint, type SelectionListener, SelectionManager, type SnapshotNode, type StoreEvent, type StoreEventType, type StoreListener, TAG_MAP, ThreeDom, type ThreeDomProps, ThreeElement, type ThreeTagName, type Wheel3DOptions, type Wheel3DResult, type WheelOptions, type WorldDelta, applyAttributes, circlePath, click3D, computeAttributes, contextMenu3D, createFlatSnapshot, createSnapshot, curvePath, dispatchClick, dispatchContextMenu, dispatchDoubleClick, dispatchDrag, dispatchHover, dispatchPointerMiss, dispatchUnhover, dispatchWheel, doubleClick3D, drag3D, drawPath, enableDebug, ensureCustomElements, getCanvasIds, getHighlighter, getInspectController, getMirror, getSelectionManager, getStore, getTagForType, hover3D, isDebugEnabled, isInFrustum, isPatched, linePath, patchObject3D, pointerMiss3D, previewDragWorldDelta, projectAllSamplePoints, projectToScreen, r3fLog, rectPath, resolveObject, restoreObject3D, screenDeltaToWorld, unhover3D, useR3FRegister, verifyRaycastHit, verifyRaycastHitMultiPoint, version, wheel3D };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Object3D, Scene, Camera, WebGLRenderer, Intersection, Vector3 } from 'three';
2
2
 
3
- declare const version = "0.3.0";
3
+ declare const version = "0.4.0";
4
4
 
5
5
  /**
6
6
  * Enable or disable debug logging globally.
@@ -261,6 +261,17 @@ interface R3FDOM {
261
261
  setInspectMode(on: boolean): void;
262
262
  /** Returns true if inspect mode is currently active. */
263
263
  getInspectMode(): boolean;
264
+ /**
265
+ * Manually register a Three.js object (and materialize its mirror DOM node).
266
+ * Useful in `manual` mode or to add specific objects in `auto` mode that
267
+ * were excluded by a filter.
268
+ */
269
+ r3fRegister(obj: Object3D): void;
270
+ /**
271
+ * Unregister a previously registered object and its descendants.
272
+ * Removes the mirror DOM node and store entry.
273
+ */
274
+ r3fUnregister(obj: Object3D): void;
264
275
  /**
265
276
  * Manually sweep orphaned objects from the store.
266
277
  * Removes objects that are no longer in any tracked scene graph.
@@ -400,6 +411,8 @@ declare class ObjectStore {
400
411
  /**
401
412
  * Unregister an entire subtree (object + all descendants).
402
413
  */
414
+ /** Mark a root as tracked without traversing/registering its children. */
415
+ addTrackedRoot(root: Object3D): void;
403
416
  unregisterTree(root: Object3D): void;
404
417
  /**
405
418
  * Refresh dynamic Tier 1 fields from the live Three.js object.
@@ -1010,7 +1023,7 @@ declare class InspectController {
1010
1023
  updateCamera(camera: Camera): void;
1011
1024
  /** Activate inspect mode — creates overlay on top of canvas. */
1012
1025
  enable(): void;
1013
- /** Deactivate inspect mode — removes overlay and clears hover state. */
1026
+ /** Deactivate inspect mode — removes overlay and clears all inspect state. */
1014
1027
  disable(): void;
1015
1028
  /** Disable and release all resources. */
1016
1029
  dispose(): void;
@@ -1044,6 +1057,19 @@ interface ThreeDomProps {
1044
1057
  * Default: true when canvasId is omitted, false otherwise.
1045
1058
  */
1046
1059
  primary?: boolean;
1060
+ /**
1061
+ * Registration mode:
1062
+ * - "auto" (default): traverses and registers all objects in the scene.
1063
+ * - "manual": nothing is auto-registered. Use `useR3FRegister` hook or
1064
+ * `__R3F_DOM__.r3fRegister()` to add objects explicitly.
1065
+ */
1066
+ mode?: 'auto' | 'manual';
1067
+ /**
1068
+ * Filter function for auto mode. Only objects that pass the filter are
1069
+ * registered. Ignored in manual mode.
1070
+ * Example: `filter={(obj) => !!obj.userData.testId}`
1071
+ */
1072
+ filter?: (obj: Object3D) => boolean;
1047
1073
  /** CSS selector or HTMLElement for the mirror DOM root. Default: "#three-dom-root" */
1048
1074
  root?: string | HTMLElement;
1049
1075
  /** Objects to process per amortized batch per frame. Default: 500 */
@@ -1073,7 +1099,7 @@ declare function getHighlighter(canvasId?: string): Highlighter | null;
1073
1099
  declare function getInspectController(canvasId?: string): InspectController | null;
1074
1100
  /** List all active canvas IDs. */
1075
1101
  declare function getCanvasIds(): string[];
1076
- declare function ThreeDom({ canvasId, primary, root, batchSize, syncBudgetMs, maxDomNodes, initialDepth, enabled, debug, inspect: inspectProp, }?: ThreeDomProps): null;
1102
+ declare function ThreeDom({ canvasId, primary, root, mode, filter, batchSize, syncBudgetMs, maxDomNodes, initialDepth, enabled, debug, inspect: inspectProp, }?: ThreeDomProps): null;
1077
1103
 
1078
1104
  /**
1079
1105
  * Patch Object3D.prototype.add and Object3D.prototype.remove to intercept
@@ -1083,7 +1109,7 @@ declare function ThreeDom({ canvasId, primary, root, batchSize, syncBudgetMs, ma
1083
1109
  * @param mirror - The DomMirror managing the parallel DOM tree
1084
1110
  * @returns A cleanup function that unregisters this store/mirror pair
1085
1111
  */
1086
- declare function patchObject3D(store: ObjectStore, mirror: DomMirror): () => void;
1112
+ declare function patchObject3D(store: ObjectStore, mirror: DomMirror, instanceKey?: string): () => void;
1087
1113
  /**
1088
1114
  * Restore the original Object3D.prototype.add and remove methods.
1089
1115
  * Called automatically when the last store/mirror pair is removed.
@@ -1766,4 +1792,36 @@ declare function circlePath(center: {
1766
1792
  */
1767
1793
  declare function resolveObject(idOrUuid: string): Object3D;
1768
1794
 
1769
- export { type CameraState, type CanvasSize, type Click3DOptions, type Click3DResult, DomMirror, type Drag3DOptions, type Drag3DResult, type DragOptions, type DrawPathOptions, type DrawPathResult, type DrawPoint, type GeometryInspection, Highlighter, type HighlighterOptions, type Hover3DOptions, type Hover3DResult, InspectController, type InspectOptions, MANAGED_ATTRIBUTES, type MaterialInspection, type ObjectInspection, type ObjectMetadata, ObjectStore, type PointerMiss3DOptions, type ProjectionResult, type R3FDOM, RaycastAccelerator, type RaycastResult, type SceneSnapshot, type ScreenDelta, type ScreenPoint, type SelectionListener, SelectionManager, type SnapshotNode, type StoreEvent, type StoreEventType, type StoreListener, TAG_MAP, ThreeDom, type ThreeDomProps, ThreeElement, type ThreeTagName, type Wheel3DOptions, type Wheel3DResult, type WheelOptions, type WorldDelta, applyAttributes, circlePath, click3D, computeAttributes, contextMenu3D, createFlatSnapshot, createSnapshot, curvePath, dispatchClick, dispatchContextMenu, dispatchDoubleClick, dispatchDrag, dispatchHover, dispatchPointerMiss, dispatchUnhover, dispatchWheel, doubleClick3D, drag3D, drawPath, enableDebug, ensureCustomElements, getCanvasIds, getHighlighter, getInspectController, getMirror, getSelectionManager, getStore, getTagForType, hover3D, isDebugEnabled, isInFrustum, isPatched, linePath, patchObject3D, pointerMiss3D, previewDragWorldDelta, projectAllSamplePoints, projectToScreen, r3fLog, rectPath, resolveObject, restoreObject3D, screenDeltaToWorld, unhover3D, verifyRaycastHit, verifyRaycastHitMultiPoint, version, wheel3D };
1795
+ /**
1796
+ * Registers a Three.js object with the react-three-dom bridge on mount and
1797
+ * unregisters it on unmount. Works in both `auto` and `manual` mode.
1798
+ *
1799
+ * - In `manual` mode this is the primary way to opt objects into the mirror DOM.
1800
+ * - In `auto` mode it can force-register objects excluded by a `filter`.
1801
+ * - Handles `ref.current` identity changes automatically.
1802
+ * - If the bridge isn't ready on mount, retries each frame until available.
1803
+ * - Supports multi-canvas via the optional `canvasId` parameter.
1804
+ *
1805
+ * @param ref - Ref to the Three.js object to register
1806
+ * @param canvasId - Optional canvas ID for multi-canvas setups. When omitted,
1807
+ * uses the primary bridge (`window.__R3F_DOM__`).
1808
+ *
1809
+ * @example
1810
+ * ```tsx
1811
+ * function Wall({ geometry }) {
1812
+ * const ref = useRef<Mesh>(null!);
1813
+ * useR3FRegister(ref);
1814
+ * return <mesh ref={ref} geometry={geometry} userData={{ testId: 'wall' }} />;
1815
+ * }
1816
+ *
1817
+ * // Multi-canvas
1818
+ * function Overlay() {
1819
+ * const ref = useRef<Mesh>(null!);
1820
+ * useR3FRegister(ref, 'overlay');
1821
+ * return <mesh ref={ref} />;
1822
+ * }
1823
+ * ```
1824
+ */
1825
+ declare function useR3FRegister(ref: React.RefObject<Object3D | null>, canvasId?: string): void;
1826
+
1827
+ export { type CameraState, type CanvasSize, type Click3DOptions, type Click3DResult, DomMirror, type Drag3DOptions, type Drag3DResult, type DragOptions, type DrawPathOptions, type DrawPathResult, type DrawPoint, type GeometryInspection, Highlighter, type HighlighterOptions, type Hover3DOptions, type Hover3DResult, InspectController, type InspectOptions, MANAGED_ATTRIBUTES, type MaterialInspection, type ObjectInspection, type ObjectMetadata, ObjectStore, type PointerMiss3DOptions, type ProjectionResult, type R3FDOM, RaycastAccelerator, type RaycastResult, type SceneSnapshot, type ScreenDelta, type ScreenPoint, type SelectionListener, SelectionManager, type SnapshotNode, type StoreEvent, type StoreEventType, type StoreListener, TAG_MAP, ThreeDom, type ThreeDomProps, ThreeElement, type ThreeTagName, type Wheel3DOptions, type Wheel3DResult, type WheelOptions, type WorldDelta, applyAttributes, circlePath, click3D, computeAttributes, contextMenu3D, createFlatSnapshot, createSnapshot, curvePath, dispatchClick, dispatchContextMenu, dispatchDoubleClick, dispatchDrag, dispatchHover, dispatchPointerMiss, dispatchUnhover, dispatchWheel, doubleClick3D, drag3D, drawPath, enableDebug, ensureCustomElements, getCanvasIds, getHighlighter, getInspectController, getMirror, getSelectionManager, getStore, getTagForType, hover3D, isDebugEnabled, isInFrustum, isPatched, linePath, patchObject3D, pointerMiss3D, previewDragWorldDelta, projectAllSamplePoints, projectToScreen, r3fLog, rectPath, resolveObject, restoreObject3D, screenDeltaToWorld, unhover3D, useR3FRegister, verifyRaycastHit, verifyRaycastHitMultiPoint, version, wheel3D };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Box3, Object3D, Vector3, Matrix4, Frustum, Raycaster, Vector2, BufferGeometry, Material, Color, MeshBasicMaterial, DoubleSide, Mesh, BoxGeometry, EdgesGeometry, LineBasicMaterial, LineSegments, InstancedMesh, PerspectiveCamera, OrthographicCamera } from 'three';
2
- import { useRef, useEffect } from 'react';
2
+ import { useRef, useEffect, useCallback } from 'react';
3
3
  import { useThree, useFrame } from '@react-three/fiber';
4
4
  import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-mesh-bvh';
5
5
 
6
6
  // src/version.ts
7
- var version = "0.3.0";
7
+ var version = "0.4.0";
8
8
 
9
9
  // src/debug.ts
10
10
  var _enabled = false;
@@ -399,6 +399,7 @@ var ObjectStore = class {
399
399
  this._dirtyQueue.delete(obj);
400
400
  this._flatListDirty = true;
401
401
  delete obj.userData.__r3fdom_tracked;
402
+ delete obj.userData.__r3fdom_manual;
402
403
  if (meta.testId) {
403
404
  this._objectsByTestId.delete(meta.testId);
404
405
  }
@@ -419,6 +420,10 @@ var ObjectStore = class {
419
420
  /**
420
421
  * Unregister an entire subtree (object + all descendants).
421
422
  */
423
+ /** Mark a root as tracked without traversing/registering its children. */
424
+ addTrackedRoot(root) {
425
+ this._trackedRoots.add(root);
426
+ }
422
427
  unregisterTree(root) {
423
428
  root.traverse((obj) => {
424
429
  this.unregister(obj);
@@ -1515,16 +1520,16 @@ function findTrackingPair(obj) {
1515
1520
  }
1516
1521
  return null;
1517
1522
  }
1518
- function registerSubtree(obj, store, mirror) {
1523
+ function registerSubtree(obj, store, mirror, instanceKey) {
1519
1524
  obj.traverse((child) => {
1520
- if (!store.has(child)) {
1525
+ if (!store.has(child) && shouldRegister(instanceKey, child)) {
1521
1526
  store.register(child);
1522
1527
  mirror.onObjectAdded(child);
1523
1528
  }
1524
1529
  });
1525
1530
  }
1526
- function patchObject3D(store, mirror) {
1527
- _activePairs.push({ store, mirror });
1531
+ function patchObject3D(store, mirror, instanceKey = "") {
1532
+ _activePairs.push({ store, mirror, instanceKey });
1528
1533
  if (!_patched) {
1529
1534
  r3fLog("patch", "Patching Object3D.prototype.add and .remove");
1530
1535
  _originalAdd = Object3D.prototype.add;
@@ -1537,7 +1542,7 @@ function patchObject3D(store, mirror) {
1537
1542
  if (obj === this) continue;
1538
1543
  try {
1539
1544
  r3fLog("patch", `patchedAdd: "${obj.name || obj.type}" added to "${this.name || this.type}"`);
1540
- registerSubtree(obj, pair.store, pair.mirror);
1545
+ registerSubtree(obj, pair.store, pair.mirror, pair.instanceKey);
1541
1546
  } catch (err) {
1542
1547
  r3fLog("patch", `patchedAdd: failed to register "${obj.name || obj.type}"`, err);
1543
1548
  }
@@ -3096,7 +3101,7 @@ var InspectController = class {
3096
3101
  this._overlay = overlay;
3097
3102
  r3fLog("inspect", "Inspect mode enabled \u2014 hover to highlight, click to select");
3098
3103
  }
3099
- /** Deactivate inspect mode — removes overlay and clears hover state. */
3104
+ /** Deactivate inspect mode — removes overlay and clears all inspect state. */
3100
3105
  disable() {
3101
3106
  if (!this._active) return;
3102
3107
  this._active = false;
@@ -3343,6 +3348,15 @@ var _mirrors = /* @__PURE__ */ new Map();
3343
3348
  var _selectionManagers = /* @__PURE__ */ new Map();
3344
3349
  var _highlighters = /* @__PURE__ */ new Map();
3345
3350
  var _inspectControllers = /* @__PURE__ */ new Map();
3351
+ var _filters = /* @__PURE__ */ new Map();
3352
+ var _modes = /* @__PURE__ */ new Map();
3353
+ function shouldRegister(instanceKey, obj) {
3354
+ const mode = _modes.get(instanceKey);
3355
+ if (mode === "manual") return false;
3356
+ const filter = _filters.get(instanceKey);
3357
+ if (filter) return filter(obj);
3358
+ return true;
3359
+ }
3346
3360
  function getStore2(canvasId = "") {
3347
3361
  return _stores.get(canvasId) ?? null;
3348
3362
  }
@@ -3493,6 +3507,31 @@ function exposeGlobalAPI(store, gl, cameraRef, selMgr, inspCtrl, mirror, canvasI
3493
3507
  }
3494
3508
  return state;
3495
3509
  },
3510
+ r3fRegister: (obj) => {
3511
+ if (store.has(obj)) return;
3512
+ if (!store.isInTrackedScene(obj)) {
3513
+ console.warn(
3514
+ `[react-three-dom] r3fRegister: object "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" is not in a tracked scene. Add it to the scene graph first.`
3515
+ );
3516
+ return;
3517
+ }
3518
+ obj.userData.__r3fdom_manual = true;
3519
+ store.register(obj);
3520
+ mirror?.onObjectAdded(obj);
3521
+ mirror?.materialize(obj.uuid);
3522
+ r3fLog("bridge", `r3fRegister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type})`);
3523
+ },
3524
+ r3fUnregister: (obj) => {
3525
+ if (!store.has(obj)) return;
3526
+ if (!obj.userData?.__r3fdom_manual) {
3527
+ r3fLog("bridge", `r3fUnregister skipped \u2014 "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" was auto-registered`);
3528
+ return;
3529
+ }
3530
+ delete obj.userData.__r3fdom_manual;
3531
+ mirror?.onObjectRemoved(obj);
3532
+ obj.traverse((child) => store.unregister(child));
3533
+ r3fLog("bridge", `r3fUnregister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type})`);
3534
+ },
3496
3535
  fuzzyFind: (query, limit = 5) => {
3497
3536
  const q = query.toLowerCase();
3498
3537
  const results = [];
@@ -3615,6 +3654,10 @@ function createStubBridge(error, canvasId) {
3615
3654
  setInspectMode: () => {
3616
3655
  },
3617
3656
  getInspectMode: () => false,
3657
+ r3fRegister: () => {
3658
+ },
3659
+ r3fUnregister: () => {
3660
+ },
3618
3661
  sweepOrphans: () => 0,
3619
3662
  getDiagnostics: () => ({
3620
3663
  version,
@@ -3649,6 +3692,8 @@ function ThreeDom({
3649
3692
  canvasId,
3650
3693
  primary,
3651
3694
  root = "#three-dom-root",
3695
+ mode = "auto",
3696
+ filter,
3652
3697
  batchSize = 500,
3653
3698
  syncBudgetMs = 0.5,
3654
3699
  maxDomNodes = 2e3,
@@ -3723,18 +3768,38 @@ function ThreeDom({
3723
3768
  mirror.setRoot(rootElement);
3724
3769
  r3fLog("setup", "Store and mirror created");
3725
3770
  ensureCustomElements(store);
3726
- unpatch = patchObject3D(store, mirror);
3771
+ _modes.set(instanceKey, mode);
3772
+ _filters.set(instanceKey, filter ?? null);
3773
+ unpatch = patchObject3D(store, mirror, instanceKey);
3727
3774
  setInteractionState(store, camera, gl, size);
3728
- r3fLog("setup", "Object3D patched, interaction state set");
3729
- store.registerTree(scene);
3730
- if (!store.has(camera)) {
3731
- const camMeta = store.register(camera);
3732
- camMeta.parentUuid = scene.uuid;
3733
- mirror.materialize(camera.uuid);
3775
+ r3fLog("setup", `Object3D patched (mode=${mode}), interaction state set`);
3776
+ if (mode === "auto") {
3777
+ if (filter) {
3778
+ store.addTrackedRoot(scene);
3779
+ scene.traverse((obj) => {
3780
+ if (filter(obj)) {
3781
+ store.register(obj);
3782
+ mirror.onObjectAdded(obj);
3783
+ }
3784
+ });
3785
+ } else {
3786
+ store.registerTree(scene);
3787
+ }
3788
+ if (!store.has(camera)) {
3789
+ const camMeta = store.register(camera);
3790
+ camMeta.parentUuid = scene.uuid;
3791
+ mirror.materialize(camera.uuid);
3792
+ }
3793
+ mirror.materializeSubtree(scene.uuid, initialDepth);
3794
+ if (!filter) {
3795
+ cancelAsyncReg = store.registerTreeAsync(scene);
3796
+ }
3797
+ } else {
3798
+ store.addTrackedRoot(scene);
3799
+ store.register(scene);
3800
+ mirror.onObjectAdded(scene);
3734
3801
  }
3735
- mirror.materializeSubtree(scene.uuid, initialDepth);
3736
- cancelAsyncReg = store.registerTreeAsync(scene);
3737
- r3fLog("setup", `Scene registered: ${store.getCount()} objects, async watcher started`);
3802
+ r3fLog("setup", `Scene registered (mode=${mode}): ${store.getCount()} objects`);
3738
3803
  selectionManager = new SelectionManager();
3739
3804
  highlighter = new Highlighter();
3740
3805
  highlighter.attach(scene, selectionManager, camera, gl, store);
@@ -3806,6 +3871,8 @@ function ThreeDom({
3806
3871
  _selectionManagers.delete(instanceKey);
3807
3872
  _highlighters.delete(instanceKey);
3808
3873
  _inspectControllers.delete(instanceKey);
3874
+ _modes.delete(instanceKey);
3875
+ _filters.delete(instanceKey);
3809
3876
  if (debug) enableDebug(false);
3810
3877
  r3fLog("setup", "ThreeDom cleanup complete");
3811
3878
  };
@@ -3853,7 +3920,42 @@ function ThreeDom({
3853
3920
  });
3854
3921
  return null;
3855
3922
  }
3923
+ function getAPI(canvasId) {
3924
+ if (canvasId) {
3925
+ return window.__R3F_DOM_INSTANCES__?.[canvasId];
3926
+ }
3927
+ return window.__R3F_DOM__;
3928
+ }
3929
+ function useR3FRegister(ref, canvasId) {
3930
+ const trackedObj = useRef(null);
3931
+ const canvasIdRef = useRef(canvasId);
3932
+ canvasIdRef.current = canvasId;
3933
+ const register = useCallback((obj) => {
3934
+ const api = getAPI(canvasIdRef.current);
3935
+ if (!api) return false;
3936
+ api.r3fRegister(obj);
3937
+ trackedObj.current = obj;
3938
+ return true;
3939
+ }, []);
3940
+ const unregister = useCallback(() => {
3941
+ if (!trackedObj.current) return;
3942
+ const api = getAPI(canvasIdRef.current);
3943
+ api?.r3fUnregister(trackedObj.current);
3944
+ trackedObj.current = null;
3945
+ }, []);
3946
+ useEffect(() => {
3947
+ const obj = ref.current;
3948
+ if (obj) register(obj);
3949
+ return () => unregister();
3950
+ }, [ref, register, unregister]);
3951
+ useFrame(() => {
3952
+ const current = ref.current;
3953
+ if (current === trackedObj.current) return;
3954
+ unregister();
3955
+ if (current) register(current);
3956
+ });
3957
+ }
3856
3958
 
3857
- export { DomMirror, Highlighter, InspectController, MANAGED_ATTRIBUTES, ObjectStore, RaycastAccelerator, SelectionManager, TAG_MAP, ThreeDom, ThreeElement, applyAttributes, circlePath, click3D, computeAttributes, contextMenu3D, createFlatSnapshot, createSnapshot, curvePath, dispatchClick, dispatchContextMenu, dispatchDoubleClick, dispatchDrag, dispatchHover, dispatchPointerMiss, dispatchUnhover, dispatchWheel, doubleClick3D, drag3D, drawPath, enableDebug, ensureCustomElements, getCanvasIds, getHighlighter, getInspectController, getMirror, getSelectionManager, getStore2 as getStore, getTagForType, hover3D, isDebugEnabled, isInFrustum, isPatched, linePath, patchObject3D, pointerMiss3D, previewDragWorldDelta, projectAllSamplePoints, projectToScreen, r3fLog, rectPath, resolveObject, restoreObject3D, screenDeltaToWorld, unhover3D, verifyRaycastHit, verifyRaycastHitMultiPoint, version, wheel3D };
3959
+ export { DomMirror, Highlighter, InspectController, MANAGED_ATTRIBUTES, ObjectStore, RaycastAccelerator, SelectionManager, TAG_MAP, ThreeDom, ThreeElement, applyAttributes, circlePath, click3D, computeAttributes, contextMenu3D, createFlatSnapshot, createSnapshot, curvePath, dispatchClick, dispatchContextMenu, dispatchDoubleClick, dispatchDrag, dispatchHover, dispatchPointerMiss, dispatchUnhover, dispatchWheel, doubleClick3D, drag3D, drawPath, enableDebug, ensureCustomElements, getCanvasIds, getHighlighter, getInspectController, getMirror, getSelectionManager, getStore2 as getStore, getTagForType, hover3D, isDebugEnabled, isInFrustum, isPatched, linePath, patchObject3D, pointerMiss3D, previewDragWorldDelta, projectAllSamplePoints, projectToScreen, r3fLog, rectPath, resolveObject, restoreObject3D, screenDeltaToWorld, unhover3D, useR3FRegister, verifyRaycastHit, verifyRaycastHitMultiPoint, version, wheel3D };
3858
3960
  //# sourceMappingURL=index.js.map
3859
3961
  //# sourceMappingURL=index.js.map