@blade-hq/agent-react 2612.0.0-beta.6 → 2612.0.0-beta.60

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.
Files changed (112) hide show
  1. package/README.md +30 -1
  2. package/dist/chat/AgentLoopBlock.d.ts +0 -2
  3. package/dist/chat/AskUserQuestionBlock.d.ts +3 -20
  4. package/dist/chat/AssistantTurnBlock.d.ts +1 -9
  5. package/dist/chat/AttachmentPreviewDialog.d.ts +0 -1
  6. package/dist/chat/ChatLoadingIndicator.d.ts +7 -0
  7. package/dist/chat/ContextAttachmentPills.d.ts +4 -0
  8. package/dist/chat/CurrentPlanPanel.d.ts +0 -3
  9. package/dist/chat/FileDeliveryCards.d.ts +0 -4
  10. package/dist/chat/FinalArtifactCards.d.ts +0 -1
  11. package/dist/chat/ImageLightbox.d.ts +1 -1
  12. package/dist/chat/LiteMessageList.d.ts +1 -3
  13. package/dist/chat/MessageFileAttachmentList.d.ts +0 -5
  14. package/dist/chat/QuestionnaireQuestionBlock.d.ts +11 -0
  15. package/dist/chat/SessionReplayBar.d.ts +0 -1
  16. package/dist/chat/TextAttachmentPills.d.ts +0 -3
  17. package/dist/chat/TurnNavRail.d.ts +0 -3
  18. package/dist/chat/WorkflowRunBlock.d.ts +21 -4
  19. package/dist/chat/ai-elements/shimmer.d.ts +0 -4
  20. package/dist/chat/customization.d.ts +1 -1
  21. package/dist/chat/index.d.ts +1 -4
  22. package/dist/chat/lib/citations.d.ts +2 -23
  23. package/dist/chat/markdown/ExternalAnchor.d.ts +2 -2
  24. package/dist/chat/markdown/MarkdownContent.d.ts +3 -0
  25. package/dist/chat/message-list-blocks.d.ts +0 -1
  26. package/dist/chat/runtime/actions.d.ts +2 -3
  27. package/dist/chat/runtime/background-task-dismissals.d.ts +0 -1
  28. package/dist/chat/runtime/composer-channels.d.ts +0 -3
  29. package/dist/chat/runtime/input-cache-reset.d.ts +0 -5
  30. package/dist/chat/runtime/input-services.d.ts +10 -6
  31. package/dist/chat/runtime/session-source.d.ts +0 -5
  32. package/dist/chat/runtime/use-chat-actions.d.ts +0 -4
  33. package/dist/{chat-OCLOVB3H.css → chat-IHPRU35X.css} +5 -18
  34. package/dist/chat.css +5 -18
  35. package/dist/chunk-2E6DQCXM.js +602 -0
  36. package/dist/chunk-2E6DQCXM.js.map +1 -0
  37. package/dist/chunk-ARX4JV5T.js +1988 -0
  38. package/dist/chunk-ARX4JV5T.js.map +1 -0
  39. package/dist/chunk-HBMFIJG2.js +13622 -0
  40. package/dist/chunk-HBMFIJG2.js.map +1 -0
  41. package/dist/chunk-HS5G33FO.js +6894 -0
  42. package/dist/chunk-HS5G33FO.js.map +1 -0
  43. package/dist/{chunk-UKQFSXGC.js → chunk-MAFNMG2O.js} +2419 -13726
  44. package/dist/chunk-MAFNMG2O.js.map +1 -0
  45. package/dist/{chunk-XJLFFRQP.js → chunk-UC7SY3G5.js} +3744 -25994
  46. package/dist/chunk-UC7SY3G5.js.map +1 -0
  47. package/dist/chunk-VSIUEXGV.js +4981 -0
  48. package/dist/chunk-VSIUEXGV.js.map +1 -0
  49. package/dist/chunk-XV6AO37F.js +9480 -0
  50. package/dist/chunk-XV6AO37F.js.map +1 -0
  51. package/dist/components/AccountConnectors.d.ts +13 -0
  52. package/dist/components/BrowserConnectionDialog.d.ts +7 -0
  53. package/dist/components/ChatInput.d.ts +1 -15
  54. package/dist/components/ConnectorAuthorizationProgress.d.ts +6 -0
  55. package/dist/components/ConnectorPanel.d.ts +3 -1
  56. package/dist/components/ConnectorRuntimeDiagnostics.d.ts +5 -0
  57. package/dist/components/DeviceConnection.d.ts +15 -0
  58. package/dist/components/FeishuCapabilitySelection.d.ts +14 -0
  59. package/dist/components/FeishuPermissionStatus.d.ts +5 -0
  60. package/dist/components/LlmAdvancedSettings.d.ts +0 -1
  61. package/dist/components/PlanUpdateBlock.d.ts +0 -1
  62. package/dist/components/SessionPluginConfigForm.d.ts +3 -1
  63. package/dist/components/plugin-connector.d.ts +1 -0
  64. package/dist/connectors.d.ts +1 -0
  65. package/dist/connectors.js +9 -0
  66. package/dist/connectors.js.map +1 -0
  67. package/dist/embed/blade-chat-element.d.ts +0 -8
  68. package/dist/embed/entry.d.ts +8 -19
  69. package/dist/hooks/use-message-pin.d.ts +1 -1
  70. package/dist/host.d.ts +0 -4
  71. package/dist/index.d.ts +1 -0
  72. package/dist/index.js +12 -3
  73. package/dist/internal.d.ts +4 -2
  74. package/dist/internal.js +45 -70
  75. package/dist/internal.js.map +1 -1
  76. package/dist/lib/message-shortcuts.d.ts +5 -0
  77. package/dist/questions.d.ts +7 -0
  78. package/dist/questions.js +909 -0
  79. package/dist/questions.js.map +1 -0
  80. package/dist/rich-input/ConnectButton.d.ts +7 -5
  81. package/dist/rich-input/FileCompletionMenu.d.ts +5 -11
  82. package/dist/rich-input/InputActionSubmenu.d.ts +10 -0
  83. package/dist/rich-input/SkillActionMenu.d.ts +10 -0
  84. package/dist/rich-input/SkillStatusBar.d.ts +0 -6
  85. package/dist/rich-input/extensions/skill-mention.d.ts +3 -20
  86. package/dist/rich-input/index.d.ts +3 -4
  87. package/dist/rich-input/lib/composer-content.d.ts +7 -22
  88. package/dist/rich-input/lib/plugin-suggestions.d.ts +5 -5
  89. package/dist/rich-input/lib/reference-candidates.d.ts +0 -4
  90. package/dist/rich-input/lib/skill-suggestions.d.ts +1 -1
  91. package/dist/rich-input/lib/user-home.d.ts +0 -17
  92. package/dist/rich-input/services.d.ts +2 -3
  93. package/dist/rich-input/voice/VoiceWaveform.d.ts +0 -3
  94. package/dist/rich-input.js +786 -1300
  95. package/dist/rich-input.js.map +1 -1
  96. package/dist/style.css +16 -55
  97. package/dist/style.full.css +23 -77
  98. package/dist/tool-details.d.ts +8 -0
  99. package/dist/tool-details.js +24 -0
  100. package/dist/tool-details.js.map +1 -0
  101. package/package.json +15 -3
  102. package/public-api.md +10 -5
  103. package/dist/chat/tool-renderers/KbSearchRenderer.d.ts +0 -2
  104. package/dist/chat/tool-renderers/kb-search-result.d.ts +0 -19
  105. package/dist/chunk-IRBOKMF5.js +0 -258
  106. package/dist/chunk-IRBOKMF5.js.map +0 -1
  107. package/dist/chunk-UKQFSXGC.js.map +0 -1
  108. package/dist/chunk-XJLFFRQP.js.map +0 -1
  109. package/dist/lib/agent-computer-command.d.ts +0 -32
  110. package/dist/rich-input/extensions/kb-mention.d.ts +0 -18
  111. package/dist/rich-input/extensions/workflow-mention.d.ts +0 -10
  112. package/dist/rich-input/lib/kb-suggestions.d.ts +0 -37
