@mindexec/cli 0.2.309 → 0.2.311
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 +1 -1
- package/server.js +129 -0
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +14 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +3 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +345 -26
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +1 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js +92 -4
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +99 -1
- package/wwwroot/_framework/{MindExecution.Shared.rv3rw2h14g.dll → MindExecution.Shared.ojp3psugsm.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +3 -3
- package/wwwroot/index.html +3 -3
- package/wwwroot/service-worker-assets.js +11 -11
- package/wwwroot/service-worker.js +1 -1
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -1871,6 +1871,135 @@ async function getRegisteredLocalFile(id) {
|
|
|
1871
1871
|
return normalizedId ? localFileRegistry.get(normalizedId) || null : null;
|
|
1872
1872
|
}
|
|
1873
1873
|
|
|
1874
|
+
function decodeAssetRequestPath(rawPath) {
|
|
1875
|
+
let value = String(rawPath || '').trim();
|
|
1876
|
+
if (!value) {
|
|
1877
|
+
return { requestPath: '', isThumbs: false };
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
try {
|
|
1881
|
+
if (/^https?:\/\//i.test(value)) {
|
|
1882
|
+
value = new URL(value).pathname;
|
|
1883
|
+
}
|
|
1884
|
+
} catch {
|
|
1885
|
+
// Keep the original value and let the path safety checks reject it if needed.
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
value = value.split(/[?#]/)[0].replaceAll('\\', '/');
|
|
1889
|
+
const lower = value.toLowerCase();
|
|
1890
|
+
const thumbsMarker = '/assets/thumbs/';
|
|
1891
|
+
const assetsMarker = '/assets/';
|
|
1892
|
+
let isThumbs = false;
|
|
1893
|
+
const thumbsIndex = lower.indexOf(thumbsMarker);
|
|
1894
|
+
const assetsIndex = lower.indexOf(assetsMarker);
|
|
1895
|
+
|
|
1896
|
+
if (thumbsIndex >= 0) {
|
|
1897
|
+
value = value.slice(thumbsIndex + thumbsMarker.length);
|
|
1898
|
+
isThumbs = true;
|
|
1899
|
+
} else if (assetsIndex >= 0) {
|
|
1900
|
+
value = value.slice(assetsIndex + assetsMarker.length);
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
try {
|
|
1904
|
+
value = decodeURIComponent(value);
|
|
1905
|
+
} catch {
|
|
1906
|
+
// A malformed escape sequence should not crash the bridge.
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
return {
|
|
1910
|
+
requestPath: value.replace(/^\/+/, ''),
|
|
1911
|
+
isThumbs
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
function encodeAssetUrlPath(relativePath) {
|
|
1916
|
+
return normalizePathForClient(relativePath)
|
|
1917
|
+
.replace(/^\/+/, '')
|
|
1918
|
+
.split('/')
|
|
1919
|
+
.filter(Boolean)
|
|
1920
|
+
.map(segment => encodeURIComponent(segment))
|
|
1921
|
+
.join('/');
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
async function resolveAssetPathForRequest(normalizedRequestPath, options = {}) {
|
|
1925
|
+
const requestPath = String(normalizedRequestPath || '').replace(/^\/+/, '');
|
|
1926
|
+
if (!requestPath) {
|
|
1927
|
+
return { status: 'missing', resolvedPath: null };
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
const isThumbs = options.thumbs === true;
|
|
1931
|
+
const primaryAssetsDir = getAssetsPath();
|
|
1932
|
+
const legacyAssetsDir = path.join(workspacePath, 'assets');
|
|
1933
|
+
const primaryDir = isThumbs ? path.join(primaryAssetsDir, 'thumbs') : primaryAssetsDir;
|
|
1934
|
+
const legacyDir = isThumbs ? path.join(legacyAssetsDir, 'thumbs') : legacyAssetsDir;
|
|
1935
|
+
const primaryPath = path.resolve(primaryDir, requestPath);
|
|
1936
|
+
const legacyPath = path.resolve(legacyDir, requestPath);
|
|
1937
|
+
|
|
1938
|
+
const candidates = [];
|
|
1939
|
+
if (isPathWithin(primaryDir, primaryPath)) {
|
|
1940
|
+
candidates.push(primaryPath);
|
|
1941
|
+
}
|
|
1942
|
+
if (isPathWithin(legacyDir, legacyPath)) {
|
|
1943
|
+
candidates.push(legacyPath);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
if (candidates.length === 0) {
|
|
1947
|
+
return { status: 'forbidden', resolvedPath: null };
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
let resolvedPath = await findFirstAccessiblePath(candidates);
|
|
1951
|
+
if (!resolvedPath) {
|
|
1952
|
+
resolvedPath = await findFileByStem(
|
|
1953
|
+
[primaryDir, legacyDir],
|
|
1954
|
+
requestPath,
|
|
1955
|
+
isThumbs
|
|
1956
|
+
? ['.jpg', '.jpeg', '.webp', '.png']
|
|
1957
|
+
: ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp', '.svg', '.mp4', '.webm', '.mov']
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
if (!resolvedPath && isThumbs) {
|
|
1962
|
+
resolvedPath = await findFileByStem(
|
|
1963
|
+
[primaryAssetsDir, legacyAssetsDir],
|
|
1964
|
+
requestPath,
|
|
1965
|
+
['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp']
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
return {
|
|
1970
|
+
status: resolvedPath ? 'found' : 'missing',
|
|
1971
|
+
resolvedPath
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
app.get('/api/assets/resolve', async (req, res) => {
|
|
1976
|
+
const decoded = decodeAssetRequestPath(req.query?.path || req.query?.url || '');
|
|
1977
|
+
if (!decoded.requestPath) {
|
|
1978
|
+
return res.json({
|
|
1979
|
+
exists: false,
|
|
1980
|
+
reason: 'empty-path'
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
const result = await resolveAssetPathForRequest(decoded.requestPath, {
|
|
1985
|
+
thumbs: decoded.isThumbs
|
|
1986
|
+
});
|
|
1987
|
+
|
|
1988
|
+
if (result.status === 'forbidden') {
|
|
1989
|
+
return res.status(403).json({
|
|
1990
|
+
exists: false,
|
|
1991
|
+
reason: 'forbidden'
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
const urlPath = encodeAssetUrlPath(decoded.requestPath);
|
|
1996
|
+
return res.json({
|
|
1997
|
+
exists: result.status === 'found',
|
|
1998
|
+
url: `/${decoded.isThumbs ? 'assets/thumbs' : 'assets'}/${urlPath}`,
|
|
1999
|
+
path: decoded.requestPath
|
|
2000
|
+
});
|
|
2001
|
+
});
|
|
2002
|
+
|
|
1874
2003
|
// Static file serving for assets (direct image loading - bypasses base64 encoding)
|
|
1875
2004
|
// This allows browsers to directly fetch images via http://127.0.0.1:5147/assets/filename.png
|
|
1876
2005
|
app.use('/assets', async (req, res, next) => {
|
|
@@ -2061,6 +2061,20 @@ html.mindcanvas-platform-apple .css3d-resolution-wrapper.is-automation-relation-
|
|
|
2061
2061
|
caret-color: transparent !important;
|
|
2062
2062
|
}
|
|
2063
2063
|
|
|
2064
|
+
.css3d-resolution-wrapper.mind-map-v2-source-suspended:not(.node-type-image):not(.node-type-video):not(.node-type-embed),
|
|
2065
|
+
.css3d-resolution-wrapper.mind-map-v2-source-suspended:not(.node-type-image):not(.node-type-video):not(.node-type-embed) > .map-node,
|
|
2066
|
+
.css3d-resolution-wrapper.mind-map-v2-source-suspended:not(.node-type-image):not(.node-type-video):not(.node-type-embed) > .map-node-bubble,
|
|
2067
|
+
.css3d-resolution-wrapper.mind-map-v2-source-suspended:not(.node-type-image):not(.node-type-video):not(.node-type-embed) > .map-node-note,
|
|
2068
|
+
.css3d-resolution-wrapper.mind-map-v2-source-suspended:not(.node-type-image):not(.node-type-video):not(.node-type-embed) > .map-node-code,
|
|
2069
|
+
.css3d-resolution-wrapper.mind-map-v2-source-suspended:not(.node-type-image):not(.node-type-video):not(.node-type-embed) > .map-node-memo:not(.map-node-agent):not(.map-node-automation) {
|
|
2070
|
+
background: transparent !important;
|
|
2071
|
+
background-color: transparent !important;
|
|
2072
|
+
background-image: none !important;
|
|
2073
|
+
border-color: transparent !important;
|
|
2074
|
+
outline-color: transparent !important;
|
|
2075
|
+
box-shadow: none !important;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2064
2078
|
.css3d-resolution-wrapper.node-type-image .mind-map-v2-source-suspended,
|
|
2065
2079
|
.css3d-resolution-wrapper.node-type-video .mind-map-v2-source-suspended,
|
|
2066
2080
|
.css3d-resolution-wrapper.node-type-embed .mind-map-v2-source-suspended,
|
|
@@ -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-
|
|
8
|
+
const MINDMAP_CORE_BUILD_ID = '20260621-nodeview-host-policy-v794';
|
|
9
9
|
const CanvasPhase = Object.freeze({
|
|
10
10
|
Booting: 'booting',
|
|
11
11
|
BoardFileLoading: 'board-file-loading',
|
|
@@ -1131,6 +1131,8 @@
|
|
|
1131
1131
|
enableDomOverlay: true,
|
|
1132
1132
|
enablePassiveOverlayForVisibleNodes: true,
|
|
1133
1133
|
enableOverlayCssZoom: true,
|
|
1134
|
+
enableSharedNodeViewOverlay: true,
|
|
1135
|
+
preferOverlayNodeViewHost: false,
|
|
1134
1136
|
enableVisibilityCulling: true,
|
|
1135
1137
|
enableLodUpdate: true,
|
|
1136
1138
|
enableWebglRender: true,
|
|
@@ -27,14 +27,22 @@
|
|
|
27
27
|
let _cssImagePromotionTimer = 0;
|
|
28
28
|
let _cssImageBlobUrlCache = new Map();
|
|
29
29
|
let _cssImageBlobUrlPending = new Map();
|
|
30
|
+
let _cssImageMissingLoopbackUrls = new Set();
|
|
30
31
|
let _cssVideoBlobUrlCache = new Map();
|
|
31
32
|
let _cssVideoBlobUrlPending = new Map();
|
|
33
|
+
let _cssVideoMissingLoopbackUrls = new Set();
|
|
34
|
+
let _cssLoopbackAssetResolveCache = new Map();
|
|
35
|
+
let _cssLoopbackAssetResolvePending = new Map();
|
|
32
36
|
let _cssImageDisplayQueue = [];
|
|
33
37
|
let _cssImageDisplayActiveCount = 0;
|
|
34
38
|
let _cssImageDisplayPumpScheduled = false;
|
|
35
39
|
const CSS_VIDEO_PROXY_MAX_DEVICE_PIXEL_RATIO = 2;
|
|
36
40
|
const VIDEO_PLAYBACK_INTENT_METADATA_KEY = 'VideoPlaybackIntent';
|
|
37
41
|
const CSV_TABLE_CONTENT_TYPE = 'csv-table';
|
|
42
|
+
const NODE_VIEW_HOST_CSS3D = 'css3d';
|
|
43
|
+
const NODE_VIEW_HOST_OVERLAY = 'overlay';
|
|
44
|
+
const NODE_VIEW_HOST_HYBRID = 'hybrid';
|
|
45
|
+
const NODE_VIEW_RENDERER_VERSION = 'shared-node-view-v1';
|
|
38
46
|
const CSV_TABLE_MAX_RENDER_ROWS = 500;
|
|
39
47
|
const CSV_TABLE_MAX_RENDER_COLUMNS = 80;
|
|
40
48
|
const REMOTE_FLEET_DISPLAY_NAME = 'Multi Desktop Monitor';
|
|
@@ -1653,6 +1661,92 @@
|
|
|
1653
1661
|
return LOOPBACK_ASSET_URL_REGEX.test(String(url || '').trim());
|
|
1654
1662
|
}
|
|
1655
1663
|
|
|
1664
|
+
function getCssLoopbackAssetResolveUrl(targetUrl) {
|
|
1665
|
+
const normalized = normalizeCssMediaUrl(targetUrl);
|
|
1666
|
+
if (!normalized || !isLoopbackCssAssetUrl(normalized)) {
|
|
1667
|
+
return '';
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
try {
|
|
1671
|
+
const currentOrigin = window?.location?.origin || '';
|
|
1672
|
+
if (!currentOrigin) {
|
|
1673
|
+
return '';
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
const target = new URL(normalized, window?.location?.href || currentOrigin);
|
|
1677
|
+
return `${currentOrigin}/api/assets/resolve?path=${encodeURIComponent(target.pathname)}`;
|
|
1678
|
+
} catch {
|
|
1679
|
+
return '';
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
async function resolveLoopbackCssAssetUrl(targetUrl, mediaKind = 'asset') {
|
|
1684
|
+
const normalized = normalizeCssMediaUrl(targetUrl);
|
|
1685
|
+
if (!normalized || !isLoopbackCssAssetUrl(normalized)) {
|
|
1686
|
+
return normalized;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
const cached = _cssLoopbackAssetResolveCache.get(normalized);
|
|
1690
|
+
if (cached) {
|
|
1691
|
+
if (cached.exists === false) {
|
|
1692
|
+
throw new Error(`Loopback ${mediaKind} asset was already reported missing`);
|
|
1693
|
+
}
|
|
1694
|
+
return normalizeCssMediaUrl(cached.url || normalized) || normalized;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
const existingPending = _cssLoopbackAssetResolvePending.get(normalized);
|
|
1698
|
+
if (existingPending) {
|
|
1699
|
+
return await existingPending;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
const resolveUrl = getCssLoopbackAssetResolveUrl(normalized);
|
|
1703
|
+
if (!resolveUrl) {
|
|
1704
|
+
return normalized;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
const pending = (async () => {
|
|
1708
|
+
const response = await fetch(resolveUrl, {
|
|
1709
|
+
cache: 'no-store',
|
|
1710
|
+
targetAddressSpace: 'loopback'
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
if (response.status === 404) {
|
|
1714
|
+
// Older LocalBridge builds do not expose the resolver. Keep the
|
|
1715
|
+
// previous direct asset path so existing installations still work.
|
|
1716
|
+
return normalized;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
if (!response.ok) {
|
|
1720
|
+
throw new Error(`Asset resolve HTTP ${response.status}`);
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
const payload = await response.json().catch(() => null);
|
|
1724
|
+
if (!payload || payload.exists !== true) {
|
|
1725
|
+
_cssLoopbackAssetResolveCache.set(normalized, {
|
|
1726
|
+
exists: false,
|
|
1727
|
+
url: normalized
|
|
1728
|
+
});
|
|
1729
|
+
throw new Error(`Loopback ${mediaKind} asset is missing`);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
const resolvedUrl = normalizeCssMediaUrl(payload.url || normalized) || normalized;
|
|
1733
|
+
_cssLoopbackAssetResolveCache.set(normalized, {
|
|
1734
|
+
exists: true,
|
|
1735
|
+
url: resolvedUrl
|
|
1736
|
+
});
|
|
1737
|
+
return resolvedUrl;
|
|
1738
|
+
})();
|
|
1739
|
+
|
|
1740
|
+
_cssLoopbackAssetResolvePending.set(normalized, pending);
|
|
1741
|
+
try {
|
|
1742
|
+
return await pending;
|
|
1743
|
+
} finally {
|
|
1744
|
+
if (_cssLoopbackAssetResolvePending.get(normalized) === pending) {
|
|
1745
|
+
_cssLoopbackAssetResolvePending.delete(normalized);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1656
1750
|
function shouldAttachCssMediaAuthorization(targetUrl, authToken) {
|
|
1657
1751
|
const token = typeof authToken === 'string' ? authToken.trim() : '';
|
|
1658
1752
|
if (!token) {
|
|
@@ -3565,6 +3659,10 @@
|
|
|
3565
3659
|
return normalized;
|
|
3566
3660
|
}
|
|
3567
3661
|
|
|
3662
|
+
if (_cssImageMissingLoopbackUrls.has(normalized)) {
|
|
3663
|
+
throw new Error('Loopback image asset was already reported missing');
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3568
3666
|
const cached = _cssImageBlobUrlCache.get(normalized);
|
|
3569
3667
|
if (cached?.objectUrl) {
|
|
3570
3668
|
touchCssImageBlobCacheEntry(normalized, cached);
|
|
@@ -3577,8 +3675,19 @@
|
|
|
3577
3675
|
}
|
|
3578
3676
|
|
|
3579
3677
|
const pendingFetch = (async () => {
|
|
3580
|
-
|
|
3678
|
+
let resolvedAssetUrl = normalized;
|
|
3679
|
+
try {
|
|
3680
|
+
resolvedAssetUrl = await resolveLoopbackCssAssetUrl(normalized, 'image');
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
_cssImageMissingLoopbackUrls.add(normalized);
|
|
3683
|
+
throw error;
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
const response = await fetch(resolvedAssetUrl, getCssMediaFetchOptions(resolvedAssetUrl));
|
|
3581
3687
|
if (!response.ok) {
|
|
3688
|
+
if (response.status === 404) {
|
|
3689
|
+
_cssImageMissingLoopbackUrls.add(normalized);
|
|
3690
|
+
}
|
|
3582
3691
|
throw new Error(`HTTP ${response.status}`);
|
|
3583
3692
|
}
|
|
3584
3693
|
|
|
@@ -3622,6 +3731,10 @@
|
|
|
3622
3731
|
return normalized;
|
|
3623
3732
|
}
|
|
3624
3733
|
|
|
3734
|
+
if (_cssVideoMissingLoopbackUrls.has(normalized)) {
|
|
3735
|
+
throw new Error('Loopback video asset was already reported missing');
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3625
3738
|
const cached = _cssVideoBlobUrlCache.get(normalized);
|
|
3626
3739
|
if (cached?.objectUrl) {
|
|
3627
3740
|
touchCssVideoBlobCacheEntry(normalized, cached);
|
|
@@ -3634,8 +3747,19 @@
|
|
|
3634
3747
|
}
|
|
3635
3748
|
|
|
3636
3749
|
const pendingFetch = (async () => {
|
|
3637
|
-
|
|
3750
|
+
let resolvedAssetUrl = normalized;
|
|
3751
|
+
try {
|
|
3752
|
+
resolvedAssetUrl = await resolveLoopbackCssAssetUrl(normalized, 'video');
|
|
3753
|
+
} catch (error) {
|
|
3754
|
+
_cssVideoMissingLoopbackUrls.add(normalized);
|
|
3755
|
+
throw error;
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
const response = await fetch(resolvedAssetUrl, getCssMediaFetchOptions(resolvedAssetUrl));
|
|
3638
3759
|
if (!response.ok) {
|
|
3760
|
+
if (response.status === 404) {
|
|
3761
|
+
_cssVideoMissingLoopbackUrls.add(normalized);
|
|
3762
|
+
}
|
|
3639
3763
|
throw new Error(`HTTP ${response.status}`);
|
|
3640
3764
|
}
|
|
3641
3765
|
|
|
@@ -3726,6 +3850,16 @@
|
|
|
3726
3850
|
return false;
|
|
3727
3851
|
}
|
|
3728
3852
|
|
|
3853
|
+
if (isLoopbackCssAssetUrl(normalized)) {
|
|
3854
|
+
mediaEl.dataset.resolvedMediaSourceUrl = '';
|
|
3855
|
+
mediaEl.removeAttribute('src');
|
|
3856
|
+
if (mediaEl.tagName === 'IMG') {
|
|
3857
|
+
mediaEl.dataset.mediaReady = '0';
|
|
3858
|
+
mediaEl.style.opacity = '0';
|
|
3859
|
+
}
|
|
3860
|
+
return false;
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3729
3863
|
const currentDisplaySource = String(mediaEl.getAttribute('src') || '').trim();
|
|
3730
3864
|
if (currentDisplaySource !== normalized) {
|
|
3731
3865
|
mediaEl.setAttribute('src', normalized);
|
|
@@ -23061,6 +23195,183 @@
|
|
|
23061
23195
|
}
|
|
23062
23196
|
// ▲▲▲ [New] ▲▲▲
|
|
23063
23197
|
|
|
23198
|
+
function getNodeViewContentInfo(nodeModel) {
|
|
23199
|
+
const contentTypeLower = String(nodeModel?.contentType ?? nodeModel?.ContentType ?? '').toLowerCase();
|
|
23200
|
+
const remoteFleetMonitor = isRemoteFleetMonitorNode(nodeModel);
|
|
23201
|
+
const visualContentTypeLower = remoteFleetMonitor ? 'templatelauncher' : contentTypeLower;
|
|
23202
|
+
const isMediaNode = contentTypeLower === 'image' || contentTypeLower === 'video' || contentTypeLower === 'embed';
|
|
23203
|
+
|
|
23204
|
+
return {
|
|
23205
|
+
contentTypeLower,
|
|
23206
|
+
remoteFleetMonitor,
|
|
23207
|
+
visualContentTypeLower,
|
|
23208
|
+
isMediaNode
|
|
23209
|
+
};
|
|
23210
|
+
}
|
|
23211
|
+
|
|
23212
|
+
function shouldCreateDynamicNodeView(nodeModel, info = getNodeViewContentInfo(nodeModel)) {
|
|
23213
|
+
if (info.remoteFleetMonitor) {
|
|
23214
|
+
return true;
|
|
23215
|
+
}
|
|
23216
|
+
|
|
23217
|
+
const dynamicTypes = ['text', 'markdown', 'code', 'note', 'memo', CSV_TABLE_CONTENT_TYPE, 'templatelauncher', 'image', 'video', 'embed'];
|
|
23218
|
+
return dynamicTypes.includes(info.contentTypeLower);
|
|
23219
|
+
}
|
|
23220
|
+
|
|
23221
|
+
function shouldBypassNodeViewTemplate(nodeModel, info = getNodeViewContentInfo(nodeModel)) {
|
|
23222
|
+
if (info.remoteFleetMonitor) {
|
|
23223
|
+
return true;
|
|
23224
|
+
}
|
|
23225
|
+
|
|
23226
|
+
return info.contentTypeLower === 'text'
|
|
23227
|
+
|| info.contentTypeLower === 'markdown'
|
|
23228
|
+
|| info.contentTypeLower === 'note'
|
|
23229
|
+
|| info.contentTypeLower === 'memo'
|
|
23230
|
+
|| info.contentTypeLower === CSV_TABLE_CONTENT_TYPE
|
|
23231
|
+
|| info.contentTypeLower === 'templatelauncher'
|
|
23232
|
+
|| info.contentTypeLower === 'image'
|
|
23233
|
+
|| info.contentTypeLower === 'video'
|
|
23234
|
+
|| info.contentTypeLower === 'embed';
|
|
23235
|
+
}
|
|
23236
|
+
|
|
23237
|
+
function normalizeNodeViewHost(value, fallback = NODE_VIEW_HOST_CSS3D) {
|
|
23238
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
23239
|
+
if (normalized === NODE_VIEW_HOST_CSS3D ||
|
|
23240
|
+
normalized === NODE_VIEW_HOST_OVERLAY ||
|
|
23241
|
+
normalized === NODE_VIEW_HOST_HYBRID) {
|
|
23242
|
+
return normalized;
|
|
23243
|
+
}
|
|
23244
|
+
|
|
23245
|
+
return fallback;
|
|
23246
|
+
}
|
|
23247
|
+
|
|
23248
|
+
function getNodeViewPrimaryHost(module = _module) {
|
|
23249
|
+
const flags = module?.renderDebugFlags || {};
|
|
23250
|
+
const explicit = normalizeNodeViewHost(flags.nodeViewPrimaryHost, '');
|
|
23251
|
+
if (explicit) {
|
|
23252
|
+
return explicit;
|
|
23253
|
+
}
|
|
23254
|
+
|
|
23255
|
+
return flags.preferOverlayNodeViewHost === true
|
|
23256
|
+
? NODE_VIEW_HOST_OVERLAY
|
|
23257
|
+
: NODE_VIEW_HOST_CSS3D;
|
|
23258
|
+
}
|
|
23259
|
+
|
|
23260
|
+
function getNodeViewHostPolicy(module, nodeModel, options = {}) {
|
|
23261
|
+
const requestedHost = normalizeNodeViewHost(options.hostKind, NODE_VIEW_HOST_CSS3D);
|
|
23262
|
+
const primaryHost = getNodeViewPrimaryHost(module);
|
|
23263
|
+
const info = getNodeViewContentInfo(nodeModel);
|
|
23264
|
+
const overlayPrimary = primaryHost === NODE_VIEW_HOST_OVERLAY;
|
|
23265
|
+
const hybridPrimary = primaryHost === NODE_VIEW_HOST_HYBRID;
|
|
23266
|
+
|
|
23267
|
+
return {
|
|
23268
|
+
rendererVersion: NODE_VIEW_RENDERER_VERSION,
|
|
23269
|
+
requestedHost,
|
|
23270
|
+
primaryHost,
|
|
23271
|
+
effectiveHost: requestedHost,
|
|
23272
|
+
isCss3dPrimary: primaryHost === NODE_VIEW_HOST_CSS3D || hybridPrimary,
|
|
23273
|
+
isOverlayPrimary: overlayPrimary,
|
|
23274
|
+
isHybridPrimary: hybridPrimary,
|
|
23275
|
+
useSharedOverlay: module?.renderDebugFlags?.enableSharedNodeViewOverlay !== false,
|
|
23276
|
+
supportsNodeView: shouldCreateDynamicNodeView(nodeModel, info) || !shouldBypassNodeViewTemplate(nodeModel, info),
|
|
23277
|
+
contentTypeLower: info.contentTypeLower,
|
|
23278
|
+
visualContentTypeLower: info.visualContentTypeLower
|
|
23279
|
+
};
|
|
23280
|
+
}
|
|
23281
|
+
|
|
23282
|
+
function createNodeViewElement(module, nodeModel, options = {}) {
|
|
23283
|
+
if (module) {
|
|
23284
|
+
_module = module;
|
|
23285
|
+
}
|
|
23286
|
+
|
|
23287
|
+
const nodeId = getNodeId(nodeModel);
|
|
23288
|
+
if (!nodeId) {
|
|
23289
|
+
return null;
|
|
23290
|
+
}
|
|
23291
|
+
|
|
23292
|
+
const hostPolicy = getNodeViewHostPolicy(module, nodeModel, options);
|
|
23293
|
+
const hostKind = hostPolicy.effectiveHost;
|
|
23294
|
+
const info = getNodeViewContentInfo(nodeModel);
|
|
23295
|
+
const templateId = `node-${nodeId}`;
|
|
23296
|
+
let templateElement = document.getElementById(templateId);
|
|
23297
|
+
|
|
23298
|
+
if (shouldBypassNodeViewTemplate(nodeModel, info)) {
|
|
23299
|
+
templateElement = null;
|
|
23300
|
+
}
|
|
23301
|
+
|
|
23302
|
+
let isDynamicallyCreated = false;
|
|
23303
|
+
if (!templateElement) {
|
|
23304
|
+
if (shouldCreateDynamicNodeView(nodeModel, info)) {
|
|
23305
|
+
log(`[MindMapCss3DManager] Creating shared NodeView DOM for ${info.visualContentTypeLower || info.contentTypeLower} node ${nodeId} (${hostKind})`);
|
|
23306
|
+
templateElement = createDynamicNodeElement(nodeModel);
|
|
23307
|
+
isDynamicallyCreated = true;
|
|
23308
|
+
} else {
|
|
23309
|
+
if (!window._templateWarningCount) window._templateWarningCount = 0;
|
|
23310
|
+
if (window._templateWarningCount < 5) {
|
|
23311
|
+
warn(`[MindMapCss3DManager] Template not found for node ${nodeId} (async templates pending). Template ID: ${templateId}`);
|
|
23312
|
+
window._templateWarningCount++;
|
|
23313
|
+
if (window._templateWarningCount === 5) {
|
|
23314
|
+
warn('[MindMapCss3DManager] ... suppressing further template warnings. Templates will be available after async render.');
|
|
23315
|
+
}
|
|
23316
|
+
}
|
|
23317
|
+
return null;
|
|
23318
|
+
}
|
|
23319
|
+
}
|
|
23320
|
+
|
|
23321
|
+
const width = Number(nodeModel.width || nodeModel.Width || 400);
|
|
23322
|
+
const height = Number(nodeModel.height || nodeModel.Height || 200);
|
|
23323
|
+
const allowsExternalNodeChrome = allowsNodeExternalChrome(nodeModel);
|
|
23324
|
+
const allowsExternalMemoChrome =
|
|
23325
|
+
info.contentTypeLower === 'memo'
|
|
23326
|
+
&& allowsExternalNodeChrome;
|
|
23327
|
+
const element = isDynamicallyCreated
|
|
23328
|
+
? templateElement
|
|
23329
|
+
: templateElement.cloneNode(true);
|
|
23330
|
+
const idPrefix = String(options.idPrefix || (hostKind === NODE_VIEW_HOST_OVERLAY ? 'node-view-overlay' : 'css3d-node')).trim();
|
|
23331
|
+
const resolutionScale = Math.max(1, Number(options.resolutionScale || getCss3dNodeResolutionScale(info.contentTypeLower) || 1));
|
|
23332
|
+
|
|
23333
|
+
element.id = `${idPrefix}-${nodeId}`;
|
|
23334
|
+
element.dataset.nodeId = nodeId;
|
|
23335
|
+
element.dataset.nodeViewHost = hostKind;
|
|
23336
|
+
element.dataset.nodeViewRenderer = NODE_VIEW_RENDERER_VERSION;
|
|
23337
|
+
element.dataset.contentType = info.visualContentTypeLower || info.contentTypeLower || '';
|
|
23338
|
+
element.style.overflow = allowsExternalNodeChrome ? 'visible' : 'hidden';
|
|
23339
|
+
if (allowsExternalNodeChrome) {
|
|
23340
|
+
element.style.contain = 'layout style';
|
|
23341
|
+
element.style.contentVisibility = 'visible';
|
|
23342
|
+
element.style.containIntrinsicSize = 'auto';
|
|
23343
|
+
}
|
|
23344
|
+
element.style.display = '';
|
|
23345
|
+
element.style.transformOrigin = '0% 0%';
|
|
23346
|
+
applyCss3dResolutionLayout(element, width, height, resolutionScale, {
|
|
23347
|
+
resetLayoutTransform: true
|
|
23348
|
+
});
|
|
23349
|
+
element.style.borderRadius = '0px';
|
|
23350
|
+
element.style.position = 'absolute';
|
|
23351
|
+
element.style.left = '0px';
|
|
23352
|
+
element.style.top = '0px';
|
|
23353
|
+
element.style.transition = 'none';
|
|
23354
|
+
element.style.webkitTransition = 'none';
|
|
23355
|
+
element.style.willChange = 'auto';
|
|
23356
|
+
element.style.backfaceVisibility = 'visible';
|
|
23357
|
+
|
|
23358
|
+
return {
|
|
23359
|
+
element,
|
|
23360
|
+
width,
|
|
23361
|
+
height,
|
|
23362
|
+
contentTypeLower: info.contentTypeLower,
|
|
23363
|
+
visualContentTypeLower: info.visualContentTypeLower,
|
|
23364
|
+
isMediaNode: info.isMediaNode,
|
|
23365
|
+
isDynamicallyCreated,
|
|
23366
|
+
allowsExternalNodeChrome,
|
|
23367
|
+
allowsExternalMemoChrome,
|
|
23368
|
+
resolutionScale,
|
|
23369
|
+
hostKind,
|
|
23370
|
+
hostPolicy,
|
|
23371
|
+
rendererVersion: NODE_VIEW_RENDERER_VERSION
|
|
23372
|
+
};
|
|
23373
|
+
}
|
|
23374
|
+
|
|
23064
23375
|
function appendCss3dResizeHitZones(wrapper, nodeModel) {
|
|
23065
23376
|
if (!(wrapper instanceof HTMLElement) || !nodeModel?.id) {
|
|
23066
23377
|
return;
|
|
@@ -23134,7 +23445,8 @@
|
|
|
23134
23445
|
return null;
|
|
23135
23446
|
}
|
|
23136
23447
|
|
|
23137
|
-
const
|
|
23448
|
+
const resolvedNodeId = getNodeId(nodeModel);
|
|
23449
|
+
const templateId = `node-${resolvedNodeId}`;
|
|
23138
23450
|
let templateElement = document.getElementById(templateId);
|
|
23139
23451
|
|
|
23140
23452
|
// ▼▼▼ [New] 템플릿이 없으면 동적으로 생성 (text/markdown 타입용) ▼▼▼
|
|
@@ -23164,14 +23476,14 @@
|
|
|
23164
23476
|
const dynamicTypes = ['text', 'markdown', 'code', 'note', 'memo', CSV_TABLE_CONTENT_TYPE, 'templatelauncher', 'image', 'video', 'embed'];
|
|
23165
23477
|
|
|
23166
23478
|
if (remoteFleetMonitor || dynamicTypes.includes(contentTypeLower)) {
|
|
23167
|
-
log(`[MindMapCss3DManager]
|
|
23168
|
-
templateElement =
|
|
23479
|
+
log(`[MindMapCss3DManager] Deferring dynamic DOM element for ${visualContentTypeLower || contentTypeLower} node ${resolvedNodeId} to shared NodeView`);
|
|
23480
|
+
templateElement = document.createElement('div');
|
|
23169
23481
|
isDynamicallyCreated = true;
|
|
23170
23482
|
} else {
|
|
23171
23483
|
// 경고 로그 제한 (초기 로딩 시 템플릿이 아직 렌더링되지 않은 경우 스팸 방지)
|
|
23172
23484
|
if (!window._templateWarningCount) window._templateWarningCount = 0;
|
|
23173
23485
|
if (window._templateWarningCount < 5) {
|
|
23174
|
-
warn(`[MindMapCss3DManager] Template not found for node ${
|
|
23486
|
+
warn(`[MindMapCss3DManager] Template not found for node ${resolvedNodeId} (async templates pending). Template ID: ${templateId}`);
|
|
23175
23487
|
window._templateWarningCount++;
|
|
23176
23488
|
if (window._templateWarningCount === 5) {
|
|
23177
23489
|
warn(`[MindMapCss3DManager] ... suppressing further template warnings. Templates will be available after async render.`);
|
|
@@ -23182,11 +23494,11 @@
|
|
|
23182
23494
|
}
|
|
23183
23495
|
// ▲▲▲ [New] ▲▲▲
|
|
23184
23496
|
|
|
23185
|
-
const existingCss3dId = `css3d-node-${
|
|
23186
|
-
const nodeEntry = module.nodeObjectsById.get(
|
|
23497
|
+
const existingCss3dId = `css3d-node-${resolvedNodeId}`;
|
|
23498
|
+
const nodeEntry = module.nodeObjectsById.get(resolvedNodeId);
|
|
23187
23499
|
|
|
23188
23500
|
if (nodeEntry && nodeEntry.cssObject) {
|
|
23189
|
-
warn(`[MindMapCss3DManager] CSS3D object already exists in memory for node ${
|
|
23501
|
+
warn(`[MindMapCss3DManager] CSS3D object already exists in memory for node ${resolvedNodeId}. Returning existing object.`);
|
|
23190
23502
|
return nodeEntry.cssObject;
|
|
23191
23503
|
}
|
|
23192
23504
|
|
|
@@ -23210,18 +23522,22 @@
|
|
|
23210
23522
|
*/
|
|
23211
23523
|
// ▲▲▲ [Critical fix] ▲▲▲
|
|
23212
23524
|
|
|
23213
|
-
const
|
|
23214
|
-
|
|
23215
|
-
|
|
23216
|
-
|
|
23217
|
-
|
|
23218
|
-
|
|
23219
|
-
|
|
23220
|
-
|
|
23221
|
-
|
|
23222
|
-
|
|
23223
|
-
|
|
23224
|
-
|
|
23525
|
+
const nodeView = createNodeViewElement(module, nodeModel, {
|
|
23526
|
+
hostKind: NODE_VIEW_HOST_CSS3D,
|
|
23527
|
+
idPrefix: 'css3d-node'
|
|
23528
|
+
});
|
|
23529
|
+
if (!nodeView?.element) {
|
|
23530
|
+
return null;
|
|
23531
|
+
}
|
|
23532
|
+
|
|
23533
|
+
const width = nodeView.width;
|
|
23534
|
+
const height = nodeView.height;
|
|
23535
|
+
const isMediaNode = nodeView.isMediaNode;
|
|
23536
|
+
const allowsExternalNodeChrome = nodeView.allowsExternalNodeChrome;
|
|
23537
|
+
const allowsExternalMemoChrome = nodeView.allowsExternalMemoChrome;
|
|
23538
|
+
const clonedElement = nodeView.element;
|
|
23539
|
+
clonedElement.id = `css3d-node-${resolvedNodeId}`;
|
|
23540
|
+
clonedElement.dataset.nodeId = resolvedNodeId;
|
|
23225
23541
|
clonedElement.style.overflow = allowsExternalNodeChrome ? 'visible' : 'hidden';
|
|
23226
23542
|
if (allowsExternalNodeChrome) {
|
|
23227
23543
|
clonedElement.style.contain = 'layout style';
|
|
@@ -23232,7 +23548,7 @@
|
|
|
23232
23548
|
clonedElement.style.transformOrigin = '0% 0%';
|
|
23233
23549
|
|
|
23234
23550
|
// ▼▼▼ [Clarity] Scale factor for high-resolution rendering ▼▼▼
|
|
23235
|
-
const resolutionScale =
|
|
23551
|
+
const resolutionScale = nodeView.resolutionScale;
|
|
23236
23552
|
applyCss3dResolutionLayout(clonedElement, width, height, resolutionScale, {
|
|
23237
23553
|
resetLayoutTransform: true
|
|
23238
23554
|
});
|
|
@@ -23265,7 +23581,7 @@
|
|
|
23265
23581
|
wrapper.style.minHeight = `${height * resolutionScale}px`;
|
|
23266
23582
|
wrapper.style.maxWidth = `${width * resolutionScale}px`;
|
|
23267
23583
|
wrapper.style.maxHeight = `${height * resolutionScale}px`;
|
|
23268
|
-
wrapper.dataset.nodeId =
|
|
23584
|
+
wrapper.dataset.nodeId = resolvedNodeId;
|
|
23269
23585
|
wrapper.style.transition = CSS3D_WRAPPER_TRANSITION;
|
|
23270
23586
|
wrapper.style.webkitTransition = CSS3D_WRAPPER_WEBKIT_TRANSITION;
|
|
23271
23587
|
|
|
@@ -23369,7 +23685,7 @@
|
|
|
23369
23685
|
const css3dObj = new CSS3DObjectCtor(wrapper);
|
|
23370
23686
|
css3dObj.scale.set(1 / resolutionScale, 1 / resolutionScale, 1 / resolutionScale);
|
|
23371
23687
|
css3dObj.visible = false;
|
|
23372
|
-
css3dObj.userData.nodeId =
|
|
23688
|
+
css3dObj.userData.nodeId = resolvedNodeId;
|
|
23373
23689
|
css3dObj.userData.worldWidth = width;
|
|
23374
23690
|
css3dObj.userData.worldHeight = height;
|
|
23375
23691
|
css3dObj.userData.resolutionScale = resolutionScale;
|
|
@@ -23387,7 +23703,7 @@
|
|
|
23387
23703
|
// ▲▲▲ [Fix] ▲▲▲
|
|
23388
23704
|
|
|
23389
23705
|
requestAnimationFrame(() => {
|
|
23390
|
-
syncCss3dScrollFromModel(module,
|
|
23706
|
+
syncCss3dScrollFromModel(module, resolvedNodeId, {
|
|
23391
23707
|
source: 'createCss3dObject'
|
|
23392
23708
|
});
|
|
23393
23709
|
});
|
|
@@ -23728,7 +24044,7 @@
|
|
|
23728
24044
|
void resolveLocalCssMediaSource(mediaEl, nodeModel, contentTypeLower);
|
|
23729
24045
|
} else {
|
|
23730
24046
|
delete mediaEl.dataset.videoCanvasProxyFailed;
|
|
23731
|
-
mediaEl
|
|
24047
|
+
void setCssVideoElementSource(mediaEl, nodeModel, content, { load: true });
|
|
23732
24048
|
}
|
|
23733
24049
|
if (contentTypeLower === 'video' &&
|
|
23734
24050
|
mediaEl.dataset?.lodMediaSuspended !== 'true' &&
|
|
@@ -24178,6 +24494,9 @@
|
|
|
24178
24494
|
clearSelectableTextOverlay: clearSelectableTextOverlay,
|
|
24179
24495
|
clearNativeTextSelectionSource: clearNativeTextSelectionSource,
|
|
24180
24496
|
createCss3dObject: createCss3dObject,
|
|
24497
|
+
createNodeViewElement: createNodeViewElement,
|
|
24498
|
+
getNodeViewHostPolicy: getNodeViewHostPolicy,
|
|
24499
|
+
getNodeViewPrimaryHost: getNodeViewPrimaryHost,
|
|
24181
24500
|
createEditingOverlayContent: createEditingOverlayContent,
|
|
24182
24501
|
clearEditingOverlay: clearEditingOverlay,
|
|
24183
24502
|
getTextInteractionContentSelectors: getTextInteractionContentSelectors,
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
// ▲▲▲ [Usage] ▲▲▲
|
|
52
52
|
|
|
53
53
|
// Procedural line rendering settings
|
|
54
|
-
const LOD_RENDERER_BUILD_ID = '20260621-
|
|
54
|
+
const LOD_RENDERER_BUILD_ID = '20260621-nodeview-host-policy-v794';
|
|
55
55
|
const DirtyKind = Object.freeze({
|
|
56
56
|
ResidentFullRebuild: 'resident-full-rebuild',
|
|
57
57
|
ResidentPatch: 'resident-patch',
|
|
@@ -2851,6 +2851,85 @@
|
|
|
2851
2851
|
return finalizeReadonlySourceClone(module, sourceRoot, preparedClone, type, interactive, { renderMode: renderMode });
|
|
2852
2852
|
}
|
|
2853
2853
|
|
|
2854
|
+
function shouldUseSharedNodeViewOverlay(module, entry, mode) {
|
|
2855
|
+
const manager = getCss3dManager();
|
|
2856
|
+
const model = getModel(entry);
|
|
2857
|
+
const policy = manager?.getNodeViewHostPolicy?.(module, model, { hostKind: 'overlay' }) || null;
|
|
2858
|
+
if (policy?.useSharedOverlay === false || module?.renderDebugFlags?.enableSharedNodeViewOverlay === false) {
|
|
2859
|
+
return false;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
const type = getContentType(entry);
|
|
2863
|
+
const normalizedMode = String(mode || '').trim().toLowerCase();
|
|
2864
|
+
return policy?.isOverlayPrimary === true
|
|
2865
|
+
|| normalizedMode === 'full'
|
|
2866
|
+
|| type === 'text'
|
|
2867
|
+
|| type === 'markdown'
|
|
2868
|
+
|| type === 'note'
|
|
2869
|
+
|| type === 'memo'
|
|
2870
|
+
|| type === 'code'
|
|
2871
|
+
|| type === CSV_TABLE_CONTENT_TYPE;
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
function isOverlayPrimaryNodeViewHost(module, entry) {
|
|
2875
|
+
const manager = getCss3dManager();
|
|
2876
|
+
const model = getModel(entry);
|
|
2877
|
+
const policy = manager?.getNodeViewHostPolicy?.(module, model, { hostKind: 'overlay' }) || null;
|
|
2878
|
+
return policy?.isOverlayPrimary === true;
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
function createReadonlyOverlayFromNodeView(module, entry, options = {}) {
|
|
2882
|
+
const manager = getCss3dManager();
|
|
2883
|
+
const model = getModel(entry);
|
|
2884
|
+
if (!manager?.createNodeViewElement || !model) {
|
|
2885
|
+
return null;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
const type = getContentType(entry);
|
|
2889
|
+
const interactive = options.interactive === true;
|
|
2890
|
+
const renderMode = String(options.renderMode || getReadonlySelectionMode(entry) || type || '').trim().toLowerCase();
|
|
2891
|
+
if (!shouldUseSharedNodeViewOverlay(module, entry, renderMode)) {
|
|
2892
|
+
return null;
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
const nodeView = manager.createNodeViewElement(module, model, {
|
|
2896
|
+
hostKind: 'overlay',
|
|
2897
|
+
idPrefix: 'node-view-overlay',
|
|
2898
|
+
readonly: true
|
|
2899
|
+
});
|
|
2900
|
+
const nodeViewRoot = nodeView?.element || null;
|
|
2901
|
+
if (!nodeViewRoot) {
|
|
2902
|
+
return null;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
nodeViewRoot.classList.add('mind-map-text-overlay-v2-node-view-source');
|
|
2906
|
+
nodeViewRoot.style.position = 'relative';
|
|
2907
|
+
nodeViewRoot.style.left = '0px';
|
|
2908
|
+
nodeViewRoot.style.top = '0px';
|
|
2909
|
+
nodeViewRoot.style.pointerEvents = 'none';
|
|
2910
|
+
|
|
2911
|
+
const preparedRoot = prepareOverlayClone(nodeViewRoot);
|
|
2912
|
+
if (!preparedRoot) {
|
|
2913
|
+
return null;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
const sourceRoot = options.sourceRoot || getReadonlySelectionSourceRoot(entry) || nodeViewRoot;
|
|
2917
|
+
const finalized = finalizeReadonlySourceClone(module, sourceRoot, preparedRoot, type, interactive, {
|
|
2918
|
+
renderMode: 'node-view'
|
|
2919
|
+
});
|
|
2920
|
+
if (!finalized?.container) {
|
|
2921
|
+
return null;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
finalized.container.dataset.overlaySource = 'node-view';
|
|
2925
|
+
finalized.container.dataset.nodeViewRenderer = nodeView.rendererVersion || 'shared-node-view-v1';
|
|
2926
|
+
return {
|
|
2927
|
+
...finalized,
|
|
2928
|
+
sourceRoot,
|
|
2929
|
+
nodeViewSource: true
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2854
2933
|
function getRelativeFragmentPlacement(hostElement, sourceElement, hostBaseWidth, hostBaseHeight) {
|
|
2855
2934
|
if (!hostElement || !sourceElement) {
|
|
2856
2935
|
return null;
|
|
@@ -3115,14 +3194,20 @@
|
|
|
3115
3194
|
// Memo headers collapse too easily when reconstructed from
|
|
3116
3195
|
// independent fragments. Clone the canonical shell but strip the
|
|
3117
3196
|
// visual chrome so CSS3D keeps ownership of card background/edge.
|
|
3118
|
-
return
|
|
3197
|
+
return createReadonlyOverlayFromNodeView(module, entry, {
|
|
3198
|
+
...options,
|
|
3199
|
+
renderMode: mode
|
|
3200
|
+
}) || createReadonlyMemoShellClone(module, entry, options);
|
|
3119
3201
|
}
|
|
3120
3202
|
|
|
3121
3203
|
if (mode !== 'none') {
|
|
3122
3204
|
// Passive readonly overlays only need the text surface.
|
|
3123
3205
|
// Keep card chrome/background/borders in CSS3D to avoid duplicate
|
|
3124
3206
|
// painting and reduce DOM work during zoom/pan.
|
|
3125
|
-
return
|
|
3207
|
+
return createReadonlyOverlayFromNodeView(module, entry, {
|
|
3208
|
+
...options,
|
|
3209
|
+
renderMode: mode
|
|
3210
|
+
}) || createReadonlyContentOnlyShell(module, entry, options);
|
|
3126
3211
|
}
|
|
3127
3212
|
|
|
3128
3213
|
return null;
|
|
@@ -4216,11 +4301,14 @@
|
|
|
4216
4301
|
|
|
4217
4302
|
const isSupported = interactive ? supportsSelection(entry) : supportsPassiveDisplay(entry);
|
|
4218
4303
|
const canRenderFromSource = !!(entry?.cssObject?.element && entry.currentType === 'CSS' && entry.cssObject.visible);
|
|
4304
|
+
const canRenderFromNodeViewHost = !interactive &&
|
|
4305
|
+
isOverlayPrimaryNodeViewHost(module, entry) &&
|
|
4306
|
+
isSupported;
|
|
4219
4307
|
const canReuseExistingPassiveCard = !interactive &&
|
|
4220
4308
|
shouldRenderPassive &&
|
|
4221
4309
|
!!existingCard &&
|
|
4222
4310
|
existingCard.childElementCount > 0;
|
|
4223
|
-
if (!entry || !isSupported || (!canRenderFromSource && !canReuseExistingPassiveCard)) {
|
|
4311
|
+
if (!entry || !isSupported || (!canRenderFromSource && !canRenderFromNodeViewHost && !canReuseExistingPassiveCard)) {
|
|
4224
4312
|
hideCard(existingCard);
|
|
4225
4313
|
if (interactive) {
|
|
4226
4314
|
clearSelection(module, nodeId);
|
|
@@ -4230,7 +4318,7 @@
|
|
|
4230
4318
|
return;
|
|
4231
4319
|
}
|
|
4232
4320
|
|
|
4233
|
-
const forceRebuild = canRenderFromSource && state?.dirtyNodes?.has?.(nodeId) === true;
|
|
4321
|
+
const forceRebuild = (canRenderFromSource || canRenderFromNodeViewHost) && state?.dirtyNodes?.has?.(nodeId) === true;
|
|
4234
4322
|
const card = renderSelectionCard(module, entry, {
|
|
4235
4323
|
interactive: interactive,
|
|
4236
4324
|
forceRebuild: forceRebuild
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
const renderers = new Map();
|
|
16
16
|
const failedImageAssetCache = new Map();
|
|
17
17
|
const pendingImageAssetFetches = new Map();
|
|
18
|
+
const imageAssetResolveCache = new Map();
|
|
19
|
+
const pendingImageAssetResolves = new Map();
|
|
18
20
|
let sharedMeasurer = null;
|
|
19
21
|
|
|
20
22
|
function getSharedMeasurer() {
|
|
@@ -91,6 +93,99 @@
|
|
|
91
93
|
.test(String(url || '').trim());
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
function getImageAssetResolveUrl(targetUrl) {
|
|
97
|
+
const normalized = normalizeImageAssetUrl(targetUrl);
|
|
98
|
+
if (!normalized || !isLoopbackImageAssetUrl(normalized)) {
|
|
99
|
+
return '';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const currentOrigin = window?.location?.origin || '';
|
|
104
|
+
if (!currentOrigin) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const target = new URL(normalized, window?.location?.href || currentOrigin);
|
|
109
|
+
return `${currentOrigin}/api/assets/resolve?path=${encodeURIComponent(target.pathname)}`;
|
|
110
|
+
} catch {
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function resolveLoopbackImageAssetUrl(targetUrl) {
|
|
116
|
+
const normalized = normalizeImageAssetUrl(targetUrl);
|
|
117
|
+
if (!normalized || !isLoopbackImageAssetUrl(normalized)) {
|
|
118
|
+
return normalized;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const cached = imageAssetResolveCache.get(normalized);
|
|
122
|
+
if (cached) {
|
|
123
|
+
if (cached.exists === false) {
|
|
124
|
+
const error = new Error('Loopback image asset is missing');
|
|
125
|
+
error.status = 404;
|
|
126
|
+
error.url = normalized;
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
return normalizeImageAssetUrl(cached.url || normalized) || normalized;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const existingPending = pendingImageAssetResolves.get(normalized);
|
|
133
|
+
if (existingPending) {
|
|
134
|
+
return await existingPending;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const resolveUrl = getImageAssetResolveUrl(normalized);
|
|
138
|
+
if (!resolveUrl) {
|
|
139
|
+
return normalized;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const pending = (async () => {
|
|
143
|
+
const response = await fetch(resolveUrl, {
|
|
144
|
+
cache: 'no-store',
|
|
145
|
+
targetAddressSpace: 'loopback'
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (response.status === 404) {
|
|
149
|
+
return normalized;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
const error = new Error(`Asset resolve HTTP ${response.status}`);
|
|
154
|
+
error.status = response.status;
|
|
155
|
+
error.url = normalized;
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const payload = await response.json().catch(() => null);
|
|
160
|
+
if (!payload || payload.exists !== true) {
|
|
161
|
+
imageAssetResolveCache.set(normalized, {
|
|
162
|
+
exists: false,
|
|
163
|
+
url: normalized
|
|
164
|
+
});
|
|
165
|
+
const error = new Error('Loopback image asset is missing');
|
|
166
|
+
error.status = 404;
|
|
167
|
+
error.url = normalized;
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const resolvedUrl = normalizeImageAssetUrl(payload.url || normalized) || normalized;
|
|
172
|
+
imageAssetResolveCache.set(normalized, {
|
|
173
|
+
exists: true,
|
|
174
|
+
url: resolvedUrl
|
|
175
|
+
});
|
|
176
|
+
return resolvedUrl;
|
|
177
|
+
})();
|
|
178
|
+
|
|
179
|
+
pendingImageAssetResolves.set(normalized, pending);
|
|
180
|
+
try {
|
|
181
|
+
return await pending;
|
|
182
|
+
} finally {
|
|
183
|
+
if (pendingImageAssetResolves.get(normalized) === pending) {
|
|
184
|
+
pendingImageAssetResolves.delete(normalized);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
94
189
|
function shouldAttachImageAssetAuthorization(targetUrl, authToken) {
|
|
95
190
|
const token = typeof authToken === 'string' ? authToken.trim() : '';
|
|
96
191
|
if (!token) {
|
|
@@ -1839,7 +1934,10 @@
|
|
|
1839
1934
|
return Object.keys(requestOptions).length > 0 ? requestOptions : undefined;
|
|
1840
1935
|
};
|
|
1841
1936
|
|
|
1842
|
-
const fetchWithAuth = (targetUrl) =>
|
|
1937
|
+
const fetchWithAuth = async (targetUrl) => {
|
|
1938
|
+
const resolvedTargetUrl = await resolveLoopbackImageAssetUrl(targetUrl);
|
|
1939
|
+
return fetch(resolvedTargetUrl, getFetchOptions(resolvedTargetUrl));
|
|
1940
|
+
};
|
|
1843
1941
|
const fetchBlobWithAuth = async (targetUrl) => {
|
|
1844
1942
|
const requestKey = getImageAssetErrorCacheKey(targetUrl) || normalizeImageAssetUrl(targetUrl);
|
|
1845
1943
|
if (!requestKey) {
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mainAssemblyName": "MindExecution.Web",
|
|
3
3
|
"resources": {
|
|
4
|
-
"hash": "sha256-
|
|
4
|
+
"hash": "sha256-D1kQwIGK/5ULx6Yo1WH3vsTRIzWc/7/k8RpSswCSqh4=",
|
|
5
5
|
"fingerprinting": {
|
|
6
6
|
"Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
|
|
7
7
|
"Markdig.d1j7v41cl1.dll": "Markdig.dll",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"MindExecution.Plugins.Directory.ju87t8h3zs.dll": "MindExecution.Plugins.Directory.dll",
|
|
132
132
|
"MindExecution.Plugins.PlanMaster.krn3jgvjpa.dll": "MindExecution.Plugins.PlanMaster.dll",
|
|
133
133
|
"MindExecution.Plugins.YouTube.5m53srfud6.dll": "MindExecution.Plugins.YouTube.dll",
|
|
134
|
-
"MindExecution.Shared.
|
|
134
|
+
"MindExecution.Shared.ojp3psugsm.dll": "MindExecution.Shared.dll",
|
|
135
135
|
"MindExecution.Web.82k6ktlkfg.dll": "MindExecution.Web.dll",
|
|
136
136
|
"dotnet.js": "dotnet.js",
|
|
137
137
|
"dotnet.native.qc8g39g30v.js": "dotnet.native.js",
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
"MindExecution.Plugins.Business.nyr3v25v48.dll": "sha256-T4c7+fDE4TQAXAoFmHnYM+tT7RSvAM7W0VinLPoD+6U=",
|
|
284
284
|
"MindExecution.Plugins.Concept.l9z9tx9svt.dll": "sha256-N/QoILmTilvX5Zo4SCy7ENTCqMzSC6RIEQlEwtxkGPo=",
|
|
285
285
|
"MindExecution.Plugins.PlanMaster.krn3jgvjpa.dll": "sha256-8IFpm/2fpsWD3syyl58LTSWSGC8PfcrsLdmmSKR1Sq0=",
|
|
286
|
-
"MindExecution.Shared.
|
|
286
|
+
"MindExecution.Shared.ojp3psugsm.dll": "sha256-FC0pE2bsyE9i7GNSXCghKULaYYZXUEdepb2VxPfcYaA=",
|
|
287
287
|
"MindExecution.Web.82k6ktlkfg.dll": "sha256-ECPSyJrziGEVhCw8ZUXkRsfTDjWCk676m5yz99b13N4="
|
|
288
288
|
},
|
|
289
289
|
"lazyAssembly": {
|
package/wwwroot/index.html
CHANGED
|
@@ -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-
|
|
11
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260621-
|
|
10
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260621-nodeview-host-policy-v794" />
|
|
11
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260621-nodeview-host-policy-v794" />
|
|
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-
|
|
582
|
+
const scriptVersion = '20260621-nodeview-host-policy-v794';
|
|
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": "
|
|
2
|
+
"version": "yv2PsIsP",
|
|
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-
|
|
45
|
+
"hash": "sha256-ha65+2w0xEoB2Mo1nEHyp7RzEI399tRfJqk2UVmQ49w=",
|
|
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-
|
|
81
|
+
"hash": "sha256-wKghwA30YN9dfi92pXm49piiFewBTKC3qbNbi5Xnd+k=",
|
|
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-
|
|
89
|
+
"hash": "sha256-m19Ex/iUFkKEfN82ioo9gB90TJKEIMs77mCnyk6oVoM=",
|
|
90
90
|
"url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"url": "_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js"
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
|
-
"hash": "sha256-
|
|
117
|
+
"hash": "sha256-x/0q02ck7cYzpzM8Imv2OIKtGY/o5/k5LyoVPCU5Xc0=",
|
|
118
118
|
"url": "_content/MindExecution.Shared/js/mind-map-lod-renderer.js"
|
|
119
119
|
},
|
|
120
120
|
{
|
|
@@ -154,11 +154,11 @@
|
|
|
154
154
|
"url": "_content/MindExecution.Shared/js/mind-map-text-lod-system.js"
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
|
-
"hash": "sha256-
|
|
157
|
+
"hash": "sha256-7wG4IolMJjOrSd1bOHKkXQ5t+Kz53EVo0hnmWfFY+xI=",
|
|
158
158
|
"url": "_content/MindExecution.Shared/js/mind-map-text-overlay-v2.js"
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
|
-
"hash": "sha256-
|
|
161
|
+
"hash": "sha256-uWZUzao/aNpU3SD4jtCVFMrTaGYtCAOAO6lxNt5UL1g=",
|
|
162
162
|
"url": "_content/MindExecution.Shared/js/mind-map-texture-factory.js"
|
|
163
163
|
},
|
|
164
164
|
{
|
|
@@ -442,8 +442,8 @@
|
|
|
442
442
|
"url": "_framework/MindExecution.Plugins.YouTube.5m53srfud6.dll"
|
|
443
443
|
},
|
|
444
444
|
{
|
|
445
|
-
"hash": "sha256-
|
|
446
|
-
"url": "_framework/MindExecution.Shared.
|
|
445
|
+
"hash": "sha256-FC0pE2bsyE9i7GNSXCghKULaYYZXUEdepb2VxPfcYaA=",
|
|
446
|
+
"url": "_framework/MindExecution.Shared.ojp3psugsm.dll"
|
|
447
447
|
},
|
|
448
448
|
{
|
|
449
449
|
"hash": "sha256-ECPSyJrziGEVhCw8ZUXkRsfTDjWCk676m5yz99b13N4=",
|
|
@@ -770,7 +770,7 @@
|
|
|
770
770
|
"url": "_framework/Websocket.Client.vapounvmnl.dll"
|
|
771
771
|
},
|
|
772
772
|
{
|
|
773
|
-
"hash": "sha256
|
|
773
|
+
"hash": "sha256-Flu3r26LvbLO2wUcZVQG1lKL/SCiMrzqzajuoIajSks=",
|
|
774
774
|
"url": "_framework/blazor.boot.json"
|
|
775
775
|
},
|
|
776
776
|
{
|
|
@@ -834,7 +834,7 @@
|
|
|
834
834
|
"url": "image-manifest.json"
|
|
835
835
|
},
|
|
836
836
|
{
|
|
837
|
-
"hash": "sha256-
|
|
837
|
+
"hash": "sha256-1ogFlgItkavFXyCr6MK610wsv1X0dpjib/iK1tVMs5k=",
|
|
838
838
|
"url": "index.html"
|
|
839
839
|
},
|
|
840
840
|
{
|