@react-three-dom/core 0.3.0 → 0.5.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.cjs CHANGED
@@ -6,7 +6,7 @@ var fiber = require('@react-three/fiber');
6
6
  var threeMeshBvh = require('three-mesh-bvh');
7
7
 
8
8
  // src/version.ts
9
- var version = "0.3.0";
9
+ var version = "0.5.0";
10
10
 
11
11
  // src/debug.ts
12
12
  var _enabled = false;
@@ -401,6 +401,7 @@ var ObjectStore = class {
401
401
  this._dirtyQueue.delete(obj);
402
402
  this._flatListDirty = true;
403
403
  delete obj.userData.__r3fdom_tracked;
404
+ delete obj.userData.__r3fdom_manual;
404
405
  if (meta.testId) {
405
406
  this._objectsByTestId.delete(meta.testId);
406
407
  }
@@ -421,6 +422,10 @@ var ObjectStore = class {
421
422
  /**
422
423
  * Unregister an entire subtree (object + all descendants).
423
424
  */
425
+ /** Mark a root as tracked without traversing/registering its children. */
426
+ addTrackedRoot(root) {
427
+ this._trackedRoots.add(root);
428
+ }
424
429
  unregisterTree(root) {
425
430
  root.traverse((obj) => {
426
431
  this.unregister(obj);
@@ -1517,16 +1522,17 @@ function findTrackingPair(obj) {
1517
1522
  }
1518
1523
  return null;
1519
1524
  }
1520
- function registerSubtree(obj, store, mirror) {
1525
+ function registerSubtree(obj, store, mirror, instanceKey) {
1521
1526
  obj.traverse((child) => {
1522
- if (!store.has(child)) {
1527
+ if (!store.has(child) && shouldRegister(instanceKey, child)) {
1528
+ ensureAncestorChain(child, store, mirror);
1523
1529
  store.register(child);
1524
1530
  mirror.onObjectAdded(child);
1525
1531
  }
1526
1532
  });
1527
1533
  }
1528
- function patchObject3D(store, mirror) {
1529
- _activePairs.push({ store, mirror });
1534
+ function patchObject3D(store, mirror, instanceKey = "") {
1535
+ _activePairs.push({ store, mirror, instanceKey });
1530
1536
  if (!_patched) {
1531
1537
  r3fLog("patch", "Patching Object3D.prototype.add and .remove");
1532
1538
  _originalAdd = three.Object3D.prototype.add;
@@ -1539,7 +1545,7 @@ function patchObject3D(store, mirror) {
1539
1545
  if (obj === this) continue;
1540
1546
  try {
1541
1547
  r3fLog("patch", `patchedAdd: "${obj.name || obj.type}" added to "${this.name || this.type}"`);
1542
- registerSubtree(obj, pair.store, pair.mirror);
1548
+ registerSubtree(obj, pair.store, pair.mirror, pair.instanceKey);
1543
1549
  } catch (err) {
1544
1550
  r3fLog("patch", `patchedAdd: failed to register "${obj.name || obj.type}"`, err);
1545
1551
  }
@@ -3098,7 +3104,7 @@ var InspectController = class {
3098
3104
  this._overlay = overlay;
3099
3105
  r3fLog("inspect", "Inspect mode enabled \u2014 hover to highlight, click to select");
3100
3106
  }
3101
- /** Deactivate inspect mode — removes overlay and clears hover state. */
3107
+ /** Deactivate inspect mode — removes overlay and clears all inspect state. */
3102
3108
  disable() {
3103
3109
  if (!this._active) return;
3104
3110
  this._active = false;
@@ -3345,6 +3351,30 @@ var _mirrors = /* @__PURE__ */ new Map();
3345
3351
  var _selectionManagers = /* @__PURE__ */ new Map();
3346
3352
  var _highlighters = /* @__PURE__ */ new Map();
3347
3353
  var _inspectControllers = /* @__PURE__ */ new Map();
3354
+ var _filters = /* @__PURE__ */ new Map();
3355
+ var _modes = /* @__PURE__ */ new Map();
3356
+ function shouldRegister(instanceKey, obj) {
3357
+ const mode = _modes.get(instanceKey);
3358
+ if (mode === "manual") return false;
3359
+ const filter = _filters.get(instanceKey);
3360
+ if (filter) return filter(obj);
3361
+ return true;
3362
+ }
3363
+ function ensureAncestorChain(obj, store, mirror) {
3364
+ const chain = [];
3365
+ let cursor = obj.parent;
3366
+ while (cursor) {
3367
+ if (store.has(cursor)) break;
3368
+ chain.push(cursor);
3369
+ cursor = cursor.parent;
3370
+ }
3371
+ for (let i = chain.length - 1; i >= 0; i--) {
3372
+ const ancestor = chain[i];
3373
+ store.register(ancestor);
3374
+ mirror.onObjectAdded(ancestor);
3375
+ mirror.materialize(ancestor.uuid);
3376
+ }
3377
+ }
3348
3378
  function getStore2(canvasId = "") {
3349
3379
  return _stores.get(canvasId) ?? null;
3350
3380
  }
@@ -3495,6 +3525,38 @@ function exposeGlobalAPI(store, gl, cameraRef, selMgr, inspCtrl, mirror, canvasI
3495
3525
  }
3496
3526
  return state;
3497
3527
  },
3528
+ r3fRegister: (obj) => {
3529
+ if (store.has(obj)) return;
3530
+ if (!store.isInTrackedScene(obj)) {
3531
+ console.warn(
3532
+ `[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.`
3533
+ );
3534
+ return;
3535
+ }
3536
+ obj.userData.__r3fdom_manual = true;
3537
+ ensureAncestorChain(obj, store, mirror);
3538
+ let count = 0;
3539
+ obj.traverse((child) => {
3540
+ if (!store.has(child)) {
3541
+ store.register(child);
3542
+ mirror?.onObjectAdded(child);
3543
+ mirror?.materialize(child.uuid);
3544
+ count++;
3545
+ }
3546
+ });
3547
+ r3fLog("bridge", `r3fRegister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type}) \u2014 ${count} objects`);
3548
+ },
3549
+ r3fUnregister: (obj) => {
3550
+ if (!store.has(obj)) return;
3551
+ if (!obj.userData?.__r3fdom_manual) {
3552
+ r3fLog("bridge", `r3fUnregister skipped \u2014 "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" was auto-registered`);
3553
+ return;
3554
+ }
3555
+ delete obj.userData.__r3fdom_manual;
3556
+ mirror?.onObjectRemoved(obj);
3557
+ obj.traverse((child) => store.unregister(child));
3558
+ r3fLog("bridge", `r3fUnregister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type})`);
3559
+ },
3498
3560
  fuzzyFind: (query, limit = 5) => {
3499
3561
  const q = query.toLowerCase();
3500
3562
  const results = [];
@@ -3617,6 +3679,10 @@ function createStubBridge(error, canvasId) {
3617
3679
  setInspectMode: () => {
3618
3680
  },
3619
3681
  getInspectMode: () => false,
3682
+ r3fRegister: () => {
3683
+ },
3684
+ r3fUnregister: () => {
3685
+ },
3620
3686
  sweepOrphans: () => 0,
3621
3687
  getDiagnostics: () => ({
3622
3688
  version,
@@ -3651,6 +3717,8 @@ function ThreeDom({
3651
3717
  canvasId,
3652
3718
  primary,
3653
3719
  root = "#three-dom-root",
3720
+ mode = "auto",
3721
+ filter,
3654
3722
  batchSize = 500,
3655
3723
  syncBudgetMs = 0.5,
3656
3724
  maxDomNodes = 2e3,
@@ -3725,18 +3793,42 @@ function ThreeDom({
3725
3793
  mirror.setRoot(rootElement);
3726
3794
  r3fLog("setup", "Store and mirror created");
3727
3795
  ensureCustomElements(store);
3728
- unpatch = patchObject3D(store, mirror);
3796
+ _modes.set(instanceKey, mode);
3797
+ _filters.set(instanceKey, filter ?? null);
3798
+ unpatch = patchObject3D(store, mirror, instanceKey);
3729
3799
  setInteractionState(store, camera, gl, size);
3730
- r3fLog("setup", "Object3D patched, interaction state set");
3731
- store.registerTree(scene);
3732
- if (!store.has(camera)) {
3733
- const camMeta = store.register(camera);
3734
- camMeta.parentUuid = scene.uuid;
3735
- mirror.materialize(camera.uuid);
3800
+ r3fLog("setup", `Object3D patched (mode=${mode}), interaction state set`);
3801
+ if (mode === "auto") {
3802
+ if (filter) {
3803
+ store.addTrackedRoot(scene);
3804
+ store.register(scene);
3805
+ mirror.onObjectAdded(scene);
3806
+ scene.traverse((obj) => {
3807
+ if (obj === scene) return;
3808
+ if (filter(obj)) {
3809
+ ensureAncestorChain(obj, store, mirror);
3810
+ store.register(obj);
3811
+ mirror.onObjectAdded(obj);
3812
+ }
3813
+ });
3814
+ } else {
3815
+ store.registerTree(scene);
3816
+ }
3817
+ if (!store.has(camera)) {
3818
+ const camMeta = store.register(camera);
3819
+ camMeta.parentUuid = scene.uuid;
3820
+ mirror.materialize(camera.uuid);
3821
+ }
3822
+ mirror.materializeSubtree(scene.uuid, initialDepth);
3823
+ if (!filter) {
3824
+ cancelAsyncReg = store.registerTreeAsync(scene);
3825
+ }
3826
+ } else {
3827
+ store.addTrackedRoot(scene);
3828
+ store.register(scene);
3829
+ mirror.onObjectAdded(scene);
3736
3830
  }
3737
- mirror.materializeSubtree(scene.uuid, initialDepth);
3738
- cancelAsyncReg = store.registerTreeAsync(scene);
3739
- r3fLog("setup", `Scene registered: ${store.getCount()} objects, async watcher started`);
3831
+ r3fLog("setup", `Scene registered (mode=${mode}): ${store.getCount()} objects`);
3740
3832
  selectionManager = new SelectionManager();
3741
3833
  highlighter = new Highlighter();
3742
3834
  highlighter.attach(scene, selectionManager, camera, gl, store);
@@ -3808,6 +3900,8 @@ function ThreeDom({
3808
3900
  _selectionManagers.delete(instanceKey);
3809
3901
  _highlighters.delete(instanceKey);
3810
3902
  _inspectControllers.delete(instanceKey);
3903
+ _modes.delete(instanceKey);
3904
+ _filters.delete(instanceKey);
3811
3905
  if (debug) enableDebug(false);
3812
3906
  r3fLog("setup", "ThreeDom cleanup complete");
3813
3907
  };
@@ -3855,6 +3949,41 @@ function ThreeDom({
3855
3949
  });
3856
3950
  return null;
3857
3951
  }
3952
+ function getAPI(canvasId) {
3953
+ if (canvasId) {
3954
+ return window.__R3F_DOM_INSTANCES__?.[canvasId];
3955
+ }
3956
+ return window.__R3F_DOM__;
3957
+ }
3958
+ function useR3FRegister(ref, canvasId) {
3959
+ const trackedObj = react.useRef(null);
3960
+ const canvasIdRef = react.useRef(canvasId);
3961
+ canvasIdRef.current = canvasId;
3962
+ const register = react.useCallback((obj) => {
3963
+ const api = getAPI(canvasIdRef.current);
3964
+ if (!api) return false;
3965
+ api.r3fRegister(obj);
3966
+ trackedObj.current = obj;
3967
+ return true;
3968
+ }, []);
3969
+ const unregister = react.useCallback(() => {
3970
+ if (!trackedObj.current) return;
3971
+ const api = getAPI(canvasIdRef.current);
3972
+ api?.r3fUnregister(trackedObj.current);
3973
+ trackedObj.current = null;
3974
+ }, []);
3975
+ react.useEffect(() => {
3976
+ const obj = ref.current;
3977
+ if (obj) register(obj);
3978
+ return () => unregister();
3979
+ }, [ref, register, unregister]);
3980
+ fiber.useFrame(() => {
3981
+ const current = ref.current;
3982
+ if (current === trackedObj.current) return;
3983
+ unregister();
3984
+ if (current) register(current);
3985
+ });
3986
+ }
3858
3987
 
3859
3988
  exports.DomMirror = DomMirror;
3860
3989
  exports.Highlighter = Highlighter;
@@ -3910,6 +4039,7 @@ exports.resolveObject = resolveObject;
3910
4039
  exports.restoreObject3D = restoreObject3D;
3911
4040
  exports.screenDeltaToWorld = screenDeltaToWorld;
3912
4041
  exports.unhover3D = unhover3D;
4042
+ exports.useR3FRegister = useR3FRegister;
3913
4043
  exports.verifyRaycastHit = verifyRaycastHit;
3914
4044
  exports.verifyRaycastHitMultiPoint = verifyRaycastHitMultiPoint;
3915
4045
  exports.version = version;