diff --git a/ui/src/components/MarkdownEditor.tsx b/ui/src/components/MarkdownEditor.tsx index 223b5755631..d9edaf6c175 100644 --- a/ui/src/components/MarkdownEditor.tsx +++ b/ui/src/components/MarkdownEditor.tsx @@ -509,11 +509,21 @@ export const MarkdownEditor = forwardRef // also fires after typing (e.g. space to dismiss). const onInput = () => requestAnimationFrame(checkMention); - document.addEventListener("selectionchange", checkMention); + let selRafId: number | null = null; + const onSelectionChange = () => { + if (selRafId !== null) return; + selRafId = requestAnimationFrame(() => { + selRafId = null; + checkMention(); + }); + }; + + document.addEventListener("selectionchange", onSelectionChange); el?.addEventListener("input", onInput, true); return () => { - document.removeEventListener("selectionchange", checkMention); + document.removeEventListener("selectionchange", onSelectionChange); el?.removeEventListener("input", onInput, true); + if (selRafId !== null) cancelAnimationFrame(selRafId); }; }, [checkMention, mentions, slashCommands.length]); @@ -540,15 +550,23 @@ export const MarkdownEditor = forwardRef const editable = containerRef.current?.querySelector('[contenteditable="true"]'); if (!editable) return; decorateProjectMentions(); + let rafId: number | null = null; const observer = new MutationObserver(() => { - decorateProjectMentions(); + if (rafId !== null) return; + rafId = requestAnimationFrame(() => { + rafId = null; + decorateProjectMentions(); + }); }); observer.observe(editable, { subtree: true, childList: true, characterData: true, }); - return () => observer.disconnect(); + return () => { + observer.disconnect(); + if (rafId !== null) cancelAnimationFrame(rafId); + }; }, [decorateProjectMentions]); const selectMention = useCallback(