@mindexec/cli 0.2.444 → 0.2.446

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 (26) hide show
  1. package/package.json +6 -3
  2. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  3. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  4. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  5. package/remote-hub.js +170 -5
  6. package/scripts/remote-fast-live-rate-smoke.mjs +1 -0
  7. package/server.js +12 -12
  8. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +165 -10
  9. package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +47 -58
  10. package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4 -24
  11. package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +31 -18
  12. package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +6 -3
  13. package/wwwroot/_framework/{MindExecution.Core.4moacskf64.dll → MindExecution.Core.djyzj5mxyk.dll} +0 -0
  14. package/wwwroot/_framework/{MindExecution.Kernel.pa251cfsz4.dll → MindExecution.Kernel.6e5cj9aijz.dll} +0 -0
  15. package/wwwroot/_framework/{MindExecution.Plugins.Admin.dxyuwmxv1s.dll → MindExecution.Plugins.Admin.siulh9tfrf.dll} +0 -0
  16. package/wwwroot/_framework/{MindExecution.Plugins.Business.7uj5erqhhn.dll → MindExecution.Plugins.Business.8cjtrc7qd3.dll} +0 -0
  17. package/wwwroot/_framework/{MindExecution.Plugins.Concept.108rmsxc96.dll → MindExecution.Plugins.Concept.rpp78mc75u.dll} +0 -0
  18. package/wwwroot/_framework/{MindExecution.Plugins.Directory.ccsy3g0fe5.dll → MindExecution.Plugins.Directory.wfv0ff9v4u.dll} +0 -0
  19. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll → MindExecution.Plugins.PlanMaster.jdppnbd6g4.dll} +0 -0
  20. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.q8ml98phj9.dll → MindExecution.Plugins.YouTube.a43f2q3pkw.dll} +0 -0
  21. package/wwwroot/_framework/{MindExecution.Shared.2pywq19zjy.dll → MindExecution.Shared.gwtyj0e9lw.dll} +0 -0
  22. package/wwwroot/_framework/{MindExecution.Web.qucbls1wfr.dll → MindExecution.Web.j3zaapcxtt.dll} +0 -0
  23. package/wwwroot/_framework/blazor.boot.json +21 -21
  24. package/wwwroot/index.html +1 -1
  25. package/wwwroot/service-worker-assets.js +28 -28
  26. 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
  }
