dsh-pocket 1.16.0 → 1.16.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/client/client.js CHANGED
@@ -1376,7 +1376,6 @@ var zh2 = {
1376
1376
  "confirm": "\u786E\u5B9A",
1377
1377
  "lanAddress": "\u5C40\u57DF\u7F51\u5730\u5740",
1378
1378
  "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
1379
  "lanPin": "\u5C40\u57DF\u7F51\u8BBF\u95EE\u5BC6\u7801",
1381
1380
  "on": "\u5F00",
1382
1381
  "off": "\u5173",
@@ -1411,6 +1410,7 @@ var zh2 = {
1411
1410
  "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
1411
  "namedNeedCfg": "\u8BF7\u5148\u586B\u5199\u56FA\u5B9A\u57DF\u540D\u4E0E Tunnel Token",
1413
1412
  "namedRunningHint": "\u56FA\u5B9A\u57DF\u540D\uFF08Cloudflare \u547D\u540D\u96A7\u9053\uFF09\u2014\u2014\u5730\u5740\u4E0D\u968F\u91CD\u542F\u53D8\u5316",
1413
+ "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
1414
  "disclaimerTitle": "\u26A0\uFE0F \u5B89\u5168\u514D\u8D23\u58F0\u660E",
1415
1415
  "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
1416
  "disclaimerAgree": "\u6211\u5DF2\u77E5\u60C5\uFF0C\u540C\u610F\u5F00\u542F",
@@ -1458,7 +1458,6 @@ var en2 = {
1458
1458
  "confirm": "Confirm",
1459
1459
  "lanAddress": "LAN address",
1460
1460
  "lanAddressAuto": "Auto (recommended)",
1461
- "lanAddressHint": "Advanced option: usually no change needed; select manually when accessing through Tailscale/VPN",
1462
1461
  "lanPin": "LAN access PIN",
1463
1462
  "on": "On",
1464
1463
  "off": "Off",
@@ -1493,6 +1492,7 @@ var en2 = {
1493
1492
  "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
1493
  "namedNeedCfg": "Set the fixed domain and Tunnel Token first",
1495
1494
  "namedRunningHint": "Fixed domain (Cloudflare named tunnel) \u2014 the URL no longer changes on restart",
1495
+ "namedTakeEffect": "Fixed-domain config saved \u2014 turn public access off and on again to take effect",
1496
1496
  "disclaimerTitle": "\u26A0\uFE0F Security disclaimer",
1497
1497
  "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
1498
  "disclaimerAgree": "I understand and agree",
@@ -1771,6 +1771,16 @@ function PocketSettingsTab({ rpcCall, t }) {
1771
1771
  const tunnelStateStarted = tunnelState?.startedAt ?? null;
1772
1772
  const tunnelModeView = status?.tunnelConfig ?? { mode: "quick", hostname: "", tokenSet: false };
1773
1773
  const namedMode = tunnelModeView.mode === "named";
1774
+ const namedActive = namedMode || tunnelCfg !== null;
1775
+ const modeBtnStyle = (active) => ({
1776
+ ...styles.btn,
1777
+ height: 28,
1778
+ padding: "0 12px",
1779
+ fontSize: 12,
1780
+ fontWeight: active ? 600 : 400,
1781
+ background: active ? "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))" : "var(--dsw-alias-bg-layer-1,#fff)",
1782
+ color: active ? "var(--dsw-alias-label-primary-foreground, #fff)" : "var(--dsw-alias-label-primary,inherit)"
1783
+ });
1774
1784
  return (0, import_react2.createElement)(
1775
1785
  "div",
1776
1786
  { style: styles.card },
@@ -1869,7 +1879,6 @@ function PocketSettingsTab({ rpcCall, t }) {
1869
1879
  (status?.lanCandidates || []).map((ip) => (0, import_react2.createElement)("option", { key: ip, value: ip }, ip))
1870
1880
  )
1871
1881
  ),
1872
- (0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 2 } }, t("lanAddressHint")),
1873
1882
  // 访问密码开关(issue #24):默认开启;关闭后扫码直连(仅同一局域网设备可访问)
1874
1883
  (0, import_react2.createElement)(
1875
1884
  "div",
@@ -1916,78 +1925,84 @@ function PocketSettingsTab({ rpcCall, t }) {
1916
1925
  status?.publicPinCustom ? (0, import_react2.createElement)("div", { style: { marginTop: 2, fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)" } }, t("pinCustomHint")) : null,
1917
1926
  namedMode ? (0, import_react2.createElement)("div", { style: { marginTop: 2, fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)" } }, t("namedSecurity")) : null
1918
1927
  ) : null,
1919
- (0, import_react2.createElement)("button", { style: styles.btn, onClick: stopTunnel }, t("stopTunnel"))
1920
- ) : (0, import_react2.createElement)(
1921
- "div",
1922
- null,
1923
- // 公网模式(issue #66):随机域名(默认)/ 固定域名(命名隧道)
1928
+ (0, import_react2.createElement)("button", { style: styles.btn, onClick: stopTunnel }, t("stopTunnel")),
1929
+ // 公网模式(issue #66):仅在公网开启后才显示/可选
1924
1930
  (0, import_react2.createElement)(
1925
1931
  "div",
1926
- { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 8, flexWrap: "wrap" } },
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 } },
1932
+ { style: { marginTop: 10 } },
1950
1933
  (0, import_react2.createElement)(
1951
1934
  "div",
1952
- null,
1953
- t("namedHostnameLabel"),
1954
- (0, import_react2.createElement)("input", {
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
- })
1935
+ { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" } },
1936
+ (0, import_react2.createElement)("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)" } }, t("tunnelMode")),
1937
+ (0, import_react2.createElement)("button", {
1938
+ style: modeBtnStyle(!namedActive),
1939
+ onClick: namedMode ? switchToQuick : tunnelCfg ? () => setTunnelCfg(null) : void 0
1940
+ }, t("modeQuick")),
1941
+ (0, import_react2.createElement)("button", {
1942
+ style: modeBtnStyle(namedActive),
1943
+ onClick: () => setTunnelCfg(tunnelCfg ? null : { hostname: tunnelModeView.hostname ?? "", token: "", err: null })
1944
+ }, t("modeNamed"))
1965
1945
  ),
1966
- (0, import_react2.createElement)(
1946
+ // 刚保存固定域名但当前连接仍是随机域名:需关闭后重新开启才生效
1947
+ namedMode && /trycloudflare\.com/i.test(tunnelUrl ?? "") ? (0, import_react2.createElement)("div", { style: { marginTop: 4, fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)" } }, t("namedTakeEffect")) : null,
1948
+ // 固定域名:已保存的摘要 + 「修改」入口(非编辑态)
1949
+ namedMode && !tunnelCfg ? (0, import_react2.createElement)(
1967
1950
  "div",
1968
- { style: { marginTop: 6 } },
1969
- t("namedTokenLabel"),
1970
- (0, import_react2.createElement)("input", {
1971
- 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" },
1972
- type: "password",
1973
- value: tunnelCfg.token ?? "",
1974
- onChange: (e) => setTunnelCfg((c) => ({ ...c, token: e.target.value.trim(), err: null })),
1975
- onKeyDown: (e) => {
1976
- if (e.key === "Enter") saveNamedTunnel();
1977
- if (e.key === "Escape") setTunnelCfg(null);
1978
- }
1979
- })
1980
- ),
1981
- (0, import_react2.createElement)(
1951
+ { style: { marginTop: 6, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.5 } },
1952
+ fmt(t, "namedSummary", { host: tunnelModeView.hostname || "\u2014", token: tunnelModeView.tokenSet ? t("namedTokenSet") : t("namedTokenMissing") }),
1953
+ (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")),
1954
+ (0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 4 } }, t("namedHow")),
1955
+ !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
1956
+ ) : null,
1957
+ // 固定域名:编辑表单(域名 + Tunnel Token,Token 留空保持不变)
1958
+ tunnelCfg ? (0, import_react2.createElement)(
1982
1959
  "div",
1983
- { style: { marginTop: 6, display: "flex", gap: 8 } },
1984
- (0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: saveNamedTunnel }, t("save")),
1985
- (0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: () => setTunnelCfg(null) }, t("cancel"))
1986
- ),
1987
- (0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 6 } }, t("namedHow")),
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,
1960
+ { style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.6 } },
1961
+ (0, import_react2.createElement)(
1962
+ "div",
1963
+ null,
1964
+ t("namedHostnameLabel"),
1965
+ (0, import_react2.createElement)("input", {
1966
+ 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 },
1967
+ placeholder: "pocket.example.com",
1968
+ value: tunnelCfg.hostname ?? "",
1969
+ autoFocus: true,
1970
+ onChange: (e) => setTunnelCfg((c) => ({ ...c, hostname: e.target.value.trim(), err: null })),
1971
+ onKeyDown: (e) => {
1972
+ if (e.key === "Enter") saveNamedTunnel();
1973
+ if (e.key === "Escape") setTunnelCfg(null);
1974
+ }
1975
+ })
1976
+ ),
1977
+ (0, import_react2.createElement)(
1978
+ "div",
1979
+ { style: { marginTop: 6 } },
1980
+ t("namedTokenLabel"),
1981
+ (0, import_react2.createElement)("input", {
1982
+ 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" },
1983
+ type: "password",
1984
+ value: tunnelCfg.token ?? "",
1985
+ onChange: (e) => setTunnelCfg((c) => ({ ...c, token: e.target.value.trim(), err: null })),
1986
+ onKeyDown: (e) => {
1987
+ if (e.key === "Enter") saveNamedTunnel();
1988
+ if (e.key === "Escape") setTunnelCfg(null);
1989
+ }
1990
+ })
1991
+ ),
1992
+ (0, import_react2.createElement)(
1993
+ "div",
1994
+ { style: { marginTop: 6, display: "flex", gap: 8 } },
1995
+ (0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: saveNamedTunnel }, t("save")),
1996
+ (0, import_react2.createElement)("button", { style: { ...styles.btn, height: 26, padding: "0 10px", fontSize: 12 }, onClick: () => setTunnelCfg(null) }, t("cancel"))
1997
+ ),
1998
+ (0, import_react2.createElement)("div", { style: { ...styles.muted, marginTop: 6 } }, t("namedHow")),
1999
+ (0, import_react2.createElement)("div", { style: { marginTop: 2, fontSize: 11, color: "var(--dsw-alias-state-warn-primary,#b45309)", lineHeight: 1.5 } }, t("namedSecurity")),
2000
+ tunnelCfg.err ? (0, import_react2.createElement)("div", { style: { color: "var(--dsw-alias-state-error-primary,#dc2626)", marginTop: 4 } }, tunnelCfg.err) : null
2001
+ ) : null
2002
+ )
2003
+ ) : (0, import_react2.createElement)(
2004
+ "div",
2005
+ null,
1991
2006
  (0, import_react2.createElement)("button", { style: { ...styles.primary, margin: "8px 0" }, onClick: startTunnel, disabled: busy || tunnelStarting }, busy ? t("opening") : t("enable")),
1992
2007
  tunnelStarting ? (0, import_react2.createElement)(
1993
2008
  "div",
package/client/index.jsx CHANGED
@@ -307,6 +307,14 @@ 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
+ });
310
318
 
