From f3022f8be97c80afbb8c18d3f0b0152f7cc6d8d2 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Sun, 24 May 2026 17:16:37 -0400 Subject: [PATCH] fix: defer moveCursor to next tick to prevent Slate DOM resolution crash 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 --- src/app/components/editor/utils.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/components/editor/utils.ts b/src/app/components/editor/utils.ts index cf3b5e410..20c9cd26c 100644 --- a/src/app/components/editor/utils.ts +++ b/src/app/components/editor/utils.ts @@ -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 {