@portabletext/editor 8.0.2 → 8.1.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/lib/index.js CHANGED
@@ -746,6 +746,40 @@ function move(editor, options = {}) {
746
746
  }
747
747
  editor.setSelection(props);
748
748
  }
749
+ function subscribeToOperations(editor, listener, options) {
750
+ let phase = options?.phase ?? "after";
751
+ return editor.operationListeners[phase] = [...editor.operationListeners[phase], listener], () => {
752
+ let listeners = editor.operationListeners[phase], index = listeners.indexOf(listener);
753
+ index !== -1 && (editor.operationListeners[phase] = [...listeners.slice(0, index), ...listeners.slice(index + 1)]);
754
+ };
755
+ }
756
+ /**
757
+ * Captures the operation together with the engine state that `after`
758
+ * listeners can no longer read once the operation has been applied. All
759
+ * engine flags are stable for the duration of a single apply call, so
760
+ * capturing them at entry is equivalent to reading them at any point during
761
+ * the apply.
762
+ */
763
+ function createOperationEvent(editor, operation) {
764
+ let isNormalizingNode = !!editor.isNormalizingNode, isProcessingRemoteChanges = !!editor.isProcessingRemoteChanges, isUndoing = !!editor.isUndoing, isRedoing = !!editor.isRedoing;
765
+ return {
766
+ operation,
767
+ beforeValue: editor.snapshot.context.value,
768
+ beforeSelection: editor.snapshot.context.selection,
769
+ operationsInProgress: editor.operations.length > 0,
770
+ isNormalizingNode,
771
+ isPatching: !!editor.isPatching,
772
+ isProcessingRemoteChanges,
773
+ isUndoing,
774
+ isRedoing,
775
+ withHistory: !!editor.withHistory,
776
+ undoStepId: editor.undoStepId,
777
+ origin: isProcessingRemoteChanges ? "remote" : isUndoing ? "undo" : isRedoing ? "redo" : isNormalizingNode ? "normalization" : "local"
778
+ };
779
+ }
780
+ function emitOperationEvent(listeners, event) {
781
+ for (let index = 0; index < listeners.length; index++) listeners[index]?.(event);
782
+ }
749
783
  const bold$1 = createKeyboardShortcut({
750
784
  default: [{
751
785
  key: "B",
@@ -1179,13 +1213,14 @@ function mapPointThroughStep(step, point, options = {}) {
1179
1213
  };
1180
1214
  }
1181
1215
  case "rekey": {
1182
- let path;
1183
- for (let i = 0; i < point.path.length; i++) {
1184
- let segment = point.path[i];
1185
- isKeyedSegment(segment) && segment._key === step.oldKey && (path === void 0 && (path = [...point.path]), path[i] = { _key: step.newKey });
1186
- }
1187
- return path === void 0 ? point : {
1188
- path,
1216
+ if (point.path.length <= step.path.length || !pathEquals(point.path.slice(0, step.path.length), step.path)) return point;
1217
+ let segment = point.path[step.path.length];
1218
+ return !isKeyedSegment(segment) || segment._key !== step.oldKey ? point : {
1219
+ path: [
1220
+ ...step.path,
1221
+ { _key: step.newKey },
1222
+ ...point.path.slice(step.path.length + 1)
1223
+ ],
1189
1224
  offset: point.offset
1190
1225
  };
1191
1226
  }
@@ -1303,6 +1338,7 @@ function operationToSteps(op) {
1303
1338
  let oldKey = op.inverse?.type === "set" && typeof op.inverse.value == "string" ? op.inverse.value : void 0;
1304
1339
  return oldKey ? [{
1305
1340
  type: "rekey",
1341
+ path: nodePath.slice(0, -1),
1306
1342
  oldKey,
1307
1343
  newKey: op.value
1308
1344
  }] : [];
@@ -2787,9 +2823,12 @@ const RestoreDOM = IS_ANDROID ? RestoreDOMComponent : ({ children }) => /* @__PU
2787
2823
  editor.forceRender = forceRender, editor.readOnly = readOnly;
2788
2824
  let state = useMemo(() => ({
2789
2825
  isUpdatingSelection: !1,
2790
- latestElement: null
2826
+ latestElement: null,
2827
+ localContentChangePending: !1
2791
2828
  }), []);
2792
- useEffect(() => {
2829
+ useEffect(() => subscribeToOperations(editor, (event) => {
2830
+ event.operation.type !== "set.selection" && (event.origin === "local" || event.origin === "undo" || event.origin === "redo") && (state.localContentChangePending = !0);
2831
+ }), [editor, state]), useEffect(() => {
2793
2832
  ref.current && autoFocus && ref.current.focus();
2794
2833
  }, [autoFocus]);
2795
2834
  /**
@@ -2844,6 +2883,8 @@ const RestoreDOM = IS_ANDROID ? RestoreDOMComponent : ({ children }) => /* @__PU
2844
2883
  onDOMSelectionChange,
2845
2884
  scheduleOnDOMSelectionChange
2846
2885
  }), useIsomorphicLayoutEffect(() => {
2886
+ let hadPendingLocalContentChange = state.localContentChangePending;
2887
+ state.localContentChangePending = !1;
2847
2888
  let window = null;
2848
2889
  ref.current && (window = getDefaultView(ref.current)) && (editor.domWindow = window, editor.domElement = ref.current);
2849
2890
  let { selection } = editor.snapshot.context, root = DOMEditor.findDocumentOrShadowRoot(editor), domSelection = getSelection(root);
@@ -2862,7 +2903,13 @@ const RestoreDOM = IS_ANDROID ? RestoreDOMComponent : ({ children }) => /* @__PU
2862
2903
  exactMatch: !0,
2863
2904
  suppressThrow: !0
2864
2905
  });
2865
- if (editorSelection && rangeEquals(editorSelection, selection)) return;
2906
+ if (editorSelection && rangeEquals(editorSelection, selection)) {
2907
+ if (hadPendingLocalContentChange) try {
2908
+ let domRange = DOMEditor.toDOMRange(editor, selection);
2909
+ scrollSelectionIntoView(editor, domRange);
2910
+ } catch {}
2911
+ return;
2912
+ }
2866
2913
  }
2867
2914
  if (selection && !DOMEditor.hasRange(editor, selection)) {
2868
2915
  let fallbackRange = DOMEditor.toEditorSelection(editor, domSelection, {
@@ -4114,40 +4161,6 @@ function performHotkey({ editorActor, editor, portableTextEditor, hotkeys, event
4114
4161
  }
4115
4162
  });
4116
4163
  }
4117
- function subscribeToOperations(editor, listener, options) {
4118
- let phase = options?.phase ?? "after";
4119
- return editor.operationListeners[phase] = [...editor.operationListeners[phase], listener], () => {
4120
- let listeners = editor.operationListeners[phase], index = listeners.indexOf(listener);
4121
- index !== -1 && (editor.operationListeners[phase] = [...listeners.slice(0, index), ...listeners.slice(index + 1)]);
4122
- };
4123
- }
4124
- /**
4125
- * Captures the operation together with the engine state that `after`
4126
- * listeners can no longer read once the operation has been applied. All
4127
- * engine flags are stable for the duration of a single apply call, so
4128
- * capturing them at entry is equivalent to reading them at any point during
4129
- * the apply.
4130
- */
4131
- function createOperationEvent(editor, operation) {
4132
- let isNormalizingNode = !!editor.isNormalizingNode, isProcessingRemoteChanges = !!editor.isProcessingRemoteChanges, isUndoing = !!editor.isUndoing, isRedoing = !!editor.isRedoing;
4133
- return {
4134
- operation,
4135
- beforeValue: editor.snapshot.context.value,
4136
- beforeSelection: editor.snapshot.context.selection,
4137
- operationsInProgress: editor.operations.length > 0,
4138
- isNormalizingNode,
4139
- isPatching: !!editor.isPatching,
4140
- isProcessingRemoteChanges,
4141
- isUndoing,
4142
- isRedoing,
4143
- withHistory: !!editor.withHistory,
4144
- undoStepId: editor.undoStepId,
4145
- origin: isProcessingRemoteChanges ? "remote" : isUndoing ? "undo" : isRedoing ? "redo" : isNormalizingNode ? "normalization" : "local"
4146
- };
4147
- }
4148
- function emitOperationEvent(listeners, event) {
4149
- for (let index = 0; index < listeners.length; index++) listeners[index]?.(event);
4150
- }
4151
4164
  function isForwardRange(range, root) {
4152
4165
  return !isBackwardRange(range, root);
4153
4166
  }
@@ -4190,7 +4203,12 @@ const engineOperationCallback = ({ input, sendBack }) => subscribeToOperations(i
4190
4203
  operation: event.operation,
4191
4204
  origin: event.origin
4192
4205
  });
4193
- }, { phase: "before" }), rangeDecorationsMachine = setup({
4206
+ }, { phase: "before" });
4207
+ function mergeRangeDecoration(leaf, decoration) {
4208
+ let { rangeDecoration } = decoration;
4209
+ leaf.rangeDecorations = [...leaf.rangeDecorations ?? [], rangeDecoration];
4210
+ }
4211
+ const rangeDecorationsMachine = setup({
4194
4212
  types: {
4195
4213
  context: {},
4196
4214
  input: {},
@@ -4211,6 +4229,7 @@ const engineOperationCallback = ({ input, sendBack }) => subscribeToOperations(i
4211
4229
  }
4212
4230
  rangeDecorationState.push({
4213
4231
  rangeDecoration,
4232
+ merge: mergeRangeDecoration,
4214
4233
  ...rangeDecoration.selection
4215
4234
  });
4216
4235
  }
@@ -4230,6 +4249,7 @@ const engineOperationCallback = ({ input, sendBack }) => subscribeToOperations(i
4230
4249
  }
4231
4250
  rangeDecorationState.push({
4232
4251
  rangeDecoration,
4252
+ merge: mergeRangeDecoration,
4233
4253
  ...rangeDecoration.selection
4234
4254
  });
4235
4255
  }
@@ -4258,7 +4278,8 @@ const engineOperationCallback = ({ input, sendBack }) => subscribeToOperations(i
4258
4278
  rangeDecoration: {
4259
4279
  ...decoratedRange.rangeDecoration,
4260
4280
  selection: newRange || currentSelection
4261
- }
4281
+ },
4282
+ merge: mergeRangeDecoration
4262
4283
  });
4263
4284
  }
