@react-three-dom/core 0.4.0 → 0.6.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.4.0";
9
+ var version = "0.5.0";
10
10
 
11
11
  // src/debug.ts
12
12
  var _enabled = false;
@@ -1525,6 +1525,7 @@ function findTrackingPair(obj) {
1525
1525
  function registerSubtree(obj, store, mirror, instanceKey) {
1526
1526
  obj.traverse((child) => {
1527
1527
  if (!store.has(child) && shouldRegister(instanceKey, child)) {
1528
+ ensureAncestorChain(child, store, mirror);
1528
1529
  store.register(child);
1529
1530
  mirror.onObjectAdded(child);
1530
1531
  }
@@ -2573,6 +2574,82 @@ function circlePath(center, radiusX, radiusY, steps = 36, pressure = 0.5) {
2573
2574
  function sleep2(ms) {
2574
2575
  return new Promise((resolve) => setTimeout(resolve, ms));
2575
2576
  }
2577
+ var _v = new three.Vector3();
2578
+ function projectWorld(point, camera, size) {
2579
+ _v.set(point.x, point.y, point.z).project(camera);
2580
+ if (_v.z > 1) return null;
2581
+ return {
2582
+ x: (_v.x + 1) / 2 * size.width,
2583
+ y: (-_v.y + 1) / 2 * size.height
2584
+ };
2585
+ }
2586
+ function clickAtWorld(point, _options = {}) {
2587
+ const camera = getCamera();
2588
+ const gl = getRenderer();
2589
+ const size = getCanvasSize();
2590
+ const canvas = gl.domElement;
2591
+ const screen = projectWorld(point, camera, size);
2592
+ if (!screen) {
2593
+ r3fLog("interaction", `clickAtWorld(${point.x}, ${point.y}, ${point.z}) \u2014 behind camera, skipped`);
2594
+ return { dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: true };
2595
+ }
2596
+ r3fLog("interaction", `clickAtWorld(${point.x}, ${point.y}, ${point.z}) \u2192 screen(${Math.round(screen.x)}, ${Math.round(screen.y)})`);
2597
+ dispatchClick(canvas, screen);
2598
+ return { dispatched: true, screenPoint: screen, behindCamera: false };
2599
+ }
2600
+ function doubleClickAtWorld(point, _options = {}) {
2601
+ const camera = getCamera();
2602
+ const gl = getRenderer();
2603
+ const size = getCanvasSize();
2604
+ const canvas = gl.domElement;
2605
+ const screen = projectWorld(point, camera, size);
2606
+ if (!screen) {
2607
+ r3fLog("interaction", `doubleClickAtWorld(${point.x}, ${point.y}, ${point.z}) \u2014 behind camera, skipped`);
2608
+ return { dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: true };
2609
+ }
2610
+ r3fLog("interaction", `doubleClickAtWorld(${point.x}, ${point.y}, ${point.z}) \u2192 screen(${Math.round(screen.x)}, ${Math.round(screen.y)})`);
2611
+ dispatchDoubleClick(canvas, screen);
2612
+ return { dispatched: true, screenPoint: screen, behindCamera: false };
2613
+ }
2614
+ function contextMenuAtWorld(point, _options = {}) {
2615
+ const camera = getCamera();
2616
+ const gl = getRenderer();
2617
+ const size = getCanvasSize();
2618
+ const canvas = gl.domElement;
2619
+ const screen = projectWorld(point, camera, size);
2620
+ if (!screen) {
2621
+ r3fLog("interaction", `contextMenuAtWorld(${point.x}, ${point.y}, ${point.z}) \u2014 behind camera, skipped`);
2622
+ return { dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: true };
2623
+ }
2624
+ r3fLog("interaction", `contextMenuAtWorld(${point.x}, ${point.y}, ${point.z}) \u2192 screen(${Math.round(screen.x)}, ${Math.round(screen.y)})`);
2625
+ dispatchContextMenu(canvas, screen);
2626
+ return { dispatched: true, screenPoint: screen, behindCamera: false };
2627
+ }
2628
+ function hoverAtWorld(point, _options = {}) {
2629
+ const camera = getCamera();
2630
+ const gl = getRenderer();
2631
+ const size = getCanvasSize();
2632
+ const canvas = gl.domElement;
2633
+ const screen = projectWorld(point, camera, size);
2634
+ if (!screen) {
2635
+ r3fLog("interaction", `hoverAtWorld(${point.x}, ${point.y}, ${point.z}) \u2014 behind camera, skipped`);
2636
+ return { dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: true };
2637
+ }
2638
+ r3fLog("interaction", `hoverAtWorld(${point.x}, ${point.y}, ${point.z}) \u2192 screen(${Math.round(screen.x)}, ${Math.round(screen.y)})`);
2639
+ dispatchHover(canvas, screen);
2640
+ return { dispatched: true, screenPoint: screen, behindCamera: false };
2641
+ }
2642
+ async function clickAtWorldSequence(points, options = {}) {
2643
+ const { delayMs = 50, ...interactionOpts } = options;
2644
+ const results = [];
2645
+ for (let i = 0; i < points.length; i++) {
2646
+ results.push(clickAtWorld(points[i], interactionOpts));
2647
+ if (delayMs > 0 && i < points.length - 1) {
2648
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
2649
+ }
2650
+ }
2651
+ return results;
2652
+ }
2576
2653
 
2577
2654
  // src/highlight/SelectionManager.ts
2578
2655
  var SelectionManager = class {
@@ -2828,6 +2905,9 @@ var Highlighter = class {
2828
2905
  this._hoverPollId = null;
2829
2906
  this._lastHoveredUuid = null;
2830
2907
  this._store = null;
2908
+ /** When true, the hover poll is paused and showHoverHighlight is blocked. */
2909
+ this._hoverPollSuppressed = false;
2910
+ this._suppressTimer = null;
2831
2911
  }
2832
2912
  // -----------------------------------------------------------------------
2833
2913
  // Lifecycle
@@ -2850,6 +2930,11 @@ var Highlighter = class {
2850
2930
  this._unsubscribe = null;
2851
2931
  }
2852
2932
  this._stopHoverPolling();
2933
+ if (this._suppressTimer) {
2934
+ clearTimeout(this._suppressTimer);
2935
+ this._suppressTimer = null;
2936
+ }
2937
+ this._hoverPollSuppressed = false;
2853
2938
  this.clearHoverHighlight();
2854
2939
  this._clearAllSelectionHighlights();
2855
2940
  this._scene = null;
@@ -2877,6 +2962,7 @@ var Highlighter = class {
2877
2962
  // -----------------------------------------------------------------------
2878
2963
  /** Show a hover highlight on the given object (replaces any previous hover). */
2879
2964
  showHoverHighlight(obj) {
2965
+ if (this._hoverPollSuppressed) return;
2880
2966
  if (obj === this._hoverTarget) return;
2881
2967
  this._clearHoverVisuals();
2882
2968
  if (!this._scene) return;
@@ -2915,6 +3001,26 @@ var Highlighter = class {
2915
3001
  this.clearHoverHighlight();
2916
3002
  this._clearAllSelectionHighlights();
2917
3003
  }
3004
+ /**
3005
+ * Stop the hover poll, clear all hover state, and restart after a delay.
3006
+ * This prevents the DevTools extension from re-applying highlights after
3007
+ * inspect mode is disabled (the extension polls `$0` every 200ms and
3008
+ * keeps writing `__r3fdom_hovered__`).
3009
+ */
3010
+ suppressHoverPoll(durationMs = 1500) {
3011
+ this._hoverPollSuppressed = true;
3012
+ this._stopHoverPolling();
3013
+ this._clearHoverVisuals();
3014
+ this._lastHoveredUuid = null;
3015
+ window.__r3fdom_hovered__ = null;
3016
+ if (this._suppressTimer) clearTimeout(this._suppressTimer);
3017
+ this._suppressTimer = setTimeout(() => {
3018
+ this._hoverPollSuppressed = false;
3019
+ this._suppressTimer = null;
3020
+ window.__r3fdom_hovered__ = null;
3021
+ this._startHoverPolling();
3022
+ }, durationMs);
3023
+ }
2918
3024
  // -----------------------------------------------------------------------
2919
3025
  // Internal: selection highlights
2920
3026
  // -----------------------------------------------------------------------
@@ -2997,6 +3103,16 @@ var Highlighter = class {
2997
3103
  try {
2998
3104
  const hoveredEl = window.__r3fdom_hovered__;
2999
3105
  const uuid = hoveredEl?.getAttribute?.("data-uuid") ?? null;
3106
+ if (this._hoverPollSuppressed) {
3107
+ if (uuid) {
3108
+ window.__r3fdom_hovered__ = null;
3109
+ }
3110
+ if (this._hoverTarget) {
3111
+ this._clearHoverVisuals();
3112
+ }
3113
+ this._lastHoveredUuid = null;
3114
+ return;
3115
+ }
3000
3116
  if (uuid === this._lastHoveredUuid) return;
3001
3117
  this._lastHoveredUuid = uuid;
3002
3118
  if (!uuid) {
@@ -3119,9 +3235,12 @@ var InspectController = class {
3119
3235
  this._boundContextMenu = null;
3120
3236
  this._hoveredObject = null;
3121
3237
  this._cancelHoverReveal();
3122
- this._highlighter.clearHoverHighlight();
3238
+ this._highlighter.clearAll();
3239
+ this._highlighter.suppressHoverPoll();
3240
+ this._selectionManager.clearSelection();
3241
+ window.__r3fdom_hovered__ = null;
3123
3242
  window.__r3fdom_selected_element__ = null;
3124
- r3fLog("inspect", "InspectController disabled");
3243
+ r3fLog("inspect", "InspectController disabled \u2014 highlights and selection cleared");
3125
3244
  }
3126
3245
  /** Disable and release all resources. */
3127
3246
  dispose() {
@@ -3359,6 +3478,21 @@ function shouldRegister(instanceKey, obj) {
3359
3478
  if (filter) return filter(obj);
3360
3479
  return true;
3361
3480
  }
3481
+ function ensureAncestorChain(obj, store, mirror) {
3482
+ const chain = [];
3483
+ let cursor = obj.parent;
3484
+ while (cursor) {
3485
+ if (store.has(cursor)) break;
3486
+ chain.push(cursor);
3487
+ cursor = cursor.parent;
3488
+ }
3489
+ for (let i = chain.length - 1; i >= 0; i--) {
3490
+ const ancestor = chain[i];
3491
+ store.register(ancestor);
3492
+ mirror.onObjectAdded(ancestor);
3493
+ mirror.materialize(ancestor.uuid);
3494
+ }
3495
+ }
3362
3496
  function getStore2(canvasId = "") {
3363
3497
  return _stores.get(canvasId) ?? null;
3364
3498
  }
@@ -3430,6 +3564,11 @@ function exposeGlobalAPI(store, gl, cameraRef, selMgr, inspCtrl, mirror, canvasI
3430
3564
  const result = await drawPath(points, options);
3431
3565
  return { eventCount: result.eventCount, pointCount: result.pointCount };
3432
3566
  },
3567
+ clickAtWorld: (point, options) => clickAtWorld(point, options),
3568
+ doubleClickAtWorld: (point, options) => doubleClickAtWorld(point, options),
3569
+ contextMenuAtWorld: (point, options) => contextMenuAtWorld(point, options),
3570
+ hoverAtWorld: (point, options) => hoverAtWorld(point, options),
3571
+ clickAtWorldSequence: (points, options) => clickAtWorldSequence(points, options),
3433
3572
  select: (idOrUuid) => {
3434
3573
  const obj = store.getObject3D(idOrUuid);
3435
3574
  if (obj && selMgr) selMgr.select(obj);
@@ -3518,10 +3657,17 @@ function exposeGlobalAPI(store, gl, cameraRef, selMgr, inspCtrl, mirror, canvasI
3518
3657
  return;
3519
3658
  }
3520
3659
  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})`);
3660
+ ensureAncestorChain(obj, store, mirror);
3661
+ let count = 0;
3662
+ obj.traverse((child) => {
3663
+ if (!store.has(child)) {
3664
+ store.register(child);
3665
+ mirror?.onObjectAdded(child);
3666
+ mirror?.materialize(child.uuid);
3667
+ count++;
3668
+ }
3669
+ });
3670
+ r3fLog("bridge", `r3fRegister: "${obj.userData?.testId || obj.name || obj.uuid.slice(0, 8)}" (${obj.type}) \u2014 ${count} objects`);
3525
3671
  },
3526
3672
  r3fUnregister: (obj) => {
3527
3673
  if (!store.has(obj)) return;
@@ -3646,6 +3792,11 @@ function createStubBridge(error, canvasId) {
3646
3792
  pointerMiss: () => {
3647
3793
  },
3648
3794
  drawPath: async () => ({ eventCount: 0, pointCount: 0 }),
3795
+ clickAtWorld: () => ({ dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: false }),
3796
+ doubleClickAtWorld: () => ({ dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: false }),
3797
+ contextMenuAtWorld: () => ({ dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: false }),
3798
+ hoverAtWorld: () => ({ dispatched: false, screenPoint: { x: 0, y: 0 }, behindCamera: false }),
3799
+ clickAtWorldSequence: async () => [],
3649
3800
  select: () => {
3650
3801
  },
3651
3802
  clearSelection: () => {
@@ -3778,8 +3929,12 @@ function ThreeDom({
3778
3929
  if (mode === "auto") {
3779
3930
  if (filter) {
3780
3931
  store.addTrackedRoot(scene);
3932
+ store.register(scene);
3933
+ mirror.onObjectAdded(scene);
3781
3934
  scene.traverse((obj) => {
3935
+ if (obj === scene) return;
3782
3936
  if (filter(obj)) {
3937
+ ensureAncestorChain(obj, store, mirror);
3783
3938
  store.register(obj);
3784
3939
  mirror.onObjectAdded(obj);
3785
3940
  }
@@ -3971,8 +4126,11 @@ exports.ThreeElement = ThreeElement;
3971
4126
  exports.applyAttributes = applyAttributes;
3972
4127
  exports.circlePath = circlePath;
3973
4128
  exports.click3D = click3D;
4129
+ exports.clickAtWorld = clickAtWorld;
4130
+ exports.clickAtWorldSequence = clickAtWorldSequence;
3974
4131
  exports.computeAttributes = computeAttributes;
3975
4132
  exports.contextMenu3D = contextMenu3D;
4133
+ exports.contextMenuAtWorld = contextMenuAtWorld;
3976
4134
  exports.createFlatSnapshot = createFlatSnapshot;
3977
4135
  exports.createSnapshot = createSnapshot;
3978
4136
  exports.curvePath = curvePath;
@@ -3985,6 +4143,7 @@ exports.dispatchPointerMiss = dispatchPointerMiss;
3985
4143
  exports.dispatchUnhover = dispatchUnhover;
3986
4144
  exports.dispatchWheel = dispatchWheel;
3987
4145
  exports.doubleClick3D = doubleClick3D;
4146
+ exports.doubleClickAtWorld = doubleClickAtWorld;
3988
4147
  exports.drag3D = drag3D;
3989
4148
  exports.drawPath = drawPath;
3990
4149
  exports.enableDebug = enableDebug;
@@ -3997,6 +4156,7 @@ exports.getSelectionManager = getSelectionManager;
3997
4156
  exports.getStore = getStore2;
3998
4157
  exports.getTagForType = getTagForType;
3999
4158
  exports.hover3D = hover3D;
4159
+ exports.hoverAtWorld = hoverAtWorld;
4000
4160
  exports.isDebugEnabled = isDebugEnabled;
4001
4161
  exports.isInFrustum = isInFrustum;
4002
4162
  exports.isPatched = isPatched;