dsh-mobile 0.8.4 → 0.8.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 +29 -0
- package/CONTRIBUTORS.md +16 -4
- package/README.en.md +5 -5
- package/README.md +5 -5
- package/apps/mobile/README.md +2 -0
- package/apps/mobile/README.zh-CN.md +2 -0
- package/docs/CLIENT_MODULES.en.md +1 -1
- package/docs/CLIENT_MODULES.md +1 -1
- package/docs/CONNECTION_DIAGNOSTICS.en.md +2 -0
- package/docs/CONNECTION_DIAGNOSTICS.md +2 -0
- package/docs/IROH.en.md +1 -0
- package/docs/IROH.md +1 -0
- package/docs/README.md +1 -1
- package/docs/SLOW_CONNECTIONS.en.md +8 -0
- package/docs/SLOW_CONNECTIONS.md +8 -0
- package/docs/WEB_PUSH.en.md +20 -4
- package/docs/WEB_PUSH.md +20 -4
- package/lib/cli.js +156 -24
- package/lib/cli.js.map +1 -1
- package/lib/client.js +267 -18
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +59 -1
- package/lib/index.mjs +1134 -64
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/packages/question-fixes/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -803,7 +803,7 @@ window.__ModuleLoader__.load({
|
|
|
803
803
|
lanSetupConfiguring: "Creating private certificates and local network rules…",
|
|
804
804
|
lanSetupRequired: "Local network setup has not been completed. Choose a network above to continue.",
|
|
805
805
|
lanSetupRestart: "Setup complete for {origin}. Restart DSH once to start local network access.",
|
|
806
|
-
lanSetupFailed: "Setup was not completed.
|
|
806
|
+
lanSetupFailed: "Setup was not completed. Existing firewall rules were retained when possible. Approve the Windows prompt and retry; if it still fails, check dsh-mobile.log for the exact firewall error.",
|
|
807
807
|
lanChangeTitle: "Change network",
|
|
808
808
|
lanChangeIntro: "Move local network access to another interface. The private CA and paired devices are kept; connected phones reconnect with the new address.",
|
|
809
809
|
lanChangeSelect: "New network",
|
|
@@ -833,7 +833,7 @@ window.__ModuleLoader__.load({
|
|
|
833
833
|
lanSetupConfiguring: "Creazione dei certificati privati e delle regole di rete locale…",
|
|
834
834
|
lanSetupRequired: "La configurazione della rete locale non è completa. Scegli una rete qui sopra per continuare.",
|
|
835
835
|
lanSetupRestart: "Configurazione completata per {origin}. Riavvia DSH una volta per attivare l’accesso locale.",
|
|
836
|
-
lanSetupFailed: "Configurazione non completata.
|
|
836
|
+
lanSetupFailed: "Configurazione non completata. Le regole firewall esistenti sono state mantenute quando possibile. Approva la richiesta di Windows e riprova; se fallisce ancora, controlla dsh-mobile.log per l’errore esatto.",
|
|
837
837
|
lanChangeTitle: "Cambia rete",
|
|
838
838
|
lanChangeIntro: "Sposta l’accesso alla rete locale su un’altra interfaccia. La CA privata e i dispositivi abbinati restano; i telefoni collegati si riconnettono con il nuovo indirizzo.",
|
|
839
839
|
lanChangeSelect: "Nuova rete",
|
|
@@ -863,7 +863,7 @@ window.__ModuleLoader__.load({
|
|
|
863
863
|
lanSetupConfiguring: "正在生成私有证书并配置局域网规则…",
|
|
864
864
|
lanSetupRequired: "尚未完成局域网配置,请选择上方网络继续。",
|
|
865
865
|
lanSetupRestart: "已为 {origin} 完成配置。请重启一次 DSH,局域网访问会自动开启。",
|
|
866
|
-
lanSetupFailed: "
|
|
866
|
+
lanSetupFailed: "配置未完成。原有防火墙规则已尽量保留;请允许 Windows 授权提示后重试,若仍失败请查看 dsh-mobile.log 中的具体错误。",
|
|
867
867
|
lanChangeTitle: "更换网络",
|
|
868
868
|
lanChangeIntro: "把局域网访问切换到另一块网卡。私有 CA 与已配对设备都会保留,已连接的手机需要用新地址重新连接。",
|
|
869
869
|
lanChangeSelect: "新网络",
|
|
@@ -1172,6 +1172,7 @@ window.__ModuleLoader__.load({
|
|
|
1172
1172
|
irohConnecting: "Sidecar connected; starting the DSH authentication gateway…",
|
|
1173
1173
|
irohCompanionMissing: "No verified iroh sidecar is available. Use Install official component below; a separate companion plugin is optional.",
|
|
1174
1174
|
irohDataDirectoryInvalid: "Could not prepare the private sidecar directory. Check that DSH can manage its own profile directory, then retry.",
|
|
1175
|
+
irohDataDirectoryInsecure: "Windows parent folders of the iroh state path must not grant write or replacement rights to other users. Choose a private profile directory or correct the parent ACLs, then retry.",
|
|
1175
1176
|
irohSidecarInvalid: "The installed sidecar failed checksum verification. Reinstall the component.",
|
|
1176
1177
|
irohDownloadFailed: "The sidecar download failed before verification. Check the network and retry.",
|
|
1177
1178
|
irohPlatformUnsupported: "iroh sidecars are published for Windows x64 and Linux x64 only.",
|
|
@@ -1207,6 +1208,7 @@ window.__ModuleLoader__.load({
|
|
|
1207
1208
|
irohConnecting: "Sidecar connesso; avvio del gateway di autenticazione DSH…",
|
|
1208
1209
|
irohCompanionMissing: "Nessun sidecar iroh verificato disponibile. Usa Installa componente ufficiale qui sotto; il plugin complementare è facoltativo.",
|
|
1209
1210
|
irohDataDirectoryInvalid: "Impossibile preparare la directory privata del sidecar. Verifica che DSH possa gestire la propria directory del profilo, poi riprova.",
|
|
1211
|
+
irohDataDirectoryInsecure: "Le cartelle Windows superiori del percorso di stato iroh non devono concedere ad altri utenti diritti di scrittura o sostituzione. Scegli una directory profilo privata o correggi le ACL superiori, poi riprova.",
|
|
1210
1212
|
irohSidecarInvalid: "Il sidecar installato non ha superato la verifica del checksum. Reinstalla il componente.",
|
|
1211
1213
|
irohDownloadFailed: "Download del sidecar non riuscito prima della verifica. Controlla la rete e riprova.",
|
|
1212
1214
|
irohPlatformUnsupported: "I sidecar iroh sono pubblicati solo per Windows x64 e Linux x64.",
|
|
@@ -1242,6 +1244,7 @@ window.__ModuleLoader__.load({
|
|
|
1242
1244
|
irohConnecting: "Sidecar 已连接,正在启动 DSH 认证网关…",
|
|
1243
1245
|
irohCompanionMissing: "尚无校验通过的 iroh 组件,请点击下方「安装官方组件」;无需另装配套插件。",
|
|
1244
1246
|
irohDataDirectoryInvalid: "无法准备 sidecar 私有目录。请确认 DSH 能管理自己的配置目录后重试。",
|
|
1247
|
+
irohDataDirectoryInsecure: "iroh 状态路径的 Windows 上级目录不能向其他用户授予写入或替换权限;请改用私有配置目录或修正上级目录 ACL 后重试。",
|
|
1245
1248
|
irohSidecarInvalid: "已安装的 sidecar 哈希校验失败,请重装组件。",
|
|
1246
1249
|
irohDownloadFailed: "下载 sidecar 失败,还没开始校验。请检查网络后重试。",
|
|
1247
1250
|
irohPlatformUnsupported: "iroh sidecar 仅发布 Windows x64 与 Linux x64 版本。",
|
|
@@ -2213,6 +2216,27 @@ window.__ModuleLoader__.load({
|
|
|
2213
2216
|
frpConfigurationMissingAttach: "填写既有 frps 信息,在本机安装 frpc,然后保存并验证连接。",
|
|
2214
2217
|
purgeFrpConfirmSelfSigned: "要停止 FRP 并删除本机 frpc、Token、配置、证书、私钥、身份记录、临时文件和日志吗?通过此自签入口配对的手机都需重新配对;VPS 不会改变。"
|
|
2215
2218
|
});
|
|
2219
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
|
|
2220
|
+
macPermissionTitle: "请确认已开启「完全磁盘访问权限」",
|
|
2221
|
+
macPermissionSummary: "开启后,远程使用期间不会再弹出需要确认的文件夹授权窗口,连接也不会因此中断。",
|
|
2222
|
+
macPermissionSteps: "系统设置 → 隐私与安全性 → 完全磁盘访问权限 → 打开 DeepSeek Harness",
|
|
2223
|
+
macPermissionOpen: "打开系统设置",
|
|
2224
|
+
macPermissionDismiss: "我知道了"
|
|
2225
|
+
});
|
|
2226
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
2227
|
+
macPermissionTitle: "Confirm Full Disk Access is enabled",
|
|
2228
|
+
macPermissionSummary: "With it enabled, remote use will not raise folder authorization dialogs, and the connection will not stall on them.",
|
|
2229
|
+
macPermissionSteps: "System Settings → Privacy & Security → Full Disk Access → turn on DeepSeek Harness",
|
|
2230
|
+
macPermissionOpen: "Open System Settings",
|
|
2231
|
+
macPermissionDismiss: "Got it"
|
|
2232
|
+
});
|
|
2233
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.it, {
|
|
2234
|
+
macPermissionTitle: "Verifica che l’accesso completo al disco sia attivo",
|
|
2235
|
+
macPermissionSummary: "Se attivo, durante l’uso remoto non compariranno finestre di autorizzazione per le cartelle e la connessione non si bloccherà.",
|
|
2236
|
+
macPermissionSteps: "Impostazioni di Sistema → Privacy e sicurezza → Accesso completo al disco → attiva DeepSeek Harness",
|
|
2237
|
+
macPermissionOpen: "Apri Impostazioni di Sistema",
|
|
2238
|
+
macPermissionDismiss: "Ho capito"
|
|
2239
|
+
});
|
|
2216
2240
|
/** Caddy snippet owned entirely by DSH Mobile; the main Caddyfile only imports it. */
|
|
2217
2241
|
const FRP_CADDY_SNIPPET_PATH = "/etc/caddy/dsh-mobile-dsh.caddy";
|
|
2218
2242
|
/** First line of the owned snippet; also the legacy whole-file marker. */
|
|
@@ -3767,6 +3791,28 @@ window.__ModuleLoader__.load({
|
|
|
3767
3791
|
if (bridge === void 0 || typeof bridge.capabilities !== "function" || typeof bridge.invoke !== "function") return void 0;
|
|
3768
3792
|
return bridge;
|
|
3769
3793
|
}
|
|
3794
|
+
/** Encode one opaque Session id for the App's internal notification route. */
|
|
3795
|
+
function taskNotificationNavigate(sessionId) {
|
|
3796
|
+
if (sessionId === "") return void 0;
|
|
3797
|
+
const bytes = new TextEncoder().encode(sessionId);
|
|
3798
|
+
let binary = "";
|
|
3799
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
3800
|
+
const route = `/?dsh-session=${globalThis.btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "")}`;
|
|
3801
|
+
return route.length <= 270 ? route : void 0;
|
|
3802
|
+
}
|
|
3803
|
+
/** Decode only the App's bounded internal Session route. */
|
|
3804
|
+
function decodeTaskNotificationNavigate(value) {
|
|
3805
|
+
if (value === null || value === void 0 || !/^[A-Za-z0-9_-]{1,256}$/u.test(value)) return void 0;
|
|
3806
|
+
try {
|
|
3807
|
+
const padded = value.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - value.length % 4) % 4);
|
|
3808
|
+
const binary = globalThis.atob(padded);
|
|
3809
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
3810
|
+
const decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3811
|
+
return decoded === "" ? void 0 : decoded;
|
|
3812
|
+
} catch {
|
|
3813
|
+
return;
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3770
3816
|
/** Parse a `task-notify` SSE payload; rejects anything malformed or empty. */
|
|
3771
3817
|
function parseTaskNotifyPayload(value) {
|
|
3772
3818
|
let record;
|
|
@@ -3821,7 +3867,8 @@ window.__ModuleLoader__.load({
|
|
|
3821
3867
|
return bridge.invoke("notification.notify", {
|
|
3822
3868
|
title: event.title,
|
|
3823
3869
|
body: event.body,
|
|
3824
|
-
tag: event.tag
|
|
3870
|
+
tag: event.tag,
|
|
3871
|
+
...event.navigate === void 0 ? {} : { navigate: event.navigate }
|
|
3825
3872
|
});
|
|
3826
3873
|
}).then(() => void 0, (error) => {
|
|
3827
3874
|
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
|
|
@@ -9558,10 +9605,7 @@ window.__ModuleLoader__.load({
|
|
|
9558
9605
|
});
|
|
9559
9606
|
if (currentHostSubscription) consentedSubscriptions.delete(currentHostSubscription);
|
|
9560
9607
|
currentHostSubscription = void 0;
|
|
9561
|
-
|
|
9562
|
-
if (sub) {
|
|
9563
|
-
if (await sub.unsubscribe().catch(() => false) && ownedSubscription === sub) ownedSubscription = void 0;
|
|
9564
|
-
}
|
|
9608
|
+
ownedSubscription = void 0;
|
|
9565
9609
|
if (await refresh(token) && current(token) && operation === attempt && config?.status === "disabled") publish({
|
|
9566
9610
|
state: "disabled",
|
|
9567
9611
|
preferences: config.preferences
|
|
@@ -9722,7 +9766,13 @@ window.__ModuleLoader__.load({
|
|
|
9722
9766
|
const MOBILE_PUSH_SETTINGS_COPY = {
|
|
9723
9767
|
en: {
|
|
9724
9768
|
title: "Web push notifications",
|
|
9725
|
-
desc: "Receive notifications when this page is closed.",
|
|
9769
|
+
desc: "Receive notifications when this page is closed. Disable stops sends but keeps the browser subscription so it can be reused safely.",
|
|
9770
|
+
permission: {
|
|
9771
|
+
granted: "System permission: granted. A reinstalled Home Screen app can inherit this site-level grant.",
|
|
9772
|
+
default: "System permission: not decided.",
|
|
9773
|
+
denied: "System permission: blocked.",
|
|
9774
|
+
unknown: "System permission: unavailable."
|
|
9775
|
+
},
|
|
9726
9776
|
enable: "Enable",
|
|
9727
9777
|
disable: "Disable",
|
|
9728
9778
|
test: "Send test",
|
|
@@ -9747,7 +9797,13 @@ window.__ModuleLoader__.load({
|
|
|
9747
9797
|
},
|
|
9748
9798
|
zh: {
|
|
9749
9799
|
title: "网页推送通知",
|
|
9750
|
-
desc: "
|
|
9800
|
+
desc: "关闭此页面后仍可接收通知。停用会停止发送,但保留浏览器订阅,便于安全地重新启用。",
|
|
9801
|
+
permission: {
|
|
9802
|
+
granted: "系统权限:已允许。重新安装的主屏幕 App 可能继承站点级授权。",
|
|
9803
|
+
default: "系统权限:尚未选择。",
|
|
9804
|
+
denied: "系统权限:已阻止。",
|
|
9805
|
+
unknown: "系统权限:不可读取。"
|
|
9806
|
+
},
|
|
9751
9807
|
enable: "启用",
|
|
9752
9808
|
disable: "停用",
|
|
9753
9809
|
test: "发送测试",
|
|
@@ -9772,7 +9828,13 @@ window.__ModuleLoader__.load({
|
|
|
9772
9828
|
},
|
|
9773
9829
|
it: {
|
|
9774
9830
|
title: "Notifiche Web push",
|
|
9775
|
-
desc: "Ricevi notifiche quando la pagina è chiusa.",
|
|
9831
|
+
desc: "Ricevi notifiche quando la pagina è chiusa. La disattivazione interrompe gli invii ma conserva l’iscrizione del browser per poterla riutilizzare.",
|
|
9832
|
+
permission: {
|
|
9833
|
+
granted: "Permesso di sistema: concesso. Una Web App reinstallata può ereditare questa autorizzazione del sito.",
|
|
9834
|
+
default: "Permesso di sistema: non ancora scelto.",
|
|
9835
|
+
denied: "Permesso di sistema: bloccato.",
|
|
9836
|
+
unknown: "Permesso di sistema: non disponibile."
|
|
9837
|
+
},
|
|
9776
9838
|
enable: "Attiva",
|
|
9777
9839
|
disable: "Disattiva",
|
|
9778
9840
|
test: "Invia test",
|
|
@@ -9821,6 +9883,7 @@ window.__ModuleLoader__.load({
|
|
|
9821
9883
|
if (snapshot.state === "unsupported") return null;
|
|
9822
9884
|
const prefs = snapshot.preferences;
|
|
9823
9885
|
const Switch = switchComponent ?? requirePublicMobilePushSwitch();
|
|
9886
|
+
const permission = typeof Notification === "undefined" ? "unknown" : Notification.permission;
|
|
9824
9887
|
const toggle = (key, checked) => {
|
|
9825
9888
|
if (!prefs) return;
|
|
9826
9889
|
controller.updatePreferences({
|
|
@@ -9842,7 +9905,7 @@ window.__ModuleLoader__.load({
|
|
|
9842
9905
|
}, (0, react.createElement)("div", { className: "dsh-mobile-settings_title" }, copy.title), (0, react.createElement)("div", {
|
|
9843
9906
|
className: "dsh-mobile-settings_desc",
|
|
9844
9907
|
"aria-live": "polite"
|
|
9845
|
-
}, message(snapshot, copy)), prefs && Switch !== void 0 ? (0, react.createElement)("div", { className: "dsh-mobile-push-categories" }, ...KEYS.map((key, index) => {
|
|
9908
|
+
}, message(snapshot, copy)), (0, react.createElement)("div", { className: "dsh-mobile-settings_desc" }, copy.permission[permission]), prefs && Switch !== void 0 ? (0, react.createElement)("div", { className: "dsh-mobile-push-categories" }, ...KEYS.map((key, index) => {
|
|
9846
9909
|
const name = copy.categories[index];
|
|
9847
9910
|
if (name === void 0) return null;
|
|
9848
9911
|
return (0, react.createElement)("label", {
|
|
@@ -9883,7 +9946,6 @@ window.__ModuleLoader__.load({
|
|
|
9883
9946
|
}, copy.busy)));
|
|
9884
9947
|
}
|
|
9885
9948
|
// src/mobile-push/navigation.ts
|
|
9886
|
-
const REF_PARAMETER = "mobile-push";
|
|
9887
9949
|
/**
|
|
9888
9950
|
* Consume one authenticated tap reference after the public Workspace and Session
|
|
9889
9951
|
* list baselines are ready. Return the disposal function for list subscriptions.
|
|
@@ -9894,15 +9956,16 @@ window.__ModuleLoader__.load({
|
|
|
9894
9956
|
location: window.location,
|
|
9895
9957
|
history: window.history
|
|
9896
9958
|
};
|
|
9959
|
+
const refParameter = options.parameter ?? "mobile-push";
|
|
9897
9960
|
const params = new URLSearchParams(browser.location.search);
|
|
9898
|
-
const reference = params.get(
|
|
9961
|
+
const reference = params.get(refParameter);
|
|
9899
9962
|
let disposed = false;
|
|
9900
9963
|
let consumed = false;
|
|
9901
9964
|
let resolving = false;
|
|
9902
9965
|
let opened = false;
|
|
9903
9966
|
const clearReference = () => {
|
|
9904
|
-
if (!params.has(
|
|
9905
|
-
params.delete(
|
|
9967
|
+
if (!params.has(refParameter)) return;
|
|
9968
|
+
params.delete(refParameter);
|
|
9906
9969
|
const query = params.toString();
|
|
9907
9970
|
const url = `${browser.location.pathname}${query ? `?${query}` : ""}${browser.location.hash}`;
|
|
9908
9971
|
browser.history.replaceState(null, "", url);
|
|
@@ -11388,6 +11451,111 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
11388
11451
|
onClick: invoke
|
|
11389
11452
|
}, state === "busy" ? copy.busy : state === "success" ? copy.reopen : state === "error" ? copy.retry : copy.action));
|
|
11390
11453
|
}
|
|
11454
|
+
function mobileBackgroundNotificationCopy(locale) {
|
|
11455
|
+
if (locale === "zh") return {
|
|
11456
|
+
title: "后台任务提醒",
|
|
11457
|
+
description: "App 页面被系统挂起后,通过可关闭的常驻连接继续接收任务完成提醒。",
|
|
11458
|
+
enabled: "关闭后台提醒",
|
|
11459
|
+
disabled: "开启后台提醒",
|
|
11460
|
+
busy: "正在处理…",
|
|
11461
|
+
error: "无法更新后台提醒;请确认任务通知权限和当前连接。"
|
|
11462
|
+
};
|
|
11463
|
+
if (locale === "it") return {
|
|
11464
|
+
title: "Promemoria in background",
|
|
11465
|
+
description: "Continua a ricevere gli avvisi quando Android sospende la pagina, tramite una connessione persistente disattivabile.",
|
|
11466
|
+
enabled: "Disattiva promemoria",
|
|
11467
|
+
disabled: "Attiva promemoria",
|
|
11468
|
+
busy: "Operazione…",
|
|
11469
|
+
error: "Impossibile aggiornare i promemoria; controlla il permesso notifiche e la connessione attiva."
|
|
11470
|
+
};
|
|
11471
|
+
return {
|
|
11472
|
+
title: "Background task reminders",
|
|
11473
|
+
description: "Keep receiving completion alerts after Android suspends the page, through a dismissible persistent connection.",
|
|
11474
|
+
enabled: "Disable background reminders",
|
|
11475
|
+
disabled: "Enable background reminders",
|
|
11476
|
+
busy: "Working…",
|
|
11477
|
+
error: "Could not update background reminders. Check notification permission and the active connection."
|
|
11478
|
+
};
|
|
11479
|
+
}
|
|
11480
|
+
/** Android-only opt-in for the foreground SSE fallback when the WebView is suspended. */
|
|
11481
|
+
function MobileBackgroundNotificationRow() {
|
|
11482
|
+
const [supported, setSupported] = (0, react.useState)(false);
|
|
11483
|
+
const [enabled, setEnabled] = (0, react.useState)(false);
|
|
11484
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
11485
|
+
const [error, setError] = (0, react.useState)(false);
|
|
11486
|
+
const generation = (0, react.useRef)(0);
|
|
11487
|
+
(0, react.useEffect)(() => {
|
|
11488
|
+
let active = true;
|
|
11489
|
+
const refresh = () => {
|
|
11490
|
+
const attempt = ++generation.current;
|
|
11491
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
11492
|
+
if (bridge === void 0) {
|
|
11493
|
+
if (active) {
|
|
11494
|
+
setSupported(false);
|
|
11495
|
+
setError(false);
|
|
11496
|
+
}
|
|
11497
|
+
return;
|
|
11498
|
+
}
|
|
11499
|
+
Promise.resolve(bridge.capabilities()).then(async (capabilities) => {
|
|
11500
|
+
const available = capabilities.includes("notification.background.get") && capabilities.includes("notification.background.set");
|
|
11501
|
+
if (!active || attempt !== generation.current) return;
|
|
11502
|
+
setSupported(available);
|
|
11503
|
+
if (!available) return;
|
|
11504
|
+
const result = await bridge.invoke("notification.background.get", {});
|
|
11505
|
+
if (!active || attempt !== generation.current) return;
|
|
11506
|
+
if (typeof result?.enabled !== "boolean") throw new Error("invalid background notification status");
|
|
11507
|
+
setEnabled(result.enabled);
|
|
11508
|
+
setError(false);
|
|
11509
|
+
}).catch(() => {
|
|
11510
|
+
if (active && attempt === generation.current) {
|
|
11511
|
+
setSupported(true);
|
|
11512
|
+
setError(true);
|
|
11513
|
+
}
|
|
11514
|
+
});
|
|
11515
|
+
};
|
|
11516
|
+
refresh();
|
|
11517
|
+
window.addEventListener("dsh-mobile-native-ready", refresh);
|
|
11518
|
+
return () => {
|
|
11519
|
+
active = false;
|
|
11520
|
+
generation.current += 1;
|
|
11521
|
+
window.removeEventListener("dsh-mobile-native-ready", refresh);
|
|
11522
|
+
};
|
|
11523
|
+
}, []);
|
|
11524
|
+
if (!supported) return null;
|
|
11525
|
+
const locale = selectedMobileControlLocale();
|
|
11526
|
+
const copy = mobileBackgroundNotificationCopy(locale);
|
|
11527
|
+
const toggle = () => {
|
|
11528
|
+
const bridge = window.__DSH_MOBILE_NATIVE__;
|
|
11529
|
+
if (bridge === void 0 || busy) return;
|
|
11530
|
+
const attempt = ++generation.current;
|
|
11531
|
+
setBusy(true);
|
|
11532
|
+
setError(false);
|
|
11533
|
+
bridge.invoke("notification.background.set", { enabled: !enabled }).then((result) => {
|
|
11534
|
+
if (attempt !== generation.current) return;
|
|
11535
|
+
const next = result?.enabled;
|
|
11536
|
+
if (typeof next !== "boolean") throw new Error("invalid background notification status");
|
|
11537
|
+
setEnabled(next);
|
|
11538
|
+
}).catch(() => {
|
|
11539
|
+
if (attempt === generation.current) setError(true);
|
|
11540
|
+
}).finally(() => {
|
|
11541
|
+
if (attempt === generation.current) setBusy(false);
|
|
11542
|
+
});
|
|
11543
|
+
};
|
|
11544
|
+
return (0, react.createElement)("div", {
|
|
11545
|
+
className: "dsh-mobile-settings_row",
|
|
11546
|
+
lang: locale
|
|
11547
|
+
}, (0, react.createElement)("div", { className: "dsh-mobile-settings_rowText" }, (0, react.createElement)("div", { className: "dsh-mobile-settings_title" }, copy.title), (0, react.createElement)("div", {
|
|
11548
|
+
className: "dsh-mobile-settings_desc",
|
|
11549
|
+
"aria-live": "polite"
|
|
11550
|
+
}, error ? copy.error : copy.description)), (0, react.createElement)("button", {
|
|
11551
|
+
type: "button",
|
|
11552
|
+
className: "dsh-mobile-settings_selector",
|
|
11553
|
+
disabled: busy,
|
|
11554
|
+
"aria-busy": busy ? "true" : void 0,
|
|
11555
|
+
"aria-pressed": enabled ? "true" : "false",
|
|
11556
|
+
onClick: toggle
|
|
11557
|
+
}, busy ? copy.busy : enabled ? copy.enabled : copy.disabled));
|
|
11558
|
+
}
|
|
11391
11559
|
const CONTROL_REQUEST_TIMEOUT_MS = 15e3;
|
|
11392
11560
|
const LONG_CONTROL_REQUEST_TIMEOUT_MS = 21e4;
|
|
11393
11561
|
const GITHUB_RELEASES_URL = "https://github.com/saya-ch/dsh-mobile/releases";
|
|
@@ -11617,6 +11785,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
11617
11785
|
frp_exited: "frpExited",
|
|
11618
11786
|
iroh_companion_missing: "irohCompanionMissing",
|
|
11619
11787
|
iroh_data_directory_invalid: "irohDataDirectoryInvalid",
|
|
11788
|
+
iroh_data_directory_insecure: "irohDataDirectoryInsecure",
|
|
11620
11789
|
iroh_sidecar_invalid: "irohSidecarInvalid",
|
|
11621
11790
|
iroh_download_failed: "irohDownloadFailed",
|
|
11622
11791
|
iroh_download_size_mismatch: "irohDownloadFailed",
|
|
@@ -12950,6 +13119,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12950
13119
|
remoteStatus.setAttribute("aria-live", "polite");
|
|
12951
13120
|
const remoteGuide = element("section", "dsh-mobile-control__guide");
|
|
12952
13121
|
remoteGuide.hidden = true;
|
|
13122
|
+
remoteGuide.dataset.dshMobileGuide = "funnel";
|
|
12953
13123
|
remoteGuide.setAttribute("aria-label", t("funnelGuideAria"));
|
|
12954
13124
|
const remoteGuideTitle = element("h3", "dsh-mobile-control__guide-title");
|
|
12955
13125
|
remoteGuideTitle.textContent = t("funnelGuideTitle");
|
|
@@ -12976,6 +13146,26 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
12976
13146
|
remoteSetupRetry.textContent = t("retryNow");
|
|
12977
13147
|
remoteGuideActions.append(remoteSetup, remoteSetupRetry);
|
|
12978
13148
|
remoteGuide.append(remoteGuideTitle, remoteGuideSummary, remoteGuideSteps, remoteGuideNote, remoteGuideActions);
|
|
13149
|
+
const remotePermission = element("section", "dsh-mobile-control__guide");
|
|
13150
|
+
remotePermission.hidden = true;
|
|
13151
|
+
remotePermission.dataset.dshMobileGuide = "mac-file-access";
|
|
13152
|
+
remotePermission.setAttribute("aria-label", t("macPermissionTitle"));
|
|
13153
|
+
remotePermission.setAttribute("role", "status");
|
|
13154
|
+
const remotePermissionTitle = element("h3", "dsh-mobile-control__guide-title");
|
|
13155
|
+
remotePermissionTitle.textContent = t("macPermissionTitle");
|
|
13156
|
+
const remotePermissionSummary = element("p", "dsh-mobile-control__guide-summary");
|
|
13157
|
+
remotePermissionSummary.textContent = t("macPermissionSummary");
|
|
13158
|
+
const remotePermissionSteps = element("p", "dsh-mobile-control__guide-note");
|
|
13159
|
+
remotePermissionSteps.textContent = t("macPermissionSteps");
|
|
13160
|
+
const remotePermissionActions = element("div", "dsh-mobile-control__guide-actions");
|
|
13161
|
+
const remotePermissionOpen = element("button", "dsh-mobile-control__primary");
|
|
13162
|
+
remotePermissionOpen.type = "button";
|
|
13163
|
+
remotePermissionOpen.textContent = t("macPermissionOpen");
|
|
13164
|
+
const remotePermissionDismissButton = element("button", "dsh-mobile-control__secondary");
|
|
13165
|
+
remotePermissionDismissButton.type = "button";
|
|
13166
|
+
remotePermissionDismissButton.textContent = t("macPermissionDismiss");
|
|
13167
|
+
remotePermissionActions.append(remotePermissionOpen, remotePermissionDismissButton);
|
|
13168
|
+
remotePermission.append(remotePermissionTitle, remotePermissionSummary, remotePermissionSteps, remotePermissionActions);
|
|
12979
13169
|
const remoteActions = element("div", "dsh-mobile-control__actions");
|
|
12980
13170
|
const remoteToggle = element("button", "dsh-mobile-control__primary");
|
|
12981
13171
|
remoteToggle.type = "button";
|
|
@@ -13224,7 +13414,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13224
13414
|
macosFolderHelpBody.append(macosFolderHelpNote);
|
|
13225
13415
|
macosFolderHelp.append(macosFolderHelpSummary, macosFolderHelpBody);
|
|
13226
13416
|
setupDetailsRow.append(macosFolderHelp);
|
|
13227
|
-
remoteWorkspace.append(remoteStatus, remoteAccess, remoteGuide, providerSetupBody, remoteQr, pairingValidity, providerInstallRow, remoteActions, remotePairLink, setupDetailsRow);
|
|
13417
|
+
remoteWorkspace.append(remoteStatus, remoteAccess, remoteGuide, remotePermission, providerSetupBody, remoteQr, pairingValidity, providerInstallRow, remoteActions, remotePairLink, setupDetailsRow);
|
|
13228
13418
|
const diagnosticsView = element("div", "dsh-mobile-control__view is-diagnostics");
|
|
13229
13419
|
diagnosticsView.hidden = true;
|
|
13230
13420
|
const devicesView = element("div", "dsh-mobile-control__view is-devices");
|
|
@@ -13323,6 +13513,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
13323
13513
|
let remoteLoginUrl = "";
|
|
13324
13514
|
let remoteSetupUrl = "";
|
|
13325
13515
|
let remoteSetupPending = false;
|
|
13516
|
+
let remotePermissionDismissed = false;
|
|
13326
13517
|
let remoteSetupOpenedAt = 0;
|
|
13327
13518
|
let remoteReconnectBusy = false;
|
|
13328
13519
|
let remoteProviderBusy = false;
|
|
@@ -14202,6 +14393,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
14202
14393
|
const needsFunnelSetup = state === "error" && remoteSetupUrl !== "";
|
|
14203
14394
|
remoteReady = remoteRunning && state === "ready" && remoteOrigin !== "";
|
|
14204
14395
|
macosFolderHelp.hidden = data.macosHost !== true || !remoteReady;
|
|
14396
|
+
const macFileAccess = data.macFileAccess === "missing" ? "missing" : data.macFileAccess === "ok" ? "ok" : void 0;
|
|
14397
|
+
remotePermission.hidden = macFileAccess !== "missing" || remotePermissionDismissed || !remoteReady;
|
|
14205
14398
|
frpChoiceBadge.textContent = frpConfigured && frpInstalled ? t("ready") : t("advanced");
|
|
14206
14399
|
originChoiceBadge.textContent = managedOrigin ? t(remoteReady ? "ready" : "advanced") : originListening ? t("originBackendReady") : t("advanced");
|
|
14207
14400
|
if (remoteStatus.style) remoteStatus.style.display = state === "off" && remoteFailureText === "" ? "none" : "";
|
|
@@ -15620,6 +15813,13 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
15620
15813
|
remoteSetupPending = false;
|
|
15621
15814
|
reconnectRemote();
|
|
15622
15815
|
});
|
|
15816
|
+
remotePermissionOpen.addEventListener("click", () => {
|
|
15817
|
+
controlRequestJson("/api/mobile-access/remote/macos-file-access/open-settings", { method: "POST" }).catch(() => {});
|
|
15818
|
+
});
|
|
15819
|
+
remotePermissionDismissButton.addEventListener("click", () => {
|
|
15820
|
+
remotePermissionDismissed = true;
|
|
15821
|
+
remotePermission.hidden = true;
|
|
15822
|
+
});
|
|
15623
15823
|
remoteSetup.addEventListener("click", () => {
|
|
15624
15824
|
if (remoteSetupUrl === "") return;
|
|
15625
15825
|
remoteSetupPending = true;
|
|
@@ -17196,7 +17396,8 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
17196
17396
|
kind: "done",
|
|
17197
17397
|
title: label("Attività completata", "Task finished", "任务已完成"),
|
|
17198
17398
|
body: label("Il tuo task DSH è terminato", "Your DSH task finished", "你的 DSH 任务已完成"),
|
|
17199
|
-
tag: taskCompletionTag(parsed.sessionId, parsed.turn)
|
|
17399
|
+
tag: taskCompletionTag(parsed.sessionId, parsed.turn),
|
|
17400
|
+
navigate: taskNotificationNavigate(parsed.sessionId)
|
|
17200
17401
|
});
|
|
17201
17402
|
}, () => {
|
|
17202
17403
|
fireDeviceRevoked();
|
|
@@ -17415,6 +17616,46 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
17415
17616
|
page.body.append(node);
|
|
17416
17617
|
view.setTimeout(() => node.remove(), 3e3);
|
|
17417
17618
|
}
|
|
17619
|
+
/** Consume the Android background service's opaque Session route on cold or warm launch. */
|
|
17620
|
+
function installTaskNotificationNavigation(ctx, page, view) {
|
|
17621
|
+
if (view.__DSH_MOBILE_FRONTEND__ !== "dedicated" || ctx.inject === void 0) return () => void 0;
|
|
17622
|
+
if (new URLSearchParams(view.location.search).get("dsh-session") === null) return () => void 0;
|
|
17623
|
+
let closed = false;
|
|
17624
|
+
let removeNavigation;
|
|
17625
|
+
const fiber = ctx.inject(mobilePushNavigationServices, (navigationContext) => {
|
|
17626
|
+
if (closed) return () => void 0;
|
|
17627
|
+
if (new URLSearchParams(view.location.search).get("dsh-session") === null) return () => void 0;
|
|
17628
|
+
const remove = installPushNavigation(navigationContext, {
|
|
17629
|
+
parameter: "dsh-session",
|
|
17630
|
+
resolve: async (value) => {
|
|
17631
|
+
const sessionId = decodeTaskNotificationNavigate(value);
|
|
17632
|
+
return sessionId === void 0 ? void 0 : {
|
|
17633
|
+
sessionId,
|
|
17634
|
+
interaction: "current"
|
|
17635
|
+
};
|
|
17636
|
+
},
|
|
17637
|
+
notify: (key) => {
|
|
17638
|
+
if (key === "session-unavailable" || key === "session-resolved") showMobilePushNavigationNotice(page, view, key);
|
|
17639
|
+
},
|
|
17640
|
+
browser: {
|
|
17641
|
+
location: view.location,
|
|
17642
|
+
history: view.history
|
|
17643
|
+
}
|
|
17644
|
+
});
|
|
17645
|
+
removeNavigation = remove;
|
|
17646
|
+
return () => {
|
|
17647
|
+
if (removeNavigation === remove) removeNavigation = void 0;
|
|
17648
|
+
remove();
|
|
17649
|
+
};
|
|
17650
|
+
});
|
|
17651
|
+
return () => {
|
|
17652
|
+
if (closed) return;
|
|
17653
|
+
closed = true;
|
|
17654
|
+
removeNavigation?.();
|
|
17655
|
+
removeNavigation = void 0;
|
|
17656
|
+
fiber.dispose();
|
|
17657
|
+
};
|
|
17658
|
+
}
|
|
17418
17659
|
/** Own the browser-only push controller, settings row, and optional authenticated tap navigation. */
|
|
17419
17660
|
function installMobilePushBrowserClient(ctx, runtime) {
|
|
17420
17661
|
const { page, view } = runtime;
|
|
@@ -17526,6 +17767,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
17526
17767
|
page: document,
|
|
17527
17768
|
view: window
|
|
17528
17769
|
}), "dsh-mobile: authenticated browser push lifecycle");
|
|
17770
|
+
ctx.effect(() => installTaskNotificationNavigation(ctx, document, window), "dsh-mobile: native task notification navigation");
|
|
17529
17771
|
ctx.effect(() => {
|
|
17530
17772
|
const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__, location.protocol);
|
|
17531
17773
|
const gatewayCustomization = ownsMobileCustomization(document.querySelector("meta[name=\"dsh-mobile-gateway\"][content=\"authenticated\"]") !== null, window.__DSH_MOBILE_FRONTEND__);
|
|
@@ -17583,7 +17825,13 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
17583
17825
|
id: "dsh-mobile-task-notifications",
|
|
17584
17826
|
order: 110
|
|
17585
17827
|
}, MobileTaskNotificationRow);
|
|
17828
|
+
const removeBackgroundNotifications = ctx.slots.register({
|
|
17829
|
+
name: "settings.general.item",
|
|
17830
|
+
id: "dsh-mobile-background-notifications",
|
|
17831
|
+
order: 111
|
|
17832
|
+
}, MobileBackgroundNotificationRow);
|
|
17586
17833
|
return () => {
|
|
17834
|
+
removeBackgroundNotifications();
|
|
17587
17835
|
removeTaskNotifications();
|
|
17588
17836
|
removeSwitchComputer();
|
|
17589
17837
|
removeDisplayScale();
|
|
@@ -17696,6 +17944,7 @@ dialog.dshm-picker[open]{display:grid;place-items:center}dialog.dshm-picker::bac
|
|
|
17696
17944
|
exports.installDshLanguageBoundSurface = installDshLanguageBoundSurface;
|
|
17697
17945
|
exports.installMobileNativeBack = installMobileNativeBack;
|
|
17698
17946
|
exports.installMobilePushBrowserClient = installMobilePushBrowserClient;
|
|
17947
|
+
exports.installTaskNotificationNavigation = installTaskNotificationNavigation;
|
|
17699
17948
|
exports.localizedDiagnosticEntryCopy = localizedDiagnosticEntryCopy;
|
|
17700
17949
|
exports.mobileBackBlockedMessage = mobileBackBlockedMessage;
|
|
17701
17950
|
exports.mobilePushNavigationNotice = mobilePushNavigationNotice;
|