@blueking/chat-x 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/mcp/generated/docs/activity-message.md +428 -0
  2. package/dist/mcp/generated/docs/ai-image.md +227 -0
  3. package/dist/mcp/generated/docs/ai-loading.md +129 -0
  4. package/dist/mcp/generated/docs/ai-selection.md +436 -0
  5. package/dist/mcp/generated/docs/animation-text.md +199 -0
  6. package/dist/mcp/generated/docs/assistant-message.md +424 -0
  7. package/dist/mcp/generated/docs/chat-container.md +365 -0
  8. package/dist/mcp/generated/docs/chat-input.md +625 -0
  9. package/dist/mcp/generated/docs/cite-content.md +138 -0
  10. package/dist/mcp/generated/docs/code-content.md +199 -0
  11. package/dist/mcp/generated/docs/common-error-content.md +70 -0
  12. package/dist/mcp/generated/docs/constants.md +216 -0
  13. package/dist/mcp/generated/docs/content-render.md +238 -0
  14. package/dist/mcp/generated/docs/delete-tool.md +188 -0
  15. package/dist/mcp/generated/docs/desc-panel.md +139 -0
  16. package/dist/mcp/generated/docs/execution-summary.md +126 -0
  17. package/dist/mcp/generated/docs/file-content.md +300 -0
  18. package/dist/mcp/generated/docs/file-upload-btn.md +174 -0
  19. package/dist/mcp/generated/docs/flow-message.md +305 -0
  20. package/dist/mcp/generated/docs/highlight-keyword.md +144 -0
  21. package/dist/mcp/generated/docs/image-content.md +178 -0
  22. package/dist/mcp/generated/docs/image-preview-group.md +181 -0
  23. package/dist/mcp/generated/docs/image-preview.md +224 -0
  24. package/dist/mcp/generated/docs/info-message.md +124 -0
  25. package/dist/mcp/generated/docs/key-value-content.md +124 -0
  26. package/dist/mcp/generated/docs/latex-content.md +196 -0
  27. package/dist/mcp/generated/docs/loading-message.md +171 -0
  28. package/dist/mcp/generated/docs/markdown-content.md +186 -0
  29. package/dist/mcp/generated/docs/markdown-latex.md +208 -0
  30. package/dist/mcp/generated/docs/markdown-mermaid.md +250 -0
  31. package/dist/mcp/generated/docs/mermaid-content.md +185 -0
  32. package/dist/mcp/generated/docs/message-container.md +534 -0
  33. package/dist/mcp/generated/docs/message-render.md +329 -0
  34. package/dist/mcp/generated/docs/message-tools.md +376 -0
  35. package/dist/mcp/generated/docs/messages.md +472 -0
  36. package/dist/mcp/generated/docs/overflow-tips.md +209 -0
  37. package/dist/mcp/generated/docs/reasoning-message.md +233 -0
  38. package/dist/mcp/generated/docs/reference-content.md +132 -0
  39. package/dist/mcp/generated/docs/scroll-btn.md +155 -0
  40. package/dist/mcp/generated/docs/selection-footer.md +75 -0
  41. package/dist/mcp/generated/docs/shortcut-btn.md +202 -0
  42. package/dist/mcp/generated/docs/shortcut-btns.md +264 -0
  43. package/dist/mcp/generated/docs/shortcut-render.md +418 -0
  44. package/dist/mcp/generated/docs/text-content.md +74 -0
  45. package/dist/mcp/generated/docs/theme.md +388 -0
  46. package/dist/mcp/generated/docs/tool-btn.md +254 -0
  47. package/dist/mcp/generated/docs/tool-message.md +217 -0
  48. package/dist/mcp/generated/docs/toolcall-render.md +299 -0
  49. package/dist/mcp/generated/docs/use-animation-text.md +198 -0
  50. package/dist/mcp/generated/docs/use-clipboard.md +206 -0
  51. package/dist/mcp/generated/docs/use-command-selection.md +128 -0
  52. package/dist/mcp/generated/docs/use-container-scroll.md +56 -0
  53. package/dist/mcp/generated/docs/use-custom-tab.md +122 -0
  54. package/dist/mcp/generated/docs/use-global-config.md +154 -0
  55. package/dist/mcp/generated/docs/use-menu-keydown.md +164 -0
  56. package/dist/mcp/generated/docs/use-message-group.md +175 -0
  57. package/dist/mcp/generated/docs/use-observer-visible-list.md +189 -0
  58. package/dist/mcp/generated/docs/use-parent-scrolling.md +46 -0
  59. package/dist/mcp/generated/docs/user-feedback.md +229 -0
  60. package/dist/mcp/generated/docs/user-message.md +347 -0
  61. package/dist/mcp/generated/index.json +1311 -0
  62. package/dist/mcp/index.d.ts +2 -0
  63. package/dist/mcp/index.js +42 -0
  64. package/dist/mcp/index.js.map +1 -0
  65. package/dist/mcp/server.d.ts +2 -0
  66. package/dist/mcp/server.js +43 -0
  67. package/dist/mcp/server.js.map +1 -0
  68. package/dist/mcp/tools/get-component-doc.d.ts +19 -0
  69. package/dist/mcp/tools/get-component-doc.js +60 -0
  70. package/dist/mcp/tools/get-component-doc.js.map +1 -0
  71. package/dist/mcp/tools/list-components.d.ts +35 -0
  72. package/dist/mcp/tools/list-components.js +147 -0
  73. package/dist/mcp/tools/list-components.js.map +1 -0
  74. package/dist/mcp/tools/search-docs.d.ts +14 -0
  75. package/dist/mcp/tools/search-docs.js +82 -0
  76. package/dist/mcp/tools/search-docs.js.map +1 -0
  77. package/dist/mcp/utils/doc-loader.d.ts +35 -0
  78. package/dist/mcp/utils/doc-loader.js +64 -0
  79. package/dist/mcp/utils/doc-loader.js.map +1 -0
  80. package/package.json +5 -7
