WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Conversation

@aiden-art
Copy link

@aiden-art aiden-art commented Nov 27, 2025

Description

On Android devices, using handwriting input (e.g., Sogou Input Method) causes the initial stroke of a character to be committed prematurely. For example, when writing "我", the first stroke "一" is inserted immediately, leading to a final incorrect result of "一我".

The root cause is that Android handwriting IME triggers compositionStart/compositionEnd events per stroke, unlike keyboard input which only fires these events at the beginning and end of the entire input session. During composition, multiple code paths (handleInput(), scheduleAction(), handleUserSelect()) were calling flush(), which applies pending diffs to the editor via Editor.insertText(). This DOM modification causes the browser to terminate the current composition session, and the next stroke starts a new composition that appends to the already-committed text rather than replacing it.

The fix adds IS_COMPOSING checks in flush(), scheduleAction(), and handleInput() to prevent flushing during an active composition session. The pending diffs now properly accumulate and are only flushed after compositionEnd fires.

Issue

Fixes: #5979

Checks

  • The new code matches the existing patterns and styles.
  • The tests pass with yarn test.
  • The linter passes with yarn lint. (Fix errors with yarn fix.)
  • The relevant examples still work. (Run examples with yarn start.)
  • You've added a changeset if changing functionality. (Add one with yarn changeset add.)

@changeset-bot
Copy link

changeset-bot bot commented Nov 27, 2025

🦋 Changeset detected

Latest commit: ae1ab7b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
slate-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@aiden-art aiden-art force-pushed the fix/android-handwriting-composition-5979 branch 3 times, most recently from 1e926e7 to 068ee68 Compare November 27, 2025 10:18
@12joan
Copy link
Contributor

12joan commented Nov 27, 2025

@aiden-art What are the implications of this for the timing of onChange and insertText? I think previously, onChange was called continuously during composition; is this still the case?

Can you also check if the mentions example still works with this change? The combobox should update itself while composition is still in progress. Many Android devices also use composition for writing English text, so this could break a lot of features if we're not careful.

@aiden-art
Copy link
Author

aiden-art commented Nov 27, 2025

@12joan Thanks for the thoughtful review!

Yes, this change does affect onChange timing during composition:

  • Before: onChange was called continuously during composition
  • After: onChange is deferred until compositionEnd + 25ms

I've tested the mentions example and it works correctly. I'll attach a video demonstrating this.

Record_2025-11-27-19-45-50_40deb401b9ffe8e1df2f1c.mp4

Here's a video demonstrating the fix for Android handwriting input:

Record_2025-11-27-19-54-26_40deb401b9ffe8e1df2f1c.mp4

@12joan
Copy link
Contributor

12joan commented Nov 27, 2025

@aiden-art Thanks for confirming that about onChange.

It looks like in your video where you try out the mentions example, your device isn't using composition for English text, which I believe is common in Gboard and perhaps other keyboard applications running on recent versions of Android.

Samsung Keyboard and older versions of Gboard also use composition for English, which you can tell by the underline while typing. I've just tried your PR on my older Samsung phone, and the combobox doesn't show up until I explicitly end composition.

I'm seeing the same thing on a Samsung Galaxy S23 running on BrowserStack Live.

recording.mp4

Ideally, it would be great if we could fix this problem without causing any breaking changes. If changing the way Slate works during composition is unavoidable, then we should make sure that existing patterns have valid workarounds, and these should be clearly communicated in the changeset and probably the docs too.

@aiden-art aiden-art force-pushed the fix/android-handwriting-composition-5979 branch 2 times, most recently from d58df45 to e521f49 Compare November 28, 2025 03:05
…oid handwriting input

Fixes ianstormtaylor#5979

On Android devices, handwriting input on empty lines triggers childList mutations
when creating new DOM structure for the first character. These mutations get
restored by RestoreDOM during React re-render, which interrupts the IME composition
session and causes premature stroke commits (e.g., writing '我' produces '一我').

The fix adds a targeted check in flush() to skip flushing only when:
1. Currently in composition (IS_COMPOSING is true)
2. Cursor is at the first character of an empty line

This preserves normal onChange behavior for other scenarios while preventing
the handwriting issue on empty lines.
@aiden-art aiden-art force-pushed the fix/android-handwriting-composition-5979 branch from e521f49 to ae1ab7b Compare November 28, 2025 04:02
@aiden-art
Copy link
Author

Thanks for your feedback and testing!

After detailed log analysis, I found that the issue only occurs when writing the first character of an empty line (including new lines).

Root cause: When writing on an empty line, Android handwriting triggers childList mutations (creating new DOM structure). These mutations get restored by RestoreDOM, which interrupts the IME composition session. On lines with existing content, only characterData mutations occur, which are already skipped by RestoreDOM.

Current fix: Skip flush during composition only when at the first character of an empty line:

if (IS_COMPOSING.get(editor) && isAtEmptyLineFirstChar()) {
return
}However, this is a very targeted fix that may not cover all edge cases. Since this doesn't seem applicable to most scenarios (like the Samsung keyboard composition issue you mentioned), I'll close this PR.

I hope this analysis and approach can help others who encounter similar handwriting input issues on Android. The key insight is that the problem is related to childList vs characterData mutation types and how RestoreDOM handles them during composition.

@aiden-art aiden-art closed this Nov 28, 2025
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.

Handwriting on Android prematurely commits initial strokes

2 participants