@mindexec/cli 0.2.152 → 0.2.154
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 -5
- package/scripts/remote-fleet-render-smoke.mjs +131 -102
- package/scripts/remote-input-ws-smoke.mjs +263 -0
- package/server.js +249 -0
- package/wwwroot/_content/MindExecution.Shared/js/mind-map-css3d-manager.js +567 -94
- package/wwwroot/_framework/{MindExecution.Plugins.Concept.5gltgdqyo3.dll → MindExecution.Plugins.Concept.4ncya2e794.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.PlanMaster.cn6ppe6qoz.dll → MindExecution.Plugins.PlanMaster.0yfii1nh3p.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Plugins.YouTube.d7nnsxrftv.dll → MindExecution.Plugins.YouTube.y4vxx807fo.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Shared.mnpwk5h86c.dll → MindExecution.Shared.50ptsgk9eg.dll} +0 -0
- package/wwwroot/_framework/{MindExecution.Web.ygql0sg6b3.dll → MindExecution.Web.hztii75ar1.dll} +0 -0
- package/wwwroot/_framework/blazor.boot.json +11 -11
- package/wwwroot/service-worker-assets.js +13 -13
- package/wwwroot/service-worker.js +1 -1
|
@@ -13494,6 +13494,8 @@
|
|
|
13494
13494
|
const REMOTE_FLEET_LIVE_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_MONITOR_LIVE_FPS);
|
|
13495
13495
|
const REMOTE_FLEET_CONTROL_LIVE_FPS = 20;
|
|
13496
13496
|
const REMOTE_FLEET_CONTROL_FRAME_REFRESH_MS = Math.round(1000 / REMOTE_FLEET_CONTROL_LIVE_FPS);
|
|
13497
|
+
const REMOTE_FLEET_CONTROL_INPUT_MOVE_MS = 16;
|
|
13498
|
+
const REMOTE_FLEET_CONTROL_INPUT_QUEUE_LIMIT = 64;
|
|
13497
13499
|
const REMOTE_FLEET_THUMBNAIL_FRAME_REFRESH_MS = 2000;
|
|
13498
13500
|
const REMOTE_FLEET_FRAME_CANVAS_MAX_DPR = 2;
|
|
13499
13501
|
const REMOTE_FLEET_FRAME_BLOB_CACHE_MS = 10000;
|
|
@@ -13728,6 +13730,7 @@
|
|
|
13728
13730
|
|
|
13729
13731
|
function clearRemoteFleetTimers(bodyView, options = {}) {
|
|
13730
13732
|
if (!bodyView) return;
|
|
13733
|
+
closeRemoteFleetControlNodeSession(bodyView, 'clear');
|
|
13731
13734
|
if (options.keepBinaryFrameSocket !== true) {
|
|
13732
13735
|
releaseRemoteFleetBinaryFrameSocket(bodyView);
|
|
13733
13736
|
}
|
|
@@ -13829,6 +13832,22 @@
|
|
|
13829
13832
|
return url.toString();
|
|
13830
13833
|
}
|
|
13831
13834
|
|
|
13835
|
+
function buildRemoteFleetInputWsUrl(status) {
|
|
13836
|
+
const baseUrl = String(status?.__remoteFleetBridgeBaseUrl || window.location?.origin || '').trim();
|
|
13837
|
+
const path = String(status?.remoteInputWsPath || '/api/remote/input/ws').trim() || '/api/remote/input/ws';
|
|
13838
|
+
if (!baseUrl) {
|
|
13839
|
+
return '';
|
|
13840
|
+
}
|
|
13841
|
+
|
|
13842
|
+
const url = new URL(path, baseUrl);
|
|
13843
|
+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
13844
|
+
const bridgeToken = String(status?.bridgeToken || '').trim();
|
|
13845
|
+
if (bridgeToken) {
|
|
13846
|
+
url.searchParams.set('token', bridgeToken);
|
|
13847
|
+
}
|
|
13848
|
+
return url.toString();
|
|
13849
|
+
}
|
|
13850
|
+
|
|
13832
13851
|
async function parseRemoteFleetBinaryFrameMessage(data) {
|
|
13833
13852
|
let buffer = null;
|
|
13834
13853
|
if (data instanceof ArrayBuffer) {
|
|
@@ -15729,6 +15748,8 @@
|
|
|
15729
15748
|
activeRemoteFleetControlPopup = null;
|
|
15730
15749
|
session.active = false;
|
|
15731
15750
|
detachRemoteFleetControlBinaryFrameSession(session);
|
|
15751
|
+
closeRemoteFleetControlInputSocket(session, 'close');
|
|
15752
|
+
session.inputCleanup?.();
|
|
15732
15753
|
if (session.timer) {
|
|
15733
15754
|
clearTimeout(session.timer);
|
|
15734
15755
|
session.timer = null;
|
|
@@ -15847,6 +15868,8 @@
|
|
|
15847
15868
|
|
|
15848
15869
|
drawRemoteFleetFrameToCanvas(session.canvas, bitmap, 'contain');
|
|
15849
15870
|
session.canvas.style.display = 'block';
|
|
15871
|
+
session.placeholder?.remove?.();
|
|
15872
|
+
session.placeholder = null;
|
|
15850
15873
|
if (typeof bitmap.close === 'function') {
|
|
15851
15874
|
bitmap.close();
|
|
15852
15875
|
}
|
|
@@ -15897,6 +15920,8 @@
|
|
|
15897
15920
|
session.canvas.style.display = 'block';
|
|
15898
15921
|
session.lastControlFrameAt = Date.now();
|
|
15899
15922
|
session.status.textContent = `Control ${REMOTE_FLEET_CONTROL_LIVE_FPS}fps`;
|
|
15923
|
+
session.placeholder?.remove?.();
|
|
15924
|
+
session.placeholder = null;
|
|
15900
15925
|
if (typeof bitmap.close === 'function') {
|
|
15901
15926
|
bitmap.close();
|
|
15902
15927
|
}
|
|
@@ -15910,34 +15935,268 @@
|
|
|
15910
15935
|
}
|
|
15911
15936
|
}
|
|
15912
15937
|
|
|
15913
|
-
function
|
|
15914
|
-
if (!session
|
|
15938
|
+
function closeRemoteFleetControlInputSocket(session, reason = 'close') {
|
|
15939
|
+
if (!session) {
|
|
15915
15940
|
return;
|
|
15916
15941
|
}
|
|
15917
15942
|
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
15943
|
+
if (session.inputWsOpenTimer) {
|
|
15944
|
+
clearTimeout(session.inputWsOpenTimer);
|
|
15945
|
+
session.inputWsOpenTimer = null;
|
|
15946
|
+
}
|
|
15947
|
+
|
|
15948
|
+
const ws = session.inputWs;
|
|
15949
|
+
session.inputWs = null;
|
|
15950
|
+
session.inputWsConnecting = false;
|
|
15951
|
+
if (ws && ws.readyState !== 3) {
|
|
15952
|
+
try {
|
|
15953
|
+
ws.close();
|
|
15954
|
+
} catch {
|
|
15955
|
+
// Best-effort input socket cleanup.
|
|
15921
15956
|
}
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
15957
|
+
}
|
|
15958
|
+
|
|
15959
|
+
if (reason === 'close') {
|
|
15960
|
+
session.inputQueue = [];
|
|
15961
|
+
}
|
|
15962
|
+
}
|
|
15963
|
+
|
|
15964
|
+
function createRemoteFleetControlInput(session, payload) {
|
|
15965
|
+
return {
|
|
15966
|
+
...payload,
|
|
15967
|
+
controlLeaseId: session.controlLeaseId,
|
|
15968
|
+
issuedAt: new Date().toISOString()
|
|
15969
|
+
};
|
|
15970
|
+
}
|
|
15971
|
+
|
|
15972
|
+
function sendRemoteFleetControlInputFallback(session, input) {
|
|
15973
|
+
if (!session?.active || !input?.type) {
|
|
15974
|
+
return;
|
|
15975
|
+
}
|
|
15976
|
+
|
|
15977
|
+
invokeDotNetAsync('SendRemoteFleetInputFromJs', session.nodeId, session.deviceId, {
|
|
15978
|
+
...input,
|
|
15979
|
+
controlLeaseId: input.controlLeaseId || session.controlLeaseId,
|
|
15980
|
+
issuedAt: input.issuedAt || new Date().toISOString()
|
|
15981
|
+
}).catch(error => {
|
|
15982
|
+
if (session.active) {
|
|
15927
15983
|
session.status.textContent = error?.message || 'Input failed';
|
|
15984
|
+
}
|
|
15985
|
+
});
|
|
15986
|
+
}
|
|
15987
|
+
|
|
15988
|
+
function drainRemoteFleetControlInputQueueToFallback(session) {
|
|
15989
|
+
if (!session?.inputQueue?.length) {
|
|
15990
|
+
return;
|
|
15991
|
+
}
|
|
15992
|
+
|
|
15993
|
+
const queued = session.inputQueue.splice(0);
|
|
15994
|
+
queued.forEach(envelope => sendRemoteFleetControlInputFallback(session, envelope.input));
|
|
15995
|
+
}
|
|
15996
|
+
|
|
15997
|
+
function enqueueRemoteFleetControlInput(session, envelope) {
|
|
15998
|
+
if (!session) {
|
|
15999
|
+
return;
|
|
16000
|
+
}
|
|
16001
|
+
|
|
16002
|
+
if (!Array.isArray(session.inputQueue)) {
|
|
16003
|
+
session.inputQueue = [];
|
|
16004
|
+
}
|
|
16005
|
+
|
|
16006
|
+
const inputType = String(envelope?.input?.type || '').toLowerCase();
|
|
16007
|
+
if (inputType === 'pointermove') {
|
|
16008
|
+
for (let i = session.inputQueue.length - 1; i >= 0; i -= 1) {
|
|
16009
|
+
if (String(session.inputQueue[i]?.input?.type || '').toLowerCase() === 'pointermove') {
|
|
16010
|
+
session.inputQueue.splice(i, 1);
|
|
16011
|
+
}
|
|
16012
|
+
}
|
|
16013
|
+
}
|
|
16014
|
+
|
|
16015
|
+
session.inputQueue.push(envelope);
|
|
16016
|
+
while (session.inputQueue.length > REMOTE_FLEET_CONTROL_INPUT_QUEUE_LIMIT) {
|
|
16017
|
+
const moveIndex = session.inputQueue.findIndex(item =>
|
|
16018
|
+
String(item?.input?.type || '').toLowerCase() === 'pointermove');
|
|
16019
|
+
session.inputQueue.splice(moveIndex >= 0 ? moveIndex : 0, 1);
|
|
16020
|
+
}
|
|
16021
|
+
}
|
|
16022
|
+
|
|
16023
|
+
function flushRemoteFleetControlInputQueue(session) {
|
|
16024
|
+
const ws = session?.inputWs;
|
|
16025
|
+
if (!session?.active || !ws || ws.readyState !== 1 || !session.inputQueue?.length) {
|
|
16026
|
+
return;
|
|
16027
|
+
}
|
|
16028
|
+
|
|
16029
|
+
const queued = session.inputQueue.splice(0);
|
|
16030
|
+
for (const envelope of queued) {
|
|
16031
|
+
if (!sendRemoteFleetControlInputEnvelope(session, envelope)) {
|
|
16032
|
+
enqueueRemoteFleetControlInput(session, envelope);
|
|
16033
|
+
break;
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
}
|
|
16037
|
+
|
|
16038
|
+
function sendRemoteFleetControlInputEnvelope(session, envelope) {
|
|
16039
|
+
const ws = session?.inputWs;
|
|
16040
|
+
if (!session?.active || !ws || ws.readyState !== 1) {
|
|
16041
|
+
return false;
|
|
16042
|
+
}
|
|
16043
|
+
|
|
16044
|
+
const inputType = String(envelope?.input?.type || '').toLowerCase();
|
|
16045
|
+
if (inputType === 'pointermove' && Number(ws.bufferedAmount || 0) > 256 * 1024) {
|
|
16046
|
+
window.RuntimeTrace?.emit?.('remote.control.input.drop', {
|
|
16047
|
+
reason: 'ws-buffered',
|
|
16048
|
+
deviceId: session.deviceId,
|
|
16049
|
+
bufferedAmount: Number(ws.bufferedAmount || 0)
|
|
15928
16050
|
});
|
|
16051
|
+
return true;
|
|
16052
|
+
}
|
|
16053
|
+
|
|
16054
|
+
try {
|
|
16055
|
+
ws.send(JSON.stringify(envelope));
|
|
16056
|
+
return true;
|
|
16057
|
+
} catch {
|
|
16058
|
+
return false;
|
|
16059
|
+
}
|
|
16060
|
+
}
|
|
16061
|
+
|
|
16062
|
+
function handleRemoteFleetControlInputSocketFailure(session) {
|
|
16063
|
+
if (!session?.active) {
|
|
16064
|
+
return;
|
|
16065
|
+
}
|
|
16066
|
+
|
|
16067
|
+
closeRemoteFleetControlInputSocket(session, 'failed');
|
|
16068
|
+
session.inputWsUnavailableUntil = Date.now() + 1000;
|
|
16069
|
+
drainRemoteFleetControlInputQueueToFallback(session);
|
|
16070
|
+
}
|
|
16071
|
+
|
|
16072
|
+
function ensureRemoteFleetControlInputSocket(session) {
|
|
16073
|
+
if (!session?.active || typeof WebSocket !== 'function') {
|
|
16074
|
+
return false;
|
|
16075
|
+
}
|
|
16076
|
+
|
|
16077
|
+
const ws = session.inputWs;
|
|
16078
|
+
if (ws && (ws.readyState === 0 || ws.readyState === 1)) {
|
|
16079
|
+
return true;
|
|
16080
|
+
}
|
|
16081
|
+
|
|
16082
|
+
if (session.inputWsConnecting === true) {
|
|
16083
|
+
return true;
|
|
16084
|
+
}
|
|
16085
|
+
|
|
16086
|
+
if ((session.inputWsUnavailableUntil || 0) > Date.now()) {
|
|
16087
|
+
return false;
|
|
16088
|
+
}
|
|
16089
|
+
|
|
16090
|
+
const connectToken = `input-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
16091
|
+
session.inputWsConnecting = true;
|
|
16092
|
+
session.inputWsConnectToken = connectToken;
|
|
16093
|
+
getRemoteFleetBridgeStatusForFrames()
|
|
16094
|
+
.then(status => {
|
|
16095
|
+
if (!session.active || session.inputWsConnectToken !== connectToken) {
|
|
16096
|
+
return;
|
|
16097
|
+
}
|
|
16098
|
+
|
|
16099
|
+
const wsUrl = buildRemoteFleetInputWsUrl(status);
|
|
16100
|
+
if (!wsUrl) {
|
|
16101
|
+
throw new Error('input-ws-url-unavailable');
|
|
16102
|
+
}
|
|
16103
|
+
|
|
16104
|
+
const inputWs = new WebSocket(wsUrl);
|
|
16105
|
+
session.inputWs = inputWs;
|
|
16106
|
+
inputWs.onopen = () => {
|
|
16107
|
+
if (!session.active || session.inputWs !== inputWs) {
|
|
16108
|
+
return;
|
|
16109
|
+
}
|
|
16110
|
+
|
|
16111
|
+
session.inputWsConnecting = false;
|
|
16112
|
+
session.inputWsUnavailableUntil = 0;
|
|
16113
|
+
if (session.inputWsOpenTimer) {
|
|
16114
|
+
clearTimeout(session.inputWsOpenTimer);
|
|
16115
|
+
session.inputWsOpenTimer = null;
|
|
16116
|
+
}
|
|
16117
|
+
flushRemoteFleetControlInputQueue(session);
|
|
16118
|
+
};
|
|
16119
|
+
inputWs.onmessage = event => {
|
|
16120
|
+
try {
|
|
16121
|
+
const message = JSON.parse(String(event?.data || ''));
|
|
16122
|
+
if (message?.ok === false && session.active) {
|
|
16123
|
+
session.status.textContent = message.error || 'Input failed';
|
|
16124
|
+
}
|
|
16125
|
+
} catch {
|
|
16126
|
+
// Ignore input ack parse errors.
|
|
16127
|
+
}
|
|
16128
|
+
};
|
|
16129
|
+
inputWs.onerror = () => {
|
|
16130
|
+
if (session.inputWs === inputWs) {
|
|
16131
|
+
handleRemoteFleetControlInputSocketFailure(session);
|
|
16132
|
+
}
|
|
16133
|
+
};
|
|
16134
|
+
inputWs.onclose = () => {
|
|
16135
|
+
if (session.inputWs === inputWs) {
|
|
16136
|
+
handleRemoteFleetControlInputSocketFailure(session);
|
|
16137
|
+
}
|
|
16138
|
+
};
|
|
16139
|
+
session.inputWsOpenTimer = setTimeout(() => {
|
|
16140
|
+
if (session.active && session.inputWs === inputWs && inputWs.readyState === 0) {
|
|
16141
|
+
handleRemoteFleetControlInputSocketFailure(session);
|
|
16142
|
+
}
|
|
16143
|
+
}, 900);
|
|
16144
|
+
})
|
|
16145
|
+
.catch(() => {
|
|
16146
|
+
if (!session.active || session.inputWsConnectToken !== connectToken) {
|
|
16147
|
+
return;
|
|
16148
|
+
}
|
|
16149
|
+
|
|
16150
|
+
session.inputWsConnecting = false;
|
|
16151
|
+
session.inputWsUnavailableUntil = Date.now() + 1000;
|
|
16152
|
+
drainRemoteFleetControlInputQueueToFallback(session);
|
|
16153
|
+
});
|
|
16154
|
+
|
|
16155
|
+
return true;
|
|
16156
|
+
}
|
|
16157
|
+
|
|
16158
|
+
function sendRemoteFleetControlInputNow(session, payload) {
|
|
16159
|
+
if (!session?.active || !payload?.type) {
|
|
16160
|
+
return;
|
|
16161
|
+
}
|
|
16162
|
+
|
|
16163
|
+
const input = createRemoteFleetControlInput(session, payload);
|
|
16164
|
+
const envelope = {
|
|
16165
|
+
type: 'input',
|
|
16166
|
+
nodeId: session.nodeId,
|
|
16167
|
+
deviceId: session.deviceId,
|
|
16168
|
+
requestId: `input-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
16169
|
+
input
|
|
15929
16170
|
};
|
|
15930
16171
|
|
|
16172
|
+
const wsReady = ensureRemoteFleetControlInputSocket(session);
|
|
16173
|
+
if (wsReady && sendRemoteFleetControlInputEnvelope(session, envelope)) {
|
|
16174
|
+
return;
|
|
16175
|
+
}
|
|
16176
|
+
|
|
16177
|
+
if (wsReady) {
|
|
16178
|
+
enqueueRemoteFleetControlInput(session, envelope);
|
|
16179
|
+
return;
|
|
16180
|
+
}
|
|
16181
|
+
|
|
16182
|
+
sendRemoteFleetControlInputFallback(session, input);
|
|
16183
|
+
}
|
|
16184
|
+
|
|
16185
|
+
function sendRemoteFleetControlInput(session, payload, throttleMove = false) {
|
|
16186
|
+
if (!session?.active || !payload?.type) {
|
|
16187
|
+
return;
|
|
16188
|
+
}
|
|
16189
|
+
|
|
15931
16190
|
if (!throttleMove) {
|
|
15932
|
-
|
|
16191
|
+
sendRemoteFleetControlInputNow(session, payload);
|
|
15933
16192
|
return;
|
|
15934
16193
|
}
|
|
15935
16194
|
|
|
15936
16195
|
const now = performance.now();
|
|
15937
16196
|
const elapsed = now - (session.lastMoveSentAt || 0);
|
|
15938
|
-
if (elapsed >=
|
|
16197
|
+
if (elapsed >= REMOTE_FLEET_CONTROL_INPUT_MOVE_MS) {
|
|
15939
16198
|
session.lastMoveSentAt = now;
|
|
15940
|
-
|
|
16199
|
+
sendRemoteFleetControlInputNow(session, payload);
|
|
15941
16200
|
return;
|
|
15942
16201
|
}
|
|
15943
16202
|
|
|
@@ -15952,18 +16211,82 @@
|
|
|
15952
16211
|
session.pendingMove = null;
|
|
15953
16212
|
if (next) {
|
|
15954
16213
|
session.lastMoveSentAt = performance.now();
|
|
15955
|
-
|
|
16214
|
+
sendRemoteFleetControlInputNow(session, next);
|
|
15956
16215
|
}
|
|
15957
|
-
}, Math.max(1,
|
|
16216
|
+
}, Math.max(1, REMOTE_FLEET_CONTROL_INPUT_MOVE_MS - elapsed));
|
|
15958
16217
|
}
|
|
15959
16218
|
|
|
15960
|
-
function
|
|
16219
|
+
function isRemoteFleetControlBypassTarget(target, session) {
|
|
16220
|
+
let current = target || null;
|
|
16221
|
+
while (current) {
|
|
16222
|
+
if (current === session?.stage || current === session?.shell) {
|
|
16223
|
+
return false;
|
|
16224
|
+
}
|
|
16225
|
+
|
|
16226
|
+
const tag = String(current.tagName || '').toLowerCase();
|
|
16227
|
+
if (current?.dataset?.remoteFleetControlClose === 'true'
|
|
16228
|
+
|| tag === 'button'
|
|
16229
|
+
|| tag === 'input'
|
|
16230
|
+
|| tag === 'textarea'
|
|
16231
|
+
|| tag === 'select'
|
|
16232
|
+
|| String(current.getAttribute?.('contenteditable') || '').toLowerCase() === 'true') {
|
|
16233
|
+
return true;
|
|
16234
|
+
}
|
|
16235
|
+
|
|
16236
|
+
current = current.parentElement || null;
|
|
16237
|
+
}
|
|
16238
|
+
|
|
16239
|
+
return false;
|
|
16240
|
+
}
|
|
16241
|
+
|
|
16242
|
+
function bindRemoteFleetControlInput(session, options = {}) {
|
|
15961
16243
|
const { overlay, shell, stage, canvas } = session;
|
|
15962
16244
|
let pointerActive = false;
|
|
15963
16245
|
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
16246
|
+
if (options.captureOverlayEvents !== false) {
|
|
16247
|
+
['mousedown', 'mouseup', 'click', 'dblclick', 'keydown', 'keyup', 'wheel', 'contextmenu'].forEach(eventName => {
|
|
16248
|
+
overlay.addEventListener(eventName, event => {
|
|
16249
|
+
if (!isRemoteFleetControlBypassTarget(event.target, session)) {
|
|
16250
|
+
event.stopPropagation();
|
|
16251
|
+
}
|
|
16252
|
+
}, true);
|
|
16253
|
+
});
|
|
16254
|
+
}
|
|
16255
|
+
|
|
16256
|
+
const shouldIgnoreKeyboardEvent = event =>
|
|
16257
|
+
isRemoteFleetControlBypassTarget(event?.target, session);
|
|
16258
|
+
|
|
16259
|
+
const handleKeyboardEvent = (event, type) => {
|
|
16260
|
+
if (!session.active || shouldIgnoreKeyboardEvent(event) || event.__remoteFleetControlHandled === true) {
|
|
16261
|
+
return;
|
|
16262
|
+
}
|
|
16263
|
+
|
|
16264
|
+
event.__remoteFleetControlHandled = true;
|
|
16265
|
+
event.preventDefault();
|
|
16266
|
+
event.stopPropagation();
|
|
16267
|
+
shell.focus({ preventScroll: true });
|
|
16268
|
+
sendRemoteFleetControlInput(session, {
|
|
16269
|
+
type,
|
|
16270
|
+
key: event.key,
|
|
16271
|
+
code: event.code,
|
|
16272
|
+
repeat: event.repeat === true
|
|
16273
|
+
});
|
|
16274
|
+
};
|
|
16275
|
+
|
|
16276
|
+
const handleWindowKeyDown = event => handleKeyboardEvent(event, 'keyDown');
|
|
16277
|
+
const handleWindowKeyUp = event => handleKeyboardEvent(event, 'keyUp');
|
|
16278
|
+
const captureWindowKeyboard = options.windowKeyboardCapture !== false;
|
|
16279
|
+
if (captureWindowKeyboard && typeof window.addEventListener === 'function') {
|
|
16280
|
+
window.addEventListener('keydown', handleWindowKeyDown, true);
|
|
16281
|
+
window.addEventListener('keyup', handleWindowKeyUp, true);
|
|
16282
|
+
}
|
|
16283
|
+
session.inputCleanup = () => {
|
|
16284
|
+
if (captureWindowKeyboard && typeof window.removeEventListener === 'function') {
|
|
16285
|
+
window.removeEventListener('keydown', handleWindowKeyDown, true);
|
|
16286
|
+
window.removeEventListener('keyup', handleWindowKeyUp, true);
|
|
16287
|
+
}
|
|
16288
|
+
session.inputCleanup = null;
|
|
16289
|
+
};
|
|
15967
16290
|
|
|
15968
16291
|
stage.addEventListener('pointerdown', event => {
|
|
15969
16292
|
const point = getRemoteFleetControlPoint(event, canvas, false);
|
|
@@ -16041,35 +16364,8 @@
|
|
|
16041
16364
|
event.stopPropagation();
|
|
16042
16365
|
});
|
|
16043
16366
|
|
|
16044
|
-
shell.addEventListener('keydown', event =>
|
|
16045
|
-
|
|
16046
|
-
return;
|
|
16047
|
-
}
|
|
16048
|
-
|
|
16049
|
-
event.preventDefault();
|
|
16050
|
-
event.stopPropagation();
|
|
16051
|
-
sendRemoteFleetControlInput(session, {
|
|
16052
|
-
type: 'keyDown',
|
|
16053
|
-
key: event.key,
|
|
16054
|
-
code: event.code,
|
|
16055
|
-
repeat: event.repeat === true
|
|
16056
|
-
});
|
|
16057
|
-
});
|
|
16058
|
-
|
|
16059
|
-
shell.addEventListener('keyup', event => {
|
|
16060
|
-
if (event.target?.closest?.('[data-remote-fleet-control-close="true"]')) {
|
|
16061
|
-
return;
|
|
16062
|
-
}
|
|
16063
|
-
|
|
16064
|
-
event.preventDefault();
|
|
16065
|
-
event.stopPropagation();
|
|
16066
|
-
sendRemoteFleetControlInput(session, {
|
|
16067
|
-
type: 'keyUp',
|
|
16068
|
-
key: event.key,
|
|
16069
|
-
code: event.code,
|
|
16070
|
-
repeat: event.repeat === true
|
|
16071
|
-
});
|
|
16072
|
-
});
|
|
16367
|
+
shell.addEventListener('keydown', event => handleKeyboardEvent(event, 'keyDown'));
|
|
16368
|
+
shell.addEventListener('keyup', event => handleKeyboardEvent(event, 'keyUp'));
|
|
16073
16369
|
}
|
|
16074
16370
|
|
|
16075
16371
|
function openRemoteFleetControlPopup(bodyView, nodeModel, deviceId) {
|
|
@@ -16233,10 +16529,18 @@
|
|
|
16233
16529
|
lastMoveSentAt: 0,
|
|
16234
16530
|
lastControlFrameAt: 0,
|
|
16235
16531
|
lastControlFrameSeq: 0,
|
|
16236
|
-
binaryFramePreferred: false
|
|
16532
|
+
binaryFramePreferred: false,
|
|
16533
|
+
inputWs: null,
|
|
16534
|
+
inputWsConnecting: false,
|
|
16535
|
+
inputWsConnectToken: '',
|
|
16536
|
+
inputWsOpenTimer: null,
|
|
16537
|
+
inputWsUnavailableUntil: 0,
|
|
16538
|
+
inputQueue: [],
|
|
16539
|
+
inputCleanup: null
|
|
16237
16540
|
};
|
|
16238
16541
|
activeRemoteFleetControlPopup = session;
|
|
16239
16542
|
bindRemoteFleetControlInput(session);
|
|
16543
|
+
ensureRemoteFleetControlInputSocket(session);
|
|
16240
16544
|
attachRemoteFleetControlBinaryFrameSession(session, bodyView);
|
|
16241
16545
|
|
|
16242
16546
|
const refresh = async () => {
|
|
@@ -16315,6 +16619,93 @@
|
|
|
16315
16619
|
});
|
|
16316
16620
|
}
|
|
16317
16621
|
|
|
16622
|
+
function closeRemoteFleetControlNodeSession(bodyView, reason = 'close') {
|
|
16623
|
+
const session = bodyView?._remoteFleetControlNodeSession || null;
|
|
16624
|
+
if (!session) {
|
|
16625
|
+
return;
|
|
16626
|
+
}
|
|
16627
|
+
|
|
16628
|
+
bodyView._remoteFleetControlNodeSession = null;
|
|
16629
|
+
session.active = false;
|
|
16630
|
+
detachRemoteFleetControlBinaryFrameSession(session);
|
|
16631
|
+
closeRemoteFleetControlInputSocket(session, 'close');
|
|
16632
|
+
session.inputCleanup?.();
|
|
16633
|
+
if (session.moveTimer) {
|
|
16634
|
+
clearTimeout(session.moveTimer);
|
|
16635
|
+
session.moveTimer = null;
|
|
16636
|
+
}
|
|
16637
|
+
session.pendingMove = null;
|
|
16638
|
+
window.RuntimeTrace?.emit?.('remote.control.nodeClosed', {
|
|
16639
|
+
nodeId: session.nodeId,
|
|
16640
|
+
deviceId: session.deviceId,
|
|
16641
|
+
reason
|
|
16642
|
+
});
|
|
16643
|
+
}
|
|
16644
|
+
|
|
16645
|
+
function startRemoteFleetControlNodeSession(bodyView, nodeId, deviceId, stage, canvas, status, placeholder = null) {
|
|
16646
|
+
const normalizedNodeId = String(nodeId || '').trim();
|
|
16647
|
+
const normalizedDeviceId = String(deviceId || '').trim();
|
|
16648
|
+
if (!bodyView || !normalizedNodeId || !normalizedDeviceId || !stage || !canvas) {
|
|
16649
|
+
return null;
|
|
16650
|
+
}
|
|
16651
|
+
|
|
16652
|
+
closeRemoteFleetControlNodeSession(bodyView, 'replace');
|
|
16653
|
+
const session = {
|
|
16654
|
+
active: true,
|
|
16655
|
+
nodeId: normalizedNodeId,
|
|
16656
|
+
deviceId: normalizedDeviceId,
|
|
16657
|
+
overlay: bodyView,
|
|
16658
|
+
shell: stage,
|
|
16659
|
+
stage,
|
|
16660
|
+
canvas,
|
|
16661
|
+
status: status || { textContent: '' },
|
|
16662
|
+
placeholder,
|
|
16663
|
+
wasLiveActive: false,
|
|
16664
|
+
startedStream: false,
|
|
16665
|
+
streamId: '',
|
|
16666
|
+
controlLeaseId: `control-node-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
|
|
16667
|
+
timer: null,
|
|
16668
|
+
moveTimer: null,
|
|
16669
|
+
pendingMove: null,
|
|
16670
|
+
lastMoveSentAt: 0,
|
|
16671
|
+
lastControlFrameAt: 0,
|
|
16672
|
+
lastControlFrameSeq: 0,
|
|
16673
|
+
binaryFramePreferred: false,
|
|
16674
|
+
inputWs: null,
|
|
16675
|
+
inputWsConnecting: false,
|
|
16676
|
+
inputWsConnectToken: '',
|
|
16677
|
+
inputWsOpenTimer: null,
|
|
16678
|
+
inputWsUnavailableUntil: 0,
|
|
16679
|
+
inputQueue: [],
|
|
16680
|
+
inputCleanup: null
|
|
16681
|
+
};
|
|
16682
|
+
|
|
16683
|
+
bodyView._remoteFleetControlNodeSession = session;
|
|
16684
|
+
bindRemoteFleetControlInput(session, {
|
|
16685
|
+
captureOverlayEvents: false,
|
|
16686
|
+
windowKeyboardCapture: false
|
|
16687
|
+
});
|
|
16688
|
+
ensureRemoteFleetControlInputSocket(session);
|
|
16689
|
+
startRemoteFleetBinaryFrameSocket(bodyView, () => [normalizedDeviceId], {
|
|
16690
|
+
autoStartLive: true,
|
|
16691
|
+
fps: REMOTE_FLEET_CONTROL_LIVE_FPS,
|
|
16692
|
+
maxWidth: 1280,
|
|
16693
|
+
maxHeight: 720,
|
|
16694
|
+
quality: 65,
|
|
16695
|
+
mode: 'remote-fast-control-node'
|
|
16696
|
+
});
|
|
16697
|
+
attachRemoteFleetControlBinaryFrameSession(session, bodyView);
|
|
16698
|
+
stage.focus?.({ preventScroll: true });
|
|
16699
|
+
if (status) {
|
|
16700
|
+
status.textContent = `Control ${REMOTE_FLEET_CONTROL_LIVE_FPS}fps`;
|
|
16701
|
+
}
|
|
16702
|
+
window.RuntimeTrace?.emit?.('remote.control.nodeOpened', {
|
|
16703
|
+
nodeId: normalizedNodeId,
|
|
16704
|
+
deviceId: normalizedDeviceId
|
|
16705
|
+
});
|
|
16706
|
+
return session;
|
|
16707
|
+
}
|
|
16708
|
+
|
|
16318
16709
|
function isRemoteFleetLiveActive(device) {
|
|
16319
16710
|
return getRemoteFleetDeviceField(device, 'liveStreamActive', 'LiveStreamActive', false) === true;
|
|
16320
16711
|
}
|
|
@@ -16559,7 +16950,7 @@
|
|
|
16559
16950
|
|
|
16560
16951
|
if (!device || !deviceId) {
|
|
16561
16952
|
const empty = document.createElement('div');
|
|
16562
|
-
empty.textContent = lastError || '
|
|
16953
|
+
empty.textContent = lastError || 'Remote control device snapshot is unavailable.';
|
|
16563
16954
|
empty.style.cssText = `
|
|
16564
16955
|
flex: 1 1 auto;
|
|
16565
16956
|
display: flex;
|
|
@@ -16575,7 +16966,7 @@
|
|
|
16575
16966
|
text-align: center;
|
|
16576
16967
|
background: rgba(255, 255, 255, 0.74);
|
|
16577
16968
|
`;
|
|
16578
|
-
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this
|
|
16969
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this remote control device', 'refresh-device');
|
|
16579
16970
|
refreshButton.style.height = '32px';
|
|
16580
16971
|
refreshButton.addEventListener('click', async event => {
|
|
16581
16972
|
event.preventDefault();
|
|
@@ -16654,52 +17045,109 @@
|
|
|
16654
17045
|
header.appendChild(statusBadge);
|
|
16655
17046
|
bodyView.appendChild(header);
|
|
16656
17047
|
|
|
16657
|
-
const
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
: getRemoteFleetDeviceField(device, 'thumbnailFrameSeq', 'ThumbnailFrameSeq', 0));
|
|
16664
|
-
preview.style.cssText = `
|
|
17048
|
+
const controlStage = document.createElement('div');
|
|
17049
|
+
controlStage.dataset.remoteFleetControlNodeStage = 'true';
|
|
17050
|
+
controlStage.dataset.deviceId = deviceId;
|
|
17051
|
+
controlStage.tabIndex = 0;
|
|
17052
|
+
controlStage.title = `${name} remote control`;
|
|
17053
|
+
controlStage.style.cssText = `
|
|
16665
17054
|
position: relative;
|
|
16666
|
-
flex:
|
|
17055
|
+
flex: 1 1 auto;
|
|
17056
|
+
min-height: 260px;
|
|
16667
17057
|
width: 100%;
|
|
16668
|
-
aspect-ratio: 16 / 9;
|
|
16669
17058
|
overflow: hidden;
|
|
16670
|
-
border-radius:
|
|
16671
|
-
background:
|
|
16672
|
-
border: 1px solid rgba(15, 23, 42, 0.
|
|
17059
|
+
border-radius: 0;
|
|
17060
|
+
background: #020617;
|
|
17061
|
+
border: 1px solid rgba(15, 23, 42, 0.18);
|
|
17062
|
+
cursor: crosshair;
|
|
17063
|
+
touch-action: none;
|
|
17064
|
+
outline: none;
|
|
16673
17065
|
`;
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
17066
|
+
const controlCanvas = document.createElement('canvas');
|
|
17067
|
+
controlCanvas.dataset.remoteFleetControlNodeCanvas = 'true';
|
|
17068
|
+
controlCanvas.style.cssText = `
|
|
17069
|
+
position: absolute;
|
|
17070
|
+
inset: 0;
|
|
17071
|
+
width: 100%;
|
|
17072
|
+
height: 100%;
|
|
17073
|
+
display: block;
|
|
17074
|
+
background: #020617;
|
|
17075
|
+
`;
|
|
17076
|
+
const controlOverlay = document.createElement('div');
|
|
17077
|
+
controlOverlay.style.cssText = `
|
|
17078
|
+
position: absolute;
|
|
17079
|
+
left: 8px;
|
|
17080
|
+
top: 8px;
|
|
17081
|
+
display: flex;
|
|
17082
|
+
align-items: center;
|
|
17083
|
+
gap: 6px;
|
|
17084
|
+
pointer-events: none;
|
|
17085
|
+
z-index: 2;
|
|
17086
|
+
`;
|
|
17087
|
+
const controlDot = document.createElement('span');
|
|
17088
|
+
controlDot.style.cssText = `
|
|
17089
|
+
width: 7px;
|
|
17090
|
+
height: 7px;
|
|
17091
|
+
border-radius: 50%;
|
|
17092
|
+
background: ${connected ? '#10b981' : '#94a3b8'};
|
|
17093
|
+
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.72);
|
|
17094
|
+
`;
|
|
17095
|
+
const controlStatus = document.createElement('span');
|
|
17096
|
+
controlStatus.dataset.remoteFleetControlNodeStatus = 'true';
|
|
17097
|
+
controlStatus.textContent = connected ? 'Connecting' : 'Offline';
|
|
17098
|
+
controlStatus.style.cssText = `
|
|
17099
|
+
color: rgba(248, 250, 252, 0.94);
|
|
17100
|
+
font-size: 10px;
|
|
17101
|
+
font-weight: 900;
|
|
17102
|
+
line-height: 1;
|
|
17103
|
+
padding: 4px 6px;
|
|
17104
|
+
border-radius: 999px;
|
|
17105
|
+
background: rgba(15, 23, 42, 0.72);
|
|
17106
|
+
letter-spacing: 0;
|
|
17107
|
+
`;
|
|
17108
|
+
const controlPlaceholder = document.createElement('div');
|
|
17109
|
+
controlPlaceholder.dataset.remoteFleetControlNodePlaceholder = 'true';
|
|
17110
|
+
controlPlaceholder.textContent = connected ? 'Waiting for screen' : 'Offline';
|
|
17111
|
+
controlPlaceholder.style.cssText = `
|
|
17112
|
+
position: absolute;
|
|
17113
|
+
inset: 0;
|
|
17114
|
+
display: flex;
|
|
17115
|
+
align-items: center;
|
|
17116
|
+
justify-content: center;
|
|
17117
|
+
color: rgba(226, 232, 240, 0.74);
|
|
17118
|
+
font-size: 12px;
|
|
17119
|
+
font-weight: 900;
|
|
17120
|
+
letter-spacing: 0;
|
|
17121
|
+
pointer-events: none;
|
|
17122
|
+
`;
|
|
17123
|
+
controlOverlay.appendChild(controlDot);
|
|
17124
|
+
controlOverlay.appendChild(controlStatus);
|
|
17125
|
+
controlStage.appendChild(controlCanvas);
|
|
17126
|
+
controlStage.appendChild(controlPlaceholder);
|
|
17127
|
+
controlStage.appendChild(controlOverlay);
|
|
17128
|
+
bodyView.appendChild(controlStage);
|
|
17129
|
+
|
|
17130
|
+
if (connected) {
|
|
17131
|
+
const session = startRemoteFleetControlNodeSession(
|
|
17132
|
+
bodyView,
|
|
17133
|
+
nodeId,
|
|
17134
|
+
deviceId,
|
|
17135
|
+
controlStage,
|
|
17136
|
+
controlCanvas,
|
|
17137
|
+
controlStatus,
|
|
17138
|
+
controlPlaceholder);
|
|
17139
|
+
if (session && isRemoteFleetFrameSource(previewSource)) {
|
|
17140
|
+
paintRemoteFleetControlFrame(session, {
|
|
17141
|
+
deviceId,
|
|
17142
|
+
kind: hasLiveFrame ? 'live' : 'thumbnail',
|
|
17143
|
+
frameSeq: hasLiveFrame
|
|
17144
|
+
? getRemoteFleetDeviceField(device, 'liveFrameSeq', 'LiveFrameSeq', 0)
|
|
17145
|
+
: getRemoteFleetDeviceField(device, 'thumbnailFrameSeq', 'ThumbnailFrameSeq', 0),
|
|
17146
|
+
frameUrl: previewSource,
|
|
17147
|
+
receivedAt: previewAt
|
|
17148
|
+
}).catch(() => undefined);
|
|
17149
|
+
}
|
|
16701
17150
|
}
|
|
16702
|
-
bodyView.appendChild(preview);
|
|
16703
17151
|
|
|
16704
17152
|
const stats = document.createElement('div');
|
|
16705
17153
|
stats.style.cssText = 'display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px;flex:0 0 auto;';
|
|
@@ -16766,7 +17214,7 @@
|
|
|
16766
17214
|
|
|
16767
17215
|
const actions = document.createElement('div');
|
|
16768
17216
|
actions.style.cssText = 'display:flex;align-items:center;gap:7px;flex-wrap:wrap;flex:0 0 auto;';
|
|
16769
|
-
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this
|
|
17217
|
+
const refreshButton = createRemoteFleetButton('Refresh', 'Refresh this remote control device', 'refresh-device');
|
|
16770
17218
|
refreshButton.style.height = '30px';
|
|
16771
17219
|
actions.appendChild(refreshButton);
|
|
16772
17220
|
if (connected && thumbnailEnabled) {
|
|
@@ -18111,7 +18559,32 @@
|
|
|
18111
18559
|
const deviceId = String(card.dataset.deviceId || '').trim();
|
|
18112
18560
|
if (!deviceId) return;
|
|
18113
18561
|
bodyView.dataset.remoteFleetSelectedDeviceId = deviceId;
|
|
18114
|
-
|
|
18562
|
+
if (card.dataset.remoteFleetOpeningControlNode === 'true') {
|
|
18563
|
+
return;
|
|
18564
|
+
}
|
|
18565
|
+
|
|
18566
|
+
card.dataset.remoteFleetOpeningControlNode = 'true';
|
|
18567
|
+
invokeDotNetAsync('AddRemoteFleetDeviceNodeFromJs', nodeId, deviceId)
|
|
18568
|
+
.then(async result => {
|
|
18569
|
+
await syncRemoteFleetNodeStateFromResult(result);
|
|
18570
|
+
if (result?.success) {
|
|
18571
|
+
const controlNodeId = String(result.nodeId || result.NodeId || '').trim();
|
|
18572
|
+
if (controlNodeId) {
|
|
18573
|
+
window.mindMap?.focusNode?.(controlNodeId, { selectNode: true });
|
|
18574
|
+
window.mindMap?.selectNode?.(controlNodeId);
|
|
18575
|
+
invokeDotNetAsync('SelectNodeInBlazor', controlNodeId).catch(() => undefined);
|
|
18576
|
+
}
|
|
18577
|
+
setTaskFeedback('');
|
|
18578
|
+
} else {
|
|
18579
|
+
setTaskFeedback(result?.error || result?.Error || 'Remote control node failed.', 'error');
|
|
18580
|
+
}
|
|
18581
|
+
})
|
|
18582
|
+
.catch(error => {
|
|
18583
|
+
setTaskFeedback(error?.message || 'Remote control node failed.', 'error');
|
|
18584
|
+
})
|
|
18585
|
+
.finally(() => {
|
|
18586
|
+
delete card.dataset.remoteFleetOpeningControlNode;
|
|
18587
|
+
});
|
|
18115
18588
|
});
|
|
18116
18589
|
const selectCard = event => {
|
|
18117
18590
|
event.preventDefault();
|