(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/pm/state'), require('@tiptap/pm/view'), require('@tiptap/pm/keymap'), require('@tiptap/pm/model'), require('@tiptap/pm/transform'), require('@tiptap/pm/commands'), require('@tiptap/pm/schema-list')) :
  typeof define === 'function' && define.amd ? define(['exports', '@tiptap/pm/state', '@tiptap/pm/view', '@tiptap/pm/keymap', '@tiptap/pm/model', '@tiptap/pm/transform', '@tiptap/pm/commands', '@tiptap/pm/schema-list'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/core"] = {}, global.state, global.view, global.keymap, global.model, global.transform, global.commands$1, global.schemaList));
})(this, (function (exports, state, view, keymap, model, transform, commands$1, schemaList) { 'use strict';

  /**
   * Takes a Transaction & Editor State and turns it into a chainable state object
   * @param config The transaction and state to create the chainable state from
   * @returns A chainable Editor state object
   */
  function createChainableState(config) {
      const { state, transaction } = config;
      let { selection } = transaction;
      let { doc } = transaction;
      let { storedMarks } = transaction;
      return {
          ...state,
          apply: state.apply.bind(state),
          applyTransaction: state.applyTransaction.bind(state),
          plugins: state.plugins,
          schema: state.schema,
          reconfigure: state.reconfigure.bind(state),
          toJSON: state.toJSON.bind(state),
          get storedMarks() {
              return storedMarks;
          },
          get selection() {
              return selection;
          },
          get doc() {
              return doc;
          },
          get tr() {
              selection = transaction.selection;
              doc = transaction.doc;
              storedMarks = transaction.storedMarks;
              return transaction;
          },
      };
  }

  class CommandManager {
      constructor(props) {
          this.editor = props.editor;
          this.rawCommands = this.editor.extensionManager.commands;
          this.customState = props.state;
      }
      get hasCustomState() {
          return !!this.customState;
      }
      get state() {
          return this.customState || this.editor.state;
      }
      get commands() {
          const { rawCommands, editor, state } = this;
          const { view } = editor;
          const { tr } = state;
          const props = this.buildProps(tr);
          return Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
              const method = (...args) => {
                  const callback = command(...args)(props);
                  if (!tr.getMeta('preventDispatch') && !this.hasCustomState) {
                      view.dispatch(tr);
                  }
                  return callback;
              };
              return [name, method];
          }));
      }
      get chain() {
          return () => this.createChain();
      }
      get can() {
          return () => this.createCan();
      }
      createChain(startTr, shouldDispatch = true) {
          const { rawCommands, editor, state } = this;
          const { view } = editor;
          const callbacks = [];
          const hasStartTransaction = !!startTr;
          const tr = startTr || state.tr;
          const run = () => {
              if (!hasStartTransaction
                  && shouldDispatch
                  && !tr.getMeta('preventDispatch')
                  && !this.hasCustomState) {
                  view.dispatch(tr);
              }
              return callbacks.every(callback => callback === true);
          };
          const chain = {
              ...Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
                  const chainedCommand = (...args) => {
                      const props = this.buildProps(tr, shouldDispatch);
                      const callback = command(...args)(props);
                      callbacks.push(callback);
                      return chain;
                  };
                  return [name, chainedCommand];
              })),
              run,
          };
          return chain;
      }
      createCan(startTr) {
          const { rawCommands, state } = this;
          const dispatch = false;
          const tr = startTr || state.tr;
          const props = this.buildProps(tr, dispatch);
          const formattedCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
              return [name, (...args) => command(...args)({ ...props, dispatch: undefined })];
          }));
          return {
              ...formattedCommands,
              chain: () => this.createChain(tr, dispatch),
          };
      }
      buildProps(tr, shouldDispatch = true) {
          const { rawCommands, editor, state } = this;
          const { view } = editor;
          const props = {
              tr,
              editor,
              view,
              state: createChainableState({
                  state,
                  transaction: tr,
              }),
              dispatch: shouldDispatch ? () => undefined : undefined,
              chain: () => this.createChain(tr, shouldDispatch),
              can: () => this.createCan(tr),
              get commands() {
                  return Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
                      return [name, (...args) => command(...args)(props)];
                  }));
              },
          };
          return props;
      }
  }

  class EventEmitter {
      constructor() {
          this.callbacks = {};
      }
      on(event, fn) {
          if (!this.callbacks[event]) {
              this.callbacks[event] = [];
          }
          this.callbacks[event].push(fn);
          return this;
      }
      emit(event, ...args) {
          const callbacks = this.callbacks[event];
          if (callbacks) {
              callbacks.forEach(callback => callback.apply(this, args));
          }
          return this;
      }
      off(event, fn) {
          const callbacks = this.callbacks[event];
          if (callbacks) {
              if (fn) {
                  this.callbacks[event] = callbacks.filter(callback => callback !== fn);
              }
              else {
                  delete this.callbacks[event];
              }
          }
          return this;
      }
      once(event, fn) {
          const onceFn = (...args) => {
              this.off(event, onceFn);
              fn.apply(this, args);
          };
          return this.on(event, onceFn);
      }
      removeAllListeners() {
          this.callbacks = {};
      }
  }

  /**
   * Returns a field from an extension
   * @param extension The Tiptap extension
   * @param field The field, for example `renderHTML` or `priority`
   * @param context The context object that should be passed as `this` into the function
   * @returns The field value
   */
  function getExtensionField(extension, field, context) {
      if (extension.config[field] === undefined && extension.parent) {
          return getExtensionField(extension.parent, field, context);
      }
      if (typeof extension.config[field] === 'function') {
          const value = extension.config[field].bind({
              ...context,
              parent: extension.parent
                  ? getExtensionField(extension.parent, field, context)
                  : null,
          });
          return value;
      }
      return extension.config[field];
  }

  function splitExtensions(extensions) {
      const baseExtensions = extensions.filter(extension => extension.type === 'extension');
      const nodeExtensions = extensions.filter(extension => extension.type === 'node');
      const markExtensions = extensions.filter(extension => extension.type === 'mark');
      return {
          baseExtensions,
          nodeExtensions,
          markExtensions,
      };
  }

  /**
   * Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.
   * @param extensions List of extensions
   */
  function getAttributesFromExtensions(extensions) {
      const extensionAttributes = [];
      const { nodeExtensions, markExtensions } = splitExtensions(extensions);
      const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions];
      const defaultAttribute = {
          default: null,
          rendered: true,
          renderHTML: null,
          parseHTML: null,
          keepOnSplit: true,
          isRequired: false,
      };
      extensions.forEach(extension => {
          const context = {
              name: extension.name,
              options: extension.options,
              storage: extension.storage,
              extensions: nodeAndMarkExtensions,
          };
          const addGlobalAttributes = getExtensionField(extension, 'addGlobalAttributes', context);
          if (!addGlobalAttributes) {
              return;
          }
          const globalAttributes = addGlobalAttributes();
          globalAttributes.forEach(globalAttribute => {
              globalAttribute.types.forEach(type => {
                  Object
                      .entries(globalAttribute.attributes)
                      .forEach(([name, attribute]) => {
                      extensionAttributes.push({
                          type,
                          name,
                          attribute: {
                              ...defaultAttribute,
                              ...attribute,
                          },
                      });
                  });
              });
          });
      });
      nodeAndMarkExtensions.forEach(extension => {
          const context = {
              name: extension.name,
              options: extension.options,
              storage: extension.storage,
          };
          const addAttributes = getExtensionField(extension, 'addAttributes', context);
          if (!addAttributes) {
              return;
          }
          // TODO: remove `as Attributes`
          const attributes = addAttributes();
          Object
              .entries(attributes)
              .forEach(([name, attribute]) => {
              const mergedAttr = {
                  ...defaultAttribute,
                  ...attribute,
              };
              if (typeof (mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.default) === 'function') {
                  mergedAttr.default = mergedAttr.default();
              }
              if ((mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.isRequired) && (mergedAttr === null || mergedAttr === void 0 ? void 0 : mergedAttr.default) === undefined) {
                  delete mergedAttr.default;
              }
              extensionAttributes.push({
                  type: extension.name,
                  name,
                  attribute: mergedAttr,
              });
          });
      });
      return extensionAttributes;
  }

  function getNodeType(nameOrType, schema) {
      if (typeof nameOrType === 'string') {
          if (!schema.nodes[nameOrType]) {
              throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
          }
          return schema.nodes[nameOrType];
      }
      return nameOrType;
  }

  function mergeAttributes(...objects) {
      return objects
          .filter(item => !!item)
          .reduce((items, item) => {
          const mergedAttributes = { ...items };
          Object.entries(item).forEach(([key, value]) => {
              const exists = mergedAttributes[key];
              if (!exists) {
                  mergedAttributes[key] = value;
                  return;
              }
              if (key === 'class') {
                  const valueClasses = value ? String(value).split(' ') : [];
                  const existingClasses = mergedAttributes[key] ? mergedAttributes[key].split(' ') : [];
                  const insertClasses = valueClasses.filter(valueClass => !existingClasses.includes(valueClass));
                  mergedAttributes[key] = [...existingClasses, ...insertClasses].join(' ');
              }
              else if (key === 'style') {
                  const newStyles = value ? value.split(';').map((style) => style.trim()).filter(Boolean) : [];
                  const existingStyles = mergedAttributes[key] ? mergedAttributes[key].split(';').map((style) => style.trim()).filter(Boolean) : [];
                  const styleMap = new Map();
                  existingStyles.forEach(style => {
                      const [property, val] = style.split(':').map(part => part.trim());
                      styleMap.set(property, val);
                  });
                  newStyles.forEach(style => {
                      const [property, val] = style.split(':').map(part => part.trim());
                      styleMap.set(property, val);
                  });
                  mergedAttributes[key] = Array.from(styleMap.entries()).map(([property, val]) => `${property}: ${val}`).join('; ');
              }
              else {
                  mergedAttributes[key] = value;
              }
          });
          return mergedAttributes;
      }, {});
  }

  function getRenderedAttributes(nodeOrMark, extensionAttributes) {
      return extensionAttributes
          .filter(attribute => attribute.type === nodeOrMark.type.name)
          .filter(item => item.attribute.rendered)
          .map(item => {
          if (!item.attribute.renderHTML) {
              return {
                  [item.name]: nodeOrMark.attrs[item.name],
              };
          }
          return item.attribute.renderHTML(nodeOrMark.attrs) || {};
      })
          .reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {});
  }

  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
  function isFunction(value) {
      return typeof value === 'function';
  }

  /**
   * Optionally calls `value` as a function.
   * Otherwise it is returned directly.
   * @param value Function or any value.
   * @param context Optional context to bind to function.
   * @param props Optional props to pass to function.
   */
  function callOrReturn(value, context = undefined, ...props) {
      if (isFunction(value)) {
          if (context) {
              return value.bind(context)(...props);
          }
          return value(...props);
      }
      return value;
  }

  function isEmptyObject(value = {}) {
      return Object.keys(value).length === 0 && value.constructor === Object;
  }

  function fromString(value) {
      if (typeof value !== 'string') {
          return value;
      }
      if (value.match(/^[+-]?(?:\d*\.)?\d+$/)) {
          return Number(value);
      }
      if (value === 'true') {
          return true;
      }
      if (value === 'false') {
          return false;
      }
      return value;
  }

  /**
   * This function merges extension attributes into parserule attributes (`attrs` or `getAttrs`).
   * Cancels when `getAttrs` returned `false`.
   * @param parseRule ProseMirror ParseRule
   * @param extensionAttributes List of attributes to inject
   */
  function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
      if ('style' in parseRule) {
          return parseRule;
      }
      return {
          ...parseRule,
          getAttrs: (node) => {
              const oldAttributes = parseRule.getAttrs ? parseRule.getAttrs(node) : parseRule.attrs;
              if (oldAttributes === false) {
                  return false;
              }
              const newAttributes = extensionAttributes.reduce((items, item) => {
                  const value = item.attribute.parseHTML
                      ? item.attribute.parseHTML(node)
                      : fromString((node).getAttribute(item.name));
                  if (value === null || value === undefined) {
                      return items;
                  }
                  return {
                      ...items,
                      [item.name]: value,
                  };
              }, {});
              return { ...oldAttributes, ...newAttributes };
          },
      };
  }

  function cleanUpSchemaItem(data) {
      return Object.fromEntries(
      // @ts-ignore
      Object.entries(data).filter(([key, value]) => {
          if (key === 'attrs' && isEmptyObject(value)) {
              return false;
          }
          return value !== null && value !== undefined;
      }));
  }
  /**
   * Creates a new Prosemirror schema based on the given extensions.
   * @param extensions An array of Tiptap extensions
   * @param editor The editor instance
   * @returns A Prosemirror schema
   */
  function getSchemaByResolvedExtensions(extensions, editor) {
      var _a;
      const allAttributes = getAttributesFromExtensions(extensions);
      const { nodeExtensions, markExtensions } = splitExtensions(extensions);
      const topNode = (_a = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))) === null || _a === void 0 ? void 0 : _a.name;
      const nodes = Object.fromEntries(nodeExtensions.map(extension => {
          const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name);
          const context = {
              name: extension.name,
              options: extension.options,
              storage: extension.storage,
              editor,
          };
          const extraNodeFields = extensions.reduce((fields, e) => {
              const extendNodeSchema = getExtensionField(e, 'extendNodeSchema', context);
              return {
                  ...fields,
                  ...(extendNodeSchema ? extendNodeSchema(extension) : {}),
              };
          }, {});
          const schema = cleanUpSchemaItem({
              ...extraNodeFields,
              content: callOrReturn(getExtensionField(extension, 'content', context)),
              marks: callOrReturn(getExtensionField(extension, 'marks', context)),
              group: callOrReturn(getExtensionField(extension, 'group', context)),
              inline: callOrReturn(getExtensionField(extension, 'inline', context)),
              atom: callOrReturn(getExtensionField(extension, 'atom', context)),
              selectable: callOrReturn(getExtensionField(extension, 'selectable', context)),
              draggable: callOrReturn(getExtensionField(extension, 'draggable', context)),
              code: callOrReturn(getExtensionField(extension, 'code', context)),
              whitespace: callOrReturn(getExtensionField(extension, 'whitespace', context)),
              linebreakReplacement: callOrReturn(getExtensionField(extension, 'linebreakReplacement', context)),
              defining: callOrReturn(getExtensionField(extension, 'defining', context)),
              isolating: callOrReturn(getExtensionField(extension, 'isolating', context)),
              attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
                  var _a;
                  return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
              })),
          });
          const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
          if (parseHTML) {
              schema.parseDOM = parseHTML.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
          }
          const renderHTML = getExtensionField(extension, 'renderHTML', context);
          if (renderHTML) {
              schema.toDOM = node => renderHTML({
                  node,
                  HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
              });
          }
          const renderText = getExtensionField(extension, 'renderText', context);
          if (renderText) {
              schema.toText = renderText;
          }
          return [extension.name, schema];
      }));
      const marks = Object.fromEntries(markExtensions.map(extension => {
          const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name);
          const context = {
              name: extension.name,
              options: extension.options,
              storage: extension.storage,
              editor,
          };
          const extraMarkFields = extensions.reduce((fields, e) => {
              const extendMarkSchema = getExtensionField(e, 'extendMarkSchema', context);
              return {
                  ...fields,
                  ...(extendMarkSchema ? extendMarkSchema(extension) : {}),
              };
          }, {});
          const schema = cleanUpSchemaItem({
              ...extraMarkFields,
              inclusive: callOrReturn(getExtensionField(extension, 'inclusive', context)),
              excludes: callOrReturn(getExtensionField(extension, 'excludes', context)),
              group: callOrReturn(getExtensionField(extension, 'group', context)),
              spanning: callOrReturn(getExtensionField(extension, 'spanning', context)),
              code: callOrReturn(getExtensionField(extension, 'code', context)),
              attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
                  var _a;
                  return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
              })),
          });
          const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
          if (parseHTML) {
              schema.parseDOM = parseHTML.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
          }
          const renderHTML = getExtensionField(extension, 'renderHTML', context);
          if (renderHTML) {
              schema.toDOM = mark => renderHTML({
                  mark,
                  HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
              });
          }
          return [extension.name, schema];
      }));
      return new model.Schema({
          topNode,
          nodes,
          marks,
      });
  }

  /**
   * Tries to get a node or mark type by its name.
   * @param name The name of the node or mark type
   * @param schema The Prosemiror schema to search in
   * @returns The node or mark type, or null if it doesn't exist
   */
  function getSchemaTypeByName(name, schema) {
      return schema.nodes[name] || schema.marks[name] || null;
  }

  function isExtensionRulesEnabled(extension, enabled) {
      if (Array.isArray(enabled)) {
          return enabled.some(enabledExtension => {
              const name = typeof enabledExtension === 'string'
                  ? enabledExtension
                  : enabledExtension.name;
              return name === extension.name;
          });
      }
      return enabled;
  }

  function getHTMLFromFragment(fragment, schema) {
      const documentFragment = model.DOMSerializer.fromSchema(schema).serializeFragment(fragment);
      const temporaryDocument = document.implementation.createHTMLDocument();
      const container = temporaryDocument.createElement('div');
      container.appendChild(documentFragment);
      return container.innerHTML;
  }

  /**
   * Returns the text content of a resolved prosemirror position
   * @param $from The resolved position to get the text content from
   * @param maxMatch The maximum number of characters to match
   * @returns The text content
   */
  const getTextContentFromNodes = ($from, maxMatch = 500) => {
      let textBefore = '';
      const sliceEndPos = $from.parentOffset;
      $from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index) => {
          var _a, _b;
          const chunk = ((_b = (_a = node.type.spec).toText) === null || _b === void 0 ? void 0 : _b.call(_a, {
              node,
              pos,
              parent,
              index,
          }))
              || node.textContent
              || '%leaf%';
          textBefore += node.isAtom && !node.isText ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos));
      });
      return textBefore;
  };

  function isRegExp(value) {
      return Object.prototype.toString.call(value) === '[object RegExp]';
  }

  class InputRule {
      constructor(config) {
          this.find = config.find;
          this.handler = config.handler;
      }
  }
  const inputRuleMatcherHandler = (text, find) => {
      if (isRegExp(find)) {
          return find.exec(text);
      }
      const inputRuleMatch = find(text);
      if (!inputRuleMatch) {
          return null;
      }
      const result = [inputRuleMatch.text];
      result.index = inputRuleMatch.index;
      result.input = text;
      result.data = inputRuleMatch.data;
      if (inputRuleMatch.replaceWith) {
          if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {
              console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".');
          }
          result.push(inputRuleMatch.replaceWith);
      }
      return result;
  };
  function run$1(config) {
      var _a;
      const { editor, from, to, text, rules, plugin, } = config;
      const { view } = editor;
      if (view.composing) {
          return false;
      }
      const $from = view.state.doc.resolve(from);
      if (
      // check for code node
      $from.parent.type.spec.code
          // check for code mark
          || !!((_a = ($from.nodeBefore || $from.nodeAfter)) === null || _a === void 0 ? void 0 : _a.marks.find(mark => mark.type.spec.code))) {
          return false;
      }
      let matched = false;
      const textBefore = getTextContentFromNodes($from) + text;
      rules.forEach(rule => {
          if (matched) {
              return;
          }
          const match = inputRuleMatcherHandler(textBefore, rule.find);
          if (!match) {
              return;
          }
          const tr = view.state.tr;
          const state = createChainableState({
              state: view.state,
              transaction: tr,
          });
          const range = {
              from: from - (match[0].length - text.length),
              to,
          };
          const { commands, chain, can } = new CommandManager({
              editor,
              state,
          });
          const handler = rule.handler({
              state,
              range,
              match,
              commands,
              chain,
              can,
          });
          // stop if there are no changes
          if (handler === null || !tr.steps.length) {
              return;
          }
          // store transform as meta data
          // so we can undo input rules within the `undoInputRules` command
          tr.setMeta(plugin, {
              transform: tr,
              from,
              to,
              text,
          });
          view.dispatch(tr);
          matched = true;
      });
      return matched;
  }
  /**
   * Create an input rules plugin. When enabled, it will cause text
   * input that matches any of the given rules to trigger the rule’s
   * action.
   */
  function inputRulesPlugin(props) {
      const { editor, rules } = props;
      const plugin = new state.Plugin({
          state: {
              init() {
                  return null;
              },
              apply(tr, prev, state) {
                  const stored = tr.getMeta(plugin);
                  if (stored) {
                      return stored;
                  }
                  // if InputRule is triggered by insertContent()
                  const simulatedInputMeta = tr.getMeta('applyInputRules');
                  const isSimulatedInput = !!simulatedInputMeta;
                  if (isSimulatedInput) {
                      setTimeout(() => {
                          let { text } = simulatedInputMeta;
                          if (typeof text === 'string') {
                              text = text;
                          }
                          else {
                              text = getHTMLFromFragment(model.Fragment.from(text), state.schema);
                          }
                          const { from } = simulatedInputMeta;
                          const to = from + text.length;
                          run$1({
                              editor,
                              from,
                              to,
                              text,
                              rules,
                              plugin,
                          });
                      });
                  }
                  return tr.selectionSet || tr.docChanged ? null : prev;
              },
          },
          props: {
              handleTextInput(view, from, to, text) {
                  return run$1({
                      editor,
                      from,
                      to,
                      text,
                      rules,
                      plugin,
                  });
              },
              handleDOMEvents: {
                  compositionend: view => {
                      setTimeout(() => {
                          const { $cursor } = view.state.selection;
                          if ($cursor) {
                              run$1({
                                  editor,
                                  from: $cursor.pos,
                                  to: $cursor.pos,
                                  text: '',
                                  rules,
                                  plugin,
                              });
                          }
                      });
                      return false;
                  },
              },
              // add support for input rules to trigger on enter
              // this is useful for example for code blocks
              handleKeyDown(view, event) {
                  if (event.key !== 'Enter') {
                      return false;
                  }
                  const { $cursor } = view.state.selection;
                  if ($cursor) {
                      return run$1({
                          editor,
                          from: $cursor.pos,
                          to: $cursor.pos,
                          text: '\n',
                          rules,
                          plugin,
                      });
                  }
                  return false;
              },
          },
          // @ts-ignore
          isInputRules: true,
      });
      return plugin;
  }

  // see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts
  function getType(value) {
      return Object.prototype.toString.call(value).slice(8, -1);
  }
  function isPlainObject(value) {
      if (getType(value) !== 'Object') {
          return false;
      }
      return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
  }

  function mergeDeep(target, source) {
      const output = { ...target };
      if (isPlainObject(target) && isPlainObject(source)) {
          Object.keys(source).forEach(key => {
              if (isPlainObject(source[key]) && isPlainObject(target[key])) {
                  output[key] = mergeDeep(target[key], source[key]);
              }
              else {
                  output[key] = source[key];
              }
          });
      }
      return output;
  }

  /**
   * The Mark class is used to create custom mark extensions.
   * @see https://tiptap.dev/api/extensions#create-a-new-extension
   */
  class Mark {
      constructor(config = {}) {
          this.type = 'mark';
          this.name = 'mark';
          this.parent = null;
          this.child = null;
          this.config = {
              name: this.name,
              defaultOptions: {},
          };
          this.config = {
              ...this.config,
              ...config,
          };
          this.name = this.config.name;
          if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
              console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
          }
          // TODO: remove `addOptions` fallback
          this.options = this.config.defaultOptions;
          if (this.config.addOptions) {
              this.options = callOrReturn(getExtensionField(this, 'addOptions', {
                  name: this.name,
              }));
          }
          this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
              name: this.name,
              options: this.options,
          })) || {};
      }
      static create(config = {}) {
          return new Mark(config);
      }
      configure(options = {}) {
          // return a new instance so we can use the same extension
          // with different calls of `configure`
          const extension = this.extend({
              ...this.config,
              addOptions: () => {
                  return mergeDeep(this.options, options);
              },
          });
          // Always preserve the current name
          extension.name = this.name;
          // Set the parent to be our parent
          extension.parent = this.parent;
          return extension;
      }
      extend(extendedConfig = {}) {
          const extension = new Mark(extendedConfig);
          extension.parent = this;
          this.child = extension;
          extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
          if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
              console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
          }
          extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
              name: extension.name,
          }));
          extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
              name: extension.name,
              options: extension.options,
          }));
          return extension;
      }
      static handleExit({ editor, mark }) {
          const { tr } = editor.state;
          const currentPos = editor.state.selection.$from;
          const isAtEnd = currentPos.pos === currentPos.end();
          if (isAtEnd) {
              const currentMarks = currentPos.marks();
              const isInMark = !!currentMarks.find(m => (m === null || m === void 0 ? void 0 : m.type.name) === mark.name);
              if (!isInMark) {
                  return false;
              }
              const removeMark = currentMarks.find(m => (m === null || m === void 0 ? void 0 : m.type.name) === mark.name);
              if (removeMark) {
                  tr.removeStoredMark(removeMark);
              }
              tr.insertText(' ', currentPos.pos);
              editor.view.dispatch(tr);
              return true;
          }
          return false;
      }
  }

  function isNumber(value) {
      return typeof value === 'number';
  }

  /**
   * Paste rules are used to react to pasted content.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
   */
  class PasteRule {
      constructor(config) {
          this.find = config.find;
          this.handler = config.handler;
      }
  }
  const pasteRuleMatcherHandler = (text, find, event) => {
      if (isRegExp(find)) {
          return [...text.matchAll(find)];
      }
      const matches = find(text, event);
      if (!matches) {
          return [];
      }
      return matches.map(pasteRuleMatch => {
          const result = [pasteRuleMatch.text];
          result.index = pasteRuleMatch.index;
          result.input = text;
          result.data = pasteRuleMatch.data;
          if (pasteRuleMatch.replaceWith) {
              if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {
                  console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".');
              }
              result.push(pasteRuleMatch.replaceWith);
          }
          return result;
      });
  };
  function run(config) {
      const { editor, state, from, to, rule, pasteEvent, dropEvent, } = config;
      const { commands, chain, can } = new CommandManager({
          editor,
          state,
      });
      const handlers = [];
      state.doc.nodesBetween(from, to, (node, pos) => {
          if (!node.isTextblock || node.type.spec.code) {
              return;
          }
          const resolvedFrom = Math.max(from, pos);
          const resolvedTo = Math.min(to, pos + node.content.size);
          const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc');
          const matches = pasteRuleMatcherHandler(textToMatch, rule.find, pasteEvent);
          matches.forEach(match => {
              if (match.index === undefined) {
                  return;
              }
              const start = resolvedFrom + match.index + 1;
              const end = start + match[0].length;
              const range = {
                  from: state.tr.mapping.map(start),
                  to: state.tr.mapping.map(end),
              };
              const handler = rule.handler({
                  state,
                  range,
                  match,
                  commands,
                  chain,
                  can,
                  pasteEvent,
                  dropEvent,
              });
              handlers.push(handler);
          });
      });
      const success = handlers.every(handler => handler !== null);
      return success;
  }
  // When dragging across editors, must get another editor instance to delete selection content.
  let tiptapDragFromOtherEditor = null;
  const createClipboardPasteEvent = (text) => {
      var _a;
      const event = new ClipboardEvent('paste', {
          clipboardData: new DataTransfer(),
      });
      (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.setData('text/html', text);
      return event;
  };
  /**
   * Create an paste rules plugin. When enabled, it will cause pasted
   * text that matches any of the given rules to trigger the rule’s
   * action.
   */
  function pasteRulesPlugin(props) {
      const { editor, rules } = props;
      let dragSourceElement = null;
      let isPastedFromProseMirror = false;
      let isDroppedFromProseMirror = false;
      let pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null;
      let dropEvent;
      try {
          dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null;
      }
      catch {
          dropEvent = null;
      }
      const processEvent = ({ state, from, to, rule, pasteEvt, }) => {
          const tr = state.tr;
          const chainableState = createChainableState({
              state,
              transaction: tr,
          });
          const handler = run({
              editor,
              state: chainableState,
              from: Math.max(from - 1, 0),
              to: to.b - 1,
              rule,
              pasteEvent: pasteEvt,
              dropEvent,
          });
          if (!handler || !tr.steps.length) {
              return;
          }
          try {
              dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null;
          }
          catch {
              dropEvent = null;
          }
          pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null;
          return tr;
      };
      const plugins = rules.map(rule => {
          return new state.Plugin({
              // we register a global drag handler to track the current drag source element
              view(view) {
                  const handleDragstart = (event) => {
                      var _a;
                      dragSourceElement = ((_a = view.dom.parentElement) === null || _a === void 0 ? void 0 : _a.contains(event.target))
                          ? view.dom.parentElement
                          : null;
                      if (dragSourceElement) {
                          tiptapDragFromOtherEditor = editor;
                      }
                  };
                  const handleDragend = () => {
                      if (tiptapDragFromOtherEditor) {
                          tiptapDragFromOtherEditor = null;
                      }
                  };
                  window.addEventListener('dragstart', handleDragstart);
                  window.addEventListener('dragend', handleDragend);
                  return {
                      destroy() {
                          window.removeEventListener('dragstart', handleDragstart);
                          window.removeEventListener('dragend', handleDragend);
                      },
                  };
              },
              props: {
                  handleDOMEvents: {
                      drop: (view, event) => {
                          isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement;
                          dropEvent = event;
                          if (!isDroppedFromProseMirror) {
                              const dragFromOtherEditor = tiptapDragFromOtherEditor;
                              if (dragFromOtherEditor === null || dragFromOtherEditor === void 0 ? void 0 : dragFromOtherEditor.isEditable) {
                                  // setTimeout to avoid the wrong content after drop, timeout arg can't be empty or 0
                                  setTimeout(() => {
                                      const selection = dragFromOtherEditor.state.selection;
                                      if (selection) {
                                          dragFromOtherEditor.commands.deleteRange({ from: selection.from, to: selection.to });
                                      }
                                  }, 10);
                              }
                          }
                          return false;
                      },
                      paste: (_view, event) => {
                          var _a;
                          const html = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/html');
                          pasteEvent = event;
                          isPastedFromProseMirror = !!(html === null || html === void 0 ? void 0 : html.includes('data-pm-slice'));
                          return false;
                      },
                  },
              },
              appendTransaction: (transactions, oldState, state) => {
                  const transaction = transactions[0];
                  const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror;
                  const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror;
                  // if PasteRule is triggered by insertContent()
                  const simulatedPasteMeta = transaction.getMeta('applyPasteRules');
                  const isSimulatedPaste = !!simulatedPasteMeta;
                  if (!isPaste && !isDrop && !isSimulatedPaste) {
                      return;
                  }
                  // Handle simulated paste
                  if (isSimulatedPaste) {
                      let { text } = simulatedPasteMeta;
                      if (typeof text === 'string') {
                          text = text;
                      }
                      else {
                          text = getHTMLFromFragment(model.Fragment.from(text), state.schema);
                      }
                      const { from } = simulatedPasteMeta;
                      const to = from + text.length;
                      const pasteEvt = createClipboardPasteEvent(text);
                      return processEvent({
                          rule,
                          state,
                          from,
                          to: { b: to },
                          pasteEvt,
                      });
                  }
                  // handle actual paste/drop
                  const from = oldState.doc.content.findDiffStart(state.doc.content);
                  const to = oldState.doc.content.findDiffEnd(state.doc.content);
                  // stop if there is no changed range
                  if (!isNumber(from) || !to || from === to.b) {
                      return;
                  }
                  return processEvent({
                      rule,
                      state,
                      from,
                      to,
                      pasteEvt: pasteEvent,
                  });
              },
          });
      });
      return plugins;
  }

  function findDuplicates(items) {
      const filtered = items.filter((el, index) => items.indexOf(el) !== index);
      return Array.from(new Set(filtered));
  }

  class ExtensionManager {
      constructor(extensions, editor) {
          this.splittableMarks = [];
          this.editor = editor;
          this.extensions = ExtensionManager.resolve(extensions);
          this.schema = getSchemaByResolvedExtensions(this.extensions, editor);
          this.setupExtensions();
      }
      /**
       * Returns a flattened and sorted extension list while
       * also checking for duplicated extensions and warns the user.
       * @param extensions An array of Tiptap extensions
       * @returns An flattened and sorted array of Tiptap extensions
       */
      static resolve(extensions) {
          const resolvedExtensions = ExtensionManager.sort(ExtensionManager.flatten(extensions));
          const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name));
          if (duplicatedNames.length) {
              console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames
                .map(item => `'${item}'`)
                .join(', ')}]. This can lead to issues.`);
          }
          return resolvedExtensions;
      }
      /**
       * Create a flattened array of extensions by traversing the `addExtensions` field.
       * @param extensions An array of Tiptap extensions
       * @returns A flattened array of Tiptap extensions
       */
      static flatten(extensions) {
          return (extensions
              .map(extension => {
              const context = {
                  name: extension.name,
                  options: extension.options,
                  storage: extension.storage,
              };
              const addExtensions = getExtensionField(extension, 'addExtensions', context);
              if (addExtensions) {
                  return [extension, ...this.flatten(addExtensions())];
              }
              return extension;
          })
              // `Infinity` will break TypeScript so we set a number that is probably high enough
              .flat(10));
      }
      /**
       * Sort extensions by priority.
       * @param extensions An array of Tiptap extensions
       * @returns A sorted array of Tiptap extensions by priority
       */
      static sort(extensions) {
          const defaultPriority = 100;
          return extensions.sort((a, b) => {
              const priorityA = getExtensionField(a, 'priority') || defaultPriority;
              const priorityB = getExtensionField(b, 'priority') || defaultPriority;
              if (priorityA > priorityB) {
                  return -1;
              }
              if (priorityA < priorityB) {
                  return 1;
              }
              return 0;
          });
      }
      /**
       * Get all commands from the extensions.
       * @returns An object with all commands where the key is the command name and the value is the command function
       */
      get commands() {
          return this.extensions.reduce((commands, extension) => {
              const context = {
                  name: extension.name,
                  options: extension.options,
                  storage: extension.storage,
                  editor: this.editor,
                  type: getSchemaTypeByName(extension.name, this.schema),
              };
              const addCommands = getExtensionField(extension, 'addCommands', context);
              if (!addCommands) {
                  return commands;
              }
              return {
                  ...commands,
                  ...addCommands(),
              };
          }, {});
      }
      /**
       * Get all registered Prosemirror plugins from the extensions.
       * @returns An array of Prosemirror plugins
       */
      get plugins() {
          const { editor } = this;
          // With ProseMirror, first plugins within an array are executed first.
          // In Tiptap, we provide the ability to override plugins,
          // so it feels more natural to run plugins at the end of an array first.
          // That’s why we have to reverse the `extensions` array and sort again
          // based on the `priority` option.
          const extensions = ExtensionManager.sort([...this.extensions].reverse());
          const inputRules = [];
          const pasteRules = [];
          const allPlugins = extensions
              .map(extension => {
              const context = {
                  name: extension.name,
                  options: extension.options,
                  storage: extension.storage,
                  editor,
                  type: getSchemaTypeByName(extension.name, this.schema),
              };
              const plugins = [];
              const addKeyboardShortcuts = getExtensionField(extension, 'addKeyboardShortcuts', context);
              let defaultBindings = {};
              // bind exit handling
              if (extension.type === 'mark' && getExtensionField(extension, 'exitable', context)) {
                  defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: extension });
              }
              if (addKeyboardShortcuts) {
                  const bindings = Object.fromEntries(Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {
                      return [shortcut, () => method({ editor })];
                  }));
                  defaultBindings = { ...defaultBindings, ...bindings };
              }
              const keyMapPlugin = keymap.keymap(defaultBindings);
              plugins.push(keyMapPlugin);
              const addInputRules = getExtensionField(extension, 'addInputRules', context);
              if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {
                  inputRules.push(...addInputRules());
              }
              const addPasteRules = getExtensionField(extension, 'addPasteRules', context);
              if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {
                  pasteRules.push(...addPasteRules());
              }
              const addProseMirrorPlugins = getExtensionField(extension, 'addProseMirrorPlugins', context);
              if (addProseMirrorPlugins) {
                  const proseMirrorPlugins = addProseMirrorPlugins();
                  plugins.push(...proseMirrorPlugins);
              }
              return plugins;
          })
              .flat();
          return [
              inputRulesPlugin({
                  editor,
                  rules: inputRules,
              }),
              ...pasteRulesPlugin({
                  editor,
                  rules: pasteRules,
              }),
              ...allPlugins,
          ];
      }
      /**
       * Get all attributes from the extensions.
       * @returns An array of attributes
       */
      get attributes() {
          return getAttributesFromExtensions(this.extensions);
      }
      /**
       * Get all node views from the extensions.
       * @returns An object with all node views where the key is the node name and the value is the node view function
       */
      get nodeViews() {
          const { editor } = this;
          const { nodeExtensions } = splitExtensions(this.extensions);
          return Object.fromEntries(nodeExtensions
              .filter(extension => !!getExtensionField(extension, 'addNodeView'))
              .map(extension => {
              const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name);
              const context = {
                  name: extension.name,
                  options: extension.options,
                  storage: extension.storage,
                  editor,
                  type: getNodeType(extension.name, this.schema),
              };
              const addNodeView = getExtensionField(extension, 'addNodeView', context);
              if (!addNodeView) {
                  return [];
              }
              const nodeview = (node, view, getPos, decorations, innerDecorations) => {
                  const HTMLAttributes = getRenderedAttributes(node, extensionAttributes);
                  return addNodeView()({
                      // pass-through
                      node,
                      view,
                      getPos: getPos,
                      decorations,
                      innerDecorations,
                      // tiptap-specific
                      editor,
                      extension,
                      HTMLAttributes,
                  });
              };
              return [extension.name, nodeview];
          }));
      }
      /**
       * Go through all extensions, create extension storages & setup marks
       * & bind editor event listener.
       */
      setupExtensions() {
          this.extensions.forEach(extension => {
              var _a;
              // store extension storage in editor
              this.editor.extensionStorage[extension.name] = extension.storage;
              const context = {
                  name: extension.name,
                  options: extension.options,
                  storage: extension.storage,
                  editor: this.editor,
                  type: getSchemaTypeByName(extension.name, this.schema),
              };
              if (extension.type === 'mark') {
                  const keepOnSplit = (_a = callOrReturn(getExtensionField(extension, 'keepOnSplit', context))) !== null && _a !== void 0 ? _a : true;
                  if (keepOnSplit) {
                      this.splittableMarks.push(extension.name);
                  }
              }
              const onBeforeCreate = getExtensionField(extension, 'onBeforeCreate', context);
              const onCreate = getExtensionField(extension, 'onCreate', context);
              const onUpdate = getExtensionField(extension, 'onUpdate', context);
              const onSelectionUpdate = getExtensionField(extension, 'onSelectionUpdate', context);
              const onTransaction = getExtensionField(extension, 'onTransaction', context);
              const onFocus = getExtensionField(extension, 'onFocus', context);
              const onBlur = getExtensionField(extension, 'onBlur', context);
              const onDestroy = getExtensionField(extension, 'onDestroy', context);
              if (onBeforeCreate) {
                  this.editor.on('beforeCreate', onBeforeCreate);
              }
              if (onCreate) {
                  this.editor.on('create', onCreate);
              }
              if (onUpdate) {
                  this.editor.on('update', onUpdate);
              }
              if (onSelectionUpdate) {
                  this.editor.on('selectionUpdate', onSelectionUpdate);
              }
              if (onTransaction) {
                  this.editor.on('transaction', onTransaction);
              }
              if (onFocus) {
                  this.editor.on('focus', onFocus);
              }
              if (onBlur) {
                  this.editor.on('blur', onBlur);
              }
              if (onDestroy) {
                  this.editor.on('destroy', onDestroy);
              }
          });
      }
  }

  /**
   * The Extension class is the base class for all extensions.
   * @see https://tiptap.dev/api/extensions#create-a-new-extension
   */
  class Extension {
      constructor(config = {}) {
          this.type = 'extension';
          this.name = 'extension';
          this.parent = null;
          this.child = null;
          this.config = {
              name: this.name,
              defaultOptions: {},
          };
          this.config = {
              ...this.config,
              ...config,
          };
          this.name = this.config.name;
          if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
              console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
          }
          // TODO: remove `addOptions` fallback
          this.options = this.config.defaultOptions;
          if (this.config.addOptions) {
              this.options = callOrReturn(getExtensionField(this, 'addOptions', {
                  name: this.name,
              }));
          }
          this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
              name: this.name,
              options: this.options,
          })) || {};
      }
      static create(config = {}) {
          return new Extension(config);
      }
      configure(options = {}) {
          // return a new instance so we can use the same extension
          // with different calls of `configure`
          const extension = this.extend({
              ...this.config,
              addOptions: () => {
                  return mergeDeep(this.options, options);
              },
          });
          // Always preserve the current name
          extension.name = this.name;
          // Set the parent to be our parent
          extension.parent = this.parent;
          return extension;
      }
      extend(extendedConfig = {}) {
          const extension = new Extension({ ...this.config, ...extendedConfig });
          extension.parent = this;
          this.child = extension;
          extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
          if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
              console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
          }
          extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
              name: extension.name,
          }));
          extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
              name: extension.name,
              options: extension.options,
          }));
          return extension;
      }
  }

  /**
   * Gets the text between two positions in a Prosemirror node
   * and serializes it using the given text serializers and block separator (see getText)
   * @param startNode The Prosemirror node to start from
   * @param range The range of the text to get
   * @param options Options for the text serializer & block separator
   * @returns The text between the two positions
   */
  function getTextBetween(startNode, range, options) {
      const { from, to } = range;
      const { blockSeparator = '\n\n', textSerializers = {} } = options || {};
      let text = '';
      startNode.nodesBetween(from, to, (node, pos, parent, index) => {
          var _a;
          if (node.isBlock && pos > from) {
              text += blockSeparator;
          }
          const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
          if (textSerializer) {
              if (parent) {
                  text += textSerializer({
                      node,
                      pos,
                      parent,
                      index,
                      range,
                  });
              }
              // do not descend into child nodes when there exists a serializer
              return false;
          }
          if (node.isText) {
              text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos); // eslint-disable-line
          }
      });
      return text;
  }

  /**
   * Find text serializers `toText` in a Prosemirror schema
   * @param schema The Prosemirror schema to search in
   * @returns A record of text serializers by node name
   */
  function getTextSerializersFromSchema(schema) {
      return Object.fromEntries(Object.entries(schema.nodes)
          .filter(([, node]) => node.spec.toText)
          .map(([name, node]) => [name, node.spec.toText]));
  }

  const ClipboardTextSerializer = Extension.create({
      name: 'clipboardTextSerializer',
      addOptions() {
          return {
              blockSeparator: undefined,
          };
      },
      addProseMirrorPlugins() {
          return [
              new state.Plugin({
                  key: new state.PluginKey('clipboardTextSerializer'),
                  props: {
                      clipboardTextSerializer: () => {
                          const { editor } = this;
                          const { state, schema } = editor;
                          const { doc, selection } = state;
                          const { ranges } = selection;
                          const from = Math.min(...ranges.map(range => range.$from.pos));
                          const to = Math.max(...ranges.map(range => range.$to.pos));
                          const textSerializers = getTextSerializersFromSchema(schema);
                          const range = { from, to };
                          return getTextBetween(doc, range, {
                              ...(this.options.blockSeparator !== undefined
                                  ? { blockSeparator: this.options.blockSeparator }
                                  : {}),
                              textSerializers,
                          });
                      },
                  },
              }),
          ];
      },
  });

  const blur = () => ({ editor, view }) => {
      requestAnimationFrame(() => {
          var _a;
          if (!editor.isDestroyed) {
              view.dom.blur();
              // Browsers should remove the caret on blur but safari does not.
              // See: https://github.com/ueberdosis/tiptap/issues/2405
              (_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
          }
      });
      return true;
  };

  const clearContent = (emitUpdate = false) => ({ commands }) => {
      return commands.setContent('', emitUpdate);
  };

  const clearNodes = () => ({ state, tr, dispatch }) => {
      const { selection } = tr;
      const { ranges } = selection;
      if (!dispatch) {
          return true;
      }
      ranges.forEach(({ $from, $to }) => {
          state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
              if (node.type.isText) {
                  return;
              }
              const { doc, mapping } = tr;
              const $mappedFrom = doc.resolve(mapping.map(pos));
              const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
              const nodeRange = $mappedFrom.blockRange($mappedTo);
              if (!nodeRange) {
                  return;
              }
              const targetLiftDepth = transform.liftTarget(nodeRange);
              if (node.type.isTextblock) {
                  const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
                  tr.setNodeMarkup(nodeRange.start, defaultType);
              }
              if (targetLiftDepth || targetLiftDepth === 0) {
                  tr.lift(nodeRange, targetLiftDepth);
              }
          });
      });
      return true;
  };

  const command = fn => props => {
      return fn(props);
  };

  const createParagraphNear = () => ({ state, dispatch }) => {
      return commands$1.createParagraphNear(state, dispatch);
  };

  const cut = (originRange, targetPos) => ({ editor, tr }) => {
      const { state: state$1 } = editor;
      const contentSlice = state$1.doc.slice(originRange.from, originRange.to);
      tr.deleteRange(originRange.from, originRange.to);
      const newPos = tr.mapping.map(targetPos);
      tr.insert(newPos, contentSlice.content);
      tr.setSelection(new state.TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))));
      return true;
  };

  const deleteCurrentNode = () => ({ tr, dispatch }) => {
      const { selection } = tr;
      const currentNode = selection.$anchor.node();
      // if there is content inside the current node, break out of this command
      if (currentNode.content.size > 0) {
          return false;
      }
      const $pos = tr.selection.$anchor;
      for (let depth = $pos.depth; depth > 0; depth -= 1) {
          const node = $pos.node(depth);
          if (node.type === currentNode.type) {
              if (dispatch) {
                  const from = $pos.before(depth);
                  const to = $pos.after(depth);
                  tr.delete(from, to).scrollIntoView();
              }
              return true;
          }
      }
      return false;
  };

  const deleteNode = typeOrName => ({ tr, state, dispatch }) => {
      const type = getNodeType(typeOrName, state.schema);
      const $pos = tr.selection.$anchor;
      for (let depth = $pos.depth; depth > 0; depth -= 1) {
          const node = $pos.node(depth);
          if (node.type === type) {
              if (dispatch) {
                  const from = $pos.before(depth);
                  const to = $pos.after(depth);
                  tr.delete(from, to).scrollIntoView();
              }
              return true;
          }
      }
      return false;
  };

  const deleteRange = range => ({ tr, dispatch }) => {
      const { from, to } = range;
      if (dispatch) {
          tr.delete(from, to);
      }
      return true;
  };

  const deleteSelection = () => ({ state, dispatch }) => {
      return commands$1.deleteSelection(state, dispatch);
  };

  const enter = () => ({ commands }) => {
      return commands.keyboardShortcut('Enter');
  };

  const exitCode = () => ({ state, dispatch }) => {
      return commands$1.exitCode(state, dispatch);
  };

  /**
   * Check if object1 includes object2
   * @param object1 Object
   * @param object2 Object
   */
  function objectIncludes(object1, object2, options = { strict: true }) {
      const keys = Object.keys(object2);
      if (!keys.length) {
          return true;
      }
      return keys.every(key => {
          if (options.strict) {
              return object2[key] === object1[key];
          }
          if (isRegExp(object2[key])) {
              return object2[key].test(object1[key]);
          }
          return object2[key] === object1[key];
      });
  }

  function findMarkInSet(marks, type, attributes = {}) {
      return marks.find(item => {
          return (item.type === type
              && objectIncludes(
              // Only check equality for the attributes that are provided
              Object.fromEntries(Object.keys(attributes).map(k => [k, item.attrs[k]])), attributes));
      });
  }
  function isMarkInSet(marks, type, attributes = {}) {
      return !!findMarkInSet(marks, type, attributes);
  }
  /**
   * Get the range of a mark at a resolved position.
   */
  function getMarkRange(
  /**
   * The position to get the mark range for.
   */
  $pos, 
  /**
   * The mark type to get the range for.
   */
  type, 
  /**
   * The attributes to match against.
   * If not provided, only the first mark at the position will be matched.
   */
  attributes) {
      var _a;
      if (!$pos || !type) {
          return;
      }
      let start = $pos.parent.childAfter($pos.parentOffset);
      // If the cursor is at the start of a text node that does not have the mark, look backward
      if (!start.node || !start.node.marks.some(mark => mark.type === type)) {
          start = $pos.parent.childBefore($pos.parentOffset);
      }
      // If there is no text node with the mark even backward, return undefined
      if (!start.node || !start.node.marks.some(mark => mark.type === type)) {
          return;
      }
      // Default to only matching against the first mark's attributes
      attributes = attributes || ((_a = start.node.marks[0]) === null || _a === void 0 ? void 0 : _a.attrs);
      // We now know that the cursor is either at the start, middle or end of a text node with the specified mark
      // so we can look it up on the targeted mark
      const mark = findMarkInSet([...start.node.marks], type, attributes);
      if (!mark) {
          return;
      }
      let startIndex = start.index;
      let startPos = $pos.start() + start.offset;
      let endIndex = startIndex + 1;
      let endPos = startPos + start.node.nodeSize;
      while (startIndex > 0
          && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
          startIndex -= 1;
          startPos -= $pos.parent.child(startIndex).nodeSize;
      }
      while (endIndex < $pos.parent.childCount
          && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
          endPos += $pos.parent.child(endIndex).nodeSize;
          endIndex += 1;
      }
      return {
          from: startPos,
          to: endPos,
      };
  }

  function getMarkType(nameOrType, schema) {
      if (typeof nameOrType === 'string') {
          if (!schema.marks[nameOrType]) {
              throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
          }
          return schema.marks[nameOrType];
      }
      return nameOrType;
  }

  const extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state: state$1, dispatch }) => {
      const type = getMarkType(typeOrName, state$1.schema);
      const { doc, selection } = tr;
      const { $from, from, to } = selection;
      if (dispatch) {
          const range = getMarkRange($from, type, attributes);
          if (range && range.from <= from && range.to >= to) {
              const newSelection = state.TextSelection.create(doc, range.from, range.to);
              tr.setSelection(newSelection);
          }
      }
      return true;
  };

  const first = commands => props => {
      const items = typeof commands === 'function'
          ? commands(props)
          : commands;
      for (let i = 0; i < items.length; i += 1) {
          if (items[i](props)) {
              return true;
          }
      }
      return false;
  };

  function isTextSelection(value) {
      return value instanceof state.TextSelection;
  }

  function minMax(value = 0, min = 0, max = 0) {
      return Math.min(Math.max(value, min), max);
  }

  function resolveFocusPosition(doc, position = null) {
      if (!position) {
          return null;
      }
      const selectionAtStart = state.Selection.atStart(doc);
      const selectionAtEnd = state.Selection.atEnd(doc);
      if (position === 'start' || position === true) {
          return selectionAtStart;
      }
      if (position === 'end') {
          return selectionAtEnd;
      }
      const minPos = selectionAtStart.from;
      const maxPos = selectionAtEnd.to;
      if (position === 'all') {
          return state.TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
      }
      return state.TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
  }

  function isAndroid() {
      return navigator.platform === 'Android' || /android/i.test(navigator.userAgent);
  }

  function isiOS() {
      return [
          'iPad Simulator',
          'iPhone Simulator',
          'iPod Simulator',
          'iPad',
          'iPhone',
          'iPod',
      ].includes(navigator.platform)
          // iPad on iOS 13 detection
          || (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
  }

  const focus = (position = null, options = {}) => ({ editor, view, tr, dispatch, }) => {
      options = {
          scrollIntoView: true,
          ...options,
      };
      const delayedFocus = () => {
          // focus within `requestAnimationFrame` breaks focus on iOS and Android
          // so we have to call this
          if (isiOS() || isAndroid()) {
              view.dom.focus();
          }
          // For React we have to focus asynchronously. Otherwise wild things happen.
          // see: https://github.com/ueberdosis/tiptap/issues/1520
          requestAnimationFrame(() => {
              if (!editor.isDestroyed) {
                  view.focus();
                  if (options === null || options === void 0 ? void 0 : options.scrollIntoView) {
                      editor.commands.scrollIntoView();
                  }
              }
          });
      };
      if ((view.hasFocus() && position === null) || position === false) {
          return true;
      }
      // we don’t try to resolve a NodeSelection or CellSelection
      if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
          delayedFocus();
          return true;
      }
      // pass through tr.doc instead of editor.state.doc
      // since transactions could change the editors state before this command has been run
      const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
      const isSameSelection = editor.state.selection.eq(selection);
      if (dispatch) {
          if (!isSameSelection) {
              tr.setSelection(selection);
          }
          // `tr.setSelection` resets the stored marks
          // so we’ll restore them if the selection is the same as before
          if (isSameSelection && tr.storedMarks) {
              tr.setStoredMarks(tr.storedMarks);
          }
          delayedFocus();
      }
      return true;
  };

  const forEach = (items, fn) => props => {
      return items.every((item, index) => fn(item, { ...props, index }));
  };

  const insertContent = (value, options) => ({ tr, commands }) => {
      return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
  };

  const removeWhitespaces = (node) => {
      const children = node.childNodes;
      for (let i = children.length - 1; i >= 0; i -= 1) {
          const child = children[i];
          if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
              node.removeChild(child);
          }
          else if (child.nodeType === 1) {
              removeWhitespaces(child);
          }
      }
      return node;
  };
  function elementFromString(value) {
      // add a wrapper to preserve leading and trailing whitespace
      const wrappedValue = `<body>${value}</body>`;
      const html = new window.DOMParser().parseFromString(wrappedValue, 'text/html').body;
      return removeWhitespaces(html);
  }

  /**
   * Takes a JSON or HTML content and creates a Prosemirror node or fragment from it.
   * @param content The JSON or HTML content to create the node from
   * @param schema The Prosemirror schema to use for the node
   * @param options Options for the parser
   * @returns The created Prosemirror node or fragment
   */
  function createNodeFromContent(content, schema, options) {
      if (content instanceof model.Node || content instanceof model.Fragment) {
          return content;
      }
      options = {
          slice: true,
          parseOptions: {},
          ...options,
      };
      const isJSONContent = typeof content === 'object' && content !== null;
      const isTextContent = typeof content === 'string';
      if (isJSONContent) {
          try {
              const isArrayContent = Array.isArray(content) && content.length > 0;
              // if the JSON Content is an array of nodes, create a fragment for each node
              if (isArrayContent) {
                  return model.Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)));
              }
              const node = schema.nodeFromJSON(content);
              if (options.errorOnInvalidContent) {
                  node.check();
              }
              return node;
          }
          catch (error) {
              if (options.errorOnInvalidContent) {
                  throw new Error('[tiptap error]: Invalid JSON content', { cause: error });
              }
              console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
              return createNodeFromContent('', schema, options);
          }
      }
      if (isTextContent) {
          // Check for invalid content
          if (options.errorOnInvalidContent) {
              let hasInvalidContent = false;
              let invalidContent = '';
              // A copy of the current schema with a catch-all node at the end
              const contentCheckSchema = new model.Schema({
                  topNode: schema.spec.topNode,
                  marks: schema.spec.marks,
                  // Prosemirror's schemas are executed such that: the last to execute, matches last
                  // This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
                  nodes: schema.spec.nodes.append({
                      __tiptap__private__unknown__catch__all__node: {
                          content: 'inline*',
                          group: 'block',
                          parseDOM: [
                              {
                                  tag: '*',
                                  getAttrs: e => {
                                      // If this is ever called, we know that the content has something that we don't know how to handle in the schema
                                      hasInvalidContent = true;
                                      // Try to stringify the element for a more helpful error message
                                      invalidContent = typeof e === 'string' ? e : e.outerHTML;
                                      return null;
                                  },
                              },
                          ],
                      },
                  }),
              });
              if (options.slice) {
                  model.DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
              }
              else {
                  model.DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
              }
              if (options.errorOnInvalidContent && hasInvalidContent) {
                  throw new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) });
              }
          }
          const parser = model.DOMParser.fromSchema(schema);
          if (options.slice) {
              return parser.parseSlice(elementFromString(content), options.parseOptions).content;
          }
          return parser.parse(elementFromString(content), options.parseOptions);
      }
      return createNodeFromContent('', schema, options);
  }

  // source: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466
  function selectionToInsertionEnd(tr, startLen, bias) {
      const last = tr.steps.length - 1;
      if (last < startLen) {
          return;
      }
      const step = tr.steps[last];
      if (!(step instanceof transform.ReplaceStep || step instanceof transform.ReplaceAroundStep)) {
          return;
      }
      const map = tr.mapping.maps[last];
      let end = 0;
      map.forEach((_from, _to, _newFrom, newTo) => {
          if (end === 0) {
              end = newTo;
          }
      });
      tr.setSelection(state.Selection.near(tr.doc.resolve(end), bias));
  }

  const isFragment = (nodeOrFragment) => {
      return !('type' in nodeOrFragment);
  };
  const insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
      var _a;
      if (dispatch) {
          options = {
              parseOptions: editor.options.parseOptions,
              updateSelection: true,
              applyInputRules: false,
              applyPasteRules: false,
              ...options,
          };
          let content;
          const emitContentError = (error) => {
              editor.emit('contentError', {
                  editor,
                  error,
                  disableCollaboration: () => {
                      if (editor.storage.collaboration) {
                          editor.storage.collaboration.isDisabled = true;
                      }
                  },
              });
          };
          const parseOptions = {
              preserveWhitespace: 'full',
              ...options.parseOptions,
          };
          // If `emitContentError` is enabled, we want to check the content for errors
          // but ignore them (do not remove the invalid content from the document)
          if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {
              try {
                  createNodeFromContent(value, editor.schema, {
                      parseOptions,
                      errorOnInvalidContent: true,
                  });
              }
              catch (e) {
                  emitContentError(e);
              }
          }
          try {
              content = createNodeFromContent(value, editor.schema, {
                  parseOptions,
                  errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck,
              });
          }
          catch (e) {
              emitContentError(e);
              return false;
          }
          let { from, to } = typeof position === 'number' ? { from: position, to: position } : { from: position.from, to: position.to };
          let isOnlyTextContent = true;
          let isOnlyBlockContent = true;
          const nodes = isFragment(content) ? content : [content];
          nodes.forEach(node => {
              // check if added node is valid
              node.check();
              isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
              isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
          });
          // check if we can replace the wrapping node by
          // the newly inserted content
          // example:
          // replace an empty paragraph by an inserted image
          // instead of inserting the image below the paragraph
          if (from === to && isOnlyBlockContent) {
              const { parent } = tr.doc.resolve(from);
              const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
              if (isEmptyTextBlock) {
                  from -= 1;
                  to += 1;
              }
          }
          let newContent;
          // if there is only plain text we have to use `insertText`
          // because this will keep the current marks
          if (isOnlyTextContent) {
              // if value is string, we can use it directly
              // otherwise if it is an array, we have to join it
              if (Array.isArray(value)) {
                  newContent = value.map(v => v.text || '').join('');
              }
              else if (value instanceof model.Fragment) {
                  let text = '';
                  value.forEach(node => {
                      if (node.text) {
                          text += node.text;
                      }
                  });
                  newContent = text;
              }
              else if (typeof value === 'object' && !!value && !!value.text) {
                  newContent = value.text;
              }
              else {
                  newContent = value;
              }
              tr.insertText(newContent, from, to);
          }
          else {
              newContent = content;
              tr.replaceWith(from, to, newContent);
          }
          // set cursor at end of inserted content
          if (options.updateSelection) {
              selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
          }
          if (options.applyInputRules) {
              tr.setMeta('applyInputRules', { from, text: newContent });
          }
          if (options.applyPasteRules) {
              tr.setMeta('applyPasteRules', { from, text: newContent });
          }
      }
      return true;
  };

  const joinUp = () => ({ state, dispatch }) => {
      return commands$1.joinUp(state, dispatch);
  };
  const joinDown = () => ({ state, dispatch }) => {
      return commands$1.joinDown(state, dispatch);
  };
  const joinBackward = () => ({ state, dispatch }) => {
      return commands$1.joinBackward(state, dispatch);
  };
  const joinForward = () => ({ state, dispatch }) => {
      return commands$1.joinForward(state, dispatch);
  };

  const joinItemBackward = () => ({ state, dispatch, tr, }) => {
      try {
          const point = transform.joinPoint(state.doc, state.selection.$from.pos, -1);
          if (point === null || point === undefined) {
              return false;
          }
          tr.join(point, 2);
          if (dispatch) {
              dispatch(tr);
          }
          return true;
      }
      catch {
          return false;
      }
  };

  const joinItemForward = () => ({ state, dispatch, tr, }) => {
      try {
          const point = transform.joinPoint(state.doc, state.selection.$from.pos, +1);
          if (point === null || point === undefined) {
              return false;
          }
          tr.join(point, 2);
          if (dispatch) {
              dispatch(tr);
          }
          return true;
      }
      catch {
          return false;
      }
  };

  const joinTextblockBackward = () => ({ state, dispatch }) => {
      return commands$1.joinTextblockBackward(state, dispatch);
  };

  const joinTextblockForward = () => ({ state, dispatch }) => {
      return commands$1.joinTextblockForward(state, dispatch);
  };

  function isMacOS() {
      return typeof navigator !== 'undefined'
          ? /Mac/.test(navigator.platform)
          : false;
  }

  function normalizeKeyName(name) {
      const parts = name.split(/-(?!$)/);
      let result = parts[parts.length - 1];
      if (result === 'Space') {
          result = ' ';
      }
      let alt;
      let ctrl;
      let shift;
      let meta;
      for (let i = 0; i < parts.length - 1; i += 1) {
          const mod = parts[i];
          if (/^(cmd|meta|m)$/i.test(mod)) {
              meta = true;
          }
          else if (/^a(lt)?$/i.test(mod)) {
              alt = true;
          }
          else if (/^(c|ctrl|control)$/i.test(mod)) {
              ctrl = true;
          }
          else if (/^s(hift)?$/i.test(mod)) {
              shift = true;
          }
          else if (/^mod$/i.test(mod)) {
              if (isiOS() || isMacOS()) {
                  meta = true;
              }
              else {
                  ctrl = true;
              }
          }
          else {
              throw new Error(`Unrecognized modifier name: ${mod}`);
          }
      }
      if (alt) {
          result = `Alt-${result}`;
      }
      if (ctrl) {
          result = `Ctrl-${result}`;
      }
      if (meta) {
          result = `Meta-${result}`;
      }
      if (shift) {
          result = `Shift-${result}`;
      }
      return result;
  }
  const keyboardShortcut = name => ({ editor, view, tr, dispatch, }) => {
      const keys = normalizeKeyName(name).split(/-(?!$)/);
      const key = keys.find(item => !['Alt', 'Ctrl', 'Meta', 'Shift'].includes(item));
      const event = new KeyboardEvent('keydown', {
          key: key === 'Space'
              ? ' '
              : key,
          altKey: keys.includes('Alt'),
          ctrlKey: keys.includes('Ctrl'),
          metaKey: keys.includes('Meta'),
          shiftKey: keys.includes('Shift'),
          bubbles: true,
          cancelable: true,
      });
      const capturedTransaction = editor.captureTransaction(() => {
          view.someProp('handleKeyDown', f => f(view, event));
      });
      capturedTransaction === null || capturedTransaction === void 0 ? void 0 : capturedTransaction.steps.forEach(step => {
          const newStep = step.map(tr.mapping);
          if (newStep && dispatch) {
              tr.maybeStep(newStep);
          }
      });
      return true;
  };

  function isNodeActive(state, typeOrName, attributes = {}) {
      const { from, to, empty } = state.selection;
      const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
      const nodeRanges = [];
      state.doc.nodesBetween(from, to, (node, pos) => {
          if (node.isText) {
              return;
          }
          const relativeFrom = Math.max(from, pos);
          const relativeTo = Math.min(to, pos + node.nodeSize);
          nodeRanges.push({
              node,
              from: relativeFrom,
              to: relativeTo,
          });
      });
      const selectionRange = to - from;
      const matchedNodeRanges = nodeRanges
          .filter(nodeRange => {
          if (!type) {
              return true;
          }
          return type.name === nodeRange.node.type.name;
      })
          .filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
      if (empty) {
          return !!matchedNodeRanges.length;
      }
      const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
      return range >= selectionRange;
  }

  const lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
      const type = getNodeType(typeOrName, state.schema);
      const isActive = isNodeActive(state, type, attributes);
      if (!isActive) {
          return false;
      }
      return commands$1.lift(state, dispatch);
  };

  const liftEmptyBlock = () => ({ state, dispatch }) => {
      return commands$1.liftEmptyBlock(state, dispatch);
  };

  const liftListItem = typeOrName => ({ state, dispatch }) => {
      const type = getNodeType(typeOrName, state.schema);
      return schemaList.liftListItem(type)(state, dispatch);
  };

  const newlineInCode = () => ({ state, dispatch }) => {
      return commands$1.newlineInCode(state, dispatch);
  };

  /**
   * Get the type of a schema item by its name.
   * @param name The name of the schema item
   * @param schema The Prosemiror schema to search in
   * @returns The type of the schema item (`node` or `mark`), or null if it doesn't exist
   */
  function getSchemaTypeNameByName(name, schema) {
      if (schema.nodes[name]) {
          return 'node';
      }
      if (schema.marks[name]) {
          return 'mark';
      }
      return null;
  }

  /**
   * Remove a property or an array of properties from an object
   * @param obj Object
   * @param key Key to remove
   */
  function deleteProps(obj, propOrProps) {
      const props = typeof propOrProps === 'string'
          ? [propOrProps]
          : propOrProps;
      return Object
          .keys(obj)
          .reduce((newObj, prop) => {
          if (!props.includes(prop)) {
              newObj[prop] = obj[prop];
          }
          return newObj;
      }, {});
  }

  const resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
      let nodeType = null;
      let markType = null;
      const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string' ? typeOrName : typeOrName.name, state.schema);
      if (!schemaType) {
          return false;
      }
      if (schemaType === 'node') {
          nodeType = getNodeType(typeOrName, state.schema);
      }
      if (schemaType === 'mark') {
          markType = getMarkType(typeOrName, state.schema);
      }
      if (dispatch) {
          tr.selection.ranges.forEach(range => {
              state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
                  if (nodeType && nodeType === node.type) {
                      tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes));
                  }
                  if (markType && node.marks.length) {
                      node.marks.forEach(mark => {
                          if (markType === mark.type) {
                              tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
                          }
                      });
                  }
              });
          });
      }
      return true;
  };

  const scrollIntoView = () => ({ tr, dispatch }) => {
      if (dispatch) {
          tr.scrollIntoView();
      }
      return true;
  };

  const selectAll = () => ({ tr, dispatch }) => {
      if (dispatch) {
          const selection = new state.AllSelection(tr.doc);
          tr.setSelection(selection);
      }
      return true;
  };

  const selectNodeBackward = () => ({ state, dispatch }) => {
      return commands$1.selectNodeBackward(state, dispatch);
  };

  const selectNodeForward = () => ({ state, dispatch }) => {
      return commands$1.selectNodeForward(state, dispatch);
  };

  const selectParentNode = () => ({ state, dispatch }) => {
      return commands$1.selectParentNode(state, dispatch);
  };

  // @ts-ignore
  // TODO: add types to @types/prosemirror-commands
  const selectTextblockEnd = () => ({ state, dispatch }) => {
      return commands$1.selectTextblockEnd(state, dispatch);
  };

  // @ts-ignore
  // TODO: add types to @types/prosemirror-commands
  const selectTextblockStart = () => ({ state, dispatch }) => {
      return commands$1.selectTextblockStart(state, dispatch);
  };

  /**
   * Create a new Prosemirror document node from content.
   * @param content The JSON or HTML content to create the document from
   * @param schema The Prosemirror schema to use for the document
   * @param parseOptions Options for the parser
   * @returns The created Prosemirror document node
   */
  function createDocument(content, schema, parseOptions = {}, options = {}) {
      return createNodeFromContent(content, schema, {
          slice: false,
          parseOptions,
          errorOnInvalidContent: options.errorOnInvalidContent,
      });
  }

  const setContent = (content, emitUpdate = false, parseOptions = {}, options = {}) => ({ editor, tr, dispatch, commands, }) => {
      var _a, _b;
      const { doc } = tr;
      // This is to keep backward compatibility with the previous behavior
      // TODO remove this in the next major version
      if (parseOptions.preserveWhitespace !== 'full') {
          const document = createDocument(content, editor.schema, parseOptions, {
              errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck,
          });
          if (dispatch) {
              tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate);
          }
          return true;
      }
      if (dispatch) {
          tr.setMeta('preventUpdate', !emitUpdate);
      }
      return commands.insertContentAt({ from: 0, to: doc.content.size }, content, {
          parseOptions,
          errorOnInvalidContent: (_b = options.errorOnInvalidContent) !== null && _b !== void 0 ? _b : editor.options.enableContentCheck,
      });
  };

  function getMarkAttributes(state, typeOrName) {
      const type = getMarkType(typeOrName, state.schema);
      const { from, to, empty } = state.selection;
      const marks = [];
      if (empty) {
          if (state.storedMarks) {
              marks.push(...state.storedMarks);
          }
          marks.push(...state.selection.$head.marks());
      }
      else {
          state.doc.nodesBetween(from, to, node => {
              marks.push(...node.marks);
          });
      }
      const mark = marks.find(markItem => markItem.type.name === type.name);
      if (!mark) {
          return {};
      }
      return { ...mark.attrs };
  }

  /**
   * Returns a new `Transform` based on all steps of the passed transactions.
   * @param oldDoc The Prosemirror node to start from
   * @param transactions The transactions to combine
   * @returns A new `Transform` with all steps of the passed transactions
   */
  function combineTransactionSteps(oldDoc, transactions) {
      const transform$1 = new transform.Transform(oldDoc);
      transactions.forEach(transaction => {
          transaction.steps.forEach(step => {
              transform$1.step(step);
          });
      });
      return transform$1;
  }

  /**
   * Gets the default block type at a given match
   * @param match The content match to get the default block type from
   * @returns The default block type or null
   */
  function defaultBlockAt(match) {
      for (let i = 0; i < match.edgeCount; i += 1) {
          const { type } = match.edge(i);
          if (type.isTextblock && !type.hasRequiredAttrs()) {
              return type;
          }
      }
      return null;
  }

  /**
   * Find children inside a Prosemirror node that match a predicate.
   * @param node The Prosemirror node to search in
   * @param predicate The predicate to match
   * @returns An array of nodes with their positions
   */
  function findChildren(node, predicate) {
      const nodesWithPos = [];
      node.descendants((child, pos) => {
          if (predicate(child)) {
              nodesWithPos.push({
                  node: child,
                  pos,
              });
          }
      });
      return nodesWithPos;
  }

  /**
   * Same as `findChildren` but searches only within a `range`.
   * @param node The Prosemirror node to search in
   * @param range The range to search in
   * @param predicate The predicate to match
   * @returns An array of nodes with their positions
   */
  function findChildrenInRange(node, range, predicate) {
      const nodesWithPos = [];
      // if (range.from === range.to) {
      //   const nodeAt = node.nodeAt(range.from)
      //   if (nodeAt) {
      //     nodesWithPos.push({
      //       node: nodeAt,
      //       pos: range.from,
      //     })
      //   }
      // }
      node.nodesBetween(range.from, range.to, (child, pos) => {
          if (predicate(child)) {
              nodesWithPos.push({
                  node: child,
                  pos,
              });
          }
      });
      return nodesWithPos;
  }

  /**
   * Finds the closest parent node to a resolved position that matches a predicate.
   * @param $pos The resolved position to search from
   * @param predicate The predicate to match
   * @returns The closest parent node to the resolved position that matches the predicate
   * @example ```js
   * findParentNodeClosestToPos($from, node => node.type.name === 'paragraph')
   * ```
   */
  function findParentNodeClosestToPos($pos, predicate) {
      for (let i = $pos.depth; i > 0; i -= 1) {
          const node = $pos.node(i);
          if (predicate(node)) {
              return {
                  pos: i > 0 ? $pos.before(i) : 0,
                  start: $pos.start(i),
                  depth: i,
                  node,
              };
          }
      }
  }

  /**
   * Finds the closest parent node to the current selection that matches a predicate.
   * @param predicate The predicate to match
   * @returns A command that finds the closest parent node to the current selection that matches the predicate
   * @example ```js
   * findParentNode(node => node.type.name === 'paragraph')
   * ```
   */
  function findParentNode(predicate) {
      return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
  }

  function getSchema(extensions, editor) {
      const resolvedExtensions = ExtensionManager.resolve(extensions);
      return getSchemaByResolvedExtensions(resolvedExtensions, editor);
  }

  /**
   * Generate HTML from a JSONContent
   * @param doc The JSONContent to generate HTML from
   * @param extensions The extensions to use for the schema
   * @returns The generated HTML
   */
  function generateHTML(doc, extensions) {
      const schema = getSchema(extensions);
      const contentNode = model.Node.fromJSON(schema, doc);
      return getHTMLFromFragment(contentNode.content, schema);
  }

  /**
   * Generate JSONContent from HTML
   * @param html The HTML to generate JSONContent from
   * @param extensions The extensions to use for the schema
   * @returns The generated JSONContent
   */
  function generateJSON(html, extensions) {
      const schema = getSchema(extensions);
      const dom = elementFromString(html);
      return model.DOMParser.fromSchema(schema).parse(dom).toJSON();
  }

  /**
   * Gets the text of a Prosemirror node
   * @param node The Prosemirror node
   * @param options Options for the text serializer & block separator
   * @returns The text of the node
   * @example ```js
   * const text = getText(node, { blockSeparator: '\n' })
   * ```
   */
  function getText(node, options) {
      const range = {
          from: 0,
          to: node.content.size,
      };
      return getTextBetween(node, range, options);
  }

  /**
   * Generate raw text from a JSONContent
   * @param doc The JSONContent to generate text from
   * @param extensions The extensions to use for the schema
   * @param options Options for the text generation f.e. blockSeparator or textSerializers
   * @returns The generated text
   */
  function generateText(doc, extensions, options) {
      const { blockSeparator = '\n\n', textSerializers = {} } = options || {};
      const schema = getSchema(extensions);
      const contentNode = model.Node.fromJSON(schema, doc);
      return getText(contentNode, {
          blockSeparator,
          textSerializers: {
              ...getTextSerializersFromSchema(schema),
              ...textSerializers,
          },
      });
  }

  function getNodeAttributes(state, typeOrName) {
      const type = getNodeType(typeOrName, state.schema);
      const { from, to } = state.selection;
      const nodes = [];
      state.doc.nodesBetween(from, to, node => {
          nodes.push(node);
      });
      const node = nodes.reverse().find(nodeItem => nodeItem.type.name === type.name);
      if (!node) {
          return {};
      }
      return { ...node.attrs };
  }

  /**
   * Get node or mark attributes by type or name on the current editor state
   * @param state The current editor state
   * @param typeOrName The node or mark type or name
   * @returns The attributes of the node or mark or an empty object
   */
  function getAttributes(state, typeOrName) {
      const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string' ? typeOrName : typeOrName.name, state.schema);
      if (schemaType === 'node') {
          return getNodeAttributes(state, typeOrName);
      }
      if (schemaType === 'mark') {
          return getMarkAttributes(state, typeOrName);
      }
      return {};
  }

  /**
   * Removes duplicated values within an array.
   * Supports numbers, strings and objects.
   */
  function removeDuplicates(array, by = JSON.stringify) {
      const seen = {};
      return array.filter(item => {
          const key = by(item);
          return Object.prototype.hasOwnProperty.call(seen, key)
              ? false
              : (seen[key] = true);
      });
  }

  /**
   * Removes duplicated ranges and ranges that are
   * fully captured by other ranges.
   */
  function simplifyChangedRanges(changes) {
      const uniqueChanges = removeDuplicates(changes);
      return uniqueChanges.length === 1
          ? uniqueChanges
          : uniqueChanges.filter((change, index) => {
              const rest = uniqueChanges.filter((_, i) => i !== index);
              return !rest.some(otherChange => {
                  return change.oldRange.from >= otherChange.oldRange.from
                      && change.oldRange.to <= otherChange.oldRange.to
                      && change.newRange.from >= otherChange.newRange.from
                      && change.newRange.to <= otherChange.newRange.to;
              });
          });
  }
  /**
   * Returns a list of changed ranges
   * based on the first and last state of all steps.
   */
  function getChangedRanges(transform) {
      const { mapping, steps } = transform;
      const changes = [];
      mapping.maps.forEach((stepMap, index) => {
          const ranges = [];
          // This accounts for step changes where no range was actually altered
          // e.g. when setting a mark, node attribute, etc.
          // @ts-ignore
          if (!stepMap.ranges.length) {
              const { from, to } = steps[index];
              if (from === undefined || to === undefined) {
                  return;
              }
              ranges.push({ from, to });
          }
          else {
              stepMap.forEach((from, to) => {
                  ranges.push({ from, to });
              });
          }
          ranges.forEach(({ from, to }) => {
              const newStart = mapping.slice(index).map(from, -1);
              const newEnd = mapping.slice(index).map(to);
              const oldStart = mapping.invert().map(newStart, -1);
              const oldEnd = mapping.invert().map(newEnd);
              changes.push({
                  oldRange: {
                      from: oldStart,
                      to: oldEnd,
                  },
                  newRange: {
                      from: newStart,
                      to: newEnd,
                  },
              });
          });
      });
      return simplifyChangedRanges(changes);
  }

  function getDebugJSON(node, startOffset = 0) {
      const isTopNode = node.type === node.type.schema.topNodeType;
      const increment = isTopNode ? 0 : 1;
      const from = startOffset;
      const to = from + node.nodeSize;
      const marks = node.marks.map(mark => {
          const output = {
              type: mark.type.name,
          };
          if (Object.keys(mark.attrs).length) {
              output.attrs = { ...mark.attrs };
          }
          return output;
      });
      const attrs = { ...node.attrs };
      const output = {
          type: node.type.name,
          from,
          to,
      };
      if (Object.keys(attrs).length) {
          output.attrs = attrs;
      }
      if (marks.length) {
          output.marks = marks;
      }
      if (node.content.childCount) {
          output.content = [];
          node.forEach((child, offset) => {
              var _a;
              (_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
          });
      }
      if (node.text) {
          output.text = node.text;
      }
      return output;
  }

  function getMarksBetween(from, to, doc) {
      const marks = [];
      // get all inclusive marks on empty selection
      if (from === to) {
          doc
              .resolve(from)
              .marks()
              .forEach(mark => {
              const $pos = doc.resolve(from);
              const range = getMarkRange($pos, mark.type);
              if (!range) {
                  return;
              }
              marks.push({
                  mark,
                  ...range,
              });
          });
      }
      else {
          doc.nodesBetween(from, to, (node, pos) => {
              if (!node || (node === null || node === void 0 ? void 0 : node.nodeSize) === undefined) {
                  return;
              }
              marks.push(...node.marks.map(mark => ({
                  from: pos,
                  to: pos + node.nodeSize,
                  mark,
              })));
          });
      }
      return marks;
  }

  /**
   * Finds the first node of a given type or name in the current selection.
   * @param state The editor state.
   * @param typeOrName The node type or name.
   * @param pos The position to start searching from.
   * @param maxDepth The maximum depth to search.
   * @returns The node and the depth as an array.
   */
  const getNodeAtPosition = (state, typeOrName, pos, maxDepth = 20) => {
      const $pos = state.doc.resolve(pos);
      let currentDepth = maxDepth;
      let node = null;
      while (currentDepth > 0 && node === null) {
          const currentNode = $pos.node(currentDepth);
          if ((currentNode === null || currentNode === void 0 ? void 0 : currentNode.type.name) === typeOrName) {
              node = currentNode;
          }
          else {
              currentDepth -= 1;
          }
      }
      return [node, currentDepth];
  };

  /**
   * Return attributes of an extension that should be splitted by keepOnSplit flag
   * @param extensionAttributes Array of extension attributes
   * @param typeName The type of the extension
   * @param attributes The attributes of the extension
   * @returns The splitted attributes
   */
  function getSplittedAttributes(extensionAttributes, typeName, attributes) {
      return Object.fromEntries(Object
          .entries(attributes)
          .filter(([name]) => {
          const extensionAttribute = extensionAttributes.find(item => {
              return item.type === typeName && item.name === name;
          });
          if (!extensionAttribute) {
              return false;
          }
          return extensionAttribute.attribute.keepOnSplit;
      }));
  }

  function isMarkActive(state, typeOrName, attributes = {}) {
      const { empty, ranges } = state.selection;
      const type = typeOrName ? getMarkType(typeOrName, state.schema) : null;
      if (empty) {
          return !!(state.storedMarks || state.selection.$from.marks())
              .filter(mark => {
              if (!type) {
                  return true;
              }
              return type.name === mark.type.name;
          })
              .find(mark => objectIncludes(mark.attrs, attributes, { strict: false }));
      }
      let selectionRange = 0;
      const markRanges = [];
      ranges.forEach(({ $from, $to }) => {
          const from = $from.pos;
          const to = $to.pos;
          state.doc.nodesBetween(from, to, (node, pos) => {
              if (!node.isText && !node.marks.length) {
                  return;
              }
              const relativeFrom = Math.max(from, pos);
              const relativeTo = Math.min(to, pos + node.nodeSize);
              const range = relativeTo - relativeFrom;
              selectionRange += range;
              markRanges.push(...node.marks.map(mark => ({
                  mark,
                  from: relativeFrom,
                  to: relativeTo,
              })));
          });
      });
      if (selectionRange === 0) {
          return false;
      }
      // calculate range of matched mark
      const matchedRange = markRanges
          .filter(markRange => {
          if (!type) {
              return true;
          }
          return type.name === markRange.mark.type.name;
      })
          .filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
          .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
      // calculate range of marks that excludes the searched mark
      // for example `code` doesn’t allow any other marks
      const excludedRange = markRanges
          .filter(markRange => {
          if (!type) {
              return true;
          }
          return markRange.mark.type !== type && markRange.mark.type.excludes(type);
      })
          .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
      // we only include the result of `excludedRange`
      // if there is a match at all
      const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange;
      return range >= selectionRange;
  }

  function isActive(state, name, attributes = {}) {
      if (!name) {
          return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
      }
      const schemaType = getSchemaTypeNameByName(name, state.schema);
      if (schemaType === 'node') {
          return isNodeActive(state, name, attributes);
      }
      if (schemaType === 'mark') {
          return isMarkActive(state, name, attributes);
      }
      return false;
  }

  const isAtEndOfNode = (state, nodeType) => {
      const { $from, $to, $anchor } = state.selection;
      if (nodeType) {
          const parentNode = findParentNode(node => node.type.name === nodeType)(state.selection);
          if (!parentNode) {
              return false;
          }
          const $parentPos = state.doc.resolve(parentNode.pos + 1);
          if ($anchor.pos + 1 === $parentPos.end()) {
              return true;
          }
          return false;
      }
      if ($to.parentOffset < $to.parent.nodeSize - 2 || $from.pos !== $to.pos) {
          return false;
      }
      return true;
  };

  const isAtStartOfNode = (state) => {
      const { $from, $to } = state.selection;
      if ($from.parentOffset > 0 || $from.pos !== $to.pos) {
          return false;
      }
      return true;
  };

  function isList(name, extensions) {
      const { nodeExtensions } = splitExtensions(extensions);
      const extension = nodeExtensions.find(item => item.name === name);
      if (!extension) {
          return false;
      }
      const context = {
          name: extension.name,
          options: extension.options,
          storage: extension.storage,
      };
      const group = callOrReturn(getExtensionField(extension, 'group', context));
      if (typeof group !== 'string') {
          return false;
      }
      return group.split(' ').includes('list');
  }

  /**
   * Returns true if the given prosemirror node is empty.
   */
  function isNodeEmpty(node, { checkChildren = true, ignoreWhitespace = false, } = {}) {
      var _a;
      if (ignoreWhitespace) {
          if (node.type.name === 'hardBreak') {
              // Hard breaks are considered empty
              return true;
          }
          if (node.isText) {
              return /^\s*$/m.test((_a = node.text) !== null && _a !== void 0 ? _a : '');
          }
      }
      if (node.isText) {
          return !node.text;
      }
      if (node.isAtom || node.isLeaf) {
          return false;
      }
      if (node.content.childCount === 0) {
          return true;
      }
      if (checkChildren) {
          let isContentEmpty = true;
          node.content.forEach(childNode => {
              if (isContentEmpty === false) {
                  // Exit early for perf
                  return;
              }
              if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {
                  isContentEmpty = false;
              }
          });
          return isContentEmpty;
      }
      return false;
  }

  function isNodeSelection(value) {
      return value instanceof state.NodeSelection;
  }

  function posToDOMRect(view, from, to) {
      const minPos = 0;
      const maxPos = view.state.doc.content.size;
      const resolvedFrom = minMax(from, minPos, maxPos);
      const resolvedEnd = minMax(to, minPos, maxPos);
      const start = view.coordsAtPos(resolvedFrom);
      const end = view.coordsAtPos(resolvedEnd, -1);
      const top = Math.min(start.top, end.top);
      const bottom = Math.max(start.bottom, end.bottom);
      const left = Math.min(start.left, end.left);
      const right = Math.max(start.right, end.right);
      const width = right - left;
      const height = bottom - top;
      const x = left;
      const y = top;
      const data = {
          top,
          bottom,
          left,
          right,
          width,
          height,
          x,
          y,
      };
      return {
          ...data,
          toJSON: () => data,
      };
  }

  /**
   * The actual implementation of the rewriteUnknownContent function
   */
  function rewriteUnknownContentInner({ json, validMarks, validNodes, options, rewrittenContent = [], }) {
      if (json.marks && Array.isArray(json.marks)) {
          json.marks = json.marks.filter(mark => {
              const name = typeof mark === 'string' ? mark : mark.type;
              if (validMarks.has(name)) {
                  return true;
              }
              rewrittenContent.push({
                  original: JSON.parse(JSON.stringify(mark)),
                  unsupported: name,
              });
              // Just ignore any unknown marks
              return false;
          });
      }
      if (json.content && Array.isArray(json.content)) {
          json.content = json.content
              .map(value => rewriteUnknownContentInner({
              json: value,
              validMarks,
              validNodes,
              options,
              rewrittenContent,
          }).json)
              .filter(a => a !== null && a !== undefined);
      }
      if (json.type && !validNodes.has(json.type)) {
          rewrittenContent.push({
              original: JSON.parse(JSON.stringify(json)),
              unsupported: json.type,
          });
          if (json.content && Array.isArray(json.content) && ((options === null || options === void 0 ? void 0 : options.fallbackToParagraph) !== false)) {
              // Just treat it like a paragraph and hope for the best
              json.type = 'paragraph';
              return {
                  json,
                  rewrittenContent,
              };
          }
          // or just omit it entirely
          return {
              json: null,
              rewrittenContent,
          };
      }
      return { json, rewrittenContent };
  }
  /**
   * Rewrite unknown nodes and marks within JSON content
   * Allowing for user within the editor
   */
  function rewriteUnknownContent(
  /**
   * The JSON content to clean of unknown nodes and marks
   */
  json, 
  /**
   * The schema to use for validation
   */
  schema, 
  /**
   * Options for the cleaning process
   */
  options) {
      return rewriteUnknownContentInner({
          json,
          validNodes: new Set(Object.keys(schema.nodes)),
          validMarks: new Set(Object.keys(schema.marks)),
          options,
      });
  }

  function canSetMark(state, tr, newMarkType) {
      var _a;
      const { selection } = tr;
      let cursor = null;
      if (isTextSelection(selection)) {
          cursor = selection.$cursor;
      }
      if (cursor) {
          const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
          // There can be no current marks that exclude the new mark
          return (!!newMarkType.isInSet(currentMarks)
              || !currentMarks.some(mark => mark.type.excludes(newMarkType)));
      }
      const { ranges } = selection;
      return ranges.some(({ $from, $to }) => {
          let someNodeSupportsMark = $from.depth === 0
              ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType)
              : false;
          state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
              // If we already found a mark that we can enable, return false to bypass the remaining search
              if (someNodeSupportsMark) {
                  return false;
              }
              if (node.isInline) {
                  const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
                  const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks)
                      || !node.marks.some(otherMark => otherMark.type.excludes(newMarkType));
                  someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
              }
              return !someNodeSupportsMark;
          });
          return someNodeSupportsMark;
      });
  }
  const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
      const { selection } = tr;
      const { empty, ranges } = selection;
      const type = getMarkType(typeOrName, state.schema);
      if (dispatch) {
          if (empty) {
              const oldAttributes = getMarkAttributes(state, type);
              tr.addStoredMark(type.create({
                  ...oldAttributes,
                  ...attributes,
              }));
          }
          else {
              ranges.forEach(range => {
                  const from = range.$from.pos;
                  const to = range.$to.pos;
                  state.doc.nodesBetween(from, to, (node, pos) => {
                      const trimmedFrom = Math.max(pos, from);
                      const trimmedTo = Math.min(pos + node.nodeSize, to);
                      const someHasMark = node.marks.find(mark => mark.type === type);
                      // if there is already a mark of this type
                      // we know that we have to merge its attributes
                      // otherwise we add a fresh new mark
                      if (someHasMark) {
                          node.marks.forEach(mark => {
                              if (type === mark.type) {
                                  tr.addMark(trimmedFrom, trimmedTo, type.create({
                                      ...mark.attrs,
                                      ...attributes,
                                  }));
                              }
                          });
                      }
                      else {
                          tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
                      }
                  });
              });
          }
      }
      return canSetMark(state, tr, type);
  };

  const setMeta = (key, value) => ({ tr }) => {
      tr.setMeta(key, value);
      return true;
  };

  const setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
      const type = getNodeType(typeOrName, state.schema);
      let attributesToCopy;
      if (state.selection.$anchor.sameParent(state.selection.$head)) {
          // only copy attributes if the selection is pointing to a node of the same type
          attributesToCopy = state.selection.$anchor.parent.attrs;
      }
      // TODO: use a fallback like insertContent?
      if (!type.isTextblock) {
          console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
          return false;
      }
      return (chain()
          // try to convert node to default node if needed
          .command(({ commands }) => {
          const canSetBlock = commands$1.setBlockType(type, { ...attributesToCopy, ...attributes })(state);
          if (canSetBlock) {
              return true;
          }
          return commands.clearNodes();
      })
          .command(({ state: updatedState }) => {
          return commands$1.setBlockType(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch);
      })
          .run());
  };

  const setNodeSelection = position => ({ tr, dispatch }) => {
      if (dispatch) {
          const { doc } = tr;
          const from = minMax(position, 0, doc.content.size);
          const selection = state.NodeSelection.create(doc, from);
          tr.setSelection(selection);
      }
      return true;
  };

  const setTextSelection = position => ({ tr, dispatch }) => {
      if (dispatch) {
          const { doc } = tr;
          const { from, to } = typeof position === 'number' ? { from: position, to: position } : position;
          const minPos = state.TextSelection.atStart(doc).from;
          const maxPos = state.TextSelection.atEnd(doc).to;
          const resolvedFrom = minMax(from, minPos, maxPos);
          const resolvedEnd = minMax(to, minPos, maxPos);
          const selection = state.TextSelection.create(doc, resolvedFrom, resolvedEnd);
          tr.setSelection(selection);
      }
      return true;
  };

  const sinkListItem = typeOrName => ({ state, dispatch }) => {
      const type = getNodeType(typeOrName, state.schema);
      return schemaList.sinkListItem(type)(state, dispatch);
  };

  function ensureMarks(state, splittableMarks) {
      const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
      if (marks) {
          const filteredMarks = marks.filter(mark => splittableMarks === null || splittableMarks === void 0 ? void 0 : splittableMarks.includes(mark.type.name));
          state.tr.ensureMarks(filteredMarks);
      }
  }
  const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state: state$1, dispatch, editor, }) => {
      const { selection, doc } = tr;
      const { $from, $to } = selection;
      const extensionAttributes = editor.extensionManager.attributes;
      const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
      if (selection instanceof state.NodeSelection && selection.node.isBlock) {
          if (!$from.parentOffset || !transform.canSplit(doc, $from.pos)) {
              return false;
          }
          if (dispatch) {
              if (keepMarks) {
                  ensureMarks(state$1, editor.extensionManager.splittableMarks);
              }
              tr.split($from.pos).scrollIntoView();
          }
          return true;
      }
      if (!$from.parent.isBlock) {
          return false;
      }
      const atEnd = $to.parentOffset === $to.parent.content.size;
      const deflt = $from.depth === 0
          ? undefined
          : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
      let types = atEnd && deflt
          ? [
              {
                  type: deflt,
                  attrs: newAttributes,
              },
          ]
          : undefined;
      let can = transform.canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
      if (!types
          && !can
          && transform.canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)) {
          can = true;
          types = deflt
              ? [
                  {
                      type: deflt,
                      attrs: newAttributes,
                  },
              ]
              : undefined;
      }
      if (dispatch) {
          if (can) {
              if (selection instanceof state.TextSelection) {
                  tr.deleteSelection();
              }
              tr.split(tr.mapping.map($from.pos), 1, types);
              if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
                  const first = tr.mapping.map($from.before());
                  const $first = tr.doc.resolve(first);
                  if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
                      tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
                  }
              }
          }
          if (keepMarks) {
              ensureMarks(state$1, editor.extensionManager.splittableMarks);
          }
          tr.scrollIntoView();
      }
      return can;
  };

  const splitListItem = (typeOrName, overrideAttrs = {}) => ({ tr, state: state$1, dispatch, editor, }) => {
      var _a;
      const type = getNodeType(typeOrName, state$1.schema);
      const { $from, $to } = state$1.selection;
      // @ts-ignore
      // eslint-disable-next-line
      const node = state$1.selection.node;
      if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) {
          return false;
      }
      const grandParent = $from.node(-1);
      if (grandParent.type !== type) {
          return false;
      }
      const extensionAttributes = editor.extensionManager.attributes;
      if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {
          // In an empty block. If this is a nested list, the wrapping
          // list item should be split. Otherwise, bail out and let next
          // command handle lifting.
          if ($from.depth === 2
              || $from.node(-3).type !== type
              || $from.index(-2) !== $from.node(-2).childCount - 1) {
              return false;
          }
          if (dispatch) {
              let wrap = model.Fragment.empty;
              // eslint-disable-next-line
              const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
              // Build a fragment containing empty versions of the structure
              // from the outer list item to the parent node of the cursor
              for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
                  wrap = model.Fragment.from($from.node(d).copy(wrap));
              }
              // eslint-disable-next-line
              const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
              // Add a second list item with an empty default start node
              const newNextTypeAttributes = {
                  ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
                  ...overrideAttrs,
              };
              const nextType = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes)) || undefined;
              wrap = wrap.append(model.Fragment.from(type.createAndFill(null, nextType) || undefined));
              const start = $from.before($from.depth - (depthBefore - 1));
              tr.replace(start, $from.after(-depthAfter), new model.Slice(wrap, 4 - depthBefore, 0));
              let sel = -1;
              tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
                  if (sel > -1) {
                      return false;
                  }
                  if (n.isTextblock && n.content.size === 0) {
                      sel = pos + 1;
                  }
              });
              if (sel > -1) {
                  tr.setSelection(state.TextSelection.near(tr.doc.resolve(sel)));
              }
              tr.scrollIntoView();
          }
          return true;
      }
      const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
      const newTypeAttributes = {
          ...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),
          ...overrideAttrs,
      };
      const newNextTypeAttributes = {
          ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
          ...overrideAttrs,
      };
      tr.delete($from.pos, $to.pos);
      const types = nextType
          ? [
              { type, attrs: newTypeAttributes },
              { type: nextType, attrs: newNextTypeAttributes },
          ]
          : [{ type, attrs: newTypeAttributes }];
      if (!transform.canSplit(tr.doc, $from.pos, 2)) {
          return false;
      }
      if (dispatch) {
          const { selection, storedMarks } = state$1;
          const { splittableMarks } = editor.extensionManager;
          const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks());
          tr.split($from.pos, 2, types).scrollIntoView();
          if (!marks || !dispatch) {
              return true;
          }
          const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name));
          tr.ensureMarks(filteredMarks);
      }
      return true;
  };

  const joinListBackwards = (tr, listType) => {
      const list = findParentNode(node => node.type === listType)(tr.selection);
      if (!list) {
          return true;
      }
      const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
      if (before === undefined) {
          return true;
      }
      const nodeBefore = tr.doc.nodeAt(before);
      const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) && transform.canJoin(tr.doc, list.pos);
      if (!canJoinBackwards) {
          return true;
      }
      tr.join(list.pos);
      return true;
  };
  const joinListForwards = (tr, listType) => {
      const list = findParentNode(node => node.type === listType)(tr.selection);
      if (!list) {
          return true;
      }
      const after = tr.doc.resolve(list.start).after(list.depth);
      if (after === undefined) {
          return true;
      }
      const nodeAfter = tr.doc.nodeAt(after);
      const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type) && transform.canJoin(tr.doc, after);
      if (!canJoinForwards) {
          return true;
      }
      tr.join(after);
      return true;
  };
  const toggleList = (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) => ({ editor, tr, state, dispatch, chain, commands, can, }) => {
      const { extensions, splittableMarks } = editor.extensionManager;
      const listType = getNodeType(listTypeOrName, state.schema);
      const itemType = getNodeType(itemTypeOrName, state.schema);
      const { selection, storedMarks } = state;
      const { $from, $to } = selection;
      const range = $from.blockRange($to);
      const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks());
      if (!range) {
          return false;
      }
      const parentList = findParentNode(node => isList(node.type.name, extensions))(selection);
      if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
          // remove list
          if (parentList.node.type === listType) {
              return commands.liftListItem(itemType);
          }
          // change list type
          if (isList(parentList.node.type.name, extensions)
              && listType.validContent(parentList.node.content)
              && dispatch) {
              return chain()
                  .command(() => {
                  tr.setNodeMarkup(parentList.pos, listType);
                  return true;
              })
                  .command(() => joinListBackwards(tr, listType))
                  .command(() => joinListForwards(tr, listType))
                  .run();
          }
      }
      if (!keepMarks || !marks || !dispatch) {
          return chain()
              // try to convert node to default node if needed
              .command(() => {
              const canWrapInList = can().wrapInList(listType, attributes);
              if (canWrapInList) {
                  return true;
              }
              return commands.clearNodes();
          })
              .wrapInList(listType, attributes)
              .command(() => joinListBackwards(tr, listType))
              .command(() => joinListForwards(tr, listType))
              .run();
      }
      return (chain()
          // try to convert node to default node if needed
          .command(() => {
          const canWrapInList = can().wrapInList(listType, attributes);
          const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name));
          tr.ensureMarks(filteredMarks);
          if (canWrapInList) {
              return true;
          }
          return commands.clearNodes();
      })
          .wrapInList(listType, attributes)
          .command(() => joinListBackwards(tr, listType))
          .command(() => joinListForwards(tr, listType))
          .run());
  };

  const toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands }) => {
      const { extendEmptyMarkRange = false } = options;
      const type = getMarkType(typeOrName, state.schema);
      const isActive = isMarkActive(state, type, attributes);
      if (isActive) {
          return commands.unsetMark(type, { extendEmptyMarkRange });
      }
      return commands.setMark(type, attributes);
  };

  const toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands }) => {
      const type = getNodeType(typeOrName, state.schema);
      const toggleType = getNodeType(toggleTypeOrName, state.schema);
      const isActive = isNodeActive(state, type, attributes);
      let attributesToCopy;
      if (state.selection.$anchor.sameParent(state.selection.$head)) {
          // only copy attributes if the selection is pointing to a node of the same type
          attributesToCopy = state.selection.$anchor.parent.attrs;
      }
      if (isActive) {
          return commands.setNode(toggleType, attributesToCopy);
      }
      // If the node is not active, we want to set the new node type with the given attributes
      // Copying over the attributes from the current node if the selection is pointing to a node of the same type
      return commands.setNode(type, { ...attributesToCopy, ...attributes });
  };

  const toggleWrap = (typeOrName, attributes = {}) => ({ state, commands }) => {
      const type = getNodeType(typeOrName, state.schema);
      const isActive = isNodeActive(state, type, attributes);
      if (isActive) {
          return commands.lift(type);
      }
      return commands.wrapIn(type, attributes);
  };

  const undoInputRule = () => ({ state, dispatch }) => {
      const plugins = state.plugins;
      for (let i = 0; i < plugins.length; i += 1) {
          const plugin = plugins[i];
          let undoable;
          // @ts-ignore
          // eslint-disable-next-line
          if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
              if (dispatch) {
                  const tr = state.tr;
                  const toUndo = undoable.transform;
                  for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
                      tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
                  }
                  if (undoable.text) {
                      const marks = tr.doc.resolve(undoable.from).marks();
                      tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
                  }
                  else {
                      tr.delete(undoable.from, undoable.to);
                  }
              }
              return true;
          }
      }
      return false;
  };

  const unsetAllMarks = () => ({ tr, dispatch }) => {
      const { selection } = tr;
      const { empty, ranges } = selection;
      if (empty) {
          return true;
      }
      if (dispatch) {
          ranges.forEach(range => {
              tr.removeMark(range.$from.pos, range.$to.pos);
          });
      }
      return true;
  };

  const unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
      var _a;
      const { extendEmptyMarkRange = false } = options;
      const { selection } = tr;
      const type = getMarkType(typeOrName, state.schema);
      const { $from, empty, ranges } = selection;
      if (!dispatch) {
          return true;
      }
      if (empty && extendEmptyMarkRange) {
          let { from, to } = selection;
          const attrs = (_a = $from.marks().find(mark => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
          const range = getMarkRange($from, type, attrs);
          if (range) {
              from = range.from;
              to = range.to;
          }
          tr.removeMark(from, to, type);
      }
      else {
          ranges.forEach(range => {
              tr.removeMark(range.$from.pos, range.$to.pos, type);
          });
      }
      tr.removeStoredMark(type);
      return true;
  };

  const updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
      let nodeType = null;
      let markType = null;
      const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string' ? typeOrName : typeOrName.name, state.schema);
      if (!schemaType) {
          return false;
      }
      if (schemaType === 'node') {
          nodeType = getNodeType(typeOrName, state.schema);
      }
      if (schemaType === 'mark') {
          markType = getMarkType(typeOrName, state.schema);
      }
      if (dispatch) {
          tr.selection.ranges.forEach((range) => {
              const from = range.$from.pos;
              const to = range.$to.pos;
              let lastPos;
              let lastNode;
              let trimmedFrom;
              let trimmedTo;
              if (tr.selection.empty) {
                  state.doc.nodesBetween(from, to, (node, pos) => {
                      if (nodeType && nodeType === node.type) {
                          trimmedFrom = Math.max(pos, from);
                          trimmedTo = Math.min(pos + node.nodeSize, to);
                          lastPos = pos;
                          lastNode = node;
                      }
                  });
              }
              else {
                  state.doc.nodesBetween(from, to, (node, pos) => {
                      if (pos < from && nodeType && nodeType === node.type) {
                          trimmedFrom = Math.max(pos, from);
                          trimmedTo = Math.min(pos + node.nodeSize, to);
                          lastPos = pos;
                          lastNode = node;
                      }
                      if (pos >= from && pos <= to) {
                          if (nodeType && nodeType === node.type) {
                              tr.setNodeMarkup(pos, undefined, {
                                  ...node.attrs,
                                  ...attributes,
                              });
                          }
                          if (markType && node.marks.length) {
                              node.marks.forEach((mark) => {
                                  if (markType === mark.type) {
                                      const trimmedFrom2 = Math.max(pos, from);
                                      const trimmedTo2 = Math.min(pos + node.nodeSize, to);
                                      tr.addMark(trimmedFrom2, trimmedTo2, markType.create({
                                          ...mark.attrs,
                                          ...attributes,
                                      }));
                                  }
                              });
                          }
                      }
                  });
              }
              if (lastNode) {
                  if (lastPos !== undefined) {
                      tr.setNodeMarkup(lastPos, undefined, {
                          ...lastNode.attrs,
                          ...attributes,
                      });
                  }
                  if (markType && lastNode.marks.length) {
                      lastNode.marks.forEach((mark) => {
                          if (markType === mark.type) {
                              tr.addMark(trimmedFrom, trimmedTo, markType.create({
                                  ...mark.attrs,
                                  ...attributes,
                              }));
                          }
                      });
                  }
              }
          });
      }
      return true;
  };

  const wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
      const type = getNodeType(typeOrName, state.schema);
      return commands$1.wrapIn(type, attributes)(state, dispatch);
  };

  const wrapInList = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
      const type = getNodeType(typeOrName, state.schema);
      return schemaList.wrapInList(type, attributes)(state, dispatch);
  };

  var commands = /*#__PURE__*/Object.freeze({
    __proto__: null,
    blur: blur,
    clearContent: clearContent,
    clearNodes: clearNodes,
    command: command,
    createParagraphNear: createParagraphNear,
    cut: cut,
    deleteCurrentNode: deleteCurrentNode,
    deleteNode: deleteNode,
    deleteRange: deleteRange,
    deleteSelection: deleteSelection,
    enter: enter,
    exitCode: exitCode,
    extendMarkRange: extendMarkRange,
    first: first,
    focus: focus,
    forEach: forEach,
    insertContent: insertContent,
    insertContentAt: insertContentAt,
    joinBackward: joinBackward,
    joinDown: joinDown,
    joinForward: joinForward,
    joinItemBackward: joinItemBackward,
    joinItemForward: joinItemForward,
    joinTextblockBackward: joinTextblockBackward,
    joinTextblockForward: joinTextblockForward,
    joinUp: joinUp,
    keyboardShortcut: keyboardShortcut,
    lift: lift,
    liftEmptyBlock: liftEmptyBlock,
    liftListItem: liftListItem,
    newlineInCode: newlineInCode,
    resetAttributes: resetAttributes,
    scrollIntoView: scrollIntoView,
    selectAll: selectAll,
    selectNodeBackward: selectNodeBackward,
    selectNodeForward: selectNodeForward,
    selectParentNode: selectParentNode,
    selectTextblockEnd: selectTextblockEnd,
    selectTextblockStart: selectTextblockStart,
    setContent: setContent,
    setMark: setMark,
    setMeta: setMeta,
    setNode: setNode,
    setNodeSelection: setNodeSelection,
    setTextSelection: setTextSelection,
    sinkListItem: sinkListItem,
    splitBlock: splitBlock,
    splitListItem: splitListItem,
    toggleList: toggleList,
    toggleMark: toggleMark,
    toggleNode: toggleNode,
    toggleWrap: toggleWrap,
    undoInputRule: undoInputRule,
    unsetAllMarks: unsetAllMarks,
    unsetMark: unsetMark,
    updateAttributes: updateAttributes,
    wrapIn: wrapIn,
    wrapInList: wrapInList
  });

  const Commands = Extension.create({
      name: 'commands',
      addCommands() {
          return {
              ...commands,
          };
      },
  });

  const Drop = Extension.create({
      name: 'drop',
      addProseMirrorPlugins() {
          return [
              new state.Plugin({
                  key: new state.PluginKey('tiptapDrop'),
                  props: {
                      handleDrop: (_, e, slice, moved) => {
                          this.editor.emit('drop', {
                              editor: this.editor,
                              event: e,
                              slice,
                              moved,
                          });
                      },
                  },
              }),
          ];
      },
  });

  const Editable = Extension.create({
      name: 'editable',
      addProseMirrorPlugins() {
          return [
              new state.Plugin({
                  key: new state.PluginKey('editable'),
                  props: {
                      editable: () => this.editor.options.editable,
                  },
              }),
          ];
      },
  });

  const focusEventsPluginKey = new state.PluginKey('focusEvents');
  const FocusEvents = Extension.create({
      name: 'focusEvents',
      addProseMirrorPlugins() {
          const { editor } = this;
          return [
              new state.Plugin({
                  key: focusEventsPluginKey,
                  props: {
                      handleDOMEvents: {
                          focus: (view, event) => {
                              editor.isFocused = true;
                              const transaction = editor.state.tr
                                  .setMeta('focus', { event })
                                  .setMeta('addToHistory', false);
                              view.dispatch(transaction);
                              return false;
                          },
                          blur: (view, event) => {
                              editor.isFocused = false;
                              const transaction = editor.state.tr
                                  .setMeta('blur', { event })
                                  .setMeta('addToHistory', false);
                              view.dispatch(transaction);
                              return false;
                          },
                      },
                  },
              }),
          ];
      },
  });

  const Keymap = Extension.create({
      name: 'keymap',
      addKeyboardShortcuts() {
          const handleBackspace = () => this.editor.commands.first(({ commands }) => [
              () => commands.undoInputRule(),
              // maybe convert first text block node to default node
              () => commands.command(({ tr }) => {
                  const { selection, doc } = tr;
                  const { empty, $anchor } = selection;
                  const { pos, parent } = $anchor;
                  const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor;
                  const parentIsIsolating = $parentPos.parent.type.spec.isolating;
                  const parentPos = $anchor.pos - $anchor.parentOffset;
                  const isAtStart = (parentIsIsolating && $parentPos.parent.childCount === 1)
                      ? parentPos === $anchor.pos
                      : state.Selection.atStart(doc).from === pos;
                  if (!empty
                      || !parent.type.isTextblock
                      || parent.textContent.length
                      || !isAtStart
                      || (isAtStart && $anchor.parent.type.name === 'paragraph') // prevent clearNodes when no nodes to clear, otherwise history stack is appended
                  ) {
                      return false;
                  }
                  return commands.clearNodes();
              }),
              () => commands.deleteSelection(),
              () => commands.joinBackward(),
              () => commands.selectNodeBackward(),
          ]);
          const handleDelete = () => this.editor.commands.first(({ commands }) => [
              () => commands.deleteSelection(),
              () => commands.deleteCurrentNode(),
              () => commands.joinForward(),
              () => commands.selectNodeForward(),
          ]);
          const handleEnter = () => this.editor.commands.first(({ commands }) => [
              () => commands.newlineInCode(),
              () => commands.createParagraphNear(),
              () => commands.liftEmptyBlock(),
              () => commands.splitBlock(),
          ]);
          const baseKeymap = {
              Enter: handleEnter,
              'Mod-Enter': () => this.editor.commands.exitCode(),
              Backspace: handleBackspace,
              'Mod-Backspace': handleBackspace,
              'Shift-Backspace': handleBackspace,
              Delete: handleDelete,
              'Mod-Delete': handleDelete,
              'Mod-a': () => this.editor.commands.selectAll(),
          };
          const pcKeymap = {
              ...baseKeymap,
          };
          const macKeymap = {
              ...baseKeymap,
              'Ctrl-h': handleBackspace,
              'Alt-Backspace': handleBackspace,
              'Ctrl-d': handleDelete,
              'Ctrl-Alt-Backspace': handleDelete,
              'Alt-Delete': handleDelete,
              'Alt-d': handleDelete,
              'Ctrl-a': () => this.editor.commands.selectTextblockStart(),
              'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),
          };
          if (isiOS() || isMacOS()) {
              return macKeymap;
          }
          return pcKeymap;
      },
      addProseMirrorPlugins() {
          return [
              // With this plugin we check if the whole document was selected and deleted.
              // In this case we will additionally call `clearNodes()` to convert e.g. a heading
              // to a paragraph if necessary.
              // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
              // with many other commands.
              new state.Plugin({
                  key: new state.PluginKey('clearDocument'),
                  appendTransaction: (transactions, oldState, newState) => {
                      if (transactions.some(tr => tr.getMeta('composition'))) {
                          return;
                      }
                      const docChanges = transactions.some(transaction => transaction.docChanged)
                          && !oldState.doc.eq(newState.doc);
                      const ignoreTr = transactions.some(transaction => transaction.getMeta('preventClearDocument'));
                      if (!docChanges || ignoreTr) {
                          return;
                      }
                      const { empty, from, to } = oldState.selection;
                      const allFrom = state.Selection.atStart(oldState.doc).from;
                      const allEnd = state.Selection.atEnd(oldState.doc).to;
                      const allWasSelected = from === allFrom && to === allEnd;
                      if (empty || !allWasSelected) {
                          return;
                      }
                      const isEmpty = isNodeEmpty(newState.doc);
                      if (!isEmpty) {
                          return;
                      }
                      const tr = newState.tr;
                      const state$1 = createChainableState({
                          state: newState,
                          transaction: tr,
                      });
                      const { commands } = new CommandManager({
                          editor: this.editor,
                          state: state$1,
                      });
                      commands.clearNodes();
                      if (!tr.steps.length) {
                          return;
                      }
                      return tr;
                  },
              }),
          ];
      },
  });

  const Paste = Extension.create({
      name: 'paste',
      addProseMirrorPlugins() {
          return [
              new state.Plugin({
                  key: new state.PluginKey('tiptapPaste'),
                  props: {
                      handlePaste: (_view, e, slice) => {
                          this.editor.emit('paste', {
                              editor: this.editor,
                              event: e,
                              slice,
                          });
                      },
                  },
              }),
          ];
      },
  });

  const Tabindex = Extension.create({
      name: 'tabindex',
      addProseMirrorPlugins() {
          return [
              new state.Plugin({
                  key: new state.PluginKey('tabindex'),
                  props: {
                      attributes: () => (this.editor.isEditable ? { tabindex: '0' } : {}),
                  },
              }),
          ];
      },
  });

  var index = /*#__PURE__*/Object.freeze({
    __proto__: null,
    ClipboardTextSerializer: ClipboardTextSerializer,
    Commands: Commands,
    Drop: Drop,
    Editable: Editable,
    FocusEvents: FocusEvents,
    Keymap: Keymap,
    Paste: Paste,
    Tabindex: Tabindex,
    focusEventsPluginKey: focusEventsPluginKey
  });

  class NodePos {
      get name() {
          return this.node.type.name;
      }
      constructor(pos, editor, isBlock = false, node = null) {
          this.currentNode = null;
          this.actualDepth = null;
          this.isBlock = isBlock;
          this.resolvedPos = pos;
          this.editor = editor;
          this.currentNode = node;
      }
      get node() {
          return this.currentNode || this.resolvedPos.node();
      }
      get element() {
          return this.editor.view.domAtPos(this.pos).node;
      }
      get depth() {
          var _a;
          return (_a = this.actualDepth) !== null && _a !== void 0 ? _a : this.resolvedPos.depth;
      }
      get pos() {
          return this.resolvedPos.pos;
      }
      get content() {
          return this.node.content;
      }
      set content(content) {
          let from = this.from;
          let to = this.to;
          if (this.isBlock) {
              if (this.content.size === 0) {
                  console.error(`You can’t set content on a block node. Tried to set content on ${this.name} at ${this.pos}`);
                  return;
              }
              from = this.from + 1;
              to = this.to - 1;
          }
          this.editor.commands.insertContentAt({ from, to }, content);
      }
      get attributes() {
          return this.node.attrs;
      }
      get textContent() {
          return this.node.textContent;
      }
      get size() {
          return this.node.nodeSize;
      }
      get from() {
          if (this.isBlock) {
              return this.pos;
          }
          return this.resolvedPos.start(this.resolvedPos.depth);
      }
      get range() {
          return {
              from: this.from,
              to: this.to,
          };
      }
      get to() {
          if (this.isBlock) {
              return this.pos + this.size;
          }
          return this.resolvedPos.end(this.resolvedPos.depth) + (this.node.isText ? 0 : 1);
      }
      get parent() {
          if (this.depth === 0) {
              return null;
          }
          const parentPos = this.resolvedPos.start(this.resolvedPos.depth - 1);
          const $pos = this.resolvedPos.doc.resolve(parentPos);
          return new NodePos($pos, this.editor);
      }
      get before() {
          let $pos = this.resolvedPos.doc.resolve(this.from - (this.isBlock ? 1 : 2));
          if ($pos.depth !== this.depth) {
              $pos = this.resolvedPos.doc.resolve(this.from - 3);
          }
          return new NodePos($pos, this.editor);
      }
      get after() {
          let $pos = this.resolvedPos.doc.resolve(this.to + (this.isBlock ? 2 : 1));
          if ($pos.depth !== this.depth) {
              $pos = this.resolvedPos.doc.resolve(this.to + 3);
          }
          return new NodePos($pos, this.editor);
      }
      get children() {
          const children = [];
          this.node.content.forEach((node, offset) => {
              const isBlock = node.isBlock && !node.isTextblock;
              const isNonTextAtom = node.isAtom && !node.isText;
              const targetPos = this.pos + offset + (isNonTextAtom ? 0 : 1);
              // Check if targetPos is within valid document range
              if (targetPos < 0 || targetPos > this.resolvedPos.doc.nodeSize - 2) {
                  return;
              }
              const $pos = this.resolvedPos.doc.resolve(targetPos);
              if (!isBlock && $pos.depth <= this.depth) {
                  return;
              }
              const childNodePos = new NodePos($pos, this.editor, isBlock, isBlock ? node : null);
              if (isBlock) {
                  childNodePos.actualDepth = this.depth + 1;
              }
              children.push(new NodePos($pos, this.editor, isBlock, isBlock ? node : null));
          });
          return children;
      }
      get firstChild() {
          return this.children[0] || null;
      }
      get lastChild() {
          const children = this.children;
          return children[children.length - 1] || null;
      }
      closest(selector, attributes = {}) {
          let node = null;
          let currentNode = this.parent;
          while (currentNode && !node) {
              if (currentNode.node.type.name === selector) {
                  if (Object.keys(attributes).length > 0) {
                      const nodeAttributes = currentNode.node.attrs;
                      const attrKeys = Object.keys(attributes);
                      for (let index = 0; index < attrKeys.length; index += 1) {
                          const key = attrKeys[index];
                          if (nodeAttributes[key] !== attributes[key]) {
                              break;
                          }
                      }
                  }
                  else {
                      node = currentNode;
                  }
              }
              currentNode = currentNode.parent;
          }
          return node;
      }
      querySelector(selector, attributes = {}) {
          return this.querySelectorAll(selector, attributes, true)[0] || null;
      }
      querySelectorAll(selector, attributes = {}, firstItemOnly = false) {
          let nodes = [];
          if (!this.children || this.children.length === 0) {
              return nodes;
          }
          const attrKeys = Object.keys(attributes);
          /**
           * Finds all children recursively that match the selector and attributes
           * If firstItemOnly is true, it will return the first item found
           */
          this.children.forEach(childPos => {
              // If we already found a node and we only want the first item, we dont need to keep going
              if (firstItemOnly && nodes.length > 0) {
                  return;
              }
              if (childPos.node.type.name === selector) {
                  const doesAllAttributesMatch = attrKeys.every(key => attributes[key] === childPos.node.attrs[key]);
                  if (doesAllAttributesMatch) {
                      nodes.push(childPos);
                  }
              }
              // If we already found a node and we only want the first item, we can stop here and skip the recursion
              if (firstItemOnly && nodes.length > 0) {
                  return;
              }
              nodes = nodes.concat(childPos.querySelectorAll(selector, attributes, firstItemOnly));
          });
          return nodes;
      }
      setAttribute(attributes) {
          const { tr } = this.editor.state;
          tr.setNodeMarkup(this.from, undefined, {
              ...this.node.attrs,
              ...attributes,
          });
          this.editor.view.dispatch(tr);
      }
  }

  const style = `.ProseMirror {
  position: relative;
}

