@mx-space/cli 0.14.0 → 0.14.2

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 (52) hide show
  1. package/dist/{Lexical.dev-DTxuYHmG.mjs → Lexical.dev-CtGmYDoh.mjs} +770 -271
  2. package/dist/{Lexical.node-BlCLnfA6.mjs → Lexical.node-8sRFmq2X.mjs} +15 -5
  3. package/dist/Lexical.prod-D-VfeKDz.mjs +7200 -0
  4. package/dist/{LexicalClipboard.dev-CdFkLR_D.mjs → LexicalClipboard.dev-CzQ15z6E.mjs} +6 -6
  5. package/dist/{LexicalClipboard.node-ktUajxsv.mjs → LexicalClipboard.node-D4gMGws6.mjs} +2 -2
  6. package/dist/{LexicalClipboard.prod-DliyLofW.mjs → LexicalClipboard.prod-CltkQpzS.mjs} +10 -8
  7. package/dist/{LexicalCodeCore.dev-CL9tHWJX.mjs → LexicalCodeCore.dev-DkYBZRGB.mjs} +11 -9
  8. package/dist/{LexicalCodeCore.prod-BoMSJc29.mjs → LexicalCodeCore.prod-COc3_QL8.mjs} +178 -167
  9. package/dist/{LexicalDragon.dev-CqW7Aq5H.mjs → LexicalDragon.dev-Dv5_WUae.mjs} +3 -3
  10. package/dist/{LexicalDragon.node-DLXctJyy.mjs → LexicalDragon.node-DgjrmWkd.mjs} +2 -2
  11. package/dist/{LexicalDragon.prod-DHI3T6n5.mjs → LexicalDragon.prod-DnpbDe-_.mjs} +5 -4
  12. package/dist/{LexicalExtension.dev-BP-18zns.mjs → LexicalExtension.dev-JMH_6-OZ.mjs} +73 -52
  13. package/dist/{LexicalExtension.node-BWH6xbzG.mjs → LexicalExtension.node-BUozzx4t.mjs} +4 -2
  14. package/dist/{LexicalExtension.prod-Cs6OgrDn.mjs → LexicalExtension.prod-UiyDgmou.mjs} +370 -347
  15. package/dist/{LexicalHeadless.dev-DJp__UcP.mjs → LexicalHeadless.dev-BfLGciiX.mjs} +2 -2
  16. package/dist/{LexicalHeadless.prod-o0v9cIu5.mjs → LexicalHeadless.prod-CWF7edmQ.mjs} +2 -2
  17. package/dist/{LexicalHtml.dev-DoymwFpG.mjs → LexicalHtml.dev-D5EF5PGW.mjs} +13 -11
  18. package/dist/{LexicalHtml.node-CVkbWtUA.mjs → LexicalHtml.node-BYbzHNVe.mjs} +4 -4
  19. package/dist/{LexicalHtml.prod-C2b3z99k.mjs → LexicalHtml.prod-D_FfkvVD.mjs} +330 -299
  20. package/dist/{LexicalLink.dev-7cgLOLIe.mjs → LexicalLink.dev-D3Pgh6ik.mjs} +53 -23
  21. package/dist/{LexicalLink.prod-DWFgoXHR.mjs → LexicalLink.prod-XZoAYVYD.mjs} +217 -198
  22. package/dist/{LexicalList.dev-Ci2iOxFx.mjs → LexicalList.dev-B5d3ObhB.mjs} +49 -44
  23. package/dist/LexicalList.prod-OQDpVAEb.mjs +967 -0
  24. package/dist/{LexicalMarkdown.dev-C0ZYCyuR.mjs → LexicalMarkdown.dev-BkM8G3DG.mjs} +61 -42
  25. package/dist/{LexicalMarkdown.prod-0M-Nc-rh.mjs → LexicalMarkdown.prod-DcVXCex6.mjs} +254 -202
  26. package/dist/{LexicalRichText.dev-Cnr1kDym.mjs → LexicalRichText.dev-DppD7IrA.mjs} +337 -52
  27. package/dist/{LexicalRichText.node-BUJZAiap.mjs → LexicalRichText.node-CPSn7dkg.mjs} +12 -8
  28. package/dist/LexicalRichText.prod-h7JPxFNp.mjs +718 -0
  29. package/dist/{LexicalSelection.dev-C4L7l8OB.mjs → LexicalSelection.dev-CD0SLbxP.mjs} +48 -17
  30. package/dist/{LexicalSelection.node-CLKroUMQ.mjs → LexicalSelection.node-CGlJgUlZ.mjs} +5 -4
  31. package/dist/{LexicalSelection.prod-DzFqpTlw.mjs → LexicalSelection.prod-DbVZN5AV.mjs} +101 -92
  32. package/dist/{LexicalTable.dev-Cc9-zqKa.mjs → LexicalTable.dev-D0HU14UV.mjs} +340 -138
  33. package/dist/LexicalTable.prod-BcPNSrxP.mjs +2468 -0
  34. package/dist/{LexicalUtils.dev-BRgK0L6G.mjs → LexicalUtils.dev-iKy_TrRT.mjs} +15 -19
  35. package/dist/{LexicalUtils.node-BVAwOkL4.mjs → LexicalUtils.node-CIgMS8cG.mjs} +6 -6
  36. package/dist/{LexicalUtils.prod-C1gtvVDP.mjs → LexicalUtils.prod-DIAsKJzu.mjs} +155 -159
  37. package/dist/bin/mxs.mjs +1 -1
  38. package/dist/esm-CH7CrOvh.mjs +2 -0
  39. package/dist/{esm-BNu4sUWV.mjs → esm-CwXl1N0a.mjs} +1801 -6013
  40. package/dist/index.d.mts +57 -62
  41. package/dist/index.mjs +1 -1
  42. package/dist/{mxs-BUqt-N3N.mjs → mxs-iW9DgqSZ.mjs} +14960 -9235
  43. package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
  44. package/dist/vendor/litexml/cli.mjs +6693 -5762
  45. package/dist/vendor/litexml/litexml-html-preview-client.js +11912 -8698
  46. package/package.json +31 -30
  47. package/LICENSE +0 -33
  48. package/dist/Lexical.prod-CeB9UQuo.mjs +0 -6816
  49. package/dist/LexicalList.prod-D0xX5d3G.mjs +0 -944
  50. package/dist/LexicalRichText.prod-BfbMQ9uM.mjs +0 -538
  51. package/dist/LexicalTable.prod-BgO4nCxy.mjs +0 -2336
  52. 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.48.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.48.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);
@@ -2912,7 +3011,7 @@ function onPointerDown(event, editor) {
2912
3011
  const target = getComposedEventTarget(event);
2913
3012
  const pointerType = event.pointerType;
2914
3013
  if (isDOMNode(target) && pointerType !== "touch" && pointerType !== "pen" && event.button === 0) updateEditorSync(editor, () => {
2915
- if (!isDOMCapturingSelection(target, editor)) isSelectionChangeFromMouseDown = true;
3014
+ if (!isDOMCapturingSelection(target, editor)) editor._inputState.isSelectionChangeFromMouseDown = true;
2916
3015
  });
2917
3016
  }