@@ -1931,7 +1929,6 @@ window.MindMapInteractions = (function () {
1931
1929
  let bestPriority = -Infinity;
1932
1930
  const worldPoint = getWorldPoint(module, clientX, clientY);
1933
1931
  const candidateIds = getCssFallbackCandidateIds(module, worldPoint);
1934
-
1935
1932
  for (let i = 0; i < candidateIds.length; i++) {
1936
1933
  const nodeId = candidateIds[i];
1937
1934
  const entry = module.nodeObjectsById.get(nodeId);
@@ -1971,7 +1968,6 @@ window.MindMapInteractions = (function () {
1971
1968
  let bestHit = null;
1972
1969
  let bestPriority = -Infinity;
1973
1970
  const candidateIds = getCssFallbackCandidateIds(module, worldPoint);
1974
-
1975
1971
  for (let i = 0; i < candidateIds.length; i++) {
1976
1972
  const nodeId = candidateIds[i];
1977
1973
  const entry = module.nodeObjectsById.get(nodeId);
@@ -2134,6 +2130,7 @@ window.MindMapInteractions = (function () {
2134
2130
  }
2135
2131
 
2136
2132
  function getHitTestCandidateNodeIds(module, point, options = {}) {
2133
+ if (!module?.nodeObjectsById || !point) return null;
2137
2134
  const normalizedCellRadius = Math.max(0, Math.min(3, Math.round(Number(options.cellRadius || 1))));
2138
2135
  module?.logicWorkerBridge?.primeHitTestCandidates?.(module, {
2139
2136
  point,
@@ -2148,6 +2145,7 @@ window.MindMapInteractions = (function () {
2148
2145
  return cachedCandidateIds;
2149
2146
  }
2150
2147
 
2148
+ if (!module.spatialGrid) return null;
2151
2149
  return collectLocalHitTestCandidateNodeIds(module, point, normalizedCellRadius);
2152
2150
  }
2153
2151
 
@@ -2188,10 +2186,14 @@ window.MindMapInteractions = (function () {
2188
2186
  function getNearbyMeshes(module, point, options = {}) {
2189
2187
  const candidateIds = getHitTestCandidateNodeIds(module, point, options);
2190
2188
  const meshes = [];
2189
+ meshes._hitTestCandidatesAvailable = Array.isArray(candidateIds);
2191
2190
  appendMeshesForHitTestCandidateIds(module, candidateIds, meshes, options);
2192
2191
  return meshes;
2193
2192
  }
2194
2193
 
2194
+ function appendFallbackMeshes(module, point, meshes) {
2195
+ (getCssFallbackCandidateIds(module, point) || []).forEach(id => { const g = module.nodeObjectsById.get(id)?.glObject; if (g && (g.isMesh || g.children?.length > 0)) meshes.push(g); });
2196
+ }
2195
2197
  function buildSelectionRectFromWorldPoints(worldStartPos, worldEndPos) {
2196
2198
  if (!worldStartPos || !worldEndPos) {
2197
2199
  return null;
@@ -2337,11 +2339,19 @@ window.MindMapInteractions = (function () {
2337
2339
  const candidateIds = worldPoint
2338
2340
  ? getHitTestCandidateNodeIds(module, worldPoint, { visibleOnly: false })
2339
2341
  : [];
2340
- if (Array.isArray(candidateIds) && candidateIds.length > 0) {
2342
+ if (Array.isArray(candidateIds)) {
2341
2343
  return candidateIds;
2342
2344
  }
2343
2345
 
2344
- return Array.from(module.nodeObjectsById.keys());
2346
+ const fallbackIds = new Set();
2347
+ const addId = value => { const id = String(value || '').trim(); if (id && module.nodeObjectsById.has(id)) fallbackIds.add(id); };
2348
+ module._visibleIds?.forEach?.(addId);
2349
+ module._prevVisibleIds?.forEach?.(addId);
2350
+ addId(module.selectedNodeIdJs);
2351
+ module.multiSelectedNodeIds?.forEach?.(addId);
2352
+ addId(module.draggedNodeId);
2353
+ module.container?.querySelectorAll?.('[data-node-id]')?.forEach?.(element => addId(element.dataset?.nodeId));
2354
+ return Array.from(fallbackIds);
2345
2355
  }
2346
2356
  // ▲▲▲ [New] ▲▲▲
2347
2357
 
@@ -2964,13 +2974,11 @@ window.MindMapInteractions = (function () {
2964
2974
  function getGuardedTargetNodeId(module, target, event) {
2965
2975
  const nodeId = getTargetNodeId(target);
2966
2976
  if (!nodeId || !event) {
2967
- const topCssHit = event
2968
- ? findCssNodeHitAtClientPoint(module, event.clientX, event.clientY)
2969
- : null;
2977
+ const topCssHit = event ? getPointerCssHit(module, event) : null;
2970
2978
  return topCssHit?.nodeId || nodeId;
2971
2979
  }
2972
2980
 
2973
- const topCssHit = findCssNodeHitAtClientPoint(module, event.clientX, event.clientY);
2981
+ const topCssHit = getPointerCssHit(module, event);
2974
2982
  if (topCssHit?.nodeId && topCssHit.nodeId !== nodeId) {
2975
2983
  return topCssHit.nodeId;
2976
2984
  }
@@ -2979,7 +2987,15 @@ window.MindMapInteractions = (function () {
2979
2987
  ? null
2980
2988
  : nodeId;
2981
2989
  }
2982
-
2990
+ function getPointerCssHit(module, event) {
2991
+ if (!event || !module) return null;
2992
+ let context = pointerHitContexts.get(event);
2993
+ if (!context || context.module !== module) {
2994
+ context = { module, cssHit: findCssNodeHitAtClientPoint(module, event.clientX, event.clientY) };
2995
+ pointerHitContexts.set(event, context);
2996
+ }
2997
+ return context.cssHit;
2998
+ }
2983
2999
  function getMountedEmbedCanvasDragTarget(module, target, event) {
2984
3000
  if (!module || !(target instanceof Element)) {
2985
3001
  return null;
@@ -3364,26 +3380,24 @@ window.MindMapInteractions = (function () {
3364
3380
  return;
3365
3381
  }
3366
3382
 
3367
- const selectedIds = new Set();
3368
- if (module.selectedNodeIdJs) {
3369
- selectedIds.add(String(module.selectedNodeIdJs));
3370
- }
3383
+ const selectedIds = new Set(module.selectedNodeIdJs ? [String(module.selectedNodeIdJs)] : []);
3371
3384
  module.multiSelectedNodeIds?.forEach?.(id => selectedIds.add(String(id)));
3372
3385
 
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
- }
3386
+ const previousIds = module._embedInteractivityNodeIds instanceof Set ? module._embedInteractivityNodeIds : new Set();
3387
+ if (previousIds.size === selectedIds.size && [...selectedIds].every(id => previousIds.has(id))) return;
3378
3388
 
3379
- const isSelected = selectedIds.has(String(nodeId || entry?.model?.id || entry?.model?.Id || ''));
3389
+ const changedIds = new Set([...previousIds, ...selectedIds]);
3390
+ changedIds.forEach(nodeId => {
3391
+ const entry = module.nodeObjectsById.get(nodeId);
3392
+ if (String(entry?.model?.contentType ?? entry?.model?.ContentType ?? '').toLowerCase() !== 'embed' || !entry?.cssObject?.element) return;
3393
+ const isSelected = selectedIds.has(nodeId);
3380
3394
  window.MindMapCss3DManager.syncCss3dEmbedInteractivity(entry.cssObject.element, {
3381
3395
  isInteractive: isSelected,
3382
3396
  isSelected
3383
3397
  });
3384
3398
  });
3399
+ module._embedInteractivityNodeIds = selectedIds;
3385
3400
  }
3386
-
3387
3401
  function switchPreviousSelectedNoteToWebGL(module, respectHighMode = true) {
3388
3402
  if (!module?.selectedNodeIdJs) return;
3389
3403
  const prevEntry = module.nodeObjectsById?.get(module.selectedNodeIdJs);
@@ -4235,7 +4249,6 @@ window.MindMapInteractions = (function () {
4235
4249
  }
4236
4250
 
4237
4251
  // ▼▼▼ [Debug] Click timing measurement ▼▼▼
4238
- const clickT0 = performance.now();
4239
4252
  // ▲▲▲ [Debug] ▲▲▲
4240
4253
 
4241
4254
  updatePointer(module, e);
@@ -4245,43 +4258,28 @@ window.MindMapInteractions = (function () {
4245
4258
  const meshesToIntersect = [];
4246
4259
 
4247
4260
  // ▼▼▼ [Debug] ▼▼▼
4248
- const clickT1 = performance.now();
4249
- let gridCellsChecked = 0;
4250
- let nodesChecked = 0;
4251
4261
  let residentLodHit = null;
4252
4262
  // ▲▲▲ [Debug] ▲▲▲
4253
4263
 
4254
4264
  if (module.raycaster.ray.intersectPlane(module.plane, module.intersectPoint)) {
4255
4265
  const nearby = getNearbyMeshes(module, module.intersectPoint);
4256
4266
  meshesToIntersect.push(...nearby);
4267
+ meshesToIntersect._hitTestCandidatesAvailable = nearby._hitTestCandidatesAvailable === true;
4257
4268
 
4258
4269
  // ▼▼▼ [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
- });
4270
+ if (meshesToIntersect.length === 0 && meshesToIntersect._hitTestCandidatesAvailable !== true && module.nodeObjectsById.size > 0) {
4271
+ appendFallbackMeshes(module, module.intersectPoint, meshesToIntersect);
4271
4272
  residentLodHit = module.lodRenderer?.pickResidentNodeAtWorldPoint?.(module.intersectPoint) || null;
4272
4273
  }
4273
4274
  // ▲▲▲ [Fix] ▲▲▲
4274
4275
  }
4275
4276
 
4276
4277
  // ▼▼▼ [Debug] ▼▼▼
4277
- const clickT2 = performance.now();
4278
4278
  // ▲▲▲ [Debug] ▲▲▲
4279
4279
 
4280
4280
  const intersects = module.raycaster.intersectObjects(meshesToIntersect, true);
4281
4281
 
4282
4282
  // ▼▼▼ [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
4283
  // ▲▲▲ [Debug] ▲▲▲
4286
4284
 
4287
4285
  const topIntersect = getTopIntersectionHit(module, intersects);
@@ -4289,7 +4287,7 @@ window.MindMapInteractions = (function () {
4289
4287
  const topIntersectPriority = topIntersect
4290
4288
  ? getIntersectionInteractionPriority(module, topIntersect)
4291
4289
  : -Infinity;
4292
- const cssFallbackHit = findCssNodeHitAtClientPoint(module, e.clientX, e.clientY);
4290
+ const cssFallbackHit = getPointerCssHit(module, e);
4293
4291
  const cssFallbackPriority = cssFallbackHit?.priority ?? -Infinity;
4294
4292
  const shouldPreferCssFallback =
4295
4293
  !!cssFallbackHit?.nodeId &&
@@ -4308,15 +4306,6 @@ window.MindMapInteractions = (function () {
4308
4306
  if (topIntersect && !shouldPreferCssFallback) {
4309
4307
  let clickedMesh = topIntersect.object;
4310
4308
 
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
4309
  // ▲▲▲ [Debug] ▲▲▲
4321
4310
 
4322
4311
  // ▼▼▼ [FIX] 클릭된 대상이 'body'(히트박스)라면 부모 그룹을 선택하도록 강제 보정 ▼▼▼
@@ -4226,30 +4226,10 @@
4226
4226
  }
4227
4227
  // ▲▲▲ [Optimization] ▲▲▲
4228
4228
 
4229
- // ▼▼▼ [Fix] 다른 노드들의 텍스트 선택을 비활성화하여 선택 번짐 방지 ▼▼▼
4230
- module.nodeObjectsById.forEach((otherEntry, otherId) => {
4231
- if (otherId === nodeId) return; // 현재 노드는 건너뛰기
4232
- if (!otherEntry.cssObject?.element) return;
4233
-
4234
- const otherWrapper = otherEntry.cssObject.element;
4235
- // 다른 노드들의 텍스트 선택 비활성화
4236
- const contentEls = otherWrapper.querySelectorAll(NODE_TEXT_SELECTION_SELECTORS);
4237
- contentEls.forEach(el => {
4238
- el.style.userSelect = 'none';
4239
- el.style.webkitUserSelect = 'none';
4240
- });
4241
- });
4242
- // ▲▲▲ [Fix] ▲▲▲
4243
-
4244
- // ▼▼▼ [Fix] 현재 노드의 텍스트 선택 활성화 ▼▼▼
4245
- const currentWrapper = nodeEntry.cssObject.element;
4246
- if (currentWrapper) {
4247
- const contentEls = currentWrapper.querySelectorAll(NODE_TEXT_SELECTION_SELECTORS);
4248
- contentEls.forEach(el => {
4249
- el.style.userSelect = 'text';
4250
- el.style.webkitUserSelect = 'text';
4251
- });
4252
- }
4229
+ // CSS3D text selection ownership is updated as a previous/next delta.
4230
+ // The manager patches only those two wrappers instead of walking the
4231
+ // complete node map on every CSS3D promotion.
4232
+ window.MindMapCss3DManager?.setNativeTextSelectionOwnerClass?.(module, nodeId);
4253
4233
  // ▲▲▲ [Fix] ▲▲▲
4254
4234
 
4255
4235
  // Sync CSS3D scroll position with WebGL/model scrollOffset (ratio-based).
@@ -146,9 +146,10 @@
146
146
 
147
147
  // ▼▼▼ [Perf] Render queue (time-sliced) ▼▼▼
148
148
  const renderQueue = [];
149
- const renderQueueSet = new Set();
150
- let renderQueueScheduled = false;
151
- let renderModuleRef = null;
149
+ const renderQueueSet = new Set();
150
+ let renderQueueScheduled = false;
151
+ let renderModuleRef = null;
152
+ let renderQueueDirty = false;
152
153
  const RENDER_BUDGET_MS = 8;
153
154
  let renderSeq = 0;
154
155
 
@@ -178,9 +179,10 @@
178
179
  function enqueueRender(module, nodeId) {
179
180
  if (!nodeId) return;
180
181
  renderModuleRef = module;
181
- if (!renderQueueSet.has(nodeId)) {
182
- renderQueue.push({ nodeId, priority: computeRenderPriority(module, nodeId), seq: renderSeq++ });
183
- renderQueueSet.add(nodeId);
182
+ if (!renderQueueSet.has(nodeId)) {
183
+ renderQueue.push({ nodeId, priority: computeRenderPriority(module, nodeId), seq: renderSeq++ });
184
+ renderQueueSet.add(nodeId);
185
+ renderQueueDirty = true;
184
186
  }
185
187
  scheduleRenderQueue();
186
188
  }
@@ -195,12 +197,15 @@
195
197
  const start = performance.now();
196
198
 
197
199
  // 우선순위 정렬 (가시/선택/거리)
198
- if (renderQueue.length > 1) {
199
- renderQueue.sort((a, b) => (a.priority - b.priority) || (a.seq - b.seq));
200
- }
201
-
202
- while (renderQueue.length > 0) {
203
- const item = renderQueue.shift();
200
+ if (renderQueueDirty) {
201
+ if (renderQueue.length > 1) {
202
+ renderQueue.sort((a, b) => (b.priority - a.priority) || (b.seq - a.seq));
203
+ }
204
+ renderQueueDirty = false;
205
+ }
206
+
207
+ while (renderQueue.length > 0) {
208
+ const item = renderQueue.pop();
204
209
  const nodeId = item.nodeId;
205
210
  renderQueueSet.delete(nodeId);
206
211
  if (renderModuleRef) {
@@ -224,7 +229,8 @@
224
229
  // ▲▲▲ [New] ▲▲▲
225
230
 
226
231
  // ▼▼▼ [Perf] Deferred disposal to avoid GC/driver spikes ▼▼▼
227
- const disposeQueue = [];
232
+ const disposeQueue = [];
233
+ let disposeQueueHead = 0;
228
234
  let disposeScheduled = false;
229
235
  const DISPOSE_BUDGET_MS = 6; // soft budget per frame
230
236
 
@@ -246,8 +252,8 @@
246
252
 
247
253
  function processDisposeQueue(deadline) {
248
254
  const start = performance.now();
249
- while (disposeQueue.length > 0) {
250
- const item = disposeQueue.shift();
255
+ while (disposeQueueHead < disposeQueue.length) {
256
+ const item = disposeQueue[disposeQueueHead++];
251
257
  try { item.dispose(); } catch { }
252
258
 
253
259
  if (deadline && typeof deadline.timeRemaining === 'function') {
@@ -258,9 +264,16 @@
258
264
  }
259
265
 
260
266
  disposeScheduled = false;
261
- if (disposeQueue.length > 0) {
262
- scheduleDispose();
263
- }
267
+ if (disposeQueueHead >= disposeQueue.length) {
268
+ disposeQueue.length = 0;
269
+ disposeQueueHead = 0;
270
+ } else if (disposeQueue.length - disposeQueueHead > 64) {
271
+ disposeQueue.splice(0, disposeQueueHead);
272
+ disposeQueueHead = 0;
273
+ }
274
+ if (disposeQueueHead < disposeQueue.length) {
275
+ scheduleDispose();
276
+ }
264
277
  }
265
278
  // ▲▲▲ [Perf] ▲▲▲
266
279
 
@@ -98,7 +98,7 @@
98
98
  return true;
99
99
  }
100
100
 
101
- function ensureDecoder(canvas, frame, state) {
101
+ function ensureDecoder(canvas, frame, state, onPaint) {
102
102
  if (typeof VideoDecoder !== 'function' || typeof EncodedVideoChunk !== 'function') {
103
103
  return null;
104
104
  }
@@ -106,6 +106,8 @@
106
106
  const width = Number(frame.width || 0) || canvas.width || 1920;
107
107
  const height = Number(frame.height || 0) || canvas.height || 1080;
108
108
  const key = `${width}x${height}`;
109
+ state.onPaint = onPaint;
110
+ state.latestFrame = frame;
109
111
  if (state.decoder && state.width === width && state.height === height) {
110
112
  return state.decoder;
111
113
  }
@@ -126,6 +128,7 @@
126
128
  const context = canvas.getContext('2d', { alpha: false, desynchronized: true });
127
129
  context?.drawImage(videoFrame, 0, 0, canvas.width, canvas.height);
128
130
  state.lastPaintAt = performance.now();
131
+ try { state.onPaint?.(canvas, state.latestFrame || frame); } catch (error) { emit('remote.h264.paintCallbackFailed', { error: error?.message || String(error || '') }); }
129
132
  emit('remote.h264.paint', { frameSeq: state.lastSeq, width: canvas.width, height: canvas.height });
130
133
  } finally {
131
134
  videoFrame.close?.();
@@ -154,7 +157,7 @@
154
157
  return decoder;
155
158
  }
156
159
 
157
- async function decode(canvas, frame) {
160
+ async function decode(canvas, frame, onPaint) {
158
161
  const payload = frame?._remoteFleetPayloadBlob;
159
162
  if (!canvas || !payload) {
160
163
  return false;
@@ -168,7 +171,7 @@
168
171
  const seq = Number(frame.streamFrameSeq || frame.frameSeq || 0) || 0;
169
172
  if (seq > 0 && seq <= state.lastSeq) return true;
170
173
  state.lastSeq = seq;
171
- const decoder = ensureDecoder(canvas, frame, state);
174
+ const decoder = ensureDecoder(canvas, frame, state, onPaint);
172
175
  if (!decoder || decoder.state === 'closed') return false;
173
176
  if (state.pending >= MAX_DECODE_QUEUE) {
174
177
  emit('remote.h264.drop', { reason: 'decode-queue', frameSeq: seq });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-vyIwPs5NXG0WvUn1crlwB/OCRyxH6tUNE8CXfOaHuOI=",
4
+ "hash": "sha256-o6tmG5oGpl7vciF713W3t451harGm461qOhCvK87lOE=",
5
5
  "fingerprinting": {
6
6
  "Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
7
7
  "Markdig.d1j7v41cl1.dll": "Markdig.dll",
@@ -122,16 +122,16 @@
122
122
  "System.brmz7yk5qh.dll": "System.dll",
123
123
  "netstandard.b50t77veor.dll": "netstandard.dll",
124
124
  "System.Private.CoreLib.g6ztz7cmo2.dll": "System.Private.CoreLib.dll",
125
- "MindExecution.Core.4moacskf64.dll": "MindExecution.Core.dll",
126
- "MindExecution.Kernel.pa251cfsz4.dll": "MindExecution.Kernel.dll",
127
- "MindExecution.Plugins.Admin.dxyuwmxv1s.dll": "MindExecution.Plugins.Admin.dll",
128
- "MindExecution.Plugins.Business.7uj5erqhhn.dll": "MindExecution.Plugins.Business.dll",
129
- "MindExecution.Plugins.Concept.108rmsxc96.dll": "MindExecution.Plugins.Concept.dll",
130
- "MindExecution.Plugins.Directory.ccsy3g0fe5.dll": "MindExecution.Plugins.Directory.dll",
131
- "MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll": "MindExecution.Plugins.PlanMaster.dll",
132
- "MindExecution.Plugins.YouTube.q8ml98phj9.dll": "MindExecution.Plugins.YouTube.dll",
133
- "MindExecution.Shared.2pywq19zjy.dll": "MindExecution.Shared.dll",
134
- "MindExecution.Web.qucbls1wfr.dll": "MindExecution.Web.dll",
125
+ "MindExecution.Core.djyzj5mxyk.dll": "MindExecution.Core.dll",
126
+ "MindExecution.Kernel.6e5cj9aijz.dll": "MindExecution.Kernel.dll",
127
+ "MindExecution.Plugins.Admin.siulh9tfrf.dll": "MindExecution.Plugins.Admin.dll",
128
+ "MindExecution.Plugins.Business.8cjtrc7qd3.dll": "MindExecution.Plugins.Business.dll",
129
+ "MindExecution.Plugins.Concept.rpp78mc75u.dll": "MindExecution.Plugins.Concept.dll",
130
+ "MindExecution.Plugins.Directory.wfv0ff9v4u.dll": "MindExecution.Plugins.Directory.dll",
131
+ "MindExecution.Plugins.PlanMaster.jdppnbd6g4.dll": "MindExecution.Plugins.PlanMaster.dll",
132
+ "MindExecution.Plugins.YouTube.a43f2q3pkw.dll": "MindExecution.Plugins.YouTube.dll",
133
+ "MindExecution.Shared.gwtyj0e9lw.dll": "MindExecution.Shared.dll",
134
+ "MindExecution.Web.j3zaapcxtt.dll": "MindExecution.Web.dll",
135
135
  "dotnet.native.566r55w3xu.js": "dotnet.native.js",
136
136
  "dotnet.native.x6q5aixc38.wasm": "dotnet.native.wasm",
137
137
  "dotnet.js": "dotnet.js",
@@ -276,18 +276,18 @@
276
276
  "System.Xml.XDocument.sn51jas17n.dll": "sha256-GNI2kFgFmPTwzuzwUn8gxK+AzGLUWRJFdg9JzIbrybQ=",
277
277
  "System.brmz7yk5qh.dll": "sha256-CfM2miyj1KHApFmqMdLYWio3S/jrdON2pW9Xr2nTwlo=",
278
278
  "netstandard.b50t77veor.dll": "sha256-//jQGOXjb8ET8WtXgOJrAxLx6E7zDJ5RjRRutwkvmxo=",
279
- "MindExecution.Core.4moacskf64.dll": "sha256-5OiBWkHcOvL4NsaHtQ7O3z5N1Ba7nTRi3xkehUMMgkE=",
280
- "MindExecution.Kernel.pa251cfsz4.dll": "sha256-4dswyE6zmKAS+CnExpbXK871UMxIp+a7SPMIMwmZ+2Q=",
281
- "MindExecution.Plugins.Business.7uj5erqhhn.dll": "sha256-URKvk2etEOiEa/URXJpkYx7aJcPQd1Xs0Yxn3Uqg/ok=",
282
- "MindExecution.Plugins.Concept.108rmsxc96.dll": "sha256-QTd+zvSVcfwz3gYaKZ3pP0wgxrj9R1aP6qQKsByDRR0=",
283
- "MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll": "sha256-M3Fvm8WU5xbx24cTyACzH1IuZXuIY06dkeCfNCgeUT4=",
284
- "MindExecution.Shared.2pywq19zjy.dll": "sha256-Wjp+pPwb4TDj0YtYTzvNQGIzQI7xAg9DeyO0g/jxyxU=",
285
- "MindExecution.Web.qucbls1wfr.dll": "sha256-0kpuM4z2XvRReo2Eiy7cJ/SAfoF/dVnn6WChaz//lGc="
279
+ "MindExecution.Core.djyzj5mxyk.dll": "sha256-mWD5B7qIqi95K+2PlXhtVYKTU0NXvGjFUMZIIKhgOJM=",
280
+ "MindExecution.Kernel.6e5cj9aijz.dll": "sha256-soI6Vf+R5EO4br1BPNT8w1Dojx0MrwEpnoU3hlN/Jo4=",
281
+ "MindExecution.Plugins.Business.8cjtrc7qd3.dll": "sha256-KKJ9wMXw9lRRp8Yej3hIx2A/KlT4IMnCNvE6nC15NHc=",
282
+ "MindExecution.Plugins.Concept.rpp78mc75u.dll": "sha256-EeJ9pPy1kMPUMU1/VZq086s7Rpx8Kl8JNJin/q8CCk0=",
283
+ "MindExecution.Plugins.PlanMaster.jdppnbd6g4.dll": "sha256-tx9+RxTaAfdk4GoSZt3mj8tZfGOwNAlK/JMf0NyrkUY=",
284
+ "MindExecution.Shared.gwtyj0e9lw.dll": "sha256-4N4HdjdoXtRylfVW6OYliAy9JOlx67Vudw/CbXjCZDs=",
285
+ "MindExecution.Web.j3zaapcxtt.dll": "sha256-0eGBQjTRRbbCk0PMweH2fX57WJ/z8Nj2h8IRCvURzUI="
286
286
  },
287
287
  "lazyAssembly": {
288
- "MindExecution.Plugins.Admin.dxyuwmxv1s.dll": "sha256-URTQOP1nUuBC2leUgGdGWb+rc/1blCxbpbhRGE1MW/I=",
289
- "MindExecution.Plugins.Directory.ccsy3g0fe5.dll": "sha256-BFk4mut2rsioBC9unD8+BoxnoJqTJlS5uZV7uXr8i5E=",
290
- "MindExecution.Plugins.YouTube.q8ml98phj9.dll": "sha256-2vzs6nizBsYO1JjXPxdSi6O7uyZLpqFblwE0q09mvfw="
288
+ "MindExecution.Plugins.Admin.siulh9tfrf.dll": "sha256-xJKUGBrCw9pX8r+5p5uCn5oEikqnBhhN8PQk1UQXvwk=",
289
+ "MindExecution.Plugins.Directory.wfv0ff9v4u.dll": "sha256-LPqtVKjGSWJC9OfNV3Sc/ohbNBfwPMkt/fvpx4nMRnQ=",
290
+ "MindExecution.Plugins.YouTube.a43f2q3pkw.dll": "sha256-duhXTTnMwRKd8YPDL5IkWyunMITVbxMbk0xWZXdaTPU="
291
291
  }
292
292
  },
293
293
  "cacheBootResources": true,
@@ -579,7 +579,7 @@
579
579
  }
580
580
 
581
581
  const base = '_content/MindExecution.Shared/js/';
582
- const scriptVersion = '20260717-livedesk-wall-lzo-control-h264-v937';
582
+ const scriptVersion = '20260724-near-css3d-input-optimization-v939';
583
583
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
584
584
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
585
585
  const criticalScripts = [