加固:粘贴接管改为「真的插进正文之后」才生效,插入为空时放行默认粘贴
ResourceReferenceInput 的 PASTE_COMMAND 处理器原先先 preventDefault 再去插入:插入为空时这段文字既不进我们的插入、又被挡掉了默认导入。现在先做插入,只有插入确实发生了才 preventDefault 并返回 true,否则返回 false 交给 Lexical 的默认粘贴 $insertContentAtSelection 改为返回「插进去没有」:选区取不到、或每个 part 都没落下内容时返回 false 编辑器已在一次更新里时 editor.update 会把回调排队,这时按原口径先接管(ranSync 标记区分「回调已同步跑完」与「回调被排队」),避免误判成插入为空而放行默认粘贴造成双份插入 同步 AGC 聊天素材引用功能说明的粘贴解析小节
This commit is contained in:
+33
-10
@@ -325,18 +325,28 @@ function $selectionOrRootEnd() {
|
||||
*
|
||||
* 认不出的引用 part(provider 的 `toReference` 解析不出来)退回它的 token 文本,
|
||||
* 绝不静默丢掉——粘贴进来的内容一个字符都不会凭空消失。
|
||||
*
|
||||
* 返回「这次到底插进去没有」:调用方据此决定要不要接管这次粘贴,插入为空时必须放行
|
||||
* 编辑器的默认粘贴,否则这段文字两边都不管。
|
||||
*/
|
||||
function $insertContentAtSelection(
|
||||
content: readonly DirectCodexUserContentPart[],
|
||||
providers: readonly ReferenceProvider[],
|
||||
) {
|
||||
): boolean {
|
||||
// 每插一段都重新取一次选区:插入会移动光标,上一轮拿到的那个 RangeSelection 会过期。
|
||||
if (!$selectionOrRootEnd()) return;
|
||||
if (!$selectionOrRootEnd()) return false;
|
||||
let inserted = false;
|
||||
content.forEach((part) => {
|
||||
if (part.type === 'input_text') {
|
||||
part.text.split('\n').forEach((line, index) => {
|
||||
if (index > 0) $selectionOrRootEnd()?.insertParagraph();
|
||||
if (line) $selectionOrRootEnd()?.insertText(line);
|
||||
if (index > 0) {
|
||||
$selectionOrRootEnd()?.insertParagraph();
|
||||
inserted = true;
|
||||
}
|
||||
if (line) {
|
||||
$selectionOrRootEnd()?.insertText(line);
|
||||
inserted = true;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -345,12 +355,17 @@ function $insertContentAtSelection(
|
||||
$selectionOrRootEnd()?.insertNodes([
|
||||
$createResourceReferenceNode(reference),
|
||||
]);
|
||||
inserted = true;
|
||||
return;
|
||||
}
|
||||
// 解析不出的 part 已经在上游被摘掉了 token,这里必须把文本补回去,否则这段内容会静默消失。
|
||||
const token = mentionTokenFromPart(providers, part);
|
||||
if (token) $selectionOrRootEnd()?.insertText(token);
|
||||
if (token) {
|
||||
$selectionOrRootEnd()?.insertText(token);
|
||||
inserted = true;
|
||||
}
|
||||
});
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -567,9 +582,9 @@ function ResourceReferenceEditor({
|
||||
);
|
||||
}, [editor, multiline]);
|
||||
|
||||
// 粘贴解析:只有「粘贴文本里真的解析出了引用 token」时才接管,其余一律返回 false 放行
|
||||
// Lexical 的默认导入——同 namespace 复制出来的 Lexical payload 本来就能还原真 chip,
|
||||
// 不含 token 的纯文本、图片文件粘贴也都保持原行为。
|
||||
// 粘贴解析:只有「粘贴文本里真的解析出了引用 token」且「这一整段真的插进了正文」时才接管,
|
||||
// 其余一律返回 false 放行 Lexical 的默认导入——同 namespace 复制出来的 Lexical payload
|
||||
// 本来就能还原真 chip,不含 token 的纯文本、图片文件粘贴也都保持原行为。
|
||||
//
|
||||
// 接管时整段文本在一次 update 内插入(与默认粘贴同样打 PASTE_TAG),所以一次 Ctrl+Z 就整体
|
||||
// 回退;token 之外的每个字符逐字保留(换行落成段落分隔,与默认纯文本粘贴同形状)。
|
||||
@@ -592,14 +607,22 @@ function ResourceReferenceEditor({
|
||||
);
|
||||
const content = buildContentFromPastedText(text, references);
|
||||
if (!content) return false;
|
||||
event.preventDefault();
|
||||
// 先真的插进去,再决定接管这次粘贴:插入为空(取不到选区)时必须放行默认粘贴,
|
||||
// 否则这段文字既没进我们的插入、又被 preventDefault 挡掉了默认导入,静默消失。
|
||||
// 编辑器已经在一次更新里时 `editor.update` 会把回调排队,这时 `ranSync` 仍是 false,
|
||||
// 按原口径先接管,等队列里的那次插入落地。
|
||||
let ranSync = false;
|
||||
let inserted = false;
|
||||
editor.update(
|
||||
() => {
|
||||
$insertContentAtSelection(content, providersRef.current);
|
||||
ranSync = true;
|
||||
inserted = $insertContentAtSelection(content, providersRef.current);
|
||||
},
|
||||
// 与编辑器默认粘贴同一口径:粘贴是它自己的一条撤销记录。
|
||||
{ tag: PASTE_TAG },
|
||||
);
|
||||
if (ranSync && !inserted) return false;
|
||||
event.preventDefault();
|
||||
return true;
|
||||
},
|
||||
COMMAND_PRIORITY_CRITICAL,
|
||||
|
||||
@@ -39,7 +39,7 @@ AGC 聊天输入框支持以结构化引用标记当前项目已登记素材,
|
||||
- 宁可不成芯片也不能认错:同名多候选(同一个 token 对应多条引用身份)一律按文本保留;未命中的 token 静默保留,不提示、也不猜文件名或路径。
|
||||
- 名字为空的引用(token 只剩一个裸触发符)同样不参与解析:裸 `@` / `$` 会在正文里匹配到任何一处触发符,认下来就是把无关文字错认成引用。显示名一侧另有兜底(见下节「词干为空的素材名回退 `asset.id`」),这条是解析侧的最后一道。
|
||||
- 附件与运行画面区域的 token(`@附件名` / `@区域标签`)不参与解析——这两类引用没有候选,身份来自文件与运行记录,纯文本重建不出来,所以粘贴时按文本保留。
|
||||
- 只有真的解析出引用时才接管粘贴:同 namespace 的 Lexical 负载(跨输入区复制芯片)、不含 token 的纯文本、图片文件粘贴都继续走编辑器默认导入,现有行为不变。接管时整段文本在一次编辑更新内插入,一次 Ctrl+Z 就是一次撤销。
|
||||
- 只有真的解析出引用、并且这一整段真的插进了正文时才接管粘贴:同 namespace 的 Lexical 负载(跨输入区复制芯片)、不含 token 的纯文本、图片文件粘贴都继续走编辑器默认导入,现有行为不变。取不到选区时插入为空,这时也放行默认粘贴,粘贴的文字不会「两边都不管」。接管时整段文本在一次编辑更新内插入,一次 Ctrl+Z 就是一次撤销。
|
||||
- 候选来源按「模糊 / 精确」分两个口:菜单走 `fuzzyLookup(query)`(包含匹配 + 截断到候选上限),粘贴解析走 `lookup()`(就绪的全量候选,不模糊、不截断)。
|
||||
- Skill 目录是应用级异步读取,仍然只在用户第一次敲出 `$` 时读:冷启动时粘贴 `$名称` 就按字面文本保留(粘贴不会为了解析去提前读盘,也不会等待目录),用户敲过一次 `$` 之后粘贴即可重建芯片。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user