@stll/folio-core 0.33.1 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +15 -0
- package/dist/ai-edits/apply.js +271 -48
- package/dist/ai-edits/headless.d.ts +47 -3
- package/dist/ai-edits/headless.js +53 -3
- package/dist/ai-edits/index.d.ts +1 -1
- package/dist/ai-edits/snapshot.d.ts +19 -1
- package/dist/ai-edits/snapshot.js +49 -5
- package/dist/ai-edits/table-geometry.d.ts +56 -0
- package/dist/ai-edits/table-geometry.js +217 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +34 -16
- package/dist/ai-edits/table-row-column-mutations.js +12 -4
- package/dist/ai-edits/table-template.d.ts +60 -0
- package/dist/ai-edits/table-template.js +176 -0
- package/dist/ai-edits/types.d.ts +13 -6
- package/dist/compare/__fixtures__/body-sequence.d.ts +61 -2
- package/dist/compare/__fixtures__/body-sequence.js +94 -6
- package/dist/compare/compare.d.ts +14 -7
- package/dist/compare/compare.js +136 -38
- package/dist/compare/formatting.d.ts +1 -1
- package/dist/compare/formatting.js +38 -9
- package/dist/compare/plan.d.ts +31 -1
- package/dist/compare/plan.js +233 -66
- package/dist/compare/types.d.ts +9 -7
- package/dist/compare/types.js +7 -5
- package/dist/compare/verification.d.ts +31 -12
- package/dist/compare/verification.js +106 -18
- package/dist/compat/eigenpal.d.ts +4 -4
- package/dist/compat/eigenpal.js +2 -2
- package/dist/controller/headerFooterEditorManager.js +11 -9
- package/dist/controller/layoutPipeline.js +24 -3
- package/dist/display-list/build/watermarkPrimitives.js +15 -3
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/document-operations.d.ts +10 -1
- package/dist/document-operations.js +33 -5
- package/dist/docx/appVersionNormalization.d.ts +49 -0
- package/dist/docx/appVersionNormalization.js +74 -0
- package/dist/docx/blockContentParser.js +3 -2
- package/dist/docx/drawingUtils.js +4 -3
- package/dist/docx/headerFooterParser.js +8 -14
- package/dist/docx/paragraphParser.js +48 -2
- package/dist/docx/rezip.d.ts +10 -4
- package/dist/docx/rezip.js +54 -18
- package/dist/docx/runParser.js +9 -8
- package/dist/docx/sdtProperties.js +4 -3
- package/dist/docx/selectiveSave.js +7 -6
- package/dist/docx/serializer/commentSerializer.d.ts +1 -1
- package/dist/docx/serializer/commentSerializer.js +43 -19
- package/dist/docx/serializer/documentSerializer.d.ts +3 -1
- package/dist/docx/serializer/documentSerializer.js +42 -78
- package/dist/docx/serializer/fontTableSerializer.js +8 -5
- package/dist/docx/serializer/headerFooterSerializer.d.ts +4 -1
- package/dist/docx/serializer/headerFooterSerializer.js +55 -31
- package/dist/docx/serializer/noteSerializer.js +34 -30
- package/dist/docx/serializer/numberingSerializer.js +8 -4
- package/dist/docx/serializer/paragraphSerializer.d.ts +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +22 -12
- package/dist/docx/serializer/partNamespaces.d.ts +78 -0
- package/dist/docx/serializer/partNamespaces.js +324 -0
- package/dist/docx/serializer/runSerializer.js +1 -1
- package/dist/docx/serializer/settingsSerializer.js +8 -2
- package/dist/docx/serializer/stylesSerializer.js +8 -5
- package/dist/docx/serializer/tableSerializer.js +83 -18
- package/dist/docx/serializer/themeSerializer.js +10 -2
- package/dist/docx/server/build.d.ts +1 -1
- package/dist/docx/settingsParser.js +3 -0
- package/dist/docx/strictValueEncodings.gen.d.ts +15 -0
- package/dist/docx/strictValueEncodings.gen.js +275 -0
- package/dist/docx/tableParser.d.ts +0 -6
- package/dist/docx/tableParser.js +52 -7
- package/dist/docx/transitionalSpelling.d.ts +23 -0
- package/dist/docx/transitionalSpelling.js +36 -0
- package/dist/docx/universalMeasure.d.ts +20 -0
- package/dist/docx/universalMeasure.js +32 -0
- package/dist/docx/verbatimCapture.d.ts +20 -0
- package/dist/docx/verbatimCapture.js +131 -0
- package/dist/docx/vmlImageParser.js +4 -3
- package/dist/docx/watermarkParser.d.ts +2 -4
- package/dist/docx/watermarkParser.js +8 -9
- package/dist/docx/xmlParser.d.ts +23 -2
- package/dist/docx/xmlParser.js +43 -11
- package/dist/headless-layout.js +14 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -2
- package/dist/layout-bridge/convert/footnoteLayout.js +23 -10
- package/dist/layout-bridge/convert/headerFooterLayout.js +13 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +96 -13
- package/dist/layout-engine/index.js +11 -4
- package/dist/layout-engine/justifiedLineFit.d.ts +4 -4
- package/dist/layout-engine/justifiedLineFit.js +4 -4
- package/dist/layout-engine/measure/lineBreakProvider.js +1 -0
- package/dist/layout-engine/measure/measureBlocks.js +1 -1
- package/dist/layout-engine/measure/measureParagraph.js +29 -29
- package/dist/layout-painter/renderPage.js +6 -1
- package/dist/layout-painter/renderParagraph.js +18 -8
- package/dist/layout-painter/renderWatermark.js +11 -4
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/prosemirror/attrs/index.js +11 -0
- package/dist/prosemirror/commands/comments.js +24 -3
- package/dist/prosemirror/containerFinalParagraph.d.ts +47 -0
- package/dist/prosemirror/containerFinalParagraph.js +92 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +47 -9
- package/dist/prosemirror/conversion/toProseDoc.js +61 -14
- package/dist/prosemirror/extensions/core/DocExtension.js +7 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +1 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +5 -0
- package/dist/prosemirror/schema/marks.d.ts +3 -9
- package/dist/prosemirror/schema/nodes.d.ts +22 -0
- package/dist/server.d.ts +1 -1
- package/dist/types/block-id.d.ts +12 -1
- package/dist/types/block-id.js +17 -1
- package/dist/utils/canonicalJson.d.ts +14 -0
- package/dist/utils/canonicalJson.js +19 -0
- package/dist/utils/fontResolver.js +51 -0
- package/dist/utils/formatToStyle.d.ts +1 -1
- package/dist/utils/formatToStyle.js +41 -1
- package/dist/watermark/index.js +7 -0
- package/package.json +2 -2
package/dist/ai-edits/apply.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FolioTableTemplates } from "./table-template.js";
|
|
1
2
|
import { FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
|
|
2
3
|
import { WordDiffGranularity } from "./word-diff.js";
|
|
3
4
|
import { EditorState, Transaction } from "prosemirror-state";
|
|
@@ -42,6 +43,20 @@ type ApplyFolioAIEditOperationsOptions = {
|
|
|
42
43
|
revisionStamp?: FolioRevisionStamp;
|
|
43
44
|
/** How a replacement's redline is cut. */
|
|
44
45
|
wordDiff?: FolioWordDiffOptions;
|
|
46
|
+
/**
|
|
47
|
+
* Tables and rows an `insertTable` / `insertTableRow` operation should place
|
|
48
|
+
* verbatim, by operation id.
|
|
49
|
+
*
|
|
50
|
+
* The operations describe their content as cell texts, which is what a
|
|
51
|
+
* caller writing a table from nothing has. A caller copying one it already
|
|
52
|
+
* holds — a comparison placing the target document's table — has the whole
|
|
53
|
+
* thing: `w:tblPr`, the `w:tblGrid` widths, `w:trPr`, `w:tcPr` with its
|
|
54
|
+
* spans and merges, and cell paragraphs with their own properties. Rebuilt
|
|
55
|
+
* from text, none of that survives. Kept out of the operation payload
|
|
56
|
+
* because it is a document node rather than JSON: the serialized contract
|
|
57
|
+
* still describes a table by its cell texts.
|
|
58
|
+
*/
|
|
59
|
+
tableTemplates?: FolioTableTemplates;
|
|
45
60
|
};
|
|
46
61
|
/**
|
|
47
62
|
* How an apply batch cuts the redline for a replacement. Word-level by
|
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { expectParagraphAttrs, expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
1
|
+
import { expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
2
2
|
import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../prosemirror/commands/propertyChangeScope.js";
|
|
3
|
+
import { addedBreakCarrierBefore, finalParagraphsOf, paragraphEndsItsContainer } from "../prosemirror/containerFinalParagraph.js";
|
|
3
4
|
import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
|
|
4
5
|
import { requestDeterministicParaIds } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
|
|
5
6
|
import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
@@ -11,8 +12,9 @@ import { hasInlineEmphasis, parseInlineEmphasisRuns, stripInlineEmphasisMarkers
|
|
|
11
12
|
import { hashFolioAIBlockText, isHiddenTableRow, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
12
13
|
import { mergeTableRectangle, mergeTrackedVerticalTableCells, splitTableRectangle, splitTrackedVerticalTableCell } from "./table-cell-mutations.js";
|
|
13
14
|
import { planTableMutations } from "./table-mutation-plan.js";
|
|
14
|
-
import { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDeletion, applyTableRowInsertion, findTableColumnInsertion, findTableRowInsertion, getTableColumnCoordinateKey, splitCellParagraphTexts } from "./table-row-column-mutations.js";
|
|
15
|
+
import { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDeletion, applyTableRowInsertion, findTableColumnInsertion, findTableRowInsertion, getTableColumnCoordinateKey, markTableRowContent, splitCellParagraphTexts } from "./table-row-column-mutations.js";
|
|
15
16
|
import { findEnclosingTableCell, findEnclosingTableRow, findOutermostTableBoundary, tableRectangleCutsMergedCell } from "./table-targets.js";
|
|
17
|
+
import { tableFromTemplate } from "./table-template.js";
|
|
16
18
|
import { diffWordSegments } from "./word-diff.js";
|
|
17
19
|
import { panic } from "better-result";
|
|
18
20
|
import { TableMap } from "prosemirror-tables";
|
|
@@ -152,24 +154,132 @@ const REPLACEMENT_BACKGROUND_CLEAR_FORMATTING = {
|
|
|
152
154
|
highlight: false,
|
|
153
155
|
runShading: false
|
|
154
156
|
};
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
const INLINE_FORMATTING_MARK_NAMES = {
|
|
158
|
+
bold: "bold",
|
|
159
|
+
italic: "italic",
|
|
160
|
+
underline: "underline",
|
|
161
|
+
strike: "strike",
|
|
162
|
+
fontFamily: "fontFamily",
|
|
163
|
+
fontSizePt: "fontSize",
|
|
164
|
+
color: "textColor",
|
|
165
|
+
highlight: "highlight",
|
|
166
|
+
runShading: "runShading"
|
|
167
|
+
};
|
|
168
|
+
const DIRECT_FONT_PROPERTIES = [
|
|
169
|
+
"fontFamily",
|
|
170
|
+
"fontSize",
|
|
171
|
+
"color"
|
|
172
|
+
];
|
|
173
|
+
const formattingMarkName = (property) => {
|
|
174
|
+
if (!Object.hasOwn(INLINE_FORMATTING_MARK_NAMES, property)) return null;
|
|
175
|
+
const name = Reflect.get(INLINE_FORMATTING_MARK_NAMES, property);
|
|
176
|
+
return typeof name === "string" ? name : null;
|
|
177
|
+
};
|
|
178
|
+
const formattingMarkAttrs = (property, value) => {
|
|
179
|
+
switch (property) {
|
|
180
|
+
case "underline": return { style: "single" };
|
|
181
|
+
case "fontFamily": return typeof value === "string" ? {
|
|
182
|
+
ascii: value,
|
|
183
|
+
hAnsi: value
|
|
184
|
+
} : null;
|
|
185
|
+
case "fontSizePt": return typeof value === "number" ? { size: value * 2 } : null;
|
|
186
|
+
case "color": return typeof value === "string" ? { rgb: value.replace(/^#/u, "").toUpperCase() } : null;
|
|
187
|
+
default: return {};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const applyInlineFormatting = ({ tr, schema, from, to, formatting, includedProperties }) => {
|
|
191
|
+
for (const [property, value] of Object.entries(formatting)) {
|
|
192
|
+
if (includedProperties && !includedProperties.includes(property)) continue;
|
|
193
|
+
const markName = formattingMarkName(property);
|
|
194
|
+
const markType = markName ? schema.marks[markName] : void 0;
|
|
158
195
|
if (!markType) continue;
|
|
159
|
-
if (
|
|
160
|
-
|
|
196
|
+
if (value !== false && value !== null) {
|
|
197
|
+
const attrs = formattingMarkAttrs(property, value);
|
|
198
|
+
if (attrs) tr.addMark(from, to, markType.create(attrs));
|
|
161
199
|
continue;
|
|
162
200
|
}
|
|
163
201
|
tr.removeMark(from, to, markType);
|
|
164
202
|
}
|
|
203
|
+
applyDirectFontProvenance({
|
|
204
|
+
tr,
|
|
205
|
+
schema,
|
|
206
|
+
from,
|
|
207
|
+
to,
|
|
208
|
+
formatting,
|
|
209
|
+
...includedProperties ? { includedProperties } : {}
|
|
210
|
+
});
|
|
165
211
|
return tr;
|
|
166
212
|
};
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
213
|
+
const applyDirectFontProvenance = ({ tr, schema, from, to, formatting, includedProperties }) => {
|
|
214
|
+
if ("highlight" in formatting) return;
|
|
215
|
+
const changed = [
|
|
216
|
+
[
|
|
217
|
+
"fontFamily",
|
|
218
|
+
"fontFamily",
|
|
219
|
+
formatting.fontFamily
|
|
220
|
+
],
|
|
221
|
+
[
|
|
222
|
+
"fontSize",
|
|
223
|
+
"fontSizePt",
|
|
224
|
+
formatting.fontSizePt
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
"color",
|
|
228
|
+
"color",
|
|
229
|
+
formatting.color
|
|
230
|
+
]
|
|
231
|
+
].filter(([, inputProperty, value]) => value !== void 0 && (!includedProperties || includedProperties.includes(inputProperty)));
|
|
232
|
+
const markType = schema.marks["runFormattingOverride"];
|
|
233
|
+
if (!markType || changed.length === 0) return;
|
|
234
|
+
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
235
|
+
if (!node.isText) return;
|
|
236
|
+
const segmentFrom = Math.max(from, pos);
|
|
237
|
+
const segmentTo = Math.min(to, pos + node.nodeSize);
|
|
238
|
+
const existing = node.marks.find((mark) => mark.type === markType);
|
|
239
|
+
const attrs = existing ? expectRunFormattingOverrideMarkAttrs(existing) : {};
|
|
240
|
+
const directFontProperties = new Set(attrs.directFontProperties);
|
|
241
|
+
for (const [property, , value] of changed) if (value === null) directFontProperties.delete(property);
|
|
242
|
+
else directFontProperties.add(property);
|
|
243
|
+
tr.removeMark(segmentFrom, segmentTo, markType);
|
|
244
|
+
const nextAttrs = {
|
|
245
|
+
...attrs,
|
|
246
|
+
directFontProperties: DIRECT_FONT_PROPERTIES.filter((property) => directFontProperties.has(property))
|
|
247
|
+
};
|
|
248
|
+
if (Object.entries(nextAttrs).some(([key, value]) => key === "directFontProperties" ? Array.isArray(value) && value.length > 0 : value !== null && value !== void 0)) tr.addMark(segmentFrom, segmentTo, markType.create(nextAttrs));
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
const directFontPropertyForFormattingProperty = (property) => {
|
|
252
|
+
switch (property) {
|
|
253
|
+
case "fontFamily": return "fontFamily";
|
|
254
|
+
case "fontSizePt": return "fontSize";
|
|
255
|
+
case "color": return "color";
|
|
256
|
+
default: return null;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const formattingPropertyWouldChange = (marks, property, value) => {
|
|
260
|
+
const markName = formattingMarkName(property);
|
|
261
|
+
const mark = marks.find((candidate) => candidate.type.name === markName);
|
|
262
|
+
const directFontProperty = directFontPropertyForFormattingProperty(property);
|
|
263
|
+
if (directFontProperty) {
|
|
264
|
+
const overrideMark = marks.find((candidate) => candidate.type.name === "runFormattingOverride");
|
|
265
|
+
const isDirect = overrideMark ? (expectRunFormattingOverrideMarkAttrs(overrideMark).directFontProperties ?? []).includes(directFontProperty) : false;
|
|
266
|
+
if (value === false || value === null) return isDirect;
|
|
267
|
+
if (!isDirect) return true;
|
|
268
|
+
} else if (value === false || value === null) return mark !== void 0;
|
|
269
|
+
if (property === "underline") return mark?.attrs["style"] !== "single";
|
|
270
|
+
if (property === "fontFamily") return mark?.attrs["ascii"] !== value || mark?.attrs["hAnsi"] !== value;
|
|
271
|
+
if (property === "fontSizePt") return Number(mark?.attrs["size"]) !== Number(value) * 2;
|
|
272
|
+
if (property === "color") {
|
|
273
|
+
const current = mark?.attrs["rgb"];
|
|
274
|
+
return (typeof current === "string" ? current.replace(/^#/u, "").toUpperCase() : null) !== String(value).replace(/^#/u, "").toUpperCase();
|
|
275
|
+
}
|
|
171
276
|
return mark === void 0;
|
|
172
|
-
}
|
|
277
|
+
};
|
|
278
|
+
const formattingChangesForMarks = (marks, formatting) => {
|
|
279
|
+
const changes = [];
|
|
280
|
+
for (const [property, value] of Object.entries(formatting)) if (formattingPropertyWouldChange(marks, property, value)) changes.push(property);
|
|
281
|
+
return changes;
|
|
282
|
+
};
|
|
173
283
|
const clearReplacementBackground = ({ tr, schema, from, to, mode, revisionId, author, date, initials, suggestionId = null }) => {
|
|
174
284
|
if (![schema.marks["highlight"], schema.marks["runShading"]].some((markType) => markType !== void 0 && tr.doc.rangeHasMark(from, to, markType))) return tr;
|
|
175
285
|
if (mode === "direct") return applyInlineFormatting({
|
|
@@ -198,7 +308,9 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
|
|
|
198
308
|
if (!propertyChangeType) return tr;
|
|
199
309
|
const segments = [];
|
|
200
310
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
201
|
-
if (!node.isText
|
|
311
|
+
if (!node.isText) return;
|
|
312
|
+
const includedProperties = formattingChangesForMarks(node.marks, formatting);
|
|
313
|
+
if (includedProperties.length === 0) return;
|
|
202
314
|
const segmentFrom = Math.max(from, pos);
|
|
203
315
|
const segmentTo = Math.min(to, pos + node.nodeSize);
|
|
204
316
|
const previousFormatting = marksToTextFormatting(node.marks);
|
|
@@ -217,6 +329,7 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
|
|
|
217
329
|
segments.push({
|
|
218
330
|
from: segmentFrom,
|
|
219
331
|
to: segmentTo,
|
|
332
|
+
includedProperties,
|
|
220
333
|
changes: [...existingChanges, change]
|
|
221
334
|
});
|
|
222
335
|
});
|
|
@@ -225,17 +338,20 @@ const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, r
|
|
|
225
338
|
provenance: "suggested",
|
|
226
339
|
suggestionId
|
|
227
340
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
341
|
+
for (const segment of segments) {
|
|
342
|
+
applyInlineFormatting({
|
|
343
|
+
tr,
|
|
344
|
+
schema,
|
|
345
|
+
from: segment.from,
|
|
346
|
+
to: segment.to,
|
|
347
|
+
formatting,
|
|
348
|
+
includedProperties: segment.includedProperties
|
|
349
|
+
});
|
|
350
|
+
tr.addMark(segment.from, segment.to, propertyChangeType.create({
|
|
351
|
+
changes: segment.changes,
|
|
352
|
+
...suggestionAttrs
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
239
355
|
return tr;
|
|
240
356
|
};
|
|
241
357
|
/**
|
|
@@ -364,9 +480,11 @@ const ordinalAmongSameHash = (snapshot, blockId) => {
|
|
|
364
480
|
const SIGNATURE_LINE = "_".repeat(28);
|
|
365
481
|
/**
|
|
366
482
|
* A plain table from a grid of cell texts, `null` when the schema has no
|
|
367
|
-
* tables. In tracked mode every row carries `trIns
|
|
368
|
-
* "this table is new": there is
|
|
369
|
-
* that were inserted
|
|
483
|
+
* tables. In tracked mode every row carries `trIns` and every run inside it
|
|
484
|
+
* carries `w:ins`, which is how the format says "this table is new": there is
|
|
485
|
+
* no whole-table insertion element, only rows that were inserted, and a
|
|
486
|
+
* consumer that reads only run-level revisions keeps the text of a rejected
|
|
487
|
+
* insertion when the row marker stands alone.
|
|
370
488
|
*/
|
|
371
489
|
const buildTableNode = ({ schema, rows, revision }) => {
|
|
372
490
|
const paragraphType = schema.nodes["paragraph"];
|
|
@@ -374,7 +492,16 @@ const buildTableNode = ({ schema, rows, revision }) => {
|
|
|
374
492
|
const rowType = schema.nodes["tableRow"];
|
|
375
493
|
const tableType = schema.nodes["table"];
|
|
376
494
|
if (!paragraphType || !cellType || !rowType || !tableType || rows.length === 0) return null;
|
|
377
|
-
const
|
|
495
|
+
const insertionType = schema.marks["insertion"];
|
|
496
|
+
const marks = revision && insertionType ? [insertionType.create({
|
|
497
|
+
revisionId: revision.revisionId,
|
|
498
|
+
author: revision.author,
|
|
499
|
+
date: revision.date,
|
|
500
|
+
...revision.initials != null && { initials: revision.initials },
|
|
501
|
+
...revision.provenance != null && { provenance: revision.provenance },
|
|
502
|
+
...revision.suggestionId != null && { suggestionId: revision.suggestionId }
|
|
503
|
+
})] : void 0;
|
|
504
|
+
const rowNodes = rows.map((cells) => rowType.create(revision ? { trIns: revision } : null, cells.map((text) => cellType.create(null, splitCellParagraphTexts(text).map((line) => paragraphType.create(null, line.length > 0 ? schema.text(line, marks) : null))))));
|
|
378
505
|
return tableType.create(null, rowNodes);
|
|
379
506
|
};
|
|
380
507
|
/**
|
|
@@ -468,6 +595,75 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
468
595
|
const isEmptyInsert = insertTexts.length === 1 && insertTexts[0]?.length === 0;
|
|
469
596
|
return !(mode === "tracked-changes" && item.operation.pageBreakBefore === true && isEmptyInsert);
|
|
470
597
|
};
|
|
598
|
+
/**
|
|
599
|
+
* Move an ADDED paragraph break off every paragraph its container ends with.
|
|
600
|
+
*
|
|
601
|
+
* A container's final mark carries no revision in either direction: nothing
|
|
602
|
+
* follows it, so "join this paragraph with the one after it" and its mirror
|
|
603
|
+
* both state an edit that cannot be carried out, and a reader is left with a
|
|
604
|
+
* revision neither accepting nor rejecting everything can clear.
|
|
605
|
+
*
|
|
606
|
+
* Appending at a container's end therefore rotates exactly as removing from it
|
|
607
|
+
* does. The break that was added sits between the paragraph the run was
|
|
608
|
+
* appended after and the first appended one, so THAT paragraph's mark is the
|
|
609
|
+
* inserted one, each appended paragraph but the last keeps an inserted mark of
|
|
610
|
+
* its own, and the paragraph the container now ends with takes the free mark
|
|
611
|
+
* the run was appended after. Only which paragraph is left markless changes.
|
|
612
|
+
*
|
|
613
|
+
* A paragraph's properties live on its mark, so the paragraph that inherits
|
|
614
|
+
* the free one records the other's as `w:pPrChange` — what a rejection reads
|
|
615
|
+
* to put the container's ending back the way it was.
|
|
616
|
+
*
|
|
617
|
+
* This runs once over the finished document rather than at each insertion:
|
|
618
|
+
* which paragraph ends a container is only settled when the batch is, and an
|
|
619
|
+
* insertion that looked final was undone by the next operation writing a table
|
|
620
|
+
* after it.
|
|
621
|
+
*/
|
|
622
|
+
const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials }) => {
|
|
623
|
+
const paragraphTypeName = tr.doc.type.schema.nodes["paragraph"]?.name ?? "paragraph";
|
|
624
|
+
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).filter(({ node }) => {
|
|
625
|
+
const mark = node.attrs["pPrMark"];
|
|
626
|
+
if (typeof mark !== "object" || mark === null || !("kind" in mark)) return false;
|
|
627
|
+
if (mark.kind !== "ins" && mark.kind !== "moveTo") return false;
|
|
628
|
+
const info = "info" in mark ? mark.info : void 0;
|
|
629
|
+
const revisionId = typeof info === "object" && info !== null && "id" in info ? info.id : void 0;
|
|
630
|
+
return typeof revisionId === "number" && batchRevisionIds.has(revisionId);
|
|
631
|
+
});
|
|
632
|
+
let next = tr;
|
|
633
|
+
let nextRevisionId = revisionSeed;
|
|
634
|
+
for (const { position: finalPosition, node: final } of rotations) {
|
|
635
|
+
const carrier = addedBreakCarrierBefore(next.doc.resolve(finalPosition), final.type.name);
|
|
636
|
+
if (!carrier) {
|
|
637
|
+
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
next = next.setNodeAttribute(carrier.position, "pPrMark", final.attrs["pPrMark"]);
|
|
641
|
+
const previousFormatting = paragraphPropertiesSnapshot(carrier.node);
|
|
642
|
+
if (JSON.stringify(previousFormatting) === JSON.stringify(paragraphPropertiesSnapshot(final))) {
|
|
643
|
+
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const existing = expectParagraphAttrs(final)._propertyChanges;
|
|
647
|
+
next = next.setNodeMarkup(finalPosition, void 0, {
|
|
648
|
+
...final.attrs,
|
|
649
|
+
pPrMark: null,
|
|
650
|
+
_propertyChanges: [...Array.isArray(existing) ? existing : [], {
|
|
651
|
+
type: "paragraphPropertyChange",
|
|
652
|
+
info: {
|
|
653
|
+
id: nextRevisionId++,
|
|
654
|
+
author,
|
|
655
|
+
date,
|
|
656
|
+
...initials ? { initials } : {}
|
|
657
|
+
},
|
|
658
|
+
previousFormatting
|
|
659
|
+
}]
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
transaction: next,
|
|
664
|
+
nextRevisionId
|
|
665
|
+
};
|
|
666
|
+
};
|
|
471
667
|
const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, commentMark, suggestionId, revisionSeed, isPairedMove }) => {
|
|
472
668
|
const operation = item.operation;
|
|
473
669
|
if (operation.type !== "insertAfterBlock" && operation.type !== "insertBeforeBlock") panic("Only paragraph insertions can build inserted paragraphs", { type: operation.type });
|
|
@@ -551,7 +747,7 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
551
747
|
nextRevisionId
|
|
552
748
|
};
|
|
553
749
|
};
|
|
554
|
-
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff }) => {
|
|
750
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, tableTemplates }) => {
|
|
555
751
|
const applied = [];
|
|
556
752
|
const skipped = [];
|
|
557
753
|
const normalizations = [];
|
|
@@ -978,11 +1174,13 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
978
1174
|
continue;
|
|
979
1175
|
}
|
|
980
1176
|
const revision = structuralRevision;
|
|
1177
|
+
const template = tableTemplates?.get(item.operation.id);
|
|
981
1178
|
const result = applyTableRowInsertion({
|
|
982
1179
|
tr,
|
|
983
1180
|
insertion,
|
|
984
1181
|
cellTexts: item.operation.cellTexts,
|
|
985
|
-
revision
|
|
1182
|
+
revision,
|
|
1183
|
+
...template !== void 0 && { template }
|
|
986
1184
|
});
|
|
987
1185
|
if (result.type === "unsupported") {
|
|
988
1186
|
skipped.push({
|
|
@@ -1172,7 +1370,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1172
1370
|
case "deleteBlock":
|
|
1173
1371
|
if (mode === "direct") {
|
|
1174
1372
|
const at = tr.doc.resolve(item.blockFrom);
|
|
1175
|
-
tr = !(at
|
|
1373
|
+
tr = !paragraphEndsItsContainer(at, item.blockNode.type.name) || at.nodeBefore?.type.name === item.blockNode.type.name ? tr.delete(item.blockFrom, item.blockTo) : tr.delete(item.blockFrom + 1, item.blockTo - 1);
|
|
1176
1374
|
break;
|
|
1177
1375
|
}
|
|
1178
1376
|
if (deletionType) {
|
|
@@ -1190,8 +1388,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1190
1388
|
const inlineRevisionApplied = item.from < item.to || atomRanges.length > 0;
|
|
1191
1389
|
appliedRevisionIds = inlineRevisionApplied ? [revisionId] : [];
|
|
1192
1390
|
const markPosition = tr.mapping.map(item.blockFrom);
|
|
1193
|
-
|
|
1194
|
-
if (!(markPlace.index() === markPlace.parent.childCount - 1) && tr.doc.nodeAt(markPosition)?.attrs["pPrMark"] == null) {
|
|
1391
|
+
if (!paragraphEndsItsContainer(tr.doc.resolve(markPosition), item.blockNode.type.name) && tr.doc.nodeAt(markPosition)?.attrs["pPrMark"] == null) {
|
|
1195
1392
|
const markRevisionId = revisionSeed++;
|
|
1196
1393
|
tr = tr.setNodeAttribute(markPosition, "pPrMark", {
|
|
1197
1394
|
kind: isPairedMove(item.operation.moveId) ? "moveFrom" : "del",
|
|
@@ -1208,15 +1405,21 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1208
1405
|
if (commentMark) tr = tr.addMark(item.from, item.to, commentMark);
|
|
1209
1406
|
break;
|
|
1210
1407
|
case "insertTable": {
|
|
1211
|
-
const
|
|
1408
|
+
const revision = {
|
|
1409
|
+
revisionId: revisionSeed,
|
|
1410
|
+
author,
|
|
1411
|
+
date,
|
|
1412
|
+
...trackedRevisionExtras
|
|
1413
|
+
};
|
|
1414
|
+
const template = tableTemplates?.get(item.operation.id);
|
|
1415
|
+
const table = (template === void 0 ? null : tableFromTemplate({
|
|
1416
|
+
schema: view.state.schema,
|
|
1417
|
+
template,
|
|
1418
|
+
...producesTrackedChanges && { revision }
|
|
1419
|
+
})) ?? buildTableNode({
|
|
1212
1420
|
schema: view.state.schema,
|
|
1213
1421
|
rows: item.operation.rows,
|
|
1214
|
-
...producesTrackedChanges && { revision
|
|
1215
|
-
revisionId: revisionSeed,
|
|
1216
|
-
author,
|
|
1217
|
-
date,
|
|
1218
|
-
...trackedRevisionExtras
|
|
1219
|
-
} }
|
|
1422
|
+
...producesTrackedChanges && { revision }
|
|
1220
1423
|
});
|
|
1221
1424
|
if (!table) {
|
|
1222
1425
|
skipped.push({
|
|
@@ -1281,13 +1484,23 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1281
1484
|
});
|
|
1282
1485
|
continue;
|
|
1283
1486
|
}
|
|
1284
|
-
for (const rowPosition of rowPositions.toReversed())
|
|
1487
|
+
for (const rowPosition of rowPositions.toReversed()) {
|
|
1488
|
+
tr = tr.setNodeAttribute(rowPosition, "trDel", revision);
|
|
1489
|
+
markTableRowContent({
|
|
1490
|
+
tr,
|
|
1491
|
+
rowPosition,
|
|
1492
|
+
kind: "deletion",
|
|
1493
|
+
revision
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1285
1496
|
appliedRevisionIds = [revision.revisionId];
|
|
1286
1497
|
markStructuralChange(tr);
|
|
1287
1498
|
break;
|
|
1288
1499
|
}
|
|
1289
1500
|
case "setBlockParagraphProperties": {
|
|
1290
|
-
const
|
|
1501
|
+
const blockPosition = tr.mapping.map(item.blockFrom);
|
|
1502
|
+
const liveBlock = tr.doc.nodeAt(blockPosition) ?? item.blockNode;
|
|
1503
|
+
const patch = paragraphPropertiesPatch(liveBlock, item.operation.properties);
|
|
1291
1504
|
if (patch === null) {
|
|
1292
1505
|
skipped.push({
|
|
1293
1506
|
id: item.operation.id,
|
|
@@ -1296,8 +1509,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1296
1509
|
continue;
|
|
1297
1510
|
}
|
|
1298
1511
|
if (mode === "direct") {
|
|
1299
|
-
tr = tr.setNodeMarkup(
|
|
1300
|
-
...
|
|
1512
|
+
tr = tr.setNodeMarkup(blockPosition, void 0, {
|
|
1513
|
+
...liveBlock.attrs,
|
|
1301
1514
|
...patch
|
|
1302
1515
|
});
|
|
1303
1516
|
break;
|
|
@@ -1311,11 +1524,11 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1311
1524
|
date,
|
|
1312
1525
|
...initials ? { initials } : {}
|
|
1313
1526
|
},
|
|
1314
|
-
previousFormatting: paragraphPropertiesSnapshot(
|
|
1527
|
+
previousFormatting: paragraphPropertiesSnapshot(liveBlock)
|
|
1315
1528
|
};
|
|
1316
|
-
const existing = expectParagraphAttrs(
|
|
1317
|
-
tr = tr.setNodeMarkup(
|
|
1318
|
-
...
|
|
1529
|
+
const existing = expectParagraphAttrs(liveBlock)._propertyChanges;
|
|
1530
|
+
tr = tr.setNodeMarkup(blockPosition, void 0, {
|
|
1531
|
+
...liveBlock.attrs,
|
|
1319
1532
|
...patch,
|
|
1320
1533
|
_propertyChanges: [...Array.isArray(existing) ? existing : [], change]
|
|
1321
1534
|
});
|
|
@@ -1410,6 +1623,16 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1410
1623
|
if (tr.docChanged) {
|
|
1411
1624
|
const batchRevisionIds = new Set(applied.flatMap(({ revisionIds }) => revisionIds ?? []));
|
|
1412
1625
|
tr = withoutRevisionsOnZeroWidthAnchors(tr, batchRevisionIds);
|
|
1626
|
+
const rotated = withRotatedAddedFinalBreaks({
|
|
1627
|
+
tr,
|
|
1628
|
+
batchRevisionIds,
|
|
1629
|
+
revisionSeed,
|
|
1630
|
+
author,
|
|
1631
|
+
date,
|
|
1632
|
+
initials
|
|
1633
|
+
});
|
|
1634
|
+
tr = rotated.transaction;
|
|
1635
|
+
revisionSeed = rotated.nextRevisionId;
|
|
1413
1636
|
if (revisionStamp) requestDeterministicParaIds(tr, `${revisionStamp.date}:${String(revisionStamp.idSeed)}`);
|
|
1414
1637
|
view.dispatch(tr);
|
|
1415
1638
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { FolioTableTemplates } from "./table-template.js";
|
|
1
3
|
import { FolioAIBlock, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
|
|
2
4
|
import { FolioRevisionStamp, FolioWordDiffOptions } from "./apply.js";
|
|
3
|
-
import { document_d_exports } from "../types/document.js";
|
|
4
5
|
import { FolioDocumentOperationBatch, FolioDocumentOperationResult, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult } from "../document-operations.js";
|
|
5
6
|
import { FolioReviewChange, FolioReviewChangeKind } from "./read.js";
|
|
7
|
+
import { FolioStoryTable } from "./snapshot.js";
|
|
8
|
+
import { TableGeometryPairing } from "./table-geometry.js";
|
|
9
|
+
import { Node } from "prosemirror-model";
|
|
6
10
|
//#region src/ai-edits/headless.d.ts
|
|
7
11
|
/** Options for {@link FolioDocxReviewer.fromBuffer}. */
|
|
8
12
|
type FolioDocxReviewerOptions = {
|
|
@@ -76,6 +80,15 @@ type FolioReadReviewedStoryOptions = {
|
|
|
76
80
|
story?: FolioEditableDocumentStoryHandle;
|
|
77
81
|
view?: FolioReviewedView;
|
|
78
82
|
};
|
|
83
|
+
/** What {@link FolioDocxReviewer.matchStoryTableGeometry} needs to move a table's properties. */
|
|
84
|
+
type FolioMatchStoryTableGeometryOptions = {
|
|
85
|
+
story?: FolioEditableDocumentStoryHandle;
|
|
86
|
+
/** The other document's tables, by the index its snapshot numbers them with. */
|
|
87
|
+
targetTables: ReadonlyMap<number, Node>;
|
|
88
|
+
/** Base cells and the target cells they were aligned with. */
|
|
89
|
+
pairings: readonly TableGeometryPairing[];
|
|
90
|
+
revisionStamp: FolioRevisionStamp;
|
|
91
|
+
};
|
|
79
92
|
type FolioResolveReviewedStoryOptions = {
|
|
80
93
|
story?: FolioEditableDocumentStoryHandle;
|
|
81
94
|
view: FolioResolvedReviewedView;
|
|
@@ -100,6 +113,14 @@ declare class FolioDocumentStoryNotFoundError extends FolioDocumentStoryNotFound
|
|
|
100
113
|
type FolioApplyDocumentOperationsToStoryOptions = FolioApplyDocumentOperationsOptions & {
|
|
101
114
|
story: FolioEditableDocumentStoryHandle;
|
|
102
115
|
batch: FolioDocumentOperationBatch;
|
|
116
|
+
/**
|
|
117
|
+
* Tables and rows an `insertTable` / `insertTableRow` in the batch places
|
|
118
|
+
* verbatim, by operation id. A caller copying a table it already holds —
|
|
119
|
+
* `compareDocx` placing the target document's table — hands it over whole
|
|
120
|
+
* instead of letting the operation rebuild it from its cell texts and lose
|
|
121
|
+
* every table, row and cell property on the way.
|
|
122
|
+
*/
|
|
123
|
+
tableTemplates?: FolioTableTemplates;
|
|
103
124
|
};
|
|
104
125
|
declare const isFolioReviewedView: (value: unknown) => value is FolioReviewedView;
|
|
105
126
|
declare const isFolioResolvedReviewedView: (value: unknown) => value is FolioResolvedReviewedView;
|
|
@@ -201,6 +222,29 @@ declare class FolioDocxReviewer {
|
|
|
201
222
|
getDocumentProperties(): Readonly<NonNullable<document_d_exports.Document["package"]["properties"]>> | null;
|
|
202
223
|
/** Snapshot one editable story into stable, operation-ready blocks. */
|
|
203
224
|
snapshotStory(story: FolioEditableDocumentStoryHandle): FolioAIEditSnapshot | null;
|
|
225
|
+
/**
|
|
226
|
+
* One story's tables, in the document order {@link snapshotStory} numbers
|
|
227
|
+
* them with, read through a reviewed view.
|
|
228
|
+
*
|
|
229
|
+
* A block snapshot says which table a paragraph sits in and nothing about
|
|
230
|
+
* the table itself. A caller that has to reproduce one — a comparison
|
|
231
|
+
* copying the target's table into the base, or checking that accepting its
|
|
232
|
+
* own redline reproduced it — needs the node: its `w:tblPr`, its `w:tblGrid`
|
|
233
|
+
* widths, and every row's and cell's properties.
|
|
234
|
+
*/
|
|
235
|
+
storyTables({ story, view }?: FolioReadReviewedStoryOptions): readonly FolioStoryTable[];
|
|
236
|
+
/**
|
|
237
|
+
* Move the paired tables' own properties onto this story's tables, as
|
|
238
|
+
* tracked property changes.
|
|
239
|
+
*
|
|
240
|
+
* `w:tblPr`, `w:trPr` and `w:tcPr` belong to no block, so no block operation
|
|
241
|
+
* can carry them: a table that stayed in place while its widths, shading,
|
|
242
|
+
* borders or header row changed reads as unedited. Each difference is
|
|
243
|
+
* written as the target's property set plus a `w:tblPrChange` /
|
|
244
|
+
* `w:trPrChange` / `w:tcPrChange` holding the previous one, which is what a
|
|
245
|
+
* reject restores. A set that already agrees produces no revision.
|
|
246
|
+
*/
|
|
247
|
+
matchStoryTableGeometry({ story, targetTables, pairings, revisionStamp }: FolioMatchStoryTableGeometryOptions): number;
|
|
204
248
|
/** Read one story through an immutable reviewed-view projection. */
|
|
205
249
|
readReviewedStory(options?: FolioReadReviewedStoryOptions): FolioReviewedStory | null;
|
|
206
250
|
/** Resolve one editable story to its original or final state. */
|
|
@@ -219,7 +263,7 @@ declare class FolioDocxReviewer {
|
|
|
219
263
|
*/
|
|
220
264
|
applyDocumentOperations(batch: FolioDocumentOperationBatch, options?: FolioApplyDocumentOperationsOptions): FolioDocumentOperationResult;
|
|
221
265
|
/** Apply a versioned operation batch to one editable document story. */
|
|
222
|
-
applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff }: FolioApplyDocumentOperationsToStoryOptions): FolioDocumentOperationResult;
|
|
266
|
+
applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates }: FolioApplyDocumentOperationsToStoryOptions): FolioDocumentOperationResult;
|
|
223
267
|
private applyDocumentOperationsInternal;
|
|
224
268
|
/** Undo the latest unchanged document-operation batch and its created comments. */
|
|
225
269
|
undoDocumentOperations(undoHandle: FolioDocumentOperationUndoHandle): FolioDocumentOperationUndoResult;
|
|
@@ -382,4 +426,4 @@ type ApplyFolioAIEditsToBufferResult = FolioAIEditApplyResult & {
|
|
|
382
426
|
*/
|
|
383
427
|
declare const applyFolioAIEditsToBuffer: (buffer: ArrayBuffer, operations: FolioAIEditOperation[], options?: ApplyFolioAIEditsToBufferOptions) => Promise<ApplyFolioAIEditsToBufferResult>;
|
|
384
428
|
//#endregion
|
|
385
|
-
export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioGetContentAsTextOptions, FolioNumberingLevel, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, type FolioRevisionStamp, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
|
|
429
|
+
export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioGetContentAsTextOptions, FolioMatchStoryTableGeometryOptions, FolioNumberingLevel, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, type FolioRevisionStamp, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
|
|
@@ -14,7 +14,8 @@ import { schema, singletonManager } from "../prosemirror/schema/index.js";
|
|
|
14
14
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
15
15
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
16
16
|
import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
17
|
-
import { createFolioAIEditSnapshot, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
17
|
+
import { createFolioAIEditSnapshot, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
18
|
+
import { matchTableGeometry } from "./table-geometry.js";
|
|
18
19
|
import { TaggedError } from "better-result";
|
|
19
20
|
import { Fragment } from "prosemirror-model";
|
|
20
21
|
import { EditorState } from "prosemirror-state";
|
|
@@ -314,6 +315,53 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
314
315
|
const state = this.getEditableStoryState(story);
|
|
315
316
|
return state ? createFolioAIEditSnapshot(state.doc) : null;
|
|
316
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* One story's tables, in the document order {@link snapshotStory} numbers
|
|
320
|
+
* them with, read through a reviewed view.
|
|
321
|
+
*
|
|
322
|
+
* A block snapshot says which table a paragraph sits in and nothing about
|
|
323
|
+
* the table itself. A caller that has to reproduce one — a comparison
|
|
324
|
+
* copying the target's table into the base, or checking that accepting its
|
|
325
|
+
* own redline reproduced it — needs the node: its `w:tblPr`, its `w:tblGrid`
|
|
326
|
+
* widths, and every row's and cell's properties.
|
|
327
|
+
*/
|
|
328
|
+
storyTables({ story = MAIN_STORY, view = "final" } = {}) {
|
|
329
|
+
if (!isFolioReviewedView(view)) throw new UnsupportedFolioReviewedViewError({
|
|
330
|
+
message: "Unsupported reviewed document view.",
|
|
331
|
+
receivedView: view
|
|
332
|
+
});
|
|
333
|
+
const sourceState = this.getEditableStoryState(story);
|
|
334
|
+
return sourceState ? folioStoryTables(resolveReviewedState(sourceState, view).doc) : [];
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Move the paired tables' own properties onto this story's tables, as
|
|
338
|
+
* tracked property changes.
|
|
339
|
+
*
|
|
340
|
+
* `w:tblPr`, `w:trPr` and `w:tcPr` belong to no block, so no block operation
|
|
341
|
+
* can carry them: a table that stayed in place while its widths, shading,
|
|
342
|
+
* borders or header row changed reads as unedited. Each difference is
|
|
343
|
+
* written as the target's property set plus a `w:tblPrChange` /
|
|
344
|
+
* `w:trPrChange` / `w:tcPrChange` holding the previous one, which is what a
|
|
345
|
+
* reject restores. A set that already agrees produces no revision.
|
|
346
|
+
*/
|
|
347
|
+
matchStoryTableGeometry({ story = MAIN_STORY, targetTables, pairings, revisionStamp }) {
|
|
348
|
+
const state = this.getEditableStoryState(story);
|
|
349
|
+
if (!state || pairings.length === 0) return revisionStamp.idSeed;
|
|
350
|
+
const transaction = state.tr;
|
|
351
|
+
const { nextRevisionId } = matchTableGeometry({
|
|
352
|
+
tr: transaction,
|
|
353
|
+
baseTables: folioStoryTables(state.doc),
|
|
354
|
+
targetTables,
|
|
355
|
+
pairings,
|
|
356
|
+
revision: {
|
|
357
|
+
author: this.author,
|
|
358
|
+
date: revisionStamp.date,
|
|
359
|
+
idSeed: revisionStamp.idSeed
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
if (transaction.docChanged) this.setEditableStoryState(story, state.apply(transaction));
|
|
363
|
+
return nextRevisionId;
|
|
364
|
+
}
|
|
317
365
|
/** Read one story through an immutable reviewed-view projection. */
|
|
318
366
|
readReviewedStory(options = {}) {
|
|
319
367
|
const story = options.story ?? MAIN_STORY;
|
|
@@ -388,17 +436,18 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
388
436
|
});
|
|
389
437
|
}
|
|
390
438
|
/** Apply a versioned operation batch to one editable document story. */
|
|
391
|
-
applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff }) {
|
|
439
|
+
applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates }) {
|
|
392
440
|
return this.applyDocumentOperationsInternal({
|
|
393
441
|
story,
|
|
394
442
|
batch,
|
|
395
443
|
...snapshot !== void 0 && { snapshot },
|
|
396
444
|
...revisionStamp !== void 0 && { revisionStamp },
|
|
397
445
|
...wordDiff !== void 0 && { wordDiff },
|
|
446
|
+
...tableTemplates !== void 0 && { tableTemplates },
|
|
398
447
|
createUndoEntry: true
|
|
399
448
|
});
|
|
400
449
|
}
|
|
401
|
-
applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, createUndoEntry }) {
|
|
450
|
+
applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, createUndoEntry }) {
|
|
402
451
|
const beforeState = this.requireEditableStoryState(story);
|
|
403
452
|
const createdCommentsLengthBefore = this.createdComments.length;
|
|
404
453
|
const view = {
|
|
@@ -415,6 +464,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
415
464
|
author: this.author,
|
|
416
465
|
...revisionStamp !== void 0 && { revisionStamp },
|
|
417
466
|
...wordDiff !== void 0 && { wordDiff },
|
|
467
|
+
...tableTemplates !== void 0 && { tableTemplates },
|
|
418
468
|
createCommentId: (text) => {
|
|
419
469
|
const comment = createReviewerComment(this.nextCommentId(), text, this.author);
|
|
420
470
|
this.createdComments.push(comment);
|