{"version":3,"sources":["../src/index.ts","../src/blockquote.tsx","../src/handleBackspace.ts"],"sourcesContent":["import { Blockquote } from './blockquote.jsx'\n\nexport * from './blockquote.jsx'\n\nexport default Blockquote\n","/** @jsxImportSource @tiptap/core */\nimport { mergeAttributes, Node, wrappingInputRule } from '@tiptap/core'\n\nimport { handleBackspace } from './handleBackspace.js'\n\nexport interface BlockquoteOptions {\n  /**\n   * HTML attributes to add to the blockquote element\n   * @default {}\n   * @example { class: 'foo' }\n   */\n  HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n  interface Commands<ReturnType> {\n    blockQuote: {\n      /**\n       * Set a blockquote node\n       */\n      setBlockquote: () => ReturnType\n      /**\n       * Toggle a blockquote node\n       */\n      toggleBlockquote: () => ReturnType\n      /**\n       * Unset a blockquote node\n       */\n      unsetBlockquote: () => ReturnType\n    }\n  }\n}\n\n/**\n * Matches a blockquote to a `>` as input.\n */\nexport const inputRegex = /^\\s*>\\s$/\n\n/**\n * This extension allows you to create blockquotes.\n * @see https://tiptap.dev/api/nodes/blockquote\n */\nexport const Blockquote = Node.create<BlockquoteOptions>({\n  name: 'blockquote',\n\n  addOptions() {\n    return {\n      HTMLAttributes: {},\n    }\n  },\n\n  content: 'block+',\n\n  group: 'block',\n\n  defining: true,\n\n  parseHTML() {\n    return [{ tag: 'blockquote' }]\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return (\n      <blockquote {...mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)}>\n        <slot />\n      </blockquote>\n    )\n  },\n\n  parseMarkdown: (token, helpers) => {\n    const parseBlockChildren = helpers.parseBlockChildren ?? helpers.parseChildren\n\n    return helpers.createNode('blockquote', undefined, parseBlockChildren(token.tokens || []))\n  },\n\n  renderMarkdown: (node, h) => {\n    if (!node.content) {\n      return ''\n    }\n\n    // Use a single '>' prefix regardless of nesting level\n    // Nested blockquotes will add their own '>' prefix recursively\n    const prefix = '>'\n    const result: string[] = []\n\n    node.content.forEach((child, index) => {\n      const childContent = h.renderChild?.(child, index) ?? h.renderChildren([child])\n      const lines = childContent.split('\\n')\n\n      const linesWithPrefix = lines.map(line => {\n        // Don't add prefix to empty lines\n        if (line.trim() === '') {\n          return prefix\n        }\n\n        // Nested blockquotes will already have their own prefixes\n        // We just need to add our own prefix at the start\n        return `${prefix} ${line}`\n      })\n\n      result.push(linesWithPrefix.join('\\n'))\n    })\n\n    // Add separator lines between children\n    return result.join(`\\n${prefix}\\n`)\n  },\n\n  addCommands() {\n    return {\n      setBlockquote:\n        () =>\n        ({ commands }) => {\n          return commands.wrapIn(this.name)\n        },\n      toggleBlockquote:\n        () =>\n        ({ commands }) => {\n          return commands.toggleWrap(this.name)\n        },\n      unsetBlockquote:\n        () =>\n        ({ commands }) => {\n          return commands.lift(this.name)\n        },\n    }\n  },\n\n  addKeyboardShortcuts() {\n    return {\n      'Mod-Shift-b': () => this.editor.commands.toggleBlockquote(),\n      Backspace: () => handleBackspace(this.editor, this.type),\n    }\n  },\n\n  addInputRules() {\n    return [\n      wrappingInputRule({\n        find: inputRegex,\n        type: this.type,\n      }),\n    ]\n  },\n})\n","import type { Editor } from '@tiptap/core'\nimport type { NodeType } from '@tiptap/pm/model'\nimport { TextSelection } from '@tiptap/pm/state'\n\n/**\n * Restructure the blockquote boundary at the caret.\n *\n * Two cases are handled in a single backspace:\n *\n * 1. Caret at the start of a non-first child of a blockquote — lift the\n *    current child out, splitting the blockquote around it.\n * 2. Caret at the start of a top-level textblock whose previous sibling is\n *    a blockquote with a textblock last child — merge the current\n *    textblock's inline content into the blockquote's last textblock\n *    instead of letting joinBackward pull the paragraph back inside.\n *\n * Returns true when the backspace was consumed.\n */\nexport const handleBackspace = (editor: Editor, type: NodeType): boolean => {\n  const { state, view } = editor\n  const { selection } = state\n  if (!selection.empty) return false\n\n  const { $from } = selection\n  if ($from.parentOffset !== 0) return false\n\n  const parentDepth = $from.depth - 1\n  // At the very start of the document the caret can resolve to the top (doc)\n  // level — for example a gap cursor before a leading image — where there is\n  // no parent block. Bail out so backspace is a no-op instead of dereferencing\n  // an undefined parent at a negative depth. (#7973)\n  if (parentDepth < 0) return false\n\n  const parent = $from.node(parentDepth)\n  const index = $from.index(parentDepth)\n  if (index === 0) return false\n\n  // Non-first child of a blockquote: lift to split the blockquote around it.\n  if (parent.type === type) {\n    return editor.commands.lift(type.name)\n  }\n\n  // Previous sibling is a blockquote whose last child is a textblock:\n  // merge the inline content in instead of letting joinBackward pull the\n  // paragraph back inside the blockquote.\n  const previous = parent.child(index - 1)\n  if (previous.type !== type || !previous.lastChild?.isTextblock) {\n    return false\n  }\n\n  const blockStart = $from.before()\n  // `blockStart` sits in the shared parent at the position right after\n  // the previous blockquote. In ProseMirror coordinates, each closing\n  // token costs one position: step one back to land inside the blockquote\n  // right after its last child, then one more to land inside that last\n  // child at the end of its inline content.\n  const insideBlockquoteEnd = blockStart - 1\n  const targetPos = insideBlockquoteEnd - 1\n  const { tr } = state\n  tr.delete(blockStart, $from.after()).insert(targetPos, $from.parent.content)\n  tr.setSelection(TextSelection.create(tr.doc, targetPos))\n  view.dispatch(tr.scrollIntoView())\n  return true\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,kBAAyD;;;ACCzD,mBAA8B;AAgBvB,IAAM,kBAAkB,CAAC,QAAgB,SAA4B;AAlB5E;AAmBE,QAAM,EAAE,OAAO,KAAK,IAAI;AACxB,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,CAAC,UAAU,MAAO,QAAO;AAE7B,QAAM,EAAE,MAAM,IAAI;AAClB,MAAI,MAAM,iBAAiB,EAAG,QAAO;AAErC,QAAM,cAAc,MAAM,QAAQ;AAKlC,MAAI,cAAc,EAAG,QAAO;AAE5B,QAAM,SAAS,MAAM,KAAK,WAAW;AACrC,QAAM,QAAQ,MAAM,MAAM,WAAW;AACrC,MAAI,UAAU,EAAG,QAAO;AAGxB,MAAI,OAAO,SAAS,MAAM;AACxB,WAAO,OAAO,SAAS,KAAK,KAAK,IAAI;AAAA,EACvC;AAKA,QAAM,WAAW,OAAO,MAAM,QAAQ,CAAC;AACvC,MAAI,SAAS,SAAS,QAAQ,GAAC,cAAS,cAAT,mBAAoB,cAAa;AAC9D,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,OAAO;AAMhC,QAAM,sBAAsB,aAAa;AACzC,QAAM,YAAY,sBAAsB;AACxC,QAAM,EAAE,GAAG,IAAI;AACf,KAAG,OAAO,YAAY,MAAM,MAAM,CAAC,EAAE,OAAO,WAAW,MAAM,OAAO,OAAO;AAC3E,KAAG,aAAa,2BAAc,OAAO,GAAG,KAAK,SAAS,CAAC;AACvD,OAAK,SAAS,GAAG,eAAe,CAAC;AACjC,SAAO;AACT;;;ADCQ;AA5BD,IAAM,aAAa;AAMnB,IAAM,aAAa,iBAAK,OAA0B;AAAA,EACvD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,UAAU;AAAA,EAEV,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,aAAa,CAAC;AAAA,EAC/B;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WACE,4CAAC,gBAAY,OAAG,6BAAgB,KAAK,QAAQ,gBAAgB,cAAc,GACzE,sDAAC,UAAK,GACR;AAAA,EAEJ;AAAA,EAEA,eAAe,CAAC,OAAO,YAAY;AArErC;AAsEI,UAAM,sBAAqB,aAAQ,uBAAR,YAA8B,QAAQ;AAEjE,WAAO,QAAQ,WAAW,cAAc,QAAW,mBAAmB,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EAC3F;AAAA,EAEA,gBAAgB,CAAC,MAAM,MAAM;AAC3B,QAAI,CAAC,KAAK,SAAS;AACjB,aAAO;AAAA,IACT;AAIA,UAAM,SAAS;AACf,UAAM,SAAmB,CAAC;AAE1B,SAAK,QAAQ,QAAQ,CAAC,OAAO,UAAU;AArF3C;AAsFM,YAAM,gBAAe,aAAE,gBAAF,2BAAgB,OAAO,WAAvB,YAAiC,EAAE,eAAe,CAAC,KAAK,CAAC;AAC9E,YAAM,QAAQ,aAAa,MAAM,IAAI;AAErC,YAAM,kBAAkB,MAAM,IAAI,UAAQ;AAExC,YAAI,KAAK,KAAK,MAAM,IAAI;AACtB,iBAAO;AAAA,QACT;AAIA,eAAO,GAAG,MAAM,IAAI,IAAI;AAAA,MAC1B,CAAC;AAED,aAAO,KAAK,gBAAgB,KAAK,IAAI,CAAC;AAAA,IACxC,CAAC;AAGD,WAAO,OAAO,KAAK;AAAA,EAAK,MAAM;AAAA,CAAI;AAAA,EACpC;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,eACE,MACA,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,OAAO,KAAK,IAAI;AAAA,MAClC;AAAA,MACF,kBACE,MACA,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,WAAW,KAAK,IAAI;AAAA,MACtC;AAAA,MACF,iBACE,MACA,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,KAAK,KAAK,IAAI;AAAA,MAChC;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,eAAe,MAAM,KAAK,OAAO,SAAS,iBAAiB;AAAA,MAC3D,WAAW,MAAM,gBAAgB,KAAK,QAAQ,KAAK,IAAI;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,UACL,+BAAkB;AAAA,QAChB,MAAM;AAAA,QACN,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AD1ID,IAAO,gBAAQ;","names":[]}