@mindexec/cli 0.2.145 → 0.2.147

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindexec/cli",
3
- "version": "0.2.145",
3
+ "version": "0.2.147",
4
4
  "description": "MindExec local runtime and bridge CLI",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -856,12 +856,41 @@ try {
856
856
  return { success: true };
857
857
  }
858
858
  };
859
- manager.setModuleForTest({ dotNetHelper });
859
+ const moduleRef = {
860
+ dotNetHelper,
861
+ nodeObjectsById: new Map(),
862
+ _visibleIds: new Set(['remote-fleet-render-smoke']),
863
+ _prevVisibleIds: new Set(['remote-fleet-render-smoke']),
864
+ _forceUpdateFrames: 0,
865
+ _textOverlayV2State: {
866
+ cards: new Map(),
867
+ passiveCandidateIds: new Set(['remote-fleet-render-smoke']),
868
+ layer: document.createElement('div')
869
+ },
870
+ requestAnimationWake(reason) {
871
+ this._lastAnimationWakeReason = reason;
872
+ this._animationWakeCount = Number(this._animationWakeCount || 0) + 1;
873
+ },
874
+ isAnimationLoopActive() {
875
+ return true;
876
+ }
877
+ };
878
+ manager.setModuleForTest(moduleRef);
860
879
 
861
880
  const { nodeShell, bodyView } = createRemoteFleetTemplateShell(document, focusedDevice.DeviceId);
862
881
  document.body.appendChild(nodeShell);
882
+ const monitorNode = buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches);
883
+ moduleRef.nodeObjectsById.set('remote-fleet-render-smoke', {
884
+ model: monitorNode,
885
+ currentType: 'CSS',
886
+ cssObject: {
887
+ visible: true,
888
+ element: nodeShell
889
+ }
890
+ });
891
+ document.body.appendChild(moduleRef._textOverlayV2State.layer);
863
892
 
864
- manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
893
+ manager.renderRemoteFleetMonitorForTest(bodyView, monitorNode);
865
894
 
866
895
  let cards = bodyView.querySelectorAll('article[data-device-id]');
867
896
  assert.equal(cards.length, connectedCount);
@@ -894,7 +923,13 @@ try {
894
923
  assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="task-device"]').length, 0);
