@mindexec/cli 0.2.456 → 0.2.458
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 +77 -77
- 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/scripts/remote-fleet-render-smoke.mjs +50 -229
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +301 -604
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +1326 -1323
- package/wwwroot/_framework/{MindExecution.Core.glgqkah960.dll → MindExecution.Core.2ch68iyy8o.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.1nb0zfmie1.dll → MindExecution.Kernel.dh617xfv36.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.4pdpv5q991.dll → MindExecution.Plugins.Admin.0sm50hbae9.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.jpl2k4nnhh.dll → MindExecution.Plugins.Business.3wq01orrbu.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.rissr3n74r.dll → MindExecution.Plugins.Concept.z8yl28fa2a.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.tkopeqv16k.dll → MindExecution.Plugins.Directory.m7murp5oes.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.jmjftycwo4.dll → MindExecution.Plugins.PlanMaster.yuyrbpf0vh.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.drj98vf3ti.dll → MindExecution.Plugins.YouTube.rwxvn00rm2.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.cbieupjmfp.dll → MindExecution.Shared.r9k48iyijb.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.xl8ygqgy0i.dll → MindExecution.Web.742aribkxm.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +21 -21
- package/wwwroot/index.html +722 -722
- package/wwwroot/service-worker-assets.js +25 -25
- package/wwwroot/service-worker.js +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// File: mind-map-css3d-manager.js
|
|
2
|
-
// [Refactor] Owns CSS3DObject creation, DOM cloning, and event binding.
|
|
3
|
-
(function () {
|
|
4
|
-
'use strict';
|
|
5
|
-
|
|
6
|
-
// ▼▼▼ [Perf] Debug logging (disable in production) ▼▼▼
|
|
1
|
+
// File: mind-map-css3d-manager.js
|
|
2
|
+
// [Refactor] Owns CSS3DObject creation, DOM cloning, and event binding.
|
|
3
|
+
(function () {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
// ▼▼▼ [Perf] Debug logging (disable in production) ▼▼▼
|
|
7
7
|
const DEBUG = false;
|
|
8
8
|
const log = DEBUG ? console.log.bind(console) : () => { };
|
|
9
9
|
const warn = DEBUG ? console.warn.bind(console) : () => { };
|
|
@@ -46,8 +46,9 @@
|
|
|
46
46
|
const CSS3D_ONLY_NODE_RENDERING = true;
|
|
47
47
|
const CSV_TABLE_MAX_RENDER_ROWS = 500;
|
|
48
48
|
const CSV_TABLE_MAX_RENDER_COLUMNS = 80;
|
|
49
|
-
const REMOTE_FLEET_DISPLAY_NAME = '
|
|
50
|
-
const REMOTE_FLEET_LEGACY_DISPLAY_NAME = '
|
|
49
|
+
const REMOTE_FLEET_DISPLAY_NAME = 'Remote Work Console';
|
|
50
|
+
const REMOTE_FLEET_LEGACY_DISPLAY_NAME = 'Multi Desktop Monitor';
|
|
51
|
+
const REMOTE_FLEET_LEGACY_DISPLAY_NAME_V0 = 'Remote Fleet Monitor';
|
|
51
52
|
const _cssVideoProxyStateByVideo = new WeakMap();
|
|
52
53
|
|
|
53
54
|
function syncMindCanvasPlatformRenderClasses() {
|
|
@@ -355,12 +356,12 @@
|
|
|
355
356
|
renderCsvTableContent
|
|
356
357
|
};
|
|
357
358
|
// ▲▲▲ [Perf] ▲▲▲
|
|
358
|
-
|
|
359
|
-
// ▼▼▼ [New] 기본 마크다운 스타일 주입 ▼▼▼
|
|
359
|
+
|
|
360
|
+
// ▼▼▼ [New] 기본 마크다운 스타일 주입 ▼▼▼
|
|
360
361
|
function injectMarkdownStyles() {
|
|
361
|
-
// ▼▼▼ [FIX] 기존 스타일 제거 후 새로 주입 (업데이트 보장) ▼▼▼
|
|
362
|
-
const existingStyle = document.getElementById('mind-map-markdown-styles');
|
|
363
|
-
if (existingStyle) existingStyle.remove();
|
|
362
|
+
// ▼▼▼ [FIX] 기존 스타일 제거 후 새로 주입 (업데이트 보장) ▼▼▼
|
|
363
|
+
const existingStyle = document.getElementById('mind-map-markdown-styles');
|
|
364
|
+
if (existingStyle) existingStyle.remove();
|
|
364
365
|
const style = document.createElement('style');
|
|
365
366
|
style.id = 'mind-map-markdown-styles';
|
|
366
367
|
style.textContent = `
|
|
@@ -388,17 +389,17 @@
|
|
|
388
389
|
.markdown-body h2 { font-size: 1.4em; font-weight: bold; margin-top: 1em; margin-bottom: 0.5em; border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; }
|
|
389
390
|
.markdown-body h3 { font-size: 1.2em; font-weight: bold; margin-top: 1em; margin-bottom: 0.5em; }
|
|
390
391
|
.markdown-body p { margin-bottom: 0.8em; white-space: normal; }
|
|
391
|
-
.markdown-body ul { padding-left: 1.5em; margin-bottom: 1em; list-style-type: disc; }
|
|
392
|
-
.markdown-body ol { padding-left: 1.5em; margin-bottom: 1em; list-style-type: decimal; }
|
|
393
|
-
.markdown-body li { margin-bottom: 0.25em; }
|
|
394
|
-
.markdown-body blockquote { border-left: 4px solid #dfe2e5; padding: 0 1em; color: #6a737d; margin: 0 0 1em 0; }
|
|
395
|
-
.markdown-body code { font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; background-color: rgba(27,31,35,0.05); padding: 0.2em 0.4em; border-radius: 3px; font-size: 85%; }
|
|
396
|
-
.markdown-body pre { background-color: #f6f8fa; padding: 12px; overflow: auto; border-radius: 3px; margin-bottom: 1em; }
|
|
397
|
-
.markdown-body pre code { background-color: transparent; padding: 0; font-size: 100%; }
|
|
398
|
-
.markdown-body a { color: #0366d6; text-decoration: none; }
|
|
399
|
-
.markdown-body a:hover { text-decoration: underline; }
|
|
400
|
-
.markdown-body img { max-width: 100%; box-sizing: content-box; background-color: #fff; }
|
|
401
|
-
.markdown-body table { border-spacing: 0; border-collapse: collapse; width: 100%; margin-bottom: 1em; }
|
|
392
|
+
.markdown-body ul { padding-left: 1.5em; margin-bottom: 1em; list-style-type: disc; }
|
|
393
|
+
.markdown-body ol { padding-left: 1.5em; margin-bottom: 1em; list-style-type: decimal; }
|
|
394
|
+
.markdown-body li { margin-bottom: 0.25em; }
|
|
395
|
+
.markdown-body blockquote { border-left: 4px solid #dfe2e5; padding: 0 1em; color: #6a737d; margin: 0 0 1em 0; }
|
|
396
|
+
.markdown-body code { font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; background-color: rgba(27,31,35,0.05); padding: 0.2em 0.4em; border-radius: 3px; font-size: 85%; }
|
|
397
|
+
.markdown-body pre { background-color: #f6f8fa; padding: 12px; overflow: auto; border-radius: 3px; margin-bottom: 1em; }
|
|
398
|
+
.markdown-body pre code { background-color: transparent; padding: 0; font-size: 100%; }
|
|
399
|
+
.markdown-body a { color: #0366d6; text-decoration: none; }
|
|
400
|
+
.markdown-body a:hover { text-decoration: underline; }
|
|
401
|
+
.markdown-body img { max-width: 100%; box-sizing: content-box; background-color: #fff; }
|
|
402
|
+
.markdown-body table { border-spacing: 0; border-collapse: collapse; width: 100%; margin-bottom: 1em; }
|
|
402
403
|
.markdown-body table th, .markdown-body table td { padding: 6px 13px; border: 1px solid #dfe2e5; }
|
|
403
404
|
.markdown-body table th { font-weight: 600; background-color: #f6f8fa; }
|
|
404
405
|
.markdown-body hr { height: 0.25em; padding: 0; margin: 24px 0; background-color: #e1e4e8; border: 0; }
|
|
@@ -597,10 +598,10 @@
|
|
|
597
598
|
}
|
|
598
599
|
/* Force list markers to be visible */
|
|
599
600
|
.markdown-body ul li { list-style-type: disc; }
|
|
600
|
-
.markdown-body ol li { list-style-type: decimal; }
|
|
601
|
-
|
|
602
|
-
/* ▼▼▼ [NEW] 슬림 스크롤바 스타일 - 모든 텍스트/노트/코드 영역 (Razor + JS 동적노드 전체) ▼▼▼ */
|
|
603
|
-
/* 1. Firefox 대응 + 여백 최소화 (속성 선택자로 모든 노드 잡기) */
|
|
601
|
+
.markdown-body ol li { list-style-type: decimal; }
|
|
602
|
+
|
|
603
|
+
/* ▼▼▼ [NEW] 슬림 스크롤바 스타일 - 모든 텍스트/노트/코드 영역 (Razor + JS 동적노드 전체) ▼▼▼ */
|
|
604
|
+
/* 1. Firefox 대응 + 여백 최소화 (속성 선택자로 모든 노드 잡기) */
|
|
604
605
|
.note-textarea,
|
|
605
606
|
.note-content,
|
|
606
607
|
.markdown-body,
|
|
@@ -659,7 +660,7 @@
|
|
|
659
660
|
width: 8px !important;
|
|
660
661
|
height: 8px !important;
|
|
661
662
|
}
|
|
662
|
-
|
|
663
|
+
|
|
663
664
|
.note-textarea::-webkit-scrollbar-thumb,
|
|
664
665
|
.note-content::-webkit-scrollbar-thumb,
|
|
665
666
|
.markdown-body::-webkit-scrollbar-thumb,
|
|
@@ -750,7 +751,7 @@
|
|
|
750
751
|
.map-node-code .code-body::-webkit-scrollbar-thumb:active {
|
|
751
752
|
background: rgba(0, 0, 0, 0.45) !important;
|
|
752
753
|
}
|
|
753
|
-
|
|
754
|
+
|
|
754
755
|
/* 3. 노트 편집기는 렌더링 markdown과 같은 타이포그래피/박스 모델을 유지 */
|
|
755
756
|
.map-node-note .note-textarea,
|
|
756
757
|
.map-node-note textarea.note-textarea {
|
|
@@ -788,7 +789,7 @@
|
|
|
788
789
|
color: transparent !important;
|
|
789
790
|
text-shadow: 0 0 0 #ffffff !important;
|
|
790
791
|
}
|
|
791
|
-
|
|
792
|
+
|
|
792
793
|
.prose, [id^="node-response-"]:not(.image-content):not(.video-content):not(.embed-content) {
|
|
793
794
|
padding-right: 14px !important;
|
|
794
795
|
}
|
|
@@ -809,8 +810,8 @@
|
|
|
809
810
|
overflow-wrap: inherit !important;
|
|
810
811
|
}
|
|
811
812
|
/* ▲▲▲ [NEW] ▲▲▲ */
|
|
812
|
-
|
|
813
|
-
/* ▼▼▼ [NEW] CSS3D 텍스트 앨리어싱 개선 ▼▼▼ */
|
|
813
|
+
|
|
814
|
+
/* ▼▼▼ [NEW] CSS3D 텍스트 앨리어싱 개선 ▼▼▼ */
|
|
814
815
|
.css3d-resolution-wrapper,
|
|
815
816
|
.css3d-dynamic-node,
|
|
816
817
|
[id^="css3d-node-"],
|
|
@@ -13458,7 +13459,7 @@
|
|
|
13458
13459
|
|
|
13459
13460
|
function getRemoteFleetDisplayTitle(value) {
|
|
13460
13461
|
const title = String(value ?? '').trim();
|
|
13461
|
-
if (!title || title === REMOTE_FLEET_LEGACY_DISPLAY_NAME) {
|
|
13462
|
+
if (!title || title === REMOTE_FLEET_LEGACY_DISPLAY_NAME || title === REMOTE_FLEET_LEGACY_DISPLAY_NAME_V0) {
|
|
13462
13463
|
return REMOTE_FLEET_DISPLAY_NAME;
|
|
13463
13464
|
}
|
|
13464
13465
|
return title;
|
|
@@ -13552,7 +13553,7 @@
|
|
|
13552
13553
|
|
|
13553
13554
|
const titleBlock = document.createElement('div');
|
|
13554
13555
|
titleBlock.className = 'template-card__title-block';
|
|
13555
|
-
titleBlock.appendChild(createTemplateCardText('div', 'template-card__eyebrow', 'Remote'));
|
|
13556
|
+
titleBlock.appendChild(createTemplateCardText('div', 'template-card__eyebrow', 'Remote Work'));
|
|
13556
13557
|
titleBlock.appendChild(createTemplateCardText('div', 'template-card__title', title));
|
|
13557
13558
|
header.appendChild(titleBlock);
|
|
13558
13559
|
shell.appendChild(header);
|
|
@@ -14605,7 +14606,7 @@
|
|
|
14605
14606
|
const REMOTE_FLEET_MONITOR_REFRESH_MS = 10000;
|
|
14606
14607
|
const REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS = 2500;
|
|
14607
14608
|
const REMOTE_FLEET_LIVE_REFRESH_MS = 30000;
|
|
14608
|
-
const REMOTE_FLEET_MONITOR_LIVE_FPS =
|
|
14609
|
+
const REMOTE_FLEET_MONITOR_LIVE_FPS = 2;
|
|
14609
14610
|
const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_MONITOR_LIVE_FPS);
|
|
14610
14611
|
const REMOTE_FLEET_CONTROL_LIVE_FPS = 30;
|
|
14611
14612
|
const REMOTE_FLEET_CONTROL_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_CONTROL_LIVE_FPS);
|
|
@@ -14626,8 +14627,6 @@
|
|
|
14626
14627
|
const REMOTE_FLEET_BINARY_FRAME_CACHE_MS = 10000;
|
|
14627
14628
|
const REMOTE_FLEET_BINARY_FRAME_CACHE_LIMIT = 240;
|
|
14628
14629
|
const REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS = 8000;
|
|
14629
|
-
const REMOTE_FLEET_AUTO_LIVE_START_MAX_PARALLEL = 8;
|
|
14630
|
-
const REMOTE_FLEET_AUTO_LIVE_START_COOLDOWN_MS = 3000;
|
|
14631
14630
|
const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
|
|
14632
14631
|
const REMOTE_FLEET_TASK_FOLLOW_REFRESH_MS = 2000;
|
|
14633
14632
|
const REMOTE_FLEET_TASK_FOLLOW_MAX_TICKS = 60;
|
|
@@ -15690,12 +15689,13 @@
|
|
|
15690
15689
|
}
|
|
15691
15690
|
session.bodies.add(bodyView);
|
|
15692
15691
|
session.getDeviceIds = getDeviceIds;
|
|
15693
|
-
|
|
15692
|
+
const fps = Math.max(1, Math.min(30, Number(options.fps || REMOTE_FLEET_MONITOR_LIVE_FPS) || REMOTE_FLEET_MONITOR_LIVE_FPS));
|
|
15693
|
+
session.options = { fps, width: Number(options.width || 1920) || 1920, height: Number(options.height || 1080) || 1080, tileWidth: Number(options.tileWidth || 320) || 320, tileHeight: Number(options.tileHeight || 180) || 180 };
|
|
15694
15694
|
const fallback = () => {
|
|
15695
15695
|
if (session.fallbackStarted) return;
|
|
15696
15696
|
session.fallbackStarted = true;
|
|
15697
15697
|
try { session.ws?.close?.(); } catch { /* encoder failure fallback */ }
|
|
15698
|
-
session.bodies.forEach(body => startRemoteFleetBinaryFrameSocket(body, session.getDeviceIds, { autoStartLive: true, fps:
|
|
15698
|
+
session.bodies.forEach(body => startRemoteFleetBinaryFrameSocket(body, session.getDeviceIds, { autoStartLive: true, fps: session.options.fps, maxWidth: 960, maxHeight: 540, quality: 60, mode: 'mode2-lzo', streamPurpose: 'wall' }));
|
|
15699
15699
|
window.RuntimeTrace?.emit?.('remote.atlas.fallback', { nodeId: session.nodeId, mode: 'mode2-lzo' });
|
|
15700
15700
|
};
|
|
15701
15701
|
session.fallback = fallback;
|
|
@@ -15709,7 +15709,7 @@
|
|
|
15709
15709
|
ws.binaryType = 'arraybuffer';
|
|
15710
15710
|
ws.onopen = () => {
|
|
15711
15711
|
session.wsOpening = false;
|
|
15712
|
-
ws.send(JSON.stringify({ type: 'configure', deviceIds: getRemoteFleetBinaryFrameDeviceIds(session), fps:
|
|
15712
|
+
ws.send(JSON.stringify({ type: 'configure', deviceIds: getRemoteFleetBinaryFrameDeviceIds(session), fps: session.options.fps, width: session.options.width, height: session.options.height, tileWidth: session.options.tileWidth, tileHeight: session.options.tileHeight }));
|
|
15713
15713
|
window.RuntimeTrace?.emit?.('remote.atlas.wsOpen', { nodeId: session.nodeId });
|
|
15714
15714
|
};
|
|
15715
15715
|
ws.onmessage = event => {
|
|
@@ -17273,16 +17273,6 @@
|
|
|
17273
17273
|
}
|
|
17274
17274
|
|
|
17275
17275
|
binarySession.controlSessions.add(controlSession);
|
|
17276
|
-
binarySession.liveOptions = {
|
|
17277
|
-
...(binarySession.liveOptions || {}),
|
|
17278
|
-
autoStartLive: true,
|
|
17279
|
-
fps: REMOTE_FLEET_CONTROL_LIVE_FPS,
|
|
17280
|
-
maxWidth: 1280,
|
|
17281
|
-
maxHeight: 720,
|
|
17282
|
-
quality: 65,
|
|
17283
|
-
mode: 'mode3-h264-hw',
|
|
17284
|
-
streamPurpose: 'control'
|
|
17285
|
-
};
|
|
17286
17276
|
controlSession.binaryFrameSession = binarySession;
|
|
17287
17277
|
controlSession.binaryFramePreferred = true;
|
|
17288
17278
|
refreshRemoteFleetBinaryFrameSubscription(binarySession, true);
|
|
@@ -17368,7 +17358,7 @@
|
|
|
17368
17358
|
session.canvas.dataset.remoteFleetFrameSeq = String(frameSeq || 0);
|
|
17369
17359
|
session.canvas.style.display = 'block';
|
|
17370
17360
|
session.lastControlFrameAt = Date.now();
|
|
17371
|
-
session.status.textContent =
|
|
17361
|
+
session.status.textContent = `${session.statusLabel || 'Control'} ${Number(session.fps || REMOTE_FLEET_CONTROL_LIVE_FPS)}fps`;
|
|
17372
17362
|
session.placeholder?.remove?.();
|
|
17373
17363
|
session.placeholder = null;
|
|
17374
17364
|
}
|
|
@@ -17403,7 +17393,7 @@
|
|
|
17403
17393
|
session.canvas.dataset.remoteFleetFrameUrl = String(frame.frameUrl || '');
|
|
17404
17394
|
session.canvas.style.display = 'block';
|
|
17405
17395
|
session.lastControlFrameAt = Date.now();
|
|
17406
|
-
session.status.textContent =
|
|
17396
|
+
session.status.textContent = `${session.statusLabel || 'Control'} ${Number(session.fps || REMOTE_FLEET_CONTROL_LIVE_FPS)}fps`;
|
|
17407
17397
|
session.placeholder?.remove?.();
|
|
17408
17398
|
session.placeholder = null;
|
|
17409
17399
|
if (typeof bitmap.close === 'function') {
|
|
@@ -18152,7 +18142,9 @@
|
|
|
18152
18142
|
wasLiveActive: false,
|
|
18153
18143
|
startedStream: false,
|
|
18154
18144
|
streamId: '',
|
|
18155
|
-
controlLeaseId: `
|
|
18145
|
+
controlLeaseId: `observe-node-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
18146
|
+
statusLabel: 'Observe',
|
|
18147
|
+
fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
|
|
18156
18148
|
timer: null,
|
|
18157
18149
|
moveTimer: null,
|
|
18158
18150
|
pendingMove: null,
|
|
@@ -18170,25 +18162,21 @@
|
|
|
18170
18162
|
};
|
|
18171
18163
|
|
|
18172
18164
|
bodyView._remoteFleetControlNodeSession = session;
|
|
18173
|
-
bindRemoteFleetControlInput(session, {
|
|
18174
|
-
captureOverlayEvents: false,
|
|
18175
|
-
windowKeyboardCapture: false
|
|
18176
|
-
});
|
|
18177
|
-
ensureRemoteFleetControlInputSocket(session);
|
|
18178
18165
|
startRemoteFleetBinaryFrameSocket(bodyView, () => [normalizedDeviceId], {
|
|
18179
18166
|
autoStartLive: true,
|
|
18180
|
-
fps:
|
|
18181
|
-
maxWidth:
|
|
18182
|
-
maxHeight:
|
|
18183
|
-
quality:
|
|
18184
|
-
mode: '
|
|
18167
|
+
fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
|
|
18168
|
+
maxWidth: 960,
|
|
18169
|
+
maxHeight: 540,
|
|
18170
|
+
quality: 60,
|
|
18171
|
+
mode: 'mode2-lzo',
|
|
18172
|
+
streamPurpose: 'wall'
|
|
18185
18173
|
});
|
|
18186
18174
|
attachRemoteFleetControlBinaryFrameSession(session, bodyView);
|
|
18187
18175
|
stage.focus?.({ preventScroll: true });
|
|
18188
18176
|
if (status) {
|
|
18189
|
-
status.textContent = `
|
|
18177
|
+
status.textContent = `Observe ${REMOTE_FLEET_MONITOR_LIVE_FPS}fps`;
|
|
18190
18178
|
}
|
|
18191
|
-
window.RuntimeTrace?.emit?.('remote.
|
|
18179
|
+
window.RuntimeTrace?.emit?.('remote.observe.nodeOpened', {
|
|
18192
18180
|
nodeId: normalizedNodeId,
|
|
18193
18181
|
deviceId: normalizedDeviceId
|
|
18194
18182
|
});
|
|
@@ -18440,7 +18428,7 @@
|
|
|
18440
18428
|
|
|
18441
18429
|
if (!device || !deviceId) {
|
|
18442
18430
|
const empty = document.createElement('div');
|
|
18443
|
-
empty.textContent = lastError || 'Remote
|
|
18431
|
+
empty.textContent = lastError || 'Remote workstation snapshot is unavailable.';
|
|
18444
18432
|
empty.style.cssText = `
|
|
18445
18433
|
flex: 1 1 auto;
|
|
18446
18434
|
display: flex;
|
|
@@ -18456,7 +18444,7 @@
|
|
|
18456
18444
|
text-align: center;
|
|
18457
18445
|
background: rgba(255, 255, 255, 0.74);
|
|
18458
18446
|
`;
|
|
18459
|
-
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this remote
|
|
18447
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this remote workstation', 'refresh-device');
|
|
18460
18448
|
refreshButton.style.height = '32px';
|
|
18461
18449
|
refreshButton.addEventListener('click', async event => {
|
|
18462
18450
|
event.preventDefault();
|
|
@@ -18579,7 +18567,7 @@
|
|
|
18579
18567
|
controlStage.dataset.remoteFleetControlNodeStage = 'true';
|
|
18580
18568
|
controlStage.dataset.deviceId = deviceId;
|
|
18581
18569
|
controlStage.tabIndex = 0;
|
|
18582
|
-
controlStage.title = `${name} remote
|
|
18570
|
+
controlStage.title = `${name} observation and remote work`;
|
|
18583
18571
|
controlStage.style.cssText = `
|
|
18584
18572
|
position: relative;
|
|
18585
18573
|
flex: 1 1 auto;
|
|
@@ -18589,7 +18577,7 @@
|
|
|
18589
18577
|
border-radius: 0;
|
|
18590
18578
|
background: #020617;
|
|
18591
18579
|
border: 1px solid rgba(15, 23, 42, 0.20);
|
|
18592
|
-
cursor:
|
|
18580
|
+
cursor: default;
|
|
18593
18581
|
touch-action: none;
|
|
18594
18582
|
outline: none;
|
|
18595
18583
|
`;
|
|
@@ -18637,7 +18625,7 @@
|
|
|
18637
18625
|
`;
|
|
18638
18626
|
const controlPlaceholder = document.createElement('div');
|
|
18639
18627
|
controlPlaceholder.dataset.remoteFleetControlNodePlaceholder = 'true';
|
|
18640
|
-
controlPlaceholder.textContent = connected ? 'Waiting for
|
|
18628
|
+
controlPlaceholder.textContent = connected ? 'Waiting for observation frame' : 'Offline';
|
|
18641
18629
|
controlPlaceholder.style.cssText = `
|
|
18642
18630
|
position: absolute;
|
|
18643
18631
|
inset: 0;
|
|
@@ -18672,7 +18660,7 @@
|
|
|
18672
18660
|
if (session && restoredControlSurface) {
|
|
18673
18661
|
session.lastControlFrameSeq = Number(preservedControlSurface?.seq || session.lastControlFrameSeq || 0) || 0;
|
|
18674
18662
|
session.lastControlFrameAt = Date.now();
|
|
18675
|
-
controlStatus.textContent = `
|
|
18663
|
+
controlStatus.textContent = `Observe ${REMOTE_FLEET_MONITOR_LIVE_FPS}fps`;
|
|
18676
18664
|
window.RuntimeTrace?.emit?.('remote.control.surfaceRestored', {
|
|
18677
18665
|
nodeId,
|
|
18678
18666
|
deviceId,
|
|
@@ -18692,7 +18680,7 @@
|
|
|
18692
18680
|
}
|
|
18693
18681
|
}
|
|
18694
18682
|
|
|
18695
|
-
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this remote
|
|
18683
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this remote workstation', 'refresh-device');
|
|
18696
18684
|
refreshButton.style.height = '26px';
|
|
18697
18685
|
actions.appendChild(refreshButton);
|
|
18698
18686
|
if (connected && thumbnailEnabled) {
|
|
@@ -18733,8 +18721,8 @@
|
|
|
18733
18721
|
right: 8px;
|
|
18734
18722
|
top: 42px;
|
|
18735
18723
|
z-index: 6;
|
|
18736
|
-
width: min(
|
|
18737
|
-
display:
|
|
18724
|
+
width: min(520px, calc(100% - 16px));
|
|
18725
|
+
display: grid;
|
|
18738
18726
|
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
18739
18727
|
gap: 6px;
|
|
18740
18728
|
align-items: stretch;
|
|
@@ -18979,8 +18967,7 @@
|
|
|
18979
18967
|
|
|
18980
18968
|
function renderRemoteFleetMonitor(bodyView, nodeModel) {
|
|
18981
18969
|
if (!bodyView) return;
|
|
18982
|
-
clearRemoteFleetTimers(bodyView
|
|
18983
|
-
const preservedFrameSurfaces = collectRemoteFleetPreviewFrameSurfaces(bodyView);
|
|
18970
|
+
clearRemoteFleetTimers(bodyView);
|
|
18984
18971
|
|
|
18985
18972
|
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
18986
18973
|
const searchState = bodyView.dataset.remoteFleetSearch || '';
|
|
@@ -18990,12 +18977,11 @@
|
|
|
18990
18977
|
const densityState = normalizeRemoteFleetMonitorSize(bodyView.dataset.remoteFleetDensity || 'cards');
|
|
18991
18978
|
const aiAssistState = bodyView.dataset.remoteFleetAiAssist === 'true';
|
|
18992
18979
|
const autoMonitorState = bodyView.dataset.remoteFleetAutoMonitor !== 'false';
|
|
18993
|
-
const focusState = bodyView.dataset.remoteFleetFocusDeviceId || '';
|
|
18994
18980
|
const latestTaskBatch = parseRemoteFleetLatestTaskBatch(nodeModel);
|
|
18995
18981
|
const recentTaskBatches = parseRemoteFleetRecentTaskBatches(nodeModel);
|
|
18996
18982
|
const latestTaskBatchKey = getRemoteFleetTaskBatchKey(latestTaskBatch);
|
|
18997
18983
|
const latestTaskBatchActive = isRemoteFleetTaskBatchActive(latestTaskBatch);
|
|
18998
|
-
const showTaskSummarySurfaces =
|
|
18984
|
+
const showTaskSummarySurfaces = true;
|
|
18999
18985
|
if (latestTaskBatchKey && bodyView.dataset.remoteFleetTaskFollowKey !== latestTaskBatchKey) {
|
|
19000
18986
|
bodyView.dataset.remoteFleetTaskFollowKey = latestTaskBatchKey;
|
|
19001
18987
|
bodyView.dataset.remoteFleetTaskFollowTicks = '0';
|
|
@@ -19009,18 +18995,10 @@
|
|
|
19009
18995
|
: [...sortedDevices].sort((left, right) =>
|
|
19010
18996
|
compareRemoteFleetGroups(left, right, groupState)
|
|
19011
18997
|
|| compareRemoteFleetDevices(left, right, sortState));
|
|
19012
|
-
const hasActiveLiveStream = devices.some(isRemoteFleetLiveActive);
|
|
19013
18998
|
const hasMonitorTargets = devices.some(device =>
|
|
19014
18999
|
isRemoteFleetDeviceConnected(device) && isRemoteFleetThumbnailCapable(device));
|
|
19015
|
-
const
|
|
19016
|
-
|| devices.find(device => isRemoteFleetLiveActive(device))
|
|
19017
|
-
|| null;
|
|
19018
|
-
if (focusedDevice) {
|
|
19019
|
-
bodyView.dataset.remoteFleetFocusDeviceId = getRemoteFleetDeviceId(focusedDevice);
|
|
19020
|
-
}
|
|
19021
|
-
const selectedState = bodyView.dataset.remoteFleetSelectedDeviceId || focusState || '';
|
|
19000
|
+
const selectedState = bodyView.dataset.remoteFleetSelectedDeviceId || '';
|
|
19022
19001
|
const selectedDevice = devices.find(device => getRemoteFleetDeviceId(device) === selectedState)
|
|
19023
|
-
|| focusedDevice
|
|
19024
19002
|
|| devices[0]
|
|
19025
19003
|
|| null;
|
|
19026
19004
|
if (selectedDevice) {
|
|
@@ -19147,7 +19125,7 @@
|
|
|
19147
19125
|
const searchInput = document.createElement('input');
|
|
19148
19126
|
searchInput.type = 'search';
|
|
19149
19127
|
searchInput.value = searchState;
|
|
19150
|
-
searchInput.placeholder = '';
|
|
19128
|
+
searchInput.placeholder = 'Find a computer or task';
|
|
19151
19129
|
searchInput.dataset.remoteFleetSearch = 'true';
|
|
19152
19130
|
searchInput.autocomplete = 'off';
|
|
19153
19131
|
searchInput.spellcheck = false;
|
|
@@ -19172,8 +19150,7 @@
|
|
|
19172
19150
|
{ value: 'offline', label: 'Offline' },
|
|
19173
19151
|
{ value: 'task', label: 'Task' },
|
|
19174
19152
|
{ value: 'ai', label: 'AI' },
|
|
19175
|
-
{ value: '
|
|
19176
|
-
{ value: 'frames', label: 'Frames' },
|
|
19153
|
+
{ value: 'frames', label: 'Thumbnails' },
|
|
19177
19154
|
{ value: 'issues', label: 'Issues' }
|
|
19178
19155
|
], filterState, 'Device filter');
|
|
19179
19156
|
|
|
@@ -19220,11 +19197,11 @@
|
|
|
19220
19197
|
filterRow.appendChild(groupSelect);
|
|
19221
19198
|
filterRow.appendChild(densitySelect);
|
|
19222
19199
|
filterRow.appendChild(matchCount);
|
|
19223
|
-
filterRow.dataset.remoteFleetAdvancedFilters = '
|
|
19200
|
+
filterRow.dataset.remoteFleetAdvancedFilters = 'visible';
|
|
19224
19201
|
|
|
19225
19202
|
const taskRow = document.createElement('div');
|
|
19226
19203
|
taskRow.style.cssText = `
|
|
19227
|
-
display:
|
|
19204
|
+
display: grid;
|
|
19228
19205
|
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
|
19229
19206
|
gap: 8px;
|
|
19230
19207
|
align-items: stretch;
|
|
@@ -19232,7 +19209,7 @@
|
|
|
19232
19209
|
`;
|
|
19233
19210
|
const taskInput = document.createElement('textarea');
|
|
19234
19211
|
taskInput.dataset.remoteFleetTaskInput = 'true';
|
|
19235
|
-
taskInput.placeholder = '';
|
|
19212
|
+
taskInput.placeholder = 'Describe the work: inspect Git, run tests, build, deploy, or summarize results';
|
|
19236
19213
|
taskInput.rows = 2;
|
|
19237
19214
|
taskInput.style.cssText = `
|
|
19238
19215
|
width: 100%;
|
|
@@ -19286,7 +19263,7 @@
|
|
|
19286
19263
|
aiToggleText.textContent = 'AI';
|
|
19287
19264
|
aiToggleLabel.appendChild(aiToggle);
|
|
19288
19265
|
aiToggleLabel.appendChild(aiToggleText);
|
|
19289
|
-
const sendVisibleButton = createRemoteFleetButton('Send
|
|
19266
|
+
const sendVisibleButton = createRemoteFleetButton('Send selected', 'Dispatch task to the selected agent', 'task-selected');
|
|
19290
19267
|
sendVisibleButton.style.height = '54px';
|
|
19291
19268
|
const sendConnectedButton = createRemoteFleetButton('Send all', 'Dispatch task to all connected task-capable devices', 'task-connected');
|
|
19292
19269
|
sendConnectedButton.style.height = '54px';
|
|
@@ -19294,7 +19271,7 @@
|
|
|
19294
19271
|
taskRow.appendChild(aiToggleLabel);
|
|
19295
19272
|
taskRow.appendChild(sendVisibleButton);
|
|
19296
19273
|
taskRow.appendChild(sendConnectedButton);
|
|
19297
|
-
taskRow.dataset.remoteFleetTaskComposer = '
|
|
19274
|
+
taskRow.dataset.remoteFleetTaskComposer = 'visible';
|
|
19298
19275
|
|
|
19299
19276
|
const taskFeedback = document.createElement('div');
|
|
19300
19277
|
taskFeedback.dataset.remoteFleetTaskFeedback = 'true';
|
|
@@ -19313,6 +19290,8 @@
|
|
|
19313
19290
|
text-overflow: ellipsis;
|
|
19314
19291
|
white-space: nowrap;
|
|
19315
19292
|
`;
|
|
19293
|
+
bodyView.appendChild(filterRow);
|
|
19294
|
+
bodyView.appendChild(taskRow);
|
|
19316
19295
|
bodyView.appendChild(taskFeedback);
|
|
19317
19296
|
|
|
19318
19297
|
if (showTaskSummarySurfaces && latestTaskBatch) {
|
|
@@ -19484,23 +19463,16 @@
|
|
|
19484
19463
|
const createDevicePreview = (device, mode = 'tile') => {
|
|
19485
19464
|
const name = getRemoteFleetDeviceName(device);
|
|
19486
19465
|
const connectedDevice = isRemoteFleetDeviceConnected(device);
|
|
19487
|
-
const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
|
|
19488
|
-
const liveFrameReceivedAt = String(device?.liveFrameReceivedAt || device?.LiveFrameReceivedAt || '');
|
|
19489
19466
|
const hasThumbnail = hasRemoteFleetThumbnail(device);
|
|
19490
|
-
const
|
|
19491
|
-
const previewSource = hasLiveFrame
|
|
19492
|
-
? getRemoteFleetFrameSource(device, 'live')
|
|
19493
|
-
: getRemoteFleetFrameSource(device, 'thumbnail');
|
|
19467
|
+
const previewSource = getRemoteFleetFrameSource(device, 'thumbnail');
|
|
19494
19468
|
const hasPreviewSource = isRemoteFleetFrameSource(previewSource);
|
|
19495
|
-
const isDetail = mode === 'detail';
|
|
19496
19469
|
|
|
19497
19470
|
const preview = document.createElement('div');
|
|
19498
19471
|
preview.dataset.remoteFleetDevicePreview = mode;
|
|
19499
19472
|
preview.dataset.deviceId = getRemoteFleetDeviceId(device);
|
|
19500
|
-
preview.dataset.remoteFleetFrameKind =
|
|
19501
|
-
preview.dataset.remoteFleetFrameSeq = String(
|
|
19502
|
-
|
|
19503
|
-
: getRemoteFleetDeviceField(device, 'thumbnailFrameSeq', 'ThumbnailFrameSeq', 0));
|
|
19473
|
+
preview.dataset.remoteFleetFrameKind = hasThumbnail ? 'thumbnail' : '';
|
|
19474
|
+
preview.dataset.remoteFleetFrameSeq = String(
|
|
19475
|
+
getRemoteFleetDeviceField(device, 'thumbnailFrameSeq', 'ThumbnailFrameSeq', 0));
|
|
19504
19476
|
preview.title = getRemoteFleetDeviceTooltip(device);
|
|
19505
19477
|
preview.style.cssText = `
|
|
19506
19478
|
position: relative;
|
|
@@ -19516,13 +19488,13 @@
|
|
|
19516
19488
|
`;
|
|
19517
19489
|
ensureRemoteFleetFrameCanvas(preview);
|
|
19518
19490
|
|
|
19519
|
-
if (
|
|
19491
|
+
if (hasThumbnail && hasPreviewSource) {
|
|
19520
19492
|
const image = document.createElement('img');
|
|
19521
19493
|
image.dataset.remoteFleetFrameImage = 'true';
|
|
19522
|
-
image.dataset.remoteFleetFrameKind =
|
|
19494
|
+
image.dataset.remoteFleetFrameKind = 'thumbnail';
|
|
19523
19495
|
image.dataset.remoteFleetFrameSeq = preview.dataset.remoteFleetFrameSeq;
|
|
19524
19496
|
image.src = previewSource;
|
|
19525
|
-
image.alt =
|
|
19497
|
+
image.alt = `${name} thumbnail`;
|
|
19526
19498
|
image.loading = 'lazy';
|
|
19527
19499
|
image.decoding = 'async';
|
|
19528
19500
|
image.style.cssText = `
|
|
@@ -19543,7 +19515,6 @@
|
|
|
19543
19515
|
preview.appendChild(placeholder);
|
|
19544
19516
|
}
|
|
19545
19517
|
|
|
19546
|
-
restoreRemoteFleetPreviewFrameSurface(preview, preservedFrameSurfaces, getRemoteFleetDeviceId(device));
|
|
19547
19518
|
return preview;
|
|
19548
19519
|
};
|
|
19549
19520
|
|
|
@@ -19611,10 +19582,7 @@
|
|
|
19611
19582
|
const connectedDevice = isRemoteFleetDeviceConnected(device);
|
|
19612
19583
|
const name = getRemoteFleetDeviceName(device);
|
|
19613
19584
|
const deviceId = getRemoteFleetDeviceId(device);
|
|
19614
|
-
const liveStreamEnabled = isRemoteFleetLiveCapable(device);
|
|
19615
|
-
const liveStreamActive = isRemoteFleetLiveActive(device);
|
|
19616
19585
|
const hasThumbnail = hasRemoteFleetThumbnail(device);
|
|
19617
|
-
const hasLiveFrame = hasRemoteFleetLiveFrame(device);
|
|
19618
19586
|
const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
|
|
19619
19587
|
const aiAssistEnabled = isRemoteFleetDeviceAiCapable(device);
|
|
19620
19588
|
const latestTaskStatus = String(device?.latestTaskStatus || device?.LatestTaskStatus || '');
|
|
@@ -19662,15 +19630,13 @@
|
|
|
19662
19630
|
card.dataset.remoteFleetConnected = connectedDevice ? 'true' : 'false';
|
|
19663
19631
|
card.dataset.remoteFleetTaskCapable = taskEnabled ? 'true' : 'false';
|
|
19664
19632
|
card.dataset.remoteFleetAiCapable = aiAssistEnabled ? 'true' : 'false';
|
|
19665
|
-
card.dataset.remoteFleetHasFrame =
|
|
19666
|
-
card.dataset.remoteFleetLiveCapable = liveStreamEnabled ? 'true' : 'false';
|
|
19667
|
-
card.dataset.remoteFleetLiveActive = liveStreamActive ? 'true' : 'false';
|
|
19633
|
+
card.dataset.remoteFleetHasFrame = hasThumbnail ? 'true' : 'false';
|
|
19668
19634
|
card.dataset.remoteFleetIssue = (!connectedDevice || latestTaskStatus === 'failed' || !!latestTaskError) ? 'true' : 'false';
|
|
19669
19635
|
card.dataset.remoteFleetSelected = isSelected ? 'true' : 'false';
|
|
19670
19636
|
card.dataset.remoteFleetAction = 'select-device';
|
|
19671
19637
|
card.setAttribute('role', 'button');
|
|
19672
19638
|
card.setAttribute('aria-pressed', isSelected ? 'true' : 'false');
|
|
19673
|
-
card.setAttribute('aria-label', `
|
|
19639
|
+
card.setAttribute('aria-label', `Select ${name} as the agent task target`);
|
|
19674
19640
|
card.tabIndex = 0;
|
|
19675
19641
|
card.title = getRemoteFleetDeviceTooltip(device);
|
|
19676
19642
|
card.style.cssText = `
|
|
@@ -19778,121 +19744,12 @@
|
|
|
19778
19744
|
const readTaskInstruction = () => String(taskInput.value || '').trim();
|
|
19779
19745
|
const useAiAssist = () => aiToggle.checked === true;
|
|
19780
19746
|
const getDeviceCards = () => Array.from(grid.querySelectorAll('article[data-device-id]'));
|
|
19781
|
-
const
|
|
19782
|
-
|
|
19783
|
-
if (id && !ids.includes(id)) {
|
|
19784
|
-
ids.push(id);
|
|
19785
|
-
}
|
|
19786
|
-
};
|
|
19787
|
-
const getVisibleFrameDeviceIds = () => {
|
|
19788
|
-
const ids = getDeviceCards()
|
|
19747
|
+
const getVisibleFrameDeviceIds = () => Array.from(new Set(
|
|
19748
|
+
getDeviceCards()
|
|
19789
19749
|
.filter(card => card.style.display !== 'none')
|
|
19790
19750
|
.map(card => String(card.dataset.deviceId || '').trim())
|
|
19791
|
-
.filter(Boolean)
|
|
19792
|
-
|
|
19793
|
-
};
|
|
19794
|
-
const getVisibleLiveDevices = () => {
|
|
19795
|
-
const visibleIds = new Set(getVisibleFrameDeviceIds());
|
|
19796
|
-
return devices
|
|
19797
|
-
.filter(device => {
|
|
19798
|
-
const id = getRemoteFleetDeviceId(device);
|
|
19799
|
-
return id
|
|
19800
|
-
&& visibleIds.has(id)
|
|
19801
|
-
&& isRemoteFleetDeviceConnected(device)
|
|
19802
|
-
&& isRemoteFleetLiveCapable(device);
|
|
19803
|
-
})
|
|
19804
|
-
.slice(0, 120);
|
|
19805
|
-
};
|
|
19806
|
-
const getVisibleLiveFrameDeviceIds = () => {
|
|
19807
|
-
const ids = [];
|
|
19808
|
-
getVisibleLiveDevices().forEach(device => pushUniqueFrameDeviceId(ids, device));
|
|
19809
|
-
devices
|
|
19810
|
-
.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetLiveActive(device))
|
|
19811
|
-
.forEach(device => pushUniqueFrameDeviceId(ids, device));
|
|
19812
|
-
return ids.slice(0, 120);
|
|
19813
|
-
};
|
|
19814
|
-
const ensureVisibleRemoteFleetLiveStreams = async () => {
|
|
19815
|
-
if (!(bodyView._remoteFleetAutoLiveStartInFlight instanceof Set)) {
|
|
19816
|
-
bodyView._remoteFleetAutoLiveStartInFlight = new Set();
|
|
19817
|
-
}
|
|
19818
|
-
if (!(bodyView._remoteFleetAutoLiveStartAttemptAt instanceof Map)) {
|
|
19819
|
-
bodyView._remoteFleetAutoLiveStartAttemptAt = new Map();
|
|
19820
|
-
}
|
|
19821
|
-
const now = Date.now();
|
|
19822
|
-
const targets = getVisibleLiveDevices()
|
|
19823
|
-
.filter(device => {
|
|
19824
|
-
const deviceId = getRemoteFleetDeviceId(device);
|
|
19825
|
-
const lastAttemptAt = Number(bodyView._remoteFleetAutoLiveStartAttemptAt.get(deviceId) || 0);
|
|
19826
|
-
return deviceId
|
|
19827
|
-
&& !isRemoteFleetLiveActive(device)
|
|
19828
|
-
&& !bodyView._remoteFleetAutoLiveStartInFlight.has(deviceId)
|
|
19829
|
-
&& (now - lastAttemptAt >= REMOTE_FLEET_AUTO_LIVE_START_COOLDOWN_MS);
|
|
19830
|
-
});
|
|
19831
|
-
if (targets.length === 0) {
|
|
19832
|
-
return null;
|
|
19833
|
-
}
|
|
19834
|
-
|
|
19835
|
-
let started = 0;
|
|
19836
|
-
let lastResult = null;
|
|
19837
|
-
const queue = targets.slice(0, 120);
|
|
19838
|
-
const startOne = async target => {
|
|
19839
|
-
const deviceId = getRemoteFleetDeviceId(target);
|
|
19840
|
-
if (!deviceId || bodyView._remoteFleetAutoLiveStartInFlight.has(deviceId)) {
|
|
19841
|
-
return;
|
|
19842
|
-
}
|
|
19843
|
-
|
|
19844
|
-
bodyView._remoteFleetAutoLiveStartInFlight.add(deviceId);
|
|
19845
|
-
bodyView._remoteFleetAutoLiveStartAttemptAt.set(deviceId, Date.now());
|
|
19846
|
-
try {
|
|
19847
|
-
const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId, REMOTE_FLEET_MONITOR_LIVE_FPS);
|
|
19848
|
-
lastResult = result;
|
|
19849
|
-
const success = isRemoteFleetResultSuccess(result);
|
|
19850
|
-
window.RuntimeTrace?.emit?.('remote.live.autoStart', {
|
|
19851
|
-
nodeId,
|
|
19852
|
-
deviceId,
|
|
19853
|
-
success,
|
|
19854
|
-
fps: result?.fps || result?.Fps || 0,
|
|
19855
|
-
mode: 'visible-grid',
|
|
19856
|
-
error: result?.error || result?.Error || ''
|
|
19857
|
-
});
|
|
19858
|
-
if (success) {
|
|
19859
|
-
started += 1;
|
|
19860
|
-
}
|
|
19861
|
-
} finally {
|
|
19862
|
-
bodyView._remoteFleetAutoLiveStartInFlight.delete(deviceId);
|
|
19863
|
-
}
|
|
19864
|
-
};
|
|
19865
|
-
|
|
19866
|
-
const workerCount = Math.min(REMOTE_FLEET_AUTO_LIVE_START_MAX_PARALLEL, queue.length);
|
|
19867
|
-
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
19868
|
-
while (queue.length > 0) {
|
|
19869
|
-
const target = queue.shift();
|
|
19870
|
-
if (target) {
|
|
19871
|
-
await startOne(target);
|
|
19872
|
-
}
|
|
19873
|
-
}
|
|
19874
|
-
}));
|
|
19875
|
-
|
|
19876
|
-
if (started > 0) {
|
|
19877
|
-
try {
|
|
19878
|
-
await refreshRemoteFleetNode();
|
|
19879
|
-
} catch (error) {
|
|
19880
|
-
window.RuntimeTrace?.emit?.('remote.live.autoRefreshFailed', {
|
|
19881
|
-
nodeId,
|
|
19882
|
-
started,
|
|
19883
|
-
error: error?.message || String(error || '')
|
|
19884
|
-
});
|
|
19885
|
-
}
|
|
19886
|
-
|
|
19887
|
-
window.RuntimeTrace?.emit?.('remote.live.awaitingBinaryFrames', {
|
|
19888
|
-
nodeId,
|
|
19889
|
-
started,
|
|
19890
|
-
transport: 'ws-binary'
|
|
19891
|
-
});
|
|
19892
|
-
}
|
|
19893
|
-
|
|
19894
|
-
return lastResult || { success: started > 0, started };
|
|
19895
|
-
};
|
|
19751
|
+
.filter(Boolean)))
|
|
19752
|
+
.slice(0, 120);
|
|
19896
19753
|
const refreshRemoteFleetNode = async () => {
|
|
19897
19754
|
if (bodyView._remoteFleetRefreshInFlight === true) {
|
|
19898
19755
|
return null;
|
|
@@ -19909,7 +19766,9 @@
|
|
|
19909
19766
|
};
|
|
19910
19767
|
const refreshRemoteFleetVisibleFrames = async (requestThumbnails = false) => {
|
|
19911
19768
|
const result = await invokeDotNetAsync('RefreshRemoteFleetFramesFromJs', nodeId, getVisibleFrameDeviceIds(), requestThumbnails === true);
|
|
19912
|
-
|
|
19769
|
+
const thumbnailPatches = normalizeRemoteFleetDeviceFramePatches(result)
|
|
19770
|
+
.filter(frame => String(frame?.kind || '').toLowerCase() === 'thumbnail');
|
|
19771
|
+
applyRemoteFleetFramePatches(bodyView, thumbnailPatches);
|
|
19913
19772
|
return result;
|
|
19914
19773
|
};
|
|
19915
19774
|
const startRemoteFleetMonitorRefreshTimer = (intervalMs, reason = 'monitor') => {
|
|
@@ -20011,15 +19870,18 @@
|
|
|
20011
19870
|
scheduleRemoteFleetTaskFollow();
|
|
20012
19871
|
}
|
|
20013
19872
|
};
|
|
20014
|
-
const
|
|
19873
|
+
const getSelectedEligibleDeviceId = () => {
|
|
19874
|
+
const selectedId = String(bodyView.dataset.remoteFleetSelectedDeviceId || '').trim();
|
|
19875
|
+
if (!selectedId) return '';
|
|
19876
|
+
const card = getDeviceCards().find(item => String(item.dataset.deviceId || '').trim() === selectedId);
|
|
20015
19877
|
const wantsAi = useAiAssist();
|
|
20016
|
-
return
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
19878
|
+
return card
|
|
19879
|
+
&& card.style.display !== 'none'
|
|
19880
|
+
&& card.dataset.remoteFleetConnected === 'true'
|
|
19881
|
+
&& card.dataset.remoteFleetTaskCapable === 'true'
|
|
19882
|
+
&& (!wantsAi || card.dataset.remoteFleetAiCapable === 'true')
|
|
19883
|
+
? selectedId
|
|
19884
|
+
: '';
|
|
20023
19885
|
};
|
|
20024
19886
|
|
|
20025
19887
|
const applyRemoteFleetFilters = () => {
|
|
@@ -20038,7 +19900,6 @@
|
|
|
20038
19900
|
|| (filterMode === 'offline' && card.dataset.remoteFleetConnected !== 'true')
|
|
20039
19901
|
|| (filterMode === 'task' && card.dataset.remoteFleetConnected === 'true' && card.dataset.remoteFleetTaskCapable === 'true')
|
|
20040
19902
|
|| (filterMode === 'ai' && card.dataset.remoteFleetConnected === 'true' && card.dataset.remoteFleetAiCapable === 'true')
|
|
20041
|
-
|| (filterMode === 'live' && card.dataset.remoteFleetLiveActive === 'true')
|
|
20042
19903
|
|| (filterMode === 'frames' && card.dataset.remoteFleetHasFrame === 'true')
|
|
20043
19904
|
|| (filterMode === 'issues' && card.dataset.remoteFleetIssue === 'true');
|
|
20044
19905
|
const show = matchesSearch && matchesMode;
|
|
@@ -20072,10 +19933,11 @@
|
|
|
20072
19933
|
|
|
20073
19934
|
matchCount.textContent = `${visible}/${devices.length}`;
|
|
20074
19935
|
matchCount.title = `${visible} visible, ${eligible} targetable`;
|
|
20075
|
-
|
|
20076
|
-
sendVisibleButton.
|
|
20077
|
-
|
|
20078
|
-
|
|
19936
|
+
const selectedTargetId = getSelectedEligibleDeviceId();
|
|
19937
|
+
sendVisibleButton.disabled = !selectedTargetId;
|
|
19938
|
+
sendVisibleButton.title = selectedTargetId
|
|
19939
|
+
? `Dispatch to selected agent ${selectedTargetId}`
|
|
19940
|
+
: 'Select an online task-capable agent';
|
|
20079
19941
|
const allTargetCount = wantsAi ? aiCapableCount : taskCapableCount;
|
|
20080
19942
|
sendConnectedButton.disabled = allTargetCount <= 0;
|
|
20081
19943
|
sendConnectedButton.title = allTargetCount > 0
|
|
@@ -20149,6 +20011,7 @@
|
|
|
20149
20011
|
deviceId: normalizedSelectedId,
|
|
20150
20012
|
mode: 'dom-patch'
|
|
20151
20013
|
});
|
|
20014
|
+
applyRemoteFleetFilters();
|
|
20152
20015
|
};
|
|
20153
20016
|
getDeviceCards().forEach(card => {
|
|
20154
20017
|
['pointerdown', 'mousedown', 'mouseup'].forEach(eventName => {
|
|
@@ -20164,40 +20027,6 @@
|
|
|
20164
20027
|
event.stopImmediatePropagation?.();
|
|
20165
20028
|
});
|
|
20166
20029
|
});
|
|
20167
|
-
card.addEventListener('dblclick', event => {
|
|
20168
|
-
event.preventDefault();
|
|
20169
|
-
event.stopPropagation();
|
|
20170
|
-
event.stopImmediatePropagation?.();
|
|
20171
|
-
const deviceId = String(card.dataset.deviceId || '').trim();
|
|
20172
|
-
if (!deviceId) return;
|
|
20173
|
-
applySelectedDeviceVisualState(deviceId);
|
|
20174
|
-
if (card.dataset.remoteFleetOpeningControlNode === 'true') {
|
|
20175
|
-
return;
|
|
20176
|
-
}
|
|
20177
|
-
|
|
20178
|
-
card.dataset.remoteFleetOpeningControlNode = 'true';
|
|
20179
|
-
invokeDotNetAsync('AddRemoteFleetDeviceNodeFromJs', nodeId, deviceId)
|
|
20180
|
-
.then(async result => {
|
|
20181
|
-
await syncRemoteFleetNodeStateFromResult(result);
|
|
20182
|
-
if (result?.success) {
|
|
20183
|
-
const controlNodeId = String(result.nodeId || result.NodeId || '').trim();
|
|
20184
|
-
if (controlNodeId) {
|
|
20185
|
-
window.mindMap?.focusNode?.(controlNodeId, { selectNode: true });
|
|
20186
|
-
window.mindMap?.selectNode?.(controlNodeId);
|
|
20187
|
-
invokeDotNetAsync('SelectNodeInBlazor', controlNodeId).catch(() => undefined);
|
|
20188
|
-
}
|
|
20189
|
-
setTaskFeedback('');
|
|
20190
|
-
} else {
|
|
20191
|
-
setTaskFeedback(result?.error || result?.Error || 'Remote control node failed.', 'error');
|
|
20192
|
-
}
|
|
20193
|
-
})
|
|
20194
|
-
.catch(error => {
|
|
20195
|
-
setTaskFeedback(error?.message || 'Remote control node failed.', 'error');
|
|
20196
|
-
})
|
|
20197
|
-
.finally(() => {
|
|
20198
|
-
delete card.dataset.remoteFleetOpeningControlNode;
|
|
20199
|
-
});
|
|
20200
|
-
});
|
|
20201
20030
|
const selectCard = event => {
|
|
20202
20031
|
event.preventDefault();
|
|
20203
20032
|
event.stopPropagation();
|
|
@@ -20233,8 +20062,6 @@
|
|
|
20233
20062
|
scheduleRemoteFleetTaskFollow();
|
|
20234
20063
|
}
|
|
20235
20064
|
|
|
20236
|
-
primeRemoteFleetRenderedFrameSurfaces(bodyView, 'monitor-render');
|
|
20237
|
-
|
|
20238
20065
|
sendVisibleButton.addEventListener('click', async event => {
|
|
20239
20066
|
event.preventDefault();
|
|
20240
20067
|
event.stopPropagation();
|
|
@@ -20245,19 +20072,19 @@
|
|
|
20245
20072
|
return;
|
|
20246
20073
|
}
|
|
20247
20074
|
|
|
20248
|
-
const
|
|
20249
|
-
if (
|
|
20250
|
-
setTaskFeedback('
|
|
20075
|
+
const targetId = getSelectedEligibleDeviceId();
|
|
20076
|
+
if (!targetId) {
|
|
20077
|
+
setTaskFeedback('Select an online task-capable agent.', 'error');
|
|
20251
20078
|
return;
|
|
20252
20079
|
}
|
|
20253
20080
|
|
|
20254
20081
|
sendVisibleButton.disabled = true;
|
|
20255
|
-
setTaskFeedback('Dispatching task to
|
|
20082
|
+
setTaskFeedback('Dispatching task to selected agent...');
|
|
20256
20083
|
try {
|
|
20257
|
-
const result = await invokeDotNetAsync('
|
|
20084
|
+
const result = await invokeDotNetAsync('DispatchRemoteFleetTaskFromJs', nodeId, targetId, instruction, useAiAssist());
|
|
20258
20085
|
await syncRemoteFleetNodeStateFromResult(result);
|
|
20259
|
-
const mode = useAiAssist() ? '
|
|
20260
|
-
const feedback = formatRemoteFleetDispatchFeedback(result,
|
|
20086
|
+
const mode = useAiAssist() ? 'selected AI task' : 'selected agent task';
|
|
20087
|
+
const feedback = formatRemoteFleetDispatchFeedback(result, 1, mode);
|
|
20261
20088
|
setTaskFeedback(feedback.text, feedback.tone);
|
|
20262
20089
|
startRemoteFleetTaskFollowFromResult(result);
|
|
20263
20090
|
} finally {
|
|
@@ -20381,84 +20208,6 @@
|
|
|
20381
20208
|
});
|
|
20382
20209
|
});
|
|
20383
20210
|
|
|
20384
|
-
bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').forEach(button => {
|
|
20385
|
-
button.addEventListener('click', async event => {
|
|
20386
|
-
event.preventDefault();
|
|
20387
|
-
event.stopPropagation();
|
|
20388
|
-
const deviceId = String(button.dataset.deviceId || '').trim();
|
|
20389
|
-
if (!deviceId) return;
|
|
20390
|
-
button.disabled = true;
|
|
20391
|
-
setTaskFeedback('Pinning device to canvas...');
|
|
20392
|
-
try {
|
|
20393
|
-
const result = await invokeDotNetAsync('AddRemoteFleetDeviceNodeFromJs', nodeId, deviceId);
|
|
20394
|
-
await syncRemoteFleetNodeStateFromResult(result);
|
|
20395
|
-
if (result?.success) {
|
|
20396
|
-
setTaskFeedback('Pinned device node.', 'success');
|
|
20397
|
-
} else {
|
|
20398
|
-
setTaskFeedback(result?.error || 'Pin failed.', 'error');
|
|
20399
|
-
}
|
|
20400
|
-
} finally {
|
|
20401
|
-
button.disabled = false;
|
|
20402
|
-
}
|
|
20403
|
-
});
|
|
20404
|
-
});
|
|
20405
|
-
|
|
20406
|
-
bodyView.querySelectorAll('[data-remote-fleet-action="live-focus"]').forEach(button => {
|
|
20407
|
-
button.addEventListener('click', event => {
|
|
20408
|
-
event.preventDefault();
|
|
20409
|
-
event.stopPropagation();
|
|
20410
|
-
const deviceId = String(button.dataset.deviceId || '').trim();
|
|
20411
|
-
if (!deviceId) return;
|
|
20412
|
-
bodyView.dataset.remoteFleetFocusDeviceId = deviceId;
|
|
20413
|
-
renderRemoteFleetMonitor(bodyView, nodeModel);
|
|
20414
|
-
});
|
|
20415
|
-
});
|
|
20416
|
-
|
|
20417
|
-
bodyView.querySelectorAll('[data-remote-fleet-action="live-start"]').forEach(button => {
|
|
20418
|
-
button.addEventListener('click', async event => {
|
|
20419
|
-
event.preventDefault();
|
|
20420
|
-
event.stopPropagation();
|
|
20421
|
-
const deviceId = String(button.dataset.deviceId || '').trim();
|
|
20422
|
-
if (!deviceId) return;
|
|
20423
|
-
bodyView.dataset.remoteFleetFocusDeviceId = deviceId;
|
|
20424
|
-
button.disabled = true;
|
|
20425
|
-
setTaskFeedback('Starting focused live stream...');
|
|
20426
|
-
try {
|
|
20427
|
-
const result = await invokeDotNetAsync('StartRemoteFleetLiveStreamFromJs', nodeId, deviceId);
|
|
20428
|
-
await syncRemoteFleetNodeStateFromResult(result);
|
|
20429
|
-
if (result?.success) {
|
|
20430
|
-
setTaskFeedback('Focused live stream started.', 'success');
|
|
20431
|
-
} else {
|
|
20432
|
-
setTaskFeedback(result?.error || 'Live stream start failed.', 'error');
|
|
20433
|
-
}
|
|
20434
|
-
} finally {
|
|
20435
|
-
button.disabled = false;
|
|
20436
|
-
}
|
|
20437
|
-
});
|
|
20438
|
-
});
|
|
20439
|
-
|
|
20440
|
-
bodyView.querySelectorAll('[data-remote-fleet-action="live-stop"]').forEach(button => {
|
|
20441
|
-
button.addEventListener('click', async event => {
|
|
20442
|
-
event.preventDefault();
|
|
20443
|
-
event.stopPropagation();
|
|
20444
|
-
const deviceId = String(button.dataset.deviceId || '').trim();
|
|
20445
|
-
if (!deviceId) return;
|
|
20446
|
-
button.disabled = true;
|
|
20447
|
-
setTaskFeedback('Stopping focused live stream...');
|
|
20448
|
-
try {
|
|
20449
|
-
const result = await invokeDotNetAsync('StopRemoteFleetLiveStreamFromJs', nodeId, deviceId, button.dataset.streamId || '');
|
|
20450
|
-
await syncRemoteFleetNodeStateFromResult(result);
|
|
20451
|
-
if (result?.success) {
|
|
20452
|
-
setTaskFeedback('Focused live stream stopped.', 'success');
|
|
20453
|
-
} else {
|
|
20454
|
-
setTaskFeedback(result?.error || 'Live stream stop failed.', 'error');
|
|
20455
|
-
}
|
|
20456
|
-
} finally {
|
|
20457
|
-
button.disabled = false;
|
|
20458
|
-
}
|
|
20459
|
-
});
|
|
20460
|
-
});
|
|
20461
|
-
|
|
20462
20211
|
bodyView.querySelectorAll('[data-remote-fleet-action="ping-device"]').forEach(button => {
|
|
20463
20212
|
button.addEventListener('click', async event => {
|
|
20464
20213
|
event.preventDefault();
|
|
@@ -20525,65 +20274,13 @@
|
|
|
20525
20274
|
}, 250);
|
|
20526
20275
|
}
|
|
20527
20276
|
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
if (shouldUseBinaryFrameSocket) {
|
|
20536
|
-
const atlasSocketStarted = startRemoteFleetAtlasSocket(bodyView, () => {
|
|
20537
|
-
const liveIds = getVisibleLiveFrameDeviceIds();
|
|
20538
|
-
return liveIds.length > 0 ? liveIds : getVisibleFrameDeviceIds();
|
|
20539
|
-
}, {
|
|
20540
|
-
fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
|
|
20541
|
-
width: 1920,
|
|
20542
|
-
height: 1080,
|
|
20543
|
-
tileWidth: 320,
|
|
20544
|
-
tileHeight: 180
|
|
20545
|
-
});
|
|
20546
|
-
if (!atlasSocketStarted) {
|
|
20547
|
-
window.RuntimeTrace?.emit?.('remote.frame.wsUnavailable', {
|
|
20548
|
-
nodeId,
|
|
20549
|
-
reason: 'atlas-frame-socket-not-started'
|
|
20550
|
-
});
|
|
20551
|
-
ensureVisibleRemoteFleetLiveStreams().catch(error => {
|
|
20552
|
-
window.RuntimeTrace?.emit?.('remote.live.autoStartFailed', {
|
|
20553
|
-
nodeId,
|
|
20554
|
-
error: error?.message || String(error || '')
|
|
20555
|
-
});
|
|
20556
|
-
});
|
|
20557
|
-
} else {
|
|
20558
|
-
window.RuntimeTrace?.emit?.('remote.live.wsAutoStartRequested', {
|
|
20559
|
-
nodeId,
|
|
20560
|
-
transport: 'ws-atlas-h264',
|
|
20561
|
-
fps: REMOTE_FLEET_MONITOR_LIVE_FPS,
|
|
20562
|
-
visibleFrameTargets: visibleFrameDeviceIds.length,
|
|
20563
|
-
visibleLiveTargets: getVisibleLiveFrameDeviceIds().length
|
|
20564
|
-
});
|
|
20565
|
-
}
|
|
20566
|
-
bodyView._remoteFleetLiveRefreshTimer = setInterval(async () => {
|
|
20567
|
-
if (!document.body.contains(bodyView)) {
|
|
20568
|
-
clearRemoteFleetTimers(bodyView);
|
|
20569
|
-
return;
|
|
20570
|
-
}
|
|
20571
|
-
|
|
20572
|
-
try {
|
|
20573
|
-
await refreshRemoteFleetNode();
|
|
20574
|
-
} catch {
|
|
20575
|
-
clearRemoteFleetTimers(bodyView);
|
|
20576
|
-
}
|
|
20577
|
-
}, REMOTE_FLEET_LIVE_REFRESH_MS);
|
|
20578
|
-
} else {
|
|
20579
|
-
releaseRemoteFleetBinaryFrameSocket(bodyView);
|
|
20580
|
-
releaseRemoteFleetAtlasSocket(bodyView);
|
|
20581
|
-
if (autoMonitorState && hasMonitorTargets) {
|
|
20582
|
-
startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(true));
|
|
20583
|
-
startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_MONITOR_REFRESH_MS, 'visible-devices');
|
|
20584
|
-
} else if (autoMonitorState) {
|
|
20585
|
-
startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS, 'empty-discovery');
|
|
20586
|
-
}
|
|
20277
|
+
releaseRemoteFleetBinaryFrameSocket(bodyView);
|
|
20278
|
+
releaseRemoteFleetAtlasSocket(bodyView);
|
|
20279
|
+
if (autoMonitorState && hasMonitorTargets) {
|
|
20280
|
+
startRemoteFleetFrameLoop(bodyView, REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS, () => refreshRemoteFleetVisibleFrames(true));
|
|
20281
|
+
startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_MONITOR_REFRESH_MS, 'thumbnail-only');
|
|
20282
|
+
} else if (autoMonitorState) {
|
|
20283
|
+
startRemoteFleetMonitorRefreshTimer(REMOTE_FLEET_EMPTY_MONITOR_REFRESH_MS, 'empty-discovery');
|
|
20587
20284
|
}
|
|
20588
20285
|
}
|
|
20589
20286
|
|
|
@@ -21112,7 +20809,7 @@
|
|
|
21112
20809
|
* ▼▼▼ [New] text/markdown 노드를 위한 동적 DOM 요소 생성 ▼▼▼
|
|
21113
20810
|
* Blazor 템플릿이 없는 노드 타입을 위해 JS에서 직접 DOM 생성
|
|
21114
20811
|
*/
|
|
21115
|
-
// Expose helper to create markdown content consistently
|
|
20812
|
+
// Expose helper to create markdown content consistently
|
|
21116
20813
|
function createMarkdownContent(content, isCodeNode = false) {
|
|
21117
20814
|
const wrapper = document.createElement('div');
|
|
21118
20815
|
const normalizedContent = typeof content === 'string' ? content : String(content || '');
|
|
@@ -21169,22 +20866,22 @@
|
|
|
21169
20866
|
contentEl.textContent = nextText;
|
|
21170
20867
|
}
|
|
21171
20868
|
}
|
|
21172
|
-
|
|
21173
|
-
function getCachedMarkdownHtml(nodeModel, content) {
|
|
21174
|
-
if (!nodeModel) return null;
|
|
21175
|
-
if (nodeModel._cachedMarkdownSource === content && nodeModel._cachedMarkdownHtml) {
|
|
21176
|
-
return nodeModel._cachedMarkdownHtml;
|
|
21177
|
-
}
|
|
21178
|
-
return null;
|
|
21179
|
-
}
|
|
21180
|
-
|
|
21181
|
-
function setCachedMarkdownHtml(nodeModel, content, html) {
|
|
21182
|
-
if (!nodeModel) return;
|
|
21183
|
-
nodeModel._cachedMarkdownSource = content;
|
|
21184
|
-
nodeModel._cachedMarkdownHtml = html;
|
|
21185
|
-
}
|
|
21186
|
-
|
|
21187
|
-
// HTML 이스케이프 헬퍼 - kept for compatibility
|
|
20869
|
+
|
|
20870
|
+
function getCachedMarkdownHtml(nodeModel, content) {
|
|
20871
|
+
if (!nodeModel) return null;
|
|
20872
|
+
if (nodeModel._cachedMarkdownSource === content && nodeModel._cachedMarkdownHtml) {
|
|
20873
|
+
return nodeModel._cachedMarkdownHtml;
|
|
20874
|
+
}
|
|
20875
|
+
return null;
|
|
20876
|
+
}
|
|
20877
|
+
|
|
20878
|
+
function setCachedMarkdownHtml(nodeModel, content, html) {
|
|
20879
|
+
if (!nodeModel) return;
|
|
20880
|
+
nodeModel._cachedMarkdownSource = content;
|
|
20881
|
+
nodeModel._cachedMarkdownHtml = html;
|
|
20882
|
+
}
|
|
20883
|
+
|
|
20884
|
+
// HTML 이스케이프 헬퍼 - kept for compatibility
|
|
21188
20885
|
function escapeHtml(text) {
|
|
21189
20886
|
const div = document.createElement('div');
|
|
21190
20887
|
div.textContent = text;
|
|
@@ -23193,7 +22890,7 @@
|
|
|
23193
22890
|
ensurePromptCopyDelegation();
|
|
23194
22891
|
|
|
23195
22892
|
// ... (logging)
|
|
23196
|
-
|
|
22893
|
+
|
|
23197
22894
|
// 컨테이너 생성
|
|
23198
22895
|
const container = document.createElement('div');
|
|
23199
22896
|
container.id = `node-${nodeModel.id}`;
|
|
@@ -23217,7 +22914,7 @@
|
|
|
23217
22914
|
color: #1f2937;
|
|
23218
22915
|
box-shadow: none;
|
|
23219
22916
|
`;
|
|
23220
|
-
|
|
22917
|
+
|
|
23221
22918
|
// 콘텐츠 래퍼 생성
|
|
23222
22919
|
const contentWrapper = document.createElement('div');
|
|
23223
22920
|
contentWrapper.className = isCsvTableNode
|
|
@@ -23316,12 +23013,12 @@
|
|
|
23316
23013
|
// 응답 영역 생성
|
|
23317
23014
|
const responseDiv = document.createElement('div');
|
|
23318
23015
|
responseDiv.id = `node-response-${nodeModel.id}`;
|
|
23319
|
-
|
|
23320
|
-
// [FIX] 'note' type should also render markdown
|
|
23016
|
+
|
|
23017
|
+
// [FIX] 'note' type should also render markdown
|
|
23321
23018
|
const isCodeNode = contentTypeLower === 'code';
|
|
23322
23019
|
// Variable isNoteNode declared above
|
|
23323
23020
|
const isMarkdownType = ['text', 'markdown'].includes(contentTypeLower); // Removed 'note'
|
|
23324
|
-
|
|
23021
|
+
|
|
23325
23022
|
if (isCsvTableNode) {
|
|
23326
23023
|
responseDiv.className = 'node-response csv-table-scroll';
|
|
23327
23024
|
responseDiv.style.cssText = `
|
|
@@ -23387,44 +23084,44 @@
|
|
|
23387
23084
|
padding-right: 14px;
|
|
23388
23085
|
box-sizing: border-box;
|
|
23389
23086
|
`;
|
|
23390
|
-
|
|
23391
|
-
// 1. Save Metadata
|
|
23392
|
-
responseDiv.dataset.src = content;
|
|
23393
|
-
responseDiv.dataset.nodeId = nodeModel.id;
|
|
23394
|
-
|
|
23395
|
-
// 2. Render Markdown or Text
|
|
23396
|
-
const cachedHtml = getCachedMarkdownHtml(nodeModel, content);
|
|
23397
|
-
if (cachedHtml) {
|
|
23398
|
-
responseDiv.innerHTML = cachedHtml;
|
|
23399
|
-
} else if (typeof marked !== 'undefined' && content) {
|
|
23400
|
-
const options = { breaks: true, gfm: true };
|
|
23401
|
-
try {
|
|
23402
|
-
if (typeof marked.parse === 'function') {
|
|
23403
|
-
responseDiv.innerHTML = marked.parse(content, options);
|
|
23404
|
-
} else if (typeof marked === 'function') {
|
|
23405
|
-
responseDiv.innerHTML = marked(content, options);
|
|
23406
|
-
} else {
|
|
23407
|
-
responseDiv.innerText = content;
|
|
23408
|
-
}
|
|
23409
|
-
setCachedMarkdownHtml(nodeModel, content, responseDiv.innerHTML);
|
|
23410
|
-
} catch {
|
|
23411
|
-
responseDiv.innerText = content;
|
|
23412
|
-
}
|
|
23413
|
-
} else {
|
|
23414
|
-
responseDiv.innerText = content;
|
|
23415
|
-
}
|
|
23416
|
-
|
|
23417
|
-
// 3. (Removed) Click Event Listener
|
|
23418
|
-
// Centralized interaction logic in mind-map-interactions.js handles selection and editing.
|
|
23419
|
-
// Also pointer-events are none by default, so this wouldn't fire.
|
|
23420
|
-
|
|
23421
|
-
// Prevent drag interference (optional, but harmless)
|
|
23422
|
-
responseDiv.ondragstart = (e) => {
|
|
23423
|
-
if (e.target.tagName !== 'A' && !e.target.closest('a')) {
|
|
23424
|
-
e.preventDefault();
|
|
23425
|
-
}
|
|
23426
|
-
};
|
|
23427
|
-
|
|
23087
|
+
|
|
23088
|
+
// 1. Save Metadata
|
|
23089
|
+
responseDiv.dataset.src = content;
|
|
23090
|
+
responseDiv.dataset.nodeId = nodeModel.id;
|
|
23091
|
+
|
|
23092
|
+
// 2. Render Markdown or Text
|
|
23093
|
+
const cachedHtml = getCachedMarkdownHtml(nodeModel, content);
|
|
23094
|
+
if (cachedHtml) {
|
|
23095
|
+
responseDiv.innerHTML = cachedHtml;
|
|
23096
|
+
} else if (typeof marked !== 'undefined' && content) {
|
|
23097
|
+
const options = { breaks: true, gfm: true };
|
|
23098
|
+
try {
|
|
23099
|
+
if (typeof marked.parse === 'function') {
|
|
23100
|
+
responseDiv.innerHTML = marked.parse(content, options);
|
|
23101
|
+
} else if (typeof marked === 'function') {
|
|
23102
|
+
responseDiv.innerHTML = marked(content, options);
|
|
23103
|
+
} else {
|
|
23104
|
+
responseDiv.innerText = content;
|
|
23105
|
+
}
|
|
23106
|
+
setCachedMarkdownHtml(nodeModel, content, responseDiv.innerHTML);
|
|
23107
|
+
} catch {
|
|
23108
|
+
responseDiv.innerText = content;
|
|
23109
|
+
}
|
|
23110
|
+
} else {
|
|
23111
|
+
responseDiv.innerText = content;
|
|
23112
|
+
}
|
|
23113
|
+
|
|
23114
|
+
// 3. (Removed) Click Event Listener
|
|
23115
|
+
// Centralized interaction logic in mind-map-interactions.js handles selection and editing.
|
|
23116
|
+
// Also pointer-events are none by default, so this wouldn't fire.
|
|
23117
|
+
|
|
23118
|
+
// Prevent drag interference (optional, but harmless)
|
|
23119
|
+
responseDiv.ondragstart = (e) => {
|
|
23120
|
+
if (e.target.tagName !== 'A' && !e.target.closest('a')) {
|
|
23121
|
+
e.preventDefault();
|
|
23122
|
+
}
|
|
23123
|
+
};
|
|
23124
|
+
|
|
23428
23125
|
} else if (isMarkdownType) {
|
|
23429
23126
|
responseDiv.className = 'node-response markdown-body mind-map-text-scrollable-view';
|
|
23430
23127
|
// ▼▼▼ [FIX] 텍스트 선택을 위한 스타일 설정 ▼▼▼
|
|
@@ -23442,15 +23139,15 @@
|
|
|
23442
23139
|
user-select: text;
|
|
23443
23140
|
-webkit-user-select: text;
|
|
23444
23141
|
`;
|
|
23445
|
-
// ▲▲▲ [FIX] ▲▲▲
|
|
23446
|
-
const cachedHtml = getCachedMarkdownHtml(nodeModel, content);
|
|
23447
|
-
if (cachedHtml) {
|
|
23448
|
-
responseDiv.innerHTML = cachedHtml;
|
|
23449
|
-
} else {
|
|
23450
|
-
const html = createMarkdownContent(content);
|
|
23451
|
-
responseDiv.innerHTML = html;
|
|
23452
|
-
setCachedMarkdownHtml(nodeModel, content, html);
|
|
23453
|
-
}
|
|
23142
|
+
// ▲▲▲ [FIX] ▲▲▲
|
|
23143
|
+
const cachedHtml = getCachedMarkdownHtml(nodeModel, content);
|
|
23144
|
+
if (cachedHtml) {
|
|
23145
|
+
responseDiv.innerHTML = cachedHtml;
|
|
23146
|
+
} else {
|
|
23147
|
+
const html = createMarkdownContent(content);
|
|
23148
|
+
responseDiv.innerHTML = html;
|
|
23149
|
+
setCachedMarkdownHtml(nodeModel, content, html);
|
|
23150
|
+
}
|
|
23454
23151
|
} else if (contentTypeLower === 'image') {
|
|
23455
23152
|
responseDiv.className = 'node-response image-content';
|
|
23456
23153
|
responseDiv.style.cssText = `width: 100%; height: 100%; display: ${hasContent ? 'block' : 'none'}; position: relative; overflow: hidden; pointer-events: none; padding: 0 !important; margin: 0 !important; background: transparent; border: 0 !important; scrollbar-width: none;`;
|
|
@@ -23534,15 +23231,15 @@
|
|
|
23534
23231
|
|
|
23535
23232
|
return container;
|
|
23536
23233
|
}
|
|
23537
|
-
|
|
23538
|
-
// HTML 이스케이프 헬퍼
|
|
23539
|
-
function escapeHtml(text) {
|
|
23540
|
-
const div = document.createElement('div');
|
|
23541
|
-
div.textContent = text;
|
|
23542
|
-
return div.innerHTML;
|
|
23543
|
-
}
|
|
23544
|
-
// ▲▲▲ [New] ▲▲▲
|
|
23545
|
-
|
|
23234
|
+
|
|
23235
|
+
// HTML 이스케이프 헬퍼
|
|
23236
|
+
function escapeHtml(text) {
|
|
23237
|
+
const div = document.createElement('div');
|
|
23238
|
+
div.textContent = text;
|
|
23239
|
+
return div.innerHTML;
|
|
23240
|
+
}
|
|
23241
|
+
// ▲▲▲ [New] ▲▲▲
|
|
23242
|
+
|
|
23546
23243
|
function getNodeViewContentInfo(nodeModel) {
|
|
23547
23244
|
const contentTypeLower = String(nodeModel?.contentType ?? nodeModel?.ContentType ?? '').toLowerCase();
|
|
23548
23245
|
const remoteFleetMonitor = isRemoteFleetMonitorNode(nodeModel);
|
|
@@ -23800,7 +23497,7 @@
|
|
|
23800
23497
|
console.error('[MindMapCss3DManager] CSS3DObject not available');
|
|
23801
23498
|
return null;
|
|
23802
23499
|
}
|
|
23803
|
-
|
|
23500
|
+
|
|
23804
23501
|
const resolvedNodeId = getNodeId(nodeModel);
|
|
23805
23502
|
const templateId = `node-${resolvedNodeId}`;
|
|
23806
23503
|
let templateElement = document.getElementById(templateId);
|
|
@@ -23822,60 +23519,60 @@
|
|
|
23822
23519
|
|| contentTypeLower === 'embed') {
|
|
23823
23520
|
templateElement = null;
|
|
23824
23521
|
}
|
|
23825
|
-
|
|
23826
|
-
let isDynamicallyCreated = false;
|
|
23827
|
-
// text/markdown 타입은 Blazor 템플릿이 없으므로 동적 생성
|
|
23828
|
-
if (!templateElement) {
|
|
23829
|
-
// text/markdown type has no Blazor template, so create dynamically.
|
|
23830
|
-
// [FIX] 'note' type also falls back to dynamic creation if not found (or if we force dynamic for consistent MD)
|
|
23831
|
-
// But usually 'note' comes from Razor. If missing, we create it.
|
|
23522
|
+
|
|
23523
|
+
let isDynamicallyCreated = false;
|
|
23524
|
+
// text/markdown 타입은 Blazor 템플릿이 없으므로 동적 생성
|
|
23525
|
+
if (!templateElement) {
|
|
23526
|
+
// text/markdown type has no Blazor template, so create dynamically.
|
|
23527
|
+
// [FIX] 'note' type also falls back to dynamic creation if not found (or if we force dynamic for consistent MD)
|
|
23528
|
+
// But usually 'note' comes from Razor. If missing, we create it.
|
|
23832
23529
|
const dynamicTypes = ['text', 'markdown', 'code', 'note', 'memo', CSV_TABLE_CONTENT_TYPE, 'templatelauncher', 'image', 'video', 'embed'];
|
|
23833
23530
|
|
|
23834
23531
|
if (remoteFleetMonitor || dynamicTypes.includes(contentTypeLower)) {
|
|
23835
23532
|
log(`[MindMapCss3DManager] Deferring dynamic DOM element for ${visualContentTypeLower || contentTypeLower} node ${resolvedNodeId} to shared NodeView`);
|
|
23836
23533
|
templateElement = document.createElement('div');
|
|
23837
23534
|
isDynamicallyCreated = true;
|
|
23838
|
-
} else {
|
|
23839
|
-
// 경고 로그 제한 (초기 로딩 시 템플릿이 아직 렌더링되지 않은 경우 스팸 방지)
|
|
23840
|
-
if (!window._templateWarningCount) window._templateWarningCount = 0;
|
|
23841
|
-
if (window._templateWarningCount < 5) {
|
|
23535
|
+
} else {
|
|
23536
|
+
// 경고 로그 제한 (초기 로딩 시 템플릿이 아직 렌더링되지 않은 경우 스팸 방지)
|
|
23537
|
+
if (!window._templateWarningCount) window._templateWarningCount = 0;
|
|
23538
|
+
if (window._templateWarningCount < 5) {
|
|
23842
23539
|
warn(`[MindMapCss3DManager] Template not found for node ${resolvedNodeId} (async templates pending). Template ID: ${templateId}`);
|
|
23843
|
-
window._templateWarningCount++;
|
|
23844
|
-
if (window._templateWarningCount === 5) {
|
|
23845
|
-
warn(`[MindMapCss3DManager] ... suppressing further template warnings. Templates will be available after async render.`);
|
|
23846
|
-
}
|
|
23847
|
-
}
|
|
23848
|
-
return null;
|
|
23849
|
-
}
|
|
23850
|
-
}
|
|
23851
|
-
// ▲▲▲ [New] ▲▲▲
|
|
23852
|
-
|
|
23540
|
+
window._templateWarningCount++;
|
|
23541
|
+
if (window._templateWarningCount === 5) {
|
|
23542
|
+
warn(`[MindMapCss3DManager] ... suppressing further template warnings. Templates will be available after async render.`);
|
|
23543
|
+
}
|
|
23544
|
+
}
|
|
23545
|
+
return null;
|
|
23546
|
+
}
|
|
23547
|
+
}
|
|
23548
|
+
// ▲▲▲ [New] ▲▲▲
|
|
23549
|
+
|
|
23853
23550
|
const existingCss3dId = `css3d-node-${resolvedNodeId}`;
|
|
23854
23551
|
const nodeEntry = module.nodeObjectsById.get(resolvedNodeId);
|
|
23855
|
-
|
|
23856
|
-
if (nodeEntry && nodeEntry.cssObject) {
|
|
23552
|
+
|
|
23553
|
+
if (nodeEntry && nodeEntry.cssObject) {
|
|
23857
23554
|
warn(`[MindMapCss3DManager] CSS3D object already exists in memory for node ${resolvedNodeId}. Returning existing object.`);
|
|
23858
|
-
return nodeEntry.cssObject;
|
|
23859
|
-
}
|
|
23860
|
-
|
|
23861
|
-
const existingCss3dElement = document.getElementById(existingCss3dId);
|
|
23862
|
-
if (existingCss3dElement) {
|
|
23863
|
-
warn(`[MindMapCss3DManager] Removing duplicate CSS3D element from DOM: ${existingCss3dId}`);
|
|
23864
|
-
if (existingCss3dElement.parentElement) {
|
|
23865
|
-
existingCss3dElement.parentElement.removeChild(existingCss3dElement);
|
|
23866
|
-
}
|
|
23867
|
-
}
|
|
23868
|
-
|
|
23869
|
-
// (It lives in a hidden div, so it is not visible.)
|
|
23870
|
-
/*
|
|
23871
|
-
const razorMapNode = document.querySelector(`#${templateId}`);
|
|
23872
|
-
if (razorMapNode && razorMapNode.parentElement) {
|
|
23873
|
-
console.warn(`[MindMapCss3DManager] Removing Razor-generated MapNode from DOM: ${templateId}`);
|
|
23874
|
-
razorMapNode.parentElement.removeChild(razorMapNode);
|
|
23875
|
-
}
|
|
23876
|
-
*/
|
|
23877
|
-
// ▲▲▲ [Critical fix] ▲▲▲
|
|
23878
|
-
|
|
23555
|
+
return nodeEntry.cssObject;
|
|
23556
|
+
}
|
|
23557
|
+
|
|
23558
|
+
const existingCss3dElement = document.getElementById(existingCss3dId);
|
|
23559
|
+
if (existingCss3dElement) {
|
|
23560
|
+
warn(`[MindMapCss3DManager] Removing duplicate CSS3D element from DOM: ${existingCss3dId}`);
|
|
23561
|
+
if (existingCss3dElement.parentElement) {
|
|
23562
|
+
existingCss3dElement.parentElement.removeChild(existingCss3dElement);
|
|
23563
|
+
}
|
|
23564
|
+
}
|
|
23565
|
+
|
|
23566
|
+
// (It lives in a hidden div, so it is not visible.)
|
|
23567
|
+
/*
|
|
23568
|
+
const razorMapNode = document.querySelector(`#${templateId}`);
|
|
23569
|
+
if (razorMapNode && razorMapNode.parentElement) {
|
|
23570
|
+
console.warn(`[MindMapCss3DManager] Removing Razor-generated MapNode from DOM: ${templateId}`);
|
|
23571
|
+
razorMapNode.parentElement.removeChild(razorMapNode);
|
|
23572
|
+
}
|
|
23573
|
+
*/
|
|
23574
|
+
// ▲▲▲ [Critical fix] ▲▲▲
|
|
23575
|
+
|
|
23879
23576
|
const nodeView = createNodeViewElement(module, nodeModel, {
|
|
23880
23577
|
hostKind: NODE_VIEW_HOST_CSS3D,
|
|
23881
23578
|
idPrefix: 'css3d-node'
|
|
@@ -23900,31 +23597,31 @@
|
|
|
23900
23597
|
}
|
|
23901
23598
|
clonedElement.style.display = '';
|
|
23902
23599
|
clonedElement.style.transformOrigin = '0% 0%';
|
|
23903
|
-
|
|
23904
|
-
// ▼▼▼ [Clarity] Scale factor for high-resolution rendering ▼▼▼
|
|
23600
|
+
|
|
23601
|
+
// ▼▼▼ [Clarity] Scale factor for high-resolution rendering ▼▼▼
|
|
23905
23602
|
const resolutionScale = nodeView.resolutionScale;
|
|
23906
23603
|
applyCss3dResolutionLayout(clonedElement, width, height, resolutionScale, {
|
|
23907
23604
|
resetLayoutTransform: true
|
|
23908
23605
|
});
|
|
23909
23606
|
clonedElement.style.borderRadius = '0px';
|
|
23910
|
-
|
|
23911
|
-
// to the inner element, the node shifts down-right.
|
|
23912
|
-
// Remove it so the element sits at the wrapper origin (0,0).
|
|
23913
|
-
clonedElement.style.position = 'absolute';
|
|
23914
|
-
clonedElement.style.left = '0px';
|
|
23915
|
-
clonedElement.style.top = '0px';
|
|
23916
|
-
// ▲▲▲ [Changed] ▲▲▲
|
|
23917
|
-
|
|
23607
|
+
|
|
23608
|
+
// to the inner element, the node shifts down-right.
|
|
23609
|
+
// Remove it so the element sits at the wrapper origin (0,0).
|
|
23610
|
+
clonedElement.style.position = 'absolute';
|
|
23611
|
+
clonedElement.style.left = '0px';
|
|
23612
|
+
clonedElement.style.top = '0px';
|
|
23613
|
+
// ▲▲▲ [Changed] ▲▲▲
|
|
23614
|
+
|
|
23918
23615
|
clonedElement.style.transition = 'none';
|
|
23919
23616
|
clonedElement.style.webkitTransition = 'none';
|
|
23920
|
-
|
|
23921
|
-
// ▼▼▼ [Perf] GPU acceleration for smooth zoom/pan ▼▼▼
|
|
23922
|
-
// 초기에는 꺼둠 (auto). 카메라 이동 시 mind-map-core.js가 setWillChange(true) 호출
|
|
23617
|
+
|
|
23618
|
+
// ▼▼▼ [Perf] GPU acceleration for smooth zoom/pan ▼▼▼
|
|
23619
|
+
// 초기에는 꺼둠 (auto). 카메라 이동 시 mind-map-core.js가 setWillChange(true) 호출
|
|
23923
23620
|
clonedElement.style.willChange = 'auto'; // 'transform';
|
|
23924
23621
|
clonedElement.style.backfaceVisibility = isMediaNode ? 'visible' : 'hidden';
|
|
23925
23622
|
clonedElement.style.webkitBackfaceVisibility = isMediaNode ? 'visible' : 'hidden';
|
|
23926
23623
|
// ▲▲▲ [Perf] ▲▲▲
|
|
23927
|
-
|
|
23624
|
+
|
|
23928
23625
|
const wrapper = document.createElement('div');
|
|
23929
23626
|
const wrapperTypeClass = `node-type-${visualContentTypeLower || 'unknown'}`;
|
|
23930
23627
|
wrapper.className = `css3d-resolution-wrapper lod-high ${wrapperTypeClass}`;
|
|
@@ -23937,10 +23634,10 @@
|
|
|
23937
23634
|
wrapper.dataset.nodeId = resolvedNodeId;
|
|
23938
23635
|
wrapper.style.transition = CSS3D_WRAPPER_TRANSITION;
|
|
23939
23636
|
wrapper.style.webkitTransition = CSS3D_WRAPPER_WEBKIT_TRANSITION;
|
|
23940
|
-
|
|
23941
|
-
wrapper.style.transformOrigin = '0 0';
|
|
23942
|
-
// ▲▲▲ [Critical fix] ▲▲▲
|
|
23943
|
-
|
|
23637
|
+
|
|
23638
|
+
wrapper.style.transformOrigin = '0 0';
|
|
23639
|
+
// ▲▲▲ [Critical fix] ▲▲▲
|
|
23640
|
+
|
|
23944
23641
|
// ▼▼▼ [Fix] 외부 스크롤바 방지 - 내부 콘텐츠 스크롤바만 표시 ▼▼▼
|
|
23945
23642
|
wrapper.style.overflow = allowsExternalNodeChrome ? 'visible' : 'hidden';
|
|
23946
23643
|
if (allowsExternalNodeChrome) {
|
|
@@ -23949,14 +23646,14 @@
|
|
|
23949
23646
|
wrapper.style.containIntrinsicSize = 'auto';
|
|
23950
23647
|
}
|
|
23951
23648
|
// ▲▲▲ [Fix] ▲▲▲
|
|
23952
|
-
|
|
23953
|
-
// ▼▼▼ [Perf] GPU acceleration for smooth zoom/pan ▼▼▼
|
|
23649
|
+
|
|
23650
|
+
// ▼▼▼ [Perf] GPU acceleration for smooth zoom/pan ▼▼▼
|
|
23954
23651
|
wrapper.style.willChange = 'auto'; // 'transform';
|
|
23955
23652
|
wrapper.style.backfaceVisibility = isMediaNode ? 'visible' : 'hidden';
|
|
23956
23653
|
wrapper.style.webkitBackfaceVisibility = isMediaNode ? 'visible' : 'hidden';
|
|
23957
23654
|
wrapper.style.transformStyle = isMediaNode ? 'flat' : '';
|
|
23958
23655
|
// ▲▲▲ [Perf] ▲▲▲
|
|
23959
|
-
|
|
23656
|
+
|
|
23960
23657
|
if (contentTypeLower === 'image' || contentTypeLower === 'video') {
|
|
23961
23658
|
wrapper.style.pointerEvents = 'auto'; // [Fix] Changed from none to auto for resize handle detection
|
|
23962
23659
|
clonedElement.style.pointerEvents = 'none';
|
|
@@ -24033,10 +23730,10 @@
|
|
|
24033
23730
|
}
|
|
24034
23731
|
|
|
24035
23732
|
// ... (event listeners) ...
|
|
24036
|
-
|
|
24037
|
-
const css3dObj = new CSS3DObjectCtor(wrapper);
|
|
24038
|
-
css3dObj.scale.set(1 / resolutionScale, 1 / resolutionScale, 1 / resolutionScale);
|
|
24039
|
-
css3dObj.visible = false;
|
|
23733
|
+
|
|
23734
|
+
const css3dObj = new CSS3DObjectCtor(wrapper);
|
|
23735
|
+
css3dObj.scale.set(1 / resolutionScale, 1 / resolutionScale, 1 / resolutionScale);
|
|
23736
|
+
css3dObj.visible = false;
|
|
24040
23737
|
css3dObj.userData.nodeId = resolvedNodeId;
|
|
24041
23738
|
css3dObj.userData.worldWidth = width;
|
|
24042
23739
|
css3dObj.userData.worldHeight = height;
|
|
@@ -24046,10 +23743,10 @@
|
|
|
24046
23743
|
}
|
|
24047
23744
|
|
|
24048
23745
|
// ▼▼▼ [Fix] nodeModel의 위치 정보를 사용하여 초기 위치 설정 ▼▼▼
|
|
24049
|
-
// 호출자가 위치를 따로 설정하지 않아도 올바른 위치에 생성됩니다.
|
|
24050
|
-
// Z 오프셋 0: 그리드(Z=0)와 동일한 평면에 배치하여 원근/시차 완전 제거
|
|
24051
|
-
const posX = nodeModel.positionX ?? nodeModel.PositionX ?? 0;
|
|
24052
|
-
const posY = nodeModel.positionY ?? nodeModel.PositionY ?? 0;
|
|
23746
|
+
// 호출자가 위치를 따로 설정하지 않아도 올바른 위치에 생성됩니다.
|
|
23747
|
+
// Z 오프셋 0: 그리드(Z=0)와 동일한 평면에 배치하여 원근/시차 완전 제거
|
|
23748
|
+
const posX = nodeModel.positionX ?? nodeModel.PositionX ?? 0;
|
|
23749
|
+
const posY = nodeModel.positionY ?? nodeModel.PositionY ?? 0;
|
|
24053
23750
|
const posZ = (nodeModel.positionZ ?? nodeModel.PositionZ ?? 0); // [Fix] Z Offset 제거 (0으로 통일)
|
|
24054
23751
|
css3dObj.position.set(posX, posY, posZ);
|
|
24055
23752
|
// ▲▲▲ [Fix] ▲▲▲
|
|
@@ -24062,7 +23759,7 @@
|
|
|
24062
23759
|
|
|
24063
23760
|
return css3dObj;
|
|
24064
23761
|
}
|
|
24065
|
-
|
|
23762
|
+
|
|
24066
23763
|
function syncCss3dContent(nodeModel, cssObject) {
|
|
24067
23764
|
if (!nodeModel || !cssObject || !cssObject.element) return;
|
|
24068
23765
|
const nodeId = String(getNodeId(nodeModel) || '').trim();
|
|
@@ -24147,7 +23844,7 @@
|
|
|
24147
23844
|
});
|
|
24148
23845
|
}
|
|
24149
23846
|
}
|
|
24150
|
-
|
|
23847
|
+
|
|
24151
23848
|
const prompt = nodeModel.prompt ?? nodeModel.Prompt ?? '';
|
|
24152
23849
|
if (contentTypeLower === 'image' || contentTypeLower === 'video' || contentTypeLower === 'embed') {
|
|
24153
23850
|
restoreCss3dMediaOverlayState(nodeModel, cssObject);
|
|
@@ -24506,19 +24203,19 @@
|
|
|
24506
24203
|
renderBusinessAutomationChrome(resolveBusinessAutomationChromeContainer(el), nodeModel);
|
|
24507
24204
|
}
|
|
24508
24205
|
// Reusable objects for Frustum Culling (avoid GC)
|
|
24509
|
-
const _frustum = new THREE.Frustum();
|
|
24510
|
-
const _projScreenMatrix = new THREE.Matrix4();
|
|
24511
|
-
const _worldPos = new THREE.Vector3();
|
|
24512
|
-
const _worldQuat = new THREE.Quaternion();
|
|
24513
|
-
const _worldScale = new THREE.Vector3();
|
|
24514
|
-
const _nodeBox = new THREE.Box3(); // 바운딩 박스용
|
|
24515
|
-
|
|
24516
|
-
|
|
24517
|
-
// ▼▼▼ [New] Minimal update loop - only syncs positions when dirty ▼▼▼
|
|
24518
|
-
// 그리드와 동일한 Z 평면(Z=0)에 배치하여 원근/시차 완전 제거
|
|
24519
|
-
// [Fix] CSS3D 노드도 Z=0으로 완전 평탄화하여 카메라 이동 시 그리드와 정렬 유지
|
|
24520
|
-
const CSS3D_Z_OFFSET = 0;
|
|
24521
|
-
|
|
24206
|
+
const _frustum = new THREE.Frustum();
|
|
24207
|
+
const _projScreenMatrix = new THREE.Matrix4();
|
|
24208
|
+
const _worldPos = new THREE.Vector3();
|
|
24209
|
+
const _worldQuat = new THREE.Quaternion();
|
|
24210
|
+
const _worldScale = new THREE.Vector3();
|
|
24211
|
+
const _nodeBox = new THREE.Box3(); // 바운딩 박스용
|
|
24212
|
+
|
|
24213
|
+
|
|
24214
|
+
// ▼▼▼ [New] Minimal update loop - only syncs positions when dirty ▼▼▼
|
|
24215
|
+
// 그리드와 동일한 Z 평면(Z=0)에 배치하여 원근/시차 완전 제거
|
|
24216
|
+
// [Fix] CSS3D 노드도 Z=0으로 완전 평탄화하여 카메라 이동 시 그리드와 정렬 유지
|
|
24217
|
+
const CSS3D_Z_OFFSET = 0;
|
|
24218
|
+
|
|
24522
24219
|
function syncCssPositionFromGl(module, nodeEntry, targetZ) {
|
|
24523
24220
|
const glObject = nodeEntry.glObject;
|
|
24524
24221
|
const cssPos = nodeEntry.cssObject.position;
|
|
@@ -24622,8 +24319,8 @@
|
|
|
24622
24319
|
dirtyIds?.clear?.();
|
|
24623
24320
|
module._cssParentRepairNeeded = false;
|
|
24624
24321
|
}
|
|
24625
|
-
// ▲▲▲ [New] ▲▲▲
|
|
24626
|
-
|
|
24322
|
+
// ▲▲▲ [New] ▲▲▲
|
|
24323
|
+
|
|
24627
24324
|
// ▼▼▼ [New] 일괄 will-change 제어 (성능 최적화) ▼▼▼
|
|
24628
24325
|
let _willChangeState = null; // null = unknown, true = enabled, false = disabled
|
|
24629
24326
|
let _willChangeTouchedIds = new Set();
|
|
@@ -24818,10 +24515,10 @@
|
|
|
24818
24515
|
}
|
|
24819
24516
|
if (el.style.willChange !== value) el.style.willChange = value;
|
|
24820
24517
|
});
|
|
24821
|
-
|
|
24822
|
-
// Razor 템플릿 기반 요소 (id가 css3d-node-로 시작하는 것들)
|
|
24823
|
-
// (위의 createCss3dObject에서 clonedElement에 해당)
|
|
24824
|
-
// querySelectorAll로 잡을 수 있음
|
|
24518
|
+
|
|
24519
|
+
// Razor 템플릿 기반 요소 (id가 css3d-node-로 시작하는 것들)
|
|
24520
|
+
// (위의 createCss3dObject에서 clonedElement에 해당)
|
|
24521
|
+
// querySelectorAll로 잡을 수 있음
|
|
24825
24522
|
const clonedElements = document.querySelectorAll('[id^="css3d-node-"]');
|
|
24826
24523
|
clonedElements.forEach(el => {
|
|
24827
24524
|
if (effectiveEnabled && shouldPreserveAgentMotionCssElement(el)) {
|
|
@@ -24831,9 +24528,9 @@
|
|
|
24831
24528
|
if (el.style.willChange !== value) el.style.willChange = value;
|
|
24832
24529
|
});
|
|
24833
24530
|
}
|
|
24834
|
-
// ▲▲▲ [New] ▲▲▲
|
|
24835
|
-
|
|
24836
|
-
// Expose MindMapCss3DManager globally
|
|
24531
|
+
// ▲▲▲ [New] ▲▲▲
|
|
24532
|
+
|
|
24533
|
+
// Expose MindMapCss3DManager globally
|
|
24837
24534
|
window.MindMapCss3DManager = {
|
|
24838
24535
|
activateSelectableTextOverlay: activateSelectableTextOverlay,
|
|
24839
24536
|
activateNativeTextSelectionSource: activateNativeTextSelectionSource,
|
|
@@ -24912,6 +24609,6 @@
|
|
|
24912
24609
|
hideBusinessAutomationFloatingTooltipForNode: hideBusinessAutomationFloatingTooltipForNode,
|
|
24913
24610
|
update: update // Export
|
|
24914
24611
|
};
|
|
24915
|
-
|
|
24916
|
-
console.log('✓ mind-map-css3d-manager.js loaded (Note editing fixed)');
|
|
24917
|
-
})();
|
|
24612
|
+
|
|
24613
|
+
console.log('✓ mind-map-css3d-manager.js loaded (Note editing fixed)');
|
|
24614
|
+
})();
|