@blueking/chat-x 0.0.23 → 0.0.25
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/common/constants.d.ts +5 -0
- package/dist/components/chat-message/message-container/message-container.vue.d.ts +2 -0
- package/dist/index.css +1 -1
- package/dist/index.js +1430 -1414
- package/dist/index.js.map +1 -1
- package/dist/mcp/generated/docs/assistant-message.md +3 -3
- package/dist/mcp/generated/docs/chat-container.md +36 -6
- package/dist/mcp/generated/docs/chat-input.md +4 -0
- package/dist/mcp/generated/docs/code-content.md +10 -5
- package/dist/mcp/generated/docs/desc-panel.md +7 -8
- package/dist/mcp/generated/docs/key-value-content.md +6 -6
- package/dist/mcp/generated/docs/markdown-content.md +1 -1
- package/dist/mcp/generated/docs/message-container.md +4 -0
- package/dist/mcp/generated/docs/use-command-selection.md +40 -17
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ AI 助手消息展示组件,负责渲染 AI 回复的文本内容和工具调
|
|
|
20
20
|
## 渲染管线
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
AssistantMessage
|
|
24
|
-
├── assistant-message-content(内容区)
|
|
23
|
+
AssistantMessage(根类名:ai-assistant-message)
|
|
24
|
+
├── ai-assistant-message-content(内容区)
|
|
25
25
|
│ └── [default slot] 或 ContentRender → MarkdownContent(Markdown 渲染)
|
|
26
26
|
└── ToolCallRender × N(每个 toolCall 独立渲染,不受 slot 影响)
|
|
27
27
|
```
|
|
@@ -273,7 +273,7 @@ AI 可在一次回复中发起多个工具调用,组件依次渲染:
|
|
|
273
273
|
默认插槽替换**内容区**的渲染(即 `ContentRender` 部分),工具调用仍在内容区外独立渲染,不受插槽影响:
|
|
274
274
|
|
|
275
275
|
```
|
|
276
|
-
[自定义 slot 内容] ←
|
|
276
|
+
[自定义 slot 内容] ← 替换 ai-assistant-message-content 内默认渲染
|
|
277
277
|
[ToolCallRender] ← 不受影响,仍正常渲染
|
|
278
278
|
[ToolCallRender]
|
|
279
279
|
```
|
|
@@ -337,11 +337,12 @@ ChatContainer 的 Props 继承自 `ChatInputProps` 和 `MessageContainerProps`
|
|
|
337
337
|
|
|
338
338
|
### v-model
|
|
339
339
|
|
|
340
|
-
| 属性名 | 类型 | 说明
|
|
341
|
-
| ---------------- | --------------------- |
|
|
342
|
-
| modelValue | `string \| TagSchema` | 输入框内容,支持纯文本或标签结构
|
|
343
|
-
| selectedShortcut | `Shortcut \| null` | 当前选中的快捷指令
|
|
344
|
-
| cite | `string` | 引用内容
|
|
340
|
+
| 属性名 | 类型 | 说明 |
|
|
341
|
+
| ---------------- | --------------------- | ------------------------------------------------------------ |
|
|
342
|
+
| modelValue | `string \| TagSchema` | 输入框内容,支持纯文本或标签结构 |
|
|
343
|
+
| selectedShortcut | `Shortcut \| null` | 当前选中的快捷指令 |
|
|
344
|
+
| cite | `string` | 引用内容 |
|
|
345
|
+
| renderMode | `RenderMode` | 渲染模式(默认 `Chat`)。`Share` 模式隐藏侧边栏和折叠按钮;`Test` 模式隐藏分享按钮 |
|
|
345
346
|
|
|
346
347
|
### Events
|
|
347
348
|
|
|
@@ -373,10 +374,39 @@ ChatContainer 的 Props 继承自 `ChatInputProps` 和 `MessageContainerProps`
|
|
|
373
374
|
| removeCustomTab | `(tabName: string) => void` | 移除自定义 Tab |
|
|
374
375
|
| selectCustomTab | `(tab: CustomTab) => void` | 切换到指定 Tab |
|
|
375
376
|
|
|
377
|
+
## 渲染模式
|
|
378
|
+
|
|
379
|
+
通过 `v-model:render-mode` 控制容器的渲染行为:
|
|
380
|
+
|
|
381
|
+
| `renderMode` | 侧边栏 Tab / 折叠按钮 | 底部输入区域 | MessageTools 工具栏 | 说明 |
|
|
382
|
+
| ------------ | ---------------------- | --------------------------------- | --------------------- | -------------------------------- |
|
|
383
|
+
| `Chat` | 正常显示 | 正常显示(ChatInput / ShortcutRender / SelectionFooter) | 全部工具按钮 | 默认对话模式 |
|
|
384
|
+
| `Share` | **隐藏** | **隐藏** | **隐藏**(多选模式) | 分享预览模式,仅展示消息 |
|
|
385
|
+
| `Test` | 正常显示 | 正常显示 | 过滤掉「分享」按钮 | 测试/嵌入模式,隐藏分享入口 |
|
|
386
|
+
|
|
387
|
+
```vue
|
|
388
|
+
<template>
|
|
389
|
+
<ChatContainer
|
|
390
|
+
v-model="inputValue"
|
|
391
|
+
v-model:render-mode="renderMode"
|
|
392
|
+
:messages="messages"
|
|
393
|
+
:message-status="messageStatus"
|
|
394
|
+
:on-send-message="handleSendMessage"
|
|
395
|
+
/>
|
|
396
|
+
</template>
|
|
397
|
+
|
|
398
|
+
<script setup lang="ts">
|
|
399
|
+
import { shallowRef } from 'vue';
|
|
400
|
+
import { ChatContainer, RenderMode } from '@blueking/chat-x';
|
|
401
|
+
|
|
402
|
+
const renderMode = shallowRef<RenderMode>(RenderMode.Chat);
|
|
403
|
+
</script>
|
|
404
|
+
```
|
|
405
|
+
|
|
376
406
|
## 类型定义
|
|
377
407
|
|
|
378
408
|
```typescript
|
|
379
|
-
import { ChatContainer, type CustomTab, type Shortcut, type Message } from '@blueking/chat-x';
|
|
409
|
+
import { ChatContainer, RenderMode, type CustomTab, type Shortcut, type Message } from '@blueking/chat-x';
|
|
380
410
|
|
|
381
411
|
// 自定义 Tab
|
|
382
412
|
interface CustomTab<T = Record<string, unknown>> {
|
|
@@ -37,6 +37,10 @@ chat-input-container
|
|
|
37
37
|
|
|
38
38
|
> **注意**:`slot#attachment` 只替换快捷指令区,`FileUploadBtn` 在其外部,使用该 slot 不会移除上传按钮。`slot#send-icon` 只替换图标,按钮的点击处理和样式仍由组件控制。
|
|
39
39
|
|
|
40
|
+
### AiSlashInput 与 modelValue 同步
|
|
41
|
+
|
|
42
|
+
内部编辑器 `AiSlashInput` 在 **`modelValue` 由外部异步更新**(例如从历史会话回填、父组件重置)且与当前文档不一致时,会通过 `useCommandSelection` 提供的 `GetDocSnapshot` 读取编辑器快照,与 `docToString(modelValue)` 比对后,必要时执行 `ReplaceAll` 将编辑器内容同步为新的 `modelValue`,避免内外状态脱节。
|
|
43
|
+
|
|
40
44
|
## 基础用法
|
|
41
45
|
|
|
42
46
|
```vue
|
|
@@ -17,8 +17,10 @@ CodeContent 接收 markdown-it 的 fence/code_block token,按行 highlight.js
|
|
|
17
17
|
|
|
18
18
|
## 组件结构
|
|
19
19
|
|
|
20
|
+
样式根选择器为 **`.ai-message-container .code-content-wrapper`**:代码块头部与 `pre` 区样式仅在消息容器(如 `MessageContainer` 根上的 `ai-message-container`)下生效。Wiki 与业务中独立演示时,请将 `CodeContent` 包在带 `ai-message-container` 类名的父节点内。
|
|
21
|
+
|
|
20
22
|
```
|
|
21
|
-
.code-content-wrapper(width: 100%,margin-bottom: 12px)
|
|
23
|
+
.ai-message-container .code-content-wrapper(width: 100%,margin-bottom: 12px)
|
|
22
24
|
├── .code-content-header(height: 40px,bg: #2f333d,border: 1px solid #1a1a1a)
|
|
23
25
|
│ ├── .code-header-language(color: #999,显示 token.info 原始字符串)
|
|
24
26
|
│ ├── slot#header({ language, token })— 自定义头部操作按钮区域
|
|
@@ -40,10 +42,13 @@ CodeContent 接收 markdown-it 的 fence/code_block token,按行 highlight.js
|
|
|
40
42
|
|
|
41
43
|
```vue
|
|
42
44
|
<template>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
<!-- 完整深色头部与 pre 样式依赖父级 .ai-message-container(与对话消息区一致) -->
|
|
46
|
+
<div class="ai-message-container">
|
|
47
|
+
<CodeContent
|
|
48
|
+
:token="codeTokens"
|
|
49
|
+
@mounted="handleMounted"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
47
52
|
</template>
|
|
48
53
|
|
|
49
54
|
<script setup lang="ts">
|
|
@@ -26,14 +26,13 @@ DescPanel 将描述字符串尝试 JSON 解析为键值列表展示,否则按
|
|
|
26
26
|
│ └── {{ title }}
|
|
27
27
|
└── .desc-panel(flex column,gap: 4px)
|
|
28
28
|
├── [JSON 对象/数组] v-for 逐项渲染 .desc-panel-item
|
|
29
|
-
│ ├── .desc-label →
|
|
30
|
-
│ └── .desc-value
|
|
31
|
-
|
|
32
|
-
│ · value 为对象/数组时,tooltip 显示 JSON.stringify(value)
|
|
33
|
-
│ · value 为原始值时,tooltip 显示原始值
|
|
34
|
-
└── [非 JSON / 解析失败] 直接渲染 {{ data }}(纯文本)
|
|
29
|
+
│ ├── .desc-label → HighlightKeyword(key)
|
|
30
|
+
│ └── .desc-value → HighlightKeyword(值的文本或 JSON 字符串),`word-break: break-all`
|
|
31
|
+
└── [非 JSON / 解析失败] HighlightKeyword(data),`word-break: break-all`
|
|
35
32
|
```
|
|
36
33
|
|
|
34
|
+
> **说明**:键值与纯文本均通过 `HighlightKeyword` 展示,长内容依赖换行与面板宽度展示,**不再**使用 `v-overflow-tips` 悬停气泡。
|
|
35
|
+
|
|
37
36
|
## desc 解析规则
|
|
38
37
|
|
|
39
38
|
`data` 是一个 computed,逻辑如下:
|
|
@@ -60,7 +59,7 @@ const data = computed(() => {
|
|
|
60
59
|
| `'普通文本'` | 解析抛出 | — | 纯文本(原始字符串) |
|
|
61
60
|
| `''` / `undefined` | 解析抛出 | — | 纯文本(空白) |
|
|
62
61
|
|
|
63
|
-
>
|
|
62
|
+
> **嵌套对象**:值本身是对象时,文本区域通过 `JSON.stringify(value)` 展示完整 JSON(`HighlightKeyword` + `word-break: break-all`),不再使用悬停 tooltip。
|
|
64
63
|
|
|
65
64
|
## 基础用法:JSON 参数
|
|
66
65
|
|
|
@@ -105,7 +104,7 @@ JSON 数组同样被视为 `object`,以数组索引(`0:`、`1:`…)作为
|
|
|
105
104
|
|
|
106
105
|
## 嵌套 JSON
|
|
107
106
|
|
|
108
|
-
|
|
107
|
+
嵌套对象的值在文本区域直接渲染为 `JSON.stringify(value)` 字符串,便于在面板内换行阅读:
|
|
109
108
|
|
|
110
109
|
```vue
|
|
111
110
|
<template>
|
|
@@ -25,15 +25,15 @@ div.ai-key-value-content(flex column,gap: 8px,font-size: 12px,color: #4d
|
|
|
25
25
|
│ ├── ThinkingIcon(14×14px)
|
|
26
26
|
│ └── {{ title }}
|
|
27
27
|
└── div.ai-key-value-content(flex column,无 gap) ← 与外层同名嵌套
|
|
28
|
-
└── div.key-value-item × N(flex row,gap: 3px,height: 20px)
|
|
29
|
-
├── div.item-key → {{ item.key }}(font-weight: bold,color: #333)
|
|
28
|
+
└── div.key-value-item × N(flex row,gap: 3px,min-height: 20px)
|
|
29
|
+
├── div.item-key → {{ item.key }}(flex: 0 0 auto,font-weight: bold,color: #333)
|
|
30
30
|
├── ":" → 硬编码文本节点,冒号不属于任何 div
|
|
31
|
-
└── div.item-value → {{ item.value }}(overflow hidden,ellipsis
|
|
31
|
+
└── div.item-value → {{ item.value }}(overflow hidden,ellipsis,`word-break: break-all` 允许多行换行)
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
> **注意**:内层也是 `.ai-key-value-content` 类(与外层同名),仅用于布局,无额外样式差异。
|
|
35
35
|
> **注意**:`v-for` 使用 `item.key` 作为 `:key`,`content` 中的 `key` 字段必须唯一,否则触发 Vue 重复 key 警告。
|
|
36
|
-
> **注意**:`item.value`
|
|
36
|
+
> **注意**:`item.value` 在单行方向仍可能因 `text-overflow: ellipsis` 显示省略,但配合 `word-break: break-all` 长串会优先换行展示,**无 tooltip**(与 `DescPanel` 不同)。
|
|
37
37
|
|
|
38
38
|
## 基础用法
|
|
39
39
|
|
|
@@ -71,9 +71,9 @@ div.ai-key-value-content(flex column,gap: 8px,font-size: 12px,color: #4d
|
|
|
71
71
|
</template>
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
## 超长 value
|
|
74
|
+
## 超长 value 换行
|
|
75
75
|
|
|
76
|
-
`.item-value`
|
|
76
|
+
`.item-value` 使用 `word-break: break-all`,长 URL 或长文本会在容器内换行;仍保留 `overflow: hidden` 与 `text-overflow: ellipsis` 以约束极端情况,**悬停无 tooltip**:
|
|
77
77
|
|
|
78
78
|
## API
|
|
79
79
|
|
|
@@ -91,7 +91,7 @@ props.content → completeMarkdownSyntax → md.parse → groupTokens → groupe
|
|
|
91
91
|
|
|
92
92
|
## 代码块
|
|
93
93
|
|
|
94
|
-
代码块由 `CodeContent` 渲染,支持 highlight.js
|
|
94
|
+
代码块由 `CodeContent` 渲染,支持 highlight.js 语法高亮、语言标签、一键复制。语法高亮主题样式由 `CodeContent` 侧引入(`github-dark`),`MarkdownContent` **不再**全局引入 `highlight.js` 主题 CSS,避免与代码块组件重复加载、并保持与消息区样式一致。
|
|
95
95
|
|
|
96
96
|
## 表格
|
|
97
97
|
|
|
@@ -113,8 +113,11 @@ MessageContainer 是消息列表的核心容器。接收父组件用 useMessageG
|
|
|
113
113
|
- `role: 'tool'` 消息**不会独立渲染**,而是被注入到对应 AssistantMessage 的 `toolCall.toolMessage` 字段
|
|
114
114
|
- 若 `toolMessage.error` 存在,AssistantMessage 的 `status` 会被强制设为 `MessageStatus.Error`
|
|
115
115
|
- `MessageTools` 工具栏只在 `type === 'assistant'` 的消息组底部渲染(不依赖鼠标悬停,始终可见),且满足以下条件时**不渲染**:
|
|
116
|
+
- `renderMode === RenderMode.Share`(分享预览模式)
|
|
116
117
|
- 消息组的 `pause` 为 `true`(来源于 `message.property?.extra?.pause`)
|
|
117
118
|
- 多选模式(`enableSelection`)开启且消息组不是 Loading 类型
|
|
119
|
+
- `renderMode === RenderMode.Test` 时,工具栏会过滤掉「分享」按钮,其余正常
|
|
120
|
+
- `renderMode === RenderMode.Share` 时,`message-group-messages` 自动添加 `message-group-enabled-selection` 类名(与 `enableSelection: true` 一致的多选视觉效果)
|
|
118
121
|
- Loading 消息组的 `type` 是 `MessageRole.Loading`,不显示工具栏和多选 Checkbox
|
|
119
122
|
|
|
120
123
|
## 等待响应(Loading 自动注入)
|
|
@@ -464,6 +467,7 @@ AI 回复状态为 `error` 时,消息以错误样式展示:
|
|
|
464
467
|
| onUserAction | `(tool: IToolBtn, message: Message) => Promise<string[] \| void>` | — | 用户消息工具操作回调 |
|
|
465
468
|
| onUserInputConfirm | `(message: Message, content: UserMessage['content'], docSchema: TagSchema) => Promise<void>` | — | 用户编辑消息确认回调 |
|
|
466
469
|
| onUserShortcutConfirm | `(message: Message, formModel: Record<string, unknown>) => Promise<void>` | — | 用户快捷指令表单提交回调 |
|
|
470
|
+
| renderMode | `RenderMode` | — | 渲染模式。`Share` 模式下启用多选样式并隐藏工具栏;`Test` 模式下过滤掉「分享」按钮;不传或 `Chat` 为默认行为 |
|
|
467
471
|
|
|
468
472
|
### v-model
|
|
469
473
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- AI SUMMARY -->
|
|
2
2
|
## 快速了解
|
|
3
3
|
|
|
4
|
-
useCommandSelection 无参数,返回 commandSelection
|
|
4
|
+
useCommandSelection 无参数,返回 commandSelection、docSnapshot、GetCursorPosition、GetDocSnapshot。 GetCursorPosition 写入光标行列;GetDocSnapshot 将当前文档快照写入 docSnapshot,供外部 modelValue 与编辑器比对同步。 仅在 AiSlashInput(@ 菜单插入与 modelValue 同步)内部使用。
|
|
5
5
|
|
|
6
6
|
### 关联组件
|
|
7
7
|
- **chat-input** — AiSlashInput 内部使用
|
|
@@ -29,9 +29,16 @@ editor.command(GetCursorPosition)
|
|
|
29
29
|
commandSelection.value = { column, line }
|
|
30
30
|
↓
|
|
31
31
|
commandSelection(shallowRef,初始值 { column: 0, line: 0 })
|
|
32
|
+
|
|
33
|
+
editor.command(GetDocSnapshot)
|
|
34
|
+
└── GetDocSnapshot(doc)
|
|
35
|
+
docSnapshot.value = doc // 当前文档快照,用于与 props.modelValue 比对
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
**使用时机**:
|
|
39
|
+
|
|
40
|
+
- 在 `@` 资源插入流程中,先执行 `GetCursorPosition` 快照当前光标,再据此计算删除范围和插入位置。
|
|
41
|
+
- 在外部 `modelValue` 变化时,先执行 `GetDocSnapshot` 取得编辑器当前文档,与 `docToString(modelValue)` 比对,决定是否 `ReplaceAll` 同步。
|
|
35
42
|
|
|
36
43
|
## 概念演示
|
|
37
44
|
|
|
@@ -42,20 +49,29 @@ editor.command(GetCursorPosition)
|
|
|
42
49
|
## 在 AiSlashInput 中的实际用法
|
|
43
50
|
|
|
44
51
|
```typescript
|
|
45
|
-
|
|
52
|
+
import { watch } from 'vue';
|
|
53
|
+
|
|
54
|
+
const { commandSelection, GetCursorPosition, GetDocSnapshot, docSnapshot } = useCommandSelection();
|
|
46
55
|
|
|
47
56
|
// 用户从 @xxx 菜单中选择资源时:
|
|
48
57
|
const insertTagAtCursor = (tag: IAiSlashMenuItem) => {
|
|
49
|
-
// 1. 执行命令,快照当前光标位置 → 写入 commandSelection.value
|
|
50
58
|
editor.command(GetCursorPosition);
|
|
51
|
-
|
|
52
|
-
// 2. 读取快照的行列位置
|
|
53
59
|
const { column, line } = commandSelection.value;
|
|
54
|
-
|
|
55
|
-
// 3. 根据位置删除已输入的 "@keyword",插入 tag
|
|
56
60
|
editor.command(DeleteTag, [line, column - keyword.value.length - 1], [line, column]);
|
|
57
61
|
editor.command(InsertTag, [line, column], tag);
|
|
58
62
|
};
|
|
63
|
+
|
|
64
|
+
// 外部 modelValue 变化时,与编辑器文档对齐(简化示意;需已存在 editor、props、text、docToString)
|
|
65
|
+
watch(
|
|
66
|
+
() => props.modelValue,
|
|
67
|
+
() => {
|
|
68
|
+
editor.command(GetDocSnapshot);
|
|
69
|
+
if (docToString(docSnapshot.value || []) !== docToString(text.value || [])) {
|
|
70
|
+
editor.command(ReplaceAll, docToString(text.value || []) as unknown as string);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{ deep: false },
|
|
74
|
+
);
|
|
59
75
|
```
|
|
60
76
|
|
|
61
77
|
## API
|
|
@@ -69,7 +85,9 @@ const insertTagAtCursor = (tag: IAiSlashMenuItem) => {
|
|
|
69
85
|
| 属性名 | 类型 | 初始值 | 说明 |
|
|
70
86
|
| ------------------- | ---------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------ |
|
|
71
87
|
| `commandSelection` | `ShallowRef<{ column: number; line: number }>` | `{ column: 0, line: 0 }` | 存储最近一次执行 `GetCursorPosition` 时的光标行列位置 |
|
|
88
|
+
| `docSnapshot` | `ShallowRef<DocFragment>` | `[]` | 存储最近一次执行 `GetDocSnapshot` 时的文档快照 |
|
|
72
89
|
| `GetCursorPosition` | `EditorCommand<[]>` | — | 编辑器命令,由 `editor.command(GetCursorPosition)` 触发,将当前光标写入 `commandSelection` |
|
|
90
|
+
| `GetDocSnapshot` | `EditorCommand<[]>` | — | 编辑器命令,由 `editor.command(GetDocSnapshot)` 触发,将当前文档写入 `docSnapshot` |
|
|
73
91
|
|
|
74
92
|
## 类型说明
|
|
75
93
|
|
|
@@ -84,7 +102,9 @@ type EditorCommand<A extends unknown[]> = (
|
|
|
84
102
|
// useCommandSelection 返回值
|
|
85
103
|
interface UseCommandSelectionReturn {
|
|
86
104
|
commandSelection: ShallowRef<{ column: number; line: number }>;
|
|
105
|
+
docSnapshot: ShallowRef<DocFragment>;
|
|
87
106
|
GetCursorPosition: EditorCommand<[]>;
|
|
107
|
+
GetDocSnapshot: EditorCommand<[]>;
|
|
88
108
|
}
|
|
89
109
|
```
|
|
90
110
|
|
|
@@ -92,26 +112,29 @@ interface UseCommandSelectionReturn {
|
|
|
92
112
|
|
|
93
113
|
```typescript
|
|
94
114
|
import { shallowRef } from 'vue';
|
|
115
|
+
|
|
95
116
|
import type { EditorCommand } from '../edix';
|
|
117
|
+
import type { DocFragment } from '../edix/doc/types';
|
|
96
118
|
|
|
97
119
|
export const useCommandSelection = () => {
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
column: 0,
|
|
101
|
-
line: 0,
|
|
102
|
-
});
|
|
120
|
+
const commandSelection = shallowRef<{ column: number; line: number }>({ column: 0, line: 0 });
|
|
121
|
+
const docSnapshot = shallowRef<DocFragment>([]);
|
|
103
122
|
|
|
104
|
-
// EditorCommand:由 editor.command() 调用,注入 doc 和 selection
|
|
105
123
|
const GetCursorPosition: EditorCommand<[]> = (_doc, selection) => {
|
|
106
|
-
const [, focus] = selection;
|
|
124
|
+
const [, focus] = selection;
|
|
107
125
|
const [line, column] = focus;
|
|
108
126
|
commandSelection.value = { column, line };
|
|
109
|
-
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const GetDocSnapshot: EditorCommand<[]> = doc => {
|
|
130
|
+
docSnapshot.value = doc;
|
|
110
131
|
};
|
|
111
132
|
|
|
112
133
|
return {
|
|
113
134
|
commandSelection,
|
|
135
|
+
docSnapshot,
|
|
114
136
|
GetCursorPosition,
|
|
137
|
+
GetDocSnapshot,
|
|
115
138
|
};
|
|
116
139
|
};
|
|
117
140
|
```
|
|
@@ -121,7 +144,7 @@ export const useCommandSelection = () => {
|
|
|
121
144
|
1. **只读命令**:`GetCursorPosition` 不返回 `Transaction`,不修改编辑器文档内容,仅记录位置
|
|
122
145
|
2. **异步快照**:`commandSelection` 在 `editor.command(GetCursorPosition)` 执行后**同步**更新,下一行代码即可安全读取
|
|
123
146
|
3. **`shallowRef` 而非 `ref`**:对象引用替换触发响应式,内部字段修改不触发(此处每次整体替换,无影响)
|
|
124
|
-
4. **仅适用于 edix 编辑器**:`GetCursorPosition` 依赖 edix
|
|
147
|
+
4. **仅适用于 edix 编辑器**:`GetCursorPosition` / `GetDocSnapshot` 依赖 edix 的文档与选区格式,不适用于原生 contenteditable 或其他富文本库
|
|
125
148
|
|
|
126
149
|
## 关联组件
|
|
127
150
|
|