dsh-mobile 0.8.1 → 0.8.2

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/lib/client.js CHANGED
@@ -5,7 +5,7 @@ window.__ModuleLoader__.load({
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
- //#region src/client-messages.ts
8
+ // src/client-messages.ts
9
9
  const frpFormMessages = {
10
10
  en: {
11
11
  frpServerAddressInvalid: "Enter the frps IP address or full hostname only, without https://, a port, or a path.",
@@ -1866,6 +1866,93 @@ window.__ModuleLoader__.load({
1866
1866
  cloudflaredTunnelSettingsInvalid: "隧道设置未通过校验。",
1867
1867
  cloudflaredTunnelConfigMissing: "使用命名隧道需要同时填写令牌、公网域名和端口。"
1868
1868
  });
1869
+ /** Measurement boundaries shared by diagnostic cards and copied reports. */
1870
+ const DIAGNOSTIC_SCOPE_MESSAGES = {
1871
+ en: {
1872
+ scripts: "Page scripts",
1873
+ configuration: "Configuration",
1874
+ computer: "Computer-side check",
1875
+ phone: "Phone not tested",
1876
+ versions: "Version information",
1877
+ provider: "Local channel state",
1878
+ dns: "System DNS",
1879
+ coverage: "Computer-side checks only. Phone reachability, authenticated pages, resources and WebSockets have not been verified."
1880
+ },
1881
+ it: {
1882
+ scripts: "Script pagina",
1883
+ configuration: "Configurazione",
1884
+ computer: "Controllo dal computer",
1885
+ phone: "Telefono non verificato",
1886
+ versions: "Informazioni versioni",
1887
+ provider: "Stato del canale locale",
1888
+ dns: "DNS di sistema",
1889
+ coverage: "Solo controlli dal computer. Raggiungibilità dal telefono, pagine autenticate, risorse e WebSocket non verificati."
1890
+ },
1891
+ zh: {
1892
+ scripts: "页面脚本",
1893
+ configuration: "配置信息",
1894
+ computer: "电脑侧检查",
1895
+ phone: "手机尚未验证",
1896
+ versions: "版本信息",
1897
+ provider: "本机通道状态",
1898
+ dns: "系统 DNS",
1899
+ coverage: "仅为电脑侧检查。手机可达性、认证页面、资源加载和 WebSocket 尚未验证。"
1900
+ }
1901
+ };
1902
+ /** Fixed DNS/egress observations never imply acceptance from another network. */
1903
+ const DIAGNOSTIC_NETWORK_MESSAGES = {
1904
+ en: {
1905
+ "remote-service-ready": ["{provider} reports its local component ready; public and phone paths are not verified.", ""],
1906
+ "plugin-inspection-unavailable": ["Active page scripts could not be inspected; absence of a competing remote channel is unverified.", "Other network checks are independent. Keep the reproduction environment and inspect the relevant plugins."],
1907
+ "firewall-inactive": ["LAN is off, so LAN firewall rules were not probed. This does not assess the remote entry.", "Enable LAN first if you need a local connection."],
1908
+ "remote-tailnet-shadowed": ["System DNS returned tailnet addresses and this Funnel-only listener does not accept that private path. This failed check does not establish a public Funnel outage.", "Compare from outside the tailnet. If you voluntarily change device DNS for a test, consider other private services and restore it afterward."],
1909
+ "dns-public": ["System DNS returned ordinary public-address results. This does not establish endpoint reachability.", ""],
1910
+ "dns-tailnet": ["System DNS returned tailnet addresses, not Fake-IP. This is a private path, not an independent public-path test.", ""],
1911
+ "dns-fake-ip": ["System DNS returned Fake-IP-range addresses; proxy routing may affect the measurement.", "Check the DNS/routing policy without disabling certificate verification."],
1912
+ "dns-private": ["System DNS returned private/local addresses. This does not test the public ingress.", ""],
1913
+ "dns-mixed": ["System DNS returned mixed address categories; do not attribute the failure to a single private address.", "Compare the actual routes and address families."],
1914
+ "dns-unknown": ["System DNS context was unavailable or exceeded its wait budget; no public DNS service was queried.", ""],
1915
+ "remote-dns-failed": ["The computer-side endpoint probe failed during name resolution.", "Check system DNS; compare from the phone network before judging the public service."],
1916
+ "remote-connect-failed": ["The computer-side endpoint probe could not establish or retain its connection.", "Check the current route and ingress, then compare from another network."],
1917
+ "remote-tls-failed": ["The computer-side TLS identity check failed.", "Check the certificate, hostname and proxy route. Do not disable certificate verification."],
1918
+ "remote-probe-timeout": ["The computer-side endpoint probe exceeded its wait budget.", "A timeout does not locate the failed hop. Compare from another network."],
1919
+ "remote-http-failed": ["The computer reached an HTTP endpoint but did not receive a successful health status.", "Check the ingress route and provider access policy; no credentials were sent by this probe."]
1920
+ },
1921
+ it: {
1922
+ "remote-service-ready": ["{provider} segnala il componente locale pronto; i percorsi pubblico e del telefono non sono verificati.", ""],
1923
+ "plugin-inspection-unavailable": ["Script attivi non verificati; non è confermata l’assenza di un altro canale remoto.", "Gli altri controlli sono indipendenti. Mantieni il caso riproducibile e controlla i plugin coinvolti."],
1924
+ "firewall-inactive": ["LAN disattivata: regole firewall locali non verificate. Il canale remoto non viene valutato da questo controllo.", "Abilita LAN prima di verificare una connessione locale."],
1925
+ "remote-tailnet-shadowed": ["Il DNS restituisce indirizzi tailnet e questo listener solo Funnel non accetta il percorso privato. Il controllo fallito non prova un guasto pubblico.", "Confronta da una rete esterna alla tailnet. Se modifichi volontariamente il DNS del dispositivo, valuta gli altri servizi privati e ripristinalo dopo il test."],
1926
+ "dns-public": ["Il DNS di sistema restituisce indirizzi pubblici ordinari; non prova la raggiungibilità.", ""],
1927
+ "dns-tailnet": ["Il DNS restituisce indirizzi tailnet, non Fake-IP. È un percorso privato, non un test pubblico indipendente.", ""],
1928
+ "dns-fake-ip": ["Il DNS restituisce indirizzi Fake-IP; il routing del proxy può influire sulla misura.", "Controlla DNS e routing senza disabilitare la verifica dei certificati."],
1929
+ "dns-private": ["Il DNS restituisce indirizzi privati/locali; non verifica l’ingresso pubblico.", ""],
1930
+ "dns-mixed": ["Il DNS restituisce categorie miste; non attribuire il problema a un solo indirizzo privato.", "Confronta percorsi e famiglie IP effettivi."],
1931
+ "dns-unknown": ["Il contesto DNS non è disponibile o ha superato il tempo previsto; nessun DNS pubblico è stato interrogato.", ""],
1932
+ "remote-dns-failed": ["La risoluzione del nome è fallita nel controllo dal computer.", "Controlla il DNS di sistema e confronta dalla rete del telefono."],
1933
+ "remote-connect-failed": ["Il controllo dal computer non ha stabilito o mantenuto la connessione.", "Controlla percorso e ingresso, poi confronta da un’altra rete."],
1934
+ "remote-tls-failed": ["La verifica TLS dal computer è fallita.", "Controlla certificato, nome host e proxy. Non disabilitare la verifica TLS."],
1935
+ "remote-probe-timeout": ["Il controllo dal computer ha superato il tempo previsto.", "Il timeout non identifica il punto guasto; confronta da un’altra rete."],
1936
+ "remote-http-failed": ["Il computer ha raggiunto HTTP ma senza uno stato di salute riuscito.", "Controlla percorso e criteri di accesso. Il test non invia credenziali."]
1937
+ },
1938
+ zh: {
1939
+ "remote-service-ready": ["{provider} 报告本机组件已就绪;公网与手机路径尚未验证。", ""],
1940
+ "plugin-inspection-unavailable": ["本次未能检查活动页面脚本,不能据此认定不存在其他远程通道。", "其余网络项目独立检查;保留复现环境并核对相关插件。"],
1941
+ "firewall-inactive": ["局域网未启用,本次没有探测局域网防火墙规则,不用于判断远程入口。", "需要局域网连接时,先启用再检查。"],
1942
+ "remote-tailnet-shadowed": ["系统 DNS 返回 tailnet 地址,而当前 Funnel-only 监听不接受此私网路径。本次失败不能证明公网 Funnel 故障。", "用 tailnet 外的网络对照。若自愿临时调整设备 DNS,注意其他私网服务的影响,并在测试后恢复。"],
1943
+ "dns-public": ["系统 DNS 返回普通公网地址类别;这本身不证明端点可达。", ""],
1944
+ "dns-tailnet": ["系统 DNS 返回 tailnet 地址,不是 Fake-IP。这是私网路径,不是独立的公网验证。", ""],
1945
+ "dns-fake-ip": ["系统 DNS 返回 Fake-IP 网段;代理路由可能影响本次测量。", "核对 DNS 和路由策略,不要关闭证书验证。"],
1946
+ "dns-private": ["系统 DNS 返回私网或本机地址,不是在验证公网入口。", ""],
1947
+ "dns-mixed": ["系统 DNS 返回混合地址类别,不能只凭其中一个私网地址归因。", "对照实际路由与 IPv4/IPv6 路径。"],
1948
+ "dns-unknown": ["系统 DNS 信息不可用或等待超时;没有查询公共 DNS 服务。", ""],
1949
+ "remote-dns-failed": ["电脑出口探测在名称解析阶段失败。", "检查系统 DNS,并用手机当前网络对照后再判断公网状态。"],
1950
+ "remote-connect-failed": ["电脑出口探测无法建立或保持连接。", "核对当前路由和入口,再用其他网络对照。"],
1951
+ "remote-tls-failed": ["电脑出口探测的 TLS 身份验证失败。", "检查证书、主机名和代理路由,不要关闭证书验证。"],
1952
+ "remote-probe-timeout": ["电脑出口探测超过了等待预算。", "超时不能定位具体故障节点,请用其他网络对照。"],
1953
+ "remote-http-failed": ["电脑已到达 HTTP 入口,但未得到成功的健康状态。", "检查入口路由与服务商访问策略;本次探测不发送配对凭据。"]
1954
+ }
1955
+ };
1869
1956
  const DIAGNOSTIC_REASON_MESSAGES = {
1870
1957
  en: {
1871
1958
  "lan-setup-required": ["Local network setup has not been completed.", "Return to Local network and choose a network to finish setup."],
@@ -1873,24 +1960,24 @@ window.__ModuleLoader__.load({
1873
1960
  "network-unavailable": ["The saved local network adapter is unavailable.", "Run dsh-mobile setup again."],
1874
1961
  "network-interface": ["Using network interface {interfaceName}.", ""],
1875
1962
  "network-fixed": ["A fixed local network configuration is in use.", ""],
1876
- "lan-ready": ["The local gateway is listening at {endpointSuffix}; pairing is available.", ""],
1963
+ "lan-ready": ["The computer gateway is listening at {endpointSuffix}; phone reachability is unverified.", ""],
1877
1964
  "lan-off": ["Local network access is currently off.", "Enable local network access when the phone must connect directly."],
1878
- "firewall-ready": ["Local TCP and discovery firewall rules are enabled.", ""],
1965
+ "firewall-ready": ["Enabled local TCP/discovery rules were found; this does not prove the phone route is permitted.", ""],
1879
1966
  "firewall-missing": ["The complete local network firewall rules were not found.", "Run dsh-mobile setup as administrator and check the firewall rules."],
1880
1967
  "firewall-unknown": ["The system did not allow the plugin to read firewall status.", "If the phone cannot find this computer, run setup as administrator."],
1881
1968
  "remote-off": ["Remote access through {provider} is currently off.", ""],
1882
1969
  "remote-origin-ready": ["The private HTTP backend is listening; public HTTPS is not verified.", "Verify your reverse proxy, public certificate, external Host including port, and WebSocket access from your phone. Keep the HTTP backend private."],
1883
- "remote-ready": ["{provider} endpoint {endpointSuffix} is reachable in about {latencyMs} ms.", ""],
1970
+ "remote-ready": ["Computer-side HTTP reached {provider} endpoint {endpointSuffix} in about {latencyMs} ms; phone and independent public-path reachability are unverified.", ""],
1884
1971
  "remote-ready-via-proxy": ["The computer reached {provider} endpoint {endpointSuffix} through its configured proxy in about {latencyMs} ms; phone reachability is not verified.", "Test the connection from the phone’s network."],
1885
1972
  "remote-rate-limited": ["{provider} endpoint {endpointSuffix} is reachable, but this check observed rate limiting.", "Try again later; older sessions load on demand to reduce traffic."],
1886
1973
  "remote-rate-limited-via-proxy": ["The computer reached {provider} endpoint {endpointSuffix} through its proxy, but the service rate-limited this check; phone reachability is not verified.", "Try again later and test from the phone’s network."],
1887
- "remote-fake-ip": ["The Tailscale address is intercepted by the current VPN or DNS proxy, so TLS was not established.", "Switch VPN node or proxy mode; if it still fails, use cpolar."],
1888
- "remote-unreachable": ["{provider} reports ready, but endpoint {endpointSuffix} is not reachable.", "Reconnect, then check the provider status if it still fails."],
1974
+ "remote-fake-ip": ["System DNS returned Fake-IP-range addresses and the computer-side probe failed; this does not prove a public outage.", "Check DNS/proxy routing and compare another network without disabling certificate checks."],
1975
+ "remote-unreachable": ["The computer-side probe of {provider} endpoint {endpointSuffix} failed; this does not establish failure from other networks.", "Compare DNS, routing, TLS and ingress from the phone network before reconnecting."],
1889
1976
  "remote-needs-login": ["Tailscale is waiting for login to finish.", "Return to Remote access and continue login."],
1890
1977
  "remote-connecting": ["The {provider} remote channel is still connecting.", "Wait briefly, then check again."],
1891
1978
  "remote-controller-error": ["The {provider} controller reported {controllerCode}.", "Return to Remote access and reconnect."],
1892
1979
  "competing-remote-channel": ["Another remote plugin is injecting its own request channel; the phone may show that plugin’s pairing screen.", "Turn off dsh-remote-web-ui remote access on the computer, refresh, and pair using the current DSH Mobile QR code. Do not mix pairing codes."],
1893
- "phone-network-unknown": ["The computer cannot determine whether the router isolates the phone.", "Confirm the phone and computer use the same network, then disable guest-network or AP isolation."]
1980
+ "phone-network-unknown": ["The phone network, paired page, resources and WebSocket have not been tested by this computer.", "Test from the current phone network; record the failing stage without sharing pairing secrets."]
1894
1981
  },
1895
1982
  it: {
1896
1983
  "lan-setup-required": ["La configurazione della rete locale non è completa.", "Torna a Rete locale e scegli una rete per completare la configurazione."],
@@ -1898,24 +1985,24 @@ window.__ModuleLoader__.load({
1898
1985
  "network-unavailable": ["La scheda di rete locale salvata non è disponibile.", "Esegui di nuovo dsh-mobile setup."],
1899
1986
  "network-interface": ["È in uso l’interfaccia di rete {interfaceName}.", ""],
1900
1987
  "network-fixed": ["È in uso una configurazione di rete locale fissa.", ""],
1901
- "lan-ready": ["Il gateway locale è in ascolto su {endpointSuffix}; l’abbinamento è disponibile.", ""],
1988
+ "lan-ready": ["Il gateway del computer è in ascolto su {endpointSuffix}; il telefono non è verificato.", ""],
1902
1989
  "lan-off": ["L’accesso dalla rete locale è disattivato.", "Attiva l’accesso locale quando il telefono deve collegarsi direttamente."],
1903
- "firewall-ready": ["Le regole firewall TCP locale e di rilevamento sono attive.", ""],
1990
+ "firewall-ready": ["Trovate regole TCP/discovery abilitate; il percorso dal telefono non è verificato.", ""],
1904
1991
  "firewall-missing": ["Le regole firewall complete per la rete locale non sono state trovate.", "Esegui dsh-mobile setup come amministratore e controlla le regole firewall."],
1905
1992
  "firewall-unknown": ["Il sistema non ha consentito al plugin di leggere lo stato del firewall.", "Se il telefono non trova il computer, esegui setup come amministratore."],
1906
1993
  "remote-off": ["L’accesso remoto tramite {provider} è disattivato.", ""],
1907
1994
  "remote-origin-ready": ["Il backend HTTP privato è in ascolto; HTTPS pubblico non è verificato.", "Verifica dal telefono reverse proxy, certificato pubblico, Host esterno inclusa la porta e accesso WebSocket. Mantieni privato il backend HTTP."],
1908
- "remote-ready": ["L’endpoint {provider} {endpointSuffix} è raggiungibile in circa {latencyMs} ms.", ""],
1995
+ "remote-ready": ["HTTP dal computer ha raggiunto {provider} su {endpointSuffix} in circa {latencyMs} ms; telefono e percorso pubblico indipendente non verificati.", ""],
1909
1996
  "remote-ready-via-proxy": ["Il computer ha raggiunto l’endpoint {provider} {endpointSuffix} tramite il proxy configurato in circa {latencyMs} ms; la raggiungibilità dal telefono non è verificata.", "Prova la connessione dalla rete del telefono."],
1910
1997
  "remote-rate-limited": ["L’endpoint {provider} {endpointSuffix} è raggiungibile, ma il controllo ha rilevato una limitazione temporanea.", "Riprova più tardi; le sessioni precedenti vengono caricate su richiesta per ridurre il traffico."],
1911
1998
  "remote-rate-limited-via-proxy": ["Il computer ha raggiunto l’endpoint {provider} {endpointSuffix} tramite proxy, ma il servizio ha limitato questo controllo; la raggiungibilità dal telefono non è verificata.", "Riprova più tardi e verifica dalla rete del telefono."],
1912
- "remote-fake-ip": ["L’indirizzo Tailscale è intercettato dalla VPN o dal proxy DNS corrente e TLS non è stato stabilito.", "Cambia nodo VPN o modalità proxy; se il problema continua, usa cpolar."],
1913
- "remote-unreachable": ["{provider} risulta pronto, ma l’endpoint {endpointSuffix} non è raggiungibile.", "Riconnettiti; se il problema continua, controlla lo stato del provider."],
1999
+ "remote-fake-ip": ["Il DNS restituisce indirizzi Fake-IP e il controllo dal computer è fallito; non prova un guasto pubblico.", "Controlla DNS e routing e confronta una rete alternativa senza disabilitare TLS."],
2000
+ "remote-unreachable": ["Il controllo dal computer di {provider} su {endpointSuffix} è fallito; altre reti non sono verificate.", "Confronta DNS, routing, TLS e ingresso dalla rete del telefono."],
1914
2001
  "remote-needs-login": ["Tailscale attende il completamento dell’accesso.", "Torna ad Accesso remoto e continua l’accesso."],
1915
2002
  "remote-connecting": ["Il canale remoto {provider} è ancora in connessione.", "Attendi qualche istante e ripeti il controllo."],
1916
2003
  "remote-controller-error": ["Il controller {provider} ha segnalato {controllerCode}.", "Torna ad Accesso remoto e riconnettiti."],
1917
2004
  "competing-remote-channel": ["Un altro plugin remoto sta inserendo il proprio canale di richieste; sul telefono potrebbe apparire la sua schermata di abbinamento.", "Disattiva l’accesso remoto di dsh-remote-web-ui sul computer, aggiorna la pagina e usa il codice QR corrente di DSH Mobile. Non mescolare i codici."],
1918
- "phone-network-unknown": ["Il computer non può stabilire se il router isola il telefono.", "Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP."]
2005
+ "phone-network-unknown": ["Rete del telefono, pagina abbinata, risorse e WebSocket non sono stati verificati dal computer.", "Verifica dalla rete del telefono e indica la fase del problema senza condividere credenziali."]
1919
2006
  },
1920
2007
  zh: {
1921
2008
  "lan-setup-required": ["尚未完成局域网初始化。", "返回局域网页选择网卡并完成配置。"],
@@ -1923,24 +2010,24 @@ window.__ModuleLoader__.load({
1923
2010
  "network-unavailable": ["已保存的局域网网卡当前不可用。", "重新运行 dsh-mobile setup。"],
1924
2011
  "network-interface": ["正在使用网卡 {interfaceName}。", ""],
1925
2012
  "network-fixed": ["当前使用固定局域网配置。", ""],
1926
- "lan-ready": ["局域网网关正在监听 {endpointSuffix},配对入口可用。", ""],
2013
+ "lan-ready": ["电脑网关已监听 {endpointSuffix};手机能否到达尚未验证。", ""],
1927
2014
  "lan-off": ["局域网访问当前未开启。", "手机需要直连时开启局域网访问。"],
1928
- "firewall-ready": ["局域网 TCP 与发现防火墙规则已启用。", ""],
2015
+ "firewall-ready": ["已发现启用的局域网 TCP 与发现规则;这不证明手机路由或全部防火墙策略已放行。", ""],
1929
2016
  "firewall-missing": ["未找到完整的局域网防火墙规则。", "以管理员身份运行 dsh-mobile setup 并检查防火墙规则。"],
1930
2017
  "firewall-unknown": ["系统未允许插件读取防火墙状态。", "若手机找不到电脑,请以管理员身份运行 setup。"],
1931
2018
  "remote-off": ["{provider} 远程访问当前未启用。", ""],
1932
2019
  "remote-origin-ready": ["私有 HTTP 后端已监听;尚未验证公网 HTTPS。", "请用手机核对反向代理、公网证书、含端口的外部 Host 及 WebSocket 访问。HTTP 后端必须留在私网。"],
1933
- "remote-ready": ["{provider} 端点 {endpointSuffix} 可达,往返约 {latencyMs} ms。", ""],
2020
+ "remote-ready": ["电脑当前出口可访问 {provider} 入口 {endpointSuffix},约 {latencyMs} ms;公网及手机路径未独立验证。", ""],
1934
2021
  "remote-ready-via-proxy": ["电脑经已配置代理访问 {provider} 端点 {endpointSuffix},约 {latencyMs} ms;尚未验证手机网络是否可达。", "请用手机当前网络实际连接测试。"],
1935
2022
  "remote-rate-limited": ["{provider} 端点 {endpointSuffix} 可达,但本次检查观察到服务限流。", "稍后重试;旧会话会按需加载以减少流量。"],
1936
2023
  "remote-rate-limited-via-proxy": ["电脑经代理访问 {provider} 端点 {endpointSuffix} 时遇到限流;尚未验证手机网络是否可达。", "稍后重试,并用手机当前网络实际连接测试。"],
1937
- "remote-fake-ip": ["Tailscale 地址被当前 VPN 或 DNS 代理接管,TLS 链路未建立。", "切换 VPN 节点或代理模式;仍失败时改用 cpolar。"],
1938
- "remote-unreachable": ["{provider} 显示已就绪,但端点 {endpointSuffix} 暂不可达。", "点击重新连接;仍失败时检查提供方状态。"],
2024
+ "remote-fake-ip": ["系统 DNS 返回 Fake-IP 网段,电脑出口探测未成功;这不是公网故障的证明。", "核对 DNS 与代理路由,用其他网络对照,不要关闭证书验证。"],
2025
+ "remote-unreachable": ["电脑当前出口无法完成 {provider} 入口 {endpointSuffix} 的探测;这不证明其他网络也失败。", "先对照手机网络及 DNS、路由、证书和入口,再决定是否重连。"],
1939
2026
  "remote-needs-login": ["Tailscale 正在等待完成登录。", "返回远程访问并继续登录。"],
1940
2027
  "remote-connecting": ["{provider} 远程通道仍在连接。", "等待片刻后重新检查。"],
1941
2028
  "remote-controller-error": ["{provider} 控制器报告 {controllerCode}。", "返回远程访问并重新连接。"],
1942
2029
  "competing-remote-channel": ["检测到另一个远程插件注入自己的请求通道;手机可能显示它的配对页面。", "在电脑端关闭 dsh-remote-web-ui 的远程访问并刷新,再用 DSH Mobile 当前二维码配对;不要混用配对码。"],
1943
- "phone-network-unknown": ["电脑无法判断路由器是否隔离了手机。", "确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。"]
2030
+ "phone-network-unknown": ["电脑没有验证手机网络、配对后的页面、资源和 WebSocket。", "请用手机当前网络实际连接,记录失败阶段,不要提供配对密钥。"]
1944
2031
  }
1945
2032
  };
1946
2033
  Object.assign(MOBILE_CONTROL_MESSAGES.en, {
@@ -2228,8 +2315,7 @@ window.__ModuleLoader__.load({
2228
2315
  if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), "");
2229
2316
  return lines.join("\n");
2230
2317
  }
2231
- //#endregion
2232
- //#region src/ip.ts
2318
+ // src/ip.ts
2233
2319
  /**
2234
2320
  * Browser-safe IP literal classification.
2235
2321
  *
@@ -2310,8 +2396,7 @@ window.__ModuleLoader__.load({
2310
2396
  if (leftGroups < 0 || rightGroups < 0) return 0;
2311
2397
  return leftGroups + rightGroups >= 8 ? 0 : 6;
2312
2398
  }
2313
- //#endregion
2314
- //#region src/network.ts
2399
+ // src/network.ts
2315
2400
  /**
2316
2401
  * IPv4 ranges that are never a public VPS endpoint (IANA special-purpose,
2317
2402
  * private, shared, loopback, link-local, documentation, benchmark, multicast,
@@ -2569,8 +2654,7 @@ window.__ModuleLoader__.load({
2569
2654
  ""
2570
2655
  ].join("\n");
2571
2656
  }
2572
- //#endregion
2573
- //#region src/frp-attach.ts
2657
+ // src/frp-attach.ts
2574
2658
  /** Default proof path used to confirm the public entry answered from this computer. */
2575
2659
  const FRP_ATTACH_DISCOVERY_PATH = "/mobile-access/discovery";
2576
2660
  /** Declaration printed above the VPS half of an attach template. */
@@ -2767,8 +2851,7 @@ window.__ModuleLoader__.load({
2767
2851
  function createFrpAttachTemplate(settings, options = {}) {
2768
2852
  return createFrpAttachTemplateParts(settings, options).text;
2769
2853
  }
2770
- //#endregion
2771
- //#region src/frp-form-validation.ts
2854
+ // src/frp-form-validation.ts
2772
2855
  /** Field ownership for errors returned by the Host parser. */
2773
2856
  const FRP_FORM_ERRORS = {
2774
2857
  frp_server_address_invalid: {
@@ -2846,8 +2929,7 @@ window.__ModuleLoader__.load({
2846
2929
  }
2847
2930
  return issues;
2848
2931
  }
2849
- //#endregion
2850
- //#region node_modules/@noble/hashes/esm/utils.js
2932
+ // node_modules/@noble/hashes/esm/utils.js
2851
2933
  /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2852
2934
  /** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
2853
2935
  function isBytes(a) {
@@ -2923,8 +3005,7 @@ window.__ModuleLoader__.load({
2923
3005
  hashC.create = () => hashCons();
2924
3006
  return hashC;
2925
3007
  }
2926
- //#endregion
2927
- //#region node_modules/@noble/hashes/esm/_md.js
3008
+ // node_modules/@noble/hashes/esm/_md.js
2928
3009
  /**
2929
3010
  * Internal Merkle-Damgard hash utils.
2930
3011
  * @module
@@ -3052,8 +3133,7 @@ window.__ModuleLoader__.load({
3052
3133
  528734635,
3053
3134
  1541459225
3054
3135
  ]);
3055
- //#endregion
3056
- //#region node_modules/@noble/hashes/esm/sha2.js
3136
+ // node_modules/@noble/hashes/esm/sha2.js
3057
3137
  /**
3058
3138
  * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
3059
3139
  * SHA256 is the fastest hash implementable in JS, even faster than Blake3.
@@ -3209,8 +3289,7 @@ window.__ModuleLoader__.load({
3209
3289
  clean(this.buffer);
3210
3290
  }
3211
3291
  };
3212
- //#endregion
3213
- //#region node_modules/@noble/hashes/esm/sha256.js
3292
+ // node_modules/@noble/hashes/esm/sha256.js
3214
3293
  /**
3215
3294
  * SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.
3216
3295
  *
@@ -3521,8 +3600,7 @@ window.__ModuleLoader__.load({
3521
3600
  if (anchorTimer !== 0) window.clearTimeout(anchorTimer);
3522
3601
  };
3523
3602
  }
3524
- //#endregion
3525
- //#region src/mobile-composer-toolbar.ts
3603
+ // src/mobile-composer-toolbar.ts
3526
3604
  /**
3527
3605
  * Compact the stock InputBar without changing its React ownership, handlers,
3528
3606
  * activity tree or the two groups observed by DSH's model-width controller.
@@ -3557,8 +3635,7 @@ window.__ModuleLoader__.load({
3557
3635
  [data-dsh-mobile-composer-model-label] { flex:1 1 auto !important; max-width:none !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }
3558
3636
  }
3559
3637
  `;
3560
- //#endregion
3561
- //#region src/browser-file-picker.ts
3638
+ // src/browser-file-picker.ts
3562
3639
  /** Browser chooser lifetime shared by camera controls and the extension SDK. */
3563
3640
  const BROWSER_FILE_PICKER_DEADLINE_MS = 3e5;
3564
3641
  const browserEnvironment = {
@@ -3678,8 +3755,7 @@ window.__ModuleLoader__.load({
3678
3755
  }
3679
3756
  });
3680
3757
  }
3681
- //#endregion
3682
- //#region src/mobile-sidebar-scroll.ts
3758
+ // src/mobile-sidebar-scroll.ts
3683
3759
  function sidebarRestoreTop(snapshot, anchorTop) {
3684
3760
  return Math.max(0, anchorTop === void 0 || snapshot.offset === void 0 ? snapshot.top : anchorTop - snapshot.offset);
3685
3761
  }
@@ -3835,8 +3911,7 @@ window.__ModuleLoader__.load({
3835
3911
  }
3836
3912
  };
3837
3913
  }
3838
- //#endregion
3839
- //#region src/native-mobile.ts
3914
+ // src/native-mobile.ts
3840
3915
  /**
3841
3916
  * Viewport below which the injected chrome becomes an overlay drawer: the
3842
3917
  * sidebar slides in as a sheet, the right panel as a drawer, and the scrim dims
@@ -4176,6 +4251,14 @@ window.__ModuleLoader__.load({
4176
4251
  });
4177
4252
  }
4178
4253
  /**
4254
+ * Detect upstream ownership before applying legacy frame geometry.
4255
+ * @param frame - the stock frame's element lookup, if mounted.
4256
+ * @returns whether it provides the responsive sidebar opener.
4257
+ */
4258
+ function stockOwnsMobileDrawer(frame) {
4259
+ return frame?.querySelector("[data-sidebar-opener]") != null;
4260
+ }
4261
+ /**
4179
4262
  * Mirror the classes of the stock Add-menu item on an injected attachment action.
4180
4263
  *
4181
4264
  * An empty class name is not nullish: trusting it would strip the plugin's own
@@ -5464,11 +5547,27 @@ window.__ModuleLoader__.load({
5464
5547
  const nextFrame = resolveNativeMobileFrame(document, dedicatedCenter);
5465
5548
  if (frame !== nextFrame) frame?.removeAttribute("data-dsh-mobile-frame");
5466
5549
  frame = nextFrame;
5467
- if (frame !== void 0) frame.dataset.dshMobileFrame = "true";
5468
- sidebar = frame === void 0 ? document.querySelector(".dshm-drawer") ?? void 0 : firstByClassSuffix(frame, "_sidebarCol");
5550
+ const nativeDrawer = stockOwnsMobileDrawer(frame);
5551
+ if (frame !== void 0) {
5552
+ if (nativeDrawer) frame.removeAttribute("data-dsh-mobile-frame");
5553
+ else frame.dataset.dshMobileFrame = "true";
5554
+ }
5555
+ if (nativeDrawer) {
5556
+ sidebar?.removeAttribute("data-dsh-mobile-sidebar");
5557
+ sidebarRoot?.removeAttribute("data-dsh-mobile-sidebar-root");
5558
+ toggle?.removeAttribute("data-dsh-mobile-toggle");
5559
+ for (const node of frame.querySelectorAll("[data-dsh-mobile-details],[data-dsh-mobile-handle]")) {
5560
+ node.removeAttribute("data-dsh-mobile-details");
5561
+ node.removeAttribute("data-dsh-mobile-handle");
5562
+ }
5563
+ sidebarRoot = void 0;
5564
+ toggle = void 0;
5565
+ backdrop.hidden = true;
5566
+ }
5567
+ sidebar = nativeDrawer ? void 0 : frame === void 0 ? document.querySelector(".dshm-drawer") ?? void 0 : firstByClassSuffix(frame, "_sidebarCol");
5469
5568
  const center = frame === void 0 ? dedicatedCenter : firstByClassSuffix(frame, "_centerCol");
5470
- const details = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_detailsCol");
5471
- const handle = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_handle");
5569
+ const details = frame === void 0 || nativeDrawer ? void 0 : firstByClassSuffix(frame, "_detailsCol");
5570
+ const handle = frame === void 0 || nativeDrawer ? void 0 : firstByClassSuffix(frame, "_handle");
5472
5571
  markNativeMobileSettings(document);
5473
5572
  if (center === void 0) {
5474
5573
  syncPanHeader(void 0);
@@ -5482,12 +5581,15 @@ window.__ModuleLoader__.load({
5482
5581
  return;
5483
5582
  }
5484
5583
  if (center !== void 0) {
5485
- center.dataset.dshMobileCenter = "true";
5584
+ if (nativeDrawer) center.removeAttribute("data-dsh-mobile-center");
5585
+ else center.dataset.dshMobileCenter = "true";
5486
5586
  const header = center.querySelector("header") ?? void 0;
5487
- header?.setAttribute("data-dsh-mobile-header", "true");
5488
- syncPanHeader(header);
5587
+ if (nativeDrawer) header?.removeAttribute("data-dsh-mobile-header");
5588
+ else header?.setAttribute("data-dsh-mobile-header", "true");
5589
+ syncPanHeader(nativeDrawer ? void 0 : header);
5489
5590
  viewArea = firstByClassSuffix(center, "_viewArea");
5490
- if (viewArea !== void 0) viewArea.dataset.dshMobileView = "true";
5591
+ if (nativeDrawer) viewArea?.removeAttribute("data-dsh-mobile-view");
5592
+ else if (viewArea !== void 0) viewArea.dataset.dshMobileView = "true";
5491
5593
  const conversation = center.querySelector("[data-conversation-scroll]");
5492
5594
  const composerCard = center.querySelector("[data-composer-card]");
5493
5595
  bindHistoryScroller(conversation ?? void 0, composerCard ?? null);
@@ -5696,8 +5798,7 @@ window.__ModuleLoader__.load({
5696
5798
  delete document.documentElement.dataset.dshMobileInput;
5697
5799
  };
5698
5800
  }
5699
- //#endregion
5700
- //#region src/voice-session.ts
5801
+ // src/voice-session.ts
5701
5802
  /** Screen wake-lock ownership and mobile composer focus during DSH dictation. */
5702
5803
  /** Voice phases during which the user is dictating. */
5703
5804
  const DICTATING_PHASES = [
@@ -6154,8 +6255,7 @@ window.__ModuleLoader__.load({
6154
6255
  }
6155
6256
  };
6156
6257
  }
6157
- //#endregion
6158
- //#region src/mobile-font-capabilities.ts
6258
+ // src/mobile-font-capabilities.ts
6159
6259
  const NO_ADVANCED_MOBILE_TYPOGRAPHY = Object.freeze({
6160
6260
  codeSize: false,
6161
6261
  textFamily: false,
@@ -6211,8 +6311,7 @@ window.__ModuleLoader__.load({
6211
6311
  code.remove();
6212
6312
  }
6213
6313
  }
6214
- //#endregion
6215
- //#region src/mobile-font.ts
6314
+ // src/mobile-font.ts
6216
6315
  /** Device-local typography for authenticated mobile pages, without Host settings writes. */
6217
6316
  const MOBILE_FONT_STORAGE_KEY = "dsh-mobile.content-font-size.v1";
6218
6317
  const MOBILE_TYPOGRAPHY_STORAGE_KEY = "dsh-mobile.typography.v1";
@@ -6638,8 +6737,7 @@ window.__ModuleLoader__.load({
6638
6737
  }
6639
6738
  }, copy.reset)));
6640
6739
  }
6641
- //#endregion
6642
- //#region src/mobile-display-scale.ts
6740
+ // src/mobile-display-scale.ts
6643
6741
  /** Native App page scaling in DSH General settings, independent from local typography. */
6644
6742
  /** Validate the native reply before displaying or reusing its values in a request. */
6645
6743
  function parseMobileDisplayScale(value) {
@@ -6856,8 +6954,7 @@ window.__ModuleLoader__.load({
6856
6954
  }
6857
6955
  }, copy.reset)));
6858
6956
  }
6859
- //#endregion
6860
- //#region src/mobile-native-settings-action.ts
6957
+ // src/mobile-native-settings-action.ts
6861
6958
  /** Own capability discovery and explicit calls; native actions retain the App's own timeout. */
6862
6959
  var MobileNativeSettingsActionController = class {
6863
6960
  action;
@@ -6966,8 +7063,7 @@ window.__ModuleLoader__.load({
6966
7063
  for (const listener of this.listeners) listener();
6967
7064
  }
6968
7065
  };
6969
- //#endregion
6970
- //#region src/local-admin-host.ts
7066
+ // src/local-admin-host.ts
6971
7067
  /** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */
6972
7068
  const DESKTOP_ADMIN_HEADER = "x-dsh-mobile-desktop-admin";
6973
7069
  const DESKTOP_ADMIN_MARKER = "dsh-app://app";
@@ -7031,8 +7127,7 @@ window.__ModuleLoader__.load({
7031
7127
  const query = search.startsWith("?") ? search.slice(1) : search;
7032
7128
  return (protocol === "dsh-app:" ? hostname === "app" : (protocol === "" || protocol === "http:" || protocol === "https:") && isLocalAdminHostname(hostname)) && frontend !== "dedicated" && !new URLSearchParams(query).has("dsh-mobile-preview");
7033
7129
  }
7034
- //#endregion
7035
- //#region src/client-module-presentation.ts
7130
+ // src/client-module-presentation.ts
7036
7131
  /** Group boot-protected entries before considering their package namespace.
7037
7132
  * @param entry - Catalog entry with server-owned required and dependency fields.
7038
7133
  * @returns Display group, never an authorization or compatibility decision.
@@ -7109,6 +7204,21 @@ window.__ModuleLoader__.load({
7109
7204
  }
7110
7205
  return entries.filter((entry) => !keep.has(entry.id)).map((entry) => entry.id);
7111
7206
  }
7207
+ /**
7208
+ * Describe the optional modules removed by the minimal preset without treating unmeasured sizes as zero.
7209
+ * @param entries - The complete displayed catalog, including required dependencies.
7210
+ * @returns Excluded count, known gzip bytes when measured, and the number still unmeasured.
7211
+ */
7212
+ function clientModuleMinimalImpact(entries) {
7213
+ const excluded = new Set(clientModuleMinimalExclusion(entries));
7214
+ const rows = entries.filter((entry) => excluded.has(entry.id));
7215
+ const measured = rows.filter((entry) => entry.gzipBytes !== void 0);
7216
+ return {
7217
+ count: rows.length,
7218
+ ...measured.length === 0 ? {} : { gzipBytes: measured.reduce((sum, entry) => sum + entry.gzipBytes, 0) },
7219
+ unknown: rows.length - measured.length
7220
+ };
7221
+ }
7112
7222
  /** Editable selections retain unknown server exclusions and never cascade dependencies. */
7113
7223
  var ClientModuleDraft = class {
7114
7224
  initial;
@@ -7236,8 +7346,7 @@ window.__ModuleLoader__.load({
7236
7346
  return filter === "loaded" ? [] : this.invalidExclusions.filter((entry) => matchesQuery(entry.id, query));
7237
7347
  }
7238
7348
  };
7239
- //#endregion
7240
- //#region src/client-module-ui.ts
7349
+ // src/client-module-ui.ts
7241
7350
  /** Device-local module selection and the computer's defaults share a read-only catalog. */
7242
7351
  const CLIENT_MODULE_COPY = {
7243
7352
  zh: {
@@ -7266,6 +7375,7 @@ window.__ModuleLoader__.load({
7266
7375
  failed: "暂时无法读取模块。请确认移动访问已启用后重试。",
7267
7376
  saveFailed: "未确认保存成功,已保留你的选择。可重新保存,或重新读取已保存设置。",
7268
7377
  conflict: "无法保存:",
7378
+ catalogChanged: "模块目录已变化(插件或版本有更新),本次保存已取消。请点「重新读取」,确认后再保存。",
7269
7379
  computerSource: "当前来源:电脑默认设置",
7270
7380
  deviceSource: "当前来源:本设备设置",
7271
7381
  pluginSource: "当前来源:插件默认设置",
@@ -7295,6 +7405,7 @@ window.__ModuleLoader__.load({
7295
7405
  totalUnknown: "勾选合计:部分模块未测到体积",
7296
7406
  minimal: "仅必需(最小加载)",
7297
7407
  minimalConfirm: "把除了启动必需项及其依赖之外全部设为不加载?保存后下次打开生效。",
7408
+ minimalDetail: "将排除 {count} 个可选模块;已测 gzip 合计 {size},另有 {unknown} 项体积未知。启动必需项及其依赖会保留。",
7298
7409
  retry: "重新读取",
7299
7410
  saving: "正在保存…",
7300
7411
  readTimeout: "读取超时。当前页面和已保存设置未变,请重试。",
@@ -7332,6 +7443,7 @@ window.__ModuleLoader__.load({
7332
7443
  failed: "Could not read modules. Enable mobile access and try again.",
7333
7444
  saveFailed: "Saving was not confirmed. Your choices are kept. Save again or reread the saved settings.",
7334
7445
  conflict: "Could not save: ",
7446
+ catalogChanged: "The module catalog changed (a plugin or version update). This save was cancelled; reread the settings, then confirm again.",
7335
7447
  computerSource: "Source: computer defaults",
7336
7448
  deviceSource: "Source: this device’s settings",
7337
7449
  pluginSource: "Source: plugin defaults",
@@ -7361,6 +7473,7 @@ window.__ModuleLoader__.load({
7361
7473
  totalUnknown: "Selection total: some sizes are unknown",
7362
7474
  minimal: "Required only (minimal load)",
7363
7475
  minimalConfirm: "Mark everything except required modules and their dependencies as not loaded? Applies on the next open after saving.",
7476
+ minimalDetail: "This excludes {count} optional modules; measured gzip total: {size}; {unknown} remain unmeasured. Required modules and their dependencies stay loaded.",
7364
7477
  retry: "Reread settings",
7365
7478
  saving: "Saving…",
7366
7479
  readTimeout: "Reading timed out. This page and saved settings are unchanged; try again.",
@@ -7398,6 +7511,7 @@ window.__ModuleLoader__.load({
7398
7511
  failed: "Impossibile leggere i moduli. Abilita accesso mobile e riprova.",
7399
7512
  saveFailed: "Salvataggio non confermato. Le scelte restano; salva di nuovo o rileggi le impostazioni salvate.",
7400
7513
  conflict: "Impossibile salvare: ",
7514
+ catalogChanged: "Il catalogo dei moduli è cambiato (aggiornamento di plugin o versione). Salvataggio annullato: rileggi le impostazioni e conferma di nuovo.",
7401
7515
  computerSource: "Origine: predefiniti del computer",
7402
7516
  deviceSource: "Origine: impostazioni del dispositivo",
7403
7517
  pluginSource: "Origine: predefiniti del plugin",
@@ -7427,6 +7541,7 @@ window.__ModuleLoader__.load({
7427
7541
  totalUnknown: "Totale selezione: alcune dimensioni sono sconosciute",
7428
7542
  minimal: "Solo obbligatori (caricamento minimo)",
7429
7543
  minimalConfirm: "Impostare come non caricati tutti i moduli tranne gli obbligatori e le loro dipendenze? Vale alla prossima apertura dopo il salvataggio.",
7544
+ minimalDetail: "Verranno esclusi {count} moduli facoltativi; totale gzip misurato: {size}; {unknown} non misurati. Gli obbligatori e le loro dipendenze restano caricati.",
7430
7545
  retry: "Rileggi impostazioni",
7431
7546
  saving: "Salvataggio…",
7432
7547
  readTimeout: "Lettura scaduta. Pagina e impostazioni salvate non cambiano; riprova.",
@@ -7441,9 +7556,11 @@ window.__ModuleLoader__.load({
7441
7556
  };
7442
7557
  var ClientModuleUiConflict = class extends Error {
7443
7558
  detail;
7444
- constructor(detail) {
7559
+ code;
7560
+ constructor(detail, code = "invalid_selection") {
7445
7561
  super("client_module_conflict");
7446
7562
  this.detail = detail;
7563
+ this.code = code;
7447
7564
  }
7448
7565
  };
7449
7566
  /** A deadline does not imply that a POST was rolled back by the server. */
@@ -7503,12 +7620,16 @@ window.__ModuleLoader__.load({
7503
7620
  };
7504
7621
  });
7505
7622
  if (new Set(entries.map((entry) => entry.id)).size !== entries.length) throw new Error("invalid_module_catalog");
7623
+ const revision = value.revision;
7624
+ if (revision !== void 0 && (typeof revision !== "string" || revision === "" || revision.length > 64)) throw new Error("invalid_module_catalog");
7625
+ const excludedClientModules = value.excludedClientModules.map((id) => {
7626
+ if (typeof id !== "string") throw new Error("invalid_module_catalog");
7627
+ return id;
7628
+ });
7506
7629
  return {
7507
7630
  entries,
7508
- excludedClientModules: value.excludedClientModules.map((id) => {
7509
- if (typeof id !== "string") throw new Error("invalid_module_catalog");
7510
- return id;
7511
- }),
7631
+ ...typeof revision === "string" ? { revision } : {},
7632
+ excludedClientModules,
7512
7633
  source: value.source === "device" ? "device" : value.source === "computer" ? "computer" : "plugin"
7513
7634
  };
7514
7635
  }
@@ -7555,7 +7676,9 @@ window.__ModuleLoader__.load({
7555
7676
  const value = await response.json();
7556
7677
  if (!response.ok) {
7557
7678
  const detail = typeof value === "object" && value !== null && "detail" in value && typeof value.detail === "string" ? value.detail.slice(0, 512) : "";
7558
- if (response.status === 409 && detail !== "" && typeof value === "object" && value !== null && "error" in value && value.error === "excluded_client_modules_invalid") throw new ClientModuleUiConflict(detail);
7679
+ const code = typeof value === "object" && value !== null && "error" in value && typeof value.error === "string" ? value.error : "";
7680
+ if (response.status === 409 && detail !== "" && code === "client_module_catalog_changed") throw new ClientModuleUiConflict(detail, "catalog_changed");
7681
+ if (response.status === 409 && detail !== "" && code === "excluded_client_modules_invalid") throw new ClientModuleUiConflict(detail);
7559
7682
  throw new Error("client_module_request_failed");
7560
7683
  }
7561
7684
  return parseClientModuleView(value);
@@ -7715,6 +7838,7 @@ window.__ModuleLoader__.load({
7715
7838
  let measurementController;
7716
7839
  let generation = 0;
7717
7840
  let draft;
7841
+ let catalogRevision;
7718
7842
  let unconfirmedWrite = false;
7719
7843
  const expansion = /* @__PURE__ */ new Map([
7720
7844
  ["extensions", true],
@@ -7886,12 +8010,13 @@ window.__ModuleLoader__.load({
7886
8010
  };
7887
8011
  const render = (view) => {
7888
8012
  draft = new ClientModuleDraft(view.entries, view.excludedClientModules);
8013
+ catalogRevision = view.revision;
7889
8014
  source.textContent = view.source === "device" ? copy.deviceSource : view.source === "computer" ? copy.computerSource : copy.pluginSource;
7890
8015
  renderList();
7891
8016
  };
7892
8017
  const failed = (error, mutation) => {
7893
8018
  status.classList.add("dsh-module-error");
7894
- status.textContent = error instanceof ClientModuleUiConflict ? `${copy.conflict}${error.detail}` : error instanceof ClientModuleRequestTimeout ? error.mutation ? copy.writeTimeout : copy.readTimeout : mutation ? copy.saveFailed : copy.failed;
8019
+ status.textContent = error instanceof ClientModuleUiConflict ? error.code === "catalog_changed" ? copy.catalogChanged : `${copy.conflict}${error.detail}` : error instanceof ClientModuleRequestTimeout ? error.mutation ? copy.writeTimeout : copy.readTimeout : mutation ? copy.saveFailed : copy.failed;
7895
8020
  status.focus({ preventScroll: true });
7896
8021
  };
7897
8022
  const runRequest = (payload) => {
@@ -7933,13 +8058,24 @@ window.__ModuleLoader__.load({
7933
8058
  search.addEventListener("input", renderList);
7934
8059
  filter.addEventListener("change", renderList);
7935
8060
  save.addEventListener("click", () => {
7936
- if ((draft?.changeCount ?? 0) > 0) runRequest({ excludedClientModules: draft?.excludedIds });
8061
+ if ((draft?.changeCount ?? 0) > 0) runRequest({
8062
+ excludedClientModules: draft?.excludedIds,
8063
+ ...catalogRevision === void 0 ? {} : { revision: catalogRevision }
8064
+ });
7937
8065
  });
7938
8066
  reset.addEventListener("click", () => {
7939
8067
  if (busy === void 0 && draft !== void 0 && window.confirm(copy.resetConfirm)) runRequest({ reset: true });
7940
8068
  });
7941
8069
  minimal.addEventListener("click", () => {
7942
- if (busy !== void 0 || draft === void 0 || !window.confirm(copy.minimalConfirm)) return;
8070
+ if (busy !== void 0 || draft === void 0) return;
8071
+ const impact = clientModuleMinimalImpact(draft.entries);
8072
+ const detail = copy.minimalDetail.replace("{count}", String(impact.count)).replace("{size}", impact.gzipBytes === void 0 ? "—" : formatClientModuleSize({
8073
+ id: "",
8074
+ required: false,
8075
+ dependencies: [],
8076
+ gzipBytes: impact.gzipBytes
8077
+ }) ?? "—").replace("{unknown}", String(impact.unknown));
8078
+ if (!window.confirm(`${copy.minimalConfirm}\n\n${detail}`)) return;
7943
8079
  if (draft.applyMinimalSelection()) {
7944
8080
  renderList();
7945
8081
  search.focus({ preventScroll: true });
@@ -8018,8 +8154,7 @@ window.__ModuleLoader__.load({
8018
8154
  }
8019
8155
  }, copy.open));
8020
8156
  }
8021
- //#endregion
8022
- //#region src/extension-recovery-ui.ts
8157
+ // src/extension-recovery-ui.ts
8023
8158
  /** Validate the Host response and select only stopped workers, never in-process hosts. */
8024
8159
  function parseUnavailableExtensionHosts(payload) {
8025
8160
  if (typeof payload !== "object" || payload === null || !("hosts" in payload) || !Array.isArray(payload.hosts)) return void 0;
@@ -8221,8 +8356,7 @@ window.__ModuleLoader__.load({
8221
8356
  .dsh-mobile-control__extension-recovery button:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover))}
8222
8357
  .dsh-mobile-control__extension-recovery .dsh-mobile-control__extension-recovery-error{color:var(--dsw-alias-label-primary)}
8223
8358
  `;
8224
- //#endregion
8225
- //#region src/native-capability.ts
8359
+ // src/native-capability.ts
8226
8360
  /** Cancel queued calls before native effects start; started effects are not rolled back. */
8227
8361
  function invokeBoundNativeCapability(bridge, currentBridge, action, input, signal) {
8228
8362
  const abortReason = () => signal.reason ?? new DOMException("mobile extension disposed", "AbortError");
@@ -8256,8 +8390,7 @@ window.__ModuleLoader__.load({
8256
8390
  });
8257
8391
  });
8258
8392
  }
8259
- //#endregion
8260
- //#region src/mobile-custom-assets.ts
8393
+ // src/mobile-custom-assets.ts
8261
8394
  const COPY = {
8262
8395
  en: {
8263
8396
  gateway_required: "Custom mobile CSS, scripts and extensions require a paired DSH Mobile access entry. This page is not using the Mobile gateway.",
@@ -8397,8 +8530,7 @@ window.__ModuleLoader__.load({
8397
8530
  reader.releaseLock();
8398
8531
  }
8399
8532
  }
8400
- //#endregion
8401
- //#region src/mobile-customization-notice.ts
8533
+ // src/mobile-customization-notice.ts
8402
8534
  /** Copy for the notice, per locale, kept beside the existing dictionaries. */
8403
8535
  const NOTICE_COPY = {
8404
8536
  en: {
@@ -8516,8 +8648,7 @@ window.__ModuleLoader__.load({
8516
8648
  style.remove();
8517
8649
  };
8518
8650
  }
8519
- //#endregion
8520
- //#region src/extension-limits.ts
8651
+ // src/extension-limits.ts
8521
8652
  /** Maximum sizes accepted for legacy mobile customization resources. */
8522
8653
  const LEGACY_CUSTOM_ASSET_LIMIT = 262144;
8523
8654
  /** Maximum sizes enforced when reading local extension files. */
@@ -8530,8 +8661,7 @@ window.__ModuleLoader__.load({
8530
8661
  assetBytes: 33554432,
8531
8662
  assetDepth: 8
8532
8663
  });
8533
- //#endregion
8534
- //#region src/client-recovery.ts
8664
+ // src/client-recovery.ts
8535
8665
  /** Only a visible, disconnected public transport needs an immediate resume nudge. */
8536
8666
  function shouldNudgeReconnect(visibility, connection) {
8537
8667
  return visibility === "visible" && connection !== "connected";
@@ -8711,15 +8841,13 @@ window.__ModuleLoader__.load({
8711
8841
  onRejected?.(rejected, retry);
8712
8842
  return dispose;
8713
8843
  }
8714
- //#endregion
8715
- //#region src/browser-ui-locale.ts
8844
+ // src/browser-ui-locale.ts
8716
8845
  const UI_LOCALE_COOKIE = "dsh_ma_ui_locale";
8717
8846
  /** Host-only, non-secret preference, independent of the short authentication session. */
8718
8847
  function browserUiLocaleCookie(locale, secure) {
8719
8848
  return `${UI_LOCALE_COOKIE}=${locale}; Path=/mobile-access; Max-Age=31536000; SameSite=Lax${secure ? "; Secure" : ""}`;
8720
8849
  }
8721
- //#endregion
8722
- //#region src/mobile-ui-locale.ts
8850
+ // src/mobile-ui-locale.ts
8723
8851
  /** Remember the DSH display language only on authenticated Mobile Gateway documents. */
8724
8852
  function installMobileUiLocalePersistence(page, view) {
8725
8853
  if (page.querySelector("meta[name=\"dsh-mobile-gateway\"][content=\"authenticated\"]") === null) return () => {};
@@ -8748,8 +8876,7 @@ window.__ModuleLoader__.load({
8748
8876
  observer.disconnect();
8749
8877
  };
8750
8878
  }
8751
- //#endregion
8752
- //#region src/mobile-push/browser-api.ts
8879
+ // src/mobile-push/browser-api.ts
8753
8880
  function apiError(code) {
8754
8881
  return Object.assign(/* @__PURE__ */ new Error(code === "stale_generation" ? "Push settings changed elsewhere." : "Push request failed."), { code });
8755
8882
  }
@@ -8815,16 +8942,14 @@ window.__ModuleLoader__.load({
8815
8942
  }
8816
8943
  };
8817
8944
  }
8818
- //#endregion
8819
- //#region src/mobile-push/installation-id.ts
8945
+ // src/mobile-push/installation-id.ts
8820
8946
  /** Browser-safe installation-id validation shared by the browser controller and the Gateway. */
8821
8947
  const INSTALLATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
8822
8948
  /** True only for a UUID-shaped installation id with an allowed version and variant nibble. */
8823
8949
  function isPushInstallationId(value) {
8824
8950
  return typeof value === "string" && INSTALLATION_ID.test(value);
8825
8951
  }
8826
- //#endregion
8827
- //#region src/mobile-push/client.ts
8952
+ // src/mobile-push/client.ts
8828
8953
  const ID_KEY = "dsh-mobile-push-installation";
8829
8954
  const defaults = Object.freeze({
8830
8955
  question: true,
@@ -9259,8 +9384,7 @@ window.__ModuleLoader__.load({
9259
9384
  locale: view.document.documentElement.lang.toLowerCase().startsWith("zh") ? "zh-CN" : view.document.documentElement.lang.toLowerCase().startsWith("it") ? "it" : "en"
9260
9385
  };
9261
9386
  }
9262
- //#endregion
9263
- //#region src/mobile-push/switch-component.ts
9387
+ // src/mobile-push/switch-component.ts
9264
9388
  /**
9265
9389
  * Lazily require the public Switch from the DSH module loader at category render time.
9266
9390
  *
@@ -9280,8 +9404,7 @@ window.__ModuleLoader__.load({
9280
9404
  return;
9281
9405
  }
9282
9406
  }
9283
- //#endregion
9284
- //#region src/mobile-push/settings.ts
9407
+ // src/mobile-push/settings.ts
9285
9408
  const MOBILE_PUSH_SETTINGS_COPY = {
9286
9409
  en: {
9287
9410
  title: "Web push notifications",
@@ -9441,8 +9564,7 @@ window.__ModuleLoader__.load({
9441
9564
  disabled: true
9442
9565
  }, copy.busy)));
9443
9566
  }
9444
- //#endregion
9445
- //#region src/mobile-push/navigation.ts
9567
+ // src/mobile-push/navigation.ts
9446
9568
  const REF_PARAMETER = "mobile-push";
9447
9569
  /**
9448
9570
  * Consume one authenticated tap reference after the public Workspace and Session
@@ -9521,8 +9643,7 @@ window.__ModuleLoader__.load({
9521
9643
  const snapshot = ctx.sessions.list.getSnapshot();
9522
9644
  return snapshot.phase === "ready" && snapshot.ids.includes(sessionId) && snapshot.byId[sessionId] !== void 0;
9523
9645
  }
9524
- //#endregion
9525
- //#region src/mobile-directory-browser.ts
9646
+ // src/mobile-directory-browser.ts
9526
9647
  /** Expected Gateway or response-validation failure for one read-only listing. */
9527
9648
  var MobileDirectoryReadError = class extends Error {
9528
9649
  code;
@@ -9916,8 +10037,7 @@ window.__ModuleLoader__.load({
9916
10037
  for (const listener of this.listeners) listener();
9917
10038
  }
9918
10039
  };
9919
- //#endregion
9920
- //#region src/mobile-directory-picker.ts
10040
+ // src/mobile-directory-picker.ts
9921
10041
  /** Dedicated Mobile directory-flow occupant; stock DSH retains its own chooser. */
9922
10042
  const MESSAGES = {
9923
10043
  zh: {
@@ -10589,8 +10709,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
10589
10709
  if (failures.length > 0) throw new AggregateError(failures, "Mobile directory picker disposal failed");
10590
10710
  };
10591
10711
  }
10592
- //#endregion
10593
- //#region src/client.ts
10712
+ // src/client.ts
10594
10713
  /** Map a host-reported provider onto the UI union, defaulting to the built-in one. */
10595
10714
  function parseMobileRemoteProvider(value) {
10596
10715
  return value === "cpolar" || value === "cloudflared" || value === "frp" || value === "origin" || value === "iroh" ? value : "tailscale";
@@ -10674,7 +10793,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
10674
10793
  const wrapper = top.parentElement;
10675
10794
  const mask = wrapper?.firstElementChild;
10676
10795
  const maskStyle = mask === void 0 || mask === null ? void 0 : view.getComputedStyle(mask);
10677
- if (wrapper?.getAttribute("role") === "presentation" && wrapper.children.length === 2 && wrapper.lastElementChild === top && mask?.tagName === "DIV" && mask.getAttribute("aria-hidden") === "true" && mask.isConnected && !mask.hidden && maskStyle?.display !== "none" && maskStyle?.visibility !== "hidden" && mask.getClientRects().length > 0) {
10796
+ if (wrapper?.getAttribute("role") === "presentation" && wrapper.children.length === 2 && wrapper.lastElementChild === top && mask?.tagName === "DIV" && mask.getAttribute("aria-hidden") !== "false" && mask.getAttribute("role") === null && mask.getAttribute("tabindex") === null && mask.getAttribute("contenteditable") === null && mask.children.length === 0 && (mask.textContent ?? "").trim() === "" && mask.isConnected && !mask.hidden && maskStyle?.display !== "none" && maskStyle?.visibility !== "hidden" && mask.getClientRects().length > 0) {
10678
10797
  mask.click();
10679
10798
  return;
10680
10799
  }
@@ -11282,7 +11401,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
11282
11401
  const facts = entry.facts !== null && typeof entry.facts === "object" ? entry.facts : {};
11283
11402
  const copyReason = facts.viaProxy === true && (reason === "remote-ready" || reason === "remote-rate-limited") ? `${reason}-via-proxy` : reason;
11284
11403
  const catalog = DIAGNOSTIC_REASON_MESSAGES[locale];
11285
- const templates = Object.prototype.hasOwnProperty.call(catalog, copyReason) ? catalog[copyReason] : void 0;
11404
+ const networkCatalog = DIAGNOSTIC_NETWORK_MESSAGES[locale];
11405
+ const templates = Object.prototype.hasOwnProperty.call(networkCatalog, copyReason) ? networkCatalog[copyReason] : Object.prototype.hasOwnProperty.call(catalog, copyReason) ? catalog[copyReason] : void 0;
11286
11406
  if (templates === void 0) return serverCopy;
11287
11407
  const providerNames = {
11288
11408
  tailscale: "Tailscale Funnel",
@@ -11305,6 +11425,15 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
11305
11425
  const interpolate = (template) => template.replace(/\{(\w+)\}/gu, (_match, key) => values[key] ?? "");
11306
11426
  let detail = reason === "remote-controller-error" && controllerCode === "" ? t("remoteError") : interpolate(templates[0]);
11307
11427
  let action = interpolate(templates[1]);
11428
+ if (reason.startsWith("dns-")) {
11429
+ const families = {
11430
+ ipv4: "IPv4",
11431
+ ipv6: "IPv6",
11432
+ dual: "IPv4 + IPv6"
11433
+ };
11434
+ const family = typeof facts.dnsFamily === "string" && Object.hasOwn(families, facts.dnsFamily) ? families[facts.dnsFamily] : void 0;
11435
+ if (family !== void 0) detail += ` (${family})`;
11436
+ }
11308
11437
  if (reason === "versions-current") {
11309
11438
  const fields = versions !== null && typeof versions === "object" ? versions : {};
11310
11439
  if ([
@@ -13543,6 +13672,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
13543
13672
  const attachSelected = frpMode.value === "attach";
13544
13673
  const selfSignedSelected = attachSelected && frpEntryTls.value === "self-signed";
13545
13674
  frpStep2Title.textContent = t(attachSelected ? "frpStep2TitleAttach" : "frpStep2Title");
13675
+ frpVpsSummary.textContent = t(attachSelected ? "frpStep2TitleAttach" : "frpStep2Title");
13546
13676
  frpStep2Text.textContent = attachSelected && configuredFrpProxyName === void 0 ? t("loadingRemoteStatus") : t(attachSelected ? "frpStep2TextAttach" : "frpStep2Text");
13547
13677
  frpStep4Text.textContent = t(attachSelected ? "frpStep4TextAttach" : "frpStep4Text");
13548
13678
  frpAppRequirement.textContent = t(selfSignedSelected ? "frpAppRequirementSelfSigned" : "frpAppRequirement");
@@ -15250,11 +15380,13 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
15250
15380
  });
15251
15381
  });
15252
15382
  const renderDiagnostics = (data) => {
15383
+ const scopeCopy = DIAGNOSTIC_SCOPE_MESSAGES[locale];
15384
+ const scopeOf = (entry) => entry.scope === "configuration" || entry.id === "versions" ? scopeCopy.configuration : entry.scope === "phone" || entry.id === "phone-network" ? scopeCopy.phone : scopeCopy.computer;
15253
15385
  const entries = [...diagnosticEntriesForRender(data)];
15254
15386
  const overall = diagnosticOverallForChecks(data.overall, entries.map((entry) => entry.status));
15255
15387
  diagnosticsSummary.className = `dsh-mobile-control__diagnostic-summary is-${overall}`;
15256
- diagnosticsSummaryTitle.textContent = overall === "ok" ? t("diagnosticsComplete") : overall === "attention" ? t("diagnosticsAttention") : t("diagnosticsProblem");
15257
- diagnosticsSummaryText.textContent = locale === "zh" && data.overall === overall && typeof data.summary === "string" ? data.summary : t("diagnosticsCompleteFallback");
15388
+ diagnosticsSummaryTitle.textContent = `${scopeCopy.computer} · ${overall === "ok" ? t("diagnosticsComplete") : overall === "attention" ? t("diagnosticsAttention") : t("diagnosticsProblem")}`;
15389
+ diagnosticsSummaryText.textContent = scopeCopy.coverage;
15258
15390
  diagnosticsChecks.replaceChildren();
15259
15391
  const statusLabels = {
15260
15392
  ok: t("diagnosticStatusOk"),
@@ -15299,8 +15431,14 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
15299
15431
  const localizedCopy = localizedEntryCopy(entry);
15300
15432
  const detail = element("p");
15301
15433
  detail.textContent = localizedCopy.detail;
15434
+ if (entry.id === "versions") rowTitle.textContent = scopeCopy.versions;
15435
+ else if (entry.id === "remote-service") rowTitle.textContent = scopeCopy.provider;
15436
+ else if (entry.id === "remote-dns") rowTitle.textContent = scopeCopy.dns;
15437
+ else if (entry.id === "plugin-inspection" || entry.id === "remote-plugin-conflict") rowTitle.textContent = scopeCopy.scripts;
15438
+ const scope = element("small", "dsh-mobile-control__diagnostic-scope");
15439
+ scope.textContent = scopeOf(entry);
15302
15440
  rowHeader.append(rowTitle, badge);
15303
- rowBody.append(rowHeader, detail);
15441
+ rowBody.append(rowHeader, scope, detail);
15304
15442
  if (localizedCopy.action !== "") {
15305
15443
  const action = element("p", "dsh-mobile-control__diagnostic-action");
15306
15444
  const actionLabel = element("span");
@@ -15330,11 +15468,13 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
15330
15468
  const labelKey = typeof entry.id === "string" ? diagnosticLabelKeys[entry.id] : void 0;
15331
15469
  const label = labelKey === void 0 && typeof entry.label === "string" ? entry.label : labelKey === void 0 ? t("diagnosticCheck") : t(labelKey);
15332
15470
  const localizedCopy = localizedEntryCopy(entry);
15333
- return `[${statusLabels[state]}] ${label}: ${localizedCopy.detail}${localizedCopy.action === "" ? "" : ` ${localizedCopy.action}`}`;
15471
+ const scopedLabel = entry.id === "versions" ? scopeCopy.versions : entry.id === "remote-service" ? scopeCopy.provider : entry.id === "remote-dns" ? scopeCopy.dns : entry.id === "plugin-inspection" || entry.id === "remote-plugin-conflict" ? scopeCopy.scripts : label;
15472
+ return `[${statusLabels[state]}][${scopeOf(entry)}] ${scopedLabel}: ${localizedCopy.detail}${localizedCopy.action === "" ? "" : ` ${localizedCopy.action}`}`;
15334
15473
  });
15335
15474
  copiedDiagnosticReport = [
15336
15475
  reportCopy.reportTitle,
15337
15476
  `${reportCopy.generated}: ${generatedAt}`,
15477
+ scopeCopy.coverage,
15338
15478
  ...lines
15339
15479
  ].join("\n");
15340
15480
  diagnosticsReport.textContent = copiedDiagnosticReport;
@@ -16444,6 +16584,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
16444
16584
  manifestEtag = "";
16445
16585
  };
16446
16586
  const refreshExtensions = async (signal) => {
16587
+ let failureStage = "manifest_unavailable";
16447
16588
  try {
16448
16589
  const headers = {};
16449
16590
  if (manifestEtag !== "") headers["if-none-match"] = manifestEtag;
@@ -16486,6 +16627,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
16486
16627
  failed("manifest_invalid", signal);
16487
16628
  return false;
16488
16629
  }
16630
+ failureStage = "resource_unavailable";
16489
16631
  events?.setEnabled(true);
16490
16632
  const entries = payload.extensions;
16491
16633
  const seen = new Set(entries.map((entry) => entry.id));
@@ -16645,7 +16787,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
16645
16787
  } else manifestEtag = "";
16646
16788
  return refreshComplete;
16647
16789
  } catch {
16648
- failed("refresh_failed", signal);
16790
+ failed(failureStage, signal);
16649
16791
  return false;
16650
16792
  }
16651
16793
  };
@@ -16777,7 +16919,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
16777
16919
  style rule of its own. */
16778
16920
  .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}
16779
16921
  .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%))}
16780
- .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>.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)}
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)}
16781
16923
  .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}
16782
16924
  .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}
16783
16925
  .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}
@@ -17142,9 +17284,10 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
17142
17284
  }
17143
17285
  /** Client services required by the mobile adaptation. */
17144
17286
  const inject = ["slots"];
17145
- //#endregion
17146
17287
  exports.CONTROL_STYLES = CONTROL_STYLES;
17288
+ exports.DIAGNOSTIC_NETWORK_MESSAGES = DIAGNOSTIC_NETWORK_MESSAGES;
17147
17289
  exports.DIAGNOSTIC_REASON_MESSAGES = DIAGNOSTIC_REASON_MESSAGES;
17290
+ exports.DIAGNOSTIC_SCOPE_MESSAGES = DIAGNOSTIC_SCOPE_MESSAGES;
17148
17291
  exports.LOCALIZED_DIAGNOSTIC_COPY = LOCALIZED_DIAGNOSTIC_COPY;
17149
17292
  exports.MOBILE_CONTROL_MESSAGES = MOBILE_CONTROL_MESSAGES;
17150
17293
  exports.PerIdActivationLifecycle = PerIdActivationLifecycle;