@mindexec/cli 0.2.445 → 0.2.447

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.
Files changed (34) hide show
  1. package/README.md +2 -2
  2. package/package.json +6 -3
  3. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  4. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  5. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  6. package/remote-hub.js +171 -6
  7. package/scripts/remote-fast-live-rate-smoke.mjs +1 -0
  8. package/scripts/remote-fleet-render-smoke.mjs +74 -36
  9. package/scripts/remote-hub-smoke.mjs +7 -0
  10. package/server.js +2 -2
  11. package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1 -3
  12. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +199 -100
  13. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -0
  14. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +186 -360
  15. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +124 -179
  16. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +167 -24
  17. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +96 -74
  18. package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +66 -30
  19. package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +6 -3
  20. package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +35 -2
  21. package/wwwroot/_framework/{MindExecution.Core.4moacskf64.dll → MindExecution.Core.mswl4wlkm9.dll} +0 -0
  22. package/wwwroot/_framework/{MindExecution.Kernel.pa251cfsz4.dll → MindExecution.Kernel.zupeldfptg.dll} +0 -0
  23. package/wwwroot/_framework/{MindExecution.Plugins.Admin.dxyuwmxv1s.dll → MindExecution.Plugins.Admin.hq0vyqnqtn.dll} +0 -0
  24. package/wwwroot/_framework/{MindExecution.Plugins.Business.7uj5erqhhn.dll → MindExecution.Plugins.Business.pucw3o7rno.dll} +0 -0
  25. package/wwwroot/_framework/{MindExecution.Plugins.Concept.108rmsxc96.dll → MindExecution.Plugins.Concept.557tzzkrrx.dll} +0 -0
  26. package/wwwroot/_framework/{MindExecution.Plugins.Directory.ccsy3g0fe5.dll → MindExecution.Plugins.Directory.zh1ddg1fn6.dll} +0 -0
  27. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll → MindExecution.Plugins.PlanMaster.0z9pzynpu9.dll} +0 -0
  28. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.q8ml98phj9.dll → MindExecution.Plugins.YouTube.y8g77r54fa.dll} +0 -0
  29. package/wwwroot/_framework/{MindExecution.Shared.2pywq19zjy.dll → MindExecution.Shared.1ymsdbwid2.dll} +0 -0
  30. package/wwwroot/_framework/{MindExecution.Web.qucbls1wfr.dll → MindExecution.Web.bb1txupuv0.dll} +0 -0
  31. package/wwwroot/_framework/blazor.boot.json +21 -21
  32. package/wwwroot/index.html +2 -1
  33. package/wwwroot/service-worker-assets.js +36 -32
  34. package/wwwroot/service-worker.js +1 -1