895
924
  assert.equal(bodyView.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').length, 0);
896
925
  assert.equal(bodyView.querySelectorAll('[data-remote-fleet-frame-badge="true"]').length, 0);
897
- assert.ok(cards[0]?.querySelector('[data-remote-fleet-device-preview="tile"]'));
926
+ const firstPreview = cards[0]?.querySelector('[data-remote-fleet-device-preview="tile"]');
927
+ assert.ok(firstPreview);
928
+ assert.match(cards[0]?.style.cssText || '', /border-radius:\s*0/);
929
+ assert.match(firstPreview.style.cssText, /border-radius:\s*0/);
930
+ assert.match(cards[0]?.title || '', /Status:\s*(Connected|Offline)/);
931
+ assert.match(cards[0]?.title || '', /Seen:/);
932
+ assert.match(cards[0]?.title || '', /Uptime:/);
898
933
  assert.equal(/\b(Seen|Uptime|Mem|Load)\b/.test(cards[0]?.textContent || ''), false);
899
934
  assert.match(deviceGrid.style.cssText, /minmax\(132px,\s*1fr\)/);
900
935
  sizeControls.querySelector('[data-remote-fleet-action="monitor-size-up"]').dispatchEvent({ type: 'click' });
@@ -963,6 +998,16 @@ try {
963
998
  false,
964
999
  'binary Blob frame paint must not clear/fill the canvas before drawing');
965
1000
 
1001
+ const liveOverlayCard = document.createElement('div');
1002
+ liveOverlayCard.dataset.nodeId = 'remote-fleet-render-smoke';
1003
+ liveOverlayCard.dataset.sourceKind = 'live';
1004
+ liveOverlayCard.dataset.liveInteractive = 'true';
1005
+ liveOverlayCard.style.display = 'block';
1006
+ moduleRef._textOverlayV2State.cards.set('selection:remote-fleet-render-smoke', liveOverlayCard);
1007
+ moduleRef._forceUpdateFrames = 0;
1008
+ moduleRef._animationWakeCount = 0;
1009
+ moduleRef._lastAnimationWakeReason = '';
1010
+
966
1011
  await wait(20);
967
1012
  const liveWs = diagnostics.webSocketConnections[0];
968
1013
  assert.ok(liveWs, 'expected MDM render to open the binary frame WebSocket');
@@ -990,6 +1035,14 @@ try {
990
1035
  patchPreview.dataset.remoteFleetFrameSeq,
991
1036
  String(websocketFrameSeq),
992
1037
  JSON.stringify(diagnostics.runtimeTrace.slice(-12)));
1038
+ await wait(10);
1039
+ assert.ok(
1040
+ Number(moduleRef._forceUpdateFrames || 0) >= 2,
1041
+ 'binary live frame commits must wake the passive MDM overlay frame loop');
1042
+ assert.equal(moduleRef._lastAnimationWakeReason, 'remote-live-overlay-frame');
1043
+ assert.ok(
1044
+ diagnostics.runtimeTrace.some(event => event.type === 'remote.frame.overlayWake'),
1045
+ 'binary live frame commits must trace the MDM overlay wake path');
993
1046
 
994
1047
  manager.renderRemoteFleetMonitorForTest(bodyView, buildMonitorNode(devices, hub.getStatus({ includeSecrets: true }), latestTaskBatch, recentTaskBatches));
995
1048
  await wait(30);
@@ -1227,6 +1280,7 @@ try {
1227
1280
  const emptyScreens = emptyBody.querySelectorAll('[data-remote-fleet-empty-screen="true"]');
1228
1281
  assert.ok(emptyScreens.length >= 24, `expected empty monitor to fill space, got ${emptyScreens.length}`);
1229
1282
  assert.notEqual(emptyScreens.length, 6);
1283
+ assert.match(emptyScreens[0].style.cssText, /border-radius:\s*0/);
1230
1284
  assert.ok(Array.from(emptyBody.children).map(child =>
1231
1285
  Object.keys(child.dataset || {}).join(',') || child.tagName).join('|').endsWith('remoteFleetStatusRail'));
1232
1286
  assert.equal(emptyBody.textContent.includes('No devices connected yet.'), false);
@@ -1234,6 +1288,24 @@ try {
1234
1288
  assert.equal(emptyBody.textContent.includes('No screen'), false);
1235
1289
  assert.equal(emptyBody.textContent.includes('Endpoint 127.0.0.1'), false);
1236
1290
 
1291
+ const oneDevice = devices.find(device => device.Connected === true);
1292
+ assert.ok(oneDevice);
1293
+ const { nodeShell: oneShell, bodyView: oneBody } = createRemoteFleetTemplateShell(document);
1294
+ document.body.appendChild(oneShell);
1295
+ manager.renderRemoteFleetMonitorForTest(oneBody, buildMonitorNode([oneDevice], hub.getStatus({ includeSecrets: true })));
1296
+ const oneCards = oneBody.querySelectorAll('article[data-device-id]');
1297
+ assert.equal(oneCards.length, 1);
1298
+ const onePreview = oneCards[0]?.querySelector('[data-remote-fleet-device-preview="tile"]');
1299
+ assert.ok(onePreview);
1300
+ assert.match(oneCards[0]?.style.cssText || '', /border-radius:\s*0/);
1301
+ assert.match(onePreview.style.cssText, /border-radius:\s*0/);
1302
+ assert.match(oneCards[0]?.title || '', /Status:\s*Connected/);
1303
+ assert.match(oneCards[0]?.title || '', /Seen:/);
1304
+ assert.match(oneCards[0]?.title || '', /Uptime:/);
1305
+ const oneFillers = oneBody.querySelectorAll('[data-remote-fleet-empty-screen="true"]');
1306
+ assert.ok(oneFillers.length > 0, 'single connected monitor wall should keep blank screen placeholders');
1307
+ assert.match(oneFillers[0].style.cssText, /border-radius:\s*0/);
1308
+
1237
1309
  const { nodeShell: errorShell, bodyView: errorBody } = createRemoteFleetTemplateShell(document);
1238
1310
  document.body.appendChild(errorShell);
1239
1311
  manager.renderRemoteFleetMonitorForTest(errorBody, buildMonitorNode([], hub.getStatus({ includeSecrets: true }), null, [], 'RemoteHub is unavailable. Start LocalBridge and retry.'));
@@ -1258,6 +1330,7 @@ try {
1258
1330
 
1259
1331
  nodeShell.remove();
1260
1332
  emptyShell.remove();
1333
+ oneShell.remove();
1261
1334
  errorShell.remove();
1262
1335
  deviceBody.remove();
1263
1336
  await wait(2200);
@@ -13374,6 +13374,33 @@
13374
13374
  return Math.max(12, Math.min(80, columns * rows));
13375
13375
  }
13376
13376
 
13377
+ function createRemoteFleetEmptyScreenElement(metrics) {
13378
+ const screen = document.createElement('div');
13379
+ screen.dataset.remoteFleetEmptyScreen = 'true';
13380
+ screen.style.cssText = `
13381
+ width: 100%;
13382
+ height: auto;
13383
+ aspect-ratio: 16 / 9;
13384
+ min-height: ${metrics.emptyMinHeight}px;
13385
+ box-sizing: border-box;
13386
+ border-radius: 0;
13387
+ border: 1px solid rgba(203, 213, 225, 0.66);
13388
+ background: #ffffff;
13389
+ box-shadow: none;
13390
+ `;
13391
+ return screen;
13392
+ }
13393
+
13394
+ function appendRemoteFleetEmptyScreens(container, nodeModel, sizeState = 'cards', occupiedScreens = 0) {
13395
+ const metrics = getRemoteFleetTileMetrics(sizeState);
13396
+ const targetScreenCount = getRemoteFleetEmptyScreenCount(nodeModel, sizeState);
13397
+ const screenCount = Math.max(0, targetScreenCount - Math.max(0, Number(occupiedScreens) || 0));
13398
+ for (let index = 0; index < screenCount; index += 1) {
13399
+ container.appendChild(createRemoteFleetEmptyScreenElement(metrics));
13400
+ }
13401
+ return screenCount;
13402
+ }
13403
+
13377
13404
  function createRemoteFleetEmptyScreens(nodeModel, sizeState = 'cards') {
13378
13405
  const metrics = getRemoteFleetTileMetrics(sizeState);
13379
13406
  const shell = document.createElement('section');
@@ -13399,23 +13426,7 @@
13399
13426
  padding: 2px 4px 6px 4px;
13400
13427
  `;
13401
13428
 
13402
- const screenCount = getRemoteFleetEmptyScreenCount(nodeModel, sizeState);
13403
- for (let index = 0; index < screenCount; index += 1) {
13404
- const screen = document.createElement('div');
13405
- screen.dataset.remoteFleetEmptyScreen = 'true';
13406
- screen.style.cssText = `
13407
- width: 100%;
13408
- height: auto;
13409
- aspect-ratio: 16 / 9;
13410
- min-height: ${metrics.emptyMinHeight}px;
13411
- box-sizing: border-box;
13412
- border-radius: 8px;
13413
- border: 1px solid rgba(203, 213, 225, 0.66);
13414
- background: #ffffff;
13415
- box-shadow: none;
13416
- `;
13417
- shell.appendChild(screen);
13418
- }
13429
+ appendRemoteFleetEmptyScreens(shell, nodeModel, sizeState, 0);
13419
13430
 
13420
13431
  return shell;
13421
13432
  }
@@ -13877,6 +13888,145 @@
13877
13888
  : 0;
13878
13889
  }
13879
13890
 
13891
+ function getRemoteFleetBodyNodeId(bodyView) {
13892
+ return String(
13893
+ bodyView?.dataset?.nodeId
13894
+ || bodyView?.dataset?.remoteFleetMonitorNodeId
13895
+ || bodyView?.closest?.('.map-node-remote-fleet__body[data-node-id]')?.dataset?.nodeId
13896
+ || bodyView?.closest?.('[data-node-id]')?.dataset?.nodeId
13897
+ || ''
13898
+ ).trim();
13899
+ }
13900
+
13901
+ function getRemoteFleetBodyFromPreview(preview) {
13902
+ if (!preview) {
13903
+ return null;
13904
+ }
13905
+
13906
+ return preview.closest?.('.map-node-remote-fleet__body[data-node-id]')
13907
+ || preview.closest?.('.map-node-remote-fleet__body[data-remote-fleet-monitor-node-id]')
13908
+ || null;
13909
+ }
13910
+
13911
+ function wakeRemoteFleetModuleAnimation(module, reason) {
13912
+ if (!module) {
13913
+ return;
13914
+ }
13915
+
13916
+ if (typeof module.requestAnimationWake === 'function') {
13917
+ module.requestAnimationWake(reason);
13918
+ }
13919
+
13920
+ if (typeof module.isAnimationLoopActive === 'function'
13921
+ && module.isAnimationLoopActive() !== true
13922
+ && typeof module.animate === 'function') {
13923
+ module.animate();
13924
+ }
13925
+ }
13926
+
13927
+ function flushRemoteFleetLiveOverlayWake(module) {
13928
+ const state = module?._remoteFleetLiveOverlayWakeState || null;
13929
+ if (!module || !state || !(state.nodeIds instanceof Set)) {
13930
+ return;
13931
+ }
13932
+
13933
+ state.raf = 0;
13934
+ let woke = false;
13935
+ const overlayState = module._textOverlayV2State || null;
13936
+ state.nodeIds.forEach(nodeId => {
13937
+ const normalizedNodeId = String(nodeId || '').trim();
13938
+ if (!normalizedNodeId) {
13939
+ return;
13940
+ }
13941
+
13942
+ const entry = module.nodeObjectsById?.get?.(normalizedNodeId) || null;
13943
+ if (!entry?.model || !isRemoteFleetMonitorNode(entry.model)) {
13944
+ return;
13945
+ }
13946
+
13947
+ const card = overlayState?.cards?.get?.(`selection:${normalizedNodeId}`) || null;
13948
+ const hasLiveOverlayCard =
13949
+ !!card &&
13950
+ card.style?.display !== 'none' &&
13951
+ card.dataset?.sourceKind === 'live' &&
13952
+ card.dataset?.liveInteractive === 'true';
13953
+ const mayCreatePassiveOverlay =
13954
+ window.MindMapTextOverlayV2?.shouldRenderPassiveDomOverlays?.(module) === true &&
13955
+ (
13956
+ overlayState?.passiveCandidateIds?.has?.(normalizedNodeId) === true ||
13957
+ module._visibleIds?.has?.(normalizedNodeId) === true ||
13958
+ module._prevVisibleIds?.has?.(normalizedNodeId) === true ||
13959
+ String(module.selectedNodeIdJs || '').trim() === normalizedNodeId
13960
+ );
13961
+
13962
+ if (!hasLiveOverlayCard && !mayCreatePassiveOverlay) {
13963
+ return;
13964
+ }
13965
+
13966
+ if (hasLiveOverlayCard) {
13967
+ if (overlayState?.layer?.style && module._suppressPassiveDomOverlayDuringZoom !== true) {
13968
+ overlayState.layer.style.display = '';
13969
+ }
13970
+ card.style.display = 'block';
13971
+ } else {
13972
+ window.MindMapTextOverlayV2?.invalidateNode?.(module, normalizedNodeId, 'remote-live-frame');
13973
+ module._overlayDirty = true;
13974
+ module._lastOverlayKey = '';
13975
+ }
13976
+
13977
+ woke = true;
13978
+ });
13979
+ state.nodeIds.clear();
13980
+
13981
+ if (!woke) {
13982
+ return;
13983
+ }
13984
+
13985
+ module._forceUpdateFrames = Math.max(Number(module._forceUpdateFrames || 0), 2);
13986
+ module._css3dVisualChangedThisFrame = true;
13987
+ module._css3dVisualChangedSinceLastRender = true;
13988
+ wakeRemoteFleetModuleAnimation(module, 'remote-live-overlay-frame');
13989
+
13990
+ const now = getRemoteFleetFrameNow();
13991
+ if (now - Number(module._remoteFleetLiveOverlayWakeLastTraceAt || 0) >= 1000) {
13992
+ module._remoteFleetLiveOverlayWakeLastTraceAt = now;
13993
+ window.RuntimeTrace?.emit?.('remote.frame.overlayWake', {
13994
+ nodes: state.lastWakeCount || 0
13995
+ });
13996
+ }
13997
+ }
13998
+
13999
+ function requestRemoteFleetLiveOverlayWake(bodyView, frame) {
14000
+ if (!_module || !bodyView || String(frame?.kind || '').toLowerCase() !== 'live') {
14001
+ return false;
14002
+ }
14003
+
14004
+ const nodeId = getRemoteFleetBodyNodeId(bodyView);
14005
+ if (!nodeId) {
14006
+ return false;
14007
+ }
14008
+
14009
+ const state = _module._remoteFleetLiveOverlayWakeState || {
14010
+ nodeIds: new Set(),
14011
+ raf: 0,
14012
+ lastWakeCount: 0
14013
+ };
14014
+ _module._remoteFleetLiveOverlayWakeState = state;
14015
+ state.nodeIds.add(nodeId);
14016
+ state.lastWakeCount = state.nodeIds.size;
14017
+
14018
+ if (!state.raf) {
14019
+ state.raf = requestRemoteFleetFrameLoopFrame(() => flushRemoteFleetLiveOverlayWake(_module));
14020
+ }
14021
+
14022
+ return true;
14023
+ }
14024
+
14025
+ function requestRemoteFleetLiveOverlayWakeFromPreview(preview, frame) {
14026
+ const bodyView = getRemoteFleetBodyFromPreview(preview);
14027
+ return requestRemoteFleetLiveOverlayWake(bodyView, frame);
14028
+ }
14029
+
13880
14030
  function refreshRemoteFleetBinaryFrameSubscription(session, force = false) {
13881
14031
  if (!session?.ws || session.ws.readyState !== WebSocket.OPEN) {
13882
14032
  return false;
@@ -14181,6 +14331,54 @@
14181
14331
  || 'device');
14182
14332
  }
14183
14333
 
14334
+ function getRemoteFleetDeviceTooltip(device) {
14335
+ const name = getRemoteFleetDeviceName(device);
14336
+ const deviceId = getRemoteFleetDeviceId(device);
14337
+ const hostname = String(getRemoteFleetDeviceField(device, 'hostname', 'Hostname', '') || '').trim();
14338
+ const connected = isRemoteFleetDeviceConnected(device);
14339
+ const platform = [
14340
+ getRemoteFleetDeviceField(device, 'platform', 'Platform', ''),
14341
+ getRemoteFleetDeviceField(device, 'release', 'Release', ''),
14342
+ getRemoteFleetDeviceField(device, 'arch', 'Arch', '')
14343
+ ].map(value => String(value || '').trim()).filter(Boolean).join(' ');
14344
+ const agentVersion = String(getRemoteFleetDeviceField(device, 'agentVersion', 'AgentVersion', '') || '').trim();
14345
+ const remoteAddress = String(getRemoteFleetDeviceField(device, 'remoteAddress', 'RemoteAddress', '') || '').trim();
14346
+ const remotePort = Number(getRemoteFleetDeviceField(device, 'remotePort', 'RemotePort', 0)) || 0;
14347
+ const remoteEndpoint = remoteAddress ? `${remoteAddress}${remotePort > 0 ? `:${remotePort}` : ''}` : '';
14348
+ const liveSeq = getRemoteFleetFrameSeq(device, 'live');
14349
+ const thumbnailSeq = getRemoteFleetFrameSeq(device, 'thumbnail');
14350
+ const liveReceivedAt = String(getRemoteFleetDeviceField(device, 'liveFrameReceivedAt', 'LiveFrameReceivedAt', '') || '').trim();
14351
+ const thumbnailReceivedAt = String(getRemoteFleetDeviceField(device, 'thumbnailReceivedAt', 'ThumbnailReceivedAt', '') || '').trim();
14352
+ const latestTaskStatus = String(getRemoteFleetDeviceField(device, 'latestTaskStatus', 'LatestTaskStatus', '') || '').trim();
14353
+ const latestTaskTitle = String(getRemoteFleetDeviceField(device, 'latestTaskTitle', 'LatestTaskTitle', '') || '').trim();
14354
+ const latestTaskError = String(getRemoteFleetDeviceField(device, 'latestTaskError', 'LatestTaskError', '') || '').trim();
14355
+ const capabilityText = [
14356
+ isRemoteFleetLiveCapable(device) ? 'live' : '',
14357
+ isRemoteFleetThumbnailCapable(device) ? 'thumbnail' : '',
14358
+ isRemoteFleetDeviceTaskCapable(device) ? 'task' : '',
14359
+ isRemoteFleetDeviceAiCapable(device) ? 'ai' : ''
14360
+ ].filter(Boolean).join(', ');
14361
+
14362
+ return [
14363
+ name,
14364
+ deviceId ? `ID: ${deviceId}` : '',
14365
+ hostname && hostname !== name ? `Host: ${hostname}` : '',
14366
+ `Status: ${connected ? 'Connected' : 'Offline'}`,
14367
+ platform ? `OS: ${platform}` : '',
14368
+ agentVersion ? `Agent: ${agentVersion}` : '',
14369
+ remoteEndpoint ? `Remote: ${remoteEndpoint}` : '',
14370
+ `Seen: ${formatRemoteFleetAge(getRemoteFleetDeviceField(device, 'lastSeenAt', 'LastSeenAt', ''))}`,
14371
+ `Uptime: ${formatRemoteFleetDuration(getRemoteFleetDeviceField(device, 'uptimeSec', 'UptimeSec', Number.NaN))}`,
14372
+ `Memory: ${formatRemoteFleetPercent(getRemoteFleetDeviceField(device, 'usedMemRatio', 'UsedMemRatio', Number.NaN))}`,
14373
+ `Load: ${formatRemoteFleetNumber(getRemoteFleetDeviceField(device, 'load1', 'Load1', Number.NaN), 2)}`,
14374
+ liveSeq > 0 ? `Live frame: #${liveSeq}${liveReceivedAt ? ` (${formatRemoteFleetAge(liveReceivedAt)})` : ''}` : '',
14375
+ thumbnailSeq > 0 ? `Thumbnail: #${thumbnailSeq}${thumbnailReceivedAt ? ` (${formatRemoteFleetAge(thumbnailReceivedAt)})` : ''}` : '',
14376
+ capabilityText ? `Capabilities: ${capabilityText}` : '',
14377
+ latestTaskStatus ? `Task: ${latestTaskStatus}${latestTaskTitle ? ` - ${latestTaskTitle}` : ''}` : '',
14378
+ latestTaskError ? `Task error: ${formatRemoteFleetTaskError(latestTaskError)}` : ''
14379
+ ].filter(Boolean).join('\n');
14380
+ }
14381
+
14184
14382
  function isRemoteFleetDeviceConnected(device) {
14185
14383
  return getRemoteFleetDeviceField(device, 'connected', 'Connected', false) === true;
14186
14384
  }
@@ -14701,7 +14899,11 @@
14701
14899
  canvas.style.display = 'none';
14702
14900
  }
14703
14901
 
14704
- return commitRemoteFleetFrameMetadata(preview, frame);
14902
+ const committed = commitRemoteFleetFrameMetadata(preview, frame);
14903
+ if (committed) {
14904
+ requestRemoteFleetLiveOverlayWakeFromPreview(preview, frame);
14905
+ }
14906
+ return committed;
14705
14907
  }
