dsh-mobile 0.3.16 → 0.4.0
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/CHANGELOG.md +18 -0
- package/README.en.md +59 -9
- package/README.md +69 -18
- package/lib/cli.js +96 -81
- package/lib/cli.js.map +1 -1
- package/lib/client.js +736 -89
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +86 -2
- package/lib/index.mjs +644 -114
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -83,6 +83,7 @@ window.__ModuleLoader__.load({
|
|
|
83
83
|
continueLogin: "Continue login",
|
|
84
84
|
reconnect: "Reconnect",
|
|
85
85
|
generateRemoteQr: "Generate remote pairing QR code",
|
|
86
|
+
copyRemoteLink: "Copy remote pairing link",
|
|
86
87
|
manageRemoteDevices: "Manage remote devices",
|
|
87
88
|
resetRemoteLogin: "Sign out and clear remote login",
|
|
88
89
|
resetRemoteDevices: "Disable and clear remote devices",
|
|
@@ -250,6 +251,7 @@ window.__ModuleLoader__.load({
|
|
|
250
251
|
continueLogin: "Continua accesso",
|
|
251
252
|
reconnect: "Riconnetti",
|
|
252
253
|
generateRemoteQr: "Genera QR di abbinamento remoto",
|
|
254
|
+
copyRemoteLink: "Copia link di abbinamento remoto",
|
|
253
255
|
manageRemoteDevices: "Gestisci dispositivi remoti",
|
|
254
256
|
resetRemoteLogin: "Esci e rimuovi accesso remoto",
|
|
255
257
|
resetRemoteDevices: "Disattiva e rimuovi dispositivi remoti",
|
|
@@ -419,6 +421,7 @@ window.__ModuleLoader__.load({
|
|
|
419
421
|
continueLogin: "继续登录",
|
|
420
422
|
reconnect: "重新连接",
|
|
421
423
|
generateRemoteQr: "生成远程配对二维码",
|
|
424
|
+
copyRemoteLink: "复制远程配对链接",
|
|
422
425
|
manageRemoteDevices: "管理远程设备",
|
|
423
426
|
resetRemoteLogin: "退出并清除远程登录",
|
|
424
427
|
resetRemoteDevices: "关闭并清除远程设备",
|
|
@@ -510,6 +513,51 @@ window.__ModuleLoader__.load({
|
|
|
510
513
|
wsPathsAnnounce: "{n} 条插件连接等待放行",
|
|
511
514
|
wsPathsAdvancedHint: "通常不需要手动填写:用上方按钮即可。只有当你确切知道所需路径时才填。"
|
|
512
515
|
});
|
|
516
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
517
|
+
lanSetupTitle: "Finish local network setup",
|
|
518
|
+
lanSetupIntro: "Choose the network shared by this computer and your phone. DSH Mobile creates private certificates only after you confirm.",
|
|
519
|
+
lanSetupNetwork: "Computer network",
|
|
520
|
+
lanSetupChooseNetwork: "Choose a network",
|
|
521
|
+
lanSetupRecommended: "Recommended",
|
|
522
|
+
lanSetupNoNetworks: "No private Wi-Fi or Ethernet address was found. Connect this computer to the local network, then scan again.",
|
|
523
|
+
lanSetupRefresh: "Scan again",
|
|
524
|
+
lanSetupFirewallNote: "Windows may request administrator approval for two inbound rules limited to the local subnet.",
|
|
525
|
+
lanSetupConfigure: "Configure local network",
|
|
526
|
+
lanSetupConfiguring: "Creating private certificates and local network rules…",
|
|
527
|
+
lanSetupRequired: "Local network setup has not been completed. Choose a network above to continue.",
|
|
528
|
+
lanSetupRestart: "Setup complete for {origin}. Restart DSH once to start local network access.",
|
|
529
|
+
lanSetupFailed: "Setup was not completed. Confirm the network selection and approve the Windows prompt, then try again."
|
|
530
|
+
});
|
|
531
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.it, {
|
|
532
|
+
lanSetupTitle: "Completa la configurazione della rete locale",
|
|
533
|
+
lanSetupIntro: "Scegli la rete condivisa da questo computer e dal telefono. DSH Mobile crea certificati privati solo dopo la conferma.",
|
|
534
|
+
lanSetupNetwork: "Rete del computer",
|
|
535
|
+
lanSetupChooseNetwork: "Scegli una rete",
|
|
536
|
+
lanSetupRecommended: "Consigliata",
|
|
537
|
+
lanSetupNoNetworks: "Nessun indirizzo Wi-Fi o Ethernet privato trovato. Collega il computer alla rete locale e ripeti la ricerca.",
|
|
538
|
+
lanSetupRefresh: "Cerca di nuovo",
|
|
539
|
+
lanSetupFirewallNote: "Windows può richiedere l’approvazione amministratore per due regole in ingresso limitate alla sottorete locale.",
|
|
540
|
+
lanSetupConfigure: "Configura rete locale",
|
|
541
|
+
lanSetupConfiguring: "Creazione dei certificati privati e delle regole di rete locale…",
|
|
542
|
+
lanSetupRequired: "La configurazione della rete locale non è completa. Scegli una rete qui sopra per continuare.",
|
|
543
|
+
lanSetupRestart: "Configurazione completata per {origin}. Riavvia DSH una volta per attivare l’accesso locale.",
|
|
544
|
+
lanSetupFailed: "Configurazione non completata. Controlla la rete e approva la richiesta di Windows, quindi riprova."
|
|
545
|
+
});
|
|
546
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
|
|
547
|
+
lanSetupTitle: "完成局域网配置",
|
|
548
|
+
lanSetupIntro: "选择电脑与手机共同使用的网络。确认后才会生成私有证书,不会在安装时自动开放端口。",
|
|
549
|
+
lanSetupNetwork: "电脑当前网络",
|
|
550
|
+
lanSetupChooseNetwork: "请选择网络",
|
|
551
|
+
lanSetupRecommended: "推荐",
|
|
552
|
+
lanSetupNoNetworks: "未找到可用的私有 Wi-Fi 或以太网地址。请先让电脑接入局域网,然后重新扫描。",
|
|
553
|
+
lanSetupRefresh: "重新扫描",
|
|
554
|
+
lanSetupFirewallNote: "Windows 可能请求管理员授权,仅创建两条限本地子网访问的入站规则。",
|
|
555
|
+
lanSetupConfigure: "完成配置",
|
|
556
|
+
lanSetupConfiguring: "正在生成私有证书并配置局域网规则…",
|
|
557
|
+
lanSetupRequired: "尚未完成局域网配置,请选择上方网络继续。",
|
|
558
|
+
lanSetupRestart: "已为 {origin} 完成配置。请重启一次 DSH,局域网访问会自动开启。",
|
|
559
|
+
lanSetupFailed: "配置未完成。请确认网卡选择并允许 Windows 授权提示,然后重试。"
|
|
560
|
+
});
|
|
513
561
|
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
514
562
|
selfHostedConnections: "Self-hosted connection",
|
|
515
563
|
selfHostedDescription: "For users with a VPS and domain. It does not depend on public tunnel bandwidth.",
|
|
@@ -966,12 +1014,32 @@ window.__ModuleLoader__.load({
|
|
|
966
1014
|
generated: "Generato"
|
|
967
1015
|
},
|
|
968
1016
|
zh: {
|
|
1017
|
+
versions: "已显示插件、DSH 和 Android App 最低版本。",
|
|
1018
|
+
networkOk: "已配置的局域网网卡可用。",
|
|
1019
|
+
networkError: "已保存的局域网网卡不可用。",
|
|
1020
|
+
networkInfo: "当前使用固定局域网配置。",
|
|
1021
|
+
networkAction: "重新运行 dsh-mobile setup。",
|
|
1022
|
+
lanOk: "局域网网关正在监听,配对入口可用。",
|
|
1023
|
+
lanInfo: "局域网访问当前未开启。",
|
|
1024
|
+
lanAction: "需要手机直连时开启局域网访问。",
|
|
1025
|
+
firewallOk: "局域网 TCP 与发现防火墙规则已启用。",
|
|
1026
|
+
firewallWarning: "未找到完整的局域网防火墙规则。",
|
|
1027
|
+
firewallInfo: "系统未允许插件读取防火墙状态。",
|
|
1028
|
+
firewallAction: "以管理员身份运行 dsh-mobile setup 并检查防火墙规则。",
|
|
1029
|
+
remoteOk: "远程公网地址已通过可达性检查。",
|
|
1030
|
+
remoteWarning: "远程通道需要处理或仍在连接。",
|
|
1031
|
+
remoteError: "远程连接当前不可达。",
|
|
1032
|
+
remoteInfo: "远程访问当前未开启。",
|
|
1033
|
+
remoteAction: "返回远程访问,按提供方提示操作并重新连接。",
|
|
1034
|
+
phone: "电脑无法判断路由器是否隔离了手机。",
|
|
1035
|
+
phoneAction: "确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。",
|
|
969
1036
|
reportTitle: "DSH Mobile 诊断报告",
|
|
970
1037
|
generated: "生成时间"
|
|
971
1038
|
}
|
|
972
1039
|
};
|
|
973
1040
|
const DIAGNOSTIC_REASON_MESSAGES = {
|
|
974
1041
|
en: {
|
|
1042
|
+
"lan-setup-required": ["Local network setup has not been completed.", "Return to Local network and choose a network to finish setup."],
|
|
975
1043
|
"versions-current": ["Installed plugin, DSH, and minimum Android app versions are shown.", ""],
|
|
976
1044
|
"network-unavailable": ["The saved local network adapter is unavailable.", "Run dsh-mobile setup again."],
|
|
977
1045
|
"network-interface": ["Using network interface {interfaceName}.", ""],
|
|
@@ -992,6 +1060,7 @@ window.__ModuleLoader__.load({
|
|
|
992
1060
|
"phone-network-unknown": ["The computer cannot determine whether the router isolates the phone.", "Confirm the phone and computer use the same network, then disable guest-network or AP isolation."]
|
|
993
1061
|
},
|
|
994
1062
|
it: {
|
|
1063
|
+
"lan-setup-required": ["La configurazione della rete locale non è completa.", "Torna a Rete locale e scegli una rete per completare la configurazione."],
|
|
995
1064
|
"versions-current": ["Sono indicate le versioni installate di plugin e DSH e la versione minima dell’app Android.", ""],
|
|
996
1065
|
"network-unavailable": ["La scheda di rete locale salvata non è disponibile.", "Esegui di nuovo dsh-mobile setup."],
|
|
997
1066
|
"network-interface": ["È in uso l’interfaccia di rete {interfaceName}.", ""],
|
|
@@ -1012,6 +1081,7 @@ window.__ModuleLoader__.load({
|
|
|
1012
1081
|
"phone-network-unknown": ["Il computer non può stabilire se il router isola il telefono.", "Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP."]
|
|
1013
1082
|
},
|
|
1014
1083
|
zh: {
|
|
1084
|
+
"lan-setup-required": ["尚未完成局域网初始化。", "返回局域网页选择网卡并完成配置。"],
|
|
1015
1085
|
"versions-current": ["已显示插件、DSH 和 Android App 最低版本。", ""],
|
|
1016
1086
|
"network-unavailable": ["已保存的局域网网卡当前不可用。", "重新运行 dsh-mobile setup。"],
|
|
1017
1087
|
"network-interface": ["正在使用网卡 {interfaceName}。", ""],
|
|
@@ -1129,6 +1199,304 @@ window.__ModuleLoader__.load({
|
|
|
1129
1199
|
if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), "");
|
|
1130
1200
|
return lines.join("\n");
|
|
1131
1201
|
}
|
|
1202
|
+
/** Wire bounds mirrored by the Android notification policy. */
|
|
1203
|
+
const TASK_NOTIFY_LIMITS = Object.freeze({
|
|
1204
|
+
title: 80,
|
|
1205
|
+
body: 200,
|
|
1206
|
+
tag: 64
|
|
1207
|
+
});
|
|
1208
|
+
const MAX_SEEN_QUESTION_KEYS = 20;
|
|
1209
|
+
function sanitizeTagFragment(value) {
|
|
1210
|
+
return value.toLowerCase().replaceAll(/[^a-z0-9-]+/gu, "-").replaceAll(/^-+|-+$/gu, "").slice(0, TASK_NOTIFY_LIMITS.tag);
|
|
1211
|
+
}
|
|
1212
|
+
/** Stable per-turn tag so concurrent sessions keep separate notifications. */
|
|
1213
|
+
function taskCompletionTag(sessionId, turn) {
|
|
1214
|
+
return `dsh-task-done-${sanitizeTagFragment(`${sessionId}-${String(turn)}`) || "task"}`.slice(0, TASK_NOTIFY_LIMITS.tag);
|
|
1215
|
+
}
|
|
1216
|
+
/** Decide whether the current snapshot deserves a system notification. */
|
|
1217
|
+
var TaskNotifyTracker = class {
|
|
1218
|
+
lastActivityAt;
|
|
1219
|
+
notifiedDoneAt;
|
|
1220
|
+
wasBusy = false;
|
|
1221
|
+
busySince;
|
|
1222
|
+
idleAnchoredAt;
|
|
1223
|
+
seenQuestionKeys = /* @__PURE__ */ new Set();
|
|
1224
|
+
quietMs;
|
|
1225
|
+
graceMs;
|
|
1226
|
+
minBusyMs;
|
|
1227
|
+
now;
|
|
1228
|
+
format;
|
|
1229
|
+
constructor(options) {
|
|
1230
|
+
this.quietMs = options.quietMs ?? 9e4;
|
|
1231
|
+
this.graceMs = options.graceMs ?? 1e4;
|
|
1232
|
+
this.minBusyMs = options.minBusyMs ?? 2e4;
|
|
1233
|
+
this.now = options.now ?? Date.now;
|
|
1234
|
+
this.format = options.format;
|
|
1235
|
+
}
|
|
1236
|
+
/** Record assistant-side activity; re-arms completion after a notification. */
|
|
1237
|
+
markActivity() {
|
|
1238
|
+
const now = this.now();
|
|
1239
|
+
this.lastActivityAt = now;
|
|
1240
|
+
if (this.notifiedDoneAt !== void 0 && this.notifiedDoneAt <= now) this.notifiedDoneAt = void 0;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Milliseconds until a pending completion anchor fires, or undefined when
|
|
1244
|
+
* no anchor exists. Lets the watcher schedule one exact timer instead of
|
|
1245
|
+
* waiting for the next coarse poll tick.
|
|
1246
|
+
*/
|
|
1247
|
+
pendingAnchorDelayMs() {
|
|
1248
|
+
if (this.idleAnchoredAt === void 0) return void 0;
|
|
1249
|
+
return Math.max(0, this.graceMs - (this.now() - this.idleAnchoredAt));
|
|
1250
|
+
}
|
|
1251
|
+
evaluate(snapshot) {
|
|
1252
|
+
const now = this.now();
|
|
1253
|
+
if (!this.wasBusy && snapshot.composerBusy) this.busySince = now;
|
|
1254
|
+
if (this.wasBusy && !snapshot.composerBusy) {
|
|
1255
|
+
const busyFor = this.busySince === void 0 ? 0 : now - this.busySince;
|
|
1256
|
+
this.busySince = void 0;
|
|
1257
|
+
if (snapshot.pageHidden && !snapshot.pendingQuestion && busyFor >= this.minBusyMs) {
|
|
1258
|
+
this.idleAnchoredAt = void 0;
|
|
1259
|
+
this.notifiedDoneAt = now;
|
|
1260
|
+
return this.event("done", snapshot.sessionLabel, void 0);
|
|
1261
|
+
}
|
|
1262
|
+
this.idleAnchoredAt = snapshot.pageHidden ? now : void 0;
|
|
1263
|
+
if (!snapshot.pageHidden) this.notifiedDoneAt = now;
|
|
1264
|
+
}
|
|
1265
|
+
this.wasBusy = snapshot.composerBusy;
|
|
1266
|
+
if (snapshot.composerBusy) this.idleAnchoredAt = void 0;
|
|
1267
|
+
else if (this.idleAnchoredAt !== void 0 && !snapshot.pageHidden) {
|
|
1268
|
+
this.idleAnchoredAt = void 0;
|
|
1269
|
+
this.notifiedDoneAt = now;
|
|
1270
|
+
}
|
|
1271
|
+
if (!snapshot.pageHidden) {
|
|
1272
|
+
if (snapshot.pendingQuestion && snapshot.questionKey !== void 0) this.rememberQuestionKey(snapshot.questionKey);
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
if (snapshot.pendingQuestion) {
|
|
1276
|
+
if (snapshot.questionKey === void 0 || this.seenQuestionKeys.has(snapshot.questionKey)) return void 0;
|
|
1277
|
+
this.rememberQuestionKey(snapshot.questionKey);
|
|
1278
|
+
return this.event("question", snapshot.sessionLabel, snapshot.questionKey);
|
|
1279
|
+
}
|
|
1280
|
+
if (this.idleAnchoredAt !== void 0) {
|
|
1281
|
+
if (now - this.idleAnchoredAt < this.graceMs) return void 0;
|
|
1282
|
+
this.idleAnchoredAt = void 0;
|
|
1283
|
+
this.notifiedDoneAt = now;
|
|
1284
|
+
return this.event("done", snapshot.sessionLabel, void 0);
|
|
1285
|
+
}
|
|
1286
|
+
if (this.lastActivityAt === void 0) return void 0;
|
|
1287
|
+
if (now - this.lastActivityAt < this.quietMs) return void 0;
|
|
1288
|
+
if (now - this.lastActivityAt > 18e5) {
|
|
1289
|
+
this.lastActivityAt = void 0;
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
if (this.notifiedDoneAt !== void 0 && this.notifiedDoneAt >= this.lastActivityAt) return void 0;
|
|
1293
|
+
this.notifiedDoneAt = now;
|
|
1294
|
+
return this.event("done", snapshot.sessionLabel, void 0);
|
|
1295
|
+
}
|
|
1296
|
+
rememberQuestionKey(key) {
|
|
1297
|
+
this.seenQuestionKeys.add(key);
|
|
1298
|
+
if (this.seenQuestionKeys.size > MAX_SEEN_QUESTION_KEYS) {
|
|
1299
|
+
const oldest = this.seenQuestionKeys.values().next();
|
|
1300
|
+
if (!oldest.done) this.seenQuestionKeys.delete(oldest.value);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
event(kind, sessionLabel, questionKey) {
|
|
1304
|
+
const text = this.format(kind, sessionLabel);
|
|
1305
|
+
const tag = kind === "question" && questionKey !== void 0 ? `dsh-task-question-${sanitizeTagFragment(questionKey) || "pending"}` : "dsh-task-done";
|
|
1306
|
+
return Object.freeze({
|
|
1307
|
+
kind,
|
|
1308
|
+
title: text.title,
|
|
1309
|
+
body: text.body,
|
|
1310
|
+
tag
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
/**
|
|
1315
|
+
* Mirror of the composer busy signal used for media actions: the run is busy
|
|
1316
|
+
* while the composer is aria-busy, disabled, read-only, or non-interactive.
|
|
1317
|
+
*/
|
|
1318
|
+
function readComposerBusyState(root) {
|
|
1319
|
+
if (typeof root.querySelector !== "function") return false;
|
|
1320
|
+
const card = root.querySelector("[data-composer-card]");
|
|
1321
|
+
if (card === null || typeof card.getAttribute !== "function" || typeof card.querySelector !== "function") return false;
|
|
1322
|
+
if (card.getAttribute("aria-busy") === "true") return true;
|
|
1323
|
+
const field = card.querySelector("textarea,input,[contenteditable=\"true\"],[contenteditable=\"plaintext-only\"]");
|
|
1324
|
+
if (field === null) return false;
|
|
1325
|
+
return field.disabled === true || field.readOnly === true || field.getAttribute?.("aria-disabled") === "true";
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Best-effort session label for the notification body, bounded for the wire.
|
|
1329
|
+
* Callers accept that titles may name the active session on the lock screen.
|
|
1330
|
+
*/
|
|
1331
|
+
function readSessionLabel(title) {
|
|
1332
|
+
if (typeof title !== "string") return "";
|
|
1333
|
+
return title.normalize("NFC").replace(/[\u0000-\u001f\u007f]+/gu, " ").trim().slice(0, 80);
|
|
1334
|
+
}
|
|
1335
|
+
/** Whether the document currently shows a card that waits for user input. */
|
|
1336
|
+
function hasPendingInputQuestion(root) {
|
|
1337
|
+
if (typeof root.querySelector !== "function") return false;
|
|
1338
|
+
return root.querySelector("[data-question-key],[data-plan-review-key]") !== null;
|
|
1339
|
+
}
|
|
1340
|
+
/** Stable key of the waiting input card, when the DOM exposes one. */
|
|
1341
|
+
function pendingInputQuestionKey(root) {
|
|
1342
|
+
if (typeof root.querySelector !== "function") return void 0;
|
|
1343
|
+
const card = root.querySelector("[data-question-key],[data-plan-review-key]");
|
|
1344
|
+
const getAttribute = card?.getAttribute;
|
|
1345
|
+
if (typeof getAttribute !== "function") return void 0;
|
|
1346
|
+
const key = getAttribute.call(card, "data-question-key") ?? getAttribute.call(card, "data-plan-review-key");
|
|
1347
|
+
if (key === null || key === "") return void 0;
|
|
1348
|
+
return key;
|
|
1349
|
+
}
|
|
1350
|
+
/** Observe assistant-side conversation mutations; returns a disposer. */
|
|
1351
|
+
function observeTaskActivity(target, onActivity, observe = typeof MutationObserver === "function" ? MutationObserver : void 0) {
|
|
1352
|
+
if (observe === void 0) return () => void 0;
|
|
1353
|
+
const observer = new observe(() => {
|
|
1354
|
+
onActivity();
|
|
1355
|
+
});
|
|
1356
|
+
try {
|
|
1357
|
+
observer.observe(target, {
|
|
1358
|
+
childList: true,
|
|
1359
|
+
characterData: true,
|
|
1360
|
+
subtree: true
|
|
1361
|
+
});
|
|
1362
|
+
} catch {
|
|
1363
|
+
return () => void 0;
|
|
1364
|
+
}
|
|
1365
|
+
return () => {
|
|
1366
|
+
observer.disconnect();
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
function readNativeBridge() {
|
|
1370
|
+
if (typeof window === "undefined") return void 0;
|
|
1371
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
1372
|
+
if (bridge === void 0 || typeof bridge.capabilities !== "function" || typeof bridge.invoke !== "function") return void 0;
|
|
1373
|
+
return bridge;
|
|
1374
|
+
}
|
|
1375
|
+
/** Parse a `task-notify` SSE payload; rejects anything malformed or empty. */
|
|
1376
|
+
function parseTaskNotifyPayload(value) {
|
|
1377
|
+
let record;
|
|
1378
|
+
if (typeof value === "string") try {
|
|
1379
|
+
const parsed = JSON.parse(value);
|
|
1380
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
1381
|
+
record = parsed;
|
|
1382
|
+
} catch {
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
else if (value !== null && typeof value === "object" && !Array.isArray(value)) record = value;
|
|
1386
|
+
else return void 0;
|
|
1387
|
+
if (typeof record.sessionId !== "string" || record.sessionId.trim() === "") return void 0;
|
|
1388
|
+
const turn = typeof record.turn === "number" && Number.isSafeInteger(record.turn) && record.turn >= 0 ? record.turn : 0;
|
|
1389
|
+
return Object.freeze({
|
|
1390
|
+
sessionId: record.sessionId.slice(0, 128),
|
|
1391
|
+
turn
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
/** Parse the small authenticated event used when the Host revokes this device. */
|
|
1395
|
+
function isDeviceRevokedPayload(value) {
|
|
1396
|
+
let record;
|
|
1397
|
+
if (typeof value === "string") try {
|
|
1398
|
+
const parsed = JSON.parse(value);
|
|
1399
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
1400
|
+
record = parsed;
|
|
1401
|
+
} catch {
|
|
1402
|
+
return false;
|
|
1403
|
+
}
|
|
1404
|
+
else if (value !== null && typeof value === "object" && !Array.isArray(value)) record = value;
|
|
1405
|
+
else return false;
|
|
1406
|
+
return record.reason === "device_revoked";
|
|
1407
|
+
}
|
|
1408
|
+
/** Tell the Android shell to transition the active record to the revoked state. */
|
|
1409
|
+
function fireDeviceRevoked() {
|
|
1410
|
+
const bridge = readNativeBridge();
|
|
1411
|
+
if (bridge === void 0) return false;
|
|
1412
|
+
bridge.invoke("device.revoked", {}).catch(() => void 0);
|
|
1413
|
+
return true;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Deliver one notification through the native bridge when available.
|
|
1417
|
+
* Returns false when there is no bridge (desktop browsers keep their own
|
|
1418
|
+
* UI) so callers can fall back; a denial flips the module sticky flag via
|
|
1419
|
+
* onDenied for the session-lifetime nag guard.
|
|
1420
|
+
*/
|
|
1421
|
+
function fireTaskNotifyEvent(event, onDenied) {
|
|
1422
|
+
const bridge = readNativeBridge();
|
|
1423
|
+
if (bridge === void 0) return false;
|
|
1424
|
+
Promise.resolve().then(() => bridge.capabilities()).then((capabilities) => {
|
|
1425
|
+
if (!capabilities.includes("notification.notify")) return;
|
|
1426
|
+
return bridge.invoke("notification.notify", {
|
|
1427
|
+
title: event.title,
|
|
1428
|
+
body: event.body,
|
|
1429
|
+
tag: event.tag
|
|
1430
|
+
});
|
|
1431
|
+
}).then(() => void 0, (error) => {
|
|
1432
|
+
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
|
|
1433
|
+
if ((code === "permission_denied" || code === "denied") && onDenied !== void 0) onDenied();
|
|
1434
|
+
});
|
|
1435
|
+
return true;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Watch the rendered conversation for finished runs and waiting questions,
|
|
1439
|
+
* notifying through the native bridge while the page is hidden. Returns a
|
|
1440
|
+
* disposer. Silent when no native bridge exists (desktop browsers keep their
|
|
1441
|
+
* own UI) or after the user denies the permission.
|
|
1442
|
+
*/
|
|
1443
|
+
function installTaskCompletionWatcher(options) {
|
|
1444
|
+
if (typeof document === "undefined" || typeof window === "undefined") return () => void 0;
|
|
1445
|
+
const tracker = new TaskNotifyTracker({ format: options.label });
|
|
1446
|
+
const completionFallback = options.completionFallback ?? true;
|
|
1447
|
+
let notifyBlocked = false;
|
|
1448
|
+
let timer = 0;
|
|
1449
|
+
let anchorTimer = 0;
|
|
1450
|
+
const fire = (event) => {
|
|
1451
|
+
if (notifyBlocked) return;
|
|
1452
|
+
if (!fireTaskNotifyEvent(event, () => {
|
|
1453
|
+
notifyBlocked = true;
|
|
1454
|
+
})) return;
|
|
1455
|
+
};
|
|
1456
|
+
const evaluateNow = () => {
|
|
1457
|
+
const busy = completionFallback && readComposerBusyState(document);
|
|
1458
|
+
const event = tracker.evaluate({
|
|
1459
|
+
pageHidden: document.hidden,
|
|
1460
|
+
pendingQuestion: hasPendingInputQuestion(document),
|
|
1461
|
+
questionKey: pendingInputQuestionKey(document),
|
|
1462
|
+
sessionLabel: readSessionLabel(document.title),
|
|
1463
|
+
composerBusy: busy
|
|
1464
|
+
});
|
|
1465
|
+
if (event !== void 0) fire(event);
|
|
1466
|
+
scheduleAnchorTimer();
|
|
1467
|
+
};
|
|
1468
|
+
const scheduleAnchorTimer = () => {
|
|
1469
|
+
const delay = tracker.pendingAnchorDelayMs();
|
|
1470
|
+
if (delay === void 0) {
|
|
1471
|
+
if (anchorTimer !== 0) {
|
|
1472
|
+
window.clearTimeout(anchorTimer);
|
|
1473
|
+
anchorTimer = 0;
|
|
1474
|
+
}
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
if (anchorTimer !== 0) return;
|
|
1478
|
+
anchorTimer = window.setTimeout(() => {
|
|
1479
|
+
anchorTimer = 0;
|
|
1480
|
+
evaluateNow();
|
|
1481
|
+
}, delay);
|
|
1482
|
+
};
|
|
1483
|
+
const disposeObservation = observeTaskActivity(document.documentElement, () => {
|
|
1484
|
+
if (completionFallback) tracker.markActivity();
|
|
1485
|
+
evaluateNow();
|
|
1486
|
+
});
|
|
1487
|
+
const onVisibility = () => {
|
|
1488
|
+
evaluateNow();
|
|
1489
|
+
};
|
|
1490
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
1491
|
+
if (typeof window.setInterval === "function") timer = window.setInterval(evaluateNow, 15e3);
|
|
1492
|
+
evaluateNow();
|
|
1493
|
+
return () => {
|
|
1494
|
+
disposeObservation();
|
|
1495
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
1496
|
+
if (timer !== 0) window.clearInterval(timer);
|
|
1497
|
+
if (anchorTimer !== 0) window.clearTimeout(anchorTimer);
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1132
1500
|
//#endregion
|
|
1133
1501
|
//#region src/native-mobile.ts
|
|
1134
1502
|
/**
|
|
@@ -1148,7 +1516,14 @@ window.__ModuleLoader__.load({
|
|
|
1148
1516
|
still collapsed the sidebar. Keep the neutral state explicitly invisible —
|
|
1149
1517
|
the query restores the fixed scrim, and its more specific [hidden] rule
|
|
1150
1518
|
keeps winning there. */
|
|
1151
|
-
.dsh-native-mobile-backdrop,.dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:none; }
|
|
1519
|
+
.dsh-native-mobile-backdrop,.dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:none; }
|
|
1520
|
+
.dsh-mobile-settings_row { display:flex; align-items:center; gap:8px; padding:16px 0; border-bottom:0.5px solid var(--dsw-alias-border-l2); }
|
|
1521
|
+
.dsh-mobile-settings_rowText { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; padding-right:48px; }
|
|
1522
|
+
.dsh-mobile-settings_title { color:var(--dsw-alias-label-primary); font-size:14px; font-weight:400; line-height:22px; }
|
|
1523
|
+
.dsh-mobile-settings_desc { color:var(--dsw-alias-label-tertiary); font-size:12px; font-weight:400; line-height:18px; }
|
|
1524
|
+
.dsh-mobile-settings_selector { display:inline-flex; align-items:center; justify-content:center; min-width:max-content; height:36px; padding:0 14px; border:0; border-radius:18px; background:var(--dsw-alias-bg-module-platform); color:var(--dsw-alias-label-primary); font:inherit; font-size:14px; line-height:22px; cursor:pointer; }
|
|
1525
|
+
.dsh-mobile-settings_selector:hover:not(:disabled) { background:var(--dsw-alias-interactive-bg-hover); }
|
|
1526
|
+
.dsh-mobile-settings_selector:disabled { cursor:wait; opacity:.55; }
|
|
1152
1527
|
@media ${NATIVE_MOBILE_OVERLAY_QUERY} {
|
|
1153
1528
|
html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }
|
|
1154
1529
|
html.dsh-native-mobile-active { --dsh-mobile-motion-duration:200ms; --dsh-mobile-motion-ease:cubic-bezier(.22,1,.36,1); }
|
|
@@ -1191,8 +1566,12 @@ window.__ModuleLoader__.load({
|
|
|
1191
1566
|
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }
|
|
1192
1567
|
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }
|
|
1193
1568
|
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) { position:fixed !important; z-index:244 !important; top:env(safe-area-inset-top) !important; left:0 !important; box-sizing:border-box !important; width:50px !important; height:52px !important; padding:4px !important; border:0 !important; background:transparent !important; }
|
|
1194
|
-
|
|
1195
|
-
|
|
1569
|
+
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) > :not([data-dsh-mobile-toggle]) { display:none !important; }
|
|
1570
|
+
/* The stock settings shell is mounted under the collapsed sidebar. Let
|
|
1571
|
+
its fixed overlay escape the zero-width drawer while it is open. */
|
|
1572
|
+
[data-dsh-mobile-sidebar]:has([data-dsh-mobile-settings]) { width:100vw !important; inset:0 !important; }
|
|
1573
|
+
[data-dsh-mobile-sidebar]:has([data-dsh-mobile-settings]) > * { width:100vw !important; overflow:visible !important; }
|
|
1574
|
+
[data-dsh-mobile-toggle] { width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; }
|
|
1196
1575
|
[data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-toggle] > svg[class*="_railFish"] { transform:translateY(-4px) !important; }
|
|
1197
1576
|
.dsh-native-mobile-backdrop { display:block; position:fixed; z-index:235; inset:env(safe-area-inset-top) 0 0; border:0; background:rgb(15 23 42 / 32%); }
|
|
1198
1577
|
.dsh-native-mobile-backdrop:not([hidden]) { animation:dsh-mobile-fade-in var(--dsh-mobile-motion-duration) ease-out; }
|
|
@@ -1208,11 +1587,11 @@ window.__ModuleLoader__.load({
|
|
|
1208
1587
|
[data-dsh-mobile-settings-list] [class*="_navCell"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }
|
|
1209
1588
|
[data-dsh-mobile-settings-list] [aria-current="true"] { border-color:transparent !important; outline:0 !important; box-shadow:none !important; }
|
|
1210
1589
|
[data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1590
|
+
[data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }
|
|
1591
|
+
[data-dsh-mobile-settings-header] [class*="_close"] { width:36px !important; height:36px !important; }
|
|
1592
|
+
[data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }
|
|
1593
|
+
[data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }
|
|
1594
|
+
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] > [class*="_row"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }
|
|
1216
1595
|
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_rowText"] { width:100% !important; padding-right:0 !important; }
|
|
1217
1596
|
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:44px !important; max-width:100% !important; }
|
|
1218
1597
|
[data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }
|
|
@@ -1281,8 +1660,9 @@ window.__ModuleLoader__.load({
|
|
|
1281
1660
|
[data-dsh-mobile-center] [role="dialog"][aria-label*="Context"] :is(dt,dd) { white-space:nowrap !important; word-break:keep-all !important; }
|
|
1282
1661
|
.dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:block; position:fixed; z-index:330; top:max(12px,env(safe-area-inset-top)); left:50%; max-width:calc(100vw - 32px); box-sizing:border-box; padding:7px 14px; border:1px solid rgb(15 23 42 / 10%); border-radius:999px; background:rgb(15 23 42 / 92%); color:#fff; font-size:13px; line-height:20px; text-align:center; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; opacity:0; pointer-events:none; transform:translate(-50%,-8px); transition:opacity 160ms ease,transform 160ms ease; }
|
|
1283
1662
|
.dsh-mobile-branch-toast[data-visible="true"],.dsh-mobile-media-toast[data-visible="true"] { opacity:1; transform:translate(-50%,0); }
|
|
1284
|
-
|
|
1285
|
-
|
|
1663
|
+
/* Fallback only: when the native Add group has no item to clone, keep the
|
|
1664
|
+
camera action on the same row metrics and tokens as the native menu. */
|
|
1665
|
+
.dsh-mobile-media-action { box-sizing:border-box; display:flex; align-items:center; justify-content:flex-start; gap:8px; width:100%; min-width:0; min-height:40px; padding:8px 10px; border:0; border-radius:10px; background:transparent; color:var(--dsw-alias-label-primary,inherit); cursor:pointer; font:inherit; font-size:14px; line-height:22px; text-align:left; touch-action:manipulation; }
|
|
1286
1666
|
.dsh-mobile-media-action:active { opacity:.72; }
|
|
1287
1667
|
.dsh-mobile-media-action:focus-visible { outline:2px solid var(--dsw-alias-interactive-border-focus,#4c82f7); outline-offset:1px; }
|
|
1288
1668
|
.dsh-mobile-media-action:disabled { cursor:default; opacity:.38; }
|
|
@@ -1416,19 +1796,12 @@ window.__ModuleLoader__.load({
|
|
|
1416
1796
|
const language = resolveNativeMobileLanguage(document.documentElement.lang, browserLanguages);
|
|
1417
1797
|
const restoreLanguageMarker = applyNativeMobileLanguageMarker(document.documentElement, language);
|
|
1418
1798
|
const label = (italian, english, chinese) => language === "it" ? italian : language === "zh" ? chinese : english;
|
|
1419
|
-
const mediaIcon = (
|
|
1799
|
+
const mediaIcon = () => {
|
|
1420
1800
|
const namespace = "http://www.w3.org/2000/svg";
|
|
1421
1801
|
const icon = document.createElementNS(namespace, "svg");
|
|
1422
1802
|
icon.setAttribute("viewBox", "0 0 16 16");
|
|
1423
1803
|
icon.setAttribute("fill", "none");
|
|
1424
1804
|
icon.setAttribute("aria-hidden", "true");
|
|
1425
|
-
if (kind === "attachment") {
|
|
1426
|
-
const path = document.createElementNS(namespace, "path");
|
|
1427
|
-
path.setAttribute("d", "M5.5498 9.75V5H6.9502V9.75C6.9502 10.3299 7.4201 10.7998 8 10.7998C8.5799 10.7998 9.0498 10.3299 9.0498 9.75V4.5C9.0498 2.9536 7.7964 1.7002 6.25 1.7002C4.7036 1.7002 3.4502 2.9536 3.4502 4.5V9.75C3.4502 12.2629 5.4871 14.2998 8 14.2998C10.5129 14.2998 12.5498 12.2629 12.5498 9.75V4H13.9502V9.75C13.9502 13.0361 11.2861 15.7002 8 15.7002C4.71391 15.7002 2.0498 13.0361 2.0498 9.75V4.5C2.04981 2.1804 3.9304 0.299806 6.25 0.299805C8.5696 0.299805 10.4502 2.1804 10.4502 4.5V9.75C10.4502 11.1031 9.3531 12.2002 8 12.2002C6.6469 12.2002 5.5498 11.1031 5.5498 9.75Z");
|
|
1428
|
-
path.setAttribute("fill", "currentColor");
|
|
1429
|
-
icon.append(path);
|
|
1430
|
-
return icon;
|
|
1431
|
-
}
|
|
1432
1805
|
const body = document.createElementNS(namespace, "path");
|
|
1433
1806
|
body.setAttribute("d", "M5.15 3.2 6.05 2h3.9l.9 1.2h1.45c1.05 0 1.9.85 1.9 1.9v6c0 1.05-.85 1.9-1.9 1.9H3.7a1.9 1.9 0 0 1-1.9-1.9v-6c0-1.05.85-1.9 1.9-1.9h1.45Zm-1.45 1.3a.6.6 0 0 0-.6.6v6c0 .33.27.6.6.6h8.6a.6.6 0 0 0 .6-.6v-6a.6.6 0 0 0-.6-.6h-2.1l-.9-1.2H6.7l-.9 1.2H3.7Z");
|
|
1434
1807
|
body.setAttribute("fill", "currentColor");
|
|
@@ -1441,13 +1814,15 @@ window.__ModuleLoader__.load({
|
|
|
1441
1814
|
icon.append(body, lens);
|
|
1442
1815
|
return icon;
|
|
1443
1816
|
};
|
|
1444
|
-
const createMediaAction = (
|
|
1817
|
+
const createMediaAction = (text) => {
|
|
1445
1818
|
const button = document.createElement("button");
|
|
1446
1819
|
button.type = "button";
|
|
1447
1820
|
button.className = "dsh-mobile-media-action";
|
|
1448
1821
|
button.lang = language;
|
|
1449
|
-
button.dataset.dshMobileMediaAction =
|
|
1450
|
-
|
|
1822
|
+
button.dataset.dshMobileMediaAction = "camera";
|
|
1823
|
+
const icon = document.createElement("span");
|
|
1824
|
+
icon.append(mediaIcon());
|
|
1825
|
+
button.append(icon);
|
|
1451
1826
|
const caption = document.createElement("span");
|
|
1452
1827
|
caption.textContent = text;
|
|
1453
1828
|
button.append(caption);
|
|
@@ -1484,17 +1859,8 @@ window.__ModuleLoader__.load({
|
|
|
1484
1859
|
mediaToast.setAttribute("role", "status");
|
|
1485
1860
|
mediaToast.setAttribute("aria-live", "polite");
|
|
1486
1861
|
document.body.append(mediaToast);
|
|
1487
|
-
const
|
|
1488
|
-
mediaActions.className = "dsh-mobile-media-shortcuts";
|
|
1489
|
-
mediaActions.lang = language;
|
|
1490
|
-
mediaActions.dataset.dshMobileMediaShortcuts = "true";
|
|
1491
|
-
mediaActions.setAttribute("role", "group");
|
|
1492
|
-
mediaActions.setAttribute("aria-label", label("Aggiungi immagine", "Add image", "添加图片"));
|
|
1493
|
-
const fileButton = createMediaAction("attachment", label("Scegli immagine", "Choose image", "选择图片"));
|
|
1494
|
-
const cameraButton = createMediaAction("camera", label("Scatta foto", "Take photo", "拍照"));
|
|
1495
|
-
fileButton.disabled = true;
|
|
1862
|
+
const cameraButton = createMediaAction(label("Scatta foto", "Take photo", "拍照"));
|
|
1496
1863
|
cameraButton.disabled = true;
|
|
1497
|
-
mediaActions.append(fileButton, cameraButton);
|
|
1498
1864
|
let branchToastTimer = 0;
|
|
1499
1865
|
let mediaToastTimer = 0;
|
|
1500
1866
|
const showMediaToast = (message) => {
|
|
@@ -1566,11 +1932,11 @@ window.__ModuleLoader__.load({
|
|
|
1566
1932
|
if (files.length === 0 || !mediaRequestIsCurrent(context)) return;
|
|
1567
1933
|
dispatchComposerImageDrop(document, files);
|
|
1568
1934
|
};
|
|
1569
|
-
const
|
|
1935
|
+
const launchBrowserCamera = (context) => {
|
|
1570
1936
|
const input = document.createElement("input");
|
|
1571
1937
|
input.type = "file";
|
|
1572
|
-
input.accept = "image
|
|
1573
|
-
|
|
1938
|
+
input.accept = "image/*";
|
|
1939
|
+
input.capture = "environment";
|
|
1574
1940
|
input.hidden = true;
|
|
1575
1941
|
const signal = mediaPickerAbortController.signal;
|
|
1576
1942
|
let cleanupTimer = 0;
|
|
@@ -1619,17 +1985,51 @@ window.__ModuleLoader__.load({
|
|
|
1619
1985
|
input.click();
|
|
1620
1986
|
} catch {
|
|
1621
1987
|
cleanup();
|
|
1622
|
-
if (mediaRequestIsCurrent(context)) showMediaToast(label("Impossibile aprire
|
|
1988
|
+
if (mediaRequestIsCurrent(context)) showMediaToast(label("Impossibile aprire la fotocamera", "Could not open the camera", "无法打开相机"));
|
|
1623
1989
|
}
|
|
1624
1990
|
};
|
|
1625
1991
|
const dismissCommandMenu = () => {
|
|
1626
1992
|
(boundComposer?.querySelector("button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]"))?.click();
|
|
1627
1993
|
};
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1994
|
+
const addGroupLabel = label("Aggiungi", "Add", "添加");
|
|
1995
|
+
const placeCameraAction = (commandMenu) => {
|
|
1996
|
+
const sectionTitle = Array.from(commandMenu.querySelectorAll("[role=\"listbox\"] [role=\"presentation\"]")).find((candidate) => !candidate.hasAttribute("data-source") && candidate.textContent?.trim() === addGroupLabel);
|
|
1997
|
+
if (sectionTitle === void 0) {
|
|
1998
|
+
cameraButton.remove();
|
|
1999
|
+
return false;
|
|
2000
|
+
}
|
|
2001
|
+
cameraButton.className = "dsh-mobile-media-action";
|
|
2002
|
+
const nativeItem = (() => {
|
|
2003
|
+
let sibling = sectionTitle.nextElementSibling;
|
|
2004
|
+
while (sibling instanceof HTMLElement && sibling.getAttribute("role") !== "presentation") {
|
|
2005
|
+
if (sibling.matches("button[role=\"option\"]:not([aria-selected=\"true\"])")) return sibling;
|
|
2006
|
+
sibling = sibling.nextElementSibling;
|
|
2007
|
+
}
|
|
2008
|
+
})();
|
|
2009
|
+
if (nativeItem !== void 0) {
|
|
2010
|
+
cameraButton.className = nativeItem.className;
|
|
2011
|
+
const cameraIcon = cameraButton.firstElementChild;
|
|
2012
|
+
const cameraLabel = cameraButton.lastElementChild;
|
|
2013
|
+
const nativeIcon = firstByClassSuffix(nativeItem, "_itemIcon");
|
|
2014
|
+
const nativeLabel = firstByClassSuffix(nativeItem, "_itemName");
|
|
2015
|
+
if (cameraIcon instanceof HTMLElement && nativeIcon !== void 0) cameraIcon.className = nativeIcon.className;
|
|
2016
|
+
if (cameraLabel instanceof HTMLElement && nativeLabel !== void 0) cameraLabel.className = nativeLabel.className;
|
|
2017
|
+
}
|
|
2018
|
+
cameraButton.setAttribute("role", "option");
|
|
2019
|
+
cameraButton.setAttribute("aria-selected", "false");
|
|
2020
|
+
cameraButton.setAttribute("aria-label", label("Scatta foto", "Take photo", "拍照"));
|
|
2021
|
+
let lastItem = sectionTitle;
|
|
2022
|
+
let sibling = sectionTitle.nextElementSibling;
|
|
2023
|
+
while (sibling instanceof HTMLElement && sibling.getAttribute("role") !== "presentation") {
|
|
2024
|
+
if (sibling !== cameraButton) lastItem = sibling;
|
|
2025
|
+
sibling = sibling.nextElementSibling;
|
|
1632
2026
|
}
|
|
2027
|
+
if (cameraButton.parentElement !== sectionTitle.parentElement || cameraButton.previousElementSibling !== lastItem) lastItem.after(cameraButton);
|
|
2028
|
+
return true;
|
|
2029
|
+
};
|
|
2030
|
+
const setCameraActionDisabled = (disabled, title) => {
|
|
2031
|
+
if (cameraButton.disabled !== disabled) cameraButton.disabled = disabled;
|
|
2032
|
+
if (cameraButton.title !== title) cameraButton.title = title;
|
|
1633
2033
|
};
|
|
1634
2034
|
const quietMediaPointer = (event) => {
|
|
1635
2035
|
event.preventDefault();
|
|
@@ -1637,9 +2037,9 @@ window.__ModuleLoader__.load({
|
|
|
1637
2037
|
const active = document.activeElement;
|
|
1638
2038
|
if (active instanceof HTMLElement && (active.matches("input,textarea") || active.isContentEditable)) active.blur();
|
|
1639
2039
|
};
|
|
1640
|
-
const
|
|
2040
|
+
const takePhoto = () => {
|
|
1641
2041
|
if (!canAcceptComposerDrop()) {
|
|
1642
|
-
|
|
2042
|
+
setCameraActionDisabled(true, label("Allegati immagine non disponibili", "Image attachments are unavailable", "图片附件不可用"));
|
|
1643
2043
|
dismissCommandMenu();
|
|
1644
2044
|
return;
|
|
1645
2045
|
}
|
|
@@ -1647,36 +2047,21 @@ window.__ModuleLoader__.load({
|
|
|
1647
2047
|
dismissCommandMenu();
|
|
1648
2048
|
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
1649
2049
|
if (bridge === void 0) {
|
|
1650
|
-
|
|
2050
|
+
launchBrowserCamera(context);
|
|
1651
2051
|
return;
|
|
1652
2052
|
}
|
|
1653
|
-
|
|
1654
|
-
const input = camera ? {} : { accept: [
|
|
1655
|
-
"image/png",
|
|
1656
|
-
"image/jpeg",
|
|
1657
|
-
"image/webp",
|
|
1658
|
-
"image/gif"
|
|
1659
|
-
] };
|
|
1660
|
-
Promise.resolve().then(() => bridge.invoke(action, input)).then((value) => {
|
|
2053
|
+
Promise.resolve().then(() => bridge.invoke("camera.capture", {})).then((value) => {
|
|
1661
2054
|
if (!mediaRequestIsCurrent(context)) return;
|
|
1662
2055
|
if (value instanceof File) deliverImages([value], context);
|
|
1663
|
-
else showMediaToast(label("
|
|
2056
|
+
else showMediaToast(label("La foto scattata non è utilizzabile", "The captured photo is unavailable", "所拍照片不可用"));
|
|
1664
2057
|
}).catch((error) => {
|
|
1665
2058
|
if (!mediaRequestIsCurrent(context)) return;
|
|
1666
2059
|
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
|
|
1667
2060
|
if (code === "cancelled") return;
|
|
1668
|
-
showMediaToast(code === "payload_too_large" ? label("
|
|
2061
|
+
showMediaToast(code === "payload_too_large" ? label("La foto supera il limite di 8 MiB", "The photo exceeds the 8 MiB limit", "照片超过 8 MiB 限制") : label("Impossibile aggiungere la foto", "Could not attach the photo", "无法附加照片"));
|
|
1669
2062
|
});
|
|
1670
2063
|
};
|
|
1671
|
-
const chooseImage = () => {
|
|
1672
|
-
pickImage(false);
|
|
1673
|
-
};
|
|
1674
|
-
const takePhoto = () => {
|
|
1675
|
-
pickImage(true);
|
|
1676
|
-
};
|
|
1677
|
-
fileButton.addEventListener("pointerdown", quietMediaPointer);
|
|
1678
2064
|
cameraButton.addEventListener("pointerdown", quietMediaPointer);
|
|
1679
|
-
fileButton.addEventListener("click", chooseImage);
|
|
1680
2065
|
cameraButton.addEventListener("click", takePhoto);
|
|
1681
2066
|
const showBranchToast = () => {
|
|
1682
2067
|
const header = document.querySelector("[data-dsh-mobile-header]");
|
|
@@ -1785,8 +2170,8 @@ window.__ModuleLoader__.load({
|
|
|
1785
2170
|
if (center === void 0) {
|
|
1786
2171
|
bindHistoryScroller(void 0);
|
|
1787
2172
|
syncMediaBinding(null);
|
|
1788
|
-
|
|
1789
|
-
|
|
2173
|
+
setCameraActionDisabled(true, label("Apri prima una sessione", "Open a session first", "请先打开会话"));
|
|
2174
|
+
cameraButton.remove();
|
|
1790
2175
|
return;
|
|
1791
2176
|
}
|
|
1792
2177
|
if (center !== void 0) {
|
|
@@ -1824,8 +2209,8 @@ window.__ModuleLoader__.load({
|
|
|
1824
2209
|
const composerRow = composerCard?.querySelector(":scope > [data-input-scroll]")?.nextElementSibling;
|
|
1825
2210
|
if (!(composerRow instanceof HTMLElement)) {
|
|
1826
2211
|
syncMediaBinding(null);
|
|
1827
|
-
|
|
1828
|
-
|
|
2212
|
+
setCameraActionDisabled(true, label("Apri prima una sessione", "Open a session first", "请先打开会话"));
|
|
2213
|
+
cameraButton.remove();
|
|
1829
2214
|
}
|
|
1830
2215
|
if (composerRow instanceof HTMLElement) {
|
|
1831
2216
|
composerRow.dataset.dshMobileComposerRow = "true";
|
|
@@ -1840,10 +2225,11 @@ window.__ModuleLoader__.load({
|
|
|
1840
2225
|
const composerBusy = composerCard?.getAttribute("aria-busy") === "true" || composerInput?.disabled === true || composerInput?.readOnly === true || composerEditor?.getAttribute("aria-disabled") === "true";
|
|
1841
2226
|
const attachmentBlocked = !canAcceptComposerDrop();
|
|
1842
2227
|
const mediaDisabled = conversation === null || currentSessionOrigin().sessionId === null || composerBusy || attachmentBlocked;
|
|
1843
|
-
const mediaTitle = composerBusy ? label("Attendi il completamento della risposta", "Wait for the response to finish", "请等待回复完成") : attachmentBlocked ? label("Allegati immagine non disponibili", "Image attachments are unavailable", "图片附件不可用") : mediaDisabled ? label("Apri prima una sessione", "Open a session first", "请先打开会话") : label("
|
|
1844
|
-
|
|
2228
|
+
const mediaTitle = composerBusy ? label("Attendi il completamento della risposta", "Wait for the response to finish", "请等待回复完成") : attachmentBlocked ? label("Allegati immagine non disponibili", "Image attachments are unavailable", "图片附件不可用") : mediaDisabled ? label("Apri prima una sessione", "Open a session first", "请先打开会话") : label("Scatta una foto e allegala", "Take a photo and attach it", "拍照并附加到当前消息");
|
|
2229
|
+
setCameraActionDisabled(mediaDisabled, mediaTitle);
|
|
1845
2230
|
const commandMenu = composerCard?.querySelector("[data-trigger-menu]") ?? null;
|
|
1846
|
-
if (commandMenu !== null
|
|
2231
|
+
if (commandMenu !== null) placeCameraAction(commandMenu);
|
|
2232
|
+
else cameraButton.remove();
|
|
1847
2233
|
}
|
|
1848
2234
|
if (composerTrailing !== void 0 && composerTrailing !== composerTools) {
|
|
1849
2235
|
composerTrailing.dataset.dshMobileComposerTrailing = "true";
|
|
@@ -1899,8 +2285,19 @@ window.__ModuleLoader__.load({
|
|
|
1899
2285
|
if (sidebar?.dataset.open === "true") toggle?.click();
|
|
1900
2286
|
});
|
|
1901
2287
|
sync();
|
|
2288
|
+
const disposeTaskWatcher = installTaskCompletionWatcher({
|
|
2289
|
+
completionFallback: false,
|
|
2290
|
+
label: (kind, sessionLabel) => kind === "done" ? {
|
|
2291
|
+
title: label("Attività completata", "Task finished", "任务已完成"),
|
|
2292
|
+
body: sessionLabel === "" ? label("Il tuo task DSH è terminato", "Your DSH task finished", "你的 DSH 任务已完成") : label(`Il tuo task DSH è terminato: ${sessionLabel}`, `Your DSH task finished: ${sessionLabel}`, `你的 DSH 任务已完成:${sessionLabel}`)
|
|
2293
|
+
} : {
|
|
2294
|
+
title: label("È richiesto un input", "Input needed", "需要你确认"),
|
|
2295
|
+
body: label("DSH attende una tua scelta", "DSH waits for your choice", "DSH 等待你的选择")
|
|
2296
|
+
}
|
|
2297
|
+
});
|
|
1902
2298
|
return () => {
|
|
1903
2299
|
disposed = true;
|
|
2300
|
+
disposeTaskWatcher();
|
|
1904
2301
|
mediaRequestGeneration += 1;
|
|
1905
2302
|
mediaPickerAbortController.abort();
|
|
1906
2303
|
restoreLanguageMarker();
|
|
@@ -1908,15 +2305,13 @@ window.__ModuleLoader__.load({
|
|
|
1908
2305
|
observer.disconnect();
|
|
1909
2306
|
overlayQuery.removeEventListener("change", schedule);
|
|
1910
2307
|
document.removeEventListener("click", onBranchClick, true);
|
|
1911
|
-
fileButton.removeEventListener("pointerdown", quietMediaPointer);
|
|
1912
2308
|
cameraButton.removeEventListener("pointerdown", quietMediaPointer);
|
|
1913
|
-
fileButton.removeEventListener("click", chooseImage);
|
|
1914
2309
|
cameraButton.removeEventListener("click", takePhoto);
|
|
1915
2310
|
if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer);
|
|
1916
2311
|
if (mediaToastTimer !== 0) window.clearTimeout(mediaToastTimer);
|
|
1917
2312
|
branchToast.remove();
|
|
1918
2313
|
mediaToast.remove();
|
|
1919
|
-
|
|
2314
|
+
cameraButton.remove();
|
|
1920
2315
|
if (scheduled !== 0) cancelAnimationFrame(scheduled);
|
|
1921
2316
|
if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame);
|
|
1922
2317
|
if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame);
|
|
@@ -2053,6 +2448,82 @@ window.__ModuleLoader__.load({
|
|
|
2053
2448
|
if (className !== void 0) node.className = className;
|
|
2054
2449
|
return node;
|
|
2055
2450
|
}
|
|
2451
|
+
function mobileSwitchComputerCopy(locale) {
|
|
2452
|
+
if (locale === "zh") return {
|
|
2453
|
+
title: "切换电脑",
|
|
2454
|
+
description: "返回已配对设备列表,选择要打开的电脑。",
|
|
2455
|
+
action: "切换电脑",
|
|
2456
|
+
busy: "正在切换…",
|
|
2457
|
+
retry: "重试",
|
|
2458
|
+
error: "切换失败,请重试。"
|
|
2459
|
+
};
|
|
2460
|
+
if (locale === "it") return {
|
|
2461
|
+
title: "Cambia computer",
|
|
2462
|
+
description: "Torna all’elenco dei computer associati e scegli quale aprire.",
|
|
2463
|
+
action: "Cambia computer",
|
|
2464
|
+
busy: "Cambio in corso…",
|
|
2465
|
+
retry: "Riprova",
|
|
2466
|
+
error: "Cambio non riuscito. Riprova."
|
|
2467
|
+
};
|
|
2468
|
+
return {
|
|
2469
|
+
title: "Switch computer",
|
|
2470
|
+
description: "Return to the paired-device list and choose a computer to open.",
|
|
2471
|
+
action: "Switch computer",
|
|
2472
|
+
busy: "Switching…",
|
|
2473
|
+
retry: "Retry",
|
|
2474
|
+
error: "Could not switch computers. Try again."
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
/** Render the native-app-only action in DSH's General settings row style. */
|
|
2478
|
+
function MobileSwitchComputerRow() {
|
|
2479
|
+
const [nativeReady, setNativeReady] = (0, react.useState)(false);
|
|
2480
|
+
const [state, setState] = (0, react.useState)("idle");
|
|
2481
|
+
(0, react.useEffect)(() => {
|
|
2482
|
+
let disposed = false;
|
|
2483
|
+
const checkNativeAction = () => {
|
|
2484
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
2485
|
+
if (bridge === void 0) {
|
|
2486
|
+
setNativeReady(false);
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
Promise.resolve().then(() => bridge.capabilities()).then((capabilities) => {
|
|
2490
|
+
if (!disposed) setNativeReady(capabilities.includes("mobile.switch-computer"));
|
|
2491
|
+
}, () => {
|
|
2492
|
+
if (!disposed) setNativeReady(false);
|
|
2493
|
+
});
|
|
2494
|
+
};
|
|
2495
|
+
checkNativeAction();
|
|
2496
|
+
window.addEventListener("dsh-mobile-native-ready", checkNativeAction);
|
|
2497
|
+
return () => {
|
|
2498
|
+
disposed = true;
|
|
2499
|
+
window.removeEventListener("dsh-mobile-native-ready", checkNativeAction);
|
|
2500
|
+
};
|
|
2501
|
+
}, []);
|
|
2502
|
+
if (!nativeReady) return null;
|
|
2503
|
+
const locale = selectedMobileControlLocale();
|
|
2504
|
+
const copy = mobileSwitchComputerCopy(locale);
|
|
2505
|
+
const invoke = () => {
|
|
2506
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
2507
|
+
if (bridge === void 0 || state === "busy") return;
|
|
2508
|
+
setState("busy");
|
|
2509
|
+
bridge.invoke("mobile.switch-computer", {}).catch(() => {
|
|
2510
|
+
setState("error");
|
|
2511
|
+
});
|
|
2512
|
+
};
|
|
2513
|
+
return (0, react.createElement)("div", {
|
|
2514
|
+
className: "dsh-mobile-settings_row",
|
|
2515
|
+
lang: locale
|
|
2516
|
+
}, (0, react.createElement)("div", { className: "dsh-mobile-settings_rowText" }, (0, react.createElement)("div", { className: "dsh-mobile-settings_title" }, copy.title), (0, react.createElement)("div", {
|
|
2517
|
+
className: "dsh-mobile-settings_desc",
|
|
2518
|
+
"aria-live": "polite"
|
|
2519
|
+
}, state === "error" ? copy.error : copy.description)), (0, react.createElement)("button", {
|
|
2520
|
+
type: "button",
|
|
2521
|
+
className: "dsh-mobile-settings_selector",
|
|
2522
|
+
disabled: state === "busy",
|
|
2523
|
+
"aria-busy": state === "busy" ? "true" : void 0,
|
|
2524
|
+
onClick: invoke
|
|
2525
|
+
}, state === "busy" ? copy.busy : state === "error" ? copy.retry : copy.action));
|
|
2526
|
+
}
|
|
2056
2527
|
const CONTROL_REQUEST_TIMEOUT_MS = 15e3;
|
|
2057
2528
|
const LONG_CONTROL_REQUEST_TIMEOUT_MS = 21e4;
|
|
2058
2529
|
const GITHUB_RELEASES_URL = "https://github.com/saya-ch/dsh-mobile/releases";
|
|
@@ -2193,6 +2664,33 @@ window.__ModuleLoader__.load({
|
|
|
2193
2664
|
remoteTab.setAttribute("aria-pressed", "false");
|
|
2194
2665
|
switcher.append(lanTab, remoteTab);
|
|
2195
2666
|
const lanView = element("div", "dsh-mobile-control__view");
|
|
2667
|
+
const lanSetup = element("section", "dsh-mobile-control__lan-setup");
|
|
2668
|
+
lanSetup.hidden = true;
|
|
2669
|
+
const lanSetupTitle = element("h3", "dsh-mobile-control__section-title");
|
|
2670
|
+
lanSetupTitle.textContent = t("lanSetupTitle");
|
|
2671
|
+
const lanSetupIntro = element("p", "dsh-mobile-control__intro");
|
|
2672
|
+
lanSetupIntro.textContent = t("lanSetupIntro");
|
|
2673
|
+
const lanSetupForm = element("div", "dsh-mobile-control__lan-setup-form");
|
|
2674
|
+
const lanSetupLabel = element("label", "dsh-mobile-control__field");
|
|
2675
|
+
lanSetupLabel.textContent = t("lanSetupNetwork");
|
|
2676
|
+
const lanSetupSelect = element("select");
|
|
2677
|
+
lanSetupSelect.setAttribute("aria-label", t("lanSetupNetwork"));
|
|
2678
|
+
const lanSetupNote = element("p", "dsh-mobile-control__component-note");
|
|
2679
|
+
lanSetupNote.textContent = t("lanSetupFirewallNote");
|
|
2680
|
+
const lanSetupActions = element("div", "dsh-mobile-control__lan-setup-actions");
|
|
2681
|
+
const lanSetupRefresh = element("button", "dsh-mobile-control__secondary");
|
|
2682
|
+
lanSetupRefresh.type = "button";
|
|
2683
|
+
lanSetupRefresh.textContent = t("lanSetupRefresh");
|
|
2684
|
+
const lanSetupConfigure = element("button", "dsh-mobile-control__primary");
|
|
2685
|
+
lanSetupConfigure.type = "button";
|
|
2686
|
+
lanSetupConfigure.textContent = t("lanSetupConfigure");
|
|
2687
|
+
const lanSetupStatus = element("p", "dsh-mobile-control__status");
|
|
2688
|
+
lanSetupStatus.setAttribute("role", "status");
|
|
2689
|
+
lanSetupStatus.setAttribute("aria-live", "polite");
|
|
2690
|
+
lanSetupLabel.append(lanSetupSelect);
|
|
2691
|
+
lanSetupActions.append(lanSetupRefresh, lanSetupConfigure);
|
|
2692
|
+
lanSetupForm.append(lanSetupLabel, lanSetupNote, lanSetupActions);
|
|
2693
|
+
lanSetup.append(lanSetupTitle, lanSetupIntro, lanSetupForm, lanSetupStatus);
|
|
2196
2694
|
const access = element("div", "dsh-mobile-control__access");
|
|
2197
2695
|
access.hidden = true;
|
|
2198
2696
|
const accessLabel = element("span", "dsh-mobile-control__access-label");
|
|
@@ -2625,7 +3123,15 @@ window.__ModuleLoader__.load({
|
|
|
2625
3123
|
remotePair.type = "button";
|
|
2626
3124
|
remotePair.textContent = t("generateRemoteQr");
|
|
2627
3125
|
remotePair.disabled = true;
|
|
2628
|
-
|
|
3126
|
+
const remoteCopyLink = element("button", "dsh-mobile-control__secondary");
|
|
3127
|
+
remoteCopyLink.type = "button";
|
|
3128
|
+
remoteCopyLink.textContent = t("copyRemoteLink");
|
|
3129
|
+
remoteCopyLink.disabled = true;
|
|
3130
|
+
const remotePairLink = element("p", "dsh-mobile-control__status");
|
|
3131
|
+
remotePairLink.hidden = true;
|
|
3132
|
+
let activeRemotePairUrl = "";
|
|
3133
|
+
let activeRemotePairExpiresAt = 0;
|
|
3134
|
+
remoteActions.append(remoteToggle, remoteLogin, remoteReconnect, remotePair, remoteCopyLink);
|
|
2629
3135
|
const remoteManageRow = element("div", "dsh-mobile-control__manage-row");
|
|
2630
3136
|
const remoteDevices = element("button", "dsh-mobile-control__manage");
|
|
2631
3137
|
remoteDevices.type = "button";
|
|
@@ -2821,7 +3327,7 @@ window.__ModuleLoader__.load({
|
|
|
2821
3327
|
saveWsPaths([...wsPathsCurrent, value]);
|
|
2822
3328
|
});
|
|
2823
3329
|
const remoteWorkspace = element("section", "dsh-mobile-control__remote-workspace");
|
|
2824
|
-
remoteWorkspace.append(providerSetupHeader, remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteActions, remoteQr, remoteManageRow, remoteDevicePanel);
|
|
3330
|
+
remoteWorkspace.append(providerSetupHeader, remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteActions, remoteQr, remotePairLink, remoteManageRow, remoteDevicePanel);
|
|
2825
3331
|
const diagnosticsView = element("div", "dsh-mobile-control__view is-diagnostics");
|
|
2826
3332
|
diagnosticsView.hidden = true;
|
|
2827
3333
|
const diagnosticsIntro = element("p", "dsh-mobile-control__intro");
|
|
@@ -2865,7 +3371,7 @@ window.__ModuleLoader__.load({
|
|
|
2865
3371
|
diagnosticsDetails.append(diagnosticsDetailsSummary, diagnosticsReport);
|
|
2866
3372
|
header.append(title, headerActions);
|
|
2867
3373
|
actions.append(toggle, pair, linkPair);
|
|
2868
|
-
lanView.append(access, qrBox, status, extensionStatus, actions, manageRow, devicePanel);
|
|
3374
|
+
lanView.append(lanSetup, access, qrBox, status, extensionStatus, actions, manageRow, devicePanel);
|
|
2869
3375
|
remoteView.append(remoteIntro, providerSection, remoteWorkspace);
|
|
2870
3376
|
diagnosticsView.append(diagnosticsIntro, diagnosticsSummary, diagnosticsToolbar, diagnosticsFeedback, diagnosticsChecks, wsPathsSection, diagnosticsDetails);
|
|
2871
3377
|
panel.append(header, releaseNotice, updateCard, appDownload, switcher, lanView, remoteView, diagnosticsView);
|
|
@@ -2873,6 +3379,9 @@ window.__ModuleLoader__.load({
|
|
|
2873
3379
|
document.body.append(root);
|
|
2874
3380
|
let running = false;
|
|
2875
3381
|
let origin = "";
|
|
3382
|
+
let lanConfigured = true;
|
|
3383
|
+
let lanRestartRequired = false;
|
|
3384
|
+
let lanSetupLoaded = false;
|
|
2876
3385
|
let remoteRunning = false;
|
|
2877
3386
|
let remoteReady = false;
|
|
2878
3387
|
let remoteProvider = "tailscale";
|
|
@@ -2976,14 +3485,25 @@ window.__ModuleLoader__.load({
|
|
|
2976
3485
|
for (const trigger of document.querySelectorAll(".dsh-mobile-control__trigger")) trigger.setAttribute("aria-expanded", String(open));
|
|
2977
3486
|
};
|
|
2978
3487
|
const render = (data) => {
|
|
3488
|
+
lanConfigured = data.configured !== false;
|
|
3489
|
+
lanRestartRequired = data.restartRequired === true;
|
|
2979
3490
|
running = data.running === true;
|
|
2980
3491
|
origin = running && typeof data.origin === "string" ? data.origin : "";
|
|
3492
|
+
lanSetup.hidden = lanConfigured && !lanRestartRequired;
|
|
3493
|
+
lanSetupForm.hidden = lanRestartRequired;
|
|
3494
|
+
lanSetupIntro.hidden = lanRestartRequired;
|
|
3495
|
+
status.hidden = !lanConfigured || lanRestartRequired;
|
|
3496
|
+
if (!lanConfigured && !lanSetupLoaded) loadLanSetup();
|
|
3497
|
+
if (lanRestartRequired) {
|
|
3498
|
+
const pendingOrigin = typeof data.pendingOrigin === "string" ? data.pendingOrigin : "";
|
|
3499
|
+
lanSetupStatus.textContent = t("lanSetupRestart", { origin: pendingOrigin });
|
|
3500
|
+
} else if (!lanConfigured && lanSetupStatus.textContent === "") lanSetupStatus.textContent = t("lanSetupRequired");
|
|
2981
3501
|
access.hidden = origin === "";
|
|
2982
3502
|
accessLink.href = origin;
|
|
2983
3503
|
accessLink.textContent = origin;
|
|
2984
3504
|
accessLink.title = origin;
|
|
2985
3505
|
status.classList.toggle("is-running", running);
|
|
2986
|
-
status.textContent = running ? t("lanOn") : t("lanOff");
|
|
3506
|
+
status.textContent = !lanConfigured ? t("lanSetupRequired") : lanRestartRequired ? t("lanSetupRestart", { origin: typeof data.pendingOrigin === "string" ? data.pendingOrigin : "" }) : running ? t("lanOn") : t("lanOff");
|
|
2987
3507
|
const extensionData = data.extensions;
|
|
2988
3508
|
if (extensionData !== null && typeof extensionData === "object") {
|
|
2989
3509
|
const loaded = typeof extensionData.loaded === "number" ? extensionData.loaded : 0;
|
|
@@ -2996,11 +3516,78 @@ window.__ModuleLoader__.load({
|
|
|
2996
3516
|
} else extensionStatus.hidden = true;
|
|
2997
3517
|
if (!running) qrBox.hidden = true;
|
|
2998
3518
|
toggle.textContent = running ? t("disableLan") : t("enableLan");
|
|
3519
|
+
actions.hidden = !lanConfigured || lanRestartRequired;
|
|
3520
|
+
manageRow.hidden = !lanConfigured || lanRestartRequired;
|
|
2999
3521
|
pair.disabled = !running;
|
|
3000
3522
|
linkPair.disabled = !running;
|
|
3001
3523
|
manageDevices.disabled = !running;
|
|
3002
3524
|
resetAll.disabled = !running;
|
|
3003
3525
|
};
|
|
3526
|
+
const renderLanSetup = (data) => {
|
|
3527
|
+
if (data.restartRequired === true || data.configured === true) {
|
|
3528
|
+
render(data);
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
const networks = Array.isArray(data.networks) ? data.networks.filter((entry) => typeof entry === "object" && entry !== null) : [];
|
|
3532
|
+
lanSetupSelect.replaceChildren();
|
|
3533
|
+
const hasRecommended = networks.some((network) => network.recommended === true);
|
|
3534
|
+
if (!hasRecommended && networks.length > 1) {
|
|
3535
|
+
const placeholder = element("option");
|
|
3536
|
+
placeholder.value = "";
|
|
3537
|
+
placeholder.textContent = t("lanSetupChooseNetwork");
|
|
3538
|
+
placeholder.disabled = true;
|
|
3539
|
+
placeholder.selected = true;
|
|
3540
|
+
lanSetupSelect.append(placeholder);
|
|
3541
|
+
}
|
|
3542
|
+
for (const network of networks) {
|
|
3543
|
+
if (typeof network.address !== "string" || typeof network.name !== "string") continue;
|
|
3544
|
+
const option = element("option");
|
|
3545
|
+
option.value = network.address;
|
|
3546
|
+
option.textContent = `${network.name} · ${network.address}${network.recommended === true ? ` · ${t("lanSetupRecommended")}` : ""}`;
|
|
3547
|
+
option.selected = network.recommended === true || !hasRecommended && networks.length === 1;
|
|
3548
|
+
lanSetupSelect.append(option);
|
|
3549
|
+
}
|
|
3550
|
+
const available = networks.some((network) => typeof network.address === "string" && typeof network.name === "string");
|
|
3551
|
+
lanSetupConfigure.disabled = lanSetupSelect.value === "";
|
|
3552
|
+
lanSetupStatus.textContent = available ? t("lanSetupRequired") : t("lanSetupNoNetworks");
|
|
3553
|
+
lanSetupNote.hidden = data.windowsFirewall !== true;
|
|
3554
|
+
};
|
|
3555
|
+
lanSetupSelect.addEventListener("change", () => {
|
|
3556
|
+
lanSetupConfigure.disabled = lanSetupSelect.value === "";
|
|
3557
|
+
});
|
|
3558
|
+
const loadLanSetup = () => {
|
|
3559
|
+
lanSetupLoaded = true;
|
|
3560
|
+
lanSetupRefresh.disabled = true;
|
|
3561
|
+
controlRequestJson("/api/mobile-access/lan/setup").then(renderLanSetup, () => {
|
|
3562
|
+
lanSetupStatus.textContent = t("lanSetupFailed");
|
|
3563
|
+
}).finally(() => {
|
|
3564
|
+
lanSetupRefresh.disabled = false;
|
|
3565
|
+
});
|
|
3566
|
+
};
|
|
3567
|
+
lanSetupRefresh.addEventListener("click", () => {
|
|
3568
|
+
loadLanSetup();
|
|
3569
|
+
});
|
|
3570
|
+
lanSetupConfigure.addEventListener("click", () => {
|
|
3571
|
+
if (lanSetupSelect.value === "") return;
|
|
3572
|
+
lanSetupConfigure.disabled = true;
|
|
3573
|
+
lanSetupRefresh.disabled = true;
|
|
3574
|
+
lanSetupConfigure.textContent = t("lanSetupConfiguring");
|
|
3575
|
+
lanSetupStatus.textContent = t("lanSetupConfiguring");
|
|
3576
|
+
controlRequestJson("/api/mobile-access/lan/setup", {
|
|
3577
|
+
method: "POST",
|
|
3578
|
+
body: JSON.stringify({
|
|
3579
|
+
confirm: true,
|
|
3580
|
+
address: lanSetupSelect.value,
|
|
3581
|
+
configureFirewall: true
|
|
3582
|
+
})
|
|
3583
|
+
}, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(renderLanSetup, () => {
|
|
3584
|
+
lanSetupStatus.textContent = t("lanSetupFailed");
|
|
3585
|
+
}).finally(() => {
|
|
3586
|
+
lanSetupConfigure.textContent = t("lanSetupConfigure");
|
|
3587
|
+
lanSetupRefresh.disabled = false;
|
|
3588
|
+
lanSetupConfigure.disabled = lanRestartRequired || lanSetupSelect.options.length === 0;
|
|
3589
|
+
});
|
|
3590
|
+
});
|
|
3004
3591
|
const showQr = (svg, target = qrBox) => {
|
|
3005
3592
|
target.replaceChildren();
|
|
3006
3593
|
if (svg === "") {
|
|
@@ -3263,8 +3850,14 @@ window.__ModuleLoader__.load({
|
|
|
3263
3850
|
remoteReconnect.hidden = needsFunnelSetup || state !== "error" && state !== "unavailable" || !providerPrepared;
|
|
3264
3851
|
remoteActions.hidden = !providerPrepared;
|
|
3265
3852
|
remotePair.disabled = !remoteReady;
|
|
3853
|
+
remoteCopyLink.disabled = !remoteReady;
|
|
3266
3854
|
remoteDevices.disabled = !remoteReady;
|
|
3267
|
-
if (!remoteReady)
|
|
3855
|
+
if (!remoteReady) {
|
|
3856
|
+
remoteQr.hidden = true;
|
|
3857
|
+
remotePairLink.hidden = true;
|
|
3858
|
+
activeRemotePairUrl = "";
|
|
3859
|
+
activeRemotePairExpiresAt = 0;
|
|
3860
|
+
}
|
|
3268
3861
|
if (!needsFunnelSetup) remoteSetupPending = false;
|
|
3269
3862
|
};
|
|
3270
3863
|
let remoteLoadInFlight = false;
|
|
@@ -3765,24 +4358,48 @@ window.__ModuleLoader__.load({
|
|
|
3765
4358
|
};
|
|
3766
4359
|
window.addEventListener("focus", retryAfterSetup);
|
|
3767
4360
|
document.addEventListener("visibilitychange", retryAfterSetup);
|
|
4361
|
+
const copyRemotePairUrl = async () => {
|
|
4362
|
+
try {
|
|
4363
|
+
await navigator.clipboard.writeText(activeRemotePairUrl);
|
|
4364
|
+
remoteStatus.textContent = t("linkCopied");
|
|
4365
|
+
} catch {
|
|
4366
|
+
remoteStatus.textContent = t("remoteQrReady");
|
|
4367
|
+
}
|
|
4368
|
+
};
|
|
4369
|
+
const openRemotePairing = () => controlRequestJson("/api/mobile-access/remote/pairing/open", {
|
|
4370
|
+
method: "POST",
|
|
4371
|
+
body: "{}"
|
|
4372
|
+
}).then(async (data) => {
|
|
4373
|
+
const pairUrl = typeof data.pairUrl === "string" ? data.pairUrl : "";
|
|
4374
|
+
const expiresAt = typeof data.expiresAt === "number" && Number.isFinite(data.expiresAt) ? data.expiresAt : 0;
|
|
4375
|
+
showQr(typeof data.qrSvg === "string" ? data.qrSvg : "", remoteQr);
|
|
4376
|
+
if (pairUrl === "") {
|
|
4377
|
+
remoteStatus.textContent = t("keyGenerationFailed");
|
|
4378
|
+
return;
|
|
4379
|
+
}
|
|
4380
|
+
activeRemotePairUrl = pairUrl;
|
|
4381
|
+
activeRemotePairExpiresAt = expiresAt;
|
|
4382
|
+
remotePairLink.textContent = pairUrl;
|
|
4383
|
+
remotePairLink.classList.add("is-key");
|
|
4384
|
+
remotePairLink.hidden = false;
|
|
4385
|
+
await copyRemotePairUrl();
|
|
4386
|
+
});
|
|
3768
4387
|
remotePair.addEventListener("click", () => {
|
|
3769
4388
|
remotePair.disabled = true;
|
|
3770
|
-
|
|
3771
|
-
method: "POST",
|
|
3772
|
-
body: "{}"
|
|
3773
|
-
}).then(async (data) => {
|
|
3774
|
-
const pairUrl = typeof data.pairUrl === "string" ? data.pairUrl : "";
|
|
3775
|
-
showQr(typeof data.qrSvg === "string" ? data.qrSvg : "", remoteQr);
|
|
3776
|
-
if (pairUrl !== "") try {
|
|
3777
|
-
await navigator.clipboard.writeText(pairUrl);
|
|
3778
|
-
} catch {}
|
|
3779
|
-
remoteStatus.textContent = t("remoteQrReady");
|
|
3780
|
-
}, (error) => {
|
|
4389
|
+
openRemotePairing().catch((error) => {
|
|
3781
4390
|
remoteStatus.textContent = t("requestFailed", { error: String(error) });
|
|
3782
4391
|
}).finally(() => {
|
|
3783
4392
|
remotePair.disabled = !remoteReady;
|
|
3784
4393
|
});
|
|
3785
4394
|
});
|
|
4395
|
+
remoteCopyLink.addEventListener("click", () => {
|
|
4396
|
+
remoteCopyLink.disabled = true;
|
|
4397
|
+
(activeRemotePairUrl !== "" && activeRemotePairExpiresAt > Date.now() ? copyRemotePairUrl() : openRemotePairing()).catch((error) => {
|
|
4398
|
+
remoteStatus.textContent = t("requestFailed", { error: String(error) });
|
|
4399
|
+
}).finally(() => {
|
|
4400
|
+
remoteCopyLink.disabled = !remoteReady;
|
|
4401
|
+
});
|
|
4402
|
+
});
|
|
3786
4403
|
const renderRemoteDevices = (data) => {
|
|
3787
4404
|
const devices = Array.isArray(data.devices) ? data.devices : [];
|
|
3788
4405
|
remoteDevicePanel.replaceChildren();
|
|
@@ -4473,7 +5090,7 @@ window.__ModuleLoader__.load({
|
|
|
4473
5090
|
function startExtensionChangeStream(changed, runtime = {
|
|
4474
5091
|
window,
|
|
4475
5092
|
create: (url) => new EventSource(url, { withCredentials: true })
|
|
4476
|
-
}) {
|
|
5093
|
+
}, onTaskEvent, onDeviceRevoked) {
|
|
4477
5094
|
let source;
|
|
4478
5095
|
let timer;
|
|
4479
5096
|
let disposed = false;
|
|
@@ -4492,6 +5109,14 @@ window.__ModuleLoader__.load({
|
|
|
4492
5109
|
retryMs = 1e3;
|
|
4493
5110
|
};
|
|
4494
5111
|
next.addEventListener("extensions-changed", changed);
|
|
5112
|
+
if (onTaskEvent !== void 0) next.addEventListener("task-notify", (message) => {
|
|
5113
|
+
const data = message.data;
|
|
5114
|
+
onTaskEvent(data);
|
|
5115
|
+
});
|
|
5116
|
+
if (onDeviceRevoked !== void 0) next.addEventListener("device-revoked", (message) => {
|
|
5117
|
+
const data = message.data;
|
|
5118
|
+
if (isDeviceRevokedPayload(data)) onDeviceRevoked();
|
|
5119
|
+
});
|
|
4495
5120
|
next.onerror = () => {
|
|
4496
5121
|
if (source !== next) return;
|
|
4497
5122
|
next.close();
|
|
@@ -5252,6 +5877,20 @@ window.__ModuleLoader__.load({
|
|
|
5252
5877
|
});
|
|
5253
5878
|
const stopEvents = startExtensionChangeStream(() => {
|
|
5254
5879
|
stopRefresh.refresh();
|
|
5880
|
+
}, void 0, (payload) => {
|
|
5881
|
+
if (document.hidden !== true) return;
|
|
5882
|
+
const parsed = parseTaskNotifyPayload(payload);
|
|
5883
|
+
if (parsed === void 0) return;
|
|
5884
|
+
const language = resolveNativeMobileLanguage(document.documentElement.lang, navigator.languages.length > 0 ? [...navigator.languages] : [navigator.language]);
|
|
5885
|
+
const label = (italian, english, chinese) => language === "it" ? italian : language === "zh" ? chinese : english;
|
|
5886
|
+
fireTaskNotifyEvent({
|
|
5887
|
+
kind: "done",
|
|
5888
|
+
title: label("Attività completata", "Task finished", "任务已完成"),
|
|
5889
|
+
body: label("Il tuo task DSH è terminato", "Your DSH task finished", "你的 DSH 任务已完成"),
|
|
5890
|
+
tag: taskCompletionTag(parsed.sessionId, parsed.turn)
|
|
5891
|
+
});
|
|
5892
|
+
}, () => {
|
|
5893
|
+
fireDeviceRevoked();
|
|
5255
5894
|
});
|
|
5256
5895
|
return () => {
|
|
5257
5896
|
disposed = true;
|
|
@@ -5320,6 +5959,7 @@ window.__ModuleLoader__.load({
|
|
|
5320
5959
|
.dsh-mobile-control__cpolar-setup{margin:0 0 12px;padding:12px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__cpolar-setup[hidden],.dsh-mobile-control__cpolar-account[hidden],.dsh-mobile-control__details[hidden],.dsh-mobile-control__view.is-remote .dsh-mobile-control__actions[hidden],.dsh-mobile-control__danger[hidden]{display:none}.dsh-mobile-control__component-status,.dsh-mobile-control__component-note{margin:0 0 10px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.55}.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__primary{width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:600 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__cpolar-account{margin-top:10px}.dsh-mobile-control__link-row{display:flex;flex-wrap:wrap;gap:6px 12px;margin:0 0 10px}.dsh-mobile-control__text-link{color:#2563eb;font-size:11px;text-decoration:none}.dsh-mobile-control__text-link:hover{text-decoration:underline}.dsh-mobile-control__token-label{display:flex;flex-direction:column;gap:5px;margin:0 0 8px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__token{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-normal,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2,#fff));color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__cpolar-connect{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;min-height:44px;padding:10px 14px;border-radius:12px;font:650 13px/1.2 system-ui;cursor:pointer;transition:background-color 160ms ease,border-color 160ms ease,opacity 160ms ease}.dsh-mobile-control__cpolar-connect:hover:not(:disabled){border-color:#1d4ed8;background:#1d4ed8}.dsh-mobile-control__cpolar-connect:active:not(:disabled){border-color:#1e40af;background:#1e40af}.dsh-mobile-control__cpolar-connect:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__details{margin:10px 0 0;border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb);padding-top:9px}.dsh-mobile-control__details>summary{min-height:30px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:30px;cursor:pointer}.dsh-mobile-control__details-body{display:flex;flex-wrap:wrap;align-items:center;gap:7px 12px;padding:4px 0}.dsh-mobile-control__details-body p{flex:1 0 100%;margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__storage{display:block;flex:1 0 100%;max-width:100%;overflow:hidden;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#475569);font:10px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__danger{flex:1 0 100%;min-height:38px;margin-top:3px;padding:7px 10px;border:1px solid #dc2626;border-radius:9px;background:transparent;color:#dc2626;font:12px/1.3 system-ui;cursor:pointer}
|
|
5321
5960
|
.dsh-mobile-control__frp-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-setup[hidden]{display:none}.dsh-mobile-control__frp-step{padding:11px 0}.dsh-mobile-control__frp-step + .dsh-mobile-control__frp-step{border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb)}.dsh-mobile-control__frp-step>strong{display:block;margin-bottom:3px;font-size:12px;line-height:1.4}.dsh-mobile-control__frp-step>p{margin:0 0 9px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes{margin:0 0 9px;padding:0;list-style:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes li{position:relative;margin:0;padding-left:12px}.dsh-mobile-control__frp-changes li+li{margin-top:5px}.dsh-mobile-control__frp-changes li::before{content:"•";position:absolute;left:2px;color:var(--dsw-alias-label-tertiary,#98a1ad)}.dsh-mobile-control__frp-step>.dsh-mobile-control__frp-requirement{padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:11px}.dsh-mobile-control__frp-fields{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:8px}.dsh-mobile-control__field{display:flex;min-width:0;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__field:nth-child(3),.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__field input{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-normal,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__frp-action{box-sizing:border-box;width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__frp-action:disabled{cursor:not-allowed;opacity:.5}.dsh-mobile-control__remote-workspace{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f7f8fa)}.dsh-mobile-control__stage-header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:9px}.dsh-mobile-control__stage-header .dsh-mobile-control__section-title{margin:0}.dsh-mobile-control__stage-meta{display:flex;min-width:0;align-items:center;justify-content:flex-end;gap:5px}.dsh-mobile-control__stage-value{max-width:115px;overflow:hidden;color:var(--dsw-alias-label-primary,#16181d);font:650 10px/1.3 system-ui;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__state-badge{flex:none;padding:3px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#606873);font:650 9px/1.25 system-ui}.dsh-mobile-control__state-badge.is-ready{background:#e6f7f0;color:#087454}.dsh-mobile-control__state-badge.is-busy{background:#e8f0ff;color:#1d4ed8}.dsh-mobile-control__state-badge.is-attention{background:#fff4dc;color:#935100}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__status{box-sizing:border-box;margin:0 0 10px;padding:9px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:11px;line-height:1.45}.dsh-mobile-control__provider-setup-body{margin:0 0 10px}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup{margin:0}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__section-title,.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__frp-setup>.dsh-mobile-control__section-title{display:none}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__details{margin:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__actions{margin-top:2px}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__qr{margin:10px 0 0}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__manage-row{margin-top:10px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb)}
|
|
5322
5961
|
.dsh-mobile-control__frp-overview{display:grid;grid-template-columns:32px minmax(0,1fr);align-items:center;gap:10px;margin:0 0 10px;padding:10px;border:1px solid #a9dfc9;border-radius:11px;background:#edf9f4}.dsh-mobile-control__frp-overview[hidden]{display:none}.dsh-mobile-control__frp-overview-mark{display:grid;width:32px;height:32px;place-items:center;border-radius:50%;background:#087454;color:#fff;font:700 15px/1 system-ui}.dsh-mobile-control__frp-overview-body{display:flex;min-width:0;flex-direction:column;gap:2px}.dsh-mobile-control__frp-overview-body strong{color:#075d46;font-size:12px;line-height:1.35}.dsh-mobile-control__frp-overview-body span{overflow:hidden;color:#357061;font:10px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__frp-group{margin:8px 0 0;overflow:hidden;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__frp-group>summary{box-sizing:border-box;min-height:44px;padding:12px 34px 10px 12px;color:var(--dsw-alias-label-primary,#16181d);font:650 12px/1.4 system-ui;cursor:pointer}.dsh-mobile-control__frp-group[open]>summary{border-bottom:1px solid var(--dsw-alias-border-subtle,#e1e5eb);background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step{padding:12px}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step>strong:first-child{display:none}.dsh-mobile-control__frp-group .dsh-mobile-control__frp-step{border-top:0}.dsh-mobile-control__frp-setup>.dsh-mobile-control__frp-step{margin-bottom:8px;padding:10px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}
|
|
5962
|
+
.dsh-mobile-control__lan-setup{box-sizing:border-box;margin:0 0 12px;padding:14px;border:1px solid #6f96db;border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f5f8ff)}.dsh-mobile-control__lan-setup[hidden],.dsh-mobile-control__lan-setup-form[hidden]{display:none}.dsh-mobile-control__lan-setup>.dsh-mobile-control__section-title{margin-bottom:5px}.dsh-mobile-control__lan-setup>.dsh-mobile-control__intro{margin-bottom:12px}.dsh-mobile-control__lan-setup-form{display:grid;gap:9px}.dsh-mobile-control__lan-setup .dsh-mobile-control__field select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-normal,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__lan-setup .dsh-mobile-control__component-note{margin:0}.dsh-mobile-control__lan-setup-actions{display:grid;grid-template-columns:1fr 1.35fr;gap:8px}.dsh-mobile-control__lan-setup-actions button{box-sizing:border-box;min-width:0;min-height:44px;padding:9px 10px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__lan-setup-actions button:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status{margin:10px 0 0;line-height:1.5}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status:empty{display:none}
|
|
5323
5963
|
.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:":"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:#2563eb;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}.dsh-mobile-control__qr{display:flex;justify-content:center;margin:0 0 12px}.dsh-mobile-control__qr[hidden]{display:none}.dsh-mobile-control__qr img{border-radius:12px;background:#fff;padding:8px}
|
|
5324
5964
|
.dsh-mobile-control__status{margin:0 0 14px;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary,#606873)}.dsh-mobile-control__status::before{display:inline-block;width:8px;height:8px;margin-right:7px;border-radius:50%;background:#98a1ad;content:""}.dsh-mobile-control__status.is-running::before{background:#16a36a}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}
|
|
5325
5965
|
.dsh-mobile-control__guide{margin:0 0 14px;padding:12px;border:1px solid #6f96db;border-radius:12px;background:var(--dsw-alias-bg-layer-1,#eff6ff)}.dsh-mobile-control__guide[hidden]{display:none}.dsh-mobile-control__guide-title{margin:0;color:var(--dsw-alias-label-primary,#172554);font:650 13px/1.45 system-ui}.dsh-mobile-control__guide-summary,.dsh-mobile-control__guide-note{margin:4px 0 0;color:var(--dsw-alias-label-secondary,#475569);font-size:12px;line-height:1.5}.dsh-mobile-control__guide-steps{margin:8px 0 0;padding-left:20px;color:var(--dsw-alias-label-primary,#1e293b);font-size:12px;line-height:1.6}.dsh-mobile-control__guide-note{color:var(--dsw-alias-label-secondary,#64748b)}.dsh-mobile-control__guide-actions{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px}.dsh-mobile-control__guide-actions button{min-width:0;min-height:44px;padding:8px;border-radius:10px;font:12px/1.25 system-ui;cursor:pointer}.dsh-mobile-control__guide-actions button:disabled{cursor:not-allowed;opacity:.45}
|
|
@@ -5330,8 +5970,8 @@ window.__ModuleLoader__.load({
|
|
|
5330
5970
|
.dsh-mobile-control__diagnostic-checks{display:grid;gap:12px;margin-top:12px;animation:dsh-diagnostic-reveal 160ms ease-out both}.dsh-mobile-control__diagnostic-checks[hidden]{display:none}.dsh-mobile-control__diagnostic-group{overflow:hidden;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__diagnostic-group-header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 11px;border-bottom:1px solid var(--dsw-alias-border-subtle,#e1e5eb);background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__diagnostic-group-header h3{margin:0;font:650 11px/1.4 system-ui}.dsh-mobile-control__diagnostic-group-header span{color:var(--dsw-alias-label-secondary,#606873);font-size:10px}.dsh-mobile-control__diagnostic-list{display:flex;flex-direction:column}.dsh-mobile-control__diagnostic-check{display:grid;grid-template-columns:26px minmax(0,1fr);gap:9px;padding:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__diagnostic-check + .dsh-mobile-control__diagnostic-check{border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb)}.dsh-mobile-control__diagnostic-marker{position:relative;width:26px;height:26px;border-radius:50%;background:#edf1f5;color:var(--dsh-diagnostic-info)}.dsh-mobile-control__diagnostic-marker::before,.dsh-mobile-control__diagnostic-marker::after{position:absolute;content:""}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-marker{background:#e6f7f0;color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-marker::before{top:7px;left:7px;width:9px;height:5px;border-bottom:1.8px solid currentColor;border-left:1.8px solid currentColor;transform:rotate(-45deg)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker{background:#fff4dc;color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker{background:#fdecec;color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker::before,.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker::before{top:6px;left:12px;width:2px;height:9px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker::after,.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker::after{bottom:6px;left:12px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-check.is-info .dsh-mobile-control__diagnostic-marker::before{top:6px;left:12px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-check.is-info .dsh-mobile-control__diagnostic-marker::after{top:10px;left:12px;width:2px;height:9px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-check-body{min-width:0}.dsh-mobile-control__diagnostic-check-header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.dsh-mobile-control__diagnostic-check-header strong{min-width:0;font-size:12px;line-height:1.4}.dsh-mobile-control__diagnostic-badge{flex:none;padding:2px 6px;border-radius:999px;background:#edf1f5;color:var(--dsh-diagnostic-info);font:650 10px/1.3 system-ui}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-badge{background:#e6f7f0;color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-badge{background:#fff4dc;color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-badge{background:#fdecec;color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-check p{margin:4px 0 0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5;overflow-wrap:anywhere}.dsh-mobile-control__diagnostic-check .dsh-mobile-control__diagnostic-action{margin-top:7px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f8fafc);color:var(--dsw-alias-label-primary,#16181d)}.dsh-mobile-control__diagnostic-action span{display:inline-block;margin-right:6px;color:#2563eb;font-weight:700}.dsh-mobile-control__diagnostic-details{margin-top:12px}.dsh-mobile-control__diagnostic-details[hidden]{display:none}.dsh-mobile-control__diagnostic-details>summary{box-sizing:border-box;min-height:44px;line-height:44px}.dsh-mobile-control__diagnostic-report{box-sizing:border-box;max-height:220px;margin:4px 0 0;overflow:auto;padding:10px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#606873);font:10px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
5331
5971
|
.dsh-mobile-control__diagnostic-checks{transition:opacity 150ms ease}.dsh-mobile-control__diagnostic-checks.is-refreshing{opacity:.52}
|
|
5332
5972
|
@keyframes dsh-diagnostic-spin{to{transform:rotate(360deg)}}@keyframes dsh-diagnostic-reveal{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}
|
|
5333
|
-
.dsh-mobile-control__actions{display:flex;flex-wrap:nowrap;gap:6px}.dsh-mobile-control__actions button{flex:1 1 0;min-width:0;min-height:40px;padding:8px 4px;border-radius:10px;font:12px/1.2 system-ui;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-normal,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}
|
|
5334
|
-
.dsh-mobile-control button:focus-visible,.dsh-mobile-control a:focus-visible,.dsh-mobile-control input:focus-visible,.dsh-mobile-control summary:focus-visible{outline:3px solid rgb(37 99 235 / 28%);outline-offset:2px}
|
|
5973
|
+
.dsh-mobile-control__actions{display:flex;flex-wrap:nowrap;gap:6px}.dsh-mobile-control__actions[hidden],.dsh-mobile-control__manage-row[hidden]{display:none}.dsh-mobile-control__actions button{flex:1 1 0;min-width:0;min-height:40px;padding:8px 4px;border-radius:10px;font:12px/1.2 system-ui;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-normal,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}
|
|
5974
|
+
.dsh-mobile-control button:focus-visible,.dsh-mobile-control a:focus-visible,.dsh-mobile-control input:focus-visible,.dsh-mobile-control select:focus-visible,.dsh-mobile-control summary:focus-visible{outline:3px solid rgb(37 99 235 / 28%);outline-offset:2px}
|
|
5335
5975
|
.dsh-mobile-control__trigger{box-sizing:border-box;flex:1 1 auto;display:flex;align-items:center;gap:8px;width:100%;height:42px;margin:4px 0;padding:0 10px 0 8px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font-family:inherit;font-size:14px;line-height:22px;cursor:pointer;overflow:hidden}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger:active,.dsh-mobile-control__trigger[aria-expanded="true"]{background:var(--dsw-alias-interactive-bg-active,#e8ebf0)}.dsh-mobile-control__trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px}.dsh-mobile-control__trigger.is-rail{flex:0 0 auto;width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;gap:0;border-radius:50%}.dsh-mobile-control__trigger-icon{display:block;flex:none}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
|
5336
5976
|
.dsh-mobile-control__manage-row{display:flex;justify-content:space-between;gap:8px;margin-top:10px}.dsh-mobile-control__manage{flex:1 1 0;min-width:0;min-height:34px;padding:6px 8px;border:1px solid var(--dsw-alias-border-normal,#cfd5dd);border-radius:10px;background:transparent;color:inherit;font:12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__devices{margin-top:10px;border:1px solid var(--dsw-alias-border-subtle,#e1e5eb);border-radius:10px;padding:8px;max-height:220px;overflow-y:auto}.dsh-mobile-control__device-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px;margin:0}.dsh-mobile-control__device{display:flex;align-items:center;gap:8px;padding:6px 2px}.dsh-mobile-control__device + .dsh-mobile-control__device{border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb)}.dsh-mobile-control__device-label{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.dsh-mobile-control__device-meta{flex:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;white-space:nowrap}.dsh-mobile-control__device-revoke{flex:none;min-height:28px;padding:4px 8px;border:1px solid #dc2626;border-radius:8px;background:transparent;color:#dc2626;font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__ws-paths{margin:10px 0 0;padding:12px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__ws-paths-list{margin:0 0 8px;padding:0;list-style:none}.dsh-mobile-control__ws-paths-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px}.dsh-mobile-control__ws-paths-item{display:flex;align-items:center;gap:8px;padding:6px 0}.dsh-mobile-control__ws-paths-item + .dsh-mobile-control__ws-paths-item{border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb)}.dsh-mobile-control__ws-paths-item code{flex:1 1 0;min-width:0;overflow:hidden;font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__ws-paths-remove{flex:none;min-height:28px;padding:4px 8px;border:1px solid var(--dsw-alias-border-normal,#cfd5dd);border-radius:8px;background:transparent;color:inherit;font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__trigger,.dsh-mobile-control__diagnostic-entry{position:relative}.dsh-mobile-control__ws-dot{position:absolute;top:2px;right:2px;z-index:1;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:#dc2626;color:#fff;font:650 10px/16px system-ui;text-align:center;cursor:pointer}.dsh-mobile-control__ws-dot::after{content:'';position:absolute;inset:-5px}.dsh-mobile-control__ws-sr-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.dsh-mobile-control__ws-paths-row{display:flex;gap:8px}.dsh-mobile-control__ws-paths-row .dsh-mobile-control__primary{width:auto;flex:none;padding:9px 14px;border-radius:10px}.dsh-mobile-control__ws-paths-group{padding:6px 0}.dsh-mobile-control__ws-paths-group + .dsh-mobile-control__ws-paths-group{border-top:1px solid var(--dsw-alias-border-subtle,#e1e5eb)}.dsh-mobile-control__ws-paths-group-head{display:flex;align-items:center;gap:8px}.dsh-mobile-control__ws-count{flex:none;padding:1px 7px;border-radius:999px;background:#e8f0ff;color:#1d4ed8;font:650 10px/1.6 system-ui}.dsh-mobile-control__ws-paths-item.is-child{padding-left:12px}.dsh-mobile-control__ws-paths-group-head .dsh-mobile-control__primary,.dsh-mobile-control__ws-paths-item .dsh-mobile-control__primary{width:auto;flex:none;min-height:34px;padding:6px 12px;border-radius:10px}.dsh-mobile-control__ws-paths-row input{flex:1 1 0;min-width:0;box-sizing:border-box;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-normal,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__ws-paths-row button{flex:none}.dsh-mobile-control__ws-paths-detected-title{margin:0 0 2px;font:600 12px/1.4 system-ui}.dsh-mobile-control__ws-paths-advanced{margin-top:8px}.dsh-mobile-control__ws-paths-advanced summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#606873);font:12px/1.4 system-ui;user-select:none}.dsh-mobile-control__ws-paths-advanced summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px;border-radius:4px}.dsh-mobile-control__ws-paths-advanced[open] summary{margin-bottom:6px}.dsh-mobile-control__ws-paths-advanced .dsh-mobile-control__status{margin:6px 0 0}
|
|
5337
5977
|
@media (max-width:359px){.dsh-mobile-control__provider-choices{grid-template-columns:1fr}.dsh-mobile-control__provider{min-height:68px}}@media (prefers-reduced-motion:reduce){.dsh-mobile-control__provider,.dsh-mobile-control__cpolar-connect{transition:none}.dsh-mobile-control__diagnostic-summary.is-running .dsh-mobile-control__diagnostic-summary-icon::before,.dsh-mobile-control__diagnostic-checks{animation:none}}
|
|
@@ -5349,9 +5989,15 @@ window.__ModuleLoader__.load({
|
|
|
5349
5989
|
style.textContent = loopback ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
|
|
5350
5990
|
document.head.append(style);
|
|
5351
5991
|
if (!loopback) {
|
|
5992
|
+
const removeSettingsAction = ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
5993
|
+
name: "settings.general.item",
|
|
5994
|
+
id: "dsh-mobile-switch-computer",
|
|
5995
|
+
order: 100
|
|
5996
|
+
}, MobileSwitchComputerRow));
|
|
5352
5997
|
const removeCustom = installCustomAssets();
|
|
5353
5998
|
const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface);
|
|
5354
5999
|
return () => {
|
|
6000
|
+
removeSettingsAction();
|
|
5355
6001
|
removeCustom();
|
|
5356
6002
|
removeSurface();
|
|
5357
6003
|
style.remove();
|
|
@@ -5408,6 +6054,7 @@ window.__ModuleLoader__.load({
|
|
|
5408
6054
|
//#endregion
|
|
5409
6055
|
exports.CONTROL_STYLES = CONTROL_STYLES;
|
|
5410
6056
|
exports.DIAGNOSTIC_REASON_MESSAGES = DIAGNOSTIC_REASON_MESSAGES;
|
|
6057
|
+
exports.LOCALIZED_DIAGNOSTIC_COPY = LOCALIZED_DIAGNOSTIC_COPY;
|
|
5411
6058
|
exports.MOBILE_CONTROL_MESSAGES = MOBILE_CONTROL_MESSAGES;
|
|
5412
6059
|
exports.PerIdActivationLifecycle = PerIdActivationLifecycle;
|
|
5413
6060
|
exports.apply = apply;
|