@stll/folio-core 0.54.0 → 0.54.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +1 -1
- package/dist/ai-edits/apply.js +43 -31
- package/dist/ai-edits/headless.d.ts +1 -2
- package/dist/ai-edits/headless.js +12 -27
- package/dist/ai-edits/read.d.ts +1 -0
- package/dist/ai-edits/read.js +7 -2
- package/dist/ai-edits/table-mutation-plan.d.ts +2 -1
- package/dist/ai-edits/table-mutation-plan.js +39 -1
- package/dist/ai-suggestions/apply.d.ts +1 -1
- package/dist/ai-suggestions/apply.js +3 -2
- package/dist/compare/compare.js +2 -1
- package/dist/docx/bookmarkIds.d.ts +11 -0
- package/dist/docx/bookmarkIds.js +33 -0
- package/dist/docx/documentParser.d.ts +14 -1
- package/dist/docx/documentParser.js +17 -4
- package/dist/docx/drawingIdNormalization.js +23 -8
- package/dist/docx/ensureParaIds.d.ts +5 -0
- package/dist/docx/ensureParaIds.js +10 -2
- package/dist/docx/listNumberingInstances.js +11 -9
- package/dist/docx/noteIds.d.ts +4 -0
- package/dist/docx/noteIds.js +13 -0
- package/dist/docx/numberingIds.d.ts +14 -0
- package/dist/docx/numberingIds.js +16 -0
- package/dist/docx/numericIdAllocator.d.ts +12 -0
- package/dist/docx/numericIdAllocator.js +33 -0
- package/dist/docx/numericIdNormalization.d.ts +16 -0
- package/dist/docx/numericIdNormalization.js +174 -0
- package/dist/docx/paragraphPropertySource.js +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +2 -1
- package/dist/docx/parser.js +17 -5
- package/dist/docx/rasterMime.d.ts +64 -0
- package/dist/docx/rasterMime.js +116 -0
- package/dist/docx/replyToComment.js +13 -6
- package/dist/docx/rezip.js +14 -7
- package/dist/docx/selectiveSave.js +10 -0
- package/dist/docx/serializer/partNamespaces.js +4 -2
- package/dist/docx/serializer/runSerializer.js +5 -4
- package/dist/docx/serializer/trackedChangeAttributes.js +7 -1
- package/dist/docx/server/build.js +14 -3
- package/dist/docx/server/createBilingualDocument.js +10 -9
- package/dist/docx/streamingXmlParser.d.ts +10 -1
- package/dist/docx/streamingXmlParser.js +88 -19
- package/dist/docx/unzip.d.ts +3 -1
- package/dist/docx/unzip.js +36 -16
- package/dist/docx/verticalMergeProjection.d.ts +17 -0
- package/dist/docx/verticalMergeProjection.js +120 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.d.ts +6 -0
- package/dist/layout-bridge/convert/fixedTableColumnWidths.js +23 -0
- package/dist/layout-bridge/convert/tableConversion.js +4 -1
- package/dist/layout-painter/index.js +2 -5
- package/dist/paged-layout/editorScrollRoot.d.ts +15 -0
- package/dist/paged-layout/editorScrollRoot.js +72 -0
- package/dist/paged-layout/scrollToPmPosition.js +8 -22
- package/dist/prosemirror/commands/clearParagraphIndent.d.ts +6 -0
- package/dist/prosemirror/commands/clearParagraphIndent.js +25 -0
- package/dist/prosemirror/commands/comments.js +6 -1
- package/dist/prosemirror/commands/propertyChangeScope.js +4 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.js +18 -24
- package/dist/prosemirror/commands/resolveParagraphProperties.js +13 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -9
- package/dist/prosemirror/commands/tableCellMergeResolution.js +28 -25
- package/dist/prosemirror/commands/tableMergeFoldDecisions.d.ts +61 -0
- package/dist/prosemirror/commands/tableMergeFoldDecisions.js +79 -0
- package/dist/prosemirror/commentIdAllocator.d.ts +15 -22
- package/dist/prosemirror/commentIdAllocator.js +50 -25
- package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -6
- package/dist/prosemirror/conversion/toProseDoc.js +2 -117
- package/dist/prosemirror/extensions/core/HistoryExtension.js +97 -5
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +3 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +22 -28
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -6
- package/dist/prosemirror/listNumbering.js +14 -14
- package/dist/prosemirror/listRendering.d.ts +4 -2
- package/dist/prosemirror/listRendering.js +3 -4
- package/dist/prosemirror/paragraphPropertyCarry.js +2 -5
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -19
- package/dist/prosemirror/plugins/revisionIds.js +110 -29
- package/dist/prosemirror/plugins/suggestionMode.d.ts +2 -2
- package/dist/prosemirror/plugins/suggestionMode.js +197 -45
- package/dist/prosemirror/plugins/templateDirectives.d.ts +6 -0
- package/dist/prosemirror/plugins/templateDirectives.js +21 -7
- package/dist/prosemirror/plugins/templatePreviewValues.js +14 -21
- package/dist/prosemirror/storyListNumbering.js +11 -9
- package/dist/prosemirror/textInput.d.ts +6 -1
- package/dist/prosemirror/textInput.js +9 -2
- package/dist/prosemirror/utils/visualLineNavigation.js +6 -8
- package/dist/utils/mergeDocumentContent.d.ts +2 -2
- package/dist/utils/mergeDocumentContent.js +11 -8
- package/package.json +3 -3
package/dist/docx/unzip.js
CHANGED
|
@@ -2,6 +2,7 @@ import { bytesToDataUrl } from "../utils/base64.js";
|
|
|
2
2
|
import { compressionRatioLimitFor, countCentralDirectoryRecords, createInflationBudget, exceedsCompressionRatio, getZipEntrySizes, inflateEntryWithinLimits, isStoredZipEntry } from "./archiveInflation.js";
|
|
3
3
|
import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./encryption/containerFormat.js";
|
|
4
4
|
import { openDocxBuffer } from "./encryption/openEncryptedDocx.js";
|
|
5
|
+
import { RASTER_MIME_TYPES, detectRasterMimeType } from "./rasterMime.js";
|
|
5
6
|
import { decodeXmlBytes } from "./xmlEncoding.js";
|
|
6
7
|
import { FOLIO_XML_RESOURCE_LIMITS, assertXmlResourceLimits, createXmlPackageBudget } from "./xmlResourceLimits.js";
|
|
7
8
|
import JSZip from "jszip";
|
|
@@ -38,12 +39,7 @@ var DocxSecurityError = class extends Error {
|
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
41
|
const DEFAULT_ALLOWED_MEDIA_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
41
|
-
|
|
42
|
-
"image/jpeg",
|
|
43
|
-
"image/gif",
|
|
44
|
-
"image/bmp",
|
|
45
|
-
"image/tiff",
|
|
46
|
-
"image/webp",
|
|
42
|
+
...RASTER_MIME_TYPES,
|
|
47
43
|
"image/x-emf",
|
|
48
44
|
"image/x-wmf"
|
|
49
45
|
]);
|
|
@@ -227,11 +223,12 @@ async function unzipDocx(buffer, options = {}, { verifyUnreadEntries = true } =
|
|
|
227
223
|
return null;
|
|
228
224
|
}
|
|
229
225
|
const binaryContent = result.bytes.buffer;
|
|
230
|
-
|
|
226
|
+
const effectiveMimeType = RASTER_MIME_TYPES.has(mimeType) ? detectRasterMimeType(binaryContent) : mimeType;
|
|
227
|
+
if (!effectiveMimeType || !limits.allowedMediaMimeTypes.has(effectiveMimeType) || !isMediaContentAllowed(binaryContent, effectiveMimeType)) return null;
|
|
231
228
|
return {
|
|
232
229
|
type: "media",
|
|
233
230
|
path,
|
|
234
|
-
mimeType,
|
|
231
|
+
mimeType: effectiveMimeType,
|
|
235
232
|
content: binaryContent
|
|
236
233
|
};
|
|
237
234
|
});
|
|
@@ -274,6 +271,26 @@ async function unzipDocx(buffer, options = {}, { verifyUnreadEntries = true } =
|
|
|
274
271
|
}
|
|
275
272
|
return content;
|
|
276
273
|
}
|
|
274
|
+
/** Keep extracted projections and the source ZIP aligned after parser normalization. */
|
|
275
|
+
const replaceRawDocxXmlParts = async (content, parts) => {
|
|
276
|
+
let identitiesChanged = false;
|
|
277
|
+
for (const [path, xmlContent] of parts) {
|
|
278
|
+
if (content.allXml.get(path) === xmlContent) continue;
|
|
279
|
+
indexXmlContent(content, {
|
|
280
|
+
type: "xml",
|
|
281
|
+
path,
|
|
282
|
+
lowerPath: path.toLowerCase(),
|
|
283
|
+
content: xmlContent
|
|
284
|
+
});
|
|
285
|
+
content.originalZip.file(path, xmlContent);
|
|
286
|
+
identitiesChanged = true;
|
|
287
|
+
}
|
|
288
|
+
if (identitiesChanged) content.originalBuffer = await content.originalZip.generateAsync({
|
|
289
|
+
type: "arraybuffer",
|
|
290
|
+
compression: "DEFLATE",
|
|
291
|
+
compressionOptions: { level: 1 }
|
|
292
|
+
});
|
|
293
|
+
};
|
|
277
294
|
/**
|
|
278
295
|
* Preflight every XML part the unzip retains, not a named few.
|
|
279
296
|
*
|
|
@@ -291,6 +308,14 @@ function assignXmlContent(content, { path, lowerPath, content: xmlContent }, lim
|
|
|
291
308
|
partPath: path,
|
|
292
309
|
budget
|
|
293
310
|
});
|
|
311
|
+
indexXmlContent(content, {
|
|
312
|
+
type: "xml",
|
|
313
|
+
path,
|
|
314
|
+
lowerPath,
|
|
315
|
+
content: xmlContent
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const indexXmlContent = (content, { path, lowerPath, content: xmlContent }) => {
|
|
294
319
|
content.allXml.set(path, xmlContent);
|
|
295
320
|
if (lowerPath === "word/document.xml") content.documentXml = xmlContent;
|
|
296
321
|
else if (lowerPath === "word/styles.xml") content.stylesXml = xmlContent;
|
|
@@ -317,7 +342,7 @@ function assignXmlContent(content, { path, lowerPath, content: xmlContent }, lim
|
|
|
317
342
|
const filename = path.split("/").pop() || path;
|
|
318
343
|
content.footers.set(filename, xmlContent);
|
|
319
344
|
}
|
|
320
|
-
}
|
|
345
|
+
};
|
|
321
346
|
function inflationLimitError(limit, path) {
|
|
322
347
|
switch (limit) {
|
|
323
348
|
case "declared-size": return new DocxSecurityError(`DOCX entry inflates past its declared size: ${path}`);
|
|
@@ -499,14 +524,9 @@ function isEntryTooLarge(declaredSize, maxBytes) {
|
|
|
499
524
|
return declaredSize !== null && declaredSize > maxBytes;
|
|
500
525
|
}
|
|
501
526
|
function isMediaContentAllowed(data, mimeType) {
|
|
527
|
+
if (RASTER_MIME_TYPES.has(mimeType)) return detectRasterMimeType(data) === mimeType;
|
|
502
528
|
const bytes = new Uint8Array(data);
|
|
503
529
|
switch (mimeType) {
|
|
504
|
-
case "image/png": return bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71;
|
|
505
|
-
case "image/jpeg": return bytes[0] === 255 && bytes[1] === 216;
|
|
506
|
-
case "image/gif": return bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56;
|
|
507
|
-
case "image/bmp": return bytes[0] === 66 && bytes[1] === 77;
|
|
508
|
-
case "image/webp": return bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80;
|
|
509
|
-
case "image/tiff": return bytes[0] === 73 && bytes[1] === 73 || bytes[0] === 77 && bytes[1] === 77;
|
|
510
530
|
case "image/x-emf":
|
|
511
531
|
case "image/emf": return bytes.length >= 44 && bytes[0] === 1 && bytes[40] === 32 && bytes[41] === 69 && bytes[42] === 77 && bytes[43] === 70;
|
|
512
532
|
case "image/x-wmf":
|
|
@@ -605,4 +625,4 @@ function getContentSummary(content) {
|
|
|
605
625
|
};
|
|
606
626
|
}
|
|
607
627
|
//#endregion
|
|
608
|
-
export { DocxSecurityError, extractFile, getContentSummary, getEntryUncompressedSize, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, unzipDocx };
|
|
628
|
+
export { DocxSecurityError, extractFile, getContentSummary, getEntryUncompressedSize, getFileList, getMediaMimeType, hasFile, mediaToDataUrl, replaceRawDocxXmlParts, unzipDocx };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/docx/verticalMergeProjection.d.ts
|
|
3
|
+
/** The reader and revision resolver share which continuation cells remain visible. */
|
|
4
|
+
type RowSpanInfo = {
|
|
5
|
+
rowSpan: number;
|
|
6
|
+
skip: boolean;
|
|
7
|
+
preserveVMergeRestart?: boolean;
|
|
8
|
+
continuationCells?: document_d_exports.TableCell[];
|
|
9
|
+
};
|
|
10
|
+
declare function calculateRowSpans(table: document_d_exports.Table): Map<string, RowSpanInfo>;
|
|
11
|
+
/**
|
|
12
|
+
* Whether a cell carries content a vertical-merge continuation must not hide.
|
|
13
|
+
* The reader keeps such a continuation as a cell of its own.
|
|
14
|
+
*/
|
|
15
|
+
declare function tableCellHasMeaningfulContent(cell: document_d_exports.TableCell): boolean;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { RowSpanInfo, calculateRowSpans, tableCellHasMeaningfulContent };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { isCellMergeContinuation } from "./tableParser.js";
|
|
2
|
+
//#region src/docx/verticalMergeProjection.ts
|
|
3
|
+
function calculateRowSpans(table) {
|
|
4
|
+
const result = /* @__PURE__ */ new Map();
|
|
5
|
+
const numRows = table.rows.length;
|
|
6
|
+
const activeMerges = /* @__PURE__ */ new Map();
|
|
7
|
+
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
|
|
8
|
+
const row = table.rows[rowIndex];
|
|
9
|
+
if (row.cells.length === 0) {
|
|
10
|
+
clearActiveVerticalMerges(activeMerges, result);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
let colIndex = row.formatting?.gridBefore ?? 0;
|
|
14
|
+
const rowCells = row.cells.map((cell) => {
|
|
15
|
+
const colspan = cell.formatting?.gridSpan ?? 1;
|
|
16
|
+
const vMerge = cell.formatting?.vMerge;
|
|
17
|
+
const isMergeContinuation = isCellMergeContinuation(cell);
|
|
18
|
+
const startRow = isMergeContinuation ? activeMerges.get(colIndex) : void 0;
|
|
19
|
+
const info = {
|
|
20
|
+
cell,
|
|
21
|
+
colIndex,
|
|
22
|
+
colspan,
|
|
23
|
+
vMerge,
|
|
24
|
+
isMergeContinuation,
|
|
25
|
+
startRow,
|
|
26
|
+
hasMeaningfulContent: tableCellHasMeaningfulContent(cell),
|
|
27
|
+
shouldSkip: isMergeContinuation && startRow !== void 0
|
|
28
|
+
};
|
|
29
|
+
colIndex += colspan;
|
|
30
|
+
return info;
|
|
31
|
+
});
|
|
32
|
+
const rowWouldBeEmpty = rowCells.length > 0 && rowCells.every((cell) => cell.shouldSkip);
|
|
33
|
+
for (const cellInfo of rowCells) {
|
|
34
|
+
const { colIndex: cellColIndex, vMerge, isMergeContinuation, startRow, hasMeaningfulContent } = cellInfo;
|
|
35
|
+
const key = `${rowIndex}-${cellColIndex}`;
|
|
36
|
+
if (vMerge === "restart") {
|
|
37
|
+
closeVerticalMerge(activeMerges, result, cellColIndex);
|
|
38
|
+
activeMerges.set(cellColIndex, rowIndex);
|
|
39
|
+
result.set(key, {
|
|
40
|
+
rowSpan: 1,
|
|
41
|
+
skip: false
|
|
42
|
+
});
|
|
43
|
+
} else if (isMergeContinuation) {
|
|
44
|
+
if (startRow === void 0 || rowWouldBeEmpty || hasMeaningfulContent) {
|
|
45
|
+
result.set(key, {
|
|
46
|
+
rowSpan: 1,
|
|
47
|
+
skip: false
|
|
48
|
+
});
|
|
49
|
+
if ((rowWouldBeEmpty || hasMeaningfulContent) && startRow !== void 0) {
|
|
50
|
+
const restartCell = result.get(`${startRow}-${cellColIndex}`);
|
|
51
|
+
if (restartCell) restartCell.preserveVMergeRestart = true;
|
|
52
|
+
activeMerges.delete(cellColIndex);
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const startKey = `${startRow}-${cellColIndex}`;
|
|
57
|
+
const startCell = result.get(startKey);
|
|
58
|
+
if (startCell) {
|
|
59
|
+
startCell.rowSpan++;
|
|
60
|
+
startCell.continuationCells ??= [];
|
|
61
|
+
startCell.continuationCells.push(cellInfo.cell);
|
|
62
|
+
}
|
|
63
|
+
result.set(key, {
|
|
64
|
+
rowSpan: 1,
|
|
65
|
+
skip: true
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
closeVerticalMerge(activeMerges, result, cellColIndex);
|
|
69
|
+
result.set(key, {
|
|
70
|
+
rowSpan: 1,
|
|
71
|
+
skip: false
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
clearActiveVerticalMerges(activeMerges, result);
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* End the vertical merge active in one column.
|
|
81
|
+
*
|
|
82
|
+
* A `w:vMerge="restart"` is carried through the editor by its cell's rowspan,
|
|
83
|
+
* which only exists once a continuation joins it. A merge that closes with a
|
|
84
|
+
* rowspan of 1 — a restart with no continuation, one interrupted by a plain
|
|
85
|
+
* cell, one whose continuation is in a row a revision removed — has nothing
|
|
86
|
+
* but this flag to say the cell was a merge origin, and dropping it changes
|
|
87
|
+
* the table's visible structure.
|
|
88
|
+
*/
|
|
89
|
+
function closeVerticalMerge(activeMerges, result, colIndex) {
|
|
90
|
+
const startRow = activeMerges.get(colIndex);
|
|
91
|
+
if (startRow === void 0) return;
|
|
92
|
+
const restartCell = result.get(`${startRow}-${colIndex}`);
|
|
93
|
+
if (restartCell && restartCell.rowSpan === 1) restartCell.preserveVMergeRestart = true;
|
|
94
|
+
activeMerges.delete(colIndex);
|
|
95
|
+
}
|
|
96
|
+
function clearActiveVerticalMerges(activeMerges, result) {
|
|
97
|
+
for (const colIndex of [...activeMerges.keys()]) closeVerticalMerge(activeMerges, result, colIndex);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Whether a cell carries content a vertical-merge continuation must not hide.
|
|
101
|
+
* The reader keeps such a continuation as a cell of its own.
|
|
102
|
+
*/
|
|
103
|
+
function tableCellHasMeaningfulContent(cell) {
|
|
104
|
+
return cell.content.some(blockHasMeaningfulContent);
|
|
105
|
+
}
|
|
106
|
+
function blockHasMeaningfulContent(block) {
|
|
107
|
+
if (block.type === "preservedBlock") return true;
|
|
108
|
+
if (block.type === "bookmarkStart" || block.type === "bookmarkEnd") return true;
|
|
109
|
+
if (block.type === "table") return block.rows.some((row) => row.cells.some((cell) => tableCellHasMeaningfulContent(cell)));
|
|
110
|
+
if (block.type === "blockSdt" || block.type === "blockCustomXml") return true;
|
|
111
|
+
return block.content.some(paragraphContentHasMeaningfulContent);
|
|
112
|
+
}
|
|
113
|
+
function paragraphContentHasMeaningfulContent(content) {
|
|
114
|
+
if (content.type === "run") return content.content.length > 0;
|
|
115
|
+
if (content.type === "hyperlink") return content.children.some(paragraphContentHasMeaningfulContent);
|
|
116
|
+
if (content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") return content.content.some(paragraphContentHasMeaningfulContent);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { calculateRowSpans, tableCellHasMeaningfulContent };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/layout-bridge/convert/fixedTableColumnWidths.d.ts
|
|
3
|
+
/** Resolve absolute cell preferences for an unbounded fixed-layout grid (§17.4.53). */
|
|
4
|
+
declare const fixedTableColumnWidths: (table: Node, grid: readonly number[]) => readonly number[];
|
|
5
|
+
//#endregion
|
|
6
|
+
export { fixedTableColumnWidths };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expectTableCellAttrs, expectTableRowAttrs } from "../../prosemirror/attrs/index.js";
|
|
2
|
+
//#region src/layout-bridge/convert/fixedTableColumnWidths.ts
|
|
3
|
+
/** Resolve absolute cell preferences for an unbounded fixed-layout grid (§17.4.53). */
|
|
4
|
+
const fixedTableColumnWidths = (table, grid) => {
|
|
5
|
+
const preferred = /* @__PURE__ */ new Map();
|
|
6
|
+
for (let rowIndex = 0; rowIndex < table.childCount; rowIndex++) {
|
|
7
|
+
const row = table.child(rowIndex);
|
|
8
|
+
const rowAttrs = expectTableRowAttrs(row);
|
|
9
|
+
if (rowAttrs.trIns || rowAttrs.trDel || rowAttrs.hidden) return grid;
|
|
10
|
+
let column = Math.max(0, rowAttrs._originalFormatting?.gridBefore ?? 0);
|
|
11
|
+
for (let cellIndex = 0; cellIndex < row.childCount; cellIndex++) {
|
|
12
|
+
const attrs = expectTableCellAttrs(row.child(cellIndex));
|
|
13
|
+
if (attrs._omittedGridSlot) continue;
|
|
14
|
+
const width = attrs._authoredWidth;
|
|
15
|
+
if (attrs.colspan !== 1 || attrs.rowspan !== 1 || attrs._preserveVMergeRestart || attrs._originalFormatting?.vMerge !== void 0 || !width || width.type !== "dxa" || !Number.isFinite(width.value) || width.value <= 0) return grid;
|
|
16
|
+
if (column < grid.length) preferred.set(column, Math.max(preferred.get(column) ?? 0, width.value));
|
|
17
|
+
column++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return grid.map((width, column) => preferred.get(column) ?? width);
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { fixedTableColumnWidths };
|
|
@@ -3,6 +3,7 @@ import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
|
|
|
3
3
|
import { expectParagraphAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs } from "../../prosemirror/attrs/index.js";
|
|
4
4
|
import { textBoxHostParagraph } from "../../prosemirror/textBoxHostParagraph.js";
|
|
5
5
|
import { normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
|
|
6
|
+
import { fixedTableColumnWidths } from "./fixedTableColumnWidths.js";
|
|
6
7
|
import { convertBorderSpecToLayout } from "./flowBorders.js";
|
|
7
8
|
import { nextBlockId, twipsToPixels } from "./flowConversionShared.js";
|
|
8
9
|
import { countPageBreakRuns, hasSingleLeadingProjectedPageBreak } from "./pageBreakSplitting.js";
|
|
@@ -183,7 +184,9 @@ function convertTable(node, startPos, options) {
|
|
|
183
184
|
if (child.type.name === "tableRow") rows.push(convertTableRow(child, offset, options, rowContext));
|
|
184
185
|
offset += child.nodeSize;
|
|
185
186
|
});
|
|
186
|
-
|
|
187
|
+
const columnWidthsTwips = attrs.columnWidths;
|
|
188
|
+
const hasTableWidth = (attrs.widthType === "dxa" || attrs.widthType === "pct") && (attrs.width ?? 0) > 0;
|
|
189
|
+
let columnWidths = (columnWidthsTwips && attrs._originalFormatting?.layout === "fixed" && !hasTableWidth ? fixedTableColumnWidths(node, columnWidthsTwips) : columnWidthsTwips)?.map(twipsToPixels);
|
|
187
190
|
const width = attrs.width;
|
|
188
191
|
const widthType = attrs.widthType;
|
|
189
192
|
if (!columnWidths && rows.length > 0) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { scrollEditorElementIntoView } from "../paged-layout/editorScrollRoot.js";
|
|
2
2
|
import { createDefaultRegistry } from "./registry/modules.js";
|
|
3
3
|
import { createFeatureRegistry } from "./registry/registry.js";
|
|
4
4
|
import { FRAGMENT_CLASS_NAMES, renderFragment } from "./renderFragment.js";
|
|
@@ -167,10 +167,7 @@ var LayoutPainter = class {
|
|
|
167
167
|
*/
|
|
168
168
|
scrollToPage(pageNumber) {
|
|
169
169
|
const state = this.pageStates.find((s) => s.pageNumber === pageNumber);
|
|
170
|
-
if (state?.element) state.element
|
|
171
|
-
behavior: prefersReducedMotionBehavior(),
|
|
172
|
-
block: "start"
|
|
173
|
-
});
|
|
170
|
+
if (state?.element) scrollEditorElementIntoView(state.element, { block: "start" });
|
|
174
171
|
}
|
|
175
172
|
};
|
|
176
173
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/paged-layout/editorScrollRoot.d.ts
|
|
2
|
+
declare const getEditorScrollRoot: (element: HTMLElement | null) => HTMLElement | null;
|
|
3
|
+
/** Resolve the root even when a caller holds a painted descendant or viewport. */
|
|
4
|
+
declare const scrollEditorTo: (element: HTMLElement | null, options: ScrollToOptions) => void;
|
|
5
|
+
declare const scrollEditorBy: (element: HTMLElement | null, delta: number) => void;
|
|
6
|
+
type ScrollEditorElementOptions = {
|
|
7
|
+
block?: "start" | "center" | "end" | "nearest";
|
|
8
|
+
behavior?: ScrollBehavior;
|
|
9
|
+
/** Space to leave between the target and the visible root edge. */
|
|
10
|
+
margin?: number;
|
|
11
|
+
};
|
|
12
|
+
/** Scroll only the marked root, including when the target is inside a scaled viewport. */
|
|
13
|
+
declare const scrollEditorElementIntoView: (element: HTMLElement | null, { block, behavior, margin }?: ScrollEditorElementOptions) => void;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { getEditorScrollRoot, scrollEditorBy, scrollEditorElementIntoView, scrollEditorTo };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { prefersReducedMotionBehavior } from "./scrollNavigation.js";
|
|
2
|
+
//#region src/paged-layout/editorScrollRoot.ts
|
|
3
|
+
/** The marked editor owns scrolling; navigation must never move host ancestors. */
|
|
4
|
+
const EDITOR_SCROLL_ROOT_SELECTOR = "[data-folio-scroll]";
|
|
5
|
+
const getEditorScrollRoot = (element) => element?.closest(EDITOR_SCROLL_ROOT_SELECTOR) ?? null;
|
|
6
|
+
/** Resolve the root even when a caller holds a painted descendant or viewport. */
|
|
7
|
+
const scrollEditorTo = (element, options) => {
|
|
8
|
+
getEditorScrollRoot(element)?.scrollTo(options);
|
|
9
|
+
};
|
|
10
|
+
const scrollEditorBy = (element, delta) => {
|
|
11
|
+
const root = getEditorScrollRoot(element);
|
|
12
|
+
if (root) scrollEditorTo(root, {
|
|
13
|
+
top: root.scrollTop + delta,
|
|
14
|
+
behavior: "instant"
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
const nearestScrollOffset = ({ targetStart, targetEnd, viewportStart, viewportEnd }) => {
|
|
18
|
+
const above = targetStart < viewportStart;
|
|
19
|
+
const below = targetEnd > viewportEnd;
|
|
20
|
+
if (above && below) return 0;
|
|
21
|
+
const targetSize = targetEnd - targetStart;
|
|
22
|
+
const viewportSize = viewportEnd - viewportStart;
|
|
23
|
+
if (above && targetSize <= viewportSize || below && targetSize > viewportSize) return targetStart - viewportStart;
|
|
24
|
+
if (below && targetSize <= viewportSize || above && targetSize > viewportSize) return targetEnd - viewportEnd;
|
|
25
|
+
return 0;
|
|
26
|
+
};
|
|
27
|
+
/** Scroll only the marked root, including when the target is inside a scaled viewport. */
|
|
28
|
+
const scrollEditorElementIntoView = (element, { block = "center", behavior = prefersReducedMotionBehavior(), margin = 0 } = {}) => {
|
|
29
|
+
const root = getEditorScrollRoot(element);
|
|
30
|
+
if (!root || !element) return;
|
|
31
|
+
const targetRect = element.getBoundingClientRect();
|
|
32
|
+
const rootRect = root.getBoundingClientRect();
|
|
33
|
+
const visibleTop = rootRect.top + root.clientTop;
|
|
34
|
+
const targetTop = targetRect.top - visibleTop + root.scrollTop;
|
|
35
|
+
const targetBottom = targetRect.bottom - visibleTop + root.scrollTop;
|
|
36
|
+
let top;
|
|
37
|
+
switch (block) {
|
|
38
|
+
case "start":
|
|
39
|
+
top = targetTop - margin;
|
|
40
|
+
break;
|
|
41
|
+
case "center":
|
|
42
|
+
top = targetTop - (root.clientHeight - targetRect.height) / 2;
|
|
43
|
+
break;
|
|
44
|
+
case "end":
|
|
45
|
+
top = targetBottom - root.clientHeight + margin;
|
|
46
|
+
break;
|
|
47
|
+
case "nearest":
|
|
48
|
+
top = root.scrollTop + nearestScrollOffset({
|
|
49
|
+
targetStart: targetTop,
|
|
50
|
+
targetEnd: targetBottom,
|
|
51
|
+
viewportStart: root.scrollTop + margin,
|
|
52
|
+
viewportEnd: root.scrollTop + root.clientHeight - margin
|
|
53
|
+
});
|
|
54
|
+
break;
|
|
55
|
+
default: return block;
|
|
56
|
+
}
|
|
57
|
+
const visibleLeft = rootRect.left + root.clientLeft;
|
|
58
|
+
const left = root.scrollLeft + nearestScrollOffset({
|
|
59
|
+
targetStart: targetRect.left,
|
|
60
|
+
targetEnd: targetRect.right,
|
|
61
|
+
viewportStart: visibleLeft,
|
|
62
|
+
viewportEnd: visibleLeft + root.clientWidth
|
|
63
|
+
});
|
|
64
|
+
if (top === root.scrollTop && left === root.scrollLeft) return;
|
|
65
|
+
scrollEditorTo(root, {
|
|
66
|
+
top: Math.max(0, top),
|
|
67
|
+
left: Math.max(0, left),
|
|
68
|
+
behavior
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { getEditorScrollRoot, scrollEditorBy, scrollEditorElementIntoView, scrollEditorTo };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { findBodyPmAnchor, findBodyPmAnchors } from "../layout-bridge/dom/findBodyPmSpans.js";
|
|
2
2
|
import { findPageShellForPmPos } from "../layout-painter/renderPage.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getEditorScrollRoot, scrollEditorElementIntoView } from "./editorScrollRoot.js";
|
|
4
|
+
import { isValidPmScrollPosition } from "./scrollNavigation.js";
|
|
4
5
|
//#region src/paged-layout/scrollToPmPosition.ts
|
|
5
6
|
/** Class on the pages container `PagedEditor` paints into. */
|
|
6
7
|
const PAGES_CONTAINER_CLASS = "paged-editor__pages";
|
|
@@ -9,10 +10,7 @@ const scrollPagesToPmPosition = (pageContainer, pmPos) => {
|
|
|
9
10
|
if (!isValidPmScrollPosition(pmPos)) return;
|
|
10
11
|
const exact = findBodyPmAnchor(pageContainer, pmPos);
|
|
11
12
|
if (exact) {
|
|
12
|
-
exact
|
|
13
|
-
behavior: prefersReducedMotionBehavior(),
|
|
14
|
-
block: "center"
|
|
15
|
-
});
|
|
13
|
+
scrollEditorElementIntoView(exact);
|
|
16
14
|
return;
|
|
17
15
|
}
|
|
18
16
|
let runMatch = null;
|
|
@@ -27,28 +25,19 @@ const scrollPagesToPmPosition = (pageContainer, pmPos) => {
|
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
27
|
if (runMatch) {
|
|
30
|
-
runMatch
|
|
31
|
-
behavior: prefersReducedMotionBehavior(),
|
|
32
|
-
block: "center"
|
|
33
|
-
});
|
|
28
|
+
scrollEditorElementIntoView(runMatch);
|
|
34
29
|
return;
|
|
35
30
|
}
|
|
36
31
|
const shellHit = findPageShellForPmPos(pageContainer, pmPos);
|
|
37
32
|
if (!shellHit) return;
|
|
38
33
|
const { element: shell } = shellHit;
|
|
39
|
-
shell
|
|
40
|
-
behavior: prefersReducedMotionBehavior(),
|
|
41
|
-
block: "center"
|
|
42
|
-
});
|
|
34
|
+
scrollEditorElementIntoView(shell);
|
|
43
35
|
let attempts = 0;
|
|
44
36
|
const refine = () => {
|
|
45
37
|
attempts++;
|
|
46
38
|
const exactInShell = findBodyPmAnchor(shell, pmPos);
|
|
47
39
|
if (exactInShell) {
|
|
48
|
-
exactInShell
|
|
49
|
-
behavior: prefersReducedMotionBehavior(),
|
|
50
|
-
block: "center"
|
|
51
|
-
});
|
|
40
|
+
scrollEditorElementIntoView(exactInShell);
|
|
52
41
|
return;
|
|
53
42
|
}
|
|
54
43
|
let bestEl = null;
|
|
@@ -68,10 +57,7 @@ const scrollPagesToPmPosition = (pageContainer, pmPos) => {
|
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
59
|
if (bestEl) {
|
|
71
|
-
bestEl
|
|
72
|
-
behavior: prefersReducedMotionBehavior(),
|
|
73
|
-
block: "center"
|
|
74
|
-
});
|
|
60
|
+
scrollEditorElementIntoView(bestEl);
|
|
75
61
|
return;
|
|
76
62
|
}
|
|
77
63
|
if (attempts < 20) requestAnimationFrame(refine);
|
|
@@ -86,7 +72,7 @@ const scrollPagesToPmPosition = (pageContainer, pmPos) => {
|
|
|
86
72
|
* callers can fall back to coordinate-based scrolling.
|
|
87
73
|
*/
|
|
88
74
|
const scrollFolioPositionIntoView = (view, pmPos) => {
|
|
89
|
-
const pageContainer = view.dom
|
|
75
|
+
const pageContainer = getEditorScrollRoot(view.dom)?.querySelector(`.paged-editor__pages`) ?? null;
|
|
90
76
|
if (!pageContainer) return false;
|
|
91
77
|
scrollPagesToPmPosition(pageContainer, pmPos);
|
|
92
78
|
return true;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Command } from "prosemirror-state";
|
|
2
|
+
//#region src/prosemirror/commands/clearParagraphIndent.d.ts
|
|
3
|
+
/** Clear indentation at paragraph start before Backspace joins paragraphs. */
|
|
4
|
+
declare const clearIndentOnBackspace: Command;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { clearIndentOnBackspace };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { expectParagraphAttrs } from "../attrs/index.js";
|
|
2
|
+
import { TextSelection } from "prosemirror-state";
|
|
3
|
+
//#region src/prosemirror/commands/clearParagraphIndent.ts
|
|
4
|
+
/** Clear indentation at paragraph start before Backspace joins paragraphs. */
|
|
5
|
+
const clearIndentOnBackspace = (state, dispatch) => {
|
|
6
|
+
if (!(state.selection instanceof TextSelection)) return false;
|
|
7
|
+
const { $cursor } = state.selection;
|
|
8
|
+
if (!$cursor || $cursor.parentOffset !== 0 || $cursor.parent.type.name !== "paragraph") return false;
|
|
9
|
+
const attrs = expectParagraphAttrs($cursor.parent);
|
|
10
|
+
const hasFirstLine = typeof attrs.indentFirstLine === "number" && attrs.indentFirstLine > 0;
|
|
11
|
+
const hasIndentLeft = typeof attrs.indentLeft === "number" && attrs.indentLeft > 0;
|
|
12
|
+
if (!hasFirstLine && !attrs.hangingIndent && !hasIndentLeft) return false;
|
|
13
|
+
if (dispatch) {
|
|
14
|
+
const position = $cursor.before();
|
|
15
|
+
dispatch(state.tr.setNodeMarkup(position, void 0, {
|
|
16
|
+
...attrs,
|
|
17
|
+
indentFirstLine: null,
|
|
18
|
+
hangingIndent: null,
|
|
19
|
+
indentLeft: null
|
|
20
|
+
}).scrollIntoView());
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { clearIndentOnBackspace };
|
|
@@ -287,7 +287,12 @@ function resolveChange(from, to, mode, revisionIds) {
|
|
|
287
287
|
const cell = tr.doc.nodeAt(mappedPos);
|
|
288
288
|
if (!cell || cell.type.name !== "tableCell" && cell.type.name !== "tableHeader") continue;
|
|
289
289
|
if (op.type === "merge") {
|
|
290
|
-
const resolved = op.source === "collapsed" ? resolveCollapsedTableCellMerge(tr, mappedPos, op.mode, op.revisionSet) : resolveVisibleTableCellMerge(
|
|
290
|
+
const resolved = op.source === "collapsed" ? resolveCollapsedTableCellMerge(tr, mappedPos, op.mode, op.revisionSet) : resolveVisibleTableCellMerge({
|
|
291
|
+
tr,
|
|
292
|
+
cellPos: mappedPos,
|
|
293
|
+
mode: op.mode,
|
|
294
|
+
revisionSet: op.revisionSet
|
|
295
|
+
});
|
|
291
296
|
if (!resolved) {
|
|
292
297
|
failedTableCellMergeResolution = true;
|
|
293
298
|
break;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStyleSourcedParagraphNumbering } from "../../internal/paragraphFormattingSerialization.js";
|
|
1
2
|
import { expectParagraphAttrs } from "../attrs/index.js";
|
|
2
3
|
import { readParagraphNumberingAttr } from "../numberingAttr.js";
|
|
3
4
|
import { directParagraphAlignment } from "../paragraphAlignment.js";
|
|
@@ -126,7 +127,9 @@ const paragraphPropertiesSnapshot = (node) => {
|
|
|
126
127
|
const attrs = expectParagraphAttrs(node);
|
|
127
128
|
const snapshot = {};
|
|
128
129
|
for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) {
|
|
129
|
-
if (key === "alignment" || PPR_SPACING_ATTR_KEYS.has(key)) continue;
|
|
130
|
+
if (key === "alignment" || PPR_SPACING_ATTR_KEYS.has(key) || key === "numPr" && isStyleSourcedParagraphNumbering(attrs.numPr, attrs.numPrFromStyle)) continue;
|
|
131
|
+
if (styleSuppliedParagraphValue(attrs, key)) continue;
|
|
132
|
+
if (key === "hangingIndent" && styleSuppliedParagraphValue(attrs, "indentFirstLine")) continue;
|
|
130
133
|
if (styleSuppliedParagraphValue(attrs, key)) continue;
|
|
131
134
|
if (key === "hangingIndent" && styleSuppliedParagraphValue(attrs, "indentFirstLine")) continue;
|
|
132
135
|
const value = attrs[key];
|
|
@@ -5,7 +5,8 @@ import { nodePropertyRevisionSites, propertyRevisionRecords } from "../revisionC
|
|
|
5
5
|
import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
|
|
6
6
|
import { isTableCellRetainedInReviewView } from "../tableCellRevisionVisibility.js";
|
|
7
7
|
import { resolveAllNodePropertyChangeAttrs } from "./resolveNodePropertyChangeAttrs.js";
|
|
8
|
-
import {
|
|
8
|
+
import { createRestoredTableCell, hasMatchingCollapsedTableCellMerge, tableCellContinuationCells, tableCellContinuationFromNode, tableCellContinuationPayload } from "./tableCellMergeResolution.js";
|
|
9
|
+
import { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions } from "./tableMergeFoldDecisions.js";
|
|
9
10
|
import { panic } from "better-result";
|
|
10
11
|
import { TableMap } from "prosemirror-tables";
|
|
11
12
|
import { StepMap } from "prosemirror-transform";
|
|
@@ -358,6 +359,12 @@ const compactColumnWidths = (widths, removals) => {
|
|
|
358
359
|
/** Resolve all cell edits in reverse order against one grid and mutable slots. */
|
|
359
360
|
const resolvePureTableMerges = ({ table, mode, rowMetadata, tablePropertyTouched, sourceTable }) => {
|
|
360
361
|
const map = TableMap.get(table);
|
|
362
|
+
const mergeDecisions = tableMergeFoldDecisions({
|
|
363
|
+
table,
|
|
364
|
+
mode,
|
|
365
|
+
revisionSet: null
|
|
366
|
+
});
|
|
367
|
+
if (mergeDecisions.invalid.size > 0) return failedTableResolution(table);
|
|
361
368
|
const cellRectangle = indexedTableCells(map);
|
|
362
369
|
const rows = Array.from({ length: table.childCount }, (_, index) => table.child(index));
|
|
363
370
|
const rowCells = rows.map(() => /* @__PURE__ */ new Map());
|
|
@@ -383,7 +390,7 @@ const resolvePureTableMerges = ({ table, mode, rowMetadata, tablePropertyTouched
|
|
|
383
390
|
for (let coveredRow = rectangle.top; coveredRow < rectangle.bottom; coveredRow++) rowOccupancy[coveredRow] = elementAt(rowOccupancy, coveredRow) + (rectangle.right - rectangle.left);
|
|
384
391
|
const initial = elementAt(elementAt(rowMetadata, rowIndex).cells, cellIndex);
|
|
385
392
|
const entry = {
|
|
386
|
-
node,
|
|
393
|
+
node: mergeDecisions.origins.has(cellOffset) ? node.type.create(resolvedTableMergeOriginAttrs(node), node.content, node.marks) : node,
|
|
387
394
|
position: cellOffset,
|
|
388
395
|
left,
|
|
389
396
|
row: rowIndex,
|
|
@@ -438,28 +445,15 @@ const resolvePureTableMerges = ({ table, mode, rowMetadata, tablePropertyTouched
|
|
|
438
445
|
});
|
|
439
446
|
}
|
|
440
447
|
} else if (marker?.kind === "merge") {
|
|
441
|
-
const joinsAbove =
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
const original = entry.node.attrs["_originalFormatting"];
|
|
451
|
-
let formatting = original;
|
|
452
|
-
if (typeof original === "object" && original !== null) {
|
|
453
|
-
const copy = { ...original };
|
|
454
|
-
delete copy["vMerge"];
|
|
455
|
-
formatting = copy;
|
|
456
|
-
}
|
|
457
|
-
entry.node = entry.node.type.create({
|
|
458
|
-
...entry.node.attrs,
|
|
459
|
-
cellMarker: null,
|
|
460
|
-
_originalFormatting: formatting
|
|
461
|
-
}, entry.node.content, entry.node.marks);
|
|
462
|
-
} else {
|
|
448
|
+
const joinsAbove = mergeDecisions.folds.get(entry.position);
|
|
449
|
+
if (joinsAbove === void 0) return panic("Merge resolution lost its cell");
|
|
450
|
+
if (mode === "reject" && isTableCellMergeRevisionContinuation(marker.verticalMergeOriginal) && cellRectangle(entry.position).top === 0) return failedTableResolution(table);
|
|
451
|
+
if (!joinsAbove) entry.node = entry.node.type.create(resolvedVisibleTableCellMergeAttrs({
|
|
452
|
+
cell: entry.node,
|
|
453
|
+
mode,
|
|
454
|
+
origin: mergeDecisions.origins.has(entry.position)
|
|
455
|
+
}), entry.node.content, entry.node.marks);
|
|
456
|
+
else {
|
|
463
457
|
const rectangle = cellRectangle(entry.position);
|
|
464
458
|
if (rectangle.top === 0) return failedTableResolution(table);
|
|
465
459
|
const abovePosition = map.map[(rectangle.top - 1) * map.width + rectangle.left];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { resolveParagraphNumbering } from "../../docx/numberingReference.js";
|
|
1
2
|
import { expectParagraphAttrs } from "../attrs/index.js";
|
|
2
3
|
import { rejectedListRenderingPatch } from "../listRendering.js";
|
|
4
|
+
import { paragraphNumberingAttr } from "../numberingAttr.js";
|
|
3
5
|
import { styleResolvedParagraphFormatting } from "../paragraphFormattingProvenance.js";
|
|
4
6
|
import { resolveParagraphDefaultTextFormatting } from "../styles/paragraphStyleCascade.js";
|
|
5
7
|
import { paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, removeParagraphPropertyChanges, sectionRejectProperties } from "./propertyChangeScope.js";
|
|
@@ -38,11 +40,20 @@ const resolveParagraphChangeAttrs = ({ node, boundaryCovered, mode, revisionSet,
|
|
|
38
40
|
...previousFormattingFromStyle,
|
|
39
41
|
...resolvedFromStyle
|
|
40
42
|
};
|
|
43
|
+
let styleNumbering = previousFormattingFromStyle?.numPr;
|
|
44
|
+
if (!styleResolver && sameStyle) styleNumbering = currentAttrs.numPrFromStyle ?? void 0;
|
|
45
|
+
const inheritedNumbering = styleNumbering == null ? null : paragraphNumberingAttr(resolveParagraphNumbering(styleNumbering));
|
|
46
|
+
const recordedNumbering = rejection.previousFormatting?.numPr;
|
|
47
|
+
const restoredNumbering = recordedNumbering ?? inheritedNumbering;
|
|
41
48
|
Object.assign(nextAttrs, paragraphRejectAttrPatch(rejection.previousFormatting, inheritedFormatting), rejectedListRenderingPatch({
|
|
42
49
|
current: expectParagraphAttrs(node),
|
|
43
50
|
previousFormatting: rejection.previousFormatting,
|
|
44
|
-
numbering
|
|
45
|
-
|
|
51
|
+
numbering,
|
|
52
|
+
restoredNumbering
|
|
53
|
+
}), {
|
|
54
|
+
numPr: restoredNumbering,
|
|
55
|
+
numPrFromStyle: recordedNumbering == null ? inheritedNumbering : null
|
|
56
|
+
});
|
|
46
57
|
const restoredFormatting = paragraphRejectOriginalFormatting(rejection.previousFormatting, node.attrs["_originalFormatting"]);
|
|
47
58
|
nextAttrs["_originalFormatting"] = restoredFormatting;
|
|
48
59
|
if (styleResolver) nextAttrs["defaultTextFormatting"] = resolveParagraphDefaultTextFormatting(rejection.previousFormatting?.styleId, restoredFormatting ?? void 0, styleResolver) ?? null;
|