14706
14908
 
14707
14909
  function commitRemoteFleetFrameToCanvas(preview, canvas, image, bitmap, frame) {
@@ -14722,7 +14924,11 @@
14722
14924
  image.style.display = 'none';
14723
14925
  }
14724
14926
 
14725
- return commitRemoteFleetFrameMetadata(preview, frame);
14927
+ const committed = commitRemoteFleetFrameMetadata(preview, frame);
14928
+ if (committed) {
14929
+ requestRemoteFleetLiveOverlayWakeFromPreview(preview, frame);
14930
+ }
14931
+ return committed;
14726
14932
  }
14727
14933
 
14728
14934
  function clearRemoteFleetPendingFrameDataset(preview, frame) {
@@ -17122,12 +17328,13 @@
17122
17328
  preview.dataset.remoteFleetFrameSeq = String(hasLiveFrame
17123
17329
  ? getRemoteFleetDeviceField(device, 'liveFrameSeq', 'LiveFrameSeq', 0)
17124
17330
  : getRemoteFleetDeviceField(device, 'thumbnailFrameSeq', 'ThumbnailFrameSeq', 0));
17331
+ preview.title = getRemoteFleetDeviceTooltip(device);
17125
17332
  preview.style.cssText = `
17126
17333
  position: relative;
17127
17334
  width: 100%;
17128
17335
  aspect-ratio: 16 / 9;
17129
17336
  overflow: hidden;
17130
- border-radius: ${isDetail ? '8px' : '6px'};
17337
+ border-radius: 0;
17131
17338
  background: ${(hasLiveFrame || hasThumbnail) ? 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)' : '#ffffff'};
17132
17339
  border: 1px solid rgba(148, 163, 184, ${isDetail ? '0.34' : '0.24'});
17133
17340
  box-shadow: none;
@@ -17288,7 +17495,7 @@
17288
17495
  card.setAttribute('aria-pressed', isSelected ? 'true' : 'false');
17289
17496
  card.setAttribute('aria-label', `Open remote control for ${name}`);
17290
17497
  card.tabIndex = 0;
17291
- card.title = name;
17498
+ card.title = getRemoteFleetDeviceTooltip(device);
17292
17499
  card.style.cssText = `
17293
17500
  position: relative;
17294
17501
  display: flex;
@@ -17301,7 +17508,7 @@
17301
17508
  box-sizing: border-box;
17302
17509
  overflow: hidden;
17303
17510
  padding: 0;
17304
- border-radius: 8px;
17511
+ border-radius: 0;
17305
17512
  background: transparent;
17306
17513
  border: 0;
17307
17514
  box-shadow: none;
@@ -17319,6 +17526,8 @@
17319
17526
  grid.appendChild(card);
17320
17527
  });
