dsh-mobile 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/CONTRIBUTORS.md +15 -3
- package/README.en.md +7 -3
- package/README.md +7 -3
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/docs/ATTACH_EXISTING_FRPS.en.md +2 -0
- package/docs/ATTACH_EXISTING_FRPS.md +2 -0
- package/docs/CONNECTION_DIAGNOSTICS.en.md +9 -1
- package/docs/CONNECTION_DIAGNOSTICS.md +9 -1
- package/docs/IROH.en.md +1 -1
- package/docs/IROH.md +1 -1
- package/docs/MOBILE_PLUGIN_ADAPTATION.en.md +1 -1
- package/docs/MOBILE_PLUGIN_ADAPTATION.md +1 -1
- package/docs/SLOW_CONNECTIONS.en.md +3 -1
- package/docs/SLOW_CONNECTIONS.md +3 -1
- package/docs/TRUSTED_NETWORKS.en.md +6 -0
- package/docs/TRUSTED_NETWORKS.md +6 -0
- package/lib/client.js +515 -134
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +8 -0
- package/lib/index.mjs +56 -33
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +115 -29
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +2 -2
- package/packages/question-fixes/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1160,6 +1160,7 @@ window.__ModuleLoader__.load({
|
|
|
1160
1160
|
enlargeQr: "Enlarge the QR code",
|
|
1161
1161
|
closeEnlargedQr: "Close the enlarged QR code",
|
|
1162
1162
|
irohPairingReady: "iroh pairing code {code} is live for five minutes. Scan it from the iroh pairing screen; opening another code replaces this one.",
|
|
1163
|
+
irohPairingCopyOnly: "The QR image could not be generated. Copy the pairing link and paste it into the App’s iroh pairing screen.",
|
|
1163
1164
|
irohDescription: "Peer-to-peer encrypted link with relay fallback. No domain, port or account needed; requires the full Android App.",
|
|
1164
1165
|
prepareIroh: "Prepare iroh",
|
|
1165
1166
|
irohInstallNote: "The Install button downloads the pinned sidecar release (dsh-mobile-iroh 0.1.0-alpha.1, about 15 MB on Windows x64, 24 MB on Linux x64) into the plugin private directory and verifies its SHA-256. An installed companion with a verified copy is reused instead, with no download.",
|
|
@@ -1194,6 +1195,7 @@ window.__ModuleLoader__.load({
|
|
|
1194
1195
|
enlargeQr: "Ingrandisci il codice QR",
|
|
1195
1196
|
closeEnlargedQr: "Chiudi il codice QR ingrandito",
|
|
1196
1197
|
irohPairingReady: "Il codice iroh {code} è attivo per cinque minuti. Scansionalo dalla schermata di abbinamento iroh; aprirne un altro sostituisce questo.",
|
|
1198
|
+
irohPairingCopyOnly: "Impossibile generare il codice QR. Copia il link di abbinamento e incollalo nella schermata di abbinamento iroh dell’app.",
|
|
1197
1199
|
irohDescription: "Collegamento cifrato peer-to-peer con ripiego su relay. Nessun dominio, porta o account richiesto; richiede l’App Android completa.",
|
|
1198
1200
|
prepareIroh: "Prepara iroh",
|
|
1199
1201
|
irohInstallNote: "Il pulsante Installa scarica la release pinnata del sidecar (dsh-mobile-iroh 0.1.0-alpha.1, circa 15 MB su Windows x64, 24 MB su Linux x64) nella directory privata del plugin e ne verifica lo SHA-256. Un complemento installato con una copia verificata viene invece riutilizzato senza scaricare nulla.",
|
|
@@ -1228,6 +1230,7 @@ window.__ModuleLoader__.load({
|
|
|
1228
1230
|
enlargeQr: "放大二维码",
|
|
1229
1231
|
closeEnlargedQr: "关闭放大的二维码",
|
|
1230
1232
|
irohPairingReady: "iroh 配对码 {code} 已生效,有效期 5 分钟。请在 App 的 iroh 配对页扫码;再生成一个会替换当前这个。",
|
|
1233
|
+
irohPairingCopyOnly: "未能生成二维码。可复制配对链接,粘贴到 App 的 iroh 配对页。",
|
|
1231
1234
|
irohDescription: "点对点加密直连,跨网自动经中继。无需域名、端口或账号;需完整版 App。",
|
|
1232
1235
|
prepareIroh: "准备 iroh",
|
|
1233
1236
|
irohInstallNote: "点击安装会下载固定的 sidecar 发行版(dsh-mobile-iroh 0.1.0-alpha.1,Windows x64 约 15 MB,Linux x64 约 24 MB)到插件私有目录并校验 SHA-256;已安装的配套插件如有校验通过的副本会直接复用,不再下载。",
|
|
@@ -2042,6 +2045,13 @@ window.__ModuleLoader__.load({
|
|
|
2042
2045
|
frpVhostHttpPortHint: "The loopback plaintext vhost port of your own frps (upstream default 7080).",
|
|
2043
2046
|
frpPublicPort: "Public entry port",
|
|
2044
2047
|
frpPublicPortHint: "Raw TCP proxy port used as the public HTTPS entry (default 33080, never 3080/3443/3444).",
|
|
2048
|
+
frpUser: "frpc user (optional)",
|
|
2049
|
+
frpUserPlaceholder: "myfrpuser",
|
|
2050
|
+
funnelListenerReady: "Funnel is listening. Verify public access from a network outside this tailnet.",
|
|
2051
|
+
macosFolderHelpTitle: "Before leaving this Mac",
|
|
2052
|
+
macosFolderHelpNote: "Open the workspace folder you plan to use in DSH on this Mac and respond to any macOS prompt. In System Settings → Privacy & Security → Files and Folders, allow only the locations you need. If opening a folder remotely times out, check this Mac for a pending prompt before changing the pairing.",
|
|
2053
|
+
frpUserHint: "Optional user field of shared frps services: the proxy registers as user.proxyName. Leave blank to omit it.",
|
|
2054
|
+
frpUserInvalid: "The user must be 1–128 letters, digits, dots, underscores, or hyphens.",
|
|
2045
2055
|
frpAttachPlan: "Copy attach runbook",
|
|
2046
2056
|
frpAttachPlanCopied: "Attach runbook copied: local frpc.toml plus the VPS checklist.",
|
|
2047
2057
|
frpAttachPlanFailed: "Could not build the attach runbook: {error}",
|
|
@@ -2095,6 +2105,13 @@ window.__ModuleLoader__.load({
|
|
|
2095
2105
|
frpVhostHttpPortHint: "Porta vhost in chiaro su loopback del tuo frps (predefinita 7080).",
|
|
2096
2106
|
frpPublicPort: "Porta pubblica di ingresso",
|
|
2097
2107
|
frpPublicPortHint: "Porta del proxy TCP grezzo usata come ingresso HTTPS pubblico (predefinita 33080, mai 3080/3443/3444).",
|
|
2108
|
+
frpUser: "Utente frpc (facoltativo)",
|
|
2109
|
+
frpUserPlaceholder: "myfrpuser",
|
|
2110
|
+
funnelListenerReady: "Funnel è in ascolto. Verifica l’accesso pubblico da una rete esterna alla tailnet.",
|
|
2111
|
+
macosFolderHelpTitle: "Prima di lasciare il Mac",
|
|
2112
|
+
macosFolderHelpNote: "Apri in DSH su questo Mac la cartella di lavoro che userai e rispondi alle eventuali richieste di macOS. In Impostazioni di Sistema → Privacy e sicurezza → File e cartelle, autorizza solo le posizioni necessarie. Se una cartella non si apre da remoto per un timeout, controlla se sul Mac c’è una richiesta in attesa prima di modificare l’abbinamento.",
|
|
2113
|
+
frpUserHint: "Campo utente facoltativo dei servizi frps condivisi: il proxy si registra come user.nomeproxy. Lascia vuoto per ometterlo.",
|
|
2114
|
+
frpUserInvalid: "L’utente deve essere di 1–128 lettere, cifre, punti, trattini bassi o trattini.",
|
|
2098
2115
|
frpAttachPlan: "Copia il runbook attach",
|
|
2099
2116
|
frpAttachPlanCopied: "Runbook attach copiato: frpc.toml locale più la checklist VPS.",
|
|
2100
2117
|
frpAttachPlanFailed: "Impossibile creare il runbook attach: {error}",
|
|
@@ -2148,6 +2165,13 @@ window.__ModuleLoader__.load({
|
|
|
2148
2165
|
frpVhostHttpPortHint: "你自己 frps 的回环明文 vhost 端口(上游默认 7080)。",
|
|
2149
2166
|
frpPublicPort: "公网入口端口",
|
|
2150
2167
|
frpPublicPortHint: "作为公网 HTTPS 入口的 TCP 透传端口(默认 33080,不得为 3080/3443/3444)。",
|
|
2168
|
+
frpUser: "用户名(可选)",
|
|
2169
|
+
frpUserPlaceholder: "myfrpuser",
|
|
2170
|
+
funnelListenerReady: "Funnel 已启动监听。请在此 tailnet 之外的网络验证公网访问。",
|
|
2171
|
+
macosFolderHelpTitle: "离开这台 Mac 前",
|
|
2172
|
+
macosFolderHelpNote: "先在这台 Mac 的 DSH 中打开远程会用到的工作区目录,处理可能出现的系统提示。在「系统设置 → 隐私与安全性 → 文件与文件夹」只授权需要的位置。手机打开文件夹超时时,先检查 Mac 上是否有待确认提示,不要急着重新配对。",
|
|
2173
|
+
frpUserHint: "共享 frp 服务的可选 user 字段:代理会以「用户名.代理名」注册。留空则不发送。",
|
|
2174
|
+
frpUserInvalid: "用户名只能包含 1–128 个字母、数字、点、下划线或连字符。",
|
|
2151
2175
|
frpAttachPlan: "复制接入清单",
|
|
2152
2176
|
frpAttachPlanCopied: "接入清单已复制:本机 frpc.toml 与 VPS 侧待办。",
|
|
2153
2177
|
frpAttachPlanFailed: "生成接入清单失败:{error}",
|
|
@@ -2466,7 +2490,8 @@ window.__ModuleLoader__.load({
|
|
|
2466
2490
|
"mode",
|
|
2467
2491
|
"entryTls",
|
|
2468
2492
|
"vhostHttpPort",
|
|
2469
|
-
"publicPort"
|
|
2493
|
+
"publicPort",
|
|
2494
|
+
"user"
|
|
2470
2495
|
]);
|
|
2471
2496
|
function hostname(value) {
|
|
2472
2497
|
if (value.length > 253 || !value.includes(".")) return false;
|
|
@@ -2489,6 +2514,11 @@ window.__ModuleLoader__.load({
|
|
|
2489
2514
|
if (typeof value !== "string" || value.length < 16 || value.length > 512 || /[\s\u0000-\u001f\u007f]/u.test(value)) throw new Error("frp_token_invalid");
|
|
2490
2515
|
return value;
|
|
2491
2516
|
}
|
|
2517
|
+
/** Validate the optional frpc `user` field; it must stay one safe TOML token. */
|
|
2518
|
+
function validateFrpUser(value) {
|
|
2519
|
+
if (typeof value !== "string" || value.length < 1 || value.length > 128 || !/^[A-Za-z0-9._-]+$/u.test(value)) throw new Error("frp_user_invalid");
|
|
2520
|
+
return value;
|
|
2521
|
+
}
|
|
2492
2522
|
/** Validate the public HTTPS origin used by Caddy and Android pairing. */
|
|
2493
2523
|
function validateFrpPublicOrigin(value) {
|
|
2494
2524
|
if (typeof value !== "string" || value.length > 512) throw new Error("frp_public_origin_invalid");
|
|
@@ -2566,6 +2596,7 @@ window.__ModuleLoader__.load({
|
|
|
2566
2596
|
const entryTls = record.entryTls === void 0 ? void 0 : validateFrpEntryTls(record.entryTls);
|
|
2567
2597
|
const vhostHttpPort = record.vhostHttpPort === void 0 ? void 0 : validateFrpVhostHttpPort(record.vhostHttpPort);
|
|
2568
2598
|
const publicPort = record.publicPort === void 0 ? void 0 : validateFrpPublicPort(record.publicPort);
|
|
2599
|
+
const user = record.user === void 0 || record.user === "" ? void 0 : validateFrpUser(record.user);
|
|
2569
2600
|
const resolvedMode = mode ?? "deploy";
|
|
2570
2601
|
const resolvedEntryTls = entryTls ?? "public-ip-cert";
|
|
2571
2602
|
if (resolvedEntryTls === "self-signed" && resolvedMode !== "attach") throw new Error("frp_entry_tls_invalid");
|
|
@@ -2581,7 +2612,8 @@ window.__ModuleLoader__.load({
|
|
|
2581
2612
|
...mode === void 0 ? {} : { mode },
|
|
2582
2613
|
...entryTls === void 0 ? {} : { entryTls },
|
|
2583
2614
|
...vhostHttpPort === void 0 ? {} : { vhostHttpPort },
|
|
2584
|
-
...publicPort === void 0 ? {} : { publicPort }
|
|
2615
|
+
...publicPort === void 0 ? {} : { publicPort },
|
|
2616
|
+
...user === void 0 ? {} : { user }
|
|
2585
2617
|
});
|
|
2586
2618
|
}
|
|
2587
2619
|
function tomlString(value) {
|
|
@@ -2627,6 +2659,7 @@ window.__ModuleLoader__.load({
|
|
|
2627
2659
|
`serverPort = ${String(settings.serverPort)}`,
|
|
2628
2660
|
"auth.method = \"token\"",
|
|
2629
2661
|
`auth.token = ${tomlString(settings.token)}`,
|
|
2662
|
+
...settings.user === void 0 ? [] : [`user = ${tomlString(settings.user)}`],
|
|
2630
2663
|
"transport.tls.enable = true",
|
|
2631
2664
|
"",
|
|
2632
2665
|
"[[proxies]]",
|
|
@@ -2874,6 +2907,11 @@ window.__ModuleLoader__.load({
|
|
|
2874
2907
|
code: "frp_public_origin_invalid",
|
|
2875
2908
|
messageKey: "frpPublicOriginInvalid"
|
|
2876
2909
|
},
|
|
2910
|
+
frp_user_invalid: {
|
|
2911
|
+
field: "user",
|
|
2912
|
+
code: "frp_user_invalid",
|
|
2913
|
+
messageKey: "frpUserInvalid"
|
|
2914
|
+
},
|
|
2877
2915
|
frp_attach_mode_requires_vhost_port: {
|
|
2878
2916
|
field: "vhostHttpPort",
|
|
2879
2917
|
code: "frp_attach_mode_requires_vhost_port",
|
|
@@ -2907,6 +2945,7 @@ window.__ModuleLoader__.load({
|
|
|
2907
2945
|
check("serverAddress", "frpServerAddressInvalid", () => validateFrpServerAddress(form.serverAddress));
|
|
2908
2946
|
check("serverPort", "frpServerPortInvalid", () => validateFrpServerPort(form.serverPort));
|
|
2909
2947
|
if (form.token !== "" || !allowSavedToken) check("token", "frpTokenInvalid", () => validateFrpToken(form.token));
|
|
2948
|
+
if (form.user !== void 0 && form.user !== "") check("user", "frpUserInvalid", () => validateFrpUser(form.user));
|
|
2910
2949
|
check("publicOrigin", "frpPublicOriginInvalid", () => validateFrpPublicOrigin(form.publicOrigin));
|
|
2911
2950
|
check("mode", "frpModeInvalid", () => validateFrpMode(form.mode));
|
|
2912
2951
|
check("entryTls", "frpEntryTlsInvalid", () => validateFrpEntryTls(form.entryTls));
|
|
@@ -2929,45 +2968,173 @@ window.__ModuleLoader__.load({
|
|
|
2929
2968
|
}
|
|
2930
2969
|
return issues;
|
|
2931
2970
|
}
|
|
2932
|
-
// node_modules/@noble/hashes/
|
|
2933
|
-
|
|
2934
|
-
|
|
2971
|
+
// node_modules/@noble/hashes/_u64.js
|
|
2972
|
+
const fromNumH = (n) => n / 2 ** 32 | 0;
|
|
2973
|
+
const fromNumL = (n) => n >>> 0;
|
|
2974
|
+
function setU64FromNum(view, byteOffset, n, isLE) {
|
|
2975
|
+
const h = fromNumH(n);
|
|
2976
|
+
const l = fromNumL(n);
|
|
2977
|
+
view.setUint32(byteOffset, isLE ? l : h, isLE);
|
|
2978
|
+
view.setUint32(byteOffset + 4, isLE ? h : l, isLE);
|
|
2979
|
+
}
|
|
2980
|
+
// node_modules/@noble/hashes/utils.js
|
|
2981
|
+
/**
|
|
2982
|
+
* Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.
|
|
2983
|
+
* @param a - value to test
|
|
2984
|
+
* @returns `true` when the value is a Uint8Array-compatible view.
|
|
2985
|
+
* @example
|
|
2986
|
+
* Check whether a value is a Uint8Array-compatible view.
|
|
2987
|
+
* ```ts
|
|
2988
|
+
* isBytes(new Uint8Array([1, 2, 3]));
|
|
2989
|
+
* ```
|
|
2990
|
+
*/
|
|
2935
2991
|
function isBytes(a) {
|
|
2936
|
-
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
2992
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
2937
2993
|
}
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2994
|
+
const atitle = (title) => title ? `"${title}" ` : "";
|
|
2995
|
+
/**
|
|
2996
|
+
* Asserts something is a non-negative integer.
|
|
2997
|
+
* @param n - number to validate
|
|
2998
|
+
* @param title - label included in thrown errors
|
|
2999
|
+
* @returns The validated number.
|
|
3000
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3001
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
3002
|
+
* @example
|
|
3003
|
+
* Validate a non-negative integer option.
|
|
3004
|
+
* ```ts
|
|
3005
|
+
* anumber(32, 'length');
|
|
3006
|
+
* ```
|
|
3007
|
+
*/
|
|
3008
|
+
function anumber(n, title = "") {
|
|
3009
|
+
if (typeof n !== "number") throw new TypeError(atitle(title) + "expected number, got " + typeof n);
|
|
3010
|
+
if (!Number.isSafeInteger(n) || n < 0) throw new RangeError(atitle(title) + "expected integer >= 0, got " + n);
|
|
3011
|
+
return n;
|
|
2942
3012
|
}
|
|
2943
|
-
/**
|
|
3013
|
+
/**
|
|
3014
|
+
* Asserts something is Uint8Array.
|
|
3015
|
+
* @param value - value to validate
|
|
3016
|
+
* @param length - optional exact length constraint
|
|
3017
|
+
* @param title - label included in thrown errors
|
|
3018
|
+
* @returns The validated byte array.
|
|
3019
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3020
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
3021
|
+
* @example
|
|
3022
|
+
* Validate that a value is a byte array.
|
|
3023
|
+
* ```ts
|
|
3024
|
+
* abytes(new Uint8Array([1, 2, 3]));
|
|
3025
|
+
* ```
|
|
3026
|
+
*/
|
|
3027
|
+
function abytes(value, length, title = "") {
|
|
3028
|
+
if (isBytes(value) && (length === void 0 || value.length === length)) return value;
|
|
3029
|
+
if (length !== void 0) anumber(length, "length");
|
|
3030
|
+
const bytes = isBytes(value);
|
|
3031
|
+
const ofLen = length !== void 0 ? ` of length ${length}` : "";
|
|
3032
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
3033
|
+
const message = atitle(title) + "expected Uint8Array" + ofLen + ", got " + got;
|
|
3034
|
+
if (!bytes) throw new TypeError(message);
|
|
3035
|
+
throw new RangeError(message);
|
|
3036
|
+
}
|
|
3037
|
+
const aobject = (value, label) => {
|
|
3038
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
|
|
3039
|
+
};
|
|
3040
|
+
const aopts = (value, label) => {
|
|
3041
|
+
aobject(value, label);
|
|
3042
|
+
const proto = Object.getPrototypeOf(value);
|
|
3043
|
+
if (proto !== Object.prototype && proto !== null) throw new TypeError(`"${label}" expected plain object`);
|
|
3044
|
+
if (Object.hasOwn(value, "__proto__")) throw new TypeError(`"${label}.__proto__" is not allowed`);
|
|
3045
|
+
};
|
|
3046
|
+
/**
|
|
3047
|
+
* Asserts a hash instance has not been destroyed or finished.
|
|
3048
|
+
* @param instance - hash instance to validate
|
|
3049
|
+
* @param checkFinished - whether to reject finalized instances
|
|
3050
|
+
* @throws If the hash instance has already been destroyed or finalized. {@link Error}
|
|
3051
|
+
* @example
|
|
3052
|
+
* Validate that a hash instance is still usable.
|
|
3053
|
+
* ```ts
|
|
3054
|
+
* import { aexists } from '@noble/hashes/utils.js';
|
|
3055
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
3056
|
+
* const hash = sha256.create();
|
|
3057
|
+
* aexists(hash);
|
|
3058
|
+
* ```
|
|
3059
|
+
*/
|
|
2944
3060
|
function aexists(instance, checkFinished = true) {
|
|
2945
|
-
if (instance.destroyed) throw new Error("
|
|
2946
|
-
if (checkFinished && instance.finished) throw new Error("
|
|
3061
|
+
if (instance.destroyed) throw new Error("hash was destroyed");
|
|
3062
|
+
if (checkFinished && instance.finished) throw new Error("digest() was already called");
|
|
2947
3063
|
}
|
|
2948
|
-
/**
|
|
3064
|
+
/**
|
|
3065
|
+
* Asserts output is a sufficiently-sized byte array.
|
|
3066
|
+
* @param out - destination buffer
|
|
3067
|
+
* @param instance - hash instance providing output length
|
|
3068
|
+
* Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.
|
|
3069
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3070
|
+
* @throws On wrong argument ranges or values. {@link RangeError}
|
|
3071
|
+
* @example
|
|
3072
|
+
* Validate a caller-provided digest buffer.
|
|
3073
|
+
* ```ts
|
|
3074
|
+
* import { aoutput } from '@noble/hashes/utils.js';
|
|
3075
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
3076
|
+
* const hash = sha256.create();
|
|
3077
|
+
* aoutput(new Uint8Array(hash.outputLen), hash);
|
|
3078
|
+
* ```
|
|
3079
|
+
*/
|
|
2949
3080
|
function aoutput(out, instance) {
|
|
2950
|
-
abytes(out);
|
|
3081
|
+
abytes(out, void 0, "output");
|
|
2951
3082
|
const min = instance.outputLen;
|
|
2952
|
-
if (out.length
|
|
3083
|
+
if (!(out.length >= min)) throw new RangeError("\"output\" expected length >= " + min);
|
|
2953
3084
|
}
|
|
2954
|
-
/**
|
|
3085
|
+
/**
|
|
3086
|
+
* Zeroizes typed arrays in place. Warning: JS provides no guarantees.
|
|
3087
|
+
* @param arrays - arrays to overwrite with zeros
|
|
3088
|
+
* @example
|
|
3089
|
+
* Zeroize sensitive buffers in place.
|
|
3090
|
+
* ```ts
|
|
3091
|
+
* clean(new Uint8Array([1, 2, 3]));
|
|
3092
|
+
* ```
|
|
3093
|
+
*/
|
|
2955
3094
|
function clean(...arrays) {
|
|
2956
3095
|
for (let i = 0; i < arrays.length; i++) arrays[i].fill(0);
|
|
2957
3096
|
}
|
|
2958
|
-
/**
|
|
3097
|
+
/**
|
|
3098
|
+
* Creates a DataView for byte-level manipulation.
|
|
3099
|
+
* @param arr - source typed array
|
|
3100
|
+
* @returns DataView over the same buffer region.
|
|
3101
|
+
* @example
|
|
3102
|
+
* Create a DataView over an existing buffer.
|
|
3103
|
+
* ```ts
|
|
3104
|
+
* createView(new Uint8Array(4));
|
|
3105
|
+
* ```
|
|
3106
|
+
*/
|
|
2959
3107
|
function createView(arr) {
|
|
2960
3108
|
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
2961
3109
|
}
|
|
2962
|
-
/**
|
|
3110
|
+
/**
|
|
3111
|
+
* Rotate-right operation for uint32 values.
|
|
3112
|
+
* @param word - source word
|
|
3113
|
+
* @param shift - shift amount in bits
|
|
3114
|
+
* @returns Rotated word.
|
|
3115
|
+
* @example
|
|
3116
|
+
* Rotate a 32-bit word to the right.
|
|
3117
|
+
* ```ts
|
|
3118
|
+
* rotr(0x12345678, 8);
|
|
3119
|
+
* ```
|
|
3120
|
+
*/
|
|
2963
3121
|
function rotr(word, shift) {
|
|
2964
3122
|
return word << 32 - shift | word >>> shift;
|
|
2965
3123
|
}
|
|
2966
3124
|
const hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")();
|
|
2967
3125
|
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
2968
3126
|
/**
|
|
2969
|
-
* Convert byte array to hex string.
|
|
2970
|
-
*
|
|
3127
|
+
* Convert byte array to hex string.
|
|
3128
|
+
* Uses the built-in function when available and assumes it matches the tested
|
|
3129
|
+
* fallback semantics.
|
|
3130
|
+
* @param bytes - bytes to encode
|
|
3131
|
+
* @returns Lowercase hexadecimal string.
|
|
3132
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3133
|
+
* @example
|
|
3134
|
+
* Convert bytes to lowercase hexadecimal.
|
|
3135
|
+
* ```ts
|
|
3136
|
+
* bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'
|
|
3137
|
+
* ```
|
|
2971
3138
|
*/
|
|
2972
3139
|
function bytesToHex(bytes) {
|
|
2973
3140
|
abytes(bytes);
|
|
@@ -2978,69 +3145,169 @@ window.__ModuleLoader__.load({
|
|
|
2978
3145
|
}
|
|
2979
3146
|
/**
|
|
2980
3147
|
* Converts string to bytes using UTF8 encoding.
|
|
2981
|
-
*
|
|
3148
|
+
* Built-in doesn't validate input to be string: we do the check.
|
|
3149
|
+
* Non-ASCII details are delegated to the platform `TextEncoder`.
|
|
3150
|
+
* @param str - string to encode
|
|
3151
|
+
* @returns UTF-8 encoded bytes.
|
|
3152
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3153
|
+
* @example
|
|
3154
|
+
* Encode a string as UTF-8 bytes.
|
|
3155
|
+
* ```ts
|
|
3156
|
+
* utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])
|
|
3157
|
+
* ```
|
|
2982
3158
|
*/
|
|
2983
3159
|
function utf8ToBytes(str) {
|
|
2984
|
-
if (typeof str !== "string") throw new
|
|
2985
|
-
|
|
3160
|
+
if (typeof str !== "string") throw new TypeError("string expected");
|
|
3161
|
+
const encoded = new TextEncoder().encode(str);
|
|
3162
|
+
try {
|
|
3163
|
+
return new Uint8Array(encoded);
|
|
3164
|
+
} finally {
|
|
3165
|
+
clean(encoded);
|
|
3166
|
+
}
|
|
2986
3167
|
}
|
|
2987
3168
|
/**
|
|
2988
|
-
*
|
|
2989
|
-
*
|
|
2990
|
-
*
|
|
3169
|
+
* Merges default options and passed options.
|
|
3170
|
+
* @param defaults - base option object
|
|
3171
|
+
* @param opts - user overrides
|
|
3172
|
+
* @param title - label included in thrown override errors
|
|
3173
|
+
* @returns Fresh merged option object with a null prototype.
|
|
3174
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3175
|
+
* @example
|
|
3176
|
+
* Merge user overrides onto default options.
|
|
3177
|
+
* ```ts
|
|
3178
|
+
* checkOpts({ dkLen: 32 }, { asyncTick: 10 });
|
|
3179
|
+
* ```
|
|
2991
3180
|
*/
|
|
2992
|
-
function
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
return
|
|
2996
|
-
}
|
|
2997
|
-
/**
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3181
|
+
function checkOpts(defaults, opts, title = "opts") {
|
|
3182
|
+
aopts(defaults, "defaults");
|
|
3183
|
+
if (opts !== void 0) aopts(opts, title);
|
|
3184
|
+
return Object.assign(Object.create(null), defaults, opts);
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3187
|
+
* Creates a callable hash function from a stateful class constructor.
|
|
3188
|
+
* @param hashCons - hash constructor or factory
|
|
3189
|
+
* @param info - optional metadata such as DER OID
|
|
3190
|
+
* @returns Frozen callable hash wrapper with `.create()`.
|
|
3191
|
+
* Wrapper construction eagerly calls `hashCons(undefined)` once to read
|
|
3192
|
+
* `outputLen` / `blockLen`, so constructor side effects happen at module
|
|
3193
|
+
* init time.
|
|
3194
|
+
* @throws On wrong argument types. {@link TypeError}
|
|
3195
|
+
* @example
|
|
3196
|
+
* Wrap a stateful hash constructor into a callable helper.
|
|
3197
|
+
* ```ts
|
|
3198
|
+
* import { createHasher } from '@noble/hashes/utils.js';
|
|
3199
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
3200
|
+
* const wrapped = createHasher(sha256.create, { oid: sha256.oid });
|
|
3201
|
+
* wrapped(new Uint8Array([1]));
|
|
3202
|
+
* ```
|
|
3203
|
+
*/
|
|
3204
|
+
function createHasher(hashCons, info = {}) {
|
|
3205
|
+
if (typeof hashCons !== "function") throw new TypeError("\"hashCons\" expected function, got type=" + typeof hashCons);
|
|
3206
|
+
info = checkOpts({}, info, "info");
|
|
3207
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
3208
|
+
const tmp = hashCons(void 0);
|
|
3003
3209
|
hashC.outputLen = tmp.outputLen;
|
|
3004
3210
|
hashC.blockLen = tmp.blockLen;
|
|
3005
|
-
hashC.
|
|
3006
|
-
|
|
3211
|
+
hashC.canXOF = tmp.canXOF;
|
|
3212
|
+
hashC.create = (opts) => hashCons(opts);
|
|
3213
|
+
Object.assign(hashC, info);
|
|
3214
|
+
return Object.freeze(hashC);
|
|
3007
3215
|
}
|
|
3008
|
-
|
|
3216
|
+
/**
|
|
3217
|
+
* Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.
|
|
3218
|
+
* @param suffix - final OID byte for the selected hash.
|
|
3219
|
+
* The helper accepts any byte even though only the documented NIST hash
|
|
3220
|
+
* suffixes are meaningful downstream.
|
|
3221
|
+
* @returns Object containing the DER-encoded OID.
|
|
3222
|
+
* @example
|
|
3223
|
+
* Build OID metadata for a NIST hash.
|
|
3224
|
+
* ```ts
|
|
3225
|
+
* oidNist(0x01);
|
|
3226
|
+
* ```
|
|
3227
|
+
*/
|
|
3228
|
+
const oidNist = (suffix) => ({ oid: Uint8Array.from([
|
|
3229
|
+
6,
|
|
3230
|
+
9,
|
|
3231
|
+
96,
|
|
3232
|
+
134,
|
|
3233
|
+
72,
|
|
3234
|
+
1,
|
|
3235
|
+
101,
|
|
3236
|
+
3,
|
|
3237
|
+
4,
|
|
3238
|
+
2,
|
|
3239
|
+
suffix
|
|
3240
|
+
]) });
|
|
3241
|
+
// node_modules/@noble/hashes/_md.js
|
|
3009
3242
|
/**
|
|
3010
3243
|
* Internal Merkle-Damgard hash utils.
|
|
3011
3244
|
* @module
|
|
3012
3245
|
*/
|
|
3013
|
-
/**
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3246
|
+
/**
|
|
3247
|
+
* Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.
|
|
3248
|
+
* Returns bits from `b` when `a` is set, otherwise from `c`.
|
|
3249
|
+
* The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never
|
|
3250
|
+
* set the same bit.
|
|
3251
|
+
* @param a - selector word
|
|
3252
|
+
* @param b - word chosen when selector bit is set
|
|
3253
|
+
* @param c - word chosen when selector bit is clear
|
|
3254
|
+
* @returns Mixed 32-bit word.
|
|
3255
|
+
* @example
|
|
3256
|
+
* Combine three words with the shared 32-bit choice primitive.
|
|
3257
|
+
* ```ts
|
|
3258
|
+
* Chi(0xffffffff, 0x12345678, 0x87654321);
|
|
3259
|
+
* ```
|
|
3260
|
+
*/
|
|
3026
3261
|
function Chi(a, b, c) {
|
|
3027
3262
|
return a & b ^ ~a & c;
|
|
3028
3263
|
}
|
|
3029
|
-
/**
|
|
3264
|
+
/**
|
|
3265
|
+
* Shared 32-bit majority primitive reused by SHA-256 and SHA-1.
|
|
3266
|
+
* Returns bits shared by at least two inputs.
|
|
3267
|
+
* @param a - first input word
|
|
3268
|
+
* @param b - second input word
|
|
3269
|
+
* @param c - third input word
|
|
3270
|
+
* @returns Mixed 32-bit word.
|
|
3271
|
+
* @example
|
|
3272
|
+
* Combine three words with the shared 32-bit majority primitive.
|
|
3273
|
+
* ```ts
|
|
3274
|
+
* Maj(0xffffffff, 0x12345678, 0x87654321);
|
|
3275
|
+
* ```
|
|
3276
|
+
*/
|
|
3030
3277
|
function Maj(a, b, c) {
|
|
3031
3278
|
return a & b ^ a & c ^ b & c;
|
|
3032
3279
|
}
|
|
3033
3280
|
/**
|
|
3034
3281
|
* Merkle-Damgard hash construction base class.
|
|
3035
3282
|
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
|
|
3283
|
+
* Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit
|
|
3284
|
+
* strings with partial-byte tails.
|
|
3285
|
+
* @param blockLen - internal block size in bytes
|
|
3286
|
+
* @param outputLen - digest size in bytes
|
|
3287
|
+
* @param padOffset - trailing length field size in bytes
|
|
3288
|
+
* @param isLE - whether length and state words are encoded in little-endian
|
|
3289
|
+
* @example
|
|
3290
|
+
* Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.
|
|
3291
|
+
* ```ts
|
|
3292
|
+
* import { _SHA1 } from '@noble/hashes/legacy.js';
|
|
3293
|
+
* const hash = new _SHA1();
|
|
3294
|
+
* hash.update(new Uint8Array([97, 98, 99]));
|
|
3295
|
+
* hash.digest();
|
|
3296
|
+
* ```
|
|
3036
3297
|
*/
|
|
3037
|
-
var HashMD = class
|
|
3298
|
+
var HashMD = class {
|
|
3299
|
+
blockLen;
|
|
3300
|
+
outputLen;
|
|
3301
|
+
canXOF = false;
|
|
3302
|
+
padOffset;
|
|
3303
|
+
isLE;
|
|
3304
|
+
buffer;
|
|
3305
|
+
view;
|
|
3306
|
+
finished = false;
|
|
3307
|
+
length = 0;
|
|
3308
|
+
pos = 0;
|
|
3309
|
+
destroyed = false;
|
|
3038
3310
|
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
3039
|
-
super();
|
|
3040
|
-
this.finished = false;
|
|
3041
|
-
this.length = 0;
|
|
3042
|
-
this.pos = 0;
|
|
3043
|
-
this.destroyed = false;
|
|
3044
3311
|
this.blockLen = blockLen;
|
|
3045
3312
|
this.outputLen = outputLen;
|
|
3046
3313
|
this.padOffset = padOffset;
|
|
@@ -3050,27 +3317,29 @@ window.__ModuleLoader__.load({
|
|
|
3050
3317
|
}
|
|
3051
3318
|
update(data) {
|
|
3052
3319
|
aexists(this);
|
|
3053
|
-
data = toBytes(data);
|
|
3054
3320
|
abytes(data);
|
|
3055
3321
|
const { view, buffer, blockLen } = this;
|
|
3056
3322
|
const len = data.length;
|
|
3323
|
+
let processed = false;
|
|
3057
3324
|
for (let pos = 0; pos < len;) {
|
|
3058
3325
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
3059
3326
|
if (take === blockLen) {
|
|
3060
3327
|
const dataView = createView(data);
|
|
3061
3328
|
for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);
|
|
3329
|
+
processed = true;
|
|
3062
3330
|
continue;
|
|
3063
3331
|
}
|
|
3064
|
-
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
3332
|
+
buffer.set(pos === 0 && take === len ? data : data.subarray(pos, pos + take), this.pos);
|
|
3065
3333
|
this.pos += take;
|
|
3066
3334
|
pos += take;
|
|
3067
3335
|
if (this.pos === blockLen) {
|
|
3068
3336
|
this.process(view, 0);
|
|
3069
3337
|
this.pos = 0;
|
|
3338
|
+
processed = true;
|
|
3070
3339
|
}
|
|
3071
3340
|
}
|
|
3072
3341
|
this.length += data.length;
|
|
3073
|
-
this.roundClean();
|
|
3342
|
+
if (processed) this.roundClean();
|
|
3074
3343
|
return this;
|
|
3075
3344
|
}
|
|
3076
3345
|
digestInto(out) {
|
|
@@ -3080,20 +3349,19 @@ window.__ModuleLoader__.load({
|
|
|
3080
3349
|
const { buffer, view, blockLen, isLE } = this;
|
|
3081
3350
|
let { pos } = this;
|
|
3082
3351
|
buffer[pos++] = 128;
|
|
3083
|
-
|
|
3352
|
+
buffer.fill(0, pos);
|
|
3084
3353
|
if (this.padOffset > blockLen - pos) {
|
|
3085
3354
|
this.process(view, 0);
|
|
3086
|
-
|
|
3355
|
+
buffer.fill(0);
|
|
3087
3356
|
}
|
|
3088
|
-
|
|
3089
|
-
setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
3357
|
+
setU64FromNum(view, blockLen - 8, this.length * 8, isLE);
|
|
3090
3358
|
this.process(view, 0);
|
|
3091
|
-
|
|
3359
|
+
this.roundClean();
|
|
3360
|
+
const oview = out === buffer ? view : createView(out);
|
|
3092
3361
|
const len = this.outputLen;
|
|
3093
|
-
if (len % 4) throw new Error("_sha2: outputLen should be aligned to 32bit");
|
|
3094
3362
|
const outLen = len / 4;
|
|
3095
3363
|
const state = this.get();
|
|
3096
|
-
if (outLen > state.length) throw new Error("
|
|
3364
|
+
if (len % 4 || outLen > state.length) throw new Error("invalid outputLen");
|
|
3097
3365
|
for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);
|
|
3098
3366
|
}
|
|
3099
3367
|
digest() {
|
|
@@ -3103,15 +3371,13 @@ window.__ModuleLoader__.load({
|
|
|
3103
3371
|
this.destroy();
|
|
3104
3372
|
return res;
|
|
3105
3373
|
}
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
to.set(...this.get());
|
|
3109
|
-
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
3374
|
+
_cloneIntoMeta(to) {
|
|
3375
|
+
const { buffer, length, finished, destroyed, pos } = this;
|
|
3110
3376
|
to.destroyed = destroyed;
|
|
3111
3377
|
to.finished = finished;
|
|
3112
3378
|
to.length = length;
|
|
3113
3379
|
to.pos = pos;
|
|
3114
|
-
if (
|
|
3380
|
+
if (pos) to.buffer.set(buffer);
|
|
3115
3381
|
return to;
|
|
3116
3382
|
}
|
|
3117
3383
|
clone() {
|
|
@@ -3122,7 +3388,9 @@ window.__ModuleLoader__.load({
|
|
|
3122
3388
|
* Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.
|
|
3123
3389
|
* Check out `test/misc/sha2-gen-iv.js` for recomputation guide.
|
|
3124
3390
|
*/
|
|
3125
|
-
/** Initial SHA256 state.
|
|
3391
|
+
/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the
|
|
3392
|
+
* square roots of the first eight prime numbers. Exported as a shared table; callers must treat
|
|
3393
|
+
* it as read-only because constructors copy words from it by index. */
|
|
3126
3394
|
const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
3127
3395
|
1779033703,
|
|
3128
3396
|
3144134277,
|
|
@@ -3133,17 +3401,17 @@ window.__ModuleLoader__.load({
|
|
|
3133
3401
|
528734635,
|
|
3134
3402
|
1541459225
|
|
3135
3403
|
]);
|
|
3136
|
-
// node_modules/@noble/hashes/
|
|
3404
|
+
// node_modules/@noble/hashes/sha2.js
|
|
3137
3405
|
/**
|
|
3138
3406
|
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
|
|
3139
3407
|
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
|
|
3140
|
-
* Check out
|
|
3141
|
-
*
|
|
3408
|
+
* Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and
|
|
3409
|
+
* {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.
|
|
3142
3410
|
* @module
|
|
3143
3411
|
*/
|
|
3144
3412
|
/**
|
|
3145
|
-
*
|
|
3146
|
-
*
|
|
3413
|
+
* SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits
|
|
3414
|
+
* of the cube roots of the first 64 primes (2..311).
|
|
3147
3415
|
*/
|
|
3148
3416
|
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
3149
3417
|
1116352408,
|
|
@@ -3211,19 +3479,28 @@ window.__ModuleLoader__.load({
|
|
|
3211
3479
|
3204031479,
|
|
3212
3480
|
3329325298
|
|
3213
3481
|
]);
|
|
3214
|
-
/** Reusable
|
|
3482
|
+
/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */
|
|
3215
3483
|
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
3216
|
-
|
|
3217
|
-
|
|
3484
|
+
/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */
|
|
3485
|
+
var SHA2_32B = class extends HashMD {
|
|
3486
|
+
A = 0;
|
|
3487
|
+
B = 0;
|
|
3488
|
+
C = 0;
|
|
3489
|
+
D = 0;
|
|
3490
|
+
E = 0;
|
|
3491
|
+
F = 0;
|
|
3492
|
+
G = 0;
|
|
3493
|
+
H = 0;
|
|
3494
|
+
constructor(outputLen, IV) {
|
|
3218
3495
|
super(64, outputLen, 8, false);
|
|
3219
|
-
this.A =
|
|
3220
|
-
this.B =
|
|
3221
|
-
this.C =
|
|
3222
|
-
this.D =
|
|
3223
|
-
this.E =
|
|
3224
|
-
this.F =
|
|
3225
|
-
this.G =
|
|
3226
|
-
this.H =
|
|
3496
|
+
this.A = IV[0] | 0;
|
|
3497
|
+
this.B = IV[1] | 0;
|
|
3498
|
+
this.C = IV[2] | 0;
|
|
3499
|
+
this.D = IV[3] | 0;
|
|
3500
|
+
this.E = IV[4] | 0;
|
|
3501
|
+
this.F = IV[5] | 0;
|
|
3502
|
+
this.G = IV[6] | 0;
|
|
3503
|
+
this.H = IV[7] | 0;
|
|
3227
3504
|
}
|
|
3228
3505
|
get() {
|
|
3229
3506
|
const { A, B, C, D, E, F, G, H } = this;
|
|
@@ -3248,6 +3525,10 @@ window.__ModuleLoader__.load({
|
|
|
3248
3525
|
this.G = G | 0;
|
|
3249
3526
|
this.H = H | 0;
|
|
3250
3527
|
}
|
|
3528
|
+
_cloneInto(to) {
|
|
3529
|
+
(to ||= new this.constructor()).set(...this.get());
|
|
3530
|
+
return this._cloneIntoMeta(to);
|
|
3531
|
+
}
|
|
3251
3532
|
process(view, offset) {
|
|
3252
3533
|
for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
|
|
3253
3534
|
for (let i = 16; i < 64; i++) {
|
|
@@ -3285,23 +3566,34 @@ window.__ModuleLoader__.load({
|
|
|
3285
3566
|
clean(SHA256_W);
|
|
3286
3567
|
}
|
|
3287
3568
|
destroy() {
|
|
3569
|
+
this.destroyed = true;
|
|
3288
3570
|
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
3289
3571
|
clean(this.buffer);
|
|
3290
3572
|
}
|
|
3291
3573
|
};
|
|
3292
|
-
|
|
3574
|
+
/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */
|
|
3575
|
+
var _SHA256 = class extends SHA2_32B {
|
|
3576
|
+
constructor() {
|
|
3577
|
+
super(32, SHA256_IV);
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3293
3580
|
/**
|
|
3294
|
-
* SHA2-256
|
|
3581
|
+
* SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:
|
|
3295
3582
|
*
|
|
3296
|
-
*
|
|
3297
|
-
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
3298
|
-
*
|
|
3299
|
-
*
|
|
3300
|
-
* @
|
|
3301
|
-
* @
|
|
3583
|
+
* - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.
|
|
3584
|
+
* - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
3585
|
+
* - Each sha256 hash is executing 2^18 bit operations.
|
|
3586
|
+
* - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.
|
|
3587
|
+
* @param msg - message bytes to hash
|
|
3588
|
+
* @param opts - Reserved hash options.
|
|
3589
|
+
* @returns Digest bytes.
|
|
3590
|
+
* @example
|
|
3591
|
+
* Hash a message with SHA2-256.
|
|
3592
|
+
* ```ts
|
|
3593
|
+
* sha256(new Uint8Array([97, 98, 99]));
|
|
3594
|
+
* ```
|
|
3302
3595
|
*/
|
|
3303
|
-
|
|
3304
|
-
const sha256 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
3596
|
+
const sha256 = /* @__PURE__ */ createHasher(() => new _SHA256(), /* @__PURE__ */ oidNist(1));
|
|
3305
3597
|
Object.freeze({
|
|
3306
3598
|
title: 80,
|
|
3307
3599
|
body: 200,
|
|
@@ -3310,7 +3602,7 @@ window.__ModuleLoader__.load({
|
|
|
3310
3602
|
const MAX_SEEN_QUESTION_KEYS = 20;
|
|
3311
3603
|
/** A bounded native identifier derived from the complete, case-sensitive wire ID. */
|
|
3312
3604
|
function notificationIdentity(value) {
|
|
3313
|
-
return bytesToHex(sha256(JSON.stringify(value)).subarray(0, 16));
|
|
3605
|
+
return bytesToHex(sha256(utf8ToBytes(JSON.stringify(value))).subarray(0, 16));
|
|
3314
3606
|
}
|
|
3315
3607
|
/** Stable per-turn tag so concurrent sessions keep separate notifications. */
|
|
3316
3608
|
function taskCompletionTag(sessionId, turn) {
|
|
@@ -3755,6 +4047,20 @@ window.__ModuleLoader__.load({
|
|
|
3755
4047
|
}
|
|
3756
4048
|
});
|
|
3757
4049
|
}
|
|
4050
|
+
// src/mobile-layout-attributes.ts
|
|
4051
|
+
/**
|
|
4052
|
+
* Root marker the dedicated layout publishes for its wide/narrow band.
|
|
4053
|
+
*
|
|
4054
|
+
* The band depends on the device screen size, which a media query cannot read,
|
|
4055
|
+
* so the layout decides it in JS and writes this attribute on `.dshm-shell`:
|
|
4056
|
+
* `"1"` docks the sidebar, anything else — `"0"`, or no marker — is the overlay
|
|
4057
|
+
* drawer. The stylesheet and the sidebar scroll adapter both follow the marker,
|
|
4058
|
+
* so the band has exactly one owner.
|
|
4059
|
+
*
|
|
4060
|
+
* This module stays free of imports: the dedicated layout bundle and the native
|
|
4061
|
+
* client bundle both use it, and neither should pull the other in.
|
|
4062
|
+
*/
|
|
4063
|
+
const WIDE_LAYOUT_ATTRIBUTE = "data-dshm-wide";
|
|
3758
4064
|
// src/mobile-sidebar-scroll.ts
|
|
3759
4065
|
function sidebarRestoreTop(snapshot, anchorTop) {
|
|
3760
4066
|
return Math.max(0, anchorTop === void 0 || snapshot.offset === void 0 ? snapshot.top : anchorTop - snapshot.offset);
|
|
@@ -3785,7 +4091,16 @@ window.__ModuleLoader__.load({
|
|
|
3785
4091
|
function installMobileSidebarScrollMemory() {
|
|
3786
4092
|
const memories = /* @__PURE__ */ new Map();
|
|
3787
4093
|
const stockViewport = window.matchMedia("(max-width:720px)");
|
|
3788
|
-
|
|
4094
|
+
/**
|
|
4095
|
+
* Whether the dedicated layout currently renders the sidebar as an overlay
|
|
4096
|
+
* drawer. That is the layout's own decision on the shell — a landscape phone
|
|
4097
|
+
* is an overlay there although its viewport is wide — so a width query would
|
|
4098
|
+
* disable this memory exactly where the drawer is hardest to reach.
|
|
4099
|
+
*/
|
|
4100
|
+
const drawerBand = () => {
|
|
4101
|
+
const shell = document.querySelector(".dshm-shell");
|
|
4102
|
+
return shell === null || shell.getAttribute("data-dshm-wide") !== "1";
|
|
4103
|
+
};
|
|
3789
4104
|
let root;
|
|
3790
4105
|
let open = false;
|
|
3791
4106
|
let ports = /* @__PURE__ */ new Map();
|
|
@@ -3836,7 +4151,7 @@ window.__ModuleLoader__.load({
|
|
|
3836
4151
|
const sync = () => {
|
|
3837
4152
|
if (disposed) return;
|
|
3838
4153
|
const next = document.querySelector("[data-dsh-mobile-sidebar-root]") ?? void 0;
|
|
3839
|
-
const mobile = next?.closest("[data-pane=\"sidebar\"]") != null ?
|
|
4154
|
+
const mobile = next?.closest("[data-pane=\"sidebar\"]") != null ? drawerBand() : stockViewport.matches;
|
|
3840
4155
|
if (next !== root) {
|
|
3841
4156
|
cancel();
|
|
3842
4157
|
ports.clear();
|
|
@@ -3881,7 +4196,6 @@ window.__ModuleLoader__.load({
|
|
|
3881
4196
|
remember();
|
|
3882
4197
|
};
|
|
3883
4198
|
stockViewport.addEventListener("change", sync);
|
|
3884
|
-
dedicatedViewport.addEventListener("change", sync);
|
|
3885
4199
|
document.addEventListener("scroll", onScroll, true);
|
|
3886
4200
|
for (const type of [
|
|
3887
4201
|
"pointerdown",
|
|
@@ -3896,7 +4210,6 @@ window.__ModuleLoader__.load({
|
|
|
3896
4210
|
disposed = true;
|
|
3897
4211
|
cancel();
|
|
3898
4212
|
stockViewport.removeEventListener("change", sync);
|
|
3899
|
-
dedicatedViewport.removeEventListener("change", sync);
|
|
3900
4213
|
document.removeEventListener("scroll", onScroll, true);
|
|
3901
4214
|
for (const type of [
|
|
3902
4215
|
"pointerdown",
|
|
@@ -5696,7 +6009,8 @@ window.__ModuleLoader__.load({
|
|
|
5696
6009
|
"readonly",
|
|
5697
6010
|
"aria-busy",
|
|
5698
6011
|
"aria-selected",
|
|
5699
|
-
"data-dsh-mobile-session"
|
|
6012
|
+
"data-dsh-mobile-session",
|
|
6013
|
+
WIDE_LAYOUT_ATTRIBUTE
|
|
5700
6014
|
]
|
|
5701
6015
|
});
|
|
5702
6016
|
overlayQuery.addEventListener("change", schedule);
|
|
@@ -9516,11 +9830,15 @@ window.__ModuleLoader__.load({
|
|
|
9516
9830
|
};
|
|
9517
9831
|
return (0, react.createElement)("div", {
|
|
9518
9832
|
className: "dsh-mobile-settings_row",
|
|
9519
|
-
style: {
|
|
9833
|
+
style: {
|
|
9834
|
+
flexDirection: "column",
|
|
9835
|
+
alignItems: "stretch",
|
|
9836
|
+
gap: 12
|
|
9837
|
+
},
|
|
9520
9838
|
lang: locale === "zh" ? "zh-CN" : locale
|
|
9521
9839
|
}, (0, react.createElement)("div", {
|
|
9522
9840
|
className: "dsh-mobile-settings_rowText",
|
|
9523
|
-
style: { paddingRight:
|
|
9841
|
+
style: { paddingRight: 0 }
|
|
9524
9842
|
}, (0, react.createElement)("div", { className: "dsh-mobile-settings_title" }, copy.title), (0, react.createElement)("div", {
|
|
9525
9843
|
className: "dsh-mobile-settings_desc",
|
|
9526
9844
|
"aria-live": "polite"
|
|
@@ -10789,6 +11107,23 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
10789
11107
|
if (!escape.defaultPrevented) blocked();
|
|
10790
11108
|
return;
|
|
10791
11109
|
}
|
|
11110
|
+
if (top.getAttribute("data-shortcut-modal") === "sidebar" && top.closest("[data-sidebar-drawer]") !== null) {
|
|
11111
|
+
if (layers.some((layer) => layer !== top && !top.contains(layer))) {
|
|
11112
|
+
blocked();
|
|
11113
|
+
return;
|
|
11114
|
+
}
|
|
11115
|
+
const focused = page.activeElement;
|
|
11116
|
+
const target = focused !== null && top.contains(focused) ? focused : top;
|
|
11117
|
+
const escape = new KeyboardEvent("keydown", {
|
|
11118
|
+
key: "Escape",
|
|
11119
|
+
code: "Escape",
|
|
11120
|
+
bubbles: true,
|
|
11121
|
+
cancelable: true
|
|
11122
|
+
});
|
|
11123
|
+
target.dispatchEvent(escape);
|
|
11124
|
+
if (!escape.defaultPrevented) blocked();
|
|
11125
|
+
return;
|
|
11126
|
+
}
|
|
10792
11127
|
if (top.getAttribute("role") === "dialog") {
|
|
10793
11128
|
const wrapper = top.parentElement;
|
|
10794
11129
|
const mask = wrapper?.firstElementChild;
|
|
@@ -11147,7 +11482,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
11147
11482
|
mode: "attach",
|
|
11148
11483
|
entryTls: form.entryTls,
|
|
11149
11484
|
...form.entryTls === "self-signed" || form.vhostHttpPort === void 0 ? {} : { vhostHttpPort: form.vhostHttpPort },
|
|
11150
|
-
...form.publicPort === void 0 ? {} : { publicPort: form.publicPort }
|
|
11485
|
+
...form.publicPort === void 0 ? {} : { publicPort: form.publicPort },
|
|
11486
|
+
...form.user === void 0 || form.user === "" ? {} : { user: form.user }
|
|
11151
11487
|
});
|
|
11152
11488
|
}
|
|
11153
11489
|
/**
|
|
@@ -12071,6 +12407,16 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12071
12407
|
frpToken.autocomplete = "off";
|
|
12072
12408
|
frpToken.spellcheck = false;
|
|
12073
12409
|
frpToken.placeholder = t("frpTokenPlaceholder");
|
|
12410
|
+
const frpUserLabel = element("label", "dsh-mobile-control__field");
|
|
12411
|
+
frpUserLabel.textContent = t("frpUser");
|
|
12412
|
+
const frpUser = element("input");
|
|
12413
|
+
frpUser.type = "text";
|
|
12414
|
+
frpUser.autocomplete = "off";
|
|
12415
|
+
frpUser.spellcheck = false;
|
|
12416
|
+
frpUser.placeholder = t("frpUserPlaceholder");
|
|
12417
|
+
const frpUserHint = element("p", "dsh-mobile-control__frp-hint");
|
|
12418
|
+
frpUserHint.textContent = t("frpUserHint");
|
|
12419
|
+
frpUserLabel.append(frpUser, frpUserHint);
|
|
12074
12420
|
const frpOriginLabel = element("label", "dsh-mobile-control__field");
|
|
12075
12421
|
frpOriginLabel.textContent = t("frpPublicOrigin");
|
|
12076
12422
|
const frpOrigin = element("input");
|
|
@@ -12133,6 +12479,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12133
12479
|
serverAddress: frpServer,
|
|
12134
12480
|
serverPort: frpPort,
|
|
12135
12481
|
token: frpToken,
|
|
12482
|
+
user: frpUser,
|
|
12136
12483
|
publicOrigin: frpOrigin,
|
|
12137
12484
|
mode: frpMode,
|
|
12138
12485
|
entryTls: frpEntryTls,
|
|
@@ -12143,6 +12490,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12143
12490
|
serverAddress: frpServerLabel,
|
|
12144
12491
|
serverPort: frpPortLabel,
|
|
12145
12492
|
token: frpTokenLabel,
|
|
12493
|
+
user: frpUserLabel,
|
|
12146
12494
|
publicOrigin: frpOriginLabel,
|
|
12147
12495
|
mode: frpModeLabel,
|
|
12148
12496
|
entryTls: frpEntryTlsLabel,
|
|
@@ -12153,6 +12501,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12153
12501
|
serverAddress: "frpServerAddress",
|
|
12154
12502
|
serverPort: "frpServerPort",
|
|
12155
12503
|
token: "frpToken",
|
|
12504
|
+
user: "frpUser",
|
|
12156
12505
|
publicOrigin: "frpPublicOrigin",
|
|
12157
12506
|
mode: "frpMode",
|
|
12158
12507
|
entryTls: "frpEntryTls",
|
|
@@ -12175,7 +12524,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12175
12524
|
frpOriginHint.textContent = t("frpPublicOriginHint");
|
|
12176
12525
|
frpOriginLabel.append(frpOriginHint);
|
|
12177
12526
|
frpOrigin.setAttribute("aria-describedby", `${frpOriginHint.id} ${frpOrigin.getAttribute("aria-describedby") ?? ""}`);
|
|
12178
|
-
frpFields.append(frpServerLabel, frpPortLabel, frpTokenLabel, frpOriginLabel, frpModeLabel, frpEntryTlsLabel, frpVhostLabel, frpPublicPortLabel);
|
|
12527
|
+
frpFields.append(frpServerLabel, frpPortLabel, frpTokenLabel, frpUserLabel, frpOriginLabel, frpModeLabel, frpEntryTlsLabel, frpVhostLabel, frpPublicPortLabel);
|
|
12179
12528
|
frpStep1.append(frpStep1Title, frpStep1Text, frpFields);
|
|
12180
12529
|
const frpStep2 = element("section", "dsh-mobile-control__frp-step");
|
|
12181
12530
|
const frpStep2Title = element("strong");
|
|
@@ -12535,6 +12884,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12535
12884
|
const irohQr = element("div", "dsh-mobile-control__qr is-expandable");
|
|
12536
12885
|
irohQr.hidden = true;
|
|
12537
12886
|
const irohPairStatus = element("p", "dsh-mobile-control__component-status");
|
|
12887
|
+
irohPairStatus.setAttribute("aria-live", "polite");
|
|
12538
12888
|
const irohCopyDescriptor = element("button", "dsh-mobile-control__secondary");
|
|
12539
12889
|
irohCopyDescriptor.type = "button";
|
|
12540
12890
|
irohCopyDescriptor.textContent = t("copyPairLink");
|
|
@@ -12864,6 +13214,16 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12864
13214
|
disclosure.hidden = true;
|
|
12865
13215
|
setupDetailsRow.append(disclosure);
|
|
12866
13216
|
}
|
|
13217
|
+
const macosFolderHelp = element("details", "dsh-mobile-control__details");
|
|
13218
|
+
macosFolderHelp.hidden = true;
|
|
13219
|
+
const macosFolderHelpSummary = element("summary");
|
|
13220
|
+
macosFolderHelpSummary.textContent = t("macosFolderHelpTitle");
|
|
13221
|
+
const macosFolderHelpBody = element("div", "dsh-mobile-control__details-body");
|
|
13222
|
+
const macosFolderHelpNote = element("p");
|
|
13223
|
+
macosFolderHelpNote.textContent = t("macosFolderHelpNote");
|
|
13224
|
+
macosFolderHelpBody.append(macosFolderHelpNote);
|
|
13225
|
+
macosFolderHelp.append(macosFolderHelpSummary, macosFolderHelpBody);
|
|
13226
|
+
setupDetailsRow.append(macosFolderHelp);
|
|
12867
13227
|
remoteWorkspace.append(remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteQr, pairingValidity, providerInstallRow, remoteActions, remotePairLink, setupDetailsRow);
|
|
12868
13228
|
const diagnosticsView = element("div", "dsh-mobile-control__view is-diagnostics");
|
|
12869
13229
|
diagnosticsView.hidden = true;
|
|
@@ -13013,6 +13373,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13013
13373
|
let configuredFrpOrigin = "";
|
|
13014
13374
|
let configuredFrpVhostPort = 7080;
|
|
13015
13375
|
let configuredFrpPublicPort = 33080;
|
|
13376
|
+
let configuredFrpUser = "";
|
|
13377
|
+
let frpUserDraft = false;
|
|
13016
13378
|
/** Proxy name the host writes into its frpc.toml; the local clipboard copy must match. */
|
|
13017
13379
|
let configuredFrpProxyName;
|
|
13018
13380
|
let frpConfiguredMode = "deploy";
|
|
@@ -13390,6 +13752,17 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13390
13752
|
irohQr.hidden = false;
|
|
13391
13753
|
irohQr.append(placeholder);
|
|
13392
13754
|
};
|
|
13755
|
+
const clearIrohPairing = () => {
|
|
13756
|
+
activeIrohDescriptor = "";
|
|
13757
|
+
activeIrohPairExpiresAt = 0;
|
|
13758
|
+
activeIrohQrSvg = "";
|
|
13759
|
+
clearQrTarget(irohQr);
|
|
13760
|
+
irohQr.hidden = true;
|
|
13761
|
+
const wasZoomed = !irohQrZoom.hidden;
|
|
13762
|
+
setIrohQrZoomOpen(false);
|
|
13763
|
+
irohQrZoomImage.removeAttribute("src");
|
|
13764
|
+
if (wasZoomed && !irohPair.disabled && !irohPair.hidden) irohPair.focus();
|
|
13765
|
+
};
|
|
13393
13766
|
const showRemoteQrPlaceholder = (text) => {
|
|
13394
13767
|
clearQrTarget(remoteQr);
|
|
13395
13768
|
const placeholder = element("span", "dsh-mobile-control__qr-placeholder");
|
|
@@ -13661,6 +14034,9 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13661
14034
|
if (frpConfigured && !frpEntryTlsDraft) frpEntryTls.value = configuredFrpEntryTls;
|
|
13662
14035
|
if (typeof frpConfiguration.vhostHttpPort === "number") configuredFrpVhostPort = frpConfiguration.vhostHttpPort;
|
|
13663
14036
|
if (typeof frpConfiguration.publicPort === "number") configuredFrpPublicPort = frpConfiguration.publicPort;
|
|
14037
|
+
configuredFrpUser = typeof frpConfiguration.user === "string" ? frpConfiguration.user : "";
|
|
14038
|
+
if (!frpUserDraft) frpUser.value = configuredFrpUser;
|
|
14039
|
+
else if (frpUser.value.trim() === configuredFrpUser) frpUserDraft = false;
|
|
13664
14040
|
configuredFrpProxyName = typeof frpConfiguration.proxyName === "string" && frpConfiguration.proxyName !== "" ? frpConfiguration.proxyName : void 0;
|
|
13665
14041
|
if (!frpVhostDraft) {
|
|
13666
14042
|
if (frpMode.value === "attach" && configuredFrpMode === "attach" && configuredFrpEntryTls === "public-ip-cert") {
|
|
@@ -13825,6 +14201,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13825
14201
|
}[errorCode] ?? "";
|
|
13826
14202
|
const needsFunnelSetup = state === "error" && remoteSetupUrl !== "";
|
|
13827
14203
|
remoteReady = remoteRunning && state === "ready" && remoteOrigin !== "";
|
|
14204
|
+
macosFolderHelp.hidden = data.macosHost !== true || !remoteReady;
|
|
13828
14205
|
frpChoiceBadge.textContent = frpConfigured && frpInstalled ? t("ready") : t("advanced");
|
|
13829
14206
|
originChoiceBadge.textContent = managedOrigin ? t(remoteReady ? "ready" : "advanced") : originListening ? t("originBackendReady") : t("advanced");
|
|
13830
14207
|
if (remoteStatus.style) remoteStatus.style.display = state === "off" && remoteFailureText === "" ? "none" : "";
|
|
@@ -13839,7 +14216,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13839
14216
|
starting: cpolar ? t("remoteStartingCpolar") : cloudflared ? t("remoteStartingCloudflared") : frp ? t("remoteStartingFrp") : origin ? t("originStarting") : iroh ? t("irohStarting") : t("remoteStartingTailscale"),
|
|
13840
14217
|
"needs-login": t("remoteNeedsLogin"),
|
|
13841
14218
|
connecting: cpolar ? t("remoteConnectingCpolar") : cloudflared ? t("remoteConnectingCloudflared") : frp ? t("remoteConnectingFrp") : iroh ? t("irohConnecting") : t("remoteConnectingTailscale"),
|
|
13842
|
-
ready: origin ? t(managedOrigin ? "caddyVerified" : "originReady") : t("remoteReady"),
|
|
14219
|
+
ready: origin ? t(managedOrigin ? "caddyVerified" : "originReady") : tailscale ? t("funnelListenerReady") : t("remoteReady"),
|
|
13843
14220
|
error: t("remoteError")
|
|
13844
14221
|
};
|
|
13845
14222
|
const errorLabels = translatedRemoteErrors();
|
|
@@ -13877,15 +14254,15 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13877
14254
|
irohPair.disabled = !remoteReady || !iroh || remoteProviderBusy;
|
|
13878
14255
|
irohPair.hidden = !iroh;
|
|
13879
14256
|
irohCopyDescriptor.hidden = !iroh;
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
14257
|
+
const irohPairingExpired = activeIrohPairExpiresAt > 0 && activeIrohPairExpiresAt <= Date.now();
|
|
14258
|
+
if (activeIrohDescriptor !== "" && (!iroh || !irohInstalled || !remoteReady || irohPairingExpired)) {
|
|
14259
|
+
clearIrohPairing();
|
|
14260
|
+
irohPairStatus.textContent = iroh && irohInstalled && remoteReady && irohPairingExpired ? t("pairingExpired") : "";
|
|
14261
|
+
}
|
|
14262
|
+
if (!iroh || !irohInstalled) irohQr.hidden = true;
|
|
14263
|
+
else if (activeIrohQrSvg === "") {
|
|
14264
|
+
if (activeIrohDescriptor === "") showIrohQrPlaceholder();
|
|
14265
|
+
else irohQr.hidden = true;
|
|
13889
14266
|
}
|
|
13890
14267
|
irohCopyDescriptor.disabled = !iroh || !remoteReady || remoteProviderBusy || activeIrohDescriptor === "" || activeIrohPairExpiresAt <= Date.now();
|
|
13891
14268
|
if (origin) {
|
|
@@ -14074,8 +14451,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14074
14451
|
});
|
|
14075
14452
|
const openIrohPairing = async () => {
|
|
14076
14453
|
const generation = remotePairingGeneration;
|
|
14077
|
-
|
|
14078
|
-
activeIrohPairExpiresAt = 0;
|
|
14454
|
+
clearIrohPairing();
|
|
14079
14455
|
irohCopyDescriptor.disabled = true;
|
|
14080
14456
|
const data = await controlRequestJson("/api/mobile-access/remote/iroh/pairing/begin", {
|
|
14081
14457
|
method: "POST",
|
|
@@ -14086,12 +14462,12 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14086
14462
|
const qrSvg = typeof record.qrSvg === "string" ? record.qrSvg : "";
|
|
14087
14463
|
const code = typeof record.irohCode === "string" ? record.irohCode : "";
|
|
14088
14464
|
const expiresAt = typeof record.expiresAt === "number" && Number.isFinite(record.expiresAt) ? record.expiresAt : 0;
|
|
14089
|
-
|
|
14090
|
-
if (qrSvg === "" || code === "" || expiresAt <= Date.now()) {
|
|
14091
|
-
activeIrohQrSvg = "";
|
|
14465
|
+
if (!/^\d{6}$/u.test(code) || expiresAt <= Date.now()) {
|
|
14092
14466
|
irohPairStatus.textContent = t("keyGenerationFailed");
|
|
14093
14467
|
return;
|
|
14094
14468
|
}
|
|
14469
|
+
if (qrSvg === "") irohQr.hidden = true;
|
|
14470
|
+
else showQr(qrSvg, irohQr);
|
|
14095
14471
|
activeIrohQrSvg = qrSvg;
|
|
14096
14472
|
const fields = [
|
|
14097
14473
|
"version",
|
|
@@ -14109,7 +14485,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14109
14485
|
for (const field of fields) descriptor[field] = record[field];
|
|
14110
14486
|
activeIrohDescriptor = JSON.stringify(descriptor);
|
|
14111
14487
|
activeIrohPairExpiresAt = expiresAt;
|
|
14112
|
-
irohPairStatus.textContent = t("irohPairingReady", { code });
|
|
14488
|
+
irohPairStatus.textContent = qrSvg === "" ? t("irohPairingCopyOnly") : t("irohPairingReady", { code });
|
|
14113
14489
|
};
|
|
14114
14490
|
irohPair.addEventListener("click", () => {
|
|
14115
14491
|
if (remoteProviderBusy || !remoteReady || remoteProvider !== "iroh") return;
|
|
@@ -14427,6 +14803,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14427
14803
|
serverAddress: String(frpServer.value ?? "").trim(),
|
|
14428
14804
|
serverPort: Number(frpPort.value),
|
|
14429
14805
|
token: String(frpToken.value ?? ""),
|
|
14806
|
+
user: String(frpUser.value ?? "").trim(),
|
|
14430
14807
|
publicOrigin: String(frpOrigin.value ?? "").trim(),
|
|
14431
14808
|
mode,
|
|
14432
14809
|
entryTls: frpEntryTls.value === "self-signed" ? "self-signed" : "public-ip-cert",
|
|
@@ -14445,7 +14822,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14445
14822
|
publicOrigin: form.publicOrigin,
|
|
14446
14823
|
entryTls: form.entryTls,
|
|
14447
14824
|
...form.entryTls === "self-signed" || form.vhostHttpPort === void 0 ? {} : { vhostHttpPort: form.vhostHttpPort },
|
|
14448
|
-
...form.publicPort === void 0 ? {} : { publicPort: form.publicPort }
|
|
14825
|
+
...form.publicPort === void 0 ? {} : { publicPort: form.publicPort },
|
|
14826
|
+
...form.user === "" ? {} : { user: form.user }
|
|
14449
14827
|
});
|
|
14450
14828
|
const validFrpForm = (form) => {
|
|
14451
14829
|
return validateFrpForm(form).length === 0;
|
|
@@ -14476,6 +14854,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14476
14854
|
return issues.length === 0;
|
|
14477
14855
|
};
|
|
14478
14856
|
for (const [name, input] of Object.entries(frpInputs)) input.addEventListener(name === "mode" || name === "entryTls" ? "change" : "input", () => {
|
|
14857
|
+
if (name === "user") frpUserDraft = true;
|
|
14479
14858
|
if (frpValidationShown) renderFrpIssues(validateFrpForm(frpForm(), frpValidationAllowsSavedToken));
|
|
14480
14859
|
});
|
|
14481
14860
|
frpCopyTemplate.addEventListener("click", () => {
|
|
@@ -14658,7 +15037,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14658
15037
|
mode: form.mode,
|
|
14659
15038
|
entryTls: form.entryTls,
|
|
14660
15039
|
...form.vhostHttpPort === void 0 ? {} : { vhostHttpPort: form.vhostHttpPort },
|
|
14661
|
-
...form.publicPort === void 0 ? {} : { publicPort: form.publicPort }
|
|
15040
|
+
...form.publicPort === void 0 ? {} : { publicPort: form.publicPort },
|
|
15041
|
+
user: form.user
|
|
14662
15042
|
};
|
|
14663
15043
|
};
|
|
14664
15044
|
const validVpsFrpForm = () => {
|
|
@@ -14898,9 +15278,10 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14898
15278
|
frpConfigure.addEventListener("click", () => {
|
|
14899
15279
|
if (remoteProviderBusy || !frpInstalled) return;
|
|
14900
15280
|
const form = frpForm();
|
|
14901
|
-
const unchanged = frpConfigured && form.token === "" && form.serverAddress === configuredFrpServer && form.serverPort === configuredFrpPort && form.publicOrigin === configuredFrpOrigin && form.mode === (frpConfiguredMode === "attach" ? "attach" : "deploy") && form.entryTls === (frpConfiguredEntryTls === "self-signed" ? "self-signed" : "public-ip-cert") && (form.vhostHttpPort ?? configuredFrpVhostPort) === configuredFrpVhostPort && (form.publicPort ?? configuredFrpPublicPort) === configuredFrpPublicPort;
|
|
15281
|
+
const unchanged = frpConfigured && form.token === "" && form.serverAddress === configuredFrpServer && form.serverPort === configuredFrpPort && form.publicOrigin === configuredFrpOrigin && form.mode === (frpConfiguredMode === "attach" ? "attach" : "deploy") && form.entryTls === (frpConfiguredEntryTls === "self-signed" ? "self-signed" : "public-ip-cert") && (form.vhostHttpPort ?? configuredFrpVhostPort) === configuredFrpVhostPort && (form.publicPort ?? configuredFrpPublicPort) === configuredFrpPublicPort && form.user === configuredFrpUser;
|
|
14902
15282
|
if (!unchanged) {
|
|
14903
|
-
|
|
15283
|
+
const sameServer = frpConfigured && form.serverAddress === configuredFrpServer && form.serverPort === configuredFrpPort;
|
|
15284
|
+
if (!checkFrpForm(form, sameServer)) return;
|
|
14904
15285
|
}
|
|
14905
15286
|
renderFrpIssues([]);
|
|
14906
15287
|
remoteProviderBusy = true;
|
|
@@ -16919,7 +17300,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
16919
17300
|
style rule of its own. */
|
|
16920
17301
|
.dsh-mobile-control__cpolar-setup{margin:0 0 12px;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__cpolar-setup[hidden],.dsh-mobile-control__cpolar-account[hidden],.dsh-mobile-control__details[hidden],.dsh-mobile-control__view.is-remote .dsh-mobile-control__actions[hidden],.dsh-mobile-control__danger[hidden]{display:none}.dsh-mobile-control__component-status,.dsh-mobile-control__component-note{margin:0 0 10px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.55}.dsh-mobile-control__progress{box-sizing:border-box;width:100%;height:6px;margin:0 0 10px}.dsh-mobile-control__progress[hidden]{display:none}.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__primary{width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:600 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__cpolar-account{margin-top:10px}.dsh-mobile-control__link-row{display:flex;flex-wrap:wrap;gap:6px 12px;margin:0 0 10px}.dsh-mobile-control__text-link{color:var(--dsw-alias-label-primary-bluish,#2563eb);font-size:11px;text-decoration:none}.dsh-mobile-control__text-link:hover{text-decoration:underline}.dsh-mobile-control__token-label{display:flex;flex-direction:column;gap:5px;margin:0 0 8px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__token{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2,#fff));color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__cpolar-connect{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;min-height:44px;padding:10px 14px;border-radius:12px;font:650 13px/1.2 system-ui;cursor:pointer;transition:background-color 160ms ease,border-color 160ms ease,opacity 160ms ease}.dsh-mobile-control__cpolar-connect:hover:not(:disabled){border-color:#1d4ed8;background:#1d4ed8}.dsh-mobile-control__cpolar-connect:active:not(:disabled){border-color:#1e40af;background:#1e40af}.dsh-mobile-control__cpolar-connect:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__details{margin:10px 0 0;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb);padding-top:9px}.dsh-mobile-control__details>summary{min-height:36px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:30px;cursor:pointer}.dsh-mobile-control__details-body{display:flex;flex-wrap:wrap;align-items:center;gap:7px 12px;padding:4px 0}.dsh-mobile-control__details-body p{flex:1 0 100%;margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__storage{display:block;flex:1 0 100%;max-width:100%;overflow:hidden;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#475569);font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__danger{flex:1 0 100%;min-height:44px;margin-top:3px;padding:7px 10px;border:1px solid var(--dsw-alias-state-error-primary,#dc2626);border-radius:9px;background:transparent;color:var(--dsw-alias-label-primary,#dc2626);font:12px/1.3 system-ui;cursor:pointer}
|
|
16921
17302
|
.dsh-mobile-control__danger:hover:not(:disabled),.dsh-mobile-control__device-revoke:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgb(220 38 38 / 8%))}
|
|
16922
|
-
.dsh-mobile-control__frp-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-setup[hidden]{display:none}.dsh-mobile-control__frp-step{padding:11px 0}.dsh-mobile-control__frp-step + .dsh-mobile-control__frp-step{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__frp-step>strong{display:block;margin-bottom:3px;font-size:12px;line-height:1.4}.dsh-mobile-control__frp-step>p{margin:0 0 9px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes{margin:0 0 9px;padding:0;list-style:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes li{position:relative;margin:0;padding-left:12px}.dsh-mobile-control__frp-changes li+li{margin-top:5px}.dsh-mobile-control__frp-changes li::before{content:"•";position:absolute;left:2px;color:var(--dsw-alias-label-tertiary,#98a1ad)}.dsh-mobile-control__frp-step>.dsh-mobile-control__frp-requirement{padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:11px}.dsh-mobile-control__frp-fields{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:8px}.dsh-mobile-control__frp-fields[hidden]{display:none}.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(5),.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(6){grid-column:1/-1}.dsh-mobile-control__frp-fields>.dsh-mobile-control__field[hidden]{display:none}.dsh-mobile-control__frp-fields select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__frp-fields select:disabled{cursor:not-allowed;opacity:.55}.dsh-mobile-control__frp-hint{margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__field{display:flex;min-width:0;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__field:nth-child(3),.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__field input{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__frp-action{box-sizing:border-box;width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__frp-action:disabled{cursor:not-allowed;opacity:.5}.dsh-mobile-control__remote-workspace{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f7f8fa)}.dsh-mobile-control__stage-header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:9px}.dsh-mobile-control__stage-header .dsh-mobile-control__section-title{margin:0}.dsh-mobile-control__stage-meta{display:flex;min-width:0;align-items:center;justify-content:flex-end;gap:5px}.dsh-mobile-control__stage-value{max-width:115px;overflow:hidden;color:var(--dsw-alias-label-primary,#16181d);font:650 11px/1.3 system-ui;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__state-badge{flex:none;padding:3px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#606873);font:650 11px/1.25 system-ui}.dsh-mobile-control__state-badge.is-ready{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsw-alias-label-primary,#087454)}.dsh-mobile-control__state-badge.is-busy{background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8)}.dsh-mobile-control__state-badge.is-attention{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsw-alias-label-primary,#935100)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__status{box-sizing:border-box;margin:0 0 10px;padding:9px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:11px;line-height:1.45}.dsh-mobile-control__provider-setup-body{margin:0 0 10px}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup{margin:0}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__section-title,.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__frp-setup>.dsh-mobile-control__section-title{display:none}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__details{margin:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__actions{margin-top:2px}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__qr{margin:10px 0 0}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__manage-row{margin-top:10px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}
|
|
17303
|
+
.dsh-mobile-control__frp-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-setup[hidden]{display:none}.dsh-mobile-control__frp-step{padding:11px 0}.dsh-mobile-control__frp-step + .dsh-mobile-control__frp-step{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__frp-step>strong{display:block;margin-bottom:3px;font-size:12px;line-height:1.4}.dsh-mobile-control__frp-step>p{margin:0 0 9px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes{margin:0 0 9px;padding:0;list-style:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes li{position:relative;margin:0;padding-left:12px}.dsh-mobile-control__frp-changes li+li{margin-top:5px}.dsh-mobile-control__frp-changes li::before{content:"•";position:absolute;left:2px;color:var(--dsw-alias-label-tertiary,#98a1ad)}.dsh-mobile-control__frp-step>.dsh-mobile-control__frp-requirement{padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:11px}.dsh-mobile-control__frp-fields{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:8px}.dsh-mobile-control__frp-fields[hidden]{display:none}.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(5),.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(6),.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(7){grid-column:1/-1}.dsh-mobile-control__frp-fields>.dsh-mobile-control__field[hidden]{display:none}.dsh-mobile-control__frp-fields select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__frp-fields select:disabled{cursor:not-allowed;opacity:.55}.dsh-mobile-control__frp-hint{margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__field{display:flex;min-width:0;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__field:nth-child(3),.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__field input{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__frp-action{box-sizing:border-box;width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__frp-action:disabled{cursor:not-allowed;opacity:.5}.dsh-mobile-control__remote-workspace{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f7f8fa)}.dsh-mobile-control__stage-header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:9px}.dsh-mobile-control__stage-header .dsh-mobile-control__section-title{margin:0}.dsh-mobile-control__stage-meta{display:flex;min-width:0;align-items:center;justify-content:flex-end;gap:5px}.dsh-mobile-control__stage-value{max-width:115px;overflow:hidden;color:var(--dsw-alias-label-primary,#16181d);font:650 11px/1.3 system-ui;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__state-badge{flex:none;padding:3px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#606873);font:650 11px/1.25 system-ui}.dsh-mobile-control__state-badge.is-ready{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsw-alias-label-primary,#087454)}.dsh-mobile-control__state-badge.is-busy{background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8)}.dsh-mobile-control__state-badge.is-attention{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsw-alias-label-primary,#935100)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__status{box-sizing:border-box;margin:0 0 10px;padding:9px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:11px;line-height:1.45}.dsh-mobile-control__provider-setup-body{margin:0 0 10px}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup{margin:0}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__section-title,.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__frp-setup>.dsh-mobile-control__section-title{display:none}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__details{margin:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__actions{margin-top:2px}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__qr{margin:10px 0 0}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__manage-row{margin-top:10px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}
|
|
16923
17304
|
.dsh-mobile-control__frp-overview{display:grid;grid-template-columns:32px minmax(0,1fr);align-items:center;gap:10px;margin:0 0 10px;padding:10px;border:1px solid var(--dsw-alias-state-success-primary,#a9dfc9);border-radius:11px;background:var(--dsw-alias-state-success-tertiary,#edf9f4)}.dsh-mobile-control__frp-overview[hidden]{display:none}.dsh-mobile-control__frp-overview-mark{display:grid;width:32px;height:32px;place-items:center;border-radius:50%;background:#087454;color:#fff;font:700 15px/1 system-ui}.dsh-mobile-control__frp-overview-body{display:flex;min-width:0;flex-direction:column;gap:2px}.dsh-mobile-control__frp-overview-body strong{color:var(--dsw-alias-label-primary,#075d46);font-size:12px;line-height:1.35}.dsh-mobile-control__frp-overview-body span{overflow:hidden;color:var(--dsw-alias-label-secondary,#357061);font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__frp-group{margin:8px 0 0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__frp-group>summary{box-sizing:border-box;min-height:44px;padding:12px 34px 10px 12px;color:var(--dsw-alias-label-primary,#16181d);font:650 12px/1.4 system-ui;cursor:pointer}.dsh-mobile-control__frp-group[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l2,#e1e5eb);background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step{padding:12px}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step>strong:first-child{display:none}.dsh-mobile-control__frp-group .dsh-mobile-control__frp-step{border-top:0}.dsh-mobile-control__frp-setup>.dsh-mobile-control__frp-step{margin-bottom:8px;padding:10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__tunnel{margin:0 0 10px}.dsh-mobile-control__tunnel[hidden]{display:none}
|
|
16924
17305
|
.dsh-mobile-control__lan-setup{box-sizing:border-box;margin:0 0 12px;padding:14px;border:1px solid #6f96db;border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f5f8ff)}.dsh-mobile-control__lan-setup[hidden],.dsh-mobile-control__lan-setup-form[hidden]{display:none}.dsh-mobile-control__lan-setup>.dsh-mobile-control__section-title{margin-bottom:5px}.dsh-mobile-control__lan-setup>.dsh-mobile-control__intro{margin-bottom:12px}.dsh-mobile-control__lan-setup-form{display:grid;gap:9px}.dsh-mobile-control__lan-setup .dsh-mobile-control__field select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__lan-setup .dsh-mobile-control__component-note{margin:0}.dsh-mobile-control__lan-setup-actions{display:grid;grid-template-columns:1fr 1.35fr;gap:8px}.dsh-mobile-control__lan-setup-actions button{box-sizing:border-box;min-width:0;min-height:44px;padding:9px 10px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__lan-setup-actions button:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status{margin:10px 0 0;line-height:1.5}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status:empty{display:none}
|
|
16925
17306
|
.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:":"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:var(--dsw-alias-label-primary-bluish,#2563eb);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}.dsh-mobile-control__qr{display:flex;justify-content:center;margin:0 0 12px}.dsh-mobile-control__qr[hidden]{display:none}.dsh-mobile-control__qr img{border-radius:12px;background:#fff;padding:8px}.dsh-mobile-control__qr.is-expandable{cursor:zoom-in}.dsh-mobile-control__qr.is-expandable:focus-visible{outline:3px solid var(--dsw-alias-state-business-primary,#2563eb);outline-offset:2px;border-radius:12px}.dsh-mobile-control__qr-zoom{position:fixed;inset:0;z-index:1001;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.72)}.dsh-mobile-control__qr-zoom[hidden]{display:none}.dsh-mobile-control__qr-zoom-image{width:min(84vw,360px);height:min(84vw,360px);border-radius:12px;background:#fff;padding:12px}.dsh-mobile-control__qr-zoom-close{position:absolute;top:12px;right:16px;min-width:44px;min-height:44px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:600 18px/1 system-ui;cursor:pointer}
|