@mx-space/cli 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +35 -11
  2. package/dist/{Lexical.dev-DTxuYHmG.mjs → Lexical.dev-Cck4n26y.mjs} +998 -584
  3. package/dist/{Lexical.node-BlCLnfA6.mjs → Lexical.node-D6wf2D5M.mjs} +15 -5
  4. package/dist/Lexical.prod-DCwS1oTq.mjs +7170 -0
  5. package/dist/{LexicalClipboard.dev-CdFkLR_D.mjs → LexicalClipboard.dev-CMkPHV7O.mjs} +6 -6
  6. package/dist/{LexicalClipboard.node-ktUajxsv.mjs → LexicalClipboard.node-m4nZ7IYw.mjs} +2 -2
  7. package/dist/{LexicalClipboard.prod-DliyLofW.mjs → LexicalClipboard.prod-B_rN1vE1.mjs} +10 -8
  8. package/dist/{LexicalCodeCore.dev-CL9tHWJX.mjs → LexicalCodeCore.dev-DyJw3i8V.mjs} +62 -72
  9. package/dist/{LexicalCodeCore.prod-BoMSJc29.mjs → LexicalCodeCore.prod-DPXLHQLF.mjs} +212 -213
  10. package/dist/{LexicalDragon.dev-CqW7Aq5H.mjs → LexicalDragon.dev-ZkNfMwrE.mjs} +3 -3
  11. package/dist/{LexicalDragon.node-DLXctJyy.mjs → LexicalDragon.node-Ds9oLUFh.mjs} +2 -2
  12. package/dist/{LexicalDragon.prod-DHI3T6n5.mjs → LexicalDragon.prod-B58ic2av.mjs} +5 -4
  13. package/dist/{LexicalExtension.dev-BP-18zns.mjs → LexicalExtension.dev-DvwHs1Xv.mjs} +79 -67
  14. package/dist/{LexicalExtension.node-BWH6xbzG.mjs → LexicalExtension.node-5OymZQfy.mjs} +4 -2
  15. package/dist/{LexicalExtension.prod-Cs6OgrDn.mjs → LexicalExtension.prod-Ce0T5smQ.mjs} +372 -358
  16. package/dist/{LexicalHeadless.dev-DJp__UcP.mjs → LexicalHeadless.dev-DA0WldSR.mjs} +2 -2
  17. package/dist/{LexicalHeadless.prod-o0v9cIu5.mjs → LexicalHeadless.prod-1kQgKB6c.mjs} +2 -2
  18. package/dist/{LexicalHtml.dev-DoymwFpG.mjs → LexicalHtml.dev-BDB7CUrx.mjs} +14 -12
  19. package/dist/{LexicalHtml.node-CVkbWtUA.mjs → LexicalHtml.node-DpVNt7It.mjs} +4 -4
  20. package/dist/{LexicalHtml.prod-C2b3z99k.mjs → LexicalHtml.prod-HkxM5scw.mjs} +331 -300
  21. package/dist/{LexicalLink.dev-7cgLOLIe.mjs → LexicalLink.dev-C5zNJlrm.mjs} +65 -59
  22. package/dist/{LexicalLink.prod-DWFgoXHR.mjs → LexicalLink.prod-Bkt9hQAt.mjs} +196 -201
  23. package/dist/{LexicalList.dev-Ci2iOxFx.mjs → LexicalList.dev-B7VgME-m.mjs} +55 -82
  24. package/dist/LexicalList.prod-B8c7TDjH.mjs +951 -0
  25. package/dist/{LexicalMarkdown.dev-C0ZYCyuR.mjs → LexicalMarkdown.dev-B5z9kLbV.mjs} +62 -42
  26. package/dist/{LexicalMarkdown.prod-0M-Nc-rh.mjs → LexicalMarkdown.prod-wKzw7FFH.mjs} +255 -202
  27. package/dist/{LexicalRichText.dev-Cnr1kDym.mjs → LexicalRichText.dev-CUd6Li5F.mjs} +399 -120
  28. package/dist/{LexicalRichText.node-BUJZAiap.mjs → LexicalRichText.node-BKfaQ-CH.mjs} +12 -8
  29. package/dist/LexicalRichText.prod-SBewjJHx.mjs +708 -0
  30. package/dist/{LexicalSelection.dev-C4L7l8OB.mjs → LexicalSelection.dev-D27JOCqj.mjs} +48 -17
  31. package/dist/{LexicalSelection.node-CLKroUMQ.mjs → LexicalSelection.node-DBEwPjBu.mjs} +5 -4
  32. package/dist/{LexicalSelection.prod-DzFqpTlw.mjs → LexicalSelection.prod-OaYhXHOL.mjs} +101 -92
  33. package/dist/{LexicalTable.dev-Cc9-zqKa.mjs → LexicalTable.dev-6IFFOAE0.mjs} +590 -259
  34. package/dist/LexicalTable.prod-2CaLZ7fH.mjs +2524 -0
  35. package/dist/{LexicalUtils.dev-BRgK0L6G.mjs → LexicalUtils.dev-DJA7J_7v.mjs} +15 -19
  36. package/dist/{LexicalUtils.node-BVAwOkL4.mjs → LexicalUtils.node-Df9oPTlA.mjs} +6 -6
  37. package/dist/{LexicalUtils.prod-C1gtvVDP.mjs → LexicalUtils.prod-Bb7V4Ewg.mjs} +155 -159
  38. package/dist/bin/mxs.mjs +1 -1
  39. package/dist/esm-CDcJmHkG.mjs +2 -0
  40. package/dist/{esm-BNu4sUWV.mjs → esm-DLzrv7gD.mjs} +1837 -6015
  41. package/dist/index.d.mts +57 -62
  42. package/dist/index.mjs +1 -1
  43. package/dist/{mxs-CMX2yklg.mjs → mxs-BIRW5Axj.mjs} +13579 -8682
  44. package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
  45. package/dist/vendor/litexml/cli.mjs +7043 -6207
  46. package/dist/vendor/litexml/litexml-html-preview-client.css +80 -47
  47. package/dist/vendor/litexml/litexml-html-preview-client.js +14422 -10838
  48. package/dist/vendor/litexml/style.css +6 -2
  49. package/package.json +23 -23
  50. package/skills/commands-ai.md +45 -12
  51. package/skills/overview.md +1 -1
  52. package/dist/Lexical.prod-CeB9UQuo.mjs +0 -6816
  53. package/dist/LexicalList.prod-D0xX5d3G.mjs +0 -944
  54. package/dist/LexicalRichText.prod-BfbMQ9uM.mjs +0 -538
  55. package/dist/LexicalTable.prod-BgO4nCxy.mjs +0 -2336
  56. package/dist/esm-jbwcVZth.mjs +0 -2
@@ -1,4 +1,4 @@
1
- //#region ../../node_modules/.pnpm/lexical@0.46.0_typescript@6.0.3/node_modules/lexical/dist/Lexical.dev.mjs
1
+ //#region ../../node_modules/.pnpm/lexical@0.49.0_typescript@6.0.3/node_modules/lexical/dist/Lexical.dev.mjs
2
2
  /**
3
3
  * Copyright (c) Meta Platforms, Inc. and affiliates.
4
4
  *
@@ -368,11 +368,11 @@ var ElementDOMSlot = class ElementDOMSlot extends DOMSlot {
368
368
  }
369
369
  const element = this.element;
370
370
  const before = this.before;
371
- const br = document.createElement("br");
371
+ const br = $getDocument().createElement("br");
372
372
  br.setAttribute("data-lexical-managed-linebreak", "true");
373
373
  element.insertBefore(br, before);
374
374
  if (webkitHack) {
375
- const img = document.createElement("img");
375
+ const img = $getDocument().createElement("img");
376
376
  img.setAttribute("data-lexical-managed-linebreak", "true");
377
377
  img.style.setProperty("display", "inline", "important");
378
378
  img.style.setProperty("border", "0px", "important");
@@ -448,7 +448,7 @@ function indexPath(root, child) {
448
448
  */
449
449
  let envLexicalVersion;
450
450
  try {
451
- envLexicalVersion = "0.46.0+dev.esm";
451
+ envLexicalVersion = "0.49.0+dev.esm";
452
452
  } catch (_unused) {}
453
453
  const LEXICAL_VERSION = envLexicalVersion ?? "\"<unknown>+source\"";
454
454
  /**
@@ -1784,8 +1784,8 @@ function $setElementDirection(dom, node) {
1784
1784
  if (direction !== null) dom.dir = direction;
1785
1785
  else dom.removeAttribute("dir");
1786
1786
  }
1787
- function createSlotDOM(name) {
1788
- const container = document.createElement("div");
1787
+ function $createSlotDOM(name) {
1788
+ const container = $getDocument().createElement("div");
1789
1789
  container.setAttribute("data-lexical-slot", name);
1790
1790
  container.style.display = "none";
1791
1791
  return container;
@@ -1801,7 +1801,7 @@ function $mountSlotChildren(node, hostDom, slots) {
1801
1801
  let totalText = "";
1802
1802
  const decoratorHost = $isDecoratorNode(node);
1803
1803
  for (const [name, slotKey] of slots) {
1804
- const container = createSlotDOM(name);
1804
+ const container = $createSlotDOM(name);
1805
1805
  $applySlotEditable(hostDom, decoratorHost, container);
1806
1806
  hostDom.appendChild(container);
1807
1807
  subTreeTextContent = "";
@@ -1848,7 +1848,7 @@ function $reconcileSlotChildren(prevNode, nextNode, hostDom) {
1848
1848
  subTreeTextContent = "";
1849
1849
  const saved = $beginCaptureGuard();
1850
1850
  if (container === null) {
1851
- container = createSlotDOM(name);
1851
+ container = $createSlotDOM(name);
1852
1852
  let firstNonSlot = null;
1853
1853
  for (const child of hostDom.children) if (!child.hasAttribute("data-lexical-slot")) {
1854
1854
  firstNonSlot = child;
@@ -2636,6 +2636,12 @@ const DELETE_LINE_COMMAND = /* @__PURE__ */ createCommand("DELETE_LINE_COMMAND")
2636
2636
  */
2637
2637
  const FORMAT_TEXT_COMMAND = /* @__PURE__ */ createCommand("FORMAT_TEXT_COMMAND");
2638
2638
  /**
2639
+ * Dispatched to explicitly set or unset text formats on the selection.
2640
+ * Unlike FORMAT_TEXT_COMMAND which toggles, this command sets each specified
2641
+ * format to the exact boolean value provided.
2642
+ */
2643
+ const SET_TEXT_FORMAT_COMMAND = /* @__PURE__ */ createCommand("SET_TEXT_FORMAT_COMMAND");
2644
+ /**
2639
2645
  * Dispatched on undo (Cmd+Z on macOS, Ctrl+Z elsewhere).
2640
2646
  */
2641
2647
  const UNDO_COMMAND = /* @__PURE__ */ createCommand("UNDO_COMMAND");
@@ -2743,6 +2749,114 @@ const BLUR_COMMAND = /* @__PURE__ */ createCommand("BLUR_COMMAND");
2743
2749
  * Dispatched after any KeyboardEvent when modifiers are pressed
2744
2750
  */
2745
2751
  const KEY_MODIFIER_COMMAND = /* @__PURE__ */ createCommand("KEY_MODIFIER_COMMAND");
2752
+ /**
2753
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2754
+ *
2755
+ * This source code is licensed under the MIT license found in the
2756
+ * LICENSE file in the root directory of this source tree.
2757
+ *
2758
+ */
2759
+ /**
2760
+ * A registry mapping keys to a per-key activation, reference counted so the
2761
+ * activation is created on the first registration for a key and torn down
2762
+ * only when the last outstanding registration for that key is released. This
2763
+ * lets the same key be driven by more than one caller (or survive a
2764
+ * re-entrant / double registration) without double-wiring or premature
2765
+ * teardown.
2766
+ *
2767
+ * Keys are compared by identity (Map semantics), so any object works — a DOM
2768
+ * element, a `Document`, a `Window`, or an opaque handle.
2769
+ */
2770
+ /**
2771
+ * Creates a {@link RefCountedRegistry}.
2772
+ *
2773
+ * @param activate - Wires `key` and returns its teardown. Called on the first
2774
+ * registration of each key.
2775
+ */
2776
+ function createRefCountedRegistry(activate) {
2777
+ const entries = /* @__PURE__ */ new Map();
2778
+ return {
2779
+ dispose() {
2780
+ for (const entry of entries.values()) entry.dispose();
2781
+ entries.clear();
2782
+ },
2783
+ register(key, options) {
2784
+ let entry = entries.get(key);
2785
+ if (entry === void 0) {
2786
+ entry = {
2787
+ dispose: activate(key, options),
2788
+ holders: /* @__PURE__ */ new Set()
2789
+ };
2790
+ entries.set(key, entry);
2791
+ }
2792
+ const release = () => {
2793
+ const current = entries.get(key);
2794
+ if (current && current.holders.delete(release) && current.holders.size === 0) {
2795
+ entries.delete(key);
2796
+ current.dispose();
2797
+ }
2798
+ };
2799
+ entry.holders.add(release);
2800
+ return release;
2801
+ }
2802
+ };
2803
+ }
2804
+ /**
2805
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2806
+ *
2807
+ * This source code is licensed under the MIT license found in the
2808
+ * LICENSE file in the root directory of this source tree.
2809
+ *
2810
+ */
2811
+ /**
2812
+ * The typed event map for a given {@link EventTarget}. Falls back to the global
2813
+ * handlers map (rather than a permissive `Record<string, Event>`) so unknown
2814
+ * event names are rejected as typos. Shared by {@link registerEventListener}
2815
+ * and `registerEventListeners`; the former additionally has a `string` overload
2816
+ * as an escape hatch for non-standard names (e.g. the legacy `textInput`),
2817
+ * which the object form intentionally does not.
2818
+ */
2819
+ /**
2820
+ * Add an event listener to `target` and return a function that removes it.
2821
+ *
2822
+ * This is a thin, strongly typed wrapper around
2823
+ * {@link EventTarget.addEventListener} that mirrors its overloads but returns a
2824
+ * dispose function instead of `void`. It removes the
2825
+ * `addEventListener`/`removeEventListener` boilerplate that every DOM
2826
+ * subscription would otherwise duplicate, and composes cleanly with
2827
+ * {@link mergeRegister} or as the return value of an effect.
2828
+ *
2829
+ * The same `options` value is forwarded to both `addEventListener` and
2830
+ * `removeEventListener` so that the `capture` flag always matches, which is
2831
+ * required for the listener to be removed correctly.
2832
+ *
2833
+ * @example
2834
+ * ```ts
2835
+ * // Returned directly from a React effect
2836
+ * useEffect(
2837
+ * () => registerEventListener(container, 'keydown', handler),
2838
+ * [container],
2839
+ * );
2840
+ * ```
2841
+ * @example
2842
+ * ```ts
2843
+ * // Composed with other teardown via mergeRegister
2844
+ * return mergeRegister(
2845
+ * registerEventListener(window, 'resize', onResize),
2846
+ * registerEventListener(document, 'selectionchange', onSelectionChange),
2847
+ * );
2848
+ * ```
2849
+ *
2850
+ * @param target - The {@link EventTarget} to subscribe to
2851
+ * @param type - The event type to listen for (e.g. `'keydown'`)
2852
+ * @param listener - The listener invoked when a matching event is dispatched
2853
+ * @param options - Options forwarded to `add`/`removeEventListener`
2854
+ * @returns A function that removes the listener when called
2855
+ */
2856
+ function registerEventListener(target, type, listener, options) {
2857
+ target.addEventListener(type, listener, options);
2858
+ return target.removeEventListener.bind(target, type, listener, options);
2859
+ }
2746
2860
  const PASS_THROUGH_COMMAND = Object.freeze({});
2747
2861
  const ANDROID_COMPOSITION_LATENCY = 30;
