@stll/folio-core 0.33.1 → 0.33.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.d.ts +15 -0
- package/dist/ai-edits/apply.js +134 -25
- 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 +25 -3
- 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 +7 -0
- 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/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 +88 -17
- package/dist/compat/eigenpal.d.ts +4 -4
- package/dist/compat/eigenpal.js +2 -2
- package/dist/display-list/primitives.d.ts +1 -1
- package/dist/document-operations.d.ts +10 -1
- package/dist/document-operations.js +3 -2
- 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/paragraphParser.js +3 -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 +34 -29
- package/dist/docx/serializer/noteSerializer.js +34 -30
- package/dist/docx/serializer/numberingSerializer.js +8 -4
- package/dist/docx/serializer/paragraphSerializer.js +3 -3
- 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/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.js +4 -3
- package/dist/docx/xmlParser.d.ts +23 -2
- package/dist/docx/xmlParser.js +43 -11
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/prosemirror/commands/comments.js +2 -1
- 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 +15 -4
- package/dist/prosemirror/conversion/toProseDoc.js +12 -3
- package/dist/prosemirror/extensions/nodes/TableExtension.js +5 -0
- package/dist/prosemirror/schema/marks.d.ts +1 -1
- 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/formatToStyle.d.ts +1 -1
- 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
1
|
import { expectParagraphAttrs, 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";
|
|
@@ -364,9 +366,11 @@ const ordinalAmongSameHash = (snapshot, blockId) => {
|
|
|
364
366
|
const SIGNATURE_LINE = "_".repeat(28);
|
|
365
367
|
/**
|
|
366
368
|
* 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
|
|
369
|
+
* tables. In tracked mode every row carries `trIns` and every run inside it
|
|
370
|
+
* carries `w:ins`, which is how the format says "this table is new": there is
|
|
371
|
+
* no whole-table insertion element, only rows that were inserted, and a
|
|
372
|
+
* consumer that reads only run-level revisions keeps the text of a rejected
|
|
373
|
+
* insertion when the row marker stands alone.
|
|
370
374
|
*/
|
|
371
375
|
const buildTableNode = ({ schema, rows, revision }) => {
|
|
372
376
|
const paragraphType = schema.nodes["paragraph"];
|
|
@@ -374,7 +378,16 @@ const buildTableNode = ({ schema, rows, revision }) => {
|
|
|
374
378
|
const rowType = schema.nodes["tableRow"];
|
|
375
379
|
const tableType = schema.nodes["table"];
|
|
376
380
|
if (!paragraphType || !cellType || !rowType || !tableType || rows.length === 0) return null;
|
|
377
|
-
const
|
|
381
|
+
const insertionType = schema.marks["insertion"];
|
|
382
|
+
const marks = revision && insertionType ? [insertionType.create({
|
|
383
|
+
revisionId: revision.revisionId,
|
|
384
|
+
author: revision.author,
|
|
385
|
+
date: revision.date,
|
|
386
|
+
...revision.initials != null && { initials: revision.initials },
|
|
387
|
+
...revision.provenance != null && { provenance: revision.provenance },
|
|
388
|
+
...revision.suggestionId != null && { suggestionId: revision.suggestionId }
|
|
389
|
+
})] : void 0;
|
|
390
|
+
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
391
|
return tableType.create(null, rowNodes);
|
|
379
392
|
};
|
|
380
393
|
/**
|
|
@@ -468,6 +481,75 @@ const isBatchableParagraphInsertion = (item, mode) => {
|
|
|
468
481
|
const isEmptyInsert = insertTexts.length === 1 && insertTexts[0]?.length === 0;
|
|
469
482
|
return !(mode === "tracked-changes" && item.operation.pageBreakBefore === true && isEmptyInsert);
|
|
470
483
|
};
|
|
484
|
+
/**
|
|
485
|
+
* Move an ADDED paragraph break off every paragraph its container ends with.
|
|
486
|
+
*
|
|
487
|
+
* A container's final mark carries no revision in either direction: nothing
|
|
488
|
+
* follows it, so "join this paragraph with the one after it" and its mirror
|
|
489
|
+
* both state an edit that cannot be carried out, and a reader is left with a
|
|
490
|
+
* revision neither accepting nor rejecting everything can clear.
|
|
491
|
+
*
|
|
492
|
+
* Appending at a container's end therefore rotates exactly as removing from it
|
|
493
|
+
* does. The break that was added sits between the paragraph the run was
|
|
494
|
+
* appended after and the first appended one, so THAT paragraph's mark is the
|
|
495
|
+
* inserted one, each appended paragraph but the last keeps an inserted mark of
|
|
496
|
+
* its own, and the paragraph the container now ends with takes the free mark
|
|
497
|
+
* the run was appended after. Only which paragraph is left markless changes.
|
|
498
|
+
*
|
|
499
|
+
* A paragraph's properties live on its mark, so the paragraph that inherits
|
|
500
|
+
* the free one records the other's as `w:pPrChange` — what a rejection reads
|
|
501
|
+
* to put the container's ending back the way it was.
|
|
502
|
+
*
|
|
503
|
+
* This runs once over the finished document rather than at each insertion:
|
|
504
|
+
* which paragraph ends a container is only settled when the batch is, and an
|
|
505
|
+
* insertion that looked final was undone by the next operation writing a table
|
|
506
|
+
* after it.
|
|
507
|
+
*/
|
|
508
|
+
const withRotatedAddedFinalBreaks = ({ tr, batchRevisionIds, revisionSeed, author, date, initials }) => {
|
|
509
|
+
const paragraphTypeName = tr.doc.type.schema.nodes["paragraph"]?.name ?? "paragraph";
|
|
510
|
+
const rotations = finalParagraphsOf(tr.doc, paragraphTypeName).filter(({ node }) => {
|
|
511
|
+
const mark = node.attrs["pPrMark"];
|
|
512
|
+
if (typeof mark !== "object" || mark === null || !("kind" in mark)) return false;
|
|
513
|
+
if (mark.kind !== "ins" && mark.kind !== "moveTo") return false;
|
|
514
|
+
const info = "info" in mark ? mark.info : void 0;
|
|
515
|
+
const revisionId = typeof info === "object" && info !== null && "id" in info ? info.id : void 0;
|
|
516
|
+
return typeof revisionId === "number" && batchRevisionIds.has(revisionId);
|
|
517
|
+
});
|
|
518
|
+
let next = tr;
|
|
519
|
+
let nextRevisionId = revisionSeed;
|
|
520
|
+
for (const { position: finalPosition, node: final } of rotations) {
|
|
521
|
+
const carrier = addedBreakCarrierBefore(next.doc.resolve(finalPosition), final.type.name);
|
|
522
|
+
if (!carrier) {
|
|
523
|
+
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
next = next.setNodeAttribute(carrier.position, "pPrMark", final.attrs["pPrMark"]);
|
|
527
|
+
const previousFormatting = paragraphPropertiesSnapshot(carrier.node);
|
|
528
|
+
if (JSON.stringify(previousFormatting) === JSON.stringify(paragraphPropertiesSnapshot(final))) {
|
|
529
|
+
next = next.setNodeAttribute(finalPosition, "pPrMark", null);
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
const existing = expectParagraphAttrs(final)._propertyChanges;
|
|
533
|
+
next = next.setNodeMarkup(finalPosition, void 0, {
|
|
534
|
+
...final.attrs,
|
|
535
|
+
pPrMark: null,
|
|
536
|
+
_propertyChanges: [...Array.isArray(existing) ? existing : [], {
|
|
537
|
+
type: "paragraphPropertyChange",
|
|
538
|
+
info: {
|
|
539
|
+
id: nextRevisionId++,
|
|
540
|
+
author,
|
|
541
|
+
date,
|
|
542
|
+
...initials ? { initials } : {}
|
|
543
|
+
},
|
|
544
|
+
previousFormatting
|
|
545
|
+
}]
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
transaction: next,
|
|
550
|
+
nextRevisionId
|
|
551
|
+
};
|
|
552
|
+
};
|
|
471
553
|
const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, commentMark, suggestionId, revisionSeed, isPairedMove }) => {
|
|
472
554
|
const operation = item.operation;
|
|
473
555
|
if (operation.type !== "insertAfterBlock" && operation.type !== "insertBeforeBlock") panic("Only paragraph insertions can build inserted paragraphs", { type: operation.type });
|
|
@@ -551,7 +633,7 @@ const buildInsertedParagraphs = ({ item, schema, mode, author, date, initials, c
|
|
|
551
633
|
nextRevisionId
|
|
552
634
|
};
|
|
553
635
|
};
|
|
554
|
-
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff }) => {
|
|
636
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionStamp, revisionIdSeed, wordDiff, tableTemplates }) => {
|
|
555
637
|
const applied = [];
|
|
556
638
|
const skipped = [];
|
|
557
639
|
const normalizations = [];
|
|
@@ -978,11 +1060,13 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
978
1060
|
continue;
|
|
979
1061
|
}
|
|
980
1062
|
const revision = structuralRevision;
|
|
1063
|
+
const template = tableTemplates?.get(item.operation.id);
|
|
981
1064
|
const result = applyTableRowInsertion({
|
|
982
1065
|
tr,
|
|
983
1066
|
insertion,
|
|
984
1067
|
cellTexts: item.operation.cellTexts,
|
|
985
|
-
revision
|
|
1068
|
+
revision,
|
|
1069
|
+
...template !== void 0 && { template }
|
|
986
1070
|
});
|
|
987
1071
|
if (result.type === "unsupported") {
|
|
988
1072
|
skipped.push({
|
|
@@ -1172,7 +1256,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1172
1256
|
case "deleteBlock":
|
|
1173
1257
|
if (mode === "direct") {
|
|
1174
1258
|
const at = tr.doc.resolve(item.blockFrom);
|
|
1175
|
-
tr = !(at
|
|
1259
|
+
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
1260
|
break;
|
|
1177
1261
|
}
|
|
1178
1262
|
if (deletionType) {
|
|
@@ -1190,8 +1274,7 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1190
1274
|
const inlineRevisionApplied = item.from < item.to || atomRanges.length > 0;
|
|
1191
1275
|
appliedRevisionIds = inlineRevisionApplied ? [revisionId] : [];
|
|
1192
1276
|
const markPosition = tr.mapping.map(item.blockFrom);
|
|
1193
|
-
|
|
1194
|
-
if (!(markPlace.index() === markPlace.parent.childCount - 1) && tr.doc.nodeAt(markPosition)?.attrs["pPrMark"] == null) {
|
|
1277
|
+
if (!paragraphEndsItsContainer(tr.doc.resolve(markPosition), item.blockNode.type.name) && tr.doc.nodeAt(markPosition)?.attrs["pPrMark"] == null) {
|
|
1195
1278
|
const markRevisionId = revisionSeed++;
|
|
1196
1279
|
tr = tr.setNodeAttribute(markPosition, "pPrMark", {
|
|
1197
1280
|
kind: isPairedMove(item.operation.moveId) ? "moveFrom" : "del",
|
|
@@ -1208,15 +1291,21 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1208
1291
|
if (commentMark) tr = tr.addMark(item.from, item.to, commentMark);
|
|
1209
1292
|
break;
|
|
1210
1293
|
case "insertTable": {
|
|
1211
|
-
const
|
|
1294
|
+
const revision = {
|
|
1295
|
+
revisionId: revisionSeed,
|
|
1296
|
+
author,
|
|
1297
|
+
date,
|
|
1298
|
+
...trackedRevisionExtras
|
|
1299
|
+
};
|
|
1300
|
+
const template = tableTemplates?.get(item.operation.id);
|
|
1301
|
+
const table = (template === void 0 ? null : tableFromTemplate({
|
|
1302
|
+
schema: view.state.schema,
|
|
1303
|
+
template,
|
|
1304
|
+
...producesTrackedChanges && { revision }
|
|
1305
|
+
})) ?? buildTableNode({
|
|
1212
1306
|
schema: view.state.schema,
|
|
1213
1307
|
rows: item.operation.rows,
|
|
1214
|
-
...producesTrackedChanges && { revision
|
|
1215
|
-
revisionId: revisionSeed,
|
|
1216
|
-
author,
|
|
1217
|
-
date,
|
|
1218
|
-
...trackedRevisionExtras
|
|
1219
|
-
} }
|
|
1308
|
+
...producesTrackedChanges && { revision }
|
|
1220
1309
|
});
|
|
1221
1310
|
if (!table) {
|
|
1222
1311
|
skipped.push({
|
|
@@ -1281,13 +1370,23 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1281
1370
|
});
|
|
1282
1371
|
continue;
|
|
1283
1372
|
}
|
|
1284
|
-
for (const rowPosition of rowPositions.toReversed())
|
|
1373
|
+
for (const rowPosition of rowPositions.toReversed()) {
|
|
1374
|
+
tr = tr.setNodeAttribute(rowPosition, "trDel", revision);
|
|
1375
|
+
markTableRowContent({
|
|
1376
|
+
tr,
|
|
1377
|
+
rowPosition,
|
|
1378
|
+
kind: "deletion",
|
|
1379
|
+
revision
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1285
1382
|
appliedRevisionIds = [revision.revisionId];
|
|
1286
1383
|
markStructuralChange(tr);
|
|
1287
1384
|
break;
|
|
1288
1385
|
}
|
|
1289
1386
|
case "setBlockParagraphProperties": {
|
|
1290
|
-
const
|
|
1387
|
+
const blockPosition = tr.mapping.map(item.blockFrom);
|
|
1388
|
+
const liveBlock = tr.doc.nodeAt(blockPosition) ?? item.blockNode;
|
|
1389
|
+
const patch = paragraphPropertiesPatch(liveBlock, item.operation.properties);
|
|
1291
1390
|
if (patch === null) {
|
|
1292
1391
|
skipped.push({
|
|
1293
1392
|
id: item.operation.id,
|
|
@@ -1296,8 +1395,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1296
1395
|
continue;
|
|
1297
1396
|
}
|
|
1298
1397
|
if (mode === "direct") {
|
|
1299
|
-
tr = tr.setNodeMarkup(
|
|
1300
|
-
...
|
|
1398
|
+
tr = tr.setNodeMarkup(blockPosition, void 0, {
|
|
1399
|
+
...liveBlock.attrs,
|
|
1301
1400
|
...patch
|
|
1302
1401
|
});
|
|
1303
1402
|
break;
|
|
@@ -1311,11 +1410,11 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1311
1410
|
date,
|
|
1312
1411
|
...initials ? { initials } : {}
|
|
1313
1412
|
},
|
|
1314
|
-
previousFormatting: paragraphPropertiesSnapshot(
|
|
1413
|
+
previousFormatting: paragraphPropertiesSnapshot(liveBlock)
|
|
1315
1414
|
};
|
|
1316
|
-
const existing = expectParagraphAttrs(
|
|
1317
|
-
tr = tr.setNodeMarkup(
|
|
1318
|
-
...
|
|
1415
|
+
const existing = expectParagraphAttrs(liveBlock)._propertyChanges;
|
|
1416
|
+
tr = tr.setNodeMarkup(blockPosition, void 0, {
|
|
1417
|
+
...liveBlock.attrs,
|
|
1319
1418
|
...patch,
|
|
1320
1419
|
_propertyChanges: [...Array.isArray(existing) ? existing : [], change]
|
|
1321
1420
|
});
|
|
@@ -1410,6 +1509,16 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1410
1509
|
if (tr.docChanged) {
|
|
1411
1510
|
const batchRevisionIds = new Set(applied.flatMap(({ revisionIds }) => revisionIds ?? []));
|
|
1412
1511
|
tr = withoutRevisionsOnZeroWidthAnchors(tr, batchRevisionIds);
|
|
1512
|
+
const rotated = withRotatedAddedFinalBreaks({
|
|
1513
|
+
tr,
|
|
1514
|
+
batchRevisionIds,
|
|
1515
|
+
revisionSeed,
|
|
1516
|
+
author,
|
|
1517
|
+
date,
|
|
1518
|
+
initials
|
|
1519
|
+
});
|
|
1520
|
+
tr = rotated.transaction;
|
|
1521
|
+
revisionSeed = rotated.nextRevisionId;
|
|
1413
1522
|
if (revisionStamp) requestDeterministicParaIds(tr, `${revisionStamp.date}:${String(revisionStamp.idSeed)}`);
|
|
1414
1523
|
view.dispatch(tr);
|
|
1415
1524
|
}
|
|
@@ -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);
|
package/dist/ai-edits/index.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolv
|
|
|
5
5
|
import { buildAnnotatedBlockText } from "./clean-text.js";
|
|
6
6
|
import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
|
|
7
7
|
import { FolioCommentAnchor, FolioReviewChange, FolioReviewChangeKind, getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
|
|
8
|
-
import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsToStoryOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
|
|
9
8
|
import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
9
|
+
import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsToStoryOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
|
|
10
10
|
import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
|
|
11
11
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
12
12
|
export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineFormatting, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsToStoryOptions, type FolioCommentAnchor, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioEditableDocumentStoryHandle, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeKind, type FolioReviewedStory, type FolioReviewedView, type FolioRevisionStamp, type FolioWordDiffOptions, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
|
|
@@ -50,6 +50,24 @@ declare const createFolioAITextRangeHandle: ({ blockId, text, startOffset, endOf
|
|
|
50
50
|
* whole subtree, so a table nested inside a hidden row is hidden with it.
|
|
51
51
|
*/
|
|
52
52
|
declare const isHiddenTableRow: (node: Node) => boolean;
|
|
53
|
+
/** One table of a story, numbered the way {@link createFolioAIEditSnapshot} numbers it. */
|
|
54
|
+
type FolioStoryTable = {
|
|
55
|
+
/** Document-order index over every table, nested ones included. */
|
|
56
|
+
index: number;
|
|
57
|
+
/** The table node's position in the story document. */
|
|
58
|
+
start: number;
|
|
59
|
+
node: Node;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Every table of one story in document order, nested tables included and
|
|
63
|
+
* hidden rows' subtrees excluded.
|
|
64
|
+
*
|
|
65
|
+
* The single place a story's tables are numbered. `table.tableIndex` on a
|
|
66
|
+
* block, the geometry a comparison copies out of the target and the geometry
|
|
67
|
+
* it checks its own work against all read this list, so no second walk can
|
|
68
|
+
* number the same document differently.
|
|
69
|
+
*/
|
|
70
|
+
declare const folioStoryTables: (doc: Node) => FolioStoryTable[];
|
|
53
71
|
declare const createFolioAIEditSnapshot: (doc: Node) => FolioAIEditSnapshot;
|
|
54
72
|
//#endregion
|
|
55
|
-
export { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, trailingBodyBlockId };
|
|
73
|
+
export { FolioStoryTable, createFolioAIEditSnapshot, createFolioAITextRangeHandle, folioStoryTables, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, trailingBodyBlockId };
|
|
@@ -69,6 +69,29 @@ const TABLE_ROLE_ROW = "row";
|
|
|
69
69
|
*/
|
|
70
70
|
const isHiddenTableRow = (node) => node.type.name === TABLE_ROW_NODE_NAME && node.attrs["hidden"] === true;
|
|
71
71
|
/**
|
|
72
|
+
* Every table of one story in document order, nested tables included and
|
|
73
|
+
* hidden rows' subtrees excluded.
|
|
74
|
+
*
|
|
75
|
+
* The single place a story's tables are numbered. `table.tableIndex` on a
|
|
76
|
+
* block, the geometry a comparison copies out of the target and the geometry
|
|
77
|
+
* it checks its own work against all read this list, so no second walk can
|
|
78
|
+
* number the same document differently.
|
|
79
|
+
*/
|
|
80
|
+
const folioStoryTables = (doc) => {
|
|
81
|
+
const tables = [];
|
|
82
|
+
doc.descendants((node, pos) => {
|
|
83
|
+
if (node.isTextblock) return false;
|
|
84
|
+
if (isHiddenTableRow(node)) return false;
|
|
85
|
+
if (node.type.spec["tableRole"] === TABLE_ROLE_TABLE) tables.push({
|
|
86
|
+
index: tables.length,
|
|
87
|
+
start: pos,
|
|
88
|
+
node
|
|
89
|
+
});
|
|
90
|
+
return true;
|
|
91
|
+
});
|
|
92
|
+
return tables;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
72
95
|
* Locate the block inside its innermost enclosing table, or `undefined` when
|
|
73
96
|
* it sits outside every table.
|
|
74
97
|
*/
|
|
@@ -102,7 +125,7 @@ const createFolioAIEditSnapshot = (doc) => {
|
|
|
102
125
|
const draftBlocks = [];
|
|
103
126
|
const hashCounts = /* @__PURE__ */ new Map();
|
|
104
127
|
const usedBlockIds = /* @__PURE__ */ new Set();
|
|
105
|
-
const tableIndexByStart =
|
|
128
|
+
const tableIndexByStart = new Map(folioStoryTables(doc).map(({ start, index }) => [start, index]));
|
|
106
129
|
const path = [];
|
|
107
130
|
let blockIndex = 0;
|
|
108
131
|
let blankIndex = 0;
|
|
@@ -110,7 +133,6 @@ const createFolioAIEditSnapshot = (doc) => {
|
|
|
110
133
|
while (pos >= (path.at(-1)?.end ?? Number.POSITIVE_INFINITY)) path.pop();
|
|
111
134
|
if (!node.isTextblock) {
|
|
112
135
|
if (isHiddenTableRow(node)) return false;
|
|
113
|
-
if (node.type.spec["tableRole"] === TABLE_ROLE_TABLE) tableIndexByStart.set(pos, tableIndexByStart.size);
|
|
114
136
|
if (!node.isLeaf) path.push({
|
|
115
137
|
node,
|
|
116
138
|
start: pos,
|
|
@@ -332,4 +354,4 @@ const getColorFromAttrs = (attrs) => {
|
|
|
332
354
|
const samePreviewRunStyle = (run, style) => run.bold === style.bold && run.italic === style.italic && run.underline === style.underline && run.strike === style.strike && run.fontFamily === style.fontFamily && run.fontSizePt === style.fontSizePt && run.color === style.color;
|
|
333
355
|
const isUnstyledPreviewRun = ({ bold, italic, underline, strike, fontFamily, fontSizePt, color }) => bold === void 0 && italic === void 0 && underline === void 0 && strike === void 0 && fontFamily === void 0 && fontSizePt === void 0 && color === void 0;
|
|
334
356
|
//#endregion
|
|
335
|
-
export { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, trailingBodyBlockId };
|
|
357
|
+
export { createFolioAIEditSnapshot, createFolioAITextRangeHandle, folioStoryTables, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, trailingBodyBlockId };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { FolioStoryTable } from "./snapshot.js";
|
|
2
|
+
import { Transaction } from "prosemirror-state";
|
|
3
|
+
import { Node } from "prosemirror-model";
|
|
4
|
+
//#region src/ai-edits/table-geometry.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* One line per table, in the document order tables are numbered in: the
|
|
7
|
+
* table's own properties, then each row's and each cell's. Nested tables get
|
|
8
|
+
* their own lines rather than being folded into their parent's, so a
|
|
9
|
+
* difference names the table it is in.
|
|
10
|
+
*/
|
|
11
|
+
declare const projectTableGeometry: (tables: readonly FolioStoryTable[]) => string[];
|
|
12
|
+
/** Where a cell sits: which table, which row of it, which cell of that row. */
|
|
13
|
+
type TableCellCoordinate = {
|
|
14
|
+
tableIndex: number;
|
|
15
|
+
rowIndex: number;
|
|
16
|
+
cellIndex: number;
|
|
17
|
+
};
|
|
18
|
+
/** One base cell and the target cell it was aligned with. */
|
|
19
|
+
type TableGeometryPairing = {
|
|
20
|
+
base: TableCellCoordinate;
|
|
21
|
+
target: TableCellCoordinate;
|
|
22
|
+
};
|
|
23
|
+
type MatchTableGeometryOptions = {
|
|
24
|
+
tr: Transaction;
|
|
25
|
+
/** The base story's tables, with the positions the transaction will write at. */
|
|
26
|
+
baseTables: readonly FolioStoryTable[];
|
|
27
|
+
/** The target story's tables, by the same index the pairings name. */
|
|
28
|
+
targetTables: ReadonlyMap<number, Node>;
|
|
29
|
+
pairings: readonly TableGeometryPairing[];
|
|
30
|
+
revision: {
|
|
31
|
+
author: string;
|
|
32
|
+
date: string;
|
|
33
|
+
idSeed: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
type MatchTableGeometryResult = {
|
|
37
|
+
/** First revision id a following batch may allocate. */
|
|
38
|
+
nextRevisionId: number;
|
|
39
|
+
/** Tables, rows and cells whose properties the match moved. */
|
|
40
|
+
matched: number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Copy a paired table's, row's and cell's properties from the document it was
|
|
44
|
+
* compared against, recording the previous set so a reject restores it.
|
|
45
|
+
*
|
|
46
|
+
* Pairings name cells, because that is what the block alignment resolves; the
|
|
47
|
+
* row and the table each cell sits in are matched with it. A property set that
|
|
48
|
+
* already agrees is left alone, so an unchanged table produces no revision.
|
|
49
|
+
*
|
|
50
|
+
* `colspan` / `rowspan` never move: they shape the table map, and changing one
|
|
51
|
+
* without restructuring the rows around it leaves the map inconsistent with
|
|
52
|
+
* its own grid. That is the rule a reject of a `w:tcPrChange` follows too.
|
|
53
|
+
*/
|
|
54
|
+
declare const matchTableGeometry: ({ tr, baseTables, targetTables, pairings, revision }: MatchTableGeometryOptions) => MatchTableGeometryResult;
|
|
55
|
+
//#endregion
|
|
56
|
+
export { MatchTableGeometryResult, TableCellCoordinate, TableGeometryPairing, matchTableGeometry, projectTableGeometry };
|