Skip to content

[lexical] Bug Fix: Skip ZWSP insertion for format mismatch on Android Chrome#8769

Merged
etrepum merged 2 commits into
facebook:mainfrom
mayrang:fix/7649-format-mismatch-android
Jul 2, 2026
Merged

[lexical] Bug Fix: Skip ZWSP insertion for format mismatch on Android Chrome#8769
etrepum merged 2 commits into
facebook:mainfrom
mayrang:fix/7649-format-mismatch-android

Conversation

@mayrang

@mayrang mayrang commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Description

On Android Chrome, Samsung Keyboard and other non-Gboard keyboards (Yandex, FUTO) cache the composing region internally. When a user toggles bold/italic during composition, $handleCompositionStart detects a format mismatch between selection.format and node.getFormat() and inserts a ZWSP via CONTROLLED_TEXT_INSERTION_COMMAND. This DOM mutation triggers Samsung Keyboard's restartInput(), which restores the cached composing text and causes duplication.

The original issue (#7649) reports text duplication after toggling format on Samsung Keyboard. On our Android device the duplication didn't reproduce on the current main, but a related symptom did: typing Korean with bold enabled on Samsung Keyboard splits jamo (e.g. typing "한글" produces "ㅎㅏㄴㄱㅡㄹ" with each consonant/vowel separated). The root cause is the same — the ZWSP insertion disrupts Samsung's composing region tracking.

Fix

Guard the format and style mismatch conditions with !IS_ANDROID_CHROME so the ZWSP insertion is skipped on Android Chrome. The other conditions that trigger ZWSP (element anchor, non-collapsed selection, ANDROID_COMPOSITION_LATENCY timing heuristic) remain unguarded since those handle structural cases unrelated to Samsung's caching behavior.

The trade-off: toggling format during an active composition on Android Chrome Samsung Keyboard won't show the format change visually until the composition ends. The format itself is preserved correctly — selection.format persists through the composition cycle and applies on the next insertText.

Closes #7649

Test plan

  • pnpm vitest run --project unit — all pass, no regression.
  • pnpm tsc --noEmit clean.
  • New unit test LexicalAndroidChromeComposition.test.ts:
    • Format mismatch with IS_ANDROID_CHROME: true does not dispatch CONTROLLED_TEXT_INSERTION_COMMAND.
    • Element anchor still dispatches CONTROLLED_TEXT_INSERTION_COMMAND (guard doesn't leak to other conditions).
    • Test fails on main, passes with fix (verified by stashing the change and running against original code).
  • Manual Android Chrome (Samsung Keyboard):
    • Tap Bold → type Korean "한글": jamo compose correctly (no separation into "ㅎㅏㄴㄱㅡㄹ").
    • Type "hello" → tap Bold → type "world": no duplication, "world" appears once.
    • Gboard unaffected (Gboard handles restartInput gracefully).

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview Jul 2, 2026 6:40am
lexical-playground Ready Ready Preview Jul 2, 2026 6:40am

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 1, 2026
Comment thread packages/lexical/src/LexicalEvents.ts Outdated
Comment on lines +1335 to +1338
(!IS_ANDROID_CHROME && node.getFormat() !== selection.format) ||
(!IS_ANDROID_CHROME &&
$isTextNode(node) &&
node.getStyle() !== selection.style)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly shorter if the conditions are fused (suggestion won't pass ci-check cleanly without running prettier first)

Suggested change
(!IS_ANDROID_CHROME && node.getFormat() !== selection.format) ||
(!IS_ANDROID_CHROME &&
$isTextNode(node) &&
node.getStyle() !== selection.style)
(!IS_ANDROID_CHROME && (node.getFormat() !== selection.format ||
$isTextNode(node) &&
node.getStyle() !== selection.style))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, pushed in 37da33f.

@etrepum etrepum added this pull request to the merge queue Jul 2, 2026
Merged via the queue into facebook:main with commit 5ac6053 Jul 2, 2026
50 checks passed
@etrepum etrepum mentioned this pull request Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Regressions in typing experience on Android

2 participants