403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/ui/lexical/dev/dist/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/ui/lexical/dev/dist/lexical.dev.bundle.js
/* eslint-disable */
this.BX = this.BX || {};
this.BX.UI = this.BX.UI || {};
(function (exports) {
  'use strict';

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function createCommand(type) {
    return {
      type
    };
  }
  const SELECTION_CHANGE_COMMAND = createCommand('SELECTION_CHANGE_COMMAND');
  const SELECTION_INSERT_CLIPBOARD_NODES_COMMAND = createCommand('SELECTION_INSERT_CLIPBOARD_NODES_COMMAND');
  const CLICK_COMMAND = createCommand('CLICK_COMMAND');
  const DELETE_CHARACTER_COMMAND = createCommand('DELETE_CHARACTER_COMMAND');
  const INSERT_LINE_BREAK_COMMAND = createCommand('INSERT_LINE_BREAK_COMMAND');
  const INSERT_PARAGRAPH_COMMAND = createCommand('INSERT_PARAGRAPH_COMMAND');
  const CONTROLLED_TEXT_INSERTION_COMMAND = createCommand('CONTROLLED_TEXT_INSERTION_COMMAND');
  const PASTE_COMMAND = createCommand('PASTE_COMMAND');
  const REMOVE_TEXT_COMMAND = createCommand('REMOVE_TEXT_COMMAND');
  const DELETE_WORD_COMMAND = createCommand('DELETE_WORD_COMMAND');
  const DELETE_LINE_COMMAND = createCommand('DELETE_LINE_COMMAND');
  const FORMAT_TEXT_COMMAND = createCommand('FORMAT_TEXT_COMMAND');
  const UNDO_COMMAND = createCommand('UNDO_COMMAND');
  const REDO_COMMAND = createCommand('REDO_COMMAND');
  const KEY_DOWN_COMMAND = createCommand('KEYDOWN_COMMAND');
  const KEY_ARROW_RIGHT_COMMAND = createCommand('KEY_ARROW_RIGHT_COMMAND');
  const MOVE_TO_END = createCommand('MOVE_TO_END');
  const KEY_ARROW_LEFT_COMMAND = createCommand('KEY_ARROW_LEFT_COMMAND');
  const MOVE_TO_START = createCommand('MOVE_TO_START');
  const KEY_ARROW_UP_COMMAND = createCommand('KEY_ARROW_UP_COMMAND');
  const KEY_ARROW_DOWN_COMMAND = createCommand('KEY_ARROW_DOWN_COMMAND');
  const KEY_ENTER_COMMAND = createCommand('KEY_ENTER_COMMAND');
  const KEY_SPACE_COMMAND = createCommand('KEY_SPACE_COMMAND');
  const KEY_BACKSPACE_COMMAND = createCommand('KEY_BACKSPACE_COMMAND');
  const KEY_ESCAPE_COMMAND = createCommand('KEY_ESCAPE_COMMAND');
  const KEY_DELETE_COMMAND = createCommand('KEY_DELETE_COMMAND');
  const KEY_TAB_COMMAND = createCommand('KEY_TAB_COMMAND');
  const INSERT_TAB_COMMAND = createCommand('INSERT_TAB_COMMAND');
  const INDENT_CONTENT_COMMAND = createCommand('INDENT_CONTENT_COMMAND');
  const OUTDENT_CONTENT_COMMAND = createCommand('OUTDENT_CONTENT_COMMAND');
  const DROP_COMMAND = createCommand('DROP_COMMAND');
  const FORMAT_ELEMENT_COMMAND = createCommand('FORMAT_ELEMENT_COMMAND');
  const DRAGSTART_COMMAND = createCommand('DRAGSTART_COMMAND');
  const DRAGOVER_COMMAND = createCommand('DRAGOVER_COMMAND');
  const DRAGEND_COMMAND = createCommand('DRAGEND_COMMAND');
  const COPY_COMMAND = createCommand('COPY_COMMAND');
  const CUT_COMMAND = createCommand('CUT_COMMAND');
  const SELECT_ALL_COMMAND = createCommand('SELECT_ALL_COMMAND');
  const CLEAR_EDITOR_COMMAND = createCommand('CLEAR_EDITOR_COMMAND');
  const CLEAR_HISTORY_COMMAND = createCommand('CLEAR_HISTORY_COMMAND');
  const CAN_REDO_COMMAND = createCommand('CAN_REDO_COMMAND');
  const CAN_UNDO_COMMAND = createCommand('CAN_UNDO_COMMAND');
  const FOCUS_COMMAND = createCommand('FOCUS_COMMAND');
  const BLUR_COMMAND = createCommand('BLUR_COMMAND');
  const KEY_MODIFIER_COMMAND = createCommand('KEY_MODIFIER_COMMAND');

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const documentMode = CAN_USE_DOM && 'documentMode' in document ? document.documentMode : null;
  const IS_APPLE = CAN_USE_DOM && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
  const IS_FIREFOX = CAN_USE_DOM && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
  const CAN_USE_BEFORE_INPUT = CAN_USE_DOM && 'InputEvent' in window && !documentMode ? 'getTargetRanges' in new window.InputEvent('input') : false;
  const IS_SAFARI = CAN_USE_DOM && /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
  const IS_IOS = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
  const IS_ANDROID = CAN_USE_DOM && /Android/.test(navigator.userAgent);

  // Keep these in case we need to use them in the future.
  // export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
  const IS_CHROME = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent);
  // export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;

  const IS_ANDROID_CHROME = CAN_USE_DOM && IS_ANDROID && IS_CHROME;
  const IS_APPLE_WEBKIT = CAN_USE_DOM && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !IS_CHROME;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // DOM
  const DOM_ELEMENT_TYPE = 1;
  const DOM_TEXT_TYPE = 3;

  // Reconciling
  const NO_DIRTY_NODES = 0;
  const HAS_DIRTY_NODES = 1;
  const FULL_RECONCILE = 2;

  // Text node modes
  const IS_NORMAL = 0;
  const IS_TOKEN = 1;
  const IS_SEGMENTED = 2;
  // IS_INERT = 3

  // Text node formatting
  const IS_BOLD = 1;
  const IS_ITALIC = 1 << 1;
  const IS_STRIKETHROUGH = 1 << 2;
  const IS_UNDERLINE = 1 << 3;
  const IS_CODE = 1 << 4;
  const IS_SUBSCRIPT = 1 << 5;
  const IS_SUPERSCRIPT = 1 << 6;
  const IS_HIGHLIGHT = 1 << 7;
  const IS_ALL_FORMATTING = IS_BOLD | IS_ITALIC | IS_STRIKETHROUGH | IS_UNDERLINE | IS_CODE | IS_SUBSCRIPT | IS_SUPERSCRIPT | IS_HIGHLIGHT;

  // Text node details
  const IS_DIRECTIONLESS = 1;
  const IS_UNMERGEABLE = 1 << 1;

  // Element node formatting
  const IS_ALIGN_LEFT = 1;
  const IS_ALIGN_CENTER = 2;
  const IS_ALIGN_RIGHT = 3;
  const IS_ALIGN_JUSTIFY = 4;
  const IS_ALIGN_START = 5;
  const IS_ALIGN_END = 6;

  // Reconciliation
  const NON_BREAKING_SPACE = '\u00A0';
  const ZERO_WIDTH_SPACE = '\u200b';

  // For iOS/Safari we use a non breaking space, otherwise the cursor appears
  // overlapping the composed text.
  const COMPOSITION_SUFFIX = IS_SAFARI || IS_IOS || IS_APPLE_WEBKIT ? NON_BREAKING_SPACE : ZERO_WIDTH_SPACE;
  const DOUBLE_LINE_BREAK = '\n\n';

  // For FF, we need to use a non-breaking space, or it gets composition
  // in a stuck state.
  const COMPOSITION_START_CHAR = IS_FIREFOX ? NON_BREAKING_SPACE : COMPOSITION_SUFFIX;
  const RTL = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
  const LTR = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6' + '\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C' + '\uFE00-\uFE6F\uFEFD-\uFFFF';

  // eslint-disable-next-line no-misleading-character-class
  const RTL_REGEX = new RegExp('^[^' + LTR + ']*[' + RTL + ']');
  // eslint-disable-next-line no-misleading-character-class
  const LTR_REGEX = new RegExp('^[^' + RTL + ']*[' + LTR + ']');
  const TEXT_TYPE_TO_FORMAT = {
    bold: IS_BOLD,
    code: IS_CODE,
    highlight: IS_HIGHLIGHT,
    italic: IS_ITALIC,
    strikethrough: IS_STRIKETHROUGH,
    subscript: IS_SUBSCRIPT,
    superscript: IS_SUPERSCRIPT,
    underline: IS_UNDERLINE
  };
  const DETAIL_TYPE_TO_DETAIL = {
    directionless: IS_DIRECTIONLESS,
    unmergeable: IS_UNMERGEABLE
  };
  const ELEMENT_TYPE_TO_FORMAT = {
    center: IS_ALIGN_CENTER,
    end: IS_ALIGN_END,
    justify: IS_ALIGN_JUSTIFY,
    left: IS_ALIGN_LEFT,
    right: IS_ALIGN_RIGHT,
    start: IS_ALIGN_START
  };
  const ELEMENT_FORMAT_TO_TYPE = {
    [IS_ALIGN_CENTER]: 'center',
    [IS_ALIGN_END]: 'end',
    [IS_ALIGN_JUSTIFY]: 'justify',
    [IS_ALIGN_LEFT]: 'left',
    [IS_ALIGN_RIGHT]: 'right',
    [IS_ALIGN_START]: 'start'
  };
  const TEXT_MODE_TO_TYPE = {
    normal: IS_NORMAL,
    segmented: IS_SEGMENTED,
    token: IS_TOKEN
  };
  const TEXT_TYPE_TO_MODE = {
    [IS_NORMAL]: 'normal',
    [IS_SEGMENTED]: 'segmented',
    [IS_TOKEN]: 'token'
  };

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function normalizeClassNames(...classNames) {
    const rval = [];
    for (const className of classNames) {
      if (className && typeof className === 'string') {
        for (const [s] of className.matchAll(/\S+/g)) {
          rval.push(s);
        }
      }
    }
    return rval;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // The time between a text entry event and the mutation observer firing.
  const TEXT_MUTATION_VARIANCE = 100;
  let isProcessingMutations = false;
  let lastTextEntryTimeStamp = 0;
  function getIsProcessingMutations() {
    return isProcessingMutations;
  }
  function updateTimeStamp(event) {
    lastTextEntryTimeStamp = event.timeStamp;
  }
  function initTextEntryListener(editor) {
    if (lastTextEntryTimeStamp === 0) {
      getWindow(editor).addEventListener('textInput', updateTimeStamp, true);
    }
  }
  function isManagedLineBreak(dom, target, editor) {
    return (
      // @ts-expect-error: internal field
      target.__lexicalLineBreak === dom ||
      // @ts-ignore We intentionally add this to the Node.
      dom[`__lexicalKey_${editor._key}`] !== undefined
    );
  }
  function getLastSelection(editor) {
    return editor.getEditorState().read(() => {
      const selection = $getSelection();
      return selection !== null ? selection.clone() : null;
    });
  }
  function $handleTextMutation(target, node, editor) {
    const domSelection = getDOMSelection(editor._window);
    let anchorOffset = null;
    let focusOffset = null;
    if (domSelection !== null && domSelection.anchorNode === target) {
      anchorOffset = domSelection.anchorOffset;
      focusOffset = domSelection.focusOffset;
    }
    const text = target.nodeValue;
    if (text !== null) {
      $updateTextNodeFromDOMContent(node, text, anchorOffset, focusOffset, false);
    }
  }
  function shouldUpdateTextNodeFromMutation(selection, targetDOM, targetNode) {
    if ($isRangeSelection(selection)) {
      const anchorNode = selection.anchor.getNode();
      if (anchorNode.is(targetNode) && selection.format !== anchorNode.getFormat()) {
        return false;
      }
    }
    return targetDOM.nodeType === DOM_TEXT_TYPE && targetNode.isAttached();
  }
  function $flushMutations$1(editor, mutations, observer) {
    isProcessingMutations = true;
    const shouldFlushTextMutations = performance.now() - lastTextEntryTimeStamp > TEXT_MUTATION_VARIANCE;
    try {
      updateEditor(editor, () => {
        const selection = $getSelection() || getLastSelection(editor);
        const badDOMTargets = new Map();
        const rootElement = editor.getRootElement();
        // We use the current editor state, as that reflects what is
        // actually "on screen".
        const currentEditorState = editor._editorState;
        const blockCursorElement = editor._blockCursorElement;
        let shouldRevertSelection = false;
        let possibleTextForFirefoxPaste = '';
        for (let i = 0; i < mutations.length; i++) {
          const mutation = mutations[i];
          const type = mutation.type;
          const targetDOM = mutation.target;
          let targetNode = $getNearestNodeFromDOMNode(targetDOM, currentEditorState);
          if (targetNode === null && targetDOM !== rootElement || $isDecoratorNode(targetNode)) {
            continue;
          }
          if (type === 'characterData') {
            // Text mutations are deferred and passed to mutation listeners to be
            // processed outside of the Lexical engine.
            if (shouldFlushTextMutations && $isTextNode(targetNode) && shouldUpdateTextNodeFromMutation(selection, targetDOM, targetNode)) {
              $handleTextMutation(
              // nodeType === DOM_TEXT_TYPE is a Text DOM node
              targetDOM, targetNode, editor);
            }
          } else if (type === 'childList') {
            shouldRevertSelection = true;
            // We attempt to "undo" any changes that have occurred outside
            // of Lexical. We want Lexical's editor state to be source of truth.
            // To the user, these will look like no-ops.
            const addedDOMs = mutation.addedNodes;
            for (let s = 0; s < addedDOMs.length; s++) {
              const addedDOM = addedDOMs[s];
              const node = $getNodeFromDOMNode(addedDOM);
              const parentDOM = addedDOM.parentNode;
              if (parentDOM != null && addedDOM !== blockCursorElement && node === null && (addedDOM.nodeName !== 'BR' || !isManagedLineBreak(addedDOM, parentDOM, editor))) {
                if (IS_FIREFOX) {
                  const possibleText = addedDOM.innerText || addedDOM.nodeValue;
                  if (possibleText) {
                    possibleTextForFirefoxPaste += possibleText;
                  }
                }
                parentDOM.removeChild(addedDOM);
              }
            }
            const removedDOMs = mutation.removedNodes;
            const removedDOMsLength = removedDOMs.length;
            if (removedDOMsLength > 0) {
              let unremovedBRs = 0;
              for (let s = 0; s < removedDOMsLength; s++) {
                const removedDOM = removedDOMs[s];
                if (removedDOM.nodeName === 'BR' && isManagedLineBreak(removedDOM, targetDOM, editor) || blockCursorElement === removedDOM) {
                  targetDOM.appendChild(removedDOM);
                  unremovedBRs++;
                }
              }
              if (removedDOMsLength !== unremovedBRs) {
                if (targetDOM === rootElement) {
                  targetNode = internalGetRoot(currentEditorState);
                }
                badDOMTargets.set(targetDOM, targetNode);
              }
            }
          }
        }

        // Now we process each of the unique target nodes, attempting
        // to restore their contents back to the source of truth, which
        // is Lexical's "current" editor state. This is basically like
        // an internal revert on the DOM.
        if (badDOMTargets.size > 0) {
          for (const [targetDOM, targetNode] of badDOMTargets) {
            if ($isElementNode(targetNode)) {
              const childKeys = targetNode.getChildrenKeys();
              let currentDOM = targetDOM.firstChild;
              for (let s = 0; s < childKeys.length; s++) {
                const key = childKeys[s];
                const correctDOM = editor.getElementByKey(key);
                if (correctDOM === null) {
                  continue;
                }
                if (currentDOM == null) {
                  targetDOM.appendChild(correctDOM);
                  currentDOM = correctDOM;
                } else if (currentDOM !== correctDOM) {
                  targetDOM.replaceChild(correctDOM, currentDOM);
                }
                currentDOM = currentDOM.nextSibling;
              }
            } else if ($isTextNode(targetNode)) {
              targetNode.markDirty();
            }
          }
        }

        // Capture all the mutations made during this function. This
        // also prevents us having to process them on the next cycle
        // of onMutation, as these mutations were made by us.
        const records = observer.takeRecords();

        // Check for any random auto-added <br> elements, and remove them.
        // These get added by the browser when we undo the above mutations
        // and this can lead to a broken UI.
        if (records.length > 0) {
          for (let i = 0; i < records.length; i++) {
            const record = records[i];
            const addedNodes = record.addedNodes;
            const target = record.target;
            for (let s = 0; s < addedNodes.length; s++) {
              const addedDOM = addedNodes[s];
              const parentDOM = addedDOM.parentNode;
              if (parentDOM != null && addedDOM.nodeName === 'BR' && !isManagedLineBreak(addedDOM, target, editor)) {
                parentDOM.removeChild(addedDOM);
              }
            }
          }

          // Clear any of those removal mutations
          observer.takeRecords();
        }
        if (selection !== null) {
          if (shouldRevertSelection) {
            selection.dirty = true;
            $setSelection(selection);
          }
          if (IS_FIREFOX && isFirefoxClipboardEvents(editor)) {
            selection.insertRawText(possibleTextForFirefoxPaste);
          }
        }
      });
    } finally {
      isProcessingMutations = false;
    }
  }
  function $flushRootMutations(editor) {
    const observer = editor._observer;
    if (observer !== null) {
      const mutations = observer.takeRecords();
      $flushMutations$1(editor, mutations, observer);
    }
  }
  function initMutationObserver(editor) {
    initTextEntryListener(editor);
    editor._observer = new MutationObserver((mutations, observer) => {
      $flushMutations$1(editor, mutations, observer);
    });
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function $canSimpleTextNodesBeMerged(node1, node2) {
    const node1Mode = node1.__mode;
    const node1Format = node1.__format;
    const node1Style = node1.__style;
    const node2Mode = node2.__mode;
    const node2Format = node2.__format;
    const node2Style = node2.__style;
    return (node1Mode === null || node1Mode === node2Mode) && (node1Format === null || node1Format === node2Format) && (node1Style === null || node1Style === node2Style);
  }
  function $mergeTextNodes(node1, node2) {
    const writableNode1 = node1.mergeWithSibling(node2);
    const normalizedNodes = getActiveEditor()._normalizedNodes;
    normalizedNodes.add(node1.__key);
    normalizedNodes.add(node2.__key);
    return writableNode1;
  }
  function $normalizeTextNode(textNode) {
    let node = textNode;
    if (node.__text === '' && node.isSimpleText() && !node.isUnmergeable()) {
      node.remove();
      return;
    }

    // Backward
    let previousNode;
    while ((previousNode = node.getPreviousSibling()) !== null && $isTextNode(previousNode) && previousNode.isSimpleText() && !previousNode.isUnmergeable()) {
      if (previousNode.__text === '') {
        previousNode.remove();
      } else if ($canSimpleTextNodesBeMerged(previousNode, node)) {
        node = $mergeTextNodes(previousNode, node);
        break;
      } else {
        break;
      }
    }

    // Forward
    let nextNode;
    while ((nextNode = node.getNextSibling()) !== null && $isTextNode(nextNode) && nextNode.isSimpleText() && !nextNode.isUnmergeable()) {
      if (nextNode.__text === '') {
        nextNode.remove();
      } else if ($canSimpleTextNodesBeMerged(node, nextNode)) {
        node = $mergeTextNodes(node, nextNode);
        break;
      } else {
        break;
      }
    }
  }
  function $normalizeSelection(selection) {
    $normalizePoint(selection.anchor);
    $normalizePoint(selection.focus);
    return selection;
  }
  function $normalizePoint(point) {
    while (point.type === 'element') {
      const node = point.getNode();
      const offset = point.offset;
      let nextNode;
      let nextOffsetAtEnd;
      if (offset === node.getChildrenSize()) {
        nextNode = node.getChildAtIndex(offset - 1);
        nextOffsetAtEnd = true;
      } else {
        nextNode = node.getChildAtIndex(offset);
        nextOffsetAtEnd = false;
      }
      if ($isTextNode(nextNode)) {
        point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getTextContentSize() : 0, 'text');
        break;
      } else if (!$isElementNode(nextNode)) {
        break;
      }
      point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getChildrenSize() : 0, 'element');
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  let keyCounter = 1;
  function resetRandomKey() {
    keyCounter = 1;
  }
  function generateRandomKey() {
    return '' + keyCounter++;
  }
  function getRegisteredNodeOrThrow(editor, nodeType) {
    const registeredNode = editor._nodes.get(nodeType);
    if (registeredNode === undefined) {
      {
        throw Error(`registeredNode: Type ${nodeType} not found`);
      }
    }
    return registeredNode;
  }
  const scheduleMicroTask = typeof queueMicrotask === 'function' ? queueMicrotask : fn => {
    // No window prefix intended (#1400)
    Promise.resolve().then(fn);
  };
  function $isSelectionCapturedInDecorator(node) {
    return $isDecoratorNode($getNearestNodeFromDOMNode(node));
  }
  function isSelectionCapturedInDecoratorInput(anchorDOM) {
    const activeElement = document.activeElement;
    if (activeElement === null) {
      return false;
    }
    const nodeName = activeElement.nodeName;
    return $isDecoratorNode($getNearestNodeFromDOMNode(anchorDOM)) && (nodeName === 'INPUT' || nodeName === 'TEXTAREA' || activeElement.contentEditable === 'true' && getEditorPropertyFromDOMNode(activeElement) == null);
  }
  function isSelectionWithinEditor(editor, anchorDOM, focusDOM) {
    const rootElement = editor.getRootElement();
    try {
      return rootElement !== null && rootElement.contains(anchorDOM) && rootElement.contains(focusDOM) &&
      // Ignore if selection is within nested editor
      anchorDOM !== null && !isSelectionCapturedInDecoratorInput(anchorDOM) && getNearestEditorFromDOMNode(anchorDOM) === editor;
    } catch (error) {
      return false;
    }
  }

  /**
   * @returns true if the given argument is a LexicalEditor instance from this build of Lexical
   */
  function isLexicalEditor(editor) {
    // Check instanceof to prevent issues with multiple embedded Lexical installations
    return editor instanceof LexicalEditor;
  }
  function getNearestEditorFromDOMNode(node) {
    let currentNode = node;
    while (currentNode != null) {
      const editor = getEditorPropertyFromDOMNode(currentNode);
      if (isLexicalEditor(editor)) {
        return editor;
      }
      currentNode = getParentElement(currentNode);
    }
    return null;
  }

  /** @internal */
  function getEditorPropertyFromDOMNode(node) {
    // @ts-expect-error: internal field
    return node ? node.__lexicalEditor : null;
  }
  function getTextDirection(text) {
    if (RTL_REGEX.test(text)) {
      return 'rtl';
    }
    if (LTR_REGEX.test(text)) {
      return 'ltr';
    }
    return null;
  }
  function $isTokenOrSegmented(node) {
    return node.isToken() || node.isSegmented();
  }
  function isDOMNodeLexicalTextNode(node) {
    return node.nodeType === DOM_TEXT_TYPE;
  }
  function getDOMTextNode(element) {
    let node = element;
    while (node != null) {
      if (isDOMNodeLexicalTextNode(node)) {
        return node;
      }
      node = node.firstChild;
    }
    return null;
  }
  function toggleTextFormatType(format, type, alignWithFormat) {
    const activeFormat = TEXT_TYPE_TO_FORMAT[type];
    if (alignWithFormat !== null && (format & activeFormat) === (alignWithFormat & activeFormat)) {
      return format;
    }
    let newFormat = format ^ activeFormat;
    if (type === 'subscript') {
      newFormat &= ~TEXT_TYPE_TO_FORMAT.superscript;
    } else if (type === 'superscript') {
      newFormat &= ~TEXT_TYPE_TO_FORMAT.subscript;
    }
    return newFormat;
  }
  function $isLeafNode(node) {
    return $isTextNode(node) || $isLineBreakNode(node) || $isDecoratorNode(node);
  }
  function $setNodeKey(node, existingKey) {
    if (existingKey != null) {
      {
        errorOnNodeKeyConstructorMismatch(node, existingKey);
      }
      node.__key = existingKey;
      return;
    }
    errorOnReadOnly();
    errorOnInfiniteTransforms();
    const editor = getActiveEditor();
    const editorState = getActiveEditorState();
    const key = generateRandomKey();
    editorState._nodeMap.set(key, node);
    // TODO Split this function into leaf/element
    if ($isElementNode(node)) {
      editor._dirtyElements.set(key, true);
    } else {
      editor._dirtyLeaves.add(key);
    }
    editor._cloneNotNeeded.add(key);
    editor._dirtyType = HAS_DIRTY_NODES;
    node.__key = key;
  }
  function errorOnNodeKeyConstructorMismatch(node, existingKey) {
    const editorState = internalGetActiveEditorState();
    if (!editorState) {
      // tests expect to be able to do this kind of clone without an active editor state
      return;
    }
    const existingNode = editorState._nodeMap.get(existingKey);
    if (existingNode && existingNode.constructor !== node.constructor) {
      // Lifted condition to if statement because the inverted logic is a bit confusing
      if (node.constructor.name !== existingNode.constructor.name) {
        {
          throw Error(`Lexical node with constructor ${node.constructor.name} attempted to re-use key from node in active editor state with constructor ${existingNode.constructor.name}. Keys must not be re-used when the type is changed.`);
        }
      } else {
        {
          throw Error(`Lexical node with constructor ${node.constructor.name} attempted to re-use key from node in active editor state with different constructor with the same name (possibly due to invalid Hot Module Replacement). Keys must not be re-used when the type is changed.`);
        }
      }
    }
  }
  function internalMarkParentElementsAsDirty(parentKey, nodeMap, dirtyElements) {
    let nextParentKey = parentKey;
    while (nextParentKey !== null) {
      if (dirtyElements.has(nextParentKey)) {
        return;
      }
      const node = nodeMap.get(nextParentKey);
      if (node === undefined) {
        break;
      }
      dirtyElements.set(nextParentKey, false);
      nextParentKey = node.__parent;
    }
  }

  // TODO #6031 this function or their callers have to adjust selection (i.e. insertBefore)
  function removeFromParent(node) {
    const oldParent = node.getParent();
    if (oldParent !== null) {
      const writableNode = node.getWritable();
      const writableParent = oldParent.getWritable();
      const prevSibling = node.getPreviousSibling();
      const nextSibling = node.getNextSibling();
      // TODO: this function duplicates a bunch of operations, can be simplified.
      if (prevSibling === null) {
        if (nextSibling !== null) {
          const writableNextSibling = nextSibling.getWritable();
          writableParent.__first = nextSibling.__key;
          writableNextSibling.__prev = null;
        } else {
          writableParent.__first = null;
        }
      } else {
        const writablePrevSibling = prevSibling.getWritable();
        if (nextSibling !== null) {
          const writableNextSibling = nextSibling.getWritable();
          writableNextSibling.__prev = writablePrevSibling.__key;
          writablePrevSibling.__next = writableNextSibling.__key;
        } else {
          writablePrevSibling.__next = null;
        }
        writableNode.__prev = null;
      }
      if (nextSibling === null) {
        if (prevSibling !== null) {
          const writablePrevSibling = prevSibling.getWritable();
          writableParent.__last = prevSibling.__key;
          writablePrevSibling.__next = null;
        } else {
          writableParent.__last = null;
        }
      } else {
        const writableNextSibling = nextSibling.getWritable();
        if (prevSibling !== null) {
          const writablePrevSibling = prevSibling.getWritable();
          writablePrevSibling.__next = writableNextSibling.__key;
          writableNextSibling.__prev = writablePrevSibling.__key;
        } else {
          writableNextSibling.__prev = null;
        }
        writableNode.__next = null;
      }
      writableParent.__size--;
      writableNode.__parent = null;
    }
  }

  // Never use this function directly! It will break
  // the cloning heuristic. Instead use node.getWritable().
  function internalMarkNodeAsDirty(node) {
    errorOnInfiniteTransforms();
    const latest = node.getLatest();
    const parent = latest.__parent;
    const editorState = getActiveEditorState();
    const editor = getActiveEditor();
    const nodeMap = editorState._nodeMap;
    const dirtyElements = editor._dirtyElements;
    if (parent !== null) {
      internalMarkParentElementsAsDirty(parent, nodeMap, dirtyElements);
    }
    const key = latest.__key;
    editor._dirtyType = HAS_DIRTY_NODES;
    if ($isElementNode(node)) {
      dirtyElements.set(key, true);
    } else {
      // TODO split internally MarkNodeAsDirty into two dedicated Element/leave functions
      editor._dirtyLeaves.add(key);
    }
  }
  function internalMarkSiblingsAsDirty(node) {
    const previousNode = node.getPreviousSibling();
    const nextNode = node.getNextSibling();
    if (previousNode !== null) {
      internalMarkNodeAsDirty(previousNode);
    }
    if (nextNode !== null) {
      internalMarkNodeAsDirty(nextNode);
    }
  }
  function $setCompositionKey(compositionKey) {
    errorOnReadOnly();
    const editor = getActiveEditor();
    const previousCompositionKey = editor._compositionKey;
    if (compositionKey !== previousCompositionKey) {
      editor._compositionKey = compositionKey;
      if (previousCompositionKey !== null) {
        const node = $getNodeByKey(previousCompositionKey);
        if (node !== null) {
          node.getWritable();
        }
      }
      if (compositionKey !== null) {
        const node = $getNodeByKey(compositionKey);
        if (node !== null) {
          node.getWritable();
        }
      }
    }
  }
  function $getCompositionKey() {
    if (isCurrentlyReadOnlyMode()) {
      return null;
    }
    const editor = getActiveEditor();
    return editor._compositionKey;
  }
  function $getNodeByKey(key, _editorState) {
    const editorState = _editorState || getActiveEditorState();
    const node = editorState._nodeMap.get(key);
    if (node === undefined) {
      return null;
    }
    return node;
  }
  function $getNodeFromDOMNode(dom, editorState) {
    const editor = getActiveEditor();
    // @ts-ignore We intentionally add this to the Node.
    const key = dom[`__lexicalKey_${editor._key}`];
    if (key !== undefined) {
      return $getNodeByKey(key, editorState);
    }
    return null;
  }
  function $getNearestNodeFromDOMNode(startingDOM, editorState) {
    let dom = startingDOM;
    while (dom != null) {
      const node = $getNodeFromDOMNode(dom, editorState);
      if (node !== null) {
        return node;
      }
      dom = getParentElement(dom);
    }
    return null;
  }
  function cloneDecorators(editor) {
    const currentDecorators = editor._decorators;
    const pendingDecorators = Object.assign({}, currentDecorators);
    editor._pendingDecorators = pendingDecorators;
    return pendingDecorators;
  }
  function getEditorStateTextContent(editorState) {
    return editorState.read(() => $getRoot().getTextContent());
  }
  function markAllNodesAsDirty(editor, type) {
    // Mark all existing text nodes as dirty
    updateEditor(editor, () => {
      const editorState = getActiveEditorState();
      if (editorState.isEmpty()) {
        return;
      }
      if (type === 'root') {
        $getRoot().markDirty();
        return;
      }
      const nodeMap = editorState._nodeMap;
      for (const [, node] of nodeMap) {
        node.markDirty();
      }
    }, editor._pendingEditorState === null ? {
      tag: 'history-merge'
    } : undefined);
  }
  function $getRoot() {
    return internalGetRoot(getActiveEditorState());
  }
  function internalGetRoot(editorState) {
    return editorState._nodeMap.get('root');
  }
  function $setSelection(selection) {
    errorOnReadOnly();
    const editorState = getActiveEditorState();
    if (selection !== null) {
      {
        if (Object.isFrozen(selection)) {
          {
            throw Error(`$setSelection called on frozen selection object. Ensure selection is cloned before passing in.`);
          }
        }
      }
      selection.dirty = true;
      selection.setCachedNodes(null);
    }
    editorState._selection = selection;
  }
  function $flushMutations() {
    errorOnReadOnly();
    const editor = getActiveEditor();
    $flushRootMutations(editor);
  }
  function $getNodeFromDOM(dom) {
    const editor = getActiveEditor();
    const nodeKey = getNodeKeyFromDOM(dom, editor);
    if (nodeKey === null) {
      const rootElement = editor.getRootElement();
      if (dom === rootElement) {
        return $getNodeByKey('root');
      }
      return null;
    }
    return $getNodeByKey(nodeKey);
  }
  function getTextNodeOffset(node, moveSelectionToEnd) {
    return moveSelectionToEnd ? node.getTextContentSize() : 0;
  }
  function getNodeKeyFromDOM(
  // Note that node here refers to a DOM Node, not an Lexical Node
  dom, editor) {
    let node = dom;
    while (node != null) {
      // @ts-ignore We intentionally add this to the Node.
      const key = node[`__lexicalKey_${editor._key}`];
      if (key !== undefined) {
        return key;
      }
      node = getParentElement(node);
    }
    return null;
  }
  function doesContainGrapheme(str) {
    return /[\uD800-\uDBFF][\uDC00-\uDFFF]/g.test(str);
  }
  function getEditorsToPropagate(editor) {
    const editorsToPropagate = [];
    let currentEditor = editor;
    while (currentEditor !== null) {
      editorsToPropagate.push(currentEditor);
      currentEditor = currentEditor._parentEditor;
    }
    return editorsToPropagate;
  }
  function createUID() {
    return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
  }
  function getAnchorTextFromDOM(anchorNode) {
    if (anchorNode.nodeType === DOM_TEXT_TYPE) {
      return anchorNode.nodeValue;
    }
    return null;
  }
  function $updateSelectedTextFromDOM(isCompositionEnd, editor, data) {
    // Update the text content with the latest composition text
    const domSelection = getDOMSelection(editor._window);
    if (domSelection === null) {
      return;
    }
    const anchorNode = domSelection.anchorNode;
    let {
      anchorOffset,
      focusOffset
    } = domSelection;
    if (anchorNode !== null) {
      let textContent = getAnchorTextFromDOM(anchorNode);
      const node = $getNearestNodeFromDOMNode(anchorNode);
      if (textContent !== null && $isTextNode(node)) {
        // Data is intentionally truthy, as we check for boolean, null and empty string.
        if (textContent === COMPOSITION_SUFFIX && data) {
          const offset = data.length;
          textContent = data;
          anchorOffset = offset;
          focusOffset = offset;
        }
        if (textContent !== null) {
          $updateTextNodeFromDOMContent(node, textContent, anchorOffset, focusOffset, isCompositionEnd);
        }
      }
    }
  }
  function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focusOffset, compositionEnd) {
    let node = textNode;
    if (node.isAttached() && (compositionEnd || !node.isDirty())) {
      const isComposing = node.isComposing();
      let normalizedTextContent = textContent;
      if ((isComposing || compositionEnd) && textContent[textContent.length - 1] === COMPOSITION_SUFFIX) {
        normalizedTextContent = textContent.slice(0, -1);
      }
      const prevTextContent = node.getTextContent();
      if (compositionEnd || normalizedTextContent !== prevTextContent) {
        if (normalizedTextContent === '') {
          $setCompositionKey(null);
          if (!IS_SAFARI && !IS_IOS && !IS_APPLE_WEBKIT) {
            // For composition (mainly Android), we have to remove the node on a later update
            const editor = getActiveEditor();
            setTimeout(() => {
              editor.update(() => {
                if (node.isAttached()) {
                  node.remove();
                }
              });
            }, 20);
          } else {
            node.remove();
          }
          return;
        }
        const parent = node.getParent();
        const prevSelection = $getPreviousSelection();
        const prevTextContentSize = node.getTextContentSize();
        const compositionKey = $getCompositionKey();
        const nodeKey = node.getKey();
        if (node.isToken() || compositionKey !== null && nodeKey === compositionKey && !isComposing ||
        // Check if character was added at the start or boundaries when not insertable, and we need
        // to clear this input from occurring as that action wasn't permitted.
        $isRangeSelection(prevSelection) && (parent !== null && !parent.canInsertTextBefore() && prevSelection.anchor.offset === 0 || prevSelection.anchor.key === textNode.__key && prevSelection.anchor.offset === 0 && !node.canInsertTextBefore() && !isComposing || prevSelection.focus.key === textNode.__key && prevSelection.focus.offset === prevTextContentSize && !node.canInsertTextAfter() && !isComposing)) {
          node.markDirty();
          return;
        }
        const selection = $getSelection();
        if (!$isRangeSelection(selection) || anchorOffset === null || focusOffset === null) {
          node.setTextContent(normalizedTextContent);
          return;
        }
        selection.setTextNodeRange(node, anchorOffset, node, focusOffset);
        if (node.isSegmented()) {
          const originalTextContent = node.getTextContent();
          const replacement = $createTextNode(originalTextContent);
          node.replace(replacement);
          node = replacement;
        }
        node.setTextContent(normalizedTextContent);
      }
    }
  }
  function $previousSiblingDoesNotAcceptText(node) {
    const previousSibling = node.getPreviousSibling();
    return ($isTextNode(previousSibling) || $isElementNode(previousSibling) && previousSibling.isInline()) && !previousSibling.canInsertTextAfter();
  }

  // This function is connected to $shouldPreventDefaultAndInsertText and determines whether the
  // TextNode boundaries are writable or we should use the previous/next sibling instead. For example,
  // in the case of a LinkNode, boundaries are not writable.
  function $shouldInsertTextAfterOrBeforeTextNode(selection, node) {
    if (node.isSegmented()) {
      return true;
    }
    if (!selection.isCollapsed()) {
      return false;
    }
    const offset = selection.anchor.offset;
    const parent = node.getParentOrThrow();
    const isToken = node.isToken();
    if (offset === 0) {
      return !node.canInsertTextBefore() || !parent.canInsertTextBefore() && !node.isComposing() || isToken || $previousSiblingDoesNotAcceptText(node);
    } else if (offset === node.getTextContentSize()) {
      return !node.canInsertTextAfter() || !parent.canInsertTextAfter() && !node.isComposing() || isToken;
    } else {
      return false;
    }
  }
  function isTab(key, altKey, ctrlKey, metaKey) {
    return key === 'Tab' && !altKey && !ctrlKey && !metaKey;
  }
  function isBold(key, altKey, metaKey, ctrlKey) {
    return key.toLowerCase() === 'b' && !altKey && controlOrMeta(metaKey, ctrlKey);
  }
  function isItalic(key, altKey, metaKey, ctrlKey) {
    return key.toLowerCase() === 'i' && !altKey && controlOrMeta(metaKey, ctrlKey);
  }
  function isUnderline(key, altKey, metaKey, ctrlKey) {
    return key.toLowerCase() === 'u' && !altKey && controlOrMeta(metaKey, ctrlKey);
  }
  function isParagraph(key, shiftKey) {
    return isReturn(key) && !shiftKey;
  }
  function isLineBreak(key, shiftKey) {
    return isReturn(key) && shiftKey;
  }

  // Inserts a new line after the selection

  function isOpenLineBreak(key, ctrlKey) {
    // 79 = KeyO
    return IS_APPLE && ctrlKey && key.toLowerCase() === 'o';
  }
  function isDeleteWordBackward(key, altKey, ctrlKey) {
    return isBackspace(key) && (IS_APPLE ? altKey : ctrlKey);
  }
  function isDeleteWordForward(key, altKey, ctrlKey) {
    return isDelete(key) && (IS_APPLE ? altKey : ctrlKey);
  }
  function isDeleteLineBackward(key, metaKey) {
    return IS_APPLE && metaKey && isBackspace(key);
  }
  function isDeleteLineForward(key, metaKey) {
    return IS_APPLE && metaKey && isDelete(key);
  }
  function isDeleteBackward(key, altKey, metaKey, ctrlKey) {
    if (IS_APPLE) {
      if (altKey || metaKey) {
        return false;
      }
      return isBackspace(key) || key.toLowerCase() === 'h' && ctrlKey;
    }
    if (ctrlKey || altKey || metaKey) {
      return false;
    }
    return isBackspace(key);
  }
  function isDeleteForward(key, ctrlKey, shiftKey, altKey, metaKey) {
    if (IS_APPLE) {
      if (shiftKey || altKey || metaKey) {
        return false;
      }
      return isDelete(key) || key.toLowerCase() === 'd' && ctrlKey;
    }
    if (ctrlKey || altKey || metaKey) {
      return false;
    }
    return isDelete(key);
  }
  function isUndo(key, shiftKey, metaKey, ctrlKey) {
    return key.toLowerCase() === 'z' && !shiftKey && controlOrMeta(metaKey, ctrlKey);
  }
  function isRedo(key, shiftKey, metaKey, ctrlKey) {
    if (IS_APPLE) {
      return key.toLowerCase() === 'z' && metaKey && shiftKey;
    }
    return key.toLowerCase() === 'y' && ctrlKey || key.toLowerCase() === 'z' && ctrlKey && shiftKey;
  }
  function isCopy(key, shiftKey, metaKey, ctrlKey) {
    if (shiftKey) {
      return false;
    }
    if (key.toLowerCase() === 'c') {
      return IS_APPLE ? metaKey : ctrlKey;
    }
    return false;
  }
  function isCut(key, shiftKey, metaKey, ctrlKey) {
    if (shiftKey) {
      return false;
    }
    if (key.toLowerCase() === 'x') {
      return IS_APPLE ? metaKey : ctrlKey;
    }
    return false;
  }
  function isArrowLeft(key) {
    return key === 'ArrowLeft';
  }
  function isArrowRight(key) {
    return key === 'ArrowRight';
  }
  function isArrowUp(key) {
    return key === 'ArrowUp';
  }
  function isArrowDown(key) {
    return key === 'ArrowDown';
  }
  function isMoveBackward(key, ctrlKey, altKey, metaKey) {
    return isArrowLeft(key) && !ctrlKey && !metaKey && !altKey;
  }
  function isMoveToStart(key, ctrlKey, shiftKey, altKey, metaKey) {
    return isArrowLeft(key) && !altKey && !shiftKey && (ctrlKey || metaKey);
  }
  function isMoveForward(key, ctrlKey, altKey, metaKey) {
    return isArrowRight(key) && !ctrlKey && !metaKey && !altKey;
  }
  function isMoveToEnd(key, ctrlKey, shiftKey, altKey, metaKey) {
    return isArrowRight(key) && !altKey && !shiftKey && (ctrlKey || metaKey);
  }
  function isMoveUp(key, ctrlKey, metaKey) {
    return isArrowUp(key) && !ctrlKey && !metaKey;
  }
  function isMoveDown(key, ctrlKey, metaKey) {
    return isArrowDown(key) && !ctrlKey && !metaKey;
  }
  function isModifier(ctrlKey, shiftKey, altKey, metaKey) {
    return ctrlKey || shiftKey || altKey || metaKey;
  }
  function isSpace(key) {
    return key === ' ';
  }
  function controlOrMeta(metaKey, ctrlKey) {
    if (IS_APPLE) {
      return metaKey;
    }
    return ctrlKey;
  }
  function isReturn(key) {
    return key === 'Enter';
  }
  function isBackspace(key) {
    return key === 'Backspace';
  }
  function isEscape(key) {
    return key === 'Escape';
  }
  function isDelete(key) {
    return key === 'Delete';
  }
  function isSelectAll(key, metaKey, ctrlKey) {
    return key.toLowerCase() === 'a' && controlOrMeta(metaKey, ctrlKey);
  }
  function $selectAll() {
    const root = $getRoot();
    const selection = root.select(0, root.getChildrenSize());
    $setSelection($normalizeSelection(selection));
  }
  function getCachedClassNameArray(classNamesTheme, classNameThemeType) {
    if (classNamesTheme.__lexicalClassNameCache === undefined) {
      classNamesTheme.__lexicalClassNameCache = {};
    }
    const classNamesCache = classNamesTheme.__lexicalClassNameCache;
    const cachedClassNames = classNamesCache[classNameThemeType];
    if (cachedClassNames !== undefined) {
      return cachedClassNames;
    }
    const classNames = classNamesTheme[classNameThemeType];
    // As we're using classList, we need
    // to handle className tokens that have spaces.
    // The easiest way to do this to convert the
    // className tokens to an array that can be
    // applied to classList.add()/remove().
    if (typeof classNames === 'string') {
      const classNamesArr = normalizeClassNames(classNames);
      classNamesCache[classNameThemeType] = classNamesArr;
      return classNamesArr;
    }
    return classNames;
  }
  function setMutatedNode(mutatedNodes, registeredNodes, mutationListeners, node, mutation) {
    if (mutationListeners.size === 0) {
      return;
    }
    const nodeType = node.__type;
    const nodeKey = node.__key;
    const registeredNode = registeredNodes.get(nodeType);
    if (registeredNode === undefined) {
      {
        throw Error(`Type ${nodeType} not in registeredNodes`);
      }
    }
    const klass = registeredNode.klass;
    let mutatedNodesByType = mutatedNodes.get(klass);
    if (mutatedNodesByType === undefined) {
      mutatedNodesByType = new Map();
      mutatedNodes.set(klass, mutatedNodesByType);
    }
    const prevMutation = mutatedNodesByType.get(nodeKey);
    // If the node has already been "destroyed", yet we are
    // re-making it, then this means a move likely happened.
    // We should change the mutation to be that of "updated"
    // instead.
    const isMove = prevMutation === 'destroyed' && mutation === 'created';
    if (prevMutation === undefined || isMove) {
      mutatedNodesByType.set(nodeKey, isMove ? 'updated' : mutation);
    }
  }
  function $nodesOfType(klass) {
    const klassType = klass.getType();
    const editorState = getActiveEditorState();
    if (editorState._readOnly) {
      const nodes = getCachedTypeToNodeMap(editorState).get(klassType);
      return nodes ? Array.from(nodes.values()) : [];
    }
    const nodes = editorState._nodeMap;
    const nodesOfType = [];
    for (const [, node] of nodes) {
      if (node instanceof klass && node.__type === klassType && node.isAttached()) {
        nodesOfType.push(node);
      }
    }
    return nodesOfType;
  }
  function resolveElement(element, isBackward, focusOffset) {
    const parent = element.getParent();
    let offset = focusOffset;
    let block = element;
    if (parent !== null) {
      if (isBackward && focusOffset === 0) {
        offset = block.getIndexWithinParent();
        block = parent;
      } else if (!isBackward && focusOffset === block.getChildrenSize()) {
        offset = block.getIndexWithinParent() + 1;
        block = parent;
      }
    }
    return block.getChildAtIndex(isBackward ? offset - 1 : offset);
  }
  function $getAdjacentNode(focus, isBackward) {
    const focusOffset = focus.offset;
    if (focus.type === 'element') {
      const block = focus.getNode();
      return resolveElement(block, isBackward, focusOffset);
    } else {
      const focusNode = focus.getNode();
      if (isBackward && focusOffset === 0 || !isBackward && focusOffset === focusNode.getTextContentSize()) {
        const possibleNode = isBackward ? focusNode.getPreviousSibling() : focusNode.getNextSibling();
        if (possibleNode === null) {
          return resolveElement(focusNode.getParentOrThrow(), isBackward, focusNode.getIndexWithinParent() + (isBackward ? 0 : 1));
        }
        return possibleNode;
      }
    }
    return null;
  }
  function isFirefoxClipboardEvents(editor) {
    const event = getWindow(editor).event;
    const inputType = event && event.inputType;
    return inputType === 'insertFromPaste' || inputType === 'insertFromPasteAsQuotation';
  }
  function dispatchCommand(editor, command, payload) {
    return triggerCommandListeners(editor, command, payload);
  }
  function $textContentRequiresDoubleLinebreakAtEnd(node) {
    return !$isRootNode(node) && !node.isLastChild() && !node.isInline();
  }
  function getElementByKeyOrThrow(editor, key) {
    const element = editor._keyToDOMMap.get(key);
    if (element === undefined) {
      {
        throw Error(`Reconciliation: could not find DOM element for node key ${key}`);
      }
    }
    return element;
  }
  function getParentElement(node) {
    const parentElement = node.assignedSlot || node.parentElement;
    return parentElement !== null && parentElement.nodeType === 11 ? parentElement.host : parentElement;
  }
  function scrollIntoViewIfNeeded(editor, selectionRect, rootElement) {
    const doc = rootElement.ownerDocument;
    const defaultView = doc.defaultView;
    if (defaultView === null) {
      return;
    }
    let {
      top: currentTop,
      bottom: currentBottom
    } = selectionRect;
    let targetTop = 0;
    let targetBottom = 0;
    let element = rootElement;
    while (element !== null) {
      const isBodyElement = element === doc.body;
      if (isBodyElement) {
        targetTop = 0;
        targetBottom = getWindow(editor).innerHeight;
      } else {
        const targetRect = element.getBoundingClientRect();
        targetTop = targetRect.top;
        targetBottom = targetRect.bottom;
      }
      let diff = 0;
      if (currentTop < targetTop) {
        diff = -(targetTop - currentTop);
      } else if (currentBottom > targetBottom) {
        diff = currentBottom - targetBottom;
      }
      if (diff !== 0) {
        if (isBodyElement) {
          // Only handles scrolling of Y axis
          defaultView.scrollBy(0, diff);
        } else {
          const scrollTop = element.scrollTop;
          element.scrollTop += diff;
          const yOffset = element.scrollTop - scrollTop;
          currentTop -= yOffset;
          currentBottom -= yOffset;
        }
      }
      if (isBodyElement) {
        break;
      }
      element = getParentElement(element);
    }
  }
  function $hasUpdateTag(tag) {
    const editor = getActiveEditor();
    return editor._updateTags.has(tag);
  }
  function $addUpdateTag(tag) {
    errorOnReadOnly();
    const editor = getActiveEditor();
    editor._updateTags.add(tag);
  }
  function $maybeMoveChildrenSelectionToParent(parentNode) {
    const selection = $getSelection();
    if (!$isRangeSelection(selection) || !$isElementNode(parentNode)) {
      return selection;
    }
    const {
      anchor,
      focus
    } = selection;
    const anchorNode = anchor.getNode();
    const focusNode = focus.getNode();
    if ($hasAncestor(anchorNode, parentNode)) {
      anchor.set(parentNode.__key, 0, 'element');
    }
    if ($hasAncestor(focusNode, parentNode)) {
      focus.set(parentNode.__key, 0, 'element');
    }
    return selection;
  }
  function $hasAncestor(child, targetNode) {
    let parent = child.getParent();
    while (parent !== null) {
      if (parent.is(targetNode)) {
        return true;
      }
      parent = parent.getParent();
    }
    return false;
  }
  function getDefaultView(domElem) {
    const ownerDoc = domElem.ownerDocument;
    return ownerDoc && ownerDoc.defaultView || null;
  }
  function getWindow(editor) {
    const windowObj = editor._window;
    if (windowObj === null) {
      {
        throw Error(`window object not found`);
      }
    }
    return windowObj;
  }
  function $isInlineElementOrDecoratorNode(node) {
    return $isElementNode(node) && node.isInline() || $isDecoratorNode(node) && node.isInline();
  }
  function $getNearestRootOrShadowRoot(node) {
    let parent = node.getParentOrThrow();
    while (parent !== null) {
      if ($isRootOrShadowRoot(parent)) {
        return parent;
      }
      parent = parent.getParentOrThrow();
    }
    return parent;
  }
  function $isRootOrShadowRoot(node) {
    return $isRootNode(node) || $isElementNode(node) && node.isShadowRoot();
  }

  /**
   * Returns a shallow clone of node with a new key
   *
   * @param node - The node to be copied.
   * @returns The copy of the node.
   */
  function $copyNode(node) {
    const copy = node.constructor.clone(node);
    $setNodeKey(copy, null);
    return copy;
  }
  function $applyNodeReplacement(node) {
    const editor = getActiveEditor();
    const nodeType = node.constructor.getType();
    const registeredNode = editor._nodes.get(nodeType);
    if (registeredNode === undefined) {
      {
        throw Error(`$initializeNode failed. Ensure node has been registered to the editor. You can do this by passing the node class via the "nodes" array in the editor config.`);
      }
    }
    const replaceFunc = registeredNode.replace;
    if (replaceFunc !== null) {
      const replacementNode = replaceFunc(node);
      if (!(replacementNode instanceof node.constructor)) {
        {
          throw Error(`$initializeNode failed. Ensure replacement node is a subclass of the original node.`);
        }
      }
      return replacementNode;
    }
    return node;
  }
  function errorOnInsertTextNodeOnRoot(node, insertNode) {
    const parentNode = node.getParent();
    if ($isRootNode(parentNode) && !$isElementNode(insertNode) && !$isDecoratorNode(insertNode)) {
      {
        throw Error(`Only element or decorator nodes can be inserted in to the root node`);
      }
    }
  }
  function $getNodeByKeyOrThrow(key) {
    const node = $getNodeByKey(key);
    if (node === null) {
      {
        throw Error(`Expected node with key ${key} to exist but it's not in the nodeMap.`);
      }
    }
    return node;
  }
  function createBlockCursorElement(editorConfig) {
    const theme = editorConfig.theme;
    const element = document.createElement('div');
    element.contentEditable = 'false';
    element.setAttribute('data-lexical-cursor', 'true');
    let blockCursorTheme = theme.blockCursor;
    if (blockCursorTheme !== undefined) {
      if (typeof blockCursorTheme === 'string') {
        const classNamesArr = normalizeClassNames(blockCursorTheme);
        // @ts-expect-error: intentional
        blockCursorTheme = theme.blockCursor = classNamesArr;
      }
      if (blockCursorTheme !== undefined) {
        element.classList.add(...blockCursorTheme);
      }
    }
    return element;
  }
  function needsBlockCursor(node) {
    return ($isDecoratorNode(node) || $isElementNode(node) && !node.canBeEmpty()) && !node.isInline();
  }
  function removeDOMBlockCursorElement(blockCursorElement, editor, rootElement) {
    rootElement.style.removeProperty('caret-color');
    editor._blockCursorElement = null;
    const parentElement = blockCursorElement.parentElement;
    if (parentElement !== null) {
      parentElement.removeChild(blockCursorElement);
    }
  }
  function updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
    let blockCursorElement = editor._blockCursorElement;
    if ($isRangeSelection(nextSelection) && nextSelection.isCollapsed() && nextSelection.anchor.type === 'element' && rootElement.contains(document.activeElement)) {
      const anchor = nextSelection.anchor;
      const elementNode = anchor.getNode();
      const offset = anchor.offset;
      const elementNodeSize = elementNode.getChildrenSize();
      let isBlockCursor = false;
      let insertBeforeElement = null;
      if (offset === elementNodeSize) {
        const child = elementNode.getChildAtIndex(offset - 1);
        if (needsBlockCursor(child)) {
          isBlockCursor = true;
        }
      } else {
        const child = elementNode.getChildAtIndex(offset);
        if (needsBlockCursor(child)) {
          const sibling = child.getPreviousSibling();
          if (sibling === null || needsBlockCursor(sibling)) {
            isBlockCursor = true;
            insertBeforeElement = editor.getElementByKey(child.__key);
          }
        }
      }
      if (isBlockCursor) {
        const elementDOM = editor.getElementByKey(elementNode.__key);
        if (blockCursorElement === null) {
          editor._blockCursorElement = blockCursorElement = createBlockCursorElement(editor._config);
        }
        rootElement.style.caretColor = 'transparent';
        if (insertBeforeElement === null) {
          elementDOM.appendChild(blockCursorElement);
        } else {
          elementDOM.insertBefore(blockCursorElement, insertBeforeElement);
        }
        return;
      }
    }
    // Remove cursor
    if (blockCursorElement !== null) {
      removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
    }
  }
  function getDOMSelection(targetWindow) {
    return !CAN_USE_DOM ? null : (targetWindow || window).getSelection();
  }
  function $splitNode(node, offset) {
    let startNode = node.getChildAtIndex(offset);
    if (startNode == null) {
      startNode = node;
    }
    if (!!$isRootOrShadowRoot(node)) {
      throw Error(`Can not call $splitNode() on root element`);
    }
    const recurse = currentNode => {
      const parent = currentNode.getParentOrThrow();
      const isParentRoot = $isRootOrShadowRoot(parent);
      // The node we start split from (leaf) is moved, but its recursive
      // parents are copied to create separate tree
      const nodeToMove = currentNode === startNode && !isParentRoot ? currentNode : $copyNode(currentNode);
      if (isParentRoot) {
        if (!($isElementNode(currentNode) && $isElementNode(nodeToMove))) {
          throw Error(`Children of a root must be ElementNode`);
        }
        currentNode.insertAfter(nodeToMove);
        return [currentNode, nodeToMove, nodeToMove];
      } else {
        const [leftTree, rightTree, newParent] = recurse(parent);
        const nextSiblings = currentNode.getNextSiblings();
        newParent.append(nodeToMove, ...nextSiblings);
        return [leftTree, rightTree, nodeToMove];
      }
    };
    const [leftTree, rightTree] = recurse(startNode);
    return [leftTree, rightTree];
  }

  /**
   * @param x - The element being tested
   * @returns Returns true if x is an HTML anchor tag, false otherwise
   */
  function isHTMLAnchorElement(x) {
    return isHTMLElement(x) && x.tagName === 'A';
  }

  /**
   * @param x - The element being testing
   * @returns Returns true if x is an HTML element, false otherwise.
   */
  function isHTMLElement(x) {
    // @ts-ignore-next-line - strict check on nodeType here should filter out non-Element EventTarget implementors
    return x.nodeType === 1;
  }

  /**
   *
   * @param node - the Dom Node to check
   * @returns if the Dom Node is an inline node
   */
  function isInlineDomNode(node) {
    const inlineNodes = new RegExp(/^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var|#text)$/, 'i');
    return node.nodeName.match(inlineNodes) !== null;
  }

  /**
   *
   * @param node - the Dom Node to check
   * @returns if the Dom Node is a block node
   */
  function isBlockDomNode(node) {
    const blockNodes = new RegExp(/^(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hr|li|main|nav|noscript|ol|p|pre|section|table|td|tfoot|ul|video)$/, 'i');
    return node.nodeName.match(blockNodes) !== null;
  }

  /**
   * This function is for internal use of the library.
   * Please do not use it as it may change in the future.
   */
  function INTERNAL_$isBlock(node) {
    if ($isRootNode(node) || $isDecoratorNode(node) && !node.isInline()) {
      return true;
    }
    if (!$isElementNode(node) || $isRootOrShadowRoot(node)) {
      return false;
    }
    const firstChild = node.getFirstChild();
    const isLeafElement = firstChild === null || $isLineBreakNode(firstChild) || $isTextNode(firstChild) || firstChild.isInline();
    return !node.isInline() && node.canBeEmpty() !== false && isLeafElement;
  }
  function $getAncestor(node, predicate) {
    let parent = node;
    while (parent !== null && parent.getParent() !== null && !predicate(parent)) {
      parent = parent.getParentOrThrow();
    }
    return predicate(parent) ? parent : null;
  }

  /**
   * Utility function for accessing current active editor instance.
   * @returns Current active editor
   */
  function $getEditor() {
    return getActiveEditor();
  }

  /** @internal */

  /**
   * @internal
   * Compute a cached Map of node type to nodes for a frozen EditorState
   */
  const cachedNodeMaps = new WeakMap();
  const EMPTY_TYPE_TO_NODE_MAP = new Map();
  function getCachedTypeToNodeMap(editorState) {
    // If this is a new Editor it may have a writable this._editorState
    // with only a 'root' entry.
    if (!editorState._readOnly && editorState.isEmpty()) {
      return EMPTY_TYPE_TO_NODE_MAP;
    }
    if (!editorState._readOnly) {
      throw Error(`getCachedTypeToNodeMap called with a writable EditorState`);
    }
    let typeToNodeMap = cachedNodeMaps.get(editorState);
    if (!typeToNodeMap) {
      typeToNodeMap = new Map();
      cachedNodeMaps.set(editorState, typeToNodeMap);
      for (const [nodeKey, node] of editorState._nodeMap) {
        const nodeType = node.__type;
        let nodeMap = typeToNodeMap.get(nodeType);
        if (!nodeMap) {
          nodeMap = new Map();
          typeToNodeMap.set(nodeType, nodeMap);
        }
        nodeMap.set(nodeKey, node);
      }
    }
    return typeToNodeMap;
  }

  /**
   * Returns a clone of a node using `node.constructor.clone()` followed by
   * `clone.afterCloneFrom(node)`. The resulting clone must have the same key,
   * parent/next/prev pointers, and other properties that are not set by
   * `node.constructor.clone` (format, style, etc.). This is primarily used by
   * {@link LexicalNode.getWritable} to create a writable version of an
   * existing node. The clone is the same logical node as the original node,
   * do not try and use this function to duplicate or copy an existing node.
   *
   * Does not mutate the EditorState.
   * @param node - The node to be cloned.
   * @returns The clone of the node.
   */
  function $cloneWithProperties(latestNode) {
    const constructor = latestNode.constructor;
    const mutableNode = constructor.clone(latestNode);
    mutableNode.afterCloneFrom(latestNode);
    {
      if (!(mutableNode.__key === latestNode.__key)) {
        throw Error(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') did not return a node with the same key, make sure to specify node.__key as the last argument to the constructor`);
      }
      if (!(mutableNode.__parent === latestNode.__parent && mutableNode.__next === latestNode.__next && mutableNode.__prev === latestNode.__prev)) {
        throw Error(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') overrided afterCloneFrom but did not call super.afterCloneFrom(prevNode)`);
      }
    }
    return mutableNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function $garbageCollectDetachedDecorators(editor, pendingEditorState) {
    const currentDecorators = editor._decorators;
    const pendingDecorators = editor._pendingDecorators;
    let decorators = pendingDecorators || currentDecorators;
    const nodeMap = pendingEditorState._nodeMap;
    let key;
    for (key in decorators) {
      if (!nodeMap.has(key)) {
        if (decorators === currentDecorators) {
          decorators = cloneDecorators(editor);
        }
        delete decorators[key];
      }
    }
  }
  function $garbageCollectDetachedDeepChildNodes(node, parentKey, prevNodeMap, nodeMap, nodeMapDelete, dirtyNodes) {
    let child = node.getFirstChild();
    while (child !== null) {
      const childKey = child.__key;
      // TODO Revise condition below, redundant? LexicalNode already cleans up children when moving Nodes
      if (child.__parent === parentKey) {
        if ($isElementNode(child)) {
          $garbageCollectDetachedDeepChildNodes(child, childKey, prevNodeMap, nodeMap, nodeMapDelete, dirtyNodes);
        }

        // If we have created a node and it was dereferenced, then also
        // remove it from out dirty nodes Set.
        if (!prevNodeMap.has(childKey)) {
          dirtyNodes.delete(childKey);
        }
        nodeMapDelete.push(childKey);
      }
      child = child.getNextSibling();
    }
  }
  function $garbageCollectDetachedNodes(prevEditorState, editorState, dirtyLeaves, dirtyElements) {
    const prevNodeMap = prevEditorState._nodeMap;
    const nodeMap = editorState._nodeMap;
    // Store dirtyElements in a queue for later deletion; deleting dirty subtrees too early will
    // hinder accessing .__next on child nodes
    const nodeMapDelete = [];
    for (const [nodeKey] of dirtyElements) {
      const node = nodeMap.get(nodeKey);
      if (node !== undefined) {
        // Garbage collect node and its children if they exist
        if (!node.isAttached()) {
          if ($isElementNode(node)) {
            $garbageCollectDetachedDeepChildNodes(node, nodeKey, prevNodeMap, nodeMap, nodeMapDelete, dirtyElements);
          }
          // If we have created a node and it was dereferenced, then also
          // remove it from out dirty nodes Set.
          if (!prevNodeMap.has(nodeKey)) {
            dirtyElements.delete(nodeKey);
          }
          nodeMapDelete.push(nodeKey);
        }
      }
    }
    for (const nodeKey of nodeMapDelete) {
      nodeMap.delete(nodeKey);
    }
    for (const nodeKey of dirtyLeaves) {
      const node = nodeMap.get(nodeKey);
      if (node !== undefined && !node.isAttached()) {
        if (!prevNodeMap.has(nodeKey)) {
          dirtyLeaves.delete(nodeKey);
        }
        nodeMap.delete(nodeKey);
      }
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  let subTreeTextContent = '';
  let subTreeDirectionedTextContent = '';
  let subTreeTextFormat = null;
  let subTreeTextStyle = '';
  let editorTextContent = '';
  let activeEditorConfig;
  let activeEditor$1;
  let activeEditorNodes;
  let treatAllNodesAsDirty = false;
  let activeEditorStateReadOnly = false;
  let activeMutationListeners;
  let activeTextDirection = null;
  let activeDirtyElements;
  let activeDirtyLeaves;
  let activePrevNodeMap;
  let activeNextNodeMap;
  let activePrevKeyToDOMMap;
  let mutatedNodes;
  function destroyNode(key, parentDOM) {
    const node = activePrevNodeMap.get(key);
    if (parentDOM !== null) {
      const dom = getPrevElementByKeyOrThrow(key);
      if (dom.parentNode === parentDOM) {
        parentDOM.removeChild(dom);
      }
    }

    // This logic is really important, otherwise we will leak DOM nodes
    // when their corresponding LexicalNodes are removed from the editor state.
    if (!activeNextNodeMap.has(key)) {
      activeEditor$1._keyToDOMMap.delete(key);
    }
    if ($isElementNode(node)) {
      const children = createChildrenArray(node, activePrevNodeMap);
      destroyChildren(children, 0, children.length - 1, null);
    }
    if (node !== undefined) {
      setMutatedNode(mutatedNodes, activeEditorNodes, activeMutationListeners, node, 'destroyed');
    }
  }
  function destroyChildren(children, _startIndex, endIndex, dom) {
    let startIndex = _startIndex;
    for (; startIndex <= endIndex; ++startIndex) {
      const child = children[startIndex];
      if (child !== undefined) {
        destroyNode(child, dom);
      }
    }
  }
  function setTextAlign(domStyle, value) {
    domStyle.setProperty('text-align', value);
  }
  const DEFAULT_INDENT_VALUE = '40px';
  function setElementIndent(dom, indent) {
    const indentClassName = activeEditorConfig.theme.indent;
    if (typeof indentClassName === 'string') {
      const elementHasClassName = dom.classList.contains(indentClassName);
      if (indent > 0 && !elementHasClassName) {
        dom.classList.add(indentClassName);
      } else if (indent < 1 && elementHasClassName) {
        dom.classList.remove(indentClassName);
      }
    }
    const indentationBaseValue = getComputedStyle(dom).getPropertyValue('--lexical-indent-base-value') || DEFAULT_INDENT_VALUE;
    dom.style.setProperty('padding-inline-start', indent === 0 ? '' : `calc(${indent} * ${indentationBaseValue})`);
  }
  function setElementFormat(dom, format) {
    const domStyle = dom.style;
    if (format === 0) {
      setTextAlign(domStyle, '');
    } else if (format === IS_ALIGN_LEFT) {
      setTextAlign(domStyle, 'left');
    } else if (format === IS_ALIGN_CENTER) {
      setTextAlign(domStyle, 'center');
    } else if (format === IS_ALIGN_RIGHT) {
      setTextAlign(domStyle, 'right');
    } else if (format === IS_ALIGN_JUSTIFY) {
      setTextAlign(domStyle, 'justify');
    } else if (format === IS_ALIGN_START) {
      setTextAlign(domStyle, 'start');
    } else if (format === IS_ALIGN_END) {
      setTextAlign(domStyle, 'end');
    }
  }
  function $createNode(key, parentDOM, insertDOM) {
    const node = activeNextNodeMap.get(key);
    if (node === undefined) {
      {
        throw Error(`createNode: node does not exist in nodeMap`);
      }
    }
    const dom = node.createDOM(activeEditorConfig, activeEditor$1);
    storeDOMWithKey(key, dom, activeEditor$1);

    // This helps preserve the text, and stops spell check tools from
    // merging or break the spans (which happens if they are missing
    // this attribute).
    if ($isTextNode(node)) {
      dom.setAttribute('data-lexical-text', 'true');
    } else if ($isDecoratorNode(node)) {
      dom.setAttribute('data-lexical-decorator', 'true');
    }
    if ($isElementNode(node)) {
      const indent = node.__indent;
      const childrenSize = node.__size;
      if (indent !== 0) {
        setElementIndent(dom, indent);
      }
      if (childrenSize !== 0) {
        const endIndex = childrenSize - 1;
        const children = createChildrenArray(node, activeNextNodeMap);
        $createChildrenWithDirection(children, endIndex, node, dom);
      }
      const format = node.__format;
      if (format !== 0) {
        setElementFormat(dom, format);
      }
      if (!node.isInline()) {
        reconcileElementTerminatingLineBreak(null, node, dom);
      }
      if ($textContentRequiresDoubleLinebreakAtEnd(node)) {
        subTreeTextContent += DOUBLE_LINE_BREAK;
        editorTextContent += DOUBLE_LINE_BREAK;
      }
    } else {
      const text = node.getTextContent();
      if ($isDecoratorNode(node)) {
        const decorator = node.decorate(activeEditor$1, activeEditorConfig);
        if (decorator !== null) {
          reconcileDecorator(key, decorator);
        }
        // Decorators are always non editable
        dom.contentEditable = 'false';
      } else if ($isTextNode(node)) {
        if (!node.isDirectionless()) {
          subTreeDirectionedTextContent += text;
        }
      }
      subTreeTextContent += text;
      editorTextContent += text;
    }
    if (parentDOM !== null) {
      if (insertDOM != null) {
        parentDOM.insertBefore(dom, insertDOM);
      } else {
        // @ts-expect-error: internal field
        const possibleLineBreak = parentDOM.__lexicalLineBreak;
        if (possibleLineBreak != null) {
          parentDOM.insertBefore(dom, possibleLineBreak);
        } else {
          parentDOM.appendChild(dom);
        }
      }
    }
    {
      // Freeze the node in DEV to prevent accidental mutations
      Object.freeze(node);
    }
    setMutatedNode(mutatedNodes, activeEditorNodes, activeMutationListeners, node, 'created');
    return dom;
  }
  function $createChildrenWithDirection(children, endIndex, element, dom) {
    const previousSubTreeDirectionedTextContent = subTreeDirectionedTextContent;
    subTreeDirectionedTextContent = '';
    $createChildren(children, element, 0, endIndex, dom, null);
    reconcileBlockDirection(element, dom);
    subTreeDirectionedTextContent = previousSubTreeDirectionedTextContent;
  }
  function $createChildren(children, element, _startIndex, endIndex, dom, insertDOM) {
    const previousSubTreeTextContent = subTreeTextContent;
    subTreeTextContent = '';
    let startIndex = _startIndex;
    for (; startIndex <= endIndex; ++startIndex) {
      $createNode(children[startIndex], dom, insertDOM);
      const node = activeNextNodeMap.get(children[startIndex]);
      if (node !== null && $isTextNode(node)) {
        if (subTreeTextFormat === null) {
          subTreeTextFormat = node.getFormat();
        }
        if (subTreeTextStyle === '') {
          subTreeTextStyle = node.getStyle();
        }
      }
    }
    if ($textContentRequiresDoubleLinebreakAtEnd(element)) {
      subTreeTextContent += DOUBLE_LINE_BREAK;
    }
    // @ts-expect-error: internal field
    dom.__lexicalTextContent = subTreeTextContent;
    subTreeTextContent = previousSubTreeTextContent + subTreeTextContent;
  }
  function isLastChildLineBreakOrDecorator(childKey, nodeMap) {
    const node = nodeMap.get(childKey);
    return $isLineBreakNode(node) || $isDecoratorNode(node) && node.isInline();
  }

  // If we end an element with a LineBreakNode, then we need to add an additional <br>
  function reconcileElementTerminatingLineBreak(prevElement, nextElement, dom) {
    const prevLineBreak = prevElement !== null && (prevElement.__size === 0 || isLastChildLineBreakOrDecorator(prevElement.__last, activePrevNodeMap));
    const nextLineBreak = nextElement.__size === 0 || isLastChildLineBreakOrDecorator(nextElement.__last, activeNextNodeMap);
    if (prevLineBreak) {
      if (!nextLineBreak) {
        // @ts-expect-error: internal field
        const element = dom.__lexicalLineBreak;
        if (element != null) {
          try {
            dom.removeChild(element);
          } catch (error) {
            if (typeof error === 'object' && error != null) {
              const msg = `${error.toString()} Parent: ${dom.tagName}, child: ${element.tagName}.`;
              throw new Error(msg);
            } else {
              throw error;
            }
          }
        }

        // @ts-expect-error: internal field
        dom.__lexicalLineBreak = null;
      }
    } else if (nextLineBreak) {
      const element = document.createElement('br');
      // @ts-expect-error: internal field
      dom.__lexicalLineBreak = element;
      dom.appendChild(element);
    }
  }
  function reconcileParagraphFormat(element) {
    if ($isParagraphNode(element) && subTreeTextFormat != null && subTreeTextFormat !== element.__textFormat && !activeEditorStateReadOnly) {
      element.setTextFormat(subTreeTextFormat);
      element.setTextStyle(subTreeTextStyle);
    }
  }
  function reconcileParagraphStyle(element) {
    if ($isParagraphNode(element) && subTreeTextStyle !== '' && subTreeTextStyle !== element.__textStyle && !activeEditorStateReadOnly) {
      element.setTextStyle(subTreeTextStyle);
    }
  }
  function reconcileBlockDirection(element, dom) {
    const previousSubTreeDirectionTextContent =
    // @ts-expect-error: internal field
    dom.__lexicalDirTextContent;
    // @ts-expect-error: internal field
    const previousDirection = dom.__lexicalDir;
    if (previousSubTreeDirectionTextContent !== subTreeDirectionedTextContent || previousDirection !== activeTextDirection) {
      const hasEmptyDirectionedTextContent = subTreeDirectionedTextContent === '';
      const direction = hasEmptyDirectionedTextContent ? activeTextDirection : getTextDirection(subTreeDirectionedTextContent);
      if (direction !== previousDirection) {
        const classList = dom.classList;
        const theme = activeEditorConfig.theme;
        let previousDirectionTheme = previousDirection !== null ? theme[previousDirection] : undefined;
        let nextDirectionTheme = direction !== null ? theme[direction] : undefined;

        // Remove the old theme classes if they exist
        if (previousDirectionTheme !== undefined) {
          if (typeof previousDirectionTheme === 'string') {
            const classNamesArr = normalizeClassNames(previousDirectionTheme);
            previousDirectionTheme = theme[previousDirection] = classNamesArr;
          }

          // @ts-ignore: intentional
          classList.remove(...previousDirectionTheme);
        }
        if (direction === null || hasEmptyDirectionedTextContent && direction === 'ltr') {
          // Remove direction
          dom.removeAttribute('dir');
        } else {
          // Apply the new theme classes if they exist
          if (nextDirectionTheme !== undefined) {
            if (typeof nextDirectionTheme === 'string') {
              const classNamesArr = normalizeClassNames(nextDirectionTheme);
              // @ts-expect-error: intentional
              nextDirectionTheme = theme[direction] = classNamesArr;
            }
            if (nextDirectionTheme !== undefined) {
              classList.add(...nextDirectionTheme);
            }
          }

          // Update direction
          dom.dir = direction;
        }
        if (!activeEditorStateReadOnly) {
          const writableNode = element.getWritable();
          writableNode.__dir = direction;
        }
      }
      activeTextDirection = direction;
      // @ts-expect-error: internal field
      dom.__lexicalDirTextContent = subTreeDirectionedTextContent;
      // @ts-expect-error: internal field
      dom.__lexicalDir = direction;
    }
  }
  function $reconcileChildrenWithDirection(prevElement, nextElement, dom) {
    const previousSubTreeDirectionTextContent = subTreeDirectionedTextContent;
    subTreeDirectionedTextContent = '';
    subTreeTextFormat = null;
    subTreeTextStyle = '';
    $reconcileChildren(prevElement, nextElement, dom);
    reconcileBlockDirection(nextElement, dom);
    reconcileParagraphFormat(nextElement);
    reconcileParagraphStyle(nextElement);
    subTreeDirectionedTextContent = previousSubTreeDirectionTextContent;
  }
  function createChildrenArray(element, nodeMap) {
    const children = [];
    let nodeKey = element.__first;
    while (nodeKey !== null) {
      const node = nodeMap.get(nodeKey);
      if (node === undefined) {
        {
          throw Error(`createChildrenArray: node does not exist in nodeMap`);
        }
      }
      children.push(nodeKey);
      nodeKey = node.__next;
    }
    return children;
  }
  function $reconcileChildren(prevElement, nextElement, dom) {
    const previousSubTreeTextContent = subTreeTextContent;
    const prevChildrenSize = prevElement.__size;
    const nextChildrenSize = nextElement.__size;
    subTreeTextContent = '';
    if (prevChildrenSize === 1 && nextChildrenSize === 1) {
      const prevFirstChildKey = prevElement.__first;
      const nextFrstChildKey = nextElement.__first;
      if (prevFirstChildKey === nextFrstChildKey) {
        $reconcileNode(prevFirstChildKey, dom);
      } else {
        const lastDOM = getPrevElementByKeyOrThrow(prevFirstChildKey);
        const replacementDOM = $createNode(nextFrstChildKey, null, null);
        try {
          dom.replaceChild(replacementDOM, lastDOM);
        } catch (error) {
          if (typeof error === 'object' && error != null) {
            const msg = `${error.toString()} Parent: ${dom.tagName}, new child: {tag: ${replacementDOM.tagName} key: ${nextFrstChildKey}}, old child: {tag: ${lastDOM.tagName}, key: ${prevFirstChildKey}}.`;
            throw new Error(msg);
          } else {
            throw error;
          }
        }
        destroyNode(prevFirstChildKey, null);
      }
      const nextChildNode = activeNextNodeMap.get(nextFrstChildKey);
      if ($isTextNode(nextChildNode)) {
        if (subTreeTextFormat === null) {
          subTreeTextFormat = nextChildNode.getFormat();
        }
        if (subTreeTextStyle === '') {
          subTreeTextStyle = nextChildNode.getStyle();
        }
      }
    } else {
      const prevChildren = createChildrenArray(prevElement, activePrevNodeMap);
      const nextChildren = createChildrenArray(nextElement, activeNextNodeMap);
      if (prevChildrenSize === 0) {
        if (nextChildrenSize !== 0) {
          $createChildren(nextChildren, nextElement, 0, nextChildrenSize - 1, dom, null);
        }
      } else if (nextChildrenSize === 0) {
        if (prevChildrenSize !== 0) {
          // @ts-expect-error: internal field
          const lexicalLineBreak = dom.__lexicalLineBreak;
          const canUseFastPath = lexicalLineBreak == null;
          destroyChildren(prevChildren, 0, prevChildrenSize - 1, canUseFastPath ? null : dom);
          if (canUseFastPath) {
            // Fast path for removing DOM nodes
            dom.textContent = '';
          }
        }
      } else {
        $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChildrenSize, nextChildrenSize, dom);
      }
    }
    if ($textContentRequiresDoubleLinebreakAtEnd(nextElement)) {
      subTreeTextContent += DOUBLE_LINE_BREAK;
    }

    // @ts-expect-error: internal field
    dom.__lexicalTextContent = subTreeTextContent;
    subTreeTextContent = previousSubTreeTextContent + subTreeTextContent;
  }
  function $reconcileNode(key, parentDOM) {
    const prevNode = activePrevNodeMap.get(key);
    let nextNode = activeNextNodeMap.get(key);
    if (prevNode === undefined || nextNode === undefined) {
      {
        throw Error(`reconcileNode: prevNode or nextNode does not exist in nodeMap`);
      }
    }
    const isDirty = treatAllNodesAsDirty || activeDirtyLeaves.has(key) || activeDirtyElements.has(key);
    const dom = getElementByKeyOrThrow(activeEditor$1, key);

    // If the node key points to the same instance in both states
    // and isn't dirty, we just update the text content cache
    // and return the existing DOM Node.
    if (prevNode === nextNode && !isDirty) {
      if ($isElementNode(prevNode)) {
        // @ts-expect-error: internal field
        const previousSubTreeTextContent = dom.__lexicalTextContent;
        if (previousSubTreeTextContent !== undefined) {
          subTreeTextContent += previousSubTreeTextContent;
          editorTextContent += previousSubTreeTextContent;
        }

        // @ts-expect-error: internal field
        const previousSubTreeDirectionTextContent = dom.__lexicalDirTextContent;
        if (previousSubTreeDirectionTextContent !== undefined) {
          subTreeDirectionedTextContent += previousSubTreeDirectionTextContent;
        }
      } else {
        const text = prevNode.getTextContent();
        if ($isTextNode(prevNode) && !prevNode.isDirectionless()) {
          subTreeDirectionedTextContent += text;
        }
        editorTextContent += text;
        subTreeTextContent += text;
      }
      return dom;
    }
    // If the node key doesn't point to the same instance in both maps,
    // it means it were cloned. If they're also dirty, we mark them as mutated.
    if (prevNode !== nextNode && isDirty) {
      setMutatedNode(mutatedNodes, activeEditorNodes, activeMutationListeners, nextNode, 'updated');
    }

    // Update node. If it returns true, we need to unmount and re-create the node
    if (nextNode.updateDOM(prevNode, dom, activeEditorConfig)) {
      const replacementDOM = $createNode(key, null, null);
      if (parentDOM === null) {
        {
          throw Error(`reconcileNode: parentDOM is null`);
        }
      }
      parentDOM.replaceChild(replacementDOM, dom);
      destroyNode(key, null);
      return replacementDOM;
    }
    if ($isElementNode(prevNode) && $isElementNode(nextNode)) {
      // Reconcile element children
      const nextIndent = nextNode.__indent;
      if (nextIndent !== prevNode.__indent) {
        setElementIndent(dom, nextIndent);
      }
      const nextFormat = nextNode.__format;
      if (nextFormat !== prevNode.__format) {
        setElementFormat(dom, nextFormat);
      }
      if (isDirty) {
        $reconcileChildrenWithDirection(prevNode, nextNode, dom);
        if (!$isRootNode(nextNode) && !nextNode.isInline()) {
          reconcileElementTerminatingLineBreak(prevNode, nextNode, dom);
        }
      }
      if ($textContentRequiresDoubleLinebreakAtEnd(nextNode)) {
        subTreeTextContent += DOUBLE_LINE_BREAK;
        editorTextContent += DOUBLE_LINE_BREAK;
      }
    } else {
      const text = nextNode.getTextContent();
      if ($isDecoratorNode(nextNode)) {
        const decorator = nextNode.decorate(activeEditor$1, activeEditorConfig);
        if (decorator !== null) {
          reconcileDecorator(key, decorator);
        }
      } else if ($isTextNode(nextNode) && !nextNode.isDirectionless()) {
        // Handle text content, for LTR, LTR cases.
        subTreeDirectionedTextContent += text;
      }
      subTreeTextContent += text;
      editorTextContent += text;
    }
    if (!activeEditorStateReadOnly && $isRootNode(nextNode) && nextNode.__cachedText !== editorTextContent) {
      // Cache the latest text content.
      const nextRootNode = nextNode.getWritable();
      nextRootNode.__cachedText = editorTextContent;
      nextNode = nextRootNode;
    }
    {
      // Freeze the node in DEV to prevent accidental mutations
      Object.freeze(nextNode);
    }
    return dom;
  }
  function reconcileDecorator(key, decorator) {
    let pendingDecorators = activeEditor$1._pendingDecorators;
    const currentDecorators = activeEditor$1._decorators;
    if (pendingDecorators === null) {
      if (currentDecorators[key] === decorator) {
        return;
      }
      pendingDecorators = cloneDecorators(activeEditor$1);
    }
    pendingDecorators[key] = decorator;
  }
  function getFirstChild(element) {
    return element.firstChild;
  }
  function getNextSibling(element) {
    let nextSibling = element.nextSibling;
    if (nextSibling !== null && nextSibling === activeEditor$1._blockCursorElement) {
      nextSibling = nextSibling.nextSibling;
    }
    return nextSibling;
  }
  function $reconcileNodeChildren(nextElement, prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, dom) {
    const prevEndIndex = prevChildrenLength - 1;
    const nextEndIndex = nextChildrenLength - 1;
    let prevChildrenSet;
    let nextChildrenSet;
    let siblingDOM = getFirstChild(dom);
    let prevIndex = 0;
    let nextIndex = 0;
    while (prevIndex <= prevEndIndex && nextIndex <= nextEndIndex) {
      const prevKey = prevChildren[prevIndex];
      const nextKey = nextChildren[nextIndex];
      if (prevKey === nextKey) {
        siblingDOM = getNextSibling($reconcileNode(nextKey, dom));
        prevIndex++;
        nextIndex++;
      } else {
        if (prevChildrenSet === undefined) {
          prevChildrenSet = new Set(prevChildren);
        }
        if (nextChildrenSet === undefined) {
          nextChildrenSet = new Set(nextChildren);
        }
        const nextHasPrevKey = nextChildrenSet.has(prevKey);
        const prevHasNextKey = prevChildrenSet.has(nextKey);
        if (!nextHasPrevKey) {
          // Remove prev
          siblingDOM = getNextSibling(getPrevElementByKeyOrThrow(prevKey));
          destroyNode(prevKey, dom);
          prevIndex++;
        } else if (!prevHasNextKey) {
          // Create next
          $createNode(nextKey, dom, siblingDOM);
          nextIndex++;
        } else {
          // Move next
          const childDOM = getElementByKeyOrThrow(activeEditor$1, nextKey);
          if (childDOM === siblingDOM) {
            siblingDOM = getNextSibling($reconcileNode(nextKey, dom));
          } else {
            if (siblingDOM != null) {
              dom.insertBefore(childDOM, siblingDOM);
            } else {
              dom.appendChild(childDOM);
            }
            $reconcileNode(nextKey, dom);
          }
          prevIndex++;
          nextIndex++;
        }
      }
      const node = activeNextNodeMap.get(nextKey);
      if (node !== null && $isTextNode(node)) {
        if (subTreeTextFormat === null) {
          subTreeTextFormat = node.getFormat();
        }
        if (subTreeTextStyle === '') {
          subTreeTextStyle = node.getStyle();
        }
      }
    }
    const appendNewChildren = prevIndex > prevEndIndex;
    const removeOldChildren = nextIndex > nextEndIndex;
    if (appendNewChildren && !removeOldChildren) {
      const previousNode = nextChildren[nextEndIndex + 1];
      const insertDOM = previousNode === undefined ? null : activeEditor$1.getElementByKey(previousNode);
      $createChildren(nextChildren, nextElement, nextIndex, nextEndIndex, dom, insertDOM);
    } else if (removeOldChildren && !appendNewChildren) {
      destroyChildren(prevChildren, prevIndex, prevEndIndex, dom);
    }
  }
  function $reconcileRoot(prevEditorState, nextEditorState, editor, dirtyType, dirtyElements, dirtyLeaves) {
    // We cache text content to make retrieval more efficient.
    // The cache must be rebuilt during reconciliation to account for any changes.
    subTreeTextContent = '';
    editorTextContent = '';
    subTreeDirectionedTextContent = '';
    // Rather than pass around a load of arguments through the stack recursively
    // we instead set them as bindings within the scope of the module.
    treatAllNodesAsDirty = dirtyType === FULL_RECONCILE;
    activeTextDirection = null;
    activeEditor$1 = editor;
    activeEditorConfig = editor._config;
    activeEditorNodes = editor._nodes;
    activeMutationListeners = activeEditor$1._listeners.mutation;
    activeDirtyElements = dirtyElements;
    activeDirtyLeaves = dirtyLeaves;
    activePrevNodeMap = prevEditorState._nodeMap;
    activeNextNodeMap = nextEditorState._nodeMap;
    activeEditorStateReadOnly = nextEditorState._readOnly;
    activePrevKeyToDOMMap = new Map(editor._keyToDOMMap);
    // We keep track of mutated nodes so we can trigger mutation
    // listeners later in the update cycle.
    const currentMutatedNodes = new Map();
    mutatedNodes = currentMutatedNodes;
    $reconcileNode('root', null);
    // We don't want a bunch of void checks throughout the scope
    // so instead we make it seem that these values are always set.
    // We also want to make sure we clear them down, otherwise we
    // can leak memory.
    // @ts-ignore
    activeEditor$1 = undefined;
    // @ts-ignore
    activeEditorNodes = undefined;
    // @ts-ignore
    activeDirtyElements = undefined;
    // @ts-ignore
    activeDirtyLeaves = undefined;
    // @ts-ignore
    activePrevNodeMap = undefined;
    // @ts-ignore
    activeNextNodeMap = undefined;
    // @ts-ignore
    activeEditorConfig = undefined;
    // @ts-ignore
    activePrevKeyToDOMMap = undefined;
    // @ts-ignore
    mutatedNodes = undefined;
    return currentMutatedNodes;
  }
  function storeDOMWithKey(key, dom, editor) {
    const keyToDOMMap = editor._keyToDOMMap;
    // @ts-ignore We intentionally add this to the Node.
    dom['__lexicalKey_' + editor._key] = key;
    keyToDOMMap.set(key, dom);
  }
  function getPrevElementByKeyOrThrow(key) {
    const element = activePrevKeyToDOMMap.get(key);
    if (element === undefined) {
      {
        throw Error(`Reconciliation: could not find DOM element for node key ${key}`);
      }
    }
    return element;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const PASS_THROUGH_COMMAND = Object.freeze({});
  const ANDROID_COMPOSITION_LATENCY = 30;
  const rootElementEvents = [['keydown', onKeyDown], ['pointerdown', onPointerDown], ['compositionstart', onCompositionStart], ['compositionend', onCompositionEnd], ['input', onInput], ['click', onClick], ['cut', PASS_THROUGH_COMMAND], ['copy', PASS_THROUGH_COMMAND], ['dragstart', PASS_THROUGH_COMMAND], ['dragover', PASS_THROUGH_COMMAND], ['dragend', PASS_THROUGH_COMMAND], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
  if (CAN_USE_BEFORE_INPUT) {
    rootElementEvents.push(['beforeinput', (event, editor) => onBeforeInput(event, editor)]);
  }
  let lastKeyDownTimeStamp = 0;
  let lastKeyCode = null;
  let lastBeforeInputInsertTextTimeStamp = 0;
  let unprocessedBeforeInputData = null;
  const rootElementsRegistered = new WeakMap();
  let isSelectionChangeFromDOMUpdate = false;
  let isSelectionChangeFromMouseDown = false;
  let isInsertLineBreak = false;
  let isFirefoxEndingComposition = false;
  let collapsedSelectionFormat = [0, '', 0, 'root', 0];

  // This function is used to determine if Lexical should attempt to override
  // the default browser behavior for insertion of text and use its own internal
  // heuristics. This is an extremely important function, and makes much of Lexical
  // work as intended between different browsers and across word, line and character
  // boundary/formats. It also is important for text replacement, node schemas and
  // composition mechanics.

  function $shouldPreventDefaultAndInsertText(selection, domTargetRange, text, timeStamp, isBeforeInput) {
    const anchor = selection.anchor;
    const focus = selection.focus;
    const anchorNode = anchor.getNode();
    const editor = getActiveEditor();
    const domSelection = getDOMSelection(editor._window);
    const domAnchorNode = domSelection !== null ? domSelection.anchorNode : null;
    const anchorKey = anchor.key;
    const backingAnchorElement = editor.getElementByKey(anchorKey);
    const textLength = text.length;
    return anchorKey !== focus.key ||
    // If we're working with a non-text node.
    !$isTextNode(anchorNode) ||
    // If we are replacing a range with a single character or grapheme, and not composing.
    (!isBeforeInput && (!CAN_USE_BEFORE_INPUT ||
    // We check to see if there has been
    // a recent beforeinput event for "textInput". If there has been one in the last
    // 50ms then we proceed as normal. However, if there is not, then this is likely
    // a dangling `input` event caused by execCommand('insertText').
    lastBeforeInputInsertTextTimeStamp < timeStamp + 50) || anchorNode.isDirty() && textLength < 2 || doesContainGrapheme(text)) && anchor.offset !== focus.offset && !anchorNode.isComposing() ||
    // Any non standard text node.
    $isTokenOrSegmented(anchorNode) ||
    // If the text length is more than a single character and we're either
    // dealing with this in "beforeinput" or where the node has already recently
    // been changed (thus is dirty).
    anchorNode.isDirty() && textLength > 1 ||
    // If the DOM selection element is not the same as the backing node during beforeinput.
    (isBeforeInput || !CAN_USE_BEFORE_INPUT) && backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== getDOMTextNode(backingAnchorElement) ||
    // If TargetRange is not the same as the DOM selection; browser trying to edit random parts
    // of the editor.
    domSelection !== null && domTargetRange !== null && (!domTargetRange.collapsed || domTargetRange.startContainer !== domSelection.anchorNode || domTargetRange.startOffset !== domSelection.anchorOffset) ||
    // Check if we're changing from bold to italics, or some other format.
    anchorNode.getFormat() !== selection.format || anchorNode.getStyle() !== selection.style ||
    // One last set of heuristics to check against.
    $shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
  }
  function shouldSkipSelectionChange(domNode, offset) {
    return domNode !== null && domNode.nodeValue !== null && domNode.nodeType === DOM_TEXT_TYPE && offset !== 0 && offset !== domNode.nodeValue.length;
  }
  function onSelectionChange(domSelection, editor, isActive) {
    const {
      anchorNode: anchorDOM,
      anchorOffset,
      focusNode: focusDOM,
      focusOffset
    } = domSelection;
    if (isSelectionChangeFromDOMUpdate) {
      isSelectionChangeFromDOMUpdate = false;

      // If native DOM selection is on a DOM element, then
      // we should continue as usual, as Lexical's selection
      // may have normalized to a better child. If the DOM
      // element is a text node, we can safely apply this
      // optimization and skip the selection change entirely.
      // We also need to check if the offset is at the boundary,
      // because in this case, we might need to normalize to a
      // sibling instead.
      if (shouldSkipSelectionChange(anchorDOM, anchorOffset) && shouldSkipSelectionChange(focusDOM, focusOffset)) {
        return;
      }
    }
    updateEditor(editor, () => {
      // Non-active editor don't need any extra logic for selection, it only needs update
      // to reconcile selection (set it to null) to ensure that only one editor has non-null selection.
      if (!isActive) {
        $setSelection(null);
        return;
      }
      if (!isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
        return;
      }
      const selection = $getSelection();

      // Update the selection format
      if ($isRangeSelection(selection)) {
        const anchor = selection.anchor;
        const anchorNode = anchor.getNode();
        if (selection.isCollapsed()) {
          // Badly interpreted range selection when collapsed - #1482
          if (domSelection.type === 'Range' && domSelection.anchorNode === domSelection.focusNode) {
            selection.dirty = true;
          }

          // If we have marked a collapsed selection format, and we're
          // within the given time range – then attempt to use that format
          // instead of getting the format from the anchor node.
          const windowEvent = getWindow(editor).event;
          const currentTimeStamp = windowEvent ? windowEvent.timeStamp : performance.now();
          const [lastFormat, lastStyle, lastOffset, lastKey, timeStamp] = collapsedSelectionFormat;
          const root = $getRoot();
          const isRootTextContentEmpty = editor.isComposing() === false && root.getTextContent() === '';
          if (currentTimeStamp < timeStamp + 200 && anchor.offset === lastOffset && anchor.key === lastKey) {
            selection.format = lastFormat;
            selection.style = lastStyle;
          } else {
            if (anchor.type === 'text') {
              if (!$isTextNode(anchorNode)) {
                throw Error(`Point.getNode() must return TextNode when type is text`);
              }
              selection.format = anchorNode.getFormat();
              selection.style = anchorNode.getStyle();
            } else if (anchor.type === 'element' && !isRootTextContentEmpty) {
              const lastNode = anchor.getNode();
              selection.style = '';
              if (lastNode instanceof ParagraphNode && lastNode.getChildrenSize() === 0) {
                selection.format = lastNode.getTextFormat();
                selection.style = lastNode.getTextStyle();
              } else {
                selection.format = 0;
              }
            }
          }
        } else {
          const anchorKey = anchor.key;
          const focus = selection.focus;
          const focusKey = focus.key;
          const nodes = selection.getNodes();
          const nodesLength = nodes.length;
          const isBackward = selection.isBackward();
          const startOffset = isBackward ? focusOffset : anchorOffset;
          const endOffset = isBackward ? anchorOffset : focusOffset;
          const startKey = isBackward ? focusKey : anchorKey;
          const endKey = isBackward ? anchorKey : focusKey;
          let combinedFormat = IS_ALL_FORMATTING;
          let hasTextNodes = false;
          for (let i = 0; i < nodesLength; i++) {
            const node = nodes[i];
            const textContentSize = node.getTextContentSize();
            if ($isTextNode(node) && textContentSize !== 0 &&
            // Exclude empty text nodes at boundaries resulting from user's selection
            !(i === 0 && node.__key === startKey && startOffset === textContentSize || i === nodesLength - 1 && node.__key === endKey && endOffset === 0)) {
              // TODO: what about style?
              hasTextNodes = true;
              combinedFormat &= node.getFormat();
              if (combinedFormat === 0) {
                break;
              }
            }
          }
          selection.format = hasTextNodes ? combinedFormat : 0;
        }
      }
      dispatchCommand(editor, SELECTION_CHANGE_COMMAND, undefined);
    });
  }

  // This is a work-around is mainly Chrome specific bug where if you select
  // the contents of an empty block, you cannot easily unselect anything.
  // This results in a tiny selection box that looks buggy/broken. This can
  // also help other browsers when selection might "appear" lost, when it
  // really isn't.
  function onClick(event, editor) {
    updateEditor(editor, () => {
      const selection = $getSelection();
      const domSelection = getDOMSelection(editor._window);
      const lastSelection = $getPreviousSelection();
      if (domSelection) {
        if ($isRangeSelection(selection)) {
          const anchor = selection.anchor;
          const anchorNode = anchor.getNode();
          if (anchor.type === 'element' && anchor.offset === 0 && selection.isCollapsed() && !$isRootNode(anchorNode) && $getRoot().getChildrenSize() === 1 && anchorNode.getTopLevelElementOrThrow().isEmpty() && lastSelection !== null && selection.is(lastSelection)) {
            domSelection.removeAllRanges();
            selection.dirty = true;
          } else if (event.detail === 3 && !selection.isCollapsed()) {
            // Tripple click causing selection to overflow into the nearest element. In that
            // case visually it looks like a single element content is selected, focus node
            // is actually at the beginning of the next element (if present) and any manipulations
            // with selection (formatting) are affecting second element as well
            const focus = selection.focus;
            const focusNode = focus.getNode();
            if (anchorNode !== focusNode) {
              if ($isElementNode(anchorNode)) {
                anchorNode.select(0);
              } else {
                anchorNode.getParentOrThrow().select(0);
              }
            }
          }
        } else if (event.pointerType === 'touch') {
          // This is used to update the selection on touch devices when the user clicks on text after a
          // node selection. See isSelectionChangeFromMouseDown for the inverse
          const domAnchorNode = domSelection.anchorNode;
          if (domAnchorNode !== null) {
            const nodeType = domAnchorNode.nodeType;
            // If the user is attempting to click selection back onto text, then
            // we should attempt create a range selection.
            // When we click on an empty paragraph node or the end of a paragraph that ends
            // with an image/poll, the nodeType will be ELEMENT_NODE
            if (nodeType === DOM_ELEMENT_TYPE || nodeType === DOM_TEXT_TYPE) {
              const newSelection = $internalCreateRangeSelection(lastSelection, domSelection, editor, event);
              $setSelection(newSelection);
            }
          }
        }
      }
      dispatchCommand(editor, CLICK_COMMAND, event);
    });
  }
  function onPointerDown(event, editor) {
    // TODO implement text drag & drop
    const target = event.target;
    const pointerType = event.pointerType;
    if (target instanceof Node && pointerType !== 'touch') {
      updateEditor(editor, () => {
        // Drag & drop should not recompute selection until mouse up; otherwise the initially
        // selected content is lost.
        if (!$isSelectionCapturedInDecorator(target)) {
          isSelectionChangeFromMouseDown = true;
        }
      });
    }
  }
  function getTargetRange(event) {
    if (!event.getTargetRanges) {
      return null;
    }
    const targetRanges = event.getTargetRanges();
    if (targetRanges.length === 0) {
      return null;
    }
    return targetRanges[0];
  }
  function $canRemoveText(anchorNode, focusNode) {
    return anchorNode !== focusNode || $isElementNode(anchorNode) || $isElementNode(focusNode) || !anchorNode.isToken() || !focusNode.isToken();
  }
  function isPossiblyAndroidKeyPress(timeStamp) {
    return lastKeyCode === 'MediaLast' && timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
  }
  function onBeforeInput(event, editor) {
    const inputType = event.inputType;
    const targetRange = getTargetRange(event);

    // We let the browser do its own thing for composition.
    if (inputType === 'deleteCompositionText' ||
    // If we're pasting in FF, we shouldn't get this event
    // as the `paste` event should have triggered, unless the
    // user has dom.event.clipboardevents.enabled disabled in
    // about:config. In that case, we need to process the
    // pasted content in the DOM mutation phase.
    IS_FIREFOX && isFirefoxClipboardEvents(editor)) {
      return;
    } else if (inputType === 'insertCompositionText') {
      return;
    }
    updateEditor(editor, () => {
      const selection = $getSelection();
      if (inputType === 'deleteContentBackward') {
        if (selection === null) {
          // Use previous selection
          const prevSelection = $getPreviousSelection();
          if (!$isRangeSelection(prevSelection)) {
            return;
          }
          $setSelection(prevSelection.clone());
        }
        if ($isRangeSelection(selection)) {
          const isSelectionAnchorSameAsFocus = selection.anchor.key === selection.focus.key;
          if (isPossiblyAndroidKeyPress(event.timeStamp) && editor.isComposing() && isSelectionAnchorSameAsFocus) {
            $setCompositionKey(null);
            lastKeyDownTimeStamp = 0;
            // Fixes an Android bug where selection flickers when backspacing
            setTimeout(() => {
              updateEditor(editor, () => {
                $setCompositionKey(null);
              });
            }, ANDROID_COMPOSITION_LATENCY);
            if ($isRangeSelection(selection)) {
              const anchorNode = selection.anchor.getNode();
              anchorNode.markDirty();
              selection.format = anchorNode.getFormat();
              if (!$isTextNode(anchorNode)) {
                throw Error(`Anchor node must be a TextNode`);
              }
              selection.style = anchorNode.getStyle();
            }
          } else {
            $setCompositionKey(null);
            event.preventDefault();
            // Chromium Android at the moment seems to ignore the preventDefault
            // on 'deleteContentBackward' and still deletes the content. Which leads
            // to multiple deletions. So we let the browser handle the deletion in this case.
            const selectedNodeText = selection.anchor.getNode().getTextContent();
            const hasSelectedAllTextInNode = selection.anchor.offset === 0 && selection.focus.offset === selectedNodeText.length;
            const shouldLetBrowserHandleDelete = IS_ANDROID_CHROME && isSelectionAnchorSameAsFocus && !hasSelectedAllTextInNode;
            if (!shouldLetBrowserHandleDelete) {
              dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
            }
          }
          return;
        }
      }
      if (!$isRangeSelection(selection)) {
        return;
      }
      const data = event.data;

      // This represents the case when two beforeinput events are triggered at the same time (without a
      // full event loop ending at input). This happens with MacOS with the default keyboard settings,
      // a combination of autocorrection + autocapitalization.
      // Having Lexical run everything in controlled mode would fix the issue without additional code
      // but this would kill the massive performance win from the most common typing event.
      // Alternatively, when this happens we can prematurely update our EditorState based on the DOM
      // content, a job that would usually be the input event's responsibility.
      if (unprocessedBeforeInputData !== null) {
        $updateSelectedTextFromDOM(false, editor, unprocessedBeforeInputData);
      }
      if ((!selection.dirty || unprocessedBeforeInputData !== null) && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode()) && targetRange !== null) {
        selection.applyDOMRange(targetRange);
      }
      unprocessedBeforeInputData = null;
      const anchor = selection.anchor;
      const focus = selection.focus;
      const anchorNode = anchor.getNode();
      const focusNode = focus.getNode();
      if (inputType === 'insertText' || inputType === 'insertTranspose') {
        if (data === '\n') {
          event.preventDefault();
          dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
        } else if (data === DOUBLE_LINE_BREAK) {
          event.preventDefault();
          dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
        } else if (data == null && event.dataTransfer) {
          // Gets around a Safari text replacement bug.
          const text = event.dataTransfer.getData('text/plain');
          event.preventDefault();
          selection.insertRawText(text);
        } else if (data != null && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, true)) {
          event.preventDefault();
          dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
        } else {
          unprocessedBeforeInputData = data;
        }
        lastBeforeInputInsertTextTimeStamp = event.timeStamp;
        return;
      }

      // Prevent the browser from carrying out
      // the input event, so we can control the
      // output.
      event.preventDefault();
      switch (inputType) {
        case 'insertFromYank':
        case 'insertFromDrop':
        case 'insertReplacementText':
          {
            dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
            break;
          }
        case 'insertFromComposition':
          {
            // This is the end of composition
            $setCompositionKey(null);
            dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
            break;
          }
        case 'insertLineBreak':
          {
            // Used for Android
            $setCompositionKey(null);
            dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
            break;
          }
        case 'insertParagraph':
          {
            // Used for Android
            $setCompositionKey(null);

            // Safari does not provide the type "insertLineBreak".
            // So instead, we need to infer it from the keyboard event.
            // We do not apply this logic to iOS to allow newline auto-capitalization
            // work without creating linebreaks when pressing Enter
            if (isInsertLineBreak && !IS_IOS) {
              isInsertLineBreak = false;
              dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
            } else {
              dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
            }
            break;
          }
        case 'insertFromPaste':
        case 'insertFromPasteAsQuotation':
          {
            dispatchCommand(editor, PASTE_COMMAND, event);
            break;
          }
        case 'deleteByComposition':
          {
            if ($canRemoveText(anchorNode, focusNode)) {
              dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
            }
            break;
          }
        case 'deleteByDrag':
        case 'deleteByCut':
          {
            dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
            break;
          }
        case 'deleteContent':
          {
            dispatchCommand(editor, DELETE_CHARACTER_COMMAND, false);
            break;
          }
        case 'deleteWordBackward':
          {
            dispatchCommand(editor, DELETE_WORD_COMMAND, true);
            break;
          }
        case 'deleteWordForward':
          {
            dispatchCommand(editor, DELETE_WORD_COMMAND, false);
            break;
          }
        case 'deleteHardLineBackward':
        case 'deleteSoftLineBackward':
          {
            dispatchCommand(editor, DELETE_LINE_COMMAND, true);
            break;
          }
        case 'deleteContentForward':
        case 'deleteHardLineForward':
        case 'deleteSoftLineForward':
          {
            dispatchCommand(editor, DELETE_LINE_COMMAND, false);
            break;
          }
        case 'formatStrikeThrough':
          {
            dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'strikethrough');
            break;
          }
        case 'formatBold':
          {
            dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'bold');
            break;
          }
        case 'formatItalic':
          {
            dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'italic');
            break;
          }
        case 'formatUnderline':
          {
            dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'underline');
            break;
          }
        case 'historyUndo':
          {
            dispatchCommand(editor, UNDO_COMMAND, undefined);
            break;
          }
        case 'historyRedo':
          {
            dispatchCommand(editor, REDO_COMMAND, undefined);
            break;
          }
        // NO-OP
      }
    });
  }

  function onInput(event, editor) {
    // We don't want the onInput to bubble, in the case of nested editors.
    event.stopPropagation();
    updateEditor(editor, () => {
      const selection = $getSelection();
      const data = event.data;
      const targetRange = getTargetRange(event);
      if (data != null && $isRangeSelection(selection) && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, false)) {
        // Given we're over-riding the default behavior, we will need
        // to ensure to disable composition before dispatching the
        // insertText command for when changing the sequence for FF.
        if (isFirefoxEndingComposition) {
          $onCompositionEndImpl(editor, data);
          isFirefoxEndingComposition = false;
        }
        const anchor = selection.anchor;
        const anchorNode = anchor.getNode();
        const domSelection = getDOMSelection(editor._window);
        if (domSelection === null) {
          return;
        }
        const isBackward = selection.isBackward();
        const startOffset = isBackward ? selection.anchor.offset : selection.focus.offset;
        const endOffset = isBackward ? selection.focus.offset : selection.anchor.offset;
        // If the content is the same as inserted, then don't dispatch an insertion.
        // Given onInput doesn't take the current selection (it uses the previous)
        // we can compare that against what the DOM currently says.
        if (!CAN_USE_BEFORE_INPUT || selection.isCollapsed() || !$isTextNode(anchorNode) || domSelection.anchorNode === null || anchorNode.getTextContent().slice(0, startOffset) + data + anchorNode.getTextContent().slice(startOffset + endOffset) !== getAnchorTextFromDOM(domSelection.anchorNode)) {
          dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
        }
        const textLength = data.length;

        // Another hack for FF, as it's possible that the IME is still
        // open, even though compositionend has already fired (sigh).
        if (IS_FIREFOX && textLength > 1 && event.inputType === 'insertCompositionText' && !editor.isComposing()) {
          selection.anchor.offset -= textLength;
        }

        // This ensures consistency on Android.
        if (!IS_SAFARI && !IS_IOS && !IS_APPLE_WEBKIT && editor.isComposing()) {
          lastKeyDownTimeStamp = 0;
          $setCompositionKey(null);
        }
      } else {
        const characterData = data !== null ? data : undefined;
        $updateSelectedTextFromDOM(false, editor, characterData);

        // onInput always fires after onCompositionEnd for FF.
        if (isFirefoxEndingComposition) {
          $onCompositionEndImpl(editor, data || undefined);
          isFirefoxEndingComposition = false;
        }
      }

      // Also flush any other mutations that might have occurred
      // since the change.
      $flushMutations();
    });
    unprocessedBeforeInputData = null;
  }
  function onCompositionStart(event, editor) {
    updateEditor(editor, () => {
      const selection = $getSelection();
      if ($isRangeSelection(selection) && !editor.isComposing()) {
        const anchor = selection.anchor;
        const node = selection.anchor.getNode();
        $setCompositionKey(anchor.key);
        if (
        // If it has been 30ms since the last keydown, then we should
        // apply the empty space heuristic. We can't do this for Safari,
        // as the keydown fires after composition start.
        event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY ||
        // FF has issues around composing multibyte characters, so we also
        // need to invoke the empty space heuristic below.
        anchor.type === 'element' || !selection.isCollapsed() || node.getFormat() !== selection.format || $isTextNode(node) && node.getStyle() !== selection.style) {
          // We insert a zero width character, ready for the composition
          // to get inserted into the new node we create. If
          // we don't do this, Safari will fail on us because
          // there is no text node matching the selection.
          dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, COMPOSITION_START_CHAR);
        }
      }
    });
  }
  function $onCompositionEndImpl(editor, data) {
    const compositionKey = editor._compositionKey;
    $setCompositionKey(null);

    // Handle termination of composition.
    if (compositionKey !== null && data != null) {
      // Composition can sometimes move to an adjacent DOM node when backspacing.
      // So check for the empty case.
      if (data === '') {
        const node = $getNodeByKey(compositionKey);
        const textNode = getDOMTextNode(editor.getElementByKey(compositionKey));
        if (textNode !== null && textNode.nodeValue !== null && $isTextNode(node)) {
          $updateTextNodeFromDOMContent(node, textNode.nodeValue, null, null, true);
        }
        return;
      }

      // Composition can sometimes be that of a new line. In which case, we need to
      // handle that accordingly.
      if (data[data.length - 1] === '\n') {
        const selection = $getSelection();
        if ($isRangeSelection(selection)) {
          // If the last character is a line break, we also need to insert
          // a line break.
          const focus = selection.focus;
          selection.anchor.set(focus.key, focus.offset, focus.type);
          dispatchCommand(editor, KEY_ENTER_COMMAND, null);
          return;
        }
      }
    }
    $updateSelectedTextFromDOM(true, editor, data);
  }
  function onCompositionEnd(event, editor) {
    // Firefox fires onCompositionEnd before onInput, but Chrome/Webkit,
    // fire onInput before onCompositionEnd. To ensure the sequence works
    // like Chrome/Webkit we use the isFirefoxEndingComposition flag to
    // defer handling of onCompositionEnd in Firefox till we have processed
    // the logic in onInput.
    if (IS_FIREFOX) {
      isFirefoxEndingComposition = true;
    } else {
      updateEditor(editor, () => {
        $onCompositionEndImpl(editor, event.data);
      });
    }
  }
  function onKeyDown(event, editor) {
    lastKeyDownTimeStamp = event.timeStamp;
    lastKeyCode = event.key;
    if (editor.isComposing()) {
      return;
    }
    const {
      key,
      shiftKey,
      ctrlKey,
      metaKey,
      altKey
    } = event;
    if (dispatchCommand(editor, KEY_DOWN_COMMAND, event)) {
      return;
    }
    if (key == null) {
      return;
    }
    if (isMoveForward(key, ctrlKey, altKey, metaKey)) {
      dispatchCommand(editor, KEY_ARROW_RIGHT_COMMAND, event);
    } else if (isMoveToEnd(key, ctrlKey, shiftKey, altKey, metaKey)) {
      dispatchCommand(editor, MOVE_TO_END, event);
    } else if (isMoveBackward(key, ctrlKey, altKey, metaKey)) {
      dispatchCommand(editor, KEY_ARROW_LEFT_COMMAND, event);
    } else if (isMoveToStart(key, ctrlKey, shiftKey, altKey, metaKey)) {
      dispatchCommand(editor, MOVE_TO_START, event);
    } else if (isMoveUp(key, ctrlKey, metaKey)) {
      dispatchCommand(editor, KEY_ARROW_UP_COMMAND, event);
    } else if (isMoveDown(key, ctrlKey, metaKey)) {
      dispatchCommand(editor, KEY_ARROW_DOWN_COMMAND, event);
    } else if (isLineBreak(key, shiftKey)) {
      isInsertLineBreak = true;
      dispatchCommand(editor, KEY_ENTER_COMMAND, event);
    } else if (isSpace(key)) {
      dispatchCommand(editor, KEY_SPACE_COMMAND, event);
    } else if (isOpenLineBreak(key, ctrlKey)) {
      event.preventDefault();
      isInsertLineBreak = true;
      dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, true);
    } else if (isParagraph(key, shiftKey)) {
      isInsertLineBreak = false;
      dispatchCommand(editor, KEY_ENTER_COMMAND, event);
    } else if (isDeleteBackward(key, altKey, metaKey, ctrlKey)) {
      if (isBackspace(key)) {
        dispatchCommand(editor, KEY_BACKSPACE_COMMAND, event);
      } else {
        event.preventDefault();
        dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
      }
    } else if (isEscape(key)) {
      dispatchCommand(editor, KEY_ESCAPE_COMMAND, event);
    } else if (isDeleteForward(key, ctrlKey, shiftKey, altKey, metaKey)) {
      if (isDelete(key)) {
        dispatchCommand(editor, KEY_DELETE_COMMAND, event);
      } else {
        event.preventDefault();
        dispatchCommand(editor, DELETE_CHARACTER_COMMAND, false);
      }
    } else if (isDeleteWordBackward(key, altKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, DELETE_WORD_COMMAND, true);
    } else if (isDeleteWordForward(key, altKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, DELETE_WORD_COMMAND, false);
    } else if (isDeleteLineBackward(key, metaKey)) {
      event.preventDefault();
      dispatchCommand(editor, DELETE_LINE_COMMAND, true);
    } else if (isDeleteLineForward(key, metaKey)) {
      event.preventDefault();
      dispatchCommand(editor, DELETE_LINE_COMMAND, false);
    } else if (isBold(key, altKey, metaKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'bold');
    } else if (isUnderline(key, altKey, metaKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'underline');
    } else if (isItalic(key, altKey, metaKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'italic');
    } else if (isTab(key, altKey, ctrlKey, metaKey)) {
      dispatchCommand(editor, KEY_TAB_COMMAND, event);
    } else if (isUndo(key, shiftKey, metaKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, UNDO_COMMAND, undefined);
    } else if (isRedo(key, shiftKey, metaKey, ctrlKey)) {
      event.preventDefault();
      dispatchCommand(editor, REDO_COMMAND, undefined);
    } else {
      const prevSelection = editor._editorState._selection;
      if ($isNodeSelection(prevSelection)) {
        if (isCopy(key, shiftKey, metaKey, ctrlKey)) {
          event.preventDefault();
          dispatchCommand(editor, COPY_COMMAND, event);
        } else if (isCut(key, shiftKey, metaKey, ctrlKey)) {
          event.preventDefault();
          dispatchCommand(editor, CUT_COMMAND, event);
        } else if (isSelectAll(key, metaKey, ctrlKey)) {
          event.preventDefault();
          dispatchCommand(editor, SELECT_ALL_COMMAND, event);
        }
        // FF does it well (no need to override behavior)
      } else if (!IS_FIREFOX && isSelectAll(key, metaKey, ctrlKey)) {
        event.preventDefault();
        dispatchCommand(editor, SELECT_ALL_COMMAND, event);
      }
    }
    if (isModifier(ctrlKey, shiftKey, altKey, metaKey)) {
      dispatchCommand(editor, KEY_MODIFIER_COMMAND, event);
    }
  }
  function getRootElementRemoveHandles(rootElement) {
    // @ts-expect-error: internal field
    let eventHandles = rootElement.__lexicalEventHandles;
    if (eventHandles === undefined) {
      eventHandles = [];
      // @ts-expect-error: internal field
      rootElement.__lexicalEventHandles = eventHandles;
    }
    return eventHandles;
  }

  // Mapping root editors to their active nested editors, contains nested editors
  // mapping only, so if root editor is selected map will have no reference to free up memory
  const activeNestedEditorsMap = new Map();
  function onDocumentSelectionChange(event) {
    const target = event.target;
    const targetWindow = target == null ? null : target.nodeType === 9 ? target.defaultView : target.ownerDocument.defaultView;
    const domSelection = getDOMSelection(targetWindow);
    if (domSelection === null) {
      return;
    }
    const nextActiveEditor = getNearestEditorFromDOMNode(domSelection.anchorNode);
    if (nextActiveEditor === null) {
      return;
    }
    if (isSelectionChangeFromMouseDown) {
      isSelectionChangeFromMouseDown = false;
      updateEditor(nextActiveEditor, () => {
        const lastSelection = $getPreviousSelection();
        const domAnchorNode = domSelection.anchorNode;
        if (domAnchorNode === null) {
          return;
        }
        const nodeType = domAnchorNode.nodeType;
        // If the user is attempting to click selection back onto text, then
        // we should attempt create a range selection.
        // When we click on an empty paragraph node or the end of a paragraph that ends
        // with an image/poll, the nodeType will be ELEMENT_NODE
        if (nodeType !== DOM_ELEMENT_TYPE && nodeType !== DOM_TEXT_TYPE) {
          return;
        }
        const newSelection = $internalCreateRangeSelection(lastSelection, domSelection, nextActiveEditor, event);
        $setSelection(newSelection);
      });
    }

    // When editor receives selection change event, we're checking if
    // it has any sibling editors (within same parent editor) that were active
    // before, and trigger selection change on it to nullify selection.
    const editors = getEditorsToPropagate(nextActiveEditor);
    const rootEditor = editors[editors.length - 1];
    const rootEditorKey = rootEditor._key;
    const activeNestedEditor = activeNestedEditorsMap.get(rootEditorKey);
    const prevActiveEditor = activeNestedEditor || rootEditor;
    if (prevActiveEditor !== nextActiveEditor) {
      onSelectionChange(domSelection, prevActiveEditor, false);
    }
    onSelectionChange(domSelection, nextActiveEditor, true);

    // If newly selected editor is nested, then add it to the map, clean map otherwise
    if (nextActiveEditor !== rootEditor) {
      activeNestedEditorsMap.set(rootEditorKey, nextActiveEditor);
    } else if (activeNestedEditor) {
      activeNestedEditorsMap.delete(rootEditorKey);
    }
  }
  function stopLexicalPropagation(event) {
    // We attach a special property to ensure the same event doesn't re-fire
    // for parent editors.
    // @ts-ignore
    event._lexicalHandled = true;
  }
  function hasStoppedLexicalPropagation(event) {
    // @ts-ignore
    const stopped = event._lexicalHandled === true;
    return stopped;
  }
  function addRootElementEvents(rootElement, editor) {
    // We only want to have a single global selectionchange event handler, shared
    // between all editor instances.
    const doc = rootElement.ownerDocument;
    const documentRootElementsCount = rootElementsRegistered.get(doc);
    if (documentRootElementsCount === undefined || documentRootElementsCount < 1) {
      doc.addEventListener('selectionchange', onDocumentSelectionChange);
    }
    rootElementsRegistered.set(doc, (documentRootElementsCount || 0) + 1);

    // @ts-expect-error: internal field
    rootElement.__lexicalEditor = editor;
    const removeHandles = getRootElementRemoveHandles(rootElement);
    for (let i = 0; i < rootElementEvents.length; i++) {
      const [eventName, onEvent] = rootElementEvents[i];
      const eventHandler = typeof onEvent === 'function' ? event => {
        if (hasStoppedLexicalPropagation(event)) {
          return;
        }
        stopLexicalPropagation(event);
        if (editor.isEditable() || eventName === 'click') {
          onEvent(event, editor);
        }
      } : event => {
        if (hasStoppedLexicalPropagation(event)) {
          return;
        }
        stopLexicalPropagation(event);
        const isEditable = editor.isEditable();
        switch (eventName) {
          case 'cut':
            return isEditable && dispatchCommand(editor, CUT_COMMAND, event);
          case 'copy':
            return dispatchCommand(editor, COPY_COMMAND, event);
          case 'paste':
            return isEditable && dispatchCommand(editor, PASTE_COMMAND, event);
          case 'dragstart':
            return isEditable && dispatchCommand(editor, DRAGSTART_COMMAND, event);
          case 'dragover':
            return isEditable && dispatchCommand(editor, DRAGOVER_COMMAND, event);
          case 'dragend':
            return isEditable && dispatchCommand(editor, DRAGEND_COMMAND, event);
          case 'focus':
            return isEditable && dispatchCommand(editor, FOCUS_COMMAND, event);
          case 'blur':
            {
              return isEditable && dispatchCommand(editor, BLUR_COMMAND, event);
            }
          case 'drop':
            return isEditable && dispatchCommand(editor, DROP_COMMAND, event);
        }
      };
      rootElement.addEventListener(eventName, eventHandler);
      removeHandles.push(() => {
        rootElement.removeEventListener(eventName, eventHandler);
      });
    }
  }
  function removeRootElementEvents(rootElement) {
    const doc = rootElement.ownerDocument;
    const documentRootElementsCount = rootElementsRegistered.get(doc);
    if (!(documentRootElementsCount !== undefined)) {
      throw Error(`Root element not registered`);
    } // We only want to have a single global selectionchange event handler, shared
    // between all editor instances.
    const newCount = documentRootElementsCount - 1;
    if (!(newCount >= 0)) {
      throw Error(`Root element count less than 0`);
    }
    rootElementsRegistered.set(doc, newCount);
    if (newCount === 0) {
      doc.removeEventListener('selectionchange', onDocumentSelectionChange);
    }
    const editor = getEditorPropertyFromDOMNode(rootElement);
    if (isLexicalEditor(editor)) {
      cleanActiveNestedEditorsMap(editor);
      // @ts-expect-error: internal field
      rootElement.__lexicalEditor = null;
    } else if (editor) {
      {
        throw Error(`Attempted to remove event handlers from a node that does not belong to this build of Lexical`);
      }
    }
    const removeHandles = getRootElementRemoveHandles(rootElement);
    for (let i = 0; i < removeHandles.length; i++) {
      removeHandles[i]();
    }

    // @ts-expect-error: internal field
    rootElement.__lexicalEventHandles = [];
  }
  function cleanActiveNestedEditorsMap(editor) {
    if (editor._parentEditor !== null) {
      // For nested editor cleanup map if this editor was marked as active
      const editors = getEditorsToPropagate(editor);
      const rootEditor = editors[editors.length - 1];
      const rootEditorKey = rootEditor._key;
      if (activeNestedEditorsMap.get(rootEditorKey) === editor) {
        activeNestedEditorsMap.delete(rootEditorKey);
      }
    } else {
      // For top-level editors cleanup map
      activeNestedEditorsMap.delete(editor._key);
    }
  }
  function markSelectionChangeFromDOMUpdate() {
    isSelectionChangeFromDOMUpdate = true;
  }
  function markCollapsedSelectionFormat(format, style, offset, key, timeStamp) {
    collapsedSelectionFormat = [format, style, offset, key, timeStamp];
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function $removeNode(nodeToRemove, restoreSelection, preserveEmptyParent) {
    errorOnReadOnly();
    const key = nodeToRemove.__key;
    const parent = nodeToRemove.getParent();
    if (parent === null) {
      return;
    }
    const selection = $maybeMoveChildrenSelectionToParent(nodeToRemove);
    let selectionMoved = false;
    if ($isRangeSelection(selection) && restoreSelection) {
      const anchor = selection.anchor;
      const focus = selection.focus;
      if (anchor.key === key) {
        moveSelectionPointToSibling(anchor, nodeToRemove, parent, nodeToRemove.getPreviousSibling(), nodeToRemove.getNextSibling());
        selectionMoved = true;
      }
      if (focus.key === key) {
        moveSelectionPointToSibling(focus, nodeToRemove, parent, nodeToRemove.getPreviousSibling(), nodeToRemove.getNextSibling());
        selectionMoved = true;
      }
    } else if ($isNodeSelection(selection) && restoreSelection && nodeToRemove.isSelected()) {
      nodeToRemove.selectPrevious();
    }
    if ($isRangeSelection(selection) && restoreSelection && !selectionMoved) {
      // Doing this is O(n) so lets avoid it unless we need to do it
      const index = nodeToRemove.getIndexWithinParent();
      removeFromParent(nodeToRemove);
      $updateElementSelectionOnCreateDeleteNode(selection, parent, index, -1);
    } else {
      removeFromParent(nodeToRemove);
    }
    if (!preserveEmptyParent && !$isRootOrShadowRoot(parent) && !parent.canBeEmpty() && parent.isEmpty()) {
      $removeNode(parent, restoreSelection);
    }
    if (restoreSelection && $isRootNode(parent) && parent.isEmpty()) {
      parent.selectEnd();
    }
  }
  class LexicalNode {
    // Allow us to look up the type including static props

    /** @internal */

    /** @internal */
    //@ts-ignore We set the key in the constructor.

    /** @internal */

    /** @internal */

    /** @internal */

    // Flow doesn't support abstract classes unfortunately, so we can't _force_
    // subclasses of Node to implement statics. All subclasses of Node should have
    // a static getType and clone method though. We define getType and clone here so we can call it
    // on any  Node, and we throw this error by default since the subclass should provide
    // their own implementation.
    /**
     * Returns the string type of this node. Every node must
     * implement this and it MUST BE UNIQUE amongst nodes registered
     * on the editor.
     *
     */
    static getType() {
      {
        throw Error(`LexicalNode: Node ${this.name} does not implement .getType().`);
      }
    }

    /**
     * Clones this node, creating a new node with a different key
     * and adding it to the EditorState (but not attaching it anywhere!). All nodes must
     * implement this method.
     *
     */
    static clone(_data) {
      {
        throw Error(`LexicalNode: Node ${this.name} does not implement .clone().`);
      }
    }

    /**
     * Perform any state updates on the clone of prevNode that are not already
     * handled by the constructor call in the static clone method. If you have
     * state to update in your clone that is not handled directly by the
     * constructor, it is advisable to override this method but it is required
     * to include a call to `super.afterCloneFrom(prevNode)` in your
     * implementation. This is only intended to be called by
     * {@link $cloneWithProperties} function or via a super call.
     *
     * @example
     * ```ts
     * class ClassesTextNode extends TextNode {
     *   // Not shown: static getType, static importJSON, exportJSON, createDOM, updateDOM
     *   __classes = new Set<string>();
     *   static clone(node: ClassesTextNode): ClassesTextNode {
     *     // The inherited TextNode constructor is used here, so
     *     // classes is not set by this method.
     *     return new ClassesTextNode(node.__text, node.__key);
     *   }
     *   afterCloneFrom(node: this): void {
     *     // This calls TextNode.afterCloneFrom and LexicalNode.afterCloneFrom
     *     // for necessary state updates
     *     super.afterCloneFrom(node);
     *     this.__addClasses(node.__classes);
     *   }
     *   // This method is a private implementation detail, it is not
     *   // suitable for the public API because it does not call getWritable
     *   __addClasses(classNames: Iterable<string>): this {
     *     for (const className of classNames) {
     *       this.__classes.add(className);
     *     }
     *     return this;
     *   }
     *   addClass(...classNames: string[]): this {
     *     return this.getWritable().__addClasses(classNames);
     *   }
     *   removeClass(...classNames: string[]): this {
     *     const node = this.getWritable();
     *     for (const className of classNames) {
     *       this.__classes.delete(className);
     *     }
     *     return this;
     *   }
     *   getClasses(): Set<string> {
     *     return this.getLatest().__classes;
     *   }
     * }
     * ```
     *
     */
    afterCloneFrom(prevNode) {
      this.__parent = prevNode.__parent;
      this.__next = prevNode.__next;
      this.__prev = prevNode.__prev;
    }

    // eslint-disable-next-line @typescript-eslint/no-explicit-any

    constructor(key) {
      this.__type = this.constructor.getType();
      this.__parent = null;
      this.__prev = null;
      this.__next = null;
      $setNodeKey(this, key);
      {
        if (this.__type !== 'root') {
          errorOnReadOnly();
          errorOnTypeKlassMismatch(this.__type, this.constructor);
        }
      }
    }
    // Getters and Traversers

    /**
     * Returns the string type of this node.
     */
    getType() {
      return this.__type;
    }
    isInline() {
      {
        throw Error(`LexicalNode: Node ${this.constructor.name} does not implement .isInline().`);
      }
    }

    /**
     * Returns true if there is a path between this node and the RootNode, false otherwise.
     * This is a way of determining if the node is "attached" EditorState. Unattached nodes
     * won't be reconciled and will ultimatelt be cleaned up by the Lexical GC.
     */
    isAttached() {
      let nodeKey = this.__key;
      while (nodeKey !== null) {
        if (nodeKey === 'root') {
          return true;
        }
        const node = $getNodeByKey(nodeKey);
        if (node === null) {
          break;
        }
        nodeKey = node.__parent;
      }
      return false;
    }

    /**
     * Returns true if this node is contained within the provided Selection., false otherwise.
     * Relies on the algorithms implemented in {@link BaseSelection.getNodes} to determine
     * what's included.
     *
     * @param selection - The selection that we want to determine if the node is in.
     */
    isSelected(selection) {
      const targetSelection = selection || $getSelection();
      if (targetSelection == null) {
        return false;
      }
      const isSelected = targetSelection.getNodes().some(n => n.__key === this.__key);
      if ($isTextNode(this)) {
        return isSelected;
      }
      // For inline images inside of element nodes.
      // Without this change the image will be selected if the cursor is before or after it.
      const isElementRangeSelection = $isRangeSelection(targetSelection) && targetSelection.anchor.type === 'element' && targetSelection.focus.type === 'element';
      if (isElementRangeSelection) {
        if (targetSelection.isCollapsed()) {
          return false;
        }
        const parentNode = this.getParent();
        if ($isDecoratorNode(this) && this.isInline() && parentNode) {
          const firstPoint = targetSelection.isBackward() ? targetSelection.focus : targetSelection.anchor;
          const firstElement = firstPoint.getNode();
          if (firstPoint.offset === firstElement.getChildrenSize() && firstElement.is(parentNode) && firstElement.getLastChildOrThrow().is(this)) {
            return false;
          }
        }
      }
      return isSelected;
    }

    /**
     * Returns this nodes key.
     */
    getKey() {
      // Key is stable between copies
      return this.__key;
    }

    /**
     * Returns the zero-based index of this node within the parent.
     */
    getIndexWithinParent() {
      const parent = this.getParent();
      if (parent === null) {
        return -1;
      }
      let node = parent.getFirstChild();
      let index = 0;
      while (node !== null) {
        if (this.is(node)) {
          return index;
        }
        index++;
        node = node.getNextSibling();
      }
      return -1;
    }

    /**
     * Returns the parent of this node, or null if none is found.
     */
    getParent() {
      const parent = this.getLatest().__parent;
      if (parent === null) {
        return null;
      }
      return $getNodeByKey(parent);
    }

    /**
     * Returns the parent of this node, or throws if none is found.
     */
    getParentOrThrow() {
      const parent = this.getParent();
      if (parent === null) {
        {
          throw Error(`Expected node ${this.__key} to have a parent.`);
        }
      }
      return parent;
    }

    /**
     * Returns the highest (in the EditorState tree)
     * non-root ancestor of this node, or null if none is found. See {@link lexical!$isRootOrShadowRoot}
     * for more information on which Elements comprise "roots".
     */
    getTopLevelElement() {
      let node = this;
      while (node !== null) {
        const parent = node.getParent();
        if ($isRootOrShadowRoot(parent)) {
          if (!($isElementNode(node) || node === this && $isDecoratorNode(node))) {
            throw Error(`Children of root nodes must be elements or decorators`);
          }
          return node;
        }
        node = parent;
      }
      return null;
    }

    /**
     * Returns the highest (in the EditorState tree)
     * non-root ancestor of this node, or throws if none is found. See {@link lexical!$isRootOrShadowRoot}
     * for more information on which Elements comprise "roots".
     */
    getTopLevelElementOrThrow() {
      const parent = this.getTopLevelElement();
      if (parent === null) {
        {
          throw Error(`Expected node ${this.__key} to have a top parent element.`);
        }
      }
      return parent;
    }

    /**
     * Returns a list of the every ancestor of this node,
     * all the way up to the RootNode.
     *
     */
    getParents() {
      const parents = [];
      let node = this.getParent();
      while (node !== null) {
        parents.push(node);
        node = node.getParent();
      }
      return parents;
    }

    /**
     * Returns a list of the keys of every ancestor of this node,
     * all the way up to the RootNode.
     *
     */
    getParentKeys() {
      const parents = [];
      let node = this.getParent();
      while (node !== null) {
        parents.push(node.__key);
        node = node.getParent();
      }
      return parents;
    }

    /**
     * Returns the "previous" siblings - that is, the node that comes
     * before this one in the same parent.
     *
     */
    getPreviousSibling() {
      const self = this.getLatest();
      const prevKey = self.__prev;
      return prevKey === null ? null : $getNodeByKey(prevKey);
    }

    /**
     * Returns the "previous" siblings - that is, the nodes that come between
     * this one and the first child of it's parent, inclusive.
     *
     */
    getPreviousSiblings() {
      const siblings = [];
      const parent = this.getParent();
      if (parent === null) {
        return siblings;
      }
      let node = parent.getFirstChild();
      while (node !== null) {
        if (node.is(this)) {
          break;
        }
        siblings.push(node);
        node = node.getNextSibling();
      }
      return siblings;
    }

    /**
     * Returns the "next" siblings - that is, the node that comes
     * after this one in the same parent
     *
     */
    getNextSibling() {
      const self = this.getLatest();
      const nextKey = self.__next;
      return nextKey === null ? null : $getNodeByKey(nextKey);
    }

    /**
     * Returns all "next" siblings - that is, the nodes that come between this
     * one and the last child of it's parent, inclusive.
     *
     */
    getNextSiblings() {
      const siblings = [];
      let node = this.getNextSibling();
      while (node !== null) {
        siblings.push(node);
        node = node.getNextSibling();
      }
      return siblings;
    }

    /**
     * Returns the closest common ancestor of this node and the provided one or null
     * if one cannot be found.
     *
     * @param node - the other node to find the common ancestor of.
     */
    getCommonAncestor(node) {
      const a = this.getParents();
      const b = node.getParents();
      if ($isElementNode(this)) {
        a.unshift(this);
      }
      if ($isElementNode(node)) {
        b.unshift(node);
      }
      const aLength = a.length;
      const bLength = b.length;
      if (aLength === 0 || bLength === 0 || a[aLength - 1] !== b[bLength - 1]) {
        return null;
      }
      const bSet = new Set(b);
      for (let i = 0; i < aLength; i++) {
        const ancestor = a[i];
        if (bSet.has(ancestor)) {
          return ancestor;
        }
      }
      return null;
    }

    /**
     * Returns true if the provided node is the exact same one as this node, from Lexical's perspective.
     * Always use this instead of referential equality.
     *
     * @param object - the node to perform the equality comparison on.
     */
    is(object) {
      if (object == null) {
        return false;
      }
      return this.__key === object.__key;
    }

    /**
     * Returns true if this node logical precedes the target node in the editor state.
     *
     * @param targetNode - the node we're testing to see if it's after this one.
     */
    isBefore(targetNode) {
      if (this === targetNode) {
        return false;
      }
      if (targetNode.isParentOf(this)) {
        return true;
      }
      if (this.isParentOf(targetNode)) {
        return false;
      }
      const commonAncestor = this.getCommonAncestor(targetNode);
      let indexA = 0;
      let indexB = 0;
      let node = this;
      while (true) {
        const parent = node.getParentOrThrow();
        if (parent === commonAncestor) {
          indexA = node.getIndexWithinParent();
          break;
        }
        node = parent;
      }
      node = targetNode;
      while (true) {
        const parent = node.getParentOrThrow();
        if (parent === commonAncestor) {
          indexB = node.getIndexWithinParent();
          break;
        }
        node = parent;
      }
      return indexA < indexB;
    }

    /**
     * Returns true if this node is the parent of the target node, false otherwise.
     *
     * @param targetNode - the would-be child node.
     */
    isParentOf(targetNode) {
      const key = this.__key;
      if (key === targetNode.__key) {
        return false;
      }
      let node = targetNode;
      while (node !== null) {
        if (node.__key === key) {
          return true;
        }
        node = node.getParent();
      }
      return false;
    }

    // TO-DO: this function can be simplified a lot
    /**
     * Returns a list of nodes that are between this node and
     * the target node in the EditorState.
     *
     * @param targetNode - the node that marks the other end of the range of nodes to be returned.
     */
    getNodesBetween(targetNode) {
      const isBefore = this.isBefore(targetNode);
      const nodes = [];
      const visited = new Set();
      let node = this;
      while (true) {
        if (node === null) {
          break;
        }
        const key = node.__key;
        if (!visited.has(key)) {
          visited.add(key);
          nodes.push(node);
        }
        if (node === targetNode) {
          break;
        }
        const child = $isElementNode(node) ? isBefore ? node.getFirstChild() : node.getLastChild() : null;
        if (child !== null) {
          node = child;
          continue;
        }
        const nextSibling = isBefore ? node.getNextSibling() : node.getPreviousSibling();
        if (nextSibling !== null) {
          node = nextSibling;
          continue;
        }
        const parent = node.getParentOrThrow();
        if (!visited.has(parent.__key)) {
          nodes.push(parent);
        }
        if (parent === targetNode) {
          break;
        }
        let parentSibling = null;
        let ancestor = parent;
        do {
          if (ancestor === null) {
            {
              throw Error(`getNodesBetween: ancestor is null`);
            }
          }
          parentSibling = isBefore ? ancestor.getNextSibling() : ancestor.getPreviousSibling();
          ancestor = ancestor.getParent();
          if (ancestor !== null) {
            if (parentSibling === null && !visited.has(ancestor.__key)) {
              nodes.push(ancestor);
            }
          } else {
            break;
          }
        } while (parentSibling === null);
        node = parentSibling;
      }
      if (!isBefore) {
        nodes.reverse();
      }
      return nodes;
    }

    /**
     * Returns true if this node has been marked dirty during this update cycle.
     *
     */
    isDirty() {
      const editor = getActiveEditor();
      const dirtyLeaves = editor._dirtyLeaves;
      return dirtyLeaves !== null && dirtyLeaves.has(this.__key);
    }

    /**
     * Returns the latest version of the node from the active EditorState.
     * This is used to avoid getting values from stale node references.
     *
     */
    getLatest() {
      const latest = $getNodeByKey(this.__key);
      if (latest === null) {
        {
          throw Error(`Lexical node does not exist in active editor state. Avoid using the same node references between nested closures from editorState.read/editor.update.`);
        }
      }
      return latest;
    }

    /**
     * Returns a mutable version of the node using {@link $cloneWithProperties}
     * if necessary. Will throw an error if called outside of a Lexical Editor
     * {@link LexicalEditor.update} callback.
     *
     */
    getWritable() {
      errorOnReadOnly();
      const editorState = getActiveEditorState();
      const editor = getActiveEditor();
      const nodeMap = editorState._nodeMap;
      const key = this.__key;
      // Ensure we get the latest node from pending state
      const latestNode = this.getLatest();
      const cloneNotNeeded = editor._cloneNotNeeded;
      const selection = $getSelection();
      if (selection !== null) {
        selection.setCachedNodes(null);
      }
      if (cloneNotNeeded.has(key)) {
        // Transforms clear the dirty node set on each iteration to keep track on newly dirty nodes
        internalMarkNodeAsDirty(latestNode);
        return latestNode;
      }
      const mutableNode = $cloneWithProperties(latestNode);
      cloneNotNeeded.add(key);
      internalMarkNodeAsDirty(mutableNode);
      // Update reference in node map
      nodeMap.set(key, mutableNode);
      return mutableNode;
    }

    /**
     * Returns the text content of the node. Override this for
     * custom nodes that should have a representation in plain text
     * format (for copy + paste, for example)
     *
     */
    getTextContent() {
      return '';
    }

    /**
     * Returns the length of the string produced by calling getTextContent on this node.
     *
     */
    getTextContentSize() {
      return this.getTextContent().length;
    }

    // View

    /**
     * Called during the reconciliation process to determine which nodes
     * to insert into the DOM for this Lexical Node.
     *
     * This method must return exactly one HTMLElement. Nested elements are not supported.
     *
     * Do not attempt to update the Lexical EditorState during this phase of the update lifecyle.
     *
     * @param _config - allows access to things like the EditorTheme (to apply classes) during reconciliation.
     * @param _editor - allows access to the editor for context during reconciliation.
     *
     * */
    createDOM(_config, _editor) {
      {
        throw Error(`createDOM: base method not extended`);
      }
    }

    /**
     * Called when a node changes and should update the DOM
     * in whatever way is necessary to make it align with any changes that might
     * have happened during the update.
     *
     * Returning "true" here will cause lexical to unmount and recreate the DOM node
     * (by calling createDOM). You would need to do this if the element tag changes,
     * for instance.
     *
     * */
    updateDOM(_prevNode, _dom, _config) {
      {
        throw Error(`updateDOM: base method not extended`);
      }
    }

    /**
     * Controls how the this node is serialized to HTML. This is important for
     * copy and paste between Lexical and non-Lexical editors, or Lexical editors with different namespaces,
     * in which case the primary transfer format is HTML. It's also important if you're serializing
     * to HTML for any other reason via {@link @lexical/html!$generateHtmlFromNodes}. You could
     * also use this method to build your own HTML renderer.
     *
     * */
    exportDOM(editor) {
      const element = this.createDOM(editor._config, editor);
      return {
        element
      };
    }

    /**
     * Controls how the this node is serialized to JSON. This is important for
     * copy and paste between Lexical editors sharing the same namespace. It's also important
     * if you're serializing to JSON for persistent storage somewhere.
     * See [Serialization & Deserialization](https://lexical.dev/docs/concepts/serialization#lexical---html).
     *
     * */
    exportJSON() {
      {
        throw Error(`exportJSON: base method not extended`);
      }
    }

    /**
     * Controls how the this node is deserialized from JSON. This is usually boilerplate,
     * but provides an abstraction between the node implementation and serialized interface that can
     * be important if you ever make breaking changes to a node schema (by adding or removing properties).
     * See [Serialization & Deserialization](https://lexical.dev/docs/concepts/serialization#lexical---html).
     *
     * */
    static importJSON(_serializedNode) {
      {
        throw Error(`LexicalNode: Node ${this.name} does not implement .importJSON().`);
      }
    }
    /**
     * @experimental
     *
     * Registers the returned function as a transform on the node during
     * Editor initialization. Most such use cases should be addressed via
     * the {@link LexicalEditor.registerNodeTransform} API.
     *
     * Experimental - use at your own risk.
     */
    static transform() {
      return null;
    }

    // Setters and mutators

    /**
     * Removes this LexicalNode from the EditorState. If the node isn't re-inserted
     * somewhere, the Lexical garbage collector will eventually clean it up.
     *
     * @param preserveEmptyParent - If falsy, the node's parent will be removed if
     * it's empty after the removal operation. This is the default behavior, subject to
     * other node heuristics such as {@link ElementNode#canBeEmpty}
     * */
    remove(preserveEmptyParent) {
      $removeNode(this, true, preserveEmptyParent);
    }

    /**
     * Replaces this LexicalNode with the provided node, optionally transferring the children
     * of the replaced node to the replacing node.
     *
     * @param replaceWith - The node to replace this one with.
     * @param includeChildren - Whether or not to transfer the children of this node to the replacing node.
     * */
    replace(replaceWith, includeChildren) {
      errorOnReadOnly();
      let selection = $getSelection();
      if (selection !== null) {
        selection = selection.clone();
      }
      errorOnInsertTextNodeOnRoot(this, replaceWith);
      const self = this.getLatest();
      const toReplaceKey = this.__key;
      const key = replaceWith.__key;
      const writableReplaceWith = replaceWith.getWritable();
      const writableParent = this.getParentOrThrow().getWritable();
      const size = writableParent.__size;
      removeFromParent(writableReplaceWith);
      const prevSibling = self.getPreviousSibling();
      const nextSibling = self.getNextSibling();
      const prevKey = self.__prev;
      const nextKey = self.__next;
      const parentKey = self.__parent;
      $removeNode(self, false, true);
      if (prevSibling === null) {
        writableParent.__first = key;
      } else {
        const writablePrevSibling = prevSibling.getWritable();
        writablePrevSibling.__next = key;
      }
      writableReplaceWith.__prev = prevKey;
      if (nextSibling === null) {
        writableParent.__last = key;
      } else {
        const writableNextSibling = nextSibling.getWritable();
        writableNextSibling.__prev = key;
      }
      writableReplaceWith.__next = nextKey;
      writableReplaceWith.__parent = parentKey;
      writableParent.__size = size;
      if (includeChildren) {
        if (!($isElementNode(this) && $isElementNode(writableReplaceWith))) {
          throw Error(`includeChildren should only be true for ElementNodes`);
        }
        this.getChildren().forEach(child => {
          writableReplaceWith.append(child);
        });
      }
      if ($isRangeSelection(selection)) {
        $setSelection(selection);
        const anchor = selection.anchor;
        const focus = selection.focus;
        if (anchor.key === toReplaceKey) {
          $moveSelectionPointToEnd(anchor, writableReplaceWith);
        }
        if (focus.key === toReplaceKey) {
          $moveSelectionPointToEnd(focus, writableReplaceWith);
        }
      }
      if ($getCompositionKey() === toReplaceKey) {
        $setCompositionKey(key);
      }
      return writableReplaceWith;
    }

    /**
     * Inserts a node after this LexicalNode (as the next sibling).
     *
     * @param nodeToInsert - The node to insert after this one.
     * @param restoreSelection - Whether or not to attempt to resolve the
     * selection to the appropriate place after the operation is complete.
     * */
    insertAfter(nodeToInsert, restoreSelection = true) {
      errorOnReadOnly();
      errorOnInsertTextNodeOnRoot(this, nodeToInsert);
      const writableSelf = this.getWritable();
      const writableNodeToInsert = nodeToInsert.getWritable();
      const oldParent = writableNodeToInsert.getParent();
      const selection = $getSelection();
      let elementAnchorSelectionOnNode = false;
      let elementFocusSelectionOnNode = false;
      if (oldParent !== null) {
        // TODO: this is O(n), can we improve?
        const oldIndex = nodeToInsert.getIndexWithinParent();
        removeFromParent(writableNodeToInsert);
        if ($isRangeSelection(selection)) {
          const oldParentKey = oldParent.__key;
          const anchor = selection.anchor;
          const focus = selection.focus;
          elementAnchorSelectionOnNode = anchor.type === 'element' && anchor.key === oldParentKey && anchor.offset === oldIndex + 1;
          elementFocusSelectionOnNode = focus.type === 'element' && focus.key === oldParentKey && focus.offset === oldIndex + 1;
        }
      }
      const nextSibling = this.getNextSibling();
      const writableParent = this.getParentOrThrow().getWritable();
      const insertKey = writableNodeToInsert.__key;
      const nextKey = writableSelf.__next;
      if (nextSibling === null) {
        writableParent.__last = insertKey;
      } else {
        const writableNextSibling = nextSibling.getWritable();
        writableNextSibling.__prev = insertKey;
      }
      writableParent.__size++;
      writableSelf.__next = insertKey;
      writableNodeToInsert.__next = nextKey;
      writableNodeToInsert.__prev = writableSelf.__key;
      writableNodeToInsert.__parent = writableSelf.__parent;
      if (restoreSelection && $isRangeSelection(selection)) {
        const index = this.getIndexWithinParent();
        $updateElementSelectionOnCreateDeleteNode(selection, writableParent, index + 1);
        const writableParentKey = writableParent.__key;
        if (elementAnchorSelectionOnNode) {
          selection.anchor.set(writableParentKey, index + 2, 'element');
        }
        if (elementFocusSelectionOnNode) {
          selection.focus.set(writableParentKey, index + 2, 'element');
        }
      }
      return nodeToInsert;
    }

    /**
     * Inserts a node before this LexicalNode (as the previous sibling).
     *
     * @param nodeToInsert - The node to insert before this one.
     * @param restoreSelection - Whether or not to attempt to resolve the
     * selection to the appropriate place after the operation is complete.
     * */
    insertBefore(nodeToInsert, restoreSelection = true) {
      errorOnReadOnly();
      errorOnInsertTextNodeOnRoot(this, nodeToInsert);
      const writableSelf = this.getWritable();
      const writableNodeToInsert = nodeToInsert.getWritable();
      const insertKey = writableNodeToInsert.__key;
      removeFromParent(writableNodeToInsert);
      const prevSibling = this.getPreviousSibling();
      const writableParent = this.getParentOrThrow().getWritable();
      const prevKey = writableSelf.__prev;
      // TODO: this is O(n), can we improve?
      const index = this.getIndexWithinParent();
      if (prevSibling === null) {
        writableParent.__first = insertKey;
      } else {
        const writablePrevSibling = prevSibling.getWritable();
        writablePrevSibling.__next = insertKey;
      }
      writableParent.__size++;
      writableSelf.__prev = insertKey;
      writableNodeToInsert.__prev = prevKey;
      writableNodeToInsert.__next = writableSelf.__key;
      writableNodeToInsert.__parent = writableSelf.__parent;
      const selection = $getSelection();
      if (restoreSelection && $isRangeSelection(selection)) {
        const parent = this.getParentOrThrow();
        $updateElementSelectionOnCreateDeleteNode(selection, parent, index);
      }
      return nodeToInsert;
    }

    /**
     * Whether or not this node has a required parent. Used during copy + paste operations
     * to normalize nodes that would otherwise be orphaned. For example, ListItemNodes without
     * a ListNode parent or TextNodes with a ParagraphNode parent.
     *
     * */
    isParentRequired() {
      return false;
    }

    /**
     * The creation logic for any required parent. Should be implemented if {@link isParentRequired} returns true.
     *
     * */
    createParentElementNode() {
      return $createParagraphNode();
    }
    selectStart() {
      return this.selectPrevious();
    }
    selectEnd() {
      return this.selectNext(0, 0);
    }

    /**
     * Moves selection to the previous sibling of this node, at the specified offsets.
     *
     * @param anchorOffset - The anchor offset for selection.
     * @param focusOffset -  The focus offset for selection
     * */
    selectPrevious(anchorOffset, focusOffset) {
      errorOnReadOnly();
      const prevSibling = this.getPreviousSibling();
      const parent = this.getParentOrThrow();
      if (prevSibling === null) {
        return parent.select(0, 0);
      }
      if ($isElementNode(prevSibling)) {
        return prevSibling.select();
      } else if (!$isTextNode(prevSibling)) {
        const index = prevSibling.getIndexWithinParent() + 1;
        return parent.select(index, index);
      }
      return prevSibling.select(anchorOffset, focusOffset);
    }

    /**
     * Moves selection to the next sibling of this node, at the specified offsets.
     *
     * @param anchorOffset - The anchor offset for selection.
     * @param focusOffset -  The focus offset for selection
     * */
    selectNext(anchorOffset, focusOffset) {
      errorOnReadOnly();
      const nextSibling = this.getNextSibling();
      const parent = this.getParentOrThrow();
      if (nextSibling === null) {
        return parent.select();
      }
      if ($isElementNode(nextSibling)) {
        return nextSibling.select(0, 0);
      } else if (!$isTextNode(nextSibling)) {
        const index = nextSibling.getIndexWithinParent();
        return parent.select(index, index);
      }
      return nextSibling.select(anchorOffset, focusOffset);
    }

    /**
     * Marks a node dirty, triggering transforms and
     * forcing it to be reconciled during the update cycle.
     *
     * */
    markDirty() {
      this.getWritable();
    }
  }
  function errorOnTypeKlassMismatch(type, klass) {
    const registeredNode = getActiveEditor()._nodes.get(type);
    // Common error - split in its own invariant
    if (registeredNode === undefined) {
      {
        throw Error(`Create node: Attempted to create node ${klass.name} that was not configured to be used on the editor.`);
      }
    }
    const editorKlass = registeredNode.klass;
    if (editorKlass !== klass) {
      {
        throw Error(`Create node: Type ${type} in node ${klass.name} does not match registered node ${editorKlass.name} with the same type`);
      }
    }
  }

  /**
   * Insert a series of nodes after this LexicalNode (as next siblings)
   *
   * @param firstToInsert - The first node to insert after this one.
   * @param lastToInsert - The last node to insert after this one. Must be a
   * later sibling of FirstNode. If not provided, it will be its last sibling.
   */
  function insertRangeAfter(node, firstToInsert, lastToInsert) {
    const lastToInsert2 = firstToInsert.getParentOrThrow().getLastChild();
    let current = firstToInsert;
    const nodesToInsert = [firstToInsert];
    while (current !== lastToInsert2) {
      if (!current.getNextSibling()) {
        {
          throw Error(`insertRangeAfter: lastToInsert must be a later sibling of firstToInsert`);
        }
      }
      current = current.getNextSibling();
      nodesToInsert.push(current);
    }
    let currentNode = node;
    for (const nodeToInsert of nodesToInsert) {
      currentNode = currentNode.insertAfter(nodeToInsert);
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class LineBreakNode extends LexicalNode {
    static getType() {
      return 'linebreak';
    }
    static clone(node) {
      return new LineBreakNode(node.__key);
    }
    constructor(key) {
      super(key);
    }
    getTextContent() {
      return '\n';
    }
    createDOM() {
      return document.createElement('br');
    }
    updateDOM() {
      return false;
    }
    static importDOM() {
      return {
        br: node => {
          if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) {
            return null;
          }
          return {
            conversion: $convertLineBreakElement,
            priority: 0
          };
        }
      };
    }
    static importJSON(serializedLineBreakNode) {
      return $createLineBreakNode();
    }
    exportJSON() {
      return {
        type: 'linebreak',
        version: 1
      };
    }
  }
  function $convertLineBreakElement(node) {
    return {
      node: $createLineBreakNode()
    };
  }
  function $createLineBreakNode() {
    return $applyNodeReplacement(new LineBreakNode());
  }
  function $isLineBreakNode(node) {
    return node instanceof LineBreakNode;
  }
  function isOnlyChildInBlockNode(node) {
    const parentElement = node.parentElement;
    if (parentElement !== null && isBlockDomNode(parentElement)) {
      const firstChild = parentElement.firstChild;
      if (firstChild === node || firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild)) {
        const lastChild = parentElement.lastChild;
        if (lastChild === node || lastChild.previousSibling === node && isWhitespaceDomTextNode(lastChild)) {
          return true;
        }
      }
    }
    return false;
  }
  function isLastChildInBlockNode(node) {
    const parentElement = node.parentElement;
    if (parentElement !== null && isBlockDomNode(parentElement)) {
      // check if node is first child, because only childs dont count
      const firstChild = parentElement.firstChild;
      if (firstChild === node || firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild)) {
        return false;
      }

      // check if its last child
      const lastChild = parentElement.lastChild;
      if (lastChild === node || lastChild.previousSibling === node && isWhitespaceDomTextNode(lastChild)) {
        return true;
      }
    }
    return false;
  }
  function isWhitespaceDomTextNode(node) {
    return node.nodeType === DOM_TEXT_TYPE && /^( |\t|\r?\n)+$/.test(node.textContent || '');
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function getElementOuterTag(node, format) {
    if (format & IS_CODE) {
      return 'code';
    }
    if (format & IS_HIGHLIGHT) {
      return 'mark';
    }
    if (format & IS_SUBSCRIPT) {
      return 'sub';
    }
    if (format & IS_SUPERSCRIPT) {
      return 'sup';
    }
    return null;
  }
  function getElementInnerTag(node, format) {
    if (format & IS_BOLD) {
      return 'strong';
    }
    if (format & IS_ITALIC) {
      return 'em';
    }
    return 'span';
  }
  function setTextThemeClassNames(tag, prevFormat, nextFormat, dom, textClassNames) {
    const domClassList = dom.classList;
    // Firstly we handle the base theme.
    let classNames = getCachedClassNameArray(textClassNames, 'base');
    if (classNames !== undefined) {
      domClassList.add(...classNames);
    }
    // Secondly we handle the special case: underline + strikethrough.
    // We have to do this as we need a way to compose the fact that
    // the same CSS property will need to be used: text-decoration.
    // In an ideal world we shouldn't have to do this, but there's no
    // easy workaround for many atomic CSS systems today.
    classNames = getCachedClassNameArray(textClassNames, 'underlineStrikethrough');
    let hasUnderlineStrikethrough = false;
    const prevUnderlineStrikethrough = prevFormat & IS_UNDERLINE && prevFormat & IS_STRIKETHROUGH;
    const nextUnderlineStrikethrough = nextFormat & IS_UNDERLINE && nextFormat & IS_STRIKETHROUGH;
    if (classNames !== undefined) {
      if (nextUnderlineStrikethrough) {
        hasUnderlineStrikethrough = true;
        if (!prevUnderlineStrikethrough) {
          domClassList.add(...classNames);
        }
      } else if (prevUnderlineStrikethrough) {
        domClassList.remove(...classNames);
      }
    }
    for (const key in TEXT_TYPE_TO_FORMAT) {
      const format = key;
      const flag = TEXT_TYPE_TO_FORMAT[format];
      classNames = getCachedClassNameArray(textClassNames, key);
      if (classNames !== undefined) {
        if (nextFormat & flag) {
          if (hasUnderlineStrikethrough && (key === 'underline' || key === 'strikethrough')) {
            if (prevFormat & flag) {
              domClassList.remove(...classNames);
            }
            continue;
          }
          if ((prevFormat & flag) === 0 || prevUnderlineStrikethrough && key === 'underline' || key === 'strikethrough') {
            domClassList.add(...classNames);
          }
        } else if (prevFormat & flag) {
          domClassList.remove(...classNames);
        }
      }
    }
  }
  function diffComposedText(a, b) {
    const aLength = a.length;
    const bLength = b.length;
    let left = 0;
    let right = 0;
    while (left < aLength && left < bLength && a[left] === b[left]) {
      left++;
    }
    while (right + left < aLength && right + left < bLength && a[aLength - right - 1] === b[bLength - right - 1]) {
      right++;
    }
    return [left, aLength - left - right, b.slice(left, bLength - right)];
  }
  function setTextContent(nextText, dom, node) {
    const firstChild = dom.firstChild;
    const isComposing = node.isComposing();
    // Always add a suffix if we're composing a node
    const suffix = isComposing ? COMPOSITION_SUFFIX : '';
    const text = nextText + suffix;
    if (firstChild == null) {
      dom.textContent = text;
    } else {
      const nodeValue = firstChild.nodeValue;
      if (nodeValue !== text) {
        if (isComposing || IS_FIREFOX) {
          // We also use the diff composed text for general text in FF to avoid
          // the spellcheck red line from flickering.
          const [index, remove, insert] = diffComposedText(nodeValue, text);
          if (remove !== 0) {
            // @ts-expect-error
            firstChild.deleteData(index, remove);
          }
          // @ts-expect-error
          firstChild.insertData(index, insert);
        } else {
          firstChild.nodeValue = text;
        }
      }
    }
  }
  function createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
    setTextContent(text, innerDOM, node);
    const theme = config.theme;
    // Apply theme class names
    const textClassNames = theme.text;
    if (textClassNames !== undefined) {
      setTextThemeClassNames(innerTag, 0, format, innerDOM, textClassNames);
    }
  }
  function wrapElementWith(element, tag) {
    const el = document.createElement(tag);
    el.appendChild(element);
    return el;
  }

  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging

  /** @noInheritDoc */
  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
  class TextNode extends LexicalNode {
    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    static getType() {
      return 'text';
    }
    static clone(node) {
      return new TextNode(node.__text, node.__key);
    }
    afterCloneFrom(prevNode) {
      super.afterCloneFrom(prevNode);
      this.__format = prevNode.__format;
      this.__style = prevNode.__style;
      this.__mode = prevNode.__mode;
      this.__detail = prevNode.__detail;
    }
    constructor(text, key) {
      super(key);
      this.__text = text;
      this.__format = 0;
      this.__style = '';
      this.__mode = 0;
      this.__detail = 0;
    }

    /**
     * Returns a 32-bit integer that represents the TextFormatTypes currently applied to the
     * TextNode. You probably don't want to use this method directly - consider using TextNode.hasFormat instead.
     *
     * @returns a number representing the format of the text node.
     */
    getFormat() {
      const self = this.getLatest();
      return self.__format;
    }

    /**
     * Returns a 32-bit integer that represents the TextDetailTypes currently applied to the
     * TextNode. You probably don't want to use this method directly - consider using TextNode.isDirectionless
     * or TextNode.isUnmergeable instead.
     *
     * @returns a number representing the detail of the text node.
     */
    getDetail() {
      const self = this.getLatest();
      return self.__detail;
    }

    /**
     * Returns the mode (TextModeType) of the TextNode, which may be "normal", "token", or "segmented"
     *
     * @returns TextModeType.
     */
    getMode() {
      const self = this.getLatest();
      return TEXT_TYPE_TO_MODE[self.__mode];
    }

    /**
     * Returns the styles currently applied to the node. This is analogous to CSSText in the DOM.
     *
     * @returns CSSText-like string of styles applied to the underlying DOM node.
     */
    getStyle() {
      const self = this.getLatest();
      return self.__style;
    }

    /**
     * Returns whether or not the node is in "token" mode. TextNodes in token mode can be navigated through character-by-character
     * with a RangeSelection, but are deleted as a single entity (not invdividually by character).
     *
     * @returns true if the node is in token mode, false otherwise.
     */
    isToken() {
      const self = this.getLatest();
      return self.__mode === IS_TOKEN;
    }

    /**
     *
     * @returns true if Lexical detects that an IME or other 3rd-party script is attempting to
     * mutate the TextNode, false otherwise.
     */
    isComposing() {
      return this.__key === $getCompositionKey();
    }

    /**
     * Returns whether or not the node is in "segemented" mode. TextNodes in segemented mode can be navigated through character-by-character
     * with a RangeSelection, but are deleted in space-delimited "segments".
     *
     * @returns true if the node is in segmented mode, false otherwise.
     */
    isSegmented() {
      const self = this.getLatest();
      return self.__mode === IS_SEGMENTED;
    }
    /**
     * Returns whether or not the node is "directionless". Directionless nodes don't respect changes between RTL and LTR modes.
     *
     * @returns true if the node is directionless, false otherwise.
     */
    isDirectionless() {
      const self = this.getLatest();
      return (self.__detail & IS_DIRECTIONLESS) !== 0;
    }
    /**
     * Returns whether or not the node is unmergeable. In some scenarios, Lexical tries to merge
     * adjacent TextNodes into a single TextNode. If a TextNode is unmergeable, this won't happen.
     *
     * @returns true if the node is unmergeable, false otherwise.
     */
    isUnmergeable() {
      const self = this.getLatest();
      return (self.__detail & IS_UNMERGEABLE) !== 0;
    }

    /**
     * Returns whether or not the node has the provided format applied. Use this with the human-readable TextFormatType
     * string values to get the format of a TextNode.
     *
     * @param type - the TextFormatType to check for.
     *
     * @returns true if the node has the provided format, false otherwise.
     */
    hasFormat(type) {
      const formatFlag = TEXT_TYPE_TO_FORMAT[type];
      return (this.getFormat() & formatFlag) !== 0;
    }

    /**
     * Returns whether or not the node is simple text. Simple text is defined as a TextNode that has the string type "text"
     * (i.e., not a subclass) and has no mode applied to it (i.e., not segmented or token).
     *
     * @returns true if the node is simple text, false otherwise.
     */
    isSimpleText() {
      return this.__type === 'text' && this.__mode === 0;
    }

    /**
     * Returns the text content of the node as a string.
     *
     * @returns a string representing the text content of the node.
     */
    getTextContent() {
      const self = this.getLatest();
      return self.__text;
    }

    /**
     * Returns the format flags applied to the node as a 32-bit integer.
     *
     * @returns a number representing the TextFormatTypes applied to the node.
     */
    getFormatFlags(type, alignWithFormat) {
      const self = this.getLatest();
      const format = self.__format;
      return toggleTextFormatType(format, type, alignWithFormat);
    }

    /**
     *
     * @returns true if the text node supports font styling, false otherwise.
     */
    canHaveFormat() {
      return true;
    }

    // View

    createDOM(config, editor) {
      const format = this.__format;
      const outerTag = getElementOuterTag(this, format);
      const innerTag = getElementInnerTag(this, format);
      const tag = outerTag === null ? innerTag : outerTag;
      const dom = document.createElement(tag);
      let innerDOM = dom;
      if (this.hasFormat('code')) {
        dom.setAttribute('spellcheck', 'false');
      }
      if (outerTag !== null) {
        innerDOM = document.createElement(innerTag);
        dom.appendChild(innerDOM);
      }
      const text = this.__text;
      createTextInnerDOM(innerDOM, this, innerTag, format, text, config);
      const style = this.__style;
      if (style !== '') {
        dom.style.cssText = style;
      }
      return dom;
    }
    updateDOM(prevNode, dom, config) {
      const nextText = this.__text;
      const prevFormat = prevNode.__format;
      const nextFormat = this.__format;
      const prevOuterTag = getElementOuterTag(this, prevFormat);
      const nextOuterTag = getElementOuterTag(this, nextFormat);
      const prevInnerTag = getElementInnerTag(this, prevFormat);
      const nextInnerTag = getElementInnerTag(this, nextFormat);
      const prevTag = prevOuterTag === null ? prevInnerTag : prevOuterTag;
      const nextTag = nextOuterTag === null ? nextInnerTag : nextOuterTag;
      if (prevTag !== nextTag) {
        return true;
      }
      if (prevOuterTag === nextOuterTag && prevInnerTag !== nextInnerTag) {
        // should always be an element
        const prevInnerDOM = dom.firstChild;
        if (prevInnerDOM == null) {
          {
            throw Error(`updateDOM: prevInnerDOM is null or undefined`);
          }
        }
        const nextInnerDOM = document.createElement(nextInnerTag);
        createTextInnerDOM(nextInnerDOM, this, nextInnerTag, nextFormat, nextText, config);
        dom.replaceChild(nextInnerDOM, prevInnerDOM);
        return false;
      }
      let innerDOM = dom;
      if (nextOuterTag !== null) {
        if (prevOuterTag !== null) {
          innerDOM = dom.firstChild;
          if (innerDOM == null) {
            {
              throw Error(`updateDOM: innerDOM is null or undefined`);
            }
          }
        }
      }
      setTextContent(nextText, innerDOM, this);
      const theme = config.theme;
      // Apply theme class names
      const textClassNames = theme.text;
      if (textClassNames !== undefined && prevFormat !== nextFormat) {
        setTextThemeClassNames(nextInnerTag, prevFormat, nextFormat, innerDOM, textClassNames);
      }
      const prevStyle = prevNode.__style;
      const nextStyle = this.__style;
      if (prevStyle !== nextStyle) {
        dom.style.cssText = nextStyle;
      }
      return false;
    }
    static importDOM() {
      return {
        '#text': () => ({
          conversion: $convertTextDOMNode,
          priority: 0
        }),
        b: () => ({
          conversion: convertBringAttentionToElement,
          priority: 0
        }),
        code: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        em: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        i: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        s: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        span: () => ({
          conversion: convertSpanElement,
          priority: 0
        }),
        strong: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        sub: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        sup: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        }),
        u: () => ({
          conversion: convertTextFormatElement,
          priority: 0
        })
      };
    }
    static importJSON(serializedNode) {
      const node = $createTextNode(serializedNode.text);
      node.setFormat(serializedNode.format);
      node.setDetail(serializedNode.detail);
      node.setMode(serializedNode.mode);
      node.setStyle(serializedNode.style);
      return node;
    }

    // This improves Lexical's basic text output in copy+paste plus
    // for headless mode where people might use Lexical to generate
    // HTML content and not have the ability to use CSS classes.
    exportDOM(editor) {
      let {
        element
      } = super.exportDOM(editor);
      if (!(element !== null && isHTMLElement(element))) {
        throw Error(`Expected TextNode createDOM to always return a HTMLElement`);
      }
      element.style.whiteSpace = 'pre-wrap';
      // This is the only way to properly add support for most clients,
      // even if it's semantically incorrect to have to resort to using
      // <b>, <u>, <s>, <i> elements.
      if (this.hasFormat('bold')) {
        element = wrapElementWith(element, 'b');
      }
      if (this.hasFormat('italic')) {
        element = wrapElementWith(element, 'i');
      }
      if (this.hasFormat('strikethrough')) {
        element = wrapElementWith(element, 's');
      }
      if (this.hasFormat('underline')) {
        element = wrapElementWith(element, 'u');
      }
      return {
        element
      };
    }
    exportJSON() {
      return {
        detail: this.getDetail(),
        format: this.getFormat(),
        mode: this.getMode(),
        style: this.getStyle(),
        text: this.getTextContent(),
        type: 'text',
        version: 1
      };
    }

    // Mutators
    selectionTransform(prevSelection, nextSelection) {
      return;
    }

    /**
     * Sets the node format to the provided TextFormatType or 32-bit integer. Note that the TextFormatType
     * version of the argument can only specify one format and doing so will remove all other formats that
     * may be applied to the node. For toggling behavior, consider using {@link TextNode.toggleFormat}
     *
     * @param format - TextFormatType or 32-bit integer representing the node format.
     *
     * @returns this TextNode.
     * // TODO 0.12 This should just be a `string`.
     */
    setFormat(format) {
      const self = this.getWritable();
      self.__format = typeof format === 'string' ? TEXT_TYPE_TO_FORMAT[format] : format;
      return self;
    }

    /**
     * Sets the node detail to the provided TextDetailType or 32-bit integer. Note that the TextDetailType
     * version of the argument can only specify one detail value and doing so will remove all other detail values that
     * may be applied to the node. For toggling behavior, consider using {@link TextNode.toggleDirectionless}
     * or {@link TextNode.toggleUnmergeable}
     *
     * @param detail - TextDetailType or 32-bit integer representing the node detail.
     *
     * @returns this TextNode.
     * // TODO 0.12 This should just be a `string`.
     */
    setDetail(detail) {
      const self = this.getWritable();
      self.__detail = typeof detail === 'string' ? DETAIL_TYPE_TO_DETAIL[detail] : detail;
      return self;
    }

    /**
     * Sets the node style to the provided CSSText-like string. Set this property as you
     * would an HTMLElement style attribute to apply inline styles to the underlying DOM Element.
     *
     * @param style - CSSText to be applied to the underlying HTMLElement.
     *
     * @returns this TextNode.
     */
    setStyle(style) {
      const self = this.getWritable();
      self.__style = style;
      return self;
    }

    /**
     * Applies the provided format to this TextNode if it's not present. Removes it if it's present.
     * The subscript and superscript formats are mutually exclusive.
     * Prefer using this method to turn specific formats on and off.
     *
     * @param type - TextFormatType to toggle.
     *
     * @returns this TextNode.
     */
    toggleFormat(type) {
      const format = this.getFormat();
      const newFormat = toggleTextFormatType(format, type, null);
      return this.setFormat(newFormat);
    }

    /**
     * Toggles the directionless detail value of the node. Prefer using this method over setDetail.
     *
     * @returns this TextNode.
     */
    toggleDirectionless() {
      const self = this.getWritable();
      self.__detail ^= IS_DIRECTIONLESS;
      return self;
    }

    /**
     * Toggles the unmergeable detail value of the node. Prefer using this method over setDetail.
     *
     * @returns this TextNode.
     */
    toggleUnmergeable() {
      const self = this.getWritable();
      self.__detail ^= IS_UNMERGEABLE;
      return self;
    }

    /**
     * Sets the mode of the node.
     *
     * @returns this TextNode.
     */
    setMode(type) {
      const mode = TEXT_MODE_TO_TYPE[type];
      if (this.__mode === mode) {
        return this;
      }
      const self = this.getWritable();
      self.__mode = mode;
      return self;
    }

    /**
     * Sets the text content of the node.
     *
     * @param text - the string to set as the text value of the node.
     *
     * @returns this TextNode.
     */
    setTextContent(text) {
      if (this.__text === text) {
        return this;
      }
      const self = this.getWritable();
      self.__text = text;
      return self;
    }

    /**
     * Sets the current Lexical selection to be a RangeSelection with anchor and focus on this TextNode at the provided offsets.
     *
     * @param _anchorOffset - the offset at which the Selection anchor will be placed.
     * @param _focusOffset - the offset at which the Selection focus will be placed.
     *
     * @returns the new RangeSelection.
     */
    select(_anchorOffset, _focusOffset) {
      errorOnReadOnly();
      let anchorOffset = _anchorOffset;
      let focusOffset = _focusOffset;
      const selection = $getSelection();
      const text = this.getTextContent();
      const key = this.__key;
      if (typeof text === 'string') {
        const lastOffset = text.length;
        if (anchorOffset === undefined) {
          anchorOffset = lastOffset;
        }
        if (focusOffset === undefined) {
          focusOffset = lastOffset;
        }
      } else {
        anchorOffset = 0;
        focusOffset = 0;
      }
      if (!$isRangeSelection(selection)) {
        return $internalMakeRangeSelection(key, anchorOffset, key, focusOffset, 'text', 'text');
      } else {
        const compositionKey = $getCompositionKey();
        if (compositionKey === selection.anchor.key || compositionKey === selection.focus.key) {
          $setCompositionKey(key);
        }
        selection.setTextNodeRange(this, anchorOffset, this, focusOffset);
      }
      return selection;
    }
    selectStart() {
      return this.select(0, 0);
    }
    selectEnd() {
      const size = this.getTextContentSize();
      return this.select(size, size);
    }

    /**
     * Inserts the provided text into this TextNode at the provided offset, deleting the number of characters
     * specified. Can optionally calculate a new selection after the operation is complete.
     *
     * @param offset - the offset at which the splice operation should begin.
     * @param delCount - the number of characters to delete, starting from the offset.
     * @param newText - the text to insert into the TextNode at the offset.
     * @param moveSelection - optional, whether or not to move selection to the end of the inserted substring.
     *
     * @returns this TextNode.
     */
    spliceText(offset, delCount, newText, moveSelection) {
      const writableSelf = this.getWritable();
      const text = writableSelf.__text;
      const handledTextLength = newText.length;
      let index = offset;
      if (index < 0) {
        index = handledTextLength + index;
        if (index < 0) {
          index = 0;
        }
      }
      const selection = $getSelection();
      if (moveSelection && $isRangeSelection(selection)) {
        const newOffset = offset + handledTextLength;
        selection.setTextNodeRange(writableSelf, newOffset, writableSelf, newOffset);
      }
      const updatedText = text.slice(0, index) + newText + text.slice(index + delCount);
      writableSelf.__text = updatedText;
      return writableSelf;
    }

    /**
     * This method is meant to be overriden by TextNode subclasses to control the behavior of those nodes
     * when a user event would cause text to be inserted before them in the editor. If true, Lexical will attempt
     * to insert text into this node. If false, it will insert the text in a new sibling node.
     *
     * @returns true if text can be inserted before the node, false otherwise.
     */
    canInsertTextBefore() {
      return true;
    }

    /**
     * This method is meant to be overriden by TextNode subclasses to control the behavior of those nodes
     * when a user event would cause text to be inserted after them in the editor. If true, Lexical will attempt
     * to insert text into this node. If false, it will insert the text in a new sibling node.
     *
     * @returns true if text can be inserted after the node, false otherwise.
     */
    canInsertTextAfter() {
      return true;
    }

    /**
     * Splits this TextNode at the provided character offsets, forming new TextNodes from the substrings
     * formed by the split, and inserting those new TextNodes into the editor, replacing the one that was split.
     *
     * @param splitOffsets - rest param of the text content character offsets at which this node should be split.
     *
     * @returns an Array containing the newly-created TextNodes.
     */
    splitText(...splitOffsets) {
      errorOnReadOnly();
      const self = this.getLatest();
      const textContent = self.getTextContent();
      const key = self.__key;
      const compositionKey = $getCompositionKey();
      const offsetsSet = new Set(splitOffsets);
      const parts = [];
      const textLength = textContent.length;
      let string = '';
      for (let i = 0; i < textLength; i++) {
        if (string !== '' && offsetsSet.has(i)) {
          parts.push(string);
          string = '';
        }
        string += textContent[i];
      }
      if (string !== '') {
        parts.push(string);
      }
      const partsLength = parts.length;
      if (partsLength === 0) {
        return [];
      } else if (parts[0] === textContent) {
        return [self];
      }
      const firstPart = parts[0];
      const parent = self.getParent();
      let writableNode;
      const format = self.getFormat();
      const style = self.getStyle();
      const detail = self.__detail;
      let hasReplacedSelf = false;
      if (self.isSegmented()) {
        // Create a new TextNode
        writableNode = $createTextNode(firstPart);
        writableNode.__format = format;
        writableNode.__style = style;
        writableNode.__detail = detail;
        hasReplacedSelf = true;
      } else {
        // For the first part, update the existing node
        writableNode = self.getWritable();
        writableNode.__text = firstPart;
      }

      // Handle selection
      const selection = $getSelection();

      // Then handle all other parts
      const splitNodes = [writableNode];
      let textSize = firstPart.length;
      for (let i = 1; i < partsLength; i++) {
        const part = parts[i];
        const partSize = part.length;
        const sibling = $createTextNode(part).getWritable();
        sibling.__format = format;
        sibling.__style = style;
        sibling.__detail = detail;
        const siblingKey = sibling.__key;
        const nextTextSize = textSize + partSize;
        if ($isRangeSelection(selection)) {
          const anchor = selection.anchor;
          const focus = selection.focus;
          if (anchor.key === key && anchor.type === 'text' && anchor.offset > textSize && anchor.offset <= nextTextSize) {
            anchor.key = siblingKey;
            anchor.offset -= textSize;
            selection.dirty = true;
          }
          if (focus.key === key && focus.type === 'text' && focus.offset > textSize && focus.offset <= nextTextSize) {
            focus.key = siblingKey;
            focus.offset -= textSize;
            selection.dirty = true;
          }
        }
        if (compositionKey === key) {
          $setCompositionKey(siblingKey);
        }
        textSize = nextTextSize;
        splitNodes.push(sibling);
      }

      // Insert the nodes into the parent's children
      if (parent !== null) {
        internalMarkSiblingsAsDirty(this);
        const writableParent = parent.getWritable();
        const insertionIndex = this.getIndexWithinParent();
        if (hasReplacedSelf) {
          writableParent.splice(insertionIndex, 0, splitNodes);
          this.remove();
        } else {
          writableParent.splice(insertionIndex, 1, splitNodes);
        }
        if ($isRangeSelection(selection)) {
          $updateElementSelectionOnCreateDeleteNode(selection, parent, insertionIndex, partsLength - 1);
        }
      }
      return splitNodes;
    }

    /**
     * Merges the target TextNode into this TextNode, removing the target node.
     *
     * @param target - the TextNode to merge into this one.
     *
     * @returns this TextNode.
     */
    mergeWithSibling(target) {
      const isBefore = target === this.getPreviousSibling();
      if (!isBefore && target !== this.getNextSibling()) {
        {
          throw Error(`mergeWithSibling: sibling must be a previous or next sibling`);
        }
      }
      const key = this.__key;
      const targetKey = target.__key;
      const text = this.__text;
      const textLength = text.length;
      const compositionKey = $getCompositionKey();
      if (compositionKey === targetKey) {
        $setCompositionKey(key);
      }
      const selection = $getSelection();
      if ($isRangeSelection(selection)) {
        const anchor = selection.anchor;
        const focus = selection.focus;
        if (anchor !== null && anchor.key === targetKey) {
          adjustPointOffsetForMergedSibling(anchor, isBefore, key, target, textLength);
          selection.dirty = true;
        }
        if (focus !== null && focus.key === targetKey) {
          adjustPointOffsetForMergedSibling(focus, isBefore, key, target, textLength);
          selection.dirty = true;
        }
      }
      const targetText = target.__text;
      const newText = isBefore ? targetText + text : text + targetText;
      this.setTextContent(newText);
      const writableSelf = this.getWritable();
      target.remove();
      return writableSelf;
    }

    /**
     * This method is meant to be overriden by TextNode subclasses to control the behavior of those nodes
     * when used with the registerLexicalTextEntity function. If you're using registerLexicalTextEntity, the
     * node class that you create and replace matched text with should return true from this method.
     *
     * @returns true if the node is to be treated as a "text entity", false otherwise.
     */
    isTextEntity() {
      return false;
    }
  }
  function convertSpanElement(domNode) {
    // domNode is a <span> since we matched it by nodeName
    const span = domNode;
    const style = span.style;
    return {
      forChild: applyTextFormatFromStyle(style),
      node: null
    };
  }
  function convertBringAttentionToElement(domNode) {
    // domNode is a <b> since we matched it by nodeName
    const b = domNode;
    // Google Docs wraps all copied HTML in a <b> with font-weight normal
    const hasNormalFontWeight = b.style.fontWeight === 'normal';
    return {
      forChild: applyTextFormatFromStyle(b.style, hasNormalFontWeight ? undefined : 'bold'),
      node: null
    };
  }
  const preParentCache = new WeakMap();
  function isNodePre(node) {
    return node.nodeName === 'PRE' || node.nodeType === DOM_ELEMENT_TYPE && node.style !== undefined && node.style.whiteSpace !== undefined && node.style.whiteSpace.startsWith('pre');
  }
  function findParentPreDOMNode(node) {
    let cached;
    let parent = node.parentNode;
    const visited = [node];
    while (parent !== null && (cached = preParentCache.get(parent)) === undefined && !isNodePre(parent)) {
      visited.push(parent);
      parent = parent.parentNode;
    }
    const resultNode = cached === undefined ? parent : cached;
    for (let i = 0; i < visited.length; i++) {
      preParentCache.set(visited[i], resultNode);
    }
    return resultNode;
  }
  function $convertTextDOMNode(domNode) {
    const domNode_ = domNode;
    const parentDom = domNode.parentElement;
    if (!(parentDom !== null)) {
      throw Error(`Expected parentElement of Text not to be null`);
    }
    let textContent = domNode_.textContent || '';
    // No collapse and preserve segment break for pre, pre-wrap and pre-line
    if (findParentPreDOMNode(domNode_) !== null) {
      const parts = textContent.split(/(\r?\n|\t)/);
      const nodes = [];
      const length = parts.length;
      for (let i = 0; i < length; i++) {
        const part = parts[i];
        if (part === '\n' || part === '\r\n') {
          nodes.push($createLineBreakNode());
        } else if (part === '\t') {
          nodes.push($createTabNode());
        } else if (part !== '') {
          nodes.push($createTextNode(part));
        }
      }
      return {
        node: nodes
      };
    }
    textContent = textContent.replace(/\r/g, '').replace(/[ \t\n]+/g, ' ');
    if (textContent === '') {
      return {
        node: null
      };
    }
    if (textContent[0] === ' ') {
      // Traverse backward while in the same line. If content contains new line or tab -> pontential
      // delete, other elements can borrow from this one. Deletion depends on whether it's also the
      // last space (see next condition: textContent[textContent.length - 1] === ' '))
      let previousText = domNode_;
      let isStartOfLine = true;
      while (previousText !== null && (previousText = findTextInLine(previousText, false)) !== null) {
        const previousTextContent = previousText.textContent || '';
        if (previousTextContent.length > 0) {
          if (/[ \t\n]$/.test(previousTextContent)) {
            textContent = textContent.slice(1);
          }
          isStartOfLine = false;
          break;
        }
      }
      if (isStartOfLine) {
        textContent = textContent.slice(1);
      }
    }
    if (textContent[textContent.length - 1] === ' ') {
      // Traverse forward while in the same line, preserve if next inline will require a space
      let nextText = domNode_;
      let isEndOfLine = true;
      while (nextText !== null && (nextText = findTextInLine(nextText, true)) !== null) {
        const nextTextContent = (nextText.textContent || '').replace(/^( |\t|\r?\n)+/, '');
        if (nextTextContent.length > 0) {
          isEndOfLine = false;
          break;
        }
      }
      if (isEndOfLine) {
        textContent = textContent.slice(0, textContent.length - 1);
      }
    }
    if (textContent === '') {
      return {
        node: null
      };
    }
    return {
      node: $createTextNode(textContent)
    };
  }
  function findTextInLine(text, forward) {
    let node = text;
    // eslint-disable-next-line no-constant-condition
    while (true) {
      let sibling;
      while ((sibling = forward ? node.nextSibling : node.previousSibling) === null) {
        const parentElement = node.parentElement;
        if (parentElement === null) {
          return null;
        }
        node = parentElement;
      }
      node = sibling;
      if (node.nodeType === DOM_ELEMENT_TYPE) {
        const display = node.style.display;
        if (display === '' && !isInlineDomNode(node) || display !== '' && !display.startsWith('inline')) {
          return null;
        }
      }
      let descendant = node;
      while ((descendant = forward ? node.firstChild : node.lastChild) !== null) {
        node = descendant;
      }
      if (node.nodeType === DOM_TEXT_TYPE) {
        return node;
      } else if (node.nodeName === 'BR') {
        return null;
      }
    }
  }
  const nodeNameToTextFormat = {
    code: 'code',
    em: 'italic',
    i: 'italic',
    s: 'strikethrough',
    strong: 'bold',
    sub: 'subscript',
    sup: 'superscript',
    u: 'underline'
  };
  function convertTextFormatElement(domNode) {
    const format = nodeNameToTextFormat[domNode.nodeName.toLowerCase()];
    if (format === undefined) {
      return {
        node: null
      };
    }
    return {
      forChild: applyTextFormatFromStyle(domNode.style, format),
      node: null
    };
  }
  function $createTextNode(text = '') {
    return $applyNodeReplacement(new TextNode(text));
  }
  function $isTextNode(node) {
    return node instanceof TextNode;
  }
  function applyTextFormatFromStyle(style, shouldApply) {
    const fontWeight = style.fontWeight;
    const textDecoration = style.textDecoration.split(' ');
    // Google Docs uses span tags + font-weight for bold text
    const hasBoldFontWeight = fontWeight === '700' || fontWeight === 'bold';
    // Google Docs uses span tags + text-decoration: line-through for strikethrough text
    const hasLinethroughTextDecoration = textDecoration.includes('line-through');
    // Google Docs uses span tags + font-style for italic text
    const hasItalicFontStyle = style.fontStyle === 'italic';
    // Google Docs uses span tags + text-decoration: underline for underline text
    const hasUnderlineTextDecoration = textDecoration.includes('underline');
    // Google Docs uses span tags + vertical-align to specify subscript and superscript
    const verticalAlign = style.verticalAlign;
    return lexicalNode => {
      if (!$isTextNode(lexicalNode)) {
        return lexicalNode;
      }
      if (hasBoldFontWeight && !lexicalNode.hasFormat('bold')) {
        lexicalNode.toggleFormat('bold');
      }
      if (hasLinethroughTextDecoration && !lexicalNode.hasFormat('strikethrough')) {
        lexicalNode.toggleFormat('strikethrough');
      }
      if (hasItalicFontStyle && !lexicalNode.hasFormat('italic')) {
        lexicalNode.toggleFormat('italic');
      }
      if (hasUnderlineTextDecoration && !lexicalNode.hasFormat('underline')) {
        lexicalNode.toggleFormat('underline');
      }
      if (verticalAlign === 'sub' && !lexicalNode.hasFormat('subscript')) {
        lexicalNode.toggleFormat('subscript');
      }
      if (verticalAlign === 'super' && !lexicalNode.hasFormat('superscript')) {
        lexicalNode.toggleFormat('superscript');
      }
      if (shouldApply && !lexicalNode.hasFormat(shouldApply)) {
        lexicalNode.toggleFormat(shouldApply);
      }
      return lexicalNode;
    };
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class TabNode extends TextNode {
    static getType() {
      return 'tab';
    }
    static clone(node) {
      return new TabNode(node.__key);
    }
    afterCloneFrom(prevNode) {
      super.afterCloneFrom(prevNode);
      // TabNode __text can be either '\t' or ''. insertText will remove the empty Node
      this.__text = prevNode.__text;
    }
    constructor(key) {
      super('\t', key);
      this.__detail = IS_UNMERGEABLE;
    }
    static importDOM() {
      return null;
    }
    static importJSON(serializedTabNode) {
      const node = $createTabNode();
      node.setFormat(serializedTabNode.format);
      node.setStyle(serializedTabNode.style);
      return node;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        type: 'tab',
        version: 1
      };
    }
    setTextContent(_text) {
      {
        throw Error(`TabNode does not support setTextContent`);
      }
    }
    setDetail(_detail) {
      {
        throw Error(`TabNode does not support setDetail`);
      }
    }
    setMode(_type) {
      {
        throw Error(`TabNode does not support setMode`);
      }
    }
    canInsertTextBefore() {
      return false;
    }
    canInsertTextAfter() {
      return false;
    }
  }
  function $createTabNode() {
    return $applyNodeReplacement(new TabNode());
  }
  function $isTabNode(node) {
    return node instanceof TabNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  class Point {
    constructor(key, offset, type) {
      this._selection = null;
      this.key = key;
      this.offset = offset;
      this.type = type;
    }
    is(point) {
      return this.key === point.key && this.offset === point.offset && this.type === point.type;
    }
    isBefore(b) {
      let aNode = this.getNode();
      let bNode = b.getNode();
      const aOffset = this.offset;
      const bOffset = b.offset;
      if ($isElementNode(aNode)) {
        const aNodeDescendant = aNode.getDescendantByIndex(aOffset);
        aNode = aNodeDescendant != null ? aNodeDescendant : aNode;
      }
      if ($isElementNode(bNode)) {
        const bNodeDescendant = bNode.getDescendantByIndex(bOffset);
        bNode = bNodeDescendant != null ? bNodeDescendant : bNode;
      }
      if (aNode === bNode) {
        return aOffset < bOffset;
      }
      return aNode.isBefore(bNode);
    }
    getNode() {
      const key = this.key;
      const node = $getNodeByKey(key);
      if (node === null) {
        {
          throw Error(`Point.getNode: node not found`);
        }
      }
      return node;
    }
    set(key, offset, type) {
      const selection = this._selection;
      const oldKey = this.key;
      this.key = key;
      this.offset = offset;
      this.type = type;
      if (!isCurrentlyReadOnlyMode()) {
        if ($getCompositionKey() === oldKey) {
          $setCompositionKey(key);
        }
        if (selection !== null) {
          selection.setCachedNodes(null);
          selection.dirty = true;
        }
      }
    }
  }
  function $createPoint(key, offset, type) {
    // @ts-expect-error: intentionally cast as we use a class for perf reasons
    return new Point(key, offset, type);
  }
  function selectPointOnNode(point, node) {
    let key = node.__key;
    let offset = point.offset;
    let type = 'element';
    if ($isTextNode(node)) {
      type = 'text';
      const textContentLength = node.getTextContentSize();
      if (offset > textContentLength) {
        offset = textContentLength;
      }
    } else if (!$isElementNode(node)) {
      const nextSibling = node.getNextSibling();
      if ($isTextNode(nextSibling)) {
        key = nextSibling.__key;
        offset = 0;
        type = 'text';
      } else {
        const parentNode = node.getParent();
        if (parentNode) {
          key = parentNode.__key;
          offset = node.getIndexWithinParent() + 1;
        }
      }
    }
    point.set(key, offset, type);
  }
  function $moveSelectionPointToEnd(point, node) {
    if ($isElementNode(node)) {
      const lastNode = node.getLastDescendant();
      if ($isElementNode(lastNode) || $isTextNode(lastNode)) {
        selectPointOnNode(point, lastNode);
      } else {
        selectPointOnNode(point, node);
      }
    } else {
      selectPointOnNode(point, node);
    }
  }
  function $transferStartingElementPointToTextPoint(start, end, format, style) {
    const element = start.getNode();
    const placementNode = element.getChildAtIndex(start.offset);
    const textNode = $createTextNode();
    const target = $isRootNode(element) ? $createParagraphNode().append(textNode) : textNode;
    textNode.setFormat(format);
    textNode.setStyle(style);
    if (placementNode === null) {
      element.append(target);
    } else {
      placementNode.insertBefore(target);
    }
    // Transfer the element point to a text point.
    if (start.is(end)) {
      end.set(textNode.__key, 0, 'text');
    }
    start.set(textNode.__key, 0, 'text');
  }
  function $setPointValues(point, key, offset, type) {
    point.key = key;
    point.offset = offset;
    point.type = type;
  }
  class NodeSelection {
    constructor(objects) {
      this._cachedNodes = null;
      this._nodes = objects;
      this.dirty = false;
    }
    getCachedNodes() {
      return this._cachedNodes;
    }
    setCachedNodes(nodes) {
      this._cachedNodes = nodes;
    }
    is(selection) {
      if (!$isNodeSelection(selection)) {
        return false;
      }
      const a = this._nodes;
      const b = selection._nodes;
      return a.size === b.size && Array.from(a).every(key => b.has(key));
    }
    isCollapsed() {
      return false;
    }
    isBackward() {
      return false;
    }
    getStartEndPoints() {
      return null;
    }
    add(key) {
      this.dirty = true;
      this._nodes.add(key);
      this._cachedNodes = null;
    }
    delete(key) {
      this.dirty = true;
      this._nodes.delete(key);
      this._cachedNodes = null;
    }
    clear() {
      this.dirty = true;
      this._nodes.clear();
      this._cachedNodes = null;
    }
    has(key) {
      return this._nodes.has(key);
    }
    clone() {
      return new NodeSelection(new Set(this._nodes));
    }
    extract() {
      return this.getNodes();
    }
    insertRawText(text) {
      // Do nothing?
    }
    insertText() {
      // Do nothing?
    }
    insertNodes(nodes) {
      const selectedNodes = this.getNodes();
      const selectedNodesLength = selectedNodes.length;
      const lastSelectedNode = selectedNodes[selectedNodesLength - 1];
      let selectionAtEnd;
      // Insert nodes
      if ($isTextNode(lastSelectedNode)) {
        selectionAtEnd = lastSelectedNode.select();
      } else {
        const index = lastSelectedNode.getIndexWithinParent() + 1;
        selectionAtEnd = lastSelectedNode.getParentOrThrow().select(index, index);
      }
      selectionAtEnd.insertNodes(nodes);
      // Remove selected nodes
      for (let i = 0; i < selectedNodesLength; i++) {
        selectedNodes[i].remove();
      }
    }
    getNodes() {
      const cachedNodes = this._cachedNodes;
      if (cachedNodes !== null) {
        return cachedNodes;
      }
      const objects = this._nodes;
      const nodes = [];
      for (const object of objects) {
        const node = $getNodeByKey(object);
        if (node !== null) {
          nodes.push(node);
        }
      }
      if (!isCurrentlyReadOnlyMode()) {
        this._cachedNodes = nodes;
      }
      return nodes;
    }
    getTextContent() {
      const nodes = this.getNodes();
      let textContent = '';
      for (let i = 0; i < nodes.length; i++) {
        textContent += nodes[i].getTextContent();
      }
      return textContent;
    }
  }
  function $isRangeSelection(x) {
    return x instanceof RangeSelection;
  }
  class RangeSelection {
    constructor(anchor, focus, format, style) {
      this.anchor = anchor;
      this.focus = focus;
      anchor._selection = this;
      focus._selection = this;
      this._cachedNodes = null;
      this.format = format;
      this.style = style;
      this.dirty = false;
    }
    getCachedNodes() {
      return this._cachedNodes;
    }
    setCachedNodes(nodes) {
      this._cachedNodes = nodes;
    }

    /**
     * Used to check if the provided selections is equal to this one by value,
     * inluding anchor, focus, format, and style properties.
     * @param selection - the Selection to compare this one to.
     * @returns true if the Selections are equal, false otherwise.
     */
    is(selection) {
      if (!$isRangeSelection(selection)) {
        return false;
      }
      return this.anchor.is(selection.anchor) && this.focus.is(selection.focus) && this.format === selection.format && this.style === selection.style;
    }

    /**
     * Returns whether the Selection is "collapsed", meaning the anchor and focus are
     * the same node and have the same offset.
     *
     * @returns true if the Selection is collapsed, false otherwise.
     */
    isCollapsed() {
      return this.anchor.is(this.focus);
    }

    /**
     * Gets all the nodes in the Selection. Uses caching to make it generally suitable
     * for use in hot paths.
     *
     * @returns an Array containing all the nodes in the Selection
     */
    getNodes() {
      const cachedNodes = this._cachedNodes;
      if (cachedNodes !== null) {
        return cachedNodes;
      }
      const anchor = this.anchor;
      const focus = this.focus;
      const isBefore = anchor.isBefore(focus);
      const firstPoint = isBefore ? anchor : focus;
      const lastPoint = isBefore ? focus : anchor;
      let firstNode = firstPoint.getNode();
      let lastNode = lastPoint.getNode();
      const startOffset = firstPoint.offset;
      const endOffset = lastPoint.offset;
      if ($isElementNode(firstNode)) {
        const firstNodeDescendant = firstNode.getDescendantByIndex(startOffset);
        firstNode = firstNodeDescendant != null ? firstNodeDescendant : firstNode;
      }
      if ($isElementNode(lastNode)) {
        let lastNodeDescendant = lastNode.getDescendantByIndex(endOffset);
        // We don't want to over-select, as node selection infers the child before
        // the last descendant, not including that descendant.
        if (lastNodeDescendant !== null && lastNodeDescendant !== firstNode && lastNode.getChildAtIndex(endOffset) === lastNodeDescendant) {
          lastNodeDescendant = lastNodeDescendant.getPreviousSibling();
        }
        lastNode = lastNodeDescendant != null ? lastNodeDescendant : lastNode;
      }
      let nodes;
      if (firstNode.is(lastNode)) {
        if ($isElementNode(firstNode) && firstNode.getChildrenSize() > 0) {
          nodes = [];
        } else {
          nodes = [firstNode];
        }
      } else {
        nodes = firstNode.getNodesBetween(lastNode);
      }
      if (!isCurrentlyReadOnlyMode()) {
        this._cachedNodes = nodes;
      }
      return nodes;
    }

    /**
     * Sets this Selection to be of type "text" at the provided anchor and focus values.
     *
     * @param anchorNode - the anchor node to set on the Selection
     * @param anchorOffset - the offset to set on the Selection
     * @param focusNode - the focus node to set on the Selection
     * @param focusOffset - the focus offset to set on the Selection
     */
    setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset) {
      $setPointValues(this.anchor, anchorNode.__key, anchorOffset, 'text');
      $setPointValues(this.focus, focusNode.__key, focusOffset, 'text');
      this._cachedNodes = null;
      this.dirty = true;
    }

    /**
     * Gets the (plain) text content of all the nodes in the selection.
     *
     * @returns a string representing the text content of all the nodes in the Selection
     */
    getTextContent() {
      const nodes = this.getNodes();
      if (nodes.length === 0) {
        return '';
      }
      const firstNode = nodes[0];
      const lastNode = nodes[nodes.length - 1];
      const anchor = this.anchor;
      const focus = this.focus;
      const isBefore = anchor.isBefore(focus);
      const [anchorOffset, focusOffset] = $getCharacterOffsets(this);
      let textContent = '';
      let prevWasElement = true;
      for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        if ($isElementNode(node) && !node.isInline()) {
          if (!prevWasElement) {
            textContent += '\n';
          }
          if (node.isEmpty()) {
            prevWasElement = false;
          } else {
            prevWasElement = true;
          }
        } else {
          prevWasElement = false;
          if ($isTextNode(node)) {
            let text = node.getTextContent();
            if (node === firstNode) {
              if (node === lastNode) {
                if (anchor.type !== 'element' || focus.type !== 'element' || focus.offset === anchor.offset) {
                  text = anchorOffset < focusOffset ? text.slice(anchorOffset, focusOffset) : text.slice(focusOffset, anchorOffset);
                }
              } else {
                text = isBefore ? text.slice(anchorOffset) : text.slice(focusOffset);
              }
            } else if (node === lastNode) {
              text = isBefore ? text.slice(0, focusOffset) : text.slice(0, anchorOffset);
            }
            textContent += text;
          } else if (($isDecoratorNode(node) || $isLineBreakNode(node)) && (node !== lastNode || !this.isCollapsed())) {
            textContent += node.getTextContent();
          }
        }
      }
      return textContent;
    }

    /**
     * Attempts to map a DOM selection range onto this Lexical Selection,
     * setting the anchor, focus, and type accordingly
     *
     * @param range a DOM Selection range conforming to the StaticRange interface.
     */
    applyDOMRange(range) {
      const editor = getActiveEditor();
      const currentEditorState = editor.getEditorState();
      const lastSelection = currentEditorState._selection;
      const resolvedSelectionPoints = $internalResolveSelectionPoints(range.startContainer, range.startOffset, range.endContainer, range.endOffset, editor, lastSelection);
      if (resolvedSelectionPoints === null) {
        return;
      }
      const [anchorPoint, focusPoint] = resolvedSelectionPoints;
      $setPointValues(this.anchor, anchorPoint.key, anchorPoint.offset, anchorPoint.type);
      $setPointValues(this.focus, focusPoint.key, focusPoint.offset, focusPoint.type);
      this._cachedNodes = null;
    }

    /**
     * Creates a new RangeSelection, copying over all the property values from this one.
     *
     * @returns a new RangeSelection with the same property values as this one.
     */
    clone() {
      const anchor = this.anchor;
      const focus = this.focus;
      const selection = new RangeSelection($createPoint(anchor.key, anchor.offset, anchor.type), $createPoint(focus.key, focus.offset, focus.type), this.format, this.style);
      return selection;
    }

    /**
     * Toggles the provided format on all the TextNodes in the Selection.
     *
     * @param format a string TextFormatType to toggle on the TextNodes in the selection
     */
    toggleFormat(format) {
      this.format = toggleTextFormatType(this.format, format, null);
      this.dirty = true;
    }

    /**
     * Sets the value of the style property on the Selection
     *
     * @param style - the style to set at the value of the style property.
     */
    setStyle(style) {
      this.style = style;
      this.dirty = true;
    }

    /**
     * Returns whether the provided TextFormatType is present on the Selection. This will be true if any node in the Selection
     * has the specified format.
     *
     * @param type the TextFormatType to check for.
     * @returns true if the provided format is currently toggled on on the Selection, false otherwise.
     */
    hasFormat(type) {
      const formatFlag = TEXT_TYPE_TO_FORMAT[type];
      return (this.format & formatFlag) !== 0;
    }

    /**
     * Attempts to insert the provided text into the EditorState at the current Selection.
     * converts tabs, newlines, and carriage returns into LexicalNodes.
     *
     * @param text the text to insert into the Selection
     */
    insertRawText(text) {
      const parts = text.split(/(\r?\n|\t)/);
      const nodes = [];
      const length = parts.length;
      for (let i = 0; i < length; i++) {
        const part = parts[i];
        if (part === '\n' || part === '\r\n') {
          nodes.push($createLineBreakNode());
        } else if (part === '\t') {
          nodes.push($createTabNode());
        } else {
          nodes.push($createTextNode(part));
        }
      }
      this.insertNodes(nodes);
    }

    /**
     * Attempts to insert the provided text into the EditorState at the current Selection as a new
     * Lexical TextNode, according to a series of insertion heuristics based on the selection type and position.
     *
     * @param text the text to insert into the Selection
     */
    insertText(text) {
      const anchor = this.anchor;
      const focus = this.focus;
      const format = this.format;
      const style = this.style;
      let firstPoint = anchor;
      let endPoint = focus;
      if (!this.isCollapsed() && focus.isBefore(anchor)) {
        firstPoint = focus;
        endPoint = anchor;
      }
      if (firstPoint.type === 'element') {
        $transferStartingElementPointToTextPoint(firstPoint, endPoint, format, style);
      }
      const startOffset = firstPoint.offset;
      let endOffset = endPoint.offset;
      const selectedNodes = this.getNodes();
      const selectedNodesLength = selectedNodes.length;
      let firstNode = selectedNodes[0];
      if (!$isTextNode(firstNode)) {
        {
          throw Error(`insertText: first node is not a text node`);
        }
      }
      const firstNodeText = firstNode.getTextContent();
      const firstNodeTextLength = firstNodeText.length;
      const firstNodeParent = firstNode.getParentOrThrow();
      const lastIndex = selectedNodesLength - 1;
      let lastNode = selectedNodes[lastIndex];
      if (selectedNodesLength === 1 && endPoint.type === 'element') {
        endOffset = firstNodeTextLength;
        endPoint.set(firstPoint.key, endOffset, 'text');
      }
      if (this.isCollapsed() && startOffset === firstNodeTextLength && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextAfter() || !firstNodeParent.canInsertTextAfter() && firstNode.getNextSibling() === null)) {
        let nextSibling = firstNode.getNextSibling();
        if (!$isTextNode(nextSibling) || !nextSibling.canInsertTextBefore() || $isTokenOrSegmented(nextSibling)) {
          nextSibling = $createTextNode();
          nextSibling.setFormat(format);
          nextSibling.setStyle(style);
          if (!firstNodeParent.canInsertTextAfter()) {
            firstNodeParent.insertAfter(nextSibling);
          } else {
            firstNode.insertAfter(nextSibling);
          }
        }
        nextSibling.select(0, 0);
        firstNode = nextSibling;
        if (text !== '') {
          this.insertText(text);
          return;
        }
      } else if (this.isCollapsed() && startOffset === 0 && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextBefore() || !firstNodeParent.canInsertTextBefore() && firstNode.getPreviousSibling() === null)) {
        let prevSibling = firstNode.getPreviousSibling();
        if (!$isTextNode(prevSibling) || $isTokenOrSegmented(prevSibling)) {
          prevSibling = $createTextNode();
          prevSibling.setFormat(format);
          if (!firstNodeParent.canInsertTextBefore()) {
            firstNodeParent.insertBefore(prevSibling);
          } else {
            firstNode.insertBefore(prevSibling);
          }
        }
        prevSibling.select();
        firstNode = prevSibling;
        if (text !== '') {
          this.insertText(text);
          return;
        }
      } else if (firstNode.isSegmented() && startOffset !== firstNodeTextLength) {
        const textNode = $createTextNode(firstNode.getTextContent());
        textNode.setFormat(format);
        firstNode.replace(textNode);
        firstNode = textNode;
      } else if (!this.isCollapsed() && text !== '') {
        // When the firstNode or lastNode parents are elements that
        // do not allow text to be inserted before or after, we first
        // clear the content. Then we normalize selection, then insert
        // the new content.
        const lastNodeParent = lastNode.getParent();
        if (!firstNodeParent.canInsertTextBefore() || !firstNodeParent.canInsertTextAfter() || $isElementNode(lastNodeParent) && (!lastNodeParent.canInsertTextBefore() || !lastNodeParent.canInsertTextAfter())) {
          this.insertText('');
          $normalizeSelectionPointsForBoundaries(this.anchor, this.focus, null);
          this.insertText(text);
          return;
        }
      }
      if (selectedNodesLength === 1) {
        if (firstNode.isToken()) {
          const textNode = $createTextNode(text);
          textNode.select();
          firstNode.replace(textNode);
          return;
        }
        const firstNodeFormat = firstNode.getFormat();
        const firstNodeStyle = firstNode.getStyle();
        if (startOffset === endOffset && (firstNodeFormat !== format || firstNodeStyle !== style)) {
          if (firstNode.getTextContent() === '') {
            firstNode.setFormat(format);
            firstNode.setStyle(style);
          } else {
            const textNode = $createTextNode(text);
            textNode.setFormat(format);
            textNode.setStyle(style);
            textNode.select();
            if (startOffset === 0) {
              firstNode.insertBefore(textNode, false);
            } else {
              const [targetNode] = firstNode.splitText(startOffset);
              targetNode.insertAfter(textNode, false);
            }
            // When composing, we need to adjust the anchor offset so that
            // we correctly replace that right range.
            if (textNode.isComposing() && this.anchor.type === 'text') {
              this.anchor.offset -= text.length;
            }
            return;
          }
        } else if ($isTabNode(firstNode)) {
          // We don't need to check for delCount because there is only the entire selected node case
          // that can hit here for content size 1 and with canInsertTextBeforeAfter false
          const textNode = $createTextNode(text);
          textNode.setFormat(format);
          textNode.setStyle(style);
          textNode.select();
          firstNode.replace(textNode);
          return;
        }
        const delCount = endOffset - startOffset;
        firstNode = firstNode.spliceText(startOffset, delCount, text, true);
        if (firstNode.getTextContent() === '') {
          firstNode.remove();
        } else if (this.anchor.type === 'text') {
          if (firstNode.isComposing()) {
            // When composing, we need to adjust the anchor offset so that
            // we correctly replace that right range.
            this.anchor.offset -= text.length;
          } else {
            this.format = firstNodeFormat;
            this.style = firstNodeStyle;
          }
        }
      } else {
        const markedNodeKeysForKeep = new Set([...firstNode.getParentKeys(), ...lastNode.getParentKeys()]);

        // We have to get the parent elements before the next section,
        // as in that section we might mutate the lastNode.
        const firstElement = $isElementNode(firstNode) ? firstNode : firstNode.getParentOrThrow();
        let lastElement = $isElementNode(lastNode) ? lastNode : lastNode.getParentOrThrow();
        let lastElementChild = lastNode;

        // If the last element is inline, we should instead look at getting
        // the nodes of its parent, rather than itself. This behavior will
        // then better match how text node insertions work. We will need to
        // also update the last element's child accordingly as we do this.
        if (!firstElement.is(lastElement) && lastElement.isInline()) {
          // Keep traversing till we have a non-inline element parent.
          do {
            lastElementChild = lastElement;
            lastElement = lastElement.getParentOrThrow();
          } while (lastElement.isInline());
        }

        // Handle mutations to the last node.
        if (endPoint.type === 'text' && (endOffset !== 0 || lastNode.getTextContent() === '') || endPoint.type === 'element' && lastNode.getIndexWithinParent() < endOffset) {
          if ($isTextNode(lastNode) && !lastNode.isToken() && endOffset !== lastNode.getTextContentSize()) {
            if (lastNode.isSegmented()) {
              const textNode = $createTextNode(lastNode.getTextContent());
              lastNode.replace(textNode);
              lastNode = textNode;
            }
            // root node selections only select whole nodes, so no text splice is necessary
            if (!$isRootNode(endPoint.getNode()) && endPoint.type === 'text') {
              lastNode = lastNode.spliceText(0, endOffset, '');
            }
            markedNodeKeysForKeep.add(lastNode.__key);
          } else {
            const lastNodeParent = lastNode.getParentOrThrow();
            if (!lastNodeParent.canBeEmpty() && lastNodeParent.getChildrenSize() === 1) {
              lastNodeParent.remove();
            } else {
              lastNode.remove();
            }
          }
        } else {
          markedNodeKeysForKeep.add(lastNode.__key);
        }

        // Either move the remaining nodes of the last parent to after
        // the first child, or remove them entirely. If the last parent
        // is the same as the first parent, this logic also works.
        const lastNodeChildren = lastElement.getChildren();
        const selectedNodesSet = new Set(selectedNodes);
        const firstAndLastElementsAreEqual = firstElement.is(lastElement);

        // We choose a target to insert all nodes after. In the case of having
        // and inline starting parent element with a starting node that has no
        // siblings, we should insert after the starting parent element, otherwise
        // we will incorrectly merge into the starting parent element.
        // TODO: should we keep on traversing parents if we're inside another
        // nested inline element?
        const insertionTarget = firstElement.isInline() && firstNode.getNextSibling() === null ? firstElement : firstNode;
        for (let i = lastNodeChildren.length - 1; i >= 0; i--) {
          const lastNodeChild = lastNodeChildren[i];
          if (lastNodeChild.is(firstNode) || $isElementNode(lastNodeChild) && lastNodeChild.isParentOf(firstNode)) {
            break;
          }
          if (lastNodeChild.isAttached()) {
            if (!selectedNodesSet.has(lastNodeChild) || lastNodeChild.is(lastElementChild)) {
              if (!firstAndLastElementsAreEqual) {
                insertionTarget.insertAfter(lastNodeChild, false);
              }
            } else {
              lastNodeChild.remove();
            }
          }
        }
        if (!firstAndLastElementsAreEqual) {
          // Check if we have already moved out all the nodes of the
          // last parent, and if so, traverse the parent tree and mark
          // them all as being able to deleted too.
          let parent = lastElement;
          let lastRemovedParent = null;
          while (parent !== null) {
            const children = parent.getChildren();
            const childrenLength = children.length;
            if (childrenLength === 0 || children[childrenLength - 1].is(lastRemovedParent)) {
              markedNodeKeysForKeep.delete(parent.__key);
              lastRemovedParent = parent;
            }
            parent = parent.getParent();
          }
        }

        // Ensure we do splicing after moving of nodes, as splicing
        // can have side-effects (in the case of hashtags).
        if (!firstNode.isToken()) {
          firstNode = firstNode.spliceText(startOffset, firstNodeTextLength - startOffset, text, true);
          if (firstNode.getTextContent() === '') {
            firstNode.remove();
          } else if (firstNode.isComposing() && this.anchor.type === 'text') {
            // When composing, we need to adjust the anchor offset so that
            // we correctly replace that right range.
            this.anchor.offset -= text.length;
          }
        } else if (startOffset === firstNodeTextLength) {
          firstNode.select();
        } else {
          const textNode = $createTextNode(text);
          textNode.select();
          firstNode.replace(textNode);
        }

        // Remove all selected nodes that haven't already been removed.
        for (let i = 1; i < selectedNodesLength; i++) {
          const selectedNode = selectedNodes[i];
          const key = selectedNode.__key;
          if (!markedNodeKeysForKeep.has(key)) {
            selectedNode.remove();
          }
        }
      }
    }

    /**
     * Removes the text in the Selection, adjusting the EditorState accordingly.
     */
    removeText() {
      this.insertText('');
    }

    /**
     * Applies the provided format to the TextNodes in the Selection, splitting or
     * merging nodes as necessary.
     *
     * @param formatType the format type to apply to the nodes in the Selection.
     */
    formatText(formatType) {
      if (this.isCollapsed()) {
        this.toggleFormat(formatType);
        // When changing format, we should stop composition
        $setCompositionKey(null);
        return;
      }
      const selectedNodes = this.getNodes();
      const selectedTextNodes = [];
      for (const selectedNode of selectedNodes) {
        if ($isTextNode(selectedNode)) {
          selectedTextNodes.push(selectedNode);
        }
      }
      const selectedTextNodesLength = selectedTextNodes.length;
      if (selectedTextNodesLength === 0) {
        this.toggleFormat(formatType);
        // When changing format, we should stop composition
        $setCompositionKey(null);
        return;
      }
      const anchor = this.anchor;
      const focus = this.focus;
      const isBackward = this.isBackward();
      const startPoint = isBackward ? focus : anchor;
      const endPoint = isBackward ? anchor : focus;
      let firstIndex = 0;
      let firstNode = selectedTextNodes[0];
      let startOffset = startPoint.type === 'element' ? 0 : startPoint.offset;

      // In case selection started at the end of text node use next text node
      if (startPoint.type === 'text' && startOffset === firstNode.getTextContentSize()) {
        firstIndex = 1;
        firstNode = selectedTextNodes[1];
        startOffset = 0;
      }
      if (firstNode == null) {
        return;
      }
      const firstNextFormat = firstNode.getFormatFlags(formatType, null);
      const lastIndex = selectedTextNodesLength - 1;
      let lastNode = selectedTextNodes[lastIndex];
      const endOffset = endPoint.type === 'text' ? endPoint.offset : lastNode.getTextContentSize();

      // Single node selected
      if (firstNode.is(lastNode)) {
        // No actual text is selected, so do nothing.
        if (startOffset === endOffset) {
          return;
        }
        // The entire node is selected or it is token, so just format it
        if ($isTokenOrSegmented(firstNode) || startOffset === 0 && endOffset === firstNode.getTextContentSize()) {
          firstNode.setFormat(firstNextFormat);
        } else {
          // Node is partially selected, so split it into two nodes
          // add style the selected one.
          const splitNodes = firstNode.splitText(startOffset, endOffset);
          const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
          replacement.setFormat(firstNextFormat);

          // Update selection only if starts/ends on text node
          if (startPoint.type === 'text') {
            startPoint.set(replacement.__key, 0, 'text');
          }
          if (endPoint.type === 'text') {
            endPoint.set(replacement.__key, endOffset - startOffset, 'text');
          }
        }
        this.format = firstNextFormat;
        return;
      }
      // Multiple nodes selected
      // The entire first node isn't selected, so split it
      if (startOffset !== 0 && !$isTokenOrSegmented(firstNode)) {
        [, firstNode] = firstNode.splitText(startOffset);
        startOffset = 0;
      }
      firstNode.setFormat(firstNextFormat);
      const lastNextFormat = lastNode.getFormatFlags(formatType, firstNextFormat);
      // If the offset is 0, it means no actual characters are selected,
      // so we skip formatting the last node altogether.
      if (endOffset > 0) {
        if (endOffset !== lastNode.getTextContentSize() && !$isTokenOrSegmented(lastNode)) {
          [lastNode] = lastNode.splitText(endOffset);
        }
        lastNode.setFormat(lastNextFormat);
      }

      // Process all text nodes in between
      for (let i = firstIndex + 1; i < lastIndex; i++) {
        const textNode = selectedTextNodes[i];
        const nextFormat = textNode.getFormatFlags(formatType, lastNextFormat);
        textNode.setFormat(nextFormat);
      }

      // Update selection only if starts/ends on text node
      if (startPoint.type === 'text') {
        startPoint.set(firstNode.__key, startOffset, 'text');
      }
      if (endPoint.type === 'text') {
        endPoint.set(lastNode.__key, endOffset, 'text');
      }
      this.format = firstNextFormat | lastNextFormat;
    }

    /**
     * Attempts to "intelligently" insert an arbitrary list of Lexical nodes into the EditorState at the
     * current Selection according to a set of heuristics that determine how surrounding nodes
     * should be changed, replaced, or moved to accomodate the incoming ones.
     *
     * @param nodes - the nodes to insert
     */
    insertNodes(nodes) {
      if (nodes.length === 0) {
        return;
      }
      if (this.anchor.key === 'root') {
        this.insertParagraph();
        const selection = $getSelection();
        if (!$isRangeSelection(selection)) {
          throw Error(`Expected RangeSelection after insertParagraph`);
        }
        return selection.insertNodes(nodes);
      }
      const firstPoint = this.isBackward() ? this.focus : this.anchor;
      const firstBlock = $getAncestor(firstPoint.getNode(), INTERNAL_$isBlock);
      const last = nodes[nodes.length - 1];

      // CASE 1: insert inside a code block
      if ('__language' in firstBlock && $isElementNode(firstBlock)) {
        if ('__language' in nodes[0]) {
          this.insertText(nodes[0].getTextContent());
        } else {
          const index = $removeTextAndSplitBlock(this);
          firstBlock.splice(index, 0, nodes);
          last.selectEnd();
        }
        return;
      }

      // CASE 2: All elements of the array are inline
      const notInline = node => ($isElementNode(node) || $isDecoratorNode(node)) && !node.isInline();
      if (!nodes.some(notInline)) {
        if (!$isElementNode(firstBlock)) {
          throw Error(`Expected 'firstBlock' to be an ElementNode`);
        }
        const index = $removeTextAndSplitBlock(this);
        firstBlock.splice(index, 0, nodes);
        last.selectEnd();
        return;
      }

      // CASE 3: At least 1 element of the array is not inline
      const blocksParent = $wrapInlineNodes(nodes);
      const nodeToSelect = blocksParent.getLastDescendant();
      const blocks = blocksParent.getChildren();
      const isMergeable = node => $isElementNode(node) && INTERNAL_$isBlock(node) && !node.isEmpty() && $isElementNode(firstBlock) && (!firstBlock.isEmpty() || firstBlock.canMergeWhenEmpty());
      const shouldInsert = !$isElementNode(firstBlock) || !firstBlock.isEmpty();
      const insertedParagraph = shouldInsert ? this.insertParagraph() : null;
      const lastToInsert = blocks[blocks.length - 1];
      let firstToInsert = blocks[0];
      if (isMergeable(firstToInsert)) {
        if (!$isElementNode(firstBlock)) {
          throw Error(`Expected 'firstBlock' to be an ElementNode`);
        }
        firstBlock.append(...firstToInsert.getChildren());
        firstToInsert = blocks[1];
      }
      if (firstToInsert) {
        insertRangeAfter(firstBlock, firstToInsert);
      }
      const lastInsertedBlock = $getAncestor(nodeToSelect, INTERNAL_$isBlock);
      if (insertedParagraph && $isElementNode(lastInsertedBlock) && (insertedParagraph.canMergeWhenEmpty() || INTERNAL_$isBlock(lastToInsert))) {
        lastInsertedBlock.append(...insertedParagraph.getChildren());
        insertedParagraph.remove();
      }
      if ($isElementNode(firstBlock) && firstBlock.isEmpty()) {
        firstBlock.remove();
      }
      nodeToSelect.selectEnd();

      // To understand this take a look at the test "can wrap post-linebreak nodes into new element"
      const lastChild = $isElementNode(firstBlock) ? firstBlock.getLastChild() : null;
      if ($isLineBreakNode(lastChild) && lastInsertedBlock !== firstBlock) {
        lastChild.remove();
      }
    }

    /**
     * Inserts a new ParagraphNode into the EditorState at the current Selection
     *
     * @returns the newly inserted node.
     */
    insertParagraph() {
      if (this.anchor.key === 'root') {
        const paragraph = $createParagraphNode();
        $getRoot().splice(this.anchor.offset, 0, [paragraph]);
        paragraph.select();
        return paragraph;
      }
      const index = $removeTextAndSplitBlock(this);
      const block = $getAncestor(this.anchor.getNode(), INTERNAL_$isBlock);
      if (!$isElementNode(block)) {
        throw Error(`Expected ancestor to be an ElementNode`);
      }
      const firstToAppend = block.getChildAtIndex(index);
      const nodesToInsert = firstToAppend ? [firstToAppend, ...firstToAppend.getNextSiblings()] : [];
      const newBlock = block.insertNewAfter(this, false);
      if (newBlock) {
        newBlock.append(...nodesToInsert);
        newBlock.selectStart();
        return newBlock;
      }
      // if newBlock is null, it means that block is of type CodeNode.
      return null;
    }

    /**
     * Inserts a logical linebreak, which may be a new LineBreakNode or a new ParagraphNode, into the EditorState at the
     * current Selection.
     */
    insertLineBreak(selectStart) {
      const lineBreak = $createLineBreakNode();
      this.insertNodes([lineBreak]);
      // this is used in MacOS with the command 'ctrl-O' (openLineBreak)
      if (selectStart) {
        const parent = lineBreak.getParentOrThrow();
        const index = lineBreak.getIndexWithinParent();
        parent.select(index, index);
      }
    }

    /**
     * Extracts the nodes in the Selection, splitting nodes where necessary
     * to get offset-level precision.
     *
     * @returns The nodes in the Selection
     */
    extract() {
      const selectedNodes = this.getNodes();
      const selectedNodesLength = selectedNodes.length;
      const lastIndex = selectedNodesLength - 1;
      const anchor = this.anchor;
      const focus = this.focus;
      let firstNode = selectedNodes[0];
      let lastNode = selectedNodes[lastIndex];
      const [anchorOffset, focusOffset] = $getCharacterOffsets(this);
      if (selectedNodesLength === 0) {
        return [];
      } else if (selectedNodesLength === 1) {
        if ($isTextNode(firstNode) && !this.isCollapsed()) {
          const startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
          const endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset;
          const splitNodes = firstNode.splitText(startOffset, endOffset);
          const node = startOffset === 0 ? splitNodes[0] : splitNodes[1];
          return node != null ? [node] : [];
        }
        return [firstNode];
      }
      const isBefore = anchor.isBefore(focus);
      if ($isTextNode(firstNode)) {
        const startOffset = isBefore ? anchorOffset : focusOffset;
        if (startOffset === firstNode.getTextContentSize()) {
          selectedNodes.shift();
        } else if (startOffset !== 0) {
          [, firstNode] = firstNode.splitText(startOffset);
          selectedNodes[0] = firstNode;
        }
      }
      if ($isTextNode(lastNode)) {
        const lastNodeText = lastNode.getTextContent();
        const lastNodeTextLength = lastNodeText.length;
        const endOffset = isBefore ? focusOffset : anchorOffset;
        if (endOffset === 0) {
          selectedNodes.pop();
        } else if (endOffset !== lastNodeTextLength) {
          [lastNode] = lastNode.splitText(endOffset);
          selectedNodes[lastIndex] = lastNode;
        }
      }
      return selectedNodes;
    }

    /**
     * Modifies the Selection according to the parameters and a set of heuristics that account for
     * various node types. Can be used to safely move or extend selection by one logical "unit" without
     * dealing explicitly with all the possible node types.
     *
     * @param alter the type of modification to perform
     * @param isBackward whether or not selection is backwards
     * @param granularity the granularity at which to apply the modification
     */
    modify(alter, isBackward, granularity) {
      const focus = this.focus;
      const anchor = this.anchor;
      const collapse = alter === 'move';

      // Handle the selection movement around decorators.
      const possibleNode = $getAdjacentNode(focus, isBackward);
      if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
        // Make it possible to move selection from range selection to
        // node selection on the node.
        if (collapse && possibleNode.isKeyboardSelectable()) {
          const nodeSelection = $createNodeSelection();
          nodeSelection.add(possibleNode.__key);
          $setSelection(nodeSelection);
          return;
        }
        const sibling = isBackward ? possibleNode.getPreviousSibling() : possibleNode.getNextSibling();
        if (!$isTextNode(sibling)) {
          const parent = possibleNode.getParentOrThrow();
          let offset;
          let elementKey;
          if ($isElementNode(sibling)) {
            elementKey = sibling.__key;
            offset = isBackward ? sibling.getChildrenSize() : 0;
          } else {
            offset = possibleNode.getIndexWithinParent();
            elementKey = parent.__key;
            if (!isBackward) {
              offset++;
            }
          }
          focus.set(elementKey, offset, 'element');
          if (collapse) {
            anchor.set(elementKey, offset, 'element');
          }
          return;
        } else {
          const siblingKey = sibling.__key;
          const offset = isBackward ? sibling.getTextContent().length : 0;
          focus.set(siblingKey, offset, 'text');
          if (collapse) {
            anchor.set(siblingKey, offset, 'text');
          }
          return;
        }
      }
      const editor = getActiveEditor();
      const domSelection = getDOMSelection(editor._window);
      if (!domSelection) {
        return;
      }
      const blockCursorElement = editor._blockCursorElement;
      const rootElement = editor._rootElement;
      // Remove the block cursor element if it exists. This will ensure selection
      // works as intended. If we leave it in the DOM all sorts of strange bugs
      // occur. :/
      if (rootElement !== null && blockCursorElement !== null && $isElementNode(possibleNode) && !possibleNode.isInline() && !possibleNode.canBeEmpty()) {
        removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
      }
      // We use the DOM selection.modify API here to "tell" us what the selection
      // will be. We then use it to update the Lexical selection accordingly. This
      // is much more reliable than waiting for a beforeinput and using the ranges
      // from getTargetRanges(), and is also better than trying to do it ourselves
      // using Intl.Segmenter or other workarounds that struggle with word segments
      // and line segments (especially with word wrapping and non-Roman languages).
      moveNativeSelection(domSelection, alter, isBackward ? 'backward' : 'forward', granularity);
      // Guard against no ranges
      if (domSelection.rangeCount > 0) {
        const range = domSelection.getRangeAt(0);
        // Apply the DOM selection to our Lexical selection.
        const anchorNode = this.anchor.getNode();
        const root = $isRootNode(anchorNode) ? anchorNode : $getNearestRootOrShadowRoot(anchorNode);
        this.applyDOMRange(range);
        this.dirty = true;
        if (!collapse) {
          // Validate selection; make sure that the new extended selection respects shadow roots
          const nodes = this.getNodes();
          const validNodes = [];
          let shrinkSelection = false;
          for (let i = 0; i < nodes.length; i++) {
            const nextNode = nodes[i];
            if ($hasAncestor(nextNode, root)) {
              validNodes.push(nextNode);
            } else {
              shrinkSelection = true;
            }
          }
          if (shrinkSelection && validNodes.length > 0) {
            // validNodes length check is a safeguard against an invalid selection; as getNodes()
            // will return an empty array in this case
            if (isBackward) {
              const firstValidNode = validNodes[0];
              if ($isElementNode(firstValidNode)) {
                firstValidNode.selectStart();
              } else {
                firstValidNode.getParentOrThrow().selectStart();
              }
            } else {
              const lastValidNode = validNodes[validNodes.length - 1];
              if ($isElementNode(lastValidNode)) {
                lastValidNode.selectEnd();
              } else {
                lastValidNode.getParentOrThrow().selectEnd();
              }
            }
          }

          // Because a range works on start and end, we might need to flip
          // the anchor and focus points to match what the DOM has, not what
          // the range has specifically.
          if (domSelection.anchorNode !== range.startContainer || domSelection.anchorOffset !== range.startOffset) {
            $swapPoints(this);
          }
        }
      }
    }
    /**
     * Helper for handling forward character and word deletion that prevents element nodes
     * like a table, columns layout being destroyed
     *
     * @param anchor the anchor
     * @param anchorNode the anchor node in the selection
     * @param isBackward whether or not selection is backwards
     */
    forwardDeletion(anchor, anchorNode, isBackward) {
      if (!isBackward && (
      // Delete forward handle case
      anchor.type === 'element' && $isElementNode(anchorNode) && anchor.offset === anchorNode.getChildrenSize() || anchor.type === 'text' && anchor.offset === anchorNode.getTextContentSize())) {
        const parent = anchorNode.getParent();
        const nextSibling = anchorNode.getNextSibling() || (parent === null ? null : parent.getNextSibling());
        if ($isElementNode(nextSibling) && nextSibling.isShadowRoot()) {
          return true;
        }
      }
      return false;
    }

    /**
     * Performs one logical character deletion operation on the EditorState based on the current Selection.
     * Handles different node types.
     *
     * @param isBackward whether or not the selection is backwards.
     */
    deleteCharacter(isBackward) {
      const wasCollapsed = this.isCollapsed();
      if (this.isCollapsed()) {
        const anchor = this.anchor;
        let anchorNode = anchor.getNode();
        if (this.forwardDeletion(anchor, anchorNode, isBackward)) {
          return;
        }

        // Handle the deletion around decorators.
        const focus = this.focus;
        const possibleNode = $getAdjacentNode(focus, isBackward);
        if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
          // Make it possible to move selection from range selection to
          // node selection on the node.
          if (possibleNode.isKeyboardSelectable() && $isElementNode(anchorNode) && anchorNode.getChildrenSize() === 0) {
            anchorNode.remove();
            const nodeSelection = $createNodeSelection();
            nodeSelection.add(possibleNode.__key);
            $setSelection(nodeSelection);
          } else {
            possibleNode.remove();
            const editor = getActiveEditor();
            editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
          }
          return;
        } else if (!isBackward && $isElementNode(possibleNode) && $isElementNode(anchorNode) && anchorNode.isEmpty()) {
          anchorNode.remove();
          possibleNode.selectStart();
          return;
        }
        this.modify('extend', isBackward, 'character');
        if (!this.isCollapsed()) {
          const focusNode = focus.type === 'text' ? focus.getNode() : null;
          anchorNode = anchor.type === 'text' ? anchor.getNode() : null;
          if (focusNode !== null && focusNode.isSegmented()) {
            const offset = focus.offset;
            const textContentSize = focusNode.getTextContentSize();
            if (focusNode.is(anchorNode) || isBackward && offset !== textContentSize || !isBackward && offset !== 0) {
              $removeSegment(focusNode, isBackward, offset);
              return;
            }
          } else if (anchorNode !== null && anchorNode.isSegmented()) {
            const offset = anchor.offset;
            const textContentSize = anchorNode.getTextContentSize();
            if (anchorNode.is(focusNode) || isBackward && offset !== 0 || !isBackward && offset !== textContentSize) {
              $removeSegment(anchorNode, isBackward, offset);
              return;
            }
          }
          $updateCaretSelectionForUnicodeCharacter(this, isBackward);
        } else if (isBackward && anchor.offset === 0) {
          // Special handling around rich text nodes
          const element = anchor.type === 'element' ? anchor.getNode() : anchor.getNode().getParentOrThrow();
          if (element.collapseAtStart(this)) {
            return;
          }
        }
      }
      this.removeText();
      if (isBackward && !wasCollapsed && this.isCollapsed() && this.anchor.type === 'element' && this.anchor.offset === 0) {
        const anchorNode = this.anchor.getNode();
        if (anchorNode.isEmpty() && $isRootNode(anchorNode.getParent()) && anchorNode.getIndexWithinParent() === 0) {
          anchorNode.collapseAtStart(this);
        }
      }
    }

    /**
     * Performs one logical line deletion operation on the EditorState based on the current Selection.
     * Handles different node types.
     *
     * @param isBackward whether or not the selection is backwards.
     */
    deleteLine(isBackward) {
      if (this.isCollapsed()) {
        // Since `domSelection.modify('extend', ..., 'lineboundary')` works well for text selections
        // but doesn't properly handle selections which end on elements, a space character is added
        // for such selections transforming their anchor's type to 'text'
        const anchorIsElement = this.anchor.type === 'element';
        if (anchorIsElement) {
          this.insertText(' ');
        }
        this.modify('extend', isBackward, 'lineboundary');

        // If selection is extended to cover text edge then extend it one character more
        // to delete its parent element. Otherwise text content will be deleted but empty
        // parent node will remain
        const endPoint = isBackward ? this.focus : this.anchor;
        if (endPoint.offset === 0) {
          this.modify('extend', isBackward, 'character');
        }

        // Adjusts selection to include an extra character added for element anchors to remove it
        if (anchorIsElement) {
          const startPoint = isBackward ? this.anchor : this.focus;
          startPoint.set(startPoint.key, startPoint.offset + 1, startPoint.type);
        }
      }
      this.removeText();
    }

    /**
     * Performs one logical word deletion operation on the EditorState based on the current Selection.
     * Handles different node types.
     *
     * @param isBackward whether or not the selection is backwards.
     */
    deleteWord(isBackward) {
      if (this.isCollapsed()) {
        const anchor = this.anchor;
        const anchorNode = anchor.getNode();
        if (this.forwardDeletion(anchor, anchorNode, isBackward)) {
          return;
        }
        this.modify('extend', isBackward, 'word');
      }
      this.removeText();
    }

    /**
     * Returns whether the Selection is "backwards", meaning the focus
     * logically precedes the anchor in the EditorState.
     * @returns true if the Selection is backwards, false otherwise.
     */
    isBackward() {
      return this.focus.isBefore(this.anchor);
    }
    getStartEndPoints() {
      return [this.anchor, this.focus];
    }
  }
  function $isNodeSelection(x) {
    return x instanceof NodeSelection;
  }
  function getCharacterOffset(point) {
    const offset = point.offset;
    if (point.type === 'text') {
      return offset;
    }
    const parent = point.getNode();
    return offset === parent.getChildrenSize() ? parent.getTextContent().length : 0;
  }
  function $getCharacterOffsets(selection) {
    const anchorAndFocus = selection.getStartEndPoints();
    if (anchorAndFocus === null) {
      return [0, 0];
    }
    const [anchor, focus] = anchorAndFocus;
    if (anchor.type === 'element' && focus.type === 'element' && anchor.key === focus.key && anchor.offset === focus.offset) {
      return [0, 0];
    }
    return [getCharacterOffset(anchor), getCharacterOffset(focus)];
  }
  function $swapPoints(selection) {
    const focus = selection.focus;
    const anchor = selection.anchor;
    const anchorKey = anchor.key;
    const anchorOffset = anchor.offset;
    const anchorType = anchor.type;
    $setPointValues(anchor, focus.key, focus.offset, focus.type);
    $setPointValues(focus, anchorKey, anchorOffset, anchorType);
    selection._cachedNodes = null;
  }
  function moveNativeSelection(domSelection, alter, direction, granularity) {
    // Selection.modify() method applies a change to the current selection or cursor position,
    // but is still non-standard in some browsers.
    domSelection.modify(alter, direction, granularity);
  }
  function $updateCaretSelectionForUnicodeCharacter(selection, isBackward) {
    const anchor = selection.anchor;
    const focus = selection.focus;
    const anchorNode = anchor.getNode();
    const focusNode = focus.getNode();
    if (anchorNode === focusNode && anchor.type === 'text' && focus.type === 'text') {
      // Handling of multibyte characters
      const anchorOffset = anchor.offset;
      const focusOffset = focus.offset;
      const isBefore = anchorOffset < focusOffset;
      const startOffset = isBefore ? anchorOffset : focusOffset;
      const endOffset = isBefore ? focusOffset : anchorOffset;
      const characterOffset = endOffset - 1;
      if (startOffset !== characterOffset) {
        const text = anchorNode.getTextContent().slice(startOffset, endOffset);
        if (!doesContainGrapheme(text)) {
          if (isBackward) {
            focus.offset = characterOffset;
          } else {
            anchor.offset = characterOffset;
          }
        }
      }
    }
  }
  function $removeSegment(node, isBackward, offset) {
    const textNode = node;
    const textContent = textNode.getTextContent();
    const split = textContent.split(/(?=\s)/g);
    const splitLength = split.length;
    let segmentOffset = 0;
    let restoreOffset = 0;
    for (let i = 0; i < splitLength; i++) {
      const text = split[i];
      const isLast = i === splitLength - 1;
      restoreOffset = segmentOffset;
      segmentOffset += text.length;
      if (isBackward && segmentOffset === offset || segmentOffset > offset || isLast) {
        split.splice(i, 1);
        if (isLast) {
          restoreOffset = undefined;
        }
        break;
      }
    }
    const nextTextContent = split.join('').trim();
    if (nextTextContent === '') {
      textNode.remove();
    } else {
      textNode.setTextContent(nextTextContent);
      textNode.select(restoreOffset, restoreOffset);
    }
  }
  function shouldResolveAncestor(resolvedElement, resolvedOffset, lastPoint) {
    const parent = resolvedElement.getParent();
    return lastPoint === null || parent === null || !parent.canBeEmpty() || parent !== lastPoint.getNode();
  }
  function $internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
    let resolvedOffset = offset;
    let resolvedNode;
    // If we have selection on an element, we will
    // need to figure out (using the offset) what text
    // node should be selected.

    if (dom.nodeType === DOM_ELEMENT_TYPE) {
      // Resolve element to a ElementNode, or TextNode, or null
      let moveSelectionToEnd = false;
      // Given we're moving selection to another node, selection is
      // definitely dirty.
      // We use the anchor to find which child node to select
      const childNodes = dom.childNodes;
      const childNodesLength = childNodes.length;
      const blockCursorElement = editor._blockCursorElement;
      // If the anchor is the same as length, then this means we
      // need to select the very last text node.
      if (resolvedOffset === childNodesLength) {
        moveSelectionToEnd = true;
        resolvedOffset = childNodesLength - 1;
      }
      let childDOM = childNodes[resolvedOffset];
      let hasBlockCursor = false;
      if (childDOM === blockCursorElement) {
        childDOM = childNodes[resolvedOffset + 1];
        hasBlockCursor = true;
      } else if (blockCursorElement !== null) {
        const blockCursorElementParent = blockCursorElement.parentNode;
        if (dom === blockCursorElementParent) {
          const blockCursorOffset = Array.prototype.indexOf.call(blockCursorElementParent.children, blockCursorElement);
          if (offset > blockCursorOffset) {
            resolvedOffset--;
          }
        }
      }
      resolvedNode = $getNodeFromDOM(childDOM);
      if ($isTextNode(resolvedNode)) {
        resolvedOffset = getTextNodeOffset(resolvedNode, moveSelectionToEnd);
      } else {
        let resolvedElement = $getNodeFromDOM(dom);
        // Ensure resolvedElement is actually a element.
        if (resolvedElement === null) {
          return null;
        }
        if ($isElementNode(resolvedElement)) {
          resolvedOffset = Math.min(resolvedElement.getChildrenSize(), resolvedOffset);
          let child = resolvedElement.getChildAtIndex(resolvedOffset);
          if ($isElementNode(child) && shouldResolveAncestor(child, resolvedOffset, lastPoint)) {
            const descendant = moveSelectionToEnd ? child.getLastDescendant() : child.getFirstDescendant();
            if (descendant === null) {
              resolvedElement = child;
            } else {
              child = descendant;
              resolvedElement = $isElementNode(child) ? child : child.getParentOrThrow();
            }
            resolvedOffset = 0;
          }
          if ($isTextNode(child)) {
            resolvedNode = child;
            resolvedElement = null;
            resolvedOffset = getTextNodeOffset(child, moveSelectionToEnd);
          } else if (child !== resolvedElement && moveSelectionToEnd && !hasBlockCursor) {
            resolvedOffset++;
          }
        } else {
          const index = resolvedElement.getIndexWithinParent();
          // When selecting decorators, there can be some selection issues when using resolvedOffset,
          // and instead we should be checking if we're using the offset
          if (offset === 0 && $isDecoratorNode(resolvedElement) && $getNodeFromDOM(dom) === resolvedElement) {
            resolvedOffset = index;
          } else {
            resolvedOffset = index + 1;
          }
          resolvedElement = resolvedElement.getParentOrThrow();
        }
        if ($isElementNode(resolvedElement)) {
          return $createPoint(resolvedElement.__key, resolvedOffset, 'element');
        }
      }
    } else {
      // TextNode or null
      resolvedNode = $getNodeFromDOM(dom);
    }
    if (!$isTextNode(resolvedNode)) {
      return null;
    }
    return $createPoint(resolvedNode.__key, resolvedOffset, 'text');
  }
  function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
    const offset = point.offset;
    const node = point.getNode();
    if (offset === 0) {
      const prevSibling = node.getPreviousSibling();
      const parent = node.getParent();
      if (!isBackward) {
        if ($isElementNode(prevSibling) && !isCollapsed && prevSibling.isInline()) {
          point.key = prevSibling.__key;
          point.offset = prevSibling.getChildrenSize();
          // @ts-expect-error: intentional
          point.type = 'element';
        } else if ($isTextNode(prevSibling)) {
          point.key = prevSibling.__key;
          point.offset = prevSibling.getTextContent().length;
        }
      } else if ((isCollapsed || !isBackward) && prevSibling === null && $isElementNode(parent) && parent.isInline()) {
        const parentSibling = parent.getPreviousSibling();
        if ($isTextNode(parentSibling)) {
          point.key = parentSibling.__key;
          point.offset = parentSibling.getTextContent().length;
        }
      }
    } else if (offset === node.getTextContent().length) {
      const nextSibling = node.getNextSibling();
      const parent = node.getParent();
      if (isBackward && $isElementNode(nextSibling) && nextSibling.isInline()) {
        point.key = nextSibling.__key;
        point.offset = 0;
        // @ts-expect-error: intentional
        point.type = 'element';
      } else if ((isCollapsed || isBackward) && nextSibling === null && $isElementNode(parent) && parent.isInline() && !parent.canInsertTextAfter()) {
        const parentSibling = parent.getNextSibling();
        if ($isTextNode(parentSibling)) {
          point.key = parentSibling.__key;
          point.offset = 0;
        }
      }
    }
  }
  function $normalizeSelectionPointsForBoundaries(anchor, focus, lastSelection) {
    if (anchor.type === 'text' && focus.type === 'text') {
      const isBackward = anchor.isBefore(focus);
      const isCollapsed = anchor.is(focus);

      // Attempt to normalize the offset to the previous sibling if we're at the
      // start of a text node and the sibling is a text node or inline element.
      resolveSelectionPointOnBoundary(anchor, isBackward, isCollapsed);
      resolveSelectionPointOnBoundary(focus, !isBackward, isCollapsed);
      if (isCollapsed) {
        focus.key = anchor.key;
        focus.offset = anchor.offset;
        focus.type = anchor.type;
      }
      const editor = getActiveEditor();
      if (editor.isComposing() && editor._compositionKey !== anchor.key && $isRangeSelection(lastSelection)) {
        const lastAnchor = lastSelection.anchor;
        const lastFocus = lastSelection.focus;
        $setPointValues(anchor, lastAnchor.key, lastAnchor.offset, lastAnchor.type);
        $setPointValues(focus, lastFocus.key, lastFocus.offset, lastFocus.type);
      }
    }
  }
  function $internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focusOffset, editor, lastSelection) {
    if (anchorDOM === null || focusDOM === null || !isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
      return null;
    }
    const resolvedAnchorPoint = $internalResolveSelectionPoint(anchorDOM, anchorOffset, $isRangeSelection(lastSelection) ? lastSelection.anchor : null, editor);
    if (resolvedAnchorPoint === null) {
      return null;
    }
    const resolvedFocusPoint = $internalResolveSelectionPoint(focusDOM, focusOffset, $isRangeSelection(lastSelection) ? lastSelection.focus : null, editor);
    if (resolvedFocusPoint === null) {
      return null;
    }
    if (resolvedAnchorPoint.type === 'element' && resolvedFocusPoint.type === 'element') {
      const anchorNode = $getNodeFromDOM(anchorDOM);
      const focusNode = $getNodeFromDOM(focusDOM);
      // Ensure if we're selecting the content of a decorator that we
      // return null for this point, as it's not in the controlled scope
      // of Lexical.
      if ($isDecoratorNode(anchorNode) && $isDecoratorNode(focusNode)) {
        return null;
      }
    }

    // Handle normalization of selection when it is at the boundaries.
    $normalizeSelectionPointsForBoundaries(resolvedAnchorPoint, resolvedFocusPoint, lastSelection);
    return [resolvedAnchorPoint, resolvedFocusPoint];
  }
  function $isBlockElementNode(node) {
    return $isElementNode(node) && !node.isInline();
  }

  // This is used to make a selection when the existing
  // selection is null, i.e. forcing selection on the editor
  // when it current exists outside the editor.

  function $internalMakeRangeSelection(anchorKey, anchorOffset, focusKey, focusOffset, anchorType, focusType) {
    const editorState = getActiveEditorState();
    const selection = new RangeSelection($createPoint(anchorKey, anchorOffset, anchorType), $createPoint(focusKey, focusOffset, focusType), 0, '');
    selection.dirty = true;
    editorState._selection = selection;
    return selection;
  }
  function $createRangeSelection() {
    const anchor = $createPoint('root', 0, 'element');
    const focus = $createPoint('root', 0, 'element');
    return new RangeSelection(anchor, focus, 0, '');
  }
  function $createNodeSelection() {
    return new NodeSelection(new Set());
  }
  function $internalCreateSelection(editor) {
    const currentEditorState = editor.getEditorState();
    const lastSelection = currentEditorState._selection;
    const domSelection = getDOMSelection(editor._window);
    if ($isRangeSelection(lastSelection) || lastSelection == null) {
      return $internalCreateRangeSelection(lastSelection, domSelection, editor, null);
    }
    return lastSelection.clone();
  }
  function $createRangeSelectionFromDom(domSelection, editor) {
    return $internalCreateRangeSelection(null, domSelection, editor, null);
  }
  function $internalCreateRangeSelection(lastSelection, domSelection, editor, event) {
    const windowObj = editor._window;
    if (windowObj === null) {
      return null;
    }
    // When we create a selection, we try to use the previous
    // selection where possible, unless an actual user selection
    // change has occurred. When we do need to create a new selection
    // we validate we can have text nodes for both anchor and focus
    // nodes. If that holds true, we then return that selection
    // as a mutable object that we use for the editor state for this
    // update cycle. If a selection gets changed, and requires a
    // update to native DOM selection, it gets marked as "dirty".
    // If the selection changes, but matches with the existing
    // DOM selection, then we only need to sync it. Otherwise,
    // we generally bail out of doing an update to selection during
    // reconciliation unless there are dirty nodes that need
    // reconciling.

    const windowEvent = event || windowObj.event;
    const eventType = windowEvent ? windowEvent.type : undefined;
    const isSelectionChange = eventType === 'selectionchange';
    const useDOMSelection = !getIsProcessingMutations() && (isSelectionChange || eventType === 'beforeinput' || eventType === 'compositionstart' || eventType === 'compositionend' || eventType === 'click' && windowEvent && windowEvent.detail === 3 || eventType === 'drop' || eventType === undefined);
    let anchorDOM, focusDOM, anchorOffset, focusOffset;
    if (!$isRangeSelection(lastSelection) || useDOMSelection) {
      if (domSelection === null) {
        return null;
      }
      anchorDOM = domSelection.anchorNode;
      focusDOM = domSelection.focusNode;
      anchorOffset = domSelection.anchorOffset;
      focusOffset = domSelection.focusOffset;
      if (isSelectionChange && $isRangeSelection(lastSelection) && !isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
        return lastSelection.clone();
      }
    } else {
      return lastSelection.clone();
    }
    // Let's resolve the text nodes from the offsets and DOM nodes we have from
    // native selection.
    const resolvedSelectionPoints = $internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focusOffset, editor, lastSelection);
    if (resolvedSelectionPoints === null) {
      return null;
    }
    const [resolvedAnchorPoint, resolvedFocusPoint] = resolvedSelectionPoints;
    return new RangeSelection(resolvedAnchorPoint, resolvedFocusPoint, !$isRangeSelection(lastSelection) ? 0 : lastSelection.format, !$isRangeSelection(lastSelection) ? '' : lastSelection.style);
  }
  function $getSelection() {
    const editorState = getActiveEditorState();
    return editorState._selection;
  }
  function $getPreviousSelection() {
    const editor = getActiveEditor();
    return editor._editorState._selection;
  }
  function $updateElementSelectionOnCreateDeleteNode(selection, parentNode, nodeOffset, times = 1) {
    const anchor = selection.anchor;
    const focus = selection.focus;
    const anchorNode = anchor.getNode();
    const focusNode = focus.getNode();
    if (!parentNode.is(anchorNode) && !parentNode.is(focusNode)) {
      return;
    }
    const parentKey = parentNode.__key;
    // Single node. We shift selection but never redimension it
    if (selection.isCollapsed()) {
      const selectionOffset = anchor.offset;
      if (nodeOffset <= selectionOffset && times > 0 || nodeOffset < selectionOffset && times < 0) {
        const newSelectionOffset = Math.max(0, selectionOffset + times);
        anchor.set(parentKey, newSelectionOffset, 'element');
        focus.set(parentKey, newSelectionOffset, 'element');
        // The new selection might point to text nodes, try to resolve them
        $updateSelectionResolveTextNodes(selection);
      }
    } else {
      // Multiple nodes selected. We shift or redimension selection
      const isBackward = selection.isBackward();
      const firstPoint = isBackward ? focus : anchor;
      const firstPointNode = firstPoint.getNode();
      const lastPoint = isBackward ? anchor : focus;
      const lastPointNode = lastPoint.getNode();
      if (parentNode.is(firstPointNode)) {
        const firstPointOffset = firstPoint.offset;
        if (nodeOffset <= firstPointOffset && times > 0 || nodeOffset < firstPointOffset && times < 0) {
          firstPoint.set(parentKey, Math.max(0, firstPointOffset + times), 'element');
        }
      }
      if (parentNode.is(lastPointNode)) {
        const lastPointOffset = lastPoint.offset;
        if (nodeOffset <= lastPointOffset && times > 0 || nodeOffset < lastPointOffset && times < 0) {
          lastPoint.set(parentKey, Math.max(0, lastPointOffset + times), 'element');
        }
      }
    }
    // The new selection might point to text nodes, try to resolve them
    $updateSelectionResolveTextNodes(selection);
  }
  function $updateSelectionResolveTextNodes(selection) {
    const anchor = selection.anchor;
    const anchorOffset = anchor.offset;
    const focus = selection.focus;
    const focusOffset = focus.offset;
    const anchorNode = anchor.getNode();
    const focusNode = focus.getNode();
    if (selection.isCollapsed()) {
      if (!$isElementNode(anchorNode)) {
        return;
      }
      const childSize = anchorNode.getChildrenSize();
      const anchorOffsetAtEnd = anchorOffset >= childSize;
      const child = anchorOffsetAtEnd ? anchorNode.getChildAtIndex(childSize - 1) : anchorNode.getChildAtIndex(anchorOffset);
      if ($isTextNode(child)) {
        let newOffset = 0;
        if (anchorOffsetAtEnd) {
          newOffset = child.getTextContentSize();
        }
        anchor.set(child.__key, newOffset, 'text');
        focus.set(child.__key, newOffset, 'text');
      }
      return;
    }
    if ($isElementNode(anchorNode)) {
      const childSize = anchorNode.getChildrenSize();
      const anchorOffsetAtEnd = anchorOffset >= childSize;
      const child = anchorOffsetAtEnd ? anchorNode.getChildAtIndex(childSize - 1) : anchorNode.getChildAtIndex(anchorOffset);
      if ($isTextNode(child)) {
        let newOffset = 0;
        if (anchorOffsetAtEnd) {
          newOffset = child.getTextContentSize();
        }
        anchor.set(child.__key, newOffset, 'text');
      }
    }
    if ($isElementNode(focusNode)) {
      const childSize = focusNode.getChildrenSize();
      const focusOffsetAtEnd = focusOffset >= childSize;
      const child = focusOffsetAtEnd ? focusNode.getChildAtIndex(childSize - 1) : focusNode.getChildAtIndex(focusOffset);
      if ($isTextNode(child)) {
        let newOffset = 0;
        if (focusOffsetAtEnd) {
          newOffset = child.getTextContentSize();
        }
        focus.set(child.__key, newOffset, 'text');
      }
    }
  }
  function applySelectionTransforms(nextEditorState, editor) {
    const prevEditorState = editor.getEditorState();
    const prevSelection = prevEditorState._selection;
    const nextSelection = nextEditorState._selection;
    if ($isRangeSelection(nextSelection)) {
      const anchor = nextSelection.anchor;
      const focus = nextSelection.focus;
      let anchorNode;
      if (anchor.type === 'text') {
        anchorNode = anchor.getNode();
        anchorNode.selectionTransform(prevSelection, nextSelection);
      }
      if (focus.type === 'text') {
        const focusNode = focus.getNode();
        if (anchorNode !== focusNode) {
          focusNode.selectionTransform(prevSelection, nextSelection);
        }
      }
    }
  }
  function moveSelectionPointToSibling(point, node, parent, prevSibling, nextSibling) {
    let siblingKey = null;
    let offset = 0;
    let type = null;
    if (prevSibling !== null) {
      siblingKey = prevSibling.__key;
      if ($isTextNode(prevSibling)) {
        offset = prevSibling.getTextContentSize();
        type = 'text';
      } else if ($isElementNode(prevSibling)) {
        offset = prevSibling.getChildrenSize();
        type = 'element';
      }
    } else {
      if (nextSibling !== null) {
        siblingKey = nextSibling.__key;
        if ($isTextNode(nextSibling)) {
          type = 'text';
        } else if ($isElementNode(nextSibling)) {
          type = 'element';
        }
      }
    }
    if (siblingKey !== null && type !== null) {
      point.set(siblingKey, offset, type);
    } else {
      offset = node.getIndexWithinParent();
      if (offset === -1) {
        // Move selection to end of parent
        offset = parent.getChildrenSize();
      }
      point.set(parent.__key, offset, 'element');
    }
  }
  function adjustPointOffsetForMergedSibling(point, isBefore, key, target, textLength) {
    if (point.type === 'text') {
      point.key = key;
      if (!isBefore) {
        point.offset += textLength;
      }
    } else if (point.offset > target.getIndexWithinParent()) {
      point.offset -= 1;
    }
  }
  function updateDOMSelection(prevSelection, nextSelection, editor, domSelection, tags, rootElement, nodeCount) {
    const anchorDOMNode = domSelection.anchorNode;
    const focusDOMNode = domSelection.focusNode;
    const anchorOffset = domSelection.anchorOffset;
    const focusOffset = domSelection.focusOffset;
    const activeElement = document.activeElement;

    // TODO: make this not hard-coded, and add another config option
    // that makes this configurable.
    if (tags.has('collaboration') && activeElement !== rootElement || activeElement !== null && isSelectionCapturedInDecoratorInput(activeElement)) {
      return;
    }
    if (!$isRangeSelection(nextSelection)) {
      // We don't remove selection if the prevSelection is null because
      // of editor.setRootElement(). If this occurs on init when the
      // editor is already focused, then this can cause the editor to
      // lose focus.
      if (prevSelection !== null && isSelectionWithinEditor(editor, anchorDOMNode, focusDOMNode)) {
        domSelection.removeAllRanges();
      }
      return;
    }
    const anchor = nextSelection.anchor;
    const focus = nextSelection.focus;
    const anchorKey = anchor.key;
    const focusKey = focus.key;
    const anchorDOM = getElementByKeyOrThrow(editor, anchorKey);
    const focusDOM = getElementByKeyOrThrow(editor, focusKey);
    const nextAnchorOffset = anchor.offset;
    const nextFocusOffset = focus.offset;
    const nextFormat = nextSelection.format;
    const nextStyle = nextSelection.style;
    const isCollapsed = nextSelection.isCollapsed();
    let nextAnchorNode = anchorDOM;
    let nextFocusNode = focusDOM;
    let anchorFormatOrStyleChanged = false;
    if (anchor.type === 'text') {
      nextAnchorNode = getDOMTextNode(anchorDOM);
      const anchorNode = anchor.getNode();
      anchorFormatOrStyleChanged = anchorNode.getFormat() !== nextFormat || anchorNode.getStyle() !== nextStyle;
    } else if ($isRangeSelection(prevSelection) && prevSelection.anchor.type === 'text') {
      anchorFormatOrStyleChanged = true;
    }
    if (focus.type === 'text') {
      nextFocusNode = getDOMTextNode(focusDOM);
    }

    // If we can't get an underlying text node for selection, then
    // we should avoid setting selection to something incorrect.
    if (nextAnchorNode === null || nextFocusNode === null) {
      return;
    }
    if (isCollapsed && (prevSelection === null || anchorFormatOrStyleChanged || $isRangeSelection(prevSelection) && (prevSelection.format !== nextFormat || prevSelection.style !== nextStyle))) {
      markCollapsedSelectionFormat(nextFormat, nextStyle, nextAnchorOffset, anchorKey, performance.now());
    }

    // Diff against the native DOM selection to ensure we don't do
    // an unnecessary selection update. We also skip this check if
    // we're moving selection to within an element, as this can
    // sometimes be problematic around scrolling.
    if (anchorOffset === nextAnchorOffset && focusOffset === nextFocusOffset && anchorDOMNode === nextAnchorNode && focusDOMNode === nextFocusNode &&
    // Badly interpreted range selection when collapsed - #1482
    !(domSelection.type === 'Range' && isCollapsed)) {
      // If the root element does not have focus, ensure it has focus
      if (activeElement === null || !rootElement.contains(activeElement)) {
        rootElement.focus({
          preventScroll: true
        });
      }
      if (anchor.type !== 'element') {
        return;
      }
    }

    // Apply the updated selection to the DOM. Note: this will trigger
    // a "selectionchange" event, although it will be asynchronous.
    try {
      domSelection.setBaseAndExtent(nextAnchorNode, nextAnchorOffset, nextFocusNode, nextFocusOffset);
    } catch (error) {
      // If we encounter an error, continue. This can sometimes
      // occur with FF and there's no good reason as to why it
      // should happen.
      {
        console.warn(error);
      }
    }
    if (!tags.has('skip-scroll-into-view') && nextSelection.isCollapsed() && rootElement !== null && rootElement === document.activeElement) {
      const selectionTarget = nextSelection instanceof RangeSelection && nextSelection.anchor.type === 'element' ? nextAnchorNode.childNodes[nextAnchorOffset] || null : domSelection.rangeCount > 0 ? domSelection.getRangeAt(0) : null;
      if (selectionTarget !== null) {
        let selectionRect;
        if (selectionTarget instanceof Text) {
          const range = document.createRange();
          range.selectNode(selectionTarget);
          selectionRect = range.getBoundingClientRect();
        } else {
          selectionRect = selectionTarget.getBoundingClientRect();
        }
        scrollIntoViewIfNeeded(editor, selectionRect, rootElement);
      }
    }
    markSelectionChangeFromDOMUpdate();
  }
  function $insertNodes(nodes) {
    let selection = $getSelection() || $getPreviousSelection();
    if (selection === null) {
      selection = $getRoot().selectEnd();
    }
    selection.insertNodes(nodes);
  }
  function $getTextContent() {
    const selection = $getSelection();
    if (selection === null) {
      return '';
    }
    return selection.getTextContent();
  }
  function $removeTextAndSplitBlock(selection) {
    let selection_ = selection;
    if (!selection.isCollapsed()) {
      selection_.removeText();
    }
    // A new selection can originate as a result of node replacement, in which case is registered via
    // $setSelection
    const newSelection = $getSelection();
    if ($isRangeSelection(newSelection)) {
      selection_ = newSelection;
    }
    if (!$isRangeSelection(selection_)) {
      throw Error(`Unexpected dirty selection to be null`);
    }
    const anchor = selection_.anchor;
    let node = anchor.getNode();
    let offset = anchor.offset;
    while (!INTERNAL_$isBlock(node)) {
      [node, offset] = $splitNodeAtPoint(node, offset);
    }
    return offset;
  }
  function $splitNodeAtPoint(node, offset) {
    const parent = node.getParent();
    if (!parent) {
      const paragraph = $createParagraphNode();
      $getRoot().append(paragraph);
      paragraph.select();
      return [$getRoot(), 0];
    }
    if ($isTextNode(node)) {
      const split = node.splitText(offset);
      if (split.length === 0) {
        return [parent, node.getIndexWithinParent()];
      }
      const x = offset === 0 ? 0 : 1;
      const index = split[0].getIndexWithinParent() + x;
      return [parent, index];
    }
    if (!$isElementNode(node) || offset === 0) {
      return [parent, node.getIndexWithinParent()];
    }
    const firstToAppend = node.getChildAtIndex(offset);
    if (firstToAppend) {
      const insertPoint = new RangeSelection($createPoint(node.__key, offset, 'element'), $createPoint(node.__key, offset, 'element'), 0, '');
      const newElement = node.insertNewAfter(insertPoint);
      if (newElement) {
        newElement.append(firstToAppend, ...firstToAppend.getNextSiblings());
      }
    }
    return [parent, node.getIndexWithinParent() + 1];
  }
  function $wrapInlineNodes(nodes) {
    // We temporarily insert the topLevelNodes into an arbitrary ElementNode,
    // since insertAfter does not work on nodes that have no parent (TO-DO: fix that).
    const virtualRoot = $createParagraphNode();
    let currentBlock = null;
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      const isLineBreakNode = $isLineBreakNode(node);
      if (isLineBreakNode || $isDecoratorNode(node) && node.isInline() || $isElementNode(node) && node.isInline() || $isTextNode(node) || node.isParentRequired()) {
        if (currentBlock === null) {
          currentBlock = node.createParentElementNode();
          virtualRoot.append(currentBlock);
          // In the case of LineBreakNode, we just need to
          // add an empty ParagraphNode to the topLevelBlocks.
          if (isLineBreakNode) {
            continue;
          }
        }
        if (currentBlock !== null) {
          currentBlock.append(node);
        }
      } else {
        virtualRoot.append(node);
        currentBlock = null;
      }
    }
    return virtualRoot;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  let activeEditorState = null;
  let activeEditor = null;
  let isReadOnlyMode = false;
  let isAttemptingToRecoverFromReconcilerError = false;
  let infiniteTransformCount = 0;
  const observerOptions = {
    characterData: true,
    childList: true,
    subtree: true
  };
  function isCurrentlyReadOnlyMode() {
    return isReadOnlyMode || activeEditorState !== null && activeEditorState._readOnly;
  }
  function errorOnReadOnly() {
    if (isReadOnlyMode) {
      {
        throw Error(`Cannot use method in read-only mode.`);
      }
    }
  }
  function errorOnInfiniteTransforms() {
    if (infiniteTransformCount > 99) {
      {
        throw Error(`One or more transforms are endlessly triggering additional transforms. May have encountered infinite recursion caused by transforms that have their preconditions too lose and/or conflict with each other.`);
      }
    }
  }
  function getActiveEditorState() {
    if (activeEditorState === null) {
      {
        throw Error(`Unable to find an active editor state. State helpers or node methods can only be used synchronously during the callback of editor.update(), editor.read(), or editorState.read().${collectBuildInformation()}`);
      }
    }
    return activeEditorState;
  }
  function getActiveEditor() {
    if (activeEditor === null) {
      {
        throw Error(`Unable to find an active editor. This method can only be used synchronously during the callback of editor.update() or editor.read().${collectBuildInformation()}`);
      }
    }
    return activeEditor;
  }
  function collectBuildInformation() {
    let compatibleEditors = 0;
    const incompatibleEditors = new Set();
    const thisVersion = LexicalEditor.version;
    if (typeof window !== 'undefined') {
      for (const node of document.querySelectorAll('[contenteditable]')) {
        const editor = getEditorPropertyFromDOMNode(node);
        if (isLexicalEditor(editor)) {
          compatibleEditors++;
        } else if (editor) {
          let version = String(editor.constructor.version || '<0.17.1');
          if (version === thisVersion) {
            version += ' (separately built, likely a bundler configuration issue)';
          }
          incompatibleEditors.add(version);
        }
      }
    }
    let output = ` Detected on the page: ${compatibleEditors} compatible editor(s) with version ${thisVersion}`;
    if (incompatibleEditors.size) {
      output += ` and incompatible editors with versions ${Array.from(incompatibleEditors).join(', ')}`;
    }
    return output;
  }
  function internalGetActiveEditor() {
    return activeEditor;
  }
  function internalGetActiveEditorState() {
    return activeEditorState;
  }
  function $applyTransforms(editor, node, transformsCache) {
    const type = node.__type;
    const registeredNode = getRegisteredNodeOrThrow(editor, type);
    let transformsArr = transformsCache.get(type);
    if (transformsArr === undefined) {
      transformsArr = Array.from(registeredNode.transforms);
      transformsCache.set(type, transformsArr);
    }
    const transformsArrLength = transformsArr.length;
    for (let i = 0; i < transformsArrLength; i++) {
      transformsArr[i](node);
      if (!node.isAttached()) {
        break;
      }
    }
  }
  function $isNodeValidForTransform(node, compositionKey) {
    return node !== undefined &&
    // We don't want to transform nodes being composed
    node.__key !== compositionKey && node.isAttached();
  }
  function $normalizeAllDirtyTextNodes(editorState, editor) {
    const dirtyLeaves = editor._dirtyLeaves;
    const nodeMap = editorState._nodeMap;
    for (const nodeKey of dirtyLeaves) {
      const node = nodeMap.get(nodeKey);
      if ($isTextNode(node) && node.isAttached() && node.isSimpleText() && !node.isUnmergeable()) {
        $normalizeTextNode(node);
      }
    }
  }

  /**
   * Transform heuristic:
   * 1. We transform leaves first. If transforms generate additional dirty nodes we repeat step 1.
   * The reasoning behind this is that marking a leaf as dirty marks all its parent elements as dirty too.
   * 2. We transform elements. If element transforms generate additional dirty nodes we repeat step 1.
   * If element transforms only generate additional dirty elements we only repeat step 2.
   *
   * Note that to keep track of newly dirty nodes and subtrees we leverage the editor._dirtyNodes and
   * editor._subtrees which we reset in every loop.
   */
  function $applyAllTransforms(editorState, editor) {
    const dirtyLeaves = editor._dirtyLeaves;
    const dirtyElements = editor._dirtyElements;
    const nodeMap = editorState._nodeMap;
    const compositionKey = $getCompositionKey();
    const transformsCache = new Map();
    let untransformedDirtyLeaves = dirtyLeaves;
    let untransformedDirtyLeavesLength = untransformedDirtyLeaves.size;
    let untransformedDirtyElements = dirtyElements;
    let untransformedDirtyElementsLength = untransformedDirtyElements.size;
    while (untransformedDirtyLeavesLength > 0 || untransformedDirtyElementsLength > 0) {
      if (untransformedDirtyLeavesLength > 0) {
        // We leverage editor._dirtyLeaves to track the new dirty leaves after the transforms
        editor._dirtyLeaves = new Set();
        for (const nodeKey of untransformedDirtyLeaves) {
          const node = nodeMap.get(nodeKey);
          if ($isTextNode(node) && node.isAttached() && node.isSimpleText() && !node.isUnmergeable()) {
            $normalizeTextNode(node);
          }
          if (node !== undefined && $isNodeValidForTransform(node, compositionKey)) {
            $applyTransforms(editor, node, transformsCache);
          }
          dirtyLeaves.add(nodeKey);
        }
        untransformedDirtyLeaves = editor._dirtyLeaves;
        untransformedDirtyLeavesLength = untransformedDirtyLeaves.size;

        // We want to prioritize node transforms over element transforms
        if (untransformedDirtyLeavesLength > 0) {
          infiniteTransformCount++;
          continue;
        }
      }

      // All dirty leaves have been processed. Let's do elements!
      // We have previously processed dirty leaves, so let's restart the editor leaves Set to track
      // new ones caused by element transforms
      editor._dirtyLeaves = new Set();
      editor._dirtyElements = new Map();
      for (const currentUntransformedDirtyElement of untransformedDirtyElements) {
        const nodeKey = currentUntransformedDirtyElement[0];
        const intentionallyMarkedAsDirty = currentUntransformedDirtyElement[1];
        if (nodeKey !== 'root' && !intentionallyMarkedAsDirty) {
          continue;
        }
        const node = nodeMap.get(nodeKey);
        if (node !== undefined && $isNodeValidForTransform(node, compositionKey)) {
          $applyTransforms(editor, node, transformsCache);
        }
        dirtyElements.set(nodeKey, intentionallyMarkedAsDirty);
      }
      untransformedDirtyLeaves = editor._dirtyLeaves;
      untransformedDirtyLeavesLength = untransformedDirtyLeaves.size;
      untransformedDirtyElements = editor._dirtyElements;
      untransformedDirtyElementsLength = untransformedDirtyElements.size;
      infiniteTransformCount++;
    }
    editor._dirtyLeaves = dirtyLeaves;
    editor._dirtyElements = dirtyElements;
  }
  function $parseSerializedNode(serializedNode) {
    const internalSerializedNode = serializedNode;
    return $parseSerializedNodeImpl(internalSerializedNode, getActiveEditor()._nodes);
  }
  function $parseSerializedNodeImpl(serializedNode, registeredNodes) {
    const type = serializedNode.type;
    const registeredNode = registeredNodes.get(type);
    if (registeredNode === undefined) {
      {
        throw Error(`parseEditorState: type "${type}" + not found`);
      }
    }
    const nodeClass = registeredNode.klass;
    if (serializedNode.type !== nodeClass.getType()) {
      {
        throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
      }
    }
    const node = nodeClass.importJSON(serializedNode);
    const children = serializedNode.children;
    if ($isElementNode(node) && Array.isArray(children)) {
      for (let i = 0; i < children.length; i++) {
        const serializedJSONChildNode = children[i];
        const childNode = $parseSerializedNodeImpl(serializedJSONChildNode, registeredNodes);
        node.append(childNode);
      }
    }
    return node;
  }
  function parseEditorState(serializedEditorState, editor, updateFn) {
    const editorState = createEmptyEditorState();
    const previousActiveEditorState = activeEditorState;
    const previousReadOnlyMode = isReadOnlyMode;
    const previousActiveEditor = activeEditor;
    const previousDirtyElements = editor._dirtyElements;
    const previousDirtyLeaves = editor._dirtyLeaves;
    const previousCloneNotNeeded = editor._cloneNotNeeded;
    const previousDirtyType = editor._dirtyType;
    editor._dirtyElements = new Map();
    editor._dirtyLeaves = new Set();
    editor._cloneNotNeeded = new Set();
    editor._dirtyType = 0;
    activeEditorState = editorState;
    isReadOnlyMode = false;
    activeEditor = editor;
    try {
      const registeredNodes = editor._nodes;
      const serializedNode = serializedEditorState.root;
      $parseSerializedNodeImpl(serializedNode, registeredNodes);
      if (updateFn) {
        updateFn();
      }

      // Make the editorState immutable
      editorState._readOnly = true;
      {
        handleDEVOnlyPendingUpdateGuarantees(editorState);
      }
    } catch (error) {
      if (error instanceof Error) {
        editor._onError(error);
      }
    } finally {
      editor._dirtyElements = previousDirtyElements;
      editor._dirtyLeaves = previousDirtyLeaves;
      editor._cloneNotNeeded = previousCloneNotNeeded;
      editor._dirtyType = previousDirtyType;
      activeEditorState = previousActiveEditorState;
      isReadOnlyMode = previousReadOnlyMode;
      activeEditor = previousActiveEditor;
    }
    return editorState;
  }

  // This technically isn't an update but given we need
  // exposure to the module's active bindings, we have this
  // function here

  function readEditorState(editor, editorState, callbackFn) {
    const previousActiveEditorState = activeEditorState;
    const previousReadOnlyMode = isReadOnlyMode;
    const previousActiveEditor = activeEditor;
    activeEditorState = editorState;
    isReadOnlyMode = true;
    activeEditor = editor;
    try {
      return callbackFn();
    } finally {
      activeEditorState = previousActiveEditorState;
      isReadOnlyMode = previousReadOnlyMode;
      activeEditor = previousActiveEditor;
    }
  }
  function handleDEVOnlyPendingUpdateGuarantees(pendingEditorState) {
    // Given we can't Object.freeze the nodeMap as it's a Map,
    // we instead replace its set, clear and delete methods.
    const nodeMap = pendingEditorState._nodeMap;
    nodeMap.set = () => {
      throw new Error('Cannot call set() on a frozen Lexical node map');
    };
    nodeMap.clear = () => {
      throw new Error('Cannot call clear() on a frozen Lexical node map');
    };
    nodeMap.delete = () => {
      throw new Error('Cannot call delete() on a frozen Lexical node map');
    };
  }
  function $commitPendingUpdates(editor, recoveryEditorState) {
    const pendingEditorState = editor._pendingEditorState;
    const rootElement = editor._rootElement;
    const shouldSkipDOM = editor._headless || rootElement === null;
    if (pendingEditorState === null) {
      return;
    }

    // ======
    // Reconciliation has started.
    // ======

    const currentEditorState = editor._editorState;
    const currentSelection = currentEditorState._selection;
    const pendingSelection = pendingEditorState._selection;
    const needsUpdate = editor._dirtyType !== NO_DIRTY_NODES;
    const previousActiveEditorState = activeEditorState;
    const previousReadOnlyMode = isReadOnlyMode;
    const previousActiveEditor = activeEditor;
    const previouslyUpdating = editor._updating;
    const observer = editor._observer;
    let mutatedNodes = null;
    editor._pendingEditorState = null;
    editor._editorState = pendingEditorState;
    if (!shouldSkipDOM && needsUpdate && observer !== null) {
      activeEditor = editor;
      activeEditorState = pendingEditorState;
      isReadOnlyMode = false;
      // We don't want updates to sync block the reconciliation.
      editor._updating = true;
      try {
        const dirtyType = editor._dirtyType;
        const dirtyElements = editor._dirtyElements;
        const dirtyLeaves = editor._dirtyLeaves;
        observer.disconnect();
        mutatedNodes = $reconcileRoot(currentEditorState, pendingEditorState, editor, dirtyType, dirtyElements, dirtyLeaves);
      } catch (error) {
        // Report errors
        if (error instanceof Error) {
          editor._onError(error);
        }

        // Reset editor and restore incoming editor state to the DOM
        if (!isAttemptingToRecoverFromReconcilerError) {
          resetEditor(editor, null, rootElement, pendingEditorState);
          initMutationObserver(editor);
          editor._dirtyType = FULL_RECONCILE;
          isAttemptingToRecoverFromReconcilerError = true;
          $commitPendingUpdates(editor, currentEditorState);
          isAttemptingToRecoverFromReconcilerError = false;
        } else {
          // To avoid a possible situation of infinite loops, lets throw
          throw error;
        }
        return;
      } finally {
        observer.observe(rootElement, observerOptions);
        editor._updating = previouslyUpdating;
        activeEditorState = previousActiveEditorState;
        isReadOnlyMode = previousReadOnlyMode;
        activeEditor = previousActiveEditor;
      }
    }
    if (!pendingEditorState._readOnly) {
      pendingEditorState._readOnly = true;
      {
        handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
        if ($isRangeSelection(pendingSelection)) {
          Object.freeze(pendingSelection.anchor);
          Object.freeze(pendingSelection.focus);
        }
        Object.freeze(pendingSelection);
      }
    }
    const dirtyLeaves = editor._dirtyLeaves;
    const dirtyElements = editor._dirtyElements;
    const normalizedNodes = editor._normalizedNodes;
    const tags = editor._updateTags;
    const deferred = editor._deferred;
    if (needsUpdate) {
      editor._dirtyType = NO_DIRTY_NODES;
      editor._cloneNotNeeded.clear();
      editor._dirtyLeaves = new Set();
      editor._dirtyElements = new Map();
      editor._normalizedNodes = new Set();
      editor._updateTags = new Set();
    }
    $garbageCollectDetachedDecorators(editor, pendingEditorState);

    // ======
    // Reconciliation has finished. Now update selection and trigger listeners.
    // ======

    const domSelection = shouldSkipDOM ? null : getDOMSelection(editor._window);

    // Attempt to update the DOM selection, including focusing of the root element,
    // and scroll into view if needed.
    if (editor._editable &&
    // domSelection will be null in headless
    domSelection !== null && (needsUpdate || pendingSelection === null || pendingSelection.dirty)) {
      activeEditor = editor;
      activeEditorState = pendingEditorState;
      try {
        if (observer !== null) {
          observer.disconnect();
        }
        if (needsUpdate || pendingSelection === null || pendingSelection.dirty) {
          const blockCursorElement = editor._blockCursorElement;
          if (blockCursorElement !== null) {
            removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
          }
          updateDOMSelection(currentSelection, pendingSelection, editor, domSelection, tags, rootElement);
        }
        updateDOMBlockCursorElement(editor, rootElement, pendingSelection);
        if (observer !== null) {
          observer.observe(rootElement, observerOptions);
        }
      } finally {
        activeEditor = previousActiveEditor;
        activeEditorState = previousActiveEditorState;
      }
    }
    if (mutatedNodes !== null) {
      triggerMutationListeners(editor, mutatedNodes, tags, dirtyLeaves, currentEditorState);
    }
    if (!$isRangeSelection(pendingSelection) && pendingSelection !== null && (currentSelection === null || !currentSelection.is(pendingSelection))) {
      editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
    }
    /**
     * Capture pendingDecorators after garbage collecting detached decorators
     */
    const pendingDecorators = editor._pendingDecorators;
    if (pendingDecorators !== null) {
      editor._decorators = pendingDecorators;
      editor._pendingDecorators = null;
      triggerListeners('decorator', editor, true, pendingDecorators);
    }

    // If reconciler fails, we reset whole editor (so current editor state becomes empty)
    // and attempt to re-render pendingEditorState. If that goes through we trigger
    // listeners, but instead use recoverEditorState which is current editor state before reset
    // This specifically important for collab that relies on prevEditorState from update
    // listener to calculate delta of changed nodes/properties
    triggerTextContentListeners(editor, recoveryEditorState || currentEditorState, pendingEditorState);
    triggerListeners('update', editor, true, {
      dirtyElements,
      dirtyLeaves,
      editorState: pendingEditorState,
      normalizedNodes,
      prevEditorState: recoveryEditorState || currentEditorState,
      tags
    });
    triggerDeferredUpdateCallbacks(editor, deferred);
    $triggerEnqueuedUpdates(editor);
  }
  function triggerTextContentListeners(editor, currentEditorState, pendingEditorState) {
    const currentTextContent = getEditorStateTextContent(currentEditorState);
    const latestTextContent = getEditorStateTextContent(pendingEditorState);
    if (currentTextContent !== latestTextContent) {
      triggerListeners('textcontent', editor, true, latestTextContent);
    }
  }
  function triggerMutationListeners(editor, mutatedNodes, updateTags, dirtyLeaves, prevEditorState) {
    const listeners = Array.from(editor._listeners.mutation);
    const listenersLength = listeners.length;
    for (let i = 0; i < listenersLength; i++) {
      const [listener, klass] = listeners[i];
      const mutatedNodesByType = mutatedNodes.get(klass);
      if (mutatedNodesByType !== undefined) {
        listener(mutatedNodesByType, {
          dirtyLeaves,
          prevEditorState,
          updateTags
        });
      }
    }
  }
  function triggerListeners(type, editor, isCurrentlyEnqueuingUpdates, ...payload) {
    const previouslyUpdating = editor._updating;
    editor._updating = isCurrentlyEnqueuingUpdates;
    try {
      const listeners = Array.from(editor._listeners[type]);
      for (let i = 0; i < listeners.length; i++) {
        // @ts-ignore
        listeners[i].apply(null, payload);
      }
    } finally {
      editor._updating = previouslyUpdating;
    }
  }
  function triggerCommandListeners(editor, type, payload) {
    if (editor._updating === false || activeEditor !== editor) {
      let returnVal = false;
      editor.update(() => {
        returnVal = triggerCommandListeners(editor, type, payload);
      });
      return returnVal;
    }
    const editors = getEditorsToPropagate(editor);
    for (let i = 4; i >= 0; i--) {
      for (let e = 0; e < editors.length; e++) {
        const currentEditor = editors[e];
        const commandListeners = currentEditor._commands;
        const listenerInPriorityOrder = commandListeners.get(type);
        if (listenerInPriorityOrder !== undefined) {
          const listenersSet = listenerInPriorityOrder[i];
          if (listenersSet !== undefined) {
            const listeners = Array.from(listenersSet);
            const listenersLength = listeners.length;
            for (let j = 0; j < listenersLength; j++) {
              if (listeners[j](payload, editor) === true) {
                return true;
              }
            }
          }
        }
      }
    }
    return false;
  }
  function $triggerEnqueuedUpdates(editor) {
    const queuedUpdates = editor._updates;
    if (queuedUpdates.length !== 0) {
      const queuedUpdate = queuedUpdates.shift();
      if (queuedUpdate) {
        const [updateFn, options] = queuedUpdate;
        $beginUpdate(editor, updateFn, options);
      }
    }
  }
  function triggerDeferredUpdateCallbacks(editor, deferred) {
    editor._deferred = [];
    if (deferred.length !== 0) {
      const previouslyUpdating = editor._updating;
      editor._updating = true;
      try {
        for (let i = 0; i < deferred.length; i++) {
          deferred[i]();
        }
      } finally {
        editor._updating = previouslyUpdating;
      }
    }
  }
  function processNestedUpdates(editor, initialSkipTransforms) {
    const queuedUpdates = editor._updates;
    let skipTransforms = initialSkipTransforms || false;

    // Updates might grow as we process them, we so we'll need
    // to handle each update as we go until the updates array is
    // empty.
    while (queuedUpdates.length !== 0) {
      const queuedUpdate = queuedUpdates.shift();
      if (queuedUpdate) {
        const [nextUpdateFn, options] = queuedUpdate;
        let onUpdate;
        let tag;
        if (options !== undefined) {
          onUpdate = options.onUpdate;
          tag = options.tag;
          if (options.skipTransforms) {
            skipTransforms = true;
          }
          if (options.discrete) {
            const pendingEditorState = editor._pendingEditorState;
            if (!(pendingEditorState !== null)) {
              throw Error(`Unexpected empty pending editor state on discrete nested update`);
            }
            pendingEditorState._flushSync = true;
          }
          if (onUpdate) {
            editor._deferred.push(onUpdate);
          }
          if (tag) {
            editor._updateTags.add(tag);
          }
        }
        nextUpdateFn();
      }
    }
    return skipTransforms;
  }
  function $beginUpdate(editor, updateFn, options) {
    const updateTags = editor._updateTags;
    let onUpdate;
    let tag;
    let skipTransforms = false;
    let discrete = false;
    if (options !== undefined) {
      onUpdate = options.onUpdate;
      tag = options.tag;
      if (tag != null) {
        updateTags.add(tag);
      }
      skipTransforms = options.skipTransforms || false;
      discrete = options.discrete || false;
    }
    if (onUpdate) {
      editor._deferred.push(onUpdate);
    }
    const currentEditorState = editor._editorState;
    let pendingEditorState = editor._pendingEditorState;
    let editorStateWasCloned = false;
    if (pendingEditorState === null || pendingEditorState._readOnly) {
      pendingEditorState = editor._pendingEditorState = cloneEditorState(pendingEditorState || currentEditorState);
      editorStateWasCloned = true;
    }
    pendingEditorState._flushSync = discrete;
    const previousActiveEditorState = activeEditorState;
    const previousReadOnlyMode = isReadOnlyMode;
    const previousActiveEditor = activeEditor;
    const previouslyUpdating = editor._updating;
    activeEditorState = pendingEditorState;
    isReadOnlyMode = false;
    editor._updating = true;
    activeEditor = editor;
    try {
      if (editorStateWasCloned) {
        if (editor._headless) {
          if (currentEditorState._selection !== null) {
            pendingEditorState._selection = currentEditorState._selection.clone();
          }
        } else {
          pendingEditorState._selection = $internalCreateSelection(editor);
        }
      }
      const startingCompositionKey = editor._compositionKey;
      updateFn();
      skipTransforms = processNestedUpdates(editor, skipTransforms);
      applySelectionTransforms(pendingEditorState, editor);
      if (editor._dirtyType !== NO_DIRTY_NODES) {
        if (skipTransforms) {
          $normalizeAllDirtyTextNodes(pendingEditorState, editor);
        } else {
          $applyAllTransforms(pendingEditorState, editor);
        }
        processNestedUpdates(editor);
        $garbageCollectDetachedNodes(currentEditorState, pendingEditorState, editor._dirtyLeaves, editor._dirtyElements);
      }
      const endingCompositionKey = editor._compositionKey;
      if (startingCompositionKey !== endingCompositionKey) {
        pendingEditorState._flushSync = true;
      }
      const pendingSelection = pendingEditorState._selection;
      if ($isRangeSelection(pendingSelection)) {
        const pendingNodeMap = pendingEditorState._nodeMap;
        const anchorKey = pendingSelection.anchor.key;
        const focusKey = pendingSelection.focus.key;
        if (pendingNodeMap.get(anchorKey) === undefined || pendingNodeMap.get(focusKey) === undefined) {
          {
            throw Error(`updateEditor: selection has been lost because the previously selected nodes have been removed and selection wasn't moved to another node. Ensure selection changes after removing/replacing a selected node.`);
          }
        }
      } else if ($isNodeSelection(pendingSelection)) {
        // TODO: we should also validate node selection?
        if (pendingSelection._nodes.size === 0) {
          pendingEditorState._selection = null;
        }
      }
    } catch (error) {
      // Report errors
      if (error instanceof Error) {
        editor._onError(error);
      }

      // Restore existing editor state to the DOM
      editor._pendingEditorState = currentEditorState;
      editor._dirtyType = FULL_RECONCILE;
      editor._cloneNotNeeded.clear();
      editor._dirtyLeaves = new Set();
      editor._dirtyElements.clear();
      $commitPendingUpdates(editor);
      return;
    } finally {
      activeEditorState = previousActiveEditorState;
      isReadOnlyMode = previousReadOnlyMode;
      activeEditor = previousActiveEditor;
      editor._updating = previouslyUpdating;
      infiniteTransformCount = 0;
    }
    const shouldUpdate = editor._dirtyType !== NO_DIRTY_NODES || editorStateHasDirtySelection(pendingEditorState, editor);
    if (shouldUpdate) {
      if (pendingEditorState._flushSync) {
        pendingEditorState._flushSync = false;
        $commitPendingUpdates(editor);
      } else if (editorStateWasCloned) {
        scheduleMicroTask(() => {
          $commitPendingUpdates(editor);
        });
      }
    } else {
      pendingEditorState._flushSync = false;
      if (editorStateWasCloned) {
        updateTags.clear();
        editor._deferred = [];
        editor._pendingEditorState = null;
      }
    }
  }
  function updateEditor(editor, updateFn, options) {
    if (editor._updating) {
      editor._updates.push([updateFn, options]);
    } else {
      $beginUpdate(editor, updateFn, options);
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging

  /** @noInheritDoc */
  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
  class ElementNode extends LexicalNode {
    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    constructor(key) {
      super(key);
      this.__first = null;
      this.__last = null;
      this.__size = 0;
      this.__format = 0;
      this.__style = '';
      this.__indent = 0;
      this.__dir = null;
    }
    afterCloneFrom(prevNode) {
      super.afterCloneFrom(prevNode);
      this.__first = prevNode.__first;
      this.__last = prevNode.__last;
      this.__size = prevNode.__size;
      this.__indent = prevNode.__indent;
      this.__format = prevNode.__format;
      this.__style = prevNode.__style;
      this.__dir = prevNode.__dir;
    }
    getFormat() {
      const self = this.getLatest();
      return self.__format;
    }
    getFormatType() {
      const format = this.getFormat();
      return ELEMENT_FORMAT_TO_TYPE[format] || '';
    }
    getStyle() {
      const self = this.getLatest();
      return self.__style;
    }
    getIndent() {
      const self = this.getLatest();
      return self.__indent;
    }
    getChildren() {
      const children = [];
      let child = this.getFirstChild();
      while (child !== null) {
        children.push(child);
        child = child.getNextSibling();
      }
      return children;
    }
    getChildrenKeys() {
      const children = [];
      let child = this.getFirstChild();
      while (child !== null) {
        children.push(child.__key);
        child = child.getNextSibling();
      }
      return children;
    }
    getChildrenSize() {
      const self = this.getLatest();
      return self.__size;
    }
    isEmpty() {
      return this.getChildrenSize() === 0;
    }
    isDirty() {
      const editor = getActiveEditor();
      const dirtyElements = editor._dirtyElements;
      return dirtyElements !== null && dirtyElements.has(this.__key);
    }
    isLastChild() {
      const self = this.getLatest();
      const parentLastChild = this.getParentOrThrow().getLastChild();
      return parentLastChild !== null && parentLastChild.is(self);
    }
    getAllTextNodes() {
      const textNodes = [];
      let child = this.getFirstChild();
      while (child !== null) {
        if ($isTextNode(child)) {
          textNodes.push(child);
        }
        if ($isElementNode(child)) {
          const subChildrenNodes = child.getAllTextNodes();
          textNodes.push(...subChildrenNodes);
        }
        child = child.getNextSibling();
      }
      return textNodes;
    }
    getFirstDescendant() {
      let node = this.getFirstChild();
      while ($isElementNode(node)) {
        const child = node.getFirstChild();
        if (child === null) {
          break;
        }
        node = child;
      }
      return node;
    }
    getLastDescendant() {
      let node = this.getLastChild();
      while ($isElementNode(node)) {
        const child = node.getLastChild();
        if (child === null) {
          break;
        }
        node = child;
      }
      return node;
    }
    getDescendantByIndex(index) {
      const children = this.getChildren();
      const childrenLength = children.length;
      // For non-empty element nodes, we resolve its descendant
      // (either a leaf node or the bottom-most element)
      if (index >= childrenLength) {
        const resolvedNode = children[childrenLength - 1];
        return $isElementNode(resolvedNode) && resolvedNode.getLastDescendant() || resolvedNode || null;
      }
      const resolvedNode = children[index];
      return $isElementNode(resolvedNode) && resolvedNode.getFirstDescendant() || resolvedNode || null;
    }
    getFirstChild() {
      const self = this.getLatest();
      const firstKey = self.__first;
      return firstKey === null ? null : $getNodeByKey(firstKey);
    }
    getFirstChildOrThrow() {
      const firstChild = this.getFirstChild();
      if (firstChild === null) {
        {
          throw Error(`Expected node ${this.__key} to have a first child.`);
        }
      }
      return firstChild;
    }
    getLastChild() {
      const self = this.getLatest();
      const lastKey = self.__last;
      return lastKey === null ? null : $getNodeByKey(lastKey);
    }
    getLastChildOrThrow() {
      const lastChild = this.getLastChild();
      if (lastChild === null) {
        {
          throw Error(`Expected node ${this.__key} to have a last child.`);
        }
      }
      return lastChild;
    }
    getChildAtIndex(index) {
      const size = this.getChildrenSize();
      let node;
      let i;
      if (index < size / 2) {
        node = this.getFirstChild();
        i = 0;
        while (node !== null && i <= index) {
          if (i === index) {
            return node;
          }
          node = node.getNextSibling();
          i++;
        }
        return null;
      }
      node = this.getLastChild();
      i = size - 1;
      while (node !== null && i >= index) {
        if (i === index) {
          return node;
        }
        node = node.getPreviousSibling();
        i--;
      }
      return null;
    }
    getTextContent() {
      let textContent = '';
      const children = this.getChildren();
      const childrenLength = children.length;
      for (let i = 0; i < childrenLength; i++) {
        const child = children[i];
        textContent += child.getTextContent();
        if ($isElementNode(child) && i !== childrenLength - 1 && !child.isInline()) {
          textContent += DOUBLE_LINE_BREAK;
        }
      }
      return textContent;
    }
    getTextContentSize() {
      let textContentSize = 0;
      const children = this.getChildren();
      const childrenLength = children.length;
      for (let i = 0; i < childrenLength; i++) {
        const child = children[i];
        textContentSize += child.getTextContentSize();
        if ($isElementNode(child) && i !== childrenLength - 1 && !child.isInline()) {
          textContentSize += DOUBLE_LINE_BREAK.length;
        }
      }
      return textContentSize;
    }
    getDirection() {
      const self = this.getLatest();
      return self.__dir;
    }
    hasFormat(type) {
      if (type !== '') {
        const formatFlag = ELEMENT_TYPE_TO_FORMAT[type];
        return (this.getFormat() & formatFlag) !== 0;
      }
      return false;
    }

    // Mutators

    select(_anchorOffset, _focusOffset) {
      errorOnReadOnly();
      const selection = $getSelection();
      let anchorOffset = _anchorOffset;
      let focusOffset = _focusOffset;
      const childrenCount = this.getChildrenSize();
      if (!this.canBeEmpty()) {
        if (_anchorOffset === 0 && _focusOffset === 0) {
          const firstChild = this.getFirstChild();
          if ($isTextNode(firstChild) || $isElementNode(firstChild)) {
            return firstChild.select(0, 0);
          }
        } else if ((_anchorOffset === undefined || _anchorOffset === childrenCount) && (_focusOffset === undefined || _focusOffset === childrenCount)) {
          const lastChild = this.getLastChild();
          if ($isTextNode(lastChild) || $isElementNode(lastChild)) {
            return lastChild.select();
          }
        }
      }
      if (anchorOffset === undefined) {
        anchorOffset = childrenCount;
      }
      if (focusOffset === undefined) {
        focusOffset = childrenCount;
      }
      const key = this.__key;
      if (!$isRangeSelection(selection)) {
        return $internalMakeRangeSelection(key, anchorOffset, key, focusOffset, 'element', 'element');
      } else {
        selection.anchor.set(key, anchorOffset, 'element');
        selection.focus.set(key, focusOffset, 'element');
        selection.dirty = true;
      }
      return selection;
    }
    selectStart() {
      const firstNode = this.getFirstDescendant();
      return firstNode ? firstNode.selectStart() : this.select();
    }
    selectEnd() {
      const lastNode = this.getLastDescendant();
      return lastNode ? lastNode.selectEnd() : this.select();
    }
    clear() {
      const writableSelf = this.getWritable();
      const children = this.getChildren();
      children.forEach(child => child.remove());
      return writableSelf;
    }
    append(...nodesToAppend) {
      return this.splice(this.getChildrenSize(), 0, nodesToAppend);
    }
    setDirection(direction) {
      const self = this.getWritable();
      self.__dir = direction;
      return self;
    }
    setFormat(type) {
      const self = this.getWritable();
      self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0;
      return this;
    }
    setStyle(style) {
      const self = this.getWritable();
      self.__style = style || '';
      return this;
    }
    setIndent(indentLevel) {
      const self = this.getWritable();
      self.__indent = indentLevel;
      return this;
    }
    splice(start, deleteCount, nodesToInsert) {
      const nodesToInsertLength = nodesToInsert.length;
      const oldSize = this.getChildrenSize();
      const writableSelf = this.getWritable();
      const writableSelfKey = writableSelf.__key;
      const nodesToInsertKeys = [];
      const nodesToRemoveKeys = [];
      const nodeAfterRange = this.getChildAtIndex(start + deleteCount);
      let nodeBeforeRange = null;
      let newSize = oldSize - deleteCount + nodesToInsertLength;
      if (start !== 0) {
        if (start === oldSize) {
          nodeBeforeRange = this.getLastChild();
        } else {
          const node = this.getChildAtIndex(start);
          if (node !== null) {
            nodeBeforeRange = node.getPreviousSibling();
          }
        }
      }
      if (deleteCount > 0) {
        let nodeToDelete = nodeBeforeRange === null ? this.getFirstChild() : nodeBeforeRange.getNextSibling();
        for (let i = 0; i < deleteCount; i++) {
          if (nodeToDelete === null) {
            {
              throw Error(`splice: sibling not found`);
            }
          }
          const nextSibling = nodeToDelete.getNextSibling();
          const nodeKeyToDelete = nodeToDelete.__key;
          const writableNodeToDelete = nodeToDelete.getWritable();
          removeFromParent(writableNodeToDelete);
          nodesToRemoveKeys.push(nodeKeyToDelete);
          nodeToDelete = nextSibling;
        }
      }
      let prevNode = nodeBeforeRange;
      for (let i = 0; i < nodesToInsertLength; i++) {
        const nodeToInsert = nodesToInsert[i];
        if (prevNode !== null && nodeToInsert.is(prevNode)) {
          nodeBeforeRange = prevNode = prevNode.getPreviousSibling();
        }
        const writableNodeToInsert = nodeToInsert.getWritable();
        if (writableNodeToInsert.__parent === writableSelfKey) {
          newSize--;
        }
        removeFromParent(writableNodeToInsert);
        const nodeKeyToInsert = nodeToInsert.__key;
        if (prevNode === null) {
          writableSelf.__first = nodeKeyToInsert;
          writableNodeToInsert.__prev = null;
        } else {
          const writablePrevNode = prevNode.getWritable();
          writablePrevNode.__next = nodeKeyToInsert;
          writableNodeToInsert.__prev = writablePrevNode.__key;
        }
        if (nodeToInsert.__key === writableSelfKey) {
          {
            throw Error(`append: attempting to append self`);
          }
        }
        // Set child parent to self
        writableNodeToInsert.__parent = writableSelfKey;
        nodesToInsertKeys.push(nodeKeyToInsert);
        prevNode = nodeToInsert;
      }
      if (start + deleteCount === oldSize) {
        if (prevNode !== null) {
          const writablePrevNode = prevNode.getWritable();
          writablePrevNode.__next = null;
          writableSelf.__last = prevNode.__key;
        }
      } else if (nodeAfterRange !== null) {
        const writableNodeAfterRange = nodeAfterRange.getWritable();
        if (prevNode !== null) {
          const writablePrevNode = prevNode.getWritable();
          writableNodeAfterRange.__prev = prevNode.__key;
          writablePrevNode.__next = nodeAfterRange.__key;
        } else {
          writableNodeAfterRange.__prev = null;
        }
      }
      writableSelf.__size = newSize;

      // In case of deletion we need to adjust selection, unlink removed nodes
      // and clean up node itself if it becomes empty. None of these needed
      // for insertion-only cases
      if (nodesToRemoveKeys.length) {
        // Adjusting selection, in case node that was anchor/focus will be deleted
        const selection = $getSelection();
        if ($isRangeSelection(selection)) {
          const nodesToRemoveKeySet = new Set(nodesToRemoveKeys);
          const nodesToInsertKeySet = new Set(nodesToInsertKeys);
          const {
            anchor,
            focus
          } = selection;
          if (isPointRemoved(anchor, nodesToRemoveKeySet, nodesToInsertKeySet)) {
            moveSelectionPointToSibling(anchor, anchor.getNode(), this, nodeBeforeRange, nodeAfterRange);
          }
          if (isPointRemoved(focus, nodesToRemoveKeySet, nodesToInsertKeySet)) {
            moveSelectionPointToSibling(focus, focus.getNode(), this, nodeBeforeRange, nodeAfterRange);
          }
          // Cleanup if node can't be empty
          if (newSize === 0 && !this.canBeEmpty() && !$isRootOrShadowRoot(this)) {
            this.remove();
          }
        }
      }
      return writableSelf;
    }
    // JSON serialization
    exportJSON() {
      return {
        children: [],
        direction: this.getDirection(),
        format: this.getFormatType(),
        indent: this.getIndent(),
        type: 'element',
        version: 1
      };
    }
    // These are intended to be extends for specific element heuristics.
    insertNewAfter(selection, restoreSelection) {
      return null;
    }
    canIndent() {
      return true;
    }
    /*
     * This method controls the behavior of a the node during backwards
     * deletion (i.e., backspace) when selection is at the beginning of
     * the node (offset 0)
     */
    collapseAtStart(selection) {
      return false;
    }
    excludeFromCopy(destination) {
      return false;
    }
    /** @deprecated @internal */
    canReplaceWith(replacement) {
      return true;
    }
    /** @deprecated @internal */
    canInsertAfter(node) {
      return true;
    }
    canBeEmpty() {
      return true;
    }
    canInsertTextBefore() {
      return true;
    }
    canInsertTextAfter() {
      return true;
    }
    isInline() {
      return false;
    }
    // A shadow root is a Node that behaves like RootNode. The shadow root (and RootNode) mark the
    // end of the hiercharchy, most implementations should treat it as there's nothing (upwards)
    // beyond this point. For example, node.getTopLevelElement(), when performed inside a TableCellNode
    // will return the immediate first child underneath TableCellNode instead of RootNode.
    isShadowRoot() {
      return false;
    }
    /** @deprecated @internal */
    canMergeWith(node) {
      return false;
    }
    extractWithChild(child, selection, destination) {
      return false;
    }

    /**
     * Determines whether this node, when empty, can merge with a first block
     * of nodes being inserted.
     *
     * This method is specifically called in {@link RangeSelection.insertNodes}
     * to determine merging behavior during nodes insertion.
     *
     * @example
     * // In a ListItemNode or QuoteNode implementation:
     * canMergeWhenEmpty(): true {
     *  return true;
     * }
     */
    canMergeWhenEmpty() {
      return false;
    }
  }
  function $isElementNode(node) {
    return node instanceof ElementNode;
  }
  function isPointRemoved(point, nodesToRemoveKeySet, nodesToInsertKeySet) {
    let node = point.getNode();
    while (node) {
      const nodeKey = node.__key;
      if (nodesToRemoveKeySet.has(nodeKey) && !nodesToInsertKeySet.has(nodeKey)) {
        return true;
      }
      node = node.getParent();
    }
    return false;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // eslint-disable-next-line @typescript-eslint/no-unused-vars

  /** @noInheritDoc */
  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
  class DecoratorNode extends LexicalNode {
    constructor(key) {
      super(key);
    }

    /**
     * The returned value is added to the LexicalEditor._decorators
     */
    decorate(editor, config) {
      {
        throw Error(`decorate: base method not extended`);
      }
    }
    isIsolated() {
      return false;
    }
    isInline() {
      return true;
    }
    isKeyboardSelectable() {
      return true;
    }
  }
  function $isDecoratorNode(node) {
    return node instanceof DecoratorNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class RootNode extends ElementNode {
    /** @internal */

    static getType() {
      return 'root';
    }
    static clone() {
      return new RootNode();
    }
    constructor() {
      super('root');
      this.__cachedText = null;
    }
    getTopLevelElementOrThrow() {
      {
        throw Error(`getTopLevelElementOrThrow: root nodes are not top level elements`);
      }
    }
    getTextContent() {
      const cachedText = this.__cachedText;
      if (isCurrentlyReadOnlyMode() || getActiveEditor()._dirtyType === NO_DIRTY_NODES) {
        if (cachedText !== null) {
          return cachedText;
        }
      }
      return super.getTextContent();
    }
    remove() {
      {
        throw Error(`remove: cannot be called on root nodes`);
      }
    }
    replace(node) {
      {
        throw Error(`replace: cannot be called on root nodes`);
      }
    }
    insertBefore(nodeToInsert) {
      {
        throw Error(`insertBefore: cannot be called on root nodes`);
      }
    }
    insertAfter(nodeToInsert) {
      {
        throw Error(`insertAfter: cannot be called on root nodes`);
      }
    }

    // View

    updateDOM(prevNode, dom) {
      return false;
    }

    // Mutate

    append(...nodesToAppend) {
      for (let i = 0; i < nodesToAppend.length; i++) {
        const node = nodesToAppend[i];
        if (!$isElementNode(node) && !$isDecoratorNode(node)) {
          {
            throw Error(`rootNode.append: Only element or decorator nodes can be appended to the root node`);
          }
        }
      }
      return super.append(...nodesToAppend);
    }
    static importJSON(serializedNode) {
      // We don't create a root, and instead use the existing root.
      const node = $getRoot();
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      return node;
    }
    exportJSON() {
      return {
        children: [],
        direction: this.getDirection(),
        format: this.getFormatType(),
        indent: this.getIndent(),
        type: 'root',
        version: 1
      };
    }
    collapseAtStart() {
      return true;
    }
  }
  function $createRootNode() {
    return new RootNode();
  }
  function $isRootNode(node) {
    return node instanceof RootNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function editorStateHasDirtySelection(editorState, editor) {
    const currentSelection = editor.getEditorState()._selection;
    const pendingSelection = editorState._selection;

    // Check if we need to update because of changes in selection
    if (pendingSelection !== null) {
      if (pendingSelection.dirty || !pendingSelection.is(currentSelection)) {
        return true;
      }
    } else if (currentSelection !== null) {
      return true;
    }
    return false;
  }
  function cloneEditorState(current) {
    return new EditorState(new Map(current._nodeMap));
  }
  function createEmptyEditorState() {
    return new EditorState(new Map([['root', $createRootNode()]]));
  }
  function exportNodeToJSON(node) {
    const serializedNode = node.exportJSON();
    const nodeClass = node.constructor;
    if (serializedNode.type !== nodeClass.getType()) {
      {
        throw Error(`LexicalNode: Node ${nodeClass.name} does not match the serialized type. Check if .exportJSON() is implemented and it is returning the correct type.`);
      }
    }
    if ($isElementNode(node)) {
      const serializedChildren = serializedNode.children;
      if (!Array.isArray(serializedChildren)) {
        {
          throw Error(`LexicalNode: Node ${nodeClass.name} is an element but .exportJSON() does not have a children array.`);
        }
      }
      const children = node.getChildren();
      for (let i = 0; i < children.length; i++) {
        const child = children[i];
        const serializedChildNode = exportNodeToJSON(child);
        serializedChildren.push(serializedChildNode);
      }
    }

    // @ts-expect-error
    return serializedNode;
  }
  class EditorState {
    constructor(nodeMap, selection) {
      this._nodeMap = nodeMap;
      this._selection = selection || null;
      this._flushSync = false;
      this._readOnly = false;
    }
    isEmpty() {
      return this._nodeMap.size === 1 && this._selection === null;
    }
    read(callbackFn, options) {
      return readEditorState(options && options.editor || null, this, callbackFn);
    }
    clone(selection) {
      const editorState = new EditorState(this._nodeMap, selection === undefined ? this._selection : selection);
      editorState._readOnly = true;
      return editorState;
    }
    toJSON() {
      return readEditorState(null, this, () => ({
        root: exportNodeToJSON($getRoot())
      }));
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // TODO: Cleanup ArtificialNode__DO_NOT_USE #5966
  class ArtificialNode__DO_NOT_USE extends ElementNode {
    static getType() {
      return 'artificial';
    }
    createDOM(config) {
      // this isnt supposed to be used and is not used anywhere but defining it to appease the API
      const dom = document.createElement('div');
      return dom;
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class ParagraphNode extends ElementNode {
    /** @internal */

    constructor(key) {
      super(key);
      this.__textFormat = 0;
      this.__textStyle = '';
    }
    static getType() {
      return 'paragraph';
    }
    getTextFormat() {
      const self = this.getLatest();
      return self.__textFormat;
    }
    setTextFormat(type) {
      const self = this.getWritable();
      self.__textFormat = type;
      return self;
    }
    hasTextFormat(type) {
      const formatFlag = TEXT_TYPE_TO_FORMAT[type];
      return (this.getTextFormat() & formatFlag) !== 0;
    }
    getTextStyle() {
      const self = this.getLatest();
      return self.__textStyle;
    }
    setTextStyle(style) {
      const self = this.getWritable();
      self.__textStyle = style;
      return self;
    }
    static clone(node) {
      return new ParagraphNode(node.__key);
    }
    afterCloneFrom(prevNode) {
      super.afterCloneFrom(prevNode);
      this.__textFormat = prevNode.__textFormat;
      this.__textStyle = prevNode.__textStyle;
    }

    // View

    createDOM(config) {
      const dom = document.createElement('p');
      const classNames = getCachedClassNameArray(config.theme, 'paragraph');
      if (classNames !== undefined) {
        const domClassList = dom.classList;
        domClassList.add(...classNames);
      }
      return dom;
    }
    updateDOM(prevNode, dom, config) {
      return false;
    }
    static importDOM() {
      return {
        p: node => ({
          conversion: $convertParagraphElement,
          priority: 0
        })
      };
    }
    exportDOM(editor) {
      const {
        element
      } = super.exportDOM(editor);
      if (element && isHTMLElement(element)) {
        if (this.isEmpty()) {
          element.append(document.createElement('br'));
        }
        const formatType = this.getFormatType();
        element.style.textAlign = formatType;
        const direction = this.getDirection();
        if (direction) {
          element.dir = direction;
        }
        const indent = this.getIndent();
        if (indent > 0) {
          // padding-inline-start is not widely supported in email HTML, but
          // Lexical Reconciler uses padding-inline-start. Using text-indent instead.
          element.style.textIndent = `${indent * 20}px`;
        }
      }
      return {
        element
      };
    }
    static importJSON(serializedNode) {
      const node = $createParagraphNode();
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      node.setTextFormat(serializedNode.textFormat);
      return node;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        textFormat: this.getTextFormat(),
        textStyle: this.getTextStyle(),
        type: 'paragraph',
        version: 1
      };
    }

    // Mutation

    insertNewAfter(rangeSelection, restoreSelection) {
      const newElement = $createParagraphNode();
      newElement.setTextFormat(rangeSelection.format);
      newElement.setTextStyle(rangeSelection.style);
      const direction = this.getDirection();
      newElement.setDirection(direction);
      newElement.setFormat(this.getFormatType());
      newElement.setStyle(this.getTextStyle());
      this.insertAfter(newElement, restoreSelection);
      return newElement;
    }
    collapseAtStart() {
      const children = this.getChildren();
      // If we have an empty (trimmed) first paragraph and try and remove it,
      // delete the paragraph as long as we have another sibling to go to
      if (children.length === 0 || $isTextNode(children[0]) && children[0].getTextContent().trim() === '') {
        const nextSibling = this.getNextSibling();
        if (nextSibling !== null) {
          this.selectNext();
          this.remove();
          return true;
        }
        const prevSibling = this.getPreviousSibling();
        if (prevSibling !== null) {
          this.selectPrevious();
          this.remove();
          return true;
        }
      }
      return false;
    }
  }
  function $convertParagraphElement(element) {
    const node = $createParagraphNode();
    if (element.style) {
      node.setFormat(element.style.textAlign);
      const indent = parseInt(element.style.textIndent, 10) / 20;
      if (indent > 0) {
        node.setIndent(indent);
      }
    }
    return {
      node
    };
  }
  function $createParagraphNode() {
    return $applyNodeReplacement(new ParagraphNode());
  }
  function $isParagraphNode(node) {
    return node instanceof ParagraphNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // https://github.com/microsoft/TypeScript/issues/3841
  // eslint-disable-next-line @typescript-eslint/no-explicit-any

  // eslint-disable-next-line @typescript-eslint/no-explicit-any

  const DEFAULT_SKIP_INITIALIZATION = true;
  const COMMAND_PRIORITY_EDITOR = 0;
  const COMMAND_PRIORITY_LOW = 1;
  const COMMAND_PRIORITY_NORMAL = 2;
  const COMMAND_PRIORITY_HIGH = 3;
  const COMMAND_PRIORITY_CRITICAL = 4;

  // eslint-disable-next-line @typescript-eslint/no-unused-vars

  /**
   * Type helper for extracting the payload type from a command.
   *
   * @example
   * ```ts
   * const MY_COMMAND = createCommand<SomeType>();
   *
   * // ...
   *
   * editor.registerCommand(MY_COMMAND, payload => {
   *   // Type of `payload` is inferred here. But lets say we want to extract a function to delegate to
   *   handleMyCommand(editor, payload);
   *   return true;
   * });
   *
   * function handleMyCommand(editor: LexicalEditor, payload: CommandPayloadType<typeof MY_COMMAND>) {
   *   // `payload` is of type `SomeType`, extracted from the command.
   * }
   * ```
   */

  function resetEditor(editor, prevRootElement, nextRootElement, pendingEditorState) {
    const keyNodeMap = editor._keyToDOMMap;
    keyNodeMap.clear();
    editor._editorState = createEmptyEditorState();
    editor._pendingEditorState = pendingEditorState;
    editor._compositionKey = null;
    editor._dirtyType = NO_DIRTY_NODES;
    editor._cloneNotNeeded.clear();
    editor._dirtyLeaves = new Set();
    editor._dirtyElements.clear();
    editor._normalizedNodes = new Set();
    editor._updateTags = new Set();
    editor._updates = [];
    editor._blockCursorElement = null;
    const observer = editor._observer;
    if (observer !== null) {
      observer.disconnect();
      editor._observer = null;
    }

    // Remove all the DOM nodes from the root element
    if (prevRootElement !== null) {
      prevRootElement.textContent = '';
    }
    if (nextRootElement !== null) {
      nextRootElement.textContent = '';
      keyNodeMap.set('root', nextRootElement);
    }
  }
  function initializeConversionCache(nodes, additionalConversions) {
    const conversionCache = new Map();
    const handledConversions = new Set();
    const addConversionsToCache = map => {
      Object.keys(map).forEach(key => {
        let currentCache = conversionCache.get(key);
        if (currentCache === undefined) {
          currentCache = [];
          conversionCache.set(key, currentCache);
        }
        currentCache.push(map[key]);
      });
    };
    nodes.forEach(node => {
      const importDOM = node.klass.importDOM;
      if (importDOM == null || handledConversions.has(importDOM)) {
        return;
      }
      handledConversions.add(importDOM);
      const map = importDOM.call(node.klass);
      if (map !== null) {
        addConversionsToCache(map);
      }
    });
    if (additionalConversions) {
      addConversionsToCache(additionalConversions);
    }
    return conversionCache;
  }

  /**
   * Creates a new LexicalEditor attached to a single contentEditable (provided in the config). This is
   * the lowest-level initialization API for a LexicalEditor. If you're using React or another framework,
   * consider using the appropriate abstractions, such as LexicalComposer
   * @param editorConfig - the editor configuration.
   * @returns a LexicalEditor instance
   */
  function createEditor(editorConfig) {
    const config = editorConfig || {};
    const activeEditor = internalGetActiveEditor();
    const theme = config.theme || {};
    const parentEditor = editorConfig === undefined ? activeEditor : config.parentEditor || null;
    const disableEvents = config.disableEvents || false;
    const editorState = createEmptyEditorState();
    const namespace = config.namespace || (parentEditor !== null ? parentEditor._config.namespace : createUID());
    const initialEditorState = config.editorState;
    const nodes = [RootNode, TextNode, LineBreakNode, TabNode, ParagraphNode, ArtificialNode__DO_NOT_USE, ...(config.nodes || [])];
    const {
      onError,
      html
    } = config;
    const isEditable = config.editable !== undefined ? config.editable : true;
    let registeredNodes;
    if (editorConfig === undefined && activeEditor !== null) {
      registeredNodes = activeEditor._nodes;
    } else {
      registeredNodes = new Map();
      for (let i = 0; i < nodes.length; i++) {
        let klass = nodes[i];
        let replace = null;
        let replaceWithKlass = null;
        if (typeof klass !== 'function') {
          const options = klass;
          klass = options.replace;
          replace = options.with;
          replaceWithKlass = options.withKlass || null;
        }
        // Ensure custom nodes implement required methods and replaceWithKlass is instance of base klass.
        {
          // ArtificialNode__DO_NOT_USE can get renamed, so we use the type
          const nodeType = Object.prototype.hasOwnProperty.call(klass, 'getType') && klass.getType();
          const name = klass.name;
          if (replaceWithKlass) {
            if (!(replaceWithKlass.prototype instanceof klass)) {
              throw Error(`${replaceWithKlass.name} doesn't extend the ${name}`);
            }
          }
          if (name !== 'RootNode' && nodeType !== 'root' && nodeType !== 'artificial') {
            const proto = klass.prototype;
            ['getType', 'clone'].forEach(method => {
              // eslint-disable-next-line no-prototype-builtins
              if (!klass.hasOwnProperty(method)) {
                console.warn(`${name} must implement static "${method}" method`);
              }
            });
            if (
            // eslint-disable-next-line no-prototype-builtins
            !klass.hasOwnProperty('importDOM') &&
            // eslint-disable-next-line no-prototype-builtins
            klass.hasOwnProperty('exportDOM')) {
              console.warn(`${name} should implement "importDOM" if using a custom "exportDOM" method to ensure HTML serialization (important for copy & paste) works as expected`);
            }
            if (proto instanceof DecoratorNode) {
              // eslint-disable-next-line no-prototype-builtins
              if (!proto.hasOwnProperty('decorate')) {
                console.warn(`${proto.constructor.name} must implement "decorate" method`);
              }
            }
            if (
            // eslint-disable-next-line no-prototype-builtins
            !klass.hasOwnProperty('importJSON')) {
              console.warn(`${name} should implement "importJSON" method to ensure JSON and default HTML serialization works as expected`);
            }
            if (
            // eslint-disable-next-line no-prototype-builtins
            !proto.hasOwnProperty('exportJSON')) {
              console.warn(`${name} should implement "exportJSON" method to ensure JSON and default HTML serialization works as expected`);
            }
          }
        }
        const type = klass.getType();
        const transform = klass.transform();
        const transforms = new Set();
        if (transform !== null) {
          transforms.add(transform);
        }
        registeredNodes.set(type, {
          exportDOM: html && html.export ? html.export.get(klass) : undefined,
          klass,
          replace,
          replaceWithKlass,
          transforms
        });
      }
    }
    const editor = new LexicalEditor(editorState, parentEditor, registeredNodes, {
      disableEvents,
      namespace,
      theme
    }, onError ? onError : console.error, initializeConversionCache(registeredNodes, html ? html.import : undefined), isEditable);
    if (initialEditorState !== undefined) {
      editor._pendingEditorState = initialEditorState;
      editor._dirtyType = FULL_RECONCILE;
    }
    return editor;
  }
  class LexicalEditor {
    /** The version with build identifiers for this editor (since 0.17.1) */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */
    constructor(editorState, parentEditor, nodes, config, onError, htmlConversions, editable) {
      this._parentEditor = parentEditor;
      // The root element associated with this editor
      this._rootElement = null;
      // The current editor state
      this._editorState = editorState;
      // Handling of drafts and updates
      this._pendingEditorState = null;
      // Used to help co-ordinate selection and events
      this._compositionKey = null;
      this._deferred = [];
      // Used during reconciliation
      this._keyToDOMMap = new Map();
      this._updates = [];
      this._updating = false;
      // Listeners
      this._listeners = {
        decorator: new Set(),
        editable: new Set(),
        mutation: new Map(),
        root: new Set(),
        textcontent: new Set(),
        update: new Set()
      };
      // Commands
      this._commands = new Map();
      // Editor configuration for theme/context.
      this._config = config;
      // Mapping of types to their nodes
      this._nodes = nodes;
      // React node decorators for portals
      this._decorators = {};
      this._pendingDecorators = null;
      // Used to optimize reconciliation
      this._dirtyType = NO_DIRTY_NODES;
      this._cloneNotNeeded = new Set();
      this._dirtyLeaves = new Set();
      this._dirtyElements = new Map();
      this._normalizedNodes = new Set();
      this._updateTags = new Set();
      // Handling of DOM mutations
      this._observer = null;
      // Used for identifying owning editors
      this._key = createUID();
      this._onError = onError;
      this._htmlConversions = htmlConversions;
      this._editable = editable;
      this._headless = parentEditor !== null && parentEditor._headless;
      this._window = null;
      this._blockCursorElement = null;
    }

    /**
     *
     * @returns true if the editor is currently in "composition" mode due to receiving input
     * through an IME, or 3P extension, for example. Returns false otherwise.
     */
    isComposing() {
      return this._compositionKey != null;
    }
    /**
     * Registers a listener for Editor update event. Will trigger the provided callback
     * each time the editor goes through an update (via {@link LexicalEditor.update}) until the
     * teardown function is called.
     *
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerUpdateListener(listener) {
      const listenerSetOrMap = this._listeners.update;
      listenerSetOrMap.add(listener);
      return () => {
        listenerSetOrMap.delete(listener);
      };
    }
    /**
     * Registers a listener for for when the editor changes between editable and non-editable states.
     * Will trigger the provided callback each time the editor transitions between these states until the
     * teardown function is called.
     *
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerEditableListener(listener) {
      const listenerSetOrMap = this._listeners.editable;
      listenerSetOrMap.add(listener);
      return () => {
        listenerSetOrMap.delete(listener);
      };
    }
    /**
     * Registers a listener for when the editor's decorator object changes. The decorator object contains
     * all DecoratorNode keys -> their decorated value. This is primarily used with external UI frameworks.
     *
     * Will trigger the provided callback each time the editor transitions between these states until the
     * teardown function is called.
     *
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerDecoratorListener(listener) {
      const listenerSetOrMap = this._listeners.decorator;
      listenerSetOrMap.add(listener);
      return () => {
        listenerSetOrMap.delete(listener);
      };
    }
    /**
     * Registers a listener for when Lexical commits an update to the DOM and the text content of
     * the editor changes from the previous state of the editor. If the text content is the
     * same between updates, no notifications to the listeners will happen.
     *
     * Will trigger the provided callback each time the editor transitions between these states until the
     * teardown function is called.
     *
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerTextContentListener(listener) {
      const listenerSetOrMap = this._listeners.textcontent;
      listenerSetOrMap.add(listener);
      return () => {
        listenerSetOrMap.delete(listener);
      };
    }
    /**
     * Registers a listener for when the editor's root DOM element (the content editable
     * Lexical attaches to) changes. This is primarily used to attach event listeners to the root
     *  element. The root listener function is executed directly upon registration and then on
     * any subsequent update.
     *
     * Will trigger the provided callback each time the editor transitions between these states until the
     * teardown function is called.
     *
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerRootListener(listener) {
      const listenerSetOrMap = this._listeners.root;
      listener(this._rootElement, null);
      listenerSetOrMap.add(listener);
      return () => {
        listener(null, this._rootElement);
        listenerSetOrMap.delete(listener);
      };
    }
    /**
     * Registers a listener that will trigger anytime the provided command
     * is dispatched, subject to priority. Listeners that run at a higher priority can "intercept"
     * commands and prevent them from propagating to other handlers by returning true.
     *
     * Listeners registered at the same priority level will run deterministically in the order of registration.
     *
     * @param command - the command that will trigger the callback.
     * @param listener - the function that will execute when the command is dispatched.
     * @param priority - the relative priority of the listener. 0 | 1 | 2 | 3 | 4
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerCommand(command, listener, priority) {
      if (priority === undefined) {
        {
          throw Error(`Listener for type "command" requires a "priority".`);
        }
      }
      const commandsMap = this._commands;
      if (!commandsMap.has(command)) {
        commandsMap.set(command, [new Set(), new Set(), new Set(), new Set(), new Set()]);
      }
      const listenersInPriorityOrder = commandsMap.get(command);
      if (listenersInPriorityOrder === undefined) {
        {
          throw Error(`registerCommand: Command ${String(command)} not found in command map`);
        }
      }
      const listeners = listenersInPriorityOrder[priority];
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
        if (listenersInPriorityOrder.every(listenersSet => listenersSet.size === 0)) {
          commandsMap.delete(command);
        }
      };
    }

    /**
     * Registers a listener that will run when a Lexical node of the provided class is
     * mutated. The listener will receive a list of nodes along with the type of mutation
     * that was performed on each: created, destroyed, or updated.
     *
     * One common use case for this is to attach DOM event listeners to the underlying DOM nodes as Lexical nodes are created.
     * {@link LexicalEditor.getElementByKey} can be used for this.
     *
     * If any existing nodes are in the DOM, and skipInitialization is not true, the listener
     * will be called immediately with an updateTag of 'registerMutationListener' where all
     * nodes have the 'created' NodeMutation. This can be controlled with the skipInitialization option
     * (default is currently true for backwards compatibility in 0.16.x but will change to false in 0.17.0).
     *
     * @param klass - The class of the node that you want to listen to mutations on.
     * @param listener - The logic you want to run when the node is mutated.
     * @param options - see {@link MutationListenerOptions}
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerMutationListener(klass, listener, options) {
      const klassToMutate = this.resolveRegisteredNodeAfterReplacements(this.getRegisteredNode(klass)).klass;
      const mutations = this._listeners.mutation;
      mutations.set(listener, klassToMutate);
      const skipInitialization = options && options.skipInitialization;
      if (!(skipInitialization === undefined ? DEFAULT_SKIP_INITIALIZATION : skipInitialization)) {
        this.initializeMutationListener(listener, klassToMutate);
      }
      return () => {
        mutations.delete(listener);
      };
    }

    /** @internal */
    getRegisteredNode(klass) {
      const registeredNode = this._nodes.get(klass.getType());
      if (registeredNode === undefined) {
        {
          throw Error(`Node ${klass.name} has not been registered. Ensure node has been passed to createEditor.`);
        }
      }
      return registeredNode;
    }

    /** @internal */
    resolveRegisteredNodeAfterReplacements(registeredNode) {
      while (registeredNode.replaceWithKlass) {
        registeredNode = this.getRegisteredNode(registeredNode.replaceWithKlass);
      }
      return registeredNode;
    }

    /** @internal */
    initializeMutationListener(listener, klass) {
      const prevEditorState = this._editorState;
      const nodeMap = getCachedTypeToNodeMap(prevEditorState).get(klass.getType());
      if (!nodeMap) {
        return;
      }
      const nodeMutationMap = new Map();
      for (const k of nodeMap.keys()) {
        nodeMutationMap.set(k, 'created');
      }
      if (nodeMutationMap.size > 0) {
        listener(nodeMutationMap, {
          dirtyLeaves: new Set(),
          prevEditorState,
          updateTags: new Set(['registerMutationListener'])
        });
      }
    }

    /** @internal */
    registerNodeTransformToKlass(klass, listener) {
      const registeredNode = this.getRegisteredNode(klass);
      registeredNode.transforms.add(listener);
      return registeredNode;
    }

    /**
     * Registers a listener that will run when a Lexical node of the provided class is
     * marked dirty during an update. The listener will continue to run as long as the node
     * is marked dirty. There are no guarantees around the order of transform execution!
     *
     * Watch out for infinite loops. See [Node Transforms](https://lexical.dev/docs/concepts/transforms)
     * @param klass - The class of the node that you want to run transforms on.
     * @param listener - The logic you want to run when the node is updated.
     * @returns a teardown function that can be used to cleanup the listener.
     */
    registerNodeTransform(klass, listener) {
      const registeredNode = this.registerNodeTransformToKlass(klass, listener);
      const registeredNodes = [registeredNode];
      const replaceWithKlass = registeredNode.replaceWithKlass;
      if (replaceWithKlass != null) {
        const registeredReplaceWithNode = this.registerNodeTransformToKlass(replaceWithKlass, listener);
        registeredNodes.push(registeredReplaceWithNode);
      }
      markAllNodesAsDirty(this, klass.getType());
      return () => {
        registeredNodes.forEach(node => node.transforms.delete(listener));
      };
    }

    /**
     * Used to assert that a certain node is registered, usually by plugins to ensure nodes that they
     * depend on have been registered.
     * @returns True if the editor has registered the provided node type, false otherwise.
     */
    hasNode(node) {
      return this._nodes.has(node.getType());
    }

    /**
     * Used to assert that certain nodes are registered, usually by plugins to ensure nodes that they
     * depend on have been registered.
     * @returns True if the editor has registered all of the provided node types, false otherwise.
     */
    hasNodes(nodes) {
      return nodes.every(this.hasNode.bind(this));
    }

    /**
     * Dispatches a command of the specified type with the specified payload.
     * This triggers all command listeners (set by {@link LexicalEditor.registerCommand})
     * for this type, passing them the provided payload.
     * @param type - the type of command listeners to trigger.
     * @param payload - the data to pass as an argument to the command listeners.
     */
    dispatchCommand(type, payload) {
      return dispatchCommand(this, type, payload);
    }

    /**
     * Gets a map of all decorators in the editor.
     * @returns A mapping of call decorator keys to their decorated content
     */
    getDecorators() {
      return this._decorators;
    }

    /**
     *
     * @returns the current root element of the editor. If you want to register
     * an event listener, do it via {@link LexicalEditor.registerRootListener}, since
     * this reference may not be stable.
     */
    getRootElement() {
      return this._rootElement;
    }

    /**
     * Gets the key of the editor
     * @returns The editor key
     */
    getKey() {
      return this._key;
    }

    /**
     * Imperatively set the root contenteditable element that Lexical listens
     * for events on.
     */
    setRootElement(nextRootElement) {
      const prevRootElement = this._rootElement;
      if (nextRootElement !== prevRootElement) {
        const classNames = getCachedClassNameArray(this._config.theme, 'root');
        const pendingEditorState = this._pendingEditorState || this._editorState;
        this._rootElement = nextRootElement;
        resetEditor(this, prevRootElement, nextRootElement, pendingEditorState);
        if (prevRootElement !== null) {
          // TODO: remove this flag once we no longer use UEv2 internally
          if (!this._config.disableEvents) {
            removeRootElementEvents(prevRootElement);
          }
          if (classNames != null) {
            prevRootElement.classList.remove(...classNames);
          }
        }
        if (nextRootElement !== null) {
          const windowObj = getDefaultView(nextRootElement);
          const style = nextRootElement.style;
          style.userSelect = 'text';
          style.whiteSpace = 'pre-wrap';
          style.wordBreak = 'break-word';
          nextRootElement.setAttribute('data-lexical-editor', 'true');
          this._window = windowObj;
          this._dirtyType = FULL_RECONCILE;
          initMutationObserver(this);
          this._updateTags.add('history-merge');
          $commitPendingUpdates(this);

          // TODO: remove this flag once we no longer use UEv2 internally
          if (!this._config.disableEvents) {
            addRootElementEvents(nextRootElement, this);
          }
          if (classNames != null) {
            nextRootElement.classList.add(...classNames);
          }
        } else {
          // If content editable is unmounted we'll reset editor state back to original
          // (or pending) editor state since there will be no reconciliation
          this._editorState = pendingEditorState;
          this._pendingEditorState = null;
          this._window = null;
        }
        triggerListeners('root', this, false, nextRootElement, prevRootElement);
      }
    }

    /**
     * Gets the underlying HTMLElement associated with the LexicalNode for the given key.
     * @returns the HTMLElement rendered by the LexicalNode associated with the key.
     * @param key - the key of the LexicalNode.
     */
    getElementByKey(key) {
      return this._keyToDOMMap.get(key) || null;
    }

    /**
     * Gets the active editor state.
     * @returns The editor state
     */
    getEditorState() {
      return this._editorState;
    }

    /**
     * Imperatively set the EditorState. Triggers reconciliation like an update.
     * @param editorState - the state to set the editor
     * @param options - options for the update.
     */
    setEditorState(editorState, options) {
      if (editorState.isEmpty()) {
        {
          throw Error(`setEditorState: the editor state is empty. Ensure the editor state's root node never becomes empty.`);
        }
      }
      $flushRootMutations(this);
      const pendingEditorState = this._pendingEditorState;
      const tags = this._updateTags;
      const tag = options !== undefined ? options.tag : null;
      if (pendingEditorState !== null && !pendingEditorState.isEmpty()) {
        if (tag != null) {
          tags.add(tag);
        }
        $commitPendingUpdates(this);
      }
      this._pendingEditorState = editorState;
      this._dirtyType = FULL_RECONCILE;
      this._dirtyElements.set('root', false);
      this._compositionKey = null;
      if (tag != null) {
        tags.add(tag);
      }
      $commitPendingUpdates(this);
    }

    /**
     * Parses a SerializedEditorState (usually produced by {@link EditorState.toJSON}) and returns
     * and EditorState object that can be, for example, passed to {@link LexicalEditor.setEditorState}. Typically,
     * deserialization from JSON stored in a database uses this method.
     * @param maybeStringifiedEditorState
     * @param updateFn
     * @returns
     */
    parseEditorState(maybeStringifiedEditorState, updateFn) {
      const serializedEditorState = typeof maybeStringifiedEditorState === 'string' ? JSON.parse(maybeStringifiedEditorState) : maybeStringifiedEditorState;
      return parseEditorState(serializedEditorState, this, updateFn);
    }

    /**
     * Executes a read of the editor's state, with the
     * editor context available (useful for exporting and read-only DOM
     * operations). Much like update, but prevents any mutation of the
     * editor's state. Any pending updates will be flushed immediately before
     * the read.
     * @param callbackFn - A function that has access to read-only editor state.
     */
    read(callbackFn) {
      $commitPendingUpdates(this);
      return this.getEditorState().read(callbackFn, {
        editor: this
      });
    }

    /**
     * Executes an update to the editor state. The updateFn callback is the ONLY place
     * where Lexical editor state can be safely mutated.
     * @param updateFn - A function that has access to writable editor state.
     * @param options - A bag of options to control the behavior of the update.
     * @param options.onUpdate - A function to run once the update is complete.
     * Useful for synchronizing updates in some cases.
     * @param options.skipTransforms - Setting this to true will suppress all node
     * transforms for this update cycle.
     * @param options.tag - A tag to identify this update, in an update listener, for instance.
     * Some tags are reserved by the core and control update behavior in different ways.
     * @param options.discrete - If true, prevents this update from being batched, forcing it to
     * run synchronously.
     */
    update(updateFn, options) {
      updateEditor(this, updateFn, options);
    }

    /**
     * Focuses the editor
     * @param callbackFn - A function to run after the editor is focused.
     * @param options - A bag of options
     * @param options.defaultSelection - Where to move selection when the editor is
     * focused. Can be rootStart, rootEnd, or undefined. Defaults to rootEnd.
     */
    focus(callbackFn, options = {}) {
      const rootElement = this._rootElement;
      if (rootElement !== null) {
        // This ensures that iOS does not trigger caps lock upon focus
        rootElement.setAttribute('autocapitalize', 'off');
        updateEditor(this, () => {
          const selection = $getSelection();
          const root = $getRoot();
          if (selection !== null) {
            // Marking the selection dirty will force the selection back to it
            selection.dirty = true;
          } else if (root.getChildrenSize() !== 0) {
            if (options.defaultSelection === 'rootStart') {
              root.selectStart();
            } else {
              root.selectEnd();
            }
          }
        }, {
          onUpdate: () => {
            rootElement.removeAttribute('autocapitalize');
            if (callbackFn) {
              callbackFn();
            }
          },
          tag: 'focus'
        });
        // In the case where onUpdate doesn't fire (due to the focus update not
        // occuring).
        if (this._pendingEditorState === null) {
          rootElement.removeAttribute('autocapitalize');
        }
      }
    }

    /**
     * Removes focus from the editor.
     */
    blur() {
      const rootElement = this._rootElement;
      if (rootElement !== null) {
        rootElement.blur();
      }
      const domSelection = getDOMSelection(this._window);
      if (domSelection !== null) {
        domSelection.removeAllRanges();
      }
    }
    /**
     * Returns true if the editor is editable, false otherwise.
     * @returns True if the editor is editable, false otherwise.
     */
    isEditable() {
      return this._editable;
    }
    /**
     * Sets the editable property of the editor. When false, the
     * editor will not listen for user events on the underling contenteditable.
     * @param editable - the value to set the editable mode to.
     */
    setEditable(editable) {
      if (this._editable !== editable) {
        this._editable = editable;
        triggerListeners('editable', this, true, editable);
      }
    }
    /**
     * Returns a JSON-serializable javascript object NOT a JSON string.
     * You still must call JSON.stringify (or something else) to turn the
     * state into a string you can transfer over the wire and store in a database.
     *
     * See {@link LexicalNode.exportJSON}
     *
     * @returns A JSON-serializable javascript object
     */
    toJSON() {
      return {
        editorState: this._editorState.toJSON()
      };
    }
  }
  LexicalEditor.version = "0.17.1+dev.esm";

  var modDev = /*#__PURE__*/Object.freeze({
    $addUpdateTag: $addUpdateTag,
    $applyNodeReplacement: $applyNodeReplacement,
    $cloneWithProperties: $cloneWithProperties,
    $copyNode: $copyNode,
    $createLineBreakNode: $createLineBreakNode,
    $createNodeSelection: $createNodeSelection,
    $createParagraphNode: $createParagraphNode,
    $createPoint: $createPoint,
    $createRangeSelection: $createRangeSelection,
    $createRangeSelectionFromDom: $createRangeSelectionFromDom,
    $createTabNode: $createTabNode,
    $createTextNode: $createTextNode,
    $getAdjacentNode: $getAdjacentNode,
    $getCharacterOffsets: $getCharacterOffsets,
    $getEditor: $getEditor,
    $getNearestNodeFromDOMNode: $getNearestNodeFromDOMNode,
    $getNearestRootOrShadowRoot: $getNearestRootOrShadowRoot,
    $getNodeByKey: $getNodeByKey,
    $getNodeByKeyOrThrow: $getNodeByKeyOrThrow,
    $getPreviousSelection: $getPreviousSelection,
    $getRoot: $getRoot,
    $getSelection: $getSelection,
    $getTextContent: $getTextContent,
    $hasAncestor: $hasAncestor,
    $hasUpdateTag: $hasUpdateTag,
    $insertNodes: $insertNodes,
    $isBlockElementNode: $isBlockElementNode,
    $isDecoratorNode: $isDecoratorNode,
    $isElementNode: $isElementNode,
    $isInlineElementOrDecoratorNode: $isInlineElementOrDecoratorNode,
    $isLeafNode: $isLeafNode,
    $isLineBreakNode: $isLineBreakNode,
    $isNodeSelection: $isNodeSelection,
    $isParagraphNode: $isParagraphNode,
    $isRangeSelection: $isRangeSelection,
    $isRootNode: $isRootNode,
    $isRootOrShadowRoot: $isRootOrShadowRoot,
    $isTabNode: $isTabNode,
    $isTextNode: $isTextNode,
    $isTokenOrSegmented: $isTokenOrSegmented,
    $nodesOfType: $nodesOfType,
    $normalizeSelection__EXPERIMENTAL: $normalizeSelection,
    $parseSerializedNode: $parseSerializedNode,
    $selectAll: $selectAll,
    $setCompositionKey: $setCompositionKey,
    $setSelection: $setSelection,
    $splitNode: $splitNode,
    ArtificialNode__DO_NOT_USE: ArtificialNode__DO_NOT_USE,
    BLUR_COMMAND: BLUR_COMMAND,
    CAN_REDO_COMMAND: CAN_REDO_COMMAND,
    CAN_UNDO_COMMAND: CAN_UNDO_COMMAND,
    CLEAR_EDITOR_COMMAND: CLEAR_EDITOR_COMMAND,
    CLEAR_HISTORY_COMMAND: CLEAR_HISTORY_COMMAND,
    CLICK_COMMAND: CLICK_COMMAND,
    COMMAND_PRIORITY_CRITICAL: COMMAND_PRIORITY_CRITICAL,
    COMMAND_PRIORITY_EDITOR: COMMAND_PRIORITY_EDITOR,
    COMMAND_PRIORITY_HIGH: COMMAND_PRIORITY_HIGH,
    COMMAND_PRIORITY_LOW: COMMAND_PRIORITY_LOW,
    COMMAND_PRIORITY_NORMAL: COMMAND_PRIORITY_NORMAL,
    CONTROLLED_TEXT_INSERTION_COMMAND: CONTROLLED_TEXT_INSERTION_COMMAND,
    COPY_COMMAND: COPY_COMMAND,
    CUT_COMMAND: CUT_COMMAND,
    DELETE_CHARACTER_COMMAND: DELETE_CHARACTER_COMMAND,
    DELETE_LINE_COMMAND: DELETE_LINE_COMMAND,
    DELETE_WORD_COMMAND: DELETE_WORD_COMMAND,
    DRAGEND_COMMAND: DRAGEND_COMMAND,
    DRAGOVER_COMMAND: DRAGOVER_COMMAND,
    DRAGSTART_COMMAND: DRAGSTART_COMMAND,
    DROP_COMMAND: DROP_COMMAND,
    DecoratorNode: DecoratorNode,
    ElementNode: ElementNode,
    FOCUS_COMMAND: FOCUS_COMMAND,
    FORMAT_ELEMENT_COMMAND: FORMAT_ELEMENT_COMMAND,
    FORMAT_TEXT_COMMAND: FORMAT_TEXT_COMMAND,
    INDENT_CONTENT_COMMAND: INDENT_CONTENT_COMMAND,
    INSERT_LINE_BREAK_COMMAND: INSERT_LINE_BREAK_COMMAND,
    INSERT_PARAGRAPH_COMMAND: INSERT_PARAGRAPH_COMMAND,
    INSERT_TAB_COMMAND: INSERT_TAB_COMMAND,
    IS_ALL_FORMATTING: IS_ALL_FORMATTING,
    IS_BOLD: IS_BOLD,
    IS_CODE: IS_CODE,
    IS_HIGHLIGHT: IS_HIGHLIGHT,
    IS_ITALIC: IS_ITALIC,
    IS_STRIKETHROUGH: IS_STRIKETHROUGH,
    IS_SUBSCRIPT: IS_SUBSCRIPT,
    IS_SUPERSCRIPT: IS_SUPERSCRIPT,
    IS_UNDERLINE: IS_UNDERLINE,
    KEY_ARROW_DOWN_COMMAND: KEY_ARROW_DOWN_COMMAND,
    KEY_ARROW_LEFT_COMMAND: KEY_ARROW_LEFT_COMMAND,
    KEY_ARROW_RIGHT_COMMAND: KEY_ARROW_RIGHT_COMMAND,
    KEY_ARROW_UP_COMMAND: KEY_ARROW_UP_COMMAND,
    KEY_BACKSPACE_COMMAND: KEY_BACKSPACE_COMMAND,
    KEY_DELETE_COMMAND: KEY_DELETE_COMMAND,
    KEY_DOWN_COMMAND: KEY_DOWN_COMMAND,
    KEY_ENTER_COMMAND: KEY_ENTER_COMMAND,
    KEY_ESCAPE_COMMAND: KEY_ESCAPE_COMMAND,
    KEY_MODIFIER_COMMAND: KEY_MODIFIER_COMMAND,
    KEY_SPACE_COMMAND: KEY_SPACE_COMMAND,
    KEY_TAB_COMMAND: KEY_TAB_COMMAND,
    LineBreakNode: LineBreakNode,
    MOVE_TO_END: MOVE_TO_END,
    MOVE_TO_START: MOVE_TO_START,
    OUTDENT_CONTENT_COMMAND: OUTDENT_CONTENT_COMMAND,
    PASTE_COMMAND: PASTE_COMMAND,
    ParagraphNode: ParagraphNode,
    REDO_COMMAND: REDO_COMMAND,
    REMOVE_TEXT_COMMAND: REMOVE_TEXT_COMMAND,
    RootNode: RootNode,
    SELECTION_CHANGE_COMMAND: SELECTION_CHANGE_COMMAND,
    SELECTION_INSERT_CLIPBOARD_NODES_COMMAND: SELECTION_INSERT_CLIPBOARD_NODES_COMMAND,
    SELECT_ALL_COMMAND: SELECT_ALL_COMMAND,
    TEXT_TYPE_TO_FORMAT: TEXT_TYPE_TO_FORMAT,
    TabNode: TabNode,
    TextNode: TextNode,
    UNDO_COMMAND: UNDO_COMMAND,
    createCommand: createCommand,
    createEditor: createEditor,
    getEditorPropertyFromDOMNode: getEditorPropertyFromDOMNode,
    getNearestEditorFromDOMNode: getNearestEditorFromDOMNode,
    isBlockDomNode: isBlockDomNode,
    isCurrentlyReadOnlyMode: isCurrentlyReadOnlyMode,
    isHTMLAnchorElement: isHTMLAnchorElement,
    isHTMLElement: isHTMLElement,
    isInlineDomNode: isInlineDomNode,
    isLexicalEditor: isLexicalEditor,
    isSelectionCapturedInDecoratorInput: isSelectionCapturedInDecoratorInput,
    isSelectionWithinEditor: isSelectionWithinEditor,
    resetRandomKey: resetRandomKey
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const j = "undefined" != typeof window && void 0 !== window.document && void 0 !== window.document.createElement,
    H = j && "documentMode" in document ? document.documentMode : null,
    q = j && /Mac|iPod|iPhone|iPad/.test(navigator.platform),
    Q = j && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent),
    X = !(!j || !("InputEvent" in window) || H) && "getTargetRanges" in new window.InputEvent("input"),
    Y = j && /Version\/[\d.]+.*Safari/.test(navigator.userAgent),
    Z = j && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,
    G = j && /Android/.test(navigator.userAgent),
    tt = j && /^(?=.*Chrome).*/i.test(navigator.userAgent),
    nt = j && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !tt;
  function Bt(t) {
    return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
  }
  var Rt = Bt(function (t) {
    const e = new URLSearchParams();
    e.append("code", t);
    for (let t = 1; t < arguments.length; t++) e.append("v", arguments[t]);
    throw Error(`Minified Lexical error #${t}; visit https://lexical.dev/docs/error?${e} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });
  const hr = Object.freeze({});

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod = modDev;
  const $addUpdateTag$1 = mod.$addUpdateTag;
  const $applyNodeReplacement$1 = mod.$applyNodeReplacement;
  const $cloneWithProperties$1 = mod.$cloneWithProperties;
  const $copyNode$1 = mod.$copyNode;
  const $createLineBreakNode$1 = mod.$createLineBreakNode;
  const $createNodeSelection$1 = mod.$createNodeSelection;
  const $createParagraphNode$1 = mod.$createParagraphNode;
  const $createPoint$1 = mod.$createPoint;
  const $createRangeSelection$1 = mod.$createRangeSelection;
  const $createRangeSelectionFromDom$1 = mod.$createRangeSelectionFromDom;
  const $createTabNode$1 = mod.$createTabNode;
  const $createTextNode$1 = mod.$createTextNode;
  const $getAdjacentNode$1 = mod.$getAdjacentNode;
  const $getCharacterOffsets$1 = mod.$getCharacterOffsets;
  const $getEditor$1 = mod.$getEditor;
  const $getNearestNodeFromDOMNode$1 = mod.$getNearestNodeFromDOMNode;
  const $getNearestRootOrShadowRoot$1 = mod.$getNearestRootOrShadowRoot;
  const $getNodeByKey$1 = mod.$getNodeByKey;
  const $getNodeByKeyOrThrow$1 = mod.$getNodeByKeyOrThrow;
  const $getPreviousSelection$1 = mod.$getPreviousSelection;
  const $getRoot$1 = mod.$getRoot;
  const $getSelection$1 = mod.$getSelection;
  const $getTextContent$1 = mod.$getTextContent;
  const $hasAncestor$1 = mod.$hasAncestor;
  const $hasUpdateTag$1 = mod.$hasUpdateTag;
  const $insertNodes$1 = mod.$insertNodes;
  const $isBlockElementNode$1 = mod.$isBlockElementNode;
  const $isDecoratorNode$1 = mod.$isDecoratorNode;
  const $isElementNode$1 = mod.$isElementNode;
  const $isInlineElementOrDecoratorNode$1 = mod.$isInlineElementOrDecoratorNode;
  const $isLeafNode$1 = mod.$isLeafNode;
  const $isLineBreakNode$1 = mod.$isLineBreakNode;
  const $isNodeSelection$1 = mod.$isNodeSelection;
  const $isParagraphNode$1 = mod.$isParagraphNode;
  const $isRangeSelection$1 = mod.$isRangeSelection;
  const $isRootNode$1 = mod.$isRootNode;
  const $isRootOrShadowRoot$1 = mod.$isRootOrShadowRoot;
  const $isTabNode$1 = mod.$isTabNode;
  const $isTextNode$1 = mod.$isTextNode;
  const $isTokenOrSegmented$1 = mod.$isTokenOrSegmented;
  const $nodesOfType$1 = mod.$nodesOfType;
  const $normalizeSelection__EXPERIMENTAL = mod.$normalizeSelection__EXPERIMENTAL;
  const $parseSerializedNode$1 = mod.$parseSerializedNode;
  const $selectAll$1 = mod.$selectAll;
  const $setCompositionKey$1 = mod.$setCompositionKey;
  const $setSelection$1 = mod.$setSelection;
  const $splitNode$1 = mod.$splitNode;
  const ArtificialNode__DO_NOT_USE$1 = mod.ArtificialNode__DO_NOT_USE;
  const BLUR_COMMAND$1 = mod.BLUR_COMMAND;
  const CAN_REDO_COMMAND$1 = mod.CAN_REDO_COMMAND;
  const CAN_UNDO_COMMAND$1 = mod.CAN_UNDO_COMMAND;
  const CLEAR_EDITOR_COMMAND$1 = mod.CLEAR_EDITOR_COMMAND;
  const CLEAR_HISTORY_COMMAND$1 = mod.CLEAR_HISTORY_COMMAND;
  const CLICK_COMMAND$1 = mod.CLICK_COMMAND;
  const COMMAND_PRIORITY_CRITICAL$1 = mod.COMMAND_PRIORITY_CRITICAL;
  const COMMAND_PRIORITY_EDITOR$1 = mod.COMMAND_PRIORITY_EDITOR;
  const COMMAND_PRIORITY_HIGH$1 = mod.COMMAND_PRIORITY_HIGH;
  const COMMAND_PRIORITY_LOW$1 = mod.COMMAND_PRIORITY_LOW;
  const COMMAND_PRIORITY_NORMAL$1 = mod.COMMAND_PRIORITY_NORMAL;
  const CONTROLLED_TEXT_INSERTION_COMMAND$1 = mod.CONTROLLED_TEXT_INSERTION_COMMAND;
  const COPY_COMMAND$1 = mod.COPY_COMMAND;
  const CUT_COMMAND$1 = mod.CUT_COMMAND;
  const DELETE_CHARACTER_COMMAND$1 = mod.DELETE_CHARACTER_COMMAND;
  const DELETE_LINE_COMMAND$1 = mod.DELETE_LINE_COMMAND;
  const DELETE_WORD_COMMAND$1 = mod.DELETE_WORD_COMMAND;
  const DRAGEND_COMMAND$1 = mod.DRAGEND_COMMAND;
  const DRAGOVER_COMMAND$1 = mod.DRAGOVER_COMMAND;
  const DRAGSTART_COMMAND$1 = mod.DRAGSTART_COMMAND;
  const DROP_COMMAND$1 = mod.DROP_COMMAND;
  const DecoratorNode$1 = mod.DecoratorNode;
  const ElementNode$1 = mod.ElementNode;
  const FOCUS_COMMAND$1 = mod.FOCUS_COMMAND;
  const FORMAT_ELEMENT_COMMAND$1 = mod.FORMAT_ELEMENT_COMMAND;
  const FORMAT_TEXT_COMMAND$1 = mod.FORMAT_TEXT_COMMAND;
  const INDENT_CONTENT_COMMAND$1 = mod.INDENT_CONTENT_COMMAND;
  const INSERT_LINE_BREAK_COMMAND$1 = mod.INSERT_LINE_BREAK_COMMAND;
  const INSERT_PARAGRAPH_COMMAND$1 = mod.INSERT_PARAGRAPH_COMMAND;
  const INSERT_TAB_COMMAND$1 = mod.INSERT_TAB_COMMAND;
  const IS_ALL_FORMATTING$1 = mod.IS_ALL_FORMATTING;
  const IS_BOLD$1 = mod.IS_BOLD;
  const IS_CODE$1 = mod.IS_CODE;
  const IS_HIGHLIGHT$1 = mod.IS_HIGHLIGHT;
  const IS_ITALIC$1 = mod.IS_ITALIC;
  const IS_STRIKETHROUGH$1 = mod.IS_STRIKETHROUGH;
  const IS_SUBSCRIPT$1 = mod.IS_SUBSCRIPT;
  const IS_SUPERSCRIPT$1 = mod.IS_SUPERSCRIPT;
  const IS_UNDERLINE$1 = mod.IS_UNDERLINE;
  const KEY_ARROW_DOWN_COMMAND$1 = mod.KEY_ARROW_DOWN_COMMAND;
  const KEY_ARROW_LEFT_COMMAND$1 = mod.KEY_ARROW_LEFT_COMMAND;
  const KEY_ARROW_RIGHT_COMMAND$1 = mod.KEY_ARROW_RIGHT_COMMAND;
  const KEY_ARROW_UP_COMMAND$1 = mod.KEY_ARROW_UP_COMMAND;
  const KEY_BACKSPACE_COMMAND$1 = mod.KEY_BACKSPACE_COMMAND;
  const KEY_DELETE_COMMAND$1 = mod.KEY_DELETE_COMMAND;
  const KEY_DOWN_COMMAND$1 = mod.KEY_DOWN_COMMAND;
  const KEY_ENTER_COMMAND$1 = mod.KEY_ENTER_COMMAND;
  const KEY_ESCAPE_COMMAND$1 = mod.KEY_ESCAPE_COMMAND;
  const KEY_MODIFIER_COMMAND$1 = mod.KEY_MODIFIER_COMMAND;
  const KEY_SPACE_COMMAND$1 = mod.KEY_SPACE_COMMAND;
  const KEY_TAB_COMMAND$1 = mod.KEY_TAB_COMMAND;
  const LineBreakNode$1 = mod.LineBreakNode;
  const MOVE_TO_END$1 = mod.MOVE_TO_END;
  const MOVE_TO_START$1 = mod.MOVE_TO_START;
  const OUTDENT_CONTENT_COMMAND$1 = mod.OUTDENT_CONTENT_COMMAND;
  const PASTE_COMMAND$1 = mod.PASTE_COMMAND;
  const ParagraphNode$1 = mod.ParagraphNode;
  const REDO_COMMAND$1 = mod.REDO_COMMAND;
  const REMOVE_TEXT_COMMAND$1 = mod.REMOVE_TEXT_COMMAND;
  const RootNode$1 = mod.RootNode;
  const SELECTION_CHANGE_COMMAND$1 = mod.SELECTION_CHANGE_COMMAND;
  const SELECTION_INSERT_CLIPBOARD_NODES_COMMAND$1 = mod.SELECTION_INSERT_CLIPBOARD_NODES_COMMAND;
  const SELECT_ALL_COMMAND$1 = mod.SELECT_ALL_COMMAND;
  const TEXT_TYPE_TO_FORMAT$1 = mod.TEXT_TYPE_TO_FORMAT;
  const TabNode$1 = mod.TabNode;
  const TextNode$1 = mod.TextNode;
  const UNDO_COMMAND$1 = mod.UNDO_COMMAND;
  const createCommand$1 = mod.createCommand;
  const createEditor$1 = mod.createEditor;
  const getEditorPropertyFromDOMNode$1 = mod.getEditorPropertyFromDOMNode;
  const getNearestEditorFromDOMNode$1 = mod.getNearestEditorFromDOMNode;
  const isBlockDomNode$1 = mod.isBlockDomNode;
  const isCurrentlyReadOnlyMode$1 = mod.isCurrentlyReadOnlyMode;
  const isHTMLAnchorElement$1 = mod.isHTMLAnchorElement;
  const isHTMLElement$1 = mod.isHTMLElement;
  const isInlineDomNode$1 = mod.isInlineDomNode;
  const isLexicalEditor$1 = mod.isLexicalEditor;
  const isSelectionCapturedInDecoratorInput$1 = mod.isSelectionCapturedInDecoratorInput;
  const isSelectionWithinEditor$1 = mod.isSelectionWithinEditor;
  const resetRandomKey$1 = mod.resetRandomKey;

  var Lexical = /*#__PURE__*/Object.freeze({
    $addUpdateTag: $addUpdateTag$1,
    $applyNodeReplacement: $applyNodeReplacement$1,
    $cloneWithProperties: $cloneWithProperties$1,
    $copyNode: $copyNode$1,
    $createLineBreakNode: $createLineBreakNode$1,
    $createNodeSelection: $createNodeSelection$1,
    $createParagraphNode: $createParagraphNode$1,
    $createPoint: $createPoint$1,
    $createRangeSelection: $createRangeSelection$1,
    $createRangeSelectionFromDom: $createRangeSelectionFromDom$1,
    $createTabNode: $createTabNode$1,
    $createTextNode: $createTextNode$1,
    $getAdjacentNode: $getAdjacentNode$1,
    $getCharacterOffsets: $getCharacterOffsets$1,
    $getEditor: $getEditor$1,
    $getNearestNodeFromDOMNode: $getNearestNodeFromDOMNode$1,
    $getNearestRootOrShadowRoot: $getNearestRootOrShadowRoot$1,
    $getNodeByKey: $getNodeByKey$1,
    $getNodeByKeyOrThrow: $getNodeByKeyOrThrow$1,
    $getPreviousSelection: $getPreviousSelection$1,
    $getRoot: $getRoot$1,
    $getSelection: $getSelection$1,
    $getTextContent: $getTextContent$1,
    $hasAncestor: $hasAncestor$1,
    $hasUpdateTag: $hasUpdateTag$1,
    $insertNodes: $insertNodes$1,
    $isBlockElementNode: $isBlockElementNode$1,
    $isDecoratorNode: $isDecoratorNode$1,
    $isElementNode: $isElementNode$1,
    $isInlineElementOrDecoratorNode: $isInlineElementOrDecoratorNode$1,
    $isLeafNode: $isLeafNode$1,
    $isLineBreakNode: $isLineBreakNode$1,
    $isNodeSelection: $isNodeSelection$1,
    $isParagraphNode: $isParagraphNode$1,
    $isRangeSelection: $isRangeSelection$1,
    $isRootNode: $isRootNode$1,
    $isRootOrShadowRoot: $isRootOrShadowRoot$1,
    $isTabNode: $isTabNode$1,
    $isTextNode: $isTextNode$1,
    $isTokenOrSegmented: $isTokenOrSegmented$1,
    $nodesOfType: $nodesOfType$1,
    $normalizeSelection__EXPERIMENTAL: $normalizeSelection__EXPERIMENTAL,
    $parseSerializedNode: $parseSerializedNode$1,
    $selectAll: $selectAll$1,
    $setCompositionKey: $setCompositionKey$1,
    $setSelection: $setSelection$1,
    $splitNode: $splitNode$1,
    ArtificialNode__DO_NOT_USE: ArtificialNode__DO_NOT_USE$1,
    BLUR_COMMAND: BLUR_COMMAND$1,
    CAN_REDO_COMMAND: CAN_REDO_COMMAND$1,
    CAN_UNDO_COMMAND: CAN_UNDO_COMMAND$1,
    CLEAR_EDITOR_COMMAND: CLEAR_EDITOR_COMMAND$1,
    CLEAR_HISTORY_COMMAND: CLEAR_HISTORY_COMMAND$1,
    CLICK_COMMAND: CLICK_COMMAND$1,
    COMMAND_PRIORITY_CRITICAL: COMMAND_PRIORITY_CRITICAL$1,
    COMMAND_PRIORITY_EDITOR: COMMAND_PRIORITY_EDITOR$1,
    COMMAND_PRIORITY_HIGH: COMMAND_PRIORITY_HIGH$1,
    COMMAND_PRIORITY_LOW: COMMAND_PRIORITY_LOW$1,
    COMMAND_PRIORITY_NORMAL: COMMAND_PRIORITY_NORMAL$1,
    CONTROLLED_TEXT_INSERTION_COMMAND: CONTROLLED_TEXT_INSERTION_COMMAND$1,
    COPY_COMMAND: COPY_COMMAND$1,
    CUT_COMMAND: CUT_COMMAND$1,
    DELETE_CHARACTER_COMMAND: DELETE_CHARACTER_COMMAND$1,
    DELETE_LINE_COMMAND: DELETE_LINE_COMMAND$1,
    DELETE_WORD_COMMAND: DELETE_WORD_COMMAND$1,
    DRAGEND_COMMAND: DRAGEND_COMMAND$1,
    DRAGOVER_COMMAND: DRAGOVER_COMMAND$1,
    DRAGSTART_COMMAND: DRAGSTART_COMMAND$1,
    DROP_COMMAND: DROP_COMMAND$1,
    DecoratorNode: DecoratorNode$1,
    ElementNode: ElementNode$1,
    FOCUS_COMMAND: FOCUS_COMMAND$1,
    FORMAT_ELEMENT_COMMAND: FORMAT_ELEMENT_COMMAND$1,
    FORMAT_TEXT_COMMAND: FORMAT_TEXT_COMMAND$1,
    INDENT_CONTENT_COMMAND: INDENT_CONTENT_COMMAND$1,
    INSERT_LINE_BREAK_COMMAND: INSERT_LINE_BREAK_COMMAND$1,
    INSERT_PARAGRAPH_COMMAND: INSERT_PARAGRAPH_COMMAND$1,
    INSERT_TAB_COMMAND: INSERT_TAB_COMMAND$1,
    IS_ALL_FORMATTING: IS_ALL_FORMATTING$1,
    IS_BOLD: IS_BOLD$1,
    IS_CODE: IS_CODE$1,
    IS_HIGHLIGHT: IS_HIGHLIGHT$1,
    IS_ITALIC: IS_ITALIC$1,
    IS_STRIKETHROUGH: IS_STRIKETHROUGH$1,
    IS_SUBSCRIPT: IS_SUBSCRIPT$1,
    IS_SUPERSCRIPT: IS_SUPERSCRIPT$1,
    IS_UNDERLINE: IS_UNDERLINE$1,
    KEY_ARROW_DOWN_COMMAND: KEY_ARROW_DOWN_COMMAND$1,
    KEY_ARROW_LEFT_COMMAND: KEY_ARROW_LEFT_COMMAND$1,
    KEY_ARROW_RIGHT_COMMAND: KEY_ARROW_RIGHT_COMMAND$1,
    KEY_ARROW_UP_COMMAND: KEY_ARROW_UP_COMMAND$1,
    KEY_BACKSPACE_COMMAND: KEY_BACKSPACE_COMMAND$1,
    KEY_DELETE_COMMAND: KEY_DELETE_COMMAND$1,
    KEY_DOWN_COMMAND: KEY_DOWN_COMMAND$1,
    KEY_ENTER_COMMAND: KEY_ENTER_COMMAND$1,
    KEY_ESCAPE_COMMAND: KEY_ESCAPE_COMMAND$1,
    KEY_MODIFIER_COMMAND: KEY_MODIFIER_COMMAND$1,
    KEY_SPACE_COMMAND: KEY_SPACE_COMMAND$1,
    KEY_TAB_COMMAND: KEY_TAB_COMMAND$1,
    LineBreakNode: LineBreakNode$1,
    MOVE_TO_END: MOVE_TO_END$1,
    MOVE_TO_START: MOVE_TO_START$1,
    OUTDENT_CONTENT_COMMAND: OUTDENT_CONTENT_COMMAND$1,
    PASTE_COMMAND: PASTE_COMMAND$1,
    ParagraphNode: ParagraphNode$1,
    REDO_COMMAND: REDO_COMMAND$1,
    REMOVE_TEXT_COMMAND: REMOVE_TEXT_COMMAND$1,
    RootNode: RootNode$1,
    SELECTION_CHANGE_COMMAND: SELECTION_CHANGE_COMMAND$1,
    SELECTION_INSERT_CLIPBOARD_NODES_COMMAND: SELECTION_INSERT_CLIPBOARD_NODES_COMMAND$1,
    SELECT_ALL_COMMAND: SELECT_ALL_COMMAND$1,
    TEXT_TYPE_TO_FORMAT: TEXT_TYPE_TO_FORMAT$1,
    TabNode: TabNode$1,
    TextNode: TextNode$1,
    UNDO_COMMAND: UNDO_COMMAND$1,
    createCommand: createCommand$1,
    createEditor: createEditor$1,
    getEditorPropertyFromDOMNode: getEditorPropertyFromDOMNode$1,
    getNearestEditorFromDOMNode: getNearestEditorFromDOMNode$1,
    isBlockDomNode: isBlockDomNode$1,
    isCurrentlyReadOnlyMode: isCurrentlyReadOnlyMode$1,
    isHTMLAnchorElement: isHTMLAnchorElement$1,
    isHTMLElement: isHTMLElement$1,
    isInlineDomNode: isInlineDomNode$1,
    isLexicalEditor: isLexicalEditor$1,
    isSelectionCapturedInDecoratorInput: isSelectionCapturedInDecoratorInput$1,
    isSelectionWithinEditor: isSelectionWithinEditor$1,
    resetRandomKey: resetRandomKey$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const CSS_TO_STYLES = new Map();

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function getDOMTextNode$1(element) {
    let node = element;
    while (node != null) {
      if (node.nodeType === Node.TEXT_NODE) {
        return node;
      }
      node = node.firstChild;
    }
    return null;
  }
  function getDOMIndexWithinParent(node) {
    const parent = node.parentNode;
    if (parent == null) {
      throw new Error('Should never happen');
    }
    return [parent, Array.from(parent.childNodes).indexOf(node)];
  }

  /**
   * Creates a selection range for the DOM.
   * @param editor - The lexical editor.
   * @param anchorNode - The anchor node of a selection.
   * @param _anchorOffset - The amount of space offset from the anchor to the focus.
   * @param focusNode - The current focus.
   * @param _focusOffset - The amount of space offset from the focus to the anchor.
   * @returns The range of selection for the DOM that was created.
   */
  function createDOMRange(editor, anchorNode, _anchorOffset, focusNode, _focusOffset) {
    const anchorKey = anchorNode.getKey();
    const focusKey = focusNode.getKey();
    const range = document.createRange();
    let anchorDOM = editor.getElementByKey(anchorKey);
    let focusDOM = editor.getElementByKey(focusKey);
    let anchorOffset = _anchorOffset;
    let focusOffset = _focusOffset;
    if ($isTextNode$1(anchorNode)) {
      anchorDOM = getDOMTextNode$1(anchorDOM);
    }
    if ($isTextNode$1(focusNode)) {
      focusDOM = getDOMTextNode$1(focusDOM);
    }
    if (anchorNode === undefined || focusNode === undefined || anchorDOM === null || focusDOM === null) {
      return null;
    }
    if (anchorDOM.nodeName === 'BR') {
      [anchorDOM, anchorOffset] = getDOMIndexWithinParent(anchorDOM);
    }
    if (focusDOM.nodeName === 'BR') {
      [focusDOM, focusOffset] = getDOMIndexWithinParent(focusDOM);
    }
    const firstChild = anchorDOM.firstChild;
    if (anchorDOM === focusDOM && firstChild != null && firstChild.nodeName === 'BR' && anchorOffset === 0 && focusOffset === 0) {
      focusOffset = 1;
    }
    try {
      range.setStart(anchorDOM, anchorOffset);
      range.setEnd(focusDOM, focusOffset);
    } catch (e) {
      return null;
    }
    if (range.collapsed && (anchorOffset !== focusOffset || anchorKey !== focusKey)) {
      // Range is backwards, we need to reverse it
      range.setStart(focusDOM, focusOffset);
      range.setEnd(anchorDOM, anchorOffset);
    }
    return range;
  }

  /**
   * Creates DOMRects, generally used to help the editor find a specific location on the screen.
   * @param editor - The lexical editor
   * @param range - A fragment of a document that can contain nodes and parts of text nodes.
   * @returns The selectionRects as an array.
   */
  function createRectsFromDOMRange(editor, range) {
    const rootElement = editor.getRootElement();
    if (rootElement === null) {
      return [];
    }
    const rootRect = rootElement.getBoundingClientRect();
    const computedStyle = getComputedStyle(rootElement);
    const rootPadding = parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight);
    const selectionRects = Array.from(range.getClientRects());
    let selectionRectsLength = selectionRects.length;
    //sort rects from top left to bottom right.
    selectionRects.sort((a, b) => {
      const top = a.top - b.top;
      // Some rects match position closely, but not perfectly,
      // so we give a 3px tolerance.
      if (Math.abs(top) <= 3) {
        return a.left - b.left;
      }
      return top;
    });
    let prevRect;
    for (let i = 0; i < selectionRectsLength; i++) {
      const selectionRect = selectionRects[i];
      // Exclude rects that overlap preceding Rects in the sorted list.
      const isOverlappingRect = prevRect && prevRect.top <= selectionRect.top && prevRect.top + prevRect.height > selectionRect.top && prevRect.left + prevRect.width > selectionRect.left;
      // Exclude selections that span the entire element
      const selectionSpansElement = selectionRect.width + rootPadding === rootRect.width;
      if (isOverlappingRect || selectionSpansElement) {
        selectionRects.splice(i--, 1);
        selectionRectsLength--;
        continue;
      }
      prevRect = selectionRect;
    }
    return selectionRects;
  }

  /**
   * Creates an object containing all the styles and their values provided in the CSS string.
   * @param css - The CSS string of styles and their values.
   * @returns The styleObject containing all the styles and their values.
   */
  function getStyleObjectFromRawCSS(css) {
    const styleObject = {};
    const styles = css.split(';');
    for (const style of styles) {
      if (style !== '') {
        const [key, value] = style.split(/:([^]+)/); // split on first colon
        if (key && value) {
          styleObject[key.trim()] = value.trim();
        }
      }
    }
    return styleObject;
  }

  /**
   * Given a CSS string, returns an object from the style cache.
   * @param css - The CSS property as a string.
   * @returns The value of the given CSS property.
   */
  function getStyleObjectFromCSS(css) {
    let value = CSS_TO_STYLES.get(css);
    if (value === undefined) {
      value = getStyleObjectFromRawCSS(css);
      CSS_TO_STYLES.set(css, value);
    }
    {
      // Freeze the value in DEV to prevent accidental mutations
      Object.freeze(value);
    }
    return value;
  }

  /**
   * Gets the CSS styles from the style object.
   * @param styles - The style object containing the styles to get.
   * @returns A string containing the CSS styles and their values.
   */
  function getCSSFromStyleObject(styles) {
    let css = '';
    for (const style in styles) {
      if (style) {
        css += `${style}: ${styles[style]};`;
      }
    }
    return css;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Generally used to append text content to HTML and JSON. Grabs the text content and "slices"
   * it to be generated into the new TextNode.
   * @param selection - The selection containing the node whose TextNode is to be edited.
   * @param textNode - The TextNode to be edited.
   * @returns The updated TextNode.
   */
  function $sliceSelectedTextNodeContent(selection, textNode) {
    const anchorAndFocus = selection.getStartEndPoints();
    if (textNode.isSelected(selection) && !textNode.isSegmented() && !textNode.isToken() && anchorAndFocus !== null) {
      const [anchor, focus] = anchorAndFocus;
      const isBackward = selection.isBackward();
      const anchorNode = anchor.getNode();
      const focusNode = focus.getNode();
      const isAnchor = textNode.is(anchorNode);
      const isFocus = textNode.is(focusNode);
      if (isAnchor || isFocus) {
        const [anchorOffset, focusOffset] = $getCharacterOffsets$1(selection);
        const isSame = anchorNode.is(focusNode);
        const isFirst = textNode.is(isBackward ? focusNode : anchorNode);
        const isLast = textNode.is(isBackward ? anchorNode : focusNode);
        let startOffset = 0;
        let endOffset = undefined;
        if (isSame) {
          startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
          endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset;
        } else if (isFirst) {
          const offset = isBackward ? focusOffset : anchorOffset;
          startOffset = offset;
          endOffset = undefined;
        } else if (isLast) {
          const offset = isBackward ? anchorOffset : focusOffset;
          startOffset = 0;
          endOffset = offset;
        }
        textNode.__text = textNode.__text.slice(startOffset, endOffset);
        return textNode;
      }
    }
    return textNode;
  }

  /**
   * Determines if the current selection is at the end of the node.
   * @param point - The point of the selection to test.
   * @returns true if the provided point offset is in the last possible position, false otherwise.
   */
  function $isAtNodeEnd(point) {
    if (point.type === 'text') {
      return point.offset === point.getNode().getTextContentSize();
    }
    const node = point.getNode();
    if (!$isElementNode$1(node)) {
      throw Error(`isAtNodeEnd: node must be a TextNode or ElementNode`);
    }
    return point.offset === node.getChildrenSize();
  }

  /**
   * Trims text from a node in order to shorten it, eg. to enforce a text's max length. If it deletes text
   * that is an ancestor of the anchor then it will leave 2 indents, otherwise, if no text content exists, it deletes
   * the TextNode. It will move the focus to either the end of any left over text or beginning of a new TextNode.
   * @param editor - The lexical editor.
   * @param anchor - The anchor of the current selection, where the selection should be pointing.
   * @param delCount - The amount of characters to delete. Useful as a dynamic variable eg. textContentSize - maxLength;
   */
  function $trimTextContentFromAnchor(editor, anchor, delCount) {
    // Work from the current selection anchor point
    let currentNode = anchor.getNode();
    let remaining = delCount;
    if ($isElementNode$1(currentNode)) {
      const descendantNode = currentNode.getDescendantByIndex(anchor.offset);
      if (descendantNode !== null) {
        currentNode = descendantNode;
      }
    }
    while (remaining > 0 && currentNode !== null) {
      if ($isElementNode$1(currentNode)) {
        const lastDescendant = currentNode.getLastDescendant();
        if (lastDescendant !== null) {
          currentNode = lastDescendant;
        }
      }
      let nextNode = currentNode.getPreviousSibling();
      let additionalElementWhitespace = 0;
      if (nextNode === null) {
        let parent = currentNode.getParentOrThrow();
        let parentSibling = parent.getPreviousSibling();
        while (parentSibling === null) {
          parent = parent.getParent();
          if (parent === null) {
            nextNode = null;
            break;
          }
          parentSibling = parent.getPreviousSibling();
        }
        if (parent !== null) {
          additionalElementWhitespace = parent.isInline() ? 0 : 2;
          nextNode = parentSibling;
        }
      }
      let text = currentNode.getTextContent();
      // If the text is empty, we need to consider adding in two line breaks to match
      // the content if we were to get it from its parent.
      if (text === '' && $isElementNode$1(currentNode) && !currentNode.isInline()) {
        // TODO: should this be handled in core?
        text = '\n\n';
      }
      const currentNodeSize = text.length;
      if (!$isTextNode$1(currentNode) || remaining >= currentNodeSize) {
        const parent = currentNode.getParent();
        currentNode.remove();
        if (parent != null && parent.getChildrenSize() === 0 && !$isRootNode$1(parent)) {
          parent.remove();
        }
        remaining -= currentNodeSize + additionalElementWhitespace;
        currentNode = nextNode;
      } else {
        const key = currentNode.getKey();
        // See if we can just revert it to what was in the last editor state
        const prevTextContent = editor.getEditorState().read(() => {
          const prevNode = $getNodeByKey$1(key);
          if ($isTextNode$1(prevNode) && prevNode.isSimpleText()) {
            return prevNode.getTextContent();
          }
          return null;
        });
        const offset = currentNodeSize - remaining;
        const slicedText = text.slice(0, offset);
        if (prevTextContent !== null && prevTextContent !== text) {
          const prevSelection = $getPreviousSelection$1();
          let target = currentNode;
          if (!currentNode.isSimpleText()) {
            const textNode = $createTextNode$1(prevTextContent);
            currentNode.replace(textNode);
            target = textNode;
          } else {
            currentNode.setTextContent(prevTextContent);
          }
          if ($isRangeSelection$1(prevSelection) && prevSelection.isCollapsed()) {
            const prevOffset = prevSelection.anchor.offset;
            target.select(prevOffset, prevOffset);
          }
        } else if (currentNode.isSimpleText()) {
          // Split text
          const isSelected = anchor.key === key;
          let anchorOffset = anchor.offset;
          // Move offset to end if it's less than the remaining number, otherwise
          // we'll have a negative splitStart.
          if (anchorOffset < remaining) {
            anchorOffset = currentNodeSize;
          }
          const splitStart = isSelected ? anchorOffset - remaining : 0;
          const splitEnd = isSelected ? anchorOffset : offset;
          if (isSelected && splitStart === 0) {
            const [excessNode] = currentNode.splitText(splitStart, splitEnd);
            excessNode.remove();
          } else {
            const [, excessNode] = currentNode.splitText(splitStart, splitEnd);
            excessNode.remove();
          }
        } else {
          const textNode = $createTextNode$1(slicedText);
          currentNode.replace(textNode);
        }
        remaining = 0;
      }
    }
  }

  /**
   * Gets the TextNode's style object and adds the styles to the CSS.
   * @param node - The TextNode to add styles to.
   */
  function $addNodeStyle(node) {
    const CSSText = node.getStyle();
    const styles = getStyleObjectFromRawCSS(CSSText);
    CSS_TO_STYLES.set(CSSText, styles);
  }
  function $patchStyle(target, patch) {
    const prevStyles = getStyleObjectFromCSS('getStyle' in target ? target.getStyle() : target.style);
    const newStyles = Object.entries(patch).reduce((styles, [key, value]) => {
      if (typeof value === 'function') {
        styles[key] = value(prevStyles[key], target);
      } else if (value === null) {
        delete styles[key];
      } else {
        styles[key] = value;
      }
      return styles;
    }, {
      ...prevStyles
    } || {});
    const newCSSText = getCSSFromStyleObject(newStyles);
    target.setStyle(newCSSText);
    CSS_TO_STYLES.set(newCSSText, newStyles);
  }

  /**
   * Applies the provided styles to the TextNodes in the provided Selection.
   * Will update partially selected TextNodes by splitting the TextNode and applying
   * the styles to the appropriate one.
   * @param selection - The selected node(s) to update.
   * @param patch - The patch to apply, which can include multiple styles. \\{CSSProperty: value\\} . Can also accept a function that returns the new property value.
   */
  function $patchStyleText(selection, patch) {
    const selectedNodes = selection.getNodes();
    const selectedNodesLength = selectedNodes.length;
    const anchorAndFocus = selection.getStartEndPoints();
    if (anchorAndFocus === null) {
      return;
    }
    const [anchor, focus] = anchorAndFocus;
    const lastIndex = selectedNodesLength - 1;
    let firstNode = selectedNodes[0];
    let lastNode = selectedNodes[lastIndex];
    if (selection.isCollapsed() && $isRangeSelection$1(selection)) {
      $patchStyle(selection, patch);
      return;
    }
    const firstNodeText = firstNode.getTextContent();
    const firstNodeTextLength = firstNodeText.length;
    const focusOffset = focus.offset;
    let anchorOffset = anchor.offset;
    const isBefore = anchor.isBefore(focus);
    let startOffset = isBefore ? anchorOffset : focusOffset;
    let endOffset = isBefore ? focusOffset : anchorOffset;
    const startType = isBefore ? anchor.type : focus.type;
    const endType = isBefore ? focus.type : anchor.type;
    const endKey = isBefore ? focus.key : anchor.key;

    // This is the case where the user only selected the very end of the
    // first node so we don't want to include it in the formatting change.
    if ($isTextNode$1(firstNode) && startOffset === firstNodeTextLength) {
      const nextSibling = firstNode.getNextSibling();
      if ($isTextNode$1(nextSibling)) {
        // we basically make the second node the firstNode, changing offsets accordingly
        anchorOffset = 0;
        startOffset = 0;
        firstNode = nextSibling;
      }
    }

    // This is the case where we only selected a single node
    if (selectedNodes.length === 1) {
      if ($isTextNode$1(firstNode) && firstNode.canHaveFormat()) {
        startOffset = startType === 'element' ? 0 : anchorOffset > focusOffset ? focusOffset : anchorOffset;
        endOffset = endType === 'element' ? firstNodeTextLength : anchorOffset > focusOffset ? anchorOffset : focusOffset;

        // No actual text is selected, so do nothing.
        if (startOffset === endOffset) {
          return;
        }

        // The entire node is selected or a token/segment, so just format it
        if ($isTokenOrSegmented$1(firstNode) || startOffset === 0 && endOffset === firstNodeTextLength) {
          $patchStyle(firstNode, patch);
          firstNode.select(startOffset, endOffset);
        } else {
          // The node is partially selected, so split it into two nodes
          // and style the selected one.
          const splitNodes = firstNode.splitText(startOffset, endOffset);
          const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
          $patchStyle(replacement, patch);
          replacement.select(0, endOffset - startOffset);
        }
      } // multiple nodes selected.
    } else {
      if ($isTextNode$1(firstNode) && startOffset < firstNode.getTextContentSize() && firstNode.canHaveFormat()) {
        if (startOffset !== 0 && !$isTokenOrSegmented$1(firstNode)) {
          // the entire first node isn't selected and it isn't a token or segmented, so split it
          firstNode = firstNode.splitText(startOffset)[1];
          startOffset = 0;
          if (isBefore) {
            anchor.set(firstNode.getKey(), startOffset, 'text');
          } else {
            focus.set(firstNode.getKey(), startOffset, 'text');
          }
        }
        $patchStyle(firstNode, patch);
      }
      if ($isTextNode$1(lastNode) && lastNode.canHaveFormat()) {
        const lastNodeText = lastNode.getTextContent();
        const lastNodeTextLength = lastNodeText.length;

        // The last node might not actually be the end node
        //
        // If not, assume the last node is fully-selected unless the end offset is
        // zero.
        if (lastNode.__key !== endKey && endOffset !== 0) {
          endOffset = lastNodeTextLength;
        }

        // if the entire last node isn't selected and it isn't a token or segmented, split it
        if (endOffset !== lastNodeTextLength && !$isTokenOrSegmented$1(lastNode)) {
          [lastNode] = lastNode.splitText(endOffset);
        }
        if (endOffset !== 0 || endType === 'element') {
          $patchStyle(lastNode, patch);
        }
      }

      // style all the text nodes in between
      for (let i = 1; i < lastIndex; i++) {
        const selectedNode = selectedNodes[i];
        const selectedNodeKey = selectedNode.getKey();
        if ($isTextNode$1(selectedNode) && selectedNode.canHaveFormat() && selectedNodeKey !== firstNode.getKey() && selectedNodeKey !== lastNode.getKey() && !selectedNode.isToken()) {
          $patchStyle(selectedNode, patch);
        }
      }
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Converts all nodes in the selection that are of one block type to another.
   * @param selection - The selected blocks to be converted.
   * @param createElement - The function that creates the node. eg. $createParagraphNode.
   */
  function $setBlocksType(selection, createElement) {
    if (selection === null) {
      return;
    }
    const anchorAndFocus = selection.getStartEndPoints();
    const anchor = anchorAndFocus ? anchorAndFocus[0] : null;
    if (anchor !== null && anchor.key === 'root') {
      const element = createElement();
      const root = $getRoot$1();
      const firstChild = root.getFirstChild();
      if (firstChild) {
        firstChild.replace(element, true);
      } else {
        root.append(element);
      }
      return;
    }
    const nodes = selection.getNodes();
    const firstSelectedBlock = anchor !== null ? $getAncestor$1(anchor.getNode(), INTERNAL_$isBlock$1) : false;
    if (firstSelectedBlock && nodes.indexOf(firstSelectedBlock) === -1) {
      nodes.push(firstSelectedBlock);
    }
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if (!INTERNAL_$isBlock$1(node)) {
        continue;
      }
      if (!$isElementNode$1(node)) {
        throw Error(`Expected block node to be an ElementNode`);
      }
      const targetElement = createElement();
      targetElement.setFormat(node.getFormatType());
      targetElement.setIndent(node.getIndent());
      node.replace(targetElement, true);
    }
  }
  function isPointAttached(point) {
    return point.getNode().isAttached();
  }
  function $removeParentEmptyElements(startingNode) {
    let node = startingNode;
    while (node !== null && !$isRootOrShadowRoot$1(node)) {
      const latest = node.getLatest();
      const parentNode = node.getParent();
      if (latest.getChildrenSize() === 0) {
        node.remove(true);
      }
      node = parentNode;
    }
  }

  /**
   * @deprecated
   * Wraps all nodes in the selection into another node of the type returned by createElement.
   * @param selection - The selection of nodes to be wrapped.
   * @param createElement - A function that creates the wrapping ElementNode. eg. $createParagraphNode.
   * @param wrappingElement - An element to append the wrapped selection and its children to.
   */
  function $wrapNodes(selection, createElement, wrappingElement = null) {
    const anchorAndFocus = selection.getStartEndPoints();
    const anchor = anchorAndFocus ? anchorAndFocus[0] : null;
    const nodes = selection.getNodes();
    const nodesLength = nodes.length;
    if (anchor !== null && (nodesLength === 0 || nodesLength === 1 && anchor.type === 'element' && anchor.getNode().getChildrenSize() === 0)) {
      const target = anchor.type === 'text' ? anchor.getNode().getParentOrThrow() : anchor.getNode();
      const children = target.getChildren();
      let element = createElement();
      element.setFormat(target.getFormatType());
      element.setIndent(target.getIndent());
      children.forEach(child => element.append(child));
      if (wrappingElement) {
        element = wrappingElement.append(element);
      }
      target.replace(element);
      return;
    }
    let topLevelNode = null;
    let descendants = [];
    for (let i = 0; i < nodesLength; i++) {
      const node = nodes[i];
      // Determine whether wrapping has to be broken down into multiple chunks. This can happen if the
      // user selected multiple Root-like nodes that have to be treated separately as if they are
      // their own branch. I.e. you don't want to wrap a whole table, but rather the contents of each
      // of each of the cell nodes.
      if ($isRootOrShadowRoot$1(node)) {
        $wrapNodesImpl(selection, descendants, descendants.length, createElement, wrappingElement);
        descendants = [];
        topLevelNode = node;
      } else if (topLevelNode === null || topLevelNode !== null && $hasAncestor$1(node, topLevelNode)) {
        descendants.push(node);
      } else {
        $wrapNodesImpl(selection, descendants, descendants.length, createElement, wrappingElement);
        descendants = [node];
      }
    }
    $wrapNodesImpl(selection, descendants, descendants.length, createElement, wrappingElement);
  }

  /**
   * Wraps each node into a new ElementNode.
   * @param selection - The selection of nodes to wrap.
   * @param nodes - An array of nodes, generally the descendants of the selection.
   * @param nodesLength - The length of nodes.
   * @param createElement - A function that creates the wrapping ElementNode. eg. $createParagraphNode.
   * @param wrappingElement - An element to wrap all the nodes into.
   * @returns
   */
  function $wrapNodesImpl(selection, nodes, nodesLength, createElement, wrappingElement = null) {
    if (nodes.length === 0) {
      return;
    }
    const firstNode = nodes[0];
    const elementMapping = new Map();
    const elements = [];
    // The below logic is to find the right target for us to
    // either insertAfter/insertBefore/append the corresponding
    // elements to. This is made more complicated due to nested
    // structures.
    let target = $isElementNode$1(firstNode) ? firstNode : firstNode.getParentOrThrow();
    if (target.isInline()) {
      target = target.getParentOrThrow();
    }
    let targetIsPrevSibling = false;
    while (target !== null) {
      const prevSibling = target.getPreviousSibling();
      if (prevSibling !== null) {
        target = prevSibling;
        targetIsPrevSibling = true;
        break;
      }
      target = target.getParentOrThrow();
      if ($isRootOrShadowRoot$1(target)) {
        break;
      }
    }
    const emptyElements = new Set();

    // Find any top level empty elements
    for (let i = 0; i < nodesLength; i++) {
      const node = nodes[i];
      if ($isElementNode$1(node) && node.getChildrenSize() === 0) {
        emptyElements.add(node.getKey());
      }
    }
    const movedNodes = new Set();

    // Move out all leaf nodes into our elements array.
    // If we find a top level empty element, also move make
    // an element for that.
    for (let i = 0; i < nodesLength; i++) {
      const node = nodes[i];
      let parent = node.getParent();
      if (parent !== null && parent.isInline()) {
        parent = parent.getParent();
      }
      if (parent !== null && $isLeafNode$1(node) && !movedNodes.has(node.getKey())) {
        const parentKey = parent.getKey();
        if (elementMapping.get(parentKey) === undefined) {
          const targetElement = createElement();
          targetElement.setFormat(parent.getFormatType());
          targetElement.setIndent(parent.getIndent());
          elements.push(targetElement);
          elementMapping.set(parentKey, targetElement);
          // Move node and its siblings to the new
          // element.
          parent.getChildren().forEach(child => {
            targetElement.append(child);
            movedNodes.add(child.getKey());
            if ($isElementNode$1(child)) {
              // Skip nested leaf nodes if the parent has already been moved
              child.getChildrenKeys().forEach(key => movedNodes.add(key));
            }
          });
          $removeParentEmptyElements(parent);
        }
      } else if (emptyElements.has(node.getKey())) {
        if (!$isElementNode$1(node)) {
          throw Error(`Expected node in emptyElements to be an ElementNode`);
        }
        const targetElement = createElement();
        targetElement.setFormat(node.getFormatType());
        targetElement.setIndent(node.getIndent());
        elements.push(targetElement);
        node.remove(true);
      }
    }
    if (wrappingElement !== null) {
      for (let i = 0; i < elements.length; i++) {
        const element = elements[i];
        wrappingElement.append(element);
      }
    }
    let lastElement = null;

    // If our target is Root-like, let's see if we can re-adjust
    // so that the target is the first child instead.
    if ($isRootOrShadowRoot$1(target)) {
      if (targetIsPrevSibling) {
        if (wrappingElement !== null) {
          target.insertAfter(wrappingElement);
        } else {
          for (let i = elements.length - 1; i >= 0; i--) {
            const element = elements[i];
            target.insertAfter(element);
          }
        }
      } else {
        const firstChild = target.getFirstChild();
        if ($isElementNode$1(firstChild)) {
          target = firstChild;
        }
        if (firstChild === null) {
          if (wrappingElement) {
            target.append(wrappingElement);
          } else {
            for (let i = 0; i < elements.length; i++) {
              const element = elements[i];
              target.append(element);
              lastElement = element;
            }
          }
        } else {
          if (wrappingElement !== null) {
            firstChild.insertBefore(wrappingElement);
          } else {
            for (let i = 0; i < elements.length; i++) {
              const element = elements[i];
              firstChild.insertBefore(element);
              lastElement = element;
            }
          }
        }
      }
    } else {
      if (wrappingElement) {
        target.insertAfter(wrappingElement);
      } else {
        for (let i = elements.length - 1; i >= 0; i--) {
          const element = elements[i];
          target.insertAfter(element);
          lastElement = element;
        }
      }
    }
    const prevSelection = $getPreviousSelection$1();
    if ($isRangeSelection$1(prevSelection) && isPointAttached(prevSelection.anchor) && isPointAttached(prevSelection.focus)) {
      $setSelection$1(prevSelection.clone());
    } else if (lastElement !== null) {
      lastElement.selectEnd();
    } else {
      selection.dirty = true;
    }
  }

  /**
   * Determines if the default character selection should be overridden. Used with DecoratorNodes
   * @param selection - The selection whose default character selection may need to be overridden.
   * @param isBackward - Is the selection backwards (the focus comes before the anchor)?
   * @returns true if it should be overridden, false if not.
   */
  function $shouldOverrideDefaultCharacterSelection(selection, isBackward) {
    const possibleNode = $getAdjacentNode$1(selection.focus, isBackward);
    return $isDecoratorNode$1(possibleNode) && !possibleNode.isIsolated() || $isElementNode$1(possibleNode) && !possibleNode.isInline() && !possibleNode.canBeEmpty();
  }

  /**
   * Moves the selection according to the arguments.
   * @param selection - The selected text or nodes.
   * @param isHoldingShift - Is the shift key being held down during the operation.
   * @param isBackward - Is the selection selected backwards (the focus comes before the anchor)?
   * @param granularity - The distance to adjust the current selection.
   */
  function $moveCaretSelection(selection, isHoldingShift, isBackward, granularity) {
    selection.modify(isHoldingShift ? 'extend' : 'move', isBackward, granularity);
  }

  /**
   * Tests a parent element for right to left direction.
   * @param selection - The selection whose parent is to be tested.
   * @returns true if the selections' parent element has a direction of 'rtl' (right to left), false otherwise.
   */
  function $isParentElementRTL(selection) {
    const anchorNode = selection.anchor.getNode();
    const parent = $isRootNode$1(anchorNode) ? anchorNode : anchorNode.getParentOrThrow();
    return parent.getDirection() === 'rtl';
  }

  /**
   * Moves selection by character according to arguments.
   * @param selection - The selection of the characters to move.
   * @param isHoldingShift - Is the shift key being held down during the operation.
   * @param isBackward - Is the selection backward (the focus comes before the anchor)?
   */
  function $moveCharacter(selection, isHoldingShift, isBackward) {
    const isRTL = $isParentElementRTL(selection);
    $moveCaretSelection(selection, isHoldingShift, isBackward ? !isRTL : isRTL, 'character');
  }

  /**
   * Expands the current Selection to cover all of the content in the editor.
   * @param selection - The current selection.
   */
  function $selectAll$2(selection) {
    const anchor = selection.anchor;
    const focus = selection.focus;
    const anchorNode = anchor.getNode();
    const topParent = anchorNode.getTopLevelElementOrThrow();
    const root = topParent.getParentOrThrow();
    let firstNode = root.getFirstDescendant();
    let lastNode = root.getLastDescendant();
    let firstType = 'element';
    let lastType = 'element';
    let lastOffset = 0;
    if ($isTextNode$1(firstNode)) {
      firstType = 'text';
    } else if (!$isElementNode$1(firstNode) && firstNode !== null) {
      firstNode = firstNode.getParentOrThrow();
    }
    if ($isTextNode$1(lastNode)) {
      lastType = 'text';
      lastOffset = lastNode.getTextContentSize();
    } else if (!$isElementNode$1(lastNode) && lastNode !== null) {
      lastNode = lastNode.getParentOrThrow();
    }
    if (firstNode && lastNode) {
      anchor.set(firstNode.getKey(), 0, firstType);
      focus.set(lastNode.getKey(), lastOffset, lastType);
    }
  }

  /**
   * Returns the current value of a CSS property for Nodes, if set. If not set, it returns the defaultValue.
   * @param node - The node whose style value to get.
   * @param styleProperty - The CSS style property.
   * @param defaultValue - The default value for the property.
   * @returns The value of the property for node.
   */
  function $getNodeStyleValueForProperty(node, styleProperty, defaultValue) {
    const css = node.getStyle();
    const styleObject = getStyleObjectFromCSS(css);
    if (styleObject !== null) {
      return styleObject[styleProperty] || defaultValue;
    }
    return defaultValue;
  }

  /**
   * Returns the current value of a CSS property for TextNodes in the Selection, if set. If not set, it returns the defaultValue.
   * If all TextNodes do not have the same value, it returns an empty string.
   * @param selection - The selection of TextNodes whose value to find.
   * @param styleProperty - The CSS style property.
   * @param defaultValue - The default value for the property, defaults to an empty string.
   * @returns The value of the property for the selected TextNodes.
   */
  function $getSelectionStyleValueForProperty(selection, styleProperty, defaultValue = '') {
    let styleValue = null;
    const nodes = selection.getNodes();
    const anchor = selection.anchor;
    const focus = selection.focus;
    const isBackward = selection.isBackward();
    const endOffset = isBackward ? focus.offset : anchor.offset;
    const endNode = isBackward ? focus.getNode() : anchor.getNode();
    if ($isRangeSelection$1(selection) && selection.isCollapsed() && selection.style !== '') {
      const css = selection.style;
      const styleObject = getStyleObjectFromCSS(css);
      if (styleObject !== null && styleProperty in styleObject) {
        return styleObject[styleProperty];
      }
    }
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];

      // if no actual characters in the end node are selected, we don't
      // include it in the selection for purposes of determining style
      // value
      if (i !== 0 && endOffset === 0 && node.is(endNode)) {
        continue;
      }
      if ($isTextNode$1(node)) {
        const nodeStyleValue = $getNodeStyleValueForProperty(node, styleProperty, defaultValue);
        if (styleValue === null) {
          styleValue = nodeStyleValue;
        } else if (styleValue !== nodeStyleValue) {
          // multiple text nodes are in the selection and they don't all
          // have the same style.
          styleValue = '';
          break;
        }
      }
    }
    return styleValue === null ? defaultValue : styleValue;
  }

  /**
   * This function is for internal use of the library.
   * Please do not use it as it may change in the future.
   */
  function INTERNAL_$isBlock$1(node) {
    if ($isDecoratorNode$1(node)) {
      return false;
    }
    if (!$isElementNode$1(node) || $isRootOrShadowRoot$1(node)) {
      return false;
    }
    const firstChild = node.getFirstChild();
    const isLeafElement = firstChild === null || $isLineBreakNode$1(firstChild) || $isTextNode$1(firstChild) || firstChild.isInline();
    return !node.isInline() && node.canBeEmpty() !== false && isLeafElement;
  }
  function $getAncestor$1(node, predicate) {
    let parent = node;
    while (parent !== null && parent.getParent() !== null && !predicate(parent)) {
      parent = parent.getParentOrThrow();
    }
    return predicate(parent) ? parent : null;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @deprecated renamed to {@link $trimTextContentFromAnchor} by @lexical/eslint-plugin rules-of-lexical */
  const trimTextContentFromAnchor = $trimTextContentFromAnchor;

  var modDev$1 = /*#__PURE__*/Object.freeze({
    $addNodeStyle: $addNodeStyle,
    $getSelectionStyleValueForProperty: $getSelectionStyleValueForProperty,
    $isAtNodeEnd: $isAtNodeEnd,
    $isParentElementRTL: $isParentElementRTL,
    $moveCaretSelection: $moveCaretSelection,
    $moveCharacter: $moveCharacter,
    $patchStyleText: $patchStyleText,
    $selectAll: $selectAll$2,
    $setBlocksType: $setBlocksType,
    $shouldOverrideDefaultCharacterSelection: $shouldOverrideDefaultCharacterSelection,
    $sliceSelectedTextNodeContent: $sliceSelectedTextNodeContent,
    $trimTextContentFromAnchor: $trimTextContentFromAnchor,
    $wrapNodes: $wrapNodes,
    createDOMRange: createDOMRange,
    createRectsFromDOMRange: createRectsFromDOMRange,
    getStyleObjectFromCSS: getStyleObjectFromCSS,
    trimTextContentFromAnchor: trimTextContentFromAnchor,
    $cloneWithProperties: $cloneWithProperties$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  function m$1(e) {
    return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
  }
  var T$1 = m$1(function (e) {
    const t = new URLSearchParams();
    t.append("code", e);
    for (let e = 1; e < arguments.length; e++) t.append("v", arguments[e]);
    throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$1 = modDev$1;
  const $addNodeStyle$1 = mod$1.$addNodeStyle;
  const $cloneWithProperties$2 = mod$1.$cloneWithProperties;
  const $getSelectionStyleValueForProperty$1 = mod$1.$getSelectionStyleValueForProperty;
  const $isAtNodeEnd$1 = mod$1.$isAtNodeEnd;
  const $isParentElementRTL$1 = mod$1.$isParentElementRTL;
  const $moveCaretSelection$1 = mod$1.$moveCaretSelection;
  const $moveCharacter$1 = mod$1.$moveCharacter;
  const $patchStyleText$1 = mod$1.$patchStyleText;
  const $selectAll$3 = mod$1.$selectAll;
  const $setBlocksType$1 = mod$1.$setBlocksType;
  const $shouldOverrideDefaultCharacterSelection$1 = mod$1.$shouldOverrideDefaultCharacterSelection;
  const $sliceSelectedTextNodeContent$1 = mod$1.$sliceSelectedTextNodeContent;
  const $trimTextContentFromAnchor$1 = mod$1.$trimTextContentFromAnchor;
  const $wrapNodes$1 = mod$1.$wrapNodes;
  const createDOMRange$1 = mod$1.createDOMRange;
  const createRectsFromDOMRange$1 = mod$1.createRectsFromDOMRange;
  const getStyleObjectFromCSS$1 = mod$1.getStyleObjectFromCSS;
  const trimTextContentFromAnchor$1 = mod$1.trimTextContentFromAnchor;

  var LexicalSelection = /*#__PURE__*/Object.freeze({
    $addNodeStyle: $addNodeStyle$1,
    $cloneWithProperties: $cloneWithProperties$2,
    $getSelectionStyleValueForProperty: $getSelectionStyleValueForProperty$1,
    $isAtNodeEnd: $isAtNodeEnd$1,
    $isParentElementRTL: $isParentElementRTL$1,
    $moveCaretSelection: $moveCaretSelection$1,
    $moveCharacter: $moveCharacter$1,
    $patchStyleText: $patchStyleText$1,
    $selectAll: $selectAll$3,
    $setBlocksType: $setBlocksType$1,
    $shouldOverrideDefaultCharacterSelection: $shouldOverrideDefaultCharacterSelection$1,
    $sliceSelectedTextNodeContent: $sliceSelectedTextNodeContent$1,
    $trimTextContentFromAnchor: $trimTextContentFromAnchor$1,
    $wrapNodes: $wrapNodes$1,
    createDOMRange: createDOMRange$1,
    createRectsFromDOMRange: createRectsFromDOMRange$1,
    getStyleObjectFromCSS: getStyleObjectFromCSS$1,
    trimTextContentFromAnchor: trimTextContentFromAnchor$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const CAN_USE_DOM$1 = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const documentMode$1 = CAN_USE_DOM$1 && 'documentMode' in document ? document.documentMode : null;
  const IS_APPLE$1 = CAN_USE_DOM$1 && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
  const IS_FIREFOX$1 = CAN_USE_DOM$1 && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
  const CAN_USE_BEFORE_INPUT$1 = CAN_USE_DOM$1 && 'InputEvent' in window && !documentMode$1 ? 'getTargetRanges' in new window.InputEvent('input') : false;
  const IS_SAFARI$1 = CAN_USE_DOM$1 && /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
  const IS_IOS$1 = CAN_USE_DOM$1 && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
  const IS_ANDROID$1 = CAN_USE_DOM$1 && /Android/.test(navigator.userAgent);

  // Keep these in case we need to use them in the future.
  // export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
  const IS_CHROME$1 = CAN_USE_DOM$1 && /^(?=.*Chrome).*/i.test(navigator.userAgent);
  // export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;

  const IS_ANDROID_CHROME$1 = CAN_USE_DOM$1 && IS_ANDROID$1 && IS_CHROME$1;
  const IS_APPLE_WEBKIT$1 = CAN_USE_DOM$1 && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !IS_CHROME$1;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function normalizeClassNames$1(...classNames) {
    const rval = [];
    for (const className of classNames) {
      if (className && typeof className === 'string') {
        for (const [s] of className.matchAll(/\S+/g)) {
          rval.push(s);
        }
      }
    }
    return rval;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Returns a function that will execute all functions passed when called. It is generally used
   * to register multiple lexical listeners and then tear them down with a single function call, such
   * as React's useEffect hook.
   * @example
   * ```ts
   * useEffect(() => {
   *   return mergeRegister(
   *     editor.registerCommand(...registerCommand1 logic),
   *     editor.registerCommand(...registerCommand2 logic),
   *     editor.registerCommand(...registerCommand3 logic)
   *   )
   * }, [editor])
   * ```
   * In this case, useEffect is returning the function returned by mergeRegister as a cleanup
   * function to be executed after either the useEffect runs again (due to one of its dependencies
   * updating) or the component it resides in unmounts.
   * Note the functions don't neccesarily need to be in an array as all arguments
   * are considered to be the func argument and spread from there.
   * The order of cleanup is the reverse of the argument order. Generally it is
   * expected that the first "acquire" will be "released" last (LIFO order),
   * because a later step may have some dependency on an earlier one.
   * @param func - An array of cleanup functions meant to be executed by the returned function.
   * @returns the function which executes all the passed cleanup functions.
   */
  function mergeRegister(...func) {
    return () => {
      for (let i = func.length - 1; i >= 0; i--) {
        func[i]();
      }
      // Clean up the references and make future calls a no-op
      func.length = 0;
    };
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function px(value) {
    return `${value}px`;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const mutationObserverConfig = {
    attributes: true,
    characterData: true,
    childList: true,
    subtree: true
  };
  function positionNodeOnRange(editor, range, onReposition) {
    let rootDOMNode = null;
    let parentDOMNode = null;
    let observer = null;
    let lastNodes = [];
    const wrapperNode = document.createElement('div');
    function position() {
      if (!(rootDOMNode !== null)) {
        throw Error(`Unexpected null rootDOMNode`);
      }
      if (!(parentDOMNode !== null)) {
        throw Error(`Unexpected null parentDOMNode`);
      }
      const {
        left: rootLeft,
        top: rootTop
      } = rootDOMNode.getBoundingClientRect();
      const parentDOMNode_ = parentDOMNode;
      const rects = createRectsFromDOMRange$1(editor, range);
      if (!wrapperNode.isConnected) {
        parentDOMNode_.append(wrapperNode);
      }
      let hasRepositioned = false;
      for (let i = 0; i < rects.length; i++) {
        const rect = rects[i];
        // Try to reuse the previously created Node when possible, no need to
        // remove/create on the most common case reposition case
        const rectNode = lastNodes[i] || document.createElement('div');
        const rectNodeStyle = rectNode.style;
        if (rectNodeStyle.position !== 'absolute') {
          rectNodeStyle.position = 'absolute';
          hasRepositioned = true;
        }
        const left = px(rect.left - rootLeft);
        if (rectNodeStyle.left !== left) {
          rectNodeStyle.left = left;
          hasRepositioned = true;
        }
        const top = px(rect.top - rootTop);
        if (rectNodeStyle.top !== top) {
          rectNode.style.top = top;
          hasRepositioned = true;
        }
        const width = px(rect.width);
        if (rectNodeStyle.width !== width) {
          rectNode.style.width = width;
          hasRepositioned = true;
        }
        const height = px(rect.height);
        if (rectNodeStyle.height !== height) {
          rectNode.style.height = height;
          hasRepositioned = true;
        }
        if (rectNode.parentNode !== wrapperNode) {
          wrapperNode.append(rectNode);
          hasRepositioned = true;
        }
        lastNodes[i] = rectNode;
      }
      while (lastNodes.length > rects.length) {
        lastNodes.pop();
      }
      if (hasRepositioned) {
        onReposition(lastNodes);
      }
    }
    function stop() {
      parentDOMNode = null;
      rootDOMNode = null;
      if (observer !== null) {
        observer.disconnect();
      }
      observer = null;
      wrapperNode.remove();
      for (const node of lastNodes) {
        node.remove();
      }
      lastNodes = [];
    }
    function restart() {
      const currentRootDOMNode = editor.getRootElement();
      if (currentRootDOMNode === null) {
        return stop();
      }
      const currentParentDOMNode = currentRootDOMNode.parentElement;
      if (!(currentParentDOMNode instanceof HTMLElement)) {
        return stop();
      }
      stop();
      rootDOMNode = currentRootDOMNode;
      parentDOMNode = currentParentDOMNode;
      observer = new MutationObserver(mutations => {
        const nextRootDOMNode = editor.getRootElement();
        const nextParentDOMNode = nextRootDOMNode && nextRootDOMNode.parentElement;
        if (nextRootDOMNode !== rootDOMNode || nextParentDOMNode !== parentDOMNode) {
          return restart();
        }
        for (const mutation of mutations) {
          if (!wrapperNode.contains(mutation.target)) {
            // TODO throttle
            return position();
          }
        }
      });
      observer.observe(currentParentDOMNode, mutationObserverConfig);
      position();
    }
    const removeRootListener = editor.registerRootListener(restart);
    return () => {
      removeRootListener();
      stop();
    };
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function markSelection(editor, onReposition) {
    let previousAnchorNode = null;
    let previousAnchorOffset = null;
    let previousFocusNode = null;
    let previousFocusOffset = null;
    let removeRangeListener = () => {};
    function compute(editorState) {
      editorState.read(() => {
        const selection = $getSelection$1();
        if (!$isRangeSelection$1(selection)) {
          // TODO
          previousAnchorNode = null;
          previousAnchorOffset = null;
          previousFocusNode = null;
          previousFocusOffset = null;
          removeRangeListener();
          removeRangeListener = () => {};
          return;
        }
        const {
          anchor,
          focus
        } = selection;
        const currentAnchorNode = anchor.getNode();
        const currentAnchorNodeKey = currentAnchorNode.getKey();
        const currentAnchorOffset = anchor.offset;
        const currentFocusNode = focus.getNode();
        const currentFocusNodeKey = currentFocusNode.getKey();
        const currentFocusOffset = focus.offset;
        const currentAnchorNodeDOM = editor.getElementByKey(currentAnchorNodeKey);
        const currentFocusNodeDOM = editor.getElementByKey(currentFocusNodeKey);
        const differentAnchorDOM = previousAnchorNode === null || currentAnchorNodeDOM === null || currentAnchorOffset !== previousAnchorOffset || currentAnchorNodeKey !== previousAnchorNode.getKey() || currentAnchorNode !== previousAnchorNode && (!(previousAnchorNode instanceof TextNode$1) || currentAnchorNode.updateDOM(previousAnchorNode, currentAnchorNodeDOM, editor._config));
        const differentFocusDOM = previousFocusNode === null || currentFocusNodeDOM === null || currentFocusOffset !== previousFocusOffset || currentFocusNodeKey !== previousFocusNode.getKey() || currentFocusNode !== previousFocusNode && (!(previousFocusNode instanceof TextNode$1) || currentFocusNode.updateDOM(previousFocusNode, currentFocusNodeDOM, editor._config));
        if (differentAnchorDOM || differentFocusDOM) {
          const anchorHTMLElement = editor.getElementByKey(anchor.getNode().getKey());
          const focusHTMLElement = editor.getElementByKey(focus.getNode().getKey());
          // TODO handle selection beyond the common TextNode
          if (anchorHTMLElement !== null && focusHTMLElement !== null && anchorHTMLElement.tagName === 'SPAN' && focusHTMLElement.tagName === 'SPAN') {
            const range = document.createRange();
            let firstHTMLElement;
            let firstOffset;
            let lastHTMLElement;
            let lastOffset;
            if (focus.isBefore(anchor)) {
              firstHTMLElement = focusHTMLElement;
              firstOffset = focus.offset;
              lastHTMLElement = anchorHTMLElement;
              lastOffset = anchor.offset;
            } else {
              firstHTMLElement = anchorHTMLElement;
              firstOffset = anchor.offset;
              lastHTMLElement = focusHTMLElement;
              lastOffset = focus.offset;
            }
            const firstTextNode = firstHTMLElement.firstChild;
            if (!(firstTextNode !== null)) {
              throw Error(`Expected text node to be first child of span`);
            }
            const lastTextNode = lastHTMLElement.firstChild;
            if (!(lastTextNode !== null)) {
              throw Error(`Expected text node to be first child of span`);
            }
            range.setStart(firstTextNode, firstOffset);
            range.setEnd(lastTextNode, lastOffset);
            removeRangeListener();
            removeRangeListener = positionNodeOnRange(editor, range, domNodes => {
              for (const domNode of domNodes) {
                const domNodeStyle = domNode.style;
                if (domNodeStyle.background !== 'Highlight') {
                  domNodeStyle.background = 'Highlight';
                }
                if (domNodeStyle.color !== 'HighlightText') {
                  domNodeStyle.color = 'HighlightText';
                }
                if (domNodeStyle.zIndex !== '-1') {
                  domNodeStyle.zIndex = '-1';
                }
                if (domNodeStyle.pointerEvents !== 'none') {
                  domNodeStyle.pointerEvents = 'none';
                }
                if (domNodeStyle.marginTop !== px(-1.5)) {
                  domNodeStyle.marginTop = px(-1.5);
                }
                if (domNodeStyle.paddingTop !== px(4)) {
                  domNodeStyle.paddingTop = px(4);
                }
                if (domNodeStyle.paddingBottom !== px(0)) {
                  domNodeStyle.paddingBottom = px(0);
                }
              }
              if (onReposition !== undefined) {
                onReposition(domNodes);
              }
            });
          }
        }
        previousAnchorNode = currentAnchorNode;
        previousAnchorOffset = currentAnchorOffset;
        previousFocusNode = currentFocusNode;
        previousFocusOffset = currentFocusOffset;
      });
    }
    compute(editor.getEditorState());
    return mergeRegister(editor.registerUpdateListener(({
      editorState
    }) => compute(editorState)), removeRangeListener, () => {
      removeRangeListener();
    });
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  // Hotfix to export these with inlined types #5918
  const CAN_USE_BEFORE_INPUT$2 = CAN_USE_BEFORE_INPUT$1;
  const CAN_USE_DOM$2 = CAN_USE_DOM$1;
  const IS_ANDROID$2 = IS_ANDROID$1;
  const IS_ANDROID_CHROME$2 = IS_ANDROID_CHROME$1;
  const IS_APPLE$2 = IS_APPLE$1;
  const IS_APPLE_WEBKIT$2 = IS_APPLE_WEBKIT$1;
  const IS_CHROME$2 = IS_CHROME$1;
  const IS_FIREFOX$2 = IS_FIREFOX$1;
  const IS_IOS$2 = IS_IOS$1;
  const IS_SAFARI$2 = IS_SAFARI$1;
  /**
   * Takes an HTML element and adds the classNames passed within an array,
   * ignoring any non-string types. A space can be used to add multiple classes
   * eg. addClassNamesToElement(element, ['element-inner active', true, null])
   * will add both 'element-inner' and 'active' as classes to that element.
   * @param element - The element in which the classes are added
   * @param classNames - An array defining the class names to add to the element
   */
  function addClassNamesToElement(element, ...classNames) {
    const classesToAdd = normalizeClassNames$1(...classNames);
    if (classesToAdd.length > 0) {
      element.classList.add(...classesToAdd);
    }
  }

  /**
   * Takes an HTML element and removes the classNames passed within an array,
   * ignoring any non-string types. A space can be used to remove multiple classes
   * eg. removeClassNamesFromElement(element, ['active small', true, null])
   * will remove both the 'active' and 'small' classes from that element.
   * @param element - The element in which the classes are removed
   * @param classNames - An array defining the class names to remove from the element
   */
  function removeClassNamesFromElement(element, ...classNames) {
    const classesToRemove = normalizeClassNames$1(...classNames);
    if (classesToRemove.length > 0) {
      element.classList.remove(...classesToRemove);
    }
  }

  /**
   * Returns true if the file type matches the types passed within the acceptableMimeTypes array, false otherwise.
   * The types passed must be strings and are CASE-SENSITIVE.
   * eg. if file is of type 'text' and acceptableMimeTypes = ['TEXT', 'IMAGE'] the function will return false.
   * @param file - The file you want to type check.
   * @param acceptableMimeTypes - An array of strings of types which the file is checked against.
   * @returns true if the file is an acceptable mime type, false otherwise.
   */
  function isMimeType(file, acceptableMimeTypes) {
    for (const acceptableType of acceptableMimeTypes) {
      if (file.type.startsWith(acceptableType)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Lexical File Reader with:
   *  1. MIME type support
   *  2. batched results (HistoryPlugin compatibility)
   *  3. Order aware (respects the order when multiple Files are passed)
   *
   * const filesResult = await mediaFileReader(files, ['image/']);
   * filesResult.forEach(file => editor.dispatchCommand('INSERT_IMAGE', \\{
   *   src: file.result,
   * \\}));
   */
  function mediaFileReader(files, acceptableMimeTypes) {
    const filesIterator = files[Symbol.iterator]();
    return new Promise((resolve, reject) => {
      const processed = [];
      const handleNextFile = () => {
        const {
          done,
          value: file
        } = filesIterator.next();
        if (done) {
          return resolve(processed);
        }
        const fileReader = new FileReader();
        fileReader.addEventListener('error', reject);
        fileReader.addEventListener('load', () => {
          const result = fileReader.result;
          if (typeof result === 'string') {
            processed.push({
              file,
              result
            });
          }
          handleNextFile();
        });
        if (isMimeType(file, acceptableMimeTypes)) {
          fileReader.readAsDataURL(file);
        } else {
          handleNextFile();
        }
      };
      handleNextFile();
    });
  }

  /**
   * "Depth-First Search" starts at the root/top node of a tree and goes as far as it can down a branch end
   * before backtracking and finding a new path. Consider solving a maze by hugging either wall, moving down a
   * branch until you hit a dead-end (leaf) and backtracking to find the nearest branching path and repeat.
   * It will then return all the nodes found in the search in an array of objects.
   * @param startingNode - The node to start the search, if ommitted, it will start at the root node.
   * @param endingNode - The node to end the search, if ommitted, it will find all descendants of the startingNode.
   * @returns An array of objects of all the nodes found by the search, including their depth into the tree.
   * \\{depth: number, node: LexicalNode\\} It will always return at least 1 node (the ending node) so long as it exists
   */
  function $dfs(startingNode, endingNode) {
    const nodes = [];
    const start = (startingNode || $getRoot$1()).getLatest();
    const end = endingNode || ($isElementNode$1(start) ? start.getLastDescendant() || start : start);
    let node = start;
    let depth = $getDepth(node);
    while (node !== null && !node.is(end)) {
      nodes.push({
        depth,
        node
      });
      if ($isElementNode$1(node) && node.getChildrenSize() > 0) {
        node = node.getFirstChild();
        depth++;
      } else {
        // Find immediate sibling or nearest parent sibling
        let sibling = null;
        while (sibling === null && node !== null) {
          sibling = node.getNextSibling();
          if (sibling === null) {
            node = node.getParent();
            depth--;
          } else {
            node = sibling;
          }
        }
      }
    }
    if (node !== null && node.is(end)) {
      nodes.push({
        depth,
        node
      });
    }
    return nodes;
  }
  function $getDepth(node) {
    let innerNode = node;
    let depth = 0;
    while ((innerNode = innerNode.getParent()) !== null) {
      depth++;
    }
    return depth;
  }

  /**
   * Performs a right-to-left preorder tree traversal.
   * From the starting node it goes to the rightmost child, than backtracks to paret and finds new rightmost path.
   * It will return the next node in traversal sequence after the startingNode.
   * The traversal is similar to $dfs functions above, but the nodes are visited right-to-left, not left-to-right.
   * @param startingNode - The node to start the search.
   * @returns The next node in pre-order right to left traversal sequence or `null`, if the node does not exist
   */
  function $getNextRightPreorderNode(startingNode) {
    let node = startingNode;
    if ($isElementNode$1(node) && node.getChildrenSize() > 0) {
      node = node.getLastChild();
    } else {
      let sibling = null;
      while (sibling === null && node !== null) {
        sibling = node.getPreviousSibling();
        if (sibling === null) {
          node = node.getParent();
        } else {
          node = sibling;
        }
      }
    }
    return node;
  }

  /**
   * Takes a node and traverses up its ancestors (toward the root node)
   * in order to find a specific type of node.
   * @param node - the node to begin searching.
   * @param klass - an instance of the type of node to look for.
   * @returns the node of type klass that was passed, or null if none exist.
   */
  function $getNearestNodeOfType(node, klass) {
    let parent = node;
    while (parent != null) {
      if (parent instanceof klass) {
        return parent;
      }
      parent = parent.getParent();
    }
    return null;
  }

  /**
   * Returns the element node of the nearest ancestor, otherwise throws an error.
   * @param startNode - The starting node of the search
   * @returns The ancestor node found
   */
  function $getNearestBlockElementAncestorOrThrow(startNode) {
    const blockNode = $findMatchingParent(startNode, node => $isElementNode$1(node) && !node.isInline());
    if (!$isElementNode$1(blockNode)) {
      {
        throw Error(`Expected node ${startNode.__key} to have closest block element node.`);
      }
    }
    return blockNode;
  }
  /**
   * Starts with a node and moves up the tree (toward the root node) to find a matching node based on
   * the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be
   * passed as an argument. eg. if( (node) => node.__type === 'div') ) return true; otherwise return false
   * @param startingNode - The node where the search starts.
   * @param findFn - A testing function that returns true if the current node satisfies the testing parameters.
   * @returns A parent node that matches the findFn parameters, or null if one wasn't found.
   */
  const $findMatchingParent = (startingNode, findFn) => {
    let curr = startingNode;
    while (curr !== $getRoot$1() && curr != null) {
      if (findFn(curr)) {
        return curr;
      }
      curr = curr.getParent();
    }
    return null;
  };

  /**
   * Attempts to resolve nested element nodes of the same type into a single node of that type.
   * It is generally used for marks/commenting
   * @param editor - The lexical editor
   * @param targetNode - The target for the nested element to be extracted from.
   * @param cloneNode - See {@link $createMarkNode}
   * @param handleOverlap - Handles any overlap between the node to extract and the targetNode
   * @returns The lexical editor
   */
  function registerNestedElementResolver(editor, targetNode, cloneNode, handleOverlap) {
    const $isTargetNode = node => {
      return node instanceof targetNode;
    };
    const $findMatch = node => {
      // First validate we don't have any children that are of the target,
      // as we need to handle them first.
      const children = node.getChildren();
      for (let i = 0; i < children.length; i++) {
        const child = children[i];
        if ($isTargetNode(child)) {
          return null;
        }
      }
      let parentNode = node;
      let childNode = node;
      while (parentNode !== null) {
        childNode = parentNode;
        parentNode = parentNode.getParent();
        if ($isTargetNode(parentNode)) {
          return {
            child: childNode,
            parent: parentNode
          };
        }
      }
      return null;
    };
    const $elementNodeTransform = node => {
      const match = $findMatch(node);
      if (match !== null) {
        const {
          child,
          parent
        } = match;

        // Simple path, we can move child out and siblings into a new parent.

        if (child.is(node)) {
          handleOverlap(parent, node);
          const nextSiblings = child.getNextSiblings();
          const nextSiblingsLength = nextSiblings.length;
          parent.insertAfter(child);
          if (nextSiblingsLength !== 0) {
            const newParent = cloneNode(parent);
            child.insertAfter(newParent);
            for (let i = 0; i < nextSiblingsLength; i++) {
              newParent.append(nextSiblings[i]);
            }
          }
          if (!parent.canBeEmpty() && parent.getChildrenSize() === 0) {
            parent.remove();
          }
        }
      }
    };
    return editor.registerNodeTransform(targetNode, $elementNodeTransform);
  }

  /**
   * Clones the editor and marks it as dirty to be reconciled. If there was a selection,
   * it would be set back to its previous state, or null otherwise.
   * @param editor - The lexical editor
   * @param editorState - The editor's state
   */
  function $restoreEditorState(editor, editorState) {
    const FULL_RECONCILE = 2;
    const nodeMap = new Map();
    const activeEditorState = editor._pendingEditorState;
    for (const [key, node] of editorState._nodeMap) {
      nodeMap.set(key, $cloneWithProperties$1(node));
    }
    if (activeEditorState) {
      activeEditorState._nodeMap = nodeMap;
    }
    editor._dirtyType = FULL_RECONCILE;
    const selection = editorState._selection;
    $setSelection$1(selection === null ? null : selection.clone());
  }

  /**
   * If the selected insertion area is the root/shadow root node (see {@link lexical!$isRootOrShadowRoot}),
   * the node will be appended there, otherwise, it will be inserted before the insertion area.
   * If there is no selection where the node is to be inserted, it will be appended after any current nodes
   * within the tree, as a child of the root node. A paragraph node will then be added after the inserted node and selected.
   * @param node - The node to be inserted
   * @returns The node after its insertion
   */
  function $insertNodeToNearestRoot(node) {
    const selection = $getSelection$1() || $getPreviousSelection$1();
    if ($isRangeSelection$1(selection)) {
      const {
        focus
      } = selection;
      const focusNode = focus.getNode();
      const focusOffset = focus.offset;
      if ($isRootOrShadowRoot$1(focusNode)) {
        const focusChild = focusNode.getChildAtIndex(focusOffset);
        if (focusChild == null) {
          focusNode.append(node);
        } else {
          focusChild.insertBefore(node);
        }
        node.selectNext();
      } else {
        let splitNode;
        let splitOffset;
        if ($isTextNode$1(focusNode)) {
          splitNode = focusNode.getParentOrThrow();
          splitOffset = focusNode.getIndexWithinParent();
          if (focusOffset > 0) {
            splitOffset += 1;
            focusNode.splitText(focusOffset);
          }
        } else {
          splitNode = focusNode;
          splitOffset = focusOffset;
        }
        const [, rightTree] = $splitNode$1(splitNode, splitOffset);
        rightTree.insertBefore(node);
        rightTree.selectStart();
      }
    } else {
      if (selection != null) {
        const nodes = selection.getNodes();
        nodes[nodes.length - 1].getTopLevelElementOrThrow().insertAfter(node);
      } else {
        const root = $getRoot$1();
        root.append(node);
      }
      const paragraphNode = $createParagraphNode$1();
      node.insertAfter(paragraphNode);
      paragraphNode.select();
    }
    return node.getLatest();
  }

  /**
   * Wraps the node into another node created from a createElementNode function, eg. $createParagraphNode
   * @param node - Node to be wrapped.
   * @param createElementNode - Creates a new lexical element to wrap the to-be-wrapped node and returns it.
   * @returns A new lexical element with the previous node appended within (as a child, including its children).
   */
  function $wrapNodeInElement(node, createElementNode) {
    const elementNode = createElementNode();
    node.replace(elementNode);
    elementNode.append(node);
    return elementNode;
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any

  /**
   * @param object = The instance of the type
   * @param objectClass = The class of the type
   * @returns Whether the object is has the same Klass of the objectClass, ignoring the difference across window (e.g. different iframs)
   */
  function objectKlassEquals(object, objectClass) {
    return object !== null ? Object.getPrototypeOf(object).constructor.name === objectClass.name : false;
  }

  /**
   * Filter the nodes
   * @param nodes Array of nodes that needs to be filtered
   * @param filterFn A filter function that returns node if the current node satisfies the condition otherwise null
   * @returns Array of filtered nodes
   */

  function $filter(nodes, filterFn) {
    const result = [];
    for (let i = 0; i < nodes.length; i++) {
      const node = filterFn(nodes[i]);
      if (node !== null) {
        result.push(node);
      }
    }
    return result;
  }
  /**
   * Appends the node before the first child of the parent node
   * @param parent A parent node
   * @param node Node that needs to be appended
   */
  function $insertFirst(parent, node) {
    const firstChild = parent.getFirstChild();
    if (firstChild !== null) {
      firstChild.insertBefore(node);
    } else {
      parent.append(node);
    }
  }

  /**
   * Calculates the zoom level of an element as a result of using
   * css zoom property.
   * @param element
   */
  function calculateZoomLevel(element) {
    if (IS_FIREFOX$2) {
      return 1;
    }
    let zoom = 1;
    while (element) {
      zoom *= Number(window.getComputedStyle(element).getPropertyValue('zoom'));
      element = element.parentElement;
    }
    return zoom;
  }

  /**
   * Checks if the editor is a nested editor created by LexicalNestedComposer
   */
  function $isEditorIsNestedEditor(editor) {
    return editor._parentEditor !== null;
  }

  var modDev$2 = /*#__PURE__*/Object.freeze({
    $dfs: $dfs,
    $filter: $filter,
    $findMatchingParent: $findMatchingParent,
    $getNearestBlockElementAncestorOrThrow: $getNearestBlockElementAncestorOrThrow,
    $getNearestNodeOfType: $getNearestNodeOfType,
    $getNextRightPreorderNode: $getNextRightPreorderNode,
    $insertFirst: $insertFirst,
    $insertNodeToNearestRoot: $insertNodeToNearestRoot,
    $isEditorIsNestedEditor: $isEditorIsNestedEditor,
    $restoreEditorState: $restoreEditorState,
    $wrapNodeInElement: $wrapNodeInElement,
    CAN_USE_BEFORE_INPUT: CAN_USE_BEFORE_INPUT$2,
    CAN_USE_DOM: CAN_USE_DOM$2,
    IS_ANDROID: IS_ANDROID$2,
    IS_ANDROID_CHROME: IS_ANDROID_CHROME$2,
    IS_APPLE: IS_APPLE$2,
    IS_APPLE_WEBKIT: IS_APPLE_WEBKIT$2,
    IS_CHROME: IS_CHROME$2,
    IS_FIREFOX: IS_FIREFOX$2,
    IS_IOS: IS_IOS$2,
    IS_SAFARI: IS_SAFARI$2,
    addClassNamesToElement: addClassNamesToElement,
    calculateZoomLevel: calculateZoomLevel,
    isMimeType: isMimeType,
    markSelection: markSelection,
    mediaFileReader: mediaFileReader,
    mergeRegister: mergeRegister,
    objectKlassEquals: objectKlassEquals,
    positionNodeOnRange: positionNodeOnRange,
    registerNestedElementResolver: registerNestedElementResolver,
    removeClassNamesFromElement: removeClassNamesFromElement,
    $splitNode: $splitNode$1,
    isBlockDomNode: isBlockDomNode$1,
    isHTMLAnchorElement: isHTMLAnchorElement$1,
    isHTMLElement: isHTMLElement$1,
    isInlineDomNode: isInlineDomNode$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  function g$1(e) {
    return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
  }
  var p$1 = g$1(function (e) {
    const t = new URLSearchParams();
    t.append("code", e);
    for (let e = 1; e < arguments.length; e++) t.append("v", arguments[e]);
    throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });
  const h$1 = "undefined" != typeof window && void 0 !== window.document && void 0 !== window.document.createElement,
    m$2 = h$1 && "documentMode" in document ? document.documentMode : null,
    v$2 = h$1 && /Mac|iPod|iPhone|iPad/.test(navigator.platform),
    y$1 = h$1 && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent),
    w$2 = !(!h$1 || !("InputEvent" in window) || m$2) && "getTargetRanges" in new window.InputEvent("input"),
    E$2 = h$1 && /Version\/[\d.]+.*Safari/.test(navigator.userAgent),
    P$2 = h$1 && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,
    S$2 = h$1 && /Android/.test(navigator.userAgent),
    x$2 = h$1 && /^(?=.*Chrome).*/i.test(navigator.userAgent),
    A$2 = h$1 && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !x$2;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$2 = modDev$2;
  const $dfs$1 = mod$2.$dfs;
  const $filter$1 = mod$2.$filter;
  const $findMatchingParent$1 = mod$2.$findMatchingParent;
  const $getNearestBlockElementAncestorOrThrow$1 = mod$2.$getNearestBlockElementAncestorOrThrow;
  const $getNearestNodeOfType$1 = mod$2.$getNearestNodeOfType;
  const $getNextRightPreorderNode$1 = mod$2.$getNextRightPreorderNode;
  const $insertFirst$1 = mod$2.$insertFirst;
  const $insertNodeToNearestRoot$1 = mod$2.$insertNodeToNearestRoot;
  const $isEditorIsNestedEditor$1 = mod$2.$isEditorIsNestedEditor;
  const $restoreEditorState$1 = mod$2.$restoreEditorState;
  const $splitNode$2 = mod$2.$splitNode;
  const $wrapNodeInElement$1 = mod$2.$wrapNodeInElement;
  const CAN_USE_BEFORE_INPUT$3 = mod$2.CAN_USE_BEFORE_INPUT;
  const CAN_USE_DOM$3 = mod$2.CAN_USE_DOM;
  const IS_ANDROID$3 = mod$2.IS_ANDROID;
  const IS_ANDROID_CHROME$3 = mod$2.IS_ANDROID_CHROME;
  const IS_APPLE$3 = mod$2.IS_APPLE;
  const IS_APPLE_WEBKIT$3 = mod$2.IS_APPLE_WEBKIT;
  const IS_CHROME$3 = mod$2.IS_CHROME;
  const IS_FIREFOX$3 = mod$2.IS_FIREFOX;
  const IS_IOS$3 = mod$2.IS_IOS;
  const IS_SAFARI$3 = mod$2.IS_SAFARI;
  const addClassNamesToElement$1 = mod$2.addClassNamesToElement;
  const calculateZoomLevel$1 = mod$2.calculateZoomLevel;
  const isBlockDomNode$2 = mod$2.isBlockDomNode;
  const isHTMLAnchorElement$2 = mod$2.isHTMLAnchorElement;
  const isHTMLElement$2 = mod$2.isHTMLElement;
  const isInlineDomNode$2 = mod$2.isInlineDomNode;
  const isMimeType$1 = mod$2.isMimeType;
  const markSelection$1 = mod$2.markSelection;
  const mediaFileReader$1 = mod$2.mediaFileReader;
  const mergeRegister$1 = mod$2.mergeRegister;
  const objectKlassEquals$1 = mod$2.objectKlassEquals;
  const positionNodeOnRange$1 = mod$2.positionNodeOnRange;
  const registerNestedElementResolver$1 = mod$2.registerNestedElementResolver;
  const removeClassNamesFromElement$1 = mod$2.removeClassNamesFromElement;

  var LexicalUtils = /*#__PURE__*/Object.freeze({
    $dfs: $dfs$1,
    $filter: $filter$1,
    $findMatchingParent: $findMatchingParent$1,
    $getNearestBlockElementAncestorOrThrow: $getNearestBlockElementAncestorOrThrow$1,
    $getNearestNodeOfType: $getNearestNodeOfType$1,
    $getNextRightPreorderNode: $getNextRightPreorderNode$1,
    $insertFirst: $insertFirst$1,
    $insertNodeToNearestRoot: $insertNodeToNearestRoot$1,
    $isEditorIsNestedEditor: $isEditorIsNestedEditor$1,
    $restoreEditorState: $restoreEditorState$1,
    $splitNode: $splitNode$2,
    $wrapNodeInElement: $wrapNodeInElement$1,
    CAN_USE_BEFORE_INPUT: CAN_USE_BEFORE_INPUT$3,
    CAN_USE_DOM: CAN_USE_DOM$3,
    IS_ANDROID: IS_ANDROID$3,
    IS_ANDROID_CHROME: IS_ANDROID_CHROME$3,
    IS_APPLE: IS_APPLE$3,
    IS_APPLE_WEBKIT: IS_APPLE_WEBKIT$3,
    IS_CHROME: IS_CHROME$3,
    IS_FIREFOX: IS_FIREFOX$3,
    IS_IOS: IS_IOS$3,
    IS_SAFARI: IS_SAFARI$3,
    addClassNamesToElement: addClassNamesToElement$1,
    calculateZoomLevel: calculateZoomLevel$1,
    isBlockDomNode: isBlockDomNode$2,
    isHTMLAnchorElement: isHTMLAnchorElement$2,
    isHTMLElement: isHTMLElement$2,
    isInlineDomNode: isInlineDomNode$2,
    isMimeType: isMimeType$1,
    markSelection: markSelection$1,
    mediaFileReader: mediaFileReader$1,
    mergeRegister: mergeRegister$1,
    objectKlassEquals: objectKlassEquals$1,
    positionNodeOnRange: positionNodeOnRange$1,
    registerNestedElementResolver: registerNestedElementResolver$1,
    removeClassNamesFromElement: removeClassNamesFromElement$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * How you parse your html string to get a document is left up to you. In the browser you can use the native
   * DOMParser API to generate a document (see clipboard.ts), but to use in a headless environment you can use JSDom
   * or an equivalent library and pass in the document here.
   */
  function $generateNodesFromDOM(editor, dom) {
    const elements = dom.body ? dom.body.childNodes : [];
    let lexicalNodes = [];
    const allArtificialNodes = [];
    for (let i = 0; i < elements.length; i++) {
      const element = elements[i];
      if (!IGNORE_TAGS.has(element.nodeName)) {
        const lexicalNode = $createNodesFromDOM(element, editor, allArtificialNodes, false);
        if (lexicalNode !== null) {
          lexicalNodes = lexicalNodes.concat(lexicalNode);
        }
      }
    }
    $unwrapArtificalNodes(allArtificialNodes);
    return lexicalNodes;
  }
  function $generateHtmlFromNodes(editor, selection) {
    if (typeof document === 'undefined' || typeof window === 'undefined' && typeof global.window === 'undefined') {
      throw new Error('To use $generateHtmlFromNodes in headless mode please initialize a headless browser implementation such as JSDom before calling this function.');
    }
    const container = document.createElement('div');
    const root = $getRoot$1();
    const topLevelChildren = root.getChildren();
    for (let i = 0; i < topLevelChildren.length; i++) {
      const topLevelNode = topLevelChildren[i];
      $appendNodesToHTML(editor, topLevelNode, container, selection);
    }
    return container.innerHTML;
  }
  function $appendNodesToHTML(editor, currentNode, parentElement, selection = null) {
    let shouldInclude = selection !== null ? currentNode.isSelected(selection) : true;
    const shouldExclude = $isElementNode$1(currentNode) && currentNode.excludeFromCopy('html');
    let target = currentNode;
    if (selection !== null) {
      let clone = $cloneWithProperties$1(currentNode);
      clone = $isTextNode$1(clone) && selection !== null ? $sliceSelectedTextNodeContent$1(selection, clone) : clone;
      target = clone;
    }
    const children = $isElementNode$1(target) ? target.getChildren() : [];
    const registeredNode = editor._nodes.get(target.getType());
    let exportOutput;

    // Use HTMLConfig overrides, if available.
    if (registeredNode && registeredNode.exportDOM !== undefined) {
      exportOutput = registeredNode.exportDOM(editor, target);
    } else {
      exportOutput = target.exportDOM(editor);
    }
    const {
      element,
      after
    } = exportOutput;
    if (!element) {
      return false;
    }
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < children.length; i++) {
      const childNode = children[i];
      const shouldIncludeChild = $appendNodesToHTML(editor, childNode, fragment, selection);
      if (!shouldInclude && $isElementNode$1(currentNode) && shouldIncludeChild && currentNode.extractWithChild(childNode, selection, 'html')) {
        shouldInclude = true;
      }
    }
    if (shouldInclude && !shouldExclude) {
      if (isHTMLElement$2(element)) {
        element.append(fragment);
      }
      parentElement.append(element);
      if (after) {
        const newElement = after.call(target, element);
        if (newElement) {
          element.replaceWith(newElement);
        }
      }
    } else {
      parentElement.append(fragment);
    }
    return shouldInclude;
  }
  function getConversionFunction(domNode, editor) {
    const {
      nodeName
    } = domNode;
    const cachedConversions = editor._htmlConversions.get(nodeName.toLowerCase());
    let currentConversion = null;
    if (cachedConversions !== undefined) {
      for (const cachedConversion of cachedConversions) {
        const domConversion = cachedConversion(domNode);
        if (domConversion !== null && (currentConversion === null || (currentConversion.priority || 0) < (domConversion.priority || 0))) {
          currentConversion = domConversion;
        }
      }
    }
    return currentConversion !== null ? currentConversion.conversion : null;
  }
  const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']);
  function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorLexicalNode, forChildMap = new Map(), parentLexicalNode) {
    let lexicalNodes = [];
    if (IGNORE_TAGS.has(node.nodeName)) {
      return lexicalNodes;
    }
    let currentLexicalNode = null;
    const transformFunction = getConversionFunction(node, editor);
    const transformOutput = transformFunction ? transformFunction(node) : null;
    let postTransform = null;
    if (transformOutput !== null) {
      postTransform = transformOutput.after;
      const transformNodes = transformOutput.node;
      currentLexicalNode = Array.isArray(transformNodes) ? transformNodes[transformNodes.length - 1] : transformNodes;
      if (currentLexicalNode !== null) {
        for (const [, forChildFunction] of forChildMap) {
          currentLexicalNode = forChildFunction(currentLexicalNode, parentLexicalNode);
          if (!currentLexicalNode) {
            break;
          }
        }
        if (currentLexicalNode) {
          lexicalNodes.push(...(Array.isArray(transformNodes) ? transformNodes : [currentLexicalNode]));
        }
      }
      if (transformOutput.forChild != null) {
        forChildMap.set(node.nodeName, transformOutput.forChild);
      }
    }

    // If the DOM node doesn't have a transformer, we don't know what
    // to do with it but we still need to process any childNodes.
    const children = node.childNodes;
    let childLexicalNodes = [];
    const hasBlockAncestorLexicalNodeForChildren = currentLexicalNode != null && $isRootOrShadowRoot$1(currentLexicalNode) ? false : currentLexicalNode != null && $isBlockElementNode$1(currentLexicalNode) || hasBlockAncestorLexicalNode;
    for (let i = 0; i < children.length; i++) {
      childLexicalNodes.push(...$createNodesFromDOM(children[i], editor, allArtificialNodes, hasBlockAncestorLexicalNodeForChildren, new Map(forChildMap), currentLexicalNode));
    }
    if (postTransform != null) {
      childLexicalNodes = postTransform(childLexicalNodes);
    }
    if (isBlockDomNode$2(node)) {
      if (!hasBlockAncestorLexicalNodeForChildren) {
        childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, $createParagraphNode$1);
      } else {
        childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, () => {
          const artificialNode = new ArtificialNode__DO_NOT_USE$1();
          allArtificialNodes.push(artificialNode);
          return artificialNode;
        });
      }
    }
    if (currentLexicalNode == null) {
      if (childLexicalNodes.length > 0) {
        // If it hasn't been converted to a LexicalNode, we hoist its children
        // up to the same level as it.
        lexicalNodes = lexicalNodes.concat(childLexicalNodes);
      } else {
        if (isBlockDomNode$2(node) && isDomNodeBetweenTwoInlineNodes(node)) {
          // Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes
          lexicalNodes = lexicalNodes.concat($createLineBreakNode$1());
        }
      }
    } else {
      if ($isElementNode$1(currentLexicalNode)) {
        // If the current node is a ElementNode after conversion,
        // we can append all the children to it.
        currentLexicalNode.append(...childLexicalNodes);
      }
    }
    return lexicalNodes;
  }
  function wrapContinuousInlines(domNode, nodes, createWrapperFn) {
    const textAlign = domNode.style.textAlign;
    const out = [];
    let continuousInlines = [];
    // wrap contiguous inline child nodes in para
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if ($isBlockElementNode$1(node)) {
        if (textAlign && !node.getFormat()) {
          node.setFormat(textAlign);
        }
        out.push(node);
      } else {
        continuousInlines.push(node);
        if (i === nodes.length - 1 || i < nodes.length - 1 && $isBlockElementNode$1(nodes[i + 1])) {
          const wrapper = createWrapperFn();
          wrapper.setFormat(textAlign);
          wrapper.append(...continuousInlines);
          out.push(wrapper);
          continuousInlines = [];
        }
      }
    }
    return out;
  }
  function $unwrapArtificalNodes(allArtificialNodes) {
    for (const node of allArtificialNodes) {
      if (node.getNextSibling() instanceof ArtificialNode__DO_NOT_USE$1) {
        node.insertAfter($createLineBreakNode$1());
      }
    }
    // Replace artificial node with it's children
    for (const node of allArtificialNodes) {
      const children = node.getChildren();
      for (const child of children) {
        node.insertBefore(child);
      }
      node.remove();
    }
  }
  function isDomNodeBetweenTwoInlineNodes(node) {
    if (node.nextSibling == null || node.previousSibling == null) {
      return false;
    }
    return isInlineDomNode$1(node.nextSibling) && isInlineDomNode$1(node.previousSibling);
  }

  var modDev$3 = /*#__PURE__*/Object.freeze({
    $generateHtmlFromNodes: $generateHtmlFromNodes,
    $generateNodesFromDOM: $generateNodesFromDOM
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$3 = modDev$3;
  const $generateHtmlFromNodes$1 = mod$3.$generateHtmlFromNodes;
  const $generateNodesFromDOM$1 = mod$3.$generateNodesFromDOM;

  var LexicalHtml = /*#__PURE__*/Object.freeze({
    $generateHtmlFromNodes: $generateHtmlFromNodes$1,
    $generateNodesFromDOM: $generateNodesFromDOM$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Checks the depth of listNode from the root node.
   * @param listNode - The ListNode to be checked.
   * @returns The depth of the ListNode.
   */
  function $getListDepth(listNode) {
    let depth = 1;
    let parent = listNode.getParent();
    while (parent != null) {
      if ($isListItemNode(parent)) {
        const parentList = parent.getParent();
        if ($isListNode(parentList)) {
          depth++;
          parent = parentList.getParent();
          continue;
        }
        {
          throw Error(`A ListItemNode must have a ListNode for a parent.`);
        }
      }
      return depth;
    }
    return depth;
  }

  /**
   * Finds the nearest ancestral ListNode and returns it, throws an invariant if listItem is not a ListItemNode.
   * @param listItem - The node to be checked.
   * @returns The ListNode found.
   */
  function $getTopListNode(listItem) {
    let list = listItem.getParent();
    if (!$isListNode(list)) {
      {
        throw Error(`A ListItemNode must have a ListNode for a parent.`);
      }
    }
    let parent = list;
    while (parent !== null) {
      parent = parent.getParent();
      if ($isListNode(parent)) {
        list = parent;
      }
    }
    return list;
  }

  /**
   * A recursive Depth-First Search (Postorder Traversal) that finds all of a node's children
   * that are of type ListItemNode and returns them in an array.
   * @param node - The ListNode to start the search.
   * @returns An array containing all nodes of type ListItemNode found.
   */
  // This should probably be $getAllChildrenOfType
  function $getAllListItems(node) {
    let listItemNodes = [];
    const listChildren = node.getChildren().filter($isListItemNode);
    for (let i = 0; i < listChildren.length; i++) {
      const listItemNode = listChildren[i];
      const firstChild = listItemNode.getFirstChild();
      if ($isListNode(firstChild)) {
        listItemNodes = listItemNodes.concat($getAllListItems(firstChild));
      } else {
        listItemNodes.push(listItemNode);
      }
    }
    return listItemNodes;
  }

  /**
   * Checks to see if the passed node is a ListItemNode and has a ListNode as a child.
   * @param node - The node to be checked.
   * @returns true if the node is a ListItemNode and has a ListNode child, false otherwise.
   */
  function isNestedListNode(node) {
    return $isListItemNode(node) && $isListNode(node.getFirstChild());
  }

  /**
   * Takes a deeply nested ListNode or ListItemNode and traverses up the branch to delete the first
   * ancestral ListNode (which could be the root ListNode) or ListItemNode with siblings, essentially
   * bringing the deeply nested node up the branch once. Would remove sublist if it has siblings.
   * Should not break ListItem -> List -> ListItem chain as empty List/ItemNodes should be removed on .remove().
   * @param sublist - The nested ListNode or ListItemNode to be brought up the branch.
   */
  function $removeHighestEmptyListParent(sublist) {
    // Nodes may be repeatedly indented, to create deeply nested lists that each
    // contain just one bullet.
    // Our goal is to remove these (empty) deeply nested lists. The easiest
    // way to do that is crawl back up the tree until we find a node that has siblings
    // (e.g. is actually part of the list contents) and delete that, or delete
    // the root of the list (if no list nodes have siblings.)
    let emptyListPtr = sublist;
    while (emptyListPtr.getNextSibling() == null && emptyListPtr.getPreviousSibling() == null) {
      const parent = emptyListPtr.getParent();
      if (parent == null || !($isListItemNode(emptyListPtr) || $isListNode(emptyListPtr))) {
        break;
      }
      emptyListPtr = parent;
    }
    emptyListPtr.remove();
  }

  /**
   * Wraps a node into a ListItemNode.
   * @param node - The node to be wrapped into a ListItemNode
   * @returns The ListItemNode which the passed node is wrapped in.
   */
  function $wrapInListItem(node) {
    const listItemWrapper = $createListItemNode();
    return listItemWrapper.append(node);
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function $isSelectingEmptyListItem(anchorNode, nodes) {
    return $isListItemNode(anchorNode) && (nodes.length === 0 || nodes.length === 1 && anchorNode.is(nodes[0]) && anchorNode.getChildrenSize() === 0);
  }

  /**
   * Inserts a new ListNode. If the selection's anchor node is an empty ListItemNode and is a child of
   * the root/shadow root, it will replace the ListItemNode with a ListNode and the old ListItemNode.
   * Otherwise it will replace its parent with a new ListNode and re-insert the ListItemNode and any previous children.
   * If the selection's anchor node is not an empty ListItemNode, it will add a new ListNode or merge an existing ListNode,
   * unless the the node is a leaf node, in which case it will attempt to find a ListNode up the branch and replace it with
   * a new ListNode, or create a new ListNode at the nearest root/shadow root.
   * @param editor - The lexical editor.
   * @param listType - The type of list, "number" | "bullet" | "check".
   */
  function insertList(editor, listType) {
    editor.update(() => {
      const selection = $getSelection$1();
      if (selection !== null) {
        const nodes = selection.getNodes();
        if ($isRangeSelection$1(selection)) {
          const anchorAndFocus = selection.getStartEndPoints();
          if (!(anchorAndFocus !== null)) {
            throw Error(`insertList: anchor should be defined`);
          }
          const [anchor] = anchorAndFocus;
          const anchorNode = anchor.getNode();
          const anchorNodeParent = anchorNode.getParent();
          if ($isSelectingEmptyListItem(anchorNode, nodes)) {
            const list = $createListNode(listType);
            if ($isRootOrShadowRoot$1(anchorNodeParent)) {
              anchorNode.replace(list);
              const listItem = $createListItemNode();
              if ($isElementNode$1(anchorNode)) {
                listItem.setFormat(anchorNode.getFormatType());
                listItem.setIndent(anchorNode.getIndent());
              }
              list.append(listItem);
            } else if ($isListItemNode(anchorNode)) {
              const parent = anchorNode.getParentOrThrow();
              append(list, parent.getChildren());
              parent.replace(list);
            }
            return;
          }
        }
        const handled = new Set();
        for (let i = 0; i < nodes.length; i++) {
          const node = nodes[i];
          if ($isElementNode$1(node) && node.isEmpty() && !$isListItemNode(node) && !handled.has(node.getKey())) {
            $createListOrMerge(node, listType);
            continue;
          }
          if ($isLeafNode$1(node)) {
            let parent = node.getParent();
            while (parent != null) {
              const parentKey = parent.getKey();
              if ($isListNode(parent)) {
                if (!handled.has(parentKey)) {
                  const newListNode = $createListNode(listType);
                  append(newListNode, parent.getChildren());
                  parent.replace(newListNode);
                  handled.add(parentKey);
                }
                break;
              } else {
                const nextParent = parent.getParent();
                if ($isRootOrShadowRoot$1(nextParent) && !handled.has(parentKey)) {
                  handled.add(parentKey);
                  $createListOrMerge(parent, listType);
                  break;
                }
                parent = nextParent;
              }
            }
          }
        }
      }
    });
  }
  function append(node, nodesToAppend) {
    node.splice(node.getChildrenSize(), 0, nodesToAppend);
  }
  function $createListOrMerge(node, listType) {
    if ($isListNode(node)) {
      return node;
    }
    const previousSibling = node.getPreviousSibling();
    const nextSibling = node.getNextSibling();
    const listItem = $createListItemNode();
    listItem.setFormat(node.getFormatType());
    listItem.setIndent(node.getIndent());
    append(listItem, node.getChildren());
    if ($isListNode(previousSibling) && listType === previousSibling.getListType()) {
      previousSibling.append(listItem);
      node.remove();
      // if the same type of list is on both sides, merge them.

      if ($isListNode(nextSibling) && listType === nextSibling.getListType()) {
        append(previousSibling, nextSibling.getChildren());
        nextSibling.remove();
      }
      return previousSibling;
    } else if ($isListNode(nextSibling) && listType === nextSibling.getListType()) {
      nextSibling.getFirstChildOrThrow().insertBefore(listItem);
      node.remove();
      return nextSibling;
    } else {
      const list = $createListNode(listType);
      list.append(listItem);
      node.replace(list);
      return list;
    }
  }

  /**
   * A recursive function that goes through each list and their children, including nested lists,
   * appending list2 children after list1 children and updating ListItemNode values.
   * @param list1 - The first list to be merged.
   * @param list2 - The second list to be merged.
   */
  function mergeLists(list1, list2) {
    const listItem1 = list1.getLastChild();
    const listItem2 = list2.getFirstChild();
    if (listItem1 && listItem2 && isNestedListNode(listItem1) && isNestedListNode(listItem2)) {
      mergeLists(listItem1.getFirstChild(), listItem2.getFirstChild());
      listItem2.remove();
    }
    const toMerge = list2.getChildren();
    if (toMerge.length > 0) {
      list1.append(...toMerge);
    }
    list2.remove();
  }

  /**
   * Searches for the nearest ancestral ListNode and removes it. If selection is an empty ListItemNode
   * it will remove the whole list, including the ListItemNode. For each ListItemNode in the ListNode,
   * removeList will also generate new ParagraphNodes in the removed ListNode's place. Any child node
   * inside a ListItemNode will be appended to the new ParagraphNodes.
   * @param editor - The lexical editor.
   */
  function removeList(editor) {
    editor.update(() => {
      const selection = $getSelection$1();
      if ($isRangeSelection$1(selection)) {
        const listNodes = new Set();
        const nodes = selection.getNodes();
        const anchorNode = selection.anchor.getNode();
        if ($isSelectingEmptyListItem(anchorNode, nodes)) {
          listNodes.add($getTopListNode(anchorNode));
        } else {
          for (let i = 0; i < nodes.length; i++) {
            const node = nodes[i];
            if ($isLeafNode$1(node)) {
              const listItemNode = $getNearestNodeOfType$1(node, ListItemNode);
              if (listItemNode != null) {
                listNodes.add($getTopListNode(listItemNode));
              }
            }
          }
        }
        for (const listNode of listNodes) {
          let insertionPoint = listNode;
          const listItems = $getAllListItems(listNode);
          for (const listItemNode of listItems) {
            const paragraph = $createParagraphNode$1();
            append(paragraph, listItemNode.getChildren());
            insertionPoint.insertAfter(paragraph);
            insertionPoint = paragraph;

            // When the anchor and focus fall on the textNode
            // we don't have to change the selection because the textNode will be appended to
            // the newly generated paragraph.
            // When selection is in empty nested list item, selection is actually on the listItemNode.
            // When the corresponding listItemNode is deleted and replaced by the newly generated paragraph
            // we should manually set the selection's focus and anchor to the newly generated paragraph.
            if (listItemNode.__key === selection.anchor.key) {
              selection.anchor.set(paragraph.getKey(), 0, 'element');
            }
            if (listItemNode.__key === selection.focus.key) {
              selection.focus.set(paragraph.getKey(), 0, 'element');
            }
            listItemNode.remove();
          }
          listNode.remove();
        }
      }
    });
  }

  /**
   * Takes the value of a child ListItemNode and makes it the value the ListItemNode
   * should be if it isn't already. Also ensures that checked is undefined if the
   * parent does not have a list type of 'check'.
   * @param list - The list whose children are updated.
   */
  function updateChildrenListItemValue(list) {
    const isNotChecklist = list.getListType() !== 'check';
    let value = list.getStart();
    for (const child of list.getChildren()) {
      if ($isListItemNode(child)) {
        if (child.getValue() !== value) {
          child.setValue(value);
        }
        if (isNotChecklist && child.getLatest().__checked != null) {
          child.setChecked(undefined);
        }
        if (!$isListNode(child.getFirstChild())) {
          value++;
        }
      }
    }
  }

  /**
   * Merge the next sibling list if same type.
   * <ul> will merge with <ul>, but NOT <ul> with <ol>.
   * @param list - The list whose next sibling should be potentially merged
   */
  function mergeNextSiblingListIfSameType(list) {
    const nextSibling = list.getNextSibling();
    if ($isListNode(nextSibling) && list.getListType() === nextSibling.getListType()) {
      mergeLists(list, nextSibling);
    }
  }

  /**
   * Adds an empty ListNode/ListItemNode chain at listItemNode, so as to
   * create an indent effect. Won't indent ListItemNodes that have a ListNode as
   * a child, but does merge sibling ListItemNodes if one has a nested ListNode.
   * @param listItemNode - The ListItemNode to be indented.
   */
  function $handleIndent(listItemNode) {
    // go through each node and decide where to move it.
    const removed = new Set();
    if (isNestedListNode(listItemNode) || removed.has(listItemNode.getKey())) {
      return;
    }
    const parent = listItemNode.getParent();

    // We can cast both of the below `isNestedListNode` only returns a boolean type instead of a user-defined type guards
    const nextSibling = listItemNode.getNextSibling();
    const previousSibling = listItemNode.getPreviousSibling();
    // if there are nested lists on either side, merge them all together.

    if (isNestedListNode(nextSibling) && isNestedListNode(previousSibling)) {
      const innerList = previousSibling.getFirstChild();
      if ($isListNode(innerList)) {
        innerList.append(listItemNode);
        const nextInnerList = nextSibling.getFirstChild();
        if ($isListNode(nextInnerList)) {
          const children = nextInnerList.getChildren();
          append(innerList, children);
          nextSibling.remove();
          removed.add(nextSibling.getKey());
        }
      }
    } else if (isNestedListNode(nextSibling)) {
      // if the ListItemNode is next to a nested ListNode, merge them
      const innerList = nextSibling.getFirstChild();
      if ($isListNode(innerList)) {
        const firstChild = innerList.getFirstChild();
        if (firstChild !== null) {
          firstChild.insertBefore(listItemNode);
        }
      }
    } else if (isNestedListNode(previousSibling)) {
      const innerList = previousSibling.getFirstChild();
      if ($isListNode(innerList)) {
        innerList.append(listItemNode);
      }
    } else {
      // otherwise, we need to create a new nested ListNode

      if ($isListNode(parent)) {
        const newListItem = $createListItemNode();
        const newList = $createListNode(parent.getListType());
        newListItem.append(newList);
        newList.append(listItemNode);
        if (previousSibling) {
          previousSibling.insertAfter(newListItem);
        } else if (nextSibling) {
          nextSibling.insertBefore(newListItem);
        } else {
          parent.append(newListItem);
        }
      }
    }
  }

  /**
   * Removes an indent by removing an empty ListNode/ListItemNode chain. An indented ListItemNode
   * has a great grandparent node of type ListNode, which is where the ListItemNode will reside
   * within as a child.
   * @param listItemNode - The ListItemNode to remove the indent (outdent).
   */
  function $handleOutdent(listItemNode) {
    // go through each node and decide where to move it.

    if (isNestedListNode(listItemNode)) {
      return;
    }
    const parentList = listItemNode.getParent();
    const grandparentListItem = parentList ? parentList.getParent() : undefined;
    const greatGrandparentList = grandparentListItem ? grandparentListItem.getParent() : undefined;
    // If it doesn't have these ancestors, it's not indented.

    if ($isListNode(greatGrandparentList) && $isListItemNode(grandparentListItem) && $isListNode(parentList)) {
      // if it's the first child in it's parent list, insert it into the
      // great grandparent list before the grandparent
      const firstChild = parentList ? parentList.getFirstChild() : undefined;
      const lastChild = parentList ? parentList.getLastChild() : undefined;
      if (listItemNode.is(firstChild)) {
        grandparentListItem.insertBefore(listItemNode);
        if (parentList.isEmpty()) {
          grandparentListItem.remove();
        }
        // if it's the last child in it's parent list, insert it into the
        // great grandparent list after the grandparent.
      } else if (listItemNode.is(lastChild)) {
        grandparentListItem.insertAfter(listItemNode);
        if (parentList.isEmpty()) {
          grandparentListItem.remove();
        }
      } else {
        // otherwise, we need to split the siblings into two new nested lists
        const listType = parentList.getListType();
        const previousSiblingsListItem = $createListItemNode();
        const previousSiblingsList = $createListNode(listType);
        previousSiblingsListItem.append(previousSiblingsList);
        listItemNode.getPreviousSiblings().forEach(sibling => previousSiblingsList.append(sibling));
        const nextSiblingsListItem = $createListItemNode();
        const nextSiblingsList = $createListNode(listType);
        nextSiblingsListItem.append(nextSiblingsList);
        append(nextSiblingsList, listItemNode.getNextSiblings());
        // put the sibling nested lists on either side of the grandparent list item in the great grandparent.
        grandparentListItem.insertBefore(previousSiblingsListItem);
        grandparentListItem.insertAfter(nextSiblingsListItem);
        // replace the grandparent list item (now between the siblings) with the outdented list item.
        grandparentListItem.replace(listItemNode);
      }
    }
  }

  /**
   * Attempts to insert a ParagraphNode at selection and selects the new node. The selection must contain a ListItemNode
   * or a node that does not already contain text. If its grandparent is the root/shadow root, it will get the ListNode
   * (which should be the parent node) and insert the ParagraphNode as a sibling to the ListNode. If the ListNode is
   * nested in a ListItemNode instead, it will add the ParagraphNode after the grandparent ListItemNode.
   * Throws an invariant if the selection is not a child of a ListNode.
   * @returns true if a ParagraphNode was inserted succesfully, false if there is no selection
   * or the selection does not contain a ListItemNode or the node already holds text.
   */
  function $handleListInsertParagraph() {
    const selection = $getSelection$1();
    if (!$isRangeSelection$1(selection) || !selection.isCollapsed()) {
      return false;
    }
    // Only run this code on empty list items
    const anchor = selection.anchor.getNode();
    if (!$isListItemNode(anchor) || anchor.getChildrenSize() !== 0) {
      return false;
    }
    const topListNode = $getTopListNode(anchor);
    const parent = anchor.getParent();
    if (!$isListNode(parent)) {
      throw Error(`A ListItemNode must have a ListNode for a parent.`);
    }
    const grandparent = parent.getParent();
    let replacementNode;
    if ($isRootOrShadowRoot$1(grandparent)) {
      replacementNode = $createParagraphNode$1();
      topListNode.insertAfter(replacementNode);
    } else if ($isListItemNode(grandparent)) {
      replacementNode = $createListItemNode();
      grandparent.insertAfter(replacementNode);
    } else {
      return false;
    }
    replacementNode.select();
    const nextSiblings = anchor.getNextSiblings();
    if (nextSiblings.length > 0) {
      const newList = $createListNode(parent.getListType());
      if ($isParagraphNode$1(replacementNode)) {
        replacementNode.insertAfter(newList);
      } else {
        const newListItem = $createListItemNode();
        newListItem.append(newList);
        replacementNode.insertAfter(newListItem);
      }
      nextSiblings.forEach(sibling => {
        sibling.remove();
        newList.append(sibling);
      });
    }

    // Don't leave hanging nested empty lists
    $removeHighestEmptyListParent(anchor);
    return true;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function normalizeClassNames$2(...classNames) {
    const rval = [];
    for (const className of classNames) {
      if (className && typeof className === 'string') {
        for (const [s] of className.matchAll(/\S+/g)) {
          rval.push(s);
        }
      }
    }
    return rval;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class ListItemNode extends ElementNode$1 {
    /** @internal */

    /** @internal */

    static getType() {
      return 'listitem';
    }
    static clone(node) {
      return new ListItemNode(node.__value, node.__checked, node.__key);
    }
    constructor(value, checked, key) {
      super(key);
      this.__value = value === undefined ? 1 : value;
      this.__checked = checked;
    }
    createDOM(config) {
      const element = document.createElement('li');
      const parent = this.getParent();
      if ($isListNode(parent) && parent.getListType() === 'check') {
        updateListItemChecked(element, this, null);
      }
      element.value = this.__value;
      $setListItemThemeClassNames(element, config.theme, this);
      return element;
    }
    updateDOM(prevNode, dom, config) {
      const parent = this.getParent();
      if ($isListNode(parent) && parent.getListType() === 'check') {
        updateListItemChecked(dom, this, prevNode);
      }
      // @ts-expect-error - this is always HTMLListItemElement
      dom.value = this.__value;
      $setListItemThemeClassNames(dom, config.theme, this);
      return false;
    }
    static transform() {
      return node => {
        if (!$isListItemNode(node)) {
          throw Error(`node is not a ListItemNode`);
        }
        if (node.__checked == null) {
          return;
        }
        const parent = node.getParent();
        if ($isListNode(parent)) {
          if (parent.getListType() !== 'check' && node.getChecked() != null) {
            node.setChecked(undefined);
          }
        }
      };
    }
    static importDOM() {
      return {
        li: () => ({
          conversion: $convertListItemElement,
          priority: 0
        })
      };
    }
    static importJSON(serializedNode) {
      const node = $createListItemNode();
      node.setChecked(serializedNode.checked);
      node.setValue(serializedNode.value);
      node.setFormat(serializedNode.format);
      node.setDirection(serializedNode.direction);
      return node;
    }
    exportDOM(editor) {
      const element = this.createDOM(editor._config);
      element.style.textAlign = this.getFormatType();
      return {
        element
      };
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        checked: this.getChecked(),
        type: 'listitem',
        value: this.getValue(),
        version: 1
      };
    }
    append(...nodes) {
      for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        if ($isElementNode$1(node) && this.canMergeWith(node)) {
          const children = node.getChildren();
          this.append(...children);
          node.remove();
        } else {
          super.append(node);
        }
      }
      return this;
    }
    replace(replaceWithNode, includeChildren) {
      if ($isListItemNode(replaceWithNode)) {
        return super.replace(replaceWithNode);
      }
      this.setIndent(0);
      const list = this.getParentOrThrow();
      if (!$isListNode(list)) {
        return replaceWithNode;
      }
      if (list.__first === this.getKey()) {
        list.insertBefore(replaceWithNode);
      } else if (list.__last === this.getKey()) {
        list.insertAfter(replaceWithNode);
      } else {
        // Split the list
        const newList = $createListNode(list.getListType());
        let nextSibling = this.getNextSibling();
        while (nextSibling) {
          const nodeToAppend = nextSibling;
          nextSibling = nextSibling.getNextSibling();
          newList.append(nodeToAppend);
        }
        list.insertAfter(replaceWithNode);
        replaceWithNode.insertAfter(newList);
      }
      if (includeChildren) {
        if (!$isElementNode$1(replaceWithNode)) {
          throw Error(`includeChildren should only be true for ElementNodes`);
        }
        this.getChildren().forEach(child => {
          replaceWithNode.append(child);
        });
      }
      this.remove();
      if (list.getChildrenSize() === 0) {
        list.remove();
      }
      return replaceWithNode;
    }
    insertAfter(node, restoreSelection = true) {
      const listNode = this.getParentOrThrow();
      if (!$isListNode(listNode)) {
        {
          throw Error(`insertAfter: list node is not parent of list item node`);
        }
      }
      if ($isListItemNode(node)) {
        return super.insertAfter(node, restoreSelection);
      }
      const siblings = this.getNextSiblings();

      // Split the lists and insert the node in between them
      listNode.insertAfter(node, restoreSelection);
      if (siblings.length !== 0) {
        const newListNode = $createListNode(listNode.getListType());
        siblings.forEach(sibling => newListNode.append(sibling));
        node.insertAfter(newListNode, restoreSelection);
      }
      return node;
    }
    remove(preserveEmptyParent) {
      const prevSibling = this.getPreviousSibling();
      const nextSibling = this.getNextSibling();
      super.remove(preserveEmptyParent);
      if (prevSibling && nextSibling && isNestedListNode(prevSibling) && isNestedListNode(nextSibling)) {
        mergeLists(prevSibling.getFirstChild(), nextSibling.getFirstChild());
        nextSibling.remove();
      }
    }
    insertNewAfter(_, restoreSelection = true) {
      const newElement = $createListItemNode(this.__checked == null ? undefined : false);
      this.insertAfter(newElement, restoreSelection);
      return newElement;
    }
    collapseAtStart(selection) {
      const paragraph = $createParagraphNode$1();
      const children = this.getChildren();
      children.forEach(child => paragraph.append(child));
      const listNode = this.getParentOrThrow();
      const listNodeParent = listNode.getParentOrThrow();
      const isIndented = $isListItemNode(listNodeParent);
      if (listNode.getChildrenSize() === 1) {
        if (isIndented) {
          // if the list node is nested, we just want to remove it,
          // effectively unindenting it.
          listNode.remove();
          listNodeParent.select();
        } else {
          listNode.insertBefore(paragraph);
          listNode.remove();
          // If we have selection on the list item, we'll need to move it
          // to the paragraph
          const anchor = selection.anchor;
          const focus = selection.focus;
          const key = paragraph.getKey();
          if (anchor.type === 'element' && anchor.getNode().is(this)) {
            anchor.set(key, anchor.offset, 'element');
          }
          if (focus.type === 'element' && focus.getNode().is(this)) {
            focus.set(key, focus.offset, 'element');
          }
        }
      } else {
        listNode.insertBefore(paragraph);
        this.remove();
      }
      return true;
    }
    getValue() {
      const self = this.getLatest();
      return self.__value;
    }
    setValue(value) {
      const self = this.getWritable();
      self.__value = value;
    }
    getChecked() {
      const self = this.getLatest();
      let listType;
      const parent = this.getParent();
      if ($isListNode(parent)) {
        listType = parent.getListType();
      }
      return listType === 'check' ? Boolean(self.__checked) : undefined;
    }
    setChecked(checked) {
      const self = this.getWritable();
      self.__checked = checked;
    }
    toggleChecked() {
      this.setChecked(!this.__checked);
    }
    getIndent() {
      // If we don't have a parent, we are likely serializing
      const parent = this.getParent();
      if (parent === null) {
        return this.getLatest().__indent;
      }
      // ListItemNode should always have a ListNode for a parent.
      let listNodeParent = parent.getParentOrThrow();
      let indentLevel = 0;
      while ($isListItemNode(listNodeParent)) {
        listNodeParent = listNodeParent.getParentOrThrow().getParentOrThrow();
        indentLevel++;
      }
      return indentLevel;
    }
    setIndent(indent) {
      if (!(typeof indent === 'number')) {
        throw Error(`Invalid indent value.`);
      }
      indent = Math.floor(indent);
      if (!(indent >= 0)) {
        throw Error(`Indent value must be non-negative.`);
      }
      let currentIndent = this.getIndent();
      while (currentIndent !== indent) {
        if (currentIndent < indent) {
          $handleIndent(this);
          currentIndent++;
        } else {
          $handleOutdent(this);
          currentIndent--;
        }
      }
      return this;
    }

    /** @deprecated @internal */
    canInsertAfter(node) {
      return $isListItemNode(node);
    }

    /** @deprecated @internal */
    canReplaceWith(replacement) {
      return $isListItemNode(replacement);
    }
    canMergeWith(node) {
      return $isParagraphNode$1(node) || $isListItemNode(node);
    }
    extractWithChild(child, selection) {
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      const anchorNode = selection.anchor.getNode();
      const focusNode = selection.focus.getNode();
      return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && this.getTextContent().length === selection.getTextContent().length;
    }
    isParentRequired() {
      return true;
    }
    createParentElementNode() {
      return $createListNode('bullet');
    }
    canMergeWhenEmpty() {
      return true;
    }
  }
  function $setListItemThemeClassNames(dom, editorThemeClasses, node) {
    const classesToAdd = [];
    const classesToRemove = [];
    const listTheme = editorThemeClasses.list;
    const listItemClassName = listTheme ? listTheme.listitem : undefined;
    let nestedListItemClassName;
    if (listTheme && listTheme.nested) {
      nestedListItemClassName = listTheme.nested.listitem;
    }
    if (listItemClassName !== undefined) {
      classesToAdd.push(...normalizeClassNames$2(listItemClassName));
    }
    if (listTheme) {
      const parentNode = node.getParent();
      const isCheckList = $isListNode(parentNode) && parentNode.getListType() === 'check';
      const checked = node.getChecked();
      if (!isCheckList || checked) {
        classesToRemove.push(listTheme.listitemUnchecked);
      }
      if (!isCheckList || !checked) {
        classesToRemove.push(listTheme.listitemChecked);
      }
      if (isCheckList) {
        classesToAdd.push(checked ? listTheme.listitemChecked : listTheme.listitemUnchecked);
      }
    }
    if (nestedListItemClassName !== undefined) {
      const nestedListItemClasses = normalizeClassNames$2(nestedListItemClassName);
      if (node.getChildren().some(child => $isListNode(child))) {
        classesToAdd.push(...nestedListItemClasses);
      } else {
        classesToRemove.push(...nestedListItemClasses);
      }
    }
    if (classesToRemove.length > 0) {
      removeClassNamesFromElement$1(dom, ...classesToRemove);
    }
    if (classesToAdd.length > 0) {
      addClassNamesToElement$1(dom, ...classesToAdd);
    }
  }
  function updateListItemChecked(dom, listItemNode, prevListItemNode, listNode) {
    // Only add attributes for leaf list items
    if ($isListNode(listItemNode.getFirstChild())) {
      dom.removeAttribute('role');
      dom.removeAttribute('tabIndex');
      dom.removeAttribute('aria-checked');
    } else {
      dom.setAttribute('role', 'checkbox');
      dom.setAttribute('tabIndex', '-1');
      if (!prevListItemNode || listItemNode.__checked !== prevListItemNode.__checked) {
        dom.setAttribute('aria-checked', listItemNode.getChecked() ? 'true' : 'false');
      }
    }
  }
  function $convertListItemElement(domNode) {
    const isGitHubCheckList = domNode.classList.contains('task-list-item');
    if (isGitHubCheckList) {
      for (const child of domNode.children) {
        if (child.tagName === 'INPUT') {
          return $convertCheckboxInput(child);
        }
      }
    }
    const ariaCheckedAttr = domNode.getAttribute('aria-checked');
    const checked = ariaCheckedAttr === 'true' ? true : ariaCheckedAttr === 'false' ? false : undefined;
    return {
      node: $createListItemNode(checked)
    };
  }
  function $convertCheckboxInput(domNode) {
    const isCheckboxInput = domNode.getAttribute('type') === 'checkbox';
    if (!isCheckboxInput) {
      return {
        node: null
      };
    }
    const checked = domNode.hasAttribute('checked');
    return {
      node: $createListItemNode(checked)
    };
  }

  /**
   * Creates a new List Item node, passing true/false will convert it to a checkbox input.
   * @param checked - Is the List Item a checkbox and, if so, is it checked? undefined/null: not a checkbox, true/false is a checkbox and checked/unchecked, respectively.
   * @returns The new List Item.
   */
  function $createListItemNode(checked) {
    return $applyNodeReplacement$1(new ListItemNode(undefined, checked));
  }

  /**
   * Checks to see if the node is a ListItemNode.
   * @param node - The node to be checked.
   * @returns true if the node is a ListItemNode, false otherwise.
   */
  function $isListItemNode(node) {
    return node instanceof ListItemNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class ListNode extends ElementNode$1 {
    /** @internal */

    /** @internal */

    /** @internal */

    static getType() {
      return 'list';
    }
    static clone(node) {
      const listType = node.__listType || TAG_TO_LIST_TYPE[node.__tag];
      return new ListNode(listType, node.__start, node.__key);
    }
    constructor(listType, start, key) {
      super(key);
      const _listType = TAG_TO_LIST_TYPE[listType] || listType;
      this.__listType = _listType;
      this.__tag = _listType === 'number' ? 'ol' : 'ul';
      this.__start = start;
    }
    getTag() {
      return this.__tag;
    }
    setListType(type) {
      const writable = this.getWritable();
      writable.__listType = type;
      writable.__tag = type === 'number' ? 'ol' : 'ul';
    }
    getListType() {
      return this.__listType;
    }
    getStart() {
      return this.__start;
    }

    // View

    createDOM(config, _editor) {
      const tag = this.__tag;
      const dom = document.createElement(tag);
      if (this.__start !== 1) {
        dom.setAttribute('start', String(this.__start));
      }
      // @ts-expect-error Internal field.
      dom.__lexicalListType = this.__listType;
      $setListThemeClassNames(dom, config.theme, this);
      return dom;
    }
    updateDOM(prevNode, dom, config) {
      if (prevNode.__tag !== this.__tag) {
        return true;
      }
      $setListThemeClassNames(dom, config.theme, this);
      return false;
    }
    static transform() {
      return node => {
        if (!$isListNode(node)) {
          throw Error(`node is not a ListNode`);
        }
        mergeNextSiblingListIfSameType(node);
        updateChildrenListItemValue(node);
      };
    }
    static importDOM() {
      return {
        ol: () => ({
          conversion: $convertListNode,
          priority: 0
        }),
        ul: () => ({
          conversion: $convertListNode,
          priority: 0
        })
      };
    }
    static importJSON(serializedNode) {
      const node = $createListNode(serializedNode.listType, serializedNode.start);
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      return node;
    }
    exportDOM(editor) {
      const {
        element
      } = super.exportDOM(editor);
      if (element && isHTMLElement$2(element)) {
        if (this.__start !== 1) {
          element.setAttribute('start', String(this.__start));
        }
        if (this.__listType === 'check') {
          element.setAttribute('__lexicalListType', 'check');
        }
      }
      return {
        element
      };
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        listType: this.getListType(),
        start: this.getStart(),
        tag: this.getTag(),
        type: 'list',
        version: 1
      };
    }
    canBeEmpty() {
      return false;
    }
    canIndent() {
      return false;
    }
    append(...nodesToAppend) {
      for (let i = 0; i < nodesToAppend.length; i++) {
        const currentNode = nodesToAppend[i];
        if ($isListItemNode(currentNode)) {
          super.append(currentNode);
        } else {
          const listItemNode = $createListItemNode();
          if ($isListNode(currentNode)) {
            listItemNode.append(currentNode);
          } else if ($isElementNode$1(currentNode)) {
            const textNode = $createTextNode$1(currentNode.getTextContent());
            listItemNode.append(textNode);
          } else {
            listItemNode.append(currentNode);
          }
          super.append(listItemNode);
        }
      }
      return this;
    }
    extractWithChild(child) {
      return $isListItemNode(child);
    }
  }
  function $setListThemeClassNames(dom, editorThemeClasses, node) {
    const classesToAdd = [];
    const classesToRemove = [];
    const listTheme = editorThemeClasses.list;
    if (listTheme !== undefined) {
      const listLevelsClassNames = listTheme[`${node.__tag}Depth`] || [];
      const listDepth = $getListDepth(node) - 1;
      const normalizedListDepth = listDepth % listLevelsClassNames.length;
      const listLevelClassName = listLevelsClassNames[normalizedListDepth];
      const listClassName = listTheme[node.__tag];
      let nestedListClassName;
      const nestedListTheme = listTheme.nested;
      const checklistClassName = listTheme.checklist;
      if (nestedListTheme !== undefined && nestedListTheme.list) {
        nestedListClassName = nestedListTheme.list;
      }
      if (listClassName !== undefined) {
        classesToAdd.push(listClassName);
      }
      if (checklistClassName !== undefined && node.__listType === 'check') {
        classesToAdd.push(checklistClassName);
      }
      if (listLevelClassName !== undefined) {
        classesToAdd.push(...normalizeClassNames$2(listLevelClassName));
        for (let i = 0; i < listLevelsClassNames.length; i++) {
          if (i !== normalizedListDepth) {
            classesToRemove.push(node.__tag + i);
          }
        }
      }
      if (nestedListClassName !== undefined) {
        const nestedListItemClasses = normalizeClassNames$2(nestedListClassName);
        if (listDepth > 1) {
          classesToAdd.push(...nestedListItemClasses);
        } else {
          classesToRemove.push(...nestedListItemClasses);
        }
      }
    }
    if (classesToRemove.length > 0) {
      removeClassNamesFromElement$1(dom, ...classesToRemove);
    }
    if (classesToAdd.length > 0) {
      addClassNamesToElement$1(dom, ...classesToAdd);
    }
  }

  /*
   * This function normalizes the children of a ListNode after the conversion from HTML,
   * ensuring that they are all ListItemNodes and contain either a single nested ListNode
   * or some other inline content.
   */
  function $normalizeChildren(nodes) {
    const normalizedListItems = [];
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if ($isListItemNode(node)) {
        normalizedListItems.push(node);
        const children = node.getChildren();
        if (children.length > 1) {
          children.forEach(child => {
            if ($isListNode(child)) {
              normalizedListItems.push($wrapInListItem(child));
            }
          });
        }
      } else {
        normalizedListItems.push($wrapInListItem(node));
      }
    }
    return normalizedListItems;
  }
  function isDomChecklist(domNode) {
    if (domNode.getAttribute('__lexicallisttype') === 'check' ||
    // is github checklist
    domNode.classList.contains('contains-task-list')) {
      return true;
    }
    // if children are checklist items, the node is a checklist ul. Applicable for googledoc checklist pasting.
    for (const child of domNode.childNodes) {
      if (isHTMLElement$2(child) && child.hasAttribute('aria-checked')) {
        return true;
      }
    }
    return false;
  }
  function $convertListNode(domNode) {
    const nodeName = domNode.nodeName.toLowerCase();
    let node = null;
    if (nodeName === 'ol') {
      // @ts-ignore
      const start = domNode.start;
      node = $createListNode('number', start);
    } else if (nodeName === 'ul') {
      if (isDomChecklist(domNode)) {
        node = $createListNode('check');
      } else {
        node = $createListNode('bullet');
      }
    }
    return {
      after: $normalizeChildren,
      node
    };
  }
  const TAG_TO_LIST_TYPE = {
    ol: 'number',
    ul: 'bullet'
  };

  /**
   * Creates a ListNode of listType.
   * @param listType - The type of list to be created. Can be 'number', 'bullet', or 'check'.
   * @param start - Where an ordered list starts its count, start = 1 if left undefined.
   * @returns The new ListNode
   */
  function $createListNode(listType, start = 1) {
    return $applyNodeReplacement$1(new ListNode(listType, start));
  }

  /**
   * Checks to see if the node is a ListNode.
   * @param node - The node to be checked.
   * @returns true if the node is a ListNode, false otherwise.
   */
  function $isListNode(node) {
    return node instanceof ListNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const INSERT_UNORDERED_LIST_COMMAND = createCommand$1('INSERT_UNORDERED_LIST_COMMAND');
  const INSERT_ORDERED_LIST_COMMAND = createCommand$1('INSERT_ORDERED_LIST_COMMAND');
  const INSERT_CHECK_LIST_COMMAND = createCommand$1('INSERT_CHECK_LIST_COMMAND');
  const REMOVE_LIST_COMMAND = createCommand$1('REMOVE_LIST_COMMAND');

  var modDev$4 = /*#__PURE__*/Object.freeze({
    $createListItemNode: $createListItemNode,
    $createListNode: $createListNode,
    $getListDepth: $getListDepth,
    $handleListInsertParagraph: $handleListInsertParagraph,
    $isListItemNode: $isListItemNode,
    $isListNode: $isListNode,
    INSERT_CHECK_LIST_COMMAND: INSERT_CHECK_LIST_COMMAND,
    INSERT_ORDERED_LIST_COMMAND: INSERT_ORDERED_LIST_COMMAND,
    INSERT_UNORDERED_LIST_COMMAND: INSERT_UNORDERED_LIST_COMMAND,
    ListItemNode: ListItemNode,
    ListNode: ListNode,
    REMOVE_LIST_COMMAND: REMOVE_LIST_COMMAND,
    insertList: insertList,
    removeList: removeList
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  function p$3(e) {
    return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
  }
  var _$3 = p$3(function (e) {
    const t = new URLSearchParams();
    t.append("code", e);
    for (let e = 1; e < arguments.length; e++) t.append("v", arguments[e]);
    throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });
  const j$3 = createCommand$1("INSERT_UNORDERED_LIST_COMMAND"),
    q$2 = createCommand$1("INSERT_ORDERED_LIST_COMMAND"),
    H$3 = createCommand$1("INSERT_CHECK_LIST_COMMAND"),
    G$2 = createCommand$1("REMOVE_LIST_COMMAND");

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$4 = modDev$4;
  const $createListItemNode$1 = mod$4.$createListItemNode;
  const $createListNode$1 = mod$4.$createListNode;
  const $getListDepth$1 = mod$4.$getListDepth;
  const $handleListInsertParagraph$1 = mod$4.$handleListInsertParagraph;
  const $isListItemNode$1 = mod$4.$isListItemNode;
  const $isListNode$1 = mod$4.$isListNode;
  const INSERT_CHECK_LIST_COMMAND$1 = mod$4.INSERT_CHECK_LIST_COMMAND;
  const INSERT_ORDERED_LIST_COMMAND$1 = mod$4.INSERT_ORDERED_LIST_COMMAND;
  const INSERT_UNORDERED_LIST_COMMAND$1 = mod$4.INSERT_UNORDERED_LIST_COMMAND;
  const ListItemNode$1 = mod$4.ListItemNode;
  const ListNode$1 = mod$4.ListNode;
  const REMOVE_LIST_COMMAND$1 = mod$4.REMOVE_LIST_COMMAND;
  const insertList$1 = mod$4.insertList;
  const removeList$1 = mod$4.removeList;

  var LexicalList = /*#__PURE__*/Object.freeze({
    $createListItemNode: $createListItemNode$1,
    $createListNode: $createListNode$1,
    $getListDepth: $getListDepth$1,
    $handleListInsertParagraph: $handleListInsertParagraph$1,
    $isListItemNode: $isListItemNode$1,
    $isListNode: $isListNode$1,
    INSERT_CHECK_LIST_COMMAND: INSERT_CHECK_LIST_COMMAND$1,
    INSERT_ORDERED_LIST_COMMAND: INSERT_ORDERED_LIST_COMMAND$1,
    INSERT_UNORDERED_LIST_COMMAND: INSERT_UNORDERED_LIST_COMMAND$1,
    ListItemNode: ListItemNode$1,
    ListNode: ListNode$1,
    REMOVE_LIST_COMMAND: REMOVE_LIST_COMMAND$1,
    insertList: insertList$1,
    removeList: removeList$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const SUPPORTED_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'sms:', 'tel:']);

  /** @noInheritDoc */
  class LinkNode extends ElementNode$1 {
    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    static getType() {
      return 'link';
    }
    static clone(node) {
      return new LinkNode(node.__url, {
        rel: node.__rel,
        target: node.__target,
        title: node.__title
      }, node.__key);
    }
    constructor(url, attributes = {}, key) {
      super(key);
      const {
        target = null,
        rel = null,
        title = null
      } = attributes;
      this.__url = url;
      this.__target = target;
      this.__rel = rel;
      this.__title = title;
    }
    createDOM(config) {
      const element = document.createElement('a');
      element.href = this.sanitizeUrl(this.__url);
      if (this.__target !== null) {
        element.target = this.__target;
      }
      if (this.__rel !== null) {
        element.rel = this.__rel;
      }
      if (this.__title !== null) {
        element.title = this.__title;
      }
      addClassNamesToElement$1(element, config.theme.link);
      return element;
    }
    updateDOM(prevNode, anchor, config) {
      if (anchor instanceof HTMLAnchorElement) {
        const url = this.__url;
        const target = this.__target;
        const rel = this.__rel;
        const title = this.__title;
        if (url !== prevNode.__url) {
          anchor.href = url;
        }
        if (target !== prevNode.__target) {
          if (target) {
            anchor.target = target;
          } else {
            anchor.removeAttribute('target');
          }
        }
        if (rel !== prevNode.__rel) {
          if (rel) {
            anchor.rel = rel;
          } else {
            anchor.removeAttribute('rel');
          }
        }
        if (title !== prevNode.__title) {
          if (title) {
            anchor.title = title;
          } else {
            anchor.removeAttribute('title');
          }
        }
      }
      return false;
    }
    static importDOM() {
      return {
        a: node => ({
          conversion: $convertAnchorElement,
          priority: 1
        })
      };
    }
    static importJSON(serializedNode) {
      const node = $createLinkNode(serializedNode.url, {
        rel: serializedNode.rel,
        target: serializedNode.target,
        title: serializedNode.title
      });
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      return node;
    }
    sanitizeUrl(url) {
      try {
        const parsedUrl = new URL(url);
        // eslint-disable-next-line no-script-url
        if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) {
          return 'about:blank';
        }
      } catch (_unused) {
        return url;
      }
      return url;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        rel: this.getRel(),
        target: this.getTarget(),
        title: this.getTitle(),
        type: 'link',
        url: this.getURL(),
        version: 1
      };
    }
    getURL() {
      return this.getLatest().__url;
    }
    setURL(url) {
      const writable = this.getWritable();
      writable.__url = url;
    }
    getTarget() {
      return this.getLatest().__target;
    }
    setTarget(target) {
      const writable = this.getWritable();
      writable.__target = target;
    }
    getRel() {
      return this.getLatest().__rel;
    }
    setRel(rel) {
      const writable = this.getWritable();
      writable.__rel = rel;
    }
    getTitle() {
      return this.getLatest().__title;
    }
    setTitle(title) {
      const writable = this.getWritable();
      writable.__title = title;
    }
    insertNewAfter(_, restoreSelection = true) {
      const linkNode = $createLinkNode(this.__url, {
        rel: this.__rel,
        target: this.__target,
        title: this.__title
      });
      this.insertAfter(linkNode, restoreSelection);
      return linkNode;
    }
    canInsertTextBefore() {
      return false;
    }
    canInsertTextAfter() {
      return false;
    }
    canBeEmpty() {
      return false;
    }
    isInline() {
      return true;
    }
    extractWithChild(child, selection, destination) {
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      const anchorNode = selection.anchor.getNode();
      const focusNode = selection.focus.getNode();
      return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && selection.getTextContent().length > 0;
    }
    isEmailURI() {
      return this.__url.startsWith('mailto:');
    }
    isWebSiteURI() {
      return this.__url.startsWith('https://') || this.__url.startsWith('http://');
    }
  }
  function $convertAnchorElement(domNode) {
    let node = null;
    if (isHTMLAnchorElement$2(domNode)) {
      const content = domNode.textContent;
      if (content !== null && content !== '' || domNode.children.length > 0) {
        node = $createLinkNode(domNode.getAttribute('href') || '', {
          rel: domNode.getAttribute('rel'),
          target: domNode.getAttribute('target'),
          title: domNode.getAttribute('title')
        });
      }
    }
    return {
      node
    };
  }

  /**
   * Takes a URL and creates a LinkNode.
   * @param url - The URL the LinkNode should direct to.
   * @param attributes - Optional HTML a tag attributes \\{ target, rel, title \\}
   * @returns The LinkNode.
   */
  function $createLinkNode(url, attributes) {
    return $applyNodeReplacement$1(new LinkNode(url, attributes));
  }

  /**
   * Determines if node is a LinkNode.
   * @param node - The node to be checked.
   * @returns true if node is a LinkNode, false otherwise.
   */
  function $isLinkNode(node) {
    return node instanceof LinkNode;
  }
  // Custom node type to override `canInsertTextAfter` that will
  // allow typing within the link
  class AutoLinkNode extends LinkNode {
    /** @internal */
    /** Indicates whether the autolink was ever unlinked. **/

    constructor(url, attributes = {}, key) {
      super(url, attributes, key);
      this.__isUnlinked = attributes.isUnlinked !== undefined && attributes.isUnlinked !== null ? attributes.isUnlinked : false;
    }
    static getType() {
      return 'autolink';
    }
    static clone(node) {
      return new AutoLinkNode(node.__url, {
        isUnlinked: node.__isUnlinked,
        rel: node.__rel,
        target: node.__target,
        title: node.__title
      }, node.__key);
    }
    getIsUnlinked() {
      return this.__isUnlinked;
    }
    setIsUnlinked(value) {
      const self = this.getWritable();
      self.__isUnlinked = value;
      return self;
    }
    createDOM(config) {
      if (this.__isUnlinked) {
        return document.createElement('span');
      } else {
        return super.createDOM(config);
      }
    }
    updateDOM(prevNode, anchor, config) {
      return super.updateDOM(prevNode, anchor, config) || prevNode.__isUnlinked !== this.__isUnlinked;
    }
    static importJSON(serializedNode) {
      const node = $createAutoLinkNode(serializedNode.url, {
        isUnlinked: serializedNode.isUnlinked,
        rel: serializedNode.rel,
        target: serializedNode.target,
        title: serializedNode.title
      });
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      return node;
    }
    static importDOM() {
      // TODO: Should link node should handle the import over autolink?
      return null;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        isUnlinked: this.__isUnlinked,
        type: 'autolink',
        version: 1
      };
    }
    insertNewAfter(selection, restoreSelection = true) {
      const element = this.getParentOrThrow().insertNewAfter(selection, restoreSelection);
      if ($isElementNode$1(element)) {
        const linkNode = $createAutoLinkNode(this.__url, {
          isUnlinked: this.__isUnlinked,
          rel: this.__rel,
          target: this.__target,
          title: this.__title
        });
        element.append(linkNode);
        return linkNode;
      }
      return null;
    }
  }

  /**
   * Takes a URL and creates an AutoLinkNode. AutoLinkNodes are generally automatically generated
   * during typing, which is especially useful when a button to generate a LinkNode is not practical.
   * @param url - The URL the LinkNode should direct to.
   * @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
   * @returns The LinkNode.
   */
  function $createAutoLinkNode(url, attributes) {
    return $applyNodeReplacement$1(new AutoLinkNode(url, attributes));
  }

  /**
   * Determines if node is an AutoLinkNode.
   * @param node - The node to be checked.
   * @returns true if node is an AutoLinkNode, false otherwise.
   */
  function $isAutoLinkNode(node) {
    return node instanceof AutoLinkNode;
  }
  const TOGGLE_LINK_COMMAND = createCommand$1('TOGGLE_LINK_COMMAND');

  /**
   * Generates or updates a LinkNode. It can also delete a LinkNode if the URL is null,
   * but saves any children and brings them up to the parent node.
   * @param url - The URL the link directs to.
   * @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
   */
  function $toggleLink(url, attributes = {}) {
    const {
      target,
      title
    } = attributes;
    const rel = attributes.rel === undefined ? 'noreferrer' : attributes.rel;
    const selection = $getSelection$1();
    if (!$isRangeSelection$1(selection)) {
      return;
    }
    const nodes = selection.extract();
    if (url === null) {
      // Remove LinkNodes
      nodes.forEach(node => {
        const parent = node.getParent();
        if (!$isAutoLinkNode(parent) && $isLinkNode(parent)) {
          const children = parent.getChildren();
          for (let i = 0; i < children.length; i++) {
            parent.insertBefore(children[i]);
          }
          parent.remove();
        }
      });
    } else {
      // Add or merge LinkNodes
      if (nodes.length === 1) {
        const firstNode = nodes[0];
        // if the first node is a LinkNode or if its
        // parent is a LinkNode, we update the URL, target and rel.
        const linkNode = $getAncestor$2(firstNode, $isLinkNode);
        if (linkNode !== null) {
          linkNode.setURL(url);
          if (target !== undefined) {
            linkNode.setTarget(target);
          }
          if (rel !== null) {
            linkNode.setRel(rel);
          }
          if (title !== undefined) {
            linkNode.setTitle(title);
          }
          return;
        }
      }
      let prevParent = null;
      let linkNode = null;
      nodes.forEach(node => {
        const parent = node.getParent();
        if (parent === linkNode || parent === null || $isElementNode$1(node) && !node.isInline()) {
          return;
        }
        if ($isLinkNode(parent)) {
          linkNode = parent;
          parent.setURL(url);
          if (target !== undefined) {
            parent.setTarget(target);
          }
          if (rel !== null) {
            linkNode.setRel(rel);
          }
          if (title !== undefined) {
            linkNode.setTitle(title);
          }
          return;
        }
        if (!parent.is(prevParent)) {
          prevParent = parent;
          linkNode = $createLinkNode(url, {
            rel,
            target,
            title
          });
          if ($isLinkNode(parent)) {
            if (node.getPreviousSibling() === null) {
              parent.insertBefore(linkNode);
            } else {
              parent.insertAfter(linkNode);
            }
          } else {
            node.insertBefore(linkNode);
          }
        }
        if ($isLinkNode(node)) {
          if (node.is(linkNode)) {
            return;
          }
          if (linkNode !== null) {
            const children = node.getChildren();
            for (let i = 0; i < children.length; i++) {
              linkNode.append(children[i]);
            }
          }
          node.remove();
          return;
        }
        if (linkNode !== null) {
          linkNode.append(node);
        }
      });
    }
  }
  /** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
  const toggleLink = $toggleLink;
  function $getAncestor$2(node, predicate) {
    let parent = node;
    while (parent !== null && parent.getParent() !== null && !predicate(parent)) {
      parent = parent.getParentOrThrow();
    }
    return predicate(parent) ? parent : null;
  }

  var modDev$5 = /*#__PURE__*/Object.freeze({
    $createAutoLinkNode: $createAutoLinkNode,
    $createLinkNode: $createLinkNode,
    $isAutoLinkNode: $isAutoLinkNode,
    $isLinkNode: $isLinkNode,
    $toggleLink: $toggleLink,
    AutoLinkNode: AutoLinkNode,
    LinkNode: LinkNode,
    TOGGLE_LINK_COMMAND: TOGGLE_LINK_COMMAND,
    toggleLink: toggleLink
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const p$4 = createCommand$1("TOGGLE_LINK_COMMAND");

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$5 = modDev$5;
  const $createAutoLinkNode$1 = mod$5.$createAutoLinkNode;
  const $createLinkNode$1 = mod$5.$createLinkNode;
  const $isAutoLinkNode$1 = mod$5.$isAutoLinkNode;
  const $isLinkNode$1 = mod$5.$isLinkNode;
  const $toggleLink$1 = mod$5.$toggleLink;
  const AutoLinkNode$1 = mod$5.AutoLinkNode;
  const LinkNode$1 = mod$5.LinkNode;
  const TOGGLE_LINK_COMMAND$1 = mod$5.TOGGLE_LINK_COMMAND;
  const toggleLink$1 = mod$5.toggleLink;

  var LexicalLink = /*#__PURE__*/Object.freeze({
    $createAutoLinkNode: $createAutoLinkNode$1,
    $createLinkNode: $createLinkNode$1,
    $isAutoLinkNode: $isAutoLinkNode$1,
    $isLinkNode: $isLinkNode$1,
    $toggleLink: $toggleLink$1,
    AutoLinkNode: AutoLinkNode$1,
    LinkNode: LinkNode$1,
    TOGGLE_LINK_COMMAND: TOGGLE_LINK_COMMAND$1,
    toggleLink: toggleLink$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const CAN_USE_DOM$4 = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const getDOMSelection$1 = targetWindow => CAN_USE_DOM$4 ? (targetWindow || window).getSelection() : null;
  /**
   * Returns the *currently selected* Lexical content as an HTML string, relying on the
   * logic defined in the exportDOM methods on the LexicalNode classes. Note that
   * this will not return the HTML content of the entire editor (unless all the content is included
   * in the current selection).
   *
   * @param editor - LexicalEditor instance to get HTML content from
   * @param selection - The selection to use (default is $getSelection())
   * @returns a string of HTML content
   */
  function $getHtmlContent(editor, selection = $getSelection$1()) {
    if (selection == null) {
      {
        throw Error(`Expected valid LexicalSelection`);
      }
    }

    // If we haven't selected anything
    if ($isRangeSelection$1(selection) && selection.isCollapsed() || selection.getNodes().length === 0) {
      return '';
    }
    return $generateHtmlFromNodes$1(editor, selection);
  }

  /**
   * Returns the *currently selected* Lexical content as a JSON string, relying on the
   * logic defined in the exportJSON methods on the LexicalNode classes. Note that
   * this will not return the JSON content of the entire editor (unless all the content is included
   * in the current selection).
   *
   * @param editor  - LexicalEditor instance to get the JSON content from
   * @param selection - The selection to use (default is $getSelection())
   * @returns
   */
  function $getLexicalContent(editor, selection = $getSelection$1()) {
    if (selection == null) {
      {
        throw Error(`Expected valid LexicalSelection`);
      }
    }

    // If we haven't selected anything
    if ($isRangeSelection$1(selection) && selection.isCollapsed() || selection.getNodes().length === 0) {
      return null;
    }
    return JSON.stringify($generateJSONFromSelectedNodes(editor, selection));
  }

  /**
   * Attempts to insert content of the mime-types text/plain or text/uri-list from
   * the provided DataTransfer object into the editor at the provided selection.
   * text/uri-list is only used if text/plain is not also provided.
   *
   * @param dataTransfer an object conforming to the [DataTransfer interface] (https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface)
   * @param selection the selection to use as the insertion point for the content in the DataTransfer object
   */
  function $insertDataTransferForPlainText(dataTransfer, selection) {
    const text = dataTransfer.getData('text/plain') || dataTransfer.getData('text/uri-list');
    if (text != null) {
      selection.insertRawText(text);
    }
  }

  /**
   * Attempts to insert content of the mime-types application/x-lexical-editor, text/html,
   * text/plain, or text/uri-list (in descending order of priority) from the provided DataTransfer
   * object into the editor at the provided selection.
   *
   * @param dataTransfer an object conforming to the [DataTransfer interface] (https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface)
   * @param selection the selection to use as the insertion point for the content in the DataTransfer object
   * @param editor the LexicalEditor the content is being inserted into.
   */
  function $insertDataTransferForRichText(dataTransfer, selection, editor) {
    const lexicalString = dataTransfer.getData('application/x-lexical-editor');
    if (lexicalString) {
      try {
        const payload = JSON.parse(lexicalString);
        if (payload.namespace === editor._config.namespace && Array.isArray(payload.nodes)) {
          const nodes = $generateNodesFromSerializedNodes(payload.nodes);
          return $insertGeneratedNodes(editor, nodes, selection);
        }
      } catch (_unused) {
        // Fail silently.
      }
    }
    const htmlString = dataTransfer.getData('text/html');
    if (htmlString) {
      try {
        const parser = new DOMParser();
        const dom = parser.parseFromString(htmlString, 'text/html');
        const nodes = $generateNodesFromDOM$1(editor, dom);
        return $insertGeneratedNodes(editor, nodes, selection);
      } catch (_unused2) {
        // Fail silently.
      }
    }

    // Multi-line plain text in rich text mode pasted as separate paragraphs
    // instead of single paragraph with linebreaks.
    // Webkit-specific: Supports read 'text/uri-list' in clipboard.
    const text = dataTransfer.getData('text/plain') || dataTransfer.getData('text/uri-list');
    if (text != null) {
      if ($isRangeSelection$1(selection)) {
        const parts = text.split(/(\r?\n|\t)/);
        if (parts[parts.length - 1] === '') {
          parts.pop();
        }
        for (let i = 0; i < parts.length; i++) {
          const currentSelection = $getSelection$1();
          if ($isRangeSelection$1(currentSelection)) {
            const part = parts[i];
            if (part === '\n' || part === '\r\n') {
              currentSelection.insertParagraph();
            } else if (part === '\t') {
              currentSelection.insertNodes([$createTabNode$1()]);
            } else {
              currentSelection.insertText(part);
            }
          }
        }
      } else {
        selection.insertRawText(text);
      }
    }
  }

  /**
   * Inserts Lexical nodes into the editor using different strategies depending on
   * some simple selection-based heuristics. If you're looking for a generic way to
   * to insert nodes into the editor at a specific selection point, you probably want
   * {@link lexical.$insertNodes}
   *
   * @param editor LexicalEditor instance to insert the nodes into.
   * @param nodes The nodes to insert.
   * @param selection The selection to insert the nodes into.
   */
  function $insertGeneratedNodes(editor, nodes, selection) {
    if (!editor.dispatchCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND$1, {
      nodes,
      selection
    })) {
      selection.insertNodes(nodes);
    }
    return;
  }
  function exportNodeToJSON$1(node) {
    const serializedNode = node.exportJSON();
    const nodeClass = node.constructor;
    if (serializedNode.type !== nodeClass.getType()) {
      {
        throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .exportJSON().`);
      }
    }
    if ($isElementNode$1(node)) {
      const serializedChildren = serializedNode.children;
      if (!Array.isArray(serializedChildren)) {
        {
          throw Error(`LexicalNode: Node ${nodeClass.name} is an element but .exportJSON() does not have a children array.`);
        }
      }
    }
    return serializedNode;
  }
  function $appendNodesToJSON(editor, selection, currentNode, targetArray = []) {
    let shouldInclude = selection !== null ? currentNode.isSelected(selection) : true;
    const shouldExclude = $isElementNode$1(currentNode) && currentNode.excludeFromCopy('html');
    let target = currentNode;
    if (selection !== null) {
      let clone = $cloneWithProperties$1(currentNode);
      clone = $isTextNode$1(clone) && selection !== null ? $sliceSelectedTextNodeContent$1(selection, clone) : clone;
      target = clone;
    }
    const children = $isElementNode$1(target) ? target.getChildren() : [];
    const serializedNode = exportNodeToJSON$1(target);

    // TODO: TextNode calls getTextContent() (NOT node.__text) within its exportJSON method
    // which uses getLatest() to get the text from the original node with the same key.
    // This is a deeper issue with the word "clone" here, it's still a reference to the
    // same node as far as the LexicalEditor is concerned since it shares a key.
    // We need a way to create a clone of a Node in memory with its own key, but
    // until then this hack will work for the selected text extract use case.
    if ($isTextNode$1(target)) {
      const text = target.__text;
      // If an uncollapsed selection ends or starts at the end of a line of specialized,
      // TextNodes, such as code tokens, we will get a 'blank' TextNode here, i.e., one
      // with text of length 0. We don't want this, it makes a confusing mess. Reset!
      if (text.length > 0) {
        serializedNode.text = text;
      } else {
        shouldInclude = false;
      }
    }
    for (let i = 0; i < children.length; i++) {
      const childNode = children[i];
      const shouldIncludeChild = $appendNodesToJSON(editor, selection, childNode, serializedNode.children);
      if (!shouldInclude && $isElementNode$1(currentNode) && shouldIncludeChild && currentNode.extractWithChild(childNode, selection, 'clone')) {
        shouldInclude = true;
      }
    }
    if (shouldInclude && !shouldExclude) {
      targetArray.push(serializedNode);
    } else if (Array.isArray(serializedNode.children)) {
      for (let i = 0; i < serializedNode.children.length; i++) {
        const serializedChildNode = serializedNode.children[i];
        targetArray.push(serializedChildNode);
      }
    }
    return shouldInclude;
  }

  // TODO why $ function with Editor instance?
  /**
   * Gets the Lexical JSON of the nodes inside the provided Selection.
   *
   * @param editor LexicalEditor to get the JSON content from.
   * @param selection Selection to get the JSON content from.
   * @returns an object with the editor namespace and a list of serializable nodes as JavaScript objects.
   */
  function $generateJSONFromSelectedNodes(editor, selection) {
    const nodes = [];
    const root = $getRoot$1();
    const topLevelChildren = root.getChildren();
    for (let i = 0; i < topLevelChildren.length; i++) {
      const topLevelNode = topLevelChildren[i];
      $appendNodesToJSON(editor, selection, topLevelNode, nodes);
    }
    return {
      namespace: editor._config.namespace,
      nodes
    };
  }

  /**
   * This method takes an array of objects conforming to the BaseSeralizedNode interface and returns
   * an Array containing instances of the corresponding LexicalNode classes registered on the editor.
   * Normally, you'd get an Array of BaseSerialized nodes from {@link $generateJSONFromSelectedNodes}
   *
   * @param serializedNodes an Array of objects conforming to the BaseSerializedNode interface.
   * @returns an Array of Lexical Node objects.
   */
  function $generateNodesFromSerializedNodes(serializedNodes) {
    const nodes = [];
    for (let i = 0; i < serializedNodes.length; i++) {
      const serializedNode = serializedNodes[i];
      const node = $parseSerializedNode$1(serializedNode);
      if ($isTextNode$1(node)) {
        $addNodeStyle$1(node);
      }
      nodes.push(node);
    }
    return nodes;
  }
  const EVENT_LATENCY = 50;
  let clipboardEventTimeout = null;

  // TODO custom selection
  // TODO potentially have a node customizable version for plain text
  /**
   * Copies the content of the current selection to the clipboard in
   * text/plain, text/html, and application/x-lexical-editor (Lexical JSON)
   * formats.
   *
   * @param editor the LexicalEditor instance to copy content from
   * @param event the native browser ClipboardEvent to add the content to.
   * @returns
   */
  async function copyToClipboard(editor, event, data) {
    if (clipboardEventTimeout !== null) {
      // Prevent weird race conditions that can happen when this function is run multiple times
      // synchronously. In the future, we can do better, we can cancel/override the previously running job.
      return false;
    }
    if (event !== null) {
      return new Promise((resolve, reject) => {
        editor.update(() => {
          resolve($copyToClipboardEvent(editor, event, data));
        });
      });
    }
    const rootElement = editor.getRootElement();
    const windowDocument = editor._window == null ? window.document : editor._window.document;
    const domSelection = getDOMSelection$1(editor._window);
    if (rootElement === null || domSelection === null) {
      return false;
    }
    const element = windowDocument.createElement('span');
    element.style.cssText = 'position: fixed; top: -1000px;';
    element.append(windowDocument.createTextNode('#'));
    rootElement.append(element);
    const range = new Range();
    range.setStart(element, 0);
    range.setEnd(element, 1);
    domSelection.removeAllRanges();
    domSelection.addRange(range);
    return new Promise((resolve, reject) => {
      const removeListener = editor.registerCommand(COPY_COMMAND$1, secondEvent => {
        if (objectKlassEquals$1(secondEvent, ClipboardEvent)) {
          removeListener();
          if (clipboardEventTimeout !== null) {
            window.clearTimeout(clipboardEventTimeout);
            clipboardEventTimeout = null;
          }
          resolve($copyToClipboardEvent(editor, secondEvent, data));
        }
        // Block the entire copy flow while we wait for the next ClipboardEvent
        return true;
      }, COMMAND_PRIORITY_CRITICAL$1);
      // If the above hack execCommand hack works, this timeout code should never fire. Otherwise,
      // the listener will be quickly freed so that the user can reuse it again
      clipboardEventTimeout = window.setTimeout(() => {
        removeListener();
        clipboardEventTimeout = null;
        resolve(false);
      }, EVENT_LATENCY);
      windowDocument.execCommand('copy');
      element.remove();
    });
  }

  // TODO shouldn't pass editor (pass namespace directly)
  function $copyToClipboardEvent(editor, event, data) {
    if (data === undefined) {
      const domSelection = getDOMSelection$1(editor._window);
      if (!domSelection) {
        return false;
      }
      const anchorDOM = domSelection.anchorNode;
      const focusDOM = domSelection.focusNode;
      if (anchorDOM !== null && focusDOM !== null && !isSelectionWithinEditor$1(editor, anchorDOM, focusDOM)) {
        return false;
      }
      const selection = $getSelection$1();
      if (selection === null) {
        return false;
      }
      data = $getClipboardDataFromSelection(selection);
    }
    event.preventDefault();
    const clipboardData = event.clipboardData;
    if (clipboardData === null) {
      return false;
    }
    setLexicalClipboardDataTransfer(clipboardData, data);
    return true;
  }
  const clipboardDataFunctions = [['text/html', $getHtmlContent], ['application/x-lexical-editor', $getLexicalContent]];

  /**
   * Serialize the content of the current selection to strings in
   * text/plain, text/html, and application/x-lexical-editor (Lexical JSON)
   * formats (as available).
   *
   * @param selection the selection to serialize (defaults to $getSelection())
   * @returns LexicalClipboardData
   */
  function $getClipboardDataFromSelection(selection = $getSelection$1()) {
    const clipboardData = {
      'text/plain': selection ? selection.getTextContent() : ''
    };
    if (selection) {
      const editor = $getEditor$1();
      for (const [mimeType, $editorFn] of clipboardDataFunctions) {
        const v = $editorFn(editor, selection);
        if (v !== null) {
          clipboardData[mimeType] = v;
        }
      }
    }
    return clipboardData;
  }

  /**
   * Call setData on the given clipboardData for each MIME type present
   * in the given data (from {@link $getClipboardDataFromSelection})
   *
   * @param clipboardData the event.clipboardData to populate from data
   * @param data The lexical data
   */
  function setLexicalClipboardDataTransfer(clipboardData, data) {
    for (const k in data) {
      const v = data[k];
      if (v !== undefined) {
        clipboardData.setData(k, v);
      }
    }
  }

  var modDev$6 = /*#__PURE__*/Object.freeze({
    $generateJSONFromSelectedNodes: $generateJSONFromSelectedNodes,
    $generateNodesFromSerializedNodes: $generateNodesFromSerializedNodes,
    $getClipboardDataFromSelection: $getClipboardDataFromSelection,
    $getHtmlContent: $getHtmlContent,
    $getLexicalContent: $getLexicalContent,
    $insertDataTransferForPlainText: $insertDataTransferForPlainText,
    $insertDataTransferForRichText: $insertDataTransferForRichText,
    $insertGeneratedNodes: $insertGeneratedNodes,
    copyToClipboard: copyToClipboard,
    setLexicalClipboardDataTransfer: setLexicalClipboardDataTransfer
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  function w$4(t) {
    return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
  }
  var y$4 = w$4(function (t) {
    const e = new URLSearchParams();
    e.append("code", t);
    for (let t = 1; t < arguments.length; t++) e.append("v", arguments[t]);
    throw Error(`Minified Lexical error #${t}; visit https://lexical.dev/docs/error?${e} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });
  const v$4 = "undefined" != typeof window && void 0 !== window.document && void 0 !== window.document.createElement;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$6 = modDev$6;
  const $generateJSONFromSelectedNodes$1 = mod$6.$generateJSONFromSelectedNodes;
  const $generateNodesFromSerializedNodes$1 = mod$6.$generateNodesFromSerializedNodes;
  const $getClipboardDataFromSelection$1 = mod$6.$getClipboardDataFromSelection;
  const $getHtmlContent$1 = mod$6.$getHtmlContent;
  const $getLexicalContent$1 = mod$6.$getLexicalContent;
  const $insertDataTransferForPlainText$1 = mod$6.$insertDataTransferForPlainText;
  const $insertDataTransferForRichText$1 = mod$6.$insertDataTransferForRichText;
  const $insertGeneratedNodes$1 = mod$6.$insertGeneratedNodes;
  const copyToClipboard$1 = mod$6.copyToClipboard;
  const setLexicalClipboardDataTransfer$1 = mod$6.setLexicalClipboardDataTransfer;

  var LexicalClipboard = /*#__PURE__*/Object.freeze({
    $generateJSONFromSelectedNodes: $generateJSONFromSelectedNodes$1,
    $generateNodesFromSerializedNodes: $generateNodesFromSerializedNodes$1,
    $getClipboardDataFromSelection: $getClipboardDataFromSelection$1,
    $getHtmlContent: $getHtmlContent$1,
    $getLexicalContent: $getLexicalContent$1,
    $insertDataTransferForPlainText: $insertDataTransferForPlainText$1,
    $insertDataTransferForRichText: $insertDataTransferForRichText$1,
    $insertGeneratedNodes: $insertGeneratedNodes$1,
    copyToClipboard: copyToClipboard$1,
    setLexicalClipboardDataTransfer: setLexicalClipboardDataTransfer$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const HISTORY_MERGE = 0;
  const HISTORY_PUSH = 1;
  const DISCARD_HISTORY_CANDIDATE = 2;
  const OTHER = 0;
  const COMPOSING_CHARACTER = 1;
  const INSERT_CHARACTER_AFTER_SELECTION = 2;
  const DELETE_CHARACTER_BEFORE_SELECTION = 3;
  const DELETE_CHARACTER_AFTER_SELECTION = 4;
  function getDirtyNodes(editorState, dirtyLeaves, dirtyElements) {
    const nodeMap = editorState._nodeMap;
    const nodes = [];
    for (const dirtyLeafKey of dirtyLeaves) {
      const dirtyLeaf = nodeMap.get(dirtyLeafKey);
      if (dirtyLeaf !== undefined) {
        nodes.push(dirtyLeaf);
      }
    }
    for (const [dirtyElementKey, intentionallyMarkedAsDirty] of dirtyElements) {
      if (!intentionallyMarkedAsDirty) {
        continue;
      }
      const dirtyElement = nodeMap.get(dirtyElementKey);
      if (dirtyElement !== undefined && !$isRootNode$1(dirtyElement)) {
        nodes.push(dirtyElement);
      }
    }
    return nodes;
  }
  function getChangeType(prevEditorState, nextEditorState, dirtyLeavesSet, dirtyElementsSet, isComposing) {
    if (prevEditorState === null || dirtyLeavesSet.size === 0 && dirtyElementsSet.size === 0 && !isComposing) {
      return OTHER;
    }
    const nextSelection = nextEditorState._selection;
    const prevSelection = prevEditorState._selection;
    if (isComposing) {
      return COMPOSING_CHARACTER;
    }
    if (!$isRangeSelection$1(nextSelection) || !$isRangeSelection$1(prevSelection) || !prevSelection.isCollapsed() || !nextSelection.isCollapsed()) {
      return OTHER;
    }
    const dirtyNodes = getDirtyNodes(nextEditorState, dirtyLeavesSet, dirtyElementsSet);
    if (dirtyNodes.length === 0) {
      return OTHER;
    }

    // Catching the case when inserting new text node into an element (e.g. first char in paragraph/list),
    // or after existing node.
    if (dirtyNodes.length > 1) {
      const nextNodeMap = nextEditorState._nodeMap;
      const nextAnchorNode = nextNodeMap.get(nextSelection.anchor.key);
      const prevAnchorNode = nextNodeMap.get(prevSelection.anchor.key);
      if (nextAnchorNode && prevAnchorNode && !prevEditorState._nodeMap.has(nextAnchorNode.__key) && $isTextNode$1(nextAnchorNode) && nextAnchorNode.__text.length === 1 && nextSelection.anchor.offset === 1) {
        return INSERT_CHARACTER_AFTER_SELECTION;
      }
      return OTHER;
    }
    const nextDirtyNode = dirtyNodes[0];
    const prevDirtyNode = prevEditorState._nodeMap.get(nextDirtyNode.__key);
    if (!$isTextNode$1(prevDirtyNode) || !$isTextNode$1(nextDirtyNode) || prevDirtyNode.__mode !== nextDirtyNode.__mode) {
      return OTHER;
    }
    const prevText = prevDirtyNode.__text;
    const nextText = nextDirtyNode.__text;
    if (prevText === nextText) {
      return OTHER;
    }
    const nextAnchor = nextSelection.anchor;
    const prevAnchor = prevSelection.anchor;
    if (nextAnchor.key !== prevAnchor.key || nextAnchor.type !== 'text') {
      return OTHER;
    }
    const nextAnchorOffset = nextAnchor.offset;
    const prevAnchorOffset = prevAnchor.offset;
    const textDiff = nextText.length - prevText.length;
    if (textDiff === 1 && prevAnchorOffset === nextAnchorOffset - 1) {
      return INSERT_CHARACTER_AFTER_SELECTION;
    }
    if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset + 1) {
      return DELETE_CHARACTER_BEFORE_SELECTION;
    }
    if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset) {
      return DELETE_CHARACTER_AFTER_SELECTION;
    }
    return OTHER;
  }
  function isTextNodeUnchanged(key, prevEditorState, nextEditorState) {
    const prevNode = prevEditorState._nodeMap.get(key);
    const nextNode = nextEditorState._nodeMap.get(key);
    const prevSelection = prevEditorState._selection;
    const nextSelection = nextEditorState._selection;
    const isDeletingLine = $isRangeSelection$1(prevSelection) && $isRangeSelection$1(nextSelection) && prevSelection.anchor.type === 'element' && prevSelection.focus.type === 'element' && nextSelection.anchor.type === 'text' && nextSelection.focus.type === 'text';
    if (!isDeletingLine && $isTextNode$1(prevNode) && $isTextNode$1(nextNode) && prevNode.__parent === nextNode.__parent) {
      // This has the assumption that object key order won't change if the
      // content did not change, which should normally be safe given
      // the manner in which nodes and exportJSON are typically implemented.
      return JSON.stringify(prevEditorState.read(() => prevNode.exportJSON())) === JSON.stringify(nextEditorState.read(() => nextNode.exportJSON()));
    }
    return false;
  }
  function createMergeActionGetter(editor, delay) {
    let prevChangeTime = Date.now();
    let prevChangeType = OTHER;
    return (prevEditorState, nextEditorState, currentHistoryEntry, dirtyLeaves, dirtyElements, tags) => {
      const changeTime = Date.now();

      // If applying changes from history stack there's no need
      // to run history logic again, as history entries already calculated
      if (tags.has('historic')) {
        prevChangeType = OTHER;
        prevChangeTime = changeTime;
        return DISCARD_HISTORY_CANDIDATE;
      }
      const changeType = getChangeType(prevEditorState, nextEditorState, dirtyLeaves, dirtyElements, editor.isComposing());
      const mergeAction = (() => {
        const isSameEditor = currentHistoryEntry === null || currentHistoryEntry.editor === editor;
        const shouldPushHistory = tags.has('history-push');
        const shouldMergeHistory = !shouldPushHistory && isSameEditor && tags.has('history-merge');
        if (shouldMergeHistory) {
          return HISTORY_MERGE;
        }
        if (prevEditorState === null) {
          return HISTORY_PUSH;
        }
        const selection = nextEditorState._selection;
        const hasDirtyNodes = dirtyLeaves.size > 0 || dirtyElements.size > 0;
        if (!hasDirtyNodes) {
          if (selection !== null) {
            return HISTORY_MERGE;
          }
          return DISCARD_HISTORY_CANDIDATE;
        }
        if (shouldPushHistory === false && changeType !== OTHER && changeType === prevChangeType && changeTime < prevChangeTime + delay && isSameEditor) {
          return HISTORY_MERGE;
        }

        // A single node might have been marked as dirty, but not have changed
        // due to some node transform reverting the change.
        if (dirtyLeaves.size === 1) {
          const dirtyLeafKey = Array.from(dirtyLeaves)[0];
          if (isTextNodeUnchanged(dirtyLeafKey, prevEditorState, nextEditorState)) {
            return HISTORY_MERGE;
          }
        }
        return HISTORY_PUSH;
      })();
      prevChangeTime = changeTime;
      prevChangeType = changeType;
      return mergeAction;
    };
  }
  function redo(editor, historyState) {
    const redoStack = historyState.redoStack;
    const undoStack = historyState.undoStack;
    if (redoStack.length !== 0) {
      const current = historyState.current;
      if (current !== null) {
        undoStack.push(current);
        editor.dispatchCommand(CAN_UNDO_COMMAND$1, true);
      }
      const historyStateEntry = redoStack.pop();
      if (redoStack.length === 0) {
        editor.dispatchCommand(CAN_REDO_COMMAND$1, false);
      }
      historyState.current = historyStateEntry || null;
      if (historyStateEntry) {
        historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
          tag: 'historic'
        });
      }
    }
  }
  function undo(editor, historyState) {
    const redoStack = historyState.redoStack;
    const undoStack = historyState.undoStack;
    const undoStackLength = undoStack.length;
    if (undoStackLength !== 0) {
      const current = historyState.current;
      const historyStateEntry = undoStack.pop();
      if (current !== null) {
        redoStack.push(current);
        editor.dispatchCommand(CAN_REDO_COMMAND$1, true);
      }
      if (undoStack.length === 0) {
        editor.dispatchCommand(CAN_UNDO_COMMAND$1, false);
      }
      historyState.current = historyStateEntry || null;
      if (historyStateEntry) {
        historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
          tag: 'historic'
        });
      }
    }
  }
  function clearHistory(historyState) {
    historyState.undoStack = [];
    historyState.redoStack = [];
    historyState.current = null;
  }

  /**
   * Registers necessary listeners to manage undo/redo history stack and related editor commands.
   * It returns `unregister` callback that cleans up all listeners and should be called on editor unmount.
   * @param editor - The lexical editor.
   * @param historyState - The history state, containing the current state and the undo/redo stack.
   * @param delay - The time (in milliseconds) the editor should delay generating a new history stack,
   * instead of merging the current changes with the current stack.
   * @returns The listeners cleanup callback function.
   */
  function registerHistory(editor, historyState, delay) {
    const getMergeAction = createMergeActionGetter(editor, delay);
    const applyChange = ({
      editorState,
      prevEditorState,
      dirtyLeaves,
      dirtyElements,
      tags
    }) => {
      const current = historyState.current;
      const redoStack = historyState.redoStack;
      const undoStack = historyState.undoStack;
      const currentEditorState = current === null ? null : current.editorState;
      if (current !== null && editorState === currentEditorState) {
        return;
      }
      const mergeAction = getMergeAction(prevEditorState, editorState, current, dirtyLeaves, dirtyElements, tags);
      if (mergeAction === HISTORY_PUSH) {
        if (redoStack.length !== 0) {
          historyState.redoStack = [];
          editor.dispatchCommand(CAN_REDO_COMMAND$1, false);
        }
        if (current !== null) {
          undoStack.push({
            ...current
          });
          editor.dispatchCommand(CAN_UNDO_COMMAND$1, true);
        }
      } else if (mergeAction === DISCARD_HISTORY_CANDIDATE) {
        return;
      }

      // Else we merge
      historyState.current = {
        editor,
        editorState
      };
    };
    const unregister = mergeRegister$1(editor.registerCommand(UNDO_COMMAND$1, () => {
      undo(editor, historyState);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(REDO_COMMAND$1, () => {
      redo(editor, historyState);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(CLEAR_EDITOR_COMMAND$1, () => {
      clearHistory(historyState);
      return false;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(CLEAR_HISTORY_COMMAND$1, () => {
      clearHistory(historyState);
      editor.dispatchCommand(CAN_REDO_COMMAND$1, false);
      editor.dispatchCommand(CAN_UNDO_COMMAND$1, false);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerUpdateListener(applyChange));
    return unregister;
  }

  /**
   * Creates an empty history state.
   * @returns - The empty history state, as an object.
   */
  function createEmptyHistoryState() {
    return {
      current: null,
      redoStack: [],
      undoStack: []
    };
  }

  var modDev$7 = /*#__PURE__*/Object.freeze({
    createEmptyHistoryState: createEmptyHistoryState,
    registerHistory: registerHistory
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$7 = modDev$7;
  const createEmptyHistoryState$1 = mod$7.createEmptyHistoryState;
  const registerHistory$1 = mod$7.registerHistory;

  var LexicalHistory = /*#__PURE__*/Object.freeze({
    createEmptyHistoryState: createEmptyHistoryState$1,
    registerHistory: registerHistory$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Returns the root's text content.
   * @returns The root's text content.
   */
  function $rootTextContent() {
    const root = $getRoot$1();
    return root.getTextContent();
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Determines if the root has any text content and can trim any whitespace if it does.
   * @param isEditorComposing - Is the editor in composition mode due to an active Input Method Editor?
   * @param trim - Should the root text have its whitespaced trimmed? Defaults to true.
   * @returns true if text content is empty, false if there is text or isEditorComposing is true.
   */
  function $isRootTextContentEmpty(isEditorComposing, trim = true) {
    if (isEditorComposing) {
      return false;
    }
    let text = $rootTextContent();
    if (trim) {
      text = text.trim();
    }
    return text === '';
  }

  /**
   * Returns a function that executes {@link $isRootTextContentEmpty}
   * @param isEditorComposing - Is the editor in composition mode due to an active Input Method Editor?
   * @param trim - Should the root text have its whitespaced trimmed? Defaults to true.
   * @returns A function that executes $isRootTextContentEmpty based on arguments.
   */
  function $isRootTextContentEmptyCurry(isEditorComposing, trim) {
    return () => $isRootTextContentEmpty(isEditorComposing, trim);
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Determines if the input should show the placeholder. If anything is in
   * in the root the placeholder should not be shown.
   * @param isComposing - Is the editor in composition mode due to an active Input Method Editor?
   * @returns true if the input should show the placeholder, false otherwise.
   */
  function $canShowPlaceholder(isComposing) {
    if (!$isRootTextContentEmpty(isComposing, false)) {
      return false;
    }
    const root = $getRoot$1();
    const children = root.getChildren();
    const childrenLength = children.length;
    if (childrenLength > 1) {
      return false;
    }
    for (let i = 0; i < childrenLength; i++) {
      const topBlock = children[i];
      if ($isDecoratorNode$1(topBlock)) {
        return false;
      }
      if ($isElementNode$1(topBlock)) {
        if (!$isParagraphNode$1(topBlock)) {
          return false;
        }
        if (topBlock.__indent !== 0) {
          return false;
        }
        const topBlockChildren = topBlock.getChildren();
        const topBlockChildrenLength = topBlockChildren.length;
        for (let s = 0; s < topBlockChildrenLength; s++) {
          const child = topBlockChildren[i];
          if (!$isTextNode$1(child)) {
            return false;
          }
        }
      }
    }
    return true;
  }

  /**
   * Returns a function that executes {@link $canShowPlaceholder}
   * @param isEditorComposing - Is the editor in composition mode due to an active Input Method Editor?
   * @returns A function that executes $canShowPlaceholder with arguments.
   */
  function $canShowPlaceholderCurry(isEditorComposing) {
    return () => $canShowPlaceholder(isEditorComposing);
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Finds a TextNode with a size larger than targetCharacters and returns
   * the node along with the remaining length of the text.
   * @param root - The RootNode.
   * @param targetCharacters - The number of characters whose TextNode must be larger than.
   * @returns The TextNode and the intersections offset, or null if no TextNode is found.
   */
  function $findTextIntersectionFromCharacters(root, targetCharacters) {
    let node = root.getFirstChild();
    let currentCharacters = 0;
    mainLoop: while (node !== null) {
      if ($isElementNode$1(node)) {
        const child = node.getFirstChild();
        if (child !== null) {
          node = child;
          continue;
        }
      } else if ($isTextNode$1(node)) {
        const characters = node.getTextContentSize();
        if (currentCharacters + characters > targetCharacters) {
          return {
            node,
            offset: targetCharacters - currentCharacters
          };
        }
        currentCharacters += characters;
      }
      const sibling = node.getNextSibling();
      if (sibling !== null) {
        node = sibling;
        continue;
      }
      let parent = node.getParent();
      while (parent !== null) {
        const parentSibling = parent.getNextSibling();
        if (parentSibling !== null) {
          node = parentSibling;
          continue mainLoop;
        }
        parent = parent.getParent();
      }
      break;
    }
    return null;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Returns a tuple that can be rested (...) into mergeRegister to clean up
   * node transforms listeners that transforms text into another node, eg. a HashtagNode.
   * @example
   * ```ts
   *   useEffect(() => {
      return mergeRegister(
        ...registerLexicalTextEntity(editor, getMatch, targetNode, createNode),
      );
    }, [createNode, editor, getMatch, targetNode]);
   * ```
   * Where targetNode is the type of node containing the text you want to transform (like a text input),
   * then getMatch uses a regex to find a matching text and creates the proper node to include the matching text.
   * @param editor - The lexical editor.
   * @param getMatch - Finds a matching string that satisfies a regex expression.
   * @param targetNode - The node type that contains text to match with. eg. HashtagNode
   * @param createNode - A function that creates a new node to contain the matched text. eg createHashtagNode
   * @returns An array containing the plain text and reverse node transform listeners.
   */
  function registerLexicalTextEntity(editor, getMatch, targetNode, createNode) {
    const isTargetNode = node => {
      return node instanceof targetNode;
    };
    const $replaceWithSimpleText = node => {
      const textNode = $createTextNode$1(node.getTextContent());
      textNode.setFormat(node.getFormat());
      node.replace(textNode);
    };
    const getMode = node => {
      return node.getLatest().__mode;
    };
    const $textNodeTransform = node => {
      if (!node.isSimpleText()) {
        return;
      }
      let prevSibling = node.getPreviousSibling();
      let text = node.getTextContent();
      let currentNode = node;
      let match;
      if ($isTextNode$1(prevSibling)) {
        const previousText = prevSibling.getTextContent();
        const combinedText = previousText + text;
        const prevMatch = getMatch(combinedText);
        if (isTargetNode(prevSibling)) {
          if (prevMatch === null || getMode(prevSibling) !== 0) {
            $replaceWithSimpleText(prevSibling);
            return;
          } else {
            const diff = prevMatch.end - previousText.length;
            if (diff > 0) {
              const concatText = text.slice(0, diff);
              const newTextContent = previousText + concatText;
              prevSibling.select();
              prevSibling.setTextContent(newTextContent);
              if (diff === text.length) {
                node.remove();
              } else {
                const remainingText = text.slice(diff);
                node.setTextContent(remainingText);
              }
              return;
            }
          }
        } else if (prevMatch === null || prevMatch.start < previousText.length) {
          return;
        }
      }
      let prevMatchLengthToSkip = 0;
      // eslint-disable-next-line no-constant-condition
      while (true) {
        match = getMatch(text);
        let nextText = match === null ? '' : text.slice(match.end);
        text = nextText;
        if (nextText === '') {
          const nextSibling = currentNode.getNextSibling();
          if ($isTextNode$1(nextSibling)) {
            nextText = currentNode.getTextContent() + nextSibling.getTextContent();
            const nextMatch = getMatch(nextText);
            if (nextMatch === null) {
              if (isTargetNode(nextSibling)) {
                $replaceWithSimpleText(nextSibling);
              } else {
                nextSibling.markDirty();
              }
              return;
            } else if (nextMatch.start !== 0) {
              return;
            }
          }
        }
        if (match === null) {
          return;
        }
        if (match.start === 0 && $isTextNode$1(prevSibling) && prevSibling.isTextEntity()) {
          prevMatchLengthToSkip += match.end;
          continue;
        }
        let nodeToReplace;
        if (match.start === 0) {
          [nodeToReplace, currentNode] = currentNode.splitText(match.end);
        } else {
          [, nodeToReplace, currentNode] = currentNode.splitText(match.start + prevMatchLengthToSkip, match.end + prevMatchLengthToSkip);
        }
        if (!(nodeToReplace !== undefined)) {
          throw Error(`${'nodeToReplace'} should not be undefined. You may want to check splitOffsets passed to the splitText.`);
        }
        const replacementNode = createNode(nodeToReplace);
        replacementNode.setFormat(nodeToReplace.getFormat());
        nodeToReplace.replace(replacementNode);
        if (currentNode == null) {
          return;
        }
        prevMatchLengthToSkip = 0;
        prevSibling = replacementNode;
      }
    };
    const $reverseNodeTransform = node => {
      const text = node.getTextContent();
      const match = getMatch(text);
      if (match === null || match.start !== 0) {
        $replaceWithSimpleText(node);
        return;
      }
      if (text.length > match.end) {
        // This will split out the rest of the text as simple text
        node.splitText(match.end);
        return;
      }
      const prevSibling = node.getPreviousSibling();
      if ($isTextNode$1(prevSibling) && prevSibling.isTextEntity()) {
        $replaceWithSimpleText(prevSibling);
        $replaceWithSimpleText(node);
      }
      const nextSibling = node.getNextSibling();
      if ($isTextNode$1(nextSibling) && nextSibling.isTextEntity()) {
        $replaceWithSimpleText(nextSibling);

        // This may have already been converted in the previous block
        if (isTargetNode(node)) {
          $replaceWithSimpleText(node);
        }
      }
    };
    const removePlainTextTransform = editor.registerNodeTransform(TextNode$1, $textNodeTransform);
    const removeReverseNodeTransform = editor.registerNodeTransform(targetNode, $reverseNodeTransform);
    return [removePlainTextTransform, removeReverseNodeTransform];
  }

  var modDev$8 = /*#__PURE__*/Object.freeze({
    $canShowPlaceholder: $canShowPlaceholder,
    $canShowPlaceholderCurry: $canShowPlaceholderCurry,
    $findTextIntersectionFromCharacters: $findTextIntersectionFromCharacters,
    $isRootTextContentEmpty: $isRootTextContentEmpty,
    $isRootTextContentEmptyCurry: $isRootTextContentEmptyCurry,
    $rootTextContent: $rootTextContent,
    registerLexicalTextEntity: registerLexicalTextEntity
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  function d$2(t) {
    return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
  }
  var x$6 = d$2(function (t) {
    const e = new URLSearchParams();
    e.append("code", t);
    for (let t = 1; t < arguments.length; t++) e.append("v", arguments[t]);
    throw Error(`Minified Lexical error #${t}; visit https://lexical.dev/docs/error?${e} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$8 = modDev$8;
  const $canShowPlaceholder$1 = mod$8.$canShowPlaceholder;
  const $canShowPlaceholderCurry$1 = mod$8.$canShowPlaceholderCurry;
  const $findTextIntersectionFromCharacters$1 = mod$8.$findTextIntersectionFromCharacters;
  const $isRootTextContentEmpty$1 = mod$8.$isRootTextContentEmpty;
  const $isRootTextContentEmptyCurry$1 = mod$8.$isRootTextContentEmptyCurry;
  const $rootTextContent$1 = mod$8.$rootTextContent;
  const registerLexicalTextEntity$1 = mod$8.registerLexicalTextEntity;

  var LexicalText = /*#__PURE__*/Object.freeze({
    $canShowPlaceholder: $canShowPlaceholder$1,
    $canShowPlaceholderCurry: $canShowPlaceholderCurry$1,
    $findTextIntersectionFromCharacters: $findTextIntersectionFromCharacters$1,
    $isRootTextContentEmpty: $isRootTextContentEmpty$1,
    $isRootTextContentEmptyCurry: $isRootTextContentEmptyCurry$1,
    $rootTextContent: $rootTextContent$1,
    registerLexicalTextEntity: registerLexicalTextEntity$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function caretFromPoint(x, y) {
    if (typeof document.caretRangeFromPoint !== 'undefined') {
      const range = document.caretRangeFromPoint(x, y);
      if (range === null) {
        return null;
      }
      return {
        node: range.startContainer,
        offset: range.startOffset
      };
      // @ts-ignore
    } else if (document.caretPositionFromPoint !== 'undefined') {
      // @ts-ignore FF - no types
      const range = document.caretPositionFromPoint(x, y);
      if (range === null) {
        return null;
      }
      return {
        node: range.offsetNode,
        offset: range.offset
      };
    } else {
      // Gracefully handle IE
      return null;
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const CAN_USE_DOM$5 = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const documentMode$2 = CAN_USE_DOM$5 && 'documentMode' in document ? document.documentMode : null;
  const CAN_USE_BEFORE_INPUT$4 = CAN_USE_DOM$5 && 'InputEvent' in window && !documentMode$2 ? 'getTargetRanges' in new window.InputEvent('input') : false;
  const IS_SAFARI$4 = CAN_USE_DOM$5 && /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
  const IS_IOS$4 = CAN_USE_DOM$5 && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;

  // Keep these in case we need to use them in the future.
  // export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
  const IS_CHROME$4 = CAN_USE_DOM$5 && /^(?=.*Chrome).*/i.test(navigator.userAgent);
  const IS_APPLE_WEBKIT$4 = CAN_USE_DOM$5 && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !IS_CHROME$4;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const DRAG_DROP_PASTE = createCommand$1('DRAG_DROP_PASTE_FILE');
  /** @noInheritDoc */
  class QuoteNode extends ElementNode$1 {
    static getType() {
      return 'quote';
    }
    static clone(node) {
      return new QuoteNode(node.__key);
    }
    constructor(key) {
      super(key);
    }

    // View

    createDOM(config) {
      const element = document.createElement('blockquote');
      addClassNamesToElement$1(element, config.theme.quote);
      return element;
    }
    updateDOM(prevNode, dom) {
      return false;
    }
    static importDOM() {
      return {
        blockquote: node => ({
          conversion: $convertBlockquoteElement,
          priority: 0
        })
      };
    }
    exportDOM(editor) {
      const {
        element
      } = super.exportDOM(editor);
      if (element && isHTMLElement$2(element)) {
        if (this.isEmpty()) {
          element.append(document.createElement('br'));
        }
        const formatType = this.getFormatType();
        element.style.textAlign = formatType;
        const direction = this.getDirection();
        if (direction) {
          element.dir = direction;
        }
      }
      return {
        element
      };
    }
    static importJSON(serializedNode) {
      const node = $createQuoteNode();
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      return node;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        type: 'quote'
      };
    }

    // Mutation

    insertNewAfter(_, restoreSelection) {
      const newBlock = $createParagraphNode$1();
      const direction = this.getDirection();
      newBlock.setDirection(direction);
      this.insertAfter(newBlock, restoreSelection);
      return newBlock;
    }
    collapseAtStart() {
      const paragraph = $createParagraphNode$1();
      const children = this.getChildren();
      children.forEach(child => paragraph.append(child));
      this.replace(paragraph);
      return true;
    }
    canMergeWhenEmpty() {
      return true;
    }
  }
  function $createQuoteNode() {
    return $applyNodeReplacement$1(new QuoteNode());
  }
  function $isQuoteNode(node) {
    return node instanceof QuoteNode;
  }
  /** @noInheritDoc */
  class HeadingNode extends ElementNode$1 {
    /** @internal */

    static getType() {
      return 'heading';
    }
    static clone(node) {
      return new HeadingNode(node.__tag, node.__key);
    }
    constructor(tag, key) {
      super(key);
      this.__tag = tag;
    }
    getTag() {
      return this.__tag;
    }

    // View

    createDOM(config) {
      const tag = this.__tag;
      const element = document.createElement(tag);
      const theme = config.theme;
      const classNames = theme.heading;
      if (classNames !== undefined) {
        const className = classNames[tag];
        addClassNamesToElement$1(element, className);
      }
      return element;
    }
    updateDOM(prevNode, dom) {
      return false;
    }
    static importDOM() {
      return {
        h1: node => ({
          conversion: $convertHeadingElement,
          priority: 0
        }),
        h2: node => ({
          conversion: $convertHeadingElement,
          priority: 0
        }),
        h3: node => ({
          conversion: $convertHeadingElement,
          priority: 0
        }),
        h4: node => ({
          conversion: $convertHeadingElement,
          priority: 0
        }),
        h5: node => ({
          conversion: $convertHeadingElement,
          priority: 0
        }),
        h6: node => ({
          conversion: $convertHeadingElement,
          priority: 0
        }),
        p: node => {
          // domNode is a <p> since we matched it by nodeName
          const paragraph = node;
          const firstChild = paragraph.firstChild;
          if (firstChild !== null && isGoogleDocsTitle(firstChild)) {
            return {
              conversion: () => ({
                node: null
              }),
              priority: 3
            };
          }
          return null;
        },
        span: node => {
          if (isGoogleDocsTitle(node)) {
            return {
              conversion: domNode => {
                return {
                  node: $createHeadingNode('h1')
                };
              },
              priority: 3
            };
          }
          return null;
        }
      };
    }
    exportDOM(editor) {
      const {
        element
      } = super.exportDOM(editor);
      if (element && isHTMLElement$2(element)) {
        if (this.isEmpty()) {
          element.append(document.createElement('br'));
        }
        const formatType = this.getFormatType();
        element.style.textAlign = formatType;
        const direction = this.getDirection();
        if (direction) {
          element.dir = direction;
        }
      }
      return {
        element
      };
    }
    static importJSON(serializedNode) {
      const node = $createHeadingNode(serializedNode.tag);
      node.setFormat(serializedNode.format);
      node.setIndent(serializedNode.indent);
      node.setDirection(serializedNode.direction);
      return node;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        tag: this.getTag(),
        type: 'heading',
        version: 1
      };
    }

    // Mutation
    insertNewAfter(selection, restoreSelection = true) {
      const anchorOffet = selection ? selection.anchor.offset : 0;
      const lastDesc = this.getLastDescendant();
      const isAtEnd = !lastDesc || selection && selection.anchor.key === lastDesc.getKey() && anchorOffet === lastDesc.getTextContentSize();
      const newElement = isAtEnd || !selection ? $createParagraphNode$1() : $createHeadingNode(this.getTag());
      const direction = this.getDirection();
      newElement.setDirection(direction);
      this.insertAfter(newElement, restoreSelection);
      if (anchorOffet === 0 && !this.isEmpty() && selection) {
        const paragraph = $createParagraphNode$1();
        paragraph.select();
        this.replace(paragraph, true);
      }
      return newElement;
    }
    collapseAtStart() {
      const newElement = !this.isEmpty() ? $createHeadingNode(this.getTag()) : $createParagraphNode$1();
      const children = this.getChildren();
      children.forEach(child => newElement.append(child));
      this.replace(newElement);
      return true;
    }
    extractWithChild() {
      return true;
    }
  }
  function isGoogleDocsTitle(domNode) {
    if (domNode.nodeName.toLowerCase() === 'span') {
      return domNode.style.fontSize === '26pt';
    }
    return false;
  }
  function $convertHeadingElement(element) {
    const nodeName = element.nodeName.toLowerCase();
    let node = null;
    if (nodeName === 'h1' || nodeName === 'h2' || nodeName === 'h3' || nodeName === 'h4' || nodeName === 'h5' || nodeName === 'h6') {
      node = $createHeadingNode(nodeName);
      if (element.style !== null) {
        node.setFormat(element.style.textAlign);
      }
    }
    return {
      node
    };
  }
  function $convertBlockquoteElement(element) {
    const node = $createQuoteNode();
    if (element.style !== null) {
      node.setFormat(element.style.textAlign);
    }
    return {
      node
    };
  }
  function $createHeadingNode(headingTag) {
    return $applyNodeReplacement$1(new HeadingNode(headingTag));
  }
  function $isHeadingNode(node) {
    return node instanceof HeadingNode;
  }
  function onPasteForRichText(event, editor) {
    event.preventDefault();
    editor.update(() => {
      const selection = $getSelection$1();
      const clipboardData = objectKlassEquals$1(event, InputEvent) || objectKlassEquals$1(event, KeyboardEvent) ? null : event.clipboardData;
      if (clipboardData != null && selection !== null) {
        $insertDataTransferForRichText$1(clipboardData, selection, editor);
      }
    }, {
      tag: 'paste'
    });
  }
  async function onCutForRichText(event, editor) {
    await copyToClipboard$1(editor, objectKlassEquals$1(event, ClipboardEvent) ? event : null);
    editor.update(() => {
      const selection = $getSelection$1();
      if ($isRangeSelection$1(selection)) {
        selection.removeText();
      } else if ($isNodeSelection$1(selection)) {
        selection.getNodes().forEach(node => node.remove());
      }
    });
  }

  // Clipboard may contain files that we aren't allowed to read. While the event is arguably useless,
  // in certain occasions, we want to know whether it was a file transfer, as opposed to text. We
  // control this with the first boolean flag.
  function eventFiles(event) {
    let dataTransfer = null;
    if (objectKlassEquals$1(event, DragEvent)) {
      dataTransfer = event.dataTransfer;
    } else if (objectKlassEquals$1(event, ClipboardEvent)) {
      dataTransfer = event.clipboardData;
    }
    if (dataTransfer === null) {
      return [false, [], false];
    }
    const types = dataTransfer.types;
    const hasFiles = types.includes('Files');
    const hasContent = types.includes('text/html') || types.includes('text/plain');
    return [hasFiles, Array.from(dataTransfer.files), hasContent];
  }
  function $handleIndentAndOutdent(indentOrOutdent) {
    const selection = $getSelection$1();
    if (!$isRangeSelection$1(selection)) {
      return false;
    }
    const alreadyHandled = new Set();
    const nodes = selection.getNodes();
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      const key = node.getKey();
      if (alreadyHandled.has(key)) {
        continue;
      }
      const parentBlock = $findMatchingParent$1(node, parentNode => $isElementNode$1(parentNode) && !parentNode.isInline());
      if (parentBlock === null) {
        continue;
      }
      const parentKey = parentBlock.getKey();
      if (parentBlock.canIndent() && !alreadyHandled.has(parentKey)) {
        alreadyHandled.add(parentKey);
        indentOrOutdent(parentBlock);
      }
    }
    return alreadyHandled.size > 0;
  }
  function $isTargetWithinDecorator(target) {
    const node = $getNearestNodeFromDOMNode$1(target);
    return $isDecoratorNode$1(node);
  }
  function $isSelectionAtEndOfRoot(selection) {
    const focus = selection.focus;
    return focus.key === 'root' && focus.offset === $getRoot$1().getChildrenSize();
  }
  function registerRichText(editor) {
    const removeListener = mergeRegister$1(editor.registerCommand(CLICK_COMMAND$1, payload => {
      const selection = $getSelection$1();
      if ($isNodeSelection$1(selection)) {
        selection.clear();
        return true;
      }
      return false;
    }, 0), editor.registerCommand(DELETE_CHARACTER_COMMAND$1, isBackward => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.deleteCharacter(isBackward);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(DELETE_WORD_COMMAND$1, isBackward => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.deleteWord(isBackward);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(DELETE_LINE_COMMAND$1, isBackward => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.deleteLine(isBackward);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(CONTROLLED_TEXT_INSERTION_COMMAND$1, eventOrText => {
      const selection = $getSelection$1();
      if (typeof eventOrText === 'string') {
        if (selection !== null) {
          selection.insertText(eventOrText);
        }
      } else {
        if (selection === null) {
          return false;
        }
        const dataTransfer = eventOrText.dataTransfer;
        if (dataTransfer != null) {
          $insertDataTransferForRichText$1(dataTransfer, selection, editor);
        } else if ($isRangeSelection$1(selection)) {
          const data = eventOrText.data;
          if (data) {
            selection.insertText(data);
          }
          return true;
        }
      }
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(REMOVE_TEXT_COMMAND$1, () => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.removeText();
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(FORMAT_TEXT_COMMAND$1, format => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.formatText(format);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(FORMAT_ELEMENT_COMMAND$1, format => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection) && !$isNodeSelection$1(selection)) {
        return false;
      }
      const nodes = selection.getNodes();
      for (const node of nodes) {
        const element = $findMatchingParent$1(node, parentNode => $isElementNode$1(parentNode) && !parentNode.isInline());
        if (element !== null) {
          element.setFormat(format);
        }
      }
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(INSERT_LINE_BREAK_COMMAND$1, selectStart => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.insertLineBreak(selectStart);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(INSERT_PARAGRAPH_COMMAND$1, () => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      selection.insertParagraph();
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(INSERT_TAB_COMMAND$1, () => {
      $insertNodes$1([$createTabNode$1()]);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(INDENT_CONTENT_COMMAND$1, () => {
      return $handleIndentAndOutdent(block => {
        const indent = block.getIndent();
        block.setIndent(indent + 1);
      });
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(OUTDENT_CONTENT_COMMAND$1, () => {
      return $handleIndentAndOutdent(block => {
        const indent = block.getIndent();
        if (indent > 0) {
          block.setIndent(indent - 1);
        }
      });
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_ARROW_UP_COMMAND$1, event => {
      const selection = $getSelection$1();
      if ($isNodeSelection$1(selection) && !$isTargetWithinDecorator(event.target)) {
        // If selection is on a node, let's try and move selection
        // back to being a range selection.
        const nodes = selection.getNodes();
        if (nodes.length > 0) {
          nodes[0].selectPrevious();
          return true;
        }
      } else if ($isRangeSelection$1(selection)) {
        const possibleNode = $getAdjacentNode$1(selection.focus, true);
        if (!event.shiftKey && $isDecoratorNode$1(possibleNode) && !possibleNode.isIsolated() && !possibleNode.isInline()) {
          possibleNode.selectPrevious();
          event.preventDefault();
          return true;
        }
      }
      return false;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_ARROW_DOWN_COMMAND$1, event => {
      const selection = $getSelection$1();
      if ($isNodeSelection$1(selection)) {
        // If selection is on a node, let's try and move selection
        // back to being a range selection.
        const nodes = selection.getNodes();
        if (nodes.length > 0) {
          nodes[0].selectNext(0, 0);
          return true;
        }
      } else if ($isRangeSelection$1(selection)) {
        if ($isSelectionAtEndOfRoot(selection)) {
          event.preventDefault();
          return true;
        }
        const possibleNode = $getAdjacentNode$1(selection.focus, false);
        if (!event.shiftKey && $isDecoratorNode$1(possibleNode) && !possibleNode.isIsolated() && !possibleNode.isInline()) {
          possibleNode.selectNext();
          event.preventDefault();
          return true;
        }
      }
      return false;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_ARROW_LEFT_COMMAND$1, event => {
      const selection = $getSelection$1();
      if ($isNodeSelection$1(selection)) {
        // If selection is on a node, let's try and move selection
        // back to being a range selection.
        const nodes = selection.getNodes();
        if (nodes.length > 0) {
          event.preventDefault();
          nodes[0].selectPrevious();
          return true;
        }
      }
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      if ($shouldOverrideDefaultCharacterSelection$1(selection, true)) {
        const isHoldingShift = event.shiftKey;
        event.preventDefault();
        $moveCharacter$1(selection, isHoldingShift, true);
        return true;
      }
      return false;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_ARROW_RIGHT_COMMAND$1, event => {
      const selection = $getSelection$1();
      if ($isNodeSelection$1(selection) && !$isTargetWithinDecorator(event.target)) {
        // If selection is on a node, let's try and move selection
        // back to being a range selection.
        const nodes = selection.getNodes();
        if (nodes.length > 0) {
          event.preventDefault();
          nodes[0].selectNext(0, 0);
          return true;
        }
      }
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      const isHoldingShift = event.shiftKey;
      if ($shouldOverrideDefaultCharacterSelection$1(selection, false)) {
        event.preventDefault();
        $moveCharacter$1(selection, isHoldingShift, false);
        return true;
      }
      return false;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_BACKSPACE_COMMAND$1, event => {
      if ($isTargetWithinDecorator(event.target)) {
        return false;
      }
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      event.preventDefault();
      const {
        anchor
      } = selection;
      const anchorNode = anchor.getNode();
      if (selection.isCollapsed() && anchor.offset === 0 && !$isRootNode$1(anchorNode)) {
        const element = $getNearestBlockElementAncestorOrThrow$1(anchorNode);
        if (element.getIndent() > 0) {
          return editor.dispatchCommand(OUTDENT_CONTENT_COMMAND$1, undefined);
        }
      }
      return editor.dispatchCommand(DELETE_CHARACTER_COMMAND$1, true);
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_DELETE_COMMAND$1, event => {
      if ($isTargetWithinDecorator(event.target)) {
        return false;
      }
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      event.preventDefault();
      return editor.dispatchCommand(DELETE_CHARACTER_COMMAND$1, false);
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_ENTER_COMMAND$1, event => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      if (event !== null) {
        // If we have beforeinput, then we can avoid blocking
        // the default behavior. This ensures that the iOS can
        // intercept that we're actually inserting a paragraph,
        // and autocomplete, autocapitalize etc work as intended.
        // This can also cause a strange performance issue in
        // Safari, where there is a noticeable pause due to
        // preventing the key down of enter.
        if ((IS_IOS$4 || IS_SAFARI$4 || IS_APPLE_WEBKIT$4) && CAN_USE_BEFORE_INPUT$4) {
          return false;
        }
        event.preventDefault();
        if (event.shiftKey) {
          return editor.dispatchCommand(INSERT_LINE_BREAK_COMMAND$1, false);
        }
      }
      return editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND$1, undefined);
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(KEY_ESCAPE_COMMAND$1, () => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection)) {
        return false;
      }
      editor.blur();
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(DROP_COMMAND$1, event => {
      const [, files] = eventFiles(event);
      if (files.length > 0) {
        const x = event.clientX;
        const y = event.clientY;
        const eventRange = caretFromPoint(x, y);
        if (eventRange !== null) {
          const {
            offset: domOffset,
            node: domNode
          } = eventRange;
          const node = $getNearestNodeFromDOMNode$1(domNode);
          if (node !== null) {
            const selection = $createRangeSelection$1();
            if ($isTextNode$1(node)) {
              selection.anchor.set(node.getKey(), domOffset, 'text');
              selection.focus.set(node.getKey(), domOffset, 'text');
            } else {
              const parentKey = node.getParentOrThrow().getKey();
              const offset = node.getIndexWithinParent() + 1;
              selection.anchor.set(parentKey, offset, 'element');
              selection.focus.set(parentKey, offset, 'element');
            }
            const normalizedSelection = $normalizeSelection__EXPERIMENTAL(selection);
            $setSelection$1(normalizedSelection);
          }
          editor.dispatchCommand(DRAG_DROP_PASTE, files);
        }
        event.preventDefault();
        return true;
      }
      const selection = $getSelection$1();
      if ($isRangeSelection$1(selection)) {
        return true;
      }
      return false;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(DRAGSTART_COMMAND$1, event => {
      const [isFileTransfer] = eventFiles(event);
      const selection = $getSelection$1();
      if (isFileTransfer && !$isRangeSelection$1(selection)) {
        return false;
      }
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(DRAGOVER_COMMAND$1, event => {
      const [isFileTransfer] = eventFiles(event);
      const selection = $getSelection$1();
      if (isFileTransfer && !$isRangeSelection$1(selection)) {
        return false;
      }
      const x = event.clientX;
      const y = event.clientY;
      const eventRange = caretFromPoint(x, y);
      if (eventRange !== null) {
        const node = $getNearestNodeFromDOMNode$1(eventRange.node);
        if ($isDecoratorNode$1(node)) {
          // Show browser caret as the user is dragging the media across the screen. Won't work
          // for DecoratorNode nor it's relevant.
          event.preventDefault();
        }
      }
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(SELECT_ALL_COMMAND$1, () => {
      $selectAll$1();
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(COPY_COMMAND$1, event => {
      copyToClipboard$1(editor, objectKlassEquals$1(event, ClipboardEvent) ? event : null);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(CUT_COMMAND$1, event => {
      onCutForRichText(event, editor);
      return true;
    }, COMMAND_PRIORITY_EDITOR$1), editor.registerCommand(PASTE_COMMAND$1, event => {
      const [, files, hasTextContent] = eventFiles(event);
      if (files.length > 0 && !hasTextContent) {
        editor.dispatchCommand(DRAG_DROP_PASTE, files);
        return true;
      }

      // if inputs then paste within the input ignore creating a new node on paste event
      if (isSelectionCapturedInDecoratorInput$1(event.target)) {
        return false;
      }
      const selection = $getSelection$1();
      if (selection !== null) {
        onPasteForRichText(event, editor);
        return true;
      }
      return false;
    }, COMMAND_PRIORITY_EDITOR$1));
    return removeListener;
  }

  var modDev$9 = /*#__PURE__*/Object.freeze({
    $createHeadingNode: $createHeadingNode,
    $createQuoteNode: $createQuoteNode,
    $isHeadingNode: $isHeadingNode,
    $isQuoteNode: $isQuoteNode,
    DRAG_DROP_PASTE: DRAG_DROP_PASTE,
    HeadingNode: HeadingNode,
    QuoteNode: QuoteNode,
    eventFiles: eventFiles,
    registerRichText: registerRichText
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const ct$1 = "undefined" != typeof window && void 0 !== window.document && void 0 !== window.document.createElement,
    at$1 = ct$1 && "documentMode" in document ? document.documentMode : null,
    ut$1 = !(!ct$1 || !("InputEvent" in window) || at$1) && "getTargetRanges" in new window.InputEvent("input"),
    lt$1 = ct$1 && /Version\/[\d.]+.*Safari/.test(navigator.userAgent),
    dt$1 = ct$1 && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,
    mt$1 = ct$1 && /^(?=.*Chrome).*/i.test(navigator.userAgent),
    ft$1 = ct$1 && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !mt$1,
    gt$1 = createCommand$1("DRAG_DROP_PASTE_FILE");

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$9 = modDev$9;
  const $createHeadingNode$1 = mod$9.$createHeadingNode;
  const $createQuoteNode$1 = mod$9.$createQuoteNode;
  const $isHeadingNode$1 = mod$9.$isHeadingNode;
  const $isQuoteNode$1 = mod$9.$isQuoteNode;
  const DRAG_DROP_PASTE$1 = mod$9.DRAG_DROP_PASTE;
  const HeadingNode$1 = mod$9.HeadingNode;
  const QuoteNode$1 = mod$9.QuoteNode;
  const eventFiles$1 = mod$9.eventFiles;
  const registerRichText$1 = mod$9.registerRichText;

  var LexicalRichText = /*#__PURE__*/Object.freeze({
    $createHeadingNode: $createHeadingNode$1,
    $createQuoteNode: $createQuoteNode$1,
    $isHeadingNode: $isHeadingNode$1,
    $isQuoteNode: $isQuoteNode$1,
    DRAG_DROP_PASTE: DRAG_DROP_PASTE$1,
    HeadingNode: HeadingNode$1,
    QuoteNode: QuoteNode$1,
    eventFiles: eventFiles$1,
    registerRichText: registerRichText$1
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const PIXEL_VALUE_REG_EXP = /^(\d+(?:\.\d+)?)px$/;

  // .PlaygroundEditorTheme__tableCell width value from
  // packages/lexical-playground/src/themes/PlaygroundEditorTheme.css
  const COLUMN_WIDTH = 75;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const TableCellHeaderStates = {
    BOTH: 3,
    COLUMN: 2,
    NO_STATUS: 0,
    ROW: 1
  };
  /** @noInheritDoc */
  class TableCellNode extends ElementNode$1 {
    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    /** @internal */

    static getType() {
      return 'tablecell';
    }
    static clone(node) {
      const cellNode = new TableCellNode(node.__headerState, node.__colSpan, node.__width, node.__key);
      cellNode.__rowSpan = node.__rowSpan;
      cellNode.__backgroundColor = node.__backgroundColor;
      return cellNode;
    }
    static importDOM() {
      return {
        td: node => ({
          conversion: $convertTableCellNodeElement,
          priority: 0
        }),
        th: node => ({
          conversion: $convertTableCellNodeElement,
          priority: 0
        })
      };
    }
    static importJSON(serializedNode) {
      const colSpan = serializedNode.colSpan || 1;
      const rowSpan = serializedNode.rowSpan || 1;
      const cellNode = $createTableCellNode(serializedNode.headerState, colSpan, serializedNode.width || undefined);
      cellNode.__rowSpan = rowSpan;
      cellNode.__backgroundColor = serializedNode.backgroundColor || null;
      return cellNode;
    }
    constructor(headerState = TableCellHeaderStates.NO_STATUS, colSpan = 1, width, key) {
      super(key);
      this.__colSpan = colSpan;
      this.__rowSpan = 1;
      this.__headerState = headerState;
      this.__width = width;
      this.__backgroundColor = null;
    }
    createDOM(config) {
      const element = document.createElement(this.getTag());
      if (this.__width) {
        element.style.width = `${this.__width}px`;
      }
      if (this.__colSpan > 1) {
        element.colSpan = this.__colSpan;
      }
      if (this.__rowSpan > 1) {
        element.rowSpan = this.__rowSpan;
      }
      if (this.__backgroundColor !== null) {
        element.style.backgroundColor = this.__backgroundColor;
      }
      addClassNamesToElement$1(element, config.theme.tableCell, this.hasHeader() && config.theme.tableCellHeader);
      return element;
    }
    exportDOM(editor) {
      const {
        element
      } = super.exportDOM(editor);
      if (element) {
        const element_ = element;
        element_.style.border = '1px solid black';
        if (this.__colSpan > 1) {
          element_.colSpan = this.__colSpan;
        }
        if (this.__rowSpan > 1) {
          element_.rowSpan = this.__rowSpan;
        }
        element_.style.width = `${this.getWidth() || COLUMN_WIDTH}px`;
        element_.style.verticalAlign = 'top';
        element_.style.textAlign = 'start';
        const backgroundColor = this.getBackgroundColor();
        if (backgroundColor !== null) {
          element_.style.backgroundColor = backgroundColor;
        } else if (this.hasHeader()) {
          element_.style.backgroundColor = '#f2f3f5';
        }
      }
      return {
        element
      };
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        backgroundColor: this.getBackgroundColor(),
        colSpan: this.__colSpan,
        headerState: this.__headerState,
        rowSpan: this.__rowSpan,
        type: 'tablecell',
        width: this.getWidth()
      };
    }
    getColSpan() {
      return this.__colSpan;
    }
    setColSpan(colSpan) {
      this.getWritable().__colSpan = colSpan;
      return this;
    }
    getRowSpan() {
      return this.__rowSpan;
    }
    setRowSpan(rowSpan) {
      this.getWritable().__rowSpan = rowSpan;
      return this;
    }
    getTag() {
      return this.hasHeader() ? 'th' : 'td';
    }
    setHeaderStyles(headerState) {
      const self = this.getWritable();
      self.__headerState = headerState;
      return this.__headerState;
    }
    getHeaderStyles() {
      return this.getLatest().__headerState;
    }
    setWidth(width) {
      const self = this.getWritable();
      self.__width = width;
      return this.__width;
    }
    getWidth() {
      return this.getLatest().__width;
    }
    getBackgroundColor() {
      return this.getLatest().__backgroundColor;
    }
    setBackgroundColor(newBackgroundColor) {
      this.getWritable().__backgroundColor = newBackgroundColor;
    }
    toggleHeaderStyle(headerStateToToggle) {
      const self = this.getWritable();
      if ((self.__headerState & headerStateToToggle) === headerStateToToggle) {
        self.__headerState -= headerStateToToggle;
      } else {
        self.__headerState += headerStateToToggle;
      }
      return self;
    }
    hasHeaderState(headerState) {
      return (this.getHeaderStyles() & headerState) === headerState;
    }
    hasHeader() {
      return this.getLatest().__headerState !== TableCellHeaderStates.NO_STATUS;
    }
    updateDOM(prevNode) {
      return prevNode.__headerState !== this.__headerState || prevNode.__width !== this.__width || prevNode.__colSpan !== this.__colSpan || prevNode.__rowSpan !== this.__rowSpan || prevNode.__backgroundColor !== this.__backgroundColor;
    }
    isShadowRoot() {
      return true;
    }
    collapseAtStart() {
      return true;
    }
    canBeEmpty() {
      return false;
    }
    canIndent() {
      return false;
    }
  }
  function $convertTableCellNodeElement(domNode) {
    const domNode_ = domNode;
    const nodeName = domNode.nodeName.toLowerCase();
    let width = undefined;
    if (PIXEL_VALUE_REG_EXP.test(domNode_.style.width)) {
      width = parseFloat(domNode_.style.width);
    }
    const tableCellNode = $createTableCellNode(nodeName === 'th' ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS, domNode_.colSpan, width);
    tableCellNode.__rowSpan = domNode_.rowSpan;
    const backgroundColor = domNode_.style.backgroundColor;
    if (backgroundColor !== '') {
      tableCellNode.__backgroundColor = backgroundColor;
    }
    const style = domNode_.style;
    const textDecoration = style.textDecoration.split(' ');
    const hasBoldFontWeight = style.fontWeight === '700' || style.fontWeight === 'bold';
    const hasLinethroughTextDecoration = textDecoration.includes('line-through');
    const hasItalicFontStyle = style.fontStyle === 'italic';
    const hasUnderlineTextDecoration = textDecoration.includes('underline');
    return {
      after: childLexicalNodes => {
        if (childLexicalNodes.length === 0) {
          childLexicalNodes.push($createParagraphNode$1());
        }
        return childLexicalNodes;
      },
      forChild: (lexicalNode, parentLexicalNode) => {
        if ($isTableCellNode(parentLexicalNode) && !$isElementNode$1(lexicalNode)) {
          const paragraphNode = $createParagraphNode$1();
          if ($isLineBreakNode$1(lexicalNode) && lexicalNode.getTextContent() === '\n') {
            return null;
          }
          if ($isTextNode$1(lexicalNode)) {
            if (hasBoldFontWeight) {
              lexicalNode.toggleFormat('bold');
            }
            if (hasLinethroughTextDecoration) {
              lexicalNode.toggleFormat('strikethrough');
            }
            if (hasItalicFontStyle) {
              lexicalNode.toggleFormat('italic');
            }
            if (hasUnderlineTextDecoration) {
              lexicalNode.toggleFormat('underline');
            }
          }
          paragraphNode.append(lexicalNode);
          return paragraphNode;
        }
        return lexicalNode;
      },
      node: tableCellNode
    };
  }
  function $createTableCellNode(headerState, colSpan = 1, width) {
    return $applyNodeReplacement$1(new TableCellNode(headerState, colSpan, width));
  }
  function $isTableCellNode(node) {
    return node instanceof TableCellNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const INSERT_TABLE_COMMAND = createCommand$1('INSERT_TABLE_COMMAND');

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class TableRowNode extends ElementNode$1 {
    /** @internal */

    static getType() {
      return 'tablerow';
    }
    static clone(node) {
      return new TableRowNode(node.__height, node.__key);
    }
    static importDOM() {
      return {
        tr: node => ({
          conversion: $convertTableRowElement,
          priority: 0
        })
      };
    }
    static importJSON(serializedNode) {
      return $createTableRowNode(serializedNode.height);
    }
    constructor(height, key) {
      super(key);
      this.__height = height;
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        ...(this.getHeight() && {
          height: this.getHeight()
        }),
        type: 'tablerow',
        version: 1
      };
    }
    createDOM(config) {
      const element = document.createElement('tr');
      if (this.__height) {
        element.style.height = `${this.__height}px`;
      }
      addClassNamesToElement$1(element, config.theme.tableRow);
      return element;
    }
    isShadowRoot() {
      return true;
    }
    setHeight(height) {
      const self = this.getWritable();
      self.__height = height;
      return this.__height;
    }
    getHeight() {
      return this.getLatest().__height;
    }
    updateDOM(prevNode) {
      return prevNode.__height !== this.__height;
    }
    canBeEmpty() {
      return false;
    }
    canIndent() {
      return false;
    }
  }
  function $convertTableRowElement(domNode) {
    const domNode_ = domNode;
    let height = undefined;
    if (PIXEL_VALUE_REG_EXP.test(domNode_.style.height)) {
      height = parseFloat(domNode_.style.height);
    }
    return {
      node: $createTableRowNode(height)
    };
  }
  function $createTableRowNode(height) {
    return $applyNodeReplacement$1(new TableRowNode(height));
  }
  function $isTableRowNode(node) {
    return node instanceof TableRowNode;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const CAN_USE_DOM$6 = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  function $createTableNodeWithDimensions(rowCount, columnCount, includeHeaders = true) {
    const tableNode = $createTableNode();
    for (let iRow = 0; iRow < rowCount; iRow++) {
      const tableRowNode = $createTableRowNode();
      for (let iColumn = 0; iColumn < columnCount; iColumn++) {
        let headerState = TableCellHeaderStates.NO_STATUS;
        if (typeof includeHeaders === 'object') {
          if (iRow === 0 && includeHeaders.rows) {
            headerState |= TableCellHeaderStates.ROW;
          }
          if (iColumn === 0 && includeHeaders.columns) {
            headerState |= TableCellHeaderStates.COLUMN;
          }
        } else if (includeHeaders) {
          if (iRow === 0) {
            headerState |= TableCellHeaderStates.ROW;
          }
          if (iColumn === 0) {
            headerState |= TableCellHeaderStates.COLUMN;
          }
        }
        const tableCellNode = $createTableCellNode(headerState);
        const paragraphNode = $createParagraphNode$1();
        paragraphNode.append($createTextNode$1());
        tableCellNode.append(paragraphNode);
        tableRowNode.append(tableCellNode);
      }
      tableNode.append(tableRowNode);
    }
    return tableNode;
  }
  function $getTableCellNodeFromLexicalNode(startingNode) {
    const node = $findMatchingParent$1(startingNode, n => $isTableCellNode(n));
    if ($isTableCellNode(node)) {
      return node;
    }
    return null;
  }
  function $getTableRowNodeFromTableCellNodeOrThrow(startingNode) {
    const node = $findMatchingParent$1(startingNode, n => $isTableRowNode(n));
    if ($isTableRowNode(node)) {
      return node;
    }
    throw new Error('Expected table cell to be inside of table row.');
  }
  function $getTableNodeFromLexicalNodeOrThrow(startingNode) {
    const node = $findMatchingParent$1(startingNode, n => $isTableNode(n));
    if ($isTableNode(node)) {
      return node;
    }
    throw new Error('Expected table cell to be inside of table.');
  }
  function $getTableRowIndexFromTableCellNode(tableCellNode) {
    const tableRowNode = $getTableRowNodeFromTableCellNodeOrThrow(tableCellNode);
    const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableRowNode);
    return tableNode.getChildren().findIndex(n => n.is(tableRowNode));
  }
  function $getTableColumnIndexFromTableCellNode(tableCellNode) {
    const tableRowNode = $getTableRowNodeFromTableCellNodeOrThrow(tableCellNode);
    return tableRowNode.getChildren().findIndex(n => n.is(tableCellNode));
  }
  function $getTableCellSiblingsFromTableCellNode(tableCellNode, table) {
    const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
    const {
      x,
      y
    } = tableNode.getCordsFromCellNode(tableCellNode, table);
    return {
      above: tableNode.getCellNodeFromCords(x, y - 1, table),
      below: tableNode.getCellNodeFromCords(x, y + 1, table),
      left: tableNode.getCellNodeFromCords(x - 1, y, table),
      right: tableNode.getCellNodeFromCords(x + 1, y, table)
    };
  }
  function $removeTableRowAtIndex(tableNode, indexToDelete) {
    const tableRows = tableNode.getChildren();
    if (indexToDelete >= tableRows.length || indexToDelete < 0) {
      throw new Error('Expected table cell to be inside of table row.');
    }
    const targetRowNode = tableRows[indexToDelete];
    targetRowNode.remove();
    return tableNode;
  }
  function $insertTableRow(tableNode, targetIndex, shouldInsertAfter = true, rowCount, table) {
    const tableRows = tableNode.getChildren();
    if (targetIndex >= tableRows.length || targetIndex < 0) {
      throw new Error('Table row target index out of range');
    }
    const targetRowNode = tableRows[targetIndex];
    if ($isTableRowNode(targetRowNode)) {
      for (let r = 0; r < rowCount; r++) {
        const tableRowCells = targetRowNode.getChildren();
        const tableColumnCount = tableRowCells.length;
        const newTableRowNode = $createTableRowNode();
        for (let c = 0; c < tableColumnCount; c++) {
          const tableCellFromTargetRow = tableRowCells[c];
          if (!$isTableCellNode(tableCellFromTargetRow)) {
            throw Error(`Expected table cell`);
          }
          const {
            above,
            below
          } = $getTableCellSiblingsFromTableCellNode(tableCellFromTargetRow, table);
          let headerState = TableCellHeaderStates.NO_STATUS;
          const width = above && above.getWidth() || below && below.getWidth() || undefined;
          if (above && above.hasHeaderState(TableCellHeaderStates.COLUMN) || below && below.hasHeaderState(TableCellHeaderStates.COLUMN)) {
            headerState |= TableCellHeaderStates.COLUMN;
          }
          const tableCellNode = $createTableCellNode(headerState, 1, width);
          tableCellNode.append($createParagraphNode$1());
          newTableRowNode.append(tableCellNode);
        }
        if (shouldInsertAfter) {
          targetRowNode.insertAfter(newTableRowNode);
        } else {
          targetRowNode.insertBefore(newTableRowNode);
        }
      }
    } else {
      throw new Error('Row before insertion index does not exist.');
    }
    return tableNode;
  }
  const getHeaderState = (currentState, possibleState) => {
    if (currentState === TableCellHeaderStates.BOTH || currentState === possibleState) {
      return possibleState;
    }
    return TableCellHeaderStates.NO_STATUS;
  };
  function $insertTableRow__EXPERIMENTAL(insertAfter = true) {
    const selection = $getSelection$1();
    if (!($isRangeSelection$1(selection) || $isTableSelection(selection))) {
      throw Error(`Expected a RangeSelection or TableSelection`);
    }
    const focus = selection.focus.getNode();
    const [focusCell,, grid] = $getNodeTriplet(focus);
    const [gridMap, focusCellMap] = $computeTableMap(grid, focusCell, focusCell);
    const columnCount = gridMap[0].length;
    const {
      startRow: focusStartRow
    } = focusCellMap;
    if (insertAfter) {
      const focusEndRow = focusStartRow + focusCell.__rowSpan - 1;
      const focusEndRowMap = gridMap[focusEndRow];
      const newRow = $createTableRowNode();
      for (let i = 0; i < columnCount; i++) {
        const {
          cell,
          startRow
        } = focusEndRowMap[i];
        if (startRow + cell.__rowSpan - 1 <= focusEndRow) {
          const currentCell = focusEndRowMap[i].cell;
          const currentCellHeaderState = currentCell.__headerState;
          const headerState = getHeaderState(currentCellHeaderState, TableCellHeaderStates.COLUMN);
          newRow.append($createTableCellNode(headerState).append($createParagraphNode$1()));
        } else {
          cell.setRowSpan(cell.__rowSpan + 1);
        }
      }
      const focusEndRowNode = grid.getChildAtIndex(focusEndRow);
      if (!$isTableRowNode(focusEndRowNode)) {
        throw Error(`focusEndRow is not a TableRowNode`);
      }
      focusEndRowNode.insertAfter(newRow);
    } else {
      const focusStartRowMap = gridMap[focusStartRow];
      const newRow = $createTableRowNode();
      for (let i = 0; i < columnCount; i++) {
        const {
          cell,
          startRow
        } = focusStartRowMap[i];
        if (startRow === focusStartRow) {
          const currentCell = focusStartRowMap[i].cell;
          const currentCellHeaderState = currentCell.__headerState;
          const headerState = getHeaderState(currentCellHeaderState, TableCellHeaderStates.COLUMN);
          newRow.append($createTableCellNode(headerState).append($createParagraphNode$1()));
        } else {
          cell.setRowSpan(cell.__rowSpan + 1);
        }
      }
      const focusStartRowNode = grid.getChildAtIndex(focusStartRow);
      if (!$isTableRowNode(focusStartRowNode)) {
        throw Error(`focusEndRow is not a TableRowNode`);
      }
      focusStartRowNode.insertBefore(newRow);
    }
  }
  function $insertTableColumn(tableNode, targetIndex, shouldInsertAfter = true, columnCount, table) {
    const tableRows = tableNode.getChildren();
    const tableCellsToBeInserted = [];
    for (let r = 0; r < tableRows.length; r++) {
      const currentTableRowNode = tableRows[r];
      if ($isTableRowNode(currentTableRowNode)) {
        for (let c = 0; c < columnCount; c++) {
          const tableRowChildren = currentTableRowNode.getChildren();
          if (targetIndex >= tableRowChildren.length || targetIndex < 0) {
            throw new Error('Table column target index out of range');
          }
          const targetCell = tableRowChildren[targetIndex];
          if (!$isTableCellNode(targetCell)) {
            throw Error(`Expected table cell`);
          }
          const {
            left,
            right
          } = $getTableCellSiblingsFromTableCellNode(targetCell, table);
          let headerState = TableCellHeaderStates.NO_STATUS;
          if (left && left.hasHeaderState(TableCellHeaderStates.ROW) || right && right.hasHeaderState(TableCellHeaderStates.ROW)) {
            headerState |= TableCellHeaderStates.ROW;
          }
          const newTableCell = $createTableCellNode(headerState);
          newTableCell.append($createParagraphNode$1());
          tableCellsToBeInserted.push({
            newTableCell,
            targetCell
          });
        }
      }
    }
    tableCellsToBeInserted.forEach(({
      newTableCell,
      targetCell
    }) => {
      if (shouldInsertAfter) {
        targetCell.insertAfter(newTableCell);
      } else {
        targetCell.insertBefore(newTableCell);
      }
    });
    return tableNode;
  }
  function $insertTableColumn__EXPERIMENTAL(insertAfter = true) {
    const selection = $getSelection$1();
    if (!($isRangeSelection$1(selection) || $isTableSelection(selection))) {
      throw Error(`Expected a RangeSelection or TableSelection`);
    }
    const anchor = selection.anchor.getNode();
    const focus = selection.focus.getNode();
    const [anchorCell] = $getNodeTriplet(anchor);
    const [focusCell,, grid] = $getNodeTriplet(focus);
    const [gridMap, focusCellMap, anchorCellMap] = $computeTableMap(grid, focusCell, anchorCell);
    const rowCount = gridMap.length;
    const startColumn = insertAfter ? Math.max(focusCellMap.startColumn, anchorCellMap.startColumn) : Math.min(focusCellMap.startColumn, anchorCellMap.startColumn);
    const insertAfterColumn = insertAfter ? startColumn + focusCell.__colSpan - 1 : startColumn - 1;
    const gridFirstChild = grid.getFirstChild();
    if (!$isTableRowNode(gridFirstChild)) {
      throw Error(`Expected firstTable child to be a row`);
    }
    let firstInsertedCell = null;
    function $createTableCellNodeForInsertTableColumn(headerState = TableCellHeaderStates.NO_STATUS) {
      const cell = $createTableCellNode(headerState).append($createParagraphNode$1());
      if (firstInsertedCell === null) {
        firstInsertedCell = cell;
      }
      return cell;
    }
    let loopRow = gridFirstChild;
    rowLoop: for (let i = 0; i < rowCount; i++) {
      if (i !== 0) {
        const currentRow = loopRow.getNextSibling();
        if (!$isTableRowNode(currentRow)) {
          throw Error(`Expected row nextSibling to be a row`);
        }
        loopRow = currentRow;
      }
      const rowMap = gridMap[i];
      const currentCellHeaderState = rowMap[insertAfterColumn < 0 ? 0 : insertAfterColumn].cell.__headerState;
      const headerState = getHeaderState(currentCellHeaderState, TableCellHeaderStates.ROW);
      if (insertAfterColumn < 0) {
        $insertFirst$2(loopRow, $createTableCellNodeForInsertTableColumn(headerState));
        continue;
      }
      const {
        cell: currentCell,
        startColumn: currentStartColumn,
        startRow: currentStartRow
      } = rowMap[insertAfterColumn];
      if (currentStartColumn + currentCell.__colSpan - 1 <= insertAfterColumn) {
        let insertAfterCell = currentCell;
        let insertAfterCellRowStart = currentStartRow;
        let prevCellIndex = insertAfterColumn;
        while (insertAfterCellRowStart !== i && insertAfterCell.__rowSpan > 1) {
          prevCellIndex -= currentCell.__colSpan;
          if (prevCellIndex >= 0) {
            const {
              cell: cell_,
              startRow: startRow_
            } = rowMap[prevCellIndex];
            insertAfterCell = cell_;
            insertAfterCellRowStart = startRow_;
          } else {
            loopRow.append($createTableCellNodeForInsertTableColumn(headerState));
            continue rowLoop;
          }
        }
        insertAfterCell.insertAfter($createTableCellNodeForInsertTableColumn(headerState));
      } else {
        currentCell.setColSpan(currentCell.__colSpan + 1);
      }
    }
    if (firstInsertedCell !== null) {
      $moveSelectionToCell(firstInsertedCell);
    }
  }
  function $deleteTableColumn(tableNode, targetIndex) {
    const tableRows = tableNode.getChildren();
    for (let i = 0; i < tableRows.length; i++) {
      const currentTableRowNode = tableRows[i];
      if ($isTableRowNode(currentTableRowNode)) {
        const tableRowChildren = currentTableRowNode.getChildren();
        if (targetIndex >= tableRowChildren.length || targetIndex < 0) {
          throw new Error('Table column target index out of range');
        }
        tableRowChildren[targetIndex].remove();
      }
    }
    return tableNode;
  }
  function $deleteTableRow__EXPERIMENTAL() {
    const selection = $getSelection$1();
    if (!($isRangeSelection$1(selection) || $isTableSelection(selection))) {
      throw Error(`Expected a RangeSelection or TableSelection`);
    }
    const anchor = selection.anchor.getNode();
    const focus = selection.focus.getNode();
    const [anchorCell,, grid] = $getNodeTriplet(anchor);
    const [focusCell] = $getNodeTriplet(focus);
    const [gridMap, anchorCellMap, focusCellMap] = $computeTableMap(grid, anchorCell, focusCell);
    const {
      startRow: anchorStartRow
    } = anchorCellMap;
    const {
      startRow: focusStartRow
    } = focusCellMap;
    const focusEndRow = focusStartRow + focusCell.__rowSpan - 1;
    if (gridMap.length === focusEndRow - anchorStartRow + 1) {
      // Empty grid
      grid.remove();
      return;
    }
    const columnCount = gridMap[0].length;
    const nextRow = gridMap[focusEndRow + 1];
    const nextRowNode = grid.getChildAtIndex(focusEndRow + 1);
    for (let row = focusEndRow; row >= anchorStartRow; row--) {
      for (let column = columnCount - 1; column >= 0; column--) {
        const {
          cell,
          startRow: cellStartRow,
          startColumn: cellStartColumn
        } = gridMap[row][column];
        if (cellStartColumn !== column) {
          // Don't repeat work for the same Cell
          continue;
        }
        // Rows overflowing top have to be trimmed
        if (row === anchorStartRow && cellStartRow < anchorStartRow) {
          cell.setRowSpan(cell.__rowSpan - (cellStartRow - anchorStartRow));
        }
        // Rows overflowing bottom have to be trimmed and moved to the next row
        if (cellStartRow >= anchorStartRow && cellStartRow + cell.__rowSpan - 1 > focusEndRow) {
          cell.setRowSpan(cell.__rowSpan - (focusEndRow - cellStartRow + 1));
          if (!(nextRowNode !== null)) {
            throw Error(`Expected nextRowNode not to be null`);
          }
          if (column === 0) {
            $insertFirst$2(nextRowNode, cell);
          } else {
            const {
              cell: previousCell
            } = nextRow[column - 1];
            previousCell.insertAfter(cell);
          }
        }
      }
      const rowNode = grid.getChildAtIndex(row);
      if (!$isTableRowNode(rowNode)) {
        throw Error(`Expected GridNode childAtIndex(${String(row)}) to be RowNode`);
      }
      rowNode.remove();
    }
    if (nextRow !== undefined) {
      const {
        cell
      } = nextRow[0];
      $moveSelectionToCell(cell);
    } else {
      const previousRow = gridMap[anchorStartRow - 1];
      const {
        cell
      } = previousRow[0];
      $moveSelectionToCell(cell);
    }
  }
  function $deleteTableColumn__EXPERIMENTAL() {
    const selection = $getSelection$1();
    if (!($isRangeSelection$1(selection) || $isTableSelection(selection))) {
      throw Error(`Expected a RangeSelection or TableSelection`);
    }
    const anchor = selection.anchor.getNode();
    const focus = selection.focus.getNode();
    const [anchorCell,, grid] = $getNodeTriplet(anchor);
    const [focusCell] = $getNodeTriplet(focus);
    const [gridMap, anchorCellMap, focusCellMap] = $computeTableMap(grid, anchorCell, focusCell);
    const {
      startColumn: anchorStartColumn
    } = anchorCellMap;
    const {
      startRow: focusStartRow,
      startColumn: focusStartColumn
    } = focusCellMap;
    const startColumn = Math.min(anchorStartColumn, focusStartColumn);
    const endColumn = Math.max(anchorStartColumn + anchorCell.__colSpan - 1, focusStartColumn + focusCell.__colSpan - 1);
    const selectedColumnCount = endColumn - startColumn + 1;
    const columnCount = gridMap[0].length;
    if (columnCount === endColumn - startColumn + 1) {
      // Empty grid
      grid.selectPrevious();
      grid.remove();
      return;
    }
    const rowCount = gridMap.length;
    for (let row = 0; row < rowCount; row++) {
      for (let column = startColumn; column <= endColumn; column++) {
        const {
          cell,
          startColumn: cellStartColumn
        } = gridMap[row][column];
        if (cellStartColumn < startColumn) {
          if (column === startColumn) {
            const overflowLeft = startColumn - cellStartColumn;
            // Overflowing left
            cell.setColSpan(cell.__colSpan -
            // Possible overflow right too
            Math.min(selectedColumnCount, cell.__colSpan - overflowLeft));
          }
        } else if (cellStartColumn + cell.__colSpan - 1 > endColumn) {
          if (column === endColumn) {
            // Overflowing right
            const inSelectedArea = endColumn - cellStartColumn + 1;
            cell.setColSpan(cell.__colSpan - inSelectedArea);
          }
        } else {
          cell.remove();
        }
      }
    }
    const focusRowMap = gridMap[focusStartRow];
    const nextColumn = anchorStartColumn > focusStartColumn ? focusRowMap[anchorStartColumn + anchorCell.__colSpan] : focusRowMap[focusStartColumn + focusCell.__colSpan];
    if (nextColumn !== undefined) {
      const {
        cell
      } = nextColumn;
      $moveSelectionToCell(cell);
    } else {
      const previousRow = focusStartColumn < anchorStartColumn ? focusRowMap[focusStartColumn - 1] : focusRowMap[anchorStartColumn - 1];
      const {
        cell
      } = previousRow;
      $moveSelectionToCell(cell);
    }
  }
  function $moveSelectionToCell(cell) {
    const firstDescendant = cell.getFirstDescendant();
    if (firstDescendant == null) {
      cell.selectStart();
    } else {
      firstDescendant.getParentOrThrow().selectStart();
    }
  }
  function $insertFirst$2(parent, node) {
    const firstChild = parent.getFirstChild();
    if (firstChild !== null) {
      firstChild.insertBefore(node);
    } else {
      parent.append(node);
    }
  }
  function $unmergeCell() {
    const selection = $getSelection$1();
    if (!($isRangeSelection$1(selection) || $isTableSelection(selection))) {
      throw Error(`Expected a RangeSelection or TableSelection`);
    }
    const anchor = selection.anchor.getNode();
    const [cell, row, grid] = $getNodeTriplet(anchor);
    const colSpan = cell.__colSpan;
    const rowSpan = cell.__rowSpan;
    if (colSpan > 1) {
      for (let i = 1; i < colSpan; i++) {
        cell.insertAfter($createTableCellNode(TableCellHeaderStates.NO_STATUS).append($createParagraphNode$1()));
      }
      cell.setColSpan(1);
    }
    if (rowSpan > 1) {
      const [map, cellMap] = $computeTableMap(grid, cell, cell);
      const {
        startColumn,
        startRow
      } = cellMap;
      let currentRowNode;
      for (let i = 1; i < rowSpan; i++) {
        const currentRow = startRow + i;
        const currentRowMap = map[currentRow];
        currentRowNode = (currentRowNode || row).getNextSibling();
        if (!$isTableRowNode(currentRowNode)) {
          throw Error(`Expected row next sibling to be a row`);
        }
        let insertAfterCell = null;
        for (let column = 0; column < startColumn; column++) {
          const currentCellMap = currentRowMap[column];
          const currentCell = currentCellMap.cell;
          if (currentCellMap.startRow === currentRow) {
            insertAfterCell = currentCell;
          }
          if (currentCell.__colSpan > 1) {
            column += currentCell.__colSpan - 1;
          }
        }
        if (insertAfterCell === null) {
          for (let j = 0; j < colSpan; j++) {
            $insertFirst$2(currentRowNode, $createTableCellNode(TableCellHeaderStates.NO_STATUS).append($createParagraphNode$1()));
          }
        } else {
          for (let j = 0; j < colSpan; j++) {
            insertAfterCell.insertAfter($createTableCellNode(TableCellHeaderStates.NO_STATUS).append($createParagraphNode$1()));
          }
        }
      }
      cell.setRowSpan(1);
    }
  }
  function $computeTableMap(grid, cellA, cellB) {
    const [tableMap, cellAValue, cellBValue] = $computeTableMapSkipCellCheck(grid, cellA, cellB);
    if (!(cellAValue !== null)) {
      throw Error(`Anchor not found in Grid`);
    }
    if (!(cellBValue !== null)) {
      throw Error(`Focus not found in Grid`);
    }
    return [tableMap, cellAValue, cellBValue];
  }
  function $computeTableMapSkipCellCheck(grid, cellA, cellB) {
    const tableMap = [];
    let cellAValue = null;
    let cellBValue = null;
    function write(startRow, startColumn, cell) {
      const value = {
        cell,
        startColumn,
        startRow
      };
      const rowSpan = cell.__rowSpan;
      const colSpan = cell.__colSpan;
      for (let i = 0; i < rowSpan; i++) {
        if (tableMap[startRow + i] === undefined) {
          tableMap[startRow + i] = [];
        }
        for (let j = 0; j < colSpan; j++) {
          tableMap[startRow + i][startColumn + j] = value;
        }
      }
      if (cellA !== null && cellA.is(cell)) {
        cellAValue = value;
      }
      if (cellB !== null && cellB.is(cell)) {
        cellBValue = value;
      }
    }
    function isEmpty(row, column) {
      return tableMap[row] === undefined || tableMap[row][column] === undefined;
    }
    const gridChildren = grid.getChildren();
    for (let i = 0; i < gridChildren.length; i++) {
      const row = gridChildren[i];
      if (!$isTableRowNode(row)) {
        throw Error(`Expected GridNode children to be TableRowNode`);
      }
      const rowChildren = row.getChildren();
      let j = 0;
      for (const cell of rowChildren) {
        if (!$isTableCellNode(cell)) {
          throw Error(`Expected TableRowNode children to be TableCellNode`);
        }
        while (!isEmpty(i, j)) {
          j++;
        }
        write(i, j, cell);
        j += cell.__colSpan;
      }
    }
    return [tableMap, cellAValue, cellBValue];
  }
  function $getNodeTriplet(source) {
    let cell;
    if (source instanceof TableCellNode) {
      cell = source;
    } else if ('__type' in source) {
      const cell_ = $findMatchingParent$1(source, $isTableCellNode);
      if (!$isTableCellNode(cell_)) {
        throw Error(`Expected to find a parent TableCellNode`);
      }
      cell = cell_;
    } else {
      const cell_ = $findMatchingParent$1(source.getNode(), $isTableCellNode);
      if (!$isTableCellNode(cell_)) {
        throw Error(`Expected to find a parent TableCellNode`);
      }
      cell = cell_;
    }
    const row = cell.getParent();
    if (!$isTableRowNode(row)) {
      throw Error(`Expected TableCellNode to have a parent TableRowNode`);
    }
    const grid = row.getParent();
    if (!$isTableNode(grid)) {
      throw Error(`Expected TableRowNode to have a parent GridNode`);
    }
    return [cell, row, grid];
  }
  function $getTableCellNodeRect(tableCellNode) {
    const [cellNode,, gridNode] = $getNodeTriplet(tableCellNode);
    const rows = gridNode.getChildren();
    const rowCount = rows.length;
    const columnCount = rows[0].getChildren().length;

    // Create a matrix of the same size as the table to track the position of each cell
    const cellMatrix = new Array(rowCount);
    for (let i = 0; i < rowCount; i++) {
      cellMatrix[i] = new Array(columnCount);
    }
    for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
      const row = rows[rowIndex];
      const cells = row.getChildren();
      let columnIndex = 0;
      for (let cellIndex = 0; cellIndex < cells.length; cellIndex++) {
        // Find the next available position in the matrix, skip the position of merged cells
        while (cellMatrix[rowIndex][columnIndex]) {
          columnIndex++;
        }
        const cell = cells[cellIndex];
        const rowSpan = cell.__rowSpan || 1;
        const colSpan = cell.__colSpan || 1;

        // Put the cell into the corresponding position in the matrix
        for (let i = 0; i < rowSpan; i++) {
          for (let j = 0; j < colSpan; j++) {
            cellMatrix[rowIndex + i][columnIndex + j] = cell;
          }
        }

        // Return to the original index, row span and column span of the cell.
        if (cellNode === cell) {
          return {
            colSpan,
            columnIndex,
            rowIndex,
            rowSpan
          };
        }
        columnIndex += colSpan;
      }
    }
    return null;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  class TableSelection {
    constructor(tableKey, anchor, focus) {
      this.anchor = anchor;
      this.focus = focus;
      anchor._selection = this;
      focus._selection = this;
      this._cachedNodes = null;
      this.dirty = false;
      this.tableKey = tableKey;
    }
    getStartEndPoints() {
      return [this.anchor, this.focus];
    }

    /**
     * Returns whether the Selection is "backwards", meaning the focus
     * logically precedes the anchor in the EditorState.
     * @returns true if the Selection is backwards, false otherwise.
     */
    isBackward() {
      return this.focus.isBefore(this.anchor);
    }
    getCachedNodes() {
      return this._cachedNodes;
    }
    setCachedNodes(nodes) {
      this._cachedNodes = nodes;
    }
    is(selection) {
      if (!$isTableSelection(selection)) {
        return false;
      }
      return this.tableKey === selection.tableKey && this.anchor.is(selection.anchor) && this.focus.is(selection.focus);
    }
    set(tableKey, anchorCellKey, focusCellKey) {
      this.dirty = true;
      this.tableKey = tableKey;
      this.anchor.key = anchorCellKey;
      this.focus.key = focusCellKey;
      this._cachedNodes = null;
    }
    clone() {
      return new TableSelection(this.tableKey, this.anchor, this.focus);
    }
    isCollapsed() {
      return false;
    }
    extract() {
      return this.getNodes();
    }
    insertRawText(text) {
      // Do nothing?
    }
    insertText() {
      // Do nothing?
    }
    insertNodes(nodes) {
      const focusNode = this.focus.getNode();
      if (!$isElementNode$1(focusNode)) {
        throw Error(`Expected TableSelection focus to be an ElementNode`);
      }
      const selection = $normalizeSelection__EXPERIMENTAL(focusNode.select(0, focusNode.getChildrenSize()));
      selection.insertNodes(nodes);
    }

    // TODO Deprecate this method. It's confusing when used with colspan|rowspan
    getShape() {
      const anchorCellNode = $getNodeByKey$1(this.anchor.key);
      if (!$isTableCellNode(anchorCellNode)) {
        throw Error(`Expected TableSelection anchor to be (or a child of) TableCellNode`);
      }
      const anchorCellNodeRect = $getTableCellNodeRect(anchorCellNode);
      if (!(anchorCellNodeRect !== null)) {
        throw Error(`getCellRect: expected to find AnchorNode`);
      }
      const focusCellNode = $getNodeByKey$1(this.focus.key);
      if (!$isTableCellNode(focusCellNode)) {
        throw Error(`Expected TableSelection focus to be (or a child of) TableCellNode`);
      }
      const focusCellNodeRect = $getTableCellNodeRect(focusCellNode);
      if (!(focusCellNodeRect !== null)) {
        throw Error(`getCellRect: expected to find focusCellNode`);
      }
      const startX = Math.min(anchorCellNodeRect.columnIndex, focusCellNodeRect.columnIndex);
      const stopX = Math.max(anchorCellNodeRect.columnIndex, focusCellNodeRect.columnIndex);
      const startY = Math.min(anchorCellNodeRect.rowIndex, focusCellNodeRect.rowIndex);
      const stopY = Math.max(anchorCellNodeRect.rowIndex, focusCellNodeRect.rowIndex);
      return {
        fromX: Math.min(startX, stopX),
        fromY: Math.min(startY, stopY),
        toX: Math.max(startX, stopX),
        toY: Math.max(startY, stopY)
      };
    }
    getNodes() {
      const cachedNodes = this._cachedNodes;
      if (cachedNodes !== null) {
        return cachedNodes;
      }
      const anchorNode = this.anchor.getNode();
      const focusNode = this.focus.getNode();
      const anchorCell = $findMatchingParent$1(anchorNode, $isTableCellNode);
      // todo replace with triplet
      const focusCell = $findMatchingParent$1(focusNode, $isTableCellNode);
      if (!$isTableCellNode(anchorCell)) {
        throw Error(`Expected TableSelection anchor to be (or a child of) TableCellNode`);
      }
      if (!$isTableCellNode(focusCell)) {
        throw Error(`Expected TableSelection focus to be (or a child of) TableCellNode`);
      }
      const anchorRow = anchorCell.getParent();
      if (!$isTableRowNode(anchorRow)) {
        throw Error(`Expected anchorCell to have a parent TableRowNode`);
      }
      const tableNode = anchorRow.getParent();
      if (!$isTableNode(tableNode)) {
        throw Error(`Expected tableNode to have a parent TableNode`);
      }
      const focusCellGrid = focusCell.getParents()[1];
      if (focusCellGrid !== tableNode) {
        if (!tableNode.isParentOf(focusCell)) {
          // focus is on higher Grid level than anchor
          const gridParent = tableNode.getParent();
          if (!(gridParent != null)) {
            throw Error(`Expected gridParent to have a parent`);
          }
          this.set(this.tableKey, gridParent.getKey(), focusCell.getKey());
        } else {
          // anchor is on higher Grid level than focus
          const focusCellParent = focusCellGrid.getParent();
          if (!(focusCellParent != null)) {
            throw Error(`Expected focusCellParent to have a parent`);
          }
          this.set(this.tableKey, focusCell.getKey(), focusCellParent.getKey());
        }
        return this.getNodes();
      }

      // TODO Mapping the whole Grid every time not efficient. We need to compute the entire state only
      // once (on load) and iterate on it as updates occur. However, to do this we need to have the
      // ability to store a state. Killing TableSelection and moving the logic to the plugin would make
      // this possible.
      const [map, cellAMap, cellBMap] = $computeTableMap(tableNode, anchorCell, focusCell);
      let minColumn = Math.min(cellAMap.startColumn, cellBMap.startColumn);
      let minRow = Math.min(cellAMap.startRow, cellBMap.startRow);
      let maxColumn = Math.max(cellAMap.startColumn + cellAMap.cell.__colSpan - 1, cellBMap.startColumn + cellBMap.cell.__colSpan - 1);
      let maxRow = Math.max(cellAMap.startRow + cellAMap.cell.__rowSpan - 1, cellBMap.startRow + cellBMap.cell.__rowSpan - 1);
      let exploredMinColumn = minColumn;
      let exploredMinRow = minRow;
      let exploredMaxColumn = minColumn;
      let exploredMaxRow = minRow;
      function expandBoundary(mapValue) {
        const {
          cell,
          startColumn: cellStartColumn,
          startRow: cellStartRow
        } = mapValue;
        minColumn = Math.min(minColumn, cellStartColumn);
        minRow = Math.min(minRow, cellStartRow);
        maxColumn = Math.max(maxColumn, cellStartColumn + cell.__colSpan - 1);
        maxRow = Math.max(maxRow, cellStartRow + cell.__rowSpan - 1);
      }
      while (minColumn < exploredMinColumn || minRow < exploredMinRow || maxColumn > exploredMaxColumn || maxRow > exploredMaxRow) {
        if (minColumn < exploredMinColumn) {
          // Expand on the left
          const rowDiff = exploredMaxRow - exploredMinRow;
          const previousColumn = exploredMinColumn - 1;
          for (let i = 0; i <= rowDiff; i++) {
            expandBoundary(map[exploredMinRow + i][previousColumn]);
          }
          exploredMinColumn = previousColumn;
        }
        if (minRow < exploredMinRow) {
          // Expand on top
          const columnDiff = exploredMaxColumn - exploredMinColumn;
          const previousRow = exploredMinRow - 1;
          for (let i = 0; i <= columnDiff; i++) {
            expandBoundary(map[previousRow][exploredMinColumn + i]);
          }
          exploredMinRow = previousRow;
        }
        if (maxColumn > exploredMaxColumn) {
          // Expand on the right
          const rowDiff = exploredMaxRow - exploredMinRow;
          const nextColumn = exploredMaxColumn + 1;
          for (let i = 0; i <= rowDiff; i++) {
            expandBoundary(map[exploredMinRow + i][nextColumn]);
          }
          exploredMaxColumn = nextColumn;
        }
        if (maxRow > exploredMaxRow) {
          // Expand on the bottom
          const columnDiff = exploredMaxColumn - exploredMinColumn;
          const nextRow = exploredMaxRow + 1;
          for (let i = 0; i <= columnDiff; i++) {
            expandBoundary(map[nextRow][exploredMinColumn + i]);
          }
          exploredMaxRow = nextRow;
        }
      }
      const nodes = [tableNode];
      let lastRow = null;
      for (let i = minRow; i <= maxRow; i++) {
        for (let j = minColumn; j <= maxColumn; j++) {
          const {
            cell
          } = map[i][j];
          const currentRow = cell.getParent();
          if (!$isTableRowNode(currentRow)) {
            throw Error(`Expected TableCellNode parent to be a TableRowNode`);
          }
          if (currentRow !== lastRow) {
            nodes.push(currentRow);
          }
          nodes.push(cell, ...$getChildrenRecursively(cell));
          lastRow = currentRow;
        }
      }
      if (!isCurrentlyReadOnlyMode$1()) {
        this._cachedNodes = nodes;
      }
      return nodes;
    }
    getTextContent() {
      const nodes = this.getNodes().filter(node => $isTableCellNode(node));
      let textContent = '';
      for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        const row = node.__parent;
        const nextRow = (nodes[i + 1] || {}).__parent;
        textContent += node.getTextContent() + (nextRow !== row ? '\n' : '\t');
      }
      return textContent;
    }
  }
  function $isTableSelection(x) {
    return x instanceof TableSelection;
  }
  function $createTableSelection() {
    const anchor = $createPoint$1('root', 0, 'element');
    const focus = $createPoint$1('root', 0, 'element');
    return new TableSelection('root', anchor, focus);
  }
  function $getChildrenRecursively(node) {
    const nodes = [];
    const stack = [node];
    while (stack.length > 0) {
      const currentNode = stack.pop();
      if (!(currentNode !== undefined)) {
        throw Error(`Stack.length > 0; can't be undefined`);
      }
      if ($isElementNode$1(currentNode)) {
        stack.unshift(...currentNode.getChildren());
      }
      if (currentNode !== node) {
        nodes.push(currentNode);
      }
    }
    return nodes;
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  class TableObserver {
    constructor(editor, tableNodeKey) {
      this.isHighlightingCells = false;
      this.anchorX = -1;
      this.anchorY = -1;
      this.focusX = -1;
      this.focusY = -1;
      this.listenersToRemove = new Set();
      this.tableNodeKey = tableNodeKey;
      this.editor = editor;
      this.table = {
        columns: 0,
        domRows: [],
        rows: 0
      };
      this.tableSelection = null;
      this.anchorCellNodeKey = null;
      this.focusCellNodeKey = null;
      this.anchorCell = null;
      this.focusCell = null;
      this.hasHijackedSelectionStyles = false;
      this.trackTable();
      this.isSelecting = false;
    }
    getTable() {
      return this.table;
    }
    removeListeners() {
      Array.from(this.listenersToRemove).forEach(removeListener => removeListener());
    }
    trackTable() {
      const observer = new MutationObserver(records => {
        this.editor.update(() => {
          let gridNeedsRedraw = false;
          for (let i = 0; i < records.length; i++) {
            const record = records[i];
            const target = record.target;
            const nodeName = target.nodeName;
            if (nodeName === 'TABLE' || nodeName === 'TBODY' || nodeName === 'THEAD' || nodeName === 'TR') {
              gridNeedsRedraw = true;
              break;
            }
          }
          if (!gridNeedsRedraw) {
            return;
          }
          const tableElement = this.editor.getElementByKey(this.tableNodeKey);
          if (!tableElement) {
            throw new Error('Expected to find TableElement in DOM');
          }
          this.table = getTable(tableElement);
        });
      });
      this.editor.update(() => {
        const tableElement = this.editor.getElementByKey(this.tableNodeKey);
        if (!tableElement) {
          throw new Error('Expected to find TableElement in DOM');
        }
        this.table = getTable(tableElement);
        observer.observe(tableElement, {
          attributes: true,
          childList: true,
          subtree: true
        });
      });
    }
    clearHighlight() {
      const editor = this.editor;
      this.isHighlightingCells = false;
      this.anchorX = -1;
      this.anchorY = -1;
      this.focusX = -1;
      this.focusY = -1;
      this.tableSelection = null;
      this.anchorCellNodeKey = null;
      this.focusCellNodeKey = null;
      this.anchorCell = null;
      this.focusCell = null;
      this.hasHijackedSelectionStyles = false;
      this.enableHighlightStyle();
      editor.update(() => {
        const tableNode = $getNodeByKey$1(this.tableNodeKey);
        if (!$isTableNode(tableNode)) {
          throw new Error('Expected TableNode.');
        }
        const tableElement = editor.getElementByKey(this.tableNodeKey);
        if (!tableElement) {
          throw new Error('Expected to find TableElement in DOM');
        }
        const grid = getTable(tableElement);
        $updateDOMForSelection(editor, grid, null);
        $setSelection$1(null);
        editor.dispatchCommand(SELECTION_CHANGE_COMMAND$1, undefined);
      });
    }
    enableHighlightStyle() {
      const editor = this.editor;
      editor.update(() => {
        const tableElement = editor.getElementByKey(this.tableNodeKey);
        if (!tableElement) {
          throw new Error('Expected to find TableElement in DOM');
        }
        removeClassNamesFromElement$1(tableElement, editor._config.theme.tableSelection);
        tableElement.classList.remove('disable-selection');
        this.hasHijackedSelectionStyles = false;
      });
    }
    disableHighlightStyle() {
      const editor = this.editor;
      editor.update(() => {
        const tableElement = editor.getElementByKey(this.tableNodeKey);
        if (!tableElement) {
          throw new Error('Expected to find TableElement in DOM');
        }
        addClassNamesToElement$1(tableElement, editor._config.theme.tableSelection);
        this.hasHijackedSelectionStyles = true;
      });
    }
    updateTableTableSelection(selection) {
      if (selection !== null && selection.tableKey === this.tableNodeKey) {
        const editor = this.editor;
        this.tableSelection = selection;
        this.isHighlightingCells = true;
        this.disableHighlightStyle();
        $updateDOMForSelection(editor, this.table, this.tableSelection);
      } else if (selection == null) {
        this.clearHighlight();
      } else {
        this.tableNodeKey = selection.tableKey;
        this.updateTableTableSelection(selection);
      }
    }
    setFocusCellForSelection(cell, ignoreStart = false) {
      const editor = this.editor;
      editor.update(() => {
        const tableNode = $getNodeByKey$1(this.tableNodeKey);
        if (!$isTableNode(tableNode)) {
          throw new Error('Expected TableNode.');
        }
        const tableElement = editor.getElementByKey(this.tableNodeKey);
        if (!tableElement) {
          throw new Error('Expected to find TableElement in DOM');
        }
        const cellX = cell.x;
        const cellY = cell.y;
        this.focusCell = cell;
        if (this.anchorCell !== null) {
          const domSelection = getDOMSelection$2(editor._window);
          // Collapse the selection
          if (domSelection) {
            domSelection.setBaseAndExtent(this.anchorCell.elem, 0, this.focusCell.elem, 0);
          }
        }
        if (!this.isHighlightingCells && (this.anchorX !== cellX || this.anchorY !== cellY || ignoreStart)) {
          this.isHighlightingCells = true;
          this.disableHighlightStyle();
        } else if (cellX === this.focusX && cellY === this.focusY) {
          return;
        }
        this.focusX = cellX;
        this.focusY = cellY;
        if (this.isHighlightingCells) {
          const focusTableCellNode = $getNearestNodeFromDOMNode$1(cell.elem);
          if (this.tableSelection != null && this.anchorCellNodeKey != null && $isTableCellNode(focusTableCellNode) && tableNode.is($findTableNode(focusTableCellNode))) {
            const focusNodeKey = focusTableCellNode.getKey();
            this.tableSelection = this.tableSelection.clone() || $createTableSelection();
            this.focusCellNodeKey = focusNodeKey;
            this.tableSelection.set(this.tableNodeKey, this.anchorCellNodeKey, this.focusCellNodeKey);
            $setSelection$1(this.tableSelection);
            editor.dispatchCommand(SELECTION_CHANGE_COMMAND$1, undefined);
            $updateDOMForSelection(editor, this.table, this.tableSelection);
          }
        }
      });
    }
    setAnchorCellForSelection(cell) {
      this.isHighlightingCells = false;
      this.anchorCell = cell;
      this.anchorX = cell.x;
      this.anchorY = cell.y;
      this.editor.update(() => {
        const anchorTableCellNode = $getNearestNodeFromDOMNode$1(cell.elem);
        if ($isTableCellNode(anchorTableCellNode)) {
          const anchorNodeKey = anchorTableCellNode.getKey();
          this.tableSelection = this.tableSelection != null ? this.tableSelection.clone() : $createTableSelection();
          this.anchorCellNodeKey = anchorNodeKey;
        }
      });
    }
    formatCells(type) {
      this.editor.update(() => {
        const selection = $getSelection$1();
        if (!$isTableSelection(selection)) {
          {
            throw Error(`Expected grid selection`);
          }
        }
        const formatSelection = $createRangeSelection$1();
        const anchor = formatSelection.anchor;
        const focus = formatSelection.focus;
        selection.getNodes().forEach(cellNode => {
          if ($isTableCellNode(cellNode) && cellNode.getTextContentSize() !== 0) {
            anchor.set(cellNode.getKey(), 0, 'element');
            focus.set(cellNode.getKey(), cellNode.getChildrenSize(), 'element');
            formatSelection.formatText(type);
          }
        });
        $setSelection$1(selection);
        this.editor.dispatchCommand(SELECTION_CHANGE_COMMAND$1, undefined);
      });
    }
    clearText() {
      const editor = this.editor;
      editor.update(() => {
        const tableNode = $getNodeByKey$1(this.tableNodeKey);
        if (!$isTableNode(tableNode)) {
          throw new Error('Expected TableNode.');
        }
        const selection = $getSelection$1();
        if (!$isTableSelection(selection)) {
          {
            throw Error(`Expected grid selection`);
          }
        }
        const selectedNodes = selection.getNodes().filter($isTableCellNode);
        if (selectedNodes.length === this.table.columns * this.table.rows) {
          tableNode.selectPrevious();
          // Delete entire table
          tableNode.remove();
          const rootNode = $getRoot$1();
          rootNode.selectStart();
          return;
        }
        selectedNodes.forEach(cellNode => {
          if ($isElementNode$1(cellNode)) {
            const paragraphNode = $createParagraphNode$1();
            const textNode = $createTextNode$1();
            paragraphNode.append(textNode);
            cellNode.append(paragraphNode);
            cellNode.getChildren().forEach(child => {
              if (child !== paragraphNode) {
                child.remove();
              }
            });
          }
        });
        $updateDOMForSelection(editor, this.table, null);
        $setSelection$1(null);
        editor.dispatchCommand(SELECTION_CHANGE_COMMAND$1, undefined);
      });
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  const LEXICAL_ELEMENT_KEY = '__lexicalTableSelection';
  const getDOMSelection$2 = targetWindow => CAN_USE_DOM$6 ? (targetWindow || window).getSelection() : null;
  const isMouseDownOnEvent = event => {
    return (event.buttons & 1) === 1;
  };
  function applyTableHandlers(tableNode, tableElement, editor, hasTabHandler) {
    const rootElement = editor.getRootElement();
    if (rootElement === null) {
      throw new Error('No root element.');
    }
    const tableObserver = new TableObserver(editor, tableNode.getKey());
    const editorWindow = editor._window || window;
    attachTableObserverToTableElement(tableElement, tableObserver);
    const createMouseHandlers = () => {
      const onMouseUp = () => {
        tableObserver.isSelecting = false;
        editorWindow.removeEventListener('mouseup', onMouseUp);
        editorWindow.removeEventListener('mousemove', onMouseMove);
      };
      const onMouseMove = moveEvent => {
        // delaying mousemove handler to allow selectionchange handler from LexicalEvents.ts to be executed first
        setTimeout(() => {
          if (!isMouseDownOnEvent(moveEvent) && tableObserver.isSelecting) {
            tableObserver.isSelecting = false;
            editorWindow.removeEventListener('mouseup', onMouseUp);
            editorWindow.removeEventListener('mousemove', onMouseMove);
            return;
          }
          const focusCell = getDOMCellFromTarget(moveEvent.target);
          if (focusCell !== null && (tableObserver.anchorX !== focusCell.x || tableObserver.anchorY !== focusCell.y)) {
            moveEvent.preventDefault();
            tableObserver.setFocusCellForSelection(focusCell);
          }
        }, 0);
      };
      return {
        onMouseMove: onMouseMove,
        onMouseUp: onMouseUp
      };
    };
    tableElement.addEventListener('mousedown', event => {
      setTimeout(() => {
        if (event.button !== 0) {
          return;
        }
        if (!editorWindow) {
          return;
        }
        const anchorCell = getDOMCellFromTarget(event.target);
        if (anchorCell !== null) {
          stopEvent(event);
          tableObserver.setAnchorCellForSelection(anchorCell);
        }
        const {
          onMouseUp,
          onMouseMove
        } = createMouseHandlers();
        tableObserver.isSelecting = true;
        editorWindow.addEventListener('mouseup', onMouseUp);
        editorWindow.addEventListener('mousemove', onMouseMove);
      }, 0);
    });

    // Clear selection when clicking outside of dom.
    const mouseDownCallback = event => {
      if (event.button !== 0) {
        return;
      }
      editor.update(() => {
        const selection = $getSelection$1();
        const target = event.target;
        if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey && rootElement.contains(target)) {
          tableObserver.clearHighlight();
        }
      });
    };
    editorWindow.addEventListener('mousedown', mouseDownCallback);
    tableObserver.listenersToRemove.add(() => editorWindow.removeEventListener('mousedown', mouseDownCallback));
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_DOWN_COMMAND$1, event => $handleArrowKey(editor, event, 'down', tableNode, tableObserver), COMMAND_PRIORITY_HIGH$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_UP_COMMAND$1, event => $handleArrowKey(editor, event, 'up', tableNode, tableObserver), COMMAND_PRIORITY_HIGH$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_LEFT_COMMAND$1, event => $handleArrowKey(editor, event, 'backward', tableNode, tableObserver), COMMAND_PRIORITY_HIGH$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ARROW_RIGHT_COMMAND$1, event => $handleArrowKey(editor, event, 'forward', tableNode, tableObserver), COMMAND_PRIORITY_HIGH$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_ESCAPE_COMMAND$1, event => {
      const selection = $getSelection$1();
      if ($isTableSelection(selection)) {
        const focusCellNode = $findMatchingParent$1(selection.focus.getNode(), $isTableCellNode);
        if ($isTableCellNode(focusCellNode)) {
          stopEvent(event);
          focusCellNode.selectEnd();
          return true;
        }
      }
      return false;
    }, COMMAND_PRIORITY_HIGH$1));
    const deleteTextHandler = command => () => {
      const selection = $getSelection$1();
      if (!$isSelectionInTable(selection, tableNode)) {
        return false;
      }
      if ($isTableSelection(selection)) {
        tableObserver.clearText();
        return true;
      } else if ($isRangeSelection$1(selection)) {
        const tableCellNode = $findMatchingParent$1(selection.anchor.getNode(), n => $isTableCellNode(n));
        if (!$isTableCellNode(tableCellNode)) {
          return false;
        }
        const anchorNode = selection.anchor.getNode();
        const focusNode = selection.focus.getNode();
        const isAnchorInside = tableNode.isParentOf(anchorNode);
        const isFocusInside = tableNode.isParentOf(focusNode);
        const selectionContainsPartialTable = isAnchorInside && !isFocusInside || isFocusInside && !isAnchorInside;
        if (selectionContainsPartialTable) {
          tableObserver.clearText();
          return true;
        }
        const nearestElementNode = $findMatchingParent$1(selection.anchor.getNode(), n => $isElementNode$1(n));
        const topLevelCellElementNode = nearestElementNode && $findMatchingParent$1(nearestElementNode, n => $isElementNode$1(n) && $isTableCellNode(n.getParent()));
        if (!$isElementNode$1(topLevelCellElementNode) || !$isElementNode$1(nearestElementNode)) {
          return false;
        }
        if (command === DELETE_LINE_COMMAND$1 && topLevelCellElementNode.getPreviousSibling() === null) {
          // TODO: Fix Delete Line in Table Cells.
          return true;
        }
      }
      return false;
    };
    [DELETE_WORD_COMMAND$1, DELETE_LINE_COMMAND$1, DELETE_CHARACTER_COMMAND$1].forEach(command => {
      tableObserver.listenersToRemove.add(editor.registerCommand(command, deleteTextHandler(command), COMMAND_PRIORITY_CRITICAL$1));
    });
    const $deleteCellHandler = event => {
      const selection = $getSelection$1();
      if (!$isSelectionInTable(selection, tableNode)) {
        const nodes = selection ? selection.getNodes() : null;
        if (nodes) {
          const table = nodes.find(node => $isTableNode(node) && node.getKey() === tableObserver.tableNodeKey);
          if ($isTableNode(table)) {
            const parentNode = table.getParent();
            if (!parentNode) {
              return false;
            }
            table.remove();
          }
        }
        return false;
      }
      if ($isTableSelection(selection)) {
        if (event) {
          event.preventDefault();
          event.stopPropagation();
        }
        tableObserver.clearText();
        return true;
      } else if ($isRangeSelection$1(selection)) {
        const tableCellNode = $findMatchingParent$1(selection.anchor.getNode(), n => $isTableCellNode(n));
        if (!$isTableCellNode(tableCellNode)) {
          return false;
        }
      }
      return false;
    };
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_BACKSPACE_COMMAND$1, $deleteCellHandler, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(KEY_DELETE_COMMAND$1, $deleteCellHandler, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(CUT_COMMAND$1, event => {
      const selection = $getSelection$1();
      if (selection) {
        if (!($isTableSelection(selection) || $isRangeSelection$1(selection))) {
          return false;
        }
        // Copying to the clipboard is async so we must capture the data
        // before we delete it
        void copyToClipboard$1(editor, objectKlassEquals$1(event, ClipboardEvent) ? event : null, $getClipboardDataFromSelection$1(selection));
        const intercepted = $deleteCellHandler(event);
        if ($isRangeSelection$1(selection)) {
          selection.removeText();
        }
        return intercepted;
      }
      return false;
    }, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(FORMAT_TEXT_COMMAND$1, payload => {
      const selection = $getSelection$1();
      if (!$isSelectionInTable(selection, tableNode)) {
        return false;
      }
      if ($isTableSelection(selection)) {
        tableObserver.formatCells(payload);
        return true;
      } else if ($isRangeSelection$1(selection)) {
        const tableCellNode = $findMatchingParent$1(selection.anchor.getNode(), n => $isTableCellNode(n));
        if (!$isTableCellNode(tableCellNode)) {
          return false;
        }
      }
      return false;
    }, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(FORMAT_ELEMENT_COMMAND$1, formatType => {
      const selection = $getSelection$1();
      if (!$isTableSelection(selection) || !$isSelectionInTable(selection, tableNode)) {
        return false;
      }
      const anchorNode = selection.anchor.getNode();
      const focusNode = selection.focus.getNode();
      if (!$isTableCellNode(anchorNode) || !$isTableCellNode(focusNode)) {
        return false;
      }
      const [tableMap, anchorCell, focusCell] = $computeTableMap(tableNode, anchorNode, focusNode);
      const maxRow = Math.max(anchorCell.startRow, focusCell.startRow);
      const maxColumn = Math.max(anchorCell.startColumn, focusCell.startColumn);
      const minRow = Math.min(anchorCell.startRow, focusCell.startRow);
      const minColumn = Math.min(anchorCell.startColumn, focusCell.startColumn);
      for (let i = minRow; i <= maxRow; i++) {
        for (let j = minColumn; j <= maxColumn; j++) {
          const cell = tableMap[i][j].cell;
          cell.setFormat(formatType);
          const cellChildren = cell.getChildren();
          for (let k = 0; k < cellChildren.length; k++) {
            const child = cellChildren[k];
            if ($isElementNode$1(child) && !child.isInline()) {
              child.setFormat(formatType);
            }
          }
        }
      }
      return true;
    }, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(CONTROLLED_TEXT_INSERTION_COMMAND$1, payload => {
      const selection = $getSelection$1();
      if (!$isSelectionInTable(selection, tableNode)) {
        return false;
      }
      if ($isTableSelection(selection)) {
        tableObserver.clearHighlight();
        return false;
      } else if ($isRangeSelection$1(selection)) {
        const tableCellNode = $findMatchingParent$1(selection.anchor.getNode(), n => $isTableCellNode(n));
        if (!$isTableCellNode(tableCellNode)) {
          return false;
        }
        if (typeof payload === 'string') {
          const edgePosition = $getTableEdgeCursorPosition(editor, selection, tableNode);
          if (edgePosition) {
            $insertParagraphAtTableEdge(edgePosition, tableNode, [$createTextNode$1(payload)]);
            return true;
          }
        }
      }
      return false;
    }, COMMAND_PRIORITY_CRITICAL$1));
    if (hasTabHandler) {
      tableObserver.listenersToRemove.add(editor.registerCommand(KEY_TAB_COMMAND$1, event => {
        const selection = $getSelection$1();
        if (!$isRangeSelection$1(selection) || !selection.isCollapsed() || !$isSelectionInTable(selection, tableNode)) {
          return false;
        }
        const tableCellNode = $findCellNode(selection.anchor.getNode());
        if (tableCellNode === null) {
          return false;
        }
        stopEvent(event);
        const currentCords = tableNode.getCordsFromCellNode(tableCellNode, tableObserver.table);
        selectTableNodeInDirection(tableObserver, tableNode, currentCords.x, currentCords.y, !event.shiftKey ? 'forward' : 'backward');
        return true;
      }, COMMAND_PRIORITY_CRITICAL$1));
    }
    tableObserver.listenersToRemove.add(editor.registerCommand(FOCUS_COMMAND$1, payload => {
      return tableNode.isSelected();
    }, COMMAND_PRIORITY_HIGH$1));
    function getObserverCellFromCellNode(tableCellNode) {
      const currentCords = tableNode.getCordsFromCellNode(tableCellNode, tableObserver.table);
      return tableNode.getDOMCellFromCordsOrThrow(currentCords.x, currentCords.y, tableObserver.table);
    }
    tableObserver.listenersToRemove.add(editor.registerCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND$1, selectionPayload => {
      const {
        nodes,
        selection
      } = selectionPayload;
      const anchorAndFocus = selection.getStartEndPoints();
      const isTableSelection = $isTableSelection(selection);
      const isRangeSelection = $isRangeSelection$1(selection);
      const isSelectionInsideOfGrid = isRangeSelection && $findMatchingParent$1(selection.anchor.getNode(), n => $isTableCellNode(n)) !== null && $findMatchingParent$1(selection.focus.getNode(), n => $isTableCellNode(n)) !== null || isTableSelection;
      if (nodes.length !== 1 || !$isTableNode(nodes[0]) || !isSelectionInsideOfGrid || anchorAndFocus === null) {
        return false;
      }
      const [anchor] = anchorAndFocus;
      const newGrid = nodes[0];
      const newGridRows = newGrid.getChildren();
      const newColumnCount = newGrid.getFirstChildOrThrow().getChildrenSize();
      const newRowCount = newGrid.getChildrenSize();
      const gridCellNode = $findMatchingParent$1(anchor.getNode(), n => $isTableCellNode(n));
      const gridRowNode = gridCellNode && $findMatchingParent$1(gridCellNode, n => $isTableRowNode(n));
      const gridNode = gridRowNode && $findMatchingParent$1(gridRowNode, n => $isTableNode(n));
      if (!$isTableCellNode(gridCellNode) || !$isTableRowNode(gridRowNode) || !$isTableNode(gridNode)) {
        return false;
      }
      const startY = gridRowNode.getIndexWithinParent();
      const stopY = Math.min(gridNode.getChildrenSize() - 1, startY + newRowCount - 1);
      const startX = gridCellNode.getIndexWithinParent();
      const stopX = Math.min(gridRowNode.getChildrenSize() - 1, startX + newColumnCount - 1);
      const fromX = Math.min(startX, stopX);
      const fromY = Math.min(startY, stopY);
      const toX = Math.max(startX, stopX);
      const toY = Math.max(startY, stopY);
      const gridRowNodes = gridNode.getChildren();
      let newRowIdx = 0;
      for (let r = fromY; r <= toY; r++) {
        const currentGridRowNode = gridRowNodes[r];
        if (!$isTableRowNode(currentGridRowNode)) {
          return false;
        }
        const newGridRowNode = newGridRows[newRowIdx];
        if (!$isTableRowNode(newGridRowNode)) {
          return false;
        }
        const gridCellNodes = currentGridRowNode.getChildren();
        const newGridCellNodes = newGridRowNode.getChildren();
        let newColumnIdx = 0;
        for (let c = fromX; c <= toX; c++) {
          const currentGridCellNode = gridCellNodes[c];
          if (!$isTableCellNode(currentGridCellNode)) {
            return false;
          }
          const newGridCellNode = newGridCellNodes[newColumnIdx];
          if (!$isTableCellNode(newGridCellNode)) {
            return false;
          }
          const originalChildren = currentGridCellNode.getChildren();
          newGridCellNode.getChildren().forEach(child => {
            if ($isTextNode$1(child)) {
              const paragraphNode = $createParagraphNode$1();
              paragraphNode.append(child);
              currentGridCellNode.append(child);
            } else {
              currentGridCellNode.append(child);
            }
          });
          originalChildren.forEach(n => n.remove());
          newColumnIdx++;
        }
        newRowIdx++;
      }
      return true;
    }, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(SELECTION_CHANGE_COMMAND$1, () => {
      const selection = $getSelection$1();
      const prevSelection = $getPreviousSelection$1();
      if ($isRangeSelection$1(selection)) {
        const {
          anchor,
          focus
        } = selection;
        const anchorNode = anchor.getNode();
        const focusNode = focus.getNode();
        // Using explicit comparison with table node to ensure it's not a nested table
        // as in that case we'll leave selection resolving to that table
        const anchorCellNode = $findCellNode(anchorNode);
        const focusCellNode = $findCellNode(focusNode);
        const isAnchorInside = !!(anchorCellNode && tableNode.is($findTableNode(anchorCellNode)));
        const isFocusInside = !!(focusCellNode && tableNode.is($findTableNode(focusCellNode)));
        const isPartialyWithinTable = isAnchorInside !== isFocusInside;
        const isWithinTable = isAnchorInside && isFocusInside;
        const isBackward = selection.isBackward();
        if (isPartialyWithinTable) {
          const newSelection = selection.clone();
          if (isFocusInside) {
            const [tableMap] = $computeTableMap(tableNode, focusCellNode, focusCellNode);
            const firstCell = tableMap[0][0].cell;
            const lastCell = tableMap[tableMap.length - 1].at(-1).cell;
            newSelection.focus.set(isBackward ? firstCell.getKey() : lastCell.getKey(), isBackward ? firstCell.getChildrenSize() : lastCell.getChildrenSize(), 'element');
          }
          $setSelection$1(newSelection);
          $addHighlightStyleToTable(editor, tableObserver);
        } else if (isWithinTable) {
          // Handle case when selection spans across multiple cells but still
          // has range selection, then we convert it into grid selection
          if (!anchorCellNode.is(focusCellNode)) {
            tableObserver.setAnchorCellForSelection(getObserverCellFromCellNode(anchorCellNode));
            tableObserver.setFocusCellForSelection(getObserverCellFromCellNode(focusCellNode), true);
            if (!tableObserver.isSelecting) {
              setTimeout(() => {
                const {
                  onMouseUp,
                  onMouseMove
                } = createMouseHandlers();
                tableObserver.isSelecting = true;
                editorWindow.addEventListener('mouseup', onMouseUp);
                editorWindow.addEventListener('mousemove', onMouseMove);
              }, 0);
            }
          }
        }
      } else if (selection && $isTableSelection(selection) && selection.is(prevSelection) && selection.tableKey === tableNode.getKey()) {
        // if selection goes outside of the table we need to change it to Range selection
        const domSelection = getDOMSelection$2(editor._window);
        if (domSelection && domSelection.anchorNode && domSelection.focusNode) {
          const focusNode = $getNearestNodeFromDOMNode$1(domSelection.focusNode);
          const isFocusOutside = focusNode && !tableNode.is($findTableNode(focusNode));
          const anchorNode = $getNearestNodeFromDOMNode$1(domSelection.anchorNode);
          const isAnchorInside = anchorNode && tableNode.is($findTableNode(anchorNode));
          if (isFocusOutside && isAnchorInside && domSelection.rangeCount > 0) {
            const newSelection = $createRangeSelectionFromDom$1(domSelection, editor);
            if (newSelection) {
              newSelection.anchor.set(tableNode.getKey(), selection.isBackward() ? tableNode.getChildrenSize() : 0, 'element');
              domSelection.removeAllRanges();
              $setSelection$1(newSelection);
            }
          }
        }
      }
      if (selection && !selection.is(prevSelection) && ($isTableSelection(selection) || $isTableSelection(prevSelection)) && tableObserver.tableSelection && !tableObserver.tableSelection.is(prevSelection)) {
        if ($isTableSelection(selection) && selection.tableKey === tableObserver.tableNodeKey) {
          tableObserver.updateTableTableSelection(selection);
        } else if (!$isTableSelection(selection) && $isTableSelection(prevSelection) && prevSelection.tableKey === tableObserver.tableNodeKey) {
          tableObserver.updateTableTableSelection(null);
        }
        return false;
      }
      if (tableObserver.hasHijackedSelectionStyles && !tableNode.isSelected()) {
        $removeHighlightStyleToTable(editor, tableObserver);
      } else if (!tableObserver.hasHijackedSelectionStyles && tableNode.isSelected()) {
        $addHighlightStyleToTable(editor, tableObserver);
      }
      return false;
    }, COMMAND_PRIORITY_CRITICAL$1));
    tableObserver.listenersToRemove.add(editor.registerCommand(INSERT_PARAGRAPH_COMMAND$1, () => {
      const selection = $getSelection$1();
      if (!$isRangeSelection$1(selection) || !selection.isCollapsed() || !$isSelectionInTable(selection, tableNode)) {
        return false;
      }
      const edgePosition = $getTableEdgeCursorPosition(editor, selection, tableNode);
      if (edgePosition) {
        $insertParagraphAtTableEdge(edgePosition, tableNode);
        return true;
      }
      return false;
    }, COMMAND_PRIORITY_CRITICAL$1));
    return tableObserver;
  }
  function attachTableObserverToTableElement(tableElement, tableObserver) {
    tableElement[LEXICAL_ELEMENT_KEY] = tableObserver;
  }
  function getTableObserverFromTableElement(tableElement) {
    return tableElement[LEXICAL_ELEMENT_KEY];
  }
  function getDOMCellFromTarget(node) {
    let currentNode = node;
    while (currentNode != null) {
      const nodeName = currentNode.nodeName;
      if (nodeName === 'TD' || nodeName === 'TH') {
        // @ts-expect-error: internal field
        const cell = currentNode._cell;
        if (cell === undefined) {
          return null;
        }
        return cell;
      }
      currentNode = currentNode.parentNode;
    }
    return null;
  }
  function getTable(tableElement) {
    const domRows = [];
    const grid = {
      columns: 0,
      domRows,
      rows: 0
    };
    let currentNode = tableElement.firstChild;
    let x = 0;
    let y = 0;
    domRows.length = 0;
    while (currentNode != null) {
      const nodeMame = currentNode.nodeName;
      if (nodeMame === 'TD' || nodeMame === 'TH') {
        const elem = currentNode;
        const cell = {
          elem,
          hasBackgroundColor: elem.style.backgroundColor !== '',
          highlighted: false,
          x,
          y
        };

        // @ts-expect-error: internal field
        currentNode._cell = cell;
        let row = domRows[y];
        if (row === undefined) {
          row = domRows[y] = [];
        }
        row[x] = cell;
      } else {
        const child = currentNode.firstChild;
        if (child != null) {
          currentNode = child;
          continue;
        }
      }
      const sibling = currentNode.nextSibling;
      if (sibling != null) {
        x++;
        currentNode = sibling;
        continue;
      }
      const parent = currentNode.parentNode;
      if (parent != null) {
        const parentSibling = parent.nextSibling;
        if (parentSibling == null) {
          break;
        }
        y++;
        x = 0;
        currentNode = parentSibling;
      }
    }
    grid.columns = x + 1;
    grid.rows = y + 1;
    return grid;
  }
  function $updateDOMForSelection(editor, table, selection) {
    const selectedCellNodes = new Set(selection ? selection.getNodes() : []);
    $forEachTableCell(table, (cell, lexicalNode) => {
      const elem = cell.elem;
      if (selectedCellNodes.has(lexicalNode)) {
        cell.highlighted = true;
        $addHighlightToDOM(editor, cell);
      } else {
        cell.highlighted = false;
        $removeHighlightFromDOM(editor, cell);
        if (!elem.getAttribute('style')) {
          elem.removeAttribute('style');
        }
      }
    });
  }
  function $forEachTableCell(grid, cb) {
    const {
      domRows
    } = grid;
    for (let y = 0; y < domRows.length; y++) {
      const row = domRows[y];
      if (!row) {
        continue;
      }
      for (let x = 0; x < row.length; x++) {
        const cell = row[x];
        if (!cell) {
          continue;
        }
        const lexicalNode = $getNearestNodeFromDOMNode$1(cell.elem);
        if (lexicalNode !== null) {
          cb(cell, lexicalNode, {
            x,
            y
          });
        }
      }
    }
  }
  function $addHighlightStyleToTable(editor, tableSelection) {
    tableSelection.disableHighlightStyle();
    $forEachTableCell(tableSelection.table, cell => {
      cell.highlighted = true;
      $addHighlightToDOM(editor, cell);
    });
  }
  function $removeHighlightStyleToTable(editor, tableObserver) {
    tableObserver.enableHighlightStyle();
    $forEachTableCell(tableObserver.table, cell => {
      const elem = cell.elem;
      cell.highlighted = false;
      $removeHighlightFromDOM(editor, cell);
      if (!elem.getAttribute('style')) {
        elem.removeAttribute('style');
      }
    });
  }
  const selectTableNodeInDirection = (tableObserver, tableNode, x, y, direction) => {
    const isForward = direction === 'forward';
    switch (direction) {
      case 'backward':
      case 'forward':
        if (x !== (isForward ? tableObserver.table.columns - 1 : 0)) {
          selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(x + (isForward ? 1 : -1), y, tableObserver.table), isForward);
        } else {
          if (y !== (isForward ? tableObserver.table.rows - 1 : 0)) {
            selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(isForward ? 0 : tableObserver.table.columns - 1, y + (isForward ? 1 : -1), tableObserver.table), isForward);
          } else if (!isForward) {
            tableNode.selectPrevious();
          } else {
            tableNode.selectNext();
          }
        }
        return true;
      case 'up':
        if (y !== 0) {
          selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(x, y - 1, tableObserver.table), false);
        } else {
          tableNode.selectPrevious();
        }
        return true;
      case 'down':
        if (y !== tableObserver.table.rows - 1) {
          selectTableCellNode(tableNode.getCellNodeFromCordsOrThrow(x, y + 1, tableObserver.table), true);
        } else {
          tableNode.selectNext();
        }
        return true;
      default:
        return false;
    }
  };
  const adjustFocusNodeInDirection = (tableObserver, tableNode, x, y, direction) => {
    const isForward = direction === 'forward';
    switch (direction) {
      case 'backward':
      case 'forward':
        if (x !== (isForward ? tableObserver.table.columns - 1 : 0)) {
          tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x + (isForward ? 1 : -1), y, tableObserver.table));
        }
        return true;
      case 'up':
        if (y !== 0) {
          tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x, y - 1, tableObserver.table));
          return true;
        } else {
          return false;
        }
      case 'down':
        if (y !== tableObserver.table.rows - 1) {
          tableObserver.setFocusCellForSelection(tableNode.getDOMCellFromCordsOrThrow(x, y + 1, tableObserver.table));
          return true;
        } else {
          return false;
        }
      default:
        return false;
    }
  };
  function $isSelectionInTable(selection, tableNode) {
    if ($isRangeSelection$1(selection) || $isTableSelection(selection)) {
      const isAnchorInside = tableNode.isParentOf(selection.anchor.getNode());
      const isFocusInside = tableNode.isParentOf(selection.focus.getNode());
      return isAnchorInside && isFocusInside;
    }
    return false;
  }
  function selectTableCellNode(tableCell, fromStart) {
    if (fromStart) {
      tableCell.selectStart();
    } else {
      tableCell.selectEnd();
    }
  }
  const BROWSER_BLUE_RGB = '172,206,247';
  function $addHighlightToDOM(editor, cell) {
    const element = cell.elem;
    const node = $getNearestNodeFromDOMNode$1(element);
    if (!$isTableCellNode(node)) {
      throw Error(`Expected to find LexicalNode from Table Cell DOMNode`);
    }
    const backgroundColor = node.getBackgroundColor();
    if (backgroundColor === null) {
      element.style.setProperty('background-color', `rgb(${BROWSER_BLUE_RGB})`);
    } else {
      element.style.setProperty('background-image', `linear-gradient(to right, rgba(${BROWSER_BLUE_RGB},0.85), rgba(${BROWSER_BLUE_RGB},0.85))`);
    }
    element.style.setProperty('caret-color', 'transparent');
  }
  function $removeHighlightFromDOM(editor, cell) {
    const element = cell.elem;
    const node = $getNearestNodeFromDOMNode$1(element);
    if (!$isTableCellNode(node)) {
      throw Error(`Expected to find LexicalNode from Table Cell DOMNode`);
    }
    const backgroundColor = node.getBackgroundColor();
    if (backgroundColor === null) {
      element.style.removeProperty('background-color');
    }
    element.style.removeProperty('background-image');
    element.style.removeProperty('caret-color');
  }
  function $findCellNode(node) {
    const cellNode = $findMatchingParent$1(node, $isTableCellNode);
    return $isTableCellNode(cellNode) ? cellNode : null;
  }
  function $findTableNode(node) {
    const tableNode = $findMatchingParent$1(node, $isTableNode);
    return $isTableNode(tableNode) ? tableNode : null;
  }
  function $handleArrowKey(editor, event, direction, tableNode, tableObserver) {
    if ((direction === 'up' || direction === 'down') && isTypeaheadMenuInView(editor)) {
      return false;
    }
    const selection = $getSelection$1();
    if (!$isSelectionInTable(selection, tableNode)) {
      if ($isRangeSelection$1(selection)) {
        if (selection.isCollapsed() && direction === 'backward') {
          const anchorType = selection.anchor.type;
          const anchorOffset = selection.anchor.offset;
          if (anchorType !== 'element' && !(anchorType === 'text' && anchorOffset === 0)) {
            return false;
          }
          const anchorNode = selection.anchor.getNode();
          if (!anchorNode) {
            return false;
          }
          const parentNode = $findMatchingParent$1(anchorNode, n => $isElementNode$1(n) && !n.isInline());
          if (!parentNode) {
            return false;
          }
          const siblingNode = parentNode.getPreviousSibling();
          if (!siblingNode || !$isTableNode(siblingNode)) {
            return false;
          }
          stopEvent(event);
          siblingNode.selectEnd();
          return true;
        } else if (event.shiftKey && (direction === 'up' || direction === 'down')) {
          const focusNode = selection.focus.getNode();
          if ($isRootOrShadowRoot$1(focusNode)) {
            const selectedNode = selection.getNodes()[0];
            if (selectedNode) {
              const tableCellNode = $findMatchingParent$1(selectedNode, $isTableCellNode);
              if (tableCellNode && tableNode.isParentOf(tableCellNode)) {
                const firstDescendant = tableNode.getFirstDescendant();
                const lastDescendant = tableNode.getLastDescendant();
                if (!firstDescendant || !lastDescendant) {
                  return false;
                }
                const [firstCellNode] = $getNodeTriplet(firstDescendant);
                const [lastCellNode] = $getNodeTriplet(lastDescendant);
                const firstCellCoords = tableNode.getCordsFromCellNode(firstCellNode, tableObserver.table);
                const lastCellCoords = tableNode.getCordsFromCellNode(lastCellNode, tableObserver.table);
                const firstCellDOM = tableNode.getDOMCellFromCordsOrThrow(firstCellCoords.x, firstCellCoords.y, tableObserver.table);
                const lastCellDOM = tableNode.getDOMCellFromCordsOrThrow(lastCellCoords.x, lastCellCoords.y, tableObserver.table);
                tableObserver.setAnchorCellForSelection(firstCellDOM);
                tableObserver.setFocusCellForSelection(lastCellDOM, true);
                return true;
              }
            }
            return false;
          } else {
            const focusParentNode = $findMatchingParent$1(focusNode, n => $isElementNode$1(n) && !n.isInline());
            if (!focusParentNode) {
              return false;
            }
            const sibling = direction === 'down' ? focusParentNode.getNextSibling() : focusParentNode.getPreviousSibling();
            if ($isTableNode(sibling) && tableObserver.tableNodeKey === sibling.getKey()) {
              const firstDescendant = sibling.getFirstDescendant();
              const lastDescendant = sibling.getLastDescendant();
              if (!firstDescendant || !lastDescendant) {
                return false;
              }
              const [firstCellNode] = $getNodeTriplet(firstDescendant);
              const [lastCellNode] = $getNodeTriplet(lastDescendant);
              const newSelection = selection.clone();
              newSelection.focus.set((direction === 'up' ? firstCellNode : lastCellNode).getKey(), direction === 'up' ? 0 : lastCellNode.getChildrenSize(), 'element');
              $setSelection$1(newSelection);
              return true;
            }
          }
        }
      }
      return false;
    }
    if ($isRangeSelection$1(selection) && selection.isCollapsed()) {
      const {
        anchor,
        focus
      } = selection;
      const anchorCellNode = $findMatchingParent$1(anchor.getNode(), $isTableCellNode);
      const focusCellNode = $findMatchingParent$1(focus.getNode(), $isTableCellNode);
      if (!$isTableCellNode(anchorCellNode) || !anchorCellNode.is(focusCellNode)) {
        return false;
      }
      const anchorCellTable = $findTableNode(anchorCellNode);
      if (anchorCellTable !== tableNode && anchorCellTable != null) {
        const anchorCellTableElement = editor.getElementByKey(anchorCellTable.getKey());
        if (anchorCellTableElement != null) {
          tableObserver.table = getTable(anchorCellTableElement);
          return $handleArrowKey(editor, event, direction, anchorCellTable, tableObserver);
        }
      }
      if (direction === 'backward' || direction === 'forward') {
        const anchorType = anchor.type;
        const anchorOffset = anchor.offset;
        const anchorNode = anchor.getNode();
        if (!anchorNode) {
          return false;
        }
        const selectedNodes = selection.getNodes();
        if (selectedNodes.length === 1 && $isDecoratorNode$1(selectedNodes[0])) {
          return false;
        }
        if (isExitingTableAnchor(anchorType, anchorOffset, anchorNode, direction)) {
          return $handleTableExit(event, anchorNode, tableNode, direction);
        }
        return false;
      }
      const anchorCellDom = editor.getElementByKey(anchorCellNode.__key);
      const anchorDOM = editor.getElementByKey(anchor.key);
      if (anchorDOM == null || anchorCellDom == null) {
        return false;
      }
      let edgeSelectionRect;
      if (anchor.type === 'element') {
        edgeSelectionRect = anchorDOM.getBoundingClientRect();
      } else {
        const domSelection = window.getSelection();
        if (domSelection === null || domSelection.rangeCount === 0) {
          return false;
        }
        const range = domSelection.getRangeAt(0);
        edgeSelectionRect = range.getBoundingClientRect();
      }
      const edgeChild = direction === 'up' ? anchorCellNode.getFirstChild() : anchorCellNode.getLastChild();
      if (edgeChild == null) {
        return false;
      }
      const edgeChildDOM = editor.getElementByKey(edgeChild.__key);
      if (edgeChildDOM == null) {
        return false;
      }
      const edgeRect = edgeChildDOM.getBoundingClientRect();
      const isExiting = direction === 'up' ? edgeRect.top > edgeSelectionRect.top - edgeSelectionRect.height : edgeSelectionRect.bottom + edgeSelectionRect.height > edgeRect.bottom;
      if (isExiting) {
        stopEvent(event);
        const cords = tableNode.getCordsFromCellNode(anchorCellNode, tableObserver.table);
        if (event.shiftKey) {
          const cell = tableNode.getDOMCellFromCordsOrThrow(cords.x, cords.y, tableObserver.table);
          tableObserver.setAnchorCellForSelection(cell);
          tableObserver.setFocusCellForSelection(cell, true);
        } else {
          return selectTableNodeInDirection(tableObserver, tableNode, cords.x, cords.y, direction);
        }
        return true;
      }
    } else if ($isTableSelection(selection)) {
      const {
        anchor,
        focus
      } = selection;
      const anchorCellNode = $findMatchingParent$1(anchor.getNode(), $isTableCellNode);
      const focusCellNode = $findMatchingParent$1(focus.getNode(), $isTableCellNode);
      const [tableNodeFromSelection] = selection.getNodes();
      const tableElement = editor.getElementByKey(tableNodeFromSelection.getKey());
      if (!$isTableCellNode(anchorCellNode) || !$isTableCellNode(focusCellNode) || !$isTableNode(tableNodeFromSelection) || tableElement == null) {
        return false;
      }
      tableObserver.updateTableTableSelection(selection);
      const grid = getTable(tableElement);
      const cordsAnchor = tableNode.getCordsFromCellNode(anchorCellNode, grid);
      const anchorCell = tableNode.getDOMCellFromCordsOrThrow(cordsAnchor.x, cordsAnchor.y, grid);
      tableObserver.setAnchorCellForSelection(anchorCell);
      stopEvent(event);
      if (event.shiftKey) {
        const cords = tableNode.getCordsFromCellNode(focusCellNode, grid);
        return adjustFocusNodeInDirection(tableObserver, tableNodeFromSelection, cords.x, cords.y, direction);
      } else {
        focusCellNode.selectEnd();
      }
      return true;
    }
    return false;
  }
  function stopEvent(event) {
    event.preventDefault();
    event.stopImmediatePropagation();
    event.stopPropagation();
  }
  function isTypeaheadMenuInView(editor) {
    // There is no inbuilt way to check if the component picker is in view
    // but we can check if the root DOM element has the aria-controls attribute "typeahead-menu".
    const root = editor.getRootElement();
    if (!root) {
      return false;
    }
    return root.hasAttribute('aria-controls') && root.getAttribute('aria-controls') === 'typeahead-menu';
  }
  function isExitingTableAnchor(type, offset, anchorNode, direction) {
    return isExitingTableElementAnchor(type, anchorNode, direction) || $isExitingTableTextAnchor(type, offset, anchorNode, direction);
  }
  function isExitingTableElementAnchor(type, anchorNode, direction) {
    return type === 'element' && (direction === 'backward' ? anchorNode.getPreviousSibling() === null : anchorNode.getNextSibling() === null);
  }
  function $isExitingTableTextAnchor(type, offset, anchorNode, direction) {
    const parentNode = $findMatchingParent$1(anchorNode, n => $isElementNode$1(n) && !n.isInline());
    if (!parentNode) {
      return false;
    }
    const hasValidOffset = direction === 'backward' ? offset === 0 : offset === anchorNode.getTextContentSize();
    return type === 'text' && hasValidOffset && (direction === 'backward' ? parentNode.getPreviousSibling() === null : parentNode.getNextSibling() === null);
  }
  function $handleTableExit(event, anchorNode, tableNode, direction) {
    const anchorCellNode = $findMatchingParent$1(anchorNode, $isTableCellNode);
    if (!$isTableCellNode(anchorCellNode)) {
      return false;
    }
    const [tableMap, cellValue] = $computeTableMap(tableNode, anchorCellNode, anchorCellNode);
    if (!isExitingCell(tableMap, cellValue, direction)) {
      return false;
    }
    const toNode = $getExitingToNode(anchorNode, direction, tableNode);
    if (!toNode || $isTableNode(toNode)) {
      return false;
    }
    stopEvent(event);
    if (direction === 'backward') {
      toNode.selectEnd();
    } else {
      toNode.selectStart();
    }
    return true;
  }
  function isExitingCell(tableMap, cellValue, direction) {
    const firstCell = tableMap[0][0];
    const lastCell = tableMap[tableMap.length - 1][tableMap[0].length - 1];
    const {
      startColumn,
      startRow
    } = cellValue;
    return direction === 'backward' ? startColumn === firstCell.startColumn && startRow === firstCell.startRow : startColumn === lastCell.startColumn && startRow === lastCell.startRow;
  }
  function $getExitingToNode(anchorNode, direction, tableNode) {
    const parentNode = $findMatchingParent$1(anchorNode, n => $isElementNode$1(n) && !n.isInline());
    if (!parentNode) {
      return undefined;
    }
    const anchorSibling = direction === 'backward' ? parentNode.getPreviousSibling() : parentNode.getNextSibling();
    return anchorSibling && $isTableNode(anchorSibling) ? anchorSibling : direction === 'backward' ? tableNode.getPreviousSibling() : tableNode.getNextSibling();
  }
  function $insertParagraphAtTableEdge(edgePosition, tableNode, children) {
    const paragraphNode = $createParagraphNode$1();
    if (edgePosition === 'first') {
      tableNode.insertBefore(paragraphNode);
    } else {
      tableNode.insertAfter(paragraphNode);
    }
    paragraphNode.append(...(children || []));
    paragraphNode.selectEnd();
  }
  function $getTableEdgeCursorPosition(editor, selection, tableNode) {
    const tableNodeParent = tableNode.getParent();
    if (!tableNodeParent) {
      return undefined;
    }
    const tableNodeParentDOM = editor.getElementByKey(tableNodeParent.getKey());
    if (!tableNodeParentDOM) {
      return undefined;
    }

    // TODO: Add support for nested tables
    const domSelection = window.getSelection();
    if (!domSelection || domSelection.anchorNode !== tableNodeParentDOM) {
      return undefined;
    }
    const anchorCellNode = $findMatchingParent$1(selection.anchor.getNode(), n => $isTableCellNode(n));
    if (!anchorCellNode) {
      return undefined;
    }
    const parentTable = $findMatchingParent$1(anchorCellNode, n => $isTableNode(n));
    if (!$isTableNode(parentTable) || !parentTable.is(tableNode)) {
      return undefined;
    }
    const [tableMap, cellValue] = $computeTableMap(tableNode, anchorCellNode, anchorCellNode);
    const firstCell = tableMap[0][0];
    const lastCell = tableMap[tableMap.length - 1][tableMap[0].length - 1];
    const {
      startRow,
      startColumn
    } = cellValue;
    const isAtFirstCell = startRow === firstCell.startRow && startColumn === firstCell.startColumn;
    const isAtLastCell = startRow === lastCell.startRow && startColumn === lastCell.startColumn;
    if (isAtFirstCell) {
      return 'first';
    } else if (isAtLastCell) {
      return 'last';
    } else {
      return undefined;
    }
  }

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */

  /** @noInheritDoc */
  class TableNode extends ElementNode$1 {
    static getType() {
      return 'table';
    }
    static clone(node) {
      return new TableNode(node.__key);
    }
    static importDOM() {
      return {
        table: _node => ({
          conversion: $convertTableElement,
          priority: 1
        })
      };
    }
    static importJSON(_serializedNode) {
      return $createTableNode();
    }
    constructor(key) {
      super(key);
    }
    exportJSON() {
      return {
        ...super.exportJSON(),
        type: 'table',
        version: 1
      };
    }
    createDOM(config, editor) {
      const tableElement = document.createElement('table');
      addClassNamesToElement$1(tableElement, config.theme.table);
      return tableElement;
    }
    updateDOM() {
      return false;
    }
    exportDOM(editor) {
      return {
        ...super.exportDOM(editor),
        after: tableElement => {
          if (tableElement) {
            const newElement = tableElement.cloneNode();
            const colGroup = document.createElement('colgroup');
            const tBody = document.createElement('tbody');
            if (isHTMLElement$2(tableElement)) {
              tBody.append(...tableElement.children);
            }
            const firstRow = this.getFirstChildOrThrow();
            if (!$isTableRowNode(firstRow)) {
              throw new Error('Expected to find row node.');
            }
            const colCount = firstRow.getChildrenSize();
            for (let i = 0; i < colCount; i++) {
              const col = document.createElement('col');
              colGroup.append(col);
            }
            newElement.replaceChildren(colGroup, tBody);
            return newElement;
          }
        }
      };
    }
    canBeEmpty() {
      return false;
    }
    isShadowRoot() {
      return true;
    }
    getCordsFromCellNode(tableCellNode, table) {
      const {
        rows,
        domRows
      } = table;
      for (let y = 0; y < rows; y++) {
        const row = domRows[y];
        if (row == null) {
          continue;
        }
        const x = row.findIndex(cell => {
          if (!cell) {
            return;
          }
          const {
            elem
          } = cell;
          const cellNode = $getNearestNodeFromDOMNode$1(elem);
          return cellNode === tableCellNode;
        });
        if (x !== -1) {
          return {
            x,
            y
          };
        }
      }
      throw new Error('Cell not found in table.');
    }
    getDOMCellFromCords(x, y, table) {
      const {
        domRows
      } = table;
      const row = domRows[y];
      if (row == null) {
        return null;
      }
      const index = x < row.length ? x : row.length - 1;
      const cell = row[index];
      if (cell == null) {
        return null;
      }
      return cell;
    }
    getDOMCellFromCordsOrThrow(x, y, table) {
      const cell = this.getDOMCellFromCords(x, y, table);
      if (!cell) {
        throw new Error('Cell not found at cords.');
      }
      return cell;
    }
    getCellNodeFromCords(x, y, table) {
      const cell = this.getDOMCellFromCords(x, y, table);
      if (cell == null) {
        return null;
      }
      const node = $getNearestNodeFromDOMNode$1(cell.elem);
      if ($isTableCellNode(node)) {
        return node;
      }
      return null;
    }
    getCellNodeFromCordsOrThrow(x, y, table) {
      const node = this.getCellNodeFromCords(x, y, table);
      if (!node) {
        throw new Error('Node at cords not TableCellNode.');
      }
      return node;
    }
    canSelectBefore() {
      return true;
    }
    canIndent() {
      return false;
    }
  }
  function $getElementForTableNode(editor, tableNode) {
    const tableElement = editor.getElementByKey(tableNode.getKey());
    if (tableElement == null) {
      throw new Error('Table Element Not Found');
    }
    return getTable(tableElement);
  }
  function $convertTableElement(_domNode) {
    return {
      node: $createTableNode()
    };
  }
  function $createTableNode() {
    return $applyNodeReplacement$1(new TableNode());
  }
  function $isTableNode(node) {
    return node instanceof TableNode;
  }

  var modDev$a = /*#__PURE__*/Object.freeze({
    $computeTableMap: $computeTableMap,
    $computeTableMapSkipCellCheck: $computeTableMapSkipCellCheck,
    $createTableCellNode: $createTableCellNode,
    $createTableNode: $createTableNode,
    $createTableNodeWithDimensions: $createTableNodeWithDimensions,
    $createTableRowNode: $createTableRowNode,
    $createTableSelection: $createTableSelection,
    $deleteTableColumn: $deleteTableColumn,
    $deleteTableColumn__EXPERIMENTAL: $deleteTableColumn__EXPERIMENTAL,
    $deleteTableRow__EXPERIMENTAL: $deleteTableRow__EXPERIMENTAL,
    $findCellNode: $findCellNode,
    $findTableNode: $findTableNode,
    $getElementForTableNode: $getElementForTableNode,
    $getNodeTriplet: $getNodeTriplet,
    $getTableCellNodeFromLexicalNode: $getTableCellNodeFromLexicalNode,
    $getTableCellNodeRect: $getTableCellNodeRect,
    $getTableColumnIndexFromTableCellNode: $getTableColumnIndexFromTableCellNode,
    $getTableNodeFromLexicalNodeOrThrow: $getTableNodeFromLexicalNodeOrThrow,
    $getTableRowIndexFromTableCellNode: $getTableRowIndexFromTableCellNode,
    $getTableRowNodeFromTableCellNodeOrThrow: $getTableRowNodeFromTableCellNodeOrThrow,
    $insertTableColumn: $insertTableColumn,
    $insertTableColumn__EXPERIMENTAL: $insertTableColumn__EXPERIMENTAL,
    $insertTableRow: $insertTableRow,
    $insertTableRow__EXPERIMENTAL: $insertTableRow__EXPERIMENTAL,
    $isTableCellNode: $isTableCellNode,
    $isTableNode: $isTableNode,
    $isTableRowNode: $isTableRowNode,
    $isTableSelection: $isTableSelection,
    $removeTableRowAtIndex: $removeTableRowAtIndex,
    $unmergeCell: $unmergeCell,
    INSERT_TABLE_COMMAND: INSERT_TABLE_COMMAND,
    TableCellHeaderStates: TableCellHeaderStates,
    TableCellNode: TableCellNode,
    TableNode: TableNode,
    TableObserver: TableObserver,
    TableRowNode: TableRowNode,
    applyTableHandlers: applyTableHandlers,
    getDOMCellFromTarget: getDOMCellFromTarget,
    getTableObserverFromTableElement: getTableObserverFromTableElement
  });

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const ne$2 = createCommand$1("INSERT_TABLE_COMMAND");
  function ie$1(e) {
    return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
  }
  var ce$1 = ie$1(function (e) {
    const t = new URLSearchParams();
    t.append("code", e);
    for (let e = 1; e < arguments.length; e++) t.append("v", arguments[e]);
    throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`);
  });
  const ae$1 = "undefined" != typeof window && void 0 !== window.document && void 0 !== window.document.createElement;

  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  const mod$a = modDev$a;
  const $computeTableMap$1 = mod$a.$computeTableMap;
  const $computeTableMapSkipCellCheck$1 = mod$a.$computeTableMapSkipCellCheck;
  const $createTableCellNode$1 = mod$a.$createTableCellNode;
  const $createTableNode$1 = mod$a.$createTableNode;
  const $createTableNodeWithDimensions$1 = mod$a.$createTableNodeWithDimensions;
  const $createTableRowNode$1 = mod$a.$createTableRowNode;
  const $createTableSelection$1 = mod$a.$createTableSelection;
  const $deleteTableColumn$1 = mod$a.$deleteTableColumn;
  const $deleteTableColumn__EXPERIMENTAL$1 = mod$a.$deleteTableColumn__EXPERIMENTAL;
  const $deleteTableRow__EXPERIMENTAL$1 = mod$a.$deleteTableRow__EXPERIMENTAL;
  const $findCellNode$1 = mod$a.$findCellNode;
  const $findTableNode$1 = mod$a.$findTableNode;
  const $getElementForTableNode$1 = mod$a.$getElementForTableNode;
  const $getNodeTriplet$1 = mod$a.$getNodeTriplet;
  const $getTableCellNodeFromLexicalNode$1 = mod$a.$getTableCellNodeFromLexicalNode;
  const $getTableCellNodeRect$1 = mod$a.$getTableCellNodeRect;
  const $getTableColumnIndexFromTableCellNode$1 = mod$a.$getTableColumnIndexFromTableCellNode;
  const $getTableNodeFromLexicalNodeOrThrow$1 = mod$a.$getTableNodeFromLexicalNodeOrThrow;
  const $getTableRowIndexFromTableCellNode$1 = mod$a.$getTableRowIndexFromTableCellNode;
  const $getTableRowNodeFromTableCellNodeOrThrow$1 = mod$a.$getTableRowNodeFromTableCellNodeOrThrow;
  const $insertTableColumn$1 = mod$a.$insertTableColumn;
  const $insertTableColumn__EXPERIMENTAL$1 = mod$a.$insertTableColumn__EXPERIMENTAL;
  const $insertTableRow$1 = mod$a.$insertTableRow;
  const $insertTableRow__EXPERIMENTAL$1 = mod$a.$insertTableRow__EXPERIMENTAL;
  const $isTableCellNode$1 = mod$a.$isTableCellNode;
  const $isTableNode$1 = mod$a.$isTableNode;
  const $isTableRowNode$1 = mod$a.$isTableRowNode;
  const $isTableSelection$1 = mod$a.$isTableSelection;
  const $removeTableRowAtIndex$1 = mod$a.$removeTableRowAtIndex;
  const $unmergeCell$1 = mod$a.$unmergeCell;
  const INSERT_TABLE_COMMAND$1 = mod$a.INSERT_TABLE_COMMAND;
  const TableCellHeaderStates$1 = mod$a.TableCellHeaderStates;
  const TableCellNode$1 = mod$a.TableCellNode;
  const TableNode$1 = mod$a.TableNode;
  const TableObserver$1 = mod$a.TableObserver;
  const TableRowNode$1 = mod$a.TableRowNode;
  const applyTableHandlers$1 = mod$a.applyTableHandlers;
  const getDOMCellFromTarget$1 = mod$a.getDOMCellFromTarget;
  const getTableObserverFromTableElement$1 = mod$a.getTableObserverFromTableElement;

  var LexicalTable = /*#__PURE__*/Object.freeze({
    $computeTableMap: $computeTableMap$1,
    $computeTableMapSkipCellCheck: $computeTableMapSkipCellCheck$1,
    $createTableCellNode: $createTableCellNode$1,
    $createTableNode: $createTableNode$1,
    $createTableNodeWithDimensions: $createTableNodeWithDimensions$1,
    $createTableRowNode: $createTableRowNode$1,
    $createTableSelection: $createTableSelection$1,
    $deleteTableColumn: $deleteTableColumn$1,
    $deleteTableColumn__EXPERIMENTAL: $deleteTableColumn__EXPERIMENTAL$1,
    $deleteTableRow__EXPERIMENTAL: $deleteTableRow__EXPERIMENTAL$1,
    $findCellNode: $findCellNode$1,
    $findTableNode: $findTableNode$1,
    $getElementForTableNode: $getElementForTableNode$1,
    $getNodeTriplet: $getNodeTriplet$1,
    $getTableCellNodeFromLexicalNode: $getTableCellNodeFromLexicalNode$1,
    $getTableCellNodeRect: $getTableCellNodeRect$1,
    $getTableColumnIndexFromTableCellNode: $getTableColumnIndexFromTableCellNode$1,
    $getTableNodeFromLexicalNodeOrThrow: $getTableNodeFromLexicalNodeOrThrow$1,
    $getTableRowIndexFromTableCellNode: $getTableRowIndexFromTableCellNode$1,
    $getTableRowNodeFromTableCellNodeOrThrow: $getTableRowNodeFromTableCellNodeOrThrow$1,
    $insertTableColumn: $insertTableColumn$1,
    $insertTableColumn__EXPERIMENTAL: $insertTableColumn__EXPERIMENTAL$1,
    $insertTableRow: $insertTableRow$1,
    $insertTableRow__EXPERIMENTAL: $insertTableRow__EXPERIMENTAL$1,
    $isTableCellNode: $isTableCellNode$1,
    $isTableNode: $isTableNode$1,
    $isTableRowNode: $isTableRowNode$1,
    $isTableSelection: $isTableSelection$1,
    $removeTableRowAtIndex: $removeTableRowAtIndex$1,
    $unmergeCell: $unmergeCell$1,
    INSERT_TABLE_COMMAND: INSERT_TABLE_COMMAND$1,
    TableCellHeaderStates: TableCellHeaderStates$1,
    TableCellNode: TableCellNode$1,
    TableNode: TableNode$1,
    TableObserver: TableObserver$1,
    TableRowNode: TableRowNode$1,
    applyTableHandlers: applyTableHandlers$1,
    getDOMCellFromTarget: getDOMCellFromTarget$1,
    getTableObserverFromTableElement: getTableObserverFromTableElement$1
  });

  exports.Core = Lexical;
  exports.Html = LexicalHtml;
  exports.List = LexicalList;
  exports.Link = LexicalLink;
  exports.Clipboard = LexicalClipboard;
  exports.Selection = LexicalSelection;
  exports.History = LexicalHistory;
  exports.Utils = LexicalUtils;
  exports.Text = LexicalText;
  exports.RichText = LexicalRichText;
  exports.Table = LexicalTable;

}((this.BX.UI.Lexical = this.BX.UI.Lexical || {})));
//# sourceMappingURL=lexical.dev.bundle.js.map

Youez - 2016 - github.com/yon3zu
LinuXploit