311
319
  return h('div', { style: styles.card },
312
320
  h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
@@ -394,7 +402,6 @@ function PocketSettingsTab({ rpcCall, t }) {
394
402
  (status?.lanCandidates || []).map((ip) => h('option', { key: ip, value: ip }, ip)),
395
403
  ),
396
404
  ),
397
- h('div', { style: { ...styles.muted, marginTop: 2 } }, t('lanAddressHint')),
398
405
  // 访问密码开关(issue #24):默认开启;关闭后扫码直连(仅同一局域网设备可访问)
399
406
  h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 } },
400
407
  h('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } }, t('lanPin')),
@@ -440,58 +447,62 @@ function PocketSettingsTab({ rpcCall, t }) {
440
447
  ))
441
448
  : null,
442
449
  h('button', { style: styles.btn, onClick: stopTunnel }, t('stopTunnel')),
450
+ // 公网模式(issue #66):仅在公网开启后才显示/可选
451
+ h('div', { style: { marginTop: 10 } },
452
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } },
453
+ h('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } }, t('tunnelMode')),
454
+ h('button', {
455
+ style: modeBtnStyle(!namedActive),
456
+ onClick: namedMode ? switchToQuick : (tunnelCfg ? () => setTunnelCfg(null) : undefined),
457
+ }, t('modeQuick')),
458
+ h('button', {
459
+ style: modeBtnStyle(namedActive),
460
+ onClick: () => setTunnelCfg(tunnelCfg ? null : { hostname: tunnelModeView.hostname ?? '', token: '', err: null }),
461
+ }, t('modeNamed')),
462
+ ),
463
+ // 刚保存固定域名但当前连接仍是随机域名:需关闭后重新开启才生效
464
+ namedMode && /trycloudflare\.com/i.test(tunnelUrl ?? '') ? h('div', { style: { marginTop: 4, fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)' } }, t('namedTakeEffect')) : null,
465
+ // 固定域名:已保存的摘要 + 「修改」入口(非编辑态)
466
+ namedMode && !tunnelCfg ? h('div', { style: { marginTop: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.5 } },
467
+ fmt(t, 'namedSummary', { host: tunnelModeView.hostname || '—', token: tunnelModeView.tokenSet ? t('namedTokenSet') : t('namedTokenMissing') }),
468
+ h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12, marginLeft: 8 }, onClick: () => setTunnelCfg({ hostname: tunnelModeView.hostname ?? '', token: '', err: null }) }, t('namedEdit')),
469
+ h('div', { style: { ...styles.muted, marginTop: 4 } }, t('namedHow')),
470
+ !tunnelModeView.tokenSet || !tunnelModeView.hostname ? h('div', { style: { marginTop: 2, fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#dc2626)' } }, t('namedNeedCfg')) : null,
471
+ ) : null,
472
+ // 固定域名:编辑表单(域名 + Tunnel Token,Token 留空保持不变)
473
+ tunnelCfg ? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.6 } },
474
+ h('div', null,
475
+ t('namedHostnameLabel'),
476
+ h('input', {
477
+ 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 },
478
+ placeholder: 'pocket.example.com',
479
+ value: tunnelCfg.hostname ?? '',
480
+ autoFocus: true,
481
+ onChange: (e) => setTunnelCfg((c) => ({ ...c, hostname: e.target.value.trim(), err: null })),
482
+ onKeyDown: (e) => { if (e.key === 'Enter') saveNamedTunnel(); if (e.key === 'Escape') setTunnelCfg(null); },
483
+ }),
484
+ ),
485
+ h('div', { style: { marginTop: 6 } },
486
+ t('namedTokenLabel'),
487
+ h('input', {
488
+ 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' },
489
+ type: 'password',
490
+ value: tunnelCfg.token ?? '',
491
+ onChange: (e) => setTunnelCfg((c) => ({ ...c, token: e.target.value.trim(), err: null })),
492
+ onKeyDown: (e) => { if (e.key === 'Enter') saveNamedTunnel(); if (e.key === 'Escape') setTunnelCfg(null); },
493
+ }),
494
+ ),
495
+ h('div', { style: { marginTop: 6, display: 'flex', gap: 8 } },
496
+ h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: saveNamedTunnel }, t('save')),
497
+ h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: () => setTunnelCfg(null) }, t('cancel')),
498
+ ),
499
+ h('div', { style: { ...styles.muted, marginTop: 6 } }, t('namedHow')),
500
+ h('div', { style: { marginTop: 2, fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)', lineHeight: 1.5 } }, t('namedSecurity')),
501
+ tunnelCfg.err ? h('div', { style: { color: 'var(--dsw-alias-state-error-primary,#dc2626)', marginTop: 4 } }, tunnelCfg.err) : null,
502
+ ) : null,
503
+ ),
443
504
  )