2748
2862
  const rootElementEvents = [
@@ -2763,28 +2877,12 @@ const rootElementEvents = [
2763
2877
  ["drop", PASS_THROUGH_COMMAND]
2764
2878
  ];
2765
2879
  if (CAN_USE_BEFORE_INPUT) rootElementEvents.push(["beforeinput", (event, editor) => onBeforeInput(event, editor)]);
2766
- let lastKeyDownTimeStamp = 0;
2767
- let lastKeyCode = null;
2768
- let lastBeforeInputInsertTextTimeStamp = 0;
2769
- let unprocessedBeforeInputData = null;
2770
- let isInsertTextAfterHandledSelectionCommand = false;
2771
- let handledSelectionCommandTimeoutId = null;
2772
2880
  const rootElementToDocument = /* @__PURE__ */ new WeakMap();
2773
2881
  const documentRegistrations = /* @__PURE__ */ new WeakMap();
2774
- let isSelectionChangeFromDOMUpdate = false;
2775
- let isSelectionChangeFromMouseDown = false;
2776
- let isInsertLineBreak = false;
2777
- let isFirefoxEndingComposition = false;
2778
- let isSafariEndingComposition = false;
2779
- let safariEndCompositionEventData = "";
2780
- let postDeleteSelectionToRestore = null;
2781
- let collapsedSelectionFormat = [
2782
- 0,
2783
- "",
2784
- 0,
2785
- "root",
2786
- 0
2787
- ];
2882
+ const documentSelectionChange = createRefCountedRegistry((doc) => {
2883
+ doc.addEventListener("selectionchange", onDocumentSelectionChange);
2884
+ return () => doc.removeEventListener("selectionchange", onDocumentSelectionChange);
2885
+ });
2788
2886
  function $shouldPreventDefaultAndInsertText(selection, domTargetRange, text, timeStamp, isBeforeInput, cachedDOMSelectionPoints) {
2789
2887
  const anchor = selection.anchor;
2790
2888
  const focus = selection.focus;
@@ -2800,16 +2898,17 @@ function $shouldPreventDefaultAndInsertText(selection, domTargetRange, text, tim
2800
2898
  const anchorKey = anchor.key;
2801
2899
  const backingAnchorElement = editor.getElementByKey(anchorKey);
2802
2900
  const textLength = text.length;
2803
- return anchorKey !== focus.key || !$isTextNode(anchorNode) || (!isBeforeInput && (!CAN_USE_BEFORE_INPUT || lastBeforeInputInsertTextTimeStamp < timeStamp + 50) || anchorNode.isDirty() && textLength < 2 || doesContainSurrogatePair(text)) && anchor.offset !== focus.offset && !anchorNode.isComposing() || $isTokenOrSegmented(anchorNode) || anchorNode.isDirty() && textLength > 1 || (isBeforeInput || !CAN_USE_BEFORE_INPUT) && backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== $getDOMTextNode(anchorNode, backingAnchorElement, editor) || domSelectionPoints !== null && domTargetRange !== null && (!domTargetRange.collapsed || domTargetRange.startContainer !== domSelectionPoints.anchorNode || domTargetRange.startOffset !== domSelectionPoints.anchorOffset) || !anchorNode.isComposing() && (anchorNode.getFormat() !== selection.format || anchorNode.getStyle() !== selection.style) || $shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
2901
+ return anchorKey !== focus.key || !$isTextNode(anchorNode) || (!isBeforeInput && (!CAN_USE_BEFORE_INPUT || editor._inputState.lastBeforeInputInsertTextTimeStamp < timeStamp + 50) || anchorNode.isDirty() && textLength < 2 || doesContainSurrogatePair(text)) && anchor.offset !== focus.offset && !anchorNode.isComposing() || $isTokenOrSegmented(anchorNode) || anchorNode.isDirty() && textLength > 1 || (isBeforeInput || !CAN_USE_BEFORE_INPUT) && backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== $getDOMTextNode(anchorNode, backingAnchorElement, editor) || domSelectionPoints !== null && domTargetRange !== null && (!domTargetRange.collapsed || domTargetRange.startContainer !== domSelectionPoints.anchorNode || domTargetRange.startOffset !== domSelectionPoints.anchorOffset) || !anchorNode.isComposing() && (anchorNode.getFormat() !== selection.format || anchorNode.getStyle() !== selection.style) || $shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
2804
2902
  }
2805
2903
  function shouldSkipSelectionChange(domNode, offset) {
2806
2904
  return isDOMTextNode(domNode) && domNode.nodeValue !== null && offset !== 0 && offset !== domNode.nodeValue.length;
2807
2905
  }
2808
2906
  function onSelectionChange(domSelection, editor, isActive) {
2809
2907
  const { anchorNode: anchorDOM, anchorOffset, focusNode: focusDOM, focusOffset } = getDOMSelectionPoints(domSelection, editor._rootElement);
2810
- if (isSelectionChangeFromDOMUpdate) {
2811
- isSelectionChangeFromDOMUpdate = false;
2812
- if (shouldSkipSelectionChange(anchorDOM, anchorOffset) && shouldSkipSelectionChange(focusDOM, focusOffset) && !postDeleteSelectionToRestore) return;
2908
+ const inputState = editor._inputState;
2909
+ if (inputState.isSelectionChangeFromDOMUpdate) {
2910
+ inputState.isSelectionChangeFromDOMUpdate = false;
2911
+ if (shouldSkipSelectionChange(anchorDOM, anchorOffset) && shouldSkipSelectionChange(focusDOM, focusOffset) && !inputState.postDeleteSelectionToRestore) return;
2813
2912
  }
2814
2913
  updateEditorSync(editor, () => {
2815
2914
  if (!isActive) {
@@ -2818,15 +2917,15 @@ function onSelectionChange(domSelection, editor, isActive) {
2818
2917
  }
2819
2918
  if (!isSelectionWithinEditor(editor, anchorDOM, focusDOM)) return;
2820
2919
  let selection = $getSelection();
2821
- if (postDeleteSelectionToRestore && $isRangeSelection(selection) && selection.isCollapsed()) {
2920
+ if (inputState.postDeleteSelectionToRestore && $isRangeSelection(selection) && selection.isCollapsed()) {
2822
2921
  const curAnchor = selection.anchor;
2823
- const prevAnchor = postDeleteSelectionToRestore.anchor;
2922
+ const prevAnchor = inputState.postDeleteSelectionToRestore.anchor;
2824
2923
  if (curAnchor.key === prevAnchor.key && curAnchor.offset === prevAnchor.offset + 1 || curAnchor.offset === 1 && prevAnchor.getNode().is(curAnchor.getNode().getPreviousSibling())) {
2825
- selection = postDeleteSelectionToRestore.clone();
2924
+ selection = inputState.postDeleteSelectionToRestore.clone();
2826
2925
  $setSelection(selection);
2827
2926
  }
2828
2927
  }
2829
- postDeleteSelectionToRestore = null;
2928
+ inputState.postDeleteSelectionToRestore = null;
2830
2929
  if ($isRangeSelection(selection)) {
2831
2930
  const anchor = selection.anchor;
2832
2931
  const anchorNode = anchor.getNode();
@@ -2834,7 +2933,7 @@ function onSelectionChange(domSelection, editor, isActive) {
2834
2933
  if (domSelection.type === "Range" && anchorDOM === focusDOM) selection.dirty = true;
2835
2934
  const windowEvent = getWindow(editor).event;
2836
2935
  const currentTimeStamp = windowEvent ? windowEvent.timeStamp : performance.now();
2837
- const [lastFormat, lastStyle, lastOffset, lastKey, timeStamp] = collapsedSelectionFormat;
2936
+ const { format: lastFormat, style: lastStyle, offset: lastOffset, key: lastKey, timeStamp } = inputState.collapsedSelectionFormat;
2838
2937
  const root = $getRoot();
2839
2938
  const isRootTextContentEmpty = editor.isComposing() === false && root.getTextContent() === "";
2840
2939
  if (currentTimeStamp < timeStamp + 200 && anchor.offset === lastOffset && anchor.key === lastKey) $updateSelectionFormatStyle(selection, lastFormat, lastStyle);
@@ -2871,7 +2970,7 @@ function onSelectionChange(domSelection, editor, isActive) {
2871
2970
  selection.format = hasTextNodes ? combinedFormat : 0;
2872
2971
  }
2873
2972
  }
2874
- dispatchCommand(editor, SELECTION_CHANGE_COMMAND, void 0);
2973
+ dispatchCommand(editor, SELECTION_CHANGE_COMMAND);
2875
2974
  });
2876
2975
  }
2877
2976
  function $updateSelectionFormatStyle(selection, format, style) {
@@ -2905,6 +3004,27 @@ function onClick(event, editor) {
2905
3004
  if (isHTMLElement(domAnchorNode) || isDOMTextNode(domAnchorNode)) $setSelection($internalCreateRangeSelection(lastSelection, domSelection, editor, event));
2906
3005
  }
2907
3006
  }
3007
+ if (IS_FIREFOX && domSelection !== null && domSelection.rangeCount === 0) {
3008
+ const rootElement = editor._rootElement;
3009
+ if (rootElement !== null && event.target === rootElement) {
3010
+ const clientY = event.clientY;
3011
+ let offset = rootElement.childNodes.length;
3012
+ for (let i = 0; i < rootElement.childNodes.length; i++) {
3013
+ const child = rootElement.childNodes[i];
3014
+ if (isHTMLElement(child)) {
3015
+ const rect = child.getBoundingClientRect();
3016
+ if (clientY <= (rect.top + rect.bottom) / 2) {
3017
+ offset = i;
3018
+ break;
3019
+ }
3020
+ }
3021
+ }
3022
+ domSelection.setBaseAndExtent(rootElement, offset, rootElement, offset);
3023
+ const newSelection = $internalCreateRangeSelection(lastSelection, domSelection, editor, event);
3024
+ if (newSelection !== null) $setSelection(newSelection);
3025
+ else domSelection.removeAllRanges();
3026
+ }
3027
+ }
2908
3028
  dispatchCommand(editor, CLICK_COMMAND, event);
2909
3029
  });
2910
3030
  }
@@ -2912,7 +3032,7 @@ function onPointerDown(event, editor) {
2912
3032
  const target = getComposedEventTarget(event);
2913
3033
  const pointerType = event.pointerType;
2914
3034
  if (isDOMNode(target) && pointerType !== "touch" && pointerType !== "pen" && event.button === 0) updateEditorSync(editor, () => {
2915
- if (!isDOMCapturingSelection(target, editor)) isSelectionChangeFromMouseDown = true;
3035
+ if (!isDOMCapturingSelection(target, editor)) editor._inputState.isSelectionChangeFromMouseDown = true;
2916
3036
  });
2917
3037
  }
2918
3038
  function getTargetRange(event) {
@@ -2922,6 +3042,7 @@ function getTargetRange(event) {
2922
3042
  return targetRanges[0];
2923
3043
  }
2924
3044
  function $maybeMoveSelectionPastTrailingAcceptanceBoundary(insertedText) {
3045
+ const { lastKeyCode } = getActiveEditor()._inputState;
2925
3046
  if (insertedText == null || insertedText.length <= 1 || lastKeyCode == null) return;
2926
3047
  const characterToSearchFor = lastKeyCode.length === 1 ? lastKeyCode : lastKeyCode === "Enter" ? "\n" : lastKeyCode === "Tab" ? " " : null;
2927
3048
  if (!characterToSearchFor) return;
@@ -2947,20 +3068,20 @@ function $maybeMoveSelectionPastTrailingAcceptanceBoundary(insertedText) {
2947
3068
  function $canRemoveText(anchorNode, focusNode) {
2948
3069
  return anchorNode !== focusNode || $isElementNode(anchorNode) || $isElementNode(focusNode) || !$isTokenOrTab(anchorNode) || !$isTokenOrTab(focusNode);
2949
3070
  }
2950
- function isPossiblyAndroidKeyPress(timeStamp) {
2951
- return lastKeyCode === "MediaLast" && timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
3071
+ function isPossiblyAndroidKeyPress(inputState, timeStamp) {
3072
+ return inputState.lastKeyCode === "MediaLast" && timeStamp < inputState.lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
2952
3073
  }
2953
- function clearHandledSelectionCommandInsertText() {
2954
- isInsertTextAfterHandledSelectionCommand = false;
2955
- if (handledSelectionCommandTimeoutId !== null) {
2956
- clearTimeout(handledSelectionCommandTimeoutId);
2957
- handledSelectionCommandTimeoutId = null;
3074
+ function clearHandledSelectionCommandInsertText(inputState) {
3075
+ inputState.isInsertTextAfterHandledSelectionCommand = false;
3076
+ if (inputState.handledSelectionCommandTimeoutId !== null) {
3077
+ clearTimeout(inputState.handledSelectionCommandTimeoutId);
3078
+ inputState.handledSelectionCommandTimeoutId = null;
2958
3079
  }
2959
3080
  }
2960
- function markHandledSelectionCommandInsertText() {
2961
- clearHandledSelectionCommandInsertText();
2962
- isInsertTextAfterHandledSelectionCommand = true;
2963
- handledSelectionCommandTimeoutId = setTimeout(clearHandledSelectionCommandInsertText, 0);
3081
+ function markHandledSelectionCommandInsertText(inputState) {
3082
+ clearHandledSelectionCommandInsertText(inputState);
3083
+ inputState.isInsertTextAfterHandledSelectionCommand = true;
3084
+ inputState.handledSelectionCommandTimeoutId = setTimeout(() => clearHandledSelectionCommandInsertText(inputState), 0);
2964
3085
  }
2965
3086
  function registerDefaultCommandHandlers(editor) {
2966
3087
  editor.registerCommand(BEFORE_INPUT_COMMAND, $handleBeforeInput, 0);
@@ -3002,9 +3123,10 @@ function $handleBeforeInput(event) {
3002
3123
  const inputType = event.inputType;
3003
3124
  const targetRange = getTargetRange(event);
3004
3125
  const editor = getActiveEditor();
3126
+ const inputState = editor._inputState;
3005
3127
  const selection = $getSelection();
3006
- if (inputType === "insertText" && event.data && isInsertTextAfterHandledSelectionCommand) {
3007
- clearHandledSelectionCommandInsertText();
3128
+ if (inputType === "insertText" && event.data && inputState.isInsertTextAfterHandledSelectionCommand) {
3129
+ clearHandledSelectionCommandInsertText(inputState);
3008
3130
  event.preventDefault();
3009
3131
  if ($isRangeSelection(selection) && !selection.isCollapsed()) {
3010
3132
  const point = selection.isBackward() ? selection.anchor : selection.focus;
@@ -3021,9 +3143,9 @@ function $handleBeforeInput(event) {
3021
3143
  }
3022
3144
  if ($isRangeSelection(selection)) {
3023
3145
  const isSelectionAnchorSameAsFocus = selection.anchor.key === selection.focus.key;
3024
- if (isPossiblyAndroidKeyPress(event.timeStamp) && editor.isComposing() && isSelectionAnchorSameAsFocus) {
3146
+ if (isPossiblyAndroidKeyPress(inputState, event.timeStamp) && editor.isComposing() && isSelectionAnchorSameAsFocus) {
3025
3147
  $setCompositionKey(null);
3026
- lastKeyDownTimeStamp = 0;
3148
+ inputState.lastKeyDownTimeStamp = 0;
3027
3149
  setTimeout(() => {
3028
3150
  updateEditorSync(editor, () => {
3029
3151
  $setCompositionKey(null);
@@ -3056,8 +3178,8 @@ function $handleBeforeInput(event) {
3056
3178
  dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
3057
3179
  const selectionAfterDelete = $getSelection();
3058
3180
  if (IS_ANDROID_CHROME && $isRangeSelection(selectionAfterDelete) && selectionAfterDelete.isCollapsed()) {
3059
- postDeleteSelectionToRestore = selectionAfterDelete;
3060
- setTimeout(() => postDeleteSelectionToRestore = null);
3181
+ inputState.postDeleteSelectionToRestore = selectionAfterDelete;
3182
+ setTimeout(() => inputState.postDeleteSelectionToRestore = null);
3061
3183
  }
3062
3184
  }
3063
3185
  }
@@ -3066,9 +3188,9 @@ function $handleBeforeInput(event) {
3066
3188
  }
3067
3189
  if (!$isRangeSelection(selection)) return true;
3068
3190
  const data = event.data;
3069
- if (unprocessedBeforeInputData !== null) $updateSelectedTextFromDOM(false, editor, unprocessedBeforeInputData);
3070
- if ((!selection.dirty || unprocessedBeforeInputData !== null) && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode()) && targetRange !== null) selection.applyDOMRange(targetRange);
3071
- unprocessedBeforeInputData = null;
3191
+ if (inputState.unprocessedBeforeInputData !== null) $updateSelectedTextFromDOM(false, editor, inputState.unprocessedBeforeInputData);
3192
+ if ((!selection.dirty || inputState.unprocessedBeforeInputData !== null) && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode()) && targetRange !== null) selection.applyDOMRange(targetRange);
3193
+ inputState.unprocessedBeforeInputData = null;
3072
3194
  const anchor = selection.anchor;
3073
3195
  const focus = selection.focus;
3074
3196
  const anchorNode = anchor.getNode();
@@ -3079,7 +3201,7 @@ function $handleBeforeInput(event) {
3079
3201
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
3080
3202
  } else if (data === DOUBLE_LINE_BREAK) {
3081
3203
  event.preventDefault();
3082
- dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, void 0);
3204
+ dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND);
3083
3205
  } else if (data == null && event.dataTransfer) {
3084
3206
  const text = event.dataTransfer.getData("text/plain");
3085
3207
  event.preventDefault();
@@ -3088,8 +3210,8 @@ function $handleBeforeInput(event) {
3088
3210
  event.preventDefault();
3089
3211
  dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
3090
3212
  $maybeMoveSelectionPastTrailingAcceptanceBoundary(data);
3091
- } else unprocessedBeforeInputData = data;
3092
- lastBeforeInputInsertTextTimeStamp = event.timeStamp;
3213
+ } else inputState.unprocessedBeforeInputData = data;
3214
+ inputState.lastBeforeInputInsertTextTimeStamp = event.timeStamp;
3093
3215
  return true;
3094
3216
  }
3095
3217
  event.preventDefault();
@@ -3100,20 +3222,25 @@ function $handleBeforeInput(event) {
3100
3222
  dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
3101
3223
  $maybeMoveSelectionPastTrailingAcceptanceBoundary((event.dataTransfer ? event.dataTransfer.getData("text/plain") : null) ?? event.data);
3102
3224
  break;
3103
- case "insertFromComposition":
3225
+ case "insertFromComposition": {
3226
+ const skipRedundantInsert = inputState.hadOrphanedCompositionEvents;
3227
+ inputState.hadOrphanedCompositionEvents = false;
3228
+ const prevCompositionKey = editor._compositionKey;
3104
3229
  $setCompositionKey(null);
3105
- dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
3230
+ if (!skipRedundantInsert) dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
3231
+ $cleanupComposedSubclass(prevCompositionKey);
3106
3232
  break;
3233
+ }
3107
3234
  case "insertLineBreak":
3108
3235
  $setCompositionKey(null);
3109
3236
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
3110
3237
  break;
3111
3238
  case "insertParagraph":
3112
3239
  $setCompositionKey(null);
3113
- if (isInsertLineBreak && !IS_IOS) {
3114
- isInsertLineBreak = false;
3240
+ if (inputState.isInsertLineBreak && !IS_IOS) {
3241
+ inputState.isInsertLineBreak = false;
3115
3242
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
3116
- } else dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, void 0);
3243
+ } else dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND);
3117
3244
  break;
3118
3245
  case "insertFromPaste":
3119
3246
  case "insertFromPasteAsQuotation":
@@ -3160,24 +3287,26 @@ function $handleBeforeInput(event) {
3160
3287
  dispatchCommand(editor, FORMAT_TEXT_COMMAND, "underline");
3161
3288
  break;
3162
3289
  case "historyUndo":
3163
- dispatchCommand(editor, UNDO_COMMAND, void 0);
3290
+ dispatchCommand(editor, UNDO_COMMAND);
3164
3291
  break;
3165
3292
  case "historyRedo":
3166
- dispatchCommand(editor, REDO_COMMAND, void 0);
3293
+ dispatchCommand(editor, REDO_COMMAND);
3167
3294
  break;
3168
3295
  }
3169
3296
  return true;
3170
3297
  }
3171
3298
  function onInput(event, editor) {
3172
3299
  event.stopPropagation();
3173
- clearHandledSelectionCommandInsertText();
3300
+ const inputState = editor._inputState;
3301
+ clearHandledSelectionCommandInsertText(inputState);
3174
3302
  updateEditorSync(editor, () => {
3175
3303
  if (!isInputEventTargetingCapturedSelection(event, editor)) editor.dispatchCommand(INPUT_COMMAND, event);
3176
3304
  }, { event });
3177
- unprocessedBeforeInputData = null;
3305
+ inputState.unprocessedBeforeInputData = null;
3178
3306
  }
3179
3307
  function $handleInput(event) {
3180
3308
  const editor = getActiveEditor();
3309
+ const inputState = editor._inputState;
3181
3310
  const selection = $getSelection();
3182
3311
  const data = event.data;
3183
3312
  const targetRange = getTargetRange(event);
@@ -3185,11 +3314,20 @@ function $handleInput(event) {
3185
3314
  if (data != null && $isRangeSelection(selection)) {
3186
3315
  const domSelection = getDOMSelection(getWindow(editor));
3187
3316
  const domSelectionPoints = domSelection !== null ? getDOMSelectionPoints(domSelection, editor._rootElement) : null;
3188
- if ($shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, false, domSelectionPoints)) {
3317
+ const isOrphanedCompositionEnd = event.inputType === "insertCompositionText" && inputState.compositionPhase !== "ending-firefox" && !editor.isComposing();
3318
+ if (isOrphanedCompositionEnd) inputState.hadOrphanedCompositionEvents = true;
3319
+ const inputAnchorNode = selection.anchor.getNode();
3320
+ const isCompositionOnToken = event.inputType === "insertCompositionText" && inputState.compositionPhase !== "ending-firefox" && editor.isComposing() && $isTextNode(inputAnchorNode) && $isTokenOrSegmented(inputAnchorNode);
3321
+ if (!isOrphanedCompositionEnd && !isCompositionOnToken && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, false, domSelectionPoints)) {
3189
3322
  handled = true;
3190
- if (isFirefoxEndingComposition) {
3191
- $onCompositionEndImpl(editor, data);
3192
- isFirefoxEndingComposition = false;
3323
+ if (inputState.compositionPhase === "ending-firefox") {
3324
+ const tokenRedirected = $onCompositionEndImpl(editor, data);
3325
+ inputState.compositionPhase = "idle";
3326
+ if (tokenRedirected) {
3327
+ $addUpdateTag(COMPOSITION_END_TAG);
3328
+ $flushMutations();
3329
+ return true;
3330
+ }
3193
3331
  }
3194
3332
  const anchorNode = selection.anchor.getNode();
3195
3333
  if (domSelection === null || domSelectionPoints === null) return true;
@@ -3204,17 +3342,17 @@ function $handleInput(event) {
3204
3342
  selection._cachedIsBackward = null;
3205
3343
  }
3206
3344
  if (IS_ANDROID_CHROME && editor.isComposing()) {
3207
- lastKeyDownTimeStamp = 0;
3345
+ inputState.lastKeyDownTimeStamp = 0;
3208
3346
  $setCompositionKey(null);
3209
3347
  }
3210
3348
  }
3211
3349
  }
3212
3350
  if (!handled) {
3213
3351
  $updateSelectedTextFromDOM(false, editor, data !== null ? data : void 0);
3214
- if (isFirefoxEndingComposition) {
3352
+ if (inputState.compositionPhase === "ending-firefox") {
3215
3353
  $onCompositionEndImpl(editor, data || void 0);
3216
3354
  $addUpdateTag(COMPOSITION_END_TAG);
3217
- isFirefoxEndingComposition = false;
3355
+ inputState.compositionPhase = "idle";
3218
3356
  }
3219
3357
  }
3220
3358
  $flushMutations();
@@ -3225,21 +3363,45 @@ function onCompositionStart(event, editor) {
3225
3363
  }
3226
3364
  function $handleCompositionStart(event) {
3227
3365
  const editor = getActiveEditor();
3366
+ const inputState = editor._inputState;
3228
3367
  const selection = $getSelection();
3229
3368
  if ($isRangeSelection(selection) && !editor.isComposing()) {
3369
+ inputState.compositionPhase = "composing";
3370
+ inputState.hadOrphanedCompositionEvents = false;
3230
3371
  const anchor = selection.anchor;
3231
3372
  const node = selection.anchor.getNode();
3232
3373
  $setCompositionKey(anchor.key);
3233
3374
  $addUpdateTag(COMPOSITION_START_TAG);
3234
- if (event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY || anchor.type === "element" || !selection.isCollapsed() || node.getFormat() !== selection.format || $isTextNode(node) && node.getStyle() !== selection.style) dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, COMPOSITION_START_CHAR);
3375
+ if (event.timeStamp < inputState.lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY || anchor.type === "element" || !selection.isCollapsed() || !IS_ANDROID_CHROME && (node.getFormat() !== selection.format || $isTextNode(node) && node.getStyle() !== selection.style) || $isTextNode(node) && ($isTokenOrSegmented(node) || anchor.offset === 0 && !node.canInsertTextBefore() || anchor.offset === node.getTextContentSize() && !node.canInsertTextAfter())) {
3376
+ dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, COMPOSITION_START_CHAR);
3377
+ const updatedSelection = $getSelection();
3378
+ if ($isRangeSelection(updatedSelection)) $setCompositionKey(updatedSelection.anchor.key);
3379
+ }
3235
3380
  }
3236
3381
  return true;
3237
3382
  }
3238
3383
  function $handleCompositionEnd(event) {
3239
- $onCompositionEndImpl(getActiveEditor(), event.data);
3384
+ const editor = getActiveEditor();
3385
+ editor._inputState.compositionPhase = "idle";
3386
+ $onCompositionEndImpl(editor, event.data);
3240
3387
  $addUpdateTag(COMPOSITION_END_TAG);
3241
3388
  return true;
3242
3389
  }
3390
+ function $cleanupComposedSubclass(compositionKey) {
3391
+ if (compositionKey === null) return;
3392
+ const composedNode = $getNodeByKey(compositionKey);
3393
+ if (!$isTextNode(composedNode) || composedNode.getType() === "text" || $isTokenOrSegmented(composedNode) || !composedNode.isAttached()) return;
3394
+ const sel = $getSelection();
3395
+ const offset = $isRangeSelection(sel) && sel.anchor.key === compositionKey ? sel.anchor.offset : null;
3396
+ const replacement = $createTextNode(composedNode.getTextContent());
3397
+ replacement.setFormat(composedNode.getFormat());
3398
+ replacement.setStyle(composedNode.getStyle());
3399
+ composedNode.replace(replacement);
3400
+ if (offset !== null) {
3401
+ const safeOffset = Math.min(offset, replacement.getTextContentSize());
3402
+ replacement.select(safeOffset, safeOffset);
3403
+ }
3404
+ }
3243
3405
  function $onCompositionEndImpl(editor, data) {
3244
3406
  const compositionKey = editor._compositionKey;
3245
3407
  $setCompositionKey(null);
@@ -3259,7 +3421,8 @@ function $onCompositionEndImpl(editor, data) {
3259
3421
  }
3260
3422
  $updateTextNodeFromDOMContent(node, textNode.nodeValue, anchorOffset, focusOffset, true);
3261
3423
  }
3262
- return;
3424
+ $cleanupComposedSubclass(compositionKey);
3425
+ return false;
3263
3426
  } else if (data[data.length - 1] === "\n") {
3264
3427
  const selection = $getSelection();
3265
3428
  if ($isRangeSelection(selection) || $isNodeSelection(selection)) {
@@ -3268,40 +3431,52 @@ function $onCompositionEndImpl(editor, data) {
3268
3431
  selection.anchor.set(focus.key, focus.offset, focus.type);
3269
3432
  }
3270
3433
  dispatchCommand(editor, KEY_ENTER_COMMAND, null);
3271
- return;
3434
+ $cleanupComposedSubclass(compositionKey);
3435
+ return false;
3272
3436
  }
3273
3437
  }
3438
+ const node = $getNodeByKey(compositionKey);
3439
+ if (node !== null && $isTextNode(node) && $isTokenOrSegmented(node)) {
3440
+ node.markDirty();
3441
+ const selection = $getSelection();
3442
+ const textLen = node.getTextContentSize();
3443
+ const offset = $isRangeSelection(selection) && selection.anchor.key === compositionKey ? selection.anchor.offset : textLen;
3444
+ node.select(offset, offset).insertText(data);
3445
+ return true;
3446
+ }
3274
3447
  }
3275
3448
  $updateSelectedTextFromDOM(true, editor, data);
3449
+ $cleanupComposedSubclass(compositionKey);
3450
+ return false;
3276
3451
  }
3277
3452
  function onCompositionEnd(event, editor) {
3278
- if (IS_FIREFOX) isFirefoxEndingComposition = true;
3453
+ const inputState = editor._inputState;
3454
+ if (IS_FIREFOX) inputState.compositionPhase = "ending-firefox";
3279
3455
  else if (!IS_IOS && (IS_SAFARI || IS_APPLE_WEBKIT)) {
3280
- isSafariEndingComposition = true;
3281
- safariEndCompositionEventData = event.data;
3456
+ inputState.compositionPhase = "ending-safari";
3457
+ inputState.compositionEndData = event.data;
3282
3458
  } else dispatchCommand(editor, COMPOSITION_END_COMMAND, event);
3283
3459
  }
3284
3460
  function onKeyDown(event, editor) {
3285
- lastKeyDownTimeStamp = event.timeStamp;
3286
- lastKeyCode = event.key;
3287
- if (event.key !== "Backspace") clearHandledSelectionCommandInsertText();
3461
+ const inputState = editor._inputState;
3462
+ inputState.lastKeyDownTimeStamp = event.timeStamp;
3463
+ inputState.lastKeyCode = event.key;
3464
+ if (event.key !== "Backspace") clearHandledSelectionCommandInsertText(inputState);
3288
3465
  if (editor.isComposing()) return;
3289
3466
  dispatchCommand(editor, KEY_DOWN_COMMAND, event);
3290
3467
  }
3291
3468
  function $handleKeyDown(event) {
3292
3469
  const editor = getActiveEditor();
3470
+ const inputState = editor._inputState;
3293
3471
  if (event.key == null) return true;
3294
- if (isSafariEndingComposition) {
3295
- if (isBackspace(event)) {
3296
- updateEditorSync(editor, () => {
3297
- $onCompositionEndImpl(editor, safariEndCompositionEventData);
3298
- });
3299
- isSafariEndingComposition = false;
3300
- safariEndCompositionEventData = "";
3301
- return true;
3302
- }
3303
- isSafariEndingComposition = false;
3304
- safariEndCompositionEventData = "";
3472
+ if (inputState.compositionPhase === "ending-safari") {
3473
+ const isBack = isBackspace(event);
3474
+ if (isBack) updateEditorSync(editor, () => {
3475
+ $onCompositionEndImpl(editor, inputState.compositionEndData);
3476
+ });
3477
+ inputState.compositionPhase = "idle";
3478
+ inputState.compositionEndData = "";
3479
+ if (isBack) return true;
3305
3480
  }
3306
3481
  if (isMoveForward(event)) dispatchCommand(editor, KEY_ARROW_RIGHT_COMMAND, event);
3307
3482
  else if (isMoveToEnd(event)) dispatchCommand(editor, MOVE_TO_END, event);
@@ -3310,18 +3485,18 @@ function $handleKeyDown(event) {
3310
3485
  else if (isMoveUp(event)) dispatchCommand(editor, KEY_ARROW_UP_COMMAND, event);
3311
3486
  else if (isMoveDown(event)) dispatchCommand(editor, KEY_ARROW_DOWN_COMMAND, event);
3312
3487
  else if (isLineBreak(event)) {
3313
- isInsertLineBreak = true;
3488
+ inputState.isInsertLineBreak = true;
3314
3489
  dispatchCommand(editor, KEY_ENTER_COMMAND, event);
3315
3490
  } else if (isSpace(event)) dispatchCommand(editor, KEY_SPACE_COMMAND, event);
3316
3491
  else if (isOpenLineBreak(event)) {
3317
3492
  event.preventDefault();
3318
- isInsertLineBreak = true;
3493
+ inputState.isInsertLineBreak = true;
3319
3494
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, true);
3320
3495
  } else if (isParagraph(event)) {
3321
- isInsertLineBreak = false;
3496
+ inputState.isInsertLineBreak = false;
3322
3497
  dispatchCommand(editor, KEY_ENTER_COMMAND, event);
3323
3498
  } else if (isDeleteBackward(event)) if (isBackspace(event)) {
3324
- if (dispatchCommand(editor, KEY_BACKSPACE_COMMAND, event)) markHandledSelectionCommandInsertText();
3499
+ if (dispatchCommand(editor, KEY_BACKSPACE_COMMAND, event)) markHandledSelectionCommandInsertText(inputState);
3325
3500
  } else {
3326
3501
  event.preventDefault();
3327
3502
  dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
@@ -3356,15 +3531,15 @@ function $handleKeyDown(event) {
3356
3531
  } else if (isTab(event)) dispatchCommand(editor, KEY_TAB_COMMAND, event);
3357
3532
  else if (isUndo(event)) {
3358
3533
  event.preventDefault();
3359
- dispatchCommand(editor, UNDO_COMMAND, void 0);
3534
+ dispatchCommand(editor, UNDO_COMMAND);
3360
3535
  } else if (isRedo(event)) {
3361
3536
  event.preventDefault();
3362
- dispatchCommand(editor, REDO_COMMAND, void 0);
3537
+ dispatchCommand(editor, REDO_COMMAND);
3363
3538
  } else {
3364
3539
  const prevSelection = editor._editorState._selection;
3365
3540
  if (isSelectAll(event)) {
3366
3541
  event.preventDefault();
3367
- if (dispatchCommand(editor, SELECT_ALL_COMMAND, event)) markHandledSelectionCommandInsertText();
3542
+ if (dispatchCommand(editor, SELECT_ALL_COMMAND, event)) markHandledSelectionCommandInsertText(inputState);
3368
3543
  } else if (prevSelection !== null && !$isRangeSelection(prevSelection)) {
3369
3544
  if (isCopy(event)) {
3370
3545
  event.preventDefault();
@@ -3393,8 +3568,8 @@ function onDocumentSelectionChange(event) {
3393
3568
  const ownerDocument = getDOMOwnerDocument(event.target);
3394
3569
  let nextActiveEditor = null;
3395
3570
  let resolvedAnchorNode = null;
3571
+ const registration = ownerDocument !== null ? documentRegistrations.get(ownerDocument) : void 0;
3396
3572
  if (ownerDocument !== null) {
3397
- const registration = documentRegistrations.get(ownerDocument);
3398
3573
  if (registration !== void 0) {
3399
3574
  const editorsForDoc = registration.editors;
3400
3575
  let hasShadow = registration.hasShadowEditor;
@@ -3443,8 +3618,8 @@ function onDocumentSelectionChange(event) {
3443
3618
  }
3444
3619
  }
3445
3620
  if (nextActiveEditor === null) return;
3446
- if (isSelectionChangeFromMouseDown) {
3447
- isSelectionChangeFromMouseDown = false;
3621
+ if (nextActiveEditor._inputState.isSelectionChangeFromMouseDown) {
3622
+ if (registration !== void 0) for (const ed of registration.editors) ed._inputState.isSelectionChangeFromMouseDown = false;
3448
3623
  updateEditorSync(nextActiveEditor, () => {
3449
3624
  const lastSelection = $getPreviousSelection();
3450
3625
  const domAnchorNode = resolvedAnchorNode ?? getDOMSelectionPoints(domSelection, nextActiveEditor._rootElement).anchorNode;
@@ -3475,17 +3650,15 @@ function addRootElementEvents(rootElement, editor) {
3475
3650
  if (registration === void 0) {
3476
3651
  registration = {
3477
3652
  editors: /* @__PURE__ */ new Set(),
3478
- hasShadowEditor: void 0,
3479
- rootElementCount: 0
3653
+ hasShadowEditor: void 0
3480
3654
  };
3481
3655
  documentRegistrations.set(doc, registration);
3482
3656
  }
3483
- if (registration.rootElementCount < 1) doc.addEventListener("selectionchange", onDocumentSelectionChange);
3484
- registration.rootElementCount += 1;
3485
3657
  registration.editors.add(editor);
3486
3658
  registration.hasShadowEditor = void 0;
3487
3659
  rootElement.__lexicalEditor = editor;
3488
3660
  const removeHandles = getRootElementRemoveHandles(rootElement);
3661
+ removeHandles.push(documentSelectionChange.register(doc));
3489
3662
  for (let i = 0; i < rootElementEvents.length; i++) {
3490
3663
  const [eventName, onEvent] = rootElementEvents[i];
3491
3664
  const eventHandler = typeof onEvent === "function" ? (event) => {
@@ -3508,10 +3681,7 @@ function addRootElementEvents(rootElement, editor) {
3508
3681
  case "drop": return isEditable && dispatchCommand(editor, DROP_COMMAND, event);
3509
3682
  }
3510
3683
  };
3511
- rootElement.addEventListener(eventName, eventHandler);
3512
- removeHandles.push(() => {
3513
- rootElement.removeEventListener(eventName, eventHandler);
3514
- });
3684
+ removeHandles.push(registerEventListener(rootElement, eventName, eventHandler));
3515
3685
  }
3516
3686
  }
3517
3687
  const rootElementNotRegisteredWarning = warnOnlyOnce("Root element not registered");
@@ -3526,11 +3696,7 @@ function removeRootElementEvents(rootElement) {
3526
3696
  rootElementNotRegisteredWarning();
3527
3697
  return;
3528
3698
  }
3529
- const newCount = registration.rootElementCount - 1;
3530
- if (!(newCount >= 0)) formatDevErrorMessage(`Root element count less than 0`);
3531
3699
  rootElementToDocument.delete(rootElement);
3532
- registration.rootElementCount = newCount;
3533
- if (newCount === 0) doc.removeEventListener("selectionchange", onDocumentSelectionChange);
3534
3700
  const editor = getEditorPropertyFromDOMNode(rootElement);
3535
3701
  if (isLexicalEditor(editor)) {
3536
3702
  cleanActiveNestedEditorsMap(editor);
@@ -3549,17 +3715,19 @@ function cleanActiveNestedEditorsMap(editor) {
3549
3715
  if (activeNestedEditorsMap.get(rootEditorKey) === editor) activeNestedEditorsMap.delete(rootEditorKey);
3550
3716
  } else activeNestedEditorsMap.delete(editor._key);
3551
3717
  }
3552
- function markSelectionChangeFromDOMUpdate() {
3553
- isSelectionChangeFromDOMUpdate = true;
3718
+ /** @internal */
3719
+ function markSelectionChangeFromDOMUpdate(editor) {
3720
+ editor._inputState.isSelectionChangeFromDOMUpdate = true;
3554
3721
  }
3555
- function markCollapsedSelectionFormat(format, style, offset, key, timeStamp) {
3556
- collapsedSelectionFormat = [
3722
+ /** @internal */
3723
+ function markCollapsedSelectionFormat(editor, format, style, offset, key, timeStamp) {
3724
+ editor._inputState.collapsedSelectionFormat = {
3557
3725
  format,
3558
- style,
3559
- offset,
3560
3726
  key,
3727
+ offset,
3728
+ style,
3561
3729
  timeStamp
3562
- ];
3730
+ };
3563
3731
  }
3564
3732
  /**
3565
3733
  * The base type for all serialized nodes
@@ -4215,8 +4383,7 @@ var LexicalNode = class {
4215
4383
  * @param targetNode - the would-be child node.
4216
4384
  */
4217
4385
  isParentOf(targetNode) {
4218
- const result = $getCommonAncestor(this, targetNode);
4219
- return result !== null && result.type === "ancestor";
4386
+ return $hasAncestor(targetNode, this);
4220
4387
  }
4221
4388
  /**
4222
4389
  * Returns a list of nodes that are between this node and
@@ -5001,7 +5168,7 @@ function $setTextContent(nextText, dom, node) {
5001
5168
  const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot(node, dom, editor);
5002
5169
  const firstChild = slot.getFirstChild();
5003
5170
  if (firstChild === null || firstChild.nodeType !== Node.TEXT_NODE) {
5004
- slot.insertChild(document.createTextNode(text));
5171
+ slot.insertChild($getDocument().createTextNode(text));
5005
5172
  return;
5006
5173
  }
5007
5174
  const textChild = firstChild;
@@ -5018,13 +5185,16 @@ function $createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
5018
5185
  const textClassNames = config.theme.text;
5019
5186
  if (textClassNames !== void 0) setTextThemeClassNames(innerTag, 0, format, innerDOM, textClassNames);
5020
5187
  }
5021
- function wrapElementWith(element, tag) {
5022
- const el = document.createElement(tag);
5188
+ function $wrapElementWith(element, tag) {
5189
+ const el = $getDocument().createElement(tag);
5023
5190
  el.appendChild(element);
5024
5191
  return el;
5025
5192
  }
5193
+ function $isInlineFormattable(node) {
5194
+ return node != null && node.__isInlineFormattable === true;
5195
+ }
5026
5196
  /** @noInheritDoc */
5027
- var TextNode = class TextNode extends LexicalNode {
5197
+ var TextNode = class extends LexicalNode {
5028
5198
  /** @internal */
5029
5199
  __text;
5030
5200
  /** @internal */
@@ -5035,11 +5205,61 @@ var TextNode = class TextNode extends LexicalNode {
5035
5205
  __mode;
5036
5206
  /** @internal */
5037
5207
  __detail;
5038
- static getType() {
5039
- return "text";
5208
+ /** @internal */
5209
+ get __isInlineFormattable() {
5210
+ return true;
5040
5211
  }
5041
- static clone(node) {
5042
- return new TextNode(node.__text, node.__key);
5212
+ $config() {
5213
+ return this.config("text", { importDOM: {
5214
+ "#text": () => ({
5215
+ conversion: $convertTextDOMNode,
5216
+ priority: 0
5217
+ }),
5218
+ b: () => ({
5219
+ conversion: convertBringAttentionToElement,
5220
+ priority: 0
5221
+ }),
5222
+ code: () => ({
5223
+ conversion: convertTextFormatElement,
5224
+ priority: 0
5225
+ }),
5226
+ em: () => ({
5227
+ conversion: convertTextFormatElement,
5228
+ priority: 0
5229
+ }),
5230
+ i: () => ({
5231
+ conversion: convertTextFormatElement,
5232
+ priority: 0
5233
+ }),
5234
+ mark: () => ({
5235
+ conversion: convertTextFormatElement,
5236
+ priority: 0
5237
+ }),
5238
+ s: () => ({
5239
+ conversion: convertTextFormatElement,
5240
+ priority: 0
5241
+ }),
5242
+ span: () => ({
5243
+ conversion: convertSpanElement,
5244
+ priority: 0
5245
+ }),
5246
+ strong: () => ({
5247
+ conversion: convertTextFormatElement,
5248
+ priority: 0
5249
+ }),
5250
+ sub: () => ({
5251
+ conversion: convertTextFormatElement,
5252
+ priority: 0
5253
+ }),
5254
+ sup: () => ({
5255
+ conversion: convertTextFormatElement,
5256
+ priority: 0
5257
+ }),
5258
+ u: () => ({
5259
+ conversion: convertTextFormatElement,
5260
+ priority: 0
5261
+ })
5262
+ } });
5043
5263
  }
5044
5264
  afterCloneFrom(prevNode) {
5045
5265
  super.afterCloneFrom(prevNode);
@@ -5082,7 +5302,8 @@ var TextNode = class TextNode extends LexicalNode {
5082
5302
  * @returns TextModeType.
5083
5303
  */
5084
5304
  getMode() {
5085
- return TEXT_TYPE_TO_MODE[this.getLatest().__mode];
5305
+ const self = this.getLatest();
5306
+ return TEXT_TYPE_TO_MODE[self.__mode];
5086
5307
  }
5087
5308
  /**
5088
5309
  * Returns the styles currently applied to the node. This is analogous to CSSText in the DOM.
@@ -5191,11 +5412,11 @@ var TextNode = class TextNode extends LexicalNode {
5191
5412
  const outerTag = getElementOuterTag(this, format);
5192
5413
  const innerTag = getElementInnerTag(this, format);
5193
5414
  const tag = outerTag === null ? innerTag : outerTag;
5194
- const dom = document.createElement(tag);
5415
+ const dom = $getDocument().createElement(tag);
5195
5416
  let innerDOM = dom;
5196
5417
  if (this.hasFormat("code")) dom.setAttribute("spellcheck", "false");
5197
5418
  if (outerTag !== null) {
5198
- innerDOM = document.createElement(innerTag);
5419
+ innerDOM = $getDocument().createElement(innerTag);
5199
5420
  dom.appendChild(innerDOM);
5200
5421
  }
5201
5422
  const text = this.__text;
@@ -5216,7 +5437,7 @@ var TextNode = class TextNode extends LexicalNode {
5216
5437
  if (prevOuterTag === nextOuterTag && prevInnerTag !== nextInnerTag) {
5217
5438
  const prevInnerDOM = dom.firstChild;
5218
5439
  if (prevInnerDOM == null) formatDevErrorMessage(`updateDOM: prevInnerDOM is null or undefined`);
5219
- const nextInnerDOM = document.createElement(nextInnerTag);
5440
+ const nextInnerDOM = $getDocument().createElement(nextInnerTag);
5220
5441
  $createTextInnerDOM(nextInnerDOM, this, nextInnerTag, nextFormat, nextText, config);
5221
5442
  dom.replaceChild(nextInnerDOM, prevInnerDOM);
5222
5443
  return false;
@@ -5236,61 +5457,6 @@ var TextNode = class TextNode extends LexicalNode {
5236
5457
  if (prevStyle !== nextStyle) setDOMStyleFromCSS(dom.style, nextStyle, prevStyle);
5237
5458
  return false;
5238
5459
  }
5239
- static importDOM() {
5240
- return {
5241
- "#text": () => ({
5242
- conversion: $convertTextDOMNode,
5243
- priority: 0
5244
- }),
5245
- b: () => ({
5246
- conversion: convertBringAttentionToElement,
5247
- priority: 0
5248
- }),
5249
- code: () => ({
5250
- conversion: convertTextFormatElement,
5251
- priority: 0
5252
- }),
5253
- em: () => ({
5254
- conversion: convertTextFormatElement,
5255
- priority: 0
5256
- }),
5257
- i: () => ({
5258
- conversion: convertTextFormatElement,
5259
- priority: 0
5260
- }),
5261
- mark: () => ({
5262
- conversion: convertTextFormatElement,
5263
- priority: 0
5264
- }),
5265
- s: () => ({
5266
- conversion: convertTextFormatElement,
5267
- priority: 0
5268
- }),
5269
- span: () => ({
5270
- conversion: convertSpanElement,
5271
- priority: 0
5272
- }),
5273
- strong: () => ({
5274
- conversion: convertTextFormatElement,
5275
- priority: 0
5276
- }),
5277
- sub: () => ({
5278
- conversion: convertTextFormatElement,
5279
- priority: 0
5280
- }),
5281
- sup: () => ({
5282
- conversion: convertTextFormatElement,
5283
- priority: 0
5284
- }),
5285
- u: () => ({
5286
- conversion: convertTextFormatElement,
5287
- priority: 0
5288
- })
5289
- };
5290
- }
5291
- static importJSON(serializedNode) {
5292
- return $createTextNode().updateFromJSON(serializedNode);
5293
- }
5294
5460
  updateFromJSON(serializedNode) {
5295
5461
  return super.updateFromJSON(serializedNode).setTextContent(serializedNode.text).setFormat(serializedNode.format).setDetail(serializedNode.detail).setMode(serializedNode.mode).setStyle(serializedNode.style);
5296
5462
  }
@@ -5301,10 +5467,10 @@ var TextNode = class TextNode extends LexicalNode {
5301
5467
  if (this.hasFormat("lowercase")) element.style.textTransform = "lowercase";
5302
5468
  else if (this.hasFormat("uppercase")) element.style.textTransform = "uppercase";
5303
5469
  else if (this.hasFormat("capitalize")) element.style.textTransform = "capitalize";
5304
- if (this.hasFormat("bold")) element = wrapElementWith(element, "b");
5305
- if (this.hasFormat("italic")) element = wrapElementWith(element, "i");
5306
- if (this.hasFormat("strikethrough")) element = wrapElementWith(element, "s");
5307
- if (this.hasFormat("underline")) element = wrapElementWith(element, "u");
5470
+ if (this.hasFormat("bold")) element = $wrapElementWith(element, "b");
5471
+ if (this.hasFormat("italic")) element = $wrapElementWith(element, "i");
5472
+ if (this.hasFormat("strikethrough")) element = $wrapElementWith(element, "s");
5473
+ if (this.hasFormat("underline")) element = $wrapElementWith(element, "u");
5308
5474
  return { element };
5309
5475
  }
5310
5476
  exportJSON() {
@@ -5398,6 +5564,12 @@ var TextNode = class TextNode extends LexicalNode {
5398
5564
  /**
5399
5565
  * Sets the mode of the node.
5400
5566
  *
5567
+ * Note: during IME composition, a segmented TextNode may be temporarily
5568
+ * switched to normal mode to preserve the DOM element that the browser's
5569
+ * composition tracker is bound to. Subclass transforms or method overrides
5570
+ * that assume the node is always in segmented mode should account for this
5571
+ * transient state.
5572
+ *
5401
5573
  * @returns this TextNode.
5402
5574
  */
5403
5575
  setMode(type) {
@@ -5758,7 +5930,7 @@ function applyTextFormatFromStyle(style, shouldApply) {
5758
5930
  const hasUnderlineTextDecoration = textDecoration.includes("underline");
5759
5931
  const verticalAlign = style.verticalAlign;
5760
5932
  return (lexicalNode) => {
5761
- if (!$isTextNode(lexicalNode)) return lexicalNode;
5933
+ if (!$isTextNode(lexicalNode) && !$isInlineFormattable(lexicalNode)) return lexicalNode;
5762
5934
  if (hasBoldFontWeight && !lexicalNode.hasFormat("bold")) lexicalNode.toggleFormat("bold");
5763
5935
  if (hasLinethroughTextDecoration && !lexicalNode.hasFormat("strikethrough")) lexicalNode.toggleFormat("strikethrough");
5764
5936
  if (hasItalicFontStyle && !lexicalNode.hasFormat("italic")) lexicalNode.toggleFormat("italic");
@@ -5770,29 +5942,20 @@ function applyTextFormatFromStyle(style, shouldApply) {
5770
5942
  };
5771
5943
  }
5772
5944
  /** @noInheritDoc */
5773
- var TabNode = class TabNode extends TextNode {
5774
- static getType() {
5775
- return "tab";
5776
- }
5777
- static clone(node) {
5778
- return new TabNode(node.__key);
5945
+ var TabNode = class extends TextNode {
5946
+ $config() {
5947
+ return this.config("tab", { extends: TextNode });
5779
5948
  }
5780
- constructor(key) {
5949
+ constructor(key = void 0) {
5781
5950
  super(" ", key);
5782
5951
  this.__detail = IS_UNMERGEABLE;
5783
5952
  }
5784
- static importDOM() {
5785
- return null;
5786
- }
5787
5953
  createDOM(config) {
5788
5954
  const dom = super.createDOM(config);
5789
5955
  const classNames = getCachedClassNameArray(config.theme, "tab");
5790
5956
  if (classNames !== void 0) dom.classList.add(...classNames);
5791
5957
  return dom;
5792
5958
  }
5793
- static importJSON(serializedTabNode) {
5794
- return $createTabNode().updateFromJSON(serializedTabNode);
5795
- }
5796
5959
  /**
5797
5960
  * Always normalizes the stored content to `'\t'` regardless of input — see
5798
5961
  * comment below for the rationale.
@@ -5926,6 +6089,26 @@ function $transferStartingElementPointToTextPoint(start, end, format, style) {
5926
6089
  if (start.is(end)) end.set(textNode.__key, 0, "text");
5927
6090
  start.set(textNode.__key, 0, "text");
5928
6091
  }
6092
+ function $insertTextAtPoint(selection, text, format, style) {
6093
+ const anchorNode = selection.anchor.getNode();
6094
+ if (!$isTextNode(anchorNode)) formatDevErrorMessage(`insertText: anchor is not a text node`);
6095
+ const offset = selection.anchor.offset;
6096
+ const textNode = $createTextNode(text);
6097
+ textNode.setFormat(format);
6098
+ textNode.setStyle(style);
6099
+ const parent = anchorNode.getParentOrThrow();
6100
+ if (offset === 0) if (parent.isInline() && !anchorNode.__prev) parent.insertBefore(textNode);
6101
+ else anchorNode.insertBefore(textNode, false);
6102
+ else if (offset === anchorNode.getTextContentSize()) if (parent.isInline() && !anchorNode.__next) parent.insertAfter(textNode);
6103
+ else anchorNode.insertAfter(textNode, false);
6104
+ else {
6105
+ const [before] = anchorNode.splitText(offset);
6106
+ before.insertAfter(textNode, false);
6107
+ }
6108
+ if (anchorNode.getTextContent() === "" && anchorNode.isAttached()) anchorNode.remove();
6109
+ textNode.selectEnd();
6110
+ if (textNode.isComposing() && selection.anchor.type === "text") selection.anchor.set(selection.anchor.key, selection.anchor.offset - text.length, selection.anchor.type);
6111
+ }
5929
6112
  var NodeSelection = class NodeSelection {
5930
6113
  _nodes;
5931
6114
  _cachedNodes;
@@ -6026,8 +6209,17 @@ var NodeSelection = class NodeSelection {
6026
6209
  $setSelectionFromCaretRange($getCaretRange(firstCaret, firstCaret));
6027
6210
  }
6028
6211
  for (const node of nodes) node.remove();
6212
+ $ensureRootHasParagraph();
6029
6213
  }
6030
6214
  };
6215
+ function $ensureRootHasParagraph() {
6216
+ const root = $getRoot();
6217
+ if (root.isEmpty()) {
6218
+ const paragraph = $createParagraphNode();
6219
+ root.append(paragraph);
6220
+ paragraph.select();
6221
+ }
6222
+ }
6031
6223
  function $isRangeSelection(x) {
6032
6224
  return x instanceof RangeSelection;
6033
6225
  }
@@ -6207,11 +6399,11 @@ var RangeSelection = class RangeSelection {
6207
6399
  this.dirty = true;
6208
6400
  }
6209
6401
  /**
6210
- * Returns whether the provided TextFormatType is present on the Selection. This will be true if any node in the Selection
6211
- * has the specified format.
6402
+ * Returns whether the provided TextFormatType is present on the Selection. This will be true if all text nodes in the Selection
6403
+ * have the specified format.
6212
6404
  *
6213
6405
  * @param type the TextFormatType to check for.
6214
- * @returns true if the provided format is currently toggled on on the Selection, false otherwise.
6406
+ * @returns true if the provided format is currently toggled on the Selection, false otherwise.
6215
6407
  */
6216
6408
  hasFormat(type) {
6217
6409
  const formatFlag = TEXT_TYPE_TO_FORMAT[type];
@@ -6232,196 +6424,91 @@ var RangeSelection = class RangeSelection {
6232
6424
  * @param text the text to insert into the Selection
6233
6425
  */
6234
6426
  insertText(text) {
6235
- const anchor = this.anchor;
6236
- const focus = this.focus;
6237
- const format = this.format;
6238
- const style = this.style;
6239
- let firstPoint = anchor;
6240
- let endPoint = focus;
6241
- if (!this.isCollapsed() && focus.isBefore(anchor)) {
6242
- firstPoint = focus;
6243
- endPoint = anchor;
6244
- }
6245
- if (firstPoint.type === "element") $transferStartingElementPointToTextPoint(firstPoint, endPoint, format, style);
6246
- if (endPoint.type === "element") $setPointFromCaret(endPoint, $normalizeCaret($caretFromPoint(endPoint, "next")));
6247
- const startOffset = firstPoint.offset;
6248
- let endOffset = endPoint.offset;
6249
- const selectedNodes = this.getNodes();
6250
- const selectedNodesLength = selectedNodes.length;
6251
- let firstNode = selectedNodes[0];
6252
- if (!$isTextNode(firstNode)) formatDevErrorMessage(`insertText: first node is not a text node`);
6253
- const firstNodeTextLength = firstNode.getTextContent().length;
6254
- const firstNodeParent = firstNode.getParentOrThrow();
6255
- let lastNode = selectedNodes[selectedNodesLength - 1];
6256
- if (selectedNodesLength === 1 && endPoint.type === "element") {
6257
- endOffset = firstNodeTextLength;
6258
- endPoint.set(firstPoint.key, endOffset, "text");
6259
- }
6260
- if (this.isCollapsed() && startOffset === firstNodeTextLength && ($isTokenOrSegmented(firstNode) || !firstNode.canInsertTextAfter() || !firstNodeParent.canInsertTextAfter() && firstNode.getNextSibling() === null)) {
6261
- const candidateNextSibling = firstNode.getNextSibling();
6262
- let nextSibling;
6263
- if (!$isTextNode(candidateNextSibling) || !candidateNextSibling.canInsertTextBefore() || $isTokenOrSegmented(candidateNextSibling)) {
6264
- nextSibling = $createTextNode();
6265
- nextSibling.setFormat(format);
6266
- nextSibling.setStyle(style);
6267
- if (!firstNodeParent.canInsertTextAfter()) firstNodeParent.insertAfter(nextSibling);
6268
- else firstNode.insertAfter(nextSibling);
6269
- } else nextSibling = candidateNextSibling;
6270
- nextSibling.select(0, 0);
6271
- firstNode = nextSibling;
6272
- if (text !== "") {
6273
- this.insertText(text);
6274
- return;
6427
+ let format = this.format;
6428
+ let style = this.style;
6429
+ if (!this.isCollapsed()) {
6430
+ const firstNode = (this.focus.isBefore(this.anchor) ? this.focus : this.anchor).getNode();
6431
+ if ($isTextNode(firstNode)) {
6432
+ format = firstNode.getFormat();
6433
+ style = firstNode.getStyle();
6275
6434
  }
6276
- } else if (this.isCollapsed() && startOffset === 0 && ($isTokenOrSegmented(firstNode) || !firstNode.canInsertTextBefore() || !firstNodeParent.canInsertTextBefore() && firstNode.getPreviousSibling() === null)) {
6277
- const candidatePrevSibling = firstNode.getPreviousSibling();
6278
- let prevSibling;
6279
- if (!$isTextNode(candidatePrevSibling) || $isTokenOrSegmented(candidatePrevSibling)) {
6280
- prevSibling = $createTextNode();
6281
- prevSibling.setFormat(format);
6282
- if (!firstNodeParent.canInsertTextBefore()) firstNodeParent.insertBefore(prevSibling);
6283
- else firstNode.insertBefore(prevSibling);
6284
- } else prevSibling = candidatePrevSibling;
6285
- prevSibling.select();
6286
- firstNode = prevSibling;
6287
- if (text !== "") {
6288
- this.insertText(text);
6289
- return;
6290
- }
6291
- } else if (firstNode.isSegmented() && startOffset !== firstNodeTextLength) {
6292
- const textNode = $createTextNode(firstNode.getTextContent());
6293
- textNode.setFormat(format);
6294
- firstNode.replace(textNode);
6295
- firstNode = textNode;
6296
- } else if (!this.isCollapsed() && text !== "") {
6297
- const lastNodeParent = lastNode.getParent();
6298
- if (!firstNodeParent.canInsertTextBefore() || !firstNodeParent.canInsertTextAfter() || $isElementNode(lastNodeParent) && (!lastNodeParent.canInsertTextBefore() || !lastNodeParent.canInsertTextAfter())) {
6299
- this.insertText("");
6300
- $normalizeSelectionPointsForBoundaries(this.anchor, this.focus);
6301
- this.insertText(text);
6435
+ this.removeText();
6436
+ this.format = format;
6437
+ this.style = style;
6438
+ if (text === "") return;
6439
+ if ($getCompositionKey() === null) {
6440
+ if (this.anchor.type === "element") $transferStartingElementPointToTextPoint(this.anchor, this.focus, format, style);
6441
+ $insertTextAtPoint(this, text, format, style);
6302
6442
  return;
6303
6443
  }
6304
6444
  }
6305
- if (selectedNodesLength === 1) {
6306
- if ($isTokenOrTab(firstNode)) {
6307
- const textNode = $createTextNode(text);
6308
- textNode.select();
6309
- firstNode.replace(textNode);
6310
- return;
6311
- }
6312
- const firstNodeFormat = firstNode.getFormat();
6313
- const firstNodeStyle = firstNode.getStyle();
6314
- if (startOffset === endOffset && (firstNodeFormat !== format || firstNodeStyle !== style)) if (firstNode.getTextContent() === "") {
6315
- firstNode.setFormat(format);
6316
- firstNode.setStyle(style);
6317
- } else {
6318
- const textNode = $createTextNode(text);
6319
- textNode.setFormat(format);
6320
- textNode.setStyle(style);
6321
- textNode.select();
6322
- if (startOffset === 0) firstNode.insertBefore(textNode, false);
6445
+ if (this.anchor.type === "element") $transferStartingElementPointToTextPoint(this.anchor, this.focus, format, style);
6446
+ const anchorNode = this.anchor.getNode();
6447
+ if (!$isTextNode(anchorNode)) formatDevErrorMessage(`insertText: anchor is not a text node`);
6448
+ const offset = this.anchor.offset;
6449
+ const anchorParent = anchorNode.getParentOrThrow();
6450
+ const anchorSize = anchorNode.getTextContentSize();
6451
+ if ($isTokenOrSegmented(anchorNode) || offset === 0 && (!anchorNode.canInsertTextBefore() || !anchorParent.canInsertTextBefore() && !anchorNode.__prev) || offset === anchorSize && (!anchorNode.canInsertTextAfter() || !anchorParent.canInsertTextAfter() && !anchorNode.__next)) {
6452
+ if (anchorNode.isSegmented() && offset !== 0 && offset !== anchorSize) {
6453
+ if ($getCompositionKey() !== null) anchorNode.setMode("normal").setFormat(format).setStyle(style);
6323
6454
  else {
6324
- const [targetNode] = firstNode.splitText(startOffset);
6325
- targetNode.insertAfter(textNode, false);
6326
- }
6327
- if (textNode.isComposing() && this.anchor.type === "text") {
6328
- this.anchor.offset -= text.length;
6329
- this._cachedNodes = null;
6330
- this._cachedIsBackward = null;
6455
+ const replacement = $createTextNode(anchorNode.getTextContent());
6456
+ replacement.setFormat(format);
6457
+ replacement.setStyle(style);
6458
+ anchorNode.replace(replacement);
6459
+ replacement.select(offset, offset);
6331
6460
  }
6461
+ if (text !== "") this.insertText(text);
6332
6462
  return;
6333
6463
  }
6334
- else if ($isTabNode(firstNode)) {
6335
- const textNode = $createTextNode(text);
6336
- textNode.setFormat(format);
6337
- textNode.setStyle(style);
6338
- textNode.select();
6339
- firstNode.replace(textNode);
6464
+ if (text === "") return;
6465
+ if (offset === 0) {
6466
+ const prev = anchorNode.getPreviousSibling();
6467
+ if ($isTextNode(prev) && prev.canInsertTextAfter() && !$isTokenOrSegmented(prev)) prev.select();
6468
+ else {
6469
+ const newNode = $createTextNode();
6470
+ newNode.setFormat(format);
6471
+ newNode.setStyle(style);
6472
+ if (!anchorParent.canInsertTextBefore()) anchorParent.insertBefore(newNode);
6473
+ else anchorNode.insertBefore(newNode);
6474
+ newNode.select();
6475
+ }
6476
+ this.insertText(text);
6340
6477
  return;
6341
- }
6342
- const delCount = endOffset - startOffset;
6343
- firstNode = firstNode.spliceText(startOffset, delCount, text, true);
6344
- if (firstNode.getTextContent() === "") firstNode.remove();
6345
- else if (this.anchor.type === "text") {
6346
- this.format = firstNodeFormat;
6347
- this.style = firstNodeStyle;
6348
- if (firstNode.isComposing()) {
6349
- this.anchor.offset -= text.length;
6350
- this._cachedNodes = null;
6351
- this._cachedIsBackward = null;
6478
+ } else if (offset === anchorSize) {
6479
+ const next = anchorNode.getNextSibling();
6480
+ if ($isTextNode(next) && next.canInsertTextBefore() && !$isTokenOrSegmented(next)) next.select(0, 0);
6481
+ else {
6482
+ const newNode = $createTextNode();
6483
+ newNode.setFormat(format);
6484
+ newNode.setStyle(style);
6485
+ if (!anchorParent.canInsertTextAfter()) anchorParent.insertAfter(newNode);
6486
+ else anchorNode.insertAfter(newNode);
6487
+ newNode.select(0, 0);
6352
6488
  }
6489
+ this.insertText(text);
6490
+ return;
6353
6491
  }
6492
+ const newNode = $createTextNode(text);
6493
+ newNode.setFormat(format);
6494
+ newNode.setStyle(style);
6495
+ anchorNode.replace(newNode);
6496
+ newNode.select();
6497
+ return;
6498
+ }
6499
+ if (text === "") return;
6500
+ const atStartOfInline = anchorParent.isInline() && offset === 0 && !anchorNode.__prev;
6501
+ const atEndOfInline = anchorParent.isInline() && offset === anchorSize && !anchorNode.__next;
6502
+ const formatDiffers = anchorNode.getFormat() !== format || anchorNode.getStyle() !== style;
6503
+ if (atStartOfInline || atEndOfInline || formatDiffers) if (anchorNode.getTextContent() === "" && !atStartOfInline && !atEndOfInline) {
6504
+ anchorNode.setFormat(format);
6505
+ anchorNode.setStyle(style);
6354
6506
  } else {
6355
- const markedNodeKeysForKeep = /* @__PURE__ */ new Set([...firstNode.getParentKeys(), ...lastNode.getParentKeys()]);
6356
- const firstElement = $isElementNode(firstNode) ? firstNode : firstNode.getParentOrThrow();
6357
- let lastElement = $isElementNode(lastNode) ? lastNode : lastNode.getParentOrThrow();
6358
- let lastElementChild = lastNode;
6359
- if (!firstElement.is(lastElement) && lastElement.isInline()) do {
6360
- lastElementChild = lastElement;
6361
- lastElement = lastElement.getParentOrThrow();
6362
- } while (lastElement.isInline());
6363
- if (endPoint.type === "text" && (endOffset !== 0 || lastNode.getTextContent() === "") || endPoint.type === "element" && lastNode.getIndexWithinParent() < endOffset) if ($isTextNode(lastNode) && !$isTokenOrTab(lastNode) && endOffset !== lastNode.getTextContentSize()) {
6364
- if (lastNode.isSegmented()) {
6365
- const textNode = $createTextNode(lastNode.getTextContent());
6366
- lastNode.replace(textNode);
6367
- lastNode = textNode;
6368
- }
6369
- if (!$isRootNode(endPoint.getNode()) && endPoint.type === "text") lastNode = lastNode.spliceText(0, endOffset, "");
6370
- markedNodeKeysForKeep.add(lastNode.__key);
6371
- } else {
6372
- const lastNodeParent = lastNode.getParentOrThrow();
6373
- if (!lastNodeParent.canBeEmpty() && lastNodeParent.getChildrenSize() === 1) lastNodeParent.remove();
6374
- else lastNode.remove();
6375
- }
6376
- else markedNodeKeysForKeep.add(lastNode.__key);
6377
- const lastNodeChildren = lastElement.getChildren();
6378
- const selectedNodesSet = new Set(selectedNodes);
6379
- const firstAndLastElementsAreEqual = firstElement.is(lastElement);
6380
- const insertionTarget = firstElement.isInline() && firstNode.getNextSibling() === null ? firstElement : firstNode;
6381
- for (let i = lastNodeChildren.length - 1; i >= 0; i--) {
6382
- const lastNodeChild = lastNodeChildren[i];
6383
- if (lastNodeChild.is(firstNode) || $isElementNode(lastNodeChild) && lastNodeChild.isParentOf(firstNode)) break;
6384
- if (lastNodeChild.isAttached()) if (!selectedNodesSet.has(lastNodeChild) || lastNodeChild.is(lastElementChild)) {
6385
- if (!firstAndLastElementsAreEqual) insertionTarget.insertAfter(lastNodeChild, false);
6386
- } else lastNodeChild.remove();
6387
- }
6388
- if (!firstAndLastElementsAreEqual) {
6389
- let parent = lastElement;
6390
- let lastRemovedParent = null;
6391
- while (parent !== null) {
6392
- const children = parent.getChildren();
6393
- const childrenLength = children.length;
6394
- if (childrenLength === 0 || children[childrenLength - 1].is(lastRemovedParent)) {
6395
- markedNodeKeysForKeep.delete(parent.__key);
6396
- lastRemovedParent = parent;
6397
- }
6398
- parent = parent.getParent();
6399
- }
6400
- }
6401
- if (!$isTokenOrTab(firstNode)) {
6402
- firstNode = firstNode.spliceText(startOffset, firstNodeTextLength - startOffset, text, true);
6403
- if (firstNode.getTextContent() === "") firstNode.remove();
6404
- else if (this.anchor.type === "text") {
6405
- this.format = firstNode.getFormat();
6406
- this.style = firstNode.getStyle();
6407
- if (firstNode.isComposing()) {
6408
- this.anchor.offset -= text.length;
6409
- this._cachedNodes = null;
6410
- this._cachedIsBackward = null;
6411
- }
6412
- }
6413
- } else if (startOffset === firstNodeTextLength) firstNode.select();
6414
- else {
6415
- const textNode = $createTextNode(text);
6416
- textNode.select();
6417
- firstNode.replace(textNode);
6418
- }
6419
- for (let i = 1; i < selectedNodesLength; i++) {
6420
- const selectedNode = selectedNodes[i];
6421
- const key = selectedNode.__key;
6422
- if (!markedNodeKeysForKeep.has(key)) selectedNode.remove();
6423
- }
6507
+ $insertTextAtPoint(this, text, format, style);
6508
+ return;
6424
6509
  }
6510
+ anchorNode.spliceText(offset, 0, text, true);
6511
+ if (anchorNode.isComposing() && this.anchor.type === "text") this.anchor.set(this.anchor.key, this.anchor.offset - text.length, this.anchor.type);
6425
6512
  }
6426
6513
  /**
6427
6514
  * Removes the text in the Selection, adjusting the EditorState accordingly.
@@ -6440,79 +6527,7 @@ var RangeSelection = class RangeSelection {
6440
6527
  * @param alignWithFormat a 32-bit integer representing formatting flags to align with.
6441
6528
  */
6442
6529
  formatText(formatType, alignWithFormat = null) {
6443
- if (this.isCollapsed()) {
6444
- this.toggleFormat(formatType);
6445
- $setCompositionKey(null);
6446
- return;
6447
- }
6448
- const selectedNodes = this.getNodes();
6449
- const selectedTextNodes = [];
6450
- for (const selectedNode of selectedNodes) if ($isTextNode(selectedNode)) selectedTextNodes.push(selectedNode);
6451
- const applyFormatToElements = (alignWith) => {
6452
- selectedNodes.forEach((node) => {
6453
- if ($isElementNode(node)) {
6454
- const newFormat = node.getFormatFlags(formatType, alignWith);
6455
- node.setTextFormat(newFormat);
6456
- }
6457
- });
6458
- };
6459
- const selectedTextNodesLength = selectedTextNodes.length;
6460
- if (selectedTextNodesLength === 0) {
6461
- this.toggleFormat(formatType);
6462
- $setCompositionKey(null);
6463
- applyFormatToElements(alignWithFormat);
6464
- return;
6465
- }
6466
- const anchor = this.anchor;
6467
- const focus = this.focus;
6468
- const isBackward = this.isBackward();
6469
- const startPoint = isBackward ? focus : anchor;
6470
- const endPoint = isBackward ? anchor : focus;
6471
- let firstIndex = 0;
6472
- let firstNode = selectedTextNodes[0];
6473
- let startOffset = startPoint.type === "element" ? 0 : startPoint.offset;
6474
- if (startPoint.type === "text" && startOffset === firstNode.getTextContentSize()) {
6475
- firstIndex = 1;
6476
- firstNode = selectedTextNodes[1];
6477
- startOffset = 0;
6478
- }
6479
- if (firstNode == null) return;
6480
- const firstNextFormat = firstNode.getFormatFlags(formatType, alignWithFormat);
6481
- applyFormatToElements(firstNextFormat);
6482
- const lastIndex = selectedTextNodesLength - 1;
6483
- let lastNode = selectedTextNodes[lastIndex];
6484
- const endOffset = endPoint.type === "text" ? endPoint.offset : lastNode.getTextContentSize();
6485
- if (firstNode.is(lastNode)) {
6486
- if (startOffset === endOffset) return;
6487
- if ($isTokenOrSegmented(firstNode) || startOffset === 0 && endOffset === firstNode.getTextContentSize()) firstNode.setFormat(firstNextFormat);
6488
- else {
6489
- const splitNodes = firstNode.splitText(startOffset, endOffset);
6490
- const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
6491
- replacement.setFormat(firstNextFormat);
6492
- if (startPoint.type === "text") startPoint.set(replacement.__key, 0, "text");
6493
- if (endPoint.type === "text") endPoint.set(replacement.__key, endOffset - startOffset, "text");
6494
- }
6495
- this.format = firstNextFormat;
6496
- return;
6497
- }
6498
- if (startOffset !== 0 && !$isTokenOrSegmented(firstNode)) {
6499
- [, firstNode] = firstNode.splitText(startOffset);
6500
- startOffset = 0;
6501
- }
6502
- firstNode.setFormat(firstNextFormat);
6503
- const lastNextFormat = lastNode.getFormatFlags(formatType, firstNextFormat);
6504
- if (endOffset > 0) {
6505
- if (endOffset !== lastNode.getTextContentSize() && !$isTokenOrSegmented(lastNode)) [lastNode] = lastNode.splitText(endOffset);
6506
- lastNode.setFormat(lastNextFormat);
6507
- }
6508
- for (let i = firstIndex + 1; i < lastIndex; i++) {
6509
- const textNode = selectedTextNodes[i];
6510
- const nextFormat = textNode.getFormatFlags(formatType, lastNextFormat);
6511
- textNode.setFormat(nextFormat);
6512
- }
6513
- if (startPoint.type === "text") startPoint.set(firstNode.__key, startOffset, "text");
6514
- if (endPoint.type === "text") endPoint.set(lastNode.__key, endOffset, "text");
6515
- this.format = firstNextFormat | lastNextFormat;
6530
+ $formatText(this, formatType, alignWithFormat);
6516
6531
  }
6517
6532
  /**
6518
6533
  * Attempts to "intelligently" insert an arbitrary list of Lexical nodes into the EditorState at the
@@ -6546,8 +6561,8 @@ var RangeSelection = class RangeSelection {
6546
6561
  if (nodeToSelect !== null) nodeToSelect.selectEnd();
6547
6562
  return;
6548
6563
  }
6549
- const firstNode = (this.isBackward() ? this.focus : this.anchor).getNode();
6550
- const firstBlock = $findMatchingParent(firstNode, INTERNAL_$isBlock);
6564
+ let firstNode = (this.isBackward() ? this.focus : this.anchor).getNode();
6565
+ let firstBlock = $findMatchingParent(firstNode, INTERNAL_$isBlock);
6551
6566
  const last = nodes[nodes.length - 1];
6552
6567
  if ($isElementNode(firstBlock) && "__language" in firstBlock) {
6553
6568
  if ("__language" in nodes[0]) this.insertText(nodes[0].getTextContent());
@@ -6597,6 +6612,10 @@ var RangeSelection = class RangeSelection {
6597
6612
  const blocks = blocksParent.getChildren();
6598
6613
  const isMergeable = (node) => $isElementNode(node) && INTERNAL_$isBlock(node) && !node.isEmpty() && $isElementNode(firstBlock) && (!firstBlock.isEmpty() || firstBlock.canMergeWhenEmpty());
6599
6614
  const insertedParagraph = !$isElementNode(firstBlock) || !firstBlock.isEmpty() ? this.insertParagraph() : null;
6615
+ if (insertedParagraph && !firstBlock.isAttached()) {
6616
+ firstNode = this.anchor.getNode();
6617
+ firstBlock = $findMatchingParent(firstNode, INTERNAL_$isBlock);
6618
+ }
6600
6619
  const lastToInsert = blocks[blocks.length - 1];
6601
6620
  let firstToInsert = blocks[0];
6602
6621
  if (isMergeable(firstToInsert)) {
@@ -6725,21 +6744,37 @@ var RangeSelection = class RangeSelection {
6725
6744
  const rootElement = editor._rootElement;
6726
6745
  const focusNode = this.focus.getNode();
6727
6746
  if (rootElement !== null && blockCursorElement !== null && $isElementNode(focusNode) && !focusNode.isInline() && !focusNode.canBeEmpty()) removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
6747
+ const focusKeyedDOM = getElementByKeyOrThrow(editor, this.focus.key);
6748
+ let nextFocusDOM = focusKeyedDOM;
6749
+ if (this.focus.type === "text") nextFocusDOM = $isTextNode(focusNode) ? $getDOMTextNode(focusNode, focusKeyedDOM, editor) : null;
6728
6750
  if (this.dirty) {
6729
6751
  const anchorKeyedDOM = getElementByKeyOrThrow(editor, this.anchor.key);
6730
- const focusKeyedDOM = getElementByKeyOrThrow(editor, this.focus.key);
6731
6752
  let nextAnchorDOM = anchorKeyedDOM;
6732
- let nextFocusDOM = focusKeyedDOM;
6733
6753
  if (this.anchor.type === "text") {
6734
6754
  const node = this.anchor.getNode();
6735
6755
  nextAnchorDOM = $isTextNode(node) ? $getDOMTextNode(node, anchorKeyedDOM, editor) : null;
6736
6756
  }
6737
- if (this.focus.type === "text") {
6738
- const node = this.focus.getNode();
6739
- nextFocusDOM = $isTextNode(node) ? $getDOMTextNode(node, focusKeyedDOM, editor) : null;
6740
- }
6741
6757
  if (nextAnchorDOM && nextFocusDOM) setDOMSelectionBaseAndExtent(domSelection, nextAnchorDOM, this.anchor.offset, nextFocusDOM, this.focus.offset);
6742
6758
  }
6759
+ if (granularity === "character" && $isTextNode(focusNode) && focusNode.isUnmergeable()) {
6760
+ if (isBackward ? this.focus.offset === 0 : this.focus.offset === focusNode.getTextContentSize()) {
6761
+ const sibling = $getSiblingCaret(focusNode, isBackward ? "previous" : "next").getNodeAtCaret();
6762
+ if ($isTextNode(sibling)) if (collapse) {
6763
+ const sibKeyedDOM = editor.getElementByKey(sibling.getKey());
6764
+ const sibDOM = sibKeyedDOM ? $getDOMTextNode(sibling, sibKeyedDOM, editor) : null;
6765
+ if (sibDOM) {
6766
+ const sibOffset = isBackward ? sibDOM.length : 0;
6767
+ setDOMSelectionBaseAndExtent(domSelection, sibDOM, sibOffset, sibDOM, sibOffset);
6768
+ }
6769
+ } else {
6770
+ const sibLen = sibling.getTextContentSize();
6771
+ if (isBackward) this.focus.set(sibling.__key, sibLen - 1, "text");
6772
+ else this.focus.set(sibling.__key, 1, "text");
6773
+ this.dirty = true;
6774
+ return;
6775
+ }
6776
+ }
6777
+ }
6743
6778
  moveNativeSelection(domSelection, alter, isBackward ? "backward" : "forward", granularity);
6744
6779
  if (domSelection.rangeCount > 0) {
6745
6780
  const composedRange = getComposedStaticRange(domSelection, editor._rootElement);
@@ -6749,23 +6784,7 @@ var RangeSelection = class RangeSelection {
6749
6784
  this.applyDOMRange(range);
6750
6785
  this.dirty = true;
6751
6786
  if (!collapse) {
6752
- const nodes = this.getNodes();
6753
- const validNodes = [];
6754
- let shrinkSelection = false;
6755
- for (let i = 0; i < nodes.length; i++) {
6756
- const nextNode = nodes[i];
6757
- if ($hasAncestor(nextNode, root)) validNodes.push(nextNode);
6758
- else shrinkSelection = true;
6759
- }
6760
- if (shrinkSelection && validNodes.length > 0) if (isBackward) {
6761
- const firstValidNode = validNodes[0];
6762
- if ($isElementNode(firstValidNode)) firstValidNode.selectStart();
6763
- else firstValidNode.getParentOrThrow().selectStart();
6764
- } else {
6765
- const lastValidNode = validNodes[validNodes.length - 1];
6766
- if ($isElementNode(lastValidNode)) lastValidNode.selectEnd();
6767
- else lastValidNode.getParentOrThrow().selectEnd();
6768
- }
6787
+ $shrinkSelectionToRoot(this, isBackward, root);
6769
6788
  if (!(composedRange ? domSelection.direction !== "backward" : domSelection.anchorNode === range.startContainer && domSelection.anchorOffset === range.startOffset)) $swapPoints(this);
6770
6789
  }
6771
6790
  }
@@ -6862,7 +6881,7 @@ var RangeSelection = class RangeSelection {
6862
6881
  }
6863
6882
  }
6864
6883
  const focus = this.focus;
6865
- this.modify("extend", isBackward, "character");
6884
+ $extendSelectionForDeletion(this, isBackward, "character");
6866
6885
  if (!this.isCollapsed()) {
6867
6886
  const focusNode = focus.type === "text" ? focus.getNode() : null;
6868
6887
  anchorNode = anchor.type === "text" ? anchor.getNode() : null;
@@ -6890,6 +6909,7 @@ var RangeSelection = class RangeSelection {
6890
6909
  if (isBackward && !wasCollapsed && this.isCollapsed() && this.anchor.type === "element" && this.anchor.offset === 0) {
6891
6910
  const anchorNode = this.anchor.getNode();
6892
6911
  if (anchorNode.isEmpty() && $isRootNode(anchorNode.getParent()) && anchorNode.getPreviousSibling() === null) $collapseAtStart(this, anchorNode);
6912
+ $ensureRootHasParagraph();
6893
6913
  }
6894
6914
  }
6895
6915
  /**
@@ -6905,9 +6925,12 @@ var RangeSelection = class RangeSelection {
6905
6925
  this.deleteCharacter(isBackward);
6906
6926
  return;
6907
6927
  }
6908
- if (this.isCollapsed()) this.modify("extend", isBackward, "lineboundary");
6928
+ if (this.isCollapsed()) $extendSelectionForDeletion(this, isBackward, "lineboundary");
6909
6929
  if (this.isCollapsed()) this.deleteCharacter(isBackward);
6910
- else this.removeText();
6930
+ else if ($findMatchingParent(this.anchor.getNode(), INTERNAL_$isBlock) !== $findMatchingParent(this.focus.getNode(), INTERNAL_$isBlock)) {
6931
+ this.focus.set(this.anchor.key, this.anchor.offset, this.anchor.type);
6932
+ this.deleteCharacter(isBackward);
6933
+ } else this.removeText();
6911
6934
  }
6912
6935
  /**
6913
6936
  * Performs one logical word deletion operation on the EditorState based on the current Selection.
@@ -6920,7 +6943,7 @@ var RangeSelection = class RangeSelection {
6920
6943
  const anchor = this.anchor;
6921
6944
  const anchorNode = anchor.getNode();
6922
6945
  if (this.forwardDeletion(anchor, anchorNode, isBackward)) return;
6923
- this.modify("extend", isBackward, "word");
6946
+ $extendSelectionForDeletion(this, isBackward, "word");
6924
6947
  }
6925
6948
  if (this.isCollapsed()) this.deleteCharacter(isBackward);
6926
6949
  else this.removeText();
@@ -6944,6 +6967,124 @@ var RangeSelection = class RangeSelection {
6944
6967
  function $isNodeSelection(x) {
6945
6968
  return x instanceof NodeSelection;
6946
6969
  }
6970
+ /**
6971
+ * Applies a pure bitmask transform to every formattable node in the selection
6972
+ * in a single traversal, splitting the first and last TextNodes as necessary
6973
+ * so that only the selected text is affected. Each node receives exactly one
6974
+ * `setFormat(applyFormat(getFormat()))` (ElementNodes use their textFormat).
6975
+ *
6976
+ * @param selection - the selection whose nodes should be formatted.
6977
+ * @param applyFormat - maps a node's current 32-bit format to its new format.
6978
+ */
6979
+ function $updateTextFormat(selection, applyFormat) {
6980
+ if ($isNodeSelection(selection)) {
6981
+ for (const node of selection.getNodes()) if ($isInlineFormattable(node)) node.setFormat(applyFormat(node.getFormat()));
6982
+ return;
6983
+ }
6984
+ if (selection.isCollapsed()) {
6985
+ selection.setFormat(applyFormat(selection.format));
6986
+ $setCompositionKey(null);
6987
+ return;
6988
+ }
6989
+ const selectedTextNodes = [];
6990
+ for (const node of selection.getNodes()) if ($isTextNode(node)) selectedTextNodes.push(node);
6991
+ else if ($isElementNode(node)) node.setTextFormat(applyFormat(node.getTextFormat()));
6992
+ else if ($isInlineFormattable(node)) node.setFormat(applyFormat(node.getFormat()));
6993
+ const selectedTextNodesLength = selectedTextNodes.length;
6994
+ if (selectedTextNodesLength === 0) {
6995
+ selection.setFormat(applyFormat(selection.format));
6996
+ $setCompositionKey(null);
6997
+ return;
6998
+ }
6999
+ const anchor = selection.anchor;
7000
+ const focus = selection.focus;
7001
+ const isBackward = selection.isBackward();
7002
+ const startPoint = isBackward ? focus : anchor;
7003
+ const endPoint = isBackward ? anchor : focus;
7004
+ let firstIndex = 0;
7005
+ let firstNode = selectedTextNodes[0];
7006
+ let startOffset = startPoint.type === "element" ? 0 : startPoint.offset;
7007
+ if (startPoint.type === "text" && startOffset === firstNode.getTextContentSize()) {
7008
+ firstIndex = 1;
7009
+ firstNode = selectedTextNodes[1];
7010
+ startOffset = 0;
7011
+ }
7012
+ if (firstNode == null) return;
7013
+ const lastIndex = selectedTextNodesLength - 1;
7014
+ let lastNode = selectedTextNodes[lastIndex];
7015
+ const endOffset = endPoint.type === "text" ? endPoint.offset : lastNode.getTextContentSize();
7016
+ if (firstNode.is(lastNode)) {
7017
+ if (startOffset === endOffset) return;
7018
+ const newFormat = applyFormat(firstNode.getFormat());
7019
+ if ($isTokenOrSegmented(firstNode) || startOffset === 0 && endOffset === firstNode.getTextContentSize()) firstNode.setFormat(newFormat);
7020
+ else {
7021
+ const splitNodes = firstNode.splitText(startOffset, endOffset);
7022
+ const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
7023
+ replacement.setFormat(newFormat);
7024
+ if (startPoint.type === "text") startPoint.set(replacement.__key, 0, "text");
7025
+ if (endPoint.type === "text") endPoint.set(replacement.__key, endOffset - startOffset, "text");
7026
+ }
7027
+ selection.format = newFormat;
7028
+ return;
7029
+ }
7030
+ if (startOffset !== 0 && !$isTokenOrSegmented(firstNode)) {
7031
+ [, firstNode] = firstNode.splitText(startOffset);
7032
+ startOffset = 0;
7033
+ }
7034
+ const firstNextFormat = applyFormat(firstNode.getFormat());
7035
+ firstNode.setFormat(firstNextFormat);
7036
+ const lastNextFormat = applyFormat(lastNode.getFormat());
7037
+ if (endOffset > 0) {
7038
+ if (endOffset !== lastNode.getTextContentSize() && !$isTokenOrSegmented(lastNode)) [lastNode] = lastNode.splitText(endOffset);
7039
+ lastNode.setFormat(lastNextFormat);
7040
+ }
7041
+ for (let i = firstIndex + 1; i < lastIndex; i++) {
7042
+ const textNode = selectedTextNodes[i];
7043
+ textNode.setFormat(applyFormat(textNode.getFormat()));
7044
+ }
7045
+ if (startPoint.type === "text") startPoint.set(firstNode.__key, startOffset, "text");
7046
+ if (endPoint.type === "text") endPoint.set(lastNode.__key, endOffset, "text");
7047
+ selection.format = firstNextFormat | lastNextFormat;
7048
+ }
7049
+ /**
7050
+ * Explicitly sets or unsets text formats on the selection. Unlike $formatText
7051
+ * which toggles based on the current selection state, this function sets each
7052
+ * specified format to the exact boolean value provided. Mutually exclusive
7053
+ * formats (subscript/superscript, lowercase/uppercase/capitalize) are
7054
+ * reconciled by {@link toggleTextFormatType}, with later entries winning when
7055
+ * the requested formats conflict.
7056
+ *
7057
+ * @param selection - the selection whose nodes should be formatted.
7058
+ * @param formats - a partial record mapping TextFormatType to boolean.
7059
+ */
7060
+ function $setTextFormat(selection, formats) {
7061
+ const entries = [];
7062
+ for (const [type, value] of Object.entries(formats)) if (typeof value === "boolean") entries.push([type, value]);
7063
+ if (entries.length === 0) return;
7064
+ $updateTextFormat(selection, (format) => {
7065
+ for (const [type, value] of entries) format = toggleTextFormatType(format, type, value ? TEXT_TYPE_TO_FORMAT[type] : 0);
7066
+ return format;
7067
+ });
7068
+ }
7069
+ /**
7070
+ * Applies the provided format to TextNodes and inline formattable nodes
7071
+ * (e.g. DecoratorTextNode) in the selection, splitting or merging TextNodes
7072
+ * as necessary and aligning all formattable nodes to the same target format.
7073
+ *
7074
+ * For RangeSelection the toggle direction is determined by the selection's
7075
+ * computed format (intersection of all text nodes) when no explicit alignment
7076
+ * is given. For NodeSelection each node is toggled independently when no
7077
+ * explicit alignment is given, since there is no TextNode to use as an
7078
+ * alignment reference.
7079
+ *
7080
+ * @param selection - the selection whose nodes should be formatted.
7081
+ * @param formatType - the format type to apply.
7082
+ * @param alignWithFormat - optional 32-bit bitmask to align with.
7083
+ */
7084
+ function $formatText(selection, formatType, alignWithFormat = null) {
7085
+ const effectiveAlign = alignWithFormat === null && $isRangeSelection(selection) ? toggleTextFormatType(selection.format, formatType, null) : alignWithFormat;
7086
+ $updateTextFormat(selection, (format) => toggleTextFormatType(format, formatType, effectiveAlign));
7087
+ }
6947
7088
  function getCharacterOffset(point) {
6948
7089
  const offset = point.offset;
6949
7090
  if (point.type === "text") return offset;
@@ -6980,9 +7121,126 @@ function moveNativeSelection(domSelection, alter, direction, granularity) {
6980
7121
  domSelection.modify(alter, direction, granularity);
6981
7122
  }
6982
7123
  /**
7124
+ * Validate that the selection respects `root` (the nearest root or shadow
7125
+ * root): if any selected node lies outside of it, shrink the selection to the
7126
+ * valid edge in the given direction. The valid node check is a safeguard
7127
+ * against an invalid selection, for which getNodes() returns an empty array.
7128
+ *
7129
+ * @returns true if the selection was shrunk
7130
+ */
7131
+ function $shrinkSelectionToRoot(selection, isBackward, root) {
7132
+ const nodes = selection.getNodes();
7133
+ const validNodes = nodes.filter((node) => $hasAncestor(node, root));
7134
+ if (validNodes.length === 0 || validNodes.length === nodes.length) return false;
7135
+ const edgeNode = isBackward ? validNodes[0] : validNodes[validNodes.length - 1];
7136
+ const edgeElement = $isElementNode(edgeNode) ? edgeNode : edgeNode.getParentOrThrow();
7137
+ if (isBackward) edgeElement.selectStart();
7138
+ else edgeElement.selectEnd();
7139
+ return true;
7140
+ }
7141
+ /**
7142
+ * Extend a collapsed selection by one unit (`character`, `word` or
7143
+ * `lineboundary`) in the deletion direction without ever creating a
7144
+ * non-collapsed DOM selection.
7145
+ *
7146
+ * On Linux/X11, browsers propagate any non-collapsed DOM selection made
7147
+ * during a user gesture to the PRIMARY selection (the middle-click paste
7148
+ * buffer), so a deletion must never pass through a transient non-collapsed
7149
+ * DOM selection or every Backspace/Delete overwrites the user's paste
7150
+ * buffer (https://github.com/facebook/lexical/issues/8766). A collapsed
7151
+ * caret never takes PRIMARY ownership, so the DOM caret is moved with the
7152
+ * native `modify('move')` to measure where the engine places the unit
7153
+ * boundary, and the `[original .. landed]` range is constructed in the
7154
+ * model only. `applyDOMRange` reads just the range's boundary points (it
7155
+ * never touches the DOM selection), giving the same point resolution,
7156
+ * decorator pre/post handling, shadow-root shrink validation and
7157
+ * anchor/focus orientation as a native selection extension would, while
7158
+ * the DOM selection is only ever collapsed.
7159
+ *
7160
+ * When the measurement is not possible — no DOM selection or no
7161
+ * `Selection.modify` (headless environments can polyfill it), or an
7162
+ * unresolvable anchor — the selection is left collapsed, so the deletion
7163
+ * becomes a no-op for that keystroke.
7164
+ */
7165
+ function $extendSelectionForDeletion(selection, isBackward, granularity) {
7166
+ if ($modifySelectionAroundDecoratorsAndBlocks(selection, "extend", isBackward, granularity)) return;
7167
+ const editor = getActiveEditor();
7168
+ const domSelection = getDOMSelection(getWindow(editor));
7169
+ if (!domSelection || typeof domSelection.modify !== "function") return;
7170
+ const blockCursorElement = editor._blockCursorElement;
7171
+ const rootElement = editor._rootElement;
7172
+ const anchor = selection.anchor;
7173
+ const focusNode = selection.focus.getNode();
7174
+ if (rootElement !== null && blockCursorElement !== null && $isElementNode(focusNode) && !focusNode.isInline() && !focusNode.canBeEmpty()) removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
7175
+ const $resolvePointDOM = (point) => {
7176
+ const pointNode = point.getNode();
7177
+ const keyedDOM = editor.getElementByKey(point.key);
7178
+ return keyedDOM !== null && point.type === "text" && $isTextNode(pointNode) ? $getDOMTextNode(pointNode, keyedDOM, editor) : keyedDOM;
7179
+ };
7180
+ const anchorNode = anchor.getNode();
7181
+ const anchorDOM = $resolvePointDOM(anchor);
7182
+ if (anchorDOM === null) return;
7183
+ const anchorOffset = anchor.offset;
7184
+ const wasCollapsed = selection.isCollapsed();
7185
+ const focus = selection.focus;
7186
+ const focusDOM = wasCollapsed ? anchorDOM : $resolvePointDOM(focus);
7187
+ if (focusDOM === null) return;
7188
+ const focusOffset = focus.offset;
7189
+ setDOMSelectionBaseAndExtent(domSelection, focusDOM, focusOffset, focusDOM, focusOffset);
7190
+ moveNativeSelection(domSelection, "move", isBackward ? "backward" : "forward", granularity);
7191
+ if (domSelection.rangeCount === 0) return;
7192
+ const landedRange = getComposedStaticRange(domSelection, rootElement) || domSelection.getRangeAt(0);
7193
+ const landedContainer = landedRange.startContainer;
7194
+ const landedOffset = landedRange.startOffset;
7195
+ if (wasCollapsed && granularity === "character" && anchor.type === "text" && $isTextNode(anchorNode) && anchorNode.isUnmergeable()) {
7196
+ if (anchorOffset === (isBackward ? 0 : anchorNode.getTextContentSize())) {
7197
+ const sibling = $getSiblingCaret(anchorNode, isBackward ? "previous" : "next").getNodeAtCaret();
7198
+ if ($isTextNode(sibling)) {
7199
+ const sibOffset = isBackward ? sibling.getTextContentSize() - 1 : 1;
7200
+ selection.focus.set(sibling.__key, sibOffset, "text");
7201
+ selection.dirty = true;
7202
+ return;
7203
+ }
7204
+ }
7205
+ }
7206
+ if (wasCollapsed && granularity === "character" && anchor.type === "text") {
7207
+ const edgeOffset = isBackward ? 0 : anchorNode.getTextContentSize();
7208
+ const clampedOffset = landedContainer === anchorDOM ? landedOffset : anchorOffset !== edgeOffset ? edgeOffset : -1;
7209
+ if (clampedOffset >= 0) {
7210
+ if (clampedOffset !== anchorOffset) {
7211
+ selection.focus.set(anchor.key, clampedOffset, "text");
7212
+ selection.dirty = true;
7213
+ }
7214
+ return;
7215
+ }
7216
+ }
7217
+ const [startContainer, startOffset, endContainer, endOffset] = isBackward ? [
7218
+ landedContainer,
7219
+ landedOffset,
7220
+ anchorDOM,
7221
+ anchorOffset
7222
+ ] : [
7223
+ anchorDOM,
7224
+ anchorOffset,
7225
+ landedContainer,
7226
+ landedOffset
7227
+ ];
7228
+ const root = $isRootNode(anchorNode) ? anchorNode : $getNearestRootOrShadowRoot(anchorNode);
7229
+ selection.applyDOMRange({
7230
+ collapsed: false,
7231
+ endContainer,
7232
+ endOffset,
7233
+ startContainer,
7234
+ startOffset
7235
+ });
7236
+ selection.dirty = true;
7237
+ if (!$shrinkSelectionToRoot(selection, isBackward, root) && isBackward) $swapPoints(selection);
7238
+ if (granularity === "lineboundary") $modifySelectionAroundDecoratorsAndBlocks(selection, "extend", isBackward, granularity, "decorators");
7239
+ }
7240
+ /**
6983
7241
  * Called by `RangeSelection.deleteCharacter` to determine if
6984
- * `this.modify('extend', isBackward, 'character')` extended the selection
6985
- * further than a user would expect for that operation.
7242
+ * `$extendSelectionForDeletion` extended the selection further
7243
+ * than a user would expect for that operation.
6986
7244
  *
6987
7245
  * A short(?) JavaScript string vs. Unicode primer:
6988
7246
  *
@@ -7016,7 +7274,7 @@ function moveNativeSelection(domSelection, alter, direction, granularity) {
7016
7274
  * essentially be arbitrarily long, as there are many ways to combine
7017
7275
  * them.
7018
7276
  *
7019
- * The `this.modify(…)` call has already extended our selection by one
7277
+ * The native caret measurement has already extended our selection by one
7020
7278
  * *grapheme* in the direction we want to delete. Sounds great, it's done
7021
7279
  * a lot of awfully tricky work for us because this functionality has only
7022
7280
  * recently become available in JavaScript via `Intl.Segmenter`. The
@@ -7197,7 +7455,7 @@ function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
7197
7455
  const parent = node.getParent();
7198
7456
  if (!isBackward) {
7199
7457
  if ($isElementNode(prevSibling) && !isCollapsed && prevSibling.isInline()) point.set(prevSibling.__key, prevSibling.getChildrenSize(), "element");
7200
- else if ($isTextNode(prevSibling)) point.set(prevSibling.__key, prevSibling.getTextContent().length, "text");
7458
+ else if ($isTextNode(prevSibling) && !node.isUnmergeable()) point.set(prevSibling.__key, prevSibling.getTextContent().length, "text");
7201
7459
  } else if ((isCollapsed || !isBackward) && prevSibling === null && $isElementNode(parent) && parent.isInline()) {
7202
7460
  const parentSibling = parent.getPreviousSibling();
7203
7461
  if ($isTextNode(parentSibling)) point.set(parentSibling.__key, parentSibling.getTextContent().length, "text");
@@ -7206,7 +7464,7 @@ function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
7206
7464
  const nextSibling = node.getNextSibling();
7207
7465
  const parent = node.getParent();
7208
7466
  if (isBackward && $isElementNode(nextSibling) && nextSibling.isInline()) point.set(nextSibling.__key, 0, "element");
7209
- else if ((isCollapsed || isBackward) && nextSibling === null && $isElementNode(parent) && parent.isInline() && !parent.canInsertTextAfter()) {
7467
+ else if ((isCollapsed || isBackward) && nextSibling === null && $isElementNode(parent) && parent.isInline() && !parent.canInsertTextAfter() && parent.getTextContentSize() > 1) {
7210
7468
  const parentSibling = parent.getNextSibling();
7211
7469
  if ($isTextNode(parentSibling)) point.set(parentSibling.__key, 0, "text");
7212
7470
  }
@@ -7565,7 +7823,7 @@ function $updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
7565
7823
  } else if ($isRangeSelection(prevSelection) && prevSelection.anchor.type === "text") anchorFormatOrStyleChanged = true;
7566
7824
  if (focus.type === "text") nextFocusNode = $isTextNode(focusNode) ? $getDOMTextNode(focusNode, focusDOM, editor) : null;
7567
7825
  if (nextAnchorNode === null || nextFocusNode === null) return;
7568
- if (isCollapsed && (prevSelection === null || anchorFormatOrStyleChanged || $isRangeSelection(prevSelection) && (prevSelection.format !== nextFormat || prevSelection.style !== nextStyle))) markCollapsedSelectionFormat(nextFormat, nextStyle, nextAnchorOffset, anchor.key, performance.now());
7826
+ if (isCollapsed && (prevSelection === null || anchorFormatOrStyleChanged || $isRangeSelection(prevSelection) && (prevSelection.format !== nextFormat || prevSelection.style !== nextStyle))) markCollapsedSelectionFormat(editor, nextFormat, nextStyle, nextAnchorOffset, anchor.key, performance.now());
7569
7827
  if (!(domSelection.type === "Range" && isCollapsed) && currentPoints.anchorOffset === nextAnchorOffset && currentPoints.focusOffset === nextFocusOffset && currentPoints.anchorNode === nextAnchorNode && currentPoints.focusNode === nextFocusNode) {
7570
7828
  if (activeElement === null || !rootElement.contains(activeElement)) {
7571
7829
  const focusEditor = activeElement !== null ? getNearestEditorFromDOMNode(activeElement) : null;
@@ -7594,7 +7852,7 @@ function $updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
7594
7852
  scrollIntoViewIfNeeded(editor, selectionRect, rootElement);
7595
7853
  }
7596
7854
  }
7597
- markSelectionChangeFromDOMUpdate();
7855
+ markSelectionChangeFromDOMUpdate(editor);
7598
7856
  }
7599
7857
  function $insertNodes(nodes) {
7600
7858
  let selection = $getSelection() || $getPreviousSelection();
@@ -8072,7 +8330,7 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8072
8330
  const rootElement = editor._rootElement;
8073
8331
  const shouldSkipDOM = editor._headless || rootElement === null;
8074
8332
  if (pendingEditorState === null) {
8075
- if (editor._deferred.length > 0) triggerDeferredUpdateCallbacks(editor, editor._deferred);
8333
+ if (!editor._updating && editor._deferred.length > 0) triggerDeferredUpdateCallbacks(editor, editor._deferred);
8076
8334
  return;
8077
8335
  }
8078
8336
  const currentEditorState = editor._editorState;
@@ -8130,7 +8388,6 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8130
8388
  const dirtyElements = editor._dirtyElements;
8131
8389
  const normalizedNodes = editor._normalizedNodes;
8132
8390
  const tags = editor._updateTags;
8133
- const deferred = editor._deferred;
8134
8391
  if (needsUpdate) {
8135
8392
  editor._dirtyType = NO_DIRTY_NODES;
8136
8393
  editor._cloneNotNeeded.clear();
@@ -8159,7 +8416,7 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8159
8416
  }
8160
8417
  }
8161
8418
  if (mutatedNodes !== null) triggerMutationListeners(editor, mutatedNodes, tags, dirtyLeaves, currentEditorState);
8162
- if (!$isRangeSelection(pendingSelection) && pendingSelection !== null && (currentSelection === null || !currentSelection.is(pendingSelection))) editor.dispatchCommand(SELECTION_CHANGE_COMMAND, void 0);
8419
+ if (!$isRangeSelection(pendingSelection) && pendingSelection !== null && (currentSelection === null || !currentSelection.is(pendingSelection))) editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
8163
8420
  /**
8164
8421
  * Capture pendingDecorators after garbage collecting detached decorators
8165
8422
  */
@@ -8179,7 +8436,10 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8179
8436
  prevEditorState: recoveryEditorState || currentEditorState,
8180
8437
  tags
8181
8438
  });
8182
- triggerDeferredUpdateCallbacks(editor, deferred);
8439
+ if (!previouslyUpdating) {
8440
+ const deferred = editor._deferred;
8441
+ triggerDeferredUpdateCallbacks(editor, deferred);
8442
+ }
8183
8443
  $triggerEnqueuedUpdates(editor);
8184
8444
  }
8185
8445
  function triggerTextContentListeners(editor, currentEditorState, pendingEditorState) {
@@ -8407,7 +8667,10 @@ function $beginUpdate(editor, updateFn, options) {
8407
8667
  * within an update
8408
8668
  */
8409
8669
  function updateEditorSync(editor, updateFn, options) {
8410
- if (activeEditor === editor && options === void 0) updateFn();
8670
+ if (activeEditor === editor && options === void 0) if (isCurrentlyReadOnlyMode()) {
8671
+ console.warn("updateEditorSync: an editor update (e.g. a command listener that mutates the editor) ran while a read-only context was on the stack. This most commonly happens when a command is dispatched from inside editor.read(). The update has been deferred to a fresh writable update so it still applies, but dispatching mutations from a read-only context is an anti-pattern — dispatch after editor.read() returns, or via queueMicrotask.");
8672
+ $beginUpdate(editor, updateFn, options);
8673
+ } else updateFn();
8411
8674
  else $beginUpdate(editor, updateFn, options);
8412
8675
  }
8413
8676
  function updateEditor(editor, updateFn, options) {
@@ -8500,7 +8763,8 @@ var ElementNode = class extends LexicalNode {
8500
8763
  return this.getLatest().__format;
8501
8764
  }
8502
8765
  getFormatType() {
8503
- return ELEMENT_FORMAT_TO_TYPE[this.getFormat()] || "";
8766
+ const format = this.getFormat();
8767
+ return ELEMENT_FORMAT_TO_TYPE[format] || "";
8504
8768
  }
8505
8769
  getStyle() {
8506
8770
  return this.getLatest().__style;
@@ -9086,14 +9350,11 @@ function $isDecoratorNode(node) {
9086
9350
  return node instanceof DecoratorNode;
9087
9351
  }
9088
9352
  /** @noInheritDoc */
9089
- var RootNode = class RootNode extends ElementNode {
9353
+ var RootNode = class extends ElementNode {
9090
9354
  /** @internal */
9091
9355
  __cachedText;
9092
- static getType() {
9093
- return "root";
9094
- }
9095
- static clone() {
9096
- return new RootNode();
9356
+ $config() {
9357
+ return this.config("root", { extends: ElementNode });
9097
9358
  }
9098
9359
  constructor() {
9099
9360
  super("root");
@@ -9230,11 +9491,11 @@ var EditorState = class EditorState {
9230
9491
  */
9231
9492
  /** @internal */
9232
9493
  var ArtificialNode__DO_NOT_USE = class extends ElementNode {
9233
- static getType() {
9234
- return "artificial";
9494
+ $config() {
9495
+ return this.config("artificial", { extends: ElementNode });
9235
9496
  }
9236
9497
  createDOM(config) {
9237
- return document.createElement("div");
9498
+ return $getDocument().createElement("div");
9238
9499
  }
9239
9500
  };
9240
9501
  /**
@@ -9245,22 +9506,22 @@ var ArtificialNode__DO_NOT_USE = class extends ElementNode {
9245
9506
  *
9246
9507
  */
9247
9508
  /** @noInheritDoc */
9248
- var LineBreakNode = class LineBreakNode extends LexicalNode {
9509
+ var LineBreakNode = class extends LexicalNode {
9249
9510
  /** @internal */
9250
- static getType() {
9251
- return "linebreak";
9252
- }
9253
- static clone(node) {
9254
- return new LineBreakNode(node.__key);
9255
- }
9256
- constructor(key) {
9257
- super(key);
9511
+ $config() {
9512
+ return this.config("linebreak", { importDOM: { br: (node) => {
9513
+ if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) return null;
9514
+ return {
9515
+ conversion: $convertLineBreakElement,
9516
+ priority: 0
9517
+ };
9518
+ } } });
9258
9519
  }
9259
9520
  getTextContent() {
9260
9521
  return "\n";
9261
9522
  }
9262
9523
  createDOM() {
9263
- return document.createElement("br");
9524
+ return $getDocument().createElement("br");
9264
9525
  }
9265
9526
  updateDOM() {
9266
9527
  return false;
@@ -9268,18 +9529,6 @@ var LineBreakNode = class LineBreakNode extends LexicalNode {
9268
9529
  isInline() {
9269
9530
  return true;
9270
9531
  }
9271
- static importDOM() {
9272
- return { br: (node) => {
9273
- if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) return null;
9274
- return {
9275
- conversion: $convertLineBreakElement,
9276
- priority: 0
9277
- };
9278
- } };
9279
- }
9280
- static importJSON(serializedLineBreakNode) {
9281
- return $createLineBreakNode().updateFromJSON(serializedLineBreakNode);
9282
- }
9283
9532
  };
9284
9533
  function $convertLineBreakElement(node) {
9285
9534
  return { node: $createLineBreakNode() };
@@ -9338,16 +9587,19 @@ function isWhitespaceDomTextNode(node) {
9338
9587
  *
9339
9588
  */
9340
9589
  /** @noInheritDoc */
9341
- var ParagraphNode = class ParagraphNode extends ElementNode {
9590
+ var ParagraphNode = class extends ElementNode {
9342
9591
  /** @internal */
9343
- static getType() {
9344
- return "paragraph";
9345
- }
9346
- static clone(node) {
9347
- return new ParagraphNode(node.__key);
9592
+ $config() {
9593
+ return this.config("paragraph", {
9594
+ extends: ElementNode,
9595
+ importDOM: { p: () => ({
9596
+ conversion: $convertParagraphElement,
9597
+ priority: 0
9598
+ }) }
9599
+ });
9348
9600
  }
9349
9601
  createDOM(config) {
9350
- const dom = document.createElement("p");
9602
+ const dom = $getDocument().createElement("p");
9351
9603
  const classNames = getCachedClassNameArray(config.theme, "paragraph");
9352
9604
  if (classNames !== void 0) dom.classList.add(...classNames);
9353
9605
  return dom;
@@ -9355,24 +9607,15 @@ var ParagraphNode = class ParagraphNode extends ElementNode {
9355
9607
  updateDOM(prevNode, dom, config) {
9356
9608
  return false;
9357
9609
  }
9358
- static importDOM() {
9359
- return { p: (node) => ({
9360
- conversion: $convertParagraphElement,
9361
- priority: 0
9362
- }) };
9363
- }
9364
9610
  exportDOM(editor) {
9365
9611
  const { element } = super.exportDOM(editor);
9366
9612
  if (isHTMLElement(element)) {
9367
- if (this.isEmpty()) element.append(document.createElement("br"));
9613
+ if (this.isEmpty()) element.append($getDocument().createElement("br"));
9368
9614
  const formatType = this.getFormatType();
9369
9615
  if (formatType) element.style.textAlign = formatType;
9370
9616
  }
9371
9617
  return { element };
9372
9618
  }
9373
- static importJSON(serializedNode) {
9374
- return $createParagraphNode().updateFromJSON(serializedNode);
9375
- }
9376
9619
  exportJSON() {
9377
9620
  const json = super.exportJSON();
9378
9621
  if (json.textFormat === void 0 || json.textStyle === void 0) {
@@ -9448,6 +9691,33 @@ function $isParagraphNode(node) {
9448
9691
  * - `'latest'` reads the latest committed state without flushing pending
9449
9692
  * updates, equivalent to `editor.getEditorState().read(callbackFn, {editor})`.
9450
9693
  */
9694
+ /** @internal */
9695
+ /** @internal */
9696
+ /** @internal */
9697
+ function createInputState() {
9698
+ return {
9699
+ collapsedSelectionFormat: {
9700
+ format: 0,
9701
+ key: "root",
9702
+ offset: 0,
9703
+ style: "",
9704
+ timeStamp: 0
9705
+ },
9706
+ compositionEndData: "",
9707
+ compositionPhase: "idle",
9708
+ hadOrphanedCompositionEvents: false,
9709
+ handledSelectionCommandTimeoutId: null,
9710
+ isInsertLineBreak: false,
9711
+ isInsertTextAfterHandledSelectionCommand: false,
9712
+ isSelectionChangeFromDOMUpdate: false,
9713
+ isSelectionChangeFromMouseDown: false,
9714
+ lastBeforeInputInsertTextTimeStamp: 0,
9715
+ lastKeyCode: null,
9716
+ lastKeyDownTimeStamp: 0,
9717
+ postDeleteSelectionToRestore: null,
9718
+ unprocessedBeforeInputData: null
9719
+ };
9720
+ }
9451
9721
  /**
9452
9722
  * Configuration entry passed in {@link CreateEditorArgs.nodes} to substitute
9453
9723
  * a core node class with a custom subclass. The replacement class itself
@@ -9585,6 +9855,8 @@ function resetEditor(editor, prevRootElement, nextRootElement, pendingEditorStat
9585
9855
  editor._cascadeCount = 0;
9586
9856
  }
9587
9857
  editor._blockCursorElement = null;
9858
+ if (editor._inputState.handledSelectionCommandTimeoutId !== null) clearTimeout(editor._inputState.handledSelectionCommandTimeoutId);
9859
+ editor._inputState = createInputState();
9588
9860
  const observer = editor._observer;
9589
9861
  if (observer !== null) {
9590
9862
  observer.disconnect();
@@ -9838,6 +10110,8 @@ var LexicalEditor = class {
9838
10110
  */
9839
10111
  _slotsUsed;
9840
10112
  /** @internal */
10113
+ _inputState;
10114
+ /** @internal */
9841
10115
  _createEditorArgs;
9842
10116
  /** @internal */
9843
10117
  constructor(editorState, parentEditor, nodes, config, onError, onWarn, htmlConversions, editable, createEditorArgs) {
@@ -9881,6 +10155,7 @@ var LexicalEditor = class {
9881
10155
  this._window = null;
9882
10156
  this._blockCursorElement = null;
9883
10157
  this._slotsUsed = false;
10158
+ this._inputState = createInputState();
9884
10159
  }
9885
10160
  /**
9886
10161
  *
@@ -9901,7 +10176,7 @@ var LexicalEditor = class {
9901
10176
  return registerListener(this._listeners.update, listener);
9902
10177
  }
9903
10178
  /**
9904
- * Registers a listener for for when the editor changes between editable and non-editable states.
10179
+ * Registers a listener for when the editor changes between editable and non-editable states.
9905
10180
  * Will trigger the provided callback each time the editor transitions between these states until the
9906
10181
  * teardown function is called.
9907
10182
  *
@@ -10112,8 +10387,8 @@ var LexicalEditor = class {
10112
10387
  * @param type - the type of command listeners to trigger.
10113
10388
  * @param payload - the data to pass as an argument to the command listeners.
10114
10389
  */
10115
- dispatchCommand(type, payload) {
10116
- return dispatchCommand(this, type, payload);
10390
+ dispatchCommand(type, ...args) {
10391
+ return dispatchCommand(this, type, ...args);
10117
10392
  }
10118
10393
  /**
10119
10394
  * Gets a map of all decorators in the editor.
@@ -10348,6 +10623,7 @@ function getPendingNodeToClone() {
10348
10623
  pendingNodeToClone = null;
10349
10624
  return node;
10350
10625
  }
10626
+ const INTERNAL_SKIP_AFTER_CLONE_FROM = Symbol("INTERNAL_SKIP_AFTER_CLONE_FROM");
10351
10627
  let keyCounter = 1;
10352
10628
  function resetRandomKey() {
10353
10629
  keyCounter = 1;
@@ -10736,6 +11012,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
10736
11012
  let node = textNode;
10737
11013
  if (node.isAttached() && (compositionEnd || !node.isDirty())) {
10738
11014
  const isComposing = node.isComposing();
11015
+ if (node.isToken() && isComposing) return;
10739
11016
  let normalizedTextContent = textContent;
10740
11017
  if (isComposing || compositionEnd) {
10741
11018
  if (textContent.endsWith(COMPOSITION_SUFFIX)) normalizedTextContent = textContent.slice(0, -COMPOSITION_SUFFIX.length);
@@ -10770,7 +11047,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
10770
11047
  const prevTextContentSize = node.getTextContentSize();
10771
11048
  const compositionKey = $getCompositionKey();
10772
11049
  const nodeKey = node.getKey();
10773
- if (node.isToken() || compositionKey !== null && nodeKey === compositionKey && !isComposing || $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)) {
11050
+ if (node.isToken() && !isComposing || compositionKey !== null && nodeKey === compositionKey && !isComposing || $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)) {
10774
11051
  node.markDirty();
10775
11052
  return;
10776
11053
  }
@@ -11099,8 +11376,8 @@ function isFirefoxClipboardEvents(editor) {
11099
11376
  const inputType = event && event.inputType;
11100
11377
  return inputType === "insertFromPaste" || inputType === "insertFromPasteAsQuotation";
11101
11378
  }
11102
- function dispatchCommand(editor, command, payload) {
11103
- return triggerCommandListeners(editor, command, payload, editor);
11379
+ function dispatchCommand(editor, command, ...args) {
11380
+ return triggerCommandListeners(editor, command, args[0], editor);
11104
11381
  }
11105
11382
  function getElementByKeyOrThrow(editor, key) {
11106
11383
  const element = editor._keyToDOMMap.get(key);
@@ -11120,6 +11397,8 @@ function scrollIntoViewIfNeeded(editor, selectionRect, rootElement) {
11120
11397
  const doc = getDOMOwnerDocument(rootElement);
11121
11398
  const defaultView = getDefaultView(doc);
11122
11399
  if (doc === null || defaultView === null) return;
11400
+ const rootRect = rootElement.getBoundingClientRect();
11401
+ if (selectionRect.bottom < rootRect.top) return;
11123
11402
  let { top: currentTop, bottom: currentBottom } = selectionRect;
11124
11403
  let targetTop = 0;
11125
11404
  let targetBottom = 0;
@@ -11142,7 +11421,7 @@ function scrollIntoViewIfNeeded(editor, selectionRect, rootElement) {
11142
11421
  if (isFinite(scrollPaddingTop)) targetTop += scrollPaddingTop;
11143
11422
  if (isFinite(scrollPaddingBottom)) targetBottom -= scrollPaddingBottom;
11144
11423
  } else {
11145
- const targetRect = element.getBoundingClientRect();
11424
+ const targetRect = element === rootElement ? rootRect : element.getBoundingClientRect();
11146
11425
  targetTop = targetRect.top;
11147
11426
  targetBottom = targetRect.bottom;
11148
11427
  }
@@ -11219,8 +11498,11 @@ function $getNearestRootOrShadowRoot(node) {
11219
11498
  }
11220
11499
  return current;
11221
11500
  }
11501
+ function $isShadowRootNode(node) {
11502
+ return $isElementNode(node) && node.isShadowRoot();
11503
+ }
11222
11504
  function $isRootOrShadowRoot(node) {
11223
- return $isRootNode(node) || $isElementNode(node) && node.isShadowRoot();
11505
+ return $isRootNode(node) || $isShadowRootNode(node);
11224
11506
  }
11225
11507
  /**
11226
11508
  * Returns a shallow clone of node with a new key. All properties of the node
@@ -11234,7 +11516,7 @@ function $isRootOrShadowRoot(node) {
11234
11516
  * @returns The copy of the node.
11235
11517
  */
11236
11518
  function $copyNode(node, skipReset = false) {
11237
- const copy = node.constructor.clone(node);
11519
+ const copy = node.constructor.clone(node, INTERNAL_SKIP_AFTER_CLONE_FROM);
11238
11520
  $setNodeKey(copy, null);
11239
11521
  copy.afterCloneFrom(node);
11240
11522
  if (!skipReset) copy.resetOnCopyNodeFrom(node);
@@ -11275,9 +11557,9 @@ function $getNodeByKeyOrThrow(key) {
11275
11557
  if (node === null) formatDevErrorMessage(`Expected node with key ${key} to exist but it's not in the nodeMap.`);
11276
11558
  return node;
11277
11559
  }
11278
- function createBlockCursorElement(editorConfig) {
11560
+ function $createBlockCursorElement(editorConfig) {
11279
11561
  const theme = editorConfig.theme;
11280
- const element = document.createElement("div");
11562
+ const element = $getDocument().createElement("div");
11281
11563
  element.contentEditable = "false";
11282
11564
  element.setAttribute("data-lexical-cursor", "true");
11283
11565
  let blockCursorTheme = theme.blockCursor;
@@ -11287,8 +11569,24 @@ function createBlockCursorElement(editorConfig) {
11287
11569
  }
11288
11570
  return element;
11289
11571
  }
11290
- function needsBlockCursor(node) {
11291
- return ($isDecoratorNode(node) || $isElementNode(node) && !node.canBeEmpty()) && !node.isInline();
11572
+ /**
11573
+ * Returns true if the given node needs a block cursor given an adjacent selection,
11574
+ * the node must be non-inline and one of:
11575
+ * - DecoratorNode
11576
+ * - ShadowRootNode with a parent that is not also a ShadowRootNode
11577
+ * - An ElementNode that can't be empty
11578
+ */
11579
+ function $needsBlockCursorBeside(node) {
11580
+ if (!node || node.isInline()) return false;
11581
+ if ($isDecoratorNode(node)) return true;
11582
+ if ($isElementNode(node)) {
11583
+ if (node.isShadowRoot()) {
11584
+ const parent = node.getParent();
11585
+ return !($isElementNode(parent) && parent.isShadowRoot());
11586
+ }
11587
+ return !node.canBeEmpty();
11588
+ }
11589
+ return false;
11292
11590
  }
11293
11591
  function removeDOMBlockCursorElement(blockCursorElement, editor, rootElement) {
11294
11592
  rootElement.style.removeProperty("caret-color");
@@ -11306,12 +11604,12 @@ function $updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
11306
11604
  let isBlockCursor = false;
11307
11605
  let insertBeforeElement = null;
11308
11606
  if (offset === elementNodeSize) {
11309
- if (needsBlockCursor(elementNode.getChildAtIndex(offset - 1))) isBlockCursor = true;
11607
+ if ($needsBlockCursorBeside(elementNode.getChildAtIndex(offset - 1))) isBlockCursor = true;
11310
11608
  } else {
11311
11609
  const child = elementNode.getChildAtIndex(offset);
11312
- if (child !== null && needsBlockCursor(child)) {
11610
+ if (child !== null && $needsBlockCursorBeside(child)) {
11313
11611
  const sibling = child.getPreviousSibling();
11314
- if (sibling === null || needsBlockCursor(sibling)) {
11612
+ if (sibling === null || $needsBlockCursorBeside(sibling)) {
11315
11613
  isBlockCursor = true;
11316
11614
  insertBeforeElement = editor.getElementByKey(child.__key);
11317
11615
  }
@@ -11319,7 +11617,7 @@ function $updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
11319
11617
  }
11320
11618
  if (isBlockCursor) {
11321
11619
  const elementDOM = $getDOMSlot(elementNode, editor.getElementByKey(elementNode.__key), editor).element;
11322
- if (blockCursorElement === null) editor._blockCursorElement = blockCursorElement = createBlockCursorElement(editor._config);
11620
+ if (blockCursorElement === null) editor._blockCursorElement = blockCursorElement = $createBlockCursorElement(editor._config);
11323
11621
  rootElement.style.caretColor = "transparent";
11324
11622
  if (insertBeforeElement === null) elementDOM.appendChild(blockCursorElement);
11325
11623
  else elementDOM.insertBefore(blockCursorElement, insertBeforeElement);
@@ -11414,6 +11712,28 @@ function getRootOwnerDocument(rootElement) {
11414
11712
  return rootElement !== null ? rootElement.ownerDocument : document;
11415
11713
  }
11416
11714
  /**
11715
+ * Returns the {@link Document} that owns the active editor's root element.
11716
+ * Falls back to `globalThis.document` when there is no active editor (e.g.
11717
+ * a node method such as `createDOM` / `exportDOM` is invoked headlessly,
11718
+ * outside of `editor.update()` / `editor.read()`), or when the active
11719
+ * editor has no root element (e.g. headless mode with
11720
+ * {@link @lexical/headless!withDOM | withDOM}).
11721
+ *
11722
+ * Use this inside `createDOM`, `updateDOM`, and `exportDOM` instead of the
11723
+ * bare `document` global so the node works correctly when the editor lives
11724
+ * inside a Shadow DOM or a cross-origin `<iframe>`.
11725
+ *
11726
+ * Unlike most `$`-prefixed helpers, this does NOT require an ambient active
11727
+ * editor: it must remain callable from `createDOM` / `exportDOM`, which are
11728
+ * public methods that consumers may legitimately call while serializing
11729
+ * nodes headlessly. Throwing here would silently break every node whose DOM
11730
+ * methods were migrated off the bare `document` global.
11731
+ */
11732
+ function $getDocument() {
11733
+ const editor = internalGetActiveEditor();
11734
+ return getRootOwnerDocument(editor !== null ? editor._rootElement : null);
11735
+ }
11736
+ /**
11417
11737
  * A subset of `Selection` covering the four boundary-point fields Lexical
11418
11738
  * reads plus `direction`. Designed so a `Selection` instance can be returned
11419
11739
  * where a `DOMSelectionBoundaryPoints` is expected (see {@link getDOMSelectionPoints}).
@@ -11934,7 +12254,7 @@ function computeTypeToNodeMap(editorState) {
11934
12254
  */
11935
12255
  function $cloneWithProperties(latestNode) {
11936
12256
  const constructor = latestNode.constructor;
11937
- const mutableNode = constructor.clone(latestNode);
12257
+ const mutableNode = constructor.clone(latestNode, INTERNAL_SKIP_AFTER_CLONE_FROM);
11938
12258
  mutableNode.afterCloneFrom(latestNode);
11939
12259
  if (!(mutableNode.__key === latestNode.__key)) formatDevErrorMessage(`$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`);
11940
12260
  if (!(mutableNode.__parent === latestNode.__parent && mutableNode.__next === latestNode.__next && mutableNode.__prev === latestNode.__prev)) formatDevErrorMessage(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') overrode afterCloneFrom but did not call super.afterCloneFrom(prevNode)`);
@@ -12109,13 +12429,16 @@ function isAbstractNodeClass(klass) {
12109
12429
  return klass === DecoratorNode || klass === ElementNode || klass === LexicalNode;
12110
12430
  }
12111
12431
  const STATIC_NODE_CONFIG_CACHE = /* @__PURE__ */ new WeakMap();
12432
+ const SYNTHESIZED_GET_TYPE = Symbol("lexical.synthesizedGetType");
12433
+ const IS_UNOPTIMIZED_DEV_BUILD = TabNode.length === 0 && TextNode.length === 0 && TextNode.name === "TextNode";
12112
12434
  /** @internal */
12113
12435
  function getStaticNodeConfig(klass) {
12114
12436
  const cache = STATIC_NODE_CONFIG_CACHE.get(klass);
12115
12437
  if (cache) return cache;
12116
12438
  const nodeConfigRecord = klass.prototype != null && PROTOTYPE_CONFIG_METHOD in klass.prototype ? klass.prototype[PROTOTYPE_CONFIG_METHOD]() : void 0;
12117
12439
  const isAbstract = isAbstractNodeClass(klass);
12118
- const nodeType = !isAbstract && hasOwnStaticMethod(klass, "getType") ? klass.getType() : void 0;
12440
+ const ownGetType = !isAbstract && hasOwnStaticMethod(klass, "getType") ? klass.getType : void 0;
12441
+ const nodeType = ownGetType && !(SYNTHESIZED_GET_TYPE in ownGetType) ? ownGetType.call(klass) : void 0;
12119
12442
  let ownNodeConfig;
12120
12443
  let ownNodeType = nodeType;
12121
12444
  if (nodeConfigRecord) if (nodeType) ownNodeConfig = nodeConfigRecord[nodeType];
@@ -12133,18 +12456,28 @@ function getStaticNodeConfig(klass) {
12133
12456
  }
12134
12457
  }
12135
12458
  if (!isAbstract && ownNodeType) {
12136
- if (!hasOwnStaticMethod(klass, "getType")) klass.getType = () => ownNodeType;
12459
+ if (!hasOwnStaticMethod(klass, "getType")) {
12460
+ const synthesizedForKlass = klass;
12461
+ const synthesizedGetType = function() {
12462
+ if (this !== synthesizedForKlass) return LexicalNode.getType.call(this);
12463
+ return ownNodeType;
12464
+ };
12465
+ synthesizedGetType[SYNTHESIZED_GET_TYPE] = true;
12466
+ klass.getType = synthesizedGetType;
12467
+ }
12137
12468
  if (!hasOwnStaticMethod(klass, "clone")) {
12138
- if (TextNode.length === 0) {
12469
+ if (IS_UNOPTIMIZED_DEV_BUILD) {
12139
12470
  if (!(klass.length === 0)) formatDevErrorMessage(`${klass.name} (type ${ownNodeType}) must implement a static clone method since its constructor has ${String(klass.length)} required arguments (expecting 0). Use an explicit default in the first argument of your constructor(prop: T=X, nodeKey?: NodeKey).`);
12140
12471
  }
12141
- klass.clone = (prevNode) => {
12472
+ klass.clone = (prevNode, internalSkipAfterCloneFrom) => {
12142
12473
  setPendingNodeToClone(prevNode);
12143
- return new klass();
12474
+ const node = new klass();
12475
+ if (internalSkipAfterCloneFrom !== INTERNAL_SKIP_AFTER_CLONE_FROM) node.afterCloneFrom(prevNode);
12476
+ return node;
12144
12477
  };
12145
12478
  }
12146
12479
  if (!hasOwnStaticMethod(klass, "importJSON")) {
12147
- if (TextNode.length === 0) {
12480
+ if (IS_UNOPTIMIZED_DEV_BUILD) {
12148
12481
  if (!(klass.length === 0)) formatDevErrorMessage(`${klass.name} (type ${ownNodeType}) must implement a static importJSON method since its constructor has ${String(klass.length)} required arguments (expecting 0). Use an explicit default in the first argument of your constructor(prop: T=X, nodeKey?: NodeKey).`);
12149
12482
  }
12150
12483
  klass.importJSON = ownNodeConfig && ownNodeConfig.$importJSON || ((serializedNode) => new klass().updateFromJSON(serializedNode));
@@ -13452,7 +13785,13 @@ function $removeTextFromCaretRange(initialRange, sliceMode = "removeEmptySlices"
13452
13785
  const { origin } = caret;
13453
13786
  if (!$isElementNode(origin) || seenStart.has(origin.getKey())) removedNodes.push(origin);
13454
13787
  }
13455
- for (const node of removedNodes) node.remove();
13788
+ const removedParents = /* @__PURE__ */ new Set();
13789
+ for (const node of removedNodes) {
13790
+ const parent = node.getParent();
13791
+ if (parent !== null && !seenStart.has(parent.getKey())) removedParents.add(parent);
13792
+ $removeFromParent(node);
13793
+ }
13794
+ for (const parent of removedParents) if (!parent.canBeEmpty() && !$isRootOrShadowRoot(parent) && parent.isEmpty() && parent.isAttached()) parent.remove();
13456
13795
  for (const slice of range.getTextSlices()) {
13457
13796
  if (!slice) continue;
13458
13797
  const { origin } = slice.caret;
@@ -13495,6 +13834,21 @@ function $removeTextFromCaretRange(initialRange, sliceMode = "removeEmptySlices"
13495
13834
  parent = parent.getParent();
13496
13835
  element.remove(true);
13497
13836
  }
13837
+ } else if (focusCandidate) {
13838
+ const focusBlock = $getBlockFromCaret(focusCandidate);
13839
+ const focusBlockParent = focusBlock && focusBlock.getParent();
13840
+ const topmostShadowRoot = focusBlock && focusBlock.getParents().findLast($isShadowRootNode);
13841
+ if (focusBlock && focusBlockParent && !$isRootNode(focusBlockParent) && focusBlock.isEmpty() && seenStart.has(focusBlock.getKey()) && $getSlotNames(focusBlock).length === 0 && (!topmostShadowRoot || seenStart.has(topmostShadowRoot.getKey()))) {
13842
+ focusBlock.remove(true);
13843
+ let parent = focusBlockParent;
13844
+ while (parent && !$isRootNode(parent) && parent.isEmpty()) {
13845
+ const grandparent = parent.getParent();
13846
+ if (grandparent && $isRootNode(grandparent) && grandparent.getChildrenSize() <= 1) break;
13847
+ const element = parent;
13848
+ parent = grandparent;
13849
+ element.remove(true);
13850
+ }
13851
+ }
13498
13852
  }
13499
13853
  const bestCandidate = [
13500
13854
  anchorCandidate,
@@ -13505,6 +13859,16 @@ function $removeTextFromCaretRange(initialRange, sliceMode = "removeEmptySlices"
13505
13859
  if (bestCandidate) return $getCollapsedCaretRange($getCaretInDirection($normalizeCaret(bestCandidate), initialRange.direction));
13506
13860
  formatDevErrorMessage(`$removeTextFromCaretRange: selection was lost, could not find a new anchor given candidates with keys: ${JSON.stringify(anchorCandidates.map((n) => n.origin.__key))}`);
13507
13861
  }
13862
+ function $getBlockFromCaret(caret) {
13863
+ if ($isChildCaret(caret)) {
13864
+ const origin = caret.origin;
13865
+ if (INTERNAL_$isBlock(origin)) return origin;
13866
+ } else {
13867
+ const parent = caret.getParentAtCaret();
13868
+ if (parent && INTERNAL_$isBlock(parent)) return parent;
13869
+ }
13870
+ return null;
13871
+ }
13508
13872
  /**
13509
13873
  * Determine if the two caret origins are in distinct blocks that
13510
13874
  * should be merged.
@@ -13952,5 +14316,55 @@ function mergeRegister(...func) {
13952
14316
  func.length = 0;
13953
14317
  };
13954
14318
  }
14319
+ /**
14320
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14321
+ *
14322
+ * This source code is licensed under the MIT license found in the
14323
+ * LICENSE file in the root directory of this source tree.
14324
+ *
14325
+ */
14326
+ /**
14327
+ * A map of event type to listener for a given {@link EventTarget}. Each
14328
+ * listener's event argument is inferred from the event type, e.g. for an
14329
+ * `HTMLElement` the `'keydown'` listener receives a `KeyboardEvent`.
14330
+ */
14331
+ /**
14332
+ * Add several event listeners to a single `target` and return one function
14333
+ * that removes all of them.
14334
+ *
14335
+ * This is the batch form of {@link registerEventListener}: it takes a
14336
+ * `{type: listener}` object (strongly typed per event type) and shares one
14337
+ * `options` value across every listener. The returned dispose function removes
14338
+ * the listeners in reverse registration order (via {@link mergeRegister}).
14339
+ *
14340
+ * Because `options` is shared, register listeners that need a different
14341
+ * `options` value (e.g. a different `capture` flag) with a separate call and
14342
+ * combine the results with `mergeRegister`.
14343
+ *
14344
+ * @example
14345
+ * ```ts
14346
+ * // All five listeners share {capture: true}
14347
+ * return registerEventListeners(
14348
+ * window,
14349
+ * {
14350
+ * beforeinput: report,
14351
+ * cut: report,
14352
+ * keydown: report,
14353
+ * paste: report,
14354
+ * selectionchange: report,
14355
+ * },
14356
+ * {capture: true},
14357
+ * );
14358
+ * ```
14359
+ *
14360
+ * @param target - The {@link EventTarget} to subscribe to
14361
+ * @param listeners - A map of event type to listener
14362
+ * @param options - Options forwarded to `add`/`removeEventListener` for every
14363
+ * listener
14364
+ * @returns A function that removes every listener when called
14365
+ */
14366
+ function registerEventListeners(target, listeners, options) {
14367
+ return mergeRegister(...Object.entries(listeners).map(([type, listener]) => registerEventListener(target, type, listener, options)));
14368
+ }
13955
14369
  //#endregion
13956
- export { $addUpdateTag, $applyNodeReplacement, $assumeActiveEditor, $caretFromPoint, $caretRangeFromSelection, $cloneWithProperties, $cloneWithPropertiesEphemeral, $comparePointCaretNext, $copyNode, $create, $createChildrenArray, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createPoint, $createRangeSelection, $createRangeSelectionFromDom, $createTabNode, $createTextNode, $extendCaretToRange, $findMatchingParent, $fullReconcile, $generateNodesFromRawText, $getAdjacentChildCaret, $getAdjacentNode, $getAdjacentSiblingOrParentSiblingCaret, $getCaretInDirection, $getCaretRange, $getCaretRangeInDirection, $getCharacterOffsets, $getChildCaret, $getChildCaretAtIndex, $getChildCaretOrSelf, $getCollapsedCaretRange, $getCommonAncestor, $getCommonAncestorResultBranchOrder, $getDOMSlot, $getDOMTextNode, $getEditor, $getEditorDOMRenderConfig, $getNearestNodeFromDOMNode, $getNearestRootOrShadowRoot, $getNodeByKey, $getNodeByKeyOrThrow, $getNodeFromDOMNode, $getPreviousSelection, $getRoot, $getSelection, $getSiblingCaret, $getSlot, $getSlotFrame, $getSlotHost, $getSlotNameWithinHost, $getSlotNames, $getState, $getStateChange, $getTextContent, $getTextNodeOffset, $getTextPointCaret, $getTextPointCaretSlice, $getWritableNodeState, $hasAncestor, $hasUpdateTag, $insertNodeToNearestRootAtCaret, $insertNodes, $isBlockElementNode, $isChildCaret, $isDecoratorNode, $isEditorState, $isElementDOMSlot, $isElementNode, $isExtendableTextPointCaret, $isInlineElementOrDecoratorNode, $isLeafNode, $isLexicalNode, $isLineBreakNode, $isNodeCaret, $isNodeSelection, $isParagraphNode, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isSelectionCapturedInDecoratorInput, $isSiblingCaret, $isSlotChild, $isSlotHost, $isTabNode, $isTextNode, $isTextPointCaret, $isTextPointCaretSlice, $isTokenOrSegmented, $isTokenOrTab, $markSlotEditable, $nodesOfType, $normalizeCaret, $normalizeSelection as $normalizeSelection__EXPERIMENTAL, $onUpdate, $parseSerializedNode, $removeFromParent, $removeSlot, $removeTextFromCaretRange, $rewindSiblingCaret, $selectAll, $setCompositionKey, $setDirectionFromDOM, $setFormatFromDOM, $setPointFromCaret, $setSelection, $setSelectionFromCaretRange, $setSlot, $setState, $splitAtPointCaretNext, $splitNode, $updateDOMSelection, $updateRangeSelectionFromCaretRange, ArtificialNode__DO_NOT_USE, BEFORE_INPUT_COMMAND, BLUR_COMMAND, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, CAN_USE_BEFORE_INPUT, CAN_USE_DOM, CLEAR_EDITOR_COMMAND, CLEAR_HISTORY_COMMAND, CLICK_COMMAND, COLLABORATION_TAG, COMMAND_PRIORITY_BEFORE_CRITICAL, COMMAND_PRIORITY_BEFORE_EDITOR, COMMAND_PRIORITY_BEFORE_HIGH, COMMAND_PRIORITY_BEFORE_LOW, COMMAND_PRIORITY_BEFORE_NORMAL, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, COMPOSITION_END_COMMAND, COMPOSITION_END_TAG, COMPOSITION_START_COMMAND, COMPOSITION_START_TAG, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, CUT_COMMAND, CUT_TAG, DEFAULT_EDITOR_DOM_CONFIG, DELETE_CHARACTER_COMMAND, DELETE_LINE_COMMAND, DELETE_WORD_COMMAND, DRAGEND_COMMAND, DRAGOVER_COMMAND, DRAGSTART_COMMAND, DROP_COMMAND, DecoratorNode, ElementNode, FOCUS_COMMAND, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INPUT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, INTERNAL_$isBlock, IS_ALL_FORMATTING, IS_ANDROID, IS_ANDROID_CHROME, IS_APPLE, IS_APPLE_WEBKIT, IS_BOLD, IS_CHROME, IS_CODE, IS_FIREFOX, IS_HIGHLIGHT, IS_IOS, IS_ITALIC, IS_SAFARI, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, KEY_MODIFIER_COMMAND, KEY_SPACE_COMMAND, KEY_TAB_COMMAND, LineBreakNode, MOVE_TO_END, MOVE_TO_START, NODE_STATE_DIRECT, NODE_STATE_KEY, NODE_STATE_LATEST, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, PASTE_TAG, ParagraphNode, REDO_COMMAND, REMOVE_TEXT_COMMAND, RootNode, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SELECT_ALL_COMMAND, SKIP_COLLAB_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG, SKIP_SELECTION_FOCUS_TAG, TEXT_TYPE_TO_FORMAT, TabNode, TextNode, UNDO_COMMAND, addClassNamesToElement, buildImportMap, configExtension, createCommand, createEditor, createSharedNodeState, createState, declarePeerDependency, defineExtension, findAllLexicalElementsDeep, flipDirection, getActiveElement, getActiveElementDeep, getComposedEventTarget, getComposedStaticRange, getDOMOwnerDocument, getDOMSelection, getDOMSelectionFromTarget, getDOMSelectionPoints, getDOMSelectionRange, getDOMSelectionRangeAndPoints, getDOMShadowRoots, getDOMTextNode, getDeclaredSlots, getEditorPropertyFromDOMNode, getNearestEditorFromDOMNode, getParentElement, getRegisteredNode, getRegisteredNodeOrThrow, getRegisteredSubtypeMap, getRootOwnerDocument, getStaticNodeConfig, getStyleObjectFromCSS, getTextDirection, getTransformSetFromKlass, isBlockDomNode, isCurrentlyReadOnlyMode, isDOMCapturingSelection, isDOMDocumentNode, isDOMNode, isDOMShadowRoot, isDOMTextNode, isDOMUnmanaged, isDocumentFragment, isExactShortcutMatch, isHTMLAnchorElement, isHTMLElement, isHTMLTableCellElement, isHTMLTableRowElement, isInlineDomNode, isLastChildInBlockNode, isLexicalEditor, isModifierMatch, isOnlyChildInBlockNode, isSelectionCapturedInDecoratorInput, isSelectionWithinEditor, iterStaticNodeConfigChain, makeStepwiseIterator, mergeRegister, mountSlotContainer, normalizeClassNames, removeClassNamesFromElement, removeFromParent, resetRandomKey, safeCast, setDOMStyleFromCSS, setDOMStyleObject, setDOMUnmanaged, setNodeIndentFromDOM, shallowMergeConfig, stopLexicalPropagation, toggleTextFormatType, tokenizeRawText, unmountSlotContainer };
14370
+ export { $addUpdateTag, $applyNodeReplacement, $assumeActiveEditor, $caretFromPoint, $caretRangeFromSelection, $cloneWithProperties, $cloneWithPropertiesEphemeral, $comparePointCaretNext, $copyNode, $create, $createChildrenArray, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createPoint, $createRangeSelection, $createRangeSelectionFromDom, $createTabNode, $createTextNode, $extendCaretToRange, $findMatchingParent, $formatText, $fullReconcile, $generateNodesFromRawText, $getAdjacentChildCaret, $getAdjacentNode, $getAdjacentSiblingOrParentSiblingCaret, $getCaretInDirection, $getCaretRange, $getCaretRangeInDirection, $getCharacterOffsets, $getChildCaret, $getChildCaretAtIndex, $getChildCaretOrSelf, $getCollapsedCaretRange, $getCommonAncestor, $getCommonAncestorResultBranchOrder, $getDOMSlot, $getDOMTextNode, $getDocument, $getEditor, $getEditorDOMRenderConfig, $getNearestNodeFromDOMNode, $getNearestRootOrShadowRoot, $getNodeByKey, $getNodeByKeyOrThrow, $getNodeFromDOMNode, $getPreviousSelection, $getRoot, $getSelection, $getSiblingCaret, $getSlot, $getSlotFrame, $getSlotHost, $getSlotNameWithinHost, $getSlotNames, $getState, $getStateChange, $getTextContent, $getTextNodeOffset, $getTextPointCaret, $getTextPointCaretSlice, $getWritableNodeState, $hasAncestor, $hasUpdateTag, $insertNodeToNearestRootAtCaret, $insertNodes, $isBlockElementNode, $isChildCaret, $isDecoratorNode, $isEditorState, $isElementDOMSlot, $isElementNode, $isExtendableTextPointCaret, $isInlineElementOrDecoratorNode, $isInlineFormattable, $isLeafNode, $isLexicalNode, $isLineBreakNode, $isNodeCaret, $isNodeSelection, $isParagraphNode, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isSelectionCapturedInDecoratorInput, $isShadowRootNode, $isSiblingCaret, $isSlotChild, $isSlotHost, $isTabNode, $isTextNode, $isTextPointCaret, $isTextPointCaretSlice, $isTokenOrSegmented, $isTokenOrTab, $markSlotEditable, $needsBlockCursorBeside, $nodesOfType, $normalizeCaret, $normalizeSelection as $normalizeSelection__EXPERIMENTAL, $onUpdate, $parseSerializedNode, $removeFromParent, $removeSlot, $removeTextFromCaretRange, $rewindSiblingCaret, $selectAll, $setCompositionKey, $setDirectionFromDOM, $setFormatFromDOM, $setPointFromCaret, $setSelection, $setSelectionFromCaretRange, $setSlot, $setState, $setTextFormat, $splitAtPointCaretNext, $splitNode, $updateDOMSelection, $updateRangeSelectionFromCaretRange, ArtificialNode__DO_NOT_USE, BEFORE_INPUT_COMMAND, BLUR_COMMAND, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, CAN_USE_BEFORE_INPUT, CAN_USE_DOM, CLEAR_EDITOR_COMMAND, CLEAR_HISTORY_COMMAND, CLICK_COMMAND, COLLABORATION_TAG, COMMAND_PRIORITY_BEFORE_CRITICAL, COMMAND_PRIORITY_BEFORE_EDITOR, COMMAND_PRIORITY_BEFORE_HIGH, COMMAND_PRIORITY_BEFORE_LOW, COMMAND_PRIORITY_BEFORE_NORMAL, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, COMPOSITION_END_COMMAND, COMPOSITION_END_TAG, COMPOSITION_START_COMMAND, COMPOSITION_START_TAG, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, CUT_COMMAND, CUT_TAG, DEFAULT_EDITOR_DOM_CONFIG, DELETE_CHARACTER_COMMAND, DELETE_LINE_COMMAND, DELETE_WORD_COMMAND, DRAGEND_COMMAND, DRAGOVER_COMMAND, DRAGSTART_COMMAND, DROP_COMMAND, DecoratorNode, ElementNode, FOCUS_COMMAND, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INPUT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, INTERNAL_$isBlock, IS_ALL_FORMATTING, IS_ANDROID, IS_ANDROID_CHROME, IS_APPLE, IS_APPLE_WEBKIT, IS_BOLD, IS_CHROME, IS_CODE, IS_FIREFOX, IS_HIGHLIGHT, IS_IOS, IS_ITALIC, IS_SAFARI, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, KEY_MODIFIER_COMMAND, KEY_SPACE_COMMAND, KEY_TAB_COMMAND, LineBreakNode, MOVE_TO_END, MOVE_TO_START, NODE_STATE_DIRECT, NODE_STATE_KEY, NODE_STATE_LATEST, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, PASTE_TAG, ParagraphNode, REDO_COMMAND, REMOVE_TEXT_COMMAND, RootNode, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SELECT_ALL_COMMAND, SET_TEXT_FORMAT_COMMAND, SKIP_COLLAB_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG, SKIP_SELECTION_FOCUS_TAG, TEXT_TYPE_TO_FORMAT, TabNode, TextNode, UNDO_COMMAND, addClassNamesToElement, buildImportMap, configExtension, createCommand, createEditor, createRefCountedRegistry, createSharedNodeState, createState, declarePeerDependency, defineExtension, findAllLexicalElementsDeep, flipDirection, getActiveElement, getActiveElementDeep, getComposedEventTarget, getComposedStaticRange, getDOMOwnerDocument, getDOMSelection, getDOMSelectionFromTarget, getDOMSelectionPoints, getDOMSelectionRange, getDOMSelectionRangeAndPoints, getDOMShadowRoots, getDOMTextNode, getDeclaredSlots, getEditorPropertyFromDOMNode, getNearestEditorFromDOMNode, getParentElement, getRegisteredNode, getRegisteredNodeOrThrow, getRegisteredSubtypeMap, getRootOwnerDocument, getStaticNodeConfig, getStyleObjectFromCSS, getTextDirection, getTransformSetFromKlass, isBlockDomNode, isCurrentlyReadOnlyMode, isDOMCapturingSelection, isDOMDocumentNode, isDOMNode, isDOMShadowRoot, isDOMTextNode, isDOMUnmanaged, isDocumentFragment, isExactShortcutMatch, isHTMLAnchorElement, isHTMLElement, isHTMLTableCellElement, isHTMLTableRowElement, isInlineDomNode, isLastChildInBlockNode, isLexicalEditor, isModifierMatch, isOnlyChildInBlockNode, isSelectionCapturedInDecoratorInput, isSelectionWithinEditor, iterStaticNodeConfigChain, makeStepwiseIterator, mergeRegister, mountSlotContainer, normalizeClassNames, registerEventListener, registerEventListeners, removeClassNamesFromElement, removeFromParent, resetRandomKey, safeCast, setDOMStyleFromCSS, setDOMStyleObject, setDOMUnmanaged, setNodeIndentFromDOM, shallowMergeConfig, stopLexicalPropagation, toggleTextFormatType, tokenizeRawText, unmountSlotContainer };