lexical 0.25.1-nightly.20250228.0 → 0.26.1-nightly.20250303.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Lexical.dev.js CHANGED
@@ -8,7 +8,19 @@
8
8
 
9
9
  'use strict';
10
10
 
11
- var lexical = require('lexical');
11
+ /**
12
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
13
+ *
14
+ * This source code is licensed under the MIT license found in the
15
+ * LICENSE file in the root directory of this source tree.
16
+ *
17
+ */
18
+
19
+ // Do not require this module directly! Use normal `invariant` calls.
20
+
21
+ function formatDevErrorMessage(message) {
22
+ throw new Error(message);
23
+ }
12
24
 
13
25
  /**
14
26
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -459,14 +471,6 @@ function initMutationObserver(editor) {
459
471
  });
460
472
  }
461
473
 
462
- /**
463
- * Copyright (c) Meta Platforms, Inc. and affiliates.
464
- *
465
- * This source code is licensed under the MIT license found in the
466
- * LICENSE file in the root directory of this source tree.
467
- *
468
- */
469
-
470
474
  function coerceToJSON(v) {
471
475
  return v;
472
476
  }
@@ -652,7 +656,7 @@ function $checkCollision(node, stateConfig, state) {
652
656
  const collision = state.sharedConfigMap.get(stateConfig.key);
653
657
  if (collision !== undefined && collision !== stateConfig) {
654
658
  {
655
- throw Error(`$setState: State key collision ${JSON.stringify(stateConfig.key)} detected in ${node.constructor.name} node with type ${node.getType()} and key ${node.getKey()}. Only one StateConfig with a given key should be used on a node.`);
659
+ formatDevErrorMessage(`$setState: State key collision ${JSON.stringify(stateConfig.key)} detected in ${node.constructor.name} node with type ${node.getType()} and key ${node.getKey()}. Only one StateConfig with a given key should be used on a node.`);
656
660
  }
657
661
  }
658
662
  }
@@ -768,11 +772,11 @@ class NodeState {
768
772
  const computedSize = size !== undefined ? size : computeSize(sharedConfigMap, unknownState, knownState);
769
773
  {
770
774
  if (!(size === undefined || computedSize === size)) {
771
- throw Error(`NodeState: size != computedSize (${String(size)} != ${String(computedSize)})`);
775
+ formatDevErrorMessage(`NodeState: size != computedSize (${String(size)} != ${String(computedSize)})`);
772
776
  }
773
777
  for (const stateConfig of knownState.keys()) {
774
778
  if (!sharedConfigMap.has(stateConfig.key)) {
775
- throw Error(`NodeState: sharedConfigMap missing knownState key ${stateConfig.key}`);
779
+ formatDevErrorMessage(`NodeState: sharedConfigMap missing knownState key ${stateConfig.key}`);
776
780
  }
777
781
  }
778
782
  }
@@ -1141,14 +1145,6 @@ function $normalizePoint(point) {
1141
1145
  }
1142
1146
  }
1143
1147
 
1144
- /**
1145
- * Copyright (c) Meta Platforms, Inc. and affiliates.
1146
- *
1147
- * This source code is licensed under the MIT license found in the
1148
- * LICENSE file in the root directory of this source tree.
1149
- *
1150
- */
1151
-
1152
1148
  let subTreeTextContent = '';
1153
1149
  let subTreeDirectionedTextContent = '';
1154
1150
  let subTreeTextFormat = null;