4264
4285
  context.editorEngine.decoratedRanges = rangeDecorationState;
@@ -5166,29 +5187,33 @@ const PLACEHOLDER_STYLE = {
5166
5187
  right: 0
5167
5188
  };
5168
5189
  function RenderLeaf(props) {
5169
- let $ = c(12), schema = props.schema;
5190
+ let $ = c(16), schema = props.schema;
5170
5191
  if (props.leaf._type !== schema.span.name) return props.children;
5171
5192
  let t0;
5172
5193
  $[0] === props ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(RenderSpan, { ...props }), $[0] = props, $[1] = t0);
5173
- let renderedSpan = t0;
5174
- if (props.renderPlaceholder && props.leaf.placeholder && props.text.text === "") {
5175
- let t1;
5176
- $[2] === props.renderPlaceholder ? t1 = $[3] : (t1 = props.renderPlaceholder(), $[2] = props.renderPlaceholder, $[3] = t1);
5177
- let t2;
5178
- $[4] === t1 ? t2 = $[5] : (t2 = /* @__PURE__ */ jsx("span", {
5179
- style: PLACEHOLDER_STYLE,
5180
- contentEditable: !1,
5181
- children: t1
5182
- }), $[4] = t1, $[5] = t2);
5183
- let t3;
5184
- return $[6] !== renderedSpan || $[7] !== t2 ? (t3 = /* @__PURE__ */ jsxs(Fragment, { children: [t2, renderedSpan] }), $[6] = renderedSpan, $[7] = t2, $[8] = t3) : t3 = $[8], t3;
5185
- }
5186
- let rangeDecoration = props.leaf.rangeDecoration;
5187
- if (rangeDecoration) {
5188
- let t1;
5189
- $[9] !== rangeDecoration || $[10] !== renderedSpan ? (t1 = rangeDecoration.component({ children: renderedSpan }), $[9] = rangeDecoration, $[10] = renderedSpan, $[11] = t1) : t1 = $[11], renderedSpan = t1;
5190
- }
5191
- return renderedSpan;
5194
+ let renderedSpan, t1;
5195
+ if ($[2] !== props.leaf.placeholder || $[3] !== props.leaf.rangeDecorations || $[4] !== props.renderPlaceholder || $[5] !== props.text || $[6] !== t0) {
5196
+ t1 = Symbol.for("react.early_return_sentinel");
5197
+ bb0: {
5198
+ if (renderedSpan = t0, props.renderPlaceholder && props.leaf.placeholder && props.text.text === "") {
5199
+ let t2;
5200
+ $[9] === props.renderPlaceholder ? t2 = $[10] : (t2 = props.renderPlaceholder(), $[9] = props.renderPlaceholder, $[10] = t2);
5201
+ let t3;
5202
+ $[11] === t2 ? t3 = $[12] : (t3 = /* @__PURE__ */ jsx("span", {
5203
+ style: PLACEHOLDER_STYLE,
5204
+ contentEditable: !1,
5205
+ children: t2
5206
+ }), $[11] = t2, $[12] = t3);
5207
+ let t4;
5208
+ $[13] !== renderedSpan || $[14] !== t3 ? (t4 = /* @__PURE__ */ jsxs(Fragment, { children: [t3, renderedSpan] }), $[13] = renderedSpan, $[14] = t3, $[15] = t4) : t4 = $[15], t1 = t4;
5209
+ break bb0;
5210
+ }
5211
+ let rangeDecorations = props.leaf.rangeDecorations;
5212
+ if (rangeDecorations) for (let rangeDecoration of [...rangeDecorations].reverse()) renderedSpan = rangeDecoration.component({ children: renderedSpan });
5213
+ }
5214
+ $[2] = props.leaf.placeholder, $[3] = props.leaf.rangeDecorations, $[4] = props.renderPlaceholder, $[5] = props.text, $[6] = t0, $[7] = renderedSpan, $[8] = t1;
5215
+ } else renderedSpan = $[7], t1 = $[8];
5216
+ return t1 === Symbol.for("react.early_return_sentinel") ? renderedSpan : t1;
5192
5217
  }
