UNPKG

rhino-editor

Version:

A custom element wrapped rich text editor

2,408 lines 72.8 kB
import {
  RhinoChangeEvent
} from "./chunk-BYRYOV3A.js";
import {
  RhinoBlurEvent
} from "./chunk-RXTNV3CH.js";
import {
  InitializeEvent
} from "./chunk-TUFYCG5K.js";
import {
  FileAcceptEvent
} from "./chunk-CVO5J527.js";
import {
  BeforeInitializeEvent
} from "./chunk-PBNR35P2.js";
import {
  AddAttachmentEvent
} from "./chunk-CZRGOOEV.js";
import {
  SelectionChangeEvent
} from "./chunk-OVNNLQEY.js";
import {
  RhinoFocusEvent
} from "./chunk-CO3NBTSN.js";
import {
  editor_default
} from "./chunk-6LK3B3JM.js";
import {
  Dropcursor,
  Gapcursor,
  Link,
  RhinoStarterKit,
  TrailingNode,
  UndoRedo
} from "./chunk-77OKYKXL.js";
import {
  Strike
} from "./chunk-2PXEVWXN.js";
import {
  AttachmentRemoveEvent
} from "./chunk-3PWTYHXV.js";
import {
  Editor,
  Extension,
  Mark,
  Node3,
  canInsertNode,
  getNodeAtPosition,
  getNodeType,
  isAtEndOfNode,
  isAtStartOfNode,
  isNodeActive,
  isNodeSelection,
  markInputRule,
  markPasteRule,
  mergeAttributes,
  nodeInputRule,
  textblockTypeInputRule,
  wrappingInputRule
} from "./chunk-4CJ3F2XK.js";
import {
  DOMSerializer,
  NodeSelection,
  Plugin,
  PluginKey,
  Selection,
  TextSelection
} from "./chunk-XG3W5CFT.js";
import {
  AttachmentManager
} from "./chunk-T6DFG72M.js";
import {
  AttachmentUpload,
  AttachmentUploadCompleteEvent,
  AttachmentUploadStartEvent
} from "./chunk-O7ZA6FN4.js";
import {
  AttachmentEditor,
  BaseElement
} from "./chunk-IXOHWJ3R.js";
import {
  normalize
} from "./chunk-KS2J443B.js";
import {
  tipTapCoreStyles
} from "./chunk-TYRYT5SD.js";
import {
  x
} from "./chunk-6JX5B4GC.js";

// node_modules/.pnpm/@tiptap+core@3.4.4_@tiptap+pm@3.4.4/node_modules/@tiptap/core/dist/jsx-runtime/jsx-runtime.js
var h = (tag, attributes) => {
  if (tag === "slot") {
    return 0;
  }
  if (tag instanceof Function) {
    return tag(attributes);
  }
  const { children, ...rest } = attributes != null ? attributes : {};
  if (tag === "svg") {
    throw new Error("SVG elements are not supported in the JSX syntax, use the array syntax instead");
  }
  return [tag, rest, children];
};

// node_modules/.pnpm/@tiptap+extension-blockquote@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-blockquote/dist/index.js
var inputRegex = /^\s*>\s$/;
var Blockquote = Node3.create({
  name: "blockquote",
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  content: "block+",
  group: "block",
  defining: true,
  parseHTML() {
    return [{ tag: "blockquote" }];
  },
  renderHTML({ HTMLAttributes }) {
    return /* @__PURE__ */ h("blockquote", { ...mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), children: /* @__PURE__ */ h("slot", {}) });
  },
  addCommands() {
    return {
      setBlockquote: () => ({ commands }) => {
        return commands.wrapIn(this.name);
      },
      toggleBlockquote: () => ({ commands }) => {
        return commands.toggleWrap(this.name);
      },
      unsetBlockquote: () => ({ commands }) => {
        return commands.lift(this.name);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Shift-b": () => this.editor.commands.toggleBlockquote()
    };
  },
  addInputRules() {
    return [
      wrappingInputRule({
        find: inputRegex,
        type: this.type
      })
    ];
  }
});