package/dist/chat.css CHANGED
@@ -1,9 +1,3 @@
1
- /*
2
- * 聊天组件共用的非 Tailwind 规则(Markdown 排版、入场动画、扫光)。内置 Web 与插件经 agent/style.css
3
- * 引入这一份;<blade-chat> 与 npm 样式由 scripts/gen-embed-styles.mjs 放进 blade-chat-overrides 层。
4
- * 只写聊天组件自己用到的规则,页面级补丁不放这里。
5
- */
6
-
7
1
  [data-theme="light"] .prose-invert {
8
2
  --tw-prose-body: hsl(var(--foreground));
9
3
  --tw-prose-headings: hsl(var(--foreground));
@@ -25,9 +19,7 @@
25
19
  color: hsl(var(--primary));
26
20
  }
27
21
 
28
- /* --- chat markdown density: 收紧块间距,避免松散 ---------------- */
29
- /* typography 插件默认 p/li/heading 外边距偏大(1.25em/0.5em),聊天里读起来太松。
30
- plain 选择器特异性高于插件的 :where(),可直接覆盖。 */
22
+
31
23
  .chat-prose p {
32
24
  margin-top: 0.5em;
33
25
  margin-bottom: 0.5em;
@@ -85,12 +77,7 @@
85
77
  margin-bottom: 1.2em;
86
78
  }
87
79
 
88
- /* 未闭合的加粗/斜体/行内代码,在闭合符真正到达前先按最终标签
89
- * 渲染、只用透明度标出"待确认"——内容和标签结构全程不变,闭合符到达后
90
- * 这条规则自然不再匹配,恢复完全不透明,DOM 节点没有被替换过。这个属性
91
- * 只由 MarkdownRenderer 引擎自己产出,不区分调用方用的是哪套 prose 外壳
92
- * (`.chat-prose`/`.prose`/`.lite-chat-markdown`/无外壳),不按外壳类名
93
- * 逐个补,直接全局生效。 */
80
+
94
81
  [data-markdown-pending="true"] {
95
82
  opacity: 0.6;
96
83
  }
@@ -108,7 +95,7 @@
108
95
  overflow-x: auto;
109
96
  }
110
97
 
111
- /* Message entrance animation */
98
+
112
99
  @keyframes msg-in {
113
100
  from {
114
101
  opacity: 0;
@@ -124,7 +111,7 @@
124
111
  animation: msg-in 0.15s ease-out forwards;
125
112
  }
126
113
 
127
- /* Running-state sweep, borrowed from deepseek-harness's ReasoningRow. */
114
+
128
115
  .status-sweep::after {
129
116
  content: "";
130
117
  position: absolute;
@@ -153,7 +140,7 @@
153
140
  }
154
141
  }
155
142
 
