dsh-pocket 1.2.0 → 1.3.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 CHANGED
@@ -1282,14 +1282,16 @@ function mobileApply(ctx) {
1282
1282
  var name = "dsh-pocket";
1283
1283
  var inject = ["slots", "connection", "layout", "locale", "sessionLogDownload"];
1284
1284
  var styles = {
1285
- card: { background: "var(--dsw-alias-bg-layer-1,#fff)", border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", borderRadius: 12, padding: "14px 16px", maxWidth: 480 },
1286
- block: { borderTop: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", marginTop: 12, paddingTop: 12 },
1287
- muted: { color: "var(--dsw-alias-label-tertiary,#8b93a1)", fontSize: 12 },
1288
- code: { fontFamily: "ui-monospace,Menlo,monospace", fontSize: 12, wordBreak: "break-all", margin: "4px 0 8px" },
1289
- primary: { font: "inherit", cursor: "pointer", border: "none", background: "var(--dsw-alias-brand-primary,#4f6ef7)", color: "#fff", borderRadius: 8, padding: "6px 14px", fontSize: 13 },
1290
- btn: { font: "inherit", cursor: "pointer", border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", background: "var(--dsw-alias-bg-layer-1,#fff)", borderRadius: 8, padding: "6px 14px", fontSize: 13 },
1291
- qr: { width: 220, height: 220, borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", margin: "6px 0" },
1292
- warn: { color: "var(--dsw-alias-state-warn-primary,#b45309)", fontSize: 12 }
1285
+ card: { background: "var(--dsw-alias-bg-layer-1,#fff)", border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", borderRadius: 12, padding: "16px 20px", maxWidth: 480 },
1286
+ block: { borderTop: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", marginTop: 16, paddingTop: 16 },
1287
+ muted: { color: "var(--dsw-alias-label-tertiary,#8b93a1)", fontSize: 12, lineHeight: 1.5 },
1288
+ code: { fontFamily: "ui-monospace,Menlo,monospace", fontSize: 12, wordBreak: "break-all", margin: "6px 0 10px", color: "var(--dsw-alias-label-primary,inherit)" },
1289
+ // 主按钮:官方 md 胶囊形(36px)
1290
+ primary: { font: "inherit", cursor: "pointer", border: "none", background: "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))", color: "#fff", height: 36, padding: "0 16px", borderRadius: 999, fontSize: 13, fontWeight: 500, display: "inline-flex", alignItems: "center", justifyContent: "center" },
1291
+ // 次级按钮:官方 outline/ghost 胶囊形
1292
+ btn: { font: "inherit", cursor: "pointer", border: "1px solid var(--dsw-alias-button-ghost-active-border, var(--dsw-alias-border-l2,#d1d5db))", background: "var(--dsw-alias-bg-layer-1,#fff)", color: "var(--dsw-alias-label-primary,inherit)", height: 36, padding: "0 16px", borderRadius: 999, fontSize: 13, display: "inline-flex", alignItems: "center", justifyContent: "center" },
1293
+ qr: { width: 220, height: 220, borderRadius: 10, border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", margin: "8px 0" },
1294
+ warn: { color: "var(--dsw-alias-state-warn-primary,#b45309)", fontSize: 12, lineHeight: 1.5 }
1293
1295
  };
1294
1296
  function PocketSettingsTab({ rpcCall }) {
1295
1297
  const [status, setStatus] = (0, import_react2.useState)(null);
@@ -1298,6 +1300,12 @@ function PocketSettingsTab({ rpcCall }) {
1298
1300
  const [tunnelState, setTunnelState] = (0, import_react2.useState)(null);
1299
1301
  const [restartNotice, setRestartNotice] = (0, import_react2.useState)(false);
1300
1302
  const [updateInfo, setUpdateInfo] = (0, import_react2.useState)(null);
1303
+ const [now, setNow] = (0, import_react2.useState)(Date.now());
1304
+ (0, import_react2.useEffect)(() => {
1305
+ const t = setInterval(() => setNow(Date.now()), 1e3);
1306
+ return () => clearInterval(t);
1307
+ }, []);
1308
+ const elapsed = (startedAt) => startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)) : 0;
1301
1309
  const call = async (endpoint, payload) => {
1302
1310
  const res = await rpcCall(endpoint, payload);
1303
1311
  if (!res?.ok) throw new Error(res?.error?.message ?? "RPC failed");
@@ -1356,7 +1364,7 @@ function PocketSettingsTab({ rpcCall }) {
1356
1364
  };
1357
1365
  }, []);
1358
1366
  const restartPocket = async () => {
1359
- setUpdateInfo((u) => ({ ...u, restarting: true }));
1367
+ setUpdateInfo((u) => ({ ...u, restarting: true, startedAt: Date.now() }));
1360
1368
  try {
1361
1369
  await Promise.race([
1362
1370
  call(POCKET_ENDPOINTS.restart, {}),
@@ -1373,7 +1381,7 @@ function PocketSettingsTab({ rpcCall }) {
1373
1381
  }
1374
1382
  };
1375
1383
  const runUpdate = async () => {
1376
- setUpdateInfo((u) => ({ ...u, updating: true, result: null }));
1384
+ setUpdateInfo((u) => ({ ...u, updating: true, result: null, startedAt: Date.now() }));
1377
1385
  try {
1378
1386
  const r = await call(POCKET_ENDPOINTS.update, {});
1379
1387
  setUpdateInfo((u) => ({
@@ -1458,7 +1466,7 @@ function PocketSettingsTab({ rpcCall }) {
1458
1466
  (0, import_react2.createElement)(
1459
1467
  "div",
1460
1468
  { style: styles.muted, marginTop: 4 },
1461
- updateInfo.result === "ok" ? updateInfo.autoRestart ? "\u2705 \u5DF2\u66F4\u65B0\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u542F\u751F\u6548\uFF0C\u8BF7\u7A0D\u5019\u5237\u65B0 | updated \u2014 restarting automatically, refresh shortly" : "\u2705 \u5DF2\u66F4\u65B0\uFF0C\u91CD\u542F dsh web \u751F\u6548 | updated \u2014 restart dsh web" : updateInfo.result === "fail" ? `\u274C \u5931\u8D25\uFF1A${updateInfo.output || "\u672A\u77E5"}\uFF08\u624B\u52A8\u66F4\u65B0\uFF1Adsh plugin --profile web update dsh-pocket --latest -w\uFF09` : `\u5F53\u524D v${updateInfo.current} \u2192 \u6700\u65B0 v${updateInfo.latest}`
1469
+ updateInfo.updating ? `\u23F3 \u66F4\u65B0\u4E2D\uFF08\u901A\u5E38 1-2 \u5206\u949F\uFF09\xB7 \u5DF2\u7B49\u5F85 ${elapsed(updateInfo.startedAt)} \u79D2 | updating (usually 1-2 min) \xB7 ${elapsed(updateInfo.startedAt)}s` : updateInfo.restarting ? `\u23F3 \u6B63\u5728\u91CD\u542F\u751F\u6548\uFF08\u901A\u5E38 10-30 \u79D2\uFF09\xB7 \u5DF2\u7B49\u5F85 ${elapsed(updateInfo.startedAt)} \u79D2 | restarting (usually 10-30s) \xB7 ${elapsed(updateInfo.startedAt)}s` : updateInfo.result === "ok" ? updateInfo.autoRestart ? "\u2705 \u5DF2\u66F4\u65B0\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u542F\u751F\u6548\uFF0C\u8BF7\u7A0D\u5019\u5237\u65B0 | updated \u2014 restarting automatically, refresh shortly" : "\u2705 \u5DF2\u66F4\u65B0\uFF0C\u91CD\u542F dsh web \u751F\u6548 | updated \u2014 restart dsh web" : updateInfo.result === "fail" ? `\u274C \u5931\u8D25\uFF1A${updateInfo.output || "\u672A\u77E5"}\uFF08\u624B\u52A8\u66F4\u65B0\uFF1Adsh plugin --profile web update dsh-pocket --latest -w\uFF09` : `\u5F53\u524D v${updateInfo.current} \u2192 \u6700\u65B0 v${updateInfo.latest}`
1462
1470
  )
1463
1471
  ) : null,
1464
1472
  // 局域网
@@ -1494,7 +1502,7 @@ function PocketSettingsTab({ rpcCall }) {
1494
1502
  tunnelStarting ? (0, import_react2.createElement)(
1495
1503
  "div",
1496
1504
  { style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)" } },
1497
- `\u23F3 ${tunnelStateDetail}\uFF08\u5DF2\u7B49\u5F85 ${Math.floor((Date.now() - (tunnelStateStarted || Date.now())) / 1e3)} \u79D2\uFF09\u2026`
1505
+ tunnelPhase === "downloading" ? `\u23F3 \u4E0B\u8F7D cloudflared\uFF08\u9996\u6B21\u7EA6 20-50MB\uFF0C\u901A\u5E38 1-2 \u5206\u949F\uFF1B\u4E4B\u540E\u79D2\u5F00\uFF09\xB7 \u5DF2\u7B49\u5F85 ${elapsed(tunnelStateStarted)} \u79D2` : `\u23F3 \u8FDE\u63A5 Cloudflare \u8FB9\u7F18\uFF08\u901A\u5E38 5-30 \u79D2\uFF09\xB7 \u5DF2\u7B49\u5F85 ${elapsed(tunnelStateStarted)} \u79D2${elapsed(tunnelStateStarted) > 30 ? " \u2014 \u6709\u70B9\u4E45\uFF1F\u68C0\u67E5\u662F\u5426\u5F00\u7740\u4EE3\u7406/VPN\uFF08Clash TUN \u7B49\uFF09" : ""}`
1498
1506
  ) : tunnelPhase === "error" ? (0, import_react2.createElement)(
1499
1507
  "div",
1500
1508
  { style: { marginTop: 8, fontSize: 12, color: "var(--dsw-alias-state-error-primary,#dc2626)" } },
package/client/index.jsx CHANGED
@@ -15,15 +15,20 @@ import { mobileApply } from './mobile/mobile-apply.tsx';
15
15
  const name = 'dsh-pocket';
16
16
  const inject = ['slots', 'connection', 'layout', 'locale', 'sessionLogDownload'];
17
17
 
18
+ // 官方 DeepSeek Harness 设计系统(dsh-client-ui-theme design-platform.css):
19
+ // 按钮 md=36px 胶囊形 / sm=28px;品牌色 --dsw-alias-brand-primary;
20
+ // hover 走 --dsw-alias-button-*-hover;间距 4px 栅格;正文 13px。
18
21
  const styles = {
19
- card: { background: 'var(--dsw-alias-bg-layer-1,#fff)', border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', borderRadius: 12, padding: '14px 16px', maxWidth: 480 },
20
- block: { borderTop: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', marginTop: 12, paddingTop: 12 },
21
- muted: { color: 'var(--dsw-alias-label-tertiary,#8b93a1)', fontSize: 12 },
22
- code: { fontFamily: 'ui-monospace,Menlo,monospace', fontSize: 12, wordBreak: 'break-all', margin: '4px 0 8px' },
23
- primary: { font: 'inherit', cursor: 'pointer', border: 'none', background: 'var(--dsw-alias-brand-primary,#4f6ef7)', color: '#fff', borderRadius: 8, padding: '6px 14px', fontSize: 13 },
24
- btn: { font: 'inherit', cursor: 'pointer', border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', background: 'var(--dsw-alias-bg-layer-1,#fff)', borderRadius: 8, padding: '6px 14px', fontSize: 13 },
25
- qr: { width: 220, height: 220, borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', margin: '6px 0' },
26
- warn: { color: 'var(--dsw-alias-state-warn-primary,#b45309)', fontSize: 12 },
22
+ card: { background: 'var(--dsw-alias-bg-layer-1,#fff)', border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', borderRadius: 12, padding: '16px 20px', maxWidth: 480 },
23
+ block: { borderTop: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', marginTop: 16, paddingTop: 16 },
24
+ muted: { color: 'var(--dsw-alias-label-tertiary,#8b93a1)', fontSize: 12, lineHeight: 1.5 },
25
+ code: { fontFamily: 'ui-monospace,Menlo,monospace', fontSize: 12, wordBreak: 'break-all', margin: '6px 0 10px', color: 'var(--dsw-alias-label-primary,inherit)' },
26
+ // 主按钮:官方 md 胶囊形(36px)
27
+ primary: { font: 'inherit', cursor: 'pointer', border: 'none', background: 'var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary,#4f6ef7))', color: '#fff', height: 36, padding: '0 16px', borderRadius: 999, fontSize: 13, fontWeight: 500, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' },
28
+ // 次级按钮:官方 outline/ghost 胶囊形
29
+ btn: { font: 'inherit', cursor: 'pointer', border: '1px solid var(--dsw-alias-button-ghost-active-border, var(--dsw-alias-border-l2,#d1d5db))', background: 'var(--dsw-alias-bg-layer-1,#fff)', color: 'var(--dsw-alias-label-primary,inherit)', height: 36, padding: '0 16px', borderRadius: 999, fontSize: 13, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' },
30
+ qr: { width: 220, height: 220, borderRadius: 10, border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', margin: '8px 0' },
31
+ warn: { color: 'var(--dsw-alias-state-warn-primary,#b45309)', fontSize: 12, lineHeight: 1.5 },
27
32
  };
28
33
 
29
34
  function PocketSettingsTab({ rpcCall }) {
@@ -32,7 +37,15 @@ function PocketSettingsTab({ rpcCall }) {
32
37
  const [error, setError] = useState(null);
33
38
  const [tunnelState, setTunnelState] = useState(null); // 隧道进度 {phase, detail, startedAt}
34
39
  const [restartNotice, setRestartNotice] = useState(false); // 重启后提示
35
- const [updateInfo, setUpdateInfo] = useState(null); // { current, latest, updating, result } | null
40
+ const [updateInfo, setUpdateInfo] = useState(null); // { current, latest, updating, result, startedAt } | null
41
+ const [now, setNow] = useState(Date.now()); // 每秒 tick,驱动倒计时
42
+
43
+ // 进行中操作的「已等待 X 秒」倒计时
44
+ useEffect(() => {
45
+ const t = setInterval(() => setNow(Date.now()), 1000);
46
+ return () => clearInterval(t);
47
+ }, []);
48
+ const elapsed = (startedAt) => (startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1000)) : 0);
36
49
 
37
50
  const call = async (endpoint, payload) => {
38
51
  const res = await rpcCall(endpoint, payload);
@@ -92,7 +105,7 @@ function PocketSettingsTab({ rpcCall }) {
92
105
 
93
106
  // 重启宿主(更新生效必需:刷新页面不会重载服务端代码)
94
107
  const restartPocket = async () => {
95
- setUpdateInfo((u) => ({ ...u, restarting: true }));
108
+ setUpdateInfo((u) => ({ ...u, restarting: true, startedAt: Date.now() }));
96
109
  try {
97
110
  // 宿主 500ms 后自杀,RPC 响应可能来不及送达 → 3 秒超时兜底,别让按钮永远卡「重启中…」
98
111
  await Promise.race([
@@ -113,7 +126,7 @@ function PocketSettingsTab({ rpcCall }) {
113
126
 
114
127
  // 一键更新:调宿主 dsh plugin update(成功后宿主自动重启生效,用户只点一次)
115
128
  const runUpdate = async () => {
116
- setUpdateInfo((u) => ({ ...u, updating: true, result: null }));
129
+ setUpdateInfo((u) => ({ ...u, updating: true, result: null, startedAt: Date.now() }));
117
130
  try {
118
131
  const r = await call(POCKET_ENDPOINTS.update, {});
119
132
  setUpdateInfo((u) => ({
@@ -187,7 +200,11 @@ function PocketSettingsTab({ rpcCall }) {
187
200
  : h('button', { style: styles.primary, onClick: restartPocket, disabled: updateInfo.restarting }, updateInfo.restarting ? '重启中…' : '🔄 重启 dsh web 生效 | Restart now'),
188
201
  ),
189
202
  h('div', { style: styles.muted, marginTop: 4 },
190
- updateInfo.result === 'ok'
203
+ updateInfo.updating
204
+ ? `⏳ 更新中(通常 1-2 分钟)· 已等待 ${elapsed(updateInfo.startedAt)} 秒 | updating (usually 1-2 min) · ${elapsed(updateInfo.startedAt)}s`
205
+ : updateInfo.restarting
206
+ ? `⏳ 正在重启生效(通常 10-30 秒)· 已等待 ${elapsed(updateInfo.startedAt)} 秒 | restarting (usually 10-30s) · ${elapsed(updateInfo.startedAt)}s`
207
+ : updateInfo.result === 'ok'
191
208
  ? (updateInfo.autoRestart ? '✅ 已更新,正在自动重启生效,请稍候刷新 | updated — restarting automatically, refresh shortly'
192
209
  : '✅ 已更新,重启 dsh web 生效 | updated — restart dsh web')
193
210
  : updateInfo.result === 'fail' ? `❌ 失败:${updateInfo.output || '未知'}(手动更新:dsh plugin --profile web update dsh-pocket --latest -w)`
@@ -221,7 +238,9 @@ function PocketSettingsTab({ rpcCall }) {
221
238
  h('button', { style: styles.primary, onClick: startTunnel, disabled: busy || tunnelStarting }, busy ? '开启中…' : '开启公网访问 | Enable anywhere'),
222
239
  tunnelStarting
223
240
  ? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' } },
224
- `⏳ ${tunnelStateDetail}(已等待 ${Math.floor((Date.now() - (tunnelStateStarted || Date.now())) / 1000)} 秒)…`)
241
+ tunnelPhase === 'downloading'
242
+ ? `⏳ 下载 cloudflared(首次约 20-50MB,通常 1-2 分钟;之后秒开)· 已等待 ${elapsed(tunnelStateStarted)} 秒`
243
+ : `⏳ 连接 Cloudflare 边缘(通常 5-30 秒)· 已等待 ${elapsed(tunnelStateStarted)} 秒${elapsed(tunnelStateStarted) > 30 ? ' — 有点久?检查是否开着代理/VPN(Clash TUN 等)' : ''}`)
225
244
  : tunnelPhase === 'error'
226
245
  ? h('div', { style: { marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#dc2626)' } },
227
246
  `❌ 开启失败:${tunnelStateDetail || '未知错误 | failed'}(可重试;若是代理/VPN 问题见 README 排障)`)
package/package.json CHANGED
@@ -76,5 +76,5 @@
76
76
  "access": "public",
77
77
  "registry": "https://registry.npmjs.org/"
78
78
  },
79
- "version": "1.2.0"
79
+ "version": "1.3.0"
80
80
  }