@mindexec/cli 0.2.440 → 0.2.442

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 (49) hide show
  1. package/package.json +1 -1
  2. package/remote-fast/osx-arm64/MacScreenCaptureKitH264.swift +339 -0
  3. package/remote-fast/osx-arm64/MacSystemAudioCapture.swift +214 -0
  4. package/remote-fast/osx-arm64/UnixRawCaptureHelper.mjs +80 -0
  5. package/remote-fast/osx-arm64/livedesk-raw-capture-resampler.mjs +73 -0
  6. package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
  7. package/remote-fast/osx-x64/MacScreenCaptureKitH264.swift +339 -0
  8. package/remote-fast/osx-x64/MacSystemAudioCapture.swift +214 -0
  9. package/remote-fast/osx-x64/UnixRawCaptureHelper.mjs +80 -0
  10. package/remote-fast/osx-x64/livedesk-raw-capture-resampler.mjs +73 -0
  11. package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
  12. package/remote-fast/win-x64/NAudio.Asio.dll +0 -0
  13. package/remote-fast/win-x64/NAudio.Core.dll +0 -0
  14. package/remote-fast/win-x64/NAudio.Midi.dll +0 -0
  15. package/remote-fast/win-x64/NAudio.Wasapi.dll +0 -0
  16. package/remote-fast/win-x64/NAudio.WinForms.dll +0 -0
  17. package/remote-fast/win-x64/NAudio.WinMM.dll +0 -0
  18. package/remote-fast/win-x64/NAudio.dll +0 -0
  19. package/remote-fast/win-x64/SharpGen.Runtime.COM.dll +0 -0
  20. package/remote-fast/win-x64/SharpGen.Runtime.dll +0 -0
  21. package/remote-fast/win-x64/Vortice.DXGI.dll +0 -0
  22. package/remote-fast/win-x64/Vortice.Direct3D11.dll +0 -0
  23. package/remote-fast/win-x64/Vortice.DirectX.dll +0 -0
  24. package/remote-fast/win-x64/Vortice.Mathematics.dll +0 -0
  25. package/remote-fast/win-x64/mindexec-remote-fast.deps.json +238 -0
  26. package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
  27. package/remote-fast/win-x64/mindexec-remote-fast.exe +0 -0
  28. package/remote-hub.js +42 -3
  29. package/server.js +22 -6
  30. package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +49 -0
  31. package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +6 -2
  32. package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +70 -60
  33. package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +32 -6
  34. package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +126 -0
  35. package/wwwroot/_content/MindExecution.Shared/js/mind-map-video-playback-controls.js +95 -0
  36. package/wwwroot/_framework/{MindExecution.Core.808jj176e1.dll → MindExecution.Core.nxjlewduna.dll} +0 -0
  37. package/wwwroot/_framework/{MindExecution.Kernel.75hxk08tt0.dll → MindExecution.Kernel.vrfi63yg9f.dll} +0 -0
  38. package/wwwroot/_framework/{MindExecution.Plugins.Admin.0zkmk8wahq.dll → MindExecution.Plugins.Admin.1rcwhdxl0r.dll} +0 -0
  39. package/wwwroot/_framework/{MindExecution.Plugins.Business.vyw3769hrw.dll → MindExecution.Plugins.Business.rx0eieg51y.dll} +0 -0
  40. package/wwwroot/_framework/{MindExecution.Plugins.Concept.cvmglrxsjb.dll → MindExecution.Plugins.Concept.zzczjzhvk1.dll} +0 -0
  41. package/wwwroot/_framework/{MindExecution.Plugins.Directory.jtw9q1dm9n.dll → MindExecution.Plugins.Directory.bsorcntiwp.dll} +0 -0
  42. package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll → MindExecution.Plugins.PlanMaster.i5j9t4f1jf.dll} +0 -0
  43. package/wwwroot/_framework/{MindExecution.Plugins.YouTube.xb9sou9ox0.dll → MindExecution.Plugins.YouTube.1mpn9satft.dll} +0 -0
  44. package/wwwroot/_framework/{MindExecution.Shared.pqab76cjb6.dll → MindExecution.Shared.mpi1gzau9b.dll} +0 -0
  45. package/wwwroot/_framework/{MindExecution.Web.mc24u4bzol.dll → MindExecution.Web.nxykcyxd02.dll} +0 -0
  46. package/wwwroot/_framework/blazor.boot.json +21 -21
  47. package/wwwroot/index.html +3 -1
  48. package/wwwroot/service-worker-assets.js +35 -27
  49. package/wwwroot/service-worker.js +1 -1
@@ -7204,8 +7204,12 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
7204
7204
  window.MindMapMenuManager.hideMenu();
7205
7205
  }
7206
7206
  _profileSection('menu');