444
505
  : 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
- }),
476
- ),
477
- h('div', { style: { marginTop: 6 } },
478
- t('namedTokenLabel'),
479
- h('input', {
480
- 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' },
481
- type: 'password',
482
- value: tunnelCfg.token ?? '',
483
- onChange: (e) => setTunnelCfg((c) => ({ ...c, token: e.target.value.trim(), err: null })),
484
- onKeyDown: (e) => { if (e.key === 'Enter') saveNamedTunnel(); if (e.key === 'Escape') setTunnelCfg(null); },
485
- }),
486
- ),
487
- h('div', { style: { marginTop: 6, display: 'flex', gap: 8 } },
488
- h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: saveNamedTunnel }, t('save')),
489
- h('button', { style: { ...styles.btn, height: 26, padding: '0 10px', fontSize: 12 }, onClick: () => setTunnelCfg(null) }, t('cancel')),
490
- ),
491
- h('div', { style: { ...styles.muted, marginTop: 6 } }, t('namedHow')),
492
- h('div', { style: { marginTop: 2, fontSize: 11, color: 'var(--dsw-alias-state-warn-primary,#b45309)', lineHeight: 1.5 } }, t('namedSecurity')),
493
- tunnelCfg.err ? h('div', { style: { color: 'var(--dsw-alias-state-error-primary,#dc2626)', marginTop: 4 } }, tunnelCfg.err) : null,
494
- ) : null,
495
506
  h('button', { style: { ...styles.primary, margin: '8px 0' }, onClick: startTunnel, disabled: busy || tunnelStarting }, busy ? t('opening') : t('enable')),
