@react-three-dom/core 0.5.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
@@ -2574,6 +2574,82 @@ function circlePath(center, radiusX, radiusY, steps = 36, pressure = 0.5) {
2574
2574
  function sleep2(ms) {
2575
2575
  return new Promise((resolve) => setTimeout(resolve, ms));
2576
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
+ }
2577
2653
 
2578
2654
  // src/highlight/SelectionManager.ts
2579
2655
  var SelectionManager = class {
@@ -2829,6 +2905,9 @@ var Highlighter = class {
2829
2905
  this._hoverPollId = null;
2830
2906
  this._lastHoveredUuid = null;
2831
2907
  this._store = null;
2908
+ /** When true, the hover poll is paused and showHoverHighlight is blocked. */
2909
+ this._hoverPollSuppressed = false;
2910
+ this._suppressTimer = null;
2832
2911
  }
2833
2912
  // -----------------------------------------------------------------------
2834
2913
  // Lifecycle
@@ -2851,6 +2930,11 @@ var Highlighter = class {
2851
2930
  this._unsubscribe = null;
2852
2931
  }
2853
2932
  this._stopHoverPolling();
2933
+ if (this._suppressTimer) {
2934
+ clearTimeout(this._suppressTimer);
2935
+ this._suppressTimer = null;
2936
+ }
2937
+ this._hoverPollSuppressed = false;
2854
2938
  this.clearHoverHighlight();
2855
2939
  this._clearAllSelectionHighlights();
2856
2940
  this._scene = null;
@@ -2878,6 +2962,7 @@ var Highlighter = class {
2878
2962
  // -----------------------------------------------------------------------
2879
2963
  /** Show a hover highlight on the given object (replaces any previous hover). */
2880
2964
  showHoverHighlight(obj) {
2965
+ if (this._hoverPollSuppressed) return;
2881
2966
  if (obj === this._hoverTarget) return;
2882
2967
  this._clearHoverVisuals();
2883
2968
  if (!this._scene) return;
@@ -2916,6 +3001,26 @@ var Highlighter = class {
2916
3001
  this.clearHoverHighlight();
2917
3002
  this._clearAllSelectionHighlights();
2918
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
+ }
2919
3024
  // -----------------------------------------------------------------------
2920
3025
  // Internal: selection highlights
2921
3026
  // -----------------------------------------------------------------------
@@ -2998,6 +3103,16 @@ var Highlighter = class {
2998
3103
  try {
2999
3104
  const hoveredEl = window.__r3fdom_hovered__;
3000
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
+ }
3001
3116
  if (uuid === this._lastHoveredUuid) return;
3002
3117
  this._lastHoveredUuid = uuid;
3003
3118
  if (!uuid) {
@@ -3120,9 +3235,12 @@ var InspectController = class {
3120
3235
  this._boundContextMenu = null;
3121
3236
  this._hoveredObject = null;
3122
3237
  this._cancelHoverReveal();
3123
- this._highlighter.clearHoverHighlight();
3238
+ this._highlighter.clearAll();
3239
+ this._highlighter.suppressHoverPoll();
3240
+ this._selectionManager.clearSelection();
3241
+ window.__r3fdom_hovered__ = null;
3124
3242
  window.__r3fdom_selected_element__ = null;
3125
- r3fLog("inspect", "InspectController disabled");
3243
+ r3fLog("inspect", "InspectController disabled \u2014 highlights and selection cleared");
3126
3244
  }
3127
3245
  /** Disable and release all resources. */
3128
3246
  dispose() {
@@ -3446,6 +3564,11 @@ function exposeGlobalAPI(store, gl, cameraRef, selMgr, inspCtrl, mirror, canvasI
3446
3564
  const result = await drawPath(points, options);
3447
3565
  return { eventCount: result.eventCount, pointCount: result.pointCount };
3448
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),
3449
3572
  select: (idOrUuid) => {
3450
3573
  const obj = store.getObject3D(idOrUuid);
3451
3574
  if (obj && selMgr) selMgr.select(obj);
@@ -3669,6 +3792,11 @@ function createStubBridge(error, canvasId) {
3669
3792
  pointerMiss: () => {
3670
3793
  },
3671
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 () => [],
3672
3800
  select: () => {
3673
3801
  },
3674
3802
  clearSelection: () => {
@@ -3998,8 +4126,11 @@ exports.ThreeElement = ThreeElement;
3998
4126
  exports.applyAttributes = applyAttributes;
3999
4127
  exports.circlePath = circlePath;
4000
4128
  exports.click3D = click3D;
4129
+ exports.clickAtWorld = clickAtWorld;
4130
+ exports.clickAtWorldSequence = clickAtWorldSequence;
4001
4131
  exports.computeAttributes = computeAttributes;
4002
4132
  exports.contextMenu3D = contextMenu3D;
4133
+ exports.contextMenuAtWorld = contextMenuAtWorld;
4003
4134
  exports.createFlatSnapshot = createFlatSnapshot;
4004
4135
  exports.createSnapshot = createSnapshot;
4005
4136
  exports.curvePath = curvePath;
@@ -4012,6 +4143,7 @@ exports.dispatchPointerMiss = dispatchPointerMiss;
4012
4143
  exports.dispatchUnhover = dispatchUnhover;
4013
4144
  exports.dispatchWheel = dispatchWheel;
4014
4145
  exports.doubleClick3D = doubleClick3D;
4146
+ exports.doubleClickAtWorld = doubleClickAtWorld;
4015
4147
  exports.drag3D = drag3D;
4016
4148
  exports.drawPath = drawPath;
4017
4149
  exports.enableDebug = enableDebug;
@@ -4024,6 +4156,7 @@ exports.getSelectionManager = getSelectionManager;
4024
4156
  exports.getStore = getStore2;
4025
4157
  exports.getTagForType = getTagForType;
4026
4158
  exports.hover3D = hover3D;
4159
+ exports.hoverAtWorld = hoverAtWorld;
4027
4160
  exports.isDebugEnabled = isDebugEnabled;
4028
4161
  exports.isInFrustum = isInFrustum;
4029
4162
  exports.isPatched = isPatched;