diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx index 7deef1bc7..ffbbe4035 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx @@ -300,6 +300,22 @@ function applyContentToRoot( }); } +/** + * 取当前可用的选区:选区缺失、或指向已被重建掉的节点时(跨会话恢复草稿后就是这种), + * 统一回落到草稿末尾,避免把内容插到一个已经不存在的位置。取不到时返回 `null`。 + */ +function $selectionOrRootEnd() { + let selection = $getSelection(); + if ( + !$isRangeSelection(selection) || + !selection.anchor.getNode().isAttached() + ) { + $getRoot().selectEnd(); + selection = $getSelection(); + } + return $isRangeSelection(selection) ? selection : null; +} + /** * 粘贴插入:在光标处就地插入 content 对应的节点,正文其余部分逐字不动。 * @@ -311,31 +327,19 @@ function $insertContentAtSelection( content: readonly DirectCodexUserContentPart[], providers: readonly ReferenceProvider[], ) { - // 与 insertReferences 同一处置:选区可能指向已被重建掉的节点,统一回落到草稿末尾。 - // 每插一段都重新取一次选区:插入会移动光标,缓存下来的那个 RangeSelection 会过期。 - const currentSelection = () => { - let selection = $getSelection(); - if ( - !$isRangeSelection(selection) || - !selection.anchor.getNode().isAttached() - ) { - $getRoot().selectEnd(); - selection = $getSelection(); - } - return $isRangeSelection(selection) ? selection : null; - }; - if (!currentSelection()) return; + // 每插一段都重新取一次选区:插入会移动光标,上一轮拿到的那个 RangeSelection 会过期。 + if (!$selectionOrRootEnd()) return; content.forEach((part) => { if (part.type === 'input_text') { part.text.split('\n').forEach((line, index) => { - if (index > 0) currentSelection()?.insertParagraph(); - if (line) currentSelection()?.insertText(line); + if (index > 0) $selectionOrRootEnd()?.insertParagraph(); + if (line) $selectionOrRootEnd()?.insertText(line); }); return; } const reference = referenceFromPart(providers, part); if (reference) { - currentSelection()?.insertNodes([ + $selectionOrRootEnd()?.insertNodes([ $createResourceReferenceNode(reference), ]); } @@ -413,24 +417,14 @@ function ResourceReferenceEditor({ (nextReferences: ChatReference[]) => { if (nextReferences.length === 0) return; editor.update(() => { - let selection = $getSelection(); - // 跨会话恢复草稿后选区可能仍指向已被重建掉的节点,这里统一回落到草稿末尾, - // 避免把引用插到一个已经不存在的位置。 - if ( - !$isRangeSelection(selection) || - !selection.anchor.getNode().isAttached() - ) { - $getRoot().selectEnd(); - selection = $getSelection(); - } - if ($isRangeSelection(selection)) { - selection.insertNodes( - nextReferences.flatMap((reference) => [ - $createResourceReferenceNode(reference), - $createTextNode(' '), - ]), - ); - } + const selection = $selectionOrRootEnd(); + if (!selection) return; + selection.insertNodes( + nextReferences.flatMap((reference) => [ + $createResourceReferenceNode(reference), + $createTextNode(' '), + ]), + ); }); editor.focus(); }, @@ -442,19 +436,11 @@ function ResourceReferenceEditor({ const insert = text.replace(/\s+$/u, ''); if (!insert.trim()) return; editor.update(() => { - let selection = $getSelection(); - if ( - !$isRangeSelection(selection) || - !selection.anchor.getNode().isAttached() - ) { - $getRoot().selectEnd(); - selection = $getSelection(); - } - if ($isRangeSelection(selection)) { - const rootText = $getRoot().getTextContent(); - if (rootText && !/\s$/u.test(rootText)) selection.insertText(' '); - selection.insertText(insert); - } + const selection = $selectionOrRootEnd(); + if (!selection) return; + const rootText = $getRoot().getTextContent(); + if (rootText && !/\s$/u.test(rootText)) selection.insertText(' '); + selection.insertText(insert); }); editor.focus(); },