@linxin666/dsh-remote-web-ui 0.3.23 → 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 +396 -132
- package/lib/client.js.map +1 -1
- package/lib/index.js +604 -262
- 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 +56 -32
- 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 +143 -28
- 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 +104 -0
- package/lib/types/client/plugin-card-seat.d.ts.map +1 -0
- package/lib/types/client/plugin-card-seat.js +143 -0
- 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/console-output.d.ts +41 -0
- package/lib/types/console-output.d.ts.map +1 -0
- package/lib/types/console-output.js +89 -0
- 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 -33
- 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 +70 -40
- package/src/client/locales.ts +6 -8
- package/src/client/mobile-adapt.ts +136 -25
- package/src/client/pair-api.ts +47 -7
- package/src/client/plugin-card-seat.ts +192 -0
- 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/console-output.ts +97 -0
- 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 -29
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
|
}
|
|
@@ -4580,6 +4637,12 @@ window.__ModuleLoader__.load({
|
|
|
4580
4637
|
const COMPACT_CLASS = "dsh-remote-compact-picker";
|
|
4581
4638
|
/** Body class while the header actions are seated in the tabs row. */
|
|
4582
4639
|
const HEADER_SEATED_CLASS = "dsh-remote-header-seated";
|
|
4640
|
+
/**
|
|
4641
|
+
* Width the seated header actions paint over the tabs row, as a CSS variable
|
|
4642
|
+
* on <html>. The row caps its own width with it (see the v80 rules) instead of
|
|
4643
|
+
* reserving the space with padding.
|
|
4644
|
+
*/
|
|
4645
|
+
const HEADER_RESERVE_VAR = "--dsh-remote-header-actions-reserve";
|
|
4583
4646
|
/** Locale-dependent fast path for the official picker cells (zh/en). */
|
|
4584
4647
|
const PICKER_CELL_PATTERN = {
|
|
4585
4648
|
model: /模型|Model/,
|
|
@@ -4678,6 +4741,9 @@ window.__ModuleLoader__.load({
|
|
|
4678
4741
|
"[class$=\"_composerSeat\"] [class$=\"_frame\"] [class$=\"_card\"]{max-width:none;width:100%}",
|
|
4679
4742
|
`body.${HEADER_SEATED_CLASS} [class$="_header"] [class$="_titleCluster"] [class$="_headerActions"]{position:absolute;left:0;top:0;margin:0;display:flex;align-items:center;gap:6px;flex:none;z-index:2}`,
|
|
4680
4743
|
"[class$=\"_header\"] [class$=\"_tabs\"] [class*=\"_tab\"]{font-size:12px;white-space:nowrap}",
|
|
4744
|
+
`body.${HEADER_SEATED_CLASS} [class$="_header"] [class$="_tabs"]{max-width:max(0px,calc(100% - var(${HEADER_RESERVE_VAR},0px)));overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:2px;margin-bottom:-2px}`,
|
|
4745
|
+
`body.${HEADER_SEATED_CLASS} [class$="_header"] [class$="_tabs"]::-webkit-scrollbar{display:none}`,
|
|
4746
|
+
`body.${HEADER_SEATED_CLASS} [class$="_header"] [class$="_tabs"] > [class*="_tab"]{flex:0 0 auto}`,
|
|
4681
4747
|
`body.${ACTIVE_CLASS} [class$=\"_detailsCol\"]{display:none !important}`,
|
|
4682
4748
|
`body.${ACTIVE_CLASS} [data-dsh-plugin=\"ssh\"],`,
|
|
4683
4749
|
`body.${ACTIVE_CLASS} [data-dsh-plugin=\"skill-explorer\"],`,
|
|
@@ -4746,7 +4812,7 @@ window.__ModuleLoader__.load({
|
|
|
4746
4812
|
* rail compaction) while the body class stays.
|
|
4747
4813
|
*/
|
|
4748
4814
|
function ensureAdaptStyle() {
|
|
4749
|
-
if (
|
|
4815
|
+
if (nodeOf(`style[data-plugin-css="${ADAPT_CSS_ID}"]`) !== null) return;
|
|
4750
4816
|
const tag = document.createElement("style");
|
|
4751
4817
|
tag.dataset.plugin = "remote-web-ui";
|
|
4752
4818
|
tag.dataset.pluginCss = ADAPT_CSS_ID;
|
|
@@ -4786,6 +4852,7 @@ window.__ModuleLoader__.load({
|
|
|
4786
4852
|
savedViewportContent = null;
|
|
4787
4853
|
}
|
|
4788
4854
|
if (whaleEl !== null) whaleEl.style.display = "none";
|
|
4855
|
+
whaleShown = false;
|
|
4789
4856
|
setWhaleTimer(false);
|
|
4790
4857
|
if (whaleObserver !== null) {
|
|
4791
4858
|
whaleObserver.disconnect();
|
|
@@ -4848,7 +4915,7 @@ window.__ModuleLoader__.load({
|
|
|
4848
4915
|
}
|
|
4849
4916
|
function syncCompactPicker() {
|
|
4850
4917
|
if (!active) return;
|
|
4851
|
-
const tools =
|
|
4918
|
+
const tools = nodeOf("[class$=\"_composerSeat\"] [class$=\"_tools\"]");
|
|
4852
4919
|
const trigger = tools?.parentElement?.querySelector("[class$=\"_triggerEffort\"]")?.parentElement;
|
|
4853
4920
|
if (tools === null || trigger === null) {
|
|
4854
4921
|
removeCompactPicker();
|
|
@@ -5002,17 +5069,23 @@ window.__ModuleLoader__.load({
|
|
|
5002
5069
|
document.body.appendChild(whale);
|
|
5003
5070
|
}
|
|
5004
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
|
+
/**
|
|
5005
5081
|
* Restore the official draggable state this layer overrode while active.
|
|
5006
5082
|
* Rows are React-owned and may have been re-created meanwhile, so only the
|
|
5007
|
-
* 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).
|
|
5008
5085
|
*/
|
|
5009
5086
|
function restoreRowDrag() {
|
|
5010
5087
|
if (dragOverridden.size === 0) return;
|
|
5011
|
-
for (const [row, original] of dragOverridden)
|
|
5012
|
-
if (!row.isConnected) continue;
|
|
5013
|
-
if (original === null) row.removeAttribute("draggable");
|
|
5014
|
-
else row.setAttribute("draggable", original);
|
|
5015
|
-
}
|
|
5088
|
+
for (const [row, original] of dragOverridden) restoreRowDragState(row, original);
|
|
5016
5089
|
dragOverridden.clear();
|
|
5017
5090
|
}
|
|
5018
5091
|
/** Restore a dragged position when the whale becomes visible again. */
|
|
@@ -5038,6 +5111,34 @@ window.__ModuleLoader__.load({
|
|
|
5038
5111
|
if (!dragOverridden.has(row)) dragOverridden.set(row, row.getAttribute("draggable"));
|
|
5039
5112
|
row.setAttribute("draggable", "false");
|
|
5040
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;
|
|
5041
5142
|
}
|
|
5042
5143
|
function syncWhale() {
|
|
5043
5144
|
if (active) ensureAdaptStyle();
|
|
@@ -5046,8 +5147,8 @@ window.__ModuleLoader__.load({
|
|
|
5046
5147
|
whaleEl.style.display = "none";
|
|
5047
5148
|
return;
|
|
5048
5149
|
}
|
|
5049
|
-
const collapsed =
|
|
5050
|
-
const overlayUp =
|
|
5150
|
+
const collapsed = frameEl()?.hasAttribute("data-sidebar-collapsed") === true;
|
|
5151
|
+
const overlayUp = nodeOf("[class$=\"_overlay\"]") !== null;
|
|
5051
5152
|
const show = collapsed && !overlayUp;
|
|
5052
5153
|
if (show && !whaleShown) applyWhalePos();
|
|
5053
5154
|
whaleEl.style.display = show ? "" : "none";
|
|
@@ -5063,7 +5164,7 @@ window.__ModuleLoader__.load({
|
|
|
5063
5164
|
}
|
|
5064
5165
|
function seatHeaderActions() {
|
|
5065
5166
|
if (!active) return;
|
|
5066
|
-
const header =
|
|
5167
|
+
const header = nodeOf("[class$=\"_header\"]");
|
|
5067
5168
|
const tabs = header !== null ? header.querySelector("[class$=\"_tabs\"]") : null;
|
|
5068
5169
|
const actions = header !== null ? header.querySelector("[class$=\"_titleCluster\"] [class$=\"_headerActions\"]") : null;
|
|
5069
5170
|
const seated = header !== null && tabs !== null && actions !== null;
|
|
@@ -5072,7 +5173,7 @@ window.__ModuleLoader__.load({
|
|
|
5072
5173
|
}
|
|
5073
5174
|
function alignActionsText() {
|
|
5074
5175
|
if (!active) return;
|
|
5075
|
-
const header =
|
|
5176
|
+
const header = nodeOf("[class$=\"_header\"]");
|
|
5076
5177
|
if (header === null) return;
|
|
5077
5178
|
const tabs = header.querySelector("[class$=\"_tabs\"]");
|
|
5078
5179
|
const actions = header.querySelector("[class$=\"_titleCluster\"] [class$=\"_headerActions\"]");
|
|
@@ -5096,7 +5197,9 @@ window.__ModuleLoader__.load({
|
|
|
5096
5197
|
let dx = translate !== null ? parseFloat(translate[1] ?? "0") : 0;
|
|
5097
5198
|
let dy = translate !== null ? parseFloat(translate[2] ?? "0") : 0;
|
|
5098
5199
|
const actionsRect = actions.getBoundingClientRect();
|
|
5099
|
-
const
|
|
5200
|
+
const headerRect = header.getBoundingClientRect();
|
|
5201
|
+
const headerStyle = getComputedStyle(header);
|
|
5202
|
+
const rightDiff = headerRect.right - (parseFloat(headerStyle.paddingRight) || 0) - (parseFloat(headerStyle.borderRightWidth) || 0) - actionsRect.right;
|
|
5100
5203
|
if (Math.abs(rightDiff) >= .5) dx = Math.round((dx + rightDiff) * 10) / 10;
|
|
5101
5204
|
const tabBottom = textBottom(tabBtn);
|
|
5102
5205
|
if (tabBottom !== null) {
|
|
@@ -5113,10 +5216,12 @@ window.__ModuleLoader__.load({
|
|
|
5113
5216
|
const next = `translate(${dx}px, ${dy}px)`;
|
|
5114
5217
|
if (actions.style.transform !== next) actions.style.transform = next;
|
|
5115
5218
|
const reserve = `${Math.ceil(actionsRect.width) + 8}px`;
|
|
5116
|
-
|
|
5219
|
+
const rootStyle = document.documentElement.style;
|
|
5220
|
+
if (rootStyle.getPropertyValue(HEADER_RESERVE_VAR) !== reserve) rootStyle.setProperty(HEADER_RESERVE_VAR, reserve);
|
|
5117
5221
|
}
|
|
5118
5222
|
function unseatHeaderActions() {
|
|
5119
5223
|
document.body.classList.remove(HEADER_SEATED_CLASS);
|
|
5224
|
+
document.documentElement.style.removeProperty(HEADER_RESERVE_VAR);
|
|
5120
5225
|
const header = document.querySelector("[class$=\"_header\"]");
|
|
5121
5226
|
const tabs = header !== null ? header.querySelector("[class$=\"_tabs\"]") : null;
|
|
5122
5227
|
const actions = header !== null ? header.querySelector("[class$=\"_titleCluster\"] [class$=\"_headerActions\"]") : null;
|
|
@@ -5175,6 +5280,7 @@ window.__ModuleLoader__.load({
|
|
|
5175
5280
|
if (isMobilePortrait()) {
|
|
5176
5281
|
apply();
|
|
5177
5282
|
ensureWhaleObserver();
|
|
5283
|
+
if (active && drag === null) applyWhalePos();
|
|
5178
5284
|
} else revert();
|
|
5179
5285
|
}
|
|
5180
5286
|
evaluate();
|
|
@@ -5242,7 +5348,7 @@ window.__ModuleLoader__.load({
|
|
|
5242
5348
|
}
|
|
5243
5349
|
const t = e.target;
|
|
5244
5350
|
const editable = t instanceof HTMLElement && t.isContentEditable;
|
|
5245
|
-
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)) {
|
|
5246
5352
|
swipeTouch = null;
|
|
5247
5353
|
return;
|
|
5248
5354
|
}
|
|
@@ -5251,7 +5357,8 @@ window.__ModuleLoader__.load({
|
|
|
5251
5357
|
swipeTouch = {
|
|
5252
5358
|
x: ct.clientX,
|
|
5253
5359
|
y: ct.clientY,
|
|
5254
|
-
id: ct.identifier
|
|
5360
|
+
id: ct.identifier,
|
|
5361
|
+
el: t instanceof Element ? t : null
|
|
5255
5362
|
};
|
|
5256
5363
|
}, {
|
|
5257
5364
|
capture: true,
|
|
@@ -5264,12 +5371,14 @@ window.__ModuleLoader__.load({
|
|
|
5264
5371
|
if (ct === void 0 || ct.identifier !== swipeTouch.id) return;
|
|
5265
5372
|
const dx = ct.clientX - swipeTouch.x;
|
|
5266
5373
|
const dy = ct.clientY - swipeTouch.y;
|
|
5374
|
+
const start = swipeTouch;
|
|
5267
5375
|
swipeTouch = null;
|
|
5268
5376
|
if (!active) return;
|
|
5269
5377
|
if (Math.abs(dx) < 60 || Math.abs(dx) < Math.abs(dy) * 1.5) return;
|
|
5270
|
-
|
|
5378
|
+
if (start.el !== null && insideHScrollable(start.el)) return;
|
|
5379
|
+
const frame = frameEl();
|
|
5271
5380
|
if (frame === null) return;
|
|
5272
|
-
if (
|
|
5381
|
+
if (nodeOf("[class$=\"_overlay\"], [class$=\"_dialog\"], [class$=\"_menu\"], [class*=\"_portal\"]") !== null) return;
|
|
5273
5382
|
const collapsed = frame.hasAttribute("data-sidebar-collapsed");
|
|
5274
5383
|
if (dx < 0 && !collapsed) collapseSidebar();
|
|
5275
5384
|
else if (dx > 0 && collapsed) toggleSidebarVerified();
|
|
@@ -5401,10 +5510,23 @@ window.__ModuleLoader__.load({
|
|
|
5401
5510
|
return isComposerField(el) ? el : null;
|
|
5402
5511
|
};
|
|
5403
5512
|
const lanOrigFocus = HTMLElement.prototype.focus;
|
|
5404
|
-
|
|
5513
|
+
const patchedFocus = function(options) {
|
|
5405
5514
|
if (active && isComposerField(this) && Date.now() - lastComposerTap >= 800) return;
|
|
5406
5515
|
lanOrigFocus.call(this, options);
|
|
5407
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();
|
|
5408
5530
|
document.addEventListener("pointerdown", (e) => {
|
|
5409
5531
|
if (!active) return;
|
|
5410
5532
|
if (composerFieldOf(e.target) !== null) lastComposerTap = Date.now();
|
|
@@ -5416,8 +5538,13 @@ window.__ModuleLoader__.load({
|
|
|
5416
5538
|
translate: null,
|
|
5417
5539
|
setEnabled(on) {
|
|
5418
5540
|
adaptEnabled = on;
|
|
5419
|
-
if (on)
|
|
5420
|
-
|
|
5541
|
+
if (on) {
|
|
5542
|
+
installFocusPatch();
|
|
5543
|
+
evaluate();
|
|
5544
|
+
} else {
|
|
5545
|
+
revert();
|
|
5546
|
+
restoreFocusPatch();
|
|
5547
|
+
}
|
|
5421
5548
|
},
|
|
5422
5549
|
flushCloseDetails() {
|
|
5423
5550
|
if (active) w.__dshRemoteAdapt?.closeDetails?.();
|
|
@@ -5425,6 +5552,131 @@ window.__ModuleLoader__.load({
|
|
|
5425
5552
|
};
|
|
5426
5553
|
}
|
|
5427
5554
|
//#endregion
|
|
5555
|
+
//#region src/client/plugin-card-seat.ts
|
|
5556
|
+
/**
|
|
5557
|
+
* Family plugin-card seat.
|
|
5558
|
+
*
|
|
5559
|
+
* A family plugin contributes its settings card to whichever plugin-card seat
|
|
5560
|
+
* the running host actually renders:
|
|
5561
|
+
*
|
|
5562
|
+
* - `web-ui.plugin.item` — the list seat declared by the dsh-web-settings
|
|
5563
|
+
* group section (this family's own first-level "Web UI plugins" section);
|
|
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.
|
|
5569
|
+
*
|
|
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.
|
|
5577
|
+
*
|
|
5578
|
+
* The signal that actually distinguishes the two deployments is whether
|
|
5579
|
+
* dsh-web-settings is loaded: it is the package that owns the group section and
|
|
5580
|
+
* it publishes the `webUiSettings` service during `apply()`, which every
|
|
5581
|
+
* family plugin already reads for its settings form. Group loaded -> the family
|
|
5582
|
+
* seat; group absent -> the official seat.
|
|
5583
|
+
*
|
|
5584
|
+
* The decision is re-evaluated on every `slots/changed` because the group may
|
|
5585
|
+
* apply after this plugin (the family aggregate orders it first, a profile that
|
|
5586
|
+
* installs the group separately need not): the initial contribution goes to the
|
|
5587
|
+
* official seat, then moves to the family seat the moment the group's section
|
|
5588
|
+
* registers. The entry is disposed before the replacement is registered, so a
|
|
5589
|
+
* card is never in two seats at once.
|
|
5590
|
+
*
|
|
5591
|
+
* The shared tree has no client-SDK dependency, so this module reads its
|
|
5592
|
+
* context through the structural shape below; callers pass the plugin's own
|
|
5593
|
+
* `ctx`.
|
|
5594
|
+
*/
|
|
5595
|
+
/** The family list seat key. */
|
|
5596
|
+
const FAMILY_PLUGIN_CARD_SEAT = "web-ui.plugin.item";
|
|
5597
|
+
/** The official keyed plugin-card seat key (the alpha.2 bundle-configuration seat). */
|
|
5598
|
+
const OFFICIAL_PLUGIN_CARD_SEAT = "plugins.bundle.config";
|
|
5599
|
+
/** The service dsh-web-settings publishes while it is loaded. */
|
|
5600
|
+
const FAMILY_GROUP_SERVICE = "webUiSettings";
|
|
5601
|
+
/**
|
|
5602
|
+
* Whether the family group (dsh-web-settings) is loaded in this page. The
|
|
5603
|
+
* service is the group package's own contract, so the probe cannot be fooled
|
|
5604
|
+
* by a harness release that starts declaring the official seat differently.
|
|
5605
|
+
*/
|
|
5606
|
+
function familyGroupLoaded(ctx) {
|
|
5607
|
+
const get = ctx.get;
|
|
5608
|
+
if (typeof get !== "function") return false;
|
|
5609
|
+
try {
|
|
5610
|
+
return get.call(ctx, FAMILY_GROUP_SERVICE) !== void 0;
|
|
5611
|
+
} catch {
|
|
5612
|
+
return false;
|
|
5613
|
+
}
|
|
5614
|
+
}
|
|
5615
|
+
/** Report a refused registration instead of leaving the user with no card. */
|
|
5616
|
+
function warnRefusedSeat(seat, error) {
|
|
5617
|
+
try {
|
|
5618
|
+
console.warn(`[dsh-web] plugin card registration into "${seat}" was refused; the card will not render`, error);
|
|
5619
|
+
} catch {}
|
|
5620
|
+
}
|
|
5621
|
+
/**
|
|
5622
|
+
* Contribute one family plugin card to the seat this host renders, following
|
|
5623
|
+
* the group if it loads later. The entry is disposed and re-registered on a
|
|
5624
|
+
* seat change, never duplicated.
|
|
5625
|
+
* @param ctx - client context (its slot registry decides the seat).
|
|
5626
|
+
* @param seat - the card contribution.
|
|
5627
|
+
*/
|
|
5628
|
+
function installPluginCard(ctx, seat) {
|
|
5629
|
+
const slots = ctx.slots;
|
|
5630
|
+
const component = seat.component;
|
|
5631
|
+
const inject = seat.inject;
|
|
5632
|
+
let dispose;
|
|
5633
|
+
let current;
|
|
5634
|
+
/**
|
|
5635
|
+
* Re-entrancy latch. The registry emits a change event synchronously from
|
|
5636
|
+
* inside both `register` and the previous entry's disposer, so an unguarded
|
|
5637
|
+
* reconcile would re-enter itself mid-move and register the card twice into
|
|
5638
|
+
* the seat it is leaving ("already has an entry for key ...").
|
|
5639
|
+
*/
|
|
5640
|
+
let reconciling = false;
|
|
5641
|
+
/** Reconcile the contribution with the currently live seat (no-op when unchanged). */
|
|
5642
|
+
const reconcile = () => {
|
|
5643
|
+
if (reconciling) return;
|
|
5644
|
+
const target = familyGroupLoaded(ctx) ? FAMILY_PLUGIN_CARD_SEAT : OFFICIAL_PLUGIN_CARD_SEAT;
|
|
5645
|
+
if (current === target) return;
|
|
5646
|
+
reconciling = true;
|
|
5647
|
+
const previous = dispose;
|
|
5648
|
+
dispose = void 0;
|
|
5649
|
+
current = void 0;
|
|
5650
|
+
previous?.();
|
|
5651
|
+
try {
|
|
5652
|
+
dispose = slots.register(target === "web-ui.plugin.item" ? {
|
|
5653
|
+
name: FAMILY_PLUGIN_CARD_SEAT,
|
|
5654
|
+
id: seat.id,
|
|
5655
|
+
...seat.order === void 0 ? {} : { order: seat.order },
|
|
5656
|
+
...seat.label === void 0 ? {} : { label: seat.label },
|
|
5657
|
+
locale: seat.locale,
|
|
5658
|
+
...seat.inject === void 0 ? {} : { inject }
|
|
5659
|
+
} : {
|
|
5660
|
+
name: OFFICIAL_PLUGIN_CARD_SEAT,
|
|
5661
|
+
key: seat.bundle,
|
|
5662
|
+
locale: seat.locale,
|
|
5663
|
+
...seat.inject === void 0 ? {} : { inject }
|
|
5664
|
+
}, component);
|
|
5665
|
+
current = target;
|
|
5666
|
+
} catch (error) {
|
|
5667
|
+
warnRefusedSeat(target, error);
|
|
5668
|
+
} finally {
|
|
5669
|
+
reconciling = false;
|
|
5670
|
+
}
|
|
5671
|
+
};
|
|
5672
|
+
if (typeof ctx.on === "function") try {
|
|
5673
|
+
ctx.on("slots/changed", () => {
|
|
5674
|
+
reconcile();
|
|
5675
|
+
});
|
|
5676
|
+
} catch {}
|
|
5677
|
+
reconcile();
|
|
5678
|
+
}
|
|
5679
|
+
//#endregion
|
|
5428
5680
|
//#region src/client/index.ts
|
|
5429
5681
|
/**
|
|
5430
5682
|
* Remote control — browser half. Registers the `remote` dictionaries, the
|
|
@@ -5439,6 +5691,21 @@ window.__ModuleLoader__.load({
|
|
|
5439
5691
|
const NS = "remote";
|
|
5440
5692
|
/** Settings namespace the remote-control card edits (the Host plugin registers it). */
|
|
5441
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
|
+
}
|
|
5442
5709
|
/** Heartbeat cadence from a paired phone (presence + revocation liveness). */
|
|
5443
5710
|
const HEARTBEAT_INTERVAL_MS = 1e4;
|
|
5444
5711
|
/** Services required by this plugin. */
|
|
@@ -5446,7 +5713,7 @@ window.__ModuleLoader__.load({
|
|
|
5446
5713
|
"slots",
|
|
5447
5714
|
"locale",
|
|
5448
5715
|
"connection",
|
|
5449
|
-
"
|
|
5716
|
+
"configForms",
|
|
5450
5717
|
"remote"
|
|
5451
5718
|
];
|
|
5452
5719
|
/**
|
|
@@ -5489,15 +5756,16 @@ window.__ModuleLoader__.load({
|
|
|
5489
5756
|
};
|
|
5490
5757
|
const t = ctx.locale.bind(NS);
|
|
5491
5758
|
if (adapt !== void 0) adapt.translate = t;
|
|
5492
|
-
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));
|
|
5493
5761
|
const enabled = () => {
|
|
5494
|
-
const snapshot =
|
|
5762
|
+
const snapshot = settingsForm.getSnapshot();
|
|
5495
5763
|
return snapshot.status === "ready" ? snapshot.value?.enabled ?? true : snapshot.status === "unavailable";
|
|
5496
5764
|
};
|
|
5497
5765
|
const syncAdaptEnabled = () => {
|
|
5498
5766
|
window.__dshRemoteAdapt?.setEnabled?.(enabled());
|
|
5499
5767
|
};
|
|
5500
|
-
|
|
5768
|
+
settingsForm.subscribe(syncAdaptEnabled);
|
|
5501
5769
|
syncAdaptEnabled();
|
|
5502
5770
|
try {
|
|
5503
5771
|
adapt?.flushCloseDetails?.();
|
|
@@ -5517,31 +5785,25 @@ window.__ModuleLoader__.load({
|
|
|
5517
5785
|
disposeEntry = void 0;
|
|
5518
5786
|
}
|
|
5519
5787
|
};
|
|
5520
|
-
const unsubscribe =
|
|
5788
|
+
const unsubscribe = settingsForm.subscribe(syncEntry);
|
|
5521
5789
|
syncEntry();
|
|
5522
5790
|
return () => {
|
|
5523
5791
|
unsubscribe();
|
|
5524
5792
|
disposeEntry?.();
|
|
5525
5793
|
};
|
|
5526
5794
|
});
|
|
5527
|
-
const remoteSettings = new RemoteSettingsCardController(
|
|
5528
|
-
ctx
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
inject: () => remoteSettings.inject()
|
|
5536
|
-
}, RemoteSettingsCard);
|
|
5537
|
-
return () => {
|
|
5538
|
-
remoteSettings.dispose();
|
|
5539
|
-
unregister();
|
|
5540
|
-
};
|
|
5541
|
-
} catch {
|
|
5542
|
-
return () => {};
|
|
5543
|
-
}
|
|
5795
|
+
const remoteSettings = new RemoteSettingsCardController(settingsForm);
|
|
5796
|
+
installPluginCard(ctx, {
|
|
5797
|
+
bundle: "@linxin666/dsh-remote-web-ui",
|
|
5798
|
+
id: "remote-web-ui",
|
|
5799
|
+
order: 90,
|
|
5800
|
+
locale: NS,
|
|
5801
|
+
inject: () => remoteSettings.inject(),
|
|
5802
|
+
component: RemoteSettingsCard
|
|
5544
5803
|
});
|
|
5804
|
+
ctx.effect(() => () => {
|
|
5805
|
+
remoteSettings.dispose();
|
|
5806
|
+
}, "remote-web-ui: settings card");
|
|
5545
5807
|
let disposeRuntime;
|
|
5546
5808
|
const syncRuntime = () => {
|
|
5547
5809
|
if (enabled() && disposeRuntime === void 0) disposeRuntime = ctx.effect(() => {
|
|
@@ -5549,7 +5811,9 @@ window.__ModuleLoader__.load({
|
|
|
5549
5811
|
runPairBootFlow(ctx, window.location.search);
|
|
5550
5812
|
if (loopback) return () => {};
|
|
5551
5813
|
const timer = window.setInterval(() => {
|
|
5552
|
-
sendHeartbeat().
|
|
5814
|
+
sendHeartbeat().then((status) => {
|
|
5815
|
+
if (shouldStopHeartbeat(status)) window.clearInterval(timer);
|
|
5816
|
+
}).catch(() => {});
|
|
5553
5817
|
}, HEARTBEAT_INTERVAL_MS);
|
|
5554
5818
|
return () => {
|
|
5555
5819
|
window.clearInterval(timer);
|
|
@@ -5560,7 +5824,7 @@ window.__ModuleLoader__.load({
|
|
|
5560
5824
|
disposeRuntime = void 0;
|
|
5561
5825
|
}
|
|
5562
5826
|
};
|
|
5563
|
-
|
|
5827
|
+
settingsForm.subscribe(syncRuntime);
|
|
5564
5828
|
syncRuntime();
|
|
5565
5829
|
let disposeChannel;
|
|
5566
5830
|
let hostPairingPolicy;
|
|
@@ -5590,13 +5854,13 @@ window.__ModuleLoader__.load({
|
|
|
5590
5854
|
fenceNotice = void 0;
|
|
5591
5855
|
};
|
|
5592
5856
|
const handleUnpaired = () => {
|
|
5593
|
-
if (
|
|
5857
|
+
if (settingsForm.getSnapshot().status !== "ready" && hostPairingPolicy === void 0) {
|
|
5594
5858
|
unpairedWhilePolicyPending = true;
|
|
5595
5859
|
return;
|
|
5596
5860
|
}
|
|
5597
5861
|
showFenceNotice();
|
|
5598
5862
|
};
|
|
5599
|
-
const channelActive = () => remoteChannelRequired(window.location.hostname,
|
|
5863
|
+
const channelActive = () => remoteChannelRequired(window.location.hostname, settingsForm.getSnapshot(), hostPairingPolicy);
|
|
5600
5864
|
const bootSeat = () => window[REMOTE_CHANNEL_BOOT_GLOBAL];
|
|
5601
5865
|
const syncChannel = () => {
|
|
5602
5866
|
const transition = channelTransition(channelActive(), disposeChannel !== void 0);
|
|
@@ -5626,9 +5890,9 @@ window.__ModuleLoader__.load({
|
|
|
5626
5890
|
hideFenceNotice();
|
|
5627
5891
|
} else if (transition === "none" && !channelActive()) bootSeat()?.restore();
|
|
5628
5892
|
};
|
|
5629
|
-
|
|
5893
|
+
settingsForm.subscribe(syncChannel);
|
|
5630
5894
|
syncChannel();
|
|
5631
|
-
if (!isLoopbackHostname(window.location.hostname) &&
|
|
5895
|
+
if (!isLoopbackHostname(window.location.hostname) && settingsForm.getSnapshot().status !== "ready") readPairGatePolicy().then((policy) => {
|
|
5632
5896
|
hostPairingPolicy = policy.requirePairingForLan;
|
|
5633
5897
|
syncChannel();
|
|
5634
5898
|
if (hostPairingPolicy && unpairedWhilePolicyPending) showFenceNotice();
|