@mindexec/cli 0.2.1 → 0.2.3
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 +38 -0
- package/package.json +6 -4
- package/remote-hub.js +571 -0
- package/scripts/remote-hub-smoke.mjs +117 -0
- package/server.js +108 -28
- package/wwwroot/_content/MindExecution.Shared/css/mind-map-overrides.css +11 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +76 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +629 -5
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-lod-renderer.js +16 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-nodes.js +16 -5
- package/wwwroot/_framework/MindExecution.Core.5luow1xgjs.dll +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.gwwc40sc45.dll → MindExecution.Kernel.mot9nj6bzm.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.0jgrn1sckv.dll → MindExecution.Plugins.Admin.x9v2drg2f7.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.13mme2qcag.dll → MindExecution.Plugins.Business.b0kjoyx31x.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.dfp2mdt45q.dll → MindExecution.Plugins.Concept.6tojojgh1a.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.3w4t6n3se0.dll → MindExecution.Plugins.Directory.fqtbuqadsx.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.s0qpntz420.dll → MindExecution.Plugins.PlanMaster.j7llfeae6l.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.iu11fq8d16.dll → MindExecution.Plugins.YouTube.yo5fwdhugr.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.7j27dcqnrc.dll → MindExecution.Shared.0qi7vbn9a4.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Web.6cv7ad7rik.dll +0 -0
- package/wwwroot/_framework/blazor.boot.json +21 -21
- package/wwwroot/index.html +3 -3
- package/wwwroot/service-worker-assets.js +28 -28
- package/wwwroot/service-worker.js +1 -1
- package/wwwroot/_framework/MindExecution.Core.1q1trifbuu.dll +0 -0
- package/wwwroot/_framework/MindExecution.Web.pq1ty8ov2v.dll +0 -0
|
@@ -297,6 +297,17 @@
|
|
|
297
297
|
overflow: hidden !important;
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
.css3d-resolution-wrapper.node-type-templatelauncher,
|
|
301
|
+
.css3d-resolution-wrapper.node-type-templatelauncher > .map-node,
|
|
302
|
+
.css3d-resolution-wrapper.node-type-templatelauncher > .map-node-template-card {
|
|
303
|
+
contain: layout style !important;
|
|
304
|
+
content-visibility: visible !important;
|
|
305
|
+
contain-intrinsic-size: auto !important;
|
|
306
|
+
isolation: isolate !important;
|
|
307
|
+
backface-visibility: visible !important;
|
|
308
|
+
-webkit-backface-visibility: visible !important;
|
|
309
|
+
}
|
|
310
|
+
|
|
300
311
|
.css3d-resolution-wrapper.node-type-image .node-content-wrapper,
|
|
301
312
|
.css3d-resolution-wrapper.node-type-video .node-content-wrapper,
|
|
302
313
|
.css3d-resolution-wrapper.node-type-embed .node-content-wrapper,
|
|
@@ -3196,6 +3207,8 @@
|
|
|
3196
3207
|
|
|
3197
3208
|
nodeModel.response = normalized;
|
|
3198
3209
|
nodeModel.Response = normalized;
|
|
3210
|
+
mediaEl.dataset.mediaSourceUrl = normalized;
|
|
3211
|
+
mediaEl.dataset.resolvedMediaSourceUrl = normalized;
|
|
3199
3212
|
mediaEl.setAttribute('src', normalized);
|
|
3200
3213
|
|
|
3201
3214
|
if (contentTypeLower === 'video' && typeof mediaEl.load === 'function') {
|
|
@@ -3266,11 +3279,25 @@
|
|
|
3266
3279
|
return;
|
|
3267
3280
|
}
|
|
3268
3281
|
|
|
3282
|
+
const clearVideoPlaybackError = () => {
|
|
3283
|
+
if (contentTypeLower !== 'video') {
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
const metadata = ensureCssMediaMetadata(nodeModel);
|
|
3288
|
+
delete metadata.VideoPlaybackError;
|
|
3289
|
+
delete metadata.videoPlaybackError;
|
|
3290
|
+
delete metadata.VideoPlaybackErrorCode;
|
|
3291
|
+
delete metadata.videoPlaybackErrorCode;
|
|
3292
|
+
mediaEl.closest?.('.video-content')?.classList?.remove?.('mind-map-video-error');
|
|
3293
|
+
};
|
|
3294
|
+
|
|
3269
3295
|
const handleReady = () => {
|
|
3270
3296
|
if (contentTypeLower === 'image') {
|
|
3271
3297
|
mediaEl.dataset.mediaReady = '1';
|
|
3272
3298
|
mediaEl.style.opacity = '1';
|
|
3273
3299
|
}
|
|
3300
|
+
clearVideoPlaybackError();
|
|
3274
3301
|
setAssetRefreshState(nodeModel, '', '');
|
|
3275
3302
|
syncMediaLoadingRowForElement(mediaEl, nodeModel, contentTypeLower);
|
|
3276
3303
|
mediaEl.dataset.assetRefreshAttempted = '';
|
|
@@ -3282,12 +3309,61 @@
|
|
|
3282
3309
|
}
|
|
3283
3310
|
};
|
|
3284
3311
|
|
|
3285
|
-
|
|
3312
|
+
const describeVideoElementError = () => {
|
|
3313
|
+
const code = Number(mediaEl?.error?.code || 0);
|
|
3314
|
+
const reason = {
|
|
3315
|
+
1: 'loading was aborted',
|
|
3316
|
+
2: 'a network error stopped loading',
|
|
3317
|
+
3: 'the browser could not decode the media',
|
|
3318
|
+
4: 'the browser does not support this codec or container'
|
|
3319
|
+
}[code] || 'the browser could not load the media';
|
|
3320
|
+
const source = getCssMediaCanonicalSource(mediaEl, nodeModel?.response || nodeModel?.Response || '');
|
|
3321
|
+
let hint = 'Try H.264/AAC MP4 or WebM.';
|
|
3322
|
+
try {
|
|
3323
|
+
const parsed = new URL(source, window.location?.href || undefined);
|
|
3324
|
+
const extension = parsed.pathname.split('.').pop()?.toLowerCase() || '';
|
|
3325
|
+
if (extension === 'mov' || extension === 'm4v') {
|
|
3326
|
+
hint = 'MOV/M4V often fails in browsers unless encoded as H.264/AAC.';
|
|
3327
|
+
}
|
|
3328
|
+
} catch { }
|
|
3329
|
+
|
|
3330
|
+
return `Video playback failed: ${reason}. ${hint}`;
|
|
3331
|
+
};
|
|
3332
|
+
|
|
3333
|
+
const markVideoElementError = () => {
|
|
3334
|
+
if (contentTypeLower !== 'video') {
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
const metadata = ensureCssMediaMetadata(nodeModel);
|
|
3339
|
+
const message = describeVideoElementError();
|
|
3340
|
+
metadata.VideoPlaybackError = message;
|
|
3341
|
+
metadata.videoPlaybackError = message;
|
|
3342
|
+
metadata.VideoPlaybackErrorCode = String(Number(mediaEl?.error?.code || 0));
|
|
3343
|
+
metadata.videoPlaybackErrorCode = metadata.VideoPlaybackErrorCode;
|
|
3344
|
+
mediaEl.closest?.('.video-content')?.classList?.add?.('mind-map-video-error');
|
|
3345
|
+
setAssetRefreshState(nodeModel, 'failed', message);
|
|
3346
|
+
syncMediaLoadingRowForElement(mediaEl, nodeModel, contentTypeLower);
|
|
3347
|
+
};
|
|
3348
|
+
|
|
3349
|
+
const handleMediaError = async () => {
|
|
3286
3350
|
if (contentTypeLower === 'image') {
|
|
3287
3351
|
mediaEl.dataset.mediaReady = '0';
|
|
3288
3352
|
mediaEl.style.opacity = '0';
|
|
3289
3353
|
}
|
|
3290
|
-
|
|
3354
|
+
const beforeSource = getCssMediaCanonicalSource(mediaEl, nodeModel?.response || nodeModel?.Response || '');
|
|
3355
|
+
await tryRefreshCssMediaSource(mediaEl, nodeModel, contentTypeLower);
|
|
3356
|
+
if (contentTypeLower === 'video') {
|
|
3357
|
+
const afterSource = getCssMediaCanonicalSource(mediaEl, nodeModel?.response || nodeModel?.Response || '');
|
|
3358
|
+
const refreshState = getAssetRefreshState(nodeModel);
|
|
3359
|
+
if (afterSource === beforeSource && refreshState !== 'refreshing') {
|
|
3360
|
+
markVideoElementError();
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
};
|
|
3364
|
+
|
|
3365
|
+
mediaEl.onerror = () => {
|
|
3366
|
+
void handleMediaError();
|
|
3291
3367
|
};
|
|
3292
3368
|
|
|
3293
3369
|
if (contentTypeLower === 'video') {
|
|
@@ -3324,6 +3400,7 @@
|
|
|
3324
3400
|
const MEDIA_GENERATION_STATUS_METADATA_KEY = 'MediaGenerationStatus';
|
|
3325
3401
|
const MEDIA_GENERATION_STATUS_MESSAGE_METADATA_KEY = 'MediaGenerationStatusMessage';
|
|
3326
3402
|
const BUSINESS_AUTOMATION_SEMANTIC_TYPE = 'BusinessAutomationNode';
|
|
3403
|
+
const REMOTE_FLEET_SEMANTIC_TYPE = 'RemoteFleetMonitor';
|
|
3327
3404
|
const AUTOMATION_NODE_KIND_METADATA_KEY = 'AutomationNodeKind';
|
|
3328
3405
|
const AUTOMATION_NODE_LABEL_METADATA_KEY = 'AutomationNodeLabel';
|
|
3329
3406
|
const AUTOMATION_NODE_DESCRIPTION_METADATA_KEY = 'AutomationNodeDescription';
|
|
@@ -3493,6 +3570,10 @@
|
|
|
3493
3570
|
return getNodeSemanticType(nodeModel) === BUSINESS_AUTOMATION_SEMANTIC_TYPE;
|
|
3494
3571
|
}
|
|
3495
3572
|
|
|
3573
|
+
function isRemoteFleetMonitorNode(nodeModel) {
|
|
3574
|
+
return getNodeSemanticType(nodeModel) === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3496
3577
|
function isBusinessAutomationContextSourceNode(nodeModel) {
|
|
3497
3578
|
if (!nodeModel || isBusinessAutomationNode(nodeModel)) {
|
|
3498
3579
|
return false;
|
|
@@ -3506,7 +3587,8 @@
|
|
|
3506
3587
|
const semanticType = getNodeSemanticType(nodeModel);
|
|
3507
3588
|
return semanticType === 'MindCanvasAgent'
|
|
3508
3589
|
|| semanticType === 'AgentCommand'
|
|
3509
|
-
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
3590
|
+
|| semanticType === BUSINESS_AUTOMATION_SEMANTIC_TYPE
|
|
3591
|
+
|| semanticType === REMOTE_FLEET_SEMANTIC_TYPE;
|
|
3510
3592
|
}
|
|
3511
3593
|
|
|
3512
3594
|
function allowsMemoExternalChrome(nodeModel) {
|
|
@@ -5740,7 +5822,7 @@
|
|
|
5740
5822
|
tone = 'refresh';
|
|
5741
5823
|
} else if ((contentTypeLower === 'image' || contentTypeLower === 'video') && assetRefreshState === 'failed') {
|
|
5742
5824
|
showRow = true;
|
|
5743
|
-
label = 'Asset refresh failed';
|
|
5825
|
+
label = contentTypeLower === 'video' ? 'Video playback failed' : 'Asset refresh failed';
|
|
5744
5826
|
message = assetRefreshMessage || 'Reload the board or reopen this file.';
|
|
5745
5827
|
tone = 'failed';
|
|
5746
5828
|
} else if (contentTypeLower === 'video') {
|
|
@@ -11758,6 +11840,18 @@
|
|
|
11758
11840
|
function renderMemoBodyView(bodyView, nodeModel, content) {
|
|
11759
11841
|
if (!bodyView) return;
|
|
11760
11842
|
|
|
11843
|
+
if (isRemoteFleetMonitorNode(nodeModel)) {
|
|
11844
|
+
renderRemoteFleetMonitor(bodyView, nodeModel);
|
|
11845
|
+
return;
|
|
11846
|
+
}
|
|
11847
|
+
|
|
11848
|
+
bodyView.classList.remove('map-node-remote-fleet__body');
|
|
11849
|
+
bodyView.style.cssText = `
|
|
11850
|
+
flex: 1 1 auto;
|
|
11851
|
+
min-height: 0;
|
|
11852
|
+
pointer-events: auto;
|
|
11853
|
+
`;
|
|
11854
|
+
|
|
11761
11855
|
const nextValue = String(content || '');
|
|
11762
11856
|
bodyView.dataset.src = nextValue;
|
|
11763
11857
|
|
|
@@ -11840,6 +11934,533 @@
|
|
|
11840
11934
|
}, 220);
|
|
11841
11935
|
}
|
|
11842
11936
|
|
|
11937
|
+
function getRemoteFleetMetadataValue(nodeModel, key, fallback = '') {
|
|
11938
|
+
const metadata = getNodeMetadata(nodeModel) || {};
|
|
11939
|
+
const value = metadata[key];
|
|
11940
|
+
return value === undefined || value === null ? fallback : String(value);
|
|
11941
|
+
}
|
|
11942
|
+
|
|
11943
|
+
function parseRemoteFleetDevices(nodeModel) {
|
|
11944
|
+
const raw = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDevicesJson', '[]');
|
|
11945
|
+
if (!raw.trim()) {
|
|
11946
|
+
return [];
|
|
11947
|
+
}
|
|
11948
|
+
|
|
11949
|
+
try {
|
|
11950
|
+
const parsed = JSON.parse(raw);
|
|
11951
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
11952
|
+
} catch {
|
|
11953
|
+
return [];
|
|
11954
|
+
}
|
|
11955
|
+
}
|
|
11956
|
+
|
|
11957
|
+
function formatRemoteFleetNumber(value, digits = 0) {
|
|
11958
|
+
const number = Number(value);
|
|
11959
|
+
return Number.isFinite(number) ? number.toFixed(digits) : '-';
|
|
11960
|
+
}
|
|
11961
|
+
|
|
11962
|
+
function formatRemoteFleetPercent(value) {
|
|
11963
|
+
const number = Number(value);
|
|
11964
|
+
if (!Number.isFinite(number)) {
|
|
11965
|
+
return '-';
|
|
11966
|
+
}
|
|
11967
|
+
|
|
11968
|
+
return `${Math.round(Math.max(0, Math.min(1, number)) * 100)}%`;
|
|
11969
|
+
}
|
|
11970
|
+
|
|
11971
|
+
function formatRemoteFleetDuration(seconds) {
|
|
11972
|
+
const value = Number(seconds);
|
|
11973
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
11974
|
+
return '-';
|
|
11975
|
+
}
|
|
11976
|
+
|
|
11977
|
+
const days = Math.floor(value / 86400);
|
|
11978
|
+
const hours = Math.floor((value % 86400) / 3600);
|
|
11979
|
+
const minutes = Math.floor((value % 3600) / 60);
|
|
11980
|
+
if (days > 0) {
|
|
11981
|
+
return `${days}d ${hours}h`;
|
|
11982
|
+
}
|
|
11983
|
+
if (hours > 0) {
|
|
11984
|
+
return `${hours}h ${minutes}m`;
|
|
11985
|
+
}
|
|
11986
|
+
return `${minutes}m`;
|
|
11987
|
+
}
|
|
11988
|
+
|
|
11989
|
+
function formatRemoteFleetAge(value) {
|
|
11990
|
+
const timestamp = Date.parse(String(value || ''));
|
|
11991
|
+
if (!Number.isFinite(timestamp)) {
|
|
11992
|
+
return '-';
|
|
11993
|
+
}
|
|
11994
|
+
|
|
11995
|
+
const seconds = Math.max(0, Math.round((Date.now() - timestamp) / 1000));
|
|
11996
|
+
if (seconds < 60) {
|
|
11997
|
+
return 'now';
|
|
11998
|
+
}
|
|
11999
|
+
if (seconds < 3600) {
|
|
12000
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
12001
|
+
}
|
|
12002
|
+
if (seconds < 86400) {
|
|
12003
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
12004
|
+
}
|
|
12005
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
12006
|
+
}
|
|
12007
|
+
|
|
12008
|
+
function createRemoteFleetStat(label, value, tone = 'default') {
|
|
12009
|
+
const item = document.createElement('div');
|
|
12010
|
+
item.style.cssText = `
|
|
12011
|
+
display: flex;
|
|
12012
|
+
flex-direction: column;
|
|
12013
|
+
gap: 2px;
|
|
12014
|
+
min-width: 0;
|
|
12015
|
+
padding: 8px 10px;
|
|
12016
|
+
border-radius: 8px;
|
|
12017
|
+
background: ${tone === 'online' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(15, 23, 42, 0.05)'};
|
|
12018
|
+
border: 1px solid ${tone === 'online' ? 'rgba(16, 185, 129, 0.22)' : 'rgba(148, 163, 184, 0.22)'};
|
|
12019
|
+
`;
|
|
12020
|
+
|
|
12021
|
+
const labelEl = document.createElement('span');
|
|
12022
|
+
labelEl.textContent = label;
|
|
12023
|
+
labelEl.style.cssText = `
|
|
12024
|
+
color: #64748b;
|
|
12025
|
+
font-size: 10px;
|
|
12026
|
+
font-weight: 800;
|
|
12027
|
+
letter-spacing: 0;
|
|
12028
|
+
text-transform: uppercase;
|
|
12029
|
+
`;
|
|
12030
|
+
|
|
12031
|
+
const valueEl = document.createElement('strong');
|
|
12032
|
+
valueEl.textContent = value;
|
|
12033
|
+
valueEl.style.cssText = `
|
|
12034
|
+
color: ${tone === 'online' ? '#047857' : '#0f172a'};
|
|
12035
|
+
font-size: 18px;
|
|
12036
|
+
line-height: 1.1;
|
|
12037
|
+
font-weight: 900;
|
|
12038
|
+
letter-spacing: 0;
|
|
12039
|
+
overflow: hidden;
|
|
12040
|
+
text-overflow: ellipsis;
|
|
12041
|
+
white-space: nowrap;
|
|
12042
|
+
`;
|
|
12043
|
+
|
|
12044
|
+
item.appendChild(labelEl);
|
|
12045
|
+
item.appendChild(valueEl);
|
|
12046
|
+
return item;
|
|
12047
|
+
}
|
|
12048
|
+
|
|
12049
|
+
function createRemoteFleetButton(label, title, action) {
|
|
12050
|
+
const button = document.createElement('button');
|
|
12051
|
+
button.type = 'button';
|
|
12052
|
+
button.dataset.remoteFleetAction = action;
|
|
12053
|
+
button.textContent = label;
|
|
12054
|
+
button.title = title || label;
|
|
12055
|
+
button.setAttribute('aria-label', title || label);
|
|
12056
|
+
button.style.cssText = `
|
|
12057
|
+
display: inline-flex;
|
|
12058
|
+
align-items: center;
|
|
12059
|
+
justify-content: center;
|
|
12060
|
+
min-width: 0;
|
|
12061
|
+
height: 28px;
|
|
12062
|
+
padding: 0 10px;
|
|
12063
|
+
border-radius: 7px;
|
|
12064
|
+
border: 1px solid rgba(37, 99, 235, 0.32);
|
|
12065
|
+
background: #ffffff;
|
|
12066
|
+
color: #1d4ed8;
|
|
12067
|
+
font-size: 11px;
|
|
12068
|
+
font-weight: 800;
|
|
12069
|
+
letter-spacing: 0;
|
|
12070
|
+
cursor: pointer;
|
|
12071
|
+
pointer-events: auto;
|
|
12072
|
+
`;
|
|
12073
|
+
return button;
|
|
12074
|
+
}
|
|
12075
|
+
|
|
12076
|
+
async function syncRemoteFleetNodeStateFromResult(result) {
|
|
12077
|
+
const nodeState = result?.nodeState || result?.refresh?.nodeState;
|
|
12078
|
+
if (nodeState && window.mindMap?.syncNodeStates) {
|
|
12079
|
+
await window.mindMap.syncNodeStates([nodeState]);
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
|
|
12083
|
+
function renderRemoteFleetMonitor(bodyView, nodeModel) {
|
|
12084
|
+
if (!bodyView) return;
|
|
12085
|
+
|
|
12086
|
+
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
12087
|
+
const devices = parseRemoteFleetDevices(nodeModel);
|
|
12088
|
+
const total = Number(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDeviceCount', devices.length));
|
|
12089
|
+
const connected = Number(getRemoteFleetMetadataValue(
|
|
12090
|
+
nodeModel,
|
|
12091
|
+
'RemoteFleetConnectedDeviceCount',
|
|
12092
|
+
devices.filter(device => device?.connected === true || device?.Connected === true).length));
|
|
12093
|
+
const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
|
|
12094
|
+
const command = getRemoteFleetMetadataValue(
|
|
12095
|
+
nodeModel,
|
|
12096
|
+
'RemoteFleetConnectCommand',
|
|
12097
|
+
`npx @mindexec/remote connect --manager ${endpoint} --pair <pair-token>`);
|
|
12098
|
+
const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
|
|
12099
|
+
const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
|
|
12100
|
+
const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
|
|
12101
|
+
|
|
12102
|
+
bodyView.dataset.src = `remote-fleet:${refreshedAt}:${devices.length}:${connected}`;
|
|
12103
|
+
bodyView.classList.add('map-node-remote-fleet__body');
|
|
12104
|
+
bodyView.innerHTML = '';
|
|
12105
|
+
bodyView.style.cssText = `
|
|
12106
|
+
flex: 1 1 auto;
|
|
12107
|
+
min-height: 0;
|
|
12108
|
+
pointer-events: auto;
|
|
12109
|
+
display: flex;
|
|
12110
|
+
flex-direction: column;
|
|
12111
|
+
gap: 10px;
|
|
12112
|
+
padding: 12px 14px 14px;
|
|
12113
|
+
overflow: hidden;
|
|
12114
|
+
background: linear-gradient(180deg, rgba(248, 250, 252, 0.96), rgba(241, 245, 249, 0.92));
|
|
12115
|
+
`;
|
|
12116
|
+
|
|
12117
|
+
const top = document.createElement('div');
|
|
12118
|
+
top.style.cssText = `
|
|
12119
|
+
display: grid;
|
|
12120
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
12121
|
+
gap: 8px;
|
|
12122
|
+
flex: 0 0 auto;
|
|
12123
|
+
`;
|
|
12124
|
+
top.appendChild(createRemoteFleetStat('Hub', hubStatus === 'online' ? 'Online' : 'Offline', hubStatus === 'online' ? 'online' : 'default'));
|
|
12125
|
+
top.appendChild(createRemoteFleetStat('Connected', `${Number.isFinite(connected) ? connected : 0}/${Number.isFinite(total) ? total : devices.length}`, connected > 0 ? 'online' : 'default'));
|
|
12126
|
+
top.appendChild(createRemoteFleetStat('Mode', 'All', 'default'));
|
|
12127
|
+
top.appendChild(createRemoteFleetStat('Paging', 'None', 'default'));
|
|
12128
|
+
bodyView.appendChild(top);
|
|
12129
|
+
|
|
12130
|
+
const commandRow = document.createElement('div');
|
|
12131
|
+
commandRow.style.cssText = `
|
|
12132
|
+
display: grid;
|
|
12133
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
12134
|
+
gap: 8px;
|
|
12135
|
+
align-items: center;
|
|
12136
|
+
flex: 0 0 auto;
|
|
12137
|
+
`;
|
|
12138
|
+
|
|
12139
|
+
const commandText = document.createElement('code');
|
|
12140
|
+
commandText.textContent = command;
|
|
12141
|
+
commandText.style.cssText = `
|
|
12142
|
+
min-width: 0;
|
|
12143
|
+
overflow: hidden;
|
|
12144
|
+
text-overflow: ellipsis;
|
|
12145
|
+
white-space: nowrap;
|
|
12146
|
+
padding: 8px 10px;
|
|
12147
|
+
border-radius: 7px;
|
|
12148
|
+
background: rgba(15, 23, 42, 0.92);
|
|
12149
|
+
color: #e2e8f0;
|
|
12150
|
+
font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
|
|
12151
|
+
font-size: 11px;
|
|
12152
|
+
line-height: 1.2;
|
|
12153
|
+
`;
|
|
12154
|
+
commandText.title = command;
|
|
12155
|
+
|
|
12156
|
+
const copyButton = createRemoteFleetButton('Copy', 'Copy agent command', 'copy-command');
|
|
12157
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh remote devices', 'refresh');
|
|
12158
|
+
commandRow.appendChild(commandText);
|
|
12159
|
+
commandRow.appendChild(copyButton);
|
|
12160
|
+
commandRow.appendChild(refreshButton);
|
|
12161
|
+
bodyView.appendChild(commandRow);
|
|
12162
|
+
|
|
12163
|
+
if (lastError.trim()) {
|
|
12164
|
+
const errorEl = document.createElement('div');
|
|
12165
|
+
errorEl.textContent = lastError;
|
|
12166
|
+
errorEl.style.cssText = `
|
|
12167
|
+
flex: 0 0 auto;
|
|
12168
|
+
padding: 8px 10px;
|
|
12169
|
+
border-radius: 7px;
|
|
12170
|
+
background: rgba(248, 113, 113, 0.12);
|
|
12171
|
+
color: #991b1b;
|
|
12172
|
+
font-size: 12px;
|
|
12173
|
+
line-height: 1.35;
|
|
12174
|
+
font-weight: 700;
|
|
12175
|
+
overflow-wrap: break-word;
|
|
12176
|
+
`;
|
|
12177
|
+
bodyView.appendChild(errorEl);
|
|
12178
|
+
}
|
|
12179
|
+
|
|
12180
|
+
const grid = document.createElement('div');
|
|
12181
|
+
grid.style.cssText = `
|
|
12182
|
+
flex: 1 1 auto;
|
|
12183
|
+
min-height: 0;
|
|
12184
|
+
overflow-y: auto;
|
|
12185
|
+
overflow-x: hidden;
|
|
12186
|
+
display: grid;
|
|
12187
|
+
grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
|
|
12188
|
+
align-content: start;
|
|
12189
|
+
gap: 8px;
|
|
12190
|
+
padding-right: 4px;
|
|
12191
|
+
`;
|
|
12192
|
+
|
|
12193
|
+
if (devices.length === 0) {
|
|
12194
|
+
const empty = document.createElement('div');
|
|
12195
|
+
empty.textContent = 'No devices connected yet.';
|
|
12196
|
+
empty.style.cssText = `
|
|
12197
|
+
grid-column: 1 / -1;
|
|
12198
|
+
display: flex;
|
|
12199
|
+
align-items: center;
|
|
12200
|
+
min-height: 94px;
|
|
12201
|
+
padding: 14px;
|
|
12202
|
+
border-radius: 8px;
|
|
12203
|
+
border: 1px dashed rgba(100, 116, 139, 0.36);
|
|
12204
|
+
color: #475569;
|
|
12205
|
+
font-size: 13px;
|
|
12206
|
+
font-weight: 800;
|
|
12207
|
+
background: rgba(255, 255, 255, 0.74);
|
|
12208
|
+
`;
|
|
12209
|
+
grid.appendChild(empty);
|
|
12210
|
+
} else {
|
|
12211
|
+
devices.forEach(device => {
|
|
12212
|
+
const connectedDevice = device?.connected === true || device?.Connected === true;
|
|
12213
|
+
const name = String(device?.name || device?.Name || device?.hostname || device?.Hostname || device?.deviceId || device?.DeviceId || 'device');
|
|
12214
|
+
const platform = [device?.platform || device?.Platform, device?.arch || device?.Arch]
|
|
12215
|
+
.filter(Boolean)
|
|
12216
|
+
.join(' / ') || 'unknown';
|
|
12217
|
+
const release = String(device?.release || device?.Release || '');
|
|
12218
|
+
const deviceId = String(device?.deviceId || device?.DeviceId || '');
|
|
12219
|
+
const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
|
|
12220
|
+
const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
|
|
12221
|
+
const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
|
|
12222
|
+
const hasThumbnail = /^data:image\/(png|jpe?g|webp|svg\+xml);base64,/i.test(thumbnailDataUrl);
|
|
12223
|
+
const card = document.createElement('article');
|
|
12224
|
+
card.dataset.deviceId = deviceId;
|
|
12225
|
+
card.style.cssText = `
|
|
12226
|
+
display: flex;
|
|
12227
|
+
flex-direction: column;
|
|
12228
|
+
gap: 8px;
|
|
12229
|
+
min-width: 0;
|
|
12230
|
+
min-height: 134px;
|
|
12231
|
+
padding: 10px;
|
|
12232
|
+
border-radius: 8px;
|
|
12233
|
+
background: #ffffff;
|
|
12234
|
+
border: 1px solid ${connectedDevice ? 'rgba(16, 185, 129, 0.34)' : 'rgba(148, 163, 184, 0.28)'};
|
|
12235
|
+
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.06);
|
|
12236
|
+
`;
|
|
12237
|
+
|
|
12238
|
+
const preview = document.createElement('div');
|
|
12239
|
+
preview.style.cssText = `
|
|
12240
|
+
position: relative;
|
|
12241
|
+
width: 100%;
|
|
12242
|
+
aspect-ratio: 16 / 9;
|
|
12243
|
+
overflow: hidden;
|
|
12244
|
+
border-radius: 7px;
|
|
12245
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
12246
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
12247
|
+
`;
|
|
12248
|
+
if (hasThumbnail) {
|
|
12249
|
+
const image = document.createElement('img');
|
|
12250
|
+
image.src = thumbnailDataUrl;
|
|
12251
|
+
image.alt = `${name} thumbnail`;
|
|
12252
|
+
image.loading = 'lazy';
|
|
12253
|
+
image.decoding = 'async';
|
|
12254
|
+
image.style.cssText = `
|
|
12255
|
+
width: 100%;
|
|
12256
|
+
height: 100%;
|
|
12257
|
+
object-fit: cover;
|
|
12258
|
+
display: block;
|
|
12259
|
+
`;
|
|
12260
|
+
preview.appendChild(image);
|
|
12261
|
+
} else {
|
|
12262
|
+
const placeholder = document.createElement('div');
|
|
12263
|
+
placeholder.textContent = thumbnailEnabled ? 'No frame yet' : 'Status only';
|
|
12264
|
+
placeholder.style.cssText = `
|
|
12265
|
+
position: absolute;
|
|
12266
|
+
inset: 0;
|
|
12267
|
+
display: flex;
|
|
12268
|
+
align-items: center;
|
|
12269
|
+
justify-content: center;
|
|
12270
|
+
color: rgba(226, 232, 240, 0.78);
|
|
12271
|
+
font-size: 11px;
|
|
12272
|
+
font-weight: 900;
|
|
12273
|
+
letter-spacing: 0;
|
|
12274
|
+
`;
|
|
12275
|
+
preview.appendChild(placeholder);
|
|
12276
|
+
}
|
|
12277
|
+
if (thumbnailCapturedAt) {
|
|
12278
|
+
const badge = document.createElement('span');
|
|
12279
|
+
badge.textContent = formatRemoteFleetAge(thumbnailCapturedAt);
|
|
12280
|
+
badge.style.cssText = `
|
|
12281
|
+
position: absolute;
|
|
12282
|
+
right: 6px;
|
|
12283
|
+
bottom: 6px;
|
|
12284
|
+
max-width: calc(100% - 12px);
|
|
12285
|
+
padding: 3px 6px;
|
|
12286
|
+
border-radius: 999px;
|
|
12287
|
+
background: rgba(15, 23, 42, 0.74);
|
|
12288
|
+
color: #e2e8f0;
|
|
12289
|
+
font-size: 9px;
|
|
12290
|
+
font-weight: 900;
|
|
12291
|
+
line-height: 1;
|
|
12292
|
+
overflow: hidden;
|
|
12293
|
+
text-overflow: ellipsis;
|
|
12294
|
+
white-space: nowrap;
|
|
12295
|
+
`;
|
|
12296
|
+
preview.appendChild(badge);
|
|
12297
|
+
}
|
|
12298
|
+
card.appendChild(preview);
|
|
12299
|
+
|
|
12300
|
+
const cardHeader = document.createElement('div');
|
|
12301
|
+
cardHeader.style.cssText = 'display:flex;align-items:flex-start;gap:8px;min-width:0;';
|
|
12302
|
+
const dot = document.createElement('span');
|
|
12303
|
+
dot.style.cssText = `
|
|
12304
|
+
flex: 0 0 auto;
|
|
12305
|
+
width: 9px;
|
|
12306
|
+
height: 9px;
|
|
12307
|
+
margin-top: 4px;
|
|
12308
|
+
border-radius: 999px;
|
|
12309
|
+
background: ${connectedDevice ? '#10b981' : '#94a3b8'};
|
|
12310
|
+
box-shadow: 0 0 0 4px ${connectedDevice ? 'rgba(16,185,129,0.14)' : 'rgba(148,163,184,0.14)'};
|
|
12311
|
+
`;
|
|
12312
|
+
|
|
12313
|
+
const titleBox = document.createElement('div');
|
|
12314
|
+
titleBox.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:2px;';
|
|
12315
|
+
const title = document.createElement('strong');
|
|
12316
|
+
title.textContent = name;
|
|
12317
|
+
title.title = name;
|
|
12318
|
+
title.style.cssText = `
|
|
12319
|
+
color: #0f172a;
|
|
12320
|
+
font-size: 13px;
|
|
12321
|
+
font-weight: 900;
|
|
12322
|
+
line-height: 1.2;
|
|
12323
|
+
overflow: hidden;
|
|
12324
|
+
text-overflow: ellipsis;
|
|
12325
|
+
white-space: nowrap;
|
|
12326
|
+
letter-spacing: 0;
|
|
12327
|
+
`;
|
|
12328
|
+
const subtitle = document.createElement('span');
|
|
12329
|
+
subtitle.textContent = release ? `${platform} ${release}` : platform;
|
|
12330
|
+
subtitle.title = subtitle.textContent;
|
|
12331
|
+
subtitle.style.cssText = `
|
|
12332
|
+
color: #64748b;
|
|
12333
|
+
font-size: 11px;
|
|
12334
|
+
line-height: 1.2;
|
|
12335
|
+
overflow: hidden;
|
|
12336
|
+
text-overflow: ellipsis;
|
|
12337
|
+
white-space: nowrap;
|
|
12338
|
+
letter-spacing: 0;
|
|
12339
|
+
`;
|
|
12340
|
+
titleBox.appendChild(title);
|
|
12341
|
+
titleBox.appendChild(subtitle);
|
|
12342
|
+
cardHeader.appendChild(dot);
|
|
12343
|
+
cardHeader.appendChild(titleBox);
|
|
12344
|
+
card.appendChild(cardHeader);
|
|
12345
|
+
|
|
12346
|
+
const metrics = document.createElement('div');
|
|
12347
|
+
metrics.style.cssText = `
|
|
12348
|
+
display: grid;
|
|
12349
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
12350
|
+
gap: 6px;
|
|
12351
|
+
`;
|
|
12352
|
+
const addMetric = (label, value) => {
|
|
12353
|
+
const metric = document.createElement('div');
|
|
12354
|
+
metric.style.cssText = `
|
|
12355
|
+
min-width: 0;
|
|
12356
|
+
padding: 6px 7px;
|
|
12357
|
+
border-radius: 7px;
|
|
12358
|
+
background: rgba(241, 245, 249, 0.84);
|
|
12359
|
+
`;
|
|
12360
|
+
const labelEl = document.createElement('div');
|
|
12361
|
+
labelEl.textContent = label;
|
|
12362
|
+
labelEl.style.cssText = 'color:#64748b;font-size:9px;font-weight:800;letter-spacing:0;text-transform:uppercase;';
|
|
12363
|
+
const valueEl = document.createElement('div');
|
|
12364
|
+
valueEl.textContent = value;
|
|
12365
|
+
valueEl.title = value;
|
|
12366
|
+
valueEl.style.cssText = 'color:#0f172a;font-size:12px;font-weight:900;letter-spacing:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
|
|
12367
|
+
metric.appendChild(labelEl);
|
|
12368
|
+
metric.appendChild(valueEl);
|
|
12369
|
+
metrics.appendChild(metric);
|
|
12370
|
+
};
|
|
12371
|
+
addMetric('Seen', formatRemoteFleetAge(device?.lastSeenAt || device?.LastSeenAt));
|
|
12372
|
+
addMetric('Uptime', formatRemoteFleetDuration(device?.uptimeSec ?? device?.UptimeSec));
|
|
12373
|
+
addMetric('Mem', formatRemoteFleetPercent(device?.usedMemRatio ?? device?.UsedMemRatio));
|
|
12374
|
+
addMetric('Load', formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2));
|
|
12375
|
+
card.appendChild(metrics);
|
|
12376
|
+
|
|
12377
|
+
const actions = document.createElement('div');
|
|
12378
|
+
actions.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;';
|
|
12379
|
+
const status = document.createElement('span');
|
|
12380
|
+
status.textContent = connectedDevice ? 'Connected' : 'Offline';
|
|
12381
|
+
status.style.cssText = `
|
|
12382
|
+
min-width: 0;
|
|
12383
|
+
color: ${connectedDevice ? '#047857' : '#64748b'};
|
|
12384
|
+
font-size: 11px;
|
|
12385
|
+
font-weight: 900;
|
|
12386
|
+
overflow: hidden;
|
|
12387
|
+
text-overflow: ellipsis;
|
|
12388
|
+
white-space: nowrap;
|
|
12389
|
+
`;
|
|
12390
|
+
actions.appendChild(status);
|
|
12391
|
+
if (connectedDevice && deviceId) {
|
|
12392
|
+
if (thumbnailEnabled) {
|
|
12393
|
+
const thumbnailButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
|
|
12394
|
+
thumbnailButton.dataset.deviceId = deviceId;
|
|
12395
|
+
thumbnailButton.style.height = '24px';
|
|
12396
|
+
thumbnailButton.style.fontSize = '10px';
|
|
12397
|
+
actions.appendChild(thumbnailButton);
|
|
12398
|
+
}
|
|
12399
|
+
const pingButton = createRemoteFleetButton('Ping', 'Ping device', 'ping-device');
|
|
12400
|
+
pingButton.dataset.deviceId = deviceId;
|
|
12401
|
+
pingButton.style.height = '24px';
|
|
12402
|
+
pingButton.style.fontSize = '10px';
|
|
12403
|
+
actions.appendChild(pingButton);
|
|
12404
|
+
}
|
|
12405
|
+
card.appendChild(actions);
|
|
12406
|
+
grid.appendChild(card);
|
|
12407
|
+
});
|
|
12408
|
+
}
|
|
12409
|
+
|
|
12410
|
+
bodyView.appendChild(grid);
|
|
12411
|
+
|
|
12412
|
+
const footer = document.createElement('div');
|
|
12413
|
+
footer.textContent = `Endpoint ${endpoint} · refreshed ${formatRemoteFleetAge(refreshedAt)}`;
|
|
12414
|
+
footer.style.cssText = `
|
|
12415
|
+
flex: 0 0 auto;
|
|
12416
|
+
color: #64748b;
|
|
12417
|
+
font-size: 11px;
|
|
12418
|
+
font-weight: 700;
|
|
12419
|
+
line-height: 1.2;
|
|
12420
|
+
overflow: hidden;
|
|
12421
|
+
text-overflow: ellipsis;
|
|
12422
|
+
white-space: nowrap;
|
|
12423
|
+
`;
|
|
12424
|
+
bodyView.appendChild(footer);
|
|
12425
|
+
|
|
12426
|
+
copyButton.addEventListener('click', event => {
|
|
12427
|
+
event.preventDefault();
|
|
12428
|
+
event.stopPropagation();
|
|
12429
|
+
navigator.clipboard?.writeText?.(command).catch(() => { });
|
|
12430
|
+
});
|
|
12431
|
+
|
|
12432
|
+
refreshButton.addEventListener('click', async event => {
|
|
12433
|
+
event.preventDefault();
|
|
12434
|
+
event.stopPropagation();
|
|
12435
|
+
refreshButton.disabled = true;
|
|
12436
|
+
const result = await invokeDotNetAsync('RefreshRemoteFleetMonitorNodeFromJs', nodeId);
|
|
12437
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12438
|
+
refreshButton.disabled = false;
|
|
12439
|
+
});
|
|
12440
|
+
|
|
12441
|
+
grid.querySelectorAll('[data-remote-fleet-action="ping-device"]').forEach(button => {
|
|
12442
|
+
button.addEventListener('click', async event => {
|
|
12443
|
+
event.preventDefault();
|
|
12444
|
+
event.stopPropagation();
|
|
12445
|
+
button.disabled = true;
|
|
12446
|
+
const result = await invokeDotNetAsync('PingRemoteFleetDeviceFromJs', nodeId, button.dataset.deviceId || '');
|
|
12447
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12448
|
+
button.disabled = false;
|
|
12449
|
+
});
|
|
12450
|
+
});
|
|
12451
|
+
|
|
12452
|
+
grid.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').forEach(button => {
|
|
12453
|
+
button.addEventListener('click', async event => {
|
|
12454
|
+
event.preventDefault();
|
|
12455
|
+
event.stopPropagation();
|
|
12456
|
+
button.disabled = true;
|
|
12457
|
+
const result = await invokeDotNetAsync('RequestRemoteFleetThumbnailFromJs', nodeId, button.dataset.deviceId || '');
|
|
12458
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
12459
|
+
button.disabled = false;
|
|
12460
|
+
});
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
12463
|
+
|
|
11843
12464
|
function bindMemoNodeEvents(container, nodeModel) {
|
|
11844
12465
|
if (!container || !nodeModel) return;
|
|
11845
12466
|
ensureMindCanvasAgentConsoleResize(container, nodeModel);
|
|
@@ -12071,11 +12692,12 @@
|
|
|
12071
12692
|
const agentStyledMemo = isAgentStyledMemoNode(nodeModel);
|
|
12072
12693
|
const mindCanvasAgent = isMindCanvasAgentNode(nodeModel);
|
|
12073
12694
|
const businessAutomation = isBusinessAutomationNode(nodeModel);
|
|
12695
|
+
const remoteFleet = isRemoteFleetMonitorNode(nodeModel);
|
|
12074
12696
|
const externalChrome = allowsMemoExternalChrome(nodeModel);
|
|
12075
12697
|
|
|
12076
12698
|
const container = document.createElement('div');
|
|
12077
12699
|
container.id = `node-${nodeModel.id}`;
|
|
12078
|
-
container.className = `map-node css3d-dynamic-node map-node-memo${agentStyledMemo ? ' map-node-agent' : ''}${businessAutomation ? ' map-node-automation' : ''}`;
|
|
12700
|
+
container.className = `map-node css3d-dynamic-node map-node-memo${agentStyledMemo ? ' map-node-agent' : ''}${businessAutomation ? ' map-node-automation' : ''}${remoteFleet ? ' map-node-remote-fleet' : ''}`;
|
|
12079
12701
|
container.dataset.nodeId = nodeModel.id;
|
|
12080
12702
|
container.dataset.semanticType = getNodeSemanticType(nodeModel);
|
|
12081
12703
|
container.dataset.agentConsoleOpen = isMindCanvasAgentConsoleOpen(nodeModel) ? 'true' : 'false';
|
|
@@ -15214,10 +15836,12 @@
|
|
|
15214
15836
|
const iconKey = getMemoIconKey(nodeModel);
|
|
15215
15837
|
const agentStyledMemo = isAgentStyledMemoNode(nodeModel);
|
|
15216
15838
|
const businessAutomation = isBusinessAutomationNode(nodeModel);
|
|
15839
|
+
const remoteFleet = isRemoteFleetMonitorNode(nodeModel);
|
|
15217
15840
|
const externalChrome = allowsMemoExternalChrome(nodeModel);
|
|
15218
15841
|
|
|
15219
15842
|
memoEl.classList.toggle('map-node-agent', agentStyledMemo);
|
|
15220
15843
|
memoEl.classList.toggle('map-node-automation', businessAutomation);
|
|
15844
|
+
memoEl.classList.toggle('map-node-remote-fleet', remoteFleet);
|
|
15221
15845
|
memoEl.dataset.semanticType = getNodeSemanticType(nodeModel);
|
|
15222
15846
|
memoEl.dataset.agentPlanOpen = getRenderedMindCanvasAgentPlanOpen(memoEl, nodeModel) ? 'true' : 'false';
|
|
15223
15847
|
memoEl.dataset.agentConsoleOpen = getRenderedMindCanvasAgentConsoleOpen(memoEl, nodeModel) ? 'true' : 'false';
|