@blueking/chat-x 0.0.51-beta.5 → 0.0.52-dev.1

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 (198) hide show
  1. package/README.md +15 -8
  2. package/dist/ag-ui/types/contents.d.ts +2 -0
  3. package/dist/ag-ui/types/messages.d.ts +6 -1
  4. package/dist/common/constants.d.ts +2 -0
  5. package/dist/components/ai-buttons/add-menu-btn/add-menu-btn.vue.d.ts +24 -0
  6. package/dist/components/chat-content/collapsible-content/collapsible-content.vue.d.ts +28 -0
  7. package/dist/components/chat-content/file-content/upload-file-item.vue.d.ts +5 -2
  8. package/dist/components/chat-content/file-content/upload-image-item.vue.d.ts +1 -0
  9. package/dist/components/chat-content/flow-agent-content/use-flow-node-actions.d.ts +1 -1
  10. package/dist/components/chat-input/ai-slash-input/ai-slash-input.vue.d.ts +21 -10
  11. package/dist/components/chat-input/ai-slash-input/command.d.ts +6 -3
  12. package/dist/components/chat-input/ai-slash-input/constants.d.ts +2 -0
  13. package/dist/components/chat-input/ai-slash-input/use-menu-trigger.d.ts +17 -0
  14. package/dist/components/chat-input/build-default-placeholder.d.ts +9 -3
  15. package/dist/components/chat-input/chat-input.vue.d.ts +30 -18
  16. package/dist/components/chat-input/input-menu/constants.d.ts +60 -0
  17. package/dist/components/chat-input/input-menu/index.d.ts +4 -0
  18. package/dist/components/chat-input/input-menu/input-menu-option.vue.d.ts +14 -0
  19. package/dist/components/chat-input/input-menu/input-menu-panel.vue.d.ts +17 -0
  20. package/dist/components/chat-input/input-menu/use-input-menu.d.ts +22 -0
  21. package/dist/components/chat-input/model-selector/types.d.ts +3 -2
  22. package/dist/components/chat-message/message-container/message-container.vue.d.ts +1 -1
  23. package/dist/components/chat-message/user-message/user-message.vue.d.ts +1 -1
  24. package/dist/components/mention/create-mention-tippy.d.ts +27 -0
  25. package/dist/components/mention/index.d.ts +4 -0
  26. package/dist/components/{chat-input/ai-slash-input/ai-prompt-list/ai-prompt-list.vue.d.ts → mention/mention-popover.vue.d.ts} +3 -2
  27. package/dist/components/{chat-input/ai-slash-input/ai-skill-list/ai-skill-list.vue.d.ts → mention/mention-tag.vue.d.ts} +7 -3
  28. package/dist/components/{chat-input/ai-slash-input/ai-slash-menu/ai-slash-menu.vue.d.ts → mention/mention-text.vue.d.ts} +3 -3
  29. package/dist/components/resource-icon/index.d.ts +1 -0
  30. package/dist/components/resource-icon/resource-icon.vue.d.ts +11 -0
  31. package/dist/composables/index.d.ts +1 -0
  32. package/dist/composables/use-artifact-preview.d.ts +5 -2
  33. package/dist/composables/use-common.d.ts +0 -9
  34. package/dist/composables/use-global-config.d.ts +4 -0
  35. package/dist/composables/use-input-mention.d.ts +13 -0
  36. package/dist/composables/use-message-group.d.ts +1011 -72
  37. package/dist/icons/index.d.ts +1 -0
  38. package/dist/icons/input.d.ts +21 -0
  39. package/dist/icons/menu-item.d.ts +22 -0
  40. package/dist/index.css +1 -1
  41. package/dist/index.js +4953 -4429
  42. package/dist/index.js.map +1 -1
  43. package/dist/lang/lang.d.ts +13 -3
  44. package/dist/mcp/generated/docs/activity-message.md +3 -3
  45. package/dist/mcp/generated/docs/add-menu-btn.md +91 -0
  46. package/dist/mcp/generated/docs/ai-slash-input.md +72 -26
  47. package/dist/mcp/generated/docs/assistant-message.md +7 -1
  48. package/dist/mcp/generated/docs/chat-container.md +45 -6
  49. package/dist/mcp/generated/docs/chat-input.md +327 -483
  50. package/dist/mcp/generated/docs/collapsible-content.md +78 -0
  51. package/dist/mcp/generated/docs/constants.md +36 -3
  52. package/dist/mcp/generated/docs/execution-summary.md +1 -23
  53. package/dist/mcp/generated/docs/file-artifact-panel.md +24 -5
  54. package/dist/mcp/generated/docs/file-content.md +4 -0
  55. package/dist/mcp/generated/docs/file-upload-btn.md +15 -12
  56. package/dist/mcp/generated/docs/flow-agent-content.md +10 -12
  57. package/dist/mcp/generated/docs/input-menu-panel.md +166 -0
  58. package/dist/mcp/generated/docs/mention-tag.md +83 -0
  59. package/dist/mcp/generated/docs/mention-text.md +86 -0
  60. package/dist/mcp/generated/docs/message-container.md +1 -1
  61. package/dist/mcp/generated/docs/message-render.md +1 -1
  62. package/dist/mcp/generated/docs/message-tools.md +15 -4
  63. package/dist/mcp/generated/docs/messages.md +3 -0
  64. package/dist/mcp/generated/docs/model-selector.md +4 -2
  65. package/dist/mcp/generated/docs/overflow-tips.md +3 -3
  66. package/dist/mcp/generated/docs/resource-icon.md +102 -0
  67. package/dist/mcp/generated/docs/text-content.md +7 -3
  68. package/dist/mcp/generated/docs/theme.md +27 -35
  69. package/dist/mcp/generated/docs/use-artifact-preview.md +7 -3
  70. package/dist/mcp/generated/docs/use-command-selection.md +1 -1
  71. package/dist/mcp/generated/docs/use-container-scroll.md +2 -1
  72. package/dist/mcp/generated/docs/use-flow-node-actions.md +8 -15
  73. package/dist/mcp/generated/docs/use-global-config.md +11 -4
  74. package/dist/mcp/generated/docs/use-input-mention.md +119 -0
  75. package/dist/mcp/generated/docs/use-menu-keydown.md +15 -16
  76. package/dist/mcp/generated/docs/use-message-group.md +3 -6
  77. package/dist/mcp/generated/docs/user-message.md +39 -17
  78. package/dist/mcp/generated/index.json +233 -81
  79. package/dist/types/editor.d.ts +0 -29
  80. package/dist/types/index.d.ts +1 -0
  81. package/dist/types/input-menu.d.ts +46 -0
  82. package/dist/utils/artifact-tags.d.ts +36 -0
  83. package/dist/utils/collect-message-artifacts.d.ts +14 -0
  84. package/dist/utils/file.d.ts +3 -3
  85. package/dist/utils/index.d.ts +2 -1
  86. package/dist/utils/upload-file.d.ts +6 -1
  87. package/package.json +3 -2
  88. package/skills/blueking-chat-x/SKILL.md +143 -0
  89. package/skills/blueking-chat-x/references/_index.md +153 -0
  90. package/skills/blueking-chat-x/references/components/activity-layout.md +134 -0
  91. package/skills/blueking-chat-x/references/components/activity-message.md +486 -0
  92. package/skills/blueking-chat-x/references/components/add-menu-btn.md +88 -0
  93. package/skills/blueking-chat-x/references/components/ai-image.md +230 -0
  94. package/skills/blueking-chat-x/references/components/ai-loading.md +131 -0
  95. package/skills/blueking-chat-x/references/components/ai-selection.md +439 -0
  96. package/skills/blueking-chat-x/references/components/ai-slash-input.md +102 -0
  97. package/skills/blueking-chat-x/references/components/animation-text.md +202 -0
  98. package/skills/blueking-chat-x/references/components/assistant-message.md +504 -0
  99. package/skills/blueking-chat-x/references/components/chat-container.md +912 -0
  100. package/skills/blueking-chat-x/references/components/chat-input.md +614 -0
  101. package/skills/blueking-chat-x/references/components/cite-content.md +142 -0
  102. package/skills/blueking-chat-x/references/components/code-content.md +211 -0
  103. package/skills/blueking-chat-x/references/components/collapsible-content.md +76 -0
  104. package/skills/blueking-chat-x/references/components/common-error-content.md +73 -0
  105. package/skills/blueking-chat-x/references/components/content-render.md +233 -0
  106. package/skills/blueking-chat-x/references/components/delete-tool.md +191 -0
  107. package/skills/blueking-chat-x/references/components/desc-panel.md +162 -0
  108. package/skills/blueking-chat-x/references/components/detail-section.md +91 -0
  109. package/skills/blueking-chat-x/references/components/execution-summary.md +150 -0
  110. package/skills/blueking-chat-x/references/components/file-artifact-panel.md +307 -0
  111. package/skills/blueking-chat-x/references/components/file-content.md +340 -0
  112. package/skills/blueking-chat-x/references/components/file-icon.md +109 -0
  113. package/skills/blueking-chat-x/references/components/file-upload-btn.md +162 -0
  114. package/skills/blueking-chat-x/references/components/flow-agent-content.md +266 -0
  115. package/skills/blueking-chat-x/references/components/flow-agent-node-detail.md +236 -0
  116. package/skills/blueking-chat-x/references/components/highlight-keyword.md +146 -0
  117. package/skills/blueking-chat-x/references/components/image-content.md +182 -0
  118. package/skills/blueking-chat-x/references/components/image-preview-group.md +184 -0
  119. package/skills/blueking-chat-x/references/components/image-preview.md +226 -0
  120. package/skills/blueking-chat-x/references/components/info-message.md +144 -0
  121. package/skills/blueking-chat-x/references/components/input-attachment.md +49 -0
  122. package/skills/blueking-chat-x/references/components/input-info-alert.md +42 -0
  123. package/skills/blueking-chat-x/references/components/input-menu-panel.md +162 -0
  124. package/skills/blueking-chat-x/references/components/interrupt-message.md +212 -0
  125. package/skills/blueking-chat-x/references/components/key-value-content.md +128 -0
  126. package/skills/blueking-chat-x/references/components/knowledge-rag-content.md +122 -0
  127. package/skills/blueking-chat-x/references/components/latex-content.md +200 -0
  128. package/skills/blueking-chat-x/references/components/loading-message.md +192 -0
  129. package/skills/blueking-chat-x/references/components/markdown-content.md +232 -0
  130. package/skills/blueking-chat-x/references/components/mention-tag.md +79 -0
  131. package/skills/blueking-chat-x/references/components/mention-text.md +83 -0
  132. package/skills/blueking-chat-x/references/components/mermaid-content.md +189 -0
  133. package/skills/blueking-chat-x/references/components/message-container.md +645 -0
  134. package/skills/blueking-chat-x/references/components/message-loading.md +118 -0
  135. package/skills/blueking-chat-x/references/components/message-render.md +327 -0
  136. package/skills/blueking-chat-x/references/components/message-time.md +177 -0
  137. package/skills/blueking-chat-x/references/components/message-tools.md +427 -0
  138. package/skills/blueking-chat-x/references/components/model-selector.md +157 -0
  139. package/skills/blueking-chat-x/references/components/preview-toolbar.md +42 -0
  140. package/skills/blueking-chat-x/references/components/questions-container.md +85 -0
  141. package/skills/blueking-chat-x/references/components/reasoning-message.md +232 -0
  142. package/skills/blueking-chat-x/references/components/reference-content.md +135 -0
  143. package/skills/blueking-chat-x/references/components/reference-doc-content.md +109 -0
  144. package/skills/blueking-chat-x/references/components/resource-icon.md +98 -0
  145. package/skills/blueking-chat-x/references/components/scroll-btn.md +159 -0
  146. package/skills/blueking-chat-x/references/components/selection-footer.md +78 -0
  147. package/skills/blueking-chat-x/references/components/selection-question.md +88 -0
  148. package/skills/blueking-chat-x/references/components/shortcut-btn.md +204 -0
  149. package/skills/blueking-chat-x/references/components/shortcut-btns.md +266 -0
  150. package/skills/blueking-chat-x/references/components/shortcut-render.md +424 -0
  151. package/skills/blueking-chat-x/references/components/simple-table.md +101 -0
  152. package/skills/blueking-chat-x/references/components/text-content.md +80 -0
  153. package/skills/blueking-chat-x/references/components/tool-approval-card.md +183 -0
  154. package/skills/blueking-chat-x/references/components/tool-btn.md +317 -0
  155. package/skills/blueking-chat-x/references/components/tool-message.md +235 -0
  156. package/skills/blueking-chat-x/references/components/toolcall-render.md +348 -0
  157. package/skills/blueking-chat-x/references/components/user-feedback.md +233 -0
  158. package/skills/blueking-chat-x/references/components/user-message.md +444 -0
  159. package/skills/blueking-chat-x/references/components/user-question-answered-card.md +104 -0
  160. package/skills/blueking-chat-x/references/components/user-question-card.md +231 -0
  161. package/skills/blueking-chat-x/references/components/user-question-choice.md +105 -0
  162. package/skills/blueking-chat-x/references/components/user-question-option.md +42 -0
  163. package/skills/blueking-chat-x/references/components/vnode-renderer.md +122 -0
  164. package/skills/blueking-chat-x/references/composables/use-animation-text.md +196 -0
  165. package/skills/blueking-chat-x/references/composables/use-artifact-preview.md +235 -0
  166. package/skills/blueking-chat-x/references/composables/use-clipboard.md +203 -0
  167. package/skills/blueking-chat-x/references/composables/use-command-selection.md +150 -0
  168. package/skills/blueking-chat-x/references/composables/use-container-scroll.md +57 -0
  169. package/skills/blueking-chat-x/references/composables/use-custom-tab.md +158 -0
  170. package/skills/blueking-chat-x/references/composables/use-flow-node-actions.md +164 -0
  171. package/skills/blueking-chat-x/references/composables/use-full-screen.md +112 -0
  172. package/skills/blueking-chat-x/references/composables/use-global-config.md +155 -0
  173. package/skills/blueking-chat-x/references/composables/use-input-mention.md +115 -0
  174. package/skills/blueking-chat-x/references/composables/use-menu-keydown.md +161 -0
  175. package/skills/blueking-chat-x/references/composables/use-message-group.md +244 -0
  176. package/skills/blueking-chat-x/references/composables/use-observer-visible-list.md +188 -0
  177. package/skills/blueking-chat-x/references/composables/use-parent-scrolling.md +46 -0
  178. package/skills/blueking-chat-x/references/directives/index.md +61 -0
  179. package/skills/blueking-chat-x/references/directives/overflow-tips.md +205 -0
  180. package/skills/blueking-chat-x/references/icons/index.md +185 -0
  181. package/skills/blueking-chat-x/references/plugins/index.md +56 -0
  182. package/skills/blueking-chat-x/references/plugins/markdown-container.md +55 -0
  183. package/skills/blueking-chat-x/references/plugins/markdown-latex.md +205 -0
  184. package/skills/blueking-chat-x/references/plugins/markdown-mermaid.md +247 -0
  185. package/skills/blueking-chat-x/references/theme/theme.md +423 -0
  186. package/skills/blueking-chat-x/references/types/constants.md +340 -0
  187. package/skills/blueking-chat-x/references/types/index.md +120 -0
  188. package/skills/blueking-chat-x/references/types/interrupt.md +379 -0
  189. package/skills/blueking-chat-x/references/types/messages.md +556 -0
  190. package/skills/blueking-chat-x/references/types/schema.md +91 -0
  191. package/skills/blueking-chat-x/references/utils/index.md +196 -0
  192. package/skills/blueking-chat-x/scripts/generate-references.mjs +536 -0
  193. package/dist/components/chat-input/ai-slash-editor/ai-slash-editor.vue.d.ts +0 -28
  194. package/dist/components/chat-input/ai-slash-editor/theme.d.ts +0 -24
  195. package/dist/mcp/generated/docs/ai-prompt-list.md +0 -45
  196. package/dist/mcp/generated/docs/ai-skill-list.md +0 -75
  197. package/dist/mcp/generated/docs/ai-slash-editor.md +0 -43
  198. package/dist/mcp/generated/docs/ai-slash-menu.md +0 -42
