@blueking/chat-x 0.0.51-beta.2 → 0.0.51-beta.4
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/components/chat-content/file-content/upload-image-item.vue.d.ts +2 -0
- package/dist/components/chat-content/file-content/upload-spinner.vue.d.ts +3 -0
- package/dist/components/chat-input/chat-input.vue.d.ts +7 -3
- package/dist/index.css +1 -1
- package/dist/index.js +1044 -1010
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +4 -1
- package/dist/mcp/generated/docs/chat-container.md +1 -1
- package/dist/mcp/generated/docs/chat-input.md +11 -10
- package/dist/mcp/generated/docs/file-content.md +27 -10
- package/dist/mcp/generated/index.json +3 -3
- package/dist/mcp/index.js +0 -0
- package/package.json +21 -23
- package/skills/blueking-chat-x/SKILL.md +0 -139
- package/skills/blueking-chat-x/references/_index.md +0 -131
- package/skills/blueking-chat-x/references/components/activity-layout.md +0 -134
- package/skills/blueking-chat-x/references/components/activity-message.md +0 -486
- package/skills/blueking-chat-x/references/components/ai-image.md +0 -230
- package/skills/blueking-chat-x/references/components/ai-loading.md +0 -131
- package/skills/blueking-chat-x/references/components/ai-prompt-list.md +0 -44
- package/skills/blueking-chat-x/references/components/ai-selection.md +0 -439
- package/skills/blueking-chat-x/references/components/ai-skill-list.md +0 -73
- package/skills/blueking-chat-x/references/components/ai-slash-editor.md +0 -43
- package/skills/blueking-chat-x/references/components/ai-slash-input.md +0 -56
- package/skills/blueking-chat-x/references/components/ai-slash-menu.md +0 -42
- package/skills/blueking-chat-x/references/components/animation-text.md +0 -202
- package/skills/blueking-chat-x/references/components/assistant-message.md +0 -498
- package/skills/blueking-chat-x/references/components/chat-container.md +0 -869
- package/skills/blueking-chat-x/references/components/chat-input.md +0 -769
- package/skills/blueking-chat-x/references/components/cite-content.md +0 -142
- package/skills/blueking-chat-x/references/components/code-content.md +0 -211
- package/skills/blueking-chat-x/references/components/common-error-content.md +0 -73
- package/skills/blueking-chat-x/references/components/content-render.md +0 -233
- package/skills/blueking-chat-x/references/components/delete-tool.md +0 -191
- package/skills/blueking-chat-x/references/components/desc-panel.md +0 -162
- package/skills/blueking-chat-x/references/components/detail-section.md +0 -91
- package/skills/blueking-chat-x/references/components/execution-summary.md +0 -128
- package/skills/blueking-chat-x/references/components/file-artifact-panel.md +0 -289
- package/skills/blueking-chat-x/references/components/file-content.md +0 -319
- package/skills/blueking-chat-x/references/components/file-icon.md +0 -109
- package/skills/blueking-chat-x/references/components/file-upload-btn.md +0 -159
- package/skills/blueking-chat-x/references/components/flow-agent-content.md +0 -264
- package/skills/blueking-chat-x/references/components/flow-agent-node-detail.md +0 -236
- package/skills/blueking-chat-x/references/components/highlight-keyword.md +0 -146
- package/skills/blueking-chat-x/references/components/image-content.md +0 -182
- package/skills/blueking-chat-x/references/components/image-preview-group.md +0 -184
- package/skills/blueking-chat-x/references/components/image-preview.md +0 -226
- package/skills/blueking-chat-x/references/components/info-message.md +0 -144
- package/skills/blueking-chat-x/references/components/input-attachment.md +0 -49
- package/skills/blueking-chat-x/references/components/input-info-alert.md +0 -42
- package/skills/blueking-chat-x/references/components/interrupt-message.md +0 -212
- package/skills/blueking-chat-x/references/components/key-value-content.md +0 -128
- package/skills/blueking-chat-x/references/components/knowledge-rag-content.md +0 -122
- package/skills/blueking-chat-x/references/components/latex-content.md +0 -200
- package/skills/blueking-chat-x/references/components/loading-message.md +0 -192
- package/skills/blueking-chat-x/references/components/markdown-content.md +0 -232
- package/skills/blueking-chat-x/references/components/mermaid-content.md +0 -189
- package/skills/blueking-chat-x/references/components/message-container.md +0 -645
- package/skills/blueking-chat-x/references/components/message-loading.md +0 -118
- package/skills/blueking-chat-x/references/components/message-render.md +0 -327
- package/skills/blueking-chat-x/references/components/message-time.md +0 -177
- package/skills/blueking-chat-x/references/components/message-tools.md +0 -416
- package/skills/blueking-chat-x/references/components/model-selector.md +0 -155
- package/skills/blueking-chat-x/references/components/preview-toolbar.md +0 -42
- package/skills/blueking-chat-x/references/components/questions-container.md +0 -85
- package/skills/blueking-chat-x/references/components/reasoning-message.md +0 -232
- package/skills/blueking-chat-x/references/components/reference-content.md +0 -135
- package/skills/blueking-chat-x/references/components/reference-doc-content.md +0 -109
- package/skills/blueking-chat-x/references/components/scroll-btn.md +0 -159
- package/skills/blueking-chat-x/references/components/selection-footer.md +0 -78
- package/skills/blueking-chat-x/references/components/selection-question.md +0 -88
- package/skills/blueking-chat-x/references/components/shortcut-btn.md +0 -204
- package/skills/blueking-chat-x/references/components/shortcut-btns.md +0 -266
- package/skills/blueking-chat-x/references/components/shortcut-render.md +0 -424
- package/skills/blueking-chat-x/references/components/simple-table.md +0 -101
- package/skills/blueking-chat-x/references/components/text-content.md +0 -77
- package/skills/blueking-chat-x/references/components/tool-approval-card.md +0 -183
- package/skills/blueking-chat-x/references/components/tool-btn.md +0 -317
- package/skills/blueking-chat-x/references/components/tool-message.md +0 -235
- package/skills/blueking-chat-x/references/components/toolcall-render.md +0 -348
- package/skills/blueking-chat-x/references/components/user-feedback.md +0 -233
- package/skills/blueking-chat-x/references/components/user-message.md +0 -424
- package/skills/blueking-chat-x/references/components/user-question-answered-card.md +0 -104
- package/skills/blueking-chat-x/references/components/user-question-card.md +0 -231
- package/skills/blueking-chat-x/references/components/user-question-choice.md +0 -105
- package/skills/blueking-chat-x/references/components/user-question-option.md +0 -42
- package/skills/blueking-chat-x/references/components/vnode-renderer.md +0 -122
- package/skills/blueking-chat-x/references/composables/use-animation-text.md +0 -196
- package/skills/blueking-chat-x/references/composables/use-artifact-preview.md +0 -231
- package/skills/blueking-chat-x/references/composables/use-clipboard.md +0 -203
- package/skills/blueking-chat-x/references/composables/use-command-selection.md +0 -150
- package/skills/blueking-chat-x/references/composables/use-container-scroll.md +0 -57
- package/skills/blueking-chat-x/references/composables/use-custom-tab.md +0 -158
- package/skills/blueking-chat-x/references/composables/use-flow-node-actions.md +0 -157
- package/skills/blueking-chat-x/references/composables/use-full-screen.md +0 -112
- package/skills/blueking-chat-x/references/composables/use-global-config.md +0 -148
- package/skills/blueking-chat-x/references/composables/use-menu-keydown.md +0 -163
- package/skills/blueking-chat-x/references/composables/use-message-group.md +0 -247
- package/skills/blueking-chat-x/references/composables/use-observer-visible-list.md +0 -188
- package/skills/blueking-chat-x/references/composables/use-parent-scrolling.md +0 -46
- package/skills/blueking-chat-x/references/theme/theme.md +0 -431
- package/skills/blueking-chat-x/references/types/constants.md +0 -307
- package/skills/blueking-chat-x/references/types/interrupt.md +0 -379
- package/skills/blueking-chat-x/references/types/messages.md +0 -553
- package/skills/blueking-chat-x/references/types/schema.md +0 -91
- package/skills/blueking-chat-x/scripts/generate-references.mjs +0 -314
|
@@ -1,148 +0,0 @@
|
|
|
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>),以 GLOBAL_CONFIG_TOKEN provide 给后代; injectGlobalConfig 在子组件中取出配置,无 Provider 时返回 undefined。ChatContainer 在 setup 中调用 useGlobalConfig 注入 size、supportUpload 与 timezone; 后代组件可通过 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`)。与 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
|
-
│ })
|
|
29
|
-
│ └── provide(GLOBAL_CONFIG_TOKEN, { size, supportUpload, timezone })
|
|
30
|
-
│
|
|
31
|
-
└── MessageContainer → … → UserMessage / MessageTime 等
|
|
32
|
-
|
|
33
|
-
UserMessage(后代)
|
|
34
|
-
├── injectGlobalConfig()
|
|
35
|
-
│ └── inject(GLOBAL_CONFIG_TOKEN) → GlobalConfig | undefined
|
|
36
|
-
└── 模板中:globalConfig?.supportUpload.value ?? false
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
- **Provider**:必须在组件树的祖先(如 `ChatContainer`)的 `setup` 中调用 `useGlobalConfig`,否则后代 `injectGlobalConfig()` 得到 `undefined`。
|
|
40
|
-
- **Consumer**:任意后代在 `setup` 中调用 `injectGlobalConfig()`,拿到与 Provider 相同的 `GlobalConfig` 对象引用;`supportUpload` 为 `ComputedRef<boolean>`,可随根 props 响应式更新。
|
|
41
|
-
|
|
42
|
-
## 渲染示例
|
|
43
|
-
|
|
44
|
-
## 根容器用法(ChatContainer)
|
|
45
|
-
|
|
46
|
-
```vue
|
|
47
|
-
<script setup lang="ts">
|
|
48
|
-
import { computed } from 'vue';
|
|
49
|
-
import { useGlobalConfig } from '@blueking/chat-x';
|
|
50
|
-
|
|
51
|
-
const props = defineProps<{
|
|
52
|
-
size?: 'normal' | 'small';
|
|
53
|
-
supportUpload?: boolean;
|
|
54
|
-
timezone?: string;
|
|
55
|
-
}>();
|
|
56
|
-
|
|
57
|
-
useGlobalConfig({
|
|
58
|
-
size: computed(() => props.size ?? 'small'),
|
|
59
|
-
supportUpload: computed(() => props.supportUpload ?? false),
|
|
60
|
-
// 无默认值:未配置时由 MessageTime 回退到浏览器时区
|
|
61
|
-
timezone: computed(() => props.timezone),
|
|
62
|
-
});
|
|
63
|
-
</script>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## 子组件用法(UserMessage)
|
|
67
|
-
|
|
68
|
-
```vue
|
|
69
|
-
<script setup lang="ts">
|
|
70
|
-
import { injectGlobalConfig } from '@blueking/chat-x';
|
|
71
|
-
|
|
72
|
-
const globalConfig = injectGlobalConfig();
|
|
73
|
-
</script>
|
|
74
|
-
|
|
75
|
-
<template>
|
|
76
|
-
<ChatInput :support-upload="globalConfig?.supportUpload.value ?? false" />
|
|
77
|
-
</template>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## API
|
|
81
|
-
|
|
82
|
-
### 类型与函数签名(摘要)
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
import type { ComputedRef } from 'vue';
|
|
86
|
-
|
|
87
|
-
export const GLOBAL_CONFIG_TOKEN: unique symbol;
|
|
88
|
-
|
|
89
|
-
export type AiSizeMode = 'normal' | 'small';
|
|
90
|
-
|
|
91
|
-
export type GlobalConfig = {
|
|
92
|
-
size?: ComputedRef<AiSizeMode>;
|
|
93
|
-
supportUpload: ComputedRef<boolean>;
|
|
94
|
-
timezone?: ComputedRef<string | undefined>;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export function useGlobalConfig(options: GlobalConfig): {
|
|
98
|
-
size?: ComputedRef<AiSizeMode>;
|
|
99
|
-
supportUpload: ComputedRef<boolean>;
|
|
100
|
-
timezone?: ComputedRef<string | undefined>;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export function injectGlobalConfig(): GlobalConfig | undefined;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### `GLOBAL_CONFIG_TOKEN`
|
|
107
|
-
|
|
108
|
-
| 名称 | 说明 |
|
|
109
|
-
| --------------------- | -------------------------------------- |
|
|
110
|
-
| `GLOBAL_CONFIG_TOKEN` | `provide` / `inject` 使用的 Symbol key |
|
|
111
|
-
|
|
112
|
-
### `GlobalConfig`
|
|
113
|
-
|
|
114
|
-
| 字段 | 说明 |
|
|
115
|
-
| --------------- | ------------------------------------------------------------------------ |
|
|
116
|
-
| `size` | 可选。字号主题档位 `normal`(14px)/ `small`(12px),与 `ChatContainer.size` 对齐 |
|
|
117
|
-
| `supportUpload` | 是否支持上传,与根容器 `ChatContainer` 的 `supportUpload` 等展示策略对齐 |
|
|
118
|
-
| `timezone` | 可选。消息时间展示所用的 IANA 时区名,与 `ChatContainer.timezone` 对齐;未配置时 `MessageTime` 按浏览器时区展示 |
|
|
119
|
-
|
|
120
|
-
### `useGlobalConfig(options)`
|
|
121
|
-
|
|
122
|
-
| 参数 | 说明 |
|
|
123
|
-
| ----------------------- | ------------------------------------------------------------------------------------- |
|
|
124
|
-
| `options.size` | 可选。字号主题档位,建议使用 `computed(() => props.size ?? 'small')` 与根 props 同步 |
|
|
125
|
-
| `options.supportUpload` | 是否支持上传,建议使用 `computed(() => props.supportUpload ?? false)` 与根 props 同步 |
|
|
126
|
-
| `options.timezone` | 可选。消息时间时区,建议使用 `computed(() => props.timezone)` 与根 props 同步;不设默认值,交由 `MessageTime` 回退浏览器时区 |
|
|
127
|
-
|
|
128
|
-
- 调用后立即 `provide(GLOBAL_CONFIG_TOKEN, options)`。
|
|
129
|
-
- 必须在具有组件实例上下文的 `setup` 中调用(与 Vue `provide` 要求一致)。
|
|
130
|
-
|
|
131
|
-
### `injectGlobalConfig()`
|
|
132
|
-
|
|
133
|
-
| 返回值 | 说明 |
|
|
134
|
-
| -------------- | ----------------------------------------------------------- |
|
|
135
|
-
| `GlobalConfig` | 祖先已调用 `useGlobalConfig` 时,与 Provider 传入的同一对象 |
|
|
136
|
-
| `undefined` | 组件树中无对应 `provide` 时 |
|
|
137
|
-
|
|
138
|
-
## 注意事项
|
|
139
|
-
|
|
140
|
-
1. **Provider 须在祖先调用**:子组件的 `injectGlobalConfig` 依赖同一组件树内的 `useGlobalConfig`。
|
|
141
|
-
2. **可选链访问**:无 Provider 时返回 `undefined`,模板中建议使用 `globalConfig?.supportUpload.value ?? false`。
|
|
142
|
-
3. **扩展配置**:后续若在 `GlobalConfig` 中增加字段,应在根容器统一传入并在文档中说明。
|
|
143
|
-
|
|
144
|
-
## 关联组件
|
|
145
|
-
|
|
146
|
-
- [ChatContainer](../components/setup/chat-container) — 调用 `useGlobalConfig` 注入 `size`、`supportUpload` 与 `timezone`
|
|
147
|
-
- [MessageTime](../components/feedback/message-time) — 读取 `timezone` 展示消息时间
|
|
148
|
-
- [主题配置](../theme/theme) — `data-ai-size` 与 CSS 变量说明
|
|
@@ -1,163 +0,0 @@
|
|
|
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 项。 AiSlashMenu、AiPromptList(ChatInput 子模块)内部使用。
|
|
6
|
-
|
|
7
|
-
**关联**:chat-input(AiSlashMenu / AiPromptList 键盘导航)
|
|
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
|
-
## 在 AiSlashInput 内部的实际用法
|
|
97
|
-
|
|
98
|
-
`useMenuKeydown` 被 `AiSlashMenu`(`@` 资源菜单)和 `AiPromptList`(`/` 提示词列表)内部使用:
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// AiPromptList 中
|
|
102
|
-
const promptListRef = useTemplateRef<HTMLElement>('promptListRef');
|
|
103
|
-
const { activeIndex } = useMenuKeydown<string>({
|
|
104
|
-
items: computed(() => props.prompts), // ComputedRef 也可传入
|
|
105
|
-
onSelect: props.onSelect,
|
|
106
|
-
menuRef: promptListRef,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// AiSlashMenu 中
|
|
110
|
-
const menuRef = useTemplateRef<HTMLElement>('menuRef');
|
|
111
|
-
const { activeIndex } = useMenuKeydown<IAiSlashMenuItem>({
|
|
112
|
-
items: sortedResourceList,
|
|
113
|
-
onSelect: props.onSelect,
|
|
114
|
-
menuRef,
|
|
115
|
-
});
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## API
|
|
119
|
-
|
|
120
|
-
### 参数
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
useMenuKeydown<T>(props: {
|
|
124
|
-
items: ShallowRef<T[]>; // 菜单项列表
|
|
125
|
-
menuRef: Readonly<ShallowRef<HTMLElement | null>>; // 菜单容器 DOM 引用(用于可见性检测和滚动定位)
|
|
126
|
-
onSelect: (item: T) => void; // 选中回调
|
|
127
|
-
}): { activeIndex: ShallowRef<number> }
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 参数说明
|
|
131
|
-
|
|
132
|
-
| 参数 | 类型 | 说明 |
|
|
133
|
-
| ---------- | --------------------------------- | ----------------------------------------------------------- |
|
|
134
|
-
| `items` | `ShallowRef<T[]>` | 菜单项数组;为空时所有按键均不响应 |
|
|
135
|
-
| `menuRef` | `ShallowRef<HTMLElement \| null>` | 菜单容器引用;`offsetParent === null`(不可见)时按键不响应 |
|
|
136
|
-
| `onSelect` | `(item: T) => void` | Enter 确认时触发,传入当前 `activeIndex` 对应的项 |
|
|
137
|
-
|
|
138
|
-
### 返回值
|
|
139
|
-
|
|
140
|
-
| 属性名 | 类型 | 初始值 | 说明 |
|
|
141
|
-
| ------------- | -------------------- | ------ | ------------------------------------------------------------------------------ |
|
|
142
|
-
| `activeIndex` | `ShallowRef<number>` | `0` | 当前高亮项索引;需在模板中绑定 `.is-active` 类;鼠标 `mouseenter` 也可修改此值 |
|
|
143
|
-
|
|
144
|
-
### 按键行为(硬编码,不可自定义)
|
|
145
|
-
|
|
146
|
-
| 按键 | 行为 |
|
|
147
|
-
| ----------------------- | -------------------------------------- |
|
|
148
|
-
| `ArrowUp` | 高亮上一项(从第 0 项循环到末尾) |
|
|
149
|
-
| `ArrowDown` | 高亮下一项(从末尾循环到第 0 项) |
|
|
150
|
-
| `Enter` / `NumpadEnter` | 选中当前高亮项,调用 `onSelect` |
|
|
151
|
-
| `Escape` | 无处理(不在此 composable 负责范围内) |
|
|
152
|
-
|
|
153
|
-
## 注意事项
|
|
154
|
-
|
|
155
|
-
1. **`handleKeydown` 内部自动注册**:在 `window` 捕获阶段(第三参数 `true`)绑定,优先于页面其他元素;调用方**无需**手动绑定 `@keydown`
|
|
156
|
-
2. **`.is-active` 类名约定**:`scrollToActive` 通过 `menuRef.querySelector('.is-active')` 定位元素,项目模板必须在 `activeIndex === i` 时添加此类
|
|
157
|
-
3. **`activeIndex` 不自动重置**:列表内容(`items`)变化时,`activeIndex` 保持不变。如需重置(如过滤后),需在外部 `watch` items 并手动将 `activeIndex.value = 0`
|
|
158
|
-
4. **可见性检测依赖 `offsetParent`**:元素通过 `display:none` 隐藏时 `offsetParent === null`,按键会被忽略;`visibility:hidden` 或 `opacity:0` 不会被忽略
|
|
159
|
-
5. **捕获阶段拦截**:`ArrowUp`/`ArrowDown`/`Enter` 均调用 `e.preventDefault()` 和 `e.stopPropagation()`,防止方向键滚动页面或 Enter 提交表单
|
|
160
|
-
|
|
161
|
-
## 关联组件
|
|
162
|
-
|
|
163
|
-
- [ChatInput](../components/input/chat-input) — `@` 菜单与 `/` 提示词列表
|
|
@@ -1,247 +0,0 @@
|
|
|
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` 拍平当前会话所有 `AssistantMessage.property.artifacts`,供 `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
|
-
if (message.role !== MessageRole.Assistant) continue;
|
|
130
|
-
const artifacts = message.property?.artifacts;
|
|
131
|
-
if (!artifacts?.length) continue;
|
|
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
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return Array.from(byOutputId.values());
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
> `SessionArtifact` 即为 `AIFileInfo` 别名;文件名可能重复,不可作唯一键。
|
|
144
|
-
|
|
145
|
-
预览命中与取链见 [useArtifactPreview](./use-artifact-preview);侧栏列表与分类型预览(`ArtifactPreviewHost`)见 [FileArtifactPanel](../components/message/file-artifact-panel)。
|
|
146
|
-
|
|
147
|
-
## 待审批统计
|
|
148
|
-
|
|
149
|
-
`useMessageGroup` 会统计消息列表中处于待审批状态的 AI Dev 审批中断:
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
const pendingApprovalStatusSet = new Set([APPROVAL_STATUS.PENDING, APPROVAL_STATUS.DRAFT]);
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
当 `MessageRole.Interrupt` 消息的 `content.outcome.type === 'interrupt'`,且其中断项满足 `reason === InterruptReason.AIDevToolApproval`、`metadata.ticket.status` 为 `pending` 或 `draft` 时,计入 `pendingApprovalCount`。
|
|
156
|
-
|
|
157
|
-
`pendingApprovalTipText` 根据数量生成输入区提示文案:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
'当前会话有 {count} 个待审批单,如需继续,请先取消审批'
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
`ChatContainer` 会消费该返回值,向 `ChatInput` 传入 `sendDisabledTip` 并在输入区上方展示提示,从而阻止继续发送。
|
|
164
|
-
|
|
165
|
-
## 分享模式
|
|
166
|
-
|
|
167
|
-
`useMessageGroup` 提供完整的分享模式支持:
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
const {
|
|
171
|
-
isShareMode, // 是否处于分享模式
|
|
172
|
-
isAllSelected, // 是否全选
|
|
173
|
-
onToggleShareAll, // 切换全选
|
|
174
|
-
onCancelShare, // 取消分享(清空选中 + 退出分享模式)
|
|
175
|
-
onConfirmShare, // 确认分享(返回选中的消息)
|
|
176
|
-
} = useMessageGroup(options);
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
选中联动规则:
|
|
180
|
-
|
|
181
|
-
- 选中用户消息组 → 其后紧邻的 AI 回复组视觉联动选中
|
|
182
|
-
- 取消用户消息组 → 关联 AI 回复组同时取消
|
|
183
|
-
|
|
184
|
-
## 使用示例
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
import { computed, ref as deepRef, shallowRef } from 'vue';
|
|
188
|
-
import { useMessageGroup, type Message } from '@blueking/chat-x';
|
|
189
|
-
|
|
190
|
-
const keyword = shallowRef('');
|
|
191
|
-
const messages = computed(() => props.messages);
|
|
192
|
-
const selectedUserMessages = deepRef<Message[]>([]);
|
|
193
|
-
|
|
194
|
-
const {
|
|
195
|
-
messageGroups,
|
|
196
|
-
executionGroups,
|
|
197
|
-
pendingApprovalCount,
|
|
198
|
-
pendingApprovalTipText,
|
|
199
|
-
isShareMode,
|
|
200
|
-
isAllSelected,
|
|
201
|
-
onToggleShareAll,
|
|
202
|
-
onCancelShare,
|
|
203
|
-
onConfirmShare,
|
|
204
|
-
} = useMessageGroup({
|
|
205
|
-
keyword,
|
|
206
|
-
messages,
|
|
207
|
-
selectedUserMessages,
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## 返回值说明
|
|
212
|
-
|
|
213
|
-
| 属性/方法名 | 类型 | 说明 |
|
|
214
|
-
| ---------------- | ----------------------------- | --------------------------------------------------------------------------- |
|
|
215
|
-
| messageGroups | `Ref<MessageGroup[]>` | 完整消息分组列表 |
|
|
216
|
-
| executionGroups | `ComputedRef<MessageGroup[]>` | 仅包含执行类消息的分组(工具调用 + FlowAgent),自动提取 `userMessageTitle` |
|
|
217
|
-
| sessionArtifacts | `ComputedRef<SessionArtifact[]>` | 拍平会话所有 AssistantMessage 文件产物,按 `outputId` 去重(保留最后一次) |
|
|
218
|
-
| pendingApprovalCount | `ComputedRef<number>` | 当前消息中待审批 AI Dev 审批中断的数量 |
|
|
219
|
-
| pendingApprovalTipText | `ComputedRef<string>` | 待审批阻塞发送提示文案;无待审批时为空字符串 |
|
|
220
|
-
| isShareMode | `ShallowRef<boolean>` | 是否处于分享模式 |
|
|
221
|
-
| isAllSelected | `ComputedRef<boolean>` | 所有用户消息组是否全部选中 |
|
|
222
|
-
| onToggleShareAll | `(checked: boolean) => void` | 切换全选 |
|
|
223
|
-
| onCancelShare | `() => void` | 取消分享模式 |
|
|
224
|
-
| onConfirmShare | `() => Message[]` | 确认分享,返回选中的消息数组 |
|
|
225
|
-
|
|
226
|
-
## 类型定义
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
import { type MessageGroup } from '@blueking/chat-x';
|
|
230
|
-
|
|
231
|
-
interface MessageGroup {
|
|
232
|
-
uid: string;
|
|
233
|
-
type: MessageRole;
|
|
234
|
-
messages: Message[];
|
|
235
|
-
checked: boolean;
|
|
236
|
-
isHover: boolean;
|
|
237
|
-
pause?: boolean;
|
|
238
|
-
startTime?: number;
|
|
239
|
-
userMessageTitle?: number | string;
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## 关联组件
|
|
244
|
-
|
|
245
|
-
- [ChatContainer](../components/setup/chat-container) — 调用 useMessageGroup 并下传分组
|
|
246
|
-
- [MessageContainer](../components/setup/message-container) — 渲染 messageGroups
|
|
247
|
-
- [ExecutionSummary](../components/agent/execution-summary) — 消费 executionGroups
|