facebook / lexical

Lexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.
https://lexical.dev
MIT License
17.37k stars 1.43k forks source link

Bug: Korean consonant disappears when succeeded by a number or special character in Chrome #5877

Open hyunjaesung opened 2 weeks ago

hyunjaesung commented 2 weeks ago

I am currently using Lexical.js as the input system for our project. We have discovered an issue that occurs in the Chrome browser on desktop environments. When a user types a Korean consonant followed by a number or a special character, the consonant disappears, leaving only the number or special character. This behavior differs from a standard browser textarea, where the consonant remains and the number or special character is inserted alongside it.

As a temporary workaround, we have implemented a solution in our project where we create a space before receiving input to avoid this issue. We have identified that this problem arises due to the different handling of the compositionEnd event between Chrome and Safari. In Safari, the compositionEnd event does not trigger when a Korean consonant is followed by a number or a special character, whereas in Chrome, the compositionEnd event is fired in this scenario.

Our investigation has led us to believe that the issue occurs when the compositionEnd event is triggered in Chrome, causing $setCompositionKey(null) to be called within Lexical.js. Additionally, the e.preventDefault in the onBeforeInput event seems to contribute to this problem.

Thank you for your attention to this matter.

Lexical version: 0.13.1 later

Steps To Reproduce

  1. type Korean consonant
  2. type number or special character

The current behavior

Apr-12-2024 11-00-30

The expected behavior

Apr-12-2024 11-01-44

2wheeh commented 2 weeks ago

Hi !

The way I see it,

In Safari, the compositionEnd event does not trigger when a Korean consonant is followed by a number or a special character, whereas in Chrome, the compositionEnd event is fired in this scenario.

In both browsers, the following events occurs during composition:

However, in Safari, there are additional events that occur before compositionEnd:

This is why the key of the textNode is updated here:

https://github.com/facebook/lexical/assets/40269597/933ed2aa-7497-474b-8121-0fd3c03e129f

It ends up deleting the textNode and creates a new one with , then adds starting new composition cycle.

In Safari, when numbers or special characters follow, leading to the end of composition, it closes composition directly. The character that ends composition comes as separate input from the composition (keyCode is not 229) with insertText event.

In Chrome, the character leading to the end of composition is input as the last input of the composition (keyCode 229). compositionEnd comes after this.

The issue arises in a certain condition where the IME discards the character being composed, which should have been followed by the character ending the composition (such as numbers or special characters, ...).

For instance: => input 1 => 가1 should have been input as the composed result, then composition to be ended. The issue is => input 1 => 1 ( disappeared) situation.

Our investigation has led us to believe that the issue occurs when the compositionEnd event is triggered in Chrome, causing $setCompositionKey(null) to be called within Lexical.js.

I think you might guess $setCompositoniKey(null) makes the IME discard that here. However, $setCompositoniKey(null) is fired after 1 is input instead of 가1 already. IMO, the issue is not in incorrectly clearing Lexical's composition data but in Chrome IME's composing stack (?) clearing being triggered unexpectedly.

I suspect there might be a trigger during beginUpdate or commitPendingUpdates ...though I couldn't identify what it is.

Additionally, the e.preventDefault in the onBeforeInput event seems to contribute to this problem.

If you have a snippet of a valid workaround, would you mind sharing it?

The narrower condition to reproduce this:

It works correctly after clicking the editor or on the inputs following a new line break on ENTER. Plus, it can happen with either a consonant or a combined alphabet.

https://github.com/facebook/lexical/assets/40269597/490ed328-259b-4829-b695-a53a82c442eb

hyunjaesung commented 2 weeks ago

@2wheeh

Thank you for your response. It seems that the solution I had found earlier might not have worked properly.

I noticed that the related code has also been significantly modified in the meantime.

Based on the insights you provided, I will continue to search for a solution and keep updating my approach.