@stll/folio-core 0.54.0 → 0.54.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +25 -0
- package/dist/docx/paragraphPropertySource.js +1 -1
- package/dist/docx/verticalMergeProjection.d.ts +17 -0
- package/dist/docx/verticalMergeProjection.js +120 -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/conversion/toProseDoc.d.ts +1 -6
- package/dist/prosemirror/conversion/toProseDoc.js +2 -117
- 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/suggestionMode.js +1 -5
- 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/package.json +3 -3
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1486,6 +1486,13 @@ const finalParagraphPredecessor = (doc, at) => {
|
|
|
1486
1486
|
for (let index = $emptied.index() - 1; index >= 0; index--) {
|
|
1487
1487
|
const node = $emptied.parent.child(index);
|
|
1488
1488
|
position -= node.nodeSize;
|
|
1489
|
+
if (node.type.spec["tableRole"] === "table" && node.childCount > 0) {
|
|
1490
|
+
let allRowsDeleted = true;
|
|
1491
|
+
node.forEach((row) => {
|
|
1492
|
+
allRowsDeleted &&= expectTableRowAttrs(row).trDel != null;
|
|
1493
|
+
});
|
|
1494
|
+
if (allRowsDeleted) continue;
|
|
1495
|
+
}
|
|
1489
1496
|
if (node.type !== emptied.type) return null;
|
|
1490
1497
|
if (isPlainDeletedPPrMark(node.attrs["pPrMark"]) && holdsOnlyDeletedContent(node)) continue;
|
|
1491
1498
|
return {
|
|
@@ -1701,6 +1708,24 @@ const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, autho
|
|
|
1701
1708
|
};
|
|
1702
1709
|
collectSupersededDeletedBreaks(tr.doc, 0);
|
|
1703
1710
|
for (const { preceding, inserted, deletedMark, precedingMark } of supersededDeletedBreaks) {
|
|
1711
|
+
const insertionRevisionId = addedBreakRevisionId(precedingMark);
|
|
1712
|
+
if (insertionRevisionId !== null) {
|
|
1713
|
+
const previous = tr.doc.nodeAt(preceding);
|
|
1714
|
+
const current = tr.doc.nodeAt(inserted);
|
|
1715
|
+
if (!previous || !current) panic("A shifted paragraph break lost its carrier");
|
|
1716
|
+
const previousFormatting = paragraphPropertiesOnReject(previous);
|
|
1717
|
+
const existing = expectParagraphAttrs(current)._propertyChanges;
|
|
1718
|
+
if (!hasSerializableParagraphPropertyChange(existing) && JSON.stringify(previousFormatting) !== JSON.stringify(paragraphPropertiesSnapshot(current))) tr.setNodeAttribute(inserted, "_propertyChanges", [...Array.isArray(existing) ? existing : [], {
|
|
1719
|
+
type: "paragraphPropertyChange",
|
|
1720
|
+
info: {
|
|
1721
|
+
id: insertionRevisionId,
|
|
1722
|
+
author,
|
|
1723
|
+
date,
|
|
1724
|
+
...initials ? { initials } : {}
|
|
1725
|
+
},
|
|
1726
|
+
previousFormatting
|
|
1727
|
+
}]);
|
|
1728
|
+
}
|
|
1704
1729
|
tr.setNodeAttribute(preceding, "pPrMark", precedingMark);
|
|
1705
1730
|
tr.setNodeAttribute(inserted, "pPrMark", deletedMark);
|
|
1706
1731
|
}
|
|
@@ -542,7 +542,7 @@ const tableCellParagraphPropertySourceBindingForTransport = (paragraph, inspecti
|
|
|
542
542
|
};
|
|
543
543
|
/** Prepare opaque continuation cells for ProseMirror and Yjs transport. */
|
|
544
544
|
const transportTableCellsWithParagraphPropertySources = (cells) => {
|
|
545
|
-
const cloned = structuredClone(
|
|
545
|
+
const cloned = cells.map((cell) => structuredClone(cell));
|
|
546
546
|
const sources = paragraphsInTableCells(cells);
|
|
547
547
|
const targets = paragraphsInTableCells(cloned);
|
|
548
548
|
if (sources.length !== targets.length) panic("The cloned table cells changed paragraph graph ownership.");
|
|
@@ -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 };
|
|
@@ -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;
|
|
@@ -5,17 +5,16 @@ import { Transaction } from "prosemirror-state";
|
|
|
5
5
|
//#region src/prosemirror/commands/tableCellMergeResolution.d.ts
|
|
6
6
|
declare const tableCellContinuationPayload: (node: Node) => DecodedTableCellParagraphSourcePayload | null;
|
|
7
7
|
declare const hasMatchingCollapsedTableCellMerge: (node: Node, revisionSet: Set<number> | null) => boolean;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
declare const
|
|
15
|
-
declare const resolveVisibleTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject") => boolean;
|
|
8
|
+
type ResolveVisibleTableCellMergeOptions = {
|
|
9
|
+
tr: Transaction;
|
|
10
|
+
cellPos: number;
|
|
11
|
+
mode: "accept" | "reject";
|
|
12
|
+
revisionSet: ReadonlySet<number> | null;
|
|
13
|
+
};
|
|
14
|
+
declare const resolveVisibleTableCellMerge: ({ tr, cellPos, mode, revisionSet }: ResolveVisibleTableCellMergeOptions) => boolean;
|
|
16
15
|
declare const tableCellContinuationCells: (cell: Node, rowspan: number) => document_d_exports.TableCell[];
|
|
17
16
|
declare const tableCellContinuationFromNode: (cell: Node) => document_d_exports.TableCell;
|
|
18
17
|
declare const resolveCollapsedTableCellMerge: (tr: Transaction, cellPos: number, mode: "accept" | "reject", revisionSet: Set<number> | null) => boolean;
|
|
19
18
|
declare const createRestoredTableCell: (origin: Node, source: document_d_exports.TableCell) => Node | null;
|
|
20
19
|
//#endregion
|
|
21
|
-
export {
|
|
20
|
+
export { createRestoredTableCell, hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge, tableCellContinuationCells, tableCellContinuationFromNode, tableCellContinuationPayload };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { decodeTableCellParagraphSourcePayload, restoreTableCellsWithParagraphPropertySources, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
|
|
2
2
|
import { isTableCellMergeRevisionContinuation } from "../../docx/tableParser.js";
|
|
3
3
|
import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
|
|
4
|
-
import { standaloneTableCellToProseMirror
|
|
4
|
+
import { standaloneTableCellToProseMirror } from "../conversion/toProseDoc.js";
|
|
5
5
|
import { getTableCellMergeChange } from "../tableCellMergeRevision.js";
|
|
6
6
|
import { removeRowsWithoutCellsInRange } from "../tableGridMutation.js";
|
|
7
|
+
import { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions } from "./tableMergeFoldDecisions.js";
|
|
7
8
|
import { Result, panic } from "better-result";
|
|
8
9
|
import { TableMap } from "prosemirror-tables";
|
|
9
10
|
//#region src/prosemirror/commands/tableCellMergeResolution.ts
|
|
@@ -37,33 +38,35 @@ const tableCellContext = (doc, cellPos) => {
|
|
|
37
38
|
}
|
|
38
39
|
return null;
|
|
39
40
|
};
|
|
40
|
-
|
|
41
|
-
* Whether accepting a pending vertical merge folds the cell into the one above.
|
|
42
|
-
* The saved `w:vMerge` continuation is read back as its own cell when it carries
|
|
43
|
-
* content, so accepting such a merge keeps the cell standing; folding it would
|
|
44
|
-
* hide text that reappears when the document is reopened.
|
|
45
|
-
*/
|
|
46
|
-
const acceptedMergeFoldsIntoCellAbove = (cell) => !tableCellHasMeaningfulContent(standaloneTableCellFromProseMirror(cell));
|
|
47
|
-
const resolveVisibleTableCellMerge = (tr, cellPos, mode) => {
|
|
41
|
+
const resolveVisibleTableCellMerge = ({ tr, cellPos, mode, revisionSet }) => {
|
|
48
42
|
const cell = tr.doc.nodeAt(cellPos);
|
|
49
43
|
const marker = cell?.attrs["cellMarker"];
|
|
50
44
|
if (!cell || !isTableCellMergeRevisionAttr(marker)) return false;
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
nextOriginalFormatting = { ...originalFormatting };
|
|
60
|
-
delete nextOriginalFormatting.vMerge;
|
|
61
|
-
}
|
|
62
|
-
tr.setNodeMarkup(cellPos, void 0, {
|
|
63
|
-
...cell.attrs,
|
|
64
|
-
cellMarker: null,
|
|
65
|
-
_originalFormatting: nextOriginalFormatting
|
|
45
|
+
const context = tableCellContext(tr.doc, cellPos);
|
|
46
|
+
if (!context) return false;
|
|
47
|
+
const rectangle = TableMap.get(context.table).findCell(context.relativeCellPos);
|
|
48
|
+
if (mode === "reject" && isTableCellMergeRevisionContinuation(marker.verticalMergeOriginal) && rectangle.top === 0) return false;
|
|
49
|
+
const decisions = tableMergeFoldDecisions({
|
|
50
|
+
table: context.table,
|
|
51
|
+
mode,
|
|
52
|
+
revisionSet
|
|
66
53
|
});
|
|
54
|
+
if (decisions.invalid.has(context.relativeCellPos)) return false;
|
|
55
|
+
const folds = decisions.folds.get(context.relativeCellPos);
|
|
56
|
+
if (folds === void 0) return panic("Merge resolution lost its cell");
|
|
57
|
+
for (const origin of decisions.origins) {
|
|
58
|
+
if (origin === context.relativeCellPos) continue;
|
|
59
|
+
const originPos = context.tableStart + origin;
|
|
60
|
+
const originCell = tr.doc.nodeAt(originPos);
|
|
61
|
+
if (!originCell) return panic("Merge resolution lost its origin");
|
|
62
|
+
tr.setNodeMarkup(originPos, void 0, resolvedTableMergeOriginAttrs(originCell));
|
|
63
|
+
}
|
|
64
|
+
if (folds) return mergeTableCellWithCellAbove(tr, cellPos);
|
|
65
|
+
tr.setNodeMarkup(cellPos, void 0, resolvedVisibleTableCellMergeAttrs({
|
|
66
|
+
cell,
|
|
67
|
+
mode,
|
|
68
|
+
origin: decisions.origins.has(context.relativeCellPos)
|
|
69
|
+
}));
|
|
67
70
|
return true;
|
|
68
71
|
};
|
|
69
72
|
const mergeTableCellWithCellAbove = (tr, cellPos) => {
|
|
@@ -201,4 +204,4 @@ const createRestoredTableCell = (origin, source) => {
|
|
|
201
204
|
return Result.try(() => origin.type.schema.nodeFromJSON(restored.toJSON())).unwrapOr(null);
|
|
202
205
|
};
|
|
203
206
|
//#endregion
|
|
204
|
-
export {
|
|
207
|
+
export { createRestoredTableCell, hasMatchingCollapsedTableCellMerge, resolveCollapsedTableCellMerge, resolveVisibleTableCellMerge, tableCellContinuationCells, tableCellContinuationFromNode, tableCellContinuationPayload };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/prosemirror/commands/tableMergeFoldDecisions.d.ts
|
|
3
|
+
type TableMergeFoldDecisionsOptions = {
|
|
4
|
+
table: Node;
|
|
5
|
+
mode: "accept" | "reject";
|
|
6
|
+
revisionSet: ReadonlySet<number> | null;
|
|
7
|
+
};
|
|
8
|
+
/** Resolve raw merge states without converting unrelated table revisions. */
|
|
9
|
+
declare const tableMergeFoldDecisions: ({ table, mode, revisionSet }: TableMergeFoldDecisionsOptions) => {
|
|
10
|
+
folds: Map<number, boolean>;
|
|
11
|
+
origins: Set<number>;
|
|
12
|
+
invalid: Set<number>;
|
|
13
|
+
};
|
|
14
|
+
/** Persist an implicit merge origin even when reader rules retain its continuation. */
|
|
15
|
+
declare const resolvedTableMergeOriginAttrs: (cell: Node) => {
|
|
16
|
+
_originalFormatting: {
|
|
17
|
+
vMerge: string;
|
|
18
|
+
width?: import("@stll/docx-core/model").TableMeasurement;
|
|
19
|
+
borders?: import("@stll/docx-core/model").TableCellBorders;
|
|
20
|
+
margins?: import("@stll/docx-core/model").CellMargins;
|
|
21
|
+
shading?: import("@stll/docx-core/model").ShadingProperties;
|
|
22
|
+
verticalAlign?: "top" | "center" | "bottom";
|
|
23
|
+
textDirection?: import("@stll/docx-core/model").TextDirection;
|
|
24
|
+
gridSpan?: number;
|
|
25
|
+
fitText?: boolean;
|
|
26
|
+
noWrap?: boolean;
|
|
27
|
+
hideMark?: boolean;
|
|
28
|
+
conditionalFormat?: import("@stll/docx-core/model").ConditionalFormatStyle;
|
|
29
|
+
preserved?: import("@stll/docx-core/model").PreservedMarkup;
|
|
30
|
+
sourceXml?: string;
|
|
31
|
+
};
|
|
32
|
+
_preserveVMergeRestart: boolean;
|
|
33
|
+
};
|
|
34
|
+
type ResolvedVisibleTableCellMergeOptions = {
|
|
35
|
+
cell: Node;
|
|
36
|
+
mode: "accept" | "reject";
|
|
37
|
+
origin: boolean;
|
|
38
|
+
};
|
|
39
|
+
/** A retained continuation must save the merge state the reviewer resolved. */
|
|
40
|
+
declare const resolvedVisibleTableCellMergeAttrs: ({ cell, mode, origin }: ResolvedVisibleTableCellMergeOptions) => {
|
|
41
|
+
cellMarker: null;
|
|
42
|
+
_originalFormatting: {
|
|
43
|
+
width?: import("@stll/docx-core/model").TableMeasurement;
|
|
44
|
+
borders?: import("@stll/docx-core/model").TableCellBorders;
|
|
45
|
+
margins?: import("@stll/docx-core/model").CellMargins;
|
|
46
|
+
shading?: import("@stll/docx-core/model").ShadingProperties;
|
|
47
|
+
verticalAlign?: "top" | "center" | "bottom";
|
|
48
|
+
textDirection?: import("@stll/docx-core/model").TextDirection;
|
|
49
|
+
gridSpan?: number;
|
|
50
|
+
vMerge?: "restart" | "continue";
|
|
51
|
+
fitText?: boolean;
|
|
52
|
+
noWrap?: boolean;
|
|
53
|
+
hideMark?: boolean;
|
|
54
|
+
conditionalFormat?: import("@stll/docx-core/model").ConditionalFormatStyle;
|
|
55
|
+
preserved?: import("@stll/docx-core/model").PreservedMarkup;
|
|
56
|
+
sourceXml?: string;
|
|
57
|
+
};
|
|
58
|
+
_preserveVMergeRestart: boolean;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isCellMergeContinuation, isCellMergeStart, isTableCellMergeRevisionContinuation } from "../../docx/tableParser.js";
|
|
2
|
+
import { tableCellHasMeaningfulContent } from "../../docx/verticalMergeProjection.js";
|
|
3
|
+
import { expectTableCellAttrs } from "../attrs/index.js";
|
|
4
|
+
import { standaloneTableCellFromProseMirror } from "../conversion/fromProseDoc.js";
|
|
5
|
+
import { panic } from "better-result";
|
|
6
|
+
import { TableMap } from "prosemirror-tables";
|
|
7
|
+
//#region src/prosemirror/commands/tableMergeFoldDecisions.ts
|
|
8
|
+
/** Resolve raw merge states without converting unrelated table revisions. */
|
|
9
|
+
const tableMergeFoldDecisions = ({ table, mode, revisionSet }) => {
|
|
10
|
+
const decisions = /* @__PURE__ */ new Map();
|
|
11
|
+
const origins = /* @__PURE__ */ new Set();
|
|
12
|
+
const invalid = /* @__PURE__ */ new Set();
|
|
13
|
+
const map = TableMap.get(table);
|
|
14
|
+
const visited = /* @__PURE__ */ new Set();
|
|
15
|
+
for (const [index, position] of map.map.entries()) {
|
|
16
|
+
if (visited.has(position)) continue;
|
|
17
|
+
visited.add(position);
|
|
18
|
+
const cell = table.nodeAt(position);
|
|
19
|
+
if (cell?.attrs["cellMarker"]?.kind !== "merge") continue;
|
|
20
|
+
const marker = expectTableCellAttrs(cell).cellMarker;
|
|
21
|
+
if (marker?.kind !== "merge" || revisionSet !== null && !revisionSet.has(marker.info.revisionId)) continue;
|
|
22
|
+
const continues = isTableCellMergeRevisionContinuation(mode === "accept" ? marker.verticalMerge : marker.verticalMergeOriginal);
|
|
23
|
+
decisions.set(position, continues && (mode === "reject" || !tableCellHasMeaningfulContent(standaloneTableCellFromProseMirror(cell))));
|
|
24
|
+
if (!continues) continue;
|
|
25
|
+
if (Math.floor(index / map.width) === 0) {
|
|
26
|
+
invalid.add(position);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const abovePosition = map.map[index - map.width];
|
|
30
|
+
if (abovePosition === void 0) return panic("Merge resolution lost its origin");
|
|
31
|
+
const above = table.nodeAt(abovePosition);
|
|
32
|
+
if (!above) return panic("Merge resolution lost its origin");
|
|
33
|
+
const rectangle = map.findCell(position);
|
|
34
|
+
const aboveRectangle = map.findCell(abovePosition);
|
|
35
|
+
if (rectangle.left !== aboveRectangle.left || rectangle.right !== aboveRectangle.right || rectangle.top !== aboveRectangle.bottom) {
|
|
36
|
+
invalid.add(position);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const aboveMarker = expectTableCellAttrs(above).cellMarker;
|
|
40
|
+
const aboveContinues = aboveMarker?.kind === "merge" && (revisionSet === null || revisionSet.has(aboveMarker.info.revisionId)) && isTableCellMergeRevisionContinuation(mode === "accept" ? aboveMarker.verticalMerge : aboveMarker.verticalMergeOriginal);
|
|
41
|
+
const aboveModel = standaloneTableCellFromProseMirror(above);
|
|
42
|
+
if (mode === "accept" && (aboveContinues && decisions.get(abovePosition) === false || isCellMergeContinuation(aboveModel) && tableCellHasMeaningfulContent(aboveModel))) decisions.set(position, false);
|
|
43
|
+
if (aboveContinues || isCellMergeContinuation(aboveModel) || isCellMergeStart(aboveModel)) continue;
|
|
44
|
+
origins.add(abovePosition);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
folds: decisions,
|
|
48
|
+
origins,
|
|
49
|
+
invalid
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
/** Persist an implicit merge origin even when reader rules retain its continuation. */
|
|
53
|
+
const resolvedTableMergeOriginAttrs = (cell) => ({
|
|
54
|
+
...cell.attrs,
|
|
55
|
+
_originalFormatting: {
|
|
56
|
+
...expectTableCellAttrs(cell)._originalFormatting,
|
|
57
|
+
vMerge: "restart"
|
|
58
|
+
},
|
|
59
|
+
_preserveVMergeRestart: true
|
|
60
|
+
});
|
|
61
|
+
/** A retained continuation must save the merge state the reviewer resolved. */
|
|
62
|
+
const resolvedVisibleTableCellMergeAttrs = ({ cell, mode, origin }) => {
|
|
63
|
+
const attrs = expectTableCellAttrs(cell);
|
|
64
|
+
const marker = attrs.cellMarker;
|
|
65
|
+
if (marker?.kind !== "merge") return panic("Cell has no merge revision");
|
|
66
|
+
const continuation = mode === "accept" ? marker.verticalMerge : marker.verticalMergeOriginal;
|
|
67
|
+
const formatting = { ...attrs._originalFormatting };
|
|
68
|
+
if (origin) formatting.vMerge = "restart";
|
|
69
|
+
else if (isTableCellMergeRevisionContinuation(continuation)) formatting.vMerge = "continue";
|
|
70
|
+
else delete formatting.vMerge;
|
|
71
|
+
return {
|
|
72
|
+
...cell.attrs,
|
|
73
|
+
cellMarker: null,
|
|
74
|
+
_originalFormatting: formatting,
|
|
75
|
+
_preserveVMergeRestart: origin
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
export { resolvedTableMergeOriginAttrs, resolvedVisibleTableCellMergeAttrs, tableMergeFoldDecisions };
|
|
@@ -23,11 +23,6 @@ type ToProseDocOptions = {
|
|
|
23
23
|
* @param options - Conversion options including style definitions
|
|
24
24
|
*/
|
|
25
25
|
declare function toProseDoc(document: document_d_exports.Document, options?: ToProseDocOptions): Node;
|
|
26
|
-
/**
|
|
27
|
-
* Whether a cell carries content a vertical-merge continuation must not hide.
|
|
28
|
-
* The reader keeps such a continuation as a cell of its own.
|
|
29
|
-
*/
|
|
30
|
-
declare function tableCellHasMeaningfulContent(cell: document_d_exports.TableCell): boolean;
|
|
31
26
|
declare function standaloneTableCellToProseMirror(cell: document_d_exports.TableCell, nodeType: "tableCell" | "tableHeader"): Node;
|
|
32
27
|
/**
|
|
33
28
|
* Convert HeaderFooter content (array of Paragraph/Table blocks) to a ProseMirror document.
|
|
@@ -44,4 +39,4 @@ declare function footnoteToProseDoc(content: document_d_exports.BlockContent[],
|
|
|
44
39
|
*/
|
|
45
40
|
declare function createEmptyDoc(): Node;
|
|
46
41
|
//#endregion
|
|
47
|
-
export { ToProseDocOptions, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror,
|
|
42
|
+
export { ToProseDocOptions, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror, toProseDoc };
|
|
@@ -6,6 +6,7 @@ import { isBaselineVertAlign } from "../../docx/runParser.js";
|
|
|
6
6
|
import { replayableShapeAlternateContent } from "../../docx/shapeAlternateContent.js";
|
|
7
7
|
import { resolveTableLook } from "../../docx/tableLook.js";
|
|
8
8
|
import { isCellMergeContinuation } from "../../docx/tableParser.js";
|
|
9
|
+
import { calculateRowSpans } from "../../docx/verticalMergeProjection.js";
|
|
9
10
|
import { copiedWrapPolygon } from "../../docx/wrapPolygon.js";
|
|
10
11
|
import { buildPageBreakRunSourceDescendantIndex } from "../../internal/pageBreakRunSourceDescendantIndex.js";
|
|
11
12
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
@@ -893,122 +894,6 @@ function resolveTextFormatting(formatting, styleResolver) {
|
|
|
893
894
|
if (!styleResolver) return formatting;
|
|
894
895
|
return mergeTextFormatting(styleResolver.resolveRunStyle(formatting.styleId), formatting);
|
|
895
896
|
}
|
|
896
|
-
function calculateRowSpans(table) {
|
|
897
|
-
const result = /* @__PURE__ */ new Map();
|
|
898
|
-
const numRows = table.rows.length;
|
|
899
|
-
const activeMerges = /* @__PURE__ */ new Map();
|
|
900
|
-
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
|
|
901
|
-
const row = table.rows[rowIndex];
|
|
902
|
-
if (row.cells.length === 0) {
|
|
903
|
-
clearActiveVerticalMerges(activeMerges, result);
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
let colIndex = row.formatting?.gridBefore ?? 0;
|
|
907
|
-
const rowCells = row.cells.map((cell) => {
|
|
908
|
-
const colspan = cell.formatting?.gridSpan ?? 1;
|
|
909
|
-
const vMerge = cell.formatting?.vMerge;
|
|
910
|
-
const isMergeContinuation = isCellMergeContinuation(cell);
|
|
911
|
-
const startRow = isMergeContinuation ? activeMerges.get(colIndex) : void 0;
|
|
912
|
-
const info = {
|
|
913
|
-
cell,
|
|
914
|
-
colIndex,
|
|
915
|
-
colspan,
|
|
916
|
-
vMerge,
|
|
917
|
-
isMergeContinuation,
|
|
918
|
-
startRow,
|
|
919
|
-
hasMeaningfulContent: tableCellHasMeaningfulContent(cell),
|
|
920
|
-
shouldSkip: isMergeContinuation && startRow !== void 0
|
|
921
|
-
};
|
|
922
|
-
colIndex += colspan;
|
|
923
|
-
return info;
|
|
924
|
-
});
|
|
925
|
-
const rowWouldBeEmpty = rowCells.length > 0 && rowCells.every((cell) => cell.shouldSkip);
|
|
926
|
-
for (const cellInfo of rowCells) {
|
|
927
|
-
const { colIndex: cellColIndex, vMerge, isMergeContinuation, startRow, hasMeaningfulContent } = cellInfo;
|
|
928
|
-
const key = `${rowIndex}-${cellColIndex}`;
|
|
929
|
-
if (vMerge === "restart") {
|
|
930
|
-
closeVerticalMerge(activeMerges, result, cellColIndex);
|
|
931
|
-
activeMerges.set(cellColIndex, rowIndex);
|
|
932
|
-
result.set(key, {
|
|
933
|
-
rowSpan: 1,
|
|
934
|
-
skip: false
|
|
935
|
-
});
|
|
936
|
-
} else if (isMergeContinuation) {
|
|
937
|
-
if (startRow === void 0 || rowWouldBeEmpty || hasMeaningfulContent) {
|
|
938
|
-
result.set(key, {
|
|
939
|
-
rowSpan: 1,
|
|
940
|
-
skip: false
|
|
941
|
-
});
|
|
942
|
-
if ((rowWouldBeEmpty || hasMeaningfulContent) && startRow !== void 0) {
|
|
943
|
-
const restartCell = result.get(`${startRow}-${cellColIndex}`);
|
|
944
|
-
if (restartCell) restartCell.preserveVMergeRestart = true;
|
|
945
|
-
activeMerges.delete(cellColIndex);
|
|
946
|
-
}
|
|
947
|
-
continue;
|
|
948
|
-
}
|
|
949
|
-
const startKey = `${startRow}-${cellColIndex}`;
|
|
950
|
-
const startCell = result.get(startKey);
|
|
951
|
-
if (startCell) {
|
|
952
|
-
startCell.rowSpan++;
|
|
953
|
-
startCell.continuationCells ??= [];
|
|
954
|
-
startCell.continuationCells.push(cellInfo.cell);
|
|
955
|
-
}
|
|
956
|
-
result.set(key, {
|
|
957
|
-
rowSpan: 1,
|
|
958
|
-
skip: true
|
|
959
|
-
});
|
|
960
|
-
} else {
|
|
961
|
-
closeVerticalMerge(activeMerges, result, cellColIndex);
|
|
962
|
-
result.set(key, {
|
|
963
|
-
rowSpan: 1,
|
|
964
|
-
skip: false
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
clearActiveVerticalMerges(activeMerges, result);
|
|
970
|
-
return result;
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* End the vertical merge active in one column.
|
|
974
|
-
*
|
|
975
|
-
* A `w:vMerge="restart"` is carried through the editor by its cell's rowspan,
|
|
976
|
-
* which only exists once a continuation joins it. A merge that closes with a
|
|
977
|
-
* rowspan of 1 — a restart with no continuation, one interrupted by a plain
|
|
978
|
-
* cell, one whose continuation is in a row a revision removed — has nothing
|
|
979
|
-
* but this flag to say the cell was a merge origin, and dropping it changes
|
|
980
|
-
* the table's visible structure.
|
|
981
|
-
*/
|
|
982
|
-
function closeVerticalMerge(activeMerges, result, colIndex) {
|
|
983
|
-
const startRow = activeMerges.get(colIndex);
|
|
984
|
-
if (startRow === void 0) return;
|
|
985
|
-
const restartCell = result.get(`${startRow}-${colIndex}`);
|
|
986
|
-
if (restartCell && restartCell.rowSpan === 1) restartCell.preserveVMergeRestart = true;
|
|
987
|
-
activeMerges.delete(colIndex);
|
|
988
|
-
}
|
|
989
|
-
function clearActiveVerticalMerges(activeMerges, result) {
|
|
990
|
-
for (const colIndex of [...activeMerges.keys()]) closeVerticalMerge(activeMerges, result, colIndex);
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Whether a cell carries content a vertical-merge continuation must not hide.
|
|
994
|
-
* The reader keeps such a continuation as a cell of its own.
|
|
995
|
-
*/
|
|
996
|
-
function tableCellHasMeaningfulContent(cell) {
|
|
997
|
-
return cell.content.some(blockHasMeaningfulContent);
|
|
998
|
-
}
|
|
999
|
-
function blockHasMeaningfulContent(block) {
|
|
1000
|
-
if (block.type === "preservedBlock") return true;
|
|
1001
|
-
if (block.type === "bookmarkStart" || block.type === "bookmarkEnd") return true;
|
|
1002
|
-
if (block.type === "table") return block.rows.some((row) => row.cells.some((cell) => tableCellHasMeaningfulContent(cell)));
|
|
1003
|
-
if (block.type === "blockSdt" || block.type === "blockCustomXml") return true;
|
|
1004
|
-
return block.content.some(paragraphContentHasMeaningfulContent);
|
|
1005
|
-
}
|
|
1006
|
-
function paragraphContentHasMeaningfulContent(content) {
|
|
1007
|
-
if (content.type === "run") return content.content.length > 0;
|
|
1008
|
-
if (content.type === "hyperlink") return content.children.some(paragraphContentHasMeaningfulContent);
|
|
1009
|
-
if (content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") return content.content.some(paragraphContentHasMeaningfulContent);
|
|
1010
|
-
return true;
|
|
1011
|
-
}
|
|
1012
897
|
function convertTable(table, styleResolver, context) {
|
|
1013
898
|
for (const row of table.rows) for (const cell of row.cells) {
|
|
1014
899
|
if (isCellMergeContinuation(cell) && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) {
|
|
@@ -2840,4 +2725,4 @@ function createEmptyDoc() {
|
|
|
2840
2725
|
return schema.node("doc", null, [schema.node("paragraph", {}, [])]);
|
|
2841
2726
|
}
|
|
2842
2727
|
//#endregion
|
|
2843
|
-
export { createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror,
|
|
2728
|
+
export { createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror, toProseDoc };
|
|
@@ -7,6 +7,7 @@ import { listRenderingFor, recordsListRendering } from "./listRendering.js";
|
|
|
7
7
|
import { LIST_RENDERING_ATTR_KEYS } from "./listRenderingAttrs.js";
|
|
8
8
|
import { directParagraphIndentation } from "./paragraphIndentation.js";
|
|
9
9
|
import { getDocumentNumbering } from "./plugins/documentNumbering.js";
|
|
10
|
+
import { getDocumentStyleResolver } from "./plugins/documentStyleState.js";
|
|
10
11
|
import { SUGGESTION_META, makeRevisionInfo } from "./plugins/suggestionMode.js";
|
|
11
12
|
import { listAttrsFromNumbering, listLevelIndentAttrPatch } from "./styles/resolvedStyleAttrs.js";
|
|
12
13
|
//#region src/prosemirror/listNumbering.ts
|
|
@@ -20,22 +21,15 @@ const PARAGRAPH_NODE = "paragraph";
|
|
|
20
21
|
*/
|
|
21
22
|
const listChangeSnapshot = (node, attrs) => {
|
|
22
23
|
const previousFormatting = paragraphPropertiesSnapshot(node.type.create(attrs, node.content, node.marks));
|
|
23
|
-
previousFormatting["numPr"]
|
|
24
|
+
previousFormatting["numPr"] ??= null;
|
|
24
25
|
for (const key of LIST_RENDERING_ATTR_KEYS) previousFormatting[key] = attrs[key] ?? null;
|
|
25
26
|
return previousFormatting;
|
|
26
27
|
};
|
|
27
|
-
|
|
28
|
-
* The original state a pending record describes, completed with the list
|
|
29
|
-
* attrs it may not state. A record written by a list command states them all.
|
|
30
|
-
* One written for another property (or read from a file) states the scope
|
|
31
|
-
* only: its numbering is the original's, and an absent one means the original
|
|
32
|
-
* had none, so the rendering is recomputed from it rather than copied from
|
|
33
|
-
* the live paragraph, whose list may since have changed.
|
|
34
|
-
*/
|
|
35
|
-
const originalListFormatting = (record, numbering) => {
|
|
28
|
+
const originalListFormatting = ({ record, numbering, styleResolver }) => {
|
|
36
29
|
const original = { ...record };
|
|
37
30
|
if (recordsListRendering(record)) return original;
|
|
38
|
-
|
|
31
|
+
const inherited = styleResolver?.resolveParagraphStyle(record.styleId ?? void 0).paragraphFormatting?.numPr;
|
|
32
|
+
Object.assign(original, listRenderingFor(record.numPr ?? inherited, numbering));
|
|
39
33
|
original["numPr"] = record.numPr ?? null;
|
|
40
34
|
return original;
|
|
41
35
|
};
|
|
@@ -54,12 +48,16 @@ const canonicalJson = (value) => JSON.stringify(value, (_key, nested) => nested
|
|
|
54
48
|
* the file) keeps its author, id and date: the change joins that revision
|
|
55
49
|
* rather than re-attributing it, and it is never dropped.
|
|
56
50
|
*/
|
|
57
|
-
const trackListChange = ({ current, next, rev, numbering }) => {
|
|
51
|
+
const trackListChange = ({ current, next, rev, numbering, styleResolver }) => {
|
|
58
52
|
const currentAttrs = expectParagraphAttrs(current);
|
|
59
53
|
const existing = currentAttrs._propertyChanges ?? [];
|
|
60
54
|
const pending = existing.find(({ info }) => info.provenance !== "suggested");
|
|
61
55
|
const retained = existing.filter((change) => change !== pending);
|
|
62
|
-
const previousFormatting = pending?.previousFormatting ? originalListFormatting(
|
|
56
|
+
const previousFormatting = pending?.previousFormatting ? originalListFormatting({
|
|
57
|
+
record: pending.previousFormatting,
|
|
58
|
+
numbering,
|
|
59
|
+
styleResolver
|
|
60
|
+
}) : listChangeSnapshot(current, currentAttrs);
|
|
63
61
|
const ownPending = pending !== void 0 && pending.info.author === rev.author;
|
|
64
62
|
if (ownPending && canonicalJson(previousFormatting) === canonicalJson(listChangeSnapshot(current, next))) return {
|
|
65
63
|
...next,
|
|
@@ -198,12 +196,14 @@ const listItemAttrs = (attrs, { numId, ilvl }, numbering) => ({
|
|
|
198
196
|
const applyParagraphUpdates = ({ tr, state, updates }) => {
|
|
199
197
|
const rev = makeRevisionInfo(state);
|
|
200
198
|
const numbering = getDocumentNumbering(state);
|
|
199
|
+
const styleResolver = getDocumentStyleResolver(state);
|
|
201
200
|
for (const { pos, node, next } of updates) {
|
|
202
201
|
const attrs = rev ? trackListChange({
|
|
203
202
|
current: node,
|
|
204
203
|
next,
|
|
205
204
|
rev,
|
|
206
|
-
numbering
|
|
205
|
+
numbering,
|
|
206
|
+
styleResolver
|
|
207
207
|
}) : next;
|
|
208
208
|
tr.setNodeMarkup(pos, void 0, attrs);
|
|
209
209
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ParagraphAttrs, ParagraphPropertyChangeAttrs } from "./schema/nodes.js";
|
|
2
2
|
import { NumberingMap } from "../docx/numberingParser.js";
|
|
3
|
+
import { ParagraphNumberingOverride } from "../docx/numberingReference.js";
|
|
3
4
|
//#region src/prosemirror/listRendering.d.ts
|
|
4
5
|
type PreviousFormatting = NonNullable<ParagraphPropertyChangeAttrs["previousFormatting"]>;
|
|
5
6
|
/** Whether a `w:pPrChange` record states the list rendering it had (a list command's does). */
|
|
6
7
|
declare const recordsListRendering: (record: PreviousFormatting) => boolean;
|
|
7
8
|
/** Every list-rendering attr for `numPr`: its level's, or all cleared when it numbers nothing. */
|
|
8
|
-
declare const listRenderingFor: (numPr:
|
|
9
|
+
declare const listRenderingFor: (numPr: ParagraphNumberingOverride | null | undefined, numbering: NumberingMap | null) => Record<string, unknown>;
|
|
9
10
|
/**
|
|
10
11
|
* The list rendering a paragraph takes when a `w:pPrChange` is rejected. A
|
|
11
12
|
* reject restores the recorded numbering, but the rendering attrs sit outside
|
|
@@ -21,7 +22,8 @@ type RejectedListRenderingOptions = {
|
|
|
21
22
|
/** The previous state the rejected record restores. */
|
|
22
23
|
previousFormatting: PreviousFormatting | null | undefined;
|
|
23
24
|
numbering: NumberingMap | null;
|
|
25
|
+
restoredNumbering: ParagraphAttrs["numPr"] | null;
|
|
24
26
|
};
|
|
25
|
-
declare const rejectedListRenderingPatch: ({ current, previousFormatting, numbering }: RejectedListRenderingOptions) => Record<string, unknown>;
|
|
27
|
+
declare const rejectedListRenderingPatch: ({ current, previousFormatting, numbering, restoredNumbering }: RejectedListRenderingOptions) => Record<string, unknown>;
|
|
26
28
|
//#endregion
|
|
27
29
|
export { RejectedListRenderingOptions, listRenderingFor, recordsListRendering, rejectedListRenderingPatch };
|
|
@@ -16,10 +16,9 @@ const listRenderingFor = (numPr, numbering) => {
|
|
|
16
16
|
for (const key of LIST_RENDERING_ATTR_KEYS) attrs[key] = rendering[key] ?? null;
|
|
17
17
|
return attrs;
|
|
18
18
|
};
|
|
19
|
-
const rejectedListRenderingPatch = ({ current, previousFormatting, numbering }) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const restored = record.numPr ?? void 0;
|
|
19
|
+
const rejectedListRenderingPatch = ({ current, previousFormatting, numbering, restoredNumbering }) => {
|
|
20
|
+
if (recordsListRendering(previousFormatting ?? {})) return {};
|
|
21
|
+
const restored = restoredNumbering ?? void 0;
|
|
23
22
|
if (sameStatedParagraphNumbering(restored, current.numPr ?? void 0)) return {};
|
|
24
23
|
return listRenderingFor(restored, numbering);
|
|
25
24
|
};
|
|
@@ -86,7 +86,7 @@ const carryParagraphProperties = ({ tr, position, source, styleResolver, numberi
|
|
|
86
86
|
};
|
|
87
87
|
/**
|
|
88
88
|
* Whether the carried numbering is the paragraph's own or its style's: the
|
|
89
|
-
* record holds
|
|
89
|
+
* record holds only stated numbering, and a style's numbering
|
|
90
90
|
* stays the style's rather than becoming the paragraph's own `w:numPr`.
|
|
91
91
|
*/
|
|
92
92
|
const carriedNumberingProvenance = (source, target, keep) => ({ numPrFromStyle: expectParagraphAttrs(keep?.has("numPr") ? target : source).numPrFromStyle });
|
|
@@ -112,10 +112,7 @@ const paragraphLeftAfter = ({ doc, paragraphPos, removedBreakPositions }) => {
|
|
|
112
112
|
node = next;
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
|
-
const recordedFormatting =
|
|
116
|
-
...paragraphPropertiesSnapshot(node),
|
|
117
|
-
numPr: expectParagraphAttrs(node).numPr ?? null
|
|
118
|
-
});
|
|
115
|
+
const recordedFormatting = paragraphPropertiesSnapshot;
|
|
119
116
|
/**
|
|
120
117
|
* A paragraph now holds the break another one had (a split, or a paste into
|
|
121
118
|
* it, leaves that break on the last part) but reads with other properties:
|
|
@@ -407,11 +407,7 @@ function rotateIntoFinalParagraph(state, tr, replacedPos, attrs) {
|
|
|
407
407
|
const replaced = tr.doc.nodeAt(replacedPos);
|
|
408
408
|
const first = replaced ? tr.doc.nodeAt(replacedPos + replaced.nodeSize) : null;
|
|
409
409
|
if (!replaced || !first) return replacedPos;
|
|
410
|
-
const
|
|
411
|
-
const previousFormatting = {
|
|
412
|
-
...paragraphPropertiesSnapshot(replaced),
|
|
413
|
-
numPr: replacedAttrs.numPr ?? null
|
|
414
|
-
};
|
|
410
|
+
const previousFormatting = paragraphPropertiesSnapshot(replaced);
|
|
415
411
|
const { _sectionProperties: sectionProperties } = replaced.attrs;
|
|
416
412
|
tr.join(replacedPos + replaced.nodeSize);
|
|
417
413
|
tr.setNodeMarkup(replacedPos, void 0, first.attrs);
|
|
@@ -14,6 +14,12 @@ type DirectiveRange = {
|
|
|
14
14
|
* array path the loop iterates (`{% for row in items %}` ⇒ `items`).
|
|
15
15
|
*/
|
|
16
16
|
expr: string;
|
|
17
|
+
/**
|
|
18
|
+
* For an `if` / `elif` whose body is one field path plus a filter chain
|
|
19
|
+
* (`{% if consented | checkbox | label("…") %}`), that path; `expr` keeps
|
|
20
|
+
* the body as authored. Unset for every other tag.
|
|
21
|
+
*/
|
|
22
|
+
conditionPath?: string;
|
|
17
23
|
/** Clause-slot version selector, e.g. "v3" or "latest". */
|
|
18
24
|
clauseVersion?: string;
|
|
19
25
|
/** Loop alias of a `for` marker (`{% for row in items %}` ⇒ `row`); unset
|
|
@@ -3,9 +3,17 @@ import { collectBlockChunks, joinChunks, offsetToDocPos } from "./pmTextScan.js"
|
|
|
3
3
|
import { PluginKey } from "prosemirror-state";
|
|
4
4
|
import { assertNever, isBlockDirectiveKind, scanMarkers } from "@stll/template-conditions";
|
|
5
5
|
//#region src/prosemirror/plugins/templateDirectives.ts
|
|
6
|
+
/**
|
|
7
|
+
* The body of an `if` / `elif` tag as authored: its inner text after the
|
|
8
|
+
* keyword. The grammar reads a body that is one path plus a filter chain as
|
|
9
|
+
* that path, with the chain in `filters`; the document still holds the whole
|
|
10
|
+
* body, and that is what a host keys a condition by.
|
|
11
|
+
*/
|
|
12
|
+
const conditionBody = ({ inner, meta }) => inner.slice(meta.kind.length).trim();
|
|
6
13
|
/** The display expression for a marker (field path, clause name, key,
|
|
7
|
-
* condition, loop array path, loop property). */
|
|
8
|
-
const directiveExpr = (
|
|
14
|
+
* condition as authored, loop array path, loop property). */
|
|
15
|
+
const directiveExpr = (marker) => {
|
|
16
|
+
const { meta } = marker;
|
|
9
17
|
switch (meta.kind) {
|
|
10
18
|
case "placeholder": return meta.expr;
|
|
11
19
|
case "clause": return meta.name;
|
|
@@ -13,7 +21,7 @@ const directiveExpr = (meta) => {
|
|
|
13
21
|
case "ref": return meta.key;
|
|
14
22
|
case "loop": return meta.property;
|
|
15
23
|
case "if":
|
|
16
|
-
case "elif": return
|
|
24
|
+
case "elif": return conditionBody(marker);
|
|
17
25
|
case "for": return meta.path;
|
|
18
26
|
case "else":
|
|
19
27
|
case "endif":
|
|
@@ -63,6 +71,8 @@ const computeBlockDepths = (ranges) => {
|
|
|
63
71
|
};
|
|
64
72
|
/** The loop alias a `for` marker binds, or undefined for every other kind. */
|
|
65
73
|
const directiveAlias = (meta) => meta.kind === "for" ? meta.alias : void 0;
|
|
74
|
+
/** The path a condition's filter chain configures, or undefined without a chain. */
|
|
75
|
+
const directiveConditionPath = (meta) => (meta.kind === "if" || meta.kind === "elif") && meta.filters.length > 0 ? meta.expr : void 0;
|
|
66
76
|
const scanDirectives = (doc) => {
|
|
67
77
|
const ranges = [];
|
|
68
78
|
for (const chunks of collectBlockChunks(doc)) {
|
|
@@ -73,27 +83,31 @@ const scanDirectives = (doc) => {
|
|
|
73
83
|
if (sole && sole.raw === trimmed && isBlockDirectiveKind(sole.meta.kind)) {
|
|
74
84
|
const last = chunks.at(-1);
|
|
75
85
|
const alias = directiveAlias(sole.meta);
|
|
86
|
+
const conditionPath = directiveConditionPath(sole.meta);
|
|
76
87
|
ranges.push({
|
|
77
88
|
from: chunks[0]?.start ?? 0,
|
|
78
89
|
to: last ? last.end ?? last.start + last.text.length : 0,
|
|
79
90
|
kind: sole.meta.kind,
|
|
80
|
-
expr: directiveExpr(sole
|
|
91
|
+
expr: directiveExpr(sole),
|
|
81
92
|
block: true,
|
|
82
|
-
...alias !== void 0 ? { alias } : {}
|
|
93
|
+
...alias !== void 0 ? { alias } : {},
|
|
94
|
+
...conditionPath !== void 0 ? { conditionPath } : {}
|
|
83
95
|
});
|
|
84
96
|
continue;
|
|
85
97
|
}
|
|
86
98
|
for (const marker of scanMarkers(joined)) {
|
|
87
99
|
const clauseVersion = marker.meta.kind === "clause" ? marker.meta.version : void 0;
|
|
88
100
|
const alias = directiveAlias(marker.meta);
|
|
101
|
+
const conditionPath = directiveConditionPath(marker.meta);
|
|
89
102
|
ranges.push({
|
|
90
103
|
from: offsetToDocPos(chunks, marker.start),
|
|
91
104
|
to: offsetToDocPos(chunks, marker.end, "end"),
|
|
92
105
|
kind: marker.meta.kind,
|
|
93
|
-
expr: directiveExpr(marker
|
|
106
|
+
expr: directiveExpr(marker),
|
|
94
107
|
block: false,
|
|
95
108
|
...clauseVersion !== void 0 ? { clauseVersion } : {},
|
|
96
|
-
...alias !== void 0 ? { alias } : {}
|
|
109
|
+
...alias !== void 0 ? { alias } : {},
|
|
110
|
+
...conditionPath !== void 0 ? { conditionPath } : {}
|
|
97
111
|
});
|
|
98
112
|
}
|
|
99
113
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { scanDirectives } from "./templateDirectives.js";
|
|
2
2
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
3
3
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
4
|
-
import {
|
|
4
|
+
import { isFieldPath } from "@stll/template-conditions";
|
|
5
5
|
//#region src/prosemirror/plugins/templatePreviewValues.ts
|
|
6
6
|
/** Concatenated plain text of a preview value. */
|
|
7
7
|
const templatePreviewValueText = (value) => typeof value === "string" ? value : value.runs.map((run) => run.text).join("");
|
|
@@ -64,28 +64,21 @@ function buildValueWidget(value, mode) {
|
|
|
64
64
|
return span;
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
/** Loop alias the bare-path probe binds. Any identifier does; it is discarded. */
|
|
68
|
-
const CHAIN_PROBE_ALIAS = "__folioCondition";
|
|
69
67
|
/** A field path opens with a letter or underscore, so `9x` and `-x` are not one. */
|
|
70
68
|
const FIELD_PATH_HEAD_RE = /^[\p{L}_]/u;
|
|
71
69
|
/**
|
|
72
70
|
* The bare field path a condition's filter chain hangs off, or undefined when
|
|
73
|
-
* the
|
|
71
|
+
* the tag is not a path plus a chain.
|
|
74
72
|
*
|
|
75
|
-
* A host may write the tag the
|
|
76
|
-
*
|
|
77
|
-
* keying `conditions` by the bare path.
|
|
78
|
-
*
|
|
79
|
-
* that
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* which is what keeps a real expression on exact matching.
|
|
73
|
+
* A host may write the tag with the chain of the boolean it names —
|
|
74
|
+
* `{% if buyer_is_a_consumer | checkbox | ai("Is the buyer …") %}` — while
|
|
75
|
+
* keying `conditions` by the bare path. The grammar's argument-aware scan
|
|
76
|
+
* splits the chain (`label("a | b")` stays one argument) and reports the path
|
|
77
|
+
* as `conditionPath`; an expression that is not a path plus a chain of known
|
|
78
|
+
* filters (`a and b`, `items|length > 0`, `ai("a|b")`) has none, which is what
|
|
79
|
+
* keeps a real expression on exact matching.
|
|
83
80
|
*/
|
|
84
|
-
const filterChainPath = (
|
|
85
|
-
const meta = classifyMarker(`for ${CHAIN_PROBE_ALIAS} in ${expr}`, "statement");
|
|
86
|
-
if (meta?.kind !== "for" || !isFieldPath(meta.path) || !FIELD_PATH_HEAD_RE.test(meta.path)) return;
|
|
87
|
-
return meta.path;
|
|
88
|
-
};
|
|
81
|
+
const filterChainPath = ({ conditionPath }) => conditionPath !== void 0 && isFieldPath(conditionPath) && FIELD_PATH_HEAD_RE.test(conditionPath) ? conditionPath : void 0;
|
|
89
82
|
/**
|
|
90
83
|
* The verdict stored under one key. Only a key the host set itself, holding a
|
|
91
84
|
* real boolean, counts: an inherited property (`constructor`, or a boolean the
|
|
@@ -102,10 +95,10 @@ const ownVerdict = (conditions, key) => {
|
|
|
102
95
|
* by the bare path its filter chain hangs off. `undefined` means the host said
|
|
103
96
|
* nothing about this block, which leaves it exactly as authored.
|
|
104
97
|
*/
|
|
105
|
-
const conditionVerdict = (conditions,
|
|
106
|
-
const exact = ownVerdict(conditions, expr);
|
|
98
|
+
const conditionVerdict = (conditions, opener) => {
|
|
99
|
+
const exact = ownVerdict(conditions, opener.expr);
|
|
107
100
|
if (exact !== void 0) return exact;
|
|
108
|
-
const path = filterChainPath(
|
|
101
|
+
const path = filterChainPath(opener);
|
|
109
102
|
return path === void 0 ? void 0 : ownVerdict(conditions, path);
|
|
110
103
|
};
|
|
111
104
|
/**
|
|
@@ -157,7 +150,7 @@ function collectHiddenRanges(ranges, conditions, mode) {
|
|
|
157
150
|
}
|
|
158
151
|
if (block === void 0 || block.opener.kind !== "if") continue;
|
|
159
152
|
const { opener, branches } = block;
|
|
160
|
-
const verdict = conditionVerdict(conditions, opener
|
|
153
|
+
const verdict = conditionVerdict(conditions, opener);
|
|
161
154
|
if (verdict === void 0) continue;
|
|
162
155
|
if (!verdict) {
|
|
163
156
|
paired.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/folio-core",
|
|
3
|
-
"version": "0.54.
|
|
3
|
+
"version": "0.54.1",
|
|
4
4
|
"description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-model",
|
|
@@ -134,9 +134,9 @@
|
|
|
134
134
|
"perf": "bun scripts/profile-editor.ts"
|
|
135
135
|
},
|
|
136
136
|
"dependencies": {
|
|
137
|
-
"@stll/docx-core": "^0.
|
|
137
|
+
"@stll/docx-core": "^0.30.0",
|
|
138
138
|
"@stll/docx-utils": "^0.1.0",
|
|
139
|
-
"@stll/template-conditions": "
|
|
139
|
+
"@stll/template-conditions": "^0.6.2",
|
|
140
140
|
"better-result": "3.0.1",
|
|
141
141
|
"csstype": "^3.1.3",
|
|
142
142
|
"dompurify": "^3.4.13",
|