Skip to content

Commit 650728d

Browse files
committed
fix: skip editor side-effects during IME composition
Typing Chinese with the Windows IME caused characters to disappear and the cursor to jump because our custom plugins ran their update hooks during composition: the wikilink view update calls coordsAtPos and mutates an external menu element, the slash menu's shouldShow walks the doc and rebuilds the menu DOM, and the markdown listener fires setContent on the parent — each of which can interrupt the IME and make ProseMirror discard the in-flight composition. Skip all four call sites when view.composing is true. ProseMirror dispatches a transaction at compositionend, so menus and parent state catch up with the committed text on the next update. https://claude.ai/code/session_012rsJRUue9w7sBD3J5gmKWK
1 parent d4ea59d commit 650728d

1 file changed

Lines changed: 14 additions & 0 deletions

File tree

frontend/src/components/Editor/Editor.jsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class SlashMenuView {
136136
content: this.content,
137137
debounce: 50,
138138
shouldShow: (view) => {
139+
if (view.composing) return false
139140
const currentText = this.provider.getContent(
140141
view,
141142
(node) => ['paragraph', 'heading'].includes(node.type.name)
@@ -230,6 +231,9 @@ class SlashMenuView {
230231
update(view) {
231232
this.view = view
232233
this.editorViewRef.current = view
234+
// Don't query or mutate menu state mid-IME — coordsAtPos / DOM writes
235+
// triggered here can drop in-flight composition characters on Windows.
236+
if (view.composing) return
233237
this.provider.update(view)
234238
}
235239

@@ -453,6 +457,10 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi
453457
return {
454458
update(view) {
455459
wikilinkMenu._view = view
460+
// Bail during IME composition: coordsAtPos + DOM writes from
461+
// show()/hide() can disturb the active composition on Windows,
462+
// making characters disappear and the cursor jump.
463+
if (view.composing) return
456464
const { state } = view
457465
const { selection } = state
458466
if (!(selection instanceof TextSelection)) {
@@ -516,6 +524,12 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi
516524
ctx.set(rootCtx, containerRef.current)
517525
ctx.set(defaultValueCtx, defaultValue)
518526
ctx.get(listenerCtx).markdownUpdated((_ctx, markdown) => {
527+
// Skip parent state updates while IME composition is active —
528+
// the React re-render they trigger can interrupt composition on
529+
// Windows. ProseMirror dispatches a final transaction at
530+
// compositionend, which fires the listener again with the
531+
// committed text, so no input is lost.
532+
if (editorViewRef.current?.composing) return
519533
onChangeRef.current?.(markdown)
520534
})
521535
let slashMenuView = null

0 commit comments

Comments
 (0)