@mindexec/cli 0.2.24 → 0.2.26
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 +6 -6
- package/remote-hub.js +130 -1
- package/scripts/remote-fleet-render-smoke.mjs +122 -93
- package/scripts/remote-http-smoke.mjs +218 -40
- package/scripts/remote-hub-smoke.mjs +139 -0
- package/server.js +17 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-core.js +18 -1
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +676 -456
- package/wwwroot/_framework/{Microsoft.CSharp.qrvp77qmhs.dll → Microsoft.CSharp.8bsm8vx6su.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Core.6rfnfdndxq.dll +0 -0
- package/wwwroot/_framework/{MindExecution.Kernel.qt0p5apeu2.dll → MindExecution.Kernel.z56elxihok.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Admin.i9eswmhltm.dll → MindExecution.Plugins.Admin.p5cs4ap87v.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Business.1eayoj2kvc.dll → MindExecution.Plugins.Business.s35og5uz44.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.a3850nmm3d.dll → MindExecution.Plugins.Concept.zczca3fsxz.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.Directory.l3rmdlu7o7.dll → MindExecution.Plugins.Directory.y74f55e8x3.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.2x5gsi74yi.dll → MindExecution.Plugins.PlanMaster.jpdwbefrh1.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.vbl462eegw.dll → MindExecution.Plugins.YouTube.8nz4wv2nsj.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.l2w05i7sd6.dll → MindExecution.Shared.ihh8mkcn5x.dll} +0 -0
- package/wwwroot/_framework/MindExecution.Web.0cs29v57jl.dll +0 -0
- package/wwwroot/_framework/{System.Collections.x53e19vfsj.dll → System.Collections.23yxgetbju.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.Concurrent.y1zmvuyipi.dll → System.Collections.Concurrent.vow3rm1dku.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.Immutable.ug3j698qms.dll → System.Collections.Immutable.ap9596utv5.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.NonGeneric.h66hj3863h.dll → System.Collections.NonGeneric.npjkaz40oc.dll} +0 -0
- package/wwwroot/_framework/{System.Collections.Specialized.umr3y27ntj.dll → System.Collections.Specialized.ugkjbs6p02.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.Annotations.tz6gnt4ebt.dll → System.ComponentModel.Annotations.clwo0z2oyu.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.Primitives.j7tiphu4rg.dll → System.ComponentModel.Primitives.end4v0xe2c.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.TypeConverter.ujlztox1gx.dll → System.ComponentModel.TypeConverter.tgtp5sm4iv.dll} +0 -0
- package/wwwroot/_framework/{System.ComponentModel.x9xz0ojfb6.dll → System.ComponentModel.wupoltkk1t.dll} +0 -0
- package/wwwroot/_framework/{System.Console.ijzpqmj7ne.dll → System.Console.9dik0wogo2.dll} +0 -0
- package/wwwroot/_framework/{System.Data.Common.1r0sqffq1p.dll → System.Data.Common.4v8jejsiu0.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.DiagnosticSource.9upoqwq09o.dll → System.Diagnostics.DiagnosticSource.e2q75ondtq.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.Process.m99azzntjm.dll → System.Diagnostics.Process.9736yjnxs8.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.TraceSource.pl7wv26myr.dll → System.Diagnostics.TraceSource.h9al53gbbw.dll} +0 -0
- package/wwwroot/_framework/{System.Diagnostics.Tracing.crlhfx6tut.dll → System.Diagnostics.Tracing.h4bcp2fo98.dll} +0 -0
- package/wwwroot/_framework/{System.Drawing.mi7d8hwowb.dll → System.Drawing.64oovy8qts.dll} +0 -0
- package/wwwroot/_framework/{System.Drawing.Primitives.22e4y9ikq9.dll → System.Drawing.Primitives.o6jiqpgbgl.dll} +0 -0
- package/wwwroot/_framework/{System.Formats.Asn1.jx23sjiqnn.dll → System.Formats.Asn1.rylx5ipd40.dll} +0 -0
- package/wwwroot/_framework/{System.IO.Compression.6fyoii3uej.dll → System.IO.Compression.iceabaupns.dll} +0 -0
- package/wwwroot/_framework/{System.IO.Pipelines.vg77t4cd4d.dll → System.IO.Pipelines.uw8csd3mlz.dll} +0 -0
- package/wwwroot/_framework/{System.Linq.Expressions.24xqiypwdt.dll → System.Linq.Expressions.ty95ava37f.dll} +0 -0
- package/wwwroot/_framework/{System.Linq.Queryable.hvd01d6rsa.dll → System.Linq.Queryable.hs2195jrwy.dll} +0 -0
- package/wwwroot/_framework/{System.Linq.1bkoxlqgmq.dll → System.Linq.hssodjwmlf.dll} +0 -0
- package/wwwroot/_framework/{System.Memory.8dx3lwgym4.dll → System.Memory.1k78n7wdxb.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Http.eitrz660my.dll → System.Net.Http.3br8rfql4c.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Http.Json.3mhdm9l1rf.dll → System.Net.Http.Json.860wbh17d8.dll} +0 -0
- package/wwwroot/_framework/{System.Net.NetworkInformation.3pkuofcv9r.dll → System.Net.NetworkInformation.e3wr00853o.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Ping.8clj5pklrp.dll → System.Net.Ping.r5cw4mf1a4.dll} +0 -0
- package/wwwroot/_framework/{System.Net.Primitives.qrp4wcjz1p.dll → System.Net.Primitives.ksxwiwlvhu.dll} +0 -0
- package/wwwroot/_framework/{System.Net.WebSockets.qp6u31zvm5.dll → System.Net.WebSockets.6rt3n3gl2q.dll} +0 -0
- package/wwwroot/_framework/{System.Net.WebSockets.Client.2u6pv01g69.dll → System.Net.WebSockets.Client.z3usrzo7rz.dll} +0 -0
- package/wwwroot/_framework/{System.Numerics.Vectors.kc7ufp2j4l.dll → System.Numerics.Vectors.lbdzx8reja.dll} +0 -0
- package/wwwroot/_framework/{System.ObjectModel.qv82fot1ib.dll → System.ObjectModel.yct1gdirzf.dll} +0 -0
- package/wwwroot/_framework/{System.Private.CoreLib.rkafq04oma.dll → System.Private.CoreLib.ns29bor93l.dll} +0 -0
- package/wwwroot/_framework/{System.Private.Uri.t9542hmr6j.dll → System.Private.Uri.zp9kmg0z93.dll} +0 -0
- package/wwwroot/_framework/{System.Private.Xml.Linq.n8n3ptrbwu.dll → System.Private.Xml.Linq.lgv2n0akl4.dll} +0 -0
- package/wwwroot/_framework/{System.Private.Xml.rxd3tytisn.dll → System.Private.Xml.dpsk8g304y.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Emit.ILGeneration.stxyk8zoo1.dll → System.Reflection.Emit.ILGeneration.1tcuz2cmbk.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Emit.Lightweight.6xrd5v8vg0.dll → System.Reflection.Emit.Lightweight.ddt2wylovg.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Emit.9tjhp6y0j3.dll → System.Reflection.Emit.d8vkadiwhg.dll} +0 -0
- package/wwwroot/_framework/{System.Reflection.Primitives.wgn8fpwwvv.dll → System.Reflection.Primitives.cpsl71xd1z.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.InteropServices.te07xr2we9.dll → System.Runtime.InteropServices.31vjgsfk1o.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.InteropServices.JavaScript.sliym526xh.dll → System.Runtime.InteropServices.JavaScript.pn2wvizzet.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.InteropServices.RuntimeInformation.oji7zut14z.dll → System.Runtime.InteropServices.RuntimeInformation.l5rk496q70.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Intrinsics.507y4h8nzq.dll → System.Runtime.Intrinsics.0zee9qcqfy.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Loader.v7gk4bse0k.dll → System.Runtime.Loader.xw2jr9wl92.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Numerics.eqy5xjv3nd.dll → System.Runtime.Numerics.ezj1dfyvbj.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Serialization.Formatters.zpkrub8lab.dll → System.Runtime.Serialization.Formatters.0y019e9zkr.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.Serialization.Primitives.vhkpnbxjip.dll → System.Runtime.Serialization.Primitives.bia5wb62c8.dll} +0 -0
- package/wwwroot/_framework/{System.Runtime.jn319d5nyg.dll → System.Runtime.h9cduidfkh.dll} +0 -0
- package/wwwroot/_framework/{System.Security.Claims.0ztig1q9vo.dll → System.Security.Claims.2r18wim2rl.dll} +0 -0
- package/wwwroot/_framework/{System.Security.Cryptography.vttizqc9ho.dll → System.Security.Cryptography.qqgybpoucx.dll} +0 -0
- package/wwwroot/_framework/{System.Text.Encoding.Extensions.utdd47ny8f.dll → System.Text.Encoding.Extensions.9t3hs6kyll.dll} +0 -0
- package/wwwroot/_framework/{System.Text.Encodings.Web.wah8r1zoe0.dll → System.Text.Encodings.Web.wftjfh2crk.dll} +0 -0
- package/wwwroot/_framework/{System.Text.Json.kxlfxj0wrs.dll → System.Text.Json.4s25e3op6i.dll} +0 -0
- package/wwwroot/_framework/{System.Text.RegularExpressions.dbqn58klox.dll → System.Text.RegularExpressions.cvkox11l6l.dll} +0 -0
- package/wwwroot/_framework/{System.Threading.Channels.hfa7j0uv2w.dll → System.Threading.Channels.419493szqu.dll} +0 -0
- package/wwwroot/_framework/{System.Threading.Thread.caul0pdqul.dll → System.Threading.Thread.xhmys87xh5.dll} +0 -0
- package/wwwroot/_framework/{System.Threading.42ao9vi047.dll → System.Threading.b66vzsz9g1.dll} +0 -0
- package/wwwroot/_framework/{System.Transactions.Local.fimi2hamzo.dll → System.Transactions.Local.7zfffdvnwf.dll} +0 -0
- package/wwwroot/_framework/{System.Web.HttpUtility.gq8yz50p2e.dll → System.Web.HttpUtility.9up6xfbtuq.dll} +0 -0
- package/wwwroot/_framework/{System.Xml.Linq.kitin4zjoj.dll → System.Xml.Linq.n5rzv9nbf7.dll} +0 -0
- package/wwwroot/_framework/{System.Xml.ReaderWriter.kzvw3qgxb0.dll → System.Xml.ReaderWriter.ag8pilllob.dll} +0 -0
- package/wwwroot/_framework/{System.Xml.XDocument.c539ki6cuq.dll → System.Xml.XDocument.sn51jas17n.dll} +0 -0
- package/wwwroot/_framework/{System.m05i39uvk9.dll → System.brmz7yk5qh.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +161 -161
- package/wwwroot/_framework/dotnet.js +1 -1
- package/wwwroot/_framework/{dotnet.native.vz0adxojrz.wasm → dotnet.native.boem75ye5i.wasm} +0 -0
- package/wwwroot/_framework/{dotnet.native.xsn1d6x2kd.js → dotnet.native.qc8g39g30v.js} +1 -1
- package/wwwroot/_framework/{dotnet.runtime.dstopyvqzi.js → dotnet.runtime.opaiwunc3t.js} +1 -1
- package/wwwroot/_framework/{netstandard.0xet7jg7ky.dll → netstandard.yvr3prsx0x.dll} +0 -0
- package/wwwroot/index.html +1 -1
- package/wwwroot/service-worker-assets.js +166 -166
- package/wwwroot/service-worker.js +1 -1
- package/wwwroot/_framework/MindExecution.Core.ri6sjbi2qk.dll +0 -0
- package/wwwroot/_framework/MindExecution.Web.i4ojmz00kp.dll +0 -0
|
@@ -3551,6 +3551,7 @@
|
|
|
3551
3551
|
{ key: 'decision', iconClass: 'fa-solid fa-code-branch' },
|
|
3552
3552
|
{ key: 'user-check', iconClass: 'fa-solid fa-user-check' },
|
|
3553
3553
|
{ key: 'output', iconClass: 'fa-solid fa-file-export' },
|
|
3554
|
+
{ key: 'network-wired', iconClass: 'fa-solid fa-network-wired' },
|
|
3554
3555
|
{ key: 'robot', iconClass: 'fa-solid fa-robot' }
|
|
3555
3556
|
];
|
|
3556
3557
|
|
|
@@ -12139,6 +12140,25 @@
|
|
|
12139
12140
|
return `${Math.floor(seconds / 86400)}d ago`;
|
|
12140
12141
|
}
|
|
12141
12142
|
|
|
12143
|
+
function formatRemoteFleetTimeUntil(value) {
|
|
12144
|
+
const timestamp = Date.parse(String(value || ''));
|
|
12145
|
+
if (!Number.isFinite(timestamp)) {
|
|
12146
|
+
return '-';
|
|
12147
|
+
}
|
|
12148
|
+
|
|
12149
|
+
const seconds = Math.round((timestamp - Date.now()) / 1000);
|
|
12150
|
+
if (seconds <= 0) {
|
|
12151
|
+
return 'expired';
|
|
12152
|
+
}
|
|
12153
|
+
if (seconds < 60) {
|
|
12154
|
+
return `${seconds}s left`;
|
|
12155
|
+
}
|
|
12156
|
+
if (seconds < 3600) {
|
|
12157
|
+
return `${Math.floor(seconds / 60)}m left`;
|
|
12158
|
+
}
|
|
12159
|
+
return `${Math.floor(seconds / 3600)}h left`;
|
|
12160
|
+
}
|
|
12161
|
+
|
|
12142
12162
|
function createRemoteFleetStat(label, value, tone = 'default', key = '') {
|
|
12143
12163
|
const item = document.createElement('div');
|
|
12144
12164
|
if (key) {
|
|
@@ -12211,6 +12231,86 @@
|
|
|
12211
12231
|
return button;
|
|
12212
12232
|
}
|
|
12213
12233
|
|
|
12234
|
+
function attachRemoteFleetTitleActions(bodyView, hostButton, stopHostButton, hostState) {
|
|
12235
|
+
const container = bodyView?.closest?.('.map-node-remote-fleet');
|
|
12236
|
+
const header = container?.querySelector?.('.map-node-memo__header') || null;
|
|
12237
|
+
const existingActions = header?.querySelector?.('[data-remote-fleet-title-actions="true"]') || null;
|
|
12238
|
+
existingActions?.remove?.();
|
|
12239
|
+
|
|
12240
|
+
const titleActions = document.createElement('div');
|
|
12241
|
+
titleActions.dataset.remoteFleetTitleActions = 'true';
|
|
12242
|
+
titleActions.style.cssText = `
|
|
12243
|
+
display: inline-flex;
|
|
12244
|
+
align-items: center;
|
|
12245
|
+
justify-content: flex-end;
|
|
12246
|
+
gap: 7px;
|
|
12247
|
+
min-width: 0;
|
|
12248
|
+
pointer-events: auto;
|
|
12249
|
+
`;
|
|
12250
|
+
|
|
12251
|
+
const normalizedState = String(hostState || 'inactive').trim().toLowerCase();
|
|
12252
|
+
const active = normalizedState === 'hosting';
|
|
12253
|
+
const indicator = document.createElement('span');
|
|
12254
|
+
indicator.dataset.remoteFleetHostIndicator = 'true';
|
|
12255
|
+
indicator.dataset.remoteFleetHostActive = active ? 'true' : 'false';
|
|
12256
|
+
indicator.title = active ? 'Host active' : 'Host inactive';
|
|
12257
|
+
indicator.style.cssText = `
|
|
12258
|
+
flex: 0 0 auto;
|
|
12259
|
+
width: 9px;
|
|
12260
|
+
height: 9px;
|
|
12261
|
+
border-radius: 999px;
|
|
12262
|
+
background: ${active ? '#2563eb' : '#94a3b8'};
|
|
12263
|
+
box-shadow: ${active ? '0 0 0 4px rgba(37, 99, 235, 0.14)' : '0 0 0 4px rgba(148, 163, 184, 0.14)'};
|
|
12264
|
+
`;
|
|
12265
|
+
|
|
12266
|
+
titleActions.appendChild(indicator);
|
|
12267
|
+
titleActions.appendChild(hostButton);
|
|
12268
|
+
if (stopHostButton) {
|
|
12269
|
+
titleActions.appendChild(stopHostButton);
|
|
12270
|
+
}
|
|
12271
|
+
|
|
12272
|
+
if (header) {
|
|
12273
|
+
header.style.gridTemplateColumns = '46px minmax(0, 1fr) auto';
|
|
12274
|
+
header.appendChild(titleActions);
|
|
12275
|
+
} else {
|
|
12276
|
+
titleActions.style.alignSelf = 'flex-end';
|
|
12277
|
+
bodyView?.prepend?.(titleActions);
|
|
12278
|
+
}
|
|
12279
|
+
|
|
12280
|
+
return titleActions;
|
|
12281
|
+
}
|
|
12282
|
+
|
|
12283
|
+
function createRemoteFleetEmptyScreens() {
|
|
12284
|
+
const shell = document.createElement('section');
|
|
12285
|
+
shell.dataset.remoteFleetEmptyScreens = 'true';
|
|
12286
|
+
shell.style.cssText = `
|
|
12287
|
+
flex: 1 1 auto;
|
|
12288
|
+
min-height: 0;
|
|
12289
|
+
overflow: hidden;
|
|
12290
|
+
display: grid;
|
|
12291
|
+
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
|
|
12292
|
+
align-content: start;
|
|
12293
|
+
gap: 10px;
|
|
12294
|
+
padding: 2px 4px 6px 0;
|
|
12295
|
+
`;
|
|
12296
|
+
|
|
12297
|
+
for (let index = 0; index < 6; index += 1) {
|
|
12298
|
+
const screen = document.createElement('div');
|
|
12299
|
+
screen.dataset.remoteFleetEmptyScreen = 'true';
|
|
12300
|
+
screen.style.cssText = `
|
|
12301
|
+
aspect-ratio: 16 / 9;
|
|
12302
|
+
min-height: 96px;
|
|
12303
|
+
border-radius: 8px;
|
|
12304
|
+
border: 1px solid rgba(203, 213, 225, 0.72);
|
|
12305
|
+
background: #ffffff;
|
|
12306
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96), 0 8px 18px rgba(15, 23, 42, 0.04);
|
|
12307
|
+
`;
|
|
12308
|
+
shell.appendChild(screen);
|
|
12309
|
+
}
|
|
12310
|
+
|
|
12311
|
+
return shell;
|
|
12312
|
+
}
|
|
12313
|
+
|
|
12214
12314
|
function createRemoteFleetSelect(options, value, title) {
|
|
12215
12315
|
const select = document.createElement('select');
|
|
12216
12316
|
select.title = title || '';
|
|
@@ -12244,6 +12344,142 @@
|
|
|
12244
12344
|
const REMOTE_FLEET_TASK_FOLLOW_INITIAL_MS = 250;
|
|
12245
12345
|
const REMOTE_FLEET_TASK_FOLLOW_REFRESH_MS = 2000;
|
|
12246
12346
|
const REMOTE_FLEET_TASK_FOLLOW_MAX_TICKS = 60;
|
|
12347
|
+
const REMOTE_FLEET_HOST_LEASE_REFRESH_MS = 10000;
|
|
12348
|
+
const remoteFleetHostLeaseTimers = new Map();
|
|
12349
|
+
const remoteFleetLocalHostTargets = new Map();
|
|
12350
|
+
|
|
12351
|
+
function findRemoteFleetBodyByNodeId(nodeId) {
|
|
12352
|
+
const id = String(nodeId || '').trim();
|
|
12353
|
+
if (!id) return null;
|
|
12354
|
+
return Array.from(document.querySelectorAll('.map-node-remote-fleet__body[data-node-id]'))
|
|
12355
|
+
.find(body => String(body.dataset.nodeId || '').trim() === id) || null;
|
|
12356
|
+
}
|
|
12357
|
+
|
|
12358
|
+
function stopRemoteFleetHostLeaseTimer(nodeId) {
|
|
12359
|
+
const id = String(nodeId || '').trim();
|
|
12360
|
+
if (!id) return;
|
|
12361
|
+
const entry = remoteFleetHostLeaseTimers.get(id);
|
|
12362
|
+
if (entry?.timer) {
|
|
12363
|
+
clearInterval(entry.timer);
|
|
12364
|
+
}
|
|
12365
|
+
remoteFleetHostLeaseTimers.delete(id);
|
|
12366
|
+
}
|
|
12367
|
+
|
|
12368
|
+
function startRemoteFleetHostLeaseTimer(nodeId, renewCallback) {
|
|
12369
|
+
const id = String(nodeId || '').trim();
|
|
12370
|
+
if (!id || typeof renewCallback !== 'function') return;
|
|
12371
|
+
const existing = remoteFleetHostLeaseTimers.get(id);
|
|
12372
|
+
if (existing) {
|
|
12373
|
+
existing.renewCallback = renewCallback;
|
|
12374
|
+
return;
|
|
12375
|
+
}
|
|
12376
|
+
|
|
12377
|
+
const entry = { renewCallback, timer: null };
|
|
12378
|
+
const timer = setInterval(async () => {
|
|
12379
|
+
const currentBody = findRemoteFleetBodyByNodeId(id);
|
|
12380
|
+
if (!currentBody || currentBody.dataset.remoteFleetHostState !== 'hosting') {
|
|
12381
|
+
stopRemoteFleetHostLeaseTimer(id);
|
|
12382
|
+
return;
|
|
12383
|
+
}
|
|
12384
|
+
|
|
12385
|
+
try {
|
|
12386
|
+
await entry.renewCallback();
|
|
12387
|
+
} catch {
|
|
12388
|
+
stopRemoteFleetHostLeaseTimer(id);
|
|
12389
|
+
}
|
|
12390
|
+
}, REMOTE_FLEET_HOST_LEASE_REFRESH_MS);
|
|
12391
|
+
|
|
12392
|
+
timer?.unref?.();
|
|
12393
|
+
entry.timer = timer;
|
|
12394
|
+
remoteFleetHostLeaseTimers.set(id, entry);
|
|
12395
|
+
}
|
|
12396
|
+
|
|
12397
|
+
function getRemoteFleetNodeStateId(nodeState) {
|
|
12398
|
+
return String(nodeState?.nodeId ?? nodeState?.NodeId ?? nodeState?.id ?? nodeState?.Id ?? '').trim();
|
|
12399
|
+
}
|
|
12400
|
+
|
|
12401
|
+
function ensureRemoteFleetNodeStateMetadata(nodeState) {
|
|
12402
|
+
if (!nodeState || typeof nodeState !== 'object') return null;
|
|
12403
|
+
const metadata = nodeState.metadata && typeof nodeState.metadata === 'object'
|
|
12404
|
+
? nodeState.metadata
|
|
12405
|
+
: nodeState.Metadata && typeof nodeState.Metadata === 'object'
|
|
12406
|
+
? nodeState.Metadata
|
|
12407
|
+
: {};
|
|
12408
|
+
nodeState.metadata = metadata;
|
|
12409
|
+
nodeState.Metadata = metadata;
|
|
12410
|
+
return metadata;
|
|
12411
|
+
}
|
|
12412
|
+
|
|
12413
|
+
function readRemoteFleetObjectField(source, camelKey, pascalKey, fallback = '') {
|
|
12414
|
+
const value = source?.[camelKey] ?? source?.[pascalKey] ?? fallback;
|
|
12415
|
+
return value === undefined || value === null ? fallback : value;
|
|
12416
|
+
}
|
|
12417
|
+
|
|
12418
|
+
function isRemoteFleetResultSuccess(result) {
|
|
12419
|
+
return result?.success === true || result?.Success === true || result?.ok === true || result?.Ok === true;
|
|
12420
|
+
}
|
|
12421
|
+
|
|
12422
|
+
function isRemoteFleetResultActive(result) {
|
|
12423
|
+
return result?.active === true || result?.Active === true;
|
|
12424
|
+
}
|
|
12425
|
+
|
|
12426
|
+
function isRemoteFleetResultExplicitlyInactive(result) {
|
|
12427
|
+
return result?.active === false || result?.Active === false;
|
|
12428
|
+
}
|
|
12429
|
+
|
|
12430
|
+
function rememberRemoteFleetLocalHostTarget(nodeId, enabled, result) {
|
|
12431
|
+
const id = String(nodeId || '').trim();
|
|
12432
|
+
if (!id) return;
|
|
12433
|
+
if (enabled !== true) {
|
|
12434
|
+
if (isRemoteFleetResultSuccess(result)) {
|
|
12435
|
+
remoteFleetLocalHostTargets.delete(id);
|
|
12436
|
+
stopRemoteFleetHostLeaseTimer(id);
|
|
12437
|
+
}
|
|
12438
|
+
return;
|
|
12439
|
+
}
|
|
12440
|
+
|
|
12441
|
+
if (!isRemoteFleetResultSuccess(result) || isRemoteFleetResultExplicitlyInactive(result)) {
|
|
12442
|
+
return;
|
|
12443
|
+
}
|
|
12444
|
+
|
|
12445
|
+
const hostTarget = result?.hostTarget || result?.HostTarget || {};
|
|
12446
|
+
const now = new Date();
|
|
12447
|
+
const expiresAt = String(readRemoteFleetObjectField(hostTarget, 'expiresAt', 'ExpiresAt', '') || '').trim()
|
|
12448
|
+
|| new Date(now.getTime() + 60000).toISOString();
|
|
12449
|
+
remoteFleetLocalHostTargets.set(id, {
|
|
12450
|
+
nodeId: id,
|
|
12451
|
+
leaseId: String(readRemoteFleetObjectField(hostTarget, 'leaseId', 'LeaseId', '') || '').trim(),
|
|
12452
|
+
endpoint: String(readRemoteFleetObjectField(hostTarget, 'endpoint', 'Endpoint', '') || '').trim(),
|
|
12453
|
+
activatedAt: String(readRemoteFleetObjectField(hostTarget, 'activatedAt', 'ActivatedAt', '') || '').trim() || now.toISOString(),
|
|
12454
|
+
updatedAt: String(readRemoteFleetObjectField(hostTarget, 'updatedAt', 'UpdatedAt', '') || '').trim() || now.toISOString(),
|
|
12455
|
+
expiresAt
|
|
12456
|
+
});
|
|
12457
|
+
}
|
|
12458
|
+
|
|
12459
|
+
function applyRemoteFleetLocalHostTarget(nodeState) {
|
|
12460
|
+
const nodeId = getRemoteFleetNodeStateId(nodeState);
|
|
12461
|
+
if (!nodeId) return nodeState;
|
|
12462
|
+
const localHost = remoteFleetLocalHostTargets.get(nodeId);
|
|
12463
|
+
if (!localHost) return nodeState;
|
|
12464
|
+
|
|
12465
|
+
const expiresTime = Date.parse(localHost.expiresAt);
|
|
12466
|
+
if (!Number.isFinite(expiresTime) || expiresTime <= Date.now()) {
|
|
12467
|
+
remoteFleetLocalHostTargets.delete(nodeId);
|
|
12468
|
+
stopRemoteFleetHostLeaseTimer(nodeId);
|
|
12469
|
+
return nodeState;
|
|
12470
|
+
}
|
|
12471
|
+
|
|
12472
|
+
const metadata = ensureRemoteFleetNodeStateMetadata(nodeState);
|
|
12473
|
+
if (!metadata) return nodeState;
|
|
12474
|
+
metadata.RemoteFleetHostTargetState = 'hosting';
|
|
12475
|
+
metadata.RemoteFleetHostTargetNodeId = nodeId;
|
|
12476
|
+
metadata.RemoteFleetHostTargetLeaseId = localHost.leaseId;
|
|
12477
|
+
metadata.RemoteFleetHostTargetEndpoint = localHost.endpoint || metadata.RemoteFleetHubEndpoint || metadata.remoteFleetHubEndpoint || '';
|
|
12478
|
+
metadata.RemoteFleetHostTargetActivatedAtUtc = localHost.activatedAt;
|
|
12479
|
+
metadata.RemoteFleetHostTargetUpdatedAtUtc = localHost.updatedAt;
|
|
12480
|
+
metadata.RemoteFleetHostTargetExpiresAtUtc = localHost.expiresAt;
|
|
12481
|
+
return nodeState;
|
|
12482
|
+
}
|
|
12247
12483
|
|
|
12248
12484
|
function clearRemoteFleetTimers(bodyView) {
|
|
12249
12485
|
if (!bodyView) return;
|
|
@@ -12259,6 +12495,10 @@
|
|
|
12259
12495
|
clearTimeout(bodyView._remoteFleetTaskFollowTimer);
|
|
12260
12496
|
bodyView._remoteFleetTaskFollowTimer = null;
|
|
12261
12497
|
}
|
|
12498
|
+
if (bodyView._remoteFleetHostLeaseTimer) {
|
|
12499
|
+
clearInterval(bodyView._remoteFleetHostLeaseTimer);
|
|
12500
|
+
bodyView._remoteFleetHostLeaseTimer = null;
|
|
12501
|
+
}
|
|
12262
12502
|
}
|
|
12263
12503
|
|
|
12264
12504
|
function getRemoteFleetDeviceField(device, camelKey, pascalKey, fallback = '') {
|
|
@@ -12455,7 +12695,7 @@
|
|
|
12455
12695
|
async function syncRemoteFleetNodeStateFromResult(result) {
|
|
12456
12696
|
const nodeState = result?.nodeState || result?.refresh?.nodeState;
|
|
12457
12697
|
if (nodeState && window.mindMap?.syncNodeStates) {
|
|
12458
|
-
await window.mindMap.syncNodeStates([nodeState]);
|
|
12698
|
+
await window.mindMap.syncNodeStates([applyRemoteFleetLocalHostTarget(nodeState)]);
|
|
12459
12699
|
}
|
|
12460
12700
|
}
|
|
12461
12701
|
|
|
@@ -12465,11 +12705,11 @@
|
|
|
12465
12705
|
|
|
12466
12706
|
const nodeId = String(nodeModel?.id ?? nodeModel?.Id ?? '');
|
|
12467
12707
|
const device = parseRemoteFleetPinnedDevice(nodeModel);
|
|
12468
|
-
const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
|
|
12469
12708
|
const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
|
|
12470
12709
|
const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
|
|
12471
12710
|
const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
|
|
12472
12711
|
const deviceId = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetPinnedDeviceId', device ? getRemoteFleetDeviceId(device) : '');
|
|
12712
|
+
const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
|
|
12473
12713
|
|
|
12474
12714
|
bodyView.dataset.src = `remote-device:${deviceId}:${refreshedAt}`;
|
|
12475
12715
|
bodyView.classList.add('map-node-remote-fleet__body');
|
|
@@ -12997,6 +13237,16 @@
|
|
|
12997
13237
|
if (focusedDevice) {
|
|
12998
13238
|
bodyView.dataset.remoteFleetFocusDeviceId = getRemoteFleetDeviceId(focusedDevice);
|
|
12999
13239
|
}
|
|
13240
|
+
const selectedState = bodyView.dataset.remoteFleetSelectedDeviceId || focusState || '';
|
|
13241
|
+
const selectedDevice = devices.find(device => getRemoteFleetDeviceId(device) === selectedState)
|
|
13242
|
+
|| focusedDevice
|
|
13243
|
+
|| devices[0]
|
|
13244
|
+
|| null;
|
|
13245
|
+
if (selectedDevice) {
|
|
13246
|
+
bodyView.dataset.remoteFleetSelectedDeviceId = getRemoteFleetDeviceId(selectedDevice);
|
|
13247
|
+
} else {
|
|
13248
|
+
delete bodyView.dataset.remoteFleetSelectedDeviceId;
|
|
13249
|
+
}
|
|
13000
13250
|
const total = Number(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetDeviceCount', devices.length));
|
|
13001
13251
|
const connected = Number(getRemoteFleetMetadataValue(
|
|
13002
13252
|
nodeModel,
|
|
@@ -13004,18 +13254,18 @@
|
|
|
13004
13254
|
devices.filter(isRemoteFleetDeviceConnected).length));
|
|
13005
13255
|
const taskCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceTaskCapable(device)).length;
|
|
13006
13256
|
const aiCapableCount = devices.filter(device => isRemoteFleetDeviceConnected(device) && isRemoteFleetDeviceAiCapable(device)).length;
|
|
13007
|
-
const endpoint = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubEndpoint', '127.0.0.1:5197');
|
|
13008
|
-
const managerPackage = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetManagerPackage', '@mindexec/cli');
|
|
13009
|
-
const managerVersion = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetManagerVersion', '');
|
|
13010
|
-
const command = getRemoteFleetMetadataValue(
|
|
13011
|
-
nodeModel,
|
|
13012
|
-
'RemoteFleetConnectCommand',
|
|
13013
|
-
`npx @mindexec/remote connect --manager ${endpoint} --pair <pair-token>`);
|
|
13014
13257
|
const hubStatus = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHubStatus', 'offline');
|
|
13015
13258
|
const refreshedAt = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastRefreshAtUtc', '');
|
|
13016
13259
|
const lastError = getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetLastError', '');
|
|
13260
|
+
const hostTargetState = String(getRemoteFleetMetadataValue(nodeModel, 'RemoteFleetHostTargetState', 'inactive')).trim().toLowerCase();
|
|
13261
|
+
const isHostingTarget = hostTargetState === 'hosting';
|
|
13262
|
+
|
|
13263
|
+
if (!isHostingTarget) {
|
|
13264
|
+
stopRemoteFleetHostLeaseTimer(nodeId);
|
|
13265
|
+
}
|
|
13017
13266
|
|
|
13018
13267
|
bodyView.dataset.src = `remote-fleet:${refreshedAt}:${devices.length}:${connected}`;
|
|
13268
|
+
bodyView.dataset.remoteFleetHostState = hostTargetState;
|
|
13019
13269
|
bodyView.classList.add('map-node-remote-fleet__body');
|
|
13020
13270
|
bodyView.innerHTML = '';
|
|
13021
13271
|
bodyView.style.cssText = `
|
|
@@ -13030,6 +13280,28 @@
|
|
|
13030
13280
|
background: linear-gradient(180deg, rgba(248, 250, 252, 0.96), rgba(241, 245, 249, 0.92));
|
|
13031
13281
|
`;
|
|
13032
13282
|
|
|
13283
|
+
const hostButton = createRemoteFleetButton(
|
|
13284
|
+
'Set Host',
|
|
13285
|
+
isHostingTarget
|
|
13286
|
+
? 'Renew this monitor as the active host target for remote agents.'
|
|
13287
|
+
: 'Set this monitor as the active host target for remote agents.',
|
|
13288
|
+
'set-host');
|
|
13289
|
+
hostButton.dataset.remoteFleetHostActive = isHostingTarget ? 'true' : 'false';
|
|
13290
|
+
hostButton.style.height = '30px';
|
|
13291
|
+
hostButton.style.minWidth = '78px';
|
|
13292
|
+
hostButton.style.borderColor = isHostingTarget ? 'rgba(37, 99, 235, 0.44)' : 'rgba(37, 99, 235, 0.32)';
|
|
13293
|
+
hostButton.style.background = isHostingTarget ? 'rgba(239, 246, 255, 0.98)' : '#ffffff';
|
|
13294
|
+
hostButton.style.color = '#1d4ed8';
|
|
13295
|
+
let stopHostButton = null;
|
|
13296
|
+
if (isHostingTarget) {
|
|
13297
|
+
stopHostButton = createRemoteFleetButton('Stop', 'Stop using this monitor as the host target.', 'stop-host');
|
|
13298
|
+
stopHostButton.style.height = '30px';
|
|
13299
|
+
stopHostButton.style.borderColor = 'rgba(239, 68, 68, 0.30)';
|
|
13300
|
+
stopHostButton.style.color = '#b91c1c';
|
|
13301
|
+
stopHostButton.style.background = 'rgba(254, 242, 242, 0.94)';
|
|
13302
|
+
}
|
|
13303
|
+
attachRemoteFleetTitleActions(bodyView, hostButton, stopHostButton, hostTargetState);
|
|
13304
|
+
|
|
13033
13305
|
const top = document.createElement('div');
|
|
13034
13306
|
top.style.cssText = `
|
|
13035
13307
|
display: grid;
|
|
@@ -13043,92 +13315,6 @@
|
|
|
13043
13315
|
top.appendChild(createRemoteFleetStat('AI', String(aiCapableCount), aiCapableCount > 0 ? 'online' : 'default'));
|
|
13044
13316
|
bodyView.appendChild(top);
|
|
13045
13317
|
|
|
13046
|
-
const managerRow = document.createElement('div');
|
|
13047
|
-
managerRow.dataset.remoteFleetManagerVersion = 'true';
|
|
13048
|
-
managerRow.textContent = `${managerPackage}${managerVersion ? ` ${managerVersion}` : ''} - ${endpoint}`;
|
|
13049
|
-
managerRow.title = managerRow.textContent;
|
|
13050
|
-
managerRow.style.cssText = `
|
|
13051
|
-
flex: 0 0 auto;
|
|
13052
|
-
min-width: 0;
|
|
13053
|
-
overflow: hidden;
|
|
13054
|
-
text-overflow: ellipsis;
|
|
13055
|
-
white-space: nowrap;
|
|
13056
|
-
color: #475569;
|
|
13057
|
-
font-size: 10px;
|
|
13058
|
-
font-weight: 850;
|
|
13059
|
-
line-height: 1.2;
|
|
13060
|
-
letter-spacing: 0;
|
|
13061
|
-
`;
|
|
13062
|
-
bodyView.appendChild(managerRow);
|
|
13063
|
-
|
|
13064
|
-
const commandRow = document.createElement('div');
|
|
13065
|
-
commandRow.style.cssText = `
|
|
13066
|
-
display: grid;
|
|
13067
|
-
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
|
13068
|
-
gap: 8px;
|
|
13069
|
-
align-items: center;
|
|
13070
|
-
flex: 0 0 auto;
|
|
13071
|
-
`;
|
|
13072
|
-
|
|
13073
|
-
const commandText = document.createElement('code');
|
|
13074
|
-
commandText.textContent = command;
|
|
13075
|
-
commandText.style.cssText = `
|
|
13076
|
-
min-width: 0;
|
|
13077
|
-
overflow: hidden;
|
|
13078
|
-
text-overflow: ellipsis;
|
|
13079
|
-
white-space: nowrap;
|
|
13080
|
-
padding: 8px 10px;
|
|
13081
|
-
border-radius: 7px;
|
|
13082
|
-
background: rgba(15, 23, 42, 0.92);
|
|
13083
|
-
color: #e2e8f0;
|
|
13084
|
-
font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
|
|
13085
|
-
font-size: 11px;
|
|
13086
|
-
line-height: 1.2;
|
|
13087
|
-
`;
|
|
13088
|
-
commandText.title = command;
|
|
13089
|
-
|
|
13090
|
-
const copyButton = createRemoteFleetButton('Copy', 'Copy agent command', 'copy-command');
|
|
13091
|
-
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh remote devices', 'refresh');
|
|
13092
|
-
const autoMonitorLabel = document.createElement('label');
|
|
13093
|
-
autoMonitorLabel.title = 'Refresh stale thumbnails on a bounded timer';
|
|
13094
|
-
autoMonitorLabel.style.cssText = `
|
|
13095
|
-
display: inline-flex;
|
|
13096
|
-
align-items: center;
|
|
13097
|
-
justify-content: center;
|
|
13098
|
-
gap: 6px;
|
|
13099
|
-
height: 34px;
|
|
13100
|
-
padding: 0 9px;
|
|
13101
|
-
border-radius: 7px;
|
|
13102
|
-
border: 1px solid rgba(14, 165, 233, 0.28);
|
|
13103
|
-
background: ${autoMonitorState ? 'rgba(240, 249, 255, 0.92)' : 'rgba(248, 250, 252, 0.92)'};
|
|
13104
|
-
color: ${autoMonitorState ? '#0369a1' : '#475569'};
|
|
13105
|
-
font-size: 11px;
|
|
13106
|
-
font-weight: 900;
|
|
13107
|
-
letter-spacing: 0;
|
|
13108
|
-
cursor: pointer;
|
|
13109
|
-
pointer-events: auto;
|
|
13110
|
-
user-select: none;
|
|
13111
|
-
`;
|
|
13112
|
-
const autoMonitorToggle = document.createElement('input');
|
|
13113
|
-
autoMonitorToggle.type = 'checkbox';
|
|
13114
|
-
autoMonitorToggle.checked = autoMonitorState;
|
|
13115
|
-
autoMonitorToggle.dataset.remoteFleetAutoToggle = 'true';
|
|
13116
|
-
autoMonitorToggle.style.cssText = `
|
|
13117
|
-
width: 14px;
|
|
13118
|
-
height: 14px;
|
|
13119
|
-
margin: 0;
|
|
13120
|
-
accent-color: #0284c7;
|
|
13121
|
-
`;
|
|
13122
|
-
const autoMonitorText = document.createElement('span');
|
|
13123
|
-
autoMonitorText.textContent = 'Auto';
|
|
13124
|
-
autoMonitorLabel.appendChild(autoMonitorToggle);
|
|
13125
|
-
autoMonitorLabel.appendChild(autoMonitorText);
|
|
13126
|
-
commandRow.appendChild(commandText);
|
|
13127
|
-
commandRow.appendChild(autoMonitorLabel);
|
|
13128
|
-
commandRow.appendChild(copyButton);
|
|
13129
|
-
commandRow.appendChild(refreshButton);
|
|
13130
|
-
bodyView.appendChild(commandRow);
|
|
13131
|
-
|
|
13132
13318
|
const filterRow = document.createElement('div');
|
|
13133
13319
|
filterRow.style.cssText = `
|
|
13134
13320
|
display: grid;
|
|
@@ -13141,7 +13327,7 @@
|
|
|
13141
13327
|
const searchInput = document.createElement('input');
|
|
13142
13328
|
searchInput.type = 'search';
|
|
13143
13329
|
searchInput.value = searchState;
|
|
13144
|
-
searchInput.placeholder = '
|
|
13330
|
+
searchInput.placeholder = '';
|
|
13145
13331
|
searchInput.dataset.remoteFleetSearch = 'true';
|
|
13146
13332
|
searchInput.autocomplete = 'off';
|
|
13147
13333
|
searchInput.spellcheck = false;
|
|
@@ -13213,11 +13399,11 @@
|
|
|
13213
13399
|
filterRow.appendChild(groupSelect);
|
|
13214
13400
|
filterRow.appendChild(densitySelect);
|
|
13215
13401
|
filterRow.appendChild(matchCount);
|
|
13216
|
-
|
|
13402
|
+
filterRow.dataset.remoteFleetAdvancedFilters = 'hidden';
|
|
13217
13403
|
|
|
13218
13404
|
const taskRow = document.createElement('div');
|
|
13219
13405
|
taskRow.style.cssText = `
|
|
13220
|
-
display:
|
|
13406
|
+
display: none;
|
|
13221
13407
|
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
|
13222
13408
|
gap: 8px;
|
|
13223
13409
|
align-items: stretch;
|
|
@@ -13225,7 +13411,7 @@
|
|
|
13225
13411
|
`;
|
|
13226
13412
|
const taskInput = document.createElement('textarea');
|
|
13227
13413
|
taskInput.dataset.remoteFleetTaskInput = 'true';
|
|
13228
|
-
taskInput.placeholder = '
|
|
13414
|
+
taskInput.placeholder = '';
|
|
13229
13415
|
taskInput.rows = 2;
|
|
13230
13416
|
taskInput.style.cssText = `
|
|
13231
13417
|
width: 100%;
|
|
@@ -13287,7 +13473,7 @@
|
|
|
13287
13473
|
taskRow.appendChild(aiToggleLabel);
|
|
13288
13474
|
taskRow.appendChild(sendVisibleButton);
|
|
13289
13475
|
taskRow.appendChild(sendConnectedButton);
|
|
13290
|
-
|
|
13476
|
+
taskRow.dataset.remoteFleetTaskComposer = 'hidden';
|
|
13291
13477
|
|
|
13292
13478
|
const taskFeedback = document.createElement('div');
|
|
13293
13479
|
taskFeedback.dataset.remoteFleetTaskFeedback = 'true';
|
|
@@ -13626,37 +13812,297 @@
|
|
|
13626
13812
|
bodyView.appendChild(errorEl);
|
|
13627
13813
|
}
|
|
13628
13814
|
|
|
13815
|
+
const createDevicePreview = (device, mode = 'tile') => {
|
|
13816
|
+
const name = getRemoteFleetDeviceName(device);
|
|
13817
|
+
const connectedDevice = isRemoteFleetDeviceConnected(device);
|
|
13818
|
+
const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
|
|
13819
|
+
const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
|
|
13820
|
+
const liveFrameDataUrl = String(device?.liveFrameDataUrl || device?.LiveFrameDataUrl || '');
|
|
13821
|
+
const liveFrameReceivedAt = String(device?.liveFrameReceivedAt || device?.LiveFrameReceivedAt || '');
|
|
13822
|
+
const hasThumbnail = hasRemoteFleetThumbnail(device);
|
|
13823
|
+
const hasLiveFrame = hasRemoteFleetLiveFrame(device);
|
|
13824
|
+
const previewDataUrl = hasLiveFrame ? liveFrameDataUrl : thumbnailDataUrl;
|
|
13825
|
+
const previewAt = hasLiveFrame ? liveFrameReceivedAt : thumbnailCapturedAt;
|
|
13826
|
+
const isDetail = mode === 'detail';
|
|
13827
|
+
|
|
13828
|
+
const preview = document.createElement('div');
|
|
13829
|
+
preview.dataset.remoteFleetDevicePreview = mode;
|
|
13830
|
+
preview.style.cssText = `
|
|
13831
|
+
position: relative;
|
|
13832
|
+
width: 100%;
|
|
13833
|
+
aspect-ratio: 16 / 9;
|
|
13834
|
+
overflow: hidden;
|
|
13835
|
+
border-radius: ${isDetail ? '8px' : '6px'};
|
|
13836
|
+
background: ${(hasLiveFrame || hasThumbnail) ? 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)' : '#ffffff'};
|
|
13837
|
+
border: 1px solid rgba(15, 23, 42, ${isDetail ? '0.16' : '0.10'});
|
|
13838
|
+
`;
|
|
13839
|
+
|
|
13840
|
+
if (hasLiveFrame || hasThumbnail) {
|
|
13841
|
+
const image = document.createElement('img');
|
|
13842
|
+
image.src = previewDataUrl;
|
|
13843
|
+
image.alt = hasLiveFrame ? `${name} live frame` : `${name} thumbnail`;
|
|
13844
|
+
image.loading = 'lazy';
|
|
13845
|
+
image.decoding = 'async';
|
|
13846
|
+
image.style.cssText = `
|
|
13847
|
+
width: 100%;
|
|
13848
|
+
height: 100%;
|
|
13849
|
+
object-fit: cover;
|
|
13850
|
+
display: block;
|
|
13851
|
+
`;
|
|
13852
|
+
preview.appendChild(image);
|
|
13853
|
+
} else {
|
|
13854
|
+
const placeholder = document.createElement('div');
|
|
13855
|
+
placeholder.dataset.remoteFleetScreenPlaceholder = 'true';
|
|
13856
|
+
placeholder.style.cssText = `
|
|
13857
|
+
position: absolute;
|
|
13858
|
+
inset: 0;
|
|
13859
|
+
background: #ffffff;
|
|
13860
|
+
`;
|
|
13861
|
+
preview.appendChild(placeholder);
|
|
13862
|
+
}
|
|
13863
|
+
|
|
13864
|
+
const dot = document.createElement('span');
|
|
13865
|
+
dot.style.cssText = `
|
|
13866
|
+
position: absolute;
|
|
13867
|
+
left: ${isDetail ? '9px' : '6px'};
|
|
13868
|
+
top: ${isDetail ? '9px' : '6px'};
|
|
13869
|
+
width: ${isDetail ? '10px' : '8px'};
|
|
13870
|
+
height: ${isDetail ? '10px' : '8px'};
|
|
13871
|
+
border-radius: 999px;
|
|
13872
|
+
background: ${connectedDevice ? '#10b981' : '#94a3b8'};
|
|
13873
|
+
box-shadow: 0 0 0 3px ${connectedDevice ? 'rgba(16,185,129,0.20)' : 'rgba(148,163,184,0.18)'};
|
|
13874
|
+
`;
|
|
13875
|
+
preview.appendChild(dot);
|
|
13876
|
+
|
|
13877
|
+
if (hasLiveFrame || (isDetail && previewAt)) {
|
|
13878
|
+
const badge = document.createElement('span');
|
|
13879
|
+
badge.textContent = hasLiveFrame
|
|
13880
|
+
? (isDetail && previewAt ? `LIVE ${formatRemoteFleetAge(previewAt)}` : 'LIVE')
|
|
13881
|
+
: formatRemoteFleetAge(previewAt);
|
|
13882
|
+
badge.style.cssText = `
|
|
13883
|
+
position: absolute;
|
|
13884
|
+
right: ${isDetail ? '9px' : '6px'};
|
|
13885
|
+
bottom: ${isDetail ? '9px' : '6px'};
|
|
13886
|
+
max-width: calc(100% - ${isDetail ? '18px' : '12px'});
|
|
13887
|
+
padding: ${isDetail ? '4px 7px' : '3px 6px'};
|
|
13888
|
+
border-radius: 999px;
|
|
13889
|
+
background: ${hasLiveFrame ? 'rgba(220, 38, 38, 0.84)' : 'rgba(15, 23, 42, 0.72)'};
|
|
13890
|
+
color: #e2e8f0;
|
|
13891
|
+
font-size: ${isDetail ? '9px' : '8px'};
|
|
13892
|
+
font-weight: 950;
|
|
13893
|
+
line-height: 1;
|
|
13894
|
+
overflow: hidden;
|
|
13895
|
+
text-overflow: ellipsis;
|
|
13896
|
+
white-space: nowrap;
|
|
13897
|
+
letter-spacing: 0;
|
|
13898
|
+
`;
|
|
13899
|
+
preview.appendChild(badge);
|
|
13900
|
+
}
|
|
13901
|
+
|
|
13902
|
+
return preview;
|
|
13903
|
+
};
|
|
13904
|
+
|
|
13905
|
+
const createSelectedDevicePanel = device => {
|
|
13906
|
+
const panel = document.createElement('aside');
|
|
13907
|
+
panel.dataset.remoteFleetDetailPanel = 'true';
|
|
13908
|
+
panel.style.cssText = `
|
|
13909
|
+
min-width: 0;
|
|
13910
|
+
min-height: 0;
|
|
13911
|
+
overflow-y: auto;
|
|
13912
|
+
display: flex;
|
|
13913
|
+
flex-direction: column;
|
|
13914
|
+
gap: 9px;
|
|
13915
|
+
padding: 10px;
|
|
13916
|
+
border-radius: 8px;
|
|
13917
|
+
border: 1px solid rgba(148, 163, 184, 0.28);
|
|
13918
|
+
background: rgba(255, 255, 255, 0.86);
|
|
13919
|
+
`;
|
|
13920
|
+
|
|
13921
|
+
if (!device) {
|
|
13922
|
+
const empty = document.createElement('div');
|
|
13923
|
+
empty.textContent = 'Select a screen';
|
|
13924
|
+
empty.style.cssText = 'display:flex;align-items:center;justify-content:center;min-height:120px;color:#64748b;font-size:12px;font-weight:900;';
|
|
13925
|
+
panel.appendChild(empty);
|
|
13926
|
+
return panel;
|
|
13927
|
+
}
|
|
13928
|
+
|
|
13929
|
+
const deviceId = getRemoteFleetDeviceId(device);
|
|
13930
|
+
const name = getRemoteFleetDeviceName(device);
|
|
13931
|
+
const connectedDevice = isRemoteFleetDeviceConnected(device);
|
|
13932
|
+
const platform = [device?.platform || device?.Platform, device?.arch || device?.Arch]
|
|
13933
|
+
.filter(Boolean)
|
|
13934
|
+
.join(' / ') || 'unknown';
|
|
13935
|
+
const release = String(device?.release || device?.Release || '');
|
|
13936
|
+
const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
|
|
13937
|
+
const liveStreamEnabled = device?.liveStreamEnabled === true || device?.LiveStreamEnabled === true;
|
|
13938
|
+
const liveStreamActive = isRemoteFleetLiveActive(device);
|
|
13939
|
+
const liveStreamId = String(device?.liveStreamId || device?.LiveStreamId || '');
|
|
13940
|
+
const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
|
|
13941
|
+
const aiAssistEnabled = isRemoteFleetDeviceAiCapable(device);
|
|
13942
|
+
const aiModel = String(device?.aiModel || device?.AiModel || '');
|
|
13943
|
+
const latestTaskStatus = String(device?.latestTaskStatus || device?.LatestTaskStatus || '');
|
|
13944
|
+
const latestTaskTitle = String(device?.latestTaskTitle || device?.LatestTaskTitle || '');
|
|
13945
|
+
const latestTaskApproval = String(device?.latestTaskApprovalLevel || device?.LatestTaskApprovalLevel || '');
|
|
13946
|
+
const latestTaskUpdatedAt = String(device?.latestTaskUpdatedAt || device?.LatestTaskUpdatedAt || '');
|
|
13947
|
+
const latestTaskError = String(device?.latestTaskError || device?.LatestTaskError || '');
|
|
13948
|
+
const latestTaskResultModel = String(device?.latestTaskResultModel || device?.LatestTaskResultModel || '');
|
|
13949
|
+
const latestTaskResultResponseId = String(device?.latestTaskResultResponseId || device?.LatestTaskResultResponseId || '');
|
|
13950
|
+
const latestTaskResult = String(device?.latestTaskResultSummary || device?.LatestTaskResultSummary || '');
|
|
13951
|
+
const statusText = connectedDevice
|
|
13952
|
+
? (liveStreamActive ? 'Live' : (aiAssistEnabled ? `AI ${aiModel || 'ready'}` : 'Connected'))
|
|
13953
|
+
: 'Offline';
|
|
13954
|
+
|
|
13955
|
+
panel.dataset.deviceId = deviceId;
|
|
13956
|
+
panel.appendChild(createDevicePreview(device, 'detail'));
|
|
13957
|
+
|
|
13958
|
+
const header = document.createElement('div');
|
|
13959
|
+
header.style.cssText = 'display:flex;align-items:flex-start;justify-content:space-between;gap:8px;min-width:0;';
|
|
13960
|
+
const titleBox = document.createElement('div');
|
|
13961
|
+
titleBox.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:3px;';
|
|
13962
|
+
const title = document.createElement('strong');
|
|
13963
|
+
title.textContent = name;
|
|
13964
|
+
title.title = name;
|
|
13965
|
+
title.style.cssText = 'color:#0f172a;font-size:14px;font-weight:950;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
|
|
13966
|
+
const subtitle = document.createElement('span');
|
|
13967
|
+
subtitle.textContent = release ? `${platform} ${release}` : platform;
|
|
13968
|
+
subtitle.title = subtitle.textContent;
|
|
13969
|
+
subtitle.style.cssText = 'color:#64748b;font-size:11px;font-weight:750;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
|
|
13970
|
+
titleBox.appendChild(title);
|
|
13971
|
+
titleBox.appendChild(subtitle);
|
|
13972
|
+
const status = document.createElement('span');
|
|
13973
|
+
status.textContent = statusText;
|
|
13974
|
+
status.style.cssText = `
|
|
13975
|
+
flex: 0 0 auto;
|
|
13976
|
+
max-width: 82px;
|
|
13977
|
+
padding: 4px 7px;
|
|
13978
|
+
border-radius: 999px;
|
|
13979
|
+
background: ${liveStreamActive ? 'rgba(254, 226, 226, 0.92)' : (connectedDevice ? 'rgba(209, 250, 229, 0.92)' : 'rgba(226, 232, 240, 0.92)')};
|
|
13980
|
+
color: ${liveStreamActive ? '#b91c1c' : (connectedDevice ? '#047857' : '#475569')};
|
|
13981
|
+
font-size: 9px;
|
|
13982
|
+
font-weight: 950;
|
|
13983
|
+
line-height: 1;
|
|
13984
|
+
overflow: hidden;
|
|
13985
|
+
text-overflow: ellipsis;
|
|
13986
|
+
white-space: nowrap;
|
|
13987
|
+
letter-spacing: 0;
|
|
13988
|
+
`;
|
|
13989
|
+
header.appendChild(titleBox);
|
|
13990
|
+
header.appendChild(status);
|
|
13991
|
+
panel.appendChild(header);
|
|
13992
|
+
|
|
13993
|
+
const metrics = document.createElement('div');
|
|
13994
|
+
metrics.style.cssText = 'display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;';
|
|
13995
|
+
const addDetailMetric = (label, value) => {
|
|
13996
|
+
const metric = document.createElement('div');
|
|
13997
|
+
metric.style.cssText = 'min-width:0;padding:7px 8px;border-radius:7px;background:rgba(241,245,249,0.86);border:1px solid rgba(148,163,184,0.16);';
|
|
13998
|
+
const labelEl = document.createElement('div');
|
|
13999
|
+
labelEl.textContent = label;
|
|
14000
|
+
labelEl.style.cssText = 'color:#64748b;font-size:9px;font-weight:850;letter-spacing:0;text-transform:uppercase;';
|
|
14001
|
+
const valueEl = document.createElement('div');
|
|
14002
|
+
valueEl.textContent = value;
|
|
14003
|
+
valueEl.title = value;
|
|
14004
|
+
valueEl.style.cssText = 'color:#0f172a;font-size:12px;font-weight:950;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;';
|
|
14005
|
+
metric.appendChild(labelEl);
|
|
14006
|
+
metric.appendChild(valueEl);
|
|
14007
|
+
metrics.appendChild(metric);
|
|
14008
|
+
};
|
|
14009
|
+
addDetailMetric('Seen', formatRemoteFleetAge(device?.lastSeenAt || device?.LastSeenAt));
|
|
14010
|
+
addDetailMetric('Uptime', formatRemoteFleetDuration(device?.uptimeSec ?? device?.UptimeSec));
|
|
14011
|
+
addDetailMetric('Mem', formatRemoteFleetPercent(device?.usedMemRatio ?? device?.UsedMemRatio));
|
|
14012
|
+
addDetailMetric('Load', formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2));
|
|
14013
|
+
panel.appendChild(metrics);
|
|
14014
|
+
|
|
14015
|
+
if (latestTaskStatus || latestTaskTitle || latestTaskResult || latestTaskError) {
|
|
14016
|
+
const taskBox = document.createElement('div');
|
|
14017
|
+
const taskTone = latestTaskError || latestTaskStatus === 'failed'
|
|
14018
|
+
? 'error'
|
|
14019
|
+
: (latestTaskStatus === 'completed' ? 'done' : 'pending');
|
|
14020
|
+
taskBox.style.cssText = `
|
|
14021
|
+
display: flex;
|
|
14022
|
+
flex-direction: column;
|
|
14023
|
+
gap: 4px;
|
|
14024
|
+
min-width: 0;
|
|
14025
|
+
padding: 8px 9px;
|
|
14026
|
+
border-radius: 7px;
|
|
14027
|
+
background: ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.12)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(37, 99, 235, 0.08)'};
|
|
14028
|
+
border: 1px solid ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.24)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.20)' : 'rgba(37, 99, 235, 0.16)'};
|
|
14029
|
+
`;
|
|
14030
|
+
const taskLine = document.createElement('div');
|
|
14031
|
+
const taskModeLabel = latestTaskApproval === 'ai-assist' ? 'AI' : 'Task';
|
|
14032
|
+
taskLine.textContent = `${taskModeLabel} ${latestTaskStatus || 'task'}${latestTaskUpdatedAt ? ` - ${formatRemoteFleetAge(latestTaskUpdatedAt)}` : ''}${latestTaskResultModel ? ` - ${latestTaskResultModel}` : ''}`;
|
|
14033
|
+
taskLine.title = latestTaskResultResponseId
|
|
14034
|
+
? `${taskLine.textContent} (${latestTaskResultResponseId})`
|
|
14035
|
+
: taskLine.textContent;
|
|
14036
|
+
taskLine.style.cssText = `color:${taskTone === 'error' ? '#991b1b' : taskTone === 'done' ? '#047857' : '#1d4ed8'};font-size:10px;font-weight:950;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:0;`;
|
|
14037
|
+
const taskSummary = document.createElement('div');
|
|
14038
|
+
taskSummary.textContent = formatRemoteFleetTaskError(latestTaskError) || latestTaskResult || latestTaskTitle || 'Task queued';
|
|
14039
|
+
taskSummary.title = taskSummary.textContent;
|
|
14040
|
+
taskSummary.style.cssText = 'color:#334155;font-size:10px;font-weight:750;line-height:1.25;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;letter-spacing:0;';
|
|
14041
|
+
taskBox.appendChild(taskLine);
|
|
14042
|
+
taskBox.appendChild(taskSummary);
|
|
14043
|
+
panel.appendChild(taskBox);
|
|
14044
|
+
}
|
|
14045
|
+
|
|
14046
|
+
const actions = document.createElement('div');
|
|
14047
|
+
actions.style.cssText = 'display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:auto;';
|
|
14048
|
+
if (deviceId) {
|
|
14049
|
+
const pinButton = createRemoteFleetButton('Pin', 'Pin this device as a canvas node', 'pin-device');
|
|
14050
|
+
pinButton.dataset.deviceId = deviceId;
|
|
14051
|
+
actions.appendChild(pinButton);
|
|
14052
|
+
}
|
|
14053
|
+
if (connectedDevice && deviceId) {
|
|
14054
|
+
const focusButton = createRemoteFleetButton('Focus', 'Show this device in focused live panel', 'live-focus');
|
|
14055
|
+
focusButton.dataset.deviceId = deviceId;
|
|
14056
|
+
actions.appendChild(focusButton);
|
|
14057
|
+
if (liveStreamEnabled) {
|
|
14058
|
+
const liveButton = createRemoteFleetButton(liveStreamActive ? 'Stop' : 'Live', liveStreamActive ? 'Stop live stream' : 'Start focused live stream', liveStreamActive ? 'live-stop' : 'live-start');
|
|
14059
|
+
liveButton.dataset.deviceId = deviceId;
|
|
14060
|
+
liveButton.dataset.streamId = liveStreamId;
|
|
14061
|
+
if (liveStreamActive) {
|
|
14062
|
+
liveButton.style.borderColor = 'rgba(220, 38, 38, 0.32)';
|
|
14063
|
+
liveButton.style.color = '#b91c1c';
|
|
14064
|
+
}
|
|
14065
|
+
actions.appendChild(liveButton);
|
|
14066
|
+
}
|
|
14067
|
+
if (thumbnailEnabled) {
|
|
14068
|
+
const thumbnailButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
|
|
14069
|
+
thumbnailButton.dataset.deviceId = deviceId;
|
|
14070
|
+
actions.appendChild(thumbnailButton);
|
|
14071
|
+
}
|
|
14072
|
+
const pingButton = createRemoteFleetButton('Ping', 'Ping device', 'ping-device');
|
|
14073
|
+
pingButton.dataset.deviceId = deviceId;
|
|
14074
|
+
actions.appendChild(pingButton);
|
|
14075
|
+
}
|
|
14076
|
+
panel.appendChild(actions);
|
|
14077
|
+
return panel;
|
|
14078
|
+
};
|
|
14079
|
+
|
|
14080
|
+
const monitorWorkspace = document.createElement('div');
|
|
14081
|
+
monitorWorkspace.dataset.remoteFleetMonitorWorkspace = 'true';
|
|
14082
|
+
monitorWorkspace.style.cssText = `
|
|
14083
|
+
flex: 1 1 auto;
|
|
14084
|
+
min-height: 0;
|
|
14085
|
+
overflow: hidden;
|
|
14086
|
+
display: grid;
|
|
14087
|
+
grid-template-columns: minmax(0, 1fr) minmax(230px, 270px);
|
|
14088
|
+
gap: 10px;
|
|
14089
|
+
align-items: stretch;
|
|
14090
|
+
`;
|
|
14091
|
+
|
|
13629
14092
|
const grid = document.createElement('div');
|
|
14093
|
+
grid.dataset.remoteFleetDeviceGrid = 'true';
|
|
13630
14094
|
grid.style.cssText = `
|
|
13631
|
-
flex: 1 1 auto;
|
|
13632
14095
|
min-height: 0;
|
|
13633
14096
|
overflow-y: auto;
|
|
13634
14097
|
overflow-x: hidden;
|
|
13635
14098
|
display: grid;
|
|
13636
|
-
grid-template-columns: ${densityState === 'dense' ? 'repeat(auto-fill, minmax(
|
|
14099
|
+
grid-template-columns: ${densityState === 'dense' ? 'repeat(auto-fill, minmax(118px, 1fr))' : 'repeat(auto-fill, minmax(148px, 1fr))'};
|
|
13637
14100
|
align-content: start;
|
|
13638
14101
|
gap: 8px;
|
|
13639
14102
|
padding-right: 4px;
|
|
13640
14103
|
`;
|
|
13641
14104
|
|
|
13642
|
-
if (devices.length
|
|
13643
|
-
const empty = document.createElement('div');
|
|
13644
|
-
empty.textContent = 'No devices connected yet.';
|
|
13645
|
-
empty.style.cssText = `
|
|
13646
|
-
grid-column: 1 / -1;
|
|
13647
|
-
display: flex;
|
|
13648
|
-
align-items: center;
|
|
13649
|
-
min-height: 94px;
|
|
13650
|
-
padding: 14px;
|
|
13651
|
-
border-radius: 8px;
|
|
13652
|
-
border: 1px dashed rgba(100, 116, 139, 0.36);
|
|
13653
|
-
color: #475569;
|
|
13654
|
-
font-size: 13px;
|
|
13655
|
-
font-weight: 800;
|
|
13656
|
-
background: rgba(255, 255, 255, 0.74);
|
|
13657
|
-
`;
|
|
13658
|
-
grid.appendChild(empty);
|
|
13659
|
-
} else {
|
|
14105
|
+
if (devices.length > 0) {
|
|
13660
14106
|
const groupStats = new Map();
|
|
13661
14107
|
if (groupState !== 'none') {
|
|
13662
14108
|
devices.forEach(device => {
|
|
@@ -13679,32 +14125,15 @@
|
|
|
13679
14125
|
devices.forEach(device => {
|
|
13680
14126
|
const connectedDevice = isRemoteFleetDeviceConnected(device);
|
|
13681
14127
|
const name = getRemoteFleetDeviceName(device);
|
|
13682
|
-
const platform = [device?.platform || device?.Platform, device?.arch || device?.Arch]
|
|
13683
|
-
.filter(Boolean)
|
|
13684
|
-
.join(' / ') || 'unknown';
|
|
13685
|
-
const release = String(device?.release || device?.Release || '');
|
|
13686
14128
|
const deviceId = getRemoteFleetDeviceId(device);
|
|
13687
|
-
const thumbnailEnabled = device?.thumbnailEnabled === true || device?.ThumbnailEnabled === true;
|
|
13688
|
-
const thumbnailDataUrl = String(device?.thumbnailDataUrl || device?.ThumbnailDataUrl || '');
|
|
13689
|
-
const thumbnailCapturedAt = String(device?.thumbnailCapturedAt || device?.ThumbnailCapturedAt || '');
|
|
13690
14129
|
const liveStreamEnabled = device?.liveStreamEnabled === true || device?.LiveStreamEnabled === true;
|
|
13691
14130
|
const liveStreamActive = isRemoteFleetLiveActive(device);
|
|
13692
|
-
const liveStreamId = String(device?.liveStreamId || device?.LiveStreamId || '');
|
|
13693
|
-
const liveFrameDataUrl = String(device?.liveFrameDataUrl || device?.LiveFrameDataUrl || '');
|
|
13694
|
-
const liveFrameReceivedAt = String(device?.liveFrameReceivedAt || device?.LiveFrameReceivedAt || '');
|
|
13695
14131
|
const hasThumbnail = hasRemoteFleetThumbnail(device);
|
|
13696
14132
|
const hasLiveFrame = hasRemoteFleetLiveFrame(device);
|
|
13697
14133
|
const taskEnabled = isRemoteFleetDeviceTaskCapable(device);
|
|
13698
14134
|
const aiAssistEnabled = isRemoteFleetDeviceAiCapable(device);
|
|
13699
|
-
const aiModel = String(device?.aiModel || device?.AiModel || '');
|
|
13700
14135
|
const latestTaskStatus = String(device?.latestTaskStatus || device?.LatestTaskStatus || '');
|
|
13701
|
-
const latestTaskTitle = String(device?.latestTaskTitle || device?.LatestTaskTitle || '');
|
|
13702
|
-
const latestTaskApproval = String(device?.latestTaskApprovalLevel || device?.LatestTaskApprovalLevel || '');
|
|
13703
|
-
const latestTaskUpdatedAt = String(device?.latestTaskUpdatedAt || device?.LatestTaskUpdatedAt || '');
|
|
13704
14136
|
const latestTaskError = String(device?.latestTaskError || device?.LatestTaskError || '');
|
|
13705
|
-
const latestTaskResultModel = String(device?.latestTaskResultModel || device?.LatestTaskResultModel || '');
|
|
13706
|
-
const latestTaskResultResponseId = String(device?.latestTaskResultResponseId || device?.LatestTaskResultResponseId || '');
|
|
13707
|
-
const latestTaskResult = String(device?.latestTaskResultSummary || device?.LatestTaskResultSummary || '');
|
|
13708
14137
|
const groupInfo = getRemoteFleetGroupInfo(device, groupState);
|
|
13709
14138
|
if (groupState !== 'none' && groupInfo.key && groupInfo.key !== lastGroupKey) {
|
|
13710
14139
|
const stat = groupStats.get(groupInfo.key) || { label: groupInfo.label, total: 0, connected: 0 };
|
|
@@ -13739,6 +14168,8 @@
|
|
|
13739
14168
|
grid.appendChild(groupHeader);
|
|
13740
14169
|
lastGroupKey = groupInfo.key;
|
|
13741
14170
|
}
|
|
14171
|
+
const selectedDeviceId = selectedDevice ? getRemoteFleetDeviceId(selectedDevice) : '';
|
|
14172
|
+
const isSelected = deviceId && deviceId === selectedDeviceId;
|
|
13742
14173
|
const card = document.createElement('article');
|
|
13743
14174
|
card.dataset.deviceId = deviceId;
|
|
13744
14175
|
card.dataset.remoteFleetSearchText = buildRemoteFleetSearchText(device);
|
|
@@ -13750,292 +14181,28 @@
|
|
|
13750
14181
|
card.dataset.remoteFleetLiveCapable = liveStreamEnabled ? 'true' : 'false';
|
|
13751
14182
|
card.dataset.remoteFleetLiveActive = liveStreamActive ? 'true' : 'false';
|
|
13752
14183
|
card.dataset.remoteFleetIssue = (!connectedDevice || latestTaskStatus === 'failed' || !!latestTaskError) ? 'true' : 'false';
|
|
14184
|
+
card.dataset.remoteFleetSelected = isSelected ? 'true' : 'false';
|
|
14185
|
+
card.dataset.remoteFleetAction = 'select-device';
|
|
14186
|
+
card.setAttribute('role', 'button');
|
|
14187
|
+
card.setAttribute('aria-label', `Show details for ${name}`);
|
|
14188
|
+
card.tabIndex = 0;
|
|
14189
|
+
card.title = name;
|
|
13753
14190
|
card.style.cssText = `
|
|
14191
|
+
position: relative;
|
|
13754
14192
|
display: flex;
|
|
13755
|
-
|
|
13756
|
-
gap: ${densityState === 'dense' ? '6px' : '8px'};
|
|
14193
|
+
align-items: stretch;
|
|
13757
14194
|
min-width: 0;
|
|
13758
|
-
min-height: ${densityState === 'dense' ? '
|
|
13759
|
-
padding:
|
|
14195
|
+
min-height: ${densityState === 'dense' ? '66px' : '84px'};
|
|
14196
|
+
padding: 2px;
|
|
13760
14197
|
border-radius: 8px;
|
|
13761
14198
|
background: #ffffff;
|
|
13762
|
-
border:
|
|
13763
|
-
box-shadow: 0 8px
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
const previewDataUrl = hasLiveFrame ? liveFrameDataUrl : thumbnailDataUrl;
|
|
13768
|
-
const previewAt = hasLiveFrame ? liveFrameReceivedAt : thumbnailCapturedAt;
|
|
13769
|
-
const preview = document.createElement('div');
|
|
13770
|
-
preview.style.cssText = `
|
|
13771
|
-
position: relative;
|
|
13772
|
-
width: 100%;
|
|
13773
|
-
aspect-ratio: 16 / 9;
|
|
13774
|
-
overflow: hidden;
|
|
13775
|
-
border-radius: 7px;
|
|
13776
|
-
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
13777
|
-
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
13778
|
-
`;
|
|
13779
|
-
if (hasLiveFrame || hasThumbnail) {
|
|
13780
|
-
const image = document.createElement('img');
|
|
13781
|
-
image.src = previewDataUrl;
|
|
13782
|
-
image.alt = hasLiveFrame ? `${name} live frame` : `${name} thumbnail`;
|
|
13783
|
-
image.loading = 'lazy';
|
|
13784
|
-
image.decoding = 'async';
|
|
13785
|
-
image.style.cssText = `
|
|
13786
|
-
width: 100%;
|
|
13787
|
-
height: 100%;
|
|
13788
|
-
object-fit: cover;
|
|
13789
|
-
display: block;
|
|
13790
|
-
`;
|
|
13791
|
-
preview.appendChild(image);
|
|
13792
|
-
} else {
|
|
13793
|
-
const placeholder = document.createElement('div');
|
|
13794
|
-
placeholder.textContent = thumbnailEnabled ? 'No frame yet' : 'Status only';
|
|
13795
|
-
placeholder.style.cssText = `
|
|
13796
|
-
position: absolute;
|
|
13797
|
-
inset: 0;
|
|
13798
|
-
display: flex;
|
|
13799
|
-
align-items: center;
|
|
13800
|
-
justify-content: center;
|
|
13801
|
-
color: rgba(226, 232, 240, 0.78);
|
|
13802
|
-
font-size: 11px;
|
|
13803
|
-
font-weight: 900;
|
|
13804
|
-
letter-spacing: 0;
|
|
13805
|
-
`;
|
|
13806
|
-
preview.appendChild(placeholder);
|
|
13807
|
-
}
|
|
13808
|
-
if (previewAt) {
|
|
13809
|
-
const badge = document.createElement('span');
|
|
13810
|
-
badge.textContent = hasLiveFrame
|
|
13811
|
-
? `LIVE ${formatRemoteFleetAge(previewAt)}`
|
|
13812
|
-
: formatRemoteFleetAge(previewAt);
|
|
13813
|
-
badge.style.cssText = `
|
|
13814
|
-
position: absolute;
|
|
13815
|
-
right: 6px;
|
|
13816
|
-
bottom: 6px;
|
|
13817
|
-
max-width: calc(100% - 12px);
|
|
13818
|
-
padding: 3px 6px;
|
|
13819
|
-
border-radius: 999px;
|
|
13820
|
-
background: ${hasLiveFrame ? 'rgba(220, 38, 38, 0.82)' : 'rgba(15, 23, 42, 0.74)'};
|
|
13821
|
-
color: #e2e8f0;
|
|
13822
|
-
font-size: 9px;
|
|
13823
|
-
font-weight: 900;
|
|
13824
|
-
line-height: 1;
|
|
13825
|
-
overflow: hidden;
|
|
13826
|
-
text-overflow: ellipsis;
|
|
13827
|
-
white-space: nowrap;
|
|
13828
|
-
`;
|
|
13829
|
-
preview.appendChild(badge);
|
|
13830
|
-
}
|
|
13831
|
-
card.appendChild(preview);
|
|
13832
|
-
}
|
|
13833
|
-
|
|
13834
|
-
const cardHeader = document.createElement('div');
|
|
13835
|
-
cardHeader.style.cssText = 'display:flex;align-items:flex-start;gap:8px;min-width:0;';
|
|
13836
|
-
const dot = document.createElement('span');
|
|
13837
|
-
dot.style.cssText = `
|
|
13838
|
-
flex: 0 0 auto;
|
|
13839
|
-
width: 9px;
|
|
13840
|
-
height: 9px;
|
|
13841
|
-
margin-top: 4px;
|
|
13842
|
-
border-radius: 999px;
|
|
13843
|
-
background: ${connectedDevice ? '#10b981' : '#94a3b8'};
|
|
13844
|
-
box-shadow: 0 0 0 4px ${connectedDevice ? 'rgba(16,185,129,0.14)' : 'rgba(148,163,184,0.14)'};
|
|
13845
|
-
`;
|
|
13846
|
-
|
|
13847
|
-
const titleBox = document.createElement('div');
|
|
13848
|
-
titleBox.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:2px;';
|
|
13849
|
-
const title = document.createElement('strong');
|
|
13850
|
-
title.textContent = name;
|
|
13851
|
-
title.title = name;
|
|
13852
|
-
title.style.cssText = `
|
|
13853
|
-
color: #0f172a;
|
|
13854
|
-
font-size: 13px;
|
|
13855
|
-
font-weight: 900;
|
|
13856
|
-
line-height: 1.2;
|
|
13857
|
-
overflow: hidden;
|
|
13858
|
-
text-overflow: ellipsis;
|
|
13859
|
-
white-space: nowrap;
|
|
13860
|
-
letter-spacing: 0;
|
|
13861
|
-
`;
|
|
13862
|
-
const subtitle = document.createElement('span');
|
|
13863
|
-
subtitle.textContent = release ? `${platform} ${release}` : platform;
|
|
13864
|
-
subtitle.title = subtitle.textContent;
|
|
13865
|
-
subtitle.style.cssText = `
|
|
13866
|
-
color: #64748b;
|
|
13867
|
-
font-size: 11px;
|
|
13868
|
-
line-height: 1.2;
|
|
13869
|
-
overflow: hidden;
|
|
13870
|
-
text-overflow: ellipsis;
|
|
13871
|
-
white-space: nowrap;
|
|
13872
|
-
letter-spacing: 0;
|
|
13873
|
-
`;
|
|
13874
|
-
titleBox.appendChild(title);
|
|
13875
|
-
titleBox.appendChild(subtitle);
|
|
13876
|
-
cardHeader.appendChild(dot);
|
|
13877
|
-
cardHeader.appendChild(titleBox);
|
|
13878
|
-
card.appendChild(cardHeader);
|
|
13879
|
-
|
|
13880
|
-
const metrics = document.createElement('div');
|
|
13881
|
-
metrics.style.cssText = `
|
|
13882
|
-
display: grid;
|
|
13883
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
13884
|
-
gap: 6px;
|
|
13885
|
-
`;
|
|
13886
|
-
const addMetric = (label, value) => {
|
|
13887
|
-
const metric = document.createElement('div');
|
|
13888
|
-
metric.style.cssText = `
|
|
13889
|
-
min-width: 0;
|
|
13890
|
-
padding: 6px 7px;
|
|
13891
|
-
border-radius: 7px;
|
|
13892
|
-
background: rgba(241, 245, 249, 0.84);
|
|
13893
|
-
`;
|
|
13894
|
-
const labelEl = document.createElement('div');
|
|
13895
|
-
labelEl.textContent = label;
|
|
13896
|
-
labelEl.style.cssText = 'color:#64748b;font-size:9px;font-weight:800;letter-spacing:0;text-transform:uppercase;';
|
|
13897
|
-
const valueEl = document.createElement('div');
|
|
13898
|
-
valueEl.textContent = value;
|
|
13899
|
-
valueEl.title = value;
|
|
13900
|
-
valueEl.style.cssText = 'color:#0f172a;font-size:12px;font-weight:900;letter-spacing:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
|
|
13901
|
-
metric.appendChild(labelEl);
|
|
13902
|
-
metric.appendChild(valueEl);
|
|
13903
|
-
metrics.appendChild(metric);
|
|
13904
|
-
};
|
|
13905
|
-
addMetric('Seen', formatRemoteFleetAge(device?.lastSeenAt || device?.LastSeenAt));
|
|
13906
|
-
addMetric('Uptime', formatRemoteFleetDuration(device?.uptimeSec ?? device?.UptimeSec));
|
|
13907
|
-
addMetric('Mem', formatRemoteFleetPercent(device?.usedMemRatio ?? device?.UsedMemRatio));
|
|
13908
|
-
addMetric('Load', formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2));
|
|
13909
|
-
if (densityState === 'dense') {
|
|
13910
|
-
const denseMeta = document.createElement('div');
|
|
13911
|
-
denseMeta.textContent = `Seen ${formatRemoteFleetAge(device?.lastSeenAt || device?.LastSeenAt)} - Mem ${formatRemoteFleetPercent(device?.usedMemRatio ?? device?.UsedMemRatio)} - Load ${formatRemoteFleetNumber(device?.load1 ?? device?.Load1, 2)}`;
|
|
13912
|
-
denseMeta.style.cssText = `
|
|
13913
|
-
color: #475569;
|
|
13914
|
-
font-size: 10px;
|
|
13915
|
-
font-weight: 750;
|
|
13916
|
-
line-height: 1.2;
|
|
13917
|
-
overflow: hidden;
|
|
13918
|
-
text-overflow: ellipsis;
|
|
13919
|
-
white-space: nowrap;
|
|
13920
|
-
letter-spacing: 0;
|
|
13921
|
-
`;
|
|
13922
|
-
card.appendChild(denseMeta);
|
|
13923
|
-
} else {
|
|
13924
|
-
card.appendChild(metrics);
|
|
13925
|
-
}
|
|
13926
|
-
|
|
13927
|
-
if (latestTaskStatus || latestTaskTitle || latestTaskResult || latestTaskError) {
|
|
13928
|
-
const taskBox = document.createElement('div');
|
|
13929
|
-
const taskTone = latestTaskError || latestTaskStatus === 'failed'
|
|
13930
|
-
? 'error'
|
|
13931
|
-
: (latestTaskStatus === 'completed' ? 'done' : 'pending');
|
|
13932
|
-
taskBox.style.cssText = `
|
|
13933
|
-
display: flex;
|
|
13934
|
-
flex-direction: column;
|
|
13935
|
-
gap: 3px;
|
|
13936
|
-
min-width: 0;
|
|
13937
|
-
padding: 7px 8px;
|
|
13938
|
-
border-radius: 7px;
|
|
13939
|
-
background: ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.12)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.10)' : 'rgba(37, 99, 235, 0.08)'};
|
|
13940
|
-
border: 1px solid ${taskTone === 'error' ? 'rgba(248, 113, 113, 0.24)' : taskTone === 'done' ? 'rgba(16, 185, 129, 0.20)' : 'rgba(37, 99, 235, 0.16)'};
|
|
13941
|
-
`;
|
|
13942
|
-
const taskLine = document.createElement('div');
|
|
13943
|
-
const taskModeLabel = latestTaskApproval === 'ai-assist' ? 'AI' : 'Task';
|
|
13944
|
-
taskLine.textContent = `${taskModeLabel} ${latestTaskStatus || 'task'}${latestTaskUpdatedAt ? ` - ${formatRemoteFleetAge(latestTaskUpdatedAt)}` : ''}${latestTaskResultModel ? ` - ${latestTaskResultModel}` : ''}`;
|
|
13945
|
-
taskLine.title = latestTaskResultResponseId
|
|
13946
|
-
? `${taskLine.textContent} (${latestTaskResultResponseId})`
|
|
13947
|
-
: taskLine.textContent;
|
|
13948
|
-
taskLine.style.cssText = `
|
|
13949
|
-
color: ${taskTone === 'error' ? '#991b1b' : taskTone === 'done' ? '#047857' : '#1d4ed8'};
|
|
13950
|
-
font-size: 10px;
|
|
13951
|
-
font-weight: 900;
|
|
13952
|
-
line-height: 1.2;
|
|
13953
|
-
overflow: hidden;
|
|
13954
|
-
text-overflow: ellipsis;
|
|
13955
|
-
white-space: nowrap;
|
|
13956
|
-
letter-spacing: 0;
|
|
13957
|
-
`;
|
|
13958
|
-
const taskSummary = document.createElement('div');
|
|
13959
|
-
taskSummary.textContent = formatRemoteFleetTaskError(latestTaskError) || latestTaskResult || latestTaskTitle || 'Task queued';
|
|
13960
|
-
taskSummary.title = taskSummary.textContent;
|
|
13961
|
-
taskSummary.style.cssText = `
|
|
13962
|
-
color: #334155;
|
|
13963
|
-
font-size: 10px;
|
|
13964
|
-
font-weight: 700;
|
|
13965
|
-
line-height: 1.25;
|
|
13966
|
-
overflow: hidden;
|
|
13967
|
-
display: -webkit-box;
|
|
13968
|
-
-webkit-line-clamp: 2;
|
|
13969
|
-
-webkit-box-orient: vertical;
|
|
13970
|
-
letter-spacing: 0;
|
|
13971
|
-
`;
|
|
13972
|
-
taskBox.appendChild(taskLine);
|
|
13973
|
-
taskBox.appendChild(taskSummary);
|
|
13974
|
-
card.appendChild(taskBox);
|
|
13975
|
-
}
|
|
13976
|
-
|
|
13977
|
-
const actions = document.createElement('div');
|
|
13978
|
-
actions.style.cssText = 'display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:auto;';
|
|
13979
|
-
const status = document.createElement('span');
|
|
13980
|
-
status.textContent = connectedDevice
|
|
13981
|
-
? (liveStreamActive ? 'Live' : (aiAssistEnabled ? `AI ${aiModel || 'ready'}` : 'Connected'))
|
|
13982
|
-
: 'Offline';
|
|
13983
|
-
status.style.cssText = `
|
|
13984
|
-
min-width: 0;
|
|
13985
|
-
color: ${liveStreamActive ? '#b91c1c' : (connectedDevice ? '#047857' : '#64748b')};
|
|
13986
|
-
font-size: 11px;
|
|
13987
|
-
font-weight: 900;
|
|
13988
|
-
overflow: hidden;
|
|
13989
|
-
text-overflow: ellipsis;
|
|
13990
|
-
white-space: nowrap;
|
|
14199
|
+
border: 2px solid ${isSelected ? 'rgba(37, 99, 235, 0.76)' : (connectedDevice ? 'rgba(16, 185, 129, 0.30)' : 'rgba(148, 163, 184, 0.24)')};
|
|
14200
|
+
box-shadow: ${isSelected ? '0 0 0 3px rgba(37, 99, 235, 0.15), 0 10px 22px rgba(15, 23, 42, 0.08)' : '0 8px 18px rgba(15, 23, 42, 0.05)'};
|
|
14201
|
+
cursor: pointer;
|
|
14202
|
+
pointer-events: auto;
|
|
14203
|
+
user-select: none;
|
|
13991
14204
|
`;
|
|
13992
|
-
|
|
13993
|
-
if (deviceId) {
|
|
13994
|
-
const pinButton = createRemoteFleetButton('Pin', 'Pin this device as a canvas node', 'pin-device');
|
|
13995
|
-
pinButton.dataset.deviceId = deviceId;
|
|
13996
|
-
pinButton.style.height = '24px';
|
|
13997
|
-
pinButton.style.fontSize = '10px';
|
|
13998
|
-
actions.appendChild(pinButton);
|
|
13999
|
-
}
|
|
14000
|
-
if (connectedDevice && deviceId) {
|
|
14001
|
-
const focusButton = createRemoteFleetButton('Focus', 'Show this device in focused live panel', 'live-focus');
|
|
14002
|
-
focusButton.dataset.deviceId = deviceId;
|
|
14003
|
-
focusButton.style.height = '24px';
|
|
14004
|
-
focusButton.style.fontSize = '10px';
|
|
14005
|
-
actions.appendChild(focusButton);
|
|
14006
|
-
if (liveStreamEnabled) {
|
|
14007
|
-
const liveButton = createRemoteFleetButton(liveStreamActive ? 'Stop' : 'Live', liveStreamActive ? 'Stop live stream' : 'Start focused live stream', liveStreamActive ? 'live-stop' : 'live-start');
|
|
14008
|
-
liveButton.dataset.deviceId = deviceId;
|
|
14009
|
-
liveButton.dataset.streamId = liveStreamId;
|
|
14010
|
-
liveButton.style.height = '24px';
|
|
14011
|
-
liveButton.style.fontSize = '10px';
|
|
14012
|
-
if (liveStreamActive) {
|
|
14013
|
-
liveButton.style.borderColor = 'rgba(220, 38, 38, 0.32)';
|
|
14014
|
-
liveButton.style.color = '#b91c1c';
|
|
14015
|
-
}
|
|
14016
|
-
actions.appendChild(liveButton);
|
|
14017
|
-
}
|
|
14018
|
-
if (taskEnabled) {
|
|
14019
|
-
const taskButton = createRemoteFleetButton('Task', 'Dispatch task to this device', 'task-device');
|
|
14020
|
-
taskButton.dataset.deviceId = deviceId;
|
|
14021
|
-
taskButton.style.height = '24px';
|
|
14022
|
-
taskButton.style.fontSize = '10px';
|
|
14023
|
-
actions.appendChild(taskButton);
|
|
14024
|
-
}
|
|
14025
|
-
if (thumbnailEnabled) {
|
|
14026
|
-
const thumbnailButton = createRemoteFleetButton('Shot', 'Request thumbnail', 'thumbnail-device');
|
|
14027
|
-
thumbnailButton.dataset.deviceId = deviceId;
|
|
14028
|
-
thumbnailButton.style.height = '24px';
|
|
14029
|
-
thumbnailButton.style.fontSize = '10px';
|
|
14030
|
-
actions.appendChild(thumbnailButton);
|
|
14031
|
-
}
|
|
14032
|
-
const pingButton = createRemoteFleetButton('Ping', 'Ping device', 'ping-device');
|
|
14033
|
-
pingButton.dataset.deviceId = deviceId;
|
|
14034
|
-
pingButton.style.height = '24px';
|
|
14035
|
-
pingButton.style.fontSize = '10px';
|
|
14036
|
-
actions.appendChild(pingButton);
|
|
14037
|
-
}
|
|
14038
|
-
card.appendChild(actions);
|
|
14205
|
+
card.appendChild(createDevicePreview(device, 'tile'));
|
|
14039
14206
|
grid.appendChild(card);
|
|
14040
14207
|
});
|
|
14041
14208
|
|
|
@@ -14058,27 +14225,15 @@
|
|
|
14058
14225
|
grid.appendChild(noMatch);
|
|
14059
14226
|
}
|
|
14060
14227
|
|
|
14061
|
-
|
|
14062
|
-
|
|
14063
|
-
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
line-height: 1.2;
|
|
14071
|
-
overflow: hidden;
|
|
14072
|
-
text-overflow: ellipsis;
|
|
14073
|
-
white-space: nowrap;
|
|
14074
|
-
`;
|
|
14075
|
-
bodyView.appendChild(footer);
|
|
14076
|
-
|
|
14077
|
-
copyButton.addEventListener('click', event => {
|
|
14078
|
-
event.preventDefault();
|
|
14079
|
-
event.stopPropagation();
|
|
14080
|
-
navigator.clipboard?.writeText?.(command).catch(() => { });
|
|
14081
|
-
});
|
|
14228
|
+
if (devices.length === 0) {
|
|
14229
|
+
monitorWorkspace.style.display = 'flex';
|
|
14230
|
+
monitorWorkspace.style.flexDirection = 'column';
|
|
14231
|
+
monitorWorkspace.appendChild(createRemoteFleetEmptyScreens());
|
|
14232
|
+
} else {
|
|
14233
|
+
monitorWorkspace.appendChild(grid);
|
|
14234
|
+
monitorWorkspace.appendChild(createSelectedDevicePanel(selectedDevice));
|
|
14235
|
+
}
|
|
14236
|
+
bodyView.appendChild(monitorWorkspace);
|
|
14082
14237
|
|
|
14083
14238
|
const setTaskFeedback = (message, tone = 'info') => {
|
|
14084
14239
|
taskFeedback.textContent = message || '';
|
|
@@ -14284,9 +14439,12 @@
|
|
|
14284
14439
|
sendConnectedButton.title = allTargetCount > 0
|
|
14285
14440
|
? `Dispatch to ${allTargetCount} connected target(s)`
|
|
14286
14441
|
: 'No connected targetable devices';
|
|
14287
|
-
|
|
14288
|
-
const
|
|
14289
|
-
|
|
14442
|
+
bodyView.querySelectorAll('[data-remote-fleet-action="task-device"]').forEach(button => {
|
|
14443
|
+
const deviceId = String(button.dataset.deviceId || '').trim();
|
|
14444
|
+
const card = getDeviceCards().find(item => String(item.dataset.deviceId || '').trim() === deviceId);
|
|
14445
|
+
const aiCapable = button.dataset.remoteFleetAiCapable === 'true'
|
|
14446
|
+
|| card?.dataset.remoteFleetAiCapable === 'true';
|
|
14447
|
+
button.disabled = wantsAi && !aiCapable;
|
|
14290
14448
|
button.title = button.disabled ? 'AI assist is not enabled on this device' : 'Dispatch task to this device';
|
|
14291
14449
|
});
|
|
14292
14450
|
|
|
@@ -14303,19 +14461,34 @@
|
|
|
14303
14461
|
control.addEventListener(eventName, event => event.stopPropagation());
|
|
14304
14462
|
});
|
|
14305
14463
|
});
|
|
14306
|
-
[
|
|
14464
|
+
[hostButton, stopHostButton].forEach(control => {
|
|
14465
|
+
if (!control) return;
|
|
14307
14466
|
['mousedown', 'mouseup', 'click', 'dblclick', 'keydown'].forEach(eventName => {
|
|
14308
14467
|
control.addEventListener(eventName, event => event.stopPropagation());
|
|
14309
14468
|
});
|
|
14310
14469
|
});
|
|
14470
|
+
getDeviceCards().forEach(card => {
|
|
14471
|
+
['mousedown', 'mouseup', 'dblclick'].forEach(eventName => {
|
|
14472
|
+
card.addEventListener(eventName, event => event.stopPropagation());
|
|
14473
|
+
});
|
|
14474
|
+
const selectCard = event => {
|
|
14475
|
+
event.preventDefault();
|
|
14476
|
+
event.stopPropagation();
|
|
14477
|
+
const deviceId = String(card.dataset.deviceId || '').trim();
|
|
14478
|
+
if (!deviceId) return;
|
|
14479
|
+
bodyView.dataset.remoteFleetSelectedDeviceId = deviceId;
|
|
14480
|
+
renderRemoteFleetMonitor(bodyView, nodeModel);
|
|
14481
|
+
};
|
|
14482
|
+
card.addEventListener('click', selectCard);
|
|
14483
|
+
card.addEventListener('keydown', event => {
|
|
14484
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
14485
|
+
selectCard(event);
|
|
14486
|
+
});
|
|
14487
|
+
});
|
|
14311
14488
|
|
|
14312
14489
|
searchInput.addEventListener('input', applyRemoteFleetFilters);
|
|
14313
14490
|
filterSelect.addEventListener('change', applyRemoteFleetFilters);
|
|
14314
14491
|
aiToggle.addEventListener('change', applyRemoteFleetFilters);
|
|
14315
|
-
autoMonitorToggle.addEventListener('change', () => {
|
|
14316
|
-
bodyView.dataset.remoteFleetAutoMonitor = autoMonitorToggle.checked ? 'true' : 'false';
|
|
14317
|
-
renderRemoteFleetMonitor(bodyView, nodeModel);
|
|
14318
|
-
});
|
|
14319
14492
|
sortSelect.addEventListener('change', () => {
|
|
14320
14493
|
bodyView.dataset.remoteFleetSort = String(sortSelect.value || 'status');
|
|
14321
14494
|
renderRemoteFleetMonitor(bodyView, nodeModel);
|
|
@@ -14387,18 +14560,61 @@
|
|
|
14387
14560
|
}
|
|
14388
14561
|
});
|
|
14389
14562
|
|
|
14390
|
-
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14563
|
+
const setRemoteFleetHostTarget = async (enabled, options = {}) => {
|
|
14564
|
+
const quiet = options?.quiet === true;
|
|
14565
|
+
const activeButton = enabled ? hostButton : stopHostButton;
|
|
14566
|
+
if (!quiet && activeButton) {
|
|
14567
|
+
activeButton.disabled = true;
|
|
14568
|
+
}
|
|
14569
|
+
|
|
14570
|
+
if (!quiet) {
|
|
14571
|
+
setTaskFeedback(enabled ? 'Setting host target...' : 'Stopping host target...');
|
|
14572
|
+
}
|
|
14394
14573
|
try {
|
|
14395
|
-
await
|
|
14574
|
+
const result = await invokeDotNetAsync('SetRemoteFleetHostFromJs', nodeId, enabled === true);
|
|
14575
|
+
window.RuntimeTrace?.emit?.('remote.hostTarget.result', {
|
|
14576
|
+
nodeId,
|
|
14577
|
+
enabled: enabled === true,
|
|
14578
|
+
success: isRemoteFleetResultSuccess(result),
|
|
14579
|
+
active: isRemoteFleetResultActive(result),
|
|
14580
|
+
error: result?.error || result?.Error || ''
|
|
14581
|
+
});
|
|
14582
|
+
rememberRemoteFleetLocalHostTarget(nodeId, enabled === true, result);
|
|
14583
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
14584
|
+
if (quiet) {
|
|
14585
|
+
return result;
|
|
14586
|
+
}
|
|
14587
|
+
if (isRemoteFleetResultSuccess(result) && enabled !== true) {
|
|
14588
|
+
stopRemoteFleetHostLeaseTimer(nodeId);
|
|
14589
|
+
}
|
|
14590
|
+
if (isRemoteFleetResultSuccess(result)) {
|
|
14591
|
+
setTaskFeedback(enabled ? 'Host target set.' : 'Host target stopped.', 'success');
|
|
14592
|
+
} else {
|
|
14593
|
+
setTaskFeedback(result?.error || result?.Error || 'Host target update failed.', 'error');
|
|
14594
|
+
}
|
|
14595
|
+
return result;
|
|
14396
14596
|
} finally {
|
|
14397
|
-
|
|
14597
|
+
if (!quiet && activeButton) {
|
|
14598
|
+
activeButton.disabled = false;
|
|
14599
|
+
}
|
|
14398
14600
|
}
|
|
14601
|
+
};
|
|
14602
|
+
|
|
14603
|
+
hostButton.addEventListener('click', async event => {
|
|
14604
|
+
event.preventDefault();
|
|
14605
|
+
event.stopPropagation();
|
|
14606
|
+
await setRemoteFleetHostTarget(true);
|
|
14399
14607
|
});
|
|
14400
14608
|
|
|
14401
|
-
|
|
14609
|
+
if (stopHostButton) {
|
|
14610
|
+
stopHostButton.addEventListener('click', async event => {
|
|
14611
|
+
event.preventDefault();
|
|
14612
|
+
event.stopPropagation();
|
|
14613
|
+
await setRemoteFleetHostTarget(false);
|
|
14614
|
+
});
|
|
14615
|
+
}
|
|
14616
|
+
|
|
14617
|
+
bodyView.querySelectorAll('[data-remote-fleet-action="pin-device"]').forEach(button => {
|
|
14402
14618
|
button.addEventListener('click', async event => {
|
|
14403
14619
|
event.preventDefault();
|
|
14404
14620
|
event.stopPropagation();
|
|
@@ -14476,7 +14692,7 @@
|
|
|
14476
14692
|
});
|
|
14477
14693
|
});
|
|
14478
14694
|
|
|
14479
|
-
|
|
14695
|
+
bodyView.querySelectorAll('[data-remote-fleet-action="ping-device"]').forEach(button => {
|
|
14480
14696
|
button.addEventListener('click', async event => {
|
|
14481
14697
|
event.preventDefault();
|
|
14482
14698
|
event.stopPropagation();
|
|
@@ -14487,7 +14703,7 @@
|
|
|
14487
14703
|
});
|
|
14488
14704
|
});
|
|
14489
14705
|
|
|
14490
|
-
|
|
14706
|
+
bodyView.querySelectorAll('[data-remote-fleet-action="task-device"]').forEach(button => {
|
|
14491
14707
|
button.addEventListener('click', async event => {
|
|
14492
14708
|
event.preventDefault();
|
|
14493
14709
|
event.stopPropagation();
|
|
@@ -14516,7 +14732,7 @@
|
|
|
14516
14732
|
});
|
|
14517
14733
|
});
|
|
14518
14734
|
|
|
14519
|
-
|
|
14735
|
+
bodyView.querySelectorAll('[data-remote-fleet-action="thumbnail-device"]').forEach(button => {
|
|
14520
14736
|
button.addEventListener('click', async event => {
|
|
14521
14737
|
event.preventDefault();
|
|
14522
14738
|
event.stopPropagation();
|
|
@@ -14527,6 +14743,10 @@
|
|
|
14527
14743
|
});
|
|
14528
14744
|
});
|
|
14529
14745
|
|
|
14746
|
+
if (isHostingTarget) {
|
|
14747
|
+
startRemoteFleetHostLeaseTimer(nodeId, () => setRemoteFleetHostTarget(true, { quiet: true }));
|
|
14748
|
+
}
|
|
14749
|
+
|
|
14530
14750
|
if (hasActiveLiveStream) {
|
|
14531
14751
|
bodyView._remoteFleetLiveRefreshTimer = setInterval(async () => {
|
|
14532
14752
|
if (!document.body.contains(bodyView)) {
|