7207
- if (isMultiSelectOverlayUiEnabled && window.MindMapMultiSelect && shouldDeferFloatingUiDuringMotion !== true) {
7208
- window.MindMapMultiSelect.update();
7207
+ if (isMultiSelectOverlayUiEnabled && window.MindMapMultiSelect) {
7208
+ if (shouldDeferFloatingUiDuringMotion === true) {
7209
+ window.MindMapMultiSelect.updateForCameraMotion?.();
7210
+ } else {
7211
+ window.MindMapMultiSelect.update();
7212
+ }
7209
7213
  } else if (!isMultiSelectOverlayUiEnabled && window.MindMapMultiSelect?.hide) {
7210
7214
  window.MindMapMultiSelect.hide();
7211
7215
  }
@@ -2013,6 +2013,7 @@
2013
2013
  const preferredSource = getCss3dVideoDeferredSource(videoEl, nodeModel);
2014
2014
  if (videoEl.dataset) {
2015
2015
  videoEl.dataset.lodMediaSuspended = 'false';
2016
+ videoEl.dataset.videoPositionRestorePending = preferredSource ? 'true' : 'false';
2016
2017
  }
2017
2018
  videoEl.preload = videoEl.dataset?.lodSuspendedPreload || 'metadata';
2018
2019
 
@@ -3223,7 +3224,6 @@
3223
3224
  const normalizedIntent = normalizeVideoNodePlaybackIntent(intent) || 'pause';
3224
3225
  if (nodeModel) {
3225
3226
  nodeModel._videoPlayIntent = normalizedIntent;
3226
- setNodeMetadataValue(nodeModel, VIDEO_PLAYBACK_INTENT_METADATA_KEY, normalizedIntent);
3227
3227
  }
3228
3228
  if (videoEl?.dataset) {
3229
3229
  videoEl.autoplay = normalizedIntent === 'play';
@@ -3233,6 +3233,7 @@
3233
3233
  if (options?.persist === true) {
3234
3234
  persistVideoNodePlaybackIntent(nodeModel, normalizedIntent);
3235
3235
  }
3236
+ window.MindMapVideoPlaybackControls?.sync?.(videoEl, normalizedIntent);
3236
3237
  return normalizedIntent;
3237
3238
  }
3238
3239
 
@@ -3290,50 +3291,13 @@
3290
3291
  }
3291
3292
 
3292
3293
  function bindVideoPlaybackStateTracking(videoEl, nodeModel) {
3293
- if (!videoEl || !nodeModel) {
3294
- return;
3295
- }
3296
-
3297
- const nodeId = getNodeId(nodeModel);
3298
- if (videoEl.dataset?.videoPlaybackStateBoundNodeId === nodeId) {
3299
- return;
3300
- }
3301
-
3302
- if (videoEl.dataset) {
3303
- videoEl.dataset.videoPlaybackStateBoundNodeId = nodeId || '';
3304
- }
3305
-
3306
- const rememberTimeOnly = () => {
3307
- const currentTime = Number(videoEl.currentTime || 0);
3308
- if (Number.isFinite(currentTime) && currentTime >= 0) {
3309
- nodeModel._videoPlaybackCurrentTime = currentTime;
3310
- if (videoEl.dataset) {
3311
- videoEl.dataset.videoLastKnownTime = String(currentTime);
3312
- }
3313
- }
3314
- };
3315
-
3316
- const markPlaying = () => {
3317
- rememberVideoNodeRuntimePlayback(nodeModel, videoEl, { forceIntent: true });
3318
- setVideoNodePlaybackIntent(nodeModel, videoEl, 'play');
3319
- };
3320
-
3321
- const markPaused = () => {
3322
- rememberTimeOnly();
3323
- if (videoEl.dataset?.visibilityPaused === 'true' || videoEl.dataset?.videoResumePending === 'true') {
3324
- return;
3325
- }
3326
- setVideoNodePlaybackIntent(nodeModel, videoEl, 'pause');
3327
- };
3328
-
3329
- videoEl.addEventListener('play', markPlaying, { passive: true });
3330
- videoEl.addEventListener('playing', markPlaying, { passive: true });
3331
- videoEl.addEventListener('pause', markPaused, { passive: true });
3332
- videoEl.addEventListener('timeupdate', rememberTimeOnly, { passive: true });
3333
- videoEl.addEventListener('seeking', rememberTimeOnly, { passive: true });
3334
- videoEl.addEventListener('loadedmetadata', () => {
3335
- restoreVideoNodeRuntimePlaybackPosition(nodeModel, videoEl);
3336
- }, { passive: true });
3294
+ return window.MindMapVideoPlaybackControls?.bindState?.(videoEl, nodeModel, {
3295
+ getNodeId,
3296
+ getIntent: getVideoNodePlaybackIntent,
3297
+ rememberRuntime: rememberVideoNodeRuntimePlayback,
3298
+ restorePosition: restoreVideoNodeRuntimePlaybackPosition,
3299
+ setIntent: setVideoNodePlaybackIntent
3300
+ });
3337
3301
  }
3338
3302
 
