@mindexec/cli 0.2.478 → 0.2.479
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +323 -323
- package/codex-model-catalog.js +202 -202
- package/codex-runtime.js +1608 -1608
- package/desktop-workspace-state.cjs +120 -120
- package/electron/main.cjs +517 -517
- package/electron/preload.cjs +17 -17
- package/electron/recurring-update-owner-smoke.mjs +55 -55
- package/electron/recurring-update-owner.cjs +97 -97
- package/electron/source-smoke.mjs +108 -108
- package/electron/update-feed-resolver.cjs +79 -79
- package/electron/update-feed.json +10 -10
- package/electron/update-manager-smoke.mjs +146 -146
- package/electron/update-manager.cjs +297 -297
- package/electron/windows-package-smoke.mjs +156 -156
- package/launch-bridge.cjs +236 -236
- package/lzo1x.js +110 -110
- package/mode4-atlas-worker.js +440 -440
- package/mode4-atlas.js +130 -130
- package/package.json +1 -1
- package/port-guard.cjs +285 -285
- package/remote-fast/osx-arm64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-arm64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-arm64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-arm64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-arm64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/osx-x64/MacScreenCaptureKitH264.swift +339 -339
- package/remote-fast/osx-x64/MacSystemAudioCapture.swift +214 -214
- package/remote-fast/osx-x64/UnixRawCaptureHelper.mjs +80 -80
- package/remote-fast/osx-x64/livedesk-raw-capture-resampler.mjs +73 -73
- package/remote-fast/osx-x64/mindexec-remote-fast.dll +0 -0
- package/remote-fast/win-x64/mindexec-remote-fast.dll +0 -0
- package/remote-hub.js +3698 -3698
- package/scripts/auth-session-smoke.mjs +262 -262
- package/scripts/board-render-ownership-smoke.mjs +85 -85
- package/scripts/codex-model-catalog-smoke.mjs +150 -150
- package/scripts/codex-runtime-home-smoke.mjs +58 -58
- package/scripts/codex-sdk-image-live-smoke.mjs +43 -43
- package/scripts/codex-sdk-image-runtime-smoke.mjs +122 -122
- package/scripts/codex-sdk-runtime-smoke.mjs +89 -89
- package/scripts/desktop-update-publisher-smoke.mjs +89 -89
- package/scripts/desktop-update-worker-smoke.mjs +72 -72
- package/scripts/desktop-workspace-state-smoke.mjs +81 -81
- package/scripts/drive-installed-update-smoke.mjs +67 -67
- package/scripts/npm-fresh-install-smoke.mjs +216 -216
- package/scripts/publish-mindexec-desktop-updates.mjs +224 -224
- package/scripts/remote-agent-managed-smoke.mjs +318 -318
- package/scripts/remote-agent-package-smoke.mjs +64 -64
- package/scripts/remote-agent-ws-smoke.mjs +269 -269
- package/scripts/remote-fast-live-rate-smoke.mjs +343 -343
- package/scripts/remote-fast-mdm-browser-smoke.mjs +453 -453
- package/scripts/remote-fleet-render-smoke.mjs +1350 -1350
- package/scripts/remote-frame-ws-smoke.mjs +234 -234
- package/scripts/remote-http-smoke.mjs +592 -592
- package/scripts/remote-hub-identity-smoke.mjs +146 -146
- package/scripts/remote-hub-scale-smoke.mjs +124 -124
- package/scripts/remote-hub-smoke.mjs +699 -699
- package/scripts/remote-input-ws-smoke.mjs +263 -263
- package/scripts/remote-registry-follower-smoke.mjs +815 -815
- package/scripts/setup-tree-sitter-grammars.mjs +59 -59
- package/server.js +7510 -7510
- package/start-bridge.bat +32 -32
- package/start-bridge.sh +81 -81
- package/tree-sitter-grammars/README.md +18 -0
- package/tree-sitter-grammars/tree-sitter-c_sharp.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-go.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-java.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-javascript.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-python.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-rust.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-tsx.wasm +0 -0
- package/tree-sitter-grammars/tree-sitter-typescript.wasm +0 -0
- package/wwwroot/_content/MindExecution.Plugins.Admin/css/admin-dashboard.css +546 -546
- package/wwwroot/_content/MindExecution.Plugins.Directory/background.png +0 -0
- package/wwwroot/_content/MindExecution.Plugins.YouTube/css/youtube-search.css +251 -251
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +3789 -3789
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Bold.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/fonts/NotoSansKR-Regular.ttf +0 -0
- package/wwwroot/_content/MindExecution.Shared/js/agent-visualization.js +27 -27
- package/wwwroot/_content/MindExecution.Shared/js/background-themes.js +1449 -1449
- package/wwwroot/_content/MindExecution.Shared/js/code-master.js +8316 -8316
- package/wwwroot/_content/MindExecution.Shared/js/file-system-helper.js +338 -338
- package/wwwroot/_content/MindExecution.Shared/js/marked.min.js +69 -69
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-animated-image-preview.js +270 -270
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-automation-edge-motion.js +170 -170
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-board-render-ownership.js +81 -81
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +10506 -10506
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js.backup +1059 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-hotpath.js +902 -902
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +24637 -24637
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dev-guards.js +325 -325
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js +1491 -1491
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-dnd.js.bak +434 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-glow-shader.js +70 -70
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-image-policy.js +218 -218
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-interactions.js +7078 -7078
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-plan-worker.js +160 -160
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +9958 -9958
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-video-placeholder.js +107 -107
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-logic-workers.js +1004 -1004
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-menu-manager.js +1351 -1351
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-multi-select.js +865 -865
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-node-search-worker.js +554 -554
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +4744 -4744
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-normal-boundary-optimizer.js +624 -624
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js +241 -241
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-object-manager.js.backup +372 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-pipeline.js +2290 -2290
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-remote-codecs.js +202 -202
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-render-plan.js +1971 -1971
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-text-lod-system.js +93 -93
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js +2391 -2391
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-texture-factory.js.backup +1258 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-video-playback-controls.js +95 -95
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-visibility-worker.js +895 -895
- package/wwwroot/_content/MindExecution.Shared/js/mindmap-toolbar.js +636 -636
- package/wwwroot/_content/MindExecution.Shared/js/native-drop-handler.js +45 -45
- package/wwwroot/_content/MindExecution.Shared/js/plan-master.js +117 -117
- package/wwwroot/_content/MindExecution.Shared/js/renderers/CSS3DRenderer.js +114 -114
- package/wwwroot/_content/MindExecution.Shared/js/texture-worker-manager.js +10 -10
- package/wwwroot/_content/MindExecution.Shared/js/three.min.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/token-manager.js +7 -7
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js +6 -6
- package/wwwroot/_content/MindExecution.Shared/js/troika-bundle.js.map +2 -2
- package/wwwroot/_content/MindExecution.Shared/lib/font-awesome/css/all.min.css +8 -8
- package/wwwroot/_content/MindExecution.Shared/models/vocab.txt +30522 -30522
- package/wwwroot/_framework/{Google.Protobuf.33g2gb5is0.dll → Google.Protobuf.9h59ukbel7.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Core.z100xo4mg0.dll → MindExecution.Core.xxsj2s456b.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.i9cldrx8yc.dll → MindExecution.Kernel.5jofnc0wrd.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.48lzqddb0a.dll → MindExecution.Plugins.Admin.t7m4jnqjg3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.7m8u1q142p.dll → MindExecution.Plugins.Business.6yjlhhavap.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.cl5wmr53od.dll → MindExecution.Plugins.Concept.iikqq5zxu0.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.slipi39041.dll → MindExecution.Plugins.Directory.c5ribyne4m.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.qfge05l671.dll → MindExecution.Plugins.PlanMaster.365bbuifr9.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.tn7v29ut77.dll → MindExecution.Plugins.YouTube.ax415cilpk.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Shared.21e2nvf86u.dll +0 -0
- package/wwwroot/_framework/{MindExecution.Web.rwawrxxk38.dll → MindExecution.Web.ppgm8rey59.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.23yxgetbju.dll → System.Collections.zyvbaq6mpd.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +25 -25
- package/wwwroot/_headers +40 -40
- package/wwwroot/_redirects +1 -1
- package/wwwroot/index.html +772 -772
- package/wwwroot/js/marketing-tool.js +180 -180
- package/wwwroot/manifest.webmanifest +23 -23
- package/wwwroot/service-worker-assets.js +109 -81
- package/wwwroot/service-worker.js +33 -33
- package/wwwroot/_framework/MindExecution.Shared.rjqlf0umjn.dll +0 -0
|
@@ -3,769 +3,769 @@
|
|
|
3
3
|
(function () {
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
-
let currentMenuElement = null; // DOM overlay element
|
|
7
|
-
let currentMenuNodeId = null;
|
|
8
|
-
let _module = null;
|
|
9
|
-
const MEMO_COLORS = [
|
|
10
|
-
{ key: 'coral', swatch: '#ff8052', ring: 'rgba(255, 128, 82, 0.22)' },
|
|
11
|
-
{ key: 'amber', swatch: '#f59e0b', ring: 'rgba(245, 158, 11, 0.22)' },
|
|
12
|
-
{ key: 'sun', swatch: '#ca8a04', ring: 'rgba(202, 138, 4, 0.22)' },
|
|
13
|
-
{ key: 'sage', swatch: '#65a30d', ring: 'rgba(101, 163, 13, 0.22)' },
|
|
14
|
-
{ key: 'mint', swatch: '#10b981', ring: 'rgba(16, 185, 129, 0.22)' },
|
|
15
|
-
{ key: 'sky', swatch: '#3b82f6', ring: 'rgba(59, 130, 246, 0.22)' },
|
|
16
|
-
{ key: 'indigo', swatch: '#6366f1', ring: 'rgba(99, 102, 241, 0.22)' },
|
|
17
|
-
{ key: 'violet', swatch: '#8b5cf6', ring: 'rgba(139, 92, 246, 0.22)' },
|
|
18
|
-
{ key: 'rose', swatch: '#f43f5e', ring: 'rgba(244, 63, 94, 0.22)' },
|
|
19
|
-
{ key: 'gray', swatch: '#6b7280', ring: 'rgba(107, 114, 128, 0.22)' },
|
|
20
|
-
{ key: 'slate', swatch: '#64748b', ring: 'rgba(100, 116, 139, 0.22)' }
|
|
21
|
-
];
|
|
22
|
-
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
23
|
-
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
24
|
-
|
|
25
|
-
function ensureFeedbackStyles() {
|
|
26
|
-
if (document.getElementById('mindmap-feedback-styles')) return;
|
|
27
|
-
|
|
28
|
-
const style = document.createElement('style');
|
|
29
|
-
style.id = 'mindmap-feedback-styles';
|
|
30
|
-
style.textContent = `
|
|
31
|
-
.mindmap-toast-stack {
|
|
32
|
-
position: fixed;
|
|
33
|
-
right: 22px;
|
|
34
|
-
bottom: 24px;
|
|
35
|
-
z-index: 2147483000;
|
|
36
|
-
display: flex;
|
|
37
|
-
flex-direction: column;
|
|
38
|
-
gap: 10px;
|
|
39
|
-
pointer-events: none;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.mindmap-toast {
|
|
43
|
-
width: min(380px, calc(100vw - 32px));
|
|
44
|
-
padding: 12px 14px;
|
|
45
|
-
border-radius: 16px;
|
|
46
|
-
background: rgba(15, 23, 42, 0.94);
|
|
47
|
-
color: #f8fafc;
|
|
48
|
-
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.24);
|
|
49
|
-
font: 600 13px/1.45 Inter, ui-sans-serif, system-ui, sans-serif;
|
|
50
|
-
opacity: 0;
|
|
51
|
-
transform: translateY(8px);
|
|
52
|
-
transition: opacity 180ms ease, transform 180ms ease;
|
|
53
|
-
pointer-events: auto;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.mindmap-toast.is-visible {
|
|
57
|
-
opacity: 1;
|
|
58
|
-
transform: translateY(0);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.mindmap-toast[data-kind="error"] {
|
|
62
|
-
background: rgba(127, 29, 29, 0.94);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.mindmap-confirm-backdrop {
|
|
66
|
-
position: fixed;
|
|
67
|
-
inset: 0;
|
|
68
|
-
z-index: 2147483001;
|
|
69
|
-
display: flex;
|
|
70
|
-
align-items: center;
|
|
71
|
-
justify-content: center;
|
|
72
|
-
padding: 24px;
|
|
73
|
-
background: rgba(15, 23, 42, 0.20);
|
|
74
|
-
backdrop-filter: blur(5px);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.mindmap-confirm-panel {
|
|
78
|
-
width: min(440px, calc(100vw - 36px));
|
|
79
|
-
border: 1px solid rgba(203, 213, 225, 0.86);
|
|
80
|
-
border-radius: 22px;
|
|
81
|
-
background: rgba(248, 250, 252, 0.98);
|
|
82
|
-
box-shadow: 0 28px 72px rgba(15, 23, 42, 0.22);
|
|
83
|
-
padding: 20px;
|
|
84
|
-
color: #0f172a;
|
|
85
|
-
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.mindmap-confirm-title {
|
|
89
|
-
margin: 0;
|
|
90
|
-
font-size: 15px;
|
|
91
|
-
font-weight: 800;
|
|
92
|
-
letter-spacing: 0.01em;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.mindmap-confirm-message {
|
|
96
|
-
margin: 8px 0 0;
|
|
97
|
-
color: #475569;
|
|
98
|
-
font-size: 13px;
|
|
99
|
-
line-height: 1.55;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.mindmap-confirm-actions {
|
|
103
|
-
display: flex;
|
|
104
|
-
justify-content: flex-end;
|
|
105
|
-
gap: 10px;
|
|
106
|
-
margin-top: 18px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.mindmap-confirm-button {
|
|
110
|
-
border: 0;
|
|
111
|
-
border-radius: 999px;
|
|
112
|
-
padding: 9px 15px;
|
|
113
|
-
font-size: 13px;
|
|
114
|
-
font-weight: 800;
|
|
115
|
-
cursor: pointer;
|
|
116
|
-
transition: transform 120ms ease, background 120ms ease;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.mindmap-confirm-button:active {
|
|
120
|
-
transform: scale(0.98);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.mindmap-confirm-button.secondary {
|
|
124
|
-
background: #e0f2fe;
|
|
125
|
-
color: #075985;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.mindmap-confirm-button.primary {
|
|
129
|
-
background: #0f172a;
|
|
130
|
-
color: #ffffff;
|
|
131
|
-
}
|
|
132
|
-
`;
|
|
133
|
-
document.head.appendChild(style);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function getToastStack() {
|
|
137
|
-
ensureFeedbackStyles();
|
|
138
|
-
let stack = document.querySelector('.mindmap-toast-stack');
|
|
139
|
-
if (!stack) {
|
|
140
|
-
stack = document.createElement('div');
|
|
141
|
-
stack.className = 'mindmap-toast-stack';
|
|
142
|
-
document.body.appendChild(stack);
|
|
143
|
-
}
|
|
144
|
-
return stack;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function showToast(message, options = {}) {
|
|
148
|
-
const text = String(message || '').trim();
|
|
149
|
-
if (!text || !document?.body) return;
|
|
150
|
-
|
|
151
|
-
const toast = document.createElement('div');
|
|
152
|
-
toast.className = 'mindmap-toast';
|
|
153
|
-
toast.dataset.kind = options.kind || 'info';
|
|
154
|
-
toast.textContent = text;
|
|
155
|
-
|
|
156
|
-
getToastStack().appendChild(toast);
|
|
157
|
-
requestAnimationFrame(() => toast.classList.add('is-visible'));
|
|
158
|
-
|
|
159
|
-
window.setTimeout(() => {
|
|
160
|
-
toast.classList.remove('is-visible');
|
|
161
|
-
window.setTimeout(() => toast.remove(), 220);
|
|
162
|
-
}, options.duration || 2600);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function showConfirmDialog(options = {}) {
|
|
166
|
-
if (!document?.body) return Promise.resolve(false);
|
|
167
|
-
|
|
168
|
-
ensureFeedbackStyles();
|
|
169
|
-
|
|
170
|
-
return new Promise((resolve) => {
|
|
171
|
-
const backdrop = document.createElement('div');
|
|
172
|
-
backdrop.className = 'mindmap-confirm-backdrop';
|
|
173
|
-
|
|
174
|
-
const panel = document.createElement('div');
|
|
175
|
-
panel.className = 'mindmap-confirm-panel';
|
|
176
|
-
panel.setAttribute('role', 'dialog');
|
|
177
|
-
panel.setAttribute('aria-modal', 'true');
|
|
178
|
-
|
|
179
|
-
const title = document.createElement('h3');
|
|
180
|
-
title.className = 'mindmap-confirm-title';
|
|
181
|
-
title.textContent = options.title || 'Confirm action';
|
|
182
|
-
|
|
183
|
-
const message = document.createElement('p');
|
|
184
|
-
message.className = 'mindmap-confirm-message';
|
|
185
|
-
message.textContent = options.message || 'Do you want to continue?';
|
|
186
|
-
|
|
187
|
-
const actions = document.createElement('div');
|
|
188
|
-
actions.className = 'mindmap-confirm-actions';
|
|
189
|
-
|
|
190
|
-
const cancel = document.createElement('button');
|
|
191
|
-
cancel.type = 'button';
|
|
192
|
-
cancel.className = 'mindmap-confirm-button secondary';
|
|
193
|
-
cancel.textContent = options.cancelText || 'Cancel';
|
|
194
|
-
|
|
195
|
-
const confirm = document.createElement('button');
|
|
196
|
-
confirm.type = 'button';
|
|
197
|
-
confirm.className = 'mindmap-confirm-button primary';
|
|
198
|
-
confirm.textContent = options.confirmText || 'Confirm';
|
|
199
|
-
|
|
200
|
-
const close = (value) => {
|
|
201
|
-
backdrop.remove();
|
|
202
|
-
document.removeEventListener('keydown', handleKeyDown, true);
|
|
203
|
-
resolve(!!value);
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const handleKeyDown = (event) => {
|
|
207
|
-
if (event.key === 'Escape') {
|
|
208
|
-
event.preventDefault();
|
|
209
|
-
close(false);
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
cancel.addEventListener('click', () => close(false));
|
|
214
|
-
confirm.addEventListener('click', () => close(true));
|
|
215
|
-
backdrop.addEventListener('click', (event) => {
|
|
216
|
-
if (event.target === backdrop) close(false);
|
|
217
|
-
});
|
|
218
|
-
document.addEventListener('keydown', handleKeyDown, true);
|
|
219
|
-
|
|
220
|
-
actions.appendChild(cancel);
|
|
221
|
-
actions.appendChild(confirm);
|
|
222
|
-
panel.appendChild(title);
|
|
223
|
-
panel.appendChild(message);
|
|
224
|
-
panel.appendChild(actions);
|
|
225
|
-
backdrop.appendChild(panel);
|
|
226
|
-
document.body.appendChild(backdrop);
|
|
227
|
-
confirm.focus({ preventScroll: true });
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
window.MindMapFeedback = window.MindMapFeedback || {};
|
|
232
|
-
window.MindMapFeedback.toast = showToast;
|
|
233
|
-
window.MindMapFeedback.confirm = showConfirmDialog;
|
|
234
|
-
|
|
235
|
-
function init(module) {
|
|
236
|
-
_module = module;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function ensureMetadata(model) {
|
|
240
|
-
if (!model) return {};
|
|
241
|
-
if (model.metadata && typeof model.metadata === 'object') return model.metadata;
|
|
242
|
-
if (model.Metadata && typeof model.Metadata === 'object') {
|
|
243
|
-
model.metadata = model.Metadata;
|
|
244
|
-
return model.metadata;
|
|
245
|
-
}
|
|
246
|
-
model.metadata = {};
|
|
247
|
-
model.Metadata = model.metadata;
|
|
248
|
-
return model.metadata;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function getNodeSemanticType(model) {
|
|
252
|
-
const metadata = ensureMetadata(model);
|
|
253
|
-
return String(metadata.SemanticType || metadata.semanticType || '').trim();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function isAgentStyledMemoNode(model) {
|
|
257
|
-
const semanticType = getNodeSemanticType(model);
|
|
258
|
-
return semanticType === 'MindCanvasAgent'
|
|
259
|
-
|| semanticType === 'AgentCommand'
|
|
260
|
-
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function isBusinessAutomationNode(model) {
|
|
264
|
-
return getNodeSemanticType(model) === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function getBusinessAutomationNodeKind(model) {
|
|
268
|
-
const metadata = ensureMetadata(model);
|
|
269
|
-
return String(metadata.AutomationNodeKind || metadata.automationNodeKind || '').trim().toLowerCase();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function getBusinessAutomationNodeRole(model) {
|
|
273
|
-
const metadata = ensureMetadata(model);
|
|
274
|
-
return String(metadata.AutomationNodeRole || metadata.automationNodeRole || '').trim().toLowerCase();
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function isBusinessAutomationRootAiTaskNode(model) {
|
|
278
|
-
if (!isBusinessAutomationNode(model) || getBusinessAutomationNodeKind(model) !== 'ai-task') {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const role = getBusinessAutomationNodeRole(model);
|
|
283
|
-
return role.length === 0 || role === 'root';
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function isRemoteFleetMonitorNode(model) {
|
|
287
|
-
return getNodeSemanticType(model) === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function canRunExecutableNode(model) {
|
|
291
|
-
return isBusinessAutomationRootAiTaskNode(model);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function isExecutableNodeRunning(model) {
|
|
295
|
-
const metadata = ensureMetadata(model);
|
|
296
|
-
return String(metadata.AutomationRunState || '').trim().toLowerCase() === 'running';
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function setExecutableNodeRunVisualState(nodeId, runState, statusLabel) {
|
|
300
|
-
const normalizedNodeId = String(nodeId || '').trim();
|
|
301
|
-
if (!normalizedNodeId || !_module?.nodeObjectsById) {
|
|
302
|
-
return false;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const entry = _module.nodeObjectsById.get(normalizedNodeId);
|
|
306
|
-
if (!entry?.model) {
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const metadata = ensureMetadata(entry.model);
|
|
311
|
-
const normalizedRunState = String(runState || '').trim().toLowerCase();
|
|
312
|
-
metadata.AutomationRunState = normalizedRunState || 'idle';
|
|
313
|
-
|
|
314
|
-
if (normalizedRunState === 'running') {
|
|
315
|
-
metadata.AutomationLastRunStatus = 'running';
|
|
316
|
-
metadata.AutomationLastRunStatusLabel = statusLabel || 'Running';
|
|
317
|
-
metadata.AutomationLastRunError = '';
|
|
318
|
-
} else if (statusLabel) {
|
|
319
|
-
metadata.AutomationLastRunStatusLabel = statusLabel;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
entry.model.metadata = metadata;
|
|
323
|
-
entry.model.Metadata = metadata;
|
|
324
|
-
|
|
325
|
-
if (entry.cssObject && window.MindMapCss3DManager?.syncCss3dContent) {
|
|
326
|
-
try {
|
|
327
|
-
entry.isCssDirty = true;
|
|
328
|
-
window.MindMapCss3DManager.syncCss3dContent(entry.model, entry.cssObject);
|
|
329
|
-
} finally {
|
|
330
|
-
entry.isCssDirty = false;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
_module.lodRenderer?.requestVisualRefresh?.('node-run-visual-state');
|
|
335
|
-
return true;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
async function syncNodeRunResultStates(result) {
|
|
339
|
-
if (!result) {
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const nodeStates = Array.isArray(result.nodeStates)
|
|
344
|
-
? result.nodeStates
|
|
345
|
-
: (result.agentNodeState ? [result.agentNodeState] : []);
|
|
346
|
-
|
|
347
|
-
if (nodeStates.length > 0 && window.mindMap?.syncNodeStates) {
|
|
348
|
-
await window.mindMap.syncNodeStates(nodeStates);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function isTextFitNode(model) {
|
|
353
|
-
const contentType = String(model?.contentType || '').toLowerCase();
|
|
354
|
-
return contentType === 'text' || contentType === 'markdown' || contentType === 'note' || contentType === 'code';
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function getNodeContentType(model) {
|
|
358
|
-
return String(model?.contentType || '').trim().toLowerCase();
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function showActionSuccessIcon(button) {
|
|
362
|
-
const icon = button?.querySelector?.('i');
|
|
363
|
-
if (!icon) return;
|
|
364
|
-
|
|
365
|
-
const oldClass = icon.className;
|
|
366
|
-
icon.className = 'fa-solid fa-check text-green-500';
|
|
367
|
-
setTimeout(() => {
|
|
368
|
-
icon.className = oldClass;
|
|
369
|
-
}, 1000);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function getImageClipboardSourceCandidates(nodeModel) {
|
|
373
|
-
const assetUrls = window.MindMapNodes?.getNodeImageAssetUrls?.(nodeModel) || {};
|
|
374
|
-
const rawCandidates = [
|
|
375
|
-
assetUrls.fullResUrl,
|
|
376
|
-
assetUrls.originalUrl,
|
|
377
|
-
assetUrls.originalPath,
|
|
378
|
-
assetUrls.previewUrl,
|
|
379
|
-
assetUrls.responseUrl,
|
|
380
|
-
nodeModel?.response,
|
|
381
|
-
nodeModel?.Response
|
|
382
|
-
];
|
|
383
|
-
|
|
384
|
-
const candidates = [];
|
|
385
|
-
rawCandidates.forEach(value => {
|
|
386
|
-
const normalized = typeof value === 'string' ? value.trim() : '';
|
|
387
|
-
if (!normalized || candidates.includes(normalized)) {
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
candidates.push(normalized);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
return candidates;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function shouldResolveImageClipboardSourceBeforeFetch(src) {
|
|
398
|
-
const normalized = typeof src === 'string' ? src.trim().toLowerCase() : '';
|
|
399
|
-
if (!normalized || normalized.startsWith('blob:') || normalized.startsWith('data:') || normalized.startsWith('file:')) {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return normalized.startsWith('/assets/')
|
|
404
|
-
|| normalized.startsWith('assets/')
|
|
405
|
-
|| normalized.includes('/assets/thumbs/')
|
|
406
|
-
|| (normalized.includes('/assets/') && !/\.[a-z0-9]{2,5}(?:[?#]|$)/i.test(normalized));
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
async function resolveClipboardImageSource(nodeModel, src) {
|
|
410
|
-
if (!shouldResolveImageClipboardSourceBeforeFetch(src) || !_module?.dotNetHelper) {
|
|
411
|
-
return src;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const nodeId = String(nodeModel?.id || nodeModel?.Id || '').trim();
|
|
415
|
-
if (!nodeId) {
|
|
416
|
-
return src;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
try {
|
|
420
|
-
const resolved = await _module.dotNetHelper.invokeMethodAsync('ResolveNodeAssetUrl', nodeId);
|
|
421
|
-
const normalized = typeof resolved === 'string' ? resolved.trim() : '';
|
|
422
|
-
return normalized || src;
|
|
423
|
-
} catch (error) {
|
|
424
|
-
console.warn('[MindMapMenuManager] Failed to resolve image clipboard source.', error);
|
|
425
|
-
return src;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
async function fetchClipboardImageBlob(nodeModel) {
|
|
430
|
-
const candidates = getImageClipboardSourceCandidates(nodeModel);
|
|
431
|
-
let lastError = null;
|
|
432
|
-
|
|
433
|
-
for (const src of candidates) {
|
|
434
|
-
try {
|
|
435
|
-
const resolvedSrc = await resolveClipboardImageSource(nodeModel, src);
|
|
436
|
-
const response = await fetch(resolvedSrc, { cache: 'no-store' });
|
|
437
|
-
if (!response.ok) {
|
|
438
|
-
throw new Error(`HTTP ${response.status}`);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const blob = await response.blob();
|
|
442
|
-
if (!(blob instanceof Blob) || blob.size <= 0) {
|
|
443
|
-
throw new Error('Empty image blob.');
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return { blob, src: resolvedSrc };
|
|
447
|
-
} catch (error) {
|
|
448
|
-
lastError = error;
|
|
449
|
-
console.warn(`[MindMapMenuManager] Failed to fetch clipboard image from ${src}`, error);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
throw lastError || new Error('No image source was available for clipboard copy.');
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
async function decodeClipboardImageBlob(blob) {
|
|
457
|
-
if (typeof createImageBitmap === 'function') {
|
|
458
|
-
try {
|
|
459
|
-
const bitmap = await createImageBitmap(blob);
|
|
460
|
-
return {
|
|
461
|
-
width: bitmap.width,
|
|
462
|
-
height: bitmap.height,
|
|
463
|
-
draw(ctx, width, height) {
|
|
464
|
-
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
465
|
-
},
|
|
466
|
-
dispose() {
|
|
467
|
-
bitmap.close?.();
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
} catch (error) {
|
|
471
|
-
console.warn('[MindMapMenuManager] createImageBitmap failed for clipboard image conversion.', error);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
476
|
-
try {
|
|
477
|
-
const image = await new Promise((resolve, reject) => {
|
|
478
|
-
const img = new Image();
|
|
479
|
-
img.decoding = 'async';
|
|
480
|
-
img.onload = () => resolve(img);
|
|
481
|
-
img.onerror = () => reject(new Error('Failed to decode image blob.'));
|
|
482
|
-
img.src = objectUrl;
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
return {
|
|
486
|
-
width: image.naturalWidth || image.width,
|
|
487
|
-
height: image.naturalHeight || image.height,
|
|
488
|
-
draw(ctx, width, height) {
|
|
489
|
-
ctx.drawImage(image, 0, 0, width, height);
|
|
490
|
-
},
|
|
491
|
-
dispose() {
|
|
492
|
-
image.remove?.();
|
|
493
|
-
URL.revokeObjectURL(objectUrl);
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
} catch (error) {
|
|
497
|
-
URL.revokeObjectURL(objectUrl);
|
|
498
|
-
throw error;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
async function convertBlobToPngForClipboard(blob) {
|
|
503
|
-
const normalizedType = String(blob?.type || '').toLowerCase();
|
|
504
|
-
if (normalizedType === 'image/png' && blob.size > 0) {
|
|
505
|
-
return blob;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const decoded = await decodeClipboardImageBlob(blob);
|
|
509
|
-
const width = Math.max(1, Math.round(decoded?.width || 0));
|
|
510
|
-
const height = Math.max(1, Math.round(decoded?.height || 0));
|
|
511
|
-
|
|
512
|
-
if (!width || !height) {
|
|
513
|
-
decoded?.dispose?.();
|
|
514
|
-
throw new Error('Decoded image had no dimensions.');
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
try {
|
|
518
|
-
if (typeof OffscreenCanvas === 'function') {
|
|
519
|
-
const canvas = new OffscreenCanvas(width, height);
|
|
520
|
-
const ctx = canvas.getContext('2d');
|
|
521
|
-
if (!ctx) {
|
|
522
|
-
throw new Error('OffscreenCanvas 2D context is unavailable.');
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
decoded.draw(ctx, width, height);
|
|
526
|
-
return await canvas.convertToBlob({ type: 'image/png' });
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const canvas = document.createElement('canvas');
|
|
530
|
-
canvas.width = width;
|
|
531
|
-
canvas.height = height;
|
|
532
|
-
const ctx = canvas.getContext('2d');
|
|
533
|
-
if (!ctx) {
|
|
534
|
-
throw new Error('Canvas 2D context is unavailable.');
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
decoded.draw(ctx, width, height);
|
|
538
|
-
return await new Promise((resolve, reject) => {
|
|
539
|
-
canvas.toBlob(result => {
|
|
540
|
-
if (result instanceof Blob && result.size > 0) {
|
|
541
|
-
resolve(result);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
reject(new Error('Failed to encode clipboard image as PNG.'));
|
|
546
|
-
}, 'image/png');
|
|
547
|
-
});
|
|
548
|
-
} finally {
|
|
549
|
-
decoded?.dispose?.();
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
async function writeImageBlobToClipboard(blob, fallbackText) {
|
|
554
|
-
if (navigator.clipboard?.write && typeof ClipboardItem === 'function') {
|
|
555
|
-
await navigator.clipboard.write([
|
|
556
|
-
new ClipboardItem({ 'image/png': blob })
|
|
557
|
-
]);
|
|
558
|
-
return 'image';
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (navigator.clipboard?.writeText && fallbackText) {
|
|
562
|
-
await navigator.clipboard.writeText(fallbackText);
|
|
563
|
-
return 'text';
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
throw new Error('Clipboard API is unavailable.');
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function getNodeSourceFilePath(model) {
|
|
570
|
-
return String(model?.sourceFilePath || model?.SourceFilePath || '').trim();
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function getCompanyBindingValue(model, key) {
|
|
574
|
-
const metadata = ensureMetadata(model);
|
|
575
|
-
return String(metadata[key] || '').trim();
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function isCompanyBoundNode(model) {
|
|
579
|
-
return getCompanyBindingValue(model, 'CompanyCore.CompanyId').length > 0;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function getCompanyStatus(model) {
|
|
583
|
-
return getCompanyBindingValue(model, 'CompanyCore.Status').toLowerCase();
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function isPendingCompanyBinding(model) {
|
|
587
|
-
const companyId = getCompanyBindingValue(model, 'CompanyCore.CompanyId');
|
|
588
|
-
if (companyId.startsWith('pending:')) {
|
|
589
|
-
return true;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const status = getCompanyStatus(model);
|
|
593
|
-
const runId = getCompanyBindingValue(model, 'CompanyCore.RunId');
|
|
594
|
-
return !companyId && status === 'queued' && !runId;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
function applyCompanyBindingValues(model, values) {
|
|
598
|
-
const metadata = ensureMetadata(model);
|
|
599
|
-
Object.entries(values || {}).forEach(([key, value]) => {
|
|
600
|
-
if (value === null || value === undefined || value === '') {
|
|
601
|
-
delete metadata[key];
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
metadata[key] = String(value);
|
|
606
|
-
});
|
|
607
|
-
model.metadata = metadata;
|
|
608
|
-
model.Metadata = metadata;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function applyOptimisticCompanyRunState(nodeEntry, nodeId, executionMode, enableSubAgents) {
|
|
612
|
-
if (!nodeEntry?.model) return;
|
|
613
|
-
|
|
614
|
-
applyCompanyBindingValues(nodeEntry.model, {
|
|
615
|
-
'CompanyCore.CompanyId': `pending:${nodeId}`,
|
|
616
|
-
'CompanyCore.ExecutionMode': executionMode || 'Auto',
|
|
617
|
-
'CompanyCore.SubAgentsEnabled': enableSubAgents ? 'true' : 'false',
|
|
618
|
-
'CompanyCore.Status': 'Queued',
|
|
619
|
-
'CompanyCore.LastMessage': 'Starting company from this note...',
|
|
620
|
-
'CompanyCore.PendingApprovalCount': '0',
|
|
621
|
-
'CompanyCore.RunningAgentCount': '0',
|
|
622
|
-
'CompanyCore.WorkItemCount': '0',
|
|
623
|
-
'CompanyCore.ArtifactCount': '0',
|
|
624
|
-
'CompanyCore.GrowthResultCount': '0',
|
|
625
|
-
'CompanyCore.RevenueResultCount': '0'
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
function applyCompanyRunResult(nodeEntry, result, fallbackExecutionMode, fallbackEnableSubAgents) {
|
|
630
|
-
if (!nodeEntry?.model) return;
|
|
631
|
-
|
|
632
|
-
const companyId = String(result?.companyId || '').trim();
|
|
633
|
-
const status = String(result?.status || '').trim();
|
|
634
|
-
if (!companyId) {
|
|
635
|
-
applyCompanyBindingValues(nodeEntry.model, {
|
|
636
|
-
'CompanyCore.CompanyId': null,
|
|
637
|
-
'CompanyCore.RunId': null,
|
|
638
|
-
'CompanyCore.ExecutionMode': result?.executionMode || fallbackExecutionMode || 'Auto',
|
|
639
|
-
'CompanyCore.SubAgentsEnabled': typeof result?.subAgentsEnabled === 'boolean'
|
|
640
|
-
? (result.subAgentsEnabled ? 'true' : 'false')
|
|
641
|
-
: (fallbackEnableSubAgents ? 'true' : 'false'),
|
|
642
|
-
'CompanyCore.Status': status || 'Failed',
|
|
643
|
-
'CompanyCore.LastMessage': result?.lastMessage || 'Company did not start from this note.',
|
|
644
|
-
'CompanyCore.PendingApprovalCount': String(result?.pendingApprovalCount ?? 0),
|
|
645
|
-
'CompanyCore.RunningAgentCount': String(result?.runningAgentCount ?? 0),
|
|
646
|
-
'CompanyCore.WorkItemCount': String(result?.workItemCount ?? 0),
|
|
647
|
-
'CompanyCore.ArtifactCount': String(result?.artifactCount ?? 0),
|
|
648
|
-
'CompanyCore.GrowthResultCount': String(result?.growthResultCount ?? 0),
|
|
649
|
-
'CompanyCore.RevenueResultCount': String(result?.revenueResultCount ?? 0),
|
|
650
|
-
'CompanyCore.LastSnapshotAt': result?.lastSnapshotAt || null
|
|
651
|
-
});
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
applyCompanyBindingValues(nodeEntry.model, {
|
|
656
|
-
'CompanyCore.CompanyId': companyId,
|
|
657
|
-
'CompanyCore.RunId': result?.runId || null,
|
|
658
|
-
'CompanyCore.ExecutionMode': result?.executionMode || fallbackExecutionMode || 'Auto',
|
|
659
|
-
'CompanyCore.SubAgentsEnabled': typeof result?.subAgentsEnabled === 'boolean'
|
|
660
|
-
? (result.subAgentsEnabled ? 'true' : 'false')
|
|
661
|
-
: (fallbackEnableSubAgents ? 'true' : 'false'),
|
|
662
|
-
'CompanyCore.Status': status || 'Queued',
|
|
663
|
-
'CompanyCore.PendingApprovalCount': String(result?.pendingApprovalCount ?? 0),
|
|
664
|
-
'CompanyCore.RunningAgentCount': String(result?.runningAgentCount ?? 0),
|
|
665
|
-
'CompanyCore.WorkItemCount': String(result?.workItemCount ?? 0),
|
|
666
|
-
'CompanyCore.ArtifactCount': String(result?.artifactCount ?? 0),
|
|
667
|
-
'CompanyCore.GrowthResultCount': String(result?.growthResultCount ?? 0),
|
|
668
|
-
'CompanyCore.RevenueResultCount': String(result?.revenueResultCount ?? 0),
|
|
669
|
-
'CompanyCore.LastSnapshotAt': result?.lastSnapshotAt || null,
|
|
670
|
-
'CompanyCore.LastMessage': result?.lastMessage || 'Company is running from this note in the background.'
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
function clearCompanyBindingValues(nodeEntry) {
|
|
675
|
-
if (!nodeEntry?.model) return;
|
|
676
|
-
|
|
677
|
-
applyCompanyBindingValues(nodeEntry.model, {
|
|
678
|
-
'CompanyCore.CompanyId': null,
|
|
679
|
-
'CompanyCore.RunId': null,
|
|
680
|
-
'CompanyCore.CoreBaseUrl': null,
|
|
681
|
-
'CompanyCore.SourceNodeId': null,
|
|
682
|
-
'CompanyCore.BoardId': null,
|
|
683
|
-
'CompanyCore.ExecutionMode': null,
|
|
684
|
-
'CompanyCore.SubAgentsEnabled': null,
|
|
685
|
-
'CompanyCore.Status': null,
|
|
686
|
-
'CompanyCore.LastMessage': null,
|
|
687
|
-
'CompanyCore.PendingApprovalCount': null,
|
|
688
|
-
'CompanyCore.RunningAgentCount': null,
|
|
689
|
-
'CompanyCore.WorkItemCount': null,
|
|
690
|
-
'CompanyCore.ArtifactCount': null,
|
|
691
|
-
'CompanyCore.GrowthResultCount': null,
|
|
692
|
-
'CompanyCore.RevenueResultCount': null,
|
|
693
|
-
'CompanyCore.LastSnapshotAt': null
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function syncCompanyNodeVisual(nodeEntry) {
|
|
698
|
-
if (!nodeEntry?.cssObject || !window.MindMapCss3DManager?.syncCss3dContent) {
|
|
699
|
-
return;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
nodeEntry.isCssDirty = true;
|
|
703
|
-
window.MindMapCss3DManager.syncCss3dContent(nodeEntry.model, nodeEntry.cssObject);
|
|
704
|
-
nodeEntry.isCssDirty = false;
|
|
705
|
-
_module?.lodRenderer?.requestResidentPatch?.('update-node', nodeEntry.model?.id || nodeEntry.model?.Id);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
function canRunAsCompany(model) {
|
|
709
|
-
if (!model) return false;
|
|
710
|
-
if (isCompanyBoundNode(model)) return true;
|
|
711
|
-
|
|
712
|
-
const contentType = getNodeContentType(model);
|
|
713
|
-
if (!contentType) return true;
|
|
714
|
-
|
|
715
|
-
if (['image', 'video', 'pdf', 'file', 'directory'].includes(contentType)) {
|
|
716
|
-
return false;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
if (['note', 'text', 'markdown', 'memo', 'code', 'mask'].includes(contentType)) {
|
|
720
|
-
return true;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
return getNodeSourceFilePath(model).length === 0;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function getMemoColorKey(model) {
|
|
727
|
-
const metadata = ensureMetadata(model);
|
|
728
|
-
const fallbackKey = isAgentStyledMemoNode(model) ? 'gray' : 'coral';
|
|
729
|
-
const key = String(metadata.memoColor || fallbackKey).toLowerCase();
|
|
730
|
-
return MEMO_COLORS.some(color => color.key === key) ? key : fallbackKey;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function buildMemoColorPopover(selectedKey) {
|
|
734
|
-
const popover = document.createElement('div');
|
|
735
|
-
popover.className = 'menu-color-popover';
|
|
736
|
-
|
|
737
|
-
MEMO_COLORS.forEach(color => {
|
|
738
|
-
const button = document.createElement('button');
|
|
739
|
-
button.type = 'button';
|
|
740
|
-
button.className = 'menu-color-swatch';
|
|
741
|
-
button.dataset.colorKey = color.key;
|
|
742
|
-
button.title = color.key;
|
|
743
|
-
button.style.setProperty('--swatch-color', color.swatch);
|
|
744
|
-
button.style.setProperty('--swatch-ring', color.ring);
|
|
745
|
-
button.classList.toggle('is-active', color.key === selectedKey);
|
|
746
|
-
popover.appendChild(button);
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
return popover;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
function syncMemoColorPopover(popover, selectedKey) {
|
|
753
|
-
if (!popover) return;
|
|
754
|
-
popover.querySelectorAll('.menu-color-swatch').forEach(button => {
|
|
755
|
-
button.classList.toggle('is-active', button.dataset.colorKey === selectedKey);
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
function refreshMemoNode(nodeEntry) {
|
|
760
|
-
if (!nodeEntry) return;
|
|
761
|
-
nodeEntry.isCssDirty = false;
|
|
762
|
-
if (nodeEntry.cssObject && window.MindMapCss3DManager?.syncCss3dContent) {
|
|
763
|
-
window.MindMapCss3DManager.syncCss3dContent(nodeEntry.model, nodeEntry.cssObject);
|
|
764
|
-
}
|
|
765
|
-
if (_module?.lodRenderer?.requestResidentPatch) {
|
|
766
|
-
_module.lodRenderer.requestResidentPatch('update-node', nodeEntry.model?.id || nodeEntry.model?.Id);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
6
|
+
let currentMenuElement = null; // DOM overlay element
|
|
7
|
+
let currentMenuNodeId = null;
|
|
8
|
+
let _module = null;
|
|
9
|
+
const MEMO_COLORS = [
|
|
10
|
+
{ key: 'coral', swatch: '#ff8052', ring: 'rgba(255, 128, 82, 0.22)' },
|
|
11
|
+
{ key: 'amber', swatch: '#f59e0b', ring: 'rgba(245, 158, 11, 0.22)' },
|
|
12
|
+
{ key: 'sun', swatch: '#ca8a04', ring: 'rgba(202, 138, 4, 0.22)' },
|
|
13
|
+
{ key: 'sage', swatch: '#65a30d', ring: 'rgba(101, 163, 13, 0.22)' },
|
|
14
|
+
{ key: 'mint', swatch: '#10b981', ring: 'rgba(16, 185, 129, 0.22)' },
|
|
15
|
+
{ key: 'sky', swatch: '#3b82f6', ring: 'rgba(59, 130, 246, 0.22)' },
|
|
16
|
+
{ key: 'indigo', swatch: '#6366f1', ring: 'rgba(99, 102, 241, 0.22)' },
|
|
17
|
+
{ key: 'violet', swatch: '#8b5cf6', ring: 'rgba(139, 92, 246, 0.22)' },
|
|
18
|
+
{ key: 'rose', swatch: '#f43f5e', ring: 'rgba(244, 63, 94, 0.22)' },
|
|
19
|
+
{ key: 'gray', swatch: '#6b7280', ring: 'rgba(107, 114, 128, 0.22)' },
|
|
20
|
+
{ key: 'slate', swatch: '#64748b', ring: 'rgba(100, 116, 139, 0.22)' }
|
|
21
|
+
];
|
|
22
|
+
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
23
|
+
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
24
|
+
|
|
25
|
+
function ensureFeedbackStyles() {
|
|
26
|
+
if (document.getElementById('mindmap-feedback-styles')) return;
|
|
27
|
+
|
|
28
|
+
const style = document.createElement('style');
|
|
29
|
+
style.id = 'mindmap-feedback-styles';
|
|
30
|
+
style.textContent = `
|
|
31
|
+
.mindmap-toast-stack {
|
|
32
|
+
position: fixed;
|
|
33
|
+
right: 22px;
|
|
34
|
+
bottom: 24px;
|
|
35
|
+
z-index: 2147483000;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
gap: 10px;
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.mindmap-toast {
|
|
43
|
+
width: min(380px, calc(100vw - 32px));
|
|
44
|
+
padding: 12px 14px;
|
|
45
|
+
border-radius: 16px;
|
|
46
|
+
background: rgba(15, 23, 42, 0.94);
|
|
47
|
+
color: #f8fafc;
|
|
48
|
+
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.24);
|
|
49
|
+
font: 600 13px/1.45 Inter, ui-sans-serif, system-ui, sans-serif;
|
|
50
|
+
opacity: 0;
|
|
51
|
+
transform: translateY(8px);
|
|
52
|
+
transition: opacity 180ms ease, transform 180ms ease;
|
|
53
|
+
pointer-events: auto;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.mindmap-toast.is-visible {
|
|
57
|
+
opacity: 1;
|
|
58
|
+
transform: translateY(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.mindmap-toast[data-kind="error"] {
|
|
62
|
+
background: rgba(127, 29, 29, 0.94);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.mindmap-confirm-backdrop {
|
|
66
|
+
position: fixed;
|
|
67
|
+
inset: 0;
|
|
68
|
+
z-index: 2147483001;
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
padding: 24px;
|
|
73
|
+
background: rgba(15, 23, 42, 0.20);
|
|
74
|
+
backdrop-filter: blur(5px);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.mindmap-confirm-panel {
|
|
78
|
+
width: min(440px, calc(100vw - 36px));
|
|
79
|
+
border: 1px solid rgba(203, 213, 225, 0.86);
|
|
80
|
+
border-radius: 22px;
|
|
81
|
+
background: rgba(248, 250, 252, 0.98);
|
|
82
|
+
box-shadow: 0 28px 72px rgba(15, 23, 42, 0.22);
|
|
83
|
+
padding: 20px;
|
|
84
|
+
color: #0f172a;
|
|
85
|
+
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.mindmap-confirm-title {
|
|
89
|
+
margin: 0;
|
|
90
|
+
font-size: 15px;
|
|
91
|
+
font-weight: 800;
|
|
92
|
+
letter-spacing: 0.01em;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.mindmap-confirm-message {
|
|
96
|
+
margin: 8px 0 0;
|
|
97
|
+
color: #475569;
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
line-height: 1.55;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.mindmap-confirm-actions {
|
|
103
|
+
display: flex;
|
|
104
|
+
justify-content: flex-end;
|
|
105
|
+
gap: 10px;
|
|
106
|
+
margin-top: 18px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.mindmap-confirm-button {
|
|
110
|
+
border: 0;
|
|
111
|
+
border-radius: 999px;
|
|
112
|
+
padding: 9px 15px;
|
|
113
|
+
font-size: 13px;
|
|
114
|
+
font-weight: 800;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
transition: transform 120ms ease, background 120ms ease;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.mindmap-confirm-button:active {
|
|
120
|
+
transform: scale(0.98);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.mindmap-confirm-button.secondary {
|
|
124
|
+
background: #e0f2fe;
|
|
125
|
+
color: #075985;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.mindmap-confirm-button.primary {
|
|
129
|
+
background: #0f172a;
|
|
130
|
+
color: #ffffff;
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
document.head.appendChild(style);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getToastStack() {
|
|
137
|
+
ensureFeedbackStyles();
|
|
138
|
+
let stack = document.querySelector('.mindmap-toast-stack');
|
|
139
|
+
if (!stack) {
|
|
140
|
+
stack = document.createElement('div');
|
|
141
|
+
stack.className = 'mindmap-toast-stack';
|
|
142
|
+
document.body.appendChild(stack);
|
|
143
|
+
}
|
|
144
|
+
return stack;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function showToast(message, options = {}) {
|
|
148
|
+
const text = String(message || '').trim();
|
|
149
|
+
if (!text || !document?.body) return;
|
|
150
|
+
|
|
151
|
+
const toast = document.createElement('div');
|
|
152
|
+
toast.className = 'mindmap-toast';
|
|
153
|
+
toast.dataset.kind = options.kind || 'info';
|
|
154
|
+
toast.textContent = text;
|
|
155
|
+
|
|
156
|
+
getToastStack().appendChild(toast);
|
|
157
|
+
requestAnimationFrame(() => toast.classList.add('is-visible'));
|
|
158
|
+
|
|
159
|
+
window.setTimeout(() => {
|
|
160
|
+
toast.classList.remove('is-visible');
|
|
161
|
+
window.setTimeout(() => toast.remove(), 220);
|
|
162
|
+
}, options.duration || 2600);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function showConfirmDialog(options = {}) {
|
|
166
|
+
if (!document?.body) return Promise.resolve(false);
|
|
167
|
+
|
|
168
|
+
ensureFeedbackStyles();
|
|
169
|
+
|
|
170
|
+
return new Promise((resolve) => {
|
|
171
|
+
const backdrop = document.createElement('div');
|
|
172
|
+
backdrop.className = 'mindmap-confirm-backdrop';
|
|
173
|
+
|
|
174
|
+
const panel = document.createElement('div');
|
|
175
|
+
panel.className = 'mindmap-confirm-panel';
|
|
176
|
+
panel.setAttribute('role', 'dialog');
|
|
177
|
+
panel.setAttribute('aria-modal', 'true');
|
|
178
|
+
|
|
179
|
+
const title = document.createElement('h3');
|
|
180
|
+
title.className = 'mindmap-confirm-title';
|
|
181
|
+
title.textContent = options.title || 'Confirm action';
|
|
182
|
+
|
|
183
|
+
const message = document.createElement('p');
|
|
184
|
+
message.className = 'mindmap-confirm-message';
|
|
185
|
+
message.textContent = options.message || 'Do you want to continue?';
|
|
186
|
+
|
|
187
|
+
const actions = document.createElement('div');
|
|
188
|
+
actions.className = 'mindmap-confirm-actions';
|
|
189
|
+
|
|
190
|
+
const cancel = document.createElement('button');
|
|
191
|
+
cancel.type = 'button';
|
|
192
|
+
cancel.className = 'mindmap-confirm-button secondary';
|
|
193
|
+
cancel.textContent = options.cancelText || 'Cancel';
|
|
194
|
+
|
|
195
|
+
const confirm = document.createElement('button');
|
|
196
|
+
confirm.type = 'button';
|
|
197
|
+
confirm.className = 'mindmap-confirm-button primary';
|
|
198
|
+
confirm.textContent = options.confirmText || 'Confirm';
|
|
199
|
+
|
|
200
|
+
const close = (value) => {
|
|
201
|
+
backdrop.remove();
|
|
202
|
+
document.removeEventListener('keydown', handleKeyDown, true);
|
|
203
|
+
resolve(!!value);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const handleKeyDown = (event) => {
|
|
207
|
+
if (event.key === 'Escape') {
|
|
208
|
+
event.preventDefault();
|
|
209
|
+
close(false);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
cancel.addEventListener('click', () => close(false));
|
|
214
|
+
confirm.addEventListener('click', () => close(true));
|
|
215
|
+
backdrop.addEventListener('click', (event) => {
|
|
216
|
+
if (event.target === backdrop) close(false);
|
|
217
|
+
});
|
|
218
|
+
document.addEventListener('keydown', handleKeyDown, true);
|
|
219
|
+
|
|
220
|
+
actions.appendChild(cancel);
|
|
221
|
+
actions.appendChild(confirm);
|
|
222
|
+
panel.appendChild(title);
|
|
223
|
+
panel.appendChild(message);
|
|
224
|
+
panel.appendChild(actions);
|
|
225
|
+
backdrop.appendChild(panel);
|
|
226
|
+
document.body.appendChild(backdrop);
|
|
227
|
+
confirm.focus({ preventScroll: true });
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
window.MindMapFeedback = window.MindMapFeedback || {};
|
|
232
|
+
window.MindMapFeedback.toast = showToast;
|
|
233
|
+
window.MindMapFeedback.confirm = showConfirmDialog;
|
|
234
|
+
|
|
235
|
+
function init(module) {
|
|
236
|
+
_module = module;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function ensureMetadata(model) {
|
|
240
|
+
if (!model) return {};
|
|
241
|
+
if (model.metadata && typeof model.metadata === 'object') return model.metadata;
|
|
242
|
+
if (model.Metadata && typeof model.Metadata === 'object') {
|
|
243
|
+
model.metadata = model.Metadata;
|
|
244
|
+
return model.metadata;
|
|
245
|
+
}
|
|
246
|
+
model.metadata = {};
|
|
247
|
+
model.Metadata = model.metadata;
|
|
248
|
+
return model.metadata;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function getNodeSemanticType(model) {
|
|
252
|
+
const metadata = ensureMetadata(model);
|
|
253
|
+
return String(metadata.SemanticType || metadata.semanticType || '').trim();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function isAgentStyledMemoNode(model) {
|
|
257
|
+
const semanticType = getNodeSemanticType(model);
|
|
258
|
+
return semanticType === 'MindCanvasAgent'
|
|
259
|
+
|| semanticType === 'AgentCommand'
|
|
260
|
+
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function isBusinessAutomationNode(model) {
|
|
264
|
+
return getNodeSemanticType(model) === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getBusinessAutomationNodeKind(model) {
|
|
268
|
+
const metadata = ensureMetadata(model);
|
|
269
|
+
return String(metadata.AutomationNodeKind || metadata.automationNodeKind || '').trim().toLowerCase();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function getBusinessAutomationNodeRole(model) {
|
|
273
|
+
const metadata = ensureMetadata(model);
|
|
274
|
+
return String(metadata.AutomationNodeRole || metadata.automationNodeRole || '').trim().toLowerCase();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isBusinessAutomationRootAiTaskNode(model) {
|
|
278
|
+
if (!isBusinessAutomationNode(model) || getBusinessAutomationNodeKind(model) !== 'ai-task') {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const role = getBusinessAutomationNodeRole(model);
|
|
283
|
+
return role.length === 0 || role === 'root';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function isRemoteFleetMonitorNode(model) {
|
|
287
|
+
return getNodeSemanticType(model) === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function canRunExecutableNode(model) {
|
|
291
|
+
return isBusinessAutomationRootAiTaskNode(model);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function isExecutableNodeRunning(model) {
|
|
295
|
+
const metadata = ensureMetadata(model);
|
|
296
|
+
return String(metadata.AutomationRunState || '').trim().toLowerCase() === 'running';
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function setExecutableNodeRunVisualState(nodeId, runState, statusLabel) {
|
|
300
|
+
const normalizedNodeId = String(nodeId || '').trim();
|
|
301
|
+
if (!normalizedNodeId || !_module?.nodeObjectsById) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const entry = _module.nodeObjectsById.get(normalizedNodeId);
|
|
306
|
+
if (!entry?.model) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const metadata = ensureMetadata(entry.model);
|
|
311
|
+
const normalizedRunState = String(runState || '').trim().toLowerCase();
|
|
312
|
+
metadata.AutomationRunState = normalizedRunState || 'idle';
|
|
313
|
+
|
|
314
|
+
if (normalizedRunState === 'running') {
|
|
315
|
+
metadata.AutomationLastRunStatus = 'running';
|
|
316
|
+
metadata.AutomationLastRunStatusLabel = statusLabel || 'Running';
|
|
317
|
+
metadata.AutomationLastRunError = '';
|
|
318
|
+
} else if (statusLabel) {
|
|
319
|
+
metadata.AutomationLastRunStatusLabel = statusLabel;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
entry.model.metadata = metadata;
|
|
323
|
+
entry.model.Metadata = metadata;
|
|
324
|
+
|
|
325
|
+
if (entry.cssObject && window.MindMapCss3DManager?.syncCss3dContent) {
|
|
326
|
+
try {
|
|
327
|
+
entry.isCssDirty = true;
|
|
328
|
+
window.MindMapCss3DManager.syncCss3dContent(entry.model, entry.cssObject);
|
|
329
|
+
} finally {
|
|
330
|
+
entry.isCssDirty = false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
_module.lodRenderer?.requestVisualRefresh?.('node-run-visual-state');
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function syncNodeRunResultStates(result) {
|
|
339
|
+
if (!result) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const nodeStates = Array.isArray(result.nodeStates)
|
|
344
|
+
? result.nodeStates
|
|
345
|
+
: (result.agentNodeState ? [result.agentNodeState] : []);
|
|
346
|
+
|
|
347
|
+
if (nodeStates.length > 0 && window.mindMap?.syncNodeStates) {
|
|
348
|
+
await window.mindMap.syncNodeStates(nodeStates);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function isTextFitNode(model) {
|
|
353
|
+
const contentType = String(model?.contentType || '').toLowerCase();
|
|
354
|
+
return contentType === 'text' || contentType === 'markdown' || contentType === 'note' || contentType === 'code';
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function getNodeContentType(model) {
|
|
358
|
+
return String(model?.contentType || '').trim().toLowerCase();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function showActionSuccessIcon(button) {
|
|
362
|
+
const icon = button?.querySelector?.('i');
|
|
363
|
+
if (!icon) return;
|
|
364
|
+
|
|
365
|
+
const oldClass = icon.className;
|
|
366
|
+
icon.className = 'fa-solid fa-check text-green-500';
|
|
367
|
+
setTimeout(() => {
|
|
368
|
+
icon.className = oldClass;
|
|
369
|
+
}, 1000);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function getImageClipboardSourceCandidates(nodeModel) {
|
|
373
|
+
const assetUrls = window.MindMapNodes?.getNodeImageAssetUrls?.(nodeModel) || {};
|
|
374
|
+
const rawCandidates = [
|
|
375
|
+
assetUrls.fullResUrl,
|
|
376
|
+
assetUrls.originalUrl,
|
|
377
|
+
assetUrls.originalPath,
|
|
378
|
+
assetUrls.previewUrl,
|
|
379
|
+
assetUrls.responseUrl,
|
|
380
|
+
nodeModel?.response,
|
|
381
|
+
nodeModel?.Response
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
const candidates = [];
|
|
385
|
+
rawCandidates.forEach(value => {
|
|
386
|
+
const normalized = typeof value === 'string' ? value.trim() : '';
|
|
387
|
+
if (!normalized || candidates.includes(normalized)) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
candidates.push(normalized);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
return candidates;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function shouldResolveImageClipboardSourceBeforeFetch(src) {
|
|
398
|
+
const normalized = typeof src === 'string' ? src.trim().toLowerCase() : '';
|
|
399
|
+
if (!normalized || normalized.startsWith('blob:') || normalized.startsWith('data:') || normalized.startsWith('file:')) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return normalized.startsWith('/assets/')
|
|
404
|
+
|| normalized.startsWith('assets/')
|
|
405
|
+
|| normalized.includes('/assets/thumbs/')
|
|
406
|
+
|| (normalized.includes('/assets/') && !/\.[a-z0-9]{2,5}(?:[?#]|$)/i.test(normalized));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async function resolveClipboardImageSource(nodeModel, src) {
|
|
410
|
+
if (!shouldResolveImageClipboardSourceBeforeFetch(src) || !_module?.dotNetHelper) {
|
|
411
|
+
return src;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const nodeId = String(nodeModel?.id || nodeModel?.Id || '').trim();
|
|
415
|
+
if (!nodeId) {
|
|
416
|
+
return src;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
const resolved = await _module.dotNetHelper.invokeMethodAsync('ResolveNodeAssetUrl', nodeId);
|
|
421
|
+
const normalized = typeof resolved === 'string' ? resolved.trim() : '';
|
|
422
|
+
return normalized || src;
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.warn('[MindMapMenuManager] Failed to resolve image clipboard source.', error);
|
|
425
|
+
return src;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function fetchClipboardImageBlob(nodeModel) {
|
|
430
|
+
const candidates = getImageClipboardSourceCandidates(nodeModel);
|
|
431
|
+
let lastError = null;
|
|
432
|
+
|
|
433
|
+
for (const src of candidates) {
|
|
434
|
+
try {
|
|
435
|
+
const resolvedSrc = await resolveClipboardImageSource(nodeModel, src);
|
|
436
|
+
const response = await fetch(resolvedSrc, { cache: 'no-store' });
|
|
437
|
+
if (!response.ok) {
|
|
438
|
+
throw new Error(`HTTP ${response.status}`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const blob = await response.blob();
|
|
442
|
+
if (!(blob instanceof Blob) || blob.size <= 0) {
|
|
443
|
+
throw new Error('Empty image blob.');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return { blob, src: resolvedSrc };
|
|
447
|
+
} catch (error) {
|
|
448
|
+
lastError = error;
|
|
449
|
+
console.warn(`[MindMapMenuManager] Failed to fetch clipboard image from ${src}`, error);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
throw lastError || new Error('No image source was available for clipboard copy.');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function decodeClipboardImageBlob(blob) {
|
|
457
|
+
if (typeof createImageBitmap === 'function') {
|
|
458
|
+
try {
|
|
459
|
+
const bitmap = await createImageBitmap(blob);
|
|
460
|
+
return {
|
|
461
|
+
width: bitmap.width,
|
|
462
|
+
height: bitmap.height,
|
|
463
|
+
draw(ctx, width, height) {
|
|
464
|
+
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
465
|
+
},
|
|
466
|
+
dispose() {
|
|
467
|
+
bitmap.close?.();
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.warn('[MindMapMenuManager] createImageBitmap failed for clipboard image conversion.', error);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
476
|
+
try {
|
|
477
|
+
const image = await new Promise((resolve, reject) => {
|
|
478
|
+
const img = new Image();
|
|
479
|
+
img.decoding = 'async';
|
|
480
|
+
img.onload = () => resolve(img);
|
|
481
|
+
img.onerror = () => reject(new Error('Failed to decode image blob.'));
|
|
482
|
+
img.src = objectUrl;
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
width: image.naturalWidth || image.width,
|
|
487
|
+
height: image.naturalHeight || image.height,
|
|
488
|
+
draw(ctx, width, height) {
|
|
489
|
+
ctx.drawImage(image, 0, 0, width, height);
|
|
490
|
+
},
|
|
491
|
+
dispose() {
|
|
492
|
+
image.remove?.();
|
|
493
|
+
URL.revokeObjectURL(objectUrl);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
} catch (error) {
|
|
497
|
+
URL.revokeObjectURL(objectUrl);
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
async function convertBlobToPngForClipboard(blob) {
|
|
503
|
+
const normalizedType = String(blob?.type || '').toLowerCase();
|
|
504
|
+
if (normalizedType === 'image/png' && blob.size > 0) {
|
|
505
|
+
return blob;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const decoded = await decodeClipboardImageBlob(blob);
|
|
509
|
+
const width = Math.max(1, Math.round(decoded?.width || 0));
|
|
510
|
+
const height = Math.max(1, Math.round(decoded?.height || 0));
|
|
511
|
+
|
|
512
|
+
if (!width || !height) {
|
|
513
|
+
decoded?.dispose?.();
|
|
514
|
+
throw new Error('Decoded image had no dimensions.');
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
if (typeof OffscreenCanvas === 'function') {
|
|
519
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
520
|
+
const ctx = canvas.getContext('2d');
|
|
521
|
+
if (!ctx) {
|
|
522
|
+
throw new Error('OffscreenCanvas 2D context is unavailable.');
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
decoded.draw(ctx, width, height);
|
|
526
|
+
return await canvas.convertToBlob({ type: 'image/png' });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const canvas = document.createElement('canvas');
|
|
530
|
+
canvas.width = width;
|
|
531
|
+
canvas.height = height;
|
|
532
|
+
const ctx = canvas.getContext('2d');
|
|
533
|
+
if (!ctx) {
|
|
534
|
+
throw new Error('Canvas 2D context is unavailable.');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
decoded.draw(ctx, width, height);
|
|
538
|
+
return await new Promise((resolve, reject) => {
|
|
539
|
+
canvas.toBlob(result => {
|
|
540
|
+
if (result instanceof Blob && result.size > 0) {
|
|
541
|
+
resolve(result);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
reject(new Error('Failed to encode clipboard image as PNG.'));
|
|
546
|
+
}, 'image/png');
|
|
547
|
+
});
|
|
548
|
+
} finally {
|
|
549
|
+
decoded?.dispose?.();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async function writeImageBlobToClipboard(blob, fallbackText) {
|
|
554
|
+
if (navigator.clipboard?.write && typeof ClipboardItem === 'function') {
|
|
555
|
+
await navigator.clipboard.write([
|
|
556
|
+
new ClipboardItem({ 'image/png': blob })
|
|
557
|
+
]);
|
|
558
|
+
return 'image';
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (navigator.clipboard?.writeText && fallbackText) {
|
|
562
|
+
await navigator.clipboard.writeText(fallbackText);
|
|
563
|
+
return 'text';
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
throw new Error('Clipboard API is unavailable.');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function getNodeSourceFilePath(model) {
|
|
570
|
+
return String(model?.sourceFilePath || model?.SourceFilePath || '').trim();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function getCompanyBindingValue(model, key) {
|
|
574
|
+
const metadata = ensureMetadata(model);
|
|
575
|
+
return String(metadata[key] || '').trim();
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function isCompanyBoundNode(model) {
|
|
579
|
+
return getCompanyBindingValue(model, 'CompanyCore.CompanyId').length > 0;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function getCompanyStatus(model) {
|
|
583
|
+
return getCompanyBindingValue(model, 'CompanyCore.Status').toLowerCase();
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function isPendingCompanyBinding(model) {
|
|
587
|
+
const companyId = getCompanyBindingValue(model, 'CompanyCore.CompanyId');
|
|
588
|
+
if (companyId.startsWith('pending:')) {
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const status = getCompanyStatus(model);
|
|
593
|
+
const runId = getCompanyBindingValue(model, 'CompanyCore.RunId');
|
|
594
|
+
return !companyId && status === 'queued' && !runId;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function applyCompanyBindingValues(model, values) {
|
|
598
|
+
const metadata = ensureMetadata(model);
|
|
599
|
+
Object.entries(values || {}).forEach(([key, value]) => {
|
|
600
|
+
if (value === null || value === undefined || value === '') {
|
|
601
|
+
delete metadata[key];
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
metadata[key] = String(value);
|
|
606
|
+
});
|
|
607
|
+
model.metadata = metadata;
|
|
608
|
+
model.Metadata = metadata;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function applyOptimisticCompanyRunState(nodeEntry, nodeId, executionMode, enableSubAgents) {
|
|
612
|
+
if (!nodeEntry?.model) return;
|
|
613
|
+
|
|
614
|
+
applyCompanyBindingValues(nodeEntry.model, {
|
|
615
|
+
'CompanyCore.CompanyId': `pending:${nodeId}`,
|
|
616
|
+
'CompanyCore.ExecutionMode': executionMode || 'Auto',
|
|
617
|
+
'CompanyCore.SubAgentsEnabled': enableSubAgents ? 'true' : 'false',
|
|
618
|
+
'CompanyCore.Status': 'Queued',
|
|
619
|
+
'CompanyCore.LastMessage': 'Starting company from this note...',
|
|
620
|
+
'CompanyCore.PendingApprovalCount': '0',
|
|
621
|
+
'CompanyCore.RunningAgentCount': '0',
|
|
622
|
+
'CompanyCore.WorkItemCount': '0',
|
|
623
|
+
'CompanyCore.ArtifactCount': '0',
|
|
624
|
+
'CompanyCore.GrowthResultCount': '0',
|
|
625
|
+
'CompanyCore.RevenueResultCount': '0'
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function applyCompanyRunResult(nodeEntry, result, fallbackExecutionMode, fallbackEnableSubAgents) {
|
|
630
|
+
if (!nodeEntry?.model) return;
|
|
631
|
+
|
|
632
|
+
const companyId = String(result?.companyId || '').trim();
|
|
633
|
+
const status = String(result?.status || '').trim();
|
|
634
|
+
if (!companyId) {
|
|
635
|
+
applyCompanyBindingValues(nodeEntry.model, {
|
|
636
|
+
'CompanyCore.CompanyId': null,
|
|
637
|
+
'CompanyCore.RunId': null,
|
|
638
|
+
'CompanyCore.ExecutionMode': result?.executionMode || fallbackExecutionMode || 'Auto',
|
|
639
|
+
'CompanyCore.SubAgentsEnabled': typeof result?.subAgentsEnabled === 'boolean'
|
|
640
|
+
? (result.subAgentsEnabled ? 'true' : 'false')
|
|
641
|
+
: (fallbackEnableSubAgents ? 'true' : 'false'),
|
|
642
|
+
'CompanyCore.Status': status || 'Failed',
|
|
643
|
+
'CompanyCore.LastMessage': result?.lastMessage || 'Company did not start from this note.',
|
|
644
|
+
'CompanyCore.PendingApprovalCount': String(result?.pendingApprovalCount ?? 0),
|
|
645
|
+
'CompanyCore.RunningAgentCount': String(result?.runningAgentCount ?? 0),
|
|
646
|
+
'CompanyCore.WorkItemCount': String(result?.workItemCount ?? 0),
|
|
647
|
+
'CompanyCore.ArtifactCount': String(result?.artifactCount ?? 0),
|
|
648
|
+
'CompanyCore.GrowthResultCount': String(result?.growthResultCount ?? 0),
|
|
649
|
+
'CompanyCore.RevenueResultCount': String(result?.revenueResultCount ?? 0),
|
|
650
|
+
'CompanyCore.LastSnapshotAt': result?.lastSnapshotAt || null
|
|
651
|
+
});
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
applyCompanyBindingValues(nodeEntry.model, {
|
|
656
|
+
'CompanyCore.CompanyId': companyId,
|
|
657
|
+
'CompanyCore.RunId': result?.runId || null,
|
|
658
|
+
'CompanyCore.ExecutionMode': result?.executionMode || fallbackExecutionMode || 'Auto',
|
|
659
|
+
'CompanyCore.SubAgentsEnabled': typeof result?.subAgentsEnabled === 'boolean'
|
|
660
|
+
? (result.subAgentsEnabled ? 'true' : 'false')
|
|
661
|
+
: (fallbackEnableSubAgents ? 'true' : 'false'),
|
|
662
|
+
'CompanyCore.Status': status || 'Queued',
|
|
663
|
+
'CompanyCore.PendingApprovalCount': String(result?.pendingApprovalCount ?? 0),
|
|
664
|
+
'CompanyCore.RunningAgentCount': String(result?.runningAgentCount ?? 0),
|
|
665
|
+
'CompanyCore.WorkItemCount': String(result?.workItemCount ?? 0),
|
|
666
|
+
'CompanyCore.ArtifactCount': String(result?.artifactCount ?? 0),
|
|
667
|
+
'CompanyCore.GrowthResultCount': String(result?.growthResultCount ?? 0),
|
|
668
|
+
'CompanyCore.RevenueResultCount': String(result?.revenueResultCount ?? 0),
|
|
669
|
+
'CompanyCore.LastSnapshotAt': result?.lastSnapshotAt || null,
|
|
670
|
+
'CompanyCore.LastMessage': result?.lastMessage || 'Company is running from this note in the background.'
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function clearCompanyBindingValues(nodeEntry) {
|
|
675
|
+
if (!nodeEntry?.model) return;
|
|
676
|
+
|
|
677
|
+
applyCompanyBindingValues(nodeEntry.model, {
|
|
678
|
+
'CompanyCore.CompanyId': null,
|
|
679
|
+
'CompanyCore.RunId': null,
|
|
680
|
+
'CompanyCore.CoreBaseUrl': null,
|
|
681
|
+
'CompanyCore.SourceNodeId': null,
|
|
682
|
+
'CompanyCore.BoardId': null,
|
|
683
|
+
'CompanyCore.ExecutionMode': null,
|
|
684
|
+
'CompanyCore.SubAgentsEnabled': null,
|
|
685
|
+
'CompanyCore.Status': null,
|
|
686
|
+
'CompanyCore.LastMessage': null,
|
|
687
|
+
'CompanyCore.PendingApprovalCount': null,
|
|
688
|
+
'CompanyCore.RunningAgentCount': null,
|
|
689
|
+
'CompanyCore.WorkItemCount': null,
|
|
690
|
+
'CompanyCore.ArtifactCount': null,
|
|
691
|
+
'CompanyCore.GrowthResultCount': null,
|
|
692
|
+
'CompanyCore.RevenueResultCount': null,
|
|
693
|
+
'CompanyCore.LastSnapshotAt': null
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function syncCompanyNodeVisual(nodeEntry) {
|
|
698
|
+
if (!nodeEntry?.cssObject || !window.MindMapCss3DManager?.syncCss3dContent) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
nodeEntry.isCssDirty = true;
|
|
703
|
+
window.MindMapCss3DManager.syncCss3dContent(nodeEntry.model, nodeEntry.cssObject);
|
|
704
|
+
nodeEntry.isCssDirty = false;
|
|
705
|
+
_module?.lodRenderer?.requestResidentPatch?.('update-node', nodeEntry.model?.id || nodeEntry.model?.Id);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function canRunAsCompany(model) {
|
|
709
|
+
if (!model) return false;
|
|
710
|
+
if (isCompanyBoundNode(model)) return true;
|
|
711
|
+
|
|
712
|
+
const contentType = getNodeContentType(model);
|
|
713
|
+
if (!contentType) return true;
|
|
714
|
+
|
|
715
|
+
if (['image', 'video', 'pdf', 'file', 'directory'].includes(contentType)) {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (['note', 'text', 'markdown', 'memo', 'code', 'mask'].includes(contentType)) {
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return getNodeSourceFilePath(model).length === 0;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function getMemoColorKey(model) {
|
|
727
|
+
const metadata = ensureMetadata(model);
|
|
728
|
+
const fallbackKey = isAgentStyledMemoNode(model) ? 'gray' : 'coral';
|
|
729
|
+
const key = String(metadata.memoColor || fallbackKey).toLowerCase();
|
|
730
|
+
return MEMO_COLORS.some(color => color.key === key) ? key : fallbackKey;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function buildMemoColorPopover(selectedKey) {
|
|
734
|
+
const popover = document.createElement('div');
|
|
735
|
+
popover.className = 'menu-color-popover';
|
|
736
|
+
|
|
737
|
+
MEMO_COLORS.forEach(color => {
|
|
738
|
+
const button = document.createElement('button');
|
|
739
|
+
button.type = 'button';
|
|
740
|
+
button.className = 'menu-color-swatch';
|
|
741
|
+
button.dataset.colorKey = color.key;
|
|
742
|
+
button.title = color.key;
|
|
743
|
+
button.style.setProperty('--swatch-color', color.swatch);
|
|
744
|
+
button.style.setProperty('--swatch-ring', color.ring);
|
|
745
|
+
button.classList.toggle('is-active', color.key === selectedKey);
|
|
746
|
+
popover.appendChild(button);
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
return popover;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function syncMemoColorPopover(popover, selectedKey) {
|
|
753
|
+
if (!popover) return;
|
|
754
|
+
popover.querySelectorAll('.menu-color-swatch').forEach(button => {
|
|
755
|
+
button.classList.toggle('is-active', button.dataset.colorKey === selectedKey);
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function refreshMemoNode(nodeEntry) {
|
|
760
|
+
if (!nodeEntry) return;
|
|
761
|
+
nodeEntry.isCssDirty = false;
|
|
762
|
+
if (nodeEntry.cssObject && window.MindMapCss3DManager?.syncCss3dContent) {
|
|
763
|
+
window.MindMapCss3DManager.syncCss3dContent(nodeEntry.model, nodeEntry.cssObject);
|
|
764
|
+
}
|
|
765
|
+
if (_module?.lodRenderer?.requestResidentPatch) {
|
|
766
|
+
_module.lodRenderer.requestResidentPatch('update-node', nodeEntry.model?.id || nodeEntry.model?.Id);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
769
|
|
|
770
770
|
// ▼▼▼ [Refactor] Screen-space DOM overlay instead of CSS3DObject ▼▼▼
|
|
771
771
|
function showMenu(nodeId) {
|
|
@@ -842,25 +842,25 @@
|
|
|
842
842
|
}
|
|
843
843
|
|
|
844
844
|
// Menu styling — screen-space overlay
|
|
845
|
-
Object.assign(menuClone.style, {
|
|
846
|
-
position: 'fixed',
|
|
847
|
-
pointerEvents: 'auto',
|
|
848
|
-
whiteSpace: 'nowrap',
|
|
849
|
-
zIndex: '10000',
|
|
850
|
-
transform: 'translate(-50%, -100%)',
|
|
851
|
-
overflow: 'visible'
|
|
852
|
-
});
|
|
845
|
+
Object.assign(menuClone.style, {
|
|
846
|
+
position: 'fixed',
|
|
847
|
+
pointerEvents: 'auto',
|
|
848
|
+
whiteSpace: 'nowrap',
|
|
849
|
+
zIndex: '10000',
|
|
850
|
+
transform: 'translate(-50%, -100%)',
|
|
851
|
+
overflow: 'visible'
|
|
852
|
+
});
|
|
853
853
|
|
|
854
854
|
// Stop event bubbling
|
|
855
|
-
const stopProp = (e) => {
|
|
856
|
-
if (e.button === 1 || e.button === 2) {
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
e.stopPropagation();
|
|
860
|
-
};
|
|
861
|
-
menuClone.addEventListener('mousedown', stopProp);
|
|
862
|
-
menuClone.addEventListener('mouseup', stopProp);
|
|
863
|
-
menuClone.addEventListener('click', stopProp);
|
|
855
|
+
const stopProp = (e) => {
|
|
856
|
+
if (e.button === 1 || e.button === 2) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
e.stopPropagation();
|
|
860
|
+
};
|
|
861
|
+
menuClone.addEventListener('mousedown', stopProp);
|
|
862
|
+
menuClone.addEventListener('mouseup', stopProp);
|
|
863
|
+
menuClone.addEventListener('click', stopProp);
|
|
864
864
|
menuClone.addEventListener('dblclick', stopProp);
|
|
865
865
|
|
|
866
866
|
// Bind button actions
|
|
@@ -869,29 +869,29 @@
|
|
|
869
869
|
// Append to body
|
|
870
870
|
document.body.appendChild(menuClone);
|
|
871
871
|
|
|
872
|
-
// Position on screen
|
|
873
|
-
updateMenuScreenPosition(menuClone, nodeEntry);
|
|
874
|
-
requestAnimationFrame(() => {
|
|
875
|
-
if (currentMenuElement === menuClone && currentMenuNodeId === nodeId) {
|
|
876
|
-
updateMenuScreenPosition(menuClone, nodeEntry);
|
|
877
|
-
}
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
currentMenuElement = menuClone;
|
|
881
|
-
currentMenuNodeId = nodeId;
|
|
882
|
-
}
|
|
872
|
+
// Position on screen
|
|
873
|
+
updateMenuScreenPosition(menuClone, nodeEntry);
|
|
874
|
+
requestAnimationFrame(() => {
|
|
875
|
+
if (currentMenuElement === menuClone && currentMenuNodeId === nodeId) {
|
|
876
|
+
updateMenuScreenPosition(menuClone, nodeEntry);
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
currentMenuElement = menuClone;
|
|
881
|
+
currentMenuNodeId = nodeId;
|
|
882
|
+
}
|
|
883
883
|
// ▲▲▲ [Refactor] ▲▲▲
|
|
884
884
|
|
|
885
|
-
function createMenuContainer(nodeEntry) {
|
|
886
|
-
const contentType = nodeEntry.model.contentType;
|
|
887
|
-
const prompt = nodeEntry.model.prompt || '';
|
|
888
|
-
const isAiEnabled = nodeEntry.model.isAiEnabled !== false;
|
|
889
|
-
const isWordWrap = nodeEntry.model.isWordWrap === true;
|
|
890
|
-
const companyBound = isCompanyBoundNode(nodeEntry.model);
|
|
891
|
-
const companyStatus = getCompanyStatus(nodeEntry.model);
|
|
892
|
-
|
|
893
|
-
const container = document.createElement('div');
|
|
894
|
-
container.className = 'node-menu-container';
|
|
885
|
+
function createMenuContainer(nodeEntry) {
|
|
886
|
+
const contentType = nodeEntry.model.contentType;
|
|
887
|
+
const prompt = nodeEntry.model.prompt || '';
|
|
888
|
+
const isAiEnabled = nodeEntry.model.isAiEnabled !== false;
|
|
889
|
+
const isWordWrap = nodeEntry.model.isWordWrap === true;
|
|
890
|
+
const companyBound = isCompanyBoundNode(nodeEntry.model);
|
|
891
|
+
const companyStatus = getCompanyStatus(nodeEntry.model);
|
|
892
|
+
|
|
893
|
+
const container = document.createElement('div');
|
|
894
|
+
container.className = 'node-menu-container';
|
|
895
895
|
|
|
896
896
|
const addSeparator = () => {
|
|
897
897
|
const sep = document.createElement('div');
|
|
@@ -899,134 +899,134 @@
|
|
|
899
899
|
container.appendChild(sep);
|
|
900
900
|
};
|
|
901
901
|
|
|
902
|
-
const addButton = (className, title, iconClass, isDisabled = false) => {
|
|
903
|
-
const btn = document.createElement('button');
|
|
904
|
-
btn.className = `menu-btn ${className}${isDisabled ? ' disabled' : ''}`.trim();
|
|
905
|
-
btn.title = title;
|
|
906
|
-
btn.disabled = isDisabled === true;
|
|
907
|
-
const icon = document.createElement('i');
|
|
908
|
-
icon.className = iconClass;
|
|
909
|
-
btn.appendChild(icon);
|
|
910
|
-
return btn;
|
|
911
|
-
};
|
|
912
|
-
|
|
913
|
-
if (isRemoteFleetMonitorNode(nodeEntry.model)) {
|
|
914
|
-
container.appendChild(addButton('delete-btn js-btn-delete', 'Delete', 'fa-solid fa-trash'));
|
|
915
|
-
return container;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
const appendNodeRunButton = () => {
|
|
919
|
-
if (!canRunExecutableNode(nodeEntry.model)) {
|
|
920
|
-
return false;
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const isRunning = isExecutableNodeRunning(nodeEntry.model);
|
|
924
|
-
container.appendChild(addButton(
|
|
925
|
-
'js-btn-node-run',
|
|
926
|
-
isRunning ? 'Node is running' : 'Run node',
|
|
927
|
-
isRunning ? 'fa-solid fa-spinner' : 'fa-solid fa-play',
|
|
928
|
-
isRunning));
|
|
929
|
-
return true;
|
|
930
|
-
};
|
|
931
|
-
|
|
932
|
-
const appendCompanyButtons = () => {
|
|
933
|
-
if (!canRunAsCompany(nodeEntry.model)) {
|
|
934
|
-
return false;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
if (companyBound) {
|
|
938
|
-
if (isPendingCompanyBinding(nodeEntry.model)) {
|
|
939
|
-
container.appendChild(addButton('js-btn-company-pending', 'Company is starting', 'fa-solid fa-spinner', true));
|
|
940
|
-
container.appendChild(addButton('js-btn-company-refresh', 'Refresh Company status', 'fa-solid fa-rotate'));
|
|
941
|
-
container.appendChild(addButton('js-btn-company-remove', 'Remove Company from this note', 'fa-solid fa-xmark'));
|
|
942
|
-
return true;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
if (companyStatus !== 'archived') {
|
|
946
|
-
container.appendChild(addButton('js-btn-company-sync-goal', 'Update Company goal from this note', 'fa-solid fa-bullseye'));
|
|
947
|
-
}
|
|
948
|
-
container.appendChild(addButton('js-btn-company-graph', 'Open Company graph', 'fa-solid fa-diagram-project'));
|
|
949
|
-
container.appendChild(addButton('js-btn-company-refresh', 'Refresh Company status', 'fa-solid fa-rotate'));
|
|
950
|
-
|
|
951
|
-
if (companyStatus === 'archived') {
|
|
952
|
-
container.appendChild(addButton('js-btn-company-archived', 'Company is archived', 'fa-solid fa-box-archive', true));
|
|
953
|
-
} else if (companyStatus === 'paused') {
|
|
954
|
-
container.appendChild(addButton('js-btn-company-resume', 'Resume Company', 'fa-solid fa-play'));
|
|
955
|
-
} else {
|
|
956
|
-
container.appendChild(addButton('js-btn-company-pause', 'Pause Company', 'fa-solid fa-pause'));
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
if (companyStatus !== 'archived') {
|
|
960
|
-
container.appendChild(addButton('js-btn-company-archive', 'Archive Company', 'fa-solid fa-box-archive'));
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
container.appendChild(addButton('js-btn-company-remove', 'Remove Company from this note', 'fa-solid fa-xmark'));
|
|
964
|
-
return true;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
return false;
|
|
968
|
-
};
|
|
969
|
-
|
|
970
|
-
// AI toggle (non-image)
|
|
971
|
-
let hasPrimaryActions = false;
|
|
972
|
-
if (contentType !== 'image' && contentType !== 'memo') {
|
|
973
|
-
const aiBtn = addButton(`js-btn-ai ${isAiEnabled ? 'active-ai' : 'inactive-ai'}`, isAiEnabled ? 'Disable AI reference' : 'Enable AI reference', 'fa-solid fa-brain');
|
|
974
|
-
if (!isAiEnabled) {
|
|
975
|
-
const span = document.createElement('span');
|
|
976
|
-
span.className = 'slash-overlay';
|
|
977
|
-
span.textContent = '\\';
|
|
978
|
-
aiBtn.appendChild(span);
|
|
979
|
-
}
|
|
980
|
-
container.appendChild(aiBtn);
|
|
981
|
-
hasPrimaryActions = true;
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
hasPrimaryActions = appendNodeRunButton() || appendCompanyButtons() || hasPrimaryActions;
|
|
985
|
-
|
|
986
|
-
if (hasPrimaryActions) {
|
|
987
|
-
addSeparator();
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// Type-specific buttons
|
|
991
|
-
switch (contentType) {
|
|
992
|
-
case 'text': {
|
|
993
|
-
if (!nodeEntry.model.sourceFilePath) {
|
|
994
|
-
container.appendChild(addButton('js-btn-edit', 'Edit content', 'fa-solid fa-pen-to-square'));
|
|
995
|
-
}
|
|
996
|
-
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
997
|
-
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
998
|
-
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
999
|
-
break;
|
|
1000
|
-
}
|
|
1001
|
-
case 'note': {
|
|
1002
|
-
container.appendChild(addButton('js-btn-edit', 'Edit content', 'fa-solid fa-pen-to-square'));
|
|
1003
|
-
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1004
|
-
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
1005
|
-
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
1006
|
-
break;
|
|
1007
|
-
}
|
|
1008
|
-
case 'memo': {
|
|
1009
|
-
const colorBtn = addButton('js-btn-color', 'Change background color', 'fa-solid fa-palette');
|
|
1010
|
-
colorBtn.appendChild(buildMemoColorPopover(getMemoColorKey(nodeEntry.model)));
|
|
1011
|
-
container.appendChild(colorBtn);
|
|
1012
|
-
container.appendChild(addButton('js-btn-copy', 'Copy memo', 'fa-regular fa-copy'));
|
|
1013
|
-
break;
|
|
1014
|
-
}
|
|
1015
|
-
case 'code': {
|
|
1016
|
-
const wrapBtn = addButton(`js-btn-wordwrap ${isWordWrap ? '' : 'active-nowrap'}`, isWordWrap ? 'Disable word wrap' : 'Enable word wrap', isWordWrap ? 'fa-solid fa-align-left' : 'fa-solid fa-arrows-left-right');
|
|
1017
|
-
container.appendChild(wrapBtn);
|
|
1018
|
-
container.appendChild(addButton('js-btn-edit', 'Edit content', 'fa-solid fa-pen-to-square'));
|
|
1019
|
-
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1020
|
-
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
1021
|
-
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
1022
|
-
break;
|
|
1023
|
-
}
|
|
1024
|
-
case 'markdown': {
|
|
1025
|
-
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1026
|
-
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
1027
|
-
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
1028
|
-
break;
|
|
1029
|
-
}
|
|
902
|
+
const addButton = (className, title, iconClass, isDisabled = false) => {
|
|
903
|
+
const btn = document.createElement('button');
|
|
904
|
+
btn.className = `menu-btn ${className}${isDisabled ? ' disabled' : ''}`.trim();
|
|
905
|
+
btn.title = title;
|
|
906
|
+
btn.disabled = isDisabled === true;
|
|
907
|
+
const icon = document.createElement('i');
|
|
908
|
+
icon.className = iconClass;
|
|
909
|
+
btn.appendChild(icon);
|
|
910
|
+
return btn;
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
if (isRemoteFleetMonitorNode(nodeEntry.model)) {
|
|
914
|
+
container.appendChild(addButton('delete-btn js-btn-delete', 'Delete', 'fa-solid fa-trash'));
|
|
915
|
+
return container;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const appendNodeRunButton = () => {
|
|
919
|
+
if (!canRunExecutableNode(nodeEntry.model)) {
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
const isRunning = isExecutableNodeRunning(nodeEntry.model);
|
|
924
|
+
container.appendChild(addButton(
|
|
925
|
+
'js-btn-node-run',
|
|
926
|
+
isRunning ? 'Node is running' : 'Run node',
|
|
927
|
+
isRunning ? 'fa-solid fa-spinner' : 'fa-solid fa-play',
|
|
928
|
+
isRunning));
|
|
929
|
+
return true;
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
const appendCompanyButtons = () => {
|
|
933
|
+
if (!canRunAsCompany(nodeEntry.model)) {
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (companyBound) {
|
|
938
|
+
if (isPendingCompanyBinding(nodeEntry.model)) {
|
|
939
|
+
container.appendChild(addButton('js-btn-company-pending', 'Company is starting', 'fa-solid fa-spinner', true));
|
|
940
|
+
container.appendChild(addButton('js-btn-company-refresh', 'Refresh Company status', 'fa-solid fa-rotate'));
|
|
941
|
+
container.appendChild(addButton('js-btn-company-remove', 'Remove Company from this note', 'fa-solid fa-xmark'));
|
|
942
|
+
return true;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (companyStatus !== 'archived') {
|
|
946
|
+
container.appendChild(addButton('js-btn-company-sync-goal', 'Update Company goal from this note', 'fa-solid fa-bullseye'));
|
|
947
|
+
}
|
|
948
|
+
container.appendChild(addButton('js-btn-company-graph', 'Open Company graph', 'fa-solid fa-diagram-project'));
|
|
949
|
+
container.appendChild(addButton('js-btn-company-refresh', 'Refresh Company status', 'fa-solid fa-rotate'));
|
|
950
|
+
|
|
951
|
+
if (companyStatus === 'archived') {
|
|
952
|
+
container.appendChild(addButton('js-btn-company-archived', 'Company is archived', 'fa-solid fa-box-archive', true));
|
|
953
|
+
} else if (companyStatus === 'paused') {
|
|
954
|
+
container.appendChild(addButton('js-btn-company-resume', 'Resume Company', 'fa-solid fa-play'));
|
|
955
|
+
} else {
|
|
956
|
+
container.appendChild(addButton('js-btn-company-pause', 'Pause Company', 'fa-solid fa-pause'));
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (companyStatus !== 'archived') {
|
|
960
|
+
container.appendChild(addButton('js-btn-company-archive', 'Archive Company', 'fa-solid fa-box-archive'));
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
container.appendChild(addButton('js-btn-company-remove', 'Remove Company from this note', 'fa-solid fa-xmark'));
|
|
964
|
+
return true;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
return false;
|
|
968
|
+
};
|
|
969
|
+
|
|
970
|
+
// AI toggle (non-image)
|
|
971
|
+
let hasPrimaryActions = false;
|
|
972
|
+
if (contentType !== 'image' && contentType !== 'memo') {
|
|
973
|
+
const aiBtn = addButton(`js-btn-ai ${isAiEnabled ? 'active-ai' : 'inactive-ai'}`, isAiEnabled ? 'Disable AI reference' : 'Enable AI reference', 'fa-solid fa-brain');
|
|
974
|
+
if (!isAiEnabled) {
|
|
975
|
+
const span = document.createElement('span');
|
|
976
|
+
span.className = 'slash-overlay';
|
|
977
|
+
span.textContent = '\\';
|
|
978
|
+
aiBtn.appendChild(span);
|
|
979
|
+
}
|
|
980
|
+
container.appendChild(aiBtn);
|
|
981
|
+
hasPrimaryActions = true;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
hasPrimaryActions = appendNodeRunButton() || appendCompanyButtons() || hasPrimaryActions;
|
|
985
|
+
|
|
986
|
+
if (hasPrimaryActions) {
|
|
987
|
+
addSeparator();
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Type-specific buttons
|
|
991
|
+
switch (contentType) {
|
|
992
|
+
case 'text': {
|
|
993
|
+
if (!nodeEntry.model.sourceFilePath) {
|
|
994
|
+
container.appendChild(addButton('js-btn-edit', 'Edit content', 'fa-solid fa-pen-to-square'));
|
|
995
|
+
}
|
|
996
|
+
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
997
|
+
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
998
|
+
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
1001
|
+
case 'note': {
|
|
1002
|
+
container.appendChild(addButton('js-btn-edit', 'Edit content', 'fa-solid fa-pen-to-square'));
|
|
1003
|
+
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1004
|
+
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
1005
|
+
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
case 'memo': {
|
|
1009
|
+
const colorBtn = addButton('js-btn-color', 'Change background color', 'fa-solid fa-palette');
|
|
1010
|
+
colorBtn.appendChild(buildMemoColorPopover(getMemoColorKey(nodeEntry.model)));
|
|
1011
|
+
container.appendChild(colorBtn);
|
|
1012
|
+
container.appendChild(addButton('js-btn-copy', 'Copy memo', 'fa-regular fa-copy'));
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
case 'code': {
|
|
1016
|
+
const wrapBtn = addButton(`js-btn-wordwrap ${isWordWrap ? '' : 'active-nowrap'}`, isWordWrap ? 'Disable word wrap' : 'Enable word wrap', isWordWrap ? 'fa-solid fa-align-left' : 'fa-solid fa-arrows-left-right');
|
|
1017
|
+
container.appendChild(wrapBtn);
|
|
1018
|
+
container.appendChild(addButton('js-btn-edit', 'Edit content', 'fa-solid fa-pen-to-square'));
|
|
1019
|
+
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1020
|
+
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
1021
|
+
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
case 'markdown': {
|
|
1025
|
+
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1026
|
+
container.appendChild(addButton('js-btn-fit-text-height', 'Fit text height', 'fa-solid fa-arrows-up-down'));
|
|
1027
|
+
container.appendChild(addButton('disabled', 'Share (coming soon)', 'fa-solid fa-share-nodes', true));
|
|
1028
|
+
break;
|
|
1029
|
+
}
|
|
1030
1030
|
case 'file': {
|
|
1031
1031
|
container.appendChild(addButton('js-btn-copy', 'Copy content', 'fa-regular fa-copy'));
|
|
1032
1032
|
break;
|
|
@@ -1034,20 +1034,20 @@
|
|
|
1034
1034
|
case 'directory': {
|
|
1035
1035
|
container.appendChild(addButton('js-btn-settings', 'Folder settings', 'fa-solid fa-gear'));
|
|
1036
1036
|
break;
|
|
1037
|
-
}
|
|
1038
|
-
case 'image': {
|
|
1039
|
-
container.appendChild(addButton('js-btn-copy-image', 'Copy image to clipboard', 'fa-regular fa-copy'));
|
|
1040
|
-
break;
|
|
1041
|
-
}
|
|
1042
|
-
case 'video': {
|
|
1043
|
-
container.appendChild(addButton('js-btn-video-play', 'Play video', 'fa-solid fa-play'));
|
|
1044
|
-
container.appendChild(addButton('js-btn-video-pause', 'Pause video', 'fa-solid fa-pause'));
|
|
1045
|
-
break;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
addSeparator();
|
|
1050
|
-
container.appendChild(addButton('delete-btn js-btn-delete', 'Delete', 'fa-solid fa-trash'));
|
|
1037
|
+
}
|
|
1038
|
+
case 'image': {
|
|
1039
|
+
container.appendChild(addButton('js-btn-copy-image', 'Copy image to clipboard', 'fa-regular fa-copy'));
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
1042
|
+
case 'video': {
|
|
1043
|
+
container.appendChild(addButton('js-btn-video-play', 'Play video', 'fa-solid fa-play'));
|
|
1044
|
+
container.appendChild(addButton('js-btn-video-pause', 'Pause video', 'fa-solid fa-pause'));
|
|
1045
|
+
break;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
addSeparator();
|
|
1050
|
+
container.appendChild(addButton('delete-btn js-btn-delete', 'Delete', 'fa-solid fa-trash'));
|
|
1051
1051
|
|
|
1052
1052
|
return container;
|
|
1053
1053
|
}
|
|
@@ -1061,91 +1061,91 @@
|
|
|
1061
1061
|
}
|
|
1062
1062
|
|
|
1063
1063
|
// ▼▼▼ [New] Screen-space position using worldToScreen ▼▼▼
|
|
1064
|
-
function worldToScreen(worldX, worldY, camera, container) {
|
|
1065
|
-
const THREE = globalThis.THREE;
|
|
1066
|
-
const vector = new THREE.Vector3(worldX, worldY, 0);
|
|
1067
|
-
vector.project(camera);
|
|
1068
|
-
|
|
1069
|
-
const viewportWidth = Number(_module?._lastViewportWidth || _module?._lastContainerWidth || container.clientWidth || 1);
|
|
1070
|
-
const viewportHeight = Number(_module?._lastViewportHeight || _module?._lastContainerHeight || container.clientHeight || 1);
|
|
1071
|
-
const viewportLeft = Number(_module?._lastViewportLeft || 0);
|
|
1072
|
-
const viewportTop = Number(_module?._lastViewportTop || 0);
|
|
1073
|
-
const halfWidth = viewportWidth / 2;
|
|
1074
|
-
const halfHeight = viewportHeight / 2;
|
|
1075
|
-
|
|
1076
|
-
return {
|
|
1077
|
-
x: viewportLeft + (vector.x * halfWidth) + halfWidth,
|
|
1078
|
-
y: viewportTop - (vector.y * halfHeight) + halfHeight
|
|
1079
|
-
};
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
function updateMenuScreenPosition(menuEl, nodeEntry, cameraMotionOnly = false) {
|
|
1083
|
-
if (!menuEl || !nodeEntry || !_module || !_module.camera || !_module.container) return;
|
|
1084
|
-
|
|
1085
|
-
const templateNode = document.getElementById(`node-${nodeEntry.model?.id || currentMenuNodeId}`);
|
|
1086
|
-
let anchorX = null;
|
|
1087
|
-
let anchorY = null;
|
|
1088
|
-
const shouldPreferProjectedAnchor = cameraMotionOnly === true ||
|
|
1089
|
-
_module.isZooming === true ||
|
|
1090
|
-
_module.isPanning === true ||
|
|
1091
|
-
_module.isDraggingNode === true ||
|
|
1092
|
-
_module.isDraggingMultipleNodes === true ||
|
|
1093
|
-
_module.isResizing === true;
|
|
1094
|
-
|
|
1095
|
-
if (templateNode && !shouldPreferProjectedAnchor) {
|
|
1096
|
-
const rect = templateNode.getBoundingClientRect();
|
|
1097
|
-
if (Number.isFinite(rect.left) && Number.isFinite(rect.top) && rect.width > 0 && rect.height > 0) {
|
|
1098
|
-
anchorX = rect.left + rect.width / 2;
|
|
1099
|
-
anchorY = rect.top;
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
if (!Number.isFinite(anchorX) || !Number.isFinite(anchorY)) {
|
|
1104
|
-
const model = nodeEntry.model || {};
|
|
1105
|
-
const glObject = nodeEntry.glObject || null;
|
|
1106
|
-
|
|
1107
|
-
// Use live world position during drag to keep menu attached to moving node.
|
|
1108
|
-
const posX = glObject ? glObject.position.x : (model.positionX || 0);
|
|
1109
|
-
const posY = glObject ? glObject.position.y : (model.positionY || 0);
|
|
1110
|
-
|
|
1111
|
-
// Prefer runtime width when available (resize/drag in-progress), fallback to model width.
|
|
1112
|
-
const liveWidth = glObject?.userData?.width;
|
|
1113
|
-
const width = Number.isFinite(liveWidth) ? liveWidth : (model.width || 400);
|
|
1114
|
-
|
|
1115
|
-
// Node top-center in world coordinates
|
|
1116
|
-
const centerX = posX + width / 2;
|
|
1117
|
-
const topY = posY;
|
|
1118
|
-
const screenPos = worldToScreen(centerX, topY, _module.camera, _module.container);
|
|
1119
|
-
anchorX = screenPos.x;
|
|
1120
|
-
anchorY = screenPos.y;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
let halfMenuWidth = Number(menuEl._mindMapHalfWidth || 0);
|
|
1124
|
-
let menuHeight = Number(menuEl._mindMapHeight || 0);
|
|
1125
|
-
if (cameraMotionOnly !== true || halfMenuWidth <= 0 || menuHeight <= 0) {
|
|
1126
|
-
const menuRect = menuEl.getBoundingClientRect();
|
|
1127
|
-
halfMenuWidth = menuRect.width > 0 ? menuRect.width / 2 : 0;
|
|
1128
|
-
menuHeight = menuRect.height || 0;
|
|
1129
|
-
menuEl._mindMapHalfWidth = halfMenuWidth;
|
|
1130
|
-
menuEl._mindMapHeight = menuHeight;
|
|
1131
|
-
}
|
|
1132
|
-
const viewportMargin = 10;
|
|
1133
|
-
const verticalGap = 14;
|
|
1134
|
-
|
|
1135
|
-
const clampedLeft = Math.min(
|
|
1136
|
-
window.innerWidth - viewportMargin - halfMenuWidth,
|
|
1137
|
-
Math.max(viewportMargin + halfMenuWidth, anchorX)
|
|
1138
|
-
);
|
|
1139
|
-
const anchoredTop = anchorY - verticalGap;
|
|
1140
|
-
const minTop = viewportMargin + menuHeight;
|
|
1141
|
-
const clampedTop = Math.max(minTop, anchoredTop);
|
|
1142
|
-
|
|
1143
|
-
menuEl.style.left = `${clampedLeft}px`;
|
|
1144
|
-
menuEl.style.top = `${clampedTop}px`;
|
|
1145
|
-
}
|
|
1064
|
+
function worldToScreen(worldX, worldY, camera, container) {
|
|
1065
|
+
const THREE = globalThis.THREE;
|
|
1066
|
+
const vector = new THREE.Vector3(worldX, worldY, 0);
|
|
1067
|
+
vector.project(camera);
|
|
1068
|
+
|
|
1069
|
+
const viewportWidth = Number(_module?._lastViewportWidth || _module?._lastContainerWidth || container.clientWidth || 1);
|
|
1070
|
+
const viewportHeight = Number(_module?._lastViewportHeight || _module?._lastContainerHeight || container.clientHeight || 1);
|
|
1071
|
+
const viewportLeft = Number(_module?._lastViewportLeft || 0);
|
|
1072
|
+
const viewportTop = Number(_module?._lastViewportTop || 0);
|
|
1073
|
+
const halfWidth = viewportWidth / 2;
|
|
1074
|
+
const halfHeight = viewportHeight / 2;
|
|
1075
|
+
|
|
1076
|
+
return {
|
|
1077
|
+
x: viewportLeft + (vector.x * halfWidth) + halfWidth,
|
|
1078
|
+
y: viewportTop - (vector.y * halfHeight) + halfHeight
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function updateMenuScreenPosition(menuEl, nodeEntry, cameraMotionOnly = false) {
|
|
1083
|
+
if (!menuEl || !nodeEntry || !_module || !_module.camera || !_module.container) return;
|
|
1084
|
+
|
|
1085
|
+
const templateNode = document.getElementById(`node-${nodeEntry.model?.id || currentMenuNodeId}`);
|
|
1086
|
+
let anchorX = null;
|
|
1087
|
+
let anchorY = null;
|
|
1088
|
+
const shouldPreferProjectedAnchor = cameraMotionOnly === true ||
|
|
1089
|
+
_module.isZooming === true ||
|
|
1090
|
+
_module.isPanning === true ||
|
|
1091
|
+
_module.isDraggingNode === true ||
|
|
1092
|
+
_module.isDraggingMultipleNodes === true ||
|
|
1093
|
+
_module.isResizing === true;
|
|
1094
|
+
|
|
1095
|
+
if (templateNode && !shouldPreferProjectedAnchor) {
|
|
1096
|
+
const rect = templateNode.getBoundingClientRect();
|
|
1097
|
+
if (Number.isFinite(rect.left) && Number.isFinite(rect.top) && rect.width > 0 && rect.height > 0) {
|
|
1098
|
+
anchorX = rect.left + rect.width / 2;
|
|
1099
|
+
anchorY = rect.top;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
if (!Number.isFinite(anchorX) || !Number.isFinite(anchorY)) {
|
|
1104
|
+
const model = nodeEntry.model || {};
|
|
1105
|
+
const glObject = nodeEntry.glObject || null;
|
|
1106
|
+
|
|
1107
|
+
// Use live world position during drag to keep menu attached to moving node.
|
|
1108
|
+
const posX = glObject ? glObject.position.x : (model.positionX || 0);
|
|
1109
|
+
const posY = glObject ? glObject.position.y : (model.positionY || 0);
|
|
1110
|
+
|
|
1111
|
+
// Prefer runtime width when available (resize/drag in-progress), fallback to model width.
|
|
1112
|
+
const liveWidth = glObject?.userData?.width;
|
|
1113
|
+
const width = Number.isFinite(liveWidth) ? liveWidth : (model.width || 400);
|
|
1114
|
+
|
|
1115
|
+
// Node top-center in world coordinates
|
|
1116
|
+
const centerX = posX + width / 2;
|
|
1117
|
+
const topY = posY;
|
|
1118
|
+
const screenPos = worldToScreen(centerX, topY, _module.camera, _module.container);
|
|
1119
|
+
anchorX = screenPos.x;
|
|
1120
|
+
anchorY = screenPos.y;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
let halfMenuWidth = Number(menuEl._mindMapHalfWidth || 0);
|
|
1124
|
+
let menuHeight = Number(menuEl._mindMapHeight || 0);
|
|
1125
|
+
if (cameraMotionOnly !== true || halfMenuWidth <= 0 || menuHeight <= 0) {
|
|
1126
|
+
const menuRect = menuEl.getBoundingClientRect();
|
|
1127
|
+
halfMenuWidth = menuRect.width > 0 ? menuRect.width / 2 : 0;
|
|
1128
|
+
menuHeight = menuRect.height || 0;
|
|
1129
|
+
menuEl._mindMapHalfWidth = halfMenuWidth;
|
|
1130
|
+
menuEl._mindMapHeight = menuHeight;
|
|
1131
|
+
}
|
|
1132
|
+
const viewportMargin = 10;
|
|
1133
|
+
const verticalGap = 14;
|
|
1134
|
+
|
|
1135
|
+
const clampedLeft = Math.min(
|
|
1136
|
+
window.innerWidth - viewportMargin - halfMenuWidth,
|
|
1137
|
+
Math.max(viewportMargin + halfMenuWidth, anchorX)
|
|
1138
|
+
);
|
|
1139
|
+
const anchoredTop = anchorY - verticalGap;
|
|
1140
|
+
const minTop = viewportMargin + menuHeight;
|
|
1141
|
+
const clampedTop = Math.max(minTop, anchoredTop);
|
|
1142
|
+
|
|
1143
|
+
menuEl.style.left = `${clampedLeft}px`;
|
|
1144
|
+
menuEl.style.top = `${clampedTop}px`;
|
|
1145
|
+
}
|
|
1146
1146
|
// ▲▲▲ [New] ▲▲▲
|
|
1147
1147
|
|
|
1148
|
-
function update() {
|
|
1148
|
+
function update() {
|
|
1149
1149
|
if (currentMenuElement && currentMenuNodeId && _module) {
|
|
1150
1150
|
const nodeEntry = _module.nodeObjectsById.get(currentMenuNodeId);
|
|
1151
1151
|
if (nodeEntry) {
|
|
@@ -1156,37 +1156,37 @@
|
|
|
1156
1156
|
}
|
|
1157
1157
|
}
|
|
1158
1158
|
|
|
1159
|
-
function bindMenuEvents(element, nodeId, nodeEntry) {
|
|
1160
|
-
const invokeMenuAction = async (button, methodName, ...args) => {
|
|
1161
|
-
if (!button || !_module?.dotNetHelper || button.disabled) {
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
button.disabled = true;
|
|
1166
|
-
hideMenu();
|
|
1167
|
-
|
|
1168
|
-
try {
|
|
1169
|
-
await _module.dotNetHelper.invokeMethodAsync(methodName, ...args);
|
|
1170
|
-
} catch (error) {
|
|
1171
|
-
console.error(`[MindMapMenuManager] ${methodName} failed for node ${nodeId}`, error);
|
|
1172
|
-
} finally {
|
|
1173
|
-
button.disabled = false;
|
|
1174
|
-
}
|
|
1175
|
-
};
|
|
1176
|
-
|
|
1177
|
-
element.addEventListener('mousedown', (e) => {
|
|
1178
|
-
if (!e.target.closest('.js-btn-color')) {
|
|
1179
|
-
element.querySelectorAll('.menu-color-popover.is-open').forEach(popover => {
|
|
1180
|
-
popover.classList.remove('is-open');
|
|
1181
|
-
});
|
|
1182
|
-
}
|
|
1183
|
-
});
|
|
1184
|
-
|
|
1185
|
-
// AI Toggle
|
|
1186
|
-
const aiBtn = element.querySelector('.js-btn-ai');
|
|
1187
|
-
if (aiBtn) {
|
|
1188
|
-
aiBtn.addEventListener('click', (e) => {
|
|
1189
|
-
e.stopPropagation();
|
|
1159
|
+
function bindMenuEvents(element, nodeId, nodeEntry) {
|
|
1160
|
+
const invokeMenuAction = async (button, methodName, ...args) => {
|
|
1161
|
+
if (!button || !_module?.dotNetHelper || button.disabled) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
button.disabled = true;
|
|
1166
|
+
hideMenu();
|
|
1167
|
+
|
|
1168
|
+
try {
|
|
1169
|
+
await _module.dotNetHelper.invokeMethodAsync(methodName, ...args);
|
|
1170
|
+
} catch (error) {
|
|
1171
|
+
console.error(`[MindMapMenuManager] ${methodName} failed for node ${nodeId}`, error);
|
|
1172
|
+
} finally {
|
|
1173
|
+
button.disabled = false;
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
element.addEventListener('mousedown', (e) => {
|
|
1178
|
+
if (!e.target.closest('.js-btn-color')) {
|
|
1179
|
+
element.querySelectorAll('.menu-color-popover.is-open').forEach(popover => {
|
|
1180
|
+
popover.classList.remove('is-open');
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
// AI Toggle
|
|
1186
|
+
const aiBtn = element.querySelector('.js-btn-ai');
|
|
1187
|
+
if (aiBtn) {
|
|
1188
|
+
aiBtn.addEventListener('click', (e) => {
|
|
1189
|
+
e.stopPropagation();
|
|
1190
1190
|
nodeEntry.model.isAiEnabled = !nodeEntry.model.isAiEnabled;
|
|
1191
1191
|
if (_module.dotNetHelper) _module.dotNetHelper.invokeMethodAsync('ToggleNodeAi', nodeId);
|
|
1192
1192
|
|
|
@@ -1228,114 +1228,114 @@
|
|
|
1228
1228
|
}
|
|
1229
1229
|
|
|
1230
1230
|
// Copy (text)
|
|
1231
|
-
const copyBtn = element.querySelector('.js-btn-copy');
|
|
1232
|
-
if (copyBtn) {
|
|
1233
|
-
copyBtn.addEventListener('click', (e) => {
|
|
1234
|
-
e.stopPropagation();
|
|
1235
|
-
const isMemo = nodeEntry.model.contentType === 'memo';
|
|
1236
|
-
const metadataText = isMemo
|
|
1237
|
-
? [nodeEntry.model.prompt || '', nodeEntry.model.response || ''].filter(Boolean).join('\n\n')
|
|
1238
|
-
: (nodeEntry.model.response || '');
|
|
1239
|
-
const text = metadataText;
|
|
1240
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
1241
|
-
showActionSuccessIcon(copyBtn);
|
|
1242
|
-
});
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
const fitTextHeightBtn = element.querySelector('.js-btn-fit-text-height');
|
|
1247
|
-
if (fitTextHeightBtn && isTextFitNode(nodeEntry.model)) {
|
|
1248
|
-
fitTextHeightBtn.addEventListener('click', async (e) => {
|
|
1249
|
-
e.stopPropagation();
|
|
1250
|
-
fitTextHeightBtn.disabled = true;
|
|
1251
|
-
try {
|
|
1252
|
-
await window.MindMapMultiSelect?.fitTextNodesToContentAndReflowY?.([nodeId]);
|
|
1253
|
-
const icon = fitTextHeightBtn.querySelector('i');
|
|
1254
|
-
if (icon) {
|
|
1255
|
-
const oldClass = icon.className;
|
|
1256
|
-
icon.className = 'fa-solid fa-check text-green-500';
|
|
1257
|
-
setTimeout(() => icon.className = oldClass, 1000);
|
|
1258
|
-
}
|
|
1259
|
-
requestAnimationFrame(() => update());
|
|
1260
|
-
} finally {
|
|
1261
|
-
fitTextHeightBtn.disabled = false;
|
|
1262
|
-
}
|
|
1263
|
-
});
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
const colorBtn = element.querySelector('.js-btn-color');
|
|
1267
|
-
const colorPopover = colorBtn?.querySelector('.menu-color-popover');
|
|
1268
|
-
if (colorBtn && colorPopover) {
|
|
1269
|
-
syncMemoColorPopover(colorPopover, getMemoColorKey(nodeEntry.model));
|
|
1270
|
-
|
|
1271
|
-
colorBtn.addEventListener('click', (e) => {
|
|
1272
|
-
e.stopPropagation();
|
|
1273
|
-
colorPopover.classList.toggle('is-open');
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
colorPopover.querySelectorAll('.menu-color-swatch').forEach(button => {
|
|
1277
|
-
button.addEventListener('click', (e) => {
|
|
1278
|
-
e.stopPropagation();
|
|
1279
|
-
const colorKey = button.dataset.colorKey || 'coral';
|
|
1280
|
-
const metadata = ensureMetadata(nodeEntry.model);
|
|
1281
|
-
metadata.memoColor = colorKey;
|
|
1282
|
-
metadata.memoColorIsCustom = 'true';
|
|
1283
|
-
nodeEntry.model.metadata = metadata;
|
|
1284
|
-
nodeEntry.model.Metadata = metadata;
|
|
1285
|
-
syncMemoColorPopover(colorPopover, colorKey);
|
|
1286
|
-
colorPopover.classList.remove('is-open');
|
|
1287
|
-
refreshMemoNode(nodeEntry);
|
|
1288
|
-
if (_module.dotNetHelper) {
|
|
1289
|
-
_module.dotNetHelper.invokeMethodAsync('UpdateNodeMetadata', nodeId, 'memoColor', colorKey);
|
|
1290
|
-
}
|
|
1291
|
-
});
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1231
|
+
const copyBtn = element.querySelector('.js-btn-copy');
|
|
1232
|
+
if (copyBtn) {
|
|
1233
|
+
copyBtn.addEventListener('click', (e) => {
|
|
1234
|
+
e.stopPropagation();
|
|
1235
|
+
const isMemo = nodeEntry.model.contentType === 'memo';
|
|
1236
|
+
const metadataText = isMemo
|
|
1237
|
+
? [nodeEntry.model.prompt || '', nodeEntry.model.response || ''].filter(Boolean).join('\n\n')
|
|
1238
|
+
: (nodeEntry.model.response || '');
|
|
1239
|
+
const text = metadataText;
|
|
1240
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
1241
|
+
showActionSuccessIcon(copyBtn);
|
|
1242
|
+
});
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const fitTextHeightBtn = element.querySelector('.js-btn-fit-text-height');
|
|
1247
|
+
if (fitTextHeightBtn && isTextFitNode(nodeEntry.model)) {
|
|
1248
|
+
fitTextHeightBtn.addEventListener('click', async (e) => {
|
|
1249
|
+
e.stopPropagation();
|
|
1250
|
+
fitTextHeightBtn.disabled = true;
|
|
1251
|
+
try {
|
|
1252
|
+
await window.MindMapMultiSelect?.fitTextNodesToContentAndReflowY?.([nodeId]);
|
|
1253
|
+
const icon = fitTextHeightBtn.querySelector('i');
|
|
1254
|
+
if (icon) {
|
|
1255
|
+
const oldClass = icon.className;
|
|
1256
|
+
icon.className = 'fa-solid fa-check text-green-500';
|
|
1257
|
+
setTimeout(() => icon.className = oldClass, 1000);
|
|
1258
|
+
}
|
|
1259
|
+
requestAnimationFrame(() => update());
|
|
1260
|
+
} finally {
|
|
1261
|
+
fitTextHeightBtn.disabled = false;
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const colorBtn = element.querySelector('.js-btn-color');
|
|
1267
|
+
const colorPopover = colorBtn?.querySelector('.menu-color-popover');
|
|
1268
|
+
if (colorBtn && colorPopover) {
|
|
1269
|
+
syncMemoColorPopover(colorPopover, getMemoColorKey(nodeEntry.model));
|
|
1270
|
+
|
|
1271
|
+
colorBtn.addEventListener('click', (e) => {
|
|
1272
|
+
e.stopPropagation();
|
|
1273
|
+
colorPopover.classList.toggle('is-open');
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
colorPopover.querySelectorAll('.menu-color-swatch').forEach(button => {
|
|
1277
|
+
button.addEventListener('click', (e) => {
|
|
1278
|
+
e.stopPropagation();
|
|
1279
|
+
const colorKey = button.dataset.colorKey || 'coral';
|
|
1280
|
+
const metadata = ensureMetadata(nodeEntry.model);
|
|
1281
|
+
metadata.memoColor = colorKey;
|
|
1282
|
+
metadata.memoColorIsCustom = 'true';
|
|
1283
|
+
nodeEntry.model.metadata = metadata;
|
|
1284
|
+
nodeEntry.model.Metadata = metadata;
|
|
1285
|
+
syncMemoColorPopover(colorPopover, colorKey);
|
|
1286
|
+
colorPopover.classList.remove('is-open');
|
|
1287
|
+
refreshMemoNode(nodeEntry);
|
|
1288
|
+
if (_module.dotNetHelper) {
|
|
1289
|
+
_module.dotNetHelper.invokeMethodAsync('UpdateNodeMetadata', nodeId, 'memoColor', colorKey);
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
1294
|
|
|
1295
1295
|
// Copy image
|
|
1296
|
-
const copyImgBtn = element.querySelector('.js-btn-copy-image');
|
|
1297
|
-
if (copyImgBtn) {
|
|
1298
|
-
copyImgBtn.addEventListener('click', async (e) => {
|
|
1299
|
-
e.stopPropagation();
|
|
1300
|
-
const candidateSources = getImageClipboardSourceCandidates(nodeEntry.model);
|
|
1301
|
-
if (candidateSources.length === 0) {
|
|
1302
|
-
showToast('No image to copy.', { kind: 'error' });
|
|
1303
|
-
return;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
try {
|
|
1307
|
-
const { blob, src } = await fetchClipboardImageBlob(nodeEntry.model);
|
|
1308
|
-
const pngBlob = await convertBlobToPngForClipboard(blob);
|
|
1309
|
-
const copiedKind = await writeImageBlobToClipboard(pngBlob, src);
|
|
1310
|
-
|
|
1311
|
-
if (copiedKind !== 'image') {
|
|
1312
|
-
showToast('Image clipboard is unavailable in this browser. Copied the image URL instead.');
|
|
1313
|
-
return;
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
console.log(`[MindMapMenuManager] Image copied to clipboard as PNG (${pngBlob.size} bytes) from ${src}`);
|
|
1317
|
-
showActionSuccessIcon(copyImgBtn);
|
|
1318
|
-
} catch (err) {
|
|
1319
|
-
console.error('Image copy failed:', err);
|
|
1320
|
-
showToast('Failed to copy image to the clipboard.', { kind: 'error' });
|
|
1321
|
-
}
|
|
1322
|
-
});
|
|
1323
|
-
}
|
|
1296
|
+
const copyImgBtn = element.querySelector('.js-btn-copy-image');
|
|
1297
|
+
if (copyImgBtn) {
|
|
1298
|
+
copyImgBtn.addEventListener('click', async (e) => {
|
|
1299
|
+
e.stopPropagation();
|
|
1300
|
+
const candidateSources = getImageClipboardSourceCandidates(nodeEntry.model);
|
|
1301
|
+
if (candidateSources.length === 0) {
|
|
1302
|
+
showToast('No image to copy.', { kind: 'error' });
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
try {
|
|
1307
|
+
const { blob, src } = await fetchClipboardImageBlob(nodeEntry.model);
|
|
1308
|
+
const pngBlob = await convertBlobToPngForClipboard(blob);
|
|
1309
|
+
const copiedKind = await writeImageBlobToClipboard(pngBlob, src);
|
|
1310
|
+
|
|
1311
|
+
if (copiedKind !== 'image') {
|
|
1312
|
+
showToast('Image clipboard is unavailable in this browser. Copied the image URL instead.');
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
console.log(`[MindMapMenuManager] Image copied to clipboard as PNG (${pngBlob.size} bytes) from ${src}`);
|
|
1317
|
+
showActionSuccessIcon(copyImgBtn);
|
|
1318
|
+
} catch (err) {
|
|
1319
|
+
console.error('Image copy failed:', err);
|
|
1320
|
+
showToast('Failed to copy image to the clipboard.', { kind: 'error' });
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
1324
|
|
|
1325
1325
|
// Delete
|
|
1326
1326
|
const delBtn = element.querySelector('.js-btn-delete');
|
|
1327
|
-
if (delBtn) {
|
|
1328
|
-
delBtn.addEventListener('click', (e) => {
|
|
1329
|
-
e.stopPropagation();
|
|
1330
|
-
const snapshot =
|
|
1331
|
-
window.MindMapNodes?.captureDeleteSnapshot?.(_module, nodeId) ||
|
|
1332
|
-
{ nodeId: String(nodeId), currentContent: nodeEntry.model.response || '' };
|
|
1333
|
-
hideMenu();
|
|
1334
|
-
if (_module.dotNetHelper) {
|
|
1335
|
-
_module.dotNetHelper.invokeMethodAsync('DeleteSingleNodeSnapshotFromJs', snapshot);
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1327
|
+
if (delBtn) {
|
|
1328
|
+
delBtn.addEventListener('click', (e) => {
|
|
1329
|
+
e.stopPropagation();
|
|
1330
|
+
const snapshot =
|
|
1331
|
+
window.MindMapNodes?.captureDeleteSnapshot?.(_module, nodeId) ||
|
|
1332
|
+
{ nodeId: String(nodeId), currentContent: nodeEntry.model.response || '' };
|
|
1333
|
+
hideMenu();
|
|
1334
|
+
if (_module.dotNetHelper) {
|
|
1335
|
+
_module.dotNetHelper.invokeMethodAsync('DeleteSingleNodeSnapshotFromJs', snapshot);
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
1339
|
|
|
1340
1340
|
// Settings
|
|
1341
1341
|
const settingBtn = element.querySelector('.js-btn-settings');
|
|
@@ -1347,10 +1347,10 @@
|
|
|
1347
1347
|
}
|
|
1348
1348
|
|
|
1349
1349
|
// Duplicate button (for single image)
|
|
1350
|
-
const duplicateBtn = element.querySelector('.js-btn-duplicate');
|
|
1351
|
-
if (duplicateBtn) {
|
|
1352
|
-
duplicateBtn.addEventListener('click', (e) => {
|
|
1353
|
-
e.stopPropagation();
|
|
1350
|
+
const duplicateBtn = element.querySelector('.js-btn-duplicate');
|
|
1351
|
+
if (duplicateBtn) {
|
|
1352
|
+
duplicateBtn.addEventListener('click', (e) => {
|
|
1353
|
+
e.stopPropagation();
|
|
1354
1354
|
if (_module.dotNetHelper) {
|
|
1355
1355
|
_module.dotNetHelper.invokeMethodAsync('DuplicateNodeFromJs', nodeId);
|
|
1356
1356
|
}
|
|
@@ -1360,30 +1360,30 @@
|
|
|
1360
1360
|
i.className = 'fa-solid fa-check text-green-500';
|
|
1361
1361
|
setTimeout(() => i.className = oldClass, 1000);
|
|
1362
1362
|
}
|
|
1363
|
-
});
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
const videoPlayBtn = element.querySelector('.js-btn-video-play');
|
|
1367
|
-
if (videoPlayBtn) {
|
|
1368
|
-
videoPlayBtn.addEventListener('click', (e) => {
|
|
1369
|
-
e.stopPropagation();
|
|
1370
|
-
window.MindMapCss3DManager?.setVideoNodePlayback?.(nodeId, true);
|
|
1371
|
-
});
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
const videoPauseBtn = element.querySelector('.js-btn-video-pause');
|
|
1375
|
-
if (videoPauseBtn) {
|
|
1376
|
-
videoPauseBtn.addEventListener('click', (e) => {
|
|
1377
|
-
e.stopPropagation();
|
|
1378
|
-
window.MindMapCss3DManager?.setVideoNodePlayback?.(nodeId, false);
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// ▼▼▼ [NEW] Word wrap toggle for code nodes ▼▼▼
|
|
1383
|
-
const wrapBtn = element.querySelector('.js-btn-wordwrap');
|
|
1384
|
-
if (wrapBtn) {
|
|
1385
|
-
wrapBtn.addEventListener('click', (e) => {
|
|
1386
|
-
e.stopPropagation();
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const videoPlayBtn = element.querySelector('.js-btn-video-play');
|
|
1367
|
+
if (videoPlayBtn) {
|
|
1368
|
+
videoPlayBtn.addEventListener('click', (e) => {
|
|
1369
|
+
e.stopPropagation();
|
|
1370
|
+
window.MindMapCss3DManager?.setVideoNodePlayback?.(nodeId, true);
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
const videoPauseBtn = element.querySelector('.js-btn-video-pause');
|
|
1375
|
+
if (videoPauseBtn) {
|
|
1376
|
+
videoPauseBtn.addEventListener('click', (e) => {
|
|
1377
|
+
e.stopPropagation();
|
|
1378
|
+
window.MindMapCss3DManager?.setVideoNodePlayback?.(nodeId, false);
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// ▼▼▼ [NEW] Word wrap toggle for code nodes ▼▼▼
|
|
1383
|
+
const wrapBtn = element.querySelector('.js-btn-wordwrap');
|
|
1384
|
+
if (wrapBtn) {
|
|
1385
|
+
wrapBtn.addEventListener('click', (e) => {
|
|
1386
|
+
e.stopPropagation();
|
|
1387
1387
|
const newWrapState = !nodeEntry.model.isWordWrap;
|
|
1388
1388
|
nodeEntry.model.isWordWrap = newWrapState;
|
|
1389
1389
|
console.log(`[MindMapMenuManager] Word wrap toggled for ${nodeId}: ${newWrapState}`);
|
|
@@ -1404,169 +1404,169 @@
|
|
|
1404
1404
|
if (window.MindMapNodes && window.MindMapNodes.updateNodeWordWrap) {
|
|
1405
1405
|
window.MindMapNodes.updateNodeWordWrap(_module, nodeId, newWrapState);
|
|
1406
1406
|
}
|
|
1407
|
-
});
|
|
1408
|
-
}
|
|
1409
|
-
// ▲▲▲ [NEW] ▲▲▲
|
|
1410
|
-
|
|
1411
|
-
const nodeRunBtn = element.querySelector('.js-btn-node-run');
|
|
1412
|
-
if (nodeRunBtn) {
|
|
1413
|
-
nodeRunBtn.addEventListener('click', async (e) => {
|
|
1414
|
-
e.stopPropagation();
|
|
1415
|
-
if (!_module?.dotNetHelper || nodeRunBtn.disabled) {
|
|
1416
|
-
return;
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
nodeRunBtn.disabled = true;
|
|
1420
|
-
nodeRunBtn.classList.add('disabled');
|
|
1421
|
-
setExecutableNodeRunVisualState(nodeId, 'running', 'Running');
|
|
1422
|
-
hideMenu();
|
|
1423
|
-
|
|
1424
|
-
try {
|
|
1425
|
-
const result = await _module.dotNetHelper.invokeMethodAsync('RunExecutableNodeFromJs', nodeId);
|
|
1426
|
-
await syncNodeRunResultStates(result);
|
|
1427
|
-
if (result?.success === false && !Array.isArray(result?.nodeStates)) {
|
|
1428
|
-
setExecutableNodeRunVisualState(nodeId, 'idle', 'Failed');
|
|
1429
|
-
}
|
|
1430
|
-
if (result?.success === false) {
|
|
1431
|
-
window.MindMapFeedback?.toast?.(result.message || 'Node run failed.', { kind: 'error' });
|
|
1432
|
-
}
|
|
1433
|
-
} catch (error) {
|
|
1434
|
-
console.error(`[MindMapMenuManager] RunExecutableNodeFromJs failed for node ${nodeId}`, error);
|
|
1435
|
-
setExecutableNodeRunVisualState(nodeId, 'idle', 'Failed');
|
|
1436
|
-
window.MindMapFeedback?.toast?.('Node run failed.', { kind: 'error' });
|
|
1437
|
-
} finally {
|
|
1438
|
-
nodeRunBtn.disabled = false;
|
|
1439
|
-
nodeRunBtn.classList.remove('disabled');
|
|
1440
|
-
}
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
const companySyncGoalBtn = element.querySelector('.js-btn-company-sync-goal');
|
|
1445
|
-
if (companySyncGoalBtn) {
|
|
1446
|
-
companySyncGoalBtn.addEventListener('click', async (e) => {
|
|
1447
|
-
e.stopPropagation();
|
|
1448
|
-
if (!_module?.dotNetHelper || companySyncGoalBtn.disabled) {
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
companySyncGoalBtn.disabled = true;
|
|
1453
|
-
applyCompanyBindingValues(nodeEntry.model, {
|
|
1454
|
-
'CompanyCore.LastMessage': 'Updating the existing company goal from this note...',
|
|
1455
|
-
'CompanyCore.Status': getCompanyStatus(nodeEntry.model) || 'Running'
|
|
1456
|
-
});
|
|
1457
|
-
syncCompanyNodeVisual(nodeEntry);
|
|
1458
|
-
showMenu(nodeId);
|
|
1459
|
-
|
|
1460
|
-
try {
|
|
1461
|
-
const mode = getCompanyBindingValue(nodeEntry.model, 'CompanyCore.ExecutionMode') || 'Auto';
|
|
1462
|
-
const subAgentsEnabled = getCompanyBindingValue(nodeEntry.model, 'CompanyCore.SubAgentsEnabled') !== 'false';
|
|
1463
|
-
const result = await _module.dotNetHelper.invokeMethodAsync('SyncCompanyGoalFromJs', nodeId, mode, subAgentsEnabled);
|
|
1464
|
-
applyCompanyRunResult(nodeEntry, result, mode, subAgentsEnabled);
|
|
1465
|
-
} catch (error) {
|
|
1466
|
-
console.error(`[MindMapMenuManager] SyncCompanyGoalFromJs failed for node ${nodeId}`, error);
|
|
1467
|
-
applyCompanyBindingValues(nodeEntry.model, {
|
|
1468
|
-
'CompanyCore.LastMessage': 'Updating the company goal failed.',
|
|
1469
|
-
'CompanyCore.Status': 'Failed'
|
|
1470
|
-
});
|
|
1471
|
-
} finally {
|
|
1472
|
-
syncCompanyNodeVisual(nodeEntry);
|
|
1473
|
-
showMenu(nodeId);
|
|
1474
|
-
}
|
|
1475
|
-
});
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
const companyGraphBtn = element.querySelector('.js-btn-company-graph');
|
|
1479
|
-
if (companyGraphBtn) {
|
|
1480
|
-
companyGraphBtn.addEventListener('click', async (e) => {
|
|
1481
|
-
e.stopPropagation();
|
|
1482
|
-
await invokeMenuAction(companyGraphBtn, 'OpenCompanyGraphFromJs', nodeId);
|
|
1483
|
-
});
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
const companyRefreshBtn = element.querySelector('.js-btn-company-refresh');
|
|
1487
|
-
if (companyRefreshBtn) {
|
|
1488
|
-
companyRefreshBtn.addEventListener('click', async (e) => {
|
|
1489
|
-
e.stopPropagation();
|
|
1490
|
-
await invokeMenuAction(companyRefreshBtn, 'RefreshCompanyBindingFromJs', nodeId);
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
const companyPauseBtn = element.querySelector('.js-btn-company-pause');
|
|
1495
|
-
if (companyPauseBtn) {
|
|
1496
|
-
companyPauseBtn.addEventListener('click', async (e) => {
|
|
1497
|
-
e.stopPropagation();
|
|
1498
|
-
await invokeMenuAction(companyPauseBtn, 'PauseCompanyFromJs', nodeId);
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
const companyResumeBtn = element.querySelector('.js-btn-company-resume');
|
|
1503
|
-
if (companyResumeBtn) {
|
|
1504
|
-
companyResumeBtn.addEventListener('click', async (e) => {
|
|
1505
|
-
e.stopPropagation();
|
|
1506
|
-
await invokeMenuAction(companyResumeBtn, 'ResumeCompanyFromJs', nodeId);
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
const companyArchiveBtn = element.querySelector('.js-btn-company-archive');
|
|
1511
|
-
if (companyArchiveBtn) {
|
|
1512
|
-
companyArchiveBtn.addEventListener('click', async (e) => {
|
|
1513
|
-
e.stopPropagation();
|
|
1514
|
-
await invokeMenuAction(companyArchiveBtn, 'ArchiveCompanyFromJs', nodeId);
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
const companyDetachBtn = element.querySelector('.js-btn-company-detach');
|
|
1519
|
-
if (companyDetachBtn) {
|
|
1520
|
-
companyDetachBtn.addEventListener('click', async (e) => {
|
|
1521
|
-
e.stopPropagation();
|
|
1522
|
-
await invokeMenuAction(companyDetachBtn, 'DetachCompanyFromJs', nodeId);
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
const companyRemoveBtn = element.querySelector('.js-btn-company-remove');
|
|
1527
|
-
if (companyRemoveBtn) {
|
|
1528
|
-
companyRemoveBtn.addEventListener('click', async (e) => {
|
|
1529
|
-
e.stopPropagation();
|
|
1530
|
-
if (!_module?.dotNetHelper || companyRemoveBtn.disabled) {
|
|
1531
|
-
return;
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
const shouldRemove = await showConfirmDialog({
|
|
1535
|
-
title: 'Remove company?',
|
|
1536
|
-
message: 'The company will be archived and detached from this node.',
|
|
1537
|
-
confirmText: 'Remove',
|
|
1538
|
-
cancelText: 'Cancel'
|
|
1539
|
-
});
|
|
1540
|
-
if (!shouldRemove) {
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
companyRemoveBtn.disabled = true;
|
|
1545
|
-
try {
|
|
1546
|
-
await _module.dotNetHelper.invokeMethodAsync('RemoveCompanyFromJs', nodeId);
|
|
1547
|
-
clearCompanyBindingValues(nodeEntry);
|
|
1548
|
-
} catch (error) {
|
|
1549
|
-
console.error(`[MindMapMenuManager] RemoveCompanyFromJs failed for node ${nodeId}`, error);
|
|
1550
|
-
} finally {
|
|
1551
|
-
syncCompanyNodeVisual(nodeEntry);
|
|
1552
|
-
showMenu(nodeId);
|
|
1553
|
-
}
|
|
1554
|
-
});
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
function updateForCameraMotion() {
|
|
1559
|
-
if (!currentMenuElement || !currentMenuNodeId || !_module) return false;
|
|
1560
|
-
const nodeEntry = _module.nodeObjectsById.get(currentMenuNodeId);
|
|
1561
|
-
if (!nodeEntry) {
|
|
1562
|
-
hideMenu();
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
updateMenuScreenPosition(currentMenuElement, nodeEntry, true);
|
|
1566
|
-
return true;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
window.MindMapMenuManager = { init, showMenu, hideMenu, update, updateForCameraMotion };
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
// ▲▲▲ [NEW] ▲▲▲
|
|
1410
|
+
|
|
1411
|
+
const nodeRunBtn = element.querySelector('.js-btn-node-run');
|
|
1412
|
+
if (nodeRunBtn) {
|
|
1413
|
+
nodeRunBtn.addEventListener('click', async (e) => {
|
|
1414
|
+
e.stopPropagation();
|
|
1415
|
+
if (!_module?.dotNetHelper || nodeRunBtn.disabled) {
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
nodeRunBtn.disabled = true;
|
|
1420
|
+
nodeRunBtn.classList.add('disabled');
|
|
1421
|
+
setExecutableNodeRunVisualState(nodeId, 'running', 'Running');
|
|
1422
|
+
hideMenu();
|
|
1423
|
+
|
|
1424
|
+
try {
|
|
1425
|
+
const result = await _module.dotNetHelper.invokeMethodAsync('RunExecutableNodeFromJs', nodeId);
|
|
1426
|
+
await syncNodeRunResultStates(result);
|
|
1427
|
+
if (result?.success === false && !Array.isArray(result?.nodeStates)) {
|
|
1428
|
+
setExecutableNodeRunVisualState(nodeId, 'idle', 'Failed');
|
|
1429
|
+
}
|
|
1430
|
+
if (result?.success === false) {
|
|
1431
|
+
window.MindMapFeedback?.toast?.(result.message || 'Node run failed.', { kind: 'error' });
|
|
1432
|
+
}
|
|
1433
|
+
} catch (error) {
|
|
1434
|
+
console.error(`[MindMapMenuManager] RunExecutableNodeFromJs failed for node ${nodeId}`, error);
|
|
1435
|
+
setExecutableNodeRunVisualState(nodeId, 'idle', 'Failed');
|
|
1436
|
+
window.MindMapFeedback?.toast?.('Node run failed.', { kind: 'error' });
|
|
1437
|
+
} finally {
|
|
1438
|
+
nodeRunBtn.disabled = false;
|
|
1439
|
+
nodeRunBtn.classList.remove('disabled');
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
const companySyncGoalBtn = element.querySelector('.js-btn-company-sync-goal');
|
|
1445
|
+
if (companySyncGoalBtn) {
|
|
1446
|
+
companySyncGoalBtn.addEventListener('click', async (e) => {
|
|
1447
|
+
e.stopPropagation();
|
|
1448
|
+
if (!_module?.dotNetHelper || companySyncGoalBtn.disabled) {
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
companySyncGoalBtn.disabled = true;
|
|
1453
|
+
applyCompanyBindingValues(nodeEntry.model, {
|
|
1454
|
+
'CompanyCore.LastMessage': 'Updating the existing company goal from this note...',
|
|
1455
|
+
'CompanyCore.Status': getCompanyStatus(nodeEntry.model) || 'Running'
|
|
1456
|
+
});
|
|
1457
|
+
syncCompanyNodeVisual(nodeEntry);
|
|
1458
|
+
showMenu(nodeId);
|
|
1459
|
+
|
|
1460
|
+
try {
|
|
1461
|
+
const mode = getCompanyBindingValue(nodeEntry.model, 'CompanyCore.ExecutionMode') || 'Auto';
|
|
1462
|
+
const subAgentsEnabled = getCompanyBindingValue(nodeEntry.model, 'CompanyCore.SubAgentsEnabled') !== 'false';
|
|
1463
|
+
const result = await _module.dotNetHelper.invokeMethodAsync('SyncCompanyGoalFromJs', nodeId, mode, subAgentsEnabled);
|
|
1464
|
+
applyCompanyRunResult(nodeEntry, result, mode, subAgentsEnabled);
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
console.error(`[MindMapMenuManager] SyncCompanyGoalFromJs failed for node ${nodeId}`, error);
|
|
1467
|
+
applyCompanyBindingValues(nodeEntry.model, {
|
|
1468
|
+
'CompanyCore.LastMessage': 'Updating the company goal failed.',
|
|
1469
|
+
'CompanyCore.Status': 'Failed'
|
|
1470
|
+
});
|
|
1471
|
+
} finally {
|
|
1472
|
+
syncCompanyNodeVisual(nodeEntry);
|
|
1473
|
+
showMenu(nodeId);
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
const companyGraphBtn = element.querySelector('.js-btn-company-graph');
|
|
1479
|
+
if (companyGraphBtn) {
|
|
1480
|
+
companyGraphBtn.addEventListener('click', async (e) => {
|
|
1481
|
+
e.stopPropagation();
|
|
1482
|
+
await invokeMenuAction(companyGraphBtn, 'OpenCompanyGraphFromJs', nodeId);
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
const companyRefreshBtn = element.querySelector('.js-btn-company-refresh');
|
|
1487
|
+
if (companyRefreshBtn) {
|
|
1488
|
+
companyRefreshBtn.addEventListener('click', async (e) => {
|
|
1489
|
+
e.stopPropagation();
|
|
1490
|
+
await invokeMenuAction(companyRefreshBtn, 'RefreshCompanyBindingFromJs', nodeId);
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
const companyPauseBtn = element.querySelector('.js-btn-company-pause');
|
|
1495
|
+
if (companyPauseBtn) {
|
|
1496
|
+
companyPauseBtn.addEventListener('click', async (e) => {
|
|
1497
|
+
e.stopPropagation();
|
|
1498
|
+
await invokeMenuAction(companyPauseBtn, 'PauseCompanyFromJs', nodeId);
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
const companyResumeBtn = element.querySelector('.js-btn-company-resume');
|
|
1503
|
+
if (companyResumeBtn) {
|
|
1504
|
+
companyResumeBtn.addEventListener('click', async (e) => {
|
|
1505
|
+
e.stopPropagation();
|
|
1506
|
+
await invokeMenuAction(companyResumeBtn, 'ResumeCompanyFromJs', nodeId);
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const companyArchiveBtn = element.querySelector('.js-btn-company-archive');
|
|
1511
|
+
if (companyArchiveBtn) {
|
|
1512
|
+
companyArchiveBtn.addEventListener('click', async (e) => {
|
|
1513
|
+
e.stopPropagation();
|
|
1514
|
+
await invokeMenuAction(companyArchiveBtn, 'ArchiveCompanyFromJs', nodeId);
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
const companyDetachBtn = element.querySelector('.js-btn-company-detach');
|
|
1519
|
+
if (companyDetachBtn) {
|
|
1520
|
+
companyDetachBtn.addEventListener('click', async (e) => {
|
|
1521
|
+
e.stopPropagation();
|
|
1522
|
+
await invokeMenuAction(companyDetachBtn, 'DetachCompanyFromJs', nodeId);
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const companyRemoveBtn = element.querySelector('.js-btn-company-remove');
|
|
1527
|
+
if (companyRemoveBtn) {
|
|
1528
|
+
companyRemoveBtn.addEventListener('click', async (e) => {
|
|
1529
|
+
e.stopPropagation();
|
|
1530
|
+
if (!_module?.dotNetHelper || companyRemoveBtn.disabled) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
const shouldRemove = await showConfirmDialog({
|
|
1535
|
+
title: 'Remove company?',
|
|
1536
|
+
message: 'The company will be archived and detached from this node.',
|
|
1537
|
+
confirmText: 'Remove',
|
|
1538
|
+
cancelText: 'Cancel'
|
|
1539
|
+
});
|
|
1540
|
+
if (!shouldRemove) {
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
companyRemoveBtn.disabled = true;
|
|
1545
|
+
try {
|
|
1546
|
+
await _module.dotNetHelper.invokeMethodAsync('RemoveCompanyFromJs', nodeId);
|
|
1547
|
+
clearCompanyBindingValues(nodeEntry);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
console.error(`[MindMapMenuManager] RemoveCompanyFromJs failed for node ${nodeId}`, error);
|
|
1550
|
+
} finally {
|
|
1551
|
+
syncCompanyNodeVisual(nodeEntry);
|
|
1552
|
+
showMenu(nodeId);
|
|
1553
|
+
}
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
function updateForCameraMotion() {
|
|
1559
|
+
if (!currentMenuElement || !currentMenuNodeId || !_module) return false;
|
|
1560
|
+
const nodeEntry = _module.nodeObjectsById.get(currentMenuNodeId);
|
|
1561
|
+
if (!nodeEntry) {
|
|
1562
|
+
hideMenu();
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
updateMenuScreenPosition(currentMenuElement, nodeEntry, true);
|
|
1566
|
+
return true;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
window.MindMapMenuManager = { init, showMenu, hideMenu, update, updateForCameraMotion };
|
|
1570
1570
|
|
|
1571
1571
|
console.log('✅ mind-map-menu-manager.js loaded');
|
|
1572
1572
|
})();
|