@@ -0,0 +1,122 @@
1
+ <!-- AI SUMMARY -->
2
+ ## 快速了解
3
+
4
+ useCustomTabProvider 返回 tabs、selectedTab、isCollapse 及 add/remove/selectCustomTab,并通过 provide 共享;可选 onTabChange 在切换时拉取数据。 useCustomTabConsumer 在后代注入同一套 API,常用于侧栏动态节点详情等。EXECUTION_TAB_NAME 标识默认「执行情况」Tab。 ChatContainer 侧栏集成 Provider 与 Tab UI。
5
+
6
+ ### 关联组件
7
+ - **chat-container** — Provider 与侧栏 Tab 主场景
8
+
9
+ ---
10
+ <!-- FULL DOC -->
11
+
12
+ # useCustomTab 自定义 Tab 管理
13
+
14
+ > **分类**:composable
15
+
16
+ Provider/Consumer 模式的自定义 Tab 管理,用于 `ChatContainer` 侧边栏的 Tab 动态管理。Provider 在 `ChatContainer` 中创建,Consumer 在任意后代组件中注入使用。
17
+
18
+ ## 函数签名
19
+
20
+ ### useCustomTabProvider
21
+
22
+ ```typescript
23
+ function useCustomTabProvider<T extends Record<string, unknown>>(options: {
24
+ onTabChange?: (tab: CustomTab<T>) => void;
25
+ }): {
26
+ tabs: ShallowRef<CustomTab<T>[]>;
27
+ selectedTab: Ref<CustomTab<T>>;
28
+ isCollapse: ShallowRef<boolean>;
29
+ addCustomTab: (tab: CustomTab<T>) => void;
30
+ removeCustomTab: (tabName: string) => void;
31
+ selectCustomTab: (tab: CustomTab<T>) => void;
32
+ };
33
+ ```
34
+
35
+ ### useCustomTabConsumer
36
+
37
+ ```typescript
38
+ function useCustomTabConsumer<T extends Record<string, unknown>>():
39
+ | undefined
40
+ | {
41
+ tabs: ShallowRef<CustomTab<T>[]>;
42
+ selectedTab: ShallowRef<CustomTab<T> | null>;
43
+ addCustomTab: (tab: CustomTab<T>) => void;
44
+ removeCustomTab: (tabName: string) => void;
45
+ selectCustomTab: (tab: CustomTab<T>) => void;
46
+ };
47
+ ```
48
+
49
+ ## 使用示例
50
+
51
+ ### Provider(容器组件)
52
+
53
+ ```typescript
54
+ import { useCustomTabProvider, EXECUTION_TAB_NAME } from '@blueking/chat-x';
55
+
56
+ const { tabs, selectedTab, isCollapse, addCustomTab, removeCustomTab, selectCustomTab } = useCustomTabProvider({
57
+ onTabChange: async tab => {
58
+ // Tab 切换时加载数据
59
+ const data = await fetchTabData(tab.name);
60
+ return data;
61
+ },
62
+ });
63
+ ```
64
+
65
+ ### Consumer(后代组件)
66
+
67
+ ```typescript
68
+ import { useCustomTabConsumer } from '@blueking/chat-x';
69
+
70
+ const tabManager = useCustomTabConsumer();
71
+
72
+ // 添加一个自定义 Tab
73
+ tabManager?.addCustomTab({
74
+ name: 'node-detail-123',
75
+ label: '节点详情',
76
+ data: {
77
+ component: NodeDetailComponent,
78
+ props: { nodeId: '123' },
79
+ },
80
+ });
81
+
82
+ // 移除 Tab
83
+ tabManager?.removeCustomTab('node-detail-123');
84
+ ```
85
+
86
+ ## 内置常量
87
+
88
+ | 常量名 | 值 | 说明 |
89
+ | -------------------- | ------------- | ------------------------- |
90
+ | `EXECUTION_TAB_NAME` | `'execution'` | 执行情况 Tab 的固定标识 |
91
+ | `CUSTOM_TAB_TOKEN` | `Symbol` | provide/inject 注入 Token |
92
+
93
+ ## 返回值说明
94
+
95
+ | 属性/方法名 | 类型 | 说明 |
96
+ | --------------- | --------------------------- | ------------------------------------------------- |
97
+ | tabs | `ShallowRef<CustomTab[]>` | 所有 Tab 列表(含默认的执行情况 Tab) |
98
+ | selectedTab | `Ref<CustomTab>` | 当前选中的 Tab |
99
+ | isCollapse | `ShallowRef<boolean>` | 侧边栏折叠状态;`addCustomTab` 时自动设为 `false` |
100
+ | addCustomTab | `(tab: CustomTab) => void` | 添加 Tab(同名 Tab 不重复添加) |
101
+ | removeCustomTab | `(tabName: string) => void` | 移除指定 Tab |
102
+ | selectCustomTab | `(tab: CustomTab) => void` | 切换到指定 Tab,触发 `onTabChange` 回调 |
103
+
104
+ ## 类型定义
105
+
106
+ ```typescript
107
+ interface CustomTab<T = Record<string, unknown>> {
108
+ label: string;
109
+ name: string;
110
+ data?: T;
111
+ }
112
+ ```
113
+
114
+ ## 设计特点
115
+
116
+ - `tabs` 使用 `shallowRef`,`addCustomTab` 通过展开新数组触发更新,避免 `UnwrapRef<T>` 类型问题
117
+ - 默认的「执行情况」Tab 始终存在且不可关闭
118
+ - `addCustomTab` 同时展开侧边栏(`isCollapse = false`)并在 `nextTick` 后自动选中新 Tab
119
+
120
+ ## 关联组件
121
+
122
+ - [ChatContainer](../components/molecular/chat-container.md) — 侧栏 Tab 与自定义面板
@@ -0,0 +1,154 @@
1
+ <!-- AI SUMMARY -->
2
+ ## 快速了解
3
+
4
+ useGlobalConfig 提供 messageSlotId('#ai-blueking-message-slot')与 rawMessageSlotId,并 provide AI_BLUEKING_MESSAGE_SLOT_ID。 useMessageSlotId 在子组件 inject 得到 ShallowRef,供 Teleport :to 挂载到消息容器内节点。名称易误解为全局配置,仅与 Teleport 插槽相关。 ChatContainer 使用 useMessageSlotId 与 messageSlotId;FlowMessage 等用 Teleport 展示详情。
5
+
6
+ ### 关联组件
7
+ - **chat-container** — 侧栏 messageSlotId 与自定义 Tab 内容区
8
+ - **flow-message** — useMessageSlotId + Teleport 流程详情
9
+
10
+ ---
11
+ <!-- FULL DOC -->
12
+
13
+ # useGlobalConfig 消息面板 Teleport 插槽
14
+
15
+ > **分类**:composable
16
+
17
+ 为消息容器内的子组件提供共享 **Teleport 目标 ID** 的 Provider/Consumer 工具。
18
+
19
+ 根容器注册一个 `id="ai-blueking-message-slot"` 的 DOM 节点,并通过 `provide` 将其 CSS 选择器下发;子组件(如 `FlowMessage`)通过 `inject` 取到该选择器,作为 `<Teleport :to="...">` 的挂载目标,从而将详情面板等覆盖层 Teleport 到消息容器内部的指定位置,而非 `body`。
20
+
21
+ > 名称"useGlobalConfig"具有一定误导性——该模块实际上**只负责管理 Teleport 插槽 ID**,不涉及任何 locale / zIndex 等通用全局配置。
22
+
23
+ ## 工作原理
24
+
25
+ ```
26
+ 根容器组件
27
+ ├── useGlobalConfig()
28
+ │ ├── provide(AI_BLUEKING_MESSAGE_SLOT_ID, computed(() => '#ai-blueking-message-slot'))
29
+ │ └── return { messageSlotId: '#ai-blueking-message-slot', rawMessageSlotId: 'ai-blueking-message-slot' }
30
+
31
+ └── <div :id="rawMessageSlotId" /> ← Teleport 实际挂载的 DOM 节点
32
+
33
+ 后代子组件(如 FlowMessage)
34
+ ├── useMessageSlotId()
35
+ │ ├── onMounted → watchEffect → inject(AI_BLUEKING_MESSAGE_SLOT_ID).value
36
+ │ └── return { messageSlotId: ShallowRef<'#ai-blueking-message-slot' | undefined> }
37
+
38
+ └── <Teleport :to="messageSlotId"> ← Teleport 至根容器内
39
+ <FlowDetail />
40
+ </Teleport>
41
+ ```
42
+
43
+ ## 渲染示例
44
+
45
+ ## 根容器用法(useGlobalConfig)
46
+
47
+ ```vue
48
+ <template>
49
+ <div class="chat-container">
50
+ <!-- 1. 创建 Teleport 挂载目标 DOM 节点 -->
51
+ <div
52
+ :id="rawMessageSlotId"
53
+ class="ai-blueking-container-slot"
54
+ />
55
+
56
+ <!-- 2. 消息列表(FlowMessage 等子组件从此处注入插槽 ID) -->
57
+ <MessageRender :messages="messages" />
58
+ </div>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { useGlobalConfig } from '@blueking/chat-x';
63
+
64
+ // provide 插槽 ID 给所有后代,同时获取 rawMessageSlotId 作为 DOM id
65
+ const { rawMessageSlotId } = useGlobalConfig();
66
+ </script>
67
+ ```
68
+
69
+ ## 子组件用法(useMessageSlotId)
70
+
71
+ ```vue
72
+ <!-- FlowMessage 内部(简化) -->
73
+ <template>
74
+ <div class="flow-message">
75
+ <!-- 节点列表... -->
76
+
77
+ <!-- 点击节点后,将详情面板 Teleport 到根容器的插槽节点 -->
78
+ <template v-if="detailVisible && messageSlotId">
79
+ <Teleport :to="messageSlotId">
80
+ <FlowDetail
81
+ :node="selectedNode"
82
+ @close="detailVisible = false"
83
+ />
84
+ </Teleport>
85
+ </template>
86
+ </div>
87
+ </template>
88
+
89
+ <script setup lang="ts">
90
+ import { useMessageSlotId } from '@blueking/chat-x';
91
+
92
+ // 注入根容器提供的 CSS 选择器('#ai-blueking-message-slot')
93
+ const { messageSlotId } = useMessageSlotId();
94
+ </script>
95
+ ```
96
+
97
+ ## API
98
+
99
+ ### useGlobalConfig()
100
+
101
+ ```typescript
102
+ function useGlobalConfig(): {
103
+ messageSlotId: ComputedRef<string>; // '#ai-blueking-message-slot'(CSS 选择器)
104
+ readonly rawMessageSlotId: string; // 'ai-blueking-message-slot'(DOM id 属性值)
105
+ };
106
+ ```
107
+
108
+ - 调用后立即 `provide(AI_BLUEKING_MESSAGE_SLOT_ID, messageSlotId)` 向后代注入
109
+ - 必须在 Vue 组件的 `setup()` 中调用(有组件上下文才能 `provide`)
110
+
111
+ ### useMessageSlotId()
112
+
113
+ ```typescript
114
+ function useMessageSlotId(): {
115
+ messageSlotId: ShallowRef<string | undefined>;
116
+ // 初始值:undefined(onMounted 前)
117
+ // 挂载后:'#ai-blueking-message-slot'(由 inject 注入)
118
+ };
119
+ ```
120
+
121
+ - `onMounted` 后通过 `watchEffect` + `inject` 填充 `messageSlotId.value`
122
+ - 无父级 Provider 时 `inject` 返回 `undefined`,`messageSlotId.value` 保持 `undefined`
123
+ - 使用前建议配合 `v-if="messageSlotId"` 防止 Teleport 找不到目标
124
+
125
+ ### getMessageSlotId()(内部辅助)
126
+
127
+ ```typescript
128
+ function getMessageSlotId(): ComputedRef<string> | undefined;
129
+ // = inject(AI_BLUEKING_MESSAGE_SLOT_ID)
130
+ ```
131
+
132
+ - 由 `useMessageSlotId` 内部调用,通常不需要直接使用
133
+
134
+ ### 导出常量
135
+
136
+ ```typescript
137
+ // Teleport 目标元素的 id 属性值
138
+ export const MESSAGE_SLOT_ID = 'ai-blueking-message-slot';
139
+
140
+ // provide/inject 使用的 Symbol key
141
+ export const AI_BLUEKING_MESSAGE_SLOT_ID: unique symbol;
142
+ ```
143
+
144
+ ## 注意事项
145
+
146
+ 1. **`useGlobalConfig` 必须在祖先组件调用**:子组件的 `useMessageSlotId` 通过 `inject` 获取,必须在同一组件树内存在对应 Provider
147
+ 2. **`messageSlotId` 在 `onMounted` 后才有值**:`useMessageSlotId` 的返回值初始为 `undefined`,应配合 `v-if="messageSlotId"` 防止 Teleport 报错
148
+ 3. **DOM 节点必须与 `rawMessageSlotId` 对应**:根容器需确保 `<div :id="rawMessageSlotId">` 存在于 DOM 中,Teleport 才能找到目标
149
+ 4. **Teleport 目标在消息容器内**:相比直接 Teleport 到 `body`,此方案让覆盖层随消息容器的布局、z-index、overflow 上下文保持一致
150
+
151
+ ## 关联组件
152
+
153
+ - [ChatContainer](../components/molecular/chat-container.md) — 侧栏插槽节点与 messageSlotId
154
+ - [FlowMessage](../components/molecular/flow-message.md) — Teleport 流程节点详情
@@ -0,0 +1,164 @@
1
+ <!-- AI SUMMARY -->
2
+ ## 快速了解
3
+
4
+ useMenuKeydown 接收 items、menuRef、onSelect,在 window 捕获阶段监听 keydown;菜单不可见(offsetParent 为空)或列表为空时不响应。 维护 activeIndex,处理 ArrowUp/ArrowDown 循环与 Enter 选中,并 scrollIntoView 当前 .is-active 项。 AiSlashMenu、AiPromptList(ChatInput 子模块)内部使用。
5
+
6
+ ### 关联组件
7
+ - **chat-input** — AiSlashMenu / AiPromptList 键盘导航
8
+
9
+ ---
10
+ <!-- FULL DOC -->
11
+
12
+ # useMenuKeydown 菜单键盘导航
13
+
14
+ > **分类**:composable
15
+
16
+ 为弹出菜单提供键盘导航能力的组合式函数。在 `onMounted` 时于 **`window` 捕获阶段**注册 `keydown` 监听,在 `onScopeDispose` 时自动移除,通过 `menuRef.offsetParent` 检测菜单可见性来决定是否响应按键。
17
+
18
+ 内部维护 `activeIndex`(高亮项索引),由调用方将其绑定到列表的 `.is-active` 样式;`handleKeydown` 完全内部管理,**无需手动绑定到模板**。
19
+
20
+ ## 工作原理
21
+
22
+ ```
23
+ onMounted:window.addEventListener('keydown', handleKeydown, true) ← 捕获阶段
24
+ onScopeDispose:window.removeEventListener('keydown', handleKeydown, true)
25
+
26
+ handleKeydown(e):
27
+ ├── !menuRef.value?.offsetParent → return(菜单不可见,忽略)
28
+ ├── !items.value?.length → return(列表为空,忽略)
29
+
30
+ ├── ArrowUp → e.preventDefault/stopPropagation
31
+ │ activeIndex = (activeIndex - 1 + length) % length ← 循环到末尾
32
+ │ scrollToActive()
33
+ ├── ArrowDown → e.preventDefault/stopPropagation
34
+ │ activeIndex = (activeIndex + 1) % length ← 循环到开头
35
+ │ scrollToActive()
36
+ └── Enter / NumpadEnter → e.preventDefault/stopPropagation
37
+ onSelect(items[activeIndex])
38
+
39
+ scrollToActive():nextTick → menuRef.querySelector('.is-active')?.scrollIntoView({ block: 'nearest' })
40
+ ```
41
+
42
+ > **`.is-active` 依赖**:`scrollToActive` 通过 CSS 类名 `.is-active` 定位当前高亮项,调用方必须在模板中将 `activeIndex` 对应的项加上此类名。
43
+
44
+ ## 渲染示例
45
+
46
+ 使用 ↑ ↓ 键移动高亮,Enter 选中:
47
+
48
+ ## 基础用法
49
+
50
+ ```vue
51
+ <template>
52
+ <!-- 菜单容器,ref 传给 useMenuKeydown 用于可见性检测 -->
53
+ <div
54
+ ref="menuRef"
55
+ class="prompt-menu"
56
+ >
57
+ <div
58
+ v-for="(item, i) in items"
59
+ :key="item.id"
60
+ :class="['menu-item', { 'is-active': activeIndex === i }]"
61
+ @click="onSelect(item)"
62
+ @mouseenter="activeIndex = i"
63
+ >
64
+ {{ item.name }}
65
+ </div>
66
+ </div>
67
+ </template>
68
+
69
+ <script setup lang="ts">
70
+ import { shallowRef, useTemplateRef } from 'vue';
71
+ import { useMenuKeydown } from '@blueking/chat-x';
72
+
73
+ const menuRef = useTemplateRef<HTMLElement>('menuRef');
74
+ const items = shallowRef([
75
+ { id: 'ask', name: '问问小鲸' },
76
+ { id: 'translate', name: '翻译文本' },
77
+ { id: 'review', name: '代码审查' },
78
+ ]);
79
+
80
+ const onSelect = (item: (typeof items.value)[0]) => {
81
+ console.log('选中:', item.name);
82
+ };
83
+
84
+ // activeIndex 由 composable 内部管理,无需手动传入
85
+ // window keydown 监听自动注册,无需手动绑定 @keydown
86
+ const { activeIndex } = useMenuKeydown({ items, menuRef, onSelect });
87
+ </script>
88
+
89
+ <style scoped>
90
+ .menu-item.is-active {
91
+ background: #e1ecff;
92
+ color: #3a84ff;
93
+ }
94
+ </style>
95
+ ```
96
+
97
+ ## 在 AiSlashInput 内部的实际用法
98
+
99
+ `useMenuKeydown` 被 `AiSlashMenu`(`@` 资源菜单)和 `AiPromptList`(`/` 提示词列表)内部使用:
100
+
101
+ ```typescript
102
+ // AiPromptList 中
103
+ const promptListRef = useTemplateRef<HTMLElement>('promptListRef');
104
+ const { activeIndex } = useMenuKeydown<string>({
105
+ items: computed(() => props.prompts), // ComputedRef 也可传入
106
+ onSelect: props.onSelect,
107
+ menuRef: promptListRef,
108
+ });
109
+
110
+ // AiSlashMenu 中
111
+ const menuRef = useTemplateRef<HTMLElement>('menuRef');
112
+ const { activeIndex } = useMenuKeydown<IAiSlashMenuItem>({
113
+ items: sortedResourceList,
114
+ onSelect: props.onSelect,
115
+ menuRef,
116
+ });
117
+ ```
118
+
119
+ ## API
120
+
121
+ ### 参数
122
+
123
+ ```typescript
124
+ useMenuKeydown<T>(props: {
125
+ items: ShallowRef<T[]>; // 菜单项列表
126
+ menuRef: Readonly<ShallowRef<HTMLElement | null>>; // 菜单容器 DOM 引用(用于可见性检测和滚动定位)
127
+ onSelect: (item: T) => void; // 选中回调
128
+ }): { activeIndex: ShallowRef<number> }
129
+ ```
130
+
131
+ ### 参数说明
132
+
133
+ | 参数 | 类型 | 说明 |
134
+ | ---------- | --------------------------------- | ----------------------------------------------------------- |
135
+ | `items` | `ShallowRef<T[]>` | 菜单项数组;为空时所有按键均不响应 |
136
+ | `menuRef` | `ShallowRef<HTMLElement \| null>` | 菜单容器引用;`offsetParent === null`(不可见)时按键不响应 |
137
+ | `onSelect` | `(item: T) => void` | Enter 确认时触发,传入当前 `activeIndex` 对应的项 |
138
+
139
+ ### 返回值
140
+
141
+ | 属性名 | 类型 | 初始值 | 说明 |
142
+ | ------------- | -------------------- | ------ | ------------------------------------------------------------------------------ |
143
+ | `activeIndex` | `ShallowRef<number>` | `0` | 当前高亮项索引;需在模板中绑定 `.is-active` 类;鼠标 `mouseenter` 也可修改此值 |
144
+
145
+ ### 按键行为(硬编码,不可自定义)
146
+
147
+ | 按键 | 行为 |
148
+ | ----------------------- | -------------------------------------- |
149
+ | `ArrowUp` | 高亮上一项(从第 0 项循环到末尾) |
150
+ | `ArrowDown` | 高亮下一项(从末尾循环到第 0 项) |
151
+ | `Enter` / `NumpadEnter` | 选中当前高亮项,调用 `onSelect` |
152
+ | `Escape` | 无处理(不在此 composable 负责范围内) |
153
+
154
+ ## 注意事项
155
+
156
+ 1. **`handleKeydown` 内部自动注册**:在 `window` 捕获阶段(第三参数 `true`)绑定,优先于页面其他元素;调用方**无需**手动绑定 `@keydown`
157
+ 2. **`.is-active` 类名约定**:`scrollToActive` 通过 `menuRef.querySelector('.is-active')` 定位元素,项目模板必须在 `activeIndex === i` 时添加此类
158
+ 3. **`activeIndex` 不自动重置**:列表内容(`items`)变化时,`activeIndex` 保持不变。如需重置(如过滤后),需在外部 `watch` items 并手动将 `activeIndex.value = 0`
159
+ 4. **可见性检测依赖 `offsetParent`**:元素通过 `display:none` 隐藏时 `offsetParent === null`,按键会被忽略;`visibility:hidden` 或 `opacity:0` 不会被忽略
160
+ 5. **捕获阶段拦截**:`ArrowUp`/`ArrowDown`/`Enter` 均调用 `e.preventDefault()` 和 `e.stopPropagation()`,防止方向键滚动页面或 Enter 提交表单
161
+
162
+ ## 关联组件
163
+
164
+ - [ChatInput](../components/molecular/chat-input.md) — `@` 菜单与 `/` 提示词列表
@@ -0,0 +1,175 @@
1
+ <!-- AI SUMMARY -->
2
+ ## 快速了解
3
+
4
+ useMessageGroup 接收 keyword、messages、selectedUserMessages,通过 watchEffect 产出 messageGroups(User/Assistant/Tool 合并、末尾 Loading 注入、pause 与分享勾选等)。 executionGroups 供侧边执行摘要过滤,并暴露 isShareMode、全选与 onConfirmShare。 ChatContainer 组装后传给 MessageContainer;ExecutionSummary 消费 executionGroups。
5
+
6
+ ### 关联组件
7
+ - **chat-container** — 调用并传入 MessageContainer
8
+ - **message-container** — 必填 messageGroups 数据源
9
+ - **execution-summary** — 使用 executionGroups 与定位
10
+
11
+ ---
12
+ <!-- FULL DOC -->
13
+
14
+ # useMessageGroup 消息分组
15
+
16
+ > **分类**:composable
17
+
18
+ 核心消息分组逻辑,将原始 `Message[]` 数组转换为结构化的 `MessageGroup[]`。处理 Tool 消息合并、Loading 自动注入、执行摘要过滤和消息多选/分享等逻辑。
19
+
20
+ ## 函数签名
21
+
22
+ ```typescript
23
+ function useMessageGroup(options: {
24
+ keyword?: ShallowRef<string>;
25
+ messages: ComputedRef<Message[]>;
26
+ selectedUserMessages: Ref<Message[] | undefined>;
27
+ }): {
28
+ messageGroups: Ref<MessageGroup[]>;
29
+ executionGroups: ComputedRef<MessageGroup[]>;
30
+ isShareMode: ShallowRef<boolean>;
31
+ isAllSelected: ComputedRef<boolean>;
32
+ onToggleShareAll: (isAllSelected: boolean) => void;
33
+ onCancelShare: () => void;
34
+ onConfirmShare: () => Message[];
35
+ };
36
+ ```
37
+
38
+ ## 分组规则
39
+
40
+ `watchEffect` 遍历 `messages` 数组,按以下规则分组:
41
+
42
+ ```
43
+ messages 原始数组(按顺序处理)
44
+
45
+ ┌────┴────┐────────────┐
46
+ │ │ │
47
+ role=user role=tool 其他 role
48
+ │ │ │
49
+ ① 将累积的 ② 通过 ③ 累积到
50
+ assistant toolCallId assistantMessages
51
+ 消息推入 找到对应的 等待 user 消息
52
+ list 作为 Assistant 触发分组
53
+ 一组,当前 消息,注入
54
+ user 单独 toolMessage
55
+ 成组 后 continue
56
+
57
+ ④ 遍历结束后将剩余 assistantMessages 推入 list
58
+ ⑤ 末尾为 user 消息 → 追加 Loading 消息组
59
+ ```
60
+
61
+ ### Tool 消息处理
62
+
63
+ `role: 'tool'` 消息不会独立渲染,而是通过 `toolCallId` 注入到对应 AssistantMessage 的 `toolCall.toolMessage` 字段:
64
+
65
+ ```typescript
66
+ // 查找对应的 Assistant 消息
67
+ const toolMessage = messages.find(m => m.role === 'assistant' && m.toolCalls?.some(t => t.id === message.toolCallId));
68
+ // 注入到 toolCall
69
+ const toolCall = toolMessage.toolCalls.find(t => t.id === message.toolCallId);
70
+ toolCall.toolMessage = message;
71
+ ```
72
+
73
+ ### pause 字段
74
+
75
+ 每个 Assistant 消息组计算 `pause` 属性:
76
+
77
+ ```typescript
78
+ pause = assistantMessages.some(m => m.property?.extra?.pause) ?? false;
79
+ ```
80
+
81
+ `pause` 为 `true` 时,`MessageContainer` 不渲染该组的 `MessageTools` 工具栏。
82
+
83
+ ## executionGroups
84
+
85
+ `executionGroups` 从 `messageGroups` 中过滤出执行类消息,供 `ExecutionSummary` 使用。每个执行组会自动从前一组用户消息中提取 `userMessageTitle`,作为执行摘要的标题显示;若无前置用户消息则回退为当前时间戳:
86
+
87
+ ```typescript
88
+ const isExecutionMessage = (m: Message): boolean => {
89
+ return (
90
+ // 带 toolCalls 的 assistant 消息
91
+ (m.role === 'assistant' && !!m.toolCalls?.length) ||
92
+ // FlowAgent 类型的 activity 消息
93
+ (m.role === 'activity' && m.activityType === 'flow_agent')
94
+ );
95
+ };
96
+ ```
97
+
98
+ 支持关键词过滤,通过 `SEARCH_TEXT_EXTRACTORS` 注册表扩展可搜索文本:
99
+
100
+ | 消息类型 | 搜索范围 |
101
+ | ---------- | ------------------------------------------------------------ |
102
+ | toolCall | `function.name`、`mcpName`、`description`、`arguments`、`id` |
103
+ | flow_agent | `task_name`、各节点 `name` |
104
+
105
+ ## 分享模式
106
+
107
+ `useMessageGroup` 提供完整的分享模式支持:
108
+
109
+ ```typescript
110
+ const {
111
+ isShareMode, // 是否处于分享模式
112
+ isAllSelected, // 是否全选
113
+ onToggleShareAll, // 切换全选
114
+ onCancelShare, // 取消分享(清空选中 + 退出分享模式)
115
+ onConfirmShare, // 确认分享(返回选中的消息)
116
+ } = useMessageGroup(options);
117
+ ```
118
+
119
+ 选中联动规则:
120
+
121
+ - 选中用户消息组 → 其后紧邻的 AI 回复组视觉联动选中
122
+ - 取消用户消息组 → 关联 AI 回复组同时取消
123
+
124
+ ## 使用示例
125
+
126
+ ```typescript
127
+ import { computed, ref as deepRef, shallowRef } from 'vue';
128
+ import { useMessageGroup, type Message } from '@blueking/chat-x';
129
+
130
+ const keyword = shallowRef('');
131
+ const messages = computed(() => props.messages);
132
+ const selectedUserMessages = deepRef<Message[]>([]);
133
+
134
+ const { messageGroups, executionGroups, isShareMode, isAllSelected, onToggleShareAll, onCancelShare, onConfirmShare } =
135
+ useMessageGroup({
136
+ keyword,
137
+ messages,
138
+ selectedUserMessages,
139
+ });
140
+ ```
141
+
142
+ ## 返回值说明
143
+
144
+ | 属性/方法名 | 类型 | 说明 |
145
+ | ---------------- | ----------------------------- | --------------------------------------------------------------------------- |
146
+ | messageGroups | `Ref<MessageGroup[]>` | 完整消息分组列表 |
147
+ | executionGroups | `ComputedRef<MessageGroup[]>` | 仅包含执行类消息的分组(工具调用 + FlowAgent),自动提取 `userMessageTitle` |
148
+ | isShareMode | `ShallowRef<boolean>` | 是否处于分享模式 |
149
+ | isAllSelected | `ComputedRef<boolean>` | 所有用户消息组是否全部选中 |
150
+ | onToggleShareAll | `(checked: boolean) => void` | 切换全选 |
151
+ | onCancelShare | `() => void` | 取消分享模式 |
152
+ | onConfirmShare | `() => Message[]` | 确认分享,返回选中的消息数组 |
153
+
154
+ ## 类型定义
155
+
156
+ ```typescript
157
+ import { type MessageGroup } from '@blueking/chat-x';
158
+
159
+ interface MessageGroup {
160
+ uuid: string;
161
+ type: MessageRole;
162
+ messages: Message[];
163
+ checked: boolean;
164
+ isHover: boolean;
165
+ pause?: boolean;
166
+ startTime?: number;
167
+ userMessageTitle?: number | string;
168
+ }
169
+ ```
170
+
171
+ ## 关联组件
172
+
173
+ - [ChatContainer](../components/molecular/chat-container.md) — 调用 useMessageGroup 并下传分组
174
+ - [MessageContainer](../components/molecular/message-container.md) — 渲染 messageGroups
175
+ - [ExecutionSummary](../components/molecular/execution-summary.md) — 消费 executionGroups