@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.
- package/README.md +15 -8
- package/dist/ag-ui/types/contents.d.ts +2 -0
- package/dist/ag-ui/types/messages.d.ts +6 -1
- package/dist/common/constants.d.ts +2 -0
- package/dist/components/ai-buttons/add-menu-btn/add-menu-btn.vue.d.ts +24 -0
- package/dist/components/chat-content/collapsible-content/collapsible-content.vue.d.ts +28 -0
- package/dist/components/chat-content/file-content/upload-file-item.vue.d.ts +5 -2
- package/dist/components/chat-content/file-content/upload-image-item.vue.d.ts +1 -0
- package/dist/components/chat-content/flow-agent-content/use-flow-node-actions.d.ts +1 -1
- package/dist/components/chat-input/ai-slash-input/ai-slash-input.vue.d.ts +21 -10
- package/dist/components/chat-input/ai-slash-input/command.d.ts +6 -3
- package/dist/components/chat-input/ai-slash-input/constants.d.ts +2 -0
- package/dist/components/chat-input/ai-slash-input/use-menu-trigger.d.ts +17 -0
- package/dist/components/chat-input/build-default-placeholder.d.ts +9 -3
- package/dist/components/chat-input/chat-input.vue.d.ts +30 -18
- package/dist/components/chat-input/input-menu/constants.d.ts +60 -0
- package/dist/components/chat-input/input-menu/index.d.ts +4 -0
- package/dist/components/chat-input/input-menu/input-menu-option.vue.d.ts +14 -0
- package/dist/components/chat-input/input-menu/input-menu-panel.vue.d.ts +17 -0
- package/dist/components/chat-input/input-menu/use-input-menu.d.ts +22 -0
- package/dist/components/chat-input/model-selector/types.d.ts +3 -2
- package/dist/components/chat-message/message-container/message-container.vue.d.ts +1 -1
- package/dist/components/chat-message/user-message/user-message.vue.d.ts +1 -1
- package/dist/components/mention/create-mention-tippy.d.ts +27 -0
- package/dist/components/mention/index.d.ts +4 -0
- 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
- 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
- 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
- package/dist/components/resource-icon/index.d.ts +1 -0
- package/dist/components/resource-icon/resource-icon.vue.d.ts +11 -0
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/use-artifact-preview.d.ts +5 -2
- package/dist/composables/use-common.d.ts +0 -9
- package/dist/composables/use-global-config.d.ts +4 -0
- package/dist/composables/use-input-mention.d.ts +13 -0
- package/dist/composables/use-message-group.d.ts +1011 -72
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/input.d.ts +21 -0
- package/dist/icons/menu-item.d.ts +22 -0
- package/dist/index.css +1 -1
- package/dist/index.js +4953 -4429
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +13 -3
- package/dist/mcp/generated/docs/activity-message.md +3 -3
- package/dist/mcp/generated/docs/add-menu-btn.md +91 -0
- package/dist/mcp/generated/docs/ai-slash-input.md +72 -26
- package/dist/mcp/generated/docs/assistant-message.md +7 -1
- package/dist/mcp/generated/docs/chat-container.md +45 -6
- package/dist/mcp/generated/docs/chat-input.md +327 -483
- package/dist/mcp/generated/docs/collapsible-content.md +78 -0
- package/dist/mcp/generated/docs/constants.md +36 -3
- package/dist/mcp/generated/docs/execution-summary.md +1 -23
- package/dist/mcp/generated/docs/file-artifact-panel.md +24 -5
- package/dist/mcp/generated/docs/file-content.md +4 -0
- package/dist/mcp/generated/docs/file-upload-btn.md +15 -12
- package/dist/mcp/generated/docs/flow-agent-content.md +10 -12
- package/dist/mcp/generated/docs/input-menu-panel.md +166 -0
- package/dist/mcp/generated/docs/mention-tag.md +83 -0
- package/dist/mcp/generated/docs/mention-text.md +86 -0
- package/dist/mcp/generated/docs/message-container.md +1 -1
- package/dist/mcp/generated/docs/message-render.md +1 -1
- package/dist/mcp/generated/docs/message-tools.md +15 -4
- package/dist/mcp/generated/docs/messages.md +3 -0
- package/dist/mcp/generated/docs/model-selector.md +4 -2
- package/dist/mcp/generated/docs/overflow-tips.md +3 -3
- package/dist/mcp/generated/docs/resource-icon.md +102 -0
- package/dist/mcp/generated/docs/text-content.md +7 -3
- package/dist/mcp/generated/docs/theme.md +27 -35
- package/dist/mcp/generated/docs/use-artifact-preview.md +7 -3
- package/dist/mcp/generated/docs/use-command-selection.md +1 -1
- package/dist/mcp/generated/docs/use-container-scroll.md +2 -1
- package/dist/mcp/generated/docs/use-flow-node-actions.md +8 -15
- package/dist/mcp/generated/docs/use-global-config.md +11 -4
- package/dist/mcp/generated/docs/use-input-mention.md +119 -0
- package/dist/mcp/generated/docs/use-menu-keydown.md +15 -16
- package/dist/mcp/generated/docs/use-message-group.md +3 -6
- package/dist/mcp/generated/docs/user-message.md +39 -17
- package/dist/mcp/generated/index.json +233 -81
- package/dist/types/editor.d.ts +0 -29
- package/dist/types/index.d.ts +1 -0
- package/dist/types/input-menu.d.ts +46 -0
- package/dist/utils/artifact-tags.d.ts +36 -0
- package/dist/utils/collect-message-artifacts.d.ts +14 -0
- package/dist/utils/file.d.ts +3 -3
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/upload-file.d.ts +6 -1
- package/package.json +3 -2
- package/skills/blueking-chat-x/SKILL.md +143 -0
- package/skills/blueking-chat-x/references/_index.md +153 -0
- package/skills/blueking-chat-x/references/components/activity-layout.md +134 -0
- package/skills/blueking-chat-x/references/components/activity-message.md +486 -0
- package/skills/blueking-chat-x/references/components/add-menu-btn.md +88 -0
- package/skills/blueking-chat-x/references/components/ai-image.md +230 -0
- package/skills/blueking-chat-x/references/components/ai-loading.md +131 -0
- package/skills/blueking-chat-x/references/components/ai-selection.md +439 -0
- package/skills/blueking-chat-x/references/components/ai-slash-input.md +102 -0
- package/skills/blueking-chat-x/references/components/animation-text.md +202 -0
- package/skills/blueking-chat-x/references/components/assistant-message.md +504 -0
- package/skills/blueking-chat-x/references/components/chat-container.md +912 -0
- package/skills/blueking-chat-x/references/components/chat-input.md +614 -0
- package/skills/blueking-chat-x/references/components/cite-content.md +142 -0
- package/skills/blueking-chat-x/references/components/code-content.md +211 -0
- package/skills/blueking-chat-x/references/components/collapsible-content.md +76 -0
- package/skills/blueking-chat-x/references/components/common-error-content.md +73 -0
- package/skills/blueking-chat-x/references/components/content-render.md +233 -0
- package/skills/blueking-chat-x/references/components/delete-tool.md +191 -0
- package/skills/blueking-chat-x/references/components/desc-panel.md +162 -0
- package/skills/blueking-chat-x/references/components/detail-section.md +91 -0
- package/skills/blueking-chat-x/references/components/execution-summary.md +150 -0
- package/skills/blueking-chat-x/references/components/file-artifact-panel.md +307 -0
- package/skills/blueking-chat-x/references/components/file-content.md +340 -0
- package/skills/blueking-chat-x/references/components/file-icon.md +109 -0
- package/skills/blueking-chat-x/references/components/file-upload-btn.md +162 -0
- package/skills/blueking-chat-x/references/components/flow-agent-content.md +266 -0
- package/skills/blueking-chat-x/references/components/flow-agent-node-detail.md +236 -0
- package/skills/blueking-chat-x/references/components/highlight-keyword.md +146 -0
- package/skills/blueking-chat-x/references/components/image-content.md +182 -0
- package/skills/blueking-chat-x/references/components/image-preview-group.md +184 -0
- package/skills/blueking-chat-x/references/components/image-preview.md +226 -0
- package/skills/blueking-chat-x/references/components/info-message.md +144 -0
- package/skills/blueking-chat-x/references/components/input-attachment.md +49 -0
- package/skills/blueking-chat-x/references/components/input-info-alert.md +42 -0
- package/skills/blueking-chat-x/references/components/input-menu-panel.md +162 -0
- package/skills/blueking-chat-x/references/components/interrupt-message.md +212 -0
- package/skills/blueking-chat-x/references/components/key-value-content.md +128 -0
- package/skills/blueking-chat-x/references/components/knowledge-rag-content.md +122 -0
- package/skills/blueking-chat-x/references/components/latex-content.md +200 -0
- package/skills/blueking-chat-x/references/components/loading-message.md +192 -0
- package/skills/blueking-chat-x/references/components/markdown-content.md +232 -0
- package/skills/blueking-chat-x/references/components/mention-tag.md +79 -0
- package/skills/blueking-chat-x/references/components/mention-text.md +83 -0
- package/skills/blueking-chat-x/references/components/mermaid-content.md +189 -0
- package/skills/blueking-chat-x/references/components/message-container.md +645 -0
- package/skills/blueking-chat-x/references/components/message-loading.md +118 -0
- package/skills/blueking-chat-x/references/components/message-render.md +327 -0
- package/skills/blueking-chat-x/references/components/message-time.md +177 -0
- package/skills/blueking-chat-x/references/components/message-tools.md +427 -0
- package/skills/blueking-chat-x/references/components/model-selector.md +157 -0
- package/skills/blueking-chat-x/references/components/preview-toolbar.md +42 -0
- package/skills/blueking-chat-x/references/components/questions-container.md +85 -0
- package/skills/blueking-chat-x/references/components/reasoning-message.md +232 -0
- package/skills/blueking-chat-x/references/components/reference-content.md +135 -0
- package/skills/blueking-chat-x/references/components/reference-doc-content.md +109 -0
- package/skills/blueking-chat-x/references/components/resource-icon.md +98 -0
- package/skills/blueking-chat-x/references/components/scroll-btn.md +159 -0
- package/skills/blueking-chat-x/references/components/selection-footer.md +78 -0
- package/skills/blueking-chat-x/references/components/selection-question.md +88 -0
- package/skills/blueking-chat-x/references/components/shortcut-btn.md +204 -0
- package/skills/blueking-chat-x/references/components/shortcut-btns.md +266 -0
- package/skills/blueking-chat-x/references/components/shortcut-render.md +424 -0
- package/skills/blueking-chat-x/references/components/simple-table.md +101 -0
- package/skills/blueking-chat-x/references/components/text-content.md +80 -0
- package/skills/blueking-chat-x/references/components/tool-approval-card.md +183 -0
- package/skills/blueking-chat-x/references/components/tool-btn.md +317 -0
- package/skills/blueking-chat-x/references/components/tool-message.md +235 -0
- package/skills/blueking-chat-x/references/components/toolcall-render.md +348 -0
- package/skills/blueking-chat-x/references/components/user-feedback.md +233 -0
- package/skills/blueking-chat-x/references/components/user-message.md +444 -0
- package/skills/blueking-chat-x/references/components/user-question-answered-card.md +104 -0
- package/skills/blueking-chat-x/references/components/user-question-card.md +231 -0
- package/skills/blueking-chat-x/references/components/user-question-choice.md +105 -0
- package/skills/blueking-chat-x/references/components/user-question-option.md +42 -0
- package/skills/blueking-chat-x/references/components/vnode-renderer.md +122 -0
- package/skills/blueking-chat-x/references/composables/use-animation-text.md +196 -0
- package/skills/blueking-chat-x/references/composables/use-artifact-preview.md +235 -0
- package/skills/blueking-chat-x/references/composables/use-clipboard.md +203 -0
- package/skills/blueking-chat-x/references/composables/use-command-selection.md +150 -0
- package/skills/blueking-chat-x/references/composables/use-container-scroll.md +57 -0
- package/skills/blueking-chat-x/references/composables/use-custom-tab.md +158 -0
- package/skills/blueking-chat-x/references/composables/use-flow-node-actions.md +164 -0
- package/skills/blueking-chat-x/references/composables/use-full-screen.md +112 -0
- package/skills/blueking-chat-x/references/composables/use-global-config.md +155 -0
- package/skills/blueking-chat-x/references/composables/use-input-mention.md +115 -0
- package/skills/blueking-chat-x/references/composables/use-menu-keydown.md +161 -0
- package/skills/blueking-chat-x/references/composables/use-message-group.md +244 -0
- package/skills/blueking-chat-x/references/composables/use-observer-visible-list.md +188 -0
- package/skills/blueking-chat-x/references/composables/use-parent-scrolling.md +46 -0
- package/skills/blueking-chat-x/references/directives/index.md +61 -0
- package/skills/blueking-chat-x/references/directives/overflow-tips.md +205 -0
- package/skills/blueking-chat-x/references/icons/index.md +185 -0
- package/skills/blueking-chat-x/references/plugins/index.md +56 -0
- package/skills/blueking-chat-x/references/plugins/markdown-container.md +55 -0
- package/skills/blueking-chat-x/references/plugins/markdown-latex.md +205 -0
- package/skills/blueking-chat-x/references/plugins/markdown-mermaid.md +247 -0
- package/skills/blueking-chat-x/references/theme/theme.md +423 -0
- package/skills/blueking-chat-x/references/types/constants.md +340 -0
- package/skills/blueking-chat-x/references/types/index.md +120 -0
- package/skills/blueking-chat-x/references/types/interrupt.md +379 -0
- package/skills/blueking-chat-x/references/types/messages.md +556 -0
- package/skills/blueking-chat-x/references/types/schema.md +91 -0
- package/skills/blueking-chat-x/references/utils/index.md +196 -0
- package/skills/blueking-chat-x/scripts/generate-references.mjs +536 -0
- package/dist/components/chat-input/ai-slash-editor/ai-slash-editor.vue.d.ts +0 -28
- package/dist/components/chat-input/ai-slash-editor/theme.d.ts +0 -24
- package/dist/mcp/generated/docs/ai-prompt-list.md +0 -45
- package/dist/mcp/generated/docs/ai-skill-list.md +0 -75
- package/dist/mcp/generated/docs/ai-slash-editor.md +0 -43
- package/dist/mcp/generated/docs/ai-slash-menu.md +0 -42
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# useGlobalConfig
|
|
2
|
+
|
|
3
|
+
> 导入:`import { useGlobalConfig } from '@blueking/chat-x'` | since 1.0.0
|
|
4
|
+
|
|
5
|
+
useGlobalConfig 接收 GlobalConfig(含 size?: ComputedRef<AiSizeMode>、supportUpload: ComputedRef<boolean>、timezone?: ComputedRef<string | undefined>、menuSources?: ComputedRef<IInputMenuItem[]>),以 GLOBAL_CONFIG_TOKEN provide 给后代; injectGlobalConfig 在子组件中取出配置,无 Provider 时返回 undefined。ChatContainer 在 setup 中调用 useGlobalConfig 注入 size、supportUpload、timezone 与 menuSources; 后代组件可通过 injectGlobalConfig 读取配置;字号主题主要通过根节点 data-ai-size 与 CSS 变量生效。
|
|
6
|
+
|
|
7
|
+
**关联**:chat-container(根容器调用 useGlobalConfig 注入 supportUpload)、message-time(消息时间组件读取 timezone)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# useGlobalConfig 全局配置
|
|
12
|
+
|
|
13
|
+
> **分类**:composable
|
|
14
|
+
|
|
15
|
+
在聊天容器根组件与子组件之间通过 Vue `provide` / `inject` 共享**全局展示相关配置**(当前包括字号主题档位 `size`、是否支持上传 `supportUpload`、消息时间时区 `timezone`、输入框菜单数据源 `menuSources`)。与 Teleport 插槽 ID 无关。
|
|
16
|
+
|
|
17
|
+
> 字号主题主要通过 `ChatContainer` 根节点的 `data-ai-size` 与 CSS 变量(`--ai-font-size` 等)生效;`GlobalConfig.size` 供后代在逻辑层读取当前档位,样式层无需逐组件传参。
|
|
18
|
+
|
|
19
|
+
## 工作原理
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
ChatContainer(根)
|
|
23
|
+
├── :data-ai-size="size"(CSS 变量作用域)
|
|
24
|
+
├── useGlobalConfig({
|
|
25
|
+
│ size: computed(() => props.size ?? 'small'),
|
|
26
|
+
│ supportUpload: computed(() => props.supportUpload ?? false),
|
|
27
|
+
│ timezone: computed(() => props.timezone),
|
|
28
|
+
│ menuSources: resolvedMenuSources,
|
|
29
|
+
│ })
|
|
30
|
+
│ └── provide(GLOBAL_CONFIG_TOKEN, { size, supportUpload, timezone, menuSources })
|
|
31
|
+
│
|
|
32
|
+
└── MessageContainer → … → UserMessage / MessageTime 等
|
|
33
|
+
|
|
34
|
+
UserMessage(后代)
|
|
35
|
+
├── injectGlobalConfig()
|
|
36
|
+
│ └── inject(GLOBAL_CONFIG_TOKEN) → GlobalConfig | undefined
|
|
37
|
+
└── 模板中:globalConfig?.supportUpload.value ?? false
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **Provider**:必须在组件树的祖先(如 `ChatContainer`)的 `setup` 中调用 `useGlobalConfig`,否则后代 `injectGlobalConfig()` 得到 `undefined`。
|
|
41
|
+
- **Consumer**:任意后代在 `setup` 中调用 `injectGlobalConfig()`,拿到与 Provider 相同的 `GlobalConfig` 对象引用;`supportUpload` 为 `ComputedRef<boolean>`,可随根 props 响应式更新。
|
|
42
|
+
|
|
43
|
+
## 渲染示例
|
|
44
|
+
|
|
45
|
+
## 根容器用法(ChatContainer)
|
|
46
|
+
|
|
47
|
+
```vue
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import { computed } from 'vue';
|
|
50
|
+
import { useGlobalConfig } from '@blueking/chat-x';
|
|
51
|
+
|
|
52
|
+
const props = defineProps<{
|
|
53
|
+
size?: 'normal' | 'small';
|
|
54
|
+
supportUpload?: boolean;
|
|
55
|
+
timezone?: string;
|
|
56
|
+
}>();
|
|
57
|
+
|
|
58
|
+
useGlobalConfig({
|
|
59
|
+
size: computed(() => props.size ?? 'small'),
|
|
60
|
+
supportUpload: computed(() => props.supportUpload ?? false),
|
|
61
|
+
// 无默认值:未配置时由 MessageTime 回退到浏览器时区
|
|
62
|
+
timezone: computed(() => props.timezone),
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 子组件用法(UserMessage)
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<script setup lang="ts">
|
|
71
|
+
import { injectGlobalConfig } from '@blueking/chat-x';
|
|
72
|
+
|
|
73
|
+
const globalConfig = injectGlobalConfig();
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<template>
|
|
77
|
+
<ChatInput :support-upload="globalConfig?.supportUpload.value ?? false" />
|
|
78
|
+
</template>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
### 类型与函数签名(摘要)
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import type { ComputedRef } from 'vue';
|
|
87
|
+
|
|
88
|
+
export const GLOBAL_CONFIG_TOKEN: unique symbol;
|
|
89
|
+
|
|
90
|
+
export type AiSizeMode = 'normal' | 'small';
|
|
91
|
+
|
|
92
|
+
export type GlobalConfig = {
|
|
93
|
+
size?: ComputedRef<AiSizeMode>;
|
|
94
|
+
supportUpload: ComputedRef<boolean>;
|
|
95
|
+
timezone?: ComputedRef<string | undefined>;
|
|
96
|
+
/** 输入框菜单数据源;消息编辑态的内嵌输入框据此渲染 @ / \ 与 + 号菜单 */
|
|
97
|
+
menuSources?: ComputedRef<IInputMenuItem[]>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export function useGlobalConfig(options: GlobalConfig): {
|
|
101
|
+
size?: ComputedRef<AiSizeMode>;
|
|
102
|
+
supportUpload: ComputedRef<boolean>;
|
|
103
|
+
timezone?: ComputedRef<string | undefined>;
|
|
104
|
+
menuSources?: ComputedRef<IInputMenuItem[]>;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export function injectGlobalConfig(): GlobalConfig | undefined;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `GLOBAL_CONFIG_TOKEN`
|
|
111
|
+
|
|
112
|
+
| 名称 | 说明 |
|
|
113
|
+
| --------------------- | -------------------------------------- |
|
|
114
|
+
| `GLOBAL_CONFIG_TOKEN` | `provide` / `inject` 使用的 Symbol key |
|
|
115
|
+
|
|
116
|
+
### `GlobalConfig`
|
|
117
|
+
|
|
118
|
+
| 字段 | 说明 |
|
|
119
|
+
| --------------- | ------------------------------------------------------------------------ |
|
|
120
|
+
| `size` | 可选。字号主题档位 `normal`(14px)/ `small`(12px),与 `ChatContainer.size` 对齐 |
|
|
121
|
+
| `supportUpload` | 是否支持上传,与根容器 `ChatContainer` 的 `supportUpload` 等展示策略对齐 |
|
|
122
|
+
| `menuSources` | 可选。输入框菜单数据源,供消息编辑态的内嵌 `ChatInput` 使用 |
|
|
123
|
+
| `timezone` | 可选。消息时间展示所用的 IANA 时区名,与 `ChatContainer.timezone` 对齐;未配置时 `MessageTime` 按浏览器时区展示 |
|
|
124
|
+
| `menuSources` | 可选。输入框菜单数据源,与 `ChatInput.menuSources` 同源(`ChatContainer` 传入的是补齐会话产物后的结果);[UserMessage](../components/message/user-message) 编辑态据此渲染菜单,未配置时编辑态无菜单 |
|
|
125
|
+
|
|
126
|
+
### `useGlobalConfig(options)`
|
|
127
|
+
|
|
128
|
+
| 参数 | 说明 |
|
|
129
|
+
| ----------------------- | ------------------------------------------------------------------------------------- |
|
|
130
|
+
| `options.size` | 可选。字号主题档位,建议使用 `computed(() => props.size ?? 'small')` 与根 props 同步 |
|
|
131
|
+
| `options.supportUpload` | 是否支持上传,建议使用 `computed(() => props.supportUpload ?? false)` 与根 props 同步 |
|
|
132
|
+
| `options.timezone` | 可选。消息时间时区,建议使用 `computed(() => props.timezone)` 与根 props 同步;不设默认值,交由 `MessageTime` 回退浏览器时区 |
|
|
133
|
+
|
|
134
|
+
- 调用后立即 `provide(GLOBAL_CONFIG_TOKEN, options)`。
|
|
135
|
+
- 必须在具有组件实例上下文的 `setup` 中调用(与 Vue `provide` 要求一致)。
|
|
136
|
+
|
|
137
|
+
### `injectGlobalConfig()`
|
|
138
|
+
|
|
139
|
+
| 返回值 | 说明 |
|
|
140
|
+
| -------------- | ----------------------------------------------------------- |
|
|
141
|
+
| `GlobalConfig` | 祖先已调用 `useGlobalConfig` 时,与 Provider 传入的同一对象 |
|
|
142
|
+
| `undefined` | 组件树中无对应 `provide` 时 |
|
|
143
|
+
|
|
144
|
+
## 注意事项
|
|
145
|
+
|
|
146
|
+
1. **Provider 须在祖先调用**:子组件的 `injectGlobalConfig` 依赖同一组件树内的 `useGlobalConfig`。
|
|
147
|
+
2. **可选链访问**:无 Provider 时返回 `undefined`,模板中建议使用 `globalConfig?.supportUpload.value ?? false`。
|
|
148
|
+
3. **扩展配置**:后续若在 `GlobalConfig` 中增加字段,应在根容器统一传入并在文档中说明。
|
|
149
|
+
|
|
150
|
+
## 关联组件
|
|
151
|
+
|
|
152
|
+
- [ChatContainer](../components/setup/chat-container) — 调用 `useGlobalConfig` 注入 `size`、`supportUpload`、`timezone` 与 `menuSources`
|
|
153
|
+
- [UserMessage](../components/message/user-message) — 编辑态读取 `supportUpload` 与 `menuSources`
|
|
154
|
+
- [MessageTime](../components/feedback/message-time) — 读取 `timezone` 展示消息时间
|
|
155
|
+
- [主题配置](../theme/theme) — `data-ai-size` 与 CSS 变量说明
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# useInputMention
|
|
2
|
+
|
|
3
|
+
> 导入:`import { useInputMentionConsumer, useInputMentionProvider } from '@blueking/chat-x'` | since 0.0.51
|
|
4
|
+
|
|
5
|
+
useInputMentionProvider 由持有输入框的容器(ChatContainer)提供 insertMention, useInputMentionConsumer 在任意后代取出;无 Provider(只读 / 分享态)时返回 undefined,调用方据此隐藏「引用」入口。 消息区文件卡片与侧栏产物面板即用它把文件以标签形式追加进输入框。 源码位置:src/composables/use-input-mention.ts。
|
|
6
|
+
|
|
7
|
+
**关联**:chat-container(根容器提供 insertMention,内部转发给 ChatInput 实例)、chat-input(实际执行插入的组件,expose insertMention)、file-artifact-panel(侧栏产物面板据此展示「引用」按钮)、assistant-message(消息内文件卡片据此展示「引用」按钮)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# useInputMention 资源插入输入框
|
|
12
|
+
|
|
13
|
+
> **分类**:composable
|
|
14
|
+
|
|
15
|
+
把「把某个资源 `@` 进输入框」这件事从组件层级里解耦出来:消息区的文件卡片、侧栏的产物面板可能嵌套得很深,逐层透传输入框实例既啰嗦又容易漏。
|
|
16
|
+
|
|
17
|
+
## 工作原理
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
ChatContainer(根)
|
|
21
|
+
├── useInputMentionProvider({
|
|
22
|
+
│ insertMention: item => chatInputRef.value?.insertMention?.(item),
|
|
23
|
+
│ })
|
|
24
|
+
│ └── provide(INPUT_MENTION_TOKEN, context)
|
|
25
|
+
│
|
|
26
|
+
├── ChatInput(ref="chatInputRef")
|
|
27
|
+
│ └── expose insertMention → AiSlashInput.appendMention(追加到文档末尾)
|
|
28
|
+
│
|
|
29
|
+
└── MessageContainer → … → ArtifactFileCard / FileArtifactPanel
|
|
30
|
+
└── useInputMentionConsumer() → InputMentionContext | undefined
|
|
31
|
+
└── 有值才渲染「引用」按钮
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
无 Provider 时 `useInputMentionConsumer()` 返回 `undefined`,因此只读 / 分享态(没有输入框)自动不显示引用入口,不需要额外开关。
|
|
35
|
+
|
|
36
|
+
## 消费方用法
|
|
37
|
+
|
|
38
|
+
```vue
|
|
39
|
+
<template>
|
|
40
|
+
<span
|
|
41
|
+
v-if="inputMention"
|
|
42
|
+
v-tippy="{ content: t('引用') }"
|
|
43
|
+
@click.stop="handleCite"
|
|
44
|
+
>
|
|
45
|
+
<CiteIcon />
|
|
46
|
+
</span>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script setup lang="ts">
|
|
50
|
+
import { toArtifactMenuItem, useInputMentionConsumer } from '@blueking/chat-x';
|
|
51
|
+
|
|
52
|
+
const props = defineProps<{ file: AIFileInfo }>();
|
|
53
|
+
|
|
54
|
+
// 无输入框上下文时为 undefined,据此隐藏入口
|
|
55
|
+
const inputMention = useInputMentionConsumer();
|
|
56
|
+
|
|
57
|
+
const handleCite = () => {
|
|
58
|
+
inputMention?.insertMention(toArtifactMenuItem(props.file));
|
|
59
|
+
};
|
|
60
|
+
</script>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
::: warning id 必须与菜单一致
|
|
64
|
+
把文件转成菜单条目时统一走 [`toArtifactMenuItem`](/utils/#会话产物收集):`id` 不一致会导致 `@` 菜单的去重与已插入标签的匹配同时失效(同一个文件既能重复插入、又无法从候选中剔除)。
|
|
65
|
+
:::
|
|
66
|
+
|
|
67
|
+
## 自建容器用法
|
|
68
|
+
|
|
69
|
+
不使用 [ChatContainer](/components/setup/chat-container)、自行组合 `MessageContainer` + `ChatInput` 时,需要自己提供上下文:
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<script setup lang="ts">
|
|
73
|
+
import { useTemplateRef } from 'vue';
|
|
74
|
+
import { ChatInput, useInputMentionProvider } from '@blueking/chat-x';
|
|
75
|
+
|
|
76
|
+
const chatInputRef = useTemplateRef<InstanceType<typeof ChatInput>>('chatInputRef');
|
|
77
|
+
|
|
78
|
+
useInputMentionProvider({
|
|
79
|
+
insertMention: item => chatInputRef.value?.insertMention?.(item),
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## API
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
export const INPUT_MENTION_TOKEN: unique symbol;
|
|
88
|
+
|
|
89
|
+
export type InputMentionContext = {
|
|
90
|
+
/** 把一个条目以标签形式追加到输入框,效果等同于用户通过 `@` 菜单选中它 */
|
|
91
|
+
insertMention: (item: IInputMenuItem) => void;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export function useInputMentionProvider(context: InputMentionContext): InputMentionContext;
|
|
95
|
+
|
|
96
|
+
export function useInputMentionConsumer(): InputMentionContext | undefined;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| 函数 | 说明 |
|
|
100
|
+
| --------------------------- | ---------------------------------------------------------------------- |
|
|
101
|
+
| `useInputMentionProvider` | 在祖先 `setup` 中调用,`provide` 上下文并原样返回,便于就地复用 |
|
|
102
|
+
| `useInputMentionConsumer` | 在任意后代 `setup` 中调用,无 Provider 时返回 `undefined` |
|
|
103
|
+
|
|
104
|
+
## 注意事项
|
|
105
|
+
|
|
106
|
+
1. **插入位置不依赖光标**:底层走 `AiSlashInput.appendMention`,标签追加到文档末尾——外部调用时编辑器通常没有焦点,依赖光标会插错位置。
|
|
107
|
+
2. **插入的资源可能不在 `menuSources` 里**:`ChatInput` 的 `update:modelValue` 第二参数只回传能在 `menuSources` 中反查到的条目。`ChatContainer` 会自动收集消息里的会话产物,所以经容器使用时产物标签能被反查到。
|
|
108
|
+
3. **必须在 `setup` 中调用**:与 Vue `provide` / `inject` 的要求一致。
|
|
109
|
+
|
|
110
|
+
## 关联组件
|
|
111
|
+
|
|
112
|
+
- [ChatContainer](../components/setup/chat-container) — 默认的 Provider
|
|
113
|
+
- [ChatInput](../components/input/chat-input) — `insertMention` 的实际执行者
|
|
114
|
+
- [FileArtifactPanel](../components/message/file-artifact-panel) — 侧栏产物面板的引用入口
|
|
115
|
+
- [Utils 工具函数](../utils/) — `collectMessageArtifacts` / `toArtifactMenuItem`
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# useMenuKeydown
|
|
2
|
+
|
|
3
|
+
> 导入:`import { useMenuKeydown } from '@blueking/chat-x'` | since 1.0.0
|
|
4
|
+
|
|
5
|
+
useMenuKeydown 接收 items、menuRef、onSelect,在 window 捕获阶段监听 keydown;菜单不可见(offsetParent 为空)或列表为空时不响应。 维护 activeIndex,处理 ArrowUp/ArrowDown 循环与 Enter 选中,并 scrollIntoView 当前 .is-active 项。 InputMenuPanel(输入框统一菜单)与 ModelSelectorPanel(模型下拉)内部使用。
|
|
6
|
+
|
|
7
|
+
**关联**:input-menu-panel(输入框菜单的键盘导航)、model-selector(模型下拉的键盘导航)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# useMenuKeydown 菜单键盘导航
|
|
12
|
+
|
|
13
|
+
> **分类**:composable
|
|
14
|
+
|
|
15
|
+
为弹出菜单提供键盘导航能力的组合式函数。在 `onMounted` 时于 **`window` 捕获阶段**注册 `keydown` 监听,在 `onScopeDispose` 时自动移除,通过 `menuRef.offsetParent` 检测菜单可见性来决定是否响应按键。
|
|
16
|
+
|
|
17
|
+
内部维护 `activeIndex`(高亮项索引),由调用方将其绑定到列表的 `.is-active` 样式;`handleKeydown` 完全内部管理,**无需手动绑定到模板**。
|
|
18
|
+
|
|
19
|
+
## 工作原理
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
onMounted:window.addEventListener('keydown', handleKeydown, true) ← 捕获阶段
|
|
23
|
+
onScopeDispose:window.removeEventListener('keydown', handleKeydown, true)
|
|
24
|
+
|
|
25
|
+
handleKeydown(e):
|
|
26
|
+
├── !menuRef.value?.offsetParent → return(菜单不可见,忽略)
|
|
27
|
+
├── !items.value?.length → return(列表为空,忽略)
|
|
28
|
+
│
|
|
29
|
+
├── ArrowUp → e.preventDefault/stopPropagation
|
|
30
|
+
│ activeIndex = (activeIndex - 1 + length) % length ← 循环到末尾
|
|
31
|
+
│ scrollToActive()
|
|
32
|
+
├── ArrowDown → e.preventDefault/stopPropagation
|
|
33
|
+
│ activeIndex = (activeIndex + 1) % length ← 循环到开头
|
|
34
|
+
│ scrollToActive()
|
|
35
|
+
└── Enter / NumpadEnter → e.preventDefault/stopPropagation
|
|
36
|
+
onSelect(items[activeIndex])
|
|
37
|
+
|
|
38
|
+
scrollToActive():nextTick → menuRef.querySelector('.is-active')?.scrollIntoView({ block: 'nearest' })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> **`.is-active` 依赖**:`scrollToActive` 通过 CSS 类名 `.is-active` 定位当前高亮项,调用方必须在模板中将 `activeIndex` 对应的项加上此类名。
|
|
42
|
+
|
|
43
|
+
## 渲染示例
|
|
44
|
+
|
|
45
|
+
使用 ↑ ↓ 键移动高亮,Enter 选中:
|
|
46
|
+
|
|
47
|
+
## 基础用法
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<!-- 菜单容器,ref 传给 useMenuKeydown 用于可见性检测 -->
|
|
52
|
+
<div
|
|
53
|
+
ref="menuRef"
|
|
54
|
+
class="prompt-menu"
|
|
55
|
+
>
|
|
56
|
+
<div
|
|
57
|
+
v-for="(item, i) in items"
|
|
58
|
+
:key="item.id"
|
|
59
|
+
:class="['menu-item', { 'is-active': activeIndex === i }]"
|
|
60
|
+
@click="onSelect(item)"
|
|
61
|
+
@mouseenter="activeIndex = i"
|
|
62
|
+
>
|
|
63
|
+
{{ item.name }}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import { shallowRef, useTemplateRef } from 'vue';
|
|
70
|
+
import { useMenuKeydown } from '@blueking/chat-x';
|
|
71
|
+
|
|
72
|
+
const menuRef = useTemplateRef<HTMLElement>('menuRef');
|
|
73
|
+
const items = shallowRef([
|
|
74
|
+
{ id: 'ask', name: '问问小鲸' },
|
|
75
|
+
{ id: 'translate', name: '翻译文本' },
|
|
76
|
+
{ id: 'review', name: '代码审查' },
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
const onSelect = (item: (typeof items.value)[0]) => {
|
|
80
|
+
console.log('选中:', item.name);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// activeIndex 由 composable 内部管理,无需手动传入
|
|
84
|
+
// window keydown 监听自动注册,无需手动绑定 @keydown
|
|
85
|
+
const { activeIndex } = useMenuKeydown({ items, menuRef, onSelect });
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<style scoped>
|
|
89
|
+
.menu-item.is-active {
|
|
90
|
+
background: #e1ecff;
|
|
91
|
+
color: #3a84ff;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 库内部的实际用法
|
|
97
|
+
|
|
98
|
+
`useMenuKeydown` 被 [InputMenuPanel](/components/input/input-menu-panel)(输入框统一菜单)与 [ModelSelector](/components/input/model-selector) 的下拉面板内部使用:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// InputMenuPanel 中:items 为跨分组扁平化后的可选条目
|
|
102
|
+
const panelRef = useTemplateRef<HTMLElement>('panelRef');
|
|
103
|
+
const flatItems = computed(() => props.flatItems);
|
|
104
|
+
const { activeIndex } = useMenuKeydown<IInputMenuItem>({
|
|
105
|
+
items: flatItems, // ComputedRef 也可传入
|
|
106
|
+
onSelect: item => emit('select', item),
|
|
107
|
+
menuRef: panelRef,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// 结果集变化后旧的高亮下标已无意义,回到首项
|
|
111
|
+
watch(flatItems, () => {
|
|
112
|
+
activeIndex.value = 0;
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## API
|
|
117
|
+
|
|
118
|
+
### 参数
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
useMenuKeydown<T>(props: {
|
|
122
|
+
items: ShallowRef<T[]>; // 菜单项列表
|
|
123
|
+
menuRef: Readonly<ShallowRef<HTMLElement | null>>; // 菜单容器 DOM 引用(用于可见性检测和滚动定位)
|
|
124
|
+
onSelect: (item: T) => void; // 选中回调
|
|
125
|
+
}): { activeIndex: ShallowRef<number> }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 参数说明
|
|
129
|
+
|
|
130
|
+
| 参数 | 类型 | 说明 |
|
|
131
|
+
| ---------- | --------------------------------- | ----------------------------------------------------------- |
|
|
132
|
+
| `items` | `ShallowRef<T[]>` | 菜单项数组;为空时所有按键均不响应 |
|
|
133
|
+
| `menuRef` | `ShallowRef<HTMLElement \| null>` | 菜单容器引用;`offsetParent === null`(不可见)时按键不响应 |
|
|
134
|
+
| `onSelect` | `(item: T) => void` | Enter 确认时触发,传入当前 `activeIndex` 对应的项 |
|
|
135
|
+
|
|
136
|
+
### 返回值
|
|
137
|
+
|
|
138
|
+
| 属性名 | 类型 | 初始值 | 说明 |
|
|
139
|
+
| ------------- | -------------------- | ------ | ------------------------------------------------------------------------------ |
|
|
140
|
+
| `activeIndex` | `ShallowRef<number>` | `0` | 当前高亮项索引;需在模板中绑定 `.is-active` 类;鼠标 `mouseenter` 也可修改此值 |
|
|
141
|
+
|
|
142
|
+
### 按键行为(硬编码,不可自定义)
|
|
143
|
+
|
|
144
|
+
| 按键 | 行为 |
|
|
145
|
+
| ----------------------- | -------------------------------------- |
|
|
146
|
+
| `ArrowUp` | 高亮上一项(从第 0 项循环到末尾) |
|
|
147
|
+
| `ArrowDown` | 高亮下一项(从末尾循环到第 0 项) |
|
|
148
|
+
| `Enter` / `NumpadEnter` | 选中当前高亮项,调用 `onSelect` |
|
|
149
|
+
| `Escape` | 无处理(不在此 composable 负责范围内) |
|
|
150
|
+
|
|
151
|
+
## 注意事项
|
|
152
|
+
|
|
153
|
+
1. **`handleKeydown` 内部自动注册**:在 `window` 捕获阶段(第三参数 `true`)绑定,优先于页面其他元素;调用方**无需**手动绑定 `@keydown`
|
|
154
|
+
2. **`.is-active` 类名约定**:`scrollToActive` 通过 `menuRef.querySelector('.is-active')` 定位元素,项目模板必须在 `activeIndex === i` 时添加此类
|
|
155
|
+
3. **`activeIndex` 不自动重置**:列表内容(`items`)变化时,`activeIndex` 保持不变。如需重置(如过滤后),需在外部 `watch` items 并手动将 `activeIndex.value = 0`
|
|
156
|
+
4. **可见性检测依赖 `offsetParent`**:元素通过 `display:none` 隐藏时 `offsetParent === null`,按键会被忽略;`visibility:hidden` 或 `opacity:0` 不会被忽略
|
|
157
|
+
5. **捕获阶段拦截**:`ArrowUp`/`ArrowDown`/`Enter` 均调用 `e.preventDefault()` 和 `e.stopPropagation()`,防止方向键滚动页面或 Enter 提交表单
|
|
158
|
+
|
|
159
|
+
## 关联组件
|
|
160
|
+
|
|
161
|
+
- [ChatInput](../components/input/chat-input) — `@` 菜单与 `/` 提示词列表
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# useMessageGroup
|
|
2
|
+
|
|
3
|
+
> 导入:`import { useMessageGroup } from '@blueking/chat-x'` | since 1.0.0
|
|
4
|
+
|
|
5
|
+
useMessageGroup 接收 keyword、messages、selectedUserMessages,通过 watchEffect 产出 messageGroups(User/Assistant/Tool 合并、末尾 Loading 注入且占位 id 为 LOADING_MESSAGE_ID、pause 与分享勾选等)。 executionGroups 供侧边执行摘要过滤,并暴露 isShareMode、全选与 onConfirmShare。 ChatContainer 组装后传给 MessageContainer;ExecutionSummary 消费 executionGroups。
|
|
6
|
+
|
|
7
|
+
**关联**:chat-container(调用并传入 MessageContainer)、message-container(必填 messageGroups 数据源)、execution-summary(使用 executionGroups 与定位)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# useMessageGroup 消息分组
|
|
12
|
+
|
|
13
|
+
> **分类**:composable
|
|
14
|
+
|
|
15
|
+
核心消息分组逻辑,将原始 `Message[]` 数组转换为结构化的 `MessageGroup[]`。处理 Tool 消息合并、Loading 自动注入、执行摘要过滤和消息多选/分享等逻辑。
|
|
16
|
+
|
|
17
|
+
## 函数签名
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
function useMessageGroup(options: {
|
|
21
|
+
keyword?: ShallowRef<string>;
|
|
22
|
+
messages: ComputedRef<Message[]>;
|
|
23
|
+
renderMode?: MaybeRef<RenderMode>;
|
|
24
|
+
selectedUserMessages: Ref<Message[] | undefined>;
|
|
25
|
+
}): {
|
|
26
|
+
messageGroups: Ref<MessageGroup[]>;
|
|
27
|
+
executionGroups: ComputedRef<MessageGroup[]>;
|
|
28
|
+
sessionArtifacts: ComputedRef<SessionArtifact[]>;
|
|
29
|
+
pendingApprovalCount: ComputedRef<number>;
|
|
30
|
+
pendingApprovalTipText: ComputedRef<string>;
|
|
31
|
+
isShareMode: ShallowRef<boolean>;
|
|
32
|
+
isAllSelected: ComputedRef<boolean>;
|
|
33
|
+
onToggleShareAll: (isAllSelected: boolean) => void;
|
|
34
|
+
onCancelShare: () => void;
|
|
35
|
+
onConfirmShare: () => Message[];
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 分组规则
|
|
40
|
+
|
|
41
|
+
`watchEffect` 遍历 `messages` 数组,按以下规则分组:
|
|
42
|
+
|
|
43
|
+
**消息 `uid`:** 若某条消息缺少 `uid`,分组前会自动为其生成并写入 `uid`(与 `MessageGroup.uid` 及 DOM 定位约定配合)。
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
messages 原始数组(按顺序处理)
|
|
47
|
+
│
|
|
48
|
+
┌────┴────┐────────────┐
|
|
49
|
+
│ │ │
|
|
50
|
+
role=user role=tool 其他 role
|
|
51
|
+
│ │ │
|
|
52
|
+
① 将累积的 ② 通过 ③ 累积到
|
|
53
|
+
assistant toolCallId assistantMessages
|
|
54
|
+
消息推入 找到对应的 等待 user 消息
|
|
55
|
+
list 作为 Assistant 触发分组
|
|
56
|
+
一组,当前 消息,注入
|
|
57
|
+
user 单独 toolMessage
|
|
58
|
+
成组 后 continue
|
|
59
|
+
|
|
60
|
+
④ 遍历结束后将剩余 assistantMessages 推入 list
|
|
61
|
+
⑤ 末尾为 user 消息 → 追加 Loading 消息组(`renderMode` 为 `Share` 时不追加)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
注入的占位 Loading 消息使用固定 id:`LOADING_MESSAGE_ID`(`'__loading__'`,定义于 `common/constants`)。`ChatContainer` 据此判断是否在「请求中」阶段,并向 `ChatInput` / `MessageContainer` 下传 `MessageStatus.Fetching`,与流式中的停止、防重复发送行为对齐。
|
|
65
|
+
|
|
66
|
+
### Tool 消息处理
|
|
67
|
+
|
|
68
|
+
`role: 'tool'` 消息不会独立渲染,而是通过 `toolCallId` 注入到对应 AssistantMessage 的 `toolCall.toolMessage` 字段:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const assistantToolMessage = messages.find(
|
|
72
|
+
m => m.role === 'assistant' && m.toolCalls?.some(t => t.id === message.toolCallId),
|
|
73
|
+
) as AssistantMessage | undefined;
|
|
74
|
+
if (assistantToolMessage) {
|
|
75
|
+
const toolCall = assistantToolMessage.toolCalls?.find(t => t.id === message.toolCallId);
|
|
76
|
+
if (toolCall) {
|
|
77
|
+
toolCall.toolMessage = message;
|
|
78
|
+
}
|
|
79
|
+
// error 时强制 assistant 为 Error;否则保留原 status,空值兜底 Complete
|
|
80
|
+
assistantToolMessage.status = message.error
|
|
81
|
+
? MessageStatus.Error
|
|
82
|
+
: assistantToolMessage.status || MessageStatus.Complete;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
若找不到对应 `toolCall`(例如数据不一致),**跳过注入**,避免非空断言导致的运行时异常。
|
|
87
|
+
|
|
88
|
+
### pause 字段
|
|
89
|
+
|
|
90
|
+
每个 Assistant 消息组计算 `pause` 属性:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
pause = assistantMessages.some(m => m.property?.extra?.pause) ?? false;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`pause` 为 `true` 时,`MessageContainer` 不渲染该组的 `MessageTools` 工具栏。
|
|
97
|
+
|
|
98
|
+
## executionGroups
|
|
99
|
+
|
|
100
|
+
`executionGroups` 从 `messageGroups` 中过滤出执行类消息,供 `ExecutionSummary` 使用。每个执行组会自动从前一组用户消息中提取 `userMessageTitle`,作为执行摘要的标题显示;若无前置用户消息则回退为当前时间戳:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const isExecutionMessage = (m: Message): boolean => {
|
|
104
|
+
return (
|
|
105
|
+
// 带 toolCalls 的 assistant 消息
|
|
106
|
+
(m.role === 'assistant' && !!m.toolCalls?.length) ||
|
|
107
|
+
// FlowAgent 类型的 activity 消息
|
|
108
|
+
(m.role === 'activity' && m.activityType === 'flow_agent')
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
支持关键词过滤,通过 `SEARCH_TEXT_EXTRACTORS` 注册表扩展可搜索文本:
|
|
114
|
+
|
|
115
|
+
| 消息类型 | 搜索范围 |
|
|
116
|
+
| ---------- | ------------------------------------------------------------ |
|
|
117
|
+
| toolCall | `function.name`、`mcpName`、`description`、`arguments`、`id` |
|
|
118
|
+
| flow_agent | 各任务 `task_name`、各节点 `name` |
|
|
119
|
+
|
|
120
|
+
## sessionArtifacts 会话级文件产物
|
|
121
|
+
|
|
122
|
+
`sessionArtifacts` 收集当前会话具有 `outputId` 的助手产物与用户上传附件,供 `ChatContainer` 侧栏「文件产物」Tab 聚合预览。以 **`outputId`** 为会话内唯一键去重(同 `outputId` 视为同一文件),保留最后一次出现的文件信息,列表顺序与「最后一次出现」的相对顺序一致:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const sessionArtifacts = computed(() => {
|
|
126
|
+
// delete + set:同 key 覆盖内容,并把该项挪到 Map 末尾,保证「最后出现」顺序
|
|
127
|
+
const byOutputId = new Map();
|
|
128
|
+
for (const message of messages.value) {
|
|
129
|
+
for (const file of getMessageArtifacts(message)) {
|
|
130
|
+
if (byOutputId.has(file.outputId)) {
|
|
131
|
+
byOutputId.delete(file.outputId);
|
|
132
|
+
}
|
|
133
|
+
byOutputId.set(file.outputId, file);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Array.from(byOutputId.values());
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> `SessionArtifact` 即为 `AIFileInfo` 别名;文件名可能重复,不可作唯一键。
|
|
141
|
+
|
|
142
|
+
预览命中与取链见 [useArtifactPreview](./use-artifact-preview);侧栏列表与分类型预览(`ArtifactPreviewHost`)见 [FileArtifactPanel](../components/message/file-artifact-panel)。
|
|
143
|
+
|
|
144
|
+
## 待审批统计
|
|
145
|
+
|
|
146
|
+
`useMessageGroup` 会统计消息列表中处于待审批状态的 AI Dev 审批中断:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const pendingApprovalStatusSet = new Set([APPROVAL_STATUS.PENDING, APPROVAL_STATUS.DRAFT]);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
当 `MessageRole.Interrupt` 消息的 `content.outcome.type === 'interrupt'`,且其中断项满足 `reason === InterruptReason.AIDevToolApproval`、`metadata.ticket.status` 为 `pending` 或 `draft` 时,计入 `pendingApprovalCount`。
|
|
153
|
+
|
|
154
|
+
`pendingApprovalTipText` 根据数量生成输入区提示文案:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
'当前会话有 {count} 个待审批单,如需继续,请先取消审批'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`ChatContainer` 会消费该返回值,向 `ChatInput` 传入 `sendDisabledTip` 并在输入区上方展示提示,从而阻止继续发送。
|
|
161
|
+
|
|
162
|
+
## 分享模式
|
|
163
|
+
|
|
164
|
+
`useMessageGroup` 提供完整的分享模式支持:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const {
|
|
168
|
+
isShareMode, // 是否处于分享模式
|
|
169
|
+
isAllSelected, // 是否全选
|
|
170
|
+
onToggleShareAll, // 切换全选
|
|
171
|
+
onCancelShare, // 取消分享(清空选中 + 退出分享模式)
|
|
172
|
+
onConfirmShare, // 确认分享(返回选中的消息)
|
|
173
|
+
} = useMessageGroup(options);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
选中联动规则:
|
|
177
|
+
|
|
178
|
+
- 选中用户消息组 → 其后紧邻的 AI 回复组视觉联动选中
|
|
179
|
+
- 取消用户消息组 → 关联 AI 回复组同时取消
|
|
180
|
+
|
|
181
|
+
## 使用示例
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { computed, ref as deepRef, shallowRef } from 'vue';
|
|
185
|
+
import { useMessageGroup, type Message } from '@blueking/chat-x';
|
|
186
|
+
|
|
187
|
+
const keyword = shallowRef('');
|
|
188
|
+
const messages = computed(() => props.messages);
|
|
189
|
+
const selectedUserMessages = deepRef<Message[]>([]);
|
|
190
|
+
|
|
191
|
+
const {
|
|
192
|
+
messageGroups,
|
|
193
|
+
executionGroups,
|
|
194
|
+
pendingApprovalCount,
|
|
195
|
+
pendingApprovalTipText,
|
|
196
|
+
isShareMode,
|
|
197
|
+
isAllSelected,
|
|
198
|
+
onToggleShareAll,
|
|
199
|
+
onCancelShare,
|
|
200
|
+
onConfirmShare,
|
|
201
|
+
} = useMessageGroup({
|
|
202
|
+
keyword,
|
|
203
|
+
messages,
|
|
204
|
+
selectedUserMessages,
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 返回值说明
|
|
209
|
+
|
|
210
|
+
| 属性/方法名 | 类型 | 说明 |
|
|
211
|
+
| ---------------- | ----------------------------- | --------------------------------------------------------------------------- |
|
|
212
|
+
| messageGroups | `Ref<MessageGroup[]>` | 完整消息分组列表 |
|
|
213
|
+
| executionGroups | `ComputedRef<MessageGroup[]>` | 仅包含执行类消息的分组(工具调用 + FlowAgent),自动提取 `userMessageTitle` |
|
|
214
|
+
| sessionArtifacts | `ComputedRef<SessionArtifact[]>` | 收集助手产物与具有 `outputId` 的上传附件,按 `outputId` 去重(保留最后一次) |
|
|
215
|
+
| pendingApprovalCount | `ComputedRef<number>` | 当前消息中待审批 AI Dev 审批中断的数量 |
|
|
216
|
+
| pendingApprovalTipText | `ComputedRef<string>` | 待审批阻塞发送提示文案;无待审批时为空字符串 |
|
|
217
|
+
| isShareMode | `ShallowRef<boolean>` | 是否处于分享模式 |
|
|
218
|
+
| isAllSelected | `ComputedRef<boolean>` | 所有用户消息组是否全部选中 |
|
|
219
|
+
| onToggleShareAll | `(checked: boolean) => void` | 切换全选 |
|
|
220
|
+
| onCancelShare | `() => void` | 取消分享模式 |
|
|
221
|
+
| onConfirmShare | `() => Message[]` | 确认分享,返回选中的消息数组 |
|
|
222
|
+
|
|
223
|
+
## 类型定义
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { type MessageGroup } from '@blueking/chat-x';
|
|
227
|
+
|
|
228
|
+
interface MessageGroup {
|
|
229
|
+
uid: string;
|
|
230
|
+
type: MessageRole;
|
|
231
|
+
messages: Message[];
|
|
232
|
+
checked: boolean;
|
|
233
|
+
isHover: boolean;
|
|
234
|
+
pause?: boolean;
|
|
235
|
+
startTime?: number;
|
|
236
|
+
userMessageTitle?: number | string;
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## 关联组件
|
|
241
|
+
|
|
242
|
+
- [ChatContainer](../components/setup/chat-container) — 调用 useMessageGroup 并下传分组
|
|
243
|
+
- [MessageContainer](../components/setup/message-container) — 渲染 messageGroups
|
|
244
|
+
- [ExecutionSummary](../components/agent/execution-summary) — 消费 executionGroups
|