.ProseMirror {
  word-wrap: break-word;
  white-space: pre-wrap;
  white-space: break-spaces;
  -webkit-font-variant-ligatures: none;
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
}

.ProseMirror [contenteditable="false"] {
  white-space: normal;
}

.ProseMirror [contenteditable="false"] [contenteditable="true"] {
  white-space: pre-wrap;
}

.ProseMirror pre {
  white-space: pre-wrap;
}

img.ProseMirror-separator {
  display: inline !important;
  border: none !important;
  margin: 0 !important;
  width: 0 !important;
  height: 0 !important;
}

.ProseMirror-gapcursor {
  display: none;
  pointer-events: none;
  position: absolute;
  margin: 0;
}

.ProseMirror-gapcursor:after {
  content: "";
  display: block;
  position: absolute;
  top: -2px;
  width: 20px;
  border-top: 1px solid black;
  animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
}

@keyframes ProseMirror-cursor-blink {
  to {
    visibility: hidden;
  }
}

.ProseMirror-hideselection *::selection {
  background: transparent;
}

.ProseMirror-hideselection *::-moz-selection {
  background: transparent;
}

.ProseMirror-hideselection * {
  caret-color: transparent;
}

.ProseMirror-focused .ProseMirror-gapcursor {
  display: block;
}

