@mindexec/cli 0.2.157 → 0.2.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -585,15 +585,15 @@ function encodeRemoteBinaryFrame(metadata, payload = new Uint8Array([0xff, 0xd8,
|
|
|
585
585
|
return buffer;
|
|
586
586
|
}
|
|
587
587
|
|
|
588
|
-
function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTaskBatches = [], lastError = '') {
|
|
588
|
+
function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTaskBatches = [], lastError = '', nodeId = 'remote-fleet-render-smoke') {
|
|
589
589
|
const connected = devices.filter(device => device.Connected).length;
|
|
590
590
|
const endpoint = hubStatus.agentEndpoint || '127.0.0.1:5197';
|
|
591
591
|
const pairToken = hubStatus.pairToken || 'render-smoke-token';
|
|
592
592
|
const hostTargetState = hubStatus.hostTargetActive
|
|
593
|
-
? (hubStatus.hostTargetNodeId ===
|
|
593
|
+
? (hubStatus.hostTargetNodeId === nodeId ? 'hosting' : 'other-monitor')
|
|
594
594
|
: 'inactive';
|
|
595
595
|
return {
|
|
596
|
-
id:
|
|
596
|
+
id: nodeId,
|
|
597
597
|
contentType: 'templateLauncher',
|
|
598
598
|
metadata: {
|
|
599
599
|
SemanticType: 'RemoteFleetMonitor',
|
|
@@ -625,10 +625,10 @@ function buildMonitorNode(devices, hubStatus, latestTaskBatch = null, recentTask
|
|
|
625
625
|
};
|
|
626
626
|
}
|
|
627
627
|
|
|
628
|
-
function createRemoteFleetTemplateShell(document, focusDeviceId = '') {
|
|
628
|
+
function createRemoteFleetTemplateShell(document, focusDeviceId = '', nodeId = 'remote-fleet-render-smoke') {
|
|
629
629
|
const nodeShell = document.createElement('div');
|
|
630
630
|
nodeShell.setAttribute('class', 'map-node-template-card map-node-remote-fleet');
|
|
631
|
-
nodeShell.dataset.nodeId =
|
|
631
|
+
nodeShell.dataset.nodeId = nodeId;
|
|
632
632
|
const shell = document.createElement('div');
|
|
633
633
|
shell.setAttribute('class', 'template-card__shell template-card__shell--remote-fleet');
|
|
634
634
|
const header = document.createElement('div');
|
|
@@ -642,7 +642,7 @@ function createRemoteFleetTemplateShell(document, focusDeviceId = '') {
|
|
|
642
642
|
header.appendChild(titleWrap);
|
|
643
643
|
const bodyView = document.createElement('div');
|
|
644
644
|
bodyView.setAttribute('class', 'template-card__remote-fleet-body map-node-remote-fleet__body');
|
|
645
|
-
bodyView.dataset.nodeId =
|
|
645
|
+
bodyView.dataset.nodeId = nodeId;
|
|
646
646
|
bodyView.dataset.remoteFleetAutoMonitor = 'false';
|
|
647
647
|
if (focusDeviceId) {
|
|
648
648
|
bodyView.dataset.remoteFleetFocusDeviceId = focusDeviceId;
|
|
@@ -1043,6 +1043,7 @@ try {
|
|
|
1043
1043
|
binaryDrawCalls.some(call => call.op === 'clearRect' || call.op === 'fillRect'),
|
|
1044
1044
|
false,
|
|
1045
1045
|
'binary Blob frame paint must not clear/fill the canvas before drawing');
|
|
1046
|
+
const binaryCanvasBeforeRerender = binaryCanvas;
|
|
1046
1047
|
|
|
1047
1048
|
const liveOverlayCard = document.createElement('div');
|
|
1048
1049
|
liveOverlayCard.dataset.nodeId = 'remote-fleet-render-smoke';
|
|
@@ -1105,7 +1106,42 @@ try {
|
|
|
1105
1106
|
?.querySelector('[data-remote-fleet-device-preview="tile"]');
|
|
1106
1107
|
assert.ok(rerenderedPatchPreview);
|
|
1107
1108
|
assert.equal(rerenderedPatchPreview.dataset.remoteFleetFrameSeq, String(websocketFrameSeq));
|
|
1108
|
-
|
|
1109
|
+
const rerenderedPatchCanvas = rerenderedPatchPreview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]');
|
|
1110
|
+
assert.equal(rerenderedPatchCanvas?.style.display, 'block');
|
|
1111
|
+
assert.equal(
|
|
1112
|
+
rerenderedPatchCanvas,
|
|
1113
|
+
binaryCanvasBeforeRerender,
|
|
1114
|
+
'MDM rerender should reuse the already-painted canvas instead of flashing a blank replacement');
|
|
1115
|
+
|
|
1116
|
+
const otherBoardNodeId = 'remote-fleet-render-smoke-other-board';
|
|
1117
|
+
const { nodeShell: otherBoardShell, bodyView: otherBoardBodyView } =
|
|
1118
|
+
createRemoteFleetTemplateShell(document, focusedDevice.DeviceId, otherBoardNodeId);
|
|
1119
|
+
document.body.appendChild(otherBoardShell);
|
|
1120
|
+
manager.renderRemoteFleetMonitorForTest(
|
|
1121
|
+
otherBoardBodyView,
|
|
1122
|
+
buildMonitorNode(
|
|
1123
|
+
devices,
|
|
1124
|
+
hub.getStatus({ includeSecrets: true }),
|
|
1125
|
+
latestTaskBatch,
|
|
1126
|
+
recentTaskBatches,
|
|
1127
|
+
'',
|
|
1128
|
+
otherBoardNodeId));
|
|
1129
|
+
await wait(30);
|
|
1130
|
+
const otherBoardPatchPreview = otherBoardBodyView
|
|
1131
|
+
.querySelector(`article[data-device-id="${patchTarget.DeviceId}"]`)
|
|
1132
|
+
?.querySelector('[data-remote-fleet-device-preview="tile"]');
|
|
1133
|
+
assert.ok(otherBoardPatchPreview);
|
|
1134
|
+
assert.equal(
|
|
1135
|
+
otherBoardPatchPreview.dataset.remoteFleetFrameSeq,
|
|
1136
|
+
String(websocketFrameSeq),
|
|
1137
|
+
'a different-board MDM should prime from the device-global latest binary frame cache');
|
|
1138
|
+
assert.equal(
|
|
1139
|
+
otherBoardPatchPreview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]')?.style.display,
|
|
1140
|
+
'block');
|
|
1141
|
+
assert.ok(diagnostics.runtimeTrace.some(event =>
|
|
1142
|
+
event.type === 'remote.frame.wsCachePrimed'
|
|
1143
|
+
&& event.nodeId === otherBoardNodeId));
|
|
1144
|
+
otherBoardShell.remove();
|
|
1109
1145
|
|
|
1110
1146
|
const controlCard = bodyView.querySelector(`article[data-device-id="${patchTarget.DeviceId}"]`);
|
|
1111
1147
|
assert.ok(controlCard);
|
|
@@ -1404,6 +1440,18 @@ try {
|
|
|
1404
1440
|
assert.ok(deviceBody.querySelector('[data-remote-fleet-action="task-device"]'));
|
|
1405
1441
|
assert.ok(deviceBody.textContent.includes('Task timed out waiting for the agent response.'));
|
|
1406
1442
|
assert.ok(deviceBody.textContent.includes(focusedDevice.DeviceId));
|
|
1443
|
+
const controlCanvasBeforeRerender = deviceControlCanvas;
|
|
1444
|
+
manager.renderRemoteFleetDeviceForTest(deviceBody, buildDeviceNode(focusedDevice, hub.getStatus()));
|
|
1445
|
+
await wait(30);
|
|
1446
|
+
const controlCanvasAfterRerender = deviceBody.querySelector('[data-remote-fleet-control-node-canvas="true"]');
|
|
1447
|
+
assert.equal(
|
|
1448
|
+
controlCanvasAfterRerender,
|
|
1449
|
+
controlCanvasBeforeRerender,
|
|
1450
|
+
'RemoteFleetDevice rerender should reuse the already-painted control canvas instead of flashing black');
|
|
1451
|
+
assert.equal(
|
|
1452
|
+
deviceBody.querySelector('[data-remote-fleet-control-node-placeholder="true"]'),
|
|
1453
|
+
null,
|
|
1454
|
+
'RemoteFleetDevice rerender should not cover a restored control canvas with a waiting placeholder');
|
|
1407
1455
|
|
|
1408
1456
|
nodeShell.remove();
|
|
1409
1457
|
emptyShell.remove();
|
|
@@ -13511,6 +13511,7 @@
|
|
|
13511
13511
|
const REMOTE_FLEET_BINARY_FRAME_CONNECT_GRACE_MS = 1600;
|
|
13512
13512
|
const REMOTE_FLEET_BINARY_FRAME_CACHE_MS = 10000;
|
|
13513
13513
|
const REMOTE_FLEET_BINARY_FRAME_CACHE_LIMIT = 240;
|
|
13514
|
+
const REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS = 8000;
|
|
13514
13515
|
const REMOTE_FLEET_AUTO_LIVE_START_MAX_PARALLEL = 8;
|
|
13515
13516
|
const REMOTE_FLEET_AUTO_LIVE_START_COOLDOWN_MS = 3000;
|
|
13516
13517
|
const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
|
|
@@ -13522,6 +13523,7 @@
|
|
|
13522
13523
|
const remoteFleetHostLeaseTimers = new Map();
|
|
13523
13524
|
const remoteFleetLocalHostTargets = new Map();
|
|
13524
13525
|
const remoteFleetBinaryFrameSessions = new Map();
|
|
13526
|
+
const remoteFleetLatestBinaryFrames = new Map();
|
|
13525
13527
|
const remoteFleetFrameBlobCache = new Map();
|
|
13526
13528
|
let remoteFleetBridgeStatusPromise = null;
|
|
13527
13529
|
let activeRemoteFleetControlPopup = null;
|
|
@@ -13948,36 +13950,85 @@
|
|
|
13948
13950
|
}
|
|
13949
13951
|
}
|
|
13950
13952
|
|
|
13953
|
+
function pruneRemoteFleetGlobalBinaryFrameCache() {
|
|
13954
|
+
const now = getRemoteFleetFrameNow();
|
|
13955
|
+
for (const [deviceId, entry] of remoteFleetLatestBinaryFrames) {
|
|
13956
|
+
if (!deviceId
|
|
13957
|
+
|| !entry
|
|
13958
|
+
|| !entry.frame
|
|
13959
|
+
|| now - Number(entry.seenAt || 0) > REMOTE_FLEET_BINARY_FRAME_CACHE_MS
|
|
13960
|
+
|| remoteFleetLatestBinaryFrames.size > REMOTE_FLEET_BINARY_FRAME_CACHE_LIMIT) {
|
|
13961
|
+
remoteFleetLatestBinaryFrames.delete(deviceId);
|
|
13962
|
+
}
|
|
13963
|
+
}
|
|
13964
|
+
}
|
|
13965
|
+
|
|
13951
13966
|
function rememberRemoteFleetBinaryFrame(session, frame) {
|
|
13952
13967
|
const deviceId = String(frame?.deviceId || '').trim();
|
|
13953
|
-
if (!
|
|
13968
|
+
if (!deviceId || !frame?._remoteFleetPayloadBlob) {
|
|
13954
13969
|
return;
|
|
13955
13970
|
}
|
|
13956
13971
|
|
|
13957
|
-
|
|
13958
|
-
session.latestBinaryFrames = new Map();
|
|
13959
|
-
}
|
|
13960
|
-
|
|
13961
|
-
pruneRemoteFleetBinaryFrameCache(session);
|
|
13962
|
-
session.latestBinaryFrames.set(deviceId, {
|
|
13972
|
+
const entry = {
|
|
13963
13973
|
seenAt: getRemoteFleetFrameNow(),
|
|
13964
13974
|
frame: cloneRemoteFleetFramePatch(frame)
|
|
13965
|
-
}
|
|
13975
|
+
};
|
|
13976
|
+
|
|
13977
|
+
if (session) {
|
|
13978
|
+
if (!(session.latestBinaryFrames instanceof Map)) {
|
|
13979
|
+
session.latestBinaryFrames = new Map();
|
|
13980
|
+
}
|
|
13981
|
+
|
|
13982
|
+
pruneRemoteFleetBinaryFrameCache(session);
|
|
13983
|
+
session.latestBinaryFrames.set(deviceId, {
|
|
13984
|
+
seenAt: entry.seenAt,
|
|
13985
|
+
frame: cloneRemoteFleetFramePatch(frame)
|
|
13986
|
+
});
|
|
13987
|
+
}
|
|
13988
|
+
|
|
13989
|
+
pruneRemoteFleetGlobalBinaryFrameCache();
|
|
13990
|
+
remoteFleetLatestBinaryFrames.set(deviceId, entry);
|
|
13966
13991
|
}
|
|
13967
13992
|
|
|
13968
13993
|
function applyRemoteFleetBinaryFrameCacheToBody(bodyView, session) {
|
|
13969
|
-
if (!bodyView
|
|
13994
|
+
if (!bodyView) {
|
|
13970
13995
|
return 0;
|
|
13971
13996
|
}
|
|
13972
13997
|
|
|
13973
|
-
|
|
13974
|
-
const
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13998
|
+
const patchesByDeviceId = new Map();
|
|
13999
|
+
const allowedDeviceIds = new Set(getRemoteFleetBinaryFrameDeviceIds(session));
|
|
14000
|
+
const addFrame = entry => {
|
|
14001
|
+
const frame = entry?.frame;
|
|
14002
|
+
const deviceId = String(frame?.deviceId || '').trim();
|
|
14003
|
+
if (!deviceId || !frame) {
|
|
14004
|
+
return;
|
|
14005
|
+
}
|
|
14006
|
+
|
|
14007
|
+
if (allowedDeviceIds.size > 0 && !allowedDeviceIds.has(deviceId)) {
|
|
14008
|
+
return;
|
|
14009
|
+
}
|
|
14010
|
+
|
|
14011
|
+
const existing = patchesByDeviceId.get(deviceId);
|
|
14012
|
+
const existingSeq = Number(existing?.frameSeq || 0);
|
|
14013
|
+
const nextSeq = Number(frame.frameSeq || 0);
|
|
14014
|
+
if (!existing || nextSeq >= existingSeq) {
|
|
14015
|
+
patchesByDeviceId.set(deviceId, cloneRemoteFleetFramePatch(frame));
|
|
14016
|
+
}
|
|
14017
|
+
};
|
|
14018
|
+
|
|
14019
|
+
if (session?.latestBinaryFrames instanceof Map && session.latestBinaryFrames.size > 0) {
|
|
14020
|
+
pruneRemoteFleetBinaryFrameCache(session);
|
|
14021
|
+
for (const entry of session.latestBinaryFrames.values()) {
|
|
14022
|
+
addFrame(entry);
|
|
13978
14023
|
}
|
|
13979
14024
|
}
|
|
13980
14025
|
|
|
14026
|
+
pruneRemoteFleetGlobalBinaryFrameCache();
|
|
14027
|
+
for (const entry of remoteFleetLatestBinaryFrames.values()) {
|
|
14028
|
+
addFrame(entry);
|
|
14029
|
+
}
|
|
14030
|
+
|
|
14031
|
+
const patches = Array.from(patchesByDeviceId.values());
|
|
13981
14032
|
return patches.length > 0
|
|
13982
14033
|
? applyRemoteFleetFramePatches(bodyView, patches)
|
|
13983
14034
|
: 0;
|
|
@@ -14256,6 +14307,69 @@
|
|
|
14256
14307
|
}, REMOTE_FLEET_BINARY_FRAME_WS_RECONNECT_MS);
|
|
14257
14308
|
}
|
|
14258
14309
|
|
|
14310
|
+
function cancelRemoteFleetBinaryFrameSessionRelease(session) {
|
|
14311
|
+
if (!session?.releaseTimer) {
|
|
14312
|
+
return;
|
|
14313
|
+
}
|
|
14314
|
+
|
|
14315
|
+
clearTimeout(session.releaseTimer);
|
|
14316
|
+
session.releaseTimer = null;
|
|
14317
|
+
}
|
|
14318
|
+
|
|
14319
|
+
function closeRemoteFleetBinaryFrameSession(session, reason = 'close') {
|
|
14320
|
+
if (!session) {
|
|
14321
|
+
return;
|
|
14322
|
+
}
|
|
14323
|
+
|
|
14324
|
+
cancelRemoteFleetBinaryFrameSessionRelease(session);
|
|
14325
|
+
if (session.reconnectTimer) {
|
|
14326
|
+
clearTimeout(session.reconnectTimer);
|
|
14327
|
+
session.reconnectTimer = null;
|
|
14328
|
+
}
|
|
14329
|
+
if (session.subscriptionTimer) {
|
|
14330
|
+
clearInterval(session.subscriptionTimer);
|
|
14331
|
+
session.subscriptionTimer = null;
|
|
14332
|
+
}
|
|
14333
|
+
if (session.ws) {
|
|
14334
|
+
try {
|
|
14335
|
+
session.ws.close();
|
|
14336
|
+
} catch {
|
|
14337
|
+
// Ignore WebSocket close failures.
|
|
14338
|
+
}
|
|
14339
|
+
session.ws = null;
|
|
14340
|
+
}
|
|
14341
|
+
remoteFleetBinaryFrameSessions.delete(session.nodeId);
|
|
14342
|
+
window.RuntimeTrace?.emit?.('remote.frame.wsSessionClosed', {
|
|
14343
|
+
nodeId: session.nodeId,
|
|
14344
|
+
reason
|
|
14345
|
+
});
|
|
14346
|
+
}
|
|
14347
|
+
|
|
14348
|
+
function scheduleRemoteFleetBinaryFrameSessionRelease(session, reason = 'idle') {
|
|
14349
|
+
if (!session
|
|
14350
|
+
|| session.releaseTimer
|
|
14351
|
+
|| session.bodies.size > 0
|
|
14352
|
+
|| (session.controlSessions instanceof Set && session.controlSessions.size > 0)) {
|
|
14353
|
+
return;
|
|
14354
|
+
}
|
|
14355
|
+
|
|
14356
|
+
session.releaseTimer = setTimeout(() => {
|
|
14357
|
+
session.releaseTimer = null;
|
|
14358
|
+
if (session.bodies.size > 0
|
|
14359
|
+
|| (session.controlSessions instanceof Set && session.controlSessions.size > 0)) {
|
|
14360
|
+
return;
|
|
14361
|
+
}
|
|
14362
|
+
|
|
14363
|
+
closeRemoteFleetBinaryFrameSession(session, reason);
|
|
14364
|
+
}, REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS);
|
|
14365
|
+
session.releaseTimer?.unref?.();
|
|
14366
|
+
window.RuntimeTrace?.emit?.('remote.frame.wsSessionIdle', {
|
|
14367
|
+
nodeId: session.nodeId,
|
|
14368
|
+
reason,
|
|
14369
|
+
ttlMs: REMOTE_FLEET_BINARY_FRAME_SESSION_IDLE_MS
|
|
14370
|
+
});
|
|
14371
|
+
}
|
|
14372
|
+
|
|
14259
14373
|
async function openRemoteFleetBinaryFrameSocket(session) {
|
|
14260
14374
|
if (!session || (session.bodies.size === 0 && (!session.controlSessions || session.controlSessions.size === 0)) || typeof WebSocket !== 'function') {
|
|
14261
14375
|
return false;
|
|
@@ -14305,7 +14419,17 @@
|
|
|
14305
14419
|
|
|
14306
14420
|
parseRemoteFleetBinaryFrameMessage(event.data)
|
|
14307
14421
|
.then(frame => {
|
|
14308
|
-
if (!frame
|
|
14422
|
+
if (!frame) {
|
|
14423
|
+
return;
|
|
14424
|
+
}
|
|
14425
|
+
|
|
14426
|
+
const hasFrameConsumers =
|
|
14427
|
+
session.bodies.size > 0
|
|
14428
|
+
|| (session.controlSessions instanceof Set && session.controlSessions.size > 0);
|
|
14429
|
+
if (!hasFrameConsumers) {
|
|
14430
|
+
const frameAt = getRemoteFleetFrameNow();
|
|
14431
|
+
session.lastBinaryFrameAt = frameAt;
|
|
14432
|
+
rememberRemoteFleetBinaryFrame(session, frame);
|
|
14309
14433
|
if (frame?._remoteFleetObjectUrl && typeof URL !== 'undefined' && typeof URL.revokeObjectURL === 'function') {
|
|
14310
14434
|
URL.revokeObjectURL(frame._remoteFleetObjectUrl);
|
|
14311
14435
|
}
|
|
@@ -14408,6 +14532,7 @@
|
|
|
14408
14532
|
bodies: new Set(),
|
|
14409
14533
|
ws: null,
|
|
14410
14534
|
reconnectTimer: null,
|
|
14535
|
+
releaseTimer: null,
|
|
14411
14536
|
subscriptionTimer: null,
|
|
14412
14537
|
subscriptionKey: '',
|
|
14413
14538
|
latestBinaryFrames: new Map(),
|
|
@@ -14417,6 +14542,7 @@
|
|
|
14417
14542
|
remoteFleetBinaryFrameSessions.set(nodeId, session);
|
|
14418
14543
|
}
|
|
14419
14544
|
|
|
14545
|
+
cancelRemoteFleetBinaryFrameSessionRelease(session);
|
|
14420
14546
|
session.bodies.add(bodyView);
|
|
14421
14547
|
session.getDeviceIds = getDeviceIds;
|
|
14422
14548
|
session.liveOptions = {
|
|
@@ -14428,7 +14554,14 @@
|
|
|
14428
14554
|
mode: String(options.mode || 'remote-fast')
|
|
14429
14555
|
};
|
|
14430
14556
|
bodyView._remoteFleetBinaryFrameSession = session;
|
|
14431
|
-
applyRemoteFleetBinaryFrameCacheToBody(bodyView, session);
|
|
14557
|
+
const primedFrames = applyRemoteFleetBinaryFrameCacheToBody(bodyView, session);
|
|
14558
|
+
if (primedFrames > 0) {
|
|
14559
|
+
bodyView._remoteFleetLastBinaryFrameAt = getRemoteFleetFrameNow();
|
|
14560
|
+
window.RuntimeTrace?.emit?.('remote.frame.wsCachePrimed', {
|
|
14561
|
+
nodeId,
|
|
14562
|
+
count: primedFrames
|
|
14563
|
+
});
|
|
14564
|
+
}
|
|
14432
14565
|
openRemoteFleetBinaryFrameSocket(session).catch(() => undefined);
|
|
14433
14566
|
return true;
|
|
14434
14567
|
}
|
|
@@ -14446,23 +14579,7 @@
|
|
|
14446
14579
|
return;
|
|
14447
14580
|
}
|
|
14448
14581
|
|
|
14449
|
-
|
|
14450
|
-
clearTimeout(session.reconnectTimer);
|
|
14451
|
-
session.reconnectTimer = null;
|
|
14452
|
-
}
|
|
14453
|
-
if (session.subscriptionTimer) {
|
|
14454
|
-
clearInterval(session.subscriptionTimer);
|
|
14455
|
-
session.subscriptionTimer = null;
|
|
14456
|
-
}
|
|
14457
|
-
if (session.ws) {
|
|
14458
|
-
try {
|
|
14459
|
-
session.ws.close();
|
|
14460
|
-
} catch {
|
|
14461
|
-
// Ignore WebSocket close failures.
|
|
14462
|
-
}
|
|
14463
|
-
session.ws = null;
|
|
14464
|
-
}
|
|
14465
|
-
remoteFleetBinaryFrameSessions.delete(session.nodeId);
|
|
14582
|
+
scheduleRemoteFleetBinaryFrameSessionRelease(session, 'body-detached');
|
|
14466
14583
|
}
|
|
14467
14584
|
|
|
14468
14585
|
function requestRemoteFleetFrameLoopFrame(callback) {
|
|
@@ -14725,6 +14842,104 @@
|
|
|
14725
14842
|
return canvas;
|
|
14726
14843
|
}
|
|
14727
14844
|
|
|
14845
|
+
function collectRemoteFleetPreviewFrameSurfaces(root) {
|
|
14846
|
+
const surfaces = new Map();
|
|
14847
|
+
if (!root?.querySelectorAll) {
|
|
14848
|
+
return surfaces;
|
|
14849
|
+
}
|
|
14850
|
+
|
|
14851
|
+
root.querySelectorAll('[data-remote-fleet-device-preview][data-device-id]').forEach(preview => {
|
|
14852
|
+
const deviceId = String(preview.dataset.deviceId || '').trim();
|
|
14853
|
+
if (!deviceId) {
|
|
14854
|
+
return;
|
|
14855
|
+
}
|
|
14856
|
+
|
|
14857
|
+
const canvas = preview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]');
|
|
14858
|
+
const image = preview.querySelector('img[data-remote-fleet-frame-image="true"]');
|
|
14859
|
+
surfaces.set(deviceId, {
|
|
14860
|
+
canvas,
|
|
14861
|
+
image,
|
|
14862
|
+
kind: String(preview.dataset.remoteFleetFrameKind || canvas?.dataset?.remoteFleetFrameKind || image?.dataset?.remoteFleetFrameKind || '').trim().toLowerCase(),
|
|
14863
|
+
seq: Number(preview.dataset.remoteFleetFrameSeq || canvas?.dataset?.remoteFleetFrameSeq || image?.dataset?.remoteFleetFrameSeq || 0) || 0,
|
|
14864
|
+
url: String(preview.dataset.remoteFleetFrameUrl || canvas?.dataset?.remoteFleetFrameUrl || image?.dataset?.remoteFleetFrameUrl || '').trim()
|
|
14865
|
+
});
|
|
14866
|
+
});
|
|
14867
|
+
|
|
14868
|
+
return surfaces;
|
|
14869
|
+
}
|
|
14870
|
+
|
|
14871
|
+
function restoreRemoteFleetPreviewFrameSurface(preview, surfaces, deviceId) {
|
|
14872
|
+
const id = String(deviceId || preview?.dataset?.deviceId || '').trim();
|
|
14873
|
+
const surface = id && surfaces instanceof Map ? surfaces.get(id) : null;
|
|
14874
|
+
if (!preview || !surface) {
|
|
14875
|
+
return false;
|
|
14876
|
+
}
|
|
14877
|
+
|
|
14878
|
+
const existingSeq = Number(surface.seq || 0) || 0;
|
|
14879
|
+
const nextSeq = Number(preview.dataset.remoteFleetFrameSeq || 0) || 0;
|
|
14880
|
+
if (existingSeq <= 0 || (nextSeq > 0 && existingSeq < nextSeq)) {
|
|
14881
|
+
return false;
|
|
14882
|
+
}
|
|
14883
|
+
|
|
14884
|
+
const currentCanvas = preview.querySelector('canvas[data-remote-fleet-frame-canvas="true"]');
|
|
14885
|
+
if (surface.canvas && surface.canvas !== currentCanvas) {
|
|
14886
|
+
currentCanvas?.remove?.();
|
|
14887
|
+
surface.canvas.dataset.remoteFleetFrameKind = surface.kind || preview.dataset.remoteFleetFrameKind || 'live';
|
|
14888
|
+
surface.canvas.dataset.remoteFleetFrameSeq = String(existingSeq);
|
|
14889
|
+
surface.canvas.dataset.remoteFleetFrameUrl = surface.url || '';
|
|
14890
|
+
surface.canvas.style.display = 'block';
|
|
14891
|
+
if (typeof preview.insertBefore === 'function') {
|
|
14892
|
+
preview.insertBefore(surface.canvas, preview.firstChild || null);
|
|
14893
|
+
} else {
|
|
14894
|
+
preview.appendChild?.(surface.canvas);
|
|
14895
|
+
}
|
|
14896
|
+
}
|
|
14897
|
+
|
|
14898
|
+
const currentImage = preview.querySelector('img[data-remote-fleet-frame-image="true"]');
|
|
14899
|
+
if (surface.canvas) {
|
|
14900
|
+
preview.querySelector('[data-remote-fleet-screen-placeholder="true"]')?.remove?.();
|
|
14901
|
+
if (currentImage && currentImage !== surface.image) {
|
|
14902
|
+
currentImage.style.display = 'none';
|
|
14903
|
+
}
|
|
14904
|
+
if (surface.image && surface.image !== currentImage && surface.image.parentElement === preview) {
|
|
14905
|
+
surface.image.style.display = 'none';
|
|
14906
|
+
}
|
|
14907
|
+
} else if (surface.image && surface.image !== currentImage) {
|
|
14908
|
+
currentImage?.remove?.();
|
|
14909
|
+
surface.image.style.display = 'block';
|
|
14910
|
+
if (typeof preview.insertBefore === 'function') {
|
|
14911
|
+
preview.insertBefore(surface.image, preview.firstChild || null);
|
|
14912
|
+
} else {
|
|
14913
|
+
preview.appendChild?.(surface.image);
|
|
14914
|
+
}
|
|
14915
|
+
}
|
|
14916
|
+
|
|
14917
|
+
preview.dataset.remoteFleetFrameKind = surface.kind || preview.dataset.remoteFleetFrameKind || '';
|
|
14918
|
+
preview.dataset.remoteFleetFrameSeq = String(existingSeq);
|
|
14919
|
+
preview.dataset.remoteFleetFrameUrl = surface.url || '';
|
|
14920
|
+
preview.dataset.remoteFleetRestoredFrame = 'true';
|
|
14921
|
+
return true;
|
|
14922
|
+
}
|
|
14923
|
+
|
|
14924
|
+
function collectRemoteFleetControlNodeSurface(root) {
|
|
14925
|
+
const canvas = root?.querySelector?.('canvas[data-remote-fleet-control-node-canvas="true"]');
|
|
14926
|
+
if (!canvas) {
|
|
14927
|
+
return null;
|
|
14928
|
+
}
|
|
14929
|
+
|
|
14930
|
+
return {
|
|
14931
|
+
canvas,
|
|
14932
|
+
kind: String(canvas.dataset.remoteFleetFrameKind || 'live').trim().toLowerCase(),
|
|
14933
|
+
seq: Number(canvas.dataset.remoteFleetFrameSeq || 0) || 0,
|
|
14934
|
+
contentRect: canvas._remoteFleetContentRect || null
|
|
14935
|
+
};
|
|
14936
|
+
}
|
|
14937
|
+
|
|
14938
|
+
function hasRemoteFleetReusableControlSurface(surface) {
|
|
14939
|
+
return !!surface?.canvas
|
|
14940
|
+
&& (Number(surface.seq || 0) > 0 || !!surface.contentRect);
|
|
14941
|
+
}
|
|
14942
|
+
|
|
14728
14943
|
function syncRemoteFleetFrameCanvasSize(canvas) {
|
|
14729
14944
|
if (!canvas) {
|
|
14730
14945
|
return null;
|
|
@@ -14736,8 +14951,18 @@
|
|
|
14736
14951
|
const cssWidth = Math.max(1, Math.round(rect?.width || canvas.clientWidth || 320));
|
|
14737
14952
|
const cssHeight = Math.max(1, Math.round(rect?.height || canvas.clientHeight || 180));
|
|
14738
14953
|
const dpr = Math.max(1, Math.min(REMOTE_FLEET_FRAME_CANVAS_MAX_DPR, Number(window.devicePixelRatio || 1) || 1));
|
|
14739
|
-
|
|
14740
|
-
|
|
14954
|
+
let nextWidth = Math.max(1, Math.round(cssWidth * dpr));
|
|
14955
|
+
let nextHeight = Math.max(1, Math.round(cssHeight * dpr));
|
|
14956
|
+
const currentWidth = Number(canvas.width || 0) || 0;
|
|
14957
|
+
const currentHeight = Number(canvas.height || 0) || 0;
|
|
14958
|
+
const widthDelta = Math.abs(currentWidth - nextWidth);
|
|
14959
|
+
const heightDelta = Math.abs(currentHeight - nextHeight);
|
|
14960
|
+
if (currentWidth > 0 && widthDelta > 0 && widthDelta <= 2) {
|
|
14961
|
+
nextWidth = currentWidth;
|
|
14962
|
+
}
|
|
14963
|
+
if (currentHeight > 0 && heightDelta > 0 && heightDelta <= 2) {
|
|
14964
|
+
nextHeight = currentHeight;
|
|
14965
|
+
}
|
|
14741
14966
|
if (canvas.width !== nextWidth) {
|
|
14742
14967
|
canvas.width = nextWidth;
|
|
14743
14968
|
}
|
|
@@ -15829,23 +16054,7 @@
|
|
|
15829
16054
|
refreshRemoteFleetBinaryFrameSubscription(binarySession, true);
|
|
15830
16055
|
|
|
15831
16056
|
if (binarySession.bodies.size === 0 && binarySession.controlSessions.size === 0) {
|
|
15832
|
-
|
|
15833
|
-
clearTimeout(binarySession.reconnectTimer);
|
|
15834
|
-
binarySession.reconnectTimer = null;
|
|
15835
|
-
}
|
|
15836
|
-
if (binarySession.subscriptionTimer) {
|
|
15837
|
-
clearInterval(binarySession.subscriptionTimer);
|
|
15838
|
-
binarySession.subscriptionTimer = null;
|
|
15839
|
-
}
|
|
15840
|
-
if (binarySession.ws) {
|
|
15841
|
-
try {
|
|
15842
|
-
binarySession.ws.close();
|
|
15843
|
-
} catch {
|
|
15844
|
-
// Ignore WebSocket close failures.
|
|
15845
|
-
}
|
|
15846
|
-
binarySession.ws = null;
|
|
15847
|
-
}
|
|
15848
|
-
remoteFleetBinaryFrameSessions.delete(binarySession.nodeId);
|
|
16057
|
+
scheduleRemoteFleetBinaryFrameSessionRelease(binarySession, 'control-detached');
|
|
15849
16058
|
}
|
|
15850
16059
|
}
|
|
15851
16060
|
|
|
@@ -15869,7 +16078,12 @@
|
|
|
15869
16078
|
}
|
|
15870
16079
|
|
|
15871
16080
|
drawRemoteFleetFrameToCanvas(session.canvas, bitmap, 'contain');
|
|
16081
|
+
session.canvas.dataset.remoteFleetFrameKind = String(frame.kind || 'live').toLowerCase();
|
|
16082
|
+
session.canvas.dataset.remoteFleetFrameSeq = String(Number(frame.frameSeq || frame.FrameSeq || 0) || 0);
|
|
16083
|
+
session.canvas.dataset.remoteFleetFrameUrl = String(frame.frameUrl || '');
|
|
15872
16084
|
session.canvas.style.display = 'block';
|
|
16085
|
+
session.lastControlFrameSeq = Number(frame.frameSeq || frame.FrameSeq || session.lastControlFrameSeq || 0) || 0;
|
|
16086
|
+
session.lastControlFrameAt = Date.now();
|
|
15873
16087
|
session.placeholder?.remove?.();
|
|
15874
16088
|
session.placeholder = null;
|
|
15875
16089
|
if (typeof bitmap.close === 'function') {
|
|
@@ -15919,6 +16133,9 @@
|
|
|
15919
16133
|
}
|
|
15920
16134
|
|
|
15921
16135
|
drawRemoteFleetFrameToCanvas(session.canvas, bitmap, 'contain');
|
|
16136
|
+
session.canvas.dataset.remoteFleetFrameKind = String(frame.kind || 'live').toLowerCase();
|
|
16137
|
+
session.canvas.dataset.remoteFleetFrameSeq = String(frameSeq || 0);
|
|
16138
|
+
session.canvas.dataset.remoteFleetFrameUrl = String(frame.frameUrl || '');
|
|
15922
16139
|
session.canvas.style.display = 'block';
|
|
15923
16140
|
session.lastControlFrameAt = Date.now();
|
|
15924
16141
|
session.status.textContent = `Control ${REMOTE_FLEET_CONTROL_LIVE_FPS}fps`;
|
|
@@ -16870,6 +17087,7 @@
|
|
|
16870
17087
|
function renderRemoteFleetDevice(bodyView, nodeModel) {
|
|
16871
17088
|
if (!bodyView) return;
|
|
16872
17089
|
clearRemoteFleetTimers(bodyView, { keepBinaryFrameSocket: true });
|
|
17090
|
+
const preservedControlSurface = collectRemoteFleetControlNodeSurface(bodyView);
|
|
16873
17091
|
|
|
16874
17092
|
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
16875
17093
|
const device = parseRemoteFleetPinnedDevice(nodeModel);
|
|
@@ -17065,7 +17283,7 @@
|
|
|
17065
17283
|
touch-action: none;
|
|
17066
17284
|
outline: none;
|
|
17067
17285
|
`;
|
|
17068
|
-
const controlCanvas = document.createElement('canvas');
|
|
17286
|
+
const controlCanvas = preservedControlSurface?.canvas || document.createElement('canvas');
|
|
17069
17287
|
controlCanvas.dataset.remoteFleetControlNodeCanvas = 'true';
|
|
17070
17288
|
controlCanvas.style.cssText = `
|
|
17071
17289
|
position: absolute;
|
|
@@ -17125,7 +17343,10 @@
|
|
|
17125
17343
|
controlOverlay.appendChild(controlDot);
|
|
17126
17344
|
controlOverlay.appendChild(controlStatus);
|
|
17127
17345
|
controlStage.appendChild(controlCanvas);
|
|
17128
|
-
|
|
17346
|
+
const restoredControlSurface = hasRemoteFleetReusableControlSurface(preservedControlSurface);
|
|
17347
|
+
if (!restoredControlSurface) {
|
|
17348
|
+
controlStage.appendChild(controlPlaceholder);
|
|
17349
|
+
}
|
|
17129
17350
|
controlStage.appendChild(controlOverlay);
|
|
17130
17351
|
bodyView.appendChild(controlStage);
|
|
17131
17352
|
|
|
@@ -17137,7 +17358,17 @@
|
|
|
17137
17358
|
controlStage,
|
|
17138
17359
|
controlCanvas,
|
|
17139
17360
|
controlStatus,
|
|
17140
|
-
controlPlaceholder);
|
|
17361
|
+
restoredControlSurface ? null : controlPlaceholder);
|
|
17362
|
+
if (session && restoredControlSurface) {
|
|
17363
|
+
session.lastControlFrameSeq = Number(preservedControlSurface?.seq || session.lastControlFrameSeq || 0) || 0;
|
|
17364
|
+
session.lastControlFrameAt = Date.now();
|
|
17365
|
+
controlStatus.textContent = `Control ${REMOTE_FLEET_CONTROL_LIVE_FPS}fps`;
|
|
17366
|
+
window.RuntimeTrace?.emit?.('remote.control.surfaceRestored', {
|
|
17367
|
+
nodeId,
|
|
17368
|
+
deviceId,
|
|
17369
|
+
seq: session.lastControlFrameSeq
|
|
17370
|
+
});
|
|
17371
|
+
}
|
|
17141
17372
|
if (session && isRemoteFleetFrameSource(previewSource)) {
|
|
17142
17373
|
paintRemoteFleetControlFrame(session, {
|
|
17143
17374
|
deviceId,
|
|
@@ -17425,6 +17656,7 @@
|
|
|
17425
17656
|
function renderRemoteFleetMonitor(bodyView, nodeModel) {
|
|
17426
17657
|
if (!bodyView) return;
|
|
17427
17658
|
clearRemoteFleetTimers(bodyView, { keepBinaryFrameSocket: true });
|
|
17659
|
+
const preservedFrameSurfaces = collectRemoteFleetPreviewFrameSurfaces(bodyView);
|
|
17428
17660
|
|
|
17429
17661
|
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
17430
17662
|
const searchState = bodyView.dataset.remoteFleetSearch || '';
|
|
@@ -17979,6 +18211,7 @@
|
|
|
17979
18211
|
preview.appendChild(placeholder);
|
|
17980
18212
|
}
|
|
17981
18213
|
|
|
18214
|
+
restoreRemoteFleetPreviewFrameSurface(preview, preservedFrameSurfaces, getRemoteFleetDeviceId(device));
|
|
17982
18215
|
return preview;
|
|
17983
18216
|
};
|
|
17984
18217
|
|
package/wwwroot/index.html
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<title>MindExec | Run your ideas as AI task graphs</title>
|
|
8
8
|
<meta name="description" content="MindExec is an AI execution canvas for solo builders, researchers, developers, and creators. Start with free browser tools, then move serious work into saved MindCanvas projects." />
|
|
9
9
|
<base href="/" />
|
|
10
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260617-
|
|
11
|
-
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260617-
|
|
10
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260617-remote-frame-no-flash-v592" />
|
|
11
|
+
<link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260617-remote-frame-no-flash-v592" />
|
|
12
12
|
<!-- ?쇄뼹??Font Awesome (local) ?쇄뼹??-->
|
|
13
13
|
<link rel="stylesheet" href="_content/MindExecution.Shared/lib/font-awesome/css/all.min.css" />
|
|
14
14
|
<!-- ?꿎뼯??-->
|
|
@@ -579,7 +579,7 @@
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
const base = '_content/MindExecution.Shared/js/';
|
|
582
|
-
const scriptVersion = '20260617-
|
|
582
|
+
const scriptVersion = '20260617-remote-frame-no-flash-v592';
|
|
583
583
|
const scriptUrl = (script) => `${base}${script}?v=${scriptVersion}`;
|
|
584
584
|
console.log(`[Script Loader] Shared JS version: ${scriptVersion}`);
|
|
585
585
|
const criticalScripts = [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
self.assetsManifest = {
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "ikaqQxI3",
|
|
3
3
|
"assets": [
|
|
4
4
|
{
|
|
5
5
|
"hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"url": "_content/MindExecution.Shared/js/mind-map-core.js.backup"
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
"hash": "sha256-
|
|
89
|
+
"hash": "sha256-yUuxzH9Fpd62pPLMU5n0HuYjsJhKvC82LO5X5Uu/1gg=",
|
|
90
90
|
"url": "_content/MindExecution.Shared/js/mind-map-css3d-manager.js"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -834,7 +834,7 @@
|
|
|
834
834
|
"url": "image-manifest.json"
|
|
835
835
|
},
|
|
836
836
|
{
|
|
837
|
-
"hash": "sha256-
|
|
837
|
+
"hash": "sha256-8VAYYW89mBjF45WS2vA1+Hssmo6S30PB8AW03niSE2Q=",
|
|
838
838
|
"url": "index.html"
|
|
839
839
|
},
|
|
840
840
|
{
|