@blueking/chat-x 0.0.49-beta.1 → 0.0.49-beta.11
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/constants.d.ts +1 -0
- package/dist/ag-ui/types/file.d.ts +1 -11
- package/dist/ag-ui/types/messages.d.ts +2 -0
- package/dist/components/chat-message/assistant-message/assistant-message.vue.d.ts +12 -1
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-file-card.vue.d.ts +0 -2
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/artifact-preview-host.vue.d.ts +6 -3
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/preview-strategy.d.ts +4 -5
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/code-preview.vue.d.ts +8 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/image-preview.vue.d.ts +7 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/use-artifact-preview-loader.d.ts +1 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue.d.ts +1 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/message-artifacts.vue.d.ts +0 -1
- package/dist/components/chat-message/interrupt-message/user-question/use-user-question.d.ts +7 -2
- package/dist/components/chat-message/user-message/user-message.vue.d.ts +15 -2
- package/dist/components/file-icon/file-icon.vue.d.ts +7 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/components/message-tools/message-time/format-message-time.d.ts +8 -0
- package/dist/components/message-tools/message-time/message-time.vue.d.ts +8 -0
- package/dist/components/message-tools/message-tools.vue.d.ts +11 -1
- package/dist/composables/use-artifact-preview.d.ts +8 -18
- package/dist/composables/use-container-scroll.d.ts +9 -2
- package/dist/composables/use-custom-tab.d.ts +7 -3
- package/dist/composables/use-global-config.d.ts +3 -0
- package/dist/composables/use-message-group.d.ts +145 -74
- package/dist/icons/file-icons.d.ts +4 -0
- package/dist/icons/file.d.ts +0 -18
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/tools.d.ts +3 -0
- package/dist/index.css +1 -1
- package/dist/index.js +2881 -2525
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +4 -1
- package/dist/mcp/generated/docs/assistant-message.md +28 -20
- package/dist/mcp/generated/docs/chat-container.md +36 -31
- package/dist/mcp/generated/docs/chat-input.md +2 -10
- package/dist/mcp/generated/docs/constants.md +3 -1
- package/dist/mcp/generated/docs/content-render.md +2 -10
- package/dist/mcp/generated/docs/execution-summary.md +3 -3
- package/dist/mcp/generated/docs/file-artifact-panel.md +60 -46
- package/dist/mcp/generated/docs/file-icon.md +111 -0
- package/dist/mcp/generated/docs/info-message.md +6 -5
- package/dist/mcp/generated/docs/interrupt.md +1 -0
- package/dist/mcp/generated/docs/message-container.md +27 -23
- package/dist/mcp/generated/docs/message-render.md +4 -27
- package/dist/mcp/generated/docs/message-time.md +180 -0
- package/dist/mcp/generated/docs/message-tools.md +47 -12
- package/dist/mcp/generated/docs/messages.md +9 -0
- package/dist/mcp/generated/docs/reasoning-message.md +2 -9
- package/dist/mcp/generated/docs/toolcall-render.md +18 -10
- package/dist/mcp/generated/docs/use-artifact-preview.md +52 -55
- package/dist/mcp/generated/docs/use-container-scroll.md +6 -2
- package/dist/mcp/generated/docs/use-custom-tab.md +25 -8
- package/dist/mcp/generated/docs/use-global-config.md +15 -5
- package/dist/mcp/generated/docs/use-message-group.md +21 -13
- package/dist/mcp/generated/docs/user-message.md +6 -0
- package/dist/mcp/generated/docs/user-question-card.md +6 -3
- package/dist/mcp/generated/index.json +68 -12
- package/dist/utils/file-type.d.ts +14 -0
- package/dist/utils/index.d.ts +1 -0
- package/package.json +2 -2
- package/dist/components/chat-message/assistant-message/message-artifacts/file-icon.d.ts +0 -29
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AI SUMMARY -->
|
|
2
2
|
## 快速了解
|
|
3
3
|
|
|
4
|
-
汇总当前会话所有 AssistantMessage 的 artifacts
|
|
4
|
+
汇总当前会话所有 AssistantMessage 的 artifacts(按 outputId 去重),支持关键词搜索、列表选中与下载; 预览区委托 ArtifactPreviewHost:由 resolveFileKind 把扩展名归入 code / markdown / html / text / image / binary 六类, 前四类走 text_from_download 拉正文(code 交给 highlight.js 高亮),image / binary 走 preview_url; download_url / preview_url 经 onArtifactClick 每次异步获取(无 URL 缓存,并发去重); 预览重载键为 outputId:type;重试再次调用 load() 重新取链。 源码位置:src/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue。
|
|
5
5
|
|
|
6
6
|
### 关联组件
|
|
7
7
|
- **assistant-message** — 文件产物来源于 AssistantMessage.property.artifacts
|
|
@@ -22,16 +22,17 @@
|
|
|
22
22
|
|
|
23
23
|
> **导出说明**:内部侧栏面板组件,**通常不直接使用**;由 `ChatContainer` 在「文件产物」Tab 内自动挂载。预览加载与渲染为同目录下 `artifact-preview/` 内部实现,不单独导出。
|
|
24
24
|
|
|
25
|
-
点击 AI 回复中的[文件卡片](/components/message/assistant-message)后,`ChatContainer` 侧栏会弹出固定的「文件产物」Tab,聚合展示当前会话**所有** `AssistantMessage`
|
|
25
|
+
点击 AI 回复中的[文件卡片](/components/message/assistant-message)后,`ChatContainer` 侧栏会弹出固定的「文件产物」Tab,聚合展示当前会话**所有** `AssistantMessage` 的文件产物(按 `outputId` 去重),并命中被点击的文件进行预览。
|
|
26
26
|
|
|
27
27
|
面板左侧为可搜索的文件列表,右侧为预览区。通常不需要直接使用,由 `ChatContainer` 在侧栏内自动渲染。
|
|
28
28
|
|
|
29
29
|
## 核心能力
|
|
30
30
|
|
|
31
|
-
- **会话级聚合**:拍平当前会话所有 `AssistantMessage.property.artifacts
|
|
32
|
-
-
|
|
31
|
+
- **会话级聚合**:拍平当前会话所有 `AssistantMessage.property.artifacts`,以 `outputId` 去重后统一在一个列表内展示
|
|
32
|
+
- **唯一命中**:以 `outputId` 作为会话内唯一键(同 `outputId` 视为同一文件);文件名可能重复,不可作唯一键
|
|
33
33
|
- **关键词搜索**:按文件名实时过滤列表
|
|
34
|
-
-
|
|
34
|
+
- **整块空态**:`artifacts` 为空时不渲染列表与预览区,整块展示 bkui `Exception`「暂无数据」(Tab 常驻,无数据也可正常打开侧栏)
|
|
35
|
+
- **异步取链**:`AIFileInfo` 本身不含 `url` / `previewUrl`,通过 `ChatContainer` 的 `onArtifactClick` 按 `outputId` 获取 `download_url` / `preview_url`(TTL 8 分钟缓存;预览重试会 `force` 刷新)
|
|
35
36
|
- **职责拆分**:
|
|
36
37
|
- **面板本身**:列表、搜索、预览头(文件名 / 图标 / 下载)
|
|
37
38
|
- **`ArtifactPreviewHost`**:按策略加载正文或预览 URL,分派到对应 renderer;展示 loading / empty / error(含重试)
|
|
@@ -54,12 +55,12 @@
|
|
|
54
55
|
|
|
55
56
|
<script setup lang="ts">
|
|
56
57
|
import { shallowRef } from 'vue'
|
|
57
|
-
import {
|
|
58
|
+
import { useArtifactPreviewProvider } from '@blueking/chat-x'
|
|
58
59
|
import type { AIFileInfo, SessionArtifact } from '@blueking/chat-x'
|
|
59
60
|
// 内部组件:仅文档 / 调试;业务请用 ChatContainer 自动挂载
|
|
60
61
|
import FileArtifactPanel from './message-artifacts/file-artifact-panel.vue'
|
|
61
62
|
|
|
62
|
-
const
|
|
63
|
+
const sessionArtifacts: SessionArtifact[] = [
|
|
63
64
|
{ name: '周报.html', outputId: 'a-html', size: 10240, type: 'html' },
|
|
64
65
|
{ name: '说明.md', outputId: 'a-md', size: 8192, type: 'md' },
|
|
65
66
|
{ name: '纪要.txt', outputId: 'a-txt', size: 4096, type: 'txt' },
|
|
@@ -67,12 +68,6 @@
|
|
|
67
68
|
{ name: '立项.pdf', outputId: 'a-pdf', size: 204800, type: 'pdf' },
|
|
68
69
|
]
|
|
69
70
|
|
|
70
|
-
const sessionArtifacts: SessionArtifact[] = files.map((file, index) => ({
|
|
71
|
-
...file,
|
|
72
|
-
artifactId: buildArtifactId('msg-1', index, file.outputId),
|
|
73
|
-
messageUid: 'msg-1',
|
|
74
|
-
}))
|
|
75
|
-
|
|
76
71
|
useArtifactPreviewProvider({
|
|
77
72
|
getOnArtifactClick: () => async file => {
|
|
78
73
|
// 文本类返回可 fetch 的 download_url;iframe 类返回 preview_url
|
|
@@ -82,7 +77,7 @@
|
|
|
82
77
|
onOpen: () => {},
|
|
83
78
|
})
|
|
84
79
|
|
|
85
|
-
const activeArtifactId = shallowRef(sessionArtifacts[0].
|
|
80
|
+
const activeArtifactId = shallowRef(sessionArtifacts[0].outputId)
|
|
86
81
|
const handleSelect = (id: string) => {
|
|
87
82
|
activeArtifactId.value = id
|
|
88
83
|
}
|
|
@@ -160,47 +155,55 @@
|
|
|
160
155
|
|
|
161
156
|
```
|
|
162
157
|
ArtifactFileCard(点击文件卡片)
|
|
163
|
-
└─ useArtifactPreviewConsumer().openPreview({ file
|
|
158
|
+
└─ useArtifactPreviewConsumer().openPreview({ file })
|
|
164
159
|
└─ useArtifactPreviewProvider(ChatContainer 内)
|
|
165
|
-
├─ 记录命中文件 activeArtifactId
|
|
160
|
+
├─ 记录命中文件 activeArtifactId = file.outputId
|
|
166
161
|
└─ onOpen → addCustomTab('file-artifact') 展开并选中侧栏 Tab
|
|
167
162
|
└─ FileArtifactPanel
|
|
168
|
-
├─ 列表 @select → setActiveArtifactId
|
|
163
|
+
├─ 列表 @select → setActiveArtifactId(outputId)
|
|
169
164
|
├─ 下载 → resolveArtifactUrls + triggerArtifactDownload
|
|
170
165
|
└─ ArtifactPreviewHost
|
|
171
166
|
├─ useArtifactPreviewLoader(策略 + fetch / 取链,防竞态)
|
|
172
167
|
└─ HtmlPreview | MarkdownPreview | TxtPreview | UrlIframePreview
|
|
168
|
+
|
|
169
|
+
容器初始化
|
|
170
|
+
└─ ensureCustomTab('file-artifact') 常驻挂上(不展开侧栏);因 order:-1 排在首位,
|
|
171
|
+
未主动切换过 Tab 时会成为默认选中面板;无产物时由面板展示整块空态
|
|
173
172
|
```
|
|
174
173
|
|
|
175
174
|
- 文件卡片通过 `useArtifactPreviewConsumer` 注入预览上下文,无 Provider 时卡片不可点击(兜底 `undefined`)
|
|
176
175
|
- `ChatContainer` 通过 `useArtifactPreviewProvider` 提供上下文,并把「打开侧栏 Tab」这一副作用以 `onOpen` 注入,保持 composable 职责单一
|
|
177
|
-
- 侧栏「文件产物」Tab 固定不可关闭,`order: -1`
|
|
176
|
+
- 侧栏「文件产物」Tab 固定不可关闭,`order: -1` 排在「执行情况」之前;**常驻不随产物有无增删**,无产物时由面板展示整块空态
|
|
178
177
|
|
|
179
|
-
##
|
|
178
|
+
## 唯一键规则
|
|
180
179
|
|
|
181
|
-
|
|
180
|
+
会话内以 **`outputId`** 作为文件产物唯一键:
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
- 同一 `outputId` 在多条消息中出现时,聚合列表去重并保留最后一次出现的文件信息
|
|
183
|
+
- `activeId`、列表 `:key`、`select` 事件参数均使用 `outputId`
|
|
184
|
+
- 文件名可能重复,**不可**作为唯一键
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
buildArtifactId('msg-a', 2, 'output-9'); // => 'msg-a#2#output-9'
|
|
188
|
-
```
|
|
186
|
+
## 预览机制
|
|
189
187
|
|
|
190
|
-
|
|
188
|
+
预览分两步:先由 `resolveFileKind(type, name)`(`src/utils/file-type.ts`)把扩展名归入六个**分类**,再由 `getArtifactPreviewStrategy` 查表得到 **加载方式** 与 **渲染器**。面板与 Host 都不写死具体扩展名分支,后台新增文件类型时只需在分类表里补一行。
|
|
191
189
|
|
|
192
|
-
|
|
190
|
+
| 分类 | 覆盖扩展名 | load | 取链字段 | renderer |
|
|
191
|
+
| ---- | ---------- | ---- | -------- | -------- |
|
|
192
|
+
| `code` | `py` `js` `mjs` `cjs` `ts` `tsx` `jsx` `vue` `go` `rs` `rb` `java` `kt` `swift` `c` `h` `cpp` `hpp` `cs` `php` `lua` `r` `scala` `dart` `sh` `bash` `zsh` `ps1` `sql` `css` `scss` `less` `json` `jsonc` `yaml` `yml` `toml` `ini` `cfg` `conf` `env` `xml` `tex` `Dockerfile` `Makefile` `gitignore` `dockerignore` `editorconfig` | `text_from_download` | `download_url` → `fetch` 正文 | `CodePreview`(highlight.js 高亮) |
|
|
193
|
+
| `markdown` | `md` `markdown` | `text_from_download` | 同上 | `MarkdownPreview`(`MarkdownContent`) |
|
|
194
|
+
| `html` | `html` `htm` | `text_from_download` | 同上 | `HtmlPreview`(`<iframe srcdoc>`) |
|
|
195
|
+
| `text` | `txt` `rst` | `text_from_download` | 同上 | `TxtPreview`(`<pre>`) |
|
|
196
|
+
| `image` | `png` `jpg` `jpeg` `svg` | `preview_url` | `preview_url` | `ImagePreview`(`<img>`,`object-fit: contain`) |
|
|
197
|
+
| `binary` | `pdf` `docx` `xlsx` `xlsm` `xls` `pptx` `csv` `tsv`,以及**所有未登记的扩展名** | `preview_url` | `preview_url` | `UrlIframePreview`(`<iframe src>`,一般为后台转好的 PDF) |
|
|
193
198
|
|
|
194
|
-
|
|
199
|
+
关于类型解析:
|
|
195
200
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
| `txt` / `json` | `text_from_download` | 同上 | `TxtPreview`(`<pre>`) |
|
|
201
|
-
| 其余(如 `pdf` / `jpg`) | `preview_url_iframe` | `preview_url` | `UrlIframePreview`(`<iframe src>`,一般为后台转好的 PDF) |
|
|
201
|
+
- `AIFileInfo.type` 为**扩展名字符串**(如 `'pdf'` / `'py'`)或无扩展名的文件名(如 `'Dockerfile'`),大小写不敏感
|
|
202
|
+
- `type` 缺省时回退 `name` 推断;`报告.final.xlsx` 取 `xlsx`,`.gitignore` 取 `gitignore`
|
|
203
|
+
- `md` 为后台扩展名别名,与 `markdown` 等价,共用 Markdown 直渲染
|
|
204
|
+
- 未登记的扩展名一律落入 `binary` 走后台预览,前端不会因为新类型报错
|
|
202
205
|
|
|
203
|
-
|
|
206
|
+
`CodePreview` 的语言由扩展名映射到 highlight.js(`vue → xml`、`tsx → typescript`、`env / cfg / conf → ini` 等,其余交给 hljs 自身别名表,识别不了则按 `plaintext` 转义输出)。单文件超过 300KB 时跳过高亮直接转义,避免同步解析阻塞主线程。
|
|
204
207
|
|
|
205
208
|
### 加载态
|
|
206
209
|
|
|
@@ -211,7 +214,14 @@ Provider 侧聚合与文件卡片侧透传必须使用同一规则,保证命
|
|
|
211
214
|
| `empty` | 「暂无可预览的文件」(无文件 / 未传 `onArtifactClick` / 缺所需 URL) |
|
|
212
215
|
| `error` | 「预览加载失败」+ 重试按钮 |
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
### 重载与取链约定
|
|
218
|
+
|
|
219
|
+
- **重载键**:`ArtifactPreviewHost` 以 `` `${outputId}:${type}` `` 监听文件变化;`outputId` 或 `type` 任一变化会重新 `load()`,仅改文件名等其它字段不会
|
|
220
|
+
- **取链**:`resolveArtifactUrls(file)` 每次重新调用 `onArtifactClick`;同文件进行中的请求会复用(并发去重)
|
|
221
|
+
- **重试**:错误态点击重试再次走 `load()`,重新取链并加载
|
|
222
|
+
- **竞态**:切换文件时 `useArtifactPreviewLoader` 用 `loadSeq` + `AbortController` 中断上一次 `fetch`,避免过期结果覆盖最新内容
|
|
223
|
+
|
|
224
|
+
下载图标仍由面板用 bkui `Loading` spin 单独表达。
|
|
215
225
|
|
|
216
226
|
## 内部结构(不导出)
|
|
217
227
|
|
|
@@ -220,29 +230,33 @@ message-artifacts/
|
|
|
220
230
|
├── file-artifact-panel.vue # 列表 + 下载头 + 挂载 Host
|
|
221
231
|
└── artifact-preview/
|
|
222
232
|
├── artifact-preview-host.vue # 状态机 UI + 分派 renderer
|
|
223
|
-
├── preview-strategy.ts # getArtifactPreviewStrategy
|
|
233
|
+
├── preview-strategy.ts # getArtifactPreviewStrategy(分类 → 策略查表)
|
|
224
234
|
├── use-artifact-preview-loader.ts
|
|
225
235
|
└── renderers/
|
|
236
|
+
├── code-preview.vue
|
|
226
237
|
├── html-preview.vue
|
|
238
|
+
├── image-preview.vue
|
|
227
239
|
├── markdown-preview.vue
|
|
228
240
|
├── txt-preview.vue
|
|
229
241
|
└── url-iframe-preview.vue
|
|
230
242
|
```
|
|
231
243
|
|
|
244
|
+
分类表与扩展名归一化在 `src/utils/file-type.ts`(导出 `AIFileKind` / `resolveFileKind` / `normalizeFileExtension`),与[文件图标](/components/helper/file-icon)共用同一份解析入口。
|
|
245
|
+
|
|
232
246
|
## API
|
|
233
247
|
|
|
234
248
|
### Props
|
|
235
249
|
|
|
236
250
|
| 属性名 | 类型 | 必填 | 说明 |
|
|
237
251
|
| --------- | ------------------- | ---- | -------------------------------------- |
|
|
238
|
-
| activeId | `string` | ✓ | 当前命中的文件
|
|
239
|
-
| artifacts | `SessionArtifact[]` | ✓ |
|
|
252
|
+
| activeId | `string` | ✓ | 当前命中的文件 `outputId` |
|
|
253
|
+
| artifacts | `SessionArtifact[]` | ✓ | 当前会话全部文件产物(已按 `outputId` 去重) |
|
|
240
254
|
|
|
241
255
|
### Events
|
|
242
256
|
|
|
243
257
|
| 事件名 | 参数 | 说明 |
|
|
244
258
|
| ------ | ----------------- | -------------------------- |
|
|
245
|
-
| select | `(id: string)` | 列表内切换选中文件,参数为文件 `
|
|
259
|
+
| select | `(id: string)` | 列表内切换选中文件,参数为文件 `outputId` |
|
|
246
260
|
|
|
247
261
|
### Slots / Expose
|
|
248
262
|
|
|
@@ -253,20 +267,20 @@ message-artifacts/
|
|
|
253
267
|
```typescript
|
|
254
268
|
import type { AIFileInfo, SessionArtifact } from '@blueking/chat-x';
|
|
255
269
|
|
|
256
|
-
//
|
|
257
|
-
type SessionArtifact = AIFileInfo
|
|
258
|
-
artifactId: string; // 全局唯一 id:messageUid#index#outputId
|
|
259
|
-
messageUid: string; // 所属 AssistantMessage 的 uid
|
|
260
|
-
};
|
|
270
|
+
// 会话级文件产物:拍平去重后即为 AIFileInfo
|
|
271
|
+
type SessionArtifact = AIFileInfo;
|
|
261
272
|
|
|
262
273
|
type AIFileInfo = {
|
|
263
274
|
name: string;
|
|
264
275
|
outputId: string;
|
|
265
276
|
size: number;
|
|
266
|
-
|
|
277
|
+
/** 扩展名(如 'pdf' / 'py')或无扩展名文件名(如 'Dockerfile'),大小写不敏感 */
|
|
278
|
+
type: string;
|
|
267
279
|
};
|
|
268
280
|
```
|
|
269
281
|
|
|
282
|
+
> **破坏性变更**:原 `AIFileType` 枚举已移除,`AIFileInfo.type` 改为 `string`。此前写 `type: AIFileType.Pdf` 的代码改为 `type: 'pdf'` 即可;枚举成员的值与新字符串一一对应,运行时数据无需迁移。
|
|
283
|
+
|
|
270
284
|
## 关联 Composable
|
|
271
285
|
|
|
272
286
|
预览**命中与取链**由 [useArtifactPreview](/composables/use-artifact-preview) 提供(Provider / Consumer + `ARTIFACT_PREVIEW_TOKEN`)。**正文加载与渲染**由内部 `useArtifactPreviewLoader` + `ArtifactPreviewHost` 完成,不在该 composable 内。
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!-- AI SUMMARY -->
|
|
2
|
+
## 快速了解
|
|
3
|
+
|
|
4
|
+
按扩展名渲染文件类型图标:内联 svg,尺寸用 1em 跟随外层 font-size; 扩展名解析走 normalizeFileExtension(优先 fileType,缺省回退 fileName,大小写不敏感, 支持 Dockerfile / .gitignore 这类无扩展名或点号开头的文件);未登记类型返回 unknown 兜底图标。 源码位置:src/components/file-icon/file-icon.vue,图标注册表在 src/icons/file-icons.ts。
|
|
5
|
+
|
|
6
|
+
### 关联组件
|
|
7
|
+
- **file-artifact-panel** — 文件产物列表与预览头使用该组件展示类型图标
|
|
8
|
+
- **assistant-message** — 消息内的文件产物卡片使用该组件
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
<!-- FULL DOC -->
|
|
12
|
+
|
|
13
|
+
# FileIcon 文件类型图标
|
|
14
|
+
|
|
15
|
+
> **能力域**:辅助能力
|
|
16
|
+
|
|
17
|
+
`FileIcon` 按文件扩展名渲染对应的类型图标,用于文件列表、附件卡片、产物预览头等需要「一眼看出这是什么文件」的场景。图标以内联 svg 形式打进包内,不产生额外网络请求,也不需要消费方配置静态资源路径。
|
|
18
|
+
|
|
19
|
+
## 源码事实
|
|
20
|
+
|
|
21
|
+
- **源码位置**:`src/components/file-icon/file-icon.vue`
|
|
22
|
+
- **图标注册表**:`src/icons/file-icons.ts`(导出 `getFileIconSvg` / `UNKNOWN_FILE_ICON_SVG`)
|
|
23
|
+
- **扩展名解析**:`src/utils/file-type.ts` 的 `normalizeFileExtension`
|
|
24
|
+
|
|
25
|
+
## 核心能力
|
|
26
|
+
|
|
27
|
+
- **扩展名驱动**:优先取 `fileType`,缺省时回退 `fileName` 推断;大小写不敏感
|
|
28
|
+
- **特殊文件名**:`Dockerfile` / `Makefile` 这类无扩展名文件,以及 `.gitignore` / `.editorconfig` 这类点号开头的文件都能正确命中
|
|
29
|
+
- **多扩展名共用图标**:如 `xlsx` / `xls` / `csv` / `tsv` 共用表格图标,`tsx` / `jsx` 共用 React 图标
|
|
30
|
+
- **兜底不报错**:未登记的扩展名返回 `unknown` 图标,后台新增文件类型时前端不会缺图
|
|
31
|
+
- **尺寸自适应**:内部 svg 固定为 `1em`,直接用外层 `font-size` 控制大小
|
|
32
|
+
|
|
33
|
+
## 基础用法
|
|
34
|
+
|
|
35
|
+
```vue
|
|
36
|
+
<template>
|
|
37
|
+
<FileIcon file-type="pdf" />
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { FileIcon } from '@blueking/chat-x';
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**渲染效果**
|
|
46
|
+
|
|
47
|
+
## 从文件名推断
|
|
48
|
+
|
|
49
|
+
后台未下发 `type` 时传 `fileName` 即可,组件会取最后一段扩展名:
|
|
50
|
+
|
|
51
|
+
```vue
|
|
52
|
+
<template>
|
|
53
|
+
<!-- 取 xlsx -->
|
|
54
|
+
<FileIcon file-name="季度报告.final.xlsx" />
|
|
55
|
+
<!-- 取 gitignore -->
|
|
56
|
+
<FileIcon file-name=".gitignore" />
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
两者同时传入时以 `fileType` 优先。
|
|
61
|
+
|
|
62
|
+
## 控制尺寸
|
|
63
|
+
|
|
64
|
+
图标宽高为 `1em`,用外层 `font-size` 控制即可,无需改 svg:
|
|
65
|
+
|
|
66
|
+
```vue
|
|
67
|
+
<template>
|
|
68
|
+
<span style="font-size: 16px"><FileIcon file-type="py" /></span>
|
|
69
|
+
<span style="font-size: 32px"><FileIcon file-type="py" /></span>
|
|
70
|
+
</template>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## API
|
|
74
|
+
|
|
75
|
+
### Props
|
|
76
|
+
|
|
77
|
+
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|
|
78
|
+
| -------- | -------- | ---- | ----------- | -------------------------------------------------------- |
|
|
79
|
+
| fileType | `string` | 否 | `undefined` | 文件类型:扩展名(如 `pdf` / `py`)或无扩展名文件名(如 `Dockerfile`) |
|
|
80
|
+
| fileName | `string` | 否 | `undefined` | 文件名,`fileType` 缺省时用于推断扩展名 |
|
|
81
|
+
|
|
82
|
+
两者都不传时渲染兜底图标。
|
|
83
|
+
|
|
84
|
+
### Emits / Slots / Expose
|
|
85
|
+
|
|
86
|
+
- 无。
|
|
87
|
+
|
|
88
|
+
## 图标覆盖范围
|
|
89
|
+
|
|
90
|
+
| 图标 | 覆盖扩展名 |
|
|
91
|
+
| ---- | ---------- |
|
|
92
|
+
| 文档类 | `pptx` / `docx` / `pdf` / `txt` / `rst` / `md` / `markdown` / `tex` |
|
|
93
|
+
| 表格类 | `xlsx` / `xlsm` / `xls` / `csv` / `tsv` |
|
|
94
|
+
| 前端 | `html` / `htm` / `css` / `scss` / `less` / `js` / `mjs` / `cjs` / `ts` / `tsx` / `jsx` / `vue` / `xml` |
|
|
95
|
+
| 后端 / 系统 | `py` / `go` / `rs` / `rb` / `java` / `kt` / `swift` / `c` / `h` / `cpp` / `hpp` / `cs` / `php` / `lua` / `r` / `scala` / `dart` / `sql` / `sh` / `bash` / `zsh` / `ps1` |
|
|
96
|
+
| 配置 | `json` / `jsonc` / `yaml` / `yml` / `toml` / `ini` / `cfg` / `conf` / `env` / `editorconfig` / `Makefile` |
|
|
97
|
+
| 工具链 | `Dockerfile` / `dockerignore` / `gitignore` |
|
|
98
|
+
| 图片 | `png` / `jpg` / `jpeg` / `svg` |
|
|
99
|
+
| 兜底 | 以上之外的所有类型 |
|
|
100
|
+
|
|
101
|
+
新增类型时在 `src/icons/file-icons.ts` 的 `FILE_ICON_GROUPS` 里补一行即可;对应 svg 需先放进 `src/svgs/` 并按需 `?raw` 引入(只引实际用到的,避免把整个图标库打进产物)。
|
|
102
|
+
|
|
103
|
+
## 使用建议
|
|
104
|
+
|
|
105
|
+
- 图标颜色由 svg 自带,不继承 `currentColor`,不要试图用 `color` 覆盖
|
|
106
|
+
- 需要与文件名同行展示时,给父容器设 `display: flex` + `gap`,组件本身已是 `inline-flex` 且 `flex-shrink: 0`
|
|
107
|
+
|
|
108
|
+
## 关联组件
|
|
109
|
+
|
|
110
|
+
- [FileArtifactPanel](../message/file-artifact-panel.md) — 文件产物列表与预览头使用该组件。
|
|
111
|
+
- [AssistantMessage](../message/assistant-message.md) — 消息内文件产物卡片的图标来源。
|
|
@@ -20,16 +20,17 @@
|
|
|
20
20
|
|
|
21
21
|
> **导出说明**:`InfoMessage` **未**从包入口导出(入口同名是 TS interface)。消费方经 `MessageRender` / `MessageContainer` 使用。下文 `InfoMessageComp` 为文档站内部示例。
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
系统信息分隔组件,在聊天消息列表中以**左右虚线夹中文案**的形式展示非对话类信息(会话重置、时间节点、状态变更等)。
|
|
24
24
|
|
|
25
25
|
## 视觉原理
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
根节点 `.ai-info-message` 为横向 flex:两侧通过 `::before` / `::after` 拉伸出虚线(主题边框色),中间 `.ai-info-message-body` 承载文案(主题次要文案色)。整块正常占位,避免旧实现 `height: 0` 导致内容被裁切遮挡:
|
|
28
28
|
|
|
29
29
|
```
|
|
30
|
-
|
|
30
|
+
─────── 以下是新的对话 ───────
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
多行时,多条 `.ai-info-message-content` 在 body 内纵向排列(`gap: 4px`),两侧虚线仍夹住整块文案区域。
|
|
33
34
|
## 基础用法
|
|
34
35
|
|
|
35
36
|
`content` 传入字符串,渲染单行分隔信息:
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
|
|
57
58
|
## 多行信息
|
|
58
59
|
|
|
59
|
-
`content`
|
|
60
|
+
`content` 传入字符串数组时,每个元素渲染为一行居中文案,在中间 body 内纵向排列,两侧虚线夹住整块区域:
|
|
60
61
|
|
|
61
62
|
```vue
|
|
62
63
|
<template>
|
|
@@ -134,7 +135,7 @@
|
|
|
134
135
|
|
|
135
136
|
| 属性名 | 类型 | 说明 |
|
|
136
137
|
| --------- | -------------------- | ------------------------------------------------------------------ |
|
|
137
|
-
| content | `string \| string[]` |
|
|
138
|
+
| content | `string \| string[]` | 信息内容。字符串渲染单行;数组在中间区域纵向多行展示,两侧虚线夹住整块 |
|
|
138
139
|
| id | `number \| string` | 消息 ID(接收但不使用,由 MessageContainer 管理) |
|
|
139
140
|
| messageId | `number \| string` | 消息唯一标识(接收但不使用) |
|
|
140
141
|
| status | `MessageStatus` | 消息状态(接收但不使用,组件无状态相关渲染逻辑) |
|
|
@@ -138,6 +138,7 @@ type UserQuestionOptionItem = {
|
|
|
138
138
|
- 前端会为每道**选择题**追加 `label: 'others'` 的自由输入项;后端无需重复下发该选项。
|
|
139
139
|
- 当用户选择 Others 时,`answer[].description` 为用户输入文本。
|
|
140
140
|
- 业务可通过 `UserQuestionCard` 的 `#question` slot 渲染自定义表单;作答有效时调用 `setAnswer` 回传 `UserQuestionAnswerItem`,无效时传 `undefined`。
|
|
141
|
+
- UI 一次只展示一题:标题栏 `< 当前题 / 总题数 >` 切换;单选预设选项作答后自动跳下一题,多选 / Others 需手动切换(协议字段不变)。
|
|
141
142
|
|
|
142
143
|
## Interrupt
|
|
143
144
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
- **消息分组**:将连续的非用户消息合并为一组,每组共享一个工具栏
|
|
27
27
|
- **Tool 消息关联**:自动将 `role: 'tool'` 消息注入到对应 Assistant 消息的 toolCall 中
|
|
28
28
|
- **Loading 自动注入**:末尾为用户消息时,自动追加 Loading 动画组
|
|
29
|
-
- **滚动管理**:`messageStatus` 为流式、等待响应或请求中(`streaming` / `pending` / `fetching`)时显示「停止生成」,离开底部时显示「返回底部」;`renderMode` 为 `Share`
|
|
29
|
+
- **滚动管理**:`messageStatus` 为流式、等待响应或请求中(`streaming` / `pending` / `fetching`)时显示「停止生成」,离开底部时显示「返回底部」;`renderMode` 为 `Share` 时不显示「停止生成」。挂载时通过 `jumpToBottom()` 瞬时贴底,避免切换会话时从顶部平滑滚到底部的动画
|
|
30
30
|
- **多选模式**:支持按消息组勾选,用户消息与 AI 回复联动选中
|
|
31
31
|
|
|
32
32
|
## 基础用法
|
|
@@ -124,10 +124,29 @@
|
|
|
124
124
|
- `renderMode === RenderMode.Share`(分享预览模式)
|
|
125
125
|
- 消息组的 `pause` 为 `true`(来源于 `message.property?.extra?.pause`)
|
|
126
126
|
- 多选模式(`enableSelection`)开启且消息组不是 Loading 类型
|
|
127
|
+
- AI 消息组的时间通过 `MessageTools` 的 `#append` 插槽渲染在工具图标右侧,取值为组内**最后一条**带 `createdAt` 的消息(即本轮回答完成时间);组内 `reasoning` / `activity` 等子消息不单独展示时间,全组都没有 `createdAt` 时不展示
|
|
127
128
|
- `renderMode === RenderMode.Test` 时,工具栏会过滤掉「分享」按钮,其余正常
|
|
128
129
|
- `renderMode === RenderMode.Share` 时,`message-group-messages` 自动添加 `message-group-enabled-selection` 类名(与 `enableSelection: true` 一致的多选视觉效果)
|
|
129
130
|
- Loading 消息组的 `type` 是 `MessageRole.Loading`,不显示工具栏和多选 Checkbox
|
|
130
131
|
|
|
132
|
+
## DOM 定位标识
|
|
133
|
+
|
|
134
|
+
为方便业务方通过 `document.querySelector` 定位消息(埋点、自动化测试、外部滚动锚定等),渲染结构上固定输出两层标识:
|
|
135
|
+
|
|
136
|
+
| 层级 | 选择器 | 值 |
|
|
137
|
+
| -------- | ------------------------------------- | --------------------------------------------------- |
|
|
138
|
+
| 消息组 | `.message-group[data-message-group-id]` | `MessageGroup.uid`(与外层 `id` 同值) |
|
|
139
|
+
| 单条消息 | `.ai-message-item[data-message-id]` | `message.id`,缺失时回退 `message.uid`;两者都无则不输出该属性 |
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
// 定位某条消息
|
|
143
|
+
document.querySelector('[data-message-id="123"]');
|
|
144
|
+
// 定位某个消息组下的全部消息
|
|
145
|
+
document.querySelectorAll('[data-message-group-id="xxx"] [data-message-id]');
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`.ai-message-item` 是 `MessageContainer` 统一包裹的容器,`#default` 插槽自定义渲染的消息同样被它包裹,因此无论用默认 `MessageRender` 还是自定义渲染,标识都一致存在。
|
|
149
|
+
|
|
131
150
|
## 等待响应(Loading 自动注入)
|
|
132
151
|
|
|
133
152
|
当 `messages` 末尾为 `role: 'user'` 时,自动追加 Loading 消息组,展示 AI 正在处理的加载动画(`renderMode` 为 `Share` 时不追加,且 `MessageContainer` 会过滤 Loading 组):
|
|
@@ -533,10 +552,11 @@ AI 回复状态为 `error` 时,消息以错误样式展示:
|
|
|
533
552
|
| 按钮 | 显示条件 | 点击行为 |
|
|
534
553
|
| ------------ | ---------------------------------------------------------------------------------------------- | ---------------------- |
|
|
535
554
|
| 「停止生成」 | `messageStatus` 为 `streaming`、`pending`、`fetching` 或 `stop-loading`(停止中 loading 态),且 `renderMode` 不为 `Share` | 触发 `@stop-streaming` |
|
|
536
|
-
| 「返回底部」 | `debouncedShowScrollBottomBtn`(距底部 > 100px,且防抖 300ms 后才显示/隐藏) |
|
|
555
|
+
| 「返回底部」 | `debouncedShowScrollBottomBtn`(距底部 > 100px,且防抖 300ms 后才显示/隐藏) | 平滑滚动到消息列表底部(显式 `toScrollBottom('smooth')`) |
|
|
537
556
|
|
|
538
557
|
> **防抖说明**:「返回底部」按钮的显隐使用 300ms 防抖,避免快速滚动时按钮频繁闪烁。隐藏时立即生效(无防抖),显示时延迟 300ms。
|
|
539
558
|
|
|
559
|
+
> **首屏 / 切换会话贴底**:`MessageContainer` 挂载时若已有消息组,会立即调用 `jumpToBottom()`,并在下一帧再补一次,避免历史消息渲染过程中出现「从顶部滚到底部」的动画。流式输出场景下的小幅跟随仍由 markdown 挂载触发的 `toScrollBottom()`(距底较近时走 smooth)完成。
|
|
540
560
|
## API
|
|
541
561
|
|
|
542
562
|
### Props
|
|
@@ -613,32 +633,16 @@ enum MessageToolsStatus {
|
|
|
613
633
|
Hidden = 'hidden',
|
|
614
634
|
}
|
|
615
635
|
|
|
616
|
-
// 消息角色
|
|
617
|
-
|
|
618
|
-
User = 'user',
|
|
619
|
-
Assistant = 'assistant',
|
|
620
|
-
Tool = 'tool',
|
|
621
|
-
Reasoning = 'reasoning',
|
|
622
|
-
Activity = 'activity',
|
|
623
|
-
Info = 'info',
|
|
624
|
-
Interrupt = 'interrupt',
|
|
625
|
-
Loading = 'loading',
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// 消息状态
|
|
629
|
-
enum MessageStatus {
|
|
630
|
-
Pending = 'pending',
|
|
631
|
-
Streaming = 'streaming',
|
|
632
|
-
Complete = 'complete',
|
|
633
|
-
Error = 'error',
|
|
634
|
-
Stop = 'stop',
|
|
635
|
-
Disabled = 'disabled',
|
|
636
|
-
}
|
|
636
|
+
// 消息角色 / 消息状态完整枚举见常量文档,勿在此维护副本
|
|
637
|
+
// MessageRole、MessageStatus → ../../types/constants
|
|
637
638
|
```
|
|
638
639
|
|
|
640
|
+
> `MessageRole` / `MessageStatus` 完整取值见 [常量枚举](../../types/constants)。
|
|
641
|
+
|
|
639
642
|
## 关联组件
|
|
640
643
|
|
|
641
644
|
- [MessageRender](/components/message/message-render) — 按组渲染每条消息时委托使用
|
|
645
|
+
- [MessageTime](/components/feedback/message-time) — AI 消息组工具栏右侧的时间
|
|
642
646
|
- [InterruptMessage 中断消息](/components/agent/interrupt-message) — `role: 'interrupt'` 的渲染与 `onInterruptResume` 透传
|
|
643
647
|
- [ChatInput](/components/input/chat-input) — 常与输入区组合构成完整对话界面
|
|
644
648
|
- [LoadingMessage](/components/message/loading-message) — 末尾为用户消息时自动追加加载组
|
|
@@ -315,35 +315,12 @@ h(ContentRender, { content: message.content || '', status: message.status }, /*
|
|
|
315
315
|
```typescript
|
|
316
316
|
import { MessageRole, MessageStatus, MessageToolsStatus, type Message, type IToolBtn } from '@blueking/chat-x';
|
|
317
317
|
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
User = 'user',
|
|
321
|
-
Assistant = 'assistant',
|
|
322
|
-
Info = 'info',
|
|
323
|
-
Reasoning = 'reasoning',
|
|
324
|
-
Tool = 'tool',
|
|
325
|
-
Activity = 'activity',
|
|
326
|
-
Loading = 'loading',
|
|
327
|
-
Interrupt = 'interrupt',
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// 消息状态
|
|
331
|
-
enum MessageStatus {
|
|
332
|
-
Pending = 'pending',
|
|
333
|
-
Streaming = 'streaming',
|
|
334
|
-
Complete = 'complete',
|
|
335
|
-
Error = 'error',
|
|
336
|
-
Stop = 'stop',
|
|
337
|
-
Disabled = 'disabled',
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// 工具按钮状态(仅转发给 UserMessage)
|
|
341
|
-
enum MessageToolsStatus {
|
|
342
|
-
Disabled = 'disabled',
|
|
343
|
-
Hidden = 'hidden',
|
|
344
|
-
}
|
|
318
|
+
// MessageRole / MessageStatus 完整枚举见 ../../types/constants
|
|
319
|
+
// MessageToolsStatus:Disabled | Hidden(仅转发给 UserMessage)
|
|
345
320
|
```
|
|
346
321
|
|
|
322
|
+
> `MessageRole` / `MessageStatus` 完整取值见 [常量枚举](../../types/constants)。
|
|
323
|
+
|
|
347
324
|
## 关联组件
|
|
348
325
|
|
|
349
326
|
- [MessageContainer](/components/setup/message-container) — 内部按组调用以渲染每条消息
|