@@ -9,6 +9,7 @@ window.MindMapInteractions = (function () {
9
9
  const PRIMARY_CLICK_DELAY_MS = 280;
10
10
  const BACKGROUND_PRIMARY_CLICK_DELAY_MS = 320;
11
11
  const eventHandlers = new WeakMap();
12
+ const pointerHitContexts = new WeakMap();
12
13
  const _tmpDragPos = new THREE.Vector3();
13
14
  const _tmpMultiDragPos = new THREE.Vector3();
14
15
  const BROWSER_SELECTION_LOCK_CLASS = 'mindcanvas-browser-selection-locked';
@@ -1288,21 +1289,18 @@ window.MindMapInteractions = (function () {
1288
1289
  const meshes = [];
1289
1290
  let residentHit = null;
1290
1291
  if (module.raycaster.ray.intersectPlane(module.plane, module.intersectPoint)) {
1291
- meshes.push(...getNearbyMeshes(module, module.intersectPoint));
1292
- if (meshes.length === 0 && module.nodeObjectsById?.size > 0) {
1293
- module.nodeObjectsById.forEach((entry) => {
1294
- const glObject = entry?.glObject;
1295
- if (glObject && (glObject.isMesh || glObject.children?.length > 0)) {
1296
- meshes.push(glObject);
1297
- }
1298
- });
1292
+ const nearbyMeshes = getNearbyMeshes(module, module.intersectPoint);
1293
+ meshes.push(...nearbyMeshes);
1294
+ meshes._hitTestCandidatesAvailable = nearbyMeshes._hitTestCandidatesAvailable === true;
1295
+ if (meshes.length === 0 && meshes._hitTestCandidatesAvailable !== true && module.nodeObjectsById?.size > 0) {
1296
+ appendFallbackMeshes(module, module.intersectPoint, meshes);
1299
1297
  }
1300
1298
  residentHit = module.lodRenderer?.pickResidentNodeAtWorldPoint?.(module.intersectPoint) || null;
1301
1299
  }
1302
1300
  const topHit = getTopIntersectionHit(module, module.raycaster.intersectObjects(meshes, true));
1303
1301
  const topNodeId = resolveNodeIdFromHitObject(topHit?.object);
1304
1302
  const topPriority = topHit ? getIntersectionInteractionPriority(module, topHit) : -Infinity;
1305
- const cssHit = findCssNodeHitAtClientPoint(module, e.clientX, e.clientY);
1303
+ const cssHit = getPointerCssHit(module, e);
1306
1304
  if (cssHit?.nodeId && (!topHit || (cssHit.nodeId !== topNodeId && (cssHit.priority ?? -Infinity) >= topPriority))) {
1307
1305
  return cssHit.nodeId;
1308
1306
  }
@@ -1651,65 +1649,18 @@ window.MindMapInteractions = (function () {
1651
1649
  return null;
1652
1650
  }
1653
1651
 
1654
- function getNodeProjectedClientFrame(module, entry) {
1655
- if (!module?.camera || !module?.container || !entry) {
1656
- return null;
1657
- }
1658
-
1659
- const rect = module.container.getBoundingClientRect?.();
1660
- if (!rect || rect.width <= 0 || rect.height <= 0) {
1661
- return null;
1662
- }
1663
-
1664
- const sourceObject = entry.glObject || entry.cssObject;
1665
- const position = sourceObject?.position;
1666
- if (!position) {
1667
- return null;
1668
- }
1669
-
1670
- const width = Number(
1671
- entry.glObject?.userData?.worldWidth ||
1672
- entry.cssObject?.userData?.worldWidth ||
1673
- entry.model?.width ||
1674
- entry.model?.Width ||
1675
- 0
1676
- );
1677
- const height = Number(
1678
- entry.glObject?.userData?.worldHeight ||
1679
- entry.cssObject?.userData?.worldHeight ||
1680
- entry.model?.height ||
1681
- entry.model?.Height ||
1682
- 0
1683
- );
1684
- if (!(width > 0 && height > 0)) {
1685
- return null;
1686
- }
1687
-
1688
- const corners = [
1689
- new THREE.Vector3(position.x, position.y, position.z || 0),
1690
- new THREE.Vector3(position.x + width, position.y, position.z || 0),
1691
- new THREE.Vector3(position.x, position.y - height, position.z || 0),
1692
- new THREE.Vector3(position.x + width, position.y - height, position.z || 0)
1693
- ];
1694
-
1695
- const xs = [];
1696
- const ys = [];
1697
- for (const corner of corners) {
1698
- corner.project(module.camera);
1699
- xs.push(rect.left + ((corner.x + 1) * rect.width / 2));
1700
- ys.push(rect.top + ((-corner.y + 1) * rect.height / 2));
1701
- }
1702
-
1703
- return {
1704
- left: Math.min(...xs),
1705
- right: Math.max(...xs),
1706
- top: Math.min(...ys),
1707
- bottom: Math.max(...ys)
1708
- };
1652
+ function getNodeProjectedClientFrame(module, entry, context = null, nodeId = null) {
1653
+ return window.MindMapCss3dHotPath?.getProjectedClientFrame?.(
1654
+ module,
1655
+ entry,
1656
+ context,
1657
+ getCachedViewportMetrics(module),
1658
+ nodeId
1659
+ ) || null;
1709
1660
  }
1710
1661
 
1711
- function isClientPointInsideNodeFrame(module, entry, clientX, clientY, padding = 2) {
1712
- const frame = getNodeProjectedClientFrame(module, entry);
1662
+ function isClientPointInsideNodeFrame(module, entry, clientX, clientY, padding = 2, context = null, nodeId = null) {
1663
+ const frame = getNodeProjectedClientFrame(module, entry, context, nodeId);
1713
1664
  if (!frame) {
1714
1665
  return true;
1715
1666
  }
@@ -1720,13 +1671,13 @@ window.MindMapInteractions = (function () {
1720
1671
  clientY <= frame.bottom + padding;
1721
1672
  }
1722
1673
 
1723
- function isOutOfFrameNodeDomHit(module, nodeId, clientX, clientY) {
1674
+ function isOutOfFrameNodeDomHit(module, nodeId, clientX, clientY, context = null) {
1724
1675
  const entry = nodeId ? module?.nodeObjectsById?.get?.(nodeId) : null;
1725
1676
  if (!entry?.cssObject?.element) {
1726
1677
  return false;
1727
1678
  }
1728
1679
 
1729
- return !isClientPointInsideNodeFrame(module, entry, clientX, clientY, 3);
1680
+ return !isClientPointInsideNodeFrame(module, entry, clientX, clientY, 3, context, nodeId);
1730
1681
  }
1731
1682
 
1732
1683
  function resolveNodeIdFromHitObject(object) {
@@ -1873,13 +1824,15 @@ window.MindMapInteractions = (function () {
1873
1824
  return bestHit;
1874
1825
  }
1875
1826
 
1876
- function findCssNodeHitAtClientPoint(module, clientX, clientY) {
1827
+ function findCssNodeHitAtClientPoint(module, clientX, clientY, context = null) {
1877
1828
  if (!module?.nodeObjectsById) {
1878
1829
  return null;
1879
1830
  }
1880
1831
 
1881
1832
  if (typeof document !== 'undefined' && typeof document.elementsFromPoint === 'function') {
1882
- const domStack = document.elementsFromPoint(clientX, clientY);
1833
+ const domStack = context
1834
+ ? window.MindMapCss3dHotPath.getDomStack(module, context)
1835
+ : document.elementsFromPoint(clientX, clientY);
1883
1836
  let bestHit = null;
1884
1837
  let bestPriority = -Infinity;
1885
1838
 
@@ -1905,7 +1858,7 @@ window.MindMapInteractions = (function () {
1905
1858
  continue;
1906
1859
  }
1907
1860
 
1908
- if (!isClientPointInsideNodeFrame(module, entry, clientX, clientY, 2)) {
1861
+ if (!isClientPointInsideNodeFrame(module, entry, clientX, clientY, 2, context, nodeId)) {
1909
1862
  continue;
1910
1863
  }
1911
1864
 
@@ -1929,9 +1882,13 @@ window.MindMapInteractions = (function () {
1929
1882
 
1930
1883
  let bestHit = null;
1931
1884
  let bestPriority = -Infinity;
1932
- const worldPoint = getWorldPoint(module, clientX, clientY);
1933
- const candidateIds = getCssFallbackCandidateIds(module, worldPoint);
1934
-
1885
+ const worldPoint = window.MindMapCss3dHotPath
1886
+ ? window.MindMapCss3dHotPath.getContextWorldPoint(
1887
+ context,
1888
+ () => getWorldPoint(module, clientX, clientY)
1889
+ )
1890
+ : getWorldPoint(module, clientX, clientY);
1891
+ const candidateIds = getCssFallbackCandidateIds(module, worldPoint, context);
1935
1892
  for (let i = 0; i < candidateIds.length; i++) {
1936
1893
  const nodeId = candidateIds[i];
1937
1894
  const entry = module.nodeObjectsById.get(nodeId);
@@ -1948,7 +1905,7 @@ window.MindMapInteractions = (function () {
1948
1905
  continue;
1949
1906
  }
1950
1907
 
1951
- if (!isClientPointInsideNodeFrame(module, entry, clientX, clientY, 2)) {
1908
+ if (!isClientPointInsideNodeFrame(module, entry, clientX, clientY, 2, context, nodeId)) {
1952
1909
  continue;
1953
1910
  }
1954
1911
 
@@ -1971,7 +1928,6 @@ window.MindMapInteractions = (function () {
1971
1928
  let bestHit = null;
1972
1929
  let bestPriority = -Infinity;
1973
1930
  const candidateIds = getCssFallbackCandidateIds(module, worldPoint);
1974
-
1975
1931
  for (let i = 0; i < candidateIds.length; i++) {
1976
1932
  const nodeId = candidateIds[i];
1977
1933
  const entry = module.nodeObjectsById.get(nodeId);
@@ -2134,6 +2090,7 @@ window.MindMapInteractions = (function () {
2134
2090
  }
2135
2091
 
2136
2092
  function getHitTestCandidateNodeIds(module, point, options = {}) {
2093
+ if (!module?.nodeObjectsById || !point) return null;
2137
2094
  const normalizedCellRadius = Math.max(0, Math.min(3, Math.round(Number(options.cellRadius || 1))));
2138
2095
  module?.logicWorkerBridge?.primeHitTestCandidates?.(module, {
2139
2096
  point,
@@ -2145,10 +2102,20 @@ window.MindMapInteractions = (function () {
2145
2102
  cellRadius: normalizedCellRadius
2146
2103
  });
2147
2104
  if (Array.isArray(cachedCandidateIds)) {
2105
+ module._lastHitTestCandidateSource = 'worker';
2106
+ window.MindMapCss3dHotPath?.recordHitCandidates?.(module, 'worker', cachedCandidateIds.length);
2148
2107
  return cachedCandidateIds;
2149
2108
  }
2150
2109
 
2151
- return collectLocalHitTestCandidateNodeIds(module, point, normalizedCellRadius);
2110
+ if (!module.spatialGrid || module._spatialIndexRepairState) {
2111
+ module._lastHitTestCandidateSource = 'unavailable';
2112
+ window.MindMapCss3dHotPath?.noteUnavailableIndex?.(module, 'pointer-index-unavailable');
2113
+ return null;
2114
+ }
2115
+ const localIds = collectLocalHitTestCandidateNodeIds(module, point, normalizedCellRadius);
2116
+ module._lastHitTestCandidateSource = 'spatial-grid';
2117
+ window.MindMapCss3dHotPath?.recordHitCandidates?.(module, 'spatial-grid', localIds.length);
2118
+ return localIds;
2152
2119
  }
2153
2120
 
2154
2121
  function appendMeshesForHitTestCandidateIds(module, candidateIds, meshes, options = {}) {
@@ -2188,10 +2155,14 @@ window.MindMapInteractions = (function () {
2188
2155
  function getNearbyMeshes(module, point, options = {}) {
2189
2156
  const candidateIds = getHitTestCandidateNodeIds(module, point, options);
2190
2157
  const meshes = [];
2158
+ meshes._hitTestCandidatesAvailable = Array.isArray(candidateIds);
2191
2159
  appendMeshesForHitTestCandidateIds(module, candidateIds, meshes, options);
2192
2160
  return meshes;
2193
2161
  }
2194
2162
 
2163
+ function appendFallbackMeshes(module, point, meshes) {
2164
+ (getCssFallbackCandidateIds(module, point) || []).forEach(id => { const g = module.nodeObjectsById.get(id)?.glObject; if (g && (g.isMesh || g.children?.length > 0)) meshes.push(g); });
2165
+ }
2195
2166
  function buildSelectionRectFromWorldPoints(worldStartPos, worldEndPos) {
2196
2167
  if (!worldStartPos || !worldEndPos) {
2197
2168
  return null;
@@ -2205,49 +2176,19 @@ window.MindMapInteractions = (function () {
2205
2176
  };
2206
2177
  }
2207
2178
 
2208
- function collectLocalSelectionCandidateNodeIds(module, selectionRect) {
2209
- if (!module?.spatialGrid || !selectionRect) {
2210
- return [];
2211
- }
2212
-
2213
- const cellSize = Math.max(1, Number(module.GRID_CELL_SIZE || 500));
2214
- const startCellX = Math.floor(Number(selectionRect.left || 0) / cellSize);
2215
- const endCellX = Math.floor(Number(selectionRect.right || 0) / cellSize);
2216
- const startCellY = Math.floor(Number(selectionRect.bottom || 0) / cellSize);
2217
- const endCellY = Math.floor(Number(selectionRect.top || 0) / cellSize);
2218
- const candidateIds = [];
2219
- const seenIds = new Set();
2220
-
2221
- for (let cellX = startCellX; cellX <= endCellX; cellX++) {
2222
- for (let cellY = startCellY; cellY <= endCellY; cellY++) {
2223
- const nodeIds = module.spatialGrid.get(`${cellX}:${cellY}`);
2224
- if (!nodeIds) {
2225
- continue;
2226
- }
2227
-
2228
- nodeIds.forEach((nodeId) => {
2229
- const normalizedNodeId = String(nodeId || '').trim();
2230
- if (!normalizedNodeId || seenIds.has(normalizedNodeId)) {
2231
- return;
2232
- }
2233
-
2234
- seenIds.add(normalizedNodeId);
2235
- candidateIds.push(normalizedNodeId);
2236
- });
2237
- }
2238
- }
2239
-
2240
- return candidateIds;
2241
- }
2242
-
2243
2179
  function collectSelectionBoxNodeIdsLocal(module, selectionRect) {
2244
2180
  const selectedIds = new Set();
2245
2181
  if (!module?.nodeObjectsById || !selectionRect) {
2246
2182
  return selectedIds;
2247
2183
  }
2248
2184
 
2249
- const candidateIds = collectLocalSelectionCandidateNodeIds(module, selectionRect);
2185
+ let candidateIds = window.MindMapCss3dHotPath?.collectSpatialRectCandidateIds?.(module, selectionRect) ?? null;
2186
+ if (candidateIds === null) {
2187
+ window.MindMapCss3dHotPath?.noteUnavailableIndex?.(module, 'selection-box-index-unavailable');
2188
+ candidateIds = window.MindMapCss3dHotPath?.getBoundedFallbackIds?.(module) || [];
2189
+ }
2250
2190
  const processCandidateNode = (nodeId) => {
2191
+ window.MindMapCss3dHotPath?.increment?.(module, 'selection', 'nodesVisited');
2251
2192
  const nodeEntry = module.nodeObjectsById.get(nodeId);
2252
2193
  if (!nodeEntry?.glObject?.userData) {
2253
2194
  return;
@@ -2269,12 +2210,8 @@ window.MindMapInteractions = (function () {
2269
2210
  }
2270
2211
  };
2271
2212
 
2272
- if (candidateIds.length > 0) {
2273
- for (let i = 0; i < candidateIds.length; i++) {
2274
- processCandidateNode(candidateIds[i]);
2275
- }
2276
- } else {
2277
- module.nodeObjectsById.forEach((_, nodeId) => processCandidateNode(nodeId));
2213
+ for (let i = 0; i < candidateIds.length; i++) {
2214
+ processCandidateNode(candidateIds[i]);
2278
2215
  }
2279
2216
 
2280
2217
  return selectedIds;
@@ -2329,19 +2266,28 @@ window.MindMapInteractions = (function () {
2329
2266
  });
2330
2267
  }
2331
2268
 
2332
- function getCssFallbackCandidateIds(module, worldPoint) {
2269
+ function getCssFallbackCandidateIds(module, worldPoint, context = null) {
2333
2270
  if (!module?.nodeObjectsById) {
2334
2271
  return [];
2335
2272
  }
2336
2273
 
2337
- const candidateIds = worldPoint
2338
- ? getHitTestCandidateNodeIds(module, worldPoint, { visibleOnly: false })
2339
- : [];
2340
- if (Array.isArray(candidateIds) && candidateIds.length > 0) {
2274
+ if (context?.candidateIds !== undefined) return context.candidateIds;
2275
+ const candidateIds = worldPoint ? getHitTestCandidateNodeIds(module, worldPoint, { visibleOnly: false }) : [];
2276
+ if (Array.isArray(candidateIds)) {
2277
+ if (context) {
2278
+ context.candidateIds = candidateIds;
2279
+ context.candidateSource = module._lastHitTestCandidateSource || 'spatial-grid';
2280
+ }
2341
2281
  return candidateIds;
2342
2282
  }
2343
2283
 
2344
- return Array.from(module.nodeObjectsById.keys());
2284
+ const fallbackIds = window.MindMapCss3dHotPath?.getBoundedFallbackIds?.(module) || [];
2285
+ window.MindMapCss3dHotPath?.recordHitCandidates?.(module, 'bounded-fallback', fallbackIds.length);
2286
+ if (context) {
2287
+ context.candidateIds = fallbackIds;
2288
+ context.candidateSource = 'bounded-fallback';
2289
+ }
2290
+ return fallbackIds;
2345
2291
  }
2346
2292
  // ▲▲▲ [New] ▲▲▲
2347
2293
 
@@ -2954,7 +2900,8 @@ window.MindMapInteractions = (function () {
2954
2900
  }
2955
2901
 
2956
2902
  const nodeId = getTargetNodeId(target);
2957
- if (nodeId && event && isOutOfFrameNodeDomHit(module, nodeId, event.clientX, event.clientY)) {
2903
+ if (nodeId && event && isOutOfFrameNodeDomHit(
2904
+ module, nodeId, event.clientX, event.clientY, getPointerHitContext(module, event))) {
2958
2905
  return false;
2959
2906
  }
2960
2907
 
@@ -2964,22 +2911,45 @@ window.MindMapInteractions = (function () {
2964
2911
  function getGuardedTargetNodeId(module, target, event) {
2965
2912
  const nodeId = getTargetNodeId(target);
2966
2913
  if (!nodeId || !event) {
2967
- const topCssHit = event
2968
- ? findCssNodeHitAtClientPoint(module, event.clientX, event.clientY)
2969
- : null;
2914
+ const topCssHit = event ? getPointerCssHit(module, event) : null;
2970
2915
  return topCssHit?.nodeId || nodeId;
2971
2916
  }
2972
-
2973
- const topCssHit = findCssNodeHitAtClientPoint(module, event.clientX, event.clientY);
2974
- if (topCssHit?.nodeId && topCssHit.nodeId !== nodeId) {
2975
- return topCssHit.nodeId;
2917
+ const context = getPointerHitContext(module, event);
2918
+ if (nodeId && !isOutOfFrameNodeDomHit(module, nodeId, event.clientX, event.clientY, context)) {
2919
+ const entry = module.nodeObjectsById?.get?.(nodeId) || null;
2920
+ if (entry) {
2921
+ context.cssHit = {
2922
+ nodeId,
2923
+ entry,
2924
+ priority: getEntryInteractionPriority(module, entry, nodeId)
2925
+ };
2926
+ }
2927
+ return nodeId;
2976
2928
  }
2977
-
2978
- return isOutOfFrameNodeDomHit(module, nodeId, event.clientX, event.clientY)
2979
- ? null
2980
- : nodeId;
2929
+ const topCssHit = getPointerCssHit(module, event);
2930
+ return topCssHit?.nodeId || null;
2931
+ }
2932
+ function getPointerHitContext(module, event) {
2933
+ if (!event || !module) return null;
2934
+ let context = pointerHitContexts.get(event);
2935
+ if (!context || context.module !== module) {
2936
+ context = window.MindMapCss3dHotPath.createPointerHitContext(
2937
+ module,
2938
+ event,
2939
+ getCachedViewportMetrics(module)
2940
+ );
2941
+ pointerHitContexts.set(event, context);
2942
+ }
2943
+ return context;
2944
+ }
2945
+ function getPointerCssHit(module, event) {
2946
+ const context = getPointerHitContext(module, event);
2947
+ if (!context) return null;
2948
+ if (context.cssHit === undefined) {
2949
+ context.cssHit = findCssNodeHitAtClientPoint(module, event.clientX, event.clientY, context);
2950
+ }
2951
+ return context.cssHit;
2981
2952
  }
2982
-
2983
2953
  function getMountedEmbedCanvasDragTarget(module, target, event) {
2984
2954
  if (!module || !(target instanceof Element)) {
2985
2955
  return null;
@@ -3009,7 +2979,8 @@ window.MindMapInteractions = (function () {
3009
2979
  return null;
3010
2980
  }
3011
2981
 
3012
- if (event && !isClientPointInsideNodeFrame(module, nodeEntry, event.clientX, event.clientY, 3)) {
2982
+ if (event && !isClientPointInsideNodeFrame(
2983
+ module, nodeEntry, event.clientX, event.clientY, 3, getPointerHitContext(module, event), nodeId)) {
3013
2984
  return null;
3014
2985
  }
3015
2986
 
@@ -3364,26 +3335,25 @@ window.MindMapInteractions = (function () {
3364
3335
  return;
3365
3336
  }
3366
3337
 
3367
- const selectedIds = new Set();
3368
- if (module.selectedNodeIdJs) {
3369
- selectedIds.add(String(module.selectedNodeIdJs));
3370
- }
3338
+ const selectedIds = new Set(module.selectedNodeIdJs ? [String(module.selectedNodeIdJs)] : []);
3371
3339
  module.multiSelectedNodeIds?.forEach?.(id => selectedIds.add(String(id)));
3372
3340
 
3373
- module.nodeObjectsById.forEach((entry, nodeId) => {
3374
- const contentType = String(entry?.model?.contentType ?? entry?.model?.ContentType ?? '').toLowerCase();
3375
- if (contentType !== 'embed' || !entry?.cssObject?.element) {
3376
- return;
3377
- }
3341
+ const previousIds = module._embedInteractivityNodeIds instanceof Set ? module._embedInteractivityNodeIds : new Set();
3342
+ if (previousIds.size === selectedIds.size && [...selectedIds].every(id => previousIds.has(id))) return;
3378
3343
 
3379
- const isSelected = selectedIds.has(String(nodeId || entry?.model?.id || entry?.model?.Id || ''));
3344
+ const changedIds = new Set([...previousIds, ...selectedIds]);
3345
+ window.MindMapCss3dHotPath?.increment?.(module, 'selection', 'nodesVisited', changedIds.size);
3346
+ changedIds.forEach(nodeId => {
3347
+ const entry = module.nodeObjectsById.get(nodeId);
3348
+ if (String(entry?.model?.contentType ?? entry?.model?.ContentType ?? '').toLowerCase() !== 'embed' || !entry?.cssObject?.element) return;
3349
+ const isSelected = selectedIds.has(nodeId);
3380
3350
  window.MindMapCss3DManager.syncCss3dEmbedInteractivity(entry.cssObject.element, {
3381
3351
  isInteractive: isSelected,
3382
3352
  isSelected
3383
3353
  });
3384
3354
  });
3355
+ module._embedInteractivityNodeIds = selectedIds;
3385
3356
  }
3386
-
3387
3357
  function switchPreviousSelectedNoteToWebGL(module, respectHighMode = true) {
3388
3358
  if (!module?.selectedNodeIdJs) return;
3389
3359
  const prevEntry = module.nodeObjectsById?.get(module.selectedNodeIdJs);
@@ -4235,7 +4205,6 @@ window.MindMapInteractions = (function () {
4235
4205
  }
4236
4206
 
4237
4207
  // ▼▼▼ [Debug] Click timing measurement ▼▼▼
4238
- const clickT0 = performance.now();
4239
4208
  // ▲▲▲ [Debug] ▲▲▲
4240
4209
 
4241
4210
  updatePointer(module, e);
@@ -4245,43 +4214,28 @@ window.MindMapInteractions = (function () {
4245
4214
  const meshesToIntersect = [];
4246
4215
 
4247
4216
  // ▼▼▼ [Debug] ▼▼▼
4248
- const clickT1 = performance.now();
4249
- let gridCellsChecked = 0;
4250
- let nodesChecked = 0;
4251
4217
  let residentLodHit = null;
4252
4218
  // ▲▲▲ [Debug] ▲▲▲
4253
4219
 
4254
4220
  if (module.raycaster.ray.intersectPlane(module.plane, module.intersectPoint)) {
4255
4221
  const nearby = getNearbyMeshes(module, module.intersectPoint);
4256
4222
  meshesToIntersect.push(...nearby);
4223
+ meshesToIntersect._hitTestCandidatesAvailable = nearby._hitTestCandidatesAvailable === true;
4257
4224
 
4258
4225
  // ▼▼▼ [Fix] Fallback: spatial grid가 비어있으면 전체 노드 검색 ▼▼▼
4259
- if (meshesToIntersect.length === 0 && module.nodeObjectsById.size > 0) {
4260
- console.log('[onMouseDown] Spatial grid empty, falling back to all nodes');
4261
- module.nodeObjectsById.forEach((entry) => {
4262
- const glObject = entry?.glObject;
4263
- if (!glObject) {
4264
- return;
4265
- }
4266
-
4267
- if (glObject.isMesh || (Array.isArray(glObject.children) && glObject.children.length > 0)) {
4268
- meshesToIntersect.push(glObject);
4269
- }
4270
- });
4226
+ if (meshesToIntersect.length === 0 && meshesToIntersect._hitTestCandidatesAvailable !== true && module.nodeObjectsById.size > 0) {
4227
+ appendFallbackMeshes(module, module.intersectPoint, meshesToIntersect);
4271
4228
  residentLodHit = module.lodRenderer?.pickResidentNodeAtWorldPoint?.(module.intersectPoint) || null;
4272
4229
  }
4273
4230
  // ▲▲▲ [Fix] ▲▲▲
4274
4231
  }
4275
4232
 
4276
4233
  // ▼▼▼ [Debug] ▼▼▼
4277
- const clickT2 = performance.now();
4278
4234
  // ▲▲▲ [Debug] ▲▲▲
4279
4235
 
4280
4236
  const intersects = module.raycaster.intersectObjects(meshesToIntersect, true);
4281
4237
 
4282
4238
  // ▼▼▼ [Debug] ▼▼▼
4283
- const clickT3 = performance.now();
4284
- console.log(`[Click Timing] setup=${(clickT1 - clickT0).toFixed(2)}ms | grid(${gridCellsChecked}cells, ${nodesChecked}nodes, ${meshesToIntersect.length}meshes)=${(clickT2 - clickT1).toFixed(2)}ms | raycast=${(clickT3 - clickT2).toFixed(2)}ms | TOTAL=${(clickT3 - clickT0).toFixed(2)}ms`);
4285
4239
  // ▲▲▲ [Debug] ▲▲▲
4286
4240
 
4287
4241
  const topIntersect = getTopIntersectionHit(module, intersects);
@@ -4289,7 +4243,7 @@ window.MindMapInteractions = (function () {
4289
4243
  const topIntersectPriority = topIntersect
4290
4244
  ? getIntersectionInteractionPriority(module, topIntersect)
4291
4245
  : -Infinity;
4292
- const cssFallbackHit = findCssNodeHitAtClientPoint(module, e.clientX, e.clientY);
4246
+ const cssFallbackHit = getPointerCssHit(module, e);
4293
4247
  const cssFallbackPriority = cssFallbackHit?.priority ?? -Infinity;
4294
4248
  const shouldPreferCssFallback =
4295
4249
  !!cssFallbackHit?.nodeId &&
@@ -4308,15 +4262,6 @@ window.MindMapInteractions = (function () {
4308
4262
  if (topIntersect && !shouldPreferCssFallback) {
4309
4263
  let clickedMesh = topIntersect.object;
4310
4264
 
4311
- // ▼▼▼ [Debug] Raycast Hit Log ▼▼▼
4312
- console.log('[onMouseDown] Raycast Hits:', intersects.map(i => ({
4313
- name: i.object.name,
4314
- type: i.object.type,
4315
- nodeId: i.object.userData?.nodeId || i.object.parent?.userData?.nodeId,
4316
- distance: i.distance.toFixed(2),
4317
- visible: i.object.visible, // 추가 확인
4318
- parentVisible: i.object.parent?.visible // 부모 가시성 확인
4319
- })));
4320
4265
  // ▲▲▲ [Debug] ▲▲▲
4321
4266
 
4322
4267
  // ▼▼▼ [FIX] 클릭된 대상이 'body'(히트박스)라면 부모 그룹을 선택하도록 강제 보정 ▼▼▼