@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.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.4.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,16 @@ 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)) {
1523
1528
  store.register(child);
1524
1529
  mirror.onObjectAdded(child);
1525
1530
  }
1526
1531
  });
1527
1532
  }
1528
- function patchObject3D(store, mirror) {
1529
- _activePairs.push({ store, mirror });
1533
+ function patchObject3D(store, mirror, instanceKey = "") {
1534
+ _activePairs.push({ store, mirror, instanceKey });
1530
1535
  if (!_patched) {
1531
1536
  r3fLog("patch", "Patching Object3D.prototype.add and .remove");
1532
1537
  _originalAdd = three.Object3D.prototype.add;
@@ -1539,7 +1544,7 @@ function patchObject3D(store, mirror) {
1539
1544
  if (obj === this) continue;
1540
1545
  try {
1541
1546
  r3fLog("patch", `patchedAdd: "${obj.name || obj.type}" added to "${this.name || this.type}"`);
1542
- registerSubtree(obj, pair.store, pair.mirror);
1547
+ registerSubtree(obj, pair.store, pair.mirror, pair.instanceKey);
1543
1548
  } catch (err) {
1544
1549
  r3fLog("patch", `patchedAdd: failed to register "${obj.name || obj.type}"`, err);
1545
1550
  }
@@ -3098,7 +3103,7 @@ var InspectController = class {
3098
3103
  this._overlay = overlay;
3099
3104
  r3fLog("inspect", "Inspect mode enabled \u2014 hover to highlight, click to select");
3100
3105
  }
3101
- /** Deactivate inspect mode — removes overlay and clears hover state. */
3106
+ /** Deactivate inspect mode — removes overlay and clears all inspect state. */
3102
3107
  disable() {
3103
3108
  if (!this._active) return;
3104
3109
  this._active = false;
@@ -3345,6 +3350,15 @@ var _mirrors = /* @__PURE__ */ new Map();
3345
3350
  var _selectionManagers = /* @__PURE__ */ new Map();
3346
3351
  var _highlighters = /* @__PURE__ */ new Map();
3347
3352
  var _inspectControllers = /* @__PURE__ */ new Map();
3353
+ var _filters = /* @__PURE__ */ new Map();
3354
+ var _modes = /* @__PURE__ */ new Map();
3355
+ function shouldRegister(instanceKey, obj) {
3356
+ const mode = _modes.get(instanceKey);
3357
+ if (mode === "manual") return false;
3358
+ const filter = _filters.get(instanceKey);
3359
+ if (filter) return filter(obj);
3360
+ return true;
3361
+ }
3348
3362
  function getStore2(canvasId = "") {
3349
3363
  return _stores.get(canvasId) ?? null;
3350
3364
  }
@@ -3495,6 +3509,31 @@ function exposeGlobalAPI(store, gl, cameraRef, selMgr, inspCtrl, mirror, canvasI
3495
3509
  }
3496
3510
  return state;
3497
3511
  },
3512
+ r3fRegister: (obj) => {
3513
+ if (store.has(obj)) return;
3514
+ if (!store.isInTrackedScene(obj)) {
3515
+ console.warn(
3516
+ `[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.`
3517
+ );
3518
+ return;
3519
+ }
3520
+ obj.userData.__r3fdom_manual = true;
3521
+ store.register(obj);
3522
+ mirror?.onObjectAdded(obj);
3523
+ mirror?.materialize(obj.uuid);
3524
+ r3fLog("bridge", `r3fRegister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type})`);
3525
+ },
3526
+ r3fUnregister: (obj) => {
3527
+ if (!store.has(obj)) return;
3528
+ if (!obj.userData?.__r3fdom_manual) {
3529
+ r3fLog("bridge", `r3fUnregister skipped \u2014 "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" was auto-registered`);
3530
+ return;
3531
+ }
3532
+ delete obj.userData.__r3fdom_manual;
3533
+ mirror?.onObjectRemoved(obj);
3534
+ obj.traverse((child) => store.unregister(child));
3535
+ r3fLog("bridge", `r3fUnregister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type})`);
3536
+ },
3498
3537
  fuzzyFind: (query, limit = 5) => {
3499
3538
  const q = query.toLowerCase();
3500
3539
  const results = [];
@@ -3617,6 +3656,10 @@ function createStubBridge(error, canvasId) {
3617
3656
  setInspectMode: () => {
3618
3657
  },
3619
3658
  getInspectMode: () => false,
3659
+ r3fRegister: () => {
3660
+ },
3661
+ r3fUnregister: () => {
3662
+ },
3620
3663
  sweepOrphans: () => 0,
3621
3664
  getDiagnostics: () => ({
3622
3665
  version,
@@ -3651,6 +3694,8 @@ function ThreeDom({
3651
3694
  canvasId,
3652
3695
  primary,
3653
3696
  root = "#three-dom-root",
3697
+ mode = "auto",
3698
+ filter,
3654
3699
  batchSize = 500,
3655
3700
  syncBudgetMs = 0.5,
3656
3701
  maxDomNodes = 2e3,
@@ -3725,18 +3770,38 @@ function ThreeDom({
3725
3770
  mirror.setRoot(rootElement);
3726
3771
  r3fLog("setup", "Store and mirror created");
3727
3772
  ensureCustomElements(store);
3728
- unpatch = patchObject3D(store, mirror);
3773
+ _modes.set(instanceKey, mode);
3774
+ _filters.set(instanceKey, filter ?? null);
3775
+ unpatch = patchObject3D(store, mirror, instanceKey);
3729
3776
  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);
3777
+ r3fLog("setup", `Object3D patched (mode=${mode}), interaction state set`);
3778
+ if (mode === "auto") {
3779
+ if (filter) {
3780
+ store.addTrackedRoot(scene);
3781
+ scene.traverse((obj) => {
3782
+ if (filter(obj)) {
3783
+ store.register(obj);
3784
+ mirror.onObjectAdded(obj);
3785
+ }
3786
+ });
3787
+ } else {
3788
+ store.registerTree(scene);
3789
+ }
3790
+ if (!store.has(camera)) {
3791
+ const camMeta = store.register(camera);
3792
+ camMeta.parentUuid = scene.uuid;
3793
+ mirror.materialize(camera.uuid);
3794
+ }
3795
+ mirror.materializeSubtree(scene.uuid, initialDepth);
3796
+ if (!filter) {
3797
+ cancelAsyncReg = store.registerTreeAsync(scene);
3798
+ }
3799
+ } else {
3800
+ store.addTrackedRoot(scene);
3801
+ store.register(scene);
3802
+ mirror.onObjectAdded(scene);
3736
3803
  }
3737
- mirror.materializeSubtree(scene.uuid, initialDepth);
3738
- cancelAsyncReg = store.registerTreeAsync(scene);
3739
- r3fLog("setup", `Scene registered: ${store.getCount()} objects, async watcher started`);
3804
+ r3fLog("setup", `Scene registered (mode=${mode}): ${store.getCount()} objects`);
3740
3805
  selectionManager = new SelectionManager();
3741
3806
  highlighter = new Highlighter();
3742
3807
  highlighter.attach(scene, selectionManager, camera, gl, store);
@@ -3808,6 +3873,8 @@ function ThreeDom({
3808
3873
  _selectionManagers.delete(instanceKey);
3809
3874
  _highlighters.delete(instanceKey);
3810
3875
  _inspectControllers.delete(instanceKey);
3876
+ _modes.delete(instanceKey);
3877
+ _filters.delete(instanceKey);
3811
3878
  if (debug) enableDebug(false);
3812
3879
  r3fLog("setup", "ThreeDom cleanup complete");
3813
3880
  };
@@ -3855,6 +3922,41 @@ function ThreeDom({
3855
3922
  });
3856
3923
  return null;
3857
3924
  }
3925
+ function getAPI(canvasId) {
3926
+ if (canvasId) {
3927
+ return window.__R3F_DOM_INSTANCES__?.[canvasId];
3928
+ }
3929
+ return window.__R3F_DOM__;
3930
+ }
3931
+ function useR3FRegister(ref, canvasId) {
3932
+ const trackedObj = react.useRef(null);
3933
+ const canvasIdRef = react.useRef(canvasId);
3934
+ canvasIdRef.current = canvasId;
3935
+ const register = react.useCallback((obj) => {
3936
+ const api = getAPI(canvasIdRef.current);
3937
+ if (!api) return false;
3938
+ api.r3fRegister(obj);
3939
+ trackedObj.current = obj;
3940
+ return true;
3941
+ }, []);
3942
+ const unregister = react.useCallback(() => {
3943
+ if (!trackedObj.current) return;
3944
+ const api = getAPI(canvasIdRef.current);
3945
+ api?.r3fUnregister(trackedObj.current);
3946
+ trackedObj.current = null;
3947
+ }, []);
3948
+ react.useEffect(() => {
3949
+ const obj = ref.current;
3950
+ if (obj) register(obj);
3951
+ return () => unregister();
3952
+ }, [ref, register, unregister]);
3953
+ fiber.useFrame(() => {
3954
+ const current = ref.current;
3955
+ if (current === trackedObj.current) return;
3956
+ unregister();
3957
+ if (current) register(current);
3958
+ });
3959
+ }
3858
3960
 
3859
3961
  exports.DomMirror = DomMirror;
3860
3962
  exports.Highlighter = Highlighter;
@@ -3910,6 +4012,7 @@ exports.resolveObject = resolveObject;
3910
4012
  exports.restoreObject3D = restoreObject3D;
3911
4013
  exports.screenDeltaToWorld = screenDeltaToWorld;
3912
4014
  exports.unhover3D = unhover3D;
4015
+ exports.useR3FRegister = useR3FRegister;
3913
4016
  exports.verifyRaycastHit = verifyRaycastHit;
3914
4017
  exports.verifyRaycastHitMultiPoint = verifyRaycastHitMultiPoint;
3915
4018
  exports.version = version;