mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
Debounce MutationObserver and selectionchange in MarkdownEditor
The MutationObserver for mention decoration fired decorateProjectMentions synchronously on every character change, querying all links and writing to the DOM on each keystroke. The selectionchange listener similarly ran checkMention (which calls getBoundingClientRect) on every cursor move. Coalesce both into single requestAnimationFrame callbacks so multiple rapid mutations/selections batch into one decoration pass per frame. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
4a6d3a0238
commit
96f72be88e
1 file changed
+22
-4
@@ -509,11 +509,21 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
|
||||
// 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<MarkdownEditorRef, MarkdownEditorProps>
|
||||
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(
|
||||
|
||||
Reference in new issue
Block a user