@wenbin_wb/dsh-bridge 2.2.4 → 2.2.6

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
@@ -42,6 +42,7 @@ var BRIDGE_ENDPOINTS = {
42
42
  resetCloudflared: "resetCloudflared",
43
43
  saveCustomTunnelConfig: "saveCustomTunnelConfig",
44
44
  checkVersion: "checkVersion",
45
+ upgradePlugin: "upgradePlugin",
45
46
  // 平台管理器(多 IM 平台统一接口)
46
47
  listPlatforms: "listPlatforms",
47
48
  platformLogin: "platformLogin",
@@ -89,20 +90,73 @@ function semverGt(a, b) {
89
90
  return av.pre > bv.pre;
90
91
  }
91
92
  var s = {
92
- card: { background: "var(--dsw-alias-bg-layer-1,transparent)", border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", borderRadius: 12, padding: "16px 20px", marginBottom: 16 },
93
+ card: { background: "var(--dsw-alias-bg-layer-2,#f9fafb)", border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", borderRadius: 12, padding: "16px 20px", marginBottom: 16 },
93
94
  block: { borderTop: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", marginTop: 12, paddingTop: 12 },
94
95
  muted: { color: "var(--dsw-alias-label-tertiary,#8b93a1)", fontSize: 12, lineHeight: 1.5 },
95
96
  label: { color: "var(--dsw-alias-label-primary,currentColor)", fontSize: 13, fontWeight: 500 },
96
97
  code: { fontFamily: "ui-monospace,Menlo,monospace", fontSize: 12, wordBreak: "break-all", color: "var(--dsw-alias-label-primary,currentColor)" },
97
98
  btnPri: { font: "inherit", cursor: "pointer", border: "none", background: "var(--dsw-alias-brand-primary,#4f6ef7)", color: "var(--dsw-alias-label-primary-foreground,#fff)", height: 32, padding: "0 14px", borderRadius: 999, fontSize: 13, fontWeight: 500, display: "inline-flex", alignItems: "center", gap: 4 },
98
- btnGhost: { font: "inherit", cursor: "pointer", border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", background: "var(--dsw-alias-bg-layer-1,transparent)", color: "var(--dsw-alias-label-primary,currentColor)", height: 32, padding: "0 14px", borderRadius: 999, fontSize: 13, display: "inline-flex", alignItems: "center", gap: 4, textDecoration: "none" },
99
+ btnGhost: { font: "inherit", cursor: "pointer", border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", background: "var(--dsw-alias-bg-layer-2,#f9fafb)", color: "var(--dsw-alias-label-primary,currentColor)", height: 32, padding: "0 14px", borderRadius: 999, fontSize: 13, display: "inline-flex", alignItems: "center", gap: 4, textDecoration: "none" },
99
100
  btnLink: { font: "inherit", cursor: "pointer", border: "none", background: "none", color: "var(--dsw-alias-brand-primary,#4f6ef7)", fontSize: 12, padding: 0, display: "inline-flex", alignItems: "center", gap: 3, textDecoration: "none" },
100
- qr: { width: 200, height: 200, borderRadius: 10, border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", margin: "8px 0", display: "block" },
101
+ qr: { width: 200, height: 200, borderRadius: 10, border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", margin: "8px 0", display: "block", background: "#ffffff", padding: 6, boxSizing: "border-box" },
101
102
  tag: { display: "inline-flex", alignItems: "center", padding: "2px 8px", borderRadius: 999, fontSize: 12, fontWeight: 500 },
102
- input: { width: "100%", font: "inherit", fontSize: 13, padding: "7px 10px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", background: "var(--dsw-alias-bg-layer-1,transparent)", color: "var(--dsw-alias-label-primary,currentColor)", outline: "none", boxSizing: "border-box" },
103
+ input: { width: "100%", font: "inherit", fontSize: 13, padding: "7px 10px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#d1d5db)", background: "var(--dsw-alias-bg-layer-2,#f9fafb)", color: "var(--dsw-alias-label-primary,currentColor)", outline: "none", boxSizing: "border-box" },
103
104
  warn: { background: "var(--dsw-alias-state-warn-bg,#fffbeb)", border: "1px solid var(--dsw-alias-state-warn-border,#fde68a)", borderRadius: 8, padding: "10px 14px", fontSize: 12, color: "var(--dsw-alias-state-warn-primary,#92400e)", lineHeight: 1.6 },
104
105
  tip: { background: "var(--dsw-alias-bg-layer-2,#f9fafb)", borderRadius: 8, padding: "10px 14px", fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.6 }
105
106
  };
107
+ var Icons = {
108
+ wechat: (props) => React.createElement(
109
+ "svg",
110
+ { viewBox: "0 0 24 24", width: 18, height: 18, fill: "currentColor", ...props },
111
+ React.createElement("path", { d: "M8.5 2C4.36 2 1 4.91 1 8.5c0 2.01 1.05 3.81 2.69 4.97l-.69 2.06 2.45-1.22c.94.43 1.98.69 3.05.69.21 0 .42-.01.62-.03-.23-.62-.37-1.28-.37-1.97 0-3.59 3.36-6.5 7.5-6.5.21 0 .41.01.62.03C15.87 4.54 12.44 2 8.5 2zM6 6.5a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5zm5 0a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5zm7.5 3.5c-3.59 0-6.5 2.46-6.5 5.5 0 1.66.86 3.14 2.21 4.1l-.56 1.69 2.01-1c.78.36 1.64.57 2.54.57 3.59 0 6.5-2.46 6.5-5.5s-2.91-5.5-6.5-5.5zm-2 4a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm4 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" })
112
+ ),
113
+ qq: (props) => React.createElement(
114
+ "svg",
115
+ { viewBox: "0 0 24 24", width: 18, height: 18, fill: "currentColor", ...props },
116
+ React.createElement("path", { d: "M12 2C7.58 2 4 5.37 4 9.53c0 1.95.78 3.73 2.07 5.07-.37 1.15-.99 2.19-1.8 3.08-.18.2-.04.52.23.52 2.22 0 3.99-1.07 4.9-1.86.8.25 1.66.39 2.6.39 4.42 0 8-3.37 8-7.53S16.42 2 12 2zm-3 8a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5zm6 0a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5z" })
117
+ ),
118
+ feishu: (props) => React.createElement(
119
+ "svg",
120
+ { viewBox: "0 0 24 24", width: 18, height: 18, fill: "currentColor", ...props },
121
+ React.createElement("path", { d: "M12 2.5L3.5 11.2l6.8 1.8 2.2 6.5 1.8-4.7 5.2-1.4L12 2.5zm-.8 11.1l-4.1-1.1 6.5-6.6-4.2 8.3 1.8-.6z" })
122
+ ),
123
+ telegram: (props) => React.createElement(
124
+ "svg",
125
+ { viewBox: "0 0 24 24", width: 18, height: 18, fill: "currentColor", ...props },
126
+ React.createElement("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 0 0-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.74-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z" })
127
+ ),
128
+ lan: (props) => React.createElement(
129
+ "svg",
130
+ { viewBox: "0 0 24 24", width: 16, height: 16, fill: "currentColor", ...props },
131
+ React.createElement("path", { d: "M12 3c-4.97 0-9 4.03-9 9 0 2.12.74 4.07 1.97 5.61L4.29 19.3a1 1 0 0 0 1.41 1.41l1.7-1.7C9.02 19.64 10.46 20 12 20c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 15c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z" })
132
+ ),
133
+ tunnel: (props) => React.createElement(
134
+ "svg",
135
+ { viewBox: "0 0 24 24", width: 16, height: 16, fill: "currentColor", ...props },
136
+ React.createElement("path", { d: "M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3z" })
137
+ ),
138
+ bot: (props) => React.createElement(
139
+ "svg",
140
+ { viewBox: "0 0 24 24", width: 16, height: 16, fill: "currentColor", ...props },
141
+ React.createElement("path", { d: "M20 9V7c0-1.1-.9-2-2-2h-3c0-1.66-1.34-3-3-3S9 3.34 9 5H6c-1.1 0-2 .9-2 2v2c-1.66 0-3 1.34-3 3s1.34 3 3 3v4c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-4c1.66 0 3-1.34 3-3s-1.34-3-3-3zm-2 10H6V7h12v12zm-9-6c-.83 0-1.5-.67-1.5-1.5S8.17 10 9 10s1.5.67 1.5 1.5S9.83 13 9 13zm6 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" })
142
+ ),
143
+ github: (props) => React.createElement(
144
+ "svg",
145
+ { viewBox: "0 0 24 24", width: 13, height: 13, fill: "currentColor", ...props },
146
+ React.createElement("path", { d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.53 1.032 1.53 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" })
147
+ ),
148
+ refresh: (props) => React.createElement(
149
+ "svg",
150
+ { viewBox: "0 0 24 24", width: 12, height: 12, fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", ...props },
151
+ React.createElement("path", { d: "M23 4v6h-6M1 20v-6h6" }),
152
+ React.createElement("path", { d: "M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" })
153
+ ),
154
+ check: (props) => React.createElement(
155
+ "svg",
156
+ { viewBox: "0 0 24 24", width: 12, height: 12, fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", ...props },
157
+ React.createElement("polyline", { points: "20 6 9 17 4 12" })
158
+ )
159
+ };
106
160
  function useCopy() {
107
161
  const [copied, setCopied] = React.useState(false);
108
162
  const copy = React.useCallback((text) => {
@@ -129,14 +183,38 @@ function useCopy() {
129
183
  }, []);
130
184
  return [copied, copy];
131
185
  }
132
- function StatusTag({ running }) {
186
+ function StatusTag({ running, status }) {
187
+ let bg = "var(--dsw-alias-bg-layer-2,#f3f4f6)";
188
+ let color = "var(--dsw-alias-label-secondary,#6b7280)";
189
+ let text = running ? "\u8FD0\u884C\u4E2D" : "\u672A\u542F\u52A8";
190
+ if (status === "connected") {
191
+ bg = "var(--dsw-alias-state-success-bg,#ecfdf5)";
192
+ color = "var(--dsw-alias-state-success-primary,#059669)";
193
+ text = "\u5DF2\u8FDE\u63A5";
194
+ } else if (status === "starting") {
195
+ bg = "var(--dsw-alias-state-info-bg,#eff6ff)";
196
+ color = "var(--dsw-alias-state-info-primary,#3b82f6)";
197
+ text = "\u8FDE\u63A5\u4E2D\u2026";
198
+ } else if (status === "reconnecting") {
199
+ bg = "var(--dsw-alias-state-warn-bg,#fffbeb)";
200
+ color = "var(--dsw-alias-state-warn-primary,#d97706)";
201
+ text = "\u91CD\u8FDE\u4E2D\u2026";
202
+ } else if (status === "paused") {
203
+ bg = "var(--dsw-alias-state-warn-bg,#fffbeb)";
204
+ color = "var(--dsw-alias-state-warn-primary,#d97706)";
205
+ text = "\u6682\u505C\u4E2D";
206
+ } else if (status === "error") {
207
+ bg = "var(--dsw-alias-state-error-bg,#fef2f2)";
208
+ color = "var(--dsw-alias-state-error-primary,#dc2626)";
209
+ text = "\u5F02\u5E38";
210
+ } else if (running) {
211
+ bg = "var(--dsw-alias-state-success-bg,#ecfdf5)";
212
+ color = "var(--dsw-alias-state-success-primary,#059669)";
213
+ text = "\u8FD0\u884C\u4E2D";
214
+ }
133
215
  return React.createElement("span", {
134
- style: {
135
- ...s.tag,
136
- background: running ? "var(--dsw-alias-state-success-bg,#ecfdf5)" : "var(--dsw-alias-bg-layer-2,#f3f4f6)",
137
- color: running ? "var(--dsw-alias-state-success-primary,#059669)" : "var(--dsw-alias-label-secondary,#6b7280)"
138
- }
139
- }, running ? "\u8FD0\u884C\u4E2D" : "\u672A\u542F\u52A8");
216
+ style: { ...s.tag, background: bg, color }
217
+ }, text);
140
218
  }
141
219
  function QrBlock({ url, qr, onReset }) {
142
220
  const [copied, setCopied] = React.useState(false);
@@ -220,6 +298,7 @@ var CustomTunnelConfigForm = React.memo(function CustomTunnelConfigForm2({ serve
220
298
  const [serverUrl, setServerUrl] = React.useState(initUrl ?? "");
221
299
  const [accessToken, setAccessToken] = React.useState(initToken ?? "");
222
300
  const [saving, setSaving] = React.useState(false);
301
+ const [saveSuccess, setSaveSuccess] = React.useState(false);
223
302
  const syncedRef = React.useRef(false);
224
303
  React.useEffect(() => {
225
304
  if (!syncedRef.current && (initUrl || initToken)) {
@@ -231,14 +310,23 @@ var CustomTunnelConfigForm = React.memo(function CustomTunnelConfigForm2({ serve
231
310
  const dirty = serverUrl !== (initUrl ?? "") || accessToken !== (initToken ?? "");
232
311
  const handleSave = React.useCallback(async () => {
233
312
  setSaving(true);
313
+ setSaveSuccess(false);
234
314
  try {
235
315
  await onSave(serverUrl, accessToken);
316
+ setSaveSuccess(true);
317
+ setTimeout(() => setSaveSuccess(false), 2500);
236
318
  } finally {
237
319
  setSaving(false);
238
320
  }
239
321
  }, [onSave, serverUrl, accessToken]);
240
- const handleUrlChange = React.useCallback((e) => setServerUrl(e.target.value), []);
241
- const handleTokenChange = React.useCallback((e) => setAccessToken(e.target.value), []);
322
+ const handleUrlChange = React.useCallback((e) => {
323
+ setServerUrl(e.target.value);
324
+ setSaveSuccess(false);
325
+ }, []);
326
+ const handleTokenChange = React.useCallback((e) => {
327
+ setAccessToken(e.target.value);
328
+ setSaveSuccess(false);
329
+ }, []);
242
330
  return React.createElement(
243
331
  "div",
244
332
  { style: s.block },
@@ -251,6 +339,9 @@ var CustomTunnelConfigForm = React.memo(function CustomTunnelConfigForm2({ serve
251
339
  placeholder: "WebSocket \u5730\u5740\uFF0C\u4F8B\u5982 wss://tunnel.example.com/connect",
252
340
  value: serverUrl,
253
341
  onChange: handleUrlChange,
342
+ onKeyDown: (e) => {
343
+ if (e.key === "Enter" && dirty && !saving) handleSave();
344
+ },
254
345
  disabled: saving
255
346
  }),
256
347
  React.createElement("input", {
@@ -259,13 +350,21 @@ var CustomTunnelConfigForm = React.memo(function CustomTunnelConfigForm2({ serve
259
350
  placeholder: "\u8BBF\u95EE\u4EE4\u724C\uFF08Access Token\uFF09",
260
351
  value: accessToken,
261
352
  onChange: handleTokenChange,
353
+ onKeyDown: (e) => {
354
+ if (e.key === "Enter" && dirty && !saving) handleSave();
355
+ },
262
356
  disabled: saving
263
357
  }),
264
358
  React.createElement("button", {
265
- style: { ...s.btnPri, alignSelf: "flex-start", opacity: !dirty || saving ? 0.5 : 1 },
266
- disabled: !dirty || saving,
359
+ style: {
360
+ ...s.btnPri,
361
+ alignSelf: "flex-start",
362
+ opacity: !dirty || saving ? saveSuccess ? 1 : 0.5 : 1,
363
+ background: saveSuccess ? "var(--dsw-alias-state-success-primary,#059669)" : void 0
364
+ },
365
+ disabled: !dirty && !saveSuccess || saving,
267
366
  onClick: handleSave
268
- }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u914D\u7F6E")
367
+ }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : saveSuccess ? "\u2713 \u5DF2\u4FDD\u5B58" : "\u4FDD\u5B58\u914D\u7F6E")
269
368
  )
270
369
  );
271
370
  });
@@ -381,6 +480,16 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
381
480
  await act(BRIDGE_ENDPOINTS.platformSetAllowFrom, { allowFrom: list });
382
481
  }, [act, platform?.allowFrom]);
383
482
  const handleNewId = React.useCallback((e) => setNewId(e.target.value), []);
483
+ const [showSecret, setShowSecret] = React.useState(false);
484
+ const resetDefaults = React.useCallback(() => {
485
+ setCfgDraft((d) => ({
486
+ ...d,
487
+ digestIntervalSec: "300",
488
+ approvalTimeoutSec: "600",
489
+ maxMessageChars: "2000",
490
+ sendChunkDelayMs: "1500"
491
+ }));
492
+ }, []);
384
493
  const saveConfig = React.useCallback(async () => {
385
494
  if (!cfgDraft) return;
386
495
  const payload = {
@@ -417,12 +526,20 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
417
526
  React.createElement(
418
527
  "div",
419
528
  null,
420
- React.createElement("div", { style: s.label }, platformName),
529
+ React.createElement(
530
+ "div",
531
+ { style: { ...s.label, display: "flex", alignItems: "center", gap: 7 } },
532
+ platformId === "wechat" && React.createElement(Icons.wechat, { style: { color: "#07C160", width: 20, height: 20 } }),
533
+ platformId === "qq" && React.createElement(Icons.qq, { style: { color: "#12B7F5", width: 20, height: 20 } }),
534
+ platformId === "feishu" && React.createElement(Icons.feishu, { style: { color: "#00D6B9", width: 20, height: 20 } }),
535
+ platformId === "telegram" && React.createElement(Icons.telegram, { style: { color: "#24A1DE", width: 20, height: 20 } }),
536
+ platformName
537
+ ),
421
538
  React.createElement("div", { style: { ...s.muted, marginTop: 2 } }, platformDesc)
422
539
  ),
423
- React.createElement(StatusTag, { running: connected })
540
+ React.createElement(StatusTag, { status: platform?.status, running: connected })
424
541
  ),
425
- // 快捷入口:使用说明 / 命令
542
+ // 快捷入口:使用说明 / 开放平台 / 命令速查
426
543
  React.createElement(
427
544
  "div",
428
545
  { style: { display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap", alignItems: "center" } },
@@ -431,7 +548,19 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
431
548
  target: "_blank",
432
549
  rel: "noopener noreferrer",
433
550
  style: s.btnGhost
434
- }, "\u{1F4D6} \u4F7F\u7528\u8BF4\u660E"),
551
+ }, "\u{1F4D6} \u5FAE\u4FE1\u4F7F\u7528\u8BF4\u660E"),
552
+ platformId === "qq" && React.createElement("a", {
553
+ href: "https://github.com/wenbin-wb/dsh-bridge/blob/main/docs/qq-usage.md",
554
+ target: "_blank",
555
+ rel: "noopener noreferrer",
556
+ style: s.btnGhost
557
+ }, "\u{1F4D6} QQ \u4F7F\u7528\u8BF4\u660E"),
558
+ platformId === "qq" && React.createElement("a", {
559
+ href: "https://bot.q.qq.com/wiki/develop/api-v2/",
560
+ target: "_blank",
561
+ rel: "noopener noreferrer",
562
+ style: s.btnGhost
563
+ }, "\u{1F310} QQ \u5F00\u653E\u5E73\u53F0"),
435
564
  React.createElement("button", {
436
565
  style: s.btnGhost,
437
566
  onClick: () => setShowHelp((v) => !v)
@@ -441,32 +570,89 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
441
570
  showHelp && React.createElement(
442
571
  "div",
443
572
  { style: { ...s.block, fontSize: 12, lineHeight: 1.8, fontFamily: "monospace" } },
444
- React.createElement("div", null, "/new <\u63D0\u793A\u8BCD> \u2014 \u65B0\u5EFA\u4F1A\u8BDD\uFF08\u5F53\u524D\u5DE5\u4F5C\u533A\uFF09"),
445
- React.createElement("div", null, "/new <\u63D0\u793A\u8BCD> @N \u2014 \u5728\u6307\u5B9A\u5DE5\u4F5C\u533A\u65B0\u5EFA"),
446
- React.createElement("div", null, "/sessions \u2014 \u6309\u5DE5\u4F5C\u533A\u5206\u7EC4\u5217\u4F1A\u8BDD"),
447
- React.createElement("div", null, "/use N \u2014 \u5207\u6362\u5230\u4F1A\u8BDD N"),
448
- React.createElement("div", null, "/workspaces \u2014 \u5217\u51FA\u5DE5\u4F5C\u533A"),
449
- React.createElement("div", null, "/stop \u2014 \u505C\u6B62\u4EFB\u52A1"),
450
- React.createElement("div", null, "/status \u2014 \u67E5\u770B\u72B6\u6001"),
451
- React.createElement("div", null, "/yes \u6216 /no \u2014 \u56DE\u5E94\u5BA1\u6279"),
452
- React.createElement("div", null, "/help \u2014 \u5168\u90E8\u547D\u4EE4")
573
+ React.createElement("div", null, "/new <\u63D0\u793A\u8BCD> \u2014 \u65B0\u5EFA\u4F1A\u8BDD\u5E76\u5F00\u59CB\uFF08\u5F53\u524D\u5DE5\u4F5C\u533A\uFF09"),
574
+ React.createElement("div", null, "/new <\u63D0\u793A\u8BCD> @N \u2014 \u5728\u6307\u5B9A\u5DE5\u4F5C\u533A\u65B0\u5EFA\u4F1A\u8BDD"),
575
+ React.createElement("div", null, "/sessions\uFF08\u6216 /list\uFF09\u2014 \u5217\u51FA\u4F1A\u8BDD\uFF08\u6309\u5DE5\u4F5C\u533A\u5206\u7EC4\uFF0C\u5E26\u6807\u9898\uFF09"),
576
+ React.createElement("div", null, "/use N\uFF08\u6216 /resume N\uFF09\u2014 \u5207\u6362\u5230\u4F1A\u8BDD N"),
577
+ React.createElement("div", null, "/workspaces \u2014 \u5217\u51FA\u6240\u6709\u53EF\u7528\u5DE5\u4F5C\u533A"),
578
+ React.createElement("div", null, "/end \u2014 \u7ED3\u675F\u5F53\u524D\u4F1A\u8BDD\uFF08\u56DE\u5230\u65E0\u6D3B\u52A8\u4F1A\u8BDD\u72B6\u6001\uFF09"),
579
+ React.createElement("div", null, "/stop \u2014 \u505C\u6B62\u5F53\u524D\u4EFB\u52A1"),
580
+ React.createElement("div", null, "/status \u2014 \u67E5\u770B Agent \u72B6\u6001\u4E0E\u4F1A\u8BDD\u6458\u8981"),
581
+ React.createElement("div", null, "/yes \u6216 /no\uFF08\u6216 1/2\uFF09\u2014 \u56DE\u5E94\u6743\u9650\u5BA1\u6279\u8BF7\u6C42"),
582
+ React.createElement("div", null, "/help \u2014 \u663E\u793A\u5B8C\u6574\u547D\u4EE4\u5E2E\u52A9")
453
583
  ),
454
584
  err && React.createElement("div", { style: { ...s.warn, marginTop: 10 } }, err),
455
- // 已配置:状态详情 + 白名单
585
+ // 已配置:结构化状态看板 + 白名单
456
586
  platform?.configured && React.createElement(
457
587
  "div",
458
588
  { style: s.block },
589
+ // 结构化状态卡片看板
459
590
  React.createElement(
460
591
  "div",
461
- { style: { fontSize: 12, lineHeight: 1.7 } },
462
- React.createElement("div", null, `\u72B6\u6001: ${statusLabel}`),
463
- platform.accountId && React.createElement("div", null, `\u8D26\u53F7: ${platform.accountId}`),
464
- platform.sessionId && React.createElement("div", null, `\u5F53\u524D\u4F1A\u8BDD: ${platform.sessionId}`)
592
+ {
593
+ style: {
594
+ display: "grid",
595
+ gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))",
596
+ gap: 8,
597
+ marginBottom: 12
598
+ }
599
+ },
600
+ React.createElement(
601
+ "div",
602
+ {
603
+ style: {
604
+ background: "var(--dsw-alias-bg-layer-1,#fff)",
605
+ border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)",
606
+ borderRadius: 8,
607
+ padding: "8px 12px"
608
+ }
609
+ },
610
+ React.createElement("div", { style: { ...s.muted, fontSize: 11 } }, "\u8FDE\u63A5\u72B6\u6001"),
611
+ React.createElement(
612
+ "div",
613
+ { style: { ...s.label, fontSize: 13, marginTop: 2, display: "flex", alignItems: "center", gap: 6 } },
614
+ React.createElement("span", {
615
+ style: {
616
+ width: 6,
617
+ height: 6,
618
+ borderRadius: "50%",
619
+ background: connected ? "var(--dsw-alias-state-success-primary,#10b981)" : "var(--dsw-alias-label-tertiary,#9ca3af)"
620
+ }
621
+ }),
622
+ statusLabel
623
+ )
624
+ ),
625
+ platform.accountId && React.createElement(
626
+ "div",
627
+ {
628
+ style: {
629
+ background: "var(--dsw-alias-bg-layer-1,#fff)",
630
+ border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)",
631
+ borderRadius: 8,
632
+ padding: "8px 12px"
633
+ }
634
+ },
635
+ React.createElement("div", { style: { ...s.muted, fontSize: 11 } }, "\u767B\u5F55\u8D26\u53F7"),
636
+ React.createElement("div", { style: { ...s.code, fontSize: 12, marginTop: 2, fontWeight: 500 } }, platform.accountId)
637
+ ),
638
+ platform.sessionId && React.createElement(
639
+ "div",
640
+ {
641
+ style: {
642
+ background: "var(--dsw-alias-bg-layer-1,#fff)",
643
+ border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)",
644
+ borderRadius: 8,
645
+ padding: "8px 12px"
646
+ }
647
+ },
648
+ React.createElement("div", { style: { ...s.muted, fontSize: 11 } }, "\u6D3B\u52A8\u4F1A\u8BDD"),
649
+ React.createElement("div", { style: { ...s.code, fontSize: 12, marginTop: 2 } }, platform.sessionId)
650
+ )
465
651
  ),
466
652
  React.createElement(
467
653
  "div",
468
654
  { style: { ...s.muted, fontSize: 12, marginTop: 8, lineHeight: 1.6 } },
469
- "\u767D\u540D\u5355\uFF08\u4EC5\u8FD9\u4E9B\u7528\u6237\u53EF\u9A71\u52A8 agent\uFF09:"
655
+ `\u767D\u540D\u5355 (\u5DF2\u6388\u6743 ${platform.allowFrom?.length || 0} \u4E2A\u8D26\u53F7/\u7FA4):`
470
656
  ),
471
657
  React.createElement(
472
658
  "div",
@@ -493,9 +679,12 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
493
679
  { style: { display: "flex", gap: 8, marginTop: 8, alignItems: "center" } },
494
680
  React.createElement("input", {
495
681
  style: { ...s.input, flex: 1 },
496
- placeholder: platformId === "wechat" ? "\u6DFB\u52A0\u5141\u8BB8\u7684\u5FAE\u4FE1 ID\uFF08\u5982 xxx@im.wechat\uFF09" : "\u6DFB\u52A0\u5141\u8BB8\u7684\u7528\u6237 ID",
682
+ placeholder: platformId === "wechat" ? "\u6DFB\u52A0\u5141\u8BB8\u7684\u5FAE\u4FE1 ID\uFF08\u5982 xxx@im.wechat\uFF09\uFF0C\u6309 Enter \u6DFB\u52A0" : "\u6DFB\u52A0\u5141\u8BB8\u7684\u7528\u6237/\u7FA4 ID\uFF0C\u6309 Enter \u6DFB\u52A0",
497
683
  value: newId,
498
- onChange: handleNewId
684
+ onChange: handleNewId,
685
+ onKeyDown: (e) => {
686
+ if (e.key === "Enter" && newId.trim() && !busy) addAllow();
687
+ }
499
688
  }),
500
689
  React.createElement("button", {
501
690
  style: { ...s.btnGhost, whiteSpace: "nowrap", opacity: newId.trim() && !busy ? 1 : 0.5 },
@@ -543,19 +732,36 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
543
732
  style: { ...s.input, width: "100%" },
544
733
  placeholder: "\u8BF7\u8F93\u5165 AppID",
545
734
  value: cfgDraft?.appId ?? "",
546
- onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value }))
735
+ onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value })),
736
+ onKeyDown: (e) => {
737
+ if (e.key === "Enter" && cfgDraft?.appId?.trim() && cfgDraft?.clientSecret?.trim() && !busy) saveConfig();
738
+ }
547
739
  })
548
740
  ),
549
741
  React.createElement(
550
742
  "div",
551
743
  null,
552
744
  React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "ClientSecret \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5BC6\u94A5"),
553
- React.createElement("input", {
554
- style: { ...s.input, width: "100%" },
555
- placeholder: "\u8BF7\u8F93\u5165 ClientSecret",
556
- value: cfgDraft?.clientSecret ?? "",
557
- onChange: (e) => setCfgDraft((d) => ({ ...d, clientSecret: e.target.value }))
558
- })
745
+ React.createElement(
746
+ "div",
747
+ { style: { display: "flex", gap: 6, alignItems: "center" } },
748
+ React.createElement("input", {
749
+ style: { ...s.input, flex: 1 },
750
+ type: showSecret ? "text" : "password",
751
+ placeholder: "\u8BF7\u8F93\u5165 ClientSecret",
752
+ value: cfgDraft?.clientSecret ?? "",
753
+ onChange: (e) => setCfgDraft((d) => ({ ...d, clientSecret: e.target.value })),
754
+ onKeyDown: (e) => {
755
+ if (e.key === "Enter" && cfgDraft?.appId?.trim() && cfgDraft?.clientSecret?.trim() && !busy) saveConfig();
756
+ }
757
+ }),
758
+ React.createElement("button", {
759
+ style: { ...s.btnGhost, height: 32, padding: "0 10px", fontSize: 13, flexShrink: 0 },
760
+ onClick: () => setShowSecret((v) => !v),
761
+ type: "button",
762
+ title: showSecret ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u660E\u6587"
763
+ }, showSecret ? "\u{1F648} \u9690\u85CF" : "\u{1F441}\uFE0F \u663E\u793A")
764
+ )
559
765
  ),
560
766
  React.createElement(
561
767
  "div",
@@ -588,7 +794,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
588
794
  login.phase === "error" && React.createElement("div", { style: { ...s.muted, fontSize: 12 } }, login.error ?? "\u767B\u5F55\u5931\u8D25")
589
795
  )
590
796
  ),
591
- // 高级设置(可折叠)
797
+ // 高级设置(紧凑 2x2 网格 + 恢复默认值)
592
798
  cfgDraft && React.createElement(
593
799
  "div",
594
800
  { style: s.block },
@@ -599,72 +805,83 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
599
805
  showAdvanced && React.createElement(
600
806
  "div",
601
807
  { style: { display: "flex", flexDirection: "column", gap: 10 } },
602
- // 心跳间隔
603
- React.createElement(
604
- "div",
605
- null,
606
- React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5FC3\u8DF3\u95F4\u9694\uFF08\u79D2\uFF09\u2014 \u957F\u4EFB\u52A1\u5904\u7406\u4E2D\u6BCF\u9694\u591A\u4E45\u53D1\u4E00\u6B21\u8FDB\u5EA6\u63D0\u793A"),
607
- React.createElement("input", {
608
- style: { ...s.input, width: 120 },
609
- type: "number",
610
- min: 30,
611
- max: 3600,
612
- value: cfgDraft.digestIntervalSec,
613
- onChange: (e) => setCfgDraft((d) => ({ ...d, digestIntervalSec: e.target.value }))
614
- })
615
- ),
616
- // 审批超时
617
- React.createElement(
618
- "div",
619
- null,
620
- React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5BA1\u6279\u8D85\u65F6\uFF08\u79D2\uFF09\u2014 \u5DE5\u5177\u8C03\u7528\u5BA1\u6279\u65E0\u54CD\u5E94\u540E\u81EA\u52A8\u62D2\u7EDD"),
621
- React.createElement("input", {
622
- style: { ...s.input, width: 120 },
623
- type: "number",
624
- min: 30,
625
- max: 86400,
626
- value: cfgDraft.approvalTimeoutSec,
627
- onChange: (e) => setCfgDraft((d) => ({ ...d, approvalTimeoutSec: e.target.value }))
628
- })
629
- ),
630
- // 每气泡字数
631
- React.createElement(
632
- "div",
633
- null,
634
- React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u6BCF\u6761\u6D88\u606F\u6700\u5927\u5B57\u6570 \u2014 \u8D85\u51FA\u65F6\u81EA\u52A8\u5206\u591A\u6761\u53D1\u9001"),
635
- React.createElement("input", {
636
- style: { ...s.input, width: 120 },
637
- type: "number",
638
- min: 100,
639
- max: 1e4,
640
- value: cfgDraft.maxMessageChars,
641
- onChange: (e) => setCfgDraft((d) => ({ ...d, maxMessageChars: e.target.value }))
642
- })
643
- ),
644
- // 分块延迟
808
+ // 2x2 参数网格
645
809
  React.createElement(
646
810
  "div",
647
- null,
648
- React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5206\u5757\u53D1\u9001\u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09\u2014 \u591A\u6761\u6D88\u606F\u4E4B\u95F4\u7684\u95F4\u9694"),
649
- React.createElement("input", {
650
- style: { ...s.input, width: 120 },
651
- type: "number",
652
- min: 0,
653
- max: 1e4,
654
- value: cfgDraft.sendChunkDelayMs,
655
- onChange: (e) => setCfgDraft((d) => ({ ...d, sendChunkDelayMs: e.target.value }))
656
- })
811
+ {
812
+ style: {
813
+ display: "grid",
814
+ gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
815
+ gap: 10
816
+ }
817
+ },
818
+ // 心跳间隔
819
+ React.createElement(
820
+ "div",
821
+ null,
822
+ React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5FC3\u8DF3\u8FDB\u5EA6\u95F4\u9694 (\u79D2)"),
823
+ React.createElement("input", {
824
+ style: { ...s.input, width: "100%" },
825
+ type: "number",
826
+ min: 30,
827
+ max: 3600,
828
+ value: cfgDraft.digestIntervalSec,
829
+ onChange: (e) => setCfgDraft((d) => ({ ...d, digestIntervalSec: e.target.value }))
830
+ })
831
+ ),
832
+ // 审批超时
833
+ React.createElement(
834
+ "div",
835
+ null,
836
+ React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5BA1\u6279\u8D85\u65F6\u81EA\u52A8\u62D2\u7EDD (\u79D2)"),
837
+ React.createElement("input", {
838
+ style: { ...s.input, width: "100%" },
839
+ type: "number",
840
+ min: 30,
841
+ max: 86400,
842
+ value: cfgDraft.approvalTimeoutSec,
843
+ onChange: (e) => setCfgDraft((d) => ({ ...d, approvalTimeoutSec: e.target.value }))
844
+ })
845
+ ),
846
+ // 每气泡字数
847
+ React.createElement(
848
+ "div",
849
+ null,
850
+ React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5355\u6761\u6700\u5927\u5B57\u6570\u9650\u5236 (\u5B57)"),
851
+ React.createElement("input", {
852
+ style: { ...s.input, width: "100%" },
853
+ type: "number",
854
+ min: 100,
855
+ max: 1e4,
856
+ value: cfgDraft.maxMessageChars,
857
+ onChange: (e) => setCfgDraft((d) => ({ ...d, maxMessageChars: e.target.value }))
858
+ })
859
+ ),
860
+ // 分块延迟
861
+ React.createElement(
862
+ "div",
863
+ null,
864
+ React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5206\u5757\u53D1\u9001\u5EF6\u8FDF (\u6BEB\u79D2)"),
865
+ React.createElement("input", {
866
+ style: { ...s.input, width: "100%" },
867
+ type: "number",
868
+ min: 0,
869
+ max: 1e4,
870
+ value: cfgDraft.sendChunkDelayMs,
871
+ onChange: (e) => setCfgDraft((d) => ({ ...d, sendChunkDelayMs: e.target.value }))
872
+ })
873
+ )
657
874
  ),
658
875
  // QQ 平台凭证
659
876
  platformId === "qq" && React.createElement(
660
877
  "div",
661
- { style: { display: "flex", flexDirection: "column", gap: 10 } },
878
+ { style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4 } },
662
879
  React.createElement(
663
880
  "div",
664
881
  null,
665
882
  React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "AppID \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5E94\u7528 ID"),
666
883
  React.createElement("input", {
667
- style: { ...s.input, width: 220 },
884
+ style: { ...s.input, width: "100%" },
668
885
  placeholder: "\u8BF7\u8F93\u5165 AppID",
669
886
  value: cfgDraft.appId,
670
887
  onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value }))
@@ -673,20 +890,40 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
673
890
  React.createElement(
674
891
  "div",
675
892
  null,
676
- React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "ClientSecret \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5BC6\u94A5"),
677
- React.createElement("input", {
678
- style: { ...s.input, width: 220 },
679
- placeholder: "\u8BF7\u8F93\u5165 ClientSecret",
680
- value: cfgDraft.clientSecret,
681
- onChange: (e) => setCfgDraft((d) => ({ ...d, clientSecret: e.target.value }))
682
- })
893
+ React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "ClientSecret \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5BC6\u94A5\uFF08\u7559\u7A7A\u5219\u4FDD\u6301\u5DF2\u4FDD\u5B58\u5BC6\u94A5\uFF09"),
894
+ React.createElement(
895
+ "div",
896
+ { style: { display: "flex", gap: 6, alignItems: "center" } },
897
+ React.createElement("input", {
898
+ style: { ...s.input, flex: 1 },
899
+ type: showSecret ? "text" : "password",
900
+ placeholder: "\u7559\u7A7A\u4FDD\u6301\u5DF2\u4FDD\u5B58\u5BC6\u94A5",
901
+ value: cfgDraft.clientSecret,
902
+ onChange: (e) => setCfgDraft((d) => ({ ...d, clientSecret: e.target.value }))
903
+ }),
904
+ React.createElement("button", {
905
+ style: { ...s.btnGhost, height: 32, padding: "0 10px", fontSize: 13, flexShrink: 0 },
906
+ onClick: () => setShowSecret((v) => !v),
907
+ type: "button",
908
+ title: showSecret ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u660E\u6587"
909
+ }, showSecret ? "\u{1F648} \u9690\u85CF" : "\u{1F441}\uFE0F \u663E\u793A")
910
+ )
683
911
  )
684
912
  ),
685
- React.createElement("button", {
686
- style: { ...s.btnPri, alignSelf: "flex-start", opacity: cfgDirty && !busy ? 1 : 0.5 },
687
- disabled: !cfgDirty || busy,
688
- onClick: saveConfig
689
- }, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u8BBE\u7F6E")
913
+ React.createElement(
914
+ "div",
915
+ { style: { display: "flex", gap: 8, alignItems: "center", marginTop: 4 } },
916
+ React.createElement("button", {
917
+ style: { ...s.btnPri, opacity: cfgDirty && !busy ? 1 : 0.5 },
918
+ disabled: !cfgDirty || busy,
919
+ onClick: saveConfig
920
+ }, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u8BBE\u7F6E"),
921
+ React.createElement("button", {
922
+ style: { ...s.btnGhost, height: 32, fontSize: 12 },
923
+ onClick: resetDefaults,
924
+ title: "\u6062\u590D\u63A8\u8350\u9ED8\u8BA4\u914D\u7F6E"
925
+ }, "\u21BA \u6062\u590D\u63A8\u8350\u9ED8\u8BA4")
926
+ )
690
927
  )
691
928
  ),
692
929
  React.createElement(
@@ -712,7 +949,11 @@ function UpgradeCommandRow({ cmd }) {
712
949
  color: "var(--dsw-alias-label-secondary,#6b7280)",
713
950
  flex: 1,
714
951
  minWidth: 0,
715
- wordBreak: "break-all"
952
+ wordBreak: "break-all",
953
+ background: "var(--dsw-alias-bg-layer-1,#ffffff)",
954
+ padding: "4px 8px",
955
+ borderRadius: 6,
956
+ border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)"
716
957
  }
717
958
  }, cmd),
718
959
  React.createElement("button", {
@@ -725,6 +966,9 @@ function UpgradeCommandRow({ cmd }) {
725
966
  function VersionBanner({ rpcCall }) {
726
967
  const [info, setInfo] = React.useState(null);
727
968
  const [loading, setLoading] = React.useState(false);
969
+ const [upgrading, setUpgrading] = React.useState(false);
970
+ const [upgradeResult, setUpgradeResult] = React.useState(null);
971
+ const [showManual, setShowManual] = React.useState(false);
728
972
  const check = React.useCallback(async () => {
729
973
  setLoading(true);
730
974
  try {
@@ -738,6 +982,27 @@ function VersionBanner({ rpcCall }) {
738
982
  check();
739
983
  }, [check]);
740
984
  const hasUpdate = info?.latest && info?.current && !info.error && semverGt(info.latest, info.current);
985
+ const isLatest = info?.latest && info?.current && !info.error && !semverGt(info.latest, info.current);
986
+ const handleUpgrade = React.useCallback(async () => {
987
+ if (!info?.latest || upgrading) return;
988
+ setUpgrading(true);
989
+ setUpgradeResult(null);
990
+ try {
991
+ const r = await rpcCall(BRIDGE_ENDPOINTS.upgradePlugin, { version: info.latest });
992
+ if (r?.ok && r.value?.ok) {
993
+ setUpgradeResult({ ok: true, message: `\u5DF2\u6210\u529F\u5347\u7EA7\u5230 v${info.latest}\uFF01\u8BF7\u91CD\u542F DSH \u670D\u52A1\u4F7F\u65B0\u7248\u672C\u751F\u6548\u3002` });
994
+ setTimeout(() => check(), 3e3);
995
+ } else {
996
+ setUpgradeResult({ ok: false, message: r?.value?.error || r?.error?.message || "\u5347\u7EA7\u5931\u8D25" });
997
+ setShowManual(true);
998
+ }
999
+ } catch (e) {
1000
+ setUpgradeResult({ ok: false, message: e.message || "\u5347\u7EA7\u8BF7\u6C42\u5931\u8D25" });
1001
+ setShowManual(true);
1002
+ } finally {
1003
+ setUpgrading(false);
1004
+ }
1005
+ }, [info?.latest, upgrading, rpcCall, check]);
741
1006
  const links = React.createElement(
742
1007
  "div",
743
1008
  { style: { display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap" } },
@@ -746,95 +1011,174 @@ function VersionBanner({ rpcCall }) {
746
1011
  target: "_blank",
747
1012
  rel: "noreferrer",
748
1013
  style: s.btnLink
749
- }, "\u2B50 GitHub"),
1014
+ }, React.createElement(Icons.github), "GitHub"),
750
1015
  React.createElement("a", {
751
1016
  href: RELEASES_URL,
752
1017
  target: "_blank",
753
1018
  rel: "noreferrer",
754
1019
  style: s.btnLink
755
- }, "\u{1F4CB} \u66F4\u65B0\u65E5\u5FD7"),
1020
+ }, "\u66F4\u65B0\u65E5\u5FD7"),
756
1021
  React.createElement("a", {
757
1022
  href: ISSUES_URL,
758
1023
  target: "_blank",
759
1024
  rel: "noreferrer",
760
1025
  style: s.btnLink
761
- }, "\u{1F41B} \u53CD\u9988 Bug")
1026
+ }, "\u53CD\u9988 Issue")
762
1027
  );
763
- if (hasUpdate) {
764
- const cmds = upgradeCommands(info.latest);
765
- return React.createElement(
1028
+ return React.createElement(
1029
+ "div",
1030
+ { style: { marginBottom: 16 } },
1031
+ // 顶部状态行:版本徽标 + 刷新按钮 + 链接组
1032
+ React.createElement(
766
1033
  "div",
767
- { style: { marginBottom: 16 } },
1034
+ {
1035
+ style: {
1036
+ display: "flex",
1037
+ alignItems: "center",
1038
+ justifyContent: "space-between",
1039
+ flexWrap: "wrap",
1040
+ gap: 8
1041
+ }
1042
+ },
768
1043
  React.createElement(
769
1044
  "div",
770
- {
771
- style: {
772
- ...s.card,
773
- background: "var(--dsw-alias-state-info-bg,#eff6ff)",
774
- border: "1px solid var(--dsw-alias-state-info-border,#bfdbfe)",
775
- padding: "12px 16px",
776
- marginBottom: 8
777
- }
778
- },
1045
+ { style: { display: "flex", alignItems: "center", gap: 8 } },
1046
+ // 版本状态徽标
1047
+ React.createElement(
1048
+ "span",
1049
+ {
1050
+ style: {
1051
+ ...s.tag,
1052
+ background: hasUpdate ? "var(--dsw-alias-state-info-bg,#eff6ff)" : isLatest ? "var(--dsw-alias-state-success-bg,#ecfdf5)" : "var(--dsw-alias-bg-layer-2,#f3f4f6)",
1053
+ color: hasUpdate ? "var(--dsw-alias-state-info-primary,#2563eb)" : isLatest ? "var(--dsw-alias-state-success-primary,#059669)" : "var(--dsw-alias-label-secondary,#6b7280)",
1054
+ padding: "3px 10px",
1055
+ fontSize: 12,
1056
+ fontWeight: 500,
1057
+ display: "inline-flex",
1058
+ alignItems: "center",
1059
+ gap: 5
1060
+ }
1061
+ },
1062
+ loading ? React.createElement("span", {
1063
+ style: { display: "inline-flex", alignItems: "center" }
1064
+ }, React.createElement(Icons.refresh)) : isLatest ? React.createElement(Icons.check) : null,
1065
+ info ? `v${info.current}` : "\u7248\u672C\u68C0\u67E5\u4E2D\u2026",
1066
+ isLatest && React.createElement("span", { style: { opacity: 0.85, fontSize: 11, fontWeight: 400 } }, "\xB7 \u5DF2\u662F\u6700\u65B0"),
1067
+ hasUpdate && React.createElement("span", { style: { fontWeight: 600, fontSize: 11 } }, `\u2794 v${info.latest}`),
1068
+ info?.error && React.createElement("span", { style: { color: "var(--dsw-alias-state-warn-primary,#d97706)", fontSize: 11 } }, "(\u7F51\u7EDC\u8D85\u65F6)")
1069
+ ),
1070
+ // 刷新检查按钮
1071
+ React.createElement(
1072
+ "button",
1073
+ {
1074
+ style: {
1075
+ ...s.btnGhost,
1076
+ height: 24,
1077
+ padding: "0 8px",
1078
+ fontSize: 11,
1079
+ opacity: loading ? 0.5 : 1,
1080
+ gap: 4
1081
+ },
1082
+ onClick: check,
1083
+ disabled: loading || upgrading,
1084
+ title: "\u91CD\u65B0\u68C0\u67E5 npm \u7EBF\u4E0A\u7248\u672C"
1085
+ },
1086
+ React.createElement(Icons.refresh),
1087
+ loading ? "\u68C0\u67E5\u4E2D\u2026" : "\u68C0\u67E5\u66F4\u65B0"
1088
+ )
1089
+ ),
1090
+ links
1091
+ ),
1092
+ // 发现新版本高亮卡片(支持一键直接升级)
1093
+ hasUpdate && React.createElement(
1094
+ "div",
1095
+ {
1096
+ style: {
1097
+ ...s.card,
1098
+ background: "var(--dsw-alias-state-info-bg,#eff6ff)",
1099
+ border: "1px solid var(--dsw-alias-state-info-border,#bfdbfe)",
1100
+ padding: "14px 16px",
1101
+ marginTop: 10,
1102
+ marginBottom: 0
1103
+ }
1104
+ },
1105
+ React.createElement(
1106
+ "div",
1107
+ { style: { display: "flex", alignItems: "flex-start", gap: 12 } },
1108
+ React.createElement("span", { style: { fontSize: 22 } }, "\u{1F680}"),
779
1109
  React.createElement(
780
1110
  "div",
781
- { style: { display: "flex", alignItems: "center", gap: 12 } },
782
- React.createElement("span", { style: { fontSize: 20 } }, "\u{1F389}"),
1111
+ { style: { flex: 1, minWidth: 0 } },
783
1112
  React.createElement(
784
1113
  "div",
785
- { style: { flex: 1, minWidth: 0 } },
1114
+ { style: { display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: 8, marginBottom: 8 } },
786
1115
  React.createElement("div", {
787
1116
  style: {
788
1117
  fontSize: 13,
789
1118
  fontWeight: 600,
790
- color: "var(--dsw-alias-state-info-primary,#1e40af)",
791
- marginBottom: 8
1119
+ color: "var(--dsw-alias-state-info-primary,#1e40af)"
792
1120
  }
793
1121
  }, `\u53D1\u73B0\u65B0\u7248\u672C v${info.latest}\uFF08\u5F53\u524D v${info.current}\uFF09`),
794
- // 升级命令:dsh plugin 与 npx 两种,均带复制按钮
795
1122
  React.createElement(
796
- "div",
797
- { style: { display: "flex", flexDirection: "column", gap: 6 } },
798
- cmds.map(({ id, cmd }) => React.createElement(UpgradeCommandRow, { key: id, cmd }))
1123
+ "button",
1124
+ {
1125
+ style: {
1126
+ ...s.btnPri,
1127
+ height: 28,
1128
+ fontSize: 12,
1129
+ padding: "0 14px",
1130
+ background: upgradeResult?.ok ? "var(--dsw-alias-state-success-primary,#059669)" : "var(--dsw-alias-brand-primary,#4f6ef7)",
1131
+ opacity: upgrading ? 0.6 : 1
1132
+ },
1133
+ onClick: handleUpgrade,
1134
+ disabled: upgrading || upgradeResult?.ok
1135
+ },
1136
+ upgrading ? React.createElement(
1137
+ "span",
1138
+ { style: { display: "inline-flex", alignItems: "center", gap: 6 } },
1139
+ React.createElement("span", { style: { animation: "spin 1s linear infinite", display: "inline-flex" } }, React.createElement(Icons.refresh)),
1140
+ "\u6B63\u5728\u81EA\u52A8\u5347\u7EA7\u2026"
1141
+ ) : upgradeResult?.ok ? "\u2713 \u5DF2\u5B8C\u6210\u5347\u7EA7" : `\u4E00\u952E\u5347\u7EA7\u5230 v${info.latest}`
799
1142
  )
800
1143
  ),
801
- React.createElement("button", {
1144
+ upgradeResult && React.createElement("div", {
802
1145
  style: {
803
- ...s.btnGhost,
804
- height: 30,
805
- padding: "0 12px",
1146
+ background: upgradeResult.ok ? "var(--dsw-alias-state-success-bg,#ecfdf5)" : "var(--dsw-alias-state-error-bg,#fef2f2)",
1147
+ border: `1px solid ${upgradeResult.ok ? "var(--dsw-alias-state-success-border,#a7f3d0)" : "var(--dsw-alias-state-error-border,#fecaca)"}`,
1148
+ color: upgradeResult.ok ? "var(--dsw-alias-state-success-primary,#065f46)" : "var(--dsw-alias-state-error-primary,#991b1b)",
1149
+ padding: "8px 12px",
1150
+ borderRadius: 6,
806
1151
  fontSize: 12,
807
- opacity: loading ? 0.5 : 1,
808
- flexShrink: 0
1152
+ marginBottom: 8,
1153
+ lineHeight: 1.5
1154
+ }
1155
+ }, upgradeResult.message),
1156
+ React.createElement(
1157
+ "div",
1158
+ { style: { marginTop: 4 } },
1159
+ React.createElement("button", {
1160
+ style: { ...s.btnLink, fontSize: 11, color: "var(--dsw-alias-label-secondary,#6b7280)" },
1161
+ onClick: () => setShowManual((v) => !v)
1162
+ }, showManual ? "\u25B4 \u6298\u53E0\u624B\u52A8\u547D\u4EE4\u884C" : "\u25BE \u67E5\u770B\u624B\u52A8\u5347\u7EA7\u547D\u4EE4 (\u5982\u9700)")
1163
+ ),
1164
+ showManual && React.createElement(
1165
+ "div",
1166
+ {
1167
+ style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 8 }
809
1168
  },
810
- onClick: check,
811
- disabled: loading
812
- }, loading ? "\u2026" : "\u5237\u65B0")
1169
+ upgradeCommands(info.latest).map(
1170
+ ({ id, cmd }) => React.createElement(UpgradeCommandRow, { key: id, cmd })
1171
+ )
1172
+ )
813
1173
  )
814
- ),
815
- links
816
- );
817
- }
818
- return React.createElement(
819
- "div",
820
- { style: { marginBottom: 14, display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: 8 } },
821
- React.createElement(
822
- "div",
823
- { style: { ...s.muted, display: "flex", alignItems: "center", gap: 8 } },
824
- loading && !info ? React.createElement("span", null, "\u68C0\u67E5\u66F4\u65B0\u4E2D\u2026") : info ? React.createElement("span", null, `v${info.current}${info.latest && !info.error ? " \xB7 \u5DF2\u662F\u6700\u65B0" : info.error ? " \xB7 \u68C0\u67E5\u5931\u8D25" : ""}`) : null,
825
- info && React.createElement("button", {
826
- style: { ...s.btnGhost, height: 22, padding: "0 8px", fontSize: 11, opacity: loading ? 0.5 : 1 },
827
- onClick: check,
828
- disabled: loading
829
- }, loading ? "\u2026" : "\u91CD\u65B0\u68C0\u67E5")
830
- ),
831
- links
1174
+ )
1175
+ )
832
1176
  );
833
1177
  }
834
1178
  var TABS = [
835
- { id: "lan", label: "\u5C40\u57DF\u7F51" },
836
- { id: "tunnel", label: "\u516C\u7F51\u96A7\u9053" },
837
- { id: "im", label: "IM \u673A\u5668\u4EBA" }
1179
+ { id: "lan", label: "\u5C40\u57DF\u7F51", icon: Icons.lan },
1180
+ { id: "tunnel", label: "\u516C\u7F51\u96A7\u9053", icon: Icons.tunnel },
1181
+ { id: "im", label: "IM \u673A\u5668\u4EBA", icon: Icons.bot }
838
1182
  ];
839
1183
  function TabBar({ active, onChange, dots }) {
840
1184
  return React.createElement(
@@ -842,12 +1186,12 @@ function TabBar({ active, onChange, dots }) {
842
1186
  {
843
1187
  style: {
844
1188
  display: "flex",
845
- gap: 0,
1189
+ gap: 4,
846
1190
  marginBottom: 20,
847
1191
  borderBottom: "1px solid var(--dsw-alias-border-l2,#e5e7eb)"
848
1192
  }
849
1193
  },
850
- TABS.map(({ id, label }) => {
1194
+ TABS.map(({ id, label, icon: TabIcon }) => {
851
1195
  const isActive = active === id;
852
1196
  const hasDot = dots?.[id];
853
1197
  return React.createElement(
@@ -868,11 +1212,19 @@ function TabBar({ active, onChange, dots }) {
868
1212
  marginBottom: -1,
869
1213
  display: "inline-flex",
870
1214
  alignItems: "center",
871
- gap: 6,
1215
+ gap: 7,
872
1216
  transition: "color .15s, border-color .15s",
873
1217
  whiteSpace: "nowrap"
874
1218
  }
875
1219
  },
1220
+ TabIcon && React.createElement(TabIcon, {
1221
+ style: {
1222
+ color: isActive ? "var(--dsw-alias-brand-primary,#4f6ef7)" : "var(--dsw-alias-label-tertiary,#9ca3af)",
1223
+ width: 16,
1224
+ height: 16,
1225
+ flexShrink: 0
1226
+ }
1227
+ }),
876
1228
  label,
877
1229
  hasDot && React.createElement("span", {
878
1230
  style: {
@@ -1011,9 +1363,10 @@ function BridgePanel({ rpcCall }) {
1011
1363
  );
1012
1364
  } else if (activeTab === "im") {
1013
1365
  const IM_PLATFORMS = [
1014
- { id: "wechat", label: "\u5FAE\u4FE1", desc: "iLink Bot API\uFF08ClawBot\uFF09" },
1015
- { id: "qq", label: "QQ", desc: "QQ Bot OpenAPI v2\uFF08\u79C1\u804A/\u7FA4\u804A/\u6309\u94AE\uFF09" },
1016
- { id: "feishu", label: "\u98DE\u4E66", desc: "\u5B98\u65B9\u4E8B\u4EF6\u56DE\u8C03 API" }
1366
+ { id: "wechat", label: "\u5FAE\u4FE1", icon: Icons.wechat, brandColor: "#07C160", desc: "ClawBot \u626B\u7801\u76F4\u8FDE \xB7 \u65E0\u9700\u516C\u7F51" },
1367
+ { id: "qq", label: "QQ", icon: Icons.qq, brandColor: "#12B7F5", desc: "\u5B98\u65B9\u673A\u5668\u4EBA \xB7 \u79C1\u804A/\u7FA4\u804A/\u6309\u94AE" },
1368
+ { id: "feishu", label: "\u98DE\u4E66", icon: Icons.feishu, brandColor: "#00D6B9", desc: "\u5B98\u65B9\u4E8B\u4EF6\u56DE\u8C03 API" },
1369
+ { id: "telegram", label: "Telegram", icon: Icons.telegram, brandColor: "#24A1DE", desc: "\u5B98\u65B9 Bot API" }
1017
1370
  ];
1018
1371
  tabContent = React.createElement(
1019
1372
  "div",
@@ -1024,7 +1377,7 @@ function BridgePanel({ rpcCall }) {
1024
1377
  {
1025
1378
  style: { display: "flex", gap: 10, marginBottom: 16, flexWrap: "wrap" }
1026
1379
  },
1027
- IM_PLATFORMS.map(({ id, label, desc }) => {
1380
+ IM_PLATFORMS.map(({ id, label, icon: IconComponent, brandColor, desc }) => {
1028
1381
  const platformData = platforms?.[id];
1029
1382
  const available = !!platformData;
1030
1383
  const active = platformData?.status === "connected" || platformData?.status === "starting" || platformData?.status === "reconnecting";
@@ -1033,21 +1386,27 @@ function BridgePanel({ rpcCall }) {
1033
1386
  {
1034
1387
  key: id,
1035
1388
  style: {
1036
- flex: "1 1 140px",
1037
- border: `1px solid ${selectedPlatform === id ? "var(--dsw-alias-state-info-primary,#3b82f6)" : active ? "var(--dsw-alias-state-success-primary,#10b981)" : "var(--dsw-alias-border-l2,#e5e7eb)"}`,
1389
+ flex: "1 1 135px",
1390
+ border: `1px solid ${selectedPlatform === id ? "var(--dsw-alias-brand-primary,#4f6ef7)" : active ? "var(--dsw-alias-state-success-primary,#10b981)" : "var(--dsw-alias-border-l2,#e5e7eb)"}`,
1038
1391
  borderRadius: 10,
1039
1392
  padding: "12px 14px",
1040
- opacity: available ? 1 : 0.45,
1393
+ opacity: available ? 1 : 0.5,
1041
1394
  cursor: available ? "pointer" : "not-allowed",
1042
- background: selectedPlatform === id ? "var(--dsw-alias-state-info-bg,#eff6ff)" : active ? "var(--dsw-alias-state-success-bg,#ecfdf5)" : available ? "var(--dsw-alias-bg-layer-1,transparent)" : "var(--dsw-alias-bg-layer-2,#f9fafb)",
1395
+ background: selectedPlatform === id ? "var(--dsw-alias-state-info-bg,#eff6ff)" : active ? "var(--dsw-alias-state-success-bg,#ecfdf5)" : "var(--dsw-alias-bg-layer-2,#f9fafb)",
1396
+ boxShadow: selectedPlatform === id ? "0 0 0 1px var(--dsw-alias-brand-primary,#4f6ef7)" : "none",
1043
1397
  transition: "all 0.15s ease"
1044
1398
  },
1045
1399
  onClick: available ? () => setSelectedPlatform(id) : void 0
1046
1400
  },
1047
1401
  React.createElement(
1048
1402
  "div",
1049
- { style: { ...s.label, fontSize: 13, display: "flex", alignItems: "center", gap: 6 } },
1050
- label,
1403
+ { style: { ...s.label, fontSize: 13, display: "flex", alignItems: "center", justifyContent: "space-between" } },
1404
+ React.createElement(
1405
+ "span",
1406
+ { style: { display: "flex", alignItems: "center", gap: 7 } },
1407
+ IconComponent && React.createElement(IconComponent, { style: { color: brandColor, width: 18, height: 18, flexShrink: 0 } }),
1408
+ label
1409
+ ),
1051
1410
  active && React.createElement("span", {
1052
1411
  style: { width: 6, height: 6, borderRadius: "50%", background: "var(--dsw-alias-state-success-primary,#10b981)", flexShrink: 0 }
1053
1412
  }),
@@ -1058,7 +1417,7 @@ function BridgePanel({ rpcCall }) {
1058
1417
  style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary,#9ca3af)", fontWeight: 400 }
1059
1418
  }, "\u5373\u5C06\u652F\u6301")
1060
1419
  ),
1061
- React.createElement("div", { style: { ...s.muted, marginTop: 3, fontSize: 11 } }, desc)
1420
+ React.createElement("div", { style: { ...s.muted, marginTop: 4, fontSize: 11 } }, desc)
1062
1421
  );
1063
1422
  })
1064
1423
  ),
@@ -1076,7 +1435,7 @@ function BridgePanel({ rpcCall }) {
1076
1435
  }
1077
1436
  return React.createElement(
1078
1437
  "div",
1079
- { style: { maxWidth: 560 } },
1438
+ { style: { maxWidth: 620 } },
1080
1439
  err && React.createElement("div", {
1081
1440
  style: { ...s.card, background: "var(--dsw-alias-state-error-bg,#fef2f2)", color: "var(--dsw-alias-state-error-primary,#dc2626)", fontSize: 13, marginBottom: 16 }
1082
1441
  }, err),