@mindexec/cli 0.2.306 → 0.2.308

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.306",
3
+ "version": "0.2.308",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -3407,13 +3407,13 @@ html body .css3d-resolution-wrapper.node-type-embed .map-node.selected {
3407
3407
  outline-offset: 0 !important;
3408
3408
  }
3409
3409
 
3410
- /* Text nodes need a visible hard edge now that decorative glow/shadow is gone.
3410
+ /* Text nodes keep a visible but alpha-soft edge now that decorative glow/shadow is gone.
3411
3411
  Keep this scoped away from template, MDM, media, CSV, and remote-control cards. */
3412
3412
  html body .css3d-resolution-wrapper > .map-node-bubble,
3413
3413
  html body .css3d-resolution-wrapper > .map-node-note,
3414
3414
  html body .css3d-resolution-wrapper > .map-node-code,
3415
3415
  html body .css3d-resolution-wrapper > .map-node-memo:not(.map-node-agent):not(.map-node-automation):not(.map-node-template-card):not(.map-node-remote-fleet) {
3416
- border: 1px solid rgba(17, 24, 39, 0.88) !important;
3416
+ border: 1px solid rgba(17, 24, 39, 0.22) !important;
3417
3417
  -webkit-box-shadow: none !important;
3418
3418
  box-shadow: none !important;
3419
3419
  -webkit-filter: none !important;
@@ -3454,7 +3454,7 @@ html body .css3d-resolution-wrapper > .map-node-automation:focus-within:not(.map
3454
3454
  }
3455
3455
 
3456
3456
  /* Final node edge policy:
3457
- - unselected text-like surfaces use a hard dark edge because glow is removed
3457
+ - unselected text-like surfaces use a soft dark edge because glow is removed
3458
3458
  - selected nodes, including AI task nodes, use the same blue edge as notes */
3459
3459
  html body .css3d-resolution-wrapper.node-type-text > .map-node,
3460
3460
  html body .css3d-resolution-wrapper.node-type-markdown > .map-node,
@@ -3464,7 +3464,7 @@ html body .css3d-resolution-wrapper > .map-node-agent:not(.map-node-template-car
3464
3464
  html body .css3d-resolution-wrapper > .map-node-automation:not(.map-node-template-card):not(.map-node-remote-fleet),
3465
3465
  html body .mind-map-text-overlay-v2-shell.type-text,
3466
3466
  html body .mind-map-text-overlay-v2-shell.type-markdown {
3467
- border: 1px solid rgba(17, 24, 39, 0.88) !important;
3467
+ border: 1px solid rgba(17, 24, 39, 0.22) !important;
3468
3468
  -webkit-box-shadow: none !important;
3469
3469
  box-shadow: none !important;
3470
3470
  -webkit-filter: none !important;
@@ -3534,6 +3534,10 @@ html body .css3d-resolution-wrapper.lod-low.node-type-memo .map-node-memo__agent
3534
3534
  html body .css3d-resolution-wrapper.lod-low.node-type-memo .map-node-memo__agent-results,
3535
3535
  html body .css3d-resolution-wrapper.lod-low.node-type-memo .map-node-memo__automation-pins,
3536
3536
  html body .css3d-resolution-wrapper.lod-low.node-type-memo .map-node-memo__automation-pin,
3537
+ html body .css3d-resolution-wrapper.lod-low .map-node-memo__automation-pins,
3538
+ html body .css3d-resolution-wrapper.lod-low .map-node-memo__automation-pin,
3539
+ html body .css3d-resolution-wrapper.lod-low .map-node-memo__automation-pin-dot,
3540
+ html body .css3d-resolution-wrapper.lod-low .map-node-memo__automation-pin-label,
3537
3541
  html body .css3d-resolution-wrapper.lod-low > .map-node-agent .map-node-memo__header,
3538
3542
  html body .css3d-resolution-wrapper.lod-low > .map-node-automation .map-node-memo__header,
3539
3543
  html body .css3d-resolution-wrapper.lod-low > .map-node-agent .map-node-memo__automation-pins,
@@ -3552,7 +3556,31 @@ html body .css3d-resolution-wrapper.lod-low.node-type-memo .map-node-memo__shell
3552
3556
  html body .css3d-resolution-wrapper.lod-low > .map-node-agent .map-node-memo__shell,
3553
3557
  html body .css3d-resolution-wrapper.lod-low > .map-node-automation .map-node-memo__shell {
3554
3558
  background: transparent !important;
3559
+ color: transparent !important;
3560
+ font-size: 0 !important;
3561
+ line-height: 0 !important;
3562
+ text-shadow: none !important;
3555
3563
  padding: 0 !important;
3556
3564
  border: 0 !important;
3557
3565
  overflow: hidden !important;
3558
3566
  }
3567
+
3568
+ html body .css3d-resolution-wrapper.lod-low.node-type-video > .map-node {
3569
+ background: rgba(255, 255, 255, 0.9) !important;
3570
+ border: 0 !important;
3571
+ -webkit-box-shadow: none !important;
3572
+ box-shadow: none !important;
3573
+ outline: 0 !important;
3574
+ overflow: hidden !important;
3575
+ }
3576
+
3577
+ html body .css3d-resolution-wrapper.lod-low.node-type-video .node-response,
3578
+ html body .css3d-resolution-wrapper.lod-low.node-type-video .ai-loading-row,
3579
+ html body .css3d-resolution-wrapper.lod-low.node-type-video video,
3580
+ html body .css3d-resolution-wrapper.lod-low.node-type-video canvas.mind-map-video-proxy,
3581
+ html body .css3d-resolution-wrapper.lod-low.node-type-video .video-content > video,
3582
+ html body .css3d-resolution-wrapper.lod-low.node-type-video .video-content > canvas.mind-map-video-proxy {
3583
+ display: none !important;
3584
+ opacity: 0 !important;
3585
+ pointer-events: none !important;
3586
+ }
@@ -5,7 +5,7 @@
5
5
  const DEBUG = false;
6
6
  const FPS_DEBUG = false;
7
7
  const FRAME_PERF_DEBUG = false;
8
- const MINDMAP_CORE_BUILD_ID = '20260621-low-detail-proxy-css3d-v776';
8
+ const MINDMAP_CORE_BUILD_ID = '20260621-video-prefetch-v785';
9
9
  const CanvasPhase = Object.freeze({
10
10
  Booting: 'booting',
11
11
  BoardFileLoading: 'board-file-loading',
@@ -873,28 +873,37 @@
873
873
  function isCss3dEntryUnsafeForSettledFlatScene(entry) {
874
874
  const model = entry?.model || {};
875
875
  const type = String(model.contentType ?? model.ContentType ?? '').trim().toLowerCase();
876
- if (!CSS3D_SETTLED_TEXT_FLAT_SAFE_TYPES.has(type)) {
876
+ const element = entry?.cssObject?.element || null;
877
+ const classList = element?.classList || null;
878
+ const isLowDetailVideoProxy =
879
+ type === 'video' &&
880
+ classList?.contains?.('node-type-video') === true &&
881
+ classList?.contains?.('lod-low') === true;
882
+
883
+ if (!CSS3D_SETTLED_TEXT_FLAT_SAFE_TYPES.has(type) && isLowDetailVideoProxy !== true) {
877
884
  return true;
878
885
  }
879
886
 
880
- if (type === 'video' ||
887
+ if ((type === 'video' && isLowDetailVideoProxy !== true) ||
881
888
  type === 'csv-table' ||
882
889
  type === 'templatelauncher') {
883
890
  return true;
884
891
  }
885
892
 
886
- const element = entry?.cssObject?.element || null;
887
893
  if (!element) {
888
894
  return true;
889
895
  }
890
896
 
891
- const classList = element.classList;
892
- if (classList?.contains?.('node-type-video') === true ||
897
+ if ((classList?.contains?.('node-type-video') === true && isLowDetailVideoProxy !== true) ||
893
898
  classList?.contains?.('node-type-csv-table') === true ||
894
899
  classList?.contains?.('node-type-templatelauncher') === true) {
895
900
  return true;
896
901
  }
897
902
 
903
+ if (isLowDetailVideoProxy === true) {
904
+ return false;
905
+ }
906
+
898
907
  return !!element.querySelector?.(
899
908
  'iframe, video, canvas, .map-node-template-card, .map-node-remote-fleet, .csv-table-scroll, [data-remote-fleet-resize-handle="true"]'
900
909
  );
@@ -9283,7 +9292,7 @@ ${summaryLines.map(line => `<div>${escapeNodeFrameDebugHtml(line)}</div>`).join(
9283
9292
  const shouldUseAnimationCadenceForWebglFrame =
9284
9293
  isCameraNavigationOnlyInteractiveFrame === true
9285
9294
  ? false
9286
- : (shouldUseAnimation60Fps === true || shouldUseAnimationLowFpsFrame === true);
9295
+ : shouldUseAnimation60Fps === true;
9287
9296
  const shouldHoldWebglCanvasTransformDuringCameraNavigation = !!(
9288
9297
  isWebglRenderEnabled &&
9289
9298
  isCameraNavigationOnlyInteractiveFrame === true &&
@@ -14,6 +14,7 @@
14
14
  const CSS_IMAGE_PROMOTION_MAX_PER_FRAME = 4;
15
15
  const CSS_IMAGE_PROMOTION_BUDGET_MS = 4;
16
16
  const CSS_IMAGE_BLOB_CACHE_LIMIT = 160;
17
+ const CSS_VIDEO_BLOB_CACHE_LIMIT = 48;
17
18
  const CSS_IMAGE_DISPLAY_MAX_CONCURRENCY = 8;
18
19
  const CSS3D_TEXT_NODE_RESOLUTION_SCALE = 1.0;
19
20
  const CSS3D_MEDIA_NODE_RESOLUTION_SCALE = 1.0;
@@ -26,6 +27,8 @@
26
27
  let _cssImagePromotionTimer = 0;
27
28
  let _cssImageBlobUrlCache = new Map();
28
29
  let _cssImageBlobUrlPending = new Map();
30
+ let _cssVideoBlobUrlCache = new Map();
31
+ let _cssVideoBlobUrlPending = new Map();
29
32
  let _cssImageDisplayQueue = [];
30
33
  let _cssImageDisplayActiveCount = 0;
31
34
  let _cssImageDisplayPumpScheduled = false;
@@ -1314,6 +1317,13 @@
1314
1317
  return !normalized || normalized.startsWith('image/');
1315
1318
  }
1316
1319
 
1320
+ function isCssVideoContentType(value) {
1321
+ const normalized = String(value || '').split(';')[0].trim().toLowerCase();
1322
+ return !normalized
1323
+ || normalized.startsWith('video/')
1324
+ || normalized === 'application/octet-stream';
1325
+ }
1326
+
1317
1327
  function isDirectCssMediaUrl(url) {
1318
1328
  const normalized = normalizeCssMediaUrl(url).toLowerCase();
1319
1329
  return normalized.startsWith('http://')
@@ -1677,6 +1687,129 @@
1677
1687
  );
1678
1688
  }
1679
1689
 
1690
+ function getCss3dVideoElement(cssObject) {
1691
+ const element = cssObject?.element || null;
1692
+ if (element?.tagName === 'VIDEO') {
1693
+ return element;
1694
+ }
1695
+ return element?.querySelector?.('video') || null;
1696
+ }
1697
+
1698
+ function isCss3dVideoElementLowDetail(videoEl, cssObject = null) {
1699
+ const element = cssObject?.element || null;
1700
+ const root = element?.classList?.contains?.('css3d-resolution-wrapper') === true
1701
+ ? element
1702
+ : videoEl?.closest?.('.css3d-resolution-wrapper') || element?.closest?.('.css3d-resolution-wrapper') || null;
1703
+ const isVideoWrapper = root?.classList?.contains?.('node-type-video') === true ||
1704
+ videoEl?.tagName === 'VIDEO';
1705
+ return isVideoWrapper === true &&
1706
+ root?.classList?.contains?.('lod-high') !== true;
1707
+ }
1708
+
1709
+ function getCss3dVideoDeferredSource(videoEl, nodeModel) {
1710
+ return normalizeCssMediaUrl(
1711
+ videoEl?.dataset?.lodSuspendedSrc
1712
+ || videoEl?.dataset?.resolvedMediaSourceUrl
1713
+ || videoEl?.dataset?.mediaSourceUrl
1714
+ || videoEl?.currentSrc
1715
+ || videoEl?.getAttribute?.('src')
1716
+ || nodeModel?.response
1717
+ || nodeModel?.Response
1718
+ || ''
1719
+ );
1720
+ }
1721
+
1722
+ function rememberCss3dVideoSourceForLod(videoEl, nodeModel) {
1723
+ if (!videoEl?.dataset) {
1724
+ return '';
1725
+ }
1726
+
1727
+ const source = getCss3dVideoDeferredSource(videoEl, nodeModel);
1728
+ if (source) {
1729
+ videoEl.dataset.lodSuspendedSrc = source;
1730
+ }
1731
+ return source;
1732
+ }
1733
+
1734
+ function suspendCss3dVideoSourceForLod(nodeModel, cssObject) {
1735
+ const contentTypeLower = String(nodeModel?.contentType ?? nodeModel?.ContentType ?? '').toLowerCase();
1736
+ if (contentTypeLower !== 'video') {
1737
+ return false;
1738
+ }
1739
+
1740
+ const videoEl = getCss3dVideoElement(cssObject);
1741
+ if (!videoEl) {
1742
+ return false;
1743
+ }
1744
+
1745
+ rememberVideoNodeRuntimePlayback(nodeModel, videoEl);
1746
+ rememberCss3dVideoSourceForLod(videoEl, nodeModel);
1747
+ if (videoEl.dataset) {
1748
+ if (!videoEl.dataset.lodSuspendedPreload) {
1749
+ videoEl.dataset.lodSuspendedPreload = videoEl.getAttribute('preload') || videoEl.preload || 'metadata';
1750
+ }
1751
+ videoEl.dataset.lodMediaSuspended = 'true';
1752
+ videoEl.dataset.videoResumePending = 'false';
1753
+ videoEl.dataset.visibilityPaused = 'false';
1754
+ }
1755
+
1756
+ if (!videoEl.paused) {
1757
+ videoEl.pause?.();
1758
+ }
1759
+ stopCss3dVideoCanvasProxyForVisibility(videoEl);
1760
+
1761
+ const hadSource = !!videoEl.getAttribute('src');
1762
+ videoEl.removeAttribute('src');
1763
+ videoEl.preload = 'none';
1764
+ if (hadSource && typeof videoEl.load === 'function') {
1765
+ try {
1766
+ videoEl.load();
1767
+ } catch { }
1768
+ }
1769
+
1770
+ return true;
1771
+ }
1772
+
1773
+ function resumeCss3dVideoSourceForLod(nodeModel, cssObject, options = {}) {
1774
+ const contentTypeLower = String(nodeModel?.contentType ?? nodeModel?.ContentType ?? '').toLowerCase();
1775
+ if (contentTypeLower !== 'video') {
1776
+ return false;
1777
+ }
1778
+
1779
+ const videoEl = getCss3dVideoElement(cssObject);
1780
+ if (!videoEl) {
1781
+ return false;
1782
+ }
1783
+
1784
+ const preferredSource = getCss3dVideoDeferredSource(videoEl, nodeModel);
1785
+ if (videoEl.dataset) {
1786
+ videoEl.dataset.lodMediaSuspended = 'false';
1787
+ }
1788
+ videoEl.preload = videoEl.dataset?.lodSuspendedPreload || 'metadata';
1789
+
1790
+ if (!preferredSource) {
1791
+ return false;
1792
+ }
1793
+
1794
+ if (isLocalCssMediaReference(preferredSource) || getLocalCssMediaId(nodeModel, preferredSource)) {
1795
+ videoEl.dataset.mediaSourceUrl = getLocalCssMediaReference(nodeModel, preferredSource);
1796
+ videoEl.dataset.resolvedMediaSourceUrl = '';
1797
+ if (options.resolveLocal !== false) {
1798
+ void resolveLocalCssMediaSource(videoEl, nodeModel, contentTypeLower);
1799
+ }
1800
+ return true;
1801
+ }
1802
+
1803
+ void setCssVideoElementSource(videoEl, nodeModel, preferredSource, { load: true });
1804
+ return true;
1805
+ }
1806
+
1807
+ function syncCss3dVideoLodMediaSource(nodeModel, cssObject, isHigh) {
1808
+ return isHigh === true
1809
+ ? resumeCss3dVideoSourceForLod(nodeModel, cssObject)
1810
+ : suspendCss3dVideoSourceForLod(nodeModel, cssObject);
1811
+ }
1812
+
1680
1813
  function isLocalCssMediaReference(url) {
1681
1814
  const text = String(url || '').trim();
1682
1815
  if (!text) {
@@ -1770,6 +1903,14 @@
1770
1903
  delete metadata.localFileResolveError;
1771
1904
 
1772
1905
  mediaEl.dataset.resolvedMediaSourceUrl = objectUrl;
1906
+ if (isCss3dVideoElementLowDetail(mediaEl, { element: mediaEl })) {
1907
+ mediaEl.dataset.lodSuspendedSrc = objectUrl;
1908
+ suspendCss3dVideoSourceForLod(nodeModel, { element: mediaEl });
1909
+ setAssetRefreshState(nodeModel, '', '');
1910
+ syncMediaLoadingRowForElement(mediaEl, nodeModel, contentTypeLower);
1911
+ return true;
1912
+ }
1913
+
1773
1914
  if (mediaEl.getAttribute('src') !== objectUrl) {
1774
1915
  delete mediaEl.dataset.videoCanvasProxyFailed;
1775
1916
  mediaEl.src = objectUrl;
@@ -2761,11 +2902,17 @@
2761
2902
  return false;
2762
2903
  }
2763
2904
 
2764
- const videoEl = cssObject?.element?.querySelector?.('video') || null;
2905
+ const videoEl = getCss3dVideoElement(cssObject);
2765
2906
  const state = getCss3dVideoProxyState(videoEl);
2766
2907
  if (!videoEl || !state) {
2767
2908
  return false;
2768
2909
  }
2910
+ if (videoEl.dataset?.lodMediaSuspended === 'true') {
2911
+ state.active = false;
2912
+ applyCss3dVideoProxySurfaceMode(state, false);
2913
+ cancelCss3dVideoCanvasProxyLoop(state);
2914
+ return false;
2915
+ }
2769
2916
 
2770
2917
  bindCss3dVideoCanvasProxyLifecycle(videoEl, nodeModel, cssObject);
2771
2918
 
@@ -3068,7 +3215,7 @@
3068
3215
  return false;
3069
3216
  }
3070
3217
 
3071
- const videoEl = cssObject?.element?.querySelector?.('video') || null;
3218
+ const videoEl = getCss3dVideoElement(cssObject);
3072
3219
  if (!videoEl) {
3073
3220
  return false;
3074
3221
  }
@@ -3093,6 +3240,13 @@
3093
3240
  return false;
3094
3241
  }
3095
3242
 
3243
+ if (isCss3dVideoElementLowDetail(videoEl, cssObject)) {
3244
+ suspendCss3dVideoSourceForLod(nodeModel, cssObject);
3245
+ return false;
3246
+ }
3247
+
3248
+ resumeCss3dVideoSourceForLod(nodeModel, cssObject);
3249
+
3096
3250
  if (intent !== 'play' || videoEl.dataset?.userPaused === 'true') {
3097
3251
  if (videoEl.dataset) {
3098
3252
  videoEl.dataset.visibilityPaused = 'false';
@@ -3353,6 +3507,30 @@
3353
3507
  }
3354
3508
  }
3355
3509
 
3510
+ function touchCssVideoBlobCacheEntry(sourceUrl, entry) {
3511
+ if (!sourceUrl || !entry) return;
3512
+ entry.lastAccessAt = getCssImagePromotionNow();
3513
+ if (_cssVideoBlobUrlCache.get(sourceUrl) === entry) {
3514
+ _cssVideoBlobUrlCache.delete(sourceUrl);
3515
+ }
3516
+ _cssVideoBlobUrlCache.set(sourceUrl, entry);
3517
+ }
3518
+
3519
+ function evictCssVideoBlobCache() {
3520
+ while (_cssVideoBlobUrlCache.size > CSS_VIDEO_BLOB_CACHE_LIMIT) {
3521
+ const oldestKey = _cssVideoBlobUrlCache.keys().next().value;
3522
+ if (!oldestKey) {
3523
+ break;
3524
+ }
3525
+
3526
+ const entry = _cssVideoBlobUrlCache.get(oldestKey);
3527
+ _cssVideoBlobUrlCache.delete(oldestKey);
3528
+ if (entry?.objectUrl) {
3529
+ try { URL.revokeObjectURL(entry.objectUrl); } catch { }
3530
+ }
3531
+ }
3532
+ }
3533
+
3356
3534
  function getCssMediaFetchOptions(targetUrl) {
3357
3535
  const normalized = String(targetUrl || '').trim().toLowerCase();
3358
3536
  if (!normalized || normalized.startsWith('blob:') || normalized.startsWith('data:') || normalized.startsWith('file:')) {
@@ -3434,6 +3612,63 @@
3434
3612
  }
3435
3613
  }
3436
3614
 
3615
+ async function resolveCssVideoDisplaySource(targetUrl) {
3616
+ const normalized = normalizeCssMediaUrl(targetUrl);
3617
+ if (!normalized) {
3618
+ return '';
3619
+ }
3620
+
3621
+ if (!isLoopbackCssAssetUrl(normalized)) {
3622
+ return normalized;
3623
+ }
3624
+
3625
+ const cached = _cssVideoBlobUrlCache.get(normalized);
3626
+ if (cached?.objectUrl) {
3627
+ touchCssVideoBlobCacheEntry(normalized, cached);
3628
+ return cached.objectUrl;
3629
+ }
3630
+
3631
+ const existingPending = _cssVideoBlobUrlPending.get(normalized);
3632
+ if (existingPending) {
3633
+ return await existingPending;
3634
+ }
3635
+
3636
+ const pendingFetch = (async () => {
3637
+ const response = await fetch(normalized, getCssMediaFetchOptions(normalized));
3638
+ if (!response.ok) {
3639
+ throw new Error(`HTTP ${response.status}`);
3640
+ }
3641
+
3642
+ const contentType = response.headers?.get?.('content-type') || '';
3643
+ if (!isCssVideoContentType(contentType)) {
3644
+ throw new Error(`Unexpected video content-type: ${contentType || 'unknown'}`);
3645
+ }
3646
+
3647
+ const blob = await response.blob();
3648
+ if (!isCssVideoContentType(blob?.type || contentType)) {
3649
+ throw new Error(`Unexpected video blob type: ${blob?.type || contentType || 'unknown'}`);
3650
+ }
3651
+
3652
+ const objectUrl = URL.createObjectURL(blob);
3653
+ const entry = {
3654
+ objectUrl,
3655
+ lastAccessAt: getCssImagePromotionNow()
3656
+ };
3657
+ _cssVideoBlobUrlCache.set(normalized, entry);
3658
+ evictCssVideoBlobCache();
3659
+ return objectUrl;
3660
+ })();
3661
+
3662
+ _cssVideoBlobUrlPending.set(normalized, pendingFetch);
3663
+ try {
3664
+ return await pendingFetch;
3665
+ } finally {
3666
+ if (_cssVideoBlobUrlPending.get(normalized) === pendingFetch) {
3667
+ _cssVideoBlobUrlPending.delete(normalized);
3668
+ }
3669
+ }
3670
+ }
3671
+
3437
3672
  function scheduleCssImageDisplayQueuePump() {
3438
3673
  if (_cssImageDisplayPumpScheduled) {
3439
3674
  return;
@@ -3545,6 +3780,60 @@
3545
3780
  });
3546
3781
  }
3547
3782
 
3783
+ async function setCssVideoElementSource(mediaEl, nodeModel, targetUrl, options = {}) {
3784
+ if (!mediaEl) return false;
3785
+
3786
+ const normalized = normalizeCssMediaUrl(targetUrl);
3787
+ if (!normalized) {
3788
+ mediaEl.dataset.mediaSourceUrl = '';
3789
+ mediaEl.dataset.resolvedMediaSourceUrl = '';
3790
+ mediaEl.removeAttribute('src');
3791
+ if (options.load !== false && typeof mediaEl.load === 'function') {
3792
+ try { mediaEl.load(); } catch { }
3793
+ }
3794
+ return false;
3795
+ }
3796
+
3797
+ const previousCanonical = String(mediaEl.dataset.mediaSourceUrl || '').trim();
3798
+ mediaEl.dataset.mediaSourceUrl = normalized;
3799
+ if (previousCanonical !== normalized) {
3800
+ mediaEl.dataset.assetRefreshAttempted = '';
3801
+ }
3802
+
3803
+ try {
3804
+ const resolvedDisplaySource = await resolveCssVideoDisplaySource(normalized);
3805
+ if (String(mediaEl.dataset.mediaSourceUrl || '').trim() !== normalized) {
3806
+ return false;
3807
+ }
3808
+
3809
+ const currentDisplaySource = String(mediaEl.getAttribute('src') || '').trim();
3810
+ if (currentDisplaySource !== resolvedDisplaySource) {
3811
+ delete mediaEl.dataset.videoCanvasProxyFailed;
3812
+ mediaEl.setAttribute('src', resolvedDisplaySource);
3813
+ if (options.load !== false && typeof mediaEl.load === 'function') {
3814
+ try { mediaEl.load(); } catch { }
3815
+ }
3816
+ }
3817
+
3818
+ mediaEl.dataset.resolvedMediaSourceUrl = resolvedDisplaySource;
3819
+ return true;
3820
+ } catch (error) {
3821
+ if (String(mediaEl.dataset.mediaSourceUrl || '').trim() !== normalized) {
3822
+ return false;
3823
+ }
3824
+
3825
+ mediaEl.dataset.resolvedMediaSourceUrl = '';
3826
+ mediaEl.removeAttribute('src');
3827
+ if (options.load !== false && typeof mediaEl.load === 'function') {
3828
+ try { mediaEl.load(); } catch { }
3829
+ }
3830
+ setAssetRefreshState(nodeModel, 'failed', 'Video asset is missing or unavailable');
3831
+ syncMediaLoadingRowForElement(mediaEl, nodeModel, 'video');
3832
+ warn('[MindMapCss3DManager] Failed to prepare video source', normalized, error);
3833
+ return false;
3834
+ }
3835
+ }
3836
+
3548
3837
  function maybeResolveOriginalCssImageSource(mediaEl, nodeModel, contentTypeLower, options = {}) {
3549
3838
  if (!mediaEl || contentTypeLower !== 'image') return;
3550
3839
  if (options.bypassPreviewHold !== true && shouldPreferCssImagePreviewSource(nodeModel)) return;
@@ -3607,6 +3896,14 @@
3607
3896
 
3608
3897
  if (contentTypeLower === 'video'
3609
3898
  && (isLocalCssMediaReference(currentSource) || getLocalCssMediaId(nodeModel, currentSource))) {
3899
+ if (isCss3dVideoElementLowDetail(mediaEl, { element: mediaEl })) {
3900
+ mediaEl.dataset.mediaSourceUrl = getLocalCssMediaReference(nodeModel, currentSource);
3901
+ mediaEl.dataset.resolvedMediaSourceUrl = '';
3902
+ mediaEl.dataset.lodSuspendedSrc = currentSource;
3903
+ suspendCss3dVideoSourceForLod(nodeModel, { element: mediaEl });
3904
+ return;
3905
+ }
3906
+
3610
3907
  if (mediaEl.dataset.localFileResolveAttempted === '1') {
3611
3908
  return;
3612
3909
  }
@@ -3666,12 +3963,16 @@
3666
3963
 
3667
3964
  nodeModel.response = normalized;
3668
3965
  nodeModel.Response = normalized;
3669
- mediaEl.dataset.mediaSourceUrl = normalized;
3670
- mediaEl.dataset.resolvedMediaSourceUrl = normalized;
3671
- mediaEl.setAttribute('src', normalized);
3966
+ if (contentTypeLower === 'video' && isCss3dVideoElementLowDetail(mediaEl, { element: mediaEl })) {
3967
+ mediaEl.dataset.mediaSourceUrl = normalized;
3968
+ mediaEl.dataset.resolvedMediaSourceUrl = '';
3969
+ mediaEl.dataset.lodSuspendedSrc = normalized;
3970
+ suspendCss3dVideoSourceForLod(nodeModel, { element: mediaEl });
3971
+ return;
3972
+ }
3672
3973
 
3673
- if (contentTypeLower === 'video' && typeof mediaEl.load === 'function') {
3674
- mediaEl.load();
3974
+ if (contentTypeLower === 'video') {
3975
+ await setCssVideoElementSource(mediaEl, nodeModel, normalized, { load: true });
3675
3976
  }
3676
3977
  } catch (error) {
3677
3978
  setAssetRefreshState(nodeModel, 'failed', 'Fresh link request did not succeed');
@@ -3831,6 +4132,9 @@
3831
4132
  syncMediaLoadingRowForElement(mediaEl, nodeModel, contentTypeLower);
3832
4133
  mediaEl.dataset.assetRefreshAttempted = '';
3833
4134
  if (contentTypeLower === 'video') {
4135
+ if (mediaEl.dataset?.lodMediaSuspended === 'true') {
4136
+ return;
4137
+ }
3834
4138
  const playPromise = mediaEl.play?.();
3835
4139
  if (playPromise?.catch) {
3836
4140
  playPromise.catch(() => { });
@@ -3863,6 +4167,9 @@
3863
4167
  if (contentTypeLower !== 'video') {
3864
4168
  return;
3865
4169
  }
4170
+ if (mediaEl.dataset?.lodMediaSuspended === 'true') {
4171
+ return;
4172
+ }
3866
4173
 
3867
4174
  const metadata = ensureCssMediaMetadata(nodeModel);
3868
4175
  const message = describeVideoElementError();
@@ -3876,6 +4183,9 @@
3876
4183
  };
3877
4184
 
3878
4185
  const handleMediaError = async () => {
4186
+ if (contentTypeLower === 'video' && mediaEl.dataset?.lodMediaSuspended === 'true') {
4187
+ return;
4188
+ }
3879
4189
  if (contentTypeLower === 'image') {
3880
4190
  mediaEl.dataset.mediaReady = '0';
3881
4191
  mediaEl.style.opacity = '0';
@@ -3897,6 +4207,9 @@
3897
4207
 
3898
4208
  if (contentTypeLower === 'video') {
3899
4209
  mediaEl.onstalled = () => {
4210
+ if (mediaEl.dataset?.lodMediaSuspended === 'true') {
4211
+ return;
4212
+ }
3900
4213
  void tryRefreshCssMediaSource(mediaEl, nodeModel, contentTypeLower);
3901
4214
  };
3902
4215
  mediaEl.onloadedmetadata = handleReady;
@@ -22698,17 +23011,14 @@
22698
23011
  video.dataset.resolvedMediaSourceUrl = '';
22699
23012
  video.dataset.assetRefreshAttempted = '';
22700
23013
  video.dataset.localFileResolveAttempted = '';
22701
- if (isLocalCssMediaReference(normalizedContent) || getLocalCssMediaId(nodeModel, normalizedContent)) {
22702
- void resolveLocalCssMediaSource(video, nodeModel, contentTypeLower);
22703
- } else {
22704
- video.src = normalizedContent;
22705
- }
23014
+ video.dataset.lodSuspendedSrc = normalizedContent;
23015
+ video.dataset.lodMediaSuspended = 'true';
22706
23016
  }
22707
23017
  video.muted = true;
22708
23018
  video.loop = true;
22709
23019
  video.autoplay = false;
22710
23020
  video.playsInline = true;
22711
- video.preload = 'metadata';
23021
+ video.preload = 'none';
22712
23022
  video.autoplay = ensureVideoNodePlaybackState(nodeModel, video) === 'play';
22713
23023
  video.style.cssText = `position: absolute; left: 0; top: 0; width: calc(100% + var(--mind-media-surface-bleed, 0px)); height: calc(100% + var(--mind-media-surface-bleed, 0px)); min-width: calc(100% + var(--mind-media-surface-bleed, 0px)); min-height: calc(100% + var(--mind-media-surface-bleed, 0px)); max-width: none; max-height: none; object-fit: cover; object-position: center center; pointer-events: none; display: block; background: transparent; border: 0; outline: 0;`;
22714
23024
  video.ondragstart = (e) => e.preventDefault();
@@ -23405,6 +23715,12 @@
23405
23715
  mediaEl.dataset.assetRefreshAttempted = '';
23406
23716
  if (contentTypeLower === 'image') {
23407
23717
  void setCssImageElementSource(mediaEl, nodeModel, content);
23718
+ } else if (contentTypeLower === 'video' && isCss3dVideoElementLowDetail(mediaEl, cssObject)) {
23719
+ const normalizedContent = normalizeCssMediaUrl(content);
23720
+ mediaEl.dataset.mediaSourceUrl = normalizedContent;
23721
+ mediaEl.dataset.resolvedMediaSourceUrl = mediaEl.dataset.resolvedMediaSourceUrl || '';
23722
+ mediaEl.dataset.lodSuspendedSrc = normalizedContent;
23723
+ syncCss3dVideoLodMediaSource(nodeModel, cssObject, false);
23408
23724
  } else if (isLocalCssMediaReference(content) || getLocalCssMediaId(nodeModel, content)) {
23409
23725
  mediaEl.dataset.mediaSourceUrl = getLocalCssMediaReference(nodeModel, content);
23410
23726
  mediaEl.dataset.resolvedMediaSourceUrl = '';
@@ -23414,7 +23730,9 @@
23414
23730
  delete mediaEl.dataset.videoCanvasProxyFailed;
23415
23731
  mediaEl.src = content;
23416
23732
  }
23417
- if (contentTypeLower === 'video' && typeof mediaEl.load === 'function') {
23733
+ if (contentTypeLower === 'video' &&
23734
+ mediaEl.dataset?.lodMediaSuspended !== 'true' &&
23735
+ typeof mediaEl.load === 'function') {
23418
23736
  mediaEl.load();
23419
23737
  }
23420
23738
  }
@@ -23428,7 +23746,12 @@
23428
23746
  }
23429
23747
  }
23430
23748
  if (contentTypeLower === 'video') {
23431
- syncCss3dVideoCanvasProxy(nodeModel, cssObject);
23749
+ if (isCss3dVideoElementLowDetail(mediaEl, cssObject)) {
23750
+ syncCss3dVideoLodMediaSource(nodeModel, cssObject, false);
23751
+ } else {
23752
+ syncCss3dVideoLodMediaSource(nodeModel, cssObject, true);
23753
+ syncCss3dVideoCanvasProxy(nodeModel, cssObject);
23754
+ }
23432
23755
  }
23433
23756
  const loadingEl = el.querySelector(`[id^="node-loading-"]`);
23434
23757
  if (loadingEl) {
@@ -23893,6 +24216,7 @@
23893
24216
  hasMountedEmbedIframe: hasMountedEmbedIframe,
23894
24217
  shouldConsumeEmbedPointerEvent: shouldConsumeEmbedPointerEvent,
23895
24218
  syncCss3dContent: syncCss3dContent,
24219
+ syncCss3dVideoLodMediaSource: syncCss3dVideoLodMediaSource,
23896
24220
  syncCss3dScrollFromElement: syncCss3dScrollFromElement,
23897
24221
  syncCss3dScrollFromModel: syncCss3dScrollFromModel,
23898
24222
  syncCss3dMediaNodeFrame: syncCss3dMediaNodeFrame,
@@ -7405,7 +7405,7 @@ window.MindMapInteractions = (function () {
7405
7405
  caret-color: #1f2937;
7406
7406
  box-sizing: border-box; /* User Request */
7407
7407
  overflow-y: auto; /* User Request */
7408
- -webkit-font-smoothing: antialiased;
7408
+ -webkit-font-smoothing: auto;
7409
7409
  text-rendering: auto;
7410
7410
  white-space: pre-wrap;
7411
7411
  overflow-wrap: break-word;
@@ -13,13 +13,13 @@
13
13
  // LOD thresholds (explicit bands)
14
14
  const LOD_THRESHOLDS = {
15
15
  NEAR: 4500, // ~4500: DOM overlay + CSS3D
16
- NORMAL: 12000, // 4500~12000: CSS3D only
17
- MID: 12000, // 12000~30000: instanced text lines + nodes
16
+ NORMAL: 4500, // Legacy alias; NORMAL no longer keeps a scaled CSS3D band.
17
+ MID: 4500, // 4500~30000: resident instanced nodes + line silhouettes
18
18
  FAR: 30000 // 30000~: nodes only (lines hidden)
19
19
  };
20
20
 
21
21
  // Backward-compatible pivot (used by legacy LOD switch)
22
- const LOD_THRESHOLD_FAR = LOD_THRESHOLDS.MID; // Switch to LOD past this
22
+ const LOD_THRESHOLD_FAR = LOD_THRESHOLDS.MID; // Switch to resident LOD past this
23
23
  const LOD_THRESHOLD_NEAR = LOD_THRESHOLDS.MID; // Switch to full-res before this
24
24
  function isDocumentHidden() {
25
25
  return typeof document !== 'undefined' && document.visibilityState === 'hidden';
@@ -51,7 +51,7 @@
51
51
  // ▲▲▲ [Usage] ▲▲▲
52
52
 
53
53
  // Procedural line rendering settings
54
- const LOD_RENDERER_BUILD_ID = '20260621-low-detail-proxy-css3d-v776';
54
+ const LOD_RENDERER_BUILD_ID = '20260621-video-prefetch-v785';
55
55
  const DirtyKind = Object.freeze({
56
56
  ResidentFullRebuild: 'resident-full-rebuild',
57
57
  ResidentPatch: 'resident-patch',
@@ -4476,6 +4476,10 @@ const LOD_RENDERER_BUILD_ID = '20260621-low-detail-proxy-css3d-v776';
4476
4476
 
4477
4477
  _setCss3dLodClass(entry, isHigh) {
4478
4478
  this._setNodeDetailLevel(entry, isHigh ? 'HIGH' : 'LOW');
4479
+ if (getNodeContentType(entry?.model) === 'video' &&
4480
+ window.MindMapCss3DManager?.syncCss3dVideoLodMediaSource) {
4481
+ window.MindMapCss3DManager.syncCss3dVideoLodMediaSource(entry.model, entry.cssObject, isHigh === true);
4482
+ }
4479
4483
  }
4480
4484
 
4481
4485
  _shouldUseHighCss3dDetail(entry, module) {
@@ -4775,12 +4779,18 @@ const LOD_RENDERER_BUILD_ID = '20260621-low-detail-proxy-css3d-v776';
4775
4779
  }
4776
4780
  // ▲▲▲ [FIX] ▲▲▲
4777
4781
 
4778
- this._setCss3dLodClass(entry, this._shouldUseHighCss3dDetail(entry, module));
4782
+ const shouldUseHighCss3dDetail = this._shouldUseHighCss3dDetail(entry, module);
4783
+ this._setCss3dLodClass(entry, shouldUseHighCss3dDetail);
4779
4784
 
4780
4785
  // ▼▼▼ [CRITICAL] Sync content BEFORE making CSS visible ▼▼▼
4781
4786
  // Prevents one-frame display of stale/blurred text while dirty DOM is being updated.
4782
4787
  const cssMediaEl = entry.cssObject.element?.querySelector?.('img,video') || null;
4783
- const needsInitialMediaSync = !!cssMediaEl && !cssMediaEl.getAttribute('src');
4788
+ const isLowDetailVideoProxy =
4789
+ getNodeContentType(model) === 'video' &&
4790
+ shouldUseHighCss3dDetail !== true;
4791
+ const needsInitialMediaSync = !!cssMediaEl &&
4792
+ !cssMediaEl.getAttribute('src') &&
4793
+ isLowDetailVideoProxy !== true;
4784
4794
  if ((entry.isCssDirty || needsInitialMediaSync) && window.MindMapCss3DManager?.syncCss3dContent) {
4785
4795
  window.MindMapCss3DManager.syncCss3dContent(model, entry.cssObject);
4786
4796
  entry.isCssDirty = false; // sync 완료 후 clean
@@ -5743,7 +5753,7 @@ const LOD_RENDERER_BUILD_ID = '20260621-low-detail-proxy-css3d-v776';
5743
5753
  return;
5744
5754
  }
5745
5755
 
5746
- // Explicit bands: NEAR(DOM overlay + CSS3D), NORMAL(CSS3D), MID(노드+라인), FAR(노드만)
5756
+ // Explicit bands: NEAR(DOM overlay + CSS3D), MID(resident nodes+lines), FAR(nodes only)
5747
5757
  const shouldUseLOD = lodBand === 'MID' || lodBand === 'FAR';
5748
5758
  perfMode = shouldUseLOD ? 'lod' : 'fullres';
5749
5759
  const isLodPanFrame = shouldUseLOD &&
@@ -5925,7 +5935,7 @@ const LOD_RENDERER_BUILD_ID = '20260621-low-detail-proxy-css3d-v776';
5925
5935
 
5926
5936
  } else {
5927
5937
  perfTime('fullResSetup', () => {
5928
- // Full-res: NEAR(DOM overlay + CSS3D) / NORMAL(CSS3D only)
5938
+ // Full-res: NEAR only. NORMAL is intentionally collapsed into resident MID.
5929
5939
  const tNearStart = performance.now();
5930
5940
  if (this.instancedHaloMesh) this.instancedHaloMesh.visible = false;
5931
5941
  if (this.instancedConsoleMesh) this.instancedConsoleMesh.visible = false;
@@ -265,15 +265,15 @@
265
265
  const cameraZ = Number(module?.camera?.position?.z || 0);
266
266
  const thresholds = globalThis.LODRenderer?.LOD_THRESHOLDS || {
267
267
  NEAR: 4500,
268
- NORMAL: 12000,
269
- MID: 12000,
268
+ NORMAL: 4500,
269
+ MID: 4500,
270
270
  FAR: 30000
271
271
  };
272
272
 
273
273
  if (cameraZ >= Number(thresholds.FAR || 25000)) {
274
274
  return 'FAR';
275
275
  }
276
- if (cameraZ >= Number(thresholds.MID || thresholds.NORMAL || 12000)) {
276
+ if (cameraZ >= Number(thresholds.MID || thresholds.NORMAL || 4500)) {
277
277
  return 'MID';
278
278
  }
279
279
  if (cameraZ >= Number(thresholds.NORMAL || thresholds.NEAR || 4500)) {
@@ -1932,8 +1932,8 @@
1932
1932
  copyStyle(null, readComputedStyleValue(computed, null, 'text-wrap'), 'text-wrap');
1933
1933
  copyStyle(null, readComputedStyleValue(computed, null, 'text-wrap-mode'), 'text-wrap-mode');
1934
1934
  copyStyle(null, readComputedStyleValue(computed, null, 'text-wrap-style'), 'text-wrap-style');
1935
- targetElement.style.setProperty('-webkit-font-smoothing', 'antialiased');
1936
- targetElement.style.setProperty('-moz-osx-font-smoothing', 'grayscale');
1935
+ targetElement.style.setProperty('-webkit-font-smoothing', 'auto');
1936
+ targetElement.style.setProperty('-moz-osx-font-smoothing', 'auto');
1937
1937
  }
1938
1938
 
1939
1939
  function getTypographySyncChildren(element) {
@@ -7,8 +7,8 @@
7
7
  <title>MindExec | Business Execution OS for solo builders</title>
8
8
  <meta name="description" content="MindExec is an AI business execution OS for solo builders who want to turn notes, research, assets, and repeatable execution Skills into revenue-producing work." />
9
9
  <base href="/" />
10
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260621-low-detail-proxy-css3d-v776" />
11
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260621-low-detail-proxy-css3d-v776" />
10
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260621-video-prefetch-v785" />
11
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260621-video-prefetch-v785" />
12
12
  <!-- ??좎뜦堉??Font Awesome (local) ??좎뜦堉??-->
13
13
  <link rel="stylesheet" href="_content/MindExecution.Shared/lib/font-awesome/css/all.min.css" />
14
14
  <!-- ??좎뜦堉??-->
@@ -579,7 +579,7 @@
579
579
  }
580
580
 
581
581
  const base = '_content/MindExecution.Shared/js/';
582
- const scriptVersion = '20260621-low-detail-proxy-css3d-v776';
582
+ const scriptVersion = '20260621-video-prefetch-v785';
583
583
  const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
584
584
  console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
585
585
  const criticalScripts = [
@@ -1,5 +1,5 @@
1
1
  self.assetsManifest = {
2
- "version": "MbpJnc8v",
2
+ "version": "jBFdtOmP",
3
3
  "assets": [
4
4
  {
5
5
  "hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
@@ -42,7 +42,7 @@
42
42
  "url": "_content/MindExecution.Shared/css/app.css"
43
43
  },
44
44
  {
45
- "hash": "sha256-LJ0d20aCwq93DVCUqLyvJNX+mNcrsdfFaCVIH9yV2fI=",
45
+ "hash": "sha256-QyE+9OXJBCXf5eXHuMaxnGjVAMIuQr72tNhfSCk6nro=",
46
46
  "url": "_content/MindExecution.Shared/css/mind-map-overrides.css"
47
47
  },
48
48
  {
@@ -78,7 +78,7 @@
78
78
  "url": "_content/MindExecution.Shared/js/marked.min.js"
79
79
  },
80
80
  {
81
- "hash": "sha256-mpLsxGYtGGy/YqbZwr5Ex7q31qt7q9BayMYC6nRCXzg=",
81
+ "hash": "sha256-bcZumly3UqFp3yXp94h51Uect2dFJXYGVRKxtJkh4HE=",
82
82
  "url": "_content/MindExecution.Shared/js/mind-map-core.js"
83
83
  },
84
84
  {
@@ -86,7 +86,7 @@
86
86
  "url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
87
87
  },
88
88
  {
89
- "hash": "sha256-Fqn/yDEDcOO23DxXXIBdQgS4u6ukCYTkfNfn8xYMAwM=",
89
+ "hash": "sha256-1hnC4MKgwN6Nsz5+5t3Kjy6m/3kfaI7QNMFKaS8H/hA=",
90
90
  "url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
91
91
  },
92
92
  {
@@ -106,7 +106,7 @@
106
106
  "url": "_content/MindExecution.Shared/js/mind-map-glow-shader.js"
107
107
  },
108
108
  {
109
- "hash": "sha256-zEddte5cBTtErNAKwTTpVx4/HQVQpkkF460AYsYe/28=",
109
+ "hash": "sha256-iqbrsttHQqhuGvTIj1yVbjF79M218kbqXCZ3R1iyg/Y=",
110
110
  "url": "_content/MindExecution.Shared/js/mind-map-interactions.js"
111
111
  },
112
112
  {
@@ -114,7 +114,7 @@
114
114
  "url": "_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js"
115
115
  },
116
116
  {
117
- "hash": "sha256-inS/Wi5+LJCbvq9pUqv0SL85qgeXAK3mKPwOdYR5zXw=",
117
+ "hash": "sha256-btpizB1MkhK5bRl7BmaL4+ZNHh2bZrg160eaiBbzRy8=",
118
118
  "url": "_content/MindExecution.Shared/js/mind-map-lod-renderer.js"
119
119
  },
120
120
  {
@@ -154,7 +154,7 @@
154
154
  "url": "_content/MindExecution.Shared/js/mind-map-text-lod-system.js"
155
155
  },
156
156
  {
157
- "hash": "sha256-e94AeCT+ioRhNY17vPaFfz2iVFvaZ8x5nfmAKFCXGbw=",
157
+ "hash": "sha256-pEdLlTuNhb8coOCRcx1fX+xLyD8ATKxwR8CXupgbgtk=",
158
158
  "url": "_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js"
159
159
  },
160
160
  {
@@ -834,7 +834,7 @@
834
834
  "url": "image-manifest.json"
835
835
  },
836
836
  {
837
- "hash": "sha256-Sp1f8uVvLis0unU3xP8nl5340QG8tNoGJcsSIEQZfsU=",
837
+ "hash": "sha256-mvQ5z9xIyVIoYzxOcaAL8lUhs30CGFe2NcbVq3Wa0AU=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: MbpJnc8v */
1
+ /* Manifest version: jBFdtOmP */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4