@stll/folio-core 0.47.5 → 0.47.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/word-diff.d.ts +25 -6
- package/dist/ai-edits/word-diff.js +210 -38
- package/dist/controller/headerFooterEditorManager.d.ts +2 -1
- package/dist/controller/headerFooterEditorManager.js +2 -1
- package/dist/controller/noteEditorManager.d.ts +2 -1
- package/dist/controller/noteEditorManager.js +2 -1
- package/dist/docx/relsParser.js +9 -1
- package/dist/docx/selectiveSave.js +11 -2
- package/dist/prosemirror/attrs/index.d.ts +3 -2
- package/dist/prosemirror/attrs/index.js +18 -1
- package/dist/prosemirror/commands/comments.js +61 -3
- package/dist/prosemirror/conversion/fromProseDoc.js +107 -37
- package/dist/prosemirror/conversion/toProseDoc.js +33 -13
- package/dist/prosemirror/extensions/markRegistry.d.ts +5 -8
- package/dist/prosemirror/extensions/markRegistry.js +5 -8
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +2 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +32 -7
- package/dist/prosemirror/runIdentity.d.ts +5 -5
- package/dist/prosemirror/runIdentity.js +5 -5
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +17 -2
- package/dist/prosemirror/utils/extractTrackedChanges.js +21 -6
- package/package.json +1 -1
|
@@ -82,6 +82,12 @@ const NOTE_TYPES = ["footnote", "endnote"];
|
|
|
82
82
|
const NOTE_REF_VERT_ALIGNS = ["baseline", "superscript"];
|
|
83
83
|
const TEXT_BOX_DOCX_PLACEMENTS = ["standalone", "inlineWithPrevious"];
|
|
84
84
|
const TRACKED_CHANGE_MOVE_KINDS = ["moveTo", "moveFrom"];
|
|
85
|
+
const TRACKED_REVISION_ANCESTOR_TYPES = [
|
|
86
|
+
"insertion",
|
|
87
|
+
"deletion",
|
|
88
|
+
"moveFrom",
|
|
89
|
+
"moveTo"
|
|
90
|
+
];
|
|
85
91
|
const TEXT_BOX_TRACKED_CHANGE_TYPES = [
|
|
86
92
|
"insertion",
|
|
87
93
|
"deletion",
|
|
@@ -792,6 +798,7 @@ const readCommentMarkAttrs = (mark) => {
|
|
|
792
798
|
return attrsResult(attrs, issues);
|
|
793
799
|
};
|
|
794
800
|
const expectCommentMarkAttrs = (mark) => expectCachedMarkAttrs(mark, commentAttrsCache, readCommentMarkAttrs, "comment attrs");
|
|
801
|
+
const isTrackedRevisionAncestorArray = (value) => Array.isArray(value) && value.length > 0 && value.every((ancestor) => isRecord(ancestor) && TRACKED_REVISION_ANCESTOR_TYPES.some((type) => type === ancestor["type"]) && typeof ancestor["revisionId"] === "number" && Number.isSafeInteger(ancestor["revisionId"]) && ancestor["revisionId"] >= 0 && typeof ancestor["author"] === "string" && (ancestor["date"] === void 0 || typeof ancestor["date"] === "string") && (ancestor["utcDate"] === void 0 || typeof ancestor["utcDate"] === "string") && (ancestor["initials"] === void 0 || typeof ancestor["initials"] === "string") && typeof ancestor["outerWrapperCount"] === "number" && Number.isSafeInteger(ancestor["outerWrapperCount"]) && ancestor["outerWrapperCount"] >= 0);
|
|
795
802
|
const readTrackedChangeMarkAttrs = (mark) => {
|
|
796
803
|
const attrs = attrsRecord(mark.attrs);
|
|
797
804
|
const issues = [];
|
|
@@ -801,6 +808,16 @@ const readTrackedChangeMarkAttrs = (mark) => {
|
|
|
801
808
|
optionalString(attrs, "date", `${mark.type.name}.attrs.date`, issues);
|
|
802
809
|
optionalString(attrs, "utcDate", `${mark.type.name}.attrs.utcDate`, issues);
|
|
803
810
|
optionalString(attrs, "initials", `${mark.type.name}.attrs.initials`, issues);
|
|
811
|
+
const outerWrapperCount = attrs["_docxOuterWrapperCount"];
|
|
812
|
+
if (outerWrapperCount !== void 0 && outerWrapperCount !== null && (typeof outerWrapperCount !== "number" || !Number.isSafeInteger(outerWrapperCount) || outerWrapperCount < 0)) issues.push({
|
|
813
|
+
path: `${mark.type.name}.attrs._docxOuterWrapperCount`,
|
|
814
|
+
message: "Expected a non-negative safe integer."
|
|
815
|
+
});
|
|
816
|
+
const ancestors = attrs["_docxRevisionAncestors"];
|
|
817
|
+
if (ancestors !== void 0 && ancestors !== null && !isTrackedRevisionAncestorArray(ancestors)) issues.push({
|
|
818
|
+
path: `${mark.type.name}.attrs._docxRevisionAncestors`,
|
|
819
|
+
message: "Expected a non-empty array of valid revision ancestors."
|
|
820
|
+
});
|
|
804
821
|
optionalOneOf(attrs, "moveKind", `${mark.type.name}.attrs.moveKind`, issues, TRACKED_CHANGE_MOVE_KINDS);
|
|
805
822
|
optionalOneOf(attrs, "provenance", `${mark.type.name}.attrs.provenance`, issues, TRACKED_CHANGE_PROVENANCE_VALUES);
|
|
806
823
|
optionalString(attrs, "suggestionId", `${mark.type.name}.attrs.suggestionId`, issues);
|
|
@@ -2700,4 +2717,4 @@ const optionalEmptyHyperlinkArray = (value, issues) => {
|
|
|
2700
2717
|
}
|
|
2701
2718
|
};
|
|
2702
2719
|
//#endregion
|
|
2703
|
-
export { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectInlineWrapperMarkAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectPageBreakRunAttrs, expectParagraphAttrs, expectPreservedBlockAttrs, expectPreservedXmlAttrs, expectRunFormattingOverrideMarkAttrs, expectRunIdentityMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs, mergeImageAttrs, mergeParagraphAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs, readBlockSdtAttrs, readCharacterSpacingMarkAttrs, readCharacterStyleMarkAttrs, readCommentMarkAttrs, readEmphasisMarkAttrs, readFieldAttrs, readFontFamilyMarkAttrs, readFontSizeMarkAttrs, readFootnoteRefMarkAttrs, readHardBreakAttrs, readHighlightMarkAttrs, readHyperlinkMarkAttrs, readImageAttrs, readInlineWrapperMarkAttrs, readInlineWrapperStack, readLanguageMarkAttrs, readMathAttrs, readPageBreakRunAttrs, readParagraphAttrs, readPreservedBlockAttrs, readPreservedXmlAttrs, readRunFormattingOverrideMarkAttrs, readRunIdentityMarkAttrs, readRunPropertyChangeMarkAttrs, readRunShadingMarkAttrs, readSdtAttrs, readShapeAttrs, readStrikeMarkAttrs, readSymbolAttrs, readTabAttrs, readTableAttrs, readTableCellAttrs, readTableRowAttrs, readTextBoxAttrs, readTextColorMarkAttrs, readTextEffectMarkAttrs, readTrackedChangeMarkAttrs, readUnderlineMarkAttrs };
|
|
2720
|
+
export { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectInlineWrapperMarkAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectPageBreakRunAttrs, expectParagraphAttrs, expectPreservedBlockAttrs, expectPreservedXmlAttrs, expectRunFormattingOverrideMarkAttrs, expectRunIdentityMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs, isTrackedRevisionAncestorArray, mergeImageAttrs, mergeParagraphAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs, readBlockSdtAttrs, readCharacterSpacingMarkAttrs, readCharacterStyleMarkAttrs, readCommentMarkAttrs, readEmphasisMarkAttrs, readFieldAttrs, readFontFamilyMarkAttrs, readFontSizeMarkAttrs, readFootnoteRefMarkAttrs, readHardBreakAttrs, readHighlightMarkAttrs, readHyperlinkMarkAttrs, readImageAttrs, readInlineWrapperMarkAttrs, readInlineWrapperStack, readLanguageMarkAttrs, readMathAttrs, readPageBreakRunAttrs, readParagraphAttrs, readPreservedBlockAttrs, readPreservedXmlAttrs, readRunFormattingOverrideMarkAttrs, readRunIdentityMarkAttrs, readRunPropertyChangeMarkAttrs, readRunShadingMarkAttrs, readSdtAttrs, readShapeAttrs, readStrikeMarkAttrs, readSymbolAttrs, readTabAttrs, readTableAttrs, readTableCellAttrs, readTableRowAttrs, readTextBoxAttrs, readTextColorMarkAttrs, readTextEffectMarkAttrs, readTrackedChangeMarkAttrs, readUnderlineMarkAttrs };
|
|
@@ -2,7 +2,7 @@ import { sameStatedParagraphNumbering as sameStatedParagraphNumbering$1 } from "
|
|
|
2
2
|
import { joinProseParagraphsWithRightPropertySource } from "../../docx/paragraphPropertySource.js";
|
|
3
3
|
import { appendHeadlessInlineResolution } from "../../internal/headlessRevisionResolution.js";
|
|
4
4
|
import { stateAllowsHeadlessRevisionResolution } from "../../internal/headlessRevisionResolutionGuard.js";
|
|
5
|
-
import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs } from "../attrs/index.js";
|
|
5
|
+
import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
|
|
6
6
|
import { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer } from "../containerFinalParagraph.js";
|
|
7
7
|
import { resolveParagraphDefaultTextFormatting } from "../conversion/toProseDoc.js";
|
|
8
8
|
import { markChangedParagraphRanges, markStructuralChange, markTrackedSectionEndpointRemoval } from "../extensions/features/ParagraphChangeTrackerExtension.js";
|
|
@@ -19,6 +19,7 @@ import { reconcileTableGridAfterColumnRemoval } from "../tableGridMutation.js";
|
|
|
19
19
|
import { holdsNoContent } from "../zeroWidthAnchors.js";
|
|
20
20
|
import { hasSerializableParagraphPropertyChange, paragraphPropertiesSnapshot, paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, removeParagraphPropertyChanges, sectionRejectProperties, tableCellRejectAttrPatch, tablePropertyExceptionsRejectAttrPatch, tableRejectAttrPatch, tableRowRejectAttrPatch } from "./propertyChangeScope.js";
|
|
21
21
|
import { hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge } from "./tableCellMergeResolution.js";
|
|
22
|
+
import { panic } from "better-result";
|
|
22
23
|
import { PARAGRAPH_MARK_CHANGE_KINDS, REVIEW_CARRIERS } from "@stll/docx-core/model";
|
|
23
24
|
import { TableMap, removeRow } from "prosemirror-tables";
|
|
24
25
|
import { Mapping } from "prosemirror-transform";
|
|
@@ -55,6 +56,33 @@ function removeCommentMark(commentId) {
|
|
|
55
56
|
return true;
|
|
56
57
|
};
|
|
57
58
|
}
|
|
59
|
+
const revisionLayerOf = (mark) => {
|
|
60
|
+
const attrs = expectTrackedChangeMarkAttrs(mark);
|
|
61
|
+
let type;
|
|
62
|
+
if (mark.type.name === "insertion") type = attrs.moveKind === "moveTo" ? "moveTo" : "insertion";
|
|
63
|
+
else type = attrs.moveKind === "moveFrom" ? "moveFrom" : "deletion";
|
|
64
|
+
return {
|
|
65
|
+
type,
|
|
66
|
+
revisionId: attrs.revisionId,
|
|
67
|
+
author: attrs.author,
|
|
68
|
+
...attrs.date ? { date: attrs.date } : {},
|
|
69
|
+
...attrs.utcDate ? { utcDate: attrs.utcDate } : {},
|
|
70
|
+
...attrs.initials ? { initials: attrs.initials } : {},
|
|
71
|
+
outerWrapperCount: attrs._docxOuterWrapperCount ?? 0
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const revisionLayerRemovesContent = (layer, mode) => mode === "accept" ? layer.type === "deletion" || layer.type === "moveFrom" : layer.type === "insertion" || layer.type === "moveTo";
|
|
75
|
+
const containsNestedRevision = (doc) => {
|
|
76
|
+
let found = false;
|
|
77
|
+
doc.descendants((node) => {
|
|
78
|
+
if (found) return false;
|
|
79
|
+
for (const mark of node.marks) if ((mark.type.name === "insertion" || mark.type.name === "deletion") && (expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors?.length ?? 0) > 0) {
|
|
80
|
+
found = true;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return found;
|
|
85
|
+
};
|
|
58
86
|
/**
|
|
59
87
|
* Resolve a tracked change: accept or reject.
|
|
60
88
|
* - Accept: keep insertions (remove mark), delete deletions (remove text)
|
|
@@ -76,7 +104,7 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
|
|
|
76
104
|
const removeType = mode === "accept" ? deletionType : insertionType;
|
|
77
105
|
const revisionSet = revisionIds === void 0 ? null : new Set(revisionIds);
|
|
78
106
|
const removeKeptMarksInBulk = revisionSet === null && keepType !== void 0;
|
|
79
|
-
const canBulkInlineResolution = execution === "headless-bulk-inline" && revisionSet === null && from === 0 && to === state.doc.content.size && stateAllowsHeadlessRevisionResolution(state);
|
|
107
|
+
const canBulkInlineResolution = execution === "headless-bulk-inline" && revisionSet === null && from === 0 && to === state.doc.content.size && stateAllowsHeadlessRevisionResolution(state) && !containsNestedRevision(state.doc);
|
|
80
108
|
const matchesRevision = (mark) => revisionSet === null || typeof mark.attrs["revisionId"] === "number" && revisionSet.has(mark.attrs["revisionId"]);
|
|
81
109
|
if (dispatch) {
|
|
82
110
|
const tr = state.tr;
|
|
@@ -189,6 +217,36 @@ function resolveChange(from, to, mode, revisionIds, execution = "legacy") {
|
|
|
189
217
|
revisionSet,
|
|
190
218
|
styleResolver
|
|
191
219
|
});
|
|
220
|
+
const nestedMark = node.marks.find((mark) => (mark.type === insertionType || mark.type === deletionType) && (expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors?.length ?? 0) > 0);
|
|
221
|
+
if (nestedMark) {
|
|
222
|
+
const path = [...expectTrackedChangeMarkAttrs(nestedMark)._docxRevisionAncestors ?? [], revisionLayerOf(nestedMark)];
|
|
223
|
+
const selected = (layer) => revisionSet === null || revisionSet.has(layer.revisionId);
|
|
224
|
+
if (path.some(selected)) if (path.some((layer) => selected(layer) && revisionLayerRemovesContent(layer, mode))) deleteRanges.push({
|
|
225
|
+
from: rangeFrom,
|
|
226
|
+
to: rangeTo
|
|
227
|
+
});
|
|
228
|
+
else {
|
|
229
|
+
tr.removeMark(rangeFrom, rangeTo, nestedMark);
|
|
230
|
+
const remaining = path.filter((layer) => !selected(layer));
|
|
231
|
+
const active = remaining.at(-1);
|
|
232
|
+
if (active) {
|
|
233
|
+
const type = active.type === "insertion" || active.type === "moveTo" ? insertionType : deletionType;
|
|
234
|
+
if (!type) panic("A nested tracked revision has no editor mark type");
|
|
235
|
+
tr.addMark(rangeFrom, rangeTo, type.create({
|
|
236
|
+
revisionId: active.revisionId,
|
|
237
|
+
author: active.author,
|
|
238
|
+
date: active.date ?? null,
|
|
239
|
+
utcDate: active.utcDate ?? null,
|
|
240
|
+
initials: active.initials ?? null,
|
|
241
|
+
moveKind: active.type === "moveTo" || active.type === "moveFrom" ? active.type : null,
|
|
242
|
+
_historicalFormatting: active.type === "deletion" || active.type === "moveFrom" ? true : null,
|
|
243
|
+
_docxOuterWrapperCount: active.outerWrapperCount,
|
|
244
|
+
_docxRevisionAncestors: remaining.length > 1 ? remaining.slice(0, -1) : null
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
192
250
|
if (removesNode) deleteRanges.push({
|
|
193
251
|
from: rangeFrom,
|
|
194
252
|
to: rangeTo
|
|
@@ -794,7 +852,7 @@ function findAIEditRevisionRange(state, revisionIds) {
|
|
|
794
852
|
includeRange(pos, pos + node.nodeSize);
|
|
795
853
|
break;
|
|
796
854
|
}
|
|
797
|
-
if ((mark.type === insertionType || mark.type === deletionType) &&
|
|
855
|
+
if ((mark.type === insertionType || mark.type === deletionType) && (idSet.has(expectTrackedChangeMarkAttrs(mark).revisionId) || (expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors ?? []).some((ancestor) => idSet.has(ancestor.revisionId)))) {
|
|
798
856
|
includeRange(pos, pos + node.nodeSize);
|
|
799
857
|
break;
|
|
800
858
|
}
|
|
@@ -1162,12 +1162,10 @@ const nestInlineWrappers = (stack, content) => {
|
|
|
1162
1162
|
* The emitted sequence with each wrapper group nested back into the wrappers
|
|
1163
1163
|
* `toProseDoc` lifted off it.
|
|
1164
1164
|
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
* per wrapper. So a revision in the group keeps its place and takes the nest
|
|
1170
|
-
* inside it, and the items around it share a nest of their own.
|
|
1165
|
+
* A revision may be inside some wrapper layers and outside others. Its mark
|
|
1166
|
+
* records how many layers preceded it in the source, since a ProseMirror mark
|
|
1167
|
+
* set cannot express that order. Adjacent items with the same outer layers
|
|
1168
|
+
* share one wrapper rather than splitting it around each revision.
|
|
1171
1169
|
*
|
|
1172
1170
|
* A group with nothing in it writes no wrapper: an authored wrapper whose
|
|
1173
1171
|
* content was deleted or rejected is gone, and an empty one says nothing.
|
|
@@ -1185,10 +1183,83 @@ const nestInsideHyperlink = (item, stack, hyperlinkIndex, indices) => {
|
|
|
1185
1183
|
}
|
|
1186
1184
|
return false;
|
|
1187
1185
|
};
|
|
1188
|
-
const
|
|
1186
|
+
const nestRevisionAwareInlineWrappers = (items, stack, revisionOuterWrapperCounts, layerIndex = 0) => {
|
|
1187
|
+
if (layerIndex === stack.length) return [...items];
|
|
1188
|
+
const nested = [];
|
|
1189
|
+
let pending = [];
|
|
1190
|
+
const closeLayer = () => {
|
|
1191
|
+
if (pending.length === 0) return;
|
|
1192
|
+
const layer = stack[layerIndex];
|
|
1193
|
+
if (layer === void 0) panic("An inline wrapper layer disappeared during reconstruction");
|
|
1194
|
+
nested.push(inlineWrapperMember(layer, nestRevisionAwareInlineWrappers(pending, stack, revisionOuterWrapperCounts, layerIndex + 1)));
|
|
1195
|
+
pending = [];
|
|
1196
|
+
};
|
|
1197
|
+
for (const item of items) {
|
|
1198
|
+
if (!isRevisionWrapper(item) || (revisionOuterWrapperCounts.get(item) ?? 0) > layerIndex) {
|
|
1199
|
+
pending.push(item);
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
closeLayer();
|
|
1203
|
+
item.content = [nestInlineWrappers(stack.slice(layerIndex), item.content)];
|
|
1204
|
+
nested.push(item);
|
|
1205
|
+
}
|
|
1206
|
+
closeLayer();
|
|
1207
|
+
return nested;
|
|
1208
|
+
};
|
|
1209
|
+
const sameRevisionLayer = (left, right) => left.type === right.type && left.revisionId === right.revisionId && left.author === right.author && left.date === right.date && left.utcDate === right.utcDate && left.initials === right.initials && left.outerWrapperCount === right.outerWrapperCount;
|
|
1210
|
+
const revisionInfoFromLayer = (layer) => ({
|
|
1211
|
+
id: layer.revisionId,
|
|
1212
|
+
author: layer.author,
|
|
1213
|
+
...layer.date ? { date: layer.date } : {},
|
|
1214
|
+
...layer.utcDate ? { utcDate: {
|
|
1215
|
+
attribute: DATE_UTC_ATTRIBUTE,
|
|
1216
|
+
value: layer.utcDate
|
|
1217
|
+
} } : {},
|
|
1218
|
+
...layer.initials ? { initials: layer.initials } : {}
|
|
1219
|
+
});
|
|
1220
|
+
const nestRevisionAncestors = (items, ancestorsByWrapper, revisionOuterWrapperCounts) => {
|
|
1221
|
+
const nested = [];
|
|
1222
|
+
const open = [];
|
|
1223
|
+
for (const item of items) {
|
|
1224
|
+
if (!isRevisionWrapper(item)) {
|
|
1225
|
+
open.length = 0;
|
|
1226
|
+
nested.push(item);
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
const ownLayer = {
|
|
1230
|
+
type: item.type,
|
|
1231
|
+
revisionId: item.info.id,
|
|
1232
|
+
author: item.info.author,
|
|
1233
|
+
...item.info.date ? { date: item.info.date } : {},
|
|
1234
|
+
...item.info.utcDate ? { utcDate: item.info.utcDate.value } : {},
|
|
1235
|
+
...item.info.initials ? { initials: item.info.initials } : {},
|
|
1236
|
+
outerWrapperCount: revisionOuterWrapperCounts.get(item) ?? 0
|
|
1237
|
+
};
|
|
1238
|
+
const path = [...ancestorsByWrapper.get(item) ?? [], ownLayer];
|
|
1239
|
+
let shared = 0;
|
|
1240
|
+
while (shared < open.length && path[shared] && sameRevisionLayer(open[shared].layer, path[shared])) shared += 1;
|
|
1241
|
+
open.length = shared;
|
|
1242
|
+
for (const layer of path.slice(shared)) {
|
|
1243
|
+
const wrapper = createTrackedRunWrapper(layer.type, revisionInfoFromLayer(layer));
|
|
1244
|
+
revisionOuterWrapperCounts.set(wrapper, layer.outerWrapperCount);
|
|
1245
|
+
const parent = open.at(-1)?.wrapper;
|
|
1246
|
+
if (parent) parent.content.push(wrapper);
|
|
1247
|
+
else nested.push(wrapper);
|
|
1248
|
+
open.push({
|
|
1249
|
+
layer,
|
|
1250
|
+
wrapper
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
const owner = open.at(-1)?.wrapper;
|
|
1254
|
+
if (!owner) panic("A tracked revision path has no owner");
|
|
1255
|
+
owner.content.push(...item.content);
|
|
1256
|
+
}
|
|
1257
|
+
return nested;
|
|
1258
|
+
};
|
|
1259
|
+
const nestInlineWrapperGroups = (items, groups, hyperlinkIndices, revisionOuterWrapperCounts, ancestorsByWrapper) => {
|
|
1189
1260
|
const nested = [];
|
|
1190
1261
|
for (const [index, group] of groups.entries()) {
|
|
1191
|
-
const slice = items.slice(group.start, groups[index + 1]?.start ?? items.length);
|
|
1262
|
+
const slice = nestRevisionAncestors(items.slice(group.start, groups[index + 1]?.start ?? items.length), ancestorsByWrapper, revisionOuterWrapperCounts);
|
|
1192
1263
|
if (group.stack.length === 0) {
|
|
1193
1264
|
nested.push(...slice);
|
|
1194
1265
|
continue;
|
|
@@ -1205,24 +1276,18 @@ const nestInlineWrapperGroups = (items, groups, hyperlinkIndices) => {
|
|
|
1205
1276
|
nested.push(...slice);
|
|
1206
1277
|
continue;
|
|
1207
1278
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
pending.push(item);
|
|
1217
|
-
continue;
|
|
1218
|
-
}
|
|
1219
|
-
closeNest();
|
|
1220
|
-
if (item.content.length > 0) item.content = [nestInlineWrappers(outerStack, item.content)];
|
|
1221
|
-
nested.push(item);
|
|
1279
|
+
nested.push(...nestRevisionAwareInlineWrappers(slice, outerStack, revisionOuterWrapperCounts));
|
|
1280
|
+
}
|
|
1281
|
+
const coalesced = [];
|
|
1282
|
+
for (const item of nested) {
|
|
1283
|
+
const previous = coalesced.at(-1);
|
|
1284
|
+
if (previous && isRevisionWrapper(previous) && isRevisionWrapper(item) && previous.type === item.type && JSON.stringify(previous.info) === JSON.stringify(item.info) && revisionOuterWrapperCounts.get(previous) === revisionOuterWrapperCounts.get(item)) {
|
|
1285
|
+
previous.content.push(...item.content);
|
|
1286
|
+
continue;
|
|
1222
1287
|
}
|
|
1223
|
-
|
|
1288
|
+
coalesced.push(item);
|
|
1224
1289
|
}
|
|
1225
|
-
return
|
|
1290
|
+
return coalesced;
|
|
1226
1291
|
};
|
|
1227
1292
|
function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false, inheritedFormattingOverride) {
|
|
1228
1293
|
const content = [];
|
|
@@ -1252,6 +1317,8 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
1252
1317
|
let currentTrackedChange;
|
|
1253
1318
|
const sourceRunOwners = /* @__PURE__ */ new WeakMap();
|
|
1254
1319
|
const sourceHyperlinkIndices = /* @__PURE__ */ new WeakMap();
|
|
1320
|
+
const revisionOuterWrapperCounts = /* @__PURE__ */ new WeakMap();
|
|
1321
|
+
const revisionAncestorsByWrapper = /* @__PURE__ */ new WeakMap();
|
|
1255
1322
|
const openedComments = /* @__PURE__ */ new Set();
|
|
1256
1323
|
const createIndexedHyperlink = (mark) => {
|
|
1257
1324
|
const hyperlink = createHyperlink(mark);
|
|
@@ -1416,16 +1483,13 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
1416
1483
|
} } : {},
|
|
1417
1484
|
...changeAttrs.initials ? { initials: changeAttrs.initials } : {}
|
|
1418
1485
|
};
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
info,
|
|
1427
|
-
content: [anchoredContent]
|
|
1428
|
-
});
|
|
1486
|
+
let type;
|
|
1487
|
+
if (insertionMark) type = changeAttrs.moveKind === "moveTo" ? "moveTo" : "insertion";
|
|
1488
|
+
else type = changeAttrs.moveKind === "moveFrom" ? "moveFrom" : "deletion";
|
|
1489
|
+
const wrapper = createTrackedRunWrapper(type, info, anchoredContent);
|
|
1490
|
+
revisionOuterWrapperCounts.set(wrapper, changeAttrs._docxOuterWrapperCount ?? inlineWrapperStackOf(node).length);
|
|
1491
|
+
revisionAncestorsByWrapper.set(wrapper, changeAttrs._docxRevisionAncestors ?? []);
|
|
1492
|
+
content.push(wrapper);
|
|
1429
1493
|
return;
|
|
1430
1494
|
}
|
|
1431
1495
|
if (insertionMark || deletionMark) {
|
|
@@ -1447,12 +1511,15 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
1447
1511
|
let type;
|
|
1448
1512
|
if (insertionMark) type = changeAttrs.moveKind === "moveTo" ? "moveTo" : "insertion";
|
|
1449
1513
|
else type = changeAttrs.moveKind === "moveFrom" ? "moveFrom" : "deletion";
|
|
1450
|
-
const
|
|
1514
|
+
const outerWrapperCount = changeAttrs._docxOuterWrapperCount ?? inlineWrapperStackOf(node).length;
|
|
1515
|
+
const trackedChangeKey = `${type}:${JSON.stringify(info)}:${outerWrapperCount}:${JSON.stringify(changeAttrs._docxRevisionAncestors ?? [])}`;
|
|
1451
1516
|
if (linkMark) {
|
|
1452
1517
|
const linkKey = getLinkKey(linkMark);
|
|
1453
1518
|
if (!currentTrackedChange || currentTrackedChange.type !== "hyperlink" || currentTrackedChange.key !== trackedChangeKey || currentTrackedChange.hyperlinkKey !== linkKey) {
|
|
1454
1519
|
const hyperlink = createIndexedHyperlink(linkMark);
|
|
1455
1520
|
const wrapper = createTrackedRunWrapper(type, info, hyperlink);
|
|
1521
|
+
revisionOuterWrapperCounts.set(wrapper, outerWrapperCount);
|
|
1522
|
+
revisionAncestorsByWrapper.set(wrapper, changeAttrs._docxRevisionAncestors ?? []);
|
|
1456
1523
|
content.push(wrapper);
|
|
1457
1524
|
currentTrackedChange = {
|
|
1458
1525
|
type: "hyperlink",
|
|
@@ -1486,6 +1553,8 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
1486
1553
|
}
|
|
1487
1554
|
if (!currentTrackedChange || currentTrackedChange.type !== "direct" || currentTrackedChange.key !== trackedChangeKey) {
|
|
1488
1555
|
const wrapper = createTrackedRunWrapper(type, info);
|
|
1556
|
+
revisionOuterWrapperCounts.set(wrapper, outerWrapperCount);
|
|
1557
|
+
revisionAncestorsByWrapper.set(wrapper, changeAttrs._docxRevisionAncestors ?? []);
|
|
1489
1558
|
content.push(wrapper);
|
|
1490
1559
|
currentTrackedChange = {
|
|
1491
1560
|
type: "direct",
|
|
@@ -1640,7 +1709,7 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
|
|
|
1640
1709
|
type: "commentRangeEnd",
|
|
1641
1710
|
id: commentId
|
|
1642
1711
|
});
|
|
1643
|
-
return nestInlineWrapperGroups(content, wrapperGroups, sourceHyperlinkIndices);
|
|
1712
|
+
return nestInlineWrapperGroups(content, wrapperGroups, sourceHyperlinkIndices, revisionOuterWrapperCounts, revisionAncestorsByWrapper);
|
|
1644
1713
|
}
|
|
1645
1714
|
function createTrackedChangeRun({ baseParagraphFormatting, inheritedFormatting, marks, node, paragraphMarkFormatting, paragraphMarkPrecedesStyle, styleResolver }) {
|
|
1646
1715
|
const formattingContext = {
|
|
@@ -2001,7 +2070,7 @@ function restoreRunRecord(run, marks) {
|
|
|
2001
2070
|
}
|
|
2002
2071
|
const identityMark = marks.find((mark) => mark.type.name === RUN_IDENTITY_MARK_NAME);
|
|
2003
2072
|
if (!identityMark) return;
|
|
2004
|
-
const { preservedAttributes, preserved } = expectRunIdentityMarkAttrs(identityMark);
|
|
2073
|
+
const { preservedAttributes, preserved, emptyFormatting } = expectRunIdentityMarkAttrs(identityMark);
|
|
2005
2074
|
if (preservedAttributes && preservedAttributes.length > 0) run.preservedAttributes = preservedAttributes.map(({ namespace, name, value }) => namespace === void 0 ? {
|
|
2006
2075
|
name,
|
|
2007
2076
|
value
|
|
@@ -2014,6 +2083,7 @@ function restoreRunRecord(run, marks) {
|
|
|
2014
2083
|
...run.formatting,
|
|
2015
2084
|
preserved
|
|
2016
2085
|
};
|
|
2086
|
+
if (emptyFormatting && run.formatting === void 0) run.formatting = {};
|
|
2017
2087
|
}
|
|
2018
2088
|
function getRunFormattingFromMarks(marks, options) {
|
|
2019
2089
|
if (!marks || marks.length === 0 && !options) return;
|
|
@@ -16,7 +16,7 @@ import { mergeParagraphFormatting, mergeParagraphTabStops } from "../../utils/pa
|
|
|
16
16
|
import { tableOfContentsStyleLevel } from "../../utils/tableOfContentsStyle.js";
|
|
17
17
|
import { mergeTextFormatting } from "../../utils/textFormattingMerge.js";
|
|
18
18
|
import { emuToPixels, emuToStrokePixels } from "../../utils/units.js";
|
|
19
|
-
import { expectInlineWrapperMarkAttrs } from "../attrs/index.js";
|
|
19
|
+
import { expectInlineWrapperMarkAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
|
|
20
20
|
import { authoredTransformAttrs } from "../authoredTransformAttrs.js";
|
|
21
21
|
import { setAutospacingBaseValue } from "../autospacingBase.js";
|
|
22
22
|
import { planEmptyRanges } from "../emptyRangeAnchor.js";
|
|
@@ -77,16 +77,17 @@ const createTextBoxGroupIdFactory = () => {
|
|
|
77
77
|
* paragraph's and the hyperlink's — so the next payload field is added once
|
|
78
78
|
* rather than to whichever site the author happened to open.
|
|
79
79
|
*
|
|
80
|
-
* Minted when the run holds a page break
|
|
81
|
-
* rejoined into one `w:r` on save), an
|
|
82
|
-
*
|
|
80
|
+
* Minted when the run holds a page break or multiple content items including
|
|
81
|
+
* an inline atom (the leaves have to be rejoined into one `w:r` on save), an
|
|
82
|
+
* attribute remainder, a `w:rPr` sink, or an authored empty `w:rPr`.
|
|
83
83
|
*/
|
|
84
84
|
const runIdentityMark = (run, nextRunIdentityId) => {
|
|
85
85
|
const payload = {
|
|
86
86
|
preservedAttributes: run.preservedAttributes,
|
|
87
|
-
preserved: run.formatting?.preserved
|
|
87
|
+
preserved: run.formatting?.preserved,
|
|
88
|
+
emptyFormatting: run.formatting !== void 0 && Object.keys(run.formatting).length === 0 ? true : void 0
|
|
88
89
|
};
|
|
89
|
-
if (!runHasPageBreakContent(run) && !hasRunIdentityPayload(payload)) return null;
|
|
90
|
+
if (!runHasPageBreakContent(run) && !runHasMixedContent(run) && !hasRunIdentityPayload(payload)) return null;
|
|
90
91
|
return schema.mark(RUN_IDENTITY_MARK_NAME, runIdentityAttrs(nextRunIdentityId(), payload));
|
|
91
92
|
};
|
|
92
93
|
/** Keep imported hyperlink identity unique across every nested conversion scope. */
|
|
@@ -396,8 +397,8 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
|
|
|
396
397
|
type: "direct"
|
|
397
398
|
}], { ordinaryFormatting: ordinaryDefaultRunFormatting });
|
|
398
399
|
const defaultRunFormatting = defaultToggleCascade.formatting;
|
|
399
|
-
if (extraRunFormatting !== void 0) {
|
|
400
|
-
attrs._tableRunFormatting = extraRunFormatting;
|
|
400
|
+
if (extraRunFormatting !== void 0 || paragraph.content.length > 0) {
|
|
401
|
+
if (extraRunFormatting !== void 0) attrs._tableRunFormatting = extraRunFormatting;
|
|
401
402
|
if (defaultRunFormatting) attrs.defaultTextFormatting = defaultRunFormatting;
|
|
402
403
|
else delete attrs.defaultTextFormatting;
|
|
403
404
|
}
|
|
@@ -606,13 +607,21 @@ function anchorPointComment(nodes, commentId) {
|
|
|
606
607
|
* and it is what lets the save leg hoist a revision covering all of them back
|
|
607
608
|
* around the control instead of losing it.
|
|
608
609
|
*/
|
|
609
|
-
const withTrackedRunMark = (node, mark) => {
|
|
610
|
-
if (node.marks.some(({ type }) => type.name === "insertion" || type.name === "deletion")) return node;
|
|
610
|
+
const withTrackedRunMark = (node, mark, ancestor) => {
|
|
611
611
|
if (node.type.name === "sdt") {
|
|
612
612
|
const marked = [];
|
|
613
|
-
for (let index = 0; index < node.childCount; index += 1) marked.push(withTrackedRunMark(node.child(index), mark));
|
|
613
|
+
for (let index = 0; index < node.childCount; index += 1) marked.push(withTrackedRunMark(node.child(index), mark, ancestor));
|
|
614
614
|
return recreateProseNodeWithParagraphPropertySource(node, { content: marked });
|
|
615
615
|
}
|
|
616
|
+
const nestedMark = node.marks.find(({ type }) => type.name === "insertion" || type.name === "deletion");
|
|
617
|
+
if (nestedMark) {
|
|
618
|
+
const nestedAttrs = expectTrackedChangeMarkAttrs(nestedMark);
|
|
619
|
+
const updatedMark = schema.mark(nestedMark.type.name, {
|
|
620
|
+
...nestedMark.attrs,
|
|
621
|
+
_docxRevisionAncestors: [ancestor, ...nestedAttrs._docxRevisionAncestors ?? []]
|
|
622
|
+
});
|
|
623
|
+
return node.mark(updatedMark.addToSet(node.marks));
|
|
624
|
+
}
|
|
616
625
|
if (trackedRunInlineAtomDisposition(node) === "outside-wrapper") panic(`Inline atom ${JSON.stringify(node.type.name)} cannot occur in a tracked-run wrapper`);
|
|
617
626
|
if (canCarryTrackedRunMark(node)) return node.mark(mark.addToSet(node.marks));
|
|
618
627
|
return node;
|
|
@@ -652,7 +661,7 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, next
|
|
|
652
661
|
} else if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") {
|
|
653
662
|
const nestedMarkType = item.type === "insertion" || item.type === "moveTo" ? "insertion" : "deletion";
|
|
654
663
|
const nestedMoveKind = item.type === "moveFrom" || item.type === "moveTo" ? item.type : null;
|
|
655
|
-
itemNodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, nextRunIdentityId, runFormattingResolvers, styleResolver, nestedMoveKind, textBoxAnchors));
|
|
664
|
+
itemNodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, nextRunIdentityId, runFormattingResolvers, styleResolver, nestedMoveKind, textBoxAnchors, [...wrappedBy, ...stack]));
|
|
656
665
|
} else if (item.type === "inlineSdt") {
|
|
657
666
|
const sdtNode = convertInlineSdt(item, nextHyperlinkInstanceIndex, nextRunIdentityId, getTrackedRunFormatting, runFormattingResolvers, styleResolver, textBoxAnchors, [...wrappedBy, ...stack]);
|
|
658
667
|
if (sdtNode) itemNodes.push(sdtNode);
|
|
@@ -681,9 +690,19 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, next
|
|
|
681
690
|
utcDate: change.info.utcDate?.value ?? null,
|
|
682
691
|
initials: change.info.initials ?? null,
|
|
683
692
|
moveKind,
|
|
693
|
+
_docxOuterWrapperCount: wrappedBy.length,
|
|
684
694
|
...markType === "deletion" ? { _historicalFormatting: true } : {}
|
|
685
695
|
});
|
|
686
|
-
|
|
696
|
+
const ancestor = {
|
|
697
|
+
type: change.type,
|
|
698
|
+
revisionId: change.info.id,
|
|
699
|
+
author: change.info.author,
|
|
700
|
+
...change.info.date ? { date: change.info.date } : {},
|
|
701
|
+
...change.info.utcDate ? { utcDate: change.info.utcDate.value } : {},
|
|
702
|
+
...change.info.initials ? { initials: change.info.initials } : {},
|
|
703
|
+
outerWrapperCount: wrappedBy.length
|
|
704
|
+
};
|
|
705
|
+
return nodes.map((node) => withTrackedRunMark(node, mark, ancestor));
|
|
687
706
|
}
|
|
688
707
|
function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay) {
|
|
689
708
|
const formatting = paragraph.formatting;
|
|
@@ -1691,6 +1710,7 @@ function convertRun(run, resolvedStyleFormatting, nextRunIdentityId, styleResolv
|
|
|
1691
1710
|
return nodes;
|
|
1692
1711
|
}
|
|
1693
1712
|
const runHasPageBreakContent = (run) => run.content.some((content) => content.type === "break" && content.breakType === "page");
|
|
1713
|
+
const runHasMixedContent = (run) => run.content.length > 1 && run.content.some((content) => content.type !== "text");
|
|
1694
1714
|
/**
|
|
1695
1715
|
* Whether a field's result holds an explicit page break.
|
|
1696
1716
|
*
|
|
@@ -44,13 +44,10 @@ type SchemaMarkName = keyof typeof MARK_EXTENSIONS;
|
|
|
44
44
|
* Five of these marks are containers on the save leg as well, and their
|
|
45
45
|
* relative order is that leg's rather than a choice made here.
|
|
46
46
|
* `extractParagraphContent` opens a comment range around everything a leaf
|
|
47
|
-
* produces
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* the runs it holds (`fromProseDoc.ts:2567`). So the save writes
|
|
52
|
-
* `w:commentRangeStart … w:ins > w:bdo > w:hyperlink > w:r`, and the editor DOM
|
|
53
|
-
* nests `span.docx-comment > span.docx-insertion > bdo > a`.
|
|
47
|
+
* produces. For an editor-created revision, the existing wrapper stack owns
|
|
48
|
+
* the change, followed by the hyperlink and the runs it holds. So the save
|
|
49
|
+
* writes `w:commentRangeStart … w:bdo > w:ins > w:hyperlink > w:r`, and the
|
|
50
|
+
* editor DOM nests `span.docx-comment > bdo > span.docx-insertion > a`.
|
|
54
51
|
* `markNestingOrder.test.ts` holds the two legs to each other.
|
|
55
52
|
*
|
|
56
53
|
* `runIdentity` is the fifth: `w:r` is the element it names, and it is the
|
|
@@ -60,6 +57,6 @@ type SchemaMarkName = keyof typeof MARK_EXTENSIONS;
|
|
|
60
57
|
* own in OOXML, so the save leg ranks it against nothing and its place here is
|
|
61
58
|
* a presentation decision.
|
|
62
59
|
*/
|
|
63
|
-
declare const MARK_NESTING_ORDER: readonly ["bold", "italic", "underline", "strike", "textColor", "runShading", "highlight", "fontSize", "fontFamily", "language", "superscript", "subscript", "allCaps", "smallCaps", "footnoteRef", "characterSpacing", "emboss", "imprint", "hidden", "textShadow", "emphasisMark", "textOutline", "rtl", "textEffect", "runFormattingOverride", "characterStyle", "comment", "
|
|
60
|
+
declare const MARK_NESTING_ORDER: readonly ["bold", "italic", "underline", "strike", "textColor", "runShading", "highlight", "fontSize", "fontFamily", "language", "superscript", "subscript", "allCaps", "smallCaps", "footnoteRef", "characterSpacing", "emboss", "imprint", "hidden", "textShadow", "emphasisMark", "textOutline", "rtl", "textEffect", "runFormattingOverride", "characterStyle", "comment", "inlineWrapper", "insertion", "deletion", "hyperlink", "runPropertyChange", "runIdentity"];
|
|
64
61
|
//#endregion
|
|
65
62
|
export { MARK_EXTENSIONS, MARK_NESTING_ORDER, SchemaMarkName };
|
|
@@ -83,13 +83,10 @@ const markNestingOrder = (names) => names;
|
|
|
83
83
|
* Five of these marks are containers on the save leg as well, and their
|
|
84
84
|
* relative order is that leg's rather than a choice made here.
|
|
85
85
|
* `extractParagraphContent` opens a comment range around everything a leaf
|
|
86
|
-
* produces
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* the runs it holds (`fromProseDoc.ts:2567`). So the save writes
|
|
91
|
-
* `w:commentRangeStart … w:ins > w:bdo > w:hyperlink > w:r`, and the editor DOM
|
|
92
|
-
* nests `span.docx-comment > span.docx-insertion > bdo > a`.
|
|
86
|
+
* produces. For an editor-created revision, the existing wrapper stack owns
|
|
87
|
+
* the change, followed by the hyperlink and the runs it holds. So the save
|
|
88
|
+
* writes `w:commentRangeStart … w:bdo > w:ins > w:hyperlink > w:r`, and the
|
|
89
|
+
* editor DOM nests `span.docx-comment > bdo > span.docx-insertion > a`.
|
|
93
90
|
* `markNestingOrder.test.ts` holds the two legs to each other.
|
|
94
91
|
*
|
|
95
92
|
* `runIdentity` is the fifth: `w:r` is the element it names, and it is the
|
|
@@ -127,9 +124,9 @@ const MARK_NESTING_ORDER = markNestingOrder([
|
|
|
127
124
|
"runFormattingOverride",
|
|
128
125
|
"characterStyle",
|
|
129
126
|
"comment",
|
|
127
|
+
"inlineWrapper",
|
|
130
128
|
"insertion",
|
|
131
129
|
"deletion",
|
|
132
|
-
"inlineWrapper",
|
|
133
130
|
"hyperlink",
|
|
134
131
|
"runPropertyChange",
|
|
135
132
|
"runIdentity"
|
|
@@ -105,7 +105,8 @@ const RunIdentityExtension = createMarkExtension({
|
|
|
105
105
|
attrs: {
|
|
106
106
|
id: {},
|
|
107
107
|
preservedAttributes: { default: null },
|
|
108
|
-
preserved: { default: null }
|
|
108
|
+
preserved: { default: null },
|
|
109
|
+
emptyFormatting: { default: null }
|
|
109
110
|
},
|
|
110
111
|
inclusive: false,
|
|
111
112
|
parseDOM: [{
|