Skip to content

Fix IME composition Enter key handling on Windows#20

Merged
PttCodingMan merged 2 commits into
mainfrom
claude/fix-chinese-input-editor-86tSt
May 19, 2026
Merged

Fix IME composition Enter key handling on Windows#20
PttCodingMan merged 2 commits into
mainfrom
claude/fix-chinese-input-editor-86tSt

Conversation

@PttCodingMan

Copy link
Copy Markdown
Owner

Summary

Fixes a bug where pressing Enter to confirm an IME selection on Windows would trigger a spurious block split in the editor, causing the "line shifts down" symptom. The issue occurs because Windows fires a stray keydown event for Enter right after compositionend, which ProseMirror's default handler interprets as a deliberate newline.

Changes

  • New module imeGuard.js: Implements isStrayPostCompositionEnter() to detect and suppress only the stray Enter that arrives within 50ms of compositionend. The logic carefully preserves Enters that are still part of an active composition (e.g., Zhuyin pressing Enter to commit phonetic input) and those delivered to the IME itself (keyCode 229).

  • New test file imeGuard.test.js: Comprehensive test suite covering:

    • Suppression of stray Enter within the 50ms window
    • Preservation of Enters during active composition
    • Preservation of Enters delivered to the IME
    • Rejection of Enters outside the window
    • Edge case: regression test for the NO_COMPOSITION sentinel to prevent false positives during page load
  • Updated Editor.jsx: Replaced the previous composition guard logic with a cleaner implementation:

    • Removed the compositionstart handler (no longer needed)
    • Changed compositionend to record a timestamp (lastCompositionEndAt) instead of setting a boolean flag
    • Replaced the boolean check with a call to isStrayPostCompositionEnter() that uses the timestamp window
    • Removed the setTimeout(0) workaround in favor of a time-based window

Implementation Details

  • Uses performance.now() for precise timing, consistent with the timestamp recorded at compositionend
  • The 50ms window is short enough to catch the stray Enter (typically arrives within a few ms) but long enough to avoid false positives from deliberate newlines
  • The NO_COMPOSITION sentinel is Number.NEGATIVE_INFINITY to prevent false positives during the first 50ms after page load (when performance.now() is small)
  • Respects IME-specific requirements: never suppresses Enters with isComposing: true or keyCode: 229, as these are part of the IME's input flow

https://claude.ai/code/session_01KT7qFP6oQ5GdiotRi7X5De

claude added 2 commits May 19, 2026 02:09
The composition guard kept a flag true from compositionstart through one
macrotask after compositionend and preventDefault()'d every Enter in that
span. On Windows Zhuyin, the Enter that commits bopomofo into Han is part
of the active composition, so swallowing it stopped the IME from
finalizing and the raw phonetic buffer got inserted instead.

Scope the guard to a short window after compositionend and bail when the
event is still composing (isComposing / keyCode 229), so only the stray
post-composition Enter that ProseMirror would mis-handle as a block split
is suppressed. Decision logic extracted to imeGuard.js with unit tests.

https://claude.ai/code/session_01KT7qFP6oQ5GdiotRi7X5De
A 0 sentinel for "no compositionend yet" collides with performance.now()
(ms since page load): within the first POST_COMPOSITION_ENTER_MS after the
time origin, now - 0 falls inside the window and a plain Enter would be
swallowed. Use a negative-infinity sentinel so the window never matches
until a real compositionend has occurred.

https://claude.ai/code/session_01KT7qFP6oQ5GdiotRi7X5De
@PttCodingMan PttCodingMan merged commit 67c3107 into main May 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants