@mindexec/cli 0.2.445 → 0.2.447
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +6 -3
- package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
- package/remote-hub.js +171 -6
- package/scripts/remote-fast-live-rate-smoke.mjs +1 -0
- package/scripts/remote-fleet-render-smoke.mjs +74 -36
- package/scripts/remote-hub-smoke.mjs +7 -0
- package/server.js +2 -2
- package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1 -3
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +199 -100
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +186 -360
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +124 -179
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +167 -24
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +96 -74
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +66 -30
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +6 -3
- package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +35 -2
- package/wwwroot/_framework/{MindExecution.Core.4moacskf64.dll → MindExecution.Core.mswl4wlkm9.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.pa251cfsz4.dll → MindExecution.Kernel.zupeldfptg.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.dxyuwmxv1s.dll → MindExecution.Plugins.Admin.hq0vyqnqtn.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.7uj5erqhhn.dll → MindExecution.Plugins.Business.pucw3o7rno.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.108rmsxc96.dll → MindExecution.Plugins.Concept.557tzzkrrx.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.ccsy3g0fe5.dll → MindExecution.Plugins.Directory.zh1ddg1fn6.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.1vjpuoe9ff.dll → MindExecution.Plugins.PlanMaster.0z9pzynpu9.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.q8ml98phj9.dll → MindExecution.Plugins.YouTube.y8g77r54fa.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.2pywq19zjy.dll → MindExecution.Shared.1ymsdbwid2.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.qucbls1wfr.dll → MindExecution.Web.bb1txupuv0.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +21 -21
- package/wwwroot/index.html +2 -1
- package/wwwroot/service-worker-assets.js +36 -32
- package/wwwroot/service-worker.js +1 -1
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
// ▲▲▲ [Usage] ▲▲▲
|
|
52
52
|
|
|
53
53
|
// Procedural line rendering settings
|
|
54
|
-
const LOD_RENDERER_BUILD_ID = '
|
|
54
|
+
const LOD_RENDERER_BUILD_ID = '20260725-cold-mid-line-restore-v846';
|
|
55
55
|
const DirtyKind = Object.freeze({
|
|
56
56
|
ResidentFullRebuild: 'resident-full-rebuild',
|
|
57
57
|
ResidentPatch: 'resident-patch',
|
|
@@ -585,7 +585,14 @@
|
|
|
585
585
|
return !isAnimatedGifImageModel(model);
|
|
586
586
|
}
|
|
587
587
|
|
|
588
|
-
|
|
588
|
+
// Every video needs a resident MID/FAR visual. Videos without an
|
|
589
|
+
// explicit preview use a lightweight atlas placeholder so the live
|
|
590
|
+
// <video> element can stay suspended without making the node vanish.
|
|
591
|
+
if (contentType === 'video') {
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (contentType !== 'embed') {
|
|
589
596
|
return false;
|
|
590
597
|
}
|
|
591
598
|
|
|
@@ -1255,6 +1262,33 @@
|
|
|
1255
1262
|
return [invAtlas, invAtlas, invAtlas, invAtlas];
|
|
1256
1263
|
}
|
|
1257
1264
|
|
|
1265
|
+
_getOrCreateImageAtlasSlot(nodeId) {
|
|
1266
|
+
const normalizedId = String(nodeId || '').trim();
|
|
1267
|
+
if (!normalizedId) {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
let slot = this.imageAtlasSlots.get(normalizedId) || null;
|
|
1272
|
+
if (slot) {
|
|
1273
|
+
return slot;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
const slotIndex = this._allocateImageAtlasSlot();
|
|
1277
|
+
if (slotIndex < 0) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
slot = {
|
|
1282
|
+
slotIndex,
|
|
1283
|
+
key: '',
|
|
1284
|
+
uvRect: this._getImageAtlasUvRect(slotIndex),
|
|
1285
|
+
ready: false,
|
|
1286
|
+
unsupported: false
|
|
1287
|
+
};
|
|
1288
|
+
this.imageAtlasSlots.set(normalizedId, slot);
|
|
1289
|
+
return slot;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1258
1292
|
_compactImageAtlasLoadQueue(force = false) {
|
|
1259
1293
|
const cursor = this._imageAtlasLoadCursor || 0;
|
|
1260
1294
|
if (cursor <= 0) return;
|
|
@@ -1653,35 +1687,114 @@
|
|
|
1653
1687
|
}
|
|
1654
1688
|
}
|
|
1655
1689
|
|
|
1690
|
+
_drawVideoLodPlaceholder(slot, nodeWidth, nodeHeight) {
|
|
1691
|
+
if (!slot || !this.imageAtlasCanvas || typeof document === 'undefined') {
|
|
1692
|
+
return false;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
const ctx = this.imageAtlasCanvas.getContext('2d', { alpha: false });
|
|
1696
|
+
if (!ctx) {
|
|
1697
|
+
return false;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
const columns = this.imageAtlasColumns || IMAGE_LOD_ATLAS_COLUMNS;
|
|
1701
|
+
const col = slot.slotIndex % columns;
|
|
1702
|
+
const row = Math.floor(slot.slotIndex / columns);
|
|
1703
|
+
const x = col * IMAGE_LOD_THUMBNAIL_SIZE;
|
|
1704
|
+
const y = row * IMAGE_LOD_THUMBNAIL_SIZE;
|
|
1705
|
+
const size = IMAGE_LOD_THUMBNAIL_SIZE;
|
|
1706
|
+
const safeWidth = Math.max(1, Number(nodeWidth || 300));
|
|
1707
|
+
const safeHeight = Math.max(1, Number(nodeHeight || 300));
|
|
1708
|
+
const targetAspect = safeWidth / safeHeight;
|
|
1709
|
+
|
|
1710
|
+
try {
|
|
1711
|
+
ctx.save();
|
|
1712
|
+
|
|
1713
|
+
const background = ctx.createLinearGradient(x, y, x, y + size);
|
|
1714
|
+
background.addColorStop(0, '#26364d');
|
|
1715
|
+
background.addColorStop(1, '#0f172a');
|
|
1716
|
+
ctx.fillStyle = background;
|
|
1717
|
+
ctx.fillRect(x, y, size, size);
|
|
1718
|
+
|
|
1719
|
+
const inset = 4;
|
|
1720
|
+
ctx.strokeStyle = 'rgba(148, 163, 184, 0.58)';
|
|
1721
|
+
ctx.lineWidth = 2;
|
|
1722
|
+
ctx.strokeRect(x + inset, y + inset, size - inset * 2, size - inset * 2);
|
|
1723
|
+
|
|
1724
|
+
// Pre-compensate the icon for the square atlas tile being
|
|
1725
|
+
// stretched to the video's world-space aspect ratio.
|
|
1726
|
+
const radiusX = size * 0.19;
|
|
1727
|
+
const radiusY = Math.min(size * 0.34, radiusX * Math.max(0.55, targetAspect));
|
|
1728
|
+
const centerX = x + size * 0.5;
|
|
1729
|
+
const centerY = y + size * 0.48;
|
|
1730
|
+
ctx.beginPath();
|
|
1731
|
+
ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
1732
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.18)';
|
|
1733
|
+
ctx.fill();
|
|
1734
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.62)';
|
|
1735
|
+
ctx.lineWidth = 2;
|
|
1736
|
+
ctx.stroke();
|
|
1737
|
+
|
|
1738
|
+
const triangleWidth = size * 0.13;
|
|
1739
|
+
const triangleHeight = Math.min(size * 0.24, triangleWidth * Math.max(0.85, targetAspect));
|
|
1740
|
+
ctx.beginPath();
|
|
1741
|
+
ctx.moveTo(centerX - triangleWidth * 0.35, centerY - triangleHeight * 0.5);
|
|
1742
|
+
ctx.lineTo(centerX + triangleWidth * 0.7, centerY);
|
|
1743
|
+
ctx.lineTo(centerX - triangleWidth * 0.35, centerY + triangleHeight * 0.5);
|
|
1744
|
+
ctx.closePath();
|
|
1745
|
+
ctx.fillStyle = '#f8fafc';
|
|
1746
|
+
ctx.fill();
|
|
1747
|
+
|
|
1748
|
+
ctx.fillStyle = 'rgba(226, 232, 240, 0.35)';
|
|
1749
|
+
ctx.fillRect(x + size * 0.18, y + size * 0.82, size * 0.64, 2);
|
|
1750
|
+
ctx.fillStyle = 'rgba(248, 250, 252, 0.78)';
|
|
1751
|
+
ctx.fillRect(x + size * 0.18, y + size * 0.82, size * 0.2, 2);
|
|
1752
|
+
ctx.restore();
|
|
1753
|
+
|
|
1754
|
+
if (this.imageAtlasTexture) {
|
|
1755
|
+
this.imageAtlasTexture.needsUpdate = true;
|
|
1756
|
+
}
|
|
1757
|
+
return true;
|
|
1758
|
+
} catch {
|
|
1759
|
+
try { ctx.restore(); } catch { }
|
|
1760
|
+
return false;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1656
1764
|
_ensureImageLodThumbnail(entry) {
|
|
1657
1765
|
const nodeId = entry?.model?.id;
|
|
1658
1766
|
if (!nodeId) {
|
|
1659
1767
|
return null;
|
|
1660
1768
|
}
|
|
1661
1769
|
|
|
1770
|
+
const contentType = getNodeContentType(entry?.model);
|
|
1771
|
+
const nodeWidth = Math.max(1, Number(entry?.model?.width || entry?.model?.Width || 300));
|
|
1772
|
+
const nodeHeight = Math.max(1, Number(entry?.model?.height || entry?.model?.Height || 300));
|
|
1662
1773
|
const source = this._getImageLodSource(entry);
|
|
1663
1774
|
if (!source) {
|
|
1775
|
+
if (contentType === 'video') {
|
|
1776
|
+
const placeholderKey = `video-placeholder|node:${Math.round(nodeWidth)}x${Math.round(nodeHeight)}`;
|
|
1777
|
+
const placeholderSlot = this._getOrCreateImageAtlasSlot(nodeId);
|
|
1778
|
+
if (!placeholderSlot) {
|
|
1779
|
+
return null;
|
|
1780
|
+
}
|
|
1781
|
+
if (placeholderSlot.ready && placeholderSlot.key === placeholderKey) {
|
|
1782
|
+
return placeholderSlot;
|
|
1783
|
+
}
|
|
1784
|
+
if (this._drawVideoLodPlaceholder(placeholderSlot, nodeWidth, nodeHeight)) {
|
|
1785
|
+
placeholderSlot.key = placeholderKey;
|
|
1786
|
+
placeholderSlot.ready = true;
|
|
1787
|
+
placeholderSlot.unsupported = false;
|
|
1788
|
+
return placeholderSlot;
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1664
1791
|
return null;
|
|
1665
1792
|
}
|
|
1666
1793
|
|
|
1667
|
-
const nodeWidth = Math.max(1, Number(entry?.model?.width || entry?.model?.Width || 300));
|
|
1668
|
-
const nodeHeight = Math.max(1, Number(entry?.model?.height || entry?.model?.Height || 300));
|
|
1669
1794
|
const sourceKey = `${source.key}|node:${Math.round(nodeWidth)}x${Math.round(nodeHeight)}`;
|
|
1670
|
-
|
|
1671
|
-
let slot = this.imageAtlasSlots.get(nodeId) || null;
|
|
1795
|
+
const slot = this._getOrCreateImageAtlasSlot(nodeId);
|
|
1672
1796
|
if (!slot) {
|
|
1673
|
-
|
|
1674
|
-
if (slotIndex < 0) {
|
|
1675
|
-
return null;
|
|
1676
|
-
}
|
|
1677
|
-
slot = {
|
|
1678
|
-
slotIndex,
|
|
1679
|
-
key: '',
|
|
1680
|
-
uvRect: this._getImageAtlasUvRect(slotIndex),
|
|
1681
|
-
ready: false,
|
|
1682
|
-
unsupported: false
|
|
1683
|
-
};
|
|
1684
|
-
this.imageAtlasSlots.set(nodeId, slot);
|
|
1797
|
+
return null;
|
|
1685
1798
|
}
|
|
1686
1799
|
|
|
1687
1800
|
if (slot.ready && slot.key === sourceKey) {
|
|
@@ -1699,6 +1812,14 @@
|
|
|
1699
1812
|
return slot;
|
|
1700
1813
|
}
|
|
1701
1814
|
|
|
1815
|
+
if (contentType === 'video' &&
|
|
1816
|
+
this._drawVideoLodPlaceholder(slot, nodeWidth, nodeHeight)) {
|
|
1817
|
+
slot.key = `video-placeholder|source:${sourceKey}`;
|
|
1818
|
+
slot.ready = true;
|
|
1819
|
+
slot.unsupported = false;
|
|
1820
|
+
return slot;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1702
1823
|
if (source.fetchFallbackOnDrawFailure === true) {
|
|
1703
1824
|
slot.key = '';
|
|
1704
1825
|
slot.ready = false;
|
|
@@ -3340,6 +3461,8 @@
|
|
|
3340
3461
|
_hasPendingLineDetailRestore(lodBand = this._currentLodBand) {
|
|
3341
3462
|
return ENABLE_INSTANCED_TEXT_LINES === true &&
|
|
3342
3463
|
this._lineDetailsPatchPending === true &&
|
|
3464
|
+
this._deferLineRestorePending !== true &&
|
|
3465
|
+
this._disableLineRender !== true &&
|
|
3343
3466
|
this.isInLODMode === true &&
|
|
3344
3467
|
lodBand === 'MID' &&
|
|
3345
3468
|
this.isLoading !== true;
|
|
@@ -6642,15 +6765,29 @@
|
|
|
6642
6765
|
|
|
6643
6766
|
if (disableLines) {
|
|
6644
6767
|
const cachedLineData = this.lineDataCache.get(nodeId) || null;
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6768
|
+
const deferredLineData = cachedLineData || (
|
|
6769
|
+
this._deferLineRestorePending === true
|
|
6770
|
+
? window.MindMapRenderPlan?.resolveResidentLineData?.(
|
|
6771
|
+
this,
|
|
6772
|
+
entry,
|
|
6773
|
+
w,
|
|
6774
|
+
h,
|
|
6775
|
+
false,
|
|
6776
|
+
MAX_LINE_SAMPLES,
|
|
6777
|
+
LINE_HEIGHT_PX,
|
|
6778
|
+
LINE_PADDING_PX
|
|
6779
|
+
) || null
|
|
6780
|
+
: null
|
|
6781
|
+
);
|
|
6782
|
+
if (deferredLineData) {
|
|
6783
|
+
lineCounts.push(deferredLineData.lineCount || 0);
|
|
6784
|
+
lineHeights.push(deferredLineData.lineHeightPx || LINE_HEIGHT_PX);
|
|
6648
6785
|
linePaddings.push(
|
|
6649
|
-
|
|
6650
|
-
|
|
6786
|
+
deferredLineData.paddingX || LINE_PADDING_PX,
|
|
6787
|
+
deferredLineData.paddingY || LINE_PADDING_PX
|
|
6651
6788
|
);
|
|
6652
6789
|
for (let i = 0; i < MAX_LINE_SAMPLES; i++) {
|
|
6653
|
-
lineLengths.push(
|
|
6790
|
+
lineLengths.push(deferredLineData.lineLengths?.[i] || 0);
|
|
6654
6791
|
}
|
|
6655
6792
|
} else {
|
|
6656
6793
|
lineCounts.push(0);
|
|
@@ -7910,6 +8047,12 @@
|
|
|
7910
8047
|
if (!hasResidentInstances) {
|
|
7911
8048
|
this._setInstancesDirty('initial-board-build');
|
|
7912
8049
|
}
|
|
8050
|
+
if (shouldDeferLineRestore) {
|
|
8051
|
+
this._scheduleResidentLodWake(
|
|
8052
|
+
'post-load-line-restore',
|
|
8053
|
+
POST_LOAD_LINE_RESTORE_DELAY_MS + 16
|
|
8054
|
+
);
|
|
8055
|
+
}
|
|
7913
8056
|
// ▼▼▼ [UX] Show nodes immediately after loading ends ▼▼▼
|
|
7914
8057
|
if (this.instancedHaloMesh) this.instancedHaloMesh.visible = this.isInLODMode === true && shouldShowInstancedHaloInLodBand(this._currentLodBand);
|
|
7915
8058
|
if (this.instancedMesh) this.instancedMesh.visible = this.isInLODMode === true;
|
|
@@ -525,7 +525,7 @@
|
|
|
525
525
|
return nodeEntry.cssObject.userData.spatialGridKeys;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
-
return
|
|
528
|
+
return null;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
function setNodeSpatialGridKeys(nodeEntry, keys) {
|
|
@@ -589,7 +589,7 @@
|
|
|
589
589
|
const bounds = getNodeWorldBounds(nodeEntry);
|
|
590
590
|
if (!bounds) return;
|
|
591
591
|
|
|
592
|
-
const oldKeys = getNodeSpatialGridKeys(nodeEntry);
|
|
592
|
+
const oldKeys = getNodeSpatialGridKeys(nodeEntry) || new Set();
|
|
593
593
|
const newKeys = new Set();
|
|
594
594
|
const startCellX = Math.floor(bounds.minX / module.GRID_CELL_SIZE);
|
|
595
595
|
const endCellX = Math.floor(bounds.maxX / module.GRID_CELL_SIZE);
|
|
@@ -789,7 +789,7 @@
|
|
|
789
789
|
const preservedY = Number(oldGroup?.position?.y ?? model.positionY ?? 0);
|
|
790
790
|
const preservedZ = Number(oldGroup?.position?.z ?? model.positionZ ?? 0);
|
|
791
791
|
const preservedVisible = oldGroup?.visible !== false;
|
|
792
|
-
const preservedSpatialKeys = new Set(getNodeSpatialGridKeys(nodeEntry));
|
|
792
|
+
const preservedSpatialKeys = new Set(getNodeSpatialGridKeys(nodeEntry) || []);
|
|
793
793
|
const width = Number(model.width || 400);
|
|
794
794
|
const height = Number(model.height || 200);
|
|
795
795
|
|
|
@@ -846,21 +846,15 @@
|
|
|
846
846
|
function removeNodeFromSpatialGrid(module, nodeId, nodeEntry = null) {
|
|
847
847
|
if (!module?.spatialGrid || !nodeId) return;
|
|
848
848
|
|
|
849
|
-
const keys = getNodeSpatialGridKeys(nodeEntry)
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (cell && cell.size === 0) module.spatialGrid.delete(key);
|
|
855
|
-
});
|
|
856
|
-
setNodeSpatialGridKeys(nodeEntry, new Set());
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
module.spatialGrid.forEach((cell, key) => {
|
|
849
|
+
const keys = getNodeSpatialGridKeys(nodeEntry)
|
|
850
|
+
|| window.MindMapCss3dHotPath?.getSpatialGridKeysForBounds?.(module, nodeEntry)
|
|
851
|
+
|| new Set();
|
|
852
|
+
keys.forEach(key => {
|
|
853
|
+
const cell = module.spatialGrid.get(key);
|
|
861
854
|
if (!cell?.delete?.(nodeId)) return;
|
|
862
855
|
if (cell.size === 0) module.spatialGrid.delete(key);
|
|
863
856
|
});
|
|
857
|
+
setNodeSpatialGridKeys(nodeEntry, new Set());
|
|
864
858
|
}
|
|
865
859
|
// ▲▲▲ [New] ▲▲▲
|
|
866
860
|
|
|
@@ -927,6 +921,7 @@
|
|
|
927
921
|
return null;
|
|
928
922
|
}
|
|
929
923
|
|
|
924
|
+
const geometryKey = `${Math.round(width)}x${Math.round(height)}:${IMAGE_SELECTION_GLOW_PAD}`;
|
|
930
925
|
if (!outline) {
|
|
931
926
|
outline = new THREE.Mesh(
|
|
932
927
|
createRoundedRectFilledGeometry(
|
|
@@ -943,15 +938,20 @@
|
|
|
943
938
|
})
|
|
944
939
|
);
|
|
945
940
|
outline.name = 'outline';
|
|
941
|
+
outline.userData.geometrySizeKey = geometryKey;
|
|
946
942
|
group.add(outline);
|
|
943
|
+
} else {
|
|
944
|
+
window.MindMapCss3dHotPath?.replaceGeometryIfSizeChanged?.(
|
|
945
|
+
outline,
|
|
946
|
+
geometryKey,
|
|
947
|
+
() => createRoundedRectFilledGeometry(
|
|
948
|
+
width + IMAGE_SELECTION_GLOW_PAD,
|
|
949
|
+
height + IMAGE_SELECTION_GLOW_PAD,
|
|
950
|
+
20 + (IMAGE_SELECTION_GLOW_PAD * 0.5)
|
|
951
|
+
)
|
|
952
|
+
);
|
|
947
953
|
}
|
|
948
954
|
|
|
949
|
-
outline.geometry?.dispose?.();
|
|
950
|
-
outline.geometry = createRoundedRectFilledGeometry(
|
|
951
|
-
width + IMAGE_SELECTION_GLOW_PAD,
|
|
952
|
-
height + IMAGE_SELECTION_GLOW_PAD,
|
|
953
|
-
20 + (IMAGE_SELECTION_GLOW_PAD * 0.5)
|
|
954
|
-
);
|
|
955
955
|
outline.position.set(-(IMAGE_SELECTION_GLOW_PAD / 2), IMAGE_SELECTION_GLOW_PAD / 2, 0);
|
|
956
956
|
outline.renderOrder = Math.max(0, (baseRenderOrder || 1) - 1);
|
|
957
957
|
outline.visible = !!isSelected;
|
|
@@ -3487,6 +3487,66 @@
|
|
|
3487
3487
|
window.MindMapTextOverlayV2?.invalidateView?.(module);
|
|
3488
3488
|
}
|
|
3489
3489
|
|
|
3490
|
+
function applyNodeGlRenderOrder(module, entry, targetBaseOrder) {
|
|
3491
|
+
if (!entry?.glObject) return;
|
|
3492
|
+
const renderables = [];
|
|
3493
|
+
let minOrder = Infinity;
|
|
3494
|
+
let maxOrder = -Infinity;
|
|
3495
|
+
entry.glObject.traverse(child => {
|
|
3496
|
+
if (!child.isMesh && !child.isLine) return;
|
|
3497
|
+
const order = Number(child.renderOrder || 0);
|
|
3498
|
+
renderables.push(child);
|
|
3499
|
+
if (order < minOrder) minOrder = order;
|
|
3500
|
+
if (order > maxOrder) maxOrder = order;
|
|
3501
|
+
});
|
|
3502
|
+
if (!Number.isFinite(minOrder)) return;
|
|
3503
|
+
const delta = Number(targetBaseOrder || 0) - minOrder;
|
|
3504
|
+
for (let i = 0; i < renderables.length; i++) {
|
|
3505
|
+
renderables[i].renderOrder = Number(renderables[i].renderOrder || 0) + delta;
|
|
3506
|
+
}
|
|
3507
|
+
module.nodeZCounter = Math.max(Number(module.nodeZCounter || 0), maxOrder + delta + 1);
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
function flushPendingGlRenderOrder(module, entry) {
|
|
3511
|
+
if (!module || !entry || !Number.isFinite(entry._pendingGlRenderOrderBase)) {
|
|
3512
|
+
return false;
|
|
3513
|
+
}
|
|
3514
|
+
applyNodeGlRenderOrder(module, entry, entry._pendingGlRenderOrderBase);
|
|
3515
|
+
delete entry._pendingGlRenderOrderBase;
|
|
3516
|
+
entry._pendingGlRenderOrderScheduled = false;
|
|
3517
|
+
return true;
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
function schedulePendingGlRenderOrder(module, nodeId, entry, attempt = 0) {
|
|
3521
|
+
if (!module || !entry || entry._pendingGlRenderOrderScheduled === true) return;
|
|
3522
|
+
entry._pendingGlRenderOrderScheduled = true;
|
|
3523
|
+
const run = () => {
|
|
3524
|
+
entry._pendingGlRenderOrderScheduled = false;
|
|
3525
|
+
if (module.nodeObjectsById?.get?.(nodeId) !== entry ||
|
|
3526
|
+
!Number.isFinite(entry._pendingGlRenderOrderBase)) {
|
|
3527
|
+
return;
|
|
3528
|
+
}
|
|
3529
|
+
const isInteracting =
|
|
3530
|
+
module.isPanning === true ||
|
|
3531
|
+
module.isZooming === true ||
|
|
3532
|
+
module.isDraggingNode === true ||
|
|
3533
|
+
module.isDraggingMultipleNodes === true ||
|
|
3534
|
+
module.isSelecting === true;
|
|
3535
|
+
if (isInteracting && attempt < 12) {
|
|
3536
|
+
schedulePendingGlRenderOrder(module, nodeId, entry, attempt + 1);
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
if (!isInteracting) {
|
|
3540
|
+
flushPendingGlRenderOrder(module, entry);
|
|
3541
|
+
}
|
|
3542
|
+
};
|
|
3543
|
+
if (typeof requestIdleCallback === 'function') {
|
|
3544
|
+
requestIdleCallback(run, { timeout: 180 });
|
|
3545
|
+
} else {
|
|
3546
|
+
setTimeout(run, 32);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3490
3550
|
function bringNodeToFront(module, nodeId) {
|
|
3491
3551
|
if (!module || !nodeId) return;
|
|
3492
3552
|
|
|
@@ -3495,35 +3555,15 @@
|
|
|
3495
3555
|
const contentTypeLower = getNodeContentTypeLower(entry.model);
|
|
3496
3556
|
const usesSafeMediaSelectionChrome = contentTypeLower === 'video' || contentTypeLower === 'embed';
|
|
3497
3557
|
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
entry.
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
if (order > maxOrder) maxOrder = order;
|
|
3508
|
-
}
|
|
3509
|
-
});
|
|
3510
|
-
|
|
3511
|
-
if (!Number.isFinite(minOrder)) minOrder = 0;
|
|
3512
|
-
if (!Number.isFinite(maxOrder)) maxOrder = minOrder;
|
|
3513
|
-
|
|
3514
|
-
const targetBaseOrder = module.nodeZCounter++;
|
|
3515
|
-
const delta = targetBaseOrder - minOrder;
|
|
3516
|
-
|
|
3517
|
-
entry.glObject.traverse(child => {
|
|
3518
|
-
if (child.isMesh || child.isLine) {
|
|
3519
|
-
child.renderOrder = (child.renderOrder || 0) + delta;
|
|
3520
|
-
}
|
|
3521
|
-
});
|
|
3522
|
-
|
|
3523
|
-
const nextTop = maxOrder + delta + 1;
|
|
3524
|
-
if (nextTop > module.nodeZCounter) {
|
|
3525
|
-
module.nodeZCounter = nextTop;
|
|
3526
|
-
}
|
|
3558
|
+
const targetBaseOrder = module.nodeZCounter++;
|
|
3559
|
+
const deferGlOrder = entry.currentType === 'CSS' &&
|
|
3560
|
+
entry.cssObject?.visible === true &&
|
|
3561
|
+
Number(module.camera?.position?.z || 0) < 9000;
|
|
3562
|
+
if (deferGlOrder) {
|
|
3563
|
+
entry._pendingGlRenderOrderBase = targetBaseOrder;
|
|
3564
|
+
schedulePendingGlRenderOrder(module, nodeId, entry);
|
|
3565
|
+
} else {
|
|
3566
|
+
applyNodeGlRenderOrder(module, entry, targetBaseOrder);
|
|
3527
3567
|
}
|
|
3528
3568
|
|
|
3529
3569
|
// CSS3DObject re-parenting can trigger a one-frame detach flicker.
|
|
@@ -4226,30 +4266,10 @@
|
|
|
4226
4266
|
}
|
|
4227
4267
|
// ▲▲▲ [Optimization] ▲▲▲
|
|
4228
4268
|
|
|
4229
|
-
//
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
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
|
-
}
|
|
4269
|
+
// CSS3D text selection ownership is updated as a previous/next delta.
|
|
4270
|
+
// The manager patches only those two wrappers instead of walking the
|
|
4271
|
+
// complete node map on every CSS3D promotion.
|
|
4272
|
+
window.MindMapCss3DManager?.setNativeTextSelectionOwnerClass?.(module, nodeId);
|
|
4253
4273
|
// ▲▲▲ [Fix] ▲▲▲
|
|
4254
4274
|
|
|
4255
4275
|
// Sync CSS3D scroll position with WebGL/model scrollOffset (ratio-based).
|
|
@@ -4315,6 +4335,7 @@
|
|
|
4315
4335
|
// ▲▲▲ [Changed] ▲▲▲
|
|
4316
4336
|
|
|
4317
4337
|
if (nodeEntry.currentType === 'GL') {
|
|
4338
|
+
flushPendingGlRenderOrder(module, nodeEntry);
|
|
4318
4339
|
log(`[MindMapNodes] SwitchToWebGL(${nodeId}) SKIPPED (Already in GL mode).`);
|
|
4319
4340
|
return;
|
|
4320
4341
|
}
|
|
@@ -4324,6 +4345,7 @@
|
|
|
4324
4345
|
if (nodeEntry.isDeferredShell === true) {
|
|
4325
4346
|
await ensureDeferredNodeMaterialized(module, nodeEntry, { attachToScene: true });
|
|
4326
4347
|
}
|
|
4348
|
+
flushPendingGlRenderOrder(module, nodeEntry);
|
|
4327
4349
|
|
|
4328
4350
|
// Store CSS3D textarea scroll ratio into WebGL scrollOffset
|
|
4329
4351
|
const textarea = nodeEntry.cssObject.element.querySelector('textarea');
|
|
@@ -146,11 +146,14 @@
|
|
|
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;
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
const renderQueueSet = new Set();
|
|
150
|
+
let renderQueueScheduled = false;
|
|
151
|
+
let renderModuleRef = null;
|
|
152
|
+
let renderQueueDirty = false;
|
|
153
|
+
const RENDER_BUDGET_MS = 8;
|
|
154
|
+
let renderSeq = 0;
|
|
155
|
+
let renderQueueSortCount = 0;
|
|
156
|
+
let renderQueueMaxLength = 0;
|
|
154
157
|
|
|
155
158
|
function computeRenderPriority(module, nodeId) {
|
|
156
159
|
const entry = module?.nodeObjectsById?.get(nodeId);
|
|
@@ -178,9 +181,11 @@
|
|
|
178
181
|
function enqueueRender(module, nodeId) {
|
|
179
182
|
if (!nodeId) return;
|
|
180
183
|
renderModuleRef = module;
|
|
181
|
-
if (!renderQueueSet.has(nodeId)) {
|
|
182
|
-
renderQueue.push({ nodeId, priority: computeRenderPriority(module, nodeId), seq: renderSeq++ });
|
|
183
|
-
renderQueueSet.add(nodeId);
|
|
184
|
+
if (!renderQueueSet.has(nodeId)) {
|
|
185
|
+
renderQueue.push({ nodeId, priority: computeRenderPriority(module, nodeId), seq: renderSeq++ });
|
|
186
|
+
renderQueueSet.add(nodeId);
|
|
187
|
+
renderQueueDirty = true;
|
|
188
|
+
renderQueueMaxLength = Math.max(renderQueueMaxLength, renderQueue.length);
|
|
184
189
|
}
|
|
185
190
|
scheduleRenderQueue();
|
|
186
191
|
}
|
|
@@ -195,12 +200,16 @@
|
|
|
195
200
|
const start = performance.now();
|
|
196
201
|
|
|
197
202
|
// 우선순위 정렬 (가시/선택/거리)
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
if (renderQueueDirty) {
|
|
204
|
+
if (renderQueue.length > 1) {
|
|
205
|
+
renderQueue.sort((a, b) => (b.priority - a.priority) || (b.seq - a.seq));
|
|
206
|
+
renderQueueSortCount++;
|
|
207
|
+
}
|
|
208
|
+
renderQueueDirty = false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
while (renderQueue.length > 0) {
|
|
212
|
+
const item = renderQueue.pop();
|
|
204
213
|
const nodeId = item.nodeId;
|
|
205
214
|
renderQueueSet.delete(nodeId);
|
|
206
215
|
if (renderModuleRef) {
|
|
@@ -224,7 +233,8 @@
|
|
|
224
233
|
// ▲▲▲ [New] ▲▲▲
|
|
225
234
|
|
|
226
235
|
// ▼▼▼ [Perf] Deferred disposal to avoid GC/driver spikes ▼▼▼
|
|
227
|
-
const disposeQueue = [];
|
|
236
|
+
const disposeQueue = [];
|
|
237
|
+
let disposeQueueHead = 0;
|
|
228
238
|
let disposeScheduled = false;
|
|
229
239
|
const DISPOSE_BUDGET_MS = 6; // soft budget per frame
|
|
230
240
|
|
|
@@ -246,8 +256,8 @@
|
|
|
246
256
|
|
|
247
257
|
function processDisposeQueue(deadline) {
|
|
248
258
|
const start = performance.now();
|
|
249
|
-
while (disposeQueue.length
|
|
250
|
-
const item = disposeQueue
|
|
259
|
+
while (disposeQueueHead < disposeQueue.length) {
|
|
260
|
+
const item = disposeQueue[disposeQueueHead++];
|
|
251
261
|
try { item.dispose(); } catch { }
|
|
252
262
|
|
|
253
263
|
if (deadline && typeof deadline.timeRemaining === 'function') {
|
|
@@ -258,9 +268,16 @@
|
|
|
258
268
|
}
|
|
259
269
|
|
|
260
270
|
disposeScheduled = false;
|
|
261
|
-
if (disposeQueue.length
|
|
262
|
-
|
|
263
|
-
|
|
271
|
+
if (disposeQueueHead >= disposeQueue.length) {
|
|
272
|
+
disposeQueue.length = 0;
|
|
273
|
+
disposeQueueHead = 0;
|
|
274
|
+
} else if (disposeQueue.length - disposeQueueHead > 64) {
|
|
275
|
+
disposeQueue.splice(0, disposeQueueHead);
|
|
276
|
+
disposeQueueHead = 0;
|
|
277
|
+
}
|
|
278
|
+
if (disposeQueueHead < disposeQueue.length) {
|
|
279
|
+
scheduleDispose();
|
|
280
|
+
}
|
|
264
281
|
}
|
|
265
282
|
// ▲▲▲ [Perf] ▲▲▲
|
|
266
283
|
|
|
@@ -335,6 +352,7 @@
|
|
|
335
352
|
if (!group) return null;
|
|
336
353
|
|
|
337
354
|
let outline = group.getObjectByName('outline');
|
|
355
|
+
const geometryKey = `${Math.round(width)}x${Math.round(height)}:${IMAGE_SELECTION_GLOW_PAD}`;
|
|
338
356
|
if (!outline) {
|
|
339
357
|
outline = new THREE.Mesh(
|
|
340
358
|
createRoundedRectFilledGeometry(
|
|
@@ -351,16 +369,24 @@
|
|
|
351
369
|
})
|
|
352
370
|
);
|
|
353
371
|
outline.name = 'outline';
|
|
354
|
-
outline.userData = {
|
|
372
|
+
outline.userData = {
|
|
373
|
+
...(outline.userData || {}),
|
|
374
|
+
isImageSelectionGlow: true,
|
|
375
|
+
geometrySizeKey: geometryKey
|
|
376
|
+
};
|
|
355
377
|
group.add(outline);
|
|
378
|
+
} else {
|
|
379
|
+
window.MindMapCss3dHotPath?.replaceGeometryIfSizeChanged?.(
|
|
380
|
+
outline,
|
|
381
|
+
geometryKey,
|
|
382
|
+
() => createRoundedRectFilledGeometry(
|
|
383
|
+
width + IMAGE_SELECTION_GLOW_PAD,
|
|
384
|
+
height + IMAGE_SELECTION_GLOW_PAD,
|
|
385
|
+
20 + (IMAGE_SELECTION_GLOW_PAD * 0.5)
|
|
386
|
+
)
|
|
387
|
+
);
|
|
356
388
|
}
|
|
357
389
|
|
|
358
|
-
outline.geometry?.dispose?.();
|
|
359
|
-
outline.geometry = createRoundedRectFilledGeometry(
|
|
360
|
-
width + IMAGE_SELECTION_GLOW_PAD,
|
|
361
|
-
height + IMAGE_SELECTION_GLOW_PAD,
|
|
362
|
-
20 + (IMAGE_SELECTION_GLOW_PAD * 0.5)
|
|
363
|
-
);
|
|
364
390
|
outline.position.set(-(IMAGE_SELECTION_GLOW_PAD / 2), IMAGE_SELECTION_GLOW_PAD / 2, 0);
|
|
365
391
|
outline.renderOrder = baseRenderOrder - 1;
|
|
366
392
|
outline.visible = !!isSelected;
|
|
@@ -2186,7 +2212,7 @@
|
|
|
2186
2212
|
}
|
|
2187
2213
|
|
|
2188
2214
|
// Expose MindMapPipeline globally
|
|
2189
|
-
window.MindMapPipeline = {
|
|
2215
|
+
window.MindMapPipeline = {
|
|
2190
2216
|
processRenderQueue: processRenderQueue,
|
|
2191
2217
|
debouncedProcessRenderQueue: debouncedProcessRenderQueue,
|
|
2192
2218
|
throttledScrollUpdate: throttledScrollUpdate,
|
|
@@ -2194,8 +2220,18 @@
|
|
|
2194
2220
|
throttledProcessRenderQueueDuringResize: throttledProcessRenderQueueDuringResize,
|
|
2195
2221
|
startResizing: startResizing,
|
|
2196
2222
|
doResizing: doResizing,
|
|
2197
|
-
stopResizing: stopResizing,
|
|
2198
|
-
|
|
2223
|
+
stopResizing: stopResizing,
|
|
2224
|
+
getQueueDebugMetrics: () => ({
|
|
2225
|
+
sortCount: renderQueueSortCount,
|
|
2226
|
+
maxLength: renderQueueMaxLength,
|
|
2227
|
+
currentLength: renderQueue.length,
|
|
2228
|
+
disposePending: Math.max(0, disposeQueue.length - disposeQueueHead)
|
|
2229
|
+
}),
|
|
2230
|
+
resetQueueDebugMetrics: () => {
|
|
2231
|
+
renderQueueSortCount = 0;
|
|
2232
|
+
renderQueueMaxLength = renderQueue.length;
|
|
2233
|
+
},
|
|
2234
|
+
throttledUpdateScrollInCSharp: throttledUpdateScrollInCSharp
|
|
2199
2235
|
};
|
|
2200
2236
|
|
|
2201
2237
|
console.log('? mind-map-pipeline.js loaded');
|