3339
3303
  function ensureVideoNodePlaybackState(nodeModel, videoEl) {
@@ -4498,12 +4462,12 @@
4498
4462
  syncMediaLoadingRowForElement(mediaEl, nodeModel, contentTypeLower);
4499
4463
  mediaEl.dataset.assetRefreshAttempted = '';
4500
4464
  if (contentTypeLower === 'video') {
4501
- if (mediaEl.dataset?.lodMediaSuspended === 'true') {
4502
- return;
4503
- }
4504
- const playPromise = mediaEl.play?.();
4505
- if (playPromise?.catch) {
4506
- playPromise.catch(() => { });
4465
+ restoreVideoNodeRuntimePlaybackPosition(nodeModel, mediaEl);
4466
+ mediaEl.dataset.videoPositionRestorePending = 'false';
4467
+ const entry = getNodeEntryById(getNodeId(nodeModel));
4468
+ if (getVideoNodePlaybackIntent(nodeModel, mediaEl) === 'play' &&
4469
+ mediaEl.dataset?.userPaused !== 'true' && entry?.cssObject) {
4470
+ playVideoNodeForVisibility(nodeModel, entry.cssObject, mediaEl);
4507
4471
  }
4508
4472
  }
4509
4473
  };
@@ -14927,9 +14891,9 @@
14927
14891
  const REMOTE_FLEET_MONITOR_REFRESH_MS = 10000;
14928
14892
  const REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS = 2500;
14929
14893
  const REMOTE_FLEET_LIVE_REFRESH_MS = 30000;
14930
- const REMOTE_FLEET_MONITOR_LIVE_FPS = 12;
14894
+ const REMOTE_FLEET_MONITOR_LIVE_FPS = 20;
14931
14895
  const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_MONITOR_LIVE_FPS);
14932
- const REMOTE_FLEET_CONTROL_LIVE_FPS = 20;
14896
+ const REMOTE_FLEET_CONTROL_LIVE_FPS = 30;
14933
14897
  const REMOTE_FLEET_CONTROL_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_CONTROL_LIVE_FPS);
14934
14898
  const REMOTE_FLEET_CONTROL_INPUT_MOVE_MS = 16;
14935
14899
  const REMOTE_FLEET_CONTROL_INPUT_QUEUE_LIMIT = 64;
@@ -15703,7 +15667,8 @@
15703
15667
  Number(liveOptions.maxWidth || 0) || 0,
15704
15668
  Number(liveOptions.maxHeight || 0) || 0,
15705
15669
  Number(liveOptions.quality || 0) || 0,
15706
- String(liveOptions.mode || '')
15670
+ String(liveOptions.mode || ''),
15671
+ String(liveOptions.streamPurpose || 'wall')
15707
15672
  ].join('|');
15708
15673
  const key = `${deviceIds.join('\n')}::${optionKey}`;
15709
15674
  const now = getRemoteFleetFrameNow();
@@ -15728,7 +15693,9 @@
15728
15693
  maxWidth: liveOptions.maxWidth,
15729
15694
  maxHeight: liveOptions.maxHeight,
15730
15695
  quality: liveOptions.quality,
15731
- mode: liveOptions.mode
15696
+ mode: liveOptions.mode,
15697
+ frameMode: liveOptions.mode,
15698
+ streamPurpose: liveOptions.streamPurpose || 'wall'
15732
15699
  }));
15733
15700
  return true;
15734
15701
  }
@@ -15988,7 +15955,8 @@
15988
15955
  maxWidth: Number(options.maxWidth || 960) || 960,
15989
15956
  maxHeight: Number(options.maxHeight || 540) || 540,
15990
15957
  quality: Number(options.quality || 60) || 60,
15991
- mode: String(options.mode || 'remote-fast')
15958
+ mode: String(options.mode || 'mode3-h264-hw'),
15959
+ streamPurpose: String(options.streamPurpose || 'wall')
15992
15960
  };
15993
15961
  bodyView._remoteFleetBinaryFrameSession = session;
15994
15962
  primeRemoteFleetRenderedFrameSurfaces(bodyView, 'ws-start');
@@ -17222,6 +17190,20 @@
17222
17190
  }
17223
17191
 
17224
17192
  function applyRemoteFleetFramePatchToPreview(preview, frame) {
17193
+ if (frame?._remoteFleetBinaryFrame === true
17194
+ && frame?._remoteFleetPayloadBlob
17195
+ && String(frame.codec || '').toLowerCase().includes('h264')
17196
+ && window.MindExecRemoteCodecs?.decodeH264Frame) {
17197
+ const canvas = ensureRemoteFleetFrameCanvas(preview);
17198
+ if (canvas) {
17199
+ canvas.style.display = 'block';
17200
+ preview.querySelector?.('img[data-remote-fleet-frame-image="true"]')?.style.setProperty('display', 'none');
17201
+ window.MindExecRemoteCodecs.decodeH264Frame(canvas, frame).catch?.(() => undefined);
17202
+ preview.dataset.remoteFleetFrameKind = String(frame.kind || 'live').toLowerCase();
17203
+ preview.dataset.remoteFleetFrameSeq = String(frame.frameSeq || 0);
17204
+ return true;
17205
+ }
17206
+ }
17225
17207
  if (frame?._remoteFleetBinaryFrame === true && frame?._remoteFleetPayloadBlob) {
17226
17208
  preview._remoteFleetPendingImageFrame = null;
17227
17209
  delete preview.dataset.remoteFleetPendingImageKind;
@@ -17451,7 +17433,8 @@
17451
17433
  maxWidth: 1280,
17452
17434
  maxHeight: 720,
17453
17435
  quality: 65,
17454
- mode: 'remote-fast-control'
17436
+ mode: 'mode3-h264-hw',
17437
+ streamPurpose: 'control'
17455
17438
  })) {
17456
17439
  return false;
17457
17440
  }