17321
17528
 
17529
+ appendRemoteFleetEmptyScreens(grid, nodeModel, densityState, devices.length);
17530
+
17322
17531
  const noMatch = document.createElement('div');
17323
17532
  noMatch.dataset.remoteFleetNoMatch = 'true';
17324
17533
  noMatch.textContent = 'No matching devices.';
@@ -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-mdm-ws-rerender-v580" />
11
- <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260617-mdm-ws-rerender-v580" />
10
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/app.css?v=20260617-mdm-monitor-wall-v582" />
11
+ <link rel="stylesheet" href="_content/MindExecution.Shared/css/mind-map-overrides.css?v=20260617-mdm-monitor-wall-v582" />
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-mdm-ws-rerender-v580';
582
+ const scriptVersion = '20260617-mdm-monitor-wall-v582';
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": "caOR4cJE",
2
+ "version": "7b1rjf6s",
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-ct4yGB6E/ViQSpNNn3fHtt3v+usCW9LYQEbpOmzuRdk=",
89
+ "hash": "sha256-N0yX4cRkRJsZGixBtFAt5IkDeSMDDcKAmNtERmwuC+0=",
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-zI55TuKnPpHEJMEolp25Zk/P6dkZEOtbMSHObMgR84A=",
837
+ "hash": "sha256-OtihK34SGInpkBV5g69QHYb9Mt/yUTsekpGz4Jl29L0=",
838
838
  "url": "index.html"
839
839
  },
840
840
  {
@@ -1,4 +1,4 @@
1
- /* Manifest version: caOR4cJE */
1
+ /* Manifest version: 7b1rjf6s */
2
2
  // Hosted deployments should prefer the network over stale offline caches.
3
3
  // This service worker immediately clears old Blazor offline caches and unregisters itself.
4
4