496
507
  tunnelStarting
497
508
  ? h('div', { style: { marginTop: 4, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } },
@@ -39,7 +39,6 @@ export const zh = {
39
39
  'confirm': '确定',
40
40
  'lanAddress': '局域网地址',
41
41
  'lanAddressAuto': '自动(推荐)',
42
- 'lanAddressHint': '高级选项:一般不需要修改;使用 Tailscale/VPN 等远程访问时可手动选择',
43
42
  'lanPin': '局域网访问密码',
44
43
  'on': '开',
45
44
  'off': '关',
@@ -74,6 +73,7 @@ export const zh = {
74
73
  'namedSecurity': '固定域名长期暴露在公网、更易被扫描,建议同时设置自定义强密码(本模式公网密码默认不随重启轮换)。',
75
74
  'namedNeedCfg': '请先填写固定域名与 Tunnel Token',
76
75
  'namedRunningHint': '固定域名(Cloudflare 命名隧道)——地址不随重启变化',
76
+ 'namedTakeEffect': '已保存固定域名配置——需关闭并重新开启公网访问后生效',
77
77
  'disclaimerTitle': '⚠️ 安全免责声明',
78
78
  'disclaimerBody': '开启公网 = 把本机 DSH(能执行代码)暴露到互联网。任何人拿到公网链接和密码,都能访问甚至操作你的电脑。请确认:① 使用自定义强密码或妥善保管自动密码;② 用完立即「关闭公网」;③ 公司/涉密网络请先确认合规。',
79
79
  'disclaimerAgree': '我已知情,同意开启',
@@ -123,7 +123,6 @@ export const en = {
123
123
  'confirm': 'Confirm',
124
124
  'lanAddress': 'LAN address',
125
125
  'lanAddressAuto': 'Auto (recommended)',
126
- 'lanAddressHint': 'Advanced option: usually no change needed; select manually when accessing through Tailscale/VPN',
127
126
  'lanPin': 'LAN access PIN',
128
127
  'on': 'On',
129
128
  'off': 'Off',
@@ -158,6 +157,7 @@ export const en = {
158
157
  '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
158
  'namedNeedCfg': 'Set the fixed domain and Tunnel Token first',
160
159
  'namedRunningHint': 'Fixed domain (Cloudflare named tunnel) — the URL no longer changes on restart',
160
+ 'namedTakeEffect': 'Fixed-domain config saved — turn public access off and on again to take effect',
161
161
  'disclaimerTitle': '⚠️ Security disclaimer',
162
162
  '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
163
  'disclaimerAgree': 'I understand and agree',
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  "access": "public",
81
81
  "registry": "https://registry.npmjs.org/"
82
82
  },
83
- "version": "1.16.0"
83
+ "version": "1.16.1"
84
84
  }