dsh-mobile 0.6.13 → 0.6.14
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 +11 -4
- package/CONTRIBUTORS.md +10 -2
- package/README.en.md +10 -4
- package/README.md +10 -4
- package/SECURITY.md +1 -1
- package/docs/SELF_HOSTED_ORIGIN.en.md +34 -6
- package/docs/SELF_HOSTED_ORIGIN.md +34 -6
- package/docs/SLOW_CONNECTIONS.en.md +3 -3
- package/docs/SLOW_CONNECTIONS.md +3 -3
- package/lib/client.js +96 -31
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.mjs +24 -14
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/packages/question-fixes/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1219,10 +1219,10 @@ window.__ModuleLoader__.load({
|
|
|
1219
1219
|
originDescription: "Use managed Caddy or your existing HTTPS reverse proxy with DSH Mobile’s private, authenticated backend.",
|
|
1220
1220
|
prepareOrigin: "Connect your HTTPS reverse proxy",
|
|
1221
1221
|
originPublicOrigin: "Public HTTPS address",
|
|
1222
|
-
originListenHost: "Private listen IPv4",
|
|
1222
|
+
originListenHost: "Private listen IP (IPv4 / IPv6)",
|
|
1223
1223
|
originListenPort: "HTTP backend port",
|
|
1224
1224
|
originAllowedCidrs: "Allowed proxy source CIDRs",
|
|
1225
|
-
originHelp: "Same computer: use 127.0.0.1.
|
|
1225
|
+
originHelp: "Same computer: use 127.0.0.1 or ::1. Across a private network, use this computer’s private IPv4 or IPv6 ULA and allow the proxy’s direct source with /32 or /128 in the same address family. This is the NGINX-to-DSH connection, not the phone’s public IP. Separate CIDRs with spaces or commas.",
|
|
1226
1226
|
originWarning: "Keep this HTTP backend private. Never expose or port-forward it to the Internet. Your proxy must terminate HTTPS and preserve the external Host, including its port, and WebSocket upgrades.",
|
|
1227
1227
|
originSaveStart: "Save and start backend",
|
|
1228
1228
|
originBackendSaved: "Saved HTTP backend (not running)",
|
|
@@ -1257,15 +1257,15 @@ window.__ModuleLoader__.load({
|
|
|
1257
1257
|
reconnectingOrigin: "Restarting the private HTTP backend…",
|
|
1258
1258
|
resetOriginConfirm: "Stop this backend and reset remote pairing? All paired remote devices must pair again. Reverse-proxy settings and LAN devices are kept.",
|
|
1259
1259
|
originPublicOriginInvalid: "Enter a public HTTPS origin only, such as https://phone.example.com:8815; no path, query, credentials, or private address.",
|
|
1260
|
-
originListenHostInvalid: "Use
|
|
1260
|
+
originListenHostInvalid: "Use this computer’s explicit loopback/private IPv4, ::1, or IPv6 ULA (fc00::/7). Wildcard and public addresses are not allowed.",
|
|
1261
1261
|
originListenPortInvalid: "Use a whole-number port from 1024 to 65535; lower ports need privileges the plugin does not have.",
|
|
1262
1262
|
originListenPortReserved: "Port 3443 belongs to the LAN gateway and port 3080 to DSH itself, so both are always taken. Choose another port, such as 3444 or 3445.",
|
|
1263
|
-
originAllowedCidrsInvalid: "Allow 1–16
|
|
1263
|
+
originAllowedCidrsInvalid: "Allow 1–16 canonical loopback/private CIDRs in the listener’s address family. For a private-network listener, include a non-loopback proxy source, preferably IPv4/32 or IPv6/128. IPv6 supports ::1/128 and ULA (fc00::/7); public ranges are not accepted.",
|
|
1264
1264
|
originSettingsInvalid: "The reverse-proxy settings are invalid. Check the four fields and save again.",
|
|
1265
1265
|
originConfigMissing: "Save the reverse-proxy settings before starting the backend.",
|
|
1266
1266
|
originConfigInvalid: "The saved settings are invalid. Correct and save them again, or clear only these settings.",
|
|
1267
1267
|
originPortInUse: "The backend port is already in use. Stop the conflicting listener or choose another port. Note that 3443, 3080, and a port already used by a cloudflared tunnel are never free.",
|
|
1268
|
-
originAddressUnavailable: "The listen address is not available on this computer. Choose its current private IPv4.",
|
|
1268
|
+
originAddressUnavailable: "The listen address is not available on this computer. Choose its current private IPv4, ::1, or IPv6 ULA.",
|
|
1269
1269
|
originStartFailed: "The private backend could not start. Check its address, port, permissions, and local logs.",
|
|
1270
1270
|
originConfigTargetInvalid: "The saved proxy settings file is not a regular file. Remove it and save the settings again.",
|
|
1271
1271
|
originControllerUnavailable: "The reverse-proxy provider is not available in this session. Restart DSH and try again."
|
|
@@ -1278,10 +1278,10 @@ window.__ModuleLoader__.load({
|
|
|
1278
1278
|
originDescription: "Usa Caddy gestito o il tuo reverse proxy HTTPS con il backend privato autenticato di DSH Mobile.",
|
|
1279
1279
|
prepareOrigin: "Collega il tuo reverse proxy HTTPS",
|
|
1280
1280
|
originPublicOrigin: "Indirizzo HTTPS pubblico",
|
|
1281
|
-
originListenHost: "
|
|
1281
|
+
originListenHost: "IP privato di ascolto (IPv4 / IPv6)",
|
|
1282
1282
|
originListenPort: "Porta backend HTTP",
|
|
1283
1283
|
originAllowedCidrs: "CIDR sorgente del proxy consentiti",
|
|
1284
|
-
originHelp: "Stesso computer: usa 127.0.0.1
|
|
1284
|
+
originHelp: "Stesso computer: usa 127.0.0.1 o ::1. Su una rete privata usa l’IPv4 privato o l’IPv6 ULA di questo computer e consenti la sorgente diretta del proxy con /32 o /128 nella stessa famiglia. Si tratta della connessione NGINX–DSH, non dell’IP pubblico del telefono. Separa i CIDR con spazi o virgole.",
|
|
1285
1285
|
originWarning: "Mantieni privato il backend HTTP. Non esporlo né inoltrarne la porta su Internet. Il proxy deve terminare HTTPS e conservare l’Host esterno, inclusa la porta, e gli upgrade WebSocket.",
|
|
1286
1286
|
originSaveStart: "Salva e avvia backend",
|
|
1287
1287
|
originBackendSaved: "Backend HTTP salvato (non attivo)",
|
|
@@ -1316,15 +1316,15 @@ window.__ModuleLoader__.load({
|
|
|
1316
1316
|
reconnectingOrigin: "Riavvio del backend HTTP privato…",
|
|
1317
1317
|
resetOriginConfirm: "Fermare il backend e reimpostare gli abbinamenti remoti? Tutti i dispositivi remoti dovranno essere abbinati di nuovo. Le impostazioni proxy e i dispositivi LAN vengono conservati.",
|
|
1318
1318
|
originPublicOriginInvalid: "Inserisci solo un’origine HTTPS pubblica, ad esempio https://phone.example.com:8815, senza percorso, query, credenziali o indirizzo privato.",
|
|
1319
|
-
originListenHostInvalid: "Usa
|
|
1319
|
+
originListenHostInvalid: "Usa l’IPv4 loopback/privato, ::1 o un IPv6 ULA (fc00::/7) esplicito di questo computer. Indirizzi jolly e pubblici non sono ammessi.",
|
|
1320
1320
|
originListenPortInvalid: "Usa una porta intera da 1024 a 65535; le porte più basse richiedono privilegi che il plugin non ha.",
|
|
1321
1321
|
originListenPortReserved: "La porta 3443 appartiene al gateway LAN e la 3080 a DSH stesso, quindi sono sempre occupate. Scegli un’altra porta, ad esempio 3444 o 3445.",
|
|
1322
|
-
originAllowedCidrsInvalid: "Consenti 1–16 CIDR
|
|
1322
|
+
originAllowedCidrsInvalid: "Consenti 1–16 CIDR loopback/privati canonici della stessa famiglia dell’ascolto. Su rete privata includi una sorgente proxy non loopback, preferibilmente IPv4/32 o IPv6/128. IPv6 supporta ::1/128 e ULA (fc00::/7); le reti pubbliche non sono ammesse.",
|
|
1323
1323
|
originSettingsInvalid: "Impostazioni reverse proxy non valide. Controlla i quattro campi e salva di nuovo.",
|
|
1324
1324
|
originConfigMissing: "Salva le impostazioni reverse proxy prima di avviare il backend.",
|
|
1325
1325
|
originConfigInvalid: "Impostazioni salvate non valide. Correggile e salvale di nuovo, oppure cancella solo queste impostazioni.",
|
|
1326
1326
|
originPortInUse: "La porta backend è già occupata. Ferma il processo in conflitto o scegli un’altra porta. 3443, 3080 e una porta già usata da un tunnel cloudflared non sono mai libere.",
|
|
1327
|
-
originAddressUnavailable: "L’indirizzo di ascolto non è disponibile su questo computer. Scegli il suo IPv4 privato attuale.",
|
|
1327
|
+
originAddressUnavailable: "L’indirizzo di ascolto non è disponibile su questo computer. Scegli il suo IPv4 privato attuale, ::1 o un IPv6 ULA.",
|
|
1328
1328
|
originStartFailed: "Impossibile avviare il backend privato. Controlla indirizzo, porta, permessi e log locali.",
|
|
1329
1329
|
originConfigTargetInvalid: "Il file delle impostazioni proxy salvato non è un file regolare. Rimuovilo e salva di nuovo le impostazioni.",
|
|
1330
1330
|
originControllerUnavailable: "Il provider reverse proxy non è disponibile in questa sessione. Riavvia DSH e riprova."
|
|
@@ -1337,10 +1337,10 @@ window.__ModuleLoader__.load({
|
|
|
1337
1337
|
originDescription: "使用托管 Caddy 或已有 HTTPS 反向代理,接入 DSH Mobile 带认证的私有后端。",
|
|
1338
1338
|
prepareOrigin: "接入自有 HTTPS 反向代理",
|
|
1339
1339
|
originPublicOrigin: "公网 HTTPS 地址",
|
|
1340
|
-
originListenHost: "私有监听 IPv4",
|
|
1340
|
+
originListenHost: "私有监听 IP(IPv4 / IPv6)",
|
|
1341
1341
|
originListenPort: "HTTP 后端端口",
|
|
1342
1342
|
originAllowedCidrs: "允许的代理来源 CIDR",
|
|
1343
|
-
originHelp: "
|
|
1343
|
+
originHelp: "同机代理用 127.0.0.1 或 ::1;跨私网代理填写本机私有 IPv4 或 IPv6 ULA,并允许同一地址族的代理直连来源 /32 或 /128。这里检查 NGINX 到 DSH 的连接,不是手机的公网 IP。多个 CIDR 用空格或逗号分隔。",
|
|
1344
1344
|
originWarning: "HTTP 后端必须留在私网,绝不能直接暴露公网或做公网端口映射。反向代理负责 HTTPS,并须保留外部 Host(含端口)及 WebSocket 升级。",
|
|
1345
1345
|
originSaveStart: "保存并启动后端",
|
|
1346
1346
|
originBackendSaved: "已保存的 HTTP 后端(未运行)",
|
|
@@ -1375,15 +1375,15 @@ window.__ModuleLoader__.load({
|
|
|
1375
1375
|
reconnectingOrigin: "正在重启私有 HTTP 后端…",
|
|
1376
1376
|
resetOriginConfirm: "停止此后端并重置远程配对?所有已配对的远程设备都需要重新配对;反向代理配置和局域网设备保留。",
|
|
1377
1377
|
originPublicOriginInvalid: "只填写公网 HTTPS 源地址,例如 https://phone.example.com:8815;不能含路径、查询参数、账号密码或私有地址。",
|
|
1378
|
-
originListenHostInvalid: "
|
|
1378
|
+
originListenHostInvalid: "填写本机明确的回环/私有 IPv4、::1 或 IPv6 ULA(fc00::/7),不能使用全接口监听或公网地址。",
|
|
1379
1379
|
originListenPortInvalid: "端口必须是 1024–65535 的整数;更低的端口需要插件没有的特权。",
|
|
1380
1380
|
originListenPortReserved: "3443 属于局域网网关、3080 属于 DSH 自身,两者始终被占用。请换一个端口,例如 3444 或 3445。",
|
|
1381
|
-
originAllowedCidrsInvalid: "填写 1–16
|
|
1381
|
+
originAllowedCidrsInvalid: "填写 1–16 个与监听地址同族的规范回环/私有 CIDR;私网监听须包含非回环代理来源,推荐 IPv4/32 或 IPv6/128。IPv6 支持 ::1/128 和 ULA(fc00::/7),不接受公网网段。",
|
|
1382
1382
|
originSettingsInvalid: "反向代理配置无效,请检查四个字段后重新保存。",
|
|
1383
1383
|
originConfigMissing: "请先保存反向代理配置,再启动后端。",
|
|
1384
1384
|
originConfigInvalid: "已保存的配置无效,请修正并重新保存,或仅清除这项配置。",
|
|
1385
1385
|
originPortInUse: "后端端口已被占用,请停止冲突进程或改用其他端口。3443、3080 以及已被 cloudflared 隧道占用的端口永远不会空闲。",
|
|
1386
|
-
originAddressUnavailable: "本机没有这个监听地址,请选择当前本机的私有 IPv4。",
|
|
1386
|
+
originAddressUnavailable: "本机没有这个监听地址,请选择当前本机的私有 IPv4、::1 或 IPv6 ULA。",
|
|
1387
1387
|
originStartFailed: "私有后端启动失败,请检查地址、端口、权限和本地日志。",
|
|
1388
1388
|
originConfigTargetInvalid: "已保存的代理配置文件不是普通文件。请移除后重新保存设置。",
|
|
1389
1389
|
originControllerUnavailable: "当前会话里反向代理提供方不可用。请重启 DSH 后重试。"
|
|
@@ -3861,7 +3861,7 @@ window.__ModuleLoader__.load({
|
|
|
3861
3861
|
}
|
|
3862
3862
|
/**
|
|
3863
3863
|
* Maximum gap between input or scrolling events before an unused loading intent expires.
|
|
3864
|
-
* A held
|
|
3864
|
+
* A held pending intent expires without loading; a fresh gesture can create another intent.
|
|
3865
3865
|
*/
|
|
3866
3866
|
const HISTORY_SCROLL_QUIET_MS = 1600;
|
|
3867
3867
|
/**
|
|
@@ -3908,9 +3908,21 @@ window.__ModuleLoader__.load({
|
|
|
3908
3908
|
* that compensation as another upward fling, clicked "Load earlier" again, and shook
|
|
3909
3909
|
* the view in a chain. Only fresh input arms the gate, one arm buys at most one
|
|
3910
3910
|
* successful load, and an arm with no further input expires quietly.
|
|
3911
|
+
*
|
|
3912
|
+
* A touch-driven eligible scroll never clicks while the gesture is live: DSH retains
|
|
3913
|
+
* a paging anchor while a page loads, and its preservation writes fight a drag still
|
|
3914
|
+
* in progress. The gate records a pending intent instead and spends it only once the
|
|
3915
|
+
* gesture settles — a `scrollend` after every owned contact ends, or the quiet lease
|
|
3916
|
+
* expiring with no contact listed — and only while the reader still holds the top
|
|
3917
|
+
* zone. The intent dies with its arm: it is never spendable after the arm ends, and
|
|
3918
|
+
* the lease re-anchors ownership instead of clicking under a listed finger. Wheel
|
|
3919
|
+
* input outside a touch gesture keeps the immediate click: a wheel flick has no held
|
|
3920
|
+
* gesture to fight.
|
|
3911
3921
|
*/
|
|
3912
3922
|
function createHistoryAutoLoadGate(hooks) {
|
|
3913
3923
|
let armed = false;
|
|
3924
|
+
let armedByWheel = false;
|
|
3925
|
+
let pendingIntent = false;
|
|
3914
3926
|
const touchIds = /* @__PURE__ */ new Set();
|
|
3915
3927
|
let disposed = false;
|
|
3916
3928
|
let previousTop = hooks.getTop();
|
|
@@ -3922,10 +3934,32 @@ window.__ModuleLoader__.load({
|
|
|
3922
3934
|
hooks.cancel(quietId);
|
|
3923
3935
|
quietId = void 0;
|
|
3924
3936
|
};
|
|
3937
|
+
/**
|
|
3938
|
+
* Spend a pending loading intent only after every owned contact ends in the top zone.
|
|
3939
|
+
* @returns whether a load was issued.
|
|
3940
|
+
*/
|
|
3941
|
+
const settle = () => {
|
|
3942
|
+
if (!pendingIntent || touchIds.size > 0) return false;
|
|
3943
|
+
if (hooks.getTop() > AUTO_HISTORY_THRESHOLD_PX) {
|
|
3944
|
+
pendingIntent = false;
|
|
3945
|
+
return false;
|
|
3946
|
+
}
|
|
3947
|
+
if (!hooks.tryLoad()) return false;
|
|
3948
|
+
pendingIntent = false;
|
|
3949
|
+
return true;
|
|
3950
|
+
};
|
|
3925
3951
|
const onQuiet = () => {
|
|
3926
3952
|
quietPending = false;
|
|
3927
3953
|
quietId = void 0;
|
|
3954
|
+
if (touchIds.size > 0) {
|
|
3955
|
+
if (pendingIntent) touchIds.clear();
|
|
3956
|
+
pendingIntent = false;
|
|
3957
|
+
armed = false;
|
|
3958
|
+
return;
|
|
3959
|
+
}
|
|
3960
|
+
settle();
|
|
3928
3961
|
armed = false;
|
|
3962
|
+
pendingIntent = false;
|
|
3929
3963
|
};
|
|
3930
3964
|
const restartQuiet = () => {
|
|
3931
3965
|
cancelQuiet();
|
|
@@ -3933,18 +3967,24 @@ window.__ModuleLoader__.load({
|
|
|
3933
3967
|
quietPending = true;
|
|
3934
3968
|
quietId = hooks.schedule(onQuiet, HISTORY_SCROLL_QUIET_MS);
|
|
3935
3969
|
};
|
|
3936
|
-
const arm = () => {
|
|
3970
|
+
const arm = (byWheel) => {
|
|
3937
3971
|
armed = true;
|
|
3972
|
+
armedByWheel = byWheel;
|
|
3938
3973
|
restartQuiet();
|
|
3939
3974
|
};
|
|
3940
3975
|
const onScroll = () => {
|
|
3941
3976
|
const top = hooks.getTop();
|
|
3942
3977
|
const eligible = armed && hooks.shouldLoad(previousTop, top);
|
|
3943
3978
|
previousTop = top;
|
|
3944
|
-
if (eligible
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3979
|
+
if (eligible) {
|
|
3980
|
+
if (armedByWheel) {
|
|
3981
|
+
if (hooks.tryLoad()) {
|
|
3982
|
+
armed = false;
|
|
3983
|
+
pendingIntent = false;
|
|
3984
|
+
cancelQuiet();
|
|
3985
|
+
return;
|
|
3986
|
+
}
|
|
3987
|
+
} else pendingIntent = true;
|
|
3948
3988
|
}
|
|
3949
3989
|
if (armed) restartQuiet();
|
|
3950
3990
|
};
|
|
@@ -3952,15 +3992,20 @@ window.__ModuleLoader__.load({
|
|
|
3952
3992
|
handleEvent: (event) => {
|
|
3953
3993
|
if (disposed) return;
|
|
3954
3994
|
switch (event.type) {
|
|
3955
|
-
case "touchstart":
|
|
3956
|
-
|
|
3957
|
-
if (
|
|
3995
|
+
case "touchstart": {
|
|
3996
|
+
const changed = Array.from(event.changedTouches ?? []);
|
|
3997
|
+
if (changed.length === 0) break;
|
|
3998
|
+
if (touchIds.size === 0) pendingIntent = false;
|
|
3999
|
+
for (const touch of changed) touchIds.add(touch.identifier);
|
|
4000
|
+
arm(false);
|
|
3958
4001
|
break;
|
|
4002
|
+
}
|
|
3959
4003
|
case "touchmove":
|
|
3960
|
-
if (Array.from(event.changedTouches ?? []).some((touch) => touchIds.has(touch.identifier))) arm();
|
|
4004
|
+
if (Array.from(event.changedTouches ?? []).some((touch) => touchIds.has(touch.identifier))) arm(false);
|
|
3961
4005
|
break;
|
|
3962
4006
|
case "wheel":
|
|
3963
|
-
|
|
4007
|
+
if (touchIds.size === 0) pendingIntent = false;
|
|
4008
|
+
arm(touchIds.size === 0);
|
|
3964
4009
|
break;
|
|
3965
4010
|
case "touchend":
|
|
3966
4011
|
case "touchcancel": {
|
|
@@ -3970,6 +4015,15 @@ window.__ModuleLoader__.load({
|
|
|
3970
4015
|
break;
|
|
3971
4016
|
}
|
|
3972
4017
|
case "scrollend":
|
|
4018
|
+
if (pendingIntent && settle()) {
|
|
4019
|
+
armed = false;
|
|
4020
|
+
cancelQuiet();
|
|
4021
|
+
break;
|
|
4022
|
+
}
|
|
4023
|
+
if (pendingIntent) {
|
|
4024
|
+
restartQuiet();
|
|
4025
|
+
break;
|
|
4026
|
+
}
|
|
3973
4027
|
armed = false;
|
|
3974
4028
|
cancelQuiet();
|
|
3975
4029
|
break;
|
|
@@ -3978,6 +4032,8 @@ window.__ModuleLoader__.load({
|
|
|
3978
4032
|
},
|
|
3979
4033
|
reset: (top) => {
|
|
3980
4034
|
armed = false;
|
|
4035
|
+
armedByWheel = false;
|
|
4036
|
+
pendingIntent = false;
|
|
3981
4037
|
touchIds.clear();
|
|
3982
4038
|
previousTop = top;
|
|
3983
4039
|
cancelQuiet();
|
|
@@ -3985,20 +4041,27 @@ window.__ModuleLoader__.load({
|
|
|
3985
4041
|
dispose: () => {
|
|
3986
4042
|
disposed = true;
|
|
3987
4043
|
armed = false;
|
|
4044
|
+
armedByWheel = false;
|
|
4045
|
+
pendingIntent = false;
|
|
3988
4046
|
touchIds.clear();
|
|
3989
4047
|
cancelQuiet();
|
|
3990
4048
|
},
|
|
3991
4049
|
state: () => ({
|
|
3992
4050
|
armed,
|
|
3993
|
-
previousTop
|
|
4051
|
+
previousTop,
|
|
4052
|
+
pendingIntent
|
|
3994
4053
|
})
|
|
3995
4054
|
};
|
|
3996
4055
|
}
|
|
3997
|
-
/**
|
|
4056
|
+
/** Observe descendant input but only this scroller's movement; return its listener disposer. */
|
|
3998
4057
|
function bindHistoryAutoLoadGate(scroller, gate) {
|
|
3999
|
-
|
|
4058
|
+
const onEvent = (event) => {
|
|
4059
|
+
if ((event.type === "scroll" || event.type === "scrollend") && event.target !== scroller) return;
|
|
4060
|
+
gate.handleEvent(event);
|
|
4061
|
+
};
|
|
4062
|
+
for (const type of HISTORY_INTENT_EVENTS) scroller.addEventListener(type, onEvent, HISTORY_INTENT_LISTENER_OPTIONS);
|
|
4000
4063
|
return () => {
|
|
4001
|
-
for (const type of HISTORY_INTENT_EVENTS) scroller.removeEventListener(type,
|
|
4064
|
+
for (const type of HISTORY_INTENT_EVENTS) scroller.removeEventListener(type, onEvent, HISTORY_INTENT_LISTENER_OPTIONS);
|
|
4002
4065
|
};
|
|
4003
4066
|
}
|
|
4004
4067
|
/**
|
|
@@ -8585,7 +8648,7 @@ window.__ModuleLoader__.load({
|
|
|
8585
8648
|
const originHost = element("input");
|
|
8586
8649
|
originHost.type = "text";
|
|
8587
8650
|
originHost.value = "127.0.0.1";
|
|
8588
|
-
originHost.maxLength =
|
|
8651
|
+
originHost.maxLength = 45;
|
|
8589
8652
|
const originPortLabel = element("label", "dsh-mobile-control__field");
|
|
8590
8653
|
originPortLabel.textContent = t("originListenPort");
|
|
8591
8654
|
const originPort = element("input");
|
|
@@ -12413,6 +12476,8 @@ window.__ModuleLoader__.load({
|
|
|
12413
12476
|
.dsh-mobile-control__actions{display:flex;flex-wrap:nowrap;gap:6px}.dsh-mobile-control__actions[hidden],.dsh-mobile-control__manage-row[hidden]{display:none}.dsh-mobile-control__actions button{flex:1 1 0;min-width:0;min-height:44px;padding:8px 4px;border-radius:10px;font:12px/1.2 system-ui;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-l3,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}
|
|
12414
12477
|
.dsh-mobile-control button:focus-visible,.dsh-mobile-control a:focus-visible,.dsh-mobile-control input:focus-visible,.dsh-mobile-control select:focus-visible,.dsh-mobile-control summary:focus-visible{outline:3px solid rgb(37 99 235 / 28%);outline-offset:2px}
|
|
12415
12478
|
/* 42px matches the DSH sidebar footer row; it is not one of the panel target tiers. */
|
|
12479
|
+
/* Other footer entries share the shell's row; only our own action becomes compact. */
|
|
12480
|
+
.dsh-mobile-control__trigger:not(.is-rail):not(:only-child){flex:0 0 44px;width:44px;min-width:44px;padding:0;justify-content:center;gap:0}.dsh-mobile-control__trigger:not(.is-rail):not(:only-child) .dsh-mobile-control__trigger-label{display:none}
|
|
12416
12481
|
.dsh-mobile-control__trigger{box-sizing:border-box;flex:1 1 auto;display:flex;align-items:center;gap:8px;width:100%;height:42px;margin:4px 0;padding:0 10px 0 8px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font-family:inherit;font-size:14px;line-height:22px;cursor:pointer;overflow:hidden}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger:active,.dsh-mobile-control__trigger[aria-expanded="true"]{background:var(--dsw-alias-interactive-bg-active,#e8ebf0)}.dsh-mobile-control__trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px}.dsh-mobile-control__trigger.is-rail{flex:0 0 auto;width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;gap:0;border-radius:50%}.dsh-mobile-control__trigger-icon{display:block;flex:none}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
|
12417
12482
|
.dsh-mobile-control__manage-row{display:flex;justify-content:space-between;gap:8px;margin-top:10px}.dsh-mobile-control__manage{flex:1 1 0;min-width:0;min-height:36px;padding:6px 8px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:transparent;color:inherit;font:12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__devices{margin-top:10px;border:1px solid var(--dsw-alias-border-l2,#e1e5eb);border-radius:10px;padding:8px;max-height:220px;overflow-y:auto}.dsh-mobile-control__device-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px;margin:0}.dsh-mobile-control__device{display:flex;align-items:center;gap:8px;padding:6px 2px}.dsh-mobile-control__device + .dsh-mobile-control__device{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__device-label{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.dsh-mobile-control__device-meta{flex:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;white-space:nowrap}.dsh-mobile-control__device-revoke{flex:none;min-height:36px;padding:4px 8px;border:1px solid var(--dsw-alias-state-error-primary,#dc2626);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary,#dc2626);font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__ws-paths{margin:10px 0 0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__ws-paths-list{margin:0 0 8px;padding:0;list-style:none}.dsh-mobile-control__ws-paths-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px}.dsh-mobile-control__ws-paths-item{display:flex;align-items:center;gap:8px;padding:6px 0}.dsh-mobile-control__ws-paths-item + .dsh-mobile-control__ws-paths-item{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__ws-paths-item code{flex:1 1 0;min-width:0;overflow:hidden;font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__ws-paths-remove{flex:none;min-height:36px;padding:4px 8px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:8px;background:transparent;color:inherit;font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__trigger,.dsh-mobile-control__diagnostic-entry{position:relative}.dsh-mobile-control__ws-dot{position:absolute;top:2px;right:2px;z-index:1;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff;font:650 11px/16px system-ui;text-align:center;cursor:pointer}.dsh-mobile-control__ws-dot::after{content:'';position:absolute;inset:-5px}.dsh-mobile-control__ws-sr-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.dsh-mobile-control__ws-paths-row{display:flex;gap:8px}.dsh-mobile-control__ws-paths-row .dsh-mobile-control__primary{width:auto;flex:none;padding:9px 14px;border-radius:10px}.dsh-mobile-control__ws-paths-group{padding:6px 0}.dsh-mobile-control__ws-paths-group + .dsh-mobile-control__ws-paths-group{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__ws-paths-group-head{display:flex;align-items:center;gap:8px}.dsh-mobile-control__ws-count{flex:none;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8);font:650 11px/1.6 system-ui}.dsh-mobile-control__ws-paths-item.is-child{padding-left:12px}.dsh-mobile-control__ws-paths-group-head .dsh-mobile-control__primary,.dsh-mobile-control__ws-paths-item .dsh-mobile-control__primary{width:auto;flex:none;min-height:36px;padding:6px 12px;border-radius:10px}.dsh-mobile-control__ws-paths-row input{flex:1 1 0;min-width:0;box-sizing:border-box;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-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__ws-paths-row button{flex:none}.dsh-mobile-control__ws-paths-detected-title{margin:0 0 2px;font:600 12px/1.4 system-ui}.dsh-mobile-control__ws-paths-advanced{margin-top:8px}.dsh-mobile-control__ws-paths-advanced summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#606873);font:12px/1.4 system-ui;user-select:none}.dsh-mobile-control__ws-paths-advanced summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px;border-radius:4px}.dsh-mobile-control__ws-paths-advanced[open] summary{margin-bottom:6px}.dsh-mobile-control__ws-paths-advanced .dsh-mobile-control__status{margin:6px 0 0}
|
|
12418
12483
|
@media (max-width:359px){.dsh-mobile-control__provider{min-height:68px}}@media (prefers-reduced-motion:reduce){.dsh-mobile-control__provider,.dsh-mobile-control__cpolar-connect{transition:none}.dsh-mobile-control__diagnostic-summary.is-running .dsh-mobile-control__diagnostic-summary-icon::before,.dsh-mobile-control__diagnostic-checks{animation:none}}
|