5193
5218
  function RenderText(props) {
5194
5219
  let $ = c(3), t0;
@@ -6892,13 +6917,17 @@ function applyOperation(editor, op) {
6892
6917
  case "set": {
6893
6918
  let { path, value } = op;
6894
6919
  if (!op.inverse && !editor.isProcessingRemoteChanges) {
6895
- let previousValue = getValue(editor.snapshot.context.value, path);
6920
+ let previousValue = getValue(editor.snapshot.context.value, path), inversePath = path[path.length - 1] === "_key" && typeof value == "string" && isKeyedSegment(path[path.length - 2]) ? [
6921
+ ...path.slice(0, -2),
6922
+ { _key: value },
6923
+ "_key"
6924
+ ] : path;
6896
6925
  op.inverse = previousValue === void 0 ? {
6897
6926
  type: "unset",
6898
- path
6927
+ path: inversePath
6899
6928
  } : {
6900
6929
  type: "set",
6901
- path,
6930
+ path: inversePath,
6902
6931
  value: previousValue
6903
6932
  };
6904
6933
  }
@@ -7537,8 +7566,7 @@ function applyMergeNode(editor, path, position) {
7537
7566
  if (!prevEntry || !isTextBlock({ schema: editor.snapshot.context.schema }, prevEntry.node)) return;
7538
7567
  let prevChildren = prevEntry.node.children, lastInsertedKey = prevChildren.length > 0 ? prevChildren[prevChildren.length - 1]._key : void 0;
7539
7568
  for (let i = 0; i < node.children.length; i++) {
7540
- let child = node.children[i];
7541
- lastInsertedKey === void 0 ? editor.apply({
7569
+ let child = node.children[i], operation = lastInsertedKey === void 0 ? {
7542
7570
  type: "insert",
7543
7571
  path: [
7544
7572
  ...prevPath,
@@ -7547,7 +7575,7 @@ function applyMergeNode(editor, path, position) {
7547
7575
  ],
7548
7576
  node: child,
7549
7577
  position: "before"
7550
- }) : editor.apply({
7578
+ } : {
7551
7579
  type: "insert",
7552
7580
  path: [
7553
7581
  ...prevPath,
@@ -7556,7 +7584,8 @@ function applyMergeNode(editor, path, position) {
7556
7584
  ],
7557
7585
  node: child,
7558
7586
  position: "after"
7559
- }), lastInsertedKey = child._key;
7587
+ };
7588
+ editor.apply(operation), lastInsertedKey = operation.node._key;
7560
7589
  }
7561
7590
  editor.apply({
7562
7591
  type: "unset",
@@ -7675,23 +7704,20 @@ function setNodeProperties(editor, props, path) {
7675
7704
  keyIndex !== -1 && (keys.splice(keyIndex, 1), keys.unshift("_key"));
7676
7705
  let currentPath = path;
7677
7706
  for (let key of keys) if (propsRecord[key] !== nodeRecord[key]) if (propsRecord[key] != null) {
7678
- let hadProperty = nodeRecord.hasOwnProperty(key);
7679
- if (editor.apply({
7707
+ let hadProperty = nodeRecord.hasOwnProperty(key), lastSegment = currentPath[currentPath.length - 1], renamedPath = key === "_key" && typeof propsRecord[key] == "string" && isKeyedSegment(lastSegment) ? [...currentPath.slice(0, -1), { _key: propsRecord[key] }] : void 0, inversePath = renamedPath ? [...renamedPath, key] : [...currentPath, key];
7708
+ editor.apply({
7680
7709
  type: "set",
7681
7710
  path: [...currentPath, key],
7682
7711
  value: propsRecord[key],
7683
7712
  inverse: hadProperty ? {
7684
7713
  type: "set",
7685
- path: [...currentPath, key],
7714
+ path: inversePath,
7686
7715
  value: nodeRecord[key]
7687
7716
  } : {
7688
7717
  type: "unset",
7689
- path: [...currentPath, key]
7718
+ path: inversePath
7690
7719
  }
7691
- }), key === "_key" && typeof propsRecord[key] == "string") {
7692
- let lastSegment = currentPath[currentPath.length - 1];
7693
- isKeyedSegment(lastSegment) && (currentPath = [...currentPath.slice(0, -1), { _key: propsRecord[key] }]);
7694
- }
7720
+ }), renamedPath && (currentPath = renamedPath);
7695
7721
  } else nodeRecord.hasOwnProperty(key) && editor.apply({
7696
7722
  type: "unset",
7697
7723
  path: [...currentPath, key],
@@ -11607,6 +11633,63 @@ function fieldAcceptsTextBlock(editor, node, _path) {
11607
11633
  return container ? container.field.of.some((member) => member.type === editor.snapshot.context.schema.block.name) : !1;
11608
11634
  }
11609
11635
  /**
11636
+ * A block merge folds `mergingBlock`'s children (and markDefs) into
11637
+ * `destinationBlock` by key. Any child key the merging block shares with
11638
+ * the destination has to be renamed before the merge, or the engine's own
11639
+ * collision handling mints a fresh key for it, which reads on the wire as
11640
+ * that node being destroyed and a new one created instead of moved. There
11641
+ * is no such backstop for markDefs: an unrenamed collision either loses
11642
+ * one of the two defs to last-wins replacement, or, on the `insert.block`
11643
+ * path, gets silently re-minted by `adjustFragmentKeys` (see
11644
+ * `dedupeEqualMarkDefs` below).
11645
+ *
11646
+ * Pure: computes the renames and the block they produce without touching
11647
+ * an editor. Callers raise or apply the renames themselves, ahead of the
11648
+ * merge, against the still-living `mergingBlock`.
11649
+ */
11650
+ function planMergeKeyRenames(args) {
11651
+ let { context, mergingBlock, destinationBlock, dedupeEqualMarkDefs } = args, destinationChildKeys = new Set(destinationBlock.children.map((child) => child._key)), destinationMarkDefsByKey = new Map((destinationBlock.markDefs ?? []).map((markDef) => [markDef._key, markDef])), markDefKeyMap = /* @__PURE__ */ new Map(), renamedMarkDefs = mergingBlock.markDefs?.flatMap((markDef) => {
11652
+ let destinationMarkDef = destinationMarkDefsByKey.get(markDef._key);
11653
+ if (!destinationMarkDef) return [markDef];
11654
+ if (dedupeEqualMarkDefs && isDeepEqual(markDef, destinationMarkDef)) return [];
11655
+ let newKey = context.keyGenerator();
11656
+ return markDefKeyMap.set(markDef._key, newKey), [{
11657
+ ...markDef,
11658
+ _key: newKey
11659
+ }];
11660
+ }), markDefRenames = [];
11661
+ for (let markDef of mergingBlock.markDefs ?? []) {
11662
+ let newKey = markDefKeyMap.get(markDef._key);
11663
+ newKey && markDefRenames.push({
11664
+ markDefKey: markDef._key,
11665
+ newKey
11666
+ });
11667
+ }
11668
+ let childRenames = [], renamedChildren = mergingBlock.children.map((child) => {
11669
+ let currentMarks = isSpan(context, child) ? child.marks : void 0, remappedMarks = currentMarks?.map((mark) => markDefKeyMap.get(mark) ?? mark), marksChanged = !!(remappedMarks && !isEqualMarks(remappedMarks, currentMarks)), newKey = destinationChildKeys.has(child._key) ? context.keyGenerator() : child._key, props = {};
11670
+ return newKey !== child._key && (props._key = newKey), marksChanged && (props.marks = remappedMarks), Object.keys(props).length > 0 && childRenames.push({
11671
+ childKey: child._key,
11672
+ props
11673
+ }), marksChanged ? {
11674
+ ...child,
11675
+ _key: newKey,
11676
+ marks: remappedMarks
11677
+ } : {
11678
+ ...child,
11679
+ _key: newKey
11680
+ };
11681
+ });
11682
+ return {
11683
+ renamedBlock: {
11684
+ ...mergingBlock,
11685
+ children: renamedChildren,
11686
+ ...mergingBlock.markDefs ? { markDefs: renamedMarkDefs } : {}
11687
+ },
11688
+ childRenames,
11689
+ markDefRenames
11690
+ };
11691
+ }
11692
+ /**
11610
11693
  * Indic scripts whose grapheme clusters span multiple code points. Backward
11611
11694
  * character delete on these scripts removes one code point at a time instead
11612
11695
  * of the entire cluster. Besides the script ranges, the class lists the BMP
@@ -11939,12 +12022,29 @@ function mergeBlock(editor, startBlockPath, endBlockPath, removeEmptyStartBlock)
11939
12022
  removeNodeAt(editor, startBlockPath);
11940
12023
  return;
11941
12024
  }
11942
- let endMarkDefs = endBlock.node.markDefs;
11943
- if (Array.isArray(endMarkDefs) && endMarkDefs.length > 0) {
11944
- let oldDefs = startBlock.node.markDefs ?? [];
11945
- setNodeProperties(editor, { markDefs: [...new Map([...oldDefs, ...endMarkDefs].map((def) => [def._key, def])).values()] }, startBlockPath);
11946
- }
11947
- applyMergeNode(editor, endBlockPath, startBlock.node.children.length);
12025
+ let { renamedBlock, childRenames, markDefRenames } = planMergeKeyRenames({
12026
+ context: editor.snapshot.context,
12027
+ mergingBlock: endBlock.node,
12028
+ destinationBlock: startBlock.node,
12029
+ dedupeEqualMarkDefs: !0
12030
+ });
12031
+ for (let { markDefKey, newKey } of markDefRenames) editor.apply({
12032
+ type: "set",
12033
+ path: [
12034
+ ...endBlockPath,
12035
+ "markDefs",
12036
+ { _key: markDefKey },
12037
+ "_key"
12038
+ ],
12039
+ value: newKey
12040
+ });
12041
+ for (let { childKey, props } of childRenames) setNodeProperties(editor, props, [
12042
+ ...endBlockPath,
12043
+ "children",
12044
+ { _key: childKey }
12045
+ ]);
12046
+ let endMarkDefs = renamedBlock.markDefs;
12047
+ Array.isArray(endMarkDefs) && endMarkDefs.length > 0 && setNodeProperties(editor, { markDefs: [...startBlock.node.markDefs ?? [], ...endMarkDefs] }, startBlockPath), applyMergeNode(editor, endBlockPath, startBlock.node.children.length);
11948
12048
  }
11949
12049
  function removeNodeAt(editor, path) {
11950
12050
  getNode(editor.snapshot, path) && editor.apply({
@@ -13132,16 +13232,24 @@ const abstractAnnotationBehaviors = [
13132
13232
  let previousBlock = {
13133
13233
  node: previousSibling.node,
13134
13234
  path: previousSibling.path
13135
- };
13235
+ }, previousBlockEndPoint = getBlockEndPoint({
13236
+ context: snapshot.context,
13237
+ block: previousBlock
13238
+ }), { renamedBlock, renameActions } = planMergeKeyRenameActions({
13239
+ context: snapshot.context,
13240
+ mergingBlockPath: focusTextBlock.path,
13241
+ mergingBlock: focusTextBlock.node,
13242
+ destinationBlock: previousBlock.node
13243
+ });
13136
13244
  return {
13137
- previousBlockEndPoint: getBlockEndPoint({
13138
- context: snapshot.context,
13139
- block: previousBlock
13140
- }),
13141
- focusTextBlock
13245
+ previousBlockEndPoint,
13246
+ focusTextBlock,
13247
+ renamedBlock,
13248
+ renameActions
13142
13249
  };
13143
13250
  },
13144
- actions: [(_, { previousBlockEndPoint, focusTextBlock }) => [
13251
+ actions: [(_, { previousBlockEndPoint, focusTextBlock, renamedBlock, renameActions }) => [
13252
+ ...renameActions,
13145
13253
  raise$1({
13146
13254
  type: "delete.block",
13147
13255
  at: focusTextBlock.path
@@ -13155,7 +13263,7 @@ const abstractAnnotationBehaviors = [
13155
13263
  }),
13156
13264
  raise$1({
13157
13265
  type: "insert.block",
13158
- block: focusTextBlock.node,
13266
+ block: renamedBlock,
13159
13267
  placement: "auto",
13160
13268
  select: "start"
13161
13269
  })
@@ -13224,20 +13332,32 @@ const abstractAnnotationBehaviors = [
13224
13332
  }, focusTextBlock = getFocusTextBlock(adjustedSnapshot);
13225
13333
  if (!focusTextBlock) return !1;
13226
13334
  let nextSibling = getSibling(adjustedSnapshot, focusTextBlock.path, { direction: "next" });
13227
- return !nextSibling || !isAtTheEndOfBlock(focusTextBlock)(adjustedSnapshot) || !isTextBlockNode(snapshot.context, nextSibling.node) ? !1 : { nextBlock: {
13228
- node: nextSibling.node,
13229
- path: nextSibling.path
13230
- } };
13335
+ if (!nextSibling || !isAtTheEndOfBlock(focusTextBlock)(adjustedSnapshot) || !isTextBlock(snapshot.context, nextSibling.node)) return !1;
13336
+ let { renamedBlock, renameActions } = planMergeKeyRenameActions({
13337
+ context: snapshot.context,
13338
+ mergingBlockPath: nextSibling.path,
13339
+ mergingBlock: nextSibling.node,
13340
+ destinationBlock: focusTextBlock.node
13341
+ });
13342
+ return {
13343
+ nextBlockPath: nextSibling.path,
13344
+ renamedBlock,
13345
+ renameActions
13346
+ };
13231
13347
  },
13232
- actions: [(_, { nextBlock }) => [raise$1({
13233
- type: "delete.block",
13234
- at: nextBlock.path
13235
- }), raise$1({
13236
- type: "insert.block",
13237
- block: nextBlock.node,
13238
- placement: "auto",
13239
- select: "none"
13240
- })]]
13348
+ actions: [(_, { nextBlockPath, renamedBlock, renameActions }) => [
13349
+ ...renameActions,
13350
+ raise$1({
13351
+ type: "delete.block",
13352
+ at: nextBlockPath
13353
+ }),
13354
+ raise$1({
13355
+ type: "insert.block",
13356
+ block: renamedBlock,
13357
+ placement: "auto",
13358
+ select: "none"
13359
+ })
13360
+ ]]
13241
13361
  }),
13242
13362
  defineBehavior({
13243
13363
  on: "delete.block",
@@ -13307,7 +13427,44 @@ const abstractAnnotationBehaviors = [
13307
13427
  type: "delete"
13308
13428
  })]]
13309
13429
  })
13310
- ], mimeTypePriority = [
13430
+ ];
13431
+ /**
13432
+ * Raise the child/markDef renames `planMergeKeyRenames` computed as
13433
+ * `set`/`child.set` events against the still-living `mergingBlock`, so
13434
+ * they land on the wire before the merge's `unset`.
13435
+ */
13436
+ function planMergeKeyRenameActions(args) {
13437
+ let { context, mergingBlockPath, mergingBlock, destinationBlock } = args, { renamedBlock, childRenames, markDefRenames } = planMergeKeyRenames({
13438
+ context,
13439
+ mergingBlock,
13440
+ destinationBlock,
13441
+ dedupeEqualMarkDefs: !1
13442
+ }), renameActions = [];
13443
+ for (let { markDefKey, newKey } of markDefRenames) renameActions.push(raise$1({
13444
+ type: "set",
13445
+ at: [
13446
+ ...mergingBlockPath,
13447
+ "markDefs",
13448
+ { _key: markDefKey },
13449
+ "_key"
13450
+ ],
13451
+ value: newKey
13452
+ }));
13453
+ for (let { childKey, props } of childRenames) renameActions.push(raise$1({
13454
+ type: "child.set",
13455
+ at: [
13456
+ ...mergingBlockPath,
13457
+ "children",
13458
+ { _key: childKey }
13459
+ ],
13460
+ props
13461
+ }));
13462
+ return {
13463
+ renamedBlock,
13464
+ renameActions
13465
+ };
13466
+ }
13467
+ const mimeTypePriority = [
13311
13468
  "application/x-portable-text",
13312
13469
  "application/json",
13313
13470
  "text/markdown",
@@ -16455,7 +16612,8 @@ function createActors(config) {
16455
16612
  return config.subscriptions.push(mutationBatcher.subscribe), config.subscriptions.push(() => subscribeToOperations(config.editorEngine, (event) => {
16456
16613
  isPublicOperation(event.operation) && config.relay.send({
16457
16614
  type: "operation",
16458
- operation: event.operation
16615
+ operation: event.operation,
16616
+ origin: event.origin === "remote" ? "remote" : "local"
16459
16617
  });
16460
16618
  })), config.subscriptions.push(() => {
16461
16619
  let subscription = syncActor.on("*", (event) => {