dsh-pocket 1.16.0 → 2.0.0
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/client/client.js +186 -143
- package/client/index.jsx +137 -126
- package/client/pocket-locales.js +12 -2
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -1365,6 +1365,11 @@ var zh2 = {
|
|
|
1365
1365
|
"updatedRestartDetail": "\u2705 \u5DF2\u66F4\u65B0\uFF0C\u91CD\u542F dsh web \u751F\u6548",
|
|
1366
1366
|
"updateFailed": "\u274C \u5931\u8D25\uFF1A{err}\uFF08\u624B\u52A8\u66F4\u65B0\uFF1Adsh plugin --profile web update dsh-pocket --latest -w\uFF09",
|
|
1367
1367
|
"versionRange": "\u5F53\u524D v{cur} \u2192 \u6700\u65B0 v{latest}",
|
|
1368
|
+
"wanAccess": "\u516C\u7F51\u8BBF\u95EE",
|
|
1369
|
+
"pinLabel": "\u8BBF\u95EE\u5BC6\u7801",
|
|
1370
|
+
"modeLabel": "\u5730\u5740\u6A21\u5F0F",
|
|
1371
|
+
"advAddress": "\u9AD8\u7EA7 \xB7 \u624B\u52A8\u9009\u5730\u5740",
|
|
1372
|
+
"wanOffHint": "\u5F00\u542F\u540E\u53EF\u4ECE\u4EFB\u4F55\u7F51\u7EDC\u8BBF\u95EE\uFF08\u6BCF\u6B21\u5F00\u542F\u9700\u786E\u8BA4\u514D\u8D23\u58F0\u660E\uFF09",
|
|
1368
1373
|
"lanTitle": "\u{1F4F6} \u5C40\u57DF\u7F51\uFF08\u540C\u4E00 WiFi\uFF09",
|
|
1369
1374
|
"lanHint": "\u624B\u673A\u8FDE\u63A5\u540C\u4E00 WiFi \u540E\u626B\u7801\u5373\u53EF\u6253\u5F00",
|
|
1370
1375
|
"lanAccess": "\u5C40\u57DF\u7F51\u8BBF\u95EE",
|
|
@@ -1376,7 +1381,6 @@ var zh2 = {
|
|
|
1376
1381
|
"confirm": "\u786E\u5B9A",
|
|
1377
1382
|
"lanAddress": "\u5C40\u57DF\u7F51\u5730\u5740",
|
|
1378
1383
|
"lanAddressAuto": "\u81EA\u52A8\uFF08\u63A8\u8350\uFF09",
|
|
1379
|
-
"lanAddressHint": "\u9AD8\u7EA7\u9009\u9879\uFF1A\u4E00\u822C\u4E0D\u9700\u8981\u4FEE\u6539\uFF1B\u4F7F\u7528 Tailscale/VPN \u7B49\u8FDC\u7A0B\u8BBF\u95EE\u65F6\u53EF\u624B\u52A8\u9009\u62E9",
|
|
1380
1384
|
"lanPin": "\u5C40\u57DF\u7F51\u8BBF\u95EE\u5BC6\u7801",
|
|
1381
1385
|
"on": "\u5F00",
|
|
1382
1386
|
"off": "\u5173",
|
|
@@ -1411,6 +1415,7 @@ var zh2 = {
|
|
|
1411
1415
|
"namedSecurity": "\u56FA\u5B9A\u57DF\u540D\u957F\u671F\u66B4\u9732\u5728\u516C\u7F51\u3001\u66F4\u6613\u88AB\u626B\u63CF\uFF0C\u5EFA\u8BAE\u540C\u65F6\u8BBE\u7F6E\u81EA\u5B9A\u4E49\u5F3A\u5BC6\u7801\uFF08\u672C\u6A21\u5F0F\u516C\u7F51\u5BC6\u7801\u9ED8\u8BA4\u4E0D\u968F\u91CD\u542F\u8F6E\u6362\uFF09\u3002",
|
|
1412
1416
|
"namedNeedCfg": "\u8BF7\u5148\u586B\u5199\u56FA\u5B9A\u57DF\u540D\u4E0E Tunnel Token",
|
|
1413
1417
|
"namedRunningHint": "\u56FA\u5B9A\u57DF\u540D\uFF08Cloudflare \u547D\u540D\u96A7\u9053\uFF09\u2014\u2014\u5730\u5740\u4E0D\u968F\u91CD\u542F\u53D8\u5316",
|
|
1418
|
+
"namedTakeEffect": "\u5DF2\u4FDD\u5B58\u56FA\u5B9A\u57DF\u540D\u914D\u7F6E\u2014\u2014\u9700\u5173\u95ED\u5E76\u91CD\u65B0\u5F00\u542F\u516C\u7F51\u8BBF\u95EE\u540E\u751F\u6548",
|
|
1414
1419
|
"disclaimerTitle": "\u26A0\uFE0F \u5B89\u5168\u514D\u8D23\u58F0\u660E",
|
|
1415
1420
|
"disclaimerBody": "\u5F00\u542F\u516C\u7F51 = \u628A\u672C\u673A DSH\uFF08\u80FD\u6267\u884C\u4EE3\u7801\uFF09\u66B4\u9732\u5230\u4E92\u8054\u7F51\u3002\u4EFB\u4F55\u4EBA\u62FF\u5230\u516C\u7F51\u94FE\u63A5\u548C\u5BC6\u7801\uFF0C\u90FD\u80FD\u8BBF\u95EE\u751A\u81F3\u64CD\u4F5C\u4F60\u7684\u7535\u8111\u3002\u8BF7\u786E\u8BA4\uFF1A\u2460 \u4F7F\u7528\u81EA\u5B9A\u4E49\u5F3A\u5BC6\u7801\u6216\u59A5\u5584\u4FDD\u7BA1\u81EA\u52A8\u5BC6\u7801\uFF1B\u2461 \u7528\u5B8C\u7ACB\u5373\u300C\u5173\u95ED\u516C\u7F51\u300D\uFF1B\u2462 \u516C\u53F8/\u6D89\u5BC6\u7F51\u7EDC\u8BF7\u5148\u786E\u8BA4\u5408\u89C4\u3002",
|
|
1416
1421
|
"disclaimerAgree": "\u6211\u5DF2\u77E5\u60C5\uFF0C\u540C\u610F\u5F00\u542F",
|
|
@@ -1447,6 +1452,11 @@ var en2 = {
|
|
|
1447
1452
|
"updatedRestartDetail": "\u2705 Updated \u2014 restart dsh web to apply",
|
|
1448
1453
|
"updateFailed": "\u274C Failed: {err} (manual update: dsh plugin --profile web update dsh-pocket --latest -w)",
|
|
1449
1454
|
"versionRange": "Current v{cur} \u2192 latest v{latest}",
|
|
1455
|
+
"wanAccess": "Public access",
|
|
1456
|
+
"pinLabel": "Access PIN",
|
|
1457
|
+
"modeLabel": "Address mode",
|
|
1458
|
+
"advAddress": "Advanced \xB7 Pick address",
|
|
1459
|
+
"wanOffHint": "Reachable from any network once enabled (a disclaimer is confirmed on each enable)",
|
|
1450
1460
|
"lanTitle": "\u{1F4F6} LAN (same Wi-Fi)",
|
|
1451
1461
|
"lanHint": "Scan to open once your phone is on the same Wi-Fi",
|
|
1452
1462
|
"lanAccess": "LAN access",
|
|
@@ -1458,7 +1468,6 @@ var en2 = {
|
|
|
1458
1468
|
"confirm": "Confirm",
|
|
1459
1469
|
"lanAddress": "LAN address",
|
|
1460
1470
|
"lanAddressAuto": "Auto (recommended)",
|
|
1461
|
-
"lanAddressHint": "Advanced option: usually no change needed; select manually when accessing through Tailscale/VPN",
|
|
1462
1471
|
"lanPin": "LAN access PIN",
|
|
1463
1472
|
"on": "On",
|
|
1464
1473
|
"off": "Off",
|
|
@@ -1493,6 +1502,7 @@ var en2 = {
|
|
|
1493
1502
|
"namedSecurity": "A fixed domain is long-lived and easier to scan \u2014 set a strong custom PIN too (the public PIN is not rotated on restart in this mode).",
|
|
1494
1503
|
"namedNeedCfg": "Set the fixed domain and Tunnel Token first",
|
|
1495
1504
|
"namedRunningHint": "Fixed domain (Cloudflare named tunnel) \u2014 the URL no longer changes on restart",
|
|
1505
|
+
"namedTakeEffect": "Fixed-domain config saved \u2014 turn public access off and on again to take effect",
|
|
1496
1506
|
"disclaimerTitle": "\u26A0\uFE0F Security disclaimer",
|
|
1497
1507
|
"disclaimerBody": "Enabling public access exposes this computer\u2019s DSH (which can execute code) to the internet. Anyone with the public link and PIN can reach \u2014 and operate \u2014 your computer. Please confirm: \u2460 use a strong custom PIN or keep the auto-generated one safe; \u2461 turn public access OFF as soon as you\u2019re done; \u2462 on a corporate/classified network, confirm compliance first.",
|
|
1498
1508
|
"disclaimerAgree": "I understand and agree",
|
|
@@ -1771,6 +1781,41 @@ function PocketSettingsTab({ rpcCall, t }) {
|
|
|
1771
1781
|
const tunnelStateStarted = tunnelState?.startedAt ?? null;
|
|
1772
1782
|
const tunnelModeView = status?.tunnelConfig ?? { mode: "quick", hostname: "", tokenSet: false };
|
|
1773
1783
|
const namedMode = tunnelModeView.mode === "named";
|
|
1784
|
+
const namedActive = namedMode || tunnelCfg !== null;
|
|
1785
|
+
const modeBtnStyle = (active) => ({
|
|
1786
|
+
...styles.btn,
|
|
1787
|
+
height: 28,
|
|
1788
|
+
padding: "0 12px",
|
|
1789
|
+
fontSize: 12,
|
|
1790
|
+
fontWeight: active ? 600 : 400,
|
|
1791
|
+
background: active ? "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))" : "var(--dsw-alias-bg-layer-1,#fff)",
|
|
1792
|
+
color: active ? "var(--dsw-alias-label-primary-foreground, #fff)" : "var(--dsw-alias-label-primary,inherit)"
|
|
1793
|
+
});
|
|
1794
|
+
const Switch = (on, onClick) => (0, import_react2.createElement)("button", {
|
|
1795
|
+
role: "switch",
|
|
1796
|
+
"aria-checked": !!on,
|
|
1797
|
+
style: { flexShrink: 0, width: 40, height: 22, borderRadius: 11, border: "none", padding: 0, position: "relative", cursor: "pointer", font: "inherit", background: on ? "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))" : "var(--dsw-alias-border-l2,#d1d5db)" },
|
|
1798
|
+
onClick
|
|
1799
|
+
}, (0, import_react2.createElement)("span", { style: { position: "absolute", top: 2, left: on ? 20 : 2, width: 18, height: 18, borderRadius: "50%", background: "#fff" } }));
|
|
1800
|
+
const qrArea = (src, url, hint) => (0, import_react2.createElement)(
|
|
1801
|
+
"div",
|
|
1802
|
+
{ style: { background: "var(--dsw-alias-bg-layer-2,#f3f4f6)", borderRadius: 10, padding: "10px 12px", textAlign: "center", margin: "10px 0" } },
|
|
1803
|
+
(0, import_react2.createElement)("img", { src, alt: "QR", style: styles.qr }),
|
|
1804
|
+
(0, import_react2.createElement)("div", { style: styles.code }, url),
|
|
1805
|
+
(0, import_react2.createElement)("div", { style: styles.muted }, hint)
|
|
1806
|
+
);
|
|
1807
|
+
const row = (label, control, extra) => (0, import_react2.createElement)(
|
|
1808
|
+
"div",
|
|
1809
|
+
{ style: { borderTop: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", paddingTop: 9, marginTop: 9 } },
|
|
1810
|
+
(0, import_react2.createElement)(
|
|
1811
|
+
"div",
|
|
1812
|
+
{ style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 } },
|
|
1813
|
+
(0, import_react2.createElement)("span", { style: { fontSize: 13 } }, label),
|
|
1814
|
+
control
|
|
1815
|
+
),
|
|
1816
|
+
extra ?? null
|
|
1817
|
+
);
|
|
1818
|
+
const [advOpen, setAdvOpen] = (0, import_react2.useState)(false);
|
|
1774
1819
|
return (0, import_react2.createElement)(
|
|
1775
1820
|
"div",
|
|
1776
1821
|
{ style: styles.card },
|
|
@@ -1829,176 +1874,174 @@ function PocketSettingsTab({ rpcCall, t }) {
|
|
|
1829
1874
|
updateInfo.updating ? fmt(t, "updatingDetail", { s: elapsed(updateInfo.startedAt) }) : updateInfo.restarting ? fmt(t, "restartingDetail", { s: elapsed(updateInfo.startedAt) }) : updateInfo.result === "ok" ? updateInfo.autoRestart ? t("updatedAutoDetail") : t("updatedRestartDetail") : updateInfo.result === "fail" ? fmt(t, "updateFailed", { err: updateInfo.output || t("unknownError") }) : fmt(t, "versionRange", { cur: updateInfo.current, latest: updateInfo.latest })
|
|
1830
1875
|
)
|
|
1831
1876
|
) : null,
|
|
1832
|
-
//
|
|
1877
|
+
// 局域网:标题行自带总开关 → 二维码+地址 → 设置行(访问密码 / 高级·手动选地址)
|
|
1833
1878
|
(0, import_react2.createElement)(
|
|
1834
1879
|
"div",
|
|
1835
1880
|
{ style: styles.block },
|
|
1836
|
-
(0, import_react2.createElement)("div", { style: { fontWeight: 600, fontSize: 13 } }, t("lanTitle")),
|
|
1837
|
-
// 局域网访问总开关:关闭后扫码/链接直接失效(公网不受影响)
|
|
1838
1881
|
(0, import_react2.createElement)(
|
|
1839
1882
|
"div",
|
|
1840
|
-
{ style: { display: "flex", alignItems: "center",
|
|
1841
|
-
(0, import_react2.createElement)("span", { style: {
|
|
1842
|
-
(
|
|
1843
|
-
style: { ...styles.btn, height: 28, padding: "0 12px", fontSize: 12, fontWeight: status?.lanEnabled !== false ? 600 : 400, background: status?.lanEnabled !== false ? "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))" : "var(--dsw-alias-bg-layer-1,#fff)", color: status?.lanEnabled !== false ? "var(--dsw-alias-label-primary-foreground, #fff)" : "var(--dsw-alias-label-primary,inherit)" },
|
|
1844
|
-
onClick: () => requestLanToggle(true)
|
|
1845
|
-
}, t("on")),
|
|
1846
|
-
(0, import_react2.createElement)("button", {
|
|
1847
|
-
style: { ...styles.btn, height: 28, padding: "0 12px", fontSize: 12, fontWeight: status?.lanEnabled === false ? 600 : 400, background: status?.lanEnabled === false ? "var(--dsw-alias-state-error-primary,#dc2626)" : "var(--dsw-alias-bg-layer-1,#fff)", color: status?.lanEnabled === false ? "#fff" : "var(--dsw-alias-label-primary,inherit)" },
|
|
1848
|
-
onClick: () => requestLanToggle(false)
|
|
1849
|
-
}, t("off"))
|
|
1883
|
+
{ style: { display: "flex", alignItems: "center", justifyContent: "space-between" } },
|
|
1884
|
+
(0, import_react2.createElement)("span", { style: { fontWeight: 600, fontSize: 13 } }, t("lanAccess")),
|
|
1885
|
+
Switch(status?.lanEnabled !== false, () => requestLanToggle(status?.lanEnabled === false))
|
|
1850
1886
|
),
|
|
1851
1887
|
status?.lanEnabled === false ? (0, import_react2.createElement)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-state-warn-primary,#b45309)", lineHeight: 1.5 } }, t("lanDisabledHint")) : lanUrl ? (0, import_react2.createElement)(
|
|
1852
1888
|
"div",
|
|
1853
1889
|
null,
|
|
1854
|
-
(
|
|
1855
|
-
|
|
1856
|
-
(
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
"
|
|
1863
|
-
{
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
style: { font: "inherit", height: 30, padding: "0 8px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", background: "var(--dsw-alias-bg-layer-1,#fff)", color: "var(--dsw-alias-label-primary,inherit)" }
|
|
1867
|
-
},
|
|
1868
|
-
(0, import_react2.createElement)("option", { value: "" }, t("lanAddressAuto")),
|
|
1869
|
-
(status?.lanCandidates || []).map((ip) => (0, import_react2.createElement)("option", { key: ip, value: ip }, ip))
|
|
1890
|
+
qrArea(status.lanQr, lanUrl, t("lanHint")),
|
|
1891
|
+
// 访问密码行:开关 + 值(关闭时提示直连)
|
|
1892
|
+
row(
|
|
1893
|
+
t("lanPin"),
|
|
1894
|
+
Switch(status?.lanAuthEnabled !== false, () => setLanAuth(status?.lanAuthEnabled === false)),
|
|
1895
|
+
status?.lanAuthEnabled === false ? (0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 6 } }, t("lanPinOff")) : customPin?.which === "lan" ? customPinRow("lan") : (0, import_react2.createElement)(
|
|
1896
|
+
"div",
|
|
1897
|
+
{ style: { marginTop: 6, display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" } },
|
|
1898
|
+
(0, import_react2.createElement)("span", { style: { fontFamily: "ui-monospace,Menlo,monospace", fontSize: 13, letterSpacing: 1 } }, status.lanToken),
|
|
1899
|
+
(0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: refreshLanPin }, t("refresh")),
|
|
1900
|
+
customBtn("lan"),
|
|
1901
|
+
status?.lanPinCustom ? (0, import_react2.createElement)("span", { style: { fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)" } }, t("pinCustomHint")) : null
|
|
1870
1902
|
)
|
|
1871
1903
|
),
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1904
|
+
// 高级:手动选地址(默认收起)
|
|
1905
|
+
row(
|
|
1906
|
+
t("advAddress"),
|
|
1907
|
+
(0, import_react2.createElement)(
|
|
1908
|
+
"button",
|
|
1909
|
+
{ style: { border: "none", background: "none", font: "inherit", cursor: "pointer", fontSize: 12, color: "var(--dsw-alias-label-tertiary,#8b93a1)", padding: 0 }, onClick: () => setAdvOpen((v) => !v) },
|
|
1910
|
+
(status?.lanIpOverride || t("lanAddressAuto")) + " \u203A"
|
|
1911
|
+
),
|
|
1912
|
+
advOpen ? (0, import_react2.createElement)(
|
|
1913
|
+
"div",
|
|
1914
|
+
{ style: { marginTop: 8 } },
|
|
1915
|
+
(0, import_react2.createElement)(
|
|
1916
|
+
"label",
|
|
1917
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)" } },
|
|
1918
|
+
t("lanAddress"),
|
|
1919
|
+
(0, import_react2.createElement)(
|
|
1920
|
+
"select",
|
|
1921
|
+
{
|
|
1922
|
+
value: status?.lanIpOverride || "",
|
|
1923
|
+
onChange: (e) => setLanAddress(e.target.value),
|
|
1924
|
+
style: { font: "inherit", height: 30, padding: "0 8px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", background: "var(--dsw-alias-bg-layer-1,#fff)", color: "var(--dsw-alias-label-primary,inherit)" }
|
|
1925
|
+
},
|
|
1926
|
+
(0, import_react2.createElement)("option", { value: "" }, t("lanAddressAuto")),
|
|
1927
|
+
(status?.lanCandidates || []).map((ip) => (0, import_react2.createElement)("option", { key: ip, value: ip }, ip))
|
|
1928
|
+
)
|
|
1929
|
+
)
|
|
1930
|
+
) : null
|
|
1897
1931
|
)
|
|
1898
1932
|
) : (0, import_react2.createElement)("div", { style: styles.muted }, t("lanStarting"))
|
|
1899
1933
|
),
|
|
1900
|
-
//
|
|
1934
|
+
// 公网:标题行自带 开启/关闭 → 开启后:二维码+地址、地址模式行、访问密码行
|
|
1901
1935
|
(0, import_react2.createElement)(
|
|
1902
1936
|
"div",
|
|
1903
1937
|
{ style: styles.block },
|
|
1904
|
-
(0, import_react2.createElement)(
|
|
1905
|
-
tunnelUrl ? (0, import_react2.createElement)(
|
|
1938
|
+
(0, import_react2.createElement)(
|
|
1906
1939
|
"div",
|
|
1907
|
-
|
|
1908
|
-
(0, import_react2.createElement)("
|
|
1909
|
-
(0, import_react2.createElement)("
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1940
|
+
{ style: { display: "flex", alignItems: "center", justifyContent: "space-between" } },
|
|
1941
|
+
(0, import_react2.createElement)("span", { style: { fontWeight: 600, fontSize: 13 } }, t("wanAccess")),
|
|
1942
|
+
tunnelUrl ? (0, import_react2.createElement)("button", { style: { ...styles.btn, height: 28, padding: "0 12px", fontSize: 12, color: "var(--dsw-alias-state-error-primary,#dc2626)" }, onClick: stopTunnel }, t("stopTunnel")) : (0, import_react2.createElement)("button", { style: { ...styles.primary, height: 28, padding: "0 14px", fontSize: 12 }, onClick: startTunnel, disabled: busy || tunnelStarting }, busy || tunnelStarting ? t("opening") : t("enable"))
|
|
1943
|
+
),
|
|
1944
|
+
tunnelStarting ? (0, import_react2.createElement)(
|
|
1945
|
+
"div",
|
|
1946
|
+
{ style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)" } },
|
|
1947
|
+
tunnelPhase === "downloading" ? fmt(t, "downloading", { s: elapsed(tunnelStateStarted) }) : fmt(t, "connecting", { s: elapsed(tunnelStateStarted), suffix: elapsed(tunnelStateStarted) > 30 ? t("slowHint") : "" })
|
|
1948
|
+
) : tunnelPhase === "error" ? (0, import_react2.createElement)(
|
|
1949
|
+
"div",
|
|
1950
|
+
{ style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-state-error-primary,#dc2626)" } },
|
|
1951
|
+
fmt(t, "error", { detail: tunnelStateDetail || t("unknownError") })
|
|
1952
|
+
) : !tunnelUrl && !isDesktop ? (0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 8 } }, t("wanOffHint")) : null,
|
|
1953
|
+
tunnelUrl ? (0, import_react2.createElement)(
|
|
1921
1954
|
"div",
|
|
1922
1955
|
null,
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
(0, import_react2.createElement)("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)" } }, t("tunnelMode")),
|
|
1928
|
-
(0, import_react2.createElement)("button", {
|
|
1929
|
-
style: { ...styles.btn, height: 28, padding: "0 12px", fontSize: 12, fontWeight: !namedMode ? 600 : 400, background: !namedMode ? "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))" : "var(--dsw-alias-bg-layer-1,#fff)", color: !namedMode ? "var(--dsw-alias-label-primary-foreground, #fff)" : "var(--dsw-alias-label-primary,inherit)" },
|
|
1930
|
-
onClick: namedMode ? switchToQuick : void 0
|
|
1931
|
-
}, t("modeQuick")),
|
|
1932
|
-
(0, import_react2.createElement)("button", {
|
|
1933
|
-
style: { ...styles.btn, height: 28, padding: "0 12px", fontSize: 12, fontWeight: namedMode ? 600 : 400, background: namedMode ? "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))" : "var(--dsw-alias-bg-layer-1,#fff)", color: namedMode ? "var(--dsw-alias-label-primary-foreground, #fff)" : "var(--dsw-alias-label-primary,inherit)" },
|
|
1934
|
-
onClick: () => setTunnelCfg(tunnelCfg ? null : { hostname: tunnelModeView.hostname ?? "", token: "", err: null })
|
|
1935
|
-
}, t("modeNamed"))
|
|
1936
|
-
),
|
|
1937
|
-
// 固定域名:已保存的摘要 + 「修改」入口(非编辑态)
|
|
1938
|
-
namedMode && !tunnelCfg ? (0, import_react2.createElement)(
|
|
1939
|
-
"div",
|
|
1940
|
-
{ style: { marginTop: 6, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.5 } },
|
|
1941
|
-
fmt(t, "namedSummary", { host: tunnelModeView.hostname || "\u2014", token: tunnelModeView.tokenSet ? t("namedTokenSet") : t("namedTokenMissing") }),
|
|
1942
|
-
(0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12, marginLeft: 8 }, onClick: () => setTunnelCfg({ hostname: tunnelModeView.hostname ?? "", token: "", err: null }) }, t("namedEdit")),
|
|
1943
|
-
(0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 4 } }, t("namedHow")),
|
|
1944
|
-
!tunnelModeView.tokenSet || !tunnelModeView.hostname ? (0, import_react2.createElement)("div", { style: { marginTop: 2, fontSize: 12, color: "var(--dsw-alias-state-error-primary,#dc2626)" } }, t("namedNeedCfg")) : null
|
|
1945
|
-
) : null,
|
|
1946
|
-
// 固定域名:编辑表单(域名 + Tunnel Token,Token 留空保持不变)
|
|
1947
|
-
tunnelCfg ? (0, import_react2.createElement)(
|
|
1948
|
-
"div",
|
|
1949
|
-
{ style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.6 } },
|
|
1956
|
+
qrArea(status.tunnelQr, tunnelUrl, namedMode ? t("namedRunningHint") : t("wanHint")),
|
|
1957
|
+
// 地址模式行(随机/固定;固定域名选中或编辑时高亮)
|
|
1958
|
+
row(
|
|
1959
|
+
t("modeLabel"),
|
|
1950
1960
|
(0, import_react2.createElement)(
|
|
1951
|
-
"
|
|
1952
|
-
|
|
1953
|
-
t("
|
|
1954
|
-
(0, import_react2.createElement)("
|
|
1955
|
-
style: { margin: "4px 0 0 6px", padding: "4px 8px", fontSize: 13, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", borderRadius: 6, outline: "none", width: 200 },
|
|
1956
|
-
placeholder: "pocket.example.com",
|
|
1957
|
-
value: tunnelCfg.hostname ?? "",
|
|
1958
|
-
autoFocus: true,
|
|
1959
|
-
onChange: (e) => setTunnelCfg((c) => ({ ...c, hostname: e.target.value.trim(), err: null })),
|
|
1960
|
-
onKeyDown: (e) => {
|
|
1961
|
-
if (e.key === "Enter") saveNamedTunnel();
|
|
1962
|
-
if (e.key === "Escape") setTunnelCfg(null);
|
|
1963
|
-
}
|
|
1964
|
-
})
|
|
1961
|
+
"span",
|
|
1962
|
+
{ style: { display: "inline-flex", gap: 6 } },
|
|
1963
|
+
(0, import_react2.createElement)("button", { style: modeBtnStyle(!namedActive), onClick: namedMode ? switchToQuick : tunnelCfg ? () => setTunnelCfg(null) : void 0 }, t("modeQuick")),
|
|
1964
|
+
(0, import_react2.createElement)("button", { style: modeBtnStyle(namedActive), onClick: () => setTunnelCfg(tunnelCfg ? null : { hostname: tunnelModeView.hostname ?? "", token: "", err: null }) }, t("modeNamed"))
|
|
1965
1965
|
),
|
|
1966
1966
|
(0, import_react2.createElement)(
|
|
1967
1967
|
"div",
|
|
1968
1968
|
{ style: { marginTop: 6 } },
|
|
1969
|
-
|
|
1970
|
-
(0, import_react2.createElement)("
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1969
|
+
// 刚保存固定域名但当前连接仍是随机域名:需关闭后重新开启才生效
|
|
1970
|
+
namedMode && /trycloudflare\.com/i.test(tunnelUrl ?? "") ? (0, import_react2.createElement)("div", { style: { ...styles.warn } }, t("namedTakeEffect")) : null,
|
|
1971
|
+
// 固定域名:已保存摘要 + 修改入口(非编辑态)
|
|
1972
|
+
namedMode && !tunnelCfg ? (0, import_react2.createElement)(
|
|
1973
|
+
"div",
|
|
1974
|
+
{ style: { ...styles.muted } },
|
|
1975
|
+
fmt(t, "namedSummary", { host: tunnelModeView.hostname || "\u2014", token: tunnelModeView.tokenSet ? t("namedTokenSet") : t("namedTokenMissing") }),
|
|
1976
|
+
(0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12, marginLeft: 8 }, onClick: () => setTunnelCfg({ hostname: tunnelModeView.hostname ?? "", token: "", err: null }) }, t("namedEdit")),
|
|
1977
|
+
(0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 4 } }, t("namedHow")),
|
|
1978
|
+
!tunnelModeView.tokenSet || !tunnelModeView.hostname ? (0, import_react2.createElement)("div", { style: { marginTop: 2, color: "var(--dsw-alias-state-error-primary,#dc2626)" } }, t("namedNeedCfg")) : null
|
|
1979
|
+
) : null,
|
|
1980
|
+
// 固定域名:编辑表单(域名 + Tunnel Token,Token 留空保持不变)
|
|
1981
|
+
tunnelCfg ? (0, import_react2.createElement)(
|
|
1982
|
+
"div",
|
|
1983
|
+
{ style: { marginTop: 6, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.6 } },
|
|
1984
|
+
(0, import_react2.createElement)(
|
|
1985
|
+
"div",
|
|
1986
|
+
null,
|
|
1987
|
+
t("namedHostnameLabel"),
|
|
1988
|
+
(0, import_react2.createElement)("input", {
|
|
1989
|
+
style: { margin: "4px 0 0 6px", padding: "4px 8px", fontSize: 13, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", borderRadius: 6, outline: "none", width: 200 },
|
|
1990
|
+
placeholder: "pocket.example.com",
|
|
1991
|
+
value: tunnelCfg.hostname ?? "",
|
|
1992
|
+
autoFocus: true,
|
|
1993
|
+
onChange: (e) => setTunnelCfg((c) => ({ ...c, hostname: e.target.value.trim(), err: null })),
|
|
1994
|
+
onKeyDown: (e) => {
|
|
1995
|
+
if (e.key === "Enter") saveNamedTunnel();
|
|
1996
|
+
if (e.key === "Escape") setTunnelCfg(null);
|
|
1997
|
+
}
|
|
1998
|
+
})
|
|
1999
|
+
),
|
|
2000
|
+
(0, import_react2.createElement)(
|
|
2001
|
+
"div",
|
|
2002
|
+
{ style: { marginTop: 6 } },
|
|
2003
|
+
t("namedTokenLabel"),
|
|
2004
|
+
(0, import_react2.createElement)("input", {
|
|
2005
|
+
style: { margin: "4px 0 0 6px", padding: "4px 8px", fontSize: 13, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", borderRadius: 6, outline: "none", width: 240, fontFamily: "ui-monospace,Menlo,monospace" },
|
|
2006
|
+
type: "password",
|
|
2007
|
+
value: tunnelCfg.token ?? "",
|
|
2008
|
+
onChange: (e) => setTunnelCfg((c) => ({ ...c, token: e.target.value.trim(), err: null })),
|
|
2009
|
+
onKeyDown: (e) => {
|
|
2010
|
+
if (e.key === "Enter") saveNamedTunnel();
|
|
2011
|
+
if (e.key === "Escape") setTunnelCfg(null);
|
|
2012
|
+
}
|
|
2013
|
+
})
|
|
2014
|
+
),
|
|
2015
|
+
(0, import_react2.createElement)(
|
|
2016
|
+
"div",
|
|
2017
|
+
{ style: { marginTop: 6, display: "flex", gap: 8 } },
|
|
2018
|
+
(0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: saveNamedTunnel }, t("save")),
|
|
2019
|
+
(0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: () => setTunnelCfg(null) }, t("cancel"))
|
|
2020
|
+
),
|
|
2021
|
+
(0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 6 } }, t("namedHow")),
|
|
2022
|
+
(0, import_react2.createElement)("div", { style: { marginTop: 2, fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)", lineHeight: 1.5 } }, t("namedSecurity")),
|
|
2023
|
+
tunnelCfg.err ? (0, import_react2.createElement)("div", { style: { color: "var(--dsw-alias-state-error-primary,#dc2626)", marginTop: 4 } }, tunnelCfg.err) : null
|
|
2024
|
+
) : null
|
|
2025
|
+
)
|
|
2026
|
+
),
|
|
2027
|
+
// 访问密码行:值 + 自定义(自定义输入态整体替换)
|
|
2028
|
+
status.accessToken ? row(
|
|
2029
|
+
t("pinLabel"),
|
|
2030
|
+
customPin?.which === "public" ? null : (0, import_react2.createElement)(
|
|
2031
|
+
"span",
|
|
2032
|
+
{ style: { display: "inline-flex", alignItems: "center", gap: 8 } },
|
|
2033
|
+
(0, import_react2.createElement)("span", { style: { fontFamily: "ui-monospace,Menlo,monospace", fontSize: 13, letterSpacing: 1 } }, status.accessToken),
|
|
2034
|
+
customBtn("public")
|
|
1980
2035
|
),
|
|
1981
2036
|
(0, import_react2.createElement)(
|
|
1982
2037
|
"div",
|
|
1983
|
-
{ style: { marginTop: 6
|
|
1984
|
-
|
|
1985
|
-
(0, import_react2.createElement)("
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
(0, import_react2.createElement)("div", { style: { marginTop: 2, fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)", lineHeight: 1.5 } }, t("namedSecurity")),
|
|
1989
|
-
tunnelCfg.err ? (0, import_react2.createElement)("div", { style: { color: "var(--dsw-alias-state-error-primary,#dc2626)", marginTop: 4 } }, tunnelCfg.err) : null
|
|
1990
|
-
) : null,
|
|
1991
|
-
(0, import_react2.createElement)("button", { style: { ...styles.primary, margin: "8px 0" }, onClick: startTunnel, disabled: busy || tunnelStarting }, busy ? t("opening") : t("enable")),
|
|
1992
|
-
tunnelStarting ? (0, import_react2.createElement)(
|
|
1993
|
-
"div",
|
|
1994
|
-
{ style: { marginTop: 4, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)" } },
|
|
1995
|
-
tunnelPhase === "downloading" ? fmt(t, "downloading", { s: elapsed(tunnelStateStarted) }) : fmt(t, "connecting", { s: elapsed(tunnelStateStarted), suffix: elapsed(tunnelStateStarted) > 30 ? t("slowHint") : "" })
|
|
1996
|
-
) : tunnelPhase === "error" ? (0, import_react2.createElement)(
|
|
1997
|
-
"div",
|
|
1998
|
-
{ style: { marginTop: 4, fontSize: 12, color: "var(--dsw-alias-state-error-primary,#dc2626)" } },
|
|
1999
|
-
fmt(t, "error", { detail: tunnelStateDetail || t("unknownError") })
|
|
2038
|
+
{ style: { marginTop: 6 } },
|
|
2039
|
+
customPin?.which === "public" ? customPinRow("public") : null,
|
|
2040
|
+
status?.publicPinCustom ? (0, import_react2.createElement)("div", { style: { ...styles.warn } }, t("pinCustomHint")) : null,
|
|
2041
|
+
namedMode ? (0, import_react2.createElement)("div", { style: { ...styles.warn } }, t("namedSecurity")) : null
|
|
2042
|
+
)
|
|
2000
2043
|
) : null
|
|
2001
|
-
)
|
|
2044
|
+
) : null
|
|
2002
2045
|
),
|
|
2003
2046
|
error ? (0, import_react2.createElement)("div", { style: { color: "var(--dsw-alias-state-error-primary,#dc2626)", fontSize: 12, marginTop: 8 } }, `\u274C ${error}`) : null,
|
|
2004
2047
|
// 局域网访问开关确认弹框(关闭/打开时弹窗提醒)
|
package/client/index.jsx
CHANGED
|
@@ -307,6 +307,31 @@ function PocketSettingsTab({ rpcCall, t }) {
|
|
|
307
307
|
// 公网模式视图(issue #66):{ mode, hostname, tokenSet }
|
|
308
308
|
const tunnelModeView = status?.tunnelConfig ?? { mode: 'quick', hostname: '', tokenSet: false };
|
|
309
309
|
const namedMode = tunnelModeView.mode === 'named';
|
|
310
|
+
// 模式按钮选中态高亮:固定域名模式本身,或正在编辑固定域名配置,都视为「选中」
|
|
311
|
+
const namedActive = namedMode || tunnelCfg !== null;
|
|
312
|
+
const modeBtnStyle = (active) => ({
|
|
313
|
+
...styles.btn, height: 28, padding: '0 12px', fontSize: 12,
|
|
314
|
+
fontWeight: active ? 600 : 400,
|
|
315
|
+
background: active ? 'var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))' : 'var(--dsw-alias-bg-layer-1,#fff)',
|
|
316
|
+
color: active ? 'var(--dsw-alias-label-primary-foreground, #fff)' : 'var(--dsw-alias-label-primary,inherit)',
|
|
317
|
+
});
|
|
318
|
+
// iOS 风格小开关(重排后统一用:局域网总开关 / 局域网密码开关)
|
|
319
|
+
const Switch = (on, onClick) => h('button', {
|
|
320
|
+
role: 'switch', 'aria-checked': !!on,
|
|
321
|
+
style: { flexShrink: 0, width: 40, height: 22, borderRadius: 11, border: 'none', padding: 0, position: 'relative', cursor: 'pointer', font: 'inherit', background: on ? 'var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))' : 'var(--dsw-alias-border-l2,#d1d5db)' },
|
|
322
|
+
onClick,
|
|
323
|
+
}, h('span', { style: { position: 'absolute', top: 2, left: on ? 20 : 2, width: 18, height: 18, borderRadius: '50%', background: '#fff' } }));
|
|
324
|
+
// 卡片内主内容:二维码 + 地址 + 提示
|
|
325
|
+
const qrArea = (src, url, hint) => h('div', { style: { background: 'var(--dsw-alias-bg-layer-2,#f3f4f6)', borderRadius: 10, padding: '10px 12px', textAlign: 'center', margin: '10px 0' } },
|
|
326
|
+
h('img', { src, alt: 'QR', style: styles.qr }),
|
|
327
|
+
h('div', { style: styles.code }, url),
|
|
328
|
+
h('div', { style: styles.muted }, hint));
|
|
329
|
+
// 设置行:上分隔线,内部第一行 = 左标签 + 右操作;extra 作为第二段渲染
|
|
330
|
+
const row = (label, control, extra) => h('div', { style: { borderTop: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', paddingTop: 9, marginTop: 9 } },
|
|
331
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
332
|
+
h('span', { style: { fontSize: 13 } }, label), control), extra ?? null);
|
|
333
|
+
// 高级(手动选地址)展开态
|
|
334
|
+
const [advOpen, setAdvOpen] = useState(false);
|
|
310
335
|
|
|
311
336
|
return h('div', { style: styles.card },
|
|
312
337
|
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
@@ -361,148 +386,134 @@ function PocketSettingsTab({ rpcCall, t }) {
|
|
|
361
386
|
: fmt(t, 'versionRange', { cur: updateInfo.current, latest: updateInfo.latest })),
|
|
362
387
|
) : null,
|
|
363
388
|
|
|
364
|
-
//
|
|
389
|
+
// 局域网:标题行自带总开关 → 二维码+地址 → 设置行(访问密码 / 高级·手动选地址)
|
|
365
390
|
h('div', { style: styles.block },
|
|
366
|
-
h('div', { style: {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
h('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } }, t('lanAccess')),
|
|
370
|
-
h('button', {
|
|
371
|
-
style: { ...styles.btn, height: 28, padding: '0 12px', fontSize: 12, fontWeight: status?.lanEnabled !== false ? 600 : 400, background: status?.lanEnabled !== false ? 'var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))' : 'var(--dsw-alias-bg-layer-1,#fff)', color: status?.lanEnabled !== false ? 'var(--dsw-alias-label-primary-foreground, #fff)' : 'var(--dsw-alias-label-primary,inherit)' },
|
|
372
|
-
onClick: () => requestLanToggle(true),
|
|
373
|
-
}, t('on')),
|
|
374
|
-
h('button', {
|
|
375
|
-
style: { ...styles.btn, height: 28, padding: '0 12px', fontSize: 12, fontWeight: status?.lanEnabled === false ? 600 : 400, background: status?.lanEnabled === false ? 'var(--dsw-alias-state-error-primary,#dc2626)' : 'var(--dsw-alias-bg-layer-1,#fff)', color: status?.lanEnabled === false ? '#fff' : 'var(--dsw-alias-label-primary,inherit)' },
|
|
376
|
-
onClick: () => requestLanToggle(false),
|
|
377
|
-
}, t('off')),
|
|
391
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } },
|
|
392
|
+
h('span', { style: { fontWeight: 600, fontSize: 13 } }, t('lanAccess')),
|
|
393
|
+
Switch(status?.lanEnabled !== false, () => requestLanToggle(status?.lanEnabled === false)),
|
|
378
394
|
),
|
|
379
395
|
status?.lanEnabled === false
|
|
380
396
|
? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-state-warn-primary,#b45309)', lineHeight: 1.5 } }, t('lanDisabledHint'))
|
|
381
397
|
: (lanUrl
|
|
382
398
|
? h('div', null,
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
: h('div', { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.5 } },
|
|
414
|
-
fmt(t, status?.lanPinCustom ? 'lanPinCustomValue' : 'lanPinValue', { pin: status.lanToken }),
|
|
415
|
-
h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12, marginLeft: 8 }, onClick: refreshLanPin }, t('refresh')),
|
|
416
|
-
customBtn('lan'),
|
|
417
|
-
))
|
|
418
|
-
: h('div', { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-state-warn-primary,#b45309)', lineHeight: 1.5 } },
|
|
419
|
-
t('lanPinOff')),
|
|
399
|
+
qrArea(status.lanQr, lanUrl, t('lanHint')),
|
|
400
|
+
// 访问密码行:开关 + 值(关闭时提示直连)
|
|
401
|
+
row(t('lanPin'), Switch(status?.lanAuthEnabled !== false, () => setLanAuth(status?.lanAuthEnabled === false)),
|
|
402
|
+
status?.lanAuthEnabled === false
|
|
403
|
+
? h('div', { style: { ...styles.muted, marginTop: 6 } }, t('lanPinOff'))
|
|
404
|
+
: (customPin?.which === 'lan'
|
|
405
|
+
? customPinRow('lan')
|
|
406
|
+
: h('div', { style: { marginTop: 6, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
|
|
407
|
+
h('span', { style: { fontFamily: 'ui-monospace,Menlo,monospace', fontSize: 13, letterSpacing: 1 } }, status.lanToken),
|
|
408
|
+
h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: refreshLanPin }, t('refresh')),
|
|
409
|
+
customBtn('lan'),
|
|
410
|
+
status?.lanPinCustom ? h('span', { style: { fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)' } }, t('pinCustomHint')) : null,
|
|
411
|
+
))),
|
|
412
|
+
// 高级:手动选地址(默认收起)
|
|
413
|
+
row(t('advAddress'),
|
|
414
|
+
h('button', { style: { border: 'none', background: 'none', font: 'inherit', cursor: 'pointer', fontSize: 12, color: 'var(--dsw-alias-label-tertiary,#8b93a1)', padding: 0 }, onClick: () => setAdvOpen((v) => !v) },
|
|
415
|
+
(status?.lanIpOverride || t('lanAddressAuto')) + ' ›'),
|
|
416
|
+
advOpen ? h('div', { style: { marginTop: 8 } },
|
|
417
|
+
h('label', { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } },
|
|
418
|
+
t('lanAddress'),
|
|
419
|
+
h('select', {
|
|
420
|
+
value: status?.lanIpOverride || '',
|
|
421
|
+
onChange: (e) => setLanAddress(e.target.value),
|
|
422
|
+
style: { font: 'inherit', height: 30, padding: '0 8px', borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2,#d1d5db)', background: 'var(--dsw-alias-bg-layer-1,#fff)', color: 'var(--dsw-alias-label-primary,inherit)' },
|
|
423
|
+
},
|
|
424
|
+
h('option', { value: '' }, t('lanAddressAuto')),
|
|
425
|
+
(status?.lanCandidates || []).map((ip) => h('option', { key: ip, value: ip }, ip)),
|
|
426
|
+
),
|
|
427
|
+
),
|
|
428
|
+
) : null),
|
|
420
429
|
)
|
|
421
430
|
: h('div', { style: styles.muted }, t('lanStarting'))),
|
|
422
431
|
),
|
|
423
432
|
|
|
424
|
-
//
|
|
433
|
+
// 公网:标题行自带 开启/关闭 → 开启后:二维码+地址、地址模式行、访问密码行
|
|
425
434
|
h('div', { style: styles.block },
|
|
426
|
-
h('div', { style: {
|
|
435
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' } },
|
|
436
|
+
h('span', { style: { fontWeight: 600, fontSize: 13 } }, t('wanAccess')),
|
|
437
|
+
tunnelUrl
|
|
438
|
+
? h('button', { style: { ...styles.btn, height: 28, padding: '0 12px', fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#dc2626)' }, onClick: stopTunnel }, t('stopTunnel'))
|
|
439
|
+
: h('button', { style: { ...styles.primary, height: 28, padding: '0 14px', fontSize: 12 }, onClick: startTunnel, disabled: busy || tunnelStarting }, busy || tunnelStarting ? t('opening') : t('enable')),
|
|
440
|
+
),
|
|
441
|
+
tunnelStarting
|
|
442
|
+
? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } },
|
|
443
|
+
tunnelPhase === 'downloading'
|
|
444
|
+
? fmt(t, 'downloading', { s: elapsed(tunnelStateStarted) })
|
|
445
|
+
: fmt(t, 'connecting', { s: elapsed(tunnelStateStarted), suffix: elapsed(tunnelStateStarted) > 30 ? t('slowHint') : '' }))
|
|
446
|
+
: tunnelPhase === 'error'
|
|
447
|
+
? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#dc2626)' } },
|
|
448
|
+
fmt(t, 'error', { detail: tunnelStateDetail || t('unknownError') }))
|
|
449
|
+
: (!tunnelUrl && !isDesktop ? h('div', { style: { ...styles.muted, marginTop: 8 } }, t('wanOffHint')) : null),
|
|
427
450
|
tunnelUrl
|
|
428
451
|
? h('div', null,
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
: h('div', { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.5 } },
|
|
436
|
-
fmt(t, status?.publicPinCustom ? 'wanPinCustom' : 'wanPin', { pin: status.accessToken }),
|
|
437
|
-
customBtn('public'),
|
|
438
|
-
status?.publicPinCustom ? h('div', { style: { marginTop: 2, fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)' } }, t('pinCustomHint')) : null,
|
|
439
|
-
namedMode ? h('div', { style: { marginTop: 2, fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)' } }, t('namedSecurity')) : null,
|
|
440
|
-
))
|
|
441
|
-
: null,
|
|
442
|
-
h('button', { style: styles.btn, onClick: stopTunnel }, t('stopTunnel')),
|
|
443
|
-
)
|
|
444
|
-
: h('div', null,
|
|
445
|
-
// 公网模式(issue #66):随机域名(默认)/ 固定域名(命名隧道)
|
|
446
|
-
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 8, flexWrap: 'wrap' } },
|
|
447
|
-
h('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } }, t('tunnelMode')),
|
|
448
|
-
h('button', {
|
|
449
|
-
style: { ...styles.btn, height: 28, padding: '0 12px', fontSize: 12, fontWeight: !namedMode ? 600 : 400, background: !namedMode ? 'var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))' : 'var(--dsw-alias-bg-layer-1,#fff)', color: !namedMode ? 'var(--dsw-alias-label-primary-foreground, #fff)' : 'var(--dsw-alias-label-primary,inherit)' },
|
|
450
|
-
onClick: namedMode ? switchToQuick : undefined,
|
|
451
|
-
}, t('modeQuick')),
|
|
452
|
-
h('button', {
|
|
453
|
-
style: { ...styles.btn, height: 28, padding: '0 12px', fontSize: 12, fontWeight: namedMode ? 600 : 400, background: namedMode ? 'var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))' : 'var(--dsw-alias-bg-layer-1,#fff)', color: namedMode ? 'var(--dsw-alias-label-primary-foreground, #fff)' : 'var(--dsw-alias-label-primary,inherit)' },
|
|
454
|
-
onClick: () => setTunnelCfg(tunnelCfg ? null : { hostname: tunnelModeView.hostname ?? '', token: '', err: null }),
|
|
455
|
-
}, t('modeNamed')),
|
|
456
|
-
),
|
|
457
|
-
// 固定域名:已保存的摘要 + 「修改」入口(非编辑态)
|
|
458
|
-
namedMode && !tunnelCfg ? h('div', { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.5 } },
|
|
459
|
-
fmt(t, 'namedSummary', { host: tunnelModeView.hostname || '—', token: tunnelModeView.tokenSet ? t('namedTokenSet') : t('namedTokenMissing') }),
|
|
460
|
-
h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12, marginLeft: 8 }, onClick: () => setTunnelCfg({ hostname: tunnelModeView.hostname ?? '', token: '', err: null }) }, t('namedEdit')),
|
|
461
|
-
h('div', { style: { ...styles.muted, marginTop: 4 } }, t('namedHow')),
|
|
462
|
-
!tunnelModeView.tokenSet || !tunnelModeView.hostname ? h('div', { style: { marginTop: 2, fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#dc2626)' } }, t('namedNeedCfg')) : null,
|
|
463
|
-
) : null,
|
|
464
|
-
// 固定域名:编辑表单(域名 + Tunnel Token,Token 留空保持不变)
|
|
465
|
-
tunnelCfg ? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.6 } },
|
|
466
|
-
h('div', null,
|
|
467
|
-
t('namedHostnameLabel'),
|
|
468
|
-
h('input', {
|
|
469
|
-
style: { margin: '4px 0 0 6px', padding: '4px 8px', fontSize: 13, border: '1px solid var(--dsw-alias-border-l2,#d1d5db)', borderRadius: 6, outline: 'none', width: 200 },
|
|
470
|
-
placeholder: 'pocket.example.com',
|
|
471
|
-
value: tunnelCfg.hostname ?? '',
|
|
472
|
-
autoFocus: true,
|
|
473
|
-
onChange: (e) => setTunnelCfg((c) => ({ ...c, hostname: e.target.value.trim(), err: null })),
|
|
474
|
-
onKeyDown: (e) => { if (e.key === 'Enter') saveNamedTunnel(); if (e.key === 'Escape') setTunnelCfg(null); },
|
|
475
|
-
}),
|
|
452
|
+
qrArea(status.tunnelQr, tunnelUrl, namedMode ? t('namedRunningHint') : t('wanHint')),
|
|
453
|
+
// 地址模式行(随机/固定;固定域名选中或编辑时高亮)
|
|
454
|
+
row(t('modeLabel'),
|
|
455
|
+
h('span', { style: { display: 'inline-flex', gap: 6 } },
|
|
456
|
+
h('button', { style: modeBtnStyle(!namedActive), onClick: namedMode ? switchToQuick : (tunnelCfg ? () => setTunnelCfg(null) : undefined) }, t('modeQuick')),
|
|
457
|
+
h('button', { style: modeBtnStyle(namedActive), onClick: () => setTunnelCfg(tunnelCfg ? null : { hostname: tunnelModeView.hostname ?? '', token: '', err: null }) }, t('modeNamed')),
|
|
476
458
|
),
|
|
477
459
|
h('div', { style: { marginTop: 6 } },
|
|
478
|
-
|
|
479
|
-
h('
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
h('
|
|
489
|
-
|
|
460
|
+
// 刚保存固定域名但当前连接仍是随机域名:需关闭后重新开启才生效
|
|
461
|
+
namedMode && /trycloudflare\.com/i.test(tunnelUrl ?? '') ? h('div', { style: { ...styles.warn } }, t('namedTakeEffect')) : null,
|
|
462
|
+
// 固定域名:已保存摘要 + 修改入口(非编辑态)
|
|
463
|
+
namedMode && !tunnelCfg ? h('div', { style: { ...styles.muted } },
|
|
464
|
+
fmt(t, 'namedSummary', { host: tunnelModeView.hostname || '—', token: tunnelModeView.tokenSet ? t('namedTokenSet') : t('namedTokenMissing') }),
|
|
465
|
+
h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12, marginLeft: 8 }, onClick: () => setTunnelCfg({ hostname: tunnelModeView.hostname ?? '', token: '', err: null }) }, t('namedEdit')),
|
|
466
|
+
h('div', { style: { ...styles.muted, marginTop: 4 } }, t('namedHow')),
|
|
467
|
+
!tunnelModeView.tokenSet || !tunnelModeView.hostname ? h('div', { style: { marginTop: 2, color: 'var(--dsw-alias-state-error-primary,#dc2626)' } }, t('namedNeedCfg')) : null,
|
|
468
|
+
) : null,
|
|
469
|
+
// 固定域名:编辑表单(域名 + Tunnel Token,Token 留空保持不变)
|
|
470
|
+
tunnelCfg ? h('div', { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.6 } },
|
|
471
|
+
h('div', null,
|
|
472
|
+
t('namedHostnameLabel'),
|
|
473
|
+
h('input', {
|
|
474
|
+
style: { margin: '4px 0 0 6px', padding: '4px 8px', fontSize: 13, border: '1px solid var(--dsw-alias-border-l2,#d1d5db)', borderRadius: 6, outline: 'none', width: 200 },
|
|
475
|
+
placeholder: 'pocket.example.com',
|
|
476
|
+
value: tunnelCfg.hostname ?? '',
|
|
477
|
+
autoFocus: true,
|
|
478
|
+
onChange: (e) => setTunnelCfg((c) => ({ ...c, hostname: e.target.value.trim(), err: null })),
|
|
479
|
+
onKeyDown: (e) => { if (e.key === 'Enter') saveNamedTunnel(); if (e.key === 'Escape') setTunnelCfg(null); },
|
|
480
|
+
}),
|
|
481
|
+
),
|
|
482
|
+
h('div', { style: { marginTop: 6 } },
|
|
483
|
+
t('namedTokenLabel'),
|
|
484
|
+
h('input', {
|
|
485
|
+
style: { margin: '4px 0 0 6px', padding: '4px 8px', fontSize: 13, border: '1px solid var(--dsw-alias-border-l2,#d1d5db)', borderRadius: 6, outline: 'none', width: 240, fontFamily: 'ui-monospace,Menlo,monospace' },
|
|
486
|
+
type: 'password',
|
|
487
|
+
value: tunnelCfg.token ?? '',
|
|
488
|
+
onChange: (e) => setTunnelCfg((c) => ({ ...c, token: e.target.value.trim(), err: null })),
|
|
489
|
+
onKeyDown: (e) => { if (e.key === 'Enter') saveNamedTunnel(); if (e.key === 'Escape') setTunnelCfg(null); },
|
|
490
|
+
}),
|
|
491
|
+
),
|
|
492
|
+
h('div', { style: { marginTop: 6, display: 'flex', gap: 8 } },
|
|
493
|
+
h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: saveNamedTunnel }, t('save')),
|
|
494
|
+
h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: () => setTunnelCfg(null) }, t('cancel')),
|
|
495
|
+
),
|
|
496
|
+
h('div', { style: { ...styles.muted, marginTop: 6 } }, t('namedHow')),
|
|
497
|
+
h('div', { style: { marginTop: 2, fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)', lineHeight: 1.5 } }, t('namedSecurity')),
|
|
498
|
+
tunnelCfg.err ? h('div', { style: { color: 'var(--dsw-alias-state-error-primary,#dc2626)', marginTop: 4 } }, tunnelCfg.err) : null,
|
|
499
|
+
) : null,
|
|
490
500
|
),
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
)
|
|
501
|
+
),
|
|
502
|
+
// 访问密码行:值 + 自定义(自定义输入态整体替换)
|
|
503
|
+
status.accessToken
|
|
504
|
+
? row(t('pinLabel'),
|
|
505
|
+
customPin?.which === 'public'
|
|
506
|
+
? null
|
|
507
|
+
: h('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 8 } },
|
|
508
|
+
h('span', { style: { fontFamily: 'ui-monospace,Menlo,monospace', fontSize: 13, letterSpacing: 1 } }, status.accessToken),
|
|
509
|
+
customBtn('public')),
|
|
510
|
+
h('div', { style: { marginTop: 6 } },
|
|
511
|
+
customPin?.which === 'public' ? customPinRow('public') : null,
|
|
512
|
+
status?.publicPinCustom ? h('div', { style: { ...styles.warn } }, t('pinCustomHint')) : null,
|
|
513
|
+
namedMode ? h('div', { style: { ...styles.warn } }, t('namedSecurity')) : null))
|
|
514
|
+
: null,
|
|
515
|
+
)
|
|
516
|
+
: null,
|
|
506
517
|
),
|
|
507
518
|
|
|
508
519
|
error ? h('div', { style: { color: 'var(--dsw-alias-state-error-primary,#dc2626)', fontSize: 12, marginTop: 8 } }, `❌ ${error}`) : null,
|
package/client/pocket-locales.js
CHANGED
|
@@ -28,6 +28,11 @@ export const zh = {
|
|
|
28
28
|
'updatedRestartDetail': '✅ 已更新,重启 dsh web 生效',
|
|
29
29
|
'updateFailed': '❌ 失败:{err}(手动更新:dsh plugin --profile web update dsh-pocket --latest -w)',
|
|
30
30
|
'versionRange': '当前 v{cur} → 最新 v{latest}',
|
|
31
|
+
'wanAccess': '公网访问',
|
|
32
|
+
'pinLabel': '访问密码',
|
|
33
|
+
'modeLabel': '地址模式',
|
|
34
|
+
'advAddress': '高级 · 手动选地址',
|
|
35
|
+
'wanOffHint': '开启后可从任何网络访问(每次开启需确认免责声明)',
|
|
31
36
|
'lanTitle': '📶 局域网(同一 WiFi)',
|
|
32
37
|
'lanHint': '手机连接同一 WiFi 后扫码即可打开',
|
|
33
38
|
'lanAccess': '局域网访问',
|
|
@@ -39,7 +44,6 @@ export const zh = {
|
|
|
39
44
|
'confirm': '确定',
|
|
40
45
|
'lanAddress': '局域网地址',
|
|
41
46
|
'lanAddressAuto': '自动(推荐)',
|
|
42
|
-
'lanAddressHint': '高级选项:一般不需要修改;使用 Tailscale/VPN 等远程访问时可手动选择',
|
|
43
47
|
'lanPin': '局域网访问密码',
|
|
44
48
|
'on': '开',
|
|
45
49
|
'off': '关',
|
|
@@ -74,6 +78,7 @@ export const zh = {
|
|
|
74
78
|
'namedSecurity': '固定域名长期暴露在公网、更易被扫描,建议同时设置自定义强密码(本模式公网密码默认不随重启轮换)。',
|
|
75
79
|
'namedNeedCfg': '请先填写固定域名与 Tunnel Token',
|
|
76
80
|
'namedRunningHint': '固定域名(Cloudflare 命名隧道)——地址不随重启变化',
|
|
81
|
+
'namedTakeEffect': '已保存固定域名配置——需关闭并重新开启公网访问后生效',
|
|
77
82
|
'disclaimerTitle': '⚠️ 安全免责声明',
|
|
78
83
|
'disclaimerBody': '开启公网 = 把本机 DSH(能执行代码)暴露到互联网。任何人拿到公网链接和密码,都能访问甚至操作你的电脑。请确认:① 使用自定义强密码或妥善保管自动密码;② 用完立即「关闭公网」;③ 公司/涉密网络请先确认合规。',
|
|
79
84
|
'disclaimerAgree': '我已知情,同意开启',
|
|
@@ -112,6 +117,11 @@ export const en = {
|
|
|
112
117
|
'updatedRestartDetail': '✅ Updated — restart dsh web to apply',
|
|
113
118
|
'updateFailed': '❌ Failed: {err} (manual update: dsh plugin --profile web update dsh-pocket --latest -w)',
|
|
114
119
|
'versionRange': 'Current v{cur} → latest v{latest}',
|
|
120
|
+
'wanAccess': 'Public access',
|
|
121
|
+
'pinLabel': 'Access PIN',
|
|
122
|
+
'modeLabel': 'Address mode',
|
|
123
|
+
'advAddress': 'Advanced · Pick address',
|
|
124
|
+
'wanOffHint': 'Reachable from any network once enabled (a disclaimer is confirmed on each enable)',
|
|
115
125
|
'lanTitle': '📶 LAN (same Wi-Fi)',
|
|
116
126
|
'lanHint': 'Scan to open once your phone is on the same Wi-Fi',
|
|
117
127
|
'lanAccess': 'LAN access',
|
|
@@ -123,7 +133,6 @@ export const en = {
|
|
|
123
133
|
'confirm': 'Confirm',
|
|
124
134
|
'lanAddress': 'LAN address',
|
|
125
135
|
'lanAddressAuto': 'Auto (recommended)',
|
|
126
|
-
'lanAddressHint': 'Advanced option: usually no change needed; select manually when accessing through Tailscale/VPN',
|
|
127
136
|
'lanPin': 'LAN access PIN',
|
|
128
137
|
'on': 'On',
|
|
129
138
|
'off': 'Off',
|
|
@@ -158,6 +167,7 @@ export const en = {
|
|
|
158
167
|
'namedSecurity': 'A fixed domain is long-lived and easier to scan — set a strong custom PIN too (the public PIN is not rotated on restart in this mode).',
|
|
159
168
|
'namedNeedCfg': 'Set the fixed domain and Tunnel Token first',
|
|
160
169
|
'namedRunningHint': 'Fixed domain (Cloudflare named tunnel) — the URL no longer changes on restart',
|
|
170
|
+
'namedTakeEffect': 'Fixed-domain config saved — turn public access off and on again to take effect',
|
|
161
171
|
'disclaimerTitle': '⚠️ Security disclaimer',
|
|
162
172
|
'disclaimerBody': 'Enabling public access exposes this computer\u2019s DSH (which can execute code) to the internet. Anyone with the public link and PIN can reach — and operate — your computer. Please confirm: ① use a strong custom PIN or keep the auto-generated one safe; ② turn public access OFF as soon as you\u2019re done; ③ on a corporate/classified network, confirm compliance first.',
|
|
163
173
|
'disclaimerAgree': 'I understand and agree',
|
package/package.json
CHANGED