Cursor Position Issue During Chinese Text Composition

Hello!

Here’s my demo editor, modified from prosemirror-example-setup, I’ve added a color markSpec

import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {Schema, DOMParser} from "prosemirror-model"
import {schema} from "prosemirror-schema-basic"
import {addListNodes} from "prosemirror-schema-list"
import {exampleSetup} from "prosemirror-example-setup"

const mySchema = new Schema({
  nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
  marks: {
    ...schema.spec.marks,
    color: {
      attrs: {
        color: { default: "#333" },
      },
      parseDOM: [
        {
          priority: 20,
          tag: "span[style*=color]",
          getAttrs: (node: HTMLElement) => ({ color: node.getAttribute("data-color") }),
        },
      ],
      toDOM(node) {
        return ["span", { style: `color: ${node.attrs.color}`, "data-color": `color: ${node.attrs.color}` }];
      },
    }
  }
})

window.view = new EditorView(document.querySelector("#editor"), {
  state: EditorState.create({
    doc: DOMParser.fromSchema(mySchema).parse(document.querySelector("#content")),
    plugins: exampleSetup({schema: mySchema})
  })
})

The #content DOM structure is:

<div id="content">
    <p><span data-color="#f5222d" style="color: #f5222d">你好</span></p>
</div>

When composing Chinese text between “你” and “好”, the cursor appears after “好”, but I expect it to be positioned before “好”.

Also, I found that the issue occurs in Chrome but not in Safari.

Given the complexity of IMEs I think you’ll need to specify what OS and input device you’re using also.

I wasn’t able to reproduce this in TipTap on Android in a webview based on Chrome 138.x, but probably because the compositing text is not actually inserted into the editor until it’s finalized.

https://codesandbox.io/p/sandbox/dazzling-dewdney-qrsyds

I’m using macOS Sequoia 15.5 with Chrome138.x and the issue is reproducible with the default system input method.

1 Like

This adds an additional "color: " to data-color (and thus the attribute value) on every serialization-deserialization cycle. That’s causing the mark to change, which makes the editor think the changed range is bigger than it should be, which is producing the issue you’re seeing. Do "data-color": node.attrs.color instead and the problem goes away.

1 Like