// node_modules/.pnpm/@tiptap+extension-bold@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-bold/dist/index.js
var starInputRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))$/;
var starPasteRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))/g;
var underscoreInputRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))$/;
var underscorePasteRegex = /(?:^|\s)(__(?!\s+__)((?:[^_]+))__(?!\s+__))/g;
var Bold = Mark.create({
  name: "bold",
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  parseHTML() {
    return [
      {
        tag: "strong"
      },
      {
        tag: "b",
        getAttrs: (node) => node.style.fontWeight !== "normal" && null
      },
      {
        style: "font-weight=400",
        clearMark: (mark) => mark.type.name === this.name
      },
      {
        style: "font-weight",
        getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null
      }
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return /* @__PURE__ */ h("strong", { ...mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), children: /* @__PURE__ */ h("slot", {}) });
  },
  addCommands() {
    return {
      setBold: () => ({ commands }) => {
        return commands.setMark(this.name);
      },
      toggleBold: () => ({ commands }) => {
        return commands.toggleMark(this.name);
      },
      unsetBold: () => ({ commands }) => {
        return commands.unsetMark(this.name);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-b": () => this.editor.commands.toggleBold(),
      "Mod-B": () => this.editor.commands.toggleBold()
    };
  },
  addInputRules() {
    return [
      markInputRule({
        find: starInputRegex,
        type: this.type
      }),
      markInputRule({
        find: underscoreInputRegex,
        type: this.type
      })
    ];
  },
  addPasteRules() {
    return [
      markPasteRule({
        find: starPasteRegex,
        type: this.type
      }),
      markPasteRule({
        find: underscorePasteRegex,
        type: this.type
      })
    ];
  }
});

// node_modules/.pnpm/@tiptap+extension-code@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-code/dist/index.js
var inputRegex2 = /(^|[^`])`([^`]+)`(?!`)$/;
var pasteRegex = /(^|[^`])`([^`]+)`(?!`)/g;
var Code = Mark.create({
  name: "code",
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  excludes: "_",
  code: true,
  exitable: true,
  parseHTML() {
    return [{ tag: "code" }];
  },
  renderHTML({ HTMLAttributes }) {
    return ["code", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      setCode: () => ({ commands }) => {
        return commands.setMark(this.name);
      },
      toggleCode: () => ({ commands }) => {
        return commands.toggleMark(this.name);
      },
      unsetCode: () => ({ commands }) => {
        return commands.unsetMark(this.name);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-e": () => this.editor.commands.toggleCode()
    };
  },
  addInputRules() {
    return [
      markInputRule({
        find: inputRegex2,
        type: this.type
      })
    ];
  },
  addPasteRules() {
    return [
      markPasteRule({
        find: pasteRegex,
        type: this.type
      })
    ];
  }
});

// node_modules/.pnpm/@tiptap+extension-code-block@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4__@tiptap+pm@3.4.4/node_modules/@tiptap/extension-code-block/dist/index.js
var backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
var tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
var CodeBlock = Node3.create({
  name: "codeBlock",
  addOptions() {
    return {
      languageClassPrefix: "language-",
      exitOnTripleEnter: true,
      exitOnArrowDown: true,
      defaultLanguage: null,
      enableTabIndentation: false,
      tabSize: 4,
      HTMLAttributes: {}
    };
  },
  content: "text*",
  marks: "",
  group: "block",
  code: true,
  defining: true,
  addAttributes() {
    return {
      language: {
        default: this.options.defaultLanguage,
        parseHTML: (element) => {
          var _a;
          const { languageClassPrefix } = this.options;
          const classNames = [...((_a = element.firstElementChild) == null ? void 0 : _a.classList) || []];
          const languages = classNames.filter((className) => className.startsWith(languageClassPrefix)).map((className) => className.replace(languageClassPrefix, ""));
          const language = languages[0];
          if (!language) {
            return null;
          }
          return language;
        },
        rendered: false
      }
    };
  },
  parseHTML() {
    return [
      {
        tag: "pre",
        preserveWhitespace: "full"
      }
    ];
  },
  renderHTML({ node, HTMLAttributes }) {
    return [
      "pre",
      mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
      [
        "code",
        {
          class: node.attrs.language ? this.options.languageClassPrefix + node.attrs.language : null
        },
        0
      ]
    ];
  },
  addCommands() {
    return {
      setCodeBlock: (attributes) => ({ commands }) => {
        return commands.setNode(this.name, attributes);
      },
      toggleCodeBlock: (attributes) => ({ commands }) => {
        return commands.toggleNode(this.name, "paragraph", attributes);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(),
      // remove code block when at start of document or code block is empty
      Backspace: () => {
        const { empty, $anchor } = this.editor.state.selection;
        const isAtStart = $anchor.pos === 1;
        if (!empty || $anchor.parent.type.name !== this.name) {
          return false;
        }
        if (isAtStart || !$anchor.parent.textContent.length) {
          return this.editor.commands.clearNodes();
        }
        return false;
      },
      // handle tab indentation
      Tab: ({ editor }) => {
        if (!this.options.enableTabIndentation) {
          return false;
        }
        const { state } = editor;
        const { selection } = state;
        const { $from, empty } = selection;
        if ($from.parent.type !== this.type) {
          return false;
        }
        const indent = " ".repeat(this.options.tabSize);
        if (empty) {
          return editor.commands.insertContent(indent);
        }
        return editor.commands.command(({ tr }) => {
          const { from, to } = selection;
          const text = state.doc.textBetween(from, to, "\n", "\n");
          const lines = text.split("\n");
          const indentedText = lines.map((line) => indent + line).join("\n");
          tr.replaceWith(from, to, state.schema.text(indentedText));
          return true;
        });
      },
      // handle shift+tab reverse indentation
      "Shift-Tab": ({ editor }) => {
        if (!this.options.enableTabIndentation) {
          return false;
        }
        const { state } = editor;
        const { selection } = state;
        const { $from, empty } = selection;
        if ($from.parent.type !== this.type) {
          return false;
        }
        if (empty) {
          return editor.commands.command(({ tr }) => {
            var _a;
            const { pos } = $from;
            const codeBlockStart = $from.start();
            const codeBlockEnd = $from.end();
            const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, "\n", "\n");
            const lines = allText.split("\n");
            let currentLineIndex = 0;
            let charCount = 0;
            const relativeCursorPos = pos - codeBlockStart;
            for (let i = 0; i < lines.length; i += 1) {
              if (charCount + lines[i].length >= relativeCursorPos) {
                currentLineIndex = i;
                break;
              }
              charCount += lines[i].length + 1;
            }
            const currentLine = lines[currentLineIndex];
            const leadingSpaces = ((_a = currentLine.match(/^ */)) == null ? void 0 : _a[0]) || "";
            const spacesToRemove = Math.min(leadingSpaces.length, this.options.tabSize);
            if (spacesToRemove === 0) {
              return true;
            }
            let lineStartPos = codeBlockStart;
            for (let i = 0; i < currentLineIndex; i += 1) {
              lineStartPos += lines[i].length + 1;
            }
            tr.delete(lineStartPos, lineStartPos + spacesToRemove);
            const cursorPosInLine = pos - lineStartPos;
            if (cursorPosInLine <= spacesToRemove) {
              tr.setSelection(TextSelection.create(tr.doc, lineStartPos));
            }
            return true;
          });
        }
        return editor.commands.command(({ tr }) => {
          const { from, to } = selection;
          const text = state.doc.textBetween(from, to, "\n", "\n");
          const lines = text.split("\n");
          const reverseIndentText = lines.map((line) => {
            var _a;
            const leadingSpaces = ((_a = line.match(/^ */)) == null ? void 0 : _a[0]) || "";
            const spacesToRemove = Math.min(leadingSpaces.length, this.options.tabSize);
            return line.slice(spacesToRemove);
          }).join("\n");
          tr.replaceWith(from, to, state.schema.text(reverseIndentText));
          return true;
        });
      },
      // exit node on triple enter
      Enter: ({ editor }) => {
        if (!this.options.exitOnTripleEnter) {
          return false;
        }
        const { state } = editor;
        const { selection } = state;
        const { $from, empty } = selection;
        if (!empty || $from.parent.type !== this.type) {
          return false;
        }
        const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
        const endsWithDoubleNewline = $from.parent.textContent.endsWith("\n\n");
        if (!isAtEnd || !endsWithDoubleNewline) {
          return false;
        }
        return editor.chain().command(({ tr }) => {
          tr.delete($from.pos - 2, $from.pos);
          return true;
        }).exitCode().run();
      },
      // exit node on arrow down
      ArrowDown: ({ editor }) => {
        if (!this.options.exitOnArrowDown) {
          return false;
        }
        const { state } = editor;
        const { selection, doc } = state;
        const { $from, empty } = selection;
        if (!empty || $from.parent.type !== this.type) {
          return false;
        }
        const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
        if (!isAtEnd) {
          return false;
        }
        const after = $from.after();
        if (after === void 0) {
          return false;
        }
        const nodeAfter = doc.nodeAt(after);
        if (nodeAfter) {
          return editor.commands.command(({ tr }) => {
            tr.setSelection(Selection.near(doc.resolve(after)));
            return true;
          });
        }
        return editor.commands.exitCode();
      }
    };
  },
  addInputRules() {
    return [
      textblockTypeInputRule({
        find: backtickInputRegex,
        type: this.type,
        getAttributes: (match) => ({
          language: match[1]
        })
      }),
      textblockTypeInputRule({
        find: tildeInputRegex,
        type: this.type,
        getAttributes: (match) => ({
          language: match[1]
        })
      })
    ];
  },
  addProseMirrorPlugins() {
    return [
      // this plugin creates a code block for pasted content from VS Code
      // we can also detect the copied code language
      new Plugin({
        key: new PluginKey("codeBlockVSCodeHandler"),
        props: {
          handlePaste: (view, event) => {
            if (!event.clipboardData) {
              return false;
            }
            if (this.editor.isActive(this.type.name)) {
              return false;
            }
            const text = event.clipboardData.getData("text/plain");
            const vscode = event.clipboardData.getData("vscode-editor-data");
            const vscodeData = vscode ? JSON.parse(vscode) : void 0;
            const language = vscodeData == null ? void 0 : vscodeData.mode;
            if (!text || !language) {
              return false;
            }
            const { tr, schema } = view.state;
            const textNode = schema.text(text.replace(/\r\n?/g, "\n"));
            tr.replaceSelectionWith(this.type.create({ language }, textNode));
            if (tr.selection.$from.parent.type !== this.type) {
              tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
            }
            tr.setMeta("paste", true);
            view.dispatch(tr);
            return true;
          }
        }
      })
    ];
  }
});

// node_modules/.pnpm/@tiptap+extension-document@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-document/dist/index.js
var Document = Node3.create({
  name: "doc",
  topNode: true,
  content: "block+"
});

// node_modules/.pnpm/@tiptap+extension-hard-break@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-hard-break/dist/index.js
var HardBreak = Node3.create({
  name: "hardBreak",
  addOptions() {
    return {
      keepMarks: true,
      HTMLAttributes: {}
    };
  },
  inline: true,
  group: "inline",
  selectable: false,
  linebreakReplacement: true,
  parseHTML() {
    return [{ tag: "br" }];
  },
  renderHTML({ HTMLAttributes }) {
    return ["br", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];
  },
  renderText() {
    return "\n";
  },
  addCommands() {
    return {
      setHardBreak: () => ({ commands, chain, state, editor }) => {
        return commands.first([
          () => commands.exitCode(),
          () => commands.command(() => {
            const { selection, storedMarks } = state;
            if (selection.$from.parent.type.spec.isolating) {
              return false;
            }
            const { keepMarks } = this.options;
            const { splittableMarks } = editor.extensionManager;
            const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
            return chain().insertContent({ type: this.name }).command(({ tr, dispatch }) => {
              if (dispatch && marks && keepMarks) {
                const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
                tr.ensureMarks(filteredMarks);
              }
              return true;
            }).run();
          })
        ]);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Enter": () => this.editor.commands.setHardBreak(),
      "Shift-Enter": () => this.editor.commands.setHardBreak()
    };
  }
});

// node_modules/.pnpm/@tiptap+extension-heading@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-heading/dist/index.js
var Heading = Node3.create({
  name: "heading",
  addOptions() {
    return {
      levels: [1, 2, 3, 4, 5, 6],
      HTMLAttributes: {}
    };
  },
  content: "inline*",
  group: "block",
  defining: true,
  addAttributes() {
    return {
      level: {
        default: 1,
        rendered: false
      }
    };
  },
  parseHTML() {
    return this.options.levels.map((level) => ({
      tag: `h${level}`,
      attrs: { level }
    }));
  },
  renderHTML({ node, HTMLAttributes }) {
    const hasLevel = this.options.levels.includes(node.attrs.level);
    const level = hasLevel ? node.attrs.level : this.options.levels[0];
    return [`h${level}`, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      setHeading: (attributes) => ({ commands }) => {
        if (!this.options.levels.includes(attributes.level)) {
          return false;
        }
        return commands.setNode(this.name, attributes);
      },
      toggleHeading: (attributes) => ({ commands }) => {
        if (!this.options.levels.includes(attributes.level)) {
          return false;
        }
        return commands.toggleNode(this.name, "paragraph", attributes);
      }
    };
  },
  addKeyboardShortcuts() {
    return this.options.levels.reduce(
      (items, level) => ({
        ...items,
        ...{
          [`Mod-Alt-${level}`]: () => this.editor.commands.toggleHeading({ level })
        }
      }),
      {}
    );
  },
  addInputRules() {
    return this.options.levels.map((level) => {
      return textblockTypeInputRule({
        find: new RegExp(`^(#{${Math.min(...this.options.levels)},${level}})\\s$`),
        type: this.type,
        getAttributes: {
          level
        }
      });
    });
  }
});

// node_modules/.pnpm/@tiptap+extension-horizontal-rule@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4__@tiptap+pm@3.4.4/node_modules/@tiptap/extension-horizontal-rule/dist/index.js
var HorizontalRule = Node3.create({
  name: "horizontalRule",
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  group: "block",
  parseHTML() {
    return [{ tag: "hr" }];
  },
  renderHTML({ HTMLAttributes }) {
    return ["hr", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];
  },
  addCommands() {
    return {
      setHorizontalRule: () => ({ chain, state }) => {
        if (!canInsertNode(state, state.schema.nodes[this.name])) {
          return false;
        }
        const { selection } = state;
        const { $to: $originTo } = selection;
        const currentChain = chain();
        if (isNodeSelection(selection)) {
          currentChain.insertContentAt($originTo.pos, {
            type: this.name
          });
        } else {
          currentChain.insertContent({ type: this.name });
        }
        return currentChain.command(({ tr, dispatch }) => {
          var _a;
          if (dispatch) {
            const { $to } = tr.selection;
            const posAfter = $to.end();
            if ($to.nodeAfter) {
              if ($to.nodeAfter.isTextblock) {
                tr.setSelection(TextSelection.create(tr.doc, $to.pos + 1));
              } else if ($to.nodeAfter.isBlock) {
                tr.setSelection(NodeSelection.create(tr.doc, $to.pos));
              } else {
                tr.setSelection(TextSelection.create(tr.doc, $to.pos));
              }
            } else {
              const node = (_a = $to.parent.type.contentMatch.defaultType) == null ? void 0 : _a.create();
              if (node) {
                tr.insert(posAfter, node);
                tr.setSelection(TextSelection.create(tr.doc, posAfter + 1));
              }
            }
            tr.scrollIntoView();
          }
          return true;
        }).run();
      }
    };
  },
  addInputRules() {
    return [
      nodeInputRule({
        find: /^(?:---|—-|___\s|\*\*\*\s)$/,
        type: this.type
      })
    ];
  }
});

// node_modules/.pnpm/@tiptap+extension-italic@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-italic/dist/index.js
var starInputRegex2 = /(?:^|\s)(\*(?!\s+\*)((?:[^*]+))\*(?!\s+\*))$/;
var starPasteRegex2 = /(?:^|\s)(\*(?!\s+\*)((?:[^*]+))\*(?!\s+\*))/g;
var underscoreInputRegex2 = /(?:^|\s)(_(?!\s+_)((?:[^_]+))_(?!\s+_))$/;
var underscorePasteRegex2 = /(?:^|\s)(_(?!\s+_)((?:[^_]+))_(?!\s+_))/g;
var Italic = Mark.create({
  name: "italic",
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  parseHTML() {
    return [
      {
        tag: "em"
      },
      {
        tag: "i",
        getAttrs: (node) => node.style.fontStyle !== "normal" && null
      },
      {
        style: "font-style=normal",
        clearMark: (mark) => mark.type.name === this.name
      },
      {
        style: "font-style=italic"
      }
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["em", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      setItalic: () => ({ commands }) => {
        return commands.setMark(this.name);
      },
      toggleItalic: () => ({ commands }) => {
        return commands.toggleMark(this.name);
      },
      unsetItalic: () => ({ commands }) => {
        return commands.unsetMark(this.name);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-i": () => this.editor.commands.toggleItalic(),
      "Mod-I": () => this.editor.commands.toggleItalic()
    };
  },
  addInputRules() {
    return [
      markInputRule({
        find: starInputRegex2,
        type: this.type
      }),
      markInputRule({
        find: underscoreInputRegex2,
        type: this.type
      })
    ];
  },
  addPasteRules() {
    return [
      markPasteRule({
        find: starPasteRegex2,
        type: this.type
      }),
      markPasteRule({
        find: underscorePasteRegex2,
        type: this.type
      })
    ];
  }
});

// node_modules/.pnpm/@tiptap+extension-list@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4__@tiptap+pm@3.4.4/node_modules/@tiptap/extension-list/dist/index.js
var __defProp = Object.defineProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var ListItemName = "listItem";
var TextStyleName = "textStyle";
var bulletListInputRegex = /^\s*([-+*])\s$/;
var BulletList = Node3.create({
  name: "bulletList",
  addOptions() {
    return {
      itemTypeName: "listItem",
      HTMLAttributes: {},
      keepMarks: false,
      keepAttributes: false
    };
  },
  group: "block list",
  content() {
    return `${this.options.itemTypeName}+`;
  },
  parseHTML() {
    return [{ tag: "ul" }];
  },
  renderHTML({ HTMLAttributes }) {
    return ["ul", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      toggleBulletList: () => ({ commands, chain }) => {
        if (this.options.keepAttributes) {
          return chain().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(ListItemName, this.editor.getAttributes(TextStyleName)).run();
        }
        return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Shift-8": () => this.editor.commands.toggleBulletList()
    };
  },
  addInputRules() {
    let inputRule = wrappingInputRule({
      find: bulletListInputRegex,
      type: this.type
    });
    if (this.options.keepMarks || this.options.keepAttributes) {
      inputRule = wrappingInputRule({
        find: bulletListInputRegex,
        type: this.type,
        keepMarks: this.options.keepMarks,
        keepAttributes: this.options.keepAttributes,
        getAttributes: () => {
          return this.editor.getAttributes(TextStyleName);
        },
        editor: this.editor
      });
    }
    return [inputRule];
  }
});
var ListItem = Node3.create({
  name: "listItem",
  addOptions() {
    return {
      HTMLAttributes: {},
      bulletListTypeName: "bulletList",
      orderedListTypeName: "orderedList"
    };
  },
  content: "paragraph block*",
  defining: true,
  parseHTML() {
    return [
      {
        tag: "li"
      }
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["li", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addKeyboardShortcuts() {
    return {
      Enter: () => this.editor.commands.splitListItem(this.name),
      Tab: () => this.editor.commands.sinkListItem(this.name),
      "Shift-Tab": () => this.editor.commands.liftListItem(this.name)
    };
  }
});
var listHelpers_exports = {};
__export(listHelpers_exports, {
  findListItemPos: () => findListItemPos,
  getNextListDepth: () => getNextListDepth,
  handleBackspace: () => handleBackspace,
  handleDelete: () => handleDelete,
  hasListBefore: () => hasListBefore,
  hasListItemAfter: () => hasListItemAfter,
  hasListItemBefore: () => hasListItemBefore,
  listItemHasSubList: () => listItemHasSubList,
  nextListIsDeeper: () => nextListIsDeeper,
  nextListIsHigher: () => nextListIsHigher
});
var findListItemPos = (typeOrName, state) => {
  const { $from } = state.selection;
  const nodeType = getNodeType(typeOrName, state.schema);
  let currentNode = null;
  let currentDepth = $from.depth;
  let currentPos = $from.pos;
  let targetDepth = null;
  while (currentDepth > 0 && targetDepth === null) {
    currentNode = $from.node(currentDepth);
    if (currentNode.type === nodeType) {
      targetDepth = currentDepth;
    } else {
      currentDepth -= 1;
      currentPos -= 1;
    }
  }
  if (targetDepth === null) {
    return null;
  }
  return { $pos: state.doc.resolve(currentPos), depth: targetDepth };
};
var getNextListDepth = (typeOrName, state) => {
  const listItemPos = findListItemPos(typeOrName, state);
  if (!listItemPos) {
    return false;
  }
  const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4);
  return depth;
};
var hasListBefore = (editorState, name, parentListTypes) => {
  const { $anchor } = editorState.selection;
  const previousNodePos = Math.max(0, $anchor.pos - 2);
  const previousNode = editorState.doc.resolve(previousNodePos).node();
  if (!previousNode || !parentListTypes.includes(previousNode.type.name)) {
    return false;
  }
  return true;
};
var hasListItemBefore = (typeOrName, state) => {
  var _a;
  const { $anchor } = state.selection;
  const $targetPos = state.doc.resolve($anchor.pos - 2);
  if ($targetPos.index() === 0) {
    return false;
  }
  if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) {
    return false;
  }
  return true;
};
var listItemHasSubList = (typeOrName, state, node) => {
  if (!node) {
    return false;
  }
  const nodeType = getNodeType(typeOrName, state.schema);
  let hasSubList = false;
  node.descendants((child) => {
    if (child.type === nodeType) {
      hasSubList = true;
    }
  });
  return hasSubList;
};
var handleBackspace = (editor, name, parentListTypes) => {
  if (editor.commands.undoInputRule()) {
    return true;
  }
  if (editor.state.selection.from !== editor.state.selection.to) {
    return false;
  }
  if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
    const { $anchor } = editor.state.selection;
    const $listPos = editor.state.doc.resolve($anchor.before() - 1);
    const listDescendants = [];
    $listPos.node().descendants((node, pos) => {
      if (node.type.name === name) {
        listDescendants.push({ node, pos });
      }
    });
    const lastItem = listDescendants.at(-1);
    if (!lastItem) {
      return false;
    }
    const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1);
    return editor.chain().cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end()).joinForward().run();
  }
  if (!isNodeActive(editor.state, name)) {
    return false;
  }
  if (!isAtStartOfNode(editor.state)) {
    return false;
  }
  const listItemPos = findListItemPos(name, editor.state);
  if (!listItemPos) {
    return false;
  }
  const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2);
  const prevNode = $prev.node(listItemPos.depth);
  const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode);
  if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {
    return editor.commands.joinItemBackward();
  }
  return editor.chain().liftListItem(name).run();
};
var nextListIsDeeper = (typeOrName, state) => {
  const listDepth = getNextListDepth(typeOrName, state);
  const listItemPos = findListItemPos(typeOrName, state);
  if (!listItemPos || !listDepth) {
    return false;
  }
  if (listDepth > listItemPos.depth) {
    return true;
  }
  return false;
};
var nextListIsHigher = (typeOrName, state) => {
  const listDepth = getNextListDepth(typeOrName, state);
  const listItemPos = findListItemPos(typeOrName, state);
  if (!listItemPos || !listDepth) {
    return false;
  }
  if (listDepth < listItemPos.depth) {
    return true;
  }
  return false;
};
var handleDelete = (editor, name) => {
  if (!isNodeActive(editor.state, name)) {
    return false;
  }
  if (!isAtEndOfNode(editor.state, name)) {
    return false;
  }
  const { selection } = editor.state;
  const { $from, $to } = selection;
  if (!selection.empty && $from.sameParent($to)) {
    return false;
  }
  if (nextListIsDeeper(name, editor.state)) {
    return editor.chain().focus(editor.state.selection.from + 4).lift(name).joinBackward().run();
  }
  if (nextListIsHigher(name, editor.state)) {
    return editor.chain().joinForward().joinBackward().run();
  }
  return editor.commands.joinItemForward();
};
var hasListItemAfter = (typeOrName, state) => {
  var _a;
  const { $anchor } = state.selection;
  const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2);
  if ($targetPos.index() === $targetPos.parent.childCount - 1) {
    return false;
  }
  if (((_a = $targetPos.nodeAfter) == null ? void 0 : _a.type.name) !== typeOrName) {
    return false;
  }
  return true;
};
var ListKeymap = Extension.create({
  name: "listKeymap",
  addOptions() {
    return {
      listTypes: [
        {
          itemName: "listItem",
          wrapperNames: ["bulletList", "orderedList"]
        },
        {
          itemName: "taskItem",
          wrapperNames: ["taskList"]
        }
      ]
    };
  },
  addKeyboardShortcuts() {
    return {
      Delete: ({ editor }) => {
        let handled = false;
        this.options.listTypes.forEach(({ itemName }) => {
          if (editor.state.schema.nodes[itemName] === void 0) {
            return;
          }
          if (handleDelete(editor, itemName)) {
            handled = true;
          }
        });
        return handled;
      },
      "Mod-Delete": ({ editor }) => {
        let handled = false;
        this.options.listTypes.forEach(({ itemName }) => {
          if (editor.state.schema.nodes[itemName] === void 0) {
            return;
          }
          if (handleDelete(editor, itemName)) {
            handled = true;
          }
        });
        return handled;
      },
      Backspace: ({ editor }) => {
        let handled = false;
        this.options.listTypes.forEach(({ itemName, wrapperNames }) => {
          if (editor.state.schema.nodes[itemName] === void 0) {
            return;
          }
          if (handleBackspace(editor, itemName, wrapperNames)) {
            handled = true;
          }
        });
        return handled;
      },
      "Mod-Backspace": ({ editor }) => {
        let handled = false;
        this.options.listTypes.forEach(({ itemName, wrapperNames }) => {
          if (editor.state.schema.nodes[itemName] === void 0) {
            return;
          }
          if (handleBackspace(editor, itemName, wrapperNames)) {
            handled = true;
          }
        });
        return handled;
      }
    };
  }
});
var ListItemName2 = "listItem";
var TextStyleName2 = "textStyle";
var orderedListInputRegex = /^(\d+)\.\s$/;
var OrderedList = Node3.create({
  name: "orderedList",
  addOptions() {
    return {
      itemTypeName: "listItem",
      HTMLAttributes: {},
      keepMarks: false,
      keepAttributes: false
    };
  },
  group: "block list",
  content() {
    return `${this.options.itemTypeName}+`;
  },
  addAttributes() {
    return {
      start: {
        default: 1,
        parseHTML: (element) => {
          return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
        }
      },
      type: {
        default: null,
        parseHTML: (element) => element.getAttribute("type")
      }
    };
  },
  parseHTML() {
    return [
      {
        tag: "ol"
      }
    ];
  },
  renderHTML({ HTMLAttributes }) {
    const { start, ...attributesWithoutStart } = HTMLAttributes;
    return start === 1 ? ["ol", mergeAttributes(this.options.HTMLAttributes, attributesWithoutStart), 0] : ["ol", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      toggleOrderedList: () => ({ commands, chain }) => {
        if (this.options.keepAttributes) {
          return chain().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(ListItemName2, this.editor.getAttributes(TextStyleName2)).run();
        }
        return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Shift-7": () => this.editor.commands.toggleOrderedList()
    };
  },
  addInputRules() {
    let inputRule = wrappingInputRule({
      find: orderedListInputRegex,
      type: this.type,
      getAttributes: (match) => ({ start: +match[1] }),
      joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1]
    });
    if (this.options.keepMarks || this.options.keepAttributes) {
      inputRule = wrappingInputRule({
        find: orderedListInputRegex,
        type: this.type,
        keepMarks: this.options.keepMarks,
        keepAttributes: this.options.keepAttributes,
        getAttributes: (match) => ({ start: +match[1], ...this.editor.getAttributes(TextStyleName2) }),
        joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1],
        editor: this.editor
      });
    }
    return [inputRule];
  }
});
var inputRegex3 = /^\s*(\[([( |x])?\])\s$/;
var TaskItem = Node3.create({
  name: "taskItem",
  addOptions() {
    return {
      nested: false,
      HTMLAttributes: {},
      taskListTypeName: "taskList",
      a11y: void 0
    };
  },
  content() {
    return this.options.nested ? "paragraph block*" : "paragraph+";
  },
  defining: true,
  addAttributes() {
    return {
      checked: {
        default: false,
        keepOnSplit: false,
        parseHTML: (element) => {
          const dataChecked = element.getAttribute("data-checked");
          return dataChecked === "" || dataChecked === "true";
        },
        renderHTML: (attributes) => ({
          "data-checked": attributes.checked
        })
      }
    };
  },
  parseHTML() {
    return [
      {
        tag: `li[data-type="${this.name}"]`,
        priority: 51
      }
    ];
  },
  renderHTML({ node, HTMLAttributes }) {
    return [
      "li",
      mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
        "data-type": this.name
      }),
      [
        "label",
        [
          "input",
          {
            type: "checkbox",
            checked: node.attrs.checked ? "checked" : null
          }
        ],
        ["span"]
      ],
      ["div", 0]
    ];
  },
  addKeyboardShortcuts() {
    const shortcuts = {
      Enter: () => this.editor.commands.splitListItem(this.name),
      "Shift-Tab": () => this.editor.commands.liftListItem(this.name)
    };
    if (!this.options.nested) {
      return shortcuts;
    }
    return {
      ...shortcuts,
      Tab: () => this.editor.commands.sinkListItem(this.name)
    };
  },
  addNodeView() {
    return ({ node, HTMLAttributes, getPos, editor }) => {
      const listItem = document.createElement("li");
      const checkboxWrapper = document.createElement("label");
      const checkboxStyler = document.createElement("span");
      const checkbox = document.createElement("input");
      const content = document.createElement("div");
      const updateA11Y = (currentNode) => {
        var _a, _b;
        checkbox.ariaLabel = ((_b = (_a = this.options.a11y) == null ? void 0 : _a.checkboxLabel) == null ? void 0 : _b.call(_a, currentNode, checkbox.checked)) || `Task item checkbox for ${currentNode.textContent || "empty task item"}`;
      };
      updateA11Y(node);
      checkboxWrapper.contentEditable = "false";
      checkbox.type = "checkbox";
      checkbox.addEventListener("mousedown", (event) => event.preventDefault());
      checkbox.addEventListener("change", (event) => {
        if (!editor.isEditable && !this.options.onReadOnlyChecked) {
          checkbox.checked = !checkbox.checked;
          return;
        }
        const { checked } = event.target;
        if (editor.isEditable && typeof getPos === "function") {
          editor.chain().focus(void 0, { scrollIntoView: false }).command(({ tr }) => {
            const position = getPos();
            if (typeof position !== "number") {
              return false;
            }
            const currentNode = tr.doc.nodeAt(position);
            tr.setNodeMarkup(position, void 0, {
              ...currentNode == null ? void 0 : currentNode.attrs,
              checked
            });
            return true;
          }).run();
        }
        if (!editor.isEditable && this.options.onReadOnlyChecked) {
          if (!this.options.onReadOnlyChecked(node, checked)) {
            checkbox.checked = !checkbox.checked;
          }
        }
      });
      Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => {
        listItem.setAttribute(key, value);
      });
      listItem.dataset.checked = node.attrs.checked;
      checkbox.checked = node.attrs.checked;
      checkboxWrapper.append(checkbox, checkboxStyler);
      listItem.append(checkboxWrapper, content);
      Object.entries(HTMLAttributes).forEach(([key, value]) => {
        listItem.setAttribute(key, value);
      });
      return {
        dom: listItem,
        contentDOM: content,
        update: (updatedNode) => {
          if (updatedNode.type !== this.type) {
            return false;
          }
          listItem.dataset.checked = updatedNode.attrs.checked;
          checkbox.checked = updatedNode.attrs.checked;
          updateA11Y(updatedNode);
          return true;
        }
      };
    };
  },
  addInputRules() {
    return [
      wrappingInputRule({
        find: inputRegex3,
        type: this.type,
        getAttributes: (match) => ({
          checked: match[match.length - 1] === "x"
        })
      })
    ];
  }
});
var TaskList = Node3.create({
  name: "taskList",
  addOptions() {
    return {
      itemTypeName: "taskItem",
      HTMLAttributes: {}
    };
  },
  group: "block list",
  content() {
    return `${this.options.itemTypeName}+`;
  },
  parseHTML() {
    return [
      {
        tag: `ul[data-type="${this.name}"]`,
        priority: 51
      }
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["ul", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0];
  },
  addCommands() {
    return {
      toggleTaskList: () => ({ commands }) => {
        return commands.toggleList(this.name, this.options.itemTypeName);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Shift-9": () => this.editor.commands.toggleTaskList()
    };
  }
});
var ListKit = Extension.create({
  name: "listKit",
  addExtensions() {
    const extensions = [];
    if (this.options.bulletList !== false) {
      extensions.push(BulletList.configure(this.options.bulletList));
    }
    if (this.options.listItem !== false) {
      extensions.push(ListItem.configure(this.options.listItem));
    }
    if (this.options.listKeymap !== false) {
      extensions.push(ListKeymap.configure(this.options.listKeymap));
    }
    if (this.options.orderedList !== false) {
      extensions.push(OrderedList.configure(this.options.orderedList));
    }
    if (this.options.taskItem !== false) {
      extensions.push(TaskItem.configure(this.options.taskItem));
    }
    if (this.options.taskList !== false) {
      extensions.push(TaskList.configure(this.options.taskList));
    }
    return extensions;
  }
});

// node_modules/.pnpm/@tiptap+extension-paragraph@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-paragraph/dist/index.js
var Paragraph = Node3.create({
  name: "paragraph",
  priority: 1e3,
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  group: "block",
  content: "inline*",
  parseHTML() {
    return [{ tag: "p" }];
  },
  renderHTML({ HTMLAttributes }) {
    return ["p", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      setParagraph: () => ({ commands }) => {
        return commands.setNode(this.name);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-Alt-0": () => this.editor.commands.setParagraph()
    };
  }
});

// node_modules/.pnpm/@tiptap+extension-text@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-text/dist/index.js
var Text = Node3.create({
  name: "text",
  group: "inline"
});

// node_modules/.pnpm/@tiptap+extension-underline@3.4.4_@tiptap+core@3.4.4_@tiptap+pm@3.4.4_/node_modules/@tiptap/extension-underline/dist/index.js
var Underline = Mark.create({
  name: "underline",
  addOptions() {
    return {
      HTMLAttributes: {}
    };
  },
  parseHTML() {
    return [
      {
        tag: "u"
      },
      {
        style: "text-decoration",
        consuming: false,
        getAttrs: (style) => style.includes("underline") ? {} : false
      }
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["u", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
  },
  addCommands() {
    return {
      setUnderline: () => ({ commands }) => {
        return commands.setMark(this.name);
      },
      toggleUnderline: () => ({ commands }) => {
        return commands.toggleMark(this.name);
      },
      unsetUnderline: () => ({ commands }) => {
        return commands.unsetMark(this.name);
      }
    };
  },
  addKeyboardShortcuts() {
    return {
      "Mod-u": () => this.editor.commands.toggleUnderline(),
      "Mod-U": () => this.editor.commands.toggleUnderline()
    };
  }
});

// node_modules/.pnpm/@tiptap+starter-kit@3.4.4/node_modules/@tiptap/starter-kit/dist/index.js
var StarterKit = Extension.create({
  name: "starterKit",
  addExtensions() {
    var _a, _b, _c, _d;
    const extensions = [];
    if (this.options.bold !== false) {
      extensions.push(Bold.configure(this.options.bold));
    }
    if (this.options.blockquote !== false) {
      extensions.push(Blockquote.configure(this.options.blockquote));
    }
    if (this.options.bulletList !== false) {
      extensions.push(BulletList.configure(this.options.bulletList));
    }
    if (this.options.code !== false) {
      extensions.push(Code.configure(this.options.code));
    }
    if (this.options.codeBlock !== false) {
      extensions.push(CodeBlock.configure(this.options.codeBlock));
    }
    if (this.options.document !== false) {
      extensions.push(Document.configure(this.options.document));
    }
    if (this.options.dropcursor !== false) {
      extensions.push(Dropcursor.configure(this.options.dropcursor));
    }
    if (this.options.gapcursor !== false) {
      extensions.push(Gapcursor.configure(this.options.gapcursor));
    }
    if (this.options.hardBreak !== false) {
      extensions.push(HardBreak.configure(this.options.hardBreak));
    }
    if (this.options.heading !== false) {
      extensions.push(Heading.configure(this.options.heading));
    }
    if (this.options.undoRedo !== false) {
      extensions.push(UndoRedo.configure(this.options.undoRedo));
    }
    if (this.options.horizontalRule !== false) {
      extensions.push(HorizontalRule.configure(this.options.horizontalRule));
    }
    if (this.options.italic !== false) {
      extensions.push(Italic.configure(this.options.italic));
    }
    if (this.options.listItem !== false) {
      extensions.push(ListItem.configure(this.options.listItem));
    }
    if (this.options.listKeymap !== false) {
      extensions.push(ListKeymap.configure((_a = this.options) == null ? void 0 : _a.listKeymap));
    }
    if (this.options.link !== false) {
      extensions.push(Link.configure((_b = this.options) == null ? void 0 : _b.link));
    }
    if (this.options.orderedList !== false) {
      extensions.push(OrderedList.configure(this.options.orderedList));
    }
    if (this.options.paragraph !== false) {
      extensions.push(Paragraph.configure(this.options.paragraph));
    }
    if (this.options.strike !== false) {
      extensions.push(Strike.configure(this.options.strike));
    }
    if (this.options.text !== false) {
      extensions.push(Text.configure(this.options.text));
    }
    if (this.options.underline !== false) {
      extensions.push(Underline.configure((_c = this.options) == null ? void 0 : _c.underline));
    }
    if (this.options.trailingNode !== false) {
      extensions.push(TrailingNode.configure((_d = this.options) == null ? void 0 : _d.trailingNode));
    }
    return extensions;
  }
});
var index_default = StarterKit;

// src/exports/elements/tip-tap-editor-base.ts
var NON_BREAKING_SPACE = "\xA0";
var TipTapEditorBase = class extends BaseElement {
  constructor() {
    super();
    // Instance
    /**
     * Whether or not the editor should be editable.
     *
     * NOTE: a user can change this in the browser dev tools, don't rely on this to prevent
     * users from editing and attempting to save the document.
     */
    this.readonly = false;
    /**
     * Prevents premature rebuilds.
     */
    this.hasInitialized = false;
    /**
     * An array of "AttachmentUploads" added via direct upload. Check this for any attachment uploads that have not completed.
     */
    this.pendingAttachments = [];
    /**
     * JSON or HTML serializer used for determining the string to write to the hidden input.
     */
    this.serializer = "html";
    /** Comma separated string passed to the attach-files input. */
    this.accept = "*";
    this.starterKitOptions = {
      // We don't use the native strike since it requires configuring ActionText.
      strike: false,
      link: false,
      rhinoLink: {
        openOnClick: false
      }
    };
    /**
     * This will be concatenated onto RhinoStarterKit and StarterKit extensions.
     */
    this.extensions = [];
    /**
     * When the `defer-initialize` attribute is present, it will wait to start the TipTap editor until the attribute has been removed.
     */
    this.deferInitialize = false;
    /**
     * @internal
     */
    this.__initialAttributes = {};
    /**
     * @internal
     */
    this.__hasRendered = false;
    this.__initializationPromise__ = null;
    this.__initializationResolver__ = null;
    /**
     * Used for determining how to handle uploads.
     *   Override this for substituting your own
     *   direct upload functionality.
     */
    this.handleAttachment = (event) => {
      setTimeout(() => {
        if (event.defaultPrevented) {
          return;
        }
        const { attachment, target } = event;
        if (target instanceof HTMLElement && attachment.file) {
          const upload = new AttachmentUpload(attachment, target);
          upload.start();
        }
      });
    };
    /** Override this to prevent specific file types from being uploaded. */
    this.handleFileAccept = (_event) => {
    };
    this.handleDropFile = (_view, event, _slice, moved) => {
      if (!(event instanceof DragEvent)) return false;
      if (moved) return false;
      return this.handleNativeDrop(event);
    };
    this.handlePaste = async (event) => {
      if (this.editor == null) return;
      if (event == null) return;
      const { clipboardData } = event;
      if (clipboardData == null) return;
      let string = false;
      const hasFiles = clipboardData.files?.length > 0;
      let attachments = [];
      if (hasFiles) {
        attachments = await this.handleFiles(clipboardData.files);
      } else {
        let dataType = "text/plain";
        if (clipboardData.types.includes("text/html")) {
          dataType = "text/html";
        }
        string = clipboardData.getData(dataType);
      }
      setTimeout(async () => {
        if (event.defaultPrevented || event.originalPasteEvent.defaultPrevented) {
          return;
        }
        if (string !== false) {
          this.editor?.chain().focus().insertContent(string).run();
          return;
        }
        if (attachments.length > 0) {
          this.editor?.chain().focus().setAttachment(attachments).run();
          return;
        }
      });
    };
    this.__handleCreate = () => {
      this.requestUpdate();
    };
    this.__handleUpdate = () => {
      this.requestUpdate();
      if (!this.hasInitialized) {
        return;
      }
      this.updateInputElementValue();
      this.dispatchEvent(new RhinoChangeEvent());
    };
    this.__handleFocus = () => {
      this.dispatchEvent(new RhinoFocusEvent());
      this.requestUpdate();
    };
    this.__handleBlur = () => {
      this.updateInputElementValue();
      this.requestUpdate();
      this.dispatchEvent(new RhinoBlurEvent());
    };
    this.__handleSelectionUpdate = ({
      transaction
    }) => {
      this.requestUpdate();
      this.dispatchEvent(new SelectionChangeEvent({ transaction }));
    };
    this.__handleTransaction = () => {
      this.requestUpdate();
    };
    this.__addPendingAttachment = this.__addPendingAttachment.bind(this);
    this.__removePendingAttachment = this.__removePendingAttachment.bind(this);
    this.registerDependencies();
    this.addEventListener(AddAttachmentEvent.eventName, this.handleAttachment);
    this.addEventListener(
      AttachmentUploadStartEvent.eventName,
      this.__addPendingAttachment
    );
    this.addEventListener(
      AttachmentUploadCompleteEvent.eventName,
      this.__removePendingAttachment
    );
    this.addEventListener(
      AttachmentRemoveEvent.eventName,
      this.__removePendingAttachment
    );
    this.addEventListener("drop", this.handleNativeDrop);
    this.addEventListener("rhino-paste", this.handlePaste);
    this.addEventListener("rhino-file-accept", this.handleFileAccept);
  }
  static get styles() {
    return [normalize, tipTapCoreStyles, editor_default];
  }
  static get properties() {
    return {
      // Attributes
      readonly: { type: Boolean, reflect: true },
      input: { reflect: true },
      class: { reflect: true },
      accept: { reflect: true },
      serializer: { reflect: true },
      deferInitialize: {
        type: Boolean,
        attribute: "defer-initialize",
        reflect: true
      },
      // Properties
      editor: { state: true },
      editorElement: { state: true },
      starterKitOptions: { state: true },
      extensions: { state: true }
    };
  }
  __getInitialAttributes() {
    if (this.__hasRendered) return;
    const slottedEditor = this.slottedEditor;
    if (slottedEditor) {
      this.__initialAttributes = {};
      [...slottedEditor.attributes].forEach((attr) => {
        const { nodeName, nodeValue } = attr;
        if (nodeName && nodeValue != null) {
          this.__initialAttributes[nodeName] = nodeValue;
        }
      });
    }
    this.__hasRendered = true;
  }
  /**
   * Reset mechanism. This is called on first connect, and called anytime extensions,
   * or editor options get modified to make sure we have a fresh instance.
   */
  rebuildEditor() {
    if (!this.hasInitialized) return;
    const editors = this.querySelectorAll("[slot='editor']");
    this.__getInitialAttributes();
    if (this.editor) {
      this.editor.destroy();
    }
    editors.forEach((el) => {
      el.editor?.destroy();
      el.remove();
    });
    this.editor = this.__setupEditor(this);
    this.__bindEditorListeners();
    this.editorElement = this.querySelector(".ProseMirror");
    Object.entries(this.__initialAttributes)?.forEach(
      ([attrName, attrValue]) => {
        if (attrName === "class") {
          this.editorElement?.classList.add(...attrValue.split(" "));
          return;
        }
        this.editorElement?.setAttribute(attrName, attrValue);
      }
    );
    this.editorElement?.setAttribute("slot", "editor");
    this.editorElement?.classList.add("trix-content");
    this.editorElement?.setAttribute("tabindex", "0");
    this.editorElement?.setAttribute("role", "textbox");
    this.requestUpdate();
  }
  /**
   * Grabs HTML content based on a given range. If no range is given, it will return the contents
   *   of the current editor selection. If the current selection is empty, it will return an empty string.
   * @param from - The start of the selection
   * @param to - The end of the selection
   * @example Getting the HTML content of the current selection
   *    const rhinoEditor = document.querySelector("rhino-editor")
   *    rhinoEditor.getHTMLContentFromRange()
   *
   * @example Getting the HTML content of node range
   *    const rhinoEditor = document.querySelector("rhino-editor")
   *    rhinoEditor.getHTMLContentFromRange(0, 50)
   *
   * @example Getting the HTML content and falling back to entire editor HTML
   *    const rhinoEditor = document.querySelector("rhino-editor")
   *    let html = rhinoEditor.getHTMLContentFromRange()
   *    if (!html) {
   *       html = rhinoEditor.getHTML()
   *    }
   */
  getHTMLContentFromRange(from, to) {
    const editor = this.editor;
    if (!editor) return "";
    let empty;
    if (!from && !to) {
      const currentSelection = editor.state.selection;
      from = currentSelection.from;
      to = currentSelection.to;
    }
    if (empty) {
      return "";
    }
    if (from == null) {
      return "";
    }
    if (to == null) {
      return "";
    }
    const tempScript = document.createElement("div");
    const contentSlice = editor.view.state.doc.slice(from, to);
    const fragment = contentSlice.content;
    const domFragment = DOMSerializer.fromSchema(
      editor.schema
    ).serializeFragment(fragment);
    tempScript.append(domFragment);
    tempScript.querySelectorAll(":scope > p").forEach((p) => {
      preserveSignificantWhiteSpaceForElement(p);
    });
    return tempScript.innerHTML;
  }
  /**
   * Grabs plain text representation based on a given range. If no parameters are given, it will return the contents
   *   of the current selection. If the current selection is empty, it will return an empty string.
   * @param from - The start of the selection
   * @param to - The end of the selection
   * @example Getting the Text content of the current selection
   *    const rhinoEditor = document.querySelector("rhino-editor")
   *    rhinoEditor.getTextContentFromRange()
   *
   * @example Getting the Text content of node range
   *    const rhinoEditor = document.querySelector("rhino-editor")
   *    rhinoEditor.getTextContentFromRange(0, 50)
   *
   * @example Getting the Text content and falling back to entire editor Text
   *    const rhinoEditor = document.querySelector("rhino-editor")
   *    let text = rhinoEditor.getTextContentFromRange()
   *    if (!text) {
   *       text = rhinoEditor.editor.getText()
   *    }
   */
  getTextContentFromRange(from, to) {
    const editor = this.editor;
    if (!editor) {
      return "";
    }
    let empty;
    if (!from && !to) {
      const selection = editor.state.selection;
      from = selection.from;
      to = selection.to;
      empty = selection.empty;
    }
    if (empty) {
      return "";
    }
    if (from == null) {
      return "";
    }
    if (to == null) {
      return "";
    }
    return editor.state.doc.textBetween(from, to, " ");
  }
  willUpdate(changedProperties) {
    if (changedProperties.has("deferInitialize") && !this.deferInitialize) {
      this.startEditor();
    }
    if (changedProperties.has("class")) {
      this.classList.add("rhino-editor");
    }
    super.willUpdate(changedProperties);
  }
  updated(changedProperties) {
    if (!this.hasInitialized) {
      return super.updated(changedProperties);
    }
    if (changedProperties.has("readonly")) {
      this.editor?.setEditable(!this.readonly);
    }
    if (changedProperties.has("extensions") || changedProperties.has("serializer") || changedProperties.has("starterKitOptions") || changedProperties.has("translations")) {
      this.rebuildEditor();
    }
    if (changedProperties.has("serializer")) {
      this.updateInputElementValue();
    }
    super.updated(changedProperties);
    this.dispatchEvent(
      new Event("rhino-update", {
        bubbles: true,
        composed: true,
        cancelable: false
      })
    );
  }
  /** Used for registering things like <role-toolbar>, <role-tooltip>, <rhino-attachment-editor> */
  registerDependencies() {
    [AttachmentEditor].forEach((el) => el.define());
  }
  get slottedEditor() {
    return this.querySelector("[slot='editor']");
  }
  /**
   * @private
   */
  __addPendingAttachment(e) {
    this.pendingAttachments.push(e.attachmentUpload);
  }
  /**
   * @private
   */
  __removePendingAttachment(e) {
    const index = this.pendingAttachments.findIndex((attachment) => {
      if ("attachmentUpload" in e) {
        return attachment === e.attachmentUpload;
      }
      if ("attachment" in e) {
        return attachment.attachment.attachmentId === e.attachment.attachmentId;
      }
      return false;
    });
    if (index > -1) {
      this.pendingAttachments.splice(index, 1);
    }
  }
  async connectedCallback() {
    super.connectedCallback();
    this.__setupInitialization__();
    if (this.editor) {
      this.__unBindEditorListeners();
    }
    this.classList.add("rhino-editor");
    if (!this.deferInitialize) {
      this.startEditor();
    }
  }
  async startEditor() {
    await this.updateComplete;
    setTimeout(() => {
      this.dispatchEvent(new BeforeInitializeEvent());
      setTimeout(async () => {
        await this.updateComplete;
        this.hasInitialized = true;
        this.rebuildEditor();
        this.dispatchEvent(new InitializeEvent());
        this.__initializationResolver__?.();
        await this.__initializationPromise__;
        this.updateInputElementValue();
      });
    });
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this.editor?.destroy();
    this.hasInitialized = false;
    this.__initializationPromise__ = null;
    this.__initializationResolver__ = null;
  }
  __setupInitialization__() {
    if (!this.__initializationPromise__) {
      this.__initializationPromise__ = new Promise((resolve) => {
        this.__initializationResolver__ = resolve;
      });
    }
  }
  get initializationComplete() {
    this.__setupInitialization__();
    return this.__initializationPromise__;
  }
  addExtensions(...extensions) {
    let ary = [];
    extensions.forEach((ext) => {
      if (Array.isArray(ext)) {
        ary = ary.concat(ext.flat(1));
        return;
      }
      ary.push(ext);
    });
    const existingExtensions = this.extensions.map((ext) => ext.name);
    ary = ary.filter((ext) => {
      return !existingExtensions.includes(ext.name);
    });
    this.extensions = this.extensions.concat(ary);
  }
  disableStarterKitOptions(...options) {
    const disabledStarterKitOptions = {};
    options.forEach((ext) => {
      if (Array.isArray(ext)) {
        ext.flat(1).forEach((str) => disabledStarterKitOptions[str] = false);
        return;
      }
      disabledStarterKitOptions[ext] = false;
    });
    this.starterKitOptions = {
      ...this.starterKitOptions,
      ...disabledStarterKitOptions
    };
  }
  /**
   * Extend this to provide your own options, or override existing options.
   * The "element" is where the editor will be initialized.
   * This will be merged
   *   @example
   *    class ExtendedRhinoEditor extends TipTapEditor {
   *      editorOptions (_element: Element) {
   *        return {
   *          autofocus: true
   *        }
   *      }
   *    }
   *
   */
  editorOptions(_element) {
    return {};
  }
  /**
   * Finds the <input> element in the light dom and updates it with the value of `#serialize()`
   */
  updateInputElementValue() {
    if (this.inputElement != null && this.editor != null && !this.readonly) {
      this.inputElement.value = this.serialize();
    }
  }
  /**
   * Function called when grabbing the content of the editor. Currently supports JSON or HTML.
   */
  serialize() {
    const editor = this.editor;
    if (editor == null) return "";
    if (this.serializer?.toLowerCase() === "json") {
      return JSON.stringify(editor.getJSON());
    }
    return this.getHTMLAndPreserveSignificantWhiteSpace();
  }
  // compareAttributes (el1: Element, el2: Element) {
  //   function toObject (el: Element) {
  //     const obj = {} as Record<string, any>;
  //     ;[...el.attributes].forEach((attr) => {
  //       let val = attr.value
  //       let name = attr.name
  //       if (val.startsWith("{")) {
  //         try {
  //           val = JSON.parse(val)
  //         } catch (_e) {}
  //       }
  //       obj[name] = val
  //     })
  //     return obj
  //   }
  //   const obj1 = toObject(el1)
  //   const obj2 = toObject(el2)
  //   console.log({ obj1, obj2 })
  // }
  /**
   * @override
   * Apparently this is a native dom method?
   */
  getHTML() {
    const editor = this.editor;
    if (!editor) {
      return "";
    }
    return this.getHTMLAndPreserveSignificantWhiteSpace();
  }
  /**
   * Searches for the <input> element in the light dom to write the HTML or JSON to.
   */
  get inputElement() {
    if (!this.input) return void 0;
    const rootNode = this.getRootNode() || document;
    const el = rootNode.querySelector(
      `#${this.input}`
    );
    return el;
  }
  async handleFiles(files) {
    if (this.editor == null) return [];
    if (files == null) return [];
    return new Promise((resolve, _reject) => {
      const fileAcceptEvents = [...files].map((file) => {
        const event = new FileAcceptEvent(file);
        this.dispatchEvent(event);
        return event;
      });
      const allowedFiles = [];
      for (let i = 0; i < fileAcceptEvents.length; i++) {
        const event = fileAcceptEvents[i];
        if (event.defaultPrevented) {
          continue;
        }
        allowedFiles.push(event.file);
      }
      const attachments = this.transformFilesToAttachments(allowedFiles);
      if (attachments == null || attachments.length <= 0) return;
      attachments.forEach((attachment) => {
        this.dispatchEvent(new AddAttachmentEvent(attachment));
      });
      resolve(attachments);
    });
  }
  /**
   * Handles dropped files on the component, but not on the prosemirror instance.
   */
  handleNativeDrop(event) {
    if (this.editor == null) return false;
    if (event == null) return false;
    const { dataTransfer } = event;
    if (dataTransfer == null) return false;
    if (dataTransfer.files.length <= 0) return false;
    if (event.defaultPrevented) return false;
    event.preventDefault();
    this.handleFiles(dataTransfer.files).then((attachments) => {
      this.editor?.chain().focus().setAttachmentAtCoords(attachments, {
        top: event.clientY,
        left: event.clientX
      }).run();
    });
    return true;
  }
  transformFilesToAttachments(files) {
    if (this.editor == null) return;
    if (files == null || files.length === 0) return;
    const attachments = [];
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      if (file == null) return;
      const src = URL.createObjectURL(file);
      const attachment = new AttachmentManager(
        {
          src,
          file
        },
        this.editor.view
      );
      attachments.push(attachment);
    }
    return attachments;
  }
  renderToolbar() {
    return x``;
  }
  renderDialog() {
    return x``;
  }
  render() {
    return x`
      ${this.renderToolbar()}
      <div class="editor-wrapper" part="editor-wrapper">
        ${this.renderDialog()}
        <div class="editor" part="editor">
          <slot name="editor">
            <div class="trix-content"></div>
          </slot>
        </div>
      </div>
    `;
  }
  allOptions(element) {
    return {
      ...this.__defaultOptions(element),
      ...this.editorOptions(element)
    };
  }
  /**
   * Due to some inconsistencies in how Trix will render the inputElement based on if its
   * the HTML representation, or transfromed with `#to_trix_html` this gives
   * us a consistent DOM structure to parse for rich text comments.
   */
  normalizeDOM(inputElement, parser = new DOMParser()) {
    if (inputElement == null || inputElement.value == null) return;
    const doc = parser.parseFromString(inputElement.value, "text/html");
    const figures = [...doc.querySelectorAll("figure[data-trix-attachment]")];
    const filtersWithoutChildren = figures.filter(
      (figure) => figure.querySelector("figcaption") == null
    );
    doc.querySelectorAll("div > figure:first-child").forEach((el) => {
      el.parentElement?.classList.add("attachment-gallery");
    });
    filtersWithoutChildren.forEach((figure) => {
      const attrs = figure.getAttribute("data-trix-attributes");
      if (!attrs) return;
      const { caption } = JSON.parse(attrs);
      if (caption) {
        figure.insertAdjacentHTML(
          "beforeend",
          `<figcaption class="attachment__caption">${caption}</figcaption>`
        );
        return;
      }
    });
    doc.querySelectorAll(
      "figure :not(.attachment__caption--edited) .attachment__name"
    ).forEach((el) => {
      if (el.textContent?.includes(" \xB7 ") === false) return;
      el.insertAdjacentText("beforeend", " \xB7 ");
    });
    doc.querySelectorAll(":scope > p > br:not(.ProseMirror-trailingBreak)").forEach((el) => el.remove());
    const body = doc.querySelector("body");
    if (body) {
      inputElement.value = body.innerHTML;
    }
  }
  /**
   * @private
   * Use a getter here so when we rebuild the editor it pulls the latest starterKitOptions
   * This is intentionally not to be configured by a user. It makes updating extensions hard.
   *  it also is a getter and not a variable so that it will rerun in case options change.
   */
  get __starterKitExtensions__() {
    return [
      index_default.configure(this.starterKitOptions),
      RhinoStarterKit.configure(this.starterKitOptions)
    ];
  }
  /**
   * @param {Element} element - The element that the editor will be installed onto.
   */
  __defaultOptions(element) {
    let content = this.inputElement?.value || "";
    if (content) {
      try {
        content = JSON.parse(content);
      } catch (e) {
      }
    }
    const extensions = this.__starterKitExtensions__.concat(this.extensions);
    return {
      injectCSS: false,
      extensions,
      autofocus: false,
      element,
      content,
      editable: !this.readonly,
      editorProps: {
        handleDrop: this.handleDropFile
      }
    };
  }
  __bindEditorListeners() {
    if (this.editor == null) return;
    this.editor.on("focus", this.__handleFocus);
    this.editor.on("create", this.__handleCreate);
    this.editor.on("update", this.__handleUpdate);
    this.editor.on("selectionUpdate", this.__handleSelectionUpdate);
    this.editor.on("transaction", this.__handleTransaction);
    this.editor.on("blur", this.__handleBlur);
  }
  __unBindEditorListeners() {
    if (this.editor == null) return;
    this.editor.off("focus", this.__handleFocus);
    this.editor.off("create", this.__handleCreate);
    this.editor.off("update", this.__handleUpdate);
    this.editor.off("selectionUpdate", this.__handleSelectionUpdate);
    this.editor.off("transaction", this.__handleTransaction);
    this.editor.off("blur", this.__handleBlur);
  }
  __setupEditor(element = this) {
    if (!this.serializer || this.serializer === "html") {
      this.normalizeDOM(this.inputElement);
    }
    const editor = new Editor(this.allOptions(element));
    return editor;
  }
  getHTMLAndPreserveSignificantWhiteSpace() {
    const editor = this.editor;
    if (!editor) {
      return "";
    }
    const tempScript = document.createElement("div");
    const doc = editor.view.state.doc;
    const schema = editor.schema;
    const domFragment = DOMSerializer.fromSchema(schema).serializeFragment(
      doc.content
    );
    tempScript.append(domFragment);
    tempScript.querySelectorAll(":scope > p").forEach((p) => {
      preserveSignificantWhiteSpaceForElement(p);
    });
    return tempScript.innerHTML;
  }
};
// Static
/**
 * Default registration name
 */
TipTapEditorBase.baseName = "rhino-editor";
function replaceSignificantWhitespace(text, isFirst, isLast) {
  if (isLast) {
    text = text.replace(/\ $/, NON_BREAKING_SPACE);
  }
  text = text.replace(/(\S)\ {3}(\S)/g, "$1 " + NON_BREAKING_SPACE + " $2").replace(/\ {2}/g, NON_BREAKING_SPACE + " ").replace(/\ {2}/g, " " + NON_BREAKING_SPACE);
  if (isFirst) {
    text = text.replace(/^\ /, NON_BREAKING_SPACE);
  }
  return text;
}
function preserveSignificantWhiteSpaceForElement(node) {
  if (node.textContent?.trim() === "" && !node.querySelector("br")) {
    node.innerHTML = "<br>" + node.innerHTML;
    return;
  }
  const textNodes = getAllTextNodes(node);
  textNodes.forEach((textNode, index) => {
    const isFirst = index === 0;
    const isLast = index === textNodes.length - 1;
    if (textNode.textContent) {
      const text = replaceSignificantWhitespace(
        textNode.textContent,
        isFirst,
        isLast
      );
      textNode.textContent = text;
    }
  });
}
function getAllTextNodes(element) {
  const textNodes = [];
  const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);
  let node;
  while (node = walker.nextNode()) {
    textNodes.push(node);
  }
  return textNodes;
}

export {
  TipTapEditorBase
};
//# sourceMappingURL=chunk-VCBD7GOA.js.map