dsh-mobile 0.4.7 → 0.5.1
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 +10 -3
- package/README.en.md +33 -30
- package/README.md +37 -33
- package/SECURITY.md +3 -1
- package/THIRD_PARTY_NOTICES.md +4 -4
- package/docs/ATTACH_EXISTING_FRPS.en.md +2 -0
- package/docs/ATTACH_EXISTING_FRPS.md +4 -0
- package/docs/CLOUDFLARE_TUNNEL.en.md +15 -20
- package/docs/CLOUDFLARE_TUNNEL.md +15 -20
- package/docs/DSH_0.1.5_LAN.md +1 -1
- package/docs/README.md +2 -2
- package/docs/SELF_HOSTED_FRP.en.md +7 -10
- package/docs/SELF_HOSTED_FRP.md +7 -10
- package/docs/SELF_HOSTED_ORIGIN.en.md +1 -1
- package/docs/SELF_HOSTED_ORIGIN.md +1 -1
- package/lib/client.js +459 -37
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +4 -1
- package/lib/index.mjs +162 -45
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +73 -9
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +5 -5
package/lib/client.js
CHANGED
|
@@ -513,6 +513,30 @@ window.__ModuleLoader__.load({
|
|
|
513
513
|
wsPathsAnnounce: "{n} 条插件连接等待放行",
|
|
514
514
|
wsPathsAdvancedHint: "通常不需要手动填写:用上方按钮即可。只有当你确切知道所需路径时才填。"
|
|
515
515
|
});
|
|
516
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
517
|
+
cpolarDownloadFailed: "Could not download cpolar from its official site. Check this computer’s network or proxy, then retry.",
|
|
518
|
+
cpolarDownloadTimeout: "The cpolar download timed out. Check this computer’s network or proxy, then retry.",
|
|
519
|
+
cpolarDownloadHttp: "The official cpolar download returned HTTP {status}. Retry later or check the official download page.",
|
|
520
|
+
cpolarIntegrityFailed: "The cpolar download did not match the pinned build, so it was not installed. Retry later or update DSH Mobile; do not bypass verification.",
|
|
521
|
+
cpolarExtractFailed: "The cpolar download completed, but extraction failed. Check system extraction tools and security software, then retry.",
|
|
522
|
+
cpolarStorageFailed: "Could not write the cpolar component in DSH Mobile’s private directory. Check disk space, permissions, and security software, then retry."
|
|
523
|
+
});
|
|
524
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.it, {
|
|
525
|
+
cpolarDownloadFailed: "Impossibile scaricare cpolar dal sito ufficiale. Controlla rete o proxy del computer e riprova.",
|
|
526
|
+
cpolarDownloadTimeout: "Download di cpolar scaduto. Controlla rete o proxy del computer e riprova.",
|
|
527
|
+
cpolarDownloadHttp: "Il download ufficiale di cpolar ha restituito HTTP {status}. Riprova più tardi o controlla la pagina ufficiale.",
|
|
528
|
+
cpolarIntegrityFailed: "Il pacchetto cpolar non corrisponde alla versione verificata e non è stato installato. Riprova più tardi o aggiorna DSH Mobile; non aggirare la verifica.",
|
|
529
|
+
cpolarExtractFailed: "Download di cpolar completato, ma estrazione non riuscita. Controlla gli strumenti di estrazione e il software di sicurezza, poi riprova.",
|
|
530
|
+
cpolarStorageFailed: "Impossibile scrivere cpolar nella directory privata di DSH Mobile. Controlla spazio, permessi e software di sicurezza, poi riprova."
|
|
531
|
+
});
|
|
532
|
+
Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
|
|
533
|
+
cpolarDownloadFailed: "无法从 cpolar 官网下载组件。请检查这台电脑的网络或代理,然后重试。",
|
|
534
|
+
cpolarDownloadTimeout: "下载 cpolar 组件超时。请检查这台电脑的网络或代理,然后重试。",
|
|
535
|
+
cpolarDownloadHttp: "cpolar 官网下载返回 HTTP {status}。请稍后重试,或检查官网下载页。",
|
|
536
|
+
cpolarIntegrityFailed: "下载的 cpolar 组件未通过固定版本校验,因此没有安装。请稍后重试或更新 DSH Mobile,不要跳过校验。",
|
|
537
|
+
cpolarExtractFailed: "cpolar 下载完成,但解包失败。请检查系统解压组件和安全软件,然后重试。",
|
|
538
|
+
cpolarStorageFailed: "无法把 cpolar 写入 DSH Mobile 私有目录。请检查磁盘空间、目录权限和安全软件,然后重试。"
|
|
539
|
+
});
|
|
516
540
|
Object.assign(MOBILE_CONTROL_MESSAGES.en, {
|
|
517
541
|
lanSetupTitle: "Finish local network setup",
|
|
518
542
|
lanSetupIntro: "Choose the network shared by this computer and your phone. DSH Mobile creates private certificates only after you confirm.",
|
|
@@ -1402,6 +1426,7 @@ window.__ModuleLoader__.load({
|
|
|
1402
1426
|
"remote-needs-login": ["Tailscale is waiting for login to finish.", "Return to Remote access and continue login."],
|
|
1403
1427
|
"remote-connecting": ["The {provider} remote channel is still connecting.", "Wait briefly, then check again."],
|
|
1404
1428
|
"remote-controller-error": ["The {provider} controller reported {controllerCode}.", "Return to Remote access and reconnect."],
|
|
1429
|
+
"competing-remote-channel": ["Another remote plugin is injecting its own request channel; the phone may show that plugin’s pairing screen.", "Turn off dsh-remote-web-ui remote access on the computer, refresh, and pair using the current DSH Mobile QR code. Do not mix pairing codes."],
|
|
1405
1430
|
"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."]
|
|
1406
1431
|
},
|
|
1407
1432
|
it: {
|
|
@@ -1426,6 +1451,7 @@ window.__ModuleLoader__.load({
|
|
|
1426
1451
|
"remote-needs-login": ["Tailscale attende il completamento dell’accesso.", "Torna ad Accesso remoto e continua l’accesso."],
|
|
1427
1452
|
"remote-connecting": ["Il canale remoto {provider} è ancora in connessione.", "Attendi qualche istante e ripeti il controllo."],
|
|
1428
1453
|
"remote-controller-error": ["Il controller {provider} ha segnalato {controllerCode}.", "Torna ad Accesso remoto e riconnettiti."],
|
|
1454
|
+
"competing-remote-channel": ["Un altro plugin remoto sta inserendo il proprio canale di richieste; sul telefono potrebbe apparire la sua schermata di abbinamento.", "Disattiva l’accesso remoto di dsh-remote-web-ui sul computer, aggiorna la pagina e usa il codice QR corrente di DSH Mobile. Non mescolare i codici."],
|
|
1429
1455
|
"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."]
|
|
1430
1456
|
},
|
|
1431
1457
|
zh: {
|
|
@@ -1450,6 +1476,7 @@ window.__ModuleLoader__.load({
|
|
|
1450
1476
|
"remote-needs-login": ["Tailscale 正在等待完成登录。", "返回远程访问并继续登录。"],
|
|
1451
1477
|
"remote-connecting": ["{provider} 远程通道仍在连接。", "等待片刻后重新检查。"],
|
|
1452
1478
|
"remote-controller-error": ["{provider} 控制器报告 {controllerCode}。", "返回远程访问并重新连接。"],
|
|
1479
|
+
"competing-remote-channel": ["检测到另一个远程插件注入自己的请求通道;手机可能显示它的配对页面。", "在电脑端关闭 dsh-remote-web-ui 的远程访问并刷新,再用 DSH Mobile 当前二维码配对;不要混用配对码。"],
|
|
1453
1480
|
"phone-network-unknown": ["电脑无法判断路由器是否隔离了手机。", "确认手机与电脑使用同一网络,并关闭访客网络或 AP 隔离。"]
|
|
1454
1481
|
}
|
|
1455
1482
|
};
|
|
@@ -2599,12 +2626,13 @@ window.__ModuleLoader__.load({
|
|
|
2599
2626
|
[data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }
|
|
2600
2627
|
[data-dsh-mobile-center] > * { min-width:0 !important; }
|
|
2601
2628
|
[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; }
|
|
2629
|
+
[data-dsh-mobile-header] :is([class*="_titleRow"],[class*="_headerLeading"]) { touch-action:pan-y; }
|
|
2602
2630
|
[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; }
|
|
2603
2631
|
[data-dsh-mobile-header] [class*="_titleCluster"] { min-width:0; }
|
|
2604
2632
|
[data-dsh-mobile-header] [class*="_crumbs"] { min-width:0; overflow:hidden; }
|
|
2605
2633
|
[data-dsh-mobile-header] [class*="_crumb"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
2606
|
-
|
|
2607
|
-
[data-dsh-mobile-header] [class*="_headerActions"]
|
|
2634
|
+
/* Nested header actions (including the Jobs menu) must paint outside the row. */
|
|
2635
|
+
[data-dsh-mobile-header] [class*="_headerActions"] { flex:none; min-width:max-content; overflow:visible; }
|
|
2608
2636
|
[data-dsh-mobile-header] [class*="_headerUtilities"] { gap:2px !important; }
|
|
2609
2637
|
[data-dsh-mobile-header] [class*="_sessionLogButton"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }
|
|
2610
2638
|
[data-dsh-mobile-header] [class*="_sessionLogButton"] > * { display:none !important; }
|
|
@@ -2691,6 +2719,15 @@ window.__ModuleLoader__.load({
|
|
|
2691
2719
|
[data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"] [class*="_sep"] { margin:0 6px !important; }
|
|
2692
2720
|
/* Composer dock stats strip (turns/steps/tokens) reads small on phones. */
|
|
2693
2721
|
[data-dsh-mobile-center] [data-slot="conversation.composer.dock"] [class*="_root"] { font-size:10px !important; line-height:16px !important; }
|
|
2722
|
+
/* The context ring is a composer-metadata _root too, so the metadata rule
|
|
2723
|
+
above stretched it across the whole dock. It cannot shrink, which left the
|
|
2724
|
+
session statistics beside it a few pixels wide, every number clipped to an
|
|
2725
|
+
ellipsis. Put the numbers in a wrapping left column and the ring in a right
|
|
2726
|
+
column it cannot grow out of: a phone then shows every number in full, and
|
|
2727
|
+
a wide screen keeps the ring beside them on one line. */
|
|
2728
|
+
[data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"]:has(> [class*="_trigger"][aria-haspopup="dialog"]) { width:auto !important; max-width:none !important; flex:0 0 auto !important; }
|
|
2729
|
+
[data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) { display:grid !important; grid-template-columns:minmax(0,1fr) max-content !important; align-items:center !important; column-gap:12px !important; }
|
|
2730
|
+
[data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"][data-composer-stats] { box-sizing:border-box !important; width:auto !important; max-width:none !important; flex:0 1 auto !important; flex-wrap:wrap !important; justify-content:flex-start !important; row-gap:2px !important; }
|
|
2694
2731
|
/* Message runtime details are inline on desktop. Give the clock/runtime
|
|
2695
2732
|
label its own wrapping row on narrow screens so TTFT and throughput do
|
|
2696
2733
|
not push the action buttons or clip at the viewport edge. */
|
|
@@ -2739,7 +2776,6 @@ window.__ModuleLoader__.load({
|
|
|
2739
2776
|
@keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }
|
|
2740
2777
|
@keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }
|
|
2741
2778
|
@media (max-width:420px) {
|
|
2742
|
-
[data-dsh-mobile-header] [class*="_headerActions"] { max-width:42vw; }
|
|
2743
2779
|
[data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { align-self:stretch !important; width:100% !important; }
|
|
2744
2780
|
[data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }
|
|
2745
2781
|
}
|
|
@@ -2756,6 +2792,48 @@ window.__ModuleLoader__.load({
|
|
|
2756
2792
|
function firstByClassSuffix(root, suffix) {
|
|
2757
2793
|
return Array.from(root.querySelectorAll("[class]")).find((element) => classToken(element, suffix));
|
|
2758
2794
|
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Whether this composer is the landing hero rather than the composer of an open
|
|
2797
|
+
* conversation. DSH marks the hero composer's root with a `_hero` class and drops
|
|
2798
|
+
* it once a session owns the surface, which is the only difference available
|
|
2799
|
+
* without the layout's session id.
|
|
2800
|
+
*/
|
|
2801
|
+
function composerIsLandingHero(composer, depth = 4) {
|
|
2802
|
+
let node = composer;
|
|
2803
|
+
for (let step = 0; step < depth && node !== null; step += 1) {
|
|
2804
|
+
if (classToken(node, "_hero")) return true;
|
|
2805
|
+
node = node.parentElement;
|
|
2806
|
+
}
|
|
2807
|
+
return false;
|
|
2808
|
+
}
|
|
2809
|
+
/** Resolve only identities published by the current conversation or selected session row. */
|
|
2810
|
+
function resolveComposerSessionOrigin(composer, selectedRow, selectedRowToken) {
|
|
2811
|
+
const conversationRoot = composer?.closest("[data-conversation-session]") ?? null;
|
|
2812
|
+
const conversationId = conversationRoot?.getAttribute("data-conversation-session");
|
|
2813
|
+
if (conversationRoot !== null && conversationId) return {
|
|
2814
|
+
sessionRoot: conversationRoot,
|
|
2815
|
+
sessionId: conversationId
|
|
2816
|
+
};
|
|
2817
|
+
const dedicatedRoot = composer?.closest("[data-dsh-mobile-session]") ?? null;
|
|
2818
|
+
const dedicatedId = dedicatedRoot?.getAttribute("data-dsh-mobile-session");
|
|
2819
|
+
if (dedicatedRoot !== null && dedicatedId) return {
|
|
2820
|
+
sessionRoot: dedicatedRoot,
|
|
2821
|
+
sessionId: dedicatedId
|
|
2822
|
+
};
|
|
2823
|
+
if (selectedRow === null) return {
|
|
2824
|
+
sessionRoot: null,
|
|
2825
|
+
sessionId: null
|
|
2826
|
+
};
|
|
2827
|
+
const identity = selectedRow.getAttribute("data-session-id") ?? selectedRow.getAttribute("aria-label") ?? selectedRow.textContent?.trim() ?? "";
|
|
2828
|
+
return {
|
|
2829
|
+
sessionRoot: selectedRow,
|
|
2830
|
+
sessionId: `${selectedRowToken(selectedRow)}:${identity}`
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
/** An id-less non-hero composer still accepts App soft-keyboard line breaks, but not camera results. */
|
|
2834
|
+
function activeSessionForSoftEnter(composer, sessionId) {
|
|
2835
|
+
return sessionId !== null || composer !== null && composer.closest(".dshm-main") !== null && !composerIsLandingHero(composer);
|
|
2836
|
+
}
|
|
2759
2837
|
/** Find the stock DSH application frame without mistaking a feature card for the shell. */
|
|
2760
2838
|
function resolveNativeMobileFrame(root, dedicatedCenter) {
|
|
2761
2839
|
if (dedicatedCenter !== void 0) return void 0;
|
|
@@ -2782,6 +2860,93 @@ window.__ModuleLoader__.load({
|
|
|
2782
2860
|
return marked;
|
|
2783
2861
|
}
|
|
2784
2862
|
const AUTO_HISTORY_THRESHOLD_PX = 64;
|
|
2863
|
+
/** Travel, in CSS pixels, that separates a header strip pan from a tap on a chip. */
|
|
2864
|
+
const STRIP_DRAG_THRESHOLD_PX = 8;
|
|
2865
|
+
/** Measure flowing title controls without counting an open absolute-positioned menu. */
|
|
2866
|
+
function measureHeaderStripOverflow(row) {
|
|
2867
|
+
const left = row.getBoundingClientRect().left;
|
|
2868
|
+
const controls = Array.from(row.children);
|
|
2869
|
+
const actions = row.querySelector(":scope > [class*=\"_titleCluster\"] [class*=\"_headerActions\"]");
|
|
2870
|
+
if (actions !== null) controls.push(actions);
|
|
2871
|
+
const right = Math.max(left, ...controls.map((control) => control.getBoundingClientRect().right));
|
|
2872
|
+
return Math.max(0, right - left - row.clientWidth);
|
|
2873
|
+
}
|
|
2874
|
+
/** Touch-only pan state; browser pointer cancellation does not interrupt the touch stream. */
|
|
2875
|
+
function createHeaderStripPanController(options) {
|
|
2876
|
+
let offset = 0;
|
|
2877
|
+
let drag;
|
|
2878
|
+
let suppressUntil = 0;
|
|
2879
|
+
const clamp = (value) => Math.min(Math.max(0, options.range()), Math.max(0, value));
|
|
2880
|
+
const setOffset = (value) => {
|
|
2881
|
+
const next = clamp(value);
|
|
2882
|
+
if (next === offset) return;
|
|
2883
|
+
offset = next;
|
|
2884
|
+
options.render(offset);
|
|
2885
|
+
};
|
|
2886
|
+
const end = () => {
|
|
2887
|
+
if (drag === void 0) return;
|
|
2888
|
+
if (drag.claimed) suppressUntil = options.now() + 400;
|
|
2889
|
+
drag = void 0;
|
|
2890
|
+
};
|
|
2891
|
+
const cancel = () => {
|
|
2892
|
+
if (drag?.claimed) suppressUntil = options.now() + 400;
|
|
2893
|
+
drag = void 0;
|
|
2894
|
+
};
|
|
2895
|
+
return {
|
|
2896
|
+
offset: () => offset,
|
|
2897
|
+
start: (x, y) => {
|
|
2898
|
+
drag = {
|
|
2899
|
+
x,
|
|
2900
|
+
y,
|
|
2901
|
+
offset,
|
|
2902
|
+
claimed: false
|
|
2903
|
+
};
|
|
2904
|
+
suppressUntil = 0;
|
|
2905
|
+
},
|
|
2906
|
+
move: (x, y) => {
|
|
2907
|
+
if (drag === void 0) return false;
|
|
2908
|
+
const travel = x - drag.x;
|
|
2909
|
+
if (!drag.claimed) {
|
|
2910
|
+
const drift = y - drag.y;
|
|
2911
|
+
if (Math.abs(travel) < STRIP_DRAG_THRESHOLD_PX && Math.abs(drift) < STRIP_DRAG_THRESHOLD_PX) return false;
|
|
2912
|
+
if (Math.abs(drift) > Math.abs(travel)) {
|
|
2913
|
+
drag = void 0;
|
|
2914
|
+
return false;
|
|
2915
|
+
}
|
|
2916
|
+
drag.claimed = true;
|
|
2917
|
+
}
|
|
2918
|
+
setOffset(drag.offset - travel);
|
|
2919
|
+
return true;
|
|
2920
|
+
},
|
|
2921
|
+
end,
|
|
2922
|
+
cancel,
|
|
2923
|
+
sync: () => {
|
|
2924
|
+
if (options.range() === 0) {
|
|
2925
|
+
drag = void 0;
|
|
2926
|
+
suppressUntil = 0;
|
|
2927
|
+
}
|
|
2928
|
+
setOffset(offset);
|
|
2929
|
+
},
|
|
2930
|
+
reveal: (left, right, visibleLeft, visibleRight) => {
|
|
2931
|
+
if (left < visibleLeft) setOffset(offset - (visibleLeft - left));
|
|
2932
|
+
else if (right > visibleRight) setOffset(offset + right - visibleRight);
|
|
2933
|
+
},
|
|
2934
|
+
resetClickSuppression: () => {
|
|
2935
|
+
suppressUntil = 0;
|
|
2936
|
+
},
|
|
2937
|
+
suppressClick: (inHeader, detail) => {
|
|
2938
|
+
if (suppressUntil === 0 || detail === 0) return false;
|
|
2939
|
+
const suppress = inHeader && options.now() <= suppressUntil;
|
|
2940
|
+
suppressUntil = 0;
|
|
2941
|
+
return suppress;
|
|
2942
|
+
},
|
|
2943
|
+
dispose: () => {
|
|
2944
|
+
drag = void 0;
|
|
2945
|
+
suppressUntil = 0;
|
|
2946
|
+
setOffset(0);
|
|
2947
|
+
}
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2785
2950
|
function controlledFileDragEvent(type, files, initialDropEffect) {
|
|
2786
2951
|
const event = new Event(type, {
|
|
2787
2952
|
bubbles: true,
|
|
@@ -2839,6 +3004,38 @@ window.__ModuleLoader__.load({
|
|
|
2839
3004
|
function drawerScrimVisible(sidebarCollapsed, overlayActive) {
|
|
2840
3005
|
return !sidebarCollapsed && overlayActive;
|
|
2841
3006
|
}
|
|
3007
|
+
/** Let the stock page close one visible layer before Android navigates its WebView. */
|
|
3008
|
+
function installStockMobileBack(view, current, actions) {
|
|
3009
|
+
const onBack = (event) => {
|
|
3010
|
+
if (!event.cancelable || event.defaultPrevented) return;
|
|
3011
|
+
const layers = current();
|
|
3012
|
+
if (layers === null) return;
|
|
3013
|
+
if (layers.detailsOpen) {
|
|
3014
|
+
event.preventDefault();
|
|
3015
|
+
actions.closeDetails();
|
|
3016
|
+
} else if (layers.drawerOpen) {
|
|
3017
|
+
event.preventDefault();
|
|
3018
|
+
actions.closeDrawer();
|
|
3019
|
+
} else if (layers.mainPanelOpen) {
|
|
3020
|
+
event.preventDefault();
|
|
3021
|
+
actions.closeMainPanel();
|
|
3022
|
+
}
|
|
3023
|
+
};
|
|
3024
|
+
view.addEventListener("dsh-mobile:native-back", onBack);
|
|
3025
|
+
return () => {
|
|
3026
|
+
view.removeEventListener("dsh-mobile:native-back", onBack);
|
|
3027
|
+
};
|
|
3028
|
+
}
|
|
3029
|
+
function stockLayoutBackControl(value) {
|
|
3030
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
3031
|
+
}
|
|
3032
|
+
/** Whether the stock layout can return from a selected main panel. */
|
|
3033
|
+
function stockMainPanelOpen(value) {
|
|
3034
|
+
const layout = stockLayoutBackControl(value);
|
|
3035
|
+
if (typeof layout?.selectPanel !== "function" || typeof layout.panelInfo?.getSnapshot !== "function") return false;
|
|
3036
|
+
const snapshot = layout.panelInfo.getSnapshot();
|
|
3037
|
+
return typeof snapshot === "object" && snapshot !== null && typeof snapshot.activePanelId === "string";
|
|
3038
|
+
}
|
|
2842
3039
|
/** Only a known on-screen keyboard in an active App composer may change Enter into a line break. */
|
|
2843
3040
|
function isSoftKeyboardEnterLineBreak(event, context) {
|
|
2844
3041
|
if (context.nativeState?.imeVisible !== true || context.nativeState.noHardwareKeyboard !== true || !context.editable || !context.activeSession || context.commandMenuOpen || context.recentlyComposing) return false;
|
|
@@ -2886,7 +3083,7 @@ window.__ModuleLoader__.load({
|
|
|
2886
3083
|
return !current.disposed && origin.generation === current.generation && origin.href === current.href && origin.composer !== null && current.composerConnected && origin.composer === current.composer && origin.sessionRoot !== null && origin.sessionRoot === current.sessionRoot && origin.sessionId !== null && origin.sessionId === current.sessionId;
|
|
2887
3084
|
}
|
|
2888
3085
|
/** Add mobile semantics without replacing feature trees. */
|
|
2889
|
-
function installNativeMobileSurface() {
|
|
3086
|
+
function installNativeMobileSurface(backServices) {
|
|
2890
3087
|
document.documentElement.classList.add("dsh-native-mobile-active");
|
|
2891
3088
|
const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language];
|
|
2892
3089
|
const language = resolveNativeMobileLanguage(document.documentElement.lang, browserLanguages);
|
|
@@ -2983,27 +3180,15 @@ window.__ModuleLoader__.load({
|
|
|
2983
3180
|
const sessionTokens = /* @__PURE__ */ new WeakMap();
|
|
2984
3181
|
let nextSessionToken = 0;
|
|
2985
3182
|
const currentSessionOrigin = () => {
|
|
2986
|
-
const dedicatedRoot = boundComposer?.closest("[data-dsh-mobile-session]") ?? null;
|
|
2987
|
-
const dedicatedId = dedicatedRoot?.getAttribute("data-dsh-mobile-session");
|
|
2988
|
-
if (dedicatedRoot !== null && typeof dedicatedId === "string" && dedicatedId !== "") return {
|
|
2989
|
-
sessionRoot: dedicatedRoot,
|
|
2990
|
-
sessionId: dedicatedId
|
|
2991
|
-
};
|
|
2992
3183
|
const selectedRow = document.querySelector("[role=\"treeitem\"][aria-selected=\"true\"]");
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
token
|
|
3000
|
-
|
|
3001
|
-
}
|
|
3002
|
-
const identity = selectedRow.getAttribute("data-session-id") ?? selectedRow.getAttribute("aria-label") ?? selectedRow.textContent?.trim() ?? "";
|
|
3003
|
-
return {
|
|
3004
|
-
sessionRoot: selectedRow,
|
|
3005
|
-
sessionId: `${token}:${identity}`
|
|
3006
|
-
};
|
|
3184
|
+
return resolveComposerSessionOrigin(boundComposer, selectedRow, (row) => {
|
|
3185
|
+
let token = sessionTokens.get(row);
|
|
3186
|
+
if (token === void 0) {
|
|
3187
|
+
token = `stock-${String(++nextSessionToken)}`;
|
|
3188
|
+
sessionTokens.set(row, token);
|
|
3189
|
+
}
|
|
3190
|
+
return token;
|
|
3191
|
+
});
|
|
3007
3192
|
};
|
|
3008
3193
|
const mediaRequestContext = () => {
|
|
3009
3194
|
const session = currentSessionOrigin();
|
|
@@ -3180,6 +3365,101 @@ window.__ModuleLoader__.load({
|
|
|
3180
3365
|
if (branch === null || branch.hasAttribute("disabled") || branch.getAttribute("aria-disabled") === "true") return;
|
|
3181
3366
|
window.setTimeout(showBranchToast, 80);
|
|
3182
3367
|
};
|
|
3368
|
+
let stripHeader;
|
|
3369
|
+
const stripParts = () => {
|
|
3370
|
+
const row = stripHeader?.querySelector("[class*=\"_titleRow\"]");
|
|
3371
|
+
if (row === void 0 || row === null) return void 0;
|
|
3372
|
+
return {
|
|
3373
|
+
row,
|
|
3374
|
+
lead: stripHeader?.querySelector("[class*=\"_headerLeading\"]") ?? void 0
|
|
3375
|
+
};
|
|
3376
|
+
};
|
|
3377
|
+
const stripRange = () => {
|
|
3378
|
+
const parts = stripParts();
|
|
3379
|
+
if (!overlayQuery.matches || parts === void 0) return 0;
|
|
3380
|
+
const overflow = measureHeaderStripOverflow(parts.row);
|
|
3381
|
+
return overflow > 1 ? overflow : 0;
|
|
3382
|
+
};
|
|
3383
|
+
const applyStrip = (offset) => {
|
|
3384
|
+
const parts = stripParts();
|
|
3385
|
+
if (parts === void 0) return;
|
|
3386
|
+
const value = offset === 0 ? "" : `translateX(${-offset}px)`;
|
|
3387
|
+
if (parts.row.style.transform !== value) parts.row.style.transform = value;
|
|
3388
|
+
if (parts.lead !== void 0 && parts.lead.style.transform !== value) parts.lead.style.transform = value;
|
|
3389
|
+
};
|
|
3390
|
+
const pan = createHeaderStripPanController({
|
|
3391
|
+
range: stripRange,
|
|
3392
|
+
render: applyStrip,
|
|
3393
|
+
now: () => performance.now()
|
|
3394
|
+
});
|
|
3395
|
+
const syncPanHeader = (header) => {
|
|
3396
|
+
if (header !== stripHeader) {
|
|
3397
|
+
pan.dispose();
|
|
3398
|
+
stripHeader = header;
|
|
3399
|
+
}
|
|
3400
|
+
pan.sync();
|
|
3401
|
+
applyStrip(pan.offset());
|
|
3402
|
+
};
|
|
3403
|
+
let activeTouchId;
|
|
3404
|
+
const onStripTouchStart = (event) => {
|
|
3405
|
+
if (activeTouchId !== void 0) pan.cancel();
|
|
3406
|
+
activeTouchId = void 0;
|
|
3407
|
+
pan.resetClickSuppression();
|
|
3408
|
+
if (event.touches.length !== 1) return;
|
|
3409
|
+
if (!overlayQuery.matches) return;
|
|
3410
|
+
if (!(event.target instanceof Element)) return;
|
|
3411
|
+
if (event.target.closest("[data-dsh-mobile-header]") !== stripHeader) return;
|
|
3412
|
+
if (event.target.closest("[data-dsh-mobile-header] [class*=\"_titleRow\"],[data-dsh-mobile-header] [class*=\"_headerLeading\"]") === null) return;
|
|
3413
|
+
if (stripRange() === 0) return;
|
|
3414
|
+
const touch = event.touches[0];
|
|
3415
|
+
if (touch === void 0) return;
|
|
3416
|
+
activeTouchId = touch.identifier;
|
|
3417
|
+
pan.start(touch.clientX, touch.clientY);
|
|
3418
|
+
};
|
|
3419
|
+
const onStripTouchMove = (event) => {
|
|
3420
|
+
if (activeTouchId === void 0) return;
|
|
3421
|
+
if (event.touches.length !== 1) {
|
|
3422
|
+
activeTouchId = void 0;
|
|
3423
|
+
pan.cancel();
|
|
3424
|
+
return;
|
|
3425
|
+
}
|
|
3426
|
+
const touch = event.touches[0];
|
|
3427
|
+
if (touch === void 0 || touch.identifier !== activeTouchId) return;
|
|
3428
|
+
if (pan.move(touch.clientX, touch.clientY)) event.preventDefault();
|
|
3429
|
+
};
|
|
3430
|
+
const onStripTouchEnd = (event) => {
|
|
3431
|
+
if (activeTouchId === void 0 || !Array.from(event.changedTouches).some((touch) => touch.identifier === activeTouchId)) return;
|
|
3432
|
+
activeTouchId = void 0;
|
|
3433
|
+
pan.end();
|
|
3434
|
+
};
|
|
3435
|
+
const onStripTouchCancel = (event) => {
|
|
3436
|
+
if (activeTouchId === void 0 || !Array.from(event.changedTouches).some((touch) => touch.identifier === activeTouchId)) return;
|
|
3437
|
+
activeTouchId = void 0;
|
|
3438
|
+
pan.cancel();
|
|
3439
|
+
};
|
|
3440
|
+
const onStripFocus = (event) => {
|
|
3441
|
+
if (!(event.target instanceof HTMLElement)) return;
|
|
3442
|
+
const header = event.target.closest("[data-dsh-mobile-header]");
|
|
3443
|
+
if (header === null || header !== stripHeader || stripRange() === 0 || event.target.closest("[class*=\"_titleRow\"],[class*=\"_headerLeading\"]") === null) return;
|
|
3444
|
+
const focused = event.target.getBoundingClientRect();
|
|
3445
|
+
const visible = header.getBoundingClientRect();
|
|
3446
|
+
pan.reveal(focused.left, focused.right, visible.left + 42, visible.right - 8);
|
|
3447
|
+
};
|
|
3448
|
+
const onStripClickCapture = (event) => {
|
|
3449
|
+
const inHeader = event.target instanceof Element && event.target.closest("[data-dsh-mobile-header]") !== null;
|
|
3450
|
+
if (!pan.suppressClick(inHeader, event.detail)) return;
|
|
3451
|
+
event.preventDefault();
|
|
3452
|
+
event.stopPropagation();
|
|
3453
|
+
};
|
|
3454
|
+
document.addEventListener("touchstart", onStripTouchStart, true);
|
|
3455
|
+
document.addEventListener("touchmove", onStripTouchMove, {
|
|
3456
|
+
capture: true,
|
|
3457
|
+
passive: false
|
|
3458
|
+
});
|
|
3459
|
+
document.addEventListener("touchend", onStripTouchEnd, true);
|
|
3460
|
+
document.addEventListener("touchcancel", onStripTouchCancel, true);
|
|
3461
|
+
document.addEventListener("focusin", onStripFocus, true);
|
|
3462
|
+
document.addEventListener("click", onStripClickCapture, true);
|
|
3183
3463
|
document.addEventListener("click", onBranchClick, true);
|
|
3184
3464
|
let frame;
|
|
3185
3465
|
let sidebar;
|
|
@@ -3254,7 +3534,7 @@ window.__ModuleLoader__.load({
|
|
|
3254
3534
|
disposeEnterBinding = editor === null || composerCard === null ? void 0 : bindComposerSoftEnter(editor, () => ({
|
|
3255
3535
|
nativeState: window.__DSH_MOBILE_NATIVE__ === void 0 ? null : window.__DSH_MOBILE_KEYBOARD_STATE__,
|
|
3256
3536
|
editable: editor.isConnected && editor.getAttribute("contenteditable") === "true" && editor.getAttribute("aria-disabled") !== "true" && editor.getAttribute("aria-haspopup") !== "menu" && composerCard.getAttribute("aria-busy") !== "true",
|
|
3257
|
-
activeSession: currentSessionOrigin().sessionId
|
|
3537
|
+
activeSession: activeSessionForSoftEnter(composerCard, currentSessionOrigin().sessionId),
|
|
3258
3538
|
commandMenuOpen: composerCard.querySelector("[data-trigger-menu],button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]") !== null
|
|
3259
3539
|
}));
|
|
3260
3540
|
};
|
|
@@ -3280,6 +3560,7 @@ window.__ModuleLoader__.load({
|
|
|
3280
3560
|
const handle = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_handle");
|
|
3281
3561
|
markNativeMobileSettings(document);
|
|
3282
3562
|
if (center === void 0) {
|
|
3563
|
+
syncPanHeader(void 0);
|
|
3283
3564
|
syncComposerEnterNewline(null);
|
|
3284
3565
|
bindHistoryScroller(void 0);
|
|
3285
3566
|
syncMediaBinding(null);
|
|
@@ -3289,7 +3570,9 @@ window.__ModuleLoader__.load({
|
|
|
3289
3570
|
}
|
|
3290
3571
|
if (center !== void 0) {
|
|
3291
3572
|
center.dataset.dshMobileCenter = "true";
|
|
3292
|
-
center.querySelector("header")
|
|
3573
|
+
const header = center.querySelector("header") ?? void 0;
|
|
3574
|
+
header?.setAttribute("data-dsh-mobile-header", "true");
|
|
3575
|
+
syncPanHeader(header);
|
|
3293
3576
|
viewArea = firstByClassSuffix(center, "_viewArea");
|
|
3294
3577
|
if (viewArea !== void 0) viewArea.dataset.dshMobileView = "true";
|
|
3295
3578
|
const conversation = center.querySelector("[data-conversation-scroll]");
|
|
@@ -3400,6 +3683,33 @@ window.__ModuleLoader__.load({
|
|
|
3400
3683
|
if (sidebar?.dataset.open === "true") toggle?.click();
|
|
3401
3684
|
});
|
|
3402
3685
|
sync();
|
|
3686
|
+
const stopStockBack = installStockMobileBack(window, () => {
|
|
3687
|
+
if (frame?.isConnected !== true) return null;
|
|
3688
|
+
const lastColumn = frame.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
|
|
3689
|
+
return {
|
|
3690
|
+
detailsOpen: overlayQuery.matches && lastColumn !== void 0 && lastColumn !== "" && lastColumn !== "0px" && lastColumn !== "0",
|
|
3691
|
+
drawerOpen: overlayQuery.matches && sidebarRoot?.isConnected === true && toggle?.isConnected === true && !classToken(sidebarRoot, "_collapsed"),
|
|
3692
|
+
mainPanelOpen: stockMainPanelOpen(backServices.getLayout())
|
|
3693
|
+
};
|
|
3694
|
+
}, {
|
|
3695
|
+
closeDetails: () => {
|
|
3696
|
+
const layout = stockLayoutBackControl(backServices.getLayout());
|
|
3697
|
+
if (typeof layout?.closeRightbar !== "function") return;
|
|
3698
|
+
const sidebarRight = backServices.getSidebarRight();
|
|
3699
|
+
if (typeof sidebarRight === "object" && sidebarRight !== null) {
|
|
3700
|
+
const control = sidebarRight;
|
|
3701
|
+
if (typeof control.isExpanded === "function" && typeof control.toggleExpanded === "function" && control.isExpanded()) control.toggleExpanded();
|
|
3702
|
+
}
|
|
3703
|
+
layout.closeRightbar();
|
|
3704
|
+
},
|
|
3705
|
+
closeDrawer: () => {
|
|
3706
|
+
toggle?.click();
|
|
3707
|
+
},
|
|
3708
|
+
closeMainPanel: () => {
|
|
3709
|
+
const layout = stockLayoutBackControl(backServices.getLayout());
|
|
3710
|
+
if (typeof layout?.selectPanel === "function") layout.selectPanel(null);
|
|
3711
|
+
}
|
|
3712
|
+
});
|
|
3403
3713
|
const disposeTaskWatcher = installTaskCompletionWatcher({
|
|
3404
3714
|
completionFallback: false,
|
|
3405
3715
|
label: (kind, sessionLabel) => kind === "done" ? {
|
|
@@ -3412,6 +3722,7 @@ window.__ModuleLoader__.load({
|
|
|
3412
3722
|
});
|
|
3413
3723
|
return () => {
|
|
3414
3724
|
disposed = true;
|
|
3725
|
+
stopStockBack();
|
|
3415
3726
|
disposeTaskWatcher();
|
|
3416
3727
|
mediaRequestGeneration += 1;
|
|
3417
3728
|
mediaPickerAbortController.abort();
|
|
@@ -3421,6 +3732,14 @@ window.__ModuleLoader__.load({
|
|
|
3421
3732
|
observer.disconnect();
|
|
3422
3733
|
overlayQuery.removeEventListener("change", schedule);
|
|
3423
3734
|
document.removeEventListener("click", onBranchClick, true);
|
|
3735
|
+
document.removeEventListener("touchstart", onStripTouchStart, true);
|
|
3736
|
+
document.removeEventListener("touchmove", onStripTouchMove, true);
|
|
3737
|
+
document.removeEventListener("touchend", onStripTouchEnd, true);
|
|
3738
|
+
document.removeEventListener("touchcancel", onStripTouchCancel, true);
|
|
3739
|
+
document.removeEventListener("focusin", onStripFocus, true);
|
|
3740
|
+
document.removeEventListener("click", onStripClickCapture, true);
|
|
3741
|
+
pan.dispose();
|
|
3742
|
+
applyStrip(0);
|
|
3424
3743
|
cameraButton.removeEventListener("pointerdown", quietMediaPointer);
|
|
3425
3744
|
cameraButton.removeEventListener("click", takePhoto);
|
|
3426
3745
|
if (branchToastTimer !== 0) window.clearTimeout(branchToastTimer);
|
|
@@ -3445,6 +3764,16 @@ window.__ModuleLoader__.load({
|
|
|
3445
3764
|
//#endregion
|
|
3446
3765
|
//#region src/local-admin-host.ts
|
|
3447
3766
|
/** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */
|
|
3767
|
+
const DESKTOP_ADMIN_HEADER = "x-dsh-mobile-desktop-admin";
|
|
3768
|
+
const DESKTOP_ADMIN_MARKER = "dsh-app://app";
|
|
3769
|
+
/** Add the forwarded Desktop marker only to this plugin's mutating admin requests. */
|
|
3770
|
+
function localAdminRequestHeaders(init, location) {
|
|
3771
|
+
const headers = new Headers(init?.headers);
|
|
3772
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
3773
|
+
headers.delete(DESKTOP_ADMIN_HEADER);
|
|
3774
|
+
if (init?.method?.toUpperCase() === "POST" && location.protocol === "dsh-app:" && location.hostname === "app") headers.set(DESKTOP_ADMIN_HEADER, DESKTOP_ADMIN_MARKER);
|
|
3775
|
+
return headers;
|
|
3776
|
+
}
|
|
3448
3777
|
function unwrapBrackets(hostname) {
|
|
3449
3778
|
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
3450
3779
|
}
|
|
@@ -3485,11 +3814,12 @@ window.__ModuleLoader__.load({
|
|
|
3485
3814
|
/**
|
|
3486
3815
|
* Whether the current browser document should mount the desktop Mobile access
|
|
3487
3816
|
* control. Dedicated Mobile HTTPS (the phone surface) stays native even when
|
|
3488
|
-
* the Host is a private LAN address.
|
|
3817
|
+
* the Host is a private LAN address. DSH Desktop (dsh-app://app) has neither,
|
|
3818
|
+
* so its protocol gated on the fixed hostname "app" counts too.
|
|
3489
3819
|
*/
|
|
3490
|
-
function isDesktopAdminSurface(hostname, search = "", frontend) {
|
|
3820
|
+
function isDesktopAdminSurface(hostname, search = "", frontend, protocol = "") {
|
|
3491
3821
|
const query = search.startsWith("?") ? search.slice(1) : search;
|
|
3492
|
-
return isLocalAdminHostname(hostname) && frontend !== "dedicated" && !new URLSearchParams(query).has("dsh-mobile-preview");
|
|
3822
|
+
return (protocol === "dsh-app:" ? hostname === "app" : (protocol === "" || protocol === "http:" || protocol === "https:") && isLocalAdminHostname(hostname)) && frontend !== "dedicated" && !new URLSearchParams(query).has("dsh-mobile-preview");
|
|
3493
3823
|
}
|
|
3494
3824
|
//#endregion
|
|
3495
3825
|
//#region src/client.ts
|
|
@@ -3497,6 +3827,64 @@ window.__ModuleLoader__.load({
|
|
|
3497
3827
|
function parseMobileRemoteProvider(value) {
|
|
3498
3828
|
return value === "cpolar" || value === "cloudflared" || value === "frp" || value === "origin" ? value : "tailscale";
|
|
3499
3829
|
}
|
|
3830
|
+
/**
|
|
3831
|
+
* Set a surface's visibility; opening a page or overlay brings it to the front.
|
|
3832
|
+
* @param container - the registered surface element.
|
|
3833
|
+
* @param open - whether to show it.
|
|
3834
|
+
*/
|
|
3835
|
+
function setMobileSurfaceOpen(container, open) {
|
|
3836
|
+
container.hidden = !open;
|
|
3837
|
+
if (open && container.isConnected && (container.dataset.dshMobileSurfacePlacement === "page" || container.dataset.dshMobileSurfacePlacement === "overlay")) container.parentElement?.append(container);
|
|
3838
|
+
}
|
|
3839
|
+
function visibleBackLayer(view, element) {
|
|
3840
|
+
if (!element.isConnected || element.hidden || element.closest("[hidden],[inert],[aria-hidden=\"true\"]") !== null) return false;
|
|
3841
|
+
const style = view.getComputedStyle(element);
|
|
3842
|
+
return style.display !== "none" && style.visibility !== "hidden" && element.getClientRects().length > 0;
|
|
3843
|
+
}
|
|
3844
|
+
/**
|
|
3845
|
+
* Consume page-level surfaces and dismissible DSH overlays before layout navigation.
|
|
3846
|
+
* @param view - the page window receiving the native Back request.
|
|
3847
|
+
* @param page - the current DSH document.
|
|
3848
|
+
* @param blocked - feedback for a foreground layer without a safe close action.
|
|
3849
|
+
* @returns a disposer for the capture listener.
|
|
3850
|
+
*/
|
|
3851
|
+
function installMobileNativeBack(view, page, blocked) {
|
|
3852
|
+
const onBack = (event) => {
|
|
3853
|
+
if (!event.cancelable || event.defaultPrevented) return;
|
|
3854
|
+
const claim = () => {
|
|
3855
|
+
event.preventDefault();
|
|
3856
|
+
event.stopImmediatePropagation();
|
|
3857
|
+
};
|
|
3858
|
+
const surface = [...page.querySelectorAll("[data-dsh-mobile-surface-placement=\"page\"], [data-dsh-mobile-surface-placement=\"overlay\"]")].filter((element) => visibleBackLayer(view, element)).at(-1);
|
|
3859
|
+
if (surface !== void 0) {
|
|
3860
|
+
setMobileSurfaceOpen(surface, false);
|
|
3861
|
+
claim();
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
const layers = [...page.querySelectorAll("[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"]")].filter((element) => visibleBackLayer(view, element));
|
|
3865
|
+
const top = layers.at(-1);
|
|
3866
|
+
if (top === void 0) return;
|
|
3867
|
+
claim();
|
|
3868
|
+
if (top.getAttribute("role") === "dialog") {
|
|
3869
|
+
const wrapper = top.parentElement;
|
|
3870
|
+
const mask = wrapper?.firstElementChild;
|
|
3871
|
+
const maskStyle = mask === void 0 || mask === null ? void 0 : view.getComputedStyle(mask);
|
|
3872
|
+
if (wrapper?.getAttribute("role") === "presentation" && wrapper.children.length === 2 && wrapper.lastElementChild === top && mask?.tagName === "DIV" && mask.getAttribute("aria-hidden") === "true" && mask.isConnected && !mask.hidden && maskStyle?.display !== "none" && maskStyle?.visibility !== "hidden" && mask.getClientRects().length > 0) {
|
|
3873
|
+
mask.click();
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
} else if (top.getAttribute("data-menu-material") === "translucent" && layers.filter((layer) => layer.getAttribute("role") === "menu").length === 1) {
|
|
3877
|
+
page.dispatchEvent(new Event("pointerdown", { bubbles: true }));
|
|
3878
|
+
return;
|
|
3879
|
+
}
|
|
3880
|
+
blocked();
|
|
3881
|
+
};
|
|
3882
|
+
const capture = { capture: true };
|
|
3883
|
+
view.addEventListener("dsh-mobile:native-back", onBack, capture);
|
|
3884
|
+
return () => {
|
|
3885
|
+
view.removeEventListener("dsh-mobile:native-back", onBack, capture);
|
|
3886
|
+
};
|
|
3887
|
+
}
|
|
3500
3888
|
const queuedDefinitions = [];
|
|
3501
3889
|
let queuedLegacyMount;
|
|
3502
3890
|
if (typeof window !== "undefined" && window.dshMobile === void 0) window.dshMobile = {
|
|
@@ -3517,6 +3905,18 @@ window.__ModuleLoader__.load({
|
|
|
3517
3905
|
function selectedMobileControlLocale() {
|
|
3518
3906
|
return selectMobileControlLocale(document.documentElement.lang, navigator.languages?.length ? navigator.languages : [navigator.language]);
|
|
3519
3907
|
}
|
|
3908
|
+
/**
|
|
3909
|
+
* Guidance when an open third-party layer has no safe close action.
|
|
3910
|
+
* @param locale - current Mobile UI language.
|
|
3911
|
+
* @returns localized guidance.
|
|
3912
|
+
*/
|
|
3913
|
+
function mobileBackBlockedMessage(locale) {
|
|
3914
|
+
return {
|
|
3915
|
+
en: "Close the open panel first.",
|
|
3916
|
+
it: "Chiudi prima il pannello aperto.",
|
|
3917
|
+
zh: "请先关闭当前弹层。"
|
|
3918
|
+
}[locale];
|
|
3919
|
+
}
|
|
3520
3920
|
/** Remount one plugin-owned surface when DSH changes the document language. */
|
|
3521
3921
|
function installDshLanguageBoundSurface(install) {
|
|
3522
3922
|
let locale = selectedMobileControlLocale();
|
|
@@ -3810,10 +4210,7 @@ window.__ModuleLoader__.load({
|
|
|
3810
4210
|
const response = await fetch(path, {
|
|
3811
4211
|
...init,
|
|
3812
4212
|
signal: controller.signal,
|
|
3813
|
-
headers:
|
|
3814
|
-
"content-type": "application/json",
|
|
3815
|
-
...init?.headers
|
|
3816
|
-
}
|
|
4213
|
+
headers: localAdminRequestHeaders(init, location)
|
|
3817
4214
|
});
|
|
3818
4215
|
const body = await response.json();
|
|
3819
4216
|
if (!response.ok) throw new Error(typeof body.error === "string" ? body.error : `HTTP ${String(response.status)}`);
|
|
@@ -3913,6 +4310,18 @@ window.__ModuleLoader__.load({
|
|
|
3913
4310
|
control_channel_failed: "controlChannelFailed",
|
|
3914
4311
|
cpolar_component_missing: "cpolarMissing",
|
|
3915
4312
|
cpolar_component_invalid: "cpolarInvalid",
|
|
4313
|
+
cpolar_download_failed: "cpolarDownloadFailed",
|
|
4314
|
+
cpolar_download_timeout: "cpolarDownloadTimeout",
|
|
4315
|
+
cpolar_download_size_mismatch: "cpolarIntegrityFailed",
|
|
4316
|
+
cpolar_download_hash_mismatch: "cpolarIntegrityFailed",
|
|
4317
|
+
cpolar_executable_hash_mismatch: "cpolarIntegrityFailed",
|
|
4318
|
+
cpolar_download_redirect_missing: "cpolarDownloadFailed",
|
|
4319
|
+
cpolar_download_redirect_invalid: "cpolarDownloadFailed",
|
|
4320
|
+
cpolar_download_redirect_rejected: "cpolarDownloadFailed",
|
|
4321
|
+
cpolar_installer_missing: "cpolarExtractFailed",
|
|
4322
|
+
cpolar_executable_missing: "cpolarExtractFailed",
|
|
4323
|
+
cpolar_extract_failed: "cpolarExtractFailed",
|
|
4324
|
+
cpolar_storage_failed: "cpolarStorageFailed",
|
|
3916
4325
|
cpolar_config_missing: "cpolarConfigMissing",
|
|
3917
4326
|
cpolar_config_invalid: "cpolarConfigInvalid",
|
|
3918
4327
|
cpolar_port_unavailable: "cpolarPortUnavailable",
|
|
@@ -4048,6 +4457,8 @@ window.__ModuleLoader__.load({
|
|
|
4048
4457
|
*/
|
|
4049
4458
|
const remoteFailureTextFor = (error, fallbackKey) => {
|
|
4050
4459
|
const message = error instanceof Error ? error.message : String(error);
|
|
4460
|
+
const cpolarHttpStatus = /^cpolar_download_http_(\d{3})$/u.exec(message)?.[1];
|
|
4461
|
+
if (cpolarHttpStatus !== void 0) return t("cpolarDownloadHttp", { status: cpolarHttpStatus });
|
|
4051
4462
|
const key = REMOTE_ERROR_MESSAGE_KEYS[message];
|
|
4052
4463
|
return key === void 0 ? t(fallbackKey, { error: message }) : t(key);
|
|
4053
4464
|
};
|
|
@@ -7629,6 +8040,9 @@ window.__ModuleLoader__.load({
|
|
|
7629
8040
|
shellLayer().append(node);
|
|
7630
8041
|
window.setTimeout(() => node.remove(), 2600);
|
|
7631
8042
|
};
|
|
8043
|
+
const stopNativeBack = installMobileNativeBack(window, document, () => {
|
|
8044
|
+
toast(mobileBackBlockedMessage(selectedMobileControlLocale()));
|
|
8045
|
+
});
|
|
7632
8046
|
const materializeNativeFile = (value) => {
|
|
7633
8047
|
if (typeof value !== "object" || value === null || !("base64" in value) || !("name" in value)) return value;
|
|
7634
8048
|
const candidate = value;
|
|
@@ -7761,6 +8175,7 @@ window.__ModuleLoader__.load({
|
|
|
7761
8175
|
return registerUniqueDisposable(surfaces, surfaceIds, surface.id, () => {
|
|
7762
8176
|
const container = element("section");
|
|
7763
8177
|
container.dataset.dshMobileSurface = surface.id;
|
|
8178
|
+
container.dataset.dshMobileSurfacePlacement = surface.placement;
|
|
7764
8179
|
container.hidden = surface.placement === "page" || surface.placement === "overlay";
|
|
7765
8180
|
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";
|
|
7766
8181
|
const host = () => surface.placement === "page" || surface.placement === "overlay" ? shellLayer() : surfaceHost(surface.placement) ?? shellLayer();
|
|
@@ -7813,12 +8228,12 @@ window.__ModuleLoader__.load({
|
|
|
7813
8228
|
open: (surfaceId) => {
|
|
7814
8229
|
ensureCurrent();
|
|
7815
8230
|
const entry = surfaces.get(surfaceId);
|
|
7816
|
-
if (entry !== void 0) entry.container
|
|
8231
|
+
if (entry !== void 0) setMobileSurfaceOpen(entry.container, true);
|
|
7817
8232
|
},
|
|
7818
8233
|
close: (surfaceId) => {
|
|
7819
8234
|
ensureCurrent();
|
|
7820
8235
|
const entry = surfaces.get(surfaceId);
|
|
7821
|
-
if (entry !== void 0) entry.container
|
|
8236
|
+
if (entry !== void 0) setMobileSurfaceOpen(entry.container, false);
|
|
7822
8237
|
},
|
|
7823
8238
|
toast: (message) => {
|
|
7824
8239
|
ensureCurrent();
|
|
@@ -8190,6 +8605,7 @@ window.__ModuleLoader__.load({
|
|
|
8190
8605
|
});
|
|
8191
8606
|
return () => {
|
|
8192
8607
|
disposed = true;
|
|
8608
|
+
stopNativeBack();
|
|
8193
8609
|
stopEvents();
|
|
8194
8610
|
stopRefresh();
|
|
8195
8611
|
started = false;
|
|
@@ -8286,7 +8702,7 @@ window.__ModuleLoader__.load({
|
|
|
8286
8702
|
return trustAuthenticatedGatewayConnection(ctx.get("connection"));
|
|
8287
8703
|
}, "dsh-mobile: authenticated gateway client trust");
|
|
8288
8704
|
ctx.effect(() => {
|
|
8289
|
-
const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__);
|
|
8705
|
+
const desktopAdmin = isDesktopAdminSurface(location.hostname, location.search, window.__DSH_MOBILE_FRONTEND__, location.protocol);
|
|
8290
8706
|
const style = element("style");
|
|
8291
8707
|
style.dataset.plugin = "dsh-mobile";
|
|
8292
8708
|
style.textContent = desktopAdmin ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
|
|
@@ -8309,7 +8725,10 @@ window.__ModuleLoader__.load({
|
|
|
8309
8725
|
};
|
|
8310
8726
|
});
|
|
8311
8727
|
const removeCustom = installCustomAssets();
|
|
8312
|
-
const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface
|
|
8728
|
+
const removeSurface = installDshLanguageBoundSurface(() => installNativeMobileSurface({
|
|
8729
|
+
getLayout: () => ctx.get("layout"),
|
|
8730
|
+
getSidebarRight: () => ctx.get("sidebarRight")
|
|
8731
|
+
}));
|
|
8313
8732
|
return () => {
|
|
8314
8733
|
removeSettingsAction();
|
|
8315
8734
|
removeCustom();
|
|
@@ -8392,6 +8811,8 @@ window.__ModuleLoader__.load({
|
|
|
8392
8811
|
exports.handleMissingExtensionManifest = handleMissingExtensionManifest;
|
|
8393
8812
|
exports.inject = inject;
|
|
8394
8813
|
exports.installDshLanguageBoundSurface = installDshLanguageBoundSurface;
|
|
8814
|
+
exports.installMobileNativeBack = installMobileNativeBack;
|
|
8815
|
+
exports.mobileBackBlockedMessage = mobileBackBlockedMessage;
|
|
8395
8816
|
exports.normalizeDiagnosticOverall = normalizeDiagnosticOverall;
|
|
8396
8817
|
exports.normalizeDiagnosticStatus = normalizeDiagnosticStatus;
|
|
8397
8818
|
exports.parseMobileExtensionManifest = parseMobileExtensionManifest;
|
|
@@ -8402,6 +8823,7 @@ window.__ModuleLoader__.load({
|
|
|
8402
8823
|
exports.renderDiagnosticPayloadSafely = renderDiagnosticPayloadSafely;
|
|
8403
8824
|
exports.selectMobileControlLocale = selectMobileControlLocale;
|
|
8404
8825
|
exports.selectedMobileControlLocale = selectedMobileControlLocale;
|
|
8826
|
+
exports.setMobileSurfaceOpen = setMobileSurfaceOpen;
|
|
8405
8827
|
exports.startExtensionChangeStream = startExtensionChangeStream;
|
|
8406
8828
|
exports.startLifecycleRefreshScheduler = startLifecycleRefreshScheduler;
|
|
8407
8829
|
exports.trustAuthenticatedGatewayConnection = trustAuthenticatedGatewayConnection;
|