@ryubyte/dsh-a2a 0.1.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/lib/client.js ADDED
@@ -0,0 +1,574 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@ryubyte/dsh-a2a",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region src/client/index.ts
9
+ /** Services this plugin needs from the client runtime. */
10
+ const inject = ["slots"];
11
+ function apply(ctx) {
12
+ ctx.effect(() => {
13
+ try {
14
+ const off = ctx.slots.register({
15
+ name: "settings.section",
16
+ id: "a2a",
17
+ order: 90,
18
+ label: () => "A2A 连接",
19
+ inject: () => ({})
20
+ }, DashboardSection);
21
+ return () => off();
22
+ } catch (err) {
23
+ console.error("[dsh-a2a] failed to register settings section:", err);
24
+ return () => {};
25
+ }
26
+ }, "dsh-a2a: settings section");
27
+ }
28
+ async function fetchSnapshot() {
29
+ const res = await fetch("/a2a/api", { headers: { accept: "application/json" } });
30
+ if (!res.ok) throw new Error(`GET /a2a/api → HTTP ${res.status}`);
31
+ return await res.json();
32
+ }
33
+ async function postControl(action, target, payload) {
34
+ return await (await fetch("/a2a/api", {
35
+ method: "POST",
36
+ headers: { "content-type": "application/json" },
37
+ body: JSON.stringify({
38
+ action,
39
+ target,
40
+ ...payload
41
+ })
42
+ })).json();
43
+ }
44
+ /** The A2A connection dashboard settings section. */
45
+ function DashboardSection(_props) {
46
+ const [snap, setSnap] = (0, react.useState)(null);
47
+ const [error, setError] = (0, react.useState)(null);
48
+ const [busy, setBusy] = (0, react.useState)(null);
49
+ const [notice, setNotice] = (0, react.useState)(null);
50
+ const [serve, setServe] = (0, react.useState)(null);
51
+ const [addUrl, setAddUrl] = (0, react.useState)("");
52
+ const [discovering, setDiscovering] = (0, react.useState)(false);
53
+ const [discovered, setDiscovered] = (0, react.useState)(null);
54
+ const [tab, setTab] = (0, react.useState)("out");
55
+ /** connection ids whose skill list is expanded in the outbound tab. */
56
+ const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
57
+ const refresh = async () => {
58
+ try {
59
+ setError(null);
60
+ setSnap(await fetchSnapshot());
61
+ const sbody = await (await fetch("/a2a/api", {
62
+ method: "POST",
63
+ headers: { "content-type": "application/json" },
64
+ body: JSON.stringify({ action: "server-status" })
65
+ })).json();
66
+ if (sbody.ok && sbody.message) setServe(JSON.parse(sbody.message));
67
+ else setServe(null);
68
+ } catch (err) {
69
+ setError(err instanceof Error ? err.message : String(err));
70
+ }
71
+ };
72
+ (0, react.useEffect)(() => {
73
+ refresh();
74
+ const timer = setInterval(() => void refresh(), 3e3);
75
+ return () => clearInterval(timer);
76
+ }, []);
77
+ const act = async (action, target, payload) => {
78
+ setBusy(`${action}:${target}`);
79
+ setNotice(null);
80
+ try {
81
+ const result = await postControl(action, target, payload);
82
+ setNotice(`${result.ok ? "✓" : "✗"} ${result.message}`);
83
+ } catch (err) {
84
+ setNotice(`✗ ${err instanceof Error ? err.message : String(err)}`);
85
+ } finally {
86
+ setBusy(null);
87
+ await refresh();
88
+ }
89
+ };
90
+ const inbound = snap?.inbound ?? [];
91
+ const outbound = snap?.outbound ?? [];
92
+ const doDiscover = async () => {
93
+ const u = addUrl.trim();
94
+ if (!u) return;
95
+ setDiscovering(true);
96
+ setDiscovered(null);
97
+ setNotice(null);
98
+ try {
99
+ const body = await (await fetch("/a2a/api", {
100
+ method: "POST",
101
+ headers: { "content-type": "application/json" },
102
+ body: JSON.stringify({
103
+ action: "discover-agent",
104
+ agentCardUrl: u
105
+ })
106
+ })).json();
107
+ if (!body.ok || !body.message) {
108
+ setNotice(`✗ ${body.message}`);
109
+ return;
110
+ }
111
+ setDiscovered(JSON.parse(body.message));
112
+ } catch (err) {
113
+ setNotice(`✗ ${err instanceof Error ? err.message : String(err)}`);
114
+ } finally {
115
+ setDiscovering(false);
116
+ }
117
+ };
118
+ const doConnect = async () => {
119
+ if (!discovered) return;
120
+ const n = discovered.name.trim() || "remote";
121
+ await act("add-agent", n, { agentCardUrl: discovered.agentCardUrl });
122
+ if (!busy) {
123
+ setAddUrl("");
124
+ setDiscovered(null);
125
+ }
126
+ };
127
+ const inputStyle = {
128
+ padding: "4px 8px",
129
+ fontSize: 13,
130
+ borderRadius: 6,
131
+ border: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,.3))",
132
+ background: "var(--dsw-alias-fill-l1, rgba(127,127,127,.06))",
133
+ color: "var(--dsw-alias-label-primary, #222)"
134
+ };
135
+ return (0, react.createElement)("div", { style: {
136
+ display: "flex",
137
+ flexDirection: "column",
138
+ gap: 20
139
+ } }, (0, react.createElement)("div", { style: {
140
+ color: "var(--dsw-alias-label-secondary, #666)",
141
+ fontSize: 13,
142
+ lineHeight: 1.6
143
+ } }, "管理本 DSH 的 A2A Agent 连接:连接远程 Agent(出站),查看谁在调用本 DSH 的 A2A 服务(入站)。"), error ? (0, react.createElement)("div", { style: {
144
+ color: "var(--dsw-danger, #c62828)",
145
+ fontSize: 13,
146
+ background: "var(--dsw-alias-fill-l2, rgba(127,127,127,.08))",
147
+ borderRadius: 8,
148
+ padding: "8px 12px"
149
+ } }, `加载失败:${error}`) : null, notice ? (0, react.createElement)("div", { style: {
150
+ color: "var(--dsw-alias-label-primary, #222)",
151
+ fontSize: 13,
152
+ background: "var(--dsw-alias-fill-l2, rgba(127,127,127,.08))",
153
+ borderRadius: 8,
154
+ padding: "8px 12px"
155
+ } }, notice) : null, tabRail([
156
+ tabBtn("出站 Agent", outbound.length, tab === "out", () => setTab("out")),
157
+ tabBtn("入站连接", inbound.length, tab === "in", () => setTab("in")),
158
+ serve ? tabBtn("A2A 服务", 0, tab === "serve", () => setTab("serve")) : null
159
+ ]), tab === "out" ? (0, react.createElement)("div", { style: {
160
+ display: "flex",
161
+ flexDirection: "column",
162
+ gap: 16
163
+ } }, (0, react.createElement)("div", { style: {
164
+ display: "flex",
165
+ flexDirection: "column",
166
+ gap: 10
167
+ } }, sec("已连接 Agent", outbound.length), outbound.length === 0 ? emptyRow("暂无已连接的 Agent。使用下方\"添加 Agent\"通过 Agent Card URL 连接远程 A2A Agent。") : (0, react.createElement)("div", { style: {
168
+ display: "flex",
169
+ flexDirection: "column",
170
+ gap: 8
171
+ } }, ...outbound.map((a) => agentCard(a, busy, act, expanded, setExpanded)))), (0, react.createElement)("div", { style: {
172
+ display: "flex",
173
+ flexDirection: "column",
174
+ gap: 10,
175
+ background: "var(--dsw-alias-fill-l2, rgba(127,127,127,.05))",
176
+ borderRadius: 10,
177
+ padding: "14px 16px"
178
+ } }, (0, react.createElement)("div", { style: {
179
+ fontSize: 13,
180
+ fontWeight: 600,
181
+ color: "var(--dsw-alias-label-primary, #222)"
182
+ } }, "添加 Agent"), (0, react.createElement)("div", { style: {
183
+ display: "flex",
184
+ alignItems: "center",
185
+ gap: 8,
186
+ flexWrap: "wrap"
187
+ } }, (0, react.createElement)("input", {
188
+ placeholder: "Agent Card URL(https://…/.well-known/agent-card.json)",
189
+ value: addUrl,
190
+ onChange: (e) => {
191
+ setAddUrl(e.target.value);
192
+ setDiscovered(null);
193
+ },
194
+ style: {
195
+ ...inputStyle,
196
+ flex: 1,
197
+ minWidth: 260
198
+ }
199
+ }), actionBtn("导入", false, () => void doDiscover(), discovering || !addUrl.trim())), discovering ? (0, react.createElement)("div", { style: {
200
+ fontSize: 13,
201
+ color: "var(--dsw-alias-label-tertiary, #999)"
202
+ } }, "正在读取 Agent Card…") : discovered ? (0, react.createElement)("div", { style: {
203
+ display: "flex",
204
+ flexDirection: "column",
205
+ gap: 8,
206
+ background: "var(--dsw-alias-fill-l1, rgba(127,127,127,.06))",
207
+ borderRadius: 8,
208
+ padding: "12px 14px"
209
+ } }, (0, react.createElement)("div", { style: {
210
+ display: "flex",
211
+ alignItems: "center",
212
+ gap: 8
213
+ } }, (0, react.createElement)("span", { style: {
214
+ fontSize: 15,
215
+ fontWeight: 700,
216
+ color: "var(--dsw-alias-label-primary, #222)"
217
+ } }, discovered.name), discovered.version ? (0, react.createElement)("span", { style: {
218
+ fontSize: 11.5,
219
+ color: "var(--dsw-alias-label-tertiary, #999)",
220
+ fontFamily: "monospace"
221
+ } }, `v${discovered.version}`) : null, stateDot("connected")), discovered.description ? (0, react.createElement)("div", { style: {
222
+ fontSize: 12.5,
223
+ color: "var(--dsw-alias-label-secondary, #666)"
224
+ } }, discovered.description) : null, (discovered.skills?.length ?? 0) > 0 ? (0, react.createElement)("div", { style: {
225
+ display: "flex",
226
+ flexDirection: "column",
227
+ gap: 4
228
+ } }, (0, react.createElement)("div", { style: {
229
+ fontSize: 12,
230
+ fontWeight: 600,
231
+ color: "var(--dsw-alias-label-secondary, #666)"
232
+ } }, "Skills"), ...(discovered.skills ?? []).map((s) => (0, react.createElement)("div", {
233
+ key: s.id,
234
+ style: {
235
+ fontSize: 12.5,
236
+ color: "var(--dsw-alias-label-primary, #222)"
237
+ }
238
+ }, `• ${s.name}${s.description ? ` — ${s.description}` : ""}`))) : null, discovered.endpoint ? (0, react.createElement)("div", { style: {
239
+ fontSize: 11.5,
240
+ fontFamily: "monospace",
241
+ color: "var(--dsw-alias-label-tertiary, #999)"
242
+ } }, `端点 ${discovered.endpoint}`) : null, (0, react.createElement)("div", { style: {
243
+ display: "flex",
244
+ gap: 8,
245
+ marginTop: 2
246
+ } }, actionBtn("连接", true, () => void doConnect(), busy === "add-agent"), actionBtn("取消", false, () => {
247
+ setDiscovered(null);
248
+ setAddUrl("");
249
+ }, false))) : (0, react.createElement)("div", { style: {
250
+ fontSize: 12,
251
+ color: "var(--dsw-alias-label-tertiary, #999)"
252
+ } }, "输入远程 Agent 的 Agent Card URL 并点击\"导入\",将自动读取其名称、描述、技能与能力,确认后建立连接。"))) : null, tab === "serve" && serve ? (0, react.createElement)("div", { style: {
253
+ display: "flex",
254
+ flexDirection: "column",
255
+ gap: 10,
256
+ background: "var(--dsw-alias-fill-l2, rgba(127,127,127,.05))",
257
+ borderRadius: 10,
258
+ padding: "14px 16px"
259
+ } }, (0, react.createElement)("div", { style: {
260
+ display: "flex",
261
+ alignItems: "center",
262
+ gap: 8
263
+ } }, sec("A2A 服务(本 DSH 作为 Agent 对外)", 0), stateDot(serve.enabled ? "connected" : "disconnected"), (0, react.createElement)("span", { style: {
264
+ fontSize: 12.5,
265
+ color: serve.enabled ? "#2e7d32" : "#9e9e9e"
266
+ } }, serve.enabled ? "运行中" : "已下线"), (0, react.createElement)("span", { style: { marginLeft: "auto" } }, serve.enabled ? actionBtn("下线", false, () => void act("server-disable", ""), busy === "server-disable") : actionBtn("上线", true, () => void act("server-enable", ""), busy === "server-enable"))), (0, react.createElement)("div", { style: {
267
+ display: "flex",
268
+ flexDirection: "column",
269
+ gap: 6
270
+ } }, serve.agentName ? (0, react.createElement)("div", { style: {
271
+ fontSize: 13,
272
+ fontWeight: 600,
273
+ color: "var(--dsw-alias-label-primary, #222)"
274
+ } }, serve.agentName, serve.agentVersion ? (0, react.createElement)("span", { style: {
275
+ fontSize: 11.5,
276
+ color: "var(--dsw-alias-label-tertiary, #999)",
277
+ fontFamily: "monospace",
278
+ marginLeft: 8
279
+ } }, `v${serve.agentVersion}`) : null) : null, serve.agentDescription ? (0, react.createElement)("div", { style: {
280
+ fontSize: 12.5,
281
+ color: "var(--dsw-alias-label-secondary, #666)"
282
+ } }, serve.agentDescription) : null, serve.endpoint ? (0, react.createElement)("div", { style: {
283
+ fontSize: 12,
284
+ fontFamily: "monospace",
285
+ color: "var(--dsw-alias-label-tertiary, #999)"
286
+ } }, `端点 ${serve.endpoint}`) : null, serve.agentCardUrl ? (0, react.createElement)("div", { style: {
287
+ fontSize: 12,
288
+ fontFamily: "monospace",
289
+ color: "var(--dsw-alias-label-tertiary, #999)"
290
+ } }, `Agent Card ${serve.agentCardUrl}`) : null, (serve.skills?.length ?? 0) > 0 ? (0, react.createElement)("div", { style: {
291
+ display: "flex",
292
+ flexDirection: "column",
293
+ gap: 4
294
+ } }, (0, react.createElement)("div", { style: {
295
+ fontSize: 12,
296
+ fontWeight: 600,
297
+ color: "var(--dsw-alias-label-secondary, #666)"
298
+ } }, "Skills"), ...(serve.skills ?? []).map((s) => (0, react.createElement)("div", {
299
+ key: s.id,
300
+ style: {
301
+ fontSize: 12.5,
302
+ color: "var(--dsw-alias-label-primary, #222)"
303
+ }
304
+ }, `• ${s.name}${s.description ? ` — ${s.description}` : ""}`))) : null), serve.enabled && !serve.customExecutor ? (0, react.createElement)("div", { style: {
305
+ fontSize: 12,
306
+ color: "var(--dsw-warning, #b26a00)",
307
+ background: "rgba(178,106,0,.08)",
308
+ borderRadius: 6,
309
+ padding: "6px 10px"
310
+ } }, "⚠ 默认 executor 会执行 shell 命令。生产环境请通过配置指定受限的 execute 实现。") : null) : null, tab === "in" ? (0, react.createElement)("div", { style: {
311
+ display: "flex",
312
+ flexDirection: "column",
313
+ gap: 10
314
+ } }, sec("入站连接(谁在连接本 DSH)", inbound.length), inbound.length === 0 ? emptyRow("暂无入站连接。其他 A2A 客户端调用本 DSH 的 JSON-RPC 端点后会显示在这里。") : (0, react.createElement)("table", tblStyle, (0, react.createElement)("thead", null, headRow([
315
+ "来源",
316
+ "地址",
317
+ "首次连接",
318
+ "最近活动",
319
+ "任务",
320
+ "流",
321
+ "操作"
322
+ ])), (0, react.createElement)("tbody", null, ...inbound.map((p) => inboundRow(p, busy, act))))) : null, (0, react.createElement)("div", { style: {
323
+ color: "var(--dsw-alias-label-tertiary, #999)",
324
+ fontSize: 12
325
+ } }, `快照时间 ${snap ? new Date(snap.at).toLocaleTimeString() : "—"} · 每 3 秒自动刷新`));
326
+ }
327
+ function agentCard(a, busy, act, expanded, setExpanded) {
328
+ const target = a.connectionId ?? a.id;
329
+ const open = expanded.has(target);
330
+ const skills = a.skills ?? [];
331
+ const toggle = () => {
332
+ const next = new Set(expanded);
333
+ if (next.has(target)) next.delete(target);
334
+ else next.add(target);
335
+ setExpanded(next);
336
+ };
337
+ return (0, react.createElement)("div", {
338
+ key: a.id,
339
+ style: {
340
+ display: "flex",
341
+ flexDirection: "column",
342
+ gap: 8,
343
+ background: "var(--dsw-alias-fill-l1, rgba(127,127,127,.05))",
344
+ borderRadius: 10,
345
+ padding: "12px 14px",
346
+ opacity: a.enabled === false ? .72 : 1
347
+ }
348
+ }, (0, react.createElement)("div", {
349
+ style: {
350
+ display: "flex",
351
+ alignItems: "center",
352
+ gap: 10,
353
+ cursor: "pointer",
354
+ userSelect: "none",
355
+ flexWrap: "wrap"
356
+ },
357
+ onClick: toggle
358
+ }, (0, react.createElement)("span", { style: {
359
+ fontSize: 11,
360
+ color: "var(--dsw-alias-label-tertiary, #999)",
361
+ width: 14,
362
+ display: "inline-block",
363
+ transition: "transform .15s",
364
+ transform: open ? "rotate(90deg)" : "none"
365
+ } }, "▶"), stateDot(a.enabled === false ? "disabled" : a.state), (0, react.createElement)("span", { style: {
366
+ fontSize: 14,
367
+ fontWeight: 600,
368
+ color: "var(--dsw-alias-label-primary, #222)"
369
+ } }, a.agentName ?? a.name), a.enabled === false ? (0, react.createElement)("span", { style: {
370
+ fontSize: 11.5,
371
+ ...sourceBadge("#b26a00", "rgba(178,106,0,.1)", "已禁用")
372
+ } }, "已禁用") : (0, react.createElement)("span", { style: {
373
+ fontSize: 11.5,
374
+ ...a.configured ? sourceBadge("#666", "rgba(127,127,127,.12)", "配置") : sourceBadge("#4a7dff", "rgba(74,125,255,.1)", "运行时")
375
+ } }, a.configured ? "配置" : "运行时"), (0, react.createElement)("span", { style: {
376
+ fontSize: 12,
377
+ color: "var(--dsw-alias-label-tertiary, #999)",
378
+ whiteSpace: "nowrap",
379
+ overflow: "hidden",
380
+ textOverflow: "ellipsis",
381
+ minWidth: 0
382
+ } }, `${a.skillCount} 技能 · ${a.toolCount} 工具 · ${new Date(a.lastSeen).toLocaleTimeString()}`), (0, react.createElement)("div", { style: {
383
+ display: "flex",
384
+ alignItems: "center",
385
+ gap: 4,
386
+ flexShrink: 0,
387
+ marginLeft: "auto",
388
+ paddingLeft: 10
389
+ } }, a.enabled === false ? actionBtn("启用", true, () => void act("enable-agent", target), busy === `enable-agent:${target}`) : actionBtn("重连", true, () => void act("reconnect-agent", target), busy === `reconnect-agent:${target}`), actionBtn("禁用", false, () => void act("disable-agent", target), busy === `disable-agent:${target}`), actionBtn("删除", false, () => void act("remove-agent", target), busy === `remove-agent:${target}`))), (0, react.createElement)("div", { style: {
390
+ fontSize: 12,
391
+ fontFamily: "monospace",
392
+ color: "var(--dsw-alias-label-tertiary, #999)",
393
+ wordBreak: "break-all"
394
+ } }, a.agentCardUrl), open && skills.length > 0 ? (0, react.createElement)("div", { style: {
395
+ display: "flex",
396
+ flexDirection: "column",
397
+ gap: 6,
398
+ background: "var(--dsw-alias-fill-l2, rgba(0,0,0,.04))",
399
+ borderRadius: 8,
400
+ padding: "10px 12px",
401
+ marginTop: 2
402
+ } }, ...skills.map((s) => (0, react.createElement)("div", {
403
+ key: s.id,
404
+ style: {
405
+ display: "flex",
406
+ flexDirection: "column",
407
+ gap: 2
408
+ }
409
+ }, (0, react.createElement)("div", { style: {
410
+ display: "flex",
411
+ alignItems: "baseline",
412
+ gap: 6
413
+ } }, (0, react.createElement)("span", { style: {
414
+ fontSize: 12.5,
415
+ fontWeight: 600,
416
+ color: "var(--dsw-alias-label-primary, #222)",
417
+ fontFamily: "monospace"
418
+ } }, s.name), s.tags?.length ? (0, react.createElement)("span", { style: {
419
+ fontSize: 11,
420
+ color: "var(--dsw-alias-label-tertiary, #999)"
421
+ } }, s.tags.join(", ")) : null), s.description ? (0, react.createElement)("div", { style: {
422
+ fontSize: 12,
423
+ color: "var(--dsw-alias-label-secondary, #666)"
424
+ } }, s.description) : null))) : open ? (0, react.createElement)("div", { style: {
425
+ fontSize: 12,
426
+ color: "var(--dsw-alias-label-tertiary, #999)"
427
+ } }, "(该 Agent 未声明技能)") : null);
428
+ }
429
+ function sourceBadge(color, bg, _label) {
430
+ return {
431
+ color,
432
+ background: bg,
433
+ borderRadius: 4,
434
+ padding: "1px 6px"
435
+ };
436
+ }
437
+ /** A tab button matching the settings "插件" page's underline tab style. */
438
+ function tabBtn(label, count, active, onClick) {
439
+ return (0, react.createElement)("button", {
440
+ onClick,
441
+ "aria-selected": active,
442
+ role: "tab",
443
+ "data-active": active,
444
+ style: {
445
+ padding: "7px 1px 9px",
446
+ fontSize: 13,
447
+ lineHeight: "20px",
448
+ fontWeight: 400,
449
+ color: active ? "var(--dsw-alias-label-primary, #0f1115)" : "var(--dsw-alias-label-tertiary, #81858c)",
450
+ background: "transparent",
451
+ border: "none",
452
+ cursor: "pointer",
453
+ position: "relative",
454
+ boxShadow: active ? "inset 0 -2px 0 var(--dsw-alias-label-primary, #0f1115)" : "none",
455
+ borderRadius: "2px 2px 0 0",
456
+ transition: "color .15s, box-shadow .15s"
457
+ }
458
+ }, label, count > 0 ? (0, react.createElement)("span", { style: {
459
+ fontSize: 12,
460
+ color: "var(--dsw-alias-label-tertiary, #999)",
461
+ marginLeft: 6
462
+ } }, String(count)) : null);
463
+ }
464
+ /** The tab strip container replicating `.pbvGtq_tabs` (border-bottom rail). */
465
+ function tabRail(children) {
466
+ return (0, react.createElement)("div", {
467
+ role: "tablist",
468
+ style: {
469
+ display: "flex",
470
+ alignItems: "flex-end",
471
+ gap: 22,
472
+ borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(0,0,0,.1))",
473
+ marginTop: 2
474
+ }
475
+ }, ...children);
476
+ }
477
+ function sec(title, count) {
478
+ return (0, react.createElement)("h3", { style: {
479
+ margin: 0,
480
+ fontSize: 14,
481
+ fontWeight: 600,
482
+ color: "var(--dsw-alias-label-primary, #222)",
483
+ display: "flex",
484
+ alignItems: "center",
485
+ gap: 8
486
+ } }, title, (0, react.createElement)("span", { style: {
487
+ fontSize: 12,
488
+ color: "var(--dsw-alias-label-tertiary, #999)",
489
+ background: "var(--dsw-alias-fill-l2, rgba(127,127,127,.1))",
490
+ borderRadius: 10,
491
+ padding: "1px 8px"
492
+ } }, String(count)));
493
+ }
494
+ function emptyRow(text) {
495
+ return (0, react.createElement)("div", { style: {
496
+ color: "var(--dsw-alias-label-tertiary, #999)",
497
+ fontSize: 13,
498
+ background: "var(--dsw-alias-fill-l2, rgba(127,127,127,.06))",
499
+ borderRadius: 8,
500
+ padding: "12px 14px"
501
+ } }, text);
502
+ }
503
+ const tblStyle = {
504
+ borderCollapse: "collapse",
505
+ width: "100%",
506
+ fontSize: 13
507
+ };
508
+ const thStyle = {
509
+ textAlign: "left",
510
+ padding: "6px 8px",
511
+ borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,.18))",
512
+ color: "var(--dsw-alias-label-secondary, #666)",
513
+ fontWeight: 600,
514
+ whiteSpace: "nowrap"
515
+ };
516
+ const tdStyle = {
517
+ padding: "6px 8px",
518
+ borderBottom: "1px solid var(--dsw-alias-border-l1, rgba(127,127,127,.1))",
519
+ color: "var(--dsw-alias-label-primary, #222)",
520
+ verticalAlign: "top",
521
+ fontSize: 12.5
522
+ };
523
+ function headRow(cols) {
524
+ return (0, react.createElement)("tr", null, ...cols.map((c) => (0, react.createElement)("th", { style: thStyle }, c)));
525
+ }
526
+ const stateColor = {
527
+ connected: "#2e7d32",
528
+ disconnected: "#9e9e9e",
529
+ reconnecting: "#f57c00",
530
+ disabled: "#b26a00"
531
+ };
532
+ function stateDot(state) {
533
+ return (0, react.createElement)("span", { style: {
534
+ display: "inline-block",
535
+ width: 8,
536
+ height: 8,
537
+ borderRadius: 4,
538
+ marginRight: 6,
539
+ background: stateColor[state] ?? "#9e9e9e"
540
+ } });
541
+ }
542
+ function actionBtn(label, primary, onClick, disabled) {
543
+ return (0, react.createElement)("button", {
544
+ onClick,
545
+ disabled,
546
+ style: {
547
+ marginRight: 6,
548
+ padding: "3px 10px",
549
+ fontSize: 12,
550
+ lineHeight: "18px",
551
+ borderRadius: 6,
552
+ border: `1px solid ${primary ? "var(--dsw-brand, #4a7dff)" : "var(--dsw-alias-border-l2, rgba(127,127,127,.3))"}`,
553
+ background: primary ? "var(--dsw-brand, #4a7dff)" : "transparent",
554
+ color: primary ? "#fff" : "var(--dsw-alias-label-primary, #222)",
555
+ cursor: disabled ? "default" : "pointer",
556
+ opacity: disabled ? .5 : 1
557
+ }
558
+ }, label);
559
+ }
560
+ function inboundRow(p, busy, act) {
561
+ return (0, react.createElement)("tr", { key: p.id }, (0, react.createElement)("td", { style: tdStyle }, p.label), (0, react.createElement)("td", { style: {
562
+ ...tdStyle,
563
+ fontFamily: "monospace",
564
+ fontSize: 12
565
+ } }, p.source ?? "—"), (0, react.createElement)("td", { style: tdStyle }, new Date(p.firstSeen).toLocaleTimeString()), (0, react.createElement)("td", { style: tdStyle }, new Date(p.lastSeen).toLocaleTimeString()), (0, react.createElement)("td", { style: tdStyle }, String(p.taskCount)), (0, react.createElement)("td", { style: tdStyle }, p.streaming ? "● 流" : "—"), (0, react.createElement)("td", { style: tdStyle }, actionBtn("关闭", false, () => void act("close-peer", p.id), busy === `close-peer:${p.id}`)));
566
+ }
567
+ //#endregion
568
+ exports.DashboardSection = DashboardSection;
569
+ exports.apply = apply;
570
+ exports.inject = inject;
571
+
572
+ return module.exports;
573
+ }
574
+ });