@blueking/chat-x 0.0.50 → 0.0.51-beta.2

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 (147) hide show
  1. package/dist/ag-ui/types/contents.d.ts +2 -0
  2. package/dist/ag-ui/types/messages.d.ts +5 -0
  3. package/dist/common/constants.d.ts +1 -1
  4. package/dist/components/ai-buttons/file-upload-btn/file-upload-btn.vue.d.ts +0 -2
  5. package/dist/components/chat-content/file-content/file-content.vue.d.ts +5 -2
  6. package/dist/components/chat-content/file-content/upload-file-item.vue.d.ts +12 -0
  7. package/dist/components/chat-content/file-content/upload-image-item.vue.d.ts +21 -0
  8. package/dist/components/chat-input/ai-slash-editor/ai-slash-editor.vue.d.ts +1 -1
  9. package/dist/components/chat-input/build-default-placeholder.d.ts +7 -0
  10. package/dist/components/chat-input/chat-input.vue.d.ts +1 -1
  11. package/dist/components/chat-message/message-container/message-container.vue.d.ts +1 -0
  12. package/dist/components/chat-message/message-render/message-render.vue.d.ts +2 -0
  13. package/dist/components/chat-message/user-message/user-message.vue.d.ts +3 -1
  14. package/dist/components/index.d.ts +2 -1
  15. package/dist/components/message-tools/message-time/format-message-time.d.ts +8 -0
  16. package/dist/components/message-tools/message-time/message-time.vue.d.ts +8 -0
  17. package/dist/components/message-tools/message-tools.vue.d.ts +11 -1
  18. package/dist/composables/use-custom-tab.d.ts +5 -3
  19. package/dist/composables/use-global-config.d.ts +3 -0
  20. package/dist/composables/use-message-group.d.ts +150 -72
  21. package/dist/icons/execution.d.ts +6 -0
  22. package/dist/icons/tools.d.ts +3 -0
  23. package/dist/index.css +1 -1
  24. package/dist/index.js +3124 -2837
  25. package/dist/index.js.map +1 -1
  26. package/dist/lang/lang.d.ts +8 -7
  27. package/dist/mcp/generated/docs/ai-slash-input.md +2 -0
  28. package/dist/mcp/generated/docs/assistant-message.md +9 -7
  29. package/dist/mcp/generated/docs/chat-container.md +38 -32
  30. package/dist/mcp/generated/docs/chat-input.md +18 -12
  31. package/dist/mcp/generated/docs/cite-content.md +3 -3
  32. package/dist/mcp/generated/docs/desc-panel.md +32 -10
  33. package/dist/mcp/generated/docs/execution-summary.md +3 -3
  34. package/dist/mcp/generated/docs/file-artifact-panel.md +6 -4
  35. package/dist/mcp/generated/docs/file-content.md +89 -73
  36. package/dist/mcp/generated/docs/file-upload-btn.md +16 -18
  37. package/dist/mcp/generated/docs/message-container.md +3 -0
  38. package/dist/mcp/generated/docs/message-render.md +2 -1
  39. package/dist/mcp/generated/docs/message-time.md +180 -0
  40. package/dist/mcp/generated/docs/message-tools.md +47 -12
  41. package/dist/mcp/generated/docs/messages.md +9 -0
  42. package/dist/mcp/generated/docs/toolcall-render.md +82 -43
  43. package/dist/mcp/generated/docs/use-artifact-preview.md +19 -17
  44. package/dist/mcp/generated/docs/use-custom-tab.md +12 -8
  45. package/dist/mcp/generated/docs/use-global-config.md +15 -5
  46. package/dist/mcp/generated/docs/user-message.md +9 -0
  47. package/dist/mcp/generated/docs/user-question-card.md +2 -0
  48. package/dist/mcp/generated/index.json +46 -6
  49. package/dist/types/input.d.ts +6 -0
  50. package/dist/utils/file.d.ts +7 -1
  51. package/dist/utils/index.d.ts +2 -0
  52. package/dist/utils/merge-tools-by-id.d.ts +6 -0
  53. package/dist/utils/upload-file.d.ts +35 -0
  54. package/package.json +3 -2
  55. package/skills/blueking-chat-x/SKILL.md +139 -0
  56. package/skills/blueking-chat-x/references/_index.md +131 -0
  57. package/skills/blueking-chat-x/references/components/activity-layout.md +134 -0
  58. package/skills/blueking-chat-x/references/components/activity-message.md +486 -0
  59. package/skills/blueking-chat-x/references/components/ai-image.md +230 -0
  60. package/skills/blueking-chat-x/references/components/ai-loading.md +131 -0
  61. package/skills/blueking-chat-x/references/components/ai-prompt-list.md +44 -0
  62. package/skills/blueking-chat-x/references/components/ai-selection.md +439 -0
  63. package/skills/blueking-chat-x/references/components/ai-skill-list.md +73 -0
  64. package/skills/blueking-chat-x/references/components/ai-slash-editor.md +43 -0
  65. package/skills/blueking-chat-x/references/components/ai-slash-input.md +56 -0
  66. package/skills/blueking-chat-x/references/components/ai-slash-menu.md +42 -0
  67. package/skills/blueking-chat-x/references/components/animation-text.md +202 -0
  68. package/skills/blueking-chat-x/references/components/assistant-message.md +498 -0
  69. package/skills/blueking-chat-x/references/components/chat-container.md +869 -0
  70. package/skills/blueking-chat-x/references/components/chat-input.md +769 -0
  71. package/skills/blueking-chat-x/references/components/cite-content.md +142 -0
  72. package/skills/blueking-chat-x/references/components/code-content.md +211 -0
  73. package/skills/blueking-chat-x/references/components/common-error-content.md +73 -0
  74. package/skills/blueking-chat-x/references/components/content-render.md +233 -0
  75. package/skills/blueking-chat-x/references/components/delete-tool.md +191 -0
  76. package/skills/blueking-chat-x/references/components/desc-panel.md +162 -0
  77. package/skills/blueking-chat-x/references/components/detail-section.md +91 -0
  78. package/skills/blueking-chat-x/references/components/execution-summary.md +128 -0
  79. package/skills/blueking-chat-x/references/components/file-artifact-panel.md +289 -0
  80. package/skills/blueking-chat-x/references/components/file-content.md +319 -0
  81. package/skills/blueking-chat-x/references/components/file-icon.md +109 -0
  82. package/skills/blueking-chat-x/references/components/file-upload-btn.md +159 -0
  83. package/skills/blueking-chat-x/references/components/flow-agent-content.md +264 -0
  84. package/skills/blueking-chat-x/references/components/flow-agent-node-detail.md +236 -0
  85. package/skills/blueking-chat-x/references/components/highlight-keyword.md +146 -0
  86. package/skills/blueking-chat-x/references/components/image-content.md +182 -0
  87. package/skills/blueking-chat-x/references/components/image-preview-group.md +184 -0
  88. package/skills/blueking-chat-x/references/components/image-preview.md +226 -0
  89. package/skills/blueking-chat-x/references/components/info-message.md +144 -0
  90. package/skills/blueking-chat-x/references/components/input-attachment.md +49 -0
  91. package/skills/blueking-chat-x/references/components/input-info-alert.md +42 -0
  92. package/skills/blueking-chat-x/references/components/interrupt-message.md +212 -0
  93. package/skills/blueking-chat-x/references/components/key-value-content.md +128 -0
  94. package/skills/blueking-chat-x/references/components/knowledge-rag-content.md +122 -0
  95. package/skills/blueking-chat-x/references/components/latex-content.md +200 -0
  96. package/skills/blueking-chat-x/references/components/loading-message.md +192 -0
  97. package/skills/blueking-chat-x/references/components/markdown-content.md +232 -0
  98. package/skills/blueking-chat-x/references/components/mermaid-content.md +189 -0
  99. package/skills/blueking-chat-x/references/components/message-container.md +645 -0
  100. package/skills/blueking-chat-x/references/components/message-loading.md +118 -0
  101. package/skills/blueking-chat-x/references/components/message-render.md +327 -0
  102. package/skills/blueking-chat-x/references/components/message-time.md +177 -0
  103. package/skills/blueking-chat-x/references/components/message-tools.md +416 -0
  104. package/skills/blueking-chat-x/references/components/model-selector.md +155 -0
  105. package/skills/blueking-chat-x/references/components/preview-toolbar.md +42 -0
  106. package/skills/blueking-chat-x/references/components/questions-container.md +85 -0
  107. package/skills/blueking-chat-x/references/components/reasoning-message.md +232 -0
  108. package/skills/blueking-chat-x/references/components/reference-content.md +135 -0
  109. package/skills/blueking-chat-x/references/components/reference-doc-content.md +109 -0
  110. package/skills/blueking-chat-x/references/components/scroll-btn.md +159 -0
  111. package/skills/blueking-chat-x/references/components/selection-footer.md +78 -0
  112. package/skills/blueking-chat-x/references/components/selection-question.md +88 -0
  113. package/skills/blueking-chat-x/references/components/shortcut-btn.md +204 -0
  114. package/skills/blueking-chat-x/references/components/shortcut-btns.md +266 -0
  115. package/skills/blueking-chat-x/references/components/shortcut-render.md +424 -0
  116. package/skills/blueking-chat-x/references/components/simple-table.md +101 -0
  117. package/skills/blueking-chat-x/references/components/text-content.md +77 -0
  118. package/skills/blueking-chat-x/references/components/tool-approval-card.md +183 -0
  119. package/skills/blueking-chat-x/references/components/tool-btn.md +317 -0
  120. package/skills/blueking-chat-x/references/components/tool-message.md +235 -0
  121. package/skills/blueking-chat-x/references/components/toolcall-render.md +348 -0
  122. package/skills/blueking-chat-x/references/components/user-feedback.md +233 -0
  123. package/skills/blueking-chat-x/references/components/user-message.md +424 -0
  124. package/skills/blueking-chat-x/references/components/user-question-answered-card.md +104 -0
  125. package/skills/blueking-chat-x/references/components/user-question-card.md +231 -0
  126. package/skills/blueking-chat-x/references/components/user-question-choice.md +105 -0
  127. package/skills/blueking-chat-x/references/components/user-question-option.md +42 -0
  128. package/skills/blueking-chat-x/references/components/vnode-renderer.md +122 -0
  129. package/skills/blueking-chat-x/references/composables/use-animation-text.md +196 -0
  130. package/skills/blueking-chat-x/references/composables/use-artifact-preview.md +231 -0
  131. package/skills/blueking-chat-x/references/composables/use-clipboard.md +203 -0
  132. package/skills/blueking-chat-x/references/composables/use-command-selection.md +150 -0
  133. package/skills/blueking-chat-x/references/composables/use-container-scroll.md +57 -0
  134. package/skills/blueking-chat-x/references/composables/use-custom-tab.md +158 -0
  135. package/skills/blueking-chat-x/references/composables/use-flow-node-actions.md +157 -0
  136. package/skills/blueking-chat-x/references/composables/use-full-screen.md +112 -0
  137. package/skills/blueking-chat-x/references/composables/use-global-config.md +148 -0
  138. package/skills/blueking-chat-x/references/composables/use-menu-keydown.md +163 -0
  139. package/skills/blueking-chat-x/references/composables/use-message-group.md +247 -0
  140. package/skills/blueking-chat-x/references/composables/use-observer-visible-list.md +188 -0
  141. package/skills/blueking-chat-x/references/composables/use-parent-scrolling.md +46 -0
  142. package/skills/blueking-chat-x/references/theme/theme.md +431 -0
  143. package/skills/blueking-chat-x/references/types/constants.md +307 -0
  144. package/skills/blueking-chat-x/references/types/interrupt.md +379 -0
  145. package/skills/blueking-chat-x/references/types/messages.md +553 -0
  146. package/skills/blueking-chat-x/references/types/schema.md +91 -0
  147. package/skills/blueking-chat-x/scripts/generate-references.mjs +314 -0
