@blueking/chat-x 0.0.49-beta.1 → 0.0.49-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.
- package/dist/ag-ui/types/constants.d.ts +1 -0
- package/dist/ag-ui/types/file.d.ts +1 -1
- 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 +1 -2
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/use-artifact-preview-loader.d.ts +4 -2
- 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 +14 -2
- package/dist/composables/use-artifact-preview.d.ts +17 -20
- package/dist/composables/use-custom-tab.d.ts +2 -0
- package/dist/composables/use-message-group.d.ts +1 -2
- package/dist/index.css +1 -1
- package/dist/index.js +514 -469
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +3 -1
- package/dist/mcp/generated/docs/assistant-message.md +1 -1
- package/dist/mcp/generated/docs/chat-container.md +7 -6
- 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/file-artifact-panel.md +32 -36
- 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 +4 -21
- package/dist/mcp/generated/docs/message-render.md +4 -27
- package/dist/mcp/generated/docs/messages.md +5 -0
- package/dist/mcp/generated/docs/reasoning-message.md +2 -9
- package/dist/mcp/generated/docs/toolcall-render.md +8 -8
- package/dist/mcp/generated/docs/use-artifact-preview.md +45 -50
- package/dist/mcp/generated/docs/use-custom-tab.md +18 -5
- package/dist/mcp/generated/docs/use-message-group.md +13 -8
- package/dist/mcp/generated/docs/user-question-card.md +6 -3
- package/dist/mcp/generated/index.json +7 -7
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AI SUMMARY -->
|
|
2
2
|
## 快速了解
|
|
3
3
|
|
|
4
|
-
useArtifactPreviewProvider 维护 activeArtifactId
|
|
4
|
+
useArtifactPreviewProvider 维护 activeArtifactId(值为 outputId),openPreview 命中文件并触发 onOpen 打开侧栏 Tab; 并通过 getOnArtifactClick 封装 resolveArtifactUrls(TTL 8 分钟缓存;常规只传 file,force 可强制刷新,并发去重); useArtifactPreviewConsumer 在后代注入同一套 API。SessionArtifact 即 AIFileInfo,会话内以 outputId 为唯一键。 正文加载与分类型渲染不在本 composable,由 FileArtifactPanel 内 ArtifactPreviewHost 完成(重载键 outputId:type)。 FILE_ARTIFACT_TAB_NAME 标识固定「文件产物」Tab。
|
|
5
5
|
|
|
6
6
|
### 关联组件
|
|
7
7
|
- **chat-container** — Provider 主场景,聚合 sessionArtifacts 并挂载 FileArtifactPanel
|
|
@@ -15,12 +15,12 @@ useArtifactPreviewProvider 维护 activeArtifactId,openPreview 命中文件并
|
|
|
15
15
|
|
|
16
16
|
> **分类**:composable
|
|
17
17
|
|
|
18
|
-
Provider/Consumer 模式的文件产物预览状态管理。Provider 在 `ChatContainer` 中创建,负责维护当前命中的文件
|
|
18
|
+
Provider/Consumer 模式的文件产物预览状态管理。Provider 在 `ChatContainer` 中创建,负责维护当前命中的文件 `outputId`;Consumer 在深层 `ArtifactFileCard` 中注入,用于点击卡片触发预览。
|
|
19
19
|
|
|
20
20
|
**职责边界**:
|
|
21
21
|
|
|
22
|
-
- **本 composable**:维护「命中文件」与「URL
|
|
23
|
-
- **不在本 composable**:聚合会话文件列表、渲染预览面板、按类型 fetch 正文 / iframe 展示 —— 分别由 `useMessageGroup.sessionArtifacts`、`FileArtifactPanel`、内部 `ArtifactPreviewHost` + `useArtifactPreviewLoader`
|
|
22
|
+
- **本 composable**:维护「命中文件」与「URL 解析」(TTL 缓存 + 并发去重;常规 `resolveArtifactUrls(file)`,重试可 `force` 刷新);打开侧栏 Tab(`addCustomTab`)由容器通过 `onOpen` 注入
|
|
23
|
+
- **不在本 composable**:聚合会话文件列表、渲染预览面板、按类型 fetch 正文 / iframe 展示 —— 分别由 `useMessageGroup.sessionArtifacts`、`FileArtifactPanel`、内部 `ArtifactPreviewHost` + `useArtifactPreviewLoader` 承担(预览重载键为 `outputId:type`)
|
|
24
24
|
|
|
25
25
|
## 函数签名
|
|
26
26
|
|
|
@@ -31,12 +31,12 @@ function useArtifactPreviewProvider(options: {
|
|
|
31
31
|
/** 读取业务侧异步取链回调(getter 保持对 props 变更敏感) */
|
|
32
32
|
getOnArtifactClick?: () => OnArtifactClick | undefined;
|
|
33
33
|
/** 命中文件后触发:由容器负责 addCustomTab + 展开侧栏 + 选中 Tab */
|
|
34
|
-
onOpen: (
|
|
34
|
+
onOpen: (outputId: string) => void;
|
|
35
35
|
}): {
|
|
36
36
|
activeArtifactId: ShallowRef<string>;
|
|
37
37
|
canResolveArtifactUrl: ComputedRef<boolean>;
|
|
38
38
|
openPreview: (payload: OpenArtifactPreviewPayload) => void;
|
|
39
|
-
resolveArtifactUrls: (file: AIFileInfo) => Promise<ArtifactUrlResult>;
|
|
39
|
+
resolveArtifactUrls: (file: AIFileInfo, options?: { force?: boolean }) => Promise<ArtifactUrlResult>;
|
|
40
40
|
setActiveArtifactId: (id: string) => void;
|
|
41
41
|
};
|
|
42
42
|
```
|
|
@@ -50,18 +50,11 @@ function useArtifactPreviewConsumer():
|
|
|
50
50
|
activeArtifactId: Ref<string>;
|
|
51
51
|
canResolveArtifactUrl: ComputedRef<boolean>;
|
|
52
52
|
openPreview: (payload: OpenArtifactPreviewPayload) => void;
|
|
53
|
-
resolveArtifactUrls: (file: AIFileInfo) => Promise<ArtifactUrlResult>;
|
|
53
|
+
resolveArtifactUrls: (file: AIFileInfo, options?: { force?: boolean }) => Promise<ArtifactUrlResult>;
|
|
54
54
|
setActiveArtifactId: (id: string) => void;
|
|
55
55
|
};
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
### buildArtifactId
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
function buildArtifactId(messageUid: string, index: number, outputId: string): string;
|
|
62
|
-
// => `${messageUid}#${index}#${outputId}`
|
|
63
|
-
```
|
|
64
|
-
|
|
65
58
|
## 使用示例
|
|
66
59
|
|
|
67
60
|
### Provider(ChatContainer)
|
|
@@ -77,13 +70,14 @@ import {
|
|
|
77
70
|
} from '@blueking/chat-x';
|
|
78
71
|
import { t } from '@blueking/chat-x/lang';
|
|
79
72
|
|
|
80
|
-
const { addCustomTab, removeCustomTab } = useCustomTabProvider({ /* ... */ });
|
|
73
|
+
const { addCustomTab, ensureCustomTab, removeCustomTab } = useCustomTabProvider({ /* ... */ });
|
|
81
74
|
|
|
82
75
|
// 会话级文件产物聚合已内聚在 useMessageGroup,直接消费
|
|
83
76
|
const { sessionArtifacts } = useMessageGroup({ keyword, messages, selectedUserMessages });
|
|
84
77
|
|
|
85
78
|
const { activeArtifactId, setActiveArtifactId } = useArtifactPreviewProvider({
|
|
86
79
|
getOnArtifactClick: () => props.onArtifactClick,
|
|
80
|
+
// 点击文件卡片:展开侧栏并选中「文件产物」Tab
|
|
87
81
|
onOpen: () => {
|
|
88
82
|
addCustomTab({
|
|
89
83
|
closable: false,
|
|
@@ -94,13 +88,23 @@ const { activeArtifactId, setActiveArtifactId } = useArtifactPreviewProvider({
|
|
|
94
88
|
},
|
|
95
89
|
});
|
|
96
90
|
|
|
97
|
-
//
|
|
91
|
+
// 有产物时静默挂上 Tab(不抢焦点);无产物时清理
|
|
98
92
|
watch(sessionArtifacts, list => {
|
|
99
93
|
if (!list.length) {
|
|
100
94
|
removeCustomTab(FILE_ARTIFACT_TAB_NAME);
|
|
101
95
|
setActiveArtifactId('');
|
|
96
|
+
return;
|
|
102
97
|
}
|
|
103
|
-
|
|
98
|
+
ensureCustomTab({
|
|
99
|
+
closable: false,
|
|
100
|
+
label: t('文件产物'),
|
|
101
|
+
name: FILE_ARTIFACT_TAB_NAME,
|
|
102
|
+
order: -1,
|
|
103
|
+
});
|
|
104
|
+
if (!list.some(item => item.outputId === activeArtifactId.value)) {
|
|
105
|
+
setActiveArtifactId(list[0].outputId);
|
|
106
|
+
}
|
|
107
|
+
}, { immediate: true });
|
|
104
108
|
```
|
|
105
109
|
|
|
106
110
|
### Consumer(ArtifactFileCard)
|
|
@@ -118,18 +122,14 @@ const handleCardClick = () => {
|
|
|
118
122
|
props.onPreview(props.file);
|
|
119
123
|
return;
|
|
120
124
|
}
|
|
121
|
-
artifactPreview?.openPreview({
|
|
122
|
-
file: props.file,
|
|
123
|
-
index: props.index ?? 0,
|
|
124
|
-
messageUid: props.messageUid ?? '',
|
|
125
|
-
});
|
|
125
|
+
artifactPreview?.openPreview({ file: props.file });
|
|
126
126
|
};
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
### 侧栏列表内切换命中文件
|
|
130
130
|
|
|
131
131
|
```typescript
|
|
132
|
-
// FileArtifactPanel 列表点击 → emit select → 容器调用 setActiveArtifactId
|
|
132
|
+
// FileArtifactPanel 列表点击 → emit select(outputId) → 容器调用 setActiveArtifactId
|
|
133
133
|
// 右侧预览由面板内 ArtifactPreviewHost 消费 activeArtifact,自行取链并按类型渲染
|
|
134
134
|
<FileArtifactPanel
|
|
135
135
|
:active-id="activeArtifactId"
|
|
@@ -163,32 +163,27 @@ const onArtifactClick = async (file: AIFileInfo) => {
|
|
|
163
163
|
|
|
164
164
|
| 属性/方法名 | 类型 | 说明 |
|
|
165
165
|
| ------------------- | ----------------------------------------- | -------------------------------------------------------------------- |
|
|
166
|
-
| activeArtifactId | `ShallowRef<string>` | 当前命中的文件
|
|
166
|
+
| activeArtifactId | `ShallowRef<string>` | 当前命中的文件 `outputId` |
|
|
167
167
|
| canResolveArtifactUrl | `ComputedRef<boolean>` | 是否具备异步取链能力(有 `onArtifactClick` 时为 true,下载按钮据此显隐) |
|
|
168
|
-
| openPreview | `(payload: OpenArtifactPreviewPayload) => void` |
|
|
169
|
-
| resolveArtifactUrls | `(file: AIFileInfo) => Promise<ArtifactUrlResult>` | 调用 `onArtifactClick`
|
|
170
|
-
| setActiveArtifactId | `(id: string) => void` | 直接设置命中文件
|
|
168
|
+
| openPreview | `(payload: OpenArtifactPreviewPayload) => void` | 由文件卡片触发:以 `file.outputId` 更新命中态、调用 `onOpen` |
|
|
169
|
+
| resolveArtifactUrls | `(file: AIFileInfo, options?: { force?: boolean }) => Promise<ArtifactUrlResult>` | 调用 `onArtifactClick` 取链;成功结果按 `outputId` 缓存 8 分钟;**常规调用只传 `file`**,仅预览重试 / 强刷传 `{ force: true }`(不要传 `undefined` 作为第二参);并发去重 |
|
|
170
|
+
| setActiveArtifactId | `(id: string) => void` | 直接设置命中文件 `outputId`;侧栏列表内切换选中时使用 |
|
|
171
171
|
|
|
172
172
|
## 类型定义
|
|
173
173
|
|
|
174
174
|
```typescript
|
|
175
175
|
import type { AIFileInfo, ArtifactUrlResult, OnArtifactClick } from '@blueking/chat-x';
|
|
176
176
|
|
|
177
|
-
/**
|
|
177
|
+
/** 打开预览时的入参 */
|
|
178
178
|
type OpenArtifactPreviewPayload = {
|
|
179
179
|
file: AIFileInfo;
|
|
180
|
-
index: number;
|
|
181
|
-
messageUid: string;
|
|
182
180
|
};
|
|
183
181
|
|
|
184
182
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
183
|
+
* 会话级文件产物:以 outputId 为会话内唯一键(同 outputId 视为同一文件)。
|
|
184
|
+
* 拍平去重后即为 AIFileInfo,此处用别名标明语义。
|
|
187
185
|
*/
|
|
188
|
-
type SessionArtifact = AIFileInfo
|
|
189
|
-
artifactId: string;
|
|
190
|
-
messageUid: string;
|
|
191
|
-
};
|
|
186
|
+
type SessionArtifact = AIFileInfo;
|
|
192
187
|
|
|
193
188
|
/** onArtifactClick 返回值(snake_case) */
|
|
194
189
|
type ArtifactUrlResult = {
|
|
@@ -197,28 +192,27 @@ type ArtifactUrlResult = {
|
|
|
197
192
|
};
|
|
198
193
|
```
|
|
199
194
|
|
|
200
|
-
##
|
|
195
|
+
## 唯一键规则
|
|
201
196
|
|
|
202
|
-
|
|
197
|
+
会话内以 **`outputId`** 作为文件产物唯一键:
|
|
203
198
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
- `messageUid`:所属 `AssistantMessage` 的 `uid`(回退 `String(id)`)
|
|
209
|
-
- `index`:文件在所属消息 `property.artifacts` 数组中的下标
|
|
210
|
-
- `outputId`:文件自身的 `AIFileInfo.outputId`
|
|
199
|
+
- 同一 `outputId` 在多条 `AssistantMessage` 中出现时,`sessionArtifacts` 去重并保留**最后一次**出现的文件信息
|
|
200
|
+
- `activeArtifactId`、列表 `:key`、`select` 事件参数均使用 `outputId`
|
|
201
|
+
- 文件名可能重复,**不可**作为唯一键
|
|
211
202
|
|
|
212
203
|
## 完整触发链路
|
|
213
204
|
|
|
214
205
|
```
|
|
215
206
|
ArtifactFileCard(点击)
|
|
216
|
-
└─ useArtifactPreviewConsumer().openPreview({ file
|
|
207
|
+
└─ useArtifactPreviewConsumer().openPreview({ file })
|
|
217
208
|
└─ useArtifactPreviewProvider(ChatContainer)
|
|
218
|
-
├─ activeArtifactId =
|
|
219
|
-
└─ onOpen(
|
|
209
|
+
├─ activeArtifactId = file.outputId
|
|
210
|
+
└─ onOpen(outputId) → addCustomTab(FILE_ARTIFACT_TAB_NAME) 展开并选中
|
|
220
211
|
└─ FileArtifactPanel(列表 + 下载头,@select → setActiveArtifactId)
|
|
221
212
|
└─ ArtifactPreviewHost(loader + 分类型 renderer)
|
|
213
|
+
|
|
214
|
+
sessionArtifacts 变化(有产物)
|
|
215
|
+
└─ ensureCustomTab(FILE_ARTIFACT_TAB_NAME) 静默挂上,不抢当前选中(如执行情况)
|
|
222
216
|
```
|
|
223
217
|
|
|
224
218
|
分类型预览策略见 [FileArtifactPanel 预览机制](../components/message/file-artifact-panel#预览机制)。
|
|
@@ -228,10 +222,11 @@ ArtifactFileCard(点击)
|
|
|
228
222
|
- **职责单一**:composable 不直接调用 `useCustomTab`,侧栏 Tab 打开逻辑由 `onOpen` 注入;也不做正文 fetch / iframe 渲染
|
|
229
223
|
- **ShallowRef 优先**:`activeArtifactId` 使用 `shallowRef`,避免不必要的深层响应式开销
|
|
230
224
|
- **Consumer 兜底**:`useArtifactPreviewConsumer` 无 Provider 时返回 `undefined`,文件卡片在无容器上下文时自动不可点击
|
|
231
|
-
- **与 useCustomTab
|
|
225
|
+
- **与 useCustomTab 协作**:点击卡片走 `addCustomTab`(展开 + 选中);会话已有产物时走 `ensureCustomTab`(只挂载,不抢焦点);无文件产物时由容器 `removeCustomTab` 清理
|
|
232
226
|
|
|
233
227
|
## 关联组件
|
|
234
228
|
|
|
235
229
|
- [ChatContainer](../components/setup/chat-container) — Provider 主场景,内置「文件产物」Tab
|
|
236
230
|
- [FileArtifactPanel](../components/message/file-artifact-panel) — 侧栏列表与预览 Host 挂载
|
|
237
|
-
- [AssistantMessage](../components/message/assistant-message) — 文件产物来源(`property.artifacts`)
|
|
231
|
+
- [AssistantMessage](../components/message/assistant-message) — 文件产物来源(`property.artifacts`)
|
|
232
|
+
- [useCustomTab](./use-custom-tab) — `addCustomTab` / `ensureCustomTab` 分工
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AI SUMMARY -->
|
|
2
2
|
## 快速了解
|
|
3
3
|
|
|
4
|
-
useCustomTabProvider 返回 tabs、selectedTab、isCollapse 及 add/remove/selectCustomTab,并通过 provide 共享;可选 onTabChange 在切换时拉取数据。 useCustomTabConsumer 在后代注入同一套 API,常用于侧栏动态节点详情等。EXECUTION_TAB_NAME 标识默认「执行情况」Tab。 ChatContainer 侧栏集成 Provider 与 Tab UI。
|
|
4
|
+
useCustomTabProvider 返回 tabs、selectedTab、isCollapse 及 add/ensure/remove/selectCustomTab,并通过 provide 共享;可选 onTabChange 在切换时拉取数据。 ensureCustomTab 只挂载/合并元信息,不展开侧栏、不切换选中;addCustomTab 会展开并选中。 useCustomTabConsumer 在后代注入同一套 API,常用于侧栏动态节点详情等。EXECUTION_TAB_NAME 标识默认「执行情况」Tab。 ChatContainer 侧栏集成 Provider 与 Tab UI。
|
|
5
5
|
|
|
6
6
|
### 关联组件
|
|
7
7
|
- **chat-container** — Provider 与侧栏 Tab 主场景
|
|
@@ -30,6 +30,7 @@ function useCustomTabProvider<T extends Record<string, unknown>>(options: {
|
|
|
30
30
|
selectedTab: Ref<CustomTab<T>>;
|
|
31
31
|
isCollapse: ShallowRef<boolean>;
|
|
32
32
|
addCustomTab: (tab: CustomTab<T>) => void;
|
|
33
|
+
ensureCustomTab: (tab: CustomTab<T>) => void;
|
|
33
34
|
removeCustomTab: (tabName: string) => void;
|
|
34
35
|
selectCustomTab: (tab: CustomTab<T>) => void;
|
|
35
36
|
resetCustomTab: () => void;
|
|
@@ -46,6 +47,7 @@ function useCustomTabConsumer<T extends Record<string, unknown>>():
|
|
|
46
47
|
displayTabs: ComputedRef<CustomTab<T>[]>;
|
|
47
48
|
selectedTab: ShallowRef<CustomTab<T> | null>;
|
|
48
49
|
addCustomTab: (tab: CustomTab<T>) => void;
|
|
50
|
+
ensureCustomTab: (tab: CustomTab<T>) => void;
|
|
49
51
|
removeCustomTab: (tabName: string) => void;
|
|
50
52
|
selectCustomTab: (tab: CustomTab<T>) => void;
|
|
51
53
|
resetCustomTab: () => void;
|
|
@@ -59,7 +61,7 @@ function useCustomTabConsumer<T extends Record<string, unknown>>():
|
|
|
59
61
|
```typescript
|
|
60
62
|
import { useCustomTabProvider, EXECUTION_TAB_NAME } from '@blueking/chat-x';
|
|
61
63
|
|
|
62
|
-
const { tabs, selectedTab, isCollapse, addCustomTab, removeCustomTab, selectCustomTab, resetCustomTab } =
|
|
64
|
+
const { tabs, selectedTab, isCollapse, addCustomTab, ensureCustomTab, removeCustomTab, selectCustomTab, resetCustomTab } =
|
|
63
65
|
useCustomTabProvider({
|
|
64
66
|
onTabChange: async tab => {
|
|
65
67
|
// Tab 切换时加载数据
|
|
@@ -76,7 +78,7 @@ import { useCustomTabConsumer } from '@blueking/chat-x';
|
|
|
76
78
|
|
|
77
79
|
const tabManager = useCustomTabConsumer();
|
|
78
80
|
|
|
79
|
-
// 添加一个自定义 Tab
|
|
81
|
+
// 添加一个自定义 Tab(展开侧栏并选中)
|
|
80
82
|
tabManager?.addCustomTab({
|
|
81
83
|
name: 'node-detail-123',
|
|
82
84
|
label: '节点详情',
|
|
@@ -86,6 +88,14 @@ tabManager?.addCustomTab({
|
|
|
86
88
|
},
|
|
87
89
|
});
|
|
88
90
|
|
|
91
|
+
// 仅确保 Tab 存在(不展开、不切换选中)—— 如侧栏已因执行情况打开时同步挂上文件产物
|
|
92
|
+
tabManager?.ensureCustomTab({
|
|
93
|
+
name: 'file-artifact',
|
|
94
|
+
label: '文件产物',
|
|
95
|
+
closable: false,
|
|
96
|
+
order: -1,
|
|
97
|
+
});
|
|
98
|
+
|
|
89
99
|
// 移除 Tab
|
|
90
100
|
tabManager?.removeCustomTab('node-detail-123');
|
|
91
101
|
```
|
|
@@ -106,7 +116,8 @@ tabManager?.removeCustomTab('node-detail-123');
|
|
|
106
116
|
| displayTabs | `ComputedRef<CustomTab[]>` | Tab 栏实际展示列表:过滤 `visible === false`,按 `order` 升序稳定排序 |
|
|
107
117
|
| selectedTab | `Ref<CustomTab>` | 当前选中的 Tab;选中项被隐藏时自动回退到首个可见 Tab |
|
|
108
118
|
| isCollapse | `ShallowRef<boolean>` | 侧边栏折叠状态;`addCustomTab` 时自动设为 `false` |
|
|
109
|
-
| addCustomTab | `(tab: CustomTab) => void` |
|
|
119
|
+
| addCustomTab | `(tab: CustomTab) => void` | 添加/合并 Tab,**展开侧栏并选中**目标 Tab |
|
|
120
|
+
| ensureCustomTab | `(tab: CustomTab) => void` | 添加/合并 Tab,**不展开、不切换选中**;用于静默挂载(如文件产物) |
|
|
110
121
|
| removeCustomTab | `(tabName: string) => void` | 移除指定 Tab |
|
|
111
122
|
| selectCustomTab | `(tab: CustomTab) => void` | 切换到指定 Tab,触发 `onTabChange` 回调 |
|
|
112
123
|
| resetCustomTab | `() => void` | 重置为仅保留「执行情况」Tab、折叠侧栏并选中默认 Tab;`ChatContainer` 在卸载时调用,避免残留自定义 Tab |
|
|
@@ -136,7 +147,9 @@ interface CustomTab<T = Record<string, unknown>> {
|
|
|
136
147
|
- 排序为稳定排序,`order` 相同的 Tab 保持插入先后顺序
|
|
137
148
|
- 选中的 Tab 被配置隐藏时,内容不再渲染,自动切换到首个可见 Tab
|
|
138
149
|
- `addCustomTab` 同时展开侧边栏(`isCollapse = false`)并在 `nextTick` 后自动选中目标 Tab;同名 Tab 合并更新
|
|
150
|
+
- `ensureCustomTab` 与 `addCustomTab` 共用合并逻辑,但不改 `isCollapse`、不切换 `selectedTab`;适合「侧栏已打开时同步挂上文件产物」等场景
|
|
139
151
|
|
|
140
152
|
## 关联组件
|
|
141
153
|
|
|
142
|
-
- [ChatContainer](../components/setup/chat-container) — 侧栏 Tab 与自定义面板
|
|
154
|
+
- [ChatContainer](../components/setup/chat-container) — 侧栏 Tab 与自定义面板
|
|
155
|
+
- [useArtifactPreview](./use-artifact-preview) — 文件产物 Tab:有产物时 `ensureCustomTab`,点击卡片时 `addCustomTab`
|
|
@@ -119,24 +119,29 @@ const isExecutionMessage = (m: Message): boolean => {
|
|
|
119
119
|
|
|
120
120
|
## sessionArtifacts 会话级文件产物
|
|
121
121
|
|
|
122
|
-
`sessionArtifacts` 拍平当前会话所有 `AssistantMessage.property.artifacts`,供 `ChatContainer` 侧栏「文件产物」Tab
|
|
122
|
+
`sessionArtifacts` 拍平当前会话所有 `AssistantMessage.property.artifacts`,供 `ChatContainer` 侧栏「文件产物」Tab 聚合预览。以 **`outputId`** 为会话内唯一键去重(同 `outputId` 视为同一文件),保留最后一次出现的文件信息,列表顺序与「最后一次出现」的相对顺序一致:
|
|
123
123
|
|
|
124
124
|
```typescript
|
|
125
125
|
const sessionArtifacts = computed(() => {
|
|
126
|
-
|
|
126
|
+
// delete + set:同 key 覆盖内容,并把该项挪到 Map 末尾,保证「最后出现」顺序
|
|
127
|
+
const byOutputId = new Map();
|
|
127
128
|
for (const message of messages.value) {
|
|
128
129
|
if (message.role !== MessageRole.Assistant) continue;
|
|
129
130
|
const artifacts = message.property?.artifacts;
|
|
130
131
|
if (!artifacts?.length) continue;
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
for (const file of artifacts) {
|
|
133
|
+
if (byOutputId.has(file.outputId)) {
|
|
134
|
+
byOutputId.delete(file.outputId);
|
|
135
|
+
}
|
|
136
|
+
byOutputId.set(file.outputId, file);
|
|
137
|
+
}
|
|
135
138
|
}
|
|
136
|
-
return
|
|
139
|
+
return Array.from(byOutputId.values());
|
|
137
140
|
});
|
|
138
141
|
```
|
|
139
142
|
|
|
143
|
+
> `SessionArtifact` 即为 `AIFileInfo` 别名;文件名可能重复,不可作唯一键。
|
|
144
|
+
|
|
140
145
|
预览命中与取链见 [useArtifactPreview](./use-artifact-preview);侧栏列表与分类型预览(`ArtifactPreviewHost`)见 [FileArtifactPanel](../components/message/file-artifact-panel)。
|
|
141
146
|
|
|
142
147
|
## 待审批统计
|
|
@@ -209,7 +214,7 @@ const {
|
|
|
209
214
|
| ---------------- | ----------------------------- | --------------------------------------------------------------------------- |
|
|
210
215
|
| messageGroups | `Ref<MessageGroup[]>` | 完整消息分组列表 |
|
|
211
216
|
| executionGroups | `ComputedRef<MessageGroup[]>` | 仅包含执行类消息的分组(工具调用 + FlowAgent),自动提取 `userMessageTitle` |
|
|
212
|
-
| sessionArtifacts | `ComputedRef<SessionArtifact[]>` | 拍平会话所有 AssistantMessage
|
|
217
|
+
| sessionArtifacts | `ComputedRef<SessionArtifact[]>` | 拍平会话所有 AssistantMessage 文件产物,按 `outputId` 去重(保留最后一次) |
|
|
213
218
|
| pendingApprovalCount | `ComputedRef<number>` | 当前消息中待审批 AI Dev 审批中断的数量 |
|
|
214
219
|
| pendingApprovalTipText | `ComputedRef<string>` | 待审批阻塞发送提示文案;无待审批时为空字符串 |
|
|
215
220
|
| isShareMode | `ShallowRef<boolean>` | 是否处于分享模式 |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AI SUMMARY -->
|
|
2
2
|
## 快速了解
|
|
3
3
|
|
|
4
|
-
渲染 UserQuestion
|
|
4
|
+
渲染 UserQuestion 中断的待回答面板:一次只展示一题,标题栏提供题号切换, Footer 展示已完成进度;支持单选自动跳下一题、多选/Others、完成与跳过。 源码位置:src/components/chat-message/interrupt-message/user-question/user-question-card.vue。
|
|
5
5
|
|
|
6
6
|
### 关联组件
|
|
7
7
|
- **interrupt-message** — outcome.success 时挂载 UserQuestionAnsweredCard 回显回答
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
- **源码位置**:`src/components/chat-message/interrupt-message/user-question/user-question-card.vue`
|
|
18
18
|
- **能力域**:Agent 能力
|
|
19
|
-
- **能力说明**:渲染 UserQuestion
|
|
19
|
+
- **能力说明**:渲染 UserQuestion 中断的待回答面板;一次一题分页切换,支持单选/多选、Others、跳过与已完成进度。
|
|
20
20
|
|
|
21
21
|
> **能力域**:Agent 能力
|
|
22
22
|
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
|
|
25
25
|
## 交互能力
|
|
26
26
|
|
|
27
|
+
- **一次一题**:正文只展示当前题;标题栏右侧提供 `< 当前题 / 总题数 >` 切换定位(单题为 `1 / 1`),首末题对应箭头禁用。
|
|
28
|
+
- **自动跳转**:单选预设选项从未答变为有效答时,自动跳到下一题(最后一题停留);多选与 Others 不自动跳,靠箭头切换。
|
|
29
|
+
- **已完成进度**:Footer 左侧展示「已完成 N 题」;右侧为「跳过」+「完成」。
|
|
27
30
|
- **单选 / 多选**:每道题通过 `multiSelect` 控制选择行为;未传时不展示单选/多选标签,默认仍按单选处理。
|
|
28
31
|
- **Others 自由输入**:默认 [UserQuestionChoice](/components/agent/user-question-choice) 为每道题追加 `label: 'others'` 输入项,输入文本写入 `answer[].description`。
|
|
29
32
|
- **自定义作答形态**:通过 `#question` slot 可替换默认选择题,渲染任意表单;作答有效时调用 `setAnswer` 回传 `UserQuestionAnswerItem`,无效时传 `undefined`。
|
|
@@ -142,7 +145,7 @@ const payload = {
|
|
|
142
145
|
/>
|
|
143
146
|
```
|
|
144
147
|
|
|
145
|
-
-
|
|
148
|
+
- 卡片内「跳过 / 完成」→ `onInterruptResume(payload, interrupt)`(Footer 右置,跳过在前、完成在后)
|
|
146
149
|
- 输入框直接发送 → `onSendMessage(content, docSchema, { interrupt, payload })`,其中 `payload` 由 `buildSkipResumePayload(interrupt)` 生成(`status: 'cancelled'`)
|
|
147
150
|
|
|
148
151
|
## 工具函数 buildSkipResumePayload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "2.0.0",
|
|
3
|
-
"generatedAt": "2026-
|
|
3
|
+
"generatedAt": "2026-08-06T04:13:41.238Z",
|
|
4
4
|
"domains": {
|
|
5
5
|
"setup": {
|
|
6
6
|
"label": "对话搭建",
|
|
@@ -362,8 +362,8 @@
|
|
|
362
362
|
"name": "UserQuestionCard 用户问题中断",
|
|
363
363
|
"slug": "user-question-card",
|
|
364
364
|
"kind": "component",
|
|
365
|
-
"description": "渲染 UserQuestion
|
|
366
|
-
"aiSummary": "渲染 UserQuestion
|
|
365
|
+
"description": "渲染 UserQuestion 中断的待回答面板;一次一题分页切换,支持单选/多选、Others、跳过与已完成进度。",
|
|
366
|
+
"aiSummary": "渲染 UserQuestion 中断的待回答面板:一次只展示一题,标题栏提供题号切换, Footer 展示已完成进度;支持单选自动跳下一题、多选/Others、完成与跳过。 源码位置:src/components/chat-message/interrupt-message/user-question/user-question-card.vue。",
|
|
367
367
|
"relatedComponents": [
|
|
368
368
|
{
|
|
369
369
|
"slug": "interrupt-message",
|
|
@@ -1115,7 +1115,7 @@
|
|
|
1115
1115
|
"slug": "file-artifact-panel",
|
|
1116
1116
|
"kind": "component",
|
|
1117
1117
|
"description": "汇总当前会话全部文件产物,支持搜索、选中与分类型预览,挂载在 ChatContainer 侧栏「文件产物」Tab。",
|
|
1118
|
-
"aiSummary": "汇总当前会话所有 AssistantMessage 的 artifacts
|
|
1118
|
+
"aiSummary": "汇总当前会话所有 AssistantMessage 的 artifacts(按 outputId 去重),支持关键词搜索、列表选中与下载; 预览区委托 ArtifactPreviewHost:按类型走 text_from_download(html / markdown / md / txt / json) 或 preview_url_iframe(其余类型);download_url / preview_url 经 onArtifactClick 异步获取(TTL 缓存,重试 force); 预览重载键为 outputId:type;常规 resolveArtifactUrls 只传 file。 源码位置:src/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue。",
|
|
1119
1119
|
"relatedComponents": [
|
|
1120
1120
|
{
|
|
1121
1121
|
"slug": "assistant-message",
|
|
@@ -1586,7 +1586,7 @@
|
|
|
1586
1586
|
"slug": "use-artifact-preview",
|
|
1587
1587
|
"kind": "composable",
|
|
1588
1588
|
"description": "Provider/Consumer 模式的文件产物预览状态管理,用于 ChatContainer 侧栏「文件产物」Tab 的命中与切换。 Provider 在 ChatContainer 中创建,Consumer 在深层文件卡片中注入使用。",
|
|
1589
|
-
"aiSummary": "useArtifactPreviewProvider 维护 activeArtifactId
|
|
1589
|
+
"aiSummary": "useArtifactPreviewProvider 维护 activeArtifactId(值为 outputId),openPreview 命中文件并触发 onOpen 打开侧栏 Tab; 并通过 getOnArtifactClick 封装 resolveArtifactUrls(TTL 8 分钟缓存;常规只传 file,force 可强制刷新,并发去重); useArtifactPreviewConsumer 在后代注入同一套 API。SessionArtifact 即 AIFileInfo,会话内以 outputId 为唯一键。 正文加载与分类型渲染不在本 composable,由 FileArtifactPanel 内 ArtifactPreviewHost 完成(重载键 outputId:type)。 FILE_ARTIFACT_TAB_NAME 标识固定「文件产物」Tab。",
|
|
1590
1590
|
"relatedComponents": [
|
|
1591
1591
|
{
|
|
1592
1592
|
"slug": "chat-container",
|
|
@@ -1666,7 +1666,7 @@
|
|
|
1666
1666
|
"slug": "use-custom-tab",
|
|
1667
1667
|
"kind": "composable",
|
|
1668
1668
|
"description": "Provider/Consumer 模式的自定义 Tab 管理,用于 `ChatContainer` 侧边栏的 Tab 动态管理。Provider 在 `ChatContainer` 中创建,Consumer 在任意后代组件中注入使用。",
|
|
1669
|
-
"aiSummary": "useCustomTabProvider 返回 tabs、selectedTab、isCollapse 及 add/remove/selectCustomTab,并通过 provide 共享;可选 onTabChange 在切换时拉取数据。 useCustomTabConsumer 在后代注入同一套 API,常用于侧栏动态节点详情等。EXECUTION_TAB_NAME 标识默认「执行情况」Tab。 ChatContainer 侧栏集成 Provider 与 Tab UI。",
|
|
1669
|
+
"aiSummary": "useCustomTabProvider 返回 tabs、selectedTab、isCollapse 及 add/ensure/remove/selectCustomTab,并通过 provide 共享;可选 onTabChange 在切换时拉取数据。 ensureCustomTab 只挂载/合并元信息,不展开侧栏、不切换选中;addCustomTab 会展开并选中。 useCustomTabConsumer 在后代注入同一套 API,常用于侧栏动态节点详情等。EXECUTION_TAB_NAME 标识默认「执行情况」Tab。 ChatContainer 侧栏集成 Provider 与 Tab UI。",
|
|
1670
1670
|
"relatedComponents": [
|
|
1671
1671
|
{
|
|
1672
1672
|
"slug": "chat-container",
|
|
@@ -1787,7 +1787,7 @@
|
|
|
1787
1787
|
"slug": "constants",
|
|
1788
1788
|
"kind": "type",
|
|
1789
1789
|
"description": "`@blueking/chat-x` 导出的常量和枚举类型。",
|
|
1790
|
-
"aiSummary": "汇总 MessageRole、MessageStatus(含 Fetching
|
|
1790
|
+
"aiSummary": "汇总 MessageRole、MessageStatus(含 Fetching 请求中、Complete/Completed 完成态兼容)、MessageContentType、MessageToolsStatus、MessageState、Z-Index 与 CONST_MESSAGE_TOOLS 等导出常量。 用于构造消息、配置 MessageContainer 工具栏与输入态,以及层级与默认快捷指令。与类型 messages 配套使用。",
|
|
1791
1791
|
"relatedComponents": [
|
|
1792
1792
|
{
|
|
1793
1793
|
"slug": "message-tools",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blueking/chat-x",
|
|
3
|
-
"version": "0.0.49-beta.
|
|
3
|
+
"version": "0.0.49-beta.2",
|
|
4
4
|
"description": "蓝鲸智云 AI Chat 组件库 —— 遵循 AG-UI,为 AI Agent 和人类开发者共同设计的对话 UI 组件库。",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"vitepress": "2.0.0-alpha.16",
|
|
80
80
|
"vitest": "^4.0.18",
|
|
81
81
|
"vue-tsc": "^3.1.4",
|
|
82
|
-
"@blueking/chat-helper": "0.0.12-beta.
|
|
82
|
+
"@blueking/chat-helper": "0.0.12-beta.14"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|
|
85
85
|
"dev": "vite --config vite.config.ts",
|