dsh-mobile 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/CONTRIBUTORS.md +7 -3
- package/README.en.md +27 -14
- package/README.md +27 -15
- package/SECURITY.md +7 -5
- package/docs/ATTACH_EXISTING_FRPS.en.md +44 -0
- package/docs/ATTACH_EXISTING_FRPS.md +98 -0
- package/docs/README.md +3 -0
- package/docs/SELF_HOSTED_FRP.en.md +3 -1
- package/docs/SELF_HOSTED_FRP.md +7 -1
- package/lib/cli.js +52 -15
- package/lib/cli.js.map +1 -1
- package/lib/client.js +1339 -162
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +393 -10
- package/lib/index.mjs +2178 -593
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js +1 -1
- package/lib/mobile-compat.js.map +1 -1
- package/package.json +25 -21
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","createElement"],"sources":["../src/client-messages.ts","../src/frp-template.ts","../src/task-notify.ts","../src/native-mobile.ts","../src/local-admin-host.ts","../src/client.ts"],"sourcesContent":["export type MobileControlLocale = 'it' | 'en' | 'zh'\n\nexport const MOBILE_CONTROL_MESSAGES = {\n en: {\n mobileAccess: 'Mobile access', collapseMobileAccess: 'Collapse Mobile access', downloadAndroid: 'Download latest Android app', downloadAndroidVersion: 'Download latest Android app · v{version}', downloadAndroidAria: 'Download the latest Android app from GitHub Releases', downloadAndroidVersionAria: 'Download Android app v{version} from GitHub Releases', updatePlugin: 'Update plugin', updatePluginAria: 'Update DSH Mobile to v{version}', updatingPlugin: 'Updating…', pluginUpdatedRestart: 'Updated to v{version}. Restart DSH to apply it.', pluginUpdateFailed: 'Update failed: {error}', updateTo: 'Update to v{version}', updateNotice: 'Restart DSH after installing. Existing apps and paired devices need no re-pairing; unsure about the desktop version? Check the README compatibility table.', updateNotesEmpty: 'No release notes fetched — updating is still fine; full details on the GitHub Release page.', updateNow: 'Update now', updateLater: 'Not now',\n lan: 'Local network', remote: 'Remote', lanAccess: 'Local network access', remoteAccess: 'Remote access', browserAccess: 'Browser access', remoteAddress: 'Remote address', loadingStatus: 'Loading status…', loadingRemoteStatus: 'Loading remote status…',\n generateCopyKey: 'Generate and copy key', copyPairLink: 'Copy pairing link', managePairedDevices: 'Manage paired devices', clearAllDevices: 'Clear all devices', pairingQr: 'Pairing QR code',\n remoteIntro: 'Choose the remote channel that suits you. Switching or disabling remote access does not affect the local network.', httpFrameWarning: 'HTTP pages may be embedded for compatibility, but their contents are unencrypted and can be altered. Use HTTPS for sensitive work.', chooseProvider: 'Choose connection method', currentProvider: 'Current connection', remoteStateOff: 'Off', remoteStateConnecting: 'Connecting', remoteStateAttention: 'Needs attention', providerInfoAria: 'View remote connection security and network information', providerGroupAria: 'Remote connection method', providerSafeTitle: 'You remain protected', providerSafeText: 'Only paired devices can access DSH. cpolar is installed on demand and can be removed completely; Tailscale may be slow or unavailable on mainland China networks, where cpolar is recommended.',\n builtIn: 'Built in', mainlandPreferred: 'Preferred in mainland China', tailscaleDescription: 'Wider coverage; mainland China networks may be unstable. The first connection requires login and Funnel authorization.', cpolarDescription: 'Installs the official component on demand and is suitable for mainland China networks.',\n prepareCpolar: 'Prepare cpolar', checkingComponent: 'Checking component…', installOfficial: 'Install official component', cpolarAccountNote: 'Sign in to the cpolar website and copy the Authtoken. The token is stored only in the plugin private directory and is never shown on the page or in logs.', registerCpolar: 'Register with cpolar', openDashboard: 'Open dashboard to get token', tokenPlaceholder: 'Paste cpolar Authtoken', saveConnect: 'Save and connect', saving: 'Saving…', componentDetails: 'Component source and cleanup', componentDetailsText: 'Downloaded from the official cpolar website and verified at a fixed version only after you choose Install. It does not add a system service, startup item, registry entry, or PATH entry.', pluginPrivateDirectory: 'Plugin private directory', officialDownload: 'Official download page', terms: 'Terms of service', purgeCpolar: 'Completely remove cpolar component and configuration', tailscaleHelp: 'How to use Tailscale', tailscaleHelpText: 'The runtime component is included with the plugin. The first connection opens the official Tailscale login and Funnel authorization pages; the plugin never accesses your account password.',\n funnelGuideAria: 'Steps to enable Tailscale Funnel', funnelGuideTitle: 'Remote access setup · Step 2', funnelGuideSummary: 'Tailscale login is complete. Funnel must still be allowed for this computer; the official page also enables HTTPS.', funnelStep1: 'Open the official Tailscale authorization page for this node.', funnelStep2: 'Confirm Funnel; you do not need to sign in to DSH again.', funnelStep3: 'Return to DSH; the plugin checks and connects automatically.', funnelGuideNote: 'Requires an Owner, Admin, or Network admin account.', continueFunnel: 'Continue Funnel authorization', retryNow: 'Done, retry now',\n enableRemote: 'Enable remote access', disableRemote: 'Disable remote access', continueLogin: 'Continue login', reconnect: 'Reconnect', generateRemoteQr: 'Generate remote pairing QR code', copyRemoteLink: 'Copy remote pairing link', manageRemoteDevices: 'Manage remote devices', resetRemoteLogin: 'Sign out and clear remote login', resetRemoteDevices: 'Disable and clear remote devices',\n lanOn: 'Local network access is on.', lanOff: 'Local network access is off.', enableLan: 'Enable local network access', disableLan: 'Disable local network access', extensionsLoaded: 'Extensions: {loaded} loaded', extensionsFailed: 'Extensions: {loaded} loaded, {failed} failed',\n keyGenerationFailed: 'Could not generate a pairing key.', keyCopied: 'Pairing key copied. Paste it into the Android app.', linkCopied: 'Pairing link copied. Send it to the phone and paste it into the app or open it in a browser.', copySecret: 'Copy {kind}: {value}', pairingKey: 'pairing key', pairingLink: 'pairing link', noDevices: 'No paired devices.', noRemoteDevices: 'No remote paired devices.', device: 'Device', expires: 'Expires {time}', revoke: 'Revoke', confirmResetDevices: 'Remove all paired devices? Connected devices will be disconnected immediately.',\n ready: 'Ready', installed: 'Installed', installWithSize: 'Install official component · {size} MB', cpolarUnsupported: 'Windows x64 and Linux x64/arm64 are currently supported.', cpolarNotInstalled: 'Not installed. A fixed version is downloaded from the official cpolar website only after you click the button below.', cpolarNeedsToken: 'Official component {version} is verified. Save the account token to continue.', cpolarReady: 'Official component {version} and the local account configuration are ready. Free temporary addresses may change after DSH or cpolar restarts; if the app cannot connect, scan the current remote QR code again.',\n remoteOff: 'Remote access is disabled. Local network access is unaffected.', remoteUnavailableCpolar: 'cpolar is not installed or its local account is not configured.', remoteUnavailableTailscale: 'This computer is missing the Funnel runtime component. Reinstall the complete plugin package.', remoteStartingCpolar: 'Connecting to a cpolar node…', remoteStartingTailscale: 'Starting the secure Tailscale channel…', remoteNeedsLogin: 'Complete a one-time Tailscale login in the browser. The plugin never reads your password.', remoteConnectingCpolar: 'Public address allocated; starting the DSH authentication gateway…', remoteConnectingTailscale: 'Login complete; creating the public HTTPS address…', remoteReady: 'Remote access is ready. Only paired devices can access DSH.', remoteError: 'The remote connection was not established. Reconnect; local network access still works.',\n funnelPermission: 'Login is complete. Authorize Funnel to establish the remote connection automatically.', funnelHttps: 'Login is complete. Authorize Funnel; the official page also enables HTTPS.', funnelStart: 'Login is complete. Finish the initial Tailscale Funnel authorization.', tailscaleDnsMissing: 'Tailscale has not provided a remote address. Reconnect and confirm login is complete.', gatewayStartFailed: 'The remote gateway failed to start. Reconnect; local network access is unaffected.', controlChannelFailed: 'The remote component connection was interrupted. Reconnect.', cpolarMissing: 'The official cpolar component is not installed. Complete the preparation steps above.', cpolarInvalid: 'cpolar component verification failed. Remove it completely and reinstall.', cpolarConfigMissing: 'No cpolar account token is saved. Complete the preparation steps above.', cpolarConfigInvalid: 'The local cpolar configuration is invalid. Save the account token again.', cpolarPortUnavailable: 'Could not allocate a local remote-gateway port. Try again.', cpolarLaunchFailed: 'The cpolar client failed to start.', cpolarTimeout: 'Timed out connecting to a cpolar node. Reconnect.', cpolarStopped: 'The cpolar connection stopped.', cpolarExited: 'The cpolar connection exited unexpectedly. Reconnect.', cpolarOutputInvalid: 'cpolar returned an unrecognized status.', cpolarOriginInvalid: 'The public address returned by cpolar failed validation.', setupOpened: 'The official Tailscale page is open. Return to DSH after enabling Funnel; reconnection is automatic.',\n switchProviderConfirm: 'Switching connection method first disables the current remote channel. Local network access and paired devices are unaffected. Continue?', switchingCpolar: 'Switching to cpolar…', switchingTailscale: 'Switching to Tailscale Funnel…', installConfirm: 'Download and verify a fixed version from the official cpolar website (about 7.3 MB) and extract it only to the DSH Mobile private directory? No system service, PATH/registry entry, or startup item is added.', downloading: 'Downloading and verifying…', installingCpolar: 'Installing the official cpolar component. Keep DSH running until it completes.', installFailed: 'Component installation failed: {error}', invalidToken: 'Paste the complete Authtoken from the cpolar dashboard.', configuredConnecting: 'Account configuration saved; connecting through cpolar…', configureFailed: 'Configuration failed: {error}', purgeConfirm: 'Completely remove the cpolar component, token configuration, and runtime logs from the DSH Mobile private directory? Local network access, DSH data, and other system programs are unaffected.', purging: 'Stopping the channel and removing cpolar files managed by DSH Mobile…', purgeFailed: 'Cleanup failed: {error}', reconnectingCpolar: 'Reconnecting to a cpolar node…', reconnectingTailscale: 'Checking Tailscale settings and reconnecting…', remoteQrReady: 'Remote pairing QR code generated. Scan it from “Remote access” in the app.', resetCpolarConfirm: 'Disable the cpolar remote channel and remove all remote paired devices? Your cpolar account and other tunnels are unchanged.', resetTailscaleConfirm: 'Sign out of Tailscale on this computer and remove all remote paired devices? Local network configuration is unchanged.', requestFailed: 'Request failed: {error}', wsPathsTitle: 'Third-party WebSocket paths', wsPathsIntro: 'When an installed plugin cannot connect (say a sidebar terminal or a live panel), it is usually because its connection paths have not been approved yet. Everything that needs your attention is listed below — one click on “Allow” fixes it.', wsPathsPlaceholder: '/sidebar/ws/terminal', wsPathsAdd: 'Allow path', wsPathsRemove: 'Remove', wsPathsEmpty: 'Nothing to configure right now. If a plugin cannot connect later, one-click options appear here.', wsPathsInvalid: 'Enter an exact path starting with / (no query string).', wsPathsDetected: 'A plugin connection was blocked — press Allow to let it through', wsPathsAllow: 'Allow', wsPathsAllowAll: 'Allow all', wsPathsGroupMeta: '{n} paths · blocked {m} times', wsPathsNewBlocked: 'New blocked third-party connections — open diagnostics to review', wsPathsAdvancedToggle: 'Add paths manually (advanced)', wsPathsAnnounce: '{n} plugin connections waiting to be approved', wsPathsAdvancedHint: 'Manual entries are rarely necessary: use the buttons above. Only fill this in when you know the exact path.',\n },\n it: {\n mobileAccess: 'Accesso mobile', collapseMobileAccess: 'Riduci Accesso mobile', downloadAndroid: 'Scarica l’ultima app Android', downloadAndroidVersion: 'Scarica l’ultima app Android · v{version}', downloadAndroidAria: 'Scarica l’ultima app Android da GitHub Releases', downloadAndroidVersionAria: 'Scarica l’app Android v{version} da GitHub Releases', updatePlugin: 'Aggiorna plugin', updatePluginAria: 'Aggiorna DSH Mobile alla v{version}', updatingPlugin: 'Aggiornamento…', pluginUpdatedRestart: 'Aggiornato alla v{version}. Riavvia DSH per applicarlo.', pluginUpdateFailed: 'Aggiornamento non riuscito: {error}', updateTo: 'Aggiorna alla v{version}', updateNotice: 'Riavvia DSH dopo l’installazione. Le app esistenti e i dispositivi già associati non richiedono ri-associazione; in caso di dubbi sulla versione desktop, consulta la tabella di compatibilità nel README.', updateNotesEmpty: 'Note di rilascio non recuperate — l’aggiornamento resta possibile; dettagli nella pagina Release su GitHub.', updateNow: 'Aggiorna ora', updateLater: 'Non ora',\n lan: 'Rete locale', remote: 'Remoto', lanAccess: 'Accesso rete locale', remoteAccess: 'Accesso remoto', browserAccess: 'Accesso browser', remoteAddress: 'Indirizzo remoto', loadingStatus: 'Lettura stato…', loadingRemoteStatus: 'Lettura stato remoto…',\n generateCopyKey: 'Genera e copia chiave', copyPairLink: 'Copia link di abbinamento', managePairedDevices: 'Gestisci dispositivi abbinati', clearAllDevices: 'Rimuovi tutti i dispositivi', pairingQr: 'Codice QR di abbinamento', remoteIntro: 'Scegli il canale remoto più adatto. Cambiare o disattivare l’accesso remoto non influisce sulla rete locale.', httpFrameWarning: 'Le pagine HTTP possono essere incorporate per compatibilità, ma i contenuti non sono cifrati e possono essere alterati. Usa HTTPS per le attività sensibili.', chooseProvider: 'Scegli metodo di connessione', currentProvider: 'Connessione attuale', remoteStateOff: 'Disattivata', remoteStateConnecting: 'Connessione', remoteStateAttention: 'Da controllare', providerInfoAria: 'Informazioni su sicurezza e rete della connessione remota', providerGroupAria: 'Metodo di connessione remota', providerSafeTitle: 'La protezione resta attiva', providerSafeText: 'Solo i dispositivi abbinati possono accedere a DSH. cpolar viene installato su richiesta e può essere rimosso completamente; Tailscale può essere lento o non disponibile nelle reti della Cina continentale, dove è consigliato cpolar.',\n builtIn: 'Integrato', mainlandPreferred: 'Preferito in Cina continentale', tailscaleDescription: 'Copertura più ampia; le reti della Cina continentale possono essere instabili. Il primo collegamento richiede accesso e autorizzazione Funnel.', cpolarDescription: 'Installa su richiesta il componente ufficiale, adatto alle reti della Cina continentale.', prepareCpolar: 'Prepara cpolar', checkingComponent: 'Controllo componente…', installOfficial: 'Installa componente ufficiale', cpolarAccountNote: 'Accedi al sito cpolar e copia l’Authtoken. Il token resta solo nella directory privata del plugin e non viene mostrato nella pagina o nei log.', registerCpolar: 'Registrati su cpolar', openDashboard: 'Apri la dashboard per il token', tokenPlaceholder: 'Incolla Authtoken cpolar', saveConnect: 'Salva e connetti', saving: 'Salvataggio…', componentDetails: 'Origine e rimozione del componente', componentDetailsText: 'Viene scaricato dal sito ufficiale cpolar e verificato a una versione fissa solo dopo aver scelto Installa. Non aggiunge servizi di sistema, avvio automatico, voci di registro o PATH.', pluginPrivateDirectory: 'Directory privata del plugin', officialDownload: 'Pagina download ufficiale', terms: 'Termini di servizio', purgeCpolar: 'Rimuovi completamente componente e configurazione cpolar', tailscaleHelp: 'Guida a Tailscale', tailscaleHelpText: 'Il componente runtime è incluso nel plugin. Il primo collegamento apre le pagine ufficiali di accesso Tailscale e autorizzazione Funnel; il plugin non accede mai alla password.', funnelGuideAria: 'Passaggi per abilitare Tailscale Funnel', funnelGuideTitle: 'Configurazione accesso remoto · Passaggio 2', funnelGuideSummary: 'L’accesso Tailscale è completato. Devi ancora consentire Funnel per questo computer; la pagina ufficiale abilita anche HTTPS.', funnelStep1: 'Apri la pagina ufficiale di autorizzazione Tailscale per questo nodo.', funnelStep2: 'Conferma Funnel; non serve accedere di nuovo a DSH.', funnelStep3: 'Torna in DSH; il plugin controlla e si connette automaticamente.', funnelGuideNote: 'Richiede un account Owner, Admin o Network admin.', continueFunnel: 'Continua autorizzazione Funnel', retryNow: 'Fatto, riprova ora', enableRemote: 'Attiva accesso remoto', disableRemote: 'Disattiva accesso remoto', continueLogin: 'Continua accesso', reconnect: 'Riconnetti', generateRemoteQr: 'Genera QR di abbinamento remoto', copyRemoteLink: 'Copia link di abbinamento remoto', manageRemoteDevices: 'Gestisci dispositivi remoti', resetRemoteLogin: 'Esci e rimuovi accesso remoto', resetRemoteDevices: 'Disattiva e rimuovi dispositivi remoti',\n lanOn: 'Accesso dalla rete locale attivo.', lanOff: 'Accesso dalla rete locale disattivato.', enableLan: 'Attiva accesso rete locale', disableLan: 'Disattiva accesso rete locale', extensionsLoaded: 'Estensioni: {loaded} caricate', extensionsFailed: 'Estensioni: {loaded} caricate, {failed} non riuscite', keyGenerationFailed: 'Impossibile generare la chiave di abbinamento.', keyCopied: 'Chiave di abbinamento copiata. Incollala nell’app Android.', linkCopied: 'Link di abbinamento copiato. Invialo al telefono e incollalo nell’app oppure aprilo nel browser.', copySecret: 'Copia {kind}: {value}', pairingKey: 'chiave di abbinamento', pairingLink: 'link di abbinamento', noDevices: 'Nessun dispositivo abbinato.', noRemoteDevices: 'Nessun dispositivo remoto abbinato.', device: 'Dispositivo', expires: 'Scade {time}', revoke: 'Revoca', confirmResetDevices: 'Rimuovere tutti i dispositivi abbinati? I dispositivi connessi verranno disconnessi subito.', ready: 'Pronto', installed: 'Installato', installWithSize: 'Installa componente ufficiale · {size} MB', cpolarUnsupported: 'Attualmente sono supportati Windows x64 e Linux x64/arm64.', cpolarNotInstalled: 'Non installato. Una versione fissa viene scaricata dal sito ufficiale cpolar solo dopo aver premuto il pulsante.', cpolarNeedsToken: 'Componente ufficiale {version} verificato. Salva il token account per continuare.', cpolarReady: 'Componente ufficiale {version} e configurazione account locale pronti. Gli indirizzi temporanei gratuiti possono cambiare dopo il riavvio di DSH o cpolar; se l’app non si connette, scansiona di nuovo il QR remoto corrente.',\n remoteOff: 'Accesso remoto disattivato. La rete locale non è interessata.', remoteUnavailableCpolar: 'cpolar non è installato o l’account locale non è configurato.', remoteUnavailableTailscale: 'Su questo computer manca il componente runtime Funnel. Reinstalla il pacchetto completo del plugin.', remoteStartingCpolar: 'Connessione a un nodo cpolar…', remoteStartingTailscale: 'Avvio del canale sicuro Tailscale…', remoteNeedsLogin: 'Completa una volta l’accesso Tailscale nel browser. Il plugin non legge mai la password.', remoteConnectingCpolar: 'Indirizzo pubblico assegnato; avvio del gateway di autenticazione DSH…', remoteConnectingTailscale: 'Accesso completato; creazione dell’indirizzo HTTPS pubblico…', remoteReady: 'Accesso remoto pronto. Solo i dispositivi abbinati possono accedere a DSH.', remoteError: 'Connessione remota non stabilita. Riconnettiti; la rete locale continua a funzionare.', funnelPermission: 'Accesso completato. Autorizza Funnel per stabilire automaticamente la connessione remota.', funnelHttps: 'Accesso completato. Autorizza Funnel; la pagina ufficiale abilita anche HTTPS.', funnelStart: 'Accesso completato. Termina la prima autorizzazione Tailscale Funnel.', tailscaleDnsMissing: 'Tailscale non ha fornito un indirizzo remoto. Riconnettiti e verifica di aver completato l’accesso.', gatewayStartFailed: 'Avvio del gateway remoto non riuscito. Riconnettiti; la rete locale non è interessata.', controlChannelFailed: 'Connessione al componente remoto interrotta. Riconnettiti.', cpolarMissing: 'Il componente ufficiale cpolar non è installato. Completa i passaggi sopra.', cpolarInvalid: 'Verifica del componente cpolar non riuscita. Rimuovilo completamente e reinstallalo.', cpolarConfigMissing: 'Nessun token account cpolar salvato. Completa i passaggi sopra.', cpolarConfigInvalid: 'Configurazione locale cpolar non valida. Salva nuovamente il token.', cpolarPortUnavailable: 'Impossibile assegnare una porta locale al gateway remoto. Riprova.', cpolarLaunchFailed: 'Avvio del client cpolar non riuscito.', cpolarTimeout: 'Connessione al nodo cpolar scaduta. Riconnettiti.', cpolarStopped: 'Connessione cpolar arrestata.', cpolarExited: 'Connessione cpolar terminata inaspettatamente. Riconnettiti.', cpolarOutputInvalid: 'cpolar ha restituito uno stato non riconosciuto.', cpolarOriginInvalid: 'L’indirizzo pubblico restituito da cpolar non ha superato la verifica.', setupOpened: 'La pagina ufficiale Tailscale è aperta. Torna in DSH dopo aver abilitato Funnel; la riconnessione è automatica.',\n switchProviderConfirm: 'Il cambio di metodo disattiva prima il canale remoto corrente. Rete locale e dispositivi abbinati non sono interessati. Continuare?', switchingCpolar: 'Passaggio a cpolar…', switchingTailscale: 'Passaggio a Tailscale Funnel…', installConfirm: 'Scaricare e verificare una versione fissa dal sito ufficiale cpolar (circa 7,3 MB), estraendola solo nella directory privata DSH Mobile? Non vengono aggiunti servizi, PATH/registro o avvio automatico.', downloading: 'Download e verifica…', installingCpolar: 'Installazione del componente ufficiale cpolar. Mantieni DSH in esecuzione fino al termine.', installFailed: 'Installazione componente non riuscita: {error}', invalidToken: 'Incolla l’Authtoken completo dalla dashboard cpolar.', configuredConnecting: 'Configurazione account salvata; connessione tramite cpolar…', configureFailed: 'Configurazione non riuscita: {error}', purgeConfirm: 'Rimuovere completamente componente cpolar, configurazione token e log runtime dalla directory privata DSH Mobile? Rete locale, dati DSH e altri programmi non sono interessati.', purging: 'Arresto del canale e rimozione dei file cpolar gestiti da DSH Mobile…', purgeFailed: 'Pulizia non riuscita: {error}', reconnectingCpolar: 'Riconnessione a un nodo cpolar…', reconnectingTailscale: 'Controllo impostazioni Tailscale e riconnessione…', remoteQrReady: 'QR di abbinamento remoto generato. Scansionalo da “Accesso remoto” nell’app.', resetCpolarConfirm: 'Disattivare il canale remoto cpolar e rimuovere tutti i dispositivi remoti? Account cpolar e altri tunnel restano invariati.', resetTailscaleConfirm: 'Uscire da Tailscale su questo computer e rimuovere tutti i dispositivi remoti? La configurazione della rete locale resta invariata.', requestFailed: 'Richiesta non riuscita: {error}', wsPathsTitle: 'Percorsi WebSocket di terze parti', wsPathsIntro: 'Quando un plugin installato non riesce a connettersi (per esempio un terminale laterale o un pannello live), di solito è perché i suoi percorsi di connessione non sono ancora stati approvati. Tutto ciò che richiede attenzione è elencato qui sotto: un clic su “Consenti” risolve.', wsPathsPlaceholder: '/sidebar/ws/terminal', wsPathsAdd: 'Consenti percorso', wsPathsRemove: 'Rimuovi', wsPathsEmpty: 'Non serve configurare nulla per ora. Se in seguito un plugin non riesce a connettersi, qui compariranno opzioni con un clic.', wsPathsInvalid: 'Inserisci un percorso esatto che inizi con / (senza query).', wsPathsDetected: 'Una connessione di un plugin è stata bloccata — premi Consenti per lasciarla passare', wsPathsAllow: 'Consenti', wsPathsAllowAll: 'Consenti tutti', wsPathsGroupMeta: '{n} percorsi · bloccati {m} volte', wsPathsNewBlocked: 'Nuove connessioni di terze parti bloccate — apri la diagnostica', wsPathsAdvancedToggle: 'Aggiungi percorsi manualmente (avanzato)', wsPathsAnnounce: '{n} connessioni di plugin in attesa di approvazione', wsPathsAdvancedHint: 'Le voci manuali servono raramente: usa i pulsanti qui sopra. Compila solo se conosci il percorso esatto.'\n },\n zh: {} as Record<string, string>,\n} satisfies Record<MobileControlLocale, Record<string, string>>\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n mobileAccess: '移动访问', collapseMobileAccess: '收起移动访问', downloadAndroid: '下载最新 Android App', downloadAndroidVersion: '下载最新 Android App · v{version}', downloadAndroidAria: '从 GitHub Releases 下载最新版 Android App', downloadAndroidVersionAria: '从 GitHub Releases 下载 Android App v{version}', updatePlugin: '更新插件', updatePluginAria: '将 DSH Mobile 更新至 v{version}', updatingPlugin: '正在更新…', pluginUpdatedRestart: '插件已更新至 v{version},重启 DSH 后生效。', pluginUpdateFailed: '更新失败:{error}', updateTo: '更新到 v{version}', updateNotice: '安装后需重启 DSH。现有 App 与已配对设备无需重新配对;电脑端版本不确定时,先对照 README 兼容表。', updateNotesEmpty: '未取到最新版更新说明,仍可继续更新;详情见 GitHub Release 页。', updateNow: '立即更新', updateLater: '暂不', lan: '局域网', remote: '远程', lanAccess: '局域网访问', remoteAccess: '远程访问', browserAccess: '浏览器访问', remoteAddress: '远程地址', loadingStatus: '正在读取状态…', loadingRemoteStatus: '正在读取远程状态…',\n generateCopyKey: '生成并复制密钥', copyPairLink: '复制配对链接', managePairedDevices: '管理配对设备', clearAllDevices: '清除所有设备', pairingQr: '配对二维码', remoteIntro: '选择更适合你的远程通道。切换或关闭远程访问不会影响局域网。', httpFrameWarning: '为兼容性允许嵌入 HTTP 页面,但内容未加密,可能被篡改;处理敏感内容请使用 HTTPS。', chooseProvider: '选择连接方式', currentProvider: '当前连接', remoteStateOff: '未启用', remoteStateConnecting: '连接中', remoteStateAttention: '需处理', providerInfoAria: '查看远程连接安全与网络说明', providerGroupAria: '远程连接方式', providerSafeTitle: '你始终可以放心', providerSafeText: '只有已配对设备能进入 DSH。cpolar 按需安装并可彻底清理;Tailscale 在中国大陆网络下可能连接缓慢、中断或无法使用,国内网络建议优先尝试 cpolar。', builtIn: '内置', mainlandPreferred: '国内网络优先', tailscaleDescription: '覆盖更广;中国大陆网络可能不稳定,首次需登录并允许 Funnel。', cpolarDescription: '按需安装官方组件,适合国内网络环境。',\n prepareCpolar: '准备 cpolar', checkingComponent: '正在检查组件…', installOfficial: '安装官方组件', cpolarAccountNote: '登录 cpolar 官网后复制 Authtoken。令牌只保存在本机插件私有目录,不会显示在页面或日志中。', registerCpolar: '注册 cpolar', openDashboard: '打开控制台获取令牌', tokenPlaceholder: '粘贴 cpolar Authtoken', saveConnect: '保存并连接', saving: '正在保存…', componentDetails: '组件来源与清理说明', componentDetailsText: '仅在你点击安装后从 cpolar 官网下载并校验固定版本。不会写入系统服务、开机启动、注册表或 PATH。', pluginPrivateDirectory: '插件私有目录', officialDownload: '官方下载安装页', terms: '服务条款', purgeCpolar: '彻底移除 cpolar 组件与配置', tailscaleHelp: 'Tailscale 使用说明', tailscaleHelpText: '运行组件已随插件提供。首次连接会打开 Tailscale 官方登录和 Funnel 授权页;插件不会接触你的账号密码。', funnelGuideAria: 'Tailscale Funnel 启用步骤', funnelGuideTitle: '远程访问设置 · 第 2 步', funnelGuideSummary: 'Tailscale 登录已完成。还需为这台电脑允许 Funnel,官方页面会同时启用 HTTPS。', funnelStep1: '打开当前节点的 Tailscale 官方授权页。', funnelStep2: '确认启用 Funnel;无需再次登录 DSH。', funnelStep3: '返回 DSH,插件会自动检查并建立连接。', funnelGuideNote: '需要使用 Owner、Admin 或 Network admin 账号。', continueFunnel: '继续完成 Funnel 授权', retryNow: '已完成,立即重试',\n enableRemote: '启用远程访问', disableRemote: '关闭远程访问', continueLogin: '继续登录', reconnect: '重新连接', generateRemoteQr: '生成远程配对二维码', copyRemoteLink: '复制远程配对链接', manageRemoteDevices: '管理远程设备', resetRemoteLogin: '退出并清除远程登录', resetRemoteDevices: '关闭并清除远程设备', lanOn: '局域网访问已开启。', lanOff: '局域网访问已关闭。', enableLan: '开启局域网访问', disableLan: '关闭局域网访问', extensionsLoaded: '扩展:{loaded} 个已加载', extensionsFailed: '扩展:{loaded} 个已加载,{failed} 个加载失败', keyGenerationFailed: '无法生成配对密钥。', keyCopied: '配对密钥已复制,请粘贴到 Android App。', linkCopied: '配对链接已复制,发给手机后 App 粘贴或浏览器打开即可配对。', copySecret: '请复制{kind}:{value}', pairingKey: '配对密钥', pairingLink: '配对链接', noDevices: '暂无配对设备。', noRemoteDevices: '暂无远程配对设备。', device: '设备', expires: '到期 {time}', revoke: '撤销', confirmResetDevices: '确定要移除所有配对设备吗?此操作会立即终止已连接设备。', ready: '已就绪', installed: '已安装', installWithSize: '安装官方组件 · {size} MB',\n cpolarUnsupported: '当前支持 Windows x64 与 Linux x64/arm64。', cpolarNotInstalled: '尚未安装。只有点击下方按钮后,才会从 cpolar 官网下载固定版本。', cpolarNeedsToken: '官方组件 {version} 已校验,下一步只需保存账号令牌。', cpolarReady: '官方组件 {version} 与本机账号配置已就绪。免费临时地址可能在 DSH 或 cpolar 重启后变化;App 无法连接时,请重新扫描当前远程二维码。', remoteOff: '远程访问未启用。局域网访问不受影响。', remoteUnavailableCpolar: 'cpolar 尚未安装或未完成本机账号配置。', remoteUnavailableTailscale: '当前电脑缺少 Funnel 运行组件,请重新安装完整插件包。', remoteStartingCpolar: '正在连接 cpolar 国内节点…', remoteStartingTailscale: '正在启动 Tailscale 安全通道…', remoteNeedsLogin: '需要在浏览器完成一次 Tailscale 登录。插件不会读取你的密码。', remoteConnectingCpolar: '公网地址已分配,正在启动 DSH 认证网关…', remoteConnectingTailscale: '登录完成,正在建立公开 HTTPS 地址…', remoteReady: '远程访问已就绪。只有已配对设备可以进入 DSH。', remoteError: '远程连接未建立。可重新连接,局域网访问仍可正常使用。',\n funnelPermission: '登录已完成。请继续授权 Funnel,完成后会自动建立远程连接。', funnelHttps: '登录已完成。请继续授权 Funnel,官方页面会同时启用 HTTPS。', funnelStart: '登录已完成。请继续完成 Tailscale Funnel 的首次授权。', tailscaleDnsMissing: 'Tailscale 暂未提供远程地址。请重新连接并确认已完成登录。', gatewayStartFailed: '远程网关启动失败。请重新连接,局域网访问不受影响。', controlChannelFailed: '远程组件连接中断。请重新连接。', cpolarMissing: 'cpolar 官方组件尚未安装。请先完成上方准备步骤。', cpolarInvalid: 'cpolar 组件校验失败。请彻底移除后重新安装。', cpolarConfigMissing: 'cpolar 尚未保存账号令牌。请先完成上方准备步骤。', cpolarConfigInvalid: 'cpolar 本机配置无效。请重新保存账号令牌。', cpolarPortUnavailable: '无法分配本机远程网关端口,请重试。', cpolarLaunchFailed: 'cpolar 客户端未能启动。', cpolarTimeout: '连接 cpolar 国内节点超时,请重新连接。', cpolarStopped: 'cpolar 连接已停止。', cpolarExited: 'cpolar 连接意外退出,请重新连接。', cpolarOutputInvalid: 'cpolar 返回了无法识别的状态。', cpolarOriginInvalid: 'cpolar 返回的公网地址未通过校验。', setupOpened: 'Tailscale 官方页面已打开。完成启用后返回 DSH,这里会自动重新连接。',\n switchProviderConfirm: '切换连接方式会先关闭当前远程通道。局域网和配对设备不会受影响,是否继续?', switchingCpolar: '正在切换到 cpolar…', switchingTailscale: '正在切换到 Tailscale Funnel…', installConfirm: '将从 cpolar 官方网站下载并校验固定版本(约 7.3 MB),仅解压到 DSH Mobile 私有目录。不会安装系统服务、写入 PATH/注册表或设置开机启动。是否继续?', downloading: '正在下载并校验…', installingCpolar: '正在安装 cpolar 官方组件。完成前请保持 DSH 运行。', installFailed: '组件安装失败:{error}', invalidToken: '请粘贴 cpolar 控制台提供的完整 Authtoken。', configuredConnecting: '账号配置已保存,正在建立 cpolar 远程通道…', configureFailed: '配置失败:{error}', purgeConfirm: '彻底移除 DSH Mobile 私有目录中的 cpolar 组件、令牌配置和运行日志?不会影响局域网、DSH 数据或系统中的其他程序。', purging: '正在关闭通道并清理 DSH Mobile 管理的 cpolar 文件…', purgeFailed: '清理失败:{error}', reconnectingCpolar: '正在重新连接 cpolar 国内节点…', reconnectingTailscale: '正在确认 Tailscale 设置并重新连接…', remoteQrReady: '远程配对二维码已生成。请在 App 的“远程访问”中扫描。', resetCpolarConfirm: '关闭 cpolar 远程通道并移除所有远程配对设备?不会修改你的 cpolar 账号或其他隧道。', resetTailscaleConfirm: '退出电脑上的 Tailscale 登录并移除所有远程配对设备?局域网配置不会改变。', requestFailed: '请求失败:{error}', wsPathsTitle: '第三方 WebSocket 路径', wsPathsIntro: '插件(如侧边栏终端、实时面板)连不上时,通常是它的连接路径还没放行。下面列出需要处理的项,点「允许」即可。', wsPathsPlaceholder: '/sidebar/ws/terminal', wsPathsAdd: '允许该路径', wsPathsRemove: '移除', wsPathsEmpty: '目前无需任何设置。以后有插件连不上,会在这里出现可一键放行的选项。', wsPathsInvalid: '请输入以 / 开头的确切路径(不带 query)。', wsPathsDetected: '有插件连接被拦截——点「允许」放行', wsPathsAllow: '允许', wsPathsAllowAll: '全部允许', wsPathsGroupMeta: '{n} 条路径 · 共拦截 {m} 次', wsPathsNewBlocked: '发现被拦截的第三方连接,去诊断页查看', wsPathsAdvancedToggle: '手动添加路径(高级)', wsPathsAnnounce: '{n} 条插件连接等待放行', wsPathsAdvancedHint: '通常不需要手动填写:用上方按钮即可。只有当你确切知道所需路径时才填。'\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n lanSetupTitle: 'Finish local network setup', lanSetupIntro: 'Choose the network shared by this computer and your phone. DSH Mobile creates private certificates only after you confirm.', lanSetupNetwork: 'Computer network', lanSetupChooseNetwork: 'Choose a network', lanSetupRecommended: 'Recommended', lanSetupNoNetworks: 'No private Wi-Fi or Ethernet address was found. Connect this computer to the local network, then scan again.', lanSetupRefresh: 'Scan again', lanSetupFirewallNote: 'Windows may request administrator approval for two inbound rules limited to the local subnet.', lanSetupConfigure: 'Configure local network', lanSetupConfiguring: 'Creating private certificates and local network rules…', lanSetupRequired: 'Local network setup has not been completed. Choose a network above to continue.', lanSetupRestart: 'Setup complete for {origin}. Restart DSH once to start local network access.', lanSetupFailed: 'Setup was not completed. Confirm the network selection and approve the Windows prompt, then try again.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n lanSetupTitle: 'Completa la configurazione della rete locale', lanSetupIntro: 'Scegli la rete condivisa da questo computer e dal telefono. DSH Mobile crea certificati privati solo dopo la conferma.', lanSetupNetwork: 'Rete del computer', lanSetupChooseNetwork: 'Scegli una rete', lanSetupRecommended: 'Consigliata', lanSetupNoNetworks: 'Nessun indirizzo Wi-Fi o Ethernet privato trovato. Collega il computer alla rete locale e ripeti la ricerca.', lanSetupRefresh: 'Cerca di nuovo', lanSetupFirewallNote: 'Windows può richiedere l’approvazione amministratore per due regole in ingresso limitate alla sottorete locale.', lanSetupConfigure: 'Configura rete locale', lanSetupConfiguring: 'Creazione dei certificati privati e delle regole di rete locale…', lanSetupRequired: 'La configurazione della rete locale non è completa. Scegli una rete qui sopra per continuare.', lanSetupRestart: 'Configurazione completata per {origin}. Riavvia DSH una volta per attivare l’accesso locale.', lanSetupFailed: 'Configurazione non completata. Controlla la rete e approva la richiesta di Windows, quindi riprova.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n lanSetupTitle: '完成局域网配置', lanSetupIntro: '选择电脑与手机共同使用的网络。确认后才会生成私有证书,不会在安装时自动开放端口。', lanSetupNetwork: '电脑当前网络', lanSetupChooseNetwork: '请选择网络', lanSetupRecommended: '推荐', lanSetupNoNetworks: '未找到可用的私有 Wi-Fi 或以太网地址。请先让电脑接入局域网,然后重新扫描。', lanSetupRefresh: '重新扫描', lanSetupFirewallNote: 'Windows 可能请求管理员授权,仅创建两条限本地子网访问的入站规则。', lanSetupConfigure: '完成配置', lanSetupConfiguring: '正在生成私有证书并配置局域网规则…', lanSetupRequired: '尚未完成局域网配置,请选择上方网络继续。', lanSetupRestart: '已为 {origin} 完成配置。请重启一次 DSH,局域网访问会自动开启。', lanSetupFailed: '配置未完成。请确认网卡选择并允许 Windows 授权提示,然后重试。',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n selfHostedConnections: 'Self-hosted connection', advanced: 'Advanced', frpName: 'Self-hosted FRP', frpDescription: 'A restricted FRP channel for this DSH gateway only. You manage the VPS and public domain.',\n prepareFrp: 'Configure self-hosted FRP', frpStep1Title: '1 · Connection details', frpStep1Text: 'Enter the VPS address, frps control port, shared token, and public HTTPS origin.', frpServerAddress: 'VPS address', frpServerAddressPlaceholder: 'frp.example.com', frpServerPort: 'frps port', frpToken: 'Shared token', frpTokenPlaceholder: 'At least 16 characters', frpPublicOrigin: 'Public HTTPS origin', frpPublicOriginPlaceholder: 'https://dsh.example.com',\n frpStep2Title: '2 · Prepare the VPS', frpStep2Text: 'Copy the restricted frps and Caddy template, then apply it on your VPS.', copyServerTemplate: 'Copy server template', templateCopied: 'Server template copied. Save frps.toml and the Caddy snippet on the VPS, then add the import line to the Caddyfile.', templateCopyFailed: 'Could not copy the template. Check the four fields above.', vpsDeployText: 'Use an existing OpenSSH key or agent. The VPS must be Ubuntu/Debian with systemd; existing Caddy site configuration is never overwritten.', vpsDeployChangesTitle: 'Automatic deployment will change the following on the VPS (manual template changes nothing by itself):', vpsDeployChangePackages: 'Install Caddy from its official APT repository, plus a Python venv and Certbot for public-IPv4 certificates.', vpsDeployChangeServices: 'Create the dsh-mobile system user (only if missing, otherwise kept), the frps config and service, the Caddy snippet plus one import line, and a daily certificate-renewal timer.', vpsDeployChangeFirewall: 'Open the FRP control port, 80/tcp, and 443/tcp in UFW when it is active; other firewalls are left for you to adjust.', vpsDeployChangeManual: 'Manual deployment applies the same steps by hand from the copied template; automatic deployment runs them over SSH after you confirm the host keys.', vpsSshUser: 'SSH user', vpsSshPort: 'SSH port', vpsSshKey: 'Private key path (optional)', vpsSshKeyPlaceholder: 'Uses ssh-agent or ~/.ssh/config when empty', vpsDeploy: 'Deploy frps + Caddy', vpsDeploying: 'Deploying to the VPS. Keep DSH running…', vpsDeploySuccess: 'VPS deployment completed. Start FRP connection to verify the public endpoint.', vpsDeployFailed: 'VPS deployment failed: {error}', vpsDeployNotReady: 'Save the FRP settings before deploying the VPS.', vpsHostKeyFetching: 'Reading the VPS host keys…', vpsHostKeyFailed: 'Could not read the VPS host keys: {error}', vpsDeployConfirmWithKeys: 'Deploy DSH Mobile frps and Caddy to this VPS? Only DSH Mobile service/config files and firewall rules will be created. Existing non-empty Caddy configuration will stop the deployment.\\n\\nThe server presents these host keys. Confirm they match the VPS console before continuing:\\n{fingerprints}', vpsCopyUninstall: 'Copy VPS uninstall script', vpsUninstallScriptCopied: 'Uninstall script copied. Review it before running with root on the VPS.', vpsUninstallScriptFailed: 'Could not build the uninstall script: {error}', vpsUninstall: 'Remove DSH Mobile from the VPS', vpsUninstallConfirmWithKeys: 'Remove all DSH Mobile services, configs, certificates, and owned firewall rules from this VPS? Your own Caddy content and unrelated rules are kept.\\n\\nThe server presents these host keys. Confirm they match the VPS console before continuing:\\n{fingerprints}', vpsUninstalling: 'Removing DSH Mobile from the VPS. Keep DSH running…', vpsUninstallSuccess: 'VPS cleanup completed. The local FRP settings are unchanged.', vpsUninstallFailed: 'VPS cleanup failed: {error}',\n frpStep3Title: '3 · Prepare this computer', frpStep3Text: 'Download and verify the pinned official frpc client only when requested.', installFrpc: 'Install official frpc', 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.', installingFrp: 'Installing the official frpc component. Keep DSH running until it completes.', frpNotInstalled: 'frpc is not installed. Nothing is downloaded until you click Install.', frpUnsupported: 'This desktop platform is not supported by the managed frpc installer.', frpComponentReady: 'Official frpc {version} is verified.',\n frpStep4Title: '4 · Verify and connect', frpStep4Text: 'The plugin starts only its DSH HTTP vhost and verifies the public endpoint belongs to this computer.', frpAppRequirement: 'Custom remote domains require Android app 0.3.3 or later; local network, cpolar, and Tailscale remain compatible with older supported apps.', frpSaveConnect: 'Save and verify connection', frpSavingConnecting: 'Saving the private settings and verifying the complete FRP path…', frpConfigurationReady: 'Server settings are saved. The token remains only in the plugin private directory.', frpConfigurationMissing: 'Complete all fields, copy the VPS template, then save and connect.', frpInputInvalid: 'Check the VPS address, port, HTTPS origin, and token.',\n frpComponentDetails: 'FRP source and complete cleanup', frpComponentDetailsText: 'The pinned client is downloaded from the official fatedier/frp release. DSH Mobile never installs a service or accepts arbitrary FRP configuration.', frpOfficialRelease: 'Official FRP release', purgeFrp: 'Remove FRP component and private configuration', purgeFrpConfirm: 'Stop FRP and remove frpc, the shared token, generated configuration, staging files, and logs owned by DSH Mobile? The VPS is unchanged.', purgingFrp: 'Stopping FRP and removing files managed by DSH Mobile…',\n switchingFrp: 'Switching to self-hosted FRP…', reconnectingFrp: 'Verifying and reconnecting self-hosted FRP…', resetFrpConfirm: 'Disable self-hosted FRP and remove all remote paired devices? The saved VPS settings and server are unchanged.', remoteUnavailableFrp: 'frpc is not installed or the private server settings are incomplete.', remoteStartingFrp: 'Checking the VPS and preparing the restricted FRP channel…', remoteConnectingFrp: 'frpc is running; verifying the public HTTPS endpoint…', frpMissing: 'Install the official frpc component first.', frpInvalid: 'frpc verification failed. Remove it completely and reinstall.', frpConfigMissing: 'Save the self-hosted FRP connection details first.', frpConfigVerifyFailed: 'frpc rejected the generated configuration. Check the server details and token.', frpVhostPublic: 'The VPS plaintext HTTP vhost is publicly reachable. Bind it to 127.0.0.1 and let Caddy provide HTTPS.', frpVhostProbeFailed: 'The VPS address could not be checked. Confirm DNS and retry.', frpLaunchFailed: 'frpc failed to start.', frpTimeout: 'The public endpoint did not become ready. Check frps, Caddy, DNS, and firewall.', frpDiscoveryMismatch: 'The public domain points to another DSH computer. Check the Caddy and frps mappings.', frpDiscoveryInvalid: 'The public domain did not return a valid DSH Mobile discovery response.', frpStopped: 'The FRP connection stopped.', frpExited: 'frpc exited unexpectedly. Check the VPS configuration and reconnect.',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n selfHostedConnections: 'Connessione autogestita', advanced: 'Avanzato', frpName: 'FRP autogestito', frpDescription: 'Canale FRP limitato al solo gateway DSH. VPS e dominio pubblico restano sotto il tuo controllo.',\n prepareFrp: 'Configura FRP autogestito', frpStep1Title: '1 · Dati di connessione', frpStep1Text: 'Inserisci indirizzo VPS, porta di controllo frps, token condiviso e origine HTTPS pubblica.', frpServerAddress: 'Indirizzo VPS', frpServerAddressPlaceholder: 'frp.example.com', frpServerPort: 'Porta frps', frpToken: 'Token condiviso', frpTokenPlaceholder: 'Almeno 16 caratteri', frpPublicOrigin: 'Origine HTTPS pubblica', frpPublicOriginPlaceholder: 'https://dsh.example.com',\n frpStep2Title: '2 · Prepara il VPS', frpStep2Text: 'Copia il modello limitato per frps e Caddy e applicalo sul VPS.', copyServerTemplate: 'Copia modello server', templateCopied: 'Modello server copiato. Salva frps.toml e lo snippet Caddy sul VPS, poi aggiungi la riga import al Caddyfile.', templateCopyFailed: 'Impossibile copiare il modello. Controlla i quattro campi sopra.', vpsDeployText: 'Usa una chiave OpenSSH o un agent esistente. Il VPS deve essere Ubuntu/Debian con systemd; una configurazione Caddy non vuota non viene sovrascritta.', vpsDeployChangesTitle: 'Il deploy automatico modifica quanto segue sul VPS (il modello manuale da solo non cambia nulla):', vpsDeployChangePackages: 'Installa Caddy dal repository APT ufficiale, più un venv Python e Certbot per i certificati IPv4 pubblici.', vpsDeployChangeServices: 'Crea l’utente di sistema dsh-mobile (solo se assente, altrimenti mantenuto), la configurazione e il servizio frps, lo snippet Caddy più una riga import, e un timer giornaliero di rinnovo certificati.', vpsDeployChangeFirewall: 'Apre la porta di controllo FRP, 80/tcp e 443/tcp in UFW quando attivo; gli altri firewall restano a tuo carico.', vpsDeployChangeManual: 'Il deploy manuale applica gli stessi passi a mano dal modello copiato; quello automatico li esegue via SSH dopo la conferma delle chiavi host.', vpsSshUser: 'Utente SSH', vpsSshPort: 'Porta SSH', vpsSshKey: 'Percorso chiave privata (opzionale)', vpsSshKeyPlaceholder: 'Usa ssh-agent o ~/.ssh/config se vuoto', vpsDeploy: 'Installa frps + Caddy', vpsDeploying: 'Deploy sul VPS in corso. Mantieni DSH attivo…', vpsDeploySuccess: 'Deploy VPS completato. Avvia FRP per verificare l’endpoint pubblico.', vpsDeployFailed: 'Deploy VPS fallito: {error}', vpsDeployNotReady: 'Salva le impostazioni FRP prima del deploy VPS.', vpsHostKeyFetching: 'Lettura delle chiavi host del VPS…', vpsHostKeyFailed: 'Impossibile leggere le chiavi host del VPS: {error}', vpsDeployConfirmWithKeys: 'Installare frps e Caddy DSH Mobile su questo VPS? Verranno creati solo file di servizio/configurazione e regole firewall DSH Mobile. Una configurazione Caddy esistente non vuota interromperà il deploy.\\n\\nIl server presenta queste chiavi host. Verifica che corrispondano alla console del VPS prima di continuare:\\n{fingerprints}', vpsCopyUninstall: 'Copia script di disinstallazione VPS', vpsUninstallScriptCopied: 'Script di disinstallazione copiato. Controllalo prima di eseguirlo come root sul VPS.', vpsUninstallScriptFailed: 'Impossibile creare lo script di disinstallazione: {error}', vpsUninstall: 'Rimuovi DSH Mobile dal VPS', vpsUninstallConfirmWithKeys: 'Rimuovere tutti i servizi, le configurazioni, i certificati e le regole firewall di DSH Mobile da questo VPS? I tuoi contenuti Caddy e le altre regole restano invariati.\\n\\nIl server presenta queste chiavi host. Verifica che corrispondano alla console del VPS prima di continuare:\\n{fingerprints}', vpsUninstalling: 'Rimozione di DSH Mobile dal VPS. Mantieni DSH attivo…', vpsUninstallSuccess: 'Pulizia VPS completata. Le impostazioni FRP locali restano invariate.', vpsUninstallFailed: 'Pulizia VPS fallita: {error}',\n frpStep3Title: '3 · Prepara questo computer', frpStep3Text: 'Scarica e verifica il client frpc ufficiale fissato solo su richiesta.', installFrpc: 'Installa frpc ufficiale', 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.', installingFrp: 'Installazione del componente frpc ufficiale. Mantieni DSH in esecuzione.', frpNotInstalled: 'frpc non è installato. Non viene scaricato nulla finché non scegli Installa.', frpUnsupported: 'Questa piattaforma desktop non è supportata dal programma di installazione gestito.', frpComponentReady: 'frpc ufficiale {version} verificato.',\n frpStep4Title: '4 · Verifica e connetti', frpStep4Text: 'Il plugin avvia solo il vhost HTTP DSH e verifica che l’endpoint pubblico appartenga a questo computer.', 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.', frpSaveConnect: 'Salva e verifica connessione', frpSavingConnecting: 'Salvataggio privato e verifica del percorso FRP completo…', frpConfigurationReady: 'Impostazioni server salvate. Il token resta solo nella directory privata del plugin.', frpConfigurationMissing: 'Completa i campi, copia il modello VPS, quindi salva e connetti.', frpInputInvalid: 'Controlla indirizzo VPS, porta, origine HTTPS e token.',\n frpComponentDetails: 'Origine FRP e rimozione completa', frpComponentDetailsText: 'Il client fissato viene scaricato dalla release ufficiale fatedier/frp. DSH Mobile non installa servizi e non accetta configurazioni FRP arbitrarie.', frpOfficialRelease: 'Release FRP ufficiale', purgeFrp: 'Rimuovi componente FRP e configurazione privata', purgeFrpConfirm: 'Arrestare FRP e rimuovere frpc, token, configurazione, file temporanei e log gestiti da DSH Mobile? Il VPS non viene modificato.', purgingFrp: 'Arresto FRP e rimozione dei file gestiti da DSH Mobile…',\n switchingFrp: 'Passaggio a FRP autogestito…', reconnectingFrp: 'Verifica e riconnessione FRP autogestito…', resetFrpConfirm: 'Disattivare FRP autogestito e rimuovere tutti i dispositivi remoti? Impostazioni VPS e server restano invariati.', remoteUnavailableFrp: 'frpc non è installato o le impostazioni private sono incomplete.', remoteStartingFrp: 'Controllo VPS e preparazione del canale FRP limitato…', remoteConnectingFrp: 'frpc è in esecuzione; verifica dell’endpoint HTTPS pubblico…', frpMissing: 'Installa prima il componente frpc ufficiale.', frpInvalid: 'Verifica frpc non riuscita. Rimuovilo e reinstallalo.', frpConfigMissing: 'Salva prima i dati della connessione FRP.', frpConfigVerifyFailed: 'frpc ha rifiutato la configurazione generata. Controlla server e token.', frpVhostPublic: 'Il vhost HTTP in chiaro è raggiungibile pubblicamente. Limitalo a 127.0.0.1 e usa Caddy per HTTPS.', frpVhostProbeFailed: 'Impossibile controllare il VPS. Verifica il DNS e riprova.', frpLaunchFailed: 'Avvio di frpc non riuscito.', frpTimeout: 'Endpoint pubblico non pronto. Controlla frps, Caddy, DNS e firewall.', frpDiscoveryMismatch: 'Il dominio pubblico punta a un altro computer DSH. Controlla Caddy e frps.', frpDiscoveryInvalid: 'Il dominio pubblico non ha restituito una risposta DSH Mobile valida.', frpStopped: 'Connessione FRP arrestata.', frpExited: 'frpc è terminato inaspettatamente. Controlla il VPS e riconnettiti.',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n selfHostedConnections: '自建连接', advanced: '高级', frpName: '自建 FRP', frpDescription: '只为当前 DSH 网关建立单用途 FRP 通道,VPS 和公开入口由你掌控。',\n prepareFrp: '配置自建 FRP', frpStep1Title: '1 · 连接信息', frpStep1Text: '填写中转服务器地址、frps 控制端口、共享 Token 和手机访问地址。没有域名时,可直接使用公网 IPv4。', frpServerAddress: '中转服务器地址', frpServerAddressPlaceholder: '203.0.113.10', frpServerPort: 'frps 控制端口', frpToken: '共享 Token', frpTokenPlaceholder: '已保存时可留空;新建至少 16 个字符', frpPublicOrigin: '手机访问地址(HTTPS)', frpPublicOriginPlaceholder: 'https://203.0.113.10',\n frpStep2Title: '2 · 部署中转服务器', frpStep2Text: '推荐通过 SSH 一键部署。需要手动操作时,也可以复制受限的 frps 与 Caddy 配置模板。', copyServerTemplate: '复制手动部署模板', templateCopied: '服务器模板已复制,请在 VPS 上保存 frps.toml 和 Caddy 片段,并在 Caddyfile 里加一行 import。', templateCopyFailed: '无法复制模板,请检查上方连接信息。', vpsDeployText: '填写 SSH 登录信息。服务器需为带 systemd 的 Ubuntu/Debian;不会覆盖已有的 Caddy 站点配置。', vpsDeployChangesTitle: '自动部署会在 VPS 上做这些改动(只复制手动模板不会改变任何东西):', vpsDeployChangePackages: '从官方 APT 源安装 Caddy,并安装 Python venv 与 Certbot(公网 IP 证书用)。', vpsDeployChangeServices: '创建 dsh-mobile 系统用户(如已存在则复用,卸载时保留)、frps 配置与服务、Caddy 片段加一行 import,以及每天自动续证书的定时器。', vpsDeployChangeFirewall: 'UFW 开启时放行 FRP 控制端口、80/tcp 和 443/tcp;其他防火墙需要你自己放行。', vpsDeployChangeManual: '手动部署是照着复制的模板一步步自己操作;自动部署是在你确认主机指纹后通过 SSH 执行同样的步骤。', vpsSshUser: 'SSH 用户', vpsSshPort: 'SSH 端口', vpsSshKey: '私钥文件路径(可选)', vpsSshKeyPlaceholder: '留空则使用 ssh-agent 或 SSH 配置', vpsDeploy: '一键部署 frps + Caddy', vpsDeploying: '正在部署中转服务器,请保持 DSH 运行…', vpsDeploySuccess: '中转服务器部署完成,请继续完成本机组件安装和连接验证。', vpsDeployFailed: 'VPS 部署失败:{error}', vpsDeployNotReady: '请先填写完整的连接信息。', vpsHostKeyFetching: '正在读取中转服务器的主机指纹…', vpsHostKeyFailed: '读取中转服务器主机指纹失败:{error}', vpsDeployConfirmWithKeys: '要在这台 VPS 上部署 DSH Mobile 的 frps 和 Caddy 吗?只会创建 DSH Mobile 自己的服务、配置和防火墙规则;如果已有非空 Caddy 配置,部署会停止并保留原配置。\\n\\n服务器当前的主机指纹如下,请先到 VPS 控制台核对一致后再继续:\\n{fingerprints}', vpsCopyUninstall: '复制 VPS 卸载脚本', vpsUninstallScriptCopied: '卸载脚本已复制,请审阅后再以 root 身份在 VPS 上执行。', vpsUninstallScriptFailed: '生成卸载脚本失败:{error}', vpsUninstall: '清理 VPS 上的 DSH Mobile', vpsUninstallConfirmWithKeys: '要删除这台 VPS 上的 DSH Mobile 服务、配置、证书和专属防火墙规则吗?你自己的 Caddy 内容和其他规则会保留。\\n\\n服务器当前的主机指纹如下,请先到 VPS 控制台核对一致后再继续:\\n{fingerprints}', vpsUninstalling: '正在清理 VPS 上的 DSH Mobile,请保持 DSH 运行…', vpsUninstallSuccess: 'VPS 清理完成,本机 FRP 设置保持不变。', vpsUninstallFailed: 'VPS 清理失败:{error}',\n frpStep3Title: '3 · 安装本机组件', frpStep3Text: '下载并校验固定版本的官方 frpc,只安装到 DSH Mobile 私有目录。', installFrpc: '安装官方 frpc', frpInstallConfirm: '从 FRP 官方 Release 下载并校验 frpc 0.70.1(约 {size} MB),只提取 frpc 到 DSH Mobile 私有目录?不会安装服务、写入 PATH 或设置开机启动。', installingFrp: '正在安装官方 frpc,完成前请保持 DSH 运行。', frpNotInstalled: 'frpc 尚未安装;点击下方按钮后才会开始下载。', frpUnsupported: '当前桌面平台暂不支持托管安装 frpc。', frpComponentReady: '官方 frpc {version} 已安装并通过校验。',\n frpStep4Title: '4 · 启用并验证', frpStep4Text: '保存配置、启动受限的 FRP 通道,并确认手机访问地址确实连接到这台电脑。', frpAppRequirement: '通过自定义域名或公网 IP 连接,需要 Android App 0.3.3 或更高版本。局域网、cpolar 和 Tailscale 不受影响。', frpSaveConnect: '保存配置并验证连接', frpSavingConnecting: '正在保存私有配置并验证完整 FRP 链路…', frpConfigurationReady: '连接配置已安全保存', frpConfigurationMissing: '请按顺序完成连接信息、服务器部署和本机组件安装。', frpInputInvalid: '请检查服务器地址、控制端口、手机访问地址和共享 Token。',\n frpComponentDetails: 'FRP 来源与彻底清理', frpComponentDetailsText: '固定版本客户端来自 fatedier/frp 官方 Release。DSH Mobile 不安装系统服务,也不接受任意 FRP 配置。', frpOfficialRelease: 'FRP 官方 Release', purgeFrp: '彻底移除 FRP 组件与私有配置', purgeFrpConfirm: '关闭 FRP 并移除 DSH Mobile 管理的 frpc、共享 Token、生成配置、临时文件和日志?不会修改 VPS。', purgingFrp: '正在关闭 FRP 并清理 DSH Mobile 管理的文件…',\n switchingFrp: '正在切换到自建 FRP…', reconnectingFrp: '正在验证并重新连接自建 FRP…', resetFrpConfirm: '关闭自建 FRP 并移除所有远程配对设备?已保存的 VPS 配置和服务器不会改变。', remoteUnavailableFrp: 'frpc 尚未安装或私有服务器配置不完整。', remoteStartingFrp: '正在检查 VPS 并准备受限 FRP 通道…', remoteConnectingFrp: 'frpc 已启动,正在验证公网 HTTPS 端点…', frpMissing: '请先安装官方 frpc。', frpInvalid: 'frpc 校验失败,请彻底清理后重新安装。', frpConfigMissing: '请先保存自建 FRP 连接信息。', frpConfigVerifyFailed: 'frpc 拒绝了生成的配置,请检查服务器信息和 Token。', frpVhostPublic: 'VPS 明文 HTTP vhost 可被公网访问。请将其限制到 127.0.0.1,并由 Caddy 提供 HTTPS。', frpVhostProbeFailed: '无法检查 VPS 地址,请确认 DNS 后重试。', frpLaunchFailed: 'frpc 未能启动。', frpTimeout: '公网端点未能就绪,请检查 frps、Caddy、DNS 和防火墙。', frpDiscoveryMismatch: '公开域名连接到了另一台 DSH 电脑,请检查 Caddy 与 frps 映射。', frpDiscoveryInvalid: '公开域名没有返回有效的 DSH Mobile 发现信息。', frpStopped: 'FRP 连接已停止。', frpExited: 'frpc 意外退出,请检查 VPS 配置后重新连接。',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n requestTimeout: 'The operation timed out. Confirm that DSH is still running, then try again.',\n diagnostics: 'Diagnostics', openDiagnostics: 'Open connection diagnostics', back: 'Back', backToMobile: 'Back to Mobile access', connectionDiagnostics: 'Connection diagnostics',\n diagnosticsIntro: 'Check versions, gateway, network adapter, firewall, and remote channel. The report is automatically redacted and never reads conversations or credentials.',\n diagnosticsNotRun: 'Not checked yet', diagnosticsStartHint: 'Select the button below to begin.', diagnosticsIdleMeta: 'Waiting to run · connection status only', diagnosticsStart: 'Start check', diagnosticsCopy: 'Copy redacted report', diagnosticsAdvanced: 'Advanced diagnostic details',\n diagnosticsComplete: 'Check complete', diagnosticsAttention: 'Some items need attention', diagnosticsProblem: 'Connection problems found', diagnosticsCompleteFallback: 'The check is complete.',\n diagnosticStatusOk: 'OK', diagnosticStatusWarning: 'Attention', diagnosticStatusError: 'Problem', diagnosticStatusInfo: 'Info', diagnosticItems: '{count} items', diagnosticCheck: 'Check', diagnosticAction: 'Suggested action',\n diagnosticNeedsAction: 'Needs action', diagnosticDetails: 'Check details', diagnosticOther: 'Other checks', diagnosticNoBlockers: '{count} checks · no blocking issues found', diagnosticNeedsCount: '{count} checks · {issues} need attention',\n diagnosticsChecking: 'Checking…', diagnosticsCheckingTitle: 'Checking connection', diagnosticsCheckingText: 'This usually takes a few seconds. The remote channel performs a real reachability test.', diagnosticsRunningMeta: 'Running · keep DSH online',\n diagnosticsIncomplete: 'Check not completed', diagnosticsReadFailed: 'Could not read diagnostics: {error}', diagnosticsUnavailable: 'Diagnostics service unavailable · try again later', diagnosticsRetry: 'Check again', diagnosticsCopied: 'Redacted report copied. You can paste it directly into an issue.', diagnosticsCopyManual: 'Clipboard access was denied. Details are expanded for manual copying.',\n diagnosticLabelVersions: 'Version compatibility', diagnosticLabelNetwork: 'Local network adapter', diagnosticLabelLan: 'Local network gateway', diagnosticLabelFirewall: 'Windows Firewall', diagnosticLabelRemote: 'Remote channel', diagnosticLabelPhone: 'Phone network',\n funnelTimeout: 'The Tailscale component timed out while starting. Check the network, then reconnect.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n requestTimeout: 'Operazione scaduta. Verifica che DSH sia ancora in esecuzione e riprova.',\n diagnostics: 'Diagnostica', openDiagnostics: 'Apri diagnostica connessione', back: 'Indietro', backToMobile: 'Torna ad Accesso mobile', connectionDiagnostics: 'Diagnostica connessione',\n diagnosticsIntro: 'Controlla versioni, gateway, scheda di rete, firewall e canale remoto. Il report viene anonimizzato automaticamente e non legge conversazioni o credenziali.',\n diagnosticsNotRun: 'Controllo non eseguito', diagnosticsStartHint: 'Premi il pulsante sotto per iniziare.', diagnosticsIdleMeta: 'In attesa · solo stato connessione', diagnosticsStart: 'Avvia controllo', diagnosticsCopy: 'Copia report anonimizzato', diagnosticsAdvanced: 'Dettagli diagnostici avanzati',\n diagnosticsComplete: 'Controllo completato', diagnosticsAttention: 'Alcuni elementi richiedono attenzione', diagnosticsProblem: 'Rilevati problemi di connessione', diagnosticsCompleteFallback: 'Controllo completato.',\n diagnosticStatusOk: 'OK', diagnosticStatusWarning: 'Attenzione', diagnosticStatusError: 'Problema', diagnosticStatusInfo: 'Informazione', diagnosticItems: '{count} elementi', diagnosticCheck: 'Controllo', diagnosticAction: 'Suggerimento',\n diagnosticNeedsAction: 'Da risolvere', diagnosticDetails: 'Dettagli controllo', diagnosticOther: 'Altri controlli', diagnosticNoBlockers: '{count} controlli · nessun problema bloccante', diagnosticNeedsCount: '{count} controlli · {issues} richiedono attenzione',\n diagnosticsChecking: 'Controllo…', diagnosticsCheckingTitle: 'Controllo connessione', diagnosticsCheckingText: 'Di solito richiede pochi secondi. Il canale remoto esegue un test reale di raggiungibilità.', diagnosticsRunningMeta: 'In esecuzione · mantieni DSH online',\n diagnosticsIncomplete: 'Controllo non completato', diagnosticsReadFailed: 'Impossibile leggere la diagnostica: {error}', diagnosticsUnavailable: 'Servizio diagnostico non disponibile · riprova più tardi', diagnosticsRetry: 'Ripeti controllo', diagnosticsCopied: 'Report anonimizzato copiato. Puoi incollarlo direttamente in una issue.', diagnosticsCopyManual: 'Il browser ha negato la copia. I dettagli sono stati aperti per la copia manuale.',\n diagnosticLabelVersions: 'Compatibilità versioni', diagnosticLabelNetwork: 'Scheda rete locale', diagnosticLabelLan: 'Gateway rete locale', diagnosticLabelFirewall: 'Windows Firewall', diagnosticLabelRemote: 'Canale remoto', diagnosticLabelPhone: 'Rete telefono',\n funnelTimeout: 'Avvio del componente Tailscale scaduto. Controlla la rete e riconnettiti.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n requestTimeout: '操作超时,请确认 DSH 仍在运行后重试。',\n diagnostics: '诊断', openDiagnostics: '打开连接诊断', back: '返回', backToMobile: '返回移动访问', connectionDiagnostics: '连接诊断',\n diagnosticsIntro: '检查版本、网关、网卡、防火墙和远程通道。报告自动脱敏,不读取对话或凭据。', diagnosticsNotRun: '尚未检查', diagnosticsStartHint: '点击下方按钮开始。', diagnosticsIdleMeta: '等待运行 · 仅收集连接状态', diagnosticsStart: '开始检查', diagnosticsCopy: '复制脱敏报告', diagnosticsAdvanced: '高级诊断详情',\n diagnosticsComplete: '检查完成', diagnosticsAttention: '有项目需要留意', diagnosticsProblem: '发现连接问题', diagnosticsCompleteFallback: '检查已完成。', diagnosticStatusOk: '正常', diagnosticStatusWarning: '注意', diagnosticStatusError: '问题', diagnosticStatusInfo: '说明', diagnosticItems: '{count} 项', diagnosticCheck: '检查项', diagnosticAction: '建议', diagnosticNeedsAction: '需要处理', diagnosticDetails: '检查详情', diagnosticOther: '其他检查', diagnosticNoBlockers: '{count} 项检查 · 未发现阻断问题', diagnosticNeedsCount: '{count} 项检查 · {issues} 项需要处理',\n diagnosticsChecking: '正在检查…', diagnosticsCheckingTitle: '正在检查连接', diagnosticsCheckingText: '通常几秒内完成。远程通道会执行一次真实可达性测试。', diagnosticsRunningMeta: '正在运行 · 请保持 DSH 在线', diagnosticsIncomplete: '检查未完成', diagnosticsReadFailed: '无法读取诊断结果:{error}', diagnosticsUnavailable: '诊断服务暂不可用 · 请稍后重试', diagnosticsRetry: '重新检查', diagnosticsCopied: '脱敏报告已复制,可直接粘贴到 Issue。', diagnosticsCopyManual: '浏览器未允许复制,已展开详情,请手动复制。',\n diagnosticLabelVersions: '版本兼容', diagnosticLabelNetwork: '局域网网卡', diagnosticLabelLan: '局域网网关', diagnosticLabelFirewall: 'Windows 防火墙', diagnosticLabelRemote: '远程通道', diagnosticLabelPhone: '手机网络', funnelTimeout: 'Tailscale 组件启动超时,请检查网络后重新连接。',\n})\n\nexport const LOCALIZED_DIAGNOSTIC_COPY = {\n en: {\n versions: 'Installed plugin, DSH, and minimum Android app versions are shown.',\n networkOk: 'The configured local network adapter is available.', networkError: 'The saved local network adapter is unavailable.', networkInfo: 'A fixed local network configuration is in use.', networkAction: 'Run dsh-mobile setup again.',\n lanOk: 'The local gateway is listening and pairing is available.', lanInfo: 'Local network access is currently off.', lanAction: 'Enable local network access when the phone must connect directly.',\n firewallOk: 'Local TCP and discovery firewall rules are enabled.', firewallWarning: 'The complete local network firewall rules were not found.', firewallInfo: 'The system did not allow the plugin to read firewall status.', firewallAction: 'Run dsh-mobile setup as administrator and check the firewall rules.',\n remoteOk: 'The remote public address passed the reachability check.', remoteWarning: 'The remote channel needs attention or is still connecting.', remoteError: 'The remote connection is not currently reachable.', remoteInfo: 'Remote access is currently off.', remoteAction: 'Return to Remote access, follow the provider guidance, and reconnect.',\n phone: 'The computer cannot determine whether the router isolates the phone.', phoneAction: 'Confirm the phone and computer use the same network, then disable guest-network or AP isolation.',\n reportTitle: 'DSH Mobile diagnostic report', generated: 'Generated',\n },\n it: {\n versions: 'Sono indicate le versioni installate di plugin e DSH e la versione minima dell’app Android.',\n networkOk: 'La scheda di rete locale configurata è disponibile.', networkError: 'La scheda di rete locale salvata non è disponibile.', networkInfo: 'È in uso una configurazione di rete locale fissa.', networkAction: 'Esegui di nuovo dsh-mobile setup.',\n lanOk: 'Il gateway locale è in ascolto e l’abbinamento è disponibile.', lanInfo: 'L’accesso dalla rete locale è disattivato.', lanAction: 'Attiva l’accesso locale quando il telefono deve collegarsi direttamente.',\n firewallOk: 'Le regole firewall TCP locale e di rilevamento sono attive.', firewallWarning: 'Le regole firewall complete per la rete locale non sono state trovate.', firewallInfo: 'Il sistema non ha consentito al plugin di leggere lo stato del firewall.', firewallAction: 'Esegui dsh-mobile setup come amministratore e controlla le regole firewall.',\n remoteOk: 'L’indirizzo pubblico remoto ha superato il test di raggiungibilità.', remoteWarning: 'Il canale remoto richiede attenzione o è ancora in connessione.', remoteError: 'La connessione remota non è al momento raggiungibile.', remoteInfo: 'L’accesso remoto è disattivato.', remoteAction: 'Torna ad Accesso remoto, segui le indicazioni del provider e riconnettiti.',\n phone: 'Il computer non può stabilire se il router isola il telefono.', phoneAction: 'Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP.',\n reportTitle: 'Report diagnostico DSH Mobile', generated: 'Generato',\n },\n zh: {\n versions: '已显示插件、DSH 和 Android App 最低版本。',\n networkOk: '已配置的局域网网卡可用。', networkError: '已保存的局域网网卡不可用。', networkInfo: '当前使用固定局域网配置。', networkAction: '重新运行 dsh-mobile setup。',\n lanOk: '局域网网关正在监听,配对入口可用。', lanInfo: '局域网访问当前未开启。', lanAction: '需要手机直连时开启局域网访问。',\n firewallOk: '局域网 TCP 与发现防火墙规则已启用。', firewallWarning: '未找到完整的局域网防火墙规则。', firewallInfo: '系统未允许插件读取防火墙状态。', firewallAction: '以管理员身份运行 dsh-mobile setup 并检查防火墙规则。',\n remoteOk: '远程公网地址已通过可达性检查。', remoteWarning: '远程通道需要处理或仍在连接。', remoteError: '远程连接当前不可达。', remoteInfo: '远程访问当前未开启。', remoteAction: '返回远程访问,按提供方提示操作并重新连接。',\n phone: '电脑无法判断路由器是否隔离了手机。', phoneAction: '确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。',\n reportTitle: 'DSH Mobile 诊断报告', generated: '生成时间',\n },\n} satisfies Record<MobileControlLocale, {\n versions: string\n networkOk: string\n networkError: string\n networkInfo: string\n networkAction: string\n lanOk: string\n lanInfo: string\n lanAction: string\n firewallOk: string\n firewallWarning: string\n firewallInfo: string\n firewallAction: string\n remoteOk: string\n remoteWarning: string\n remoteError: string\n remoteInfo: string\n remoteAction: string\n phone: string\n phoneAction: string\n reportTitle: string\n generated: string\n}>\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n selfHostedDescription: \"Use your own VPS or HTTPS reverse proxy without a public tunnel service.\",\n originName: \"Own reverse proxy\",\n originDescription: \"You manage HTTPS. DSH Mobile provides a private, authenticated HTTP backend; no tunnel component is installed.\",\n prepareOrigin: \"Connect your HTTPS reverse proxy\",\n originPublicOrigin: \"Public HTTPS address\",\n originListenHost: \"Private listen IPv4\",\n originListenPort: \"HTTP backend port\",\n originAllowedCidrs: \"Allowed proxy source CIDRs\",\n originHelp: \"Same computer: use 127.0.0.1. For a proxy on another LAN device, use this computer’s private IPv4 and allow only the proxy’s direct source IPv4/32. Separate CIDRs with spaces or commas.\",\n originWarning: \"Keep this HTTP backend private. Never expose or port-forward it to the Internet. Your proxy must terminate HTTPS and preserve the external Host, including its port, and WebSocket upgrades.\",\n originSaveStart: \"Save and start backend\",\n originBackendSaved: \"Saved HTTP backend (not running)\",\n originBackendListening: \"Listening HTTP backend\",\n originCopyBackend: \"Copy backend address\",\n originBackendCopied: \"HTTP backend address copied.\",\n originPurge: \"Clear proxy settings\",\n originPurgeConfirm: \"Stop this private HTTP backend and delete only its reverse-proxy settings? Paired remote devices, LAN access, and other providers are kept.\",\n originPurged: \"The backend is stopped and its proxy settings are cleared. Paired devices are kept.\",\n originSaving: \"Saving settings and starting the private backend…\",\n originPurging: \"Stopping the backend and clearing its settings…\",\n originBackendReady: \"Backend listening\",\n originReady: \"The private HTTP backend is listening. Public HTTPS, its certificate, and WebSocket connectivity have NOT been verified. Test the public address from your phone before relying on it.\",\n originUnavailable: \"Configure the public HTTPS address and private backend first.\",\n originStarting: \"Starting the private HTTP backend…\",\n switchingOrigin: \"Switching to your own reverse proxy…\",\n reconnectingOrigin: \"Restarting the private HTTP backend…\",\n resetOriginConfirm: \"Stop this backend and reset remote pairing? All paired remote devices must pair again. Reverse-proxy settings and LAN devices are kept.\",\n originPublicOriginInvalid: \"Enter a public HTTPS origin only, such as https://phone.example.com:8815; no path, query, credentials, or private address.\",\n originListenHostInvalid: \"Use an explicit loopback or private IPv4 address on this computer, not 0.0.0.0 or a public address.\",\n originListenPortInvalid: \"Use a whole-number port from 1024 to 65535; lower ports need privileges the plugin does not have.\",\n originListenPortReserved: \"Port 3443 belongs to the LAN gateway and port 3080 to DSH itself, so both are always taken. Choose another port, such as 3444 or 3445.\",\n originAllowedCidrsInvalid: \"Allow 1–16 valid loopback/private IPv4 CIDRs with no host bits. For a LAN listener, include the actual proxy source IPv4/32; public ranges are not accepted.\",\n originSettingsInvalid: \"The reverse-proxy settings are invalid. Check the four fields and save again.\",\n originConfigMissing: \"Save the reverse-proxy settings before starting the backend.\",\n originConfigInvalid: \"The saved settings are invalid. Correct and save them again, or clear only these settings.\",\n originPortInUse: \"The backend port is already in use. Stop the conflicting listener or choose another port. Note that 3443, 3080, and a port already used by a cloudflared tunnel are never free.\",\n originAddressUnavailable: \"The listen address is not available on this computer. Choose its current private IPv4.\",\n originStartFailed: \"The private backend could not start. Check its address, port, permissions, and local logs.\",\n originConfigTargetInvalid: \"The saved proxy settings file is not a regular file. Remove it and save the settings again.\",\n originControllerUnavailable: \"The reverse-proxy provider is not available in this session. Restart DSH and try again.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n selfHostedDescription: \"Usa il tuo VPS o reverse proxy HTTPS senza un servizio di tunnel pubblico.\",\n originName: \"Reverse proxy proprio\",\n originDescription: \"Gestisci tu HTTPS. DSH Mobile offre un backend HTTP privato con autenticazione; non installa componenti tunnel.\",\n prepareOrigin: \"Collega il tuo reverse proxy HTTPS\",\n originPublicOrigin: \"Indirizzo HTTPS pubblico\",\n originListenHost: \"IPv4 privato di ascolto\",\n originListenPort: \"Porta backend HTTP\",\n originAllowedCidrs: \"CIDR sorgente del proxy consentiti\",\n originHelp: \"Stesso computer: usa 127.0.0.1. Per un proxy su un altro dispositivo LAN, usa l’IPv4 privato di questo computer e consenti solo l’IPv4 sorgente diretta del proxy con /32. Separa i CIDR con spazi o virgole.\",\n originWarning: \"Mantieni privato il backend HTTP. Non esporlo né inoltrarne la porta su Internet. Il proxy deve terminare HTTPS e conservare l’Host esterno, inclusa la porta, e gli upgrade WebSocket.\",\n originSaveStart: \"Salva e avvia backend\",\n originBackendSaved: \"Backend HTTP salvato (non attivo)\",\n originBackendListening: \"Backend HTTP in ascolto\",\n originCopyBackend: \"Copia indirizzo backend\",\n originBackendCopied: \"Indirizzo backend HTTP copiato.\",\n originPurge: \"Cancella impostazioni proxy\",\n originPurgeConfirm: \"Fermare questo backend HTTP privato ed eliminare solo le sue impostazioni reverse proxy? Dispositivi remoti abbinati, accesso LAN e altri provider vengono conservati.\",\n originPurged: \"Backend fermato e impostazioni proxy cancellate. I dispositivi abbinati sono conservati.\",\n originSaving: \"Salvataggio impostazioni e avvio del backend privato…\",\n originPurging: \"Arresto del backend e cancellazione impostazioni…\",\n originBackendReady: \"Backend in ascolto\",\n originReady: \"Il backend HTTP privato è in ascolto. HTTPS pubblico, certificato e connettività WebSocket NON sono stati verificati. Prova l’indirizzo pubblico dal telefono prima di usarlo.\",\n originUnavailable: \"Configura prima l’indirizzo HTTPS pubblico e il backend privato.\",\n originStarting: \"Avvio del backend HTTP privato…\",\n switchingOrigin: \"Passaggio al reverse proxy proprio…\",\n reconnectingOrigin: \"Riavvio del backend HTTP privato…\",\n resetOriginConfirm: \"Fermare il backend e reimpostare gli abbinamenti remoti? Tutti i dispositivi remoti dovranno essere abbinati di nuovo. Le impostazioni proxy e i dispositivi LAN vengono conservati.\",\n originPublicOriginInvalid: \"Inserisci solo un’origine HTTPS pubblica, ad esempio https://phone.example.com:8815, senza percorso, query, credenziali o indirizzo privato.\",\n originListenHostInvalid: \"Usa un IPv4 loopback o privato esplicito di questo computer, non 0.0.0.0 né un indirizzo pubblico.\",\n originListenPortInvalid: \"Usa una porta intera da 1024 a 65535; le porte più basse richiedono privilegi che il plugin non ha.\",\n originListenPortReserved: \"La porta 3443 appartiene al gateway LAN e la 3080 a DSH stesso, quindi sono sempre occupate. Scegli un’altra porta, ad esempio 3444 o 3445.\",\n originAllowedCidrsInvalid: \"Consenti 1–16 CIDR IPv4 loopback/privati validi senza bit host. Per l’ascolto LAN includi l’IPv4 sorgente reale del proxy con /32; le reti pubbliche non sono ammesse.\",\n originSettingsInvalid: \"Impostazioni reverse proxy non valide. Controlla i quattro campi e salva di nuovo.\",\n originConfigMissing: \"Salva le impostazioni reverse proxy prima di avviare il backend.\",\n originConfigInvalid: \"Impostazioni salvate non valide. Correggile e salvale di nuovo, oppure cancella solo queste impostazioni.\",\n originPortInUse: \"La porta backend è già occupata. Ferma il processo in conflitto o scegli un’altra porta. 3443, 3080 e una porta già usata da un tunnel cloudflared non sono mai libere.\",\n originAddressUnavailable: \"L’indirizzo di ascolto non è disponibile su questo computer. Scegli il suo IPv4 privato attuale.\",\n originStartFailed: \"Impossibile avviare il backend privato. Controlla indirizzo, porta, permessi e log locali.\",\n originConfigTargetInvalid: \"Il file delle impostazioni proxy salvato non è un file regolare. Rimuovilo e salva di nuovo le impostazioni.\",\n originControllerUnavailable: \"Il provider reverse proxy non è disponibile in questa sessione. Riavvia DSH e riprova.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n selfHostedDescription: \"使用自己的 VPS 或 HTTPS 反向代理,不依赖公共隧道服务。\",\n originName: \"自有反向代理\",\n originDescription: \"HTTPS 由你管理,插件提供带认证的私有 HTTP 后端,无需安装隧道组件。\",\n prepareOrigin: \"接入自有 HTTPS 反向代理\",\n originPublicOrigin: \"公网 HTTPS 地址\",\n originListenHost: \"私有监听 IPv4\",\n originListenPort: \"HTTP 后端端口\",\n originAllowedCidrs: \"允许的代理来源 CIDR\",\n originHelp: \"代理与 DSH 同机时用 127.0.0.1;代理在另一台局域网设备上时,填写本机私有 IPv4,并只允许代理实际直连来源 IPv4/32。多个 CIDR 用空格或逗号分隔。\",\n originWarning: \"HTTP 后端必须留在私网,绝不能直接暴露公网或做公网端口映射。反向代理负责 HTTPS,并须保留外部 Host(含端口)及 WebSocket 升级。\",\n originSaveStart: \"保存并启动后端\",\n originBackendSaved: \"已保存的 HTTP 后端(未运行)\",\n originBackendListening: \"正在监听的 HTTP 后端\",\n originCopyBackend: \"复制后端地址\",\n originBackendCopied: \"已复制 HTTP 后端地址。\",\n originPurge: \"清除代理配置\",\n originPurgeConfirm: \"停止此私有 HTTP 后端,并且只删除它的反向代理配置?已配对的远程设备、局域网访问和其他提供方均保留。\",\n originPurged: \"已停止后端并清除代理配置,已配对设备仍保留。\",\n originSaving: \"正在保存配置并启动私有后端…\",\n originPurging: \"正在停止后端并清除配置…\",\n originBackendReady: \"后端已监听\",\n originReady: \"私有 HTTP 后端已监听,但尚未验证公网 HTTPS、证书及 WebSocket 连通性。请先用手机测试公网地址,不代表公网连接已成功。\",\n originUnavailable: \"请先配置公网 HTTPS 地址和私有后端。\",\n originStarting: \"正在启动私有 HTTP 后端…\",\n switchingOrigin: \"正在切换到自有反向代理…\",\n reconnectingOrigin: \"正在重启私有 HTTP 后端…\",\n resetOriginConfirm: \"停止此后端并重置远程配对?所有已配对的远程设备都需要重新配对;反向代理配置和局域网设备保留。\",\n originPublicOriginInvalid: \"只填写公网 HTTPS 源地址,例如 https://phone.example.com:8815;不能含路径、查询参数、账号密码或私有地址。\",\n originListenHostInvalid: \"填写本机明确的回环或私有 IPv4,不能使用 0.0.0.0 或公网地址。\",\n originListenPortInvalid: \"端口必须是 1024–65535 的整数;更低的端口需要插件没有的特权。\",\n originListenPortReserved: \"3443 属于局域网网关、3080 属于 DSH 自身,两者始终被占用。请换一个端口,例如 3444 或 3445。\",\n originAllowedCidrsInvalid: \"填写 1–16 个规范的回环/私有 IPv4 CIDR,不能有主机位;局域网监听时须包含代理实际来源 IPv4/32,不接受公网网段。\",\n originSettingsInvalid: \"反向代理配置无效,请检查四个字段后重新保存。\",\n originConfigMissing: \"请先保存反向代理配置,再启动后端。\",\n originConfigInvalid: \"已保存的配置无效,请修正并重新保存,或仅清除这项配置。\",\n originPortInUse: \"后端端口已被占用,请停止冲突进程或改用其他端口。3443、3080 以及已被 cloudflared 隧道占用的端口永远不会空闲。\",\n originAddressUnavailable: \"本机没有这个监听地址,请选择当前本机的私有 IPv4。\",\n originStartFailed: \"私有后端启动失败,请检查地址、端口、权限和本地日志。\",\n originConfigTargetInvalid: \"已保存的代理配置文件不是普通文件。请移除后重新保存设置。\",\n originControllerUnavailable: \"当前会话里反向代理提供方不可用。请重启 DSH 后重试。\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n cloudflaredBadge: \"Account optional\",\n cloudflaredDescription: \"Installs the official cloudflared client on demand. A quick tunnel needs no account and gives a temporary public address; a named tunnel uses a Cloudflare account token and keeps one hostname that survives restarts.\",\n prepareCloudflared: \"Prepare cloudflared\",\n cloudflaredUnsupported: \"Windows x64 and Linux x64/arm64 are currently supported.\",\n cloudflaredNotInstalled: \"Not installed. A fixed version is downloaded from the official cloudflared release page only after you click the button below.\",\n cloudflaredReady: \"Official component {version} is ready.\",\n cloudflaredQuickNote: \"Quick tunnel: no Cloudflare account is needed, but Cloudflare positions quick tunnels for testing — the address is random, rate-limited, and carries no uptime guarantee. Do not rely on one for access that must stay reachable.\",\n cloudflaredNamedNote: \"Named tunnel: the hostname above stays the same across restarts and Cloudflare routes it to the local forward port. The connector token is stored only in the DSH Mobile private directory, is handed to cloudflared through its environment rather than the command line, and is never shown again once saved.\",\n cloudflaredComponentNote: \"Downloaded from the official cloudflared release page and verified at a fixed version only after you choose Install. The client is started with automatic updates disabled. It does not add a system service, startup item, registry entry, or PATH entry.\",\n installConfirmCloudflared: \"Download and verify cloudflared {size} from the official release page and store it only in the DSH Mobile private directory? No system service, PATH/registry entry, or startup item is added.\",\n installingCloudflared: \"Installing the official cloudflared component. Keep DSH running until it completes.\",\n purgeCloudflared: \"Completely remove the cloudflared component\",\n purgeCloudflaredConfirm: \"Completely remove the cloudflared component and its runtime logs from the DSH Mobile private directory? Local network access, DSH data, and other system programs are unaffected.\",\n purgingCloudflared: \"Stopping the channel and removing cloudflared files managed by DSH Mobile…\",\n switchingCloudflared: \"Switching to cloudflared…\",\n reconnectingCloudflared: \"Reconnecting cloudflared…\",\n remoteUnavailableCloudflared: \"cloudflared is not installed.\",\n remoteStartingCloudflared: \"Starting the cloudflared connector…\",\n remoteConnectingCloudflared: \"Starting the DSH authentication gateway…\",\n resetCloudflaredConfirm: \"Disable the cloudflared remote channel and remove all remote paired devices? The installed component and the local network configuration are unchanged.\",\n cloudflaredMissing: \"The official cloudflared component is not installed. Complete the preparation steps above.\",\n cloudflaredInvalid: \"cloudflared component verification failed. Remove it completely and reinstall.\",\n cloudflaredPortUnavailable: \"Could not allocate a local remote-gateway port. Try again.\",\n cloudflaredLaunchFailed: \"The cloudflared client failed to start.\",\n cloudflaredTimeout: \"Timed out waiting for the tunnel to become reachable. Reconnect.\",\n cloudflaredStopped: \"The cloudflared connection stopped.\",\n cloudflaredExited: \"The cloudflared connection exited unexpectedly. Reconnect.\",\n cloudflaredOutputInvalid: \"cloudflared returned an unrecognized status.\",\n cloudflaredOriginInvalid: \"The public address returned by cloudflared failed validation.\",\n cloudflaredComponentUnsupported: \"This build cannot run the cloudflared component.\",\n cloudflaredDownloadHashMismatch: \"The downloaded cloudflared failed its SHA-256 verification. Retry the install.\",\n cloudflaredDownloadSizeMismatch: \"The downloaded cloudflared is not the pinned size. Retry the install.\",\n cloudflaredDownloadRedirectMissing: \"The download page stopped redirecting to the release asset. Retry later, or install from the official page manually.\",\n cloudflaredDownloadRedirectInvalid: \"The download redirect did not point at a GitHub release asset, so it was refused.\",\n cloudflaredDownloadRedirectRejected: \"The download redirect was refused by policy. Retry later, or install from the official page manually.\",\n cloudflaredExecutableHashMismatch: \"The installed cloudflared no longer matches the verified build. Remove it completely and install again.\",\n cloudflaredPortReservationFailed: \"Could not reserve a local port for the tunnel. Try again.\",\n cloudflaredTunnelTargetInvalid: \"The saved tunnel configuration is not a regular file. Remove it and save the settings again.\",\n cloudflaredTunnelSaving: \"Saving the tunnel settings and reconnecting cloudflared…\",\n cloudflaredTunnelForgetting: \"Removing the saved tunnel token…\",\n cloudflaredTunnelTitle: \"Tunnel type\",\n cloudflaredTunnelQuick: \"Quick\",\n cloudflaredTunnelNamed: \"Named\",\n cloudflaredTunnelQuickHint: \"No account: cloudflared allocates a random address on every start.\",\n cloudflaredTunnelNamedHint: \"Uses a Cloudflare account token and keeps one address that survives restarts.\",\n cloudflaredTunnelHostname: \"Public hostname\",\n cloudflaredTunnelHostnamePlaceholder: \"dsh.example.com\",\n cloudflaredTunnelPort: \"Local forward port\",\n cloudflaredTunnelToken: \"Connector token\",\n cloudflaredTunnelTokenPlaceholder: \"Paste the tunnel token from the Cloudflare dashboard\",\n cloudflaredTunnelTokenSaved: \"A token is saved. Leave blank to keep it.\",\n cloudflaredTunnelSave: \"Save and connect\",\n cloudflaredTunnelSaveQuick: \"Switch to a quick tunnel\",\n cloudflaredTunnelConfigured: \"Named tunnel configured: {hostname} reaches this computer on 127.0.0.1:{port}.\",\n cloudflaredTunnelMissing: \"No named tunnel configured yet.\",\n cloudflaredTunnelHint: \"In the Cloudflare dashboard create a tunnel, then set its public hostname to http://127.0.0.1:{port}. The token is stored only in the DSH Mobile private directory and is never shown again after saving.\",\n cloudflaredTunnelForget: \"Remove the saved token\",\n cloudflaredTunnelForgetConfirm: \"Remove the saved tunnel token, stop the cloudflared channel, and go back to a quick tunnel? Nothing reconnects on its own: switch the channel back on afterwards. The tunnel still exists in your Cloudflare account, and its public hostname is unchanged.\",\n cloudflaredTunnelInvalid: \"The saved named-tunnel configuration is unreadable; the provider is using a quick tunnel. Save the settings again.\",\n cloudflaredTunnelPortUnavailable: \"The configured local forward port is unavailable. Cloudflare routes the public hostname to that exact port, so free it or choose another one.\",\n cloudflaredTunnelHostnameInvalid: \"Enter a public hostname under a domain on this Cloudflare account, for example dsh.example.com.\",\n cloudflaredTunnelPortInvalid: \"Enter a forward port between 1024 and 65535.\",\n cloudflaredTunnelPortReserved: \"Port 3443 is reserved for the DSH Mobile LAN gateway and is always occupied. Choose another port, such as 3444 or 3445.\",\n cloudflaredTunnelTokenInvalid: \"That does not look like a tunnel token. Copy the whole token from the Cloudflare dashboard.\",\n cloudflaredTunnelSettingsInvalid: \"The tunnel settings were rejected.\",\n cloudflaredTunnelConfigMissing: \"Enter the token, hostname, and port to use a named tunnel.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n cloudflaredBadge: \"Account facoltativo\",\n cloudflaredDescription: \"Installa su richiesta il client cloudflared ufficiale. Il tunnel rapido non richiede account e fornisce un indirizzo pubblico temporaneo; il tunnel con nome usa il token di un account Cloudflare e mantiene un hostname stabile tra i riavvii.\",\n prepareCloudflared: \"Prepara cloudflared\",\n cloudflaredUnsupported: \"Attualmente sono supportati Windows x64 e Linux x64/arm64.\",\n cloudflaredNotInstalled: \"Non installato. Una versione fissa viene scaricata dalla pagina ufficiale di cloudflared solo dopo aver premuto il pulsante.\",\n cloudflaredReady: \"Componente ufficiale {version} pronto.\",\n cloudflaredQuickNote: \"Tunnel rapido: non serve un account Cloudflare, ma Cloudflare li considera pensati per i test — l’indirizzo è casuale, soggetto a limiti di frequenza e senza garanzia di disponibilità. Non usarlo per un accesso che deve restare raggiungibile.\",\n cloudflaredNamedNote: \"Tunnel con nome: l’hostname qui sopra resta lo stesso tra i riavvii e Cloudflare lo instrada verso la porta di inoltro locale. Il token del connettore è salvato solo nella directory privata DSH Mobile, viene passato a cloudflared tramite l’ambiente e non tramite la riga di comando, e dopo il salvataggio non viene più mostrato.\",\n cloudflaredComponentNote: \"Viene scaricato dalla pagina ufficiale di cloudflared e verificato a una versione fissa solo dopo aver scelto Installa. Il client viene avviato con gli aggiornamenti automatici disattivati. Non aggiunge servizi di sistema, avvio automatico, voci di registro o PATH.\",\n installConfirmCloudflared: \"Scaricare e verificare cloudflared {size} dalla pagina ufficiale e salvarlo solo nella directory privata DSH Mobile? Non vengono aggiunti servizi, PATH/registro o avvio automatico.\",\n installingCloudflared: \"Installazione del componente cloudflared ufficiale. Mantieni DSH in esecuzione fino al termine.\",\n purgeCloudflared: \"Rimuovi completamente il componente cloudflared\",\n purgeCloudflaredConfirm: \"Rimuovere completamente il componente cloudflared e i log runtime dalla directory privata DSH Mobile? Rete locale, dati DSH e altri programmi non sono interessati.\",\n purgingCloudflared: \"Arresto del canale e rimozione dei file cloudflared gestiti da DSH Mobile…\",\n switchingCloudflared: \"Passaggio a cloudflared…\",\n reconnectingCloudflared: \"Riconnessione di cloudflared…\",\n remoteUnavailableCloudflared: \"cloudflared non è installato.\",\n remoteStartingCloudflared: \"Avvio del connettore cloudflared…\",\n remoteConnectingCloudflared: \"Avvio del gateway di autenticazione DSH…\",\n resetCloudflaredConfirm: \"Disattivare il canale remoto cloudflared e rimuovere tutti i dispositivi remoti? Il componente installato e la configurazione della rete locale restano invariati.\",\n cloudflaredMissing: \"Il componente cloudflared ufficiale non è installato. Completa i passaggi sopra.\",\n cloudflaredInvalid: \"Verifica del componente cloudflared non riuscita. Rimuovilo completamente e reinstallalo.\",\n cloudflaredPortUnavailable: \"Impossibile assegnare una porta locale al gateway remoto. Riprova.\",\n cloudflaredLaunchFailed: \"Il client cloudflared non è stato avviato.\",\n cloudflaredTimeout: \"Timeout in attesa che il tunnel diventi raggiungibile. Riconnettiti.\",\n cloudflaredStopped: \"La connessione cloudflared è stata arrestata.\",\n cloudflaredExited: \"La connessione cloudflared è terminata in modo imprevisto. Riconnettiti.\",\n cloudflaredOutputInvalid: \"cloudflared ha restituito uno stato non riconosciuto.\",\n cloudflaredOriginInvalid: \"L’indirizzo pubblico restituito da cloudflared non ha superato la verifica.\",\n cloudflaredComponentUnsupported: \"Questa build non può eseguire il componente cloudflared.\",\n cloudflaredDownloadHashMismatch: \"Il cloudflared scaricato non ha superato la verifica SHA-256. Riprova l’installazione.\",\n cloudflaredDownloadSizeMismatch: \"Il cloudflared scaricato non ha la dimensione prevista. Riprova l’installazione.\",\n cloudflaredDownloadRedirectMissing: \"La pagina di download non reindirizza più all’asset della release. Riprova più tardi o installa manualmente dalla pagina ufficiale.\",\n cloudflaredDownloadRedirectInvalid: \"Il reindirizzamento di download non puntava a un asset di release GitHub ed è stato rifiutato.\",\n cloudflaredDownloadRedirectRejected: \"Il reindirizzamento di download è stato rifiutato dalle regole. Riprova più tardi o installa manualmente dalla pagina ufficiale.\",\n cloudflaredExecutableHashMismatch: \"Il cloudflared installato non corrisponde più alla build verificata. Rimuovilo completamente e reinstalla.\",\n cloudflaredPortReservationFailed: \"Impossibile riservare una porta locale per il tunnel. Riprova.\",\n cloudflaredTunnelTargetInvalid: \"La configurazione salvata del tunnel non è un file regolare. Rimuovila e salva di nuovo le impostazioni.\",\n cloudflaredTunnelSaving: \"Salvataggio delle impostazioni e riconnessione di cloudflared…\",\n cloudflaredTunnelForgetting: \"Rimozione del token del tunnel salvato…\",\n cloudflaredTunnelTitle: \"Tipo di tunnel\",\n cloudflaredTunnelQuick: \"Rapido\",\n cloudflaredTunnelNamed: \"Con nome\",\n cloudflaredTunnelQuickHint: \"Senza account: cloudflared assegna un indirizzo casuale a ogni avvio.\",\n cloudflaredTunnelNamedHint: \"Usa il token di un account Cloudflare e mantiene un indirizzo stabile tra i riavvii.\",\n cloudflaredTunnelHostname: \"Hostname pubblico\",\n cloudflaredTunnelHostnamePlaceholder: \"dsh.example.com\",\n cloudflaredTunnelPort: \"Porta di inoltro locale\",\n cloudflaredTunnelToken: \"Token del connettore\",\n cloudflaredTunnelTokenPlaceholder: \"Incolla il token del tunnel dalla dashboard Cloudflare\",\n cloudflaredTunnelTokenSaved: \"Un token è salvato. Lascia vuoto per mantenerlo.\",\n cloudflaredTunnelSave: \"Salva e connetti\",\n cloudflaredTunnelSaveQuick: \"Passa a un tunnel rapido\",\n cloudflaredTunnelConfigured: \"Tunnel con nome configurato: {hostname} raggiunge questo computer su 127.0.0.1:{port}.\",\n cloudflaredTunnelMissing: \"Nessun tunnel con nome configurato.\",\n cloudflaredTunnelHint: \"Nella dashboard Cloudflare crea un tunnel e imposta il suo hostname pubblico su http://127.0.0.1:{port}. Il token è salvato solo nella directory privata DSH Mobile e non viene più mostrato dopo il salvataggio.\",\n cloudflaredTunnelForget: \"Rimuovi il token salvato\",\n cloudflaredTunnelForgetConfirm: \"Rimuovere il token salvato, arrestare il canale cloudflared e tornare a un tunnel rapido? Nulla si riconnette da solo: riattiva il canale in seguito. Il tunnel resta nel tuo account Cloudflare e il suo hostname pubblico non cambia.\",\n cloudflaredTunnelInvalid: \"La configurazione salvata del tunnel con nome non è leggibile; il provider usa un tunnel rapido. Salva di nuovo le impostazioni.\",\n cloudflaredTunnelPortUnavailable: \"La porta di inoltro locale configurata non è disponibile. Cloudflare instrada l’hostname pubblico esattamente su quella porta: liberala o scegline un’altra.\",\n cloudflaredTunnelHostnameInvalid: \"Inserisci un hostname pubblico sotto un dominio di questo account Cloudflare, ad esempio dsh.example.com.\",\n cloudflaredTunnelPortInvalid: \"Inserisci una porta di inoltro tra 1024 e 65535.\",\n cloudflaredTunnelPortReserved: \"La porta 3443 è riservata al gateway LAN di DSH Mobile ed è sempre occupata. Scegli un’altra porta, ad esempio 3444 o 3445.\",\n cloudflaredTunnelTokenInvalid: \"Non sembra un token di tunnel. Copia il token completo dalla dashboard Cloudflare.\",\n cloudflaredTunnelSettingsInvalid: \"Le impostazioni del tunnel sono state rifiutate.\",\n cloudflaredTunnelConfigMissing: \"Inserisci token, hostname e porta per usare un tunnel con nome.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n cloudflaredBadge: \"账号可选\",\n cloudflaredDescription: \"按需安装官方 cloudflared 客户端。快速隧道无需账号,提供临时公网地址;命名隧道使用 Cloudflare 账号令牌,地址固定、重启不变。\",\n prepareCloudflared: \"准备 cloudflared\",\n cloudflaredUnsupported: \"当前支持 Windows x64 与 Linux x64/arm64。\",\n cloudflaredNotInstalled: \"尚未安装。只有点击下方按钮后,才会从 cloudflared 官方发布页下载固定版本。\",\n cloudflaredReady: \"官方组件 {version} 已就绪。\",\n cloudflaredQuickNote: \"快速隧道:无需 Cloudflare 账号,但官方定位是测试用途 —— 地址随机、有限流、无可用性保证。请勿用于必须长期可达的访问。\",\n cloudflaredNamedNote: \"命名隧道:上面的公网域名重启后不变,由 Cloudflare 转发到本机转发端口。连接器令牌只保存在 DSH Mobile 私有目录,经环境变量而非命令行传给 cloudflared,保存后不再显示。\",\n cloudflaredComponentNote: \"仅在你点击安装后从 cloudflared 官方发布页下载并校验固定版本,且启动时关闭自动更新。不会写入系统服务、开机启动、注册表或 PATH。\",\n installConfirmCloudflared: \"将从 cloudflared 官方发布页下载并校验固定版本({size}),仅保存到 DSH Mobile 私有目录。不会安装系统服务、写入 PATH/注册表或设置开机启动。是否继续?\",\n installingCloudflared: \"正在安装 cloudflared 官方组件。完成前请保持 DSH 运行。\",\n purgeCloudflared: \"彻底移除 cloudflared 组件\",\n purgeCloudflaredConfirm: \"彻底移除 DSH Mobile 私有目录中的 cloudflared 组件和运行日志?不会影响局域网、DSH 数据或系统中的其他程序。\",\n purgingCloudflared: \"正在关闭通道并清理 DSH Mobile 管理的 cloudflared 文件…\",\n switchingCloudflared: \"正在切换到 cloudflared…\",\n reconnectingCloudflared: \"正在重连 cloudflared…\",\n remoteUnavailableCloudflared: \"cloudflared 尚未安装。\",\n remoteStartingCloudflared: \"正在启动 cloudflared 连接器…\",\n remoteConnectingCloudflared: \"正在启动 DSH 认证网关…\",\n resetCloudflaredConfirm: \"关闭 cloudflared 远程通道并移除所有远程配对设备?已安装组件与局域网配置保持不变。\",\n cloudflaredMissing: \"cloudflared 官方组件尚未安装。请先完成上方准备步骤。\",\n cloudflaredInvalid: \"cloudflared 组件校验失败。请彻底移除后重新安装。\",\n cloudflaredPortUnavailable: \"无法分配本机远程网关端口,请重试。\",\n cloudflaredLaunchFailed: \"cloudflared 客户端未能启动。\",\n cloudflaredTimeout: \"等待隧道可用超时,请重新连接。\",\n cloudflaredStopped: \"cloudflared 连接已停止。\",\n cloudflaredExited: \"cloudflared 连接意外退出,请重新连接。\",\n cloudflaredOutputInvalid: \"cloudflared 返回了无法识别的状态。\",\n cloudflaredOriginInvalid: \"cloudflared 返回的公网地址未通过校验。\",\n cloudflaredComponentUnsupported: \"当前构建无法运行 cloudflared 组件。\",\n cloudflaredDownloadHashMismatch: \"下载的 cloudflared 未通过 SHA-256 校验。请重新安装。\",\n cloudflaredDownloadSizeMismatch: \"下载的 cloudflared 体积与固定版本不符。请重新安装。\",\n cloudflaredDownloadRedirectMissing: \"下载页不再跳转到发布资源。请稍后重试,或按官方页面手动安装。\",\n cloudflaredDownloadRedirectInvalid: \"下载跳转的目标不是 GitHub 发布资源,已被拒绝。\",\n cloudflaredDownloadRedirectRejected: \"下载跳转未通过校验规则。请稍后重试,或按官方页面手动安装。\",\n cloudflaredExecutableHashMismatch: \"已安装的 cloudflared 与校验过的版本不一致。请彻底移除后重新安装。\",\n cloudflaredPortReservationFailed: \"无法为隧道预留本机端口,请重试。\",\n cloudflaredTunnelTargetInvalid: \"已保存的隧道配置不是普通文件。请移除后重新保存设置。\",\n cloudflaredTunnelSaving: \"正在保存隧道设置并重新连接 cloudflared…\",\n cloudflaredTunnelForgetting: \"正在移除已保存的隧道令牌…\",\n cloudflaredTunnelTitle: \"隧道类型\",\n cloudflaredTunnelQuick: \"快速隧道\",\n cloudflaredTunnelNamed: \"命名隧道\",\n cloudflaredTunnelQuickHint: \"无需账号:每次启动由 cloudflared 随机分配地址。\",\n cloudflaredTunnelNamedHint: \"使用 Cloudflare 账号令牌,地址固定,重启后仍然有效。\",\n cloudflaredTunnelHostname: \"公网域名\",\n cloudflaredTunnelHostnamePlaceholder: \"dsh.example.com\",\n cloudflaredTunnelPort: \"本机转发端口\",\n cloudflaredTunnelToken: \"连接器令牌\",\n cloudflaredTunnelTokenPlaceholder: \"粘贴 Cloudflare 控制台里的隧道令牌\",\n cloudflaredTunnelTokenSaved: \"已保存令牌,留空表示不更改。\",\n cloudflaredTunnelSave: \"保存并连接\",\n cloudflaredTunnelSaveQuick: \"切换到快速隧道\",\n cloudflaredTunnelConfigured: \"命名隧道已配置:{hostname} 将转发到本机 127.0.0.1:{port}。\",\n cloudflaredTunnelMissing: \"尚未配置命名隧道。\",\n cloudflaredTunnelHint: \"先在 Cloudflare 控制台创建隧道,并把它的公开主机名指向 http://127.0.0.1:{port}。令牌只保存在 DSH Mobile 私有目录,保存后不会再显示。\",\n cloudflaredTunnelForget: \"移除已保存的令牌\",\n cloudflaredTunnelForgetConfirm: \"移除已保存的隧道令牌、停止 cloudflared 通道并改回快速隧道?不会自动重连,之后需要手动重新打开开关。Cloudflare 账号里的隧道本身仍在,公开主机名也不会改变。\",\n cloudflaredTunnelInvalid: \"已保存的命名隧道配置无法读取,当前按快速隧道运行。请重新保存设置。\",\n cloudflaredTunnelPortUnavailable: \"配置的本机转发端口不可用。Cloudflare 把公开主机名固定转发到该端口,请释放它或换一个端口。\",\n cloudflaredTunnelHostnameInvalid: \"请填写本 Cloudflare 账号下某个域名的公开主机名,例如 dsh.example.com。\",\n cloudflaredTunnelPortInvalid: \"请填写 1024 到 65535 之间的转发端口。\",\n cloudflaredTunnelPortReserved: \"3443 已留给 DSH Mobile 局域网网关,且始终被占用。请换一个端口,例如 3444 或 3445。\",\n cloudflaredTunnelTokenInvalid: \"这不像是一个隧道令牌。请从 Cloudflare 控制台完整复制令牌。\",\n cloudflaredTunnelSettingsInvalid: \"隧道设置未通过校验。\",\n cloudflaredTunnelConfigMissing: \"使用命名隧道需要同时填写令牌、公网域名和端口。\",\n})\n\nexport const DIAGNOSTIC_REASON_MESSAGES = {\n en: {\n 'lan-setup-required': ['Local network setup has not been completed.', 'Return to Local network and choose a network to finish setup.'],\n 'versions-current': ['Installed plugin, DSH, and minimum Android app versions are shown.', ''],\n 'network-unavailable': ['The saved local network adapter is unavailable.', 'Run dsh-mobile setup again.'],\n 'network-interface': ['Using network interface {interfaceName}.', ''],\n 'network-fixed': ['A fixed local network configuration is in use.', ''],\n 'lan-ready': ['The local gateway is listening at {endpointSuffix}; pairing is available.', ''],\n 'lan-off': ['Local network access is currently off.', 'Enable local network access when the phone must connect directly.'],\n 'firewall-ready': ['Local TCP and discovery firewall rules are enabled.', ''],\n 'firewall-missing': ['The complete local network firewall rules were not found.', 'Run dsh-mobile setup as administrator and check the firewall rules.'],\n 'firewall-unknown': ['The system did not allow the plugin to read firewall status.', 'If the phone cannot find this computer, run setup as administrator.'],\n 'remote-off': ['Remote access through {provider} is currently off.', ''],\n 'remote-origin-ready': ['The private HTTP backend is listening; public HTTPS is not verified.', 'Verify your reverse proxy, public certificate, external Host including port, and WebSocket access from your phone. Keep the HTTP backend private.'],\n 'remote-ready': ['{provider} endpoint {endpointSuffix} is reachable in about {latencyMs} ms.', ''],\n 'remote-rate-limited': ['{provider} endpoint {endpointSuffix} is reachable, but this check observed rate limiting.', 'Try again later; older sessions load on demand to reduce traffic.'],\n 'remote-fake-ip': ['The Tailscale address is intercepted by the current VPN or DNS proxy, so TLS was not established.', 'Switch VPN node or proxy mode; if it still fails, use cpolar.'],\n 'remote-unreachable': ['{provider} reports ready, but endpoint {endpointSuffix} is not reachable.', 'Reconnect, then check the provider status if it still fails.'],\n 'remote-needs-login': ['Tailscale is waiting for login to finish.', 'Return to Remote access and continue login.'],\n 'remote-connecting': ['The {provider} remote channel is still connecting.', 'Wait briefly, then check again.'],\n 'remote-controller-error': ['The {provider} controller reported {controllerCode}.', 'Return to Remote access and reconnect.'],\n 'phone-network-unknown': ['The computer cannot determine whether the router isolates the phone.', 'Confirm the phone and computer use the same network, then disable guest-network or AP isolation.'],\n },\n it: {\n 'lan-setup-required': ['La configurazione della rete locale non è completa.', 'Torna a Rete locale e scegli una rete per completare la configurazione.'],\n 'versions-current': ['Sono indicate le versioni installate di plugin e DSH e la versione minima dell’app Android.', ''],\n 'network-unavailable': ['La scheda di rete locale salvata non è disponibile.', 'Esegui di nuovo dsh-mobile setup.'],\n 'network-interface': ['È in uso l’interfaccia di rete {interfaceName}.', ''],\n 'network-fixed': ['È in uso una configurazione di rete locale fissa.', ''],\n 'lan-ready': ['Il gateway locale è in ascolto su {endpointSuffix}; l’abbinamento è disponibile.', ''],\n 'lan-off': ['L’accesso dalla rete locale è disattivato.', 'Attiva l’accesso locale quando il telefono deve collegarsi direttamente.'],\n 'firewall-ready': ['Le regole firewall TCP locale e di rilevamento sono attive.', ''],\n 'firewall-missing': ['Le regole firewall complete per la rete locale non sono state trovate.', 'Esegui dsh-mobile setup come amministratore e controlla le regole firewall.'],\n 'firewall-unknown': ['Il sistema non ha consentito al plugin di leggere lo stato del firewall.', 'Se il telefono non trova il computer, esegui setup come amministratore.'],\n 'remote-off': ['L’accesso remoto tramite {provider} è disattivato.', ''],\n 'remote-origin-ready': ['Il backend HTTP privato è in ascolto; HTTPS pubblico non è verificato.', 'Verifica dal telefono reverse proxy, certificato pubblico, Host esterno inclusa la porta e accesso WebSocket. Mantieni privato il backend HTTP.'],\n 'remote-ready': ['L’endpoint {provider} {endpointSuffix} è raggiungibile in circa {latencyMs} ms.', ''],\n 'remote-rate-limited': ['L’endpoint {provider} {endpointSuffix} è raggiungibile, ma il controllo ha rilevato una limitazione temporanea.', 'Riprova più tardi; le sessioni precedenti vengono caricate su richiesta per ridurre il traffico.'],\n 'remote-fake-ip': ['L’indirizzo Tailscale è intercettato dalla VPN o dal proxy DNS corrente e TLS non è stato stabilito.', 'Cambia nodo VPN o modalità proxy; se il problema continua, usa cpolar.'],\n 'remote-unreachable': ['{provider} risulta pronto, ma l’endpoint {endpointSuffix} non è raggiungibile.', 'Riconnettiti; se il problema continua, controlla lo stato del provider.'],\n 'remote-needs-login': ['Tailscale attende il completamento dell’accesso.', 'Torna ad Accesso remoto e continua l’accesso.'],\n 'remote-connecting': ['Il canale remoto {provider} è ancora in connessione.', 'Attendi qualche istante e ripeti il controllo.'],\n 'remote-controller-error': ['Il controller {provider} ha segnalato {controllerCode}.', 'Torna ad Accesso remoto e riconnettiti.'],\n 'phone-network-unknown': ['Il computer non può stabilire se il router isola il telefono.', 'Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP.'],\n },\n zh: {\n 'lan-setup-required': ['尚未完成局域网初始化。', '返回局域网页选择网卡并完成配置。'],\n 'versions-current': ['已显示插件、DSH 和 Android App 最低版本。', ''],\n 'network-unavailable': ['已保存的局域网网卡当前不可用。', '重新运行 dsh-mobile setup。'],\n 'network-interface': ['正在使用网卡 {interfaceName}。', ''],\n 'network-fixed': ['当前使用固定局域网配置。', ''],\n 'lan-ready': ['局域网网关正在监听 {endpointSuffix},配对入口可用。', ''],\n 'lan-off': ['局域网访问当前未开启。', '手机需要直连时开启局域网访问。'],\n 'firewall-ready': ['局域网 TCP 与发现防火墙规则已启用。', ''],\n 'firewall-missing': ['未找到完整的局域网防火墙规则。', '以管理员身份运行 dsh-mobile setup 并检查防火墙规则。'],\n 'firewall-unknown': ['系统未允许插件读取防火墙状态。', '若手机找不到电脑,请以管理员身份运行 setup。'],\n 'remote-off': ['{provider} 远程访问当前未启用。', ''],\n 'remote-origin-ready': ['私有 HTTP 后端已监听;尚未验证公网 HTTPS。', '请用手机核对反向代理、公网证书、含端口的外部 Host 及 WebSocket 访问。HTTP 后端必须留在私网。'],\n 'remote-ready': ['{provider} 端点 {endpointSuffix} 可达,往返约 {latencyMs} ms。', ''],\n 'remote-rate-limited': ['{provider} 端点 {endpointSuffix} 可达,但本次检查观察到服务限流。', '稍后重试;旧会话会按需加载以减少流量。'],\n 'remote-fake-ip': ['Tailscale 地址被当前 VPN 或 DNS 代理接管,TLS 链路未建立。', '切换 VPN 节点或代理模式;仍失败时改用 cpolar。'],\n 'remote-unreachable': ['{provider} 显示已就绪,但端点 {endpointSuffix} 暂不可达。', '点击重新连接;仍失败时检查提供方状态。'],\n 'remote-needs-login': ['Tailscale 正在等待完成登录。', '返回远程访问并继续登录。'],\n 'remote-connecting': ['{provider} 远程通道仍在连接。', '等待片刻后重新检查。'],\n 'remote-controller-error': ['{provider} 控制器报告 {controllerCode}。', '返回远程访问并重新连接。'],\n 'phone-network-unknown': ['电脑无法判断路由器是否隔离了手机。', '确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。'],\n },\n} as const\n","/** Loopback-only HTTP vhost port used between Caddy and frps. */\nexport const FRP_VHOST_HTTP_PORT = 7080\n\n/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */\nexport const FRP_CADDY_SNIPPET_PATH = '/etc/caddy/dsh-mobile-dsh.caddy'\n\n/** First line of the owned snippet; also the legacy whole-file marker. */\nexport const FRP_CADDY_SNIPPET_MARKER = '# Managed by DSH Mobile - snippet, safe to delete'\n\n/** Exact line the main Caddyfile must contain (uncommented) for the site to load. */\nexport const FRP_CADDY_IMPORT_LINE = `import ${FRP_CADDY_SNIPPET_PATH}`\n\n/** Directory holding the public-IPv4 certificates installed by certbot. */\nexport const FRP_CADDY_IP_CERT_DIR = '/var/lib/caddy/dsh-mobile-certs'\n\nfunction publicIpv4Address(value: string): boolean {\n const parts = value.split('.')\n return parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/u.test(part)\n && Number(part) <= 255)\n}\n\nfunction publicDnsHostname(value: string): boolean {\n return value.length <= 253 && value.includes('.') && !/^[0-9.]+$/u.test(value)\n && !value.includes(':') && value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\nfunction parsePublicOrigin(publicOrigin: string): string {\n let url: URL\n try { url = new URL(publicOrigin) } catch { throw new Error('frp_template_input_invalid') }\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (!publicIpv4Address(url.hostname) && !publicDnsHostname(url.hostname))) {\n throw new Error('frp_template_input_invalid')\n }\n return url.hostname\n}\n\n/** Build the Caddy site for one public host (without markers or import wiring). */\nexport function createCaddySite(publicHost: string, certDir: string = FRP_CADDY_IP_CERT_DIR): string {\n if (publicIpv4Address(publicHost)) {\n return [\n '{',\n ` default_sni ${publicHost}`,\n '}',\n '',\n `http://${publicHost} {`,\n ` redir https://${publicHost}{uri} permanent`,\n '}',\n '',\n `https://${publicHost} {`,\n ` tls ${certDir}/fullchain.pem ${certDir}/privkey.pem`,\n ` reverse_proxy 127.0.0.1:${String(FRP_VHOST_HTTP_PORT)}`,\n '}',\n '',\n ].join('\\n')\n }\n if (!publicDnsHostname(publicHost)) throw new Error('frp_template_input_invalid')\n return [\n `${publicHost} {`,\n ` reverse_proxy 127.0.0.1:${String(FRP_VHOST_HTTP_PORT)}`,\n '}',\n '',\n ].join('\\n')\n}\n\n/** Manual certbot steps for a public-IPv4 origin (Caddy cannot issue IP certificates itself). */\nfunction manualIpCertificateGuide(publicHost: string): string {\n return [\n '# Public-IPv4 manual HTTPS: Caddy cannot issue IP certificates by itself.',\n '# On the VPS (Ubuntu/Debian, port 80 reachable from the internet), run once as root:',\n '# apt-get install -y python3-venv',\n '# python3 -m venv /opt/dsh-mobile/certbot-venv',\n \"# /opt/dsh-mobile/certbot-venv/bin/pip install 'certbot==5.8.0'\",\n '# systemctl stop caddy || true',\n `# /opt/dsh-mobile/certbot-venv/bin/certbot certonly --standalone --preferred-profile shortlived --ip-address ${publicHost} --agree-tos --register-unsafely-without-email --non-interactive --keep-until-expiring`,\n '# install -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs',\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem`,\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem`,\n '# systemctl start caddy',\n '# The site below already references those paths. Certificates last about 6 days: re-run certonly before expiry.',\n '#',\n ].join('\\n')\n}\n\n/** Build the only supported frps config and Caddy snippet from validated user inputs. */\nexport function createRestrictedFrpServerTemplate(serverPort: number, token: string, publicOrigin: string): string {\n if (!Number.isSafeInteger(serverPort) || serverPort < 1 || serverPort > 65_535\n || token.length < 16 || token.length > 512 || /[\\s\\u0000-\\u001f\\u007f]/u.test(token)) {\n throw new Error('frp_template_input_invalid')\n }\n const publicHost = parsePublicOrigin(publicOrigin)\n const lines = [\n '# frps.toml — save as /etc/dsh-mobile/frps.toml, then start the frps service.',\n `bindPort = ${String(serverPort)}`,\n 'proxyBindAddr = \"127.0.0.1\"',\n `vhostHTTPPort = ${String(FRP_VHOST_HTTP_PORT)}`,\n 'auth.method = \"token\"',\n `auth.token = ${JSON.stringify(token)}`,\n '',\n `# Caddy — save the site below as ${FRP_CADDY_SNIPPET_PATH},`,\n '# then make sure your Caddyfile contains exactly this line at the TOP of the file',\n '# (create the file with just this line if needed; globals must precede sites):',\n `# ${FRP_CADDY_IMPORT_LINE}`,\n '# finally run: caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n '# Uninstall later removes only this snippet file and the import line; your own Caddy content is kept.',\n `${FRP_CADDY_SNIPPET_MARKER}`,\n createCaddySite(publicHost).trimEnd(),\n '',\n ]\n if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), '')\n return lines.join('\\n')\n}\n","/** Task-completion notifications for the Android shell.\n *\n * The page cannot know a run \"finished\" from any single event, so completion\n * is defined conservatively: assistant activity was observed, then the\n * conversation stayed quiet for a bounded period with no pending input\n * question, while the page was hidden. Question cards, which unambiguously\n * wait for the user, notify immediately instead of waiting out the quiet\n * period. Everything here is pure except the clearly-marked DOM adapters so\n * the decision logic stays unit-testable under node.\n */\n\n/** How long the conversation must stay quiet before a run counts as done. */\nexport const TASK_NOTIFY_QUIET_MS = 90_000\n\n/**\n * Grace after a busy-to-idle transition before announcing completion.\n * Absorbs composer flicker between steps while still feeling immediate.\n */\nexport const TASK_NOTIFY_GRACE_MS = 10_000\n\n/**\n * Minimum continuous busy time for an instant announcement. A run that held\n * the composer this long is no flicker: announce at once instead of waiting\n * out the grace period.\n */\nexport const TASK_NOTIFY_MIN_BUSY_MS = 20_000\n\n/** Maximum quiet period worth waiting through before giving up on a run. */\nexport const TASK_NOTIFY_STALE_MS = 30 * 60_000\n\n/** Wire bounds mirrored by the Android notification policy. */\nexport const TASK_NOTIFY_LIMITS = Object.freeze({\n title: 80,\n body: 200,\n tag: 64,\n} as const)\n\nexport type TaskNotifyKind = 'done' | 'question'\n\nexport interface TaskNotifyTexts {\n readonly doneTitle: string\n readonly doneBody: string\n readonly questionTitle: string\n readonly questionBody: string\n}\n\nexport interface TaskNotifySnapshot {\n /** Result of document.hidden at evaluation time. */\n readonly pageHidden: boolean\n /** Whether a question or plan-review card currently waits for input. */\n readonly pendingQuestion: boolean\n /** Stable key of the waiting card, when the DOM exposes one. */\n readonly questionKey: string | undefined\n /** Best-effort session label for the notification body. */\n readonly sessionLabel: string\n /** Current composer busy flag; drives the completion transition. */\n readonly composerBusy: boolean\n}\n\nexport interface TaskNotifyEvent {\n readonly kind: TaskNotifyKind\n readonly title: string\n readonly body: string\n readonly tag: string\n}\n\nexport interface TaskNotifyOptions {\n readonly quietMs?: number\n readonly graceMs?: number\n readonly minBusyMs?: number\n readonly now?: () => number\n readonly format: (kind: TaskNotifyKind, sessionLabel: string) => { title: string; body: string }\n}\n\nconst MAX_SEEN_QUESTION_KEYS = 20\n\nfunction sanitizeTagFragment(value: string): string {\n return value.toLowerCase().replaceAll(/[^a-z0-9-]+/gu, '-').replaceAll(/^-+|-+$/gu, '').slice(0, TASK_NOTIFY_LIMITS.tag)\n}\n\n/** Stable per-turn tag so concurrent sessions keep separate notifications. */\nexport function taskCompletionTag(sessionId: string, turn: number): string {\n const suffix = sanitizeTagFragment(`${sessionId}-${String(turn)}`) || 'task'\n return `dsh-task-done-${suffix}`.slice(0, TASK_NOTIFY_LIMITS.tag)\n}\n\n/** Decide whether the current snapshot deserves a system notification. */\nexport class TaskNotifyTracker {\n private lastActivityAt: number | undefined\n private notifiedDoneAt: number | undefined\n private wasBusy = false\n private busySince: number | undefined\n private idleAnchoredAt: number | undefined\n private readonly seenQuestionKeys = new Set<string>()\n private readonly quietMs: number\n private readonly graceMs: number\n private readonly minBusyMs: number\n private readonly now: () => number\n private readonly format: (kind: TaskNotifyKind, sessionLabel: string) => { title: string; body: string }\n\n constructor(options: TaskNotifyOptions) {\n this.quietMs = options.quietMs ?? TASK_NOTIFY_QUIET_MS\n this.graceMs = options.graceMs ?? TASK_NOTIFY_GRACE_MS\n this.minBusyMs = options.minBusyMs ?? TASK_NOTIFY_MIN_BUSY_MS\n this.now = options.now ?? Date.now\n this.format = options.format\n }\n\n /** Record assistant-side activity; re-arms completion after a notification. */\n markActivity(): void {\n const now = this.now()\n this.lastActivityAt = now\n if (this.notifiedDoneAt !== undefined && this.notifiedDoneAt <= now) this.notifiedDoneAt = undefined\n }\n\n /**\n * Milliseconds until a pending completion anchor fires, or undefined when\n * no anchor exists. Lets the watcher schedule one exact timer instead of\n * waiting for the next coarse poll tick.\n */\n pendingAnchorDelayMs(): number | undefined {\n if (this.idleAnchoredAt === undefined) return undefined\n return Math.max(0, this.graceMs - (this.now() - this.idleAnchoredAt))\n }\n\n evaluate(snapshot: TaskNotifySnapshot): TaskNotifyEvent | undefined {\n const now = this.now()\n if (!this.wasBusy && snapshot.composerBusy) this.busySince = now\n if (this.wasBusy && !snapshot.composerBusy) {\n // The run just ended. A run that held the composer long enough is no\n // flicker: announce at once when hidden, unless a question card (which\n // has its own immediate path below) is already waiting.\n const busyFor = this.busySince === undefined ? 0 : now - this.busySince\n this.busySince = undefined\n if (snapshot.pageHidden && !snapshot.pendingQuestion && busyFor >= this.minBusyMs) {\n this.idleAnchoredAt = undefined\n this.notifiedDoneAt = now\n return this.event('done', snapshot.sessionLabel, undefined)\n }\n this.idleAnchoredAt = snapshot.pageHidden ? now : undefined\n if (!snapshot.pageHidden) this.notifiedDoneAt = now\n }\n this.wasBusy = snapshot.composerBusy\n if (snapshot.composerBusy) {\n this.idleAnchoredAt = undefined\n } else if (this.idleAnchoredAt !== undefined && !snapshot.pageHidden) {\n // Came back before the grace elapsed: treat the ending as seen.\n this.idleAnchoredAt = undefined\n this.notifiedDoneAt = now\n }\n if (!snapshot.pageHidden) {\n if (snapshot.pendingQuestion && snapshot.questionKey !== undefined) this.rememberQuestionKey(snapshot.questionKey)\n return undefined\n }\n if (snapshot.pendingQuestion) {\n if (snapshot.questionKey === undefined || this.seenQuestionKeys.has(snapshot.questionKey)) return undefined\n this.rememberQuestionKey(snapshot.questionKey)\n return this.event('question', snapshot.sessionLabel, snapshot.questionKey)\n }\n // Completion: the anchored transition announces fast; the quiet period\n // remains as the fallback for activity without a busy signal.\n if (this.idleAnchoredAt !== undefined) {\n if (now - this.idleAnchoredAt < this.graceMs) return undefined\n this.idleAnchoredAt = undefined\n this.notifiedDoneAt = now\n return this.event('done', snapshot.sessionLabel, undefined)\n }\n if (this.lastActivityAt === undefined) return undefined\n if (now - this.lastActivityAt < this.quietMs) return undefined\n if (now - this.lastActivityAt > TASK_NOTIFY_STALE_MS) {\n // Activity too old to attribute: drop it instead of announcing a stale run.\n this.lastActivityAt = undefined\n return undefined\n }\n if (this.notifiedDoneAt !== undefined && this.notifiedDoneAt >= this.lastActivityAt) return undefined\n this.notifiedDoneAt = now\n return this.event('done', snapshot.sessionLabel, undefined)\n }\n\n private rememberQuestionKey(key: string): void {\n this.seenQuestionKeys.add(key)\n if (this.seenQuestionKeys.size > MAX_SEEN_QUESTION_KEYS) {\n const oldest = this.seenQuestionKeys.values().next()\n if (!oldest.done) this.seenQuestionKeys.delete(oldest.value)\n }\n }\n\n private event(kind: TaskNotifyKind, sessionLabel: string, questionKey: string | undefined): TaskNotifyEvent {\n const text = this.format(kind, sessionLabel)\n const tag = kind === 'question' && questionKey !== undefined\n ? `dsh-task-question-${sanitizeTagFragment(questionKey) || 'pending'}`\n : 'dsh-task-done'\n return Object.freeze({ kind, title: text.title, body: text.body, tag })\n }\n}\n\ninterface ComposerProbe {\n getAttribute?: (name: string) => string | null\n querySelector?: (selectors: string) => {\n disabled?: boolean\n readOnly?: boolean\n getAttribute?: (name: string) => string | null\n } | null\n}\n\n/**\n * Mirror of the composer busy signal used for media actions: the run is busy\n * while the composer is aria-busy, disabled, read-only, or non-interactive.\n */\nexport function readComposerBusyState(root: ParentNode): boolean {\n if (typeof root.querySelector !== 'function') return false\n const card = root.querySelector('[data-composer-card]') as ComposerProbe | null\n if (card === null || typeof card.getAttribute !== 'function' || typeof card.querySelector !== 'function') return false\n if (card.getAttribute('aria-busy') === 'true') return true\n const field = card.querySelector('textarea,input,[contenteditable=\"true\"],[contenteditable=\"plaintext-only\"]')\n if (field === null) return false\n return field.disabled === true || field.readOnly === true || field.getAttribute?.('aria-disabled') === 'true'\n}\n\n/**\n * Best-effort session label for the notification body, bounded for the wire.\n * Callers accept that titles may name the active session on the lock screen.\n */\nexport function readSessionLabel(title: unknown): string {\n if (typeof title !== 'string') return ''\n return title.normalize('NFC').replace(/[\\u0000-\\u001f\\u007f]+/gu, ' ').trim().slice(0, 80)\n}\n\n/** Whether the document currently shows a card that waits for user input. */\nexport function hasPendingInputQuestion(root: ParentNode): boolean { if (typeof root.querySelector !== 'function') return false\n return root.querySelector('[data-question-key],[data-plan-review-key]') !== null\n}\n\n/** Stable key of the waiting input card, when the DOM exposes one. */\nexport function pendingInputQuestionKey(root: ParentNode): string | undefined {\n if (typeof root.querySelector !== 'function') return undefined\n const card = root.querySelector('[data-question-key],[data-plan-review-key]') as {\n getAttribute?: (name: string) => string | null\n } | null\n const getAttribute = card?.getAttribute\n if (typeof getAttribute !== 'function') return undefined\n const key = getAttribute.call(card, 'data-question-key') ?? getAttribute.call(card, 'data-plan-review-key')\n if (key === null || key === '') return undefined\n return key\n}\n\n/** Observe assistant-side conversation mutations; returns a disposer. */\nexport function observeTaskActivity(\n target: Node,\n onActivity: () => void,\n observe: typeof MutationObserver | undefined = typeof MutationObserver === 'function' ? MutationObserver : undefined,\n): () => void {\n if (observe === undefined) return () => undefined\n const observer = new observe(() => { onActivity() })\n try {\n observer.observe(target, { childList: true, characterData: true, subtree: true })\n } catch {\n return () => undefined\n }\n return () => { observer.disconnect() }\n}\n\nexport interface TaskWatcherLabels {\n readonly label: (kind: TaskNotifyKind, sessionLabel: string) => { title: string; body: string }\n /** Legacy DOM-only completion inference. Current gateways provide exact Host events. */\n readonly completionFallback?: boolean\n}\n\ninterface NativeBridgeHandle {\n capabilities(): Promise<readonly string[]>\n invoke(action: string, input?: unknown): Promise<unknown>\n}\n\nfunction readNativeBridge(): NativeBridgeHandle | undefined {\n if (typeof window === 'undefined') return undefined\n const bridge = (window as unknown as { __DSH_MOBILE_NATIVE__?: NativeBridgeHandle }).__DSH_MOBILE_NATIVE__\n if (bridge === undefined || typeof bridge.capabilities !== 'function' || typeof bridge.invoke !== 'function') return undefined\n return bridge\n}\n\nexport interface TaskNotifyWireEvent {\n readonly kind: TaskNotifyKind\n readonly title: string\n readonly body: string\n readonly tag: string\n}\n\n/** Parse a `task-notify` SSE payload; rejects anything malformed or empty. */\nexport function parseTaskNotifyPayload(value: unknown): { readonly sessionId: string; readonly turn: number } | undefined {\n let record: Record<string, unknown> | undefined\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined\n record = parsed as Record<string, unknown>\n } catch {\n return undefined\n }\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n record = value as Record<string, unknown>\n } else return undefined\n if (typeof record.sessionId !== 'string' || record.sessionId.trim() === '') return undefined\n const turn = typeof record.turn === 'number' && Number.isSafeInteger(record.turn) && record.turn >= 0 ? record.turn : 0\n return Object.freeze({ sessionId: record.sessionId.slice(0, 128), turn })\n}\n\n/** Parse the small authenticated event used when the Host revokes this device. */\nexport function isDeviceRevokedPayload(value: unknown): boolean {\n let record: Record<string, unknown> | undefined\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return false\n record = parsed as Record<string, unknown>\n } catch {\n return false\n }\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n record = value as Record<string, unknown>\n } else return false\n return record.reason === 'device_revoked'\n}\n\n/** Tell the Android shell to transition the active record to the revoked state. */\nexport function fireDeviceRevoked(): boolean {\n const bridge = readNativeBridge()\n if (bridge === undefined) return false\n void bridge.invoke('device.revoked', {}).catch(() => undefined)\n return true\n}\n\n/**\n * Deliver one notification through the native bridge when available.\n * Returns false when there is no bridge (desktop browsers keep their own\n * UI) so callers can fall back; a denial flips the module sticky flag via\n * onDenied for the session-lifetime nag guard.\n */\nexport function fireTaskNotifyEvent(\n event: TaskNotifyWireEvent,\n onDenied?: () => void,\n): boolean {\n const bridge = readNativeBridge()\n if (bridge === undefined) return false\n void Promise.resolve()\n .then(() => bridge.capabilities())\n .then(capabilities => {\n if (!capabilities.includes('notification.notify')) return\n return bridge.invoke('notification.notify', { title: event.title, body: event.body, tag: event.tag })\n })\n .then(\n () => undefined,\n (error: unknown) => {\n const code = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : ''\n // A denial is sticky for this page load: never nag for permission.\n if ((code === 'permission_denied' || code === 'denied') && onDenied !== undefined) onDenied()\n },\n )\n return true\n}\n\n/**\n * Watch the rendered conversation for finished runs and waiting questions,\n * notifying through the native bridge while the page is hidden. Returns a\n * disposer. Silent when no native bridge exists (desktop browsers keep their\n * own UI) or after the user denies the permission.\n */\nexport function installTaskCompletionWatcher(options: TaskWatcherLabels): () => void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return () => undefined\n const tracker = new TaskNotifyTracker({ format: options.label })\n const completionFallback = options.completionFallback ?? true\n let notifyBlocked = false\n let timer = 0\n let anchorTimer = 0\n\n const fire = (event: TaskNotifyEvent): void => {\n if (notifyBlocked) return\n if (!fireTaskNotifyEvent(event, () => { notifyBlocked = true })) return\n }\n const evaluateNow = (): void => {\n const busy = completionFallback && readComposerBusyState(document)\n const event = tracker.evaluate({\n pageHidden: document.hidden,\n pendingQuestion: hasPendingInputQuestion(document),\n questionKey: pendingInputQuestionKey(document),\n sessionLabel: readSessionLabel(document.title),\n composerBusy: busy,\n })\n if (event !== undefined) fire(event)\n scheduleAnchorTimer()\n }\n // The interval is only a backstop: an anchor schedules its own exact shot\n // so completion announces ~grace after the run ends instead of waiting out\n // the next coarse tick with no further mutations to wake it.\n const scheduleAnchorTimer = (): void => {\n const delay = tracker.pendingAnchorDelayMs()\n if (delay === undefined) {\n if (anchorTimer !== 0) {\n window.clearTimeout(anchorTimer)\n anchorTimer = 0\n }\n return\n }\n if (anchorTimer !== 0) return\n anchorTimer = window.setTimeout(() => {\n anchorTimer = 0\n evaluateNow()\n }, delay)\n }\n const disposeObservation = observeTaskActivity(document.documentElement, () => {\n if (completionFallback) tracker.markActivity()\n evaluateNow()\n })\n const onVisibility = (): void => { evaluateNow() }\n document.addEventListener('visibilitychange', onVisibility)\n if (typeof window.setInterval === 'function') {\n timer = window.setInterval(evaluateNow, 15_000)\n }\n evaluateNow()\n return () => {\n disposeObservation()\n document.removeEventListener('visibilitychange', onVisibility)\n if (timer !== 0) window.clearInterval(timer)\n if (anchorTimer !== 0) window.clearTimeout(anchorTimer)\n }\n}\n","/**\n * Viewport below which the injected chrome becomes an overlay drawer: the\n * sidebar slides in as a sheet, the right panel as a drawer, and the scrim dims\n * the page behind them. The stylesheet and the scrim's visibility both read\n * this query, so a wider viewport never grows drawer chrome whose rules cannot\n * style it.\n */\nimport { installTaskCompletionWatcher, type TaskNotifyKind } from './task-notify.js'\n\nexport const NATIVE_MOBILE_OVERLAY_QUERY = '(max-width:720px)'\n\n/** Mobile feature and compatibility rules applied to DSH React surfaces. */\nexport const NATIVE_MOBILE_STYLES = `\n/* The surface appends chrome to <body> on every non-loopback page load, but\n every rule that gives that chrome a box lives inside the overlay query\n below. Outside the query the scrim kept the UA button box: an empty,\n nameless button in normal flow at the document's bottom-left, whose click\n still collapsed the sidebar. Keep the neutral state explicitly invisible —\n the query restores the fixed scrim, and its more specific [hidden] rule\n keeps winning there. */\n .dsh-native-mobile-backdrop,.dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:none; }\n .dsh-mobile-settings_row { display:flex; align-items:center; gap:8px; padding:16px 0; border-bottom:0.5px solid var(--dsw-alias-border-l2); }\n .dsh-mobile-settings_rowText { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; padding-right:48px; }\n .dsh-mobile-settings_title { color:var(--dsw-alias-label-primary); font-size:14px; font-weight:400; line-height:22px; }\n .dsh-mobile-settings_desc { color:var(--dsw-alias-label-tertiary); font-size:12px; font-weight:400; line-height:18px; }\n .dsh-mobile-settings_selector { display:inline-flex; align-items:center; justify-content:center; min-width:max-content; height:36px; padding:0 14px; border:0; border-radius:18px; background:var(--dsw-alias-bg-module-platform); color:var(--dsw-alias-label-primary); font:inherit; font-size:14px; line-height:22px; cursor:pointer; }\n .dsh-mobile-settings_selector:hover:not(:disabled) { background:var(--dsw-alias-interactive-bg-hover); }\n .dsh-mobile-settings_selector:disabled { cursor:wait; opacity:.55; }\n@media ${NATIVE_MOBILE_OVERLAY_QUERY} {\n html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }\n html.dsh-native-mobile-active { --dsh-mobile-motion-duration:200ms; --dsh-mobile-motion-ease:cubic-bezier(.22,1,.36,1); }\n html.dsh-native-mobile-active :is(a,button,[role=\"button\"],[role=\"tab\"],[tabindex]) { -webkit-tap-highlight-color:transparent; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [role=\"treeitem\"] { -webkit-tap-highlight-color:transparent; touch-action:manipulation; }\n html.dsh-native-mobile-active[data-dsh-mobile-input=\"touch\"] :is(a,button,[role=\"button\"],[role=\"tab\"],[tabindex]):focus { outline:none !important; }\n html.dsh-native-mobile-active [role=\"tooltip\"] { display:none !important; }\n /* Touch has no persistent hover affordance: keep workspace rows neutral after a tap. */\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] { --dsw-alias-interactive-bg-hover:transparent !important; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [role=\"treeitem\"]:is(:hover,:active,:focus,[aria-selected=\"true\"]),\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_sessionRow\"][class*=\"_selected\"],\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_searchResultRow\"][class*=\"_selected\"] { background:transparent !important; outline:0 !important; box-shadow:none !important; }\n /* Sidebar row menus are hover-only on desktop. Touch has no hover, so keep\n the ellipsis action visible and give it a reliable hit target. */\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_rowActions\"] { display:inline-flex !important; align-items:center !important; gap:8px !important; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_sessionRow\"] [class*=\"_time\"] { display:none !important; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_rowActions\"] button { box-sizing:border-box !important; width:32px !important; min-width:32px !important; height:32px !important; min-height:32px !important; }\n [data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }\n [data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }\n [data-dsh-mobile-center] > * { min-width:0 !important; }\n [data-dsh-mobile-header] { box-sizing:border-box !important; width:calc(100% - 16px) !important; margin:0 8px !important; min-width:0; padding-top:max(4px,env(safe-area-inset-top)) !important; padding-right:8px !important; padding-left:42px !important; }\n [data-dsh-mobile-header] [class*=\"_titleRow\"] { box-sizing:border-box !important; display:flex !important; align-items:center !important; min-width:0; min-height:32px !important; height:32px !important; gap:6px !important; padding:0 6px !important; }\n [data-dsh-mobile-header] [class*=\"_titleCluster\"] { min-width:0; }\n [data-dsh-mobile-header] [class*=\"_crumbs\"] { min-width:0; overflow:hidden; }\n [data-dsh-mobile-header] [class*=\"_crumb\"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { min-width:0; overflow-x:auto; scrollbar-width:none; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-header] [class*=\"_headerUtilities\"] { gap:2px !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] > * { display:none !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"]::after { color:var(--dsw-alias-label-primary, #171a21); content:\"Log\"; font-size:11px; font-weight:600; }\n html[data-dsh-mobile-language=\"zh\"] [data-dsh-mobile-header] [class*=\"_sessionLogButton\"]::after { content:\"日志\"; }\n [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; }\n [data-dsh-mobile-header] [class*=\"_tab\"] { padding-bottom:5px !important; }\n [data-dsh-mobile-header] [class*=\"_tabs\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:max(env(safe-area-inset-top),0px) auto 0 0 !important; height:auto !important; transition:width 180ms var(--dsh-mobile-motion-ease),box-shadow 180ms ease !important; }\n [data-dsh-mobile-sidebar][data-open=\"true\"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; padding-top:0 !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-sidebar][data-open=\"true\"] [data-dsh-mobile-sidebar-root] [class*=\"_logoRow\"] { height:52px !important; padding:4px 0 4px 4px !important; margin-bottom:4px !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) { position:fixed !important; z-index:244 !important; top:env(safe-area-inset-top) !important; left:0 !important; box-sizing:border-box !important; width:50px !important; height:52px !important; padding:4px !important; border:0 !important; background:transparent !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) > :not([data-dsh-mobile-toggle]) { display:none !important; }\n /* The stock settings shell is mounted under the collapsed sidebar. Let\n its fixed overlay escape the zero-width drawer while it is open. */\n [data-dsh-mobile-sidebar]:has([data-dsh-mobile-settings]) { width:100vw !important; inset:0 !important; }\n [data-dsh-mobile-sidebar]:has([data-dsh-mobile-settings]) > * { width:100vw !important; overflow:visible !important; }\n [data-dsh-mobile-toggle] { width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-toggle] > svg[class*=\"_railFish\"] { transform:translateY(-4px) !important; }\n .dsh-native-mobile-backdrop { display:block; position:fixed; z-index:235; inset:env(safe-area-inset-top) 0 0; border:0; background:rgb(15 23 42 / 32%); }\n .dsh-native-mobile-backdrop:not([hidden]) { animation:dsh-mobile-fade-in var(--dsh-mobile-motion-duration) ease-out; }\n .dsh-native-mobile-backdrop[hidden] { display:none; }\n [data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); background:var(--dsw-alias-bg-layer-1, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-details][data-open=\"true\"] { transform:translateX(0); }\n [data-dsh-mobile-handle] { display:none !important; }\n [data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; animation:dsh-mobile-panel-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }\n [data-dsh-mobile-settings-nav] { flex:none !important; width:100% !important; padding:max(14px,env(safe-area-inset-top)) 12px 8px !important; gap:10px !important; border-bottom:1px solid var(--dsw-alias-border-l2,#e8ebef); }\n [data-dsh-mobile-settings-nav] [class*=\"_navTitle\"] { padding:0 8px !important; font-size:18px !important; line-height:28px !important; }\n [data-dsh-mobile-settings-list] { flex-direction:row !important; gap:4px !important; overflow-x:auto !important; scrollbar-width:none; }\n [data-dsh-mobile-settings-list]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-settings-list] [class*=\"_navCell\"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }\n [data-dsh-mobile-settings-list] [aria-current=\"true\"] { border-color:transparent !important; outline:0 !important; box-shadow:none !important; }\n [data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }\n [data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }\n [data-dsh-mobile-settings-header] [class*=\"_close\"] { width:36px !important; height:36px !important; }\n [data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }\n [data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] > [class*=\"_row\"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_rowText\"] { width:100% !important; padding-right:0 !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_selector\"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:44px !important; max-width:100% !important; }\n [data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }\n [data-dsh-mobile-settings-options] :is(input,select,textarea) { box-sizing:border-box; width:100%; min-width:0; }\n [data-dsh-mobile-settings-options] [class*=\"_head\"] { min-width:0; flex-wrap:wrap; }\n /* Provider names may shrink, but their edit/delete actions remain horizontal\n and retain a full touch target on narrow screens. */\n [data-dsh-mobile-settings-options] [class*=\"_rowHead\"]:has(> [class*=\"_rowIdentity\"]) { flex-wrap:nowrap !important; align-items:center !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowIdentity\"] { flex:1 1 auto !important; min-width:0 !important; overflow:hidden !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowName\"] { min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowActions\"] { flex:0 0 auto !important; flex-wrap:nowrap !important; width:max-content !important; min-width:max-content !important; max-width:none !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowActions\"] button { flex:none !important; width:auto !important; min-width:44px !important; max-width:none !important; min-height:44px !important; padding-inline:10px !important; white-space:nowrap !important; word-break:keep-all !important; writing-mode:horizontal-tb !important; }\n [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition=\"true\"],\n [data-dsh-mobile-view][data-dsh-mobile-view-transition=\"true\"] { animation:dsh-mobile-view-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }\n [data-dsh-mobile-center] textarea { font-size:16px !important; }\n /* Markdown tables use content-sized columns. Small tables fill the phone;\n wider tables keep readable cells and scroll inside their own region. */\n [data-dsh-mobile-table-scroll] { box-sizing:border-box; width:100%; max-width:100%; overflow-x:auto; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch; }\n [data-dsh-mobile-table-scroll] table { display:table !important; width:max-content !important; min-width:100% !important; max-width:none !important; table-layout:auto !important; }\n [data-dsh-mobile-table-scroll] :is(th,td) { box-sizing:border-box; min-width:8ch; max-width:32ch; overflow-wrap:anywhere; word-break:break-word; vertical-align:top; }\n [data-dsh-mobile-center] pre { max-width:100%; overflow-x:auto; }\n [data-dsh-mobile-center] :is(img,video,canvas,svg) { max-width:100%; }\n [data-dsh-mobile-message-scroll] { box-sizing:border-box !important; width:100% !important; padding:8px 10px 20px !important; }\n [data-dsh-mobile-history-loader] { position:relative !important; min-height:1px !important; }\n [data-dsh-mobile-history-loader] button:not(:disabled) { position:absolute !important; width:1px !important; height:1px !important; margin:-1px !important; padding:0 !important; clip-path:inset(50%) !important; opacity:0 !important; overflow:hidden !important; pointer-events:none !important; }\n [data-dsh-mobile-history-loader] button:disabled { min-height:28px !important; padding:4px 12px !important; }\n [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:10px !important; }\n [data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] { box-sizing:border-box !important; display:grid !important; grid-template-columns:16px minmax(0,1fr) !important; grid-auto-rows:auto !important; align-items:center !important; column-gap:6px !important; width:100% !important; height:auto !important; min-height:40px !important; padding:4px 0 !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_leading\"] { grid-column:1 !important; grid-row:1 !important; margin-right:0 !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_title\"] { grid-column:2 !important; grid-row:1 !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*=\"_sep\"],[class*=\"_separator\"]) { display:none !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*=\"_summary\"],[class*=\"_fileLink\"]) { grid-column:2 !important; grid-row:2 !important; width:100% !important; min-width:0 !important; max-width:100% !important; overflow:hidden !important; line-height:19px !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_summarySuffix\"] { grid-column:2 !important; grid-row:3 !important; margin-left:0 !important; }\n [data-dsh-mobile-message-column] [data-context-fields] > * { display:grid !important; grid-template-columns:minmax(72px,30%) minmax(0,1fr) !important; gap:4px 10px !important; }\n [data-dsh-mobile-message-column] [class*=\"_ioSection\"] { grid-template-columns:1fr !important; row-gap:4px !important; }\n [data-dsh-mobile-message-column] [class*=\"_body\"] { max-width:100% !important; overflow-wrap:anywhere; }\n /* Keep folded and expanded reasoning visually separate from the reply. */\n [data-dsh-mobile-message-column] [class*=\"_body\"] > div:has(> [data-variant=\"think\"]) { margin-bottom:12px !important; }\n [data-dsh-mobile-center] [data-composer-card] ~ [class*=\"_root\"],\n [data-dsh-mobile-center] [data-composer-card] ~ * [class*=\"_root\"] { box-sizing:border-box !important; width:100% !important; max-width:100% !important; margin-bottom:-6px !important; padding:3px 4px 0 !important; font-size:11px !important; line-height:18px !important; white-space:normal !important; overflow:visible !important; text-overflow:clip !important; }\n [data-dsh-mobile-center] [data-composer-card] ~ [class*=\"_root\"] [class*=\"_sep\"],\n [data-dsh-mobile-center] [data-composer-card] ~ * [class*=\"_root\"] [class*=\"_sep\"] { margin:0 6px !important; }\n /* Composer dock stats strip (turns/steps/tokens) reads small on phones. */\n [data-dsh-mobile-center] [data-slot=\"conversation.composer.dock\"] [class*=\"_root\"] { font-size:10px !important; line-height:16px !important; }\n /* Message runtime details are inline on desktop. Give the clock/runtime\n label its own wrapping row on narrow screens so TTFT and throughput do\n not push the action buttons or clip at the viewport edge. */\n [data-dsh-mobile-center] [class*=\"_actions\"]:has(> [class*=\"_timeStart\"]),\n [data-dsh-mobile-center] [class*=\"_actions\"]:has(> [class*=\"_timeEnd\"]) { box-sizing:border-box !important; width:100% !important; flex-wrap:wrap !important; justify-content:flex-end !important; height:auto !important; min-height:28px !important; row-gap:2px !important; }\n [data-dsh-mobile-center] [class*=\"_timeStart\"],\n [data-dsh-mobile-center] [class*=\"_timeEnd\"] { box-sizing:border-box !important; flex:1 1 100% !important; order:2 !important; min-width:0 !important; max-width:100% !important; padding:0 !important; line-height:20px !important; text-align:center !important; white-space:normal !important; overflow-wrap:anywhere !important; }\n [data-dsh-mobile-center] [class*=\"_timeStart\"] { box-sizing:border-box !important; flex:1 1 100% !important; order:2 !important; min-width:0 !important; max-width:100% !important; padding:0 !important; line-height:20px !important; text-align:center !important; white-space:normal !important; overflow-wrap:anywhere !important; }\n [data-dsh-mobile-center] [class*=\"_timeStart\"] [class*=\"_runTimeDot\"],\n [data-dsh-mobile-center] [class*=\"_timeEnd\"] [class*=\"_runTimeDot\"] { margin:0 6px !important; }\n /* Keep the context meter's legend rows as readable label/value pairs.\n Generic mobile flex rules can otherwise place the rows side by side and\n break Chinese labels in the middle of a word. */\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"],\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] { width:min(264px,calc(100vw - 32px)) !important; min-width:0 !important; max-width:calc(100vw - 32px) !important; }\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"] [class*=\"_rows\"],\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] [class*=\"_rows\"] { display:block !important; }\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"] [class*=\"_rows\"] > [class*=\"_row\"],\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] [class*=\"_rows\"] > [class*=\"_row\"] { display:flex !important; align-items:center !important; justify-content:space-between !important; width:100% !important; min-width:0 !important; white-space:nowrap !important; }\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"] :is(dt,dd),\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] :is(dt,dd) { white-space:nowrap !important; word-break:keep-all !important; }\n .dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:block; position:fixed; z-index:330; top:max(12px,env(safe-area-inset-top)); left:50%; max-width:calc(100vw - 32px); box-sizing:border-box; padding:7px 14px; border:1px solid rgb(15 23 42 / 10%); border-radius:999px; background:rgb(15 23 42 / 92%); color:#fff; font-size:13px; line-height:20px; text-align:center; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; opacity:0; pointer-events:none; transform:translate(-50%,-8px); transition:opacity 160ms ease,transform 160ms ease; }\n .dsh-mobile-branch-toast[data-visible=\"true\"],.dsh-mobile-media-toast[data-visible=\"true\"] { opacity:1; transform:translate(-50%,0); }\n /* Fallback only: when the native Add group has no item to clone, keep the\n camera action on the same row metrics and tokens as the native menu. */\n .dsh-mobile-media-action { box-sizing:border-box; display:flex; align-items:center; justify-content:flex-start; gap:8px; width:100%; min-width:0; min-height:40px; padding:8px 10px; border:0; border-radius:10px; background:transparent; color:var(--dsw-alias-label-primary,inherit); cursor:pointer; font:inherit; font-size:14px; line-height:22px; text-align:left; touch-action:manipulation; }\n .dsh-mobile-media-action:active { opacity:.72; }\n .dsh-mobile-media-action:focus-visible { outline:2px solid var(--dsw-alias-state-business-primary,#4c82f7); outline-offset:1px; }\n .dsh-mobile-media-action:disabled { cursor:default; opacity:.38; }\n .dsh-mobile-media-action svg { flex:none; width:16px; height:16px; color:var(--dsw-alias-label-tertiary,currentColor); }\n .dsh-mobile-media-action span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-center] [class*=\"_composer\"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }\n /* The desktop composer intentionally wraps whole toolbar groups. On a phone,\n dynamic model and status labels made that row alternate between one and\n two lines. Keep two stable columns and let only the model label shrink. */\n [data-dsh-mobile-composer-row] { display:grid !important; grid-template-columns:max-content minmax(0,1fr) !important; align-items:center !important; gap:4px 8px !important; }\n [data-dsh-mobile-composer-tools] { display:flex !important; flex-wrap:nowrap !important; width:max-content !important; min-width:0 !important; max-width:max-content !important; gap:6px !important; }\n [data-dsh-mobile-composer-trailing] { display:flex !important; flex-wrap:nowrap !important; width:100% !important; min-width:0 !important; max-width:100% !important; gap:6px !important; margin-left:0 !important; justify-content:flex-end !important; }\n [data-dsh-mobile-composer-model] { flex:1 1 0 !important; width:auto !important; min-width:0 !important; max-width:none !important; }\n [data-dsh-mobile-composer-model-trigger] { box-sizing:border-box !important; width:100% !important; max-width:100% !important; min-width:0 !important; padding-left:6px !important; padding-right:4px !important; }\n [data-dsh-mobile-composer-model-label] { flex:1 1 auto !important; max-width:none !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) { box-sizing:border-box !important; width:100% !important; padding:0 0 8px !important; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) > [class=\"\"]:last-child { display:none !important; }\n}\n@keyframes dsh-mobile-fade-in { from { opacity:0; } }\n@keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }\n@keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }\n@media (max-width:420px) {\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { max-width:42vw; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_selector\"] { align-self:stretch !important; width:100% !important; }\n [data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }\n}\n@media (prefers-reduced-motion:reduce) {\n [data-dsh-mobile-sidebar-root],[data-dsh-mobile-details] { transition:none !important; }\n .dsh-native-mobile-backdrop:not([hidden]),[data-dsh-mobile-settings],\n [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition=\"true\"],\n [data-dsh-mobile-view][data-dsh-mobile-view-transition=\"true\"] { animation:none !important; }\n}\n`\n\nfunction classToken(element: Element, suffix: string): boolean {\n return Array.from(element.classList).some(value => value.endsWith(suffix))\n}\n\nfunction firstByClassSuffix(root: ParentNode, suffix: string): HTMLElement | undefined {\n return Array.from(root.querySelectorAll<HTMLElement>('[class]')).find(element => classToken(element, suffix))\n}\n\n/** Find the stock DSH application frame without mistaking a feature card for the shell. */\nexport function resolveNativeMobileFrame(root: ParentNode, dedicatedCenter: HTMLElement | undefined): HTMLElement | undefined {\n if (dedicatedCenter !== undefined) return undefined\n return Array.from(root.querySelectorAll<HTMLElement>('[class]')).find(candidate => {\n return classToken(candidate, '_frame')\n && firstByClassSuffix(candidate, '_sidebarCol') !== undefined\n && firstByClassSuffix(candidate, '_centerCol') !== undefined\n })\n}\n\n/** Mark every mounted settings dialog so its mobile layout does not depend on the conversation shell. */\nexport function markNativeMobileSettings(root: ParentNode): number {\n let marked = 0\n for (const dialog of root.querySelectorAll<HTMLElement>('[role=\"dialog\"]')) {\n const children = Array.from(dialog.children) as HTMLElement[]\n const nav = children.find(child => classToken(child, '_nav'))\n const content = children.find(child => classToken(child, '_content'))\n if (nav === undefined || content === undefined) continue\n dialog.dataset.dshMobileSettings = 'true'\n nav.dataset.dshMobileSettingsNav = 'true'\n content.dataset.dshMobileSettingsContent = 'true'\n firstByClassSuffix(nav, '_navList')?.setAttribute('data-dsh-mobile-settings-list', 'true')\n firstByClassSuffix(content, '_header')?.setAttribute('data-dsh-mobile-settings-header', 'true')\n firstByClassSuffix(content, '_options')?.setAttribute('data-dsh-mobile-settings-options', 'true')\n marked += 1\n }\n return marked\n}\n\nconst AUTO_HISTORY_THRESHOLD_PX = 64\n\nexport type NativeMobileLanguage = 'it' | 'en' | 'zh'\n\ninterface FileDropTarget { dispatchEvent(event: Event): boolean }\n\nfunction controlledFileDragEvent(type: 'dragover' | 'drop', files: readonly File[], initialDropEffect: DataTransfer['dropEffect']): DragEvent {\n const event = new Event(type, { bubbles: true, cancelable: true }) as DragEvent\n const dataTransfer = {\n dropEffect: initialDropEffect,\n effectAllowed: 'copy',\n files: Object.freeze([...files]),\n types: Object.freeze(['Files']),\n } as unknown as DataTransfer\n Object.defineProperty(event, 'dataTransfer', { value: dataTransfer })\n return event\n}\n\n/** Ask the official document dragover listener whether the current composer accepts files. */\nexport function preflightComposerImageDrop(target: FileDropTarget, files: readonly File[]): boolean {\n if (files.length === 0) return false\n const event = controlledFileDragEvent('dragover', files, 'none')\n target.dispatchEvent(event)\n return event.dataTransfer?.dropEffect === 'copy'\n}\n\n/** Dispatch a real drop only after a fresh official-listener preflight; the result is not an attachment ACK. */\nexport function dispatchComposerImageDrop(target: FileDropTarget, files: readonly File[]): boolean {\n if (!preflightComposerImageDrop(target, files)) return false\n target.dispatchEvent(controlledFileDragEvent('drop', files, 'copy'))\n return true\n}\n\n/** Apply the resolved locale independently from the document's possibly different lang attribute. */\nexport function applyNativeMobileLanguageMarker(root: Pick<HTMLElement, 'dataset'>, language: NativeMobileLanguage): () => void {\n const previous = root.dataset.dshMobileLanguage\n root.dataset.dshMobileLanguage = language\n return () => {\n if (root.dataset.dshMobileLanguage !== language) return\n if (previous === undefined) delete root.dataset.dshMobileLanguage\n else root.dataset.dshMobileLanguage = previous\n }\n}\n\n/** Resolve the supported language used by native-mobile controls. */\nexport function resolveNativeMobileLanguage(\n documentLanguage: string,\n browserLanguages: readonly string[],\n): NativeMobileLanguage {\n return [documentLanguage, ...browserLanguages]\n .map(value => value.trim().toLowerCase().split(/[-_]/u)[0])\n .find((value): value is NativeMobileLanguage => value === 'it' || value === 'en' || value === 'zh') ?? 'en'\n}\n\n/** Whether a user-driven scroll moved upward into the automatic history-loading zone. */\nexport function shouldAutoLoadEarlier(previousTop: number, currentTop: number): boolean {\n return currentTop <= AUTO_HISTORY_THRESHOLD_PX && currentTop < previousTop - 0.5\n}\n\n/**\n * Whether the overlay scrim belongs on screen. The scrim exists for the\n * slide-in drawer only — it dims the page and catches the tap that closes the\n * drawer — so it is shown while that drawer is open *and* the overlay query is\n * in force. `hidden` carries the whole decision rather than the width rules\n * alone: the attribute still hides the element when the injected stylesheet\n * never lands (a CSP without inline styles, a shell that drops the <style>\n * node), where the element would otherwise fall back to the UA button box.\n */\nexport function drawerScrimVisible(sidebarCollapsed: boolean, overlayActive: boolean): boolean {\n return !sidebarCollapsed && overlayActive\n}\n\ninterface ComposerMediaOrigin {\n readonly generation: number\n readonly href: string\n readonly composer: object | null\n readonly sessionRoot: object | null\n readonly sessionId: string | null\n}\n\n/** Check that an asynchronous picker result still belongs to its originating session and composer. */\nexport function isComposerMediaOriginCurrent(\n origin: ComposerMediaOrigin,\n current: ComposerMediaOrigin & { readonly disposed: boolean; readonly composerConnected: boolean },\n): boolean {\n return !current.disposed\n && origin.generation === current.generation\n && origin.href === current.href\n && origin.composer !== null\n && current.composerConnected\n && origin.composer === current.composer\n && origin.sessionRoot !== null\n && origin.sessionRoot === current.sessionRoot\n && origin.sessionId !== null\n && origin.sessionId === current.sessionId\n}\n\n/** Add mobile semantics without replacing feature trees. */\nexport function installNativeMobileSurface(): () => void {\n document.documentElement.classList.add('dsh-native-mobile-active')\n const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language]\n const language = resolveNativeMobileLanguage(document.documentElement.lang, browserLanguages)\n const restoreLanguageMarker = applyNativeMobileLanguageMarker(document.documentElement, language)\n const label = (italian: string, english: string, chinese: string): string => language === 'it' ? italian : language === 'zh' ? chinese : english\n const mediaIcon = (): SVGSVGElement => {\n const namespace = 'http://www.w3.org/2000/svg'\n const icon = document.createElementNS(namespace, 'svg')\n icon.setAttribute('viewBox', '0 0 16 16')\n icon.setAttribute('fill', 'none')\n icon.setAttribute('aria-hidden', 'true')\n const body = document.createElementNS(namespace, 'path')\n body.setAttribute('d', 'M5.15 3.2 6.05 2h3.9l.9 1.2h1.45c1.05 0 1.9.85 1.9 1.9v6c0 1.05-.85 1.9-1.9 1.9H3.7a1.9 1.9 0 0 1-1.9-1.9v-6c0-1.05.85-1.9 1.9-1.9h1.45Zm-1.45 1.3a.6.6 0 0 0-.6.6v6c0 .33.27.6.6.6h8.6a.6.6 0 0 0 .6-.6v-6a.6.6 0 0 0-.6-.6h-2.1l-.9-1.2H6.7l-.9 1.2H3.7Z')\n body.setAttribute('fill', 'currentColor')\n const lens = document.createElementNS(namespace, 'circle')\n lens.setAttribute('cx', '8')\n lens.setAttribute('cy', '8.1')\n lens.setAttribute('r', '2.15')\n lens.setAttribute('stroke', 'currentColor')\n lens.setAttribute('stroke-width', '1.3')\n icon.append(body, lens)\n return icon\n }\n const createMediaAction = (text: string): HTMLButtonElement => {\n const button = document.createElement('button')\n button.type = 'button'\n button.className = 'dsh-mobile-media-action'\n button.lang = language\n button.dataset.dshMobileMediaAction = 'camera'\n const icon = document.createElement('span')\n icon.append(mediaIcon())\n button.append(icon)\n const caption = document.createElement('span')\n caption.textContent = text\n button.append(caption)\n return button\n }\n const setInputMode = (mode: 'keyboard' | 'touch'): void => {\n document.documentElement.dataset.dshMobileInput = mode\n }\n const onPointerDown = (event: PointerEvent): void => {\n if (event.pointerType === 'touch' || event.pointerType === 'pen') setInputMode('touch')\n }\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Tab' || event.key.startsWith('Arrow')) setInputMode('keyboard')\n }\n document.addEventListener('pointerdown', onPointerDown, true)\n document.addEventListener('keydown', onKeyDown, true)\n // The scrim is drawer chrome: it belongs on screen only while the overlay\n // query matches. A resize crosses that breakpoint without touching the DOM\n // the observer below watches, so the query wakes the same sync pass itself.\n const overlayQuery = window.matchMedia(NATIVE_MOBILE_OVERLAY_QUERY)\n const backdrop = document.createElement('button')\n backdrop.type = 'button'\n backdrop.className = 'dsh-native-mobile-backdrop'\n backdrop.lang = language\n backdrop.hidden = true\n backdrop.setAttribute('aria-label', label('Chiudi navigazione area di lavoro', 'Close workspace navigation', '关闭工作区导航'))\n document.body.append(backdrop)\n const branchToast = document.createElement('div')\n branchToast.className = 'dsh-mobile-branch-toast'\n branchToast.lang = language\n branchToast.setAttribute('role', 'status')\n branchToast.setAttribute('aria-live', 'polite')\n document.body.append(branchToast)\n const mediaToast = document.createElement('div')\n mediaToast.className = 'dsh-mobile-media-toast'\n mediaToast.lang = language\n mediaToast.setAttribute('role', 'status')\n mediaToast.setAttribute('aria-live', 'polite')\n document.body.append(mediaToast)\n const cameraButton = createMediaAction(label('Scatta foto', 'Take photo', '拍照'))\n cameraButton.disabled = true\n let branchToastTimer = 0\n let mediaToastTimer = 0\n const showMediaToast = (message: string): void => {\n mediaToast.textContent = message\n mediaToast.dataset.visible = 'true'\n if (mediaToastTimer !== 0) window.clearTimeout(mediaToastTimer)\n mediaToastTimer = window.setTimeout(() => {\n mediaToast.removeAttribute('data-visible')\n mediaToastTimer = 0\n }, 2200)\n }\n let mediaRequestGeneration = 0\n let disposed = false\n let boundComposer: Element | null = null\n let boundSessionRoot: Element | null = null\n let boundSessionId: string | null = null\n const preflightFile = new File([], 'dsh-mobile-preflight.png', { type: 'image/png' })\n const canAcceptComposerDrop = (): boolean => preflightComposerImageDrop(document, [preflightFile])\n const mediaPickerAbortController = new AbortController()\n const browserPickerCleanups = new Set<() => void>()\n const sessionTokens = new WeakMap<Element, string>()\n let nextSessionToken = 0\n type MediaRequestContext = ComposerMediaOrigin & { readonly composer: Element | null; readonly sessionRoot: Element | null }\n const currentSessionOrigin = (): { readonly sessionRoot: Element | null; readonly sessionId: string | null } => {\n const dedicatedRoot = boundComposer?.closest('[data-dsh-mobile-session]') ?? null\n const dedicatedId = dedicatedRoot?.getAttribute('data-dsh-mobile-session')\n if (dedicatedRoot !== null && typeof dedicatedId === 'string' && dedicatedId !== '') {\n return { sessionRoot: dedicatedRoot, sessionId: dedicatedId }\n }\n const selectedRow = document.querySelector<Element>('[role=\"treeitem\"][aria-selected=\"true\"]')\n if (selectedRow === null) return { sessionRoot: null, sessionId: null }\n let token = sessionTokens.get(selectedRow)\n if (token === undefined) { token = `stock-${String(++nextSessionToken)}`; sessionTokens.set(selectedRow, token) }\n const identity = selectedRow.getAttribute('data-session-id')\n ?? selectedRow.getAttribute('aria-label')\n ?? selectedRow.textContent?.trim()\n ?? ''\n return { sessionRoot: selectedRow, sessionId: `${token}:${identity}` }\n }\n const mediaRequestContext = (): MediaRequestContext => {\n const session = currentSessionOrigin()\n return {\n generation: ++mediaRequestGeneration,\n href: window.location.href,\n composer: boundComposer,\n ...session,\n }\n }\n const mediaRequestIsCurrent = (context: MediaRequestContext): boolean => {\n const session = currentSessionOrigin()\n const composer = boundComposer\n return isComposerMediaOriginCurrent(context, {\n generation: mediaRequestGeneration,\n href: window.location.href,\n composer,\n sessionRoot: session.sessionRoot,\n sessionId: session.sessionId,\n disposed,\n composerConnected: context.composer?.isConnected === true,\n })\n }\n const deliverImages = (files: readonly File[], context: ReturnType<typeof mediaRequestContext>): void => {\n if (files.length === 0 || !mediaRequestIsCurrent(context)) return\n dispatchComposerImageDrop(document, files)\n }\n const launchBrowserCamera = (context: ReturnType<typeof mediaRequestContext>): void => {\n const input = document.createElement('input')\n input.type = 'file'\n input.accept = 'image/*'\n input.capture = 'environment'\n input.hidden = true\n const signal = mediaPickerAbortController.signal\n let cleanupTimer = 0\n let watchdogTimer = 0\n let cleaned = false\n const scheduleCleanup = (): void => {\n if (!cleaned && cleanupTimer === 0) cleanupTimer = window.setTimeout(cleanup, 1000)\n }\n const onVisibilityChange = (): void => {\n if (document.visibilityState === 'visible') scheduleCleanup()\n }\n const onChange = (): void => {\n if (cleaned) return\n const files = input.files === null ? [] : [...input.files]\n cleanup()\n deliverImages(files, context)\n }\n const cleanup = (): void => {\n if (cleaned) return\n cleaned = true\n if (cleanupTimer !== 0) window.clearTimeout(cleanupTimer)\n if (watchdogTimer !== 0) window.clearTimeout(watchdogTimer)\n cleanupTimer = 0\n watchdogTimer = 0\n input.removeEventListener('change', onChange)\n input.removeEventListener('cancel', cleanup)\n window.removeEventListener('focus', scheduleCleanup)\n document.removeEventListener('visibilitychange', onVisibilityChange)\n signal.removeEventListener('abort', cleanup)\n browserPickerCleanups.delete(cleanup)\n input.remove()\n }\n input.addEventListener('change', onChange)\n input.addEventListener('cancel', cleanup)\n window.addEventListener('focus', scheduleCleanup)\n document.addEventListener('visibilitychange', onVisibilityChange)\n signal.addEventListener('abort', cleanup, { once: true })\n watchdogTimer = window.setTimeout(cleanup, 300_000)\n browserPickerCleanups.add(cleanup)\n if (signal.aborted) { cleanup(); return }\n try {\n document.body.append(input)\n input.click()\n } catch {\n cleanup()\n if (mediaRequestIsCurrent(context)) showMediaToast(label('Impossibile aprire la fotocamera', 'Could not open the camera', '无法打开相机'))\n }\n }\n const dismissCommandMenu = (): void => {\n const trigger = boundComposer?.querySelector<HTMLButtonElement>('button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]')\n trigger?.click()\n }\n const addGroupLabel = label('Aggiungi', 'Add', '添加')\n const placeCameraAction = (commandMenu: HTMLElement): boolean => {\n const sectionTitle = Array.from(commandMenu.querySelectorAll<HTMLElement>('[role=\"listbox\"] [role=\"presentation\"]'))\n .find(candidate => !candidate.hasAttribute('data-source') && candidate.textContent?.trim() === addGroupLabel)\n if (sectionTitle === undefined) {\n cameraButton.remove()\n return false\n }\n\n // Reuse the current DSH option classes so this injected action follows the\n // native menu's spacing, colors, and dark-mode states instead of creating a\n // second mobile-only visual language.\n cameraButton.className = 'dsh-mobile-media-action'\n const nativeItem = (() => {\n let sibling = sectionTitle.nextElementSibling\n while (sibling instanceof HTMLElement && sibling.getAttribute('role') !== 'presentation') {\n if (sibling.matches('button[role=\"option\"]:not([aria-selected=\"true\"])')) return sibling\n sibling = sibling.nextElementSibling\n }\n return undefined\n })()\n if (nativeItem !== undefined) {\n cameraButton.className = nativeItem.className\n const cameraIcon = cameraButton.firstElementChild\n const cameraLabel = cameraButton.lastElementChild\n const nativeIcon = firstByClassSuffix(nativeItem, '_itemIcon')\n const nativeLabel = firstByClassSuffix(nativeItem, '_itemName')\n if (cameraIcon instanceof HTMLElement && nativeIcon !== undefined) cameraIcon.className = nativeIcon.className\n if (cameraLabel instanceof HTMLElement && nativeLabel !== undefined) cameraLabel.className = nativeLabel.className\n }\n cameraButton.setAttribute('role', 'option')\n cameraButton.setAttribute('aria-selected', 'false')\n cameraButton.setAttribute('aria-label', label('Scatta foto', 'Take photo', '拍照'))\n\n let lastItem: Element = sectionTitle\n let sibling = sectionTitle.nextElementSibling\n while (sibling instanceof HTMLElement && sibling.getAttribute('role') !== 'presentation') {\n if (sibling !== cameraButton) lastItem = sibling\n sibling = sibling.nextElementSibling\n }\n if (cameraButton.parentElement !== sectionTitle.parentElement || cameraButton.previousElementSibling !== lastItem) {\n lastItem.after(cameraButton)\n }\n return true\n }\n const setCameraActionDisabled = (disabled: boolean, title: string): void => {\n if (cameraButton.disabled !== disabled) cameraButton.disabled = disabled\n if (cameraButton.title !== title) cameraButton.title = title\n }\n const quietMediaPointer = (event: PointerEvent): void => {\n event.preventDefault()\n event.stopPropagation()\n const active = document.activeElement\n if (active instanceof HTMLElement && (active.matches('input,textarea') || active.isContentEditable)) active.blur()\n }\n const takePhoto = (): void => {\n if (!canAcceptComposerDrop()) {\n setCameraActionDisabled(true, label('Allegati immagine non disponibili', 'Image attachments are unavailable', '图片附件不可用'))\n dismissCommandMenu()\n return\n }\n const context = mediaRequestContext()\n dismissCommandMenu()\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined) {\n launchBrowserCamera(context)\n return\n }\n void Promise.resolve().then(() => bridge.invoke('camera.capture', {})).then(value => {\n if (!mediaRequestIsCurrent(context)) return\n if (value instanceof File) deliverImages([value], context)\n else showMediaToast(label('La foto scattata non è utilizzabile', 'The captured photo is unavailable', '所拍照片不可用'))\n }).catch((error: unknown) => {\n if (!mediaRequestIsCurrent(context)) return\n const code = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : ''\n if (code === 'cancelled') return\n showMediaToast(code === 'payload_too_large'\n ? label('La foto supera il limite di 8 MiB', 'The photo exceeds the 8 MiB limit', '照片超过 8 MiB 限制')\n : label('Impossibile aggiungere la foto', 'Could not attach the photo', '无法附加照片'))\n })\n }\n cameraButton.addEventListener('pointerdown', quietMediaPointer)\n cameraButton.addEventListener('click', takePhoto)\n const showBranchToast = (): void => {\n const header = document.querySelector<HTMLElement>('[data-dsh-mobile-header]')\n const title = header === null ? undefined : header.querySelector<HTMLElement>('[class*=\"_crumbCurrent\"]')?.textContent?.trim()\n const prefix = label('Ramo corrente', 'Current branch', '当前分支')\n branchToast.textContent = title === undefined ? prefix : `${prefix}: ${title}`\n branchToast.dataset.visible = 'true'\n if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer)\n branchToastTimer = window.setTimeout(() => {\n branchToast.removeAttribute('data-visible')\n branchToastTimer = 0\n }, 1600)\n }\n const onBranchClick = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const branch = event.target.closest<HTMLButtonElement>('button[aria-label*=\"分支\"],button[aria-label*=\"Branch\"],button[aria-label*=\"branch\"],button[aria-label*=\"Ramo\"],button[aria-label*=\"ramo\"]')\n if (branch === null || branch.hasAttribute('disabled') || branch.getAttribute('aria-disabled') === 'true') return\n window.setTimeout(showBranchToast, 80)\n }\n document.addEventListener('click', onBranchClick, true)\n let frame: HTMLElement | undefined\n let sidebar: HTMLElement | undefined\n let sidebarRoot: HTMLElement | undefined\n let toggle: HTMLButtonElement | undefined\n let viewArea: HTMLElement | undefined\n let scheduled = 0\n let transitionFrame = 0\n let transitionRestartFrame = 0\n let transitionTimer = 0\n let transitionTarget: HTMLElement | undefined\n let historyScroller: HTMLElement | undefined\n let historyPreviousTop = 0\n const historyLoadButton = (): HTMLButtonElement | undefined => {\n const loader = historyScroller === undefined ? undefined : firstByClassSuffix(historyScroller, '_older')\n return loader?.querySelector<HTMLButtonElement>('button') ?? undefined\n }\n const onHistoryScroll = (): void => {\n if (historyScroller === undefined) return\n const currentTop = Math.max(0, historyScroller.scrollTop)\n const shouldLoad = shouldAutoLoadEarlier(historyPreviousTop, currentTop)\n historyPreviousTop = currentTop\n if (!shouldLoad) return\n const button = historyLoadButton()\n if (button === undefined || button.disabled || button.getAttribute('aria-disabled') === 'true') return\n button.click()\n }\n const bindHistoryScroller = (next: HTMLElement | undefined): void => {\n if (historyScroller === next) return\n historyScroller?.removeEventListener('scroll', onHistoryScroll)\n historyScroller = next\n historyPreviousTop = next?.scrollTop ?? 0\n historyScroller?.addEventListener('scroll', onHistoryScroll, { passive: true })\n }\n const animateNavigation = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const trigger = event.target.closest<HTMLElement>('button,a,[role=\"tab\"],[aria-selected]')\n if (trigger === null || trigger.hasAttribute('disabled') || trigger.getAttribute('aria-disabled') === 'true') return\n if (trigger.getAttribute('aria-selected') === 'true' || trigger.getAttribute('aria-current') === 'true') return\n const settingsNavigation = trigger.closest('[data-dsh-mobile-settings-list]') !== null\n const conversationNavigation = trigger.matches('[role=\"tab\"]')\n const sidebarNavigation = trigger.closest('[data-dsh-mobile-sidebar-root]') !== null\n && trigger.closest('[data-dsh-mobile-toggle]') === null\n if (!settingsNavigation && !conversationNavigation && !sidebarNavigation) return\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return\n if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame)\n if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame)\n transitionFrame = requestAnimationFrame(() => {\n transitionFrame = 0\n const target = settingsNavigation\n ? document.querySelector<HTMLElement>('[data-dsh-mobile-settings-content]')\n : viewArea\n if (target === null || target === undefined) return\n transitionTarget?.removeAttribute('data-dsh-mobile-view-transition')\n target.removeAttribute('data-dsh-mobile-view-transition')\n transitionRestartFrame = requestAnimationFrame(() => {\n transitionRestartFrame = 0\n transitionTarget = target\n target.dataset.dshMobileViewTransition = 'true'\n if (transitionTimer !== 0) clearTimeout(transitionTimer)\n transitionTimer = window.setTimeout(() => {\n target.removeAttribute('data-dsh-mobile-view-transition')\n if (transitionTarget === target) transitionTarget = undefined\n transitionTimer = 0\n }, 240)\n })\n })\n }\n document.addEventListener('click', animateNavigation)\n const syncMediaBinding = (composer: Element | null): void => {\n const previousComposer = boundComposer\n boundComposer = composer\n const session = currentSessionOrigin()\n if (composer === previousComposer && session.sessionRoot === boundSessionRoot && session.sessionId === boundSessionId) return\n boundSessionRoot = session.sessionRoot\n boundSessionId = session.sessionId\n mediaRequestGeneration += 1\n }\n\n const sync = (): void => {\n scheduled = 0\n const dedicatedCenter = document.querySelector<HTMLElement>('.dshm-main') ?? undefined\n const nextFrame = resolveNativeMobileFrame(document, dedicatedCenter)\n if (frame !== nextFrame) frame?.removeAttribute('data-dsh-mobile-frame')\n frame = nextFrame\n if (frame !== undefined) frame.dataset.dshMobileFrame = 'true'\n sidebar = frame === undefined\n ? document.querySelector<HTMLElement>('.dshm-drawer') ?? undefined\n : firstByClassSuffix(frame, '_sidebarCol')\n const center = frame === undefined ? dedicatedCenter : firstByClassSuffix(frame, '_centerCol')\n const details = frame === undefined ? undefined : firstByClassSuffix(frame, '_detailsCol')\n const handle = frame === undefined ? undefined : firstByClassSuffix(frame, '_handle')\n markNativeMobileSettings(document)\n if (center === undefined) {\n bindHistoryScroller(undefined)\n syncMediaBinding(null)\n setCameraActionDisabled(true, label('Apri prima una sessione', 'Open a session first', '请先打开会话'))\n cameraButton.remove()\n return\n }\n if (center !== undefined) {\n center.dataset.dshMobileCenter = 'true'\n center.querySelector<HTMLElement>('header')?.setAttribute('data-dsh-mobile-header', 'true')\n viewArea = firstByClassSuffix(center, '_viewArea')\n if (viewArea !== undefined) viewArea.dataset.dshMobileView = 'true'\n const conversation = center.querySelector<HTMLElement>('[data-conversation-scroll]')\n bindHistoryScroller(conversation ?? undefined)\n const historyLoader = conversation === null ? undefined : firstByClassSuffix(conversation, '_older')\n if (historyLoader !== undefined) {\n historyLoader.dataset.dshMobileHistoryLoader = 'true'\n historyLoader.setAttribute('aria-live', 'polite')\n const button = historyLoader.querySelector<HTMLButtonElement>('button')\n if (button !== null) {\n button.tabIndex = -1\n if (button.disabled) button.removeAttribute('aria-hidden')\n else button.setAttribute('aria-hidden', 'true')\n }\n }\n const messageColumn = conversation === null ? undefined : firstByClassSuffix(conversation, '_column')\n const messageScroll = messageColumn?.parentElement\n if (messageColumn !== undefined && messageScroll !== null && messageScroll !== undefined && classToken(messageScroll, '_scroll')) {\n messageColumn.dataset.dshMobileMessageColumn = 'true'\n messageScroll.dataset.dshMobileMessageScroll = 'true'\n }\n for (const table of center.querySelectorAll<HTMLTableElement>('table')) {\n const parent = table.parentElement\n if (parent?.dataset.dshMobileTableScroll === 'true') continue\n const wrapper = document.createElement('div')\n wrapper.dataset.dshMobileTableScroll = 'true'\n table.before(wrapper)\n wrapper.append(table)\n }\n const composerCard = center.querySelector<HTMLElement>('[data-composer-card]')\n const composerRow = composerCard?.querySelector<HTMLElement>(':scope > [data-input-scroll]')?.nextElementSibling\n if (!(composerRow instanceof HTMLElement)) {\n syncMediaBinding(null)\n setCameraActionDisabled(true, label('Apri prima una sessione', 'Open a session first', '请先打开会话'))\n cameraButton.remove()\n }\n if (composerRow instanceof HTMLElement) {\n composerRow.dataset.dshMobileComposerRow = 'true'\n const groups = Array.from(composerRow.children).filter((child): child is HTMLElement => child instanceof HTMLElement)\n const composerTools = groups[0]\n const composerTrailing = groups.at(-1)\n if (composerTools !== undefined) {\n composerTools.dataset.dshMobileComposerTools = 'true'\n syncMediaBinding(composerCard ?? null)\n const composerInput = composerCard?.querySelector<HTMLTextAreaElement>('textarea')\n const composerEditor = composerCard?.querySelector<HTMLElement>('[contenteditable=\"true\"],[contenteditable=\"plaintext-only\"]')\n const composerBusy = composerCard?.getAttribute('aria-busy') === 'true'\n || composerInput?.disabled === true\n || composerInput?.readOnly === true\n || composerEditor?.getAttribute('aria-disabled') === 'true'\n const attachmentBlocked = !canAcceptComposerDrop()\n const mediaDisabled = conversation === null || currentSessionOrigin().sessionId === null || composerBusy || attachmentBlocked\n const mediaTitle = composerBusy\n ? label('Attendi il completamento della risposta', 'Wait for the response to finish', '请等待回复完成')\n : attachmentBlocked\n ? label('Allegati immagine non disponibili', 'Image attachments are unavailable', '图片附件不可用')\n : mediaDisabled\n ? label('Apri prima una sessione', 'Open a session first', '请先打开会话')\n : label('Scatta una foto e allegala', 'Take a photo and attach it', '拍照并附加到当前消息')\n setCameraActionDisabled(mediaDisabled, mediaTitle)\n const commandMenu = composerCard?.querySelector<HTMLElement>('[data-trigger-menu]') ?? null\n if (commandMenu !== null) placeCameraAction(commandMenu)\n else cameraButton.remove()\n }\n if (composerTrailing !== undefined && composerTrailing !== composerTools) {\n composerTrailing.dataset.dshMobileComposerTrailing = 'true'\n const modelTrigger = composerTrailing.querySelector<HTMLButtonElement>('button[aria-label^=\"选择模型\"],button[aria-label^=\"Select model\"],button[aria-label^=\"Seleziona modello\"]')\n if (modelTrigger !== null) {\n modelTrigger.dataset.dshMobileComposerModelTrigger = 'true'\n modelTrigger.parentElement?.setAttribute('data-dsh-mobile-composer-model', 'true')\n modelTrigger.querySelector<HTMLElement>('[class*=\"_triggerLabel\"]')?.setAttribute('data-dsh-mobile-composer-model-label', 'true')\n }\n }\n }\n }\n if (handle !== undefined) handle.dataset.dshMobileHandle = 'true'\n if (details !== undefined) {\n details.dataset.dshMobileDetails = 'true'\n const lastColumn = frame?.style.gridTemplateColumns.trim().split(/\\s+/).at(-1)\n details.dataset.open = String(lastColumn !== undefined && lastColumn !== '0px' && lastColumn !== '0')\n }\n if (sidebar === undefined) return\n sidebar.dataset.dshMobileSidebar = 'true'\n toggle = firstByClassSuffix(sidebar, '_toggle') as HTMLButtonElement | undefined\n let candidate = toggle?.parentElement\n while (candidate !== undefined && candidate !== null && candidate !== sidebar && !classToken(candidate, '_root')) candidate = candidate.parentElement\n sidebarRoot = candidate !== sidebar && candidate !== null ? candidate : undefined\n if (sidebarRoot === undefined) return\n sidebarRoot.dataset.dshMobileSidebarRoot = 'true'\n for (const brand of sidebarRoot.querySelectorAll<HTMLElement>('[class*=\"_fallbackBrandName\"]')) {\n if (brand.textContent?.trim() === 'DSH Local Build') brand.textContent = 'DeepSeek Harness'\n }\n if (toggle !== undefined) toggle.dataset.dshMobileToggle = 'true'\n const collapsed = classToken(sidebarRoot, '_collapsed')\n sidebar.dataset.open = String(!collapsed)\n backdrop.hidden = !drawerScrimVisible(collapsed, overlayQuery.matches)\n }\n const schedule = (): void => { if (scheduled === 0) scheduled = requestAnimationFrame(sync) }\n const observer = new MutationObserver(schedule)\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style', 'disabled', 'readonly', 'aria-busy', 'aria-selected', 'data-dsh-mobile-session'] })\n overlayQuery.addEventListener('change', schedule)\n backdrop.addEventListener('click', () => { if (sidebar?.dataset.open === 'true') toggle?.click() })\n sync()\n const disposeTaskWatcher = installTaskCompletionWatcher({\n // Current gateways announce exact root turn completion. The DOM watcher\n // remains only for explicit pending-input cards, avoiding false completion\n // reminders from unrelated page mutations.\n completionFallback: false,\n label: (kind: TaskNotifyKind, sessionLabel: string) => kind === 'done'\n ? {\n title: label('Attività completata', 'Task finished', '任务已完成'),\n body: sessionLabel === ''\n ? label('Il tuo task DSH è terminato', 'Your DSH task finished', '你的 DSH 任务已完成')\n : label(`Il tuo task DSH è terminato: ${sessionLabel}`, `Your DSH task finished: ${sessionLabel}`, `你的 DSH 任务已完成:${sessionLabel}`),\n }\n : {\n title: label('È richiesto un input', 'Input needed', '需要你确认'),\n body: label('DSH attende una tua scelta', 'DSH waits for your choice', 'DSH 等待你的选择'),\n },\n })\n return () => {\n disposed = true\n disposeTaskWatcher()\n mediaRequestGeneration += 1\n mediaPickerAbortController.abort()\n restoreLanguageMarker()\n for (const cleanup of [...browserPickerCleanups]) cleanup()\n observer.disconnect()\n overlayQuery.removeEventListener('change', schedule)\n document.removeEventListener('click', onBranchClick, true)\n cameraButton.removeEventListener('pointerdown', quietMediaPointer)\n cameraButton.removeEventListener('click', takePhoto)\n if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer)\n if (mediaToastTimer !== 0) window.clearTimeout(mediaToastTimer)\n branchToast.remove()\n mediaToast.remove()\n cameraButton.remove()\n if (scheduled !== 0) cancelAnimationFrame(scheduled)\n if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame)\n if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame)\n if (transitionTimer !== 0) clearTimeout(transitionTimer)\n transitionTarget?.removeAttribute('data-dsh-mobile-view-transition')\n historyScroller?.removeEventListener('scroll', onHistoryScroll)\n document.removeEventListener('pointerdown', onPointerDown, true)\n document.removeEventListener('keydown', onKeyDown, true)\n document.removeEventListener('click', animateNavigation)\n backdrop.remove()\n document.documentElement.classList.remove('dsh-native-mobile-active')\n delete document.documentElement.dataset.dshMobileInput\n }\n}\n","/** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */\n\nfunction unwrapBrackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n}\n\nfunction parseIpv4(hostname: string): readonly [number, number, number, number] | undefined {\n const parts = hostname.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9]\\d{0,2})$/u.test(part)) return undefined\n const value = Number(part)\n if (value > 255) return undefined\n octets.push(value)\n }\n return octets as [number, number, number, number]\n}\n\nfunction isLoopbackIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 127\n}\n\nfunction isPrivateIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 10\n || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)\n || (octets[0] === 192 && octets[1] === 168)\n}\n\nfunction isLinkLocalIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 169 && octets[1] === 254\n}\n\n/**\n * Hostnames that may appear on the DSH desktop admin surface.\n * Loopback, RFC1918, and IPv4 link-local are accepted. Public IPs,\n * CGNAT, and arbitrary DNS names stay rejected so DNS rebinding\n * cannot reach `/api/mobile-access`.\n */\nexport function isLocalAdminHostname(hostname: string): boolean {\n const host = unwrapBrackets(hostname).trim().toLowerCase()\n if (host === 'localhost' || host === '::1') return true\n const octets = parseIpv4(host)\n if (octets === undefined) return false\n return isLoopbackIpv4(octets) || isPrivateIpv4(octets) || isLinkLocalIpv4(octets)\n}\n\n/**\n * Whether the current browser document should mount the desktop Mobile access\n * control. Dedicated Mobile HTTPS (the phone surface) stays native even when\n * the Host is a private LAN address.\n */\nexport function isDesktopAdminSurface(\n hostname: string,\n search = '',\n frontend?: string,\n): boolean {\n const query = search.startsWith('?') ? search.slice(1) : search\n return isLocalAdminHostname(hostname)\n && frontend !== 'dedicated'\n && !new URLSearchParams(query).has('dsh-mobile-preview')\n}\n","import { createElement, useEffect, useState } from 'react'\nimport {\n DIAGNOSTIC_REASON_MESSAGES,\n LOCALIZED_DIAGNOSTIC_COPY,\n MOBILE_CONTROL_MESSAGES,\n type MobileControlLocale,\n} from './client-messages.js'\nimport { createRestrictedFrpServerTemplate } from './frp-template.js'\nimport { installNativeMobileSurface, NATIVE_MOBILE_STYLES, resolveNativeMobileLanguage } from './native-mobile.js'\nimport { isDesktopAdminSurface } from './local-admin-host.js'\nimport { fireDeviceRevoked, fireTaskNotifyEvent, isDeviceRevokedPayload, parseTaskNotifyPayload, taskCompletionTag } from './task-notify.js'\n\nexport { DIAGNOSTIC_REASON_MESSAGES, LOCALIZED_DIAGNOSTIC_COPY, MOBILE_CONTROL_MESSAGES } from './client-messages.js'\nexport type { MobileControlLocale } from './client-messages.js'\n\n/**\n * Remote provider names the control UI knows about.\n *\n * This mirrors the server-side union in `src/remote.ts`, which cannot be imported\n * here because it pulls in Node-only modules. An unrecognized value from the\n * host falls back to the built-in provider, so a missing name would silently\n * render another provider's wording instead of failing loudly.\n */\nexport type MobileRemoteProvider = 'tailscale' | 'cpolar' | 'cloudflared' | 'frp' | 'origin'\n\n/** Map a host-reported provider onto the UI union, defaulting to the built-in one. */\nexport function parseMobileRemoteProvider(value: unknown): MobileRemoteProvider {\n return value === 'cpolar' || value === 'cloudflared' || value === 'frp' || value === 'origin'\n ? value\n : 'tailscale'\n}\n\ninterface ClientContext {\n effect(effect: () => void | (() => void), label?: string): void\n get(name: 'connection'): MobileConnectionHandle\n slots: {\n inject(key: string, callback: () => (() => void)): () => void\n register<Props>(options: { name: string; id: string; order?: number; label?: string }, component: (props: Props) => unknown): () => void\n }\n}\n\ninterface MobileConnectionHandle {\n isLoopback: boolean\n}\n\ninterface MobileExtensionContext {\n readonly document: Document\n readonly request: (path: string, init?: RequestInit) => Promise<Response>\n readonly root: HTMLElement\n readonly window: Window\n}\n\ntype MobileExtensionMount = (context: MobileExtensionContext) => void | (() => void)\ntype MobileSurfacePlacement = 'page' | 'sidebar-action' | 'header-action' | 'composer-dock' | 'settings-section' | 'overlay'\ninterface MobileSurface {\n readonly id: string\n readonly placement: MobileSurfacePlacement\n readonly label: string\n mount(container: HTMLElement): void | (() => void)\n}\ninterface MobileClientApi {\n readonly host: {\n invoke(action: string, input: unknown): Promise<unknown>\n fetch(path: string, init?: RequestInit): Promise<Response>\n assetUrl(path: string): string\n }\n readonly ui: {\n registerSurface(surface: MobileSurface): () => void\n open(surfaceId: string): void\n close(surfaceId: string): void\n toast(message: string): void\n }\n readonly native: {\n capabilities(): Promise<readonly string[]>\n invoke(action: string, input?: unknown): Promise<unknown>\n }\n readonly signal: AbortSignal\n readonly document: Document\n readonly window: Window\n}\ninterface MobileClientDefinition {\n readonly apiVersion: 1\n readonly id: string\n activate(api: MobileClientApi): void | (() => void) | Promise<void | (() => void)>\n}\n\ndeclare global {\n interface Window {\n dshMobile?: {\n register(mount: MobileExtensionMount): void\n define(definition: MobileClientDefinition): void\n }\n __DSH_MOBILE_FRONTEND__?: 'dedicated'\n __DSH_MOBILE_NATIVE__?: {\n capabilities(): Promise<readonly string[]> | readonly string[]\n invoke(action: string, input?: unknown): Promise<unknown>\n }\n }\n}\n\nconst queuedDefinitions: MobileClientDefinition[] = []\nlet queuedLegacyMount: MobileExtensionMount | undefined\nif (typeof window !== 'undefined' && window.dshMobile === undefined) {\n window.dshMobile = {\n register: mount => { queuedLegacyMount = mount },\n define: definition => { queuedDefinitions.push(definition) },\n }\n}\n\nexport function selectMobileControlLocale(documentLanguage = '', navigatorLanguages: readonly string[] = []): MobileControlLocale {\n for (const value of [documentLanguage, ...navigatorLanguages]) {\n const language = value.trim().toLowerCase().split(/[-_]/u)[0]\n if (language === 'it' || language === 'en' || language === 'zh') return language\n }\n return 'en'\n}\n\nexport function selectedMobileControlLocale(): MobileControlLocale {\n return selectMobileControlLocale(document.documentElement.lang, navigator.languages?.length ? navigator.languages : [navigator.language])\n}\n\n/** Remount one plugin-owned surface when DSH changes the document language. */\nexport function installDshLanguageBoundSurface(install: () => () => void): () => void {\n let locale = selectedMobileControlLocale()\n let dispose = install()\n const observer = new MutationObserver(() => {\n const next = selectedMobileControlLocale()\n if (next === locale) return\n dispose()\n locale = next\n dispose = install()\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] })\n return () => {\n observer.disconnect()\n dispose()\n }\n}\n\nfunction controlTranslator(locale = selectedMobileControlLocale()): (key: string, values?: Readonly<Record<string, string | number>>) => string {\n const messages = MOBILE_CONTROL_MESSAGES as Record<MobileControlLocale, Record<string, string>>\n return (key, values = {}) => {\n const template = messages[locale][key] ?? messages.en[key] ?? key\n return template.replace(/\\{(\\w+)\\}/gu, (_match: string, name: string) => String(values[name] ?? `{${name}}`))\n }\n}\n\nexport function normalizeDiagnosticOverall(value: unknown): 'ok' | 'attention' | 'error' {\n return value === 'ok' ? 'ok' : value === 'attention' ? 'attention' : 'error'\n}\n\nexport function normalizeDiagnosticStatus(value: unknown): 'ok' | 'warning' | 'error' | 'info' {\n return value === 'ok' || value === 'warning' || value === 'error' || value === 'info' ? value : 'error'\n}\n\nexport function diagnosticOverallForChecks(value: unknown, statuses: readonly unknown[]): 'ok' | 'attention' | 'error' {\n const normalized = statuses.map(normalizeDiagnosticStatus)\n const payloadOverall = normalizeDiagnosticOverall(value)\n if (payloadOverall === 'error' || normalized.includes('error')) return 'error'\n if (payloadOverall === 'attention' || normalized.includes('warning')) return 'attention'\n return 'ok'\n}\n\nexport function validateDiagnosticChecks(value: unknown): { readonly entries: readonly Record<string, unknown>[]; readonly malformed: boolean } {\n if (!Array.isArray(value)) return { entries: [], malformed: true }\n const entries: Record<string, unknown>[] = []\n let malformed = false\n for (const candidate of value) {\n if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) { malformed = true; continue }\n entries.push(candidate as Record<string, unknown>)\n }\n return { entries, malformed }\n}\n\nexport function diagnosticEntriesForRender(data: Readonly<Record<string, unknown>>): readonly Record<string, unknown>[] {\n const overallKnown = data.overall === 'ok' || data.overall === 'attention' || data.overall === 'error'\n const validated = validateDiagnosticChecks(data.checks)\n const statusesKnown = validated.entries.every(entry => entry.status === 'ok' || entry.status === 'warning' || entry.status === 'error' || entry.status === 'info')\n if (!overallKnown || validated.malformed || validated.entries.length === 0 || !statusesKnown) {\n throw new TypeError('diagnostics envelope is unavailable')\n }\n return validated.entries\n}\n\nexport function renderDiagnosticPayloadSafely(\n data: Record<string, unknown>,\n render: (data: Record<string, unknown>) => void,\n onFailure: (error: unknown) => void,\n): void {\n try { render(data) } catch (error) { onFailure(error) }\n}\n\nexport function diagnosticServerCopy(entry: Readonly<Record<string, unknown>>): { readonly label: string; readonly detail: string; readonly action: string } {\n return {\n label: typeof entry.label === 'string' ? entry.label : '',\n detail: typeof entry.detail === 'string' ? entry.detail : '',\n action: typeof entry.action === 'string' ? entry.action : '',\n }\n}\n\n/**\n * Match DSH's client-side privilege hint to the authenticated mobile gateway.\n * The gateway authenticates the paired device and forwards allowed requests to\n * DSH's loopback listener, so settings RPCs receive the same Host-side checks\n * as the desktop page even though the phone's visible URL is a LAN address.\n */\nexport function trustAuthenticatedGatewayConnection(connection: MobileConnectionHandle): () => void {\n const previous = connection.isLoopback\n connection.isLoopback = true\n return () => { connection.isLoopback = previous }\n}\n\nfunction element<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag)\n if (className !== undefined) node.className = className\n return node\n}\n\ninterface MobileSwitchComputerCopy {\n readonly title: string\n readonly description: string\n readonly action: string\n readonly busy: string\n readonly retry: string\n readonly error: string\n}\n\nfunction mobileSwitchComputerCopy(locale: MobileControlLocale): MobileSwitchComputerCopy {\n if (locale === 'zh') return {\n title: '切换电脑',\n description: '返回已配对设备列表,选择要打开的电脑。',\n action: '切换电脑',\n busy: '正在切换…',\n retry: '重试',\n error: '切换失败,请重试。',\n }\n if (locale === 'it') return {\n title: 'Cambia computer',\n description: 'Torna all’elenco dei computer associati e scegli quale aprire.',\n action: 'Cambia computer',\n busy: 'Cambio in corso…',\n retry: 'Riprova',\n error: 'Cambio non riuscito. Riprova.',\n }\n return {\n title: 'Switch computer',\n description: 'Return to the paired-device list and choose a computer to open.',\n action: 'Switch computer',\n busy: 'Switching…',\n retry: 'Retry',\n error: 'Could not switch computers. Try again.',\n }\n}\n\n/** Render the native-app-only action in DSH's General settings row style. */\nfunction MobileSwitchComputerRow(): unknown {\n const [nativeReady, setNativeReady] = useState(false)\n const [state, setState] = useState<'idle' | 'busy' | 'error'>('idle')\n useEffect(() => {\n let disposed = false\n const checkNativeAction = (): void => {\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined) {\n setNativeReady(false)\n return\n }\n void Promise.resolve().then(() => bridge.capabilities()).then(capabilities => {\n if (!disposed) setNativeReady(capabilities.includes('mobile.switch-computer'))\n }, () => {\n if (!disposed) setNativeReady(false)\n })\n }\n checkNativeAction()\n window.addEventListener('dsh-mobile-native-ready', checkNativeAction)\n return () => {\n disposed = true\n window.removeEventListener('dsh-mobile-native-ready', checkNativeAction)\n }\n }, [])\n if (!nativeReady) return null\n const locale = selectedMobileControlLocale()\n const copy = mobileSwitchComputerCopy(locale)\n const invoke = (): void => {\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined || state === 'busy') return\n setState('busy')\n // The native action normally replaces this WebView immediately; only a\n // rejected request needs to remain visible in the settings row.\n void bridge.invoke('mobile.switch-computer', {}).catch(() => { setState('error') })\n }\n return createElement('div', { className: 'dsh-mobile-settings_row', lang: locale },\n createElement('div', { className: 'dsh-mobile-settings_rowText' },\n createElement('div', { className: 'dsh-mobile-settings_title' }, copy.title),\n createElement('div', { className: 'dsh-mobile-settings_desc', 'aria-live': 'polite' }, state === 'error' ? copy.error : copy.description),\n ),\n createElement('button', {\n type: 'button',\n className: 'dsh-mobile-settings_selector',\n disabled: state === 'busy',\n 'aria-busy': state === 'busy' ? 'true' : undefined,\n onClick: invoke,\n }, state === 'busy' ? copy.busy : state === 'error' ? copy.retry : copy.action),\n )\n}\n\nconst CONTROL_REQUEST_TIMEOUT_MS = 15_000\nconst LONG_CONTROL_REQUEST_TIMEOUT_MS = 210_000\nconst GITHUB_RELEASES_URL = 'https://github.com/saya-ch/dsh-mobile/releases'\nconst CONTROL_PANEL_ID = 'dsh-mobile-control-panel'\n\nexport interface ClientReleaseInfo {\n readonly updateAvailable: boolean\n readonly latestVersion?: string\n readonly androidVersion?: string\n readonly androidDownloadUrl: string\n /** Trimmed release notes body for the preview card (best effort). */\n readonly releaseNotes?: string\n}\n\nfunction releaseVersion(value: unknown): string | undefined {\n return typeof value === 'string'\n && /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/u.test(value)\n ? value\n : undefined\n}\n\n/** Reduce the loopback release response to trusted text and download metadata. */\nexport function clientReleaseInfo(data: Readonly<Record<string, unknown>>): ClientReleaseInfo {\n const latestVersion = releaseVersion(data.latestVersion)\n const androidVersion = releaseVersion(data.androidVersion)\n const expectedAndroidDownloadUrl = androidVersion === undefined\n ? undefined\n : `${GITHUB_RELEASES_URL}/download/v${androidVersion}/dsh-mobile-android-v${androidVersion}.apk`\n const androidDownloadUrl = expectedAndroidDownloadUrl !== undefined && data.androidDownloadUrl === expectedAndroidDownloadUrl\n ? expectedAndroidDownloadUrl\n : GITHUB_RELEASES_URL\n return {\n updateAvailable: data.updateAvailable === true && latestVersion !== undefined,\n ...(latestVersion === undefined ? {} : { latestVersion }),\n ...(androidVersion === undefined ? {} : { androidVersion }),\n androidDownloadUrl,\n ...(typeof data.releaseNotes === 'string' && data.releaseNotes !== '' ? { releaseNotes: data.releaseNotes.slice(0, 4000) } : {}),\n }\n}\n\nasync function requestJson(\n path: string,\n init?: RequestInit,\n timeoutMs = CONTROL_REQUEST_TIMEOUT_MS,\n): Promise<Record<string, unknown>> {\n const controller = new AbortController()\n const upstreamSignal = init?.signal\n const abortFromUpstream = (): void => { controller.abort(upstreamSignal?.reason) }\n if (upstreamSignal?.aborted === true) abortFromUpstream()\n else upstreamSignal?.addEventListener('abort', abortFromUpstream, { once: true })\n const timer = window.setTimeout(() => { controller.abort() }, timeoutMs)\n try {\n const response = await fetch(path, {\n ...init,\n signal: controller.signal,\n headers: { 'content-type': 'application/json', ...init?.headers },\n })\n const body = await response.json() as Record<string, unknown>\n if (!response.ok) throw new Error(typeof body.error === 'string' ? body.error : `HTTP ${String(response.status)}`)\n return body\n } catch (error) {\n if (controller.signal.aborted && upstreamSignal?.aborted !== true) {\n throw new Error(controlTranslator()('requestTimeout'))\n }\n throw error\n } finally {\n clearTimeout(timer)\n upstreamSignal?.removeEventListener('abort', abortFromUpstream)\n }\n}\n\nfunction officialFunnelSetupUrl(value: unknown): string {\n if (typeof value !== 'string' || value.length > 2048) return ''\n let url: URL\n try { url = new URL(value) } catch { return '' }\n const normalized = url.toString().replace(/\\/$/u, '')\n if (normalized === 'https://tailscale.com/s/no-funnel' || normalized === 'https://tailscale.com/s/https') return normalized\n if (url.protocol !== 'https:' || url.hostname !== 'login.tailscale.com' || url.port !== ''\n || url.username !== '' || url.password !== '') return ''\n return url.toString()\n}\n\n/** Build the copy-only restricted VPS template without sending the token to the host API. */\nexport function createFrpServerTemplateForClipboard(serverPort: number, token: string, publicOrigin: string): string {\n return createRestrictedFrpServerTemplate(serverPort, token, publicOrigin)\n}\n\nconst ORIGIN_ERROR_MESSAGE_KEYS: Readonly<Record<string, string>> = {\n origin_public_origin_invalid: 'originPublicOriginInvalid',\n origin_listen_host_invalid: 'originListenHostInvalid',\n origin_listen_port_invalid: 'originListenPortInvalid',\n origin_listen_port_reserved: 'originListenPortReserved',\n origin_allowed_cidrs_invalid: 'originAllowedCidrsInvalid',\n origin_settings_invalid: 'originSettingsInvalid',\n origin_config_missing: 'originConfigMissing',\n origin_config_invalid: 'originConfigInvalid',\n origin_config_target_invalid: 'originConfigTargetInvalid',\n origin_controller_unavailable: 'originControllerUnavailable',\n origin_listen_port_in_use: 'originPortInUse',\n origin_listen_address_unavailable: 'originAddressUnavailable',\n origin_gateway_start_failed: 'originStartFailed',\n}\n\nfunction installControl(): { remove: () => void; toggle: () => void; isOpen: () => boolean } {\n /**\n * Provider failure codes to message keys.\n *\n * Kept as keys rather than translated text so the same table can serve both the\n * status repaint and a failed request: a server rejection carries its code in\n * `error.message`, and showing that raw would print `Error: cloudflared_...` in\n * every language while the translated sentence sat unreachable.\n */\n const REMOTE_ERROR_MESSAGE_KEYS: Readonly<Record<string, string>> = {\n funnel_permission_required: 'funnelPermission',\n funnel_https_required: 'funnelHttps',\n funnel_start_failed: 'funnelStart',\n funnel_start_timeout: 'funnelTimeout',\n tailscale_dns_missing: 'tailscaleDnsMissing',\n gateway_start_failed: 'gatewayStartFailed',\n control_channel_failed: 'controlChannelFailed',\n cpolar_component_missing: 'cpolarMissing',\n cpolar_component_invalid: 'cpolarInvalid',\n cpolar_config_missing: 'cpolarConfigMissing',\n cpolar_config_invalid: 'cpolarConfigInvalid',\n cpolar_port_unavailable: 'cpolarPortUnavailable',\n cpolar_launch_failed: 'cpolarLaunchFailed',\n cpolar_start_timeout: 'cpolarTimeout',\n cpolar_stopped: 'cpolarStopped',\n cpolar_exited: 'cpolarExited',\n cpolar_invalid_output: 'cpolarOutputInvalid',\n cpolar_invalid_origin: 'cpolarOriginInvalid',\n cloudflared_component_missing: 'cloudflaredMissing',\n cloudflared_component_invalid: 'cloudflaredInvalid',\n cloudflared_port_unavailable: 'cloudflaredPortUnavailable',\n cloudflared_launch_failed: 'cloudflaredLaunchFailed',\n cloudflared_start_timeout: 'cloudflaredTimeout',\n cloudflared_stopped: 'cloudflaredStopped',\n cloudflared_exited: 'cloudflaredExited',\n cloudflared_invalid_output: 'cloudflaredOutputInvalid',\n cloudflared_invalid_origin: 'cloudflaredOriginInvalid',\n cloudflared_tunnel_port_unavailable: 'cloudflaredTunnelPortUnavailable',\n cloudflared_tunnel_hostname_invalid: 'cloudflaredTunnelHostnameInvalid',\n cloudflared_tunnel_port_invalid: 'cloudflaredTunnelPortInvalid',\n cloudflared_tunnel_port_reserved: 'cloudflaredTunnelPortReserved',\n cloudflared_tunnel_token_invalid: 'cloudflaredTunnelTokenInvalid',\n cloudflared_tunnel_settings_invalid: 'cloudflaredTunnelSettingsInvalid',\n cloudflared_tunnel_config_missing: 'cloudflaredTunnelConfigMissing',\n cloudflared_tunnel_config_invalid: 'cloudflaredTunnelInvalid',\n cloudflared_tunnel_target_invalid: 'cloudflaredTunnelTargetInvalid',\n cloudflared_component_unsupported: 'cloudflaredComponentUnsupported',\n cloudflared_download_hash_mismatch: 'cloudflaredDownloadHashMismatch',\n cloudflared_download_size_mismatch: 'cloudflaredDownloadSizeMismatch',\n cloudflared_executable_hash_mismatch: 'cloudflaredExecutableHashMismatch',\n cloudflared_download_redirect_missing: 'cloudflaredDownloadRedirectMissing',\n cloudflared_download_redirect_invalid: 'cloudflaredDownloadRedirectInvalid',\n cloudflared_download_redirect_rejected: 'cloudflaredDownloadRedirectRejected',\n cloudflared_port_reservation_failed: 'cloudflaredPortReservationFailed',\n frp_component_missing: 'frpMissing',\n frp_component_invalid: 'frpInvalid',\n frp_config_missing: 'frpConfigMissing',\n frp_config_verify_failed: 'frpConfigVerifyFailed',\n frp_vhost_publicly_reachable: 'frpVhostPublic',\n frp_vhost_probe_failed: 'frpVhostProbeFailed',\n frp_launch_failed: 'frpLaunchFailed',\n frp_start_timeout: 'frpTimeout',\n frp_discovery_mismatch: 'frpDiscoveryMismatch',\n frp_discovery_invalid: 'frpDiscoveryInvalid',\n frp_stopped: 'frpStopped',\n frp_exited: 'frpExited',\n ...ORIGIN_ERROR_MESSAGE_KEYS,\n }\n\n /** The table above, translated, for the status repaint. */\n const translatedRemoteErrors = (): Record<string, string> =>\n Object.fromEntries(Object.entries(REMOTE_ERROR_MESSAGE_KEYS).map(([code, key]) => [code, t(key)]))\n\n /**\n * Text for a failed control request.\n *\n * A rejection carries its code in `error.message`, so a known code renders its\n * translated sentence; only an unrecognized one falls back to the generic message,\n * and it shows the bare message rather than a stringified Error.\n */\n const remoteFailureTextFor = (error: unknown, fallbackKey: string): string => {\n const message = error instanceof Error ? error.message : String(error)\n const key = REMOTE_ERROR_MESSAGE_KEYS[message]\n return key === undefined ? t(fallbackKey, { error: message }) : t(key)\n }\n const locale = selectedMobileControlLocale()\n const localeTag = locale === 'it' ? 'it-IT' : locale === 'zh' ? 'zh-CN' : 'en-US'\n const t = controlTranslator(locale)\n const lifecycle = new AbortController()\n const controlRequestJson = (path: string, init?: RequestInit, timeoutMs?: number): Promise<Record<string, unknown>> => requestJson(path, { ...init, signal: lifecycle.signal }, timeoutMs)\n const root = element('div', 'dsh-mobile-control'); root.lang = locale\n const panel = element('section', 'dsh-mobile-control__panel'); panel.id = CONTROL_PANEL_ID; panel.hidden = true; panel.lang = locale\n panel.setAttribute('aria-label', t('mobileAccess'))\n const header = element('header', 'dsh-mobile-control__header')\n const title = element('h2'); title.textContent = t('mobileAccess')\n const headerActions = element('div', 'dsh-mobile-control__header-actions')\n const updatePlugin = element('button', 'dsh-mobile-control__update-plugin'); updatePlugin.type = 'button'; updatePlugin.textContent = t('updatePlugin'); updatePlugin.hidden = true\n // Preview card shown before applying an update: what’s new + notices, then confirm.\n const updateCard = element('div', 'dsh-mobile-control__update-card'); updateCard.hidden = true\n const updateCardTitle = element('div', 'dsh-mobile-control__update-card-title')\n const updateCardNotes = element('pre', 'dsh-mobile-control__update-notes'); updateCardNotes.setAttribute('tabindex', '0')\n const updateCardNotice = element('p', 'dsh-mobile-control__update-notice'); updateCardNotice.textContent = t('updateNotice')\n const updateActions = element('div', 'dsh-mobile-control__update-actions')\n const updateNow = element('button', 'dsh-mobile-control__primary'); updateNow.type = 'button'; updateNow.textContent = t('updateNow')\n const updateLater = element('button', 'dsh-mobile-control__secondary'); updateLater.type = 'button'; updateLater.textContent = t('updateLater')\n updateActions.append(updateLater, updateNow); updateCard.append(updateCardTitle, updateCardNotes, updateCardNotice, updateActions)\n const diagnosticsEntry = element('button', 'dsh-mobile-control__diagnostic-entry'); diagnosticsEntry.type = 'button'; diagnosticsEntry.textContent = t('diagnostics'); diagnosticsEntry.setAttribute('aria-label', t('openDiagnostics')); diagnosticsEntry.setAttribute('aria-pressed', 'false')\n const close = element('button', 'dsh-mobile-control__close'); close.type = 'button'; close.textContent = '×'; close.setAttribute('aria-label', t('collapseMobileAccess'))\n headerActions.append(updatePlugin, diagnosticsEntry, close)\n const releaseNotice = element('p', 'dsh-mobile-control__release-notice'); releaseNotice.hidden = true; releaseNotice.setAttribute('role', 'status'); releaseNotice.setAttribute('aria-live', 'polite')\n const appDownload = element('a', 'dsh-mobile-control__app-download'); appDownload.href = GITHUB_RELEASES_URL; appDownload.target = '_blank'; appDownload.rel = 'noopener noreferrer'; appDownload.textContent = t('downloadAndroid'); appDownload.setAttribute('aria-label', t('downloadAndroidAria'))\n const switcher = element('div', 'dsh-mobile-control__switcher')\n const lanTab = element('button', 'dsh-mobile-control__tab is-active'); lanTab.type = 'button'; lanTab.textContent = t('lan')\n const remoteTab = element('button', 'dsh-mobile-control__tab'); remoteTab.type = 'button'; remoteTab.textContent = t('remote')\n lanTab.setAttribute('aria-pressed', 'true'); remoteTab.setAttribute('aria-pressed', 'false'); switcher.append(lanTab, remoteTab)\n const lanView = element('div', 'dsh-mobile-control__view')\n const lanSetup = element('section', 'dsh-mobile-control__lan-setup'); lanSetup.hidden = true\n const lanSetupTitle = element('h3', 'dsh-mobile-control__section-title'); lanSetupTitle.textContent = t('lanSetupTitle')\n const lanSetupIntro = element('p', 'dsh-mobile-control__intro'); lanSetupIntro.textContent = t('lanSetupIntro')\n const lanSetupForm = element('div', 'dsh-mobile-control__lan-setup-form')\n const lanSetupLabel = element('label', 'dsh-mobile-control__field'); lanSetupLabel.textContent = t('lanSetupNetwork')\n const lanSetupSelect = element('select'); lanSetupSelect.setAttribute('aria-label', t('lanSetupNetwork'))\n const lanSetupNote = element('p', 'dsh-mobile-control__component-note'); lanSetupNote.textContent = t('lanSetupFirewallNote')\n const lanSetupActions = element('div', 'dsh-mobile-control__lan-setup-actions')\n const lanSetupRefresh = element('button', 'dsh-mobile-control__secondary'); lanSetupRefresh.type = 'button'; lanSetupRefresh.textContent = t('lanSetupRefresh')\n const lanSetupConfigure = element('button', 'dsh-mobile-control__primary'); lanSetupConfigure.type = 'button'; lanSetupConfigure.textContent = t('lanSetupConfigure')\n const lanSetupStatus = element('p', 'dsh-mobile-control__status'); lanSetupStatus.setAttribute('role', 'status'); lanSetupStatus.setAttribute('aria-live', 'polite')\n lanSetupLabel.append(lanSetupSelect); lanSetupActions.append(lanSetupRefresh, lanSetupConfigure); lanSetupForm.append(lanSetupLabel, lanSetupNote, lanSetupActions); lanSetup.append(lanSetupTitle, lanSetupIntro, lanSetupForm, lanSetupStatus)\n const access = element('div', 'dsh-mobile-control__access'); access.hidden = true\n const accessLabel = element('span', 'dsh-mobile-control__access-label'); accessLabel.textContent = t('browserAccess')\n const accessLink = element('a', 'dsh-mobile-control__access-link'); accessLink.target = '_blank'; accessLink.rel = 'noreferrer'\n access.append(accessLabel, accessLink)\n const qrBox = element('div', 'dsh-mobile-control__qr'); qrBox.hidden = true\n const status = element('p', 'dsh-mobile-control__status'); status.textContent = t('loadingStatus')\n const extensionStatus = element('p', 'dsh-mobile-control__extensions'); extensionStatus.hidden = true\n const actions = element('div', 'dsh-mobile-control__actions')\n const toggle = element('button', 'dsh-mobile-control__secondary'); toggle.type = 'button'\n const pair = element('button', 'dsh-mobile-control__primary'); pair.type = 'button'; pair.textContent = t('generateCopyKey')\n const linkPair = element('button', 'dsh-mobile-control__secondary'); linkPair.type = 'button'; linkPair.textContent = t('copyPairLink')\n const manageRow = element('div', 'dsh-mobile-control__manage-row')\n const manageDevices = element('button', 'dsh-mobile-control__manage'); manageDevices.type = 'button'; manageDevices.textContent = t('managePairedDevices')\n const resetAll = element('button', 'dsh-mobile-control__manage'); resetAll.type = 'button'; resetAll.textContent = t('clearAllDevices')\n manageRow.append(manageDevices, resetAll)\n const devicePanel = element('div', 'dsh-mobile-control__devices'); devicePanel.hidden = true\n const remoteView = element('div', 'dsh-mobile-control__view is-remote'); remoteView.hidden = true\n const remoteIntro = element('p', 'dsh-mobile-control__intro'); remoteIntro.textContent = t('remoteIntro')\n const httpFrameWarning = element('p', 'dsh-mobile-control__http-frame-warning'); httpFrameWarning.textContent = t('httpFrameWarning'); httpFrameWarning.hidden = location.protocol !== 'https:'; httpFrameWarning.setAttribute('role', 'note')\n const providerSection = element('section', 'dsh-mobile-control__provider-section')\n const providerHeading = element('h3', 'dsh-mobile-control__section-title'); providerHeading.textContent = t('chooseProvider')\n const providerInfo = element('div', 'dsh-mobile-control__provider-info')\n const providerInfoButton = element('button', 'dsh-mobile-control__provider-info-button'); providerInfoButton.type = 'button'; providerInfoButton.setAttribute('aria-label', t('providerInfoAria')); providerInfoButton.setAttribute('aria-expanded', 'false'); providerInfoButton.setAttribute('aria-controls', 'dsh-mobile-provider-info'); providerInfoButton.setAttribute('aria-describedby', 'dsh-mobile-provider-info')\n const providerInfoGlyph = element('span', 'dsh-mobile-control__provider-info-glyph'); providerInfoGlyph.textContent = 'i'; providerInfoGlyph.setAttribute('aria-hidden', 'true')\n const providerInfoPopover = element('div', 'dsh-mobile-control__provider-info-popover'); providerInfoPopover.id = 'dsh-mobile-provider-info'; providerInfoPopover.setAttribute('role', 'tooltip'); providerInfoPopover.hidden = true\n const providerInfoTitle = element('strong'); providerInfoTitle.textContent = t('providerSafeTitle')\n const providerInfoText = element('span'); providerInfoText.textContent = t('providerSafeText')\n providerInfoButton.append(providerInfoGlyph); providerInfoPopover.append(providerInfoTitle, providerInfoText); providerInfo.append(providerInfoButton, providerInfoPopover)\n const providerChoices = element('div', 'dsh-mobile-control__provider-choices'); providerChoices.setAttribute('role', 'radiogroup'); providerChoices.setAttribute('aria-label', t('providerGroupAria'))\n const tailscaleChoice = element('button', 'dsh-mobile-control__provider'); tailscaleChoice.type = 'button'; tailscaleChoice.setAttribute('role', 'radio'); tailscaleChoice.setAttribute('aria-checked', 'true')\n const tailscaleChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const tailscaleChoiceName = element('strong'); tailscaleChoiceName.textContent = 'Tailscale Funnel'\n const tailscaleChoiceBadge = element('span', 'dsh-mobile-control__provider-badge'); tailscaleChoiceBadge.textContent = t('builtIn')\n const tailscaleChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); tailscaleChoiceDescription.textContent = t('tailscaleDescription')\n tailscaleChoiceTop.append(tailscaleChoiceName, tailscaleChoiceBadge); tailscaleChoice.append(tailscaleChoiceTop, tailscaleChoiceDescription)\n const cpolarChoice = element('button', 'dsh-mobile-control__provider'); cpolarChoice.type = 'button'; cpolarChoice.setAttribute('role', 'radio'); cpolarChoice.setAttribute('aria-checked', 'false')\n const cpolarChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const cpolarChoiceName = element('strong'); cpolarChoiceName.textContent = 'cpolar'\n const cpolarChoiceBadge = element('span', 'dsh-mobile-control__provider-badge is-cpolar'); cpolarChoiceBadge.textContent = t('mainlandPreferred')\n const cpolarChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); cpolarChoiceDescription.textContent = t('cpolarDescription')\n cpolarChoiceTop.append(cpolarChoiceName, cpolarChoiceBadge); cpolarChoice.append(cpolarChoiceTop, cpolarChoiceDescription)\n const cloudflaredChoice = element('button', 'dsh-mobile-control__provider'); cloudflaredChoice.type = 'button'; cloudflaredChoice.setAttribute('role', 'radio'); cloudflaredChoice.setAttribute('aria-checked', 'false')\n const cloudflaredChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const cloudflaredChoiceName = element('strong'); cloudflaredChoiceName.textContent = 'cloudflared'\n const cloudflaredChoiceBadge = element('span', 'dsh-mobile-control__provider-badge'); cloudflaredChoiceBadge.textContent = t('cloudflaredBadge')\n const cloudflaredChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); cloudflaredChoiceDescription.textContent = t('cloudflaredDescription')\n cloudflaredChoiceTop.append(cloudflaredChoiceName, cloudflaredChoiceBadge); cloudflaredChoice.append(cloudflaredChoiceTop, cloudflaredChoiceDescription)\n providerChoices.append(cpolarChoice, cloudflaredChoice, tailscaleChoice)\n // ARIA radios normally move focus and selection together, but choosing a provider switches\n // the remote channel behind a confirmation. The arrow keys therefore move focus only, Enter\n // or Space performs the guarded selection, and the group keeps a single tab stop.\n const providerCardOrder = [cpolarChoice, cloudflaredChoice, tailscaleChoice]\n providerChoices.addEventListener('keydown', event => {\n const step = event.key === 'ArrowRight' || event.key === 'ArrowDown' ? 1\n : event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 0\n const jump = event.key === 'Home' ? 0 : event.key === 'End' ? providerCardOrder.length - 1 : undefined\n if (step === 0 && jump === undefined) return\n const current = providerCardOrder.indexOf(document.activeElement as HTMLButtonElement)\n if (current < 0) return\n const next = jump ?? (current + step + providerCardOrder.length) % providerCardOrder.length\n event.preventDefault()\n providerCardOrder[next]?.focus()\n })\n const cpolarSetup = element('section', 'dsh-mobile-control__cpolar-setup'); cpolarSetup.hidden = true\n const cpolarSetupTitle = element('h3', 'dsh-mobile-control__section-title'); cpolarSetupTitle.textContent = t('prepareCpolar')\n const cpolarComponentStatus = element('p', 'dsh-mobile-control__component-status'); cpolarComponentStatus.textContent = t('checkingComponent')\n const cpolarInstall = element('button', 'dsh-mobile-control__primary'); cpolarInstall.type = 'button'; cpolarInstall.textContent = t('installOfficial')\n const cpolarAccount = element('div', 'dsh-mobile-control__cpolar-account'); cpolarAccount.hidden = true\n const cpolarAccountText = element('p', 'dsh-mobile-control__component-note'); cpolarAccountText.textContent = t('cpolarAccountNote')\n const cpolarAccountLinks = element('div', 'dsh-mobile-control__link-row')\n const cpolarSignup = element('a', 'dsh-mobile-control__text-link'); cpolarSignup.href = 'https://dashboard.cpolar.com/signup'; cpolarSignup.target = '_blank'; cpolarSignup.rel = 'noopener noreferrer'; cpolarSignup.textContent = t('registerCpolar')\n const cpolarDashboard = element('a', 'dsh-mobile-control__text-link'); cpolarDashboard.href = 'https://dashboard.cpolar.com/auth'; cpolarDashboard.target = '_blank'; cpolarDashboard.rel = 'noopener noreferrer'; cpolarDashboard.textContent = t('openDashboard')\n cpolarAccountLinks.append(cpolarSignup, cpolarDashboard)\n const cpolarTokenLabel = element('label', 'dsh-mobile-control__token-label'); cpolarTokenLabel.textContent = 'Authtoken'\n const cpolarToken = element('input', 'dsh-mobile-control__token'); cpolarToken.type = 'password'; cpolarToken.autocomplete = 'off'; cpolarToken.spellcheck = false; cpolarToken.placeholder = t('tokenPlaceholder'); cpolarTokenLabel.append(cpolarToken)\n const cpolarConfigure = element('button', 'dsh-mobile-control__primary dsh-mobile-control__cpolar-connect'); cpolarConfigure.type = 'button'; cpolarConfigure.textContent = t('saveConnect')\n cpolarAccount.append(cpolarAccountText, cpolarAccountLinks, cpolarTokenLabel, cpolarConfigure)\n const cpolarDetails = element('details', 'dsh-mobile-control__details')\n const cpolarDetailsSummary = element('summary'); cpolarDetailsSummary.textContent = t('componentDetails')\n const cpolarDetailsBody = element('div', 'dsh-mobile-control__details-body')\n const cpolarDetailsText = element('p'); cpolarDetailsText.textContent = t('componentDetailsText')\n const cpolarStorage = element('code', 'dsh-mobile-control__storage'); cpolarStorage.textContent = t('pluginPrivateDirectory')\n const cpolarOfficial = element('a', 'dsh-mobile-control__text-link'); cpolarOfficial.href = 'https://www.cpolar.com/download'; cpolarOfficial.target = '_blank'; cpolarOfficial.rel = 'noopener noreferrer'; cpolarOfficial.textContent = t('officialDownload')\n const cpolarTerms = element('a', 'dsh-mobile-control__text-link'); cpolarTerms.href = 'https://www.cpolar.com/tos'; cpolarTerms.target = '_blank'; cpolarTerms.rel = 'noopener noreferrer'; cpolarTerms.textContent = t('terms')\n const cpolarPurge = element('button', 'dsh-mobile-control__danger'); cpolarPurge.type = 'button'; cpolarPurge.textContent = t('purgeCpolar')\n cpolarDetailsBody.append(cpolarDetailsText, cpolarStorage, cpolarOfficial, cpolarTerms, cpolarPurge); cpolarDetails.append(cpolarDetailsSummary, cpolarDetailsBody)\n cpolarSetup.append(cpolarSetupTitle, cpolarComponentStatus, cpolarInstall, cpolarAccount, cpolarDetails)\n // Share the managed-component setup layout: a quick tunnel has no account\n // section, so only the install action, the temporary-address warning, and the\n // component source/cleanup disclosure remain.\n const cloudflaredSetup = element('section', 'dsh-mobile-control__cpolar-setup dsh-mobile-control__cloudflared-setup'); cloudflaredSetup.hidden = true\n const cloudflaredSetupTitle = element('h3', 'dsh-mobile-control__section-title'); cloudflaredSetupTitle.textContent = t('prepareCloudflared')\n // Tunnel type comes first: it decides whether an account token is needed at all,\n // while the component below is required either way.\n const cloudflaredTunnelTitle = element('h3', 'dsh-mobile-control__section-title'); cloudflaredTunnelTitle.textContent = t('cloudflaredTunnelTitle')\n const cloudflaredMode = element('div', 'dsh-mobile-control__switcher')\n const cloudflaredModeQuick = element('button', 'dsh-mobile-control__tab is-active'); cloudflaredModeQuick.type = 'button'; cloudflaredModeQuick.textContent = t('cloudflaredTunnelQuick')\n const cloudflaredModeNamed = element('button', 'dsh-mobile-control__tab'); cloudflaredModeNamed.type = 'button'; cloudflaredModeNamed.textContent = t('cloudflaredTunnelNamed')\n cloudflaredMode.append(cloudflaredModeQuick, cloudflaredModeNamed)\n const cloudflaredModeHint = element('p', 'dsh-mobile-control__component-status'); cloudflaredModeHint.textContent = t('cloudflaredTunnelQuickHint')\n const cloudflaredTunnel = element('div', 'dsh-mobile-control__tunnel'); cloudflaredTunnel.hidden = true\n const cloudflaredTunnelFields = element('div', 'dsh-mobile-control__frp-fields')\n const cloudflaredHostnameLabel = element('label', 'dsh-mobile-control__field'); cloudflaredHostnameLabel.textContent = t('cloudflaredTunnelHostname')\n const cloudflaredHostname = element('input'); cloudflaredHostname.type = 'text'; cloudflaredHostname.autocomplete = 'off'; cloudflaredHostname.spellcheck = false; cloudflaredHostname.placeholder = t('cloudflaredTunnelHostnamePlaceholder')\n const cloudflaredPortLabel = element('label', 'dsh-mobile-control__field'); cloudflaredPortLabel.textContent = t('cloudflaredTunnelPort')\n const cloudflaredPort = element('input'); cloudflaredPort.type = 'number'; cloudflaredPort.inputMode = 'numeric'; cloudflaredPort.min = '1024'; cloudflaredPort.max = '65535'; cloudflaredPort.value = '3444'\n const cloudflaredTokenLabel = element('label', 'dsh-mobile-control__field'); cloudflaredTokenLabel.textContent = t('cloudflaredTunnelToken')\n const cloudflaredToken = element('input'); cloudflaredToken.type = 'password'; cloudflaredToken.autocomplete = 'off'; cloudflaredToken.spellcheck = false; cloudflaredToken.placeholder = t('cloudflaredTunnelTokenPlaceholder')\n cloudflaredHostnameLabel.append(cloudflaredHostname); cloudflaredPortLabel.append(cloudflaredPort); cloudflaredTokenLabel.append(cloudflaredToken)\n cloudflaredTunnelFields.append(cloudflaredHostnameLabel, cloudflaredPortLabel, cloudflaredTokenLabel)\n const cloudflaredTunnelStatus = element('p', 'dsh-mobile-control__component-status'); cloudflaredTunnelStatus.textContent = t('cloudflaredTunnelMissing')\n const cloudflaredTunnelHint = element('p', 'dsh-mobile-control__component-status'); cloudflaredTunnelHint.textContent = t('cloudflaredTunnelHint', { port: '3444' })\n const cloudflaredTunnelSave = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); cloudflaredTunnelSave.type = 'button'; cloudflaredTunnelSave.textContent = t('cloudflaredTunnelSave')\n /** The button commits the shown mode, so quick mode must not promise to save settings. */\n const cloudflaredSaveLabel = (): string => (cloudflaredModeDraft ?? cloudflaredTunnelMode) === 'named'\n ? t('cloudflaredTunnelSave') : t('cloudflaredTunnelSaveQuick')\n const cloudflaredTunnelForget = element('button', 'dsh-mobile-control__danger dsh-mobile-control__frp-action'); cloudflaredTunnelForget.type = 'button'; cloudflaredTunnelForget.textContent = t('cloudflaredTunnelForget'); cloudflaredTunnelForget.hidden = true\n // Only the named-tunnel inputs live in this container. Save and Forget stay\n // outside it: Save is also how a quick tunnel is committed, so hiding the\n // container must never take the only way to submit the form with it.\n cloudflaredTunnel.append(cloudflaredTunnelFields, cloudflaredTunnelStatus, cloudflaredTunnelHint)\n const cloudflaredComponentStatus = element('p', 'dsh-mobile-control__component-status'); cloudflaredComponentStatus.textContent = t('checkingComponent')\n const cloudflaredInstall = element('button', 'dsh-mobile-control__primary'); cloudflaredInstall.type = 'button'; cloudflaredInstall.textContent = t('installOfficial')\n const cloudflaredNote = element('p', 'dsh-mobile-control__origin-warning'); cloudflaredNote.textContent = t('cloudflaredQuickNote')\n const cloudflaredDetails = element('details', 'dsh-mobile-control__details')\n const cloudflaredDetailsSummary = element('summary'); cloudflaredDetailsSummary.textContent = t('componentDetails')\n const cloudflaredDetailsBody = element('div', 'dsh-mobile-control__details-body')\n const cloudflaredDetailsText = element('p'); cloudflaredDetailsText.textContent = t('cloudflaredComponentNote')\n const cloudflaredStorage = element('code', 'dsh-mobile-control__storage'); cloudflaredStorage.textContent = t('pluginPrivateDirectory')\n const cloudflaredOfficial = element('a', 'dsh-mobile-control__text-link'); cloudflaredOfficial.href = 'https://github.com/cloudflare/cloudflared/releases'; cloudflaredOfficial.target = '_blank'; cloudflaredOfficial.rel = 'noopener noreferrer'; cloudflaredOfficial.textContent = t('officialDownload')\n const cloudflaredTerms = element('a', 'dsh-mobile-control__text-link'); cloudflaredTerms.href = 'https://www.cloudflare.com/website-terms/'; cloudflaredTerms.target = '_blank'; cloudflaredTerms.rel = 'noopener noreferrer'; cloudflaredTerms.textContent = t('terms')\n const cloudflaredPurge = element('button', 'dsh-mobile-control__danger'); cloudflaredPurge.type = 'button'; cloudflaredPurge.textContent = t('purgeCloudflared')\n cloudflaredDetailsBody.append(cloudflaredDetailsText, cloudflaredStorage, cloudflaredOfficial, cloudflaredTerms, cloudflaredPurge); cloudflaredDetails.append(cloudflaredDetailsSummary, cloudflaredDetailsBody)\n cloudflaredSetup.append(cloudflaredSetupTitle, cloudflaredTunnelTitle, cloudflaredMode, cloudflaredModeHint, cloudflaredTunnel, cloudflaredTunnelSave, cloudflaredTunnelForget, cloudflaredComponentStatus, cloudflaredInstall, cloudflaredNote, cloudflaredDetails)\n const selfHosted = element('details', 'dsh-mobile-control__self-hosted')\n const selfHostedSummary = element('summary', 'dsh-mobile-control__self-hosted-summary')\n const selfHostedSummaryText = element('span')\n const selfHostedSummaryTitle = element('strong'); selfHostedSummaryTitle.textContent = t('selfHostedConnections')\n const selfHostedSummaryDescription = element('span'); selfHostedSummaryDescription.textContent = t('selfHostedDescription')\n const selfHostedBadge = element('span', 'dsh-mobile-control__provider-badge is-frp'); selfHostedBadge.textContent = t('advanced')\n selfHostedSummaryText.append(selfHostedSummaryTitle, selfHostedSummaryDescription); selfHostedSummary.append(selfHostedSummaryText, selfHostedBadge)\n const selfHostedBody = element('div', 'dsh-mobile-control__self-hosted-body')\n const frpChoice = element('button', 'dsh-mobile-control__provider is-frp'); frpChoice.type = 'button'; frpChoice.setAttribute('aria-pressed', 'false')\n const frpChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const frpChoiceName = element('strong'); frpChoiceName.textContent = t('frpName')\n const frpChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); frpChoiceDescription.textContent = t('frpDescription')\n frpChoiceTop.append(frpChoiceName); frpChoice.append(frpChoiceTop, frpChoiceDescription)\n const originChoice = element('button', 'dsh-mobile-control__provider is-origin'); originChoice.type = 'button'; originChoice.setAttribute('aria-pressed', 'false')\n const originChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const originChoiceName = element('strong'); originChoiceName.textContent = t('originName')\n const originChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); originChoiceDescription.textContent = t('originDescription')\n originChoiceTop.append(originChoiceName); originChoice.append(originChoiceTop, originChoiceDescription)\n selfHostedBody.append(frpChoice, originChoice); selfHosted.append(selfHostedSummary, selfHostedBody)\n providerSection.append(providerHeading, providerInfo, providerChoices, selfHosted)\n const frpSetup = element('section', 'dsh-mobile-control__frp-setup'); frpSetup.hidden = true\n const frpSetupTitle = element('h3', 'dsh-mobile-control__section-title'); frpSetupTitle.textContent = t('prepareFrp')\n const frpStep1 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep1Title = element('strong'); frpStep1Title.textContent = t('frpStep1Title')\n const frpStep1Text = element('p'); frpStep1Text.textContent = t('frpStep1Text')\n const frpFields = element('div', 'dsh-mobile-control__frp-fields')\n const frpServerLabel = element('label', 'dsh-mobile-control__field'); frpServerLabel.textContent = t('frpServerAddress')\n const frpServer = element('input'); frpServer.type = 'text'; frpServer.autocomplete = 'off'; frpServer.spellcheck = false; frpServer.placeholder = t('frpServerAddressPlaceholder')\n const frpPortLabel = element('label', 'dsh-mobile-control__field'); frpPortLabel.textContent = t('frpServerPort')\n const frpPort = element('input'); frpPort.type = 'number'; frpPort.inputMode = 'numeric'; frpPort.min = '1'; frpPort.max = '65535'; frpPort.value = '7000'\n const frpTokenLabel = element('label', 'dsh-mobile-control__field'); frpTokenLabel.textContent = t('frpToken')\n const frpToken = element('input'); frpToken.type = 'password'; frpToken.autocomplete = 'off'; frpToken.spellcheck = false; frpToken.placeholder = t('frpTokenPlaceholder')\n const frpOriginLabel = element('label', 'dsh-mobile-control__field'); frpOriginLabel.textContent = t('frpPublicOrigin')\n const frpOrigin = element('input'); frpOrigin.type = 'url'; frpOrigin.autocomplete = 'off'; frpOrigin.spellcheck = false; frpOrigin.placeholder = t('frpPublicOriginPlaceholder')\n frpServerLabel.append(frpServer); frpPortLabel.append(frpPort); frpTokenLabel.append(frpToken); frpOriginLabel.append(frpOrigin)\n frpFields.append(frpServerLabel, frpPortLabel, frpTokenLabel, frpOriginLabel); frpStep1.append(frpStep1Title, frpStep1Text, frpFields)\n const frpStep2 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep2Title = element('strong'); frpStep2Title.textContent = t('frpStep2Title')\n const frpStep2Text = element('p'); frpStep2Text.textContent = t('frpStep2Text')\n const frpCopyTemplate = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); frpCopyTemplate.type = 'button'; frpCopyTemplate.textContent = t('copyServerTemplate')\n const vpsDeployText = element('p'); vpsDeployText.textContent = t('vpsDeployText')\n const vpsChangesTitle = element('p'); vpsChangesTitle.textContent = t('vpsDeployChangesTitle')\n const vpsChanges = element('ul', 'dsh-mobile-control__frp-changes')\n for (const key of ['vpsDeployChangePackages', 'vpsDeployChangeServices', 'vpsDeployChangeFirewall', 'vpsDeployChangeManual'] as const) {\n const item = element('li'); item.textContent = t(key); vpsChanges.append(item)\n }\n const vpsDeployFields = element('div', 'dsh-mobile-control__frp-fields')\n const vpsSshUserLabel = element('label', 'dsh-mobile-control__field'); vpsSshUserLabel.textContent = t('vpsSshUser')\n const vpsSshUser = element('input'); vpsSshUser.type = 'text'; vpsSshUser.autocomplete = 'username'; vpsSshUser.value = 'ubuntu'; vpsSshUser.spellcheck = false\n const vpsSshPortLabel = element('label', 'dsh-mobile-control__field'); vpsSshPortLabel.textContent = t('vpsSshPort')\n const vpsSshPort = element('input'); vpsSshPort.type = 'number'; vpsSshPort.inputMode = 'numeric'; vpsSshPort.min = '1'; vpsSshPort.max = '65535'; vpsSshPort.value = '22'\n const vpsSshKeyLabel = element('label', 'dsh-mobile-control__field'); vpsSshKeyLabel.textContent = t('vpsSshKey')\n const vpsSshKey = element('input'); vpsSshKey.type = 'text'; vpsSshKey.autocomplete = 'off'; vpsSshKey.spellcheck = false; vpsSshKey.placeholder = t('vpsSshKeyPlaceholder')\n vpsSshUserLabel.append(vpsSshUser); vpsSshPortLabel.append(vpsSshPort); vpsSshKeyLabel.append(vpsSshKey)\n vpsDeployFields.append(vpsSshUserLabel, vpsSshPortLabel, vpsSshKeyLabel)\n const vpsDeploy = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); vpsDeploy.type = 'button'; vpsDeploy.textContent = t('vpsDeploy')\n const vpsDeployStatus = element('p', 'dsh-mobile-control__component-status'); vpsDeployStatus.textContent = ''\n const vpsCopyUninstall = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); vpsCopyUninstall.type = 'button'; vpsCopyUninstall.textContent = t('vpsCopyUninstall')\n const vpsUninstall = element('button', 'dsh-mobile-control__danger dsh-mobile-control__frp-action'); vpsUninstall.type = 'button'; vpsUninstall.textContent = t('vpsUninstall')\n frpStep2.append(frpStep2Title, frpStep2Text, frpCopyTemplate, vpsDeployText, vpsChangesTitle, vpsChanges, vpsDeployFields, vpsDeploy, vpsDeployStatus, vpsCopyUninstall, vpsUninstall)\n const frpStep3 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep3Title = element('strong'); frpStep3Title.textContent = t('frpStep3Title')\n const frpStep3Text = element('p'); frpStep3Text.textContent = t('frpStep3Text')\n const frpComponentStatus = element('p', 'dsh-mobile-control__component-status'); frpComponentStatus.textContent = t('checkingComponent')\n const frpInstall = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); frpInstall.type = 'button'; frpInstall.textContent = t('installFrpc')\n frpStep3.append(frpStep3Title, frpStep3Text, frpComponentStatus, frpInstall)\n const frpStep4 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep4Title = element('strong'); frpStep4Title.textContent = t('frpStep4Title')\n const frpStep4Text = element('p'); frpStep4Text.textContent = t('frpStep4Text')\n const frpAppRequirement = element('p', 'dsh-mobile-control__frp-requirement'); frpAppRequirement.textContent = t('frpAppRequirement')\n const frpConfigurationStatus = element('p', 'dsh-mobile-control__component-status'); frpConfigurationStatus.textContent = t('frpConfigurationMissing')\n const frpConfigure = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); frpConfigure.type = 'button'; frpConfigure.textContent = t('frpSaveConnect')\n frpStep4.append(frpStep4Title, frpStep4Text, frpAppRequirement, frpConfigurationStatus, frpConfigure)\n const frpDetails = element('details', 'dsh-mobile-control__details')\n const frpDetailsSummary = element('summary'); frpDetailsSummary.textContent = t('frpComponentDetails')\n const frpDetailsBody = element('div', 'dsh-mobile-control__details-body')\n const frpDetailsText = element('p'); frpDetailsText.textContent = t('frpComponentDetailsText')\n const frpStorage = element('code', 'dsh-mobile-control__storage'); frpStorage.textContent = t('pluginPrivateDirectory')\n const frpOfficial = element('a', 'dsh-mobile-control__text-link'); frpOfficial.href = 'https://github.com/fatedier/frp/releases/tag/v0.70.1'; frpOfficial.target = '_blank'; frpOfficial.rel = 'noopener noreferrer'; frpOfficial.textContent = t('frpOfficialRelease')\n const frpPurge = element('button', 'dsh-mobile-control__danger'); frpPurge.type = 'button'; frpPurge.textContent = t('purgeFrp')\n frpDetailsBody.append(frpDetailsText, frpStorage, frpOfficial, frpPurge); frpDetails.append(frpDetailsSummary, frpDetailsBody)\n const frpOverview = element('section', 'dsh-mobile-control__frp-overview'); frpOverview.hidden = true\n const frpOverviewMark = element('span', 'dsh-mobile-control__frp-overview-mark'); frpOverviewMark.textContent = '✓'\n const frpOverviewBody = element('div', 'dsh-mobile-control__frp-overview-body')\n const frpOverviewTitle = element('strong'); frpOverviewTitle.textContent = t('frpConfigurationReady')\n const frpOverviewEndpoint = element('span'); frpOverviewEndpoint.textContent = ''\n frpOverviewBody.append(frpOverviewTitle, frpOverviewEndpoint); frpOverview.append(frpOverviewMark, frpOverviewBody)\n const frpConnectionGroup = element('details', 'dsh-mobile-control__frp-group')\n const frpConnectionSummary = element('summary'); frpConnectionSummary.textContent = t('frpStep1Title')\n frpConnectionGroup.append(frpConnectionSummary, frpStep1)\n const frpVpsGroup = element('details', 'dsh-mobile-control__frp-group')\n const frpVpsSummary = element('summary'); frpVpsSummary.textContent = t('frpStep2Title')\n frpVpsGroup.append(frpVpsSummary, frpStep2)\n const frpComponentGroup = element('details', 'dsh-mobile-control__frp-group')\n const frpComponentSummary = element('summary'); frpComponentSummary.textContent = t('frpStep3Title')\n frpComponentGroup.append(frpComponentSummary, frpStep3)\n const frpVerifyGroup = element('details', 'dsh-mobile-control__frp-group'); frpVerifyGroup.open = true\n const frpVerifySummary = element('summary'); frpVerifySummary.textContent = t('frpStep4Title')\n frpVerifyGroup.append(frpVerifySummary, frpStep4)\n frpSetup.append(frpSetupTitle, frpOverview, frpConnectionGroup, frpVpsGroup, frpComponentGroup, frpVerifyGroup, frpDetails)\n const originSetup = element('section', 'dsh-mobile-control__origin-setup'); originSetup.hidden = true\n const originSetupTitle = element('h3', 'dsh-mobile-control__section-title'); originSetupTitle.textContent = t('prepareOrigin')\n const originHelp = element('p', 'dsh-mobile-control__component-note'); originHelp.id = 'dsh-mobile-origin-help'; originHelp.textContent = t('originHelp')\n const originWarning = element('p', 'dsh-mobile-control__origin-warning'); originWarning.textContent = t('originWarning')\n const originFields = element('div', 'dsh-mobile-control__origin-fields')\n const originPublicLabel = element('label', 'dsh-mobile-control__field'); originPublicLabel.textContent = t('originPublicOrigin')\n const originPublic = element('input'); originPublic.type = 'url'; originPublic.placeholder = 'https://phone.example.com:8815'; originPublic.maxLength = 512\n const originHostLabel = element('label', 'dsh-mobile-control__field'); originHostLabel.textContent = t('originListenHost')\n const originHost = element('input'); originHost.type = 'text'; originHost.value = '127.0.0.1'; originHost.maxLength = 15\n const originPortLabel = element('label', 'dsh-mobile-control__field'); originPortLabel.textContent = t('originListenPort')\n const originPort = element('input'); originPort.type = 'number'; originPort.inputMode = 'numeric'; originPort.min = '1024'; originPort.max = '65535'; originPort.step = '1'; originPort.value = '3444'\n const originCidrsLabel = element('label', 'dsh-mobile-control__field'); originCidrsLabel.textContent = t('originAllowedCidrs')\n const originCidrs = element('input'); originCidrs.type = 'text'; originCidrs.value = '127.0.0.0/8'; originCidrs.maxLength = 1040\n for (const input of [originPublic, originHost, originPort, originCidrs]) {\n input.required = true; input.autocomplete = 'off'; input.spellcheck = false\n input.setAttribute('aria-describedby', 'dsh-mobile-origin-help dsh-mobile-origin-feedback')\n }\n originPublicLabel.append(originPublic); originHostLabel.append(originHost); originPortLabel.append(originPort); originCidrsLabel.append(originCidrs)\n originFields.append(originPublicLabel, originHostLabel, originPortLabel, originCidrsLabel)\n const originFeedback = element('p', 'dsh-mobile-control__origin-feedback'); originFeedback.id = 'dsh-mobile-origin-feedback'; originFeedback.setAttribute('role', 'status'); originFeedback.setAttribute('aria-live', 'polite'); originFeedback.hidden = true\n const originConfigure = element('button', 'dsh-mobile-control__primary'); originConfigure.type = 'button'; originConfigure.textContent = t('originSaveStart')\n const originPurge = element('button', 'dsh-mobile-control__danger'); originPurge.type = 'button'; originPurge.textContent = t('originPurge'); originPurge.hidden = true\n const originFormActions = element('div', 'dsh-mobile-control__actions'); originFormActions.append(originConfigure, originPurge)\n const originBackend = element('div', 'dsh-mobile-control__origin-backend'); originBackend.hidden = true\n const originBackendLabel = element('span', 'dsh-mobile-control__access-label')\n const originBackendAddress = element('code', 'dsh-mobile-control__storage')\n const originCopyBackend = element('button', 'dsh-mobile-control__secondary'); originCopyBackend.type = 'button'; originCopyBackend.textContent = t('originCopyBackend')\n originBackend.append(originBackendLabel, originBackendAddress, originCopyBackend)\n originSetup.append(originSetupTitle, originHelp, originWarning, originFields, originFeedback, originFormActions, originBackend)\n const tailscaleInfo = element('details', 'dsh-mobile-control__details')\n const tailscaleInfoSummary = element('summary'); tailscaleInfoSummary.textContent = t('tailscaleHelp')\n const tailscaleInfoBody = element('div', 'dsh-mobile-control__details-body')\n const tailscaleInfoText = element('p'); tailscaleInfoText.textContent = t('tailscaleHelpText')\n tailscaleInfoBody.append(tailscaleInfoText); tailscaleInfo.append(tailscaleInfoSummary, tailscaleInfoBody)\n const providerSetupHeader = element('div', 'dsh-mobile-control__stage-header')\n const providerSetupHeading = element('h3', 'dsh-mobile-control__section-title'); providerSetupHeading.textContent = t('currentProvider')\n const providerSetupName = element('span', 'dsh-mobile-control__stage-value'); providerSetupName.textContent = 'Tailscale Funnel'\n const remoteStateBadge = element('span', 'dsh-mobile-control__state-badge'); remoteStateBadge.textContent = t('remoteStateOff')\n const providerSetupMeta = element('div', 'dsh-mobile-control__stage-meta'); providerSetupMeta.append(providerSetupName, remoteStateBadge)\n providerSetupHeader.append(providerSetupHeading, providerSetupMeta)\n const providerSetupBody = element('div', 'dsh-mobile-control__provider-setup-body')\n providerSetupBody.append(cpolarSetup, cloudflaredSetup, frpSetup, originSetup, tailscaleInfo)\n const remoteAccess = element('div', 'dsh-mobile-control__access'); remoteAccess.hidden = true\n const remoteAccessLabel = element('span', 'dsh-mobile-control__access-label'); remoteAccessLabel.textContent = t('remoteAddress')\n const remoteAccessLink = element('a', 'dsh-mobile-control__access-link'); remoteAccessLink.target = '_blank'; remoteAccessLink.rel = 'noreferrer'; remoteAccess.append(remoteAccessLabel, remoteAccessLink)\n const remoteQr = element('div', 'dsh-mobile-control__qr'); remoteQr.hidden = true\n const remoteStatus = element('p', 'dsh-mobile-control__status'); remoteStatus.textContent = t('loadingRemoteStatus'); remoteStatus.setAttribute('aria-live', 'polite')\n const remoteGuide = element('section', 'dsh-mobile-control__guide'); remoteGuide.hidden = true; remoteGuide.setAttribute('aria-label', t('funnelGuideAria'))\n const remoteGuideTitle = element('h3', 'dsh-mobile-control__guide-title'); remoteGuideTitle.textContent = t('funnelGuideTitle')\n const remoteGuideSummary = element('p', 'dsh-mobile-control__guide-summary'); remoteGuideSummary.textContent = t('funnelGuideSummary')\n const remoteGuideSteps = element('ol', 'dsh-mobile-control__guide-steps')\n for (const text of [t('funnelStep1'), t('funnelStep2'), t('funnelStep3')]) {\n const item = element('li'); item.textContent = text; remoteGuideSteps.append(item)\n }\n const remoteGuideNote = element('p', 'dsh-mobile-control__guide-note'); remoteGuideNote.textContent = t('funnelGuideNote')\n const remoteGuideActions = element('div', 'dsh-mobile-control__guide-actions')\n const remoteSetup = element('button', 'dsh-mobile-control__primary'); remoteSetup.type = 'button'; remoteSetup.textContent = t('continueFunnel')\n const remoteSetupRetry = element('button', 'dsh-mobile-control__secondary'); remoteSetupRetry.type = 'button'; remoteSetupRetry.textContent = t('retryNow')\n remoteGuideActions.append(remoteSetup, remoteSetupRetry); remoteGuide.append(remoteGuideTitle, remoteGuideSummary, remoteGuideSteps, remoteGuideNote, remoteGuideActions)\n const remoteActions = element('div', 'dsh-mobile-control__actions')\n const remoteToggle = element('button', 'dsh-mobile-control__primary'); remoteToggle.type = 'button'; remoteToggle.textContent = t('enableRemote')\n const remoteLogin = element('button', 'dsh-mobile-control__primary'); remoteLogin.type = 'button'; remoteLogin.textContent = t('continueLogin'); remoteLogin.hidden = true\n const remoteReconnect = element('button', 'dsh-mobile-control__secondary'); remoteReconnect.type = 'button'; remoteReconnect.textContent = t('reconnect'); remoteReconnect.hidden = true\n const remotePair = element('button', 'dsh-mobile-control__secondary'); remotePair.type = 'button'; remotePair.textContent = t('generateRemoteQr'); remotePair.disabled = true\n const remoteCopyLink = element('button', 'dsh-mobile-control__secondary'); remoteCopyLink.type = 'button'; remoteCopyLink.textContent = t('copyRemoteLink'); remoteCopyLink.disabled = true\n const remotePairLink = element('p', 'dsh-mobile-control__status'); remotePairLink.hidden = true\n let activeRemotePairUrl = ''\n let activeRemotePairExpiresAt = 0\n remoteActions.append(remoteToggle, remoteLogin, remoteReconnect, remotePair, remoteCopyLink)\n const remoteManageRow = element('div', 'dsh-mobile-control__manage-row')\n const remoteDevices = element('button', 'dsh-mobile-control__manage'); remoteDevices.type = 'button'; remoteDevices.textContent = t('manageRemoteDevices'); remoteDevices.disabled = true\n const remoteReset = element('button', 'dsh-mobile-control__manage'); remoteReset.type = 'button'; remoteReset.textContent = t('resetRemoteLogin')\n remoteManageRow.append(remoteDevices, remoteReset)\n const remoteDevicePanel = element('div', 'dsh-mobile-control__devices'); remoteDevicePanel.hidden = true\n const wsPathsSection = element('section', 'dsh-mobile-control__ws-paths')\n const wsPathsTitle = element('h3', 'dsh-mobile-control__section-title'); wsPathsTitle.textContent = t('wsPathsTitle')\n const wsPathsIntro = element('p', 'dsh-mobile-control__intro'); wsPathsIntro.textContent = t('wsPathsIntro')\n const wsPathsDetected = element('div', 'dsh-mobile-control__ws-paths-detected'); wsPathsDetected.hidden = true\n const wsPathsDetectedTitle = element('p', 'dsh-mobile-control__ws-paths-detected-title'); wsPathsDetectedTitle.textContent = t('wsPathsDetected')\n const wsPathsDetectedList = element('ul', 'dsh-mobile-control__ws-paths-list')\n wsPathsDetected.append(wsPathsDetectedTitle, wsPathsDetectedList)\n const wsPathsList = element('ul', 'dsh-mobile-control__ws-paths-list')\n const wsPathsAdvanced = element('details', 'dsh-mobile-control__ws-paths-advanced')\n const wsPathsAdvancedSummary = element('summary'); wsPathsAdvancedSummary.textContent = t('wsPathsAdvancedToggle')\n const wsPathsAdvancedHint = element('p', 'dsh-mobile-control__intro'); wsPathsAdvancedHint.textContent = t('wsPathsAdvancedHint')\n const wsPathsRow = element('div', 'dsh-mobile-control__ws-paths-row')\n const wsPathsInput = element('input', 'dsh-mobile-control__ws-paths-input'); wsPathsInput.type = 'text'; wsPathsInput.placeholder = t('wsPathsPlaceholder'); wsPathsInput.setAttribute('aria-label', t('wsPathsTitle'))\n const wsPathsAdd = element('button', 'dsh-mobile-control__primary'); wsPathsAdd.type = 'button'; wsPathsAdd.textContent = t('wsPathsAdd')\n const wsPathsStatus = element('p', 'dsh-mobile-control__status'); wsPathsStatus.hidden = true; wsPathsStatus.setAttribute('aria-live', 'polite')\n wsPathsRow.append(wsPathsInput, wsPathsAdd)\n wsPathsAdvanced.append(wsPathsAdvancedSummary, wsPathsAdvancedHint, wsPathsRow, wsPathsStatus)\n wsPathsSection.append(wsPathsTitle, wsPathsIntro, wsPathsDetected, wsPathsList, wsPathsAdvanced)\n let wsPathsCurrent: string[] = []\n let wsPathsBlocked: { readonly path: string; readonly attempts: number }[] = []\n let wsPathsLoaded = false\n const renderWsPaths = (paths: readonly string[]): void => {\n wsPathsCurrent = [...paths]\n wsPathsList.replaceChildren()\n if (paths.length === 0) {\n const empty = element('li', 'dsh-mobile-control__ws-paths-empty'); empty.textContent = t('wsPathsEmpty'); wsPathsList.append(empty)\n }\n for (const path of paths) {\n const item = element('li', 'dsh-mobile-control__ws-paths-item')\n const label = element('code'); label.textContent = path\n const remove = element('button', 'dsh-mobile-control__ws-paths-remove'); remove.type = 'button'; remove.textContent = t('wsPathsRemove'); remove.setAttribute('aria-label', `${t('wsPathsRemove')}: ${path}`)\n remove.addEventListener('click', () => { void saveWsPaths(wsPathsCurrent.filter(entry => entry !== path)) })\n item.append(label, remove)\n wsPathsList.append(item)\n }\n }\n const wsGroupOf = (path: string): string => {\n const slash = path.lastIndexOf('/')\n return slash > 0 ? path.slice(0, slash) : '/'\n }\n let wsPathsSeenAttempts = 0\n let wsDotAnnounced = 0\n const wsDotStatus = element('p', 'dsh-mobile-control__ws-sr-status')\n wsDotStatus.setAttribute('role', 'status'); wsDotStatus.setAttribute('aria-atomic', 'true')\n const wsBlockedTotal = (blocked: readonly { readonly attempts: number }[]): number =>\n blocked.reduce((sum, entry) => sum + entry.attempts, 0)\n const renderWsDot = (): void => {\n for (const stale of document.querySelectorAll('.dsh-mobile-control__ws-dot')) stale.remove()\n if (!wsDotStatus.isConnected) root.append(wsDotStatus)\n const pending = wsPathsBlocked.filter(entry => !wsPathsCurrent.includes(entry.path))\n const pendingCount = pending.length\n if (pendingCount === 0 || wsBlockedTotal(wsPathsBlocked) <= wsPathsSeenAttempts) {\n if (pendingCount === 0 && wsDotAnnounced !== 0) { wsDotAnnounced = 0; wsDotStatus.textContent = '' }\n return\n }\n if (wsDotAnnounced !== pendingCount) {\n wsDotAnnounced = pendingCount\n wsDotStatus.textContent = t('wsPathsAnnounce', { n: String(pendingCount) })\n }\n for (const trigger of document.querySelectorAll('.dsh-mobile-control__trigger, .dsh-mobile-control__diagnostic-entry')) {\n const dot = element('span', 'dsh-mobile-control__ws-dot'); dot.textContent = String(pendingCount); dot.title = t('wsPathsNewBlocked'); dot.setAttribute('aria-hidden', 'true')\n dot.addEventListener('click', event => { event.stopPropagation(); setOpen(true); selectView('diagnostics') })\n trigger.append(dot)\n }\n }\n const renderWsBlocked = (blocked: readonly { readonly path: string; readonly attempts: number }[]): void => {\n wsPathsDetectedList.replaceChildren()\n const pending = blocked.filter(entry => !wsPathsCurrent.includes(entry.path))\n wsPathsDetected.hidden = pending.length === 0\n const groups = new Map<string, { readonly path: string; readonly attempts: number }[]>()\n for (const entry of pending) {\n const group = wsGroupOf(entry.path)\n const list = groups.get(group)\n if (list) list.push(entry); else groups.set(group, [entry])\n }\n for (const [group, entries] of groups) {\n const groupItem = element('li', 'dsh-mobile-control__ws-paths-group')\n const groupHead = element('div', 'dsh-mobile-control__ws-paths-group-head')\n const groupLabel = element('code'); groupLabel.textContent = `${group}/*`\n const groupCount = element('span', 'dsh-mobile-control__ws-count')\n groupCount.textContent = t('wsPathsGroupMeta', { n: entries.length, m: entries.reduce((sum, entry) => sum + entry.attempts, 0) })\n groupHead.append(groupLabel, groupCount)\n if (entries.length > 1) {\n const allowAll = element('button', 'dsh-mobile-control__primary'); allowAll.type = 'button'; allowAll.textContent = t('wsPathsAllowAll')\n allowAll.addEventListener('click', () => { saveWsPaths([...wsPathsCurrent, ...entries.map(entry => entry.path)]) })\n groupHead.append(allowAll)\n }\n groupItem.append(groupHead)\n for (const entry of entries) {\n const item = element('div', 'dsh-mobile-control__ws-paths-item is-child')\n const label = element('code'); label.textContent = `${entry.path} ×${String(entry.attempts)}`\n const allow = element('button', 'dsh-mobile-control__primary'); allow.type = 'button'; allow.textContent = t('wsPathsAllow'); allow.setAttribute('aria-label', `${t('wsPathsAllow')}: ${entry.path}`)\n allow.addEventListener('click', () => { saveWsPaths([...wsPathsCurrent, entry.path]) })\n item.append(label, allow)\n groupItem.append(item)\n }\n wsPathsDetectedList.append(groupItem)\n }\n renderWsDot()\n }\n const loadWsPaths = (): void => {\n void controlRequestJson('/api/mobile-access/remote/websocket-paths').then(data => {\n wsPathsLoaded = true\n wsPathsStatus.hidden = true\n renderWsPaths(Array.isArray(data.paths) ? data.paths.filter((entry): entry is string => typeof entry === 'string') : [])\n void controlRequestJson('/api/mobile-access/remote/websocket-paths/blocked').then(blocked => {\n const entries = Array.isArray(blocked.blocked) ? blocked.blocked : []\n wsPathsBlocked = entries.filter((entry): entry is { path: string; attempts: number } =>\n typeof entry === 'object' && entry !== null\n && typeof (entry as { path?: unknown }).path === 'string'\n && typeof (entry as { attempts?: unknown }).attempts === 'number')\n renderWsBlocked(wsPathsBlocked)\n }, () => { /* approved list already rendered; detected list stays hidden */ })\n }, error => {\n wsPathsStatus.hidden = false\n wsPathsStatus.textContent = t('requestFailed', { error: String(error) })\n })\n }\n const saveWsPaths = (paths: readonly string[]): void => {\n wsPathsStatus.hidden = true\n void controlRequestJson('/api/mobile-access/remote/websocket-paths', { method: 'POST', body: JSON.stringify({ paths }) }).then(data => {\n wsPathsLoaded = true\n renderWsPaths(Array.isArray(data.paths) ? data.paths.filter((entry): entry is string => typeof entry === 'string') : [])\n renderWsBlocked(wsPathsBlocked)\n }, error => {\n wsPathsStatus.hidden = false\n wsPathsStatus.textContent = t('requestFailed', { error: String(error) })\n })\n }\n wsPathsAdd.addEventListener('click', () => {\n const value = wsPathsInput.value.trim()\n if (value === '') {\n wsPathsStatus.hidden = false\n wsPathsStatus.textContent = t('wsPathsInvalid')\n return\n }\n wsPathsInput.value = ''\n saveWsPaths([...wsPathsCurrent, value])\n })\n const remoteWorkspace = element('section', 'dsh-mobile-control__remote-workspace')\n remoteWorkspace.append(providerSetupHeader, remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteActions, remoteQr, remotePairLink, remoteManageRow, remoteDevicePanel)\n const diagnosticsView = element('div', 'dsh-mobile-control__view is-diagnostics'); diagnosticsView.hidden = true\n const diagnosticsIntro = element('p', 'dsh-mobile-control__intro'); diagnosticsIntro.textContent = t('diagnosticsIntro')\n const diagnosticsSummary = element('section', 'dsh-mobile-control__diagnostic-summary is-idle'); diagnosticsSummary.setAttribute('aria-live', 'polite')\n const diagnosticsSummaryMain = element('div', 'dsh-mobile-control__diagnostic-summary-main')\n const diagnosticsSummaryIcon = element('span', 'dsh-mobile-control__diagnostic-summary-icon'); diagnosticsSummaryIcon.setAttribute('aria-hidden', 'true')\n const diagnosticsSummaryBody = element('div', 'dsh-mobile-control__diagnostic-summary-body')\n const diagnosticsSummaryTitle = element('strong'); diagnosticsSummaryTitle.textContent = t('diagnosticsNotRun')\n const diagnosticsSummaryText = element('span'); diagnosticsSummaryText.textContent = t('diagnosticsStartHint')\n const diagnosticsSummaryMeta = element('span', 'dsh-mobile-control__diagnostic-summary-meta'); diagnosticsSummaryMeta.textContent = t('diagnosticsIdleMeta')\n diagnosticsSummaryBody.append(diagnosticsSummaryTitle, diagnosticsSummaryText)\n diagnosticsSummaryMain.append(diagnosticsSummaryIcon, diagnosticsSummaryBody)\n diagnosticsSummary.append(diagnosticsSummaryMain, diagnosticsSummaryMeta)\n const diagnosticsToolbar = element('div', 'dsh-mobile-control__diagnostic-toolbar')\n const diagnosticsRun = element('button', 'dsh-mobile-control__primary dsh-mobile-control__diagnostic-run'); diagnosticsRun.type = 'button'; diagnosticsRun.textContent = t('diagnosticsStart')\n const diagnosticsCopy = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__diagnostic-copy'); diagnosticsCopy.type = 'button'; diagnosticsCopy.textContent = t('diagnosticsCopy'); diagnosticsCopy.disabled = true; diagnosticsCopy.hidden = true\n diagnosticsToolbar.append(diagnosticsRun, diagnosticsCopy)\n const diagnosticsFeedback = element('p', 'dsh-mobile-control__diagnostic-feedback'); diagnosticsFeedback.hidden = true; diagnosticsFeedback.setAttribute('role', 'status'); diagnosticsFeedback.setAttribute('aria-live', 'polite')\n const diagnosticsChecks = element('div', 'dsh-mobile-control__diagnostic-checks'); diagnosticsChecks.hidden = true\n const diagnosticsDetails = element('details', 'dsh-mobile-control__details dsh-mobile-control__diagnostic-details'); diagnosticsDetails.hidden = true\n const diagnosticsDetailsSummary = element('summary'); diagnosticsDetailsSummary.textContent = t('diagnosticsAdvanced')\n const diagnosticsReport = element('pre', 'dsh-mobile-control__diagnostic-report')\n diagnosticsDetails.append(diagnosticsDetailsSummary, diagnosticsReport)\n header.append(title, headerActions); actions.append(toggle, pair, linkPair)\n lanView.append(lanSetup, access, qrBox, status, extensionStatus, actions, manageRow, devicePanel)\n remoteView.append(remoteIntro, httpFrameWarning, providerSection, remoteWorkspace)\n diagnosticsView.append(diagnosticsIntro, diagnosticsSummary, diagnosticsToolbar, diagnosticsFeedback, diagnosticsChecks, wsPathsSection, diagnosticsDetails)\n panel.append(header, releaseNotice, updateCard, appDownload, switcher, lanView, remoteView, diagnosticsView); root.append(panel); document.body.append(root)\n let running = false\n let origin = ''\n let lanConfigured = true\n let lanRestartRequired = false\n let lanSetupLoaded = false\n let remoteRunning = false\n let remoteReady = false\n let remoteProvider: MobileRemoteProvider = 'tailscale'\n let remoteLoginUrl = ''\n let remoteSetupUrl = ''\n let remoteSetupPending = false\n let remoteSetupOpenedAt = 0\n let remoteReconnectBusy = false\n let remoteProviderBusy = false\n // A failed control action stores its message here so the repaint that loadRemote() runs in the\n // action's finally cannot replace it before the user reads it. The repaint that would have\n // erased it consumes the value instead, so it never lingers over a later state.\n let remoteFailureText: string | undefined\n let cpolarInstalled = false\n let cpolarConfigured = false\n let cloudflaredInstalled = false\n let cloudflaredDownloadSize = ''\n let cloudflaredTunnelMode: 'quick' | 'named' = 'quick'\n let cloudflaredModeDraft: 'quick' | 'named' | null = null\n let cloudflaredTunnelConfigured = false\n let configuredCloudflaredHostname = ''\n let configuredCloudflaredPort = 3444\n // Once the user types in a field, polls must stop writing to it, or a stale\n // value the user is mid-edit would be silently replaced by the saved one.\n let cloudflaredHostnameDirty = false\n let cloudflaredPortDirty = false\n let originConfigured = false\n let originFormDirty = false\n let originFormBusy = false\n let remoteSnapshotEpoch = 0\n let latestRemoteState: Record<string, unknown> = {}\n let frpInstalled = false\n let frpConfigured = false\n let frpLayoutInitialized = false\n let frpDownloadSize = '14.0'\n let configuredFrpServer = ''\n let configuredFrpPort = 7000\n let configuredFrpOrigin = ''\n let providerInfoPinned = false\n let providerInfoHovered = false\n let previousAccessView: 'lan' | 'remote' = 'lan'\n let diagnosticsBusy = false\n let copiedDiagnosticReport = ''\n let pluginUpdateAvailable = false\n let pluginLatestVersion = ''\n let pluginReleaseNotes = ''\n let updateInFlight = false\n const renderRelease = (data: Record<string, unknown>): void => {\n const release = clientReleaseInfo(data)\n pluginUpdateAvailable = release.updateAvailable\n pluginLatestVersion = release.latestVersion ?? ''\n pluginReleaseNotes = release.releaseNotes ?? ''\n updatePlugin.hidden = !pluginUpdateAvailable || !diagnosticsView.hidden || !updateCard.hidden\n updatePlugin.textContent = t('updatePlugin')\n if (pluginLatestVersion !== '') updatePlugin.setAttribute('aria-label', t('updatePluginAria', { version: pluginLatestVersion }))\n if (release.androidVersion !== undefined) {\n appDownload.textContent = t('downloadAndroidVersion', { version: release.androidVersion })\n appDownload.setAttribute('aria-label', t('downloadAndroidVersionAria', { version: release.androidVersion }))\n }\n appDownload.href = release.androidDownloadUrl\n }\n const syncProviderInfo = (): void => {\n const open = providerInfoPinned || providerInfoHovered || providerInfo.contains(document.activeElement)\n providerInfoPopover.hidden = !open\n providerInfoButton.setAttribute('aria-expanded', String(open))\n }\n providerInfo.addEventListener('pointerenter', () => { providerInfoHovered = true; syncProviderInfo() })\n providerInfo.addEventListener('pointerleave', () => { providerInfoHovered = false; syncProviderInfo() })\n providerInfo.addEventListener('focusin', syncProviderInfo)\n providerInfo.addEventListener('focusout', () => { window.setTimeout(syncProviderInfo, 0) })\n providerInfoButton.addEventListener('click', () => { providerInfoPinned = !providerInfoPinned; syncProviderInfo() })\n providerInfoButton.addEventListener('keydown', event => {\n if (event.key !== 'Escape') return\n providerInfoPinned = false\n providerInfoHovered = false\n providerInfoPopover.hidden = true\n providerInfoButton.setAttribute('aria-expanded', 'false')\n })\n const selectView = (view: 'lan' | 'remote' | 'diagnostics'): void => {\n if (view !== 'diagnostics') previousAccessView = view\n lanView.hidden = view !== 'lan'\n remoteView.hidden = view !== 'remote'\n diagnosticsView.hidden = view !== 'diagnostics'\n lanTab.classList.toggle('is-active', view === 'lan')\n remoteTab.classList.toggle('is-active', view === 'remote')\n lanTab.setAttribute('aria-pressed', String(view === 'lan'))\n remoteTab.setAttribute('aria-pressed', String(view === 'remote'))\n diagnosticsEntry.setAttribute('aria-pressed', String(view === 'diagnostics'))\n diagnosticsEntry.textContent = view === 'diagnostics' ? t('back') : t('diagnostics')\n diagnosticsEntry.setAttribute('aria-label', view === 'diagnostics' ? t('backToMobile') : t('openDiagnostics'))\n if (view === 'diagnostics') {\n wsPathsSeenAttempts = wsBlockedTotal(wsPathsBlocked)\n renderWsDot()\n if (!wsPathsLoaded) loadWsPaths()\n }\n updatePlugin.hidden = view === 'diagnostics' || !pluginUpdateAvailable\n appDownload.hidden = view === 'diagnostics'\n switcher.hidden = view === 'diagnostics'\n title.textContent = view === 'lan' ? t('lanAccess') : view === 'remote' ? t('remoteAccess') : t('connectionDiagnostics')\n }\n lanTab.addEventListener('click', () => { selectView('lan') })\n remoteTab.addEventListener('click', () => { selectView('remote'); loadRemote() })\n const setOpen = (open: boolean): void => {\n panel.hidden = !open\n for (const trigger of document.querySelectorAll('.dsh-mobile-control__trigger')) trigger.setAttribute('aria-expanded', String(open))\n }\n const render = (data: Record<string, unknown>): void => {\n lanConfigured = data.configured !== false\n lanRestartRequired = data.restartRequired === true\n running = data.running === true\n origin = running && typeof data.origin === 'string' ? data.origin : ''\n lanSetup.hidden = lanConfigured && !lanRestartRequired\n lanSetupForm.hidden = lanRestartRequired\n lanSetupIntro.hidden = lanRestartRequired\n status.hidden = !lanConfigured || lanRestartRequired\n if (!lanConfigured && !lanSetupLoaded) loadLanSetup()\n if (lanRestartRequired) {\n const pendingOrigin = typeof data.pendingOrigin === 'string' ? data.pendingOrigin : ''\n lanSetupStatus.textContent = t('lanSetupRestart', { origin: pendingOrigin })\n } else if (!lanConfigured && lanSetupStatus.textContent === '') {\n lanSetupStatus.textContent = t('lanSetupRequired')\n }\n access.hidden = origin === ''\n accessLink.href = origin\n accessLink.textContent = origin\n accessLink.title = origin\n status.classList.toggle('is-running', running)\n status.textContent = !lanConfigured ? t('lanSetupRequired') : lanRestartRequired ? t('lanSetupRestart', { origin: typeof data.pendingOrigin === 'string' ? data.pendingOrigin : '' }) : running ? t('lanOn') : t('lanOff')\n const extensionData = data.extensions\n if (extensionData !== null && typeof extensionData === 'object') {\n const loaded = typeof (extensionData as { loaded?: unknown }).loaded === 'number' ? (extensionData as { loaded: number }).loaded : 0\n const failed = typeof (extensionData as { failed?: unknown }).failed === 'number' ? (extensionData as { failed: number }).failed : 0\n extensionStatus.hidden = false\n extensionStatus.textContent = failed === 0 ? t('extensionsLoaded', { loaded }) : t('extensionsFailed', { loaded, failed })\n } else extensionStatus.hidden = true\n if (!running) qrBox.hidden = true\n toggle.textContent = running ? t('disableLan') : t('enableLan')\n actions.hidden = !lanConfigured || lanRestartRequired\n manageRow.hidden = !lanConfigured || lanRestartRequired\n pair.disabled = !running\n linkPair.disabled = !running\n manageDevices.disabled = !running\n resetAll.disabled = !running\n }\n const renderLanSetup = (data: Record<string, unknown>): void => {\n if (data.restartRequired === true || data.configured === true) {\n render(data)\n return\n }\n const networks = Array.isArray(data.networks) ? data.networks.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null) : []\n lanSetupSelect.replaceChildren()\n const hasRecommended = networks.some(network => network.recommended === true)\n if (!hasRecommended && networks.length > 1) {\n const placeholder = element('option'); placeholder.value = ''; placeholder.textContent = t('lanSetupChooseNetwork'); placeholder.disabled = true; placeholder.selected = true\n lanSetupSelect.append(placeholder)\n }\n for (const network of networks) {\n if (typeof network.address !== 'string' || typeof network.name !== 'string') continue\n const option = element('option'); option.value = network.address\n option.textContent = `${network.name} · ${network.address}${network.recommended === true ? ` · ${t('lanSetupRecommended')}` : ''}`\n option.selected = network.recommended === true || (!hasRecommended && networks.length === 1)\n lanSetupSelect.append(option)\n }\n const available = networks.some(network => typeof network.address === 'string' && typeof network.name === 'string')\n lanSetupConfigure.disabled = lanSetupSelect.value === ''\n lanSetupStatus.textContent = available ? t('lanSetupRequired') : t('lanSetupNoNetworks')\n lanSetupNote.hidden = data.windowsFirewall !== true\n }\n lanSetupSelect.addEventListener('change', () => { lanSetupConfigure.disabled = lanSetupSelect.value === '' })\n const loadLanSetup = (): void => {\n lanSetupLoaded = true\n lanSetupRefresh.disabled = true\n void controlRequestJson('/api/mobile-access/lan/setup').then(renderLanSetup, () => {\n lanSetupStatus.textContent = t('lanSetupFailed')\n }).finally(() => { lanSetupRefresh.disabled = false })\n }\n lanSetupRefresh.addEventListener('click', () => { loadLanSetup() })\n lanSetupConfigure.addEventListener('click', () => {\n if (lanSetupSelect.value === '') return\n lanSetupConfigure.disabled = true\n lanSetupRefresh.disabled = true\n lanSetupConfigure.textContent = t('lanSetupConfiguring')\n lanSetupStatus.textContent = t('lanSetupConfiguring')\n void controlRequestJson('/api/mobile-access/lan/setup', {\n method: 'POST',\n body: JSON.stringify({ confirm: true, address: lanSetupSelect.value, configureFirewall: true }),\n }, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(renderLanSetup, () => {\n lanSetupStatus.textContent = t('lanSetupFailed')\n }).finally(() => {\n lanSetupConfigure.textContent = t('lanSetupConfigure')\n lanSetupRefresh.disabled = false\n lanSetupConfigure.disabled = lanRestartRequired || lanSetupSelect.options.length === 0\n })\n })\n const showQr = (svg: string, target: HTMLDivElement = qrBox): void => {\n target.replaceChildren()\n if (svg === '') { target.hidden = true; return }\n const image = element('img')\n image.alt = t('pairingQr')\n image.width = 176\n image.height = 176\n image.src = `data:image/svg+xml;base64,${btoa(svg)}`\n target.hidden = false\n target.append(image)\n }\n const openPairing = (target: 'key' | 'link'): void => {\n void controlRequestJson('/api/mobile-access/lan/pairing/open', { method: 'POST', body: '{}' }).then(async data => {\n const value = target === 'key'\n ? (typeof data.appKey === 'string' ? data.appKey : '')\n : (typeof data.pairUrl === 'string' ? data.pairUrl : '')\n showQr(typeof data.qrSvg === 'string' ? data.qrSvg : '')\n if (value === '') { status.textContent = t('keyGenerationFailed'); return }\n try {\n await navigator.clipboard.writeText(value)\n status.textContent = target === 'key'\n ? t('keyCopied')\n : t('linkCopied')\n } catch {\n status.textContent = t('copySecret', { kind: target === 'key' ? t('pairingKey') : t('pairingLink'), value })\n status.classList.add('is-key')\n }\n }, error => { status.textContent = t('requestFailed', { error: String(error) }) }).finally(() => {\n pair.disabled = !running\n linkPair.disabled = !running\n })\n }\n toggle.addEventListener('click', () => { toggle.disabled = true; void controlRequestJson('/api/mobile-access/lan/control', { method: 'POST', body: JSON.stringify({ running: !running }) }).then(render, error => { status.textContent = t('requestFailed', { error: String(error) }) }).finally(() => { toggle.disabled = false }) })\n const formatTime = (ms: unknown): string => typeof ms === 'number' ? new Date(ms).toLocaleString(localeTag) : ''\n const formatMegabytes = (bytes: number): string => new Intl.NumberFormat(localeTag, { minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(bytes / 1024 / 1024)\n const renderDevices = (data: Record<string, unknown>): void => {\n const devices = Array.isArray(data.devices) ? data.devices as Record<string, unknown>[] : []\n devicePanel.replaceChildren()\n if (devices.length === 0) {\n const empty = element('p', 'dsh-mobile-control__device-empty'); empty.textContent = t('noDevices')\n devicePanel.append(empty)\n return\n }\n for (const device of devices) {\n const row = element('div', 'dsh-mobile-control__device')\n const label = element('span', 'dsh-mobile-control__device-label')\n label.textContent = typeof device.label === 'string' ? device.label : t('device')\n const meta = element('span', 'dsh-mobile-control__device-meta'); meta.textContent = t('expires', { time: formatTime(device.expiresAt) })\n const revoke = element('button', 'dsh-mobile-control__device-revoke'); revoke.type = 'button'; revoke.textContent = t('revoke')\n const id = typeof device.id === 'string' ? device.id : ''\n revoke.addEventListener('click', () => {\n void controlRequestJson('/api/mobile-access/lan/devices/revoke', { method: 'POST', body: JSON.stringify({ deviceId: id }) })\n .then(loadDevices, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n })\n row.append(label, meta, revoke)\n devicePanel.append(row)\n }\n }\n const loadDevices = (): void => {\n void controlRequestJson('/api/mobile-access/lan/devices').then(renderDevices, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n }\n manageDevices.addEventListener('click', () => {\n const show = devicePanel.hidden\n devicePanel.hidden = !show\n if (show) loadDevices()\n })\n resetAll.addEventListener('click', () => {\n if (!window.confirm(t('confirmResetDevices'))) return\n void controlRequestJson('/api/mobile-access/lan/devices/reset', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(loadDevices, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n })\n const renderRemote = (data: Record<string, unknown>): void => {\n latestRemoteState = data\n remoteRunning = data.running === true\n remoteProvider = parseMobileRemoteProvider(data.provider)\n const cpolar = remoteProvider === 'cpolar'\n const cloudflared = remoteProvider === 'cloudflared'\n const frp = remoteProvider === 'frp'\n const origin = remoteProvider === 'origin'\n const tailscale = remoteProvider === 'tailscale'\n tailscaleChoice.classList.toggle('is-selected', tailscale)\n cpolarChoice.classList.toggle('is-selected', cpolar)\n cloudflaredChoice.classList.toggle('is-selected', cloudflared)\n frpChoice.classList.toggle('is-selected', frp)\n originChoice.classList.toggle('is-selected', origin)\n tailscaleChoice.setAttribute('aria-checked', String(tailscale))\n cpolarChoice.setAttribute('aria-checked', String(cpolar))\n cloudflaredChoice.setAttribute('aria-checked', String(cloudflared))\n // One tab stop for the group: only the checked card is reachable with Tab.\n tailscaleChoice.tabIndex = tailscale ? 0 : -1\n cpolarChoice.tabIndex = cpolar ? 0 : -1\n cloudflaredChoice.tabIndex = cloudflared ? 0 : -1\n frpChoice.setAttribute('aria-pressed', String(frp))\n originChoice.setAttribute('aria-pressed', String(origin))\n providerSetupName.textContent = cpolar ? 'cpolar' : cloudflared ? 'cloudflared' : frp ? t('frpName') : origin ? t('originName') : 'Tailscale Funnel'\n tailscaleChoice.disabled = remoteProviderBusy\n cpolarChoice.disabled = remoteProviderBusy\n cloudflaredChoice.disabled = remoteProviderBusy\n frpChoice.disabled = remoteProviderBusy\n originChoice.disabled = remoteProviderBusy\n cpolarSetup.hidden = !cpolar\n cloudflaredSetup.hidden = !cloudflared\n frpSetup.hidden = !frp\n originSetup.hidden = !origin\n if (frp || origin) selfHosted.open = true\n tailscaleInfo.hidden = !tailscale\n remoteReset.textContent = tailscale ? t('resetRemoteLogin') : t('resetRemoteDevices')\n const providers = data.providers !== null && typeof data.providers === 'object' ? data.providers as Record<string, unknown> : {}\n const cpolarProvider = providers.cpolar !== null && typeof providers.cpolar === 'object' ? providers.cpolar as Record<string, unknown> : {}\n const component = cpolarProvider.component !== null && typeof cpolarProvider.component === 'object'\n ? cpolarProvider.component as Record<string, unknown>\n : {}\n cpolarInstalled = component.installed === true\n cpolarConfigured = component.configured === true\n cpolarChoiceBadge.textContent = cpolarConfigured ? t('ready') : cpolarInstalled ? t('installed') : t('mainlandPreferred')\n const cpolarSupported = component.supported !== false\n const componentVersion = typeof component.version === 'string' ? component.version : ''\n const componentDownloadBytes = typeof component.downloadBytes === 'number' ? component.downloadBytes : 0\n const componentStorage = typeof component.storagePath === 'string' ? component.storagePath : `DSH Mobile ${t('pluginPrivateDirectory')}`\n cpolarStorage.textContent = componentStorage\n cpolarStorage.title = componentStorage\n cpolarInstall.hidden = cpolarInstalled || !cpolarSupported\n cpolarInstall.textContent = componentDownloadBytes > 0\n ? t('installWithSize', { size: formatMegabytes(componentDownloadBytes) })\n : t('installOfficial')\n cpolarInstall.disabled = remoteProviderBusy\n cpolarAccount.hidden = !cpolarInstalled || cpolarConfigured\n cpolarConfigure.disabled = remoteProviderBusy\n cpolarPurge.hidden = !cpolarInstalled && !cpolarConfigured\n cpolarComponentStatus.textContent = !cpolarSupported\n ? t('cpolarUnsupported')\n : !cpolarInstalled\n ? t('cpolarNotInstalled')\n : !cpolarConfigured\n ? t('cpolarNeedsToken', { version: componentVersion })\n : t('cpolarReady', { version: componentVersion })\n const cloudflaredProvider = providers.cloudflared !== null && typeof providers.cloudflared === 'object' ? providers.cloudflared as Record<string, unknown> : {}\n const cloudflaredComponent = cloudflaredProvider.component !== null && typeof cloudflaredProvider.component === 'object'\n ? cloudflaredProvider.component as Record<string, unknown>\n : {}\n cloudflaredInstalled = cloudflaredComponent.installed === true\n const cloudflaredSupported = cloudflaredComponent.supported !== false\n const cloudflaredComponentError = typeof cloudflaredComponent.errorCode === 'string' ? cloudflaredComponent.errorCode : ''\n const cloudflaredVersion = typeof cloudflaredComponent.version === 'string' ? cloudflaredComponent.version : ''\n const cloudflaredDownloadBytes = typeof cloudflaredComponent.downloadBytes === 'number' ? cloudflaredComponent.downloadBytes : 0\n if (cloudflaredDownloadBytes > 0) cloudflaredDownloadSize = formatMegabytes(cloudflaredDownloadBytes)\n const cloudflaredStoragePath = typeof cloudflaredComponent.storagePath === 'string'\n ? cloudflaredComponent.storagePath\n : `DSH Mobile ${t('pluginPrivateDirectory')}`\n cloudflaredStorage.textContent = cloudflaredStoragePath\n cloudflaredStorage.title = cloudflaredStoragePath\n cloudflaredChoiceBadge.textContent = cloudflaredInstalled ? t('ready') : t('cloudflaredBadge')\n // A quick tunnel needs no account, so installation is the only precondition.\n cloudflaredInstall.hidden = cloudflaredInstalled || !cloudflaredSupported\n cloudflaredInstall.textContent = cloudflaredDownloadBytes > 0\n ? t('installWithSize', { size: formatMegabytes(cloudflaredDownloadBytes) })\n : t('installOfficial')\n cloudflaredInstall.disabled = remoteProviderBusy\n cloudflaredPurge.hidden = !cloudflaredInstalled\n cloudflaredComponentStatus.textContent = !cloudflaredSupported\n ? t('cloudflaredUnsupported')\n : !cloudflaredInstalled\n // A component that exists but failed verification must not be reported as\n // absent: that sends the user to reinstall something already installed.\n ? (cloudflaredComponentError === 'cloudflared_component_invalid' ? t('cloudflaredInvalid') : t('cloudflaredNotInstalled'))\n : t('cloudflaredReady', { version: cloudflaredVersion })\n const cloudflaredTunnelConfiguration = cloudflaredProvider.configuration !== null\n && typeof cloudflaredProvider.configuration === 'object'\n ? cloudflaredProvider.configuration as Record<string, unknown>\n : {}\n cloudflaredTunnelMode = cloudflaredTunnelConfiguration.mode === 'named' ? 'named' : 'quick'\n cloudflaredTunnelConfigured = cloudflaredTunnelConfiguration.configured === true\n configuredCloudflaredHostname = typeof cloudflaredTunnelConfiguration.hostname === 'string' ? cloudflaredTunnelConfiguration.hostname : ''\n configuredCloudflaredPort = typeof cloudflaredTunnelConfiguration.port === 'number' ? cloudflaredTunnelConfiguration.port : 3444\n const cloudflaredTunnelError = typeof cloudflaredTunnelConfiguration.errorCode === 'string' ? cloudflaredTunnelConfiguration.errorCode : ''\n // A staged choice survives polls until the saved configuration agrees with it.\n if (cloudflaredModeDraft !== null && cloudflaredModeDraft === cloudflaredTunnelMode) cloudflaredModeDraft = null\n const cloudflaredShownMode = cloudflaredModeDraft ?? cloudflaredTunnelMode\n cloudflaredModeQuick.classList.toggle('is-active', cloudflaredShownMode === 'quick')\n cloudflaredModeNamed.classList.toggle('is-active', cloudflaredShownMode === 'named')\n cloudflaredModeQuick.setAttribute('aria-pressed', String(cloudflaredShownMode === 'quick'))\n cloudflaredModeNamed.setAttribute('aria-pressed', String(cloudflaredShownMode === 'named'))\n cloudflaredModeQuick.disabled = remoteProviderBusy\n cloudflaredModeNamed.disabled = remoteProviderBusy\n cloudflaredModeHint.textContent = cloudflaredShownMode === 'named' ? t('cloudflaredTunnelNamedHint') : t('cloudflaredTunnelQuickHint')\n // The component note describes whichever tunnel is about to run, so a\n // quick-tunnel warning never sits next to named-tunnel settings.\n cloudflaredNote.textContent = cloudflaredShownMode === 'named' ? t('cloudflaredNamedNote') : t('cloudflaredQuickNote')\n cloudflaredTunnel.hidden = cloudflaredShownMode !== 'named'\n cloudflaredTunnelSave.textContent = cloudflaredSaveLabel()\n // Untouched fields always mirror the saved configuration, so a field can never\n // keep showing a value that is no longer in effect.\n if (!cloudflaredHostnameDirty) cloudflaredHostname.value = configuredCloudflaredHostname\n if (!cloudflaredPortDirty) cloudflaredPort.value = String(configuredCloudflaredPort)\n cloudflaredToken.placeholder = cloudflaredTunnelConfigured ? t('cloudflaredTunnelTokenSaved') : t('cloudflaredTunnelTokenPlaceholder')\n // The hint must describe the port Cloudflare will actually be pointed at, which\n // is the one in the field, not the last saved value.\n const cloudflaredFieldPort = String(cloudflaredPort.value ?? '').trim()\n cloudflaredTunnelHint.textContent = t('cloudflaredTunnelHint', {\n port: cloudflaredFieldPort === '' ? String(configuredCloudflaredPort) : cloudflaredFieldPort,\n })\n cloudflaredTunnelStatus.textContent = cloudflaredTunnelError !== ''\n ? t('cloudflaredTunnelInvalid')\n : cloudflaredTunnelConfigured\n ? t('cloudflaredTunnelConfigured', { hostname: configuredCloudflaredHostname, port: String(configuredCloudflaredPort) })\n : t('cloudflaredTunnelMissing')\n cloudflaredTunnelSave.disabled = remoteProviderBusy || !cloudflaredInstalled\n cloudflaredTunnelForget.hidden = !cloudflaredTunnelConfigured\n const frpProvider = providers.frp !== null && typeof providers.frp === 'object' ? providers.frp as Record<string, unknown> : {}\n const frpComponent = frpProvider.component !== null && typeof frpProvider.component === 'object'\n ? frpProvider.component as Record<string, unknown>\n : {}\n const frpConfiguration = frpProvider.configuration !== null && typeof frpProvider.configuration === 'object'\n ? frpProvider.configuration as Record<string, unknown>\n : {}\n frpInstalled = frpComponent.installed === true\n frpConfigured = frpConfiguration.configured === true\n const frpSupported = frpComponent.supported !== false\n const frpVersion = typeof frpComponent.version === 'string' ? frpComponent.version : ''\n const frpDownloadBytes = typeof frpComponent.downloadBytes === 'number' ? frpComponent.downloadBytes : 0\n if (frpDownloadBytes > 0) frpDownloadSize = formatMegabytes(frpDownloadBytes)\n const frpStoragePath = typeof frpComponent.storagePath === 'string' ? frpComponent.storagePath : `DSH Mobile ${t('pluginPrivateDirectory')}`\n configuredFrpServer = typeof frpConfiguration.serverAddress === 'string' ? frpConfiguration.serverAddress : ''\n configuredFrpPort = typeof frpConfiguration.serverPort === 'number' ? frpConfiguration.serverPort : 7000\n configuredFrpOrigin = typeof frpConfiguration.publicOrigin === 'string' ? frpConfiguration.publicOrigin : ''\n if (frpServer.value === '' && configuredFrpServer !== '') frpServer.value = configuredFrpServer\n if ((frpPort.value === '' || frpPort.value === '7000') && configuredFrpPort !== 7000) frpPort.value = String(configuredFrpPort)\n if (frpOrigin.value === '' && configuredFrpOrigin !== '') frpOrigin.value = configuredFrpOrigin\n frpStorage.textContent = frpStoragePath\n frpStorage.title = frpStoragePath\n frpInstall.hidden = frpInstalled || !frpSupported\n frpInstall.textContent = frpDownloadBytes > 0\n ? t('installWithSize', { size: formatMegabytes(frpDownloadBytes) })\n : t('installFrpc')\n frpInstall.disabled = remoteProviderBusy\n frpConfigure.disabled = remoteProviderBusy || !frpInstalled\n vpsDeploy.disabled = remoteProviderBusy || !validVpsDeploymentForm()\n vpsCopyUninstall.disabled = vpsDeploy.disabled\n vpsUninstall.disabled = vpsDeploy.disabled\n frpPurge.hidden = !frpInstalled && !frpConfigured\n frpComponentStatus.textContent = !frpSupported\n ? t('frpUnsupported')\n : frpInstalled ? t('frpComponentReady', { version: frpVersion }) : t('frpNotInstalled')\n frpConfigurationStatus.textContent = frpConfigured ? t('frpConfigurationReady') : t('frpConfigurationMissing')\n frpOverview.hidden = !frpConfigured\n frpOverviewEndpoint.textContent = configuredFrpOrigin === ''\n ? configuredFrpServer\n : `${configuredFrpOrigin} · ${configuredFrpServer}:${String(configuredFrpPort)}`\n frpToken.placeholder = frpConfigured\n ? (locale === 'zh' ? '已安全保存;留空保持不变' : locale === 'it' ? 'Salvato; lascia vuoto per mantenerlo' : 'Saved securely; leave blank to keep it')\n : t('frpTokenPlaceholder')\n vpsDeploy.textContent = frpConfigured\n ? (locale === 'zh' ? '修复或重新部署 VPS' : locale === 'it' ? 'Ripara o ridistribuisci VPS' : 'Repair or redeploy VPS')\n : t('vpsDeploy')\n frpConnectionSummary.textContent = `${frpConfigured ? '✓ ' : ''}${t('frpStep1Title')}`\n frpComponentSummary.textContent = `${frpInstalled ? '✓ ' : ''}${t('frpStep3Title')}`\n if (!frpLayoutInitialized) {\n frpConnectionGroup.open = !frpConfigured\n frpComponentGroup.open = !frpInstalled\n frpVpsGroup.open = !frpConfigured\n frpLayoutInitialized = true\n }\n const originProvider = providers.origin !== null && typeof providers.origin === 'object' ? providers.origin as Record<string, unknown> : {}\n const originConfiguration = originProvider.configuration !== null && typeof originProvider.configuration === 'object'\n ? originProvider.configuration as Record<string, unknown> : {}\n originConfigured = originConfiguration.configured === true\n if (!originFormDirty && !originFormBusy) {\n originPublic.value = typeof originConfiguration.publicOrigin === 'string' ? originConfiguration.publicOrigin : ''\n originHost.value = typeof originConfiguration.listenHost === 'string' ? originConfiguration.listenHost : '127.0.0.1'\n originPort.value = typeof originConfiguration.listenPort === 'number' ? String(originConfiguration.listenPort) : '3444'\n originCidrs.value = Array.isArray(originConfiguration.allowedCidrs) ? originConfiguration.allowedCidrs.filter(value => typeof value === 'string').join(', ') : '127.0.0.0/8'\n }\n originSetup.setAttribute('aria-busy', String(originFormBusy))\n originConfigure.setAttribute('aria-busy', String(originFormBusy))\n originConfigure.textContent = originFormBusy ? t('saving') : t('originSaveStart')\n for (const input of [originPublic, originHost, originPort, originCidrs]) input.disabled = remoteProviderBusy\n originConfigure.disabled = remoteProviderBusy\n originPurge.disabled = remoteProviderBusy\n originPurge.hidden = !originConfigured && originConfiguration.errorCode !== 'origin_config_invalid' && originProvider.running !== true\n const originListening = origin && originProvider.state === 'ready' && originProvider.running === true\n const backendOrigin = originListening && typeof data.backendOrigin === 'string' ? data.backendOrigin\n : typeof originConfiguration.backendOrigin === 'string' ? originConfiguration.backendOrigin : ''\n originBackend.hidden = backendOrigin === ''\n originBackendLabel.textContent = originListening ? t('originBackendListening') : t('originBackendSaved')\n originBackendAddress.textContent = backendOrigin\n originCopyBackend.disabled = remoteProviderBusy || backendOrigin === ''\n selfHostedBadge.textContent = origin\n ? originListening ? t('originBackendReady') : t('advanced')\n : frpConfigured && frpInstalled ? t('ready') : t('advanced')\n const state = typeof data.state === 'string' ? data.state : 'error'\n const errorCode = typeof data.errorCode === 'string' ? data.errorCode : ''\n const remoteOrigin = typeof data.origin === 'string' ? data.origin : ''\n remoteLoginUrl = typeof data.loginUrl === 'string' ? data.loginUrl : ''\n const candidateSetupUrl = tailscale ? officialFunnelSetupUrl(data.setupUrl) : ''\n const fallbackSetupUrls: Record<string, string> = {\n funnel_permission_required: 'https://tailscale.com/s/no-funnel',\n funnel_https_required: 'https://tailscale.com/s/https',\n funnel_start_failed: 'https://tailscale.com/s/no-funnel',\n }\n remoteSetupUrl = candidateSetupUrl !== '' ? candidateSetupUrl : (fallbackSetupUrls[errorCode] ?? '')\n const needsFunnelSetup = state === 'error' && remoteSetupUrl !== ''\n remoteReady = remoteRunning && state === 'ready' && remoteOrigin !== ''\n remoteStateBadge.classList.toggle('is-ready', remoteReady)\n remoteStateBadge.classList.toggle('is-busy', state === 'starting' || state === 'connecting' || state === 'needs-login')\n remoteStateBadge.classList.toggle('is-attention', state === 'error' || state === 'unavailable')\n remoteStateBadge.textContent = remoteReady\n ? origin ? t('originBackendReady') : t('ready')\n : state === 'starting' || state === 'connecting' || state === 'needs-login'\n ? t('remoteStateConnecting')\n : state === 'error' || state === 'unavailable'\n ? t('remoteStateAttention')\n : t('remoteStateOff')\n remoteAccess.hidden = !remoteReady\n remoteAccessLink.href = remoteOrigin\n remoteAccessLink.textContent = remoteOrigin\n remoteAccessLink.title = remoteOrigin\n remoteStatus.classList.toggle('is-running', remoteReady)\n const labels: Record<string, string> = {\n off: t('remoteOff'),\n unavailable: cpolar ? t('remoteUnavailableCpolar')\n : cloudflared ? t('remoteUnavailableCloudflared')\n : frp ? t('remoteUnavailableFrp') : origin ? t('originUnavailable') : t('remoteUnavailableTailscale'),\n starting: cpolar ? t('remoteStartingCpolar')\n : cloudflared ? t('remoteStartingCloudflared')\n : frp ? t('remoteStartingFrp') : origin ? t('originStarting') : t('remoteStartingTailscale'),\n 'needs-login': t('remoteNeedsLogin'),\n connecting: cpolar ? t('remoteConnectingCpolar')\n : cloudflared ? t('remoteConnectingCloudflared')\n : frp ? t('remoteConnectingFrp') : t('remoteConnectingTailscale'),\n ready: origin ? t('originReady') : t('remoteReady'),\n error: t('remoteError'),\n }\n const errorLabels: Record<string, string> = translatedRemoteErrors()\n remoteStatus.textContent = remoteSetupPending && needsFunnelSetup\n ? t('setupOpened')\n : (state === 'error' ? (errorLabels[errorCode] ?? labels.error!) : (labels[state] ?? labels.error!))\n // Keep a just-failed action's message for this one repaint, then release it.\n if (remoteFailureText !== undefined) {\n remoteStatus.textContent = remoteFailureText\n remoteFailureText = undefined\n }\n remoteGuide.hidden = !needsFunnelSetup\n remoteSetup.disabled = remoteSetupUrl === '' || remoteReconnectBusy\n remoteSetupRetry.disabled = remoteReconnectBusy\n remoteToggle.textContent = remoteRunning ? t('disableRemote') : t('enableRemote')\n const providerPrepared = cpolar ? cpolarInstalled && cpolarConfigured\n : cloudflared ? cloudflaredInstalled\n : frp ? frpInstalled && frpConfigured : origin ? originConfigured : true\n remoteToggle.disabled = remoteProviderBusy || (!providerPrepared && !(origin && remoteRunning))\n remoteLogin.hidden = !tailscale || state !== 'needs-login' || remoteLoginUrl === ''\n remoteReconnect.hidden = needsFunnelSetup || (state !== 'error' && state !== 'unavailable')\n || !providerPrepared\n remoteActions.hidden = !providerPrepared && !(origin && remoteRunning)\n remotePair.disabled = !remoteReady || originFormBusy\n remoteCopyLink.disabled = !remoteReady || originFormBusy\n remoteDevices.disabled = !remoteReady || originFormBusy\n if (origin) { remoteReconnect.disabled = remoteProviderBusy || remoteReconnectBusy; remoteReset.disabled = remoteProviderBusy }\n else remoteReset.disabled = false\n if (!remoteReady) {\n remoteQr.hidden = true\n remotePairLink.hidden = true\n activeRemotePairUrl = ''\n activeRemotePairExpiresAt = 0\n }\n if (!needsFunnelSetup) remoteSetupPending = false\n }\n let remoteLoadInFlight = false\n const loadRemote = (): void => {\n if (remoteLoadInFlight || originFormBusy) return\n const epoch = remoteSnapshotEpoch\n remoteLoadInFlight = true\n void controlRequestJson('/api/mobile-access/remote/control')\n .then(data => { if (epoch === remoteSnapshotEpoch) renderRemote(data) }, error => {\n if (epoch === remoteSnapshotEpoch) remoteStatus.textContent = t('requestFailed', { error: String(error) })\n })\n .finally(() => { remoteLoadInFlight = false })\n }\n const chooseRemoteProvider = (provider: MobileRemoteProvider): void => {\n if (remoteProviderBusy || provider === remoteProvider) return\n if (remoteRunning && !window.confirm(t('switchProviderConfirm'))) return\n remoteProviderBusy = true\n tailscaleChoice.disabled = true\n cpolarChoice.disabled = true\n cloudflaredChoice.disabled = true\n frpChoice.disabled = true\n originChoice.disabled = true\n remoteStatus.textContent = provider === 'cpolar' ? t('switchingCpolar')\n : provider === 'cloudflared' ? t('switchingCloudflared')\n : provider === 'frp' ? t('switchingFrp') : provider === 'origin' ? t('switchingOrigin') : t('switchingTailscale')\n void controlRequestJson('/api/mobile-access/remote/provider', { method: 'POST', body: JSON.stringify({ provider }) })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n }\n tailscaleChoice.addEventListener('click', () => { chooseRemoteProvider('tailscale') })\n cpolarChoice.addEventListener('click', () => { chooseRemoteProvider('cpolar') })\n cloudflaredChoice.addEventListener('click', () => { chooseRemoteProvider('cloudflared') })\n frpChoice.addEventListener('click', () => { chooseRemoteProvider('frp') })\n originChoice.addEventListener('click', () => { chooseRemoteProvider('origin') })\n cloudflaredInstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const accepted = window.confirm(t('installConfirmCloudflared', { size: cloudflaredDownloadSize }))\n if (!accepted) return\n remoteProviderBusy = true\n cloudflaredInstall.disabled = true\n cloudflaredInstall.textContent = t('downloading')\n remoteStatus.textContent = t('installingCloudflared')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/component/install', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'installFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n cloudflaredPurge.addEventListener('click', () => {\n if (remoteProviderBusy) return\n if (!window.confirm(t('purgeCloudflaredConfirm'))) return\n remoteProviderBusy = true\n cloudflaredPurge.disabled = true\n remoteStatus.textContent = t('purgingCloudflared')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/component/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = t('purgeFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cloudflaredPurge.disabled = false; loadRemote() })\n })\n // Tunnel type is staged locally and only reaches the provider through Save, so\n // clicking between the two never silently discards a saved token.\n const showCloudflaredMode = (mode: 'quick' | 'named'): void => {\n cloudflaredModeDraft = mode\n cloudflaredModeQuick.classList.toggle('is-active', mode === 'quick')\n cloudflaredModeNamed.classList.toggle('is-active', mode === 'named')\n cloudflaredModeQuick.setAttribute('aria-pressed', String(mode === 'quick'))\n cloudflaredModeNamed.setAttribute('aria-pressed', String(mode === 'named'))\n cloudflaredModeHint.textContent = mode === 'named' ? t('cloudflaredTunnelNamedHint') : t('cloudflaredTunnelQuickHint')\n // The component note follows the same choice immediately. Waiting for the next poll\n // would leave a quick-tunnel warning sitting beside named-tunnel settings.\n cloudflaredNote.textContent = mode === 'named' ? t('cloudflaredNamedNote') : t('cloudflaredQuickNote')\n cloudflaredTunnel.hidden = mode !== 'named'\n cloudflaredTunnelSave.textContent = cloudflaredSaveLabel()\n }\n cloudflaredModeQuick.addEventListener('click', () => { if (!remoteProviderBusy) showCloudflaredMode('quick') })\n cloudflaredModeNamed.addEventListener('click', () => { if (!remoteProviderBusy) showCloudflaredMode('named') })\n cloudflaredHostname.addEventListener('input', () => { cloudflaredHostnameDirty = true })\n cloudflaredPort.addEventListener('input', () => { cloudflaredPortDirty = true })\n cloudflaredTunnelSave.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const mode = cloudflaredModeDraft ?? cloudflaredTunnelMode\n const hostname = String(cloudflaredHostname.value ?? '').trim()\n const portValue = String(cloudflaredPort.value ?? '').trim()\n const port = Number(portValue)\n const token = String(cloudflaredToken.value ?? '')\n // The provider re-validates everything; these checks only keep an obviously\n // incomplete form from costing a round trip.\n if (mode === 'named') {\n if (hostname === '') { remoteStatus.textContent = t('cloudflaredTunnelHostnameInvalid'); return }\n if (portValue === '' || !Number.isSafeInteger(port) || port < 1024 || port > 65535) {\n remoteStatus.textContent = t('cloudflaredTunnelPortInvalid')\n return\n }\n // The LAN gateway owns 3443 for as long as DSH runs, so this is not a\n // transient conflict worth a round trip.\n if (port === 3443) { remoteStatus.textContent = t('cloudflaredTunnelPortReserved'); return }\n if (token === '' && !cloudflaredTunnelConfigured) { remoteStatus.textContent = t('cloudflaredTunnelConfigMissing'); return }\n if (token !== '' && (token.length < 32 || !/^[A-Za-z0-9_=+/-]+$/u.test(token))) {\n remoteStatus.textContent = t('cloudflaredTunnelTokenInvalid')\n return\n }\n }\n remoteProviderBusy = true\n cloudflaredTunnelSave.disabled = true\n cloudflaredTunnelSave.textContent = t('saving')\n remoteStatus.textContent = mode === 'named' ? t('cloudflaredTunnelSaving') : t('switchingCloudflared')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/tunnel', {\n method: 'POST',\n body: JSON.stringify(mode === 'named' ? { mode, hostname, port, token } : { mode: 'quick' }),\n })\n .then(data => {\n // The token is write-only: never keep a copy in the DOM after saving.\n cloudflaredToken.value = ''\n cloudflaredModeDraft = null\n // The saved values are now authoritative, so let the next poll restore them.\n cloudflaredHostnameDirty = false\n cloudflaredPortDirty = false\n remoteFailureText = ''\n renderRemote(data)\n }, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => {\n remoteProviderBusy = false\n cloudflaredTunnelSave.disabled = false\n cloudflaredTunnelSave.textContent = cloudflaredSaveLabel()\n loadRemote()\n })\n })\n cloudflaredTunnelForget.addEventListener('click', () => {\n if (remoteProviderBusy) return\n if (!window.confirm(t('cloudflaredTunnelForgetConfirm'))) return\n remoteProviderBusy = true\n cloudflaredTunnelForget.disabled = true\n remoteStatus.textContent = t('cloudflaredTunnelForgetting')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/tunnel/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(data => {\n cloudflaredToken.value = ''\n cloudflaredHostname.value = ''\n cloudflaredPort.value = '3444'\n cloudflaredModeDraft = null\n cloudflaredHostnameDirty = false\n cloudflaredPortDirty = false\n remoteFailureText = ''\n renderRemote(data)\n }, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cloudflaredTunnelForget.disabled = false; loadRemote() })\n })\n cpolarInstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const accepted = window.confirm(t('installConfirm'))\n if (!accepted) return\n remoteProviderBusy = true\n cpolarInstall.disabled = true\n cpolarInstall.textContent = t('downloading')\n remoteStatus.textContent = t('installingCpolar')\n void controlRequestJson('/api/mobile-access/remote/cpolar/component/install', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'installFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n cpolarConfigure.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const authtoken = cpolarToken.value.trim()\n if (authtoken.length < 20 || /\\s/u.test(authtoken)) {\n remoteStatus.textContent = t('invalidToken')\n cpolarToken.focus()\n return\n }\n remoteProviderBusy = true\n cpolarConfigure.disabled = true\n cpolarConfigure.setAttribute('aria-busy', 'true')\n cpolarConfigure.textContent = t('saving')\n void controlRequestJson('/api/mobile-access/remote/cpolar/configure', { method: 'POST', body: JSON.stringify({ authtoken }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(() => {\n cpolarToken.value = ''\n remoteStatus.textContent = t('configuredConnecting')\n return controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: true }) })\n })\n .then(renderRemote, error => { remoteFailureText = t('configureFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cpolarConfigure.setAttribute('aria-busy', 'false'); cpolarConfigure.textContent = t('saveConnect'); loadRemote() })\n })\n cpolarPurge.addEventListener('click', () => {\n if (remoteProviderBusy) return\n if (!window.confirm(t('purgeConfirm'))) return\n remoteProviderBusy = true\n cpolarPurge.disabled = true\n remoteStatus.textContent = t('purging')\n void controlRequestJson('/api/mobile-access/remote/cpolar/component/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = t('purgeFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cpolarPurge.disabled = false; loadRemote() })\n })\n const validFrpServer = (value: string): boolean => value === value.trim() && value.length > 0 && value.length <= 253\n && !/[\\s\\u0000-\\u001f\\u007f/\\\\@?#]/u.test(value)\n const frpForm = (): { readonly serverAddress: string; readonly serverPort: number; readonly token: string; readonly publicOrigin: string } => ({\n serverAddress: String(frpServer.value ?? '').trim(),\n serverPort: Number(frpPort.value),\n token: String(frpToken.value ?? ''),\n publicOrigin: String(frpOrigin.value ?? '').trim(),\n })\n const validFrpForm = (form: ReturnType<typeof frpForm>): boolean => {\n if (!validFrpServer(form.serverAddress)) return false\n try {\n createFrpServerTemplateForClipboard(form.serverPort, form.token, form.publicOrigin)\n return true\n } catch { return false }\n }\n frpCopyTemplate.addEventListener('click', () => {\n const form = frpForm()\n if (!validFrpForm(form)) {\n remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n void navigator.clipboard.writeText(createFrpServerTemplateForClipboard(form.serverPort, form.token, form.publicOrigin))\n .then(() => { remoteStatus.textContent = t('templateCopied') }, () => { remoteStatus.textContent = t('templateCopyFailed') })\n })\n const validVpsSshUser = (value: string): boolean => /^[a-z_][a-z0-9_.-]*[$]?$/iu.test(value) && value.length <= 64\n const validVpsSshKey = (value: string): boolean => value === '' || (/^[a-zA-Z]:[\\\\/]/u.test(value) || value.startsWith('/'))\n const vpsFormStorageKey = 'dsh-mobile.frp-vps-form.v1'\n try {\n const saved = JSON.parse(localStorage.getItem(vpsFormStorageKey) ?? '{}') as Record<string, unknown>\n if (typeof saved.sshUser === 'string' && validVpsSshUser(saved.sshUser)) vpsSshUser.value = saved.sshUser\n if (typeof saved.sshPort === 'number' && Number.isSafeInteger(saved.sshPort) && saved.sshPort >= 1 && saved.sshPort <= 65_535) vpsSshPort.value = String(saved.sshPort)\n if (typeof saved.sshKeyPath === 'string' && validVpsSshKey(saved.sshKeyPath)) vpsSshKey.value = saved.sshKeyPath\n } catch { /* Browser storage is an optional convenience only. */ }\n const saveVpsForm = (): void => {\n try {\n localStorage.setItem(vpsFormStorageKey, JSON.stringify({\n sshUser: String(vpsSshUser.value ?? '').trim(),\n sshPort: Number(vpsSshPort.value),\n sshKeyPath: String(vpsSshKey.value ?? '').trim(),\n }))\n } catch { /* Private browsing or a full quota must not block deployment. */ }\n }\n const validVpsDeploymentForm = (): boolean => {\n const sshPort = Number(vpsSshPort.value)\n return validVpsFrpForm()\n && validVpsSshUser(String(vpsSshUser.value ?? '').trim())\n && Number.isSafeInteger(sshPort) && sshPort >= 1 && sshPort <= 65_535\n && validVpsSshKey(String(vpsSshKey.value ?? '').trim())\n }\n /**\n * VPS actions accept blank connection fields when a configuration is already\n * saved; blanks keep their saved values (\"已保存时可留空\"). The token itself\n * is never readable here, so a blank token is merged server-side while the\n * remaining fields still pass the regular template check.\n */\n const vpsEffectiveForm = (): ReturnType<typeof frpForm> => {\n const form = frpForm()\n if (!frpConfigured) return form\n return {\n serverAddress: form.serverAddress === '' ? configuredFrpServer : form.serverAddress,\n serverPort: Number.isSafeInteger(form.serverPort) && form.serverPort >= 1 ? form.serverPort : configuredFrpPort,\n token: form.token,\n publicOrigin: form.publicOrigin === '' ? configuredFrpOrigin : form.publicOrigin,\n }\n }\n const validVpsFrpForm = (): boolean => {\n const form = vpsEffectiveForm()\n const token = form.token === '' && frpConfigured ? '0123456789abcdef' : form.token\n return validFrpForm({ ...form, token })\n }\n const refreshVpsDeployButton = (): void => {\n const disabled = remoteProviderBusy || !validVpsDeploymentForm()\n vpsDeploy.disabled = disabled\n vpsCopyUninstall.disabled = disabled\n vpsUninstall.disabled = disabled\n }\n for (const input of [frpServer, frpPort, frpToken, frpOrigin, vpsSshUser, vpsSshPort, vpsSshKey]) {\n input.addEventListener('input', refreshVpsDeployButton)\n }\n for (const input of [vpsSshUser, vpsSshPort, vpsSshKey]) input.addEventListener('input', saveVpsForm)\n const vpsCertName = (origin: string): string | undefined => {\n try {\n const host = new URL(origin).hostname\n return /^\\d{1,3}(?:\\.\\d{1,3}){3}$/u.test(host) ? host : undefined\n } catch { return undefined }\n }\n const requestVpsHostKeys = (serverAddress: string, sshUser: string, sshPort: number, sshKeyPath: string): Promise<ReadonlyArray<{ readonly display: string; readonly fingerprint: string }>> => {\n const keyPayload: Record<string, unknown> = { serverAddress, sshUser, sshPort }\n if (sshKeyPath !== '') keyPayload.sshKeyPath = sshKeyPath\n return controlRequestJson('/api/mobile-access/remote/frp/vps/host-keys', { method: 'POST', body: JSON.stringify(keyPayload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(keys => {\n const hostKeys = Array.isArray(keys.vpsHostKeys) ? keys.vpsHostKeys as Array<Record<string, unknown>> : []\n const confirmed = hostKeys\n .filter(key => typeof key.fingerprint === 'string' && typeof key.keyType === 'string')\n .map(key => ({ display: `${String(key.keyType)} ${String(key.fingerprint)}`, fingerprint: String(key.fingerprint) }))\n if (confirmed.length === 0) throw new Error(String(t('vpsHostKeyFailed', { error: 'empty' })))\n return confirmed\n })\n }\n vpsDeploy.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const form = vpsEffectiveForm()\n if (!validVpsFrpForm()) {\n vpsDeployStatus.textContent = t('vpsDeployNotReady')\n return\n }\n const sshUser = vpsSshUser.value.trim()\n const sshPort = Number(vpsSshPort.value)\n const sshKeyPath = vpsSshKey.value.trim()\n saveVpsForm()\n if (!validVpsSshUser(sshUser) || !Number.isSafeInteger(sshPort) || sshPort < 1 || sshPort > 65535 || !validVpsSshKey(sshKeyPath)) {\n vpsDeployStatus.textContent = t('vpsDeployFailed', { error: t('vpsSshKey') })\n return\n }\n remoteProviderBusy = true\n vpsDeploy.disabled = true\n vpsCopyUninstall.disabled = true\n vpsUninstall.disabled = true\n vpsDeployStatus.textContent = t('vpsHostKeyFetching')\n remoteStatus.textContent = t('vpsHostKeyFetching')\n void requestVpsHostKeys(form.serverAddress, sshUser, sshPort, sshKeyPath)\n .then(hostKeys => {\n const display = hostKeys.map(key => key.display).join('\\n')\n if (!window.confirm(t('vpsDeployConfirmWithKeys', { fingerprints: display }))) {\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n return\n }\n vpsDeployStatus.textContent = t('vpsDeploying')\n remoteStatus.textContent = t('vpsDeploying')\n const payload: Record<string, unknown> = {\n confirm: true,\n ...form,\n sshUser,\n sshPort,\n hostFingerprints: hostKeys.map(key => key.fingerprint),\n }\n if (sshKeyPath !== '') payload.sshKeyPath = sshKeyPath\n return controlRequestJson('/api/mobile-access/remote/frp/vps/deploy', { method: 'POST', body: JSON.stringify(payload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(data => {\n const deployment = data.vpsDeployment !== null && typeof data.vpsDeployment === 'object' ? data.vpsDeployment as Record<string, unknown> : {}\n vpsDeployStatus.textContent = deployment.deployed === true ? t('vpsDeploySuccess') : t('vpsDeployFailed', { error: t('vpsDeployFailed', { error: 'unknown result' }) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n renderRemote(data)\n }, error => {\n vpsDeployStatus.textContent = t('vpsDeployFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n }, error => {\n vpsDeployStatus.textContent = t('vpsHostKeyFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n })\n })\n const readVpsSshForm = (): { sshUser: string; sshPort: number; sshKeyPath: string } | undefined => {\n const sshUser = vpsSshUser.value.trim()\n const sshPort = Number(vpsSshPort.value)\n const sshKeyPath = vpsSshKey.value.trim()\n saveVpsForm()\n if (!validVpsSshUser(sshUser) || !Number.isSafeInteger(sshPort) || sshPort < 1 || sshPort > 65535 || !validVpsSshKey(sshKeyPath)) {\n vpsDeployStatus.textContent = t('vpsDeployFailed', { error: t('vpsSshKey') })\n return undefined\n }\n return { sshUser, sshPort, sshKeyPath }\n }\n vpsCopyUninstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const form = vpsEffectiveForm()\n if (!validVpsFrpForm()) {\n vpsDeployStatus.textContent = t('vpsDeployNotReady')\n return\n }\n remoteProviderBusy = true\n refreshVpsDeployButton()\n const scriptPayload: Record<string, unknown> = { serverPort: form.serverPort }\n const certName = vpsCertName(form.publicOrigin)\n if (certName !== undefined) scriptPayload.certName = certName\n void controlRequestJson('/api/mobile-access/remote/frp/vps/uninstall-script', { method: 'POST', body: JSON.stringify(scriptPayload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(data => {\n const script = typeof data.vpsUninstallScript === 'string' ? data.vpsUninstallScript : ''\n if (script === '') throw new Error('empty script')\n return navigator.clipboard.writeText(script)\n })\n .then(() => { vpsDeployStatus.textContent = t('vpsUninstallScriptCopied') }, error => {\n vpsDeployStatus.textContent = t('vpsUninstallScriptFailed', { error: String(error) })\n })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n vpsUninstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const form = vpsEffectiveForm()\n if (!validVpsFrpForm()) {\n vpsDeployStatus.textContent = t('vpsDeployNotReady')\n return\n }\n const ssh = readVpsSshForm()\n if (ssh === undefined) return\n remoteProviderBusy = true\n refreshVpsDeployButton()\n vpsDeployStatus.textContent = t('vpsHostKeyFetching')\n remoteStatus.textContent = t('vpsHostKeyFetching')\n void requestVpsHostKeys(form.serverAddress, ssh.sshUser, ssh.sshPort, ssh.sshKeyPath)\n .then(hostKeys => {\n if (!window.confirm(t('vpsUninstallConfirmWithKeys', { fingerprints: hostKeys.map(key => key.display).join('\\n') }))) {\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n return\n }\n vpsDeployStatus.textContent = t('vpsUninstalling')\n remoteStatus.textContent = t('vpsUninstalling')\n const payload: Record<string, unknown> = {\n confirm: true,\n serverAddress: form.serverAddress,\n serverPort: form.serverPort,\n sshUser: ssh.sshUser,\n sshPort: ssh.sshPort,\n hostFingerprints: hostKeys.map(key => key.fingerprint),\n }\n if (ssh.sshKeyPath !== '') payload.sshKeyPath = ssh.sshKeyPath\n const certName = vpsCertName(form.publicOrigin)\n if (certName !== undefined) payload.certName = certName\n return controlRequestJson('/api/mobile-access/remote/frp/vps/uninstall', { method: 'POST', body: JSON.stringify(payload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(data => {\n const removal = data.vpsUninstall !== null && typeof data.vpsUninstall === 'object' ? data.vpsUninstall as Record<string, unknown> : {}\n vpsDeployStatus.textContent = removal.removed === true ? t('vpsUninstallSuccess') : t('vpsUninstallFailed', { error: t('vpsUninstallFailed', { error: 'unknown result' }) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n renderRemote(data)\n }, error => {\n vpsDeployStatus.textContent = t('vpsUninstallFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n }, error => {\n vpsDeployStatus.textContent = t('vpsHostKeyFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n })\n })\n frpInstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const accepted = window.confirm(t('frpInstallConfirm', { size: frpDownloadSize }))\n if (!accepted) return\n remoteProviderBusy = true\n frpInstall.disabled = true\n frpInstall.textContent = t('downloading')\n remoteStatus.textContent = t('installingFrp')\n void controlRequestJson('/api/mobile-access/remote/frp/component/install', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'installFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n frpConfigure.addEventListener('click', () => {\n if (remoteProviderBusy || !frpInstalled) return\n const form = frpForm()\n const unchanged = frpConfigured && form.token === '' && form.serverAddress === configuredFrpServer\n && form.serverPort === configuredFrpPort && form.publicOrigin === configuredFrpOrigin\n if (!unchanged && !validFrpForm(form)) {\n remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n remoteProviderBusy = true\n frpConfigure.disabled = true\n frpConfigure.setAttribute('aria-busy', 'true')\n frpConfigure.textContent = t('saving')\n const configure = unchanged\n ? Promise.resolve<Record<string, unknown>>({})\n : controlRequestJson('/api/mobile-access/remote/frp/configure', { method: 'POST', body: JSON.stringify(form) })\n void configure.then(() => {\n frpToken.value = ''\n remoteStatus.textContent = t('frpSavingConnecting')\n return controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: true }) })\n }).then(renderRemote, error => { remoteFailureText = t('configureFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => {\n remoteProviderBusy = false\n frpConfigure.setAttribute('aria-busy', 'false')\n frpConfigure.textContent = t('frpSaveConnect')\n loadRemote()\n })\n })\n frpPurge.addEventListener('click', () => {\n if (remoteProviderBusy || !window.confirm(t('purgeFrpConfirm'))) return\n remoteProviderBusy = true\n frpPurge.disabled = true\n remoteStatus.textContent = t('purgingFrp')\n void controlRequestJson('/api/mobile-access/remote/frp/component/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = t('purgeFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; frpPurge.disabled = false; loadRemote() })\n })\n const clearOriginValidation = (): void => {\n for (const input of [originPublic, originHost, originPort, originCidrs]) input.removeAttribute('aria-invalid')\n }\n const showOriginFeedback = (message: string, error = false): void => {\n originFeedback.textContent = message\n originFeedback.hidden = false\n originFeedback.classList.toggle('is-error', error)\n }\n const showOriginError = (error: unknown): void => {\n const code = error instanceof Error ? error.message : String(error)\n const key = ORIGIN_ERROR_MESSAGE_KEYS[code]\n const fields: Record<string, HTMLInputElement> = {\n origin_public_origin_invalid: originPublic, origin_listen_host_invalid: originHost,\n origin_listen_address_unavailable: originHost, origin_listen_port_invalid: originPort,\n origin_listen_port_reserved: originPort, origin_listen_port_in_use: originPort,\n origin_allowed_cidrs_invalid: originCidrs,\n }\n clearOriginValidation()\n const input = fields[code]\n input?.setAttribute('aria-invalid', 'true')\n showOriginFeedback(key === undefined ? t('configureFailed', { error: String(error) }) : t(key), true)\n }\n const setOriginFormBusy = (busy: boolean): void => {\n originFormBusy = busy\n remoteProviderBusy = busy\n remoteSnapshotEpoch += 1\n renderRemote(latestRemoteState)\n }\n for (const input of [originPublic, originHost, originPort, originCidrs]) {\n input.addEventListener('input', () => {\n originFormDirty = true\n clearOriginValidation()\n originFeedback.hidden = true\n })\n }\n originConfigure.addEventListener('click', () => {\n if (remoteProviderBusy || remoteProvider !== 'origin') return\n clearOriginValidation()\n for (const input of [originPublic, originHost, originPort, originCidrs]) {\n if (!input.reportValidity()) return\n }\n const form = {\n publicOrigin: originPublic.value.trim(), listenHost: originHost.value.trim(),\n listenPort: Number(originPort.value), allowedCidrs: originCidrs.value.trim().split(/[\\s,]+/u).filter(Boolean),\n }\n setOriginFormBusy(true)\n showOriginFeedback(t('originSaving'))\n void controlRequestJson('/api/mobile-access/remote/origin/configure', { method: 'POST', body: JSON.stringify(form) })\n .then(() => controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: true }) }))\n .then(data => {\n originFormDirty = false\n renderRemote(data)\n if (data.state === 'error') showOriginError(new Error(typeof data.errorCode === 'string' ? data.errorCode : 'origin_gateway_start_failed'))\n else originFeedback.hidden = true\n }, showOriginError)\n .finally(() => {\n setOriginFormBusy(false)\n const invalidInput = [originPublic, originHost, originPort, originCidrs].find(input => input.getAttribute('aria-invalid') === 'true')\n invalidInput?.focus()\n loadRemote()\n })\n })\n originPurge.addEventListener('click', () => {\n if (remoteProviderBusy || !window.confirm(t('originPurgeConfirm'))) return\n setOriginFormBusy(true)\n showOriginFeedback(t('originPurging'))\n void controlRequestJson('/api/mobile-access/remote/origin/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(data => {\n originFormDirty = false\n clearOriginValidation()\n renderRemote(data)\n showOriginFeedback(t('originPurged'))\n }, showOriginError)\n .finally(() => { setOriginFormBusy(false); loadRemote() })\n })\n originCopyBackend.addEventListener('click', () => {\n const address = originBackendAddress.textContent ?? ''\n if (address === '' || remoteProviderBusy) return\n void Promise.resolve().then(() => navigator.clipboard.writeText(address))\n .then(() => { showOriginFeedback(t('originBackendCopied')) }, error => { showOriginFeedback(t('requestFailed', { error: String(error) }), true) })\n })\n remoteToggle.addEventListener('click', () => {\n remoteToggle.disabled = true\n void controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: !remoteRunning }) })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(loadRemote)\n })\n remoteLogin.addEventListener('click', () => {\n if (remoteLoginUrl !== '') window.open(remoteLoginUrl, '_blank', 'noopener,noreferrer')\n })\n const reconnectRemote = (): void => {\n if (remoteReconnectBusy) return\n remoteReconnectBusy = true\n remoteReconnect.disabled = true\n remoteSetup.disabled = true\n remoteSetupRetry.disabled = true\n remoteStatus.textContent = remoteProvider === 'cpolar' ? t('reconnectingCpolar')\n : remoteProvider === 'cloudflared' ? t('reconnectingCloudflared')\n : remoteProvider === 'frp' ? t('reconnectingFrp') : remoteProvider === 'origin' ? t('reconnectingOrigin') : t('reconnectingTailscale')\n void controlRequestJson('/api/mobile-access/remote/reconnect', { method: 'POST', body: '{}' })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => {\n remoteReconnectBusy = false\n remoteReconnect.disabled = false\n remoteSetup.disabled = remoteSetupUrl === ''\n remoteSetupRetry.disabled = false\n })\n }\n remoteReconnect.addEventListener('click', reconnectRemote)\n remoteSetupRetry.addEventListener('click', () => { remoteSetupPending = false; reconnectRemote() })\n remoteSetup.addEventListener('click', () => {\n if (remoteSetupUrl === '') return\n remoteSetupPending = true\n remoteSetupOpenedAt = Date.now()\n remoteStatus.textContent = t('setupOpened')\n window.open(remoteSetupUrl, '_blank', 'noopener,noreferrer')\n })\n const retryAfterSetup = (): void => {\n if (!remoteSetupPending || document.visibilityState === 'hidden' || Date.now() - remoteSetupOpenedAt < 800) return\n remoteSetupPending = false\n reconnectRemote()\n }\n window.addEventListener('focus', retryAfterSetup)\n document.addEventListener('visibilitychange', retryAfterSetup)\n const copyRemotePairUrl = async (): Promise<void> => {\n try {\n await navigator.clipboard.writeText(activeRemotePairUrl)\n remoteStatus.textContent = t('linkCopied')\n } catch {\n remoteStatus.textContent = t('remoteQrReady')\n }\n }\n const openRemotePairing = (): Promise<void> => controlRequestJson('/api/mobile-access/remote/pairing/open', { method: 'POST', body: '{}' }).then(async data => {\n const pairUrl = typeof data.pairUrl === 'string' ? data.pairUrl : ''\n const expiresAt = typeof data.expiresAt === 'number' && Number.isFinite(data.expiresAt) ? data.expiresAt : 0\n showQr(typeof data.qrSvg === 'string' ? data.qrSvg : '', remoteQr)\n if (pairUrl === '') { remoteStatus.textContent = t('keyGenerationFailed'); return }\n // QR and copy actions share the same one-time pairing window. Re-copying\n // cannot invalidate a QR that the phone may already be scanning.\n activeRemotePairUrl = pairUrl\n activeRemotePairExpiresAt = expiresAt\n remotePairLink.textContent = pairUrl\n remotePairLink.classList.add('is-key')\n remotePairLink.hidden = false\n await copyRemotePairUrl()\n })\n remotePair.addEventListener('click', () => {\n remotePair.disabled = true\n void openRemotePairing()\n .catch(error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remotePair.disabled = !remoteReady })\n })\n remoteCopyLink.addEventListener('click', () => {\n remoteCopyLink.disabled = true\n const operation = activeRemotePairUrl !== '' && activeRemotePairExpiresAt > Date.now()\n ? copyRemotePairUrl()\n : openRemotePairing()\n void operation\n .catch(error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteCopyLink.disabled = !remoteReady })\n })\n const renderRemoteDevices = (data: Record<string, unknown>): void => {\n const devices = Array.isArray(data.devices) ? data.devices as Record<string, unknown>[] : []\n remoteDevicePanel.replaceChildren()\n if (devices.length === 0) {\n const empty = element('p', 'dsh-mobile-control__device-empty'); empty.textContent = t('noRemoteDevices'); remoteDevicePanel.append(empty); return\n }\n for (const device of devices) {\n const row = element('div', 'dsh-mobile-control__device')\n const label = element('span', 'dsh-mobile-control__device-label'); label.textContent = typeof device.label === 'string' ? device.label : t('device')\n const meta = element('span', 'dsh-mobile-control__device-meta'); meta.textContent = t('expires', { time: formatTime(device.expiresAt) })\n const revoke = element('button', 'dsh-mobile-control__device-revoke'); revoke.type = 'button'; revoke.textContent = t('revoke')\n const id = typeof device.id === 'string' ? device.id : ''\n revoke.addEventListener('click', () => {\n void controlRequestJson('/api/mobile-access/remote/devices/revoke', { method: 'POST', body: JSON.stringify({ deviceId: id }) })\n .then(loadRemoteDevices, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n })\n row.append(label, meta, revoke); remoteDevicePanel.append(row)\n }\n }\n const loadRemoteDevices = (): void => {\n void controlRequestJson('/api/mobile-access/remote/devices').then(renderRemoteDevices, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n }\n remoteDevices.addEventListener('click', () => {\n const show = remoteDevicePanel.hidden\n remoteDevicePanel.hidden = !show\n if (show) loadRemoteDevices()\n })\n remoteReset.addEventListener('click', () => {\n const prompt = remoteProvider === 'cpolar'\n ? t('resetCpolarConfirm')\n : remoteProvider === 'cloudflared' ? t('resetCloudflaredConfirm')\n : remoteProvider === 'frp' ? t('resetFrpConfirm') : remoteProvider === 'origin' ? t('resetOriginConfirm') : t('resetTailscaleConfirm')\n if (!window.confirm(prompt)) return\n void controlRequestJson('/api/mobile-access/remote/reset', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n })\n const renderDiagnostics = (data: Record<string, unknown>): void => {\n const entries = [...diagnosticEntriesForRender(data)]\n const overall = diagnosticOverallForChecks(data.overall, entries.map(entry => entry.status))\n diagnosticsSummary.className = `dsh-mobile-control__diagnostic-summary is-${overall}`\n diagnosticsSummaryTitle.textContent = overall === 'ok' ? t('diagnosticsComplete') : overall === 'attention' ? t('diagnosticsAttention') : t('diagnosticsProblem')\n diagnosticsSummaryText.textContent = locale === 'zh' && data.overall === overall && typeof data.summary === 'string' ? data.summary : t('diagnosticsCompleteFallback')\n diagnosticsChecks.replaceChildren()\n const statusLabels: Record<string, string> = { ok: t('diagnosticStatusOk'), warning: t('diagnosticStatusWarning'), error: t('diagnosticStatusError'), info: t('diagnosticStatusInfo') }\n const diagnosticLabelKeys: Record<string, string> = {\n versions: 'diagnosticLabelVersions', network: 'diagnosticLabelNetwork', lan: 'diagnosticLabelLan',\n firewall: 'diagnosticLabelFirewall', remote: 'diagnosticLabelRemote', 'phone-network': 'diagnosticLabelPhone',\n }\n const statusOf = (entry: Record<string, unknown>): 'ok' | 'warning' | 'error' | 'info' => normalizeDiagnosticStatus(entry.status)\n const localizedEntryCopy = (entry: Record<string, unknown>): { readonly detail: string; readonly action: string } => {\n const serverCopy = diagnosticServerCopy(entry)\n const reason = typeof entry.reason === 'string' ? entry.reason : ''\n const catalog = DIAGNOSTIC_REASON_MESSAGES[locale] as Readonly<Record<string, readonly [string, string]>>\n const templates = catalog[reason]\n if (templates === undefined) return serverCopy\n const facts = entry.facts !== null && typeof entry.facts === 'object' ? entry.facts as Record<string, unknown> : {}\n // Diagnostics read as sentences, so an internal provider id or a raw controller\n // code belongs in the report, not in the prose: it renders as\n // \"Remote access through origin is currently off.\" or \"reported gateway_start_failed\".\n const providerNames: Record<string, string> = {\n tailscale: 'Tailscale Funnel', cpolar: 'cpolar', cloudflared: 'cloudflared',\n frp: t('frpName'), origin: t('originName'),\n }\n const providerId = typeof facts.provider === 'string' ? facts.provider : ''\n const controllerKey = typeof facts.controllerCode === 'string' ? REMOTE_ERROR_MESSAGE_KEYS[facts.controllerCode] : undefined\n const values: Record<string, string> = {\n provider: providerNames[providerId] ?? '',\n latencyMs: typeof facts.latencyMs === 'number' && Number.isFinite(facts.latencyMs) ? new Intl.NumberFormat(localeTag).format(facts.latencyMs) : '',\n interfaceName: typeof facts.interfaceName === 'string' ? facts.interfaceName : '',\n endpointSuffix: typeof facts.endpointSuffix === 'string' ? facts.endpointSuffix : '',\n // A translated sentence when the code is known, otherwise nothing rather than\n // an identifier no user can act on.\n controllerCode: controllerKey === undefined ? '' : t(controllerKey),\n }\n const interpolate = (template: string): string => template.replace(/\\{(\\w+)\\}/gu, (_match, key: string) => values[key] ?? '')\n let detail = interpolate(templates[0])\n let action = interpolate(templates[1])\n if (reason === 'versions-current') {\n const versions = data.versions !== null && typeof data.versions === 'object' ? data.versions as Record<string, unknown> : {}\n if ([versions.plugin, versions.dsh, versions.minimumAndroidApp].every(value => typeof value === 'string')) {\n detail += ` plugin ${String(versions.plugin)}, DSH ${String(versions.dsh)}, Android ${String(versions.minimumAndroidApp)}.`\n }\n }\n if (reason === 'remote-controller-error') {\n const controllerActionKeys: Readonly<Record<string, string>> = {\n ...ORIGIN_ERROR_MESSAGE_KEYS,\n component_missing: 'remoteUnavailableTailscale', funnel_permission_required: 'funnelPermission', funnel_https_required: 'funnelHttps', funnel_start_failed: 'funnelStart', funnel_start_timeout: 'funnelTimeout', tailscale_dns_missing: 'tailscaleDnsMissing',\n sidecar_launch_failed: 'remoteUnavailableTailscale', sidecar_stopped: 'controlChannelFailed', sidecar_exited: 'controlChannelFailed', control_channel_failed: 'controlChannelFailed',\n cpolar_component_missing: 'cpolarMissing', cpolar_component_invalid: 'cpolarInvalid', cpolar_config_missing: 'cpolarConfigMissing', cpolar_config_invalid: 'cpolarConfigInvalid', cpolar_start_timeout: 'cpolarTimeout', cpolar_stopped: 'cpolarStopped', cpolar_exited: 'cpolarExited',\n cloudflared_component_missing: 'cloudflaredMissing', cloudflared_component_invalid: 'cloudflaredInvalid', cloudflared_component_unsupported: 'cloudflaredComponentUnsupported', cloudflared_port_unavailable: 'cloudflaredPortUnavailable', cloudflared_port_reservation_failed: 'cloudflaredPortReservationFailed', cloudflared_launch_failed: 'cloudflaredLaunchFailed', cloudflared_start_timeout: 'cloudflaredTimeout', cloudflared_stopped: 'cloudflaredStopped', cloudflared_exited: 'cloudflaredExited', cloudflared_invalid_origin: 'cloudflaredOriginInvalid', cloudflared_tunnel_port_unavailable: 'cloudflaredTunnelPortUnavailable', cloudflared_tunnel_config_invalid: 'cloudflaredTunnelInvalid', cloudflared_tunnel_target_invalid: 'cloudflaredTunnelTargetInvalid', cloudflared_download_hash_mismatch: 'cloudflaredDownloadHashMismatch', cloudflared_download_size_mismatch: 'cloudflaredDownloadSizeMismatch', cloudflared_executable_hash_mismatch: 'cloudflaredExecutableHashMismatch',\n frp_component_missing: 'frpMissing', frp_component_invalid: 'frpInvalid', frp_config_missing: 'frpConfigMissing', frp_config_verify_failed: 'frpConfigVerifyFailed', frp_vhost_publicly_reachable: 'frpVhostPublic', frp_vhost_probe_failed: 'frpVhostProbeFailed', frp_launch_failed: 'frpLaunchFailed', frp_start_timeout: 'frpTimeout', frp_discovery_mismatch: 'frpDiscoveryMismatch', frp_discovery_invalid: 'frpDiscoveryInvalid', frp_stopped: 'frpStopped', frp_exited: 'frpExited', gateway_start_failed: 'gatewayStartFailed',\n }\n const actionKey = controllerActionKeys[values.controllerCode ?? '']\n if (actionKey !== undefined) action = t(actionKey)\n }\n return { detail, action }\n }\n const appendGroup = (label: string, groupEntries: Record<string, unknown>[]): void => {\n if (groupEntries.length === 0) return\n const group = element('section', 'dsh-mobile-control__diagnostic-group')\n const groupHeader = element('header', 'dsh-mobile-control__diagnostic-group-header')\n const groupTitle = element('h3'); groupTitle.textContent = label\n const groupCount = element('span'); groupCount.textContent = t('diagnosticItems', { count: groupEntries.length })\n const list = element('div', 'dsh-mobile-control__diagnostic-list'); list.setAttribute('role', 'list')\n groupHeader.append(groupTitle, groupCount)\n for (const entry of groupEntries) {\n const state = statusOf(entry)\n const row = element('section', `dsh-mobile-control__diagnostic-check is-${state}`); row.setAttribute('role', 'listitem')\n const marker = element('span', 'dsh-mobile-control__diagnostic-marker'); marker.setAttribute('aria-hidden', 'true')\n const rowBody = element('div', 'dsh-mobile-control__diagnostic-check-body')\n const rowHeader = element('div', 'dsh-mobile-control__diagnostic-check-header')\n const labelKey = typeof entry.id === 'string' ? diagnosticLabelKeys[entry.id] : undefined\n const rowTitle = element('strong'); rowTitle.textContent = locale !== 'zh' && labelKey !== undefined ? t(labelKey) : typeof entry.label === 'string' ? entry.label : t('diagnosticCheck')\n const badge = element('span', 'dsh-mobile-control__diagnostic-badge'); badge.textContent = statusLabels[state]!\n const localizedCopy = localizedEntryCopy(entry)\n const detail = element('p'); detail.textContent = localizedCopy.detail\n rowHeader.append(rowTitle, badge); rowBody.append(rowHeader, detail)\n if (localizedCopy.action !== '') {\n const action = element('p', 'dsh-mobile-control__diagnostic-action')\n const actionLabel = element('span'); actionLabel.textContent = t('diagnosticAction')\n action.append(actionLabel, document.createTextNode(localizedCopy.action)); rowBody.append(action)\n }\n row.append(marker, rowBody); list.append(row)\n }\n group.append(groupHeader, list); diagnosticsChecks.append(group)\n }\n const issues = entries.filter(entry => statusOf(entry) === 'error' || statusOf(entry) === 'warning')\n const otherChecks = entries.filter(entry => statusOf(entry) !== 'error' && statusOf(entry) !== 'warning')\n appendGroup(t('diagnosticNeedsAction'), issues)\n appendGroup(issues.length === 0 ? t('diagnosticDetails') : t('diagnosticOther'), otherChecks)\n diagnosticsSummaryMeta.textContent = issues.length === 0\n ? t('diagnosticNoBlockers', { count: entries.length })\n : t('diagnosticNeedsCount', { count: entries.length, issues: issues.length })\n diagnosticsChecks.hidden = entries.length === 0\n const reportCopy = LOCALIZED_DIAGNOSTIC_COPY[locale]\n const generatedAt = typeof data.generatedAt === 'number' ? new Date(data.generatedAt).toLocaleString(localeTag) : new Date().toLocaleString(localeTag)\n const lines = entries.map(entry => {\n const state = statusOf(entry)\n const labelKey = typeof entry.id === 'string' ? diagnosticLabelKeys[entry.id] : undefined\n const label = labelKey === undefined && typeof entry.label === 'string' ? entry.label : labelKey === undefined ? t('diagnosticCheck') : t(labelKey)\n const localizedCopy = localizedEntryCopy(entry)\n return `[${statusLabels[state]!}] ${label}: ${localizedCopy.detail}${localizedCopy.action === '' ? '' : ` ${localizedCopy.action}`}`\n })\n copiedDiagnosticReport = [reportCopy.reportTitle, `${reportCopy.generated}: ${generatedAt}`, ...lines].join('\\n')\n diagnosticsReport.textContent = copiedDiagnosticReport\n diagnosticsDetails.hidden = copiedDiagnosticReport === ''\n diagnosticsCopy.disabled = copiedDiagnosticReport === ''\n diagnosticsCopy.hidden = copiedDiagnosticReport === ''\n diagnosticsToolbar.classList.toggle('has-report', copiedDiagnosticReport !== '')\n }\n const showDiagnosticsFailure = (error: unknown): void => {\n diagnosticsSummary.className = 'dsh-mobile-control__diagnostic-summary is-error'\n diagnosticsSummaryTitle.textContent = t('diagnosticsIncomplete')\n diagnosticsSummaryText.textContent = t('diagnosticsReadFailed', { error: String(error) })\n diagnosticsSummaryMeta.textContent = t('diagnosticsUnavailable')\n diagnosticsChecks.replaceChildren()\n diagnosticsChecks.hidden = true\n copiedDiagnosticReport = ''\n diagnosticsReport.textContent = ''\n diagnosticsDetails.hidden = true\n diagnosticsCopy.disabled = true\n diagnosticsCopy.hidden = true\n diagnosticsToolbar.classList.remove('has-report')\n }\n const loadDiagnostics = (): void => {\n if (diagnosticsBusy) return\n diagnosticsBusy = true\n diagnosticsRun.disabled = true\n diagnosticsRun.setAttribute('aria-busy', 'true')\n diagnosticsRun.textContent = t('diagnosticsChecking')\n diagnosticsSummary.className = 'dsh-mobile-control__diagnostic-summary is-running'\n diagnosticsSummaryTitle.textContent = t('diagnosticsCheckingTitle')\n diagnosticsSummaryText.textContent = t('diagnosticsCheckingText')\n diagnosticsSummaryMeta.textContent = t('diagnosticsRunningMeta')\n diagnosticsFeedback.hidden = true\n diagnosticsChecks.classList.add('is-refreshing')\n diagnosticsChecks.setAttribute('aria-busy', 'true')\n void controlRequestJson('/api/mobile-access/diagnostics').then(\n data => { renderDiagnosticPayloadSafely(data, renderDiagnostics, showDiagnosticsFailure) },\n showDiagnosticsFailure,\n ).finally(() => {\n diagnosticsBusy = false\n diagnosticsRun.disabled = false\n diagnosticsRun.setAttribute('aria-busy', 'false')\n diagnosticsRun.textContent = t('diagnosticsRetry')\n diagnosticsChecks.classList.remove('is-refreshing')\n diagnosticsChecks.setAttribute('aria-busy', 'false')\n })\n }\n diagnosticsEntry.addEventListener('click', () => {\n if (!diagnosticsView.hidden) { selectView(previousAccessView); return }\n selectView('diagnostics'); loadDiagnostics()\n })\n diagnosticsRun.addEventListener('click', loadDiagnostics)\n const closeUpdateCard = (): void => {\n updateCard.hidden = true\n if (pluginUpdateAvailable && pluginLatestVersion !== '') updatePlugin.hidden = !diagnosticsView.hidden\n }\n const runPluginUpdate = (): void => {\n if (!pluginUpdateAvailable || pluginLatestVersion === '' || updateInFlight) return\n updateInFlight = true\n updateNow.disabled = true\n updateLater.disabled = true\n updateCardTitle.textContent = t('updatingPlugin')\n releaseNotice.hidden = true\n releaseNotice.classList.remove('is-error')\n void controlRequestJson('/api/mobile-access/release/update', { method: 'POST', body: '{}' }, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(data => {\n const installedVersion = releaseVersion(data.installedVersion) ?? pluginLatestVersion\n pluginUpdateAvailable = false\n updateCard.hidden = true\n updatePlugin.hidden = true\n releaseNotice.textContent = t('pluginUpdatedRestart', { version: installedVersion })\n releaseNotice.hidden = false\n }, error => {\n updateCard.hidden = true\n updatePlugin.hidden = false\n releaseNotice.textContent = t('pluginUpdateFailed', { error: String(error) })\n releaseNotice.classList.add('is-error')\n releaseNotice.hidden = false\n }).finally(() => {\n updateInFlight = false\n updateNow.disabled = false\n updateLater.disabled = false\n })\n }\n updatePlugin.addEventListener('click', () => {\n if (!pluginUpdateAvailable || pluginLatestVersion === '' || updateInFlight) return\n updatePlugin.hidden = true\n updateCardTitle.textContent = t('updateTo', { version: pluginLatestVersion })\n updateCardNotes.textContent = pluginReleaseNotes === '' ? t('updateNotesEmpty') : pluginReleaseNotes\n updateCardNotes.hidden = pluginReleaseNotes === ''\n updateCard.hidden = false\n })\n updateNow.addEventListener('click', runPluginUpdate)\n updateLater.addEventListener('click', closeUpdateCard)\n diagnosticsCopy.addEventListener('click', () => {\n if (copiedDiagnosticReport === '') return\n void navigator.clipboard.writeText(copiedDiagnosticReport).then(() => {\n diagnosticsFeedback.textContent = t('diagnosticsCopied')\n diagnosticsFeedback.hidden = false\n }, () => {\n diagnosticsDetails.open = true\n diagnosticsFeedback.textContent = t('diagnosticsCopyManual')\n diagnosticsFeedback.hidden = false\n })\n })\n pair.addEventListener('click', () => { pair.disabled = true; openPairing('key') })\n linkPair.addEventListener('click', () => { linkPair.disabled = true; openPairing('link') })\n close.addEventListener('click', () => { setOpen(false) })\n const dismiss = (event: PointerEvent): void => {\n if (panel.hidden || !(event.target instanceof Node)) return\n if (!providerInfo.contains(event.target)) {\n providerInfoPinned = false\n providerInfoHovered = false\n syncProviderInfo()\n }\n if (!panel.contains(event.target) && !document.querySelector('.dsh-mobile-control__trigger')?.contains(event.target)) setOpen(false)\n }\n document.addEventListener('pointerdown', dismiss)\n void controlRequestJson('/api/mobile-access/release').then(renderRelease, () => {})\n void controlRequestJson('/api/mobile-access/lan/control').then(render, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n loadRemote()\n const remotePoll = window.setInterval(() => { if (!panel.hidden && !remoteView.hidden) loadRemote() }, 1_500)\n const pollWsBlocked = (): void => {\n void controlRequestJson('/api/mobile-access/remote/websocket-paths/blocked').then(blocked => {\n const raw = Array.isArray(blocked.blocked) ? blocked.blocked : []\n wsPathsBlocked = raw.filter((entry): entry is { path: string; attempts: number } =>\n typeof entry === 'object' && entry !== null\n && typeof (entry as { path?: unknown }).path === 'string'\n && typeof (entry as { attempts?: unknown }).attempts === 'number')\n renderWsDot()\n if (!diagnosticsView.hidden) renderWsBlocked(wsPathsBlocked)\n }, () => { /* gateway unreachable; dot keeps its last state */ })\n }\n const wsBlockedPoll = window.setInterval(pollWsBlocked, 20_000)\n return { remove: () => { lifecycle.abort(); window.clearInterval(remotePoll); window.clearInterval(wsBlockedPoll); window.removeEventListener('focus', retryAfterSetup); document.removeEventListener('visibilitychange', retryAfterSetup); document.removeEventListener('pointerdown', dismiss); root.remove() }, toggle: () => { setOpen(panel.hidden !== false) }, isOpen: () => !panel.hidden }\n}\n\nfunction mobileRequest(path: string, init: RequestInit = {}): Promise<Response> {\n const target = new URL(path, location.href)\n if (target.origin !== location.origin) throw new TypeError('mobile extension requests must stay on the DSH origin')\n const headers = new Headers(init.headers)\n const method = (init.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const csrf = document.cookie.split(';').map(value => value.trim()).find(value => value.startsWith('dsh_ma_csrf='))?.slice(12)\n if (csrf !== undefined) headers.set('x-dsh-mobile-csrf', csrf)\n }\n return fetch(target, { ...init, headers, credentials: 'same-origin', cache: 'no-store', redirect: 'error' })\n}\n\nexport interface CombinedClientSignal {\n readonly signal: AbortSignal\n readonly cleanup: () => void\n}\n\n/** Combine extension and caller abort lifetimes and expose deterministic listener cleanup. */\nexport function combineClientSignalLifetime(first: AbortSignal, second: AbortSignal): CombinedClientSignal {\n if (first.aborted || second.aborted) {\n const aborted = new AbortController()\n aborted.abort(first.aborted ? first.reason : second.reason)\n return { signal: aborted.signal, cleanup: () => undefined }\n }\n const controller = new AbortController()\n const cleanup = (): void => {\n first.removeEventListener('abort', abortFirst)\n second.removeEventListener('abort', abortSecond)\n }\n const abortFirst = (): void => { cleanup(); controller.abort(first.reason) }\n const abortSecond = (): void => { cleanup(); controller.abort(second.reason) }\n first.addEventListener('abort', abortFirst, { once: true })\n second.addEventListener('abort', abortSecond, { once: true })\n return { signal: controller.signal, cleanup }\n}\n\n/** Combine extension and caller abort lifetimes on WebViews without AbortSignal.any. */\nexport function combineClientSignals(first: AbortSignal, second: AbortSignal): AbortSignal {\n return combineClientSignalLifetime(first, second).signal\n}\n\n/** Keep a combined request lifetime until a streamed response is consumed or cancelled. */\nexport function bindClientResponseLifetime(response: Response, cleanup: () => void): Response {\n if (response.body === null) {\n cleanup()\n return response\n }\n const reader = response.body.getReader()\n let released = false\n const release = (): void => {\n if (released) return\n released = true\n cleanup()\n }\n const body = new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const result = await reader.read()\n if (result.done) {\n release()\n controller.close()\n } else controller.enqueue(result.value)\n } catch (error) {\n release()\n controller.error(error)\n }\n },\n async cancel(reason) {\n try { await reader.cancel(reason) } finally { release() }\n },\n })\n try {\n const retained = new Response(body, { headers: response.headers, status: response.status, statusText: response.statusText })\n Object.defineProperties(retained, {\n redirected: { configurable: true, value: response.redirected },\n type: { configurable: true, value: response.type },\n url: { configurable: true, value: response.url },\n })\n return retained\n } catch (error) {\n void reader.cancel(error)\n release()\n throw error\n }\n}\n\n/** Dispose an old UI when its replacement Host generation cannot be activated. */\nexport function failClosedExtensionGenerationReplacement(\n hasActive: boolean,\n activeGeneration: string | undefined,\n replacementGeneration: string | undefined,\n dispose: () => void,\n): boolean {\n if (!hasActive || activeGeneration === replacementGeneration) return false\n dispose()\n return true\n}\n\n/** Resolve one SDK route and prove its normalized path remains in the current extension namespace. */\nexport function extensionRouteUrl(id: string, path: string, baseUrl: string): URL {\n const rawPathname = path.split(/[?#]/u, 1)[0] ?? ''\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(id) || !path.startsWith('/') || path.startsWith('//')\n || rawPathname.includes('\\\\') || rawPathname.includes('\\0') || /%(?:2f|5c)/iu.test(rawPathname)) {\n throw new TypeError('extension routes must be relative')\n }\n const origin = new URL(baseUrl).origin\n const prefix = `/mobile-access/extensions/${id}/routes`\n let target: URL\n try { target = new URL(`${prefix}${path}`, origin) } catch { throw new TypeError('extension routes must be relative') }\n let decodedPathname: string\n try { decodedPathname = decodeURIComponent(target.pathname) } catch { throw new TypeError('extension routes must be relative') }\n if (target.origin !== origin || target.hash !== '' || decodedPathname.includes('\\\\')\n || decodedPathname !== prefix && !decodedPathname.startsWith(`${prefix}/`)) {\n throw new TypeError('extension routes must be relative')\n }\n const relative = decodedPathname.slice(prefix.length)\n if (relative.split('/').some(part => part === '.' || part === '..')) throw new TypeError('extension routes must be relative')\n return target\n}\n\n/** Resolve one generation-pinned static asset URL in the current extension namespace. */\nexport function extensionAssetUrl(id: string, generation: string | undefined, path: string, baseUrl: string): URL {\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(id) || generation !== undefined && !/^[a-f\\d]{64}$/u.test(generation)) {\n throw new TypeError('extension asset path is invalid')\n }\n const normalized = path.replaceAll('\\\\', '/')\n if (normalized.length === 0 || normalized.startsWith('/') || normalized.split('/').some(part => part === '' || part === '.' || part === '..')) {\n throw new TypeError('extension asset path is invalid')\n }\n const target = new URL(`/mobile-access/extensions/${id}/assets/${normalized.split('/').map(encodeURIComponent).join('/')}`, new URL(baseUrl).origin)\n if (generation !== undefined) target.searchParams.set('generation', generation)\n return target\n}\n\n/** Add the immutable Host generation selected for one activated mobile UI. */\nexport function extensionGenerationHeaders(generation: string | undefined, headers?: HeadersInit): Headers {\n const result = new Headers(headers)\n if (generation !== undefined) result.set('x-dsh-mobile-extension-generation', generation)\n return result\n}\n\n/** Build the JSON request used by a mobile extension to invoke a Host action. */\nexport function extensionActionRequestInit(generation: string | undefined, input: unknown, signal: AbortSignal): RequestInit {\n return {\n method: 'POST',\n headers: extensionGenerationHeaders(generation, { 'content-type': 'application/json' }),\n body: JSON.stringify(input ?? {}),\n signal,\n }\n}\n\nexport function registerUniqueDisposable<T extends { readonly dispose: () => void }>(\n entries: Map<string, T>,\n claimedIds: Set<string>,\n id: string,\n mount: () => T,\n): () => void {\n if (claimedIds.has(id)) throw new Error(`duplicate lifecycle id: ${id}`)\n claimedIds.add(id)\n let mounted: T\n try { mounted = mount() } catch (error) { claimedIds.delete(id); throw error }\n let disposed = false\n const dispose = (): void => {\n if (disposed) return\n disposed = true\n mounted.dispose()\n }\n const entry = { ...mounted, dispose } as T\n entries.set(id, entry)\n return () => {\n if (entries.get(id) === entry) { entries.delete(id); claimedIds.delete(id) }\n dispose()\n }\n}\n\n/** Dispose resources omitted by a successfully fetched authoritative manifest. */\nexport function reconcileRemovedExtensions(currentIds: Iterable<string>, seen: ReadonlySet<string>, dispose: (id: string) => void): void {\n for (const id of new Set(currentIds)) if (!seen.has(id)) dispose(id)\n}\n\n/** Reconcile managed resources against one validated authoritative id set. */\nexport function publishAuthoritativeExtensionIds(\n authoritativeIds: Set<string>,\n seen: ReadonlySet<string>,\n managedIdSources: readonly Iterable<string>[],\n dispose: (id: string) => void,\n): void {\n const current = new Set(authoritativeIds)\n for (const source of managedIdSources) for (const id of source) current.add(id)\n reconcileRemovedExtensions(current, seen, dispose)\n authoritativeIds.clear()\n for (const id of seen) authoritativeIds.add(id)\n}\n\ninterface MobileExtensionManifestEntry {\n readonly id: string\n readonly generation?: string\n readonly scriptUrl?: string\n readonly styleUrl?: string\n readonly assetsUrl?: string\n}\ninterface MobileExtensionManifest {\n readonly extensions: readonly MobileExtensionManifestEntry[]\n readonly legacy: { readonly scriptRevision: string; readonly styleRevision: string }\n}\n\nfunction validManifestResourceUrl(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith('/') && !value.startsWith('//') && !value.includes('://')\n && !value.split('/').some(part => part === '..' || part === '.')\n}\n\n/** Validate the manifest before treating it as authoritative state. */\nexport function parseMobileExtensionManifest(payload: unknown): MobileExtensionManifest | undefined {\n if (typeof payload !== 'object' || payload === null) return undefined\n const candidate = payload as { protocol?: unknown; extensions?: unknown; legacy?: unknown }\n if (candidate.protocol !== 1 || !Array.isArray(candidate.extensions) || typeof candidate.legacy !== 'object' || candidate.legacy === null) return undefined\n const legacy = candidate.legacy as { scriptRevision?: unknown; styleRevision?: unknown }\n if (typeof legacy.scriptRevision !== 'string' || typeof legacy.styleRevision !== 'string') return undefined\n const ids = new Set<string>()\n const extensions: MobileExtensionManifestEntry[] = []\n for (const value of candidate.extensions) {\n if (typeof value !== 'object' || value === null) return undefined\n const entry = value as { id?: unknown; generation?: unknown; scriptUrl?: unknown; styleUrl?: unknown; assetsUrl?: unknown }\n if (typeof entry.id !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/u.test(entry.id) || ids.has(entry.id)) return undefined\n if (entry.generation !== undefined && (typeof entry.generation !== 'string' || !/^[a-f\\d]{64}$/u.test(entry.generation))) return undefined\n if (entry.scriptUrl !== undefined && !validManifestResourceUrl(entry.scriptUrl)) return undefined\n if (entry.styleUrl !== undefined && !validManifestResourceUrl(entry.styleUrl)) return undefined\n if (entry.assetsUrl !== undefined && !validManifestResourceUrl(entry.assetsUrl)) return undefined\n ids.add(entry.id)\n extensions.push({\n id: entry.id,\n ...(entry.generation === undefined ? {} : { generation: entry.generation }),\n ...(entry.scriptUrl === undefined ? {} : { scriptUrl: entry.scriptUrl }),\n ...(entry.styleUrl === undefined ? {} : { styleUrl: entry.styleUrl }),\n ...(entry.assetsUrl === undefined ? {} : { assetsUrl: entry.assetsUrl }),\n })\n }\n return { extensions, legacy: { scriptRevision: legacy.scriptRevision, styleRevision: legacy.styleRevision } }\n}\n\n/** A missing manifest is authoritative before optional legacy resources finish refreshing. */\nexport async function handleMissingExtensionManifest(\n clearManifestResources: () => void,\n refreshLegacyResources: () => Promise<readonly boolean[]>,\n signal: AbortSignal,\n): Promise<boolean> {\n clearManifestResources()\n const results = await refreshLegacyResources()\n return !signal.aborted && results.every(Boolean)\n}\n\ninterface LifecycleSchedulerRuntime {\n readonly document: Pick<Document, 'visibilityState' | 'addEventListener' | 'removeEventListener'>\n readonly window: Pick<Window, 'addEventListener' | 'removeEventListener' | 'setTimeout' | 'clearTimeout'>\n}\n\nexport interface LifecycleRefreshController {\n (): void\n refresh(): void\n}\n\nfunction refreshAborted(signal: AbortSignal): boolean { return signal.aborted }\n\n/** Run one coalesced refresh cycle, slowing down when the page is hidden. */\nexport function startLifecycleRefreshScheduler(\n refresh: (signal: AbortSignal) => void | Promise<void>,\n options: { readonly visibleIntervalMs?: number; readonly hiddenIntervalMs?: number; readonly cycleTimeoutMs?: number } = {},\n runtime: LifecycleSchedulerRuntime = { document, window },\n): LifecycleRefreshController {\n const visibleIntervalMs = options.visibleIntervalMs ?? 45_000\n const hiddenIntervalMs = options.hiddenIntervalMs ?? 300_000\n const cycleTimeoutMs = options.cycleTimeoutMs ?? 30_000\n let timer: number | undefined\n let cycleTimer: number | undefined\n let running = false\n let queued = false\n let disposed = false\n let controller: AbortController | undefined\n const clearTimer = (): void => {\n if (timer === undefined) return\n runtime.window.clearTimeout(timer)\n timer = undefined\n }\n const clearCycleTimer = (): void => {\n if (cycleTimer === undefined) return\n runtime.window.clearTimeout(cycleTimer)\n cycleTimer = undefined\n }\n const schedule = (): void => {\n if (disposed) return\n clearTimer()\n const delay = runtime.document.visibilityState === 'hidden' ? hiddenIntervalMs : visibleIntervalMs\n timer = runtime.window.setTimeout(run, delay)\n }\n const run = (): void => {\n if (disposed) return\n clearTimer()\n if (running) { queued = true; return }\n running = true\n const current = new AbortController()\n controller = current\n const refreshPromise = Promise.resolve().then(() => refresh(current.signal))\n const timeoutPromise = new Promise<void>(resolve => {\n cycleTimer = runtime.window.setTimeout(() => {\n cycleTimer = undefined\n current.abort(new DOMException('mobile extension refresh timed out', 'TimeoutError'))\n resolve()\n }, cycleTimeoutMs)\n })\n void Promise.race([refreshPromise, timeoutPromise]).catch(() => { /* Keep the last good resources during reconnects. */ }).finally(() => {\n clearCycleTimer()\n if (controller === current) controller = undefined\n running = false\n if (disposed) return\n if (queued) { queued = false; run() } else schedule()\n })\n void refreshPromise.catch(() => { /* A timed-out refresh may reject after the scheduler has moved on. */ })\n }\n const onVisibilityChange = (): void => {\n if (runtime.document.visibilityState === 'hidden') schedule()\n else run()\n }\n runtime.document.addEventListener('visibilitychange', onVisibilityChange)\n runtime.window.addEventListener('focus', run)\n runtime.window.addEventListener('online', run)\n run()\n const stop = (): void => {\n disposed = true\n queued = false\n controller?.abort()\n controller = undefined\n clearCycleTimer()\n clearTimer()\n runtime.document.removeEventListener('visibilitychange', onVisibilityChange)\n runtime.window.removeEventListener('focus', run)\n runtime.window.removeEventListener('online', run)\n }\n return Object.assign(stop, { refresh: run })\n}\n\ninterface ExtensionEventRuntime {\n readonly window: Pick<Window, 'addEventListener' | 'removeEventListener' | 'setTimeout' | 'clearTimeout'>\n readonly create: (url: string) => Pick<EventSource, 'close' | 'onopen' | 'onerror' | 'addEventListener'>\n}\n\n/** Maintain one authenticated same-origin extension event stream with bounded reconnect backoff. */\nexport function startExtensionChangeStream(\n changed: () => void,\n runtime: ExtensionEventRuntime = {\n window,\n create: url => new EventSource(url, { withCredentials: true }),\n },\n onTaskEvent?: (payload: unknown) => void,\n onDeviceRevoked?: () => void,\n): () => void {\n let source: ReturnType<ExtensionEventRuntime['create']> | undefined\n let timer: number | undefined\n let disposed = false\n let retryMs = 1_000\n const clearTimer = (): void => {\n if (timer === undefined) return\n runtime.window.clearTimeout(timer)\n timer = undefined\n }\n const connect = (): void => {\n if (disposed || source !== undefined) return\n clearTimer()\n const next = runtime.create('/mobile-access/extensions/events')\n source = next\n next.onopen = () => { retryMs = 1_000 }\n next.addEventListener('extensions-changed', changed)\n if (onTaskEvent !== undefined) {\n next.addEventListener('task-notify', (message: Event) => {\n const data = (message as MessageEvent).data\n onTaskEvent(data)\n })\n }\n if (onDeviceRevoked !== undefined) {\n next.addEventListener('device-revoked', (message: Event) => {\n const data = (message as MessageEvent).data\n if (isDeviceRevokedPayload(data)) onDeviceRevoked()\n })\n }\n next.onerror = () => {\n if (source !== next) return\n next.close()\n source = undefined\n if (disposed) return\n const delay = retryMs\n retryMs = Math.min(30_000, retryMs * 2)\n timer = runtime.window.setTimeout(connect, delay)\n }\n }\n const reconnectNow = (): void => {\n if (disposed) return\n source?.close()\n source = undefined\n retryMs = 1_000\n connect()\n }\n runtime.window.addEventListener('online', reconnectNow)\n connect()\n return () => {\n disposed = true\n clearTimer()\n source?.close()\n source = undefined\n runtime.window.removeEventListener('online', reconnectNow)\n }\n}\n\nexport interface ActivationWork<T> {\n readonly result: Promise<T>\n readonly cancel: () => void\n readonly commit?: (value: T) => void\n readonly dispose: (value: T) => void\n}\n\ninterface PendingActivation<T> {\n readonly key: object\n readonly generation: number\n readonly controller: AbortController\n readonly cancel: () => void\n readonly completion: Promise<boolean>\n}\n\n/** Keep at most one activation in flight per id and commit only its latest generation. */\nexport class PerIdActivationLifecycle<T> {\n private readonly active = new Map<string, { readonly value: T; readonly dispose: () => void }>()\n private readonly pending = new Map<string, PendingActivation<T>>()\n private readonly generations = new Map<string, number>()\n private disposed = false\n\n hasActive(id: string): boolean { return this.active.has(id) }\n getActive(id: string): T | undefined { return this.active.get(id)?.value }\n pendingCount(): number { return this.pending.size }\n\n async activate(\n id: string,\n key: object,\n cycleSignal: AbortSignal | undefined,\n create: (controller: AbortController, generation: number) => ActivationWork<T>,\n ): Promise<boolean> {\n if (this.disposed || cycleSignal?.aborted === true) return false\n const existing = this.pending.get(id)\n if (existing !== undefined) {\n if (existing.key === key && !existing.controller.signal.aborted) return this.waitFor(existing, cycleSignal)\n existing.cancel()\n }\n const generation = (this.generations.get(id) ?? 0) + 1\n this.generations.set(id, generation)\n const controller = new AbortController()\n let work: ActivationWork<T>\n try { work = create(controller, generation) } catch {\n controller.abort(new DOMException('mobile extension activation failed to start', 'AbortError'))\n return false\n }\n let cancelled = false\n let pending = {} as PendingActivation<T>\n const cancel = (): void => {\n if (cancelled) return\n cancelled = true\n if (this.pending.get(id) === pending) this.pending.delete(id)\n if (this.generations.get(id) === generation) this.generations.set(id, generation + 1)\n controller.abort(new DOMException('mobile extension activation cancelled', 'AbortError'))\n work.cancel()\n }\n const completion = Promise.resolve(work.result).then(value => {\n const current = this.pending.get(id)\n const canCommit = !this.disposed && current === pending && current.generation === this.generations.get(id)\n && !controller.signal.aborted\n if (!canCommit) {\n try { work.dispose(value) } catch { /* A stale extension disposer must not break lifecycle cleanup. */ }\n return false\n }\n try { work.commit?.(value) } catch {\n try { work.dispose(value) } catch { /* Keep the previous active extension intact. */ }\n return false\n }\n const previous = this.active.get(id)\n let valueDisposed = false\n const dispose = (): void => {\n if (valueDisposed) return\n valueDisposed = true\n controller.abort(new DOMException('mobile extension deactivated', 'AbortError'))\n try { work.dispose(value) } catch { /* Disposers are isolated per extension. */ }\n }\n this.active.set(id, { value, dispose })\n previous?.dispose()\n return true\n }, () => {\n cancel()\n return false\n }).finally(() => {\n if (this.pending.get(id) === pending) this.pending.delete(id)\n })\n Object.assign(pending, { key, generation, controller, cancel, completion })\n this.pending.set(id, pending)\n return this.waitFor(pending, cycleSignal)\n }\n\n remove(id: string): void {\n this.generations.set(id, (this.generations.get(id) ?? 0) + 1)\n this.active.get(id)?.dispose()\n this.active.delete(id)\n this.pending.get(id)?.cancel()\n }\n\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n for (const current of this.active.values()) current.dispose()\n this.active.clear()\n for (const current of this.pending.values()) current.cancel()\n }\n\n private waitFor(pending: PendingActivation<T>, signal: AbortSignal | undefined): Promise<boolean> {\n if (signal === undefined) return pending.completion\n if (signal.aborted) { pending.cancel(); return Promise.resolve(false) }\n return new Promise<boolean>(resolve => {\n let settled = false\n const finish = (value: boolean): void => {\n if (settled) return\n settled = true\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n }\n const onAbort = (): void => { pending.cancel(); finish(false) }\n signal.addEventListener('abort', onAbort, { once: true })\n void pending.completion.then(finish)\n })\n }\n}\n\nfunction installCustomAssets(): () => void {\n const legacyStyle = element('style'); legacyStyle.dataset.plugin = 'dsh-mobile-custom'; document.head.append(legacyStyle)\n const legacyCssState = { etag: '', modified: '' }\n const previous = window.dshMobile\n let legacyMount: MobileExtensionMount | undefined = queuedLegacyMount\n let legacySource = ''\n let legacyRoot: HTMLElement | undefined\n let legacyDispose: (() => void) | undefined\n const definitions = new Map<string, MobileClientDefinition>()\n type ExtensionSurfaceEntry = { readonly dispose: () => void; readonly container: HTMLElement; readonly host: () => HTMLElement }\n type ActiveExtension = { readonly controller: AbortController; readonly surfaces: Map<string, ExtensionSurfaceEntry>; readonly cleanup?: () => void }\n const activations = new PerIdActivationLifecycle<ActiveExtension>()\n const styleNodes = new Map<string, HTMLStyleElement>()\n const styleEtags = new Map<string, string>()\n const scriptDigests = new Map<string, string>()\n const activeHostGenerations = new Map<string, string | undefined>()\n const activationKeys = new Map<string, { readonly definition: MobileClientDefinition; readonly generation?: string }>()\n const manifestExtensionIds = new Set<string>()\n const managedDefinitionIds = new Set<string>()\n let manifestEtag = ''\n let disposed = false\n let expectedDefinitionId: string | undefined\n const SURFACE_HOST_STYLES: Readonly<Record<string, string>> = {\n 'sidebar-action': 'position:fixed;z-index:1100;top:calc(env(safe-area-inset-top) + 8px);left:8px;display:flex;flex-direction:column;gap:6px;pointer-events:none',\n 'header-action': 'position:fixed;z-index:1100;top:calc(env(safe-area-inset-top) + 8px);right:8px;display:flex;flex-direction:column;gap:6px;pointer-events:none',\n 'composer-dock': 'position:fixed;z-index:1100;bottom:calc(env(safe-area-inset-bottom) + 8px);left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:6px;pointer-events:none',\n 'settings-section': 'position:fixed;z-index:1100;inset:auto 8px calc(env(safe-area-inset-bottom) + 72px) 8px;max-height:40vh;overflow:auto;pointer-events:none',\n }\n const surfaceHost = (placement: string): HTMLElement | undefined => {\n const existing = document.querySelector<HTMLElement>(`[data-dsh-mobile-surface-host=\"${placement}\"]`)\n if (existing !== null) return existing\n const style = SURFACE_HOST_STYLES[placement]\n if (style === undefined) return undefined\n const host = element('div'); host.dataset.dshMobileSurfaceHost = placement; host.style.cssText = style\n document.body.append(host); return host\n }\n const shellLayer = (): HTMLElement => {\n const existing = document.querySelector<HTMLElement>('[data-dsh-mobile-extension-layer]')\n if (existing !== null) return existing\n const layer = element('div'); layer.dataset.dshMobileExtensionLayer = 'true'; layer.style.cssText = 'position:fixed;inset:0;z-index:1200;pointer-events:none;overflow:hidden'\n document.body.append(layer); return layer\n }\n const toast = (message: string): void => {\n const node = element('div'); node.textContent = message; node.style.cssText = 'position:absolute;top:16px;left:50%;transform:translateX(-50%);padding:9px 14px;border-radius:999px;background:#1f2937;color:white;font:14px system-ui;pointer-events:auto;box-shadow:0 8px 24px #0003'\n shellLayer().append(node); window.setTimeout(() => node.remove(), 2600)\n }\n const materializeNativeFile = (value: unknown): unknown => {\n if (typeof value !== 'object' || value === null || !('base64' in value) || !('name' in value)) return value\n const candidate = value as { base64?: unknown; name?: unknown; type?: unknown }\n if (typeof candidate.base64 !== 'string' || typeof candidate.name !== 'string') return value\n try {\n const encoded = atob(candidate.base64)\n const bytes = Uint8Array.from(encoded, character => character.charCodeAt(0))\n return new File([bytes], candidate.name, { type: typeof candidate.type === 'string' ? candidate.type : 'application/octet-stream' })\n } catch {\n return value\n }\n }\n const invokeNative = async (action: string, input: unknown, signal: AbortSignal): Promise<unknown> => {\n const abortReason = (): unknown => signal.reason ?? new DOMException('mobile extension disposed', 'AbortError')\n if (signal.aborted) throw abortReason()\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge !== undefined) {\n return new Promise<unknown>((resolve, reject) => {\n let settled = false\n const finish = (callback: () => void): void => { if (settled) return; settled = true; signal.removeEventListener('abort', onAbort); callback() }\n const onAbort = (): void => { finish(() => { reject(abortReason()) }) }\n signal.addEventListener('abort', onAbort, { once: true })\n void Promise.resolve().then(() => bridge.invoke(action, input)).then(\n value => { finish(() => { resolve(materializeNativeFile(value)) }) },\n error => { finish(() => { reject(error) }) },\n )\n })\n }\n if (action === 'share' && typeof navigator.share === 'function') { await navigator.share((input ?? {}) as ShareData); return { ok: true } }\n if (action === 'clipboard.read' && navigator.clipboard !== undefined) return { text: await navigator.clipboard.readText() }\n if (action === 'clipboard.write' && navigator.clipboard !== undefined) { await navigator.clipboard.writeText(typeof input === 'object' && input !== null && 'text' in input ? String((input as { text: unknown }).text) : ''); return { ok: true } }\n if (action === 'files.pick' || action === 'camera.capture') {\n const inputElement = element('input')\n inputElement.type = 'file'\n inputElement.accept = action === 'camera.capture' ? 'image/*' : '*/*'\n if (action === 'camera.capture') inputElement.capture = 'environment'\n inputElement.hidden = true\n return new Promise<File | undefined>((resolve, reject) => {\n let settled = false\n let cleaned = false\n let cleanupTimer = 0\n let watchdogTimer = 0\n const cleanup = (): void => {\n if (cleaned) return\n cleaned = true\n if (cleanupTimer !== 0) window.clearTimeout(cleanupTimer)\n if (watchdogTimer !== 0) window.clearTimeout(watchdogTimer)\n window.removeEventListener('focus', scheduleCancel)\n document.removeEventListener('visibilitychange', onVisibilityChange)\n signal.removeEventListener('abort', onAbort)\n inputElement.removeEventListener('change', onChange)\n inputElement.removeEventListener('cancel', onCancel)\n inputElement.remove()\n }\n const finish = (callback: () => void): void => { if (settled) return; settled = true; cleanup(); callback() }\n const onChange = (): void => { const file = inputElement.files?.[0]; finish(() => { resolve(file) }) }\n const onCancel = (): void => { finish(() => { resolve(undefined) }) }\n const onAbort = (): void => { finish(() => { reject(abortReason()) }) }\n const scheduleCancel = (): void => { if (!settled && cleanupTimer === 0) cleanupTimer = window.setTimeout(onCancel, 1000) }\n const onVisibilityChange = (): void => { if (document.visibilityState === 'visible') scheduleCancel() }\n inputElement.addEventListener('change', onChange, { once: true })\n inputElement.addEventListener('cancel', onCancel, { once: true })\n window.addEventListener('focus', scheduleCancel)\n document.addEventListener('visibilitychange', onVisibilityChange)\n signal.addEventListener('abort', onAbort, { once: true })\n watchdogTimer = window.setTimeout(onCancel, 300_000)\n try {\n document.body.append(inputElement)\n inputElement.click()\n } catch (error) {\n finish(() => { reject(error) })\n }\n })\n }\n throw new Error('native capability is unavailable')\n }\n const makeApi = (id: string, hostGeneration: string | undefined, controller: AbortController, surfaces: Map<string, ExtensionSurfaceEntry>, surfaceIds: Set<string>): MobileClientApi => {\n const ensureCurrent = (): void => { if (controller.signal.aborted) throw controller.signal.reason }\n const requestSignal = (signal?: AbortSignal | null): CombinedClientSignal => signal === undefined || signal === null\n ? { signal: controller.signal, cleanup: () => undefined }\n : combineClientSignalLifetime(controller.signal, signal)\n const mountSurface = (surface: MobileSurface): (() => void) => {\n ensureCurrent()\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(surface.id) || surface.label.length > 120) throw new Error('invalid mobile surface')\n return registerUniqueDisposable(surfaces, surfaceIds, surface.id, () => {\n const container = element('section'); container.dataset.dshMobileSurface = surface.id; container.hidden = surface.placement === 'page' || surface.placement === 'overlay'; container.style.cssText = surface.placement === 'page' || surface.placement === 'overlay' ? 'position:absolute;inset:0;overflow:auto;background:var(--dsw-alias-bg-layer-1,#fff);padding:16px;pointer-events:auto' : 'pointer-events:auto'\n const host = (): HTMLElement => surface.placement === 'page' || surface.placement === 'overlay' ? shellLayer() : surfaceHost(surface.placement) ?? shellLayer()\n const mounted = surface.mount(container)\n const dispose = (): void => { try { if (typeof mounted === 'function') mounted() } finally { container.remove() } }\n return { dispose, container, host }\n })\n }\n return {\n host: {\n invoke: (action: string, input: unknown) => {\n ensureCurrent()\n return mobileRequest(`/mobile-access/extensions/${encodeURIComponent(id)}/actions/${encodeURIComponent(action)}`, extensionActionRequestInit(hostGeneration, input, controller.signal)).then(async response => { const value = await response.json() as unknown; if (!response.ok) throw new Error(typeof value === 'object' && value !== null && 'error' in value ? String((value as { error: unknown }).error) : `HTTP ${String(response.status)}`); return value })\n },\n fetch: (path: string, init?: RequestInit) => {\n ensureCurrent()\n const target = extensionRouteUrl(id, path, location.href)\n const headers = extensionGenerationHeaders(hostGeneration, init?.headers)\n const lifetime = requestSignal(init?.signal)\n return mobileRequest(target.href, { ...init, headers, signal: lifetime.signal }).then(\n response => bindClientResponseLifetime(response, lifetime.cleanup),\n error => { lifetime.cleanup(); throw error },\n )\n },\n assetUrl: path => { ensureCurrent(); return extensionAssetUrl(id, hostGeneration, path, location.href).href },\n },\n ui: {\n registerSurface: mountSurface,\n open: surfaceId => { ensureCurrent(); const entry = surfaces.get(surfaceId); if (entry !== undefined) entry.container.hidden = false },\n close: surfaceId => { ensureCurrent(); const entry = surfaces.get(surfaceId); if (entry !== undefined) entry.container.hidden = true },\n toast: message => { ensureCurrent(); toast(message) },\n },\n native: {\n capabilities: async () => { ensureCurrent(); const bridge = window.__DSH_MOBILE_NATIVE__; return bridge === undefined ? ['files.pick', 'camera.capture', 'share', 'clipboard.read', 'clipboard.write'] : bridge.capabilities() },\n invoke: (action, input) => invokeNative(action, input, controller.signal),\n },\n signal: controller.signal, document, window,\n }\n }\n const activateDefinition = (definition: MobileClientDefinition, cycleSignal?: AbortSignal, commitGeneration?: () => void, hostGeneration?: string): Promise<boolean> => {\n const previousKey = activationKeys.get(definition.id)\n const key = previousKey?.definition === definition && previousKey.generation === hostGeneration\n ? previousKey\n : { definition, ...(hostGeneration === undefined ? {} : { generation: hostGeneration }) }\n activationKeys.set(definition.id, key)\n return activations.activate(\n definition.id,\n key,\n cycleSignal,\n controller => {\n const surfaces = new Map<string, ExtensionSurfaceEntry>()\n const surfaceIds = new Set<string>()\n let pendingDisposed = false\n const disposePending = (): void => {\n if (pendingDisposed) return\n pendingDisposed = true\n for (const surface of surfaces.values()) {\n try { surface.dispose() } catch { /* Dispose every staged surface independently. */ }\n }\n surfaces.clear()\n surfaceIds.clear()\n }\n const result = Promise.resolve().then(() => definition.activate(makeApi(definition.id, hostGeneration, controller, surfaces, surfaceIds))).then(cleanup => ({\n controller,\n surfaces,\n ...(typeof cleanup === 'function' ? { cleanup } : {}),\n }))\n return {\n result,\n cancel: disposePending,\n commit: value => {\n if (definitions.get(definition.id) !== definition || controller.signal.aborted) throw new Error('stale mobile extension activation')\n for (const surface of value.surfaces.values()) surface.host().append(surface.container)\n activeHostGenerations.set(definition.id, hostGeneration)\n commitGeneration?.()\n },\n dispose: value => {\n controller.abort(new DOMException('mobile extension disposed', 'AbortError'))\n try { value.cleanup?.() } finally { disposePending() }\n },\n }\n },\n )\n }\n const define = (definition: MobileClientDefinition): void => {\n if (disposed || definition.apiVersion !== 1 || !/^[a-z][a-z0-9-]{0,63}$/u.test(definition.id) || typeof definition.activate !== 'function') return\n if (expectedDefinitionId !== undefined && definition.id !== expectedDefinitionId) return\n definitions.set(definition.id, definition)\n if (started && expectedDefinitionId === undefined) void activateDefinition(definition)\n }\n let started = false\n window.dshMobile = Object.freeze({ register: mount => { legacyMount = mount }, define })\n for (const definition of queuedDefinitions.splice(0)) define(definition)\n let legacyJsEtag = ''\n let legacyJsModified = ''\n const refreshLegacy = async (signal: AbortSignal): Promise<boolean> => {\n const previousMount = legacyMount\n let pendingRoot: HTMLElement | undefined\n try {\n const headers: Record<string, string> = {}\n if (legacyJsEtag !== '') headers['if-none-match'] = legacyJsEtag\n if (legacyJsModified !== '') headers['if-modified-since'] = legacyJsModified\n const response = await fetch('/mobile-access/custom.js', { credentials: 'same-origin', cache: 'no-store', headers, signal })\n if (response.status === 304) return true\n if (!response.ok) return false\n const nextEtag = response.headers.get('etag') ?? ''\n const nextModified = response.headers.get('last-modified') ?? ''\n const next = await response.text()\n if (refreshAborted(signal)) return false\n if (next === legacySource) { legacyJsEtag = nextEtag; legacyJsModified = nextModified; return true }\n legacyMount = undefined\n const script = element('script'); script.textContent = `${next}\\n//# sourceURL=dsh-mobile-custom.js`; document.head.append(script); script.remove()\n if (refreshAborted(signal)) { legacyMount = previousMount; return false }\n const mount = legacyMount as MobileExtensionMount | undefined\n if (mount === undefined) {\n legacyDispose?.(); legacyDispose = undefined\n legacyRoot?.remove(); legacyRoot = undefined\n legacySource = next; legacyJsEtag = nextEtag; legacyJsModified = nextModified\n return true\n }\n const nextRoot = element('div'); pendingRoot = nextRoot; nextRoot.dataset.dshMobileExtension = 'true'; document.body.append(nextRoot)\n const nextDispose = mount({ document, request: mobileRequest, root: nextRoot, window })\n if (refreshAborted(signal)) { if (typeof nextDispose === 'function') nextDispose(); pendingRoot.remove(); pendingRoot = undefined; legacyMount = previousMount; return false }\n legacyDispose?.(); legacyRoot?.remove(); legacyRoot = nextRoot; pendingRoot = undefined; legacyDispose = typeof nextDispose === 'function' ? nextDispose : undefined; legacySource = next\n legacyJsEtag = nextEtag; legacyJsModified = nextModified\n return true\n } catch { pendingRoot?.remove(); legacyMount = previousMount; return false }\n }\n let legacyScriptRevision = ''\n let legacyStyleRevision = ''\n const disposeManifestExtension = (id: string): void => {\n activations.remove(id)\n styleNodes.get(id)?.remove(); styleNodes.delete(id); styleEtags.delete(id); scriptDigests.delete(id); activeHostGenerations.delete(id); activationKeys.delete(id)\n if (managedDefinitionIds.delete(id)) definitions.delete(id)\n }\n const managedManifestIdSources = (): readonly Iterable<string>[] => [styleNodes.keys(), styleEtags.keys(), scriptDigests.keys(), managedDefinitionIds]\n const clearManifestExtensions = (): void => {\n publishAuthoritativeExtensionIds(manifestExtensionIds, new Set(), managedManifestIdSources(), disposeManifestExtension)\n manifestEtag = ''\n }\n const refreshExtensions = async (signal: AbortSignal): Promise<boolean> => {\n try {\n const headers: Record<string, string> = {}\n if (manifestEtag !== '') headers['if-none-match'] = manifestEtag\n const response = await fetch('/mobile-access/extensions/manifest', { credentials: 'same-origin', cache: 'no-store', headers, signal })\n if (response.status === 404) {\n legacyScriptRevision = ''\n legacyStyleRevision = ''\n return handleMissingExtensionManifest(\n clearManifestExtensions,\n () => Promise.all([refreshLegacy(signal), refreshCssLegacy(legacyStyle, signal, legacyCssState)]),\n signal,\n )\n }\n if (response.status === 304) return true\n if (!response.ok) return false\n const nextManifestEtag = response.headers.get('etag') ?? ''\n const payload = parseMobileExtensionManifest(await response.json())\n if (refreshAborted(signal) || payload === undefined) return false\n const entries = payload.extensions\n const seen = new Set(entries.map(entry => entry.id))\n const scriptRevision = payload.legacy.scriptRevision\n const styleRevision = payload.legacy.styleRevision\n let refreshComplete = true\n if (scriptRevision === '' || scriptRevision !== legacyScriptRevision) {\n if (await refreshLegacy(signal)) legacyScriptRevision = scriptRevision\n else refreshComplete = false\n }\n if (styleRevision === '' || styleRevision !== legacyStyleRevision) {\n if (await refreshCssLegacy(legacyStyle, signal, legacyCssState)) legacyStyleRevision = styleRevision\n else refreshComplete = false\n }\n const commitStyle = (id: string, change: 'retain' | 'remove' | 'replace', css?: string, etag?: string): void => {\n if (change === 'retain') return\n const oldStyle = styleNodes.get(id)\n if (change === 'remove') {\n oldStyle?.remove()\n styleNodes.delete(id)\n styleEtags.delete(id)\n return\n }\n if (oldStyle?.textContent !== css) {\n const node = element('style'); node.dataset.dshMobileExtensionStyle = id; node.textContent = css ?? ''; document.head.append(node); styleNodes.set(id, node); oldStyle?.remove()\n }\n if (etag !== undefined && etag !== '') styleEtags.set(id, etag)\n }\n for (const entry of entries) {\n const hadActiveGeneration = activations.hasActive(entry.id)\n const previousHostGeneration = activeHostGenerations.get(entry.id)\n let previousDefinition: MobileClientDefinition | undefined\n let evaluatedDefinition = false\n try {\n let styleChange: 'retain' | 'remove' | 'replace' = entry.styleUrl === undefined ? 'remove' : 'retain'\n let pendingCss: string | undefined\n let pendingStyleEtag: string | undefined\n const cssUrl = typeof entry.styleUrl === 'string' ? entry.styleUrl : undefined\n if (cssUrl !== undefined) {\n const cssHeaders: Record<string, string> = {}\n const storedEtag = styleEtags.get(entry.id)\n if (storedEtag !== undefined) cssHeaders['if-none-match'] = storedEtag\n const cssResponse = await fetch(cssUrl, { credentials: 'same-origin', cache: 'no-store', headers: cssHeaders, signal })\n if (cssResponse.status !== 304) {\n if (!cssResponse.ok) throw new Error('mobile extension style failed to load')\n pendingStyleEtag = cssResponse.headers.get('etag') ?? undefined\n pendingCss = await cssResponse.text()\n styleChange = 'replace'\n if (refreshAborted(signal)) return false\n }\n }\n\n const scriptUrl = typeof entry.scriptUrl === 'string' ? entry.scriptUrl : undefined\n if (scriptUrl === undefined) {\n if (refreshAborted(signal)) return false\n commitStyle(entry.id, styleChange, pendingCss, pendingStyleEtag)\n activations.remove(entry.id)\n activeHostGenerations.delete(entry.id)\n scriptDigests.delete(entry.id)\n if (managedDefinitionIds.delete(entry.id)) definitions.delete(entry.id)\n } else {\n const scriptHeaders: Record<string, string> = {}\n const storedDigest = scriptDigests.get(entry.id)\n if (storedDigest !== undefined) scriptHeaders['if-none-match'] = storedDigest\n const scriptResponse = await fetch(scriptUrl, { credentials: 'same-origin', cache: 'no-store', headers: scriptHeaders, signal })\n let nextDigest: string | undefined\n if (scriptResponse.status !== 304) {\n if (!scriptResponse.ok) throw new Error('mobile extension script failed to load')\n const source = await scriptResponse.text()\n if (refreshAborted(signal)) return false\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(source))\n if (refreshAborted(signal)) return false\n const key = [...new Uint8Array(digest)].map(value => value.toString(16).padStart(2, '0')).join('')\n if (scriptDigests.get(entry.id) !== key) {\n previousDefinition = definitions.get(entry.id)\n try {\n expectedDefinitionId = entry.id\n try {\n const script = element('script'); script.textContent = `${source}\\n//# sourceURL=dsh-mobile-extension-${entry.id}.js`; document.head.append(script); script.remove()\n } finally { expectedDefinitionId = undefined }\n const nextDefinition = definitions.get(entry.id)\n if (nextDefinition === undefined || nextDefinition === previousDefinition) throw new Error('mobile extension did not define its manifest id')\n evaluatedDefinition = true\n nextDigest = key\n } catch (error) {\n if (previousDefinition === undefined) definitions.delete(entry.id)\n else definitions.set(entry.id, previousDefinition)\n throw error\n }\n }\n }\n const definition = definitions.get(entry.id)\n const commitGeneration = (): void => { commitStyle(entry.id, styleChange, pendingCss, pendingStyleEtag) }\n if (definition === undefined) throw new Error('mobile extension definition is unavailable')\n if (evaluatedDefinition || !activations.hasActive(entry.id) || activeHostGenerations.get(entry.id) !== entry.generation) {\n if (!await activateDefinition(definition, signal, commitGeneration, entry.generation)) throw new Error('mobile extension activation failed')\n } else {\n if (refreshAborted(signal)) return false\n commitGeneration()\n }\n managedDefinitionIds.add(entry.id)\n if (nextDigest !== undefined) scriptDigests.set(entry.id, nextDigest)\n }\n } catch {\n if (evaluatedDefinition) {\n if (previousDefinition === undefined) definitions.delete(entry.id)\n else definitions.set(entry.id, previousDefinition)\n }\n failClosedExtensionGenerationReplacement(\n hadActiveGeneration,\n previousHostGeneration,\n entry.generation,\n () => { disposeManifestExtension(entry.id) },\n )\n refreshComplete = false\n }\n }\n if (refreshAborted(signal)) return false\n if (refreshComplete) {\n publishAuthoritativeExtensionIds(manifestExtensionIds, seen, managedManifestIdSources(), disposeManifestExtension)\n manifestEtag = nextManifestEtag\n } else manifestEtag = ''\n return refreshComplete\n } catch { return false }\n }\n started = true\n for (const definition of definitions.values()) void activateDefinition(definition)\n const stopRefresh = startLifecycleRefreshScheduler(async signal => {\n await refreshExtensions(signal)\n })\n const stopEvents = startExtensionChangeStream(\n () => { stopRefresh.refresh() },\n undefined,\n payload => {\n // Host-pushed task completion: exact, no guessing. Each completed turn\n // keeps its own notification, and generic text avoids attributing a\n // background task to whichever session happens to be open on the phone.\n if (document.hidden !== true) return\n const parsed = parseTaskNotifyPayload(payload)\n if (parsed === undefined) return\n const language = resolveNativeMobileLanguage(\n document.documentElement.lang,\n navigator.languages.length > 0 ? [...navigator.languages] : [navigator.language],\n )\n const label = (italian: string, english: string, chinese: string): string =>\n language === 'it' ? italian : language === 'zh' ? chinese : english\n fireTaskNotifyEvent({\n kind: 'done',\n title: label('Attività completata', 'Task finished', '任务已完成'),\n body: label('Il tuo task DSH è terminato', 'Your DSH task finished', '你的 DSH 任务已完成'),\n tag: taskCompletionTag(parsed.sessionId, parsed.turn),\n })\n },\n () => { fireDeviceRevoked() },\n )\n return () => { disposed = true; stopEvents(); stopRefresh(); started = false; legacyDispose?.(); legacyDispose = undefined; legacyRoot?.remove(); legacyRoot = undefined; legacyStyle.remove(); activations.dispose(); for (const node of styleNodes.values()) node.remove(); styleNodes.clear(); const layer = document.querySelector('[data-dsh-mobile-extension-layer]'); layer?.remove(); for (const host of document.querySelectorAll('[data-dsh-mobile-surface-host]')) host.remove(); if (previous === undefined) delete window.dshMobile; else window.dshMobile = previous }\n}\n\ninterface LegacyCssState { etag: string; modified: string }\nasync function refreshCssLegacy(style: HTMLStyleElement, signal: AbortSignal, state: LegacyCssState): Promise<boolean> {\n try {\n const headers: Record<string, string> = {}\n if (state.etag !== '') headers['if-none-match'] = state.etag\n if (state.modified !== '') headers['if-modified-since'] = state.modified\n let response = await fetch('/mobile-access/custom.css', { credentials: 'same-origin', cache: 'no-store', headers, signal })\n if (response.status === 304 && style.textContent === '') {\n state.etag = ''\n state.modified = ''\n response = await fetch('/mobile-access/custom.css', { credentials: 'same-origin', cache: 'no-store', signal })\n }\n if (response.status === 304) return true\n if (response.ok) {\n const nextEtag = response.headers.get('etag') ?? ''\n const nextModified = response.headers.get('last-modified') ?? ''\n const css = await response.text()\n if (refreshAborted(signal)) return false\n style.textContent = css\n state.etag = nextEtag\n state.modified = nextModified\n return true\n }\n return false\n } catch { return false }\n}\n\n/** Theme-aware desktop Mobile Access panel styles. */\nexport const CONTROL_STYLES = `\n.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)}\n.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-l2,#e1e5eb);border-radius:18px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 18px 50px rgb(15 23 42 / 18%)}\n.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:var(--dsw-alias-label-primary-bluish,#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)}\n.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:var(--dsw-alias-label-primary,#dc2626);border-left:3px solid var(--dsw-alias-state-error-primary,#dc2626)}.dsh-mobile-control__release-notice[hidden]{display:none}.dsh-mobile-control__update-card{margin:0 0 10px;padding:11px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:12px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__update-card[hidden]{display:none}.dsh-mobile-control__update-card-title{margin:0 0 7px;color:var(--dsw-alias-label-primary,#16181d);font:650 13px/1.4 system-ui}.dsh-mobile-control__update-notes{display:block;box-sizing:border-box;max-height:150px;overflow-y:auto;margin:0 0 8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#e1e5eb);border-radius:9px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#475569);white-space:pre-wrap;word-break:break-word;font:11px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.dsh-mobile-control__update-notes[hidden]{display:none}.dsh-mobile-control__update-notice{margin:0 0 9px;padding:7px 9px;border-radius:8px;background:var(--dsw-alias-state-warn-tertiary,#fff4d6);color:var(--dsw-alias-state-warn-label,#92400e);font-size:11px;line-height:1.5}.dsh-mobile-control__update-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.dsh-mobile-control__update-actions .dsh-mobile-control__primary,.dsh-mobile-control__update-actions .dsh-mobile-control__secondary{min-height:36px;padding:0 12px}.dsh-mobile-control__app-download{display:flex;align-items:center;justify-content:space-between;box-sizing:border-box;min-height:44px;margin:0 0 10px;padding:8px 11px;border:1px solid var(--dsw-alias-border-l2,#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:var(--dsw-alias-label-primary-bluish,#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)}\n.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__http-frame-warning{box-sizing:border-box;margin:-4px 0 12px;padding:8px 10px;border:1px solid #d6a83f;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#fff7df);color:var(--dsw-alias-label-primary,#694b00);font-size:11px;line-height:1.5;overflow-wrap:anywhere}.dsh-mobile-control__http-frame-warning[hidden]{display:none}.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}\n.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: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-l2,#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:var(--dsw-alias-label-primary-bluish,#2563eb);background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-bg-layer-1,#f5f8ff));box-shadow:0 0 0 1px var(--dsw-alias-label-primary-bluish,#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:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8);font:650 11px/1.25 system-ui}.dsh-mobile-control__provider-badge.is-cpolar{background:var(--dsw-alias-state-success-tertiary,#eaf8f2);color:var(--dsw-alias-label-primary,#087454)}.dsh-mobile-control__provider-description{color:var(--dsw-alias-label-secondary,#606873);font-size:12px;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:var(--dsw-alias-label-primary-bluish,#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-l2,#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}\n.dsh-mobile-control__self-hosted{margin:8px 0 0;border:1px solid var(--dsw-alias-border-l2,#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:11px;line-height:1.35}.dsh-mobile-control__provider-badge.is-frp{background:var(--dsw-alias-bg-layer-1,#eef0f3);color:var(--dsw-alias-label-secondary,#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)}\n/* The cloudflared setup section deliberately reuses this card, so\n .dsh-mobile-control__cloudflared-setup is a DOM hook for targeting rather than a\n style rule of its own. */\n.dsh-mobile-control__cpolar-setup{margin:0 0 12px;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__cpolar-setup[hidden],.dsh-mobile-control__cpolar-account[hidden],.dsh-mobile-control__details[hidden],.dsh-mobile-control__view.is-remote .dsh-mobile-control__actions[hidden],.dsh-mobile-control__danger[hidden]{display:none}.dsh-mobile-control__component-status,.dsh-mobile-control__component-note{margin:0 0 10px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.55}.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__primary{width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:600 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__cpolar-account{margin-top:10px}.dsh-mobile-control__link-row{display:flex;flex-wrap:wrap;gap:6px 12px;margin:0 0 10px}.dsh-mobile-control__text-link{color:var(--dsw-alias-label-primary-bluish,#2563eb);font-size:11px;text-decoration:none}.dsh-mobile-control__text-link:hover{text-decoration:underline}.dsh-mobile-control__token-label{display:flex;flex-direction:column;gap:5px;margin:0 0 8px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__token{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2,#fff));color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__cpolar-connect{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;min-height:44px;padding:10px 14px;border-radius:12px;font:650 13px/1.2 system-ui;cursor:pointer;transition:background-color 160ms ease,border-color 160ms ease,opacity 160ms ease}.dsh-mobile-control__cpolar-connect:hover:not(:disabled){border-color:#1d4ed8;background:#1d4ed8}.dsh-mobile-control__cpolar-connect:active:not(:disabled){border-color:#1e40af;background:#1e40af}.dsh-mobile-control__cpolar-connect:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__details{margin:10px 0 0;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb);padding-top:9px}.dsh-mobile-control__details>summary{min-height:36px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:30px;cursor:pointer}.dsh-mobile-control__details-body{display:flex;flex-wrap:wrap;align-items:center;gap:7px 12px;padding:4px 0}.dsh-mobile-control__details-body p{flex:1 0 100%;margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__storage{display:block;flex:1 0 100%;max-width:100%;overflow:hidden;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#475569);font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__danger{flex:1 0 100%;min-height:44px;margin-top:3px;padding:7px 10px;border:1px solid var(--dsw-alias-state-error-primary,#dc2626);border-radius:9px;background:transparent;color:var(--dsw-alias-label-primary,#dc2626);font:12px/1.3 system-ui;cursor:pointer}\n.dsh-mobile-control__danger:hover:not(:disabled),.dsh-mobile-control__device-revoke:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgb(220 38 38 / 8%))}\n.dsh-mobile-control__frp-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-setup[hidden]{display:none}.dsh-mobile-control__frp-step{padding:11px 0}.dsh-mobile-control__frp-step + .dsh-mobile-control__frp-step{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__frp-step>strong{display:block;margin-bottom:3px;font-size:12px;line-height:1.4}.dsh-mobile-control__frp-step>p{margin:0 0 9px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes{margin:0 0 9px;padding:0;list-style:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes li{position:relative;margin:0;padding-left:12px}.dsh-mobile-control__frp-changes li+li{margin-top:5px}.dsh-mobile-control__frp-changes li::before{content:\"•\";position:absolute;left:2px;color:var(--dsw-alias-label-tertiary,#98a1ad)}.dsh-mobile-control__frp-step>.dsh-mobile-control__frp-requirement{padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:11px}.dsh-mobile-control__frp-fields{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:8px}.dsh-mobile-control__field{display:flex;min-width:0;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__field:nth-child(3),.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__field input{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__frp-action{box-sizing:border-box;width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__frp-action:disabled{cursor:not-allowed;opacity:.5}.dsh-mobile-control__remote-workspace{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f7f8fa)}.dsh-mobile-control__stage-header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:9px}.dsh-mobile-control__stage-header .dsh-mobile-control__section-title{margin:0}.dsh-mobile-control__stage-meta{display:flex;min-width:0;align-items:center;justify-content:flex-end;gap:5px}.dsh-mobile-control__stage-value{max-width:115px;overflow:hidden;color:var(--dsw-alias-label-primary,#16181d);font:650 11px/1.3 system-ui;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__state-badge{flex:none;padding:3px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#606873);font:650 11px/1.25 system-ui}.dsh-mobile-control__state-badge.is-ready{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsw-alias-label-primary,#087454)}.dsh-mobile-control__state-badge.is-busy{background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8)}.dsh-mobile-control__state-badge.is-attention{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsw-alias-label-primary,#935100)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__status{box-sizing:border-box;margin:0 0 10px;padding:9px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:11px;line-height:1.45}.dsh-mobile-control__provider-setup-body{margin:0 0 10px}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup{margin:0}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__section-title,.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__frp-setup>.dsh-mobile-control__section-title{display:none}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__details{margin:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__actions{margin-top:2px}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__qr{margin:10px 0 0}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__manage-row{margin-top:10px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}\n.dsh-mobile-control__frp-overview{display:grid;grid-template-columns:32px minmax(0,1fr);align-items:center;gap:10px;margin:0 0 10px;padding:10px;border:1px solid var(--dsw-alias-state-success-primary,#a9dfc9);border-radius:11px;background:var(--dsw-alias-state-success-tertiary,#edf9f4)}.dsh-mobile-control__frp-overview[hidden]{display:none}.dsh-mobile-control__frp-overview-mark{display:grid;width:32px;height:32px;place-items:center;border-radius:50%;background:#087454;color:#fff;font:700 15px/1 system-ui}.dsh-mobile-control__frp-overview-body{display:flex;min-width:0;flex-direction:column;gap:2px}.dsh-mobile-control__frp-overview-body strong{color:var(--dsw-alias-label-primary,#075d46);font-size:12px;line-height:1.35}.dsh-mobile-control__frp-overview-body span{overflow:hidden;color:var(--dsw-alias-label-secondary,#357061);font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__frp-group{margin:8px 0 0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__frp-group>summary{box-sizing:border-box;min-height:44px;padding:12px 34px 10px 12px;color:var(--dsw-alias-label-primary,#16181d);font:650 12px/1.4 system-ui;cursor:pointer}.dsh-mobile-control__frp-group[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l2,#e1e5eb);background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step{padding:12px}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step>strong:first-child{display:none}.dsh-mobile-control__frp-group .dsh-mobile-control__frp-step{border-top:0}.dsh-mobile-control__frp-setup>.dsh-mobile-control__frp-step{margin-bottom:8px;padding:10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__tunnel{margin:0 0 10px}.dsh-mobile-control__tunnel[hidden]{display:none}\n.dsh-mobile-control__lan-setup{box-sizing:border-box;margin:0 0 12px;padding:14px;border:1px solid #6f96db;border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f5f8ff)}.dsh-mobile-control__lan-setup[hidden],.dsh-mobile-control__lan-setup-form[hidden]{display:none}.dsh-mobile-control__lan-setup>.dsh-mobile-control__section-title{margin-bottom:5px}.dsh-mobile-control__lan-setup>.dsh-mobile-control__intro{margin-bottom:12px}.dsh-mobile-control__lan-setup-form{display:grid;gap:9px}.dsh-mobile-control__lan-setup .dsh-mobile-control__field select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__lan-setup .dsh-mobile-control__component-note{margin:0}.dsh-mobile-control__lan-setup-actions{display:grid;grid-template-columns:1fr 1.35fr;gap:8px}.dsh-mobile-control__lan-setup-actions button{box-sizing:border-box;min-width:0;min-height:44px;padding:9px 10px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__lan-setup-actions button:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status{margin:10px 0 0;line-height:1.5}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status:empty{display:none}\n.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:\":\"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:var(--dsw-alias-label-primary-bluish,#2563eb);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}.dsh-mobile-control__qr{display:flex;justify-content:center;margin:0 0 12px}.dsh-mobile-control__qr[hidden]{display:none}.dsh-mobile-control__qr img{border-radius:12px;background:#fff;padding:8px}\n.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:var(--dsw-alias-label-tertiary,#98a1ad);content:\"\"}.dsh-mobile-control__status.is-running::before{background:var(--dsw-alias-state-success-primary,#16a36a)}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}\n.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}\n.dsh-mobile-control__extensions{margin:0 0 12px;color:var(--dsw-alias-label-secondary,#606873);font-size:12px}\n.dsh-mobile-control__view.is-diagnostics{--dsh-diagnostic-ok:var(--dsw-alias-state-success-primary,#087454);--dsh-diagnostic-warning:var(--dsw-alias-state-warn-primary,#a35b00);--dsh-diagnostic-error:var(--dsw-alias-state-error-primary,#c62828);--dsh-diagnostic-info:var(--dsw-alias-label-secondary,#526071)}.dsh-mobile-control__diagnostic-summary{box-sizing:border-box;margin:0;padding:13px;border:1px solid var(--dsw-alias-border-l2,#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:var(--dsw-alias-bg-layer-1,#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-l2,#dbe1e8);color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.45}.dsh-mobile-control__diagnostic-summary.is-ok .dsh-mobile-control__diagnostic-summary-icon{background:var(--dsw-alias-state-success-tertiary,#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:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-summary.is-error .dsh-mobile-control__diagnostic-summary-icon{background:var(--dsw-alias-state-error-secondary,#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:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary-bluish,#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}\n.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}\n.dsh-mobile-control__diagnostic-toolbar{display:grid;grid-template-columns:1fr;gap:8px;margin-top:10px}.dsh-mobile-control__diagnostic-toolbar.has-report{grid-template-columns:1fr 1fr}.dsh-mobile-control__diagnostic-run,.dsh-mobile-control__diagnostic-copy{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border-radius:11px;font:650 12px/1.3 system-ui;cursor:pointer;touch-action:manipulation}.dsh-mobile-control__diagnostic-copy[hidden]{display:none}.dsh-mobile-control__diagnostic-run:disabled{cursor:wait;opacity:.58}.dsh-mobile-control__diagnostic-feedback{margin:8px 0 0;padding:8px 10px;border-radius:9px;background:var(--dsw-alias-state-business-tertiary,#eff6ff);color:var(--dsw-alias-label-primary,#1d4ed8);font-size:11px;line-height:1.45}.dsh-mobile-control__diagnostic-feedback[hidden]{display:none}\n.dsh-mobile-control__diagnostic-checks{display:grid;gap:12px;margin-top:12px;animation:dsh-diagnostic-reveal 160ms ease-out both}.dsh-mobile-control__diagnostic-checks[hidden]{display:none}.dsh-mobile-control__diagnostic-group{overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__diagnostic-group-header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 11px;border-bottom:1px solid var(--dsw-alias-border-l2,#e1e5eb);background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__diagnostic-group-header h3{margin:0;font:650 11px/1.4 system-ui}.dsh-mobile-control__diagnostic-group-header span{color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__diagnostic-list{display:flex;flex-direction:column}.dsh-mobile-control__diagnostic-check{display:grid;grid-template-columns:26px minmax(0,1fr);gap:9px;padding:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__diagnostic-check + .dsh-mobile-control__diagnostic-check{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__diagnostic-marker{position:relative;width:26px;height:26px;border-radius:50%;background:var(--dsw-alias-bg-layer-1,#edf1f5);color:var(--dsh-diagnostic-info)}.dsh-mobile-control__diagnostic-marker::before,.dsh-mobile-control__diagnostic-marker::after{position:absolute;content:\"\"}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-marker{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-marker::before{top:7px;left:7px;width:9px;height:5px;border-bottom:1.8px solid currentColor;border-left:1.8px solid currentColor;transform:rotate(-45deg)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker{background:var(--dsw-alias-state-error-secondary,#fdecec);color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker::before,.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker::before{top:6px;left:12px;width:2px;height:9px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker::after,.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker::after{bottom:6px;left:12px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-check.is-info .dsh-mobile-control__diagnostic-marker::before{top:6px;left:12px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-check.is-info .dsh-mobile-control__diagnostic-marker::after{top:10px;left:12px;width:2px;height:9px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-check-body{min-width:0}.dsh-mobile-control__diagnostic-check-header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.dsh-mobile-control__diagnostic-check-header strong{min-width:0;font-size:12px;line-height:1.4}.dsh-mobile-control__diagnostic-badge{flex:none;padding:2px 6px;border-radius:999px;background:var(--dsw-alias-bg-layer-1,#edf1f5);color:var(--dsh-diagnostic-info);font:650 11px/1.3 system-ui}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-badge{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-badge{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-badge{background:var(--dsw-alias-state-error-secondary,#fdecec);color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-check p{margin:4px 0 0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5;overflow-wrap:anywhere}.dsh-mobile-control__diagnostic-check .dsh-mobile-control__diagnostic-action{margin-top:7px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f8fafc);color:var(--dsw-alias-label-primary,#16181d)}.dsh-mobile-control__diagnostic-action span{display:inline-block;margin-right:6px;color:var(--dsw-alias-label-primary-bluish,#2563eb);font-weight:700}.dsh-mobile-control__diagnostic-details{margin-top:12px}.dsh-mobile-control__diagnostic-details[hidden]{display:none}.dsh-mobile-control__diagnostic-details>summary{box-sizing:border-box;min-height:44px;line-height:44px}.dsh-mobile-control__diagnostic-report{box-sizing:border-box;max-height:220px;margin:4px 0 0;overflow:auto;padding:10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#606873);font:11px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}\n.dsh-mobile-control__diagnostic-checks{transition:opacity 150ms ease}.dsh-mobile-control__diagnostic-checks.is-refreshing{opacity:.52}\n@keyframes dsh-diagnostic-spin{to{transform:rotate(360deg)}}@keyframes dsh-diagnostic-reveal{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n.dsh-mobile-control__actions{display:flex;flex-wrap:nowrap;gap:6px}.dsh-mobile-control__actions[hidden],.dsh-mobile-control__manage-row[hidden]{display:none}.dsh-mobile-control__actions button{flex:1 1 0;min-width:0;min-height:44px;padding:8px 4px;border-radius:10px;font:12px/1.2 system-ui;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-l3,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}\n.dsh-mobile-control button:focus-visible,.dsh-mobile-control a:focus-visible,.dsh-mobile-control input:focus-visible,.dsh-mobile-control select:focus-visible,.dsh-mobile-control summary:focus-visible{outline:3px solid rgb(37 99 235 / 28%);outline-offset:2px}\n/* 42px matches the DSH sidebar footer row; it is not one of the panel target tiers. */\n.dsh-mobile-control__trigger{box-sizing:border-box;flex:1 1 auto;display:flex;align-items:center;gap:8px;width:100%;height:42px;margin:4px 0;padding:0 10px 0 8px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font-family:inherit;font-size:14px;line-height:22px;cursor:pointer;overflow:hidden}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger:active,.dsh-mobile-control__trigger[aria-expanded=\"true\"]{background:var(--dsw-alias-interactive-bg-active,#e8ebf0)}.dsh-mobile-control__trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px}.dsh-mobile-control__trigger.is-rail{flex:0 0 auto;width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;gap:0;border-radius:50%}.dsh-mobile-control__trigger-icon{display:block;flex:none}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n.dsh-mobile-control__manage-row{display:flex;justify-content:space-between;gap:8px;margin-top:10px}.dsh-mobile-control__manage{flex:1 1 0;min-width:0;min-height:36px;padding:6px 8px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:transparent;color:inherit;font:12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__devices{margin-top:10px;border:1px solid var(--dsw-alias-border-l2,#e1e5eb);border-radius:10px;padding:8px;max-height:220px;overflow-y:auto}.dsh-mobile-control__device-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px;margin:0}.dsh-mobile-control__device{display:flex;align-items:center;gap:8px;padding:6px 2px}.dsh-mobile-control__device + .dsh-mobile-control__device{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__device-label{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.dsh-mobile-control__device-meta{flex:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;white-space:nowrap}.dsh-mobile-control__device-revoke{flex:none;min-height:36px;padding:4px 8px;border:1px solid var(--dsw-alias-state-error-primary,#dc2626);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary,#dc2626);font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__ws-paths{margin:10px 0 0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__ws-paths-list{margin:0 0 8px;padding:0;list-style:none}.dsh-mobile-control__ws-paths-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px}.dsh-mobile-control__ws-paths-item{display:flex;align-items:center;gap:8px;padding:6px 0}.dsh-mobile-control__ws-paths-item + .dsh-mobile-control__ws-paths-item{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__ws-paths-item code{flex:1 1 0;min-width:0;overflow:hidden;font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__ws-paths-remove{flex:none;min-height:36px;padding:4px 8px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:8px;background:transparent;color:inherit;font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__trigger,.dsh-mobile-control__diagnostic-entry{position:relative}.dsh-mobile-control__ws-dot{position:absolute;top:2px;right:2px;z-index:1;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff;font:650 11px/16px system-ui;text-align:center;cursor:pointer}.dsh-mobile-control__ws-dot::after{content:'';position:absolute;inset:-5px}.dsh-mobile-control__ws-sr-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.dsh-mobile-control__ws-paths-row{display:flex;gap:8px}.dsh-mobile-control__ws-paths-row .dsh-mobile-control__primary{width:auto;flex:none;padding:9px 14px;border-radius:10px}.dsh-mobile-control__ws-paths-group{padding:6px 0}.dsh-mobile-control__ws-paths-group + .dsh-mobile-control__ws-paths-group{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__ws-paths-group-head{display:flex;align-items:center;gap:8px}.dsh-mobile-control__ws-count{flex:none;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8);font:650 11px/1.6 system-ui}.dsh-mobile-control__ws-paths-item.is-child{padding-left:12px}.dsh-mobile-control__ws-paths-group-head .dsh-mobile-control__primary,.dsh-mobile-control__ws-paths-item .dsh-mobile-control__primary{width:auto;flex:none;min-height:36px;padding:6px 12px;border-radius:10px}.dsh-mobile-control__ws-paths-row input{flex:1 1 0;min-width:0;box-sizing:border-box;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__ws-paths-row button{flex:none}.dsh-mobile-control__ws-paths-detected-title{margin:0 0 2px;font:600 12px/1.4 system-ui}.dsh-mobile-control__ws-paths-advanced{margin-top:8px}.dsh-mobile-control__ws-paths-advanced summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#606873);font:12px/1.4 system-ui;user-select:none}.dsh-mobile-control__ws-paths-advanced summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px;border-radius:4px}.dsh-mobile-control__ws-paths-advanced[open] summary{margin-bottom:6px}.dsh-mobile-control__ws-paths-advanced .dsh-mobile-control__status{margin:6px 0 0}\n@media (max-width:359px){.dsh-mobile-control__provider{min-height:68px}}@media (prefers-reduced-motion:reduce){.dsh-mobile-control__provider,.dsh-mobile-control__cpolar-connect{transition:none}.dsh-mobile-control__diagnostic-summary.is-running .dsh-mobile-control__diagnostic-summary-icon::before,.dsh-mobile-control__diagnostic-checks{animation:none}}\n\n.dsh-mobile-control__origin-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__origin-setup[hidden],.dsh-mobile-control__origin-backend[hidden],.dsh-mobile-control__origin-feedback[hidden]{display:none}.dsh-mobile-control__origin-fields{display:grid;grid-template-columns:minmax(0,1fr) 110px;gap:12px}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field{font-size:12px}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(1),.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(2){grid-column:1}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(3){grid-column:2}.dsh-mobile-control__origin-warning{margin:12px 0;padding:10px 12px;border-left:3px solid var(--dsw-alias-state-warn-primary,#a56810);border-radius:4px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:12px;line-height:1.6}.dsh-mobile-control__origin-feedback{font-size:12px;line-height:1.6;overflow-wrap:anywhere}.dsh-mobile-control__origin-feedback.is-error{color:var(--dsw-alias-state-error-primary,#bc3030)}.dsh-mobile-control__origin-fields input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary,#bc3030)}.dsh-mobile-control__origin-backend{display:flex;min-width:0;flex-direction:column;align-items:stretch;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#dbe1e8)}.dsh-mobile-control__origin-backend button{align-self:flex-start;min-height:44px}.dsh-mobile-control__self-hosted-body .is-origin{margin-top:8px}\n`\n\n/** Mount the desktop control or mobile feature enhancements. */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => {\n if (window.__DSH_MOBILE_FRONTEND__ !== 'dedicated') return\n return trustAuthenticatedGatewayConnection(ctx.get('connection'))\n }, 'dsh-mobile: authenticated gateway client trust')\n\n ctx.effect(() => {\n const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__)\n const style = element('style'); style.dataset.plugin = 'dsh-mobile'; style.textContent = desktopAdmin\n ? CONTROL_STYLES\n : NATIVE_MOBILE_STYLES\n document.head.append(style)\n if (!desktopAdmin) {\n const removeSettingsAction = ctx.slots.inject('settings.general.item', () => ctx.slots.register({\n name: 'settings.general.item',\n id: 'dsh-mobile-switch-computer',\n order: 100,\n }, MobileSwitchComputerRow))\n const removeCustom = installCustomAssets()\n const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface)\n return () => { removeSettingsAction(); removeCustom(); removeSurface(); style.remove() }\n }\n const removeControl = installDshLanguageBoundSurface(() => {\n const control = installControl()\n const triggerLocale = selectedMobileControlLocale()\n const t = controlTranslator(triggerLocale)\n const disposeSlot = ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register<{ wide: boolean }>({ name: 'sidebar.footer.action', id: 'dsh-mobile' }, ({ wide }) => createElement('button', {\n 'aria-expanded': control.isOpen(),\n 'aria-controls': CONTROL_PANEL_ID,\n 'aria-label': t('mobileAccess'),\n className: `dsh-mobile-control__trigger${wide ? '' : ' is-rail'}`,\n lang: triggerLocale,\n type: 'button',\n title: t('mobileAccess'),\n onClick: control.toggle,\n }, createElement('svg', {\n 'aria-hidden': true,\n className: 'dsh-mobile-control__trigger-icon',\n focusable: false,\n width: wide ? 16 : 18,\n height: wide ? 16 : 18,\n viewBox: '0 0 16 16',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.5,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n }, createElement('rect', { x: 4, y: 1, width: 8, height: 14, rx: 2 }), createElement('path', { d: 'M7 12h2' })), wide ? createElement('span', { className: 'dsh-mobile-control__trigger-label' }, t('mobileAccess')) : undefined)))\n return () => { disposeSlot(); control.remove() }\n })\n return () => { removeControl(); style.remove() }\n }, 'dsh-mobile: stock mobile adaptation and local control')\n}\n\n/** Client services required by the mobile adaptation. */\nexport const inject: readonly string[] = ['slots']\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","createElement"],"sources":["../src/client-messages.ts","../src/frp-template.ts","../src/ip.ts","../src/network.ts","../src/frp-config.ts","../src/frp-attach.ts","../src/task-notify.ts","../src/native-mobile.ts","../src/local-admin-host.ts","../src/client.ts"],"sourcesContent":["export type MobileControlLocale = 'it' | 'en' | 'zh'\n\nexport const MOBILE_CONTROL_MESSAGES = {\n en: {\n mobileAccess: 'Mobile access', collapseMobileAccess: 'Collapse Mobile access', downloadAndroid: 'Download latest Android app', downloadAndroidVersion: 'Download latest Android app · v{version}', downloadAndroidAria: 'Download the latest Android app from GitHub Releases', downloadAndroidVersionAria: 'Download Android app v{version} from GitHub Releases', updatePlugin: 'Update plugin', updatePluginAria: 'Update DSH Mobile to v{version}', updatingPlugin: 'Updating…', pluginUpdatedRestart: 'Updated to v{version}. Restart DSH to apply it.', pluginUpdateFailed: 'Update failed: {error}', updateTo: 'Update to v{version}', updateNotice: 'Restart DSH after installing. Existing apps and paired devices need no re-pairing; unsure about the desktop version? Check the README compatibility table.', updateNotesEmpty: 'No release notes fetched — updating is still fine; full details on the GitHub Release page.', updateNow: 'Update now', updateLater: 'Not now',\n lan: 'Local network', remote: 'Remote', lanAccess: 'Local network access', remoteAccess: 'Remote access', browserAccess: 'Browser access', remoteAddress: 'Remote address', loadingStatus: 'Loading status…', loadingRemoteStatus: 'Loading remote status…',\n generateCopyKey: 'Generate and copy key', copyPairLink: 'Copy pairing link', managePairedDevices: 'Manage paired devices', clearAllDevices: 'Clear all devices', pairingQr: 'Pairing QR code',\n remoteIntro: 'Choose the remote channel that suits you. Switching or disabling remote access does not affect the local network.', httpFrameWarning: 'HTTP pages may be embedded for compatibility, but their contents are unencrypted and can be altered. Use HTTPS for sensitive work.', chooseProvider: 'Choose connection method', currentProvider: 'Current connection', remoteStateOff: 'Off', remoteStateConnecting: 'Connecting', remoteStateAttention: 'Needs attention', providerInfoAria: 'View remote connection security and network information', providerGroupAria: 'Remote connection method', providerSafeTitle: 'You remain protected', providerSafeText: 'Only paired devices can access DSH. cpolar is installed on demand and can be removed completely; Tailscale may be slow or unavailable on mainland China networks, where cpolar is recommended.',\n builtIn: 'Built in', mainlandPreferred: 'Preferred in mainland China', tailscaleDescription: 'Wider coverage; mainland China networks may be unstable. The first connection requires login and Funnel authorization.', cpolarDescription: 'Installs the official component on demand and is suitable for mainland China networks.',\n prepareCpolar: 'Prepare cpolar', checkingComponent: 'Checking component…', installOfficial: 'Install official component', cpolarAccountNote: 'Sign in to the cpolar website and copy the Authtoken. The token is stored only in the plugin private directory and is never shown on the page or in logs.', registerCpolar: 'Register with cpolar', openDashboard: 'Open dashboard to get token', tokenPlaceholder: 'Paste cpolar Authtoken', saveConnect: 'Save and connect', saving: 'Saving…', componentDetails: 'Component source and cleanup', componentDetailsText: 'Downloaded from the official cpolar website and verified at a fixed version only after you choose Install. It does not add a system service, startup item, registry entry, or PATH entry.', pluginPrivateDirectory: 'Plugin private directory', officialDownload: 'Official download page', terms: 'Terms of service', purgeCpolar: 'Completely remove cpolar component and configuration', tailscaleHelp: 'How to use Tailscale', tailscaleHelpText: 'The runtime component is included with the plugin. The first connection opens the official Tailscale login and Funnel authorization pages; the plugin never accesses your account password.',\n funnelGuideAria: 'Steps to enable Tailscale Funnel', funnelGuideTitle: 'Remote access setup · Step 2', funnelGuideSummary: 'Tailscale login is complete. Funnel must still be allowed for this computer; the official page also enables HTTPS.', funnelStep1: 'Open the official Tailscale authorization page for this node.', funnelStep2: 'Confirm Funnel; you do not need to sign in to DSH again.', funnelStep3: 'Return to DSH; the plugin checks and connects automatically.', funnelGuideNote: 'Requires an Owner, Admin, or Network admin account.', continueFunnel: 'Continue Funnel authorization', retryNow: 'Done, retry now',\n enableRemote: 'Enable remote access', disableRemote: 'Disable remote access', continueLogin: 'Continue login', reconnect: 'Reconnect', generateRemoteQr: 'Generate remote pairing QR code', copyRemoteLink: 'Copy remote pairing link', manageRemoteDevices: 'Manage remote devices', resetRemoteLogin: 'Sign out and clear remote login', resetRemoteDevices: 'Disable and clear remote devices',\n lanOn: 'Local network access is on.', lanOff: 'Local network access is off.', enableLan: 'Enable local network access', disableLan: 'Disable local network access', extensionsLoaded: 'Extensions: {loaded} loaded', extensionsFailed: 'Extensions: {loaded} loaded, {failed} failed',\n keyGenerationFailed: 'Could not generate a pairing key.', keyCopied: 'Pairing key copied. Paste it into the Android app.', linkCopied: 'Pairing link copied. Send it to the phone and paste it into the app or open it in a browser.', copySecret: 'Copy {kind}: {value}', pairingKey: 'pairing key', pairingLink: 'pairing link', noDevices: 'No paired devices.', noRemoteDevices: 'No remote paired devices.', device: 'Device', expires: 'Expires {time}', revoke: 'Revoke', confirmResetDevices: 'Remove all paired devices? Connected devices will be disconnected immediately.',\n ready: 'Ready', installed: 'Installed', installWithSize: 'Install official component · {size} MB', cpolarUnsupported: 'Windows x64 and Linux x64/arm64 are currently supported.', cpolarNotInstalled: 'Not installed. A fixed version is downloaded from the official cpolar website only after you click the button below.', cpolarNeedsToken: 'Official component {version} is verified. Save the account token to continue.', cpolarReady: 'Official component {version} and the local account configuration are ready. Free temporary addresses may change after DSH or cpolar restarts; if the app cannot connect, scan the current remote QR code again.',\n remoteOff: 'Remote access is disabled. Local network access is unaffected.', remoteUnavailableCpolar: 'cpolar is not installed or its local account is not configured.', remoteUnavailableTailscale: 'This computer is missing the Funnel runtime component. Reinstall the complete plugin package.', remoteStartingCpolar: 'Connecting to a cpolar node…', remoteStartingTailscale: 'Starting the secure Tailscale channel…', remoteNeedsLogin: 'Complete a one-time Tailscale login in the browser. The plugin never reads your password.', remoteConnectingCpolar: 'Public address allocated; starting the DSH authentication gateway…', remoteConnectingTailscale: 'Login complete; creating the public HTTPS address…', remoteReady: 'Remote access is ready. Only paired devices can access DSH.', remoteError: 'The remote connection was not established. Reconnect; local network access still works.',\n funnelPermission: 'Login is complete. Authorize Funnel to establish the remote connection automatically.', funnelHttps: 'Login is complete. Authorize Funnel; the official page also enables HTTPS.', funnelStart: 'Login is complete. Finish the initial Tailscale Funnel authorization.', tailscaleDnsMissing: 'Tailscale has not provided a remote address. Reconnect and confirm login is complete.', gatewayStartFailed: 'The remote gateway failed to start. Reconnect; local network access is unaffected.', controlChannelFailed: 'The remote component connection was interrupted. Reconnect.', cpolarMissing: 'The official cpolar component is not installed. Complete the preparation steps above.', cpolarInvalid: 'cpolar component verification failed. Remove it completely and reinstall.', cpolarConfigMissing: 'No cpolar account token is saved. Complete the preparation steps above.', cpolarConfigInvalid: 'The local cpolar configuration is invalid. Save the account token again.', cpolarPortUnavailable: 'Could not allocate a local remote-gateway port. Try again.', cpolarLaunchFailed: 'The cpolar client failed to start.', cpolarTimeout: 'Timed out connecting to a cpolar node. Reconnect.', cpolarStopped: 'The cpolar connection stopped.', cpolarExited: 'The cpolar connection exited unexpectedly. Reconnect.', cpolarOutputInvalid: 'cpolar returned an unrecognized status.', cpolarOriginInvalid: 'The public address returned by cpolar failed validation.', setupOpened: 'The official Tailscale page is open. Return to DSH after enabling Funnel; reconnection is automatic.',\n switchProviderConfirm: 'Switching connection method first disables the current remote channel. Local network access and paired devices are unaffected. Continue?', switchingCpolar: 'Switching to cpolar…', switchingTailscale: 'Switching to Tailscale Funnel…', installConfirm: 'Download and verify a fixed version from the official cpolar website (about 7.3 MB) and extract it only to the DSH Mobile private directory? No system service, PATH/registry entry, or startup item is added.', downloading: 'Downloading and verifying…', installingCpolar: 'Installing the official cpolar component. Keep DSH running until it completes.', installFailed: 'Component installation failed: {error}', invalidToken: 'Paste the complete Authtoken from the cpolar dashboard.', configuredConnecting: 'Account configuration saved; connecting through cpolar…', configureFailed: 'Configuration failed: {error}', purgeConfirm: 'Completely remove the cpolar component, token configuration, and runtime logs from the DSH Mobile private directory? Local network access, DSH data, and other system programs are unaffected.', purging: 'Stopping the channel and removing cpolar files managed by DSH Mobile…', purgeFailed: 'Cleanup failed: {error}', reconnectingCpolar: 'Reconnecting to a cpolar node…', reconnectingTailscale: 'Checking Tailscale settings and reconnecting…', remoteQrReady: 'Remote pairing QR code generated. Scan it from “Remote access” in the app.', resetCpolarConfirm: 'Disable the cpolar remote channel and remove all remote paired devices? Your cpolar account and other tunnels are unchanged.', resetTailscaleConfirm: 'Sign out of Tailscale on this computer and remove all remote paired devices? Local network configuration is unchanged.', requestFailed: 'Request failed: {error}', wsPathsTitle: 'Third-party WebSocket paths', wsPathsIntro: 'When an installed plugin cannot connect (say a sidebar terminal or a live panel), it is usually because its connection paths have not been approved yet. Everything that needs your attention is listed below — one click on “Allow” fixes it.', wsPathsPlaceholder: '/sidebar/ws/terminal', wsPathsAdd: 'Allow path', wsPathsRemove: 'Remove', wsPathsEmpty: 'Nothing to configure right now. If a plugin cannot connect later, one-click options appear here.', wsPathsInvalid: 'Enter an exact path starting with / (no query string).', wsPathsDetected: 'A plugin connection was blocked — press Allow to let it through', wsPathsAllow: 'Allow', wsPathsAllowAll: 'Allow all', wsPathsGroupMeta: '{n} paths · blocked {m} times', wsPathsNewBlocked: 'New blocked third-party connections — open diagnostics to review', wsPathsAdvancedToggle: 'Add paths manually (advanced)', wsPathsAnnounce: '{n} plugin connections waiting to be approved', wsPathsAdvancedHint: 'Manual entries are rarely necessary: use the buttons above. Only fill this in when you know the exact path.',\n },\n it: {\n mobileAccess: 'Accesso mobile', collapseMobileAccess: 'Riduci Accesso mobile', downloadAndroid: 'Scarica l’ultima app Android', downloadAndroidVersion: 'Scarica l’ultima app Android · v{version}', downloadAndroidAria: 'Scarica l’ultima app Android da GitHub Releases', downloadAndroidVersionAria: 'Scarica l’app Android v{version} da GitHub Releases', updatePlugin: 'Aggiorna plugin', updatePluginAria: 'Aggiorna DSH Mobile alla v{version}', updatingPlugin: 'Aggiornamento…', pluginUpdatedRestart: 'Aggiornato alla v{version}. Riavvia DSH per applicarlo.', pluginUpdateFailed: 'Aggiornamento non riuscito: {error}', updateTo: 'Aggiorna alla v{version}', updateNotice: 'Riavvia DSH dopo l’installazione. Le app esistenti e i dispositivi già associati non richiedono ri-associazione; in caso di dubbi sulla versione desktop, consulta la tabella di compatibilità nel README.', updateNotesEmpty: 'Note di rilascio non recuperate — l’aggiornamento resta possibile; dettagli nella pagina Release su GitHub.', updateNow: 'Aggiorna ora', updateLater: 'Non ora',\n lan: 'Rete locale', remote: 'Remoto', lanAccess: 'Accesso rete locale', remoteAccess: 'Accesso remoto', browserAccess: 'Accesso browser', remoteAddress: 'Indirizzo remoto', loadingStatus: 'Lettura stato…', loadingRemoteStatus: 'Lettura stato remoto…',\n generateCopyKey: 'Genera e copia chiave', copyPairLink: 'Copia link di abbinamento', managePairedDevices: 'Gestisci dispositivi abbinati', clearAllDevices: 'Rimuovi tutti i dispositivi', pairingQr: 'Codice QR di abbinamento', remoteIntro: 'Scegli il canale remoto più adatto. Cambiare o disattivare l’accesso remoto non influisce sulla rete locale.', httpFrameWarning: 'Le pagine HTTP possono essere incorporate per compatibilità, ma i contenuti non sono cifrati e possono essere alterati. Usa HTTPS per le attività sensibili.', chooseProvider: 'Scegli metodo di connessione', currentProvider: 'Connessione attuale', remoteStateOff: 'Disattivata', remoteStateConnecting: 'Connessione', remoteStateAttention: 'Da controllare', providerInfoAria: 'Informazioni su sicurezza e rete della connessione remota', providerGroupAria: 'Metodo di connessione remota', providerSafeTitle: 'La protezione resta attiva', providerSafeText: 'Solo i dispositivi abbinati possono accedere a DSH. cpolar viene installato su richiesta e può essere rimosso completamente; Tailscale può essere lento o non disponibile nelle reti della Cina continentale, dove è consigliato cpolar.',\n builtIn: 'Integrato', mainlandPreferred: 'Preferito in Cina continentale', tailscaleDescription: 'Copertura più ampia; le reti della Cina continentale possono essere instabili. Il primo collegamento richiede accesso e autorizzazione Funnel.', cpolarDescription: 'Installa su richiesta il componente ufficiale, adatto alle reti della Cina continentale.', prepareCpolar: 'Prepara cpolar', checkingComponent: 'Controllo componente…', installOfficial: 'Installa componente ufficiale', cpolarAccountNote: 'Accedi al sito cpolar e copia l’Authtoken. Il token resta solo nella directory privata del plugin e non viene mostrato nella pagina o nei log.', registerCpolar: 'Registrati su cpolar', openDashboard: 'Apri la dashboard per il token', tokenPlaceholder: 'Incolla Authtoken cpolar', saveConnect: 'Salva e connetti', saving: 'Salvataggio…', componentDetails: 'Origine e rimozione del componente', componentDetailsText: 'Viene scaricato dal sito ufficiale cpolar e verificato a una versione fissa solo dopo aver scelto Installa. Non aggiunge servizi di sistema, avvio automatico, voci di registro o PATH.', pluginPrivateDirectory: 'Directory privata del plugin', officialDownload: 'Pagina download ufficiale', terms: 'Termini di servizio', purgeCpolar: 'Rimuovi completamente componente e configurazione cpolar', tailscaleHelp: 'Guida a Tailscale', tailscaleHelpText: 'Il componente runtime è incluso nel plugin. Il primo collegamento apre le pagine ufficiali di accesso Tailscale e autorizzazione Funnel; il plugin non accede mai alla password.', funnelGuideAria: 'Passaggi per abilitare Tailscale Funnel', funnelGuideTitle: 'Configurazione accesso remoto · Passaggio 2', funnelGuideSummary: 'L’accesso Tailscale è completato. Devi ancora consentire Funnel per questo computer; la pagina ufficiale abilita anche HTTPS.', funnelStep1: 'Apri la pagina ufficiale di autorizzazione Tailscale per questo nodo.', funnelStep2: 'Conferma Funnel; non serve accedere di nuovo a DSH.', funnelStep3: 'Torna in DSH; il plugin controlla e si connette automaticamente.', funnelGuideNote: 'Richiede un account Owner, Admin o Network admin.', continueFunnel: 'Continua autorizzazione Funnel', retryNow: 'Fatto, riprova ora', enableRemote: 'Attiva accesso remoto', disableRemote: 'Disattiva accesso remoto', continueLogin: 'Continua accesso', reconnect: 'Riconnetti', generateRemoteQr: 'Genera QR di abbinamento remoto', copyRemoteLink: 'Copia link di abbinamento remoto', manageRemoteDevices: 'Gestisci dispositivi remoti', resetRemoteLogin: 'Esci e rimuovi accesso remoto', resetRemoteDevices: 'Disattiva e rimuovi dispositivi remoti',\n lanOn: 'Accesso dalla rete locale attivo.', lanOff: 'Accesso dalla rete locale disattivato.', enableLan: 'Attiva accesso rete locale', disableLan: 'Disattiva accesso rete locale', extensionsLoaded: 'Estensioni: {loaded} caricate', extensionsFailed: 'Estensioni: {loaded} caricate, {failed} non riuscite', keyGenerationFailed: 'Impossibile generare la chiave di abbinamento.', keyCopied: 'Chiave di abbinamento copiata. Incollala nell’app Android.', linkCopied: 'Link di abbinamento copiato. Invialo al telefono e incollalo nell’app oppure aprilo nel browser.', copySecret: 'Copia {kind}: {value}', pairingKey: 'chiave di abbinamento', pairingLink: 'link di abbinamento', noDevices: 'Nessun dispositivo abbinato.', noRemoteDevices: 'Nessun dispositivo remoto abbinato.', device: 'Dispositivo', expires: 'Scade {time}', revoke: 'Revoca', confirmResetDevices: 'Rimuovere tutti i dispositivi abbinati? I dispositivi connessi verranno disconnessi subito.', ready: 'Pronto', installed: 'Installato', installWithSize: 'Installa componente ufficiale · {size} MB', cpolarUnsupported: 'Attualmente sono supportati Windows x64 e Linux x64/arm64.', cpolarNotInstalled: 'Non installato. Una versione fissa viene scaricata dal sito ufficiale cpolar solo dopo aver premuto il pulsante.', cpolarNeedsToken: 'Componente ufficiale {version} verificato. Salva il token account per continuare.', cpolarReady: 'Componente ufficiale {version} e configurazione account locale pronti. Gli indirizzi temporanei gratuiti possono cambiare dopo il riavvio di DSH o cpolar; se l’app non si connette, scansiona di nuovo il QR remoto corrente.',\n remoteOff: 'Accesso remoto disattivato. La rete locale non è interessata.', remoteUnavailableCpolar: 'cpolar non è installato o l’account locale non è configurato.', remoteUnavailableTailscale: 'Su questo computer manca il componente runtime Funnel. Reinstalla il pacchetto completo del plugin.', remoteStartingCpolar: 'Connessione a un nodo cpolar…', remoteStartingTailscale: 'Avvio del canale sicuro Tailscale…', remoteNeedsLogin: 'Completa una volta l’accesso Tailscale nel browser. Il plugin non legge mai la password.', remoteConnectingCpolar: 'Indirizzo pubblico assegnato; avvio del gateway di autenticazione DSH…', remoteConnectingTailscale: 'Accesso completato; creazione dell’indirizzo HTTPS pubblico…', remoteReady: 'Accesso remoto pronto. Solo i dispositivi abbinati possono accedere a DSH.', remoteError: 'Connessione remota non stabilita. Riconnettiti; la rete locale continua a funzionare.', funnelPermission: 'Accesso completato. Autorizza Funnel per stabilire automaticamente la connessione remota.', funnelHttps: 'Accesso completato. Autorizza Funnel; la pagina ufficiale abilita anche HTTPS.', funnelStart: 'Accesso completato. Termina la prima autorizzazione Tailscale Funnel.', tailscaleDnsMissing: 'Tailscale non ha fornito un indirizzo remoto. Riconnettiti e verifica di aver completato l’accesso.', gatewayStartFailed: 'Avvio del gateway remoto non riuscito. Riconnettiti; la rete locale non è interessata.', controlChannelFailed: 'Connessione al componente remoto interrotta. Riconnettiti.', cpolarMissing: 'Il componente ufficiale cpolar non è installato. Completa i passaggi sopra.', cpolarInvalid: 'Verifica del componente cpolar non riuscita. Rimuovilo completamente e reinstallalo.', cpolarConfigMissing: 'Nessun token account cpolar salvato. Completa i passaggi sopra.', cpolarConfigInvalid: 'Configurazione locale cpolar non valida. Salva nuovamente il token.', cpolarPortUnavailable: 'Impossibile assegnare una porta locale al gateway remoto. Riprova.', cpolarLaunchFailed: 'Avvio del client cpolar non riuscito.', cpolarTimeout: 'Connessione al nodo cpolar scaduta. Riconnettiti.', cpolarStopped: 'Connessione cpolar arrestata.', cpolarExited: 'Connessione cpolar terminata inaspettatamente. Riconnettiti.', cpolarOutputInvalid: 'cpolar ha restituito uno stato non riconosciuto.', cpolarOriginInvalid: 'L’indirizzo pubblico restituito da cpolar non ha superato la verifica.', setupOpened: 'La pagina ufficiale Tailscale è aperta. Torna in DSH dopo aver abilitato Funnel; la riconnessione è automatica.',\n switchProviderConfirm: 'Il cambio di metodo disattiva prima il canale remoto corrente. Rete locale e dispositivi abbinati non sono interessati. Continuare?', switchingCpolar: 'Passaggio a cpolar…', switchingTailscale: 'Passaggio a Tailscale Funnel…', installConfirm: 'Scaricare e verificare una versione fissa dal sito ufficiale cpolar (circa 7,3 MB), estraendola solo nella directory privata DSH Mobile? Non vengono aggiunti servizi, PATH/registro o avvio automatico.', downloading: 'Download e verifica…', installingCpolar: 'Installazione del componente ufficiale cpolar. Mantieni DSH in esecuzione fino al termine.', installFailed: 'Installazione componente non riuscita: {error}', invalidToken: 'Incolla l’Authtoken completo dalla dashboard cpolar.', configuredConnecting: 'Configurazione account salvata; connessione tramite cpolar…', configureFailed: 'Configurazione non riuscita: {error}', purgeConfirm: 'Rimuovere completamente componente cpolar, configurazione token e log runtime dalla directory privata DSH Mobile? Rete locale, dati DSH e altri programmi non sono interessati.', purging: 'Arresto del canale e rimozione dei file cpolar gestiti da DSH Mobile…', purgeFailed: 'Pulizia non riuscita: {error}', reconnectingCpolar: 'Riconnessione a un nodo cpolar…', reconnectingTailscale: 'Controllo impostazioni Tailscale e riconnessione…', remoteQrReady: 'QR di abbinamento remoto generato. Scansionalo da “Accesso remoto” nell’app.', resetCpolarConfirm: 'Disattivare il canale remoto cpolar e rimuovere tutti i dispositivi remoti? Account cpolar e altri tunnel restano invariati.', resetTailscaleConfirm: 'Uscire da Tailscale su questo computer e rimuovere tutti i dispositivi remoti? La configurazione della rete locale resta invariata.', requestFailed: 'Richiesta non riuscita: {error}', wsPathsTitle: 'Percorsi WebSocket di terze parti', wsPathsIntro: 'Quando un plugin installato non riesce a connettersi (per esempio un terminale laterale o un pannello live), di solito è perché i suoi percorsi di connessione non sono ancora stati approvati. Tutto ciò che richiede attenzione è elencato qui sotto: un clic su “Consenti” risolve.', wsPathsPlaceholder: '/sidebar/ws/terminal', wsPathsAdd: 'Consenti percorso', wsPathsRemove: 'Rimuovi', wsPathsEmpty: 'Non serve configurare nulla per ora. Se in seguito un plugin non riesce a connettersi, qui compariranno opzioni con un clic.', wsPathsInvalid: 'Inserisci un percorso esatto che inizi con / (senza query).', wsPathsDetected: 'Una connessione di un plugin è stata bloccata — premi Consenti per lasciarla passare', wsPathsAllow: 'Consenti', wsPathsAllowAll: 'Consenti tutti', wsPathsGroupMeta: '{n} percorsi · bloccati {m} volte', wsPathsNewBlocked: 'Nuove connessioni di terze parti bloccate — apri la diagnostica', wsPathsAdvancedToggle: 'Aggiungi percorsi manualmente (avanzato)', wsPathsAnnounce: '{n} connessioni di plugin in attesa di approvazione', wsPathsAdvancedHint: 'Le voci manuali servono raramente: usa i pulsanti qui sopra. Compila solo se conosci il percorso esatto.'\n },\n zh: {} as Record<string, string>,\n} satisfies Record<MobileControlLocale, Record<string, string>>\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n mobileAccess: '移动访问', collapseMobileAccess: '收起移动访问', downloadAndroid: '下载最新 Android App', downloadAndroidVersion: '下载最新 Android App · v{version}', downloadAndroidAria: '从 GitHub Releases 下载最新版 Android App', downloadAndroidVersionAria: '从 GitHub Releases 下载 Android App v{version}', updatePlugin: '更新插件', updatePluginAria: '将 DSH Mobile 更新至 v{version}', updatingPlugin: '正在更新…', pluginUpdatedRestart: '插件已更新至 v{version},重启 DSH 后生效。', pluginUpdateFailed: '更新失败:{error}', updateTo: '更新到 v{version}', updateNotice: '安装后需重启 DSH。现有 App 与已配对设备无需重新配对;电脑端版本不确定时,先对照 README 兼容表。', updateNotesEmpty: '未取到最新版更新说明,仍可继续更新;详情见 GitHub Release 页。', updateNow: '立即更新', updateLater: '暂不', lan: '局域网', remote: '远程', lanAccess: '局域网访问', remoteAccess: '远程访问', browserAccess: '浏览器访问', remoteAddress: '远程地址', loadingStatus: '正在读取状态…', loadingRemoteStatus: '正在读取远程状态…',\n generateCopyKey: '生成并复制密钥', copyPairLink: '复制配对链接', managePairedDevices: '管理配对设备', clearAllDevices: '清除所有设备', pairingQr: '配对二维码', remoteIntro: '选择更适合你的远程通道。切换或关闭远程访问不会影响局域网。', httpFrameWarning: '为兼容性允许嵌入 HTTP 页面,但内容未加密,可能被篡改;处理敏感内容请使用 HTTPS。', chooseProvider: '选择连接方式', currentProvider: '当前连接', remoteStateOff: '未启用', remoteStateConnecting: '连接中', remoteStateAttention: '需处理', providerInfoAria: '查看远程连接安全与网络说明', providerGroupAria: '远程连接方式', providerSafeTitle: '你始终可以放心', providerSafeText: '只有已配对设备能进入 DSH。cpolar 按需安装并可彻底清理;Tailscale 在中国大陆网络下可能连接缓慢、中断或无法使用,国内网络建议优先尝试 cpolar。', builtIn: '内置', mainlandPreferred: '国内网络优先', tailscaleDescription: '覆盖更广;中国大陆网络可能不稳定,首次需登录并允许 Funnel。', cpolarDescription: '按需安装官方组件,适合国内网络环境。',\n prepareCpolar: '准备 cpolar', checkingComponent: '正在检查组件…', installOfficial: '安装官方组件', cpolarAccountNote: '登录 cpolar 官网后复制 Authtoken。令牌只保存在本机插件私有目录,不会显示在页面或日志中。', registerCpolar: '注册 cpolar', openDashboard: '打开控制台获取令牌', tokenPlaceholder: '粘贴 cpolar Authtoken', saveConnect: '保存并连接', saving: '正在保存…', componentDetails: '组件来源与清理说明', componentDetailsText: '仅在你点击安装后从 cpolar 官网下载并校验固定版本。不会写入系统服务、开机启动、注册表或 PATH。', pluginPrivateDirectory: '插件私有目录', officialDownload: '官方下载安装页', terms: '服务条款', purgeCpolar: '彻底移除 cpolar 组件与配置', tailscaleHelp: 'Tailscale 使用说明', tailscaleHelpText: '运行组件已随插件提供。首次连接会打开 Tailscale 官方登录和 Funnel 授权页;插件不会接触你的账号密码。', funnelGuideAria: 'Tailscale Funnel 启用步骤', funnelGuideTitle: '远程访问设置 · 第 2 步', funnelGuideSummary: 'Tailscale 登录已完成。还需为这台电脑允许 Funnel,官方页面会同时启用 HTTPS。', funnelStep1: '打开当前节点的 Tailscale 官方授权页。', funnelStep2: '确认启用 Funnel;无需再次登录 DSH。', funnelStep3: '返回 DSH,插件会自动检查并建立连接。', funnelGuideNote: '需要使用 Owner、Admin 或 Network admin 账号。', continueFunnel: '继续完成 Funnel 授权', retryNow: '已完成,立即重试',\n enableRemote: '启用远程访问', disableRemote: '关闭远程访问', continueLogin: '继续登录', reconnect: '重新连接', generateRemoteQr: '生成远程配对二维码', copyRemoteLink: '复制远程配对链接', manageRemoteDevices: '管理远程设备', resetRemoteLogin: '退出并清除远程登录', resetRemoteDevices: '关闭并清除远程设备', lanOn: '局域网访问已开启。', lanOff: '局域网访问已关闭。', enableLan: '开启局域网访问', disableLan: '关闭局域网访问', extensionsLoaded: '扩展:{loaded} 个已加载', extensionsFailed: '扩展:{loaded} 个已加载,{failed} 个加载失败', keyGenerationFailed: '无法生成配对密钥。', keyCopied: '配对密钥已复制,请粘贴到 Android App。', linkCopied: '配对链接已复制,发给手机后 App 粘贴或浏览器打开即可配对。', copySecret: '请复制{kind}:{value}', pairingKey: '配对密钥', pairingLink: '配对链接', noDevices: '暂无配对设备。', noRemoteDevices: '暂无远程配对设备。', device: '设备', expires: '到期 {time}', revoke: '撤销', confirmResetDevices: '确定要移除所有配对设备吗?此操作会立即终止已连接设备。', ready: '已就绪', installed: '已安装', installWithSize: '安装官方组件 · {size} MB',\n cpolarUnsupported: '当前支持 Windows x64 与 Linux x64/arm64。', cpolarNotInstalled: '尚未安装。只有点击下方按钮后,才会从 cpolar 官网下载固定版本。', cpolarNeedsToken: '官方组件 {version} 已校验,下一步只需保存账号令牌。', cpolarReady: '官方组件 {version} 与本机账号配置已就绪。免费临时地址可能在 DSH 或 cpolar 重启后变化;App 无法连接时,请重新扫描当前远程二维码。', remoteOff: '远程访问未启用。局域网访问不受影响。', remoteUnavailableCpolar: 'cpolar 尚未安装或未完成本机账号配置。', remoteUnavailableTailscale: '当前电脑缺少 Funnel 运行组件,请重新安装完整插件包。', remoteStartingCpolar: '正在连接 cpolar 国内节点…', remoteStartingTailscale: '正在启动 Tailscale 安全通道…', remoteNeedsLogin: '需要在浏览器完成一次 Tailscale 登录。插件不会读取你的密码。', remoteConnectingCpolar: '公网地址已分配,正在启动 DSH 认证网关…', remoteConnectingTailscale: '登录完成,正在建立公开 HTTPS 地址…', remoteReady: '远程访问已就绪。只有已配对设备可以进入 DSH。', remoteError: '远程连接未建立。可重新连接,局域网访问仍可正常使用。',\n funnelPermission: '登录已完成。请继续授权 Funnel,完成后会自动建立远程连接。', funnelHttps: '登录已完成。请继续授权 Funnel,官方页面会同时启用 HTTPS。', funnelStart: '登录已完成。请继续完成 Tailscale Funnel 的首次授权。', tailscaleDnsMissing: 'Tailscale 暂未提供远程地址。请重新连接并确认已完成登录。', gatewayStartFailed: '远程网关启动失败。请重新连接,局域网访问不受影响。', controlChannelFailed: '远程组件连接中断。请重新连接。', cpolarMissing: 'cpolar 官方组件尚未安装。请先完成上方准备步骤。', cpolarInvalid: 'cpolar 组件校验失败。请彻底移除后重新安装。', cpolarConfigMissing: 'cpolar 尚未保存账号令牌。请先完成上方准备步骤。', cpolarConfigInvalid: 'cpolar 本机配置无效。请重新保存账号令牌。', cpolarPortUnavailable: '无法分配本机远程网关端口,请重试。', cpolarLaunchFailed: 'cpolar 客户端未能启动。', cpolarTimeout: '连接 cpolar 国内节点超时,请重新连接。', cpolarStopped: 'cpolar 连接已停止。', cpolarExited: 'cpolar 连接意外退出,请重新连接。', cpolarOutputInvalid: 'cpolar 返回了无法识别的状态。', cpolarOriginInvalid: 'cpolar 返回的公网地址未通过校验。', setupOpened: 'Tailscale 官方页面已打开。完成启用后返回 DSH,这里会自动重新连接。',\n switchProviderConfirm: '切换连接方式会先关闭当前远程通道。局域网和配对设备不会受影响,是否继续?', switchingCpolar: '正在切换到 cpolar…', switchingTailscale: '正在切换到 Tailscale Funnel…', installConfirm: '将从 cpolar 官方网站下载并校验固定版本(约 7.3 MB),仅解压到 DSH Mobile 私有目录。不会安装系统服务、写入 PATH/注册表或设置开机启动。是否继续?', downloading: '正在下载并校验…', installingCpolar: '正在安装 cpolar 官方组件。完成前请保持 DSH 运行。', installFailed: '组件安装失败:{error}', invalidToken: '请粘贴 cpolar 控制台提供的完整 Authtoken。', configuredConnecting: '账号配置已保存,正在建立 cpolar 远程通道…', configureFailed: '配置失败:{error}', purgeConfirm: '彻底移除 DSH Mobile 私有目录中的 cpolar 组件、令牌配置和运行日志?不会影响局域网、DSH 数据或系统中的其他程序。', purging: '正在关闭通道并清理 DSH Mobile 管理的 cpolar 文件…', purgeFailed: '清理失败:{error}', reconnectingCpolar: '正在重新连接 cpolar 国内节点…', reconnectingTailscale: '正在确认 Tailscale 设置并重新连接…', remoteQrReady: '远程配对二维码已生成。请在 App 的“远程访问”中扫描。', resetCpolarConfirm: '关闭 cpolar 远程通道并移除所有远程配对设备?不会修改你的 cpolar 账号或其他隧道。', resetTailscaleConfirm: '退出电脑上的 Tailscale 登录并移除所有远程配对设备?局域网配置不会改变。', requestFailed: '请求失败:{error}', wsPathsTitle: '第三方 WebSocket 路径', wsPathsIntro: '插件(如侧边栏终端、实时面板)连不上时,通常是它的连接路径还没放行。下面列出需要处理的项,点「允许」即可。', wsPathsPlaceholder: '/sidebar/ws/terminal', wsPathsAdd: '允许该路径', wsPathsRemove: '移除', wsPathsEmpty: '目前无需任何设置。以后有插件连不上,会在这里出现可一键放行的选项。', wsPathsInvalid: '请输入以 / 开头的确切路径(不带 query)。', wsPathsDetected: '有插件连接被拦截——点「允许」放行', wsPathsAllow: '允许', wsPathsAllowAll: '全部允许', wsPathsGroupMeta: '{n} 条路径 · 共拦截 {m} 次', wsPathsNewBlocked: '发现被拦截的第三方连接,去诊断页查看', wsPathsAdvancedToggle: '手动添加路径(高级)', wsPathsAnnounce: '{n} 条插件连接等待放行', wsPathsAdvancedHint: '通常不需要手动填写:用上方按钮即可。只有当你确切知道所需路径时才填。'\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n lanSetupTitle: 'Finish local network setup', lanSetupIntro: 'Choose the network shared by this computer and your phone. DSH Mobile creates private certificates only after you confirm.', lanSetupNetwork: 'Computer network', lanSetupChooseNetwork: 'Choose a network', lanSetupRecommended: 'Recommended', lanSetupNoNetworks: 'No private Wi-Fi or Ethernet address was found. Connect this computer to the local network, then scan again.', lanSetupRefresh: 'Scan again', lanSetupFirewallNote: 'Windows may request administrator approval for two inbound rules limited to the local subnet.', lanSetupConfigure: 'Configure local network', lanSetupConfiguring: 'Creating private certificates and local network rules…', lanSetupRequired: 'Local network setup has not been completed. Choose a network above to continue.', lanSetupRestart: 'Setup complete for {origin}. Restart DSH once to start local network access.', lanSetupFailed: 'Setup was not completed. Confirm the network selection and approve the Windows prompt, then try again.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n lanSetupTitle: 'Completa la configurazione della rete locale', lanSetupIntro: 'Scegli la rete condivisa da questo computer e dal telefono. DSH Mobile crea certificati privati solo dopo la conferma.', lanSetupNetwork: 'Rete del computer', lanSetupChooseNetwork: 'Scegli una rete', lanSetupRecommended: 'Consigliata', lanSetupNoNetworks: 'Nessun indirizzo Wi-Fi o Ethernet privato trovato. Collega il computer alla rete locale e ripeti la ricerca.', lanSetupRefresh: 'Cerca di nuovo', lanSetupFirewallNote: 'Windows può richiedere l’approvazione amministratore per due regole in ingresso limitate alla sottorete locale.', lanSetupConfigure: 'Configura rete locale', lanSetupConfiguring: 'Creazione dei certificati privati e delle regole di rete locale…', lanSetupRequired: 'La configurazione della rete locale non è completa. Scegli una rete qui sopra per continuare.', lanSetupRestart: 'Configurazione completata per {origin}. Riavvia DSH una volta per attivare l’accesso locale.', lanSetupFailed: 'Configurazione non completata. Controlla la rete e approva la richiesta di Windows, quindi riprova.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n lanSetupTitle: '完成局域网配置', lanSetupIntro: '选择电脑与手机共同使用的网络。确认后才会生成私有证书,不会在安装时自动开放端口。', lanSetupNetwork: '电脑当前网络', lanSetupChooseNetwork: '请选择网络', lanSetupRecommended: '推荐', lanSetupNoNetworks: '未找到可用的私有 Wi-Fi 或以太网地址。请先让电脑接入局域网,然后重新扫描。', lanSetupRefresh: '重新扫描', lanSetupFirewallNote: 'Windows 可能请求管理员授权,仅创建两条限本地子网访问的入站规则。', lanSetupConfigure: '完成配置', lanSetupConfiguring: '正在生成私有证书并配置局域网规则…', lanSetupRequired: '尚未完成局域网配置,请选择上方网络继续。', lanSetupRestart: '已为 {origin} 完成配置。请重启一次 DSH,局域网访问会自动开启。', lanSetupFailed: '配置未完成。请确认网卡选择并允许 Windows 授权提示,然后重试。',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n selfHostedConnections: 'Self-hosted connection', advanced: 'Advanced', frpName: 'Self-hosted FRP', frpDescription: 'A restricted FRP channel for this DSH gateway only. You manage the VPS and public address.',\n prepareFrp: 'Configure self-hosted FRP', frpStep1Title: '1 · Connection details', frpStep1Text: 'Enter the VPS address, frps control port, shared token, and public HTTPS origin.', frpServerAddress: 'VPS address', frpServerAddressPlaceholder: 'frp.example.com', frpServerPort: 'frps port', frpToken: 'Shared token', frpTokenPlaceholder: 'At least 16 characters', frpPublicOrigin: 'Public HTTPS origin', frpPublicOriginPlaceholder: 'https://dsh.example.com',\n frpStep2Title: '2 · Prepare the VPS', frpStep2Text: 'Copy the restricted frps and Caddy template, then apply it on your VPS.', copyServerTemplate: 'Copy server template', templateCopied: 'Server template copied. Save frps.toml and the Caddy snippet on the VPS, then add the import line to the Caddyfile.', templateCopyFailed: 'Could not copy the template. Check the four fields above.', vpsDeployText: 'Use an existing OpenSSH key or agent. The VPS must be Ubuntu/Debian with systemd; existing Caddy site configuration is never overwritten.', vpsDeployChangesTitle: 'Automatic deployment will change the following on the VPS (manual template changes nothing by itself):', vpsDeployChangePackages: 'Install Caddy from its official APT repository, plus a Python venv and Certbot for public-IPv4 certificates.', vpsDeployChangeServices: 'Create the dsh-mobile system user (only if missing, otherwise kept), the frps config and service, the Caddy snippet plus one import line, and a daily certificate-renewal timer.', vpsDeployChangeFirewall: 'Open the FRP control port, 80/tcp, and 443/tcp in UFW when it is active; other firewalls are left for you to adjust.', vpsDeployChangeManual: 'Manual deployment applies the same steps by hand from the copied template; automatic deployment runs them over SSH after you confirm the host keys.', vpsSshUser: 'SSH user', vpsSshPort: 'SSH port', vpsSshKey: 'Private key path (optional)', vpsSshKeyPlaceholder: 'Uses ssh-agent or ~/.ssh/config when empty', vpsDeploy: 'Deploy frps + Caddy', vpsDeploying: 'Deploying to the VPS. Keep DSH running…', vpsDeploySuccess: 'VPS deployment completed. Start FRP connection to verify the public endpoint.', vpsDeployFailed: 'VPS deployment failed: {error}', vpsDeployNotReady: 'Save the FRP settings before deploying the VPS.', vpsHostKeyFetching: 'Reading the VPS host keys…', vpsHostKeyFailed: 'Could not read the VPS host keys: {error}', vpsDeployConfirmWithKeys: 'Deploy DSH Mobile frps and Caddy to this VPS? Only DSH Mobile service/config files and firewall rules will be created. Existing non-empty Caddy configuration will stop the deployment.\\n\\nThe server presents these host keys. Confirm they match the VPS console before continuing:\\n{fingerprints}', vpsCopyUninstall: 'Copy VPS uninstall script', vpsUninstallScriptCopied: 'Uninstall script copied. Review it before running with root on the VPS.', vpsUninstallScriptFailed: 'Could not build the uninstall script: {error}', vpsUninstall: 'Remove DSH Mobile from the VPS', vpsUninstallConfirmWithKeys: 'Remove all DSH Mobile services, configs, certificates, and owned firewall rules from this VPS? Your own Caddy content and unrelated rules are kept.\\n\\nThe server presents these host keys. Confirm they match the VPS console before continuing:\\n{fingerprints}', vpsUninstalling: 'Removing DSH Mobile from the VPS. Keep DSH running…', vpsUninstallSuccess: 'VPS cleanup completed. The local FRP settings are unchanged.', vpsUninstallFailed: 'VPS cleanup failed: {error}',\n frpStep3Title: '3 · Prepare this computer', frpStep3Text: 'Download and verify the pinned official frpc client only when requested.', installFrpc: 'Install official frpc', 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.', installingFrp: 'Installing the official frpc component. Keep DSH running until it completes.', frpNotInstalled: 'frpc is not installed. Nothing is downloaded until you click Install.', frpUnsupported: 'This desktop platform is not supported by the managed frpc installer.', frpComponentReady: 'Official frpc {version} is verified.',\n frpStep4Title: '4 · Verify and connect', frpStep4Text: 'The plugin starts only its DSH HTTP vhost and verifies the public endpoint belongs to this computer.', frpAppRequirement: 'Custom remote domains require Android app 0.3.3 or later; local network, cpolar, and Tailscale remain compatible with older supported apps.', frpSaveConnect: 'Save and verify connection', frpSavingConnecting: 'Saving the private settings and verifying the complete FRP path…', frpConfigurationReady: 'Server settings are saved. The token remains only in the plugin private directory.', frpConfigurationMissing: 'Complete all fields, copy the VPS template, then save and connect.', frpInputInvalid: 'Check the VPS address, port, HTTPS origin, and token.',\n frpComponentDetails: 'FRP source and complete cleanup', frpComponentDetailsText: 'The pinned client is downloaded from the official fatedier/frp release. DSH Mobile never installs a service or accepts arbitrary FRP configuration.', frpOfficialRelease: 'Official FRP release', purgeFrp: 'Remove FRP component and private configuration', purgeFrpConfirm: 'Stop FRP and remove frpc, the shared token, generated configuration, staging files, and logs owned by DSH Mobile? The VPS is unchanged.', purgingFrp: 'Stopping FRP and removing files managed by DSH Mobile…',\n switchingFrp: 'Switching to self-hosted FRP…', reconnectingFrp: 'Verifying and reconnecting self-hosted FRP…', resetFrpConfirm: 'Disable self-hosted FRP and remove all remote paired devices? The saved VPS settings and server are unchanged.', remoteUnavailableFrp: 'frpc is not installed or the private server settings are incomplete.', remoteStartingFrp: 'Checking the VPS and preparing the restricted FRP channel…', remoteConnectingFrp: 'frpc is running; verifying the public HTTPS endpoint…', frpMissing: 'Install the official frpc component first.', frpInvalid: 'frpc verification failed. Remove it completely and reinstall.', frpConfigMissing: 'Save the self-hosted FRP connection details first.', frpConfigVerifyFailed: 'frpc rejected the generated configuration. Check the server details and token.', frpVhostPublic: 'The VPS plaintext HTTP vhost is publicly reachable. Bind it to 127.0.0.1 and let Caddy provide HTTPS.', frpVhostProbeFailed: 'The VPS address could not be checked. Confirm DNS and retry.', frpLaunchFailed: 'frpc failed to start.', frpTimeout: 'The public endpoint did not become ready. Check frps, Caddy, DNS, and firewall.', frpDiscoveryMismatch: 'The public domain points to another DSH computer. Check the Caddy and frps mappings.', frpDiscoveryInvalid: 'The public domain did not return a valid DSH Mobile discovery response.', frpStopped: 'The FRP connection stopped.', frpExited: 'frpc exited unexpectedly. Check the VPS configuration and reconnect.',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n selfHostedConnections: 'Connessione autogestita', advanced: 'Avanzato', frpName: 'FRP autogestito', frpDescription: 'Canale FRP limitato al solo gateway DSH. VPS e indirizzo pubblico restano sotto il tuo controllo.',\n prepareFrp: 'Configura FRP autogestito', frpStep1Title: '1 · Dati di connessione', frpStep1Text: 'Inserisci indirizzo VPS, porta di controllo frps, token condiviso e origine HTTPS pubblica.', frpServerAddress: 'Indirizzo VPS', frpServerAddressPlaceholder: 'frp.example.com', frpServerPort: 'Porta frps', frpToken: 'Token condiviso', frpTokenPlaceholder: 'Almeno 16 caratteri', frpPublicOrigin: 'Origine HTTPS pubblica', frpPublicOriginPlaceholder: 'https://dsh.example.com',\n frpStep2Title: '2 · Prepara il VPS', frpStep2Text: 'Copia il modello limitato per frps e Caddy e applicalo sul VPS.', copyServerTemplate: 'Copia modello server', templateCopied: 'Modello server copiato. Salva frps.toml e lo snippet Caddy sul VPS, poi aggiungi la riga import al Caddyfile.', templateCopyFailed: 'Impossibile copiare il modello. Controlla i quattro campi sopra.', vpsDeployText: 'Usa una chiave OpenSSH o un agent esistente. Il VPS deve essere Ubuntu/Debian con systemd; una configurazione Caddy non vuota non viene sovrascritta.', vpsDeployChangesTitle: 'Il deploy automatico modifica quanto segue sul VPS (il modello manuale da solo non cambia nulla):', vpsDeployChangePackages: 'Installa Caddy dal repository APT ufficiale, più un venv Python e Certbot per i certificati IPv4 pubblici.', vpsDeployChangeServices: 'Crea l’utente di sistema dsh-mobile (solo se assente, altrimenti mantenuto), la configurazione e il servizio frps, lo snippet Caddy più una riga import, e un timer giornaliero di rinnovo certificati.', vpsDeployChangeFirewall: 'Apre la porta di controllo FRP, 80/tcp e 443/tcp in UFW quando attivo; gli altri firewall restano a tuo carico.', vpsDeployChangeManual: 'Il deploy manuale applica gli stessi passi a mano dal modello copiato; quello automatico li esegue via SSH dopo la conferma delle chiavi host.', vpsSshUser: 'Utente SSH', vpsSshPort: 'Porta SSH', vpsSshKey: 'Percorso chiave privata (opzionale)', vpsSshKeyPlaceholder: 'Usa ssh-agent o ~/.ssh/config se vuoto', vpsDeploy: 'Installa frps + Caddy', vpsDeploying: 'Deploy sul VPS in corso. Mantieni DSH attivo…', vpsDeploySuccess: 'Deploy VPS completato. Avvia FRP per verificare l’endpoint pubblico.', vpsDeployFailed: 'Deploy VPS fallito: {error}', vpsDeployNotReady: 'Salva le impostazioni FRP prima del deploy VPS.', vpsHostKeyFetching: 'Lettura delle chiavi host del VPS…', vpsHostKeyFailed: 'Impossibile leggere le chiavi host del VPS: {error}', vpsDeployConfirmWithKeys: 'Installare frps e Caddy DSH Mobile su questo VPS? Verranno creati solo file di servizio/configurazione e regole firewall DSH Mobile. Una configurazione Caddy esistente non vuota interromperà il deploy.\\n\\nIl server presenta queste chiavi host. Verifica che corrispondano alla console del VPS prima di continuare:\\n{fingerprints}', vpsCopyUninstall: 'Copia script di disinstallazione VPS', vpsUninstallScriptCopied: 'Script di disinstallazione copiato. Controllalo prima di eseguirlo come root sul VPS.', vpsUninstallScriptFailed: 'Impossibile creare lo script di disinstallazione: {error}', vpsUninstall: 'Rimuovi DSH Mobile dal VPS', vpsUninstallConfirmWithKeys: 'Rimuovere tutti i servizi, le configurazioni, i certificati e le regole firewall di DSH Mobile da questo VPS? I tuoi contenuti Caddy e le altre regole restano invariati.\\n\\nIl server presenta queste chiavi host. Verifica che corrispondano alla console del VPS prima di continuare:\\n{fingerprints}', vpsUninstalling: 'Rimozione di DSH Mobile dal VPS. Mantieni DSH attivo…', vpsUninstallSuccess: 'Pulizia VPS completata. Le impostazioni FRP locali restano invariate.', vpsUninstallFailed: 'Pulizia VPS fallita: {error}',\n frpStep3Title: '3 · Prepara questo computer', frpStep3Text: 'Scarica e verifica il client frpc ufficiale fissato solo su richiesta.', installFrpc: 'Installa frpc ufficiale', 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.', installingFrp: 'Installazione del componente frpc ufficiale. Mantieni DSH in esecuzione.', frpNotInstalled: 'frpc non è installato. Non viene scaricato nulla finché non scegli Installa.', frpUnsupported: 'Questa piattaforma desktop non è supportata dal programma di installazione gestito.', frpComponentReady: 'frpc ufficiale {version} verificato.',\n frpStep4Title: '4 · Verifica e connetti', frpStep4Text: 'Il plugin avvia solo il vhost HTTP DSH e verifica che l’endpoint pubblico appartenga a questo computer.', 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.', frpSaveConnect: 'Salva e verifica connessione', frpSavingConnecting: 'Salvataggio privato e verifica del percorso FRP completo…', frpConfigurationReady: 'Impostazioni server salvate. Il token resta solo nella directory privata del plugin.', frpConfigurationMissing: 'Completa i campi, copia il modello VPS, quindi salva e connetti.', frpInputInvalid: 'Controlla indirizzo VPS, porta, origine HTTPS e token.',\n frpComponentDetails: 'Origine FRP e rimozione completa', frpComponentDetailsText: 'Il client fissato viene scaricato dalla release ufficiale fatedier/frp. DSH Mobile non installa servizi e non accetta configurazioni FRP arbitrarie.', frpOfficialRelease: 'Release FRP ufficiale', purgeFrp: 'Rimuovi componente FRP e configurazione privata', purgeFrpConfirm: 'Arrestare FRP e rimuovere frpc, token, configurazione, file temporanei e log gestiti da DSH Mobile? Il VPS non viene modificato.', purgingFrp: 'Arresto FRP e rimozione dei file gestiti da DSH Mobile…',\n switchingFrp: 'Passaggio a FRP autogestito…', reconnectingFrp: 'Verifica e riconnessione FRP autogestito…', resetFrpConfirm: 'Disattivare FRP autogestito e rimuovere tutti i dispositivi remoti? Impostazioni VPS e server restano invariati.', remoteUnavailableFrp: 'frpc non è installato o le impostazioni private sono incomplete.', remoteStartingFrp: 'Controllo VPS e preparazione del canale FRP limitato…', remoteConnectingFrp: 'frpc è in esecuzione; verifica dell’endpoint HTTPS pubblico…', frpMissing: 'Installa prima il componente frpc ufficiale.', frpInvalid: 'Verifica frpc non riuscita. Rimuovilo e reinstallalo.', frpConfigMissing: 'Salva prima i dati della connessione FRP.', frpConfigVerifyFailed: 'frpc ha rifiutato la configurazione generata. Controlla server e token.', frpVhostPublic: 'Il vhost HTTP in chiaro è raggiungibile pubblicamente. Limitalo a 127.0.0.1 e usa Caddy per HTTPS.', frpVhostProbeFailed: 'Impossibile controllare il VPS. Verifica il DNS e riprova.', frpLaunchFailed: 'Avvio di frpc non riuscito.', frpTimeout: 'Endpoint pubblico non pronto. Controlla frps, Caddy, DNS e firewall.', frpDiscoveryMismatch: 'Il dominio pubblico punta a un altro computer DSH. Controlla Caddy e frps.', frpDiscoveryInvalid: 'Il dominio pubblico non ha restituito una risposta DSH Mobile valida.', frpStopped: 'Connessione FRP arrestata.', frpExited: 'frpc è terminato inaspettatamente. Controlla il VPS e riconnettiti.',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n selfHostedConnections: '自建连接', advanced: '高级', frpName: '自建 FRP', frpDescription: '只为当前 DSH 网关建立单用途 FRP 通道,VPS 和公开入口由你掌控。',\n prepareFrp: '配置自建 FRP', frpStep1Title: '1 · 连接信息', frpStep1Text: '填写中转服务器地址、frps 控制端口、共享 Token 和手机访问地址。没有域名时,可直接使用公网 IPv4。', frpServerAddress: '中转服务器地址', frpServerAddressPlaceholder: '203.0.113.10', frpServerPort: 'frps 控制端口', frpToken: '共享 Token', frpTokenPlaceholder: '已保存时可留空;新建至少 16 个字符', frpPublicOrigin: '手机访问地址(HTTPS)', frpPublicOriginPlaceholder: 'https://203.0.113.10',\n frpStep2Title: '2 · 部署中转服务器', frpStep2Text: '推荐通过 SSH 一键部署。需要手动操作时,也可以复制受限的 frps 与 Caddy 配置模板。', copyServerTemplate: '复制手动部署模板', templateCopied: '服务器模板已复制,请在 VPS 上保存 frps.toml 和 Caddy 片段,并在 Caddyfile 里加一行 import。', templateCopyFailed: '无法复制模板,请检查上方连接信息。', vpsDeployText: '填写 SSH 登录信息。服务器需为带 systemd 的 Ubuntu/Debian;不会覆盖已有的 Caddy 站点配置。', vpsDeployChangesTitle: '自动部署会在 VPS 上做这些改动(只复制手动模板不会改变任何东西):', vpsDeployChangePackages: '从官方 APT 源安装 Caddy,并安装 Python venv 与 Certbot(公网 IP 证书用)。', vpsDeployChangeServices: '创建 dsh-mobile 系统用户(如已存在则复用,卸载时保留)、frps 配置与服务、Caddy 片段加一行 import,以及每天自动续证书的定时器。', vpsDeployChangeFirewall: 'UFW 开启时放行 FRP 控制端口、80/tcp 和 443/tcp;其他防火墙需要你自己放行。', vpsDeployChangeManual: '手动部署是照着复制的模板一步步自己操作;自动部署是在你确认主机指纹后通过 SSH 执行同样的步骤。', vpsSshUser: 'SSH 用户', vpsSshPort: 'SSH 端口', vpsSshKey: '私钥文件路径(可选)', vpsSshKeyPlaceholder: '留空则使用 ssh-agent 或 SSH 配置', vpsDeploy: '一键部署 frps + Caddy', vpsDeploying: '正在部署中转服务器,请保持 DSH 运行…', vpsDeploySuccess: '中转服务器部署完成,请继续完成本机组件安装和连接验证。', vpsDeployFailed: 'VPS 部署失败:{error}', vpsDeployNotReady: '请先填写完整的连接信息。', vpsHostKeyFetching: '正在读取中转服务器的主机指纹…', vpsHostKeyFailed: '读取中转服务器主机指纹失败:{error}', vpsDeployConfirmWithKeys: '要在这台 VPS 上部署 DSH Mobile 的 frps 和 Caddy 吗?只会创建 DSH Mobile 自己的服务、配置和防火墙规则;如果已有非空 Caddy 配置,部署会停止并保留原配置。\\n\\n服务器当前的主机指纹如下,请先到 VPS 控制台核对一致后再继续:\\n{fingerprints}', vpsCopyUninstall: '复制 VPS 卸载脚本', vpsUninstallScriptCopied: '卸载脚本已复制,请审阅后再以 root 身份在 VPS 上执行。', vpsUninstallScriptFailed: '生成卸载脚本失败:{error}', vpsUninstall: '清理 VPS 上的 DSH Mobile', vpsUninstallConfirmWithKeys: '要删除这台 VPS 上的 DSH Mobile 服务、配置、证书和专属防火墙规则吗?你自己的 Caddy 内容和其他规则会保留。\\n\\n服务器当前的主机指纹如下,请先到 VPS 控制台核对一致后再继续:\\n{fingerprints}', vpsUninstalling: '正在清理 VPS 上的 DSH Mobile,请保持 DSH 运行…', vpsUninstallSuccess: 'VPS 清理完成,本机 FRP 设置保持不变。', vpsUninstallFailed: 'VPS 清理失败:{error}',\n frpStep3Title: '3 · 安装本机组件', frpStep3Text: '下载并校验固定版本的官方 frpc,只安装到 DSH Mobile 私有目录。', installFrpc: '安装官方 frpc', frpInstallConfirm: '从 FRP 官方 Release 下载并校验 frpc 0.70.1(约 {size} MB),只提取 frpc 到 DSH Mobile 私有目录?不会安装服务、写入 PATH 或设置开机启动。', installingFrp: '正在安装官方 frpc,完成前请保持 DSH 运行。', frpNotInstalled: 'frpc 尚未安装;点击下方按钮后才会开始下载。', frpUnsupported: '当前桌面平台暂不支持托管安装 frpc。', frpComponentReady: '官方 frpc {version} 已安装并通过校验。',\n frpStep4Title: '4 · 启用并验证', frpStep4Text: '保存配置、启动受限的 FRP 通道,并确认手机访问地址确实连接到这台电脑。', frpAppRequirement: '通过自定义域名或公网 IP 连接,需要 Android App 0.3.3 或更高版本。局域网、cpolar 和 Tailscale 不受影响。', frpSaveConnect: '保存配置并验证连接', frpSavingConnecting: '正在保存私有配置并验证完整 FRP 链路…', frpConfigurationReady: '连接配置已安全保存', frpConfigurationMissing: '请按顺序完成连接信息、服务器部署和本机组件安装。', frpInputInvalid: '请检查服务器地址、控制端口、手机访问地址和共享 Token。',\n frpComponentDetails: 'FRP 来源与彻底清理', frpComponentDetailsText: '固定版本客户端来自 fatedier/frp 官方 Release。DSH Mobile 不安装系统服务,也不接受任意 FRP 配置。', frpOfficialRelease: 'FRP 官方 Release', purgeFrp: '彻底移除 FRP 组件与私有配置', purgeFrpConfirm: '关闭 FRP 并移除 DSH Mobile 管理的 frpc、共享 Token、生成配置、临时文件和日志?不会修改 VPS。', purgingFrp: '正在关闭 FRP 并清理 DSH Mobile 管理的文件…',\n switchingFrp: '正在切换到自建 FRP…', reconnectingFrp: '正在验证并重新连接自建 FRP…', resetFrpConfirm: '关闭自建 FRP 并移除所有远程配对设备?已保存的 VPS 配置和服务器不会改变。', remoteUnavailableFrp: 'frpc 尚未安装或私有服务器配置不完整。', remoteStartingFrp: '正在检查 VPS 并准备受限 FRP 通道…', remoteConnectingFrp: 'frpc 已启动,正在验证公网 HTTPS 端点…', frpMissing: '请先安装官方 frpc。', frpInvalid: 'frpc 校验失败,请彻底清理后重新安装。', frpConfigMissing: '请先保存自建 FRP 连接信息。', frpConfigVerifyFailed: 'frpc 拒绝了生成的配置,请检查服务器信息和 Token。', frpVhostPublic: 'VPS 明文 HTTP vhost 可被公网访问。请将其限制到 127.0.0.1,并由 Caddy 提供 HTTPS。', frpVhostProbeFailed: '无法检查 VPS 地址,请确认 DNS 后重试。', frpLaunchFailed: 'frpc 未能启动。', frpTimeout: '公网端点未能就绪,请检查 frps、Caddy、DNS 和防火墙。', frpDiscoveryMismatch: '公开域名连接到了另一台 DSH 电脑,请检查 Caddy 与 frps 映射。', frpDiscoveryInvalid: '公开域名没有返回有效的 DSH Mobile 发现信息。', frpStopped: 'FRP 连接已停止。', frpExited: 'frpc 意外退出,请检查 VPS 配置后重新连接。',\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n requestTimeout: 'The operation timed out. Confirm that DSH is still running, then try again.',\n diagnostics: 'Diagnostics', openDiagnostics: 'Open connection diagnostics', back: 'Back', backToMobile: 'Back to Mobile access', connectionDiagnostics: 'Connection diagnostics',\n diagnosticsIntro: 'Check versions, gateway, network adapter, firewall, and remote channel. The report is automatically redacted and never reads conversations or credentials.',\n diagnosticsNotRun: 'Not checked yet', diagnosticsStartHint: 'Select the button below to begin.', diagnosticsIdleMeta: 'Waiting to run · connection status only', diagnosticsStart: 'Start check', diagnosticsCopy: 'Copy redacted report', diagnosticsAdvanced: 'Advanced diagnostic details',\n diagnosticsComplete: 'Check complete', diagnosticsAttention: 'Some items need attention', diagnosticsProblem: 'Connection problems found', diagnosticsCompleteFallback: 'The check is complete.',\n diagnosticStatusOk: 'OK', diagnosticStatusWarning: 'Attention', diagnosticStatusError: 'Problem', diagnosticStatusInfo: 'Info', diagnosticItems: '{count} items', diagnosticCheck: 'Check', diagnosticAction: 'Suggested action',\n diagnosticNeedsAction: 'Needs action', diagnosticDetails: 'Check details', diagnosticOther: 'Other checks', diagnosticNoBlockers: '{count} checks · no blocking issues found', diagnosticNeedsCount: '{count} checks · {issues} need attention',\n diagnosticsChecking: 'Checking…', diagnosticsCheckingTitle: 'Checking connection', diagnosticsCheckingText: 'This usually takes a few seconds. The remote channel performs a real reachability test.', diagnosticsRunningMeta: 'Running · keep DSH online',\n diagnosticsIncomplete: 'Check not completed', diagnosticsReadFailed: 'Could not read diagnostics: {error}', diagnosticsUnavailable: 'Diagnostics service unavailable · try again later', diagnosticsRetry: 'Check again', diagnosticsCopied: 'Redacted report copied. You can paste it directly into an issue.', diagnosticsCopyManual: 'Clipboard access was denied. Details are expanded for manual copying.',\n diagnosticLabelVersions: 'Version compatibility', diagnosticLabelNetwork: 'Local network adapter', diagnosticLabelLan: 'Local network gateway', diagnosticLabelFirewall: 'Windows Firewall', diagnosticLabelRemote: 'Remote channel', diagnosticLabelPhone: 'Phone network',\n funnelTimeout: 'The Tailscale component timed out while starting. Check the network, then reconnect.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n requestTimeout: 'Operazione scaduta. Verifica che DSH sia ancora in esecuzione e riprova.',\n diagnostics: 'Diagnostica', openDiagnostics: 'Apri diagnostica connessione', back: 'Indietro', backToMobile: 'Torna ad Accesso mobile', connectionDiagnostics: 'Diagnostica connessione',\n diagnosticsIntro: 'Controlla versioni, gateway, scheda di rete, firewall e canale remoto. Il report viene anonimizzato automaticamente e non legge conversazioni o credenziali.',\n diagnosticsNotRun: 'Controllo non eseguito', diagnosticsStartHint: 'Premi il pulsante sotto per iniziare.', diagnosticsIdleMeta: 'In attesa · solo stato connessione', diagnosticsStart: 'Avvia controllo', diagnosticsCopy: 'Copia report anonimizzato', diagnosticsAdvanced: 'Dettagli diagnostici avanzati',\n diagnosticsComplete: 'Controllo completato', diagnosticsAttention: 'Alcuni elementi richiedono attenzione', diagnosticsProblem: 'Rilevati problemi di connessione', diagnosticsCompleteFallback: 'Controllo completato.',\n diagnosticStatusOk: 'OK', diagnosticStatusWarning: 'Attenzione', diagnosticStatusError: 'Problema', diagnosticStatusInfo: 'Informazione', diagnosticItems: '{count} elementi', diagnosticCheck: 'Controllo', diagnosticAction: 'Suggerimento',\n diagnosticNeedsAction: 'Da risolvere', diagnosticDetails: 'Dettagli controllo', diagnosticOther: 'Altri controlli', diagnosticNoBlockers: '{count} controlli · nessun problema bloccante', diagnosticNeedsCount: '{count} controlli · {issues} richiedono attenzione',\n diagnosticsChecking: 'Controllo…', diagnosticsCheckingTitle: 'Controllo connessione', diagnosticsCheckingText: 'Di solito richiede pochi secondi. Il canale remoto esegue un test reale di raggiungibilità.', diagnosticsRunningMeta: 'In esecuzione · mantieni DSH online',\n diagnosticsIncomplete: 'Controllo non completato', diagnosticsReadFailed: 'Impossibile leggere la diagnostica: {error}', diagnosticsUnavailable: 'Servizio diagnostico non disponibile · riprova più tardi', diagnosticsRetry: 'Ripeti controllo', diagnosticsCopied: 'Report anonimizzato copiato. Puoi incollarlo direttamente in una issue.', diagnosticsCopyManual: 'Il browser ha negato la copia. I dettagli sono stati aperti per la copia manuale.',\n diagnosticLabelVersions: 'Compatibilità versioni', diagnosticLabelNetwork: 'Scheda rete locale', diagnosticLabelLan: 'Gateway rete locale', diagnosticLabelFirewall: 'Windows Firewall', diagnosticLabelRemote: 'Canale remoto', diagnosticLabelPhone: 'Rete telefono',\n funnelTimeout: 'Avvio del componente Tailscale scaduto. Controlla la rete e riconnettiti.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n requestTimeout: '操作超时,请确认 DSH 仍在运行后重试。',\n diagnostics: '诊断', openDiagnostics: '打开连接诊断', back: '返回', backToMobile: '返回移动访问', connectionDiagnostics: '连接诊断',\n diagnosticsIntro: '检查版本、网关、网卡、防火墙和远程通道。报告自动脱敏,不读取对话或凭据。', diagnosticsNotRun: '尚未检查', diagnosticsStartHint: '点击下方按钮开始。', diagnosticsIdleMeta: '等待运行 · 仅收集连接状态', diagnosticsStart: '开始检查', diagnosticsCopy: '复制脱敏报告', diagnosticsAdvanced: '高级诊断详情',\n diagnosticsComplete: '检查完成', diagnosticsAttention: '有项目需要留意', diagnosticsProblem: '发现连接问题', diagnosticsCompleteFallback: '检查已完成。', diagnosticStatusOk: '正常', diagnosticStatusWarning: '注意', diagnosticStatusError: '问题', diagnosticStatusInfo: '说明', diagnosticItems: '{count} 项', diagnosticCheck: '检查项', diagnosticAction: '建议', diagnosticNeedsAction: '需要处理', diagnosticDetails: '检查详情', diagnosticOther: '其他检查', diagnosticNoBlockers: '{count} 项检查 · 未发现阻断问题', diagnosticNeedsCount: '{count} 项检查 · {issues} 项需要处理',\n diagnosticsChecking: '正在检查…', diagnosticsCheckingTitle: '正在检查连接', diagnosticsCheckingText: '通常几秒内完成。远程通道会执行一次真实可达性测试。', diagnosticsRunningMeta: '正在运行 · 请保持 DSH 在线', diagnosticsIncomplete: '检查未完成', diagnosticsReadFailed: '无法读取诊断结果:{error}', diagnosticsUnavailable: '诊断服务暂不可用 · 请稍后重试', diagnosticsRetry: '重新检查', diagnosticsCopied: '脱敏报告已复制,可直接粘贴到 Issue。', diagnosticsCopyManual: '浏览器未允许复制,已展开详情,请手动复制。',\n diagnosticLabelVersions: '版本兼容', diagnosticLabelNetwork: '局域网网卡', diagnosticLabelLan: '局域网网关', diagnosticLabelFirewall: 'Windows 防火墙', diagnosticLabelRemote: '远程通道', diagnosticLabelPhone: '手机网络', funnelTimeout: 'Tailscale 组件启动超时,请检查网络后重新连接。',\n})\n\nexport const LOCALIZED_DIAGNOSTIC_COPY = {\n en: {\n versions: 'Installed plugin, DSH, and minimum Android app versions are shown.',\n networkOk: 'The configured local network adapter is available.', networkError: 'The saved local network adapter is unavailable.', networkInfo: 'A fixed local network configuration is in use.', networkAction: 'Run dsh-mobile setup again.',\n lanOk: 'The local gateway is listening and pairing is available.', lanInfo: 'Local network access is currently off.', lanAction: 'Enable local network access when the phone must connect directly.',\n firewallOk: 'Local TCP and discovery firewall rules are enabled.', firewallWarning: 'The complete local network firewall rules were not found.', firewallInfo: 'The system did not allow the plugin to read firewall status.', firewallAction: 'Run dsh-mobile setup as administrator and check the firewall rules.',\n remoteOk: 'The remote public address passed the reachability check.', remoteWarning: 'The remote channel needs attention or is still connecting.', remoteError: 'The remote connection is not currently reachable.', remoteInfo: 'Remote access is currently off.', remoteAction: 'Return to Remote access, follow the provider guidance, and reconnect.',\n phone: 'The computer cannot determine whether the router isolates the phone.', phoneAction: 'Confirm the phone and computer use the same network, then disable guest-network or AP isolation.',\n reportTitle: 'DSH Mobile diagnostic report', generated: 'Generated',\n certificateOk: 'The FRP entry certificate is valid.', certificateExpiring: 'The FRP entry certificate expires soon.', certificateExpired: 'The FRP entry certificate has expired.', certificateUnknown: 'The FRP entry certificate could not be read.',\n certificateAction: 'Reconnect the FRP channel to re-issue the certificate, or follow the attach runbook on the VPS.',\n },\n it: {\n versions: 'Sono indicate le versioni installate di plugin e DSH e la versione minima dell’app Android.',\n networkOk: 'La scheda di rete locale configurata è disponibile.', networkError: 'La scheda di rete locale salvata non è disponibile.', networkInfo: 'È in uso una configurazione di rete locale fissa.', networkAction: 'Esegui di nuovo dsh-mobile setup.',\n lanOk: 'Il gateway locale è in ascolto e l’abbinamento è disponibile.', lanInfo: 'L’accesso dalla rete locale è disattivato.', lanAction: 'Attiva l’accesso locale quando il telefono deve collegarsi direttamente.',\n firewallOk: 'Le regole firewall TCP locale e di rilevamento sono attive.', firewallWarning: 'Le regole firewall complete per la rete locale non sono state trovate.', firewallInfo: 'Il sistema non ha consentito al plugin di leggere lo stato del firewall.', firewallAction: 'Esegui dsh-mobile setup come amministratore e controlla le regole firewall.',\n remoteOk: 'L’indirizzo pubblico remoto ha superato il test di raggiungibilità.', remoteWarning: 'Il canale remoto richiede attenzione o è ancora in connessione.', remoteError: 'La connessione remota non è al momento raggiungibile.', remoteInfo: 'L’accesso remoto è disattivato.', remoteAction: 'Torna ad Accesso remoto, segui le indicazioni del provider e riconnettiti.',\n phone: 'Il computer non può stabilire se il router isola il telefono.', phoneAction: 'Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP.',\n reportTitle: 'Report diagnostico DSH Mobile', generated: 'Generato',\n certificateOk: 'Il certificato di ingresso FRP è valido.', certificateExpiring: 'Il certificato di ingresso FRP scade a breve.', certificateExpired: 'Il certificato di ingresso FRP è scaduto.', certificateUnknown: 'Impossibile leggere il certificato di ingresso FRP.',\n certificateAction: 'Riconnetti il canale FRP per rigenerare il certificato, oppure segui il runbook attach sul VPS.',\n },\n zh: {\n versions: '已显示插件、DSH 和 Android App 最低版本。',\n networkOk: '已配置的局域网网卡可用。', networkError: '已保存的局域网网卡不可用。', networkInfo: '当前使用固定局域网配置。', networkAction: '重新运行 dsh-mobile setup。',\n lanOk: '局域网网关正在监听,配对入口可用。', lanInfo: '局域网访问当前未开启。', lanAction: '需要手机直连时开启局域网访问。',\n firewallOk: '局域网 TCP 与发现防火墙规则已启用。', firewallWarning: '未找到完整的局域网防火墙规则。', firewallInfo: '系统未允许插件读取防火墙状态。', firewallAction: '以管理员身份运行 dsh-mobile setup 并检查防火墙规则。',\n remoteOk: '远程公网地址已通过可达性检查。', remoteWarning: '远程通道需要处理或仍在连接。', remoteError: '远程连接当前不可达。', remoteInfo: '远程访问当前未开启。', remoteAction: '返回远程访问,按提供方提示操作并重新连接。',\n phone: '电脑无法判断路由器是否隔离了手机。', phoneAction: '确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。',\n reportTitle: 'DSH Mobile 诊断报告', generated: '生成时间',\n certificateOk: 'FRP 入口证书有效。', certificateExpiring: 'FRP 入口证书即将过期。', certificateExpired: 'FRP 入口证书已过期。', certificateUnknown: '无法读取 FRP 入口证书。',\n certificateAction: '重新连接 FRP 通道即可重新签发证书,或按接入清单在 VPS 侧处理。',\n },\n} satisfies Record<MobileControlLocale, {\n versions: string\n networkOk: string\n networkError: string\n networkInfo: string\n networkAction: string\n lanOk: string\n lanInfo: string\n lanAction: string\n firewallOk: string\n firewallWarning: string\n firewallInfo: string\n firewallAction: string\n remoteOk: string\n remoteWarning: string\n remoteError: string\n remoteInfo: string\n remoteAction: string\n phone: string\n phoneAction: string\n reportTitle: string\n generated: string\n certificateOk: string\n certificateExpiring: string\n certificateExpired: string\n certificateUnknown: string\n certificateAction: string\n}>\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n selfHostedDescription: \"Use your own VPS or HTTPS reverse proxy without a public tunnel service.\",\n originName: \"Own reverse proxy\",\n originDescription: \"You manage HTTPS. DSH Mobile provides a private, authenticated HTTP backend; no tunnel component is installed.\",\n prepareOrigin: \"Connect your HTTPS reverse proxy\",\n originPublicOrigin: \"Public HTTPS address\",\n originListenHost: \"Private listen IPv4\",\n originListenPort: \"HTTP backend port\",\n originAllowedCidrs: \"Allowed proxy source CIDRs\",\n originHelp: \"Same computer: use 127.0.0.1. For a proxy on another LAN device, use this computer’s private IPv4 and allow only the proxy’s direct source IPv4/32. Separate CIDRs with spaces or commas.\",\n originWarning: \"Keep this HTTP backend private. Never expose or port-forward it to the Internet. Your proxy must terminate HTTPS and preserve the external Host, including its port, and WebSocket upgrades.\",\n originSaveStart: \"Save and start backend\",\n originBackendSaved: \"Saved HTTP backend (not running)\",\n originBackendListening: \"Listening HTTP backend\",\n originCopyBackend: \"Copy backend address\",\n originBackendCopied: \"HTTP backend address copied.\",\n originPurge: \"Clear proxy settings\",\n originPurgeConfirm: \"Stop this private HTTP backend and delete only its reverse-proxy settings? Paired remote devices, LAN access, and other providers are kept.\",\n originPurged: \"The backend is stopped and its proxy settings are cleared. Paired devices are kept.\",\n originSaving: \"Saving settings and starting the private backend…\",\n originPurging: \"Stopping the backend and clearing its settings…\",\n originBackendReady: \"Backend listening\",\n originReady: \"The private HTTP backend is listening. Public HTTPS, its certificate, and WebSocket connectivity have NOT been verified. Test the public address from your phone before relying on it.\",\n originUnavailable: \"Configure the public HTTPS address and private backend first.\",\n originStarting: \"Starting the private HTTP backend…\",\n switchingOrigin: \"Switching to your own reverse proxy…\",\n reconnectingOrigin: \"Restarting the private HTTP backend…\",\n resetOriginConfirm: \"Stop this backend and reset remote pairing? All paired remote devices must pair again. Reverse-proxy settings and LAN devices are kept.\",\n originPublicOriginInvalid: \"Enter a public HTTPS origin only, such as https://phone.example.com:8815; no path, query, credentials, or private address.\",\n originListenHostInvalid: \"Use an explicit loopback or private IPv4 address on this computer, not 0.0.0.0 or a public address.\",\n originListenPortInvalid: \"Use a whole-number port from 1024 to 65535; lower ports need privileges the plugin does not have.\",\n originListenPortReserved: \"Port 3443 belongs to the LAN gateway and port 3080 to DSH itself, so both are always taken. Choose another port, such as 3444 or 3445.\",\n originAllowedCidrsInvalid: \"Allow 1–16 valid loopback/private IPv4 CIDRs with no host bits. For a LAN listener, include the actual proxy source IPv4/32; public ranges are not accepted.\",\n originSettingsInvalid: \"The reverse-proxy settings are invalid. Check the four fields and save again.\",\n originConfigMissing: \"Save the reverse-proxy settings before starting the backend.\",\n originConfigInvalid: \"The saved settings are invalid. Correct and save them again, or clear only these settings.\",\n originPortInUse: \"The backend port is already in use. Stop the conflicting listener or choose another port. Note that 3443, 3080, and a port already used by a cloudflared tunnel are never free.\",\n originAddressUnavailable: \"The listen address is not available on this computer. Choose its current private IPv4.\",\n originStartFailed: \"The private backend could not start. Check its address, port, permissions, and local logs.\",\n originConfigTargetInvalid: \"The saved proxy settings file is not a regular file. Remove it and save the settings again.\",\n originControllerUnavailable: \"The reverse-proxy provider is not available in this session. Restart DSH and try again.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n selfHostedDescription: \"Usa il tuo VPS o reverse proxy HTTPS senza un servizio di tunnel pubblico.\",\n originName: \"Reverse proxy proprio\",\n originDescription: \"Gestisci tu HTTPS. DSH Mobile offre un backend HTTP privato con autenticazione; non installa componenti tunnel.\",\n prepareOrigin: \"Collega il tuo reverse proxy HTTPS\",\n originPublicOrigin: \"Indirizzo HTTPS pubblico\",\n originListenHost: \"IPv4 privato di ascolto\",\n originListenPort: \"Porta backend HTTP\",\n originAllowedCidrs: \"CIDR sorgente del proxy consentiti\",\n originHelp: \"Stesso computer: usa 127.0.0.1. Per un proxy su un altro dispositivo LAN, usa l’IPv4 privato di questo computer e consenti solo l’IPv4 sorgente diretta del proxy con /32. Separa i CIDR con spazi o virgole.\",\n originWarning: \"Mantieni privato il backend HTTP. Non esporlo né inoltrarne la porta su Internet. Il proxy deve terminare HTTPS e conservare l’Host esterno, inclusa la porta, e gli upgrade WebSocket.\",\n originSaveStart: \"Salva e avvia backend\",\n originBackendSaved: \"Backend HTTP salvato (non attivo)\",\n originBackendListening: \"Backend HTTP in ascolto\",\n originCopyBackend: \"Copia indirizzo backend\",\n originBackendCopied: \"Indirizzo backend HTTP copiato.\",\n originPurge: \"Cancella impostazioni proxy\",\n originPurgeConfirm: \"Fermare questo backend HTTP privato ed eliminare solo le sue impostazioni reverse proxy? Dispositivi remoti abbinati, accesso LAN e altri provider vengono conservati.\",\n originPurged: \"Backend fermato e impostazioni proxy cancellate. I dispositivi abbinati sono conservati.\",\n originSaving: \"Salvataggio impostazioni e avvio del backend privato…\",\n originPurging: \"Arresto del backend e cancellazione impostazioni…\",\n originBackendReady: \"Backend in ascolto\",\n originReady: \"Il backend HTTP privato è in ascolto. HTTPS pubblico, certificato e connettività WebSocket NON sono stati verificati. Prova l’indirizzo pubblico dal telefono prima di usarlo.\",\n originUnavailable: \"Configura prima l’indirizzo HTTPS pubblico e il backend privato.\",\n originStarting: \"Avvio del backend HTTP privato…\",\n switchingOrigin: \"Passaggio al reverse proxy proprio…\",\n reconnectingOrigin: \"Riavvio del backend HTTP privato…\",\n resetOriginConfirm: \"Fermare il backend e reimpostare gli abbinamenti remoti? Tutti i dispositivi remoti dovranno essere abbinati di nuovo. Le impostazioni proxy e i dispositivi LAN vengono conservati.\",\n originPublicOriginInvalid: \"Inserisci solo un’origine HTTPS pubblica, ad esempio https://phone.example.com:8815, senza percorso, query, credenziali o indirizzo privato.\",\n originListenHostInvalid: \"Usa un IPv4 loopback o privato esplicito di questo computer, non 0.0.0.0 né un indirizzo pubblico.\",\n originListenPortInvalid: \"Usa una porta intera da 1024 a 65535; le porte più basse richiedono privilegi che il plugin non ha.\",\n originListenPortReserved: \"La porta 3443 appartiene al gateway LAN e la 3080 a DSH stesso, quindi sono sempre occupate. Scegli un’altra porta, ad esempio 3444 o 3445.\",\n originAllowedCidrsInvalid: \"Consenti 1–16 CIDR IPv4 loopback/privati validi senza bit host. Per l’ascolto LAN includi l’IPv4 sorgente reale del proxy con /32; le reti pubbliche non sono ammesse.\",\n originSettingsInvalid: \"Impostazioni reverse proxy non valide. Controlla i quattro campi e salva di nuovo.\",\n originConfigMissing: \"Salva le impostazioni reverse proxy prima di avviare il backend.\",\n originConfigInvalid: \"Impostazioni salvate non valide. Correggile e salvale di nuovo, oppure cancella solo queste impostazioni.\",\n originPortInUse: \"La porta backend è già occupata. Ferma il processo in conflitto o scegli un’altra porta. 3443, 3080 e una porta già usata da un tunnel cloudflared non sono mai libere.\",\n originAddressUnavailable: \"L’indirizzo di ascolto non è disponibile su questo computer. Scegli il suo IPv4 privato attuale.\",\n originStartFailed: \"Impossibile avviare il backend privato. Controlla indirizzo, porta, permessi e log locali.\",\n originConfigTargetInvalid: \"Il file delle impostazioni proxy salvato non è un file regolare. Rimuovilo e salva di nuovo le impostazioni.\",\n originControllerUnavailable: \"Il provider reverse proxy non è disponibile in questa sessione. Riavvia DSH e riprova.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n selfHostedDescription: \"使用自己的 VPS 或 HTTPS 反向代理,不依赖公共隧道服务。\",\n originName: \"自有反向代理\",\n originDescription: \"HTTPS 由你管理,插件提供带认证的私有 HTTP 后端,无需安装隧道组件。\",\n prepareOrigin: \"接入自有 HTTPS 反向代理\",\n originPublicOrigin: \"公网 HTTPS 地址\",\n originListenHost: \"私有监听 IPv4\",\n originListenPort: \"HTTP 后端端口\",\n originAllowedCidrs: \"允许的代理来源 CIDR\",\n originHelp: \"代理与 DSH 同机时用 127.0.0.1;代理在另一台局域网设备上时,填写本机私有 IPv4,并只允许代理实际直连来源 IPv4/32。多个 CIDR 用空格或逗号分隔。\",\n originWarning: \"HTTP 后端必须留在私网,绝不能直接暴露公网或做公网端口映射。反向代理负责 HTTPS,并须保留外部 Host(含端口)及 WebSocket 升级。\",\n originSaveStart: \"保存并启动后端\",\n originBackendSaved: \"已保存的 HTTP 后端(未运行)\",\n originBackendListening: \"正在监听的 HTTP 后端\",\n originCopyBackend: \"复制后端地址\",\n originBackendCopied: \"已复制 HTTP 后端地址。\",\n originPurge: \"清除代理配置\",\n originPurgeConfirm: \"停止此私有 HTTP 后端,并且只删除它的反向代理配置?已配对的远程设备、局域网访问和其他提供方均保留。\",\n originPurged: \"已停止后端并清除代理配置,已配对设备仍保留。\",\n originSaving: \"正在保存配置并启动私有后端…\",\n originPurging: \"正在停止后端并清除配置…\",\n originBackendReady: \"后端已监听\",\n originReady: \"私有 HTTP 后端已监听,但尚未验证公网 HTTPS、证书及 WebSocket 连通性。请先用手机测试公网地址,不代表公网连接已成功。\",\n originUnavailable: \"请先配置公网 HTTPS 地址和私有后端。\",\n originStarting: \"正在启动私有 HTTP 后端…\",\n switchingOrigin: \"正在切换到自有反向代理…\",\n reconnectingOrigin: \"正在重启私有 HTTP 后端…\",\n resetOriginConfirm: \"停止此后端并重置远程配对?所有已配对的远程设备都需要重新配对;反向代理配置和局域网设备保留。\",\n originPublicOriginInvalid: \"只填写公网 HTTPS 源地址,例如 https://phone.example.com:8815;不能含路径、查询参数、账号密码或私有地址。\",\n originListenHostInvalid: \"填写本机明确的回环或私有 IPv4,不能使用 0.0.0.0 或公网地址。\",\n originListenPortInvalid: \"端口必须是 1024–65535 的整数;更低的端口需要插件没有的特权。\",\n originListenPortReserved: \"3443 属于局域网网关、3080 属于 DSH 自身,两者始终被占用。请换一个端口,例如 3444 或 3445。\",\n originAllowedCidrsInvalid: \"填写 1–16 个规范的回环/私有 IPv4 CIDR,不能有主机位;局域网监听时须包含代理实际来源 IPv4/32,不接受公网网段。\",\n originSettingsInvalid: \"反向代理配置无效,请检查四个字段后重新保存。\",\n originConfigMissing: \"请先保存反向代理配置,再启动后端。\",\n originConfigInvalid: \"已保存的配置无效,请修正并重新保存,或仅清除这项配置。\",\n originPortInUse: \"后端端口已被占用,请停止冲突进程或改用其他端口。3443、3080 以及已被 cloudflared 隧道占用的端口永远不会空闲。\",\n originAddressUnavailable: \"本机没有这个监听地址,请选择当前本机的私有 IPv4。\",\n originStartFailed: \"私有后端启动失败,请检查地址、端口、权限和本地日志。\",\n originConfigTargetInvalid: \"已保存的代理配置文件不是普通文件。请移除后重新保存设置。\",\n originControllerUnavailable: \"当前会话里反向代理提供方不可用。请重启 DSH 后重试。\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n cloudflaredBadge: \"Account optional\",\n cloudflaredDescription: \"Installs the official cloudflared client on demand. A quick tunnel needs no account and gives a temporary public address; a named tunnel uses a Cloudflare account token and keeps one hostname that survives restarts.\",\n prepareCloudflared: \"Prepare cloudflared\",\n cloudflaredUnsupported: \"Windows x64 and Linux x64/arm64 are currently supported.\",\n cloudflaredNotInstalled: \"Not installed. A fixed version is downloaded from the official cloudflared release page only after you click the button below.\",\n cloudflaredReady: \"Official component {version} is ready.\",\n cloudflaredQuickNote: \"Quick tunnel: no Cloudflare account is needed, but Cloudflare positions quick tunnels for testing — the address is random, rate-limited, and carries no uptime guarantee. Do not rely on one for access that must stay reachable.\",\n cloudflaredNamedNote: \"Named tunnel: the hostname above stays the same across restarts and Cloudflare routes it to the local forward port. The connector token is stored only in the DSH Mobile private directory, is handed to cloudflared through its environment rather than the command line, and is never shown again once saved.\",\n cloudflaredComponentNote: \"Downloaded from the official cloudflared release page and verified at a fixed version only after you choose Install. The client is started with automatic updates disabled. It does not add a system service, startup item, registry entry, or PATH entry.\",\n installConfirmCloudflared: \"Download and verify cloudflared {size} from the official release page and store it only in the DSH Mobile private directory? No system service, PATH/registry entry, or startup item is added.\",\n installingCloudflared: \"Installing the official cloudflared component. Keep DSH running until it completes.\",\n purgeCloudflared: \"Completely remove the cloudflared component\",\n purgeCloudflaredConfirm: \"Completely remove the cloudflared component and its runtime logs from the DSH Mobile private directory? Local network access, DSH data, and other system programs are unaffected.\",\n purgingCloudflared: \"Stopping the channel and removing cloudflared files managed by DSH Mobile…\",\n switchingCloudflared: \"Switching to cloudflared…\",\n reconnectingCloudflared: \"Reconnecting cloudflared…\",\n remoteUnavailableCloudflared: \"cloudflared is not installed.\",\n remoteStartingCloudflared: \"Starting the cloudflared connector…\",\n remoteConnectingCloudflared: \"Starting the DSH authentication gateway…\",\n resetCloudflaredConfirm: \"Disable the cloudflared remote channel and remove all remote paired devices? The installed component and the local network configuration are unchanged.\",\n cloudflaredMissing: \"The official cloudflared component is not installed. Complete the preparation steps above.\",\n cloudflaredInvalid: \"cloudflared component verification failed. Remove it completely and reinstall.\",\n cloudflaredPortUnavailable: \"Could not allocate a local remote-gateway port. Try again.\",\n cloudflaredLaunchFailed: \"The cloudflared client failed to start.\",\n cloudflaredTimeout: \"Timed out waiting for the tunnel to become reachable. Reconnect.\",\n cloudflaredStopped: \"The cloudflared connection stopped.\",\n cloudflaredExited: \"The cloudflared connection exited unexpectedly. Reconnect.\",\n cloudflaredOutputInvalid: \"cloudflared returned an unrecognized status.\",\n cloudflaredOriginInvalid: \"The public address returned by cloudflared failed validation.\",\n cloudflaredComponentUnsupported: \"This build cannot run the cloudflared component.\",\n cloudflaredDownloadHashMismatch: \"The downloaded cloudflared failed its SHA-256 verification. Retry the install.\",\n cloudflaredDownloadSizeMismatch: \"The downloaded cloudflared is not the pinned size. Retry the install.\",\n cloudflaredDownloadRedirectMissing: \"The download page stopped redirecting to the release asset. Retry later, or install from the official page manually.\",\n cloudflaredDownloadRedirectInvalid: \"The download redirect did not point at a GitHub release asset, so it was refused.\",\n cloudflaredDownloadRedirectRejected: \"The download redirect was refused by policy. Retry later, or install from the official page manually.\",\n cloudflaredExecutableHashMismatch: \"The installed cloudflared no longer matches the verified build. Remove it completely and install again.\",\n cloudflaredPortReservationFailed: \"Could not reserve a local port for the tunnel. Try again.\",\n cloudflaredTunnelTargetInvalid: \"The saved tunnel configuration is not a regular file. Remove it and save the settings again.\",\n cloudflaredTunnelSaving: \"Saving the tunnel settings and reconnecting cloudflared…\",\n cloudflaredTunnelForgetting: \"Removing the saved tunnel token…\",\n cloudflaredTunnelTitle: \"Tunnel type\",\n cloudflaredTunnelQuick: \"Quick\",\n cloudflaredTunnelNamed: \"Named\",\n cloudflaredTunnelQuickHint: \"No account: cloudflared allocates a random address on every start.\",\n cloudflaredTunnelNamedHint: \"Uses a Cloudflare account token and keeps one address that survives restarts.\",\n cloudflaredTunnelHostname: \"Public hostname\",\n cloudflaredTunnelHostnamePlaceholder: \"dsh.example.com\",\n cloudflaredTunnelPort: \"Local forward port\",\n cloudflaredTunnelToken: \"Connector token\",\n cloudflaredTunnelTokenPlaceholder: \"Paste the tunnel token from the Cloudflare dashboard\",\n cloudflaredTunnelTokenSaved: \"A token is saved. Leave blank to keep it.\",\n cloudflaredTunnelSave: \"Save and connect\",\n cloudflaredTunnelSaveQuick: \"Switch to a quick tunnel\",\n cloudflaredTunnelConfigured: \"Named tunnel configured: {hostname} reaches this computer on 127.0.0.1:{port}.\",\n cloudflaredTunnelMissing: \"No named tunnel configured yet.\",\n cloudflaredTunnelHint: \"In the Cloudflare dashboard create a tunnel, then set its public hostname to http://127.0.0.1:{port}. The token is stored only in the DSH Mobile private directory and is never shown again after saving.\",\n cloudflaredTunnelForget: \"Remove the saved token\",\n cloudflaredTunnelForgetConfirm: \"Remove the saved tunnel token, stop the cloudflared channel, and go back to a quick tunnel? Nothing reconnects on its own: switch the channel back on afterwards. The tunnel still exists in your Cloudflare account, and its public hostname is unchanged.\",\n cloudflaredTunnelInvalid: \"The saved named-tunnel configuration is unreadable; the provider is using a quick tunnel. Save the settings again.\",\n cloudflaredTunnelPortUnavailable: \"The configured local forward port is unavailable. Cloudflare routes the public hostname to that exact port, so free it or choose another one.\",\n cloudflaredTunnelHostnameInvalid: \"Enter a public hostname under a domain on this Cloudflare account, for example dsh.example.com.\",\n cloudflaredTunnelPortInvalid: \"Enter a forward port between 1024 and 65535.\",\n cloudflaredTunnelPortReserved: \"Port 3443 is reserved for the DSH Mobile LAN gateway and is always occupied. Choose another port, such as 3444 or 3445.\",\n cloudflaredTunnelTokenInvalid: \"That does not look like a tunnel token. Copy the whole token from the Cloudflare dashboard.\",\n cloudflaredTunnelSettingsInvalid: \"The tunnel settings were rejected.\",\n cloudflaredTunnelConfigMissing: \"Enter the token, hostname, and port to use a named tunnel.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n cloudflaredBadge: \"Account facoltativo\",\n cloudflaredDescription: \"Installa su richiesta il client cloudflared ufficiale. Il tunnel rapido non richiede account e fornisce un indirizzo pubblico temporaneo; il tunnel con nome usa il token di un account Cloudflare e mantiene un hostname stabile tra i riavvii.\",\n prepareCloudflared: \"Prepara cloudflared\",\n cloudflaredUnsupported: \"Attualmente sono supportati Windows x64 e Linux x64/arm64.\",\n cloudflaredNotInstalled: \"Non installato. Una versione fissa viene scaricata dalla pagina ufficiale di cloudflared solo dopo aver premuto il pulsante.\",\n cloudflaredReady: \"Componente ufficiale {version} pronto.\",\n cloudflaredQuickNote: \"Tunnel rapido: non serve un account Cloudflare, ma Cloudflare li considera pensati per i test — l’indirizzo è casuale, soggetto a limiti di frequenza e senza garanzia di disponibilità. Non usarlo per un accesso che deve restare raggiungibile.\",\n cloudflaredNamedNote: \"Tunnel con nome: l’hostname qui sopra resta lo stesso tra i riavvii e Cloudflare lo instrada verso la porta di inoltro locale. Il token del connettore è salvato solo nella directory privata DSH Mobile, viene passato a cloudflared tramite l’ambiente e non tramite la riga di comando, e dopo il salvataggio non viene più mostrato.\",\n cloudflaredComponentNote: \"Viene scaricato dalla pagina ufficiale di cloudflared e verificato a una versione fissa solo dopo aver scelto Installa. Il client viene avviato con gli aggiornamenti automatici disattivati. Non aggiunge servizi di sistema, avvio automatico, voci di registro o PATH.\",\n installConfirmCloudflared: \"Scaricare e verificare cloudflared {size} dalla pagina ufficiale e salvarlo solo nella directory privata DSH Mobile? Non vengono aggiunti servizi, PATH/registro o avvio automatico.\",\n installingCloudflared: \"Installazione del componente cloudflared ufficiale. Mantieni DSH in esecuzione fino al termine.\",\n purgeCloudflared: \"Rimuovi completamente il componente cloudflared\",\n purgeCloudflaredConfirm: \"Rimuovere completamente il componente cloudflared e i log runtime dalla directory privata DSH Mobile? Rete locale, dati DSH e altri programmi non sono interessati.\",\n purgingCloudflared: \"Arresto del canale e rimozione dei file cloudflared gestiti da DSH Mobile…\",\n switchingCloudflared: \"Passaggio a cloudflared…\",\n reconnectingCloudflared: \"Riconnessione di cloudflared…\",\n remoteUnavailableCloudflared: \"cloudflared non è installato.\",\n remoteStartingCloudflared: \"Avvio del connettore cloudflared…\",\n remoteConnectingCloudflared: \"Avvio del gateway di autenticazione DSH…\",\n resetCloudflaredConfirm: \"Disattivare il canale remoto cloudflared e rimuovere tutti i dispositivi remoti? Il componente installato e la configurazione della rete locale restano invariati.\",\n cloudflaredMissing: \"Il componente cloudflared ufficiale non è installato. Completa i passaggi sopra.\",\n cloudflaredInvalid: \"Verifica del componente cloudflared non riuscita. Rimuovilo completamente e reinstallalo.\",\n cloudflaredPortUnavailable: \"Impossibile assegnare una porta locale al gateway remoto. Riprova.\",\n cloudflaredLaunchFailed: \"Il client cloudflared non è stato avviato.\",\n cloudflaredTimeout: \"Timeout in attesa che il tunnel diventi raggiungibile. Riconnettiti.\",\n cloudflaredStopped: \"La connessione cloudflared è stata arrestata.\",\n cloudflaredExited: \"La connessione cloudflared è terminata in modo imprevisto. Riconnettiti.\",\n cloudflaredOutputInvalid: \"cloudflared ha restituito uno stato non riconosciuto.\",\n cloudflaredOriginInvalid: \"L’indirizzo pubblico restituito da cloudflared non ha superato la verifica.\",\n cloudflaredComponentUnsupported: \"Questa build non può eseguire il componente cloudflared.\",\n cloudflaredDownloadHashMismatch: \"Il cloudflared scaricato non ha superato la verifica SHA-256. Riprova l’installazione.\",\n cloudflaredDownloadSizeMismatch: \"Il cloudflared scaricato non ha la dimensione prevista. Riprova l’installazione.\",\n cloudflaredDownloadRedirectMissing: \"La pagina di download non reindirizza più all’asset della release. Riprova più tardi o installa manualmente dalla pagina ufficiale.\",\n cloudflaredDownloadRedirectInvalid: \"Il reindirizzamento di download non puntava a un asset di release GitHub ed è stato rifiutato.\",\n cloudflaredDownloadRedirectRejected: \"Il reindirizzamento di download è stato rifiutato dalle regole. Riprova più tardi o installa manualmente dalla pagina ufficiale.\",\n cloudflaredExecutableHashMismatch: \"Il cloudflared installato non corrisponde più alla build verificata. Rimuovilo completamente e reinstalla.\",\n cloudflaredPortReservationFailed: \"Impossibile riservare una porta locale per il tunnel. Riprova.\",\n cloudflaredTunnelTargetInvalid: \"La configurazione salvata del tunnel non è un file regolare. Rimuovila e salva di nuovo le impostazioni.\",\n cloudflaredTunnelSaving: \"Salvataggio delle impostazioni e riconnessione di cloudflared…\",\n cloudflaredTunnelForgetting: \"Rimozione del token del tunnel salvato…\",\n cloudflaredTunnelTitle: \"Tipo di tunnel\",\n cloudflaredTunnelQuick: \"Rapido\",\n cloudflaredTunnelNamed: \"Con nome\",\n cloudflaredTunnelQuickHint: \"Senza account: cloudflared assegna un indirizzo casuale a ogni avvio.\",\n cloudflaredTunnelNamedHint: \"Usa il token di un account Cloudflare e mantiene un indirizzo stabile tra i riavvii.\",\n cloudflaredTunnelHostname: \"Hostname pubblico\",\n cloudflaredTunnelHostnamePlaceholder: \"dsh.example.com\",\n cloudflaredTunnelPort: \"Porta di inoltro locale\",\n cloudflaredTunnelToken: \"Token del connettore\",\n cloudflaredTunnelTokenPlaceholder: \"Incolla il token del tunnel dalla dashboard Cloudflare\",\n cloudflaredTunnelTokenSaved: \"Un token è salvato. Lascia vuoto per mantenerlo.\",\n cloudflaredTunnelSave: \"Salva e connetti\",\n cloudflaredTunnelSaveQuick: \"Passa a un tunnel rapido\",\n cloudflaredTunnelConfigured: \"Tunnel con nome configurato: {hostname} raggiunge questo computer su 127.0.0.1:{port}.\",\n cloudflaredTunnelMissing: \"Nessun tunnel con nome configurato.\",\n cloudflaredTunnelHint: \"Nella dashboard Cloudflare crea un tunnel e imposta il suo hostname pubblico su http://127.0.0.1:{port}. Il token è salvato solo nella directory privata DSH Mobile e non viene più mostrato dopo il salvataggio.\",\n cloudflaredTunnelForget: \"Rimuovi il token salvato\",\n cloudflaredTunnelForgetConfirm: \"Rimuovere il token salvato, arrestare il canale cloudflared e tornare a un tunnel rapido? Nulla si riconnette da solo: riattiva il canale in seguito. Il tunnel resta nel tuo account Cloudflare e il suo hostname pubblico non cambia.\",\n cloudflaredTunnelInvalid: \"La configurazione salvata del tunnel con nome non è leggibile; il provider usa un tunnel rapido. Salva di nuovo le impostazioni.\",\n cloudflaredTunnelPortUnavailable: \"La porta di inoltro locale configurata non è disponibile. Cloudflare instrada l’hostname pubblico esattamente su quella porta: liberala o scegline un’altra.\",\n cloudflaredTunnelHostnameInvalid: \"Inserisci un hostname pubblico sotto un dominio di questo account Cloudflare, ad esempio dsh.example.com.\",\n cloudflaredTunnelPortInvalid: \"Inserisci una porta di inoltro tra 1024 e 65535.\",\n cloudflaredTunnelPortReserved: \"La porta 3443 è riservata al gateway LAN di DSH Mobile ed è sempre occupata. Scegli un’altra porta, ad esempio 3444 o 3445.\",\n cloudflaredTunnelTokenInvalid: \"Non sembra un token di tunnel. Copia il token completo dalla dashboard Cloudflare.\",\n cloudflaredTunnelSettingsInvalid: \"Le impostazioni del tunnel sono state rifiutate.\",\n cloudflaredTunnelConfigMissing: \"Inserisci token, hostname e porta per usare un tunnel con nome.\",\n})\n\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n cloudflaredBadge: \"账号可选\",\n cloudflaredDescription: \"按需安装官方 cloudflared 客户端。快速隧道无需账号,提供临时公网地址;命名隧道使用 Cloudflare 账号令牌,地址固定、重启不变。\",\n prepareCloudflared: \"准备 cloudflared\",\n cloudflaredUnsupported: \"当前支持 Windows x64 与 Linux x64/arm64。\",\n cloudflaredNotInstalled: \"尚未安装。只有点击下方按钮后,才会从 cloudflared 官方发布页下载固定版本。\",\n cloudflaredReady: \"官方组件 {version} 已就绪。\",\n cloudflaredQuickNote: \"快速隧道:无需 Cloudflare 账号,但官方定位是测试用途 —— 地址随机、有限流、无可用性保证。请勿用于必须长期可达的访问。\",\n cloudflaredNamedNote: \"命名隧道:上面的公网域名重启后不变,由 Cloudflare 转发到本机转发端口。连接器令牌只保存在 DSH Mobile 私有目录,经环境变量而非命令行传给 cloudflared,保存后不再显示。\",\n cloudflaredComponentNote: \"仅在你点击安装后从 cloudflared 官方发布页下载并校验固定版本,且启动时关闭自动更新。不会写入系统服务、开机启动、注册表或 PATH。\",\n installConfirmCloudflared: \"将从 cloudflared 官方发布页下载并校验固定版本({size}),仅保存到 DSH Mobile 私有目录。不会安装系统服务、写入 PATH/注册表或设置开机启动。是否继续?\",\n installingCloudflared: \"正在安装 cloudflared 官方组件。完成前请保持 DSH 运行。\",\n purgeCloudflared: \"彻底移除 cloudflared 组件\",\n purgeCloudflaredConfirm: \"彻底移除 DSH Mobile 私有目录中的 cloudflared 组件和运行日志?不会影响局域网、DSH 数据或系统中的其他程序。\",\n purgingCloudflared: \"正在关闭通道并清理 DSH Mobile 管理的 cloudflared 文件…\",\n switchingCloudflared: \"正在切换到 cloudflared…\",\n reconnectingCloudflared: \"正在重连 cloudflared…\",\n remoteUnavailableCloudflared: \"cloudflared 尚未安装。\",\n remoteStartingCloudflared: \"正在启动 cloudflared 连接器…\",\n remoteConnectingCloudflared: \"正在启动 DSH 认证网关…\",\n resetCloudflaredConfirm: \"关闭 cloudflared 远程通道并移除所有远程配对设备?已安装组件与局域网配置保持不变。\",\n cloudflaredMissing: \"cloudflared 官方组件尚未安装。请先完成上方准备步骤。\",\n cloudflaredInvalid: \"cloudflared 组件校验失败。请彻底移除后重新安装。\",\n cloudflaredPortUnavailable: \"无法分配本机远程网关端口,请重试。\",\n cloudflaredLaunchFailed: \"cloudflared 客户端未能启动。\",\n cloudflaredTimeout: \"等待隧道可用超时,请重新连接。\",\n cloudflaredStopped: \"cloudflared 连接已停止。\",\n cloudflaredExited: \"cloudflared 连接意外退出,请重新连接。\",\n cloudflaredOutputInvalid: \"cloudflared 返回了无法识别的状态。\",\n cloudflaredOriginInvalid: \"cloudflared 返回的公网地址未通过校验。\",\n cloudflaredComponentUnsupported: \"当前构建无法运行 cloudflared 组件。\",\n cloudflaredDownloadHashMismatch: \"下载的 cloudflared 未通过 SHA-256 校验。请重新安装。\",\n cloudflaredDownloadSizeMismatch: \"下载的 cloudflared 体积与固定版本不符。请重新安装。\",\n cloudflaredDownloadRedirectMissing: \"下载页不再跳转到发布资源。请稍后重试,或按官方页面手动安装。\",\n cloudflaredDownloadRedirectInvalid: \"下载跳转的目标不是 GitHub 发布资源,已被拒绝。\",\n cloudflaredDownloadRedirectRejected: \"下载跳转未通过校验规则。请稍后重试,或按官方页面手动安装。\",\n cloudflaredExecutableHashMismatch: \"已安装的 cloudflared 与校验过的版本不一致。请彻底移除后重新安装。\",\n cloudflaredPortReservationFailed: \"无法为隧道预留本机端口,请重试。\",\n cloudflaredTunnelTargetInvalid: \"已保存的隧道配置不是普通文件。请移除后重新保存设置。\",\n cloudflaredTunnelSaving: \"正在保存隧道设置并重新连接 cloudflared…\",\n cloudflaredTunnelForgetting: \"正在移除已保存的隧道令牌…\",\n cloudflaredTunnelTitle: \"隧道类型\",\n cloudflaredTunnelQuick: \"快速隧道\",\n cloudflaredTunnelNamed: \"命名隧道\",\n cloudflaredTunnelQuickHint: \"无需账号:每次启动由 cloudflared 随机分配地址。\",\n cloudflaredTunnelNamedHint: \"使用 Cloudflare 账号令牌,地址固定,重启后仍然有效。\",\n cloudflaredTunnelHostname: \"公网域名\",\n cloudflaredTunnelHostnamePlaceholder: \"dsh.example.com\",\n cloudflaredTunnelPort: \"本机转发端口\",\n cloudflaredTunnelToken: \"连接器令牌\",\n cloudflaredTunnelTokenPlaceholder: \"粘贴 Cloudflare 控制台里的隧道令牌\",\n cloudflaredTunnelTokenSaved: \"已保存令牌,留空表示不更改。\",\n cloudflaredTunnelSave: \"保存并连接\",\n cloudflaredTunnelSaveQuick: \"切换到快速隧道\",\n cloudflaredTunnelConfigured: \"命名隧道已配置:{hostname} 将转发到本机 127.0.0.1:{port}。\",\n cloudflaredTunnelMissing: \"尚未配置命名隧道。\",\n cloudflaredTunnelHint: \"先在 Cloudflare 控制台创建隧道,并把它的公开主机名指向 http://127.0.0.1:{port}。令牌只保存在 DSH Mobile 私有目录,保存后不会再显示。\",\n cloudflaredTunnelForget: \"移除已保存的令牌\",\n cloudflaredTunnelForgetConfirm: \"移除已保存的隧道令牌、停止 cloudflared 通道并改回快速隧道?不会自动重连,之后需要手动重新打开开关。Cloudflare 账号里的隧道本身仍在,公开主机名也不会改变。\",\n cloudflaredTunnelInvalid: \"已保存的命名隧道配置无法读取,当前按快速隧道运行。请重新保存设置。\",\n cloudflaredTunnelPortUnavailable: \"配置的本机转发端口不可用。Cloudflare 把公开主机名固定转发到该端口,请释放它或换一个端口。\",\n cloudflaredTunnelHostnameInvalid: \"请填写本 Cloudflare 账号下某个域名的公开主机名,例如 dsh.example.com。\",\n cloudflaredTunnelPortInvalid: \"请填写 1024 到 65535 之间的转发端口。\",\n cloudflaredTunnelPortReserved: \"3443 已留给 DSH Mobile 局域网网关,且始终被占用。请换一个端口,例如 3444 或 3445。\",\n cloudflaredTunnelTokenInvalid: \"这不像是一个隧道令牌。请从 Cloudflare 控制台完整复制令牌。\",\n cloudflaredTunnelSettingsInvalid: \"隧道设置未通过校验。\",\n cloudflaredTunnelConfigMissing: \"使用命名隧道需要同时填写令牌、公网域名和端口。\",\n})\n\nexport const DIAGNOSTIC_REASON_MESSAGES = {\n en: {\n 'lan-setup-required': ['Local network setup has not been completed.', 'Return to Local network and choose a network to finish setup.'],\n 'versions-current': ['Installed plugin, DSH, and minimum Android app versions are shown.', ''],\n 'network-unavailable': ['The saved local network adapter is unavailable.', 'Run dsh-mobile setup again.'],\n 'network-interface': ['Using network interface {interfaceName}.', ''],\n 'network-fixed': ['A fixed local network configuration is in use.', ''],\n 'lan-ready': ['The local gateway is listening at {endpointSuffix}; pairing is available.', ''],\n 'lan-off': ['Local network access is currently off.', 'Enable local network access when the phone must connect directly.'],\n 'firewall-ready': ['Local TCP and discovery firewall rules are enabled.', ''],\n 'firewall-missing': ['The complete local network firewall rules were not found.', 'Run dsh-mobile setup as administrator and check the firewall rules.'],\n 'firewall-unknown': ['The system did not allow the plugin to read firewall status.', 'If the phone cannot find this computer, run setup as administrator.'],\n 'remote-off': ['Remote access through {provider} is currently off.', ''],\n 'remote-origin-ready': ['The private HTTP backend is listening; public HTTPS is not verified.', 'Verify your reverse proxy, public certificate, external Host including port, and WebSocket access from your phone. Keep the HTTP backend private.'],\n 'remote-ready': ['{provider} endpoint {endpointSuffix} is reachable in about {latencyMs} ms.', ''],\n 'remote-ready-via-proxy': ['The computer reached {provider} endpoint {endpointSuffix} through its configured proxy in about {latencyMs} ms; phone reachability is not verified.', 'Test the connection from the phone’s network.'],\n 'remote-rate-limited': ['{provider} endpoint {endpointSuffix} is reachable, but this check observed rate limiting.', 'Try again later; older sessions load on demand to reduce traffic.'],\n 'remote-rate-limited-via-proxy': ['The computer reached {provider} endpoint {endpointSuffix} through its proxy, but the service rate-limited this check; phone reachability is not verified.', 'Try again later and test from the phone’s network.'],\n 'remote-fake-ip': ['The Tailscale address is intercepted by the current VPN or DNS proxy, so TLS was not established.', 'Switch VPN node or proxy mode; if it still fails, use cpolar.'],\n 'remote-unreachable': ['{provider} reports ready, but endpoint {endpointSuffix} is not reachable.', 'Reconnect, then check the provider status if it still fails.'],\n 'remote-needs-login': ['Tailscale is waiting for login to finish.', 'Return to Remote access and continue login.'],\n 'remote-connecting': ['The {provider} remote channel is still connecting.', 'Wait briefly, then check again.'],\n 'remote-controller-error': ['The {provider} controller reported {controllerCode}.', 'Return to Remote access and reconnect.'],\n 'phone-network-unknown': ['The computer cannot determine whether the router isolates the phone.', 'Confirm the phone and computer use the same network, then disable guest-network or AP isolation.'],\n },\n it: {\n 'lan-setup-required': ['La configurazione della rete locale non è completa.', 'Torna a Rete locale e scegli una rete per completare la configurazione.'],\n 'versions-current': ['Sono indicate le versioni installate di plugin e DSH e la versione minima dell’app Android.', ''],\n 'network-unavailable': ['La scheda di rete locale salvata non è disponibile.', 'Esegui di nuovo dsh-mobile setup.'],\n 'network-interface': ['È in uso l’interfaccia di rete {interfaceName}.', ''],\n 'network-fixed': ['È in uso una configurazione di rete locale fissa.', ''],\n 'lan-ready': ['Il gateway locale è in ascolto su {endpointSuffix}; l’abbinamento è disponibile.', ''],\n 'lan-off': ['L’accesso dalla rete locale è disattivato.', 'Attiva l’accesso locale quando il telefono deve collegarsi direttamente.'],\n 'firewall-ready': ['Le regole firewall TCP locale e di rilevamento sono attive.', ''],\n 'firewall-missing': ['Le regole firewall complete per la rete locale non sono state trovate.', 'Esegui dsh-mobile setup come amministratore e controlla le regole firewall.'],\n 'firewall-unknown': ['Il sistema non ha consentito al plugin di leggere lo stato del firewall.', 'Se il telefono non trova il computer, esegui setup come amministratore.'],\n 'remote-off': ['L’accesso remoto tramite {provider} è disattivato.', ''],\n 'remote-origin-ready': ['Il backend HTTP privato è in ascolto; HTTPS pubblico non è verificato.', 'Verifica dal telefono reverse proxy, certificato pubblico, Host esterno inclusa la porta e accesso WebSocket. Mantieni privato il backend HTTP.'],\n 'remote-ready': ['L’endpoint {provider} {endpointSuffix} è raggiungibile in circa {latencyMs} ms.', ''],\n 'remote-ready-via-proxy': ['Il computer ha raggiunto l’endpoint {provider} {endpointSuffix} tramite il proxy configurato in circa {latencyMs} ms; la raggiungibilità dal telefono non è verificata.', 'Prova la connessione dalla rete del telefono.'],\n 'remote-rate-limited': ['L’endpoint {provider} {endpointSuffix} è raggiungibile, ma il controllo ha rilevato una limitazione temporanea.', 'Riprova più tardi; le sessioni precedenti vengono caricate su richiesta per ridurre il traffico.'],\n 'remote-rate-limited-via-proxy': ['Il computer ha raggiunto l’endpoint {provider} {endpointSuffix} tramite proxy, ma il servizio ha limitato questo controllo; la raggiungibilità dal telefono non è verificata.', 'Riprova più tardi e verifica dalla rete del telefono.'],\n 'remote-fake-ip': ['L’indirizzo Tailscale è intercettato dalla VPN o dal proxy DNS corrente e TLS non è stato stabilito.', 'Cambia nodo VPN o modalità proxy; se il problema continua, usa cpolar.'],\n 'remote-unreachable': ['{provider} risulta pronto, ma l’endpoint {endpointSuffix} non è raggiungibile.', 'Riconnettiti; se il problema continua, controlla lo stato del provider.'],\n 'remote-needs-login': ['Tailscale attende il completamento dell’accesso.', 'Torna ad Accesso remoto e continua l’accesso.'],\n 'remote-connecting': ['Il canale remoto {provider} è ancora in connessione.', 'Attendi qualche istante e ripeti il controllo.'],\n 'remote-controller-error': ['Il controller {provider} ha segnalato {controllerCode}.', 'Torna ad Accesso remoto e riconnettiti.'],\n 'phone-network-unknown': ['Il computer non può stabilire se il router isola il telefono.', 'Verifica che telefono e computer usino la stessa rete, poi disattiva rete ospiti o isolamento AP.'],\n },\n zh: {\n 'lan-setup-required': ['尚未完成局域网初始化。', '返回局域网页选择网卡并完成配置。'],\n 'versions-current': ['已显示插件、DSH 和 Android App 最低版本。', ''],\n 'network-unavailable': ['已保存的局域网网卡当前不可用。', '重新运行 dsh-mobile setup。'],\n 'network-interface': ['正在使用网卡 {interfaceName}。', ''],\n 'network-fixed': ['当前使用固定局域网配置。', ''],\n 'lan-ready': ['局域网网关正在监听 {endpointSuffix},配对入口可用。', ''],\n 'lan-off': ['局域网访问当前未开启。', '手机需要直连时开启局域网访问。'],\n 'firewall-ready': ['局域网 TCP 与发现防火墙规则已启用。', ''],\n 'firewall-missing': ['未找到完整的局域网防火墙规则。', '以管理员身份运行 dsh-mobile setup 并检查防火墙规则。'],\n 'firewall-unknown': ['系统未允许插件读取防火墙状态。', '若手机找不到电脑,请以管理员身份运行 setup。'],\n 'remote-off': ['{provider} 远程访问当前未启用。', ''],\n 'remote-origin-ready': ['私有 HTTP 后端已监听;尚未验证公网 HTTPS。', '请用手机核对反向代理、公网证书、含端口的外部 Host 及 WebSocket 访问。HTTP 后端必须留在私网。'],\n 'remote-ready': ['{provider} 端点 {endpointSuffix} 可达,往返约 {latencyMs} ms。', ''],\n 'remote-ready-via-proxy': ['电脑经已配置代理访问 {provider} 端点 {endpointSuffix},约 {latencyMs} ms;尚未验证手机网络是否可达。', '请用手机当前网络实际连接测试。'],\n 'remote-rate-limited': ['{provider} 端点 {endpointSuffix} 可达,但本次检查观察到服务限流。', '稍后重试;旧会话会按需加载以减少流量。'],\n 'remote-rate-limited-via-proxy': ['电脑经代理访问 {provider} 端点 {endpointSuffix} 时遇到限流;尚未验证手机网络是否可达。', '稍后重试,并用手机当前网络实际连接测试。'],\n 'remote-fake-ip': ['Tailscale 地址被当前 VPN 或 DNS 代理接管,TLS 链路未建立。', '切换 VPN 节点或代理模式;仍失败时改用 cpolar。'],\n 'remote-unreachable': ['{provider} 显示已就绪,但端点 {endpointSuffix} 暂不可达。', '点击重新连接;仍失败时检查提供方状态。'],\n 'remote-needs-login': ['Tailscale 正在等待完成登录。', '返回远程访问并继续登录。'],\n 'remote-connecting': ['{provider} 远程通道仍在连接。', '等待片刻后重新检查。'],\n 'remote-controller-error': ['{provider} 控制器报告 {controllerCode}。', '返回远程访问并重新连接。'],\n 'phone-network-unknown': ['电脑无法判断路由器是否隔离了手机。', '确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。'],\n },\n} as const\n\n// ── 接入既有 frps(attach)与自签穿透档:三语必须同时补齐(N12 断言键集一致) ──\nObject.assign(MOBILE_CONTROL_MESSAGES.en, {\n frpMode: 'Provisioning mode', frpModeDeploy: 'Deploy frps here (managed)', frpModeAttach: 'Use my existing frps (no SSH, no changes)',\n frpModeHint: 'Attach manages frpc and, if selected, local ingress certificates. It never installs, edits, or restarts your frps.',\n frpEntryTls: 'Entry certificate', frpEntryTlsPublic: 'Public-CA certificate (Caddy, recommended)', frpEntryTlsSelfSigned: 'Self-signed passthrough (no public certificate at all)',\n frpVhostHttpPort: 'frps vhostHTTPPort', frpVhostHttpPortHint: 'The loopback plaintext vhost port of your own frps (upstream default 7080).',\n frpPublicPort: 'Public entry port', frpPublicPortHint: 'Raw TCP proxy port used as the public HTTPS entry (default 33080, never 3080/3443/3444).',\n frpAttachPlan: 'Copy attach runbook', frpAttachPlanCopied: 'Attach runbook copied: local frpc.toml plus the VPS checklist.', frpAttachPlanFailed: 'Could not build the attach runbook: {error}',\n frpAttachCertOk: 'Ingress certificate valid for {days} more days; the plugin checks whether its leaf needs renewal.', frpAttachCertExpiring: 'Ingress certificate expires in {days} days; the plugin will try to renew the leaf under the existing CA.', frpAttachCertExpired: 'Ingress certificate has expired. Reconnect and check the certificate error.', frpAttachCertUnknown: 'Ingress certificate is missing or unreadable. Check the certificate error before reconnecting.',\n frpAttachCaOk: 'Ingress CA valid for {days} more days.', frpAttachCaExpiring: 'Ingress CA expires in {days} days; replacing it will require pairing every phone again.',\n frpAttachSelfCheck: 'Run self-check', frpAttachFrpsReachable: 'frps control TCP port reachable', frpAttachFrpsUnreachable: 'frps control TCP port unreachable', frpAttachEntryReachable: 'public entry TCP port reachable (identity not checked)', frpAttachEntryUnreachable: 'public entry TCP port unreachable',\n frpAttachModeRequiresVhostPort: 'Attach mode needs the real vhostHTTPPort of your frps; the plugin will not guess 7080.', frpAttachCertUnknownError: 'The self-signed ingress certificate is missing or unreadable; inspect the local certificate files before reconnecting.', frpEntryTlsInvalid: 'This entry certificate mode cannot be provisioned with the selected mode.',\n frpSelfSignedRequiresPublicIpv4: 'Self-signed TCP ingress requires a public IPv4 address. Use a public-CA HTTPS entry for a domain.', frpIngressCaExpired: 'The local ingress CA has expired. Remove the FRP component from this panel, configure it again, and re-pair every phone.', frpIngressCaInvalid: 'The local ingress CA is missing or damaged. Inspect the files; if rebuilding it, use this panel’s FRP cleanup and re-pair every phone.', frpIngressCaChanged: 'The ingress CA differs from the saved fingerprint, so the channel stopped. Inspect the files; if rebuilding it, clean up FRP and re-pair every phone.', frpIngressRenewalFailed: 'The ingress certificate could not be renewed. Check the FRP certificate files and reconnect; do not replace the CA silently.',\n frpAttachStepWriteSnippet: 'Create the Caddy snippet', frpAttachStepAddImport: 'Add the import line and reload Caddy', frpAttachStepIssueCert: 'Issue the public-IP certificate', frpAttachStepCertTimer: 'Confirm the renewal timer', frpAttachStepVerifyHttps: 'Verify the public HTTPS entry',\n frpAttachStepOpenPort: 'Open the public entry port', frpAttachStepVerifyFrps: 'Confirm your frps is ready', frpAttachStepVerifyEntry: 'Verify the tunnel end to end',\n frpAttachToken: 'Copy frpc.toml with token', frpAttachTokenCopied: 'The plaintext frpc.toml is copied. It contains the shared token and stays in the clipboard until you clear it — do not paste it anywhere public.', frpAttachTokenFailed: 'Could not copy the frpc.toml with its token: {error}',\n frpAttachTokenReenter: 'Re-enter the shared token first. The plugin never returns a saved token to this page.', frpStep2TitleAttach: '2 · Check your VPS', frpStep2TextAttach: 'Check your existing frps and copy a VPS checklist; the plugin does not change the server.', frpStep4TextAttach: 'Save the local client configuration and verify that the public endpoint reaches this computer.', frpAppRequirementSelfSigned: 'Self-signed ingress requires an Android app that can pin a remote CA. Older apps without this feature cannot use this entry.', frpConfigurationMissingAttach: 'Enter your existing frps details, install frpc locally, then save and verify the connection.', purgeFrpConfirmSelfSigned: 'Stop FRP and delete its local frpc, token, configuration, certificates, private keys, identity record, staging files, and logs? Every phone paired through this self-signed entry must be paired again. Your VPS is unchanged.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.it, {\n frpMode: 'Modalità di provisioning', frpModeDeploy: 'Installa frps qui (gestito)', frpModeAttach: 'Usa il mio frps esistente (niente SSH, nessuna modifica)',\n frpModeHint: 'La modalità attach gestisce frpc e, se scelti, i certificati locali. Non installa, modifica o riavvia il tuo frps.',\n frpEntryTls: 'Certificato di ingresso', frpEntryTlsPublic: 'Certificato da CA pubblica (Caddy, consigliato)', frpEntryTlsSelfSigned: 'Passthrough autofirmato (nessun certificato pubblico)',\n frpVhostHttpPort: 'vhostHTTPPort di frps', frpVhostHttpPortHint: 'Porta vhost in chiaro su loopback del tuo frps (predefinita 7080).',\n frpPublicPort: 'Porta pubblica di ingresso', frpPublicPortHint: 'Porta del proxy TCP grezzo usata come ingresso HTTPS pubblico (predefinita 33080, mai 3080/3443/3444).',\n frpAttachPlan: 'Copia il runbook attach', frpAttachPlanCopied: 'Runbook attach copiato: frpc.toml locale più la checklist VPS.', frpAttachPlanFailed: 'Impossibile creare il runbook attach: {error}',\n frpAttachCertOk: 'Certificato di ingresso valido per altri {days} giorni; il plugin verifica se la foglia va rinnovata.', frpAttachCertExpiring: 'Il certificato di ingresso scade tra {days} giorni; il plugin proverà a rinnovare la foglia con la CA esistente.', frpAttachCertExpired: 'Il certificato di ingresso è scaduto. Riconnettiti e controlla l’errore del certificato.', frpAttachCertUnknown: 'Certificato di ingresso mancante o illeggibile. Controlla l’errore prima di riconnetterti.',\n frpAttachCaOk: 'CA di ingresso valida per altri {days} giorni.', frpAttachCaExpiring: 'La CA di ingresso scade tra {days} giorni; sostituirla richiederà una nuova associazione di ogni telefono.',\n frpAttachSelfCheck: 'Esegui autoverifica', frpAttachFrpsReachable: 'porta TCP di controllo frps raggiungibile', frpAttachFrpsUnreachable: 'porta TCP di controllo frps non raggiungibile', frpAttachEntryReachable: 'porta TCP pubblica raggiungibile (identità non verificata)', frpAttachEntryUnreachable: 'porta TCP pubblica non raggiungibile',\n frpAttachModeRequiresVhostPort: 'La modalità attach richiede il vhostHTTPPort reale del tuo frps; il plugin non presume 7080.', frpAttachCertUnknownError: 'Il certificato di ingresso autofirmato manca o è illeggibile; controlla i file locali prima di riconnetterti.', frpEntryTlsInvalid: 'Questa modalità di certificato non è compatibile con la modalità selezionata.',\n frpSelfSignedRequiresPublicIpv4: 'L’ingresso TCP autofirmato richiede un IPv4 pubblico. Per un dominio usa un ingresso HTTPS con CA pubblica.', frpIngressCaExpired: 'La CA locale è scaduta. Rimuovi FRP da questo pannello, riconfiguralo e associa di nuovo ogni telefono.', frpIngressCaInvalid: 'La CA locale manca o è danneggiata. Controlla i file; se la ricrei, usa la pulizia FRP del pannello e associa di nuovo ogni telefono.', frpIngressCaChanged: 'La CA non corrisponde all’impronta salvata, quindi il canale è stato fermato. Controlla i file; se la ricrei, pulisci FRP e associa di nuovo i telefoni.', frpIngressRenewalFailed: 'Impossibile rinnovare il certificato di ingresso. Controlla i file e riconnettiti senza sostituire la CA automaticamente.',\n frpAttachStepWriteSnippet: 'Crea lo snippet Caddy', frpAttachStepAddImport: 'Aggiungi la riga import e ricarica Caddy', frpAttachStepIssueCert: 'Emetti il certificato per l’IP pubblico', frpAttachStepCertTimer: 'Verifica il timer di rinnovo', frpAttachStepVerifyHttps: 'Verifica l’ingresso HTTPS pubblico',\n frpAttachStepOpenPort: 'Apri la porta pubblica di ingresso', frpAttachStepVerifyFrps: 'Verifica che il tuo frps sia pronto', frpAttachStepVerifyEntry: 'Verifica il tunnel end-to-end',\n frpAttachToken: 'Copia frpc.toml con token', frpAttachTokenCopied: 'frpc.toml in chiaro copiato. Contiene il token condiviso e resta negli appunti finché non li svuoti: non incollarlo in luoghi pubblici.', frpAttachTokenFailed: 'Impossibile copiare frpc.toml con il token: {error}',\n frpAttachTokenReenter: 'Inserisci di nuovo il token condiviso. Il plugin non restituisce mai un token salvato a questa pagina.', frpStep2TitleAttach: '2 · Controlla il VPS', frpStep2TextAttach: 'Controlla il tuo frps e copia la checklist VPS; il plugin non modifica il server.', frpStep4TextAttach: 'Salva la configurazione locale e verifica che l’ingresso pubblico raggiunga questo computer.', frpAppRequirementSelfSigned: 'L’ingresso autofirmato richiede un’app Android capace di fissare una CA remota. Le vecchie versioni senza questa funzione non possono usarlo.', frpConfigurationMissingAttach: 'Inserisci i dati del tuo frps, installa frpc localmente, poi salva e verifica la connessione.', purgeFrpConfirmSelfSigned: 'Arrestare FRP e cancellare frpc, token, configurazione, certificati, chiavi private, identità, file temporanei e log locali? Ogni telefono associato a questo ingresso autofirmato dovrà essere associato di nuovo. Il VPS non cambia.',\n})\nObject.assign(MOBILE_CONTROL_MESSAGES.zh, {\n frpMode: '置备方式', frpModeDeploy: '由插件部署 frps(托管)', frpModeAttach: '接入我已有的 frps(零 SSH,不改动)',\n frpModeHint: '接入模式只管理本机 frpc 与按需生成的入口证书,不会安装、修改或重启你已有的 frps。',\n frpEntryTls: '入口证书档', frpEntryTlsPublic: '公开 CA 证书(Caddy,推荐)', frpEntryTlsSelfSigned: '自签穿透(完全不用公开证书)',\n frpVhostHttpPort: 'frps 的 vhostHTTPPort', frpVhostHttpPortHint: '你自己 frps 的回环明文 vhost 端口(上游默认 7080)。',\n frpPublicPort: '公网入口端口', frpPublicPortHint: '作为公网 HTTPS 入口的 TCP 透传端口(默认 33080,不得为 3080/3443/3444)。',\n frpAttachPlan: '复制接入清单', frpAttachPlanCopied: '接入清单已复制:本机 frpc.toml 与 VPS 侧待办。', frpAttachPlanFailed: '生成接入清单失败:{error}',\n frpAttachCertOk: '入口证书还有 {days} 天有效期;插件会检查是否需要续签叶子证书。', frpAttachCertExpiring: '入口证书 {days} 天后到期;插件会尝试沿用现有 CA 续签叶子证书。', frpAttachCertExpired: '入口证书已过期,请重新连接并查看具体证书错误。', frpAttachCertUnknown: '入口证书缺失或不可读,请先查看具体证书错误。',\n frpAttachCaOk: '入口 CA 还有 {days} 天有效期。', frpAttachCaExpiring: '入口 CA {days} 天后到期;更换 CA 后所有手机需重新配对。',\n frpAttachSelfCheck: '运行自检', frpAttachFrpsReachable: 'frps 控制 TCP 端口可达', frpAttachFrpsUnreachable: 'frps 控制 TCP 端口不可达', frpAttachEntryReachable: '公网入口 TCP 端口可达(未验证身份)', frpAttachEntryUnreachable: '公网入口 TCP 端口不可达',\n frpAttachModeRequiresVhostPort: '接入模式必须填写你 frps 真实的 vhostHTTPPort,插件不会替你假定 7080。', frpAttachCertUnknownError: '自签入口证书缺失或不可读,请检查本机证书文件后再连接。', frpEntryTlsInvalid: '当前置备方式无法使用该入口证书档。',\n frpSelfSignedRequiresPublicIpv4: '自签 TCP 入口需要公网 IPv4 地址;域名请使用受公共 CA 信任的 HTTPS 入口。', frpIngressCaExpired: '本机入口 CA 已过期。请在此面板彻底清理 FRP 并重新配置,所有手机需重新配对。', frpIngressCaInvalid: '本机入口 CA 缺失或损坏。请检查证书文件;如需重建,在此面板清理 FRP 后重新配对。', frpIngressCaChanged: '入口 CA 与保存的指纹不符,通道已停止。请核查证书文件;如需重建,清理 FRP 后重新配对。', frpIngressRenewalFailed: '入口证书续签失败。请检查 FRP 证书文件并重新连接,不要静默更换 CA。',\n frpAttachStepWriteSnippet: '新增 Caddy 片段', frpAttachStepAddImport: '加入 import 行并重载 Caddy', frpAttachStepIssueCert: '为公网 IP 签发证书', frpAttachStepCertTimer: '确认续期定时器', frpAttachStepVerifyHttps: '验证公网 HTTPS 入口',\n frpAttachStepOpenPort: '放行公网入口端口', frpAttachStepVerifyFrps: '确认既有 frps 已就绪', frpAttachStepVerifyEntry: '端到端验证隧道',\n frpAttachToken: '复制含 Token 的 frpc.toml', frpAttachTokenCopied: '含 Token 的 frpc.toml(明文)已复制:它会留在系统剪贴板中,请勿粘贴到公开位置。', frpAttachTokenFailed: '复制含 Token 的 frpc.toml 失败:{error}',\n frpAttachTokenReenter: '请先重新输入共享 Token。插件不会把已保存的 Token 返回页面。', frpStep2TitleAttach: '2 · 核对既有服务器', frpStep2TextAttach: '核对既有 frps 配置并复制 VPS 操作清单;插件不会改动服务器。', frpStep4TextAttach: '保存本机 frpc 配置,并验证公开入口确实连接到这台电脑。', frpAppRequirementSelfSigned: '自签入口需要支持远程 CA 固定的 Android App;不具备此功能的旧版 App 无法使用。', frpConfigurationMissingAttach: '填写既有 frps 信息,在本机安装 frpc,然后保存并验证连接。', purgeFrpConfirmSelfSigned: '要停止 FRP 并删除本机 frpc、Token、配置、证书、私钥、身份记录、临时文件和日志吗?通过此自签入口配对的手机都需重新配对;VPS 不会改变。',\n})\n","/** Loopback-only HTTP vhost port used between Caddy and frps. */\nexport const FRP_VHOST_HTTP_PORT = 7080\n\n/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */\nexport const FRP_CADDY_SNIPPET_PATH = '/etc/caddy/dsh-mobile-dsh.caddy'\n\n/** First line of the owned snippet; also the legacy whole-file marker. */\nexport const FRP_CADDY_SNIPPET_MARKER = '# Managed by DSH Mobile - snippet, safe to delete'\n\n/** Exact line the main Caddyfile must contain (uncommented) for the site to load. */\nexport const FRP_CADDY_IMPORT_LINE = `import ${FRP_CADDY_SNIPPET_PATH}`\n\n/** Directory holding the public-IPv4 certificates installed by certbot. */\nexport const FRP_CADDY_IP_CERT_DIR = '/var/lib/caddy/dsh-mobile-certs'\n\n/**\n * Entry TLS mode for the FRP channel.\n *\n * - `public-ip-cert` (default): the VPS Caddy terminates TLS with a public-CA\n * certificate and reverse-proxies the plaintext vhost to frps.\n * - `self-signed`: no Caddy and no public certificate at all. frps publishes a\n * raw TCP proxy and the DSH gateway terminates TLS itself with a leaf signed\n * by its own pairing CA, which the Android app pins through `ca.cer`.\n */\nexport type FrpEntryTls = 'public-ip-cert' | 'self-signed'\n\n/** Optional overrides for the generated Caddy site; every field defaults to the upstream value. */\nexport interface CaddySiteOptions {\n readonly certDir?: string\n /**\n * The user's real `vhostHTTPPort`. `attach` mode must never assume the\n * upstream 7080: a wrong port silently disables the plaintext-exposure gate.\n */\n readonly vhostHttpPort?: number\n /** `self-signed` is a TCP passthrough and never produces a Caddy site. */\n readonly entryTls?: FrpEntryTls\n}\n\nfunction publicIpv4Address(value: string): boolean {\n const parts = value.split('.')\n return parts.length === 4 && parts.every(part => /^(?:0|[1-9][0-9]{0,2})$/u.test(part)\n && Number(part) <= 255)\n}\n\nfunction publicDnsHostname(value: string): boolean {\n return value.length <= 253 && value.includes('.') && !/^[0-9.]+$/u.test(value)\n && !value.includes(':') && value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\nfunction parsePublicOrigin(publicOrigin: string): string {\n let url: URL\n try { url = new URL(publicOrigin) } catch { throw new Error('frp_template_input_invalid') }\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (!publicIpv4Address(url.hostname) && !publicDnsHostname(url.hostname))) {\n throw new Error('frp_template_input_invalid')\n }\n return url.hostname\n}\n\nfunction resolveVhostHttpPort(options: CaddySiteOptions): number {\n const port = options.vhostHttpPort ?? FRP_VHOST_HTTP_PORT\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) throw new Error('frp_template_input_invalid')\n return port\n}\n\n/**\n * A self-signed entry deliberately never reaches Caddy: the tunnel is a raw TCP\n * passthrough and the DSH gateway is the TLS endpoint. Refuse loudly instead of\n * emitting a Caddy site that would need the gateway's private CA on the VPS.\n */\nfunction rejectSelfSignedCaddySite(options: CaddySiteOptions): void {\n if (options.entryTls === 'self-signed') throw new Error('frp_entry_tls_invalid')\n}\n\n/**\n * Build the Caddy site for one public host (without markers or import wiring).\n *\n * The second parameter accepts either a bare certificate directory (the legacy\n * signature) or a full {@link CaddySiteOptions} object, so existing callers keep\n * working unchanged.\n */\nexport function createCaddySite(publicHost: string, options: CaddySiteOptions | string = {}): string {\n const resolved: CaddySiteOptions = typeof options === 'string' ? { certDir: options } : options\n rejectSelfSignedCaddySite(resolved)\n const certDir = resolved.certDir ?? FRP_CADDY_IP_CERT_DIR\n const vhostHttpPort = resolveVhostHttpPort(resolved)\n if (publicIpv4Address(publicHost)) {\n return [\n '{',\n ` default_sni ${publicHost}`,\n '}',\n '',\n `http://${publicHost} {`,\n ` redir https://${publicHost}{uri} permanent`,\n '}',\n '',\n `https://${publicHost} {`,\n ` tls ${certDir}/fullchain.pem ${certDir}/privkey.pem`,\n ` reverse_proxy 127.0.0.1:${String(vhostHttpPort)}`,\n '}',\n '',\n ].join('\\n')\n }\n if (!publicDnsHostname(publicHost)) throw new Error('frp_template_input_invalid')\n return [\n `${publicHost} {`,\n ` reverse_proxy 127.0.0.1:${String(vhostHttpPort)}`,\n '}',\n '',\n ].join('\\n')\n}\n\n/** Manual certbot steps for a public-IPv4 origin (Caddy cannot issue IP certificates itself). */\nexport function manualIpCertificateGuide(publicHost: string): string {\n return [\n '# Public-IPv4 manual HTTPS: Caddy cannot issue IP certificates by itself.',\n '# On the VPS (Ubuntu/Debian, port 80 reachable from the internet), run once as root:',\n '# apt-get install -y python3-venv',\n '# python3 -m venv /opt/dsh-mobile/certbot-venv',\n \"# /opt/dsh-mobile/certbot-venv/bin/pip install 'certbot==5.8.0'\",\n '# systemctl stop caddy || true',\n `# /opt/dsh-mobile/certbot-venv/bin/certbot certonly --standalone --preferred-profile shortlived --ip-address ${publicHost} --agree-tos --register-unsafely-without-email --non-interactive --keep-until-expiring`,\n '# install -d -m 0750 -o caddy -g caddy /var/lib/caddy/dsh-mobile-certs',\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/fullchain.pem /var/lib/caddy/dsh-mobile-certs/fullchain.pem`,\n `# install -m 0640 -o caddy -g caddy /etc/letsencrypt/live/${publicHost}/privkey.pem /var/lib/caddy/dsh-mobile-certs/privkey.pem`,\n '# systemctl start caddy',\n '# The site below already references those paths. Certificates last about 6 days: re-run certonly before expiry.',\n '#',\n ].join('\\n')\n}\n\n/**\n * Build the only supported frps config and Caddy snippet from validated user inputs.\n *\n * Default `options` reproduce the upstream artefact byte for byte; `attach` mode\n * passes the user's real vhost port, and `self-signed` is rejected because that\n * mode publishes a raw TCP proxy instead of an HTTP vhost.\n */\nexport function createRestrictedFrpServerTemplate(\n serverPort: number,\n token: string,\n publicOrigin: string,\n options: CaddySiteOptions = {},\n): string {\n if (!Number.isSafeInteger(serverPort) || serverPort < 1 || serverPort > 65_535\n || token.length < 16 || token.length > 512 || /[\\s\\u0000-\\u001f\\u007f]/u.test(token)) {\n throw new Error('frp_template_input_invalid')\n }\n rejectSelfSignedCaddySite(options)\n const vhostHttpPort = resolveVhostHttpPort(options)\n const certDir = options.certDir ?? FRP_CADDY_IP_CERT_DIR\n const publicHost = parsePublicOrigin(publicOrigin)\n const lines = [\n '# frps.toml — save as /etc/dsh-mobile/frps.toml, then start the frps service.',\n `bindPort = ${String(serverPort)}`,\n 'proxyBindAddr = \"127.0.0.1\"',\n `vhostHTTPPort = ${String(vhostHttpPort)}`,\n 'auth.method = \"token\"',\n `auth.token = ${JSON.stringify(token)}`,\n '',\n `# Caddy — save the site below as ${FRP_CADDY_SNIPPET_PATH},`,\n '# then make sure your Caddyfile contains exactly this line at the TOP of the file',\n '# (create the file with just this line if needed; globals must precede sites):',\n `# ${FRP_CADDY_IMPORT_LINE}`,\n '# finally run: caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n '# Uninstall later removes only this snippet file and the import line; your own Caddy content is kept.',\n `${FRP_CADDY_SNIPPET_MARKER}`,\n createCaddySite(publicHost, { certDir, vhostHttpPort }).trimEnd(),\n '',\n ]\n if (publicIpv4Address(publicHost)) lines.push(manualIpCertificateGuide(publicHost), '')\n return lines.join('\\n')\n}","/**\n * Browser-safe IP literal classification.\n *\n * `node:net` cannot be imported from the browser Client face, so the pure\n * predicate lives here instead. Semantics are deliberately identical to\n * `net.isIP`: strict dotted-quad IPv4 (no leading zeros, no signs, no\n * whitespace), IPv6 with at most one `::`, at most four hex digits per group,\n * an optional dotted-quad tail in the final position, and an optional\n * non-empty zone id after a single `%`.\n *\n * This module is dependency-free and side-effect-free on purpose: it is the\n * single classification source shared by every face of the plugin — the Node\n * side (`config.ts`, `cloudflared-tunnel.ts`, `origin-proxy-config.ts`,\n * `vps-deploy.ts`, `gateway.ts`, `network.ts`, `frp-config.ts`) and the browser\n * Client face that cannot reach `node:net` — so both classify a given literal\n * identically. Callers rely on the strict reading above and never post-process\n * the literal afterwards, so changing the accept/reject boundary here changes\n * it everywhere, which is the intended contract.\n */\n\n/** Strict dotted-quad IPv4: POSIX `inet_pton` rules, so `01.1.1.1` is rejected. */\nfunction isStrictIpv4(value: string): boolean {\n const parts = value.split('.')\n if (parts.length !== 4) return false\n for (const part of parts) {\n if (!/^(?:0|[1-9][0-9]{0,2})$/u.test(part)) return false\n if (Number(part) > 255) return false\n }\n return true\n}\n\n/**\n * Count the 16-bit groups one side of an IPv6 address occupies.\n *\n * A dotted-quad tail counts as two groups and is accepted only where the caller\n * allows it — that is, only in the final label of the whole address.\n *\n * @returns the group count, or `-1` when any label is malformed.\n */\nfunction countIpv6Groups(labels: readonly string[], allowDottedQuad: boolean): number {\n let groups = 0\n for (let index = 0; index < labels.length; index += 1) {\n const label = labels[index] as string\n if (label.includes('.')) {\n if (index !== labels.length - 1 || !allowDottedQuad || !isStrictIpv4(label)) return -1\n groups += 2\n continue\n }\n if (!/^[\\da-f]{1,4}$/iu.test(label)) return -1\n groups += 1\n }\n return groups\n}\n\n/**\n * Classify an IP literal.\n *\n * @param value - candidate address; non-strings are never an IP.\n * @returns `4` for IPv4, `6` for IPv6, `0` when the value is neither.\n */\nexport function isIP(value: unknown): 0 | 4 | 6 {\n if (typeof value !== 'string') return 0\n if (isStrictIpv4(value)) return 4\n let text = value\n const zone = text.indexOf('%')\n if (zone !== -1) {\n const suffix = text.slice(zone + 1)\n if (suffix.length === 0 || suffix.includes('%')) return 0\n text = text.slice(0, zone)\n }\n if (text.length === 0) return 0\n const halves = text.split('::')\n if (halves.length > 2) return 0\n if (halves.length === 1) {\n return countIpv6Groups(text.split(':'), true) === 8 ? 6 : 0\n }\n const leftText = halves[0] as string\n const rightText = halves[1] as string\n const left = leftText === '' ? [] : leftText.split(':')\n const right = rightText === '' ? [] : rightText.split(':')\n // `::` must stand for at least one group, and a dotted-quad tail is valid only\n // in the final label of the final non-empty side — so `1.2.3.4::` is rejected.\n const leftGroups = countIpv6Groups(left, false)\n const rightGroups = countIpv6Groups(right, right.length > 0)\n if (leftGroups < 0 || rightGroups < 0) return 0\n return leftGroups + rightGroups >= 8 ? 0 : 6\n}","import { connect } from 'node:net'\nimport { isIP } from './ip.js'\n\n/** A parsed IP network used to authorize directly connected clients. */\nexport interface ParsedCidr {\n readonly bits: 32 | 128\n readonly network: bigint\n readonly prefix: number\n readonly source: string\n}\n\n/** A normalized public authority. A missing port is filled from the bound listener. */\nexport interface AuthoritySpec {\n readonly hostname: string\n readonly port?: number\n}\n\nfunction parseIpv4(address: string): bigint {\n const parts = address.split('.')\n if (parts.length !== 4) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n let value = 0n\n for (const part of parts) {\n if (!/^\\d{1,3}$/u.test(part)) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n const octet = Number(part)\n if (octet > 255) throw new Error(`invalid IPv4 address ${JSON.stringify(address)}`)\n value = (value << 8n) | BigInt(octet)\n }\n return value\n}\n\nfunction parseIpv6Part(part: string, address: string): number[] {\n if (part.includes('.')) {\n const ipv4 = parseIpv4(part)\n return [Number((ipv4 >> 16n) & 0xffffn), Number(ipv4 & 0xffffn)]\n }\n if (!/^[\\da-f]{1,4}$/iu.test(part)) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n return [Number.parseInt(part, 16)]\n}\n\nfunction parseIpv6(address: string): bigint {\n const withoutZone = address.split('%', 1)[0] ?? address\n if (withoutZone.split('::').length > 2) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n const [leftText, rightText] = withoutZone.split('::')\n const left = leftText === '' ? [] : leftText!.split(':').flatMap(part => parseIpv6Part(part, address))\n const right = rightText === undefined || rightText === ''\n ? []\n : rightText.split(':').flatMap(part => parseIpv6Part(part, address))\n const omitted = 8 - left.length - right.length\n if (rightText === undefined ? omitted !== 0 : omitted < 1) {\n throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n }\n const groups = [...left, ...Array.from({ length: omitted }, () => 0), ...right]\n if (groups.length !== 8) throw new Error(`invalid IPv6 address ${JSON.stringify(address)}`)\n return groups.reduce((value, group) => (value << 16n) | BigInt(group), 0n)\n}\n\nfunction mappedIpv4(address: string): string | undefined {\n const match = /^::ffff:(\\d{1,3}(?:\\.\\d{1,3}){3})$/iu.exec(address)\n return match?.[1]\n}\n\nfunction parseIp(address: string): { bits: 32 | 128; value: bigint } {\n const unwrapped = address.startsWith('[') && address.endsWith(']') ? address.slice(1, -1) : address\n const mapped = mappedIpv4(unwrapped)\n if (mapped !== undefined) return { bits: 32, value: parseIpv4(mapped) }\n const version = isIP(unwrapped.split('%', 1)[0] ?? unwrapped)\n if (version === 4) return { bits: 32, value: parseIpv4(unwrapped) }\n if (version === 6) return { bits: 128, value: parseIpv6(unwrapped) }\n throw new Error(`invalid IP address ${JSON.stringify(address)}`)\n}\n\n/** Parse and canonicalize one IPv4 or IPv6 CIDR. */\nexport function parseCidr(source: string): ParsedCidr {\n const slash = source.lastIndexOf('/')\n if (slash <= 0 || slash === source.length - 1) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const address = source.slice(0, slash)\n const parsed = parseIp(address)\n const prefixText = source.slice(slash + 1)\n if (!/^\\d{1,3}$/u.test(prefixText)) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const prefix = Number(prefixText)\n if (prefix > parsed.bits) throw new Error(`invalid CIDR ${JSON.stringify(source)}`)\n const hostBits = BigInt(parsed.bits - prefix)\n const mask = hostBits === BigInt(parsed.bits)\n ? 0n\n : ((1n << BigInt(parsed.bits)) - 1n) ^ ((1n << hostBits) - 1n)\n const network = parsed.value & mask\n if (network !== parsed.value) {\n throw new Error(`CIDR ${JSON.stringify(source)} has host bits set`)\n }\n return Object.freeze({ bits: parsed.bits, network, prefix, source })\n}\n\n/** Whether a directly connected socket address belongs to at least one allowed CIDR. */\nexport function addressAllowed(address: string | undefined, cidrs: readonly ParsedCidr[]): boolean {\n if (address === undefined) return false\n let parsed: ReturnType<typeof parseIp>\n try {\n parsed = parseIp(address)\n } catch {\n return false\n }\n return cidrs.some((cidr) => {\n if (cidr.bits !== parsed.bits) return false\n const hostBits = BigInt(cidr.bits - cidr.prefix)\n const mask = hostBits === BigInt(cidr.bits)\n ? 0n\n : ((1n << BigInt(cidr.bits)) - 1n) ^ ((1n << hostBits) - 1n)\n return (parsed.value & mask) === cidr.network\n })\n}\n\n/** Whether an IP literal is loopback and therefore eligible for HTTP-only development. */\nexport function isLoopbackAddress(address: string): boolean {\n try {\n const parsed = parseIp(address)\n if (parsed.bits === 32) return (parsed.value >> 24n) === 127n\n return parsed.value === 1n\n } catch {\n return false\n }\n}\n\n/**\n * IPv4 ranges that are never a public VPS endpoint (IANA special-purpose,\n * private, shared, loopback, link-local, documentation, benchmark, multicast,\n * and reserved space). Kept in sync with Android `RemoteHostPolicy`.\n */\nconst NON_ROUTABLE_IPV4_RANGES: ReadonlyArray<readonly [network: bigint, prefix: number]> = Object.freeze([\n [0x00000000n, 8], // \"This network\" / software scope\n [0x0a000000n, 8], // Private-Use (RFC 1918)\n [0x64400000n, 10], // Shared address space / CGNAT (RFC 6598)\n [0x7f000000n, 8], // Loopback (RFC 1122)\n [0xa9fe0000n, 16], // Link-local (RFC 3927)\n [0xac100000n, 12], // Private-Use (RFC 1918)\n [0xc0000000n, 24], // IETF protocol assignments (RFC 6890)\n [0xc0000200n, 24], // Documentation TEST-NET-1 (RFC 5737)\n [0xc01fc400n, 24], // AS112-v4 (RFC 7534)\n [0xc034c100n, 24], // AMT relay (RFC 7450)\n [0xc0586300n, 24], // 6to4 relay anycast, deprecated (RFC 7526)\n [0xc0a80000n, 16], // Private-Use (RFC 1918)\n [0xc0af3000n, 24], // Direct delegation AS112 (RFC 7535)\n [0xc6120000n, 15], // Benchmarking (RFC 2544)\n [0xc6336400n, 24], // Documentation TEST-NET-2 (RFC 5737)\n [0xcb007100n, 24], // Documentation TEST-NET-3 (RFC 5737)\n [0xe0000000n, 4], // Multicast (RFC 1112, incl. MCAST-TEST-NET)\n [0xf0000000n, 4], // Reserved for future use + broadcast (RFC 1112)\n])\n\nfunction parseStrictIpv4Octets(address: string): readonly [number, number, number, number] | undefined {\n const parts = address.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9][0-9]{0,2})$/u.test(part)) return undefined\n const octet = Number(part)\n if (octet > 255) return undefined\n octets.push(octet)\n }\n return octets as [number, number, number, number]\n}\n\n/**\n * Whether a dotted-quad IPv4 literal is globally routable and therefore usable\n * as a public VPS / remote HTTPS endpoint. Rejects documentation addresses such\n * as 203.0.113.10 alongside private, shared, and reserved space.\n */\nexport function isGloballyRoutableIpv4(address: string): boolean {\n const octets = parseStrictIpv4Octets(address)\n if (octets === undefined) return false\n const value = (BigInt(octets[0]) << 24n) | (BigInt(octets[1]) << 16n) | (BigInt(octets[2]) << 8n) | BigInt(octets[3])\n return !NON_ROUTABLE_IPV4_RANGES.some(([network, prefix]) => {\n if (prefix === 0) return true\n const hostBits = BigInt(32 - prefix)\n const mask = ((1n << 32n) - 1n) ^ ((1n << hostBits) - 1n)\n return (value & mask) === network\n })\n}\n\n/** Parse a bare host or host:port authority without accepting URL components. */\nexport function parseAuthority(source: string): AuthoritySpec {\n if (source.trim() !== source || source.length === 0 || /[/?#@\\\\]/u.test(source)) {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n let url: URL\n try {\n url = new URL(`https://${source}`)\n } catch {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n if (url.username !== '' || url.password !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== '') {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n const explicitPort = /\\]:\\d+$/u.test(source) || (!source.startsWith('[') && /:\\d+$/u.test(source))\n const hostname = url.hostname.toLowerCase()\n const port = explicitPort ? Number(url.port === '' ? 443 : url.port) : undefined\n if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {\n throw new Error(`invalid public authority ${JSON.stringify(source)}`)\n }\n return port === undefined ? Object.freeze({ hostname }) : Object.freeze({ hostname, port })\n}\n\nfunction formatHostname(hostname: string): string {\n return hostname.includes(':') && !hostname.startsWith('[') ? `[${hostname}]` : hostname\n}\n\n/** Resolve an authority against the actual listener port. */\nexport function resolveAuthority(spec: AuthoritySpec, listenerPort: number): string {\n return `${formatHostname(spec.hostname)}:${String(spec.port ?? listenerPort)}`\n}\n\n/** Exact Host/Origin/CIDR policy for the directly exposed listener. */\nexport class RequestTrustPolicy {\n readonly authorities: ReadonlySet<string>\n readonly origins: ReadonlySet<string>\n private readonly scheme: 'http' | 'https'\n\n constructor(\n specs: readonly AuthoritySpec[],\n listenerPort: number,\n readonly cidrs: readonly ParsedCidr[],\n tls: boolean,\n ) {\n this.scheme = tls ? 'https' : 'http'\n this.authorities = new Set(specs.map(spec => resolveAuthority(spec, listenerPort).toLowerCase()))\n this.origins = new Set([...this.authorities].map(\n authority => new URL(`${this.scheme}://${authority}`).origin.toLowerCase(),\n ))\n }\n\n /** Validate the exact Host header after WHATWG authority normalization. */\n acceptsHost(header: string | undefined): boolean {\n return this.canonicalHost(header) !== undefined\n }\n\n /** Return the canonical accepted Host authority, otherwise undefined. */\n canonicalHost(header: string | undefined): string | undefined {\n if (header === undefined || /[/?#@\\\\]/u.test(header)) return undefined\n let normalized: string\n try {\n const parsed = new URL(`${this.scheme}://${header}`)\n if (parsed.pathname !== '/' || parsed.username !== '' || parsed.password !== '') return undefined\n normalized = resolveAuthority({\n hostname: parsed.hostname,\n port: Number(parsed.port || (this.scheme === 'https' ? '443' : '80')),\n }, 80).toLowerCase()\n } catch {\n return undefined\n }\n return this.authorities.has(normalized) ? normalized : undefined\n }\n\n /** Validate an exact same-scheme browser Origin. */\n acceptsOrigin(header: string | undefined): boolean {\n return this.canonicalOrigin(header) !== undefined\n }\n\n /** Return the canonical accepted Origin, otherwise undefined. */\n canonicalOrigin(header: string | undefined): string | undefined {\n if (header === undefined) return undefined\n // 微信小程序 wx.connectSocket 会把允许的 Origin 与 undefined 用逗号合并。\n let normalized: string | undefined\n for (const part of header.split(',')) {\n const trimmed = part.trim()\n if (trimmed === 'undefined') continue\n let candidate: string\n try {\n const parsed = new URL(trimmed)\n if (parsed.pathname !== '/' || parsed.search !== '' || parsed.hash !== '' || parsed.username !== '' || parsed.password !== '') {\n return undefined\n }\n candidate = parsed.origin.toLowerCase()\n } catch {\n return undefined\n }\n if (!this.origins.has(candidate) || normalized !== undefined) return undefined\n normalized = candidate\n }\n return normalized\n }\n}\n\n/**\n * Open a plain TCP connection only to learn whether something is listening.\n *\n * Used by the on-demand self-check so the panel can tell \"frps is up\" from\n * \"frps is not listening\". A transparent proxy can acknowledge any connect, so\n * this result is advisory and never authorises a security decision on its own.\n */\nexport async function probeTcpReachable(host: string, port: number, timeoutMs = 1_500): Promise<boolean> {\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) return false\n return new Promise<boolean>((resolveProbe) => {\n let finished = false\n const socket = connect({ host, port })\n const finish = (reachable: boolean): void => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n socket.destroy()\n resolveProbe(reachable)\n }\n const timer = setTimeout(() => { finish(false) }, timeoutMs)\n timer.unref()\n socket.once('connect', () => { finish(true) })\n socket.once('error', () => { finish(false) })\n socket.once('close', () => { finish(false) })\n })\n}\n","import { randomBytes } from 'node:crypto'\nimport { isIP } from './ip.js'\nimport { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path'\nimport { isGloballyRoutableIpv4 } from './network.js'\nimport { restrictPrivateFile } from './private-file.js'\nimport { createRestrictedFrpServerTemplate, FRP_VHOST_HTTP_PORT, type FrpEntryTls } from './frp-template.js'\n\nconst MAX_SETTINGS_BYTES = 8 * 1024\n\n/** How the restricted FRP channel is provisioned on the VPS. */\nexport type FrpMode = 'deploy' | 'attach'\n\n/** Public entry port used by the self-signed TCP passthrough (never 443 by requirement). */\nexport const FRP_DEFAULT_PUBLIC_PORT = 33_080\n\n/** Ports owned by other DSH Mobile listeners; the public entry may never reuse them. */\nexport const FRP_RESERVED_PORTS: readonly number[] = Object.freeze([3080, 3443, 3444])\n\n/** Every key `settings.json` may contain. Unknown keys stay rejected. */\nconst FRP_SETTINGS_KEYS: ReadonlySet<string> = new Set([\n 'version', 'serverAddress', 'serverPort', 'token', 'publicOrigin',\n 'mode', 'entryTls', 'vhostHttpPort', 'publicPort',\n])\n\n/** Credentials and endpoints required by the restricted FRP provider. */\nexport interface FrpSettings {\n readonly version: 1\n readonly serverAddress: string\n readonly serverPort: number\n readonly token: string\n readonly publicOrigin: string\n /**\n * `deploy` (default) installs the plugin's own frps on the VPS.\n * `attach` reuses an frps that already runs there and never touches it.\n */\n readonly mode?: FrpMode\n /** `public-ip-cert` (default) or the gateway-terminated `self-signed` passthrough. */\n readonly entryTls?: FrpEntryTls\n /** The real `vhostHTTPPort` of the user's frps; defaults to the upstream 7080. */\n readonly vhostHttpPort?: number\n /** Public entry port of the self-signed TCP proxy; defaults to 33080. */\n readonly publicPort?: number\n}\n\n/** Safe FRP configuration fields returned to the desktop UI. */\nexport interface FrpConfigurationStatus {\n readonly configured: boolean\n readonly serverAddress?: string\n readonly serverPort?: number\n readonly publicOrigin?: string\n readonly vhostHttpPort: number\n readonly storagePath: string\n readonly errorCode?: string\n /** Present only when the saved configuration leaves the upstream `deploy` default. */\n readonly mode?: FrpMode\n /** Present only when the saved configuration uses the self-signed entry. */\n readonly entryTls?: FrpEntryTls\n /** Present only when a non-default public entry port is saved. */\n readonly publicPort?: number\n}\n\nfunction hostname(value: string): boolean {\n if (value.length > 253 || !value.includes('.')) return false\n return value.split('.').every(label => label.length >= 1 && label.length <= 63\n && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label))\n}\n\n/** Validate the FRP server hostname or IP address. */\nexport function validateFrpServerAddress(value: unknown): string {\n if (typeof value !== 'string' || value !== value.trim() || value.length === 0 || value.length > 253\n || /[\\s\\u0000-\\u001f\\u007f/\\\\@?#]/u.test(value)) throw new Error('frp_server_address_invalid')\n const normalized = value.toLowerCase().replace(/\\.$/u, '')\n if (isIP(normalized) === 0 && !hostname(normalized)) throw new Error('frp_server_address_invalid')\n return normalized\n}\n\n/** Validate the FRP control port. */\nexport function validateFrpServerPort(value: unknown): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) {\n throw new Error('frp_server_port_invalid')\n }\n return Number(value)\n}\n\n/** Validate a high-entropy FRP token before durable storage. */\nexport function validateFrpToken(value: unknown): string {\n if (typeof value !== 'string' || value.length < 16 || value.length > 512\n || /[\\s\\u0000-\\u001f\\u007f]/u.test(value)) throw new Error('frp_token_invalid')\n return value\n}\n\n/** Validate the public HTTPS origin used by Caddy and Android pairing. */\nexport function validateFrpPublicOrigin(value: unknown): string {\n if (typeof value !== 'string' || value.length > 512) throw new Error('frp_public_origin_invalid')\n let url: URL\n try { url = new URL(value) } catch { throw new Error('frp_public_origin_invalid') }\n const publicHost = url.hostname\n if (url.protocol !== 'https:' || url.port !== '' || url.pathname !== '/' || url.search !== '' || url.hash !== ''\n || url.username !== '' || url.password !== '' || (isIP(publicHost) !== 4 && !hostname(publicHost))) {\n throw new Error('frp_public_origin_invalid')\n }\n // Documentation and other non-routable IPv4 literals (e.g. 203.0.113.10)\n // can never be a real VPS endpoint; reject them instead of deploying certs.\n if (isIP(publicHost) === 4 && !isGloballyRoutableIpv4(publicHost)) throw new Error('frp_public_origin_invalid')\n return url.origin\n}\n\n/** Validate the provisioning mode. */\nexport function validateFrpMode(value: unknown): FrpMode {\n if (value !== 'deploy' && value !== 'attach') throw new Error('frp_settings_invalid')\n return value\n}\n\n/** Validate the entry TLS mode. */\nexport function validateFrpEntryTls(value: unknown): FrpEntryTls {\n if (value !== 'public-ip-cert' && value !== 'self-signed') throw new Error('frp_entry_tls_invalid')\n return value\n}\n\nfunction validatePort(value: unknown, rejectReserved: boolean): number {\n if (!Number.isSafeInteger(value) || Number(value) < 1 || Number(value) > 65_535) {\n throw new Error('frp_settings_invalid')\n }\n const port = Number(value)\n if (rejectReserved && FRP_RESERVED_PORTS.includes(port)) throw new Error('frp_settings_invalid')\n return port\n}\n\n/** Validate the loopback vhost port of the target frps. */\nexport function validateFrpVhostHttpPort(value: unknown): number {\n return validatePort(value, false)\n}\n\n/** Validate the public entry port of the self-signed TCP proxy. */\nexport function validateFrpPublicPort(value: unknown): number {\n return validatePort(value, true)\n}\n\n/** Effective provisioning mode; an absent field keeps the upstream behaviour. */\nexport function resolveFrpMode(settings: FrpSettings): FrpMode {\n return settings.mode ?? 'deploy'\n}\n\n/** Effective entry TLS mode; an absent field keeps the upstream behaviour. */\nexport function resolveFrpEntryTls(settings: FrpSettings): FrpEntryTls {\n return settings.entryTls ?? 'public-ip-cert'\n}\n\n/** Effective loopback vhost port; an absent field keeps the upstream behaviour. */\nexport function resolveFrpVhostHttpPort(settings: FrpSettings): number {\n return settings.vhostHttpPort ?? FRP_VHOST_HTTP_PORT\n}\n\n/** Effective public entry port of the self-signed passthrough. */\nexport function resolveFrpPublicPort(settings: FrpSettings): number {\n return settings.publicPort ?? FRP_DEFAULT_PUBLIC_PORT\n}\n\n/** HTTPS origin users actually open; the self-signed TCP entry is not on 443. */\nexport function frpEntryOrigin(settings: FrpSettings): string {\n if (!isFrpSelfSignedIngress(settings)) return settings.publicOrigin\n return `https://${new URL(settings.publicOrigin).hostname}:${String(resolveFrpPublicPort(settings))}`\n}\n\n/** True when the gateway terminates TLS itself behind a raw frps TCP proxy. */\nexport function isFrpSelfSignedIngress(settings: FrpSettings): boolean {\n return resolveFrpEntryTls(settings) === 'self-signed'\n}\n\n/**\n * Parse FRP settings at the loopback request and filesystem boundaries.\n *\n * Optional keys are kept only when supplied, so a legacy `settings.json` still\n * round-trips byte for byte and every default stays the upstream behaviour.\n * Two cross-field rules are enforced here because neither can be recovered later:\n * the self-signed entry is a TCP passthrough and therefore attach-only, and\n * attach mode with a public-CA entry must state the user's real vhost port —\n * silently assuming 7080 would disable the plaintext-exposure gate.\n */\nexport function parseFrpSettings(value: unknown): FrpSettings {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('frp_settings_invalid')\n const record = value as Record<string, unknown>\n if (Reflect.ownKeys(record).some(key => !FRP_SETTINGS_KEYS.has(String(key)))) {\n throw new Error('frp_settings_invalid')\n }\n if (record.version !== undefined && record.version !== 1) throw new Error('frp_settings_invalid')\n const mode = record.mode === undefined ? undefined : validateFrpMode(record.mode)\n const entryTls = record.entryTls === undefined ? undefined : validateFrpEntryTls(record.entryTls)\n const vhostHttpPort = record.vhostHttpPort === undefined ? undefined : validateFrpVhostHttpPort(record.vhostHttpPort)\n const publicPort = record.publicPort === undefined ? undefined : validateFrpPublicPort(record.publicPort)\n const resolvedMode = mode ?? 'deploy'\n const resolvedEntryTls: FrpEntryTls = entryTls ?? 'public-ip-cert'\n if (resolvedEntryTls === 'self-signed' && resolvedMode !== 'attach') throw new Error('frp_entry_tls_invalid')\n if (resolvedMode === 'attach' && resolvedEntryTls === 'public-ip-cert' && vhostHttpPort === undefined) {\n throw new Error('frp_attach_mode_requires_vhost_port')\n }\n const publicOrigin = validateFrpPublicOrigin(record.publicOrigin)\n if (resolvedEntryTls === 'self-signed' && isIP(new URL(publicOrigin).hostname) !== 4) {\n throw new Error('frp_self_signed_requires_public_ipv4')\n }\n return Object.freeze({\n version: 1,\n serverAddress: validateFrpServerAddress(record.serverAddress),\n serverPort: validateFrpServerPort(record.serverPort),\n token: validateFrpToken(record.token),\n publicOrigin,\n ...(mode === undefined ? {} : { mode }),\n ...(entryTls === undefined ? {} : { entryTls }),\n ...(vhostHttpPort === undefined ? {} : { vhostHttpPort }),\n ...(publicPort === undefined ? {} : { publicPort }),\n })\n}\n\n/**\n * Merge a partial VPS request body with the saved configuration so a blank\n * field keeps its saved value (\"已保存时可留空\"). Every merged field is still\n * validated; with nothing saved and nothing supplied the result reports a\n * missing configuration instead of silently deploying blanks.\n */\nexport function mergeSavedFrpSettings(\n partial: Readonly<Record<string, unknown>>,\n saved: FrpSettings | undefined,\n): FrpSettings {\n const pick = <K extends 'mode' | 'entryTls' | 'vhostHttpPort' | 'publicPort'>(key: K): unknown => {\n const supplied = partial[key]\n if (supplied === '' || supplied === undefined) return saved?.[key]\n return supplied\n }\n const mode = pick('mode')\n const entryTls = pick('entryTls')\n const vhostHttpPort = pick('vhostHttpPort')\n const publicPort = pick('publicPort')\n const merged = {\n serverAddress: partial.serverAddress === '' || partial.serverAddress === undefined\n ? saved?.serverAddress : partial.serverAddress,\n serverPort: typeof partial.serverPort === 'number' && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1\n ? partial.serverPort : saved?.serverPort,\n token: partial.token === '' || partial.token === undefined ? saved?.token : partial.token,\n publicOrigin: partial.publicOrigin === '' || partial.publicOrigin === undefined\n ? saved?.publicOrigin : partial.publicOrigin,\n ...(mode === '' || mode === undefined ? {} : { mode }),\n ...(entryTls === '' || entryTls === undefined ? {} : { entryTls }),\n ...(vhostHttpPort === '' || vhostHttpPort === undefined ? {} : { vhostHttpPort }),\n ...(publicPort === '' || publicPort === undefined ? {} : { publicPort }),\n }\n if (merged.serverAddress === undefined && merged.serverPort === undefined\n && merged.token === undefined && merged.publicOrigin === undefined) {\n throw new Error('frp_config_missing')\n }\n return parseFrpSettings(merged)\n}\n\n/** Merge a VPS target (address and control port) with the saved configuration. */\nexport function mergeSavedFrpTarget(\n partial: Readonly<Record<string, unknown>>,\n saved: FrpSettings | undefined,\n): { readonly serverAddress: string; readonly serverPort: number } {\n const serverAddress = partial.serverAddress === '' || partial.serverAddress === undefined\n ? saved?.serverAddress : partial.serverAddress\n const serverPort = typeof partial.serverPort === 'number' && Number.isSafeInteger(partial.serverPort) && partial.serverPort >= 1\n ? partial.serverPort : saved?.serverPort\n if (serverAddress === undefined || serverPort === undefined) throw new Error('frp_config_missing')\n return Object.freeze({\n serverAddress: validateFrpServerAddress(serverAddress),\n serverPort: validateFrpServerPort(serverPort),\n })\n}\n\nfunction tomlString(value: string): string {\n return JSON.stringify(value)\n}\n\n/**\n * Build the single-purpose frpc configuration for the current loopback gateway.\n *\n * The default (public-CA) entry is an HTTP vhost behind Caddy and is emitted byte\n * for byte as before. The self-signed entry cannot use a vhost at all: frps only\n * forwards raw TCP, and the gateway terminates TLS on that connection.\n */\nexport function createFrpcToml(settings: FrpSettings, localPort: number): string {\n if (!Number.isSafeInteger(localPort) || localPort < 1 || localPort > 65_535) throw new Error('frp_local_port_invalid')\n const header = [\n `serverAddr = ${tomlString(settings.serverAddress)}`,\n `serverPort = ${String(settings.serverPort)}`,\n 'auth.method = \"token\"',\n `auth.token = ${tomlString(settings.token)}`,\n 'transport.tls.enable = true',\n '',\n '[[proxies]]',\n 'name = \"dsh-mobile\"',\n ]\n if (isFrpSelfSignedIngress(settings)) {\n return [\n ...header,\n // Raw TCP passthrough: frps never terminates or inspects TLS. The DSH\n // gateway terminates it and the app pins the gateway CA instead of any\n // public certificate authority.\n 'type = \"tcp\"',\n 'localIP = \"127.0.0.1\"',\n `localPort = ${String(localPort)}`,\n `remotePort = ${String(resolveFrpPublicPort(settings))}`,\n 'transport.useEncryption = true',\n 'transport.useCompression = true',\n '',\n ].join('\\n')\n }\n const hostnameValue = new URL(settings.publicOrigin).hostname\n return [\n ...header,\n 'type = \"http\"',\n 'localIP = \"127.0.0.1\"',\n `localPort = ${String(localPort)}`,\n `customDomains = [${tomlString(hostnameValue)}]`,\n 'transport.useEncryption = true',\n 'transport.useCompression = true',\n '',\n ].join('\\n')\n}\n\n/** Build the matching restricted frps and Caddy templates for one VPS. */\nexport function createFrpServerTemplate(settings: FrpSettings): string {\n return createRestrictedFrpServerTemplate(settings.serverPort, settings.token, settings.publicOrigin, {\n vhostHttpPort: resolveFrpVhostHttpPort(settings),\n entryTls: resolveFrpEntryTls(settings),\n })\n}\n\nasync function atomicPrivateWrite(file: string, body: string): Promise<void> {\n const directory = dirname(file)\n await mkdir(directory, { recursive: true, mode: 0o700 })\n try {\n const current = await lstat(file)\n if (!current.isFile() || current.isSymbolicLink()) throw new Error('frp_config_target_invalid')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n const temporary = join(directory, `.${basename(file)}.${randomBytes(12).toString('hex')}.tmp`)\n try {\n await writeFile(temporary, body, { encoding: 'utf8', flag: 'wx', mode: 0o600 })\n await rename(temporary, file)\n await restrictPrivateFile(file)\n } catch (error) {\n await rm(temporary, { force: true })\n throw error\n }\n}\n\n/** Owns private FRP settings and generation-specific frpc configuration. */\nexport class FrpConfigStore {\n readonly stateRoot: string\n readonly settingsFile: string\n readonly runtimeConfigFile: string\n private settingsValue: FrpSettings | undefined\n private errorCode: string | undefined\n\n constructor(stateDirectory: string) {\n if (!isAbsolute(stateDirectory)) throw new Error('frp config state directory must be absolute')\n this.stateRoot = resolve(stateDirectory)\n this.settingsFile = join(this.stateRoot, 'settings.json')\n this.runtimeConfigFile = join(this.stateRoot, 'frpc.toml')\n }\n\n /** Load private settings while rejecting links, oversized files, and unknown fields. */\n async initialize(): Promise<void> {\n let entry\n try { entry = await lstat(this.settingsFile) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n if (!entry.isFile() || entry.isSymbolicLink() || entry.size > MAX_SETTINGS_BYTES) {\n this.errorCode = 'frp_config_invalid'\n return\n }\n await restrictPrivateFile(this.settingsFile)\n try {\n this.settingsValue = parseFrpSettings(JSON.parse(await readFile(this.settingsFile, 'utf8')) as unknown)\n this.errorCode = undefined\n } catch {\n this.settingsValue = undefined\n this.errorCode = 'frp_config_invalid'\n }\n }\n\n /**\n * Return configuration metadata without exposing the FRP token.\n *\n * The optional keys appear only when the saved configuration departs from the\n * upstream defaults, so the pre-existing field set is unchanged for every\n * legacy configuration.\n */\n status(): FrpConfigurationStatus {\n const settings = this.settingsValue\n const mode = settings === undefined ? undefined : resolveFrpMode(settings)\n const entryTls = settings === undefined ? undefined : resolveFrpEntryTls(settings)\n return Object.freeze({\n configured: settings !== undefined,\n ...(settings === undefined ? {} : {\n serverAddress: settings.serverAddress,\n serverPort: settings.serverPort,\n publicOrigin: settings.publicOrigin,\n }),\n vhostHttpPort: settings === undefined ? FRP_VHOST_HTTP_PORT : resolveFrpVhostHttpPort(settings),\n storagePath: this.stateRoot,\n ...(this.errorCode === undefined ? {} : { errorCode: this.errorCode }),\n ...(mode === undefined || mode === 'deploy' ? {} : { mode }),\n ...(entryTls === undefined || entryTls === 'public-ip-cert' ? {} : { entryTls }),\n ...(settings?.publicPort === undefined ? {} : { publicPort: settings.publicPort }),\n })\n }\n\n /** Return private settings only to the provider lifecycle. */\n settings(): FrpSettings | undefined {\n return this.settingsValue\n }\n\n /** Atomically replace private FRP settings. */\n async configure(value: unknown): Promise<FrpConfigurationStatus> {\n const settings = parseFrpSettings(value)\n await atomicPrivateWrite(this.settingsFile, `${JSON.stringify(settings)}\\n`)\n await rm(this.runtimeConfigFile, { force: true })\n this.settingsValue = settings\n this.errorCode = undefined\n return this.status()\n }\n\n /** Materialize the private generation-specific frpc configuration. */\n async writeRuntimeConfig(localPort: number): Promise<string> {\n const settings = this.settingsValue\n if (settings === undefined) throw new Error('frp_config_missing')\n await atomicPrivateWrite(this.runtimeConfigFile, createFrpcToml(settings, localPort))\n return this.runtimeConfigFile\n }\n\n /** Remove only configuration files owned by the FRP provider. */\n async purge(): Promise<FrpConfigurationStatus> {\n await rm(this.stateRoot, { recursive: true, force: true })\n this.settingsValue = undefined\n this.errorCode = undefined\n return this.status()\n }\n}\n\nexport { FRP_VHOST_HTTP_PORT as DEFAULT_VHOST_HTTP_PORT }\nexport type { FrpEntryTls } from './frp-template.js'\n","import { isIP } from './ip.js'\nimport {\n createFrpcToml,\n isFrpSelfSignedIngress,\n resolveFrpMode,\n resolveFrpPublicPort,\n resolveFrpVhostHttpPort,\n type FrpSettings,\n} from './frp-config.js'\nimport {\n createCaddySite,\n FRP_CADDY_IMPORT_LINE,\n FRP_CADDY_SNIPPET_MARKER,\n FRP_CADDY_SNIPPET_PATH,\n manualIpCertificateGuide,\n} from './frp-template.js'\n\n/** Default proof path used to confirm the public entry answered from this computer. */\nexport const FRP_ATTACH_DISCOVERY_PATH = '/mobile-access/discovery'\n\n/** Declaration printed above the VPS half of an attach template. */\nexport const FRP_ATTACH_VPS_DECLARATION = '# 本模板不安装、不改动、不重启你的 frps;只新增 Caddy 片段与证书。'\n + 'frps 侧需你自行确认 vhostHTTPPort=<N> 与 proxyBindAddr=\"127.0.0.1\"。'\n\n/** Declaration printed above the local half of an attach template. */\nexport const FRP_ATTACH_LOCAL_DECLARATION = '# 本机只写入 frpc.toml 并启动 frpc:不安装、不改动本机 frps,也不改动 DSH 自身配置。'\n\n/** Declaration for the self-signed passthrough, which keeps its CA on this computer. */\nexport const FRP_ATTACH_SELF_SIGNED_DECLARATION = '# 自签穿透档不使用 Caddy 或公开证书:'\n + 'frps 只做 TCP 透传,由本机 DSH 网关终止 TLS;服务端证书会在运行时续签,CA 到期需重新配对。'\n\n/** Optional inputs for attach artefacts; every field falls back to the validated settings. */\nexport interface FrpAttachOptions {\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n readonly certDir?: string\n /** Absolute path of the plugin-written frpc.toml, used for the `frpc verify` self-check. */\n readonly configFile?: string\n /** Loopback port the gateway will listen on; only a placeholder inside the copied runbook. */\n readonly localPort?: number\n /**\n * Explicit reveal of the shared token inside previews and clipboard copies.\n *\n * Defaults to `false`: every artefact the panel produces on its own stays\n * masked, because the copied text lands in the system clipboard. Only the\n * panel's dedicated \"copy the frpc.toml with its token\" action sets this, and\n * the result never reaches a stored state, a log, or localStorage.\n */\n readonly revealToken?: boolean\n}\n\n/** Fixed placeholder that replaces the shared token in every preview or copied artefact. */\nexport const FRP_ATTACH_TOKEN_PLACEHOLDER = '***'\n\n/**\n * One-line notice printed above the masked frpc.toml preview.\n *\n * It states both facts a reader cannot verify from the text itself: the real\n * file is written with 0600 permissions, and an unmasked copy would survive in\n * the system clipboard.\n */\nexport function frpAttachTokenMaskNotice(configFile: string): string {\n return `# 预览已打码:真正的 frpc.toml 已由插件以 0600 权限写入 ${configFile};`\n + '明文 token 不打码会随复制进入系统剪贴板,故此处只显示占位符。'\n}\n\n/** One-line notice printed above an explicitly revealed frpc.toml preview. */\nexport const FRP_ATTACH_TOKEN_REVEAL_NOTICE = '# 明文 token 已按你的显式操作显示:'\n + '复制后即进入系统剪贴板并长期留存,请勿粘贴到公开位置。'\n\n/** Matches the single `auth.token` assignment of a generated frpc.toml. */\nconst FRPC_TOKEN_ASSIGNMENT = /^auth\\.token\\s*=\\s*\"(?:[^\"\\\\]|\\\\.)*\"/mu\n\n/**\n * Replace the `auth.token` value of a rendered frpc.toml with a fixed placeholder.\n *\n * Only the preview and clipboard paths call this: the file written to disk keeps\n * the real token, because frpc has to read it. The mapping never fails open — a\n * token line that cannot be masked raises instead of leaking the secret.\n */\nexport function redactFrpcTomlToken(frpcToml: string): string {\n if (!/(^|\\n)auth\\.token/u.test(frpcToml)) return frpcToml\n const masked = frpcToml.replace(FRPC_TOKEN_ASSIGNMENT, `auth.token = \"${FRP_ATTACH_TOKEN_PLACEHOLDER}\"`)\n if (!masked.includes(`auth.token = \"${FRP_ATTACH_TOKEN_PLACEHOLDER}\"`)) throw new Error('frp_token_mask_failed')\n return masked\n}\n\n/**\n * The frpc.toml exactly as the panel displays or copies it.\n *\n * `options.revealToken` is the only way to obtain the plaintext variant; the\n * default is the masked text. `createFrpcToml` itself is untouched, so the file\n * the provider writes stays byte-for-byte identical to the upstream output.\n */\nexport function createFrpAttachFrpcToml(settings: FrpSettings, options: FrpAttachOptions = {}): string {\n const frpcToml = createFrpcToml(settings, attachLocalPort(options))\n return options.revealToken === true ? frpcToml : redactFrpcTomlToken(frpcToml)\n}\n\n/** The two separated halves of the attach artefact, plus their combined text. */\nexport interface FrpAttachTemplate {\n readonly mode: 'attach'\n readonly vps: string\n readonly local: string\n readonly text: string\n}\n\nconst DEFAULT_ATTACH_CONFIG_FILE = '<DSH Mobile 私有目录>/remote/frp/config/frpc.toml'\n\nfunction attachConfigFile(options: FrpAttachOptions): string {\n const file = options.configFile\n if (file === undefined || file === '') return DEFAULT_ATTACH_CONFIG_FILE\n return file\n}\n\nfunction attachLocalPort(options: FrpAttachOptions): number {\n const port = options.localPort ?? 3443\n if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) throw new Error('frp_local_port_invalid')\n return port\n}\n\n/**\n * Reject every attach request that could not be carried out safely before any\n * artefact is produced. Attach never installs frps, so it must know the user's\n * real vhost port (unless the self-signed passthrough removes the vhost entirely).\n */\nexport function validateAttachSettings(settings: FrpSettings, options: FrpAttachOptions = {}): void {\n if (resolveFrpMode(settings) !== 'attach') throw new Error('frp_settings_invalid')\n if (isFrpSelfSignedIngress(settings)) {\n if (settings.mode !== 'attach') throw new Error('frp_entry_tls_invalid')\n resolveFrpPublicPort(settings)\n return\n }\n const vhostHttpPort = options.vhostHttpPort ?? settings.vhostHttpPort\n if (vhostHttpPort === undefined) throw new Error('frp_attach_mode_requires_vhost_port')\n if (!Number.isSafeInteger(vhostHttpPort) || vhostHttpPort < 1 || vhostHttpPort > 65_535) {\n throw new Error('frp_settings_invalid')\n }\n}\n\n/** Strip the leading `# ` of a guide block so it can be pasted as runnable shell. */\nfunction guideCommands(guide: string): string[] {\n return guide\n .split('\\n')\n .map(line => line.replace(/^#\\s?/u, '').trimEnd())\n .filter(line => line !== '')\n}\n\n/** Shared VPS-side inputs of both the copied runbook and the panel plan. */\nexport interface FrpAttachVpsParts {\n readonly publicHost: string\n readonly selfSigned: boolean\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n readonly snippet: string\n readonly certGuide: readonly string[]\n}\n\n/**\n * Derive every VPS-side value once so the copied runbook and the panel plan can\n * never drift apart.\n */\nexport function frpAttachVpsParts(settings: FrpSettings, options: FrpAttachOptions = {}): FrpAttachVpsParts {\n validateAttachSettings(settings, options)\n const publicHost = new URL(settings.publicOrigin).hostname\n if (isFrpSelfSignedIngress(settings)) {\n return Object.freeze({\n publicHost,\n selfSigned: true,\n publicPort: resolveFrpPublicPort(settings),\n snippet: '',\n certGuide: Object.freeze([] as string[]),\n })\n }\n const vhostHttpPort = options.vhostHttpPort ?? resolveFrpVhostHttpPort(settings)\n const certDir = options.certDir\n const site = createCaddySite(publicHost, {\n vhostHttpPort,\n ...(certDir === undefined ? {} : { certDir }),\n })\n return Object.freeze({\n publicHost,\n selfSigned: false,\n vhostHttpPort,\n snippet: `${FRP_CADDY_SNIPPET_MARKER}\\n${site.trimEnd()}\\n`,\n certGuide: Object.freeze(isIP(publicHost) === 4 ? guideCommands(manualIpCertificateGuide(publicHost)) : []),\n })\n}\n\n/**\n * Build the attach artefact for an existing frps.\n *\n * The result is deliberately split in two: the VPS half contains only what the\n * user's own server needs (Caddy snippet + import + certificate guidance, or a\n * single firewall rule for the self-signed passthrough), and the local half\n * contains the generated frpc.toml plus its `frpc verify` self-check. Neither\n * half installs, rewrites, or restarts the user's frps.\n */\nexport function createFrpAttachTemplateParts(\n settings: FrpSettings,\n options: FrpAttachOptions = {},\n): FrpAttachTemplate {\n const parts = frpAttachVpsParts(settings, options)\n const configFile = attachConfigFile(options)\n const revealed = options.revealToken === true\n const frpcToml = createFrpAttachFrpcToml(settings, options)\n const local = [\n '# ---- (b) 本机侧(在这台电脑上执行)----',\n FRP_ATTACH_LOCAL_DECLARATION,\n revealed ? FRP_ATTACH_TOKEN_REVEAL_NOTICE : frpAttachTokenMaskNotice(configFile),\n `# frpc.toml 全文(插件同样会以 0600 权限写入 ${configFile})`,\n frpcToml.trimEnd(),\n '',\n '# 启动前自检:',\n `frpc verify -c \"${configFile}\"`,\n '',\n ].join('\\n')\n\n if (parts.selfSigned) {\n const publicPort = parts.publicPort ?? resolveFrpPublicPort(settings)\n const vps = [\n '# ---- (a) VPS 侧(在你的服务器上执行,共 3 项)----',\n FRP_ATTACH_SELF_SIGNED_DECLARATION,\n '#',\n '# 1) 放行公网入口端口(就是这台电脑网关的 HTTPS 入口,TCP 透传不解密):',\n `ufw allow ${String(publicPort)}/tcp`,\n `ufw status | grep ${String(publicPort)}`,\n '# 若使用 firewalld / 云厂商安全组,请放行同样的 TCP 端口。',\n '# 前提:现有 frps 的 TCP 代理已监听公网地址;若 proxyBindAddr=127.0.0.1,放行防火墙仍无法从公网访问。',\n '# 插件不会修改你现有的 frps;不满足此前提时请改选受信任证书入口。',\n '#',\n '# 2) 确认你既有的 frps 已就绪(无需修改它的配置):',\n 'systemctl is-active frps || true',\n `ss -lnt | grep -E ':(${String(settings.serverPort)}|${String(publicPort)})\\\\b' || true`,\n '#',\n '# 3) frpc 启动后,用 -k 检查连通性;此命令不验证身份,App 配对后会固定网关 CA:',\n `curl -k -sS -o /dev/null -w '%{http_code}\\\\n' https://${parts.publicHost}:${String(publicPort)}${FRP_ATTACH_DISCOVERY_PATH}`,\n '# 期望输出 200。',\n '',\n ].join('\\n')\n return Object.freeze({\n mode: 'attach' as const,\n vps,\n local,\n text: `${vps}${local}`,\n })\n }\n\n const vps = [\n '# ---- (a) VPS 侧(在你的服务器上执行)----',\n FRP_ATTACH_VPS_DECLARATION,\n '#',\n `# 1) 保存 Caddy 片段到 ${FRP_CADDY_SNIPPET_PATH}(只新增这一个文件):`,\n `cat > ${FRP_CADDY_SNIPPET_PATH} <<'DSH_MOBILE_CADDY_SNIPPET'`,\n parts.snippet.trimEnd(),\n 'DSH_MOBILE_CADDY_SNIPPET',\n '#',\n '# 2) 确认 Caddyfile 顶部含下面这一行(全局块必须在前;没有则加到文件最上方):',\n `# ${FRP_CADDY_IMPORT_LINE}`,\n 'caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy',\n '#',\n ...(parts.certGuide.length === 0\n ? ['# 3) 域名 HTTPS:确认 DNS 指向 VPS,开放 80/443;Caddy 会自动申请和续期证书。']\n : [\n '# 3) 公网 IP 的 HTTPS 证书(Caddy 不能为 IP 自动签发,故用 certbot 签发):',\n ...parts.certGuide.map(line => `# ${line}`),\n '#',\n '# 4) 确认证书续期任务,并在续期后将新证书安装到 Caddy:',\n 'systemctl list-timers certbot.timer --all || true',\n ]),\n '#',\n '# 端到端验证(公网入口为 443,与 frps 的 vhost 端口无关):',\n `curl -sS -o /dev/null -w '%{http_code}\\\\n' https://${parts.publicHost}${FRP_ATTACH_DISCOVERY_PATH}`,\n '# 期望输出 200。',\n '',\n ].join('\\n')\n return Object.freeze({\n mode: 'attach' as const,\n vps,\n local,\n text: `${vps}${local}`,\n })\n}\n\n/** Combined attach artefact, ready for the clipboard. */\nexport function createFrpAttachTemplate(settings: FrpSettings, options: FrpAttachOptions = {}): string {\n return createFrpAttachTemplateParts(settings, options).text\n}\n","/** Task-completion notifications for the Android shell.\n *\n * The page cannot know a run \"finished\" from any single event, so completion\n * is defined conservatively: assistant activity was observed, then the\n * conversation stayed quiet for a bounded period with no pending input\n * question, while the page was hidden. Question cards, which unambiguously\n * wait for the user, notify immediately instead of waiting out the quiet\n * period. Everything here is pure except the clearly-marked DOM adapters so\n * the decision logic stays unit-testable under node.\n */\n\n/** How long the conversation must stay quiet before a run counts as done. */\nexport const TASK_NOTIFY_QUIET_MS = 90_000\n\n/**\n * Grace after a busy-to-idle transition before announcing completion.\n * Absorbs composer flicker between steps while still feeling immediate.\n */\nexport const TASK_NOTIFY_GRACE_MS = 10_000\n\n/**\n * Minimum continuous busy time for an instant announcement. A run that held\n * the composer this long is no flicker: announce at once instead of waiting\n * out the grace period.\n */\nexport const TASK_NOTIFY_MIN_BUSY_MS = 20_000\n\n/** Maximum quiet period worth waiting through before giving up on a run. */\nexport const TASK_NOTIFY_STALE_MS = 30 * 60_000\n\n/** Wire bounds mirrored by the Android notification policy. */\nexport const TASK_NOTIFY_LIMITS = Object.freeze({\n title: 80,\n body: 200,\n tag: 64,\n} as const)\n\nexport type TaskNotifyKind = 'done' | 'question'\n\nexport interface TaskNotifyTexts {\n readonly doneTitle: string\n readonly doneBody: string\n readonly questionTitle: string\n readonly questionBody: string\n}\n\nexport interface TaskNotifySnapshot {\n /** Result of document.hidden at evaluation time. */\n readonly pageHidden: boolean\n /** Whether a question or plan-review card currently waits for input. */\n readonly pendingQuestion: boolean\n /** Stable key of the waiting card, when the DOM exposes one. */\n readonly questionKey: string | undefined\n /** Best-effort session label for the notification body. */\n readonly sessionLabel: string\n /** Current composer busy flag; drives the completion transition. */\n readonly composerBusy: boolean\n}\n\nexport interface TaskNotifyEvent {\n readonly kind: TaskNotifyKind\n readonly title: string\n readonly body: string\n readonly tag: string\n}\n\nexport interface TaskNotifyOptions {\n readonly quietMs?: number\n readonly graceMs?: number\n readonly minBusyMs?: number\n readonly now?: () => number\n readonly format: (kind: TaskNotifyKind, sessionLabel: string) => { title: string; body: string }\n}\n\nconst MAX_SEEN_QUESTION_KEYS = 20\n\nfunction sanitizeTagFragment(value: string): string {\n return value.toLowerCase().replaceAll(/[^a-z0-9-]+/gu, '-').replaceAll(/^-+|-+$/gu, '').slice(0, TASK_NOTIFY_LIMITS.tag)\n}\n\n/** Stable per-turn tag so concurrent sessions keep separate notifications. */\nexport function taskCompletionTag(sessionId: string, turn: number): string {\n const suffix = sanitizeTagFragment(`${sessionId}-${String(turn)}`) || 'task'\n return `dsh-task-done-${suffix}`.slice(0, TASK_NOTIFY_LIMITS.tag)\n}\n\n/** Decide whether the current snapshot deserves a system notification. */\nexport class TaskNotifyTracker {\n private lastActivityAt: number | undefined\n private notifiedDoneAt: number | undefined\n private wasBusy = false\n private busySince: number | undefined\n private idleAnchoredAt: number | undefined\n private readonly seenQuestionKeys = new Set<string>()\n private readonly quietMs: number\n private readonly graceMs: number\n private readonly minBusyMs: number\n private readonly now: () => number\n private readonly format: (kind: TaskNotifyKind, sessionLabel: string) => { title: string; body: string }\n\n constructor(options: TaskNotifyOptions) {\n this.quietMs = options.quietMs ?? TASK_NOTIFY_QUIET_MS\n this.graceMs = options.graceMs ?? TASK_NOTIFY_GRACE_MS\n this.minBusyMs = options.minBusyMs ?? TASK_NOTIFY_MIN_BUSY_MS\n this.now = options.now ?? Date.now\n this.format = options.format\n }\n\n /** Record assistant-side activity; re-arms completion after a notification. */\n markActivity(): void {\n const now = this.now()\n this.lastActivityAt = now\n if (this.notifiedDoneAt !== undefined && this.notifiedDoneAt <= now) this.notifiedDoneAt = undefined\n }\n\n /**\n * Milliseconds until a pending completion anchor fires, or undefined when\n * no anchor exists. Lets the watcher schedule one exact timer instead of\n * waiting for the next coarse poll tick.\n */\n pendingAnchorDelayMs(): number | undefined {\n if (this.idleAnchoredAt === undefined) return undefined\n return Math.max(0, this.graceMs - (this.now() - this.idleAnchoredAt))\n }\n\n evaluate(snapshot: TaskNotifySnapshot): TaskNotifyEvent | undefined {\n const now = this.now()\n if (!this.wasBusy && snapshot.composerBusy) this.busySince = now\n if (this.wasBusy && !snapshot.composerBusy) {\n // The run just ended. A run that held the composer long enough is no\n // flicker: announce at once when hidden, unless a question card (which\n // has its own immediate path below) is already waiting.\n const busyFor = this.busySince === undefined ? 0 : now - this.busySince\n this.busySince = undefined\n if (snapshot.pageHidden && !snapshot.pendingQuestion && busyFor >= this.minBusyMs) {\n this.idleAnchoredAt = undefined\n this.notifiedDoneAt = now\n return this.event('done', snapshot.sessionLabel, undefined)\n }\n this.idleAnchoredAt = snapshot.pageHidden ? now : undefined\n if (!snapshot.pageHidden) this.notifiedDoneAt = now\n }\n this.wasBusy = snapshot.composerBusy\n if (snapshot.composerBusy) {\n this.idleAnchoredAt = undefined\n } else if (this.idleAnchoredAt !== undefined && !snapshot.pageHidden) {\n // Came back before the grace elapsed: treat the ending as seen.\n this.idleAnchoredAt = undefined\n this.notifiedDoneAt = now\n }\n if (!snapshot.pageHidden) {\n if (snapshot.pendingQuestion && snapshot.questionKey !== undefined) this.rememberQuestionKey(snapshot.questionKey)\n return undefined\n }\n if (snapshot.pendingQuestion) {\n if (snapshot.questionKey === undefined || this.seenQuestionKeys.has(snapshot.questionKey)) return undefined\n this.rememberQuestionKey(snapshot.questionKey)\n return this.event('question', snapshot.sessionLabel, snapshot.questionKey)\n }\n // Completion: the anchored transition announces fast; the quiet period\n // remains as the fallback for activity without a busy signal.\n if (this.idleAnchoredAt !== undefined) {\n if (now - this.idleAnchoredAt < this.graceMs) return undefined\n this.idleAnchoredAt = undefined\n this.notifiedDoneAt = now\n return this.event('done', snapshot.sessionLabel, undefined)\n }\n if (this.lastActivityAt === undefined) return undefined\n if (now - this.lastActivityAt < this.quietMs) return undefined\n if (now - this.lastActivityAt > TASK_NOTIFY_STALE_MS) {\n // Activity too old to attribute: drop it instead of announcing a stale run.\n this.lastActivityAt = undefined\n return undefined\n }\n if (this.notifiedDoneAt !== undefined && this.notifiedDoneAt >= this.lastActivityAt) return undefined\n this.notifiedDoneAt = now\n return this.event('done', snapshot.sessionLabel, undefined)\n }\n\n private rememberQuestionKey(key: string): void {\n this.seenQuestionKeys.add(key)\n if (this.seenQuestionKeys.size > MAX_SEEN_QUESTION_KEYS) {\n const oldest = this.seenQuestionKeys.values().next()\n if (!oldest.done) this.seenQuestionKeys.delete(oldest.value)\n }\n }\n\n private event(kind: TaskNotifyKind, sessionLabel: string, questionKey: string | undefined): TaskNotifyEvent {\n const text = this.format(kind, sessionLabel)\n const tag = kind === 'question' && questionKey !== undefined\n ? `dsh-task-question-${sanitizeTagFragment(questionKey) || 'pending'}`\n : 'dsh-task-done'\n return Object.freeze({ kind, title: text.title, body: text.body, tag })\n }\n}\n\ninterface ComposerProbe {\n getAttribute?: (name: string) => string | null\n querySelector?: (selectors: string) => {\n disabled?: boolean\n readOnly?: boolean\n getAttribute?: (name: string) => string | null\n } | null\n}\n\n/**\n * Mirror of the composer busy signal used for media actions: the run is busy\n * while the composer is aria-busy, disabled, read-only, or non-interactive.\n */\nexport function readComposerBusyState(root: ParentNode): boolean {\n if (typeof root.querySelector !== 'function') return false\n const card = root.querySelector('[data-composer-card]') as ComposerProbe | null\n if (card === null || typeof card.getAttribute !== 'function' || typeof card.querySelector !== 'function') return false\n if (card.getAttribute('aria-busy') === 'true') return true\n const field = card.querySelector('textarea,input,[contenteditable=\"true\"],[contenteditable=\"plaintext-only\"]')\n if (field === null) return false\n return field.disabled === true || field.readOnly === true || field.getAttribute?.('aria-disabled') === 'true'\n}\n\n/**\n * Best-effort session label for the notification body, bounded for the wire.\n * Callers accept that titles may name the active session on the lock screen.\n */\nexport function readSessionLabel(title: unknown): string {\n if (typeof title !== 'string') return ''\n return title.normalize('NFC').replace(/[\\u0000-\\u001f\\u007f]+/gu, ' ').trim().slice(0, 80)\n}\n\n/** Whether the document currently shows a card that waits for user input. */\nexport function hasPendingInputQuestion(root: ParentNode): boolean { if (typeof root.querySelector !== 'function') return false\n return root.querySelector('[data-question-key],[data-plan-review-key]') !== null\n}\n\n/** Stable key of the waiting input card, when the DOM exposes one. */\nexport function pendingInputQuestionKey(root: ParentNode): string | undefined {\n if (typeof root.querySelector !== 'function') return undefined\n const card = root.querySelector('[data-question-key],[data-plan-review-key]') as {\n getAttribute?: (name: string) => string | null\n } | null\n const getAttribute = card?.getAttribute\n if (typeof getAttribute !== 'function') return undefined\n const key = getAttribute.call(card, 'data-question-key') ?? getAttribute.call(card, 'data-plan-review-key')\n if (key === null || key === '') return undefined\n return key\n}\n\n/** Observe assistant-side conversation mutations; returns a disposer. */\nexport function observeTaskActivity(\n target: Node,\n onActivity: () => void,\n observe: typeof MutationObserver | undefined = typeof MutationObserver === 'function' ? MutationObserver : undefined,\n): () => void {\n if (observe === undefined) return () => undefined\n const observer = new observe(() => { onActivity() })\n try {\n observer.observe(target, { childList: true, characterData: true, subtree: true })\n } catch {\n return () => undefined\n }\n return () => { observer.disconnect() }\n}\n\nexport interface TaskWatcherLabels {\n readonly label: (kind: TaskNotifyKind, sessionLabel: string) => { title: string; body: string }\n /** Legacy DOM-only completion inference. Current gateways provide exact Host events. */\n readonly completionFallback?: boolean\n}\n\ninterface NativeBridgeHandle {\n capabilities(): Promise<readonly string[]>\n invoke(action: string, input?: unknown): Promise<unknown>\n}\n\nfunction readNativeBridge(): NativeBridgeHandle | undefined {\n if (typeof window === 'undefined') return undefined\n const bridge = (window as unknown as { __DSH_MOBILE_NATIVE__?: NativeBridgeHandle }).__DSH_MOBILE_NATIVE__\n if (bridge === undefined || typeof bridge.capabilities !== 'function' || typeof bridge.invoke !== 'function') return undefined\n return bridge\n}\n\nexport interface TaskNotifyWireEvent {\n readonly kind: TaskNotifyKind\n readonly title: string\n readonly body: string\n readonly tag: string\n}\n\n/** Parse a `task-notify` SSE payload; rejects anything malformed or empty. */\nexport function parseTaskNotifyPayload(value: unknown): { readonly sessionId: string; readonly turn: number } | undefined {\n let record: Record<string, unknown> | undefined\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined\n record = parsed as Record<string, unknown>\n } catch {\n return undefined\n }\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n record = value as Record<string, unknown>\n } else return undefined\n if (typeof record.sessionId !== 'string' || record.sessionId.trim() === '') return undefined\n const turn = typeof record.turn === 'number' && Number.isSafeInteger(record.turn) && record.turn >= 0 ? record.turn : 0\n return Object.freeze({ sessionId: record.sessionId.slice(0, 128), turn })\n}\n\n/** Parse the small authenticated event used when the Host revokes this device. */\nexport function isDeviceRevokedPayload(value: unknown): boolean {\n let record: Record<string, unknown> | undefined\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return false\n record = parsed as Record<string, unknown>\n } catch {\n return false\n }\n } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n record = value as Record<string, unknown>\n } else return false\n return record.reason === 'device_revoked'\n}\n\n/** Tell the Android shell to transition the active record to the revoked state. */\nexport function fireDeviceRevoked(): boolean {\n const bridge = readNativeBridge()\n if (bridge === undefined) return false\n void bridge.invoke('device.revoked', {}).catch(() => undefined)\n return true\n}\n\n/**\n * Deliver one notification through the native bridge when available.\n * Returns false when there is no bridge (desktop browsers keep their own\n * UI) so callers can fall back; a denial flips the module sticky flag via\n * onDenied for the session-lifetime nag guard.\n */\nexport function fireTaskNotifyEvent(\n event: TaskNotifyWireEvent,\n onDenied?: () => void,\n): boolean {\n const bridge = readNativeBridge()\n if (bridge === undefined) return false\n void Promise.resolve()\n .then(() => bridge.capabilities())\n .then(capabilities => {\n if (!capabilities.includes('notification.notify')) return\n return bridge.invoke('notification.notify', { title: event.title, body: event.body, tag: event.tag })\n })\n .then(\n () => undefined,\n (error: unknown) => {\n const code = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : ''\n // A denial is sticky for this page load: never nag for permission.\n if ((code === 'permission_denied' || code === 'denied') && onDenied !== undefined) onDenied()\n },\n )\n return true\n}\n\n/**\n * Watch the rendered conversation for finished runs and waiting questions,\n * notifying through the native bridge while the page is hidden. Returns a\n * disposer. Silent when no native bridge exists (desktop browsers keep their\n * own UI) or after the user denies the permission.\n */\nexport function installTaskCompletionWatcher(options: TaskWatcherLabels): () => void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return () => undefined\n const tracker = new TaskNotifyTracker({ format: options.label })\n const completionFallback = options.completionFallback ?? true\n let notifyBlocked = false\n let timer = 0\n let anchorTimer = 0\n\n const fire = (event: TaskNotifyEvent): void => {\n if (notifyBlocked) return\n if (!fireTaskNotifyEvent(event, () => { notifyBlocked = true })) return\n }\n const evaluateNow = (): void => {\n const busy = completionFallback && readComposerBusyState(document)\n const event = tracker.evaluate({\n pageHidden: document.hidden,\n pendingQuestion: hasPendingInputQuestion(document),\n questionKey: pendingInputQuestionKey(document),\n sessionLabel: readSessionLabel(document.title),\n composerBusy: busy,\n })\n if (event !== undefined) fire(event)\n scheduleAnchorTimer()\n }\n // The interval is only a backstop: an anchor schedules its own exact shot\n // so completion announces ~grace after the run ends instead of waiting out\n // the next coarse tick with no further mutations to wake it.\n const scheduleAnchorTimer = (): void => {\n const delay = tracker.pendingAnchorDelayMs()\n if (delay === undefined) {\n if (anchorTimer !== 0) {\n window.clearTimeout(anchorTimer)\n anchorTimer = 0\n }\n return\n }\n if (anchorTimer !== 0) return\n anchorTimer = window.setTimeout(() => {\n anchorTimer = 0\n evaluateNow()\n }, delay)\n }\n const disposeObservation = observeTaskActivity(document.documentElement, () => {\n if (completionFallback) tracker.markActivity()\n evaluateNow()\n })\n const onVisibility = (): void => { evaluateNow() }\n document.addEventListener('visibilitychange', onVisibility)\n if (typeof window.setInterval === 'function') {\n timer = window.setInterval(evaluateNow, 15_000)\n }\n evaluateNow()\n return () => {\n disposeObservation()\n document.removeEventListener('visibilitychange', onVisibility)\n if (timer !== 0) window.clearInterval(timer)\n if (anchorTimer !== 0) window.clearTimeout(anchorTimer)\n }\n}\n","/**\n * Viewport below which the injected chrome becomes an overlay drawer: the\n * sidebar slides in as a sheet, the right panel as a drawer, and the scrim dims\n * the page behind them. The stylesheet and the scrim's visibility both read\n * this query, so a wider viewport never grows drawer chrome whose rules cannot\n * style it.\n */\nimport { installTaskCompletionWatcher, type TaskNotifyKind } from './task-notify.js'\n\nexport const NATIVE_MOBILE_OVERLAY_QUERY = '(max-width:720px)'\n\n/** Mobile feature and compatibility rules applied to DSH React surfaces. */\nexport const NATIVE_MOBILE_STYLES = `\n/* The surface appends chrome to <body> on every non-loopback page load, but\n every rule that gives that chrome a box lives inside the overlay query\n below. Outside the query the scrim kept the UA button box: an empty,\n nameless button in normal flow at the document's bottom-left, whose click\n still collapsed the sidebar. Keep the neutral state explicitly invisible —\n the query restores the fixed scrim, and its more specific [hidden] rule\n keeps winning there. */\n .dsh-native-mobile-backdrop,.dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:none; }\n .dsh-mobile-settings_row { display:flex; align-items:center; gap:8px; padding:16px 0; border-bottom:0.5px solid var(--dsw-alias-border-l2); }\n .dsh-mobile-settings_rowText { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; padding-right:48px; }\n .dsh-mobile-settings_title { color:var(--dsw-alias-label-primary); font-size:14px; font-weight:400; line-height:22px; }\n .dsh-mobile-settings_desc { color:var(--dsw-alias-label-tertiary); font-size:12px; font-weight:400; line-height:18px; }\n .dsh-mobile-settings_selector { display:inline-flex; align-items:center; justify-content:center; min-width:max-content; min-height:48px; padding:0 14px; border:0; border-radius:24px; background:var(--dsw-alias-bg-module-platform); color:var(--dsw-alias-label-primary); font:inherit; font-size:14px; line-height:22px; cursor:pointer; }\n .dsh-mobile-settings_selector:hover:not(:disabled) { background:var(--dsw-alias-interactive-bg-hover); }\n .dsh-mobile-settings_selector:active:not(:disabled) { background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover)); }\n .dsh-mobile-settings_selector:focus-visible { outline:2px solid var(--dsw-alias-label-primary-bluish,#2563eb); outline-offset:2px; }\n .dsh-mobile-settings_selector:disabled { cursor:wait; opacity:.55; }\n@media ${NATIVE_MOBILE_OVERLAY_QUERY} {\n html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }\n html.dsh-native-mobile-active { --dsh-mobile-motion-duration:200ms; --dsh-mobile-motion-ease:cubic-bezier(.22,1,.36,1); }\n html.dsh-native-mobile-active :is(a,button,[role=\"button\"],[role=\"tab\"],[tabindex]) { -webkit-tap-highlight-color:transparent; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [role=\"treeitem\"] { -webkit-tap-highlight-color:transparent; touch-action:manipulation; }\n html.dsh-native-mobile-active[data-dsh-mobile-input=\"touch\"] :is(a,button,[role=\"button\"],[role=\"tab\"],[tabindex]):focus { outline:none !important; }\n html.dsh-native-mobile-active [role=\"tooltip\"] { display:none !important; }\n /* Touch has no persistent hover affordance: keep workspace rows neutral after a tap. */\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] { --dsw-alias-interactive-bg-hover:transparent !important; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [role=\"treeitem\"]:is(:hover,:active,:focus,[aria-selected=\"true\"]),\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_sessionRow\"][class*=\"_selected\"],\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_searchResultRow\"][class*=\"_selected\"] { background:transparent !important; outline:0 !important; box-shadow:none !important; }\n /* Sidebar row menus are hover-only on desktop. Touch has no hover, so keep\n the ellipsis action visible and give it a reliable hit target. */\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_rowActions\"] { display:inline-flex !important; align-items:center !important; gap:8px !important; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_sessionRow\"] [class*=\"_time\"] { display:none !important; }\n html.dsh-native-mobile-active [data-dsh-mobile-sidebar] [class*=\"_rowActions\"] button { box-sizing:border-box !important; width:32px !important; min-width:32px !important; height:32px !important; min-height:32px !important; }\n [data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }\n [data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }\n [data-dsh-mobile-center] > * { min-width:0 !important; }\n [data-dsh-mobile-header] { box-sizing:border-box !important; width:calc(100% - 16px) !important; margin:0 8px !important; min-width:0; padding-top:max(4px,env(safe-area-inset-top)) !important; padding-right:8px !important; padding-left:42px !important; }\n [data-dsh-mobile-header] [class*=\"_titleRow\"] { box-sizing:border-box !important; display:flex !important; align-items:center !important; min-width:0; min-height:32px !important; height:32px !important; gap:6px !important; padding:0 6px !important; }\n [data-dsh-mobile-header] [class*=\"_titleCluster\"] { min-width:0; }\n [data-dsh-mobile-header] [class*=\"_crumbs\"] { min-width:0; overflow:hidden; }\n [data-dsh-mobile-header] [class*=\"_crumb\"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { min-width:0; overflow-x:auto; scrollbar-width:none; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-header] [class*=\"_headerUtilities\"] { gap:2px !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] > * { display:none !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"]::after { color:var(--dsw-alias-label-primary, #171a21); content:\"Log\"; font-size:11px; font-weight:600; }\n html[data-dsh-mobile-language=\"zh\"] [data-dsh-mobile-header] [class*=\"_sessionLogButton\"]::after { content:\"日志\"; }\n [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; }\n [data-dsh-mobile-header] [class*=\"_tab\"] { padding-bottom:5px !important; }\n [data-dsh-mobile-header] [class*=\"_tabs\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:max(env(safe-area-inset-top),0px) auto 0 0 !important; height:auto !important; transition:width 180ms var(--dsh-mobile-motion-ease),box-shadow 180ms ease !important; }\n [data-dsh-mobile-sidebar][data-open=\"true\"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; padding-top:0 !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-sidebar][data-open=\"true\"] [data-dsh-mobile-sidebar-root] [class*=\"_logoRow\"] { height:52px !important; padding:4px 0 4px 4px !important; margin-bottom:4px !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) { position:fixed !important; z-index:244 !important; top:env(safe-area-inset-top) !important; left:0 !important; box-sizing:border-box !important; width:50px !important; height:52px !important; padding:4px !important; border:0 !important; background:transparent !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) > :not([data-dsh-mobile-toggle]) { display:none !important; }\n /* The stock settings shell is mounted under the collapsed sidebar. Let\n its fixed overlay escape the zero-width drawer while it is open. */\n [data-dsh-mobile-sidebar]:has([data-dsh-mobile-settings]) { width:100vw !important; inset:0 !important; }\n [data-dsh-mobile-sidebar]:has([data-dsh-mobile-settings]) > * { width:100vw !important; overflow:visible !important; }\n [data-dsh-mobile-toggle] { width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-toggle] > svg[class*=\"_railFish\"] { transform:translateY(-4px) !important; }\n .dsh-native-mobile-backdrop { display:block; position:fixed; z-index:235; inset:env(safe-area-inset-top) 0 0; border:0; background:rgb(15 23 42 / 32%); }\n .dsh-native-mobile-backdrop:not([hidden]) { animation:dsh-mobile-fade-in var(--dsh-mobile-motion-duration) ease-out; }\n .dsh-native-mobile-backdrop[hidden] { display:none; }\n [data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); background:var(--dsw-alias-bg-layer-1, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-details][data-open=\"true\"] { transform:translateX(0); }\n [data-dsh-mobile-handle] { display:none !important; }\n [data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; animation:dsh-mobile-panel-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }\n [data-dsh-mobile-settings-nav] { flex:none !important; width:100% !important; padding:max(14px,env(safe-area-inset-top)) 12px 8px !important; gap:10px !important; border-bottom:1px solid var(--dsw-alias-border-l2,#e8ebef); }\n [data-dsh-mobile-settings-nav] [class*=\"_navTitle\"] { padding:0 8px !important; font-size:18px !important; line-height:28px !important; }\n [data-dsh-mobile-settings-list] { flex-direction:row !important; gap:4px !important; overflow-x:auto !important; scrollbar-width:none; }\n [data-dsh-mobile-settings-list]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-settings-list] [class*=\"_navCell\"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }\n [data-dsh-mobile-settings-list] [aria-current=\"true\"] { border-color:transparent !important; outline:0 !important; box-shadow:none !important; }\n [data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }\n [data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }\n [data-dsh-mobile-settings-header] [class*=\"_close\"] { width:36px !important; height:36px !important; }\n [data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }\n [data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] > [class*=\"_row\"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_rowText\"] { width:100% !important; padding-right:0 !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_selector\"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:48px !important; max-width:100% !important; }\n [data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }\n [data-dsh-mobile-settings-options] :is(input,select,textarea) { box-sizing:border-box; width:100%; min-width:0; }\n [data-dsh-mobile-settings-options] [class*=\"_head\"] { min-width:0; flex-wrap:wrap; }\n /* Provider names may shrink, but their edit/delete actions remain horizontal\n and retain a full touch target on narrow screens. */\n [data-dsh-mobile-settings-options] [class*=\"_rowHead\"]:has(> [class*=\"_rowIdentity\"]) { flex-wrap:nowrap !important; align-items:center !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowIdentity\"] { flex:1 1 auto !important; min-width:0 !important; overflow:hidden !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowName\"] { min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowActions\"] { flex:0 0 auto !important; flex-wrap:nowrap !important; width:max-content !important; min-width:max-content !important; max-width:none !important; }\n [data-dsh-mobile-settings-options] [class*=\"_rowActions\"] button { flex:none !important; width:auto !important; min-width:44px !important; max-width:none !important; min-height:44px !important; padding-inline:10px !important; white-space:nowrap !important; word-break:keep-all !important; writing-mode:horizontal-tb !important; }\n [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition=\"true\"],\n [data-dsh-mobile-view][data-dsh-mobile-view-transition=\"true\"] { animation:dsh-mobile-view-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }\n [data-dsh-mobile-center] textarea { font-size:16px !important; }\n /* Markdown tables use content-sized columns. Small tables fill the phone;\n wider tables keep readable cells and scroll inside their own region. */\n [data-dsh-mobile-table-scroll] { box-sizing:border-box; width:100%; max-width:100%; overflow-x:auto; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch; }\n [data-dsh-mobile-table-scroll] table { display:table !important; width:max-content !important; min-width:100% !important; max-width:none !important; table-layout:auto !important; }\n [data-dsh-mobile-table-scroll] :is(th,td) { box-sizing:border-box; min-width:8ch; max-width:32ch; overflow-wrap:anywhere; word-break:break-word; vertical-align:top; }\n [data-dsh-mobile-center] pre { max-width:100%; overflow-x:auto; }\n [data-dsh-mobile-center] :is(img,video,canvas,svg) { max-width:100%; }\n [data-dsh-mobile-message-scroll] { box-sizing:border-box !important; width:100% !important; padding:8px 10px 20px !important; }\n [data-dsh-mobile-history-loader] { position:relative !important; min-height:1px !important; }\n [data-dsh-mobile-history-loader] button:not(:disabled) { position:absolute !important; width:1px !important; height:1px !important; margin:-1px !important; padding:0 !important; clip-path:inset(50%) !important; opacity:0 !important; overflow:hidden !important; pointer-events:none !important; }\n [data-dsh-mobile-history-loader] button:disabled { min-height:28px !important; padding:4px 12px !important; }\n [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:10px !important; }\n [data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] { box-sizing:border-box !important; display:grid !important; grid-template-columns:16px minmax(0,1fr) !important; grid-auto-rows:auto !important; align-items:center !important; column-gap:6px !important; width:100% !important; height:auto !important; min-height:40px !important; padding:4px 0 !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_leading\"] { grid-column:1 !important; grid-row:1 !important; margin-right:0 !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_title\"] { grid-column:2 !important; grid-row:1 !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*=\"_sep\"],[class*=\"_separator\"]) { display:none !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*=\"_summary\"],[class*=\"_fileLink\"]) { grid-column:2 !important; grid-row:2 !important; width:100% !important; min-width:0 !important; max-width:100% !important; overflow:hidden !important; line-height:19px !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_summarySuffix\"] { grid-column:2 !important; grid-row:3 !important; margin-left:0 !important; }\n [data-dsh-mobile-message-column] [data-context-fields] > * { display:grid !important; grid-template-columns:minmax(72px,30%) minmax(0,1fr) !important; gap:4px 10px !important; }\n [data-dsh-mobile-message-column] [class*=\"_ioSection\"] { grid-template-columns:1fr !important; row-gap:4px !important; }\n [data-dsh-mobile-message-column] [class*=\"_body\"] { max-width:100% !important; overflow-wrap:anywhere; }\n /* Keep folded and expanded reasoning visually separate from the reply. */\n [data-dsh-mobile-message-column] [class*=\"_body\"] > div:has(> [data-variant=\"think\"]) { margin-bottom:12px !important; }\n [data-dsh-mobile-center] [data-composer-card] ~ [class*=\"_root\"],\n [data-dsh-mobile-center] [data-composer-card] ~ * [class*=\"_root\"] { box-sizing:border-box !important; width:100% !important; max-width:100% !important; margin-bottom:-6px !important; padding:3px 4px 0 !important; font-size:11px !important; line-height:18px !important; white-space:normal !important; overflow:visible !important; text-overflow:clip !important; }\n [data-dsh-mobile-center] [data-composer-card] ~ [class*=\"_root\"] [class*=\"_sep\"],\n [data-dsh-mobile-center] [data-composer-card] ~ * [class*=\"_root\"] [class*=\"_sep\"] { margin:0 6px !important; }\n /* Composer dock stats strip (turns/steps/tokens) reads small on phones. */\n [data-dsh-mobile-center] [data-slot=\"conversation.composer.dock\"] [class*=\"_root\"] { font-size:10px !important; line-height:16px !important; }\n /* Message runtime details are inline on desktop. Give the clock/runtime\n label its own wrapping row on narrow screens so TTFT and throughput do\n not push the action buttons or clip at the viewport edge. */\n [data-dsh-mobile-center] [class*=\"_actions\"]:has(> [class*=\"_timeStart\"]),\n [data-dsh-mobile-center] [class*=\"_actions\"]:has(> [class*=\"_timeEnd\"]) { box-sizing:border-box !important; width:100% !important; flex-wrap:wrap !important; justify-content:flex-end !important; height:auto !important; min-height:28px !important; row-gap:2px !important; }\n [data-dsh-mobile-center] [class*=\"_timeStart\"],\n [data-dsh-mobile-center] [class*=\"_timeEnd\"] { box-sizing:border-box !important; flex:1 1 100% !important; order:2 !important; min-width:0 !important; max-width:100% !important; padding:0 !important; line-height:20px !important; text-align:center !important; white-space:normal !important; overflow-wrap:anywhere !important; }\n [data-dsh-mobile-center] [class*=\"_timeStart\"] { box-sizing:border-box !important; flex:1 1 100% !important; order:2 !important; min-width:0 !important; max-width:100% !important; padding:0 !important; line-height:20px !important; text-align:center !important; white-space:normal !important; overflow-wrap:anywhere !important; }\n [data-dsh-mobile-center] [class*=\"_timeStart\"] [class*=\"_runTimeDot\"],\n [data-dsh-mobile-center] [class*=\"_timeEnd\"] [class*=\"_runTimeDot\"] { margin:0 6px !important; }\n /* Keep the context meter's legend rows as readable label/value pairs.\n Generic mobile flex rules can otherwise place the rows side by side and\n break Chinese labels in the middle of a word. */\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"],\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] { width:min(264px,calc(100vw - 32px)) !important; min-width:0 !important; max-width:calc(100vw - 32px) !important; }\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"] [class*=\"_rows\"],\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] [class*=\"_rows\"] { display:block !important; }\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"] [class*=\"_rows\"] > [class*=\"_row\"],\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] [class*=\"_rows\"] > [class*=\"_row\"] { display:flex !important; align-items:center !important; justify-content:space-between !important; width:100% !important; min-width:0 !important; white-space:nowrap !important; }\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"上下文\"] :is(dt,dd),\n [data-dsh-mobile-center] [role=\"dialog\"][aria-label*=\"Context\"] :is(dt,dd) { white-space:nowrap !important; word-break:keep-all !important; }\n .dsh-mobile-branch-toast,.dsh-mobile-media-toast { display:block; position:fixed; z-index:330; top:max(12px,env(safe-area-inset-top)); left:50%; max-width:calc(100vw - 32px); box-sizing:border-box; padding:7px 14px; border:1px solid rgb(15 23 42 / 10%); border-radius:999px; background:rgb(15 23 42 / 92%); color:#fff; font-size:13px; line-height:20px; text-align:center; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; opacity:0; pointer-events:none; transform:translate(-50%,-8px); transition:opacity 160ms ease,transform 160ms ease; }\n .dsh-mobile-branch-toast[data-visible=\"true\"],.dsh-mobile-media-toast[data-visible=\"true\"] { opacity:1; transform:translate(-50%,0); }\n /* Fallback only: when the native Add group has no item to clone, keep the\n camera action on the same row metrics and tokens as the native menu. */\n .dsh-mobile-media-action { box-sizing:border-box; display:flex; align-items:center; justify-content:flex-start; gap:8px; width:100%; min-width:0; min-height:40px; padding:8px 10px; border:0; border-radius:10px; background:transparent; color:var(--dsw-alias-label-primary,inherit); cursor:pointer; font:inherit; font-size:14px; line-height:22px; text-align:left; touch-action:manipulation; }\n .dsh-mobile-media-action:active { opacity:.72; }\n .dsh-mobile-media-action:focus-visible { outline:2px solid var(--dsw-alias-state-business-primary,#4c82f7); outline-offset:1px; }\n .dsh-mobile-media-action:disabled { cursor:default; opacity:.38; }\n .dsh-mobile-media-action svg { flex:none; width:16px; height:16px; color:var(--dsw-alias-label-tertiary,currentColor); }\n .dsh-mobile-media-action span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-center] [class*=\"_composer\"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }\n /* The desktop composer intentionally wraps whole toolbar groups. On a phone,\n dynamic model and status labels made that row alternate between one and\n two lines. Keep two stable columns and let only the model label shrink. */\n [data-dsh-mobile-composer-row] { display:grid !important; grid-template-columns:max-content minmax(0,1fr) !important; align-items:center !important; gap:4px 8px !important; }\n [data-dsh-mobile-composer-tools] { display:flex !important; flex-wrap:nowrap !important; width:max-content !important; min-width:0 !important; max-width:max-content !important; gap:6px !important; }\n [data-dsh-mobile-composer-trailing] { display:flex !important; flex-wrap:nowrap !important; width:100% !important; min-width:0 !important; max-width:100% !important; gap:6px !important; margin-left:0 !important; justify-content:flex-end !important; }\n [data-dsh-mobile-composer-model] { flex:1 1 0 !important; width:auto !important; min-width:0 !important; max-width:none !important; }\n [data-dsh-mobile-composer-model-trigger] { box-sizing:border-box !important; width:100% !important; max-width:100% !important; min-width:0 !important; padding-left:6px !important; padding-right:4px !important; }\n [data-dsh-mobile-composer-model-label] { flex:1 1 auto !important; max-width:none !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) { box-sizing:border-box !important; width:100% !important; padding:0 0 8px !important; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) > [class=\"\"]:last-child { display:none !important; }\n}\n@keyframes dsh-mobile-fade-in { from { opacity:0; } }\n@keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }\n@keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }\n@media (max-width:420px) {\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { max-width:42vw; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_selector\"] { align-self:stretch !important; width:100% !important; }\n [data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }\n}\n@media (prefers-reduced-motion:reduce) {\n [data-dsh-mobile-sidebar-root],[data-dsh-mobile-details] { transition:none !important; }\n .dsh-native-mobile-backdrop:not([hidden]),[data-dsh-mobile-settings],\n [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition=\"true\"],\n [data-dsh-mobile-view][data-dsh-mobile-view-transition=\"true\"] { animation:none !important; }\n}\n`\n\nfunction classToken(element: Element, suffix: string): boolean {\n return Array.from(element.classList).some(value => value.endsWith(suffix))\n}\n\nfunction firstByClassSuffix(root: ParentNode, suffix: string): HTMLElement | undefined {\n return Array.from(root.querySelectorAll<HTMLElement>('[class]')).find(element => classToken(element, suffix))\n}\n\n/** Find the stock DSH application frame without mistaking a feature card for the shell. */\nexport function resolveNativeMobileFrame(root: ParentNode, dedicatedCenter: HTMLElement | undefined): HTMLElement | undefined {\n if (dedicatedCenter !== undefined) return undefined\n return Array.from(root.querySelectorAll<HTMLElement>('[class]')).find(candidate => {\n return classToken(candidate, '_frame')\n && firstByClassSuffix(candidate, '_sidebarCol') !== undefined\n && firstByClassSuffix(candidate, '_centerCol') !== undefined\n })\n}\n\n/** Mark every mounted settings dialog so its mobile layout does not depend on the conversation shell. */\nexport function markNativeMobileSettings(root: ParentNode): number {\n let marked = 0\n for (const dialog of root.querySelectorAll<HTMLElement>('[role=\"dialog\"]')) {\n const children = Array.from(dialog.children) as HTMLElement[]\n const nav = children.find(child => classToken(child, '_nav'))\n const content = children.find(child => classToken(child, '_content'))\n if (nav === undefined || content === undefined) continue\n dialog.dataset.dshMobileSettings = 'true'\n nav.dataset.dshMobileSettingsNav = 'true'\n content.dataset.dshMobileSettingsContent = 'true'\n firstByClassSuffix(nav, '_navList')?.setAttribute('data-dsh-mobile-settings-list', 'true')\n firstByClassSuffix(content, '_header')?.setAttribute('data-dsh-mobile-settings-header', 'true')\n firstByClassSuffix(content, '_options')?.setAttribute('data-dsh-mobile-settings-options', 'true')\n marked += 1\n }\n return marked\n}\n\nconst AUTO_HISTORY_THRESHOLD_PX = 64\n\nexport type NativeMobileLanguage = 'it' | 'en' | 'zh'\n\ninterface FileDropTarget { dispatchEvent(event: Event): boolean }\n\nfunction controlledFileDragEvent(type: 'dragover' | 'drop', files: readonly File[], initialDropEffect: DataTransfer['dropEffect']): DragEvent {\n const event = new Event(type, { bubbles: true, cancelable: true }) as DragEvent\n const dataTransfer = {\n dropEffect: initialDropEffect,\n effectAllowed: 'copy',\n files: Object.freeze([...files]),\n types: Object.freeze(['Files']),\n } as unknown as DataTransfer\n Object.defineProperty(event, 'dataTransfer', { value: dataTransfer })\n return event\n}\n\n/** Ask the official document dragover listener whether the current composer accepts files. */\nexport function preflightComposerImageDrop(target: FileDropTarget, files: readonly File[]): boolean {\n if (files.length === 0) return false\n const event = controlledFileDragEvent('dragover', files, 'none')\n target.dispatchEvent(event)\n return event.dataTransfer?.dropEffect === 'copy'\n}\n\n/** Dispatch a real drop only after a fresh official-listener preflight; the result is not an attachment ACK. */\nexport function dispatchComposerImageDrop(target: FileDropTarget, files: readonly File[]): boolean {\n if (!preflightComposerImageDrop(target, files)) return false\n target.dispatchEvent(controlledFileDragEvent('drop', files, 'copy'))\n return true\n}\n\n/** Apply the resolved locale independently from the document's possibly different lang attribute. */\nexport function applyNativeMobileLanguageMarker(root: Pick<HTMLElement, 'dataset'>, language: NativeMobileLanguage): () => void {\n const previous = root.dataset.dshMobileLanguage\n root.dataset.dshMobileLanguage = language\n return () => {\n if (root.dataset.dshMobileLanguage !== language) return\n if (previous === undefined) delete root.dataset.dshMobileLanguage\n else root.dataset.dshMobileLanguage = previous\n }\n}\n\n/** Resolve the supported language used by native-mobile controls. */\nexport function resolveNativeMobileLanguage(\n documentLanguage: string,\n browserLanguages: readonly string[],\n): NativeMobileLanguage {\n return [documentLanguage, ...browserLanguages]\n .map(value => value.trim().toLowerCase().split(/[-_]/u)[0])\n .find((value): value is NativeMobileLanguage => value === 'it' || value === 'en' || value === 'zh') ?? 'en'\n}\n\n/** Whether a user-driven scroll moved upward into the automatic history-loading zone. */\nexport function shouldAutoLoadEarlier(previousTop: number, currentTop: number): boolean {\n return currentTop <= AUTO_HISTORY_THRESHOLD_PX && currentTop < previousTop - 0.5\n}\n\n/**\n * Whether the overlay scrim belongs on screen. The scrim exists for the\n * slide-in drawer only — it dims the page and catches the tap that closes the\n * drawer — so it is shown while that drawer is open *and* the overlay query is\n * in force. `hidden` carries the whole decision rather than the width rules\n * alone: the attribute still hides the element when the injected stylesheet\n * never lands (a CSP without inline styles, a shell that drops the <style>\n * node), where the element would otherwise fall back to the UA button box.\n */\nexport function drawerScrimVisible(sidebarCollapsed: boolean, overlayActive: boolean): boolean {\n return !sidebarCollapsed && overlayActive\n}\n\ninterface ComposerMediaOrigin {\n readonly generation: number\n readonly href: string\n readonly composer: object | null\n readonly sessionRoot: object | null\n readonly sessionId: string | null\n}\n\n/** Check that an asynchronous picker result still belongs to its originating session and composer. */\nexport function isComposerMediaOriginCurrent(\n origin: ComposerMediaOrigin,\n current: ComposerMediaOrigin & { readonly disposed: boolean; readonly composerConnected: boolean },\n): boolean {\n return !current.disposed\n && origin.generation === current.generation\n && origin.href === current.href\n && origin.composer !== null\n && current.composerConnected\n && origin.composer === current.composer\n && origin.sessionRoot !== null\n && origin.sessionRoot === current.sessionRoot\n && origin.sessionId !== null\n && origin.sessionId === current.sessionId\n}\n\n/** Add mobile semantics without replacing feature trees. */\nexport function installNativeMobileSurface(): () => void {\n document.documentElement.classList.add('dsh-native-mobile-active')\n const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language]\n const language = resolveNativeMobileLanguage(document.documentElement.lang, browserLanguages)\n const restoreLanguageMarker = applyNativeMobileLanguageMarker(document.documentElement, language)\n const label = (italian: string, english: string, chinese: string): string => language === 'it' ? italian : language === 'zh' ? chinese : english\n const mediaIcon = (): SVGSVGElement => {\n const namespace = 'http://www.w3.org/2000/svg'\n const icon = document.createElementNS(namespace, 'svg')\n icon.setAttribute('viewBox', '0 0 16 16')\n icon.setAttribute('fill', 'none')\n icon.setAttribute('aria-hidden', 'true')\n const body = document.createElementNS(namespace, 'path')\n body.setAttribute('d', 'M5.15 3.2 6.05 2h3.9l.9 1.2h1.45c1.05 0 1.9.85 1.9 1.9v6c0 1.05-.85 1.9-1.9 1.9H3.7a1.9 1.9 0 0 1-1.9-1.9v-6c0-1.05.85-1.9 1.9-1.9h1.45Zm-1.45 1.3a.6.6 0 0 0-.6.6v6c0 .33.27.6.6.6h8.6a.6.6 0 0 0 .6-.6v-6a.6.6 0 0 0-.6-.6h-2.1l-.9-1.2H6.7l-.9 1.2H3.7Z')\n body.setAttribute('fill', 'currentColor')\n const lens = document.createElementNS(namespace, 'circle')\n lens.setAttribute('cx', '8')\n lens.setAttribute('cy', '8.1')\n lens.setAttribute('r', '2.15')\n lens.setAttribute('stroke', 'currentColor')\n lens.setAttribute('stroke-width', '1.3')\n icon.append(body, lens)\n return icon\n }\n const createMediaAction = (text: string): HTMLButtonElement => {\n const button = document.createElement('button')\n button.type = 'button'\n button.className = 'dsh-mobile-media-action'\n button.lang = language\n button.dataset.dshMobileMediaAction = 'camera'\n const icon = document.createElement('span')\n icon.append(mediaIcon())\n button.append(icon)\n const caption = document.createElement('span')\n caption.textContent = text\n button.append(caption)\n return button\n }\n const setInputMode = (mode: 'keyboard' | 'touch'): void => {\n document.documentElement.dataset.dshMobileInput = mode\n }\n const onPointerDown = (event: PointerEvent): void => {\n if (event.pointerType === 'touch' || event.pointerType === 'pen') setInputMode('touch')\n }\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Tab' || event.key.startsWith('Arrow')) setInputMode('keyboard')\n }\n document.addEventListener('pointerdown', onPointerDown, true)\n document.addEventListener('keydown', onKeyDown, true)\n // The scrim is drawer chrome: it belongs on screen only while the overlay\n // query matches. A resize crosses that breakpoint without touching the DOM\n // the observer below watches, so the query wakes the same sync pass itself.\n const overlayQuery = window.matchMedia(NATIVE_MOBILE_OVERLAY_QUERY)\n const backdrop = document.createElement('button')\n backdrop.type = 'button'\n backdrop.className = 'dsh-native-mobile-backdrop'\n backdrop.lang = language\n backdrop.hidden = true\n backdrop.setAttribute('aria-label', label('Chiudi navigazione area di lavoro', 'Close workspace navigation', '关闭工作区导航'))\n document.body.append(backdrop)\n const branchToast = document.createElement('div')\n branchToast.className = 'dsh-mobile-branch-toast'\n branchToast.lang = language\n branchToast.setAttribute('role', 'status')\n branchToast.setAttribute('aria-live', 'polite')\n document.body.append(branchToast)\n const mediaToast = document.createElement('div')\n mediaToast.className = 'dsh-mobile-media-toast'\n mediaToast.lang = language\n mediaToast.setAttribute('role', 'status')\n mediaToast.setAttribute('aria-live', 'polite')\n document.body.append(mediaToast)\n const cameraButton = createMediaAction(label('Scatta foto', 'Take photo', '拍照'))\n cameraButton.disabled = true\n let branchToastTimer = 0\n let mediaToastTimer = 0\n const showMediaToast = (message: string): void => {\n mediaToast.textContent = message\n mediaToast.dataset.visible = 'true'\n if (mediaToastTimer !== 0) window.clearTimeout(mediaToastTimer)\n mediaToastTimer = window.setTimeout(() => {\n mediaToast.removeAttribute('data-visible')\n mediaToastTimer = 0\n }, 2200)\n }\n let mediaRequestGeneration = 0\n let disposed = false\n let boundComposer: Element | null = null\n let boundSessionRoot: Element | null = null\n let boundSessionId: string | null = null\n const preflightFile = new File([], 'dsh-mobile-preflight.png', { type: 'image/png' })\n const canAcceptComposerDrop = (): boolean => preflightComposerImageDrop(document, [preflightFile])\n const mediaPickerAbortController = new AbortController()\n const browserPickerCleanups = new Set<() => void>()\n const sessionTokens = new WeakMap<Element, string>()\n let nextSessionToken = 0\n type MediaRequestContext = ComposerMediaOrigin & { readonly composer: Element | null; readonly sessionRoot: Element | null }\n const currentSessionOrigin = (): { readonly sessionRoot: Element | null; readonly sessionId: string | null } => {\n const dedicatedRoot = boundComposer?.closest('[data-dsh-mobile-session]') ?? null\n const dedicatedId = dedicatedRoot?.getAttribute('data-dsh-mobile-session')\n if (dedicatedRoot !== null && typeof dedicatedId === 'string' && dedicatedId !== '') {\n return { sessionRoot: dedicatedRoot, sessionId: dedicatedId }\n }\n const selectedRow = document.querySelector<Element>('[role=\"treeitem\"][aria-selected=\"true\"]')\n if (selectedRow === null) return { sessionRoot: null, sessionId: null }\n let token = sessionTokens.get(selectedRow)\n if (token === undefined) { token = `stock-${String(++nextSessionToken)}`; sessionTokens.set(selectedRow, token) }\n const identity = selectedRow.getAttribute('data-session-id')\n ?? selectedRow.getAttribute('aria-label')\n ?? selectedRow.textContent?.trim()\n ?? ''\n return { sessionRoot: selectedRow, sessionId: `${token}:${identity}` }\n }\n const mediaRequestContext = (): MediaRequestContext => {\n const session = currentSessionOrigin()\n return {\n generation: ++mediaRequestGeneration,\n href: window.location.href,\n composer: boundComposer,\n ...session,\n }\n }\n const mediaRequestIsCurrent = (context: MediaRequestContext): boolean => {\n const session = currentSessionOrigin()\n const composer = boundComposer\n return isComposerMediaOriginCurrent(context, {\n generation: mediaRequestGeneration,\n href: window.location.href,\n composer,\n sessionRoot: session.sessionRoot,\n sessionId: session.sessionId,\n disposed,\n composerConnected: context.composer?.isConnected === true,\n })\n }\n const deliverImages = (files: readonly File[], context: ReturnType<typeof mediaRequestContext>): void => {\n if (files.length === 0 || !mediaRequestIsCurrent(context)) return\n dispatchComposerImageDrop(document, files)\n }\n const launchBrowserCamera = (context: ReturnType<typeof mediaRequestContext>): void => {\n const input = document.createElement('input')\n input.type = 'file'\n input.accept = 'image/*'\n input.capture = 'environment'\n input.hidden = true\n const signal = mediaPickerAbortController.signal\n let cleanupTimer = 0\n let watchdogTimer = 0\n let cleaned = false\n const scheduleCleanup = (): void => {\n if (!cleaned && cleanupTimer === 0) cleanupTimer = window.setTimeout(cleanup, 1000)\n }\n const onVisibilityChange = (): void => {\n if (document.visibilityState === 'visible') scheduleCleanup()\n }\n const onChange = (): void => {\n if (cleaned) return\n const files = input.files === null ? [] : [...input.files]\n cleanup()\n deliverImages(files, context)\n }\n const cleanup = (): void => {\n if (cleaned) return\n cleaned = true\n if (cleanupTimer !== 0) window.clearTimeout(cleanupTimer)\n if (watchdogTimer !== 0) window.clearTimeout(watchdogTimer)\n cleanupTimer = 0\n watchdogTimer = 0\n input.removeEventListener('change', onChange)\n input.removeEventListener('cancel', cleanup)\n window.removeEventListener('focus', scheduleCleanup)\n document.removeEventListener('visibilitychange', onVisibilityChange)\n signal.removeEventListener('abort', cleanup)\n browserPickerCleanups.delete(cleanup)\n input.remove()\n }\n input.addEventListener('change', onChange)\n input.addEventListener('cancel', cleanup)\n window.addEventListener('focus', scheduleCleanup)\n document.addEventListener('visibilitychange', onVisibilityChange)\n signal.addEventListener('abort', cleanup, { once: true })\n watchdogTimer = window.setTimeout(cleanup, 300_000)\n browserPickerCleanups.add(cleanup)\n if (signal.aborted) { cleanup(); return }\n try {\n document.body.append(input)\n input.click()\n } catch {\n cleanup()\n if (mediaRequestIsCurrent(context)) showMediaToast(label('Impossibile aprire la fotocamera', 'Could not open the camera', '无法打开相机'))\n }\n }\n const dismissCommandMenu = (): void => {\n const trigger = boundComposer?.querySelector<HTMLButtonElement>('button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]')\n trigger?.click()\n }\n const addGroupLabel = label('Aggiungi', 'Add', '添加')\n const placeCameraAction = (commandMenu: HTMLElement): boolean => {\n const sectionTitle = Array.from(commandMenu.querySelectorAll<HTMLElement>('[role=\"listbox\"] [role=\"presentation\"]'))\n .find(candidate => !candidate.hasAttribute('data-source') && candidate.textContent?.trim() === addGroupLabel)\n if (sectionTitle === undefined) {\n cameraButton.remove()\n return false\n }\n\n // Reuse the current DSH option classes so this injected action follows the\n // native menu's spacing, colors, and dark-mode states instead of creating a\n // second mobile-only visual language.\n cameraButton.className = 'dsh-mobile-media-action'\n const nativeItem = (() => {\n let sibling = sectionTitle.nextElementSibling\n while (sibling instanceof HTMLElement && sibling.getAttribute('role') !== 'presentation') {\n if (sibling.matches('button[role=\"option\"]:not([aria-selected=\"true\"])')) return sibling\n sibling = sibling.nextElementSibling\n }\n return undefined\n })()\n if (nativeItem !== undefined) {\n cameraButton.className = nativeItem.className\n const cameraIcon = cameraButton.firstElementChild\n const cameraLabel = cameraButton.lastElementChild\n const nativeIcon = firstByClassSuffix(nativeItem, '_itemIcon')\n const nativeLabel = firstByClassSuffix(nativeItem, '_itemName')\n if (cameraIcon instanceof HTMLElement && nativeIcon !== undefined) cameraIcon.className = nativeIcon.className\n if (cameraLabel instanceof HTMLElement && nativeLabel !== undefined) cameraLabel.className = nativeLabel.className\n }\n cameraButton.setAttribute('role', 'option')\n cameraButton.setAttribute('aria-selected', 'false')\n cameraButton.setAttribute('aria-label', label('Scatta foto', 'Take photo', '拍照'))\n\n let lastItem: Element = sectionTitle\n let sibling = sectionTitle.nextElementSibling\n while (sibling instanceof HTMLElement && sibling.getAttribute('role') !== 'presentation') {\n if (sibling !== cameraButton) lastItem = sibling\n sibling = sibling.nextElementSibling\n }\n if (cameraButton.parentElement !== sectionTitle.parentElement || cameraButton.previousElementSibling !== lastItem) {\n lastItem.after(cameraButton)\n }\n return true\n }\n const setCameraActionDisabled = (disabled: boolean, title: string): void => {\n if (cameraButton.disabled !== disabled) cameraButton.disabled = disabled\n if (cameraButton.title !== title) cameraButton.title = title\n }\n const quietMediaPointer = (event: PointerEvent): void => {\n event.preventDefault()\n event.stopPropagation()\n const active = document.activeElement\n if (active instanceof HTMLElement && (active.matches('input,textarea') || active.isContentEditable)) active.blur()\n }\n const takePhoto = (): void => {\n if (!canAcceptComposerDrop()) {\n setCameraActionDisabled(true, label('Allegati immagine non disponibili', 'Image attachments are unavailable', '图片附件不可用'))\n dismissCommandMenu()\n return\n }\n const context = mediaRequestContext()\n dismissCommandMenu()\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined) {\n launchBrowserCamera(context)\n return\n }\n void Promise.resolve().then(() => bridge.invoke('camera.capture', {})).then(value => {\n if (!mediaRequestIsCurrent(context)) return\n if (value instanceof File) deliverImages([value], context)\n else showMediaToast(label('La foto scattata non è utilizzabile', 'The captured photo is unavailable', '所拍照片不可用'))\n }).catch((error: unknown) => {\n if (!mediaRequestIsCurrent(context)) return\n const code = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : ''\n if (code === 'cancelled') return\n showMediaToast(code === 'payload_too_large'\n ? label('La foto supera il limite di 8 MiB', 'The photo exceeds the 8 MiB limit', '照片超过 8 MiB 限制')\n : label('Impossibile aggiungere la foto', 'Could not attach the photo', '无法附加照片'))\n })\n }\n cameraButton.addEventListener('pointerdown', quietMediaPointer)\n cameraButton.addEventListener('click', takePhoto)\n const showBranchToast = (): void => {\n const header = document.querySelector<HTMLElement>('[data-dsh-mobile-header]')\n const title = header === null ? undefined : header.querySelector<HTMLElement>('[class*=\"_crumbCurrent\"]')?.textContent?.trim()\n const prefix = label('Ramo corrente', 'Current branch', '当前分支')\n branchToast.textContent = title === undefined ? prefix : `${prefix}: ${title}`\n branchToast.dataset.visible = 'true'\n if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer)\n branchToastTimer = window.setTimeout(() => {\n branchToast.removeAttribute('data-visible')\n branchToastTimer = 0\n }, 1600)\n }\n const onBranchClick = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const branch = event.target.closest<HTMLButtonElement>('button[aria-label*=\"分支\"],button[aria-label*=\"Branch\"],button[aria-label*=\"branch\"],button[aria-label*=\"Ramo\"],button[aria-label*=\"ramo\"]')\n if (branch === null || branch.hasAttribute('disabled') || branch.getAttribute('aria-disabled') === 'true') return\n window.setTimeout(showBranchToast, 80)\n }\n document.addEventListener('click', onBranchClick, true)\n let frame: HTMLElement | undefined\n let sidebar: HTMLElement | undefined\n let sidebarRoot: HTMLElement | undefined\n let toggle: HTMLButtonElement | undefined\n let viewArea: HTMLElement | undefined\n let scheduled = 0\n let transitionFrame = 0\n let transitionRestartFrame = 0\n let transitionTimer = 0\n let transitionTarget: HTMLElement | undefined\n let historyScroller: HTMLElement | undefined\n let historyPreviousTop = 0\n const historyLoadButton = (): HTMLButtonElement | undefined => {\n const loader = historyScroller === undefined ? undefined : firstByClassSuffix(historyScroller, '_older')\n return loader?.querySelector<HTMLButtonElement>('button') ?? undefined\n }\n const onHistoryScroll = (): void => {\n if (historyScroller === undefined) return\n const currentTop = Math.max(0, historyScroller.scrollTop)\n const shouldLoad = shouldAutoLoadEarlier(historyPreviousTop, currentTop)\n historyPreviousTop = currentTop\n if (!shouldLoad) return\n const button = historyLoadButton()\n if (button === undefined || button.disabled || button.getAttribute('aria-disabled') === 'true') return\n button.click()\n }\n const bindHistoryScroller = (next: HTMLElement | undefined): void => {\n if (historyScroller === next) return\n historyScroller?.removeEventListener('scroll', onHistoryScroll)\n historyScroller = next\n historyPreviousTop = next?.scrollTop ?? 0\n historyScroller?.addEventListener('scroll', onHistoryScroll, { passive: true })\n }\n const animateNavigation = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const trigger = event.target.closest<HTMLElement>('button,a,[role=\"tab\"],[aria-selected]')\n if (trigger === null || trigger.hasAttribute('disabled') || trigger.getAttribute('aria-disabled') === 'true') return\n if (trigger.getAttribute('aria-selected') === 'true' || trigger.getAttribute('aria-current') === 'true') return\n const settingsNavigation = trigger.closest('[data-dsh-mobile-settings-list]') !== null\n const conversationNavigation = trigger.matches('[role=\"tab\"]')\n const sidebarNavigation = trigger.closest('[data-dsh-mobile-sidebar-root]') !== null\n && trigger.closest('[data-dsh-mobile-toggle]') === null\n if (!settingsNavigation && !conversationNavigation && !sidebarNavigation) return\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return\n if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame)\n if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame)\n transitionFrame = requestAnimationFrame(() => {\n transitionFrame = 0\n const target = settingsNavigation\n ? document.querySelector<HTMLElement>('[data-dsh-mobile-settings-content]')\n : viewArea\n if (target === null || target === undefined) return\n transitionTarget?.removeAttribute('data-dsh-mobile-view-transition')\n target.removeAttribute('data-dsh-mobile-view-transition')\n transitionRestartFrame = requestAnimationFrame(() => {\n transitionRestartFrame = 0\n transitionTarget = target\n target.dataset.dshMobileViewTransition = 'true'\n if (transitionTimer !== 0) clearTimeout(transitionTimer)\n transitionTimer = window.setTimeout(() => {\n target.removeAttribute('data-dsh-mobile-view-transition')\n if (transitionTarget === target) transitionTarget = undefined\n transitionTimer = 0\n }, 240)\n })\n })\n }\n document.addEventListener('click', animateNavigation)\n const syncMediaBinding = (composer: Element | null): void => {\n const previousComposer = boundComposer\n boundComposer = composer\n const session = currentSessionOrigin()\n if (composer === previousComposer && session.sessionRoot === boundSessionRoot && session.sessionId === boundSessionId) return\n boundSessionRoot = session.sessionRoot\n boundSessionId = session.sessionId\n mediaRequestGeneration += 1\n }\n\n const sync = (): void => {\n scheduled = 0\n const dedicatedCenter = document.querySelector<HTMLElement>('.dshm-main') ?? undefined\n const nextFrame = resolveNativeMobileFrame(document, dedicatedCenter)\n if (frame !== nextFrame) frame?.removeAttribute('data-dsh-mobile-frame')\n frame = nextFrame\n if (frame !== undefined) frame.dataset.dshMobileFrame = 'true'\n sidebar = frame === undefined\n ? document.querySelector<HTMLElement>('.dshm-drawer') ?? undefined\n : firstByClassSuffix(frame, '_sidebarCol')\n const center = frame === undefined ? dedicatedCenter : firstByClassSuffix(frame, '_centerCol')\n const details = frame === undefined ? undefined : firstByClassSuffix(frame, '_detailsCol')\n const handle = frame === undefined ? undefined : firstByClassSuffix(frame, '_handle')\n markNativeMobileSettings(document)\n if (center === undefined) {\n bindHistoryScroller(undefined)\n syncMediaBinding(null)\n setCameraActionDisabled(true, label('Apri prima una sessione', 'Open a session first', '请先打开会话'))\n cameraButton.remove()\n return\n }\n if (center !== undefined) {\n center.dataset.dshMobileCenter = 'true'\n center.querySelector<HTMLElement>('header')?.setAttribute('data-dsh-mobile-header', 'true')\n viewArea = firstByClassSuffix(center, '_viewArea')\n if (viewArea !== undefined) viewArea.dataset.dshMobileView = 'true'\n const conversation = center.querySelector<HTMLElement>('[data-conversation-scroll]')\n bindHistoryScroller(conversation ?? undefined)\n const historyLoader = conversation === null ? undefined : firstByClassSuffix(conversation, '_older')\n if (historyLoader !== undefined) {\n historyLoader.dataset.dshMobileHistoryLoader = 'true'\n historyLoader.setAttribute('aria-live', 'polite')\n const button = historyLoader.querySelector<HTMLButtonElement>('button')\n if (button !== null) {\n button.tabIndex = -1\n if (button.disabled) button.removeAttribute('aria-hidden')\n else button.setAttribute('aria-hidden', 'true')\n }\n }\n const messageColumn = conversation === null ? undefined : firstByClassSuffix(conversation, '_column')\n const messageScroll = messageColumn?.parentElement\n if (messageColumn !== undefined && messageScroll !== null && messageScroll !== undefined && classToken(messageScroll, '_scroll')) {\n messageColumn.dataset.dshMobileMessageColumn = 'true'\n messageScroll.dataset.dshMobileMessageScroll = 'true'\n }\n for (const table of center.querySelectorAll<HTMLTableElement>('table')) {\n const parent = table.parentElement\n if (parent?.dataset.dshMobileTableScroll === 'true') continue\n const wrapper = document.createElement('div')\n wrapper.dataset.dshMobileTableScroll = 'true'\n table.before(wrapper)\n wrapper.append(table)\n }\n const composerCard = center.querySelector<HTMLElement>('[data-composer-card]')\n const composerRow = composerCard?.querySelector<HTMLElement>(':scope > [data-input-scroll]')?.nextElementSibling\n if (!(composerRow instanceof HTMLElement)) {\n syncMediaBinding(null)\n setCameraActionDisabled(true, label('Apri prima una sessione', 'Open a session first', '请先打开会话'))\n cameraButton.remove()\n }\n if (composerRow instanceof HTMLElement) {\n composerRow.dataset.dshMobileComposerRow = 'true'\n const groups = Array.from(composerRow.children).filter((child): child is HTMLElement => child instanceof HTMLElement)\n const composerTools = groups[0]\n const composerTrailing = groups.at(-1)\n if (composerTools !== undefined) {\n composerTools.dataset.dshMobileComposerTools = 'true'\n syncMediaBinding(composerCard ?? null)\n const composerInput = composerCard?.querySelector<HTMLTextAreaElement>('textarea')\n const composerEditor = composerCard?.querySelector<HTMLElement>('[contenteditable=\"true\"],[contenteditable=\"plaintext-only\"]')\n const composerBusy = composerCard?.getAttribute('aria-busy') === 'true'\n || composerInput?.disabled === true\n || composerInput?.readOnly === true\n || composerEditor?.getAttribute('aria-disabled') === 'true'\n const attachmentBlocked = !canAcceptComposerDrop()\n const mediaDisabled = conversation === null || currentSessionOrigin().sessionId === null || composerBusy || attachmentBlocked\n const mediaTitle = composerBusy\n ? label('Attendi il completamento della risposta', 'Wait for the response to finish', '请等待回复完成')\n : attachmentBlocked\n ? label('Allegati immagine non disponibili', 'Image attachments are unavailable', '图片附件不可用')\n : mediaDisabled\n ? label('Apri prima una sessione', 'Open a session first', '请先打开会话')\n : label('Scatta una foto e allegala', 'Take a photo and attach it', '拍照并附加到当前消息')\n setCameraActionDisabled(mediaDisabled, mediaTitle)\n const commandMenu = composerCard?.querySelector<HTMLElement>('[data-trigger-menu]') ?? null\n if (commandMenu !== null) placeCameraAction(commandMenu)\n else cameraButton.remove()\n }\n if (composerTrailing !== undefined && composerTrailing !== composerTools) {\n composerTrailing.dataset.dshMobileComposerTrailing = 'true'\n const modelTrigger = composerTrailing.querySelector<HTMLButtonElement>('button[aria-label^=\"选择模型\"],button[aria-label^=\"Select model\"],button[aria-label^=\"Seleziona modello\"]')\n if (modelTrigger !== null) {\n modelTrigger.dataset.dshMobileComposerModelTrigger = 'true'\n modelTrigger.parentElement?.setAttribute('data-dsh-mobile-composer-model', 'true')\n modelTrigger.querySelector<HTMLElement>('[class*=\"_triggerLabel\"]')?.setAttribute('data-dsh-mobile-composer-model-label', 'true')\n }\n }\n }\n }\n if (handle !== undefined) handle.dataset.dshMobileHandle = 'true'\n if (details !== undefined) {\n details.dataset.dshMobileDetails = 'true'\n const lastColumn = frame?.style.gridTemplateColumns.trim().split(/\\s+/).at(-1)\n details.dataset.open = String(lastColumn !== undefined && lastColumn !== '0px' && lastColumn !== '0')\n }\n if (sidebar === undefined) return\n sidebar.dataset.dshMobileSidebar = 'true'\n toggle = firstByClassSuffix(sidebar, '_toggle') as HTMLButtonElement | undefined\n let candidate = toggle?.parentElement\n while (candidate !== undefined && candidate !== null && candidate !== sidebar && !classToken(candidate, '_root')) candidate = candidate.parentElement\n sidebarRoot = candidate !== sidebar && candidate !== null ? candidate : undefined\n if (sidebarRoot === undefined) return\n sidebarRoot.dataset.dshMobileSidebarRoot = 'true'\n for (const brand of sidebarRoot.querySelectorAll<HTMLElement>('[class*=\"_fallbackBrandName\"]')) {\n if (brand.textContent?.trim() === 'DSH Local Build') brand.textContent = 'DeepSeek Harness'\n }\n if (toggle !== undefined) toggle.dataset.dshMobileToggle = 'true'\n const collapsed = classToken(sidebarRoot, '_collapsed')\n sidebar.dataset.open = String(!collapsed)\n backdrop.hidden = !drawerScrimVisible(collapsed, overlayQuery.matches)\n }\n const schedule = (): void => { if (scheduled === 0) scheduled = requestAnimationFrame(sync) }\n const observer = new MutationObserver(schedule)\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style', 'disabled', 'readonly', 'aria-busy', 'aria-selected', 'data-dsh-mobile-session'] })\n overlayQuery.addEventListener('change', schedule)\n backdrop.addEventListener('click', () => { if (sidebar?.dataset.open === 'true') toggle?.click() })\n sync()\n const disposeTaskWatcher = installTaskCompletionWatcher({\n // Current gateways announce exact root turn completion. The DOM watcher\n // remains only for explicit pending-input cards, avoiding false completion\n // reminders from unrelated page mutations.\n completionFallback: false,\n label: (kind: TaskNotifyKind, sessionLabel: string) => kind === 'done'\n ? {\n title: label('Attività completata', 'Task finished', '任务已完成'),\n body: sessionLabel === ''\n ? label('Il tuo task DSH è terminato', 'Your DSH task finished', '你的 DSH 任务已完成')\n : label(`Il tuo task DSH è terminato: ${sessionLabel}`, `Your DSH task finished: ${sessionLabel}`, `你的 DSH 任务已完成:${sessionLabel}`),\n }\n : {\n title: label('È richiesto un input', 'Input needed', '需要你确认'),\n body: label('DSH attende una tua scelta', 'DSH waits for your choice', 'DSH 等待你的选择'),\n },\n })\n return () => {\n disposed = true\n disposeTaskWatcher()\n mediaRequestGeneration += 1\n mediaPickerAbortController.abort()\n restoreLanguageMarker()\n for (const cleanup of [...browserPickerCleanups]) cleanup()\n observer.disconnect()\n overlayQuery.removeEventListener('change', schedule)\n document.removeEventListener('click', onBranchClick, true)\n cameraButton.removeEventListener('pointerdown', quietMediaPointer)\n cameraButton.removeEventListener('click', takePhoto)\n if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer)\n if (mediaToastTimer !== 0) window.clearTimeout(mediaToastTimer)\n branchToast.remove()\n mediaToast.remove()\n cameraButton.remove()\n if (scheduled !== 0) cancelAnimationFrame(scheduled)\n if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame)\n if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame)\n if (transitionTimer !== 0) clearTimeout(transitionTimer)\n transitionTarget?.removeAttribute('data-dsh-mobile-view-transition')\n historyScroller?.removeEventListener('scroll', onHistoryScroll)\n document.removeEventListener('pointerdown', onPointerDown, true)\n document.removeEventListener('keydown', onKeyDown, true)\n document.removeEventListener('click', animateNavigation)\n backdrop.remove()\n document.documentElement.classList.remove('dsh-native-mobile-active')\n delete document.documentElement.dataset.dshMobileInput\n }\n}\n","/** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */\n\nfunction unwrapBrackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname\n}\n\nfunction parseIpv4(hostname: string): readonly [number, number, number, number] | undefined {\n const parts = hostname.split('.')\n if (parts.length !== 4) return undefined\n const octets: number[] = []\n for (const part of parts) {\n if (!/^(?:0|[1-9]\\d{0,2})$/u.test(part)) return undefined\n const value = Number(part)\n if (value > 255) return undefined\n octets.push(value)\n }\n return octets as [number, number, number, number]\n}\n\nfunction isLoopbackIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 127\n}\n\nfunction isPrivateIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 10\n || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)\n || (octets[0] === 192 && octets[1] === 168)\n}\n\nfunction isLinkLocalIpv4(octets: readonly [number, number, number, number]): boolean {\n return octets[0] === 169 && octets[1] === 254\n}\n\n/**\n * Hostnames that may appear on the DSH desktop admin surface.\n * Loopback, RFC1918, and IPv4 link-local are accepted. Public IPs,\n * CGNAT, and arbitrary DNS names stay rejected so DNS rebinding\n * cannot reach `/api/mobile-access`.\n */\nexport function isLocalAdminHostname(hostname: string): boolean {\n const host = unwrapBrackets(hostname).trim().toLowerCase()\n if (host === 'localhost' || host === '::1') return true\n const octets = parseIpv4(host)\n if (octets === undefined) return false\n return isLoopbackIpv4(octets) || isPrivateIpv4(octets) || isLinkLocalIpv4(octets)\n}\n\n/**\n * Whether the current browser document should mount the desktop Mobile access\n * control. Dedicated Mobile HTTPS (the phone surface) stays native even when\n * the Host is a private LAN address.\n */\nexport function isDesktopAdminSurface(\n hostname: string,\n search = '',\n frontend?: string,\n): boolean {\n const query = search.startsWith('?') ? search.slice(1) : search\n return isLocalAdminHostname(hostname)\n && frontend !== 'dedicated'\n && !new URLSearchParams(query).has('dsh-mobile-preview')\n}\n","import { createElement, useEffect, useState } from 'react'\nimport {\n DIAGNOSTIC_REASON_MESSAGES,\n LOCALIZED_DIAGNOSTIC_COPY,\n MOBILE_CONTROL_MESSAGES,\n type MobileControlLocale,\n} from './client-messages.js'\nimport { createRestrictedFrpServerTemplate } from './frp-template.js'\nimport { createFrpAttachFrpcToml, createFrpAttachTemplate } from './frp-attach.js'\nimport { parseFrpSettings, type FrpSettings } from './frp-config.js'\nimport { installNativeMobileSurface, NATIVE_MOBILE_STYLES, resolveNativeMobileLanguage } from './native-mobile.js'\nimport { isDesktopAdminSurface } from './local-admin-host.js'\nimport { fireDeviceRevoked, fireTaskNotifyEvent, isDeviceRevokedPayload, parseTaskNotifyPayload, taskCompletionTag } from './task-notify.js'\n\nexport { DIAGNOSTIC_REASON_MESSAGES, LOCALIZED_DIAGNOSTIC_COPY, MOBILE_CONTROL_MESSAGES } from './client-messages.js'\nexport type { MobileControlLocale } from './client-messages.js'\n\n/**\n * Remote provider names the control UI knows about.\n *\n * This mirrors the server-side union in `src/remote.ts`, which cannot be imported\n * here because it pulls in Node-only modules. An unrecognized value from the\n * host falls back to the built-in provider, so a missing name would silently\n * render another provider's wording instead of failing loudly.\n */\nexport type MobileRemoteProvider = 'tailscale' | 'cpolar' | 'cloudflared' | 'frp' | 'origin'\n\n/** Map a host-reported provider onto the UI union, defaulting to the built-in one. */\nexport function parseMobileRemoteProvider(value: unknown): MobileRemoteProvider {\n return value === 'cpolar' || value === 'cloudflared' || value === 'frp' || value === 'origin'\n ? value\n : 'tailscale'\n}\n\ninterface ClientContext {\n effect(effect: () => void | (() => void), label?: string): void\n get(name: 'connection'): MobileConnectionHandle\n slots: {\n inject(key: string, callback: () => (() => void)): () => void\n register<Props>(options: { name: string; id: string; order?: number; label?: string }, component: (props: Props) => unknown): () => void\n }\n}\n\ninterface MobileConnectionHandle {\n isLoopback: boolean\n}\n\ninterface MobileExtensionContext {\n readonly document: Document\n readonly request: (path: string, init?: RequestInit) => Promise<Response>\n readonly root: HTMLElement\n readonly window: Window\n}\n\ntype MobileExtensionMount = (context: MobileExtensionContext) => void | (() => void)\ntype MobileSurfacePlacement = 'page' | 'sidebar-action' | 'header-action' | 'composer-dock' | 'settings-section' | 'overlay'\ninterface MobileSurface {\n readonly id: string\n readonly placement: MobileSurfacePlacement\n readonly label: string\n mount(container: HTMLElement): void | (() => void)\n}\ninterface MobileClientApi {\n readonly host: {\n invoke(action: string, input: unknown): Promise<unknown>\n fetch(path: string, init?: RequestInit): Promise<Response>\n assetUrl(path: string): string\n }\n readonly ui: {\n registerSurface(surface: MobileSurface): () => void\n open(surfaceId: string): void\n close(surfaceId: string): void\n toast(message: string): void\n }\n readonly native: {\n capabilities(): Promise<readonly string[]>\n invoke(action: string, input?: unknown): Promise<unknown>\n }\n readonly signal: AbortSignal\n readonly document: Document\n readonly window: Window\n}\ninterface MobileClientDefinition {\n readonly apiVersion: 1\n readonly id: string\n activate(api: MobileClientApi): void | (() => void) | Promise<void | (() => void)>\n}\n\ndeclare global {\n interface Window {\n dshMobile?: {\n register(mount: MobileExtensionMount): void\n define(definition: MobileClientDefinition): void\n }\n __DSH_MOBILE_FRONTEND__?: 'dedicated'\n __DSH_MOBILE_NATIVE__?: {\n capabilities(): Promise<readonly string[]> | readonly string[]\n invoke(action: string, input?: unknown): Promise<unknown>\n }\n }\n}\n\nconst queuedDefinitions: MobileClientDefinition[] = []\nlet queuedLegacyMount: MobileExtensionMount | undefined\nif (typeof window !== 'undefined' && window.dshMobile === undefined) {\n window.dshMobile = {\n register: mount => { queuedLegacyMount = mount },\n define: definition => { queuedDefinitions.push(definition) },\n }\n}\n\nexport function selectMobileControlLocale(documentLanguage = '', navigatorLanguages: readonly string[] = []): MobileControlLocale {\n for (const value of [documentLanguage, ...navigatorLanguages]) {\n const language = value.trim().toLowerCase().split(/[-_]/u)[0]\n if (language === 'it' || language === 'en' || language === 'zh') return language\n }\n return 'en'\n}\n\nexport function selectedMobileControlLocale(): MobileControlLocale {\n return selectMobileControlLocale(document.documentElement.lang, navigator.languages?.length ? navigator.languages : [navigator.language])\n}\n\n/** Remount one plugin-owned surface when DSH changes the document language. */\nexport function installDshLanguageBoundSurface(install: () => () => void): () => void {\n let locale = selectedMobileControlLocale()\n let dispose = install()\n const observer = new MutationObserver(() => {\n const next = selectedMobileControlLocale()\n if (next === locale) return\n dispose()\n locale = next\n dispose = install()\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] })\n return () => {\n observer.disconnect()\n dispose()\n }\n}\n\nfunction controlTranslator(locale = selectedMobileControlLocale()): (key: string, values?: Readonly<Record<string, string | number>>) => string {\n const messages = MOBILE_CONTROL_MESSAGES as Record<MobileControlLocale, Record<string, string>>\n return (key, values = {}) => {\n const template = messages[locale][key] ?? messages.en[key] ?? key\n return template.replace(/\\{(\\w+)\\}/gu, (_match: string, name: string) => String(values[name] ?? `{${name}}`))\n }\n}\n\nexport function normalizeDiagnosticOverall(value: unknown): 'ok' | 'attention' | 'error' {\n return value === 'ok' ? 'ok' : value === 'attention' ? 'attention' : 'error'\n}\n\nexport function normalizeDiagnosticStatus(value: unknown): 'ok' | 'warning' | 'error' | 'info' {\n return value === 'ok' || value === 'warning' || value === 'error' || value === 'info' ? value : 'error'\n}\n\nexport function diagnosticOverallForChecks(value: unknown, statuses: readonly unknown[]): 'ok' | 'attention' | 'error' {\n const normalized = statuses.map(normalizeDiagnosticStatus)\n const payloadOverall = normalizeDiagnosticOverall(value)\n if (payloadOverall === 'error' || normalized.includes('error')) return 'error'\n if (payloadOverall === 'attention' || normalized.includes('warning')) return 'attention'\n return 'ok'\n}\n\nexport function validateDiagnosticChecks(value: unknown): { readonly entries: readonly Record<string, unknown>[]; readonly malformed: boolean } {\n if (!Array.isArray(value)) return { entries: [], malformed: true }\n const entries: Record<string, unknown>[] = []\n let malformed = false\n for (const candidate of value) {\n if (typeof candidate !== 'object' || candidate === null || Array.isArray(candidate)) { malformed = true; continue }\n entries.push(candidate as Record<string, unknown>)\n }\n return { entries, malformed }\n}\n\nexport function diagnosticEntriesForRender(data: Readonly<Record<string, unknown>>): readonly Record<string, unknown>[] {\n const overallKnown = data.overall === 'ok' || data.overall === 'attention' || data.overall === 'error'\n const validated = validateDiagnosticChecks(data.checks)\n const statusesKnown = validated.entries.every(entry => entry.status === 'ok' || entry.status === 'warning' || entry.status === 'error' || entry.status === 'info')\n if (!overallKnown || validated.malformed || validated.entries.length === 0 || !statusesKnown) {\n throw new TypeError('diagnostics envelope is unavailable')\n }\n return validated.entries\n}\n\nexport function renderDiagnosticPayloadSafely(\n data: Record<string, unknown>,\n render: (data: Record<string, unknown>) => void,\n onFailure: (error: unknown) => void,\n): void {\n try { render(data) } catch (error) { onFailure(error) }\n}\n\nexport function diagnosticServerCopy(entry: Readonly<Record<string, unknown>>): { readonly label: string; readonly detail: string; readonly action: string } {\n return {\n label: typeof entry.label === 'string' ? entry.label : '',\n detail: typeof entry.detail === 'string' ? entry.detail : '',\n action: typeof entry.action === 'string' ? entry.action : '',\n }\n}\n\n/**\n * Match DSH's client-side privilege hint to the authenticated mobile gateway.\n * The gateway authenticates the paired device and forwards allowed requests to\n * DSH's loopback listener, so settings RPCs receive the same Host-side checks\n * as the desktop page even though the phone's visible URL is a LAN address.\n */\nexport function trustAuthenticatedGatewayConnection(connection: MobileConnectionHandle): () => void {\n const previous = connection.isLoopback\n connection.isLoopback = true\n return () => { connection.isLoopback = previous }\n}\n\nfunction element<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag)\n if (className !== undefined) node.className = className\n return node\n}\n\ninterface MobileSwitchComputerCopy {\n readonly title: string\n readonly description: string\n readonly action: string\n readonly busy: string\n readonly retry: string\n readonly error: string\n}\n\nfunction mobileSwitchComputerCopy(locale: MobileControlLocale): MobileSwitchComputerCopy {\n if (locale === 'zh') return {\n title: '切换电脑',\n description: '返回已配对设备列表,选择要打开的电脑。',\n action: '切换电脑',\n busy: '正在切换…',\n retry: '重试',\n error: '切换失败,请重试。',\n }\n if (locale === 'it') return {\n title: 'Cambia computer',\n description: 'Torna all’elenco dei computer associati e scegli quale aprire.',\n action: 'Cambia computer',\n busy: 'Cambio in corso…',\n retry: 'Riprova',\n error: 'Cambio non riuscito. Riprova.',\n }\n return {\n title: 'Switch computer',\n description: 'Return to the paired-device list and choose a computer to open.',\n action: 'Switch computer',\n busy: 'Switching…',\n retry: 'Retry',\n error: 'Could not switch computers. Try again.',\n }\n}\n\n/** Render the native-app-only action in DSH's General settings row style. */\nfunction MobileSwitchComputerRow(): unknown {\n const [nativeReady, setNativeReady] = useState(false)\n const [state, setState] = useState<'idle' | 'busy' | 'error'>('idle')\n useEffect(() => {\n let disposed = false\n const checkNativeAction = (): void => {\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined) {\n setNativeReady(false)\n return\n }\n void Promise.resolve().then(() => bridge.capabilities()).then(capabilities => {\n if (!disposed) setNativeReady(capabilities.includes('mobile.switch-computer'))\n }, () => {\n if (!disposed) setNativeReady(false)\n })\n }\n checkNativeAction()\n window.addEventListener('dsh-mobile-native-ready', checkNativeAction)\n return () => {\n disposed = true\n window.removeEventListener('dsh-mobile-native-ready', checkNativeAction)\n }\n }, [])\n if (!nativeReady) return null\n const locale = selectedMobileControlLocale()\n const copy = mobileSwitchComputerCopy(locale)\n const invoke = (): void => {\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined || state === 'busy') return\n setState('busy')\n // The native action normally replaces this WebView immediately; only a\n // rejected request needs to remain visible in the settings row.\n void bridge.invoke('mobile.switch-computer', {}).catch(() => { setState('error') })\n }\n return createElement('div', { className: 'dsh-mobile-settings_row', lang: locale },\n createElement('div', { className: 'dsh-mobile-settings_rowText' },\n createElement('div', { className: 'dsh-mobile-settings_title' }, copy.title),\n createElement('div', { className: 'dsh-mobile-settings_desc', 'aria-live': 'polite' }, state === 'error' ? copy.error : copy.description),\n ),\n createElement('button', {\n type: 'button',\n className: 'dsh-mobile-settings_selector',\n disabled: state === 'busy',\n 'aria-busy': state === 'busy' ? 'true' : undefined,\n onClick: invoke,\n }, state === 'busy' ? copy.busy : state === 'error' ? copy.retry : copy.action),\n )\n}\n\ninterface MobileTaskNotificationCopy {\n readonly title: string\n readonly description: string\n readonly action: string\n readonly busy: string\n readonly success: string\n readonly reopen: string\n readonly error: string\n readonly retry: string\n}\n\nfunction mobileTaskNotificationCopy(locale: MobileControlLocale): MobileTaskNotificationCopy {\n if (locale === 'zh') return {\n title: '任务通知',\n description: '在 Android 系统中开启任务完成和待确认提醒。',\n action: '通知设置',\n busy: '正在打开…',\n success: '请在系统提示或设置中确认任务通知权限。',\n reopen: '重新打开',\n error: '无法打开通知设置,请重试。',\n retry: '重试',\n }\n if (locale === 'it') return {\n title: 'Notifiche delle attività',\n description: 'Attiva gli avvisi Android per le attività completate o in attesa di conferma.',\n action: 'Impostazioni notifiche',\n busy: 'Apertura…',\n success: 'Conferma il permesso nella richiesta o nelle impostazioni di sistema.',\n reopen: 'Riapri',\n error: 'Impossibile aprire le impostazioni delle notifiche. Riprova.',\n retry: 'Riprova',\n }\n return {\n title: 'Task notifications',\n description: 'Enable Android reminders for completed tasks and pending confirmation.',\n action: 'Notification settings',\n busy: 'Opening…',\n success: 'Confirm notification access in the system prompt or settings.',\n reopen: 'Open again',\n error: 'Could not open notification settings. Try again.',\n retry: 'Retry',\n }\n}\n\n/** Render the Android-only notification permission entry in DSH General settings. */\nfunction MobileTaskNotificationRow(): unknown {\n const [nativeReady, setNativeReady] = useState(false)\n const [state, setState] = useState<'idle' | 'busy' | 'success' | 'error'>('idle')\n useEffect(() => {\n let disposed = false\n const checkNativeAction = (): void => {\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined) {\n setNativeReady(false)\n return\n }\n void Promise.resolve().then(() => bridge.capabilities()).then(capabilities => {\n if (!disposed) setNativeReady(capabilities.includes('notification.settings'))\n }, () => {\n if (!disposed) setNativeReady(false)\n })\n }\n checkNativeAction()\n window.addEventListener('dsh-mobile-native-ready', checkNativeAction)\n return () => {\n disposed = true\n window.removeEventListener('dsh-mobile-native-ready', checkNativeAction)\n }\n }, [])\n if (!nativeReady) return null\n const locale = selectedMobileControlLocale()\n const copy = mobileTaskNotificationCopy(locale)\n const invoke = (): void => {\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge === undefined || state === 'busy') return\n setState('busy')\n void bridge.invoke('notification.settings', {}).then(\n () => { setState('success') },\n () => { setState('error') },\n )\n }\n return createElement('div', { className: 'dsh-mobile-settings_row', lang: locale },\n createElement('div', { className: 'dsh-mobile-settings_rowText' },\n createElement('div', { className: 'dsh-mobile-settings_title' }, copy.title),\n createElement('div', { className: 'dsh-mobile-settings_desc', 'aria-live': 'polite' },\n state === 'success' ? copy.success : state === 'error' ? copy.error : copy.description),\n ),\n createElement('button', {\n type: 'button',\n className: 'dsh-mobile-settings_selector',\n disabled: state === 'busy',\n 'aria-busy': state === 'busy' ? 'true' : undefined,\n onClick: invoke,\n }, state === 'busy' ? copy.busy : state === 'success' ? copy.reopen : state === 'error' ? copy.retry : copy.action),\n )\n}\n\nconst CONTROL_REQUEST_TIMEOUT_MS = 15_000\nconst LONG_CONTROL_REQUEST_TIMEOUT_MS = 210_000\nconst GITHUB_RELEASES_URL = 'https://github.com/saya-ch/dsh-mobile/releases'\nconst CONTROL_PANEL_ID = 'dsh-mobile-control-panel'\n\nexport interface ClientReleaseInfo {\n readonly updateAvailable: boolean\n readonly latestVersion?: string\n readonly androidVersion?: string\n readonly androidDownloadUrl: string\n /** Trimmed release notes body for the preview card (best effort). */\n readonly releaseNotes?: string\n}\n\nfunction releaseVersion(value: unknown): string | undefined {\n return typeof value === 'string'\n && /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/u.test(value)\n ? value\n : undefined\n}\n\n/** Reduce the loopback release response to trusted text and download metadata. */\nexport function clientReleaseInfo(data: Readonly<Record<string, unknown>>): ClientReleaseInfo {\n const latestVersion = releaseVersion(data.latestVersion)\n const androidVersion = releaseVersion(data.androidVersion)\n const expectedAndroidDownloadUrl = androidVersion === undefined\n ? undefined\n : `${GITHUB_RELEASES_URL}/download/v${androidVersion}/dsh-mobile-android-v${androidVersion}.apk`\n const androidDownloadUrl = expectedAndroidDownloadUrl !== undefined && data.androidDownloadUrl === expectedAndroidDownloadUrl\n ? expectedAndroidDownloadUrl\n : GITHUB_RELEASES_URL\n return {\n updateAvailable: data.updateAvailable === true && latestVersion !== undefined,\n ...(latestVersion === undefined ? {} : { latestVersion }),\n ...(androidVersion === undefined ? {} : { androidVersion }),\n androidDownloadUrl,\n ...(typeof data.releaseNotes === 'string' && data.releaseNotes !== '' ? { releaseNotes: data.releaseNotes.slice(0, 4000) } : {}),\n }\n}\n\nasync function requestJson(\n path: string,\n init?: RequestInit,\n timeoutMs = CONTROL_REQUEST_TIMEOUT_MS,\n): Promise<Record<string, unknown>> {\n const controller = new AbortController()\n const upstreamSignal = init?.signal\n const abortFromUpstream = (): void => { controller.abort(upstreamSignal?.reason) }\n if (upstreamSignal?.aborted === true) abortFromUpstream()\n else upstreamSignal?.addEventListener('abort', abortFromUpstream, { once: true })\n const timer = window.setTimeout(() => { controller.abort() }, timeoutMs)\n try {\n const response = await fetch(path, {\n ...init,\n signal: controller.signal,\n headers: { 'content-type': 'application/json', ...init?.headers },\n })\n const body = await response.json() as Record<string, unknown>\n if (!response.ok) throw new Error(typeof body.error === 'string' ? body.error : `HTTP ${String(response.status)}`)\n return body\n } catch (error) {\n if (controller.signal.aborted && upstreamSignal?.aborted !== true) {\n throw new Error(controlTranslator()('requestTimeout'))\n }\n throw error\n } finally {\n clearTimeout(timer)\n upstreamSignal?.removeEventListener('abort', abortFromUpstream)\n }\n}\n\nfunction officialFunnelSetupUrl(value: unknown): string {\n if (typeof value !== 'string' || value.length > 2048) return ''\n let url: URL\n try { url = new URL(value) } catch { return '' }\n const normalized = url.toString().replace(/\\/$/u, '')\n if (normalized === 'https://tailscale.com/s/no-funnel' || normalized === 'https://tailscale.com/s/https') return normalized\n if (url.protocol !== 'https:' || url.hostname !== 'login.tailscale.com' || url.port !== ''\n || url.username !== '' || url.password !== '') return ''\n return url.toString()\n}\n\n/** Build the copy-only restricted VPS template without sending the token to the host API. */\nexport function createFrpServerTemplateForClipboard(serverPort: number, token: string, publicOrigin: string): string {\n return createRestrictedFrpServerTemplate(serverPort, token, publicOrigin)\n}\n\n/** Raw attach form values exactly as typed in the panel. */\nexport interface FrpAttachClipboardForm {\n readonly serverAddress: string\n readonly serverPort: number\n readonly token: string\n readonly publicOrigin: string\n readonly entryTls: 'public-ip-cert' | 'self-signed'\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n}\n\n/**\n * Options of the two attach artefacts the panel builds locally.\n *\n * `revealToken` defaults to false, so the ordinary copy button always produces\n * masked text. Only the dedicated reveal action sets it, and its result goes\n * straight to the clipboard — never into a stored state, a log, or localStorage.\n */\nexport interface FrpAttachClipboardOptions {\n readonly revealToken?: boolean\n}\n\n/**\n * Validate the panel form with the same parser the host uses, so a runbook can\n * never describe a topology the provider would later refuse to start.\n */\nfunction attachClipboardSettings(form: FrpAttachClipboardForm): FrpSettings {\n return parseFrpSettings({\n serverAddress: form.serverAddress,\n serverPort: form.serverPort,\n token: form.token,\n publicOrigin: form.publicOrigin,\n mode: 'attach',\n entryTls: form.entryTls,\n ...(form.entryTls === 'self-signed' || form.vhostHttpPort === undefined\n ? {}\n : { vhostHttpPort: form.vhostHttpPort }),\n ...(form.publicPort === undefined ? {} : { publicPort: form.publicPort }),\n })\n}\n\n/**\n * Build the copy-only attach runbook locally, so the shared token never leaves\n * the browser: the panel only ever copies text out. The text is masked by\n * default; `options.revealToken` is the explicit opt-in.\n */\nexport function createFrpAttachTemplateForClipboard(\n form: FrpAttachClipboardForm,\n options: FrpAttachClipboardOptions = {},\n): string {\n return createFrpAttachTemplate(attachClipboardSettings(form), options)\n}\n\n/**\n * The frpc.toml exactly as the plugin writes it (0600), for the panel's explicit\n * reveal action only. Every other path keeps the token masked.\n */\nexport function createFrpAttachFrpcTomlForClipboard(\n form: FrpAttachClipboardForm,\n options: FrpAttachClipboardOptions = {},\n): string {\n return createFrpAttachFrpcToml(attachClipboardSettings(form), options)\n}\n\n/**\n * Attach-mode validation for the panel. The parser is the single source of\n * truth, so the panel reports exactly the codes the host would return instead of\n * duplicating the rules (and drifting from them).\n */\nexport function frpAttachFormErrorCode(form: FrpAttachClipboardForm): string | undefined {\n try {\n createFrpAttachTemplateForClipboard(form)\n return undefined\n } catch (error) {\n return error instanceof Error ? error.message : 'frp_settings_invalid'\n }\n}\n\nconst ORIGIN_ERROR_MESSAGE_KEYS: Readonly<Record<string, string>> = {\n origin_public_origin_invalid: 'originPublicOriginInvalid',\n origin_listen_host_invalid: 'originListenHostInvalid',\n origin_listen_port_invalid: 'originListenPortInvalid',\n origin_listen_port_reserved: 'originListenPortReserved',\n origin_allowed_cidrs_invalid: 'originAllowedCidrsInvalid',\n origin_settings_invalid: 'originSettingsInvalid',\n origin_config_missing: 'originConfigMissing',\n origin_config_invalid: 'originConfigInvalid',\n origin_config_target_invalid: 'originConfigTargetInvalid',\n origin_controller_unavailable: 'originControllerUnavailable',\n origin_listen_port_in_use: 'originPortInUse',\n origin_listen_address_unavailable: 'originAddressUnavailable',\n origin_gateway_start_failed: 'originStartFailed',\n}\n\n/** Provider failure codes shared by status, failed requests, and diagnostics. */\nconst REMOTE_ERROR_MESSAGE_KEYS: Readonly<Record<string, string>> = {\n funnel_permission_required: 'funnelPermission',\n funnel_https_required: 'funnelHttps',\n funnel_start_failed: 'funnelStart',\n funnel_start_timeout: 'funnelTimeout',\n tailscale_dns_missing: 'tailscaleDnsMissing',\n gateway_start_failed: 'gatewayStartFailed',\n control_channel_failed: 'controlChannelFailed',\n cpolar_component_missing: 'cpolarMissing',\n cpolar_component_invalid: 'cpolarInvalid',\n cpolar_config_missing: 'cpolarConfigMissing',\n cpolar_config_invalid: 'cpolarConfigInvalid',\n cpolar_port_unavailable: 'cpolarPortUnavailable',\n cpolar_launch_failed: 'cpolarLaunchFailed',\n cpolar_start_timeout: 'cpolarTimeout',\n cpolar_stopped: 'cpolarStopped',\n cpolar_exited: 'cpolarExited',\n cpolar_invalid_output: 'cpolarOutputInvalid',\n cpolar_invalid_origin: 'cpolarOriginInvalid',\n cloudflared_component_missing: 'cloudflaredMissing',\n cloudflared_component_invalid: 'cloudflaredInvalid',\n cloudflared_port_unavailable: 'cloudflaredPortUnavailable',\n cloudflared_launch_failed: 'cloudflaredLaunchFailed',\n cloudflared_start_timeout: 'cloudflaredTimeout',\n cloudflared_stopped: 'cloudflaredStopped',\n cloudflared_exited: 'cloudflaredExited',\n cloudflared_invalid_output: 'cloudflaredOutputInvalid',\n cloudflared_invalid_origin: 'cloudflaredOriginInvalid',\n cloudflared_tunnel_port_unavailable: 'cloudflaredTunnelPortUnavailable',\n cloudflared_tunnel_hostname_invalid: 'cloudflaredTunnelHostnameInvalid',\n cloudflared_tunnel_port_invalid: 'cloudflaredTunnelPortInvalid',\n cloudflared_tunnel_port_reserved: 'cloudflaredTunnelPortReserved',\n cloudflared_tunnel_token_invalid: 'cloudflaredTunnelTokenInvalid',\n cloudflared_tunnel_settings_invalid: 'cloudflaredTunnelSettingsInvalid',\n cloudflared_tunnel_config_missing: 'cloudflaredTunnelConfigMissing',\n cloudflared_tunnel_config_invalid: 'cloudflaredTunnelInvalid',\n cloudflared_tunnel_target_invalid: 'cloudflaredTunnelTargetInvalid',\n cloudflared_component_unsupported: 'cloudflaredComponentUnsupported',\n cloudflared_download_hash_mismatch: 'cloudflaredDownloadHashMismatch',\n cloudflared_download_size_mismatch: 'cloudflaredDownloadSizeMismatch',\n cloudflared_executable_hash_mismatch: 'cloudflaredExecutableHashMismatch',\n cloudflared_download_redirect_missing: 'cloudflaredDownloadRedirectMissing',\n cloudflared_download_redirect_invalid: 'cloudflaredDownloadRedirectInvalid',\n cloudflared_download_redirect_rejected: 'cloudflaredDownloadRedirectRejected',\n cloudflared_port_reservation_failed: 'cloudflaredPortReservationFailed',\n frp_component_missing: 'frpMissing',\n frp_component_invalid: 'frpInvalid',\n frp_config_missing: 'frpConfigMissing',\n frp_config_verify_failed: 'frpConfigVerifyFailed',\n frp_vhost_publicly_reachable: 'frpVhostPublic',\n frp_vhost_probe_failed: 'frpVhostProbeFailed',\n frp_launch_failed: 'frpLaunchFailed',\n frp_start_timeout: 'frpTimeout',\n frp_discovery_mismatch: 'frpDiscoveryMismatch',\n frp_discovery_invalid: 'frpDiscoveryInvalid',\n frp_stopped: 'frpStopped',\n frp_exited: 'frpExited',\n frp_attach_mode_requires_vhost_port: 'frpAttachModeRequiresVhostPort',\n frp_attach_cert_unknown: 'frpAttachCertUnknownError',\n frp_entry_tls_invalid: 'frpEntryTlsInvalid',\n frp_self_signed_requires_public_ipv4: 'frpSelfSignedRequiresPublicIpv4',\n frp_ingress_ca_expired: 'frpIngressCaExpired',\n frp_ingress_ca_invalid: 'frpIngressCaInvalid',\n frp_ingress_ca_changed: 'frpIngressCaChanged',\n frp_ingress_renewal_failed: 'frpIngressRenewalFailed',\n ...ORIGIN_ERROR_MESSAGE_KEYS,\n}\n\nconst DIAGNOSTIC_CONTROLLER_ACTION_OVERRIDES: Readonly<Record<string, string>> = {\n component_missing: 'remoteUnavailableTailscale',\n sidecar_launch_failed: 'remoteUnavailableTailscale',\n sidecar_stopped: 'controlChannelFailed',\n sidecar_exited: 'controlChannelFailed',\n}\n\n/** Only codes with a specific diagnostic action use the status message key. */\nconst DIAGNOSTIC_CONTROLLER_ACTION_CODES: ReadonlySet<string> = new Set([\n ...Object.keys(ORIGIN_ERROR_MESSAGE_KEYS),\n 'funnel_permission_required', 'funnel_https_required', 'funnel_start_failed', 'funnel_start_timeout',\n 'tailscale_dns_missing', 'control_channel_failed', 'gateway_start_failed',\n 'cpolar_component_missing', 'cpolar_component_invalid', 'cpolar_config_missing', 'cpolar_config_invalid',\n 'cpolar_start_timeout', 'cpolar_stopped', 'cpolar_exited',\n 'cloudflared_component_missing', 'cloudflared_component_invalid', 'cloudflared_component_unsupported',\n 'cloudflared_port_unavailable', 'cloudflared_port_reservation_failed', 'cloudflared_launch_failed',\n 'cloudflared_start_timeout', 'cloudflared_stopped', 'cloudflared_exited', 'cloudflared_invalid_origin',\n 'cloudflared_tunnel_port_unavailable', 'cloudflared_tunnel_config_invalid', 'cloudflared_tunnel_target_invalid',\n 'cloudflared_download_hash_mismatch', 'cloudflared_download_size_mismatch', 'cloudflared_executable_hash_mismatch',\n 'frp_component_missing', 'frp_component_invalid', 'frp_config_missing', 'frp_config_verify_failed',\n 'frp_vhost_publicly_reachable', 'frp_vhost_probe_failed', 'frp_launch_failed', 'frp_start_timeout',\n 'frp_discovery_mismatch', 'frp_discovery_invalid', 'frp_stopped', 'frp_exited',\n 'frp_attach_mode_requires_vhost_port', 'frp_attach_cert_unknown', 'frp_entry_tls_invalid',\n 'frp_self_signed_requires_public_ipv4', 'frp_ingress_ca_expired', 'frp_ingress_ca_invalid',\n 'frp_ingress_ca_changed', 'frp_ingress_renewal_failed',\n])\n\n/** Resolve the next-step text for a remote-controller diagnostic. */\nexport function diagnosticControllerAction(code: unknown, locale: MobileControlLocale, fallback: string): string {\n if (typeof code !== 'string') return fallback\n const key = DIAGNOSTIC_CONTROLLER_ACTION_OVERRIDES[code]\n ?? (DIAGNOSTIC_CONTROLLER_ACTION_CODES.has(code) ? REMOTE_ERROR_MESSAGE_KEYS[code] : undefined)\n return key === undefined ? fallback : controlTranslator(locale)(key)\n}\n\nfunction installControl(): { remove: () => void; toggle: () => void; isOpen: () => boolean } {\n\n /** The table above, translated, for the status repaint. */\n const translatedRemoteErrors = (): Record<string, string> =>\n Object.fromEntries(Object.entries(REMOTE_ERROR_MESSAGE_KEYS).map(([code, key]) => [code, t(key)]))\n\n /**\n * Text for a failed control request.\n *\n * A rejection carries its code in `error.message`, so a known code renders its\n * translated sentence; only an unrecognized one falls back to the generic message,\n * and it shows the bare message rather than a stringified Error.\n */\n const remoteFailureTextFor = (error: unknown, fallbackKey: string): string => {\n const message = error instanceof Error ? error.message : String(error)\n const key = REMOTE_ERROR_MESSAGE_KEYS[message]\n return key === undefined ? t(fallbackKey, { error: message }) : t(key)\n }\n const locale = selectedMobileControlLocale()\n const localeTag = locale === 'it' ? 'it-IT' : locale === 'zh' ? 'zh-CN' : 'en-US'\n const t = controlTranslator(locale)\n const lifecycle = new AbortController()\n const controlRequestJson = (path: string, init?: RequestInit, timeoutMs?: number): Promise<Record<string, unknown>> => requestJson(path, { ...init, signal: lifecycle.signal }, timeoutMs)\n const root = element('div', 'dsh-mobile-control'); root.lang = locale\n const panel = element('section', 'dsh-mobile-control__panel'); panel.id = CONTROL_PANEL_ID; panel.hidden = true; panel.lang = locale\n panel.setAttribute('aria-label', t('mobileAccess'))\n const header = element('header', 'dsh-mobile-control__header')\n const title = element('h2'); title.textContent = t('mobileAccess')\n const headerActions = element('div', 'dsh-mobile-control__header-actions')\n const updatePlugin = element('button', 'dsh-mobile-control__update-plugin'); updatePlugin.type = 'button'; updatePlugin.textContent = t('updatePlugin'); updatePlugin.hidden = true\n // Preview card shown before applying an update: what’s new + notices, then confirm.\n const updateCard = element('div', 'dsh-mobile-control__update-card'); updateCard.hidden = true\n const updateCardTitle = element('div', 'dsh-mobile-control__update-card-title')\n const updateCardNotes = element('pre', 'dsh-mobile-control__update-notes'); updateCardNotes.setAttribute('tabindex', '0')\n const updateCardNotice = element('p', 'dsh-mobile-control__update-notice'); updateCardNotice.textContent = t('updateNotice')\n const updateActions = element('div', 'dsh-mobile-control__update-actions')\n const updateNow = element('button', 'dsh-mobile-control__primary'); updateNow.type = 'button'; updateNow.textContent = t('updateNow')\n const updateLater = element('button', 'dsh-mobile-control__secondary'); updateLater.type = 'button'; updateLater.textContent = t('updateLater')\n updateActions.append(updateLater, updateNow); updateCard.append(updateCardTitle, updateCardNotes, updateCardNotice, updateActions)\n const diagnosticsEntry = element('button', 'dsh-mobile-control__diagnostic-entry'); diagnosticsEntry.type = 'button'; diagnosticsEntry.textContent = t('diagnostics'); diagnosticsEntry.setAttribute('aria-label', t('openDiagnostics')); diagnosticsEntry.setAttribute('aria-pressed', 'false')\n const close = element('button', 'dsh-mobile-control__close'); close.type = 'button'; close.textContent = '×'; close.setAttribute('aria-label', t('collapseMobileAccess'))\n headerActions.append(updatePlugin, diagnosticsEntry, close)\n const releaseNotice = element('p', 'dsh-mobile-control__release-notice'); releaseNotice.hidden = true; releaseNotice.setAttribute('role', 'status'); releaseNotice.setAttribute('aria-live', 'polite')\n const appDownload = element('a', 'dsh-mobile-control__app-download'); appDownload.href = GITHUB_RELEASES_URL; appDownload.target = '_blank'; appDownload.rel = 'noopener noreferrer'; appDownload.textContent = t('downloadAndroid'); appDownload.setAttribute('aria-label', t('downloadAndroidAria'))\n const switcher = element('div', 'dsh-mobile-control__switcher')\n const lanTab = element('button', 'dsh-mobile-control__tab is-active'); lanTab.type = 'button'; lanTab.textContent = t('lan')\n const remoteTab = element('button', 'dsh-mobile-control__tab'); remoteTab.type = 'button'; remoteTab.textContent = t('remote')\n lanTab.setAttribute('aria-pressed', 'true'); remoteTab.setAttribute('aria-pressed', 'false'); switcher.append(lanTab, remoteTab)\n const lanView = element('div', 'dsh-mobile-control__view')\n const lanSetup = element('section', 'dsh-mobile-control__lan-setup'); lanSetup.hidden = true\n const lanSetupTitle = element('h3', 'dsh-mobile-control__section-title'); lanSetupTitle.textContent = t('lanSetupTitle')\n const lanSetupIntro = element('p', 'dsh-mobile-control__intro'); lanSetupIntro.textContent = t('lanSetupIntro')\n const lanSetupForm = element('div', 'dsh-mobile-control__lan-setup-form')\n const lanSetupLabel = element('label', 'dsh-mobile-control__field'); lanSetupLabel.textContent = t('lanSetupNetwork')\n const lanSetupSelect = element('select'); lanSetupSelect.setAttribute('aria-label', t('lanSetupNetwork'))\n const lanSetupNote = element('p', 'dsh-mobile-control__component-note'); lanSetupNote.textContent = t('lanSetupFirewallNote')\n const lanSetupActions = element('div', 'dsh-mobile-control__lan-setup-actions')\n const lanSetupRefresh = element('button', 'dsh-mobile-control__secondary'); lanSetupRefresh.type = 'button'; lanSetupRefresh.textContent = t('lanSetupRefresh')\n const lanSetupConfigure = element('button', 'dsh-mobile-control__primary'); lanSetupConfigure.type = 'button'; lanSetupConfigure.textContent = t('lanSetupConfigure')\n const lanSetupStatus = element('p', 'dsh-mobile-control__status'); lanSetupStatus.setAttribute('role', 'status'); lanSetupStatus.setAttribute('aria-live', 'polite')\n lanSetupLabel.append(lanSetupSelect); lanSetupActions.append(lanSetupRefresh, lanSetupConfigure); lanSetupForm.append(lanSetupLabel, lanSetupNote, lanSetupActions); lanSetup.append(lanSetupTitle, lanSetupIntro, lanSetupForm, lanSetupStatus)\n const access = element('div', 'dsh-mobile-control__access'); access.hidden = true\n const accessLabel = element('span', 'dsh-mobile-control__access-label'); accessLabel.textContent = t('browserAccess')\n const accessLink = element('a', 'dsh-mobile-control__access-link'); accessLink.target = '_blank'; accessLink.rel = 'noreferrer'\n access.append(accessLabel, accessLink)\n const qrBox = element('div', 'dsh-mobile-control__qr'); qrBox.hidden = true\n const status = element('p', 'dsh-mobile-control__status'); status.textContent = t('loadingStatus')\n const extensionStatus = element('p', 'dsh-mobile-control__extensions'); extensionStatus.hidden = true\n const actions = element('div', 'dsh-mobile-control__actions')\n const toggle = element('button', 'dsh-mobile-control__secondary'); toggle.type = 'button'\n const pair = element('button', 'dsh-mobile-control__primary'); pair.type = 'button'; pair.textContent = t('generateCopyKey')\n const linkPair = element('button', 'dsh-mobile-control__secondary'); linkPair.type = 'button'; linkPair.textContent = t('copyPairLink')\n const manageRow = element('div', 'dsh-mobile-control__manage-row')\n const manageDevices = element('button', 'dsh-mobile-control__manage'); manageDevices.type = 'button'; manageDevices.textContent = t('managePairedDevices')\n const resetAll = element('button', 'dsh-mobile-control__manage'); resetAll.type = 'button'; resetAll.textContent = t('clearAllDevices')\n manageRow.append(manageDevices, resetAll)\n const devicePanel = element('div', 'dsh-mobile-control__devices'); devicePanel.hidden = true\n const remoteView = element('div', 'dsh-mobile-control__view is-remote'); remoteView.hidden = true\n const remoteIntro = element('p', 'dsh-mobile-control__intro'); remoteIntro.textContent = t('remoteIntro')\n const httpFrameWarning = element('p', 'dsh-mobile-control__http-frame-warning'); httpFrameWarning.textContent = t('httpFrameWarning'); httpFrameWarning.hidden = location.protocol !== 'https:'; httpFrameWarning.setAttribute('role', 'note')\n const providerSection = element('section', 'dsh-mobile-control__provider-section')\n const providerHeading = element('h3', 'dsh-mobile-control__section-title'); providerHeading.textContent = t('chooseProvider')\n const providerInfo = element('div', 'dsh-mobile-control__provider-info')\n const providerInfoButton = element('button', 'dsh-mobile-control__provider-info-button'); providerInfoButton.type = 'button'; providerInfoButton.setAttribute('aria-label', t('providerInfoAria')); providerInfoButton.setAttribute('aria-expanded', 'false'); providerInfoButton.setAttribute('aria-controls', 'dsh-mobile-provider-info'); providerInfoButton.setAttribute('aria-describedby', 'dsh-mobile-provider-info')\n const providerInfoGlyph = element('span', 'dsh-mobile-control__provider-info-glyph'); providerInfoGlyph.textContent = 'i'; providerInfoGlyph.setAttribute('aria-hidden', 'true')\n const providerInfoPopover = element('div', 'dsh-mobile-control__provider-info-popover'); providerInfoPopover.id = 'dsh-mobile-provider-info'; providerInfoPopover.setAttribute('role', 'tooltip'); providerInfoPopover.hidden = true\n const providerInfoTitle = element('strong'); providerInfoTitle.textContent = t('providerSafeTitle')\n const providerInfoText = element('span'); providerInfoText.textContent = t('providerSafeText')\n providerInfoButton.append(providerInfoGlyph); providerInfoPopover.append(providerInfoTitle, providerInfoText); providerInfo.append(providerInfoButton, providerInfoPopover)\n const providerChoices = element('div', 'dsh-mobile-control__provider-choices'); providerChoices.setAttribute('role', 'radiogroup'); providerChoices.setAttribute('aria-label', t('providerGroupAria'))\n const tailscaleChoice = element('button', 'dsh-mobile-control__provider'); tailscaleChoice.type = 'button'; tailscaleChoice.setAttribute('role', 'radio'); tailscaleChoice.setAttribute('aria-checked', 'true')\n const tailscaleChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const tailscaleChoiceName = element('strong'); tailscaleChoiceName.textContent = 'Tailscale Funnel'\n const tailscaleChoiceBadge = element('span', 'dsh-mobile-control__provider-badge'); tailscaleChoiceBadge.textContent = t('builtIn')\n const tailscaleChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); tailscaleChoiceDescription.textContent = t('tailscaleDescription')\n tailscaleChoiceTop.append(tailscaleChoiceName, tailscaleChoiceBadge); tailscaleChoice.append(tailscaleChoiceTop, tailscaleChoiceDescription)\n const cpolarChoice = element('button', 'dsh-mobile-control__provider'); cpolarChoice.type = 'button'; cpolarChoice.setAttribute('role', 'radio'); cpolarChoice.setAttribute('aria-checked', 'false')\n const cpolarChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const cpolarChoiceName = element('strong'); cpolarChoiceName.textContent = 'cpolar'\n const cpolarChoiceBadge = element('span', 'dsh-mobile-control__provider-badge is-cpolar'); cpolarChoiceBadge.textContent = t('mainlandPreferred')\n const cpolarChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); cpolarChoiceDescription.textContent = t('cpolarDescription')\n cpolarChoiceTop.append(cpolarChoiceName, cpolarChoiceBadge); cpolarChoice.append(cpolarChoiceTop, cpolarChoiceDescription)\n const cloudflaredChoice = element('button', 'dsh-mobile-control__provider'); cloudflaredChoice.type = 'button'; cloudflaredChoice.setAttribute('role', 'radio'); cloudflaredChoice.setAttribute('aria-checked', 'false')\n const cloudflaredChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const cloudflaredChoiceName = element('strong'); cloudflaredChoiceName.textContent = 'cloudflared'\n const cloudflaredChoiceBadge = element('span', 'dsh-mobile-control__provider-badge'); cloudflaredChoiceBadge.textContent = t('cloudflaredBadge')\n const cloudflaredChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); cloudflaredChoiceDescription.textContent = t('cloudflaredDescription')\n cloudflaredChoiceTop.append(cloudflaredChoiceName, cloudflaredChoiceBadge); cloudflaredChoice.append(cloudflaredChoiceTop, cloudflaredChoiceDescription)\n providerChoices.append(cpolarChoice, cloudflaredChoice, tailscaleChoice)\n // ARIA radios normally move focus and selection together, but choosing a provider switches\n // the remote channel behind a confirmation. The arrow keys therefore move focus only, Enter\n // or Space performs the guarded selection, and the group keeps a single tab stop.\n const providerCardOrder = [cpolarChoice, cloudflaredChoice, tailscaleChoice]\n providerChoices.addEventListener('keydown', event => {\n const step = event.key === 'ArrowRight' || event.key === 'ArrowDown' ? 1\n : event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 0\n const jump = event.key === 'Home' ? 0 : event.key === 'End' ? providerCardOrder.length - 1 : undefined\n if (step === 0 && jump === undefined) return\n const current = providerCardOrder.indexOf(document.activeElement as HTMLButtonElement)\n if (current < 0) return\n const next = jump ?? (current + step + providerCardOrder.length) % providerCardOrder.length\n event.preventDefault()\n providerCardOrder[next]?.focus()\n })\n const cpolarSetup = element('section', 'dsh-mobile-control__cpolar-setup'); cpolarSetup.hidden = true\n const cpolarSetupTitle = element('h3', 'dsh-mobile-control__section-title'); cpolarSetupTitle.textContent = t('prepareCpolar')\n const cpolarComponentStatus = element('p', 'dsh-mobile-control__component-status'); cpolarComponentStatus.textContent = t('checkingComponent')\n const cpolarInstall = element('button', 'dsh-mobile-control__primary'); cpolarInstall.type = 'button'; cpolarInstall.textContent = t('installOfficial')\n const cpolarAccount = element('div', 'dsh-mobile-control__cpolar-account'); cpolarAccount.hidden = true\n const cpolarAccountText = element('p', 'dsh-mobile-control__component-note'); cpolarAccountText.textContent = t('cpolarAccountNote')\n const cpolarAccountLinks = element('div', 'dsh-mobile-control__link-row')\n const cpolarSignup = element('a', 'dsh-mobile-control__text-link'); cpolarSignup.href = 'https://dashboard.cpolar.com/signup'; cpolarSignup.target = '_blank'; cpolarSignup.rel = 'noopener noreferrer'; cpolarSignup.textContent = t('registerCpolar')\n const cpolarDashboard = element('a', 'dsh-mobile-control__text-link'); cpolarDashboard.href = 'https://dashboard.cpolar.com/auth'; cpolarDashboard.target = '_blank'; cpolarDashboard.rel = 'noopener noreferrer'; cpolarDashboard.textContent = t('openDashboard')\n cpolarAccountLinks.append(cpolarSignup, cpolarDashboard)\n const cpolarTokenLabel = element('label', 'dsh-mobile-control__token-label'); cpolarTokenLabel.textContent = 'Authtoken'\n const cpolarToken = element('input', 'dsh-mobile-control__token'); cpolarToken.type = 'password'; cpolarToken.autocomplete = 'off'; cpolarToken.spellcheck = false; cpolarToken.placeholder = t('tokenPlaceholder'); cpolarTokenLabel.append(cpolarToken)\n const cpolarConfigure = element('button', 'dsh-mobile-control__primary dsh-mobile-control__cpolar-connect'); cpolarConfigure.type = 'button'; cpolarConfigure.textContent = t('saveConnect')\n cpolarAccount.append(cpolarAccountText, cpolarAccountLinks, cpolarTokenLabel, cpolarConfigure)\n const cpolarDetails = element('details', 'dsh-mobile-control__details')\n const cpolarDetailsSummary = element('summary'); cpolarDetailsSummary.textContent = t('componentDetails')\n const cpolarDetailsBody = element('div', 'dsh-mobile-control__details-body')\n const cpolarDetailsText = element('p'); cpolarDetailsText.textContent = t('componentDetailsText')\n const cpolarStorage = element('code', 'dsh-mobile-control__storage'); cpolarStorage.textContent = t('pluginPrivateDirectory')\n const cpolarOfficial = element('a', 'dsh-mobile-control__text-link'); cpolarOfficial.href = 'https://www.cpolar.com/download'; cpolarOfficial.target = '_blank'; cpolarOfficial.rel = 'noopener noreferrer'; cpolarOfficial.textContent = t('officialDownload')\n const cpolarTerms = element('a', 'dsh-mobile-control__text-link'); cpolarTerms.href = 'https://www.cpolar.com/tos'; cpolarTerms.target = '_blank'; cpolarTerms.rel = 'noopener noreferrer'; cpolarTerms.textContent = t('terms')\n const cpolarPurge = element('button', 'dsh-mobile-control__danger'); cpolarPurge.type = 'button'; cpolarPurge.textContent = t('purgeCpolar')\n cpolarDetailsBody.append(cpolarDetailsText, cpolarStorage, cpolarOfficial, cpolarTerms, cpolarPurge); cpolarDetails.append(cpolarDetailsSummary, cpolarDetailsBody)\n cpolarSetup.append(cpolarSetupTitle, cpolarComponentStatus, cpolarInstall, cpolarAccount, cpolarDetails)\n // Share the managed-component setup layout: a quick tunnel has no account\n // section, so only the install action, the temporary-address warning, and the\n // component source/cleanup disclosure remain.\n const cloudflaredSetup = element('section', 'dsh-mobile-control__cpolar-setup dsh-mobile-control__cloudflared-setup'); cloudflaredSetup.hidden = true\n const cloudflaredSetupTitle = element('h3', 'dsh-mobile-control__section-title'); cloudflaredSetupTitle.textContent = t('prepareCloudflared')\n // Tunnel type comes first: it decides whether an account token is needed at all,\n // while the component below is required either way.\n const cloudflaredTunnelTitle = element('h3', 'dsh-mobile-control__section-title'); cloudflaredTunnelTitle.textContent = t('cloudflaredTunnelTitle')\n const cloudflaredMode = element('div', 'dsh-mobile-control__switcher')\n const cloudflaredModeQuick = element('button', 'dsh-mobile-control__tab is-active'); cloudflaredModeQuick.type = 'button'; cloudflaredModeQuick.textContent = t('cloudflaredTunnelQuick')\n const cloudflaredModeNamed = element('button', 'dsh-mobile-control__tab'); cloudflaredModeNamed.type = 'button'; cloudflaredModeNamed.textContent = t('cloudflaredTunnelNamed')\n cloudflaredMode.append(cloudflaredModeQuick, cloudflaredModeNamed)\n const cloudflaredModeHint = element('p', 'dsh-mobile-control__component-status'); cloudflaredModeHint.textContent = t('cloudflaredTunnelQuickHint')\n const cloudflaredTunnel = element('div', 'dsh-mobile-control__tunnel'); cloudflaredTunnel.hidden = true\n const cloudflaredTunnelFields = element('div', 'dsh-mobile-control__frp-fields')\n const cloudflaredHostnameLabel = element('label', 'dsh-mobile-control__field'); cloudflaredHostnameLabel.textContent = t('cloudflaredTunnelHostname')\n const cloudflaredHostname = element('input'); cloudflaredHostname.type = 'text'; cloudflaredHostname.autocomplete = 'off'; cloudflaredHostname.spellcheck = false; cloudflaredHostname.placeholder = t('cloudflaredTunnelHostnamePlaceholder')\n const cloudflaredPortLabel = element('label', 'dsh-mobile-control__field'); cloudflaredPortLabel.textContent = t('cloudflaredTunnelPort')\n const cloudflaredPort = element('input'); cloudflaredPort.type = 'number'; cloudflaredPort.inputMode = 'numeric'; cloudflaredPort.min = '1024'; cloudflaredPort.max = '65535'; cloudflaredPort.value = '3444'\n const cloudflaredTokenLabel = element('label', 'dsh-mobile-control__field'); cloudflaredTokenLabel.textContent = t('cloudflaredTunnelToken')\n const cloudflaredToken = element('input'); cloudflaredToken.type = 'password'; cloudflaredToken.autocomplete = 'off'; cloudflaredToken.spellcheck = false; cloudflaredToken.placeholder = t('cloudflaredTunnelTokenPlaceholder')\n cloudflaredHostnameLabel.append(cloudflaredHostname); cloudflaredPortLabel.append(cloudflaredPort); cloudflaredTokenLabel.append(cloudflaredToken)\n cloudflaredTunnelFields.append(cloudflaredHostnameLabel, cloudflaredPortLabel, cloudflaredTokenLabel)\n const cloudflaredTunnelStatus = element('p', 'dsh-mobile-control__component-status'); cloudflaredTunnelStatus.textContent = t('cloudflaredTunnelMissing')\n const cloudflaredTunnelHint = element('p', 'dsh-mobile-control__component-status'); cloudflaredTunnelHint.textContent = t('cloudflaredTunnelHint', { port: '3444' })\n const cloudflaredTunnelSave = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); cloudflaredTunnelSave.type = 'button'; cloudflaredTunnelSave.textContent = t('cloudflaredTunnelSave')\n /** The button commits the shown mode, so quick mode must not promise to save settings. */\n const cloudflaredSaveLabel = (): string => (cloudflaredModeDraft ?? cloudflaredTunnelMode) === 'named'\n ? t('cloudflaredTunnelSave') : t('cloudflaredTunnelSaveQuick')\n const cloudflaredTunnelForget = element('button', 'dsh-mobile-control__danger dsh-mobile-control__frp-action'); cloudflaredTunnelForget.type = 'button'; cloudflaredTunnelForget.textContent = t('cloudflaredTunnelForget'); cloudflaredTunnelForget.hidden = true\n // Only the named-tunnel inputs live in this container. Save and Forget stay\n // outside it: Save is also how a quick tunnel is committed, so hiding the\n // container must never take the only way to submit the form with it.\n cloudflaredTunnel.append(cloudflaredTunnelFields, cloudflaredTunnelStatus, cloudflaredTunnelHint)\n const cloudflaredComponentStatus = element('p', 'dsh-mobile-control__component-status'); cloudflaredComponentStatus.textContent = t('checkingComponent')\n const cloudflaredInstall = element('button', 'dsh-mobile-control__primary'); cloudflaredInstall.type = 'button'; cloudflaredInstall.textContent = t('installOfficial')\n const cloudflaredNote = element('p', 'dsh-mobile-control__origin-warning'); cloudflaredNote.textContent = t('cloudflaredQuickNote')\n const cloudflaredDetails = element('details', 'dsh-mobile-control__details')\n const cloudflaredDetailsSummary = element('summary'); cloudflaredDetailsSummary.textContent = t('componentDetails')\n const cloudflaredDetailsBody = element('div', 'dsh-mobile-control__details-body')\n const cloudflaredDetailsText = element('p'); cloudflaredDetailsText.textContent = t('cloudflaredComponentNote')\n const cloudflaredStorage = element('code', 'dsh-mobile-control__storage'); cloudflaredStorage.textContent = t('pluginPrivateDirectory')\n const cloudflaredOfficial = element('a', 'dsh-mobile-control__text-link'); cloudflaredOfficial.href = 'https://github.com/cloudflare/cloudflared/releases'; cloudflaredOfficial.target = '_blank'; cloudflaredOfficial.rel = 'noopener noreferrer'; cloudflaredOfficial.textContent = t('officialDownload')\n const cloudflaredTerms = element('a', 'dsh-mobile-control__text-link'); cloudflaredTerms.href = 'https://www.cloudflare.com/website-terms/'; cloudflaredTerms.target = '_blank'; cloudflaredTerms.rel = 'noopener noreferrer'; cloudflaredTerms.textContent = t('terms')\n const cloudflaredPurge = element('button', 'dsh-mobile-control__danger'); cloudflaredPurge.type = 'button'; cloudflaredPurge.textContent = t('purgeCloudflared')\n cloudflaredDetailsBody.append(cloudflaredDetailsText, cloudflaredStorage, cloudflaredOfficial, cloudflaredTerms, cloudflaredPurge); cloudflaredDetails.append(cloudflaredDetailsSummary, cloudflaredDetailsBody)\n cloudflaredSetup.append(cloudflaredSetupTitle, cloudflaredTunnelTitle, cloudflaredMode, cloudflaredModeHint, cloudflaredTunnel, cloudflaredTunnelSave, cloudflaredTunnelForget, cloudflaredComponentStatus, cloudflaredInstall, cloudflaredNote, cloudflaredDetails)\n const selfHosted = element('details', 'dsh-mobile-control__self-hosted')\n const selfHostedSummary = element('summary', 'dsh-mobile-control__self-hosted-summary')\n const selfHostedSummaryText = element('span')\n const selfHostedSummaryTitle = element('strong'); selfHostedSummaryTitle.textContent = t('selfHostedConnections')\n const selfHostedSummaryDescription = element('span'); selfHostedSummaryDescription.textContent = t('selfHostedDescription')\n const selfHostedBadge = element('span', 'dsh-mobile-control__provider-badge is-frp'); selfHostedBadge.textContent = t('advanced')\n selfHostedSummaryText.append(selfHostedSummaryTitle, selfHostedSummaryDescription); selfHostedSummary.append(selfHostedSummaryText, selfHostedBadge)\n const selfHostedBody = element('div', 'dsh-mobile-control__self-hosted-body')\n const frpChoice = element('button', 'dsh-mobile-control__provider is-frp'); frpChoice.type = 'button'; frpChoice.setAttribute('aria-pressed', 'false')\n const frpChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const frpChoiceName = element('strong'); frpChoiceName.textContent = t('frpName')\n const frpChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); frpChoiceDescription.textContent = t('frpDescription')\n frpChoiceTop.append(frpChoiceName); frpChoice.append(frpChoiceTop, frpChoiceDescription)\n const originChoice = element('button', 'dsh-mobile-control__provider is-origin'); originChoice.type = 'button'; originChoice.setAttribute('aria-pressed', 'false')\n const originChoiceTop = element('span', 'dsh-mobile-control__provider-top')\n const originChoiceName = element('strong'); originChoiceName.textContent = t('originName')\n const originChoiceDescription = element('span', 'dsh-mobile-control__provider-description'); originChoiceDescription.textContent = t('originDescription')\n originChoiceTop.append(originChoiceName); originChoice.append(originChoiceTop, originChoiceDescription)\n selfHostedBody.append(frpChoice, originChoice); selfHosted.append(selfHostedSummary, selfHostedBody)\n providerSection.append(providerHeading, providerInfo, providerChoices, selfHosted)\n const frpSetup = element('section', 'dsh-mobile-control__frp-setup'); frpSetup.hidden = true\n const frpSetupTitle = element('h3', 'dsh-mobile-control__section-title'); frpSetupTitle.textContent = t('prepareFrp')\n const frpStep1 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep1Title = element('strong'); frpStep1Title.textContent = t('frpStep1Title')\n const frpStep1Text = element('p'); frpStep1Text.textContent = t('frpStep1Text')\n const frpFields = element('div', 'dsh-mobile-control__frp-fields')\n const frpServerLabel = element('label', 'dsh-mobile-control__field'); frpServerLabel.textContent = t('frpServerAddress')\n const frpServer = element('input'); frpServer.type = 'text'; frpServer.autocomplete = 'off'; frpServer.spellcheck = false; frpServer.placeholder = t('frpServerAddressPlaceholder')\n const frpPortLabel = element('label', 'dsh-mobile-control__field'); frpPortLabel.textContent = t('frpServerPort')\n const frpPort = element('input'); frpPort.type = 'number'; frpPort.inputMode = 'numeric'; frpPort.min = '1'; frpPort.max = '65535'; frpPort.value = '7000'\n const frpTokenLabel = element('label', 'dsh-mobile-control__field'); frpTokenLabel.textContent = t('frpToken')\n const frpToken = element('input'); frpToken.type = 'password'; frpToken.autocomplete = 'off'; frpToken.spellcheck = false; frpToken.placeholder = t('frpTokenPlaceholder')\n const frpOriginLabel = element('label', 'dsh-mobile-control__field'); frpOriginLabel.textContent = t('frpPublicOrigin')\n const frpOrigin = element('input'); frpOrigin.type = 'url'; frpOrigin.autocomplete = 'off'; frpOrigin.spellcheck = false; frpOrigin.placeholder = t('frpPublicOriginPlaceholder')\n const frpModeLabel = element('label', 'dsh-mobile-control__field'); frpModeLabel.textContent = t('frpMode')\n const frpMode = element('select')\n for (const [value, label] of [['deploy', t('frpModeDeploy')], ['attach', t('frpModeAttach')]] as const) {\n const option = element('option'); option.value = value; option.textContent = label; frpMode.append(option)\n }\n const frpModeHint = element('p', 'dsh-mobile-control__frp-hint'); frpModeHint.textContent = t('frpModeHint')\n frpModeLabel.append(frpMode, frpModeHint)\n const frpEntryTlsLabel = element('label', 'dsh-mobile-control__field'); frpEntryTlsLabel.textContent = t('frpEntryTls')\n const frpEntryTls = element('select')\n for (const [value, label] of [['public-ip-cert', t('frpEntryTlsPublic')], ['self-signed', t('frpEntryTlsSelfSigned')]] as const) {\n const option = element('option'); option.value = value; option.textContent = label; frpEntryTls.append(option)\n }\n frpEntryTlsLabel.append(frpEntryTls)\n const frpVhostLabel = element('label', 'dsh-mobile-control__field'); frpVhostLabel.textContent = t('frpVhostHttpPort')\n const frpVhostPort = element('input'); frpVhostPort.type = 'number'; frpVhostPort.inputMode = 'numeric'; frpVhostPort.min = '1'; frpVhostPort.max = '65535'; frpVhostPort.placeholder = '7080'\n const frpVhostHint = element('p', 'dsh-mobile-control__frp-hint'); frpVhostHint.id = 'dsh-mobile-frp-vhost-hint'; frpVhostHint.textContent = t('frpVhostHttpPortHint')\n frpVhostPort.setAttribute('aria-describedby', frpVhostHint.id)\n frpVhostLabel.append(frpVhostPort, frpVhostHint)\n const frpPublicPortLabel = element('label', 'dsh-mobile-control__field'); frpPublicPortLabel.textContent = t('frpPublicPort')\n const frpPublicPort = element('input'); frpPublicPort.type = 'number'; frpPublicPort.inputMode = 'numeric'; frpPublicPort.min = '1'; frpPublicPort.max = '65535'; frpPublicPort.value = '33080'; frpPublicPort.placeholder = '33080'\n const frpPublicPortHint = element('p', 'dsh-mobile-control__frp-hint'); frpPublicPortHint.textContent = t('frpPublicPortHint')\n frpPublicPortLabel.append(frpPublicPort, frpPublicPortHint)\n frpServerLabel.append(frpServer); frpPortLabel.append(frpPort); frpTokenLabel.append(frpToken); frpOriginLabel.append(frpOrigin)\n frpFields.append(frpServerLabel, frpPortLabel, frpTokenLabel, frpOriginLabel, frpModeLabel, frpEntryTlsLabel, frpVhostLabel, frpPublicPortLabel)\n frpStep1.append(frpStep1Title, frpStep1Text, frpFields)\n const frpStep2 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep2Title = element('strong'); frpStep2Title.textContent = t('frpStep2Title')\n const frpStep2Text = element('p'); frpStep2Text.textContent = t('frpStep2Text')\n const frpCopyTemplate = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); frpCopyTemplate.type = 'button'; frpCopyTemplate.textContent = t('copyServerTemplate')\n const frpCopyAttachPlan = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); frpCopyAttachPlan.type = 'button'; frpCopyAttachPlan.textContent = t('frpAttachPlan'); frpCopyAttachPlan.hidden = true\n // The only path that puts a plaintext token on the clipboard, and only after a\n // deliberate click; the default copy button above always stays masked.\n const frpCopyAttachToken = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); frpCopyAttachToken.type = 'button'; frpCopyAttachToken.textContent = t('frpAttachToken'); frpCopyAttachToken.hidden = true\n const frpSelfCheckButton = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); frpSelfCheckButton.type = 'button'; frpSelfCheckButton.textContent = t('frpAttachSelfCheck'); frpSelfCheckButton.hidden = true\n const frpSelfCheckStatus = element('p', 'dsh-mobile-control__component-status'); frpSelfCheckStatus.textContent = ''; frpSelfCheckStatus.hidden = true\n const vpsDeployText = element('p'); vpsDeployText.textContent = t('vpsDeployText')\n const vpsChangesTitle = element('p'); vpsChangesTitle.textContent = t('vpsDeployChangesTitle')\n const vpsChanges = element('ul', 'dsh-mobile-control__frp-changes')\n for (const key of ['vpsDeployChangePackages', 'vpsDeployChangeServices', 'vpsDeployChangeFirewall', 'vpsDeployChangeManual'] as const) {\n const item = element('li'); item.textContent = t(key); vpsChanges.append(item)\n }\n const vpsDeployFields = element('div', 'dsh-mobile-control__frp-fields')\n const vpsSshUserLabel = element('label', 'dsh-mobile-control__field'); vpsSshUserLabel.textContent = t('vpsSshUser')\n const vpsSshUser = element('input'); vpsSshUser.type = 'text'; vpsSshUser.autocomplete = 'username'; vpsSshUser.value = 'ubuntu'; vpsSshUser.spellcheck = false\n const vpsSshPortLabel = element('label', 'dsh-mobile-control__field'); vpsSshPortLabel.textContent = t('vpsSshPort')\n const vpsSshPort = element('input'); vpsSshPort.type = 'number'; vpsSshPort.inputMode = 'numeric'; vpsSshPort.min = '1'; vpsSshPort.max = '65535'; vpsSshPort.value = '22'\n const vpsSshKeyLabel = element('label', 'dsh-mobile-control__field'); vpsSshKeyLabel.textContent = t('vpsSshKey')\n const vpsSshKey = element('input'); vpsSshKey.type = 'text'; vpsSshKey.autocomplete = 'off'; vpsSshKey.spellcheck = false; vpsSshKey.placeholder = t('vpsSshKeyPlaceholder')\n vpsSshUserLabel.append(vpsSshUser); vpsSshPortLabel.append(vpsSshPort); vpsSshKeyLabel.append(vpsSshKey)\n vpsDeployFields.append(vpsSshUserLabel, vpsSshPortLabel, vpsSshKeyLabel)\n const vpsDeploy = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); vpsDeploy.type = 'button'; vpsDeploy.textContent = t('vpsDeploy')\n const vpsDeployStatus = element('p', 'dsh-mobile-control__component-status'); vpsDeployStatus.textContent = ''\n const vpsCopyUninstall = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__frp-action'); vpsCopyUninstall.type = 'button'; vpsCopyUninstall.textContent = t('vpsCopyUninstall')\n const vpsUninstall = element('button', 'dsh-mobile-control__danger dsh-mobile-control__frp-action'); vpsUninstall.type = 'button'; vpsUninstall.textContent = t('vpsUninstall')\n frpStep2.append(frpStep2Title, frpStep2Text, frpCopyTemplate, frpCopyAttachPlan, frpCopyAttachToken, frpSelfCheckButton, frpSelfCheckStatus, vpsDeployText, vpsChangesTitle, vpsChanges, vpsDeployFields, vpsDeploy, vpsDeployStatus, vpsCopyUninstall, vpsUninstall)\n const frpStep3 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep3Title = element('strong'); frpStep3Title.textContent = t('frpStep3Title')\n const frpStep3Text = element('p'); frpStep3Text.textContent = t('frpStep3Text')\n const frpComponentStatus = element('p', 'dsh-mobile-control__component-status'); frpComponentStatus.textContent = t('checkingComponent')\n const frpInstall = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); frpInstall.type = 'button'; frpInstall.textContent = t('installFrpc')\n frpStep3.append(frpStep3Title, frpStep3Text, frpComponentStatus, frpInstall)\n const frpStep4 = element('section', 'dsh-mobile-control__frp-step')\n const frpStep4Title = element('strong'); frpStep4Title.textContent = t('frpStep4Title')\n const frpStep4Text = element('p'); frpStep4Text.textContent = t('frpStep4Text')\n const frpAppRequirement = element('p', 'dsh-mobile-control__frp-requirement'); frpAppRequirement.textContent = t('frpAppRequirement')\n const frpConfigurationStatus = element('p', 'dsh-mobile-control__component-status'); frpConfigurationStatus.textContent = t('frpConfigurationMissing')\n const frpConfigure = element('button', 'dsh-mobile-control__primary dsh-mobile-control__frp-action'); frpConfigure.type = 'button'; frpConfigure.textContent = t('frpSaveConnect')\n frpStep4.append(frpStep4Title, frpStep4Text, frpAppRequirement, frpConfigurationStatus, frpConfigure)\n const frpDetails = element('details', 'dsh-mobile-control__details')\n const frpDetailsSummary = element('summary'); frpDetailsSummary.textContent = t('frpComponentDetails')\n const frpDetailsBody = element('div', 'dsh-mobile-control__details-body')\n const frpDetailsText = element('p'); frpDetailsText.textContent = t('frpComponentDetailsText')\n const frpStorage = element('code', 'dsh-mobile-control__storage'); frpStorage.textContent = t('pluginPrivateDirectory')\n const frpOfficial = element('a', 'dsh-mobile-control__text-link'); frpOfficial.href = 'https://github.com/fatedier/frp/releases/tag/v0.70.1'; frpOfficial.target = '_blank'; frpOfficial.rel = 'noopener noreferrer'; frpOfficial.textContent = t('frpOfficialRelease')\n const frpPurge = element('button', 'dsh-mobile-control__danger'); frpPurge.type = 'button'; frpPurge.textContent = t('purgeFrp')\n frpDetailsBody.append(frpDetailsText, frpStorage, frpOfficial, frpPurge); frpDetails.append(frpDetailsSummary, frpDetailsBody)\n const frpOverview = element('section', 'dsh-mobile-control__frp-overview'); frpOverview.hidden = true\n const frpOverviewMark = element('span', 'dsh-mobile-control__frp-overview-mark'); frpOverviewMark.textContent = '✓'\n const frpOverviewBody = element('div', 'dsh-mobile-control__frp-overview-body')\n const frpOverviewTitle = element('strong'); frpOverviewTitle.textContent = t('frpConfigurationReady')\n const frpOverviewEndpoint = element('span'); frpOverviewEndpoint.textContent = ''\n frpOverviewBody.append(frpOverviewTitle, frpOverviewEndpoint); frpOverview.append(frpOverviewMark, frpOverviewBody)\n const frpConnectionGroup = element('details', 'dsh-mobile-control__frp-group')\n const frpConnectionSummary = element('summary'); frpConnectionSummary.textContent = t('frpStep1Title')\n frpConnectionGroup.append(frpConnectionSummary, frpStep1)\n const frpVpsGroup = element('details', 'dsh-mobile-control__frp-group')\n const frpVpsSummary = element('summary'); frpVpsSummary.textContent = t('frpStep2Title')\n frpVpsGroup.append(frpVpsSummary, frpStep2)\n const frpComponentGroup = element('details', 'dsh-mobile-control__frp-group')\n const frpComponentSummary = element('summary'); frpComponentSummary.textContent = t('frpStep3Title')\n frpComponentGroup.append(frpComponentSummary, frpStep3)\n const frpVerifyGroup = element('details', 'dsh-mobile-control__frp-group'); frpVerifyGroup.open = true\n const frpVerifySummary = element('summary'); frpVerifySummary.textContent = t('frpStep4Title')\n frpVerifyGroup.append(frpVerifySummary, frpStep4)\n frpSetup.append(frpSetupTitle, frpOverview, frpConnectionGroup, frpVpsGroup, frpComponentGroup, frpVerifyGroup, frpDetails)\n const originSetup = element('section', 'dsh-mobile-control__origin-setup'); originSetup.hidden = true\n const originSetupTitle = element('h3', 'dsh-mobile-control__section-title'); originSetupTitle.textContent = t('prepareOrigin')\n const originHelp = element('p', 'dsh-mobile-control__component-note'); originHelp.id = 'dsh-mobile-origin-help'; originHelp.textContent = t('originHelp')\n const originWarning = element('p', 'dsh-mobile-control__origin-warning'); originWarning.textContent = t('originWarning')\n const originFields = element('div', 'dsh-mobile-control__origin-fields')\n const originPublicLabel = element('label', 'dsh-mobile-control__field'); originPublicLabel.textContent = t('originPublicOrigin')\n const originPublic = element('input'); originPublic.type = 'url'; originPublic.placeholder = 'https://phone.example.com:8815'; originPublic.maxLength = 512\n const originHostLabel = element('label', 'dsh-mobile-control__field'); originHostLabel.textContent = t('originListenHost')\n const originHost = element('input'); originHost.type = 'text'; originHost.value = '127.0.0.1'; originHost.maxLength = 15\n const originPortLabel = element('label', 'dsh-mobile-control__field'); originPortLabel.textContent = t('originListenPort')\n const originPort = element('input'); originPort.type = 'number'; originPort.inputMode = 'numeric'; originPort.min = '1024'; originPort.max = '65535'; originPort.step = '1'; originPort.value = '3444'\n const originCidrsLabel = element('label', 'dsh-mobile-control__field'); originCidrsLabel.textContent = t('originAllowedCidrs')\n const originCidrs = element('input'); originCidrs.type = 'text'; originCidrs.value = '127.0.0.0/8'; originCidrs.maxLength = 1040\n for (const input of [originPublic, originHost, originPort, originCidrs]) {\n input.required = true; input.autocomplete = 'off'; input.spellcheck = false\n input.setAttribute('aria-describedby', 'dsh-mobile-origin-help dsh-mobile-origin-feedback')\n }\n originPublicLabel.append(originPublic); originHostLabel.append(originHost); originPortLabel.append(originPort); originCidrsLabel.append(originCidrs)\n originFields.append(originPublicLabel, originHostLabel, originPortLabel, originCidrsLabel)\n const originFeedback = element('p', 'dsh-mobile-control__origin-feedback'); originFeedback.id = 'dsh-mobile-origin-feedback'; originFeedback.setAttribute('role', 'status'); originFeedback.setAttribute('aria-live', 'polite'); originFeedback.hidden = true\n const originConfigure = element('button', 'dsh-mobile-control__primary'); originConfigure.type = 'button'; originConfigure.textContent = t('originSaveStart')\n const originPurge = element('button', 'dsh-mobile-control__danger'); originPurge.type = 'button'; originPurge.textContent = t('originPurge'); originPurge.hidden = true\n const originFormActions = element('div', 'dsh-mobile-control__actions'); originFormActions.append(originConfigure, originPurge)\n const originBackend = element('div', 'dsh-mobile-control__origin-backend'); originBackend.hidden = true\n const originBackendLabel = element('span', 'dsh-mobile-control__access-label')\n const originBackendAddress = element('code', 'dsh-mobile-control__storage')\n const originCopyBackend = element('button', 'dsh-mobile-control__secondary'); originCopyBackend.type = 'button'; originCopyBackend.textContent = t('originCopyBackend')\n originBackend.append(originBackendLabel, originBackendAddress, originCopyBackend)\n originSetup.append(originSetupTitle, originHelp, originWarning, originFields, originFeedback, originFormActions, originBackend)\n const tailscaleInfo = element('details', 'dsh-mobile-control__details')\n const tailscaleInfoSummary = element('summary'); tailscaleInfoSummary.textContent = t('tailscaleHelp')\n const tailscaleInfoBody = element('div', 'dsh-mobile-control__details-body')\n const tailscaleInfoText = element('p'); tailscaleInfoText.textContent = t('tailscaleHelpText')\n tailscaleInfoBody.append(tailscaleInfoText); tailscaleInfo.append(tailscaleInfoSummary, tailscaleInfoBody)\n const providerSetupHeader = element('div', 'dsh-mobile-control__stage-header')\n const providerSetupHeading = element('h3', 'dsh-mobile-control__section-title'); providerSetupHeading.textContent = t('currentProvider')\n const providerSetupName = element('span', 'dsh-mobile-control__stage-value'); providerSetupName.textContent = 'Tailscale Funnel'\n const remoteStateBadge = element('span', 'dsh-mobile-control__state-badge'); remoteStateBadge.textContent = t('remoteStateOff')\n const providerSetupMeta = element('div', 'dsh-mobile-control__stage-meta'); providerSetupMeta.append(providerSetupName, remoteStateBadge)\n providerSetupHeader.append(providerSetupHeading, providerSetupMeta)\n const providerSetupBody = element('div', 'dsh-mobile-control__provider-setup-body')\n providerSetupBody.append(cpolarSetup, cloudflaredSetup, frpSetup, originSetup, tailscaleInfo)\n const remoteAccess = element('div', 'dsh-mobile-control__access'); remoteAccess.hidden = true\n const remoteAccessLabel = element('span', 'dsh-mobile-control__access-label'); remoteAccessLabel.textContent = t('remoteAddress')\n const remoteAccessLink = element('a', 'dsh-mobile-control__access-link'); remoteAccessLink.target = '_blank'; remoteAccessLink.rel = 'noreferrer'; remoteAccess.append(remoteAccessLabel, remoteAccessLink)\n const remoteQr = element('div', 'dsh-mobile-control__qr'); remoteQr.hidden = true\n const remoteStatus = element('p', 'dsh-mobile-control__status'); remoteStatus.textContent = t('loadingRemoteStatus'); remoteStatus.setAttribute('aria-live', 'polite')\n const remoteGuide = element('section', 'dsh-mobile-control__guide'); remoteGuide.hidden = true; remoteGuide.setAttribute('aria-label', t('funnelGuideAria'))\n const remoteGuideTitle = element('h3', 'dsh-mobile-control__guide-title'); remoteGuideTitle.textContent = t('funnelGuideTitle')\n const remoteGuideSummary = element('p', 'dsh-mobile-control__guide-summary'); remoteGuideSummary.textContent = t('funnelGuideSummary')\n const remoteGuideSteps = element('ol', 'dsh-mobile-control__guide-steps')\n for (const text of [t('funnelStep1'), t('funnelStep2'), t('funnelStep3')]) {\n const item = element('li'); item.textContent = text; remoteGuideSteps.append(item)\n }\n const remoteGuideNote = element('p', 'dsh-mobile-control__guide-note'); remoteGuideNote.textContent = t('funnelGuideNote')\n const remoteGuideActions = element('div', 'dsh-mobile-control__guide-actions')\n const remoteSetup = element('button', 'dsh-mobile-control__primary'); remoteSetup.type = 'button'; remoteSetup.textContent = t('continueFunnel')\n const remoteSetupRetry = element('button', 'dsh-mobile-control__secondary'); remoteSetupRetry.type = 'button'; remoteSetupRetry.textContent = t('retryNow')\n remoteGuideActions.append(remoteSetup, remoteSetupRetry); remoteGuide.append(remoteGuideTitle, remoteGuideSummary, remoteGuideSteps, remoteGuideNote, remoteGuideActions)\n const remoteActions = element('div', 'dsh-mobile-control__actions')\n const remoteToggle = element('button', 'dsh-mobile-control__primary'); remoteToggle.type = 'button'; remoteToggle.textContent = t('enableRemote')\n const remoteLogin = element('button', 'dsh-mobile-control__primary'); remoteLogin.type = 'button'; remoteLogin.textContent = t('continueLogin'); remoteLogin.hidden = true\n const remoteReconnect = element('button', 'dsh-mobile-control__secondary'); remoteReconnect.type = 'button'; remoteReconnect.textContent = t('reconnect'); remoteReconnect.hidden = true\n const remotePair = element('button', 'dsh-mobile-control__secondary'); remotePair.type = 'button'; remotePair.textContent = t('generateRemoteQr'); remotePair.disabled = true\n const remoteCopyLink = element('button', 'dsh-mobile-control__secondary'); remoteCopyLink.type = 'button'; remoteCopyLink.textContent = t('copyRemoteLink'); remoteCopyLink.disabled = true\n const remotePairLink = element('p', 'dsh-mobile-control__status'); remotePairLink.hidden = true\n let activeRemotePairUrl = ''\n let activeRemotePairExpiresAt = 0\n remoteActions.append(remoteToggle, remoteLogin, remoteReconnect, remotePair, remoteCopyLink)\n const remoteManageRow = element('div', 'dsh-mobile-control__manage-row')\n const remoteDevices = element('button', 'dsh-mobile-control__manage'); remoteDevices.type = 'button'; remoteDevices.textContent = t('manageRemoteDevices'); remoteDevices.disabled = true\n const remoteReset = element('button', 'dsh-mobile-control__manage'); remoteReset.type = 'button'; remoteReset.textContent = t('resetRemoteLogin')\n remoteManageRow.append(remoteDevices, remoteReset)\n const remoteDevicePanel = element('div', 'dsh-mobile-control__devices'); remoteDevicePanel.hidden = true\n const wsPathsSection = element('section', 'dsh-mobile-control__ws-paths')\n const wsPathsTitle = element('h3', 'dsh-mobile-control__section-title'); wsPathsTitle.textContent = t('wsPathsTitle')\n const wsPathsIntro = element('p', 'dsh-mobile-control__intro'); wsPathsIntro.textContent = t('wsPathsIntro')\n const wsPathsDetected = element('div', 'dsh-mobile-control__ws-paths-detected'); wsPathsDetected.hidden = true\n const wsPathsDetectedTitle = element('p', 'dsh-mobile-control__ws-paths-detected-title'); wsPathsDetectedTitle.textContent = t('wsPathsDetected')\n const wsPathsDetectedList = element('ul', 'dsh-mobile-control__ws-paths-list')\n wsPathsDetected.append(wsPathsDetectedTitle, wsPathsDetectedList)\n const wsPathsList = element('ul', 'dsh-mobile-control__ws-paths-list')\n const wsPathsAdvanced = element('details', 'dsh-mobile-control__ws-paths-advanced')\n const wsPathsAdvancedSummary = element('summary'); wsPathsAdvancedSummary.textContent = t('wsPathsAdvancedToggle')\n const wsPathsAdvancedHint = element('p', 'dsh-mobile-control__intro'); wsPathsAdvancedHint.textContent = t('wsPathsAdvancedHint')\n const wsPathsRow = element('div', 'dsh-mobile-control__ws-paths-row')\n const wsPathsInput = element('input', 'dsh-mobile-control__ws-paths-input'); wsPathsInput.type = 'text'; wsPathsInput.placeholder = t('wsPathsPlaceholder'); wsPathsInput.setAttribute('aria-label', t('wsPathsTitle'))\n const wsPathsAdd = element('button', 'dsh-mobile-control__primary'); wsPathsAdd.type = 'button'; wsPathsAdd.textContent = t('wsPathsAdd')\n const wsPathsStatus = element('p', 'dsh-mobile-control__status'); wsPathsStatus.hidden = true; wsPathsStatus.setAttribute('aria-live', 'polite')\n wsPathsRow.append(wsPathsInput, wsPathsAdd)\n wsPathsAdvanced.append(wsPathsAdvancedSummary, wsPathsAdvancedHint, wsPathsRow, wsPathsStatus)\n wsPathsSection.append(wsPathsTitle, wsPathsIntro, wsPathsDetected, wsPathsList, wsPathsAdvanced)\n let wsPathsCurrent: string[] = []\n let wsPathsBlocked: { readonly path: string; readonly attempts: number }[] = []\n let wsPathsLoaded = false\n const renderWsPaths = (paths: readonly string[]): void => {\n wsPathsCurrent = [...paths]\n wsPathsList.replaceChildren()\n if (paths.length === 0) {\n const empty = element('li', 'dsh-mobile-control__ws-paths-empty'); empty.textContent = t('wsPathsEmpty'); wsPathsList.append(empty)\n }\n for (const path of paths) {\n const item = element('li', 'dsh-mobile-control__ws-paths-item')\n const label = element('code'); label.textContent = path\n const remove = element('button', 'dsh-mobile-control__ws-paths-remove'); remove.type = 'button'; remove.textContent = t('wsPathsRemove'); remove.setAttribute('aria-label', `${t('wsPathsRemove')}: ${path}`)\n remove.addEventListener('click', () => { void saveWsPaths(wsPathsCurrent.filter(entry => entry !== path)) })\n item.append(label, remove)\n wsPathsList.append(item)\n }\n }\n const wsGroupOf = (path: string): string => {\n const slash = path.lastIndexOf('/')\n return slash > 0 ? path.slice(0, slash) : '/'\n }\n let wsPathsSeenAttempts = 0\n let wsDotAnnounced = 0\n const wsDotStatus = element('p', 'dsh-mobile-control__ws-sr-status')\n wsDotStatus.setAttribute('role', 'status'); wsDotStatus.setAttribute('aria-atomic', 'true')\n const wsBlockedTotal = (blocked: readonly { readonly attempts: number }[]): number =>\n blocked.reduce((sum, entry) => sum + entry.attempts, 0)\n const renderWsDot = (): void => {\n for (const stale of document.querySelectorAll('.dsh-mobile-control__ws-dot')) stale.remove()\n if (!wsDotStatus.isConnected) root.append(wsDotStatus)\n const pending = wsPathsBlocked.filter(entry => !wsPathsCurrent.includes(entry.path))\n const pendingCount = pending.length\n if (pendingCount === 0 || wsBlockedTotal(wsPathsBlocked) <= wsPathsSeenAttempts) {\n if (pendingCount === 0 && wsDotAnnounced !== 0) { wsDotAnnounced = 0; wsDotStatus.textContent = '' }\n return\n }\n if (wsDotAnnounced !== pendingCount) {\n wsDotAnnounced = pendingCount\n wsDotStatus.textContent = t('wsPathsAnnounce', { n: String(pendingCount) })\n }\n for (const trigger of document.querySelectorAll('.dsh-mobile-control__trigger, .dsh-mobile-control__diagnostic-entry')) {\n const dot = element('span', 'dsh-mobile-control__ws-dot'); dot.textContent = String(pendingCount); dot.title = t('wsPathsNewBlocked'); dot.setAttribute('aria-hidden', 'true')\n dot.addEventListener('click', event => { event.stopPropagation(); setOpen(true); selectView('diagnostics') })\n trigger.append(dot)\n }\n }\n const renderWsBlocked = (blocked: readonly { readonly path: string; readonly attempts: number }[]): void => {\n wsPathsDetectedList.replaceChildren()\n const pending = blocked.filter(entry => !wsPathsCurrent.includes(entry.path))\n wsPathsDetected.hidden = pending.length === 0\n const groups = new Map<string, { readonly path: string; readonly attempts: number }[]>()\n for (const entry of pending) {\n const group = wsGroupOf(entry.path)\n const list = groups.get(group)\n if (list) list.push(entry); else groups.set(group, [entry])\n }\n for (const [group, entries] of groups) {\n const groupItem = element('li', 'dsh-mobile-control__ws-paths-group')\n const groupHead = element('div', 'dsh-mobile-control__ws-paths-group-head')\n const groupLabel = element('code'); groupLabel.textContent = `${group}/*`\n const groupCount = element('span', 'dsh-mobile-control__ws-count')\n groupCount.textContent = t('wsPathsGroupMeta', { n: entries.length, m: entries.reduce((sum, entry) => sum + entry.attempts, 0) })\n groupHead.append(groupLabel, groupCount)\n if (entries.length > 1) {\n const allowAll = element('button', 'dsh-mobile-control__primary'); allowAll.type = 'button'; allowAll.textContent = t('wsPathsAllowAll')\n allowAll.addEventListener('click', () => { saveWsPaths([...wsPathsCurrent, ...entries.map(entry => entry.path)]) })\n groupHead.append(allowAll)\n }\n groupItem.append(groupHead)\n for (const entry of entries) {\n const item = element('div', 'dsh-mobile-control__ws-paths-item is-child')\n const label = element('code'); label.textContent = `${entry.path} ×${String(entry.attempts)}`\n const allow = element('button', 'dsh-mobile-control__primary'); allow.type = 'button'; allow.textContent = t('wsPathsAllow'); allow.setAttribute('aria-label', `${t('wsPathsAllow')}: ${entry.path}`)\n allow.addEventListener('click', () => { saveWsPaths([...wsPathsCurrent, entry.path]) })\n item.append(label, allow)\n groupItem.append(item)\n }\n wsPathsDetectedList.append(groupItem)\n }\n renderWsDot()\n }\n const loadWsPaths = (): void => {\n void controlRequestJson('/api/mobile-access/remote/websocket-paths').then(data => {\n wsPathsLoaded = true\n wsPathsStatus.hidden = true\n renderWsPaths(Array.isArray(data.paths) ? data.paths.filter((entry): entry is string => typeof entry === 'string') : [])\n void controlRequestJson('/api/mobile-access/remote/websocket-paths/blocked').then(blocked => {\n const entries = Array.isArray(blocked.blocked) ? blocked.blocked : []\n wsPathsBlocked = entries.filter((entry): entry is { path: string; attempts: number } =>\n typeof entry === 'object' && entry !== null\n && typeof (entry as { path?: unknown }).path === 'string'\n && typeof (entry as { attempts?: unknown }).attempts === 'number')\n renderWsBlocked(wsPathsBlocked)\n }, () => { /* approved list already rendered; detected list stays hidden */ })\n }, error => {\n wsPathsStatus.hidden = false\n wsPathsStatus.textContent = t('requestFailed', { error: String(error) })\n })\n }\n const saveWsPaths = (paths: readonly string[]): void => {\n wsPathsStatus.hidden = true\n void controlRequestJson('/api/mobile-access/remote/websocket-paths', { method: 'POST', body: JSON.stringify({ paths }) }).then(data => {\n wsPathsLoaded = true\n renderWsPaths(Array.isArray(data.paths) ? data.paths.filter((entry): entry is string => typeof entry === 'string') : [])\n renderWsBlocked(wsPathsBlocked)\n }, error => {\n wsPathsStatus.hidden = false\n wsPathsStatus.textContent = t('requestFailed', { error: String(error) })\n })\n }\n wsPathsAdd.addEventListener('click', () => {\n const value = wsPathsInput.value.trim()\n if (value === '') {\n wsPathsStatus.hidden = false\n wsPathsStatus.textContent = t('wsPathsInvalid')\n return\n }\n wsPathsInput.value = ''\n saveWsPaths([...wsPathsCurrent, value])\n })\n const remoteWorkspace = element('section', 'dsh-mobile-control__remote-workspace')\n remoteWorkspace.append(providerSetupHeader, remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteActions, remoteQr, remotePairLink, remoteManageRow, remoteDevicePanel)\n const diagnosticsView = element('div', 'dsh-mobile-control__view is-diagnostics'); diagnosticsView.hidden = true\n const diagnosticsIntro = element('p', 'dsh-mobile-control__intro'); diagnosticsIntro.textContent = t('diagnosticsIntro')\n const diagnosticsSummary = element('section', 'dsh-mobile-control__diagnostic-summary is-idle'); diagnosticsSummary.setAttribute('aria-live', 'polite')\n const diagnosticsSummaryMain = element('div', 'dsh-mobile-control__diagnostic-summary-main')\n const diagnosticsSummaryIcon = element('span', 'dsh-mobile-control__diagnostic-summary-icon'); diagnosticsSummaryIcon.setAttribute('aria-hidden', 'true')\n const diagnosticsSummaryBody = element('div', 'dsh-mobile-control__diagnostic-summary-body')\n const diagnosticsSummaryTitle = element('strong'); diagnosticsSummaryTitle.textContent = t('diagnosticsNotRun')\n const diagnosticsSummaryText = element('span'); diagnosticsSummaryText.textContent = t('diagnosticsStartHint')\n const diagnosticsSummaryMeta = element('span', 'dsh-mobile-control__diagnostic-summary-meta'); diagnosticsSummaryMeta.textContent = t('diagnosticsIdleMeta')\n diagnosticsSummaryBody.append(diagnosticsSummaryTitle, diagnosticsSummaryText)\n diagnosticsSummaryMain.append(diagnosticsSummaryIcon, diagnosticsSummaryBody)\n diagnosticsSummary.append(diagnosticsSummaryMain, diagnosticsSummaryMeta)\n const diagnosticsToolbar = element('div', 'dsh-mobile-control__diagnostic-toolbar')\n const diagnosticsRun = element('button', 'dsh-mobile-control__primary dsh-mobile-control__diagnostic-run'); diagnosticsRun.type = 'button'; diagnosticsRun.textContent = t('diagnosticsStart')\n const diagnosticsCopy = element('button', 'dsh-mobile-control__secondary dsh-mobile-control__diagnostic-copy'); diagnosticsCopy.type = 'button'; diagnosticsCopy.textContent = t('diagnosticsCopy'); diagnosticsCopy.disabled = true; diagnosticsCopy.hidden = true\n diagnosticsToolbar.append(diagnosticsRun, diagnosticsCopy)\n const diagnosticsFeedback = element('p', 'dsh-mobile-control__diagnostic-feedback'); diagnosticsFeedback.hidden = true; diagnosticsFeedback.setAttribute('role', 'status'); diagnosticsFeedback.setAttribute('aria-live', 'polite')\n const diagnosticsChecks = element('div', 'dsh-mobile-control__diagnostic-checks'); diagnosticsChecks.hidden = true\n const diagnosticsDetails = element('details', 'dsh-mobile-control__details dsh-mobile-control__diagnostic-details'); diagnosticsDetails.hidden = true\n const diagnosticsDetailsSummary = element('summary'); diagnosticsDetailsSummary.textContent = t('diagnosticsAdvanced')\n const diagnosticsReport = element('pre', 'dsh-mobile-control__diagnostic-report')\n diagnosticsDetails.append(diagnosticsDetailsSummary, diagnosticsReport)\n header.append(title, headerActions); actions.append(toggle, pair, linkPair)\n lanView.append(lanSetup, access, qrBox, status, extensionStatus, actions, manageRow, devicePanel)\n remoteView.append(remoteIntro, httpFrameWarning, providerSection, remoteWorkspace)\n diagnosticsView.append(diagnosticsIntro, diagnosticsSummary, diagnosticsToolbar, diagnosticsFeedback, diagnosticsChecks, wsPathsSection, diagnosticsDetails)\n panel.append(header, releaseNotice, updateCard, appDownload, switcher, lanView, remoteView, diagnosticsView); root.append(panel); document.body.append(root)\n let running = false\n let origin = ''\n let lanConfigured = true\n let lanRestartRequired = false\n let lanSetupLoaded = false\n let remoteRunning = false\n let remoteReady = false\n let remoteProvider: MobileRemoteProvider = 'tailscale'\n let remoteLoginUrl = ''\n let remoteSetupUrl = ''\n let remoteSetupPending = false\n let remoteSetupOpenedAt = 0\n let remoteReconnectBusy = false\n let remoteProviderBusy = false\n // A failed control action stores its message here so the repaint that loadRemote() runs in the\n // action's finally cannot replace it before the user reads it. The repaint that would have\n // erased it consumes the value instead, so it never lingers over a later state.\n let remoteFailureText: string | undefined\n let cpolarInstalled = false\n let cpolarConfigured = false\n let cloudflaredInstalled = false\n let cloudflaredDownloadSize = ''\n let cloudflaredTunnelMode: 'quick' | 'named' = 'quick'\n let cloudflaredModeDraft: 'quick' | 'named' | null = null\n let cloudflaredTunnelConfigured = false\n let configuredCloudflaredHostname = ''\n let configuredCloudflaredPort = 3444\n // Once the user types in a field, polls must stop writing to it, or a stale\n // value the user is mid-edit would be silently replaced by the saved one.\n let cloudflaredHostnameDirty = false\n let cloudflaredPortDirty = false\n let originConfigured = false\n let originFormDirty = false\n let originFormBusy = false\n let remoteSnapshotEpoch = 0\n let latestRemoteState: Record<string, unknown> = {}\n let frpInstalled = false\n let frpConfigured = false\n let frpLayoutInitialized = false\n let frpDownloadSize = '14.0'\n let configuredFrpServer = ''\n let configuredFrpPort = 7000\n let configuredFrpOrigin = ''\n let configuredFrpVhostPort = 7080\n let configuredFrpPublicPort = 33_080\n let frpConfiguredMode: 'deploy' | 'attach' = 'deploy'\n let frpConfiguredEntryTls: 'public-ip-cert' | 'self-signed' = 'public-ip-cert'\n let frpModeDraft = false\n let frpEntryTlsDraft = false\n let frpVhostDraft = false\n let frpAttachVhostExplicit = false\n let providerInfoPinned = false\n let providerInfoHovered = false\n let previousAccessView: 'lan' | 'remote' = 'lan'\n let diagnosticsBusy = false\n let copiedDiagnosticReport = ''\n let pluginUpdateAvailable = false\n let pluginLatestVersion = ''\n let pluginReleaseNotes = ''\n let updateInFlight = false\n const renderRelease = (data: Record<string, unknown>): void => {\n const release = clientReleaseInfo(data)\n pluginUpdateAvailable = release.updateAvailable\n pluginLatestVersion = release.latestVersion ?? ''\n pluginReleaseNotes = release.releaseNotes ?? ''\n updatePlugin.hidden = !pluginUpdateAvailable || !diagnosticsView.hidden || !updateCard.hidden\n updatePlugin.textContent = t('updatePlugin')\n if (pluginLatestVersion !== '') updatePlugin.setAttribute('aria-label', t('updatePluginAria', { version: pluginLatestVersion }))\n if (release.androidVersion !== undefined) {\n appDownload.textContent = t('downloadAndroidVersion', { version: release.androidVersion })\n appDownload.setAttribute('aria-label', t('downloadAndroidVersionAria', { version: release.androidVersion }))\n }\n appDownload.href = release.androidDownloadUrl\n }\n const syncProviderInfo = (): void => {\n const open = providerInfoPinned || providerInfoHovered || providerInfo.contains(document.activeElement)\n providerInfoPopover.hidden = !open\n providerInfoButton.setAttribute('aria-expanded', String(open))\n }\n providerInfo.addEventListener('pointerenter', () => { providerInfoHovered = true; syncProviderInfo() })\n providerInfo.addEventListener('pointerleave', () => { providerInfoHovered = false; syncProviderInfo() })\n providerInfo.addEventListener('focusin', syncProviderInfo)\n providerInfo.addEventListener('focusout', () => { window.setTimeout(syncProviderInfo, 0) })\n providerInfoButton.addEventListener('click', () => { providerInfoPinned = !providerInfoPinned; syncProviderInfo() })\n providerInfoButton.addEventListener('keydown', event => {\n if (event.key !== 'Escape') return\n providerInfoPinned = false\n providerInfoHovered = false\n providerInfoPopover.hidden = true\n providerInfoButton.setAttribute('aria-expanded', 'false')\n })\n const selectView = (view: 'lan' | 'remote' | 'diagnostics'): void => {\n if (view !== 'diagnostics') previousAccessView = view\n lanView.hidden = view !== 'lan'\n remoteView.hidden = view !== 'remote'\n diagnosticsView.hidden = view !== 'diagnostics'\n lanTab.classList.toggle('is-active', view === 'lan')\n remoteTab.classList.toggle('is-active', view === 'remote')\n lanTab.setAttribute('aria-pressed', String(view === 'lan'))\n remoteTab.setAttribute('aria-pressed', String(view === 'remote'))\n diagnosticsEntry.setAttribute('aria-pressed', String(view === 'diagnostics'))\n diagnosticsEntry.textContent = view === 'diagnostics' ? t('back') : t('diagnostics')\n diagnosticsEntry.setAttribute('aria-label', view === 'diagnostics' ? t('backToMobile') : t('openDiagnostics'))\n if (view === 'diagnostics') {\n wsPathsSeenAttempts = wsBlockedTotal(wsPathsBlocked)\n renderWsDot()\n if (!wsPathsLoaded) loadWsPaths()\n }\n updatePlugin.hidden = view === 'diagnostics' || !pluginUpdateAvailable\n appDownload.hidden = view === 'diagnostics'\n switcher.hidden = view === 'diagnostics'\n title.textContent = view === 'lan' ? t('lanAccess') : view === 'remote' ? t('remoteAccess') : t('connectionDiagnostics')\n }\n lanTab.addEventListener('click', () => { selectView('lan') })\n remoteTab.addEventListener('click', () => { selectView('remote'); loadRemote() })\n const setOpen = (open: boolean): void => {\n panel.hidden = !open\n for (const trigger of document.querySelectorAll('.dsh-mobile-control__trigger')) trigger.setAttribute('aria-expanded', String(open))\n }\n const render = (data: Record<string, unknown>): void => {\n lanConfigured = data.configured !== false\n lanRestartRequired = data.restartRequired === true\n running = data.running === true\n origin = running && typeof data.origin === 'string' ? data.origin : ''\n lanSetup.hidden = lanConfigured && !lanRestartRequired\n lanSetupForm.hidden = lanRestartRequired\n lanSetupIntro.hidden = lanRestartRequired\n status.hidden = !lanConfigured || lanRestartRequired\n if (!lanConfigured && !lanSetupLoaded) loadLanSetup()\n if (lanRestartRequired) {\n const pendingOrigin = typeof data.pendingOrigin === 'string' ? data.pendingOrigin : ''\n lanSetupStatus.textContent = t('lanSetupRestart', { origin: pendingOrigin })\n } else if (!lanConfigured && lanSetupStatus.textContent === '') {\n lanSetupStatus.textContent = t('lanSetupRequired')\n }\n access.hidden = origin === ''\n accessLink.href = origin\n accessLink.textContent = origin\n accessLink.title = origin\n status.classList.toggle('is-running', running)\n status.textContent = !lanConfigured ? t('lanSetupRequired') : lanRestartRequired ? t('lanSetupRestart', { origin: typeof data.pendingOrigin === 'string' ? data.pendingOrigin : '' }) : running ? t('lanOn') : t('lanOff')\n const extensionData = data.extensions\n if (extensionData !== null && typeof extensionData === 'object') {\n const loaded = typeof (extensionData as { loaded?: unknown }).loaded === 'number' ? (extensionData as { loaded: number }).loaded : 0\n const failed = typeof (extensionData as { failed?: unknown }).failed === 'number' ? (extensionData as { failed: number }).failed : 0\n extensionStatus.hidden = false\n extensionStatus.textContent = failed === 0 ? t('extensionsLoaded', { loaded }) : t('extensionsFailed', { loaded, failed })\n } else extensionStatus.hidden = true\n if (!running) qrBox.hidden = true\n toggle.textContent = running ? t('disableLan') : t('enableLan')\n actions.hidden = !lanConfigured || lanRestartRequired\n manageRow.hidden = !lanConfigured || lanRestartRequired\n pair.disabled = !running\n linkPair.disabled = !running\n manageDevices.disabled = !running\n resetAll.disabled = !running\n }\n const renderLanSetup = (data: Record<string, unknown>): void => {\n if (data.restartRequired === true || data.configured === true) {\n render(data)\n return\n }\n const networks = Array.isArray(data.networks) ? data.networks.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null) : []\n lanSetupSelect.replaceChildren()\n const hasRecommended = networks.some(network => network.recommended === true)\n if (!hasRecommended && networks.length > 1) {\n const placeholder = element('option'); placeholder.value = ''; placeholder.textContent = t('lanSetupChooseNetwork'); placeholder.disabled = true; placeholder.selected = true\n lanSetupSelect.append(placeholder)\n }\n for (const network of networks) {\n if (typeof network.address !== 'string' || typeof network.name !== 'string') continue\n const option = element('option'); option.value = network.address\n option.textContent = `${network.name} · ${network.address}${network.recommended === true ? ` · ${t('lanSetupRecommended')}` : ''}`\n option.selected = network.recommended === true || (!hasRecommended && networks.length === 1)\n lanSetupSelect.append(option)\n }\n const available = networks.some(network => typeof network.address === 'string' && typeof network.name === 'string')\n lanSetupConfigure.disabled = lanSetupSelect.value === ''\n lanSetupStatus.textContent = available ? t('lanSetupRequired') : t('lanSetupNoNetworks')\n lanSetupNote.hidden = data.windowsFirewall !== true\n }\n lanSetupSelect.addEventListener('change', () => { lanSetupConfigure.disabled = lanSetupSelect.value === '' })\n const loadLanSetup = (): void => {\n lanSetupLoaded = true\n lanSetupRefresh.disabled = true\n void controlRequestJson('/api/mobile-access/lan/setup').then(renderLanSetup, () => {\n lanSetupStatus.textContent = t('lanSetupFailed')\n }).finally(() => { lanSetupRefresh.disabled = false })\n }\n lanSetupRefresh.addEventListener('click', () => { loadLanSetup() })\n lanSetupConfigure.addEventListener('click', () => {\n if (lanSetupSelect.value === '') return\n lanSetupConfigure.disabled = true\n lanSetupRefresh.disabled = true\n lanSetupConfigure.textContent = t('lanSetupConfiguring')\n lanSetupStatus.textContent = t('lanSetupConfiguring')\n void controlRequestJson('/api/mobile-access/lan/setup', {\n method: 'POST',\n body: JSON.stringify({ confirm: true, address: lanSetupSelect.value, configureFirewall: true }),\n }, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(renderLanSetup, () => {\n lanSetupStatus.textContent = t('lanSetupFailed')\n }).finally(() => {\n lanSetupConfigure.textContent = t('lanSetupConfigure')\n lanSetupRefresh.disabled = false\n lanSetupConfigure.disabled = lanRestartRequired || lanSetupSelect.options.length === 0\n })\n })\n const showQr = (svg: string, target: HTMLDivElement = qrBox): void => {\n target.replaceChildren()\n if (svg === '') { target.hidden = true; return }\n const image = element('img')\n image.alt = t('pairingQr')\n image.width = 176\n image.height = 176\n image.src = `data:image/svg+xml;base64,${btoa(svg)}`\n target.hidden = false\n target.append(image)\n }\n const openPairing = (target: 'key' | 'link'): void => {\n void controlRequestJson('/api/mobile-access/lan/pairing/open', { method: 'POST', body: '{}' }).then(async data => {\n const value = target === 'key'\n ? (typeof data.appKey === 'string' ? data.appKey : '')\n : (typeof data.pairUrl === 'string' ? data.pairUrl : '')\n showQr(typeof data.qrSvg === 'string' ? data.qrSvg : '')\n if (value === '') { status.textContent = t('keyGenerationFailed'); return }\n try {\n await navigator.clipboard.writeText(value)\n status.textContent = target === 'key'\n ? t('keyCopied')\n : t('linkCopied')\n } catch {\n status.textContent = t('copySecret', { kind: target === 'key' ? t('pairingKey') : t('pairingLink'), value })\n status.classList.add('is-key')\n }\n }, error => { status.textContent = t('requestFailed', { error: String(error) }) }).finally(() => {\n pair.disabled = !running\n linkPair.disabled = !running\n })\n }\n toggle.addEventListener('click', () => { toggle.disabled = true; void controlRequestJson('/api/mobile-access/lan/control', { method: 'POST', body: JSON.stringify({ running: !running }) }).then(render, error => { status.textContent = t('requestFailed', { error: String(error) }) }).finally(() => { toggle.disabled = false }) })\n const formatTime = (ms: unknown): string => typeof ms === 'number' ? new Date(ms).toLocaleString(localeTag) : ''\n const formatMegabytes = (bytes: number): string => new Intl.NumberFormat(localeTag, { minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(bytes / 1024 / 1024)\n const renderDevices = (data: Record<string, unknown>): void => {\n const devices = Array.isArray(data.devices) ? data.devices as Record<string, unknown>[] : []\n devicePanel.replaceChildren()\n if (devices.length === 0) {\n const empty = element('p', 'dsh-mobile-control__device-empty'); empty.textContent = t('noDevices')\n devicePanel.append(empty)\n return\n }\n for (const device of devices) {\n const row = element('div', 'dsh-mobile-control__device')\n const label = element('span', 'dsh-mobile-control__device-label')\n label.textContent = typeof device.label === 'string' ? device.label : t('device')\n const meta = element('span', 'dsh-mobile-control__device-meta'); meta.textContent = t('expires', { time: formatTime(device.expiresAt) })\n const revoke = element('button', 'dsh-mobile-control__device-revoke'); revoke.type = 'button'; revoke.textContent = t('revoke')\n const id = typeof device.id === 'string' ? device.id : ''\n revoke.addEventListener('click', () => {\n void controlRequestJson('/api/mobile-access/lan/devices/revoke', { method: 'POST', body: JSON.stringify({ deviceId: id }) })\n .then(loadDevices, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n })\n row.append(label, meta, revoke)\n devicePanel.append(row)\n }\n }\n const loadDevices = (): void => {\n void controlRequestJson('/api/mobile-access/lan/devices').then(renderDevices, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n }\n manageDevices.addEventListener('click', () => {\n const show = devicePanel.hidden\n devicePanel.hidden = !show\n if (show) loadDevices()\n })\n resetAll.addEventListener('click', () => {\n if (!window.confirm(t('confirmResetDevices'))) return\n void controlRequestJson('/api/mobile-access/lan/devices/reset', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(loadDevices, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n })\n const renderRemote = (data: Record<string, unknown>): void => {\n latestRemoteState = data\n remoteRunning = data.running === true\n remoteProvider = parseMobileRemoteProvider(data.provider)\n const cpolar = remoteProvider === 'cpolar'\n const cloudflared = remoteProvider === 'cloudflared'\n const frp = remoteProvider === 'frp'\n const origin = remoteProvider === 'origin'\n const tailscale = remoteProvider === 'tailscale'\n tailscaleChoice.classList.toggle('is-selected', tailscale)\n cpolarChoice.classList.toggle('is-selected', cpolar)\n cloudflaredChoice.classList.toggle('is-selected', cloudflared)\n frpChoice.classList.toggle('is-selected', frp)\n originChoice.classList.toggle('is-selected', origin)\n tailscaleChoice.setAttribute('aria-checked', String(tailscale))\n cpolarChoice.setAttribute('aria-checked', String(cpolar))\n cloudflaredChoice.setAttribute('aria-checked', String(cloudflared))\n // One tab stop for the group: only the checked card is reachable with Tab.\n tailscaleChoice.tabIndex = tailscale ? 0 : -1\n cpolarChoice.tabIndex = cpolar ? 0 : -1\n cloudflaredChoice.tabIndex = cloudflared ? 0 : -1\n frpChoice.setAttribute('aria-pressed', String(frp))\n originChoice.setAttribute('aria-pressed', String(origin))\n providerSetupName.textContent = cpolar ? 'cpolar' : cloudflared ? 'cloudflared' : frp ? t('frpName') : origin ? t('originName') : 'Tailscale Funnel'\n tailscaleChoice.disabled = remoteProviderBusy\n cpolarChoice.disabled = remoteProviderBusy\n cloudflaredChoice.disabled = remoteProviderBusy\n frpChoice.disabled = remoteProviderBusy\n originChoice.disabled = remoteProviderBusy\n cpolarSetup.hidden = !cpolar\n cloudflaredSetup.hidden = !cloudflared\n frpSetup.hidden = !frp\n originSetup.hidden = !origin\n if (frp || origin) selfHosted.open = true\n tailscaleInfo.hidden = !tailscale\n remoteReset.textContent = tailscale ? t('resetRemoteLogin') : t('resetRemoteDevices')\n const providers = data.providers !== null && typeof data.providers === 'object' ? data.providers as Record<string, unknown> : {}\n const cpolarProvider = providers.cpolar !== null && typeof providers.cpolar === 'object' ? providers.cpolar as Record<string, unknown> : {}\n const component = cpolarProvider.component !== null && typeof cpolarProvider.component === 'object'\n ? cpolarProvider.component as Record<string, unknown>\n : {}\n cpolarInstalled = component.installed === true\n cpolarConfigured = component.configured === true\n cpolarChoiceBadge.textContent = cpolarConfigured ? t('ready') : cpolarInstalled ? t('installed') : t('mainlandPreferred')\n const cpolarSupported = component.supported !== false\n const componentVersion = typeof component.version === 'string' ? component.version : ''\n const componentDownloadBytes = typeof component.downloadBytes === 'number' ? component.downloadBytes : 0\n const componentStorage = typeof component.storagePath === 'string' ? component.storagePath : `DSH Mobile ${t('pluginPrivateDirectory')}`\n cpolarStorage.textContent = componentStorage\n cpolarStorage.title = componentStorage\n cpolarInstall.hidden = cpolarInstalled || !cpolarSupported\n cpolarInstall.textContent = componentDownloadBytes > 0\n ? t('installWithSize', { size: formatMegabytes(componentDownloadBytes) })\n : t('installOfficial')\n cpolarInstall.disabled = remoteProviderBusy\n cpolarAccount.hidden = !cpolarInstalled || cpolarConfigured\n cpolarConfigure.disabled = remoteProviderBusy\n cpolarPurge.hidden = !cpolarInstalled && !cpolarConfigured\n cpolarComponentStatus.textContent = !cpolarSupported\n ? t('cpolarUnsupported')\n : !cpolarInstalled\n ? t('cpolarNotInstalled')\n : !cpolarConfigured\n ? t('cpolarNeedsToken', { version: componentVersion })\n : t('cpolarReady', { version: componentVersion })\n const cloudflaredProvider = providers.cloudflared !== null && typeof providers.cloudflared === 'object' ? providers.cloudflared as Record<string, unknown> : {}\n const cloudflaredComponent = cloudflaredProvider.component !== null && typeof cloudflaredProvider.component === 'object'\n ? cloudflaredProvider.component as Record<string, unknown>\n : {}\n cloudflaredInstalled = cloudflaredComponent.installed === true\n const cloudflaredSupported = cloudflaredComponent.supported !== false\n const cloudflaredComponentError = typeof cloudflaredComponent.errorCode === 'string' ? cloudflaredComponent.errorCode : ''\n const cloudflaredVersion = typeof cloudflaredComponent.version === 'string' ? cloudflaredComponent.version : ''\n const cloudflaredDownloadBytes = typeof cloudflaredComponent.downloadBytes === 'number' ? cloudflaredComponent.downloadBytes : 0\n if (cloudflaredDownloadBytes > 0) cloudflaredDownloadSize = formatMegabytes(cloudflaredDownloadBytes)\n const cloudflaredStoragePath = typeof cloudflaredComponent.storagePath === 'string'\n ? cloudflaredComponent.storagePath\n : `DSH Mobile ${t('pluginPrivateDirectory')}`\n cloudflaredStorage.textContent = cloudflaredStoragePath\n cloudflaredStorage.title = cloudflaredStoragePath\n cloudflaredChoiceBadge.textContent = cloudflaredInstalled ? t('ready') : t('cloudflaredBadge')\n // A quick tunnel needs no account, so installation is the only precondition.\n cloudflaredInstall.hidden = cloudflaredInstalled || !cloudflaredSupported\n cloudflaredInstall.textContent = cloudflaredDownloadBytes > 0\n ? t('installWithSize', { size: formatMegabytes(cloudflaredDownloadBytes) })\n : t('installOfficial')\n cloudflaredInstall.disabled = remoteProviderBusy\n cloudflaredPurge.hidden = !cloudflaredInstalled\n cloudflaredComponentStatus.textContent = !cloudflaredSupported\n ? t('cloudflaredUnsupported')\n : !cloudflaredInstalled\n // A component that exists but failed verification must not be reported as\n // absent: that sends the user to reinstall something already installed.\n ? (cloudflaredComponentError === 'cloudflared_component_invalid' ? t('cloudflaredInvalid') : t('cloudflaredNotInstalled'))\n : t('cloudflaredReady', { version: cloudflaredVersion })\n const cloudflaredTunnelConfiguration = cloudflaredProvider.configuration !== null\n && typeof cloudflaredProvider.configuration === 'object'\n ? cloudflaredProvider.configuration as Record<string, unknown>\n : {}\n cloudflaredTunnelMode = cloudflaredTunnelConfiguration.mode === 'named' ? 'named' : 'quick'\n cloudflaredTunnelConfigured = cloudflaredTunnelConfiguration.configured === true\n configuredCloudflaredHostname = typeof cloudflaredTunnelConfiguration.hostname === 'string' ? cloudflaredTunnelConfiguration.hostname : ''\n configuredCloudflaredPort = typeof cloudflaredTunnelConfiguration.port === 'number' ? cloudflaredTunnelConfiguration.port : 3444\n const cloudflaredTunnelError = typeof cloudflaredTunnelConfiguration.errorCode === 'string' ? cloudflaredTunnelConfiguration.errorCode : ''\n // A staged choice survives polls until the saved configuration agrees with it.\n if (cloudflaredModeDraft !== null && cloudflaredModeDraft === cloudflaredTunnelMode) cloudflaredModeDraft = null\n const cloudflaredShownMode = cloudflaredModeDraft ?? cloudflaredTunnelMode\n cloudflaredModeQuick.classList.toggle('is-active', cloudflaredShownMode === 'quick')\n cloudflaredModeNamed.classList.toggle('is-active', cloudflaredShownMode === 'named')\n cloudflaredModeQuick.setAttribute('aria-pressed', String(cloudflaredShownMode === 'quick'))\n cloudflaredModeNamed.setAttribute('aria-pressed', String(cloudflaredShownMode === 'named'))\n cloudflaredModeQuick.disabled = remoteProviderBusy\n cloudflaredModeNamed.disabled = remoteProviderBusy\n cloudflaredModeHint.textContent = cloudflaredShownMode === 'named' ? t('cloudflaredTunnelNamedHint') : t('cloudflaredTunnelQuickHint')\n // The component note describes whichever tunnel is about to run, so a\n // quick-tunnel warning never sits next to named-tunnel settings.\n cloudflaredNote.textContent = cloudflaredShownMode === 'named' ? t('cloudflaredNamedNote') : t('cloudflaredQuickNote')\n cloudflaredTunnel.hidden = cloudflaredShownMode !== 'named'\n cloudflaredTunnelSave.textContent = cloudflaredSaveLabel()\n // Untouched fields always mirror the saved configuration, so a field can never\n // keep showing a value that is no longer in effect.\n if (!cloudflaredHostnameDirty) cloudflaredHostname.value = configuredCloudflaredHostname\n if (!cloudflaredPortDirty) cloudflaredPort.value = String(configuredCloudflaredPort)\n cloudflaredToken.placeholder = cloudflaredTunnelConfigured ? t('cloudflaredTunnelTokenSaved') : t('cloudflaredTunnelTokenPlaceholder')\n // The hint must describe the port Cloudflare will actually be pointed at, which\n // is the one in the field, not the last saved value.\n const cloudflaredFieldPort = String(cloudflaredPort.value ?? '').trim()\n cloudflaredTunnelHint.textContent = t('cloudflaredTunnelHint', {\n port: cloudflaredFieldPort === '' ? String(configuredCloudflaredPort) : cloudflaredFieldPort,\n })\n cloudflaredTunnelStatus.textContent = cloudflaredTunnelError !== ''\n ? t('cloudflaredTunnelInvalid')\n : cloudflaredTunnelConfigured\n ? t('cloudflaredTunnelConfigured', { hostname: configuredCloudflaredHostname, port: String(configuredCloudflaredPort) })\n : t('cloudflaredTunnelMissing')\n cloudflaredTunnelSave.disabled = remoteProviderBusy || !cloudflaredInstalled\n cloudflaredTunnelForget.hidden = !cloudflaredTunnelConfigured\n const frpProvider = providers.frp !== null && typeof providers.frp === 'object' ? providers.frp as Record<string, unknown> : {}\n const frpComponent = frpProvider.component !== null && typeof frpProvider.component === 'object'\n ? frpProvider.component as Record<string, unknown>\n : {}\n const frpConfiguration = frpProvider.configuration !== null && typeof frpProvider.configuration === 'object'\n ? frpProvider.configuration as Record<string, unknown>\n : {}\n frpInstalled = frpComponent.installed === true\n frpConfigured = frpConfiguration.configured === true\n const frpSupported = frpComponent.supported !== false\n const frpVersion = typeof frpComponent.version === 'string' ? frpComponent.version : ''\n const frpDownloadBytes = typeof frpComponent.downloadBytes === 'number' ? frpComponent.downloadBytes : 0\n if (frpDownloadBytes > 0) frpDownloadSize = formatMegabytes(frpDownloadBytes)\n const frpStoragePath = typeof frpComponent.storagePath === 'string' ? frpComponent.storagePath : `DSH Mobile ${t('pluginPrivateDirectory')}`\n configuredFrpServer = typeof frpConfiguration.serverAddress === 'string' ? frpConfiguration.serverAddress : ''\n configuredFrpPort = typeof frpConfiguration.serverPort === 'number' ? frpConfiguration.serverPort : 7000\n configuredFrpOrigin = typeof frpConfiguration.publicOrigin === 'string' ? frpConfiguration.publicOrigin : ''\n if (frpServer.value === '' && configuredFrpServer !== '') frpServer.value = configuredFrpServer\n if ((frpPort.value === '' || frpPort.value === '7000') && configuredFrpPort !== 7000) frpPort.value = String(configuredFrpPort)\n if (frpOrigin.value === '' && configuredFrpOrigin !== '') frpOrigin.value = configuredFrpOrigin\n frpStorage.textContent = frpStoragePath\n frpStorage.title = frpStoragePath\n // Provisioning mode and entry certificate hydrate from the saved status only\n // while the form is untouched, exactly like the other saved fields.\n const configuredFrpMode = frpConfiguration.mode === 'attach' ? 'attach' : 'deploy'\n const configuredFrpEntryTls = frpConfiguration.entryTls === 'self-signed' ? 'self-signed' : 'public-ip-cert'\n if (frpConfigured) {\n frpConfiguredMode = configuredFrpMode\n frpConfiguredEntryTls = configuredFrpEntryTls\n }\n if (frpConfigured && !frpModeDraft) frpMode.value = configuredFrpMode\n if (frpConfigured && !frpEntryTlsDraft) frpEntryTls.value = configuredFrpEntryTls\n if (typeof frpConfiguration.vhostHttpPort === 'number') configuredFrpVhostPort = frpConfiguration.vhostHttpPort\n if (typeof frpConfiguration.publicPort === 'number') configuredFrpPublicPort = frpConfiguration.publicPort\n if (!frpVhostDraft) {\n if (frpMode.value === 'attach' && configuredFrpMode === 'attach' && configuredFrpEntryTls === 'public-ip-cert') {\n frpVhostPort.value = String(configuredFrpVhostPort)\n frpAttachVhostExplicit = true\n } else {\n frpVhostPort.value = frpMode.value === 'deploy' ? String(configuredFrpVhostPort) : ''\n }\n }\n if (frpPublicPort.value === '' || frpPublicPort.value === '33080') frpPublicPort.value = String(configuredFrpPublicPort)\n // Attach mode never installs or deploys anything on the VPS: the runbook is\n // the whole deliverable, so the SSH/deploy controls disappear.\n const attachSelected = frpMode.value === 'attach'\n const selfSignedSelected = attachSelected && frpEntryTls.value === 'self-signed'\n frpStep2Title.textContent = t(attachSelected ? 'frpStep2TitleAttach' : 'frpStep2Title')\n frpStep2Text.textContent = t(attachSelected ? 'frpStep2TextAttach' : 'frpStep2Text')\n frpStep4Text.textContent = t(attachSelected ? 'frpStep4TextAttach' : 'frpStep4Text')\n frpAppRequirement.textContent = t(selfSignedSelected ? 'frpAppRequirementSelfSigned' : 'frpAppRequirement')\n frpCopyTemplate.hidden = attachSelected\n frpCopyAttachPlan.hidden = !attachSelected\n frpCopyAttachToken.hidden = !attachSelected\n frpSelfCheckButton.hidden = !frpConfigured\n frpVhostPort.disabled = selfSignedSelected\n frpVhostLabel.hidden = selfSignedSelected\n frpVhostPort.required = attachSelected && !selfSignedSelected\n frpVhostHint.textContent = frpVhostPort.required ? t('frpAttachModeRequiresVhostPort') : t('frpVhostHttpPortHint')\n frpEntryTls.disabled = !attachSelected\n frpEntryTlsLabel.hidden = !attachSelected\n frpPublicPortLabel.hidden = !selfSignedSelected\n vpsDeployText.hidden = attachSelected\n vpsChangesTitle.hidden = attachSelected\n vpsChanges.hidden = attachSelected\n vpsDeployFields.hidden = attachSelected\n vpsDeploy.hidden = attachSelected\n vpsDeployStatus.hidden = attachSelected\n vpsCopyUninstall.hidden = attachSelected\n vpsUninstall.hidden = attachSelected\n frpInstall.hidden = frpInstalled || !frpSupported\n frpInstall.textContent = frpDownloadBytes > 0\n ? t('installWithSize', { size: formatMegabytes(frpDownloadBytes) })\n : t('installFrpc')\n frpInstall.disabled = remoteProviderBusy\n frpConfigure.disabled = remoteProviderBusy || !frpInstalled\n vpsDeploy.disabled = remoteProviderBusy || !validVpsDeploymentForm()\n vpsCopyUninstall.disabled = vpsDeploy.disabled\n vpsUninstall.disabled = vpsDeploy.disabled\n frpPurge.hidden = !frpInstalled && !frpConfigured\n frpComponentStatus.textContent = !frpSupported\n ? t('frpUnsupported')\n : frpInstalled ? t('frpComponentReady', { version: frpVersion }) : t('frpNotInstalled')\n frpConfigurationStatus.textContent = frpConfigured\n ? t('frpConfigurationReady') : t(attachSelected ? 'frpConfigurationMissingAttach' : 'frpConfigurationMissing')\n frpOverview.hidden = !frpConfigured\n frpOverviewEndpoint.textContent = configuredFrpOrigin === ''\n ? configuredFrpServer\n : `${configuredFrpOrigin} · ${configuredFrpServer}:${String(configuredFrpPort)}`\n frpToken.placeholder = frpConfigured\n ? (locale === 'zh' ? '已安全保存;留空保持不变' : locale === 'it' ? 'Salvato; lascia vuoto per mantenerlo' : 'Saved securely; leave blank to keep it')\n : t('frpTokenPlaceholder')\n vpsDeploy.textContent = frpConfigured\n ? (locale === 'zh' ? '修复或重新部署 VPS' : locale === 'it' ? 'Ripara o ridistribuisci VPS' : 'Repair or redeploy VPS')\n : t('vpsDeploy')\n frpConnectionSummary.textContent = `${frpConfigured ? '✓ ' : ''}${t('frpStep1Title')}`\n frpComponentSummary.textContent = `${frpInstalled ? '✓ ' : ''}${t('frpStep3Title')}`\n if (!frpLayoutInitialized) {\n frpConnectionGroup.open = !frpConfigured\n frpComponentGroup.open = !frpInstalled\n frpVpsGroup.open = !frpConfigured\n frpLayoutInitialized = true\n }\n const originProvider = providers.origin !== null && typeof providers.origin === 'object' ? providers.origin as Record<string, unknown> : {}\n const originConfiguration = originProvider.configuration !== null && typeof originProvider.configuration === 'object'\n ? originProvider.configuration as Record<string, unknown> : {}\n originConfigured = originConfiguration.configured === true\n if (!originFormDirty && !originFormBusy) {\n originPublic.value = typeof originConfiguration.publicOrigin === 'string' ? originConfiguration.publicOrigin : ''\n originHost.value = typeof originConfiguration.listenHost === 'string' ? originConfiguration.listenHost : '127.0.0.1'\n originPort.value = typeof originConfiguration.listenPort === 'number' ? String(originConfiguration.listenPort) : '3444'\n originCidrs.value = Array.isArray(originConfiguration.allowedCidrs) ? originConfiguration.allowedCidrs.filter(value => typeof value === 'string').join(', ') : '127.0.0.0/8'\n }\n originSetup.setAttribute('aria-busy', String(originFormBusy))\n originConfigure.setAttribute('aria-busy', String(originFormBusy))\n originConfigure.textContent = originFormBusy ? t('saving') : t('originSaveStart')\n for (const input of [originPublic, originHost, originPort, originCidrs]) input.disabled = remoteProviderBusy\n originConfigure.disabled = remoteProviderBusy\n originPurge.disabled = remoteProviderBusy\n originPurge.hidden = !originConfigured && originConfiguration.errorCode !== 'origin_config_invalid' && originProvider.running !== true\n const originListening = origin && originProvider.state === 'ready' && originProvider.running === true\n const backendOrigin = originListening && typeof data.backendOrigin === 'string' ? data.backendOrigin\n : typeof originConfiguration.backendOrigin === 'string' ? originConfiguration.backendOrigin : ''\n originBackend.hidden = backendOrigin === ''\n originBackendLabel.textContent = originListening ? t('originBackendListening') : t('originBackendSaved')\n originBackendAddress.textContent = backendOrigin\n originCopyBackend.disabled = remoteProviderBusy || backendOrigin === ''\n selfHostedBadge.textContent = origin\n ? originListening ? t('originBackendReady') : t('advanced')\n : frpConfigured && frpInstalled ? t('ready') : t('advanced')\n const state = typeof data.state === 'string' ? data.state : 'error'\n const errorCode = typeof data.errorCode === 'string' ? data.errorCode : ''\n const remoteOrigin = typeof data.origin === 'string' ? data.origin : ''\n remoteLoginUrl = typeof data.loginUrl === 'string' ? data.loginUrl : ''\n const candidateSetupUrl = tailscale ? officialFunnelSetupUrl(data.setupUrl) : ''\n const fallbackSetupUrls: Record<string, string> = {\n funnel_permission_required: 'https://tailscale.com/s/no-funnel',\n funnel_https_required: 'https://tailscale.com/s/https',\n funnel_start_failed: 'https://tailscale.com/s/no-funnel',\n }\n remoteSetupUrl = candidateSetupUrl !== '' ? candidateSetupUrl : (fallbackSetupUrls[errorCode] ?? '')\n const needsFunnelSetup = state === 'error' && remoteSetupUrl !== ''\n remoteReady = remoteRunning && state === 'ready' && remoteOrigin !== ''\n remoteStateBadge.classList.toggle('is-ready', remoteReady)\n remoteStateBadge.classList.toggle('is-busy', state === 'starting' || state === 'connecting' || state === 'needs-login')\n remoteStateBadge.classList.toggle('is-attention', state === 'error' || state === 'unavailable')\n remoteStateBadge.textContent = remoteReady\n ? origin ? t('originBackendReady') : t('ready')\n : state === 'starting' || state === 'connecting' || state === 'needs-login'\n ? t('remoteStateConnecting')\n : state === 'error' || state === 'unavailable'\n ? t('remoteStateAttention')\n : t('remoteStateOff')\n remoteAccess.hidden = !remoteReady\n remoteAccessLink.href = remoteOrigin\n remoteAccessLink.textContent = remoteOrigin\n remoteAccessLink.title = remoteOrigin\n remoteStatus.classList.toggle('is-running', remoteReady)\n const labels: Record<string, string> = {\n off: t('remoteOff'),\n unavailable: cpolar ? t('remoteUnavailableCpolar')\n : cloudflared ? t('remoteUnavailableCloudflared')\n : frp ? t('remoteUnavailableFrp') : origin ? t('originUnavailable') : t('remoteUnavailableTailscale'),\n starting: cpolar ? t('remoteStartingCpolar')\n : cloudflared ? t('remoteStartingCloudflared')\n : frp ? t('remoteStartingFrp') : origin ? t('originStarting') : t('remoteStartingTailscale'),\n 'needs-login': t('remoteNeedsLogin'),\n connecting: cpolar ? t('remoteConnectingCpolar')\n : cloudflared ? t('remoteConnectingCloudflared')\n : frp ? t('remoteConnectingFrp') : t('remoteConnectingTailscale'),\n ready: origin ? t('originReady') : t('remoteReady'),\n error: t('remoteError'),\n }\n const errorLabels: Record<string, string> = translatedRemoteErrors()\n remoteStatus.textContent = remoteSetupPending && needsFunnelSetup\n ? t('setupOpened')\n : (state === 'error' ? (errorLabels[errorCode] ?? labels.error!) : (labels[state] ?? labels.error!))\n // Keep a just-failed action's message for this one repaint, then release it.\n if (remoteFailureText !== undefined) {\n remoteStatus.textContent = remoteFailureText\n remoteFailureText = undefined\n }\n remoteGuide.hidden = !needsFunnelSetup\n remoteSetup.disabled = remoteSetupUrl === '' || remoteReconnectBusy\n remoteSetupRetry.disabled = remoteReconnectBusy\n remoteToggle.textContent = remoteRunning ? t('disableRemote') : t('enableRemote')\n const providerPrepared = cpolar ? cpolarInstalled && cpolarConfigured\n : cloudflared ? cloudflaredInstalled\n : frp ? frpInstalled && frpConfigured : origin ? originConfigured : true\n remoteToggle.disabled = remoteProviderBusy || (!providerPrepared && !(origin && remoteRunning))\n remoteLogin.hidden = !tailscale || state !== 'needs-login' || remoteLoginUrl === ''\n remoteReconnect.hidden = needsFunnelSetup || (state !== 'error' && state !== 'unavailable')\n || !providerPrepared\n remoteActions.hidden = !providerPrepared && !(origin && remoteRunning)\n remotePair.disabled = !remoteReady || originFormBusy\n remoteCopyLink.disabled = !remoteReady || originFormBusy\n remoteDevices.disabled = !remoteReady || originFormBusy\n if (origin) { remoteReconnect.disabled = remoteProviderBusy || remoteReconnectBusy; remoteReset.disabled = remoteProviderBusy }\n else remoteReset.disabled = false\n if (!remoteReady) {\n remoteQr.hidden = true\n remotePairLink.hidden = true\n activeRemotePairUrl = ''\n activeRemotePairExpiresAt = 0\n }\n if (!needsFunnelSetup) remoteSetupPending = false\n }\n frpMode.addEventListener('change', () => {\n frpModeDraft = true\n if (frpMode.value === 'attach') {\n if (!frpAttachVhostExplicit) {\n frpVhostPort.value = ''\n frpVhostDraft = true\n }\n } else {\n frpEntryTls.value = 'public-ip-cert'\n frpEntryTlsDraft = true\n if (frpVhostPort.value === '') frpVhostPort.value = String(configuredFrpVhostPort)\n frpVhostDraft = true\n }\n frpVhostPort.removeAttribute('aria-invalid')\n renderRemote(latestRemoteState)\n })\n frpEntryTls.addEventListener('change', () => {\n frpEntryTlsDraft = true\n if (frpMode.value === 'attach' && frpEntryTls.value === 'public-ip-cert' && !frpAttachVhostExplicit) {\n frpVhostPort.value = ''\n frpVhostDraft = true\n }\n frpVhostPort.removeAttribute('aria-invalid')\n renderRemote(latestRemoteState)\n })\n frpVhostPort.addEventListener('input', () => {\n frpVhostDraft = true\n if (frpMode.value === 'attach') frpAttachVhostExplicit = frpVhostPort.value.trim() !== ''\n frpVhostPort.removeAttribute('aria-invalid')\n })\n let remoteLoadInFlight = false\n const loadRemote = (): void => {\n if (remoteLoadInFlight || originFormBusy) return\n const epoch = remoteSnapshotEpoch\n remoteLoadInFlight = true\n void controlRequestJson('/api/mobile-access/remote/control')\n .then(data => { if (epoch === remoteSnapshotEpoch) renderRemote(data) }, error => {\n if (epoch === remoteSnapshotEpoch) remoteStatus.textContent = t('requestFailed', { error: String(error) })\n })\n .finally(() => { remoteLoadInFlight = false })\n }\n const chooseRemoteProvider = (provider: MobileRemoteProvider): void => {\n if (remoteProviderBusy || provider === remoteProvider) return\n if (remoteRunning && !window.confirm(t('switchProviderConfirm'))) return\n remoteProviderBusy = true\n tailscaleChoice.disabled = true\n cpolarChoice.disabled = true\n cloudflaredChoice.disabled = true\n frpChoice.disabled = true\n originChoice.disabled = true\n remoteStatus.textContent = provider === 'cpolar' ? t('switchingCpolar')\n : provider === 'cloudflared' ? t('switchingCloudflared')\n : provider === 'frp' ? t('switchingFrp') : provider === 'origin' ? t('switchingOrigin') : t('switchingTailscale')\n void controlRequestJson('/api/mobile-access/remote/provider', { method: 'POST', body: JSON.stringify({ provider }) })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n }\n tailscaleChoice.addEventListener('click', () => { chooseRemoteProvider('tailscale') })\n cpolarChoice.addEventListener('click', () => { chooseRemoteProvider('cpolar') })\n cloudflaredChoice.addEventListener('click', () => { chooseRemoteProvider('cloudflared') })\n frpChoice.addEventListener('click', () => { chooseRemoteProvider('frp') })\n originChoice.addEventListener('click', () => { chooseRemoteProvider('origin') })\n cloudflaredInstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const accepted = window.confirm(t('installConfirmCloudflared', { size: cloudflaredDownloadSize }))\n if (!accepted) return\n remoteProviderBusy = true\n cloudflaredInstall.disabled = true\n cloudflaredInstall.textContent = t('downloading')\n remoteStatus.textContent = t('installingCloudflared')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/component/install', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'installFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n cloudflaredPurge.addEventListener('click', () => {\n if (remoteProviderBusy) return\n if (!window.confirm(t('purgeCloudflaredConfirm'))) return\n remoteProviderBusy = true\n cloudflaredPurge.disabled = true\n remoteStatus.textContent = t('purgingCloudflared')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/component/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = t('purgeFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cloudflaredPurge.disabled = false; loadRemote() })\n })\n // Tunnel type is staged locally and only reaches the provider through Save, so\n // clicking between the two never silently discards a saved token.\n const showCloudflaredMode = (mode: 'quick' | 'named'): void => {\n cloudflaredModeDraft = mode\n cloudflaredModeQuick.classList.toggle('is-active', mode === 'quick')\n cloudflaredModeNamed.classList.toggle('is-active', mode === 'named')\n cloudflaredModeQuick.setAttribute('aria-pressed', String(mode === 'quick'))\n cloudflaredModeNamed.setAttribute('aria-pressed', String(mode === 'named'))\n cloudflaredModeHint.textContent = mode === 'named' ? t('cloudflaredTunnelNamedHint') : t('cloudflaredTunnelQuickHint')\n // The component note follows the same choice immediately. Waiting for the next poll\n // would leave a quick-tunnel warning sitting beside named-tunnel settings.\n cloudflaredNote.textContent = mode === 'named' ? t('cloudflaredNamedNote') : t('cloudflaredQuickNote')\n cloudflaredTunnel.hidden = mode !== 'named'\n cloudflaredTunnelSave.textContent = cloudflaredSaveLabel()\n }\n cloudflaredModeQuick.addEventListener('click', () => { if (!remoteProviderBusy) showCloudflaredMode('quick') })\n cloudflaredModeNamed.addEventListener('click', () => { if (!remoteProviderBusy) showCloudflaredMode('named') })\n cloudflaredHostname.addEventListener('input', () => { cloudflaredHostnameDirty = true })\n cloudflaredPort.addEventListener('input', () => { cloudflaredPortDirty = true })\n cloudflaredTunnelSave.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const mode = cloudflaredModeDraft ?? cloudflaredTunnelMode\n const hostname = String(cloudflaredHostname.value ?? '').trim()\n const portValue = String(cloudflaredPort.value ?? '').trim()\n const port = Number(portValue)\n const token = String(cloudflaredToken.value ?? '')\n // The provider re-validates everything; these checks only keep an obviously\n // incomplete form from costing a round trip.\n if (mode === 'named') {\n if (hostname === '') { remoteStatus.textContent = t('cloudflaredTunnelHostnameInvalid'); return }\n if (portValue === '' || !Number.isSafeInteger(port) || port < 1024 || port > 65535) {\n remoteStatus.textContent = t('cloudflaredTunnelPortInvalid')\n return\n }\n // The LAN gateway owns 3443 for as long as DSH runs, so this is not a\n // transient conflict worth a round trip.\n if (port === 3443) { remoteStatus.textContent = t('cloudflaredTunnelPortReserved'); return }\n if (token === '' && !cloudflaredTunnelConfigured) { remoteStatus.textContent = t('cloudflaredTunnelConfigMissing'); return }\n if (token !== '' && (token.length < 32 || !/^[A-Za-z0-9_=+/-]+$/u.test(token))) {\n remoteStatus.textContent = t('cloudflaredTunnelTokenInvalid')\n return\n }\n }\n remoteProviderBusy = true\n cloudflaredTunnelSave.disabled = true\n cloudflaredTunnelSave.textContent = t('saving')\n remoteStatus.textContent = mode === 'named' ? t('cloudflaredTunnelSaving') : t('switchingCloudflared')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/tunnel', {\n method: 'POST',\n body: JSON.stringify(mode === 'named' ? { mode, hostname, port, token } : { mode: 'quick' }),\n })\n .then(data => {\n // The token is write-only: never keep a copy in the DOM after saving.\n cloudflaredToken.value = ''\n cloudflaredModeDraft = null\n // The saved values are now authoritative, so let the next poll restore them.\n cloudflaredHostnameDirty = false\n cloudflaredPortDirty = false\n remoteFailureText = ''\n renderRemote(data)\n }, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => {\n remoteProviderBusy = false\n cloudflaredTunnelSave.disabled = false\n cloudflaredTunnelSave.textContent = cloudflaredSaveLabel()\n loadRemote()\n })\n })\n cloudflaredTunnelForget.addEventListener('click', () => {\n if (remoteProviderBusy) return\n if (!window.confirm(t('cloudflaredTunnelForgetConfirm'))) return\n remoteProviderBusy = true\n cloudflaredTunnelForget.disabled = true\n remoteStatus.textContent = t('cloudflaredTunnelForgetting')\n void controlRequestJson('/api/mobile-access/remote/cloudflared/tunnel/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(data => {\n cloudflaredToken.value = ''\n cloudflaredHostname.value = ''\n cloudflaredPort.value = '3444'\n cloudflaredModeDraft = null\n cloudflaredHostnameDirty = false\n cloudflaredPortDirty = false\n remoteFailureText = ''\n renderRemote(data)\n }, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cloudflaredTunnelForget.disabled = false; loadRemote() })\n })\n cpolarInstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const accepted = window.confirm(t('installConfirm'))\n if (!accepted) return\n remoteProviderBusy = true\n cpolarInstall.disabled = true\n cpolarInstall.textContent = t('downloading')\n remoteStatus.textContent = t('installingCpolar')\n void controlRequestJson('/api/mobile-access/remote/cpolar/component/install', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'installFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n cpolarConfigure.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const authtoken = cpolarToken.value.trim()\n if (authtoken.length < 20 || /\\s/u.test(authtoken)) {\n remoteStatus.textContent = t('invalidToken')\n cpolarToken.focus()\n return\n }\n remoteProviderBusy = true\n cpolarConfigure.disabled = true\n cpolarConfigure.setAttribute('aria-busy', 'true')\n cpolarConfigure.textContent = t('saving')\n void controlRequestJson('/api/mobile-access/remote/cpolar/configure', { method: 'POST', body: JSON.stringify({ authtoken }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(() => {\n cpolarToken.value = ''\n remoteStatus.textContent = t('configuredConnecting')\n return controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: true }) })\n })\n .then(renderRemote, error => { remoteFailureText = t('configureFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cpolarConfigure.setAttribute('aria-busy', 'false'); cpolarConfigure.textContent = t('saveConnect'); loadRemote() })\n })\n cpolarPurge.addEventListener('click', () => {\n if (remoteProviderBusy) return\n if (!window.confirm(t('purgeConfirm'))) return\n remoteProviderBusy = true\n cpolarPurge.disabled = true\n remoteStatus.textContent = t('purging')\n void controlRequestJson('/api/mobile-access/remote/cpolar/component/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = t('purgeFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; cpolarPurge.disabled = false; loadRemote() })\n })\n const validFrpServer = (value: string): boolean => value === value.trim() && value.length > 0 && value.length <= 253\n && !/[\\s\\u0000-\\u001f\\u007f/\\\\@?#]/u.test(value)\n const frpForm = (): {\n readonly serverAddress: string\n readonly serverPort: number\n readonly token: string\n readonly publicOrigin: string\n readonly mode: 'deploy' | 'attach'\n readonly entryTls: 'public-ip-cert' | 'self-signed'\n readonly vhostHttpPort?: number\n readonly publicPort?: number\n } => {\n const vhostHttpPort = Number(frpVhostPort.value)\n const publicPort = Number(frpPublicPort.value)\n const mode = frpMode.value === 'attach' ? 'attach' as const : 'deploy' as const\n return {\n serverAddress: String(frpServer.value ?? '').trim(),\n serverPort: Number(frpPort.value),\n token: String(frpToken.value ?? ''),\n publicOrigin: String(frpOrigin.value ?? '').trim(),\n mode,\n entryTls: frpEntryTls.value === 'self-signed' ? 'self-signed' as const : 'public-ip-cert' as const,\n // A blank vhost port in attach mode must stay blank: the host refuses to\n // guess 7080 rather than silently probing the wrong port.\n ...(Number.isSafeInteger(vhostHttpPort) && vhostHttpPort >= 1 ? { vhostHttpPort } : {}),\n ...(Number.isSafeInteger(publicPort) && publicPort >= 1 ? { publicPort } : {}),\n }\n }\n /** Attach mode never installs frps; every other combination keeps the managed path. */\n const frpAttachSelected = (): boolean => frpForm().mode === 'attach'\n /**\n * Attach-mode artefacts are built locally, so the token never reaches the host\n * API and the runbook can be produced before anything is saved.\n */\n const attachClipboardForm = (form: ReturnType<typeof frpForm>): Parameters<typeof createFrpAttachTemplateForClipboard>[0] => ({\n serverAddress: form.serverAddress,\n serverPort: form.serverPort,\n token: form.token === '' && frpConfigured ? '0123456789abcdef' : form.token,\n publicOrigin: form.publicOrigin,\n entryTls: form.entryTls,\n ...(form.entryTls === 'self-signed' || form.vhostHttpPort === undefined ? {} : { vhostHttpPort: form.vhostHttpPort }),\n ...(form.publicPort === undefined ? {} : { publicPort: form.publicPort }),\n })\n const validFrpForm = (form: ReturnType<typeof frpForm>): boolean => {\n if (!validFrpServer(form.serverAddress)) return false\n try {\n createFrpServerTemplateForClipboard(form.serverPort, form.token, form.publicOrigin)\n return true\n } catch { return false }\n }\n frpCopyTemplate.addEventListener('click', () => {\n const form = frpForm()\n if (!validFrpForm(form)) {\n remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n void navigator.clipboard.writeText(createFrpServerTemplateForClipboard(form.serverPort, form.token, form.publicOrigin))\n .then(() => { remoteStatus.textContent = t('templateCopied') }, () => { remoteStatus.textContent = t('templateCopyFailed') })\n })\n const frpAttachErrorText = (code: string): string => t(REMOTE_ERROR_MESSAGE_KEYS[code] ?? 'frpInputInvalid')\n const showFrpAttachError = (code: string): void => {\n const message = frpAttachErrorText(code)\n if (code === 'frp_attach_mode_requires_vhost_port') {\n frpVhostPort.setAttribute('aria-invalid', 'true')\n frpVhostHint.textContent = message\n frpVhostPort.focus()\n }\n remoteStatus.textContent = message\n }\n frpCopyAttachPlan.addEventListener('click', () => {\n const form = frpForm()\n if (!validFrpServer(form.serverAddress) || !Number.isSafeInteger(form.serverPort)\n || form.serverPort < 1 || form.serverPort > 65_535 || form.publicOrigin === '') {\n remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n if (form.token !== '') {\n // Everything needed is local, so the token stays in the browser.\n const attachForm = attachClipboardForm(form)\n const code = frpAttachFormErrorCode(attachForm)\n if (code !== undefined) {\n showFrpAttachError(code)\n return\n }\n void navigator.clipboard.writeText(createFrpAttachTemplateForClipboard(attachForm))\n .then(() => { remoteStatus.textContent = t('frpAttachPlanCopied') },\n () => { remoteStatus.textContent = t('frpAttachPlanFailed', { error: t('templateCopyFailed') }) })\n return\n }\n if (!frpConfigured) {\n remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n // The saved token is deliberately unreadable here, so the loopback host\n // merges it and returns the same runbook text.\n remoteProviderBusy = true\n frpCopyAttachPlan.disabled = true\n void controlRequestJson('/api/mobile-access/remote/frp/attach-plan', { method: 'POST', body: JSON.stringify(form) })\n .then(data => {\n const text = typeof data.frpAttachTemplate === 'string' ? data.frpAttachTemplate : ''\n if (text === '') throw new Error('frp_settings_invalid')\n return navigator.clipboard.writeText(text).then(() => { remoteStatus.textContent = t('frpAttachPlanCopied') })\n }, error => { remoteStatus.textContent = frpAttachErrorText(String(error)); return undefined })\n .catch(error => { remoteStatus.textContent = t('frpAttachPlanFailed', { error: String(error) }) })\n .finally(() => { remoteProviderBusy = false; frpCopyAttachPlan.disabled = false })\n })\n /** Copy a token-bearing frpc.toml only from the token the user just typed. */\n frpCopyAttachToken.addEventListener('click', () => {\n const form = frpForm()\n if (!validFrpServer(form.serverAddress) || !Number.isSafeInteger(form.serverPort)\n || form.serverPort < 1 || form.serverPort > 65_535 || form.publicOrigin === '') {\n remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n if (form.token !== '') {\n // The typed token is already in this browser, so the file is built locally.\n const attachForm = attachClipboardForm(form)\n const code = frpAttachFormErrorCode(attachForm)\n if (code !== undefined) {\n showFrpAttachError(code)\n return\n }\n void navigator.clipboard.writeText(createFrpAttachFrpcTomlForClipboard(attachForm, { revealToken: true }))\n .then(() => { remoteStatus.textContent = t('frpAttachTokenCopied') },\n () => { remoteStatus.textContent = t('frpAttachTokenFailed', { error: t('templateCopyFailed') }) })\n return\n }\n frpToken.focus()\n remoteStatus.textContent = t('frpAttachTokenReenter')\n })\n const certificateStatusText = (status: Record<string, unknown> | undefined): string => {\n if (status === undefined) return ''\n const state = typeof status.state === 'string' ? status.state : 'unknown'\n const days = typeof status.daysRemaining === 'number' ? String(status.daysRemaining) : '0'\n if (state === 'ok') return t('frpAttachCertOk', { days })\n if (state === 'expiring') return t('frpAttachCertExpiring', { days })\n if (state === 'expired') return t('frpAttachCertExpired')\n return t('frpAttachCertUnknown')\n }\n const caStatusText = (status: Record<string, unknown> | undefined): string => {\n if (status === undefined) return ''\n const state = typeof status.state === 'string' ? status.state : 'unknown'\n const days = typeof status.daysRemaining === 'number' ? String(status.daysRemaining) : '0'\n if (state === 'ok') return t('frpAttachCaOk', { days })\n if (state === 'expiring') return t('frpAttachCaExpiring', { days })\n if (state === 'expired') return t('frpIngressCaExpired')\n return t('frpIngressCaInvalid')\n }\n frpSelfCheckButton.addEventListener('click', () => {\n if (remoteProviderBusy) return\n remoteProviderBusy = true\n frpSelfCheckButton.disabled = true\n frpSelfCheckStatus.hidden = false\n frpSelfCheckStatus.textContent = t('loadingStatus')\n void controlRequestJson('/api/mobile-access/remote/frp/self-check')\n .then(data => {\n const check = data.frpSelfCheck !== null && typeof data.frpSelfCheck === 'object'\n ? data.frpSelfCheck as Record<string, unknown> : {}\n const lines: string[] = []\n const caCertificate = check.caCertificate !== null && typeof check.caCertificate === 'object'\n ? check.caCertificate as Record<string, unknown> : undefined\n const caText = caStatusText(caCertificate)\n if (caText !== '') lines.push(caText)\n const certificate = check.certificate !== null && typeof check.certificate === 'object'\n ? check.certificate as Record<string, unknown> : undefined\n const certificateText = certificateStatusText(certificate)\n if (certificateText !== '') lines.push(certificateText)\n lines.push(check.frpsReachable === true ? t('frpAttachFrpsReachable') : t('frpAttachFrpsUnreachable'))\n if (check.entryTls === 'self-signed') {\n lines.push(check.entryReachable === true ? t('frpAttachEntryReachable') : t('frpAttachEntryUnreachable'))\n }\n frpSelfCheckStatus.textContent = lines.join(' · ')\n }, error => { frpSelfCheckStatus.textContent = t('requestFailed', { error: String(error) }) })\n .finally(() => { remoteProviderBusy = false; frpSelfCheckButton.disabled = false })\n })\n const validVpsSshUser = (value: string): boolean => /^[a-z_][a-z0-9_.-]*[$]?$/iu.test(value) && value.length <= 64\n const validVpsSshKey = (value: string): boolean => value === '' || (/^[a-zA-Z]:[\\\\/]/u.test(value) || value.startsWith('/'))\n const vpsFormStorageKey = 'dsh-mobile.frp-vps-form.v1'\n try {\n const saved = JSON.parse(localStorage.getItem(vpsFormStorageKey) ?? '{}') as Record<string, unknown>\n if (typeof saved.sshUser === 'string' && validVpsSshUser(saved.sshUser)) vpsSshUser.value = saved.sshUser\n if (typeof saved.sshPort === 'number' && Number.isSafeInteger(saved.sshPort) && saved.sshPort >= 1 && saved.sshPort <= 65_535) vpsSshPort.value = String(saved.sshPort)\n if (typeof saved.sshKeyPath === 'string' && validVpsSshKey(saved.sshKeyPath)) vpsSshKey.value = saved.sshKeyPath\n } catch { /* Browser storage is an optional convenience only. */ }\n const saveVpsForm = (): void => {\n try {\n localStorage.setItem(vpsFormStorageKey, JSON.stringify({\n sshUser: String(vpsSshUser.value ?? '').trim(),\n sshPort: Number(vpsSshPort.value),\n sshKeyPath: String(vpsSshKey.value ?? '').trim(),\n }))\n } catch { /* Private browsing or a full quota must not block deployment. */ }\n }\n const validVpsDeploymentForm = (): boolean => {\n const sshPort = Number(vpsSshPort.value)\n return validVpsFrpForm()\n && validVpsSshUser(String(vpsSshUser.value ?? '').trim())\n && Number.isSafeInteger(sshPort) && sshPort >= 1 && sshPort <= 65_535\n && validVpsSshKey(String(vpsSshKey.value ?? '').trim())\n }\n /**\n * VPS actions accept blank connection fields when a configuration is already\n * saved; blanks keep their saved values (\"已保存时可留空\"). The token itself\n * is never readable here, so a blank token is merged server-side while the\n * remaining fields still pass the regular template check.\n */\n const vpsEffectiveForm = (): ReturnType<typeof frpForm> => {\n const form = frpForm()\n if (!frpConfigured) return form\n return {\n serverAddress: form.serverAddress === '' ? configuredFrpServer : form.serverAddress,\n serverPort: Number.isSafeInteger(form.serverPort) && form.serverPort >= 1 ? form.serverPort : configuredFrpPort,\n token: form.token,\n publicOrigin: form.publicOrigin === '' ? configuredFrpOrigin : form.publicOrigin,\n mode: form.mode,\n entryTls: form.entryTls,\n ...(form.vhostHttpPort === undefined ? {} : { vhostHttpPort: form.vhostHttpPort }),\n ...(form.publicPort === undefined ? {} : { publicPort: form.publicPort }),\n }\n }\n const validVpsFrpForm = (): boolean => {\n const form = vpsEffectiveForm()\n const token = form.token === '' && frpConfigured ? '0123456789abcdef' : form.token\n return validFrpForm({ ...form, token })\n }\n const refreshVpsDeployButton = (): void => {\n const disabled = remoteProviderBusy || !validVpsDeploymentForm()\n vpsDeploy.disabled = disabled\n vpsCopyUninstall.disabled = disabled\n vpsUninstall.disabled = disabled\n }\n for (const input of [frpServer, frpPort, frpToken, frpOrigin, vpsSshUser, vpsSshPort, vpsSshKey]) {\n input.addEventListener('input', refreshVpsDeployButton)\n }\n for (const input of [vpsSshUser, vpsSshPort, vpsSshKey]) input.addEventListener('input', saveVpsForm)\n const vpsCertName = (origin: string): string | undefined => {\n try {\n const host = new URL(origin).hostname\n return /^\\d{1,3}(?:\\.\\d{1,3}){3}$/u.test(host) ? host : undefined\n } catch { return undefined }\n }\n const requestVpsHostKeys = (serverAddress: string, sshUser: string, sshPort: number, sshKeyPath: string): Promise<ReadonlyArray<{ readonly display: string; readonly fingerprint: string }>> => {\n const keyPayload: Record<string, unknown> = { serverAddress, sshUser, sshPort }\n if (sshKeyPath !== '') keyPayload.sshKeyPath = sshKeyPath\n return controlRequestJson('/api/mobile-access/remote/frp/vps/host-keys', { method: 'POST', body: JSON.stringify(keyPayload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(keys => {\n const hostKeys = Array.isArray(keys.vpsHostKeys) ? keys.vpsHostKeys as Array<Record<string, unknown>> : []\n const confirmed = hostKeys\n .filter(key => typeof key.fingerprint === 'string' && typeof key.keyType === 'string')\n .map(key => ({ display: `${String(key.keyType)} ${String(key.fingerprint)}`, fingerprint: String(key.fingerprint) }))\n if (confirmed.length === 0) throw new Error(String(t('vpsHostKeyFailed', { error: 'empty' })))\n return confirmed\n })\n }\n vpsDeploy.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const form = vpsEffectiveForm()\n if (!validVpsFrpForm()) {\n vpsDeployStatus.textContent = t('vpsDeployNotReady')\n return\n }\n const sshUser = vpsSshUser.value.trim()\n const sshPort = Number(vpsSshPort.value)\n const sshKeyPath = vpsSshKey.value.trim()\n saveVpsForm()\n if (!validVpsSshUser(sshUser) || !Number.isSafeInteger(sshPort) || sshPort < 1 || sshPort > 65535 || !validVpsSshKey(sshKeyPath)) {\n vpsDeployStatus.textContent = t('vpsDeployFailed', { error: t('vpsSshKey') })\n return\n }\n remoteProviderBusy = true\n vpsDeploy.disabled = true\n vpsCopyUninstall.disabled = true\n vpsUninstall.disabled = true\n vpsDeployStatus.textContent = t('vpsHostKeyFetching')\n remoteStatus.textContent = t('vpsHostKeyFetching')\n void requestVpsHostKeys(form.serverAddress, sshUser, sshPort, sshKeyPath)\n .then(hostKeys => {\n const display = hostKeys.map(key => key.display).join('\\n')\n if (!window.confirm(t('vpsDeployConfirmWithKeys', { fingerprints: display }))) {\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n return\n }\n vpsDeployStatus.textContent = t('vpsDeploying')\n remoteStatus.textContent = t('vpsDeploying')\n const payload: Record<string, unknown> = {\n confirm: true,\n ...form,\n sshUser,\n sshPort,\n hostFingerprints: hostKeys.map(key => key.fingerprint),\n }\n if (sshKeyPath !== '') payload.sshKeyPath = sshKeyPath\n return controlRequestJson('/api/mobile-access/remote/frp/vps/deploy', { method: 'POST', body: JSON.stringify(payload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(data => {\n const deployment = data.vpsDeployment !== null && typeof data.vpsDeployment === 'object' ? data.vpsDeployment as Record<string, unknown> : {}\n vpsDeployStatus.textContent = deployment.deployed === true ? t('vpsDeploySuccess') : t('vpsDeployFailed', { error: t('vpsDeployFailed', { error: 'unknown result' }) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n renderRemote(data)\n }, error => {\n vpsDeployStatus.textContent = t('vpsDeployFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n }, error => {\n vpsDeployStatus.textContent = t('vpsHostKeyFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n })\n })\n const readVpsSshForm = (): { sshUser: string; sshPort: number; sshKeyPath: string } | undefined => {\n const sshUser = vpsSshUser.value.trim()\n const sshPort = Number(vpsSshPort.value)\n const sshKeyPath = vpsSshKey.value.trim()\n saveVpsForm()\n if (!validVpsSshUser(sshUser) || !Number.isSafeInteger(sshPort) || sshPort < 1 || sshPort > 65535 || !validVpsSshKey(sshKeyPath)) {\n vpsDeployStatus.textContent = t('vpsDeployFailed', { error: t('vpsSshKey') })\n return undefined\n }\n return { sshUser, sshPort, sshKeyPath }\n }\n vpsCopyUninstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const form = vpsEffectiveForm()\n if (!validVpsFrpForm()) {\n vpsDeployStatus.textContent = t('vpsDeployNotReady')\n return\n }\n remoteProviderBusy = true\n refreshVpsDeployButton()\n const scriptPayload: Record<string, unknown> = { serverPort: form.serverPort }\n const certName = vpsCertName(form.publicOrigin)\n if (certName !== undefined) scriptPayload.certName = certName\n void controlRequestJson('/api/mobile-access/remote/frp/vps/uninstall-script', { method: 'POST', body: JSON.stringify(scriptPayload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(data => {\n const script = typeof data.vpsUninstallScript === 'string' ? data.vpsUninstallScript : ''\n if (script === '') throw new Error('empty script')\n return navigator.clipboard.writeText(script)\n })\n .then(() => { vpsDeployStatus.textContent = t('vpsUninstallScriptCopied') }, error => {\n vpsDeployStatus.textContent = t('vpsUninstallScriptFailed', { error: String(error) })\n })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n vpsUninstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const form = vpsEffectiveForm()\n if (!validVpsFrpForm()) {\n vpsDeployStatus.textContent = t('vpsDeployNotReady')\n return\n }\n const ssh = readVpsSshForm()\n if (ssh === undefined) return\n remoteProviderBusy = true\n refreshVpsDeployButton()\n vpsDeployStatus.textContent = t('vpsHostKeyFetching')\n remoteStatus.textContent = t('vpsHostKeyFetching')\n void requestVpsHostKeys(form.serverAddress, ssh.sshUser, ssh.sshPort, ssh.sshKeyPath)\n .then(hostKeys => {\n if (!window.confirm(t('vpsUninstallConfirmWithKeys', { fingerprints: hostKeys.map(key => key.display).join('\\n') }))) {\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n return\n }\n vpsDeployStatus.textContent = t('vpsUninstalling')\n remoteStatus.textContent = t('vpsUninstalling')\n const payload: Record<string, unknown> = {\n confirm: true,\n serverAddress: form.serverAddress,\n serverPort: form.serverPort,\n sshUser: ssh.sshUser,\n sshPort: ssh.sshPort,\n hostFingerprints: hostKeys.map(key => key.fingerprint),\n }\n if (ssh.sshKeyPath !== '') payload.sshKeyPath = ssh.sshKeyPath\n const certName = vpsCertName(form.publicOrigin)\n if (certName !== undefined) payload.certName = certName\n return controlRequestJson('/api/mobile-access/remote/frp/vps/uninstall', { method: 'POST', body: JSON.stringify(payload) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(data => {\n const removal = data.vpsUninstall !== null && typeof data.vpsUninstall === 'object' ? data.vpsUninstall as Record<string, unknown> : {}\n vpsDeployStatus.textContent = removal.removed === true ? t('vpsUninstallSuccess') : t('vpsUninstallFailed', { error: t('vpsUninstallFailed', { error: 'unknown result' }) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n renderRemote(data)\n }, error => {\n vpsDeployStatus.textContent = t('vpsUninstallFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n }, error => {\n vpsDeployStatus.textContent = t('vpsHostKeyFailed', { error: String(error) })\n remoteStatus.textContent = vpsDeployStatus.textContent\n remoteProviderBusy = false\n refreshVpsDeployButton()\n loadRemote()\n })\n })\n frpInstall.addEventListener('click', () => {\n if (remoteProviderBusy) return\n const accepted = window.confirm(t('frpInstallConfirm', { size: frpDownloadSize }))\n if (!accepted) return\n remoteProviderBusy = true\n frpInstall.disabled = true\n frpInstall.textContent = t('downloading')\n remoteStatus.textContent = t('installingFrp')\n void controlRequestJson('/api/mobile-access/remote/frp/component/install', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'installFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; loadRemote() })\n })\n frpConfigure.addEventListener('click', () => {\n if (remoteProviderBusy || !frpInstalled) return\n const form = frpForm()\n const unchanged = frpConfigured && form.token === '' && form.serverAddress === configuredFrpServer\n && form.serverPort === configuredFrpPort && form.publicOrigin === configuredFrpOrigin\n // A mode or entry-certificate change is a real change even when the four\n // connection fields are untouched.\n && form.mode === (frpConfiguredMode === 'attach' ? 'attach' : 'deploy')\n && form.entryTls === (frpConfiguredEntryTls === 'self-signed' ? 'self-signed' : 'public-ip-cert')\n && (form.vhostHttpPort ?? configuredFrpVhostPort) === configuredFrpVhostPort\n && (form.publicPort ?? configuredFrpPublicPort) === configuredFrpPublicPort\n if (!unchanged) {\n // Attach mode is validated by the same parser the host uses, so the panel\n // reports the exact code (for example a missing vhostHTTPPort) instead of a\n // generic \"invalid input\".\n const code = form.mode === 'attach'\n ? frpAttachFormErrorCode(attachClipboardForm(form))\n : (validFrpForm(form) ? undefined : 'frp_settings_invalid')\n if (code !== undefined) {\n if (form.mode === 'attach') showFrpAttachError(code)\n else remoteStatus.textContent = t('frpInputInvalid')\n return\n }\n }\n remoteProviderBusy = true\n frpConfigure.disabled = true\n frpConfigure.setAttribute('aria-busy', 'true')\n frpConfigure.textContent = t('saving')\n const configure = unchanged\n ? Promise.resolve<Record<string, unknown>>({})\n : controlRequestJson('/api/mobile-access/remote/frp/configure', { method: 'POST', body: JSON.stringify(form) })\n void configure.then(() => {\n frpToken.value = ''\n remoteStatus.textContent = t('frpSavingConnecting')\n return controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: true }) })\n }).then(renderRemote, error => { remoteFailureText = t('configureFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => {\n remoteProviderBusy = false\n frpConfigure.setAttribute('aria-busy', 'false')\n frpConfigure.textContent = t('frpSaveConnect')\n loadRemote()\n })\n })\n frpPurge.addEventListener('click', () => {\n if (remoteProviderBusy || !window.confirm(t(frpConfiguredMode === 'attach' && frpConfiguredEntryTls === 'self-signed'\n ? 'purgeFrpConfirmSelfSigned' : 'purgeFrpConfirm'))) return\n remoteProviderBusy = true\n frpPurge.disabled = true\n remoteStatus.textContent = t('purgingFrp')\n void controlRequestJson('/api/mobile-access/remote/frp/component/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) }, LONG_CONTROL_REQUEST_TIMEOUT_MS)\n .then(renderRemote, error => { remoteFailureText = t('purgeFailed', { error: String(error) }); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteProviderBusy = false; frpPurge.disabled = false; loadRemote() })\n })\n const clearOriginValidation = (): void => {\n for (const input of [originPublic, originHost, originPort, originCidrs]) input.removeAttribute('aria-invalid')\n }\n const showOriginFeedback = (message: string, error = false): void => {\n originFeedback.textContent = message\n originFeedback.hidden = false\n originFeedback.classList.toggle('is-error', error)\n }\n const showOriginError = (error: unknown): void => {\n const code = error instanceof Error ? error.message : String(error)\n const key = ORIGIN_ERROR_MESSAGE_KEYS[code]\n const fields: Record<string, HTMLInputElement> = {\n origin_public_origin_invalid: originPublic, origin_listen_host_invalid: originHost,\n origin_listen_address_unavailable: originHost, origin_listen_port_invalid: originPort,\n origin_listen_port_reserved: originPort, origin_listen_port_in_use: originPort,\n origin_allowed_cidrs_invalid: originCidrs,\n }\n clearOriginValidation()\n const input = fields[code]\n input?.setAttribute('aria-invalid', 'true')\n showOriginFeedback(key === undefined ? t('configureFailed', { error: String(error) }) : t(key), true)\n }\n const setOriginFormBusy = (busy: boolean): void => {\n originFormBusy = busy\n remoteProviderBusy = busy\n remoteSnapshotEpoch += 1\n renderRemote(latestRemoteState)\n }\n for (const input of [originPublic, originHost, originPort, originCidrs]) {\n input.addEventListener('input', () => {\n originFormDirty = true\n clearOriginValidation()\n originFeedback.hidden = true\n })\n }\n originConfigure.addEventListener('click', () => {\n if (remoteProviderBusy || remoteProvider !== 'origin') return\n clearOriginValidation()\n for (const input of [originPublic, originHost, originPort, originCidrs]) {\n if (!input.reportValidity()) return\n }\n const form = {\n publicOrigin: originPublic.value.trim(), listenHost: originHost.value.trim(),\n listenPort: Number(originPort.value), allowedCidrs: originCidrs.value.trim().split(/[\\s,]+/u).filter(Boolean),\n }\n setOriginFormBusy(true)\n showOriginFeedback(t('originSaving'))\n void controlRequestJson('/api/mobile-access/remote/origin/configure', { method: 'POST', body: JSON.stringify(form) })\n .then(() => controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: true }) }))\n .then(data => {\n originFormDirty = false\n renderRemote(data)\n if (data.state === 'error') showOriginError(new Error(typeof data.errorCode === 'string' ? data.errorCode : 'origin_gateway_start_failed'))\n else originFeedback.hidden = true\n }, showOriginError)\n .finally(() => {\n setOriginFormBusy(false)\n const invalidInput = [originPublic, originHost, originPort, originCidrs].find(input => input.getAttribute('aria-invalid') === 'true')\n invalidInput?.focus()\n loadRemote()\n })\n })\n originPurge.addEventListener('click', () => {\n if (remoteProviderBusy || !window.confirm(t('originPurgeConfirm'))) return\n setOriginFormBusy(true)\n showOriginFeedback(t('originPurging'))\n void controlRequestJson('/api/mobile-access/remote/origin/purge', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(data => {\n originFormDirty = false\n clearOriginValidation()\n renderRemote(data)\n showOriginFeedback(t('originPurged'))\n }, showOriginError)\n .finally(() => { setOriginFormBusy(false); loadRemote() })\n })\n originCopyBackend.addEventListener('click', () => {\n const address = originBackendAddress.textContent ?? ''\n if (address === '' || remoteProviderBusy) return\n void Promise.resolve().then(() => navigator.clipboard.writeText(address))\n .then(() => { showOriginFeedback(t('originBackendCopied')) }, error => { showOriginFeedback(t('requestFailed', { error: String(error) }), true) })\n })\n remoteToggle.addEventListener('click', () => {\n remoteToggle.disabled = true\n void controlRequestJson('/api/mobile-access/remote/control', { method: 'POST', body: JSON.stringify({ running: !remoteRunning }) })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(loadRemote)\n })\n remoteLogin.addEventListener('click', () => {\n if (remoteLoginUrl !== '') window.open(remoteLoginUrl, '_blank', 'noopener,noreferrer')\n })\n const reconnectRemote = (): void => {\n if (remoteReconnectBusy) return\n remoteReconnectBusy = true\n remoteReconnect.disabled = true\n remoteSetup.disabled = true\n remoteSetupRetry.disabled = true\n remoteStatus.textContent = remoteProvider === 'cpolar' ? t('reconnectingCpolar')\n : remoteProvider === 'cloudflared' ? t('reconnectingCloudflared')\n : remoteProvider === 'frp' ? t('reconnectingFrp') : remoteProvider === 'origin' ? t('reconnectingOrigin') : t('reconnectingTailscale')\n void controlRequestJson('/api/mobile-access/remote/reconnect', { method: 'POST', body: '{}' })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => {\n remoteReconnectBusy = false\n remoteReconnect.disabled = false\n remoteSetup.disabled = remoteSetupUrl === ''\n remoteSetupRetry.disabled = false\n })\n }\n remoteReconnect.addEventListener('click', reconnectRemote)\n remoteSetupRetry.addEventListener('click', () => { remoteSetupPending = false; reconnectRemote() })\n remoteSetup.addEventListener('click', () => {\n if (remoteSetupUrl === '') return\n remoteSetupPending = true\n remoteSetupOpenedAt = Date.now()\n remoteStatus.textContent = t('setupOpened')\n window.open(remoteSetupUrl, '_blank', 'noopener,noreferrer')\n })\n const retryAfterSetup = (): void => {\n if (!remoteSetupPending || document.visibilityState === 'hidden' || Date.now() - remoteSetupOpenedAt < 800) return\n remoteSetupPending = false\n reconnectRemote()\n }\n window.addEventListener('focus', retryAfterSetup)\n document.addEventListener('visibilitychange', retryAfterSetup)\n const copyRemotePairUrl = async (): Promise<void> => {\n try {\n await navigator.clipboard.writeText(activeRemotePairUrl)\n remoteStatus.textContent = t('linkCopied')\n } catch {\n remoteStatus.textContent = t('remoteQrReady')\n }\n }\n const openRemotePairing = (): Promise<void> => controlRequestJson('/api/mobile-access/remote/pairing/open', { method: 'POST', body: '{}' }).then(async data => {\n const pairUrl = typeof data.pairUrl === 'string' ? data.pairUrl : ''\n const expiresAt = typeof data.expiresAt === 'number' && Number.isFinite(data.expiresAt) ? data.expiresAt : 0\n showQr(typeof data.qrSvg === 'string' ? data.qrSvg : '', remoteQr)\n if (pairUrl === '') { remoteStatus.textContent = t('keyGenerationFailed'); return }\n // QR and copy actions share the same one-time pairing window. Re-copying\n // cannot invalidate a QR that the phone may already be scanning.\n activeRemotePairUrl = pairUrl\n activeRemotePairExpiresAt = expiresAt\n remotePairLink.textContent = pairUrl\n remotePairLink.classList.add('is-key')\n remotePairLink.hidden = false\n await copyRemotePairUrl()\n })\n remotePair.addEventListener('click', () => {\n remotePair.disabled = true\n void openRemotePairing()\n .catch(error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remotePair.disabled = !remoteReady })\n })\n remoteCopyLink.addEventListener('click', () => {\n remoteCopyLink.disabled = true\n const operation = activeRemotePairUrl !== '' && activeRemotePairExpiresAt > Date.now()\n ? copyRemotePairUrl()\n : openRemotePairing()\n void operation\n .catch(error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n .finally(() => { remoteCopyLink.disabled = !remoteReady })\n })\n const renderRemoteDevices = (data: Record<string, unknown>): void => {\n const devices = Array.isArray(data.devices) ? data.devices as Record<string, unknown>[] : []\n remoteDevicePanel.replaceChildren()\n if (devices.length === 0) {\n const empty = element('p', 'dsh-mobile-control__device-empty'); empty.textContent = t('noRemoteDevices'); remoteDevicePanel.append(empty); return\n }\n for (const device of devices) {\n const row = element('div', 'dsh-mobile-control__device')\n const label = element('span', 'dsh-mobile-control__device-label'); label.textContent = typeof device.label === 'string' ? device.label : t('device')\n const meta = element('span', 'dsh-mobile-control__device-meta'); meta.textContent = t('expires', { time: formatTime(device.expiresAt) })\n const revoke = element('button', 'dsh-mobile-control__device-revoke'); revoke.type = 'button'; revoke.textContent = t('revoke')\n const id = typeof device.id === 'string' ? device.id : ''\n revoke.addEventListener('click', () => {\n void controlRequestJson('/api/mobile-access/remote/devices/revoke', { method: 'POST', body: JSON.stringify({ deviceId: id }) })\n .then(loadRemoteDevices, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n })\n row.append(label, meta, revoke); remoteDevicePanel.append(row)\n }\n }\n const loadRemoteDevices = (): void => {\n void controlRequestJson('/api/mobile-access/remote/devices').then(renderRemoteDevices, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n }\n remoteDevices.addEventListener('click', () => {\n const show = remoteDevicePanel.hidden\n remoteDevicePanel.hidden = !show\n if (show) loadRemoteDevices()\n })\n remoteReset.addEventListener('click', () => {\n const prompt = remoteProvider === 'cpolar'\n ? t('resetCpolarConfirm')\n : remoteProvider === 'cloudflared' ? t('resetCloudflaredConfirm')\n : remoteProvider === 'frp' ? t('resetFrpConfirm') : remoteProvider === 'origin' ? t('resetOriginConfirm') : t('resetTailscaleConfirm')\n if (!window.confirm(prompt)) return\n void controlRequestJson('/api/mobile-access/remote/reset', { method: 'POST', body: JSON.stringify({ confirm: true }) })\n .then(renderRemote, error => { remoteFailureText = remoteFailureTextFor(error, 'requestFailed'); remoteStatus.textContent = remoteFailureText })\n })\n const renderDiagnostics = (data: Record<string, unknown>): void => {\n const entries = [...diagnosticEntriesForRender(data)]\n const overall = diagnosticOverallForChecks(data.overall, entries.map(entry => entry.status))\n diagnosticsSummary.className = `dsh-mobile-control__diagnostic-summary is-${overall}`\n diagnosticsSummaryTitle.textContent = overall === 'ok' ? t('diagnosticsComplete') : overall === 'attention' ? t('diagnosticsAttention') : t('diagnosticsProblem')\n diagnosticsSummaryText.textContent = locale === 'zh' && data.overall === overall && typeof data.summary === 'string' ? data.summary : t('diagnosticsCompleteFallback')\n diagnosticsChecks.replaceChildren()\n const statusLabels: Record<string, string> = { ok: t('diagnosticStatusOk'), warning: t('diagnosticStatusWarning'), error: t('diagnosticStatusError'), info: t('diagnosticStatusInfo') }\n const diagnosticLabelKeys: Record<string, string> = {\n versions: 'diagnosticLabelVersions', network: 'diagnosticLabelNetwork', lan: 'diagnosticLabelLan',\n firewall: 'diagnosticLabelFirewall', remote: 'diagnosticLabelRemote', 'phone-network': 'diagnosticLabelPhone',\n }\n const statusOf = (entry: Record<string, unknown>): 'ok' | 'warning' | 'error' | 'info' => normalizeDiagnosticStatus(entry.status)\n const localizedEntryCopy = (entry: Record<string, unknown>): { readonly detail: string; readonly action: string } => {\n const serverCopy = diagnosticServerCopy(entry)\n const reason = typeof entry.reason === 'string' ? entry.reason : ''\n const facts = entry.facts !== null && typeof entry.facts === 'object' ? entry.facts as Record<string, unknown> : {}\n const copyReason = facts.viaProxy === true && (reason === 'remote-ready' || reason === 'remote-rate-limited')\n ? `${reason}-via-proxy` : reason\n const catalog = DIAGNOSTIC_REASON_MESSAGES[locale] as Readonly<Record<string, readonly [string, string]>>\n const templates = catalog[copyReason]\n if (templates === undefined) return serverCopy\n // Diagnostics read as sentences, so an internal provider id or a raw controller\n // code belongs in the report, not in the prose: it renders as\n // \"Remote access through origin is currently off.\" or \"reported gateway_start_failed\".\n const providerNames: Record<string, string> = {\n tailscale: 'Tailscale Funnel', cpolar: 'cpolar', cloudflared: 'cloudflared',\n frp: t('frpName'), origin: t('originName'),\n }\n const providerId = typeof facts.provider === 'string' ? facts.provider : ''\n const controllerKey = typeof facts.controllerCode === 'string' ? REMOTE_ERROR_MESSAGE_KEYS[facts.controllerCode] : undefined\n const values: Record<string, string> = {\n provider: providerNames[providerId] ?? '',\n latencyMs: typeof facts.latencyMs === 'number' && Number.isFinite(facts.latencyMs) ? new Intl.NumberFormat(localeTag).format(facts.latencyMs) : '',\n interfaceName: typeof facts.interfaceName === 'string' ? facts.interfaceName : '',\n endpointSuffix: typeof facts.endpointSuffix === 'string' ? facts.endpointSuffix : '',\n // A translated sentence when the code is known, otherwise nothing rather than\n // an identifier no user can act on.\n controllerCode: controllerKey === undefined ? '' : t(controllerKey),\n }\n const interpolate = (template: string): string => template.replace(/\\{(\\w+)\\}/gu, (_match, key: string) => values[key] ?? '')\n let detail = interpolate(templates[0])\n let action = interpolate(templates[1])\n if (reason === 'versions-current') {\n const versions = data.versions !== null && typeof data.versions === 'object' ? data.versions as Record<string, unknown> : {}\n if ([versions.plugin, versions.dsh, versions.minimumAndroidApp].every(value => typeof value === 'string')) {\n detail += ` plugin ${String(versions.plugin)}, DSH ${String(versions.dsh)}, Android ${String(versions.minimumAndroidApp)}.`\n }\n }\n if (reason === 'remote-controller-error') action = diagnosticControllerAction(facts.controllerCode, locale, action)\n return { detail, action }\n }\n const appendGroup = (label: string, groupEntries: Record<string, unknown>[]): void => {\n if (groupEntries.length === 0) return\n const group = element('section', 'dsh-mobile-control__diagnostic-group')\n const groupHeader = element('header', 'dsh-mobile-control__diagnostic-group-header')\n const groupTitle = element('h3'); groupTitle.textContent = label\n const groupCount = element('span'); groupCount.textContent = t('diagnosticItems', { count: groupEntries.length })\n const list = element('div', 'dsh-mobile-control__diagnostic-list'); list.setAttribute('role', 'list')\n groupHeader.append(groupTitle, groupCount)\n for (const entry of groupEntries) {\n const state = statusOf(entry)\n const row = element('section', `dsh-mobile-control__diagnostic-check is-${state}`); row.setAttribute('role', 'listitem')\n const marker = element('span', 'dsh-mobile-control__diagnostic-marker'); marker.setAttribute('aria-hidden', 'true')\n const rowBody = element('div', 'dsh-mobile-control__diagnostic-check-body')\n const rowHeader = element('div', 'dsh-mobile-control__diagnostic-check-header')\n const labelKey = typeof entry.id === 'string' ? diagnosticLabelKeys[entry.id] : undefined\n const rowTitle = element('strong'); rowTitle.textContent = locale !== 'zh' && labelKey !== undefined ? t(labelKey) : typeof entry.label === 'string' ? entry.label : t('diagnosticCheck')\n const badge = element('span', 'dsh-mobile-control__diagnostic-badge'); badge.textContent = statusLabels[state]!\n const localizedCopy = localizedEntryCopy(entry)\n const detail = element('p'); detail.textContent = localizedCopy.detail\n rowHeader.append(rowTitle, badge); rowBody.append(rowHeader, detail)\n if (localizedCopy.action !== '') {\n const action = element('p', 'dsh-mobile-control__diagnostic-action')\n const actionLabel = element('span'); actionLabel.textContent = t('diagnosticAction')\n action.append(actionLabel, document.createTextNode(localizedCopy.action)); rowBody.append(action)\n }\n row.append(marker, rowBody); list.append(row)\n }\n group.append(groupHeader, list); diagnosticsChecks.append(group)\n }\n const issues = entries.filter(entry => statusOf(entry) === 'error' || statusOf(entry) === 'warning')\n const otherChecks = entries.filter(entry => statusOf(entry) !== 'error' && statusOf(entry) !== 'warning')\n appendGroup(t('diagnosticNeedsAction'), issues)\n appendGroup(issues.length === 0 ? t('diagnosticDetails') : t('diagnosticOther'), otherChecks)\n diagnosticsSummaryMeta.textContent = issues.length === 0\n ? t('diagnosticNoBlockers', { count: entries.length })\n : t('diagnosticNeedsCount', { count: entries.length, issues: issues.length })\n diagnosticsChecks.hidden = entries.length === 0\n const reportCopy = LOCALIZED_DIAGNOSTIC_COPY[locale]\n const generatedAt = typeof data.generatedAt === 'number' ? new Date(data.generatedAt).toLocaleString(localeTag) : new Date().toLocaleString(localeTag)\n const lines = entries.map(entry => {\n const state = statusOf(entry)\n const labelKey = typeof entry.id === 'string' ? diagnosticLabelKeys[entry.id] : undefined\n const label = labelKey === undefined && typeof entry.label === 'string' ? entry.label : labelKey === undefined ? t('diagnosticCheck') : t(labelKey)\n const localizedCopy = localizedEntryCopy(entry)\n return `[${statusLabels[state]!}] ${label}: ${localizedCopy.detail}${localizedCopy.action === '' ? '' : ` ${localizedCopy.action}`}`\n })\n copiedDiagnosticReport = [reportCopy.reportTitle, `${reportCopy.generated}: ${generatedAt}`, ...lines].join('\\n')\n diagnosticsReport.textContent = copiedDiagnosticReport\n diagnosticsDetails.hidden = copiedDiagnosticReport === ''\n diagnosticsCopy.disabled = copiedDiagnosticReport === ''\n diagnosticsCopy.hidden = copiedDiagnosticReport === ''\n diagnosticsToolbar.classList.toggle('has-report', copiedDiagnosticReport !== '')\n }\n const showDiagnosticsFailure = (error: unknown): void => {\n diagnosticsSummary.className = 'dsh-mobile-control__diagnostic-summary is-error'\n diagnosticsSummaryTitle.textContent = t('diagnosticsIncomplete')\n diagnosticsSummaryText.textContent = t('diagnosticsReadFailed', { error: String(error) })\n diagnosticsSummaryMeta.textContent = t('diagnosticsUnavailable')\n diagnosticsChecks.replaceChildren()\n diagnosticsChecks.hidden = true\n copiedDiagnosticReport = ''\n diagnosticsReport.textContent = ''\n diagnosticsDetails.hidden = true\n diagnosticsCopy.disabled = true\n diagnosticsCopy.hidden = true\n diagnosticsToolbar.classList.remove('has-report')\n }\n const loadDiagnostics = (): void => {\n if (diagnosticsBusy) return\n diagnosticsBusy = true\n diagnosticsRun.disabled = true\n diagnosticsRun.setAttribute('aria-busy', 'true')\n diagnosticsRun.textContent = t('diagnosticsChecking')\n diagnosticsSummary.className = 'dsh-mobile-control__diagnostic-summary is-running'\n diagnosticsSummaryTitle.textContent = t('diagnosticsCheckingTitle')\n diagnosticsSummaryText.textContent = t('diagnosticsCheckingText')\n diagnosticsSummaryMeta.textContent = t('diagnosticsRunningMeta')\n diagnosticsFeedback.hidden = true\n diagnosticsChecks.classList.add('is-refreshing')\n diagnosticsChecks.setAttribute('aria-busy', 'true')\n void controlRequestJson('/api/mobile-access/diagnostics').then(\n data => { renderDiagnosticPayloadSafely(data, renderDiagnostics, showDiagnosticsFailure) },\n showDiagnosticsFailure,\n ).finally(() => {\n diagnosticsBusy = false\n diagnosticsRun.disabled = false\n diagnosticsRun.setAttribute('aria-busy', 'false')\n diagnosticsRun.textContent = t('diagnosticsRetry')\n diagnosticsChecks.classList.remove('is-refreshing')\n diagnosticsChecks.setAttribute('aria-busy', 'false')\n })\n }\n diagnosticsEntry.addEventListener('click', () => {\n if (!diagnosticsView.hidden) { selectView(previousAccessView); return }\n selectView('diagnostics'); loadDiagnostics()\n })\n diagnosticsRun.addEventListener('click', loadDiagnostics)\n const closeUpdateCard = (): void => {\n updateCard.hidden = true\n if (pluginUpdateAvailable && pluginLatestVersion !== '') updatePlugin.hidden = !diagnosticsView.hidden\n }\n const runPluginUpdate = (): void => {\n if (!pluginUpdateAvailable || pluginLatestVersion === '' || updateInFlight) return\n updateInFlight = true\n updateNow.disabled = true\n updateLater.disabled = true\n updateCardTitle.textContent = t('updatingPlugin')\n releaseNotice.hidden = true\n releaseNotice.classList.remove('is-error')\n void controlRequestJson('/api/mobile-access/release/update', { method: 'POST', body: '{}' }, LONG_CONTROL_REQUEST_TIMEOUT_MS).then(data => {\n const installedVersion = releaseVersion(data.installedVersion) ?? pluginLatestVersion\n pluginUpdateAvailable = false\n updateCard.hidden = true\n updatePlugin.hidden = true\n releaseNotice.textContent = t('pluginUpdatedRestart', { version: installedVersion })\n releaseNotice.hidden = false\n }, error => {\n updateCard.hidden = true\n updatePlugin.hidden = false\n releaseNotice.textContent = t('pluginUpdateFailed', { error: String(error) })\n releaseNotice.classList.add('is-error')\n releaseNotice.hidden = false\n }).finally(() => {\n updateInFlight = false\n updateNow.disabled = false\n updateLater.disabled = false\n })\n }\n updatePlugin.addEventListener('click', () => {\n if (!pluginUpdateAvailable || pluginLatestVersion === '' || updateInFlight) return\n updatePlugin.hidden = true\n updateCardTitle.textContent = t('updateTo', { version: pluginLatestVersion })\n updateCardNotes.textContent = pluginReleaseNotes === '' ? t('updateNotesEmpty') : pluginReleaseNotes\n updateCardNotes.hidden = pluginReleaseNotes === ''\n updateCard.hidden = false\n })\n updateNow.addEventListener('click', runPluginUpdate)\n updateLater.addEventListener('click', closeUpdateCard)\n diagnosticsCopy.addEventListener('click', () => {\n if (copiedDiagnosticReport === '') return\n void navigator.clipboard.writeText(copiedDiagnosticReport).then(() => {\n diagnosticsFeedback.textContent = t('diagnosticsCopied')\n diagnosticsFeedback.hidden = false\n }, () => {\n diagnosticsDetails.open = true\n diagnosticsFeedback.textContent = t('diagnosticsCopyManual')\n diagnosticsFeedback.hidden = false\n })\n })\n pair.addEventListener('click', () => { pair.disabled = true; openPairing('key') })\n linkPair.addEventListener('click', () => { linkPair.disabled = true; openPairing('link') })\n close.addEventListener('click', () => { setOpen(false) })\n const dismiss = (event: PointerEvent): void => {\n if (panel.hidden || !(event.target instanceof Node)) return\n if (!providerInfo.contains(event.target)) {\n providerInfoPinned = false\n providerInfoHovered = false\n syncProviderInfo()\n }\n if (!panel.contains(event.target) && !document.querySelector('.dsh-mobile-control__trigger')?.contains(event.target)) setOpen(false)\n }\n document.addEventListener('pointerdown', dismiss)\n void controlRequestJson('/api/mobile-access/release').then(renderRelease, () => {})\n void controlRequestJson('/api/mobile-access/lan/control').then(render, error => { status.textContent = t('requestFailed', { error: String(error) }) })\n loadRemote()\n const remotePoll = window.setInterval(() => { if (!panel.hidden && !remoteView.hidden) loadRemote() }, 1_500)\n const pollWsBlocked = (): void => {\n void controlRequestJson('/api/mobile-access/remote/websocket-paths/blocked').then(blocked => {\n const raw = Array.isArray(blocked.blocked) ? blocked.blocked : []\n wsPathsBlocked = raw.filter((entry): entry is { path: string; attempts: number } =>\n typeof entry === 'object' && entry !== null\n && typeof (entry as { path?: unknown }).path === 'string'\n && typeof (entry as { attempts?: unknown }).attempts === 'number')\n renderWsDot()\n if (!diagnosticsView.hidden) renderWsBlocked(wsPathsBlocked)\n }, () => { /* gateway unreachable; dot keeps its last state */ })\n }\n const wsBlockedPoll = window.setInterval(pollWsBlocked, 20_000)\n return { remove: () => { lifecycle.abort(); window.clearInterval(remotePoll); window.clearInterval(wsBlockedPoll); window.removeEventListener('focus', retryAfterSetup); document.removeEventListener('visibilitychange', retryAfterSetup); document.removeEventListener('pointerdown', dismiss); root.remove() }, toggle: () => { setOpen(panel.hidden !== false) }, isOpen: () => !panel.hidden }\n}\n\nfunction mobileRequest(path: string, init: RequestInit = {}): Promise<Response> {\n const target = new URL(path, location.href)\n if (target.origin !== location.origin) throw new TypeError('mobile extension requests must stay on the DSH origin')\n const headers = new Headers(init.headers)\n const method = (init.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const csrf = document.cookie.split(';').map(value => value.trim()).find(value => value.startsWith('dsh_ma_csrf='))?.slice(12)\n if (csrf !== undefined) headers.set('x-dsh-mobile-csrf', csrf)\n }\n return fetch(target, { ...init, headers, credentials: 'same-origin', cache: 'no-store', redirect: 'error' })\n}\n\nexport interface CombinedClientSignal {\n readonly signal: AbortSignal\n readonly cleanup: () => void\n}\n\n/** Combine extension and caller abort lifetimes and expose deterministic listener cleanup. */\nexport function combineClientSignalLifetime(first: AbortSignal, second: AbortSignal): CombinedClientSignal {\n if (first.aborted || second.aborted) {\n const aborted = new AbortController()\n aborted.abort(first.aborted ? first.reason : second.reason)\n return { signal: aborted.signal, cleanup: () => undefined }\n }\n const controller = new AbortController()\n const cleanup = (): void => {\n first.removeEventListener('abort', abortFirst)\n second.removeEventListener('abort', abortSecond)\n }\n const abortFirst = (): void => { cleanup(); controller.abort(first.reason) }\n const abortSecond = (): void => { cleanup(); controller.abort(second.reason) }\n first.addEventListener('abort', abortFirst, { once: true })\n second.addEventListener('abort', abortSecond, { once: true })\n return { signal: controller.signal, cleanup }\n}\n\n/** Combine extension and caller abort lifetimes on WebViews without AbortSignal.any. */\nexport function combineClientSignals(first: AbortSignal, second: AbortSignal): AbortSignal {\n return combineClientSignalLifetime(first, second).signal\n}\n\n/** Keep a combined request lifetime until a streamed response is consumed or cancelled. */\nexport function bindClientResponseLifetime(response: Response, cleanup: () => void): Response {\n if (response.body === null) {\n cleanup()\n return response\n }\n const reader = response.body.getReader()\n let released = false\n const release = (): void => {\n if (released) return\n released = true\n cleanup()\n }\n const body = new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const result = await reader.read()\n if (result.done) {\n release()\n controller.close()\n } else controller.enqueue(result.value)\n } catch (error) {\n release()\n controller.error(error)\n }\n },\n async cancel(reason) {\n try { await reader.cancel(reason) } finally { release() }\n },\n })\n try {\n const retained = new Response(body, { headers: response.headers, status: response.status, statusText: response.statusText })\n Object.defineProperties(retained, {\n redirected: { configurable: true, value: response.redirected },\n type: { configurable: true, value: response.type },\n url: { configurable: true, value: response.url },\n })\n return retained\n } catch (error) {\n void reader.cancel(error)\n release()\n throw error\n }\n}\n\n/** Dispose an old UI when its replacement Host generation cannot be activated. */\nexport function failClosedExtensionGenerationReplacement(\n hasActive: boolean,\n activeGeneration: string | undefined,\n replacementGeneration: string | undefined,\n dispose: () => void,\n): boolean {\n if (!hasActive || activeGeneration === replacementGeneration) return false\n dispose()\n return true\n}\n\n/** Resolve one SDK route and prove its normalized path remains in the current extension namespace. */\nexport function extensionRouteUrl(id: string, path: string, baseUrl: string): URL {\n const rawPathname = path.split(/[?#]/u, 1)[0] ?? ''\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(id) || !path.startsWith('/') || path.startsWith('//')\n || rawPathname.includes('\\\\') || rawPathname.includes('\\0') || /%(?:2f|5c)/iu.test(rawPathname)) {\n throw new TypeError('extension routes must be relative')\n }\n const origin = new URL(baseUrl).origin\n const prefix = `/mobile-access/extensions/${id}/routes`\n let target: URL\n try { target = new URL(`${prefix}${path}`, origin) } catch { throw new TypeError('extension routes must be relative') }\n let decodedPathname: string\n try { decodedPathname = decodeURIComponent(target.pathname) } catch { throw new TypeError('extension routes must be relative') }\n if (target.origin !== origin || target.hash !== '' || decodedPathname.includes('\\\\')\n || decodedPathname !== prefix && !decodedPathname.startsWith(`${prefix}/`)) {\n throw new TypeError('extension routes must be relative')\n }\n const relative = decodedPathname.slice(prefix.length)\n if (relative.split('/').some(part => part === '.' || part === '..')) throw new TypeError('extension routes must be relative')\n return target\n}\n\n/** Resolve one generation-pinned static asset URL in the current extension namespace. */\nexport function extensionAssetUrl(id: string, generation: string | undefined, path: string, baseUrl: string): URL {\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(id) || generation !== undefined && !/^[a-f\\d]{64}$/u.test(generation)) {\n throw new TypeError('extension asset path is invalid')\n }\n const normalized = path.replaceAll('\\\\', '/')\n if (normalized.length === 0 || normalized.startsWith('/') || normalized.split('/').some(part => part === '' || part === '.' || part === '..')) {\n throw new TypeError('extension asset path is invalid')\n }\n const target = new URL(`/mobile-access/extensions/${id}/assets/${normalized.split('/').map(encodeURIComponent).join('/')}`, new URL(baseUrl).origin)\n if (generation !== undefined) target.searchParams.set('generation', generation)\n return target\n}\n\n/** Add the immutable Host generation selected for one activated mobile UI. */\nexport function extensionGenerationHeaders(generation: string | undefined, headers?: HeadersInit): Headers {\n const result = new Headers(headers)\n if (generation !== undefined) result.set('x-dsh-mobile-extension-generation', generation)\n return result\n}\n\n/** Build the JSON request used by a mobile extension to invoke a Host action. */\nexport function extensionActionRequestInit(generation: string | undefined, input: unknown, signal: AbortSignal): RequestInit {\n return {\n method: 'POST',\n headers: extensionGenerationHeaders(generation, { 'content-type': 'application/json' }),\n body: JSON.stringify(input ?? {}),\n signal,\n }\n}\n\nexport function registerUniqueDisposable<T extends { readonly dispose: () => void }>(\n entries: Map<string, T>,\n claimedIds: Set<string>,\n id: string,\n mount: () => T,\n): () => void {\n if (claimedIds.has(id)) throw new Error(`duplicate lifecycle id: ${id}`)\n claimedIds.add(id)\n let mounted: T\n try { mounted = mount() } catch (error) { claimedIds.delete(id); throw error }\n let disposed = false\n const dispose = (): void => {\n if (disposed) return\n disposed = true\n mounted.dispose()\n }\n const entry = { ...mounted, dispose } as T\n entries.set(id, entry)\n return () => {\n if (entries.get(id) === entry) { entries.delete(id); claimedIds.delete(id) }\n dispose()\n }\n}\n\n/** Dispose resources omitted by a successfully fetched authoritative manifest. */\nexport function reconcileRemovedExtensions(currentIds: Iterable<string>, seen: ReadonlySet<string>, dispose: (id: string) => void): void {\n for (const id of new Set(currentIds)) if (!seen.has(id)) dispose(id)\n}\n\n/** Reconcile managed resources against one validated authoritative id set. */\nexport function publishAuthoritativeExtensionIds(\n authoritativeIds: Set<string>,\n seen: ReadonlySet<string>,\n managedIdSources: readonly Iterable<string>[],\n dispose: (id: string) => void,\n): void {\n const current = new Set(authoritativeIds)\n for (const source of managedIdSources) for (const id of source) current.add(id)\n reconcileRemovedExtensions(current, seen, dispose)\n authoritativeIds.clear()\n for (const id of seen) authoritativeIds.add(id)\n}\n\ninterface MobileExtensionManifestEntry {\n readonly id: string\n readonly generation?: string\n readonly scriptUrl?: string\n readonly styleUrl?: string\n readonly assetsUrl?: string\n}\ninterface MobileExtensionManifest {\n readonly extensions: readonly MobileExtensionManifestEntry[]\n readonly legacy: { readonly scriptRevision: string; readonly styleRevision: string }\n}\n\nfunction validManifestResourceUrl(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith('/') && !value.startsWith('//') && !value.includes('://')\n && !value.split('/').some(part => part === '..' || part === '.')\n}\n\n/** Validate the manifest before treating it as authoritative state. */\nexport function parseMobileExtensionManifest(payload: unknown): MobileExtensionManifest | undefined {\n if (typeof payload !== 'object' || payload === null) return undefined\n const candidate = payload as { protocol?: unknown; extensions?: unknown; legacy?: unknown }\n if (candidate.protocol !== 1 || !Array.isArray(candidate.extensions) || typeof candidate.legacy !== 'object' || candidate.legacy === null) return undefined\n const legacy = candidate.legacy as { scriptRevision?: unknown; styleRevision?: unknown }\n if (typeof legacy.scriptRevision !== 'string' || typeof legacy.styleRevision !== 'string') return undefined\n const ids = new Set<string>()\n const extensions: MobileExtensionManifestEntry[] = []\n for (const value of candidate.extensions) {\n if (typeof value !== 'object' || value === null) return undefined\n const entry = value as { id?: unknown; generation?: unknown; scriptUrl?: unknown; styleUrl?: unknown; assetsUrl?: unknown }\n if (typeof entry.id !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/u.test(entry.id) || ids.has(entry.id)) return undefined\n if (entry.generation !== undefined && (typeof entry.generation !== 'string' || !/^[a-f\\d]{64}$/u.test(entry.generation))) return undefined\n if (entry.scriptUrl !== undefined && !validManifestResourceUrl(entry.scriptUrl)) return undefined\n if (entry.styleUrl !== undefined && !validManifestResourceUrl(entry.styleUrl)) return undefined\n if (entry.assetsUrl !== undefined && !validManifestResourceUrl(entry.assetsUrl)) return undefined\n ids.add(entry.id)\n extensions.push({\n id: entry.id,\n ...(entry.generation === undefined ? {} : { generation: entry.generation }),\n ...(entry.scriptUrl === undefined ? {} : { scriptUrl: entry.scriptUrl }),\n ...(entry.styleUrl === undefined ? {} : { styleUrl: entry.styleUrl }),\n ...(entry.assetsUrl === undefined ? {} : { assetsUrl: entry.assetsUrl }),\n })\n }\n return { extensions, legacy: { scriptRevision: legacy.scriptRevision, styleRevision: legacy.styleRevision } }\n}\n\n/** A missing manifest is authoritative before optional legacy resources finish refreshing. */\nexport async function handleMissingExtensionManifest(\n clearManifestResources: () => void,\n refreshLegacyResources: () => Promise<readonly boolean[]>,\n signal: AbortSignal,\n): Promise<boolean> {\n clearManifestResources()\n const results = await refreshLegacyResources()\n return !signal.aborted && results.every(Boolean)\n}\n\ninterface LifecycleSchedulerRuntime {\n readonly document: Pick<Document, 'visibilityState' | 'addEventListener' | 'removeEventListener'>\n readonly window: Pick<Window, 'addEventListener' | 'removeEventListener' | 'setTimeout' | 'clearTimeout'>\n}\n\nexport interface LifecycleRefreshController {\n (): void\n refresh(): void\n}\n\nfunction refreshAborted(signal: AbortSignal): boolean { return signal.aborted }\n\n/** Run one coalesced refresh cycle, slowing down when the page is hidden. */\nexport function startLifecycleRefreshScheduler(\n refresh: (signal: AbortSignal) => void | Promise<void>,\n options: { readonly visibleIntervalMs?: number; readonly hiddenIntervalMs?: number; readonly cycleTimeoutMs?: number } = {},\n runtime: LifecycleSchedulerRuntime = { document, window },\n): LifecycleRefreshController {\n const visibleIntervalMs = options.visibleIntervalMs ?? 45_000\n const hiddenIntervalMs = options.hiddenIntervalMs ?? 300_000\n const cycleTimeoutMs = options.cycleTimeoutMs ?? 30_000\n let timer: number | undefined\n let cycleTimer: number | undefined\n let running = false\n let queued = false\n let disposed = false\n let controller: AbortController | undefined\n const clearTimer = (): void => {\n if (timer === undefined) return\n runtime.window.clearTimeout(timer)\n timer = undefined\n }\n const clearCycleTimer = (): void => {\n if (cycleTimer === undefined) return\n runtime.window.clearTimeout(cycleTimer)\n cycleTimer = undefined\n }\n const schedule = (): void => {\n if (disposed) return\n clearTimer()\n const delay = runtime.document.visibilityState === 'hidden' ? hiddenIntervalMs : visibleIntervalMs\n timer = runtime.window.setTimeout(run, delay)\n }\n const run = (): void => {\n if (disposed) return\n clearTimer()\n if (running) { queued = true; return }\n running = true\n const current = new AbortController()\n controller = current\n const refreshPromise = Promise.resolve().then(() => refresh(current.signal))\n const timeoutPromise = new Promise<void>(resolve => {\n cycleTimer = runtime.window.setTimeout(() => {\n cycleTimer = undefined\n current.abort(new DOMException('mobile extension refresh timed out', 'TimeoutError'))\n resolve()\n }, cycleTimeoutMs)\n })\n void Promise.race([refreshPromise, timeoutPromise]).catch(() => { /* Keep the last good resources during reconnects. */ }).finally(() => {\n clearCycleTimer()\n if (controller === current) controller = undefined\n running = false\n if (disposed) return\n if (queued) { queued = false; run() } else schedule()\n })\n void refreshPromise.catch(() => { /* A timed-out refresh may reject after the scheduler has moved on. */ })\n }\n const onVisibilityChange = (): void => {\n if (runtime.document.visibilityState === 'hidden') schedule()\n else run()\n }\n runtime.document.addEventListener('visibilitychange', onVisibilityChange)\n runtime.window.addEventListener('focus', run)\n runtime.window.addEventListener('online', run)\n run()\n const stop = (): void => {\n disposed = true\n queued = false\n controller?.abort()\n controller = undefined\n clearCycleTimer()\n clearTimer()\n runtime.document.removeEventListener('visibilitychange', onVisibilityChange)\n runtime.window.removeEventListener('focus', run)\n runtime.window.removeEventListener('online', run)\n }\n return Object.assign(stop, { refresh: run })\n}\n\ninterface ExtensionEventRuntime {\n readonly window: Pick<Window, 'addEventListener' | 'removeEventListener' | 'setTimeout' | 'clearTimeout'>\n readonly create: (url: string) => Pick<EventSource, 'close' | 'onopen' | 'onerror' | 'addEventListener'>\n}\n\n/** Maintain one authenticated same-origin extension event stream with bounded reconnect backoff. */\nexport function startExtensionChangeStream(\n changed: () => void,\n runtime: ExtensionEventRuntime = {\n window,\n create: url => new EventSource(url, { withCredentials: true }),\n },\n onTaskEvent?: (payload: unknown) => void,\n onDeviceRevoked?: () => void,\n): () => void {\n let source: ReturnType<ExtensionEventRuntime['create']> | undefined\n let timer: number | undefined\n let disposed = false\n let retryMs = 1_000\n const clearTimer = (): void => {\n if (timer === undefined) return\n runtime.window.clearTimeout(timer)\n timer = undefined\n }\n const connect = (): void => {\n if (disposed || source !== undefined) return\n clearTimer()\n const next = runtime.create('/mobile-access/extensions/events')\n source = next\n next.onopen = () => { retryMs = 1_000 }\n next.addEventListener('extensions-changed', changed)\n if (onTaskEvent !== undefined) {\n next.addEventListener('task-notify', (message: Event) => {\n const data = (message as MessageEvent).data\n onTaskEvent(data)\n })\n }\n if (onDeviceRevoked !== undefined) {\n next.addEventListener('device-revoked', (message: Event) => {\n const data = (message as MessageEvent).data\n if (isDeviceRevokedPayload(data)) onDeviceRevoked()\n })\n }\n next.onerror = () => {\n if (source !== next) return\n next.close()\n source = undefined\n if (disposed) return\n const delay = retryMs\n retryMs = Math.min(30_000, retryMs * 2)\n timer = runtime.window.setTimeout(connect, delay)\n }\n }\n const reconnectNow = (): void => {\n if (disposed) return\n source?.close()\n source = undefined\n retryMs = 1_000\n connect()\n }\n runtime.window.addEventListener('online', reconnectNow)\n connect()\n return () => {\n disposed = true\n clearTimer()\n source?.close()\n source = undefined\n runtime.window.removeEventListener('online', reconnectNow)\n }\n}\n\nexport interface ActivationWork<T> {\n readonly result: Promise<T>\n readonly cancel: () => void\n readonly commit?: (value: T) => void\n readonly dispose: (value: T) => void\n}\n\ninterface PendingActivation<T> {\n readonly key: object\n readonly generation: number\n readonly controller: AbortController\n readonly cancel: () => void\n readonly completion: Promise<boolean>\n}\n\n/** Keep at most one activation in flight per id and commit only its latest generation. */\nexport class PerIdActivationLifecycle<T> {\n private readonly active = new Map<string, { readonly value: T; readonly dispose: () => void }>()\n private readonly pending = new Map<string, PendingActivation<T>>()\n private readonly generations = new Map<string, number>()\n private disposed = false\n\n hasActive(id: string): boolean { return this.active.has(id) }\n getActive(id: string): T | undefined { return this.active.get(id)?.value }\n pendingCount(): number { return this.pending.size }\n\n async activate(\n id: string,\n key: object,\n cycleSignal: AbortSignal | undefined,\n create: (controller: AbortController, generation: number) => ActivationWork<T>,\n ): Promise<boolean> {\n if (this.disposed || cycleSignal?.aborted === true) return false\n const existing = this.pending.get(id)\n if (existing !== undefined) {\n if (existing.key === key && !existing.controller.signal.aborted) return this.waitFor(existing, cycleSignal)\n existing.cancel()\n }\n const generation = (this.generations.get(id) ?? 0) + 1\n this.generations.set(id, generation)\n const controller = new AbortController()\n let work: ActivationWork<T>\n try { work = create(controller, generation) } catch {\n controller.abort(new DOMException('mobile extension activation failed to start', 'AbortError'))\n return false\n }\n let cancelled = false\n let pending = {} as PendingActivation<T>\n const cancel = (): void => {\n if (cancelled) return\n cancelled = true\n if (this.pending.get(id) === pending) this.pending.delete(id)\n if (this.generations.get(id) === generation) this.generations.set(id, generation + 1)\n controller.abort(new DOMException('mobile extension activation cancelled', 'AbortError'))\n work.cancel()\n }\n const completion = Promise.resolve(work.result).then(value => {\n const current = this.pending.get(id)\n const canCommit = !this.disposed && current === pending && current.generation === this.generations.get(id)\n && !controller.signal.aborted\n if (!canCommit) {\n try { work.dispose(value) } catch { /* A stale extension disposer must not break lifecycle cleanup. */ }\n return false\n }\n try { work.commit?.(value) } catch {\n try { work.dispose(value) } catch { /* Keep the previous active extension intact. */ }\n return false\n }\n const previous = this.active.get(id)\n let valueDisposed = false\n const dispose = (): void => {\n if (valueDisposed) return\n valueDisposed = true\n controller.abort(new DOMException('mobile extension deactivated', 'AbortError'))\n try { work.dispose(value) } catch { /* Disposers are isolated per extension. */ }\n }\n this.active.set(id, { value, dispose })\n previous?.dispose()\n return true\n }, () => {\n cancel()\n return false\n }).finally(() => {\n if (this.pending.get(id) === pending) this.pending.delete(id)\n })\n Object.assign(pending, { key, generation, controller, cancel, completion })\n this.pending.set(id, pending)\n return this.waitFor(pending, cycleSignal)\n }\n\n remove(id: string): void {\n this.generations.set(id, (this.generations.get(id) ?? 0) + 1)\n this.active.get(id)?.dispose()\n this.active.delete(id)\n this.pending.get(id)?.cancel()\n }\n\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n for (const current of this.active.values()) current.dispose()\n this.active.clear()\n for (const current of this.pending.values()) current.cancel()\n }\n\n private waitFor(pending: PendingActivation<T>, signal: AbortSignal | undefined): Promise<boolean> {\n if (signal === undefined) return pending.completion\n if (signal.aborted) { pending.cancel(); return Promise.resolve(false) }\n return new Promise<boolean>(resolve => {\n let settled = false\n const finish = (value: boolean): void => {\n if (settled) return\n settled = true\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n }\n const onAbort = (): void => { pending.cancel(); finish(false) }\n signal.addEventListener('abort', onAbort, { once: true })\n void pending.completion.then(finish)\n })\n }\n}\n\nfunction installCustomAssets(): () => void {\n const legacyStyle = element('style'); legacyStyle.dataset.plugin = 'dsh-mobile-custom'; document.head.append(legacyStyle)\n const legacyCssState = { etag: '', modified: '' }\n const previous = window.dshMobile\n let legacyMount: MobileExtensionMount | undefined = queuedLegacyMount\n let legacySource = ''\n let legacyRoot: HTMLElement | undefined\n let legacyDispose: (() => void) | undefined\n const definitions = new Map<string, MobileClientDefinition>()\n type ExtensionSurfaceEntry = { readonly dispose: () => void; readonly container: HTMLElement; readonly host: () => HTMLElement }\n type ActiveExtension = { readonly controller: AbortController; readonly surfaces: Map<string, ExtensionSurfaceEntry>; readonly cleanup?: () => void }\n const activations = new PerIdActivationLifecycle<ActiveExtension>()\n const styleNodes = new Map<string, HTMLStyleElement>()\n const styleEtags = new Map<string, string>()\n const scriptDigests = new Map<string, string>()\n const activeHostGenerations = new Map<string, string | undefined>()\n const activationKeys = new Map<string, { readonly definition: MobileClientDefinition; readonly generation?: string }>()\n const manifestExtensionIds = new Set<string>()\n const managedDefinitionIds = new Set<string>()\n let manifestEtag = ''\n let disposed = false\n let expectedDefinitionId: string | undefined\n const SURFACE_HOST_STYLES: Readonly<Record<string, string>> = {\n 'sidebar-action': 'position:fixed;z-index:1100;top:calc(env(safe-area-inset-top) + 8px);left:8px;display:flex;flex-direction:column;gap:6px;pointer-events:none',\n 'header-action': 'position:fixed;z-index:1100;top:calc(env(safe-area-inset-top) + 8px);right:8px;display:flex;flex-direction:column;gap:6px;pointer-events:none',\n 'composer-dock': 'position:fixed;z-index:1100;bottom:calc(env(safe-area-inset-bottom) + 8px);left:50%;transform:translateX(-50%);display:flex;flex-direction:column;gap:6px;pointer-events:none',\n 'settings-section': 'position:fixed;z-index:1100;inset:auto 8px calc(env(safe-area-inset-bottom) + 72px) 8px;max-height:40vh;overflow:auto;pointer-events:none',\n }\n const surfaceHost = (placement: string): HTMLElement | undefined => {\n const existing = document.querySelector<HTMLElement>(`[data-dsh-mobile-surface-host=\"${placement}\"]`)\n if (existing !== null) return existing\n const style = SURFACE_HOST_STYLES[placement]\n if (style === undefined) return undefined\n const host = element('div'); host.dataset.dshMobileSurfaceHost = placement; host.style.cssText = style\n document.body.append(host); return host\n }\n const shellLayer = (): HTMLElement => {\n const existing = document.querySelector<HTMLElement>('[data-dsh-mobile-extension-layer]')\n if (existing !== null) return existing\n const layer = element('div'); layer.dataset.dshMobileExtensionLayer = 'true'; layer.style.cssText = 'position:fixed;inset:0;z-index:1200;pointer-events:none;overflow:hidden'\n document.body.append(layer); return layer\n }\n const toast = (message: string): void => {\n const node = element('div'); node.textContent = message; node.style.cssText = 'position:absolute;top:16px;left:50%;transform:translateX(-50%);padding:9px 14px;border-radius:999px;background:#1f2937;color:white;font:14px system-ui;pointer-events:auto;box-shadow:0 8px 24px #0003'\n shellLayer().append(node); window.setTimeout(() => node.remove(), 2600)\n }\n const materializeNativeFile = (value: unknown): unknown => {\n if (typeof value !== 'object' || value === null || !('base64' in value) || !('name' in value)) return value\n const candidate = value as { base64?: unknown; name?: unknown; type?: unknown }\n if (typeof candidate.base64 !== 'string' || typeof candidate.name !== 'string') return value\n try {\n const encoded = atob(candidate.base64)\n const bytes = Uint8Array.from(encoded, character => character.charCodeAt(0))\n return new File([bytes], candidate.name, { type: typeof candidate.type === 'string' ? candidate.type : 'application/octet-stream' })\n } catch {\n return value\n }\n }\n const invokeNative = async (action: string, input: unknown, signal: AbortSignal): Promise<unknown> => {\n const abortReason = (): unknown => signal.reason ?? new DOMException('mobile extension disposed', 'AbortError')\n if (signal.aborted) throw abortReason()\n const bridge = window.__DSH_MOBILE_NATIVE__\n if (bridge !== undefined) {\n return new Promise<unknown>((resolve, reject) => {\n let settled = false\n const finish = (callback: () => void): void => { if (settled) return; settled = true; signal.removeEventListener('abort', onAbort); callback() }\n const onAbort = (): void => { finish(() => { reject(abortReason()) }) }\n signal.addEventListener('abort', onAbort, { once: true })\n void Promise.resolve().then(() => bridge.invoke(action, input)).then(\n value => { finish(() => { resolve(materializeNativeFile(value)) }) },\n error => { finish(() => { reject(error) }) },\n )\n })\n }\n if (action === 'share' && typeof navigator.share === 'function') { await navigator.share((input ?? {}) as ShareData); return { ok: true } }\n if (action === 'clipboard.read' && navigator.clipboard !== undefined) return { text: await navigator.clipboard.readText() }\n if (action === 'clipboard.write' && navigator.clipboard !== undefined) { await navigator.clipboard.writeText(typeof input === 'object' && input !== null && 'text' in input ? String((input as { text: unknown }).text) : ''); return { ok: true } }\n if (action === 'files.pick' || action === 'camera.capture') {\n const inputElement = element('input')\n inputElement.type = 'file'\n inputElement.accept = action === 'camera.capture' ? 'image/*' : '*/*'\n if (action === 'camera.capture') inputElement.capture = 'environment'\n inputElement.hidden = true\n return new Promise<File | undefined>((resolve, reject) => {\n let settled = false\n let cleaned = false\n let cleanupTimer = 0\n let watchdogTimer = 0\n const cleanup = (): void => {\n if (cleaned) return\n cleaned = true\n if (cleanupTimer !== 0) window.clearTimeout(cleanupTimer)\n if (watchdogTimer !== 0) window.clearTimeout(watchdogTimer)\n window.removeEventListener('focus', scheduleCancel)\n document.removeEventListener('visibilitychange', onVisibilityChange)\n signal.removeEventListener('abort', onAbort)\n inputElement.removeEventListener('change', onChange)\n inputElement.removeEventListener('cancel', onCancel)\n inputElement.remove()\n }\n const finish = (callback: () => void): void => { if (settled) return; settled = true; cleanup(); callback() }\n const onChange = (): void => { const file = inputElement.files?.[0]; finish(() => { resolve(file) }) }\n const onCancel = (): void => { finish(() => { resolve(undefined) }) }\n const onAbort = (): void => { finish(() => { reject(abortReason()) }) }\n const scheduleCancel = (): void => { if (!settled && cleanupTimer === 0) cleanupTimer = window.setTimeout(onCancel, 1000) }\n const onVisibilityChange = (): void => { if (document.visibilityState === 'visible') scheduleCancel() }\n inputElement.addEventListener('change', onChange, { once: true })\n inputElement.addEventListener('cancel', onCancel, { once: true })\n window.addEventListener('focus', scheduleCancel)\n document.addEventListener('visibilitychange', onVisibilityChange)\n signal.addEventListener('abort', onAbort, { once: true })\n watchdogTimer = window.setTimeout(onCancel, 300_000)\n try {\n document.body.append(inputElement)\n inputElement.click()\n } catch (error) {\n finish(() => { reject(error) })\n }\n })\n }\n throw new Error('native capability is unavailable')\n }\n const makeApi = (id: string, hostGeneration: string | undefined, controller: AbortController, surfaces: Map<string, ExtensionSurfaceEntry>, surfaceIds: Set<string>): MobileClientApi => {\n const ensureCurrent = (): void => { if (controller.signal.aborted) throw controller.signal.reason }\n const requestSignal = (signal?: AbortSignal | null): CombinedClientSignal => signal === undefined || signal === null\n ? { signal: controller.signal, cleanup: () => undefined }\n : combineClientSignalLifetime(controller.signal, signal)\n const mountSurface = (surface: MobileSurface): (() => void) => {\n ensureCurrent()\n if (!/^[a-z][a-z0-9-]{0,63}$/u.test(surface.id) || surface.label.length > 120) throw new Error('invalid mobile surface')\n return registerUniqueDisposable(surfaces, surfaceIds, surface.id, () => {\n const container = element('section'); container.dataset.dshMobileSurface = surface.id; container.hidden = surface.placement === 'page' || surface.placement === 'overlay'; container.style.cssText = surface.placement === 'page' || surface.placement === 'overlay' ? 'position:absolute;inset:0;overflow:auto;background:var(--dsw-alias-bg-layer-1,#fff);padding:16px;pointer-events:auto' : 'pointer-events:auto'\n const host = (): HTMLElement => surface.placement === 'page' || surface.placement === 'overlay' ? shellLayer() : surfaceHost(surface.placement) ?? shellLayer()\n const mounted = surface.mount(container)\n const dispose = (): void => { try { if (typeof mounted === 'function') mounted() } finally { container.remove() } }\n return { dispose, container, host }\n })\n }\n return {\n host: {\n invoke: (action: string, input: unknown) => {\n ensureCurrent()\n return mobileRequest(`/mobile-access/extensions/${encodeURIComponent(id)}/actions/${encodeURIComponent(action)}`, extensionActionRequestInit(hostGeneration, input, controller.signal)).then(async response => { const value = await response.json() as unknown; if (!response.ok) throw new Error(typeof value === 'object' && value !== null && 'error' in value ? String((value as { error: unknown }).error) : `HTTP ${String(response.status)}`); return value })\n },\n fetch: (path: string, init?: RequestInit) => {\n ensureCurrent()\n const target = extensionRouteUrl(id, path, location.href)\n const headers = extensionGenerationHeaders(hostGeneration, init?.headers)\n const lifetime = requestSignal(init?.signal)\n return mobileRequest(target.href, { ...init, headers, signal: lifetime.signal }).then(\n response => bindClientResponseLifetime(response, lifetime.cleanup),\n error => { lifetime.cleanup(); throw error },\n )\n },\n assetUrl: path => { ensureCurrent(); return extensionAssetUrl(id, hostGeneration, path, location.href).href },\n },\n ui: {\n registerSurface: mountSurface,\n open: surfaceId => { ensureCurrent(); const entry = surfaces.get(surfaceId); if (entry !== undefined) entry.container.hidden = false },\n close: surfaceId => { ensureCurrent(); const entry = surfaces.get(surfaceId); if (entry !== undefined) entry.container.hidden = true },\n toast: message => { ensureCurrent(); toast(message) },\n },\n native: {\n capabilities: async () => { ensureCurrent(); const bridge = window.__DSH_MOBILE_NATIVE__; return bridge === undefined ? ['files.pick', 'camera.capture', 'share', 'clipboard.read', 'clipboard.write'] : bridge.capabilities() },\n invoke: (action, input) => invokeNative(action, input, controller.signal),\n },\n signal: controller.signal, document, window,\n }\n }\n const activateDefinition = (definition: MobileClientDefinition, cycleSignal?: AbortSignal, commitGeneration?: () => void, hostGeneration?: string): Promise<boolean> => {\n const previousKey = activationKeys.get(definition.id)\n const key = previousKey?.definition === definition && previousKey.generation === hostGeneration\n ? previousKey\n : { definition, ...(hostGeneration === undefined ? {} : { generation: hostGeneration }) }\n activationKeys.set(definition.id, key)\n return activations.activate(\n definition.id,\n key,\n cycleSignal,\n controller => {\n const surfaces = new Map<string, ExtensionSurfaceEntry>()\n const surfaceIds = new Set<string>()\n let pendingDisposed = false\n const disposePending = (): void => {\n if (pendingDisposed) return\n pendingDisposed = true\n for (const surface of surfaces.values()) {\n try { surface.dispose() } catch { /* Dispose every staged surface independently. */ }\n }\n surfaces.clear()\n surfaceIds.clear()\n }\n const result = Promise.resolve().then(() => definition.activate(makeApi(definition.id, hostGeneration, controller, surfaces, surfaceIds))).then(cleanup => ({\n controller,\n surfaces,\n ...(typeof cleanup === 'function' ? { cleanup } : {}),\n }))\n return {\n result,\n cancel: disposePending,\n commit: value => {\n if (definitions.get(definition.id) !== definition || controller.signal.aborted) throw new Error('stale mobile extension activation')\n for (const surface of value.surfaces.values()) surface.host().append(surface.container)\n activeHostGenerations.set(definition.id, hostGeneration)\n commitGeneration?.()\n },\n dispose: value => {\n controller.abort(new DOMException('mobile extension disposed', 'AbortError'))\n try { value.cleanup?.() } finally { disposePending() }\n },\n }\n },\n )\n }\n const define = (definition: MobileClientDefinition): void => {\n if (disposed || definition.apiVersion !== 1 || !/^[a-z][a-z0-9-]{0,63}$/u.test(definition.id) || typeof definition.activate !== 'function') return\n if (expectedDefinitionId !== undefined && definition.id !== expectedDefinitionId) return\n definitions.set(definition.id, definition)\n if (started && expectedDefinitionId === undefined) void activateDefinition(definition)\n }\n let started = false\n window.dshMobile = Object.freeze({ register: mount => { legacyMount = mount }, define })\n for (const definition of queuedDefinitions.splice(0)) define(definition)\n let legacyJsEtag = ''\n let legacyJsModified = ''\n const refreshLegacy = async (signal: AbortSignal): Promise<boolean> => {\n const previousMount = legacyMount\n let pendingRoot: HTMLElement | undefined\n try {\n const headers: Record<string, string> = {}\n if (legacyJsEtag !== '') headers['if-none-match'] = legacyJsEtag\n if (legacyJsModified !== '') headers['if-modified-since'] = legacyJsModified\n const response = await fetch('/mobile-access/custom.js', { credentials: 'same-origin', cache: 'no-store', headers, signal })\n if (response.status === 304) return true\n if (!response.ok) return false\n const nextEtag = response.headers.get('etag') ?? ''\n const nextModified = response.headers.get('last-modified') ?? ''\n const next = await response.text()\n if (refreshAborted(signal)) return false\n if (next === legacySource) { legacyJsEtag = nextEtag; legacyJsModified = nextModified; return true }\n legacyMount = undefined\n const script = element('script'); script.textContent = `${next}\\n//# sourceURL=dsh-mobile-custom.js`; document.head.append(script); script.remove()\n if (refreshAborted(signal)) { legacyMount = previousMount; return false }\n const mount = legacyMount as MobileExtensionMount | undefined\n if (mount === undefined) {\n legacyDispose?.(); legacyDispose = undefined\n legacyRoot?.remove(); legacyRoot = undefined\n legacySource = next; legacyJsEtag = nextEtag; legacyJsModified = nextModified\n return true\n }\n const nextRoot = element('div'); pendingRoot = nextRoot; nextRoot.dataset.dshMobileExtension = 'true'; document.body.append(nextRoot)\n const nextDispose = mount({ document, request: mobileRequest, root: nextRoot, window })\n if (refreshAborted(signal)) { if (typeof nextDispose === 'function') nextDispose(); pendingRoot.remove(); pendingRoot = undefined; legacyMount = previousMount; return false }\n legacyDispose?.(); legacyRoot?.remove(); legacyRoot = nextRoot; pendingRoot = undefined; legacyDispose = typeof nextDispose === 'function' ? nextDispose : undefined; legacySource = next\n legacyJsEtag = nextEtag; legacyJsModified = nextModified\n return true\n } catch { pendingRoot?.remove(); legacyMount = previousMount; return false }\n }\n let legacyScriptRevision = ''\n let legacyStyleRevision = ''\n const disposeManifestExtension = (id: string): void => {\n activations.remove(id)\n styleNodes.get(id)?.remove(); styleNodes.delete(id); styleEtags.delete(id); scriptDigests.delete(id); activeHostGenerations.delete(id); activationKeys.delete(id)\n if (managedDefinitionIds.delete(id)) definitions.delete(id)\n }\n const managedManifestIdSources = (): readonly Iterable<string>[] => [styleNodes.keys(), styleEtags.keys(), scriptDigests.keys(), managedDefinitionIds]\n const clearManifestExtensions = (): void => {\n publishAuthoritativeExtensionIds(manifestExtensionIds, new Set(), managedManifestIdSources(), disposeManifestExtension)\n manifestEtag = ''\n }\n const refreshExtensions = async (signal: AbortSignal): Promise<boolean> => {\n try {\n const headers: Record<string, string> = {}\n if (manifestEtag !== '') headers['if-none-match'] = manifestEtag\n const response = await fetch('/mobile-access/extensions/manifest', { credentials: 'same-origin', cache: 'no-store', headers, signal })\n if (response.status === 404) {\n legacyScriptRevision = ''\n legacyStyleRevision = ''\n return handleMissingExtensionManifest(\n clearManifestExtensions,\n () => Promise.all([refreshLegacy(signal), refreshCssLegacy(legacyStyle, signal, legacyCssState)]),\n signal,\n )\n }\n if (response.status === 304) return true\n if (!response.ok) return false\n const nextManifestEtag = response.headers.get('etag') ?? ''\n const payload = parseMobileExtensionManifest(await response.json())\n if (refreshAborted(signal) || payload === undefined) return false\n const entries = payload.extensions\n const seen = new Set(entries.map(entry => entry.id))\n const scriptRevision = payload.legacy.scriptRevision\n const styleRevision = payload.legacy.styleRevision\n let refreshComplete = true\n if (scriptRevision === '' || scriptRevision !== legacyScriptRevision) {\n if (await refreshLegacy(signal)) legacyScriptRevision = scriptRevision\n else refreshComplete = false\n }\n if (styleRevision === '' || styleRevision !== legacyStyleRevision) {\n if (await refreshCssLegacy(legacyStyle, signal, legacyCssState)) legacyStyleRevision = styleRevision\n else refreshComplete = false\n }\n const commitStyle = (id: string, change: 'retain' | 'remove' | 'replace', css?: string, etag?: string): void => {\n if (change === 'retain') return\n const oldStyle = styleNodes.get(id)\n if (change === 'remove') {\n oldStyle?.remove()\n styleNodes.delete(id)\n styleEtags.delete(id)\n return\n }\n if (oldStyle?.textContent !== css) {\n const node = element('style'); node.dataset.dshMobileExtensionStyle = id; node.textContent = css ?? ''; document.head.append(node); styleNodes.set(id, node); oldStyle?.remove()\n }\n if (etag !== undefined && etag !== '') styleEtags.set(id, etag)\n }\n for (const entry of entries) {\n const hadActiveGeneration = activations.hasActive(entry.id)\n const previousHostGeneration = activeHostGenerations.get(entry.id)\n let previousDefinition: MobileClientDefinition | undefined\n let evaluatedDefinition = false\n try {\n let styleChange: 'retain' | 'remove' | 'replace' = entry.styleUrl === undefined ? 'remove' : 'retain'\n let pendingCss: string | undefined\n let pendingStyleEtag: string | undefined\n const cssUrl = typeof entry.styleUrl === 'string' ? entry.styleUrl : undefined\n if (cssUrl !== undefined) {\n const cssHeaders: Record<string, string> = {}\n const storedEtag = styleEtags.get(entry.id)\n if (storedEtag !== undefined) cssHeaders['if-none-match'] = storedEtag\n const cssResponse = await fetch(cssUrl, { credentials: 'same-origin', cache: 'no-store', headers: cssHeaders, signal })\n if (cssResponse.status !== 304) {\n if (!cssResponse.ok) throw new Error('mobile extension style failed to load')\n pendingStyleEtag = cssResponse.headers.get('etag') ?? undefined\n pendingCss = await cssResponse.text()\n styleChange = 'replace'\n if (refreshAborted(signal)) return false\n }\n }\n\n const scriptUrl = typeof entry.scriptUrl === 'string' ? entry.scriptUrl : undefined\n if (scriptUrl === undefined) {\n if (refreshAborted(signal)) return false\n commitStyle(entry.id, styleChange, pendingCss, pendingStyleEtag)\n activations.remove(entry.id)\n activeHostGenerations.delete(entry.id)\n scriptDigests.delete(entry.id)\n if (managedDefinitionIds.delete(entry.id)) definitions.delete(entry.id)\n } else {\n const scriptHeaders: Record<string, string> = {}\n const storedDigest = scriptDigests.get(entry.id)\n if (storedDigest !== undefined) scriptHeaders['if-none-match'] = storedDigest\n const scriptResponse = await fetch(scriptUrl, { credentials: 'same-origin', cache: 'no-store', headers: scriptHeaders, signal })\n let nextDigest: string | undefined\n if (scriptResponse.status !== 304) {\n if (!scriptResponse.ok) throw new Error('mobile extension script failed to load')\n const source = await scriptResponse.text()\n if (refreshAborted(signal)) return false\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(source))\n if (refreshAborted(signal)) return false\n const key = [...new Uint8Array(digest)].map(value => value.toString(16).padStart(2, '0')).join('')\n if (scriptDigests.get(entry.id) !== key) {\n previousDefinition = definitions.get(entry.id)\n try {\n expectedDefinitionId = entry.id\n try {\n const script = element('script'); script.textContent = `${source}\\n//# sourceURL=dsh-mobile-extension-${entry.id}.js`; document.head.append(script); script.remove()\n } finally { expectedDefinitionId = undefined }\n const nextDefinition = definitions.get(entry.id)\n if (nextDefinition === undefined || nextDefinition === previousDefinition) throw new Error('mobile extension did not define its manifest id')\n evaluatedDefinition = true\n nextDigest = key\n } catch (error) {\n if (previousDefinition === undefined) definitions.delete(entry.id)\n else definitions.set(entry.id, previousDefinition)\n throw error\n }\n }\n }\n const definition = definitions.get(entry.id)\n const commitGeneration = (): void => { commitStyle(entry.id, styleChange, pendingCss, pendingStyleEtag) }\n if (definition === undefined) throw new Error('mobile extension definition is unavailable')\n if (evaluatedDefinition || !activations.hasActive(entry.id) || activeHostGenerations.get(entry.id) !== entry.generation) {\n if (!await activateDefinition(definition, signal, commitGeneration, entry.generation)) throw new Error('mobile extension activation failed')\n } else {\n if (refreshAborted(signal)) return false\n commitGeneration()\n }\n managedDefinitionIds.add(entry.id)\n if (nextDigest !== undefined) scriptDigests.set(entry.id, nextDigest)\n }\n } catch {\n if (evaluatedDefinition) {\n if (previousDefinition === undefined) definitions.delete(entry.id)\n else definitions.set(entry.id, previousDefinition)\n }\n failClosedExtensionGenerationReplacement(\n hadActiveGeneration,\n previousHostGeneration,\n entry.generation,\n () => { disposeManifestExtension(entry.id) },\n )\n refreshComplete = false\n }\n }\n if (refreshAborted(signal)) return false\n if (refreshComplete) {\n publishAuthoritativeExtensionIds(manifestExtensionIds, seen, managedManifestIdSources(), disposeManifestExtension)\n manifestEtag = nextManifestEtag\n } else manifestEtag = ''\n return refreshComplete\n } catch { return false }\n }\n started = true\n for (const definition of definitions.values()) void activateDefinition(definition)\n const stopRefresh = startLifecycleRefreshScheduler(async signal => {\n await refreshExtensions(signal)\n })\n const stopEvents = startExtensionChangeStream(\n () => { stopRefresh.refresh() },\n undefined,\n payload => {\n // Host-pushed task completion: exact, no guessing. Each completed turn\n // keeps its own notification, and generic text avoids attributing a\n // background task to whichever session happens to be open on the phone.\n if (document.hidden !== true) return\n const parsed = parseTaskNotifyPayload(payload)\n if (parsed === undefined) return\n const language = resolveNativeMobileLanguage(\n document.documentElement.lang,\n navigator.languages.length > 0 ? [...navigator.languages] : [navigator.language],\n )\n const label = (italian: string, english: string, chinese: string): string =>\n language === 'it' ? italian : language === 'zh' ? chinese : english\n fireTaskNotifyEvent({\n kind: 'done',\n title: label('Attività completata', 'Task finished', '任务已完成'),\n body: label('Il tuo task DSH è terminato', 'Your DSH task finished', '你的 DSH 任务已完成'),\n tag: taskCompletionTag(parsed.sessionId, parsed.turn),\n })\n },\n () => { fireDeviceRevoked() },\n )\n return () => { disposed = true; stopEvents(); stopRefresh(); started = false; legacyDispose?.(); legacyDispose = undefined; legacyRoot?.remove(); legacyRoot = undefined; legacyStyle.remove(); activations.dispose(); for (const node of styleNodes.values()) node.remove(); styleNodes.clear(); const layer = document.querySelector('[data-dsh-mobile-extension-layer]'); layer?.remove(); for (const host of document.querySelectorAll('[data-dsh-mobile-surface-host]')) host.remove(); if (previous === undefined) delete window.dshMobile; else window.dshMobile = previous }\n}\n\ninterface LegacyCssState { etag: string; modified: string }\nasync function refreshCssLegacy(style: HTMLStyleElement, signal: AbortSignal, state: LegacyCssState): Promise<boolean> {\n try {\n const headers: Record<string, string> = {}\n if (state.etag !== '') headers['if-none-match'] = state.etag\n if (state.modified !== '') headers['if-modified-since'] = state.modified\n let response = await fetch('/mobile-access/custom.css', { credentials: 'same-origin', cache: 'no-store', headers, signal })\n if (response.status === 304 && style.textContent === '') {\n state.etag = ''\n state.modified = ''\n response = await fetch('/mobile-access/custom.css', { credentials: 'same-origin', cache: 'no-store', signal })\n }\n if (response.status === 304) return true\n if (response.ok) {\n const nextEtag = response.headers.get('etag') ?? ''\n const nextModified = response.headers.get('last-modified') ?? ''\n const css = await response.text()\n if (refreshAborted(signal)) return false\n style.textContent = css\n state.etag = nextEtag\n state.modified = nextModified\n return true\n }\n return false\n } catch { return false }\n}\n\n/** Theme-aware desktop Mobile Access panel styles. */\nexport const CONTROL_STYLES = `\n.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)}\n.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-l2,#e1e5eb);border-radius:18px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 18px 50px rgb(15 23 42 / 18%)}\n.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:var(--dsw-alias-label-primary-bluish,#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)}\n.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:var(--dsw-alias-label-primary,#dc2626);border-left:3px solid var(--dsw-alias-state-error-primary,#dc2626)}.dsh-mobile-control__release-notice[hidden]{display:none}.dsh-mobile-control__update-card{margin:0 0 10px;padding:11px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:12px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__update-card[hidden]{display:none}.dsh-mobile-control__update-card-title{margin:0 0 7px;color:var(--dsw-alias-label-primary,#16181d);font:650 13px/1.4 system-ui}.dsh-mobile-control__update-notes{display:block;box-sizing:border-box;max-height:150px;overflow-y:auto;margin:0 0 8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#e1e5eb);border-radius:9px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#475569);white-space:pre-wrap;word-break:break-word;font:11px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.dsh-mobile-control__update-notes[hidden]{display:none}.dsh-mobile-control__update-notice{margin:0 0 9px;padding:7px 9px;border-radius:8px;background:var(--dsw-alias-state-warn-tertiary,#fff4d6);color:var(--dsw-alias-state-warn-label,#92400e);font-size:11px;line-height:1.5}.dsh-mobile-control__update-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.dsh-mobile-control__update-actions .dsh-mobile-control__primary,.dsh-mobile-control__update-actions .dsh-mobile-control__secondary{min-height:36px;padding:0 12px}.dsh-mobile-control__app-download{display:flex;align-items:center;justify-content:space-between;box-sizing:border-box;min-height:44px;margin:0 0 10px;padding:8px 11px;border:1px solid var(--dsw-alias-border-l2,#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:var(--dsw-alias-label-primary-bluish,#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)}\n.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__http-frame-warning{box-sizing:border-box;margin:-4px 0 12px;padding:8px 10px;border:1px solid #d6a83f;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#fff7df);color:var(--dsw-alias-label-primary,#694b00);font-size:11px;line-height:1.5;overflow-wrap:anywhere}.dsh-mobile-control__http-frame-warning[hidden]{display:none}.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}\n.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: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-l2,#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:var(--dsw-alias-label-primary-bluish,#2563eb);background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-bg-layer-1,#f5f8ff));box-shadow:0 0 0 1px var(--dsw-alias-label-primary-bluish,#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:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8);font:650 11px/1.25 system-ui}.dsh-mobile-control__provider-badge.is-cpolar{background:var(--dsw-alias-state-success-tertiary,#eaf8f2);color:var(--dsw-alias-label-primary,#087454)}.dsh-mobile-control__provider-description{color:var(--dsw-alias-label-secondary,#606873);font-size:12px;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:var(--dsw-alias-label-primary-bluish,#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-l2,#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}\n.dsh-mobile-control__self-hosted{margin:8px 0 0;border:1px solid var(--dsw-alias-border-l2,#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:11px;line-height:1.35}.dsh-mobile-control__provider-badge.is-frp{background:var(--dsw-alias-bg-layer-1,#eef0f3);color:var(--dsw-alias-label-secondary,#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)}\n/* The cloudflared setup section deliberately reuses this card, so\n .dsh-mobile-control__cloudflared-setup is a DOM hook for targeting rather than a\n style rule of its own. */\n.dsh-mobile-control__cpolar-setup{margin:0 0 12px;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__cpolar-setup[hidden],.dsh-mobile-control__cpolar-account[hidden],.dsh-mobile-control__details[hidden],.dsh-mobile-control__view.is-remote .dsh-mobile-control__actions[hidden],.dsh-mobile-control__danger[hidden]{display:none}.dsh-mobile-control__component-status,.dsh-mobile-control__component-note{margin:0 0 10px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.55}.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__primary{width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:600 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__cpolar-account{margin-top:10px}.dsh-mobile-control__link-row{display:flex;flex-wrap:wrap;gap:6px 12px;margin:0 0 10px}.dsh-mobile-control__text-link{color:var(--dsw-alias-label-primary-bluish,#2563eb);font-size:11px;text-decoration:none}.dsh-mobile-control__text-link:hover{text-decoration:underline}.dsh-mobile-control__token-label{display:flex;flex-direction:column;gap:5px;margin:0 0 8px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__token{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2,#fff));color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__cpolar-connect{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;min-height:44px;padding:10px 14px;border-radius:12px;font:650 13px/1.2 system-ui;cursor:pointer;transition:background-color 160ms ease,border-color 160ms ease,opacity 160ms ease}.dsh-mobile-control__cpolar-connect:hover:not(:disabled){border-color:#1d4ed8;background:#1d4ed8}.dsh-mobile-control__cpolar-connect:active:not(:disabled){border-color:#1e40af;background:#1e40af}.dsh-mobile-control__cpolar-connect:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__details{margin:10px 0 0;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb);padding-top:9px}.dsh-mobile-control__details>summary{min-height:36px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:30px;cursor:pointer}.dsh-mobile-control__details-body{display:flex;flex-wrap:wrap;align-items:center;gap:7px 12px;padding:4px 0}.dsh-mobile-control__details-body p{flex:1 0 100%;margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__storage{display:block;flex:1 0 100%;max-width:100%;overflow:hidden;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#475569);font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__danger{flex:1 0 100%;min-height:44px;margin-top:3px;padding:7px 10px;border:1px solid var(--dsw-alias-state-error-primary,#dc2626);border-radius:9px;background:transparent;color:var(--dsw-alias-label-primary,#dc2626);font:12px/1.3 system-ui;cursor:pointer}\n.dsh-mobile-control__danger:hover:not(:disabled),.dsh-mobile-control__device-revoke:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgb(220 38 38 / 8%))}\n.dsh-mobile-control__frp-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-setup[hidden]{display:none}.dsh-mobile-control__frp-step{padding:11px 0}.dsh-mobile-control__frp-step + .dsh-mobile-control__frp-step{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__frp-step>strong{display:block;margin-bottom:3px;font-size:12px;line-height:1.4}.dsh-mobile-control__frp-step>p{margin:0 0 9px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes{margin:0 0 9px;padding:0;list-style:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__frp-changes li{position:relative;margin:0;padding-left:12px}.dsh-mobile-control__frp-changes li+li{margin-top:5px}.dsh-mobile-control__frp-changes li::before{content:\"•\";position:absolute;left:2px;color:var(--dsw-alias-label-tertiary,#98a1ad)}.dsh-mobile-control__frp-step>.dsh-mobile-control__frp-requirement{padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:11px}.dsh-mobile-control__frp-fields{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:8px}.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(5),.dsh-mobile-control__frp-fields>.dsh-mobile-control__field:nth-child(6){grid-column:1/-1}.dsh-mobile-control__frp-fields>.dsh-mobile-control__field[hidden]{display:none}.dsh-mobile-control__frp-fields select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__frp-fields select:disabled{cursor:not-allowed;opacity:.55}.dsh-mobile-control__frp-hint{margin:0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5}.dsh-mobile-control__field{display:flex;min-width:0;flex-direction:column;gap:5px;color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__field:nth-child(3),.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__field input{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__frp-action{box-sizing:border-box;width:100%;min-height:44px;padding:9px 12px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__frp-action:disabled{cursor:not-allowed;opacity:.5}.dsh-mobile-control__remote-workspace{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f7f8fa)}.dsh-mobile-control__stage-header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:9px}.dsh-mobile-control__stage-header .dsh-mobile-control__section-title{margin:0}.dsh-mobile-control__stage-meta{display:flex;min-width:0;align-items:center;justify-content:flex-end;gap:5px}.dsh-mobile-control__stage-value{max-width:115px;overflow:hidden;color:var(--dsw-alias-label-primary,#16181d);font:650 11px/1.3 system-ui;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__state-badge{flex:none;padding:3px 7px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-secondary,#606873);font:650 11px/1.25 system-ui}.dsh-mobile-control__state-badge.is-ready{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsw-alias-label-primary,#087454)}.dsh-mobile-control__state-badge.is-busy{background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8)}.dsh-mobile-control__state-badge.is-attention{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsw-alias-label-primary,#935100)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__status{box-sizing:border-box;margin:0 0 10px;padding:9px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:11px;line-height:1.45}.dsh-mobile-control__provider-setup-body{margin:0 0 10px}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup{margin:0}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__cpolar-setup>.dsh-mobile-control__section-title,.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__frp-setup>.dsh-mobile-control__section-title{display:none}.dsh-mobile-control__provider-setup-body>.dsh-mobile-control__details{margin:0;padding:9px 10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__actions{margin-top:2px}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__qr{margin:10px 0 0}.dsh-mobile-control__remote-workspace>.dsh-mobile-control__manage-row{margin-top:10px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}\n.dsh-mobile-control__frp-overview{display:grid;grid-template-columns:32px minmax(0,1fr);align-items:center;gap:10px;margin:0 0 10px;padding:10px;border:1px solid var(--dsw-alias-state-success-primary,#a9dfc9);border-radius:11px;background:var(--dsw-alias-state-success-tertiary,#edf9f4)}.dsh-mobile-control__frp-overview[hidden]{display:none}.dsh-mobile-control__frp-overview-mark{display:grid;width:32px;height:32px;place-items:center;border-radius:50%;background:#087454;color:#fff;font:700 15px/1 system-ui}.dsh-mobile-control__frp-overview-body{display:flex;min-width:0;flex-direction:column;gap:2px}.dsh-mobile-control__frp-overview-body strong{color:var(--dsw-alias-label-primary,#075d46);font-size:12px;line-height:1.35}.dsh-mobile-control__frp-overview-body span{overflow:hidden;color:var(--dsw-alias-label-secondary,#357061);font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__frp-group{margin:8px 0 0;overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__frp-group>summary{box-sizing:border-box;min-height:44px;padding:12px 34px 10px 12px;color:var(--dsw-alias-label-primary,#16181d);font:650 12px/1.4 system-ui;cursor:pointer}.dsh-mobile-control__frp-group[open]>summary{border-bottom:1px solid var(--dsw-alias-border-l2,#e1e5eb);background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step{padding:12px}.dsh-mobile-control__frp-group>.dsh-mobile-control__frp-step>strong:first-child{display:none}.dsh-mobile-control__frp-group .dsh-mobile-control__frp-step{border-top:0}.dsh-mobile-control__frp-setup>.dsh-mobile-control__frp-step{margin-bottom:8px;padding:10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:11px;background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__tunnel{margin:0 0 10px}.dsh-mobile-control__tunnel[hidden]{display:none}\n.dsh-mobile-control__lan-setup{box-sizing:border-box;margin:0 0 12px;padding:14px;border:1px solid #6f96db;border-radius:15px;background:var(--dsw-alias-bg-layer-1,#f5f8ff)}.dsh-mobile-control__lan-setup[hidden],.dsh-mobile-control__lan-setup-form[hidden]{display:none}.dsh-mobile-control__lan-setup>.dsh-mobile-control__section-title{margin-bottom:5px}.dsh-mobile-control__lan-setup>.dsh-mobile-control__intro{margin-bottom:12px}.dsh-mobile-control__lan-setup-form{display:grid;gap:9px}.dsh-mobile-control__lan-setup .dsh-mobile-control__field select{box-sizing:border-box;width:100%;min-height:44px;padding:9px 32px 9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:14px/1.4 system-ui}.dsh-mobile-control__lan-setup .dsh-mobile-control__component-note{margin:0}.dsh-mobile-control__lan-setup-actions{display:grid;grid-template-columns:1fr 1.35fr;gap:8px}.dsh-mobile-control__lan-setup-actions button{box-sizing:border-box;min-width:0;min-height:44px;padding:9px 10px;border-radius:10px;font:650 12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__lan-setup-actions button:disabled{cursor:wait;opacity:.55}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status{margin:10px 0 0;line-height:1.5}.dsh-mobile-control__lan-setup>.dsh-mobile-control__status:empty{display:none}\n.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:\":\"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:var(--dsw-alias-label-primary-bluish,#2563eb);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}.dsh-mobile-control__qr{display:flex;justify-content:center;margin:0 0 12px}.dsh-mobile-control__qr[hidden]{display:none}.dsh-mobile-control__qr img{border-radius:12px;background:#fff;padding:8px}\n.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:var(--dsw-alias-label-tertiary,#98a1ad);content:\"\"}.dsh-mobile-control__status.is-running::before{background:var(--dsw-alias-state-success-primary,#16a36a)}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}\n.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}\n.dsh-mobile-control__extensions{margin:0 0 12px;color:var(--dsw-alias-label-secondary,#606873);font-size:12px}\n.dsh-mobile-control__view.is-diagnostics{--dsh-diagnostic-ok:var(--dsw-alias-state-success-primary,#087454);--dsh-diagnostic-warning:var(--dsw-alias-state-warn-primary,#a35b00);--dsh-diagnostic-error:var(--dsw-alias-state-error-primary,#c62828);--dsh-diagnostic-info:var(--dsw-alias-label-secondary,#526071)}.dsh-mobile-control__diagnostic-summary{box-sizing:border-box;margin:0;padding:13px;border:1px solid var(--dsw-alias-border-l2,#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:var(--dsw-alias-bg-layer-1,#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-l2,#dbe1e8);color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.45}.dsh-mobile-control__diagnostic-summary.is-ok .dsh-mobile-control__diagnostic-summary-icon{background:var(--dsw-alias-state-success-tertiary,#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:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-summary.is-error .dsh-mobile-control__diagnostic-summary-icon{background:var(--dsw-alias-state-error-secondary,#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:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary-bluish,#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}\n.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}\n.dsh-mobile-control__diagnostic-toolbar{display:grid;grid-template-columns:1fr;gap:8px;margin-top:10px}.dsh-mobile-control__diagnostic-toolbar.has-report{grid-template-columns:1fr 1fr}.dsh-mobile-control__diagnostic-run,.dsh-mobile-control__diagnostic-copy{box-sizing:border-box;width:100%;min-height:44px;padding:9px 10px;border-radius:11px;font:650 12px/1.3 system-ui;cursor:pointer;touch-action:manipulation}.dsh-mobile-control__diagnostic-copy[hidden]{display:none}.dsh-mobile-control__diagnostic-run:disabled{cursor:wait;opacity:.58}.dsh-mobile-control__diagnostic-feedback{margin:8px 0 0;padding:8px 10px;border-radius:9px;background:var(--dsw-alias-state-business-tertiary,#eff6ff);color:var(--dsw-alias-label-primary,#1d4ed8);font-size:11px;line-height:1.45}.dsh-mobile-control__diagnostic-feedback[hidden]{display:none}\n.dsh-mobile-control__diagnostic-checks{display:grid;gap:12px;margin-top:12px;animation:dsh-diagnostic-reveal 160ms ease-out both}.dsh-mobile-control__diagnostic-checks[hidden]{display:none}.dsh-mobile-control__diagnostic-group{overflow:hidden;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__diagnostic-group-header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 11px;border-bottom:1px solid var(--dsw-alias-border-l2,#e1e5eb);background:var(--dsw-alias-bg-layer-1,#f8fafc)}.dsh-mobile-control__diagnostic-group-header h3{margin:0;font:650 11px/1.4 system-ui}.dsh-mobile-control__diagnostic-group-header span{color:var(--dsw-alias-label-secondary,#606873);font-size:11px}.dsh-mobile-control__diagnostic-list{display:flex;flex-direction:column}.dsh-mobile-control__diagnostic-check{display:grid;grid-template-columns:26px minmax(0,1fr);gap:9px;padding:11px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__diagnostic-check + .dsh-mobile-control__diagnostic-check{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__diagnostic-marker{position:relative;width:26px;height:26px;border-radius:50%;background:var(--dsw-alias-bg-layer-1,#edf1f5);color:var(--dsh-diagnostic-info)}.dsh-mobile-control__diagnostic-marker::before,.dsh-mobile-control__diagnostic-marker::after{position:absolute;content:\"\"}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-marker{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-marker::before{top:7px;left:7px;width:9px;height:5px;border-bottom:1.8px solid currentColor;border-left:1.8px solid currentColor;transform:rotate(-45deg)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker{background:var(--dsw-alias-state-error-secondary,#fdecec);color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker::before,.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker::before{top:6px;left:12px;width:2px;height:9px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-marker::after,.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-marker::after{bottom:6px;left:12px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-check.is-info .dsh-mobile-control__diagnostic-marker::before{top:6px;left:12px;width:2px;height:2px;border-radius:50%;background:currentColor}.dsh-mobile-control__diagnostic-check.is-info .dsh-mobile-control__diagnostic-marker::after{top:10px;left:12px;width:2px;height:9px;border-radius:2px;background:currentColor}.dsh-mobile-control__diagnostic-check-body{min-width:0}.dsh-mobile-control__diagnostic-check-header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.dsh-mobile-control__diagnostic-check-header strong{min-width:0;font-size:12px;line-height:1.4}.dsh-mobile-control__diagnostic-badge{flex:none;padding:2px 6px;border-radius:999px;background:var(--dsw-alias-bg-layer-1,#edf1f5);color:var(--dsh-diagnostic-info);font:650 11px/1.3 system-ui}.dsh-mobile-control__diagnostic-check.is-ok .dsh-mobile-control__diagnostic-badge{background:var(--dsw-alias-state-success-tertiary,#e6f7f0);color:var(--dsh-diagnostic-ok)}.dsh-mobile-control__diagnostic-check.is-warning .dsh-mobile-control__diagnostic-badge{background:var(--dsw-alias-state-warn-tertiary,#fff4dc);color:var(--dsh-diagnostic-warning)}.dsh-mobile-control__diagnostic-check.is-error .dsh-mobile-control__diagnostic-badge{background:var(--dsw-alias-state-error-secondary,#fdecec);color:var(--dsh-diagnostic-error)}.dsh-mobile-control__diagnostic-check p{margin:4px 0 0;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;line-height:1.5;overflow-wrap:anywhere}.dsh-mobile-control__diagnostic-check .dsh-mobile-control__diagnostic-action{margin-top:7px;padding:7px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f8fafc);color:var(--dsw-alias-label-primary,#16181d)}.dsh-mobile-control__diagnostic-action span{display:inline-block;margin-right:6px;color:var(--dsw-alias-label-primary-bluish,#2563eb);font-weight:700}.dsh-mobile-control__diagnostic-details{margin-top:12px}.dsh-mobile-control__diagnostic-details[hidden]{display:none}.dsh-mobile-control__diagnostic-details>summary{box-sizing:border-box;min-height:44px;line-height:44px}.dsh-mobile-control__diagnostic-report{box-sizing:border-box;max-height:220px;margin:4px 0 0;overflow:auto;padding:10px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-secondary,#606873);font:11px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}\n.dsh-mobile-control__diagnostic-checks{transition:opacity 150ms ease}.dsh-mobile-control__diagnostic-checks.is-refreshing{opacity:.52}\n@keyframes dsh-diagnostic-spin{to{transform:rotate(360deg)}}@keyframes dsh-diagnostic-reveal{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n.dsh-mobile-control__actions{display:flex;flex-wrap:nowrap;gap:6px}.dsh-mobile-control__actions[hidden],.dsh-mobile-control__manage-row[hidden]{display:none}.dsh-mobile-control__actions button{flex:1 1 0;min-width:0;min-height:44px;padding:8px 4px;border-radius:10px;font:12px/1.2 system-ui;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-l3,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}\n.dsh-mobile-control button:focus-visible,.dsh-mobile-control a:focus-visible,.dsh-mobile-control input:focus-visible,.dsh-mobile-control select:focus-visible,.dsh-mobile-control summary:focus-visible{outline:3px solid rgb(37 99 235 / 28%);outline-offset:2px}\n/* 42px matches the DSH sidebar footer row; it is not one of the panel target tiers. */\n.dsh-mobile-control__trigger{box-sizing:border-box;flex:1 1 auto;display:flex;align-items:center;gap:8px;width:100%;height:42px;margin:4px 0;padding:0 10px 0 8px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font-family:inherit;font-size:14px;line-height:22px;cursor:pointer;overflow:hidden}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger:active,.dsh-mobile-control__trigger[aria-expanded=\"true\"]{background:var(--dsw-alias-interactive-bg-active,#e8ebf0)}.dsh-mobile-control__trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px}.dsh-mobile-control__trigger.is-rail{flex:0 0 auto;width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;gap:0;border-radius:50%}.dsh-mobile-control__trigger-icon{display:block;flex:none}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n.dsh-mobile-control__manage-row{display:flex;justify-content:space-between;gap:8px;margin-top:10px}.dsh-mobile-control__manage{flex:1 1 0;min-width:0;min-height:36px;padding:6px 8px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:transparent;color:inherit;font:12px/1.3 system-ui;cursor:pointer}.dsh-mobile-control__devices{margin-top:10px;border:1px solid var(--dsw-alias-border-l2,#e1e5eb);border-radius:10px;padding:8px;max-height:220px;overflow-y:auto}.dsh-mobile-control__device-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px;margin:0}.dsh-mobile-control__device{display:flex;align-items:center;gap:8px;padding:6px 2px}.dsh-mobile-control__device + .dsh-mobile-control__device{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__device-label{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.dsh-mobile-control__device-meta{flex:none;color:var(--dsw-alias-label-secondary,#606873);font-size:11px;white-space:nowrap}.dsh-mobile-control__device-revoke{flex:none;min-height:36px;padding:4px 8px;border:1px solid var(--dsw-alias-state-error-primary,#dc2626);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary,#dc2626);font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__ws-paths{margin:10px 0 0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__ws-paths-list{margin:0 0 8px;padding:0;list-style:none}.dsh-mobile-control__ws-paths-empty{color:var(--dsw-alias-label-secondary,#606873);font-size:12px}.dsh-mobile-control__ws-paths-item{display:flex;align-items:center;gap:8px;padding:6px 0}.dsh-mobile-control__ws-paths-item + .dsh-mobile-control__ws-paths-item{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__ws-paths-item code{flex:1 1 0;min-width:0;overflow:hidden;font:11px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__ws-paths-remove{flex:none;min-height:36px;padding:4px 8px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:8px;background:transparent;color:inherit;font:12px/1.2 system-ui;cursor:pointer}.dsh-mobile-control__trigger,.dsh-mobile-control__diagnostic-entry{position:relative}.dsh-mobile-control__ws-dot{position:absolute;top:2px;right:2px;z-index:1;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff;font:650 11px/16px system-ui;text-align:center;cursor:pointer}.dsh-mobile-control__ws-dot::after{content:'';position:absolute;inset:-5px}.dsh-mobile-control__ws-sr-status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.dsh-mobile-control__ws-paths-row{display:flex;gap:8px}.dsh-mobile-control__ws-paths-row .dsh-mobile-control__primary{width:auto;flex:none;padding:9px 14px;border-radius:10px}.dsh-mobile-control__ws-paths-group{padding:6px 0}.dsh-mobile-control__ws-paths-group + .dsh-mobile-control__ws-paths-group{border-top:1px solid var(--dsw-alias-border-l2,#e1e5eb)}.dsh-mobile-control__ws-paths-group-head{display:flex;align-items:center;gap:8px}.dsh-mobile-control__ws-count{flex:none;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-state-business-tertiary,#e8f0ff);color:var(--dsw-alias-label-primary,#1d4ed8);font:650 11px/1.6 system-ui}.dsh-mobile-control__ws-paths-item.is-child{padding-left:12px}.dsh-mobile-control__ws-paths-group-head .dsh-mobile-control__primary,.dsh-mobile-control__ws-paths-item .dsh-mobile-control__primary{width:auto;flex:none;min-height:36px;padding:6px 12px;border-radius:10px}.dsh-mobile-control__ws-paths-row input{flex:1 1 0;min-width:0;box-sizing:border-box;min-height:44px;padding:9px 10px;border:1px solid var(--dsw-alias-border-l3,#cfd5dd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#16181d);font:16px/1.4 system-ui}.dsh-mobile-control__ws-paths-row button{flex:none}.dsh-mobile-control__ws-paths-detected-title{margin:0 0 2px;font:600 12px/1.4 system-ui}.dsh-mobile-control__ws-paths-advanced{margin-top:8px}.dsh-mobile-control__ws-paths-advanced summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#606873);font:12px/1.4 system-ui;user-select:none}.dsh-mobile-control__ws-paths-advanced summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,currentColor);outline-offset:2px;border-radius:4px}.dsh-mobile-control__ws-paths-advanced[open] summary{margin-bottom:6px}.dsh-mobile-control__ws-paths-advanced .dsh-mobile-control__status{margin:6px 0 0}\n@media (max-width:359px){.dsh-mobile-control__provider{min-height:68px}}@media (prefers-reduced-motion:reduce){.dsh-mobile-control__provider,.dsh-mobile-control__cpolar-connect{transition:none}.dsh-mobile-control__diagnostic-summary.is-running .dsh-mobile-control__diagnostic-summary-icon::before,.dsh-mobile-control__diagnostic-checks{animation:none}}\n\n.dsh-mobile-control__origin-setup{margin:0;padding:12px;border:1px solid var(--dsw-alias-border-l2,#dbe1e8);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff)}.dsh-mobile-control__origin-setup[hidden],.dsh-mobile-control__origin-backend[hidden],.dsh-mobile-control__origin-feedback[hidden]{display:none}.dsh-mobile-control__origin-fields{display:grid;grid-template-columns:minmax(0,1fr) 110px;gap:12px}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field{font-size:12px}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(1),.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(4){grid-column:1/-1}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(2){grid-column:1}.dsh-mobile-control__origin-fields>.dsh-mobile-control__field:nth-child(3){grid-column:2}.dsh-mobile-control__origin-warning{margin:12px 0;padding:10px 12px;border-left:3px solid var(--dsw-alias-state-warn-primary,#a56810);border-radius:4px;background:var(--dsw-alias-bg-layer-1,#f3f5f8);color:var(--dsw-alias-label-primary,#384152);font-size:12px;line-height:1.6}.dsh-mobile-control__origin-feedback{font-size:12px;line-height:1.6;overflow-wrap:anywhere}.dsh-mobile-control__origin-feedback.is-error{color:var(--dsw-alias-state-error-primary,#bc3030)}.dsh-mobile-control__origin-fields input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary,#bc3030)}.dsh-mobile-control__origin-backend{display:flex;min-width:0;flex-direction:column;align-items:stretch;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#dbe1e8)}.dsh-mobile-control__origin-backend button{align-self:flex-start;min-height:44px}.dsh-mobile-control__self-hosted-body .is-origin{margin-top:8px}\n`\n\n/** Mount the desktop control or mobile feature enhancements. */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => {\n if (window.__DSH_MOBILE_FRONTEND__ !== 'dedicated') return\n return trustAuthenticatedGatewayConnection(ctx.get('connection'))\n }, 'dsh-mobile: authenticated gateway client trust')\n\n ctx.effect(() => {\n const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__)\n const style = element('style'); style.dataset.plugin = 'dsh-mobile'; style.textContent = desktopAdmin\n ? CONTROL_STYLES\n : NATIVE_MOBILE_STYLES\n document.head.append(style)\n if (!desktopAdmin) {\n const removeSettingsAction = ctx.slots.inject('settings.general.item', () => {\n const removeSwitchComputer = ctx.slots.register({\n name: 'settings.general.item',\n id: 'dsh-mobile-switch-computer',\n order: 100,\n }, MobileSwitchComputerRow)\n const removeTaskNotifications = ctx.slots.register({\n name: 'settings.general.item',\n id: 'dsh-mobile-task-notifications',\n order: 110,\n }, MobileTaskNotificationRow)\n return () => { removeTaskNotifications(); removeSwitchComputer() }\n })\n const removeCustom = installCustomAssets()\n const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface)\n return () => { removeSettingsAction(); removeCustom(); removeSurface(); style.remove() }\n }\n const removeControl = installDshLanguageBoundSurface(() => {\n const control = installControl()\n const triggerLocale = selectedMobileControlLocale()\n const t = controlTranslator(triggerLocale)\n const disposeSlot = ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register<{ wide: boolean }>({ name: 'sidebar.footer.action', id: 'dsh-mobile' }, ({ wide }) => createElement('button', {\n 'aria-expanded': control.isOpen(),\n 'aria-controls': CONTROL_PANEL_ID,\n 'aria-label': t('mobileAccess'),\n className: `dsh-mobile-control__trigger${wide ? '' : ' is-rail'}`,\n lang: triggerLocale,\n type: 'button',\n title: t('mobileAccess'),\n onClick: control.toggle,\n }, createElement('svg', {\n 'aria-hidden': true,\n className: 'dsh-mobile-control__trigger-icon',\n focusable: false,\n width: wide ? 16 : 18,\n height: wide ? 16 : 18,\n viewBox: '0 0 16 16',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.5,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n }, createElement('rect', { x: 4, y: 1, width: 8, height: 14, rx: 2 }), createElement('path', { d: 'M7 12h2' })), wide ? createElement('span', { className: 'dsh-mobile-control__trigger-label' }, t('mobileAccess')) : undefined)))\n return () => { disposeSlot(); control.remove() }\n })\n return () => { removeControl(); style.remove() }\n }, 'dsh-mobile: stock mobile adaptation and local control')\n}\n\n/** Client services required by the mobile adaptation. */\nexport const inject: readonly string[] = ['slots']\n"],"mappings":"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"}
|