@blueking/chat-x 0.0.35 → 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.
@@ -23,8 +23,8 @@ AI 消息内容渲染的核心原子组件,集成代码高亮、LaTeX 公式
23
23
  ## 组件结构与渲染流程
24
24
 
25
25
  ```
26
- props.content → completeMarkdownSyntax → md.parse groupTokens → groupedTokens
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)→ containerScroll.toScrollBottom()
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
- `VNodeRenderer` 渲染的 HTML 统一经过 DOMPurify 过滤,并额外允许 KaTeX 所需标签:
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 数据模型一致的入口按钮
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "2.0.0",
3
- "generatedAt": "2026-05-12T09:32:40.415Z",
3
+ "generatedAt": "2026-05-14T09:46:52.773Z",
4
4
  "domains": {
5
5
  "message": {
6
6
  "label": "消息展示",
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 过滤 CSS 属性值,仅保留白名单中的安全属性,
3
+ * 并拦截 url()、expression()、javascript: 等危险模式。
4
+ *
5
+ * 注意:此函数是第二道防线。第一道由 DOMPurify 处理(过滤 script/event handler/javascript: URI 等)。
6
+ */
7
+ export declare const sanitizeCSS: (cssValue: string) => string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 轻量级 HTML 片段净化,不自动闭合标签。
3
+ * 用于流式渲染中 html_inline / html_block token 的净化——
4
+ * DOMPurify 会把孤立的 `<font color="red">` 补成 `<font color="red"></font>`,
5
+ * 导致后续内容无法继承样式,因此流式场景需要使用此函数。
6
+ */
7
+ export declare const sanitizeHtmlFragment: (html: string) => string;
@@ -1,3 +1,5 @@
1
+ export * from './css-sanitizer';
1
2
  export * from './file';
3
+ export * from './html-sanitizer';
2
4
  export * from './markdown-completer';
3
5
  export * from './utils';
@@ -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.35",
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.3"
81
+ "@blueking/chat-helper": "0.0.4"
82
82
  },
83
83
  "scripts": {
84
84
  "dev": "vite --config vite.config.ts",