From fb09553b79f60063848ff1c8eae4ff4289c81d9f Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Sat, 18 Apr 2026 11:24:01 +0800 Subject: [PATCH] fix(frontend): defer cursor offset update in onCategoryEnter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When @category is selected, ms.setCursorOffset and ms.setValue were called in the same event loop tick — the cursor sync effect later read the DOM before the new mention value flushed, restoring the old caret position. Defer ms.setCursorOffset via setTimeout so it fires after the DOM reconciliation. Same fix applied to insertCategory imperative handle. --- src/components/room/RoomChatPanel.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/room/RoomChatPanel.tsx b/src/components/room/RoomChatPanel.tsx index 972b477..94b18c4 100644 --- a/src/components/room/RoomChatPanel.tsx +++ b/src/components/room/RoomChatPanel.tsx @@ -131,8 +131,10 @@ const ChatInputArea = memo(function ChatInputArea({ const newCursorPos = startPos + 1 + category.length + 1; onDraftChangeRef.current(newValue); ms.setValue(newValue); - ms.setCursorOffset(newCursorPos); - ms.setShowMentionPopover(!!newValue.substring(0, newCursorPos).match(/@([^:@\s]*)(:([^\s]*))?$/)); + setTimeout(() => { + ms.setCursorOffset(newCursorPos); + ms.setShowMentionPopover(!!newValue.substring(0, newCursorPos).match(/@([^:@\s]*)(:([^\s]*))?$/)); + }, 0); }, })); @@ -247,7 +249,8 @@ const ChatInputArea = memo(function ChatInputArea({ const newCursorPos = startPos + 1 + category.length + 1; onDraftChangeRef.current(newValue); ms.setValue(newValue); - ms.setCursorOffset(newCursorPos); + // Defer cursor update until after DOM has flushed the new mention value + setTimeout(() => ms.setCursorOffset(newCursorPos), 0); }} suggestions={ms.suggestions} selectedIndex={ms.selectedIndex}