.tippy-box[data-animation=fade][data-state=hidden] {
  opacity: 0
}`;

  function createStyleTag(style, nonce, suffix) {
      const tiptapStyleTag = document.querySelector(`style[data-tiptap-style${suffix ? `-${suffix}` : ''}]`);
      if (tiptapStyleTag !== null) {
          return tiptapStyleTag;
      }
      const styleNode = document.createElement('style');
      if (nonce) {
          styleNode.setAttribute('nonce', nonce);
      }
      styleNode.setAttribute(`data-tiptap-style${suffix ? `-${suffix}` : ''}`, '');
      styleNode.innerHTML = style;
      document.getElementsByTagName('head')[0].appendChild(styleNode);
      return styleNode;
  }

  class Editor extends EventEmitter {
      constructor(options = {}) {
          super();
          this.isFocused = false;
          /**
           * The editor is considered initialized after the `create` event has been emitted.
           */
          this.isInitialized = false;
          this.extensionStorage = {};
          this.options = {
              element: document.createElement('div'),
              content: '',
              injectCSS: true,
              injectNonce: undefined,
              extensions: [],
              autofocus: false,
              editable: true,
              editorProps: {},
              parseOptions: {},
              coreExtensionOptions: {},
              enableInputRules: true,
              enablePasteRules: true,
              enableCoreExtensions: true,
              enableContentCheck: false,
              emitContentError: false,
              onBeforeCreate: () => null,
              onCreate: () => null,
              onUpdate: () => null,
              onSelectionUpdate: () => null,
              onTransaction: () => null,
              onFocus: () => null,
              onBlur: () => null,
              onDestroy: () => null,
              onContentError: ({ error }) => { throw error; },
              onPaste: () => null,
              onDrop: () => null,
          };
          this.isCapturingTransaction = false;
          this.capturedTransaction = null;
          this.setOptions(options);
          this.createExtensionManager();
          this.createCommandManager();
          this.createSchema();
          this.on('beforeCreate', this.options.onBeforeCreate);
          this.emit('beforeCreate', { editor: this });
          this.on('contentError', this.options.onContentError);
          this.createView();
          this.injectCSS();
          this.on('create', this.options.onCreate);
          this.on('update', this.options.onUpdate);
          this.on('selectionUpdate', this.options.onSelectionUpdate);
          this.on('transaction', this.options.onTransaction);
          this.on('focus', this.options.onFocus);
          this.on('blur', this.options.onBlur);
          this.on('destroy', this.options.onDestroy);
          this.on('drop', ({ event, slice, moved }) => this.options.onDrop(event, slice, moved));
          this.on('paste', ({ event, slice }) => this.options.onPaste(event, slice));
          window.setTimeout(() => {
              if (this.isDestroyed) {
                  return;
              }
              this.commands.focus(this.options.autofocus);
              this.emit('create', { editor: this });
              this.isInitialized = true;
          }, 0);
      }
      /**
       * Returns the editor storage.
       */
      get storage() {
          return this.extensionStorage;
      }
      /**
       * An object of all registered commands.
       */
      get commands() {
          return this.commandManager.commands;
      }
      /**
       * Create a command chain to call multiple commands at once.
       */
      chain() {
          return this.commandManager.chain();
      }
      /**
       * Check if a command or a command chain can be executed. Without executing it.
       */
      can() {
          return this.commandManager.can();
      }
      /**
       * Inject CSS styles.
       */
      injectCSS() {
          if (this.options.injectCSS && document) {
              this.css = createStyleTag(style, this.options.injectNonce);
          }
      }
      /**
       * Update editor options.
       *
       * @param options A list of options
       */
      setOptions(options = {}) {
          this.options = {
              ...this.options,
              ...options,
          };
          if (!this.view || !this.state || this.isDestroyed) {
              return;
          }
          if (this.options.editorProps) {
              this.view.setProps(this.options.editorProps);
          }
          this.view.updateState(this.state);
      }
      /**
       * Update editable state of the editor.
       */
      setEditable(editable, emitUpdate = true) {
          this.setOptions({ editable });
          if (emitUpdate) {
              this.emit('update', { editor: this, transaction: this.state.tr });
          }
      }
      /**
       * Returns whether the editor is editable.
       */
      get isEditable() {
          // since plugins are applied after creating the view
          // `editable` is always `true` for one tick.
          // that’s why we also have to check for `options.editable`
          return this.options.editable && this.view && this.view.editable;
      }
      /**
       * Returns the editor state.
       */
      get state() {
          return this.view.state;
      }
      /**
       * Register a ProseMirror plugin.
       *
       * @param plugin A ProseMirror plugin
       * @param handlePlugins Control how to merge the plugin into the existing plugins.
       * @returns The new editor state
       */
      registerPlugin(plugin, handlePlugins) {
          const plugins = isFunction(handlePlugins)
              ? handlePlugins(plugin, [...this.state.plugins])
              : [...this.state.plugins, plugin];
          const state = this.state.reconfigure({ plugins });
          this.view.updateState(state);
          return state;
      }
      /**
       * Unregister a ProseMirror plugin.
       *
       * @param nameOrPluginKeyToRemove The plugins name
       * @returns The new editor state or undefined if the editor is destroyed
       */
      unregisterPlugin(nameOrPluginKeyToRemove) {
          if (this.isDestroyed) {
              return undefined;
          }
          const prevPlugins = this.state.plugins;
          let plugins = prevPlugins;
          [].concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {
              // @ts-ignore
              const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key;
              // @ts-ignore
              plugins = plugins.filter(plugin => !plugin.key.startsWith(name));
          });
          if (prevPlugins.length === plugins.length) {
              // No plugin was removed, so we don’t need to update the state
              return undefined;
          }
          const state = this.state.reconfigure({
              plugins,
          });
          this.view.updateState(state);
          return state;
      }
      /**
       * Creates an extension manager.
       */
      createExtensionManager() {
          var _a, _b;
          const coreExtensions = this.options.enableCoreExtensions ? [
              Editable,
              ClipboardTextSerializer.configure({
                  blockSeparator: (_b = (_a = this.options.coreExtensionOptions) === null || _a === void 0 ? void 0 : _a.clipboardTextSerializer) === null || _b === void 0 ? void 0 : _b.blockSeparator,
              }),
              Commands,
              FocusEvents,
              Keymap,
              Tabindex,
              Drop,
              Paste,
          ].filter(ext => {
              if (typeof this.options.enableCoreExtensions === 'object') {
                  return this.options.enableCoreExtensions[ext.name] !== false;
              }
              return true;
          }) : [];
          const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
              return ['extension', 'node', 'mark'].includes(extension === null || extension === void 0 ? void 0 : extension.type);
          });
          this.extensionManager = new ExtensionManager(allExtensions, this);
      }
      /**
       * Creates an command manager.
       */
      createCommandManager() {
          this.commandManager = new CommandManager({
              editor: this,
          });
      }
      /**
       * Creates a ProseMirror schema.
       */
      createSchema() {
          this.schema = this.extensionManager.schema;
      }
      /**
       * Creates a ProseMirror view.
       */
      createView() {
          var _a;
          let doc;
          try {
              doc = createDocument(this.options.content, this.schema, this.options.parseOptions, { errorOnInvalidContent: this.options.enableContentCheck });
          }
          catch (e) {
              if (!(e instanceof Error) || !['[tiptap error]: Invalid JSON content', '[tiptap error]: Invalid HTML content'].includes(e.message)) {
                  // Not the content error we were expecting
                  throw e;
              }
              this.emit('contentError', {
                  editor: this,
                  error: e,
                  disableCollaboration: () => {
                      if (this.storage.collaboration) {
                          this.storage.collaboration.isDisabled = true;
                      }
                      // To avoid syncing back invalid content, reinitialize the extensions without the collaboration extension
                      this.options.extensions = this.options.extensions.filter(extension => extension.name !== 'collaboration');
                      // Restart the initialization process by recreating the extension manager with the new set of extensions
                      this.createExtensionManager();
                  },
              });
              // Content is invalid, but attempt to create it anyway, stripping out the invalid parts
              doc = createDocument(this.options.content, this.schema, this.options.parseOptions, { errorOnInvalidContent: false });
          }
          const selection = resolveFocusPosition(doc, this.options.autofocus);
          this.view = new view.EditorView(this.options.element, {
              ...this.options.editorProps,
              attributes: {
                  // add `role="textbox"` to the editor element
                  role: 'textbox',
                  ...(_a = this.options.editorProps) === null || _a === void 0 ? void 0 : _a.attributes,
              },
              dispatchTransaction: this.dispatchTransaction.bind(this),
              state: state.EditorState.create({
                  doc,
                  selection: selection || undefined,
              }),
          });
          // `editor.view` is not yet available at this time.
          // Therefore we will add all plugins and node views directly afterwards.
          const newState = this.state.reconfigure({
              plugins: this.extensionManager.plugins,
          });
          this.view.updateState(newState);
          this.createNodeViews();
          this.prependClass();
          // Let’s store the editor instance in the DOM element.
          // So we’ll have access to it for tests.
          // @ts-ignore
          const dom = this.view.dom;
          dom.editor = this;
      }
      /**
       * Creates all node views.
       */
      createNodeViews() {
          if (this.view.isDestroyed) {
              return;
          }
          this.view.setProps({
              nodeViews: this.extensionManager.nodeViews,
          });
      }
      /**
       * Prepend class name to element.
       */
      prependClass() {
          this.view.dom.className = `tiptap ${this.view.dom.className}`;
      }
      captureTransaction(fn) {
          this.isCapturingTransaction = true;
          fn();
          this.isCapturingTransaction = false;
          const tr = this.capturedTransaction;
          this.capturedTransaction = null;
          return tr;
      }
      /**
       * The callback over which to send transactions (state updates) produced by the view.
       *
       * @param transaction An editor state transaction
       */
      dispatchTransaction(transaction) {
          // if the editor / the view of the editor was destroyed
          // the transaction should not be dispatched as there is no view anymore.
          if (this.view.isDestroyed) {
              return;
          }
          if (this.isCapturingTransaction) {
              if (!this.capturedTransaction) {
                  this.capturedTransaction = transaction;
                  return;
              }
              transaction.steps.forEach(step => { var _a; return (_a = this.capturedTransaction) === null || _a === void 0 ? void 0 : _a.step(step); });
              return;
          }
          const state = this.state.apply(transaction);
          const selectionHasChanged = !this.state.selection.eq(state.selection);
          this.emit('beforeTransaction', {
              editor: this,
              transaction,
              nextState: state,
          });
          this.view.updateState(state);
          this.emit('transaction', {
              editor: this,
              transaction,
          });
          if (selectionHasChanged) {
              this.emit('selectionUpdate', {
                  editor: this,
                  transaction,
              });
          }
          const focus = transaction.getMeta('focus');
          const blur = transaction.getMeta('blur');
          if (focus) {
              this.emit('focus', {
                  editor: this,
                  event: focus.event,
                  transaction,
              });
          }
          if (blur) {
              this.emit('blur', {
                  editor: this,
                  event: blur.event,
                  transaction,
              });
          }
          if (!transaction.docChanged || transaction.getMeta('preventUpdate')) {
              return;
          }
          this.emit('update', {
              editor: this,
              transaction,
          });
      }
      /**
       * Get attributes of the currently selected node or mark.
       */
      getAttributes(nameOrType) {
          return getAttributes(this.state, nameOrType);
      }
      isActive(nameOrAttributes, attributesOrUndefined) {
          const name = typeof nameOrAttributes === 'string' ? nameOrAttributes : null;
          const attributes = typeof nameOrAttributes === 'string' ? attributesOrUndefined : nameOrAttributes;
          return isActive(this.state, name, attributes);
      }
      /**
       * Get the document as JSON.
       */
      getJSON() {
          return this.state.doc.toJSON();
      }
      /**
       * Get the document as HTML.
       */
      getHTML() {
          return getHTMLFromFragment(this.state.doc.content, this.schema);
      }
      /**
       * Get the document as text.
       */
      getText(options) {
          const { blockSeparator = '\n\n', textSerializers = {} } = options || {};
          return getText(this.state.doc, {
              blockSeparator,
              textSerializers: {
                  ...getTextSerializersFromSchema(this.schema),
                  ...textSerializers,
              },
          });
      }
      /**
       * Check if there is no content.
       */
      get isEmpty() {
          return isNodeEmpty(this.state.doc);
      }
      /**
       * Get the number of characters for the current document.
       *
       * @deprecated
       */
      getCharacterCount() {
          console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.');
          return this.state.doc.content.size - 2;
      }
      /**
       * Destroy the editor.
       */
      destroy() {
          this.emit('destroy');
          if (this.view) {
              // Cleanup our reference to prevent circular references which caused memory leaks
              // @ts-ignore
              const dom = this.view.dom;
              if (dom && dom.editor) {
                  delete dom.editor;
              }
              this.view.destroy();
          }
          this.removeAllListeners();
      }
      /**
       * Check if the editor is already destroyed.
       */
      get isDestroyed() {
          var _a;
          // @ts-ignore
          return !((_a = this.view) === null || _a === void 0 ? void 0 : _a.docView);
      }
      $node(selector, attributes) {
          var _a;
          return ((_a = this.$doc) === null || _a === void 0 ? void 0 : _a.querySelector(selector, attributes)) || null;
      }
      $nodes(selector, attributes) {
          var _a;
          return ((_a = this.$doc) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selector, attributes)) || null;
      }
      $pos(pos) {
          const $pos = this.state.doc.resolve(pos);
          return new NodePos($pos, this);
      }
      get $doc() {
          return this.$pos(0);
      }
  }

  /**
   * Build an input rule that adds a mark when the
   * matched text is typed into it.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
   */
  function markInputRule(config) {
      return new InputRule({
          find: config.find,
          handler: ({ state, range, match }) => {
              const attributes = callOrReturn(config.getAttributes, undefined, match);
              if (attributes === false || attributes === null) {
                  return null;
              }
              const { tr } = state;
              const captureGroup = match[match.length - 1];
              const fullMatch = match[0];
              if (captureGroup) {
                  const startSpaces = fullMatch.search(/\S/);
                  const textStart = range.from + fullMatch.indexOf(captureGroup);
                  const textEnd = textStart + captureGroup.length;
                  const excludedMarks = getMarksBetween(range.from, range.to, state.doc)
                      .filter(item => {
                      // @ts-ignore
                      const excluded = item.mark.type.excluded;
                      return excluded.find(type => type === config.type && type !== item.mark.type);
                  })
                      .filter(item => item.to > textStart);
                  if (excludedMarks.length) {
                      return null;
                  }
                  if (textEnd < range.to) {
                      tr.delete(textEnd, range.to);
                  }
                  if (textStart > range.from) {
                      tr.delete(range.from + startSpaces, textStart);
                  }
                  const markEnd = range.from + startSpaces + captureGroup.length;
                  tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
                  tr.removeStoredMark(config.type);
              }
          },
      });
  }

  /**
   * Build an input rule that adds a node when the
   * matched text is typed into it.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
   */
  function nodeInputRule(config) {
      return new InputRule({
          find: config.find,
          handler: ({ state, range, match }) => {
              const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
              const { tr } = state;
              const start = range.from;
              let end = range.to;
              const newNode = config.type.create(attributes);
              if (match[1]) {
                  const offset = match[0].lastIndexOf(match[1]);
                  let matchStart = start + offset;
                  if (matchStart > end) {
                      matchStart = end;
                  }
                  else {
                      end = matchStart + match[1].length;
                  }
                  // insert last typed character
                  const lastChar = match[0][match[0].length - 1];
                  tr.insertText(lastChar, start + match[0].length - 1);
                  // insert node from input rule
                  tr.replaceWith(matchStart, end, newNode);
              }
              else if (match[0]) {
                  const insertionStart = config.type.isInline ? start : start - 1;
                  tr.insert(insertionStart, config.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end));
              }
              tr.scrollIntoView();
          },
      });
  }

  /**
   * Build an input rule that changes the type of a textblock when the
   * matched text is typed into it. When using a regular expresion you’ll
   * probably want the regexp to start with `^`, so that the pattern can
   * only occur at the start of a textblock.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
   */
  function textblockTypeInputRule(config) {
      return new InputRule({
          find: config.find,
          handler: ({ state, range, match }) => {
              const $start = state.doc.resolve(range.from);
              const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
              if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {
                  return null;
              }
              state.tr
                  .delete(range.from, range.to)
                  .setBlockType(range.from, range.from, config.type, attributes);
          },
      });
  }

  /**
   * Build an input rule that replaces text when the
   * matched text is typed into it.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
   */
  function textInputRule(config) {
      return new InputRule({
          find: config.find,
          handler: ({ state, range, match }) => {
              let insert = config.replace;
              let start = range.from;
              const end = range.to;
              if (match[1]) {
                  const offset = match[0].lastIndexOf(match[1]);
                  insert += match[0].slice(offset + match[1].length);
                  start += offset;
                  const cutOff = start - end;
                  if (cutOff > 0) {
                      insert = match[0].slice(offset - cutOff, offset) + insert;
                      start = end;
                  }
              }
              state.tr.insertText(insert, start, end);
          },
      });
  }

  /**
   * Build an input rule for automatically wrapping a textblock when a
   * given string is typed. When using a regular expresion you’ll
   * probably want the regexp to start with `^`, so that the pattern can
   * only occur at the start of a textblock.
   *
   * `type` is the type of node to wrap in.
   *
   * By default, if there’s a node with the same type above the newly
   * wrapped node, the rule will try to join those
   * two nodes. You can pass a join predicate, which takes a regular
   * expression match and the node before the wrapped node, and can
   * return a boolean to indicate whether a join should happen.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
   */
  function wrappingInputRule(config) {
      return new InputRule({
          find: config.find,
          handler: ({ state, range, match, chain, }) => {
              const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
              const tr = state.tr.delete(range.from, range.to);
              const $start = tr.doc.resolve(range.from);
              const blockRange = $start.blockRange();
              const wrapping = blockRange && transform.findWrapping(blockRange, config.type, attributes);
              if (!wrapping) {
                  return null;
              }
              tr.wrap(blockRange, wrapping);
              if (config.keepMarks && config.editor) {
                  const { selection, storedMarks } = state;
                  const { splittableMarks } = config.editor.extensionManager;
                  const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks());
                  if (marks) {
                      const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name));
                      tr.ensureMarks(filteredMarks);
                  }
              }
              if (config.keepAttributes) {
                  /** If the nodeType is `bulletList` or `orderedList` set the `nodeType` as `listItem` */
                  const nodeType = config.type.name === 'bulletList' || config.type.name === 'orderedList' ? 'listItem' : 'taskList';
                  chain().updateAttributes(nodeType, attributes).run();
              }
              const before = tr.doc.resolve(range.from - 1).nodeBefore;
              if (before
                  && before.type === config.type
                  && transform.canJoin(tr.doc, range.from - 1)
                  && (!config.joinPredicate || config.joinPredicate(match, before))) {
                  tr.join(range.from - 1);
              }
          },
      });
  }

  /**
   * The Node class is used to create custom node extensions.
   * @see https://tiptap.dev/api/extensions#create-a-new-extension
   */
  class Node {
      constructor(config = {}) {
          this.type = 'node';
          this.name = 'node';
          this.parent = null;
          this.child = null;
          this.config = {
              name: this.name,
              defaultOptions: {},
          };
          this.config = {
              ...this.config,
              ...config,
          };
          this.name = this.config.name;
          if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
              console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
          }
          // TODO: remove `addOptions` fallback
          this.options = this.config.defaultOptions;
          if (this.config.addOptions) {
              this.options = callOrReturn(getExtensionField(this, 'addOptions', {
                  name: this.name,
              }));
          }
          this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
              name: this.name,
              options: this.options,
          })) || {};
      }
      static create(config = {}) {
          return new Node(config);
      }
      configure(options = {}) {
          // return a new instance so we can use the same extension
          // with different calls of `configure`
          const extension = this.extend({
              ...this.config,
              addOptions: () => {
                  return mergeDeep(this.options, options);
              },
          });
          // Always preserve the current name
          extension.name = this.name;
          // Set the parent to be our parent
          extension.parent = this.parent;
          return extension;
      }
      extend(extendedConfig = {}) {
          const extension = new Node(extendedConfig);
          extension.parent = this;
          this.child = extension;
          extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
          if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
              console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
          }
          extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
              name: extension.name,
          }));
          extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
              name: extension.name,
              options: extension.options,
          }));
          return extension;
      }
  }

  /**
   * Node views are used to customize the rendered DOM structure of a node.
   * @see https://tiptap.dev/guide/node-views
   */
  class NodeView {
      constructor(component, props, options) {
          this.isDragging = false;
          this.component = component;
          this.editor = props.editor;
          this.options = {
              stopEvent: null,
              ignoreMutation: null,
              ...options,
          };
          this.extension = props.extension;
          this.node = props.node;
          this.decorations = props.decorations;
          this.innerDecorations = props.innerDecorations;
          this.view = props.view;
          this.HTMLAttributes = props.HTMLAttributes;
          this.getPos = props.getPos;
          this.mount();
      }
      mount() {
          // eslint-disable-next-line
          return;
      }
      get dom() {
          return this.editor.view.dom;
      }
      get contentDOM() {
          return null;
      }
      onDragStart(event) {
          var _a, _b, _c, _d, _e, _f, _g;
          const { view } = this.editor;
          const target = event.target;
          // get the drag handle element
          // `closest` is not available for text nodes so we may have to use its parent
          const dragHandle = target.nodeType === 3
              ? (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.closest('[data-drag-handle]')
              : target.closest('[data-drag-handle]');
          if (!this.dom || ((_b = this.contentDOM) === null || _b === void 0 ? void 0 : _b.contains(target)) || !dragHandle) {
              return;
          }
          let x = 0;
          let y = 0;
          // calculate offset for drag element if we use a different drag handle element
          if (this.dom !== dragHandle) {
              const domBox = this.dom.getBoundingClientRect();
              const handleBox = dragHandle.getBoundingClientRect();
              // In React, we have to go through nativeEvent to reach offsetX/offsetY.
              const offsetX = (_c = event.offsetX) !== null && _c !== void 0 ? _c : (_d = event.nativeEvent) === null || _d === void 0 ? void 0 : _d.offsetX;
              const offsetY = (_e = event.offsetY) !== null && _e !== void 0 ? _e : (_f = event.nativeEvent) === null || _f === void 0 ? void 0 : _f.offsetY;
              x = handleBox.x - domBox.x + offsetX;
              y = handleBox.y - domBox.y + offsetY;
          }
          const clonedNode = this.dom.cloneNode(true);
          (_g = event.dataTransfer) === null || _g === void 0 ? void 0 : _g.setDragImage(clonedNode, x, y);
          const pos = this.getPos();
          if (typeof pos !== 'number') {
              return;
          }
          // we need to tell ProseMirror that we want to move the whole node
          // so we create a NodeSelection
          const selection = state.NodeSelection.create(view.state.doc, pos);
          const transaction = view.state.tr.setSelection(selection);
          view.dispatch(transaction);
      }
      stopEvent(event) {
          var _a;
          if (!this.dom) {
              return false;
          }
          if (typeof this.options.stopEvent === 'function') {
              return this.options.stopEvent({ event });
          }
          const target = event.target;
          const isInElement = this.dom.contains(target) && !((_a = this.contentDOM) === null || _a === void 0 ? void 0 : _a.contains(target));
          // any event from child nodes should be handled by ProseMirror
          if (!isInElement) {
              return false;
          }
          const isDragEvent = event.type.startsWith('drag');
          const isDropEvent = event.type === 'drop';
          const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable;
          // any input event within node views should be ignored by ProseMirror
          if (isInput && !isDropEvent && !isDragEvent) {
              return true;
          }
          const { isEditable } = this.editor;
          const { isDragging } = this;
          const isDraggable = !!this.node.type.spec.draggable;
          const isSelectable = state.NodeSelection.isSelectable(this.node);
          const isCopyEvent = event.type === 'copy';
          const isPasteEvent = event.type === 'paste';
          const isCutEvent = event.type === 'cut';
          const isClickEvent = event.type === 'mousedown';
          // ProseMirror tries to drag selectable nodes
          // even if `draggable` is set to `false`
          // this fix prevents that
          if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
              event.preventDefault();
          }
          if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
              event.preventDefault();
              return false;
          }
          // we have to store that dragging started
          if (isDraggable && isEditable && !isDragging && isClickEvent) {
              const dragHandle = target.closest('[data-drag-handle]');
              const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
              if (isValidDragHandle) {
                  this.isDragging = true;
                  document.addEventListener('dragend', () => {
                      this.isDragging = false;
                  }, { once: true });
                  document.addEventListener('drop', () => {
                      this.isDragging = false;
                  }, { once: true });
                  document.addEventListener('mouseup', () => {
                      this.isDragging = false;
                  }, { once: true });
              }
          }
          // these events are handled by prosemirror
          if (isDragging
              || isDropEvent
              || isCopyEvent
              || isPasteEvent
              || isCutEvent
              || (isClickEvent && isSelectable)) {
              return false;
          }
          return true;
      }
      /**
       * Called when a DOM [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or a selection change happens within the view.
       * @return `false` if the editor should re-read the selection or re-parse the range around the mutation
       * @return `true` if it can safely be ignored.
       */
      ignoreMutation(mutation) {
          if (!this.dom || !this.contentDOM) {
              return true;
          }
          if (typeof this.options.ignoreMutation === 'function') {
              return this.options.ignoreMutation({ mutation });
          }
          // a leaf/atom node is like a black box for ProseMirror
          // and should be fully handled by the node view
          if (this.node.isLeaf || this.node.isAtom) {
              return true;
          }
          // ProseMirror should handle any selections
          if (mutation.type === 'selection') {
              return false;
          }
          // try to prevent a bug on iOS and Android that will break node views on enter
          // this is because ProseMirror can’t preventDispatch on enter
          // this will lead to a re-render of the node view on enter
          // see: https://github.com/ueberdosis/tiptap/issues/1214
          // see: https://github.com/ueberdosis/tiptap/issues/2534
          if (this.dom.contains(mutation.target)
              && mutation.type === 'childList'
              && (isiOS() || isAndroid())
              && this.editor.isFocused) {
              const changedNodes = [
                  ...Array.from(mutation.addedNodes),
                  ...Array.from(mutation.removedNodes),
              ];
              // we’ll check if every changed node is contentEditable
              // to make sure it’s probably mutated by ProseMirror
              if (changedNodes.every(node => node.isContentEditable)) {
                  return false;
              }
          }
          // we will allow mutation contentDOM with attributes
          // so we can for example adding classes within our node view
          if (this.contentDOM === mutation.target && mutation.type === 'attributes') {
              return true;
          }
          // ProseMirror should handle any changes within contentDOM
          if (this.contentDOM.contains(mutation.target)) {
              return false;
          }
          return true;
      }
      /**
       * Update the attributes of the prosemirror node.
       */
      updateAttributes(attributes) {
          this.editor.commands.command(({ tr }) => {
              const pos = this.getPos();
              if (typeof pos !== 'number') {
                  return false;
              }
              tr.setNodeMarkup(pos, undefined, {
                  ...this.node.attrs,
                  ...attributes,
              });
              return true;
          });
      }
      /**
       * Delete the node.
       */
      deleteNode() {
          const from = this.getPos();
          if (typeof from !== 'number') {
              return;
          }
          const to = from + this.node.nodeSize;
          this.editor.commands.deleteRange({ from, to });
      }
  }

  /**
   * Build an paste rule that adds a mark when the
   * matched text is pasted into it.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
   */
  function markPasteRule(config) {
      return new PasteRule({
          find: config.find,
          handler: ({ state, range, match, pasteEvent, }) => {
              const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent);
              if (attributes === false || attributes === null) {
                  return null;
              }
              const { tr } = state;
              const captureGroup = match[match.length - 1];
              const fullMatch = match[0];
              let markEnd = range.to;
              if (captureGroup) {
                  const startSpaces = fullMatch.search(/\S/);
                  const textStart = range.from + fullMatch.indexOf(captureGroup);
                  const textEnd = textStart + captureGroup.length;
                  const excludedMarks = getMarksBetween(range.from, range.to, state.doc)
                      .filter(item => {
                      // @ts-ignore
                      const excluded = item.mark.type.excluded;
                      return excluded.find(type => type === config.type && type !== item.mark.type);
                  })
                      .filter(item => item.to > textStart);
                  if (excludedMarks.length) {
                      return null;
                  }
                  if (textEnd < range.to) {
                      tr.delete(textEnd, range.to);
                  }
                  if (textStart > range.from) {
                      tr.delete(range.from + startSpaces, textStart);
                  }
                  markEnd = range.from + startSpaces + captureGroup.length;
                  tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
                  tr.removeStoredMark(config.type);
              }
          },
      });
  }

  function canInsertNode(state$1, nodeType) {
      const { selection } = state$1;
      const { $from } = selection;
      // Special handling for NodeSelection
      if (selection instanceof state.NodeSelection) {
          const index = $from.index();
          const parent = $from.parent;
          // Can we replace the selected node with the horizontal rule?
          return parent.canReplaceWith(index, index + 1, nodeType);
      }
      // Default: check if we can insert at the current position
      let depth = $from.depth;
      while (depth >= 0) {
          const index = $from.index(depth);
          const parent = $from.node(depth);
          const match = parent.contentMatchAt(index);
          if (match.matchType(nodeType)) {
              return true;
          }
          depth -= 1;
      }
      return false;
  }

  // source: https://stackoverflow.com/a/6969486
  function escapeForRegEx(string) {
      return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
  }

  function isString(value) {
      return typeof value === 'string';
  }

  /**
   * Build an paste rule that adds a node when the
   * matched text is pasted into it.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
   */
  function nodePasteRule(config) {
      return new PasteRule({
          find: config.find,
          handler({ match, chain, range, pasteEvent, }) {
              const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent);
              const content = callOrReturn(config.getContent, undefined, attributes);
              if (attributes === false || attributes === null) {
                  return null;
              }
              const node = { type: config.type.name, attrs: attributes };
              if (content) {
                  node.content = content;
              }
              if (match.input) {
                  chain().deleteRange(range).insertContentAt(range.from, node);
              }
          },
      });
  }

  /**
   * Build an paste rule that replaces text when the
   * matched text is pasted into it.
   * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
   */
  function textPasteRule(config) {
      return new PasteRule({
          find: config.find,
          handler: ({ state, range, match }) => {
              let insert = config.replace;
              let start = range.from;
              const end = range.to;
              if (match[1]) {
                  const offset = match[0].lastIndexOf(match[1]);
                  insert += match[0].slice(offset + match[1].length);
                  start += offset;
                  const cutOff = start - end;
                  if (cutOff > 0) {
                      insert = match[0].slice(offset - cutOff, offset) + insert;
                      start = end;
                  }
              }
              state.tr.insertText(insert, start, end);
          },
      });
  }

  class Tracker {
      constructor(transaction) {
          this.transaction = transaction;
          this.currentStep = this.transaction.steps.length;
      }
      map(position) {
          let deleted = false;
          const mappedPosition = this.transaction.steps
              .slice(this.currentStep)
              .reduce((newPosition, step) => {
              const mapResult = step.getMap().mapResult(newPosition);
              if (mapResult.deleted) {
                  deleted = true;
              }
              return mapResult.pos;
          }, position);
          return {
              position: mappedPosition,
              deleted,
          };
      }
  }

  exports.CommandManager = CommandManager;
  exports.Editor = Editor;
  exports.Extension = Extension;
  exports.InputRule = InputRule;
  exports.Mark = Mark;
  exports.Node = Node;
  exports.NodePos = NodePos;
  exports.NodeView = NodeView;
  exports.PasteRule = PasteRule;
  exports.Tracker = Tracker;
  exports.callOrReturn = callOrReturn;
  exports.canInsertNode = canInsertNode;
  exports.combineTransactionSteps = combineTransactionSteps;
  exports.createChainableState = createChainableState;
  exports.createDocument = createDocument;
  exports.createNodeFromContent = createNodeFromContent;
  exports.createStyleTag = createStyleTag;
  exports.defaultBlockAt = defaultBlockAt;
  exports.deleteProps = deleteProps;
  exports.elementFromString = elementFromString;
  exports.escapeForRegEx = escapeForRegEx;
  exports.extensions = index;
  exports.findChildren = findChildren;
  exports.findChildrenInRange = findChildrenInRange;
  exports.findDuplicates = findDuplicates;
  exports.findParentNode = findParentNode;
  exports.findParentNodeClosestToPos = findParentNodeClosestToPos;
  exports.fromString = fromString;
  exports.generateHTML = generateHTML;
  exports.generateJSON = generateJSON;
  exports.generateText = generateText;
  exports.getAttributes = getAttributes;
  exports.getAttributesFromExtensions = getAttributesFromExtensions;
  exports.getChangedRanges = getChangedRanges;
  exports.getDebugJSON = getDebugJSON;
  exports.getExtensionField = getExtensionField;
  exports.getHTMLFromFragment = getHTMLFromFragment;
  exports.getMarkAttributes = getMarkAttributes;
  exports.getMarkRange = getMarkRange;
  exports.getMarkType = getMarkType;
  exports.getMarksBetween = getMarksBetween;
  exports.getNodeAtPosition = getNodeAtPosition;
  exports.getNodeAttributes = getNodeAttributes;
  exports.getNodeType = getNodeType;
  exports.getRenderedAttributes = getRenderedAttributes;
  exports.getSchema = getSchema;
  exports.getSchemaByResolvedExtensions = getSchemaByResolvedExtensions;
  exports.getSchemaTypeByName = getSchemaTypeByName;
  exports.getSchemaTypeNameByName = getSchemaTypeNameByName;
  exports.getSplittedAttributes = getSplittedAttributes;
  exports.getText = getText;
  exports.getTextBetween = getTextBetween;
  exports.getTextContentFromNodes = getTextContentFromNodes;
  exports.getTextSerializersFromSchema = getTextSerializersFromSchema;
  exports.injectExtensionAttributesToParseRule = injectExtensionAttributesToParseRule;
  exports.inputRulesPlugin = inputRulesPlugin;
  exports.isActive = isActive;
  exports.isAtEndOfNode = isAtEndOfNode;
  exports.isAtStartOfNode = isAtStartOfNode;
  exports.isEmptyObject = isEmptyObject;
  exports.isExtensionRulesEnabled = isExtensionRulesEnabled;
  exports.isFunction = isFunction;
  exports.isList = isList;
  exports.isMacOS = isMacOS;
  exports.isMarkActive = isMarkActive;
  exports.isNodeActive = isNodeActive;
  exports.isNodeEmpty = isNodeEmpty;
  exports.isNodeSelection = isNodeSelection;
  exports.isNumber = isNumber;
  exports.isPlainObject = isPlainObject;
  exports.isRegExp = isRegExp;
  exports.isString = isString;
  exports.isTextSelection = isTextSelection;
  exports.isiOS = isiOS;
  exports.markInputRule = markInputRule;
  exports.markPasteRule = markPasteRule;
  exports.mergeAttributes = mergeAttributes;
  exports.mergeDeep = mergeDeep;
  exports.minMax = minMax;
  exports.nodeInputRule = nodeInputRule;
  exports.nodePasteRule = nodePasteRule;
  exports.objectIncludes = objectIncludes;
  exports.pasteRulesPlugin = pasteRulesPlugin;
  exports.posToDOMRect = posToDOMRect;
  exports.removeDuplicates = removeDuplicates;
  exports.resolveFocusPosition = resolveFocusPosition;
  exports.rewriteUnknownContent = rewriteUnknownContent;
  exports.selectionToInsertionEnd = selectionToInsertionEnd;
  exports.splitExtensions = splitExtensions;
  exports.textInputRule = textInputRule;
  exports.textPasteRule = textPasteRule;
  exports.textblockTypeInputRule = textblockTypeInputRule;
  exports.wrappingInputRule = wrappingInputRule;

}));
//# sourceMappingURL=index.umd.js.map
