dsh-mobile 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTORS.md +8 -4
- package/README.en.md +36 -31
- package/README.md +40 -34
- package/SECURITY.md +2 -0
- package/THIRD_PARTY_NOTICES.md +4 -4
- package/docs/ATTACH_EXISTING_FRPS.en.md +5 -1
- package/docs/ATTACH_EXISTING_FRPS.md +6 -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 +10 -11
- package/docs/SELF_HOSTED_FRP.md +10 -11
- package/docs/SELF_HOSTED_ORIGIN.en.md +1 -1
- package/docs/SELF_HOSTED_ORIGIN.md +1 -1
- package/lib/client.js +265 -32
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +55 -3
- package/lib/index.mjs +166 -15
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +22 -5
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +5 -5
package/lib/client.js
CHANGED
|
@@ -664,6 +664,9 @@ window.__ModuleLoader__.load({
|
|
|
664
664
|
frpVhostProbeFailed: "The VPS address could not be checked. Confirm DNS and retry.",
|
|
665
665
|
frpLaunchFailed: "frpc failed to start.",
|
|
666
666
|
frpTimeout: "The public endpoint did not become ready. Check frps, Caddy, DNS, and firewall.",
|
|
667
|
+
frpProxyNameInUse: "The frps already has a proxy with this installation name in use — usually another DSH computer, or a session that has not expired yet. Disable self-hosted FRP on that machine, or remove that proxy on the frps, then retry.",
|
|
668
|
+
frpRemotePortInUse: "Another proxy on the frps already owns this public entry port. Choose a different entry port and retry.",
|
|
669
|
+
frpProxyStartFailed: "The frps refused frpc's proxy registration. Check its allowPorts, the ports already in use, and its log.",
|
|
667
670
|
frpDiscoveryMismatch: "The public domain points to another DSH computer. Check the Caddy and frps mappings.",
|
|
668
671
|
frpDiscoveryInvalid: "The public domain did not return a valid DSH Mobile discovery response.",
|
|
669
672
|
frpStopped: "The FRP connection stopped.",
|
|
@@ -751,6 +754,9 @@ window.__ModuleLoader__.load({
|
|
|
751
754
|
frpVhostProbeFailed: "Impossibile controllare il VPS. Verifica il DNS e riprova.",
|
|
752
755
|
frpLaunchFailed: "Avvio di frpc non riuscito.",
|
|
753
756
|
frpTimeout: "Endpoint pubblico non pronto. Controlla frps, Caddy, DNS e firewall.",
|
|
757
|
+
frpProxyNameInUse: "Sul frps è già in uso un proxy con il nome di questa installazione — di solito un altro computer DSH o una sessione non ancora scaduta. Disattiva l’FRP autogestito su quella macchina, oppure rimuovi quel proxy sul frps, e riprova.",
|
|
758
|
+
frpRemotePortInUse: "Un altro proxy sul frps occupa già questa porta di ingresso pubblica. Scegli un’altra porta di ingresso e riprova.",
|
|
759
|
+
frpProxyStartFailed: "Il frps ha rifiutato la registrazione del proxy da parte di frpc. Controlla allowPorts, le porte già in uso e il log del server.",
|
|
754
760
|
frpDiscoveryMismatch: "Il dominio pubblico punta a un altro computer DSH. Controlla Caddy e frps.",
|
|
755
761
|
frpDiscoveryInvalid: "Il dominio pubblico non ha restituito una risposta DSH Mobile valida.",
|
|
756
762
|
frpStopped: "Connessione FRP arrestata.",
|
|
@@ -838,6 +844,9 @@ window.__ModuleLoader__.load({
|
|
|
838
844
|
frpVhostProbeFailed: "无法检查 VPS 地址,请确认 DNS 后重试。",
|
|
839
845
|
frpLaunchFailed: "frpc 未能启动。",
|
|
840
846
|
frpTimeout: "公网端点未能就绪,请检查 frps、Caddy、DNS 和防火墙。",
|
|
847
|
+
frpProxyNameInUse: "该 frps 上已有本机安装名对应的 FRP 代理在使用中——通常来自另一台 DSH 电脑,或尚未过期的旧连接。请在那台机器上关闭自建 FRP,或在 frps 上移除该代理后重试。",
|
|
848
|
+
frpRemotePortInUse: "该公网入口端口已被 frps 上的其他代理占用,请改用其他入口端口后重试。",
|
|
849
|
+
frpProxyStartFailed: "frps 拒绝了 frpc 的代理注册,请检查 frps 的 allowPorts、端口占用与服务器日志。",
|
|
841
850
|
frpDiscoveryMismatch: "公开域名连接到了另一台 DSH 电脑,请检查 Caddy 与 frps 映射。",
|
|
842
851
|
frpDiscoveryInvalid: "公开域名没有返回有效的 DSH Mobile 发现信息。",
|
|
843
852
|
frpStopped: "FRP 连接已停止。",
|
|
@@ -2040,13 +2049,39 @@ window.__ModuleLoader__.load({
|
|
|
2040
2049
|
return JSON.stringify(value);
|
|
2041
2050
|
}
|
|
2042
2051
|
/**
|
|
2052
|
+
* Legacy proxy name, kept only as the default of {@link createFrpcToml}.
|
|
2053
|
+
*
|
|
2054
|
+
* A shared frps accepts one proxy per name across *all* its clients, so a fixed
|
|
2055
|
+
* name let any other DSH installation — or a not yet reaped session of this one —
|
|
2056
|
+
* own `dsh-mobile` first. frpc then logged `start error: proxy name [dsh-mobile]
|
|
2057
|
+
* is already in use`, kept running without publishing the entry port, and the
|
|
2058
|
+
* start-up self-check ended in `frp_start_timeout` ("the public endpoint did not
|
|
2059
|
+
* become ready"). Product callers therefore pass an installation-derived name
|
|
2060
|
+
* from {@link frpProxyName}; this constant only keeps the exported API and the
|
|
2061
|
+
* byte-for-byte compatibility test working for legacy callers.
|
|
2062
|
+
*/
|
|
2063
|
+
const FRP_DEFAULT_PROXY_NAME = "dsh-mobile";
|
|
2064
|
+
/**
|
|
2065
|
+
* Validate a proxy name for the generated frpc configuration.
|
|
2066
|
+
*
|
|
2067
|
+
* frps accepts one proxy per name on the whole server, so the name must be a
|
|
2068
|
+
* single safe token: no whitespace, no quotes, no path or bracket characters.
|
|
2069
|
+
*/
|
|
2070
|
+
function validateFrpProxyName(value) {
|
|
2071
|
+
if (typeof value !== "string" || value.length < 1 || value.length > 128 || !/^[A-Za-z0-9._-]+$/u.test(value)) throw new Error("frp_proxy_name_invalid");
|
|
2072
|
+
return value;
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2043
2075
|
* Build the single-purpose frpc configuration for the current loopback gateway.
|
|
2044
2076
|
*
|
|
2045
2077
|
* The default (public-CA) entry is an HTTP vhost behind Caddy and is emitted byte
|
|
2046
2078
|
* for byte as before. The self-signed entry cannot use a vhost at all: frps only
|
|
2047
2079
|
* forwards raw TCP, and the gateway terminates TLS on that connection.
|
|
2080
|
+
*
|
|
2081
|
+
* `proxyName` defaults to the legacy fixed name only for legacy callers; the
|
|
2082
|
+
* provider and the attach preview always pass {@link frpProxyName}.
|
|
2048
2083
|
*/
|
|
2049
|
-
function createFrpcToml(settings, localPort) {
|
|
2084
|
+
function createFrpcToml(settings, localPort, proxyName = FRP_DEFAULT_PROXY_NAME) {
|
|
2050
2085
|
if (!Number.isSafeInteger(localPort) || localPort < 1 || localPort > 65535) throw new Error("frp_local_port_invalid");
|
|
2051
2086
|
const header = [
|
|
2052
2087
|
`serverAddr = ${tomlString(settings.serverAddress)}`,
|
|
@@ -2056,7 +2091,7 @@ window.__ModuleLoader__.load({
|
|
|
2056
2091
|
"transport.tls.enable = true",
|
|
2057
2092
|
"",
|
|
2058
2093
|
"[[proxies]]",
|
|
2059
|
-
|
|
2094
|
+
`name = ${tomlString(validateFrpProxyName(proxyName))}`
|
|
2060
2095
|
];
|
|
2061
2096
|
if (isFrpSelfSignedIngress(settings)) return [
|
|
2062
2097
|
...header,
|
|
@@ -2125,7 +2160,7 @@ window.__ModuleLoader__.load({
|
|
|
2125
2160
|
* the provider writes stays byte-for-byte identical to the upstream output.
|
|
2126
2161
|
*/
|
|
2127
2162
|
function createFrpAttachFrpcToml(settings, options = {}) {
|
|
2128
|
-
const frpcToml = createFrpcToml(settings, attachLocalPort(options));
|
|
2163
|
+
const frpcToml = createFrpcToml(settings, attachLocalPort(options), options.proxyName ?? "dsh-mobile");
|
|
2129
2164
|
return options.revealToken === true ? frpcToml : redactFrpcTomlToken(frpcToml);
|
|
2130
2165
|
}
|
|
2131
2166
|
const DEFAULT_ATTACH_CONFIG_FILE = "<DSH Mobile 私有目录>/remote/frp/config/frpc.toml";
|
|
@@ -2792,6 +2827,48 @@ window.__ModuleLoader__.load({
|
|
|
2792
2827
|
function firstByClassSuffix(root, suffix) {
|
|
2793
2828
|
return Array.from(root.querySelectorAll("[class]")).find((element) => classToken(element, suffix));
|
|
2794
2829
|
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Whether this composer is the landing hero rather than the composer of an open
|
|
2832
|
+
* conversation. DSH marks the hero composer's root with a `_hero` class and drops
|
|
2833
|
+
* it once a session owns the surface, which is the only difference available
|
|
2834
|
+
* without the layout's session id.
|
|
2835
|
+
*/
|
|
2836
|
+
function composerIsLandingHero(composer, depth = 4) {
|
|
2837
|
+
let node = composer;
|
|
2838
|
+
for (let step = 0; step < depth && node !== null; step += 1) {
|
|
2839
|
+
if (classToken(node, "_hero")) return true;
|
|
2840
|
+
node = node.parentElement;
|
|
2841
|
+
}
|
|
2842
|
+
return false;
|
|
2843
|
+
}
|
|
2844
|
+
/** Resolve only identities published by the current conversation or selected session row. */
|
|
2845
|
+
function resolveComposerSessionOrigin(composer, selectedRow, selectedRowToken) {
|
|
2846
|
+
const conversationRoot = composer?.closest("[data-conversation-session]") ?? null;
|
|
2847
|
+
const conversationId = conversationRoot?.getAttribute("data-conversation-session");
|
|
2848
|
+
if (conversationRoot !== null && conversationId) return {
|
|
2849
|
+
sessionRoot: conversationRoot,
|
|
2850
|
+
sessionId: conversationId
|
|
2851
|
+
};
|
|
2852
|
+
const dedicatedRoot = composer?.closest("[data-dsh-mobile-session]") ?? null;
|
|
2853
|
+
const dedicatedId = dedicatedRoot?.getAttribute("data-dsh-mobile-session");
|
|
2854
|
+
if (dedicatedRoot !== null && dedicatedId) return {
|
|
2855
|
+
sessionRoot: dedicatedRoot,
|
|
2856
|
+
sessionId: dedicatedId
|
|
2857
|
+
};
|
|
2858
|
+
if (selectedRow === null) return {
|
|
2859
|
+
sessionRoot: null,
|
|
2860
|
+
sessionId: null
|
|
2861
|
+
};
|
|
2862
|
+
const identity = selectedRow.getAttribute("data-session-id") ?? selectedRow.getAttribute("aria-label") ?? selectedRow.textContent?.trim() ?? "";
|
|
2863
|
+
return {
|
|
2864
|
+
sessionRoot: selectedRow,
|
|
2865
|
+
sessionId: `${selectedRowToken(selectedRow)}:${identity}`
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
/** An id-less non-hero composer still accepts App soft-keyboard line breaks, but not camera results. */
|
|
2869
|
+
function activeSessionForSoftEnter(composer, sessionId) {
|
|
2870
|
+
return sessionId !== null || composer !== null && composer.closest(".dshm-main") !== null && !composerIsLandingHero(composer);
|
|
2871
|
+
}
|
|
2795
2872
|
/** Find the stock DSH application frame without mistaking a feature card for the shell. */
|
|
2796
2873
|
function resolveNativeMobileFrame(root, dedicatedCenter) {
|
|
2797
2874
|
if (dedicatedCenter !== void 0) return void 0;
|
|
@@ -2962,6 +3039,38 @@ window.__ModuleLoader__.load({
|
|
|
2962
3039
|
function drawerScrimVisible(sidebarCollapsed, overlayActive) {
|
|
2963
3040
|
return !sidebarCollapsed && overlayActive;
|
|
2964
3041
|
}
|
|
3042
|
+
/** Let the stock page close one visible layer before Android navigates its WebView. */
|
|
3043
|
+
function installStockMobileBack(view, current, actions) {
|
|
3044
|
+
const onBack = (event) => {
|
|
3045
|
+
if (!event.cancelable || event.defaultPrevented) return;
|
|
3046
|
+
const layers = current();
|
|
3047
|
+
if (layers === null) return;
|
|
3048
|
+
if (layers.detailsOpen) {
|
|
3049
|
+
event.preventDefault();
|
|
3050
|
+
actions.closeDetails();
|
|
3051
|
+
} else if (layers.drawerOpen) {
|
|
3052
|
+
event.preventDefault();
|
|
3053
|
+
actions.closeDrawer();
|
|
3054
|
+
} else if (layers.mainPanelOpen) {
|
|
3055
|
+
event.preventDefault();
|
|
3056
|
+
actions.closeMainPanel();
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
view.addEventListener("dsh-mobile:native-back", onBack);
|
|
3060
|
+
return () => {
|
|
3061
|
+
view.removeEventListener("dsh-mobile:native-back", onBack);
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
function stockLayoutBackControl(value) {
|
|
3065
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
3066
|
+
}
|
|
3067
|
+
/** Whether the stock layout can return from a selected main panel. */
|
|
3068
|
+
function stockMainPanelOpen(value) {
|
|
3069
|
+
const layout = stockLayoutBackControl(value);
|
|
3070
|
+
if (typeof layout?.selectPanel !== "function" || typeof layout.panelInfo?.getSnapshot !== "function") return false;
|
|
3071
|
+
const snapshot = layout.panelInfo.getSnapshot();
|
|
3072
|
+
return typeof snapshot === "object" && snapshot !== null && typeof snapshot.activePanelId === "string";
|
|
3073
|
+
}
|
|
2965
3074
|
/** Only a known on-screen keyboard in an active App composer may change Enter into a line break. */
|
|
2966
3075
|
function isSoftKeyboardEnterLineBreak(event, context) {
|
|
2967
3076
|
if (context.nativeState?.imeVisible !== true || context.nativeState.noHardwareKeyboard !== true || !context.editable || !context.activeSession || context.commandMenuOpen || context.recentlyComposing) return false;
|
|
@@ -3009,7 +3118,7 @@ window.__ModuleLoader__.load({
|
|
|
3009
3118
|
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;
|
|
3010
3119
|
}
|
|
3011
3120
|
/** Add mobile semantics without replacing feature trees. */
|
|
3012
|
-
function installNativeMobileSurface() {
|
|
3121
|
+
function installNativeMobileSurface(backServices) {
|
|
3013
3122
|
document.documentElement.classList.add("dsh-native-mobile-active");
|
|
3014
3123
|
const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language];
|
|
3015
3124
|
const language = resolveNativeMobileLanguage(document.documentElement.lang, browserLanguages);
|
|
@@ -3106,27 +3215,15 @@ window.__ModuleLoader__.load({
|
|
|
3106
3215
|
const sessionTokens = /* @__PURE__ */ new WeakMap();
|
|
3107
3216
|
let nextSessionToken = 0;
|
|
3108
3217
|
const currentSessionOrigin = () => {
|
|
3109
|
-
const dedicatedRoot = boundComposer?.closest("[data-dsh-mobile-session]") ?? null;
|
|
3110
|
-
const dedicatedId = dedicatedRoot?.getAttribute("data-dsh-mobile-session");
|
|
3111
|
-
if (dedicatedRoot !== null && typeof dedicatedId === "string" && dedicatedId !== "") return {
|
|
3112
|
-
sessionRoot: dedicatedRoot,
|
|
3113
|
-
sessionId: dedicatedId
|
|
3114
|
-
};
|
|
3115
3218
|
const selectedRow = document.querySelector("[role=\"treeitem\"][aria-selected=\"true\"]");
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
token
|
|
3123
|
-
|
|
3124
|
-
}
|
|
3125
|
-
const identity = selectedRow.getAttribute("data-session-id") ?? selectedRow.getAttribute("aria-label") ?? selectedRow.textContent?.trim() ?? "";
|
|
3126
|
-
return {
|
|
3127
|
-
sessionRoot: selectedRow,
|
|
3128
|
-
sessionId: `${token}:${identity}`
|
|
3129
|
-
};
|
|
3219
|
+
return resolveComposerSessionOrigin(boundComposer, selectedRow, (row) => {
|
|
3220
|
+
let token = sessionTokens.get(row);
|
|
3221
|
+
if (token === void 0) {
|
|
3222
|
+
token = `stock-${String(++nextSessionToken)}`;
|
|
3223
|
+
sessionTokens.set(row, token);
|
|
3224
|
+
}
|
|
3225
|
+
return token;
|
|
3226
|
+
});
|
|
3130
3227
|
};
|
|
3131
3228
|
const mediaRequestContext = () => {
|
|
3132
3229
|
const session = currentSessionOrigin();
|
|
@@ -3472,7 +3569,7 @@ window.__ModuleLoader__.load({
|
|
|
3472
3569
|
disposeEnterBinding = editor === null || composerCard === null ? void 0 : bindComposerSoftEnter(editor, () => ({
|
|
3473
3570
|
nativeState: window.__DSH_MOBILE_NATIVE__ === void 0 ? null : window.__DSH_MOBILE_KEYBOARD_STATE__,
|
|
3474
3571
|
editable: editor.isConnected && editor.getAttribute("contenteditable") === "true" && editor.getAttribute("aria-disabled") !== "true" && editor.getAttribute("aria-haspopup") !== "menu" && composerCard.getAttribute("aria-busy") !== "true",
|
|
3475
|
-
activeSession: currentSessionOrigin().sessionId
|
|
3572
|
+
activeSession: activeSessionForSoftEnter(composerCard, currentSessionOrigin().sessionId),
|
|
3476
3573
|
commandMenuOpen: composerCard.querySelector("[data-trigger-menu],button[aria-haspopup=\"listbox\"][aria-expanded=\"true\"]") !== null
|
|
3477
3574
|
}));
|
|
3478
3575
|
};
|
|
@@ -3621,6 +3718,33 @@ window.__ModuleLoader__.load({
|
|
|
3621
3718
|
if (sidebar?.dataset.open === "true") toggle?.click();
|
|
3622
3719
|
});
|
|
3623
3720
|
sync();
|
|
3721
|
+
const stopStockBack = installStockMobileBack(window, () => {
|
|
3722
|
+
if (frame?.isConnected !== true) return null;
|
|
3723
|
+
const lastColumn = frame.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
|
|
3724
|
+
return {
|
|
3725
|
+
detailsOpen: overlayQuery.matches && lastColumn !== void 0 && lastColumn !== "" && lastColumn !== "0px" && lastColumn !== "0",
|
|
3726
|
+
drawerOpen: overlayQuery.matches && sidebarRoot?.isConnected === true && toggle?.isConnected === true && !classToken(sidebarRoot, "_collapsed"),
|
|
3727
|
+
mainPanelOpen: stockMainPanelOpen(backServices.getLayout())
|
|
3728
|
+
};
|
|
3729
|
+
}, {
|
|
3730
|
+
closeDetails: () => {
|
|
3731
|
+
const layout = stockLayoutBackControl(backServices.getLayout());
|
|
3732
|
+
if (typeof layout?.closeRightbar !== "function") return;
|
|
3733
|
+
const sidebarRight = backServices.getSidebarRight();
|
|
3734
|
+
if (typeof sidebarRight === "object" && sidebarRight !== null) {
|
|
3735
|
+
const control = sidebarRight;
|
|
3736
|
+
if (typeof control.isExpanded === "function" && typeof control.toggleExpanded === "function" && control.isExpanded()) control.toggleExpanded();
|
|
3737
|
+
}
|
|
3738
|
+
layout.closeRightbar();
|
|
3739
|
+
},
|
|
3740
|
+
closeDrawer: () => {
|
|
3741
|
+
toggle?.click();
|
|
3742
|
+
},
|
|
3743
|
+
closeMainPanel: () => {
|
|
3744
|
+
const layout = stockLayoutBackControl(backServices.getLayout());
|
|
3745
|
+
if (typeof layout?.selectPanel === "function") layout.selectPanel(null);
|
|
3746
|
+
}
|
|
3747
|
+
});
|
|
3624
3748
|
const disposeTaskWatcher = installTaskCompletionWatcher({
|
|
3625
3749
|
completionFallback: false,
|
|
3626
3750
|
label: (kind, sessionLabel) => kind === "done" ? {
|
|
@@ -3633,6 +3757,7 @@ window.__ModuleLoader__.load({
|
|
|
3633
3757
|
});
|
|
3634
3758
|
return () => {
|
|
3635
3759
|
disposed = true;
|
|
3760
|
+
stopStockBack();
|
|
3636
3761
|
disposeTaskWatcher();
|
|
3637
3762
|
mediaRequestGeneration += 1;
|
|
3638
3763
|
mediaPickerAbortController.abort();
|
|
@@ -3737,6 +3862,64 @@ window.__ModuleLoader__.load({
|
|
|
3737
3862
|
function parseMobileRemoteProvider(value) {
|
|
3738
3863
|
return value === "cpolar" || value === "cloudflared" || value === "frp" || value === "origin" ? value : "tailscale";
|
|
3739
3864
|
}
|
|
3865
|
+
/**
|
|
3866
|
+
* Set a surface's visibility; opening a page or overlay brings it to the front.
|
|
3867
|
+
* @param container - the registered surface element.
|
|
3868
|
+
* @param open - whether to show it.
|
|
3869
|
+
*/
|
|
3870
|
+
function setMobileSurfaceOpen(container, open) {
|
|
3871
|
+
container.hidden = !open;
|
|
3872
|
+
if (open && container.isConnected && (container.dataset.dshMobileSurfacePlacement === "page" || container.dataset.dshMobileSurfacePlacement === "overlay")) container.parentElement?.append(container);
|
|
3873
|
+
}
|
|
3874
|
+
function visibleBackLayer(view, element) {
|
|
3875
|
+
if (!element.isConnected || element.hidden || element.closest("[hidden],[inert],[aria-hidden=\"true\"]") !== null) return false;
|
|
3876
|
+
const style = view.getComputedStyle(element);
|
|
3877
|
+
return style.display !== "none" && style.visibility !== "hidden" && element.getClientRects().length > 0;
|
|
3878
|
+
}
|
|
3879
|
+
/**
|
|
3880
|
+
* Consume page-level surfaces and dismissible DSH overlays before layout navigation.
|
|
3881
|
+
* @param view - the page window receiving the native Back request.
|
|
3882
|
+
* @param page - the current DSH document.
|
|
3883
|
+
* @param blocked - feedback for a foreground layer without a safe close action.
|
|
3884
|
+
* @returns a disposer for the capture listener.
|
|
3885
|
+
*/
|
|
3886
|
+
function installMobileNativeBack(view, page, blocked) {
|
|
3887
|
+
const onBack = (event) => {
|
|
3888
|
+
if (!event.cancelable || event.defaultPrevented) return;
|
|
3889
|
+
const claim = () => {
|
|
3890
|
+
event.preventDefault();
|
|
3891
|
+
event.stopImmediatePropagation();
|
|
3892
|
+
};
|
|
3893
|
+
const surface = [...page.querySelectorAll("[data-dsh-mobile-surface-placement=\"page\"], [data-dsh-mobile-surface-placement=\"overlay\"]")].filter((element) => visibleBackLayer(view, element)).at(-1);
|
|
3894
|
+
if (surface !== void 0) {
|
|
3895
|
+
setMobileSurfaceOpen(surface, false);
|
|
3896
|
+
claim();
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
const layers = [...page.querySelectorAll("[role=\"dialog\"][aria-modal=\"true\"], [role=\"menu\"]")].filter((element) => visibleBackLayer(view, element));
|
|
3900
|
+
const top = layers.at(-1);
|
|
3901
|
+
if (top === void 0) return;
|
|
3902
|
+
claim();
|
|
3903
|
+
if (top.getAttribute("role") === "dialog") {
|
|
3904
|
+
const wrapper = top.parentElement;
|
|
3905
|
+
const mask = wrapper?.firstElementChild;
|
|
3906
|
+
const maskStyle = mask === void 0 || mask === null ? void 0 : view.getComputedStyle(mask);
|
|
3907
|
+
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) {
|
|
3908
|
+
mask.click();
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
} else if (top.getAttribute("data-menu-material") === "translucent" && layers.filter((layer) => layer.getAttribute("role") === "menu").length === 1) {
|
|
3912
|
+
page.dispatchEvent(new Event("pointerdown", { bubbles: true }));
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3915
|
+
blocked();
|
|
3916
|
+
};
|
|
3917
|
+
const capture = { capture: true };
|
|
3918
|
+
view.addEventListener("dsh-mobile:native-back", onBack, capture);
|
|
3919
|
+
return () => {
|
|
3920
|
+
view.removeEventListener("dsh-mobile:native-back", onBack, capture);
|
|
3921
|
+
};
|
|
3922
|
+
}
|
|
3740
3923
|
const queuedDefinitions = [];
|
|
3741
3924
|
let queuedLegacyMount;
|
|
3742
3925
|
if (typeof window !== "undefined" && window.dshMobile === void 0) window.dshMobile = {
|
|
@@ -3757,6 +3940,18 @@ window.__ModuleLoader__.load({
|
|
|
3757
3940
|
function selectedMobileControlLocale() {
|
|
3758
3941
|
return selectMobileControlLocale(document.documentElement.lang, navigator.languages?.length ? navigator.languages : [navigator.language]);
|
|
3759
3942
|
}
|
|
3943
|
+
/**
|
|
3944
|
+
* Guidance when an open third-party layer has no safe close action.
|
|
3945
|
+
* @param locale - current Mobile UI language.
|
|
3946
|
+
* @returns localized guidance.
|
|
3947
|
+
*/
|
|
3948
|
+
function mobileBackBlockedMessage(locale) {
|
|
3949
|
+
return {
|
|
3950
|
+
en: "Close the open panel first.",
|
|
3951
|
+
it: "Chiudi prima il pannello aperto.",
|
|
3952
|
+
zh: "请先关闭当前弹层。"
|
|
3953
|
+
}[locale];
|
|
3954
|
+
}
|
|
3760
3955
|
/** Remount one plugin-owned surface when DSH changes the document language. */
|
|
3761
3956
|
function installDshLanguageBoundSurface(install) {
|
|
3762
3957
|
let locale = selectedMobileControlLocale();
|
|
@@ -4205,6 +4400,9 @@ window.__ModuleLoader__.load({
|
|
|
4205
4400
|
frp_vhost_probe_failed: "frpVhostProbeFailed",
|
|
4206
4401
|
frp_launch_failed: "frpLaunchFailed",
|
|
4207
4402
|
frp_start_timeout: "frpTimeout",
|
|
4403
|
+
frp_proxy_name_in_use: "frpProxyNameInUse",
|
|
4404
|
+
frp_remote_port_in_use: "frpRemotePortInUse",
|
|
4405
|
+
frp_proxy_start_failed: "frpProxyStartFailed",
|
|
4208
4406
|
frp_discovery_mismatch: "frpDiscoveryMismatch",
|
|
4209
4407
|
frp_discovery_invalid: "frpDiscoveryInvalid",
|
|
4210
4408
|
frp_stopped: "frpStopped",
|
|
@@ -4266,6 +4464,9 @@ window.__ModuleLoader__.load({
|
|
|
4266
4464
|
"frp_vhost_probe_failed",
|
|
4267
4465
|
"frp_launch_failed",
|
|
4268
4466
|
"frp_start_timeout",
|
|
4467
|
+
"frp_proxy_name_in_use",
|
|
4468
|
+
"frp_remote_port_in_use",
|
|
4469
|
+
"frp_proxy_start_failed",
|
|
4269
4470
|
"frp_discovery_mismatch",
|
|
4270
4471
|
"frp_discovery_invalid",
|
|
4271
4472
|
"frp_stopped",
|
|
@@ -4812,10 +5013,12 @@ window.__ModuleLoader__.load({
|
|
|
4812
5013
|
frpCopyAttachPlan.type = "button";
|
|
4813
5014
|
frpCopyAttachPlan.textContent = t("frpAttachPlan");
|
|
4814
5015
|
frpCopyAttachPlan.hidden = true;
|
|
5016
|
+
frpCopyAttachPlan.disabled = true;
|
|
4815
5017
|
const frpCopyAttachToken = element("button", "dsh-mobile-control__secondary dsh-mobile-control__frp-action");
|
|
4816
5018
|
frpCopyAttachToken.type = "button";
|
|
4817
5019
|
frpCopyAttachToken.textContent = t("frpAttachToken");
|
|
4818
5020
|
frpCopyAttachToken.hidden = true;
|
|
5021
|
+
frpCopyAttachToken.disabled = true;
|
|
4819
5022
|
const frpSelfCheckButton = element("button", "dsh-mobile-control__secondary dsh-mobile-control__frp-action");
|
|
4820
5023
|
frpSelfCheckButton.type = "button";
|
|
4821
5024
|
frpSelfCheckButton.textContent = t("frpAttachSelfCheck");
|
|
@@ -5395,6 +5598,8 @@ window.__ModuleLoader__.load({
|
|
|
5395
5598
|
let configuredFrpOrigin = "";
|
|
5396
5599
|
let configuredFrpVhostPort = 7080;
|
|
5397
5600
|
let configuredFrpPublicPort = 33080;
|
|
5601
|
+
/** Proxy name the host writes into its frpc.toml; the local clipboard copy must match. */
|
|
5602
|
+
let configuredFrpProxyName;
|
|
5398
5603
|
let frpConfiguredMode = "deploy";
|
|
5399
5604
|
let frpConfiguredEntryTls = "public-ip-cert";
|
|
5400
5605
|
let frpModeDraft = false;
|
|
@@ -5825,6 +6030,7 @@ window.__ModuleLoader__.load({
|
|
|
5825
6030
|
if (frpConfigured && !frpEntryTlsDraft) frpEntryTls.value = configuredFrpEntryTls;
|
|
5826
6031
|
if (typeof frpConfiguration.vhostHttpPort === "number") configuredFrpVhostPort = frpConfiguration.vhostHttpPort;
|
|
5827
6032
|
if (typeof frpConfiguration.publicPort === "number") configuredFrpPublicPort = frpConfiguration.publicPort;
|
|
6033
|
+
configuredFrpProxyName = typeof frpConfiguration.proxyName === "string" && frpConfiguration.proxyName !== "" ? frpConfiguration.proxyName : void 0;
|
|
5828
6034
|
if (!frpVhostDraft) {
|
|
5829
6035
|
if (frpMode.value === "attach" && configuredFrpMode === "attach" && configuredFrpEntryTls === "public-ip-cert") {
|
|
5830
6036
|
frpVhostPort.value = String(configuredFrpVhostPort);
|
|
@@ -5835,12 +6041,14 @@ window.__ModuleLoader__.load({
|
|
|
5835
6041
|
const attachSelected = frpMode.value === "attach";
|
|
5836
6042
|
const selfSignedSelected = attachSelected && frpEntryTls.value === "self-signed";
|
|
5837
6043
|
frpStep2Title.textContent = t(attachSelected ? "frpStep2TitleAttach" : "frpStep2Title");
|
|
5838
|
-
frpStep2Text.textContent = t(attachSelected ? "frpStep2TextAttach" : "frpStep2Text");
|
|
6044
|
+
frpStep2Text.textContent = attachSelected && configuredFrpProxyName === void 0 ? t("loadingRemoteStatus") : t(attachSelected ? "frpStep2TextAttach" : "frpStep2Text");
|
|
5839
6045
|
frpStep4Text.textContent = t(attachSelected ? "frpStep4TextAttach" : "frpStep4Text");
|
|
5840
6046
|
frpAppRequirement.textContent = t(selfSignedSelected ? "frpAppRequirementSelfSigned" : "frpAppRequirement");
|
|
5841
6047
|
frpCopyTemplate.hidden = attachSelected;
|
|
5842
6048
|
frpCopyAttachPlan.hidden = !attachSelected;
|
|
5843
6049
|
frpCopyAttachToken.hidden = !attachSelected;
|
|
6050
|
+
frpCopyAttachPlan.disabled = remoteProviderBusy || configuredFrpProxyName === void 0;
|
|
6051
|
+
frpCopyAttachToken.disabled = remoteProviderBusy || configuredFrpProxyName === void 0;
|
|
5844
6052
|
frpSelfCheckButton.hidden = !frpConfigured;
|
|
5845
6053
|
frpVhostPort.disabled = selfSignedSelected;
|
|
5846
6054
|
frpVhostLabel.hidden = selfSignedSelected;
|
|
@@ -6318,6 +6526,13 @@ window.__ModuleLoader__.load({
|
|
|
6318
6526
|
}
|
|
6319
6527
|
remoteStatus.textContent = message;
|
|
6320
6528
|
};
|
|
6529
|
+
const frpClipboardProxyName = () => {
|
|
6530
|
+
if (configuredFrpProxyName === void 0) {
|
|
6531
|
+
remoteStatus.textContent = t("loadingRemoteStatus");
|
|
6532
|
+
loadRemote();
|
|
6533
|
+
}
|
|
6534
|
+
return configuredFrpProxyName;
|
|
6535
|
+
};
|
|
6321
6536
|
frpCopyAttachPlan.addEventListener("click", () => {
|
|
6322
6537
|
const form = frpForm();
|
|
6323
6538
|
if (!validFrpServer(form.serverAddress) || !Number.isSafeInteger(form.serverPort) || form.serverPort < 1 || form.serverPort > 65535 || form.publicOrigin === "") {
|
|
@@ -6331,7 +6546,9 @@ window.__ModuleLoader__.load({
|
|
|
6331
6546
|
showFrpAttachError(code);
|
|
6332
6547
|
return;
|
|
6333
6548
|
}
|
|
6334
|
-
|
|
6549
|
+
const proxyName = frpClipboardProxyName();
|
|
6550
|
+
if (proxyName === void 0) return;
|
|
6551
|
+
navigator.clipboard.writeText(createFrpAttachTemplateForClipboard(attachForm, { proxyName })).then(() => {
|
|
6335
6552
|
remoteStatus.textContent = t("frpAttachPlanCopied");
|
|
6336
6553
|
}, () => {
|
|
6337
6554
|
remoteStatus.textContent = t("frpAttachPlanFailed", { error: t("templateCopyFailed") });
|
|
@@ -6359,7 +6576,7 @@ window.__ModuleLoader__.load({
|
|
|
6359
6576
|
remoteStatus.textContent = t("frpAttachPlanFailed", { error: String(error) });
|
|
6360
6577
|
}).finally(() => {
|
|
6361
6578
|
remoteProviderBusy = false;
|
|
6362
|
-
frpCopyAttachPlan.disabled =
|
|
6579
|
+
frpCopyAttachPlan.disabled = configuredFrpProxyName === void 0;
|
|
6363
6580
|
});
|
|
6364
6581
|
});
|
|
6365
6582
|
/** Copy a token-bearing frpc.toml only from the token the user just typed. */
|
|
@@ -6376,7 +6593,12 @@ window.__ModuleLoader__.load({
|
|
|
6376
6593
|
showFrpAttachError(code);
|
|
6377
6594
|
return;
|
|
6378
6595
|
}
|
|
6379
|
-
|
|
6596
|
+
const proxyName = frpClipboardProxyName();
|
|
6597
|
+
if (proxyName === void 0) return;
|
|
6598
|
+
navigator.clipboard.writeText(createFrpAttachFrpcTomlForClipboard(attachForm, {
|
|
6599
|
+
revealToken: true,
|
|
6600
|
+
proxyName
|
|
6601
|
+
})).then(() => {
|
|
6380
6602
|
remoteStatus.textContent = t("frpAttachTokenCopied");
|
|
6381
6603
|
}, () => {
|
|
6382
6604
|
remoteStatus.textContent = t("frpAttachTokenFailed", { error: t("templateCopyFailed") });
|
|
@@ -7880,6 +8102,9 @@ window.__ModuleLoader__.load({
|
|
|
7880
8102
|
shellLayer().append(node);
|
|
7881
8103
|
window.setTimeout(() => node.remove(), 2600);
|
|
7882
8104
|
};
|
|
8105
|
+
const stopNativeBack = installMobileNativeBack(window, document, () => {
|
|
8106
|
+
toast(mobileBackBlockedMessage(selectedMobileControlLocale()));
|
|
8107
|
+
});
|
|
7883
8108
|
const materializeNativeFile = (value) => {
|
|
7884
8109
|
if (typeof value !== "object" || value === null || !("base64" in value) || !("name" in value)) return value;
|
|
7885
8110
|
const candidate = value;
|
|
@@ -8012,6 +8237,7 @@ window.__ModuleLoader__.load({
|
|
|
8012
8237
|
return registerUniqueDisposable(surfaces, surfaceIds, surface.id, () => {
|
|
8013
8238
|
const container = element("section");
|
|
8014
8239
|
container.dataset.dshMobileSurface = surface.id;
|
|
8240
|
+
container.dataset.dshMobileSurfacePlacement = surface.placement;
|
|
8015
8241
|
container.hidden = surface.placement === "page" || surface.placement === "overlay";
|
|
8016
8242
|
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";
|
|
8017
8243
|
const host = () => surface.placement === "page" || surface.placement === "overlay" ? shellLayer() : surfaceHost(surface.placement) ?? shellLayer();
|
|
@@ -8064,12 +8290,12 @@ window.__ModuleLoader__.load({
|
|
|
8064
8290
|
open: (surfaceId) => {
|
|
8065
8291
|
ensureCurrent();
|
|
8066
8292
|
const entry = surfaces.get(surfaceId);
|
|
8067
|
-
if (entry !== void 0) entry.container
|
|
8293
|
+
if (entry !== void 0) setMobileSurfaceOpen(entry.container, true);
|
|
8068
8294
|
},
|
|
8069
8295
|
close: (surfaceId) => {
|
|
8070
8296
|
ensureCurrent();
|
|
8071
8297
|
const entry = surfaces.get(surfaceId);
|
|
8072
|
-
if (entry !== void 0) entry.container
|
|
8298
|
+
if (entry !== void 0) setMobileSurfaceOpen(entry.container, false);
|
|
8073
8299
|
},
|
|
8074
8300
|
toast: (message) => {
|
|
8075
8301
|
ensureCurrent();
|
|
@@ -8441,6 +8667,7 @@ window.__ModuleLoader__.load({
|
|
|
8441
8667
|
});
|
|
8442
8668
|
return () => {
|
|
8443
8669
|
disposed = true;
|
|
8670
|
+
stopNativeBack();
|
|
8444
8671
|
stopEvents();
|
|
8445
8672
|
stopRefresh();
|
|
8446
8673
|
started = false;
|
|
@@ -8560,7 +8787,10 @@ window.__ModuleLoader__.load({
|
|
|
8560
8787
|
};
|
|
8561
8788
|
});
|
|
8562
8789
|
const removeCustom = installCustomAssets();
|
|
8563
|
-
const removeSurface = installDshLanguageBoundSurface(installNativeMobileSurface
|
|
8790
|
+
const removeSurface = installDshLanguageBoundSurface(() => installNativeMobileSurface({
|
|
8791
|
+
getLayout: () => ctx.get("layout"),
|
|
8792
|
+
getSidebarRight: () => ctx.get("sidebarRight")
|
|
8793
|
+
}));
|
|
8564
8794
|
return () => {
|
|
8565
8795
|
removeSettingsAction();
|
|
8566
8796
|
removeCustom();
|
|
@@ -8643,6 +8873,8 @@ window.__ModuleLoader__.load({
|
|
|
8643
8873
|
exports.handleMissingExtensionManifest = handleMissingExtensionManifest;
|
|
8644
8874
|
exports.inject = inject;
|
|
8645
8875
|
exports.installDshLanguageBoundSurface = installDshLanguageBoundSurface;
|
|
8876
|
+
exports.installMobileNativeBack = installMobileNativeBack;
|
|
8877
|
+
exports.mobileBackBlockedMessage = mobileBackBlockedMessage;
|
|
8646
8878
|
exports.normalizeDiagnosticOverall = normalizeDiagnosticOverall;
|
|
8647
8879
|
exports.normalizeDiagnosticStatus = normalizeDiagnosticStatus;
|
|
8648
8880
|
exports.parseMobileExtensionManifest = parseMobileExtensionManifest;
|
|
@@ -8653,6 +8885,7 @@ window.__ModuleLoader__.load({
|
|
|
8653
8885
|
exports.renderDiagnosticPayloadSafely = renderDiagnosticPayloadSafely;
|
|
8654
8886
|
exports.selectMobileControlLocale = selectMobileControlLocale;
|
|
8655
8887
|
exports.selectedMobileControlLocale = selectedMobileControlLocale;
|
|
8888
|
+
exports.setMobileSurfaceOpen = setMobileSurfaceOpen;
|
|
8656
8889
|
exports.startExtensionChangeStream = startExtensionChangeStream;
|
|
8657
8890
|
exports.startLifecycleRefreshScheduler = startLifecycleRefreshScheduler;
|
|
8658
8891
|
exports.trustAuthenticatedGatewayConnection = trustAuthenticatedGatewayConnection;
|