Difficulty with Programmatically Unwrapping (Lifting) Blockquote Nodes Extended with NodeView in a Tiptap/ProseMirror Editor in Vue.js

Hi,I’m developing a Vue application using Tiptap, a rich-text editor framework based on ProseMirror. I’m facing an issue related to programmatically unwrapping (lifting) a node that is wrapped in a blockquote and that uses an extended NodeView.

Here’s my approach:

  1. Wrapping a node: I can successfully wrap a node with blockquote using the following sequence of commands:
commands.setTextSelection(pos + 1); // in parent component
commands.toggleWrap('blockquote'); // in child component

The setTextSelection() command is executed in a parent component, and the toggleWrap() command is in a child component. The blockquote wraps the node as expected.

2.Unwrapping a node: Unwrapping the blockquote node is where the issue lies. I’ve tried using the lift() method, but it doesn’t seem to be effective:

const { state, editor } = props;
const { doc, tr } = state;
const $pos = doc.resolve(getPos());

if ($pos.parent.hasMarkup(state.schema.nodes.blockquote)) {
  const start = $pos.before($pos.depth);
  const end = $pos.after($pos.depth);
  const range = new NodeRange(doc.resolve(start), doc.resolve(end), 1);

  if (tr.lift(range, $pos.depth)) {
  } else {
    console.log('Lift operation failed');

Despite this, the tr.lift() operation fails and I’m unable to determine why. Notably, if I execute setTextSelection() and toggleWrap() within the same component, neither command works as expected. If I manually move the cursor to the line first, toggleWrap() works to get unWrap well.

I’m seeking guidance on why these issues might be happening. Is there a better way to unwrap nodes or something I’m missing in my current approach? Note that I have extended the blockquote node with a custom NodeView. Any help would be appreciated.

Lifting to $pos.depth doesn’t seem like it is what you want here. Probably $pos.depth - 2 is what you want? Similarly, hard-coding a 1 for the NodeRange depth looks wrong.

After further understanding of ProseMirror’s position, I found a simple solution to unwrapping the node:

commands.setTextSelection(pos + 2);

The key realization here was that selecting pos + 1 positioned the cursor just before the <blockquote> tag, even though visually it appeared to be inside. To move the cursor within the <blockquote> wrapped content, pos + 2 should be used instead.

Thank you for your answer! :smiley: