From bb7fce9171028b9a5a30482f92b3d904457471db Mon Sep 17 00:00:00 2001 From: liangxu Date: Tue, 7 Jul 2026 23:18:29 +0800 Subject: [PATCH] fix(PromptComposer): clear empty editor fillers before chip insertion Insert a helper that strips filler-only editor content (stray
and caret-anchor text nodes) before inserting reference/annotation chips and on paste, so chips no longer land after an empty line. Also anchor the caret after each inserted/updated chip and reorder focus/insert so the caret reliably follows the new chip. --- .ai-context/ai-context | 1 + .ai-context/zero-skills | 1 + .gitignore | 7 +----- .tools/mcp-zero | 1 + .../ui/components/PromptComposer/index.tsx | 25 +++++++++++++++++-- 5 files changed, 27 insertions(+), 8 deletions(-) create mode 160000 .ai-context/ai-context create mode 160000 .ai-context/zero-skills create mode 160000 .tools/mcp-zero diff --git a/.ai-context/ai-context b/.ai-context/ai-context new file mode 160000 index 0000000..bc525ee --- /dev/null +++ b/.ai-context/ai-context @@ -0,0 +1 @@ +Subproject commit bc525eedc924fe53b5d26f27e41595cfdb347477 diff --git a/.ai-context/zero-skills b/.ai-context/zero-skills new file mode 160000 index 0000000..943a13c --- /dev/null +++ b/.ai-context/zero-skills @@ -0,0 +1 @@ +Subproject commit 943a13c5d82cbd3d8f896134ea6b34182bb4c32f diff --git a/.gitignore b/.gitignore index 6a3ff12..96d6eb1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,4 @@ frontend/.vercel/* frontend/.vercel_build_output/* output/* .DS_Store -.tools/bin/* - -# Embedded external tool/skill repos (checked out locally, not vendored) -.ai-context/ai-context/ -.ai-context/zero-skills/ -.tools/mcp-zero/ \ No newline at end of file +.tools/bin/* \ No newline at end of file diff --git a/.tools/mcp-zero b/.tools/mcp-zero new file mode 160000 index 0000000..6e87a88 --- /dev/null +++ b/.tools/mcp-zero @@ -0,0 +1 @@ +Subproject commit 6e87a88044422ba7906c139808ece2ec1fb6658f diff --git a/frontend/src/ui/components/PromptComposer/index.tsx b/frontend/src/ui/components/PromptComposer/index.tsx index befeba2..8309037 100644 --- a/frontend/src/ui/components/PromptComposer/index.tsx +++ b/frontend/src/ui/components/PromptComposer/index.tsx @@ -499,6 +499,18 @@ function ensureCaretAnchorAfter(node: Node) { return { node: anchor, offset: caretAnchor.length }; } +function removeEmptyEditorFillers(editor: HTMLElement) { + const childNodes = Array.from(editor.childNodes); + if (childNodes.length === 0) return false; + const onlyFillers = childNodes.every((node) => { + if (node instanceof HTMLBRElement) return true; + return node.nodeType === Node.TEXT_NODE && stripCaretAnchors(node.textContent ?? "").length === 0; + }); + if (!onlyFillers) return false; + editor.replaceChildren(); + return true; +} + function placeCaretAfter(node: Node) { const range = document.createRange(); const caret = isPromptChip(node) ? ensureCaretAnchorAfter(node) : null; @@ -642,6 +654,7 @@ export const PromptComposer = forwardRef< const focus = options.focus ?? true; const pending = options.pending ?? false; + removeEmptyEditorFillers(editor); if (options.replacePending) { pendingReferenceChips(editor).forEach((chip) => { if (chip.dataset.referenceId === reference.id) return; @@ -655,6 +668,7 @@ export const PromptComposer = forwardRef< const nextPending = existingPending ? pending : false; referencesRef.current.set(reference.id, { ...reference, pending: nextPending }); setReferenceChipPending(existingChip, nextPending); + ensureCaretAnchorAfter(existingChip); if (focus) { editor.focus(); placeCaretAfter(existingChip); @@ -674,10 +688,11 @@ export const PromptComposer = forwardRef< range.collapse(false); } - if (focus) editor.focus(); range.deleteContents(); range.insertNode(chip); + ensureCaretAnchorAfter(chip); if (focus) { + editor.focus(); placeCaretAfter(chip); savedRangeRef.current = window.getSelection()?.rangeCount ? window.getSelection()?.getRangeAt(0).cloneRange() ?? null : null; } @@ -741,6 +756,7 @@ export const PromptComposer = forwardRef< const editor = editorRef.current; if (!editor) return; + removeEmptyEditorFillers(editor); annotationsRef.current.set(annotation.id, annotation); const chip = createAnnotationChip(annotation); const storedRange = savedRangeRef.current; @@ -750,9 +766,10 @@ export const PromptComposer = forwardRef< range.collapse(false); } - editor.focus(); range.deleteContents(); range.insertNode(chip); + ensureCaretAnchorAfter(chip); + editor.focus(); placeCaretAfter(chip); savedRangeRef.current = window.getSelection()?.rangeCount ? window.getSelection()?.getRangeAt(0).cloneRange() ?? null : null; emitChangeFromDom(false); @@ -842,6 +859,10 @@ export const PromptComposer = forwardRef< if (!editor || !selection || selection.rangeCount === 0) return; const range = selection.getRangeAt(0); if (!rangeBelongsToEditor(range, editor)) return; + if (removeEmptyEditorFillers(editor)) { + range.selectNodeContents(editor); + range.collapse(false); + } range.deleteContents(); if (hasPromptImageReferences(pastedText)) {