@@ -17474,7 +17457,8 @@
17474
17457
  maxWidth: 1280,
17475
17458
  maxHeight: 720,
17476
17459
  quality: 65,
17477
- mode: 'remote-fast-control'
17460
+ mode: 'mode3-h264-hw',
17461
+ streamPurpose: 'control'
17478
17462
  };
17479
17463
  controlSession.binaryFrameSession = binarySession;
17480
17464
  controlSession.binaryFramePreferred = true;
@@ -17550,6 +17534,22 @@
17550
17534
  session.lastControlFrameSeq = frameSeq;
17551
17535
  }
17552
17536
 
17537
+ if (frame._remoteFleetPayloadBlob
17538
+ && String(frame.codec || '').toLowerCase().includes('h264')
17539
+ && window.MindExecRemoteCodecs?.decodeH264Frame) {
17540
+ const painted = await window.MindExecRemoteCodecs.decodeH264Frame(session.canvas, frame);
17541
+ if (painted && session.active) {
17542
+ session.canvas.dataset.remoteFleetFrameKind = 'live';
17543
+ session.canvas.dataset.remoteFleetFrameSeq = String(frameSeq || 0);
17544
+ session.canvas.style.display = 'block';
17545
+ session.lastControlFrameAt = Date.now();
17546
+ session.status.textContent = `Control ${REMOTE_FLEET_CONTROL_LIVE_FPS}fps`;
17547
+ session.placeholder?.remove?.();
17548
+ session.placeholder = null;
17549
+ }
17550
+ return painted;
17551
+ }
17552
+
17553
17553
  let bitmap = null;