@@ -0,0 +1,196 @@
1
+ # useAnimationText
2
+
3
+ > 导入:`import { useAnimationText } from '@blueking/chat-x'` | since 1.0.0
4
+
5
+ useAnimationText 接收 MaybeRef<string> 与可选 AnimationConfig(fadeDuration、easing),返回 chunks 与 animationStyle。 监听文本变化:前缀追加则增量拆分为新 chunk 并触发动画,否则重置为单 chunk,适合流式输出逐段淡入。 全局样式已含 ai-markdown-fade-in。AnimationText 组件内部封装同一逻辑。
6
+
7
+ **关联**:animation-text(封装 chunks 与样式渲染)、markdown-content(流式 Markdown 文本展示场景可组合使用)
8
+
9
+ ---
10
+
11
+ # useAnimationText
12
+
13
+ > **分类**:composable
14
+
15
+ 文本淡入动画的组合式函数。将响应式文本按**增量**拆分为独立 chunk,每个新增 chunk 对应一次淡入动画,适用于 AI 流式输出的逐段渐显效果。
16
+
17
+ ## 工作原理
18
+
19
+ 每当 `text` 发生变化时,composable 通过比较新旧文本决定如何更新 chunks:
20
+
21
+ | 变化情况 | 处理方式 |
22
+ | ----------------------------------- | ------------------------- |
23
+ | 新文本以旧文本为前缀(追加) | 将增量部分追加为新 chunk |
24
+ | 新文本与旧文本完全不同(替换/重置) | chunks 重置为 `[newText]` |
25
+ | 文本未变化 | 无操作 |
26
+
27
+ 每个 chunk 对应一个独立的 DOM 节点,节点加入 DOM 时自动触发 `ai-markdown-fade-in` 淡入动画。
28
+
29
+ > `@keyframes ai-markdown-fade-in` 已内置于 `@blueking/chat-x` 全局样式,**无需手动定义**。
30
+
31
+ ## 基础用法
32
+
33
+ 将 `Ref<string>` 传入 composable,配合 `v-for` + `:style` 渲染各 chunk。
34
+
35
+ ```vue
36
+ <template>
37
+ <div>
38
+ <span
39
+ v-for="(chunk, index) in chunks"
40
+ :key="index"
41
+ :style="animationStyle"
42
+ >{{ chunk }}</span
43
+ >
44
+ </div>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import { ref } from 'vue';
49
+ import { useAnimationText } from '@blueking/chat-x';
50
+
51
+ const text = ref('');
52
+ const { chunks, animationStyle } = useAnimationText(text);
53
+
54
+ // 模拟流式追加
55
+ async function startStreaming() {
56
+ const fullText = '这是一段模拟 AI 流式输出的文本内容...';
57
+ for (const char of fullText) {
58
+ text.value += char;
59
+ await new Promise(resolve => setTimeout(resolve, 50));
60
+ }
61
+ }
62
+
63
+ startStreaming();
64
+ </script>
65
+ ```
66
+
67
+ **渲染效果**
68
+
69
+ ## 自定义动画配置
70
+
71
+ 通过 `options` 调整淡入动画的持续时间和缓动函数。
72
+
73
+ ```vue
74
+ <script setup lang="ts">
75
+ import { ref } from 'vue';
76
+ import { useAnimationText } from '@blueking/chat-x';
77
+
78
+ const text = ref('');
79
+ const { chunks, animationStyle } = useAnimationText(text, {
80
+ fadeDuration: 600, // 淡入持续时间,默认 200ms
81
+ easing: 'ease-out', // CSS 缓动函数,默认 'ease-in-out'
82
+ });
83
+ </script>
84
+ ```
85
+
86
+ **渲染效果**(fadeDuration=600ms)
87
+
88
+ ## 文本重置行为
89
+
90
+ 当 `text` 被完全替换(新值不以旧值为前缀)时,chunks 会重置为单一片段,之前积累的 chunks 清空。这适用于多轮对话中切换消息的场景。
91
+
92
+ ```vue
93
+ <script setup lang="ts">
94
+ import { ref } from 'vue';
95
+ import { useAnimationText } from '@blueking/chat-x';
96
+
97
+ const text = ref('');
98
+ const { chunks, animationStyle } = useAnimationText(text);
99
+
100
+ async function nextMessage(newContent: string) {
101
+ // 直接替换整个 text,chunks 自动重置
102
+ text.value = '';
103
+ for (const char of newContent) {
104
+ text.value += char;
105
+ await new Promise(r => setTimeout(r, 50));
106
+ }
107
+ }
108
+ </script>
109
+ ```
110
+
111
+ **渲染效果**(两条消息依次播放,第二条时 chunks 重置)
112
+
113
+ ## 使用 AnimationText 组件
114
+
115
+ 如无需定制渲染逻辑,可直接使用封装好的 `AnimationText` 组件,内部已集成 `useAnimationText`:
116
+
117
+ ```vue
118
+ <template>
119
+ <AnimationText :text="text" />
120
+ </template>
121
+
122
+ <script setup lang="ts">
123
+ import { AnimationText } from '@blueking/chat-x';
124
+ </script>
125
+ ```
126
+
127
+ > 详见 [AnimationText 组件文档](/components/rendering/animation-text)。
128
+
129
+ ## API
130
+
131
+ ### 函数签名
132
+
133
+ ```typescript
134
+ function useAnimationText(
135
+ text: MaybeRef<string>,
136
+ options?: AnimationConfig,
137
+ ): {
138
+ chunks: Ref<string[]>;
139
+ animationStyle: ComputedRef<CSSProperties>;
140
+ };
141
+ ```
142
+
143
+ ### 参数
144
+
145
+ | 参数 | 类型 | 必填 | 说明 |
146
+ | --------- | ------------------ | ---- | ----------------------------------------- |
147
+ | `text` | `MaybeRef<string>` | 是 | 响应式文本,传入 `Ref<string>` 可追踪变化 |
148
+ | `options` | `AnimationConfig` | 否 | 动画配置 |
149
+
150
+ ### AnimationConfig
151
+
152
+ | 属性 | 类型 | 默认值 | 说明 |
153
+ | -------------- | -------- | --------------- | ---------------------- |
154
+ | `fadeDuration` | `number` | `200` | 淡入动画持续时间(ms) |
155
+ | `easing` | `string` | `'ease-in-out'` | CSS 缓动函数 |
156
+
157
+ ### 返回值
158
+
159
+ | 属性 | 类型 | 说明 |
160
+ | ---------------- | ---------------------------- | ------------------------------------------ |
161
+ | `chunks` | `Ref<string[]>` | 拆分后的文本片段数组,每个片段对应一次动画 |
162
+ | `animationStyle` | `ComputedRef<CSSProperties>` | 所有 chunk 共用的动画样式对象 |
163
+
164
+ ## 类型定义
165
+
166
+ ```typescript
167
+ import type { MaybeRef, Ref, ComputedRef, CSSProperties } from 'vue';
168
+
169
+ interface AnimationConfig {
170
+ fadeDuration?: number;
171
+ easing?: string;
172
+ }
173
+
174
+ function useAnimationText(
175
+ text: MaybeRef<string>,
176
+ options?: AnimationConfig,
177
+ ): {
178
+ chunks: Ref<string[]>;
179
+ animationStyle: ComputedRef<CSSProperties>;
180
+ };
181
+ ```
182
+
183
+ ## 注意事项
184
+
185
+ 1. **在组件 `setup` 中通过 `watch(text, ...)` 直接监听 Ref**:composable 内部使用 `watch(() => text, ...)` 的 getter 形式,当 `text` 是 `Ref` 时,getter 每次返回同一个 Ref 对象,Vue 判断为"值未变化",**watch 不会触发**。在自定义 setup 中如需直接响应文本变化,请用 `watch(text, handler)` 而非 `watch(() => text, handler)`。
186
+
187
+ 2. **`animationStyle` 为所有 chunk 共享的同一个对象**:动画效果来自每个 chunk 对应的 DOM 节点被创建时触发,而非样式本身的差异。
188
+
189
+ 3. **内置 CSS 关键帧**:引入 `@blueking/chat-x` 的全局样式后,`@keyframes ai-markdown-fade-in` 自动可用,无需手动定义。
190
+
191
+ 4. **性能**:长时间流式输出会积累大量 chunk 节点,动画完成后(`forwards`)这些节点保持 `opacity: 1`,对性能影响可接受;如有需要可在流式结束后将 `text` 合并为单次赋值以归并 chunk
192
+
193
+ ## 关联组件
194
+
195
+ - [AnimationText](../components/rendering/animation-text) — 默认封装组件
196
+ - [MarkdownContent](../components/rendering/markdown-content) — 富文本流式展示场景。
@@ -0,0 +1,235 @@
1
+ # useArtifactPreview
2
+
3
+ > 导入:`import { useArtifactPreviewConsumer, useArtifactPreviewProvider } from '@blueking/chat-x'` | since 0.0.20
4
+
5
+ useArtifactPreviewProvider 维护 activeArtifactId(值为 outputId),openPreview 命中文件并触发 onOpen 打开侧栏 Tab; 并通过 getOnArtifactClick 封装 resolveArtifactUrls(每次重新取链,并发去重); useArtifactPreviewConsumer 在后代注入同一套 API。SessionArtifact 即 AIFileInfo,会话内以 outputId 为唯一键。 正文加载与分类型渲染不在本 composable,由 FileArtifactPanel 内 ArtifactPreviewHost 完成(重载键 outputId:type)。 FILE_ARTIFACT_TAB_NAME 标识固定「文件产物」Tab。
6
+
7
+ **关联**:chat-container(Provider 主场景,聚合 sessionArtifacts 并挂载 FileArtifactPanel)、file-artifact-panel(侧栏面板消费 activeArtifactId 与 setActiveArtifactId;预览加载在面板内 Host)、assistant-message(文件产物来源 property.artifacts)
8
+
9
+ ---
10
+
11
+ # useArtifactPreview 文件产物预览
12
+
13
+ > **分类**:composable
14
+
15
+ Provider/Consumer 模式的文件产物预览状态管理。Provider 在 `ChatContainer` 中创建,负责维护当前命中的文件 `outputId`;Consumer 在深层 `ArtifactFileCard` 中注入,用于点击卡片触发预览。
16
+
17
+ **职责边界**:
18
+
19
+ - **本 composable**:维护「命中文件」与「URL 解析」(每次重新取链 + 并发去重);打开侧栏 Tab(`addCustomTab`)由容器通过 `onOpen` 注入
20
+ - **不在本 composable**:聚合会话文件列表、渲染预览面板、按类型 fetch 正文 / iframe 展示 —— 分别由 `useMessageGroup.sessionArtifacts`、`FileArtifactPanel`、内部 `ArtifactPreviewHost` + `useArtifactPreviewLoader` 承担(预览重载键为 `outputId:type`)
21
+
22
+ ## 函数签名
23
+
24
+ ### useArtifactPreviewProvider
25
+
26
+ ```typescript
27
+ function useArtifactPreviewProvider(options: {
28
+ /** 读取业务侧异步取链回调(getter 保持对 props 变更敏感) */
29
+ getOnArtifactClick?: () => OnArtifactClick | undefined;
30
+ /** 命中文件后触发:由容器负责 addCustomTab + 展开侧栏 + 选中 Tab */
31
+ onOpen: (outputId: string) => void;
32
+ }): {
33
+ activeArtifactId: ShallowRef<string>;
34
+ canResolveArtifactUrl: ComputedRef<boolean>;
35
+ openPreview: (payload: OpenArtifactPreviewPayload) => void;
36
+ resolveArtifactUrls: (file: AIFileInfo) => Promise<ArtifactUrlResult>;
37
+ setActiveArtifactId: (id: string) => void;
38
+ };
39
+ ```
40
+
41
+ ### useArtifactPreviewConsumer
42
+
43
+ ```typescript
44
+ function useArtifactPreviewConsumer():
45
+ | undefined
46
+ | {
47
+ activeArtifactId: Ref<string>;
48
+ canResolveArtifactUrl: ComputedRef<boolean>;
49
+ openPreview: (payload: OpenArtifactPreviewPayload) => void;
50
+ resolveArtifactUrls: (file: AIFileInfo) => Promise<ArtifactUrlResult>;
51
+ setActiveArtifactId: (id: string) => void;
52
+ };
53
+ ```
54
+
55
+ ## 使用示例
56
+
57
+ ### Provider(ChatContainer)
58
+
59
+ 会话级文件产物由 [`useMessageGroup`](./use-message-group) 统一聚合(`sessionArtifacts`),Provider 只负责命中与打开侧栏 Tab:
60
+
61
+ ```typescript
62
+ import {
63
+ useArtifactPreviewProvider,
64
+ useCustomTabProvider,
65
+ useMessageGroup,
66
+ FILE_ARTIFACT_TAB_NAME,
67
+ } from '@blueking/chat-x';
68
+ import { t } from '@blueking/chat-x/lang';
69
+
70
+ const { addCustomTab, ensureCustomTab, removeCustomTab } = useCustomTabProvider({ /* ... */ });
71
+
72
+ // 会话级文件产物聚合已内聚在 useMessageGroup,直接消费
73
+ const { sessionArtifacts } = useMessageGroup({ keyword, messages, selectedUserMessages });
74
+
75
+ const FILE_ARTIFACT_TAB = {
76
+ closable: false,
77
+ label: t('文件产物'),
78
+ name: FILE_ARTIFACT_TAB_NAME,
79
+ order: -1,
80
+ };
81
+
82
+ // 常驻挂载:不随产物有无增删,无产物时由面板展示空态
83
+ ensureCustomTab(FILE_ARTIFACT_TAB);
84
+
85
+ const { activeArtifactId, setActiveArtifactId } = useArtifactPreviewProvider({
86
+ getOnArtifactClick: () => props.onArtifactClick,
87
+ // 点击文件卡片:展开侧栏并选中「文件产物」Tab
88
+ onOpen: () => {
89
+ addCustomTab(FILE_ARTIFACT_TAB);
90
+ },
91
+ });
92
+
93
+ // 仅维护命中态:无产物清空,命中项失效时回落到第一个
94
+ watch(sessionArtifacts, list => {
95
+ if (!list.length) {
96
+ setActiveArtifactId('');
97
+ return;
98
+ }
99
+ if (!list.some(item => item.outputId === activeArtifactId.value)) {
100
+ setActiveArtifactId(list[0].outputId);
101
+ }
102
+ }, { immediate: true });
103
+ ```
104
+
105
+ ### Consumer(ArtifactFileCard)
106
+
107
+ ```typescript
108
+ import { useArtifactPreviewConsumer } from '@blueking/chat-x';
109
+
110
+ const artifactPreview = useArtifactPreviewConsumer();
111
+
112
+ // 有 Provider 时卡片可点击;无 Provider 时返回 undefined,卡片不可点击
113
+ const clickable = computed(() => !!props.onPreview || !!artifactPreview);
114
+
115
+ const handleCardClick = () => {
116
+ if (props.onPreview) {
117
+ props.onPreview(props.file);
118
+ return;
119
+ }
120
+ artifactPreview?.openPreview({ file: props.file });
121
+ };
122
+ ```
123
+
124
+ ### 侧栏列表内切换命中文件
125
+
126
+ ```typescript
127
+ // FileArtifactPanel 列表点击 → emit select(outputId) → 容器调用 setActiveArtifactId
128
+ // 右侧预览由面板内 ArtifactPreviewHost 消费 activeArtifact,自行取链并按类型渲染
129
+ <FileArtifactPanel
130
+ :active-id="activeArtifactId"
131
+ :artifacts="sessionArtifacts"
132
+ @select="setActiveArtifactId"
133
+ />
134
+ ```
135
+
136
+ ### 业务侧取链(ChatContainer `onArtifactClick`)
137
+
138
+ 文本类预览需要可 `fetch` 的 `download_url`;iframe 类需要 `preview_url`(一般为后台转好的 PDF):
139
+
140
+ ```typescript
141
+ const onArtifactClick = async (file: AIFileInfo) => {
142
+ const res = await api.getArtifactUrls(file.outputId);
143
+ return {
144
+ download_url: res.download_url,
145
+ preview_url: res.preview_url,
146
+ };
147
+ };
148
+ ```
149
+
150
+ ## 内置常量
151
+
152
+ | 常量名 | 值 | 说明 |
153
+ | ------------------------- | ------------------ | ----------------------------------------- |
154
+ | `FILE_ARTIFACT_TAB_NAME` | `'file-artifact'` | 「文件产物」侧栏 Tab 的固定标识,不可关闭 |
155
+ | `ARTIFACT_PREVIEW_TOKEN` | `Symbol` | provide/inject 注入 Token |
156
+
157
+ ## 返回值说明
158
+
159
+ | 属性/方法名 | 类型 | 说明 |
160
+ | ------------------- | ----------------------------------------- | -------------------------------------------------------------------- |
161
+ | activeArtifactId | `ShallowRef<string>` | 当前命中的文件 `outputId` |
162
+ | canResolveArtifactUrl | `ComputedRef<boolean>` | 是否具备异步取链能力(有 `onArtifactClick` 时为 true,下载按钮据此显隐) |
163
+ | openPreview | `(payload: OpenArtifactPreviewPayload) => void` | 由文件卡片或 `artifact` 资源标签触发:以 `file.outputId` 更新命中态、调用 `onOpen`;入参只需 `{ file: { outputId } }` |
164
+ | resolveArtifactUrls | `(file: AIFileInfo) => Promise<ArtifactUrlResult>` | 调用 `onArtifactClick` 取链;每次重新获取,不缓存;同文件并发去重 |
165
+ | setActiveArtifactId | `(id: string) => void` | 直接设置命中文件 `outputId`;侧栏列表内切换选中时使用 |
166
+
167
+ ## 类型定义
168
+
169
+ ```typescript
170
+ import type { AIFileInfo, ArtifactUrlResult, OnArtifactClick } from '@blueking/chat-x';
171
+
172
+ /**
173
+ * 打开预览时的入参。
174
+ * 命中逻辑只依赖 outputId,因此不要求完整的 AIFileInfo——
175
+ * 输入框内的 @ 文件标签只持有 id 与名称,无需为了调用而伪造 size / type。
176
+ */
177
+ type OpenArtifactPreviewPayload = {
178
+ file: Pick<AIFileInfo, 'outputId'>;
179
+ };
180
+
181
+ /**
182
+ * 会话级文件产物:以 outputId 为会话内唯一键(同 outputId 视为同一文件)。
183
+ * 拍平去重后即为 AIFileInfo,此处用别名标明语义。
184
+ */
185
+ type SessionArtifact = AIFileInfo;
186
+
187
+ /** onArtifactClick 返回值(snake_case) */
188
+ type ArtifactUrlResult = {
189
+ download_url?: string;
190
+ preview_url?: string;
191
+ };
192
+ ```
193
+
194
+ ## 唯一键规则
195
+
196
+ 会话内以 **`outputId`** 作为文件产物唯一键:
197
+
198
+ - 同一 `outputId` 在多条 `AssistantMessage` 中出现时,`sessionArtifacts` 去重并保留**最后一次**出现的文件信息
199
+ - `activeArtifactId`、列表 `:key`、`select` 事件参数均使用 `outputId`
200
+ - 文件名可能重复,**不可**作为唯一键
201
+
202
+ ## 完整触发链路
203
+
204
+ ```
205
+ ArtifactFileCard(点击)
206
+ └─ useArtifactPreviewConsumer().openPreview({ file })
207
+ └─ useArtifactPreviewProvider(ChatContainer)
208
+ ├─ activeArtifactId = file.outputId
209
+ └─ onOpen(outputId) → addCustomTab(FILE_ARTIFACT_TAB_NAME) 展开并选中
210
+ └─ FileArtifactPanel(列表 + 下载头,@select → setActiveArtifactId)
211
+ └─ ArtifactPreviewHost(loader + 分类型 renderer)
212
+
213
+ 容器初始化
214
+ └─ ensureCustomTab(FILE_ARTIFACT_TAB_NAME) 常驻挂上(不展开侧栏);因 order:-1 排在首位,
215
+ 未主动切换过 Tab 时会成为默认选中面板;无产物时由面板展示整块空态
216
+
217
+ sessionArtifacts 变化
218
+ └─ 仅维护命中态(无产物清空,命中项失效回落第一个);不增删 Tab
219
+ ```
220
+
221
+ 分类型预览策略见 [FileArtifactPanel 预览机制](../components/message/file-artifact-panel#预览机制)。
222
+
223
+ ## 设计特点
224
+
225
+ - **职责单一**:composable 不直接调用 `useCustomTab`,侧栏 Tab 打开逻辑由 `onOpen` 注入;也不做正文 fetch / iframe 渲染
226
+ - **ShallowRef 优先**:`activeArtifactId` 使用 `shallowRef`,避免不必要的深层响应式开销
227
+ - **Consumer 兜底**:`useArtifactPreviewConsumer` 无 Provider 时返回 `undefined`,文件卡片在无容器上下文时自动不可点击
228
+ - **与 useCustomTab 协作**:点击卡片走 `addCustomTab`(展开 + 选中);容器初始化时走 `ensureCustomTab` 常驻挂载(不展开;因 `order: -1` 在未主动切换前会成为默认选中),无产物也不移除,由面板展示空态
229
+
230
+ ## 关联组件
231
+
232
+ - [ChatContainer](../components/setup/chat-container) — Provider 主场景,内置「文件产物」Tab
233
+ - [FileArtifactPanel](../components/message/file-artifact-panel) — 侧栏列表与预览 Host 挂载
234
+ - [AssistantMessage](../components/message/assistant-message) — 文件产物来源(`property.artifacts`)
235
+ - [useCustomTab](./use-custom-tab) — `addCustomTab` / `ensureCustomTab` 分工
@@ -0,0 +1,203 @@
1
+ # useClipboard
2
+
3
+ > 导入:`import { useClipboard } from '@blueking/chat-x'` | since 1.0.0
4
+
5
+ useClipboard 返回 { copy },copy(text) 将字符串写入剪贴板,返回 Promise<void>。 优先使用 navigator.clipboard.writeText,失败或不支持时降级为隐藏 textarea + execCommand('copy')。 成功/失败均通过 bkui-vue Message 提示,调用方无需处理结果。CodeContent、MessageContainer、UserMessage 的复制能力内部使用。
6
+
7
+ **关联**:code-content(复制代码块时取 innerText 后调用 copy)、message-container(AI 消息复制工具回调)、user-message(用户消息复制工具回调)
8
+
9
+ ---
10
+
11
+ # useClipboard 剪贴板
12
+
13
+ > **分类**:composable
14
+
15
+ 复制文本到剪贴板的组合式函数。内置两级降级策略,并自动通过 bkui-vue `Message` 提示复制结果,调用方无需关心成功/失败处理。
16
+
17
+ ## 执行流程
18
+
19
+ ```
20
+ copy(text)
21
+
22
+ ├── isClipboardApiSupported(模块加载时判断一次)
23
+ │ = typeof navigator !== 'undefined' && 'clipboard' in navigator
24
+
25
+ ├── true → navigator.clipboard.writeText(text)
26
+ │ ├── 成功 → success = true
27
+ │ └── 失败(权限拒绝等)→ legacyCopy(text)
28
+
29
+ └── false → legacyCopy(text)
30
+ (创建隐藏 textarea,document.execCommand('copy'),finally 清理 DOM)
31
+
32
+ └── Message({ message: t('复制成功/失败'), theme: 'success/error' })
33
+ ```
34
+
35
+ > **注意**:`copy` 返回 `Promise<void>`,调用方无法获取成功/失败结果;通知由内部自动弹出,不可关闭或自定义。
36
+
37
+ ## 基础用法
38
+
39
+ ```vue
40
+ <template>
41
+ <button @click="handleCopy">复制文本</button>
42
+ </template>
43
+
44
+ <script setup lang="ts">
45
+ import { useClipboard } from '@blueking/chat-x';
46
+
47
+ const { copy } = useClipboard();
48
+
49
+ const handleCopy = () => {
50
+ copy('Hello, World!');
51
+ // 自动弹出"复制成功/失败"提示,无需额外处理
52
+ };
53
+ </script>
54
+ ```
55
+
56
+ ## 复制代码块内容
57
+
58
+ `CodeContent` 组件内部的实际用法,取 DOM 元素的 `innerText` 避免 HTML 实体:
59
+
60
+ ```vue
61
+ <template>
62
+ <div class="code-block">
63
+ <pre ref="codeRef"><code>{{ code }}</code></pre>
64
+ <button @click="handleCopyCode">复制代码</button>
65
+ </div>
66
+ </template>
67
+
68
+ <script setup lang="ts">
69
+ import { useTemplateRef } from 'vue';
70
+ import { useClipboard } from '@blueking/chat-x';
71
+
72
+ const codeRef = useTemplateRef<HTMLElement>('codeRef');
73
+ const { copy } = useClipboard();
74
+
75
+ // 使用 innerText 获取渲染后的纯文本,而非原始 token.content
76
+ const handleCopyCode = () => {
77
+ copy(codeRef.value?.innerText ?? '');
78
+ };
79
+ </script>
80
+ ```
81
+
82
+ ## 复制消息内容
83
+
84
+ `MessageContainer` 和 `UserMessage` 内部的实际用法:
85
+
86
+ ```vue
87
+ <script setup lang="ts">
88
+ import { useClipboard } from '@blueking/chat-x';
89
+
90
+ const { copy } = useClipboard();
91
+
92
+ // 在 onAction 工具回调中调用
93
+ const onAction = tool => {
94
+ if (tool.id === 'copy') {
95
+ copy(props.content ?? '');
96
+ }
97
+ };
98
+ </script>
99
+ ```
100
+
101
+ ## API
102
+
103
+ ### 返回值
104
+
105
+ | 属性名 | 类型 | 说明 |
106
+ | ------ | --------------------------------- | ---------------------------------------------------- |
107
+ | copy | `(text: string) => Promise<void>` | 复制文本;内部自动弹出结果提示,调用方无需处理返回值 |
108
+
109
+ ### copy(text)
110
+
111
+ ```typescript
112
+ const copy: (text: string) => Promise<void>;
113
+ ```
114
+
115
+ **参数:**
116
+
117
+ - `text: string`:要复制的文本内容
118
+
119
+ **内部行为:**
120
+
121
+ | 步骤 | 说明 |
122
+ | ------------ | -------------------------------------------------------------------------------------------------------------- |
123
+ | 1. 能力检测 | 模块导入时执行一次:`typeof navigator !== 'undefined' && 'clipboard' in navigator` |
124
+ | 2a. 现代路径 | `navigator.clipboard.writeText(text)`,捕获异常后降级到步骤 2b |
125
+ | 2b. 降级路径 | 创建隐藏 `textarea`(`position: fixed; left: -9999px; opacity: 0`),`execCommand('copy')`,`finally` 清理 DOM |
126
+ | 3. 通知 | 始终调用 `Message({ message: t('复制成功/失败'), theme: 'success/error' })` |
127
+
128
+ ## 实现源码
129
+
130
+ ```typescript
131
+ import { Message } from 'bkui-vue';
132
+ import { t } from '../lang/lang';
133
+
134
+ // 模块加载时检测一次,SSR 安全
135
+ const isClipboardApiSupported = typeof navigator !== 'undefined' && 'clipboard' in navigator;
136
+
137
+ const legacyCopy = (text: string): boolean => {
138
+ const textarea = document.createElement('textarea');
139
+ textarea.value = text;
140
+ textarea.style.cssText = 'position:fixed;left:-9999px;top:-9999px;opacity:0';
141
+ document.body.appendChild(textarea);
142
+ textarea.focus();
143
+ textarea.select();
144
+ try {
145
+ return document.execCommand('copy');
146
+ } catch {
147
+ return false;
148
+ } finally {
149
+ document.body.removeChild(textarea); // 无论成败都清理 DOM
150
+ }
151
+ };
152
+
153
+ export const useClipboard = () => {
154
+ const copy = async (text: string) => {
155
+ let success = false;
156
+ if (isClipboardApiSupported) {
157
+ try {
158
+ await navigator.clipboard.writeText(text);
159
+ success = true;
160
+ } catch {
161
+ success = legacyCopy(text); // Clipboard API 失败时降级
162
+ }
163
+ } else {
164
+ success = legacyCopy(text);
165
+ }
166
+ Message({
167
+ message: success ? t('复制成功') : t('复制失败'),
168
+ theme: success ? 'success' : 'error',
169
+ });
170
+ };
171
+
172
+ return { copy };
173
+ };
174
+ ```
175
+
176
+ ## 兼容性
177
+
178
+ | 策略 | 触发条件 | 要求 |
179
+ | ------------------------------- | --------------------------------------------- | ---------------------------------------------- |
180
+ | `navigator.clipboard.writeText` | 浏览器支持 Clipboard API | HTTPS 或 localhost;用户需授权剪贴板权限 |
181
+ | `document.execCommand('copy')` | 不支持 Clipboard API,或 `writeText` 抛出异常 | 需由用户事件(如 click)直接触发,否则可能失败 |
182
+
183
+ ## 内部使用场景
184
+
185
+ | 组件 | 用途 |
186
+ | ------------------ | ----------------------------------------- |
187
+ | `CodeContent` | 复制代码块,取 `codeRef.value?.innerText` |
188
+ | `MessageContainer` | 复制 AI 消息内容 |
189
+ | `UserMessage` | 复制用户消息内容 |
190
+
191
+ ## 注意事项
192
+
193
+ 1. **结果不对外暴露**:`copy` 返回 `Promise<void>`,调用方无法获知成功/失败;如需自定义通知,不应使用此 composable
194
+ 2. **通知使用 i18n**:消息文本通过 `t()` 函数处理,在多语言环境下自动切换,无法在外部覆盖
195
+ 3. **`isClipboardApiSupported` 模块级检测**:在文件 import 时执行一次(非响应式),SSR 环境下 `navigator` 为 `undefined` 时安全降级
196
+ 4. **`execCommand` 已废弃**:降级路径依赖 `document.execCommand('copy')`,该 API 在部分浏览器已标记废弃,但目前仍广泛可用
197
+ 5. **用户手势要求**:在没有用户交互上下文时(如定时器回调),两种方式都可能失败
198
+
199
+ ## 关联组件
200
+
201
+ - [CodeContent](../components/rendering/code-content) — 代码块复制
202
+ - [MessageContainer](../components/setup/message-container) — 助手消息复制
203
+ - [UserMessage](../components/message/user-message) — 用户消息复制