fix(frontend): block selectionchange during MentionInput DOM updates
Root cause: when MentionInput updates innerHTML to reflect value changes, the browser resets the selection to position 0. The selectionchange event handler reads this wrong cursor position and sets ms.cursorOffset=0, breaking mentionState calculation for the popover. Fix: - MentionInput sets window.__mentionBlockSelection=true before innerHTML - Clears it via requestAnimationFrame after caret is restored - selectionchange handler skips cursor reading when flag is set
This commit is contained in:
parent
53b0b03716
commit
aac32b1b92
@ -166,7 +166,6 @@ export const MentionInput = forwardRef<HTMLDivElement, MentionInputProps>(functi
|
||||
if (!el) return;
|
||||
|
||||
// If the DOM already contains what we want, nothing to do.
|
||||
// (Programmatic updates via onChange already set the DOM correctly.)
|
||||
if (getPlainText(el) === value) {
|
||||
isUserInputRef.current = false;
|
||||
return;
|
||||
@ -176,6 +175,9 @@ export const MentionInput = forwardRef<HTMLDivElement, MentionInputProps>(functi
|
||||
const oldCaret = isUserInputRef.current ? getCaretOffset(el) : 0;
|
||||
const oldLen = internalValueRef.current.length;
|
||||
|
||||
// Block selectionchange handler from reading wrong cursor position during DOM update
|
||||
(window as any).__mentionBlockSelection = true;
|
||||
|
||||
// Update DOM
|
||||
el.innerHTML = value.trim() ? renderToHtml(value) : '';
|
||||
internalValueRef.current = value;
|
||||
@ -187,6 +189,11 @@ export const MentionInput = forwardRef<HTMLDivElement, MentionInputProps>(functi
|
||||
const newLen = value.length;
|
||||
setCaretAtOffset(el, Math.round(ratio * newLen));
|
||||
}
|
||||
|
||||
// Unblock selectionchange after DOM has settled
|
||||
requestAnimationFrame(() => {
|
||||
(window as any).__mentionBlockSelection = false;
|
||||
});
|
||||
}, [value]);
|
||||
|
||||
/** Handle input changes — extracts plain text from DOM and sends to parent */
|
||||
|
||||
@ -83,12 +83,13 @@ const ChatInputArea = memo(function ChatInputArea({
|
||||
|
||||
// ─── Track DOM cursor offset → ms.cursorOffset on user navigation ──────
|
||||
// Uses selectionchange event which fires when caret moves via arrows/clicks.
|
||||
// Does NOT fire during programmatic updates, avoiding cursor jumps.
|
||||
// Ignores programmatic DOM updates (blocked via window.__mentionBlockSelection).
|
||||
const prevCursorRef = useRef(ms.cursorOffset);
|
||||
const skipCursorTrackingRef = useRef(false);
|
||||
useEffect(() => {
|
||||
const readCursor = () => {
|
||||
if (skipCursorTrackingRef.current) return;
|
||||
if ((window as any).__mentionBlockSelection) return;
|
||||
const el = containerRef.current;
|
||||
if (!el) return;
|
||||
const sel = window.getSelection();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user