@blueking/chat-x 0.0.34 → 0.0.36
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/dist/ag-ui/types/contents.d.ts +12 -11
- package/dist/composables/use-message-group.d.ts +3 -3
- package/dist/index.js +1939 -1873
- package/dist/index.js.map +1 -1
- package/dist/mcp/generated/docs/activity-message.md +67 -61
- package/dist/mcp/generated/docs/markdown-content.md +25 -7
- package/dist/mcp/generated/docs/shortcut-render.md +4 -0
- package/dist/mcp/generated/docs/use-message-group.md +1 -1
- package/dist/mcp/generated/index.json +1 -1
- package/dist/utils/css-sanitizer.d.ts +7 -0
- package/dist/utils/html-sanitizer.d.ts +7 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/tokens-to-vnodes.d.ts +6 -0
- package/package.json +2 -2
|
@@ -151,12 +151,12 @@ ActivityMessage 展示活动类消息:知识检索(knowledge_rag)、引用
|
|
|
151
151
|
|
|
152
152
|
## FlowAgent 执行情况模式
|
|
153
153
|
|
|
154
|
-
`activityType` 设为 `MessageContentType.FlowAgent`(`'flow_agent'`),`content` 传入 `BkFlowMessageContent`
|
|
154
|
+
`activityType` 设为 `MessageContentType.FlowAgent`(`'flow_agent'`),`content` 传入 `BkFlowMessageContent` 任务数组。用于展示一个或多个蓝鲸标准运维(BkFlow)任务的执行状态、节点列表和统计信息。
|
|
155
155
|
|
|
156
156
|
### 核心交互
|
|
157
157
|
|
|
158
|
-
- **标题栏**:显示「执行情况」+
|
|
159
|
-
-
|
|
158
|
+
- **标题栏**:显示「执行情况」+ 所有任务聚合后的各状态计数(执行中 / 成功 / 失败 / 挂起),颜色区分
|
|
159
|
+
- **任务组**:逐个展示任务行,带状态图标和总耗时
|
|
160
160
|
- **节点列表**:每个节点显示状态圆点、名称和耗时;hover 时出现「详情」按钮
|
|
161
161
|
- **节点详情**:点击「详情」按钮会通过 `useCustomTabConsumer` 在 `ChatContainer` 侧边栏新增自定义 Tab,展示节点配置(基础信息、输入参数、输出参数)
|
|
162
162
|
|
|
@@ -171,7 +171,7 @@ FlowAgentContent(activityType = 'flow_agent')
|
|
|
171
171
|
│ ├── Loading / ArrowIcon(随 status 切换)
|
|
172
172
|
│ └── 执行情况:执行中 N / 成功 N / 失败 N / 挂起 N
|
|
173
173
|
└── #default
|
|
174
|
-
└── TaskGroup
|
|
174
|
+
└── TaskGroup × N
|
|
175
175
|
├── TaskHeader(可折叠/展开)
|
|
176
176
|
│ ├── 状态图标(running=Loading / success / failed / suspended)
|
|
177
177
|
│ ├── task_name(HighlightKeyword 支持搜索高亮)
|
|
@@ -204,54 +204,56 @@ FlowAgentContent(activityType = 'flow_agent')
|
|
|
204
204
|
const collapsed = ref(false);
|
|
205
205
|
const status = ref(MessageStatus.Complete);
|
|
206
206
|
|
|
207
|
-
const flowContent: BkFlowMessageContent =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
nodes: {
|
|
217
|
-
node1: {
|
|
218
|
-
id: 'node1',
|
|
219
|
-
name: '数据拉取',
|
|
220
|
-
state: 'FINISHED',
|
|
221
|
-
elapsed_time: 12,
|
|
222
|
-
start_time: '2025-01-01 10:00:00',
|
|
223
|
-
finish_time: '2025-01-01 10:00:12',
|
|
224
|
-
loop: 1,
|
|
225
|
-
retry: 0,
|
|
226
|
-
skip: false,
|
|
227
|
-
type: 'ServiceActivity',
|
|
228
|
-
},
|
|
229
|
-
node2: {
|
|
230
|
-
id: 'node2',
|
|
231
|
-
name: '数据转换',
|
|
232
|
-
state: 'FINISHED',
|
|
233
|
-
elapsed_time: 45,
|
|
234
|
-
start_time: '2025-01-01 10:00:12',
|
|
235
|
-
finish_time: '2025-01-01 10:00:57',
|
|
236
|
-
loop: 1,
|
|
237
|
-
retry: 0,
|
|
238
|
-
skip: false,
|
|
239
|
-
type: 'ServiceActivity',
|
|
207
|
+
const flowContent: BkFlowMessageContent = [
|
|
208
|
+
{
|
|
209
|
+
task_id: 100,
|
|
210
|
+
task_name: '数据清洗流程',
|
|
211
|
+
task_state: 'FINISHED',
|
|
212
|
+
task_outputs: [],
|
|
213
|
+
statistics: {
|
|
214
|
+
total: 3,
|
|
215
|
+
state_counts: { FINISHED: 2, FAILED: 1 },
|
|
240
216
|
},
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
217
|
+
nodes: {
|
|
218
|
+
node1: {
|
|
219
|
+
id: 'node1',
|
|
220
|
+
name: '数据拉取',
|
|
221
|
+
state: 'FINISHED',
|
|
222
|
+
elapsed_time: 12,
|
|
223
|
+
start_time: '2025-01-01 10:00:00',
|
|
224
|
+
finish_time: '2025-01-01 10:00:12',
|
|
225
|
+
loop: 1,
|
|
226
|
+
retry: 0,
|
|
227
|
+
skip: false,
|
|
228
|
+
type: 'ServiceActivity',
|
|
229
|
+
},
|
|
230
|
+
node2: {
|
|
231
|
+
id: 'node2',
|
|
232
|
+
name: '数据转换',
|
|
233
|
+
state: 'FINISHED',
|
|
234
|
+
elapsed_time: 45,
|
|
235
|
+
start_time: '2025-01-01 10:00:12',
|
|
236
|
+
finish_time: '2025-01-01 10:00:57',
|
|
237
|
+
loop: 1,
|
|
238
|
+
retry: 0,
|
|
239
|
+
skip: false,
|
|
240
|
+
type: 'ServiceActivity',
|
|
241
|
+
},
|
|
242
|
+
node3: {
|
|
243
|
+
id: 'node3',
|
|
244
|
+
name: '结果写入',
|
|
245
|
+
state: 'FAILED',
|
|
246
|
+
elapsed_time: 3,
|
|
247
|
+
start_time: '2025-01-01 10:00:57',
|
|
248
|
+
finish_time: '2025-01-01 10:01:00',
|
|
249
|
+
loop: 1,
|
|
250
|
+
retry: 0,
|
|
251
|
+
skip: false,
|
|
252
|
+
type: 'ServiceActivity',
|
|
253
|
+
},
|
|
252
254
|
},
|
|
253
255
|
},
|
|
254
|
-
|
|
256
|
+
];
|
|
255
257
|
</script>
|
|
256
258
|
```
|
|
257
259
|
|
|
@@ -264,16 +266,18 @@ const messages = [
|
|
|
264
266
|
role: 'activity',
|
|
265
267
|
activityType: MessageContentType.FlowAgent, // 'flow_agent'
|
|
266
268
|
status: MessageStatus.Streaming,
|
|
267
|
-
content:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
task_id: 100,
|
|
272
|
+
task_name: '数据清洗流程',
|
|
273
|
+
task_state: 'RUNNING',
|
|
274
|
+
task_outputs: [],
|
|
275
|
+
statistics: { total: 3, state_counts: { RUNNING: 1, FINISHED: 2 } },
|
|
276
|
+
nodes: {
|
|
277
|
+
/* ... */
|
|
278
|
+
},
|
|
275
279
|
},
|
|
276
|
-
|
|
280
|
+
],
|
|
277
281
|
},
|
|
278
282
|
];
|
|
279
283
|
```
|
|
@@ -301,14 +305,16 @@ const messages = [
|
|
|
301
305
|
### 相关类型定义
|
|
302
306
|
|
|
303
307
|
```typescript
|
|
304
|
-
import { MessageContentType, type BkFlowMessageContent, type BkFlowNode } from '@blueking/chat-x';
|
|
308
|
+
import { MessageContentType, type BkFlowMessageContent, type BkFlowNode, type BkFlowTask } from '@blueking/chat-x';
|
|
309
|
+
|
|
310
|
+
type BkFlowMessageContent = BkFlowTask[];
|
|
305
311
|
|
|
306
|
-
type
|
|
312
|
+
type BkFlowTask = {
|
|
307
313
|
nodes: Record<string, BkFlowNode>;
|
|
308
314
|
statistics: { state_counts: Record<string, number>; total: number };
|
|
309
315
|
task_id: number;
|
|
310
316
|
task_name: string;
|
|
311
|
-
task_outputs: unknown
|
|
317
|
+
task_outputs: unknown;
|
|
312
318
|
task_state: string;
|
|
313
319
|
};
|
|
314
320
|
|
|
@@ -23,8 +23,8 @@ AI 消息内容渲染的核心原子组件,集成代码高亮、LaTeX 公式
|
|
|
23
23
|
## 组件结构与渲染流程
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
props.content → completeMarkdownSyntax → md.parse
|
|
27
|
-
|
|
26
|
+
props.content → completeMarkdownSyntax → md.parse(html: true)→ groupTokens → groupedTokens
|
|
27
|
+
│
|
|
28
28
|
div.ai-markdown-content(contain: layout style)
|
|
29
29
|
│
|
|
30
30
|
status === 'error' → CommonErrorContent(:content)
|
|
@@ -39,12 +39,21 @@ props.content → completeMarkdownSyntax → md.parse → groupTokens → groupe
|
|
|
39
39
|
↓ ↓ ↓ ↓
|
|
40
40
|
MermaidContent LatexContent CodeContent VNodeRenderer
|
|
41
41
|
@mounted @mounted @mounted @vue:mounted
|
|
42
|
-
│
|
|
43
|
-
└──── handleTokenMounted(throttle 100ms
|
|
42
|
+
│ │
|
|
43
|
+
└──── handleTokenMounted(throttle 100ms) │
|
|
44
|
+
→ containerScroll.toScrollBottom() │
|
|
45
|
+
sanitize(DOMPurify + sanitizeCSS)
|
|
46
|
+
sanitizeHtmlFragment(流式片段净化)
|
|
44
47
|
```
|
|
45
48
|
|
|
46
49
|
`VNodeRenderer` 的 `options` 中包含与当前 `MarkdownIt` 实例一致的 `mditOptions`(即 `md.options`),以便 `tokensToVNodes` 调用 `renderer.rules` 时第三参与 markdown-it 原生规则签名一致。
|
|
47
50
|
|
|
51
|
+
`MarkdownIt` 构造时开启 `html: true`,允许行内 HTML 标签(如 `<font>`、`<div>`)通过 markdown-it 解析为 `html_inline` / `html_block` token。
|
|
52
|
+
|
|
53
|
+
两组净化函数各司其职:
|
|
54
|
+
- `sanitize`:DOMPurify 全局过滤后,再对 `style` 属性值进行 CSS 属性白名单校验(`sanitizeCSS`),用于最终渲染的完整 HTML。
|
|
55
|
+
- `sanitizeHtmlFragment`:轻量级片段净化,**不自动闭合标签**,专用于流式渲染中拆分到多个 token 的 HTML 标签场景。
|
|
56
|
+
|
|
48
57
|
### Token 分组(groupTokens)
|
|
49
58
|
|
|
50
59
|
`groupTokens` 使用栈将扁平 Token 数组转为分组数组,每组对应一个顶层 DOM 节点(段落、标题、列表、代码块等):
|
|
@@ -189,15 +198,24 @@ props.content → completeMarkdownSyntax → md.parse → groupTokens → groupe
|
|
|
189
198
|
|
|
190
199
|
### 安全性
|
|
191
200
|
|
|
192
|
-
|
|
201
|
+
HTML 渲染采用两层安全策略:
|
|
202
|
+
|
|
203
|
+
**第一层 — DOMPurify**:全局过滤,移除危险标签和属性,同时允许 KaTeX 和 `<font>` 标签所需的内容:
|
|
193
204
|
|
|
194
205
|
```typescript
|
|
195
206
|
const domPurifyConfig = {
|
|
196
|
-
ADD_TAGS: ['semantics', 'mrow', 'mi', 'mo', 'mn', 'msup', 'msub', 'mfrac', 'mtext', 'annotation'],
|
|
197
|
-
ADD_ATTR: ['xmlns', 'mathvariant', 'encoding', 'style'],
|
|
207
|
+
ADD_TAGS: ['font', 'semantics', 'mrow', 'mi', 'mo', 'mn', 'msup', 'msub', 'mfrac', 'mtext', 'annotation'],
|
|
208
|
+
ADD_ATTR: ['xmlns', 'mathvariant', 'encoding', 'style', 'color', 'size', 'face'],
|
|
209
|
+
FORBID_TAGS: ['style'],
|
|
198
210
|
};
|
|
199
211
|
```
|
|
200
212
|
|
|
213
|
+
> `FORBID_TAGS: ['style']` 防止 `<style>` 标签注入全局 CSS,样式属性通过 `style` 属性(attribute)而非 `<style>` 标签(tag)控制。
|
|
214
|
+
|
|
215
|
+
**第二层 — CSS 属性白名单**(`sanitizeCSS`):DOMPurify 处理后,对 `style` 属性值做二次过滤,仅保留白名单内的安全 CSS 属性,拦截 `url()`、`expression()`、`javascript:` 等危险模式。
|
|
216
|
+
|
|
217
|
+
**流式场景 — `sanitizeHtmlFragment`**:流式渲染中 HTML 标签可能被拆分到多个 `html_inline` token(如 `<font color="red">` 和 `</font>` 分属不同 token),DOMPurify 会自动闭合未匹配标签导致样式丢失。`sanitizeHtmlFragment` 只做危险模式匹配不自动闭合,专用于此场景。
|
|
218
|
+
|
|
201
219
|
> `CodeContent`、`MermaidContent`、`LatexContent` 各自内部处理安全性(KaTeX `errorColor`、highlight.js 转义等),不经过 DOMPurify。
|
|
202
220
|
|
|
203
221
|
## 关联组件
|
|
@@ -411,6 +411,10 @@ interface BaseShortcutComponent {
|
|
|
411
411
|
}
|
|
412
412
|
```
|
|
413
413
|
|
|
414
|
+
## 样式说明
|
|
415
|
+
|
|
416
|
+
组件内部使用 SCSS 变量 `$bk-prefix` 拼接 bkui-vue 组件类名(如 `Form`、`FormItem`、`Radio`、`Checkbox` 等),替代原先硬编码的 `.bk-*` 类名选择器,确保在不同构建环境下类名前缀的一致性。
|
|
417
|
+
|
|
414
418
|
## 关联组件
|
|
415
419
|
|
|
416
420
|
- [ShortcutBtn](../atomic/shortcut-btn.md) — 与 Shortcut 数据模型一致的入口按钮
|
|
@@ -111,7 +111,7 @@ const isExecutionMessage = (m: Message): boolean => {
|
|
|
111
111
|
| 消息类型 | 搜索范围 |
|
|
112
112
|
| ---------- | ------------------------------------------------------------ |
|
|
113
113
|
| toolCall | `function.name`、`mcpName`、`description`、`arguments`、`id` |
|
|
114
|
-
| flow_agent | `task_name`、各节点 `name`
|
|
114
|
+
| flow_agent | 各任务 `task_name`、各节点 `name` |
|
|
115
115
|
|
|
116
116
|
## 分享模式
|
|
117
117
|
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export interface TokenToVNodeOptions {
|
|
|
17
17
|
* HTML 净化函数,用于处理 innerHTML 的内容
|
|
18
18
|
*/
|
|
19
19
|
sanitize?: (html: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* HTML 片段净化函数,用于流式场景下 html_inline/html_block token 的净化。
|
|
22
|
+
* 与 sanitize 不同,此函数不会自动闭合未匹配的标签,
|
|
23
|
+
* 因为流式渲染中 HTML 标签可能被拆分到不同的 token 中。
|
|
24
|
+
*/
|
|
25
|
+
sanitizeHtmlFragment?: (html: string) => string;
|
|
20
26
|
}
|
|
21
27
|
type TokenAttrs = [string, string][];
|
|
22
28
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blueking/chat-x",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.36",
|
|
4
4
|
"description": "蓝鲸智云 AI Chat 组件库 —— 遵循 AG-UI,为 AI Agent 和人类开发者共同设计的对话 UI 组件库。",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"vitepress": "2.0.0-alpha.16",
|
|
79
79
|
"vitest": "^4.0.18",
|
|
80
80
|
"vue-tsc": "^3.1.4",
|
|
81
|
-
"@blueking/chat-helper": "0.0.
|
|
81
|
+
"@blueking/chat-helper": "0.0.4"
|
|
82
82
|
},
|
|
83
83
|
"scripts": {
|
|
84
84
|
"dev": "vite --config vite.config.ts",
|