@mindexec/cli 0.2.477 → 0.2.478

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 (145) hide show
  1. package/README.md +323 -323
  2. package/codex-model-catalog.js +202 -202
  3. package/codex-runtime.js +1608 -1608
  4. package/desktop-workspace-state.cjs +120 -120
  5. package/electron/main.cjs +517 -517
  6. package/electron/preload.cjs +17 -17
  7. package/electron/recurring-update-owner-smoke.mjs +55 -55
  8. package/electron/recurring-update-owner.cjs +97 -97
  9. package/electron/source-smoke.mjs +108 -108
  10. package/electron/update-feed-resolver.cjs +79 -79
  11. package/electron/update-feed.json +10 -10
  12. package/electron/update-manager-smoke.mjs +146 -146
  13. package/electron/update-manager.cjs +297 -297
  14. package/electron/windows-package-smoke.mjs +156 -156
  15. package/launch-bridge.cjs +236 -236
  16. package/lzo1x.js +110 -110
  17. package/mode4-atlas-worker.js +440 -440
  18. package/mode4-atlas.js +130 -130
  19. package/package.json +1 -1
  20. package/port-guard.cjs +285 -285
  21. package/remote-fast/osx-arm64/MacScreenCaptureKitH264.swift +339 -339
  22. package/remote-fast/osx-arm64/MacSystemAudioCapture.swift +214 -214
  23. package/remote-fast/osx-arm64/UnixRawCaptureHelper.mjs +80 -80
  24. package/remote-fast/osx-arm64/livedesk-raw-capture-resampler.mjs +73 -73
  25. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  26. package/remote-fast/osx-x64/MacScreenCaptureKitH264.swift +339 -339
  27. package/remote-fast/osx-x64/MacSystemAudioCapture.swift +214 -214
  28. package/remote-fast/osx-x64/UnixRawCaptureHelper.mjs +80 -80
  29. package/remote-fast/osx-x64/livedesk-raw-capture-resampler.mjs +73 -73
  30. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  31. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  32. package/remote-hub.js +3698 -3698
  33. package/scripts/auth-session-smoke.mjs +262 -262
  34. package/scripts/board-render-ownership-smoke.mjs +85 -85
  35. package/scripts/codex-model-catalog-smoke.mjs +150 -150
  36. package/scripts/codex-runtime-home-smoke.mjs +58 -58
  37. package/scripts/codex-sdk-image-live-smoke.mjs +43 -43
  38. package/scripts/codex-sdk-image-runtime-smoke.mjs +122 -122
  39. package/scripts/codex-sdk-runtime-smoke.mjs +89 -89
  40. package/scripts/desktop-update-publisher-smoke.mjs +89 -89
  41. package/scripts/desktop-update-worker-smoke.mjs +72 -72
  42. package/scripts/desktop-workspace-state-smoke.mjs +81 -81
  43. package/scripts/drive-installed-update-smoke.mjs +67 -67
  44. package/scripts/npm-fresh-install-smoke.mjs +216 -216
  45. package/scripts/publish-mindexec-desktop-updates.mjs +224 -224
  46. package/scripts/remote-agent-managed-smoke.mjs +318 -318
  47. package/scripts/remote-agent-package-smoke.mjs +64 -64
  48. package/scripts/remote-agent-ws-smoke.mjs +269 -269
  49. package/scripts/remote-fast-live-rate-smoke.mjs +343 -343
  50. package/scripts/remote-fast-mdm-browser-smoke.mjs +453 -453
  51. package/scripts/remote-fleet-render-smoke.mjs +1350 -1350
  52. package/scripts/remote-frame-ws-smoke.mjs +234 -234
  53. package/scripts/remote-http-smoke.mjs +592 -592
  54. package/scripts/remote-hub-identity-smoke.mjs +146 -146
  55. package/scripts/remote-hub-scale-smoke.mjs +124 -124
  56. package/scripts/remote-hub-smoke.mjs +699 -699
  57. package/scripts/remote-input-ws-smoke.mjs +263 -263
  58. package/scripts/remote-registry-follower-smoke.mjs +815 -815
  59. package/scripts/setup-tree-sitter-grammars.mjs +59 -59
  60. package/server.js +7690 -7690
  61. package/start-bridge.bat +32 -32
  62. package/start-bridge.sh +81 -81
  63. package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -546
  64. package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -251
  65. package/wwwroot/_content/MindExecution.Shared/css/app.css +1 -1
  66. package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +3789 -3789
  67. package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +27 -27
  68. package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1449 -1449
  69. package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -8316
  70. package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +338 -338
  71. package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -69
  72. package/wwwroot/_content/MindExecution.Shared/js/mind-map-animated-image-preview.js +270 -270
  73. package/wwwroot/_content/MindExecution.Shared/js/mind-map-automation-edge-motion.js +170 -170
  74. package/wwwroot/_content/MindExecution.Shared/js/mind-map-board-render-ownership.js +81 -81
  75. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +10506 -10506
  76. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -902
  77. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +24637 -24637
  78. package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -325
  79. package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1491 -1491
  80. package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +70 -70
  81. package/wwwroot/_content/MindExecution.Shared/js/mind-map-image-policy.js +218 -218
  82. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7065 -7065
  83. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -160
  84. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9956 -9956
  85. package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-video-placeholder.js +107 -107
  86. package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +1004 -1004
  87. package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1351 -1351
  88. package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +865 -865
  89. package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +554 -554
  90. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4744 -4744
  91. package/wwwroot/_content/MindExecution.Shared/js/mind-map-normal-boundary-optimizer.js +624 -624
  92. package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +241 -241
  93. package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2290 -2290
  94. package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +202 -202
  95. package/wwwroot/_content/MindExecution.Shared/js/mind-map-render-plan.js +1971 -1971
  96. package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +93 -93
  97. package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2391 -2391
  98. package/wwwroot/_content/MindExecution.Shared/js/mind-map-video-playback-controls.js +95 -95
  99. package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +895 -895
  100. package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +636 -636
  101. package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +45 -45
  102. package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +117 -117
  103. package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +114 -114
  104. package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +10 -10
  105. package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -6
  106. package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +7 -7
  107. package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +6 -6
  108. package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +2 -2
  109. package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +8 -8
  110. package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -30522
  111. package/wwwroot/_framework/{Google.Protobuf.9h59ukbel7.dll → Google.Protobuf.33g2gb5is0.dll} +0 -0
  112. package/wwwroot/_framework/{MindExecution.Core.ty1xewqp36.dll → MindExecution.Core.z100xo4mg0.dll} +0 -0
  113. package/wwwroot/_framework/{MindExecution.Kernel.h7rqjoo69o.dll → MindExecution.Kernel.i9cldrx8yc.dll} +0 -0
  114. package/wwwroot/_framework/{MindExecution.Plugins.Admin.wrfrcvhjde.dll → MindExecution.Plugins.Admin.48lzqddb0a.dll} +0 -0
  115. package/wwwroot/_framework/{MindExecution.Plugins.Business.csbcyjurdh.dll → MindExecution.Plugins.Business.7m8u1q142p.dll} +0 -0
  116. package/wwwroot/_framework/{MindExecution.Plugins.Concept.buomiysqjn.dll → MindExecution.Plugins.Concept.cl5wmr53od.dll} +0 -0
  117. package/wwwroot/_framework/{MindExecution.Plugins.Directory.7k00i2v769.dll → MindExecution.Plugins.Directory.slipi39041.dll} +0 -0
  118. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.85i5ocraro.dll → MindExecution.Plugins.PlanMaster.qfge05l671.dll} +0 -0
  119. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.nbolrfakte.dll → MindExecution.Plugins.YouTube.tn7v29ut77.dll} +0 -0
  120. package/wwwroot/_framework/{MindExecution.Shared.nqh4porv7x.dll → MindExecution.Shared.rjqlf0umjn.dll} +0 -0
  121. package/wwwroot/_framework/{MindExecution.Web.211q3bf4mr.dll → MindExecution.Web.rwawrxxk38.dll} +0 -0
  122. package/wwwroot/_framework/blazor.boot.json +23 -23
  123. package/wwwroot/_headers +40 -40
  124. package/wwwroot/_redirects +1 -1
  125. package/wwwroot/index.html +772 -772
  126. package/wwwroot/js/marketing-tool.js +180 -180
  127. package/wwwroot/manifest.webmanifest +23 -23
  128. package/wwwroot/service-worker-assets.js +78 -106
  129. package/wwwroot/service-worker.js +33 -33
  130. package/tree-sitter-grammars/README.md +0 -18
  131. package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
  132. package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
  133. package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
  134. package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
  135. package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
  136. package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
  137. package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
  138. package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
  139. package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
  140. package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
  141. package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
  142. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +0 -1059
  143. package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +0 -434
  144. package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +0 -372
  145. package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +0 -1258