17554
17554
  try {
17555
17555
  if (frame._remoteFleetPayloadBlob && typeof createImageBitmap === 'function') {
@@ -17820,11 +17820,16 @@
17820
17820
  }
17821
17821
 
17822
17822
  const input = createRemoteFleetControlInput(session, payload);
17823
+ const inputSeq = Number(session.inputSeq || 0) + 1;
17824
+ session.inputSeq = inputSeq;
17825
+ input.inputSeq = inputSeq;
17826
+ input.requestAck = String(input.type || '').toLowerCase() !== 'pointermove' || inputSeq % 6 === 0;
17823
17827
  const envelope = {
17824
17828
  type: 'input',
17825
17829
  nodeId: session.nodeId,
17826
17830
  deviceId: session.deviceId,
17827
17831
  requestId: `input-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
17832
+ fireAndForget: input.requestAck !== true,
17828
17833
  input
17829
17834
  };
17830
17835
 
@@ -18351,7 +18356,7 @@
18351
18356
  maxWidth: 1280,
18352
18357
  maxHeight: 720,
18353
18358
  quality: 65,
18354
- mode: 'remote-fast-control-node'
18359
+ mode: 'mode3-h264-hw'
18355
18360
  });
18356
18361
  attachRemoteFleetControlBinaryFrameSession(session, bodyView);
18357
18362
  stage.focus?.({ preventScroll: true });
@@ -20712,7 +20717,8 @@
20712
20717
  maxWidth: 960,
20713
20718
  maxHeight: 540,
20714
20719
  quality: 60,
20715
- mode: 'remote-fast'
20720
+ mode: 'mode3-h264-hw',
20721
+ streamPurpose: 'wall'
20716
20722
  });
20717
20723
  if (!binaryFrameSocketStarted) {
20718
20724
  window.RuntimeTrace?.emit?.('remote.frame.wsUnavailable', {
@@ -23678,6 +23684,10 @@
23678
23684
 
23679
23685
  responseDiv.appendChild(video);
23680
23686
  responseDiv.appendChild(canvasProxy);
23687
+ window.MindMapVideoPlaybackControls?.mount?.(responseDiv, video, {
23688
+ getIntent: () => getVideoNodePlaybackIntent(nodeModel, video),
23689
+ onToggle: shouldPlay => setVideoNodePlayback(getNodeId(nodeModel), shouldPlay)
23690
+ });
23681
23691
  } else if (contentTypeLower === 'embed') {
23682
23692
  responseDiv.className = 'node-response embed-content';
23683
23693
  responseDiv.style.cssText = `width: 100%; height: 100%; display: block; position: relative; pointer-events: auto; padding: 0 !important; margin: 0 !important; background: transparent; border: 0 !important; overflow: hidden; scrollbar-width: none;`;
@@ -343,7 +343,7 @@
343
343
  return nodeEntry?.model?.contentType === 'image' ? nodeId : null;
344
344
  }
345
345
 
346
- function updateSingleImageOverlay() {
346
+ function updateSingleImageOverlay(forceWorldProjection = false) {
347
347
  if (!_module || !singleImageOverlay || !_module.camera || !_module.container) return;
348
348
 
349
349
  const nodeId = getSingleSelectedImageNodeId();
@@ -360,7 +360,10 @@
360
360
  return;
361
361
  }
362
362
 
363
- const screenRect = getNodeScreenRect(nodeEntry);
363
+ // Camera-only motion must stay on the projection fast path. Reading the
364
+ // CSS node rect here can force layout and may still reflect the previous
365
+ // CSS3D camera transform when this overlay is updated in the same frame.
366
+ const screenRect = forceWorldProjection ? null : getNodeScreenRect(nodeEntry);
364
367
  if (screenRect) {
365
368
  const screenWidth = Math.max(0, screenRect.right - screenRect.left);
366
369
  const screenHeight = Math.max(0, screenRect.bottom - screenRect.top);
@@ -1594,6 +1597,28 @@
1594
1597
  updateMultiSelectOverlay();
1595
1598
  updateSingleImageOverlay();
1596
1599
  }
1600
+
1601
+ // Camera-only fast path: currentBounds and node world frames are already
1602
+ // canonical, so reproject only the fixed overlays. This deliberately avoids
1603
+ // selected-node scans, DOM layout reads, menu work, and renderer rebuilds.
1604
+ function updateForCameraMotion() {
1605
+ if (!_module || !_module.camera || !_module.container) return false;
1606
+
1607
+ let updated = false;
1608
+ if (multiSelectOverlay &&
1609
+ multiSelectOverlay.style.display !== 'none' &&
1610
+ currentBounds) {
1611
+ updateOverlayPosition(null, true);
1612
+ updated = true;
1613
+ }
1614
+
1615
+ if (getSingleSelectedImageNodeId()) {
1616
+ updateSingleImageOverlay(true);
1617
+ updated = true;
1618
+ }
1619
+
1620
+ return updated;
1621
+ }
1597
1622
  // ▲▲▲ [Perf] ▲▲▲
1598
1623
 
1599
1624
  // ▼▼▼ [New] Dynamic menu button creation based on selection type ▼▼▼
@@ -1701,10 +1726,11 @@
1701
1726
 
1702
1727
  window.MindMapMultiSelect = {
1703
1728
  init,
1704
- showMultiSelectBounds,
1705
- hide,
1706
- update,
1707
- checkAndUpdateDisplay,
1729
+ showMultiSelectBounds,
1730
+ hide,
1731
+ update,
1732
+ updateForCameraMotion,
1733
+ checkAndUpdateDisplay,
1708
1734
  getSelectedImageNodeIds,
1709
1735
  fitTextNodesToContentAndReflowY,
1710
1736
  isResizing: () => isResizing,
@@ -0,0 +1,126 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ const decoderByCanvas = new WeakMap();
5
+ const MAX_DECODE_QUEUE = 3;
6
+
7
+ function emit(type, data) {
8
+ window.RuntimeTrace?.emit?.(type, data);
9
+ }
10
+
11
+ function getDecoderState(canvas) {
12
+ let state = decoderByCanvas.get(canvas);
13
+ if (state) return state;
14
+ state = {
15
+ decoder: null,
16
+ generation: 0,
17
+ width: 0,
18
+ height: 0,
19
+ pending: 0,
20
+ lastSeq: 0,
21
+ lastPaintAt: 0
22
+ };
23
+ decoderByCanvas.set(canvas, state);
24
+ return state;
25
+ }
26
+
27
+ function closeDecoder(state) {
28
+ try { state.decoder?.close?.(); } catch { /* stale decoder */ }
29
+ state.decoder = null;
30
+ state.pending = 0;
31
+ }
32
+
33
+ function ensureDecoder(canvas, frame, state) {
34
+ if (typeof VideoDecoder !== 'function' || typeof EncodedVideoChunk !== 'function') {
35
+ return null;
36
+ }
37
+
38
+ const width = Number(frame.width || 0) || canvas.width || 1920;
39
+ const height = Number(frame.height || 0) || canvas.height || 1080;
40
+ const key = `${width}x${height}`;
41
+ if (state.decoder && state.width === width && state.height === height) {
42
+ return state.decoder;
43
+ }
44
+
45
+ closeDecoder(state);
46
+ state.width = width;
47
+ state.height = height;
48
+ const generation = ++state.generation;
49
+ const decoder = new VideoDecoder({
50
+ output(videoFrame) {
51
+ if (generation !== state.generation) {
52
+ videoFrame.close?.();
53
+ return;
54
+ }
55
+ try {
56
+ canvas.width = videoFrame.displayWidth || width;
57
+ canvas.height = videoFrame.displayHeight || height;
58
+ const context = canvas.getContext('2d', { alpha: false, desynchronized: true });
59
+ context?.drawImage(videoFrame, 0, 0, canvas.width, canvas.height);
60
+ state.lastPaintAt = performance.now();
61
+ emit('remote.h264.paint', { frameSeq: state.lastSeq, width: canvas.width, height: canvas.height });
62
+ } finally {
63
+ videoFrame.close?.();
64
+ }
65
+ },
66
+ error(error) {
67
+ emit('remote.h264.decodeFailed', { error: error?.message || String(error || ''), frameSeq: state.lastSeq });
68
+ closeDecoder(state);
69
+ }
70
+ });
71
+ try {
72
+ decoder.configure({
73
+ codec: String(frame.codec || 'avc1.42E01E'),
74
+ codedWidth: width,
75
+ codedHeight: height,
76
+ hardwareAcceleration: 'prefer-software',
77
+ optimizeForLatency: true,
78
+ avc: { format: String(frame.h264Format || 'annexb').toLowerCase() === 'avc' ? 'avc' : 'annexb' }
79
+ });
80
+ } catch (error) {
81
+ emit('remote.h264.configureFailed', { key, error: error?.message || String(error || '') });
82
+ try { decoder.close(); } catch { /* ignore */ }
83
+ return null;
84
+ }
85
+ state.decoder = decoder;
86
+ return decoder;
87
+ }
88
+
89
+ async function decode(canvas, frame) {
90
+ const payload = frame?._remoteFleetPayloadBlob;
91
+ if (!canvas || !payload || !String(frame?.codec || '').toLowerCase().includes('h264')) {
92
+ return false;
93
+ }
94
+ const state = getDecoderState(canvas);
95
+ const seq = Number(frame.streamFrameSeq || frame.frameSeq || 0) || 0;
96
+ if (seq > 0 && seq <= state.lastSeq) return true;
97
+ state.lastSeq = seq;
98
+ const decoder = ensureDecoder(canvas, frame, state);
99
+ if (!decoder || decoder.state === 'closed') return false;
100
+ if (state.pending >= MAX_DECODE_QUEUE) {
101
+ emit('remote.h264.drop', { reason: 'decode-queue', frameSeq: seq });
102
+ return true;
103
+ }
104
+ state.pending += 1;
105
+ try {
106
+ const bytes = new Uint8Array(await payload.arrayBuffer());
107
+ if (!bytes.length) return false;
108
+ const timestamp = Number(frame.timestampUs || 0) || Math.round(performance.now() * 1000);
109
+ decoder.decode(new EncodedVideoChunk({
110
+ type: frame.isKeyFrame === true || String(frame.chunkType || '').toLowerCase() === 'key' ? 'key' : 'delta',
111
+ timestamp,
112
+ duration: Number(frame.durationUs || 0) || undefined,
113
+ data: bytes
114
+ }));
115
+ return true;
116
+ } catch (error) {
117
+ emit('remote.h264.decodeSubmitFailed', { error: error?.message || String(error || ''), frameSeq: seq });
118
+ closeDecoder(state);
119
+ return false;
120
+ } finally {
121
+ state.pending = Math.max(0, state.pending - 1);
122
+ }
123
+ }
124
+
125
+ window.MindExecRemoteCodecs = Object.freeze({ decodeH264Frame: decode });
126
+ })();
@@ -0,0 +1,95 @@
1
+ // Selected video-node playback chrome. Runtime playback ownership stays in the CSS3D manager.
2
+ (function () {
3
+ 'use strict';
4
+
5
+ function sync(videoEl, intent) {
6
+ const control = videoEl?.closest?.('.video-content')?.querySelector?.('.mind-map-video-playback-control');
7
+ if (!control) return false;
8
+
9
+ const wantsPlayback = String(intent || '').toLowerCase() === 'play' &&
10
+ videoEl?.dataset?.userPaused !== 'true';
11
+ control.dataset.playbackIntent = wantsPlayback ? 'play' : 'pause';
12
+ control.setAttribute('aria-label', wantsPlayback ? 'Pause video' : 'Play video');
13
+ control.setAttribute('aria-pressed', wantsPlayback ? 'true' : 'false');
14
+ const icon = control.querySelector?.('.mind-map-video-playback-control__icon');
15
+ const status = control.querySelector?.('.mind-map-video-playback-control__status');
16
+ if (icon) icon.textContent = wantsPlayback ? '❚❚' : '▶';
17
+ if (status) status.textContent = wantsPlayback ? 'Playing' : 'Paused';
18
+ return true;
19
+ }
20
+
21
+ function mount(host, videoEl, options = {}) {
22
+ if (!host || !videoEl) return null;
23
+ const existing = host.querySelector?.('.mind-map-video-playback-control');
24
+ if (existing) return existing;
25
+
26
+ const control = document.createElement('button');
27
+ control.type = 'button';
28
+ control.className = 'mind-map-video-playback-control';
29
+ const icon = document.createElement('span');
30
+ icon.className = 'mind-map-video-playback-control__icon';
31
+ icon.setAttribute('aria-hidden', 'true');
32
+ const status = document.createElement('span');
33
+ status.className = 'mind-map-video-playback-control__status';
34
+ control.appendChild(icon);
35
+ control.appendChild(status);
36
+
37
+ const stopPointerOwnership = event => event.stopPropagation();
38
+ control.addEventListener('pointerdown', stopPointerOwnership);
39
+ control.addEventListener('mousedown', stopPointerOwnership);
40
+ control.addEventListener('dblclick', stopPointerOwnership);
41
+ control.addEventListener('click', event => {
42
+ event.preventDefault();
43
+ event.stopPropagation();
44
+ const shouldPlay = String(options.getIntent?.() || '').toLowerCase() !== 'play';
45
+ options.onToggle?.(shouldPlay);
46
+ });
47
+
48
+ host.appendChild(control);
49
+ sync(videoEl, options.getIntent?.());
50
+ return control;
51
+ }
52
+
53
+ function bindState(videoEl, nodeModel, operations = {}) {
54
+ if (!videoEl || !nodeModel) return false;
55
+ const nodeId = operations.getNodeId?.(nodeModel) || '';
56
+ if (videoEl.dataset?.videoPlaybackStateBoundNodeId === nodeId) return false;
57
+ if (videoEl.dataset) videoEl.dataset.videoPlaybackStateBoundNodeId = nodeId;
58
+
59
+ const rememberTimeOnly = () => {
60
+ if (videoEl.dataset?.lodMediaSuspended === 'true' ||
61
+ videoEl.dataset?.videoPositionRestorePending === 'true') return;
62
+ const currentTime = Number(videoEl.currentTime || 0);
63
+ if (!Number.isFinite(currentTime) || currentTime < 0) return;
64
+ nodeModel._videoPlaybackCurrentTime = currentTime;
65
+ if (videoEl.dataset) videoEl.dataset.videoLastKnownTime = String(currentTime);
66
+ };
67
+ const markPlaying = () => {
68
+ operations.rememberRuntime?.(nodeModel, videoEl, { forceIntent: true });
69
+ operations.setIntent?.(nodeModel, videoEl, 'play');
70
+ };
71
+ const markPaused = () => {
72
+ rememberTimeOnly();
73
+ if (videoEl.dataset?.visibilityPaused === 'true' ||
74
+ videoEl.dataset?.videoResumePending === 'true' ||
75
+ videoEl.dataset?.lodMediaSuspended === 'true') {
76
+ sync(videoEl, operations.getIntent?.(nodeModel, videoEl));
77
+ return;
78
+ }
79
+ operations.setIntent?.(nodeModel, videoEl, 'pause');
80
+ };
81
+
82
+ videoEl.addEventListener('play', markPlaying, { passive: true });
83
+ videoEl.addEventListener('playing', markPlaying, { passive: true });
84
+ videoEl.addEventListener('pause', markPaused, { passive: true });
85
+ videoEl.addEventListener('timeupdate', rememberTimeOnly, { passive: true });
86
+ videoEl.addEventListener('seeking', rememberTimeOnly, { passive: true });
87
+ videoEl.addEventListener('loadedmetadata', () => {
88
+ operations.restorePosition?.(nodeModel, videoEl);
89
+ if (videoEl.dataset) videoEl.dataset.videoPositionRestorePending = 'false';
90
+ }, { passive: true });
91
+ return true;
92
+ }
93
+
94
+ window.MindMapVideoPlaybackControls = { bindState, mount, sync };
95
+ })();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "mainAssemblyName": "MindExecution.Web",
3
3
  "resources": {
4
- "hash": "sha256-cyYstxxu9/Yn3zgm26aHsm55DzzVHsZnB734jEONmCw=",
4
+ "hash": "sha256-WNzAmWuVhn9JyBiHwwIttruk/GbsVNnakpghIldcLg8=",
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.808jj176e1.dll": "MindExecution.Core.dll",
126
- "MindExecution.Kernel.75hxk08tt0.dll": "MindExecution.Kernel.dll",
127
- "MindExecution.Plugins.Admin.0zkmk8wahq.dll": "MindExecution.Plugins.Admin.dll",
128
- "MindExecution.Plugins.Business.vyw3769hrw.dll": "MindExecution.Plugins.Business.dll",
129
- "MindExecution.Plugins.Concept.cvmglrxsjb.dll": "MindExecution.Plugins.Concept.dll",
130
- "MindExecution.Plugins.Directory.jtw9q1dm9n.dll": "MindExecution.Plugins.Directory.dll",
131
- "MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll": "MindExecution.Plugins.PlanMaster.dll",
132
- "MindExecution.Plugins.YouTube.xb9sou9ox0.dll": "MindExecution.Plugins.YouTube.dll",
133
- "MindExecution.Shared.pqab76cjb6.dll": "MindExecution.Shared.dll",
134
- "MindExecution.Web.mc24u4bzol.dll": "MindExecution.Web.dll",
125
+ "MindExecution.Core.nxjlewduna.dll": "MindExecution.Core.dll",
126
+ "MindExecution.Kernel.vrfi63yg9f.dll": "MindExecution.Kernel.dll",
127
+ "MindExecution.Plugins.Admin.1rcwhdxl0r.dll": "MindExecution.Plugins.Admin.dll",
128
+ "MindExecution.Plugins.Business.rx0eieg51y.dll": "MindExecution.Plugins.Business.dll",
129
+ "MindExecution.Plugins.Concept.zzczjzhvk1.dll": "MindExecution.Plugins.Concept.dll",
130
+ "MindExecution.Plugins.Directory.bsorcntiwp.dll": "MindExecution.Plugins.Directory.dll",
131
+ "MindExecution.Plugins.PlanMaster.i5j9t4f1jf.dll": "MindExecution.Plugins.PlanMaster.dll",
132
+ "MindExecution.Plugins.YouTube.1mpn9satft.dll": "MindExecution.Plugins.YouTube.dll",
133
+ "MindExecution.Shared.mpi1gzau9b.dll": "MindExecution.Shared.dll",
134
+ "MindExecution.Web.nxykcyxd02.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.808jj176e1.dll": "sha256-uMmP6rEgsbuwnsZ6Cnxrpmzio4usBtoJERfkOvz25iY=",
280
- "MindExecution.Kernel.75hxk08tt0.dll": "sha256-9db04cSFp4z4iblswVb0UWgpjeluVqZUa7g27Oj3p40=",
281
- "MindExecution.Plugins.Business.vyw3769hrw.dll": "sha256-WSc1w7WNOhnmzD3DcIgg4PCYzHeeU5rc7KK7eLgsu+M=",
282
- "MindExecution.Plugins.Concept.cvmglrxsjb.dll": "sha256-yGhEvwMzCTsmRB6D80lOSfOUWCegs7b/GRvXEdIss5s=",
283
- "MindExecution.Plugins.PlanMaster.8hmc6ps1ux.dll": "sha256-NDlttv/8WOvjy9hud+07v5ul3ZJhf2n/cf2CgMfhBnA=",
284
- "MindExecution.Shared.pqab76cjb6.dll": "sha256-3zUucQiJHN3pl4KBqsG0D0j054ojYss5D69/xCNTngY=",
285
- "MindExecution.Web.mc24u4bzol.dll": "sha256-5sJydtlQZKVkPUzMIYPO5q2iWWpstxPNVn6Kyk3fBrk="
279
+ "MindExecution.Core.nxjlewduna.dll": "sha256-K/aYKYR7u21eWdPhsuyT3DfAcldItQ9fUJDpyAW1Yk8=",
280
+ "MindExecution.Kernel.vrfi63yg9f.dll": "sha256-pQ3AG4I0yHOE+LjWf41pPTzD6WAPx5QEVQR99tgmjv8=",
281
+ "MindExecution.Plugins.Business.rx0eieg51y.dll": "sha256-wXky9gfkYuS9LpiAJcaHL2ySg388QTlafZ7dDYaYGkc=",
282
+ "MindExecution.Plugins.Concept.zzczjzhvk1.dll": "sha256-caSJdsMUA/y2XoLlC69Ml7LVPyEklu9iUqK8LbLH/Ac=",
283
+ "MindExecution.Plugins.PlanMaster.i5j9t4f1jf.dll": "sha256-NsaH6mfDGUtq96FRS/yR+b5QyLzjVWublyCQBKugpss=",
284
+ "MindExecution.Shared.mpi1gzau9b.dll": "sha256-Bk/J98QCHrurXfAfiETiavqv5g5jXQ73yvEdYibuIrU=",
285
+ "MindExecution.Web.nxykcyxd02.dll": "sha256-JW4hqFR8NT2yFKIvMflIhM8Wks9Dec4w+0ofv69tU6w="
286
286
  },
287
287
  "lazyAssembly": {
288
- "MindExecution.Plugins.Admin.0zkmk8wahq.dll": "sha256-VLiwzLIr+tjKr6+yQfhMVQHq2Yx26QenAkC092+fEdw=",
289
- "MindExecution.Plugins.Directory.jtw9q1dm9n.dll": "sha256-Z1x+H7sTj3w9XjWxfqGsd3Jv+qlzwQxAur6pJIP4l20=",
290
- "MindExecution.Plugins.YouTube.xb9sou9ox0.dll": "sha256-Pf4IWNwd62loUPGvRnJ8ZgJ1X/h87tNDeBwS8ajlwyk="
288
+ "MindExecution.Plugins.Admin.1rcwhdxl0r.dll": "sha256-jV0N8WevIxIidWNinjH1vZ+NS+eKshBtlZpvb6vCfmg=",
289
+ "MindExecution.Plugins.Directory.bsorcntiwp.dll": "sha256-O07lrIPzxWQRivTDifZBsSoG+ZMptwdu2Ey2e9sF0M0=",
290
+ "MindExecution.Plugins.YouTube.1mpn9satft.dll": "sha256-l6UnB6LB9UHscjF/q5j8F2fKCunQ63w6mkwEEuWK3tg="
291
291
  }
292
292
  },
293
293
  "cacheBootResources": true,