fix: defer moveCursor to next tick to prevent Slate DOM resolution crash
CI / Build & Quality Checks (push) Successful in 11m12s

When selecting an autocomplete suggestion (user/room mention, emoticon,
command), Slate's replaceWithElement inserts a void inline node into the
model but React hasn't flushed the DOM update yet. Calling
Transforms.move + insertText immediately after causes ReactEditor.toDOMNode
to fail with "Cannot resolve a DOM node from slate node: {\"text\":\" \"}".

Fix: wrap moveCursor body in setTimeout(fn, 0) so React can flush the void
element's DOM node before Slate attempts to resolve the cursor position.
Also call ReactEditor.focus to restore editor focus after the autocomplete
menu item click blurs the editor.

Fixes all callers: UserMentionAutocomplete, RoomMentionAutocomplete,
EmoticonAutocomplete, CommandAutocomplete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 17:16:37 -04:00
parent 4e6b045c57
commit dde75ee389
+10 -2
View File
@@ -1,4 +1,5 @@
import { BasePoint, BaseRange, Editor, Element, Point, Range, Text, Transforms } from 'slate';
import { ReactEditor } from 'slate-react';
import { BlockType, MarkType } from './types';
import {
CommandElement,
@@ -201,8 +202,15 @@ export const replaceWithElement = (editor: Editor, selectRange: BaseRange, eleme
};
export const moveCursor = (editor: Editor, withSpace?: boolean) => {
Transforms.move(editor);
if (withSpace) editor.insertText(' ');
// Defer to the next tick so React can flush any pending void-element DOM
// updates (e.g. after inserting a mention) before Slate resolves cursor
// positions via ReactEditor.toDOMNode — otherwise Slate throws
// "Cannot resolve a DOM node from slate node".
setTimeout(() => {
ReactEditor.focus(editor);
Transforms.move(editor);
if (withSpace) editor.insertText(' ');
}, 0);
};
interface PointUntilCharOptions {