@@ -0,0 +1,289 @@
1
+ # FileArtifactPanel 文件产物预览
2
+
3
+ > 能力域:消息系统 | 导入:`import { FileArtifactPanel } from '@blueking/chat-x'` | since 0.0.20
4
+
5
+ 汇总当前会话所有 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。
6
+
7
+ **关联**:assistant-message(文件产物来源于 AssistantMessage.property.artifacts)、chat-container(面板挂载在侧栏「文件产物」Tab(固定、不可关闭),并通过 onArtifactClick 异步取链)、execution-summary(同为 ChatContainer 侧栏 Tab 面板,交互形态一致)、message-loading(ArtifactPreviewHost 取链 / 拉取正文过程使用 MessageLoading)
8
+
9
+ ---
10
+
11
+ # FileArtifactPanel 文件产物预览
12
+
13
+ ## 源码事实
14
+
15
+ - **源码位置**:`src/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue`
16
+ - **能力域**:消息系统
17
+ - **能力说明**:汇总当前会话全部文件产物;左侧列表搜索与选中,右侧预览委托内部 `ArtifactPreviewHost`。
18
+
19
+ > **导出说明**:内部侧栏面板组件,**通常不直接使用**;由 `ChatContainer` 在「文件产物」Tab 内自动挂载。预览加载与渲染为同目录下 `artifact-preview/` 内部实现,不单独导出。
20
+
21
+ 点击 AI 回复中的[文件卡片](/components/message/assistant-message)后,`ChatContainer` 侧栏会弹出固定的「文件产物」Tab,聚合展示当前会话**所有** `AssistantMessage` 的文件产物(按 `outputId` 去重),并命中被点击的文件进行预览。
22
+
23
+ 面板左侧为可搜索的文件列表,右侧为预览区。通常不需要直接使用,由 `ChatContainer` 在侧栏内自动渲染。
24
+
25
+ ## 核心能力
26
+
27
+ - **会话级聚合**:拍平当前会话所有 `AssistantMessage.property.artifacts`,以 `outputId` 去重后统一在一个列表内展示
28
+ - **唯一命中**:以 `outputId` 作为会话内唯一键(同 `outputId` 视为同一文件);文件名可能重复,不可作唯一键
29
+ - **关键词搜索**:按文件名实时过滤列表
30
+ - **整块空态**:`artifacts` 为空时不渲染列表与预览区,整块展示 bkui `Exception`「暂无数据」(Tab 常驻,无数据也可正常打开侧栏)
31
+ - **异步取链**:`AIFileInfo` 本身不含 `url` / `previewUrl`,通过 `ChatContainer` 的 `onArtifactClick` 按 `outputId` 获取 `download_url` / `preview_url`(TTL 8 分钟缓存;预览重试会 `force` 刷新)
32
+ - **职责拆分**:
33
+ - **面板本身**:列表、搜索、预览头(文件名 / 图标 / 下载)
34
+ - **`ArtifactPreviewHost`**:按策略加载正文或预览 URL,分派到对应 renderer;展示 loading / empty / error(含重试)
35
+ - **未传 `onArtifactClick`**:下载按钮隐藏,预览区展示无数据
36
+
37
+ ## 基础用法
38
+
39
+ 面板**未从包入口导出**,业务侧请走下方「业务接入」;下列示例仅用于文档站 / 本地调试(与 `ExecutionSummary` 文档站写法一致:相对路径引入 + 自行挂 Provider)。
40
+
41
+ ```vue
42
+ <template>
43
+ <div style="height: 480px; border: 1px solid #dcdee5; border-radius: 8px; overflow: hidden;">
44
+ <FileArtifactPanel
45
+ :active-id="activeArtifactId"
46
+ :artifacts="sessionArtifacts"
47
+ @select="handleSelect"
48
+ />
49
+ </div>
50
+ </template>
51
+
52
+ <script setup lang="ts">
53
+ import { shallowRef } from 'vue'
54
+ import { useArtifactPreviewProvider } from '@blueking/chat-x'
55
+ import type { AIFileInfo, SessionArtifact } from '@blueking/chat-x'
56
+ // 内部组件:仅文档 / 调试;业务请用 ChatContainer 自动挂载
57
+ import FileArtifactPanel from './message-artifacts/file-artifact-panel.vue'
58
+
59
+ const sessionArtifacts: SessionArtifact[] = [
60
+ { name: '周报.html', outputId: 'a-html', size: 10240, type: 'html' },
61
+ { name: '说明.md', outputId: 'a-md', size: 8192, type: 'md' },
62
+ { name: '纪要.txt', outputId: 'a-txt', size: 4096, type: 'txt' },
63
+ { name: '配置.json', outputId: 'a-json', size: 2048, type: 'json' },
64
+ { name: '立项.pdf', outputId: 'a-pdf', size: 204800, type: 'pdf' },
65
+ ]
66
+
67
+ useArtifactPreviewProvider({
68
+ getOnArtifactClick: () => async file => {
69
+ // 文本类返回可 fetch 的 download_url;iframe 类返回 preview_url
70
+ const res = await api.getArtifactUrls(file.outputId)
71
+ return { download_url: res.download_url, preview_url: res.preview_url }
72
+ },
73
+ onOpen: () => {},
74
+ })
75
+
76
+ const activeArtifactId = shallowRef(sessionArtifacts[0].outputId)
77
+ const handleSelect = (id: string) => {
78
+ activeArtifactId.value = id
79
+ }
80
+ </script>
81
+ ```
82
+
83
+ **渲染效果**(点击左侧列表切换类型;文本类直渲染,PDF 走 iframe。Mock 取链约 600ms)
84
+
85
+ ## 业务接入(ChatContainer)
86
+
87
+ 日常用法是给容器传 `messages`(含 `property.artifacts`)与 `onArtifactClick`,点击文件卡片即可打开侧栏面板:
88
+
89
+ ```vue
90
+ <template>
91
+ <ChatContainer
92
+ v-model="input"
93
+ :messages="messages"
94
+ :on-artifact-click="onArtifactClick"
95
+ @send-message="handleSend"
96
+ />
97
+ </template>
98
+
99
+ <script setup lang="ts">
100
+ import { ref, shallowRef } from 'vue'
101
+ import {
102
+ ChatContainer,
103
+ MessageRole,
104
+ MessageStatus,
105
+ type AIFileInfo,
106
+ type Message,
107
+ } from '@blueking/chat-x'
108
+
109
+ const input = ref('')
110
+ const messages = shallowRef<Message[]>([
111
+ {
112
+ id: 'u1',
113
+ messageId: 'u1',
114
+ role: MessageRole.User,
115
+ status: MessageStatus.Complete,
116
+ content: '整理本周评审材料',
117
+ },
118
+ {
119
+ id: 'a1',
120
+ messageId: 'a1',
121
+ uid: 'assistant-uid-1',
122
+ role: MessageRole.Assistant,
123
+ status: MessageStatus.Complete,
124
+ content: '已生成评审材料,点击卡片可在侧栏预览:',
125
+ property: {
126
+ artifacts: [
127
+ { name: '周报.html', outputId: 'a-html', size: 10240, type: 'html' },
128
+ { name: '说明.md', outputId: 'a-md', size: 8192, type: 'md' },
129
+ { name: '配置.json', outputId: 'a-json', size: 2048, type: 'json' },
130
+ { name: '立项.pdf', outputId: 'a-pdf', size: 204800, type: 'pdf' },
131
+ ] satisfies AIFileInfo[],
132
+ },
133
+ },
134
+ ])
135
+
136
+ const onArtifactClick = async (file: AIFileInfo) => {
137
+ const res = await api.getArtifactUrls(file.outputId)
138
+ return {
139
+ download_url: res.download_url,
140
+ preview_url: res.preview_url,
141
+ }
142
+ }
143
+
144
+ const handleSend = () => {
145
+ /* ... */
146
+ }
147
+ </script>
148
+ ```
149
+
150
+ ## 触发链路
151
+
152
+ ```
153
+ ArtifactFileCard(点击文件卡片)
154
+ └─ useArtifactPreviewConsumer().openPreview({ file })
155
+ └─ useArtifactPreviewProvider(ChatContainer 内)
156
+ ├─ 记录命中文件 activeArtifactId = file.outputId
157
+ └─ onOpen → addCustomTab('file-artifact') 展开并选中侧栏 Tab
158
+ └─ FileArtifactPanel
159
+ ├─ 列表 @select → setActiveArtifactId(outputId)
160
+ ├─ 下载 → resolveArtifactUrls + triggerArtifactDownload
161
+ └─ ArtifactPreviewHost
162
+ ├─ useArtifactPreviewLoader(策略 + fetch / 取链,防竞态)
163
+ └─ HtmlPreview | MarkdownPreview | TxtPreview | UrlIframePreview
164
+
165
+ 容器初始化
166
+ └─ ensureCustomTab('file-artifact') 常驻挂上(不展开侧栏);因 order:-1 排在首位,
167
+ 未主动切换过 Tab 时会成为默认选中面板;无产物时由面板展示整块空态
168
+ ```
169
+
170
+ - 文件卡片通过 `useArtifactPreviewConsumer` 注入预览上下文,无 Provider 时卡片不可点击(兜底 `undefined`)
171
+ - `ChatContainer` 通过 `useArtifactPreviewProvider` 提供上下文,并把「打开侧栏 Tab」这一副作用以 `onOpen` 注入,保持 composable 职责单一
172
+ - 侧栏「文件产物」Tab 固定不可关闭,`order: -1` 排在「执行情况」之前;**常驻不随产物有无增删**,无产物时由面板展示整块空态
173
+
174
+ ## 唯一键规则
175
+
176
+ 会话内以 **`outputId`** 作为文件产物唯一键:
177
+
178
+ - 同一 `outputId` 在多条消息中出现时,聚合列表去重并保留最后一次出现的文件信息
179
+ - `activeId`、列表 `:key`、`select` 事件参数均使用 `outputId`
180
+ - 文件名可能重复,**不可**作为唯一键
181
+
182
+ ## 预览机制
183
+
184
+ 预览分两步:先由 `resolveFileKind(type, name)`(`src/utils/file-type.ts`)把扩展名归入六个**分类**,再由 `getArtifactPreviewStrategy` 查表得到 **加载方式** 与 **渲染器**。面板与 Host 都不写死具体扩展名分支,后台新增文件类型时只需在分类表里补一行。
185
+
186
+ | 分类 | 覆盖扩展名 | load | 取链字段 | renderer |
187
+ | ---- | ---------- | ---- | -------- | -------- |
188
+ | `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 高亮) |
189
+ | `markdown` | `md` `markdown` | `text_from_download` | 同上 | `MarkdownPreview`(`MarkdownContent`) |
190
+ | `html` | `html` `htm` | `text_from_download` | 同上 | `HtmlPreview`(`<iframe srcdoc>`) |
191
+ | `text` | `txt` `rst` | `text_from_download` | 同上 | `TxtPreview`(`<pre>`) |
192
+ | `image` | `png` `jpg` `jpeg` `svg` | `preview_url` | `preview_url` | `ImagePreview`(`<img>`,`object-fit: contain`) |
193
+ | `binary` | `pdf` `docx` `xlsx` `xlsm` `xls` `pptx` `csv` `tsv`,以及**所有未登记的扩展名** | `preview_url` | `preview_url` | `UrlIframePreview`(`<iframe src>`,一般为后台转好的 PDF) |
194
+
195
+ 关于类型解析:
196
+
197
+ - `AIFileInfo.type` 为**扩展名字符串**(如 `'pdf'` / `'py'`)或无扩展名的文件名(如 `'Dockerfile'`),大小写不敏感
198
+ - `type` 缺省时回退 `name` 推断;`报告.final.xlsx` 取 `xlsx`,`.gitignore` 取 `gitignore`
199
+ - `md` 为后台扩展名别名,与 `markdown` 等价,共用 Markdown 直渲染
200
+ - 未登记的扩展名一律落入 `binary` 走后台预览,前端不会因为新类型报错
201
+
202
+ `CodePreview` 的语言由扩展名映射到 highlight.js(`vue → xml`、`tsx → typescript`、`env / cfg / conf → ini` 等,其余交给 hljs 自身别名表,识别不了则按 `plaintext` 转义输出)。单文件超过 300KB 时跳过高亮直接转义,避免同步解析阻塞主线程。
203
+
204
+ ### 加载态
205
+
206
+ | status | 表现 |
207
+ | ------ | ---- |
208
+ | `loading` | 预览区 [MessageLoading](/components/helper/message-loading) |
209
+ | `ready` | 对应 renderer 渲染 |
210
+ | `empty` | 「暂无可预览的文件」(无文件 / 未传 `onArtifactClick` / 缺所需 URL) |
211
+ | `error` | 「预览加载失败」+ 重试按钮 |
212
+
213
+ ### 重载与取链约定
214
+
215
+ - **重载键**:`ArtifactPreviewHost` 以 `` `${outputId}:${type}` `` 监听文件变化;`outputId` 或 `type` 任一变化会重新 `load()`,仅改文件名等其它字段不会
216
+ - **取链**:`resolveArtifactUrls(file)` 每次重新调用 `onArtifactClick`;同文件进行中的请求会复用(并发去重)
217
+ - **重试**:错误态点击重试再次走 `load()`,重新取链并加载
218
+ - **竞态**:切换文件时 `useArtifactPreviewLoader` 用 `loadSeq` + `AbortController` 中断上一次 `fetch`,避免过期结果覆盖最新内容
219
+
220
+ 下载图标仍由面板用 bkui `Loading` spin 单独表达。
221
+
222
+ ## 内部结构(不导出)
223
+
224
+ ```
225
+ message-artifacts/
226
+ ├── file-artifact-panel.vue # 列表 + 下载头 + 挂载 Host
227
+ └── artifact-preview/
228
+ ├── artifact-preview-host.vue # 状态机 UI + 分派 renderer
229
+ ├── preview-strategy.ts # getArtifactPreviewStrategy(分类 → 策略查表)
230
+ ├── use-artifact-preview-loader.ts
231
+ └── renderers/
232
+ ├── code-preview.vue
233
+ ├── html-preview.vue
234
+ ├── image-preview.vue
235
+ ├── markdown-preview.vue
236
+ ├── txt-preview.vue
237
+ └── url-iframe-preview.vue
238
+ ```
239
+
240
+ 分类表与扩展名归一化在 `src/utils/file-type.ts`(导出 `AIFileKind` / `resolveFileKind` / `normalizeFileExtension`),与[文件图标](/components/helper/file-icon)共用同一份解析入口。
241
+
242
+ ## API
243
+
244
+ ### Props
245
+
246
+ | 属性名 | 类型 | 必填 | 说明 |
247
+ | --------- | ------------------- | ---- | -------------------------------------- |
248
+ | activeId | `string` | ✓ | 当前命中的文件 `outputId` |
249
+ | artifacts | `SessionArtifact[]` | ✓ | 当前会话全部文件产物(已按 `outputId` 去重) |
250
+
251
+ ### Events
252
+
253
+ | 事件名 | 参数 | 说明 |
254
+ | ------ | ----------------- | -------------------------- |
255
+ | select | `(id: string)` | 列表内切换选中文件,参数为文件 `outputId` |
256
+
257
+ ### Slots / Expose
258
+
259
+ 无。
260
+
261
+ ## 类型定义
262
+
263
+ ```typescript
264
+ import type { AIFileInfo, SessionArtifact } from '@blueking/chat-x';
265
+
266
+ // 会话级文件产物:拍平去重后即为 AIFileInfo
267
+ type SessionArtifact = AIFileInfo;
268
+
269
+ type AIFileInfo = {
270
+ name: string;
271
+ outputId: string;
272
+ size: number;
273
+ /** 扩展名(如 'pdf' / 'py')或无扩展名文件名(如 'Dockerfile'),大小写不敏感 */
274
+ type: string;
275
+ };
276
+ ```
277
+
278
+ > **破坏性变更**:原 `AIFileType` 枚举已移除,`AIFileInfo.type` 改为 `string`。此前写 `type: AIFileType.Pdf` 的代码改为 `type: 'pdf'` 即可;枚举成员的值与新字符串一一对应,运行时数据无需迁移。
279
+
280
+ ## 关联 Composable
281
+
282
+ 预览**命中与取链**由 [useArtifactPreview](/composables/use-artifact-preview) 提供(Provider / Consumer + `ARTIFACT_PREVIEW_TOKEN`)。**正文加载与渲染**由内部 `useArtifactPreviewLoader` + `ArtifactPreviewHost` 完成,不在该 composable 内。
283
+
284
+ ## 关联组件
285
+
286
+ - [AssistantMessage](/components/message/assistant-message) — 文件产物来源(`property.artifacts`)
287
+ - [ChatContainer](/components/setup/chat-container) — 侧栏「文件产物」Tab 挂载场景,提供 `onArtifactClick`
288
+ - [MessageLoading](/components/helper/message-loading) — Host 预览区异步加载态
289
+ - [ExecutionSummary](/components/agent/execution-summary) — 同为侧栏 Tab 面板
@@ -0,0 +1,319 @@
1
+ # FileContent 文件内容
2
+
3
+ > 能力域:媒体文件 | 导入:`import { FileContent } from '@blueking/chat-x'` | since 1.0.0
4
+
5
+ 渲染文件附件,支持图片预览和下载事件。 源码位置:src/components/chat-content/file-content/file-content.vue。
6
+
7
+ **关联**:image-preview(点击图片缩略图打开全屏预览)、user-message(用户消息只读展示附件列表)
8
+
9
+ ---
10
+
11
+ # FileContent 文件内容展示
12
+ ## 源码事实
13
+
14
+ - **源码位置**:`src/components/chat-content/file-content/file-content.vue`
15
+ - **能力域**:媒体文件
16
+ - **能力说明**:渲染文件附件,支持图片预览和下载事件。
17
+
18
+ > **能力域**:媒体文件
19
+
20
+ 文件列表展示组件,支持图片缩略图预览、点击图片全屏预览(`ImagePreview`)、文件卡片展示(类型图标 / 文件名 / 文件大小)、图片加载失败占位和删除操作。
21
+
22
+ 内部由两个子组件承载单项渲染:
23
+
24
+ | 子组件 | 源码位置 | 职责 |
25
+ | ------------------ | ------------------------------------------------------------------- | --------------------------------------------- |
26
+ | `UploadImageItem` | `src/components/chat-content/file-content/upload-image-item.vue` | 图片缩略图、加载失败占位、hover 删除徽标 |
27
+ | `UploadFileItem` | `src/components/chat-content/file-content/upload-file-item.vue` | 180px 文件卡片(`FileIcon` + 文件名 + 大小) |
28
+
29
+ ## 渲染决策逻辑
30
+
31
+ 先分组、再渲染。设计稿要求**图片始终排在文件前方**,两类各自成行:
32
+
33
+ ```
34
+ splitUploadFiles(files) // 单次遍历
35
+ ├── 图片组(.ai-files-content-row.is-images)
36
+ │ 判定依据:mimeType 或 file.file?.type 以 'image/' 开头
37
+ │ src:file.url 优先,否则用本地 File 的 blob URL(按 key 缓存,移除 / 卸载时 revoke)
38
+ │ 加载失败 → 错误占位(粉色背景 + 红色边框 + 灰色图标),且不进入预览列表
39
+ └── 文件组(.ai-files-content-row.is-files)
40
+ 文件卡片:类型图标(FileIcon,按文件名解析扩展名)+ 文件名 + 文件大小
41
+ ```
42
+
43
+ > **是否为图片只看 MIME,不看 `url`。** 解除上传类型限制后,任意文件上传成功都会拿到 `url`,若按 `url` 判断会把 PDF / DOC 渲染成破图。因此 `url` 只决定 `<img src>` 从哪里来,不参与图片判定。
44
+
45
+ ## 基础用法(文件卡片)
46
+
47
+ MIME 类型非 `image/*` 的文件,渲染为固定宽 180px 的文件卡片(类型图标 + 文件名 + 大小)。类型图标与文件产物侧栏共用 `FileIcon` 的扩展名映射,`pdf` / `py` / `docx` 等各有专属图标,未登记的扩展名回退兜底图标:
48
+
49
+ ```vue
50
+ <template>
51
+ <FileContent
52
+ :files="files"
53
+ @delete-file="handleDeleteFile"
54
+ />
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ import { ref } from 'vue';
59
+ import { FileContent, type UploadFile } from '@blueking/chat-x';
60
+
61
+ const files = ref<Partial<UploadFile>[]>([
62
+ { file: new File(['content'], 'report.pdf', { type: 'application/pdf' }) },
63
+ { file: new File(['content'], 'data.xlsx', { type: 'application/vnd.ms-excel' }) },
64
+ { file: new File(['readme'], 'README.md', { type: 'text/markdown' }) },
65
+ ]);
66
+
67
+ const handleDeleteFile = (file: Partial<UploadFile>) => {
68
+ files.value = files.value.filter(f => f !== file);
69
+ };
70
+ </script>
71
+ ```
72
+
73
+ **渲染效果**(悬停文件卡片,底色加深并在右上角出现删除徽标)
74
+
75
+ ## 图片文件预览
76
+
77
+ MIME 为 `image/*` 时渲染为图片缩略图(`cursor: zoom-in`)。点击图片可打开全屏预览(内部集成 `ImagePreview` 组件),支持缩放、旋转、下载等操作:
78
+
79
+ ```vue
80
+ <script setup lang="ts">
81
+ import { ref } from 'vue';
82
+ import { type UploadFile } from '@blueking/chat-x';
83
+
84
+ const imageFiles = ref<Partial<UploadFile>[]>([
85
+ {
86
+ url: 'https://example.com/cat.jpg',
87
+ filename: 'cat.jpg',
88
+ mimeType: 'image/jpeg', // 图片判定依据
89
+ file: new File([''], 'cat.jpg', { type: 'image/jpeg' }),
90
+ },
91
+ {
92
+ url: 'https://example.com/dog.png',
93
+ filename: 'dog.png',
94
+ mimeType: 'image/png',
95
+ file: new File([''], 'dog.png', { type: 'image/png' }),
96
+ },
97
+ ]);
98
+ </script>
99
+ ```
100
+
101
+ **渲染效果**
102
+
103
+ ## 图片点击预览
104
+
105
+ 图片模式下点击缩略图会打开全屏预览弹窗。多张图片时支持左右切换。加载失败的图片不会出现在预览列表中:
106
+
107
+ ```vue
108
+ <template>
109
+ <FileContent :files="imageFiles" />
110
+ </template>
111
+
112
+ <script setup lang="ts">
113
+ import { ref } from 'vue';
114
+ import { FileContent, type UploadFile } from '@blueking/chat-x';
115
+
116
+ const imageFiles = ref<Partial<UploadFile>[]>([
117
+ { url: 'https://example.com/cat.jpg', filename: 'cat.jpg', mimeType: 'image/jpeg' },
118
+ { url: 'https://example.com/dog.png', filename: 'dog.png', mimeType: 'image/png' },
119
+ ]);
120
+ </script>
121
+ ```
122
+
123
+ > **预览行为**:组件内部自动维护 `ImagePreview` 实例,无需外部管理预览状态。只有加载成功的图片才会进入预览列表,加载失败的图片被自动过滤。
124
+
125
+ ## 图片加载失败
126
+
127
+ `<img>` 触发 `onerror` 时,切换为粉色背景 + 红色边框的错误占位:
128
+
129
+ ## 混合文件(图片 + 文件)
130
+
131
+ 同一列表可同时包含图片和其他文件,组件内部自动把图片排在前一行、文件排在后一行,各行内部横向排列并按需换行:
132
+
133
+ ```vue
134
+ <script setup lang="ts">
135
+ const files = [
136
+ { url: 'https://example.com/photo.jpg', filename: 'photo.jpg', mimeType: 'image/jpeg' },
137
+ { file: new File(['content'], 'report.pdf', { type: 'application/pdf' }) },
138
+ { file: new File(['data'], 'data.xlsx', { type: 'application/vnd.ms-excel' }) },
139
+ ];
140
+ </script>
141
+ ```
142
+
143
+ **渲染效果**
144
+
145
+ ## 只读模式(readonly)
146
+
147
+ 传入 `readonly` 时,隐藏删除按钮,适用于用户消息中展示已发送的文件:
148
+
149
+ ```vue
150
+ <FileContent :files="files" :readonly="true" />
151
+ ```
152
+
153
+ **渲染效果**(悬停无删除按钮)
154
+
155
+ ## 仅有 filename(无 File 对象)
156
+
157
+ 从服务端恢复的历史文件没有 `File` 对象时仍可渲染文件卡片,类型图标从 `filename` 推断。文件大小取 `size` 字段;未下发 `size` 时大小节点不渲染:
158
+
159
+ ```vue
160
+ <script setup lang="ts">
161
+ const remoteFiles = [
162
+ // 无 file 对象,未带 size,文件大小不渲染
163
+ { filename: 'server-report.pdf', mimeType: 'application/pdf' },
164
+ // 带 size 时正常显示「1.00M」
165
+ { filename: 'config.json', mimeType: 'application/json', size: 1024 * 1024 },
166
+ ];
167
+ </script>
168
+ ```
169
+
170
+ **渲染效果**(未带 `size` 的项无大小行)
171
+
172
+ ## 在 ChatInput 中使用
173
+
174
+ `FileContent` 由 `ChatInput` 内部自动渲染在文件预览区(`slot#files` 的默认内容),通常不需要手动引入。当 `ChatInput` 收到上传文件时,自动更新 `uploadFiles` 并渲染:
175
+
176
+ ```vue
177
+ <template>
178
+ <ChatInput
179
+ v-model="inputValue"
180
+ :on-send-message="handleSendMessage"
181
+ :on-upload="handleUpload"
182
+ />
183
+ </template>
184
+ ```
185
+
186
+ 若需自定义文件展示,通过 `slot#files` 替换默认 `FileContent`:
187
+
188
+ ```vue
189
+ <template>
190
+ <ChatInput
191
+ v-model="inputValue"
192
+ :on-upload="handleUpload"
193
+ >
194
+ <template #files="{ files }">
195
+ <!-- 自定义文件列表 UI -->
196
+ <FileContent
197
+ :files="files"
198
+ readonly
199
+ />
200
+ </template>
201
+ </ChatInput>
202
+ </template>
203
+ ```
204
+
205
+ ## API
206
+
207
+ ### Props
208
+
209
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
210
+ | -------- | ----------------------- | --------- | ---- | ------------------------------------------- |
211
+ | files | `Partial<UploadFile>[]` | - | ✅ | 文件列表 |
212
+ | readonly | `boolean` | `false` | - | 只读模式,`true` 时隐藏删除徽标与 hover 态 |
213
+ | variant | `'input' \| 'message'` | `'input'` | - | 展示形态,见下方「展示形态」 |
214
+
215
+ ### 展示形态(variant)
216
+
217
+ 只影响图片缩略图的圆角描边与整体对齐,文件卡片两种形态一致。图片尺寸规则两种形态相同:**定高 48px,宽度按原图比例,并在 48~120px 之间夹取**(竖图不至于过窄,长图不会撑破容器),超出部分由 `object-fit: cover` 裁切。
218
+
219
+ | variant | 使用场景 | 图片圆角 / 描边 | 对齐 |
220
+ | ----------- | ------------------ | --------------- | ------ |
221
+ | `'input'` | 输入框内待发送态 | 8px / `#f0f1f5` | 左对齐 |
222
+ | `'message'` | 用户消息内已发送态 | 4px / `#eaebf0` | 右对齐 |
223
+
224
+ ### Events
225
+
226
+ | 事件名 | 参数 | 触发时机 |
227
+ | ---------- | ----------------------------- | ------------------ |
228
+ | deleteFile | `(file: Partial<UploadFile>)` | 点击删除按钮时触发 |
229
+
230
+ ## 渲染模式详解
231
+
232
+ ### 图片模式
233
+
234
+ | 条件 | 图片 src | 点击行为 |
235
+ | ------------------- | ------------------------------------------------- | -------------- |
236
+ | `file.url` 有值 | `file.url` | 打开全屏预览 |
237
+ | 无 url、有 `File` | `URL.createObjectURL(file.file)`(按 key 缓存) | 打开全屏预览 |
238
+ | 图片加载失败 | 错误占位 | 不进入预览列表 |
239
+
240
+ blob URL 按附件 key 缓存,同一文件重复渲染不会重复创建;文件被移除或组件卸载时统一 `revokeObjectURL`。
241
+
242
+ ### 文件卡片模式
243
+
244
+ | 字段 | 取值优先级 |
245
+ | -------- | --------------------------------------------- |
246
+ | 文件名 | `file.filename` → `file.file?.name` |
247
+ | 类型图标 | 由文件名解析扩展名,交给 `FileIcon` 映射 |
248
+ | 文件大小 | `file.file?.size` → `file.size`;都没有则不渲染 |
249
+
250
+ ## 类型定义
251
+
252
+ ```typescript
253
+ import type { UploadFile, BinaryInputContent } from '@blueking/chat-x';
254
+
255
+ // 上传状态(ChatInput 内部使用,FileContent 不使用此字段)
256
+ enum UploadStatus {
257
+ Pending = 'pending', // 上传中
258
+ Success = 'success', // 上传成功
259
+ Error = 'error', // 上传失败
260
+ }
261
+
262
+ // 上传文件(FileContent 的 files 数组中每一项)
263
+ type UploadFile = BinaryInputContent & {
264
+ file?: File; // 原始 File 对象,无则文件大小不显示
265
+ status?: UploadStatus; // 上传状态(ChatInput 使用,FileContent 不消费)
266
+ };
267
+
268
+ // 二进制内容基础类型
269
+ interface BinaryInputContent {
270
+ type: 'binary';
271
+ url?: string; // 文件访问地址,只决定 <img src> 来源,不参与图片判定
272
+ filename?: string; // 文件名(文件卡片展示 + 类型图标解析)
273
+ mimeType?: string; // MIME 类型(图片判定依据)
274
+ size?: number; // 文件字节数,发送时由原始 File 写入
275
+ }
276
+ ```
277
+
278
+ ## 工具函数(`src/utils/upload-file.ts`)
279
+
280
+ 组件内的取值与分组逻辑都收敛在这里,`ChatInput`、`UserMessage` 共用同一套判定:
281
+
282
+ ```typescript
283
+ import {
284
+ getFileIdentity,
285
+ getUploadFileKey,
286
+ getUploadFileName,
287
+ getUploadFileSize,
288
+ isUploadImageFile,
289
+ splitUploadFiles,
290
+ } from '@blueking/chat-x';
291
+
292
+ // File 身份:文件名 + 大小 + 修改时间,用于去重与列表 key
293
+ getFileIdentity(file); // 'report.pdf_2048_1700000000000'
294
+
295
+ // 附件稳定 key:待发送态用 File 身份(上传成功回填 url 后不变),已发送态退回 url / 文件名
296
+ getUploadFileKey({ file }); // 'report.pdf_2048_1700000000000'
297
+ getUploadFileKey({ url: 'https://x/a.pdf' }); // 'https://x/a.pdf'
298
+
299
+ // 是否按图片渲染:只看 MIME,有 url 也不例外
300
+ isUploadImageFile({ mimeType: 'application/pdf', url: 'https://x/a.pdf' }); // false
301
+
302
+ // 文件名 / 字节数取值优先级
303
+ getUploadFileName({ filename: 'remote.pdf', file }); // 'remote.pdf'
304
+ getUploadFileSize({ size: 2048 }); // 2048
305
+
306
+ // 单次遍历分出图片组与其他文件组(图片在前)
307
+ splitUploadFiles(files); // { imageFiles, otherFiles }
308
+ ```
309
+
310
+ ## 使用场景
311
+
312
+ - **ChatInput 文件预览区**:上传文件后在编辑器上方展示待发送的文件列表(可删除)
313
+ - **用户消息展示**:`UserMessage` 内部以 `readonly` 模式展示已发送的图片和附件
314
+ - **历史消息回放**:服务端返回的文件信息(无 `File` 对象)也能正常渲染文档卡片
315
+
316
+ ## 关联组件
317
+
318
+ - [ImagePreview](/components/medias/image-preview) — 图片全屏预览
319
+ - [UserMessage](/components/message/user-message) — 用户消息内附件展示