dsh-mobile 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/FUNNEL_THIRD_PARTY_LICENSES.txt +2551 -0
- package/README.en.md +22 -10
- package/README.md +19 -8
- package/SECURITY.md +6 -3
- package/THIRD_PARTY_NOTICES.md +6 -2
- package/bin/dsh-mobile-funnel-win32-x64.exe +0 -0
- package/cordis.patch.yml +3 -0
- package/lib/cli.js +4 -1
- package/lib/cli.js.map +1 -0
- package/lib/client.js +715 -58
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +228 -3
- package/lib/index.mjs +1966 -396
- package/lib/index.mjs.map +1 -0
- package/package.json +17 -11
- package/assets/brand/app-icon-master.png +0 -0
- package/assets/brand/repository-hero.png +0 -0
package/lib/client.js
CHANGED
|
@@ -10,8 +10,15 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
en: {
|
|
11
11
|
mobileAccess: "Mobile access",
|
|
12
12
|
collapseMobileAccess: "Collapse Mobile access",
|
|
13
|
-
downloadAndroid: "Download Android app",
|
|
13
|
+
downloadAndroid: "Download latest Android app",
|
|
14
|
+
downloadAndroidVersion: "Download latest Android app · v{version}",
|
|
14
15
|
downloadAndroidAria: "Download the latest Android app from GitHub Releases",
|
|
16
|
+
downloadAndroidVersionAria: "Download Android app v{version} from GitHub Releases",
|
|
17
|
+
updatePlugin: "Update plugin",
|
|
18
|
+
updatePluginAria: "Update DSH Mobile to v{version}",
|
|
19
|
+
updatingPlugin: "Updating…",
|
|
20
|
+
pluginUpdatedRestart: "Updated to v{version}. Restart DSH to apply it.",
|
|
21
|
+
pluginUpdateFailed: "Update failed: {error}",
|
|
15
22
|
lan: "Local network",
|
|
16
23
|
remote: "Remote",
|
|
17
24
|
lanAccess: "Local network access",
|
|
@@ -27,6 +34,10 @@ window.__ModuleLoader__.load({
|
|
|
27
34
|
pairingQr: "Pairing QR code",
|
|
28
35
|
remoteIntro: "Choose the remote channel that suits you. Switching or disabling remote access does not affect the local network.",
|
|
29
36
|
chooseProvider: "Choose connection method",
|
|
37
|
+
currentProvider: "Current connection",
|
|
38
|
+
remoteStateOff: "Off",
|
|
39
|
+
remoteStateConnecting: "Connecting",
|
|
40
|
+
remoteStateAttention: "Needs attention",
|
|
30
41
|
providerInfoAria: "View remote connection security and network information",
|
|
31
42
|
providerGroupAria: "Remote connection method",
|
|
32
43
|
providerSafeTitle: "You remain protected",
|
|
@@ -145,8 +156,15 @@ window.__ModuleLoader__.load({
|
|
|
145
156
|
it: {
|
|
146
157
|
mobileAccess: "Accesso mobile",
|
|
147
158
|
collapseMobileAccess: "Riduci Accesso mobile",
|
|
148
|
-
downloadAndroid: "Scarica app Android",
|
|
159
|
+
downloadAndroid: "Scarica l’ultima app Android",
|
|
160
|
+
downloadAndroidVersion: "Scarica l’ultima app Android · v{version}",
|
|
149
161
|
downloadAndroidAria: "Scarica l’ultima app Android da GitHub Releases",
|
|
162
|
+
downloadAndroidVersionAria: "Scarica l’app Android v{version} da GitHub Releases",
|
|
163
|
+
updatePlugin: "Aggiorna plugin",
|
|
164
|
+
updatePluginAria: "Aggiorna DSH Mobile alla v{version}",
|
|
165
|
+
updatingPlugin: "Aggiornamento…",
|
|
166
|
+
pluginUpdatedRestart: "Aggiornato alla v{version}. Riavvia DSH per applicarlo.",
|
|
167
|
+
pluginUpdateFailed: "Aggiornamento non riuscito: {error}",
|
|
150
168
|
lan: "Rete locale",
|
|
151
169
|
remote: "Remoto",
|
|
152
170
|
lanAccess: "Accesso rete locale",
|
|
@@ -162,6 +180,10 @@ window.__ModuleLoader__.load({
|
|
|
162
180
|
pairingQr: "Codice QR di abbinamento",
|
|
163
181
|
remoteIntro: "Scegli il canale remoto più adatto. Cambiare o disattivare l’accesso remoto non influisce sulla rete locale.",
|
|
164
182
|
chooseProvider: "Scegli metodo di connessione",
|
|
183
|
+
currentProvider: "Connessione attuale",
|
|
184
|
+
remoteStateOff: "Disattivata",
|
|
185
|
+
remoteStateConnecting: "Connessione",
|
|
186
|
+
remoteStateAttention: "Da controllare",
|
|
165
187
|
providerInfoAria: "Informazioni su sicurezza e rete della connessione remota",
|
|
166
188
|
providerGroupAria: "Metodo di connessione remota",
|
|
167
189
|
providerSafeTitle: "La protezione resta attiva",
|
|
@@ -282,8 +304,15 @@ window.__ModuleLoader__.load({
|
|
|
282
304
|
Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
|
|
283
305
|
mobileAccess: "移动访问",
|
|
284
306
|
collapseMobileAccess: "收起移动访问",
|
|
285
|
-
downloadAndroid: "
|
|
286
|
-
|
|
307
|
+
downloadAndroid: "下载最新 Android App",
|
|
308
|
+
downloadAndroidVersion: "下载最新 Android App · v{version}",
|
|
309
|
+
downloadAndroidAria: "从 GitHub Releases 下载最新版 Android App",
|
|
310
|
+
downloadAndroidVersionAria: "从 GitHub Releases 下载 Android App v{version}",
|
|
311
|
+
updatePlugin: "更新插件",
|
|
312
|
+
updatePluginAria: "将 DSH Mobile 更新至 v{version}",
|
|
313
|
+
updatingPlugin: "正在更新…",
|
|
314
|
+
pluginUpdatedRestart: "插件已更新至 v{version},重启 DSH 后生效。",
|
|
315
|
+
pluginUpdateFailed: "更新失败:{error}",
|
|
287
316
|
lan: "局域网",
|
|
288
317
|
remote: "远程",
|
|
289
318
|
lanAccess: "局域网访问",
|
|
@@ -299,6 +328,10 @@ window.__ModuleLoader__.load({
|
|
|
299
328
|
pairingQr: "配对二维码",
|
|
300
329
|
remoteIntro: "选择更适合你的远程通道。切换或关闭远程访问不会影响局域网。",
|
|
301
330
|
chooseProvider: "选择连接方式",
|
|
331
|
+
currentProvider: "当前连接",
|
|
332
|
+
remoteStateOff: "未启用",
|
|
333
|
+
remoteStateConnecting: "连接中",
|
|
334
|
+
remoteStateAttention: "需处理",
|
|
302
335
|
providerInfoAria: "查看远程连接安全与网络说明",
|
|
303
336
|
providerGroupAria: "远程连接方式",
|
|
304
337
|
providerSafeTitle: "你始终可以放心",
|
|
@@ -414,6 +447,192 @@ window.__ModuleLoader__.load({
|
|
|
414
447
|
resetTailscaleConfirm: "退出电脑上的 Tailscale 登录并移除所有远程配对设备?局域网配置不会改变。",
|
|
415
448
|
requestFailed: "请求失败:{error}"
|
|
416
449
|
});
|
|
450
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
451
|
+
selfHostedConnections: "Self-hosted connection",
|
|
452
|
+
selfHostedDescription: "For users with a VPS and domain. It does not depend on public tunnel bandwidth.",
|
|
453
|
+
advanced: "Advanced",
|
|
454
|
+
frpName: "Self-hosted FRP",
|
|
455
|
+
frpDescription: "A restricted FRP channel for this DSH gateway only. You manage the VPS and public domain.",
|
|
456
|
+
prepareFrp: "Configure self-hosted FRP",
|
|
457
|
+
frpStep1Title: "1 · Connection details",
|
|
458
|
+
frpStep1Text: "Enter the VPS address, frps control port, shared token, and public HTTPS origin.",
|
|
459
|
+
frpServerAddress: "VPS address",
|
|
460
|
+
frpServerAddressPlaceholder: "frp.example.com",
|
|
461
|
+
frpServerPort: "frps port",
|
|
462
|
+
frpToken: "Shared token",
|
|
463
|
+
frpTokenPlaceholder: "At least 16 characters",
|
|
464
|
+
frpPublicOrigin: "Public HTTPS origin",
|
|
465
|
+
frpPublicOriginPlaceholder: "https://dsh.example.com",
|
|
466
|
+
frpStep2Title: "2 · Prepare the VPS",
|
|
467
|
+
frpStep2Text: "Copy the restricted frps and Caddy template, then apply it on your VPS.",
|
|
468
|
+
copyServerTemplate: "Copy server template",
|
|
469
|
+
templateCopied: "Server template copied. Apply frps.toml and the Caddyfile on the VPS.",
|
|
470
|
+
templateCopyFailed: "Could not copy the template. Check the four fields above.",
|
|
471
|
+
frpStep3Title: "3 · Prepare this computer",
|
|
472
|
+
frpStep3Text: "Download and verify the pinned official frpc client only when requested.",
|
|
473
|
+
installFrpc: "Install official frpc",
|
|
474
|
+
frpInstallConfirm: "Download and verify official frpc 0.70.1 (about {size} MB), extracting only frpc to the DSH Mobile private directory? No service, PATH entry, or startup item is added.",
|
|
475
|
+
installingFrp: "Installing the official frpc component. Keep DSH running until it completes.",
|
|
476
|
+
frpNotInstalled: "frpc is not installed. Nothing is downloaded until you click Install.",
|
|
477
|
+
frpUnsupported: "This desktop platform is not supported by the managed frpc installer.",
|
|
478
|
+
frpComponentReady: "Official frpc {version} is verified.",
|
|
479
|
+
frpStep4Title: "4 · Verify and connect",
|
|
480
|
+
frpStep4Text: "The plugin starts only its DSH HTTP vhost and verifies the public endpoint belongs to this computer.",
|
|
481
|
+
frpAppRequirement: "Custom remote domains require Android app 0.3.3 or later; local network, cpolar, and Tailscale remain compatible with older supported apps.",
|
|
482
|
+
frpSaveConnect: "Save and verify connection",
|
|
483
|
+
frpSavingConnecting: "Saving the private settings and verifying the complete FRP path…",
|
|
484
|
+
frpConfigurationReady: "Server settings are saved. The token remains only in the plugin private directory.",
|
|
485
|
+
frpConfigurationMissing: "Complete all fields, copy the VPS template, then save and connect.",
|
|
486
|
+
frpInputInvalid: "Check the VPS address, port, HTTPS origin, and token.",
|
|
487
|
+
frpComponentDetails: "FRP source and complete cleanup",
|
|
488
|
+
frpComponentDetailsText: "The pinned client is downloaded from the official fatedier/frp release. DSH Mobile never installs a service or accepts arbitrary FRP configuration.",
|
|
489
|
+
frpOfficialRelease: "Official FRP release",
|
|
490
|
+
purgeFrp: "Remove FRP component and private configuration",
|
|
491
|
+
purgeFrpConfirm: "Stop FRP and remove frpc, the shared token, generated configuration, staging files, and logs owned by DSH Mobile? The VPS is unchanged.",
|
|
492
|
+
purgingFrp: "Stopping FRP and removing files managed by DSH Mobile…",
|
|
493
|
+
switchingFrp: "Switching to self-hosted FRP…",
|
|
494
|
+
reconnectingFrp: "Verifying and reconnecting self-hosted FRP…",
|
|
495
|
+
resetFrpConfirm: "Disable self-hosted FRP and remove all remote paired devices? The saved VPS settings and server are unchanged.",
|
|
496
|
+
remoteUnavailableFrp: "frpc is not installed or the private server settings are incomplete.",
|
|
497
|
+
remoteStartingFrp: "Checking the VPS and preparing the restricted FRP channel…",
|
|
498
|
+
remoteConnectingFrp: "frpc is running; verifying the public HTTPS endpoint…",
|
|
499
|
+
frpMissing: "Install the official frpc component first.",
|
|
500
|
+
frpInvalid: "frpc verification failed. Remove it completely and reinstall.",
|
|
501
|
+
frpConfigMissing: "Save the self-hosted FRP connection details first.",
|
|
502
|
+
frpConfigVerifyFailed: "frpc rejected the generated configuration. Check the server details and token.",
|
|
503
|
+
frpVhostPublic: "The VPS plaintext HTTP vhost is publicly reachable. Bind it to 127.0.0.1 and let Caddy provide HTTPS.",
|
|
504
|
+
frpVhostProbeFailed: "The VPS address could not be checked. Confirm DNS and retry.",
|
|
505
|
+
frpLaunchFailed: "frpc failed to start.",
|
|
506
|
+
frpTimeout: "The public endpoint did not become ready. Check frps, Caddy, DNS, and firewall.",
|
|
507
|
+
frpDiscoveryMismatch: "The public domain points to another DSH computer. Check the Caddy and frps mappings.",
|
|
508
|
+
frpDiscoveryInvalid: "The public domain did not return a valid DSH Mobile discovery response.",
|
|
509
|
+
frpStopped: "The FRP connection stopped.",
|
|
510
|
+
frpExited: "frpc exited unexpectedly. Check the VPS configuration and reconnect."
|
|
511
|
+
});
|
|
512
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.it, {
|
|
513
|
+
selfHostedConnections: "Connessione autogestita",
|
|
514
|
+
selfHostedDescription: "Per chi dispone di VPS e dominio. Non dipende dalla banda dei tunnel pubblici.",
|
|
515
|
+
advanced: "Avanzato",
|
|
516
|
+
frpName: "FRP autogestito",
|
|
517
|
+
frpDescription: "Canale FRP limitato al solo gateway DSH. VPS e dominio pubblico restano sotto il tuo controllo.",
|
|
518
|
+
prepareFrp: "Configura FRP autogestito",
|
|
519
|
+
frpStep1Title: "1 · Dati di connessione",
|
|
520
|
+
frpStep1Text: "Inserisci indirizzo VPS, porta di controllo frps, token condiviso e origine HTTPS pubblica.",
|
|
521
|
+
frpServerAddress: "Indirizzo VPS",
|
|
522
|
+
frpServerAddressPlaceholder: "frp.example.com",
|
|
523
|
+
frpServerPort: "Porta frps",
|
|
524
|
+
frpToken: "Token condiviso",
|
|
525
|
+
frpTokenPlaceholder: "Almeno 16 caratteri",
|
|
526
|
+
frpPublicOrigin: "Origine HTTPS pubblica",
|
|
527
|
+
frpPublicOriginPlaceholder: "https://dsh.example.com",
|
|
528
|
+
frpStep2Title: "2 · Prepara il VPS",
|
|
529
|
+
frpStep2Text: "Copia il modello limitato per frps e Caddy e applicalo sul VPS.",
|
|
530
|
+
copyServerTemplate: "Copia modello server",
|
|
531
|
+
templateCopied: "Modello server copiato. Applica frps.toml e Caddyfile sul VPS.",
|
|
532
|
+
templateCopyFailed: "Impossibile copiare il modello. Controlla i quattro campi sopra.",
|
|
533
|
+
frpStep3Title: "3 · Prepara questo computer",
|
|
534
|
+
frpStep3Text: "Scarica e verifica il client frpc ufficiale fissato solo su richiesta.",
|
|
535
|
+
installFrpc: "Installa frpc ufficiale",
|
|
536
|
+
frpInstallConfirm: "Scaricare e verificare frpc ufficiale 0.70.1 (circa {size} MB), estraendo solo frpc nella directory privata DSH Mobile? Non vengono aggiunti servizi, PATH o avvio automatico.",
|
|
537
|
+
installingFrp: "Installazione del componente frpc ufficiale. Mantieni DSH in esecuzione.",
|
|
538
|
+
frpNotInstalled: "frpc non è installato. Non viene scaricato nulla finché non scegli Installa.",
|
|
539
|
+
frpUnsupported: "Questa piattaforma desktop non è supportata dal programma di installazione gestito.",
|
|
540
|
+
frpComponentReady: "frpc ufficiale {version} verificato.",
|
|
541
|
+
frpStep4Title: "4 · Verifica e connetti",
|
|
542
|
+
frpStep4Text: "Il plugin avvia solo il vhost HTTP DSH e verifica che l’endpoint pubblico appartenga a questo computer.",
|
|
543
|
+
frpAppRequirement: "I domini remoti personalizzati richiedono l’app Android 0.3.3 o successiva; rete locale, cpolar e Tailscale restano compatibili con le app precedenti supportate.",
|
|
544
|
+
frpSaveConnect: "Salva e verifica connessione",
|
|
545
|
+
frpSavingConnecting: "Salvataggio privato e verifica del percorso FRP completo…",
|
|
546
|
+
frpConfigurationReady: "Impostazioni server salvate. Il token resta solo nella directory privata del plugin.",
|
|
547
|
+
frpConfigurationMissing: "Completa i campi, copia il modello VPS, quindi salva e connetti.",
|
|
548
|
+
frpInputInvalid: "Controlla indirizzo VPS, porta, origine HTTPS e token.",
|
|
549
|
+
frpComponentDetails: "Origine FRP e rimozione completa",
|
|
550
|
+
frpComponentDetailsText: "Il client fissato viene scaricato dalla release ufficiale fatedier/frp. DSH Mobile non installa servizi e non accetta configurazioni FRP arbitrarie.",
|
|
551
|
+
frpOfficialRelease: "Release FRP ufficiale",
|
|
552
|
+
purgeFrp: "Rimuovi componente FRP e configurazione privata",
|
|
553
|
+
purgeFrpConfirm: "Arrestare FRP e rimuovere frpc, token, configurazione, file temporanei e log gestiti da DSH Mobile? Il VPS non viene modificato.",
|
|
554
|
+
purgingFrp: "Arresto FRP e rimozione dei file gestiti da DSH Mobile…",
|
|
555
|
+
switchingFrp: "Passaggio a FRP autogestito…",
|
|
556
|
+
reconnectingFrp: "Verifica e riconnessione FRP autogestito…",
|
|
557
|
+
resetFrpConfirm: "Disattivare FRP autogestito e rimuovere tutti i dispositivi remoti? Impostazioni VPS e server restano invariati.",
|
|
558
|
+
remoteUnavailableFrp: "frpc non è installato o le impostazioni private sono incomplete.",
|
|
559
|
+
remoteStartingFrp: "Controllo VPS e preparazione del canale FRP limitato…",
|
|
560
|
+
remoteConnectingFrp: "frpc è in esecuzione; verifica dell’endpoint HTTPS pubblico…",
|
|
561
|
+
frpMissing: "Installa prima il componente frpc ufficiale.",
|
|
562
|
+
frpInvalid: "Verifica frpc non riuscita. Rimuovilo e reinstallalo.",
|
|
563
|
+
frpConfigMissing: "Salva prima i dati della connessione FRP.",
|
|
564
|
+
frpConfigVerifyFailed: "frpc ha rifiutato la configurazione generata. Controlla server e token.",
|
|
565
|
+
frpVhostPublic: "Il vhost HTTP in chiaro è raggiungibile pubblicamente. Limitalo a 127.0.0.1 e usa Caddy per HTTPS.",
|
|
566
|
+
frpVhostProbeFailed: "Impossibile controllare il VPS. Verifica il DNS e riprova.",
|
|
567
|
+
frpLaunchFailed: "Avvio di frpc non riuscito.",
|
|
568
|
+
frpTimeout: "Endpoint pubblico non pronto. Controlla frps, Caddy, DNS e firewall.",
|
|
569
|
+
frpDiscoveryMismatch: "Il dominio pubblico punta a un altro computer DSH. Controlla Caddy e frps.",
|
|
570
|
+
frpDiscoveryInvalid: "Il dominio pubblico non ha restituito una risposta DSH Mobile valida.",
|
|
571
|
+
frpStopped: "Connessione FRP arrestata.",
|
|
572
|
+
frpExited: "frpc è terminato inaspettatamente. Controlla il VPS e riconnettiti."
|
|
573
|
+
});
|
|
574
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
|
|
575
|
+
selfHostedConnections: "自建连接",
|
|
576
|
+
selfHostedDescription: "适合已有 VPS 和域名的用户,不受公共服务带宽限制。",
|
|
577
|
+
advanced: "高级",
|
|
578
|
+
frpName: "自建 FRP",
|
|
579
|
+
frpDescription: "只为当前 DSH 网关建立单用途 FRP 通道,VPS 和公开域名由你掌控。",
|
|
580
|
+
prepareFrp: "配置自建 FRP",
|
|
581
|
+
frpStep1Title: "1 · 填写连接信息",
|
|
582
|
+
frpStep1Text: "填写 VPS 地址、frps 控制端口、共享 Token 和公开 HTTPS 地址。",
|
|
583
|
+
frpServerAddress: "VPS 地址",
|
|
584
|
+
frpServerAddressPlaceholder: "frp.example.com",
|
|
585
|
+
frpServerPort: "frps 端口",
|
|
586
|
+
frpToken: "共享 Token",
|
|
587
|
+
frpTokenPlaceholder: "至少 16 个字符",
|
|
588
|
+
frpPublicOrigin: "公开 HTTPS 地址",
|
|
589
|
+
frpPublicOriginPlaceholder: "https://dsh.example.com",
|
|
590
|
+
frpStep2Title: "2 · 准备 VPS",
|
|
591
|
+
frpStep2Text: "复制受限的 frps 与 Caddy 模板,并在 VPS 上应用。",
|
|
592
|
+
copyServerTemplate: "复制服务器模板",
|
|
593
|
+
templateCopied: "服务器模板已复制,请在 VPS 应用 frps.toml 和 Caddyfile。",
|
|
594
|
+
templateCopyFailed: "无法复制模板,请检查上方四项信息。",
|
|
595
|
+
frpStep3Title: "3 · 准备这台电脑",
|
|
596
|
+
frpStep3Text: "仅在你确认后下载并校验固定版本的官方 frpc。",
|
|
597
|
+
installFrpc: "安装官方 frpc",
|
|
598
|
+
frpInstallConfirm: "从 FRP 官方 Release 下载并校验 frpc 0.70.1(约 {size} MB),只提取 frpc 到 DSH Mobile 私有目录?不会安装服务、写入 PATH 或设置开机启动。",
|
|
599
|
+
installingFrp: "正在安装官方 frpc,完成前请保持 DSH 运行。",
|
|
600
|
+
frpNotInstalled: "frpc 尚未安装。点击安装前不会下载任何内容。",
|
|
601
|
+
frpUnsupported: "当前桌面平台暂不支持托管安装 frpc。",
|
|
602
|
+
frpComponentReady: "官方 frpc {version} 已校验。",
|
|
603
|
+
frpStep4Title: "4 · 验证并连接",
|
|
604
|
+
frpStep4Text: "插件只启动 DSH 的 HTTP vhost,并确认公网端点确实属于这台电脑。",
|
|
605
|
+
frpAppRequirement: "自定义远程域名需要 Android App 0.3.3 或更高版本;局域网、cpolar 和 Tailscale 仍兼容此前受支持的 App。",
|
|
606
|
+
frpSaveConnect: "保存并验证连接",
|
|
607
|
+
frpSavingConnecting: "正在保存私有配置并验证完整 FRP 链路…",
|
|
608
|
+
frpConfigurationReady: "服务器配置已保存,Token 只保存在插件私有目录。",
|
|
609
|
+
frpConfigurationMissing: "请填写完整信息、复制 VPS 模板,然后保存并连接。",
|
|
610
|
+
frpInputInvalid: "请检查 VPS 地址、端口、HTTPS 地址和 Token。",
|
|
611
|
+
frpComponentDetails: "FRP 来源与彻底清理",
|
|
612
|
+
frpComponentDetailsText: "固定版本客户端来自 fatedier/frp 官方 Release。DSH Mobile 不安装系统服务,也不接受任意 FRP 配置。",
|
|
613
|
+
frpOfficialRelease: "FRP 官方 Release",
|
|
614
|
+
purgeFrp: "彻底移除 FRP 组件与私有配置",
|
|
615
|
+
purgeFrpConfirm: "关闭 FRP 并移除 DSH Mobile 管理的 frpc、共享 Token、生成配置、临时文件和日志?不会修改 VPS。",
|
|
616
|
+
purgingFrp: "正在关闭 FRP 并清理 DSH Mobile 管理的文件…",
|
|
617
|
+
switchingFrp: "正在切换到自建 FRP…",
|
|
618
|
+
reconnectingFrp: "正在验证并重新连接自建 FRP…",
|
|
619
|
+
resetFrpConfirm: "关闭自建 FRP 并移除所有远程配对设备?已保存的 VPS 配置和服务器不会改变。",
|
|
620
|
+
remoteUnavailableFrp: "frpc 尚未安装或私有服务器配置不完整。",
|
|
621
|
+
remoteStartingFrp: "正在检查 VPS 并准备受限 FRP 通道…",
|
|
622
|
+
remoteConnectingFrp: "frpc 已启动,正在验证公网 HTTPS 端点…",
|
|
623
|
+
frpMissing: "请先安装官方 frpc。",
|
|
624
|
+
frpInvalid: "frpc 校验失败,请彻底清理后重新安装。",
|
|
625
|
+
frpConfigMissing: "请先保存自建 FRP 连接信息。",
|
|
626
|
+
frpConfigVerifyFailed: "frpc 拒绝了生成的配置,请检查服务器信息和 Token。",
|
|
627
|
+
frpVhostPublic: "VPS 明文 HTTP vhost 可被公网访问。请将其限制到 127.0.0.1,并由 Caddy 提供 HTTPS。",
|
|
628
|
+
frpVhostProbeFailed: "无法检查 VPS 地址,请确认 DNS 后重试。",
|
|
629
|
+
frpLaunchFailed: "frpc 未能启动。",
|
|
630
|
+
frpTimeout: "公网端点未能就绪,请检查 frps、Caddy、DNS 和防火墙。",
|
|
631
|
+
frpDiscoveryMismatch: "公开域名连接到了另一台 DSH 电脑,请检查 Caddy 与 frps 映射。",
|
|
632
|
+
frpDiscoveryInvalid: "公开域名没有返回有效的 DSH Mobile 发现信息。",
|
|
633
|
+
frpStopped: "FRP 连接已停止。",
|
|
634
|
+
frpExited: "frpc 意外退出,请检查 VPS 配置后重新连接。"
|
|
635
|
+
});
|
|
417
636
|
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
418
637
|
requestTimeout: "The operation timed out. Confirm that DSH is still running, then try again.",
|
|
419
638
|
diagnostics: "Diagnostics",
|
|
@@ -673,6 +892,38 @@ window.__ModuleLoader__.load({
|
|
|
673
892
|
}
|
|
674
893
|
};
|
|
675
894
|
//#endregion
|
|
895
|
+
//#region src/frp-template.ts
|
|
896
|
+
/** Loopback-only HTTP vhost port used between Caddy and frps. */
|
|
897
|
+
const FRP_VHOST_HTTP_PORT = 7080;
|
|
898
|
+
function publicDnsHostname(value) {
|
|
899
|
+
return value.length <= 253 && value.includes(".") && !/^[0-9.]+$/u.test(value) && !value.includes(":") && value.split(".").every((label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label));
|
|
900
|
+
}
|
|
901
|
+
/** Build the only supported frps and Caddy configuration from validated user inputs. */
|
|
902
|
+
function createRestrictedFrpServerTemplate(serverPort, token, publicOrigin) {
|
|
903
|
+
if (!Number.isSafeInteger(serverPort) || serverPort < 1 || serverPort > 65535 || token.length < 16 || token.length > 512 || /[\s\u0000-\u001f\u007f]/u.test(token)) throw new Error("frp_template_input_invalid");
|
|
904
|
+
let url;
|
|
905
|
+
try {
|
|
906
|
+
url = new URL(publicOrigin);
|
|
907
|
+
} catch {
|
|
908
|
+
throw new Error("frp_template_input_invalid");
|
|
909
|
+
}
|
|
910
|
+
if (url.protocol !== "https:" || url.port !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "" || url.username !== "" || url.password !== "" || !publicDnsHostname(url.hostname)) throw new Error("frp_template_input_invalid");
|
|
911
|
+
return [
|
|
912
|
+
"# frps.toml",
|
|
913
|
+
`bindPort = ${String(serverPort)}`,
|
|
914
|
+
"proxyBindAddr = \"127.0.0.1\"",
|
|
915
|
+
`vhostHTTPPort = ${String(FRP_VHOST_HTTP_PORT)}`,
|
|
916
|
+
"auth.method = \"token\"",
|
|
917
|
+
`auth.token = ${JSON.stringify(token)}`,
|
|
918
|
+
"",
|
|
919
|
+
"# Caddyfile",
|
|
920
|
+
`${url.hostname} {`,
|
|
921
|
+
` reverse_proxy 127.0.0.1:${String(FRP_VHOST_HTTP_PORT)}`,
|
|
922
|
+
"}",
|
|
923
|
+
""
|
|
924
|
+
].join("\n");
|
|
925
|
+
}
|
|
926
|
+
//#endregion
|
|
676
927
|
//#region src/native-mobile.ts
|
|
677
928
|
/** Mobile feature and compatibility rules applied to DSH React surfaces. */
|
|
678
929
|
const NATIVE_MOBILE_STYLES = `
|
|
@@ -706,7 +957,7 @@ window.__ModuleLoader__.load({
|
|
|
706
957
|
[data-dsh-mobile-header] [class*="_headerUtilities"] { gap:2px !important; }
|
|
707
958
|
[data-dsh-mobile-header] [class*="_sessionLogButton"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }
|
|
708
959
|
[data-dsh-mobile-header] [class*="_sessionLogButton"] > * { display:none !important; }
|
|
709
|
-
[data-dsh-mobile-header] [class*="_sessionLogButton"]::after { color:var(--dsw-
|
|
960
|
+
[data-dsh-mobile-header] [class*="_sessionLogButton"]::after { color:var(--dsw-alias-label-primary, #171a21); content:"Log"; font-size:11px; font-weight:600; }
|
|
710
961
|
html[data-dsh-mobile-language="zh"] [data-dsh-mobile-header] [class*="_sessionLogButton"]::after { content:"日志"; }
|
|
711
962
|
[data-dsh-mobile-header] [class*="_tabs"] { box-sizing:border-box !important; width:max-content !important; max-width:calc(100% - 58px) !important; min-height:28px !important; height:28px !important; margin-top:0 !important; padding-left:6px !important; padding-right:6px !important; overflow-x:auto; scrollbar-width:none; }
|
|
712
963
|
[data-dsh-mobile-header] [class*="_tab"] { padding-bottom:5px !important; }
|
|
@@ -845,6 +1096,31 @@ window.__ModuleLoader__.load({
|
|
|
845
1096
|
function firstByClassSuffix(root, suffix) {
|
|
846
1097
|
return Array.from(root.querySelectorAll("[class]")).find((element) => classToken(element, suffix));
|
|
847
1098
|
}
|
|
1099
|
+
/** Find the stock DSH application frame without mistaking a feature card for the shell. */
|
|
1100
|
+
function resolveNativeMobileFrame(root, dedicatedCenter) {
|
|
1101
|
+
if (dedicatedCenter !== void 0) return void 0;
|
|
1102
|
+
return Array.from(root.querySelectorAll("[class]")).find((candidate) => {
|
|
1103
|
+
return classToken(candidate, "_frame") && firstByClassSuffix(candidate, "_sidebarCol") !== void 0 && firstByClassSuffix(candidate, "_centerCol") !== void 0;
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
/** Mark every mounted settings dialog so its mobile layout does not depend on the conversation shell. */
|
|
1107
|
+
function markNativeMobileSettings(root) {
|
|
1108
|
+
let marked = 0;
|
|
1109
|
+
for (const dialog of root.querySelectorAll("[role=\"dialog\"]")) {
|
|
1110
|
+
const children = Array.from(dialog.children);
|
|
1111
|
+
const nav = children.find((child) => classToken(child, "_nav"));
|
|
1112
|
+
const content = children.find((child) => classToken(child, "_content"));
|
|
1113
|
+
if (nav === void 0 || content === void 0) continue;
|
|
1114
|
+
dialog.dataset.dshMobileSettings = "true";
|
|
1115
|
+
nav.dataset.dshMobileSettingsNav = "true";
|
|
1116
|
+
content.dataset.dshMobileSettingsContent = "true";
|
|
1117
|
+
firstByClassSuffix(nav, "_navList")?.setAttribute("data-dsh-mobile-settings-list", "true");
|
|
1118
|
+
firstByClassSuffix(content, "_header")?.setAttribute("data-dsh-mobile-settings-header", "true");
|
|
1119
|
+
firstByClassSuffix(content, "_options")?.setAttribute("data-dsh-mobile-settings-options", "true");
|
|
1120
|
+
marked += 1;
|
|
1121
|
+
}
|
|
1122
|
+
return marked;
|
|
1123
|
+
}
|
|
848
1124
|
const AUTO_HISTORY_THRESHOLD_PX = 64;
|
|
849
1125
|
function controlledFileDragEvent(type, files, initialDropEffect) {
|
|
850
1126
|
const event = new Event(type, {
|
|
@@ -1257,13 +1533,16 @@ window.__ModuleLoader__.load({
|
|
|
1257
1533
|
};
|
|
1258
1534
|
const sync = () => {
|
|
1259
1535
|
scheduled = 0;
|
|
1260
|
-
frame = firstByClassSuffix(document, "_frame");
|
|
1261
1536
|
const dedicatedCenter = document.querySelector(".dshm-main") ?? void 0;
|
|
1537
|
+
const nextFrame = resolveNativeMobileFrame(document, dedicatedCenter);
|
|
1538
|
+
if (frame !== nextFrame) frame?.removeAttribute("data-dsh-mobile-frame");
|
|
1539
|
+
frame = nextFrame;
|
|
1262
1540
|
if (frame !== void 0) frame.dataset.dshMobileFrame = "true";
|
|
1263
1541
|
sidebar = frame === void 0 ? document.querySelector(".dshm-drawer") ?? void 0 : firstByClassSuffix(frame, "_sidebarCol");
|
|
1264
1542
|
const center = frame === void 0 ? dedicatedCenter : firstByClassSuffix(frame, "_centerCol");
|
|
1265
1543
|
const details = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_detailsCol");
|
|
1266
1544
|
const handle = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_handle");
|
|
1545
|
+
markNativeMobileSettings(document);
|
|
1267
1546
|
if (center === void 0) {
|
|
1268
1547
|
bindHistoryScroller(void 0);
|
|
1269
1548
|
syncMediaBinding(null);
|
|
@@ -1344,23 +1623,6 @@ window.__ModuleLoader__.load({
|
|
|
1344
1623
|
const lastColumn = frame?.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
|
|
1345
1624
|
details.dataset.open = String(lastColumn !== void 0 && lastColumn !== "0px" && lastColumn !== "0");
|
|
1346
1625
|
}
|
|
1347
|
-
const settings = Array.from(document.querySelectorAll("[role=\"dialog\"]")).find((dialog) => {
|
|
1348
|
-
return Array.from(dialog.children).find((child) => child instanceof HTMLElement && classToken(child, "_nav")) !== void 0;
|
|
1349
|
-
});
|
|
1350
|
-
if (settings !== void 0) {
|
|
1351
|
-
settings.dataset.dshMobileSettings = "true";
|
|
1352
|
-
const nav = Array.from(settings.children).find((child) => child instanceof HTMLElement && classToken(child, "_nav"));
|
|
1353
|
-
const content = Array.from(settings.children).find((child) => child instanceof HTMLElement && classToken(child, "_content"));
|
|
1354
|
-
if (nav !== void 0) {
|
|
1355
|
-
nav.dataset.dshMobileSettingsNav = "true";
|
|
1356
|
-
firstByClassSuffix(nav, "_navList")?.setAttribute("data-dsh-mobile-settings-list", "true");
|
|
1357
|
-
}
|
|
1358
|
-
if (content !== void 0) {
|
|
1359
|
-
content.dataset.dshMobileSettingsContent = "true";
|
|
1360
|
-
firstByClassSuffix(content, "_header")?.setAttribute("data-dsh-mobile-settings-header", "true");
|
|
1361
|
-
firstByClassSuffix(content, "_options")?.setAttribute("data-dsh-mobile-settings-options", "true");
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
1626
|
if (sidebar === void 0) return;
|
|
1365
1627
|
sidebar.dataset.dshMobileSidebar = "true";
|
|
1366
1628
|
toggle = firstByClassSuffix(sidebar, "_toggle");
|
|
@@ -1552,6 +1814,24 @@ window.__ModuleLoader__.load({
|
|
|
1552
1814
|
}
|
|
1553
1815
|
const CONTROL_REQUEST_TIMEOUT_MS = 15e3;
|
|
1554
1816
|
const LONG_CONTROL_REQUEST_TIMEOUT_MS = 13e4;
|
|
1817
|
+
const GITHUB_RELEASES_URL = "https://github.com/saya-ch/dsh-mobile/releases";
|
|
1818
|
+
const CONTROL_PANEL_ID = "dsh-mobile-control-panel";
|
|
1819
|
+
function releaseVersion(value) {
|
|
1820
|
+
return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(value) ? value : void 0;
|
|
1821
|
+
}
|
|
1822
|
+
/** Reduce the loopback release response to trusted text and download metadata. */
|
|
1823
|
+
function clientReleaseInfo(data) {
|
|
1824
|
+
const latestVersion = releaseVersion(data.latestVersion);
|
|
1825
|
+
const androidVersion = releaseVersion(data.androidVersion);
|
|
1826
|
+
const expectedAndroidDownloadUrl = androidVersion === void 0 ? void 0 : `${GITHUB_RELEASES_URL}/download/v${androidVersion}/dsh-mobile-android-v${androidVersion}.apk`;
|
|
1827
|
+
const androidDownloadUrl = expectedAndroidDownloadUrl !== void 0 && data.androidDownloadUrl === expectedAndroidDownloadUrl ? expectedAndroidDownloadUrl : GITHUB_RELEASES_URL;
|
|
1828
|
+
return {
|
|
1829
|
+
updateAvailable: data.updateAvailable === true && latestVersion !== void 0,
|
|
1830
|
+
...latestVersion === void 0 ? {} : { latestVersion },
|
|
1831
|
+
...androidVersion === void 0 ? {} : { androidVersion },
|
|
1832
|
+
androidDownloadUrl
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1555
1835
|
async function requestJson(path, init, timeoutMs = CONTROL_REQUEST_TIMEOUT_MS) {
|
|
1556
1836
|
const controller = new AbortController();
|
|
1557
1837
|
const upstreamSignal = init?.signal;
|
|
@@ -1596,6 +1876,10 @@ window.__ModuleLoader__.load({
|
|
|
1596
1876
|
if (url.protocol !== "https:" || url.hostname !== "login.tailscale.com" || url.port !== "" || url.username !== "" || url.password !== "") return "";
|
|
1597
1877
|
return url.toString();
|
|
1598
1878
|
}
|
|
1879
|
+
/** Build the copy-only restricted VPS template without sending the token to the host API. */
|
|
1880
|
+
function createFrpServerTemplateForClipboard(serverPort, token, publicOrigin) {
|
|
1881
|
+
return createRestrictedFrpServerTemplate(serverPort, token, publicOrigin);
|
|
1882
|
+
}
|
|
1599
1883
|
function installControl() {
|
|
1600
1884
|
const locale = selectedMobileControlLocale();
|
|
1601
1885
|
const localeTag = locale === "it" ? "it-IT" : locale === "zh" ? "zh-CN" : "en-US";
|
|
@@ -1608,6 +1892,7 @@ window.__ModuleLoader__.load({
|
|
|
1608
1892
|
const root = element("div", "dsh-mobile-control");
|
|
1609
1893
|
root.lang = locale;
|
|
1610
1894
|
const panel = element("section", "dsh-mobile-control__panel");
|
|
1895
|
+
panel.id = CONTROL_PANEL_ID;
|
|
1611
1896
|
panel.hidden = true;
|
|
1612
1897
|
panel.lang = locale;
|
|
1613
1898
|
panel.setAttribute("aria-label", t("mobileAccess"));
|
|
@@ -1615,6 +1900,10 @@ window.__ModuleLoader__.load({
|
|
|
1615
1900
|
const title = element("h2");
|
|
1616
1901
|
title.textContent = t("mobileAccess");
|
|
1617
1902
|
const headerActions = element("div", "dsh-mobile-control__header-actions");
|
|
1903
|
+
const updatePlugin = element("button", "dsh-mobile-control__update-plugin");
|
|
1904
|
+
updatePlugin.type = "button";
|
|
1905
|
+
updatePlugin.textContent = t("updatePlugin");
|
|
1906
|
+
updatePlugin.hidden = true;
|
|
1618
1907
|
const diagnosticsEntry = element("button", "dsh-mobile-control__diagnostic-entry");
|
|
1619
1908
|
diagnosticsEntry.type = "button";
|
|
1620
1909
|
diagnosticsEntry.textContent = t("diagnostics");
|
|
@@ -1624,9 +1913,13 @@ window.__ModuleLoader__.load({
|
|
|
1624
1913
|
close.type = "button";
|
|
1625
1914
|
close.textContent = "×";
|
|
1626
1915
|
close.setAttribute("aria-label", t("collapseMobileAccess"));
|
|
1627
|
-
headerActions.append(diagnosticsEntry, close);
|
|
1916
|
+
headerActions.append(updatePlugin, diagnosticsEntry, close);
|
|
1917
|
+
const releaseNotice = element("p", "dsh-mobile-control__release-notice");
|
|
1918
|
+
releaseNotice.hidden = true;
|
|
1919
|
+
releaseNotice.setAttribute("role", "status");
|
|
1920
|
+
releaseNotice.setAttribute("aria-live", "polite");
|
|
1628
1921
|
const appDownload = element("a", "dsh-mobile-control__app-download");
|
|
1629
|
-
appDownload.href =
|
|
1922
|
+
appDownload.href = GITHUB_RELEASES_URL;
|
|
1630
1923
|
appDownload.target = "_blank";
|
|
1631
1924
|
appDownload.rel = "noopener noreferrer";
|
|
1632
1925
|
appDownload.textContent = t("downloadAndroid");
|
|
@@ -1733,7 +2026,6 @@ window.__ModuleLoader__.load({
|
|
|
1733
2026
|
cpolarChoiceTop.append(cpolarChoiceName, cpolarChoiceBadge);
|
|
1734
2027
|
cpolarChoice.append(cpolarChoiceTop, cpolarChoiceDescription);
|
|
1735
2028
|
providerChoices.append(cpolarChoice, tailscaleChoice);
|
|
1736
|
-
providerSection.append(providerHeading, providerInfo, providerChoices);
|
|
1737
2029
|
const cpolarSetup = element("section", "dsh-mobile-control__cpolar-setup");
|
|
1738
2030
|
cpolarSetup.hidden = true;
|
|
1739
2031
|
const cpolarSetupTitle = element("h3", "dsh-mobile-control__section-title");
|
|
@@ -1795,6 +2087,128 @@ window.__ModuleLoader__.load({
|
|
|
1795
2087
|
cpolarDetailsBody.append(cpolarDetailsText, cpolarStorage, cpolarOfficial, cpolarTerms, cpolarPurge);
|
|
1796
2088
|
cpolarDetails.append(cpolarDetailsSummary, cpolarDetailsBody);
|
|
1797
2089
|
cpolarSetup.append(cpolarSetupTitle, cpolarComponentStatus, cpolarInstall, cpolarAccount, cpolarDetails);
|
|
2090
|
+
const selfHosted = element("details", "dsh-mobile-control__self-hosted");
|
|
2091
|
+
const selfHostedSummary = element("summary", "dsh-mobile-control__self-hosted-summary");
|
|
2092
|
+
const selfHostedSummaryText = element("span");
|
|
2093
|
+
const selfHostedSummaryTitle = element("strong");
|
|
2094
|
+
selfHostedSummaryTitle.textContent = t("selfHostedConnections");
|
|
2095
|
+
const selfHostedSummaryDescription = element("span");
|
|
2096
|
+
selfHostedSummaryDescription.textContent = t("selfHostedDescription");
|
|
2097
|
+
const selfHostedBadge = element("span", "dsh-mobile-control__provider-badge is-frp");
|
|
2098
|
+
selfHostedBadge.textContent = t("advanced");
|
|
2099
|
+
selfHostedSummaryText.append(selfHostedSummaryTitle, selfHostedSummaryDescription);
|
|
2100
|
+
selfHostedSummary.append(selfHostedSummaryText, selfHostedBadge);
|
|
2101
|
+
const selfHostedBody = element("div", "dsh-mobile-control__self-hosted-body");
|
|
2102
|
+
const frpChoice = element("button", "dsh-mobile-control__provider is-frp");
|
|
2103
|
+
frpChoice.type = "button";
|
|
2104
|
+
frpChoice.setAttribute("aria-pressed", "false");
|
|
2105
|
+
const frpChoiceTop = element("span", "dsh-mobile-control__provider-top");
|
|
2106
|
+
const frpChoiceName = element("strong");
|
|
2107
|
+
frpChoiceName.textContent = t("frpName");
|
|
2108
|
+
const frpChoiceDescription = element("span", "dsh-mobile-control__provider-description");
|
|
2109
|
+
frpChoiceDescription.textContent = t("frpDescription");
|
|
2110
|
+
frpChoiceTop.append(frpChoiceName);
|
|
2111
|
+
frpChoice.append(frpChoiceTop, frpChoiceDescription);
|
|
2112
|
+
selfHostedBody.append(frpChoice);
|
|
2113
|
+
selfHosted.append(selfHostedSummary, selfHostedBody);
|
|
2114
|
+
providerSection.append(providerHeading, providerInfo, providerChoices, selfHosted);
|
|
2115
|
+
const frpSetup = element("section", "dsh-mobile-control__frp-setup");
|
|
2116
|
+
frpSetup.hidden = true;
|
|
2117
|
+
const frpSetupTitle = element("h3", "dsh-mobile-control__section-title");
|
|
2118
|
+
frpSetupTitle.textContent = t("prepareFrp");
|
|
2119
|
+
const frpStep1 = element("section", "dsh-mobile-control__frp-step");
|
|
2120
|
+
const frpStep1Title = element("strong");
|
|
2121
|
+
frpStep1Title.textContent = t("frpStep1Title");
|
|
2122
|
+
const frpStep1Text = element("p");
|
|
2123
|
+
frpStep1Text.textContent = t("frpStep1Text");
|
|
2124
|
+
const frpFields = element("div", "dsh-mobile-control__frp-fields");
|
|
2125
|
+
const frpServerLabel = element("label", "dsh-mobile-control__field");
|
|
2126
|
+
frpServerLabel.textContent = t("frpServerAddress");
|
|
2127
|
+
const frpServer = element("input");
|
|
2128
|
+
frpServer.type = "text";
|
|
2129
|
+
frpServer.autocomplete = "off";
|
|
2130
|
+
frpServer.spellcheck = false;
|
|
2131
|
+
frpServer.placeholder = t("frpServerAddressPlaceholder");
|
|
2132
|
+
const frpPortLabel = element("label", "dsh-mobile-control__field");
|
|
2133
|
+
frpPortLabel.textContent = t("frpServerPort");
|
|
2134
|
+
const frpPort = element("input");
|
|
2135
|
+
frpPort.type = "number";
|
|
2136
|
+
frpPort.inputMode = "numeric";
|
|
2137
|
+
frpPort.min = "1";
|
|
2138
|
+
frpPort.max = "65535";
|
|
2139
|
+
frpPort.value = "7000";
|
|
2140
|
+
const frpTokenLabel = element("label", "dsh-mobile-control__field");
|
|
2141
|
+
frpTokenLabel.textContent = t("frpToken");
|
|
2142
|
+
const frpToken = element("input");
|
|
2143
|
+
frpToken.type = "password";
|
|
2144
|
+
frpToken.autocomplete = "off";
|
|
2145
|
+
frpToken.spellcheck = false;
|
|
2146
|
+
frpToken.placeholder = t("frpTokenPlaceholder");
|
|
2147
|
+
const frpOriginLabel = element("label", "dsh-mobile-control__field");
|
|
2148
|
+
frpOriginLabel.textContent = t("frpPublicOrigin");
|
|
2149
|
+
const frpOrigin = element("input");
|
|
2150
|
+
frpOrigin.type = "url";
|
|
2151
|
+
frpOrigin.autocomplete = "off";
|
|
2152
|
+
frpOrigin.spellcheck = false;
|
|
2153
|
+
frpOrigin.placeholder = t("frpPublicOriginPlaceholder");
|
|
2154
|
+
frpServerLabel.append(frpServer);
|
|
2155
|
+
frpPortLabel.append(frpPort);
|
|
2156
|
+
frpTokenLabel.append(frpToken);
|
|
2157
|
+
frpOriginLabel.append(frpOrigin);
|
|
2158
|
+
frpFields.append(frpServerLabel, frpPortLabel, frpTokenLabel, frpOriginLabel);
|
|
2159
|
+
frpStep1.append(frpStep1Title, frpStep1Text, frpFields);
|
|
2160
|
+
const frpStep2 = element("section", "dsh-mobile-control__frp-step");
|
|
2161
|
+
const frpStep2Title = element("strong");
|
|
2162
|
+
frpStep2Title.textContent = t("frpStep2Title");
|
|
2163
|
+
const frpStep2Text = element("p");
|
|
2164
|
+
frpStep2Text.textContent = t("frpStep2Text");
|
|
2165
|
+
const frpCopyTemplate = element("button", "dsh-mobile-control__secondary dsh-mobile-control__frp-action");
|
|
2166
|
+
frpCopyTemplate.type = "button";
|
|
2167
|
+
frpCopyTemplate.textContent = t("copyServerTemplate");
|
|
2168
|
+
frpStep2.append(frpStep2Title, frpStep2Text, frpCopyTemplate);
|
|
2169
|
+
const frpStep3 = element("section", "dsh-mobile-control__frp-step");
|
|
2170
|
+
const frpStep3Title = element("strong");
|
|
2171
|
+
frpStep3Title.textContent = t("frpStep3Title");
|
|
2172
|
+
const frpStep3Text = element("p");
|
|
2173
|
+
frpStep3Text.textContent = t("frpStep3Text");
|
|
2174
|
+
const frpComponentStatus = element("p", "dsh-mobile-control__component-status");
|
|
2175
|
+
frpComponentStatus.textContent = t("checkingComponent");
|
|
2176
|
+
const frpInstall = element("button", "dsh-mobile-control__primary dsh-mobile-control__frp-action");
|
|
2177
|
+
frpInstall.type = "button";
|
|
2178
|
+
frpInstall.textContent = t("installFrpc");
|
|
2179
|
+
frpStep3.append(frpStep3Title, frpStep3Text, frpComponentStatus, frpInstall);
|
|
2180
|
+
const frpStep4 = element("section", "dsh-mobile-control__frp-step");
|
|
2181
|
+
const frpStep4Title = element("strong");
|
|
2182
|
+
frpStep4Title.textContent = t("frpStep4Title");
|
|
2183
|
+
const frpStep4Text = element("p");
|
|
2184
|
+
frpStep4Text.textContent = t("frpStep4Text");
|
|
2185
|
+
const frpAppRequirement = element("p", "dsh-mobile-control__frp-requirement");
|
|
2186
|
+
frpAppRequirement.textContent = t("frpAppRequirement");
|
|
2187
|
+
const frpConfigurationStatus = element("p", "dsh-mobile-control__component-status");
|
|
2188
|
+
frpConfigurationStatus.textContent = t("frpConfigurationMissing");
|
|
2189
|
+
const frpConfigure = element("button", "dsh-mobile-control__primary dsh-mobile-control__frp-action");
|
|
2190
|
+
frpConfigure.type = "button";
|
|
2191
|
+
frpConfigure.textContent = t("frpSaveConnect");
|
|
2192
|
+
frpStep4.append(frpStep4Title, frpStep4Text, frpAppRequirement, frpConfigurationStatus, frpConfigure);
|
|
2193
|
+
const frpDetails = element("details", "dsh-mobile-control__details");
|
|
2194
|
+
const frpDetailsSummary = element("summary");
|
|
2195
|
+
frpDetailsSummary.textContent = t("frpComponentDetails");
|
|
2196
|
+
const frpDetailsBody = element("div", "dsh-mobile-control__details-body");
|
|
2197
|
+
const frpDetailsText = element("p");
|
|
2198
|
+
frpDetailsText.textContent = t("frpComponentDetailsText");
|
|
2199
|
+
const frpStorage = element("code", "dsh-mobile-control__storage");
|
|
2200
|
+
frpStorage.textContent = t("pluginPrivateDirectory");
|
|
2201
|
+
const frpOfficial = element("a", "dsh-mobile-control__text-link");
|
|
2202
|
+
frpOfficial.href = "https://github.com/fatedier/frp/releases/tag/v0.70.1";
|
|
2203
|
+
frpOfficial.target = "_blank";
|
|
2204
|
+
frpOfficial.rel = "noopener noreferrer";
|
|
2205
|
+
frpOfficial.textContent = t("frpOfficialRelease");
|
|
2206
|
+
const frpPurge = element("button", "dsh-mobile-control__danger");
|
|
2207
|
+
frpPurge.type = "button";
|
|
2208
|
+
frpPurge.textContent = t("purgeFrp");
|
|
2209
|
+
frpDetailsBody.append(frpDetailsText, frpStorage, frpOfficial, frpPurge);
|
|
2210
|
+
frpDetails.append(frpDetailsSummary, frpDetailsBody);
|
|
2211
|
+
frpSetup.append(frpSetupTitle, frpStep1, frpStep2, frpStep3, frpStep4, frpDetails);
|
|
1798
2212
|
const tailscaleInfo = element("details", "dsh-mobile-control__details");
|
|
1799
2213
|
const tailscaleInfoSummary = element("summary");
|
|
1800
2214
|
tailscaleInfoSummary.textContent = t("tailscaleHelp");
|
|
@@ -1803,6 +2217,18 @@ window.__ModuleLoader__.load({
|
|
|
1803
2217
|
tailscaleInfoText.textContent = t("tailscaleHelpText");
|
|
1804
2218
|
tailscaleInfoBody.append(tailscaleInfoText);
|
|
1805
2219
|
tailscaleInfo.append(tailscaleInfoSummary, tailscaleInfoBody);
|
|
2220
|
+
const providerSetupHeader = element("div", "dsh-mobile-control__stage-header");
|
|
2221
|
+
const providerSetupHeading = element("h3", "dsh-mobile-control__section-title");
|
|
2222
|
+
providerSetupHeading.textContent = t("currentProvider");
|
|
2223
|
+
const providerSetupName = element("span", "dsh-mobile-control__stage-value");
|
|
2224
|
+
providerSetupName.textContent = "Tailscale Funnel";
|
|
2225
|
+
const remoteStateBadge = element("span", "dsh-mobile-control__state-badge");
|
|
2226
|
+
remoteStateBadge.textContent = t("remoteStateOff");
|
|
2227
|
+
const providerSetupMeta = element("div", "dsh-mobile-control__stage-meta");
|
|
2228
|
+
providerSetupMeta.append(providerSetupName, remoteStateBadge);
|
|
2229
|
+
providerSetupHeader.append(providerSetupHeading, providerSetupMeta);
|
|
2230
|
+
const providerSetupBody = element("div", "dsh-mobile-control__provider-setup-body");
|
|
2231
|
+
providerSetupBody.append(cpolarSetup, frpSetup, tailscaleInfo);
|
|
1806
2232
|
const remoteAccess = element("div", "dsh-mobile-control__access");
|
|
1807
2233
|
remoteAccess.hidden = true;
|
|
1808
2234
|
const remoteAccessLabel = element("span", "dsh-mobile-control__access-label");
|
|
@@ -1872,6 +2298,8 @@ window.__ModuleLoader__.load({
|
|
|
1872
2298
|
remoteManageRow.append(remoteDevices, remoteReset);
|
|
1873
2299
|
const remoteDevicePanel = element("div", "dsh-mobile-control__devices");
|
|
1874
2300
|
remoteDevicePanel.hidden = true;
|
|
2301
|
+
const remoteWorkspace = element("section", "dsh-mobile-control__remote-workspace");
|
|
2302
|
+
remoteWorkspace.append(providerSetupHeader, remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteActions, remoteQr, remoteManageRow, remoteDevicePanel);
|
|
1875
2303
|
const diagnosticsView = element("div", "dsh-mobile-control__view is-diagnostics");
|
|
1876
2304
|
diagnosticsView.hidden = true;
|
|
1877
2305
|
const diagnosticsIntro = element("p", "dsh-mobile-control__intro");
|
|
@@ -1916,9 +2344,9 @@ window.__ModuleLoader__.load({
|
|
|
1916
2344
|
header.append(title, headerActions);
|
|
1917
2345
|
actions.append(toggle, pair, linkPair);
|
|
1918
2346
|
lanView.append(access, qrBox, status, extensionStatus, actions, manageRow, devicePanel);
|
|
1919
|
-
remoteView.append(remoteIntro, providerSection,
|
|
2347
|
+
remoteView.append(remoteIntro, providerSection, remoteWorkspace);
|
|
1920
2348
|
diagnosticsView.append(diagnosticsIntro, diagnosticsSummary, diagnosticsToolbar, diagnosticsFeedback, diagnosticsChecks, diagnosticsDetails);
|
|
1921
|
-
panel.append(header, appDownload, switcher, lanView, remoteView, diagnosticsView);
|
|
2349
|
+
panel.append(header, releaseNotice, appDownload, switcher, lanView, remoteView, diagnosticsView);
|
|
1922
2350
|
root.append(panel);
|
|
1923
2351
|
document.body.append(root);
|
|
1924
2352
|
let running = false;
|
|
@@ -1934,11 +2362,32 @@ window.__ModuleLoader__.load({
|
|
|
1934
2362
|
let remoteProviderBusy = false;
|
|
1935
2363
|
let cpolarInstalled = false;
|
|
1936
2364
|
let cpolarConfigured = false;
|
|
2365
|
+
let frpInstalled = false;
|
|
2366
|
+
let frpConfigured = false;
|
|
2367
|
+
let frpDownloadSize = "14.0";
|
|
2368
|
+
let configuredFrpServer = "";
|
|
2369
|
+
let configuredFrpPort = 7e3;
|
|
2370
|
+
let configuredFrpOrigin = "";
|
|
1937
2371
|
let providerInfoPinned = false;
|
|
1938
2372
|
let providerInfoHovered = false;
|
|
1939
2373
|
let previousAccessView = "lan";
|
|
1940
2374
|
let diagnosticsBusy = false;
|
|
1941
2375
|
let copiedDiagnosticReport = "";
|
|
2376
|
+
let pluginUpdateAvailable = false;
|
|
2377
|
+
let pluginLatestVersion = "";
|
|
2378
|
+
const renderRelease = (data) => {
|
|
2379
|
+
const release = clientReleaseInfo(data);
|
|
2380
|
+
pluginUpdateAvailable = release.updateAvailable;
|
|
2381
|
+
pluginLatestVersion = release.latestVersion ?? "";
|
|
2382
|
+
updatePlugin.hidden = !pluginUpdateAvailable || !diagnosticsView.hidden;
|
|
2383
|
+
updatePlugin.textContent = t("updatePlugin");
|
|
2384
|
+
if (pluginLatestVersion !== "") updatePlugin.setAttribute("aria-label", t("updatePluginAria", { version: pluginLatestVersion }));
|
|
2385
|
+
if (release.androidVersion !== void 0) {
|
|
2386
|
+
appDownload.textContent = t("downloadAndroidVersion", { version: release.androidVersion });
|
|
2387
|
+
appDownload.setAttribute("aria-label", t("downloadAndroidVersionAria", { version: release.androidVersion }));
|
|
2388
|
+
}
|
|
2389
|
+
appDownload.href = release.androidDownloadUrl;
|
|
2390
|
+
};
|
|
1942
2391
|
const syncProviderInfo = () => {
|
|
1943
2392
|
const open = providerInfoPinned || providerInfoHovered || providerInfo.contains(document.activeElement);
|
|
1944
2393
|
providerInfoPopover.hidden = !open;
|
|
@@ -1979,6 +2428,7 @@ window.__ModuleLoader__.load({
|
|
|
1979
2428
|
diagnosticsEntry.setAttribute("aria-pressed", String(view === "diagnostics"));
|
|
1980
2429
|
diagnosticsEntry.textContent = view === "diagnostics" ? t("back") : t("diagnostics");
|
|
1981
2430
|
diagnosticsEntry.setAttribute("aria-label", view === "diagnostics" ? t("backToMobile") : t("openDiagnostics"));
|
|
2431
|
+
updatePlugin.hidden = view === "diagnostics" || !pluginUpdateAvailable;
|
|
1982
2432
|
appDownload.hidden = view === "diagnostics";
|
|
1983
2433
|
switcher.hidden = view === "diagnostics";
|
|
1984
2434
|
title.textContent = view === "lan" ? t("lanAccess") : view === "remote" ? t("remoteAccess") : t("connectionDiagnostics");
|
|
@@ -2130,17 +2580,25 @@ window.__ModuleLoader__.load({
|
|
|
2130
2580
|
});
|
|
2131
2581
|
const renderRemote = (data) => {
|
|
2132
2582
|
remoteRunning = data.running === true;
|
|
2133
|
-
remoteProvider = data.provider === "cpolar" ? "cpolar" : "tailscale";
|
|
2583
|
+
remoteProvider = data.provider === "cpolar" ? "cpolar" : data.provider === "frp" ? "frp" : "tailscale";
|
|
2134
2584
|
const cpolar = remoteProvider === "cpolar";
|
|
2135
|
-
|
|
2585
|
+
const frp = remoteProvider === "frp";
|
|
2586
|
+
const tailscale = remoteProvider === "tailscale";
|
|
2587
|
+
tailscaleChoice.classList.toggle("is-selected", tailscale);
|
|
2136
2588
|
cpolarChoice.classList.toggle("is-selected", cpolar);
|
|
2137
|
-
|
|
2589
|
+
frpChoice.classList.toggle("is-selected", frp);
|
|
2590
|
+
tailscaleChoice.setAttribute("aria-checked", String(tailscale));
|
|
2138
2591
|
cpolarChoice.setAttribute("aria-checked", String(cpolar));
|
|
2592
|
+
frpChoice.setAttribute("aria-pressed", String(frp));
|
|
2593
|
+
providerSetupName.textContent = cpolar ? "cpolar" : frp ? t("frpName") : "Tailscale Funnel";
|
|
2139
2594
|
tailscaleChoice.disabled = remoteProviderBusy;
|
|
2140
2595
|
cpolarChoice.disabled = remoteProviderBusy;
|
|
2596
|
+
frpChoice.disabled = remoteProviderBusy;
|
|
2141
2597
|
cpolarSetup.hidden = !cpolar;
|
|
2142
|
-
|
|
2143
|
-
|
|
2598
|
+
frpSetup.hidden = !frp;
|
|
2599
|
+
if (frp) selfHosted.open = true;
|
|
2600
|
+
tailscaleInfo.hidden = !tailscale;
|
|
2601
|
+
remoteReset.textContent = tailscale ? t("resetRemoteLogin") : t("resetRemoteDevices");
|
|
2144
2602
|
const providers = data.providers !== null && typeof data.providers === "object" ? data.providers : {};
|
|
2145
2603
|
const cpolarProvider = providers.cpolar !== null && typeof providers.cpolar === "object" ? providers.cpolar : {};
|
|
2146
2604
|
const component = cpolarProvider.component !== null && typeof cpolarProvider.component === "object" ? cpolarProvider.component : {};
|
|
@@ -2160,11 +2618,37 @@ window.__ModuleLoader__.load({
|
|
|
2160
2618
|
cpolarConfigure.disabled = remoteProviderBusy;
|
|
2161
2619
|
cpolarPurge.hidden = !cpolarInstalled && !cpolarConfigured;
|
|
2162
2620
|
cpolarComponentStatus.textContent = !cpolarSupported ? t("cpolarUnsupported") : !cpolarInstalled ? t("cpolarNotInstalled") : !cpolarConfigured ? t("cpolarNeedsToken", { version: componentVersion }) : t("cpolarReady", { version: componentVersion });
|
|
2621
|
+
const frpProvider = providers.frp !== null && typeof providers.frp === "object" ? providers.frp : {};
|
|
2622
|
+
const frpComponent = frpProvider.component !== null && typeof frpProvider.component === "object" ? frpProvider.component : {};
|
|
2623
|
+
const frpConfiguration = frpProvider.configuration !== null && typeof frpProvider.configuration === "object" ? frpProvider.configuration : {};
|
|
2624
|
+
frpInstalled = frpComponent.installed === true;
|
|
2625
|
+
frpConfigured = frpConfiguration.configured === true;
|
|
2626
|
+
const frpSupported = frpComponent.supported !== false;
|
|
2627
|
+
const frpVersion = typeof frpComponent.version === "string" ? frpComponent.version : "";
|
|
2628
|
+
const frpDownloadBytes = typeof frpComponent.downloadBytes === "number" ? frpComponent.downloadBytes : 0;
|
|
2629
|
+
if (frpDownloadBytes > 0) frpDownloadSize = formatMegabytes(frpDownloadBytes);
|
|
2630
|
+
const frpStoragePath = typeof frpComponent.storagePath === "string" ? frpComponent.storagePath : `DSH Mobile ${t("pluginPrivateDirectory")}`;
|
|
2631
|
+
configuredFrpServer = typeof frpConfiguration.serverAddress === "string" ? frpConfiguration.serverAddress : "";
|
|
2632
|
+
configuredFrpPort = typeof frpConfiguration.serverPort === "number" ? frpConfiguration.serverPort : 7e3;
|
|
2633
|
+
configuredFrpOrigin = typeof frpConfiguration.publicOrigin === "string" ? frpConfiguration.publicOrigin : "";
|
|
2634
|
+
if (frpServer.value === "" && configuredFrpServer !== "") frpServer.value = configuredFrpServer;
|
|
2635
|
+
if ((frpPort.value === "" || frpPort.value === "7000") && configuredFrpPort !== 7e3) frpPort.value = String(configuredFrpPort);
|
|
2636
|
+
if (frpOrigin.value === "" && configuredFrpOrigin !== "") frpOrigin.value = configuredFrpOrigin;
|
|
2637
|
+
frpStorage.textContent = frpStoragePath;
|
|
2638
|
+
frpStorage.title = frpStoragePath;
|
|
2639
|
+
frpInstall.hidden = frpInstalled || !frpSupported;
|
|
2640
|
+
frpInstall.textContent = frpDownloadBytes > 0 ? t("installWithSize", { size: formatMegabytes(frpDownloadBytes) }) : t("installFrpc");
|
|
2641
|
+
frpInstall.disabled = remoteProviderBusy;
|
|
2642
|
+
frpConfigure.disabled = remoteProviderBusy || !frpInstalled;
|
|
2643
|
+
frpPurge.hidden = !frpInstalled && !frpConfigured;
|
|
2644
|
+
frpComponentStatus.textContent = !frpSupported ? t("frpUnsupported") : frpInstalled ? t("frpComponentReady", { version: frpVersion }) : t("frpNotInstalled");
|
|
2645
|
+
frpConfigurationStatus.textContent = frpConfigured ? t("frpConfigurationReady") : t("frpConfigurationMissing");
|
|
2646
|
+
selfHostedBadge.textContent = frpConfigured && frpInstalled ? t("ready") : t("advanced");
|
|
2163
2647
|
const state = typeof data.state === "string" ? data.state : "error";
|
|
2164
2648
|
const errorCode = typeof data.errorCode === "string" ? data.errorCode : "";
|
|
2165
2649
|
const remoteOrigin = typeof data.origin === "string" ? data.origin : "";
|
|
2166
2650
|
remoteLoginUrl = typeof data.loginUrl === "string" ? data.loginUrl : "";
|
|
2167
|
-
const candidateSetupUrl =
|
|
2651
|
+
const candidateSetupUrl = tailscale ? officialFunnelSetupUrl(data.setupUrl) : "";
|
|
2168
2652
|
remoteSetupUrl = candidateSetupUrl !== "" ? candidateSetupUrl : {
|
|
2169
2653
|
funnel_permission_required: "https://tailscale.com/s/no-funnel",
|
|
2170
2654
|
funnel_https_required: "https://tailscale.com/s/https",
|
|
@@ -2172,6 +2656,10 @@ window.__ModuleLoader__.load({
|
|
|
2172
2656
|
}[errorCode] ?? "";
|
|
2173
2657
|
const needsFunnelSetup = state === "error" && remoteSetupUrl !== "";
|
|
2174
2658
|
remoteReady = remoteRunning && state === "ready" && remoteOrigin !== "";
|
|
2659
|
+
remoteStateBadge.classList.toggle("is-ready", remoteReady);
|
|
2660
|
+
remoteStateBadge.classList.toggle("is-busy", state === "starting" || state === "connecting" || state === "needs-login");
|
|
2661
|
+
remoteStateBadge.classList.toggle("is-attention", state === "error" || state === "unavailable");
|
|
2662
|
+
remoteStateBadge.textContent = remoteReady ? t("ready") : state === "starting" || state === "connecting" || state === "needs-login" ? t("remoteStateConnecting") : state === "error" || state === "unavailable" ? t("remoteStateAttention") : t("remoteStateOff");
|
|
2175
2663
|
remoteAccess.hidden = !remoteReady;
|
|
2176
2664
|
remoteAccessLink.href = remoteOrigin;
|
|
2177
2665
|
remoteAccessLink.textContent = remoteOrigin;
|
|
@@ -2179,10 +2667,10 @@ window.__ModuleLoader__.load({
|
|
|
2179
2667
|
remoteStatus.classList.toggle("is-running", remoteReady);
|
|
2180
2668
|
const labels = {
|
|
2181
2669
|
off: t("remoteOff"),
|
|
2182
|
-
unavailable: cpolar ? t("remoteUnavailableCpolar") : t("remoteUnavailableTailscale"),
|
|
2183
|
-
starting: cpolar ? t("remoteStartingCpolar") : t("remoteStartingTailscale"),
|
|
2670
|
+
unavailable: cpolar ? t("remoteUnavailableCpolar") : frp ? t("remoteUnavailableFrp") : t("remoteUnavailableTailscale"),
|
|
2671
|
+
starting: cpolar ? t("remoteStartingCpolar") : frp ? t("remoteStartingFrp") : t("remoteStartingTailscale"),
|
|
2184
2672
|
"needs-login": t("remoteNeedsLogin"),
|
|
2185
|
-
connecting: cpolar ? t("remoteConnectingCpolar") : t("remoteConnectingTailscale"),
|
|
2673
|
+
connecting: cpolar ? t("remoteConnectingCpolar") : frp ? t("remoteConnectingFrp") : t("remoteConnectingTailscale"),
|
|
2186
2674
|
ready: t("remoteReady"),
|
|
2187
2675
|
error: t("remoteError")
|
|
2188
2676
|
};
|
|
@@ -2204,17 +2692,30 @@ window.__ModuleLoader__.load({
|
|
|
2204
2692
|
cpolar_stopped: t("cpolarStopped"),
|
|
2205
2693
|
cpolar_exited: t("cpolarExited"),
|
|
2206
2694
|
cpolar_invalid_output: t("cpolarOutputInvalid"),
|
|
2207
|
-
cpolar_invalid_origin: t("cpolarOriginInvalid")
|
|
2695
|
+
cpolar_invalid_origin: t("cpolarOriginInvalid"),
|
|
2696
|
+
frp_component_missing: t("frpMissing"),
|
|
2697
|
+
frp_component_invalid: t("frpInvalid"),
|
|
2698
|
+
frp_config_missing: t("frpConfigMissing"),
|
|
2699
|
+
frp_config_verify_failed: t("frpConfigVerifyFailed"),
|
|
2700
|
+
frp_vhost_publicly_reachable: t("frpVhostPublic"),
|
|
2701
|
+
frp_vhost_probe_failed: t("frpVhostProbeFailed"),
|
|
2702
|
+
frp_launch_failed: t("frpLaunchFailed"),
|
|
2703
|
+
frp_start_timeout: t("frpTimeout"),
|
|
2704
|
+
frp_discovery_mismatch: t("frpDiscoveryMismatch"),
|
|
2705
|
+
frp_discovery_invalid: t("frpDiscoveryInvalid"),
|
|
2706
|
+
frp_stopped: t("frpStopped"),
|
|
2707
|
+
frp_exited: t("frpExited")
|
|
2208
2708
|
};
|
|
2209
2709
|
remoteStatus.textContent = remoteSetupPending && needsFunnelSetup ? t("setupOpened") : state === "error" ? errorLabels[errorCode] ?? labels.error : labels[state] ?? labels.error;
|
|
2210
2710
|
remoteGuide.hidden = !needsFunnelSetup;
|
|
2211
2711
|
remoteSetup.disabled = remoteSetupUrl === "" || remoteReconnectBusy;
|
|
2212
2712
|
remoteSetupRetry.disabled = remoteReconnectBusy;
|
|
2213
2713
|
remoteToggle.textContent = remoteRunning ? t("disableRemote") : t("enableRemote");
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2714
|
+
const providerPrepared = cpolar ? cpolarInstalled && cpolarConfigured : frp ? frpInstalled && frpConfigured : true;
|
|
2715
|
+
remoteToggle.disabled = remoteProviderBusy || !providerPrepared;
|
|
2716
|
+
remoteLogin.hidden = !tailscale || state !== "needs-login" || remoteLoginUrl === "";
|
|
2717
|
+
remoteReconnect.hidden = needsFunnelSetup || state !== "error" && state !== "unavailable" || !providerPrepared;
|
|
2718
|
+
remoteActions.hidden = !providerPrepared;
|
|
2218
2719
|
remotePair.disabled = !remoteReady;
|
|
2219
2720
|
remoteDevices.disabled = !remoteReady;
|
|
2220
2721
|
if (!remoteReady) remoteQr.hidden = true;
|
|
@@ -2236,7 +2737,8 @@ window.__ModuleLoader__.load({
|
|
|
2236
2737
|
remoteProviderBusy = true;
|
|
2237
2738
|
tailscaleChoice.disabled = true;
|
|
2238
2739
|
cpolarChoice.disabled = true;
|
|
2239
|
-
|
|
2740
|
+
frpChoice.disabled = true;
|
|
2741
|
+
remoteStatus.textContent = provider === "cpolar" ? t("switchingCpolar") : provider === "frp" ? t("switchingFrp") : t("switchingTailscale");
|
|
2240
2742
|
controlRequestJson("/api/mobile-access/remote/provider", {
|
|
2241
2743
|
method: "POST",
|
|
2242
2744
|
body: JSON.stringify({ provider })
|
|
@@ -2253,6 +2755,9 @@ window.__ModuleLoader__.load({
|
|
|
2253
2755
|
cpolarChoice.addEventListener("click", () => {
|
|
2254
2756
|
chooseRemoteProvider("cpolar");
|
|
2255
2757
|
});
|
|
2758
|
+
frpChoice.addEventListener("click", () => {
|
|
2759
|
+
chooseRemoteProvider("frp");
|
|
2760
|
+
});
|
|
2256
2761
|
cpolarInstall.addEventListener("click", () => {
|
|
2257
2762
|
if (remoteProviderBusy) return;
|
|
2258
2763
|
if (!window.confirm(t("installConfirm"))) return;
|
|
@@ -2318,6 +2823,98 @@ window.__ModuleLoader__.load({
|
|
|
2318
2823
|
loadRemote();
|
|
2319
2824
|
});
|
|
2320
2825
|
});
|
|
2826
|
+
const validFrpServer = (value) => value === value.trim() && value.length > 0 && value.length <= 253 && !/[\s\u0000-\u001f\u007f/\\@?#]/u.test(value);
|
|
2827
|
+
const frpForm = () => ({
|
|
2828
|
+
serverAddress: frpServer.value.trim(),
|
|
2829
|
+
serverPort: Number(frpPort.value),
|
|
2830
|
+
token: frpToken.value,
|
|
2831
|
+
publicOrigin: frpOrigin.value.trim()
|
|
2832
|
+
});
|
|
2833
|
+
const validFrpForm = (form) => {
|
|
2834
|
+
if (!validFrpServer(form.serverAddress)) return false;
|
|
2835
|
+
try {
|
|
2836
|
+
createFrpServerTemplateForClipboard(form.serverPort, form.token, form.publicOrigin);
|
|
2837
|
+
return true;
|
|
2838
|
+
} catch {
|
|
2839
|
+
return false;
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
frpCopyTemplate.addEventListener("click", () => {
|
|
2843
|
+
const form = frpForm();
|
|
2844
|
+
if (!validFrpForm(form)) {
|
|
2845
|
+
remoteStatus.textContent = t("frpInputInvalid");
|
|
2846
|
+
return;
|
|
2847
|
+
}
|
|
2848
|
+
navigator.clipboard.writeText(createFrpServerTemplateForClipboard(form.serverPort, form.token, form.publicOrigin)).then(() => {
|
|
2849
|
+
remoteStatus.textContent = t("templateCopied");
|
|
2850
|
+
}, () => {
|
|
2851
|
+
remoteStatus.textContent = t("templateCopyFailed");
|
|
2852
|
+
});
|
|
2853
|
+
});
|
|
2854
|
+
frpInstall.addEventListener("click", () => {
|
|
2855
|
+
if (remoteProviderBusy) return;
|
|
2856
|
+
if (!window.confirm(t("frpInstallConfirm", { size: frpDownloadSize }))) return;
|
|
2857
|
+
remoteProviderBusy = true;
|
|
2858
|
+
frpInstall.disabled = true;
|
|
2859
|
+
frpInstall.textContent = t("downloading");
|
|
2860
|
+
remoteStatus.textContent = t("installingFrp");
|
|
2861
|
+
controlRequestJson("/api/mobile-access/remote/frp/component/install", {
|
|
2862
|
+
method: "POST",
|
|
2863
|
+
body: JSON.stringify({ confirm: true })
|
|
2864
|
+
}, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(renderRemote, (error) => {
|
|
2865
|
+
remoteStatus.textContent = t("installFailed", { error: String(error) });
|
|
2866
|
+
}).finally(() => {
|
|
2867
|
+
remoteProviderBusy = false;
|
|
2868
|
+
loadRemote();
|
|
2869
|
+
});
|
|
2870
|
+
});
|
|
2871
|
+
frpConfigure.addEventListener("click", () => {
|
|
2872
|
+
if (remoteProviderBusy || !frpInstalled) return;
|
|
2873
|
+
const form = frpForm();
|
|
2874
|
+
const unchanged = frpConfigured && form.token === "" && form.serverAddress === configuredFrpServer && form.serverPort === configuredFrpPort && form.publicOrigin === configuredFrpOrigin;
|
|
2875
|
+
if (!unchanged && !validFrpForm(form)) {
|
|
2876
|
+
remoteStatus.textContent = t("frpInputInvalid");
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
remoteProviderBusy = true;
|
|
2880
|
+
frpConfigure.disabled = true;
|
|
2881
|
+
frpConfigure.setAttribute("aria-busy", "true");
|
|
2882
|
+
frpConfigure.textContent = t("saving");
|
|
2883
|
+
(unchanged ? Promise.resolve({}) : controlRequestJson("/api/mobile-access/remote/frp/configure", {
|
|
2884
|
+
method: "POST",
|
|
2885
|
+
body: JSON.stringify(form)
|
|
2886
|
+
})).then(() => {
|
|
2887
|
+
frpToken.value = "";
|
|
2888
|
+
remoteStatus.textContent = t("frpSavingConnecting");
|
|
2889
|
+
return controlRequestJson("/api/mobile-access/remote/control", {
|
|
2890
|
+
method: "POST",
|
|
2891
|
+
body: JSON.stringify({ running: true })
|
|
2892
|
+
});
|
|
2893
|
+
}).then(renderRemote, (error) => {
|
|
2894
|
+
remoteStatus.textContent = t("configureFailed", { error: String(error) });
|
|
2895
|
+
}).finally(() => {
|
|
2896
|
+
remoteProviderBusy = false;
|
|
2897
|
+
frpConfigure.setAttribute("aria-busy", "false");
|
|
2898
|
+
frpConfigure.textContent = t("frpSaveConnect");
|
|
2899
|
+
loadRemote();
|
|
2900
|
+
});
|
|
2901
|
+
});
|
|
2902
|
+
frpPurge.addEventListener("click", () => {
|
|
2903
|
+
if (remoteProviderBusy || !window.confirm(t("purgeFrpConfirm"))) return;
|
|
2904
|
+
remoteProviderBusy = true;
|
|
2905
|
+
frpPurge.disabled = true;
|
|
2906
|
+
remoteStatus.textContent = t("purgingFrp");
|
|
2907
|
+
controlRequestJson("/api/mobile-access/remote/frp/component/purge", {
|
|
2908
|
+
method: "POST",
|
|
2909
|
+
body: JSON.stringify({ confirm: true })
|
|
2910
|
+
}, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(renderRemote, (error) => {
|
|
2911
|
+
remoteStatus.textContent = t("purgeFailed", { error: String(error) });
|
|
2912
|
+
}).finally(() => {
|
|
2913
|
+
remoteProviderBusy = false;
|
|
2914
|
+
frpPurge.disabled = false;
|
|
2915
|
+
loadRemote();
|
|
2916
|
+
});
|
|
2917
|
+
});
|
|
2321
2918
|
remoteToggle.addEventListener("click", () => {
|
|
2322
2919
|
remoteToggle.disabled = true;
|
|
2323
2920
|
controlRequestJson("/api/mobile-access/remote/control", {
|
|
@@ -2336,7 +2933,7 @@ window.__ModuleLoader__.load({
|
|
|
2336
2933
|
remoteReconnect.disabled = true;
|
|
2337
2934
|
remoteSetup.disabled = true;
|
|
2338
2935
|
remoteSetupRetry.disabled = true;
|
|
2339
|
-
remoteStatus.textContent = remoteProvider === "cpolar" ? t("reconnectingCpolar") : t("reconnectingTailscale");
|
|
2936
|
+
remoteStatus.textContent = remoteProvider === "cpolar" ? t("reconnectingCpolar") : remoteProvider === "frp" ? t("reconnectingFrp") : t("reconnectingTailscale");
|
|
2340
2937
|
controlRequestJson("/api/mobile-access/remote/reconnect", {
|
|
2341
2938
|
method: "POST",
|
|
2342
2939
|
body: "{}"
|
|
@@ -2428,7 +3025,7 @@ window.__ModuleLoader__.load({
|
|
|
2428
3025
|
if (show) loadRemoteDevices();
|
|
2429
3026
|
});
|
|
2430
3027
|
remoteReset.addEventListener("click", () => {
|
|
2431
|
-
const prompt = remoteProvider === "cpolar" ? t("resetCpolarConfirm") : t("resetTailscaleConfirm");
|
|
3028
|
+
const prompt = remoteProvider === "cpolar" ? t("resetCpolarConfirm") : remoteProvider === "frp" ? t("resetFrpConfirm") : t("resetTailscaleConfirm");
|
|
2432
3029
|
if (!window.confirm(prompt)) return;
|
|
2433
3030
|
controlRequestJson("/api/mobile-access/remote/reset", {
|
|
2434
3031
|
method: "POST",
|
|
@@ -2466,7 +3063,7 @@ window.__ModuleLoader__.load({
|
|
|
2466
3063
|
if (templates === void 0) return serverCopy;
|
|
2467
3064
|
const facts = entry.facts !== null && typeof entry.facts === "object" ? entry.facts : {};
|
|
2468
3065
|
const values = {
|
|
2469
|
-
provider: facts.provider === "tailscale" || facts.provider === "cpolar" ? facts.provider : "",
|
|
3066
|
+
provider: facts.provider === "tailscale" || facts.provider === "cpolar" || facts.provider === "frp" ? facts.provider : "",
|
|
2470
3067
|
latencyMs: typeof facts.latencyMs === "number" && Number.isFinite(facts.latencyMs) ? new Intl.NumberFormat(localeTag).format(facts.latencyMs) : "",
|
|
2471
3068
|
interfaceName: typeof facts.interfaceName === "string" ? facts.interfaceName : "",
|
|
2472
3069
|
endpointSuffix: typeof facts.endpointSuffix === "string" ? facts.endpointSuffix : "",
|
|
@@ -2502,6 +3099,18 @@ window.__ModuleLoader__.load({
|
|
|
2502
3099
|
cpolar_start_timeout: "cpolarTimeout",
|
|
2503
3100
|
cpolar_stopped: "cpolarStopped",
|
|
2504
3101
|
cpolar_exited: "cpolarExited",
|
|
3102
|
+
frp_component_missing: "frpMissing",
|
|
3103
|
+
frp_component_invalid: "frpInvalid",
|
|
3104
|
+
frp_config_missing: "frpConfigMissing",
|
|
3105
|
+
frp_config_verify_failed: "frpConfigVerifyFailed",
|
|
3106
|
+
frp_vhost_publicly_reachable: "frpVhostPublic",
|
|
3107
|
+
frp_vhost_probe_failed: "frpVhostProbeFailed",
|
|
3108
|
+
frp_launch_failed: "frpLaunchFailed",
|
|
3109
|
+
frp_start_timeout: "frpTimeout",
|
|
3110
|
+
frp_discovery_mismatch: "frpDiscoveryMismatch",
|
|
3111
|
+
frp_discovery_invalid: "frpDiscoveryInvalid",
|
|
3112
|
+
frp_stopped: "frpStopped",
|
|
3113
|
+
frp_exited: "frpExited",
|
|
2505
3114
|
gateway_start_failed: "gatewayStartFailed"
|
|
2506
3115
|
}[values.controllerCode ?? ""];
|
|
2507
3116
|
if (actionKey !== void 0) action = t(actionKey);
|
|
@@ -2629,6 +3238,30 @@ window.__ModuleLoader__.load({
|
|
|
2629
3238
|
loadDiagnostics();
|
|
2630
3239
|
});
|
|
2631
3240
|
diagnosticsRun.addEventListener("click", loadDiagnostics);
|
|
3241
|
+
updatePlugin.addEventListener("click", () => {
|
|
3242
|
+
if (!pluginUpdateAvailable || pluginLatestVersion === "") return;
|
|
3243
|
+
updatePlugin.disabled = true;
|
|
3244
|
+
updatePlugin.textContent = t("updatingPlugin");
|
|
3245
|
+
releaseNotice.hidden = true;
|
|
3246
|
+
releaseNotice.classList.remove("is-error");
|
|
3247
|
+
controlRequestJson("/api/mobile-access/release/update", {
|
|
3248
|
+
method: "POST",
|
|
3249
|
+
body: "{}"
|
|
3250
|
+
}, LONG_CONTROL_REQUEST_TIMEOUT_MS).then((data) => {
|
|
3251
|
+
const installedVersion = releaseVersion(data.installedVersion) ?? pluginLatestVersion;
|
|
3252
|
+
pluginUpdateAvailable = false;
|
|
3253
|
+
updatePlugin.hidden = true;
|
|
3254
|
+
releaseNotice.textContent = t("pluginUpdatedRestart", { version: installedVersion });
|
|
3255
|
+
releaseNotice.hidden = false;
|
|
3256
|
+
}, (error) => {
|
|
3257
|
+
updatePlugin.textContent = t("updatePlugin");
|
|
3258
|
+
releaseNotice.textContent = t("pluginUpdateFailed", { error: String(error) });
|
|
3259
|
+
releaseNotice.classList.add("is-error");
|
|
3260
|
+
releaseNotice.hidden = false;
|
|
3261
|
+
}).finally(() => {
|
|
3262
|
+
updatePlugin.disabled = false;
|
|
3263
|
+
});
|
|
3264
|
+
});
|
|
2632
3265
|
diagnosticsCopy.addEventListener("click", () => {
|
|
2633
3266
|
if (copiedDiagnosticReport === "") return;
|
|
2634
3267
|
navigator.clipboard.writeText(copiedDiagnosticReport).then(() => {
|
|
@@ -2661,6 +3294,7 @@ window.__ModuleLoader__.load({
|
|
|
2661
3294
|
if (!panel.contains(event.target) && !document.querySelector(".dsh-mobile-control__trigger")?.contains(event.target)) setOpen(false);
|
|
2662
3295
|
};
|
|
2663
3296
|
document.addEventListener("pointerdown", dismiss);
|
|
3297
|
+
controlRequestJson("/api/mobile-access/release").then(renderRelease, () => {});
|
|
2664
3298
|
controlRequestJson("/api/mobile-access/lan/control").then(render, (error) => {
|
|
2665
3299
|
status.textContent = t("requestFailed", { error: String(error) });
|
|
2666
3300
|
});
|
|
@@ -2679,7 +3313,8 @@ window.__ModuleLoader__.load({
|
|
|
2679
3313
|
},
|
|
2680
3314
|
toggle: () => {
|
|
2681
3315
|
setOpen(panel.hidden !== false);
|
|
2682
|
-
}
|
|
3316
|
+
},
|
|
3317
|
+
isOpen: () => !panel.hidden
|
|
2683
3318
|
};
|
|
2684
3319
|
}
|
|
2685
3320
|
function mobileRequest(path, init = {}) {
|
|
@@ -3844,17 +4479,20 @@ window.__ModuleLoader__.load({
|
|
|
3844
4479
|
return false;
|
|
3845
4480
|
}
|
|
3846
4481
|
}
|
|
4482
|
+
/** Theme-aware desktop Mobile Access panel styles. */
|
|
3847
4483
|
const CONTROL_STYLES = `
|
|
3848
4484
|
.dsh-mobile-control{position:fixed;z-index:1000;left:16px;bottom:112px;font:14px/1.45 system-ui;color:var(--dsw-alias-label-primary,#16181d)}
|
|
3849
4485
|
.dsh-mobile-control__panel{box-sizing:border-box;width:min(380px,calc(100vw - 32px));max-height:calc(100vh - 140px);overflow-y:auto;padding:16px;border:1px solid var(--dsw-alias-border-subtle,#e1e5eb);border-radius:18px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 18px 50px rgb(15 23 42 / 18%)}
|
|
3850
|
-
.dsh-mobile-control__header{display:flex;align-items:center;justify-content:space-between;gap:
|
|
3851
|
-
.dsh-mobile-control__app-download{display:flex;align-items:center;justify-content:space-between;box-sizing:border-box;min-height:38px;margin:0 0 10px;padding:8px 11px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f7f8fa);color:var(--dsw-alias-label-primary,#16181d);font:600 12px/1.3 system-ui;text-decoration:none}.dsh-mobile-control__app-download[hidden]{display:none}.dsh-mobile-control__app-download::after{color:#2563eb;font-size:14px;content:"↗"}.dsh-mobile-control__app-download:hover{border-color:#9fb9e8;background
|
|
4486
|
+
.dsh-mobile-control__header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px}.dsh-mobile-control__panel h2{margin:0;font-size:17px;line-height:24px}.dsh-mobile-control__header-actions{display:flex;align-items:center;gap:2px}.dsh-mobile-control__update-plugin,.dsh-mobile-control__diagnostic-entry,.dsh-mobile-control__close{display:inline-flex;align-items:center;justify-content:center;min-width:44px;height:44px;padding:0;border:0;border-radius:10px;background:transparent;color:inherit;cursor:pointer}.dsh-mobile-control__update-plugin,.dsh-mobile-control__diagnostic-entry{padding:0 8px;color:#2563eb;font:650 12px/1 system-ui;white-space:nowrap}.dsh-mobile-control__update-plugin[hidden]{display:none}.dsh-mobile-control__update-plugin:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__close{font-size:24px;line-height:1}.dsh-mobile-control__update-plugin:hover:not(:disabled),.dsh-mobile-control__diagnostic-entry:hover,.dsh-mobile-control__close:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}
|
|
4487
|
+
.dsh-mobile-control__release-notice{margin:-2px 0 10px;padding:8px 10px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#eff6ff);color:var(--dsw-alias-label-primary,#1d4ed8);font-size:11px;line-height:1.45}.dsh-mobile-control__release-notice.is-error{color:#dc2626}.dsh-mobile-control__release-notice[hidden]{display:none}.dsh-mobile-control__app-download{display:flex;align-items:center;justify-content:space-between;box-sizing:border-box;min-height:38px;margin:0 0 10px;padding:8px 11px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f7f8fa);color:var(--dsw-alias-label-primary,#16181d);font:600 12px/1.3 system-ui;text-decoration:none}.dsh-mobile-control__app-download[hidden]{display:none}.dsh-mobile-control__app-download::after{color:#2563eb;font-size:14px;content:"↗"}.dsh-mobile-control__app-download:hover{border-color:#9fb9e8;background:var(--dsw-alias-interactive-bg-hover-solid,var(--dsw-alias-bg-layer-2,#f5f8ff));color:var(--dsw-alias-label-primary,#1d4ed8)}
|
|
3852
4488
|
.dsh-mobile-control__switcher{display:grid;grid-template-columns:repeat(2,1fr);gap:4px;margin:0 0 14px;padding:4px;border-radius:12px;background:var(--dsw-alias-bg-layer-1,#f3f5f8)}.dsh-mobile-control__switcher[hidden]{display:none}.dsh-mobile-control__tab{min-height:36px;border:0;border-radius:9px;background:transparent;color:var(--dsw-alias-label-secondary,#606873);font:600 13px/1 system-ui;cursor:pointer}.dsh-mobile-control__tab.is-active{background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);box-shadow:0 1px 3px rgb(15 23 42 / 10%)}.dsh-mobile-control__view[hidden]{display:none}.dsh-mobile-control__intro{margin:0 0 12px;color:var(--dsw-alias-label-secondary,#606873);font-size:12px;line-height:1.55}.dsh-mobile-control__view.is-remote .dsh-mobile-control__actions{display:grid;grid-template-columns:1fr 1fr;gap:8px}.dsh-mobile-control__view.is-remote .dsh-mobile-control__actions button[hidden]{display:none}
|
|
3853
|
-
.dsh-mobile-control__provider-section{position:relative;margin:0 0
|
|
3854
|
-
.dsh-mobile-
|
|
4489
|
+
.dsh-mobile-control__provider-section{position:relative;margin:0 0 14px}.dsh-mobile-control__section-title{margin:0 0 8px;color:var(--dsw-alias-label-primary,#16181d);font:650 13px/1.4 system-ui}.dsh-mobile-control__provider-section>.dsh-mobile-control__section-title{padding-right:42px}.dsh-mobile-control__provider-choices{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.dsh-mobile-control__provider{display:flex;min-width:0;flex-direction:column;gap:6px;min-height:94px;padding:10px 11px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);text-align:left;cursor:pointer;touch-action:manipulation;transition:border-color 160ms ease,background-color 160ms ease,box-shadow 160ms ease}.dsh-mobile-control__provider:hover{border-color:#6f96db;background:var(--dsw-alias-interactive-bg-hover-solid,var(--dsw-alias-bg-layer-1,#f8fbff))}.dsh-mobile-control__provider.is-selected{border-color:#2563eb;background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-bg-layer-1,#f5f8ff));box-shadow:0 0 0 1px #2563eb inset}.dsh-mobile-control__provider:disabled{cursor:wait;opacity:.62}.dsh-mobile-control__provider-top{display:flex;min-width:0;align-items:flex-start;justify-content:space-between;gap:5px}.dsh-mobile-control__provider-top strong{min-width:0;font-size:12px;line-height:1.3}.dsh-mobile-control__provider-badge{flex:none;padding:2px 5px;border-radius:999px;background:#e8f0ff;color:#1d4ed8;font:650 9px/1.25 system-ui}.dsh-mobile-control__provider-badge.is-cpolar{background:#eaf8f2;color:#087454}.dsh-mobile-control__provider-description{color:var(--dsw-alias-label-secondary,#606873);font-size:10px;line-height:1.45}.dsh-mobile-control__provider-info{position:absolute;z-index:5;top:-13px;right:-8px}.dsh-mobile-control__provider-info-button{display:flex;align-items:center;justify-content:center;width:44px;height:44px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--dsw-alias-label-secondary,#475569);cursor:pointer;touch-action:manipulation}.dsh-mobile-control__provider-info-button:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f5f9);color:#2563eb}.dsh-mobile-control__provider-info-glyph{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:18px;height:18px;border:1.5px solid currentColor;border-radius:50%;font:700 12px/1 system-ui}.dsh-mobile-control__provider-info-popover{position:absolute;z-index:6;top:38px;right:4px;box-sizing:border-box;width:min(292px,calc(100vw - 72px));padding:10px 12px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 10px 28px rgb(15 23 42 / 16%)}.dsh-mobile-control__provider-info-popover[hidden]{display:none}.dsh-mobile-control__provider-info-popover strong,.dsh-mobile-control__provider-info-popover span{display:block}.dsh-mobile-control__provider-info-popover strong{margin-bottom:3px;font-size:12px}.dsh-mobile-control__provider-info-popover span{color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.55}
|
|
4490
|
+
.dsh-mobile-control__self-hosted{margin:8px 0 0;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:12px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__self-hosted-summary{display:flex;box-sizing:border-box;min-height:48px;align-items:center;justify-content:space-between;gap:10px;padding:8px 11px;cursor:pointer;list-style-position:inside}.dsh-mobile-control__self-hosted-summary>span:first-child{display:flex;min-width:0;flex-direction:column;gap:1px}.dsh-mobile-control__self-hosted-summary strong{font-size:11px}.dsh-mobile-control__self-hosted-summary span span{color:var(--dsw-alias-label-secondary,#606873);font-size:9px;line-height:1.35}.dsh-mobile-control__provider-badge.is-frp{background:#eef0f3;color:#475569}.dsh-mobile-control__self-hosted-body{padding:0 8px 8px}.dsh-mobile-control__provider.is-frp{width:100%;min-height:64px;background:var(--dsw-alias-bg-layer-2,#fff)}
|
|
4491
|
+
.dsh-mobile-control__cpolar-setup{margin:0 0 12px;padding:12px;border:1px solid var(--dsw-alias-border-subtle,#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__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:#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-normal,#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-subtle,#e1e5eb);padding-top:9px}.dsh-mobile-control__details>summary{min-height:30px;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:10px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__danger{flex:1 0 100%;min-height:38px;margin-top:3px;padding:7px 10px;border:1px solid #dc2626;border-radius:9px;background:transparent;color:#dc2626;font:12px/1.3 system-ui;cursor:pointer}
|
|
4492
|
+
.dsh-mobile-control__frp-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-subtle,#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-subtle,#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-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__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-normal,#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-subtle,#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 10px/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 9px/1.25 system-ui}.dsh-mobile-control__state-badge.is-ready{background:#e6f7f0;color:#087454}.dsh-mobile-control__state-badge.is-busy{background:#e8f0ff;color:#1d4ed8}.dsh-mobile-control__state-badge.is-attention{background:#fff4dc;color:#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-subtle,#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-subtle,#e1e5eb)}
|
|
3855
4493
|
.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:#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}
|
|
3856
4494
|
.dsh-mobile-control__status{margin:0 0 14px;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary,#606873)}.dsh-mobile-control__status::before{display:inline-block;width:8px;height:8px;margin-right:7px;border-radius:50%;background:#98a1ad;content:""}.dsh-mobile-control__status.is-running::before{background:#16a36a}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}
|
|
3857
|
-
.dsh-mobile-control__guide{margin:0 0 14px;padding:12px;border:1px solid #
|
|
4495
|
+
.dsh-mobile-control__guide{margin:0 0 14px;padding:12px;border:1px solid #6f96db;border-radius:12px;background:var(--dsw-alias-bg-layer-1,#eff6ff)}.dsh-mobile-control__guide[hidden]{display:none}.dsh-mobile-control__guide-title{margin:0;color:var(--dsw-alias-label-primary,#172554);font:650 13px/1.45 system-ui}.dsh-mobile-control__guide-summary,.dsh-mobile-control__guide-note{margin:4px 0 0;color:var(--dsw-alias-label-secondary,#475569);font-size:12px;line-height:1.5}.dsh-mobile-control__guide-steps{margin:8px 0 0;padding-left:20px;color:var(--dsw-alias-label-primary,#1e293b);font-size:12px;line-height:1.6}.dsh-mobile-control__guide-note{color:var(--dsw-alias-label-secondary,#64748b)}.dsh-mobile-control__guide-actions{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px}.dsh-mobile-control__guide-actions button{min-width:0;min-height:44px;padding:8px;border-radius:10px;font:12px/1.25 system-ui;cursor:pointer}.dsh-mobile-control__guide-actions button:disabled{cursor:not-allowed;opacity:.45}
|
|
3858
4496
|
.dsh-mobile-control__extensions{margin:0 0 12px;color:var(--dsw-alias-label-secondary,#606873);font-size:12px}
|
|
3859
4497
|
.dsh-mobile-control__view.is-diagnostics{--dsh-diagnostic-ok:#087454;--dsh-diagnostic-warning:#a35b00;--dsh-diagnostic-error:#c62828;--dsh-diagnostic-info:#526071}.dsh-mobile-control__diagnostic-summary{box-sizing:border-box;margin:0;padding:13px;border:1px solid var(--dsw-alias-border-subtle,#dbe1e8);border-radius:16px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__diagnostic-summary-main{display:grid;grid-template-columns:36px minmax(0,1fr);align-items:center;gap:11px}.dsh-mobile-control__diagnostic-summary-icon{position:relative;display:block;width:36px;height:36px;border-radius:50%;background:#e8edf3;color:var(--dsh-diagnostic-info)}.dsh-mobile-control__diagnostic-summary-icon::before,.dsh-mobile-control__diagnostic-summary-icon::after{position:absolute;content:""}.dsh-mobile-control__diagnostic-summary-body{display:flex;min-width:0;flex-direction:column;gap:2px}.dsh-mobile-control__diagnostic-summary-body strong{font-size:13px;line-height:1.35}.dsh-mobile-control__diagnostic-summary-body span{color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__diagnostic-summary-meta{display:block;margin-top:11px;padding-top:9px;border-top:1px solid var(--dsw-alias-border-subtle,#dbe1e8);color:var(--dsw-alias-label-secondary,#606873);font-size:10px;line-height:1.45}.dsh-mobile-control__diagnostic-summary.is-ok .dsh-mobile-control__diagnostic-summary-icon{background:#e6f7f0;color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-summary.is-ok .dsh-mobile-control__diagnostic-summary-icon::before{top:10px;left:10px;width:13px;height:7px;border-bottom:2px solid currentColor;border-left:2px solid currentColor;transform:rotate(-45deg)}.dsh-mobile-control__diagnostic-summary.is-attention .dsh-mobile-control__diagnostic-summary-icon{background:#fff4dc;color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-summary.is-error .dsh-mobile-control__diagnostic-summary-icon{background:#fdecec;color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-summary.is-attention .dsh-mobile-control__diagnostic-summary-icon::before,.dsh-mobile-control__diagnostic-summary.is-error .dsh-mobile-control__diagnostic-summary-icon::before{top:8px;left:17px;width:2px;height:13px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-summary.is-attention .dsh-mobile-control__diagnostic-summary-icon::after,.dsh-mobile-control__diagnostic-summary.is-error .dsh-mobile-control__diagnostic-summary-icon::after{bottom:8px;left:17px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-summary.is-running .dsh-mobile-control__diagnostic-summary-icon{background:#e8f0ff;color:#2563eb}.dsh-mobile-control__diagnostic-summary.is-running .dsh-mobile-control__diagnostic-summary-icon::before{inset:9px;border:2px solid rgb(37 99 235 / 24%);border-top-color:currentColor;border-radius:50%;animation:dsh-diagnostic-spin .8s linear infinite}
|
|
3860
4498
|
.dsh-mobile-control__diagnostic-summary.is-idle .dsh-mobile-control__diagnostic-summary-icon::before{top:8px;left:17px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-summary.is-idle .dsh-mobile-control__diagnostic-summary-icon::after{top:13px;left:17px;width:2px;height:11px;border-radius:2px;background:currentColor}
|
|
@@ -3864,9 +4502,9 @@ window.__ModuleLoader__.load({
|
|
|
3864
4502
|
@keyframes dsh-diagnostic-spin{to{transform:rotate(360deg)}}@keyframes dsh-diagnostic-reveal{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}
|
|
3865
4503
|
.dsh-mobile-control__actions{display:flex;flex-wrap:nowrap;gap:6px}.dsh-mobile-control__actions button{flex:1 1 0;min-width:0;min-height:40px;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-normal,#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}
|
|
3866
4504
|
.dsh-mobile-control button:focus-visible,.dsh-mobile-control a:focus-visible,.dsh-mobile-control input:focus-visible,.dsh-mobile-control summary:focus-visible{outline:3px solid rgb(37 99 235 / 28%);outline-offset:2px}
|
|
3867
|
-
.dsh-mobile-control__trigger{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% +
|
|
4505
|
+
.dsh-mobile-control__trigger{box-sizing:border-box;flex:none;display:flex;align-items:center;gap:8px;width:calc(100% + 4px);height:42px;margin:4px -2px;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{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}
|
|
3868
4506
|
.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:34px;padding:6px 8px;border:1px solid var(--dsw-alias-border-normal,#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-subtle,#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-subtle,#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:28px;padding:4px 8px;border:1px solid #dc2626;border-radius:8px;background:transparent;color:#dc2626;font:12px/1.2 system-ui;cursor:pointer}
|
|
3869
|
-
@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}}
|
|
4507
|
+
@media (max-width:359px){.dsh-mobile-control__provider-choices{grid-template-columns:1fr}.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}}
|
|
3870
4508
|
`;
|
|
3871
4509
|
/** Mount the desktop control or mobile feature enhancements. */
|
|
3872
4510
|
function apply(ctx) {
|
|
@@ -3897,17 +4535,33 @@ window.__ModuleLoader__.load({
|
|
|
3897
4535
|
name: "sidebar.footer.action",
|
|
3898
4536
|
id: "dsh-mobile"
|
|
3899
4537
|
}, ({ wide }) => (0, react.createElement)("button", {
|
|
3900
|
-
"aria-expanded":
|
|
4538
|
+
"aria-expanded": control.isOpen(),
|
|
4539
|
+
"aria-controls": CONTROL_PANEL_ID,
|
|
3901
4540
|
"aria-label": t("mobileAccess"),
|
|
3902
4541
|
className: `dsh-mobile-control__trigger${wide ? "" : " is-rail"}`,
|
|
3903
4542
|
lang: triggerLocale,
|
|
3904
4543
|
type: "button",
|
|
3905
4544
|
title: t("mobileAccess"),
|
|
3906
4545
|
onClick: control.toggle
|
|
3907
|
-
}, (0, react.createElement)("
|
|
4546
|
+
}, (0, react.createElement)("svg", {
|
|
3908
4547
|
"aria-hidden": true,
|
|
3909
|
-
className: "dsh-mobile-control__trigger-icon"
|
|
3910
|
-
|
|
4548
|
+
className: "dsh-mobile-control__trigger-icon",
|
|
4549
|
+
focusable: false,
|
|
4550
|
+
width: wide ? 16 : 18,
|
|
4551
|
+
height: wide ? 16 : 18,
|
|
4552
|
+
viewBox: "0 0 16 16",
|
|
4553
|
+
fill: "none",
|
|
4554
|
+
stroke: "currentColor",
|
|
4555
|
+
strokeWidth: 1.5,
|
|
4556
|
+
strokeLinecap: "round",
|
|
4557
|
+
strokeLinejoin: "round"
|
|
4558
|
+
}, (0, react.createElement)("rect", {
|
|
4559
|
+
x: 4,
|
|
4560
|
+
y: 1,
|
|
4561
|
+
width: 8,
|
|
4562
|
+
height: 14,
|
|
4563
|
+
rx: 2
|
|
4564
|
+
}), (0, react.createElement)("path", { d: "M7 12h2" })), wide ? (0, react.createElement)("span", { className: "dsh-mobile-control__trigger-label" }, t("mobileAccess")) : void 0)));
|
|
3911
4565
|
return () => {
|
|
3912
4566
|
disposeSlot();
|
|
3913
4567
|
control.remove();
|
|
@@ -3922,13 +4576,16 @@ window.__ModuleLoader__.load({
|
|
|
3922
4576
|
/** Client services required by the mobile adaptation. */
|
|
3923
4577
|
const inject = ["slots"];
|
|
3924
4578
|
//#endregion
|
|
4579
|
+
exports.CONTROL_STYLES = CONTROL_STYLES;
|
|
3925
4580
|
exports.DIAGNOSTIC_REASON_MESSAGES = DIAGNOSTIC_REASON_MESSAGES;
|
|
3926
4581
|
exports.MOBILE_CONTROL_MESSAGES = MOBILE_CONTROL_MESSAGES;
|
|
3927
4582
|
exports.PerIdActivationLifecycle = PerIdActivationLifecycle;
|
|
3928
4583
|
exports.apply = apply;
|
|
3929
4584
|
exports.bindClientResponseLifetime = bindClientResponseLifetime;
|
|
4585
|
+
exports.clientReleaseInfo = clientReleaseInfo;
|
|
3930
4586
|
exports.combineClientSignalLifetime = combineClientSignalLifetime;
|
|
3931
4587
|
exports.combineClientSignals = combineClientSignals;
|
|
4588
|
+
exports.createFrpServerTemplateForClipboard = createFrpServerTemplateForClipboard;
|
|
3932
4589
|
exports.diagnosticEntriesForRender = diagnosticEntriesForRender;
|
|
3933
4590
|
exports.diagnosticOverallForChecks = diagnosticOverallForChecks;
|
|
3934
4591
|
exports.diagnosticServerCopy = diagnosticServerCopy;
|