ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
26.84k stars 2.24k forks source link

[Bug]: On Android browsers, if text style is set for the editor, the first letter entered on a blank line will fire compositionend event #4606

Open dxwts opened 10 months ago

dxwts commented 10 months ago

Which packages did you experience the bug in?

core starter-kit pm extension-color extension-text-style vue-3

What Tiptap version are you using?

2.1.12

What’s the bug you are facing?

  1. On Android browsers, I input text after set text color, will fire compositionend event when input first letter, input is normal after that.
  2. If move the cursor before the last letter, the compositionend event will also be triggered after entering two letters, and the cursor will be moved to the end.

What browser are you using?

Other

Code example

https://codesandbox.io/p/sandbox/angry-hoover-lpprps

What did you expect to happen?

I need not auto fire compositionend event when input first letter

Anything to add? (optional)

No response

Did you update your dependencies?

Are you sponsoring us?

dxwts commented 10 months ago

ezgif com-video-to-gif This is a screenshot of the operation

RealAlphabet commented 10 months ago

Could this be linked to an option on your virtual keyboard?

dxwts commented 9 months ago

@RealAlphabet Thank you for your response. I haven't identified which option of the virtual keyboard is causing the problem. Over the past few weeks, I have been trying to identify the cause of an issue I've encountered. Initially, my test device was the Onyx Boox Note, an e-ink reader based on Android. Later, I used a Samsung Tab S7 with the Gboard keyboard to conduct tests by typing in Korean and encountered the same issue. After setting colors through the extension-color plugin, the compositionend event is triggered after the first character is entered. I examined the code of prosemirror-view and found that in the input.ts file, the compositionupdate callback triggers an endComposition when storedMarks has a value. This call triggers the compositionend event. However, I am currently unsure how to resolve this issue.

// Drop active composition after 5 seconds of inactivity on Android
const timeoutComposition = browser.android ? 5000 : -1;

editHandlers.compositionstart = editHandlers.compositionupdate = (view) => {
  if (!view.composing) {
    view.domObserver.flush();
    let { state } = view,
      $pos = state.selection.$from;
    if (
      state.selection.empty &&
      (state.storedMarks ||
        (!$pos.textOffset &&
          $pos.parentOffset &&
          $pos.nodeBefore!.marks.some((m) => m.type.spec.inclusive === false)))
    ) {
      // Need to wrap the cursor in mark nodes different from the ones in the DOM context
      view.markCursor = view.state.storedMarks || $pos.marks();
      endComposition(view, true);    //This call leads to the occurrence of the problem
      view.markCursor = null;
    } else {
      endComposition(view);
      // In firefox, if the cursor is after but outside a marked node,
      // the inserted text won't inherit the marks. So this moves it
      // inside if necessary.
      if (
        browser.gecko &&
        state.selection.empty &&
        $pos.parentOffset &&
        !$pos.textOffset &&
        $pos.nodeBefore!.marks.length
      ) {
        let sel = view.domSelectionRange();
        for (
          let node = sel.focusNode, offset = sel.focusOffset;
          node && node.nodeType == 1 && offset != 0;

        ) {
          let before =
            offset < 0 ? node.lastChild : node.childNodes[offset - 1];
          if (!before) break;
          if (before.nodeType == 3) {
            view.domSelection().collapse(before, before.nodeValue!.length);
            break;
          } else {
            node = before;
            offset = -1;
          }
        }
      }
    }
    view.input.composing = true;
  }
  scheduleComposeEnd(view, timeoutComposition);
};
hankychung commented 6 days ago

mark, same issue