@linxin666/dsh-remote-web-ui 0.3.24 → 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/README.i18n.yaml +2 -2
- package/README.md +13 -10
- package/README.zh.md +13 -10
- package/icon.svg +3 -0
- package/lib/client.js +263 -129
- package/lib/client.js.map +1 -1
- package/lib/index.js +565 -256
- package/lib/types/client/FenceNotice.d.ts.map +1 -1
- package/lib/types/client/FenceNotice.js +0 -3
- package/lib/types/client/PluginSettingsCard.d.ts +4 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +3 -2
- package/lib/types/client/RemoteEntry.d.ts.map +1 -1
- package/lib/types/client/RemoteEntry.js +14 -9
- package/lib/types/client/RemotePanel.d.ts.map +1 -1
- package/lib/types/client/RemotePanel.js +13 -4
- package/lib/types/client/RemoteSettingsCard.d.ts +6 -6
- package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
- package/lib/types/client/RemoteSettingsCard.js +14 -8
- package/lib/types/client/UpdateEntry.js +2 -2
- package/lib/types/client/UpdatePanel.js +2 -2
- package/lib/types/client/deep-link.js +2 -2
- package/lib/types/client/index.d.ts +16 -8
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +47 -18
- package/lib/types/client/locales.d.ts +0 -2
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +6 -8
- package/lib/types/client/mobile-adapt.d.ts.map +1 -1
- package/lib/types/client/mobile-adapt.js +103 -22
- package/lib/types/client/pair-api.d.ts +32 -3
- package/lib/types/client/pair-api.d.ts.map +1 -1
- package/lib/types/client/pair-api.js +44 -6
- package/lib/types/client/plugin-card-seat.d.ts +23 -16
- package/lib/types/client/plugin-card-seat.d.ts.map +1 -1
- package/lib/types/client/plugin-card-seat.js +16 -14
- package/lib/types/client/remote-channel.d.ts.map +1 -1
- package/lib/types/client/remote-channel.js +18 -5
- package/lib/types/client/settings-form.d.ts +17 -17
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +30 -27
- package/lib/types/http.d.ts +18 -1
- package/lib/types/http.d.ts.map +1 -1
- package/lib/types/http.js +22 -1
- package/lib/types/index.d.ts +19 -11
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +91 -76
- package/lib/types/inner-auth.d.ts +3 -2
- package/lib/types/inner-auth.d.ts.map +1 -1
- package/lib/types/inner-auth.js +3 -2
- package/lib/types/pair-grant.d.ts +66 -0
- package/lib/types/pair-grant.d.ts.map +1 -0
- package/lib/types/pair-grant.js +95 -0
- package/lib/types/pairing.d.ts +44 -12
- package/lib/types/pairing.d.ts.map +1 -1
- package/lib/types/pairing.js +69 -20
- package/lib/types/pairing.persist.test.js +26 -0
- package/lib/types/posture.d.ts +11 -3
- package/lib/types/posture.d.ts.map +1 -1
- package/lib/types/posture.js +8 -3
- package/lib/types/remote-api.d.ts +10 -13
- package/lib/types/remote-api.d.ts.map +1 -1
- package/lib/types/remote-api.js +34 -34
- package/lib/types/remote-channel-boot.d.ts +6 -1
- package/lib/types/remote-channel-boot.d.ts.map +1 -1
- package/lib/types/remote-channel-boot.js +16 -8
- package/lib/types/remote-channel-rules.d.ts +14 -0
- package/lib/types/remote-channel-rules.d.ts.map +1 -1
- package/lib/types/remote-channel-rules.js +8 -0
- package/lib/types/remote-methods.d.ts +1 -1
- package/lib/types/remote-methods.d.ts.map +1 -1
- package/lib/types/remote-methods.js +1 -0
- package/lib/types/routes.d.ts +47 -5
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +160 -58
- package/lib/types/tunnel.d.ts +21 -6
- package/lib/types/tunnel.d.ts.map +1 -1
- package/lib/types/tunnel.js +58 -11
- package/lib/types/update.d.ts +4 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +74 -26
- package/package.json +23 -20
- package/src/client/FenceNotice.tsx +0 -2
- package/src/client/PluginSettingsCard.tsx +7 -2
- package/src/client/RemoteEntry.tsx +14 -9
- package/src/client/RemotePanel.tsx +17 -7
- package/src/client/RemoteSettingsCard.tsx +16 -9
- package/src/client/UpdateEntry.tsx +2 -2
- package/src/client/UpdatePanel.tsx +3 -3
- package/src/client/deep-link.ts +2 -2
- package/src/client/index.ts +61 -27
- package/src/client/locales.ts +6 -8
- package/src/client/mobile-adapt.ts +97 -20
- package/src/client/pair-api.ts +47 -7
- package/src/client/plugin-card-seat.ts +24 -17
- package/src/client/remote-channel.ts +19 -4
- package/src/client/remote.module.css +38 -0
- package/src/client/settings-form.ts +34 -31
- package/src/http.ts +23 -1
- package/src/index.ts +102 -79
- package/src/inner-auth.ts +3 -2
- package/src/pair-grant.ts +131 -0
- package/src/pairing.persist.test.ts +36 -0
- package/src/pairing.ts +75 -24
- package/src/posture.ts +12 -4
- package/src/remote-api.ts +34 -41
- package/src/remote-channel-boot.ts +16 -8
- package/src/remote-channel-rules.ts +17 -0
- package/src/remote-methods.ts +1 -0
- package/src/routes.ts +165 -57
- package/src/tunnel.ts +56 -11
- package/src/update.ts +74 -24
package/lib/client.js
CHANGED
|
@@ -1283,14 +1283,10 @@ window.__ModuleLoader__.load({
|
|
|
1283
1283
|
body: JSON.stringify({ token })
|
|
1284
1284
|
});
|
|
1285
1285
|
if (response.ok) return { ok: true };
|
|
1286
|
-
if (response.status === 404) return {
|
|
1286
|
+
if (response.status === 404 || response.status === 409) return {
|
|
1287
1287
|
ok: false,
|
|
1288
1288
|
code: "invalid"
|
|
1289
1289
|
};
|
|
1290
|
-
if (response.status === 409) return {
|
|
1291
|
-
ok: false,
|
|
1292
|
-
code: "used"
|
|
1293
|
-
};
|
|
1294
1290
|
return {
|
|
1295
1291
|
ok: false,
|
|
1296
1292
|
code: "forbidden"
|
|
@@ -1314,21 +1310,58 @@ window.__ModuleLoader__.load({
|
|
|
1314
1310
|
if (response.status === 404) return;
|
|
1315
1311
|
if (!response.ok) throw new Error(`remote-web-ui: revoke failed with ${String(response.status)}`);
|
|
1316
1312
|
}
|
|
1317
|
-
/**
|
|
1313
|
+
/**
|
|
1314
|
+
* Presence heartbeat from a paired phone.
|
|
1315
|
+
* @returns the response status, so the caller can stop polling once the server
|
|
1316
|
+
* proves this page is not paired (see {@link shouldStopHeartbeat}).
|
|
1317
|
+
*/
|
|
1318
1318
|
async function sendHeartbeat() {
|
|
1319
|
-
await fetch("/api/pair/heartbeat", { method: "POST" });
|
|
1319
|
+
return (await fetch("/api/pair/heartbeat", { method: "POST" })).status;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Whether a heartbeat answer means "this page can never be accepted again" and
|
|
1323
|
+
* the 10 s wake source should stop: 401 (unpaired, or the device was revoked)
|
|
1324
|
+
* and 403 (the fence refused it) are permanent for this page, while a network
|
|
1325
|
+
* error or a 5xx is transient and keeps the cadence.
|
|
1326
|
+
* @param status - the heartbeat response status.
|
|
1327
|
+
*/
|
|
1328
|
+
function shouldStopHeartbeat(status) {
|
|
1329
|
+
return status === 401 || status === 403;
|
|
1320
1330
|
}
|
|
1321
1331
|
/** Whether the current page URL carries a pairing token. */
|
|
1322
1332
|
function readPairParams(search) {
|
|
1323
1333
|
const pair = new URLSearchParams(search).get("pair");
|
|
1324
1334
|
return pair !== null && pair !== "" ? { pair } : {};
|
|
1325
1335
|
}
|
|
1336
|
+
/** A failed LAN-bind read, carrying the HTTP status when the server answered. */
|
|
1337
|
+
var LanBindStatusError = class extends Error {
|
|
1338
|
+
status;
|
|
1339
|
+
/**
|
|
1340
|
+
* @param status - the response status, or undefined when the request failed
|
|
1341
|
+
* before a response (network error).
|
|
1342
|
+
* @param message - the diagnostic message.
|
|
1343
|
+
*/
|
|
1344
|
+
constructor(status, message) {
|
|
1345
|
+
super(message);
|
|
1346
|
+
this.status = status;
|
|
1347
|
+
this.name = "LanBindStatusError";
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1326
1350
|
/** Read the LAN-bind facts (loopback-only endpoint). */
|
|
1327
1351
|
async function readLanBindStatus() {
|
|
1328
1352
|
const response = await fetch("/api/pair/lan-bind");
|
|
1329
|
-
if (!response.ok) throw new
|
|
1353
|
+
if (!response.ok) throw new LanBindStatusError(response.status, `remote-web-ui: lan-bind status failed with ${String(response.status)}`);
|
|
1330
1354
|
return await response.json();
|
|
1331
1355
|
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Whether a LAN-bind status failure means this origin can never read the
|
|
1358
|
+
* endpoint (401/403 from the loopback-only fence), so a poll should stop
|
|
1359
|
+
* instead of retrying a known refusal; a transient error keeps the cadence.
|
|
1360
|
+
* @param status - the response status, or undefined for a network failure.
|
|
1361
|
+
*/
|
|
1362
|
+
function shouldStopLanBindPoll(status) {
|
|
1363
|
+
return status === 401 || status === 403;
|
|
1364
|
+
}
|
|
1332
1365
|
/** Human-readable expiry clock, e.g. "10:35". */
|
|
1333
1366
|
function formatClock(epochMs) {
|
|
1334
1367
|
const date = new Date(epochMs);
|
|
@@ -1376,7 +1409,7 @@ window.__ModuleLoader__.load({
|
|
|
1376
1409
|
}
|
|
1377
1410
|
//#endregion
|
|
1378
1411
|
//#region \0dsh-css:packages/dsh-remote-web-ui/src/client/remote.module.css.mjs
|
|
1379
|
-
const css$1 = ".fThDlq_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.fThDlq_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.fThDlq_trigger{width:36px;height:36px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;transition:background-color .12s,color .12s,box-shadow .12s;display:inline-flex;position:relative}.fThDlq_trigger[data-update-available]{color:var(--dsw-alias-brand-primary)}.fThDlq_trigger[data-update-available]:after{box-sizing:border-box;border:2px solid var(--dsw-alias-bg-layer-1);background:var(--dsw-alias-brand-primary);content:\"\";pointer-events:none;border-radius:50%;width:8px;height:8px;position:absolute;top:4px;right:4px}.fThDlq_trigger[data-wide=wide]{border-radius:999px;flex:auto;justify-content:flex-start;gap:8px;width:auto;min-width:0;padding:0 10px}.fThDlq_trigger[data-update-available][data-wide=wide]{flex:none;gap:6px}.fThDlq_trigger[data-update-available][data-wide=wide]:after{display:none}.fThDlq_updateBadgeText{white-space:nowrap;font-size:13px;font-weight:500;line-height:1}.fThDlq_trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.fThDlq_trigger:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.fThDlq_trigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}.fThDlq_trigger:disabled{opacity:.5;cursor:default}.fThDlq_panel{z-index:1;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);width:560px;max-width:calc(100vw - 48px);max-height:calc(100vh - 48px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:24px;flex-direction:column;gap:14px;padding:24px;font-size:14px;line-height:22px;display:flex;position:relative;overflow:auto}.fThDlq_header{align-items:flex-start;gap:12px;display:flex}.fThDlq_heading{flex:1;min-width:0}.fThDlq_title{margin:0;font-size:18px;font-weight:600;line-height:26px}.fThDlq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px}.fThDlq_close{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;transition:background-color .12s,color .12s,box-shadow .12s;display:inline-flex}.fThDlq_close:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_close:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.fThDlq_close:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}.fThDlq_close:disabled{opacity:.5;cursor:default}.fThDlq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:16px;flex-direction:column;align-items:center;gap:12px;padding:16px;display:flex}.fThDlq_cardHeader{justify-content:space-between;align-items:center;gap:12px;width:100%;display:flex}.fThDlq_cardTitle{font-weight:500}.fThDlq_badge{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:6px;min-width:0;padding:2px 10px;font-size:12px;line-height:18px;display:inline-flex}.fThDlq_badge:before{content:\"\";background:currentColor;border-radius:50%;width:8px;height:8px}.fThDlq_badge-waiting{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badge-connected{color:var(--dsw-alias-state-success-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badge-disconnected{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badge-stopped{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badgePublic{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badges{flex:none;align-items:center;gap:6px;display:inline-flex}.fThDlq_qrWrap{background:var(--dsw-alias-bg-base);border-radius:12px;justify-content:center;align-items:center;padding:12px;display:flex}.fThDlq_qr{display:block}.fThDlq_expired{color:var(--dsw-alias-state-error-primary);margin:0;font-size:13px}.fThDlq_expiry{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px}.fThDlq_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.fThDlq_link{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-caption);font-family:var(--dsw-font-mono,ui-monospace, monospace);margin:0;font-size:12px;display:block;overflow:hidden}.fThDlq_pairLinks{flex-direction:column;gap:8px;display:flex}.fThDlq_pairLinkRow{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:10px;align-items:center;gap:10px;min-width:0;padding:10px 12px;display:flex}.fThDlq_pairLinkText{flex:1;min-width:0}.fThDlq_pairLinkLabel{color:var(--dsw-alias-label-secondary);margin-bottom:3px;font-size:12px;display:block}.fThDlq_copyLink{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);min-height:30px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;align-items:center;gap:5px;padding:0 10px;display:inline-flex}.fThDlq_oneTimeHint{color:var(--dsw-alias-label-caption);margin:0;font-size:12px}.fThDlq_stoppedHint{color:var(--dsw-alias-state-error-primary);margin:0;font-size:13px}.fThDlq_tunnelNote{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.fThDlq_tunnelFailed{color:var(--dsw-alias-state-error-primary);margin:0;font-size:13px}.fThDlq_actions{gap:8px;display:flex}.fThDlq_action{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);height:34px;color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;border-radius:10px;justify-content:center;align-items:center;gap:6px;padding:0 14px;font-size:13px;transition:background-color .12s,border-color .12s,box-shadow .12s;display:inline-flex}.fThDlq_action:hover:not(:disabled){background:var(--dsw-alias-button-floating-hover)}.fThDlq_action:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.fThDlq_action:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}.fThDlq_action:disabled{opacity:.5;cursor:default}.fThDlq_banner{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:16px;padding:16px}.fThDlq_bannerTitle{color:var(--dsw-alias-state-warn-primary);margin:0;font-weight:500}.fThDlq_bannerHint{color:var(--dsw-alias-label-secondary);margin:6px 0 0;font-size:13px}.fThDlq_fencePage{z-index:2000;box-sizing:border-box;background:var(--dsw-alias-bg-base);text-align:center;flex-direction:column;justify-content:center;align-items:center;padding:40px 24px;display:flex;position:fixed;inset:0;overflow:auto}.fThDlq_fenceCard{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:min(520px,100%);box-shadow:var(--dsw-shadow-lv3);text-align:center;border-radius:20px;margin-inline:auto;padding:36px 40px}.fThDlq_fenceMark{background:var(--dsw-alias-state-error-secondary);width:44px;height:44px;color:var(--dsw-alias-state-error-primary);border-radius:50%;place-items:center;margin-inline:auto;font-size:24px;line-height:1;display:grid}.fThDlq_fenceEyebrow{color:var(--dsw-alias-state-error-primary);margin:22px 0 8px;font-size:13px;font-weight:600}.fThDlq_fenceTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:24px;line-height:1.35}.fThDlq_fenceDetail{color:var(--dsw-alias-label-secondary);margin:12px 0 0;font-size:14px;line-height:1.65}.fThDlq_fenceSteps{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);text-align:left;border-radius:12px;margin:24px auto 0;padding:20px 20px 20px 42px;font-size:14px;line-height:1.65}.fThDlq_fenceSteps li+li{margin-top:8px}.fThDlq_fenceForm{width:100%;margin-top:20px}.fThDlq_fenceInputRow{gap:8px;width:100%;display:flex}.fThDlq_fenceInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:0;color:var(--dsw-alias-label-primary);font:inherit;border-radius:10px;flex:1;padding:10px 14px;font-size:13px;transition:border-color .12s,box-shadow .12s}.fThDlq_fenceInput:focus{border-color:var(--dsw-alias-brand-primary);outline:none;box-shadow:0 0 0 2px #0066ff26}.fThDlq_fencePairButton{border:1px solid var(--dsw-alias-button-primary-fill);background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);font:inherit;cursor:pointer;border-radius:10px;flex-shrink:0;padding:10px 18px;font-size:13px;font-weight:500;transition:filter .12s,opacity .12s}.fThDlq_fencePairButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill));border-color:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill))}.fThDlq_fencePairButton:disabled{opacity:.55;cursor:not-allowed}.fThDlq_fencePairButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.fThDlq_fenceError{color:var(--dsw-alias-state-error-primary);text-align:left;margin:10px 0 0;font-size:13px;line-height:1.4}.fThDlq_fenceRetry{border:1px solid var(--dsw-alias-border-l2);width:100%;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border-radius:10px;margin-top:12px;padding:10px 16px;font-weight:500;transition:background-color .12s,color .12s}.fThDlq_fenceRetry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.fThDlq_fenceRetry:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.fThDlq_fenceFootnote{color:var(--dsw-alias-label-tertiary);margin:14px 0 0;font-size:12px;line-height:1.55}.fThDlq_addresses{border:none;margin:12px 0 0;padding:0}.fThDlq_addresses legend{color:var(--dsw-alias-label-secondary);padding:0;font-size:13px}.fThDlq_address{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;cursor:pointer;border-radius:6px;align-items:center;gap:8px;margin-top:6px;padding:4px 6px;font-size:13px;transition:background-color .12s;display:flex}.fThDlq_address:hover{background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_address input:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);border-radius:50%;outline:none}.fThDlq_addressValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;overflow:hidden}.fThDlq_addressHint{color:var(--dsw-alias-label-tertiary);margin:6px 0 0;font-size:12px}.fThDlq_devices{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:16px;flex-direction:column;gap:8px;padding:12px 16px 14px;display:flex}.fThDlq_devicesTitle{margin:0;font-size:13px;font-weight:500;line-height:20px}.fThDlq_devicesEmpty{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.fThDlq_deviceList{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}.fThDlq_deviceRow{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.fThDlq_deviceMeta{flex-direction:column;gap:2px;min-width:0;display:flex}.fThDlq_deviceName{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}.fThDlq_devicePresence{font-size:12px;line-height:18px}.fThDlq_deviceOnline{color:var(--dsw-alias-state-success-primary)}.fThDlq_deviceOffline{color:var(--dsw-alias-label-secondary)}.fThDlq_deviceSeen{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px}.fThDlq_deviceRevoke{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;padding:6px 10px;font-size:12px;transition:background-color .12s,color .12s}.fThDlq_deviceRevoke:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.fThDlq_deviceRevoke:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}@media (prefers-reduced-motion:reduce){.fThDlq_trigger,.fThDlq_close,.fThDlq_action,.fThDlq_address,.fThDlq_deviceRevoke{transition:none}}.fThDlq_entryRow{flex:none;align-items:center;gap:6px;min-width:0;display:flex}.fThDlq_entryRow[data-rail=rail]{flex-direction:column-reverse;gap:4px}.fThDlq_updateStatus{margin:0;font-weight:600}.fThDlq_updateDetail{color:var(--dsw-alias-label-secondary);margin:6px 0 0;font-size:13px}.fThDlq_updateError{color:var(--dsw-alias-text-danger,var(--dsw-alias-label-primary));margin:0;font-weight:600}.fThDlq_updateOutput{background:var(--dsw-alias-bg-layer-1);max-height:180px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-all;border-radius:10px;margin:10px 0 0;padding:10px 12px;font-size:12px;line-height:18px;overflow:auto}.fThDlq_updateList{flex-direction:column;gap:6px;margin:12px 0 0;padding:0;list-style:none;display:flex}.fThDlq_updateListItem{justify-content:space-between;align-items:baseline;gap:12px;font-size:13px;display:flex}.fThDlq_updateListName{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);overflow:hidden}.fThDlq_updateListVersions{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.fThDlq_updateNotes{margin-top:14px}.fThDlq_updateNotesTitle{margin:0 0 8px;font-size:14px;font-weight:600}.fThDlq_updateNoteSection{flex-direction:column;gap:4px;margin-top:10px;display:flex}.fThDlq_updateNoteHeading{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;font-weight:600}.fThDlq_updateNoteList{flex-direction:column;gap:4px;margin:0;padding-left:18px;font-size:13px;display:flex}.fThDlq_updateNoteItem{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.fThDlq_updateVersions{margin-top:12px}.fThDlq_updateVersionsSummary{cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none;font-size:12px}.fThDlq_updateActions{justify-content:flex-end;margin-top:16px;display:flex}.fThDlq_updateRetry{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:10px;align-items:center;gap:6px;padding:8px 14px;font-size:13px;transition:background-color .12s,color .12s;display:inline-flex}.fThDlq_updateRetry:hover{background:var(--dsw-alias-interactive-bg-active)}@media (prefers-reduced-motion:reduce){.fThDlq_updateRetry{transition:none}}";
|
|
1412
|
+
const css$1 = ".fThDlq_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.fThDlq_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.fThDlq_trigger{width:36px;height:36px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;transition:background-color .12s,color .12s,box-shadow .12s;display:inline-flex;position:relative}.fThDlq_trigger[data-update-available]{color:var(--dsw-alias-brand-primary)}.fThDlq_trigger[data-update-available]:after{box-sizing:border-box;border:2px solid var(--dsw-alias-bg-layer-1);background:var(--dsw-alias-brand-primary);content:\"\";pointer-events:none;border-radius:50%;width:8px;height:8px;position:absolute;top:4px;right:4px}.fThDlq_trigger[data-wide=wide]{border-radius:999px;flex:auto;justify-content:flex-start;gap:8px;width:auto;min-width:0;padding:0 10px}.fThDlq_trigger[data-update-available][data-wide=wide]{flex:none;gap:6px}.fThDlq_trigger[data-update-available][data-wide=wide]:after{display:none}.fThDlq_updateBadgeText{white-space:nowrap;font-size:13px;font-weight:500;line-height:1}.fThDlq_trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.fThDlq_trigger:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.fThDlq_trigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}.fThDlq_trigger:disabled{opacity:.5;cursor:default}.fThDlq_panel{z-index:1;box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);width:560px;max-width:calc(100vw - 48px);max-height:calc(100vh - 48px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:24px;flex-direction:column;gap:14px;padding:24px;font-size:14px;line-height:22px;display:flex;position:relative;overflow:auto}.fThDlq_header{align-items:flex-start;gap:12px;display:flex}.fThDlq_heading{flex:1;min-width:0}.fThDlq_title{margin:0;font-size:18px;font-weight:600;line-height:26px}.fThDlq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px}.fThDlq_close{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;transition:background-color .12s,color .12s,box-shadow .12s;display:inline-flex}.fThDlq_close:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_close:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.fThDlq_close:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}.fThDlq_close:disabled{opacity:.5;cursor:default}.fThDlq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:16px;flex-direction:column;align-items:center;gap:12px;padding:16px;display:flex}.fThDlq_cardHeader{justify-content:space-between;align-items:center;gap:12px;width:100%;display:flex}.fThDlq_cardTitle{font-weight:500}.fThDlq_badge{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:6px;min-width:0;padding:2px 10px;font-size:12px;line-height:18px;display:inline-flex}.fThDlq_badge:before{content:\"\";background:currentColor;border-radius:50%;width:8px;height:8px}.fThDlq_badge-waiting{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badge-connected{color:var(--dsw-alias-state-success-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badge-disconnected{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badge-stopped{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badgePublic{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_badges{flex:none;align-items:center;gap:6px;display:inline-flex}.fThDlq_qrWrap{background:var(--dsw-alias-bg-base);border-radius:12px;justify-content:center;align-items:center;padding:12px;display:flex}.fThDlq_qr{display:block}.fThDlq_expired{color:var(--dsw-alias-state-error-primary);margin:0;font-size:13px}.fThDlq_expiry{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px}.fThDlq_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.fThDlq_link{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-caption);font-family:var(--dsw-font-mono,ui-monospace, monospace);margin:0;font-size:12px;display:block;overflow:hidden}.fThDlq_pairLinks{flex-direction:column;gap:8px;display:flex}.fThDlq_pairLinkRow{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:10px;align-items:center;gap:10px;min-width:0;padding:10px 12px;display:flex}.fThDlq_pairLinkText{flex:1;min-width:0}.fThDlq_pairLinkLabel{color:var(--dsw-alias-label-secondary);margin-bottom:3px;font-size:12px;display:block}.fThDlq_copyLink{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);min-height:30px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;align-items:center;gap:5px;padding:0 10px;display:inline-flex}.fThDlq_oneTimeHint{color:var(--dsw-alias-label-caption);margin:0;font-size:12px}.fThDlq_stoppedHint{color:var(--dsw-alias-state-error-primary);margin:0;font-size:13px}.fThDlq_tunnelNote{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.fThDlq_tunnelFailed{color:var(--dsw-alias-state-error-primary);margin:0;font-size:13px}.fThDlq_actions{gap:8px;display:flex}.fThDlq_action{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);height:34px;color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;border-radius:10px;justify-content:center;align-items:center;gap:6px;padding:0 14px;font-size:13px;transition:background-color .12s,border-color .12s,box-shadow .12s;display:inline-flex}.fThDlq_action:hover:not(:disabled){background:var(--dsw-alias-button-floating-hover)}.fThDlq_action:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}.fThDlq_action:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}.fThDlq_action:disabled{opacity:.5;cursor:default}.fThDlq_banner{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:16px;padding:16px}.fThDlq_bannerTitle{color:var(--dsw-alias-state-warn-primary);margin:0;font-weight:500}.fThDlq_bannerHint{color:var(--dsw-alias-label-secondary);margin:6px 0 0;font-size:13px}.fThDlq_fencePage{z-index:2000;box-sizing:border-box;background:var(--dsw-alias-bg-base);text-align:center;flex-direction:column;justify-content:center;align-items:center;padding:40px 24px;display:flex;position:fixed;inset:0;overflow:auto}.fThDlq_fenceCard{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:min(520px,100%);box-shadow:var(--dsw-shadow-lv3);text-align:center;border-radius:20px;margin-inline:auto;padding:36px 40px}.fThDlq_fenceMark{background:var(--dsw-alias-state-error-secondary);width:44px;height:44px;color:var(--dsw-alias-state-error-primary);border-radius:50%;place-items:center;margin-inline:auto;font-size:24px;line-height:1;display:grid}.fThDlq_fenceEyebrow{color:var(--dsw-alias-state-error-primary);margin:22px 0 8px;font-size:13px;font-weight:600}.fThDlq_fenceTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:24px;line-height:1.35}.fThDlq_fenceDetail{color:var(--dsw-alias-label-secondary);margin:12px 0 0;font-size:14px;line-height:1.65}.fThDlq_fenceSteps{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);text-align:left;border-radius:12px;margin:24px auto 0;padding:20px 20px 20px 42px;font-size:14px;line-height:1.65}.fThDlq_fenceSteps li+li{margin-top:8px}.fThDlq_fenceForm{width:100%;margin-top:20px}.fThDlq_fenceInputRow{gap:8px;width:100%;display:flex}.fThDlq_fenceInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:0;color:var(--dsw-alias-label-primary);font:inherit;border-radius:10px;flex:1;padding:10px 14px;font-size:13px;transition:border-color .12s,box-shadow .12s}.fThDlq_fenceInput:focus{border-color:var(--dsw-alias-brand-primary);outline:none;box-shadow:0 0 0 2px #0066ff26}.fThDlq_fencePairButton{border:1px solid var(--dsw-alias-button-primary-fill);background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);font:inherit;cursor:pointer;border-radius:10px;flex-shrink:0;padding:10px 18px;font-size:13px;font-weight:500;transition:filter .12s,opacity .12s}.fThDlq_fencePairButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill));border-color:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill))}.fThDlq_fencePairButton:disabled{opacity:.55;cursor:not-allowed}.fThDlq_fencePairButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.fThDlq_fenceError{color:var(--dsw-alias-state-error-primary);text-align:left;margin:10px 0 0;font-size:13px;line-height:1.4}.fThDlq_fenceRetry{border:1px solid var(--dsw-alias-border-l2);width:100%;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border-radius:10px;margin-top:12px;padding:10px 16px;font-weight:500;transition:background-color .12s,color .12s}.fThDlq_fenceRetry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.fThDlq_fenceRetry:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.fThDlq_fenceFootnote{color:var(--dsw-alias-label-tertiary);margin:14px 0 0;font-size:12px;line-height:1.55}.fThDlq_addresses{border:none;margin:12px 0 0;padding:0}.fThDlq_addresses legend{color:var(--dsw-alias-label-secondary);padding:0;font-size:13px}.fThDlq_address{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;cursor:pointer;border-radius:6px;align-items:center;gap:8px;margin-top:6px;padding:4px 6px;font-size:13px;transition:background-color .12s;display:flex}.fThDlq_address:hover{background:var(--dsw-alias-interactive-bg-hover)}.fThDlq_address input:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);border-radius:50%;outline:none}.fThDlq_addressValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;overflow:hidden}.fThDlq_addressHint{color:var(--dsw-alias-label-tertiary);margin:6px 0 0;font-size:12px}.fThDlq_devices{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:16px;flex-direction:column;gap:8px;padding:12px 16px 14px;display:flex}.fThDlq_devicesTitle{margin:0;font-size:13px;font-weight:500;line-height:20px}.fThDlq_devicesEmpty{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px}.fThDlq_deviceList{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}.fThDlq_deviceRow{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.fThDlq_deviceMeta{flex-direction:column;gap:2px;min-width:0;display:flex}.fThDlq_deviceName{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}.fThDlq_devicePresence{font-size:12px;line-height:18px}.fThDlq_deviceOnline{color:var(--dsw-alias-state-success-primary)}.fThDlq_deviceOffline{color:var(--dsw-alias-label-secondary)}.fThDlq_deviceSeen{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px}.fThDlq_deviceRevoke{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;flex:none;padding:6px 10px;font-size:12px;transition:background-color .12s,color .12s}.fThDlq_deviceRevoke:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.fThDlq_deviceRevoke:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-2), 0 0 0 4px var(--dsw-alias-brand-primary);outline:none}@media (prefers-reduced-motion:reduce){.fThDlq_trigger,.fThDlq_close,.fThDlq_action,.fThDlq_address,.fThDlq_deviceRevoke{transition:none}}.fThDlq_entryRow{flex:none;align-items:center;gap:6px;min-width:0;display:flex}.fThDlq_entryRow[data-rail=rail]{flex-direction:column-reverse;gap:4px}[data-dsh-frame]:not([data-sidebar-collapsed]) [class*=footArea]{flex-flow:wrap;align-items:center}[data-dsh-frame]:not([data-sidebar-collapsed]) [class*=footArea]>:not([class*=settingsArea]):not([class*=footerActions]){flex:100%;min-width:0}[data-dsh-frame]:not([data-sidebar-collapsed]) [class*=settingsArea]{flex:auto;order:1;width:auto;min-width:0}[data-dsh-frame]:not([data-sidebar-collapsed]) [class*=footerActions]{flex:none;order:2;align-items:center;width:auto;min-width:0}.fThDlq_updateStatus{margin:0;font-weight:600}.fThDlq_updateDetail{color:var(--dsw-alias-label-secondary);margin:6px 0 0;font-size:13px}.fThDlq_updateError{color:var(--dsw-alias-text-danger,var(--dsw-alias-label-primary));margin:0;font-weight:600}.fThDlq_updateOutput{background:var(--dsw-alias-bg-layer-1);max-height:180px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-all;border-radius:10px;margin:10px 0 0;padding:10px 12px;font-size:12px;line-height:18px;overflow:auto}.fThDlq_updateList{flex-direction:column;gap:6px;margin:12px 0 0;padding:0;list-style:none;display:flex}.fThDlq_updateListItem{justify-content:space-between;align-items:baseline;gap:12px;font-size:13px;display:flex}.fThDlq_updateListName{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);overflow:hidden}.fThDlq_updateListVersions{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.fThDlq_updateNotes{margin-top:14px}.fThDlq_updateNotesTitle{margin:0 0 8px;font-size:14px;font-weight:600}.fThDlq_updateNoteSection{flex-direction:column;gap:4px;margin-top:10px;display:flex}.fThDlq_updateNoteHeading{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;font-weight:600}.fThDlq_updateNoteList{flex-direction:column;gap:4px;margin:0;padding-left:18px;font-size:13px;display:flex}.fThDlq_updateNoteItem{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.fThDlq_updateVersions{margin-top:12px}.fThDlq_updateVersionsSummary{cursor:pointer;color:var(--dsw-alias-label-secondary);user-select:none;font-size:12px}.fThDlq_updateActions{justify-content:flex-end;margin-top:16px;display:flex}.fThDlq_updateRetry{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:10px;align-items:center;gap:6px;padding:8px 14px;font-size:13px;transition:background-color .12s,color .12s;display:inline-flex}.fThDlq_updateRetry:hover{background:var(--dsw-alias-interactive-bg-active)}@media (prefers-reduced-motion:reduce){.fThDlq_updateRetry{transition:none}}";
|
|
1380
1413
|
const tagId$1 = "@linxin666/dsh-remote-web-ui/packages/dsh-remote-web-ui/src/client/remote.module.css";
|
|
1381
1414
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
1382
1415
|
const tag = document.createElement("style");
|
|
@@ -1484,6 +1517,20 @@ window.__ModuleLoader__.load({
|
|
|
1484
1517
|
* actions (stop / refresh / copy). Pure presentation — all state and
|
|
1485
1518
|
* actions arrive through props from the entry's behavior component.
|
|
1486
1519
|
*/
|
|
1520
|
+
/**
|
|
1521
|
+
* The QR symbol, memoized on the link: the panel re-renders on every SSE state
|
|
1522
|
+
* frame (a paired phone heartbeats every 10s), and qrcode.react rebuilds the
|
|
1523
|
+
* SVG path and element tree on each render.
|
|
1524
|
+
*/
|
|
1525
|
+
const PairQrCode = (0, react.memo)(function PairQrCode({ url, className }) {
|
|
1526
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QRCodeSVG, {
|
|
1527
|
+
value: url,
|
|
1528
|
+
size: 184,
|
|
1529
|
+
level: "M",
|
|
1530
|
+
marginSize: 1,
|
|
1531
|
+
className
|
|
1532
|
+
});
|
|
1533
|
+
});
|
|
1487
1534
|
/** Badge text + tone per phase (ready states only). */
|
|
1488
1535
|
function statusOf(t, state) {
|
|
1489
1536
|
switch (state.phase) {
|
|
@@ -1536,7 +1583,7 @@ window.__ModuleLoader__.load({
|
|
|
1536
1583
|
className: remote_module_css_default.close,
|
|
1537
1584
|
"aria-label": t("close.label"),
|
|
1538
1585
|
onClick: onClose,
|
|
1539
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1586
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutlineRegular, { size: 14 })
|
|
1540
1587
|
})]
|
|
1541
1588
|
}), state.kind === "lan-required" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1542
1589
|
className: remote_module_css_default.banner,
|
|
@@ -1602,11 +1649,8 @@ window.__ModuleLoader__.load({
|
|
|
1602
1649
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1603
1650
|
className: remote_module_css_default.qrWrap,
|
|
1604
1651
|
"data-testid": "remote-qr",
|
|
1605
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1606
|
-
|
|
1607
|
-
size: 184,
|
|
1608
|
-
level: "M",
|
|
1609
|
-
marginSize: 1,
|
|
1652
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PairQrCode, {
|
|
1653
|
+
url: state.url,
|
|
1610
1654
|
className: remote_module_css_default.qr
|
|
1611
1655
|
})
|
|
1612
1656
|
}),
|
|
@@ -1641,7 +1685,7 @@ window.__ModuleLoader__.load({
|
|
|
1641
1685
|
type: "button",
|
|
1642
1686
|
className: remote_module_css_default.copyLink,
|
|
1643
1687
|
onClick: () => onCopy(state.url),
|
|
1644
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1688
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, { size: 14 }), copied ? t("action.copied") : t("action.copyLink")]
|
|
1645
1689
|
})]
|
|
1646
1690
|
}), state.token !== void 0 && state.token !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1647
1691
|
className: remote_module_css_default.pairLinkRow,
|
|
@@ -1659,7 +1703,7 @@ window.__ModuleLoader__.load({
|
|
|
1659
1703
|
type: "button",
|
|
1660
1704
|
className: remote_module_css_default.copyLink,
|
|
1661
1705
|
onClick: () => onCopyToken ? onCopyToken(state.token) : onCopy(state.token),
|
|
1662
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1706
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, { size: 14 }), copiedToken ? t("action.copiedToken") : t("action.copyToken")]
|
|
1663
1707
|
})]
|
|
1664
1708
|
})]
|
|
1665
1709
|
}),
|
|
@@ -1740,12 +1784,12 @@ window.__ModuleLoader__.load({
|
|
|
1740
1784
|
type: "button",
|
|
1741
1785
|
className: remote_module_css_default.action,
|
|
1742
1786
|
onClick: onStop,
|
|
1743
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1787
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconStopFillRegular, { size: 14 }), t("action.stop")]
|
|
1744
1788
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1745
1789
|
type: "button",
|
|
1746
1790
|
className: remote_module_css_default.action,
|
|
1747
1791
|
onClick: onRefresh,
|
|
1748
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1792
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, { size: 14 }), t("action.refresh")]
|
|
1749
1793
|
})]
|
|
1750
1794
|
}),
|
|
1751
1795
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -1897,7 +1941,7 @@ window.__ModuleLoader__.load({
|
|
|
1897
1941
|
className: remote_module_css_default.close,
|
|
1898
1942
|
"aria-label": t("update.close"),
|
|
1899
1943
|
onClick: onClose,
|
|
1900
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1944
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutlineRegular, {})
|
|
1901
1945
|
})]
|
|
1902
1946
|
}),
|
|
1903
1947
|
view.kind === "checking" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -1952,7 +1996,7 @@ window.__ModuleLoader__.load({
|
|
|
1952
1996
|
className: remote_module_css_default.updateRetry,
|
|
1953
1997
|
onClick: onRecheck,
|
|
1954
1998
|
children: [
|
|
1955
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
1999
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {}),
|
|
1956
2000
|
" ",
|
|
1957
2001
|
t("update.retry")
|
|
1958
2002
|
]
|
|
@@ -2258,7 +2302,7 @@ window.__ModuleLoader__.load({
|
|
|
2258
2302
|
"aria-expanded": open,
|
|
2259
2303
|
title: updateLabel,
|
|
2260
2304
|
onClick: openPanel,
|
|
2261
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
2305
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutlineRegular, { size: wide ? 16 : 18 }), updateAvailable && wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2262
2306
|
className: remote_module_css_default.updateBadgeText,
|
|
2263
2307
|
children: t("update.badge")
|
|
2264
2308
|
})]
|
|
@@ -2414,19 +2458,21 @@ window.__ModuleLoader__.load({
|
|
|
2414
2458
|
}, [state]);
|
|
2415
2459
|
(0, react.useEffect)(() => closeEventSource, [closeEventSource]);
|
|
2416
2460
|
const handleStop = (0, react.useCallback)(() => {
|
|
2417
|
-
stopPair().
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2461
|
+
stopPair().then(() => {
|
|
2462
|
+
setState((previous) => previous.kind === "ready" ? {
|
|
2463
|
+
...previous,
|
|
2464
|
+
phase: "stopped",
|
|
2465
|
+
devices: []
|
|
2466
|
+
} : previous);
|
|
2467
|
+
}).catch(() => {});
|
|
2423
2468
|
}, []);
|
|
2424
2469
|
const handleRevoke = (0, react.useCallback)((deviceId) => {
|
|
2425
|
-
revokePair(deviceId).
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2470
|
+
revokePair(deviceId).then(() => {
|
|
2471
|
+
setState((previous) => previous.kind === "ready" ? {
|
|
2472
|
+
...previous,
|
|
2473
|
+
devices: previous.devices.filter((device) => device.id !== deviceId)
|
|
2474
|
+
} : previous);
|
|
2475
|
+
}).catch(() => {});
|
|
2430
2476
|
}, []);
|
|
2431
2477
|
const handleRefresh = (0, react.useCallback)(() => {
|
|
2432
2478
|
mint().then(setState);
|
|
@@ -2688,17 +2734,20 @@ window.__ModuleLoader__.load({
|
|
|
2688
2734
|
})
|
|
2689
2735
|
]
|
|
2690
2736
|
});
|
|
2691
|
-
if (!state.exposed
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
className:
|
|
2695
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
2696
|
-
className: settings_card_module_css_default.
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2737
|
+
if (!state.exposed && props.renderChildrenWhenNotExposed !== true) {
|
|
2738
|
+
const showNotice = props.hideNotExposedNotice !== true;
|
|
2739
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2740
|
+
className: cardClass,
|
|
2741
|
+
children: [header, expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2742
|
+
className: settings_card_module_css_default.body,
|
|
2743
|
+
children: showNotice ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2744
|
+
className: settings_card_module_css_default.notExposed,
|
|
2745
|
+
role: "status",
|
|
2746
|
+
children: props.t("settings.notExposed")
|
|
2747
|
+
}) : null
|
|
2748
|
+
}) : null]
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2702
2751
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2703
2752
|
className: cardClass,
|
|
2704
2753
|
children: [header, expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3098,31 +3147,31 @@ window.__ModuleLoader__.load({
|
|
|
3098
3147
|
specs;
|
|
3099
3148
|
staged = /* @__PURE__ */ new Map();
|
|
3100
3149
|
listeners = /* @__PURE__ */ new Set();
|
|
3101
|
-
/** The
|
|
3102
|
-
|
|
3150
|
+
/** The form subscription installed in the constructor; released by dispose(). */
|
|
3151
|
+
disposeForm;
|
|
3103
3152
|
disposed = false;
|
|
3104
3153
|
saving = false;
|
|
3105
3154
|
failed = false;
|
|
3106
3155
|
failedReason;
|
|
3107
|
-
/** @param scope - the bound
|
|
3156
|
+
/** @param scope - the bound configuration form for this card's namespace. */
|
|
3108
3157
|
constructor(scope, specs) {
|
|
3109
3158
|
this.scope = scope;
|
|
3110
3159
|
this.specs = new Map(specs.map((spec) => [spec.field, spec]));
|
|
3111
|
-
this.
|
|
3160
|
+
this.disposeForm = scope.subscribe(() => {
|
|
3112
3161
|
this.publish();
|
|
3113
3162
|
});
|
|
3114
3163
|
}
|
|
3115
3164
|
/**
|
|
3116
|
-
* Release the
|
|
3165
|
+
* Release the form subscription and every bound store listener. The card
|
|
3117
3166
|
* must call this on teardown; later calls are no-ops.
|
|
3118
3167
|
*/
|
|
3119
3168
|
dispose() {
|
|
3120
3169
|
if (this.disposed) return;
|
|
3121
3170
|
this.disposed = true;
|
|
3122
|
-
this.
|
|
3171
|
+
this.disposeForm();
|
|
3123
3172
|
this.listeners.clear();
|
|
3124
3173
|
}
|
|
3125
|
-
/** Publish a projection of this form, rebuilt whenever the
|
|
3174
|
+
/** Publish a projection of this form, rebuilt whenever the form or a draft changes. */
|
|
3126
3175
|
bind(project) {
|
|
3127
3176
|
const store = createSnapshotStore(project());
|
|
3128
3177
|
this.listeners.add(() => {
|
|
@@ -3189,19 +3238,19 @@ window.__ModuleLoader__.load({
|
|
|
3189
3238
|
};
|
|
3190
3239
|
}
|
|
3191
3240
|
/**
|
|
3192
|
-
* Write every staged edit in one atomic
|
|
3241
|
+
* Write every staged edit in one atomic form mutation, then re-seed from
|
|
3193
3242
|
* what the Host accepted.
|
|
3194
3243
|
*
|
|
3195
3244
|
* The whole batch rides one mutate, so cross-field validate hooks
|
|
3196
3245
|
* (baseURL+model) judge it as a unit: the Host either applies every write
|
|
3197
|
-
* or refuses the batch. The
|
|
3198
|
-
*
|
|
3199
|
-
*
|
|
3200
|
-
* settled snapshot back
|
|
3201
|
-
* fails the whole save. A
|
|
3202
|
-
*
|
|
3203
|
-
* message. A save that did not land keeps its drafts, so
|
|
3204
|
-
* correct them instead of retyping.
|
|
3246
|
+
* or refuses the batch. The form contract answers a refusal or a skipped
|
|
3247
|
+
* write with `false` (it recovers with a fresh Host view instead of
|
|
3248
|
+
* throwing), so the outcome is judged twice: the answer itself, and then the
|
|
3249
|
+
* settled snapshot read back one planned write at a time. One missed write
|
|
3250
|
+
* fails the whole save. A transport that rejects instead (the dsh-web bridge
|
|
3251
|
+
* controller on a dead connection) reports through the same failure path
|
|
3252
|
+
* with its rejection message. A save that did not land keeps its drafts, so
|
|
3253
|
+
* the user can correct them instead of retyping.
|
|
3205
3254
|
* @returns settlement after the mutation and the read-back.
|
|
3206
3255
|
*/
|
|
3207
3256
|
async save() {
|
|
@@ -3223,12 +3272,13 @@ window.__ModuleLoader__.load({
|
|
|
3223
3272
|
path: [item.field]
|
|
3224
3273
|
});
|
|
3225
3274
|
let failedReason;
|
|
3275
|
+
let accepted = false;
|
|
3226
3276
|
try {
|
|
3227
|
-
await this.scope.mutate(ops);
|
|
3277
|
+
accepted = await this.scope.mutate(ops);
|
|
3228
3278
|
} catch (error) {
|
|
3229
3279
|
failedReason = error instanceof Error ? error.message : String(error);
|
|
3230
3280
|
}
|
|
3231
|
-
const landed = failedReason === void 0 && valid.every((item) => item.judge());
|
|
3281
|
+
const landed = accepted && failedReason === void 0 && valid.every((item) => item.judge());
|
|
3232
3282
|
for (const [field, before] of pending) if (landed && this.staged.get(field) === before) this.staged.delete(field);
|
|
3233
3283
|
this.saving = false;
|
|
3234
3284
|
this.failed = !landed;
|
|
@@ -3338,13 +3388,13 @@ window.__ModuleLoader__.load({
|
|
|
3338
3388
|
/**
|
|
3339
3389
|
* The remote-control settings card: pairing security and device limits.
|
|
3340
3390
|
* Registers into the `web-ui.plugin.item` child slot the Web UI plugin group
|
|
3341
|
-
* renders, bound to the `remote-web-ui`
|
|
3391
|
+
* renders, bound to the `remote-web-ui` profile entry's configuration form.
|
|
3342
3392
|
*/
|
|
3343
|
-
/** Bridges the `remote-web-ui`
|
|
3393
|
+
/** Bridges the `remote-web-ui` form onto the card's staged form. */
|
|
3344
3394
|
var RemoteSettingsCardController = class {
|
|
3345
3395
|
form;
|
|
3346
3396
|
store;
|
|
3347
|
-
/** @param scope - the
|
|
3397
|
+
/** @param scope - the configuration form for the `remote-web-ui` entry. */
|
|
3348
3398
|
constructor(scope) {
|
|
3349
3399
|
this.form = new CardForm(scope, [
|
|
3350
3400
|
booleanField("enabled"),
|
|
@@ -3390,7 +3440,7 @@ window.__ModuleLoader__.load({
|
|
|
3390
3440
|
};
|
|
3391
3441
|
}
|
|
3392
3442
|
/**
|
|
3393
|
-
* Release the card's
|
|
3443
|
+
* Release the card's form subscription and bound stores; the slot
|
|
3394
3444
|
* disposer calls this on teardown.
|
|
3395
3445
|
*/
|
|
3396
3446
|
dispose() {
|
|
@@ -3418,6 +3468,8 @@ window.__ModuleLoader__.load({
|
|
|
3418
3468
|
descriptionKey: "settings.description",
|
|
3419
3469
|
defaultOpen: false,
|
|
3420
3470
|
state,
|
|
3471
|
+
renderChildrenWhenNotExposed: true,
|
|
3472
|
+
hideNotExposedNotice: true,
|
|
3421
3473
|
onSave: props.save,
|
|
3422
3474
|
onDiscard: props.discard,
|
|
3423
3475
|
children: [
|
|
@@ -3615,10 +3667,12 @@ window.__ModuleLoader__.load({
|
|
|
3615
3667
|
const read = () => {
|
|
3616
3668
|
readLanBindStatus().then((value) => {
|
|
3617
3669
|
if (alive) setFrame(value);
|
|
3618
|
-
}).catch(() => {
|
|
3670
|
+
}).catch((error) => {
|
|
3671
|
+
if (error instanceof LanBindStatusError && shouldStopLanBindPoll(error.status)) window.clearInterval(timer);
|
|
3672
|
+
});
|
|
3619
3673
|
};
|
|
3620
|
-
read();
|
|
3621
3674
|
const timer = window.setInterval(read, 1e4);
|
|
3675
|
+
read();
|
|
3622
3676
|
return () => {
|
|
3623
3677
|
alive = false;
|
|
3624
3678
|
window.clearInterval(timer);
|
|
@@ -3684,9 +3738,9 @@ window.__ModuleLoader__.load({
|
|
|
3684
3738
|
"pair.linkLabel": "配对链接",
|
|
3685
3739
|
"pair.tokenLabel": "配对令牌",
|
|
3686
3740
|
"pair.dockerHint": "Docker 或反向代理环境下,可直接复制此令牌并在目标设备配对页面输入。",
|
|
3687
|
-
"pair.oneTimeHint": "
|
|
3741
|
+
"pair.oneTimeHint": "链接含限时令牌;在令牌过期或刷新二维码之前,同一链接可为多台设备完成配对(每次配对各自生成独立设备会话)。",
|
|
3688
3742
|
"pair.failed.title": "配对失败",
|
|
3689
|
-
"pair.failed.detail": "
|
|
3743
|
+
"pair.failed.detail": "链接无效或已过期,请回到电脑端刷新二维码后重新扫码。",
|
|
3690
3744
|
"fence.unpaired.title": "此设备未配对,无法访问工作区数据",
|
|
3691
3745
|
"fence.unpaired.eyebrow": "需要设备配对",
|
|
3692
3746
|
"fence.unpaired.hint": "为保护工作区、会话与插件数据,远程电脑必须先通过主电脑授权。",
|
|
@@ -3698,7 +3752,6 @@ window.__ModuleLoader__.load({
|
|
|
3698
3752
|
"fence.unpaired.pairAction": "立即配对",
|
|
3699
3753
|
"fence.unpaired.pairing": "配对中…",
|
|
3700
3754
|
"fence.unpaired.tokenInvalid": "配对链接或 Token 无效已过期",
|
|
3701
|
-
"fence.unpaired.tokenUsed": "该配对链接已被使用",
|
|
3702
3755
|
"fence.unpaired.tokenFailed": "配对失败,请检查网络或重新获取链接",
|
|
3703
3756
|
"fence.unpaired.footnote": "请勿使用他人提供的配对链接;管理员可随时取消此设备的授权。",
|
|
3704
3757
|
"posture.exposed": "/api 通道对未配对设备敞开",
|
|
@@ -3849,9 +3902,9 @@ window.__ModuleLoader__.load({
|
|
|
3849
3902
|
"pair.linkLabel": "Pairing link",
|
|
3850
3903
|
"pair.tokenLabel": "Pairing token",
|
|
3851
3904
|
"pair.dockerHint": "In Docker or reverse proxy environments, copy this token to pair directly on the target device.",
|
|
3852
|
-
"pair.oneTimeHint": "The link carries
|
|
3905
|
+
"pair.oneTimeHint": "The link carries a time-limited token: until it expires or the QR is refreshed, the same link can pair several devices, each with its own device session.",
|
|
3853
3906
|
"pair.failed.title": "Pairing failed",
|
|
3854
|
-
"pair.failed.detail": "The link is invalid or
|
|
3907
|
+
"pair.failed.detail": "The link is invalid or has expired. Refresh the QR code on your computer and scan again.",
|
|
3855
3908
|
"fence.unpaired.title": "This device is not paired and cannot reach workspace data",
|
|
3856
3909
|
"fence.unpaired.eyebrow": "Device pairing required",
|
|
3857
3910
|
"fence.unpaired.hint": "To protect workspace, session, and plugin data, a remote computer must first be authorized by the primary computer.",
|
|
@@ -3863,7 +3916,6 @@ window.__ModuleLoader__.load({
|
|
|
3863
3916
|
"fence.unpaired.pairAction": "Pair Now",
|
|
3864
3917
|
"fence.unpaired.pairing": "Pairing…",
|
|
3865
3918
|
"fence.unpaired.tokenInvalid": "Pairing link or token is invalid or expired",
|
|
3866
|
-
"fence.unpaired.tokenUsed": "This pairing link has already been used",
|
|
3867
3919
|
"fence.unpaired.tokenFailed": "Pairing failed, please check network or issue a new link",
|
|
3868
3920
|
"fence.unpaired.footnote": "Do not use pairing links from people you do not trust. An administrator can revoke this device at any time.",
|
|
3869
3921
|
"posture.exposed": "The /api channel is open to unpaired devices",
|
|
@@ -4066,13 +4118,15 @@ window.__ModuleLoader__.load({
|
|
|
4066
4118
|
"/api/remote.mux",
|
|
4067
4119
|
"/sidebar/ws/terminal",
|
|
4068
4120
|
"/sidebar/ws/agent-terminals",
|
|
4121
|
+
"/sidebar/ws/agent-opens",
|
|
4069
4122
|
"/api/dsh-ssh/terminal"
|
|
4070
4123
|
],
|
|
4071
4124
|
deviceHeader: REMOTE_DEVICE_HEADER,
|
|
4072
4125
|
deviceKey: "dsh-remote-device",
|
|
4073
4126
|
deviceQuery: REMOTE_DEVICE_QUERY,
|
|
4074
4127
|
uploadPath: "/api/session/uploadFileBinary",
|
|
4075
|
-
uploadHookGlobal: "__DSH_FILE_UPLOAD__"
|
|
4128
|
+
uploadHookGlobal: "__DSH_FILE_UPLOAD__",
|
|
4129
|
+
hostGrantGlobal: "__DSH_REMOTE_HOST_GRANT__"
|
|
4076
4130
|
};
|
|
4077
4131
|
/** The window global the boot patch publishes its seat under. */
|
|
4078
4132
|
const REMOTE_CHANNEL_BOOT_GLOBAL = "__DSH_REMOTE_CHANNEL_BOOT__";
|
|
@@ -4115,7 +4169,7 @@ window.__ModuleLoader__.load({
|
|
|
4115
4169
|
* @returns true for localhost, IPv6 loopback, or any 127/8 literal.
|
|
4116
4170
|
*/
|
|
4117
4171
|
function isLoopbackHostname(hostname) {
|
|
4118
|
-
if (hostname === "localhost" || hostname === "::1") return true;
|
|
4172
|
+
if (hostname === "localhost" || hostname === "::1" || hostname === "[::1]") return true;
|
|
4119
4173
|
const parts = hostname.split(".");
|
|
4120
4174
|
return parts.length === 4 && parts[0] === "127" && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
|
|
4121
4175
|
}
|
|
@@ -4233,10 +4287,14 @@ window.__ModuleLoader__.load({
|
|
|
4233
4287
|
const attach = (init) => {
|
|
4234
4288
|
if (device === null) return init;
|
|
4235
4289
|
const headers = init?.headers;
|
|
4236
|
-
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4290
|
+
if (typeof Headers !== "undefined" && headers instanceof Headers) try {
|
|
4291
|
+
const copy = new Headers(headers);
|
|
4292
|
+
copy.set(RULES.deviceHeader, device);
|
|
4293
|
+
return {
|
|
4294
|
+
...init,
|
|
4295
|
+
headers: copy
|
|
4296
|
+
};
|
|
4297
|
+
} catch {
|
|
4240
4298
|
return init;
|
|
4241
4299
|
}
|
|
4242
4300
|
if (typeof headers === "object" && headers !== null) return {
|
|
@@ -4259,7 +4317,7 @@ window.__ModuleLoader__.load({
|
|
|
4259
4317
|
rewritten.pathname = rewritePath(url.pathname);
|
|
4260
4318
|
const next = typeof input === "string" || input instanceof URL ? rewritten.toString() : new Request(rewritten, input);
|
|
4261
4319
|
return Promise.resolve(originalFetch.call(window, next, attach(init))).then(async (response) => {
|
|
4262
|
-
if (await isUnpairedDenied(response.clone())) options.onUnpaired?.();
|
|
4320
|
+
if (response.status === 403 && await isUnpairedDenied(response.clone())) options.onUnpaired?.();
|
|
4263
4321
|
else options.onPaired?.();
|
|
4264
4322
|
return response;
|
|
4265
4323
|
});
|
|
@@ -4403,7 +4461,6 @@ window.__ModuleLoader__.load({
|
|
|
4403
4461
|
return;
|
|
4404
4462
|
}
|
|
4405
4463
|
if (result.code === "invalid") setErrorMsg(t("fence.unpaired.tokenInvalid"));
|
|
4406
|
-
else if (result.code === "used") setErrorMsg(t("fence.unpaired.tokenUsed"));
|
|
4407
4464
|
else setErrorMsg(t("fence.unpaired.tokenFailed"));
|
|
4408
4465
|
} catch {
|
|
4409
4466
|
setErrorMsg(t("fence.unpaired.tokenFailed"));
|
|
@@ -4496,7 +4553,7 @@ window.__ModuleLoader__.load({
|
|
|
4496
4553
|
/** The building package's version, when the bundle carries it. */
|
|
4497
4554
|
function bakedVersion() {
|
|
4498
4555
|
try {
|
|
4499
|
-
return "0.
|
|
4556
|
+
return "0.4.0";
|
|
4500
4557
|
} catch {
|
|
4501
4558
|
return;
|
|
4502
4559
|
}
|
|
@@ -4755,7 +4812,7 @@ window.__ModuleLoader__.load({
|
|
|
4755
4812
|
* rail compaction) while the body class stays.
|
|
4756
4813
|
*/
|
|
4757
4814
|
function ensureAdaptStyle() {
|
|
4758
|
-
if (
|
|
4815
|
+
if (nodeOf(`style[data-plugin-css="${ADAPT_CSS_ID}"]`) !== null) return;
|
|
4759
4816
|
const tag = document.createElement("style");
|
|
4760
4817
|
tag.dataset.plugin = "remote-web-ui";
|
|
4761
4818
|
tag.dataset.pluginCss = ADAPT_CSS_ID;
|
|
@@ -4795,6 +4852,7 @@ window.__ModuleLoader__.load({
|
|
|
4795
4852
|
savedViewportContent = null;
|
|
4796
4853
|
}
|
|
4797
4854
|
if (whaleEl !== null) whaleEl.style.display = "none";
|
|
4855
|
+
whaleShown = false;
|
|
4798
4856
|
setWhaleTimer(false);
|
|
4799
4857
|
if (whaleObserver !== null) {
|
|
4800
4858
|
whaleObserver.disconnect();
|
|
@@ -4857,7 +4915,7 @@ window.__ModuleLoader__.load({
|
|
|
4857
4915
|
}
|
|
4858
4916
|
function syncCompactPicker() {
|
|
4859
4917
|
if (!active) return;
|
|
4860
|
-
const tools =
|
|
4918
|
+
const tools = nodeOf("[class$=\"_composerSeat\"] [class$=\"_tools\"]");
|
|
4861
4919
|
const trigger = tools?.parentElement?.querySelector("[class$=\"_triggerEffort\"]")?.parentElement;
|
|
4862
4920
|
if (tools === null || trigger === null) {
|
|
4863
4921
|
removeCompactPicker();
|
|
@@ -5011,17 +5069,23 @@ window.__ModuleLoader__.load({
|
|
|
5011
5069
|
document.body.appendChild(whale);
|
|
5012
5070
|
}
|
|
5013
5071
|
/**
|
|
5072
|
+
* Write one row's recorded official draggable state back onto it.
|
|
5073
|
+
* @param row - the tracked row element.
|
|
5074
|
+
* @param original - the recorded attribute value, or null when it was absent.
|
|
5075
|
+
*/
|
|
5076
|
+
function restoreRowDragState(row, original) {
|
|
5077
|
+
if (original === null) row.removeAttribute("draggable");
|
|
5078
|
+
else row.setAttribute("draggable", original);
|
|
5079
|
+
}
|
|
5080
|
+
/**
|
|
5014
5081
|
* Restore the official draggable state this layer overrode while active.
|
|
5015
5082
|
* Rows are React-owned and may have been re-created meanwhile, so only the
|
|
5016
|
-
* tracked elements are touched; a detached element is
|
|
5083
|
+
* tracked elements are touched; a detached element is written back too
|
|
5084
|
+
* (harmless, and it keeps the entry prunable at every tick).
|
|
5017
5085
|
*/
|
|
5018
5086
|
function restoreRowDrag() {
|
|
5019
5087
|
if (dragOverridden.size === 0) return;
|
|
5020
|
-
for (const [row, original] of dragOverridden)
|
|
5021
|
-
if (!row.isConnected) continue;
|
|
5022
|
-
if (original === null) row.removeAttribute("draggable");
|
|
5023
|
-
else row.setAttribute("draggable", original);
|
|
5024
|
-
}
|
|
5088
|
+
for (const [row, original] of dragOverridden) restoreRowDragState(row, original);
|
|
5025
5089
|
dragOverridden.clear();
|
|
5026
5090
|
}
|
|
5027
5091
|
/** Restore a dragged position when the whale becomes visible again. */
|
|
@@ -5047,6 +5111,34 @@ window.__ModuleLoader__.load({
|
|
|
5047
5111
|
if (!dragOverridden.has(row)) dragOverridden.set(row, row.getAttribute("draggable"));
|
|
5048
5112
|
row.setAttribute("draggable", "false");
|
|
5049
5113
|
}
|
|
5114
|
+
if (dragOverridden.size === 0) return;
|
|
5115
|
+
for (const [row, original] of dragOverridden) {
|
|
5116
|
+
if (row.isConnected) continue;
|
|
5117
|
+
restoreRowDragState(row, original);
|
|
5118
|
+
dragOverridden.delete(row);
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
/**
|
|
5122
|
+
* Official nodes resolved through the document and cached while they stay
|
|
5123
|
+
* connected. The sync tick runs every 600ms for the page lifetime, and these
|
|
5124
|
+
* selectors (suffix class matches, :has()) cannot use Blink's fast paths, so
|
|
5125
|
+
* every miss walks the whole mounted DOM — the official chat keeps the whole
|
|
5126
|
+
* conversation mounted, i.e. tens of thousands of elements. React replaces a
|
|
5127
|
+
* node on a major re-render, which the isConnected guard detects.
|
|
5128
|
+
*/
|
|
5129
|
+
const nodeCache = /* @__PURE__ */ new Map();
|
|
5130
|
+
function nodeOf(selector) {
|
|
5131
|
+
const cached = nodeCache.get(selector);
|
|
5132
|
+
if (cached !== void 0 && cached.isConnected) return cached;
|
|
5133
|
+
const found = document.querySelector(selector);
|
|
5134
|
+
if (found === null) nodeCache.delete(selector);
|
|
5135
|
+
else nodeCache.set(selector, found);
|
|
5136
|
+
return found;
|
|
5137
|
+
}
|
|
5138
|
+
/** The official application frame, through the same cached lookup. */
|
|
5139
|
+
function frameEl() {
|
|
5140
|
+
const frame = nodeOf(APP_FRAME_SELECTOR);
|
|
5141
|
+
return frame instanceof HTMLElement ? frame : null;
|
|
5050
5142
|
}
|
|
5051
5143
|
function syncWhale() {
|
|
5052
5144
|
if (active) ensureAdaptStyle();
|
|
@@ -5055,8 +5147,8 @@ window.__ModuleLoader__.load({
|
|
|
5055
5147
|
whaleEl.style.display = "none";
|
|
5056
5148
|
return;
|
|
5057
5149
|
}
|
|
5058
|
-
const collapsed =
|
|
5059
|
-
const overlayUp =
|
|
5150
|
+
const collapsed = frameEl()?.hasAttribute("data-sidebar-collapsed") === true;
|
|
5151
|
+
const overlayUp = nodeOf("[class$=\"_overlay\"]") !== null;
|
|
5060
5152
|
const show = collapsed && !overlayUp;
|
|
5061
5153
|
if (show && !whaleShown) applyWhalePos();
|
|
5062
5154
|
whaleEl.style.display = show ? "" : "none";
|
|
@@ -5072,7 +5164,7 @@ window.__ModuleLoader__.load({
|
|
|
5072
5164
|
}
|
|
5073
5165
|
function seatHeaderActions() {
|
|
5074
5166
|
if (!active) return;
|
|
5075
|
-
const header =
|
|
5167
|
+
const header = nodeOf("[class$=\"_header\"]");
|
|
5076
5168
|
const tabs = header !== null ? header.querySelector("[class$=\"_tabs\"]") : null;
|
|
5077
5169
|
const actions = header !== null ? header.querySelector("[class$=\"_titleCluster\"] [class$=\"_headerActions\"]") : null;
|
|
5078
5170
|
const seated = header !== null && tabs !== null && actions !== null;
|
|
@@ -5081,7 +5173,7 @@ window.__ModuleLoader__.load({
|
|
|
5081
5173
|
}
|
|
5082
5174
|
function alignActionsText() {
|
|
5083
5175
|
if (!active) return;
|
|
5084
|
-
const header =
|
|
5176
|
+
const header = nodeOf("[class$=\"_header\"]");
|
|
5085
5177
|
if (header === null) return;
|
|
5086
5178
|
const tabs = header.querySelector("[class$=\"_tabs\"]");
|
|
5087
5179
|
const actions = header.querySelector("[class$=\"_titleCluster\"] [class$=\"_headerActions\"]");
|
|
@@ -5188,6 +5280,7 @@ window.__ModuleLoader__.load({
|
|
|
5188
5280
|
if (isMobilePortrait()) {
|
|
5189
5281
|
apply();
|
|
5190
5282
|
ensureWhaleObserver();
|
|
5283
|
+
if (active && drag === null) applyWhalePos();
|
|
5191
5284
|
} else revert();
|
|
5192
5285
|
}
|
|
5193
5286
|
evaluate();
|
|
@@ -5255,7 +5348,7 @@ window.__ModuleLoader__.load({
|
|
|
5255
5348
|
}
|
|
5256
5349
|
const t = e.target;
|
|
5257
5350
|
const editable = t instanceof HTMLElement && t.isContentEditable;
|
|
5258
|
-
if (t instanceof Element && (t.tagName === "TEXTAREA" || t.tagName === "INPUT" || editable || t.closest(`#${WHALE_ID}`) !== null ||
|
|
5351
|
+
if (t instanceof Element && (t.tagName === "TEXTAREA" || t.tagName === "INPUT" || editable || t.closest(`#${WHALE_ID}`) !== null || t.closest("table, [class$=\"_table\"], [class$=\"_tablePane\"]") !== null)) {
|
|
5259
5352
|
swipeTouch = null;
|
|
5260
5353
|
return;
|
|
5261
5354
|
}
|
|
@@ -5264,7 +5357,8 @@ window.__ModuleLoader__.load({
|
|
|
5264
5357
|
swipeTouch = {
|
|
5265
5358
|
x: ct.clientX,
|
|
5266
5359
|
y: ct.clientY,
|
|
5267
|
-
id: ct.identifier
|
|
5360
|
+
id: ct.identifier,
|
|
5361
|
+
el: t instanceof Element ? t : null
|
|
5268
5362
|
};
|
|
5269
5363
|
}, {
|
|
5270
5364
|
capture: true,
|
|
@@ -5277,12 +5371,14 @@ window.__ModuleLoader__.load({
|
|
|
5277
5371
|
if (ct === void 0 || ct.identifier !== swipeTouch.id) return;
|
|
5278
5372
|
const dx = ct.clientX - swipeTouch.x;
|
|
5279
5373
|
const dy = ct.clientY - swipeTouch.y;
|
|
5374
|
+
const start = swipeTouch;
|
|
5280
5375
|
swipeTouch = null;
|
|
5281
5376
|
if (!active) return;
|
|
5282
5377
|
if (Math.abs(dx) < 60 || Math.abs(dx) < Math.abs(dy) * 1.5) return;
|
|
5283
|
-
|
|
5378
|
+
if (start.el !== null && insideHScrollable(start.el)) return;
|
|
5379
|
+
const frame = frameEl();
|
|
5284
5380
|
if (frame === null) return;
|
|
5285
|
-
if (
|
|
5381
|
+
if (nodeOf("[class$=\"_overlay\"], [class$=\"_dialog\"], [class$=\"_menu\"], [class*=\"_portal\"]") !== null) return;
|
|
5286
5382
|
const collapsed = frame.hasAttribute("data-sidebar-collapsed");
|
|
5287
5383
|
if (dx < 0 && !collapsed) collapseSidebar();
|
|
5288
5384
|
else if (dx > 0 && collapsed) toggleSidebarVerified();
|
|
@@ -5414,10 +5510,23 @@ window.__ModuleLoader__.load({
|
|
|
5414
5510
|
return isComposerField(el) ? el : null;
|
|
5415
5511
|
};
|
|
5416
5512
|
const lanOrigFocus = HTMLElement.prototype.focus;
|
|
5417
|
-
|
|
5513
|
+
const patchedFocus = function(options) {
|
|
5418
5514
|
if (active && isComposerField(this) && Date.now() - lastComposerTap >= 800) return;
|
|
5419
5515
|
lanOrigFocus.call(this, options);
|
|
5420
5516
|
};
|
|
5517
|
+
/** (Re-)install the composer-focus guard (see setEnabled). */
|
|
5518
|
+
const installFocusPatch = () => {
|
|
5519
|
+
HTMLElement.prototype.focus = patchedFocus;
|
|
5520
|
+
};
|
|
5521
|
+
/**
|
|
5522
|
+
* Remove the guard when the layer is disabled. A patch another plugin
|
|
5523
|
+
* installed after ours is left alone (identity check), so disabling this
|
|
5524
|
+
* layer never removes someone else's behavior.
|
|
5525
|
+
*/
|
|
5526
|
+
const restoreFocusPatch = () => {
|
|
5527
|
+
if (HTMLElement.prototype.focus === patchedFocus) HTMLElement.prototype.focus = lanOrigFocus;
|
|
5528
|
+
};
|
|
5529
|
+
installFocusPatch();
|
|
5421
5530
|
document.addEventListener("pointerdown", (e) => {
|
|
5422
5531
|
if (!active) return;
|
|
5423
5532
|
if (composerFieldOf(e.target) !== null) lastComposerTap = Date.now();
|
|
@@ -5429,8 +5538,13 @@ window.__ModuleLoader__.load({
|
|
|
5429
5538
|
translate: null,
|
|
5430
5539
|
setEnabled(on) {
|
|
5431
5540
|
adaptEnabled = on;
|
|
5432
|
-
if (on)
|
|
5433
|
-
|
|
5541
|
+
if (on) {
|
|
5542
|
+
installFocusPatch();
|
|
5543
|
+
evaluate();
|
|
5544
|
+
} else {
|
|
5545
|
+
revert();
|
|
5546
|
+
restoreFocusPatch();
|
|
5547
|
+
}
|
|
5434
5548
|
},
|
|
5435
5549
|
flushCloseDetails() {
|
|
5436
5550
|
if (active) w.__dshRemoteAdapt?.closeDetails?.();
|
|
@@ -5447,22 +5561,24 @@ window.__ModuleLoader__.load({
|
|
|
5447
5561
|
*
|
|
5448
5562
|
* - `web-ui.plugin.item` — the list seat declared by the dsh-web-settings
|
|
5449
5563
|
* group section (this family's own first-level "Web UI plugins" section);
|
|
5450
|
-
* - `
|
|
5451
|
-
*
|
|
5564
|
+
* - `plugins.bundle.config` — the official keyed seat of the harness's plugin
|
|
5565
|
+
* manager page, keyed by the bundle's package name and rendered on that
|
|
5566
|
+
* bundle's page. alpha.2 removed the `settings.plugin.item` keyed seat of the
|
|
5567
|
+
* `ui-settings-plugins` tab that this helper used before, so a card keyed by
|
|
5568
|
+
* its settings namespace has no seat to land in any more.
|
|
5452
5569
|
*
|
|
5453
|
-
* SEAT SELECTION IS NOT A DECLARATION PROBE. The official
|
|
5454
|
-
*
|
|
5455
|
-
*
|
|
5456
|
-
*
|
|
5457
|
-
*
|
|
5458
|
-
*
|
|
5459
|
-
*
|
|
5460
|
-
* heading and zero cards.
|
|
5570
|
+
* SEAT SELECTION IS NOT A DECLARATION PROBE. The official plugin surface
|
|
5571
|
+
* belongs to the harness bundle and declares its seats before any external
|
|
5572
|
+
* plugin's `apply()` runs, so "is the official seat declared?" answers yes even
|
|
5573
|
+
* in the deployment whose whole point is the family group. Choosing on that
|
|
5574
|
+
* probe sends every family card to the official page and leaves the group's own
|
|
5575
|
+
* section permanently empty — the family of reports where the section renders
|
|
5576
|
+
* its heading and zero cards.
|
|
5461
5577
|
*
|
|
5462
5578
|
* The signal that actually distinguishes the two deployments is whether
|
|
5463
5579
|
* dsh-web-settings is loaded: it is the package that owns the group section and
|
|
5464
5580
|
* it publishes the `webUiSettings` service during `apply()`, which every
|
|
5465
|
-
* family plugin already reads for its settings
|
|
5581
|
+
* family plugin already reads for its settings form. Group loaded -> the family
|
|
5466
5582
|
* seat; group absent -> the official seat.
|
|
5467
5583
|
*
|
|
5468
5584
|
* The decision is re-evaluated on every `slots/changed` because the group may
|
|
@@ -5478,8 +5594,8 @@ window.__ModuleLoader__.load({
|
|
|
5478
5594
|
*/
|
|
5479
5595
|
/** The family list seat key. */
|
|
5480
5596
|
const FAMILY_PLUGIN_CARD_SEAT = "web-ui.plugin.item";
|
|
5481
|
-
/** The official keyed plugin-card seat key. */
|
|
5482
|
-
const OFFICIAL_PLUGIN_CARD_SEAT = "
|
|
5597
|
+
/** The official keyed plugin-card seat key (the alpha.2 bundle-configuration seat). */
|
|
5598
|
+
const OFFICIAL_PLUGIN_CARD_SEAT = "plugins.bundle.config";
|
|
5483
5599
|
/** The service dsh-web-settings publishes while it is loaded. */
|
|
5484
5600
|
const FAMILY_GROUP_SERVICE = "webUiSettings";
|
|
5485
5601
|
/**
|
|
@@ -5542,7 +5658,7 @@ window.__ModuleLoader__.load({
|
|
|
5542
5658
|
...seat.inject === void 0 ? {} : { inject }
|
|
5543
5659
|
} : {
|
|
5544
5660
|
name: OFFICIAL_PLUGIN_CARD_SEAT,
|
|
5545
|
-
key: seat.
|
|
5661
|
+
key: seat.bundle,
|
|
5546
5662
|
locale: seat.locale,
|
|
5547
5663
|
...seat.inject === void 0 ? {} : { inject }
|
|
5548
5664
|
}, component);
|
|
@@ -5575,6 +5691,21 @@ window.__ModuleLoader__.load({
|
|
|
5575
5691
|
const NS = "remote";
|
|
5576
5692
|
/** Settings namespace the remote-control card edits (the Host plugin registers it). */
|
|
5577
5693
|
const REMOTE_WEB_UI_NS = "remote-web-ui";
|
|
5694
|
+
const REMOTE_WEB_UI_ENTRY_IDS = [
|
|
5695
|
+
"web-ui-remote-web-ui",
|
|
5696
|
+
"ui-remote-web-ui",
|
|
5697
|
+
REMOTE_WEB_UI_NS
|
|
5698
|
+
];
|
|
5699
|
+
function servedEntryId(forms) {
|
|
5700
|
+
let served;
|
|
5701
|
+
try {
|
|
5702
|
+
served = forms.describe().getSnapshot().view?.namespaces.map((view) => view.ns);
|
|
5703
|
+
} catch {
|
|
5704
|
+
served = void 0;
|
|
5705
|
+
}
|
|
5706
|
+
if (!served || served.length === 0) return REMOTE_WEB_UI_NS;
|
|
5707
|
+
return REMOTE_WEB_UI_ENTRY_IDS.find((id) => served.includes(id)) ?? REMOTE_WEB_UI_NS;
|
|
5708
|
+
}
|
|
5578
5709
|
/** Heartbeat cadence from a paired phone (presence + revocation liveness). */
|
|
5579
5710
|
const HEARTBEAT_INTERVAL_MS = 1e4;
|
|
5580
5711
|
/** Services required by this plugin. */
|
|
@@ -5582,7 +5713,7 @@ window.__ModuleLoader__.load({
|
|
|
5582
5713
|
"slots",
|
|
5583
5714
|
"locale",
|
|
5584
5715
|
"connection",
|
|
5585
|
-
"
|
|
5716
|
+
"configForms",
|
|
5586
5717
|
"remote"
|
|
5587
5718
|
];
|
|
5588
5719
|
/**
|
|
@@ -5625,15 +5756,16 @@ window.__ModuleLoader__.load({
|
|
|
5625
5756
|
};
|
|
5626
5757
|
const t = ctx.locale.bind(NS);
|
|
5627
5758
|
if (adapt !== void 0) adapt.translate = t;
|
|
5628
|
-
const
|
|
5759
|
+
const family = ctx.get("webUiSettings");
|
|
5760
|
+
const settingsForm = family !== void 0 && typeof family.bind === "function" ? family.bind({ namespace: REMOTE_WEB_UI_NS }) : ctx.configForms.get(servedEntryId(ctx.configForms));
|
|
5629
5761
|
const enabled = () => {
|
|
5630
|
-
const snapshot =
|
|
5762
|
+
const snapshot = settingsForm.getSnapshot();
|
|
5631
5763
|
return snapshot.status === "ready" ? snapshot.value?.enabled ?? true : snapshot.status === "unavailable";
|
|
5632
5764
|
};
|
|
5633
5765
|
const syncAdaptEnabled = () => {
|
|
5634
5766
|
window.__dshRemoteAdapt?.setEnabled?.(enabled());
|
|
5635
5767
|
};
|
|
5636
|
-
|
|
5768
|
+
settingsForm.subscribe(syncAdaptEnabled);
|
|
5637
5769
|
syncAdaptEnabled();
|
|
5638
5770
|
try {
|
|
5639
5771
|
adapt?.flushCloseDetails?.();
|
|
@@ -5653,16 +5785,16 @@ window.__ModuleLoader__.load({
|
|
|
5653
5785
|
disposeEntry = void 0;
|
|
5654
5786
|
}
|
|
5655
5787
|
};
|
|
5656
|
-
const unsubscribe =
|
|
5788
|
+
const unsubscribe = settingsForm.subscribe(syncEntry);
|
|
5657
5789
|
syncEntry();
|
|
5658
5790
|
return () => {
|
|
5659
5791
|
unsubscribe();
|
|
5660
5792
|
disposeEntry?.();
|
|
5661
5793
|
};
|
|
5662
5794
|
});
|
|
5663
|
-
const remoteSettings = new RemoteSettingsCardController(
|
|
5795
|
+
const remoteSettings = new RemoteSettingsCardController(settingsForm);
|
|
5664
5796
|
installPluginCard(ctx, {
|
|
5665
|
-
|
|
5797
|
+
bundle: "@linxin666/dsh-remote-web-ui",
|
|
5666
5798
|
id: "remote-web-ui",
|
|
5667
5799
|
order: 90,
|
|
5668
5800
|
locale: NS,
|
|
@@ -5679,7 +5811,9 @@ window.__ModuleLoader__.load({
|
|
|
5679
5811
|
runPairBootFlow(ctx, window.location.search);
|
|
5680
5812
|
if (loopback) return () => {};
|
|
5681
5813
|
const timer = window.setInterval(() => {
|
|
5682
|
-
sendHeartbeat().
|
|
5814
|
+
sendHeartbeat().then((status) => {
|
|
5815
|
+
if (shouldStopHeartbeat(status)) window.clearInterval(timer);
|
|
5816
|
+
}).catch(() => {});
|
|
5683
5817
|
}, HEARTBEAT_INTERVAL_MS);
|
|
5684
5818
|
return () => {
|
|
5685
5819
|
window.clearInterval(timer);
|
|
@@ -5690,7 +5824,7 @@ window.__ModuleLoader__.load({
|
|
|
5690
5824
|
disposeRuntime = void 0;
|
|
5691
5825
|
}
|
|
5692
5826
|
};
|
|
5693
|
-
|
|
5827
|
+
settingsForm.subscribe(syncRuntime);
|
|
5694
5828
|
syncRuntime();
|
|
5695
5829
|
let disposeChannel;
|
|
5696
5830
|
let hostPairingPolicy;
|
|
@@ -5720,13 +5854,13 @@ window.__ModuleLoader__.load({
|
|
|
5720
5854
|
fenceNotice = void 0;
|
|
5721
5855
|
};
|
|
5722
5856
|
const handleUnpaired = () => {
|
|
5723
|
-
if (
|
|
5857
|
+
if (settingsForm.getSnapshot().status !== "ready" && hostPairingPolicy === void 0) {
|
|
5724
5858
|
unpairedWhilePolicyPending = true;
|
|
5725
5859
|
return;
|
|
5726
5860
|
}
|
|
5727
5861
|
showFenceNotice();
|
|
5728
5862
|
};
|
|
5729
|
-
const channelActive = () => remoteChannelRequired(window.location.hostname,
|
|
5863
|
+
const channelActive = () => remoteChannelRequired(window.location.hostname, settingsForm.getSnapshot(), hostPairingPolicy);
|
|
5730
5864
|
const bootSeat = () => window[REMOTE_CHANNEL_BOOT_GLOBAL];
|
|
5731
5865
|
const syncChannel = () => {
|
|
5732
5866
|
const transition = channelTransition(channelActive(), disposeChannel !== void 0);
|
|
@@ -5756,9 +5890,9 @@ window.__ModuleLoader__.load({
|
|
|
5756
5890
|
hideFenceNotice();
|
|
5757
5891
|
} else if (transition === "none" && !channelActive()) bootSeat()?.restore();
|
|
5758
5892
|
};
|
|
5759
|
-
|
|
5893
|
+
settingsForm.subscribe(syncChannel);
|
|
5760
5894
|
syncChannel();
|
|
5761
|
-
if (!isLoopbackHostname(window.location.hostname) &&
|
|
5895
|
+
if (!isLoopbackHostname(window.location.hostname) && settingsForm.getSnapshot().status !== "ready") readPairGatePolicy().then((policy) => {
|
|
5762
5896
|
hostPairingPolicy = policy.requirePairingForLan;
|
|
5763
5897
|
syncChannel();
|
|
5764
5898
|
if (hostPairingPolicy && unpairedWhilePolicyPending) showFenceNotice();
|