2918
3017
  function getTargetRange(event) {
@@ -2922,6 +3021,7 @@ function getTargetRange(event) {
2922
3021
  return targetRanges[0];
2923
3022
  }
2924
3023
  function $maybeMoveSelectionPastTrailingAcceptanceBoundary(insertedText) {
3024
+ const { lastKeyCode } = getActiveEditor()._inputState;
2925
3025
  if (insertedText == null || insertedText.length <= 1 || lastKeyCode == null) return;
2926
3026
  const characterToSearchFor = lastKeyCode.length === 1 ? lastKeyCode : lastKeyCode === "Enter" ? "\n" : lastKeyCode === "Tab" ? " " : null;
2927
3027
  if (!characterToSearchFor) return;
@@ -2947,20 +3047,20 @@ function $maybeMoveSelectionPastTrailingAcceptanceBoundary(insertedText) {
2947
3047
  function $canRemoveText(anchorNode, focusNode) {
2948
3048
  return anchorNode !== focusNode || $isElementNode(anchorNode) || $isElementNode(focusNode) || !$isTokenOrTab(anchorNode) || !$isTokenOrTab(focusNode);
2949
3049
  }
2950
- function isPossiblyAndroidKeyPress(timeStamp) {
2951
- return lastKeyCode === "MediaLast" && timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
3050
+ function isPossiblyAndroidKeyPress(inputState, timeStamp) {
3051
+ return inputState.lastKeyCode === "MediaLast" && timeStamp < inputState.lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
2952
3052
  }
2953
- function clearHandledSelectionCommandInsertText() {
2954
- isInsertTextAfterHandledSelectionCommand = false;
2955
- if (handledSelectionCommandTimeoutId !== null) {
2956
- clearTimeout(handledSelectionCommandTimeoutId);
2957
- handledSelectionCommandTimeoutId = null;
3053
+ function clearHandledSelectionCommandInsertText(inputState) {
3054
+ inputState.isInsertTextAfterHandledSelectionCommand = false;
3055
+ if (inputState.handledSelectionCommandTimeoutId !== null) {
3056
+ clearTimeout(inputState.handledSelectionCommandTimeoutId);
3057
+ inputState.handledSelectionCommandTimeoutId = null;
2958
3058
  }
2959
3059
  }
2960
- function markHandledSelectionCommandInsertText() {
2961
- clearHandledSelectionCommandInsertText();
2962
- isInsertTextAfterHandledSelectionCommand = true;
2963
- handledSelectionCommandTimeoutId = setTimeout(clearHandledSelectionCommandInsertText, 0);
3060
+ function markHandledSelectionCommandInsertText(inputState) {
3061
+ clearHandledSelectionCommandInsertText(inputState);
3062
+ inputState.isInsertTextAfterHandledSelectionCommand = true;
3063
+ inputState.handledSelectionCommandTimeoutId = setTimeout(() => clearHandledSelectionCommandInsertText(inputState), 0);
2964
3064
  }
2965
3065
  function registerDefaultCommandHandlers(editor) {
2966
3066
  editor.registerCommand(BEFORE_INPUT_COMMAND, $handleBeforeInput, 0);
@@ -3002,9 +3102,10 @@ function $handleBeforeInput(event) {
3002
3102
  const inputType = event.inputType;
3003
3103
  const targetRange = getTargetRange(event);
3004
3104
  const editor = getActiveEditor();
3105
+ const inputState = editor._inputState;
3005
3106
  const selection = $getSelection();
3006
- if (inputType === "insertText" && event.data && isInsertTextAfterHandledSelectionCommand) {
3007
- clearHandledSelectionCommandInsertText();
3107
+ if (inputType === "insertText" && event.data && inputState.isInsertTextAfterHandledSelectionCommand) {
3108
+ clearHandledSelectionCommandInsertText(inputState);
3008
3109
  event.preventDefault();
3009
3110
  if ($isRangeSelection(selection) && !selection.isCollapsed()) {
3010
3111
  const point = selection.isBackward() ? selection.anchor : selection.focus;
@@ -3021,9 +3122,9 @@ function $handleBeforeInput(event) {
3021
3122
  }
3022
3123
  if ($isRangeSelection(selection)) {
3023
3124
  const isSelectionAnchorSameAsFocus = selection.anchor.key === selection.focus.key;
3024
- if (isPossiblyAndroidKeyPress(event.timeStamp) && editor.isComposing() && isSelectionAnchorSameAsFocus) {
3125
+ if (isPossiblyAndroidKeyPress(inputState, event.timeStamp) && editor.isComposing() && isSelectionAnchorSameAsFocus) {
3025
3126
  $setCompositionKey(null);
3026
- lastKeyDownTimeStamp = 0;
3127
+ inputState.lastKeyDownTimeStamp = 0;
3027
3128
  setTimeout(() => {
3028
3129
  updateEditorSync(editor, () => {
3029
3130
  $setCompositionKey(null);
@@ -3056,8 +3157,8 @@ function $handleBeforeInput(event) {
3056
3157
  dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
3057
3158
  const selectionAfterDelete = $getSelection();
3058
3159
  if (IS_ANDROID_CHROME && $isRangeSelection(selectionAfterDelete) && selectionAfterDelete.isCollapsed()) {
3059
- postDeleteSelectionToRestore = selectionAfterDelete;
3060
- setTimeout(() => postDeleteSelectionToRestore = null);
3160
+ inputState.postDeleteSelectionToRestore = selectionAfterDelete;
3161
+ setTimeout(() => inputState.postDeleteSelectionToRestore = null);
3061
3162
  }
3062
3163
  }
3063
3164
  }
@@ -3066,9 +3167,9 @@ function $handleBeforeInput(event) {
3066
3167
  }
3067
3168
  if (!$isRangeSelection(selection)) return true;
3068
3169
  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;
3170
+ if (inputState.unprocessedBeforeInputData !== null) $updateSelectedTextFromDOM(false, editor, inputState.unprocessedBeforeInputData);
3171
+ if ((!selection.dirty || inputState.unprocessedBeforeInputData !== null) && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode()) && targetRange !== null) selection.applyDOMRange(targetRange);
3172
+ inputState.unprocessedBeforeInputData = null;
3072
3173
  const anchor = selection.anchor;
3073
3174
  const focus = selection.focus;
3074
3175
  const anchorNode = anchor.getNode();
@@ -3088,8 +3189,8 @@ function $handleBeforeInput(event) {
3088
3189
  event.preventDefault();
3089
3190
  dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
3090
3191
  $maybeMoveSelectionPastTrailingAcceptanceBoundary(data);
3091
- } else unprocessedBeforeInputData = data;
3092
- lastBeforeInputInsertTextTimeStamp = event.timeStamp;
3192
+ } else inputState.unprocessedBeforeInputData = data;
3193
+ inputState.lastBeforeInputInsertTextTimeStamp = event.timeStamp;
3093
3194
  return true;
3094
3195
  }
3095
3196
  event.preventDefault();
@@ -3100,18 +3201,23 @@ function $handleBeforeInput(event) {
3100
3201
  dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
3101
3202
  $maybeMoveSelectionPastTrailingAcceptanceBoundary((event.dataTransfer ? event.dataTransfer.getData("text/plain") : null) ?? event.data);
3102
3203
  break;
3103
- case "insertFromComposition":
3204
+ case "insertFromComposition": {
3205
+ const skipRedundantInsert = inputState.hadOrphanedCompositionEvents;
3206
+ inputState.hadOrphanedCompositionEvents = false;
3207
+ const prevCompositionKey = editor._compositionKey;
3104
3208
  $setCompositionKey(null);
3105
- dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
3209
+ if (!skipRedundantInsert) dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
3210
+ $cleanupComposedSubclass(prevCompositionKey);
3106
3211
  break;
3212
+ }
3107
3213
  case "insertLineBreak":
3108
3214
  $setCompositionKey(null);
3109
3215
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
3110
3216
  break;
3111
3217
  case "insertParagraph":
3112
3218
  $setCompositionKey(null);
3113
- if (isInsertLineBreak && !IS_IOS) {
3114
- isInsertLineBreak = false;
3219
+ if (inputState.isInsertLineBreak && !IS_IOS) {
3220
+ inputState.isInsertLineBreak = false;
3115
3221
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
3116
3222
  } else dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, void 0);
3117
3223
  break;
@@ -3170,14 +3276,16 @@ function $handleBeforeInput(event) {
3170
3276
  }
3171
3277
  function onInput(event, editor) {
3172
3278
  event.stopPropagation();
3173
- clearHandledSelectionCommandInsertText();
3279
+ const inputState = editor._inputState;
3280
+ clearHandledSelectionCommandInsertText(inputState);
3174
3281
  updateEditorSync(editor, () => {
3175
3282
  if (!isInputEventTargetingCapturedSelection(event, editor)) editor.dispatchCommand(INPUT_COMMAND, event);
3176
3283
  }, { event });
3177
- unprocessedBeforeInputData = null;
3284
+ inputState.unprocessedBeforeInputData = null;
3178
3285
  }
3179
3286
  function $handleInput(event) {
3180
3287
  const editor = getActiveEditor();
3288
+ const inputState = editor._inputState;
3181
3289
  const selection = $getSelection();
3182
3290
  const data = event.data;
3183
3291
  const targetRange = getTargetRange(event);
@@ -3185,11 +3293,20 @@ function $handleInput(event) {
3185
3293
  if (data != null && $isRangeSelection(selection)) {
3186
3294
  const domSelection = getDOMSelection(getWindow(editor));
3187
3295
  const domSelectionPoints = domSelection !== null ? getDOMSelectionPoints(domSelection, editor._rootElement) : null;
3188
- if ($shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, false, domSelectionPoints)) {
3296
+ const isOrphanedCompositionEnd = event.inputType === "insertCompositionText" && inputState.compositionPhase !== "ending-firefox" && !editor.isComposing();
3297
+ if (isOrphanedCompositionEnd) inputState.hadOrphanedCompositionEvents = true;
3298
+ const inputAnchorNode = selection.anchor.getNode();
3299
+ const isCompositionOnToken = event.inputType === "insertCompositionText" && inputState.compositionPhase !== "ending-firefox" && editor.isComposing() && $isTextNode(inputAnchorNode) && $isTokenOrSegmented(inputAnchorNode);
3300
+ if (!isOrphanedCompositionEnd && !isCompositionOnToken && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, false, domSelectionPoints)) {
3189
3301
  handled = true;
3190
- if (isFirefoxEndingComposition) {
3191
- $onCompositionEndImpl(editor, data);
3192
- isFirefoxEndingComposition = false;
3302
+ if (inputState.compositionPhase === "ending-firefox") {
3303
+ const tokenRedirected = $onCompositionEndImpl(editor, data);
3304
+ inputState.compositionPhase = "idle";
3305
+ if (tokenRedirected) {
3306
+ $addUpdateTag(COMPOSITION_END_TAG);
3307
+ $flushMutations();
3308
+ return true;
3309
+ }
3193
3310
  }
3194
3311
  const anchorNode = selection.anchor.getNode();
3195
3312
  if (domSelection === null || domSelectionPoints === null) return true;
@@ -3204,17 +3321,17 @@ function $handleInput(event) {
3204
3321
  selection._cachedIsBackward = null;
3205
3322
  }
3206
3323
  if (IS_ANDROID_CHROME && editor.isComposing()) {
3207
- lastKeyDownTimeStamp = 0;
3324
+ inputState.lastKeyDownTimeStamp = 0;
3208
3325
  $setCompositionKey(null);
3209
3326
  }
3210
3327
  }
3211
3328
  }
3212
3329
  if (!handled) {
3213
3330
  $updateSelectedTextFromDOM(false, editor, data !== null ? data : void 0);
3214
- if (isFirefoxEndingComposition) {
3331
+ if (inputState.compositionPhase === "ending-firefox") {
3215
3332
  $onCompositionEndImpl(editor, data || void 0);
3216
3333
  $addUpdateTag(COMPOSITION_END_TAG);
3217
- isFirefoxEndingComposition = false;
3334
+ inputState.compositionPhase = "idle";
3218
3335
  }
3219
3336
  }
3220
3337
  $flushMutations();
@@ -3225,21 +3342,45 @@ function onCompositionStart(event, editor) {
3225
3342
  }
3226
3343
  function $handleCompositionStart(event) {
3227
3344
  const editor = getActiveEditor();
3345
+ const inputState = editor._inputState;
3228
3346
  const selection = $getSelection();
3229
3347
  if ($isRangeSelection(selection) && !editor.isComposing()) {
3348
+ inputState.compositionPhase = "composing";
3349
+ inputState.hadOrphanedCompositionEvents = false;
3230
3350
  const anchor = selection.anchor;
3231
3351
  const node = selection.anchor.getNode();
3232
3352
  $setCompositionKey(anchor.key);
3233
3353
  $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);
3354
+ 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())) {
3355
+ dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, COMPOSITION_START_CHAR);
3356
+ const updatedSelection = $getSelection();
3357
+ if ($isRangeSelection(updatedSelection)) $setCompositionKey(updatedSelection.anchor.key);
3358
+ }
3235
3359
  }
3236
3360
  return true;
3237
3361
  }
3238
3362
  function $handleCompositionEnd(event) {
3239
- $onCompositionEndImpl(getActiveEditor(), event.data);
3363
+ const editor = getActiveEditor();
3364
+ editor._inputState.compositionPhase = "idle";
3365
+ $onCompositionEndImpl(editor, event.data);
3240
3366
  $addUpdateTag(COMPOSITION_END_TAG);
3241
3367
  return true;
3242
3368
  }
3369
+ function $cleanupComposedSubclass(compositionKey) {
3370
+ if (compositionKey === null) return;
3371
+ const composedNode = $getNodeByKey(compositionKey);
3372
+ if (!$isTextNode(composedNode) || composedNode.getType() === "text" || $isTokenOrSegmented(composedNode) || !composedNode.isAttached()) return;
3373
+ const sel = $getSelection();
3374
+ const offset = $isRangeSelection(sel) && sel.anchor.key === compositionKey ? sel.anchor.offset : null;
3375
+ const replacement = $createTextNode(composedNode.getTextContent());
3376
+ replacement.setFormat(composedNode.getFormat());
3377
+ replacement.setStyle(composedNode.getStyle());
3378
+ composedNode.replace(replacement);
3379
+ if (offset !== null) {
3380
+ const safeOffset = Math.min(offset, replacement.getTextContentSize());
3381
+ replacement.select(safeOffset, safeOffset);
3382
+ }
3383
+ }
3243
3384
  function $onCompositionEndImpl(editor, data) {
3244
3385
  const compositionKey = editor._compositionKey;
3245
3386
  $setCompositionKey(null);
@@ -3259,7 +3400,8 @@ function $onCompositionEndImpl(editor, data) {
3259
3400
  }
3260
3401
  $updateTextNodeFromDOMContent(node, textNode.nodeValue, anchorOffset, focusOffset, true);
3261
3402
  }
3262
- return;
3403
+ $cleanupComposedSubclass(compositionKey);
3404
+ return false;
3263
3405
  } else if (data[data.length - 1] === "\n") {
3264
3406
  const selection = $getSelection();
3265
3407
  if ($isRangeSelection(selection) || $isNodeSelection(selection)) {
@@ -3268,40 +3410,52 @@ function $onCompositionEndImpl(editor, data) {
3268
3410
  selection.anchor.set(focus.key, focus.offset, focus.type);
3269
3411
  }
3270
3412
  dispatchCommand(editor, KEY_ENTER_COMMAND, null);
3271
- return;
3413
+ $cleanupComposedSubclass(compositionKey);
3414
+ return false;
3272
3415
  }
3273
3416
  }
3417
+ const node = $getNodeByKey(compositionKey);
3418
+ if (node !== null && $isTextNode(node) && $isTokenOrSegmented(node)) {
3419
+ node.markDirty();
3420
+ const selection = $getSelection();
3421
+ const textLen = node.getTextContentSize();
3422
+ const offset = $isRangeSelection(selection) && selection.anchor.key === compositionKey ? selection.anchor.offset : textLen;
3423
+ node.select(offset, offset).insertText(data);
3424
+ return true;
3425
+ }
3274
3426
  }
3275
3427
  $updateSelectedTextFromDOM(true, editor, data);
3428
+ $cleanupComposedSubclass(compositionKey);
3429
+ return false;
3276
3430
  }
3277
3431
  function onCompositionEnd(event, editor) {
3278
- if (IS_FIREFOX) isFirefoxEndingComposition = true;
3432
+ const inputState = editor._inputState;
3433
+ if (IS_FIREFOX) inputState.compositionPhase = "ending-firefox";
3279
3434
  else if (!IS_IOS && (IS_SAFARI || IS_APPLE_WEBKIT)) {
3280
- isSafariEndingComposition = true;
3281
- safariEndCompositionEventData = event.data;
3435
+ inputState.compositionPhase = "ending-safari";
3436
+ inputState.compositionEndData = event.data;
3282
3437
  } else dispatchCommand(editor, COMPOSITION_END_COMMAND, event);
3283
3438
  }
3284
3439
  function onKeyDown(event, editor) {
3285
- lastKeyDownTimeStamp = event.timeStamp;
3286
- lastKeyCode = event.key;
3287
- if (event.key !== "Backspace") clearHandledSelectionCommandInsertText();
3440
+ const inputState = editor._inputState;
3441
+ inputState.lastKeyDownTimeStamp = event.timeStamp;
3442
+ inputState.lastKeyCode = event.key;
3443
+ if (event.key !== "Backspace") clearHandledSelectionCommandInsertText(inputState);
3288
3444
  if (editor.isComposing()) return;
3289
3445
  dispatchCommand(editor, KEY_DOWN_COMMAND, event);
3290
3446
  }
3291
3447
  function $handleKeyDown(event) {
3292
3448
  const editor = getActiveEditor();
3449
+ const inputState = editor._inputState;
3293
3450
  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 = "";
3451
+ if (inputState.compositionPhase === "ending-safari") {
3452
+ const isBack = isBackspace(event);
3453
+ if (isBack) updateEditorSync(editor, () => {
3454
+ $onCompositionEndImpl(editor, inputState.compositionEndData);
3455
+ });
3456
+ inputState.compositionPhase = "idle";
3457
+ inputState.compositionEndData = "";
3458
+ if (isBack) return true;
3305
3459
  }
3306
3460
  if (isMoveForward(event)) dispatchCommand(editor, KEY_ARROW_RIGHT_COMMAND, event);
3307
3461
  else if (isMoveToEnd(event)) dispatchCommand(editor, MOVE_TO_END, event);
@@ -3310,18 +3464,18 @@ function $handleKeyDown(event) {
3310
3464
  else if (isMoveUp(event)) dispatchCommand(editor, KEY_ARROW_UP_COMMAND, event);
3311
3465
  else if (isMoveDown(event)) dispatchCommand(editor, KEY_ARROW_DOWN_COMMAND, event);
3312
3466
  else if (isLineBreak(event)) {
3313
- isInsertLineBreak = true;
3467
+ inputState.isInsertLineBreak = true;
3314
3468
  dispatchCommand(editor, KEY_ENTER_COMMAND, event);
3315
3469
  } else if (isSpace(event)) dispatchCommand(editor, KEY_SPACE_COMMAND, event);
3316
3470
  else if (isOpenLineBreak(event)) {
3317
3471
  event.preventDefault();
3318
- isInsertLineBreak = true;
3472
+ inputState.isInsertLineBreak = true;
3319
3473
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, true);
3320
3474
  } else if (isParagraph(event)) {
3321
- isInsertLineBreak = false;
3475
+ inputState.isInsertLineBreak = false;
3322
3476
  dispatchCommand(editor, KEY_ENTER_COMMAND, event);
3323
3477
  } else if (isDeleteBackward(event)) if (isBackspace(event)) {
3324
- if (dispatchCommand(editor, KEY_BACKSPACE_COMMAND, event)) markHandledSelectionCommandInsertText();
3478
+ if (dispatchCommand(editor, KEY_BACKSPACE_COMMAND, event)) markHandledSelectionCommandInsertText(inputState);
3325
3479
  } else {
3326
3480
  event.preventDefault();
3327
3481
  dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
@@ -3364,7 +3518,7 @@ function $handleKeyDown(event) {
3364
3518
  const prevSelection = editor._editorState._selection;
3365
3519
  if (isSelectAll(event)) {
3366
3520
  event.preventDefault();
3367
- if (dispatchCommand(editor, SELECT_ALL_COMMAND, event)) markHandledSelectionCommandInsertText();
3521
+ if (dispatchCommand(editor, SELECT_ALL_COMMAND, event)) markHandledSelectionCommandInsertText(inputState);
3368
3522
  } else if (prevSelection !== null && !$isRangeSelection(prevSelection)) {
3369
3523
  if (isCopy(event)) {
3370
3524
  event.preventDefault();
@@ -3393,8 +3547,8 @@ function onDocumentSelectionChange(event) {
3393
3547
  const ownerDocument = getDOMOwnerDocument(event.target);
3394
3548
  let nextActiveEditor = null;
3395
3549
  let resolvedAnchorNode = null;
3550
+ const registration = ownerDocument !== null ? documentRegistrations.get(ownerDocument) : void 0;
3396
3551
  if (ownerDocument !== null) {
3397
- const registration = documentRegistrations.get(ownerDocument);
3398
3552
  if (registration !== void 0) {
3399
3553
  const editorsForDoc = registration.editors;
3400
3554
  let hasShadow = registration.hasShadowEditor;
@@ -3443,8 +3597,8 @@ function onDocumentSelectionChange(event) {
3443
3597
  }
3444
3598
  }
3445
3599
  if (nextActiveEditor === null) return;
3446
- if (isSelectionChangeFromMouseDown) {
3447
- isSelectionChangeFromMouseDown = false;
3600
+ if (nextActiveEditor._inputState.isSelectionChangeFromMouseDown) {
3601
+ if (registration !== void 0) for (const ed of registration.editors) ed._inputState.isSelectionChangeFromMouseDown = false;
3448
3602
  updateEditorSync(nextActiveEditor, () => {
3449
3603
  const lastSelection = $getPreviousSelection();
3450
3604
  const domAnchorNode = resolvedAnchorNode ?? getDOMSelectionPoints(domSelection, nextActiveEditor._rootElement).anchorNode;
@@ -3475,17 +3629,15 @@ function addRootElementEvents(rootElement, editor) {
3475
3629
  if (registration === void 0) {
3476
3630
  registration = {
3477
3631
  editors: /* @__PURE__ */ new Set(),
3478
- hasShadowEditor: void 0,
3479
- rootElementCount: 0
3632
+ hasShadowEditor: void 0
3480
3633
  };
3481
3634
  documentRegistrations.set(doc, registration);
3482
3635
  }
3483
- if (registration.rootElementCount < 1) doc.addEventListener("selectionchange", onDocumentSelectionChange);
3484
- registration.rootElementCount += 1;
3485
3636
  registration.editors.add(editor);
3486
3637
  registration.hasShadowEditor = void 0;
3487
3638
  rootElement.__lexicalEditor = editor;
3488
3639
  const removeHandles = getRootElementRemoveHandles(rootElement);
3640
+ removeHandles.push(documentSelectionChange.register(doc));
3489
3641
  for (let i = 0; i < rootElementEvents.length; i++) {
3490
3642
  const [eventName, onEvent] = rootElementEvents[i];
3491
3643
  const eventHandler = typeof onEvent === "function" ? (event) => {
@@ -3508,10 +3660,7 @@ function addRootElementEvents(rootElement, editor) {
3508
3660
  case "drop": return isEditable && dispatchCommand(editor, DROP_COMMAND, event);
3509
3661
  }
3510
3662
  };
3511
- rootElement.addEventListener(eventName, eventHandler);
3512
- removeHandles.push(() => {
3513
- rootElement.removeEventListener(eventName, eventHandler);
3514
- });
3663
+ removeHandles.push(registerEventListener(rootElement, eventName, eventHandler));
3515
3664
  }
3516
3665
  }
3517
3666
  const rootElementNotRegisteredWarning = warnOnlyOnce("Root element not registered");
@@ -3526,11 +3675,7 @@ function removeRootElementEvents(rootElement) {
3526
3675
  rootElementNotRegisteredWarning();
3527
3676
  return;
3528
3677
  }
3529
- const newCount = registration.rootElementCount - 1;
3530
- if (!(newCount >= 0)) formatDevErrorMessage(`Root element count less than 0`);
3531
3678
  rootElementToDocument.delete(rootElement);
3532
- registration.rootElementCount = newCount;
3533
- if (newCount === 0) doc.removeEventListener("selectionchange", onDocumentSelectionChange);
3534
3679
  const editor = getEditorPropertyFromDOMNode(rootElement);
3535
3680
  if (isLexicalEditor(editor)) {
3536
3681
  cleanActiveNestedEditorsMap(editor);
@@ -3549,17 +3694,19 @@ function cleanActiveNestedEditorsMap(editor) {
3549
3694
  if (activeNestedEditorsMap.get(rootEditorKey) === editor) activeNestedEditorsMap.delete(rootEditorKey);
3550
3695
  } else activeNestedEditorsMap.delete(editor._key);
3551
3696
  }
3552
- function markSelectionChangeFromDOMUpdate() {
3553
- isSelectionChangeFromDOMUpdate = true;
3697
+ /** @internal */
3698
+ function markSelectionChangeFromDOMUpdate(editor) {
3699
+ editor._inputState.isSelectionChangeFromDOMUpdate = true;
3554
3700
  }
3555
- function markCollapsedSelectionFormat(format, style, offset, key, timeStamp) {
3556
- collapsedSelectionFormat = [
3701
+ /** @internal */
3702
+ function markCollapsedSelectionFormat(editor, format, style, offset, key, timeStamp) {
3703
+ editor._inputState.collapsedSelectionFormat = {
3557
3704
  format,
3558
- style,
3559
- offset,
3560
3705
  key,
3706
+ offset,
3707
+ style,
3561
3708
  timeStamp
3562
- ];
3709
+ };
3563
3710
  }
3564
3711
  /**
3565
3712
  * The base type for all serialized nodes
@@ -4215,8 +4362,7 @@ var LexicalNode = class {
4215
4362
  * @param targetNode - the would-be child node.
4216
4363
  */
4217
4364
  isParentOf(targetNode) {
4218
- const result = $getCommonAncestor(this, targetNode);
4219
- return result !== null && result.type === "ancestor";
4365
+ return $hasAncestor(targetNode, this);
4220
4366
  }
4221
4367
  /**
4222
4368
  * Returns a list of nodes that are between this node and
@@ -5001,7 +5147,7 @@ function $setTextContent(nextText, dom, node) {
5001
5147
  const slot = $getEditorDOMRenderConfig(editor).$getDOMSlot(node, dom, editor);
5002
5148
  const firstChild = slot.getFirstChild();
5003
5149
  if (firstChild === null || firstChild.nodeType !== Node.TEXT_NODE) {
5004
- slot.insertChild(document.createTextNode(text));
5150
+ slot.insertChild($getDocument().createTextNode(text));
5005
5151
  return;
5006
5152
  }
5007
5153
  const textChild = firstChild;
@@ -5018,11 +5164,14 @@ function $createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
5018
5164
  const textClassNames = config.theme.text;
5019
5165
  if (textClassNames !== void 0) setTextThemeClassNames(innerTag, 0, format, innerDOM, textClassNames);
5020
5166
  }
5021
- function wrapElementWith(element, tag) {
5022
- const el = document.createElement(tag);
5167
+ function $wrapElementWith(element, tag) {
5168
+ const el = $getDocument().createElement(tag);
5023
5169
  el.appendChild(element);
5024
5170
  return el;
5025
5171
  }
5172
+ function $isInlineFormattable(node) {
5173
+ return node != null && node.__isInlineFormattable === true;
5174
+ }
5026
5175
  /** @noInheritDoc */
5027
5176
  var TextNode = class TextNode extends LexicalNode {
5028
5177
  /** @internal */
@@ -5035,6 +5184,10 @@ var TextNode = class TextNode extends LexicalNode {
5035
5184
  __mode;
5036
5185
  /** @internal */
5037
5186
  __detail;
5187
+ /** @internal */
5188
+ get __isInlineFormattable() {
5189
+ return true;
5190
+ }
5038
5191
  static getType() {
5039
5192
  return "text";
5040
5193
  }
@@ -5082,7 +5235,8 @@ var TextNode = class TextNode extends LexicalNode {
5082
5235
  * @returns TextModeType.
5083
5236
  */
5084
5237
  getMode() {
5085
- return TEXT_TYPE_TO_MODE[this.getLatest().__mode];
5238
+ const self = this.getLatest();
5239
+ return TEXT_TYPE_TO_MODE[self.__mode];
5086
5240
  }
5087
5241
  /**
5088
5242
  * Returns the styles currently applied to the node. This is analogous to CSSText in the DOM.
@@ -5191,11 +5345,11 @@ var TextNode = class TextNode extends LexicalNode {
5191
5345
  const outerTag = getElementOuterTag(this, format);
5192
5346
  const innerTag = getElementInnerTag(this, format);
5193
5347
  const tag = outerTag === null ? innerTag : outerTag;
5194
- const dom = document.createElement(tag);
5348
+ const dom = $getDocument().createElement(tag);
5195
5349
  let innerDOM = dom;
5196
5350
  if (this.hasFormat("code")) dom.setAttribute("spellcheck", "false");
5197
5351
  if (outerTag !== null) {
5198
- innerDOM = document.createElement(innerTag);
5352
+ innerDOM = $getDocument().createElement(innerTag);
5199
5353
  dom.appendChild(innerDOM);
5200
5354
  }
5201
5355
  const text = this.__text;
@@ -5216,7 +5370,7 @@ var TextNode = class TextNode extends LexicalNode {
5216
5370
  if (prevOuterTag === nextOuterTag && prevInnerTag !== nextInnerTag) {
5217
5371
  const prevInnerDOM = dom.firstChild;
5218
5372
  if (prevInnerDOM == null) formatDevErrorMessage(`updateDOM: prevInnerDOM is null or undefined`);
5219
- const nextInnerDOM = document.createElement(nextInnerTag);
5373
+ const nextInnerDOM = $getDocument().createElement(nextInnerTag);
5220
5374
  $createTextInnerDOM(nextInnerDOM, this, nextInnerTag, nextFormat, nextText, config);
5221
5375
  dom.replaceChild(nextInnerDOM, prevInnerDOM);
5222
5376
  return false;
@@ -5301,10 +5455,10 @@ var TextNode = class TextNode extends LexicalNode {
5301
5455
  if (this.hasFormat("lowercase")) element.style.textTransform = "lowercase";
5302
5456
  else if (this.hasFormat("uppercase")) element.style.textTransform = "uppercase";
5303
5457
  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");
5458
+ if (this.hasFormat("bold")) element = $wrapElementWith(element, "b");
5459
+ if (this.hasFormat("italic")) element = $wrapElementWith(element, "i");
5460
+ if (this.hasFormat("strikethrough")) element = $wrapElementWith(element, "s");
5461
+ if (this.hasFormat("underline")) element = $wrapElementWith(element, "u");
5308
5462
  return { element };
5309
5463
  }
5310
5464
  exportJSON() {
@@ -5398,6 +5552,12 @@ var TextNode = class TextNode extends LexicalNode {
5398
5552
  /**
5399
5553
  * Sets the mode of the node.
5400
5554
  *
5555
+ * Note: during IME composition, a segmented TextNode may be temporarily
5556
+ * switched to normal mode to preserve the DOM element that the browser's
5557
+ * composition tracker is bound to. Subclass transforms or method overrides
5558
+ * that assume the node is always in segmented mode should account for this
5559
+ * transient state.
5560
+ *
5401
5561
  * @returns this TextNode.
5402
5562
  */
5403
5563
  setMode(type) {
@@ -5758,7 +5918,7 @@ function applyTextFormatFromStyle(style, shouldApply) {
5758
5918
  const hasUnderlineTextDecoration = textDecoration.includes("underline");
5759
5919
  const verticalAlign = style.verticalAlign;
5760
5920
  return (lexicalNode) => {
5761
- if (!$isTextNode(lexicalNode)) return lexicalNode;
5921
+ if (!$isTextNode(lexicalNode) && !$isInlineFormattable(lexicalNode)) return lexicalNode;
5762
5922
  if (hasBoldFontWeight && !lexicalNode.hasFormat("bold")) lexicalNode.toggleFormat("bold");
5763
5923
  if (hasLinethroughTextDecoration && !lexicalNode.hasFormat("strikethrough")) lexicalNode.toggleFormat("strikethrough");
5764
5924
  if (hasItalicFontStyle && !lexicalNode.hasFormat("italic")) lexicalNode.toggleFormat("italic");
@@ -6026,8 +6186,17 @@ var NodeSelection = class NodeSelection {
6026
6186
  $setSelectionFromCaretRange($getCaretRange(firstCaret, firstCaret));
6027
6187
  }
6028
6188
  for (const node of nodes) node.remove();
6189
+ $ensureRootHasParagraph();
6029
6190
  }
6030
6191
  };
6192
+ function $ensureRootHasParagraph() {
6193
+ const root = $getRoot();
6194
+ if (root.isEmpty()) {
6195
+ const paragraph = $createParagraphNode();
6196
+ root.append(paragraph);
6197
+ paragraph.select();
6198
+ }
6199
+ }
6031
6200
  function $isRangeSelection(x) {
6032
6201
  return x instanceof RangeSelection;
6033
6202
  }
@@ -6207,11 +6376,11 @@ var RangeSelection = class RangeSelection {
6207
6376
  this.dirty = true;
6208
6377
  }
6209
6378
  /**
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.
6379
+ * Returns whether the provided TextFormatType is present on the Selection. This will be true if all text nodes in the Selection
6380
+ * have the specified format.
6212
6381
  *
6213
6382
  * @param type the TextFormatType to check for.
6214
- * @returns true if the provided format is currently toggled on on the Selection, false otherwise.
6383
+ * @returns true if the provided format is currently toggled on the Selection, false otherwise.
6215
6384
  */
6216
6385
  hasFormat(type) {
6217
6386
  const formatFlag = TEXT_TYPE_TO_FORMAT[type];
@@ -6288,12 +6457,14 @@ var RangeSelection = class RangeSelection {
6288
6457
  this.insertText(text);
6289
6458
  return;
6290
6459
  }
6291
- } else if (firstNode.isSegmented() && startOffset !== firstNodeTextLength) {
6460
+ } else if (firstNode.isSegmented() && startOffset !== firstNodeTextLength) if ($getCompositionKey() !== null) firstNode = firstNode.setMode("normal").setFormat(format).setStyle(style);
6461
+ else {
6292
6462
  const textNode = $createTextNode(firstNode.getTextContent());
6293
6463
  textNode.setFormat(format);
6294
6464
  firstNode.replace(textNode);
6295
6465
  firstNode = textNode;
6296
- } else if (!this.isCollapsed() && text !== "") {
6466
+ }
6467
+ else if (!this.isCollapsed() && text !== "") {
6297
6468
  const lastNodeParent = lastNode.getParent();
6298
6469
  if (!firstNodeParent.canInsertTextBefore() || !firstNodeParent.canInsertTextAfter() || $isElementNode(lastNodeParent) && (!lastNodeParent.canInsertTextBefore() || !lastNodeParent.canInsertTextAfter())) {
6299
6470
  this.insertText("");
@@ -6366,7 +6537,10 @@ var RangeSelection = class RangeSelection {
6366
6537
  lastNode.replace(textNode);
6367
6538
  lastNode = textNode;
6368
6539
  }
6369
- if (!$isRootNode(endPoint.getNode()) && endPoint.type === "text") lastNode = lastNode.spliceText(0, endOffset, "");
6540
+ if (!$isRootNode(endPoint.getNode()) && endPoint.type === "text") {
6541
+ if (!$isTextNode(lastNode)) formatDevErrorMessage(`insertText: lastNode is not a TextNode`);
6542
+ lastNode = lastNode.spliceText(0, endOffset, "");
6543
+ }
6370
6544
  markedNodeKeysForKeep.add(lastNode.__key);
6371
6545
  } else {
6372
6546
  const lastNodeParent = lastNode.getParentOrThrow();
@@ -6440,79 +6614,7 @@ var RangeSelection = class RangeSelection {
6440
6614
  * @param alignWithFormat a 32-bit integer representing formatting flags to align with.
6441
6615
  */
6442
6616
  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;
6617
+ $formatText(this, formatType, alignWithFormat);
6516
6618
  }
6517
6619
  /**
6518
6620
  * Attempts to "intelligently" insert an arbitrary list of Lexical nodes into the EditorState at the
@@ -6546,8 +6648,8 @@ var RangeSelection = class RangeSelection {
6546
6648
  if (nodeToSelect !== null) nodeToSelect.selectEnd();
6547
6649
  return;
6548
6650
  }
6549
- const firstNode = (this.isBackward() ? this.focus : this.anchor).getNode();
6550
- const firstBlock = $findMatchingParent(firstNode, INTERNAL_$isBlock);
6651
+ let firstNode = (this.isBackward() ? this.focus : this.anchor).getNode();
6652
+ let firstBlock = $findMatchingParent(firstNode, INTERNAL_$isBlock);
6551
6653
  const last = nodes[nodes.length - 1];
6552
6654
  if ($isElementNode(firstBlock) && "__language" in firstBlock) {
6553
6655
  if ("__language" in nodes[0]) this.insertText(nodes[0].getTextContent());
@@ -6597,6 +6699,10 @@ var RangeSelection = class RangeSelection {
6597
6699
  const blocks = blocksParent.getChildren();
6598
6700
  const isMergeable = (node) => $isElementNode(node) && INTERNAL_$isBlock(node) && !node.isEmpty() && $isElementNode(firstBlock) && (!firstBlock.isEmpty() || firstBlock.canMergeWhenEmpty());
6599
6701
  const insertedParagraph = !$isElementNode(firstBlock) || !firstBlock.isEmpty() ? this.insertParagraph() : null;
6702
+ if (insertedParagraph && !firstBlock.isAttached()) {
6703
+ firstNode = this.anchor.getNode();
6704
+ firstBlock = $findMatchingParent(firstNode, INTERNAL_$isBlock);
6705
+ }
6600
6706
  const lastToInsert = blocks[blocks.length - 1];
6601
6707
  let firstToInsert = blocks[0];
6602
6708
  if (isMergeable(firstToInsert)) {
@@ -6725,21 +6831,37 @@ var RangeSelection = class RangeSelection {
6725
6831
  const rootElement = editor._rootElement;
6726
6832
  const focusNode = this.focus.getNode();
6727
6833
  if (rootElement !== null && blockCursorElement !== null && $isElementNode(focusNode) && !focusNode.isInline() && !focusNode.canBeEmpty()) removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
6834
+ const focusKeyedDOM = getElementByKeyOrThrow(editor, this.focus.key);
6835
+ let nextFocusDOM = focusKeyedDOM;
6836
+ if (this.focus.type === "text") nextFocusDOM = $isTextNode(focusNode) ? $getDOMTextNode(focusNode, focusKeyedDOM, editor) : null;
6728
6837
  if (this.dirty) {
6729
6838
  const anchorKeyedDOM = getElementByKeyOrThrow(editor, this.anchor.key);
6730
- const focusKeyedDOM = getElementByKeyOrThrow(editor, this.focus.key);
6731
6839
  let nextAnchorDOM = anchorKeyedDOM;
6732
- let nextFocusDOM = focusKeyedDOM;
6733
6840
  if (this.anchor.type === "text") {
6734
6841
  const node = this.anchor.getNode();
6735
6842
  nextAnchorDOM = $isTextNode(node) ? $getDOMTextNode(node, anchorKeyedDOM, editor) : null;
6736
6843
  }
6737
- if (this.focus.type === "text") {
6738
- const node = this.focus.getNode();
6739
- nextFocusDOM = $isTextNode(node) ? $getDOMTextNode(node, focusKeyedDOM, editor) : null;
6740
- }
6741
6844
  if (nextAnchorDOM && nextFocusDOM) setDOMSelectionBaseAndExtent(domSelection, nextAnchorDOM, this.anchor.offset, nextFocusDOM, this.focus.offset);
6742
6845
  }
6846
+ if (granularity === "character" && $isTextNode(focusNode) && focusNode.isUnmergeable()) {
6847
+ if (isBackward ? this.focus.offset === 0 : this.focus.offset === focusNode.getTextContentSize()) {
6848
+ const sibling = $getSiblingCaret(focusNode, isBackward ? "previous" : "next").getNodeAtCaret();
6849
+ if ($isTextNode(sibling)) if (collapse) {
6850
+ const sibKeyedDOM = editor.getElementByKey(sibling.getKey());
6851
+ const sibDOM = sibKeyedDOM ? $getDOMTextNode(sibling, sibKeyedDOM, editor) : null;
6852
+ if (sibDOM) {
6853
+ const sibOffset = isBackward ? sibDOM.length : 0;
6854
+ setDOMSelectionBaseAndExtent(domSelection, sibDOM, sibOffset, sibDOM, sibOffset);
6855
+ }
6856
+ } else {
6857
+ const sibLen = sibling.getTextContentSize();
6858
+ if (isBackward) this.focus.set(sibling.__key, sibLen - 1, "text");
6859
+ else this.focus.set(sibling.__key, 1, "text");
6860
+ this.dirty = true;
6861
+ return;
6862
+ }
6863
+ }
6864
+ }
6743
6865
  moveNativeSelection(domSelection, alter, isBackward ? "backward" : "forward", granularity);
6744
6866
  if (domSelection.rangeCount > 0) {
6745
6867
  const composedRange = getComposedStaticRange(domSelection, editor._rootElement);
@@ -6749,23 +6871,7 @@ var RangeSelection = class RangeSelection {
6749
6871
  this.applyDOMRange(range);
6750
6872
  this.dirty = true;
6751
6873
  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
- }
6874
+ $shrinkSelectionToRoot(this, isBackward, root);
6769
6875
  if (!(composedRange ? domSelection.direction !== "backward" : domSelection.anchorNode === range.startContainer && domSelection.anchorOffset === range.startOffset)) $swapPoints(this);
6770
6876
  }
6771
6877
  }
@@ -6862,7 +6968,7 @@ var RangeSelection = class RangeSelection {
6862
6968
  }
6863
6969
  }
6864
6970
  const focus = this.focus;
6865
- this.modify("extend", isBackward, "character");
6971
+ $extendSelectionForDeletion(this, isBackward, "character");
6866
6972
  if (!this.isCollapsed()) {
6867
6973
  const focusNode = focus.type === "text" ? focus.getNode() : null;
6868
6974
  anchorNode = anchor.type === "text" ? anchor.getNode() : null;
@@ -6890,6 +6996,7 @@ var RangeSelection = class RangeSelection {
6890
6996
  if (isBackward && !wasCollapsed && this.isCollapsed() && this.anchor.type === "element" && this.anchor.offset === 0) {
6891
6997
  const anchorNode = this.anchor.getNode();
6892
6998
  if (anchorNode.isEmpty() && $isRootNode(anchorNode.getParent()) && anchorNode.getPreviousSibling() === null) $collapseAtStart(this, anchorNode);
6999
+ $ensureRootHasParagraph();
6893
7000
  }
6894
7001
  }
6895
7002
  /**
@@ -6905,9 +7012,12 @@ var RangeSelection = class RangeSelection {
6905
7012
  this.deleteCharacter(isBackward);
6906
7013
  return;
6907
7014
  }
6908
- if (this.isCollapsed()) this.modify("extend", isBackward, "lineboundary");
7015
+ if (this.isCollapsed()) $extendSelectionForDeletion(this, isBackward, "lineboundary");
6909
7016
  if (this.isCollapsed()) this.deleteCharacter(isBackward);
6910
- else this.removeText();
7017
+ else if ($findMatchingParent(this.anchor.getNode(), INTERNAL_$isBlock) !== $findMatchingParent(this.focus.getNode(), INTERNAL_$isBlock)) {
7018
+ this.focus.set(this.anchor.key, this.anchor.offset, this.anchor.type);
7019
+ this.deleteCharacter(isBackward);
7020
+ } else this.removeText();
6911
7021
  }
6912
7022
  /**
6913
7023
  * Performs one logical word deletion operation on the EditorState based on the current Selection.
@@ -6920,7 +7030,7 @@ var RangeSelection = class RangeSelection {
6920
7030
  const anchor = this.anchor;
6921
7031
  const anchorNode = anchor.getNode();
6922
7032
  if (this.forwardDeletion(anchor, anchorNode, isBackward)) return;
6923
- this.modify("extend", isBackward, "word");
7033
+ $extendSelectionForDeletion(this, isBackward, "word");
6924
7034
  }
6925
7035
  if (this.isCollapsed()) this.deleteCharacter(isBackward);
6926
7036
  else this.removeText();
@@ -6944,6 +7054,124 @@ var RangeSelection = class RangeSelection {
6944
7054
  function $isNodeSelection(x) {
6945
7055
  return x instanceof NodeSelection;
6946
7056
  }
7057
+ /**
7058
+ * Applies a pure bitmask transform to every formattable node in the selection
7059
+ * in a single traversal, splitting the first and last TextNodes as necessary
7060
+ * so that only the selected text is affected. Each node receives exactly one
7061
+ * `setFormat(applyFormat(getFormat()))` (ElementNodes use their textFormat).
7062
+ *
7063
+ * @param selection - the selection whose nodes should be formatted.
7064
+ * @param applyFormat - maps a node's current 32-bit format to its new format.
7065
+ */
7066
+ function $updateTextFormat(selection, applyFormat) {
7067
+ if ($isNodeSelection(selection)) {
7068
+ for (const node of selection.getNodes()) if ($isInlineFormattable(node)) node.setFormat(applyFormat(node.getFormat()));
7069
+ return;
7070
+ }
7071
+ if (selection.isCollapsed()) {
7072
+ selection.setFormat(applyFormat(selection.format));
7073
+ $setCompositionKey(null);
7074
+ return;
7075
+ }
7076
+ const selectedTextNodes = [];
7077
+ for (const node of selection.getNodes()) if ($isTextNode(node)) selectedTextNodes.push(node);
7078
+ else if ($isElementNode(node)) node.setTextFormat(applyFormat(node.getTextFormat()));
7079
+ else if ($isInlineFormattable(node)) node.setFormat(applyFormat(node.getFormat()));
7080
+ const selectedTextNodesLength = selectedTextNodes.length;
7081
+ if (selectedTextNodesLength === 0) {
7082
+ selection.setFormat(applyFormat(selection.format));
7083
+ $setCompositionKey(null);
7084
+ return;
7085
+ }
7086
+ const anchor = selection.anchor;
7087
+ const focus = selection.focus;
7088
+ const isBackward = selection.isBackward();
7089
+ const startPoint = isBackward ? focus : anchor;
7090
+ const endPoint = isBackward ? anchor : focus;
7091
+ let firstIndex = 0;
7092
+ let firstNode = selectedTextNodes[0];
7093
+ let startOffset = startPoint.type === "element" ? 0 : startPoint.offset;
7094
+ if (startPoint.type === "text" && startOffset === firstNode.getTextContentSize()) {
7095
+ firstIndex = 1;
7096
+ firstNode = selectedTextNodes[1];
7097
+ startOffset = 0;
7098
+ }
7099
+ if (firstNode == null) return;
7100
+ const lastIndex = selectedTextNodesLength - 1;
7101
+ let lastNode = selectedTextNodes[lastIndex];
7102
+ const endOffset = endPoint.type === "text" ? endPoint.offset : lastNode.getTextContentSize();
7103
+ if (firstNode.is(lastNode)) {
7104
+ if (startOffset === endOffset) return;
7105
+ const newFormat = applyFormat(firstNode.getFormat());
7106
+ if ($isTokenOrSegmented(firstNode) || startOffset === 0 && endOffset === firstNode.getTextContentSize()) firstNode.setFormat(newFormat);
7107
+ else {
7108
+ const splitNodes = firstNode.splitText(startOffset, endOffset);
7109
+ const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
7110
+ replacement.setFormat(newFormat);
7111
+ if (startPoint.type === "text") startPoint.set(replacement.__key, 0, "text");
7112
+ if (endPoint.type === "text") endPoint.set(replacement.__key, endOffset - startOffset, "text");
7113
+ }
7114
+ selection.format = newFormat;
7115
+ return;
7116
+ }
7117
+ if (startOffset !== 0 && !$isTokenOrSegmented(firstNode)) {
7118
+ [, firstNode] = firstNode.splitText(startOffset);
7119
+ startOffset = 0;
7120
+ }
7121
+ const firstNextFormat = applyFormat(firstNode.getFormat());
7122
+ firstNode.setFormat(firstNextFormat);
7123
+ const lastNextFormat = applyFormat(lastNode.getFormat());
7124
+ if (endOffset > 0) {
7125
+ if (endOffset !== lastNode.getTextContentSize() && !$isTokenOrSegmented(lastNode)) [lastNode] = lastNode.splitText(endOffset);
7126
+ lastNode.setFormat(lastNextFormat);
7127
+ }
7128
+ for (let i = firstIndex + 1; i < lastIndex; i++) {
7129
+ const textNode = selectedTextNodes[i];
7130
+ textNode.setFormat(applyFormat(textNode.getFormat()));
7131
+ }
7132
+ if (startPoint.type === "text") startPoint.set(firstNode.__key, startOffset, "text");
7133
+ if (endPoint.type === "text") endPoint.set(lastNode.__key, endOffset, "text");
7134
+ selection.format = firstNextFormat | lastNextFormat;
7135
+ }
7136
+ /**
7137
+ * Explicitly sets or unsets text formats on the selection. Unlike $formatText
7138
+ * which toggles based on the current selection state, this function sets each
7139
+ * specified format to the exact boolean value provided. Mutually exclusive
7140
+ * formats (subscript/superscript, lowercase/uppercase/capitalize) are
7141
+ * reconciled by {@link toggleTextFormatType}, with later entries winning when
7142
+ * the requested formats conflict.
7143
+ *
7144
+ * @param selection - the selection whose nodes should be formatted.
7145
+ * @param formats - a partial record mapping TextFormatType to boolean.
7146
+ */
7147
+ function $setTextFormat(selection, formats) {
7148
+ const entries = [];
7149
+ for (const [type, value] of Object.entries(formats)) if (typeof value === "boolean") entries.push([type, value]);
7150
+ if (entries.length === 0) return;
7151
+ $updateTextFormat(selection, (format) => {
7152
+ for (const [type, value] of entries) format = toggleTextFormatType(format, type, value ? TEXT_TYPE_TO_FORMAT[type] : 0);
7153
+ return format;
7154
+ });
7155
+ }
7156
+ /**
7157
+ * Applies the provided format to TextNodes and inline formattable nodes
7158
+ * (e.g. DecoratorTextNode) in the selection, splitting or merging TextNodes
7159
+ * as necessary and aligning all formattable nodes to the same target format.
7160
+ *
7161
+ * For RangeSelection the toggle direction is determined by the selection's
7162
+ * computed format (intersection of all text nodes) when no explicit alignment
7163
+ * is given. For NodeSelection each node is toggled independently when no
7164
+ * explicit alignment is given, since there is no TextNode to use as an
7165
+ * alignment reference.
7166
+ *
7167
+ * @param selection - the selection whose nodes should be formatted.
7168
+ * @param formatType - the format type to apply.
7169
+ * @param alignWithFormat - optional 32-bit bitmask to align with.
7170
+ */
7171
+ function $formatText(selection, formatType, alignWithFormat = null) {
7172
+ const effectiveAlign = alignWithFormat === null && $isRangeSelection(selection) ? toggleTextFormatType(selection.format, formatType, null) : alignWithFormat;
7173
+ $updateTextFormat(selection, (format) => toggleTextFormatType(format, formatType, effectiveAlign));
7174
+ }
6947
7175
  function getCharacterOffset(point) {
6948
7176
  const offset = point.offset;
6949
7177
  if (point.type === "text") return offset;
@@ -6980,9 +7208,126 @@ function moveNativeSelection(domSelection, alter, direction, granularity) {
6980
7208
  domSelection.modify(alter, direction, granularity);
6981
7209
  }
6982
7210
  /**
7211
+ * Validate that the selection respects `root` (the nearest root or shadow
7212
+ * root): if any selected node lies outside of it, shrink the selection to the
7213
+ * valid edge in the given direction. The valid node check is a safeguard
7214
+ * against an invalid selection, for which getNodes() returns an empty array.
7215
+ *
7216
+ * @returns true if the selection was shrunk
7217
+ */
7218
+ function $shrinkSelectionToRoot(selection, isBackward, root) {
7219
+ const nodes = selection.getNodes();
7220
+ const validNodes = nodes.filter((node) => $hasAncestor(node, root));
7221
+ if (validNodes.length === 0 || validNodes.length === nodes.length) return false;
7222
+ const edgeNode = isBackward ? validNodes[0] : validNodes[validNodes.length - 1];
7223
+ const edgeElement = $isElementNode(edgeNode) ? edgeNode : edgeNode.getParentOrThrow();
7224
+ if (isBackward) edgeElement.selectStart();
7225
+ else edgeElement.selectEnd();
7226
+ return true;
7227
+ }
7228
+ /**
7229
+ * Extend a collapsed selection by one unit (`character`, `word` or
7230
+ * `lineboundary`) in the deletion direction without ever creating a
7231
+ * non-collapsed DOM selection.
7232
+ *
7233
+ * On Linux/X11, browsers propagate any non-collapsed DOM selection made
7234
+ * during a user gesture to the PRIMARY selection (the middle-click paste
7235
+ * buffer), so a deletion must never pass through a transient non-collapsed
7236
+ * DOM selection or every Backspace/Delete overwrites the user's paste
7237
+ * buffer (https://github.com/facebook/lexical/issues/8766). A collapsed
7238
+ * caret never takes PRIMARY ownership, so the DOM caret is moved with the
7239
+ * native `modify('move')` to measure where the engine places the unit
7240
+ * boundary, and the `[original .. landed]` range is constructed in the
7241
+ * model only. `applyDOMRange` reads just the range's boundary points (it
7242
+ * never touches the DOM selection), giving the same point resolution,
7243
+ * decorator pre/post handling, shadow-root shrink validation and
7244
+ * anchor/focus orientation as a native selection extension would, while
7245
+ * the DOM selection is only ever collapsed.
7246
+ *
7247
+ * When the measurement is not possible — no DOM selection or no
7248
+ * `Selection.modify` (headless environments can polyfill it), or an
7249
+ * unresolvable anchor — the selection is left collapsed, so the deletion
7250
+ * becomes a no-op for that keystroke.
7251
+ */
7252
+ function $extendSelectionForDeletion(selection, isBackward, granularity) {
7253
+ if ($modifySelectionAroundDecoratorsAndBlocks(selection, "extend", isBackward, granularity)) return;
7254
+ const editor = getActiveEditor();
7255
+ const domSelection = getDOMSelection(getWindow(editor));
7256
+ if (!domSelection || typeof domSelection.modify !== "function") return;
7257
+ const blockCursorElement = editor._blockCursorElement;
7258
+ const rootElement = editor._rootElement;
7259
+ const anchor = selection.anchor;
7260
+ const focusNode = selection.focus.getNode();
7261
+ if (rootElement !== null && blockCursorElement !== null && $isElementNode(focusNode) && !focusNode.isInline() && !focusNode.canBeEmpty()) removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
7262
+ const $resolvePointDOM = (point) => {
7263
+ const pointNode = point.getNode();
7264
+ const keyedDOM = editor.getElementByKey(point.key);
7265
+ return keyedDOM !== null && point.type === "text" && $isTextNode(pointNode) ? $getDOMTextNode(pointNode, keyedDOM, editor) : keyedDOM;
7266
+ };
7267
+ const anchorNode = anchor.getNode();
7268
+ const anchorDOM = $resolvePointDOM(anchor);
7269
+ if (anchorDOM === null) return;
7270
+ const anchorOffset = anchor.offset;
7271
+ const wasCollapsed = selection.isCollapsed();
7272
+ const focus = selection.focus;
7273
+ const focusDOM = wasCollapsed ? anchorDOM : $resolvePointDOM(focus);
7274
+ if (focusDOM === null) return;
7275
+ const focusOffset = focus.offset;
7276
+ setDOMSelectionBaseAndExtent(domSelection, focusDOM, focusOffset, focusDOM, focusOffset);
7277
+ moveNativeSelection(domSelection, "move", isBackward ? "backward" : "forward", granularity);
7278
+ if (domSelection.rangeCount === 0) return;
7279
+ const landedRange = getComposedStaticRange(domSelection, rootElement) || domSelection.getRangeAt(0);
7280
+ const landedContainer = landedRange.startContainer;
7281
+ const landedOffset = landedRange.startOffset;
7282
+ if (wasCollapsed && granularity === "character" && anchor.type === "text" && $isTextNode(anchorNode) && anchorNode.isUnmergeable()) {
7283
+ if (anchorOffset === (isBackward ? 0 : anchorNode.getTextContentSize())) {
7284
+ const sibling = $getSiblingCaret(anchorNode, isBackward ? "previous" : "next").getNodeAtCaret();
7285
+ if ($isTextNode(sibling)) {
7286
+ const sibOffset = isBackward ? sibling.getTextContentSize() - 1 : 1;
7287
+ selection.focus.set(sibling.__key, sibOffset, "text");
7288
+ selection.dirty = true;
7289
+ return;
7290
+ }
7291
+ }
7292
+ }
7293
+ if (wasCollapsed && granularity === "character" && anchor.type === "text") {
7294
+ const edgeOffset = isBackward ? 0 : anchorNode.getTextContentSize();
7295
+ const clampedOffset = landedContainer === anchorDOM ? landedOffset : anchorOffset !== edgeOffset ? edgeOffset : -1;
7296
+ if (clampedOffset >= 0) {
7297
+ if (clampedOffset !== anchorOffset) {
7298
+ selection.focus.set(anchor.key, clampedOffset, "text");
7299
+ selection.dirty = true;
7300
+ }
7301
+ return;
7302
+ }
7303
+ }
7304
+ const [startContainer, startOffset, endContainer, endOffset] = isBackward ? [
7305
+ landedContainer,
7306
+ landedOffset,
7307
+ anchorDOM,
7308
+ anchorOffset
7309
+ ] : [
7310
+ anchorDOM,
7311
+ anchorOffset,
7312
+ landedContainer,
7313
+ landedOffset
7314
+ ];
7315
+ const root = $isRootNode(anchorNode) ? anchorNode : $getNearestRootOrShadowRoot(anchorNode);
7316
+ selection.applyDOMRange({
7317
+ collapsed: false,
7318
+ endContainer,
7319
+ endOffset,
7320
+ startContainer,
7321
+ startOffset
7322
+ });
7323
+ selection.dirty = true;
7324
+ if (!$shrinkSelectionToRoot(selection, isBackward, root) && isBackward) $swapPoints(selection);
7325
+ if (granularity === "lineboundary") $modifySelectionAroundDecoratorsAndBlocks(selection, "extend", isBackward, granularity, "decorators");
7326
+ }
7327
+ /**
6983
7328
  * 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.
7329
+ * `$extendSelectionForDeletion` extended the selection further
7330
+ * than a user would expect for that operation.
6986
7331
  *
6987
7332
  * A short(?) JavaScript string vs. Unicode primer:
6988
7333
  *
@@ -7016,7 +7361,7 @@ function moveNativeSelection(domSelection, alter, direction, granularity) {
7016
7361
  * essentially be arbitrarily long, as there are many ways to combine
7017
7362
  * them.
7018
7363
  *
7019
- * The `this.modify(…)` call has already extended our selection by one
7364
+ * The native caret measurement has already extended our selection by one
7020
7365
  * *grapheme* in the direction we want to delete. Sounds great, it's done
7021
7366
  * a lot of awfully tricky work for us because this functionality has only
7022
7367
  * recently become available in JavaScript via `Intl.Segmenter`. The
@@ -7197,7 +7542,7 @@ function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
7197
7542
  const parent = node.getParent();
7198
7543
  if (!isBackward) {
7199
7544
  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");
7545
+ else if ($isTextNode(prevSibling) && !node.isUnmergeable()) point.set(prevSibling.__key, prevSibling.getTextContent().length, "text");
7201
7546
  } else if ((isCollapsed || !isBackward) && prevSibling === null && $isElementNode(parent) && parent.isInline()) {
7202
7547
  const parentSibling = parent.getPreviousSibling();
7203
7548
  if ($isTextNode(parentSibling)) point.set(parentSibling.__key, parentSibling.getTextContent().length, "text");
@@ -7206,7 +7551,7 @@ function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
7206
7551
  const nextSibling = node.getNextSibling();
7207
7552
  const parent = node.getParent();
7208
7553
  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()) {
7554
+ else if ((isCollapsed || isBackward) && nextSibling === null && $isElementNode(parent) && parent.isInline() && !parent.canInsertTextAfter() && parent.getTextContentSize() > 1) {
7210
7555
  const parentSibling = parent.getNextSibling();
7211
7556
  if ($isTextNode(parentSibling)) point.set(parentSibling.__key, 0, "text");
7212
7557
  }
@@ -7565,7 +7910,7 @@ function $updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
7565
7910
  } else if ($isRangeSelection(prevSelection) && prevSelection.anchor.type === "text") anchorFormatOrStyleChanged = true;
7566
7911
  if (focus.type === "text") nextFocusNode = $isTextNode(focusNode) ? $getDOMTextNode(focusNode, focusDOM, editor) : null;
7567
7912
  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());
7913
+ if (isCollapsed && (prevSelection === null || anchorFormatOrStyleChanged || $isRangeSelection(prevSelection) && (prevSelection.format !== nextFormat || prevSelection.style !== nextStyle))) markCollapsedSelectionFormat(editor, nextFormat, nextStyle, nextAnchorOffset, anchor.key, performance.now());
7569
7914
  if (!(domSelection.type === "Range" && isCollapsed) && currentPoints.anchorOffset === nextAnchorOffset && currentPoints.focusOffset === nextFocusOffset && currentPoints.anchorNode === nextAnchorNode && currentPoints.focusNode === nextFocusNode) {
7570
7915
  if (activeElement === null || !rootElement.contains(activeElement)) {
7571
7916
  const focusEditor = activeElement !== null ? getNearestEditorFromDOMNode(activeElement) : null;
@@ -7594,7 +7939,7 @@ function $updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
7594
7939
  scrollIntoViewIfNeeded(editor, selectionRect, rootElement);
7595
7940
  }
7596
7941
  }
7597
- markSelectionChangeFromDOMUpdate();
7942
+ markSelectionChangeFromDOMUpdate(editor);
7598
7943
  }
7599
7944
  function $insertNodes(nodes) {
7600
7945
  let selection = $getSelection() || $getPreviousSelection();
@@ -8072,7 +8417,7 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8072
8417
  const rootElement = editor._rootElement;
8073
8418
  const shouldSkipDOM = editor._headless || rootElement === null;
8074
8419
  if (pendingEditorState === null) {
8075
- if (editor._deferred.length > 0) triggerDeferredUpdateCallbacks(editor, editor._deferred);
8420
+ if (!editor._updating && editor._deferred.length > 0) triggerDeferredUpdateCallbacks(editor, editor._deferred);
8076
8421
  return;
8077
8422
  }
8078
8423
  const currentEditorState = editor._editorState;
@@ -8130,7 +8475,6 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8130
8475
  const dirtyElements = editor._dirtyElements;
8131
8476
  const normalizedNodes = editor._normalizedNodes;
8132
8477
  const tags = editor._updateTags;
8133
- const deferred = editor._deferred;
8134
8478
  if (needsUpdate) {
8135
8479
  editor._dirtyType = NO_DIRTY_NODES;
8136
8480
  editor._cloneNotNeeded.clear();
@@ -8179,7 +8523,10 @@ function $commitPendingUpdatesImpl(editor, recoveryEditorState) {
8179
8523
  prevEditorState: recoveryEditorState || currentEditorState,
8180
8524
  tags
8181
8525
  });
8182
- triggerDeferredUpdateCallbacks(editor, deferred);
8526
+ if (!previouslyUpdating) {
8527
+ const deferred = editor._deferred;
8528
+ triggerDeferredUpdateCallbacks(editor, deferred);
8529
+ }
8183
8530
  $triggerEnqueuedUpdates(editor);
8184
8531
  }
8185
8532
  function triggerTextContentListeners(editor, currentEditorState, pendingEditorState) {
@@ -8500,7 +8847,8 @@ var ElementNode = class extends LexicalNode {
8500
8847
  return this.getLatest().__format;
8501
8848
  }
8502
8849
  getFormatType() {
8503
- return ELEMENT_FORMAT_TO_TYPE[this.getFormat()] || "";
8850
+ const format = this.getFormat();
8851
+ return ELEMENT_FORMAT_TO_TYPE[format] || "";
8504
8852
  }
8505
8853
  getStyle() {
8506
8854
  return this.getLatest().__style;
@@ -9234,7 +9582,7 @@ var ArtificialNode__DO_NOT_USE = class extends ElementNode {
9234
9582
  return "artificial";
9235
9583
  }
9236
9584
  createDOM(config) {
9237
- return document.createElement("div");
9585
+ return $getDocument().createElement("div");
9238
9586
  }
9239
9587
  };
9240
9588
  /**
@@ -9260,7 +9608,7 @@ var LineBreakNode = class LineBreakNode extends LexicalNode {
9260
9608
  return "\n";
9261
9609
  }
9262
9610
  createDOM() {
9263
- return document.createElement("br");
9611
+ return $getDocument().createElement("br");
9264
9612
  }
9265
9613
  updateDOM() {
9266
9614
  return false;
@@ -9347,7 +9695,7 @@ var ParagraphNode = class ParagraphNode extends ElementNode {
9347
9695
  return new ParagraphNode(node.__key);
9348
9696
  }
9349
9697
  createDOM(config) {
9350
- const dom = document.createElement("p");
9698
+ const dom = $getDocument().createElement("p");
9351
9699
  const classNames = getCachedClassNameArray(config.theme, "paragraph");
9352
9700
  if (classNames !== void 0) dom.classList.add(...classNames);
9353
9701
  return dom;
@@ -9364,7 +9712,7 @@ var ParagraphNode = class ParagraphNode extends ElementNode {
9364
9712
  exportDOM(editor) {
9365
9713
  const { element } = super.exportDOM(editor);
9366
9714
  if (isHTMLElement(element)) {
9367
- if (this.isEmpty()) element.append(document.createElement("br"));
9715
+ if (this.isEmpty()) element.append($getDocument().createElement("br"));
9368
9716
  const formatType = this.getFormatType();
9369
9717
  if (formatType) element.style.textAlign = formatType;
9370
9718
  }
@@ -9448,6 +9796,33 @@ function $isParagraphNode(node) {
9448
9796
  * - `'latest'` reads the latest committed state without flushing pending
9449
9797
  * updates, equivalent to `editor.getEditorState().read(callbackFn, {editor})`.
9450
9798
  */
9799
+ /** @internal */
9800
+ /** @internal */
9801
+ /** @internal */
9802
+ function createInputState() {
9803
+ return {
9804
+ collapsedSelectionFormat: {
9805
+ format: 0,
9806
+ key: "root",
9807
+ offset: 0,
9808
+ style: "",
9809
+ timeStamp: 0
9810
+ },
9811
+ compositionEndData: "",
9812
+ compositionPhase: "idle",
9813
+ hadOrphanedCompositionEvents: false,
9814
+ handledSelectionCommandTimeoutId: null,
9815
+ isInsertLineBreak: false,
9816
+ isInsertTextAfterHandledSelectionCommand: false,
9817
+ isSelectionChangeFromDOMUpdate: false,
9818
+ isSelectionChangeFromMouseDown: false,
9819
+ lastBeforeInputInsertTextTimeStamp: 0,
9820
+ lastKeyCode: null,
9821
+ lastKeyDownTimeStamp: 0,
9822
+ postDeleteSelectionToRestore: null,
9823
+ unprocessedBeforeInputData: null
9824
+ };
9825
+ }
9451
9826
  /**
9452
9827
  * Configuration entry passed in {@link CreateEditorArgs.nodes} to substitute
9453
9828
  * a core node class with a custom subclass. The replacement class itself
@@ -9585,6 +9960,8 @@ function resetEditor(editor, prevRootElement, nextRootElement, pendingEditorStat
9585
9960
  editor._cascadeCount = 0;
9586
9961
  }
9587
9962
  editor._blockCursorElement = null;
9963
+ if (editor._inputState.handledSelectionCommandTimeoutId !== null) clearTimeout(editor._inputState.handledSelectionCommandTimeoutId);
9964
+ editor._inputState = createInputState();
9588
9965
  const observer = editor._observer;
9589
9966
  if (observer !== null) {
9590
9967
  observer.disconnect();
@@ -9838,6 +10215,8 @@ var LexicalEditor = class {
9838
10215
  */
9839
10216
  _slotsUsed;
9840
10217
  /** @internal */
10218
+ _inputState;
10219
+ /** @internal */
9841
10220
  _createEditorArgs;
9842
10221
  /** @internal */
9843
10222
  constructor(editorState, parentEditor, nodes, config, onError, onWarn, htmlConversions, editable, createEditorArgs) {
@@ -9881,6 +10260,7 @@ var LexicalEditor = class {
9881
10260
  this._window = null;
9882
10261
  this._blockCursorElement = null;
9883
10262
  this._slotsUsed = false;
10263
+ this._inputState = createInputState();
9884
10264
  }
9885
10265
  /**
9886
10266
  *
@@ -9901,7 +10281,7 @@ var LexicalEditor = class {
9901
10281
  return registerListener(this._listeners.update, listener);
9902
10282
  }
9903
10283
  /**
9904
- * Registers a listener for for when the editor changes between editable and non-editable states.
10284
+ * Registers a listener for when the editor changes between editable and non-editable states.
9905
10285
  * Will trigger the provided callback each time the editor transitions between these states until the
9906
10286
  * teardown function is called.
9907
10287
  *
@@ -10736,6 +11116,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
10736
11116
  let node = textNode;
10737
11117
  if (node.isAttached() && (compositionEnd || !node.isDirty())) {
10738
11118
  const isComposing = node.isComposing();
11119
+ if (node.isToken() && isComposing) return;
10739
11120
  let normalizedTextContent = textContent;
10740
11121
  if (isComposing || compositionEnd) {
10741
11122
  if (textContent.endsWith(COMPOSITION_SUFFIX)) normalizedTextContent = textContent.slice(0, -COMPOSITION_SUFFIX.length);
@@ -10770,7 +11151,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
10770
11151
  const prevTextContentSize = node.getTextContentSize();
10771
11152
  const compositionKey = $getCompositionKey();
10772
11153
  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)) {
11154
+ 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
11155
  node.markDirty();
10775
11156
  return;
10776
11157
  }
@@ -11120,6 +11501,8 @@ function scrollIntoViewIfNeeded(editor, selectionRect, rootElement) {
11120
11501
  const doc = getDOMOwnerDocument(rootElement);
11121
11502
  const defaultView = getDefaultView(doc);
11122
11503
  if (doc === null || defaultView === null) return;
11504
+ const rootRect = rootElement.getBoundingClientRect();
11505
+ if (selectionRect.bottom < rootRect.top) return;
11123
11506
  let { top: currentTop, bottom: currentBottom } = selectionRect;
11124
11507
  let targetTop = 0;
11125
11508
  let targetBottom = 0;
@@ -11142,7 +11525,7 @@ function scrollIntoViewIfNeeded(editor, selectionRect, rootElement) {
11142
11525
  if (isFinite(scrollPaddingTop)) targetTop += scrollPaddingTop;
11143
11526
  if (isFinite(scrollPaddingBottom)) targetBottom -= scrollPaddingBottom;
11144
11527
  } else {
11145
- const targetRect = element.getBoundingClientRect();
11528
+ const targetRect = element === rootElement ? rootRect : element.getBoundingClientRect();
11146
11529
  targetTop = targetRect.top;
11147
11530
  targetBottom = targetRect.bottom;
11148
11531
  }
@@ -11219,8 +11602,11 @@ function $getNearestRootOrShadowRoot(node) {
11219
11602
  }
11220
11603
  return current;
11221
11604
  }
11605
+ function $isShadowRootNode(node) {
11606
+ return $isElementNode(node) && node.isShadowRoot();
11607
+ }
11222
11608
  function $isRootOrShadowRoot(node) {
11223
- return $isRootNode(node) || $isElementNode(node) && node.isShadowRoot();
11609
+ return $isRootNode(node) || $isShadowRootNode(node);
11224
11610
  }
11225
11611
  /**
11226
11612
  * Returns a shallow clone of node with a new key. All properties of the node
@@ -11275,9 +11661,9 @@ function $getNodeByKeyOrThrow(key) {
11275
11661
  if (node === null) formatDevErrorMessage(`Expected node with key ${key} to exist but it's not in the nodeMap.`);
11276
11662
  return node;
11277
11663
  }
11278
- function createBlockCursorElement(editorConfig) {
11664
+ function $createBlockCursorElement(editorConfig) {
11279
11665
  const theme = editorConfig.theme;
11280
- const element = document.createElement("div");
11666
+ const element = $getDocument().createElement("div");
11281
11667
  element.contentEditable = "false";
11282
11668
  element.setAttribute("data-lexical-cursor", "true");
11283
11669
  let blockCursorTheme = theme.blockCursor;
@@ -11287,8 +11673,24 @@ function createBlockCursorElement(editorConfig) {
11287
11673
  }
11288
11674
  return element;
11289
11675
  }
11290
- function needsBlockCursor(node) {
11291
- return ($isDecoratorNode(node) || $isElementNode(node) && !node.canBeEmpty()) && !node.isInline();
11676
+ /**
11677
+ * Returns true if the given node needs a block cursor given an adjacent selection,
11678
+ * the node must be non-inline and one of:
11679
+ * - DecoratorNode
11680
+ * - ShadowRootNode with a parent that is not also a ShadowRootNode
11681
+ * - An ElementNode that can't be empty
11682
+ */
11683
+ function $needsBlockCursorBeside(node) {
11684
+ if (!node || node.isInline()) return false;
11685
+ if ($isDecoratorNode(node)) return true;
11686
+ if ($isElementNode(node)) {
11687
+ if (node.isShadowRoot()) {
11688
+ const parent = node.getParent();
11689
+ return !($isElementNode(parent) && parent.isShadowRoot());
11690
+ }
11691
+ return !node.canBeEmpty();
11692
+ }
11693
+ return false;
11292
11694
  }
11293
11695
  function removeDOMBlockCursorElement(blockCursorElement, editor, rootElement) {
11294
11696
  rootElement.style.removeProperty("caret-color");
@@ -11306,12 +11708,12 @@ function $updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
11306
11708
  let isBlockCursor = false;
11307
11709
  let insertBeforeElement = null;
11308
11710
  if (offset === elementNodeSize) {
11309
- if (needsBlockCursor(elementNode.getChildAtIndex(offset - 1))) isBlockCursor = true;
11711
+ if ($needsBlockCursorBeside(elementNode.getChildAtIndex(offset - 1))) isBlockCursor = true;
11310
11712
  } else {
11311
11713
  const child = elementNode.getChildAtIndex(offset);
11312
- if (child !== null && needsBlockCursor(child)) {
11714
+ if (child !== null && $needsBlockCursorBeside(child)) {
11313
11715
  const sibling = child.getPreviousSibling();
11314
- if (sibling === null || needsBlockCursor(sibling)) {
11716
+ if (sibling === null || $needsBlockCursorBeside(sibling)) {
11315
11717
  isBlockCursor = true;
11316
11718
  insertBeforeElement = editor.getElementByKey(child.__key);
11317
11719
  }
@@ -11319,7 +11721,7 @@ function $updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
11319
11721
  }
11320
11722
  if (isBlockCursor) {
11321
11723
  const elementDOM = $getDOMSlot(elementNode, editor.getElementByKey(elementNode.__key), editor).element;
11322
- if (blockCursorElement === null) editor._blockCursorElement = blockCursorElement = createBlockCursorElement(editor._config);
11724
+ if (blockCursorElement === null) editor._blockCursorElement = blockCursorElement = $createBlockCursorElement(editor._config);
11323
11725
  rootElement.style.caretColor = "transparent";
11324
11726
  if (insertBeforeElement === null) elementDOM.appendChild(blockCursorElement);
11325
11727
  else elementDOM.insertBefore(blockCursorElement, insertBeforeElement);
@@ -11414,6 +11816,28 @@ function getRootOwnerDocument(rootElement) {
11414
11816
  return rootElement !== null ? rootElement.ownerDocument : document;
11415
11817
  }
11416
11818
  /**
11819
+ * Returns the {@link Document} that owns the active editor's root element.
11820
+ * Falls back to `globalThis.document` when there is no active editor (e.g.
11821
+ * a node method such as `createDOM` / `exportDOM` is invoked headlessly,
11822
+ * outside of `editor.update()` / `editor.read()`), or when the active
11823
+ * editor has no root element (e.g. headless mode with
11824
+ * {@link @lexical/headless!withDOM | withDOM}).
11825
+ *
11826
+ * Use this inside `createDOM`, `updateDOM`, and `exportDOM` instead of the
11827
+ * bare `document` global so the node works correctly when the editor lives
11828
+ * inside a Shadow DOM or a cross-origin `<iframe>`.
11829
+ *
11830
+ * Unlike most `$`-prefixed helpers, this does NOT require an ambient active
11831
+ * editor: it must remain callable from `createDOM` / `exportDOM`, which are
11832
+ * public methods that consumers may legitimately call while serializing
11833
+ * nodes headlessly. Throwing here would silently break every node whose DOM
11834
+ * methods were migrated off the bare `document` global.
11835
+ */
11836
+ function $getDocument() {
11837
+ const editor = internalGetActiveEditor();
11838
+ return getRootOwnerDocument(editor !== null ? editor._rootElement : null);
11839
+ }
11840
+ /**
11417
11841
  * A subset of `Selection` covering the four boundary-point fields Lexical
11418
11842
  * reads plus `direction`. Designed so a `Selection` instance can be returned
11419
11843
  * where a `DOMSelectionBoundaryPoints` is expected (see {@link getDOMSelectionPoints}).
@@ -13495,6 +13919,21 @@ function $removeTextFromCaretRange(initialRange, sliceMode = "removeEmptySlices"
13495
13919
  parent = parent.getParent();
13496
13920
  element.remove(true);
13497
13921
  }
13922
+ } else if (focusCandidate) {
13923
+ const focusBlock = $getBlockFromCaret(focusCandidate);
13924
+ const focusBlockParent = focusBlock && focusBlock.getParent();
13925
+ const topmostShadowRoot = focusBlock && focusBlock.getParents().findLast($isShadowRootNode);
13926
+ if (focusBlock && focusBlockParent && !$isRootNode(focusBlockParent) && focusBlock.isEmpty() && seenStart.has(focusBlock.getKey()) && $getSlotNames(focusBlock).length === 0 && (!topmostShadowRoot || seenStart.has(topmostShadowRoot.getKey()))) {
13927
+ focusBlock.remove(true);
13928
+ let parent = focusBlockParent;
13929
+ while (parent && !$isRootNode(parent) && parent.isEmpty()) {
13930
+ const grandparent = parent.getParent();
13931
+ if (grandparent && $isRootNode(grandparent) && grandparent.getChildrenSize() <= 1) break;
13932
+ const element = parent;
13933
+ parent = grandparent;
13934
+ element.remove(true);
13935
+ }
13936
+ }
13498
13937
  }
13499
13938
  const bestCandidate = [
13500
13939
  anchorCandidate,
@@ -13505,6 +13944,16 @@ function $removeTextFromCaretRange(initialRange, sliceMode = "removeEmptySlices"
13505
13944
  if (bestCandidate) return $getCollapsedCaretRange($getCaretInDirection($normalizeCaret(bestCandidate), initialRange.direction));
13506
13945
  formatDevErrorMessage(`$removeTextFromCaretRange: selection was lost, could not find a new anchor given candidates with keys: ${JSON.stringify(anchorCandidates.map((n) => n.origin.__key))}`);
13507
13946
  }
13947
+ function $getBlockFromCaret(caret) {
13948
+ if ($isChildCaret(caret)) {
13949
+ const origin = caret.origin;
13950
+ if (INTERNAL_$isBlock(origin)) return origin;
13951
+ } else {
13952
+ const parent = caret.getParentAtCaret();
13953
+ if (parent && INTERNAL_$isBlock(parent)) return parent;
13954
+ }
13955
+ return null;
13956
+ }
13508
13957
  /**
13509
13958
  * Determine if the two caret origins are in distinct blocks that
13510
13959
  * should be merged.
@@ -13952,5 +14401,55 @@ function mergeRegister(...func) {
13952
14401
  func.length = 0;
13953
14402
  };
13954
14403
  }
14404
+ /**
14405
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14406
+ *
14407
+ * This source code is licensed under the MIT license found in the
14408
+ * LICENSE file in the root directory of this source tree.
14409
+ *
14410
+ */
14411
+ /**
14412
+ * A map of event type to listener for a given {@link EventTarget}. Each
14413
+ * listener's event argument is inferred from the event type, e.g. for an
14414
+ * `HTMLElement` the `'keydown'` listener receives a `KeyboardEvent`.
14415
+ */
14416
+ /**
14417
+ * Add several event listeners to a single `target` and return one function
14418
+ * that removes all of them.
14419
+ *
14420
+ * This is the batch form of {@link registerEventListener}: it takes a
14421
+ * `{type: listener}` object (strongly typed per event type) and shares one
14422
+ * `options` value across every listener. The returned dispose function removes
14423
+ * the listeners in reverse registration order (via {@link mergeRegister}).
14424
+ *
14425
+ * Because `options` is shared, register listeners that need a different
14426
+ * `options` value (e.g. a different `capture` flag) with a separate call and
14427
+ * combine the results with `mergeRegister`.
14428
+ *
14429
+ * @example
14430
+ * ```ts
14431
+ * // All five listeners share {capture: true}
14432
+ * return registerEventListeners(
14433
+ * window,
14434
+ * {
14435
+ * beforeinput: report,
14436
+ * cut: report,
14437
+ * keydown: report,
14438
+ * paste: report,
14439
+ * selectionchange: report,
14440
+ * },
14441
+ * {capture: true},
14442
+ * );
14443
+ * ```
14444
+ *
14445
+ * @param target - The {@link EventTarget} to subscribe to
14446
+ * @param listeners - A map of event type to listener
14447
+ * @param options - Options forwarded to `add`/`removeEventListener` for every
14448
+ * listener
14449
+ * @returns A function that removes every listener when called
14450
+ */
14451
+ function registerEventListeners(target, listeners, options) {
14452
+ return mergeRegister(...Object.entries(listeners).map(([type, listener]) => registerEventListener(target, type, listener, options)));
14453
+ }
13955
14454
  //#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 };
14455
+ 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 };