@@ -1,902 +1,902 @@
1
- (function () {
2
- 'use strict';
3
-
4
- const BUILD_ID = '20260724-near-css3d-hotpath-v001';
5
- const projectedCorners = Array.from({ length: 4 }, () => new THREE.Vector3());
6
-
7
- function createPerfCounters() {
8
- return {
9
- buildId: BUILD_ID,
10
- hitTest: {
11
- fullBoardFallbackCount: 0,
12
- candidateSource: '',
13
- candidateCount: 0,
14
- elementsFromPointCalls: 0,
15
- projectedFrameCalculations: 0
16
- },
17
- selection: {
18
- nodesVisited: 0,
19
- domQueries: 0
20
- },
21
- css3d: {
22
- visibilityPatchEntered: 0,
23
- visibilityPatchExited: 0,
24
- visibilityPatchFallbacks: 0,
25
- sceneObjectsVisited: 0
26
- }
27
- };
28
- }
29
-
30
- function ensurePerfCounters(module) {
31
- if (!module) return null;
32
- if (!module._nearCss3dPerfCounters ||
33
- module._nearCss3dPerfCounters.buildId !== BUILD_ID) {
34
- module._nearCss3dPerfCounters = createPerfCounters();
35
- }
36
- return module._nearCss3dPerfCounters;
37
- }
38
-
39
- function increment(module, section, key, amount = 1) {
40
- const counters = ensurePerfCounters(module);
41
- const target = counters?.[section];
42
- if (!target || !(key in target)) return;
43
- target[key] = Number(target[key] || 0) + Number(amount || 0);
44
- }
45
-
46
- function recordHitCandidates(module, source, count) {
47
- const hitTest = ensurePerfCounters(module)?.hitTest;
48
- if (!hitTest) return;
49
- hitTest.candidateSource = String(source || '');
50
- hitTest.candidateCount = Math.max(0, Number(count || 0));
51
- }
52
-
53
- function getPerfSnapshot(module) {
54
- const counters = ensurePerfCounters(module);
55
- if (!counters) return null;
56
- return {
57
- buildId: counters.buildId,
58
- hitTest: { ...counters.hitTest },
59
- selection: { ...counters.selection },
60
- css3d: { ...counters.css3d }
61
- };
62
- }
63
-
64
- function resetPerfCounters(module) {
65
- if (!module) return false;
66
- module._nearCss3dPerfCounters = createPerfCounters();
67
- return true;
68
- }
69
-
70
- function createPointerHitContext(module, event, viewportMetrics = null) {
71
- return {
72
- module,
73
- event,
74
- clientX: Number(event?.clientX || 0),
75
- clientY: Number(event?.clientY || 0),
76
- viewportMetrics,
77
- domStack: undefined,
78
- worldPoint: undefined,
79
- candidateIds: undefined,
80
- candidateSource: '',
81
- cssHit: undefined,
82
- projectedFrames: new Map()
83
- };
84
- }
85
-
86
- function getDomStack(module, context) {
87
- if (!context) return [];
88
- if (context.domStack !== undefined) return context.domStack;
89
- increment(module, 'hitTest', 'elementsFromPointCalls');
90
- context.domStack = typeof document !== 'undefined' &&
91
- typeof document.elementsFromPoint === 'function'
92
- ? document.elementsFromPoint(context.clientX, context.clientY)
93
- : [];
94
- return context.domStack;
95
- }
96
-
97
- function getContextWorldPoint(context, factory) {
98
- if (!context) return typeof factory === 'function' ? factory() : null;
99
- if (context.worldPoint !== undefined) return context.worldPoint;
100
- const point = typeof factory === 'function' ? factory() : null;
101
- context.worldPoint = point
102
- ? new THREE.Vector3(Number(point.x || 0), Number(point.y || 0), Number(point.z || 0))
103
- : null;
104
- return context.worldPoint;
105
- }
106
-
107
- function getContextCandidateIds(module, context, factory, source = '') {
108
- if (!context) {
109
- const ids = typeof factory === 'function' ? factory() : [];
110
- recordHitCandidates(module, source, Array.isArray(ids) ? ids.length : 0);
111
- return ids;
112
- }
113
- if (context.candidateIds !== undefined) return context.candidateIds;
114
- context.candidateIds = typeof factory === 'function' ? factory() : [];
115
- context.candidateSource = String(source || '');
116
- recordHitCandidates(
117
- module,
118
- context.candidateSource,
119
- Array.isArray(context.candidateIds) ? context.candidateIds.length : 0
120
- );
121
- return context.candidateIds;
122
- }
123
-
124
- function getProjectedClientFrame(module, entry, context, viewportMetrics, cacheKey = null) {
125
- if (!module?.camera || !entry) return null;
126
- const metrics = context?.viewportMetrics || viewportMetrics || null;
127
- if (!metrics || !(metrics.width > 0 && metrics.height > 0)) return null;
128
-
129
- const frameKey = cacheKey || entry;
130
- const cachedFrame = context?.projectedFrames?.get?.(frameKey);
131
- if (cachedFrame) return cachedFrame;
132
-
133
- const sourceObject = entry.glObject || entry.cssObject;
134
- const position = sourceObject?.position;
135
- if (!position) return null;
136
- const width = Number(
137
- entry.glObject?.userData?.worldWidth ??
138
- entry.cssObject?.userData?.worldWidth ??
139
- entry.model?.width ??
140
- entry.model?.Width ??
141
- 0
142
- );
143
- const height = Number(
144
- entry.glObject?.userData?.worldHeight ??
145
- entry.cssObject?.userData?.worldHeight ??
146
- entry.model?.height ??
147
- entry.model?.Height ??
148
- 0
149
- );
150
- if (!(width > 0 && height > 0)) return null;
151
-
152
- const z = Number(position.z || 0);
153
- projectedCorners[0].set(position.x, position.y, z);
154
- projectedCorners[1].set(position.x + width, position.y, z);
155
- projectedCorners[2].set(position.x, position.y - height, z);
156
- projectedCorners[3].set(position.x + width, position.y - height, z);
157
-
158
- let left = Infinity;
159
- let right = -Infinity;
160
- let top = Infinity;
161
- let bottom = -Infinity;
162
- for (let i = 0; i < projectedCorners.length; i++) {
163
- const corner = projectedCorners[i].project(module.camera);
164
- const x = Number(metrics.left || 0) + ((corner.x + 1) * metrics.width / 2);
165
- const y = Number(metrics.top || 0) + ((-corner.y + 1) * metrics.height / 2);
166
- if (x < left) left = x;
167
- if (x > right) right = x;
168
- if (y < top) top = y;
169
- if (y > bottom) bottom = y;
170
- }
171
-
172
- const frame = { left, right, top, bottom };
173
- context?.projectedFrames?.set?.(frameKey, frame);
174
- increment(module, 'hitTest', 'projectedFrameCalculations');
175
- return frame;
176
- }
177
-
178
- function getDomRefs(entry, module = null, cssObjectOverride = null) {
179
- const cssObject = cssObjectOverride || entry?.cssObject || null;
180
- const wrapper = cssObject?.element || null;
181
- if (!wrapper) return null;
182
- const version = Number(entry?._css3dDomVersion || cssObject?.userData?._domVersion || 0);
183
- const firstChild = wrapper.firstElementChild || null;
184
- const cached = entry?.domRefs || cssObject?.userData?.domRefs || null;
185
- if (cached?.wrapper === wrapper &&
186
- cached.firstChild === firstChild &&
187
- cached.version === version) {
188
- return cached;
189
- }
190
-
191
- const query = (selector) => {
192
- increment(module, 'selection', 'domQueries');
193
- return wrapper.querySelector?.(selector) || null;
194
- };
195
- const refs = {
196
- version,
197
- wrapper,
198
- firstChild,
199
- innerRoot: query('.node-container, .map-node, .css3d-dynamic-node'),
200
- response: query('.node-response, [id^="node-response-"]'),
201
- textarea: query('textarea, [contenteditable="true"]'),
202
- scrollable: query('.csv-table-scroll, .node-response, .note-content, .markdown-body, .code-body, .map-node-memo__body-view'),
203
- memoTitle: query('.map-node-memo__title'),
204
- memoBody: query('.map-node-memo__body'),
205
- memoBodyView: query('.map-node-memo__body-view'),
206
- resizeHandles: Array.from(wrapper.querySelectorAll?.('.resize-handle, [data-resize-handle]') || [])
207
- };
208
- if (entry) entry.domRefs = refs;
209
- cssObject.userData ||= {};
210
- cssObject.userData.domRefs = refs;
211
- return refs;
212
- }
213
-
214
- function invalidateDomRefs(entry, cssObjectOverride = null) {
215
- const cssObject = cssObjectOverride || entry?.cssObject || null;
216
- const nextVersion = Math.max(
217
- Number(entry?._css3dDomVersion || 0),
218
- Number(cssObject?.userData?._domVersion || 0)
219
- ) + 1;
220
- if (entry) {
221
- entry._css3dDomVersion = nextVersion;
222
- entry.domRefs = null;
223
- }
224
- if (cssObject) {
225
- cssObject.userData ||= {};
226
- cssObject.userData._domVersion = nextVersion;
227
- cssObject.userData.domRefs = null;
228
- }
229
- return nextVersion;
230
- }
231
-
232
- function invalidateDomRefsByCssObject(module, cssObject, nodeId = '') {
233
- const normalizedId = String(nodeId || cssObject?.userData?.nodeId || '').trim();
234
- const entry = normalizedId ? module?.nodeObjectsById?.get?.(normalizedId) : null;
235
- return invalidateDomRefs(entry, cssObject);
236
- }
237
-
238
- function installTextSource(helpers = {}) {
239
- const styleState = new WeakMap();
240
- const suspendedClass = 'mind-map-text-overlay-source-suspended';
241
- const suspendedAttr = 'data-text-overlay-source-suspended';
242
-
243
- function getModule() {
244
- return helpers.getModule?.() || null;
245
- }
246
-
247
- function getTextOverlaySourceTargets(nodeEntry) {
248
- if (!nodeEntry?.cssObject?.element || !helpers.supportsTextOverlay?.(nodeEntry.model)) return [];
249
- const module = getModule();
250
- const refs = getDomRefs(nodeEntry, module);
251
- if (helpers.getTextOverlayRenderMode?.(nodeEntry) === 'memo') {
252
- return [refs?.memoTitle, refs?.memoBodyView].filter(element =>
253
- !!element && (element.style.display !== 'none' || element.offsetWidth > 0 || element.offsetHeight > 0));
254
- }
255
- const root = helpers.getTextOverlayRootElement?.(nodeEntry);
256
- if (!root?.children?.length) return [];
257
- return Array.from(root.children).filter(element =>
258
- !!element &&
259
- !element.classList?.contains?.('resize-handle') &&
260
- (element.style.display !== 'none' || element.offsetWidth > 0 || element.offsetHeight > 0));
261
- }
262
-
263
- function ensureTextOverlaySourceStyleState(element) {
264
- if (!element) return null;
265
- let state = styleState.get(element) || null;
266
- if (!state) {
267
- state = {
268
- opacity: element.style.opacity,
269
- pointerEvents: element.style.pointerEvents,
270
- userSelect: element.style.userSelect,
271
- webkitUserSelect: element.style.webkitUserSelect,
272
- caretColor: element.style.caretColor,
273
- contentVisibility: element.style.contentVisibility,
274
- containIntrinsicSize: element.style.containIntrinsicSize,
275
- contain: element.style.contain
276
- };
277
- styleState.set(element, state);
278
- }
279
- return state;
280
- }
281
-
282
- function resetTextOverlaySourcePresentation(element) {
283
- if (!element) return;
284
- element.classList?.remove?.(suspendedClass);
285
- element.removeAttribute?.(suspendedAttr);
286
- element.style.opacity = '';
287
- element.style.pointerEvents = '';
288
- element.style.userSelect = '';
289
- element.style.webkitUserSelect = '';
290
- element.style.caretColor = '';
291
- element.style.contentVisibility = '';
292
- element.style.containIntrinsicSize = '';
293
- element.style.contain = '';
294
- }
295
-
296
- function suspendTextOverlaySourceTarget(element) {
297
- if (!element) return;
298
- ensureTextOverlaySourceStyleState(element);
299
- element.classList?.add?.(suspendedClass);
300
- element.setAttribute?.(suspendedAttr, 'true');
301
- element.style.pointerEvents = 'none';
302
- element.style.userSelect = 'none';
303
- element.style.webkitUserSelect = 'none';
304
- element.style.caretColor = 'transparent';
305
- element.style.opacity = '0';
306
- element.style.contain = 'layout style paint';
307
- element.style.contentVisibility = 'visible';
308
- element.style.containIntrinsicSize = '';
309
- }
310
-
311
- function restoreTextOverlaySourceTarget(element) {
312
- if (!element) return;
313
- const state = styleState.get(element) || null;
314
- if (!state) {
315
- resetTextOverlaySourcePresentation(element);
316
- return;
317
- }
318
- element.classList?.remove?.(suspendedClass);
319
- element.removeAttribute?.(suspendedAttr);
320
- Object.assign(element.style, state);
321
- styleState.delete(element);
322
- }
323
-
324
- function stripTextOverlayPresentationOverrides(root) {
325
- if (!root) return;
326
- if (root.hasAttribute?.(suspendedAttr) || root.classList?.contains?.(suspendedClass)) {
327
- resetTextOverlaySourcePresentation(root);
328
- }
329
- root.querySelectorAll?.(`[${suspendedAttr}], .${suspendedClass}`)
330
- ?.forEach?.(resetTextOverlaySourcePresentation);
331
- }
332
-
333
- function getNativeTextSelectionSourceNodeId(module) {
334
- return String(module?._nativeTextSelectionSourceNodeId || '').trim();
335
- }
336
-
337
- function hasNativeTextSelectionSource(module, nodeId = null) {
338
- const activeId = getNativeTextSelectionSourceNodeId(module);
339
- if (!activeId) return false;
340
- return nodeId === null || activeId === String(nodeId || '').trim();
341
- }
342
-
343
- function setNativeTextSelectionOwnerClass(module, nodeId = null) {
344
- if (!module) return false;
345
- const nextId = String(nodeId || '').trim();
346
- const previousId = String(module._nativeTextSelectionOwnerNodeId || '').trim();
347
- if (previousId === nextId) return true;
348
-
349
- const getWrapper = (id) => {
350
- const entry = id ? module.nodeObjectsById?.get?.(id) : null;
351
- return getDomRefs(entry, module)?.wrapper || null;
352
- };
353
- const previousWrapper = getWrapper(previousId);
354
- const nextWrapper = getWrapper(nextId);
355
- if (previousWrapper) {
356
- previousWrapper.classList?.remove?.('is-text-selection-owner');
357
- increment(module, 'selection', 'nodesVisited');
358
- }
359
- if (nextWrapper) {
360
- nextWrapper.classList?.add?.('is-text-selection-owner');
361
- increment(module, 'selection', 'nodesVisited');
362
- }
363
- module._nativeTextSelectionOwnerNodeId = nextId || null;
364
- return true;
365
- }
366
-
367
- function activateNativeTextSelectionSource(module, nodeId) {
368
- const id = String(nodeId || '').trim();
369
- if (!module || !id) return false;
370
- module._nativeTextSelectionSourceNodeId = id;
371
- setNativeTextSelectionOwnerClass(module, id);
372
- helpers.ensureTextOverlaySourceVisible?.(module, id);
373
- return true;
374
- }
375
-
376
- function clearNativeTextSelectionSource(module, nodeId = null) {
377
- const activeId = getNativeTextSelectionSourceNodeId(module);
378
- const requestedId = nodeId === null ? activeId : String(nodeId || '').trim();
379
- if (!module || !activeId || !requestedId || activeId !== requestedId) return false;
380
- module._nativeTextSelectionSourceNodeId = null;
381
- setNativeTextSelectionOwnerClass(module, null);
382
- return true;
383
- }
384
-
385
- function isTextOverlaySelectionNode(module, nodeEntry) {
386
- const contentType = String(nodeEntry?.model?.contentType ?? nodeEntry?.model?.ContentType ?? '').toLowerCase();
387
- if (contentType !== 'text' && contentType !== 'markdown') return false;
388
- const nodeId = String(helpers.getNodeId?.(nodeEntry?.model) || '').trim();
389
- return hasNativeTextSelectionSource(module, nodeId);
390
- }
391
-
392
- function setTextOverlaySourceActive(nodeEntry, isActive) {
393
- if (helpers.isCss3dMediaNode?.(nodeEntry?.model)) {
394
- helpers.restoreCss3dMediaOverlayState?.(nodeEntry.model, nodeEntry.cssObject);
395
- nodeEntry._textOverlayActive = false;
396
- nodeEntry._css3dSourceSuspended = false;
397
- return;
398
- }
399
- const host = helpers.getTextOverlayHostElement?.(nodeEntry);
400
- if (!host?.classList) return;
401
- host.classList.toggle('mind-map-text-overlay-active', !!isActive);
402
- getTextOverlaySourceTargets(nodeEntry).forEach(target => {
403
- if (isActive) suspendTextOverlaySourceTarget(target);
404
- else restoreTextOverlaySourceTarget(target);
405
- });
406
- nodeEntry._textOverlayActive = nodeEntry._css3dSourceSuspended = !!isActive;
407
- }
408
-
409
- return {
410
- getTextOverlaySourceTargets,
411
- ensureTextOverlaySourceStyleState,
412
- resetTextOverlaySourcePresentation,
413
- suspendTextOverlaySourceTarget,
414
- restoreTextOverlaySourceTarget,
415
- stripTextOverlayPresentationOverrides,
416
- getNativeTextSelectionSourceNodeId,
417
- hasNativeTextSelectionSource,
418
- setNativeTextSelectionOwnerClass,
419
- activateNativeTextSelectionSource,
420
- clearNativeTextSelectionSource,
421
- isTextOverlaySelectionNode,
422
- setTextOverlaySourceActive
423
- };
424
- }
425
-
426
- function installOverlayGeometry(helpers = {}) {
427
- function getTextOverlayPlacementInfo(module, element) {
428
- if (!module?.container || !element) return null;
429
- const rect = element.getBoundingClientRect();
430
- const containerRect = module.container.getBoundingClientRect();
431
- const width = Math.max(0, rect.width || 0);
432
- const height = Math.max(0, rect.height || 0);
433
- if (width < 1 || height < 1) return null;
434
- const left = rect.left - containerRect.left;
435
- const top = rect.top - containerRect.top;
436
- return {
437
- left,
438
- top,
439
- width,
440
- height,
441
- baseWidth: Math.max(1, Number(element.offsetWidth || width || 1)),
442
- baseHeight: Math.max(1, Number(element.offsetHeight || height || 1)),
443
- right: left + width,
444
- bottom: top + height
445
- };
446
- }
447
-
448
- function doesTextOverlayRectOverlap(a, b, padding = 0) {
449
- if (!a || !b) return false;
450
- const inset = Number.isFinite(Number(padding)) ? Number(padding) : 0;
451
- return !(
452
- (a.left + a.width + inset) < b.left ||
453
- (b.left + b.width + inset) < a.left ||
454
- (a.top + a.height + inset) < b.top ||
455
- (b.top + b.height + inset) < a.top
456
- );
457
- }
458
-
459
- function isTextOverlayPlacementVisible(module, placement, options = {}) {
460
- if (!module?.container || !placement) return false;
461
- const { ignoreMinSize = false } = options;
462
- if (!ignoreMinSize &&
463
- (placement.width < Number(helpers.minWidth || 0) ||
464
- placement.height < Number(helpers.minHeight || 0))) {
465
- return false;
466
- }
467
- const maxWidth = Number(module.container.clientWidth || 0);
468
- const maxHeight = Number(module.container.clientHeight || 0);
469
- if (!(maxWidth > 0) || !(maxHeight > 0)) return false;
470
- return !(
471
- placement.right < 0 ||
472
- placement.left > maxWidth ||
473
- placement.bottom < 0 ||
474
- placement.top > maxHeight
475
- );
476
- }
477
-
478
- function getTextOverlayPriority(module, nodeEntry) {
479
- const nodeModel = nodeEntry?.model;
480
- const nodeId = String(helpers.getNodeId?.(nodeModel) || '').trim();
481
- if (!nodeModel || !nodeId) return Number.NEGATIVE_INFINITY;
482
- let priority = 0;
483
- if (String(module?.selectedNodeIdJs || '').trim() === nodeId) priority += 100000;
484
- else if (module?.multiSelectedNodeIds?.has?.(nodeId)) priority += 50000;
485
- const width = Math.max(1, Number(nodeModel.width || nodeModel.Width || 0));
486
- const height = Math.max(1, Number(nodeModel.height || nodeModel.Height || 0));
487
- priority += Math.min(6000, (width * height) / 18);
488
- if (module?.camera) {
489
- const centerX = Number(nodeModel.positionX ?? nodeModel.PositionX ?? nodeModel.x ?? nodeModel.X ?? 0) + (width * 0.5);
490
- const centerY = Number(nodeModel.positionY ?? nodeModel.PositionY ?? nodeModel.y ?? nodeModel.Y ?? 0) - (height * 0.5);
491
- priority += Math.max(
492
- 0,
493
- 24000 -
494
- Math.abs(centerX - Number(module.camera.position?.x || 0)) -
495
- Math.abs(centerY - Number(module.camera.position?.y || 0))
496
- );
497
- }
498
- const zIndex = Number(nodeEntry?.cssObject?.element?.style?.zIndex || 0);
499
- if (Number.isFinite(zIndex)) priority += zIndex * 10;
500
- return priority;
501
- }
502
-
503
- function isAlwaysTextOverlayNode(nodeEntry) {
504
- const contentType = String(
505
- nodeEntry?.model?.contentType ??
506
- nodeEntry?.model?.ContentType ??
507
- ''
508
- ).toLowerCase();
509
- return contentType === 'markdown' || contentType === 'text';
510
- }
511
-
512
- function getTextOverlayContentSignature(nodeEntry) {
513
- const model = nodeEntry?.model || {};
514
- const contentType = String(model.contentType ?? model.ContentType ?? '').toLowerCase();
515
- const prompt = String(model.prompt ?? model.Prompt ?? '');
516
- const response = String(model.response ?? model.Response ?? '');
517
- const isLoading = (model.isLoading ?? model.IsLoading ?? false) ? '1' : '0';
518
- const width = Math.max(0, Number(model.width ?? model.Width ?? 0));
519
- const height = Math.max(0, Number(model.height ?? model.Height ?? 0));
520
- return `${contentType}||${prompt}||${response}||${isLoading}||${width}||${height}`;
521
- }
522
-
523
- function getTextOverlayLayoutSignature(sourceElement, mode = 'full', hostPlacement = null) {
524
- if (!sourceElement) return '';
525
- if (mode === 'memo') {
526
- const titleInput = sourceElement.querySelector('.map-node-memo__title');
527
- const bodyView = sourceElement.querySelector('.map-node-memo__body-view');
528
- const hostWidth = Math.round(Number(hostPlacement?.baseWidth || sourceElement.offsetWidth || 0));
529
- const hostHeight = Math.round(Number(hostPlacement?.baseHeight || sourceElement.offsetHeight || 0));
530
- const titleSignature = titleInput
531
- ? `${titleInput.offsetLeft}:${titleInput.offsetTop}:${titleInput.offsetWidth}:${titleInput.offsetHeight}:${titleInput.style.display}`
532
- : 'title:none';
533
- const bodySignature = bodyView
534
- ? `${bodyView.offsetLeft}:${bodyView.offsetTop}:${bodyView.offsetWidth}:${bodyView.offsetHeight}:${bodyView.style.display}`
535
- : 'body:none';
536
- return `${mode}||${hostWidth}x${hostHeight}||${titleSignature}||${bodySignature}`;
537
- }
538
- const rootWidth = Math.round(Number(sourceElement.offsetWidth || 0));
539
- const rootHeight = Math.round(Number(sourceElement.offsetHeight || 0));
540
- const childSignature = Array.from(sourceElement.children || [])
541
- .map(child => `${child.tagName}:${child.offsetWidth}:${child.offsetHeight}:${child.style.display}`)
542
- .join('|');
543
- return `${mode}||${rootWidth}x${rootHeight}||${childSignature}`;
544
- }
545
-
546
- return {
547
- getTextOverlayPlacementInfo,
548
- doesTextOverlayRectOverlap,
549
- isTextOverlayPlacementVisible,
550
- getTextOverlayPriority,
551
- isAlwaysTextOverlayNode,
552
- getTextOverlayContentSignature,
553
- getTextOverlayLayoutSignature
554
- };
555
- }
556
-
557
- function getNodeBounds(entry) {
558
- const shared = window.MindMapNodeBounds?.getNodeWorldBounds?.(entry);
559
- if (shared) return shared;
560
- const model = entry?.model || null;
561
- const object = entry?.glObject || entry?.cssObject || null;
562
- if (!model && !object) return null;
563
- const width = Math.max(1, Number(
564
- entry?.glObject?.userData?.worldWidth ??
565
- entry?.cssObject?.userData?.worldWidth ??
566
- model?.width ??
567
- model?.Width ??
568
- 400
569
- ));
570
- const height = Math.max(1, Number(
571
- entry?.glObject?.userData?.worldHeight ??
572
- entry?.cssObject?.userData?.worldHeight ??
573
- model?.height ??
574
- model?.Height ??
575
- 200
576
- ));
577
- const x = Number(object?.position?.x ?? model?.positionX ?? model?.x ?? 0);
578
- const y = Number(object?.position?.y ?? model?.positionY ?? model?.y ?? 0);
579
- return { minX: x, maxX: x + width, minY: y - height, maxY: y };
580
- }
581
-
582
- function getSpatialGridKeysForBounds(module, entry) {
583
- const bounds = getNodeBounds(entry);
584
- if (!bounds) return new Set();
585
- const cellSize = Math.max(1, Number(module?.GRID_CELL_SIZE || 500));
586
- const keys = new Set();
587
- const startX = Math.floor(bounds.minX / cellSize);
588
- const endX = Math.floor(bounds.maxX / cellSize);
589
- const startY = Math.floor(bounds.minY / cellSize);
590
- const endY = Math.floor(bounds.maxY / cellSize);
591
- for (let x = startX; x <= endX; x++) {
592
- for (let y = startY; y <= endY; y++) keys.add(`${x}:${y}`);
593
- }
594
- return keys;
595
- }
596
-
597
- function setEntrySpatialKeys(entry, keys) {
598
- if (!entry) return;
599
- entry._spatialGridKeys = keys;
600
- if (entry.glObject) {
601
- entry.glObject.userData ||= {};
602
- entry.glObject.userData.spatialGridKeys = keys;
603
- }
604
- if (entry.cssObject) {
605
- entry.cssObject.userData ||= {};
606
- entry.cssObject.userData.spatialGridKeys = keys;
607
- }
608
- }
609
-
610
- function collectSpatialRectCandidateIds(module, rect) {
611
- if (!module?.spatialGrid || module._spatialIndexRepairState || !rect) return null;
612
- const cellSize = Math.max(1, Number(module.GRID_CELL_SIZE || 500));
613
- const ids = [];
614
- const seen = new Set();
615
- const startX = Math.floor(Number(rect.left || 0) / cellSize);
616
- const endX = Math.floor(Number(rect.right || 0) / cellSize);
617
- const startY = Math.floor(Number(rect.bottom || 0) / cellSize);
618
- const endY = Math.floor(Number(rect.top || 0) / cellSize);
619
- for (let x = startX; x <= endX; x++) {
620
- for (let y = startY; y <= endY; y++) {
621
- module.spatialGrid.get(`${x}:${y}`)?.forEach?.(nodeId => {
622
- const id = String(nodeId || '').trim();
623
- if (id && !seen.has(id)) {
624
- seen.add(id);
625
- ids.push(id);
626
- }
627
- });
628
- }
629
- }
630
- return ids;
631
- }
632
-
633
- function getBoundedFallbackIds(module) {
634
- const ids = new Set();
635
- const add = (value) => {
636
- const id = String(value || '').trim();
637
- if (id && module?.nodeObjectsById?.has?.(id)) ids.add(id);
638
- };
639
- module?._visibleIds?.forEach?.(add);
640
- module?._prevVisibleIds?.forEach?.(add);
641
- add(module?.selectedNodeIdJs);
642
- module?.multiSelectedNodeIds?.forEach?.(add);
643
- add(module?.draggedNodeId);
644
- add(module?._nativeTextSelectionSourceNodeId);
645
- add(module?._editingOverlayState?.nodeId);
646
- add(module?._textOverlayV2State?.editing?.nodeId);
647
- module?.lodRenderer?._nearCssVisibleIds?.forEach?.(add);
648
- module?.lodRenderer?._lodImageCssFallbackNodeIds?.forEach?.(add);
649
- module?.lodRenderer?._lodTemplateCssNodeIds?.forEach?.(add);
650
- return Array.from(ids);
651
- }
652
-
653
- function scheduleSpatialIndexRepair(module, reason = 'index-unavailable') {
654
- if (!module?.nodeObjectsById || module._spatialIndexRepairState) return false;
655
- const ownerMap = module.nodeObjectsById;
656
- const state = {
657
- ownerMap,
658
- iterator: ownerMap.entries(),
659
- reason: String(reason || 'index-unavailable'),
660
- processed: 0
661
- };
662
- module._spatialIndexRepairState = state;
663
- module.spatialGrid = new Map();
664
- window.MindCanvasTrace?.emit?.('hitTest.spatialRepairScheduled', {
665
- reason: state.reason,
666
- nodeCount: ownerMap.size
667
- });
668
-
669
- const schedule = (callback) => {
670
- if (typeof requestIdleCallback === 'function') requestIdleCallback(callback, { timeout: 120 });
671
- else setTimeout(() => callback(null), 0);
672
- };
673
- const run = (deadline) => {
674
- if (module._spatialIndexRepairState !== state || module.nodeObjectsById !== ownerMap) return;
675
- let processedThisSlice = 0;
676
- while (processedThisSlice < 128 &&
677
- (!deadline?.timeRemaining || deadline.timeRemaining() > 1)) {
678
- const next = state.iterator.next();
679
- if (next.done) {
680
- module._spatialIndexRepairState = null;
681
- module.logicWorkerBridge?.rebuildNodeTables?.(module, {
682
- reason: 'hit-test-spatial-repair'
683
- });
684
- window.MindCanvasTrace?.emit?.('hitTest.spatialRepairCompleted', {
685
- reason: state.reason,
686
- processed: state.processed
687
- });
688
- return;
689
- }
690
- const [nodeId, entry] = next.value;
691
- const keys = getSpatialGridKeysForBounds(module, entry);
692
- keys.forEach(key => {
693
- if (!module.spatialGrid.has(key)) module.spatialGrid.set(key, new Set());
694
- module.spatialGrid.get(key).add(nodeId);
695
- });
696
- setEntrySpatialKeys(entry, keys);
697
- processedThisSlice++;
698
- state.processed++;
699
- }
700
- schedule(run);
701
- };
702
- schedule(run);
703
- return true;
704
- }
705
-
706
- function noteUnavailableIndex(module, reason = 'index-unavailable') {
707
- window.MindCanvasTrace?.emit?.('hitTest.fullBoardFallback', {
708
- blocked: true,
709
- reason
710
- });
711
- scheduleSpatialIndexRepair(module, reason);
712
- }
713
-
714
- function ensureCssRendererHooks(module) {
715
- const renderer = module?.cssRenderer || null;
716
- if (!renderer) return false;
717
- if (renderer._nearCss3dHooksInstalled === BUILD_ID) return true;
718
-
719
- const counters = {
720
- sceneObjectsVisited: 0,
721
- visibilityPatchEntered: 0,
722
- visibilityPatchExited: 0
723
- };
724
- const existingGetDebugCounters =
725
- typeof renderer.getDebugCounters === 'function'
726
- ? renderer.getDebugCounters.bind(renderer)
727
- : null;
728
- const existingResetDebugCounters =
729
- typeof renderer.resetDebugCounters === 'function'
730
- ? renderer.resetDebugCounters.bind(renderer)
731
- : null;
732
- const originalRender =
733
- typeof renderer.render === 'function'
734
- ? renderer.render.bind(renderer)
735
- : null;
736
-
737
- if (!existingGetDebugCounters && originalRender) {
738
- renderer.render = (scene, camera) => {
739
- let visited = 0;
740
- if (module.renderDebugFlags?.enableFramePerfProbe === true) {
741
- scene?.traverse?.(() => {
742
- visited++;
743
- });
744
- }
745
- const result = originalRender(scene, camera);
746
- counters.sceneObjectsVisited += visited;
747
- return result;
748
- };
749
- }
750
-
751
- if (typeof renderer.applyVisibilityDiff !== 'function') {
752
- renderer.applyVisibilityDiff = (enteredObjects, exitedObjects) => {
753
- const entered = Array.isArray(enteredObjects) ? enteredObjects : [];
754
- const exited = Array.isArray(exitedObjects) ? exitedObjects : [];
755
- const cameraElement = renderer.getCameraElement?.() || null;
756
- let visited = 0;
757
- const patchObject = (object) => {
758
- if (!object?.isCSS3DObject || !object.element) return;
759
- object.updateMatrixWorld?.(true);
760
- const layoutPrepared =
761
- object.userData?._residentTransitionPrepared === true ||
762
- object.userData?._fullResFringePrepared === true;
763
- object.element.style.removeProperty?.('display');
764
- if (object.visible !== true && layoutPrepared !== true) {
765
- object.element.style.display = 'none';
766
- }
767
- if (cameraElement && object.element.parentNode !== cameraElement) {
768
- cameraElement.appendChild(object.element);
769
- }
770
- visited++;
771
- };
772
- try {
773
- exited.forEach(patchObject);
774
- entered.forEach(patchObject);
775
- } catch (error) {
776
- return {
777
- complete: false,
778
- reason: 'visibility-diff-exception',
779
- visited,
780
- error
781
- };
782
- }
783
- counters.visibilityPatchEntered += entered.length;
784
- counters.visibilityPatchExited += exited.length;
785
- return {
786
- complete: true,
787
- entered: entered.length,
788
- exited: exited.length,
789
- visited
790
- };
791
- };
792
- }
793
-
794
- if (!existingGetDebugCounters) {
795
- renderer.getDebugCounters = () => ({ ...counters });
796
- }
797
- if (!existingResetDebugCounters) {
798
- renderer.resetDebugCounters = () => {
799
- counters.sceneObjectsVisited = 0;
800
- counters.visibilityPatchEntered = 0;
801
- counters.visibilityPatchExited = 0;
802
- };
803
- }
804
- renderer._nearCss3dHooksInstalled = BUILD_ID;
805
- return true;
806
- }
807
-
808
- function applyVisibilityDiff(module, enteredIds, exitedIds) {
809
- const fallback = (reason) => {
810
- increment(module, 'css3d', 'visibilityPatchFallbacks');
811
- return { complete: false, reason };
812
- };
813
- const renderer = module?.cssRenderer;
814
- ensureCssRendererHooks(module);
815
- if (!renderer || typeof renderer.applyVisibilityDiff !== 'function') return fallback('renderer-api-unavailable');
816
- if (module._cssParentRepairNeeded === true) return fallback('parent-repair-pending');
817
- if ((module._cssTransformDirtyIds?.size || 0) > 0) return fallback('transform-dirty');
818
-
819
- const cssScene = module.cssScene || module.scene || null;
820
- const enteredObjects = [];
821
- const exitedObjects = [];
822
-
823
- for (const value of enteredIds || []) {
824
- const nodeId = String(value || '').trim();
825
- const entry = nodeId ? module.nodeObjectsById?.get?.(nodeId) : null;
826
- if (!entry) continue;
827
- if (entry.currentType !== 'CSS') {
828
- if (entry.cssObject?.visible === true) return fallback('entered-owner-mismatch');
829
- continue;
830
- }
831
- if (!entry.cssObject?.element || entry.cssObject.visible !== true || entry.isCssDirty === true) {
832
- return fallback('entered-owner-not-ready');
833
- }
834
- if (cssScene && entry.cssObject.parent !== cssScene) return fallback('entered-parent-mismatch');
835
- enteredObjects.push(entry.cssObject);
836
- }
837
-
838
- for (const value of exitedIds || []) {
839
- const nodeId = String(value || '').trim();
840
- const entry = nodeId ? module.nodeObjectsById?.get?.(nodeId) : null;
841
- const cssObject = entry?.cssObject || null;
842
- if (!cssObject) continue;
843
- if (entry.isCssDirty === true) return fallback('exited-content-dirty');
844
- if (cssObject.parent === cssScene) {
845
- exitedObjects.push(cssObject);
846
- } else if (cssObject.visible === true || entry.currentType === 'CSS') {
847
- return fallback('exited-owner-mismatch');
848
- }
849
- }
850
-
851
- const result = renderer.applyVisibilityDiff(enteredObjects, exitedObjects);
852
- if (result?.complete !== true) return fallback(result?.reason || 'renderer-patch-failed');
853
-
854
- increment(module, 'css3d', 'visibilityPatchEntered', Number(result.entered || 0));
855
- increment(module, 'css3d', 'visibilityPatchExited', Number(result.exited || 0));
856
- increment(module, 'css3d', 'sceneObjectsVisited', Number(result.visited || 0));
857
- return {
858
- complete: true,
859
- entered: Number(result.entered || 0),
860
- exited: Number(result.exited || 0),
861
- visited: Number(result.visited || 0)
862
- };
863
- }
864
-
865
- function replaceGeometryIfSizeChanged(object, key, createGeometry) {
866
- if (!object || typeof createGeometry !== 'function') return false;
867
- object.userData ||= {};
868
- if (object.geometry && object.userData.geometrySizeKey === key) return false;
869
- const previous = object.geometry || null;
870
- object.geometry = createGeometry();
871
- object.userData.geometrySizeKey = key;
872
- previous?.dispose?.();
873
- return true;
874
- }
875
-
876
- window.MindMapCss3dHotPath = {
877
- BUILD_ID,
878
- applyVisibilityDiff,
879
- collectSpatialRectCandidateIds,
880
- createPointerHitContext,
881
- ensureCssRendererHooks,
882
- ensurePerfCounters,
883
- getBoundedFallbackIds,
884
- getContextCandidateIds,
885
- getContextWorldPoint,
886
- getDomRefs,
887
- getDomStack,
888
- getPerfSnapshot,
889
- getProjectedClientFrame,
890
- getSpatialGridKeysForBounds,
891
- increment,
892
- installOverlayGeometry,
893
- installTextSource,
894
- invalidateDomRefs,
895
- invalidateDomRefsByCssObject,
896
- noteUnavailableIndex,
897
- recordHitCandidates,
898
- replaceGeometryIfSizeChanged,
899
- resetPerfCounters,
900
- scheduleSpatialIndexRepair
901
- };
902
- })();
1
+ (function () {
2
+ 'use strict';
3
+
4
+ const BUILD_ID = '20260724-near-css3d-hotpath-v001';
5
+ const projectedCorners = Array.from({ length: 4 }, () => new THREE.Vector3());
6
+
7
+ function createPerfCounters() {
8
+ return {
9
+ buildId: BUILD_ID,
10
+ hitTest: {
11
+ fullBoardFallbackCount: 0,
12
+ candidateSource: '',
13
+ candidateCount: 0,
14
+ elementsFromPointCalls: 0,
15
+ projectedFrameCalculations: 0
16
+ },
17
+ selection: {
18
+ nodesVisited: 0,
19
+ domQueries: 0
20
+ },
21
+ css3d: {
22
+ visibilityPatchEntered: 0,
23
+ visibilityPatchExited: 0,
24
+ visibilityPatchFallbacks: 0,
25
+ sceneObjectsVisited: 0
26
+ }
27
+ };
28
+ }
29
+
30
+ function ensurePerfCounters(module) {
31
+ if (!module) return null;
32
+ if (!module._nearCss3dPerfCounters ||
33
+ module._nearCss3dPerfCounters.buildId !== BUILD_ID) {
34
+ module._nearCss3dPerfCounters = createPerfCounters();
35
+ }
36
+ return module._nearCss3dPerfCounters;
37
+ }
38
+
39
+ function increment(module, section, key, amount = 1) {
40
+ const counters = ensurePerfCounters(module);
41
+ const target = counters?.[section];
42
+ if (!target || !(key in target)) return;
43
+ target[key] = Number(target[key] || 0) + Number(amount || 0);
44
+ }
45
+
46
+ function recordHitCandidates(module, source, count) {
47
+ const hitTest = ensurePerfCounters(module)?.hitTest;
48
+ if (!hitTest) return;
49
+ hitTest.candidateSource = String(source || '');
50
+ hitTest.candidateCount = Math.max(0, Number(count || 0));
51
+ }
52
+
53
+ function getPerfSnapshot(module) {
54
+ const counters = ensurePerfCounters(module);
55
+ if (!counters) return null;
56
+ return {
57
+ buildId: counters.buildId,
58
+ hitTest: { ...counters.hitTest },
59
+ selection: { ...counters.selection },
60
+ css3d: { ...counters.css3d }
61
+ };
62
+ }
63
+
64
+ function resetPerfCounters(module) {
65
+ if (!module) return false;
66
+ module._nearCss3dPerfCounters = createPerfCounters();
67
+ return true;
68
+ }
69
+
70
+ function createPointerHitContext(module, event, viewportMetrics = null) {
71
+ return {
72
+ module,
73
+ event,
74
+ clientX: Number(event?.clientX || 0),
75
+ clientY: Number(event?.clientY || 0),
76
+ viewportMetrics,
77
+ domStack: undefined,
78
+ worldPoint: undefined,
79
+ candidateIds: undefined,
80
+ candidateSource: '',
81
+ cssHit: undefined,
82
+ projectedFrames: new Map()
83
+ };
84
+ }
85
+
86
+ function getDomStack(module, context) {
87
+ if (!context) return [];
88
+ if (context.domStack !== undefined) return context.domStack;
89
+ increment(module, 'hitTest', 'elementsFromPointCalls');
90
+ context.domStack = typeof document !== 'undefined' &&
91
+ typeof document.elementsFromPoint === 'function'
92
+ ? document.elementsFromPoint(context.clientX, context.clientY)
93
+ : [];
94
+ return context.domStack;
95
+ }
96
+
97
+ function getContextWorldPoint(context, factory) {
98
+ if (!context) return typeof factory === 'function' ? factory() : null;
99
+ if (context.worldPoint !== undefined) return context.worldPoint;
100
+ const point = typeof factory === 'function' ? factory() : null;
101
+ context.worldPoint = point
102
+ ? new THREE.Vector3(Number(point.x || 0), Number(point.y || 0), Number(point.z || 0))
103
+ : null;
104
+ return context.worldPoint;
105
+ }
106
+
107
+ function getContextCandidateIds(module, context, factory, source = '') {
108
+ if (!context) {
109
+ const ids = typeof factory === 'function' ? factory() : [];
110
+ recordHitCandidates(module, source, Array.isArray(ids) ? ids.length : 0);
111
+ return ids;
112
+ }
113
+ if (context.candidateIds !== undefined) return context.candidateIds;
114
+ context.candidateIds = typeof factory === 'function' ? factory() : [];
115
+ context.candidateSource = String(source || '');
116
+ recordHitCandidates(
117
+ module,
118
+ context.candidateSource,
119
+ Array.isArray(context.candidateIds) ? context.candidateIds.length : 0
120
+ );
121
+ return context.candidateIds;
122
+ }
123
+
124
+ function getProjectedClientFrame(module, entry, context, viewportMetrics, cacheKey = null) {
125
+ if (!module?.camera || !entry) return null;
126
+ const metrics = context?.viewportMetrics || viewportMetrics || null;
127
+ if (!metrics || !(metrics.width > 0 && metrics.height > 0)) return null;
128
+
129
+ const frameKey = cacheKey || entry;
130
+ const cachedFrame = context?.projectedFrames?.get?.(frameKey);
131
+ if (cachedFrame) return cachedFrame;
132
+
133
+ const sourceObject = entry.glObject || entry.cssObject;
134
+ const position = sourceObject?.position;
135
+ if (!position) return null;
136
+ const width = Number(
137
+ entry.glObject?.userData?.worldWidth ??
138
+ entry.cssObject?.userData?.worldWidth ??
139
+ entry.model?.width ??
140
+ entry.model?.Width ??
141
+ 0
142
+ );
143
+ const height = Number(
144
+ entry.glObject?.userData?.worldHeight ??
145
+ entry.cssObject?.userData?.worldHeight ??
146
+ entry.model?.height ??
147
+ entry.model?.Height ??
148
+ 0
149
+ );
150
+ if (!(width > 0 && height > 0)) return null;
151
+
152
+ const z = Number(position.z || 0);
153
+ projectedCorners[0].set(position.x, position.y, z);
154
+ projectedCorners[1].set(position.x + width, position.y, z);
155
+ projectedCorners[2].set(position.x, position.y - height, z);
156
+ projectedCorners[3].set(position.x + width, position.y - height, z);
157
+
158
+ let left = Infinity;
159
+ let right = -Infinity;
160
+ let top = Infinity;
161
+ let bottom = -Infinity;
162
+ for (let i = 0; i < projectedCorners.length; i++) {
163
+ const corner = projectedCorners[i].project(module.camera);
164
+ const x = Number(metrics.left || 0) + ((corner.x + 1) * metrics.width / 2);
165
+ const y = Number(metrics.top || 0) + ((-corner.y + 1) * metrics.height / 2);
166
+ if (x < left) left = x;
167
+ if (x > right) right = x;
168
+ if (y < top) top = y;
169
+ if (y > bottom) bottom = y;
170
+ }
171
+
172
+ const frame = { left, right, top, bottom };
173
+ context?.projectedFrames?.set?.(frameKey, frame);
174
+ increment(module, 'hitTest', 'projectedFrameCalculations');
175
+ return frame;
176
+ }
177
+
178
+ function getDomRefs(entry, module = null, cssObjectOverride = null) {
179
+ const cssObject = cssObjectOverride || entry?.cssObject || null;
180
+ const wrapper = cssObject?.element || null;
181
+ if (!wrapper) return null;
182
+ const version = Number(entry?._css3dDomVersion || cssObject?.userData?._domVersion || 0);
183
+ const firstChild = wrapper.firstElementChild || null;
184
+ const cached = entry?.domRefs || cssObject?.userData?.domRefs || null;
185
+ if (cached?.wrapper === wrapper &&
186
+ cached.firstChild === firstChild &&
187
+ cached.version === version) {
188
+ return cached;
189
+ }
190
+
191
+ const query = (selector) => {
192
+ increment(module, 'selection', 'domQueries');
193
+ return wrapper.querySelector?.(selector) || null;
194
+ };
195
+ const refs = {
196
+ version,
197
+ wrapper,
198
+ firstChild,
199
+ innerRoot: query('.node-container, .map-node, .css3d-dynamic-node'),
200
+ response: query('.node-response, [id^="node-response-"]'),
201
+ textarea: query('textarea, [contenteditable="true"]'),
202
+ scrollable: query('.csv-table-scroll, .node-response, .note-content, .markdown-body, .code-body, .map-node-memo__body-view'),
203
+ memoTitle: query('.map-node-memo__title'),
204
+ memoBody: query('.map-node-memo__body'),
205
+ memoBodyView: query('.map-node-memo__body-view'),
206
+ resizeHandles: Array.from(wrapper.querySelectorAll?.('.resize-handle, [data-resize-handle]') || [])
207
+ };
208
+ if (entry) entry.domRefs = refs;
209
+ cssObject.userData ||= {};
210
+ cssObject.userData.domRefs = refs;
211
+ return refs;
212
+ }
213
+
214
+ function invalidateDomRefs(entry, cssObjectOverride = null) {
215
+ const cssObject = cssObjectOverride || entry?.cssObject || null;
216
+ const nextVersion = Math.max(
217
+ Number(entry?._css3dDomVersion || 0),
218
+ Number(cssObject?.userData?._domVersion || 0)
219
+ ) + 1;
220
+ if (entry) {
221
+ entry._css3dDomVersion = nextVersion;
222
+ entry.domRefs = null;
223
+ }
224
+ if (cssObject) {
225
+ cssObject.userData ||= {};
226
+ cssObject.userData._domVersion = nextVersion;
227
+ cssObject.userData.domRefs = null;
228
+ }
229
+ return nextVersion;
230
+ }
231
+
232
+ function invalidateDomRefsByCssObject(module, cssObject, nodeId = '') {
233
+ const normalizedId = String(nodeId || cssObject?.userData?.nodeId || '').trim();
234
+ const entry = normalizedId ? module?.nodeObjectsById?.get?.(normalizedId) : null;
235
+ return invalidateDomRefs(entry, cssObject);
236
+ }
237
+
238
+ function installTextSource(helpers = {}) {
239
+ const styleState = new WeakMap();
240
+ const suspendedClass = 'mind-map-text-overlay-source-suspended';
241
+ const suspendedAttr = 'data-text-overlay-source-suspended';
242
+
243
+ function getModule() {
244
+ return helpers.getModule?.() || null;
245
+ }
246
+
247
+ function getTextOverlaySourceTargets(nodeEntry) {
248
+ if (!nodeEntry?.cssObject?.element || !helpers.supportsTextOverlay?.(nodeEntry.model)) return [];
249
+ const module = getModule();
250
+ const refs = getDomRefs(nodeEntry, module);
251
+ if (helpers.getTextOverlayRenderMode?.(nodeEntry) === 'memo') {
252
+ return [refs?.memoTitle, refs?.memoBodyView].filter(element =>
253
+ !!element && (element.style.display !== 'none' || element.offsetWidth > 0 || element.offsetHeight > 0));
254
+ }
255
+ const root = helpers.getTextOverlayRootElement?.(nodeEntry);
256
+ if (!root?.children?.length) return [];
257
+ return Array.from(root.children).filter(element =>
258
+ !!element &&
259
+ !element.classList?.contains?.('resize-handle') &&
260
+ (element.style.display !== 'none' || element.offsetWidth > 0 || element.offsetHeight > 0));
261
+ }
262
+
263
+ function ensureTextOverlaySourceStyleState(element) {
264
+ if (!element) return null;
265
+ let state = styleState.get(element) || null;
266
+ if (!state) {
267
+ state = {
268
+ opacity: element.style.opacity,
269
+ pointerEvents: element.style.pointerEvents,
270
+ userSelect: element.style.userSelect,
271
+ webkitUserSelect: element.style.webkitUserSelect,
272
+ caretColor: element.style.caretColor,
273
+ contentVisibility: element.style.contentVisibility,
274
+ containIntrinsicSize: element.style.containIntrinsicSize,
275
+ contain: element.style.contain
276
+ };
277
+ styleState.set(element, state);
278
+ }
279
+ return state;
280
+ }
281
+
282
+ function resetTextOverlaySourcePresentation(element) {
283
+ if (!element) return;
284
+ element.classList?.remove?.(suspendedClass);
285
+ element.removeAttribute?.(suspendedAttr);
286
+ element.style.opacity = '';
287
+ element.style.pointerEvents = '';
288
+ element.style.userSelect = '';
289
+ element.style.webkitUserSelect = '';
290
+ element.style.caretColor = '';
291
+ element.style.contentVisibility = '';
292
+ element.style.containIntrinsicSize = '';
293
+ element.style.contain = '';
294
+ }
295
+
296
+ function suspendTextOverlaySourceTarget(element) {
297
+ if (!element) return;
298
+ ensureTextOverlaySourceStyleState(element);
299
+ element.classList?.add?.(suspendedClass);
300
+ element.setAttribute?.(suspendedAttr, 'true');
301
+ element.style.pointerEvents = 'none';
302
+ element.style.userSelect = 'none';
303
+ element.style.webkitUserSelect = 'none';
304
+ element.style.caretColor = 'transparent';
305
+ element.style.opacity = '0';
306
+ element.style.contain = 'layout style paint';
307
+ element.style.contentVisibility = 'visible';
308
+ element.style.containIntrinsicSize = '';
309
+ }
310
+
311
+ function restoreTextOverlaySourceTarget(element) {
312
+ if (!element) return;
313
+ const state = styleState.get(element) || null;
314
+ if (!state) {
315
+ resetTextOverlaySourcePresentation(element);
316
+ return;
317
+ }
318
+ element.classList?.remove?.(suspendedClass);
319
+ element.removeAttribute?.(suspendedAttr);
320
+ Object.assign(element.style, state);
321
+ styleState.delete(element);
322
+ }
323
+
324
+ function stripTextOverlayPresentationOverrides(root) {
325
+ if (!root) return;
326
+ if (root.hasAttribute?.(suspendedAttr) || root.classList?.contains?.(suspendedClass)) {
327
+ resetTextOverlaySourcePresentation(root);
328
+ }
329
+ root.querySelectorAll?.(`[${suspendedAttr}], .${suspendedClass}`)
330
+ ?.forEach?.(resetTextOverlaySourcePresentation);
331
+ }
332
+
333
+ function getNativeTextSelectionSourceNodeId(module) {
334
+ return String(module?._nativeTextSelectionSourceNodeId || '').trim();
335
+ }
336
+
337
+ function hasNativeTextSelectionSource(module, nodeId = null) {
338
+ const activeId = getNativeTextSelectionSourceNodeId(module);
339
+ if (!activeId) return false;
340
+ return nodeId === null || activeId === String(nodeId || '').trim();
341
+ }
342
+
343
+ function setNativeTextSelectionOwnerClass(module, nodeId = null) {
344
+ if (!module) return false;
345
+ const nextId = String(nodeId || '').trim();
346
+ const previousId = String(module._nativeTextSelectionOwnerNodeId || '').trim();
347
+ if (previousId === nextId) return true;
348
+
349
+ const getWrapper = (id) => {
350
+ const entry = id ? module.nodeObjectsById?.get?.(id) : null;
351
+ return getDomRefs(entry, module)?.wrapper || null;
352
+ };
353
+ const previousWrapper = getWrapper(previousId);
354
+ const nextWrapper = getWrapper(nextId);
355
+ if (previousWrapper) {
356
+ previousWrapper.classList?.remove?.('is-text-selection-owner');
357
+ increment(module, 'selection', 'nodesVisited');
358
+ }
359
+ if (nextWrapper) {
360
+ nextWrapper.classList?.add?.('is-text-selection-owner');
361
+ increment(module, 'selection', 'nodesVisited');
362
+ }
363
+ module._nativeTextSelectionOwnerNodeId = nextId || null;
364
+ return true;
365
+ }
366
+
367
+ function activateNativeTextSelectionSource(module, nodeId) {
368
+ const id = String(nodeId || '').trim();
369
+ if (!module || !id) return false;
370
+ module._nativeTextSelectionSourceNodeId = id;
371
+ setNativeTextSelectionOwnerClass(module, id);
372
+ helpers.ensureTextOverlaySourceVisible?.(module, id);
373
+ return true;
374
+ }
375
+
376
+ function clearNativeTextSelectionSource(module, nodeId = null) {
377
+ const activeId = getNativeTextSelectionSourceNodeId(module);
378
+ const requestedId = nodeId === null ? activeId : String(nodeId || '').trim();
379
+ if (!module || !activeId || !requestedId || activeId !== requestedId) return false;
380
+ module._nativeTextSelectionSourceNodeId = null;
381
+ setNativeTextSelectionOwnerClass(module, null);
382
+ return true;
383
+ }
384
+
385
+ function isTextOverlaySelectionNode(module, nodeEntry) {
386
+ const contentType = String(nodeEntry?.model?.contentType ?? nodeEntry?.model?.ContentType ?? '').toLowerCase();
387
+ if (contentType !== 'text' && contentType !== 'markdown') return false;
388
+ const nodeId = String(helpers.getNodeId?.(nodeEntry?.model) || '').trim();
389
+ return hasNativeTextSelectionSource(module, nodeId);
390
+ }
391
+
392
+ function setTextOverlaySourceActive(nodeEntry, isActive) {
393
+ if (helpers.isCss3dMediaNode?.(nodeEntry?.model)) {
394
+ helpers.restoreCss3dMediaOverlayState?.(nodeEntry.model, nodeEntry.cssObject);
395
+ nodeEntry._textOverlayActive = false;
396
+ nodeEntry._css3dSourceSuspended = false;
397
+ return;
398
+ }
399
+ const host = helpers.getTextOverlayHostElement?.(nodeEntry);
400
+ if (!host?.classList) return;
401
+ host.classList.toggle('mind-map-text-overlay-active', !!isActive);
402
+ getTextOverlaySourceTargets(nodeEntry).forEach(target => {
403
+ if (isActive) suspendTextOverlaySourceTarget(target);
404
+ else restoreTextOverlaySourceTarget(target);
405
+ });
406
+ nodeEntry._textOverlayActive = nodeEntry._css3dSourceSuspended = !!isActive;
407
+ }
408
+
409
+ return {
410
+ getTextOverlaySourceTargets,
411
+ ensureTextOverlaySourceStyleState,
412
+ resetTextOverlaySourcePresentation,
413
+ suspendTextOverlaySourceTarget,
414
+ restoreTextOverlaySourceTarget,
415
+ stripTextOverlayPresentationOverrides,
416
+ getNativeTextSelectionSourceNodeId,
417
+ hasNativeTextSelectionSource,
418
+ setNativeTextSelectionOwnerClass,
419
+ activateNativeTextSelectionSource,
420
+ clearNativeTextSelectionSource,
421
+ isTextOverlaySelectionNode,
422
+ setTextOverlaySourceActive
423
+ };
424
+ }
425
+
426
+ function installOverlayGeometry(helpers = {}) {
427
+ function getTextOverlayPlacementInfo(module, element) {
428
+ if (!module?.container || !element) return null;
429
+ const rect = element.getBoundingClientRect();
430
+ const containerRect = module.container.getBoundingClientRect();
431
+ const width = Math.max(0, rect.width || 0);
432
+ const height = Math.max(0, rect.height || 0);
433
+ if (width < 1 || height < 1) return null;
434
+ const left = rect.left - containerRect.left;
435
+ const top = rect.top - containerRect.top;
436
+ return {
437
+ left,
438
+ top,
439
+ width,
440
+ height,
441
+ baseWidth: Math.max(1, Number(element.offsetWidth || width || 1)),
442
+ baseHeight: Math.max(1, Number(element.offsetHeight || height || 1)),
443
+ right: left + width,
444
+ bottom: top + height
445
+ };
446
+ }
447
+
448
+ function doesTextOverlayRectOverlap(a, b, padding = 0) {
449
+ if (!a || !b) return false;
450
+ const inset = Number.isFinite(Number(padding)) ? Number(padding) : 0;
451
+ return !(
452
+ (a.left + a.width + inset) < b.left ||
453
+ (b.left + b.width + inset) < a.left ||
454
+ (a.top + a.height + inset) < b.top ||
455
+ (b.top + b.height + inset) < a.top
456
+ );
457
+ }
458
+
459
+ function isTextOverlayPlacementVisible(module, placement, options = {}) {
460
+ if (!module?.container || !placement) return false;
461
+ const { ignoreMinSize = false } = options;
462
+ if (!ignoreMinSize &&
463
+ (placement.width < Number(helpers.minWidth || 0) ||
464
+ placement.height < Number(helpers.minHeight || 0))) {
465
+ return false;
466
+ }
467
+ const maxWidth = Number(module.container.clientWidth || 0);
468
+ const maxHeight = Number(module.container.clientHeight || 0);
469
+ if (!(maxWidth > 0) || !(maxHeight > 0)) return false;
470
+ return !(
471
+ placement.right < 0 ||
472
+ placement.left > maxWidth ||
473
+ placement.bottom < 0 ||
474
+ placement.top > maxHeight
475
+ );
476
+ }
477
+
478
+ function getTextOverlayPriority(module, nodeEntry) {
479
+ const nodeModel = nodeEntry?.model;
480
+ const nodeId = String(helpers.getNodeId?.(nodeModel) || '').trim();
481
+ if (!nodeModel || !nodeId) return Number.NEGATIVE_INFINITY;
482
+ let priority = 0;
483
+ if (String(module?.selectedNodeIdJs || '').trim() === nodeId) priority += 100000;
484
+ else if (module?.multiSelectedNodeIds?.has?.(nodeId)) priority += 50000;
485
+ const width = Math.max(1, Number(nodeModel.width || nodeModel.Width || 0));
486
+ const height = Math.max(1, Number(nodeModel.height || nodeModel.Height || 0));
487
+ priority += Math.min(6000, (width * height) / 18);
488
+ if (module?.camera) {
489
+ const centerX = Number(nodeModel.positionX ?? nodeModel.PositionX ?? nodeModel.x ?? nodeModel.X ?? 0) + (width * 0.5);
490
+ const centerY = Number(nodeModel.positionY ?? nodeModel.PositionY ?? nodeModel.y ?? nodeModel.Y ?? 0) - (height * 0.5);
491
+ priority += Math.max(
492
+ 0,
493
+ 24000 -
494
+ Math.abs(centerX - Number(module.camera.position?.x || 0)) -
495
+ Math.abs(centerY - Number(module.camera.position?.y || 0))
496
+ );
497
+ }
498
+ const zIndex = Number(nodeEntry?.cssObject?.element?.style?.zIndex || 0);
499
+ if (Number.isFinite(zIndex)) priority += zIndex * 10;
500
+ return priority;
501
+ }
502
+
503
+ function isAlwaysTextOverlayNode(nodeEntry) {
504
+ const contentType = String(
505
+ nodeEntry?.model?.contentType ??
506
+ nodeEntry?.model?.ContentType ??
507
+ ''
508
+ ).toLowerCase();
509
+ return contentType === 'markdown' || contentType === 'text';
510
+ }
511
+
512
+ function getTextOverlayContentSignature(nodeEntry) {
513
+ const model = nodeEntry?.model || {};
514
+ const contentType = String(model.contentType ?? model.ContentType ?? '').toLowerCase();
515
+ const prompt = String(model.prompt ?? model.Prompt ?? '');
516
+ const response = String(model.response ?? model.Response ?? '');
517
+ const isLoading = (model.isLoading ?? model.IsLoading ?? false) ? '1' : '0';
518
+ const width = Math.max(0, Number(model.width ?? model.Width ?? 0));
519
+ const height = Math.max(0, Number(model.height ?? model.Height ?? 0));
520
+ return `${contentType}||${prompt}||${response}||${isLoading}||${width}||${height}`;
521
+ }
522
+
523
+ function getTextOverlayLayoutSignature(sourceElement, mode = 'full', hostPlacement = null) {
524
+ if (!sourceElement) return '';
525
+ if (mode === 'memo') {
526
+ const titleInput = sourceElement.querySelector('.map-node-memo__title');
527
+ const bodyView = sourceElement.querySelector('.map-node-memo__body-view');
528
+ const hostWidth = Math.round(Number(hostPlacement?.baseWidth || sourceElement.offsetWidth || 0));
529
+ const hostHeight = Math.round(Number(hostPlacement?.baseHeight || sourceElement.offsetHeight || 0));
530
+ const titleSignature = titleInput
531
+ ? `${titleInput.offsetLeft}:${titleInput.offsetTop}:${titleInput.offsetWidth}:${titleInput.offsetHeight}:${titleInput.style.display}`
532
+ : 'title:none';
533
+ const bodySignature = bodyView
534
+ ? `${bodyView.offsetLeft}:${bodyView.offsetTop}:${bodyView.offsetWidth}:${bodyView.offsetHeight}:${bodyView.style.display}`
535
+ : 'body:none';
536
+ return `${mode}||${hostWidth}x${hostHeight}||${titleSignature}||${bodySignature}`;
537
+ }
538
+ const rootWidth = Math.round(Number(sourceElement.offsetWidth || 0));
539
+ const rootHeight = Math.round(Number(sourceElement.offsetHeight || 0));
540
+ const childSignature = Array.from(sourceElement.children || [])
541
+ .map(child => `${child.tagName}:${child.offsetWidth}:${child.offsetHeight}:${child.style.display}`)
542
+ .join('|');
543
+ return `${mode}||${rootWidth}x${rootHeight}||${childSignature}`;
544
+ }
545
+
546
+ return {
547
+ getTextOverlayPlacementInfo,
548
+ doesTextOverlayRectOverlap,
549
+ isTextOverlayPlacementVisible,
550
+ getTextOverlayPriority,
551
+ isAlwaysTextOverlayNode,
552
+ getTextOverlayContentSignature,
553
+ getTextOverlayLayoutSignature
554
+ };
555
+ }
556
+
557
+ function getNodeBounds(entry) {
558
+ const shared = window.MindMapNodeBounds?.getNodeWorldBounds?.(entry);
559
+ if (shared) return shared;
560
+ const model = entry?.model || null;
561
+ const object = entry?.glObject || entry?.cssObject || null;
562
+ if (!model && !object) return null;
563
+ const width = Math.max(1, Number(
564
+ entry?.glObject?.userData?.worldWidth ??
565
+ entry?.cssObject?.userData?.worldWidth ??
566
+ model?.width ??
567
+ model?.Width ??
568
+ 400
569
+ ));
570
+ const height = Math.max(1, Number(
571
+ entry?.glObject?.userData?.worldHeight ??
572
+ entry?.cssObject?.userData?.worldHeight ??
573
+ model?.height ??
574
+ model?.Height ??
575
+ 200
576
+ ));
577
+ const x = Number(object?.position?.x ?? model?.positionX ?? model?.x ?? 0);
578
+ const y = Number(object?.position?.y ?? model?.positionY ?? model?.y ?? 0);
579
+ return { minX: x, maxX: x + width, minY: y - height, maxY: y };
580
+ }
581
+
582
+ function getSpatialGridKeysForBounds(module, entry) {
583
+ const bounds = getNodeBounds(entry);
584
+ if (!bounds) return new Set();
585
+ const cellSize = Math.max(1, Number(module?.GRID_CELL_SIZE || 500));
586
+ const keys = new Set();
587
+ const startX = Math.floor(bounds.minX / cellSize);
588
+ const endX = Math.floor(bounds.maxX / cellSize);
589
+ const startY = Math.floor(bounds.minY / cellSize);
590
+ const endY = Math.floor(bounds.maxY / cellSize);
591
+ for (let x = startX; x <= endX; x++) {
592
+ for (let y = startY; y <= endY; y++) keys.add(`${x}:${y}`);
593
+ }
594
+ return keys;
595
+ }
596
+
597
+ function setEntrySpatialKeys(entry, keys) {
598
+ if (!entry) return;
599
+ entry._spatialGridKeys = keys;
600
+ if (entry.glObject) {
601
+ entry.glObject.userData ||= {};
602
+ entry.glObject.userData.spatialGridKeys = keys;
603
+ }
604
+ if (entry.cssObject) {
605
+ entry.cssObject.userData ||= {};
606
+ entry.cssObject.userData.spatialGridKeys = keys;
607
+ }
608
+ }
609
+
610
+ function collectSpatialRectCandidateIds(module, rect) {
611
+ if (!module?.spatialGrid || module._spatialIndexRepairState || !rect) return null;
612
+ const cellSize = Math.max(1, Number(module.GRID_CELL_SIZE || 500));
613
+ const ids = [];
614
+ const seen = new Set();
615
+ const startX = Math.floor(Number(rect.left || 0) / cellSize);
616
+ const endX = Math.floor(Number(rect.right || 0) / cellSize);
617
+ const startY = Math.floor(Number(rect.bottom || 0) / cellSize);
618
+ const endY = Math.floor(Number(rect.top || 0) / cellSize);
619
+ for (let x = startX; x <= endX; x++) {
620
+ for (let y = startY; y <= endY; y++) {
621
+ module.spatialGrid.get(`${x}:${y}`)?.forEach?.(nodeId => {
622
+ const id = String(nodeId || '').trim();
623
+ if (id && !seen.has(id)) {
624
+ seen.add(id);
625
+ ids.push(id);
626
+ }
627
+ });
628
+ }
629
+ }
630
+ return ids;
631
+ }
632
+
633
+ function getBoundedFallbackIds(module) {
634
+ const ids = new Set();
635
+ const add = (value) => {
636
+ const id = String(value || '').trim();
637
+ if (id && module?.nodeObjectsById?.has?.(id)) ids.add(id);
638
+ };
639
+ module?._visibleIds?.forEach?.(add);
640
+ module?._prevVisibleIds?.forEach?.(add);
641
+ add(module?.selectedNodeIdJs);
642
+ module?.multiSelectedNodeIds?.forEach?.(add);
643
+ add(module?.draggedNodeId);
644
+ add(module?._nativeTextSelectionSourceNodeId);
645
+ add(module?._editingOverlayState?.nodeId);
646
+ add(module?._textOverlayV2State?.editing?.nodeId);
647
+ module?.lodRenderer?._nearCssVisibleIds?.forEach?.(add);
648
+ module?.lodRenderer?._lodImageCssFallbackNodeIds?.forEach?.(add);
649
+ module?.lodRenderer?._lodTemplateCssNodeIds?.forEach?.(add);
650
+ return Array.from(ids);
651
+ }
652
+
653
+ function scheduleSpatialIndexRepair(module, reason = 'index-unavailable') {
654
+ if (!module?.nodeObjectsById || module._spatialIndexRepairState) return false;
655
+ const ownerMap = module.nodeObjectsById;
656
+ const state = {
657
+ ownerMap,
658
+ iterator: ownerMap.entries(),
659
+ reason: String(reason || 'index-unavailable'),
660
+ processed: 0
661
+ };
662
+ module._spatialIndexRepairState = state;
663
+ module.spatialGrid = new Map();
664
+ window.MindCanvasTrace?.emit?.('hitTest.spatialRepairScheduled', {
665
+ reason: state.reason,
666
+ nodeCount: ownerMap.size
667
+ });
668
+
669
+ const schedule = (callback) => {
670
+ if (typeof requestIdleCallback === 'function') requestIdleCallback(callback, { timeout: 120 });
671
+ else setTimeout(() => callback(null), 0);
672
+ };
673
+ const run = (deadline) => {
674
+ if (module._spatialIndexRepairState !== state || module.nodeObjectsById !== ownerMap) return;
675
+ let processedThisSlice = 0;
676
+ while (processedThisSlice < 128 &&
677
+ (!deadline?.timeRemaining || deadline.timeRemaining() > 1)) {
678
+ const next = state.iterator.next();
679
+ if (next.done) {
680
+ module._spatialIndexRepairState = null;
681
+ module.logicWorkerBridge?.rebuildNodeTables?.(module, {
682
+ reason: 'hit-test-spatial-repair'
683
+ });
684
+ window.MindCanvasTrace?.emit?.('hitTest.spatialRepairCompleted', {
685
+ reason: state.reason,
686
+ processed: state.processed
687
+ });
688
+ return;
689
+ }
690
+ const [nodeId, entry] = next.value;
691
+ const keys = getSpatialGridKeysForBounds(module, entry);
692
+ keys.forEach(key => {
693
+ if (!module.spatialGrid.has(key)) module.spatialGrid.set(key, new Set());
694
+ module.spatialGrid.get(key).add(nodeId);
695
+ });
696
+ setEntrySpatialKeys(entry, keys);
697
+ processedThisSlice++;
698
+ state.processed++;
699
+ }
700
+ schedule(run);
701
+ };
702
+ schedule(run);
703
+ return true;
704
+ }
705
+
706
+ function noteUnavailableIndex(module, reason = 'index-unavailable') {
707
+ window.MindCanvasTrace?.emit?.('hitTest.fullBoardFallback', {
708
+ blocked: true,
709
+ reason
710
+ });
711
+ scheduleSpatialIndexRepair(module, reason);
712
+ }
713
+
714
+ function ensureCssRendererHooks(module) {
715
+ const renderer = module?.cssRenderer || null;
716
+ if (!renderer) return false;
717
+ if (renderer._nearCss3dHooksInstalled === BUILD_ID) return true;
718
+
719
+ const counters = {
720
+ sceneObjectsVisited: 0,
721
+ visibilityPatchEntered: 0,
722
+ visibilityPatchExited: 0
723
+ };
724
+ const existingGetDebugCounters =
725
+ typeof renderer.getDebugCounters === 'function'
726
+ ? renderer.getDebugCounters.bind(renderer)
727
+ : null;
728
+ const existingResetDebugCounters =
729
+ typeof renderer.resetDebugCounters === 'function'
730
+ ? renderer.resetDebugCounters.bind(renderer)
731
+ : null;
732
+ const originalRender =
733
+ typeof renderer.render === 'function'
734
+ ? renderer.render.bind(renderer)
735
+ : null;
736
+
737
+ if (!existingGetDebugCounters && originalRender) {
738
+ renderer.render = (scene, camera) => {
739
+ let visited = 0;
740
+ if (module.renderDebugFlags?.enableFramePerfProbe === true) {
741
+ scene?.traverse?.(() => {
742
+ visited++;
743
+ });
744
+ }
745
+ const result = originalRender(scene, camera);
746
+ counters.sceneObjectsVisited += visited;
747
+ return result;
748
+ };
749
+ }
750
+
751
+ if (typeof renderer.applyVisibilityDiff !== 'function') {
752
+ renderer.applyVisibilityDiff = (enteredObjects, exitedObjects) => {
753
+ const entered = Array.isArray(enteredObjects) ? enteredObjects : [];
754
+ const exited = Array.isArray(exitedObjects) ? exitedObjects : [];
755
+ const cameraElement = renderer.getCameraElement?.() || null;
756
+ let visited = 0;
757
+ const patchObject = (object) => {
758
+ if (!object?.isCSS3DObject || !object.element) return;
759
+ object.updateMatrixWorld?.(true);
760
+ const layoutPrepared =
761
+ object.userData?._residentTransitionPrepared === true ||
762
+ object.userData?._fullResFringePrepared === true;
763
+ object.element.style.removeProperty?.('display');
764
+ if (object.visible !== true && layoutPrepared !== true) {
765
+ object.element.style.display = 'none';
766
+ }
767
+ if (cameraElement && object.element.parentNode !== cameraElement) {
768
+ cameraElement.appendChild(object.element);
769
+ }
770
+ visited++;
771
+ };
772
+ try {
773
+ exited.forEach(patchObject);
774
+ entered.forEach(patchObject);
775
+ } catch (error) {
776
+ return {
777
+ complete: false,
778
+ reason: 'visibility-diff-exception',
779
+ visited,
780
+ error
781
+ };
782
+ }
783
+ counters.visibilityPatchEntered += entered.length;
784
+ counters.visibilityPatchExited += exited.length;
785
+ return {
786
+ complete: true,
787
+ entered: entered.length,
788
+ exited: exited.length,
789
+ visited
790
+ };
791
+ };
792
+ }
793
+
794
+ if (!existingGetDebugCounters) {
795
+ renderer.getDebugCounters = () => ({ ...counters });
796
+ }
797
+ if (!existingResetDebugCounters) {
798
+ renderer.resetDebugCounters = () => {
799
+ counters.sceneObjectsVisited = 0;
800
+ counters.visibilityPatchEntered = 0;
801
+ counters.visibilityPatchExited = 0;
802
+ };
803
+ }
804
+ renderer._nearCss3dHooksInstalled = BUILD_ID;
805
+ return true;
806
+ }
807
+
808
+ function applyVisibilityDiff(module, enteredIds, exitedIds) {
809
+ const fallback = (reason) => {
810
+ increment(module, 'css3d', 'visibilityPatchFallbacks');
811
+ return { complete: false, reason };
812
+ };
813
+ const renderer = module?.cssRenderer;
814
+ ensureCssRendererHooks(module);
815
+ if (!renderer || typeof renderer.applyVisibilityDiff !== 'function') return fallback('renderer-api-unavailable');
816
+ if (module._cssParentRepairNeeded === true) return fallback('parent-repair-pending');
817
+ if ((module._cssTransformDirtyIds?.size || 0) > 0) return fallback('transform-dirty');
818
+
819
+ const cssScene = module.cssScene || module.scene || null;
820
+ const enteredObjects = [];
821
+ const exitedObjects = [];
822
+
823
+ for (const value of enteredIds || []) {
824
+ const nodeId = String(value || '').trim();
825
+ const entry = nodeId ? module.nodeObjectsById?.get?.(nodeId) : null;
826
+ if (!entry) continue;
827
+ if (entry.currentType !== 'CSS') {
828
+ if (entry.cssObject?.visible === true) return fallback('entered-owner-mismatch');
829
+ continue;
830
+ }
831
+ if (!entry.cssObject?.element || entry.cssObject.visible !== true || entry.isCssDirty === true) {
832
+ return fallback('entered-owner-not-ready');
833
+ }
834
+ if (cssScene && entry.cssObject.parent !== cssScene) return fallback('entered-parent-mismatch');
835
+ enteredObjects.push(entry.cssObject);
836
+ }
837
+
838
+ for (const value of exitedIds || []) {
839
+ const nodeId = String(value || '').trim();
840
+ const entry = nodeId ? module.nodeObjectsById?.get?.(nodeId) : null;
841
+ const cssObject = entry?.cssObject || null;
842
+ if (!cssObject) continue;
843
+ if (entry.isCssDirty === true) return fallback('exited-content-dirty');
844
+ if (cssObject.parent === cssScene) {
845
+ exitedObjects.push(cssObject);
846
+ } else if (cssObject.visible === true || entry.currentType === 'CSS') {
847
+ return fallback('exited-owner-mismatch');
848
+ }
849
+ }
850
+
851
+ const result = renderer.applyVisibilityDiff(enteredObjects, exitedObjects);
852
+ if (result?.complete !== true) return fallback(result?.reason || 'renderer-patch-failed');
853
+
854
+ increment(module, 'css3d', 'visibilityPatchEntered', Number(result.entered || 0));
855
+ increment(module, 'css3d', 'visibilityPatchExited', Number(result.exited || 0));
856
+ increment(module, 'css3d', 'sceneObjectsVisited', Number(result.visited || 0));
857
+ return {
858
+ complete: true,
859
+ entered: Number(result.entered || 0),
860
+ exited: Number(result.exited || 0),
861
+ visited: Number(result.visited || 0)
862
+ };
863
+ }
864
+
865
+ function replaceGeometryIfSizeChanged(object, key, createGeometry) {
866
+ if (!object || typeof createGeometry !== 'function') return false;
867
+ object.userData ||= {};
868
+ if (object.geometry && object.userData.geometrySizeKey === key) return false;
869
+ const previous = object.geometry || null;
870
+ object.geometry = createGeometry();
871
+ object.userData.geometrySizeKey = key;
872
+ previous?.dispose?.();
873
+ return true;
874
+ }
875
+
876
+ window.MindMapCss3dHotPath = {
877
+ BUILD_ID,
878
+ applyVisibilityDiff,
879
+ collectSpatialRectCandidateIds,
880
+ createPointerHitContext,
881
+ ensureCssRendererHooks,
882
+ ensurePerfCounters,
883
+ getBoundedFallbackIds,
884
+ getContextCandidateIds,
885
+ getContextWorldPoint,
886
+ getDomRefs,
887
+ getDomStack,
888
+ getPerfSnapshot,
889
+ getProjectedClientFrame,
890
+ getSpatialGridKeysForBounds,
891
+ increment,
892
+ installOverlayGeometry,
893
+ installTextSource,
894
+ invalidateDomRefs,
895
+ invalidateDomRefsByCssObject,
896
+ noteUnavailableIndex,
897
+ recordHitCandidates,
898
+ replaceGeometryIfSizeChanged,
899
+ resetPerfCounters,
900
+ scheduleSpatialIndexRepair
901
+ };
902
+ })();