@@ -1237,7 +1233,7 @@ function $createNode(key, slot) {
1237
1233
  const node = activeNextNodeMap.get(key);
1238
1234
  if (node === undefined) {
1239
1235
  {
1240
- throw Error(`createNode: node does not exist in nodeMap`);
1236
+ formatDevErrorMessage(`createNode: node does not exist in nodeMap`);
1241
1237
  }
1242
1238
  }
1243
1239
  const dom = node.createDOM(activeEditorConfig, activeEditor$1);
@@ -1431,7 +1427,7 @@ function createChildrenArray(element, nodeMap) {
1431
1427
  const node = nodeMap.get(nodeKey);
1432
1428
  if (node === undefined) {
1433
1429
  {
1434
- throw Error(`createChildrenArray: node does not exist in nodeMap`);
1430
+ formatDevErrorMessage(`createChildrenArray: node does not exist in nodeMap`);
1435
1431
  }
1436
1432
  }
1437
1433
  children.push(nodeKey);
@@ -1478,10 +1474,10 @@ function $reconcileChildren(prevElement, nextElement, slot) {
1478
1474
  const prevChildren = createChildrenArray(prevElement, activePrevNodeMap);
1479
1475
  const nextChildren = createChildrenArray(nextElement, activeNextNodeMap);
1480
1476
  if (!(prevChildren.length === prevChildrenSize)) {
1481
- throw Error(`$reconcileChildren: prevChildren.length !== prevChildrenSize`);
1477
+ formatDevErrorMessage(`$reconcileChildren: prevChildren.length !== prevChildrenSize`);
1482
1478
  }
1483
1479
  if (!(nextChildren.length === nextChildrenSize)) {
1484
- throw Error(`$reconcileChildren: nextChildren.length !== nextChildrenSize`);
1480
+ formatDevErrorMessage(`$reconcileChildren: nextChildren.length !== nextChildrenSize`);
1485
1481
  }
1486
1482
  if (prevChildrenSize === 0) {
1487
1483
  if (nextChildrenSize !== 0) {
@@ -1511,7 +1507,7 @@ function $reconcileNode(key, parentDOM) {
1511
1507
  let nextNode = activeNextNodeMap.get(key);
1512
1508
  if (prevNode === undefined || nextNode === undefined) {
1513
1509
  {
1514
- throw Error(`reconcileNode: prevNode or nextNode does not exist in nodeMap`);
1510
+ formatDevErrorMessage(`reconcileNode: prevNode or nextNode does not exist in nodeMap`);
1515
1511
  }
1516
1512
  }
1517
1513
  const isDirty = treatAllNodesAsDirty || activeDirtyLeaves.has(key) || activeDirtyElements.has(key);
@@ -1552,7 +1548,7 @@ function $reconcileNode(key, parentDOM) {
1552
1548
  const replacementDOM = $createNode(key, null);
1553
1549
  if (parentDOM === null) {
1554
1550
  {
1555
- throw Error(`reconcileNode: parentDOM is null`);
1551
+ formatDevErrorMessage(`reconcileNode: parentDOM is null`);
1556
1552
  }
1557
1553
  }
1558
1554
  parentDOM.replaceChild(replacementDOM, dom);
@@ -1747,7 +1743,7 @@ function getPrevElementByKeyOrThrow(key) {
1747
1743
  const element = activePrevKeyToDOMMap.get(key);
1748
1744
  if (element === undefined) {
1749
1745
  {
1750
- throw Error(`Reconciliation: could not find DOM element for node key ${key}`);
1746
+ formatDevErrorMessage(`Reconciliation: could not find DOM element for node key ${key}`);
1751
1747
  }
1752
1748
  }
1753
1749
  return element;
@@ -1812,14 +1808,6 @@ const FOCUS_COMMAND = createCommand('FOCUS_COMMAND');
1812
1808
  const BLUR_COMMAND = createCommand('BLUR_COMMAND');
1813
1809
  const KEY_MODIFIER_COMMAND = createCommand('KEY_MODIFIER_COMMAND');
1814
1810
 
1815
- /**
1816
- * Copyright (c) Meta Platforms, Inc. and affiliates.
1817
- *
1818
- * This source code is licensed under the MIT license found in the
1819
- * LICENSE file in the root directory of this source tree.
1820
- *
1821
- */
1822
-
1823
1811
  const PASS_THROUGH_COMMAND = Object.freeze({});
1824
1812
  const ANDROID_COMPOSITION_LATENCY = 30;
1825
1813
  const rootElementEvents = [['keydown', onKeyDown], ['pointerdown', onPointerDown], ['compositionstart', onCompositionStart], ['compositionend', onCompositionEnd], ['input', onInput], ['click', onClick], ['cut', PASS_THROUGH_COMMAND], ['copy', PASS_THROUGH_COMMAND], ['dragstart', PASS_THROUGH_COMMAND], ['dragover', PASS_THROUGH_COMMAND], ['dragend', PASS_THROUGH_COMMAND], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
@@ -1837,6 +1825,7 @@ let isInsertLineBreak = false;
1837
1825
  let isFirefoxEndingComposition = false;
1838
1826
  let isSafariEndingComposition = false;
1839
1827
  let safariEndCompositionEventData = '';
1828
+ let postDeleteSelectionToRestore = null;
1840
1829
  let collapsedSelectionFormat = [0, '', 0, 'root', 0];
1841
1830
 
1842
1831
  // This function is used to determine if Lexical should attempt to override
@@ -1905,7 +1894,7 @@ function onSelectionChange(domSelection, editor, isActive) {
1905
1894
  // We also need to check if the offset is at the boundary,
1906
1895
  // because in this case, we might need to normalize to a
1907
1896
  // sibling instead.
1908
- if (shouldSkipSelectionChange(anchorDOM, anchorOffset) && shouldSkipSelectionChange(focusDOM, focusOffset)) {
1897
+ if (shouldSkipSelectionChange(anchorDOM, anchorOffset) && shouldSkipSelectionChange(focusDOM, focusOffset) && !postDeleteSelectionToRestore) {
1909
1898
  return;
1910
1899
  }
1911
1900
  }
@@ -1919,7 +1908,23 @@ function onSelectionChange(domSelection, editor, isActive) {
1919
1908
  if (!isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
1920
1909
  return;
1921
1910
  }
1922
- const selection = $getSelection();
1911
+ let selection = $getSelection();
1912
+
1913
+ // Restore selection in the event of incorrect rightward shift after deletion
1914
+ if (postDeleteSelectionToRestore && $isRangeSelection(selection) && selection.isCollapsed()) {
1915
+ const curAnchor = selection.anchor;
1916
+ const prevAnchor = postDeleteSelectionToRestore.anchor;
1917
+ if (
1918
+ // Rightward shift in same node
1919
+ curAnchor.key === prevAnchor.key && curAnchor.offset === prevAnchor.offset + 1 ||
1920
+ // Or rightward shift into sibling node
1921
+ curAnchor.offset === 1 && prevAnchor.getNode().is(curAnchor.getNode().getPreviousSibling())) {
1922
+ // Restore selection
1923
+ selection = postDeleteSelectionToRestore.clone();
1924
+ $setSelection(selection);
1925
+ }
1926
+ }
1927
+ postDeleteSelectionToRestore = null;
1923
1928
 
1924
1929
  // Update the selection format
1925
1930
  if ($isRangeSelection(selection)) {
@@ -1944,12 +1949,12 @@ function onSelectionChange(domSelection, editor, isActive) {
1944
1949
  } else {
1945
1950
  if (anchor.type === 'text') {
1946
1951
  if (!$isTextNode(anchorNode)) {
1947
- throw Error(`Point.getNode() must return TextNode when type is text`);
1952
+ formatDevErrorMessage(`Point.getNode() must return TextNode when type is text`);
1948
1953
  }
1949
1954
  $updateSelectionFormatStyleFromTextNode(selection, anchorNode);
1950
1955
  } else if (anchor.type === 'element' && !isRootTextContentEmpty) {
1951
1956
  if (!$isElementNode(anchorNode)) {
1952
- throw Error(`Point.getNode() must return ElementNode when type is element`);
1957
+ formatDevErrorMessage(`Point.getNode() must return ElementNode when type is element`);
1953
1958
  }
1954
1959
  const lastNode = anchor.getNode();
1955
1960
  if (
@@ -2132,7 +2137,7 @@ function onBeforeInput(event, editor) {
2132
2137
  const anchorNode = selection.anchor.getNode();
2133
2138
  anchorNode.markDirty();
2134
2139
  if (!$isTextNode(anchorNode)) {
2135
- throw Error(`Anchor node must be a TextNode`);
2140
+ formatDevErrorMessage(`Anchor node must be a TextNode`);
2136
2141
  }
2137
2142
  $updateSelectionFormatStyleFromTextNode(selection, anchorNode);
2138
2143
  }
@@ -2156,6 +2161,14 @@ function onBeforeInput(event, editor) {
2156
2161
  }
2157
2162
  if (!shouldLetBrowserHandleDelete) {
2158
2163
  dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
2164
+ // When deleting across paragraphs, Chrome on Android incorrectly shifts the selection rightwards
2165
+ // We save the correct selection to restore later during handling of selectionchange event
2166
+ const selectionAfterDelete = $getSelection();
2167
+ if (IS_ANDROID_CHROME && $isRangeSelection(selectionAfterDelete) && selectionAfterDelete.isCollapsed()) {
2168
+ postDeleteSelectionToRestore = selectionAfterDelete;
2169
+ // Cleanup in case selectionchange does not fire
2170
+ setTimeout(() => postDeleteSelectionToRestore = null);
2171
+ }
2159
2172
  }
2160
2173
  }
2161
2174
  return;
@@ -2729,12 +2742,12 @@ function removeRootElementEvents(rootElement) {
2729
2742
  const doc = rootElement.ownerDocument;
2730
2743
  const documentRootElementsCount = rootElementsRegistered.get(doc);
2731
2744
  if (!(documentRootElementsCount !== undefined)) {
2732
- throw Error(`Root element not registered`);
2745
+ formatDevErrorMessage(`Root element not registered`);
2733
2746
  } // We only want to have a single global selectionchange event handler, shared
2734
2747
  // between all editor instances.
2735
2748
  const newCount = documentRootElementsCount - 1;
2736
2749
  if (!(newCount >= 0)) {
2737
- throw Error(`Root element count less than 0`);
2750
+ formatDevErrorMessage(`Root element count less than 0`);
2738
2751
  }
2739
2752
  rootElementsRegistered.set(doc, newCount);
2740
2753
  if (newCount === 0) {
@@ -2747,7 +2760,7 @@ function removeRootElementEvents(rootElement) {
2747
2760
  rootElement.__lexicalEditor = null;
2748
2761
  } else if (editor) {
2749
2762
  {
2750
- throw Error(`Attempted to remove event handlers from a node that does not belong to this build of Lexical`);
2763
+ formatDevErrorMessage(`Attempted to remove event handlers from a node that does not belong to this build of Lexical`);
2751
2764
  }
2752
2765
  }
2753
2766
  const removeHandles = getRootElementRemoveHandles(rootElement);
@@ -2779,15 +2792,6 @@ function markCollapsedSelectionFormat(format, style, offset, key, timeStamp) {
2779
2792
  collapsedSelectionFormat = [format, style, offset, key, timeStamp];
2780
2793
  }
2781
2794
 
2782
- /**
2783
- * Copyright (c) Meta Platforms, Inc. and affiliates.
2784
- *
2785
- * This source code is licensed under the MIT license found in the
2786
- * LICENSE file in the root directory of this source tree.
2787
- *
2788
- */
2789
-
2790
-
2791
2795
  /**
2792
2796
  * The base type for all serialized nodes
2793
2797
  */
@@ -2865,7 +2869,7 @@ class LexicalNode {
2865
2869
  */
2866
2870
  static getType() {
2867
2871
  {
2868
- throw Error(`LexicalNode: Node ${this.name} does not implement .getType().`);
2872
+ formatDevErrorMessage(`LexicalNode: Node ${this.name} does not implement .getType().`);
2869
2873
  }
2870
2874
  }
2871
2875
 
@@ -2877,7 +2881,7 @@ class LexicalNode {
2877
2881
  */
2878
2882
  static clone(_data) {
2879
2883
  {
2880
- throw Error(`LexicalNode: Node ${this.name} does not implement .clone().`);
2884
+ formatDevErrorMessage(`LexicalNode: Node ${this.name} does not implement .clone().`);
2881
2885
  }
2882
2886
  }
2883
2887
 
@@ -2969,7 +2973,7 @@ class LexicalNode {
2969
2973
  }
2970
2974
  isInline() {
2971
2975
  {
2972
- throw Error(`LexicalNode: Node ${this.constructor.name} does not implement .isInline().`);
2976
+ formatDevErrorMessage(`LexicalNode: Node ${this.constructor.name} does not implement .isInline().`);
2973
2977
  }
2974
2978
  }
2975
2979
 
@@ -3073,7 +3077,7 @@ class LexicalNode {
3073
3077
  const parent = this.getParent();
3074
3078
  if (parent === null) {
3075
3079
  {
3076
- throw Error(`Expected node ${this.__key} to have a parent.`);
3080
+ formatDevErrorMessage(`Expected node ${this.__key} to have a parent.`);
3077
3081
  }
3078
3082
  }
3079
3083
  return parent;
@@ -3090,7 +3094,7 @@ class LexicalNode {
3090
3094
  const parent = node.getParent();
3091
3095
  if ($isRootOrShadowRoot(parent)) {
3092
3096
  if (!($isElementNode(node) || node === this && $isDecoratorNode(node))) {
3093
- throw Error(`Children of root nodes must be elements or decorators`);
3097
+ formatDevErrorMessage(`Children of root nodes must be elements or decorators`);
3094
3098
  }
3095
3099
  return node;
3096
3100
  }
@@ -3108,7 +3112,7 @@ class LexicalNode {
3108
3112
  const parent = this.getTopLevelElement();
3109
3113
  if (parent === null) {
3110
3114
  {
3111
- throw Error(`Expected node ${this.__key} to have a top parent element.`);
3115
+ formatDevErrorMessage(`Expected node ${this.__key} to have a top parent element.`);
3112
3116
  }
3113
3117
  }
3114
3118
  return parent;
@@ -3252,7 +3256,7 @@ class LexicalNode {
3252
3256
  return $getCommonAncestorResultBranchOrder(compare) === -1;
3253
3257
  }
3254
3258
  if (!(compare.type === 'same' || compare.type === 'ancestor')) {
3255
- throw Error(`LexicalNode.isBefore: exhaustiveness check`);
3259
+ formatDevErrorMessage(`LexicalNode.isBefore: exhaustiveness check`);
3256
3260
  }
3257
3261
  return false;
3258
3262
  }
@@ -3313,7 +3317,7 @@ class LexicalNode {
3313
3317
  do {
3314
3318
  if (ancestor === null) {
3315
3319
  {
3316
- throw Error(`getNodesBetween: ancestor is null`);
3320
+ formatDevErrorMessage(`getNodesBetween: ancestor is null`);
3317
3321
  }
3318
3322
  }
3319
3323
  parentSibling = isBefore ? ancestor.getNextSibling() : ancestor.getPreviousSibling();
@@ -3353,7 +3357,7 @@ class LexicalNode {
3353
3357
  const latest = $getNodeByKey(this.__key);
3354
3358
  if (latest === null) {
3355
3359
  {
3356
- throw Error(`Lexical node does not exist in active editor state. Avoid using the same node references between nested closures from editorState.read/editor.update.`);
3360
+ formatDevErrorMessage(`Lexical node does not exist in active editor state. Avoid using the same node references between nested closures from editorState.read/editor.update.`);
3357
3361
  }
3358
3362
  }
3359
3363
  return latest;
@@ -3425,7 +3429,7 @@ class LexicalNode {
3425
3429
  * */
3426
3430
  createDOM(_config, _editor) {
3427
3431
  {
3428
- throw Error(`createDOM: base method not extended`);
3432
+ formatDevErrorMessage(`createDOM: base method not extended`);
3429
3433
  }
3430
3434
  }
3431
3435
 
@@ -3441,7 +3445,7 @@ class LexicalNode {
3441
3445
  * */
3442
3446
  updateDOM(_prevNode, _dom, _config) {
3443
3447
  {
3444
- throw Error(`updateDOM: base method not extended`);
3448
+ formatDevErrorMessage(`updateDOM: base method not extended`);
3445
3449
  }
3446
3450
  }
3447
3451
 
@@ -3486,7 +3490,7 @@ class LexicalNode {
3486
3490
  * */
3487
3491
  static importJSON(_serializedNode) {
3488
3492
  {
3489
- throw Error(`LexicalNode: Node ${this.name} does not implement .importJSON().`);
3493
+ formatDevErrorMessage(`LexicalNode: Node ${this.name} does not implement .importJSON().`);
3490
3494
  }
3491
3495
  }
3492
3496
 
@@ -3519,7 +3523,7 @@ class LexicalNode {
3519
3523
  * ```
3520
3524
  **/
3521
3525
  updateFromJSON(serializedNode) {
3522
- return $updateStateFromJSON(this, serializedNode[lexical.NODE_STATE_KEY]);
3526
+ return $updateStateFromJSON(this, serializedNode[NODE_STATE_KEY]);
3523
3527
  }
3524
3528
 
3525
3529
  /**
@@ -3594,7 +3598,7 @@ class LexicalNode {
3594
3598
  writableParent.__size = size;
3595
3599
  if (includeChildren) {
3596
3600
  if (!($isElementNode(this) && $isElementNode(writableReplaceWith))) {
3597
- throw Error(`includeChildren should only be true for ElementNodes`);
3601
+ formatDevErrorMessage(`includeChildren should only be true for ElementNodes`);
3598
3602
  }
3599
3603
  this.getChildren().forEach(child => {
3600
3604
  writableReplaceWith.append(child);
@@ -3804,13 +3808,13 @@ function errorOnTypeKlassMismatch(type, klass) {
3804
3808
  // Common error - split in its own invariant
3805
3809
  if (registeredNode === undefined) {
3806
3810
  {
3807
- throw Error(`Create node: Attempted to create node ${klass.name} that was not configured to be used on the editor.`);
3811
+ formatDevErrorMessage(`Create node: Attempted to create node ${klass.name} that was not configured to be used on the editor.`);
3808
3812
  }
3809
3813
  }
3810
3814
  const editorKlass = registeredNode.klass;
3811
3815
  if (editorKlass !== klass) {
3812
3816
  {
3813
- throw Error(`Create node: Type ${type} in node ${klass.name} does not match registered node ${editorKlass.name} with the same type`);
3817
+ formatDevErrorMessage(`Create node: Type ${type} in node ${klass.name} does not match registered node ${editorKlass.name} with the same type`);
3814
3818
  }
3815
3819
  }
3816
3820
  }
@@ -3829,7 +3833,7 @@ function insertRangeAfter(node, firstToInsert, lastToInsert) {
3829
3833
  while (current !== lastToInsert2) {
3830
3834
  if (!current.getNextSibling()) {
3831
3835
  {
3832
- throw Error(`insertRangeAfter: lastToInsert must be a later sibling of firstToInsert`);
3836
+ formatDevErrorMessage(`insertRangeAfter: lastToInsert must be a later sibling of firstToInsert`);
3833
3837
  }
3834
3838
  }
3835
3839
  current = current.getNextSibling();
@@ -3869,6 +3873,9 @@ class LineBreakNode extends LexicalNode {
3869
3873
  updateDOM() {
3870
3874
  return false;
3871
3875
  }
3876
+ isInline() {
3877
+ return true;
3878
+ }
3872
3879
  static importDOM() {
3873
3880
  return {
3874
3881
  br: node => {
@@ -3931,14 +3938,6 @@ function isWhitespaceDomTextNode(node) {
3931
3938
  return isDOMTextNode(node) && /^( |\t|\r?\n)+$/.test(node.textContent || '');
3932
3939
  }
3933
3940
 
3934
- /**
3935
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3936
- *
3937
- * This source code is licensed under the MIT license found in the
3938
- * LICENSE file in the root directory of this source tree.
3939
- *
3940
- */
3941
-
3942
3941
  function getElementOuterTag(node, format) {
3943
3942
  if (format & IS_CODE) {
3944
3943
  return 'code';
@@ -4246,6 +4245,13 @@ class TextNode extends LexicalNode {
4246
4245
  return true;
4247
4246
  }
4248
4247
 
4248
+ /**
4249
+ * @returns true if the text node is inline, false otherwise.
4250
+ */
4251
+ isInline() {
4252
+ return true;
4253
+ }
4254
+
4249
4255
  // View
4250
4256
 
4251
4257
  createDOM(config, editor) {
@@ -4288,7 +4294,7 @@ class TextNode extends LexicalNode {
4288
4294
  const prevInnerDOM = dom.firstChild;
4289
4295
  if (prevInnerDOM == null) {
4290
4296
  {
4291
- throw Error(`updateDOM: prevInnerDOM is null or undefined`);
4297
+ formatDevErrorMessage(`updateDOM: prevInnerDOM is null or undefined`);
4292
4298
  }
4293
4299
  }
4294
4300
  const nextInnerDOM = document.createElement(nextInnerTag);
@@ -4302,7 +4308,7 @@ class TextNode extends LexicalNode {
4302
4308
  innerDOM = dom.firstChild;
4303
4309
  if (innerDOM == null) {
4304
4310
  {
4305
- throw Error(`updateDOM: innerDOM is null or undefined`);
4311
+ formatDevErrorMessage(`updateDOM: innerDOM is null or undefined`);
4306
4312
  }
4307
4313
  }
4308
4314
  }
@@ -4388,7 +4394,7 @@ class TextNode extends LexicalNode {
4388
4394
  element
4389
4395
  } = super.exportDOM(editor);
4390
4396
  if (!isHTMLElement(element)) {
4391
- throw Error(`Expected TextNode createDOM to always return a HTMLElement`);
4397
+ formatDevErrorMessage(`Expected TextNode createDOM to always return a HTMLElement`);
4392
4398
  }
4393
4399
  element.style.whiteSpace = 'pre-wrap';
4394
4400
  // This is the only way to properly add support for most clients,
@@ -4783,7 +4789,7 @@ class TextNode extends LexicalNode {
4783
4789
  const isBefore = target === this.getPreviousSibling();
4784
4790
  if (!isBefore && target !== this.getNextSibling()) {
4785
4791
  {
4786
- throw Error(`mergeWithSibling: sibling must be a previous or next sibling`);
4792
+ formatDevErrorMessage(`mergeWithSibling: sibling must be a previous or next sibling`);
4787
4793
  }
4788
4794
  }
4789
4795
  const key = this.__key;
@@ -4871,7 +4877,7 @@ function $convertTextDOMNode(domNode) {
4871
4877
  const domNode_ = domNode;
4872
4878
  const parentDom = domNode.parentElement;
4873
4879
  if (!(parentDom !== null)) {
4874
- throw Error(`Expected parentElement of Text not to be null`);
4880
+ formatDevErrorMessage(`Expected parentElement of Text not to be null`);
4875
4881
  }
4876
4882
  let textContent = domNode_.textContent || '';
4877
4883
  // No collapse and preserve segment break for pre, pre-wrap and pre-line
@@ -5044,14 +5050,6 @@ function applyTextFormatFromStyle(style, shouldApply) {
5044
5050
  };
5045
5051
  }
5046
5052
 
5047
- /**
5048
- * Copyright (c) Meta Platforms, Inc. and affiliates.
5049
- *
5050
- * This source code is licensed under the MIT license found in the
5051
- * LICENSE file in the root directory of this source tree.
5052
- *
5053
- */
5054
-
5055
5053
  /** @noInheritDoc */
5056
5054
  class TabNode extends TextNode {
5057
5055
  static getType() {
@@ -5081,19 +5079,19 @@ class TabNode extends TextNode {
5081
5079
  }
5082
5080
  setTextContent(text) {
5083
5081
  if (!(text === '\t' || text === '')) {
5084
- throw Error(`TabNode does not support setTextContent`);
5082
+ formatDevErrorMessage(`TabNode does not support setTextContent`);
5085
5083
  }
5086
5084
  return super.setTextContent(text);
5087
5085
  }
5088
5086
  setDetail(detail) {
5089
5087
  if (!(detail === IS_UNMERGEABLE)) {
5090
- throw Error(`TabNode does not support setDetail`);
5088
+ formatDevErrorMessage(`TabNode does not support setDetail`);
5091
5089
  }
5092
5090
  return this;
5093
5091
  }
5094
5092
  setMode(type) {
5095
5093
  if (!(type === 'normal')) {
5096
- throw Error(`TabNode does not support setMode`);
5094
+ formatDevErrorMessage(`TabNode does not support setMode`);
5097
5095
  }
5098
5096
  return this;
5099
5097
  }
@@ -5111,14 +5109,6 @@ function $isTabNode(node) {
5111
5109
  return node instanceof TabNode;
5112
5110
  }
5113
5111
 
5114
- /**
5115
- * Copyright (c) Meta Platforms, Inc. and affiliates.
5116
- *
5117
- * This source code is licensed under the MIT license found in the
5118
- * LICENSE file in the root directory of this source tree.
5119
- *
5120
- */
5121
-
5122
5112
  class Point {
5123
5113
  constructor(key, offset, type) {
5124
5114
  {
@@ -5138,29 +5128,19 @@ class Point {
5138
5128
  return this.key === point.key && this.offset === point.offset && this.type === point.type;
5139
5129
  }
5140
5130
  isBefore(b) {
5141
- let aNode = this.getNode();
5142
- let bNode = b.getNode();
5143
- const aOffset = this.offset;
5144
- const bOffset = b.offset;
5145
- if ($isElementNode(aNode)) {
5146
- const aNodeDescendant = aNode.getDescendantByIndex(aOffset);
5147
- aNode = aNodeDescendant != null ? aNodeDescendant : aNode;
5148
- }
5149
- if ($isElementNode(bNode)) {
5150
- const bNodeDescendant = bNode.getDescendantByIndex(bOffset);
5151
- bNode = bNodeDescendant != null ? bNodeDescendant : bNode;
5131
+ if (this.key === b.key) {
5132
+ return this.offset < b.offset;
5152
5133
  }
5153
- if (aNode === bNode) {
5154
- return aOffset < bOffset;
5155
- }
5156
- return aNode.isBefore(bNode);
5134
+ const aCaret = $normalizeCaret($caretFromPoint(this, 'next'));
5135
+ const bCaret = $normalizeCaret($caretFromPoint(b, 'next'));
5136
+ return $comparePointCaretNext(aCaret, bCaret) < 0;
5157
5137
  }
5158
5138
  getNode() {
5159
5139
  const key = this.key;
5160
5140
  const node = $getNodeByKey(key);
5161
5141
  if (node === null) {
5162
5142
  {
5163
- throw Error(`Point.getNode: node not found`);
5143
+ formatDevErrorMessage(`Point.getNode: node not found`);
5164
5144
  }
5165
5145
  }
5166
5146
  return node;
@@ -5177,7 +5157,7 @@ class Point {
5177
5157
  {
5178
5158
  const node = $getNodeByKey(key);
5179
5159
  if (!(type === 'text' ? $isTextNode(node) : $isElementNode(node))) {
5180
- throw Error(`PointType.set: node with key ${key} is ${node ? node.__type : '[not found]'} and can not be used for a ${type} point`);
5160
+ formatDevErrorMessage(`PointType.set: node with key ${key} is ${node ? node.__type : '[not found]'} and can not be used for a ${type} point`);
5181
5161
  }
5182
5162
  }
5183
5163
  if (!isCurrentlyReadOnlyMode()) {
@@ -5420,7 +5400,7 @@ class RangeSelection {
5420
5400
  {
5421
5401
  if (this.isCollapsed() && nodes.length > 1) {
5422
5402
  {
5423
- throw Error(`RangeSelection.getNodes() returned ${String(nodes.length)} > 1 nodes in a collapsed selection`);
5403
+ formatDevErrorMessage(`RangeSelection.getNodes() returned ${String(nodes.length)} > 1 nodes in a collapsed selection`);
5424
5404
  }
5425
5405
  }
5426
5406
  }
@@ -5659,7 +5639,7 @@ class RangeSelection {
5659
5639
  let firstNode = selectedNodes[0];
5660
5640
  if (!$isTextNode(firstNode)) {
5661
5641
  {
5662
- throw Error(`insertText: first node is not a text node`);
5642
+ formatDevErrorMessage(`insertText: first node is not a text node`);
5663
5643
  }
5664
5644
  }
5665
5645
  const firstNodeText = firstNode.getTextContent();
@@ -6054,7 +6034,7 @@ class RangeSelection {
6054
6034
  this.insertParagraph();
6055
6035
  const selection = $getSelection();
6056
6036
  if (!$isRangeSelection(selection)) {
6057
- throw Error(`Expected RangeSelection after insertParagraph`);
6037
+ formatDevErrorMessage(`Expected RangeSelection after insertParagraph`);
6058
6038
  }
6059
6039
  return selection.insertNodes(nodes);
6060
6040
  }
@@ -6079,7 +6059,7 @@ class RangeSelection {
6079
6059
  const notInline = node => ($isElementNode(node) || $isDecoratorNode(node)) && !node.isInline();
6080
6060
  if (!nodes.some(notInline)) {
6081
6061
  if (!$isElementNode(firstBlock)) {
6082
- throw Error(`Expected node ${firstNode.constructor.name} of type ${firstNode.getType()} to have a block ElementNode ancestor`);
6062
+ formatDevErrorMessage(`Expected node ${firstNode.constructor.name} of type ${firstNode.getType()} to have a block ElementNode ancestor`);
6083
6063
  }
6084
6064
  const index = $removeTextAndSplitBlock(this);
6085
6065
  firstBlock.splice(index, 0, nodes);
@@ -6098,14 +6078,14 @@ class RangeSelection {
6098
6078
  let firstToInsert = blocks[0];
6099
6079
  if (isMergeable(firstToInsert)) {
6100
6080
  if (!$isElementNode(firstBlock)) {
6101
- throw Error(`Expected node ${firstNode.constructor.name} of type ${firstNode.getType()} to have a block ElementNode ancestor`);
6081
+ formatDevErrorMessage(`Expected node ${firstNode.constructor.name} of type ${firstNode.getType()} to have a block ElementNode ancestor`);
6102
6082
  }
6103
6083
  firstBlock.append(...firstToInsert.getChildren());
6104
6084
  firstToInsert = blocks[1];
6105
6085
  }
6106
6086
  if (firstToInsert) {
6107
6087
  if (!(firstBlock !== null)) {
6108
- throw Error(`Expected node ${firstNode.constructor.name} of type ${firstNode.getType()} to have a block ancestor`);
6088
+ formatDevErrorMessage(`Expected node ${firstNode.constructor.name} of type ${firstNode.getType()} to have a block ancestor`);
6109
6089
  }
6110
6090
  insertRangeAfter(firstBlock, firstToInsert);
6111
6091
  }
@@ -6141,7 +6121,7 @@ class RangeSelection {
6141
6121
  const index = $removeTextAndSplitBlock(this);
6142
6122
  const block = $getAncestor(this.anchor.getNode(), INTERNAL_$isBlock);
6143
6123
  if (!$isElementNode(block)) {
6144
- throw Error(`Expected ancestor to be a block ElementNode`);
6124
+ formatDevErrorMessage(`Expected ancestor to be a block ElementNode`);
6145
6125
  }
6146
6126
  const firstToAppend = block.getChildAtIndex(index);
6147
6127
  const nodesToInsert = firstToAppend ? [firstToAppend, ...firstToAppend.getNextSiblings()] : [];
@@ -6231,51 +6211,10 @@ class RangeSelection {
6231
6211
  * @param granularity the granularity at which to apply the modification
6232
6212
  */
6233
6213
  modify(alter, isBackward, granularity) {
6234
- const focus = this.focus;
6235
- const anchor = this.anchor;
6236
- const collapse = alter === 'move';
6237
-
6238
- // Handle the selection movement around decorators.
6239
- const possibleNode = $getAdjacentNode(focus, isBackward);
6240
- if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
6241
- // Make it possible to move selection from range selection to
6242
- // node selection on the node.
6243
- if (collapse && possibleNode.isKeyboardSelectable()) {
6244
- const nodeSelection = $createNodeSelection();
6245
- nodeSelection.add(possibleNode.__key);
6246
- $setSelection(nodeSelection);
6247
- return;
6248
- }
6249
- const sibling = isBackward ? possibleNode.getPreviousSibling() : possibleNode.getNextSibling();
6250
- if (!$isTextNode(sibling)) {
6251
- const parent = possibleNode.getParentOrThrow();
6252
- let offset;
6253
- let elementKey;
6254
- if ($isElementNode(sibling)) {
6255
- elementKey = sibling.__key;
6256
- offset = isBackward ? sibling.getChildrenSize() : 0;
6257
- } else {
6258
- offset = possibleNode.getIndexWithinParent();
6259
- elementKey = parent.__key;
6260
- if (!isBackward) {
6261
- offset++;
6262
- }
6263
- }
6264
- focus.set(elementKey, offset, 'element');
6265
- if (collapse) {
6266
- anchor.set(elementKey, offset, 'element');
6267
- }
6268
- return;
6269
- } else {
6270
- const siblingKey = sibling.__key;
6271
- const offset = isBackward ? sibling.getTextContent().length : 0;
6272
- focus.set(siblingKey, offset, 'text');
6273
- if (collapse) {
6274
- anchor.set(siblingKey, offset, 'text');
6275
- }
6276
- return;
6277
- }
6214
+ if ($modifySelectionAroundDecoratorsAndBlocks(this, alter, isBackward, granularity)) {
6215
+ return;
6278
6216
  }
6217
+ const collapse = alter === 'move';
6279
6218
  const editor = getActiveEditor();
6280
6219
  const domSelection = getDOMSelection(getWindow(editor));
6281
6220
  if (!domSelection) {
@@ -6283,12 +6222,26 @@ class RangeSelection {
6283
6222
  }
6284
6223
  const blockCursorElement = editor._blockCursorElement;
6285
6224
  const rootElement = editor._rootElement;
6225
+ const focusNode = this.focus.getNode();
6286
6226
  // Remove the block cursor element if it exists. This will ensure selection
6287
6227
  // works as intended. If we leave it in the DOM all sorts of strange bugs
6288
6228
  // occur. :/
6289
- if (rootElement !== null && blockCursorElement !== null && $isElementNode(possibleNode) && !possibleNode.isInline() && !possibleNode.canBeEmpty()) {
6229
+ if (rootElement !== null && blockCursorElement !== null && $isElementNode(focusNode) && !focusNode.isInline() && !focusNode.canBeEmpty()) {
6290
6230
  removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
6291
6231
  }
6232
+ if (this.dirty) {
6233
+ let nextAnchorDOM = getElementByKeyOrThrow(editor, this.anchor.key);
6234
+ let nextFocusDOM = getElementByKeyOrThrow(editor, this.focus.key);
6235
+ if (this.anchor.type === 'text') {
6236
+ nextAnchorDOM = getDOMTextNode(nextAnchorDOM);
6237
+ }
6238
+ if (this.focus.type === 'text') {
6239
+ nextFocusDOM = getDOMTextNode(nextFocusDOM);
6240
+ }
6241
+ if (nextAnchorDOM && nextFocusDOM) {
6242
+ setDOMSelectionBaseAndExtent(domSelection, nextAnchorDOM, this.anchor.offset, nextFocusDOM, this.focus.offset);
6243
+ }
6244
+ }
6292
6245
  // We use the DOM selection.modify API here to "tell" us what the selection
6293
6246
  // will be. We then use it to update the Lexical selection accordingly. This
6294
6247
  // is much more reliable than waiting for a beforeinput and using the ranges
@@ -6345,6 +6298,9 @@ class RangeSelection {
6345
6298
  }
6346
6299
  }
6347
6300
  }
6301
+ if (granularity === 'lineboundary') {
6302
+ $modifySelectionAroundDecoratorsAndBlocks(this, alter, isBackward, granularity, 'decorators');
6303
+ }
6348
6304
  }
6349
6305
  /**
6350
6306
  * Helper for handling forward character and word deletion that prevents element nodes
@@ -6385,7 +6341,6 @@ class RangeSelection {
6385
6341
  const initialCaret = $caretFromPoint(anchor, direction);
6386
6342
  const initialRange = $extendCaretToRange(initialCaret);
6387
6343
  if (initialRange.getTextSlices().every(slice => slice === null || slice.distance === 0)) {
6388
- // debugger;
6389
6344
  // There's no text in the direction of the deletion so we can explore our options
6390
6345
  let state = {
6391
6346
  type: 'initial'
@@ -6501,30 +6456,16 @@ class RangeSelection {
6501
6456
  */
6502
6457
  deleteLine(isBackward) {
6503
6458
  if (this.isCollapsed()) {
6504
- // Since `domSelection.modify('extend', ..., 'lineboundary')` works well for text selections
6505
- // but doesn't properly handle selections which end on elements, a space character is added
6506
- // for such selections transforming their anchor's type to 'text'
6507
- const anchorIsElement = this.anchor.type === 'element';
6508
- if (anchorIsElement) {
6509
- this.insertText(' ');
6510
- }
6511
6459
  this.modify('extend', isBackward, 'lineboundary');
6512
- const useDeleteCharacter = this.isCollapsed() && this.anchor.offset === 0;
6513
- // Adjusts selection to include an extra character added for element anchors to remove it
6514
- if (anchorIsElement) {
6515
- const startPoint = isBackward ? this.anchor : this.focus;
6516
- startPoint.set(startPoint.key, startPoint.offset + 1, startPoint.type);
6517
- }
6518
- // If the selection starts at the beginning of a text node (offset 0),
6460
+ }
6461
+ if (this.isCollapsed()) {
6462
+ // If the selection was already collapsed at the lineboundary,
6519
6463
  // use the deleteCharacter operation to handle all of the logic associated
6520
6464
  // with navigating through the parent element
6521
- if (useDeleteCharacter) {
6522
- // Remove the inserted space, if added above
6523
- this.removeText();
6524
- return this.deleteCharacter(isBackward);
6525
- }
6465
+ this.deleteCharacter(isBackward);
6466
+ } else {
6467
+ this.removeText();
6526
6468
  }
6527
- this.removeText();
6528
6469
  }
6529
6470
 
6530
6471
  /**
@@ -6702,7 +6643,7 @@ function $updateCaretSelectionForUnicodeCharacter(selection, isBackward) {
6702
6643
  function shouldDeleteExactlyOneCodeUnit(text) {
6703
6644
  {
6704
6645
  if (!(text.length > 1)) {
6705
- throw Error(`shouldDeleteExactlyOneCodeUnit: expecting to be called only with sequences of two or more code units`);
6646
+ formatDevErrorMessage(`shouldDeleteExactlyOneCodeUnit: expecting to be called only with sequences of two or more code units`);
6706
6647
  }
6707
6648
  }
6708
6649
  return !(doesContainSurrogatePair(text) || doesContainEmoji(text));
@@ -6824,13 +6765,13 @@ function $internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
6824
6765
  if ($isElementNode(resolvedElement)) {
6825
6766
  const elementDOM = editor.getElementByKey(resolvedElement.getKey());
6826
6767
  if (!(elementDOM !== null)) {
6827
- throw Error(`$internalResolveSelectionPoint: node in DOM but not keyToDOMMap`);
6768
+ formatDevErrorMessage(`$internalResolveSelectionPoint: node in DOM but not keyToDOMMap`);
6828
6769
  }
6829
6770
  const slot = resolvedElement.getDOMSlot(elementDOM);
6830
6771
  [resolvedElement, resolvedOffset] = slot.resolveChildIndex(resolvedElement, elementDOM, dom, offset);
6831
6772
  // This is just a typescript workaround, it is true but lost due to mutability
6832
6773
  if (!$isElementNode(resolvedElement)) {
6833
- throw Error(`$internalResolveSelectionPoint: resolvedElement is not an ElementNode`);
6774
+ formatDevErrorMessage(`$internalResolveSelectionPoint: resolvedElement is not an ElementNode`);
6834
6775
  }
6835
6776
  if (moveSelectionToEnd && resolvedOffset >= resolvedElement.getChildrenSize()) {
6836
6777
  resolvedOffset = Math.max(0, resolvedElement.getChildrenSize() - 1);
@@ -6852,7 +6793,7 @@ function $internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
6852
6793
  resolvedOffset = getTextNodeOffset(child, moveSelectionToEnd);
6853
6794
  } else if (child !== resolvedElement && moveSelectionToEnd && !hasBlockCursor) {
6854
6795
  if (!$isElementNode(resolvedElement)) {
6855
- throw Error(`invariant`);
6796
+ formatDevErrorMessage(`invariant`);
6856
6797
  }
6857
6798
  resolvedOffset = Math.min(resolvedElement.getChildrenSize(), resolvedOffset + 1);
6858
6799
  }
@@ -7048,23 +6989,23 @@ function $internalCreateRangeSelection(lastSelection, domSelection, editor, even
7048
6989
  function $validatePoint(editor, name, point) {
7049
6990
  const node = $getNodeByKey(point.key);
7050
6991
  if (!(node !== undefined)) {
7051
- throw Error(`$validatePoint: ${name} key ${point.key} not found in current editorState`);
6992
+ formatDevErrorMessage(`$validatePoint: ${name} key ${point.key} not found in current editorState`);
7052
6993
  }
7053
6994
  if (point.type === 'text') {
7054
6995
  if (!$isTextNode(node)) {
7055
- throw Error(`$validatePoint: ${name} key ${point.key} is not a TextNode`);
6996
+ formatDevErrorMessage(`$validatePoint: ${name} key ${point.key} is not a TextNode`);
7056
6997
  }
7057
6998
  const size = node.getTextContentSize();
7058
6999
  if (!(point.offset <= size)) {
7059
- throw Error(`$validatePoint: ${name} point.offset > node.getTextContentSize() (${String(point.offset)} > ${String(size)})`);
7000
+ formatDevErrorMessage(`$validatePoint: ${name} point.offset > node.getTextContentSize() (${String(point.offset)} > ${String(size)})`);
7060
7001
  }
7061
7002
  } else {
7062
7003
  if (!$isElementNode(node)) {
7063
- throw Error(`$validatePoint: ${name} key ${point.key} is not an ElementNode`);
7004
+ formatDevErrorMessage(`$validatePoint: ${name} key ${point.key} is not an ElementNode`);
7064
7005
  }
7065
7006
  const size = node.getChildrenSize();
7066
7007
  if (!(point.offset <= size)) {
7067
- throw Error(`$validatePoint: ${name} point.offset > node.getChildrenSize() (${String(point.offset)} > ${String(size)})`);
7008
+ formatDevErrorMessage(`$validatePoint: ${name} point.offset > node.getChildrenSize() (${String(point.offset)} > ${String(size)})`);
7068
7009
  }
7069
7010
  }
7070
7011
  }
@@ -7228,6 +7169,20 @@ function adjustPointOffsetForMergedSibling(point, isBefore, key, target, textLen
7228
7169
  point.set(point.key, point.offset - 1, 'element');
7229
7170
  }
7230
7171
  }
7172
+ function setDOMSelectionBaseAndExtent(domSelection, nextAnchorDOM, nextAnchorOffset, nextFocusDOM, nextFocusOffset) {
7173
+ // Apply the updated selection to the DOM. Note: this will trigger
7174
+ // a "selectionchange" event, although it will be asynchronous.
7175
+ try {
7176
+ domSelection.setBaseAndExtent(nextAnchorDOM, nextAnchorOffset, nextFocusDOM, nextFocusOffset);
7177
+ } catch (error) {
7178
+ // If we encounter an error, continue. This can sometimes
7179
+ // occur with FF and there's no good reason as to why it
7180
+ // should happen.
7181
+ {
7182
+ console.warn(error);
7183
+ }
7184
+ }
7185
+ }
7231
7186
  function updateDOMSelection(prevSelection, nextSelection, editor, domSelection, tags, rootElement, nodeCount) {
7232
7187
  const anchorDOMNode = domSelection.anchorNode;
7233
7188
  const focusDOMNode = domSelection.focusNode;
@@ -7304,16 +7259,7 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
7304
7259
 
7305
7260
  // Apply the updated selection to the DOM. Note: this will trigger
7306
7261
  // a "selectionchange" event, although it will be asynchronous.
7307
- try {
7308
- domSelection.setBaseAndExtent(nextAnchorNode, nextAnchorOffset, nextFocusNode, nextFocusOffset);
7309
- } catch (error) {
7310
- // If we encounter an error, continue. This can sometimes
7311
- // occur with FF and there's no good reason as to why it
7312
- // should happen.
7313
- {
7314
- console.warn(error);
7315
- }
7316
- }
7262
+ setDOMSelectionBaseAndExtent(domSelection, nextAnchorNode, nextAnchorOffset, nextFocusNode, nextFocusOffset);
7317
7263
  if (!tags.has('skip-scroll-into-view') && nextSelection.isCollapsed() && rootElement !== null && rootElement === document.activeElement) {
7318
7264
  const selectionTarget = $isRangeSelection(nextSelection) && nextSelection.anchor.type === 'element' ? nextAnchorNode.childNodes[nextAnchorOffset] || null : domSelection.rangeCount > 0 ? domSelection.getRangeAt(0) : null;
7319
7265
  if (selectionTarget !== null) {
@@ -7356,7 +7302,7 @@ function $removeTextAndSplitBlock(selection) {
7356
7302
  selection_ = newSelection;
7357
7303
  }
7358
7304
  if (!$isRangeSelection(selection_)) {
7359
- throw Error(`Unexpected dirty selection to be null`);
7305
+ formatDevErrorMessage(`Unexpected dirty selection to be null`);
7360
7306
  }
7361
7307
  const anchor = selection_.anchor;
7362
7308
  let node = anchor.getNode();
@@ -7501,12 +7447,74 @@ range) {
7501
7447
  }
7502
7448
 
7503
7449
  /**
7504
- * Copyright (c) Meta Platforms, Inc. and affiliates.
7505
- *
7506
- * This source code is licensed under the MIT license found in the
7507
- * LICENSE file in the root directory of this source tree.
7450
+ * @internal
7508
7451
  *
7452
+ * Modify the focus of the focus around possible decorators and blocks and return true
7453
+ * if the movement is done.
7509
7454
  */
7455
+ function $modifySelectionAroundDecoratorsAndBlocks(selection, alter, isBackward, granularity, mode = 'decorators-and-blocks') {
7456
+ if (alter === 'move' && granularity === 'character' && !selection.isCollapsed()) {
7457
+ // moving left or right when the selection isn't collapsed will
7458
+ // just set the anchor to the focus or vice versa depending on
7459
+ // direction
7460
+ const [src, dst] = isBackward === selection.isBackward() ? [selection.focus, selection.anchor] : [selection.anchor, selection.focus];
7461
+ dst.set(src.key, src.offset, src.type);
7462
+ return true;
7463
+ }
7464
+ const initialFocus = $caretFromPoint(selection.focus, isBackward ? 'previous' : 'next');
7465
+ const isLineBoundary = granularity === 'lineboundary';
7466
+ const collapse = alter === 'move';
7467
+ let focus = initialFocus;
7468
+ let checkForBlock = mode === 'decorators-and-blocks';
7469
+ if (!$isExtendableTextPointCaret(focus)) {
7470
+ for (const siblingCaret of focus) {
7471
+ checkForBlock = false;
7472
+ const {
7473
+ origin
7474
+ } = siblingCaret;
7475
+ if ($isDecoratorNode(origin) && !origin.isIsolated()) {
7476
+ focus = siblingCaret;
7477
+ if (isLineBoundary && origin.isInline()) {
7478
+ continue;
7479
+ }
7480
+ }
7481
+ break;
7482
+ }
7483
+ if (checkForBlock) {
7484
+ for (const nextCaret of $extendCaretToRange(initialFocus).iterNodeCarets(alter === 'extend' ? 'shadowRoot' : 'root')) {
7485
+ if ($isChildCaret(nextCaret)) {
7486
+ if (!nextCaret.origin.isInline()) {
7487
+ focus = nextCaret;
7488
+ }
7489
+ } else if ($isElementNode(nextCaret.origin)) {
7490
+ continue;
7491
+ } else if ($isDecoratorNode(nextCaret.origin) && !nextCaret.origin.isInline()) {
7492
+ focus = nextCaret;
7493
+ }
7494
+ break;
7495
+ }
7496
+ }
7497
+ }
7498
+ if (focus === initialFocus) {
7499
+ return false;
7500
+ }
7501
+ // After this point checkForBlock is true if and only if we moved to a
7502
+ // different block, so we should stop regardless of the granularity
7503
+ if (collapse && !isLineBoundary && $isDecoratorNode(focus.origin) && focus.origin.isKeyboardSelectable()) {
7504
+ // Make it possible to move selection from range selection to
7505
+ // node selection on the node.
7506
+ const nodeSelection = $createNodeSelection();
7507
+ nodeSelection.add(focus.origin.getKey());
7508
+ $setSelection(nodeSelection);
7509
+ return true;
7510
+ }
7511
+ focus = $normalizeCaret(focus);
7512
+ if (collapse) {
7513
+ $setPointFromCaret(selection.anchor, focus);
7514
+ }
7515
+ $setPointFromCaret(selection.focus, focus);
7516
+ return checkForBlock || !isLineBoundary;
7517
+ }
7510
7518
 
7511
7519
  let activeEditorState = null;
7512
7520
  let activeEditor = null;
@@ -7524,21 +7532,21 @@ function isCurrentlyReadOnlyMode() {
7524
7532
  function errorOnReadOnly() {
7525
7533
  if (isReadOnlyMode) {
7526
7534
  {
7527
- throw Error(`Cannot use method in read-only mode.`);
7535
+ formatDevErrorMessage(`Cannot use method in read-only mode.`);
7528
7536
  }
7529
7537
  }
7530
7538
  }
7531
7539
  function errorOnInfiniteTransforms() {
7532
7540
  if (infiniteTransformCount > 99) {
7533
7541
  {
7534
- throw Error(`One or more transforms are endlessly triggering additional transforms. May have encountered infinite recursion caused by transforms that have their preconditions too lose and/or conflict with each other.`);
7542
+ formatDevErrorMessage(`One or more transforms are endlessly triggering additional transforms. May have encountered infinite recursion caused by transforms that have their preconditions too lose and/or conflict with each other.`);
7535
7543
  }
7536
7544
  }
7537
7545
  }
7538
7546
  function getActiveEditorState() {
7539
7547
  if (activeEditorState === null) {
7540
7548
  {
7541
- throw Error(`Unable to find an active editor state. State helpers or node methods can only be used synchronously during the callback of editor.update(), editor.read(), or editorState.read().${collectBuildInformation()}`);
7549
+ formatDevErrorMessage(`Unable to find an active editor state. State helpers or node methods can only be used synchronously during the callback of editor.update(), editor.read(), or editorState.read().${collectBuildInformation()}`);
7542
7550
  }
7543
7551
  }
7544
7552
  return activeEditorState;
@@ -7546,7 +7554,7 @@ function getActiveEditorState() {
7546
7554
  function getActiveEditor() {
7547
7555
  if (activeEditor === null) {
7548
7556
  {
7549
- throw Error(`Unable to find an active editor. This method can only be used synchronously during the callback of editor.update() or editor.read().${collectBuildInformation()}`);
7557
+ formatDevErrorMessage(`Unable to find an active editor. This method can only be used synchronously during the callback of editor.update() or editor.read().${collectBuildInformation()}`);
7550
7558
  }
7551
7559
  }
7552
7560
  return activeEditor;
@@ -7705,13 +7713,13 @@ function $parseSerializedNodeImpl(serializedNode, registeredNodes) {
7705
7713
  const registeredNode = registeredNodes.get(type);
7706
7714
  if (registeredNode === undefined) {
7707
7715
  {
7708
- throw Error(`parseEditorState: type "${type}" + not found`);
7716
+ formatDevErrorMessage(`parseEditorState: type "${type}" + not found`);
7709
7717
  }
7710
7718
  }
7711
7719
  const nodeClass = registeredNode.klass;
7712
7720
  if (serializedNode.type !== nodeClass.getType()) {
7713
7721
  {
7714
- throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
7722
+ formatDevErrorMessage(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
7715
7723
  }
7716
7724
  }
7717
7725
  const node = nodeClass.importJSON(serializedNode);
@@ -8067,7 +8075,7 @@ function processNestedUpdates(editor, initialSkipTransforms) {
8067
8075
  if (options.discrete) {
8068
8076
  const pendingEditorState = editor._pendingEditorState;
8069
8077
  if (!(pendingEditorState !== null)) {
8070
- throw Error(`Unexpected empty pending editor state on discrete nested update`);
8078
+ formatDevErrorMessage(`Unexpected empty pending editor state on discrete nested update`);
8071
8079
  }
8072
8080
  pendingEditorState._flushSync = true;
8073
8081
  }
@@ -8146,7 +8154,7 @@ function $beginUpdate(editor, updateFn, options) {
8146
8154
  const focusKey = pendingSelection.focus.key;
8147
8155
  if (pendingNodeMap.get(anchorKey) === undefined || pendingNodeMap.get(focusKey) === undefined) {
8148
8156
  {
8149
- throw Error(`updateEditor: selection has been lost because the previously selected nodes have been removed and selection wasn't moved to another node. Ensure selection changes after removing/replacing a selected node.`);
8157
+ formatDevErrorMessage(`updateEditor: selection has been lost because the previously selected nodes have been removed and selection wasn't moved to another node. Ensure selection changes after removing/replacing a selected node.`);
8150
8158
  }
8151
8159
  }
8152
8160
  } else if ($isNodeSelection(pendingSelection)) {
@@ -8218,15 +8226,6 @@ function updateEditor(editor, updateFn, options) {
8218
8226
  }
8219
8227
  }
8220
8228
 
8221
- /**
8222
- * Copyright (c) Meta Platforms, Inc. and affiliates.
8223
- *
8224
- * This source code is licensed under the MIT license found in the
8225
- * LICENSE file in the root directory of this source tree.
8226
- *
8227
- */
8228
-
8229
-
8230
8229
  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
8231
8230
 
8232
8231
  /**
@@ -8266,7 +8265,7 @@ class ElementDOMSlot {
8266
8265
  insertChild(dom) {
8267
8266
  const before = this.before || this.getManagedLineBreak();
8268
8267
  if (!(before === null || before.parentElement === this.element)) {
8269
- throw Error(`ElementDOMSlot.insertChild: before is not in element`);
8268
+ formatDevErrorMessage(`ElementDOMSlot.insertChild: before is not in element`);
8270
8269
  }
8271
8270
  this.element.insertBefore(dom, before);
8272
8271
  return this;
@@ -8276,7 +8275,7 @@ class ElementDOMSlot {
8276
8275
  */
8277
8276
  removeChild(dom) {
8278
8277
  if (!(dom.parentElement === this.element)) {
8279
- throw Error(`ElementDOMSlot.removeChild: dom is not in element`);
8278
+ formatDevErrorMessage(`ElementDOMSlot.removeChild: dom is not in element`);
8280
8279
  }
8281
8280
  this.element.removeChild(dom);
8282
8281
  return this;
@@ -8289,7 +8288,7 @@ class ElementDOMSlot {
8289
8288
  */
8290
8289
  replaceChild(dom, prevDom) {
8291
8290
  if (!(prevDom.parentElement === this.element)) {
8292
- throw Error(`ElementDOMSlot.replaceChild: prevDom is not in element`);
8291
+ formatDevErrorMessage(`ElementDOMSlot.replaceChild: prevDom is not in element`);
8293
8292
  }
8294
8293
  this.element.replaceChild(dom, prevDom);
8295
8294
  return this;
@@ -8408,7 +8407,7 @@ function indexPath(root, child) {
8408
8407
  path.push(i);
8409
8408
  }
8410
8409
  if (!(node === root)) {
8411
- throw Error(`indexPath: root is not a parent of child`);
8410
+ formatDevErrorMessage(`indexPath: root is not a parent of child`);
8412
8411
  }
8413
8412
  return path.reverse();
8414
8413
  }
@@ -8567,7 +8566,7 @@ class ElementNode extends LexicalNode {
8567
8566
  const firstChild = this.getFirstChild();
8568
8567
  if (firstChild === null) {
8569
8568
  {
8570
- throw Error(`Expected node ${this.__key} to have a first child.`);
8569
+ formatDevErrorMessage(`Expected node ${this.__key} to have a first child.`);
8571
8570
  }
8572
8571
  }
8573
8572
  return firstChild;
@@ -8581,7 +8580,7 @@ class ElementNode extends LexicalNode {
8581
8580
  const lastChild = this.getLastChild();
8582
8581
  if (lastChild === null) {
8583
8582
  {
8584
- throw Error(`Expected node ${this.__key} to have a last child.`);
8583
+ formatDevErrorMessage(`Expected node ${this.__key} to have a last child.`);
8585
8584
  }
8586
8585
  }
8587
8586
  return lastChild;
@@ -8762,7 +8761,7 @@ class ElementNode extends LexicalNode {
8762
8761
  const oldSize = this.getChildrenSize();
8763
8762
  const writableSelf = this.getWritable();
8764
8763
  if (!(start + deleteCount <= oldSize)) {
8765
- throw Error(`ElementNode.splice: start + deleteCount > oldSize (${String(start)} + ${String(deleteCount)} > ${String(oldSize)})`);
8764
+ formatDevErrorMessage(`ElementNode.splice: start + deleteCount > oldSize (${String(start)} + ${String(deleteCount)} > ${String(oldSize)})`);
8766
8765
  }
8767
8766
  const writableSelfKey = writableSelf.__key;
8768
8767
  const nodesToInsertKeys = [];
@@ -8785,7 +8784,7 @@ class ElementNode extends LexicalNode {
8785
8784
  for (let i = 0; i < deleteCount; i++) {
8786
8785
  if (nodeToDelete === null) {
8787
8786
  {
8788
- throw Error(`splice: sibling not found`);
8787
+ formatDevErrorMessage(`splice: sibling not found`);
8789
8788
  }
8790
8789
  }
8791
8790
  const nextSibling = nodeToDelete.getNextSibling();
@@ -8818,7 +8817,7 @@ class ElementNode extends LexicalNode {
8818
8817
  }
8819
8818
  if (nodeToInsert.__key === writableSelfKey) {
8820
8819
  {
8821
- throw Error(`append: attempting to append self`);
8820
+ formatDevErrorMessage(`append: attempting to append self`);
8822
8821
  }
8823
8822
  }
8824
8823
  // Set child parent to self
@@ -9038,15 +9037,6 @@ function isPointRemoved(point, nodesToRemoveKeySet, nodesToInsertKeySet) {
9038
9037
  return false;
9039
9038
  }
9040
9039
 
9041
- /**
9042
- * Copyright (c) Meta Platforms, Inc. and affiliates.
9043
- *
9044
- * This source code is licensed under the MIT license found in the
9045
- * LICENSE file in the root directory of this source tree.
9046
- *
9047
- */
9048
-
9049
-
9050
9040
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
9051
9041
 
9052
9042
  /** @noInheritDoc */
@@ -9057,7 +9047,7 @@ class DecoratorNode extends LexicalNode {
9057
9047
  */
9058
9048
  decorate(editor, config) {
9059
9049
  {
9060
- throw Error(`decorate: base method not extended`);
9050
+ formatDevErrorMessage(`decorate: base method not extended`);
9061
9051
  }
9062
9052
  }
9063
9053
  isIsolated() {
@@ -9074,14 +9064,6 @@ function $isDecoratorNode(node) {
9074
9064
  return node instanceof DecoratorNode;
9075
9065
  }
9076
9066
 
9077
- /**
9078
- * Copyright (c) Meta Platforms, Inc. and affiliates.
9079
- *
9080
- * This source code is licensed under the MIT license found in the
9081
- * LICENSE file in the root directory of this source tree.
9082
- *
9083
- */
9084
-
9085
9067
  /** @noInheritDoc */
9086
9068
  class RootNode extends ElementNode {
9087
9069
  /** @internal */
@@ -9098,7 +9080,7 @@ class RootNode extends ElementNode {
9098
9080
  }
9099
9081
  getTopLevelElementOrThrow() {
9100
9082
  {
9101
- throw Error(`getTopLevelElementOrThrow: root nodes are not top level elements`);
9083
+ formatDevErrorMessage(`getTopLevelElementOrThrow: root nodes are not top level elements`);
9102
9084
  }
9103
9085
  }
9104
9086
  getTextContent() {
@@ -9112,22 +9094,22 @@ class RootNode extends ElementNode {
9112
9094
  }
9113
9095
  remove() {
9114
9096
  {
9115
- throw Error(`remove: cannot be called on root nodes`);
9097
+ formatDevErrorMessage(`remove: cannot be called on root nodes`);
9116
9098
  }
9117
9099
  }
9118
9100
  replace(node) {
9119
9101
  {
9120
- throw Error(`replace: cannot be called on root nodes`);
9102
+ formatDevErrorMessage(`replace: cannot be called on root nodes`);
9121
9103
  }
9122
9104
  }
9123
9105
  insertBefore(nodeToInsert) {
9124
9106
  {
9125
- throw Error(`insertBefore: cannot be called on root nodes`);
9107
+ formatDevErrorMessage(`insertBefore: cannot be called on root nodes`);
9126
9108
  }
9127
9109
  }
9128
9110
  insertAfter(nodeToInsert) {
9129
9111
  {
9130
- throw Error(`insertAfter: cannot be called on root nodes`);
9112
+ formatDevErrorMessage(`insertAfter: cannot be called on root nodes`);
9131
9113
  }
9132
9114
  }
9133
9115
 
@@ -9144,7 +9126,7 @@ class RootNode extends ElementNode {
9144
9126
  const node = nodesToAppend[i];
9145
9127
  if (!$isElementNode(node) && !$isDecoratorNode(node)) {
9146
9128
  {
9147
- throw Error(`rootNode.append: Only element or decorator nodes can be appended to the root node`);
9129
+ formatDevErrorMessage(`rootNode.append: Only element or decorator nodes can be appended to the root node`);
9148
9130
  }
9149
9131
  }
9150
9132
  }
@@ -9165,14 +9147,6 @@ function $isRootNode(node) {
9165
9147
  return node instanceof RootNode;
9166
9148
  }
9167
9149
 
9168
- /**
9169
- * Copyright (c) Meta Platforms, Inc. and affiliates.
9170
- *
9171
- * This source code is licensed under the MIT license found in the
9172
- * LICENSE file in the root directory of this source tree.
9173
- *
9174
- */
9175
-
9176
9150
  function editorStateHasDirtySelection(editorState, editor) {
9177
9151
  const currentSelection = editor.getEditorState()._selection;
9178
9152
  const pendingSelection = editorState._selection;
@@ -9198,14 +9172,14 @@ function exportNodeToJSON(node) {
9198
9172
  const nodeClass = node.constructor;
9199
9173
  if (serializedNode.type !== nodeClass.getType()) {
9200
9174
  {
9201
- throw Error(`LexicalNode: Node ${nodeClass.name} does not match the serialized type. Check if .exportJSON() is implemented and it is returning the correct type.`);
9175
+ formatDevErrorMessage(`LexicalNode: Node ${nodeClass.name} does not match the serialized type. Check if .exportJSON() is implemented and it is returning the correct type.`);
9202
9176
  }
9203
9177
  }
9204
9178
  if ($isElementNode(node)) {
9205
9179
  const serializedChildren = serializedNode.children;
9206
9180
  if (!Array.isArray(serializedChildren)) {
9207
9181
  {
9208
- throw Error(`LexicalNode: Node ${nodeClass.name} is an element but .exportJSON() does not have a children array.`);
9182
+ formatDevErrorMessage(`LexicalNode: Node ${nodeClass.name} is an element but .exportJSON() does not have a children array.`);
9209
9183
  }
9210
9184
  }
9211
9185
  const children = node.getChildren();
@@ -9386,15 +9360,6 @@ function $isParagraphNode(node) {
9386
9360
  return node instanceof ParagraphNode;
9387
9361
  }
9388
9362
 
9389
- /**
9390
- * Copyright (c) Meta Platforms, Inc. and affiliates.
9391
- *
9392
- * This source code is licensed under the MIT license found in the
9393
- * LICENSE file in the root directory of this source tree.
9394
- *
9395
- */
9396
-
9397
-
9398
9363
  // https://github.com/microsoft/TypeScript/issues/3841
9399
9364
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9400
9365
 
@@ -9533,7 +9498,7 @@ function createEditor(editorConfig) {
9533
9498
  const name = klass.name;
9534
9499
  if (replaceWithKlass) {
9535
9500
  if (!(replaceWithKlass.prototype instanceof klass)) {
9536
- throw Error(`${replaceWithKlass.name} doesn't extend the ${name}`);
9501
+ formatDevErrorMessage(`${replaceWithKlass.name} doesn't extend the ${name}`);
9537
9502
  }
9538
9503
  }
9539
9504
  if (name !== 'RootNode' && nodeType !== 'root' && nodeType !== 'artificial') {
@@ -9818,7 +9783,7 @@ class LexicalEditor {
9818
9783
  registerCommand(command, listener, priority) {
9819
9784
  if (priority === undefined) {
9820
9785
  {
9821
- throw Error(`Listener for type "command" requires a "priority".`);
9786
+ formatDevErrorMessage(`Listener for type "command" requires a "priority".`);
9822
9787
  }
9823
9788
  }
9824
9789
  const commandsMap = this._commands;
@@ -9828,7 +9793,7 @@ class LexicalEditor {
9828
9793
  const listenersInPriorityOrder = commandsMap.get(command);
9829
9794
  if (listenersInPriorityOrder === undefined) {
9830
9795
  {
9831
- throw Error(`registerCommand: Command ${String(command)} not found in command map`);
9796
+ formatDevErrorMessage(`registerCommand: Command ${String(command)} not found in command map`);
9832
9797
  }
9833
9798
  }
9834
9799
  const listeners = listenersInPriorityOrder[priority];
@@ -9877,7 +9842,7 @@ class LexicalEditor {
9877
9842
  const registeredNode = this._nodes.get(klass.getType());
9878
9843
  if (registeredNode === undefined) {
9879
9844
  {
9880
- throw Error(`Node ${klass.name} has not been registered. Ensure node has been passed to createEditor.`);
9845
+ formatDevErrorMessage(`Node ${klass.name} has not been registered. Ensure node has been passed to createEditor.`);
9881
9846
  }
9882
9847
  }
9883
9848
  return registeredNode;
@@ -10086,7 +10051,7 @@ class LexicalEditor {
10086
10051
  setEditorState(editorState, options) {
10087
10052
  if (editorState.isEmpty()) {
10088
10053
  {
10089
- throw Error(`setEditorState: the editor state is empty. Ensure the editor state's root node never becomes empty.`);
10054
+ formatDevErrorMessage(`setEditorState: the editor state is empty. Ensure the editor state's root node never becomes empty.`);
10090
10055
  }
10091
10056
  }
10092
10057
 
@@ -10256,15 +10221,7 @@ class LexicalEditor {
10256
10221
  };
10257
10222
  }
10258
10223
  }
10259
- LexicalEditor.version = "0.25.1-nightly.20250228.0+dev.cjs";
10260
-
10261
- /**
10262
- * Copyright (c) Meta Platforms, Inc. and affiliates.
10263
- *
10264
- * This source code is licensed under the MIT license found in the
10265
- * LICENSE file in the root directory of this source tree.
10266
- *
10267
- */
10224
+ LexicalEditor.version = "0.26.1-nightly.20250303.0+dev.cjs";
10268
10225
 
10269
10226
  let keyCounter = 1;
10270
10227
  function resetRandomKey() {
@@ -10277,7 +10234,7 @@ function getRegisteredNodeOrThrow(editor, nodeType) {
10277
10234
  const registeredNode = editor._nodes.get(nodeType);
10278
10235
  if (registeredNode === undefined) {
10279
10236
  {
10280
- throw Error(`registeredNode: Type ${nodeType} not found`);
10237
+ formatDevErrorMessage(`registeredNode: Type ${nodeType} not found`);
10281
10238
  }
10282
10239
  }
10283
10240
  return registeredNode;
@@ -10430,11 +10387,11 @@ function errorOnNodeKeyConstructorMismatch(node, existingKey) {
10430
10387
  // Lifted condition to if statement because the inverted logic is a bit confusing
10431
10388
  if (node.constructor.name !== existingNode.constructor.name) {
10432
10389
  {
10433
- throw Error(`Lexical node with constructor ${node.constructor.name} attempted to re-use key from node in active editor state with constructor ${existingNode.constructor.name}. Keys must not be re-used when the type is changed.`);
10390
+ formatDevErrorMessage(`Lexical node with constructor ${node.constructor.name} attempted to re-use key from node in active editor state with constructor ${existingNode.constructor.name}. Keys must not be re-used when the type is changed.`);
10434
10391
  }
10435
10392
  } else {
10436
10393
  {
10437
- throw Error(`Lexical node with constructor ${node.constructor.name} attempted to re-use key from node in active editor state with different constructor with the same name (possibly due to invalid Hot Module Replacement). Keys must not be re-used when the type is changed.`);
10394
+ formatDevErrorMessage(`Lexical node with constructor ${node.constructor.name} attempted to re-use key from node in active editor state with different constructor with the same name (possibly due to invalid Hot Module Replacement). Keys must not be re-used when the type is changed.`);
10438
10395
  }
10439
10396
  }
10440
10397
  }
@@ -10653,7 +10610,7 @@ function $setSelection(selection) {
10653
10610
  {
10654
10611
  if (Object.isFrozen(selection)) {
10655
10612
  {
10656
- throw Error(`$setSelection called on frozen selection object. Ensure selection is cloned before passing in.`);
10613
+ formatDevErrorMessage(`$setSelection called on frozen selection object. Ensure selection is cloned before passing in.`);
10657
10614
  }
10658
10615
  }
10659
10616
  }
@@ -11024,7 +10981,7 @@ function setMutatedNode(mutatedNodes, registeredNodes, mutationListeners, node,
11024
10981
  const registeredNode = registeredNodes.get(nodeType);
11025
10982
  if (registeredNode === undefined) {
11026
10983
  {
11027
- throw Error(`Type ${nodeType} not in registeredNodes`);
10984
+ formatDevErrorMessage(`Type ${nodeType} not in registeredNodes`);
11028
10985
  }
11029
10986
  }
11030
10987
  const klass = registeredNode.klass;
@@ -11109,7 +11066,7 @@ function getElementByKeyOrThrow(editor, key) {
11109
11066
  const element = editor._keyToDOMMap.get(key);
11110
11067
  if (element === undefined) {
11111
11068
  {
11112
- throw Error(`Reconciliation: could not find DOM element for node key ${key}`);
11069
+ formatDevErrorMessage(`Reconciliation: could not find DOM element for node key ${key}`);
11113
11070
  }
11114
11071
  }
11115
11072
  return element;
@@ -11227,7 +11184,7 @@ function getWindow(editor) {
11227
11184
  const windowObj = editor._window;
11228
11185
  if (windowObj === null) {
11229
11186
  {
11230
- throw Error(`window object not found`);
11187
+ formatDevErrorMessage(`window object not found`);
11231
11188
  }
11232
11189
  }
11233
11190
  return windowObj;
@@ -11265,7 +11222,7 @@ function $applyNodeReplacement(node) {
11265
11222
  const nodeType = node.constructor.getType();
11266
11223
  const registeredNode = editor._nodes.get(nodeType);
11267
11224
  if (!(registeredNode !== undefined)) {
11268
- throw Error(`$applyNodeReplacement node ${node.constructor.name} with type ${nodeType} must be registered to the editor. You can do this by passing the node class via the "nodes" array in the editor config.`);
11225
+ formatDevErrorMessage(`$applyNodeReplacement node ${node.constructor.name} with type ${nodeType} must be registered to the editor. You can do this by passing the node class via the "nodes" array in the editor config.`);
11269
11226
  }
11270
11227
  const {
11271
11228
  replace,
@@ -11276,15 +11233,15 @@ function $applyNodeReplacement(node) {
11276
11233
  const replacementNodeKlass = replacementNode.constructor;
11277
11234
  if (replaceWithKlass !== null) {
11278
11235
  if (!(replacementNode instanceof replaceWithKlass)) {
11279
- throw Error(`$applyNodeReplacement failed. Expected replacement node to be an instance of ${replaceWithKlass.name} with type ${replaceWithKlass.getType()} but returned ${replacementNodeKlass.name} with type ${replacementNodeKlass.getType()} from original node ${node.constructor.name} with type ${nodeType}`);
11236
+ formatDevErrorMessage(`$applyNodeReplacement failed. Expected replacement node to be an instance of ${replaceWithKlass.name} with type ${replaceWithKlass.getType()} but returned ${replacementNodeKlass.name} with type ${replacementNodeKlass.getType()} from original node ${node.constructor.name} with type ${nodeType}`);
11280
11237
  }
11281
11238
  } else {
11282
11239
  if (!(replacementNode instanceof node.constructor && replacementNodeKlass !== node.constructor)) {
11283
- throw Error(`$applyNodeReplacement failed. Ensure replacement node ${replacementNodeKlass.name} with type ${replacementNodeKlass.getType()} is a subclass of the original node ${node.constructor.name} with type ${nodeType}.`);
11240
+ formatDevErrorMessage(`$applyNodeReplacement failed. Ensure replacement node ${replacementNodeKlass.name} with type ${replacementNodeKlass.getType()} is a subclass of the original node ${node.constructor.name} with type ${nodeType}.`);
11284
11241
  }
11285
11242
  }
11286
11243
  if (!(replacementNode.__key !== node.__key)) {
11287
- throw Error(`$applyNodeReplacement failed. Ensure that the key argument is *not* used in your replace function (from node ${node.constructor.name} with type ${nodeType} to node ${replacementNodeKlass.name} with type ${replacementNodeKlass.getType()}), Node keys must never be re-used except by the static clone method.`);
11244
+ formatDevErrorMessage(`$applyNodeReplacement failed. Ensure that the key argument is *not* used in your replace function (from node ${node.constructor.name} with type ${nodeType} to node ${replacementNodeKlass.name} with type ${replacementNodeKlass.getType()}), Node keys must never be re-used except by the static clone method.`);
11288
11245
  }
11289
11246
  return replacementNode;
11290
11247
  }
@@ -11294,7 +11251,7 @@ function errorOnInsertTextNodeOnRoot(node, insertNode) {
11294
11251
  const parentNode = node.getParent();
11295
11252
  if ($isRootNode(parentNode) && !$isElementNode(insertNode) && !$isDecoratorNode(insertNode)) {
11296
11253
  {
11297
- throw Error(`Only element or decorator nodes can be inserted in to the root node`);
11254
+ formatDevErrorMessage(`Only element or decorator nodes can be inserted in to the root node`);
11298
11255
  }
11299
11256
  }
11300
11257
  }
@@ -11302,7 +11259,7 @@ function $getNodeByKeyOrThrow(key) {
11302
11259
  const node = $getNodeByKey(key);
11303
11260
  if (node === null) {
11304
11261
  {
11305
- throw Error(`Expected node with key ${key} to exist but it's not in the nodeMap.`);
11262
+ formatDevErrorMessage(`Expected node with key ${key} to exist but it's not in the nodeMap.`);
11306
11263
  }
11307
11264
  }
11308
11265
  return node;
@@ -11407,7 +11364,7 @@ function $splitNode(node, offset) {
11407
11364
  startNode = node;
11408
11365
  }
11409
11366
  if (!!$isRootOrShadowRoot(node)) {
11410
- throw Error(`Can not call $splitNode() on root element`);
11367
+ formatDevErrorMessage(`Can not call $splitNode() on root element`);
11411
11368
  }
11412
11369
  const recurse = currentNode => {
11413
11370
  const parent = currentNode.getParentOrThrow();
@@ -11417,7 +11374,7 @@ function $splitNode(node, offset) {
11417
11374
  const nodeToMove = currentNode === startNode && !isParentRoot ? currentNode : $copyNode(currentNode);
11418
11375
  if (isParentRoot) {
11419
11376
  if (!($isElementNode(currentNode) && $isElementNode(nodeToMove))) {
11420
- throw Error(`Children of a root must be ElementNode`);
11377
+ formatDevErrorMessage(`Children of a root must be ElementNode`);
11421
11378
  }
11422
11379
  currentNode.insertAfter(nodeToMove);
11423
11380
  return [currentNode, nodeToMove, nodeToMove];
@@ -11549,7 +11506,7 @@ function getCachedTypeToNodeMap(editorState) {
11549
11506
  return EMPTY_TYPE_TO_NODE_MAP;
11550
11507
  }
11551
11508
  if (!editorState._readOnly) {
11552
- throw Error(`getCachedTypeToNodeMap called with a writable EditorState`);
11509
+ formatDevErrorMessage(`getCachedTypeToNodeMap called with a writable EditorState`);
11553
11510
  }
11554
11511
  let typeToNodeMap = cachedNodeMaps.get(editorState);
11555
11512
  if (!typeToNodeMap) {
@@ -11596,10 +11553,10 @@ function $cloneWithProperties(latestNode) {
11596
11553
  mutableNode.afterCloneFrom(latestNode);
11597
11554
  {
11598
11555
  if (!(mutableNode.__key === latestNode.__key)) {
11599
- throw Error(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') did not return a node with the same key, make sure to specify node.__key as the last argument to the constructor`);
11556
+ formatDevErrorMessage(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') did not return a node with the same key, make sure to specify node.__key as the last argument to the constructor`);
11600
11557
  }
11601
11558
  if (!(mutableNode.__parent === latestNode.__parent && mutableNode.__next === latestNode.__next && mutableNode.__prev === latestNode.__prev)) {
11602
- throw Error(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') overrided afterCloneFrom but did not call super.afterCloneFrom(prevNode)`);
11559
+ formatDevErrorMessage(`$cloneWithProperties: ${constructor.name}.clone(node) (with type '${constructor.getType()}') overrided afterCloneFrom but did not call super.afterCloneFrom(prevNode)`);
11603
11560
  }
11604
11561
  }
11605
11562
  return mutableNode;
@@ -11631,15 +11588,6 @@ function isDOMUnmanaged(elementDom) {
11631
11588
  return el.__lexicalUnmanaged === true;
11632
11589
  }
11633
11590
 
11634
- /**
11635
- * Copyright (c) Meta Platforms, Inc. and affiliates.
11636
- *
11637
- * This source code is licensed under the MIT license found in the
11638
- * LICENSE file in the root directory of this source tree.
11639
- *
11640
- */
11641
-
11642
-
11643
11591
  /**
11644
11592
  * The direction of a caret, 'next' points towards the end of the document
11645
11593
  * and 'previous' points towards the beginning
@@ -11806,7 +11754,7 @@ class AbstractCaret {
11806
11754
  }
11807
11755
  } else {
11808
11756
  if (!(target !== null)) {
11809
- throw Error(`NodeCaret.splice: Underflow of expected nodesToRemove during splice (keys: ${Array.from(nodesToRemove).join(' ')})`);
11757
+ formatDevErrorMessage(`NodeCaret.splice: Underflow of expected nodesToRemove during splice (keys: ${Array.from(nodesToRemove).join(' ')})`);
11810
11758
  }
11811
11759
  }
11812
11760
  } else {
@@ -12084,7 +12032,7 @@ function $getTextNodeOffset(origin, offset) {
12084
12032
  const size = origin.getTextContentSize();
12085
12033
  const numericOffset = offset === 'next' ? size : offset === 'previous' ? 0 : offset;
12086
12034
  if (!(numericOffset >= 0 && numericOffset <= size)) {
12087
- throw Error(`$getTextNodeOffset: invalid offset ${String(offset)} for size ${String(size)}`);
12035
+ formatDevErrorMessage(`$getTextNodeOffset: invalid offset ${String(offset)} for size ${String(size)}`);
12088
12036
  }
12089
12037
  return numericOffset;
12090
12038
  }
@@ -12263,7 +12211,7 @@ function $extendCaretToRange(anchor) {
12263
12211
  */
12264
12212
  function $getCaretRange(anchor, focus) {
12265
12213
  if (!(anchor.direction === focus.direction)) {
12266
- throw Error(`$getCaretRange: anchor and focus must be in the same direction`);
12214
+ formatDevErrorMessage(`$getCaretRange: anchor and focus must be in the same direction`);
12267
12215
  }
12268
12216
  return new CaretRangeImpl(anchor, focus, anchor.direction);
12269
12217
  }
@@ -12341,7 +12289,7 @@ function compareNumber(a, b) {
12341
12289
  function $comparePointCaretNext(a, b) {
12342
12290
  const compare = $getCommonAncestor(a.origin, b.origin);
12343
12291
  if (!(compare !== null)) {
12344
- throw Error(`$comparePointCaretNext: a (key ${a.origin.getKey()}) and b (key ${b.origin.getKey()}) do not have a common ancestor`);
12292
+ formatDevErrorMessage(`$comparePointCaretNext: a (key ${a.origin.getKey()}) and b (key ${b.origin.getKey()}) do not have a common ancestor`);
12345
12293
  }
12346
12294
  switch (compare.type) {
12347
12295
  case 'same':
@@ -12446,7 +12394,7 @@ function $getCommonAncestor(a, b) {
12446
12394
  if (aChild === undefined) ; else if (aChild === null) {
12447
12395
  // a is the ancestor
12448
12396
  if (!$isSameNode(a, parent)) {
12449
- throw Error(`$originComparison: ancestor logic error`);
12397
+ formatDevErrorMessage(`$originComparison: ancestor logic error`);
12450
12398
  }
12451
12399
  return {
12452
12400
  commonAncestor: parent,
@@ -12455,7 +12403,7 @@ function $getCommonAncestor(a, b) {
12455
12403
  } else if (child === null) {
12456
12404
  // b is the ancestor
12457
12405
  if (!$isSameNode(b, parent)) {
12458
- throw Error(`$originComparison: descendant logic error`);
12406
+ formatDevErrorMessage(`$originComparison: descendant logic error`);
12459
12407
  }
12460
12408
  return {
12461
12409
  commonAncestor: parent,
@@ -12463,7 +12411,7 @@ function $getCommonAncestor(a, b) {
12463
12411
  };
12464
12412
  } else {
12465
12413
  if (!(($isElementNode(aChild) || $isSameNode(a, aChild)) && ($isElementNode(child) || $isSameNode(b, child)) && parent.is(aChild.getParent()) && parent.is(child.getParent()))) {
12466
- throw Error(`$originComparison: branch logic error`);
12414
+ formatDevErrorMessage(`$originComparison: branch logic error`);
12467
12415
  }
12468
12416
  return {
12469
12417
  a: aChild,
@@ -12476,15 +12424,6 @@ function $getCommonAncestor(a, b) {
12476
12424
  return null;
12477
12425
  }
12478
12426
 
12479
- /**
12480
- * Copyright (c) Meta Platforms, Inc. and affiliates.
12481
- *
12482
- * This source code is licensed under the MIT license found in the
12483
- * LICENSE file in the root directory of this source tree.
12484
- *
12485
- */
12486
-
12487
-
12488
12427
  /**
12489
12428
  * @param point
12490
12429
  * @returns a PointCaret for the point
@@ -12498,12 +12437,12 @@ function $caretFromPoint(point, direction) {
12498
12437
  const node = $getNodeByKeyOrThrow(point.key);
12499
12438
  if (type === 'text') {
12500
12439
  if (!$isTextNode(node)) {
12501
- throw Error(`$caretFromPoint: Node with type ${node.getType()} and key ${key} that does not inherit from TextNode encountered for text point`);
12440
+ formatDevErrorMessage(`$caretFromPoint: Node with type ${node.getType()} and key ${key} that does not inherit from TextNode encountered for text point`);
12502
12441
  }
12503
12442
  return $getTextPointCaret(node, direction, offset);
12504
12443
  }
12505
12444
  if (!$isElementNode(node)) {
12506
- throw Error(`$caretFromPoint: Node with type ${node.getType()} and key ${key} that does not inherit from ElementNode encountered for element point`);
12445
+ formatDevErrorMessage(`$caretFromPoint: Node with type ${node.getType()} and key ${key} that does not inherit from ElementNode encountered for element point`);
12507
12446
  }
12508
12447
  return $getChildCaretAtIndex(node, point.offset, direction);
12509
12448
  }
@@ -12530,7 +12469,7 @@ function $setPointFromCaret(point, caret) {
12530
12469
  }
12531
12470
  } else {
12532
12471
  if (!($isChildCaret(caret) && $isElementNode(origin))) {
12533
- throw Error(`$setPointFromCaret: exhaustiveness check`);
12472
+ formatDevErrorMessage(`$setPointFromCaret: exhaustiveness check`);
12534
12473
  }
12535
12474
  point.set(origin.getKey(), isNext ? 0 : origin.getChildrenSize(), 'element');
12536
12475
  }
@@ -12568,8 +12507,10 @@ function $caretRangeFromSelection(selection) {
12568
12507
  anchor,
12569
12508
  focus
12570
12509
  } = selection;
12571
- const direction = focus.isBefore(anchor) ? 'previous' : 'next';
12572
- return $getCaretRange($caretFromPoint(anchor, direction), $caretFromPoint(focus, direction));
12510
+ const anchorCaret = $caretFromPoint(anchor, 'next');
12511
+ const focusCaret = $caretFromPoint(focus, 'next');
12512
+ const direction = $comparePointCaretNext(anchorCaret, focusCaret) <= 0 ? 'next' : 'previous';
12513
+ return $getCaretRange($getCaretInDirection(anchorCaret, direction), $getCaretInDirection(focusCaret, direction));
12573
12514
  }
12574
12515
 
12575
12516
  /**
@@ -12719,7 +12660,7 @@ function $removeTextFromCaretRange(initialRange, sliceMode = 'removeEmptySlices'
12719
12660
  return $getCaretRange(anchor, anchor);
12720
12661
  }
12721
12662
  {
12722
- throw Error(`$removeTextFromCaretRange: selection was lost, could not find a new anchor given candidates with keys: ${JSON.stringify(anchorCandidates.map(n => n.origin.__key))}`);
12663
+ formatDevErrorMessage(`$removeTextFromCaretRange: selection was lost, could not find a new anchor given candidates with keys: ${JSON.stringify(anchorCandidates.map(n => n.origin.__key))}`);
12723
12664
  }
12724
12665
  }
12725
12666
 
@@ -12816,6 +12757,17 @@ function $normalizeCaret(initialCaret) {
12816
12757
  const adj = caret.getAdjacentCaret();
12817
12758
  return $isSiblingCaret(adj) && $isTextNode(adj.origin) ? $getTextPointCaret(adj.origin, direction, flipDirection(direction)) : caret;
12818
12759
  }
12760
+ /**
12761
+ * Determine whether the TextPointCaret's offset can be extended further without leaving the TextNode.
12762
+ * Returns false if the given caret is not a TextPointCaret or the offset can not be moved further in
12763
+ * direction.
12764
+ *
12765
+ * @param caret A PointCaret
12766
+ * @returns true if caret is a TextPointCaret with an offset that is not at the end of the text given the direction.
12767
+ */
12768
+ function $isExtendableTextPointCaret(caret) {
12769
+ return $isTextPointCaret(caret) && caret.offset !== $getTextNodeOffset(caret.origin, caret.direction);
12770
+ }
12819
12771
 
12820
12772
  /**
12821
12773
  * Return the caret if it's in the given direction, otherwise return
@@ -12944,6 +12896,7 @@ exports.$isBlockElementNode = $isBlockElementNode;
12944
12896
  exports.$isChildCaret = $isChildCaret;
12945
12897
  exports.$isDecoratorNode = $isDecoratorNode;
12946
12898
  exports.$isElementNode = $isElementNode;
12899
+ exports.$isExtendableTextPointCaret = $isExtendableTextPointCaret;
12947
12900
  exports.$isInlineElementOrDecoratorNode = $isInlineElementOrDecoratorNode;
12948
12901
  exports.$isLeafNode = $isLeafNode;
12949
12902
  exports.$isLineBreakNode = $isLineBreakNode;