156
- /* 流式等待的扫光文字(ai-elements/shimmer.tsx) */
143
+
157
144
  @keyframes blade-shimmer-sweep {
158
145
  from {
159
146
  background-position: 100% center;
@@ -0,0 +1,602 @@
1
+ import {
2
+ PluginConnectorList,
3
+ SessionPluginConfigForm,
4
+ SessionPluginIcon,
5
+ catalogToConnectorItems,
6
+ filterConnectorItems,
7
+ useSessionPluginActivation
8
+ } from "./chunk-MAFNMG2O.js";
9
+ import {
10
+ DeviceConnection
11
+ } from "./chunk-ARX4JV5T.js";
12
+ import {
13
+ BrowserConnectionDialog,
14
+ Check,
15
+ LoaderCircle,
16
+ Pencil,
17
+ Plug,
18
+ RefreshCw,
19
+ Search,
20
+ Trash2,
21
+ Unplug
22
+ } from "./chunk-VSIUEXGV.js";
23
+
24
+ // src/components/connector-panel-types.ts
25
+ function selectionFromItem(item) {
26
+ return {
27
+ name: item.name,
28
+ displayName: item.display_name ?? null,
29
+ icon: item.icon,
30
+ iconSource: item.iconSource
31
+ };
32
+ }
33
+ var TRIGGER_ICON_LIMIT = 3;
34
+ function triggerSummary(items) {
35
+ const visible = items.slice(0, TRIGGER_ICON_LIMIT);
36
+ return {
37
+ visible,
38
+ overflow: items.length - visible.length,
39
+ total: items.length
40
+ };
41
+ }
42
+
43
+ // src/components/AccountConnectors.tsx
44
+ import { isLocalConnectorName } from "@blade-hq/agent-client/internal";
45
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
46
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
47
+ var actionBaseClass = "inline-flex h-8 shrink-0 items-center justify-center gap-1.5 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] text-xs font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--accent))] focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50";
48
+ var actionClass = `${actionBaseClass} px-3`;
49
+ var iconActionClass = `${actionBaseClass} w-8 p-0`;
50
+ async function failureMessage(error) {
51
+ const response = error?.response;
52
+ if (response) {
53
+ try {
54
+ const body = await response.json();
55
+ if (typeof body.detail === "string") return body.detail;
56
+ if (typeof body.detail?.message === "string") return body.detail.message;
57
+ } catch {
58
+ }
59
+ }
60
+ return "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u8BD5";
61
+ }
62
+ function DeviceDialog({ client, connector, onChanged, onClose }) {
63
+ const dialog = useRef(null);
64
+ useEffect(() => {
65
+ const element = dialog.current;
66
+ element?.showModal();
67
+ return () => element?.close();
68
+ }, []);
69
+ return /* @__PURE__ */ jsxs("dialog", { ref: dialog, "aria-label": "\u8FDE\u63A5\u98DE\u4E66", onCancel: (event) => {
70
+ event.preventDefault();
71
+ onClose();
72
+ }, className: "fixed inset-0 m-auto max-h-[85vh] w-[min(640px,90vw)] overflow-y-auto rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background))] p-6 text-[hsl(var(--foreground))] backdrop:bg-black/40", children: [
73
+ /* @__PURE__ */ jsx(DeviceConnection, { read: () => client.connectors.authorization(connector.name), act: (action, options) => client.connectors.authorize(connector.name, action, void 0, options), plan: (capabilities) => client.connectors.authorizationPlan(connector.name, capabilities), diagnostics: () => client.connectors.diagnostics(connector.name), onChanged, onDisable: () => client.connectors.disconnect(connector.name) }),
74
+ /* @__PURE__ */ jsx("button", { type: "button", className: actionClass, onClick: onClose, children: "\u8FD4\u56DE\u8FDE\u63A5\u5668" })
75
+ ] });
76
+ }
77
+ function ConnectionDialog({ client, connector, onConnected, onClose }) {
78
+ const [view, setView] = useState(null);
79
+ const [failure, setFailure] = useState(null);
80
+ const [submitting, setSubmitting] = useState(false);
81
+ const [connected, setConnected] = useState(false);
82
+ const dialog = useRef(null);
83
+ const connectionRequest = useRef(null);
84
+ useEffect(() => () => connectionRequest.current?.abort(), []);
85
+ useEffect(() => {
86
+ const element = dialog.current;
87
+ element?.showModal();
88
+ return () => element?.close();
89
+ }, []);
90
+ useEffect(() => {
91
+ const abort = new AbortController();
92
+ client.connectors.read(connector.name, { signal: abort.signal }).then((value) => {
93
+ if (!abort.signal.aborted) setView(value);
94
+ }).catch(async (error) => {
95
+ const message = await failureMessage(error);
96
+ if (!abort.signal.aborted) setFailure(message);
97
+ });
98
+ return () => abort.abort();
99
+ }, [client, connector.name]);
100
+ return /* @__PURE__ */ jsx(
101
+ "dialog",
102
+ {
103
+ ref: dialog,
104
+ "aria-label": `\u8FDE\u63A5 ${connector.display_name || connector.name}`,
105
+ className: "fixed inset-0 m-0 h-full max-h-none w-full max-w-none items-center justify-center border-0 bg-black/40 p-0 open:flex",
106
+ onCancel: (event) => {
107
+ event.preventDefault();
108
+ if (!submitting) onClose();
109
+ },
110
+ children: /* @__PURE__ */ jsxs("div", { className: "max-h-[80vh] w-[min(448px,calc(100%_-_32px))] overflow-y-auto rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--popover))] p-4 text-[hsl(var(--popover-foreground))] shadow-lg", children: [
111
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 text-sm font-medium", children: [
112
+ "\u8FDE\u63A5 ",
113
+ connector.display_name || connector.name
114
+ ] }),
115
+ /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-[hsl(var(--muted-foreground))]", children: view?.config.state === "not_required" ? "\u65E0\u9700\u586B\u5199\u8D26\u53F7\uFF0C\u9A8C\u8BC1\u8FDE\u63A5\u540E\u5373\u53EF\u4F7F\u7528\u3002" : "\u586B\u5199\u4F60\u7684\u8D26\u53F7\u4FE1\u606F\uFF0C\u9A8C\u8BC1\u901A\u8FC7\u540E\u53EF\u5728\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u3002" }),
116
+ !view ? /* @__PURE__ */ jsx("div", { className: "text-xs", children: failure || "\u6B63\u5728\u83B7\u53D6\u8BA4\u8BC1\u8868\u5355\u2026" }) : !connected ? /* @__PURE__ */ jsx(
117
+ SessionPluginConfigForm,
118
+ {
119
+ config: view.config,
120
+ submitting,
121
+ errors: [],
122
+ failure,
123
+ submitLabel: "\u8FDE\u63A5\u5E76\u9A8C\u8BC1",
124
+ submittingLabel: "\u6B63\u5728\u8FDE\u63A5\u2026",
125
+ onCancel: onClose,
126
+ onSubmit: async (draft) => {
127
+ setSubmitting(true);
128
+ setFailure(null);
129
+ const request = new AbortController();
130
+ connectionRequest.current = request;
131
+ try {
132
+ const connected2 = await client.connectors.connect(connector.name, {
133
+ expected_revision: view.config.revision,
134
+ set: draft.set,
135
+ remove: draft.remove
136
+ }, { signal: request.signal });
137
+ if (request.signal.aborted) return;
138
+ setView(connected2);
139
+ setConnected(true);
140
+ try {
141
+ await onConnected();
142
+ } catch {
143
+ if (!request.signal.aborted) setFailure("\u5DF2\u8FDE\u63A5\uFF0C\u4F46\u5217\u8868\u6216\u5BF9\u8BDD\u72B6\u6001\u672A\u80FD\u5237\u65B0\u3002\u8BF7\u8FD4\u56DE\u540E\u91CD\u8BD5\u5237\u65B0\u6216\u542F\u7528\u3002");
144
+ return;
145
+ }
146
+ if (!request.signal.aborted) onClose();
147
+ } catch (error) {
148
+ if (request.signal.aborted) return;
149
+ setFailure(await failureMessage(error));
150
+ try {
151
+ setView(await client.connectors.read(connector.name));
152
+ } catch {
153
+ }
154
+ } finally {
155
+ if (!request.signal.aborted) setSubmitting(false);
156
+ }
157
+ }
158
+ }
159
+ ) : null,
160
+ connected ? /* @__PURE__ */ jsx("output", { className: "block text-sm", children: failure || "\u8D26\u53F7\u5DF2\u8FDE\u63A5\u3002" }) : null,
161
+ !view || connected ? /* @__PURE__ */ jsx("button", { type: "button", className: `${actionClass} mt-3`, onClick: onClose, children: "\u5173\u95ED" }) : null
162
+ ] })
163
+ }
164
+ );
165
+ }
166
+ function AccountConnectors({ client, query = "", sessionId = null, pending, onUse, onSelectedChange, disabledReason, catalog = false, manage = false, onEdit }) {
167
+ const [items, setItems] = useState([]);
168
+ const [editing, setEditing] = useState(null);
169
+ const [failure, setFailure] = useState(null);
170
+ const [loading, setLoading] = useState(true);
171
+ const deleteDialog = useRef(null);
172
+ const [deleting, setDeleting] = useState(null);
173
+ const [busy, setBusy] = useState(false);
174
+ useEffect(() => {
175
+ if (!deleting) return;
176
+ const element = deleteDialog.current;
177
+ element?.showModal();
178
+ return () => element?.close();
179
+ }, [deleting]);
180
+ const lifetime = useRef(null);
181
+ const load = useCallback((signal) => sessionId ? client.connectors.listForSession(sessionId, { signal }) : client.connectors.list({ signal }), [client, sessionId]);
182
+ const reload = useCallback(async () => {
183
+ const scope = lifetime.current;
184
+ const result = await load(scope?.signal);
185
+ if (scope === lifetime.current && !scope?.signal.aborted) {
186
+ setItems(result.connectors);
187
+ setFailure(null);
188
+ }
189
+ }, [load]);
190
+ useEffect(() => {
191
+ const abort = new AbortController();
192
+ lifetime.current = abort;
193
+ setItems([]);
194
+ setEditing(null);
195
+ setLoading(true);
196
+ setFailure(null);
197
+ load(abort.signal).then((value) => {
198
+ if (!abort.signal.aborted) setItems(value.connectors);
199
+ }).catch(async (error) => {
200
+ const message = await failureMessage(error);
201
+ if (!abort.signal.aborted) setFailure(message);
202
+ }).finally(() => {
203
+ if (!abort.signal.aborted) setLoading(false);
204
+ });
205
+ return () => abort.abort();
206
+ }, [load]);
207
+ const visible = useMemo(() => {
208
+ const result = items.map((item) => ({ ...item, active: sessionId ? item.active : Boolean(pending?.has(item.name)) }));
209
+ if (!sessionId) {
210
+ for (const name of pending ?? []) if (isLocalConnectorName(name) && !result.some((item) => item.name === name)) result.push({ name, display_name: name.split(".").slice(2).join("."), description: "\u8FDE\u63A5\u5668\u5DF2\u79FB\u9664\u6216\u6682\u4E0D\u53EF\u7528\uFF0C\u53EF\u53D6\u6D88\u9009\u62E9\u540E\u7EE7\u7EED\u3002", source: name.startsWith("connector.builtin.") ? "builtin" : "personal", operations: 0, available: false, active: true, connection: { status: "disconnected", revision: 0, account_label: "", verified_at: "" } });
211
+ }
212
+ return result;
213
+ }, [items, pending, sessionId]);
214
+ const selected = useRef(onSelectedChange);
215
+ selected.current = onSelectedChange;
216
+ useEffect(() => {
217
+ selected.current?.(visible.filter((item) => item.active));
218
+ }, [visible]);
219
+ const matching = visible.filter((item) => `${item.display_name} ${item.description}`.toLowerCase().includes(query.trim().toLowerCase()));
220
+ return /* @__PURE__ */ jsxs("div", { className: catalog ? "grid gap-4 sm:grid-cols-2 xl:grid-cols-3" : void 0, children: [
221
+ !catalog ? /* @__PURE__ */ jsx("div", { className: "px-3 pt-1 pb-0.5 text-[10px] text-[hsl(var(--muted-foreground))]", children: "\u8FDE\u63A5\u5668" }) : null,
222
+ loading ? /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: "\u6B63\u5728\u8BFB\u53D6\u8FDE\u63A5\u5668\u2026" }) : null,
223
+ !loading && !failure && matching.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: query.trim() ? "\u6CA1\u6709\u5339\u914D\u7684\u8FDE\u63A5\u5668" : "\u6682\u65E0\u8FDE\u63A5\u5668" }) : null,
224
+ matching.map((item) => {
225
+ const connected = item.connection.status === "connected";
226
+ const label = item.display_name || item.name;
227
+ const ConnectIcon = connected ? RefreshCw : Plug;
228
+ const connectionClass = `${actionClass} ${!connected ? "border-transparent bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:bg-[hsl(var(--primary)/0.9)]" : ""}`;
229
+ const connectAction = /* @__PURE__ */ jsxs("button", { type: "button", disabled: busy || item.available === false, className: connectionClass, onClick: () => {
230
+ setFailure(null);
231
+ setEditing(item);
232
+ }, children: [
233
+ /* @__PURE__ */ jsx(ConnectIcon, { size: 14, "aria-hidden": "true" }),
234
+ item.authorization?.type === "device" ? "\u7BA1\u7406\u6388\u6743" : connected ? "\u91CD\u65B0\u8FDE\u63A5" : "\u8FDE\u63A5"
235
+ ] });
236
+ const useAction = onUse && (item.active || connected && item.operations > 0) ? /* @__PURE__ */ jsxs("button", { type: "button", disabled: busy || Boolean(disabledReason), title: disabledReason ?? void 0, className: actionClass, onClick: async () => {
237
+ const scope = lifetime.current;
238
+ setBusy(true);
239
+ setFailure(null);
240
+ try {
241
+ await onUse(item.name, label, !item.active);
242
+ if (sessionId) await reload();
243
+ } catch (error) {
244
+ const message = await failureMessage(error);
245
+ if (scope === lifetime.current && !scope?.signal.aborted) setFailure(message);
246
+ } finally {
247
+ if (scope === lifetime.current && !scope?.signal.aborted) setBusy(false);
248
+ }
249
+ }, children: [
250
+ item.active ? /* @__PURE__ */ jsx(Check, { size: 14, "aria-hidden": "true" }) : null,
251
+ item.active ? "\u5DF2\u542F\u7528" : "\u4F7F\u7528"
252
+ ] }) : null;
253
+ return /* @__PURE__ */ jsxs("div", { className: catalog ? "flex flex-col rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5" : "px-3 py-1.5", children: [
254
+ /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-2 ${catalog ? "text-sm" : "text-[11px]"}`, children: [
255
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium", title: label, children: label }),
256
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-md bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]", children: item.source === "builtin" ? "\u5185\u7F6E" : "\u6211\u521B\u5EFA\u7684" }),
257
+ !catalog ? /* @__PURE__ */ jsxs(Fragment, { children: [
258
+ connectAction,
259
+ useAction
260
+ ] }) : null
261
+ ] }),
262
+ catalog ? /* @__PURE__ */ jsx("p", { className: "mt-2 flex-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: item.description }) : null,
263
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-1.5 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
264
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: `size-1.5 shrink-0 rounded-full ${connected ? "bg-emerald-500" : "bg-[hsl(var(--muted-foreground)/0.4)]"}` }),
265
+ connected ? `\u5DF2\u8FDE\u63A5${item.connection.account_label ? ` \xB7 ${item.connection.account_label}` : ""}${item.name === "connector.builtin.feishu" ? "\uFF0C\u6309\u5DF2\u5F00\u901A\u80FD\u529B\u63D0\u4F9B\u64CD\u4F5C" : item.operations === 0 ? "\uFF0C\u5C1A\u672A\u914D\u7F6E\u4E1A\u52A1\u64CD\u4F5C" : ""}` : item.connection.status === "auth_required" ? "\u9700\u8981\u91CD\u65B0\u8BA4\u8BC1" : "\u672A\u8FDE\u63A5"
266
+ ] }),
267
+ catalog ? /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2 border-t border-[hsl(var(--border))] pt-4", children: [
268
+ connectAction,
269
+ useAction,
270
+ manage ? /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [
271
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": item.authorization?.type === "device" ? "\u505C\u7528 BA \u8FDE\u63A5" : "\u65AD\u5F00", title: item.authorization?.type === "device" ? "\u4EC5\u505C\u7528 BA\uFF0CSuperWork \u8D26\u53F7\u548C\u6D88\u606F\u8BA2\u9605\u4FDD\u7559" : "\u65AD\u5F00\u8FDE\u63A5", className: iconActionClass, onClick: async () => {
272
+ setBusy(true);
273
+ try {
274
+ await client.connectors.disconnect(item.name);
275
+ await reload();
276
+ } catch (error) {
277
+ setFailure(await failureMessage(error));
278
+ } finally {
279
+ setBusy(false);
280
+ }
281
+ }, children: /* @__PURE__ */ jsx(Unplug, { size: 15, "aria-hidden": "true" }) }),
282
+ item.source === "personal" ? /* @__PURE__ */ jsxs(Fragment, { children: [
283
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": "\u7F16\u8F91", title: "\u7F16\u8F91\u8FDE\u63A5\u5668", className: iconActionClass, onClick: () => onEdit?.(item), children: /* @__PURE__ */ jsx(Pencil, { size: 15, "aria-hidden": "true" }) }),
284
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, "aria-label": "\u5220\u9664", title: "\u5220\u9664\u8FDE\u63A5\u5668", className: `${iconActionClass} text-[hsl(var(--destructive))] hover:border-[hsl(var(--destructive)/0.3)] hover:bg-[hsl(var(--destructive)/0.08)]`, onClick: () => setDeleting(item), children: /* @__PURE__ */ jsx(Trash2, { size: 15, "aria-hidden": "true" }) })
285
+ ] }) : null
286
+ ] }) : null
287
+ ] }) : null
288
+ ] }, item.name);
289
+ }),
290
+ deleting ? /* @__PURE__ */ jsxs("dialog", { ref: deleteDialog, onCancel: (event) => {
291
+ event.preventDefault();
292
+ if (!busy) setDeleting(null);
293
+ }, "aria-label": "\u5220\u9664\u8FDE\u63A5\u5668", className: "fixed inset-0 z-50 m-auto w-[min(420px,calc(100%_-_32px))] rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--popover))] p-5 text-[hsl(var(--popover-foreground))] shadow-xl", children: [
294
+ /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
295
+ "\u5220\u9664\u300C",
296
+ deleting.display_name || deleting.name,
297
+ "\u300D\u53CA\u5176\u8FDE\u63A5\u4FE1\u606F\uFF1F\u9700\u8981\u4F7F\u7528\u65F6\u53EF\u4EE5\u91CD\u65B0\u521B\u5EFA\u3002"
298
+ ] }),
299
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex justify-end gap-3", children: [
300
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, className: actionClass, onClick: () => setDeleting(null), children: "\u53D6\u6D88" }),
301
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: busy, className: `${actionClass} border-transparent bg-[hsl(var(--destructive))] text-white hover:bg-[hsl(var(--destructive)/0.9)]`, onClick: async () => {
302
+ setBusy(true);
303
+ try {
304
+ await client.connectors.delete(deleting.name);
305
+ setDeleting(null);
306
+ await reload();
307
+ } catch (error) {
308
+ setFailure(await failureMessage(error));
309
+ setDeleting(null);
310
+ } finally {
311
+ setBusy(false);
312
+ }
313
+ }, children: busy ? "\u6B63\u5728\u5220\u9664\u2026" : "\u786E\u8BA4\u5220\u9664" })
314
+ ] })
315
+ ] }) : null,
316
+ failure ? /* @__PURE__ */ jsxs("div", { role: "alert", className: "px-3 py-1 text-xs text-[hsl(var(--destructive))]", children: [
317
+ failure,
318
+ /* @__PURE__ */ jsx("button", { type: "button", className: `${actionClass} ml-2`, onClick: () => {
319
+ void reload().catch(() => setFailure("\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"));
320
+ }, children: "\u91CD\u8BD5" })
321
+ ] }) : null,
322
+ editing?.authorization?.type === "device" ? /* @__PURE__ */ jsx(DeviceDialog, { client, connector: editing, onChanged: reload, onClose: () => {
323
+ setEditing(null);
324
+ void reload();
325
+ } }, editing.name) : editing?.authorization?.type === "browser" ? /* @__PURE__ */ jsx(BrowserConnectionDialog, { client, name: editing.name, label: editing.display_name || editing.name, onClose: () => {
326
+ setEditing(null);
327
+ void reload().catch(() => setFailure("\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"));
328
+ } }, editing.name) : editing ? /* @__PURE__ */ jsx(ConnectionDialog, { client, connector: editing, onClose: () => setEditing(null), onConnected: async () => {
329
+ const scope = lifetime.current;
330
+ await reload();
331
+ if (scope !== lifetime.current || scope?.signal.aborted) return;
332
+ if (onUse && editing.operations > 0 && !disabledReason) {
333
+ await onUse(editing.name, editing.display_name || editing.name, true);
334
+ if (sessionId) await reload();
335
+ }
336
+ } }, editing.name) : null
337
+ ] });
338
+ }
339
+
340
+ // src/components/ConnectorPanel.tsx
341
+ import { isLocalConnectorName as isLocalConnectorName2 } from "@blade-hq/agent-client/internal";
342
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
343
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
344
+ function PanelDivider() {
345
+ return /* @__PURE__ */ jsx2("div", { className: "my-1 border-[hsl(var(--border))] border-t" });
346
+ }
347
+ function PanelTitle({ children }) {
348
+ return /* @__PURE__ */ jsx2("div", { className: "px-3 pt-1 pb-0.5 text-[10px] text-[hsl(var(--muted-foreground))]", children });
349
+ }
350
+ function ConnectorPanel({
351
+ client,
352
+ sessionId,
353
+ pendingPlugins,
354
+ onPendingPluginsChange,
355
+ pendingDisabledReason,
356
+ onPluginsChanged,
357
+ onSelectedChange,
358
+ renderComputerSection,
359
+ renderPhoneSection,
360
+ renderExternalAccounts,
361
+ renderFooterActions,
362
+ className
363
+ }) {
364
+ const [query, setQuery] = useState2("");
365
+ const [selectedConnectors, setSelectedConnectors] = useState2([]);
366
+ const [pendingCatalog, setPendingCatalog] = useState2(null);
367
+ const [pendingLoading, setPendingLoading] = useState2(false);
368
+ const [pendingError, setPendingError] = useState2(false);
369
+ const activation = useSessionPluginActivation({
370
+ client,
371
+ sessionId: sessionId ?? "",
372
+ onChange: onPluginsChanged
373
+ });
374
+ const { reload, items: sessionItems } = activation;
375
+ useEffect2(() => {
376
+ if (sessionId) reload();
377
+ }, [reload, sessionId]);
378
+ const loadPendingCatalog = useCallback2(async () => {
379
+ setPendingLoading(true);
380
+ setPendingError(false);
381
+ try {
382
+ const { plugins } = await client.plugins.catalog();
383
+ setPendingCatalog(plugins);
384
+ } catch {
385
+ setPendingError(true);
386
+ } finally {
387
+ setPendingLoading(false);
388
+ }
389
+ }, [client]);
390
+ useEffect2(() => {
391
+ if (sessionId) return;
392
+ void loadPendingCatalog();
393
+ }, [loadPendingCatalog, sessionId]);
394
+ const selected = useMemo2(
395
+ () => new Set((pendingPlugins ?? []).map((item) => item.name)),
396
+ [pendingPlugins]
397
+ );
398
+ const items = useMemo2(() => {
399
+ if (sessionId) return sessionItems;
400
+ return catalogToConnectorItems(pendingCatalog ?? []).map((item) => ({
401
+ ...item,
402
+ active: selected.has(item.name)
403
+ }));
404
+ }, [pendingCatalog, selected, sessionId, sessionItems]);
405
+ const visibleItems = useMemo2(
406
+ () => sessionId ? filterConnectorItems(items, query) : filterConnectorItems(items, query).map((item) => ({ ...item, config: void 0 })),
407
+ [items, query, sessionId]
408
+ );
409
+ const togglePending = useCallback2((item, next) => {
410
+ const kept = (pendingPlugins ?? []).filter((entry) => entry.name !== item.name);
411
+ onPendingPluginsChange?.(next ? [...kept, selectionFromItem(item)] : kept);
412
+ }, [onPendingPluginsChange, pendingPlugins]);
413
+ const toggle = useCallback2((item, next) => {
414
+ if (sessionId) activation.toggle(item, next);
415
+ else togglePending(item, next);
416
+ }, [activation, sessionId, togglePending]);
417
+ const selectedItems = useMemo2(
418
+ () => [
419
+ ...items.filter((item) => item.active && !isLocalConnectorName2(item.name)),
420
+ ...selectedConnectors.map((item) => ({
421
+ name: item.name,
422
+ display_name: item.display_name,
423
+ icon: null,
424
+ iconSource: "account",
425
+ installed: true,
426
+ active: true,
427
+ status: "valid",
428
+ projected: false,
429
+ connector: true
430
+ }))
431
+ ],
432
+ [items, selectedConnectors]
433
+ );
434
+ const onSelectedChangeRef = useRef2(onSelectedChange);
435
+ onSelectedChangeRef.current = onSelectedChange;
436
+ const latestSelected = useRef2(selectedItems);
437
+ latestSelected.current = selectedItems;
438
+ const reportSelected = useCallback2(() => {
439
+ onSelectedChangeRef.current?.(latestSelected.current);
440
+ }, []);
441
+ const selectedNames = selectedItems.map((item) => item.name).join("\0");
442
+ useEffect2(reportSelected, [reportSelected, selectedNames]);
443
+ const failed = sessionId ? activation.listError : pendingError;
444
+ return /* @__PURE__ */ jsxs2("div", { className, children: [
445
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 border-[hsl(var(--border))] border-b px-3 py-2", children: [
446
+ /* @__PURE__ */ jsx2(Search, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]", "aria-hidden": "true" }),
447
+ /* @__PURE__ */ jsx2(
448
+ "input",
449
+ {
450
+ value: query,
451
+ onChange: (event) => setQuery(event.target.value),
452
+ placeholder: "\u641C\u7D22\u8FDE\u63A5\u5668",
453
+ "aria-label": "\u641C\u7D22\u8FDE\u63A5\u5668",
454
+ className: "min-w-0 flex-1 bg-transparent text-[11px] outline-none placeholder:text-[hsl(var(--muted-foreground))]"
455
+ }
456
+ )
457
+ ] }),
458
+ renderComputerSection ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
459
+ /* @__PURE__ */ jsx2(PanelTitle, { children: "\u7535\u8111" }),
460
+ renderComputerSection(query)
461
+ ] }) : null,
462
+ renderPhoneSection ? renderPhoneSection(query) : null,
463
+ /* @__PURE__ */ jsx2(PanelDivider, {}),
464
+ /* @__PURE__ */ jsx2(
465
+ AccountConnectors,
466
+ {
467
+ client,
468
+ query,
469
+ sessionId,
470
+ pending: selected,
471
+ onSelectedChange: setSelectedConnectors,
472
+ disabledReason: pendingDisabledReason,
473
+ onUse: async (name, label, enabled) => {
474
+ if (sessionId) {
475
+ const result = await client.sessions.setSessionPluginActivation(sessionId, name, enabled);
476
+ onPluginsChanged?.(result.plugin);
477
+ } else {
478
+ const kept = (pendingPlugins ?? []).filter((item) => item.name !== name);
479
+ onPendingPluginsChange?.(enabled ? [...kept, { name, displayName: label, icon: null, iconSource: "account" }] : kept);
480
+ }
481
+ }
482
+ }
483
+ ),
484
+ /* @__PURE__ */ jsx2(PanelDivider, {}),
485
+ /* @__PURE__ */ jsx2(PanelTitle, { children: "\u63D2\u4EF6" }),
486
+ !sessionId ? /* @__PURE__ */ jsx2("div", { className: "px-3 pb-0.5 text-[10px] text-[hsl(var(--muted-foreground))]", children: "\u5148\u9009\u4E2D\uFF0C\u53D1\u9001\u8FD9\u6761\u6D88\u606F\u65F6\u4F1A\u4E00\u5E76\u542F\u7528" }) : null,
487
+ (sessionId ? activation.loading : pendingLoading) && visibleItems.length === 0 ? /* @__PURE__ */ jsxs2("div", { className: "px-3 py-1.5 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
488
+ /* @__PURE__ */ jsx2(LoaderCircle, { size: 12, className: "mr-1 inline animate-spin" }),
489
+ "\u6B63\u5728\u83B7\u53D6\u63D2\u4EF6\u2026"
490
+ ] }) : null,
491
+ failed ? /* @__PURE__ */ jsx2(
492
+ "button",
493
+ {
494
+ type: "button",
495
+ onClick: () => sessionId ? reload() : void loadPendingCatalog(),
496
+ className: "px-3 py-1.5 text-left text-[11px] text-[hsl(var(--muted-foreground))] underline",
497
+ children: "\u63D2\u4EF6\u5217\u8868\u6682\u4E0D\u53EF\u7528\uFF0C\u70B9\u51FB\u91CD\u8BD5"
498
+ }
499
+ ) : null,
500
+ /* @__PURE__ */ jsx2(
501
+ PluginConnectorList,
502
+ {
503
+ client,
504
+ sessionId,
505
+ items: visibleItems.filter((item) => !isLocalConnectorName2(item.name)),
506
+ inFlight: activation.inFlight,
507
+ busy: sessionId ? activation.busy : pendingLoading,
508
+ mutationError: sessionId ? activation.mutationError : false,
509
+ onToggle: toggle,
510
+ onRetry: activation.retry,
511
+ onEditConfig: activation.editConfig,
512
+ configuring: sessionId ? activation.configuring : null,
513
+ onConfigSaved: activation.onConfigSaved,
514
+ onConfigClose: activation.onConfigClose,
515
+ disabledReason: sessionId ? null : pendingDisabledReason,
516
+ unavailableText: failed ? null : query.trim() ? "\u6CA1\u6709\u5339\u914D\u7684\u63D2\u4EF6" : null,
517
+ emptyText: sessionId ? "\u5C1A\u672A\u5B89\u88C5\u63D2\u4EF6" : "\u5C1A\u672A\u5B89\u88C5\u63D2\u4EF6\uFF0C\u53BB\u5E94\u7528\u5E02\u573A\u6DFB\u52A0\u4E00\u4E2A",
518
+ draft: !sessionId
519
+ }
520
+ ),
521
+ renderExternalAccounts ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
522
+ /* @__PURE__ */ jsx2(PanelDivider, {}),
523
+ /* @__PURE__ */ jsx2(PanelTitle, { children: "\u5916\u90E8\u8D26\u53F7" }),
524
+ renderExternalAccounts(query)
525
+ ] }) : null,
526
+ renderFooterActions ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
527
+ /* @__PURE__ */ jsx2(PanelDivider, {}),
528
+ renderFooterActions()
529
+ ] }) : null
530
+ ] });
531
+ }
532
+
533
+ // src/components/PluginTriggerStack.tsx
534
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
535
+ var AVATAR_SIZE = 20;
536
+ function hueOf(name) {
537
+ let hash = 0;
538
+ for (const char of name) hash = (hash * 31 + char.codePointAt(0)) % 1000003;
539
+ return hash % 360;
540
+ }
541
+ var AVATAR_PALETTE = [
542
+ "bg-violet-100 text-violet-800 dark:bg-violet-500/25 dark:text-violet-200",
543
+ "bg-sky-100 text-sky-800 dark:bg-sky-500/25 dark:text-sky-200",
544
+ "bg-emerald-100 text-emerald-800 dark:bg-emerald-500/25 dark:text-emerald-200",
545
+ "bg-amber-100 text-amber-800 dark:bg-amber-500/25 dark:text-amber-200",
546
+ "bg-rose-100 text-rose-800 dark:bg-rose-500/25 dark:text-rose-200",
547
+ "bg-indigo-100 text-indigo-800 dark:bg-indigo-500/25 dark:text-indigo-200",
548
+ "bg-teal-100 text-teal-800 dark:bg-teal-500/25 dark:text-teal-200",
549
+ "bg-fuchsia-100 text-fuchsia-800 dark:bg-fuchsia-500/25 dark:text-fuchsia-200"
550
+ ];
551
+ function PluginTriggerStack({ client, sessionId, summary, className }) {
552
+ if (summary.total === 0) return null;
553
+ const ring = "ring-2 ring-[hsl(var(--background))]";
554
+ return /* @__PURE__ */ jsxs3("span", { className: `flex items-center -space-x-1 ${className ?? ""}`, children: [
555
+ summary.visible.map((item) => {
556
+ const label = item.displayName?.trim() || item.name;
557
+ const tone = AVATAR_PALETTE[hueOf(item.name) % AVATAR_PALETTE.length];
558
+ return item.icon ? /* @__PURE__ */ jsx3(
559
+ SessionPluginIcon,
560
+ {
561
+ client,
562
+ sessionId: item.iconSource === "account" ? null : sessionId,
563
+ plugin: item,
564
+ size: AVATAR_SIZE,
565
+ shape: "circle",
566
+ className: ring
567
+ },
568
+ item.name
569
+ ) : /* @__PURE__ */ jsx3(
570
+ "span",
571
+ {
572
+ title: label,
573
+ className: `flex shrink-0 items-center justify-center rounded-full font-semibold text-[11px] ${tone} ${ring}`,
574
+ style: { width: AVATAR_SIZE, height: AVATAR_SIZE },
575
+ children: Array.from(label.trim())[0]?.toUpperCase() ?? "?"
576
+ },
577
+ item.name
578
+ );
579
+ }),
580
+ summary.overflow > 0 ? /* @__PURE__ */ jsxs3(
581
+ "span",
582
+ {
583
+ className: `flex shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] font-medium text-[hsl(var(--muted-foreground))] text-[10px] ${ring}`,
584
+ style: { width: AVATAR_SIZE, height: AVATAR_SIZE },
585
+ children: [
586
+ "+",
587
+ summary.overflow
588
+ ]
589
+ }
590
+ ) : null
591
+ ] });
592
+ }
593
+
594
+ export {
595
+ selectionFromItem,
596
+ TRIGGER_ICON_LIMIT,
597
+ triggerSummary,
598
+ AccountConnectors,
599
+ ConnectorPanel,
600
+ PluginTriggerStack
601
+ };
602
+ //# sourceMappingURL=chunk-2E6DQCXM.js.map