@stll/folio-core 0.35.1 → 0.37.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/README.md +59 -1
- package/dist/ai-edits/apply.js +878 -377
- package/dist/ai-edits/block-identity.d.ts +4 -1
- package/dist/ai-edits/block-identity.js +4 -1
- package/dist/ai-edits/blockRange.js +14 -14
- package/dist/ai-edits/clean-text.d.ts +40 -1
- package/dist/ai-edits/clean-text.js +52 -2
- package/dist/ai-edits/headless.d.ts +3 -1
- package/dist/ai-edits/headless.js +141 -73
- package/dist/ai-edits/index.d.ts +3 -3
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.js +38 -20
- package/dist/ai-edits/snapshot.d.ts +14 -6
- package/dist/ai-edits/snapshot.js +105 -20
- package/dist/ai-edits/table-cell-mutations.js +1 -1
- package/dist/ai-edits/table-row-column-mutations.js +7 -0
- package/dist/ai-edits/table-template.d.ts +11 -1
- package/dist/ai-edits/table-template.js +49 -17
- package/dist/ai-edits/types.d.ts +62 -61
- package/dist/ai-edits/word-diff.d.ts +18 -3
- package/dist/ai-edits/word-diff.js +556 -67
- package/dist/ai-suggestions/conflict.js +1 -4
- package/dist/ai-suggestions/text-positions.d.ts +8 -0
- package/dist/ai-suggestions/text-positions.js +27 -5
- package/dist/compare/__fixtures__/body-sequence.js +3 -4
- package/dist/compare/column-alignment.d.ts +13 -13
- package/dist/compare/column-alignment.js +78 -39
- package/dist/compare/compare.d.ts +4 -3
- package/dist/compare/compare.js +48 -21
- package/dist/compare/content-alignment.d.ts +95 -0
- package/dist/compare/content-alignment.js +1450 -0
- package/dist/compare/content-types.d.ts +100 -0
- package/dist/compare/content-types.js +0 -0
- package/dist/compare/content.d.ts +209 -0
- package/dist/compare/content.js +885 -0
- package/dist/compare/formatting.d.ts +5 -5
- package/dist/compare/formatting.js +6 -4
- package/dist/compare/plan.d.ts +6 -1
- package/dist/compare/plan.js +171 -566
- package/dist/compare/scenario.d.ts +2 -2
- package/dist/compare/types.d.ts +3 -3
- package/dist/compare/verification.d.ts +8 -9
- package/dist/compare/verification.js +72 -50
- package/dist/compat/eigenpal.d.ts +5 -3
- package/dist/compat/eigenpal.js +3 -2
- package/dist/controller/headerFooterEditorManager.js +18 -10
- package/dist/controller/hiddenEditorApi.js +1 -1
- package/dist/controller/hiddenEditorManager.d.ts +7 -2
- package/dist/controller/hiddenEditorManager.js +15 -6
- package/dist/controller/layoutPipeline.js +5 -3
- package/dist/controller/noteEditorManager.js +12 -7
- package/dist/display-list/build/headerFooterPrimitives.js +1 -0
- package/dist/display-list/build/storyPrimitives.d.ts +5 -2
- package/dist/display-list/build/storyPrimitives.js +60 -3
- package/dist/display-list/build/tablePrimitives.js +30 -0
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/document-operations.d.ts +8 -4
- package/dist/document-operations.js +104 -12
- package/dist/docx/blockContentParser.js +6 -27
- 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/ensureParaIds.js +9 -4
- package/dist/docx/headerFooterParser.js +2 -4
- package/dist/docx/normalizeBaseDirection.js +5 -7
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +4 -1
- package/dist/docx/paragraphParser.js +34 -35
- package/dist/docx/paragraphPropertySource.d.ts +49 -0
- package/dist/docx/paragraphPropertySource.js +113 -0
- package/dist/docx/paragraphTraversal.js +30 -34
- package/dist/docx/rezip.d.ts +0 -8
- package/dist/docx/rezip.js +41 -16
- package/dist/docx/runConsolidator.js +2 -4
- package/dist/docx/runParser.js +6 -17
- package/dist/docx/sectionParser.js +1 -15
- package/dist/docx/serializer/paragraphSerializer.js +289 -30
- package/dist/docx/serializer/runSerializer.js +6 -12
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -10
- package/dist/docx/serializer/tableSerializer.js +4 -23
- package/dist/docx/serializer/trackedChangeAttributes.d.ts +11 -0
- package/dist/docx/serializer/trackedChangeAttributes.js +31 -0
- package/dist/docx/server/build.d.ts +1 -1
- 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/headless-layout.js +5 -4
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/internal/headlessRevisionResolution.d.ts +23 -0
- package/dist/internal/headlessRevisionResolution.js +155 -0
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
- package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
- package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
- package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
- package/dist/internal/pageBreakRunPartition.d.ts +26 -0
- package/dist/internal/pageBreakRunPartition.js +35 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
- package/dist/internal/sectionEndpointResolution.d.ts +32 -0
- package/dist/internal/sectionEndpointResolution.js +38 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
- package/dist/layout-bridge/convert/headerFooterLayout.js +52 -9
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +391 -82
- package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
- package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
- package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
- package/dist/layout-bridge/dom/textStreamDom.js +75 -0
- package/dist/layout-bridge/headerFooterLayout.js +16 -30
- package/dist/layout-engine/index.js +12 -3
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +7 -0
- package/dist/layout-painter/imageLayout.d.ts +1 -1
- package/dist/layout-painter/renderTable.js +20 -0
- package/dist/markdown/renderBlock.js +7 -10
- package/dist/model.d.ts +1 -1
- package/dist/paged-layout/rangeProjection.js +6 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
- package/dist/paged-layout/transactionDirtyRange.js +23 -1
- package/dist/prosemirror/attrs/index.d.ts +7 -3
- package/dist/prosemirror/attrs/index.js +128 -7
- package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
- package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
- package/dist/prosemirror/commands/comments.d.ts +10 -1
- package/dist/prosemirror/commands/comments.js +551 -87
- package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
- package/dist/prosemirror/commands/formatPainter.js +319 -63
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
- package/dist/prosemirror/commands/pageBreak.js +18 -36
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +27 -3
- package/dist/prosemirror/commands/propertyChangeScope.js +85 -14
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -5
- package/dist/prosemirror/conversion/fromProseDoc.js +935 -223
- package/dist/prosemirror/conversion/runShadingMark.js +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
- package/dist/prosemirror/conversion/toProseDoc.js +439 -291
- package/dist/prosemirror/extensions/StarterKit.js +4 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +54 -6
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -1
- package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +19 -6
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
- package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
- package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
- package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
- package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
- package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
- package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
- package/dist/prosemirror/findReplaceSelection.js +8 -3
- package/dist/prosemirror/index.d.ts +3 -3
- package/dist/prosemirror/listMarker.d.ts +26 -1
- package/dist/prosemirror/listMarker.js +108 -14
- package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
- package/dist/prosemirror/pageBreakRunProjection.js +47 -0
- package/dist/prosemirror/paraText.js +6 -0
- package/dist/prosemirror/paragraphAlignment.d.ts +12 -0
- package/dist/prosemirror/paragraphAlignment.js +13 -0
- package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
- package/dist/prosemirror/paragraphSpacing.js +125 -0
- package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
- package/dist/prosemirror/plugins/documentNumbering.js +18 -0
- package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
- package/dist/prosemirror/plugins/pmTextScan.js +14 -4
- package/dist/prosemirror/plugins/suggestionMode.js +9 -11
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
- package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
- package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
- package/dist/prosemirror/runFormattingProvenance.js +48 -0
- package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
- package/dist/prosemirror/runFormattingReconciliation.js +54 -0
- package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
- package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
- package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
- package/dist/prosemirror/runStyleFormatting.js +105 -0
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +84 -9
- package/dist/prosemirror/schema/marks.js +73 -1
- package/dist/prosemirror/schema/nodes.d.ts +30 -6
- package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +45 -23
- package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
- package/dist/prosemirror/styles/styleResolver.js +3 -3
- package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
- package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
- package/dist/prosemirror/tableGridMutation.d.ts +20 -0
- package/dist/prosemirror/tableGridMutation.js +30 -0
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
- package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
- package/dist/prosemirror/validation.js +23 -4
- package/dist/redline.js +4 -1
- package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
- package/dist/render-dom/RenderedDomContext.js +17 -34
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/style-engine/styleEngine.d.ts +1 -1
- package/dist/utils/clipboard.js +1 -1
- package/dist/utils/findReplace.js +38 -21
- package/dist/utils/mergeDocumentContent.js +3 -3
- package/dist/utils/replaceText.js +2 -1
- package/dist/version-comparison.d.ts +38 -44
- package/dist/version-comparison.js +270 -399
- package/dist/watermark/index.js +2 -4
- package/package.json +8 -2
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1,25 +1,85 @@
|
|
|
1
|
-
import { expectParagraphAttrs,
|
|
2
|
-
import {
|
|
1
|
+
import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
2
|
+
import { hasSerializableParagraphPropertyChange, paragraphPropertiesSnapshot } from "../prosemirror/commands/propertyChangeScope.js";
|
|
3
3
|
import { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer } from "../prosemirror/containerFinalParagraph.js";
|
|
4
|
-
import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
|
|
5
4
|
import { requestDeterministicParaIds } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
6
5
|
import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
6
|
+
import { CLEARED_LIST_RENDERING_ATTRS } from "../prosemirror/listMarker.js";
|
|
7
|
+
import { directParagraphAlignment } from "../prosemirror/paragraphAlignment.js";
|
|
8
|
+
import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing } from "../prosemirror/paragraphSpacing.js";
|
|
9
|
+
import { getDocumentNumbering } from "../prosemirror/plugins/documentNumbering.js";
|
|
10
|
+
import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyles.js";
|
|
11
|
+
import { selectRunFormattingCarrierRepresentations } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
12
|
+
import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
|
|
13
|
+
import { paragraphRunStyleContextAt } from "../prosemirror/runStyleFormatting.js";
|
|
14
|
+
import { listLevelAttrPatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
|
|
7
15
|
import { isZeroWidthAnchor } from "../prosemirror/zeroWidthAnchors.js";
|
|
8
16
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
9
17
|
import { stripBlockIdentityAttrs } from "./block-identity.js";
|
|
10
|
-
import { buildCleanBlockText } from "./clean-text.js";
|
|
18
|
+
import { buildCleanBlockText, resolveCleanTextRange } from "./clean-text.js";
|
|
11
19
|
import { hasInlineEmphasis, parseInlineEmphasisRuns, stripInlineEmphasisMarkers } from "./inline-emphasis.js";
|
|
12
|
-
import { hashFolioAIBlockText, isHiddenTableRow, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
20
|
+
import { hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isHiddenTableRow, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
13
21
|
import { mergeTableRectangle, mergeTrackedVerticalTableCells, splitTableRectangle, splitTrackedVerticalTableCell } from "./table-cell-mutations.js";
|
|
14
22
|
import { planTableMutations } from "./table-mutation-plan.js";
|
|
15
23
|
import { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDeletion, applyTableRowInsertion, findTableColumnInsertion, findTableRowInsertion, getTableColumnCoordinateKey, markTableRowContent, splitCellParagraphTexts } from "./table-row-column-mutations.js";
|
|
16
24
|
import { findEnclosingTableCell, findEnclosingTableRow, findOutermostTableBoundary, tableRectangleCutsMergedCell } from "./table-targets.js";
|
|
17
25
|
import { tableFromTemplate } from "./table-template.js";
|
|
18
|
-
import {
|
|
26
|
+
import { wordDiffSessionFromOptions } from "./word-diff.js";
|
|
19
27
|
import { panic } from "better-result";
|
|
28
|
+
import { Mark } from "prosemirror-model";
|
|
20
29
|
import { TableMap } from "prosemirror-tables";
|
|
21
30
|
import { canJoin, canSplit } from "prosemirror-transform";
|
|
22
31
|
//#region src/ai-edits/apply.ts
|
|
32
|
+
const coarseWordDiff = (before, after) => {
|
|
33
|
+
if (before === after) return before.length === 0 ? [] : [{
|
|
34
|
+
type: "equal",
|
|
35
|
+
text: before
|
|
36
|
+
}];
|
|
37
|
+
if (before.length === 0) return [{
|
|
38
|
+
type: "ins",
|
|
39
|
+
text: after
|
|
40
|
+
}];
|
|
41
|
+
if (after.length === 0) return [{
|
|
42
|
+
type: "del",
|
|
43
|
+
text: before
|
|
44
|
+
}];
|
|
45
|
+
return [{
|
|
46
|
+
type: "del",
|
|
47
|
+
text: before
|
|
48
|
+
}, {
|
|
49
|
+
type: "ins",
|
|
50
|
+
text: after
|
|
51
|
+
}];
|
|
52
|
+
};
|
|
53
|
+
const commentMarkRanges = (doc, commentType, commentId) => {
|
|
54
|
+
const ranges = [];
|
|
55
|
+
doc.descendants((node, position) => {
|
|
56
|
+
for (const mark of node.marks) if (mark.type === commentType && mark.attrs["commentId"] === commentId) ranges.push({
|
|
57
|
+
from: position,
|
|
58
|
+
to: position + node.nodeSize,
|
|
59
|
+
mark
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
return ranges;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Replace an operation-local comment id only after its edit has succeeded.
|
|
66
|
+
* The provisional mark lives solely in the undispatched transaction, so an
|
|
67
|
+
* operation that refuses or produces no anchor has no external comment side
|
|
68
|
+
* effect and cannot leave an orphan comments.xml entry behind.
|
|
69
|
+
*/
|
|
70
|
+
const commitProvisionalComment = ({ tr, commentType, provisionalId, committedId }) => {
|
|
71
|
+
const ranges = commentMarkRanges(tr.doc, commentType, provisionalId);
|
|
72
|
+
if (ranges.length === 0) panic("A commented operation changed the document without producing a comment anchor");
|
|
73
|
+
if (provisionalId === committedId) return tr;
|
|
74
|
+
for (const { from, to, mark } of ranges) {
|
|
75
|
+
tr = tr.removeMark(from, to, mark);
|
|
76
|
+
tr = tr.addMark(from, to, commentType.create({
|
|
77
|
+
...mark.attrs,
|
|
78
|
+
commentId: committedId
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
return tr;
|
|
82
|
+
};
|
|
23
83
|
/**
|
|
24
84
|
* Operation types applied in `"suggested"` mode. Every produced revision — an
|
|
25
85
|
* inline mark, a whole-node `_suggestedInsert` marker, or a suggested
|
|
@@ -31,6 +91,7 @@ const SUGGESTED_SUPPORTED_OPERATION_TYPES = /* @__PURE__ */ new Set([
|
|
|
31
91
|
"replaceInBlock",
|
|
32
92
|
"replaceRange",
|
|
33
93
|
"formatRange",
|
|
94
|
+
"setBlockParagraphProperties",
|
|
34
95
|
"replaceBlock",
|
|
35
96
|
"deleteBlock",
|
|
36
97
|
"insertAfterBlock",
|
|
@@ -41,47 +102,104 @@ const SUGGESTED_SUPPORTED_OPERATION_TYPES = /* @__PURE__ */ new Set([
|
|
|
41
102
|
"insertTableColumn",
|
|
42
103
|
"deleteTableColumn"
|
|
43
104
|
]);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
105
|
+
const REPLACE_BLOCK_IMPACT = {
|
|
106
|
+
text: {
|
|
107
|
+
changesStyle: false,
|
|
108
|
+
changesText: true
|
|
109
|
+
},
|
|
110
|
+
style: {
|
|
111
|
+
changesStyle: true,
|
|
112
|
+
changesText: false
|
|
113
|
+
},
|
|
114
|
+
textAndStyle: {
|
|
115
|
+
changesStyle: true,
|
|
116
|
+
changesText: true
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const isResolvedReplaceBlockOperation = (item) => item.operation.type === "replaceBlock";
|
|
120
|
+
const isResolvedBaseReplaceBlockOperation = (item) => item.operation.type === "replaceBlock";
|
|
121
|
+
const hasRepresentableCommentAnchor = (item, mode) => {
|
|
122
|
+
switch (item.operation.type) {
|
|
123
|
+
case "insertAfterBlock":
|
|
124
|
+
case "insertBeforeBlock": return (item.insertTexts ?? [""]).some((text) => stripInlineEmphasisMarkers(text).length > 0);
|
|
125
|
+
case "insertSignatureTable": return false;
|
|
126
|
+
case "replaceInBlock":
|
|
127
|
+
case "replaceRange": return stripInlineEmphasisMarkers(item.operation.replace).length > 0 || mode !== "direct" && item.from < item.to;
|
|
128
|
+
case "replaceBlock": {
|
|
129
|
+
if (!isResolvedBaseReplaceBlockOperation(item)) panic("Resolved replaceBlock operation lost its impact discriminator");
|
|
130
|
+
const { changesText } = REPLACE_BLOCK_IMPACT[item.replaceBlockImpact];
|
|
131
|
+
if (!changesText) return item.from < item.to;
|
|
132
|
+
return stripInlineEmphasisMarkers(item.operation.text).length > 0 || mode !== "direct" && item.from < item.to;
|
|
133
|
+
}
|
|
134
|
+
case "deleteBlock": return mode !== "direct" && item.from < item.to;
|
|
135
|
+
default: return item.from < item.to;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const writesParagraphPropertyChange = (item) => item.operation.type === "setBlockParagraphProperties" || isResolvedReplaceBlockOperation(item) && REPLACE_BLOCK_IMPACT[item.replaceBlockImpact].changesStyle;
|
|
139
|
+
const resolveReplaceBlockImpact = ({ changesText, changesStyle }) => {
|
|
140
|
+
if (changesText) return changesStyle ? "textAndStyle" : "text";
|
|
141
|
+
if (changesStyle) return "style";
|
|
142
|
+
return panic("Cannot resolve a replaceBlock that changes neither text nor style");
|
|
143
|
+
};
|
|
144
|
+
const paragraphPropertiesPatch = ({ node, properties, resolvedFormattingFromStyle, numbering = null }) => {
|
|
145
|
+
const attrs = expectParagraphAttrs(node);
|
|
146
|
+
const currentDirectAlignment = directParagraphAlignment(attrs);
|
|
147
|
+
const currentDirectSpacing = directParagraphSpacing(attrs);
|
|
148
|
+
const resolvedSpacingFromStyle = paragraphSpacingFromFormatting(resolvedFormattingFromStyle);
|
|
67
149
|
const patch = {};
|
|
68
|
-
|
|
150
|
+
let originalFormatting = attrs._originalFormatting === void 0 || attrs._originalFormatting === null ? void 0 : { ...attrs._originalFormatting };
|
|
151
|
+
let originalFormattingChanged = false;
|
|
152
|
+
const nextStyleId = properties.styleId;
|
|
153
|
+
const styleChanged = nextStyleId !== void 0 && (attrs.styleId ?? null) !== nextStyleId;
|
|
154
|
+
if (styleChanged) {
|
|
155
|
+
patch["styleId"] = nextStyleId;
|
|
156
|
+
patch["alignmentFromStyle"] = resolvedFormattingFromStyle?.alignment;
|
|
157
|
+
if (properties.alignment === void 0) patch["alignment"] = currentDirectAlignment ?? resolvedFormattingFromStyle?.alignment ?? null;
|
|
158
|
+
if (properties.spacing === void 0) Object.assign(patch, paragraphSpacingAttrPatch({
|
|
159
|
+
direct: currentDirectSpacing,
|
|
160
|
+
inherited: resolvedSpacingFromStyle
|
|
161
|
+
}));
|
|
162
|
+
originalFormatting ??= {};
|
|
163
|
+
if (nextStyleId === null) Reflect.deleteProperty(originalFormatting, "styleId");
|
|
164
|
+
else originalFormatting.styleId = nextStyleId;
|
|
165
|
+
if (currentDirectAlignment !== void 0) originalFormatting.alignment = currentDirectAlignment;
|
|
166
|
+
originalFormattingChanged = true;
|
|
167
|
+
}
|
|
69
168
|
if (properties.listLevel !== void 0) {
|
|
70
169
|
const numPr = node.attrs["numPr"];
|
|
71
|
-
const current = typeof numPr === "object" && numPr !== null && "ilvl" in numPr ? numPr.ilvl : void 0;
|
|
72
170
|
if (properties.listLevel === null) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
} else if (current !== properties.listLevel) {
|
|
171
|
+
patch["numPr"] = null;
|
|
172
|
+
Object.assign(patch, CLEARED_LIST_RENDERING_ATTRS);
|
|
173
|
+
} else {
|
|
78
174
|
const numId = typeof numPr === "object" && numPr !== null && "numId" in numPr ? numPr.numId : void 0;
|
|
79
|
-
|
|
80
|
-
|
|
175
|
+
if (typeof numId === "number") Object.assign(patch, listLevelAttrPatch(attrs, {
|
|
176
|
+
numId,
|
|
81
177
|
ilvl: properties.listLevel
|
|
82
|
-
};
|
|
178
|
+
}, numbering));
|
|
179
|
+
else {
|
|
180
|
+
patch["numPr"] = { ilvl: properties.listLevel };
|
|
181
|
+
Object.assign(patch, CLEARED_LIST_RENDERING_ATTRS);
|
|
182
|
+
}
|
|
83
183
|
}
|
|
84
184
|
}
|
|
185
|
+
if (properties.alignment !== void 0 && (styleChanged || (currentDirectAlignment ?? null) !== properties.alignment)) {
|
|
186
|
+
originalFormatting ??= {};
|
|
187
|
+
if (properties.alignment === null) Reflect.deleteProperty(originalFormatting, "alignment");
|
|
188
|
+
else originalFormatting.alignment = properties.alignment;
|
|
189
|
+
const alignmentFromStyle = properties.styleId === void 0 ? attrs.alignmentFromStyle : resolvedFormattingFromStyle?.alignment;
|
|
190
|
+
patch["alignment"] = properties.alignment ?? alignmentFromStyle ?? null;
|
|
191
|
+
originalFormattingChanged = true;
|
|
192
|
+
}
|
|
193
|
+
if (properties.spacing !== void 0 && (styleChanged || !paragraphSpacingEqual(currentDirectSpacing, properties.spacing))) {
|
|
194
|
+
const directSpacing = properties.spacing ?? void 0;
|
|
195
|
+
originalFormatting = withDirectParagraphSpacing(originalFormatting, directSpacing);
|
|
196
|
+
Object.assign(patch, paragraphSpacingAttrPatch({
|
|
197
|
+
direct: directSpacing,
|
|
198
|
+
inherited: resolvedSpacingFromStyle
|
|
199
|
+
}));
|
|
200
|
+
originalFormattingChanged = true;
|
|
201
|
+
}
|
|
202
|
+
if (originalFormattingChanged) patch["_originalFormatting"] = originalFormatting && Object.keys(originalFormatting).length > 0 ? originalFormatting : null;
|
|
85
203
|
return Object.keys(patch).length > 0 ? patch : null;
|
|
86
204
|
};
|
|
87
205
|
const REVISION_MARK_NAMES = /* @__PURE__ */ new Set(["insertion", "deletion"]);
|
|
@@ -132,163 +250,195 @@ const undeletedContentAtomRanges = (tr, blockFrom) => {
|
|
|
132
250
|
});
|
|
133
251
|
return ranges;
|
|
134
252
|
};
|
|
135
|
-
/** The
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
253
|
+
/** The carrier pPr before this batch changed it, for final-mark rotation. */
|
|
254
|
+
const paragraphPropertiesBeforeBatch = (node, batchRevisionIds) => {
|
|
255
|
+
const propertyChanges = expectParagraphAttrs(node)._propertyChanges;
|
|
256
|
+
if (!Array.isArray(propertyChanges)) return paragraphPropertiesSnapshot(node);
|
|
257
|
+
const earliestBatchChange = propertyChanges.find(({ info }) => batchRevisionIds.has(info.id));
|
|
258
|
+
if (!earliestBatchChange) return paragraphPropertiesSnapshot(node);
|
|
259
|
+
return earliestBatchChange.previousFormatting ?? {};
|
|
260
|
+
};
|
|
261
|
+
const resolveFormattingFromStyle = ({ attrs, styleId, styleResolver }) => {
|
|
262
|
+
const resolved = styleResolver?.resolveParagraphStyle(styleId).paragraphFormatting;
|
|
263
|
+
if (resolved !== void 0) return resolved;
|
|
264
|
+
if ((styleId ?? void 0) !== (attrs.styleId ?? void 0)) return;
|
|
265
|
+
const fallback = {};
|
|
266
|
+
if (attrs.alignmentFromStyle !== void 0) fallback.alignment = attrs.alignmentFromStyle;
|
|
267
|
+
return Object.keys(fallback).length > 0 ? fallback : void 0;
|
|
143
268
|
};
|
|
144
|
-
const applyReplaceBlockStyleId = ({ item, tr }) => {
|
|
145
|
-
if (item.operation.type !== "replaceBlock" || item.operation.styleId === void 0) return
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
269
|
+
const applyReplaceBlockStyleId = ({ item, tr, styleResolver, revisionInfo }) => {
|
|
270
|
+
if (item.operation.type !== "replaceBlock" || item.operation.styleId === void 0) return {
|
|
271
|
+
tr,
|
|
272
|
+
revisionId: null
|
|
273
|
+
};
|
|
274
|
+
const blockPosition = tr.mapping.map(item.blockFrom, -1);
|
|
275
|
+
const block = tr.doc.nodeAt(blockPosition);
|
|
276
|
+
if (!block) return {
|
|
277
|
+
tr,
|
|
278
|
+
revisionId: null
|
|
279
|
+
};
|
|
280
|
+
const attrs = expectParagraphAttrs(block);
|
|
281
|
+
const resolvedFormattingFromStyle = resolveFormattingFromStyle({
|
|
282
|
+
attrs,
|
|
283
|
+
styleId: item.operation.styleId,
|
|
284
|
+
styleResolver
|
|
285
|
+
});
|
|
286
|
+
const patch = paragraphPropertiesPatch({
|
|
287
|
+
node: block,
|
|
288
|
+
properties: { styleId: item.operation.styleId },
|
|
289
|
+
resolvedFormattingFromStyle
|
|
151
290
|
});
|
|
291
|
+
if (patch === null) return {
|
|
292
|
+
tr,
|
|
293
|
+
revisionId: null
|
|
294
|
+
};
|
|
295
|
+
const existing = attrs._propertyChanges;
|
|
296
|
+
const change = revisionInfo ? {
|
|
297
|
+
type: "paragraphPropertyChange",
|
|
298
|
+
info: revisionInfo,
|
|
299
|
+
previousFormatting: paragraphPropertiesSnapshot(block)
|
|
300
|
+
} : null;
|
|
301
|
+
return {
|
|
302
|
+
tr: tr.setNodeMarkup(blockPosition, void 0, {
|
|
303
|
+
...block.attrs,
|
|
304
|
+
...patch,
|
|
305
|
+
...change ? { _propertyChanges: [...Array.isArray(existing) ? existing : [], change] } : {}
|
|
306
|
+
}),
|
|
307
|
+
revisionId: change?.info.id ?? null
|
|
308
|
+
};
|
|
152
309
|
};
|
|
153
310
|
const REPLACEMENT_BACKGROUND_CLEAR_FORMATTING = {
|
|
154
311
|
highlight: false,
|
|
155
312
|
runShading: false
|
|
156
313
|
};
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
highlight: "highlight",
|
|
166
|
-
runShading: "runShading"
|
|
167
|
-
};
|
|
168
|
-
const DIRECT_FONT_PROPERTIES = [
|
|
169
|
-
"fontFamily",
|
|
170
|
-
"fontSize",
|
|
171
|
-
"color"
|
|
172
|
-
];
|
|
173
|
-
const formattingMarkName = (property) => {
|
|
174
|
-
if (!Object.hasOwn(INLINE_FORMATTING_MARK_NAMES, property)) return null;
|
|
175
|
-
const name = Reflect.get(INLINE_FORMATTING_MARK_NAMES, property);
|
|
176
|
-
return typeof name === "string" ? name : null;
|
|
177
|
-
};
|
|
178
|
-
const formattingMarkAttrs = (property, value) => {
|
|
179
|
-
switch (property) {
|
|
180
|
-
case "underline": return { style: "single" };
|
|
181
|
-
case "fontFamily": return typeof value === "string" ? {
|
|
182
|
-
ascii: value,
|
|
183
|
-
hAnsi: value
|
|
184
|
-
} : null;
|
|
185
|
-
case "fontSizePt": return typeof value === "number" ? { size: value * 2 } : null;
|
|
186
|
-
case "color": return typeof value === "string" ? { rgb: value.replace(/^#/u, "").toUpperCase() } : null;
|
|
187
|
-
default: return {};
|
|
314
|
+
const includedFormattingProperty = (includedProperties, property) => includedProperties === void 0 || includedProperties.includes(property);
|
|
315
|
+
const patchAuthoredRunFormatting = ({ authoredFormatting, formatting, includedProperties }) => {
|
|
316
|
+
const next = { ...authoredFormatting };
|
|
317
|
+
for (const property of ["bold", "italic"]) {
|
|
318
|
+
const value = formatting[property];
|
|
319
|
+
if (value === void 0 || !includedFormattingProperty(includedProperties, property)) continue;
|
|
320
|
+
if (value === null) Reflect.deleteProperty(next, property);
|
|
321
|
+
else next[property] = value;
|
|
188
322
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (!markType) continue;
|
|
196
|
-
if (value !== false && value !== null) {
|
|
197
|
-
const attrs = formattingMarkAttrs(property, value);
|
|
198
|
-
if (attrs) tr.addMark(from, to, markType.create(attrs));
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
tr.removeMark(from, to, markType);
|
|
323
|
+
if (formatting.underline !== void 0 && includedFormattingProperty(includedProperties, "underline")) if (formatting.underline === null) delete next.underline;
|
|
324
|
+
else next.underline = { style: formatting.underline ? "single" : "none" };
|
|
325
|
+
if (formatting.strike !== void 0 && includedFormattingProperty(includedProperties, "strike")) {
|
|
326
|
+
delete next.doubleStrike;
|
|
327
|
+
if (formatting.strike === null) delete next.strike;
|
|
328
|
+
else next.strike = formatting.strike;
|
|
202
329
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
330
|
+
if (formatting.fontFamily !== void 0 && includedFormattingProperty(includedProperties, "fontFamily")) if (formatting.fontFamily === null) delete next.fontFamily;
|
|
331
|
+
else next.fontFamily = {
|
|
332
|
+
ascii: formatting.fontFamily,
|
|
333
|
+
hAnsi: formatting.fontFamily
|
|
334
|
+
};
|
|
335
|
+
if (formatting.fontSizePt !== void 0 && includedFormattingProperty(includedProperties, "fontSizePt")) if (formatting.fontSizePt === null) delete next.fontSize;
|
|
336
|
+
else next.fontSize = formatting.fontSizePt * 2;
|
|
337
|
+
if (formatting.color !== void 0 && includedFormattingProperty(includedProperties, "color")) if (formatting.color === null) delete next.color;
|
|
338
|
+
else next.color = { rgb: formatting.color.replace(/^#/u, "").toUpperCase() };
|
|
339
|
+
if (formatting.highlight === false && includedFormattingProperty(includedProperties, "highlight")) delete next.highlight;
|
|
340
|
+
if (formatting.runShading === false && includedFormattingProperty(includedProperties, "runShading")) delete next.shading;
|
|
341
|
+
return next;
|
|
342
|
+
};
|
|
343
|
+
const applyInlineFormatting = ({ tr, from, to, formatting, includedProperties, styleResolver }) => {
|
|
344
|
+
const representations = selectRunFormattingCarrierRepresentations({
|
|
345
|
+
doc: tr.doc,
|
|
206
346
|
from,
|
|
207
|
-
to
|
|
208
|
-
formatting,
|
|
209
|
-
...includedProperties ? { includedProperties } : {}
|
|
347
|
+
to
|
|
210
348
|
});
|
|
349
|
+
for (const representation of representations) {
|
|
350
|
+
const styleContext = paragraphRunStyleContextAt({
|
|
351
|
+
doc: tr.doc,
|
|
352
|
+
pos: representation.from,
|
|
353
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
354
|
+
});
|
|
355
|
+
const authoredFormatting = readAuthoredRunFormatting({
|
|
356
|
+
context: styleContext,
|
|
357
|
+
marks: representation.node.marks,
|
|
358
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
359
|
+
});
|
|
360
|
+
const marks = reconcileRunFormattingMarks({
|
|
361
|
+
authoredFormatting: patchAuthoredRunFormatting({
|
|
362
|
+
authoredFormatting,
|
|
363
|
+
formatting,
|
|
364
|
+
...includedProperties !== void 0 ? { includedProperties } : {}
|
|
365
|
+
}),
|
|
366
|
+
context: styleContext,
|
|
367
|
+
node: representation.node,
|
|
368
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
369
|
+
});
|
|
370
|
+
applyMarksToRunFormattingRepresentation({
|
|
371
|
+
tr,
|
|
372
|
+
representation,
|
|
373
|
+
marks
|
|
374
|
+
});
|
|
375
|
+
}
|
|
211
376
|
return tr;
|
|
212
377
|
};
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
formatting.fontFamily
|
|
220
|
-
],
|
|
221
|
-
[
|
|
222
|
-
"fontSize",
|
|
223
|
-
"fontSizePt",
|
|
224
|
-
formatting.fontSizePt
|
|
225
|
-
],
|
|
226
|
-
[
|
|
227
|
-
"color",
|
|
228
|
-
"color",
|
|
229
|
-
formatting.color
|
|
230
|
-
]
|
|
231
|
-
].filter(([, inputProperty, value]) => value !== void 0 && (!includedProperties || includedProperties.includes(inputProperty)));
|
|
232
|
-
const markType = schema.marks["runFormattingOverride"];
|
|
233
|
-
if (!markType || changed.length === 0) return;
|
|
234
|
-
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
235
|
-
if (!node.isText) return;
|
|
236
|
-
const segmentFrom = Math.max(from, pos);
|
|
237
|
-
const segmentTo = Math.min(to, pos + node.nodeSize);
|
|
238
|
-
const existing = node.marks.find((mark) => mark.type === markType);
|
|
239
|
-
const attrs = existing ? expectRunFormattingOverrideMarkAttrs(existing) : {};
|
|
240
|
-
const directFontProperties = new Set(attrs.directFontProperties);
|
|
241
|
-
for (const [property, , value] of changed) if (value === null) directFontProperties.delete(property);
|
|
242
|
-
else directFontProperties.add(property);
|
|
243
|
-
tr.removeMark(segmentFrom, segmentTo, markType);
|
|
244
|
-
const nextAttrs = {
|
|
245
|
-
...attrs,
|
|
246
|
-
directFontProperties: DIRECT_FONT_PROPERTIES.filter((property) => directFontProperties.has(property))
|
|
247
|
-
};
|
|
248
|
-
if (Object.entries(nextAttrs).some(([key, value]) => key === "directFontProperties" ? Array.isArray(value) && value.length > 0 : value !== null && value !== void 0)) tr.addMark(segmentFrom, segmentTo, markType.create(nextAttrs));
|
|
249
|
-
});
|
|
250
|
-
};
|
|
251
|
-
const directFontPropertyForFormattingProperty = (property) => {
|
|
252
|
-
switch (property) {
|
|
253
|
-
case "fontFamily": return "fontFamily";
|
|
254
|
-
case "fontSizePt": return "fontSize";
|
|
255
|
-
case "color": return "color";
|
|
256
|
-
default: return null;
|
|
378
|
+
const applyMarksToRunFormattingRepresentation = ({ tr, representation, marks }) => {
|
|
379
|
+
const { node, position, from, to } = representation;
|
|
380
|
+
if (Mark.sameSet(node.marks, marks)) return;
|
|
381
|
+
if (!node.isText) {
|
|
382
|
+
tr.setNodeMarkup(position, void 0, node.attrs, marks);
|
|
383
|
+
return;
|
|
257
384
|
}
|
|
385
|
+
for (const current of node.marks) if (!marks.some((candidate) => candidate.eq(current))) tr.removeMark(from, to, current.type);
|
|
386
|
+
for (const next of marks) if (!node.marks.some((candidate) => candidate.eq(next))) tr.addMark(from, to, next);
|
|
258
387
|
};
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const isDirect = overrideMark ? (expectRunFormattingOverrideMarkAttrs(overrideMark).directFontProperties ?? []).includes(directFontProperty) : false;
|
|
266
|
-
if (value === false || value === null) return isDirect;
|
|
267
|
-
if (!isDirect) return true;
|
|
268
|
-
} else if (value === false || value === null) return mark !== void 0;
|
|
269
|
-
if (property === "underline") return mark?.attrs["style"] !== "single";
|
|
270
|
-
if (property === "fontFamily") return mark?.attrs["ascii"] !== value || mark?.attrs["hAnsi"] !== value;
|
|
271
|
-
if (property === "fontSizePt") return Number(mark?.attrs["size"]) !== Number(value) * 2;
|
|
272
|
-
if (property === "color") {
|
|
273
|
-
const current = mark?.attrs["rgb"];
|
|
274
|
-
return (typeof current === "string" ? current.replace(/^#/u, "").toUpperCase() : null) !== String(value).replace(/^#/u, "").toUpperCase();
|
|
388
|
+
const sameDefinedFormattingValue = (left, right) => {
|
|
389
|
+
if (left === right) return true;
|
|
390
|
+
if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
|
|
391
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
392
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
|
|
393
|
+
return left.every((value, index) => sameDefinedFormattingValue(value, right.at(index)));
|
|
275
394
|
}
|
|
276
|
-
|
|
395
|
+
const leftEntries = Object.entries(left).filter(([, value]) => value !== void 0);
|
|
396
|
+
const rightEntries = Object.entries(right).filter(([, value]) => value !== void 0);
|
|
397
|
+
if (leftEntries.length !== rightEntries.length) return false;
|
|
398
|
+
return leftEntries.every(([property, value]) => Object.hasOwn(right, property) && sameDefinedFormattingValue(value, Reflect.get(right, property)));
|
|
277
399
|
};
|
|
278
|
-
const
|
|
400
|
+
const formattingChangesForAuthoredFormatting = (authoredFormatting, formatting) => {
|
|
279
401
|
const changes = [];
|
|
280
|
-
for (const
|
|
402
|
+
for (const property of [
|
|
403
|
+
"bold",
|
|
404
|
+
"italic",
|
|
405
|
+
"underline",
|
|
406
|
+
"strike",
|
|
407
|
+
"fontFamily",
|
|
408
|
+
"fontSizePt",
|
|
409
|
+
"color",
|
|
410
|
+
"highlight",
|
|
411
|
+
"runShading"
|
|
412
|
+
]) {
|
|
413
|
+
if (formatting[property] === void 0) continue;
|
|
414
|
+
const next = patchAuthoredRunFormatting({
|
|
415
|
+
authoredFormatting,
|
|
416
|
+
formatting,
|
|
417
|
+
includedProperties: [property]
|
|
418
|
+
});
|
|
419
|
+
if (!sameDefinedFormattingValue(authoredFormatting, next)) changes.push(property);
|
|
420
|
+
}
|
|
281
421
|
return changes;
|
|
282
422
|
};
|
|
283
|
-
const clearReplacementBackground = ({ tr, schema, from, to, mode,
|
|
284
|
-
if (![schema.marks["highlight"], schema.marks["runShading"]].some((markType) => markType !== void 0 && tr.doc.rangeHasMark(from, to, markType))) return
|
|
285
|
-
|
|
286
|
-
tr,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
423
|
+
const clearReplacementBackground = ({ tr, schema, from, to, mode, revisionIdSeed, author, date, initials, suggestionId = null, styleResolver }) => {
|
|
424
|
+
if (![schema.marks["highlight"], schema.marks["runShading"]].some((markType) => markType !== void 0 && tr.doc.rangeHasMark(from, to, markType))) return {
|
|
425
|
+
type: "applied",
|
|
426
|
+
transaction: tr,
|
|
427
|
+
revisionIds: [],
|
|
428
|
+
nextRevisionId: revisionIdSeed
|
|
429
|
+
};
|
|
430
|
+
if (mode === "direct") return {
|
|
431
|
+
type: "applied",
|
|
432
|
+
transaction: applyInlineFormatting({
|
|
433
|
+
tr,
|
|
434
|
+
from,
|
|
435
|
+
to,
|
|
436
|
+
formatting: REPLACEMENT_BACKGROUND_CLEAR_FORMATTING,
|
|
437
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
438
|
+
}),
|
|
439
|
+
revisionIds: [],
|
|
440
|
+
nextRevisionId: revisionIdSeed
|
|
441
|
+
};
|
|
292
442
|
return applyTrackedInlineFormatting({
|
|
293
443
|
tr,
|
|
294
444
|
schema,
|
|
@@ -296,26 +446,85 @@ const clearReplacementBackground = ({ tr, schema, from, to, mode, revisionId, au
|
|
|
296
446
|
from,
|
|
297
447
|
to,
|
|
298
448
|
formatting: REPLACEMENT_BACKGROUND_CLEAR_FORMATTING,
|
|
299
|
-
|
|
449
|
+
revisionIdSeed,
|
|
300
450
|
author,
|
|
301
451
|
date,
|
|
302
452
|
initials,
|
|
303
|
-
suggestionId
|
|
453
|
+
suggestionId,
|
|
454
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
304
455
|
});
|
|
305
456
|
};
|
|
306
|
-
const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting,
|
|
457
|
+
const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, revisionIdSeed, author, date, initials, suggestionId = null, styleResolver }) => {
|
|
307
458
|
const propertyChangeType = schema.marks["runPropertyChange"];
|
|
308
|
-
if (!propertyChangeType) return
|
|
459
|
+
if (!propertyChangeType) return {
|
|
460
|
+
type: "applied",
|
|
461
|
+
transaction: tr,
|
|
462
|
+
revisionIds: [],
|
|
463
|
+
nextRevisionId: revisionIdSeed
|
|
464
|
+
};
|
|
309
465
|
const segments = [];
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
466
|
+
let hasPendingRunPropertyChange = false;
|
|
467
|
+
const representations = selectRunFormattingCarrierRepresentations({
|
|
468
|
+
doc,
|
|
469
|
+
from,
|
|
470
|
+
to
|
|
471
|
+
});
|
|
472
|
+
for (const representation of representations) {
|
|
473
|
+
const { node } = representation;
|
|
474
|
+
const styleContext = paragraphRunStyleContextAt({
|
|
475
|
+
doc,
|
|
476
|
+
pos: representation.from,
|
|
477
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
478
|
+
});
|
|
479
|
+
const previousFormatting = readAuthoredRunFormatting({
|
|
480
|
+
context: styleContext,
|
|
481
|
+
marks: node.marks,
|
|
482
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
483
|
+
});
|
|
484
|
+
const includedProperties = formattingChangesForAuthoredFormatting(previousFormatting, formatting);
|
|
485
|
+
if (includedProperties.length === 0) continue;
|
|
317
486
|
const existingMark = node.marks.find((mark) => mark.type === propertyChangeType);
|
|
318
|
-
|
|
487
|
+
if (existingMark && expectRunPropertyChangeMarkAttrs(existingMark).changes.length > 0) {
|
|
488
|
+
hasPendingRunPropertyChange = true;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const formattingMarks = reconcileRunFormattingMarks({
|
|
492
|
+
authoredFormatting: patchAuthoredRunFormatting({
|
|
493
|
+
authoredFormatting: previousFormatting,
|
|
494
|
+
formatting,
|
|
495
|
+
includedProperties
|
|
496
|
+
}),
|
|
497
|
+
context: styleContext,
|
|
498
|
+
node,
|
|
499
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
500
|
+
});
|
|
501
|
+
segments.push({
|
|
502
|
+
formattingMarks,
|
|
503
|
+
representation,
|
|
504
|
+
includedProperties,
|
|
505
|
+
previousFormatting
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (hasPendingRunPropertyChange) return {
|
|
509
|
+
type: "pendingRunPropertyChange",
|
|
510
|
+
transaction: tr,
|
|
511
|
+
revisionIds: [],
|
|
512
|
+
nextRevisionId: revisionIdSeed
|
|
513
|
+
};
|
|
514
|
+
if (segments.length === 0) return {
|
|
515
|
+
type: "applied",
|
|
516
|
+
transaction: tr,
|
|
517
|
+
revisionIds: [],
|
|
518
|
+
nextRevisionId: revisionIdSeed
|
|
519
|
+
};
|
|
520
|
+
const suggestionAttrs = suggestionId === null ? {} : {
|
|
521
|
+
provenance: "suggested",
|
|
522
|
+
suggestionId
|
|
523
|
+
};
|
|
524
|
+
const revisionIds = [];
|
|
525
|
+
for (const segment of segments) {
|
|
526
|
+
const revisionId = revisionIdSeed + revisionIds.length;
|
|
527
|
+
revisionIds.push(revisionId);
|
|
319
528
|
const change = {
|
|
320
529
|
type: "runPropertyChange",
|
|
321
530
|
info: {
|
|
@@ -324,35 +533,24 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
|
|
|
324
533
|
date,
|
|
325
534
|
...initials ? { initials } : {}
|
|
326
535
|
},
|
|
327
|
-
...Object.keys(previousFormatting).length > 0 ? { previousFormatting } : {}
|
|
536
|
+
...Object.keys(segment.previousFormatting).length > 0 ? { previousFormatting: segment.previousFormatting } : {}
|
|
328
537
|
};
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
if (segments.length === 0) return tr;
|
|
337
|
-
const suggestionAttrs = suggestionId === null ? {} : {
|
|
338
|
-
provenance: "suggested",
|
|
339
|
-
suggestionId
|
|
340
|
-
};
|
|
341
|
-
for (const segment of segments) {
|
|
342
|
-
applyInlineFormatting({
|
|
538
|
+
const marks = propertyChangeType.create({
|
|
539
|
+
changes: [change],
|
|
540
|
+
...suggestionAttrs
|
|
541
|
+
}).addToSet(segment.formattingMarks);
|
|
542
|
+
applyMarksToRunFormattingRepresentation({
|
|
343
543
|
tr,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
to: segment.to,
|
|
347
|
-
formatting,
|
|
348
|
-
includedProperties: segment.includedProperties
|
|
544
|
+
representation: segment.representation,
|
|
545
|
+
marks
|
|
349
546
|
});
|
|
350
|
-
tr.addMark(segment.from, segment.to, propertyChangeType.create({
|
|
351
|
-
changes: segment.changes,
|
|
352
|
-
...suggestionAttrs
|
|
353
|
-
}));
|
|
354
547
|
}
|
|
355
|
-
return
|
|
548
|
+
return {
|
|
549
|
+
type: "applied",
|
|
550
|
+
transaction: tr,
|
|
551
|
+
revisionIds,
|
|
552
|
+
nextRevisionId: revisionIdSeed + revisionIds.length
|
|
553
|
+
};
|
|
356
554
|
};
|
|
357
555
|
/**
|
|
358
556
|
* Module-scoped monotonic counter for tracked-change revision ids.
|
|
@@ -378,15 +576,17 @@ const nextRevisionSeed = (revisionIdCount) => {
|
|
|
378
576
|
* `insertAfterBlock` / `insertBeforeBlock` (`text` split on line breaks,
|
|
379
577
|
* see `splitInsertParagraphTexts`) allocates one id per paragraph in
|
|
380
578
|
* 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
|
-
*
|
|
579
|
+
* needs more than four once split into more than two paragraphs. Rotating a
|
|
580
|
+
* terminal run can add one paragraph-property revision per inserted paragraph;
|
|
581
|
+
* reserving less than that would let a later `nextRevisionSeed` call reuse an
|
|
582
|
+
* id this operation already stamped on the document.
|
|
384
583
|
*/
|
|
385
584
|
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
585
|
const estimateRevisionIdReservation = (item) => {
|
|
389
|
-
if (item.operation.type === "insertAfterBlock" || item.operation.type === "insertBeforeBlock")
|
|
586
|
+
if (item.operation.type === "insertAfterBlock" || item.operation.type === "insertBeforeBlock") {
|
|
587
|
+
const paragraphCount = item.insertTexts?.length ?? 1;
|
|
588
|
+
return Math.max(paragraphCount * 3, REVISION_IDS_PER_OPERATION);
|
|
589
|
+
}
|
|
390
590
|
return REVISION_IDS_PER_OPERATION;
|
|
391
591
|
};
|
|
392
592
|
/**
|
|
@@ -591,10 +791,18 @@ const buildEmphasisInlineContent = (schema, text, baseMarks) => {
|
|
|
591
791
|
};
|
|
592
792
|
const isBatchableParagraphInsertion = (item, mode) => {
|
|
593
793
|
if (item.operation.type !== "insertAfterBlock" && item.operation.type !== "insertBeforeBlock") return false;
|
|
794
|
+
if (item.commentText !== void 0) return false;
|
|
594
795
|
const insertTexts = item.insertTexts ?? [""];
|
|
595
796
|
const isEmptyInsert = insertTexts.length === 1 && insertTexts[0]?.length === 0;
|
|
596
797
|
return !(mode === "tracked-changes" && item.operation.pageBreakBefore === true && isEmptyInsert);
|
|
597
798
|
};
|
|
799
|
+
const addedBreakRevisionId = (value) => {
|
|
800
|
+
if (typeof value !== "object" || value === null || !("kind" in value) || !("info" in value)) return null;
|
|
801
|
+
if (value.kind !== "ins" && value.kind !== "moveTo") return null;
|
|
802
|
+
const info = value.info;
|
|
803
|
+
if (typeof info !== "object" || info === null || !("id" in info)) return null;
|
|
804
|
+
return typeof info.id === "number" ? info.id : null;
|
|
805
|
+
};
|
|
598
806
|
/**
|
|
599
807
|
* Move an ADDED paragraph break off every paragraph its container ends with.
|
|
600
808
|
*
|
|
@@ -604,11 +812,10 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
604
812
|
* revision neither accepting nor rejecting everything can clear.
|
|
605
813
|
*
|
|
606
814
|
* 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.
|
|
815
|
+
* does. The first inserted break moves onto the paragraph the run was appended
|
|
816
|
+
* after, every later break moves one paragraph left, and the paragraph the
|
|
817
|
+
* container now ends with takes the free mark. Only which paragraph is left
|
|
818
|
+
* markless changes; the order and ownership of the breaks do not.
|
|
612
819
|
*
|
|
613
820
|
* A paragraph's properties live on its mark, so the paragraph that inherits
|
|
614
821
|
* the free one records the other's as `w:pPrChange` — what a rejection reads
|
|
@@ -621,50 +828,96 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
621
828
|
*/
|
|
622
829
|
const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials }) => {
|
|
623
830
|
const paragraphTypeName = tr.doc.type.schema.nodes["paragraph"]?.name ?? "paragraph";
|
|
624
|
-
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).
|
|
831
|
+
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).flatMap(({ node, position }) => {
|
|
625
832
|
const mark = node.attrs["pPrMark"];
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
833
|
+
const revisionId = addedBreakRevisionId(mark);
|
|
834
|
+
return revisionId !== null && batchRevisionIds.has(revisionId) ? [{
|
|
835
|
+
mark,
|
|
836
|
+
node,
|
|
837
|
+
ownerRevisionId: revisionId,
|
|
838
|
+
position
|
|
839
|
+
}] : [];
|
|
631
840
|
});
|
|
632
841
|
let next = tr;
|
|
633
842
|
let nextRevisionId = revisionSeed;
|
|
634
|
-
|
|
635
|
-
|
|
843
|
+
const synthesizedRevisions = [];
|
|
844
|
+
for (const { position: finalPosition, node: final, mark: finalMark } of rotations) {
|
|
845
|
+
const resolved = next.doc.resolve(finalPosition);
|
|
846
|
+
const carrier = addedBreakCarrierBefore(resolved, final.type.name);
|
|
636
847
|
if (!carrier) {
|
|
637
848
|
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
638
849
|
continue;
|
|
639
850
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
851
|
+
const path = [{
|
|
852
|
+
node: final,
|
|
853
|
+
position: finalPosition
|
|
854
|
+
}];
|
|
855
|
+
let position = finalPosition;
|
|
856
|
+
for (let index = resolved.index() - 1; index >= 0; index--) {
|
|
857
|
+
const sibling = resolved.parent.child(index);
|
|
858
|
+
position -= sibling.nodeSize;
|
|
859
|
+
if (sibling.type.name !== final.type.name) continue;
|
|
860
|
+
path.push({
|
|
861
|
+
node: sibling,
|
|
862
|
+
position
|
|
863
|
+
});
|
|
864
|
+
if (position === carrier.position) break;
|
|
865
|
+
}
|
|
866
|
+
if (path.at(-1)?.position !== carrier.position) {
|
|
643
867
|
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
644
868
|
continue;
|
|
645
869
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
870
|
+
if (path.slice(0, -1).some(({ node }) => {
|
|
871
|
+
const revisionId = addedBreakRevisionId(node.attrs["pPrMark"]);
|
|
872
|
+
return revisionId === null || !batchRevisionIds.has(revisionId);
|
|
873
|
+
})) continue;
|
|
874
|
+
const previousFormatting = paragraphPropertiesBeforeBatch(carrier.node, batchRevisionIds);
|
|
875
|
+
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
876
|
+
let carriedMark = finalMark;
|
|
877
|
+
for (let index = 1; index < path.length; index++) {
|
|
878
|
+
const current = path[index - 1];
|
|
879
|
+
const previous = path[index];
|
|
880
|
+
if (!current || !previous) panic("A final-mark rotation exceeded its paragraph path", { index });
|
|
881
|
+
const ownerRevisionId = addedBreakRevisionId(carriedMark);
|
|
882
|
+
if (ownerRevisionId === null || !batchRevisionIds.has(ownerRevisionId)) panic("A final-mark rotation crossed a break outside its operation batch", { ownerRevisionId });
|
|
883
|
+
const displacedMark = previous.node.attrs["pPrMark"];
|
|
884
|
+
next = next.setNodeAttribute(previous.position, "pPrMark", carriedMark);
|
|
885
|
+
const currentFormatting = paragraphPropertiesSnapshot(current.node);
|
|
886
|
+
if (JSON.stringify(previousFormatting) !== JSON.stringify(currentFormatting)) {
|
|
887
|
+
const liveCurrent = next.doc.nodeAt(current.position);
|
|
888
|
+
if (!liveCurrent || liveCurrent.type.name !== final.type.name) panic("A final-mark rotation lost its paragraph", { position: current.position });
|
|
889
|
+
const existing = expectParagraphAttrs(liveCurrent)._propertyChanges;
|
|
890
|
+
if (hasSerializableParagraphPropertyChange(existing)) panic("A final-mark rotation cannot append a second w:pPrChange", { position: current.position });
|
|
891
|
+
const revisionId = nextRevisionId++;
|
|
892
|
+
next = next.setNodeMarkup(current.position, void 0, {
|
|
893
|
+
...liveCurrent.attrs,
|
|
894
|
+
_propertyChanges: [...Array.isArray(existing) ? existing : [], {
|
|
895
|
+
type: "paragraphPropertyChange",
|
|
896
|
+
info: {
|
|
897
|
+
id: revisionId,
|
|
898
|
+
author,
|
|
899
|
+
date,
|
|
900
|
+
...initials ? { initials } : {}
|
|
901
|
+
},
|
|
902
|
+
previousFormatting
|
|
903
|
+
}]
|
|
904
|
+
});
|
|
905
|
+
synthesizedRevisions.push({
|
|
906
|
+
ownerRevisionId,
|
|
907
|
+
revisionId
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
if (displacedMark == null) break;
|
|
911
|
+
carriedMark = displacedMark;
|
|
912
|
+
}
|
|
661
913
|
}
|
|
662
914
|
return {
|
|
663
915
|
transaction: next,
|
|
664
|
-
nextRevisionId
|
|
916
|
+
nextRevisionId,
|
|
917
|
+
synthesizedRevisions
|
|
665
918
|
};
|
|
666
919
|
};
|
|
667
|
-
const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, commentMark, suggestionId, revisionSeed, isPairedMove }) => {
|
|
920
|
+
const buildInsertedParagraphs = ({ item, schema, formattingFromStyle, mode, author, date, initials, commentMark, suggestionId, revisionSeed, isPairedMove, numbering }) => {
|
|
668
921
|
const operation = item.operation;
|
|
669
922
|
if (operation.type !== "insertAfterBlock" && operation.type !== "insertBeforeBlock") panic("Only paragraph insertions can build inserted paragraphs", { type: operation.type });
|
|
670
923
|
const insertionType = schema.marks["insertion"];
|
|
@@ -677,7 +930,12 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
677
930
|
suggestionId
|
|
678
931
|
} : {}
|
|
679
932
|
};
|
|
680
|
-
const baseAttrs = operation.inheritFormatting === false ? {} :
|
|
933
|
+
const baseAttrs = operation.inheritFormatting === false ? {} : {
|
|
934
|
+
...stripBlockIdentityAttrs(item.blockNode.attrs),
|
|
935
|
+
_propertyChanges: null,
|
|
936
|
+
pPrMark: null,
|
|
937
|
+
_suggestedInsert: null
|
|
938
|
+
};
|
|
681
939
|
const insertTexts = item.insertTexts ?? [""];
|
|
682
940
|
const revisionIds = [];
|
|
683
941
|
const nodes = [];
|
|
@@ -701,20 +959,51 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
701
959
|
const content = text.length > 0 ? buildEmphasisInlineContent(schema, text, marks) : null;
|
|
702
960
|
const attrs = isFirstParagraph ? { ...baseAttrs } : {};
|
|
703
961
|
if (isFirstParagraph && operation.pageBreakBefore === true) attrs["pageBreakBefore"] = true;
|
|
704
|
-
|
|
962
|
+
const listLevel = operation.listLevel;
|
|
963
|
+
if (isFirstParagraph && listLevel === null) {
|
|
705
964
|
attrs["numPr"] = null;
|
|
706
|
-
Object.assign(attrs,
|
|
707
|
-
} else if (isFirstParagraph &&
|
|
708
|
-
const anchorNumPr = baseAttrs
|
|
965
|
+
Object.assign(attrs, CLEARED_LIST_RENDERING_ATTRS);
|
|
966
|
+
} else if (isFirstParagraph && typeof listLevel === "number") {
|
|
967
|
+
const anchorNumPr = Reflect.get(baseAttrs, "numPr");
|
|
709
968
|
const numId = typeof anchorNumPr === "object" && anchorNumPr !== null && "numId" in anchorNumPr ? anchorNumPr.numId : void 0;
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
ilvl:
|
|
713
|
-
};
|
|
969
|
+
if (typeof numId === "number") Object.assign(attrs, listLevelAttrPatch(operation.inheritFormatting === false ? {} : expectParagraphAttrs(item.blockNode), {
|
|
970
|
+
numId,
|
|
971
|
+
ilvl: listLevel
|
|
972
|
+
}, numbering));
|
|
973
|
+
else {
|
|
974
|
+
attrs["numPr"] = { ilvl: listLevel };
|
|
975
|
+
Object.assign(attrs, CLEARED_LIST_RENDERING_ATTRS);
|
|
976
|
+
}
|
|
714
977
|
}
|
|
715
978
|
if (isFirstParagraph && operation.styleId !== void 0) {
|
|
716
979
|
attrs["styleId"] = operation.styleId;
|
|
717
|
-
if (operation.inheritFormatting !== false && operation.styleId !== null) Object.assign(attrs,
|
|
980
|
+
if (operation.inheritFormatting !== false && operation.styleId !== null) Object.assign(attrs, CLEARED_LIST_RENDERING_ATTRS);
|
|
981
|
+
}
|
|
982
|
+
if (isFirstParagraph && (operation.styleId !== void 0 || operation.alignment !== void 0 || operation.spacing !== void 0)) {
|
|
983
|
+
const inheritedDirectAlignment = operation.inheritFormatting === false ? void 0 : directParagraphAlignment(expectParagraphAttrs(item.blockNode));
|
|
984
|
+
const inheritedDirectSpacing = operation.inheritFormatting === false ? void 0 : directParagraphSpacing(expectParagraphAttrs(item.blockNode));
|
|
985
|
+
const directAlignment = operation.alignment === void 0 ? inheritedDirectAlignment : operation.alignment ?? void 0;
|
|
986
|
+
const directSpacing = operation.spacing === void 0 ? inheritedDirectSpacing : operation.spacing ?? void 0;
|
|
987
|
+
attrs["alignmentFromStyle"] = formattingFromStyle?.alignment;
|
|
988
|
+
attrs["alignment"] = directAlignment ?? formattingFromStyle?.alignment ?? null;
|
|
989
|
+
const sourceFormatting = attrs["_originalFormatting"];
|
|
990
|
+
let originalFormatting = typeof sourceFormatting === "object" && sourceFormatting !== null ? { ...sourceFormatting } : void 0;
|
|
991
|
+
if (operation.styleId !== void 0) {
|
|
992
|
+
originalFormatting ??= {};
|
|
993
|
+
if (operation.styleId === null) Reflect.deleteProperty(originalFormatting, "styleId");
|
|
994
|
+
else originalFormatting.styleId = operation.styleId;
|
|
995
|
+
}
|
|
996
|
+
if (operation.alignment !== void 0 || inheritedDirectAlignment !== void 0) {
|
|
997
|
+
originalFormatting ??= {};
|
|
998
|
+
if (directAlignment === void 0) Reflect.deleteProperty(originalFormatting, "alignment");
|
|
999
|
+
else originalFormatting.alignment = directAlignment;
|
|
1000
|
+
}
|
|
1001
|
+
if (operation.spacing !== void 0) originalFormatting = withDirectParagraphSpacing(originalFormatting, directSpacing);
|
|
1002
|
+
if (operation.styleId !== void 0 || operation.spacing !== void 0) Object.assign(attrs, paragraphSpacingAttrPatch({
|
|
1003
|
+
direct: directSpacing,
|
|
1004
|
+
inherited: paragraphSpacingFromFormatting(formattingFromStyle)
|
|
1005
|
+
}));
|
|
1006
|
+
attrs["_originalFormatting"] = originalFormatting && Object.keys(originalFormatting).length > 0 ? originalFormatting : null;
|
|
718
1007
|
}
|
|
719
1008
|
if (isSuggested && suggestionId !== null && paragraphRevisionId !== null) attrs["_suggestedInsert"] = {
|
|
720
1009
|
suggestionId,
|
|
@@ -747,7 +1036,7 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
747
1036
|
nextRevisionId
|
|
748
1037
|
};
|
|
749
1038
|
};
|
|
750
|
-
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, tableTemplates }) => {
|
|
1039
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, wordDiffMode = "bounded", tableTemplates }) => {
|
|
751
1040
|
const applied = [];
|
|
752
1041
|
const skipped = [];
|
|
753
1042
|
const normalizations = [];
|
|
@@ -755,8 +1044,45 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
755
1044
|
const insertionType = view.state.schema.marks["insertion"];
|
|
756
1045
|
const deletionType = view.state.schema.marks["deletion"];
|
|
757
1046
|
const commentType = view.state.schema.marks["comment"];
|
|
1047
|
+
const occupiedCommentIds = /* @__PURE__ */ new Set();
|
|
1048
|
+
if (commentType) view.state.doc.descendants((node) => {
|
|
1049
|
+
for (const mark of node.marks) {
|
|
1050
|
+
if (mark.type !== commentType) continue;
|
|
1051
|
+
const commentId = mark.attrs["commentId"];
|
|
1052
|
+
if (typeof commentId === "number") occupiedCommentIds.add(commentId);
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
let provisionalCommentIdCursor = Number.MIN_SAFE_INTEGER;
|
|
1056
|
+
const reserveProvisionalCommentId = () => {
|
|
1057
|
+
while (occupiedCommentIds.has(provisionalCommentIdCursor)) provisionalCommentIdCursor += 1;
|
|
1058
|
+
const id = provisionalCommentIdCursor;
|
|
1059
|
+
provisionalCommentIdCursor += 1;
|
|
1060
|
+
occupiedCommentIds.add(id);
|
|
1061
|
+
return id;
|
|
1062
|
+
};
|
|
1063
|
+
const allocateCommittedCommentId = (text) => {
|
|
1064
|
+
if (createCommentId === void 0) panic("A resolved comment operation lost its comment-id allocator");
|
|
1065
|
+
const id = createCommentId(text);
|
|
1066
|
+
if (!Number.isSafeInteger(id) || occupiedCommentIds.has(id)) panic("A comment-id allocator returned an invalid or duplicate id", { id });
|
|
1067
|
+
occupiedCommentIds.add(id);
|
|
1068
|
+
return id;
|
|
1069
|
+
};
|
|
1070
|
+
const styleResolver = getDocumentStyleResolver(view.state);
|
|
1071
|
+
const numbering = getDocumentNumbering(view.state);
|
|
1072
|
+
const formattingFromStyleForInsertion = (item) => {
|
|
1073
|
+
if (item.operation.type !== "insertAfterBlock" && item.operation.type !== "insertBeforeBlock") return;
|
|
1074
|
+
const anchorAttrs = expectParagraphAttrs(item.blockNode);
|
|
1075
|
+
const inheritedStyleId = item.operation.inheritFormatting === false ? null : anchorAttrs.styleId;
|
|
1076
|
+
const styleId = item.operation.styleId !== void 0 ? item.operation.styleId : inheritedStyleId;
|
|
1077
|
+
return resolveFormattingFromStyle({
|
|
1078
|
+
attrs: anchorAttrs,
|
|
1079
|
+
styleId,
|
|
1080
|
+
styleResolver
|
|
1081
|
+
});
|
|
1082
|
+
};
|
|
758
1083
|
const claimedTableRows = /* @__PURE__ */ new Set();
|
|
759
1084
|
const claimedTableColumns = /* @__PURE__ */ new Set();
|
|
1085
|
+
const diffText = wordDiffMode === "coarse" ? coarseWordDiff : wordDiffSessionFromOptions(wordDiff).diff;
|
|
760
1086
|
const producesTrackedChanges = mode !== "direct";
|
|
761
1087
|
const isSuggested = mode === "suggested";
|
|
762
1088
|
if (producesTrackedChanges && (!insertionType || !deletionType)) return {
|
|
@@ -792,6 +1118,13 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
792
1118
|
});
|
|
793
1119
|
continue;
|
|
794
1120
|
}
|
|
1121
|
+
if (commentText !== void 0 && !hasRepresentableCommentAnchor(resolution.operation, mode)) {
|
|
1122
|
+
skipped.push({
|
|
1123
|
+
id: operation.id,
|
|
1124
|
+
reason: "unsupportedBlock"
|
|
1125
|
+
});
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
795
1128
|
const deletion = resolution.operation.tableRowDeletion;
|
|
796
1129
|
if (deletion) {
|
|
797
1130
|
const rowKey = `${deletion.tablePosition}:${deletion.rowIndex}`;
|
|
@@ -821,11 +1154,10 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
821
1154
|
code: "splitMultilineText",
|
|
822
1155
|
paragraphCount: resolution.operation.insertTexts?.length ?? 1
|
|
823
1156
|
});
|
|
824
|
-
const commentId = commentText !== void 0 ? createCommentId?.(commentText) : void 0;
|
|
825
1157
|
resolved.push({
|
|
826
1158
|
...resolution.operation,
|
|
827
1159
|
originalIndex: index,
|
|
828
|
-
...
|
|
1160
|
+
...commentText !== void 0 && { commentText }
|
|
829
1161
|
});
|
|
830
1162
|
}
|
|
831
1163
|
const tablePlan = planTableMutations(resolved.map((item) => ({
|
|
@@ -868,6 +1200,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
868
1200
|
}
|
|
869
1201
|
let tr = view.state.tr;
|
|
870
1202
|
const revisionIdReservation = executableResolved.reduce((total, item) => total + estimateRevisionIdReservation(item), 0);
|
|
1203
|
+
const ownsSharedRevisionIdCursor = revisionIdSeed === void 0 && revisionStamp === void 0;
|
|
871
1204
|
let revisionSeed = revisionIdSeed ?? revisionStamp?.idSeed ?? nextRevisionSeed(revisionIdReservation);
|
|
872
1205
|
const date = revisionStamp?.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
873
1206
|
const insertedColumnCounts = /* @__PURE__ */ new Map();
|
|
@@ -899,6 +1232,17 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
899
1232
|
for (let executionIndex = 0; executionIndex < executionOrder.length; executionIndex++) {
|
|
900
1233
|
const item = executionOrder[executionIndex];
|
|
901
1234
|
if (!item) panic("The operation execution index exceeded the resolved plan", { executionIndex });
|
|
1235
|
+
if (mode === "tracked-changes" && writesParagraphPropertyChange(item)) {
|
|
1236
|
+
const livePosition = tr.mapping.map(item.blockFrom);
|
|
1237
|
+
const liveBlock = tr.doc.nodeAt(livePosition) ?? item.blockNode;
|
|
1238
|
+
if (hasSerializableParagraphPropertyChange(liveBlock.type.name === "paragraph" ? expectParagraphAttrs(liveBlock)._propertyChanges : void 0)) {
|
|
1239
|
+
skipped.push({
|
|
1240
|
+
id: item.operation.id,
|
|
1241
|
+
reason: "pendingParagraphPropertyChange"
|
|
1242
|
+
});
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
902
1246
|
if (isBatchableParagraphInsertion(item, mode)) {
|
|
903
1247
|
const insertionRun = [item];
|
|
904
1248
|
for (let lookahead = executionIndex + 1; lookahead < executionOrder.length; lookahead++) {
|
|
@@ -911,24 +1255,24 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
911
1255
|
const runApplied = [];
|
|
912
1256
|
for (const insertion of insertionRun) {
|
|
913
1257
|
const insertionSuggestionId = isSuggested ? insertion.operation.suggestionId ?? insertion.operation.id : null;
|
|
914
|
-
const insertionCommentMark = insertion.commentId !== void 0 && commentType ? commentType.create({ commentId: insertion.commentId }) : null;
|
|
915
1258
|
const built = buildInsertedParagraphs({
|
|
916
1259
|
item: insertion,
|
|
917
1260
|
schema: view.state.schema,
|
|
1261
|
+
formattingFromStyle: formattingFromStyleForInsertion(insertion),
|
|
918
1262
|
mode,
|
|
919
1263
|
author,
|
|
920
1264
|
date,
|
|
921
1265
|
initials,
|
|
922
|
-
commentMark:
|
|
1266
|
+
commentMark: null,
|
|
923
1267
|
suggestionId: insertionSuggestionId,
|
|
924
1268
|
revisionSeed,
|
|
925
|
-
isPairedMove
|
|
1269
|
+
isPairedMove,
|
|
1270
|
+
numbering
|
|
926
1271
|
});
|
|
927
1272
|
revisionSeed = built.nextRevisionId;
|
|
928
1273
|
nodeGroups.push(built.nodes);
|
|
929
1274
|
runApplied.push({
|
|
930
1275
|
id: insertion.operation.id,
|
|
931
|
-
...insertion.commentId !== void 0 && { commentId: insertion.commentId },
|
|
932
1276
|
...built.revisionIds[0] !== void 0 && {
|
|
933
1277
|
revisionId: built.revisionIds[0],
|
|
934
1278
|
revisionIds: built.revisionIds
|
|
@@ -944,7 +1288,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
944
1288
|
continue;
|
|
945
1289
|
}
|
|
946
1290
|
}
|
|
947
|
-
const
|
|
1291
|
+
const provisionalCommentId = item.commentText !== void 0 ? reserveProvisionalCommentId() : void 0;
|
|
1292
|
+
const commentMark = provisionalCommentId !== void 0 && commentType ? commentType.create({ commentId: provisionalCommentId }) : null;
|
|
948
1293
|
const stepsBefore = tr.steps.length;
|
|
949
1294
|
let appliedRevisionIds;
|
|
950
1295
|
if (isSuggested && !SUGGESTED_SUPPORTED_OPERATION_TYPES.has(item.operation.type)) {
|
|
@@ -962,8 +1307,9 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
962
1307
|
suggestionId
|
|
963
1308
|
} : {}
|
|
964
1309
|
};
|
|
1310
|
+
let operationRevisionSeed = revisionSeed;
|
|
965
1311
|
const structuralRevision = producesTrackedChanges ? {
|
|
966
|
-
revisionId:
|
|
1312
|
+
revisionId: operationRevisionSeed,
|
|
967
1313
|
author,
|
|
968
1314
|
date,
|
|
969
1315
|
...trackedRevisionExtras
|
|
@@ -971,22 +1317,32 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
971
1317
|
switch (item.operation.type) {
|
|
972
1318
|
case "replaceInBlock":
|
|
973
1319
|
case "replaceRange": {
|
|
974
|
-
const revisionIdDelete =
|
|
975
|
-
const revisionIdInsert =
|
|
976
|
-
const
|
|
1320
|
+
const revisionIdDelete = operationRevisionSeed;
|
|
1321
|
+
const revisionIdInsert = producesTrackedChanges ? operationRevisionSeed + 1 : operationRevisionSeed;
|
|
1322
|
+
const revisionIdBackgroundSeed = producesTrackedChanges ? operationRevisionSeed + 2 : operationRevisionSeed;
|
|
977
1323
|
const stepsBeforeBackgroundClear = tr.steps.length;
|
|
978
|
-
|
|
1324
|
+
const backgroundResult = clearReplacementBackground({
|
|
979
1325
|
tr,
|
|
980
1326
|
schema: view.state.schema,
|
|
981
1327
|
from: item.from,
|
|
982
1328
|
to: item.to,
|
|
983
1329
|
mode,
|
|
984
|
-
|
|
1330
|
+
revisionIdSeed: revisionIdBackgroundSeed,
|
|
985
1331
|
author,
|
|
986
1332
|
date,
|
|
987
1333
|
initials,
|
|
988
|
-
suggestionId
|
|
1334
|
+
suggestionId,
|
|
1335
|
+
styleResolver
|
|
989
1336
|
});
|
|
1337
|
+
if (backgroundResult.type === "pendingRunPropertyChange") {
|
|
1338
|
+
skipped.push({
|
|
1339
|
+
id: item.operation.id,
|
|
1340
|
+
reason: backgroundResult.type
|
|
1341
|
+
});
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
tr = backgroundResult.transaction;
|
|
1345
|
+
operationRevisionSeed = backgroundResult.nextRevisionId;
|
|
990
1346
|
const clearedBackground = tr.steps.length > stepsBeforeBackgroundClear;
|
|
991
1347
|
tr = applyTextReplacement({
|
|
992
1348
|
tr,
|
|
@@ -999,12 +1355,12 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
999
1355
|
commentMark,
|
|
1000
1356
|
suggestionId,
|
|
1001
1357
|
initials,
|
|
1002
|
-
|
|
1358
|
+
diffText
|
|
1003
1359
|
});
|
|
1004
1360
|
if (producesTrackedChanges) appliedRevisionIds = [
|
|
1005
1361
|
revisionIdDelete,
|
|
1006
1362
|
revisionIdInsert,
|
|
1007
|
-
...clearedBackground ?
|
|
1363
|
+
...clearedBackground ? backgroundResult.revisionIds : []
|
|
1008
1364
|
];
|
|
1009
1365
|
break;
|
|
1010
1366
|
}
|
|
@@ -1013,89 +1369,131 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1013
1369
|
break;
|
|
1014
1370
|
case "formatRange":
|
|
1015
1371
|
if (producesTrackedChanges) {
|
|
1016
|
-
const
|
|
1017
|
-
tr = applyTrackedInlineFormatting({
|
|
1372
|
+
const formattingResult = applyTrackedInlineFormatting({
|
|
1018
1373
|
tr,
|
|
1019
1374
|
schema: view.state.schema,
|
|
1020
1375
|
doc: tr.doc,
|
|
1021
1376
|
from: item.from,
|
|
1022
1377
|
to: item.to,
|
|
1023
1378
|
formatting: item.operation.formatting,
|
|
1024
|
-
|
|
1379
|
+
revisionIdSeed: operationRevisionSeed,
|
|
1025
1380
|
author,
|
|
1026
1381
|
date,
|
|
1027
1382
|
initials,
|
|
1028
|
-
suggestionId
|
|
1383
|
+
suggestionId,
|
|
1384
|
+
styleResolver
|
|
1029
1385
|
});
|
|
1030
|
-
|
|
1386
|
+
if (formattingResult.type === "pendingRunPropertyChange") {
|
|
1387
|
+
skipped.push({
|
|
1388
|
+
id: item.operation.id,
|
|
1389
|
+
reason: formattingResult.type
|
|
1390
|
+
});
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
tr = formattingResult.transaction;
|
|
1394
|
+
operationRevisionSeed = formattingResult.nextRevisionId;
|
|
1395
|
+
appliedRevisionIds = [...formattingResult.revisionIds];
|
|
1031
1396
|
break;
|
|
1032
1397
|
}
|
|
1033
1398
|
tr = applyInlineFormatting({
|
|
1034
1399
|
tr,
|
|
1035
|
-
schema: view.state.schema,
|
|
1036
1400
|
from: item.from,
|
|
1037
1401
|
to: item.to,
|
|
1038
|
-
formatting: item.operation.formatting
|
|
1402
|
+
formatting: item.operation.formatting,
|
|
1403
|
+
...styleResolver !== void 0 ? { styleResolver } : {}
|
|
1039
1404
|
});
|
|
1040
1405
|
break;
|
|
1041
1406
|
case "replaceBlock": {
|
|
1042
|
-
|
|
1043
|
-
const
|
|
1044
|
-
const
|
|
1407
|
+
if (!isResolvedReplaceBlockOperation(item)) panic("Resolved replaceBlock operation lost its impact discriminator");
|
|
1408
|
+
const { changesStyle, changesText } = REPLACE_BLOCK_IMPACT[item.replaceBlockImpact];
|
|
1409
|
+
const revisionIdDelete = operationRevisionSeed;
|
|
1410
|
+
const revisionIdInsert = changesText ? operationRevisionSeed + 1 : operationRevisionSeed;
|
|
1411
|
+
const revisionIdBackgroundSeed = changesText ? operationRevisionSeed + 2 : operationRevisionSeed;
|
|
1412
|
+
if (producesTrackedChanges && changesText) operationRevisionSeed = revisionIdBackgroundSeed;
|
|
1045
1413
|
if (item.operation.preserveFormatting === false && mode === "direct") {
|
|
1046
1414
|
const replacement = item.operation.text;
|
|
1047
1415
|
const paragraphType = view.state.schema.nodes["paragraph"];
|
|
1048
1416
|
if (paragraphType) {
|
|
1049
|
-
const
|
|
1050
|
-
const node = paragraphType.create(replaceAttrs, replacement.length === 0 ? null : buildEmphasisInlineContent(view.state.schema, replacement, []));
|
|
1417
|
+
const node = paragraphType.create(null, replacement.length === 0 ? null : buildEmphasisInlineContent(view.state.schema, replacement, commentMark ? [commentMark] : []));
|
|
1051
1418
|
tr = tr.replaceWith(item.blockFrom, item.blockTo, node);
|
|
1419
|
+
tr = applyReplaceBlockStyleId({
|
|
1420
|
+
item,
|
|
1421
|
+
tr,
|
|
1422
|
+
styleResolver
|
|
1423
|
+
}).tr;
|
|
1052
1424
|
break;
|
|
1053
1425
|
}
|
|
1054
1426
|
}
|
|
1055
1427
|
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] : []));
|
|
1428
|
+
const node = item.blockNode.type.create({ ...item.blockNode.attrs }, buildEmphasisInlineContent(view.state.schema, item.operation.text, commentMark ? [commentMark] : []));
|
|
1061
1429
|
tr = tr.replaceWith(item.blockFrom, item.blockTo, node);
|
|
1430
|
+
tr = applyReplaceBlockStyleId({
|
|
1431
|
+
item,
|
|
1432
|
+
tr,
|
|
1433
|
+
styleResolver
|
|
1434
|
+
}).tr;
|
|
1062
1435
|
break;
|
|
1063
1436
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1437
|
+
let clearedBackground = false;
|
|
1438
|
+
let backgroundRevisionIds = [];
|
|
1439
|
+
if (changesText) {
|
|
1440
|
+
const stepsBeforeBackgroundClear = tr.steps.length;
|
|
1441
|
+
const backgroundResult = clearReplacementBackground({
|
|
1442
|
+
tr,
|
|
1443
|
+
schema: view.state.schema,
|
|
1444
|
+
from: item.from,
|
|
1445
|
+
to: item.to,
|
|
1446
|
+
mode,
|
|
1447
|
+
revisionIdSeed: revisionIdBackgroundSeed,
|
|
1448
|
+
author,
|
|
1449
|
+
date,
|
|
1450
|
+
initials,
|
|
1451
|
+
suggestionId,
|
|
1452
|
+
styleResolver
|
|
1453
|
+
});
|
|
1454
|
+
if (backgroundResult.type === "pendingRunPropertyChange") {
|
|
1455
|
+
skipped.push({
|
|
1456
|
+
id: item.operation.id,
|
|
1457
|
+
reason: backgroundResult.type
|
|
1458
|
+
});
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
tr = backgroundResult.transaction;
|
|
1462
|
+
operationRevisionSeed = backgroundResult.nextRevisionId;
|
|
1463
|
+
clearedBackground = tr.steps.length > stepsBeforeBackgroundClear;
|
|
1464
|
+
backgroundRevisionIds = backgroundResult.revisionIds;
|
|
1465
|
+
tr = applyTextReplacement({
|
|
1466
|
+
tr,
|
|
1467
|
+
item,
|
|
1468
|
+
mode,
|
|
1469
|
+
author,
|
|
1470
|
+
date,
|
|
1471
|
+
revisionIdDelete,
|
|
1472
|
+
revisionIdInsert,
|
|
1473
|
+
commentMark,
|
|
1474
|
+
suggestionId,
|
|
1475
|
+
initials,
|
|
1476
|
+
diffText
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
const revisionIdParagraph = producesTrackedChanges && changesStyle ? operationRevisionSeed++ : null;
|
|
1480
|
+
const styleResult = applyReplaceBlockStyleId({
|
|
1092
1481
|
item,
|
|
1093
|
-
tr
|
|
1482
|
+
tr,
|
|
1483
|
+
styleResolver,
|
|
1484
|
+
...revisionIdParagraph !== null ? { revisionInfo: {
|
|
1485
|
+
id: revisionIdParagraph,
|
|
1486
|
+
author,
|
|
1487
|
+
date,
|
|
1488
|
+
...trackedRevisionExtras
|
|
1489
|
+
} } : {}
|
|
1094
1490
|
});
|
|
1491
|
+
tr = styleResult.tr;
|
|
1492
|
+
if (!changesText && commentMark) tr = tr.addMark(item.from, item.to, commentMark);
|
|
1095
1493
|
if (producesTrackedChanges) appliedRevisionIds = [
|
|
1096
|
-
revisionIdDelete,
|
|
1097
|
-
|
|
1098
|
-
...
|
|
1494
|
+
...changesText ? [revisionIdDelete, revisionIdInsert] : [],
|
|
1495
|
+
...clearedBackground ? backgroundRevisionIds : [],
|
|
1496
|
+
...styleResult.revisionId === null ? [] : [styleResult.revisionId]
|
|
1099
1497
|
];
|
|
1100
1498
|
break;
|
|
1101
1499
|
}
|
|
@@ -1113,16 +1511,18 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1113
1511
|
const built = buildInsertedParagraphs({
|
|
1114
1512
|
item,
|
|
1115
1513
|
schema: view.state.schema,
|
|
1514
|
+
formattingFromStyle: formattingFromStyleForInsertion(item),
|
|
1116
1515
|
mode,
|
|
1117
1516
|
author,
|
|
1118
1517
|
date,
|
|
1119
1518
|
initials,
|
|
1120
1519
|
commentMark,
|
|
1121
1520
|
suggestionId,
|
|
1122
|
-
revisionSeed,
|
|
1123
|
-
isPairedMove
|
|
1521
|
+
revisionSeed: operationRevisionSeed,
|
|
1522
|
+
isPairedMove,
|
|
1523
|
+
numbering
|
|
1124
1524
|
});
|
|
1125
|
-
|
|
1525
|
+
operationRevisionSeed = built.nextRevisionId;
|
|
1126
1526
|
if (built.revisionIds.length > 0) appliedRevisionIds = built.revisionIds;
|
|
1127
1527
|
tr = tr.insert(item.from, built.nodes);
|
|
1128
1528
|
break;
|
|
@@ -1148,7 +1548,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1148
1548
|
}
|
|
1149
1549
|
let node = signatureTable;
|
|
1150
1550
|
if (isSuggested && suggestionId !== null) {
|
|
1151
|
-
const revisionId =
|
|
1551
|
+
const revisionId = operationRevisionSeed++;
|
|
1152
1552
|
node = signatureTable.type.create({
|
|
1153
1553
|
...signatureTable.attrs,
|
|
1154
1554
|
_suggestedInsert: {
|
|
@@ -1191,7 +1591,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1191
1591
|
}
|
|
1192
1592
|
tr = result.transaction;
|
|
1193
1593
|
if (result.revisionId !== null) {
|
|
1194
|
-
|
|
1594
|
+
operationRevisionSeed++;
|
|
1195
1595
|
appliedRevisionIds = [result.revisionId];
|
|
1196
1596
|
}
|
|
1197
1597
|
break;
|
|
@@ -1221,7 +1621,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1221
1621
|
}
|
|
1222
1622
|
tr = result.transaction;
|
|
1223
1623
|
if (result.revisionId !== null) {
|
|
1224
|
-
|
|
1624
|
+
operationRevisionSeed++;
|
|
1225
1625
|
appliedRevisionIds = [result.revisionId];
|
|
1226
1626
|
}
|
|
1227
1627
|
const columnKey = getTableColumnCoordinateKey(insertion);
|
|
@@ -1254,7 +1654,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1254
1654
|
}
|
|
1255
1655
|
tr = result.transaction;
|
|
1256
1656
|
if (result.revisionId !== null) {
|
|
1257
|
-
|
|
1657
|
+
operationRevisionSeed++;
|
|
1258
1658
|
appliedRevisionIds = [result.revisionId];
|
|
1259
1659
|
}
|
|
1260
1660
|
break;
|
|
@@ -1270,7 +1670,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1270
1670
|
});
|
|
1271
1671
|
continue;
|
|
1272
1672
|
}
|
|
1273
|
-
const revisionId = mode === "tracked-changes" ?
|
|
1673
|
+
const revisionId = mode === "tracked-changes" ? operationRevisionSeed : null;
|
|
1274
1674
|
const nextTr = revisionId === null ? mergeTableRectangle({
|
|
1275
1675
|
tr,
|
|
1276
1676
|
tablePosition,
|
|
@@ -1294,7 +1694,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1294
1694
|
}
|
|
1295
1695
|
tr = nextTr;
|
|
1296
1696
|
if (revisionId !== null) {
|
|
1297
|
-
|
|
1697
|
+
operationRevisionSeed++;
|
|
1298
1698
|
appliedRevisionIds = [revisionId];
|
|
1299
1699
|
}
|
|
1300
1700
|
break;
|
|
@@ -1310,7 +1710,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1310
1710
|
});
|
|
1311
1711
|
continue;
|
|
1312
1712
|
}
|
|
1313
|
-
const revisionId = mode === "tracked-changes" ?
|
|
1713
|
+
const revisionId = mode === "tracked-changes" ? operationRevisionSeed : null;
|
|
1314
1714
|
const nextTr = revisionId === null ? splitTableRectangle({
|
|
1315
1715
|
tr,
|
|
1316
1716
|
tablePosition,
|
|
@@ -1334,7 +1734,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1334
1734
|
}
|
|
1335
1735
|
tr = nextTr;
|
|
1336
1736
|
if (revisionId !== null) {
|
|
1337
|
-
|
|
1737
|
+
operationRevisionSeed++;
|
|
1338
1738
|
appliedRevisionIds = [revisionId];
|
|
1339
1739
|
}
|
|
1340
1740
|
break;
|
|
@@ -1362,7 +1762,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1362
1762
|
}
|
|
1363
1763
|
tr = result.transaction;
|
|
1364
1764
|
if (result.revisionId !== null) {
|
|
1365
|
-
|
|
1765
|
+
operationRevisionSeed++;
|
|
1366
1766
|
appliedRevisionIds = [result.revisionId];
|
|
1367
1767
|
}
|
|
1368
1768
|
break;
|
|
@@ -1374,7 +1774,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1374
1774
|
break;
|
|
1375
1775
|
}
|
|
1376
1776
|
if (deletionType) {
|
|
1377
|
-
const revisionId =
|
|
1777
|
+
const revisionId = operationRevisionSeed++;
|
|
1378
1778
|
const deletionMark = deletionType.create({
|
|
1379
1779
|
revisionId,
|
|
1380
1780
|
author,
|
|
@@ -1389,7 +1789,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1389
1789
|
appliedRevisionIds = inlineRevisionApplied ? [revisionId] : [];
|
|
1390
1790
|
const markPosition = tr.mapping.map(item.blockFrom);
|
|
1391
1791
|
if (!paragraphEndsItsContainer(tr.doc.resolve(markPosition), item.blockNode.type.name) && tr.doc.nodeAt(markPosition)?.attrs["pPrMark"] == null) {
|
|
1392
|
-
const markRevisionId =
|
|
1792
|
+
const markRevisionId = operationRevisionSeed++;
|
|
1393
1793
|
tr = tr.setNodeAttribute(markPosition, "pPrMark", {
|
|
1394
1794
|
kind: isPairedMove(item.operation.moveId) ? "moveFrom" : "del",
|
|
1395
1795
|
info: {
|
|
@@ -1406,7 +1806,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1406
1806
|
break;
|
|
1407
1807
|
case "insertTable": {
|
|
1408
1808
|
const revision = {
|
|
1409
|
-
revisionId:
|
|
1809
|
+
revisionId: operationRevisionSeed,
|
|
1410
1810
|
author,
|
|
1411
1811
|
date,
|
|
1412
1812
|
...trackedRevisionExtras
|
|
@@ -1428,7 +1828,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1428
1828
|
});
|
|
1429
1829
|
continue;
|
|
1430
1830
|
}
|
|
1431
|
-
if (producesTrackedChanges) appliedRevisionIds = [
|
|
1831
|
+
if (producesTrackedChanges) appliedRevisionIds = [operationRevisionSeed++];
|
|
1432
1832
|
tr = tr.insert(item.from, table);
|
|
1433
1833
|
markStructuralChange(tr);
|
|
1434
1834
|
break;
|
|
@@ -1466,7 +1866,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1466
1866
|
continue;
|
|
1467
1867
|
}
|
|
1468
1868
|
const revision = {
|
|
1469
|
-
revisionId:
|
|
1869
|
+
revisionId: operationRevisionSeed++,
|
|
1470
1870
|
author,
|
|
1471
1871
|
date,
|
|
1472
1872
|
...trackedRevisionExtras
|
|
@@ -1500,7 +1900,22 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1500
1900
|
case "setBlockParagraphProperties": {
|
|
1501
1901
|
const blockPosition = tr.mapping.map(item.blockFrom);
|
|
1502
1902
|
const liveBlock = tr.doc.nodeAt(blockPosition) ?? item.blockNode;
|
|
1503
|
-
const
|
|
1903
|
+
const liveAttrs = expectParagraphAttrs(liveBlock);
|
|
1904
|
+
const resolvedFormattingFromStyle = item.operation.properties.styleId === void 0 ? resolveFormattingFromStyle({
|
|
1905
|
+
attrs: liveAttrs,
|
|
1906
|
+
styleId: liveAttrs.styleId,
|
|
1907
|
+
styleResolver
|
|
1908
|
+
}) : resolveFormattingFromStyle({
|
|
1909
|
+
attrs: liveAttrs,
|
|
1910
|
+
styleId: item.operation.properties.styleId,
|
|
1911
|
+
styleResolver
|
|
1912
|
+
});
|
|
1913
|
+
const patch = paragraphPropertiesPatch({
|
|
1914
|
+
node: liveBlock,
|
|
1915
|
+
properties: item.operation.properties,
|
|
1916
|
+
resolvedFormattingFromStyle,
|
|
1917
|
+
numbering
|
|
1918
|
+
});
|
|
1504
1919
|
if (patch === null) {
|
|
1505
1920
|
skipped.push({
|
|
1506
1921
|
id: item.operation.id,
|
|
@@ -1515,14 +1930,14 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1515
1930
|
});
|
|
1516
1931
|
break;
|
|
1517
1932
|
}
|
|
1518
|
-
const revisionId =
|
|
1933
|
+
const revisionId = operationRevisionSeed++;
|
|
1519
1934
|
const change = {
|
|
1520
1935
|
type: "paragraphPropertyChange",
|
|
1521
1936
|
info: {
|
|
1522
1937
|
id: revisionId,
|
|
1523
1938
|
author,
|
|
1524
1939
|
date,
|
|
1525
|
-
...
|
|
1940
|
+
...trackedRevisionExtras
|
|
1526
1941
|
},
|
|
1527
1942
|
previousFormatting: paragraphPropertiesSnapshot(liveBlock)
|
|
1528
1943
|
};
|
|
@@ -1541,7 +1956,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1541
1956
|
tr = tr.split(item.from);
|
|
1542
1957
|
break;
|
|
1543
1958
|
}
|
|
1544
|
-
const revisionIdMark =
|
|
1959
|
+
const revisionIdMark = operationRevisionSeed++;
|
|
1545
1960
|
const info = {
|
|
1546
1961
|
id: revisionIdMark,
|
|
1547
1962
|
author,
|
|
@@ -1550,7 +1965,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1550
1965
|
};
|
|
1551
1966
|
appliedRevisionIds = [revisionIdMark];
|
|
1552
1967
|
if (item.to > item.from && deletionType) {
|
|
1553
|
-
const revisionIdSeparator =
|
|
1968
|
+
const revisionIdSeparator = operationRevisionSeed++;
|
|
1554
1969
|
tr = tr.addMark(item.from, item.to, deletionType.create({
|
|
1555
1970
|
revisionId: revisionIdSeparator,
|
|
1556
1971
|
author,
|
|
@@ -1574,10 +1989,10 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1574
1989
|
tr = tr.join(item.blockTo + separator.length);
|
|
1575
1990
|
break;
|
|
1576
1991
|
}
|
|
1577
|
-
const revisionIdMark =
|
|
1992
|
+
const revisionIdMark = operationRevisionSeed++;
|
|
1578
1993
|
appliedRevisionIds = [revisionIdMark];
|
|
1579
1994
|
if (separator.length > 0 && insertionType) {
|
|
1580
|
-
const revisionIdSeparator =
|
|
1995
|
+
const revisionIdSeparator = operationRevisionSeed++;
|
|
1581
1996
|
tr = tr.insertText(separator, insertAt);
|
|
1582
1997
|
tr = tr.addMark(insertAt, insertAt + separator.length, insertionType.create({
|
|
1583
1998
|
revisionId: revisionIdSeparator,
|
|
@@ -1610,9 +2025,24 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1610
2025
|
});
|
|
1611
2026
|
continue;
|
|
1612
2027
|
}
|
|
2028
|
+
let committedCommentId;
|
|
2029
|
+
if (item.commentText !== void 0) {
|
|
2030
|
+
if (commentType === void 0 || provisionalCommentId === void 0 || commentMarkRanges(tr.doc, commentType, provisionalCommentId).length === 0) panic("A commented operation changed the document without producing a comment anchor", {
|
|
2031
|
+
operationId: item.operation.id,
|
|
2032
|
+
operationType: item.operation.type
|
|
2033
|
+
});
|
|
2034
|
+
committedCommentId = allocateCommittedCommentId(item.commentText);
|
|
2035
|
+
tr = commitProvisionalComment({
|
|
2036
|
+
tr,
|
|
2037
|
+
commentType,
|
|
2038
|
+
provisionalId: provisionalCommentId,
|
|
2039
|
+
committedId: committedCommentId
|
|
2040
|
+
});
|
|
2041
|
+
}
|
|
2042
|
+
revisionSeed = operationRevisionSeed;
|
|
1613
2043
|
applied.push({
|
|
1614
2044
|
id: item.operation.id,
|
|
1615
|
-
...
|
|
2045
|
+
...committedCommentId !== void 0 && { commentId: committedCommentId },
|
|
1616
2046
|
...appliedRevisionIds !== void 0 && appliedRevisionIds[0] !== void 0 && {
|
|
1617
2047
|
revisionId: appliedRevisionIds[0],
|
|
1618
2048
|
revisionIds: appliedRevisionIds
|
|
@@ -1633,6 +2063,21 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1633
2063
|
});
|
|
1634
2064
|
tr = rotated.transaction;
|
|
1635
2065
|
revisionSeed = rotated.nextRevisionId;
|
|
2066
|
+
const receiptIndexByRevisionId = /* @__PURE__ */ new Map();
|
|
2067
|
+
for (const [receiptIndex, receipt] of applied.entries()) for (const revisionId of receipt.revisionIds ?? []) receiptIndexByRevisionId.set(revisionId, receiptIndex);
|
|
2068
|
+
for (const { ownerRevisionId, revisionId } of rotated.synthesizedRevisions) {
|
|
2069
|
+
const receiptIndex = receiptIndexByRevisionId.get(ownerRevisionId);
|
|
2070
|
+
const receipt = receiptIndex === void 0 ? void 0 : applied.at(receiptIndex);
|
|
2071
|
+
if (receiptIndex === void 0 || !receipt?.revisionIds) panic("A synthesized final-mark revision lost its operation receipt", {
|
|
2072
|
+
ownerRevisionId,
|
|
2073
|
+
revisionId
|
|
2074
|
+
});
|
|
2075
|
+
applied[receiptIndex] = {
|
|
2076
|
+
...receipt,
|
|
2077
|
+
revisionIds: [...receipt.revisionIds, revisionId]
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
if (ownsSharedRevisionIdCursor) revisionIdCursor = Math.max(revisionIdCursor, revisionSeed);
|
|
1636
2081
|
if (revisionStamp) requestDeterministicParaIds(tr, `${revisionStamp.date}:${String(revisionStamp.idSeed)}`);
|
|
1637
2082
|
view.dispatch(tr);
|
|
1638
2083
|
}
|
|
@@ -1657,7 +2102,8 @@ const previewFolioAIEditOperations = (options) => {
|
|
|
1657
2102
|
...applyOptions,
|
|
1658
2103
|
view: previewView,
|
|
1659
2104
|
...createCommentId !== void 0 && { createCommentId: () => previewCommentId-- },
|
|
1660
|
-
revisionIdSeed: -1e9
|
|
2105
|
+
revisionIdSeed: -1e9,
|
|
2106
|
+
wordDiffMode: "coarse"
|
|
1661
2107
|
});
|
|
1662
2108
|
return {
|
|
1663
2109
|
applied: result.applied.map(({ id }) => ({ id })),
|
|
@@ -1666,7 +2112,7 @@ const previewFolioAIEditOperations = (options) => {
|
|
|
1666
2112
|
nextRevisionId: options.revisionStamp?.idSeed ?? revisionIdCursor
|
|
1667
2113
|
};
|
|
1668
2114
|
};
|
|
1669
|
-
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials,
|
|
2115
|
+
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials, diffText }) => {
|
|
1670
2116
|
let nextTr = tr;
|
|
1671
2117
|
const replacement = stripInlineEmphasisMarkers((() => {
|
|
1672
2118
|
if (item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") return item.operation.replace;
|
|
@@ -1720,7 +2166,7 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
1720
2166
|
}
|
|
1721
2167
|
}
|
|
1722
2168
|
if (sourceText !== null && cleanBlock !== null) {
|
|
1723
|
-
const segments =
|
|
2169
|
+
const segments = diffText(sourceText, replacement);
|
|
1724
2170
|
const offsets = cleanBlock.offsets;
|
|
1725
2171
|
const offsetAt = (cleanOffset) => offsets[cleanOffset] ?? null;
|
|
1726
2172
|
const steps = [];
|
|
@@ -1813,7 +2259,8 @@ const resolveStableBlock = ({ snapshot, blockId, liveBlocks, liveBlocksByParaId
|
|
|
1813
2259
|
const cleanBlock = buildCleanBlockText(live.node, live.from);
|
|
1814
2260
|
const currentText = cleanBlock.text;
|
|
1815
2261
|
const currentTextHash = hashFolioAIBlockText(normalizeFolioAIBlockText(currentText));
|
|
1816
|
-
|
|
2262
|
+
const structuralBoundaryHash = hashFolioAIBlockStructuralBoundaries(cleanBlock);
|
|
2263
|
+
if (currentTextHash !== anchor.textHash || structuralBoundaryHash !== anchor.structuralBoundaryHash) return {
|
|
1817
2264
|
type: "skip",
|
|
1818
2265
|
reason: "changedBlock"
|
|
1819
2266
|
};
|
|
@@ -1843,9 +2290,7 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
1843
2290
|
};
|
|
1844
2291
|
if (operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange") {
|
|
1845
2292
|
const { startOffset, endOffset, selectedTextHash } = operation.range;
|
|
1846
|
-
|
|
1847
|
-
const to = cleanBlock.offsets[endOffset];
|
|
1848
|
-
if (from === void 0 || to === void 0) return {
|
|
2293
|
+
if (cleanBlock.offsets[startOffset] === void 0 || cleanBlock.offsets[endOffset] === void 0) return {
|
|
1849
2294
|
type: "skip",
|
|
1850
2295
|
reason: "staleRange"
|
|
1851
2296
|
};
|
|
@@ -1854,6 +2299,15 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
1854
2299
|
type: "skip",
|
|
1855
2300
|
reason: "staleRange"
|
|
1856
2301
|
};
|
|
2302
|
+
const range = resolveCleanTextRange({
|
|
2303
|
+
cleanBlock,
|
|
2304
|
+
startOffset,
|
|
2305
|
+
endOffset
|
|
2306
|
+
});
|
|
2307
|
+
if (range === null) return {
|
|
2308
|
+
type: "skip",
|
|
2309
|
+
reason: "unsupportedBlock"
|
|
2310
|
+
};
|
|
1857
2311
|
if (operation.type === "replaceRange" && selectedText === operation.replace) return {
|
|
1858
2312
|
type: "skip",
|
|
1859
2313
|
reason: "noopOperation"
|
|
@@ -1862,8 +2316,8 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
1862
2316
|
type: "resolved",
|
|
1863
2317
|
operation: {
|
|
1864
2318
|
operation,
|
|
1865
|
-
from,
|
|
1866
|
-
to,
|
|
2319
|
+
from: range.from,
|
|
2320
|
+
to: range.to,
|
|
1867
2321
|
blockFrom,
|
|
1868
2322
|
blockTo,
|
|
1869
2323
|
blockNode
|
|
@@ -2166,8 +2620,10 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2166
2620
|
};
|
|
2167
2621
|
if (operation.type === "splitBlock") {
|
|
2168
2622
|
const separator = operation.separator ?? "";
|
|
2169
|
-
const
|
|
2170
|
-
const
|
|
2623
|
+
const startOffset = operation.offset;
|
|
2624
|
+
const endOffset = operation.offset + separator.length;
|
|
2625
|
+
const at = cleanBlock.offsets[startOffset];
|
|
2626
|
+
const after = cleanBlock.offsets[endOffset];
|
|
2171
2627
|
if (at === void 0 || after === void 0 || operation.offset === 0 || operation.offset + separator.length >= currentText.length) return {
|
|
2172
2628
|
type: "skip",
|
|
2173
2629
|
reason: "staleRange"
|
|
@@ -2176,7 +2632,12 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2176
2632
|
type: "skip",
|
|
2177
2633
|
reason: "staleRange"
|
|
2178
2634
|
};
|
|
2179
|
-
|
|
2635
|
+
const range = resolveCleanTextRange({
|
|
2636
|
+
cleanBlock,
|
|
2637
|
+
startOffset,
|
|
2638
|
+
endOffset
|
|
2639
|
+
});
|
|
2640
|
+
if (range === null || !canSplit(doc, range.from)) return {
|
|
2180
2641
|
type: "skip",
|
|
2181
2642
|
reason: "unsupportedBlock"
|
|
2182
2643
|
};
|
|
@@ -2184,8 +2645,8 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2184
2645
|
type: "resolved",
|
|
2185
2646
|
operation: {
|
|
2186
2647
|
operation,
|
|
2187
|
-
from:
|
|
2188
|
-
to:
|
|
2648
|
+
from: range.from,
|
|
2649
|
+
to: range.to,
|
|
2189
2650
|
blockFrom,
|
|
2190
2651
|
blockTo,
|
|
2191
2652
|
blockNode
|
|
@@ -2210,6 +2671,12 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2210
2671
|
};
|
|
2211
2672
|
}
|
|
2212
2673
|
if (operation.type === "deleteBlock" || operation.type === "replaceBlock") {
|
|
2674
|
+
const replaceChangesText = operation.type === "replaceBlock" && operation.text !== currentText;
|
|
2675
|
+
const replaceChangesStyle = operation.type === "replaceBlock" && operation.styleId !== void 0 && operation.styleId !== (expectParagraphAttrs(blockNode).styleId ?? null);
|
|
2676
|
+
if (replaceChangesText && cleanBlock.structuralBoundaries.length > 0) return {
|
|
2677
|
+
type: "skip",
|
|
2678
|
+
reason: "unsupportedBlock"
|
|
2679
|
+
};
|
|
2213
2680
|
const range = getTextRangeFromCleanBlock(cleanBlock);
|
|
2214
2681
|
if (!range) {
|
|
2215
2682
|
const insertionPoint = cleanBlock.offsets.at(0);
|
|
@@ -2217,10 +2684,25 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2217
2684
|
type: "skip",
|
|
2218
2685
|
reason: "unsupportedBlock"
|
|
2219
2686
|
};
|
|
2220
|
-
if (!(operation.type === "deleteBlock" ? true : currentText.length === 0 && operation.text.length > 0)) return {
|
|
2687
|
+
if (!(operation.type === "deleteBlock" ? true : currentText.length === 0 && (operation.text.length > 0 || replaceChangesStyle))) return {
|
|
2221
2688
|
type: "skip",
|
|
2222
2689
|
reason: "unsupportedBlock"
|
|
2223
2690
|
};
|
|
2691
|
+
if (operation.type === "replaceBlock") return {
|
|
2692
|
+
type: "resolved",
|
|
2693
|
+
operation: {
|
|
2694
|
+
operation,
|
|
2695
|
+
from: insertionPoint,
|
|
2696
|
+
to: insertionPoint,
|
|
2697
|
+
blockFrom,
|
|
2698
|
+
blockTo,
|
|
2699
|
+
blockNode,
|
|
2700
|
+
replaceBlockImpact: resolveReplaceBlockImpact({
|
|
2701
|
+
changesText: replaceChangesText,
|
|
2702
|
+
changesStyle: replaceChangesStyle
|
|
2703
|
+
})
|
|
2704
|
+
}
|
|
2705
|
+
};
|
|
2224
2706
|
return {
|
|
2225
2707
|
type: "resolved",
|
|
2226
2708
|
operation: {
|
|
@@ -2233,9 +2715,26 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
2233
2715
|
}
|
|
2234
2716
|
};
|
|
2235
2717
|
}
|
|
2236
|
-
if (operation.type === "replaceBlock" && operation.text === currentText)
|
|
2237
|
-
|
|
2238
|
-
|
|
2718
|
+
if (operation.type === "replaceBlock" && operation.text === currentText) {
|
|
2719
|
+
if (!replaceChangesStyle) return {
|
|
2720
|
+
type: "skip",
|
|
2721
|
+
reason: "noopOperation"
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
if (operation.type === "replaceBlock") return {
|
|
2725
|
+
type: "resolved",
|
|
2726
|
+
operation: {
|
|
2727
|
+
operation,
|
|
2728
|
+
from: range.from,
|
|
2729
|
+
to: range.to,
|
|
2730
|
+
blockFrom,
|
|
2731
|
+
blockTo,
|
|
2732
|
+
blockNode,
|
|
2733
|
+
replaceBlockImpact: resolveReplaceBlockImpact({
|
|
2734
|
+
changesText: replaceChangesText,
|
|
2735
|
+
changesStyle: replaceChangesStyle
|
|
2736
|
+
})
|
|
2737
|
+
}
|
|
2239
2738
|
};
|
|
2240
2739
|
return {
|
|
2241
2740
|
type: "resolved",
|
|
@@ -2273,7 +2772,7 @@ const resolveTextInCleanBlock = (cleanBlock, find) => {
|
|
|
2273
2772
|
type: "skip",
|
|
2274
2773
|
reason: "emptyOperation"
|
|
2275
2774
|
};
|
|
2276
|
-
const { text
|
|
2775
|
+
const { text } = cleanBlock;
|
|
2277
2776
|
const firstIndex = text.indexOf(find);
|
|
2278
2777
|
if (firstIndex === -1) return {
|
|
2279
2778
|
type: "skip",
|
|
@@ -2283,16 +2782,18 @@ const resolveTextInCleanBlock = (cleanBlock, find) => {
|
|
|
2283
2782
|
type: "skip",
|
|
2284
2783
|
reason: "ambiguousFind"
|
|
2285
2784
|
};
|
|
2286
|
-
const
|
|
2287
|
-
|
|
2288
|
-
|
|
2785
|
+
const range = resolveCleanTextRange({
|
|
2786
|
+
cleanBlock,
|
|
2787
|
+
startOffset: firstIndex,
|
|
2788
|
+
endOffset: firstIndex + find.length
|
|
2789
|
+
});
|
|
2790
|
+
if (range === null) return {
|
|
2289
2791
|
type: "skip",
|
|
2290
2792
|
reason: "unsupportedBlock"
|
|
2291
2793
|
};
|
|
2292
2794
|
return {
|
|
2293
2795
|
type: "resolved",
|
|
2294
|
-
|
|
2295
|
-
to
|
|
2796
|
+
...range
|
|
2296
2797
|
};
|
|
2297
2798
|
};
|
|
2298
2799
|
const getTextRangeFromCleanBlock = (cleanBlock) => {
|