ueberdosis / tiptap

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

[Bug]: RangeError: Applying a mismatched transaction on vue-compat #4620

Open dermitzos opened 11 months ago

dermitzos commented 11 months ago

Which packages did you experience the bug in?

@tiptap/core, @tiptap/vue-3, @tiptap/extension-code-block-lowlight, @tiptap/pm

What Tiptap version are you using?

2.1.12

What’s the bug you are facing?

We have created a custom extension extending code-block-lowlight to support our language grammar. In this custom extension we have a ProseMirror plugin that adds a margin-left in the nodes that need it.

addProseMirrorPlugins() {
    return [
      ...(this.parent?.() || []),
      new Plugin({
        view: view => {
          return {
            /**
             * This callback is called whenever the state is updated
             */
            update: () => {
              const { doc, tr } = view.state;
              const { initialMargin } = this.options;
              let modified = false;
              // Start with an indentation level of 0
              let nextIndent = 0;
              // Clean up any paragraph margins in paragraphs inside blockquotes
              doc.descendants((node, pos) => {
                if (node.type.name === 'paragraph') {
                  const { parent } = doc.resolve(pos);
                  if (
                    parent.type.name === 'blockquote' &&
                    node.attrs.marginLeft &&
                    node.attrs.marginLeft !== '0px'
                  ) {
                    modified = true;
                    tr.setNodeMarkup(pos, null, { marginLeft: '0px' });
                  }
                  return false;
                }
                return true;
              });
              // Parse each doc child node (block nodes)
              doc.forEach((node, offset, index) => {
                let token = null;
                let currentIndent = nextIndent;
                const { name } = node.type;
                // In case of code block find 1st token
                if (name === 'codeBlock') {
                  [token] = node.textContent.trim().split(' ');
                }
                // Compute the indentation according to the token
                switch (token) {
                  case 'if': {
                    nextIndent += 1;
                    break;
                  }
                  case 'elseif':
                  case 'else': {
                    currentIndent -= 1;
                    break;
                  }
                  case 'endif': {
                    currentIndent -= 1;
                    nextIndent -= 1;
                    break;
                  }
                  default:
                    break;
                }
                // Do not allow for negative indentation
                currentIndent = Math.max(currentIndent, 0);
                // Compute the margin by the indentation
                const marginLeft = `${initialMargin + currentIndent * 20}px`;
                // Set the transaction only on indentation change
                if (
                  node.attrs.marginLeft &&
                  node.attrs.marginLeft !== marginLeft
                ) {
                  modified = true;
                  tr.setNodeMarkup(offset, null, { marginLeft });
                }
              });
              // Dispatch the transaction only if there was a change
              if (modified) {
                view.dispatch(tr);
              }
            },
          };
        },
      }),
    ];
  }

We were using tiptap with Vue 2 and we are transitioning our app to Vue 3, currently using vue-compat. Previously the above PM plugin was working correctly, but right now when view.dispatch(tr) is called we are getting an error:

index.cjs:870 Uncaught RangeError: Applying a mismatched transaction
    at EditorState.applyInner (index.cjs:870:1)
    at EditorState.applyTransaction (index.cjs:818:1)
    at EditorState.apply (index.cjs:794:1)
    at Editor.dispatchTransaction (index.cjs:3645:1)
    at EditorView.dispatch (index.cjs:6008:1)
    at Object.update (custom-code-block-highlight.js:142:22)
    at EditorView.updatePluginViews (index.cjs:5855:1)
    at EditorView.updateStateInner (index.cjs:5802:1)
    at EditorView.updateState (index.cjs:5737:1)
    at Editor.dispatchTransaction (index.cjs:3647:1)

Any suggestions on how we can fix it? Maybe this is an issue with vue-compat or should we consider a different approach?

What browser are you using?

Chrome

Code example

No response

What did you expect to happen?

Dispatch the transaction successfully

Anything to add? (optional)

No response

Did you update your dependencies?

Are you sponsoring us?

eybarta commented 8 months ago

Have you found a solution for this? running into the same issue when testing a simple custom command, i.e:

`
const CustomTextAlignExtension = Extension.create({
    name: "customTextAlign",
    addCommands() {
        return {
            setSmartTextAlign:
                (alignment) =>
                ({ tr, dispatch, state }) => {
                    return this.editor.chain().focus().setTextAlign(alignment).run();
                },
                ...
`

The function actually works, and currently just returns the call to TextAlign extension - setTextAlign.

However, I get the same error in console:

image
dermitzos commented 8 months ago

Have you found a solution for this? running into the same issue when testing a simple custom command, i.e:

`
const CustomTextAlignExtension = Extension.create({
  name: "customTextAlign",
  addCommands() {
      return {
          setSmartTextAlign:
              (alignment) =>
              ({ tr, dispatch, state }) => {
                  return this.editor.chain().focus().setTextAlign(alignment).run();
              },
              ...
`

The function actually works, and currently just returns the call to TextAlign extension - setTextAlign.

However, I get the same error in console: image

Are you using vue-compat with your project? Our solution was to rewrite our extension and use tiptap's onUpdate event rather than a custom ProseMirrorPlugin. Not sure if tiptap handles it differently. But this is a too generic error and maybe it's different.

eybarta commented 8 months ago

Hi Thanks for your reply.

I'm using "vue": "^3.4.15", "@tiptap/vue-3": "^2.1.16", "vite": "^4.5.2"

within a Meteor project which I don't think should make a difference.

I'm trying to implement an extension that extends the TextAlign functionality, All I need is to do add a custom class to listItems when a BulletList is being text aligned. This only needs to happened when the user changes text alignment so I think running this on onUpdated is bit overkill. The error 'mismatched transaction' is very obscure, that's I was sure I'm missing something fairly basic but just can't figure it out.

BrianHung commented 3 days ago

Make sure EditorState is never wrapped in a Vue reactive proxy.