@stll/folio-core 0.35.0 → 0.36.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/dist/ai-edits/apply.js +370 -118
- package/dist/ai-edits/headless.js +16 -10
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/snapshot.js +6 -1
- package/dist/ai-edits/types.d.ts +19 -6
- package/dist/ai-edits/word-diff.d.ts +18 -3
- package/dist/ai-edits/word-diff.js +556 -67
- package/dist/compare/compare.js +7 -16
- package/dist/compare/plan.js +8 -5
- package/dist/compare/verification.d.ts +8 -9
- package/dist/compare/verification.js +51 -46
- package/dist/compat/eigenpal.d.ts +2 -2
- package/dist/compat/eigenpal.js +2 -2
- package/dist/controller/headerFooterEditorManager.js +5 -5
- package/dist/controller/hiddenEditorApi.js +1 -1
- package/dist/controller/noteEditorManager.js +2 -2
- package/dist/document-operations.d.ts +5 -3
- package/dist/document-operations.js +32 -5
- package/dist/docx/commentParser.js +2 -4
- package/dist/docx/commentRangeIntegrity.js +2 -4
- package/dist/docx/documentClone.d.ts +2 -0
- package/dist/docx/documentClone.js +2 -0
- package/dist/docx/headerFooterParser.js +2 -4
- package/dist/docx/normalizeBaseDirection.js +5 -7
- package/dist/docx/paragraphParser.js +27 -29
- package/dist/docx/paragraphPropertySource.d.ts +49 -0
- package/dist/docx/paragraphPropertySource.js +113 -0
- package/dist/docx/runConsolidator.js +3 -4
- package/dist/docx/runParser.js +2 -15
- package/dist/docx/sectionParser.js +1 -15
- package/dist/docx/serializer/paragraphSerializer.js +287 -30
- package/dist/docx/serializer/runSerializer.js +2 -10
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -10
- package/dist/docx/serializer/tableSerializer.js +4 -23
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +9 -0
- package/dist/docx/serializer/trackedChangeAttributes.js +21 -0
- package/dist/docx/server/createBilingualDocument.js +8 -10
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +22 -13
- package/dist/docx/server/validateDocxConformance.js +17 -13
- package/dist/docx/tableParser.js +1 -37
- package/dist/docx/trackedChangeInfo.d.ts +11 -0
- package/dist/docx/trackedChangeInfo.js +38 -0
- package/dist/docx/verbatimCapture.d.ts +20 -9
- package/dist/docx/verbatimCapture.js +208 -8
- package/dist/docx/xmlParser.d.ts +3 -1
- package/dist/docx/xmlParser.js +6 -1
- package/dist/docx/xmlSafety.d.ts +1 -1
- package/dist/docx/xmlSafety.js +157 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/layout-bridge/convert/headerFooterLayout.js +4 -4
- package/dist/layout-bridge/convert/toFlowBlocks.js +12 -7
- package/dist/markdown/renderBlock.js +7 -10
- package/dist/prosemirror/attrs/index.js +15 -0
- package/dist/prosemirror/commands/comments.js +365 -32
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +26 -4
- package/dist/prosemirror/commands/propertyChangeScope.js +65 -3
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -4
- package/dist/prosemirror/conversion/fromProseDoc.js +130 -18
- package/dist/prosemirror/conversion/toProseDoc.js +27 -12
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +48 -4
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +20 -7
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/paragraphAlignment.d.ts +12 -0
- package/dist/prosemirror/paragraphAlignment.js +13 -0
- package/dist/prosemirror/plugins/suggestionMode.js +1 -1
- package/dist/prosemirror/schema/marks.d.ts +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +12 -1
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/redline.js +4 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/utils/mergeDocumentContent.js +3 -3
- package/dist/utils/replaceText.js +2 -1
- package/dist/version-comparison.js +6 -4
- package/dist/watermark/index.js +2 -4
- package/package.json +7 -2
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { paragraphPropertiesSnapshot } from "../prosemirror/commands/propertyChangeScope.js";
|
|
3
3
|
import { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer } from "../prosemirror/containerFinalParagraph.js";
|
|
4
4
|
import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
|
|
5
5
|
import { requestDeterministicParaIds } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
6
6
|
import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
7
|
+
import { directParagraphAlignment } from "../prosemirror/paragraphAlignment.js";
|
|
8
|
+
import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyles.js";
|
|
7
9
|
import { isZeroWidthAnchor } from "../prosemirror/zeroWidthAnchors.js";
|
|
8
10
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
9
11
|
import { stripBlockIdentityAttrs } from "./block-identity.js";
|
|
@@ -15,11 +17,32 @@ import { planTableMutations } from "./table-mutation-plan.js";
|
|
|
15
17
|
import { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDeletion, applyTableRowInsertion, findTableColumnInsertion, findTableRowInsertion, getTableColumnCoordinateKey, markTableRowContent, splitCellParagraphTexts } from "./table-row-column-mutations.js";
|
|
16
18
|
import { findEnclosingTableCell, findEnclosingTableRow, findOutermostTableBoundary, tableRectangleCutsMergedCell } from "./table-targets.js";
|
|
17
19
|
import { tableFromTemplate } from "./table-template.js";
|
|
18
|
-
import {
|
|
20
|
+
import { wordDiffSessionFromOptions } from "./word-diff.js";
|
|
19
21
|
import { panic } from "better-result";
|
|
20
22
|
import { TableMap } from "prosemirror-tables";
|
|
21
23
|
import { canJoin, canSplit } from "prosemirror-transform";
|
|
22
24
|
//#region src/ai-edits/apply.ts
|
|
25
|
+
const coarseWordDiff = (before, after) => {
|
|
26
|
+
if (before === after) return before.length === 0 ? [] : [{
|
|
27
|
+
type: "equal",
|
|
28
|
+
text: before
|
|
29
|
+
}];
|
|
30
|
+
if (before.length === 0) return [{
|
|
31
|
+
type: "ins",
|
|
32
|
+
text: after
|
|
33
|
+
}];
|
|
34
|
+
if (after.length === 0) return [{
|
|
35
|
+
type: "del",
|
|
36
|
+
text: before
|
|
37
|
+
}];
|
|
38
|
+
return [{
|
|
39
|
+
type: "del",
|
|
40
|
+
text: before
|
|
41
|
+
}, {
|
|
42
|
+
type: "ins",
|
|
43
|
+
text: after
|
|
44
|
+
}];
|
|
45
|
+
};
|
|
23
46
|
/**
|
|
24
47
|
* Operation types applied in `"suggested"` mode. Every produced revision — an
|
|
25
48
|
* inline mark, a whole-node `_suggestedInsert` marker, or a suggested
|
|
@@ -31,6 +54,7 @@ const SUGGESTED_SUPPORTED_OPERATION_TYPES = /* @__PURE__ */ new Set([
|
|
|
31
54
|
"replaceInBlock",
|
|
32
55
|
"replaceRange",
|
|
33
56
|
"formatRange",
|
|
57
|
+
"setBlockParagraphProperties",
|
|
34
58
|
"replaceBlock",
|
|
35
59
|
"deleteBlock",
|
|
36
60
|
"insertAfterBlock",
|
|
@@ -56,16 +80,44 @@ const CLEARED_LIST_MARKER_ATTRS = Object.freeze({
|
|
|
56
80
|
listAbstractNumId: null,
|
|
57
81
|
listStartOverride: null
|
|
58
82
|
});
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
const REPLACE_BLOCK_IMPACT = {
|
|
84
|
+
text: {
|
|
85
|
+
changesStyle: false,
|
|
86
|
+
changesText: true
|
|
87
|
+
},
|
|
88
|
+
style: {
|
|
89
|
+
changesStyle: true,
|
|
90
|
+
changesText: false
|
|
91
|
+
},
|
|
92
|
+
textAndStyle: {
|
|
93
|
+
changesStyle: true,
|
|
94
|
+
changesText: true
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const isResolvedReplaceBlockOperation = (item) => item.operation.type === "replaceBlock";
|
|
98
|
+
const resolveReplaceBlockImpact = ({ changesText, changesStyle }) => {
|
|
99
|
+
if (changesText) return changesStyle ? "textAndStyle" : "text";
|
|
100
|
+
if (changesStyle) return "style";
|
|
101
|
+
return panic("Cannot resolve a replaceBlock that changes neither text nor style");
|
|
102
|
+
};
|
|
103
|
+
const paragraphPropertiesPatch = ({ node, properties, resolvedAlignmentFromStyle }) => {
|
|
104
|
+
const attrs = expectParagraphAttrs(node);
|
|
105
|
+
const currentDirectAlignment = directParagraphAlignment(attrs);
|
|
67
106
|
const patch = {};
|
|
68
|
-
|
|
107
|
+
let originalFormatting = attrs._originalFormatting === void 0 || attrs._originalFormatting === null ? void 0 : { ...attrs._originalFormatting };
|
|
108
|
+
let originalFormattingChanged = false;
|
|
109
|
+
const nextStyleId = properties.styleId;
|
|
110
|
+
const styleChanged = nextStyleId !== void 0 && (attrs.styleId ?? null) !== nextStyleId;
|
|
111
|
+
if (styleChanged) {
|
|
112
|
+
patch["styleId"] = nextStyleId;
|
|
113
|
+
patch["alignmentFromStyle"] = resolvedAlignmentFromStyle;
|
|
114
|
+
if (properties.alignment === void 0) patch["alignment"] = currentDirectAlignment ?? resolvedAlignmentFromStyle ?? null;
|
|
115
|
+
originalFormatting ??= {};
|
|
116
|
+
if (nextStyleId === null) Reflect.deleteProperty(originalFormatting, "styleId");
|
|
117
|
+
else originalFormatting.styleId = nextStyleId;
|
|
118
|
+
if (currentDirectAlignment !== void 0) originalFormatting.alignment = currentDirectAlignment;
|
|
119
|
+
originalFormattingChanged = true;
|
|
120
|
+
}
|
|
69
121
|
if (properties.listLevel !== void 0) {
|
|
70
122
|
const numPr = node.attrs["numPr"];
|
|
71
123
|
const current = typeof numPr === "object" && numPr !== null && "ilvl" in numPr ? numPr.ilvl : void 0;
|
|
@@ -82,6 +134,15 @@ const paragraphPropertiesPatch = (node, properties) => {
|
|
|
82
134
|
};
|
|
83
135
|
}
|
|
84
136
|
}
|
|
137
|
+
if (properties.alignment !== void 0 && (styleChanged || (currentDirectAlignment ?? null) !== properties.alignment)) {
|
|
138
|
+
originalFormatting ??= {};
|
|
139
|
+
if (properties.alignment === null) Reflect.deleteProperty(originalFormatting, "alignment");
|
|
140
|
+
else originalFormatting.alignment = properties.alignment;
|
|
141
|
+
const alignmentFromStyle = properties.styleId === void 0 ? attrs.alignmentFromStyle : resolvedAlignmentFromStyle;
|
|
142
|
+
patch["alignment"] = properties.alignment ?? alignmentFromStyle ?? null;
|
|
143
|
+
originalFormattingChanged = true;
|
|
144
|
+
}
|
|
145
|
+
if (originalFormattingChanged && originalFormatting !== void 0) patch["_originalFormatting"] = Object.keys(originalFormatting).length > 0 ? originalFormatting : null;
|
|
85
146
|
return Object.keys(patch).length > 0 ? patch : null;
|
|
86
147
|
};
|
|
87
148
|
const REVISION_MARK_NAMES = /* @__PURE__ */ new Set(["insertion", "deletion"]);
|
|
@@ -132,23 +193,55 @@ const undeletedContentAtomRanges = (tr, blockFrom) => {
|
|
|
132
193
|
});
|
|
133
194
|
return ranges;
|
|
134
195
|
};
|
|
135
|
-
/** The
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
return snapshot;
|
|
196
|
+
/** The carrier pPr before this batch changed it, for final-mark rotation. */
|
|
197
|
+
const paragraphPropertiesBeforeBatch = (node, batchRevisionIds) => {
|
|
198
|
+
const propertyChanges = expectParagraphAttrs(node)._propertyChanges;
|
|
199
|
+
if (!Array.isArray(propertyChanges)) return paragraphPropertiesSnapshot(node);
|
|
200
|
+
const earliestBatchChange = propertyChanges.find(({ info }) => batchRevisionIds.has(info.id));
|
|
201
|
+
if (!earliestBatchChange) return paragraphPropertiesSnapshot(node);
|
|
202
|
+
return earliestBatchChange.previousFormatting ?? {};
|
|
143
203
|
};
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
204
|
+
const resolveAlignmentFromStyle = ({ attrs, styleId, styleResolver }) => styleResolver?.resolveParagraphStyle(styleId).paragraphFormatting?.alignment ?? ((styleId ?? void 0) === (attrs.styleId ?? void 0) ? attrs.alignmentFromStyle : void 0);
|
|
205
|
+
const applyReplaceBlockStyleId = ({ item, tr, styleResolver, revisionInfo }) => {
|
|
206
|
+
if (item.operation.type !== "replaceBlock" || item.operation.styleId === void 0) return {
|
|
207
|
+
tr,
|
|
208
|
+
revisionId: null
|
|
209
|
+
};
|
|
210
|
+
const blockPosition = tr.mapping.map(item.blockFrom, -1);
|
|
211
|
+
const block = tr.doc.nodeAt(blockPosition);
|
|
212
|
+
if (!block) return {
|
|
213
|
+
tr,
|
|
214
|
+
revisionId: null
|
|
215
|
+
};
|
|
216
|
+
const attrs = expectParagraphAttrs(block);
|
|
217
|
+
const resolvedAlignmentFromStyle = resolveAlignmentFromStyle({
|
|
218
|
+
attrs,
|
|
219
|
+
styleId: item.operation.styleId,
|
|
220
|
+
styleResolver
|
|
221
|
+
});
|
|
222
|
+
const patch = paragraphPropertiesPatch({
|
|
223
|
+
node: block,
|
|
224
|
+
properties: { styleId: item.operation.styleId },
|
|
225
|
+
resolvedAlignmentFromStyle
|
|
151
226
|
});
|
|
227
|
+
if (patch === null) return {
|
|
228
|
+
tr,
|
|
229
|
+
revisionId: null
|
|
230
|
+
};
|
|
231
|
+
const existing = attrs._propertyChanges;
|
|
232
|
+
const change = revisionInfo ? {
|
|
233
|
+
type: "paragraphPropertyChange",
|
|
234
|
+
info: revisionInfo,
|
|
235
|
+
previousFormatting: paragraphPropertiesSnapshot(block)
|
|
236
|
+
} : null;
|
|
237
|
+
return {
|
|
238
|
+
tr: tr.setNodeMarkup(blockPosition, void 0, {
|
|
239
|
+
...block.attrs,
|
|
240
|
+
...patch,
|
|
241
|
+
...change ? { _propertyChanges: [...Array.isArray(existing) ? existing : [], change] } : {}
|
|
242
|
+
}),
|
|
243
|
+
revisionId: change?.info.id ?? null
|
|
244
|
+
};
|
|
152
245
|
};
|
|
153
246
|
const REPLACEMENT_BACKGROUND_CLEAR_FORMATTING = {
|
|
154
247
|
highlight: false,
|
|
@@ -378,15 +471,17 @@ const nextRevisionSeed = (revisionIdCount) => {
|
|
|
378
471
|
* `insertAfterBlock` / `insertBeforeBlock` (`text` split on line breaks,
|
|
379
472
|
* see `splitInsertParagraphTexts`) allocates one id per paragraph in
|
|
380
473
|
* tracked-changes mode plus one for each paragraph MARK it brings, so it
|
|
381
|
-
* needs more than four once split into more than two paragraphs
|
|
382
|
-
*
|
|
383
|
-
*
|
|
474
|
+
* needs more than four once split into more than two paragraphs. Rotating a
|
|
475
|
+
* terminal run can add one paragraph-property revision per inserted paragraph;
|
|
476
|
+
* reserving less than that would let a later `nextRevisionSeed` call reuse an
|
|
477
|
+
* id this operation already stamped on the document.
|
|
384
478
|
*/
|
|
385
479
|
const REVISION_IDS_PER_OPERATION = 4;
|
|
386
|
-
/** Ids one inserted paragraph allocates: its runs, and its paragraph mark. */
|
|
387
|
-
const REVISION_IDS_PER_INSERTED_PARAGRAPH = 2;
|
|
388
480
|
const estimateRevisionIdReservation = (item) => {
|
|
389
|
-
if (item.operation.type === "insertAfterBlock" || item.operation.type === "insertBeforeBlock")
|
|
481
|
+
if (item.operation.type === "insertAfterBlock" || item.operation.type === "insertBeforeBlock") {
|
|
482
|
+
const paragraphCount = item.insertTexts?.length ?? 1;
|
|
483
|
+
return Math.max(paragraphCount * 3, REVISION_IDS_PER_OPERATION);
|
|
484
|
+
}
|
|
390
485
|
return REVISION_IDS_PER_OPERATION;
|
|
391
486
|
};
|
|
392
487
|
/**
|
|
@@ -595,6 +690,13 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
595
690
|
const isEmptyInsert = insertTexts.length === 1 && insertTexts[0]?.length === 0;
|
|
596
691
|
return !(mode === "tracked-changes" && item.operation.pageBreakBefore === true && isEmptyInsert);
|
|
597
692
|
};
|
|
693
|
+
const addedBreakRevisionId = (value) => {
|
|
694
|
+
if (typeof value !== "object" || value === null || !("kind" in value) || !("info" in value)) return null;
|
|
695
|
+
if (value.kind !== "ins" && value.kind !== "moveTo") return null;
|
|
696
|
+
const info = value.info;
|
|
697
|
+
if (typeof info !== "object" || info === null || !("id" in info)) return null;
|
|
698
|
+
return typeof info.id === "number" ? info.id : null;
|
|
699
|
+
};
|
|
598
700
|
/**
|
|
599
701
|
* Move an ADDED paragraph break off every paragraph its container ends with.
|
|
600
702
|
*
|
|
@@ -604,11 +706,10 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
604
706
|
* revision neither accepting nor rejecting everything can clear.
|
|
605
707
|
*
|
|
606
708
|
* Appending at a container's end therefore rotates exactly as removing from it
|
|
607
|
-
* does. The
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
* the run was appended after. Only which paragraph is left markless changes.
|
|
709
|
+
* does. The first inserted break moves onto the paragraph the run was appended
|
|
710
|
+
* after, every later break moves one paragraph left, and the paragraph the
|
|
711
|
+
* container now ends with takes the free mark. Only which paragraph is left
|
|
712
|
+
* markless changes; the order and ownership of the breaks do not.
|
|
612
713
|
*
|
|
613
714
|
* A paragraph's properties live on its mark, so the paragraph that inherits
|
|
614
715
|
* the free one records the other's as `w:pPrChange` — what a rejection reads
|
|
@@ -621,50 +722,95 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
621
722
|
*/
|
|
622
723
|
const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials }) => {
|
|
623
724
|
const paragraphTypeName = tr.doc.type.schema.nodes["paragraph"]?.name ?? "paragraph";
|
|
624
|
-
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).
|
|
725
|
+
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).flatMap(({ node, position }) => {
|
|
625
726
|
const mark = node.attrs["pPrMark"];
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
727
|
+
const revisionId = addedBreakRevisionId(mark);
|
|
728
|
+
return revisionId !== null && batchRevisionIds.has(revisionId) ? [{
|
|
729
|
+
mark,
|
|
730
|
+
node,
|
|
731
|
+
ownerRevisionId: revisionId,
|
|
732
|
+
position
|
|
733
|
+
}] : [];
|
|
631
734
|
});
|
|
632
735
|
let next = tr;
|
|
633
736
|
let nextRevisionId = revisionSeed;
|
|
634
|
-
|
|
635
|
-
|
|
737
|
+
const synthesizedRevisions = [];
|
|
738
|
+
for (const { position: finalPosition, node: final, mark: finalMark } of rotations) {
|
|
739
|
+
const resolved = next.doc.resolve(finalPosition);
|
|
740
|
+
const carrier = addedBreakCarrierBefore(resolved, final.type.name);
|
|
636
741
|
if (!carrier) {
|
|
637
742
|
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
638
743
|
continue;
|
|
639
744
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
745
|
+
const path = [{
|
|
746
|
+
node: final,
|
|
747
|
+
position: finalPosition
|
|
748
|
+
}];
|
|
749
|
+
let position = finalPosition;
|
|
750
|
+
for (let index = resolved.index() - 1; index >= 0; index--) {
|
|
751
|
+
const sibling = resolved.parent.child(index);
|
|
752
|
+
position -= sibling.nodeSize;
|
|
753
|
+
if (sibling.type.name !== final.type.name) continue;
|
|
754
|
+
path.push({
|
|
755
|
+
node: sibling,
|
|
756
|
+
position
|
|
757
|
+
});
|
|
758
|
+
if (position === carrier.position) break;
|
|
759
|
+
}
|
|
760
|
+
if (path.at(-1)?.position !== carrier.position) {
|
|
643
761
|
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
644
762
|
continue;
|
|
645
763
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
764
|
+
if (path.slice(0, -1).some(({ node }) => {
|
|
765
|
+
const revisionId = addedBreakRevisionId(node.attrs["pPrMark"]);
|
|
766
|
+
return revisionId === null || !batchRevisionIds.has(revisionId);
|
|
767
|
+
})) continue;
|
|
768
|
+
const previousFormatting = paragraphPropertiesBeforeBatch(carrier.node, batchRevisionIds);
|
|
769
|
+
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
770
|
+
let carriedMark = finalMark;
|
|
771
|
+
for (let index = 1; index < path.length; index++) {
|
|
772
|
+
const current = path[index - 1];
|
|
773
|
+
const previous = path[index];
|
|
774
|
+
if (!current || !previous) panic("A final-mark rotation exceeded its paragraph path", { index });
|
|
775
|
+
const ownerRevisionId = addedBreakRevisionId(carriedMark);
|
|
776
|
+
if (ownerRevisionId === null || !batchRevisionIds.has(ownerRevisionId)) panic("A final-mark rotation crossed a break outside its operation batch", { ownerRevisionId });
|
|
777
|
+
const displacedMark = previous.node.attrs["pPrMark"];
|
|
778
|
+
next = next.setNodeAttribute(previous.position, "pPrMark", carriedMark);
|
|
779
|
+
const currentFormatting = paragraphPropertiesSnapshot(current.node);
|
|
780
|
+
if (JSON.stringify(previousFormatting) !== JSON.stringify(currentFormatting)) {
|
|
781
|
+
const liveCurrent = next.doc.nodeAt(current.position);
|
|
782
|
+
if (!liveCurrent || liveCurrent.type.name !== final.type.name) panic("A final-mark rotation lost its paragraph", { position: current.position });
|
|
783
|
+
const existing = expectParagraphAttrs(liveCurrent)._propertyChanges;
|
|
784
|
+
const revisionId = nextRevisionId++;
|
|
785
|
+
next = next.setNodeMarkup(current.position, void 0, {
|
|
786
|
+
...liveCurrent.attrs,
|
|
787
|
+
_propertyChanges: [...Array.isArray(existing) ? existing : [], {
|
|
788
|
+
type: "paragraphPropertyChange",
|
|
789
|
+
info: {
|
|
790
|
+
id: revisionId,
|
|
791
|
+
author,
|
|
792
|
+
date,
|
|
793
|
+
...initials ? { initials } : {}
|
|
794
|
+
},
|
|
795
|
+
previousFormatting
|
|
796
|
+
}]
|
|
797
|
+
});
|
|
798
|
+
synthesizedRevisions.push({
|
|
799
|
+
ownerRevisionId,
|
|
800
|
+
revisionId
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
if (displacedMark == null) break;
|
|
804
|
+
carriedMark = displacedMark;
|
|
805
|
+
}
|
|
661
806
|
}
|
|
662
807
|
return {
|
|
663
808
|
transaction: next,
|
|
664
|
-
nextRevisionId
|
|
809
|
+
nextRevisionId,
|
|
810
|
+
synthesizedRevisions
|
|
665
811
|
};
|
|
666
812
|
};
|
|
667
|
-
const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, commentMark, suggestionId, revisionSeed, isPairedMove }) => {
|
|
813
|
+
const buildInsertedParagraphs = ({ item, schema, alignmentFromStyle, mode, author, date, initials, commentMark, suggestionId, revisionSeed, isPairedMove }) => {
|
|
668
814
|
const operation = item.operation;
|
|
669
815
|
if (operation.type !== "insertAfterBlock" && operation.type !== "insertBeforeBlock") panic("Only paragraph insertions can build inserted paragraphs", { type: operation.type });
|
|
670
816
|
const insertionType = schema.marks["insertion"];
|
|
@@ -716,6 +862,19 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
716
862
|
attrs["styleId"] = operation.styleId;
|
|
717
863
|
if (operation.inheritFormatting !== false && operation.styleId !== null) Object.assign(attrs, CLEARED_LIST_MARKER_ATTRS);
|
|
718
864
|
}
|
|
865
|
+
if (isFirstParagraph && (operation.styleId !== void 0 || operation.alignment !== void 0)) {
|
|
866
|
+
const inheritedDirectAlignment = operation.inheritFormatting === false ? void 0 : directParagraphAlignment(expectParagraphAttrs(item.blockNode));
|
|
867
|
+
const directAlignment = operation.alignment === void 0 ? inheritedDirectAlignment : operation.alignment ?? void 0;
|
|
868
|
+
attrs["alignmentFromStyle"] = alignmentFromStyle;
|
|
869
|
+
attrs["alignment"] = directAlignment ?? alignmentFromStyle ?? null;
|
|
870
|
+
const sourceFormatting = attrs["_originalFormatting"];
|
|
871
|
+
const originalFormatting = typeof sourceFormatting === "object" && sourceFormatting !== null ? { ...sourceFormatting } : {};
|
|
872
|
+
if (operation.styleId !== void 0) if (operation.styleId === null) Reflect.deleteProperty(originalFormatting, "styleId");
|
|
873
|
+
else originalFormatting.styleId = operation.styleId;
|
|
874
|
+
if (operation.alignment !== void 0 || inheritedDirectAlignment !== void 0) if (directAlignment === void 0) Reflect.deleteProperty(originalFormatting, "alignment");
|
|
875
|
+
else originalFormatting.alignment = directAlignment;
|
|
876
|
+
attrs["_originalFormatting"] = Object.keys(originalFormatting).length > 0 ? originalFormatting : null;
|
|
877
|
+
}
|
|
719
878
|
if (isSuggested && suggestionId !== null && paragraphRevisionId !== null) attrs["_suggestedInsert"] = {
|
|
720
879
|
suggestionId,
|
|
721
880
|
revisionId: paragraphRevisionId,
|
|
@@ -747,7 +906,7 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
747
906
|
nextRevisionId
|
|
748
907
|
};
|
|
749
908
|
};
|
|
750
|
-
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, tableTemplates }) => {
|
|
909
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates }) => {
|
|
751
910
|
const applied = [];
|
|
752
911
|
const skipped = [];
|
|
753
912
|
const normalizations = [];
|
|
@@ -755,8 +914,21 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
755
914
|
const insertionType = view.state.schema.marks["insertion"];
|
|
756
915
|
const deletionType = view.state.schema.marks["deletion"];
|
|
757
916
|
const commentType = view.state.schema.marks["comment"];
|
|
917
|
+
const styleResolver = getDocumentStyleResolver(view.state);
|
|
918
|
+
const alignmentFromStyleForInsertion = (item) => {
|
|
919
|
+
if (item.operation.type !== "insertAfterBlock" && item.operation.type !== "insertBeforeBlock") return;
|
|
920
|
+
const anchorAttrs = expectParagraphAttrs(item.blockNode);
|
|
921
|
+
const inheritedStyleId = item.operation.inheritFormatting === false ? null : anchorAttrs.styleId;
|
|
922
|
+
const styleId = item.operation.styleId !== void 0 ? item.operation.styleId : inheritedStyleId;
|
|
923
|
+
return resolveAlignmentFromStyle({
|
|
924
|
+
attrs: anchorAttrs,
|
|
925
|
+
styleId,
|
|
926
|
+
styleResolver
|
|
927
|
+
});
|
|
928
|
+
};
|
|
758
929
|
const claimedTableRows = /* @__PURE__ */ new Set();
|
|
759
930
|
const claimedTableColumns = /* @__PURE__ */ new Set();
|
|
931
|
+
const diffText = wordDiffMode === "coarse" ? coarseWordDiff : wordDiffSessionFromOptions(wordDiff).diff;
|
|
760
932
|
const producesTrackedChanges = mode !== "direct";
|
|
761
933
|
const isSuggested = mode === "suggested";
|
|
762
934
|
if (producesTrackedChanges && (!insertionType || !deletionType)) return {
|
|
@@ -915,6 +1087,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
915
1087
|
const built = buildInsertedParagraphs({
|
|
916
1088
|
item: insertion,
|
|
917
1089
|
schema: view.state.schema,
|
|
1090
|
+
alignmentFromStyle: alignmentFromStyleForInsertion(insertion),
|
|
918
1091
|
mode,
|
|
919
1092
|
author,
|
|
920
1093
|
date,
|
|
@@ -999,7 +1172,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
999
1172
|
commentMark,
|
|
1000
1173
|
suggestionId,
|
|
1001
1174
|
initials,
|
|
1002
|
-
|
|
1175
|
+
diffText
|
|
1003
1176
|
});
|
|
1004
1177
|
if (producesTrackedChanges) appliedRevisionIds = [
|
|
1005
1178
|
revisionIdDelete,
|
|
@@ -1039,63 +1212,83 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1039
1212
|
});
|
|
1040
1213
|
break;
|
|
1041
1214
|
case "replaceBlock": {
|
|
1042
|
-
|
|
1043
|
-
const
|
|
1044
|
-
const
|
|
1215
|
+
if (!isResolvedReplaceBlockOperation(item)) panic("Resolved replaceBlock operation lost its impact discriminator");
|
|
1216
|
+
const { changesStyle, changesText } = REPLACE_BLOCK_IMPACT[item.replaceBlockImpact];
|
|
1217
|
+
const revisionIdDelete = revisionSeed;
|
|
1218
|
+
const revisionIdInsert = changesText ? revisionSeed + 1 : revisionSeed;
|
|
1219
|
+
const revisionIdBackground = changesText ? revisionSeed + 2 : revisionSeed;
|
|
1220
|
+
if (changesText) revisionSeed += 3;
|
|
1221
|
+
const revisionIdParagraph = producesTrackedChanges && changesStyle ? revisionSeed++ : null;
|
|
1045
1222
|
if (item.operation.preserveFormatting === false && mode === "direct") {
|
|
1046
1223
|
const replacement = item.operation.text;
|
|
1047
1224
|
const paragraphType = view.state.schema.nodes["paragraph"];
|
|
1048
1225
|
if (paragraphType) {
|
|
1049
|
-
const
|
|
1050
|
-
const node = paragraphType.create(replaceAttrs, replacement.length === 0 ? null : buildEmphasisInlineContent(view.state.schema, replacement, []));
|
|
1226
|
+
const node = paragraphType.create(null, replacement.length === 0 ? null : buildEmphasisInlineContent(view.state.schema, replacement, []));
|
|
1051
1227
|
tr = tr.replaceWith(item.blockFrom, item.blockTo, node);
|
|
1228
|
+
tr = applyReplaceBlockStyleId({
|
|
1229
|
+
item,
|
|
1230
|
+
tr,
|
|
1231
|
+
styleResolver
|
|
1232
|
+
}).tr;
|
|
1052
1233
|
break;
|
|
1053
1234
|
}
|
|
1054
1235
|
}
|
|
1055
1236
|
if (mode === "direct" && hasInlineEmphasis(item.operation.text)) {
|
|
1056
|
-
const
|
|
1057
|
-
...item.blockNode.attrs,
|
|
1058
|
-
styleId: item.operation.styleId
|
|
1059
|
-
} : { ...item.blockNode.attrs };
|
|
1060
|
-
const node = item.blockNode.type.create(attrs, buildEmphasisInlineContent(view.state.schema, item.operation.text, commentMark ? [commentMark] : []));
|
|
1237
|
+
const node = item.blockNode.type.create({ ...item.blockNode.attrs }, buildEmphasisInlineContent(view.state.schema, item.operation.text, commentMark ? [commentMark] : []));
|
|
1061
1238
|
tr = tr.replaceWith(item.blockFrom, item.blockTo, node);
|
|
1239
|
+
tr = applyReplaceBlockStyleId({
|
|
1240
|
+
item,
|
|
1241
|
+
tr,
|
|
1242
|
+
styleResolver
|
|
1243
|
+
}).tr;
|
|
1062
1244
|
break;
|
|
1063
1245
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
tr
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
tr
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1246
|
+
let clearedBackground = false;
|
|
1247
|
+
if (changesText) {
|
|
1248
|
+
const stepsBeforeBackgroundClear = tr.steps.length;
|
|
1249
|
+
tr = clearReplacementBackground({
|
|
1250
|
+
tr,
|
|
1251
|
+
schema: view.state.schema,
|
|
1252
|
+
from: item.from,
|
|
1253
|
+
to: item.to,
|
|
1254
|
+
mode,
|
|
1255
|
+
revisionId: revisionIdBackground,
|
|
1256
|
+
author,
|
|
1257
|
+
date,
|
|
1258
|
+
initials,
|
|
1259
|
+
suggestionId
|
|
1260
|
+
});
|
|
1261
|
+
clearedBackground = tr.steps.length > stepsBeforeBackgroundClear;
|
|
1262
|
+
tr = applyTextReplacement({
|
|
1263
|
+
tr,
|
|
1264
|
+
item,
|
|
1265
|
+
mode,
|
|
1266
|
+
author,
|
|
1267
|
+
date,
|
|
1268
|
+
revisionIdDelete,
|
|
1269
|
+
revisionIdInsert,
|
|
1270
|
+
commentMark,
|
|
1271
|
+
suggestionId,
|
|
1272
|
+
initials,
|
|
1273
|
+
diffText
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
const styleResult = applyReplaceBlockStyleId({
|
|
1092
1277
|
item,
|
|
1093
|
-
tr
|
|
1278
|
+
tr,
|
|
1279
|
+
styleResolver,
|
|
1280
|
+
...revisionIdParagraph !== null ? { revisionInfo: {
|
|
1281
|
+
id: revisionIdParagraph,
|
|
1282
|
+
author,
|
|
1283
|
+
date,
|
|
1284
|
+
...trackedRevisionExtras
|
|
1285
|
+
} } : {}
|
|
1094
1286
|
});
|
|
1287
|
+
tr = styleResult.tr;
|
|
1095
1288
|
if (producesTrackedChanges) appliedRevisionIds = [
|
|
1096
|
-
revisionIdDelete,
|
|
1097
|
-
|
|
1098
|
-
...
|
|
1289
|
+
...changesText ? [revisionIdDelete, revisionIdInsert] : [],
|
|
1290
|
+
...clearedBackground ? [revisionIdBackground] : [],
|
|
1291
|
+
...styleResult.revisionId === null ? [] : [styleResult.revisionId]
|
|
1099
1292
|
];
|
|
1100
1293
|
break;
|
|
1101
1294
|
}
|
|
@@ -1113,6 +1306,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1113
1306
|
const built = buildInsertedParagraphs({
|
|
1114
1307
|
item,
|
|
1115
1308
|
schema: view.state.schema,
|
|
1309
|
+
alignmentFromStyle: alignmentFromStyleForInsertion(item),
|
|
1116
1310
|
mode,
|
|
1117
1311
|
author,
|
|
1118
1312
|
date,
|
|
@@ -1500,7 +1694,17 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1500
1694
|
case "setBlockParagraphProperties": {
|
|
1501
1695
|
const blockPosition = tr.mapping.map(item.blockFrom);
|
|
1502
1696
|
const liveBlock = tr.doc.nodeAt(blockPosition) ?? item.blockNode;
|
|
1503
|
-
const
|
|
1697
|
+
const liveAttrs = expectParagraphAttrs(liveBlock);
|
|
1698
|
+
const resolvedAlignmentFromStyle = item.operation.properties.styleId === void 0 ? void 0 : resolveAlignmentFromStyle({
|
|
1699
|
+
attrs: liveAttrs,
|
|
1700
|
+
styleId: item.operation.properties.styleId,
|
|
1701
|
+
styleResolver
|
|
1702
|
+
});
|
|
1703
|
+
const patch = paragraphPropertiesPatch({
|
|
1704
|
+
node: liveBlock,
|
|
1705
|
+
properties: item.operation.properties,
|
|
1706
|
+
resolvedAlignmentFromStyle
|
|
1707
|
+
});
|
|
1504
1708
|
if (patch === null) {
|
|
1505
1709
|
skipped.push({
|
|
1506
1710
|
id: item.operation.id,
|
|
@@ -1522,7 +1726,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1522
1726
|
id: revisionId,
|
|
1523
1727
|
author,
|
|
1524
1728
|
date,
|
|
1525
|
-
...
|
|
1729
|
+
...trackedRevisionExtras
|
|
1526
1730
|
},
|
|
1527
1731
|
previousFormatting: paragraphPropertiesSnapshot(liveBlock)
|
|
1528
1732
|
};
|
|
@@ -1633,6 +1837,20 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1633
1837
|
});
|
|
1634
1838
|
tr = rotated.transaction;
|
|
1635
1839
|
revisionSeed = rotated.nextRevisionId;
|
|
1840
|
+
const receiptIndexByRevisionId = /* @__PURE__ */ new Map();
|
|
1841
|
+
for (const [receiptIndex, receipt] of applied.entries()) for (const revisionId of receipt.revisionIds ?? []) receiptIndexByRevisionId.set(revisionId, receiptIndex);
|
|
1842
|
+
for (const { ownerRevisionId, revisionId } of rotated.synthesizedRevisions) {
|
|
1843
|
+
const receiptIndex = receiptIndexByRevisionId.get(ownerRevisionId);
|
|
1844
|
+
const receipt = receiptIndex === void 0 ? void 0 : applied.at(receiptIndex);
|
|
1845
|
+
if (receiptIndex === void 0 || !receipt?.revisionIds) panic("A synthesized final-mark revision lost its operation receipt", {
|
|
1846
|
+
ownerRevisionId,
|
|
1847
|
+
revisionId
|
|
1848
|
+
});
|
|
1849
|
+
applied[receiptIndex] = {
|
|
1850
|
+
...receipt,
|
|
1851
|
+
revisionIds: [...receipt.revisionIds, revisionId]
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1636
1854
|
if (revisionStamp) requestDeterministicParaIds(tr, `${revisionStamp.date}:${String(revisionStamp.idSeed)}`);
|
|
1637
1855
|
view.dispatch(tr);
|
|
1638
1856
|
}
|
|
@@ -1657,7 +1875,8 @@ const previewFolioAIEditOperations = (options) => {
|
|
|
1657
1875
|
...applyOptions,
|
|
1658
1876
|
view: previewView,
|
|
1659
1877
|
...createCommentId !== void 0 && { createCommentId: () => previewCommentId-- },
|
|
1660
|
-
revisionIdSeed: -1e9
|
|
1878
|
+
revisionIdSeed: -1e9,
|
|
1879
|
+
wordDiffMode: "coarse"
|
|
1661
1880
|
});
|
|
1662
1881
|
return {
|
|
1663
1882
|
applied: result.applied.map(({ id }) => ({ id })),
|
|
@@ -1666,7 +1885,7 @@ const previewFolioAIEditOperations = (options) => {
|
|
|
1666
1885
|
nextRevisionId: options.revisionStamp?.idSeed ?? revisionIdCursor
|
|
1667
1886
|
};
|
|
1668
1887
|
};
|
|
1669
|
-
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials,
|
|
1888
|
+
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials, diffText }) => {
|
|
1670
1889
|
let nextTr = tr;
|
|
1671
1890
|
const replacement = stripInlineEmphasisMarkers((() => {
|
|
1672
1891
|
if (item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") return item.operation.replace;
|
|
@@ -1720,7 +1939,7 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
1720
1939
|
}
|
|
1721
1940
|
}
|
|
1722
1941
|
if (sourceText !== null && cleanBlock !== null) {
|
|
1723
|
-
const segments =
|
|
1942
|
+
const segments = diffText(sourceText, replacement);
|
|
1724
1943
|
const offsets = cleanBlock.offsets;
|
|
1725
1944
|
const offsetAt = (cleanOffset) => offsets[cleanOffset] ?? null;
|
|
1726
1945
|
const steps = [];
|
|
@@ -2210,6 +2429,7 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2210
2429
|
};
|
|
2211
2430
|
}
|
|
2212
2431
|
if (operation.type === "deleteBlock" || operation.type === "replaceBlock") {
|
|
2432
|
+
const replaceChangesStyle = operation.type === "replaceBlock" && operation.styleId !== void 0 && operation.styleId !== (expectParagraphAttrs(blockNode).styleId ?? null);
|
|
2213
2433
|
const range = getTextRangeFromCleanBlock(cleanBlock);
|
|
2214
2434
|
if (!range) {
|
|
2215
2435
|
const insertionPoint = cleanBlock.offsets.at(0);
|
|
@@ -2217,10 +2437,25 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2217
2437
|
type: "skip",
|
|
2218
2438
|
reason: "unsupportedBlock"
|
|
2219
2439
|
};
|
|
2220
|
-
if (!(operation.type === "deleteBlock" ? true : currentText.length === 0 && operation.text.length > 0)) return {
|
|
2440
|
+
if (!(operation.type === "deleteBlock" ? true : currentText.length === 0 && (operation.text.length > 0 || replaceChangesStyle))) return {
|
|
2221
2441
|
type: "skip",
|
|
2222
2442
|
reason: "unsupportedBlock"
|
|
2223
2443
|
};
|
|
2444
|
+
if (operation.type === "replaceBlock") return {
|
|
2445
|
+
type: "resolved",
|
|
2446
|
+
operation: {
|
|
2447
|
+
operation,
|
|
2448
|
+
from: insertionPoint,
|
|
2449
|
+
to: insertionPoint,
|
|
2450
|
+
blockFrom,
|
|
2451
|
+
blockTo,
|
|
2452
|
+
blockNode,
|
|
2453
|
+
replaceBlockImpact: resolveReplaceBlockImpact({
|
|
2454
|
+
changesText: operation.text !== currentText,
|
|
2455
|
+
changesStyle: replaceChangesStyle
|
|
2456
|
+
})
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2224
2459
|
return {
|
|
2225
2460
|
type: "resolved",
|
|
2226
2461
|
operation: {
|
|
@@ -2233,9 +2468,26 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2233
2468
|
}
|
|
2234
2469
|
};
|
|
2235
2470
|
}
|
|
2236
|
-
if (operation.type === "replaceBlock" && operation.text === currentText)
|
|
2237
|
-
|
|
2238
|
-
|
|
2471
|
+
if (operation.type === "replaceBlock" && operation.text === currentText) {
|
|
2472
|
+
if (!replaceChangesStyle) return {
|
|
2473
|
+
type: "skip",
|
|
2474
|
+
reason: "noopOperation"
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
if (operation.type === "replaceBlock") return {
|
|
2478
|
+
type: "resolved",
|
|
2479
|
+
operation: {
|
|
2480
|
+
operation,
|
|
2481
|
+
from: range.from,
|
|
2482
|
+
to: range.to,
|
|
2483
|
+
blockFrom,
|
|
2484
|
+
blockTo,
|
|
2485
|
+
blockNode,
|
|
2486
|
+
replaceBlockImpact: resolveReplaceBlockImpact({
|
|
2487
|
+
changesText: operation.text !== currentText,
|
|
2488
|
+
changesStyle: replaceChangesStyle
|
|
2489
|
+
})
|
|
2490
|
+
}
|
|
2239
2491
|
};
|
|
2240
2492
|
return {
|
|
2241
2493
|
type: "resolved",
|