@stll/folio-core 0.11.0 → 0.13.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 +2 -1
- package/dist/ai-edits/apply.js +693 -63
- package/dist/ai-edits/block-identity.d.ts +4 -0
- package/dist/ai-edits/block-identity.js +9 -0
- package/dist/ai-edits/blockRange.d.ts +28 -1
- package/dist/ai-edits/blockRange.js +68 -2
- package/dist/ai-edits/clean-text.js +2 -1
- package/dist/ai-edits/headless.d.ts +4 -4
- package/dist/ai-edits/headless.js +9 -9
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/ai-edits/read.d.ts +7 -7
- package/dist/ai-edits/read.js +110 -3
- package/dist/ai-edits/snapshot.js +21 -3
- package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
- package/dist/ai-edits/table-cell-mutations.js +238 -0
- package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
- package/dist/ai-edits/table-mutation-plan.js +90 -0
- package/dist/ai-edits/table-row-column-mutations.d.ts +108 -0
- package/dist/ai-edits/table-row-column-mutations.js +300 -0
- package/dist/ai-edits/table-targets.d.ts +32 -0
- package/dist/ai-edits/table-targets.js +70 -0
- package/dist/ai-edits/types.d.ts +62 -1
- package/dist/ai-edits/word-diff.js +21 -0
- package/dist/compat/eigenpal.d.ts +3 -3
- package/dist/compat/eigenpal.js +3 -3
- package/dist/content-controls/mutateContentControls.js +1 -1
- package/dist/controller/activeEditorStory.d.ts +36 -0
- package/dist/controller/activeEditorStory.js +30 -0
- package/dist/controller/contentControlWidgetController.d.ts +55 -0
- package/dist/controller/contentControlWidgetController.js +141 -0
- package/dist/controller/fontReadiness.js +2 -0
- package/dist/controller/headerFooterEditorManager.js +2 -2
- package/dist/controller/hiddenEditorApi.js +2 -2
- package/dist/controller/hiddenEditorManager.d.ts +12 -1
- package/dist/controller/hiddenEditorManager.js +44 -12
- package/dist/controller/layoutPipeline.d.ts +2 -0
- package/dist/controller/layoutPipeline.js +17 -2
- package/dist/controller/noteEditorManager.d.ts +36 -0
- package/dist/controller/noteEditorManager.js +210 -0
- package/dist/document-operations.d.ts +43 -11
- package/dist/document-operations.js +186 -18
- package/dist/docx/blockContentParser.js +21 -7
- package/dist/docx/commentParser.js +6 -1
- package/dist/docx/commentRangeIntegrity.d.ts +6 -0
- package/dist/docx/commentRangeIntegrity.js +186 -0
- package/dist/docx/conformance.js +62 -1
- package/dist/docx/documentGrid.d.ts +8 -0
- package/dist/docx/documentGrid.js +8 -0
- package/dist/docx/drawingUtils.js +1 -1
- package/dist/docx/encryption/agileDecryption.js +12 -2
- package/dist/docx/encryption/compoundFile.js +6 -1
- package/dist/docx/encryption/encryptionInfo.d.ts +9 -1
- package/dist/docx/encryption/encryptionInfo.js +18 -2
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/ensureParaIds.js +8 -7
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/groupDrawingParser.js +10 -5
- package/dist/docx/hyperlinkParser.js +1 -1
- package/dist/docx/imageParser.js +10 -6
- package/dist/docx/metadataPrivacy.js +17 -3
- package/dist/docx/notePropertiesParser.js +1 -1
- package/dist/docx/numberingParser.js +1 -1
- package/dist/docx/paragraphParser.js +70 -61
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/parser.js +3 -3
- package/dist/docx/rezip.js +20 -9
- package/dist/docx/runParser.js +17 -6
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sectionParser.js +18 -5
- package/dist/docx/selectiveSave.js +5 -1
- package/dist/docx/selectiveXmlPatch.d.ts +22 -1
- package/dist/docx/selectiveXmlPatch.js +61 -1
- package/dist/docx/serializer/blockSdtSerializer.js +3 -2
- package/dist/docx/serializer/commentSerializer.js +5 -6
- package/dist/docx/serializer/documentSerializer.js +2 -2
- package/dist/docx/serializer/headerFooterSerializer.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +2 -2
- package/dist/docx/serializer/paragraphSerializer.js +23 -15
- package/dist/docx/serializer/runSerializer.js +25 -21
- package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -1
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/tableSerializer.d.ts +5 -4
- package/dist/docx/serializer/tableSerializer.js +26 -20
- package/dist/docx/serializer/xmlUtils.d.ts +13 -1
- package/dist/docx/serializer/xmlUtils.js +27 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
- package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
- package/dist/docx/server/boundedArchive.d.ts +14 -3
- package/dist/docx/server/boundedArchive.js +56 -11
- package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
- package/dist/docx/server/extractDocxText.js +55 -8
- package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
- package/dist/docx/server/inspectDocxPackage.js +217 -0
- package/dist/docx/server/validateDocxConformance.d.ts +41 -0
- package/dist/docx/server/validateDocxConformance.js +336 -0
- package/dist/docx/settingsParser.js +26 -3
- package/dist/docx/shapeParser.js +1 -1
- package/dist/docx/styleParser.js +4 -2
- package/dist/docx/tableParser.d.ts +8 -1
- package/dist/docx/tableParser.js +31 -10
- package/dist/docx/textBoxParser.d.ts +3 -3
- package/dist/docx/textBoxParser.js +16 -12
- package/dist/docx/unzip.js +1 -0
- package/dist/docx/vmlImageParser.js +11 -3
- package/dist/docx/xmlParser.js +11 -1
- package/dist/docx/xmlSafety.d.ts +5 -0
- package/dist/docx/xmlSafety.js +9 -0
- package/dist/fields/resolveFieldValues.js +1 -3
- package/dist/fonts/embeddedFonts.d.ts +33 -5
- package/dist/fonts/embeddedFonts.js +37 -8
- package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
- package/dist/i18n/messages/catalogs.gen.js +17 -0
- package/dist/i18n/messages/messages.gen.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
- package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +151 -20
- package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
- package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
- package/dist/layout-bridge/engine/clickToPosition.js +2 -2
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +58 -30
- package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
- package/dist/layout-engine/justificationCompatibility.js +8 -0
- package/dist/layout-engine/keep-together.js +11 -1
- package/dist/layout-engine/measure/cache.js +23 -5
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +8 -2
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +2 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +56 -37
- package/dist/layout-engine/measure/measureBlocks.js +15 -4
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureHelpers.d.ts +11 -2
- package/dist/layout-engine/measure/measureHelpers.js +15 -4
- package/dist/layout-engine/measure/measureParagraph.js +85 -25
- package/dist/layout-engine/measure/measureProvider.js +4 -3
- package/dist/layout-engine/measure/tableCellFlow.js +5 -2
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
- package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
- package/dist/layout-engine/paginator.js +5 -1
- package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
- package/dist/layout-engine/paragraphSpacing.js +107 -13
- package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
- package/dist/layout-engine/types.d.ts +48 -15
- package/dist/layout-painter/registry/modules/textBox.js +5 -1
- package/dist/layout-painter/renderImage.d.ts +12 -1
- package/dist/layout-painter/renderImage.js +21 -4
- package/dist/layout-painter/renderPage.d.ts +5 -2
- package/dist/layout-painter/renderPage.js +59 -14
- package/dist/layout-painter/renderParagraph.js +55 -15
- package/dist/layout-painter/renderTable.d.ts +5 -1
- package/dist/layout-painter/renderTable.js +7 -3
- package/dist/layout-painter/renderTextBox.d.ts +2 -1
- package/dist/layout-painter/renderTextBox.js +25 -14
- package/dist/layout-painter/renderWatermark.d.ts +1 -1
- package/dist/layout-painter/renderWatermark.js +2 -1
- package/dist/managers/DocumentLoaderManager.js +1 -1
- package/dist/markdown/fromMarkdown.js +1 -1
- package/dist/model.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +175 -18
- package/dist/prosemirror/commands/comments.d.ts +79 -4
- package/dist/prosemirror/commands/comments.js +622 -13
- package/dist/prosemirror/commands/contentControls.js +1 -1
- package/dist/prosemirror/commands/hyperlink.js +15 -1
- package/dist/prosemirror/commands/image.js +7 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
- package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
- package/dist/prosemirror/commentIdAllocator.js +13 -3
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +404 -62
- package/dist/prosemirror/conversion/index.js +1 -1
- package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
- package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
- package/dist/prosemirror/conversion/toProseDoc.js +364 -103
- package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
- package/dist/prosemirror/extensions/StarterKit.js +6 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +6 -2
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +10 -2
- package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +15 -2
- package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +30 -4
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +70 -11
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +14 -6
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
- package/dist/prosemirror/index.d.ts +1 -1
- package/dist/prosemirror/index.js +1 -1
- package/dist/prosemirror/plugins/contentControlWidgets.d.ts +36 -10
- package/dist/prosemirror/plugins/contentControlWidgets.js +128 -94
- package/dist/prosemirror/plugins/revisionIds.d.ts +23 -0
- package/dist/prosemirror/plugins/revisionIds.js +57 -0
- package/dist/prosemirror/plugins/selectionTracker.js +1 -0
- package/dist/prosemirror/plugins/suggestionMode.js +5 -4
- package/dist/prosemirror/runFormattingMarkNames.d.ts +14 -0
- package/dist/prosemirror/runFormattingMarkNames.js +40 -0
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +30 -3
- package/dist/prosemirror/schema/marks.js +15 -0
- package/dist/prosemirror/schema/nodes.d.ts +111 -6
- package/dist/prosemirror/selectionState.js +1 -0
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
- package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
- package/dist/prosemirror/tableCellMergeRevision.js +22 -0
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
- package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
- package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
- package/dist/prosemirror/validation.js +9 -1
- package/dist/render-dom/RemoteSelectionOverlay.js +2 -2
- package/dist/server.d.ts +7 -3
- package/dist/server.js +7 -3
- package/dist/utils/clipboard.d.ts +10 -1
- package/dist/utils/clipboard.js +33 -6
- package/dist/utils/colorResolver.d.ts +8 -1
- package/dist/utils/colorResolver.js +35 -5
- package/dist/utils/fontFamilyMerge.js +9 -11
- package/dist/utils/fontLoader.js +3 -2
- package/dist/utils/fontResolver.d.ts +2 -1
- package/dist/utils/fontResolver.js +31 -7
- package/dist/utils/fontWeights.d.ts +1 -1
- package/dist/utils/fontWeights.js +1 -1
- package/dist/utils/hexId.d.ts +11 -1
- package/dist/utils/hexId.js +11 -1
- package/dist/utils/paragraphFormattingMerge.js +1 -0
- package/dist/utils/sanitizeImageSrc.d.ts +21 -0
- package/dist/utils/sanitizeImageSrc.js +30 -0
- package/dist/utils/stripXmlDeclarations.d.ts +11 -0
- package/dist/utils/stripXmlDeclarations.js +35 -0
- package/dist/version-comparison.d.ts +22 -2
- package/dist/version-comparison.js +41 -12
- package/dist/watermark/index.d.ts +11 -1
- package/dist/watermark/index.js +22 -1
- package/package.json +3 -2
package/dist/ai-edits/apply.js
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
|
+
import { expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
2
|
+
import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
|
|
1
3
|
import { getFolioParaIdFromBlockId } from "../types/block-id.js";
|
|
4
|
+
import { stripBlockIdentityAttrs } from "./block-identity.js";
|
|
2
5
|
import { buildCleanBlockText } from "./clean-text.js";
|
|
3
6
|
import { hasInlineEmphasis, parseInlineEmphasisRuns, stripInlineEmphasisMarkers } from "./inline-emphasis.js";
|
|
4
7
|
import { hashFolioAIBlockText, normalizeFolioAIBlockText } from "./snapshot.js";
|
|
8
|
+
import { planTableMutations } from "./table-mutation-plan.js";
|
|
9
|
+
import { findEnclosingTableBoundary, findEnclosingTableCell, findEnclosingTableRow, tableRectangleCutsMergedCell } from "./table-targets.js";
|
|
10
|
+
import { mergeTableRectangle, mergeTrackedVerticalTableCells, splitTableRectangle, splitTrackedVerticalTableCell } from "./table-cell-mutations.js";
|
|
11
|
+
import { applyTableColumnDeletion, applyTableColumnInsertion, applyTableRowDeletion, applyTableRowInsertion, findTableColumnInsertion, findTableRowInsertion, getTableColumnCoordinateKey } from "./table-row-column-mutations.js";
|
|
5
12
|
import { diffWordSegments } from "./word-diff.js";
|
|
13
|
+
import { TableMap } from "prosemirror-tables";
|
|
6
14
|
//#region src/ai-edits/apply.ts
|
|
7
15
|
/**
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
11
|
-
*
|
|
16
|
+
* Operation types applied in `"suggested"` mode. Every produced revision — an
|
|
17
|
+
* inline mark, a whole-node `_suggestedInsert` marker, or a suggested
|
|
18
|
+
* `trIns`/`trDel`/`cellMarker` — is stripped from serialized DOCX until
|
|
19
|
+
* accepted. Cell merge/split (not row/column ops) and comment ops stay
|
|
20
|
+
* `unsupportedMode`.
|
|
12
21
|
*/
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const SUGGESTED_SUPPORTED_OPERATION_TYPES = /* @__PURE__ */ new Set([
|
|
23
|
+
"replaceInBlock",
|
|
24
|
+
"replaceRange",
|
|
25
|
+
"formatRange",
|
|
26
|
+
"replaceBlock",
|
|
27
|
+
"deleteBlock",
|
|
28
|
+
"insertAfterBlock",
|
|
29
|
+
"insertBeforeBlock",
|
|
30
|
+
"insertSignatureTable",
|
|
31
|
+
"insertTableRow",
|
|
32
|
+
"deleteTableRow",
|
|
33
|
+
"insertTableColumn",
|
|
34
|
+
"deleteTableColumn"
|
|
35
|
+
]);
|
|
25
36
|
const applyReplaceBlockStyleId = ({ item, tr }) => {
|
|
26
37
|
if (item.operation.type !== "replaceBlock" || item.operation.styleId === void 0) return tr;
|
|
27
38
|
const block = tr.doc.nodeAt(item.blockFrom);
|
|
@@ -31,6 +42,69 @@ const applyReplaceBlockStyleId = ({ item, tr }) => {
|
|
|
31
42
|
styleId: item.operation.styleId
|
|
32
43
|
});
|
|
33
44
|
};
|
|
45
|
+
const applyInlineFormatting = ({ tr, schema, from, to, formatting }) => {
|
|
46
|
+
for (const [name, enabled] of Object.entries(formatting)) {
|
|
47
|
+
const markType = schema.marks[name];
|
|
48
|
+
if (!markType) continue;
|
|
49
|
+
if (enabled) {
|
|
50
|
+
tr.addMark(from, to, name === "underline" ? markType.create({ style: "single" }) : markType.create());
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
tr.removeMark(from, to, markType);
|
|
54
|
+
}
|
|
55
|
+
return tr;
|
|
56
|
+
};
|
|
57
|
+
const formattingWouldChange = (marks, formatting) => Object.entries(formatting).some(([name, enabled]) => {
|
|
58
|
+
const mark = marks.find((candidate) => candidate.type.name === name);
|
|
59
|
+
if (!enabled) return mark !== void 0;
|
|
60
|
+
if (name === "underline") return mark?.attrs["style"] !== "single";
|
|
61
|
+
return mark === void 0;
|
|
62
|
+
});
|
|
63
|
+
const applyTrackedInlineFormatting = ({ tr, schema, doc, from, to, formatting, revisionId, author, date, initials, suggestionId = null }) => {
|
|
64
|
+
const propertyChangeType = schema.marks["runPropertyChange"];
|
|
65
|
+
if (!propertyChangeType) return tr;
|
|
66
|
+
const segments = [];
|
|
67
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
68
|
+
if (!node.isText || !formattingWouldChange(node.marks, formatting)) return;
|
|
69
|
+
const segmentFrom = Math.max(from, pos);
|
|
70
|
+
const segmentTo = Math.min(to, pos + node.nodeSize);
|
|
71
|
+
const previousFormatting = marksToTextFormatting(node.marks);
|
|
72
|
+
const existingMark = node.marks.find((mark) => mark.type === propertyChangeType);
|
|
73
|
+
const existingChanges = existingMark ? expectRunPropertyChangeMarkAttrs(existingMark).changes : [];
|
|
74
|
+
const change = {
|
|
75
|
+
type: "runPropertyChange",
|
|
76
|
+
info: {
|
|
77
|
+
id: revisionId,
|
|
78
|
+
author,
|
|
79
|
+
date,
|
|
80
|
+
...initials ? { initials } : {}
|
|
81
|
+
},
|
|
82
|
+
...Object.keys(previousFormatting).length > 0 ? { previousFormatting } : {}
|
|
83
|
+
};
|
|
84
|
+
segments.push({
|
|
85
|
+
from: segmentFrom,
|
|
86
|
+
to: segmentTo,
|
|
87
|
+
changes: [...existingChanges, change]
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
if (segments.length === 0) return tr;
|
|
91
|
+
const suggestionAttrs = suggestionId === null ? {} : {
|
|
92
|
+
provenance: "suggested",
|
|
93
|
+
suggestionId
|
|
94
|
+
};
|
|
95
|
+
applyInlineFormatting({
|
|
96
|
+
tr,
|
|
97
|
+
schema,
|
|
98
|
+
from,
|
|
99
|
+
to,
|
|
100
|
+
formatting
|
|
101
|
+
});
|
|
102
|
+
for (const segment of segments) tr.addMark(segment.from, segment.to, propertyChangeType.create({
|
|
103
|
+
changes: segment.changes,
|
|
104
|
+
...suggestionAttrs
|
|
105
|
+
}));
|
|
106
|
+
return tr;
|
|
107
|
+
};
|
|
34
108
|
/**
|
|
35
109
|
* Module-scoped monotonic counter for tracked-change revision ids.
|
|
36
110
|
* Seeded once from `Date.now()` so ids are roughly time-ordered for
|
|
@@ -93,13 +167,21 @@ const collectLiveBlocksByParaId = (doc) => {
|
|
|
93
167
|
});
|
|
94
168
|
return byParaId;
|
|
95
169
|
};
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
after: resolved.after(depth)
|
|
170
|
+
const getTableMutationPlanTarget = (item) => {
|
|
171
|
+
if (item.tableCellMerge) return {
|
|
172
|
+
type: "mergeCells",
|
|
173
|
+
...item.tableCellMerge
|
|
101
174
|
};
|
|
102
|
-
return
|
|
175
|
+
if (item.tableCellSplit) return {
|
|
176
|
+
type: "splitCell",
|
|
177
|
+
...item.tableCellSplit
|
|
178
|
+
};
|
|
179
|
+
const tablePosition = item.tableRowInsertion?.tableStart !== void 0 ? item.tableRowInsertion.tableStart - 1 : item.tableRowDeletion?.tablePosition ?? item.tableColumnInsertion?.tablePosition ?? item.tableColumnDeletion?.tablePosition;
|
|
180
|
+
if (tablePosition !== void 0) return {
|
|
181
|
+
type: "tableStructure",
|
|
182
|
+
tablePosition
|
|
183
|
+
};
|
|
184
|
+
return { type: "none" };
|
|
103
185
|
};
|
|
104
186
|
/**
|
|
105
187
|
* The snapshot recorded an `hashOccurrenceCount` per anchor but
|
|
@@ -193,14 +275,18 @@ const buildEmphasisInlineContent = (schema, text, baseMarks) => {
|
|
|
193
275
|
}
|
|
194
276
|
return nodes.length > 0 ? nodes : [schema.text(text, [...baseMarks])];
|
|
195
277
|
};
|
|
196
|
-
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", createCommentId, revisionIdSeed }) => {
|
|
278
|
+
const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode = "tracked-changes", author = "AI", initials, createCommentId, revisionIdSeed }) => {
|
|
197
279
|
const applied = [];
|
|
198
280
|
const skipped = [];
|
|
199
281
|
const resolved = [];
|
|
200
282
|
const insertionType = view.state.schema.marks["insertion"];
|
|
201
283
|
const deletionType = view.state.schema.marks["deletion"];
|
|
202
284
|
const commentType = view.state.schema.marks["comment"];
|
|
203
|
-
|
|
285
|
+
const claimedTableRows = /* @__PURE__ */ new Set();
|
|
286
|
+
const claimedTableColumns = /* @__PURE__ */ new Set();
|
|
287
|
+
const producesTrackedChanges = mode !== "direct";
|
|
288
|
+
const isSuggested = mode === "suggested";
|
|
289
|
+
if (producesTrackedChanges && (!insertionType || !deletionType)) return {
|
|
204
290
|
applied,
|
|
205
291
|
skipped: operations.map((operation) => ({
|
|
206
292
|
id: operation.id,
|
|
@@ -210,13 +296,6 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
210
296
|
const liveBlocks = collectLiveBlocksByHash(view.state.doc);
|
|
211
297
|
const liveBlocksByParaId = collectLiveBlocksByParaId(view.state.doc);
|
|
212
298
|
for (const [index, operation] of operations.entries()) {
|
|
213
|
-
if (mode === "tracked-changes" && operation.type === "formatRange") {
|
|
214
|
-
skipped.push({
|
|
215
|
-
id: operation.id,
|
|
216
|
-
reason: "unsupportedMode"
|
|
217
|
-
});
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
299
|
const commentText = getOperationCommentText(operation);
|
|
221
300
|
if (commentText !== void 0 && (!commentType || createCommentId === void 0)) {
|
|
222
301
|
skipped.push({
|
|
@@ -239,6 +318,30 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
239
318
|
});
|
|
240
319
|
continue;
|
|
241
320
|
}
|
|
321
|
+
const deletion = resolution.operation.tableRowDeletion;
|
|
322
|
+
if (deletion) {
|
|
323
|
+
const rowKey = `${deletion.tablePosition}:${deletion.rowIndex}`;
|
|
324
|
+
if (claimedTableRows.has(rowKey)) {
|
|
325
|
+
skipped.push({
|
|
326
|
+
id: operation.id,
|
|
327
|
+
reason: "noopOperation"
|
|
328
|
+
});
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
claimedTableRows.add(rowKey);
|
|
332
|
+
}
|
|
333
|
+
const columnDeletion = resolution.operation.tableColumnDeletion;
|
|
334
|
+
if (columnDeletion) {
|
|
335
|
+
const columnKey = getTableColumnCoordinateKey(columnDeletion);
|
|
336
|
+
if (claimedTableColumns.has(columnKey)) {
|
|
337
|
+
skipped.push({
|
|
338
|
+
id: operation.id,
|
|
339
|
+
reason: "noopOperation"
|
|
340
|
+
});
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
claimedTableColumns.add(columnKey);
|
|
344
|
+
}
|
|
242
345
|
const commentId = commentText !== void 0 ? createCommentId?.(commentText) : void 0;
|
|
243
346
|
resolved.push({
|
|
244
347
|
...resolution.operation,
|
|
@@ -246,20 +349,70 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
246
349
|
...commentId !== void 0 && { commentId }
|
|
247
350
|
});
|
|
248
351
|
}
|
|
249
|
-
|
|
352
|
+
const tablePlan = planTableMutations(resolved.map((item) => ({
|
|
353
|
+
item,
|
|
354
|
+
operationId: item.operation.id,
|
|
355
|
+
target: getTableMutationPlanTarget(item)
|
|
356
|
+
})));
|
|
357
|
+
skipped.push(...tablePlan.skipped);
|
|
358
|
+
const executableResolved = tablePlan.executable;
|
|
359
|
+
if (executableResolved.length === 0) return {
|
|
250
360
|
applied,
|
|
251
361
|
skipped
|
|
252
362
|
};
|
|
253
363
|
let tr = view.state.tr;
|
|
254
|
-
let revisionSeed = revisionIdSeed ?? nextRevisionSeed(
|
|
364
|
+
let revisionSeed = revisionIdSeed ?? nextRevisionSeed(executableResolved.length);
|
|
255
365
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
256
|
-
|
|
366
|
+
const insertedColumnCounts = /* @__PURE__ */ new Map();
|
|
367
|
+
for (const item of executableResolved.toSorted((left, right) => {
|
|
368
|
+
const leftCellShape = left.tableCellMerge ?? left.tableCellSplit;
|
|
369
|
+
const rightCellShape = right.tableCellMerge ?? right.tableCellSplit;
|
|
370
|
+
if (!leftCellShape && rightCellShape) return -1;
|
|
371
|
+
if (leftCellShape && !rightCellShape) return 1;
|
|
372
|
+
if (leftCellShape && rightCellShape) {
|
|
373
|
+
if (leftCellShape.tablePosition !== rightCellShape.tablePosition) return rightCellShape.tablePosition - leftCellShape.tablePosition;
|
|
374
|
+
if (leftCellShape.rectangle.bottom !== rightCellShape.rectangle.bottom) return rightCellShape.rectangle.bottom - leftCellShape.rectangle.bottom;
|
|
375
|
+
if (leftCellShape.rectangle.right !== rightCellShape.rectangle.right) return rightCellShape.rectangle.right - leftCellShape.rectangle.right;
|
|
376
|
+
return right.originalIndex - left.originalIndex;
|
|
377
|
+
}
|
|
378
|
+
const leftColumn = left.tableColumnInsertion ?? left.tableColumnDeletion;
|
|
379
|
+
const rightColumn = right.tableColumnInsertion ?? right.tableColumnDeletion;
|
|
380
|
+
if (!leftColumn && rightColumn) return -1;
|
|
381
|
+
if (leftColumn && !rightColumn) return 1;
|
|
382
|
+
if (leftColumn && rightColumn) {
|
|
383
|
+
if (leftColumn.tablePosition !== rightColumn.tablePosition) return rightColumn.tablePosition - leftColumn.tablePosition;
|
|
384
|
+
if (leftColumn.columnIndex !== rightColumn.columnIndex) return rightColumn.columnIndex - leftColumn.columnIndex;
|
|
385
|
+
const leftIsInsertion = left.tableColumnInsertion !== void 0;
|
|
386
|
+
if (leftIsInsertion !== (right.tableColumnInsertion !== void 0)) return leftIsInsertion ? -1 : 1;
|
|
387
|
+
return right.originalIndex - left.originalIndex;
|
|
388
|
+
}
|
|
257
389
|
if (left.from !== right.from) return right.from - left.from;
|
|
258
390
|
return right.originalIndex - left.originalIndex;
|
|
259
391
|
})) {
|
|
260
392
|
const commentMark = item.commentId !== void 0 && commentType ? commentType.create({ commentId: item.commentId }) : null;
|
|
261
393
|
const stepsBefore = tr.steps.length;
|
|
262
394
|
let appliedRevisionIds;
|
|
395
|
+
if (isSuggested && !SUGGESTED_SUPPORTED_OPERATION_TYPES.has(item.operation.type)) {
|
|
396
|
+
skipped.push({
|
|
397
|
+
id: item.operation.id,
|
|
398
|
+
reason: "unsupportedMode"
|
|
399
|
+
});
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const suggestionId = isSuggested ? item.operation.suggestionId ?? item.operation.id : null;
|
|
403
|
+
const trackedRevisionExtras = {
|
|
404
|
+
...initials ? { initials } : {},
|
|
405
|
+
...suggestionId !== null ? {
|
|
406
|
+
provenance: "suggested",
|
|
407
|
+
suggestionId
|
|
408
|
+
} : {}
|
|
409
|
+
};
|
|
410
|
+
const structuralRevision = producesTrackedChanges ? {
|
|
411
|
+
revisionId: revisionSeed,
|
|
412
|
+
author,
|
|
413
|
+
date,
|
|
414
|
+
...trackedRevisionExtras
|
|
415
|
+
} : null;
|
|
263
416
|
switch (item.operation.type) {
|
|
264
417
|
case "replaceInBlock":
|
|
265
418
|
case "replaceRange": {
|
|
@@ -273,21 +426,42 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
273
426
|
date,
|
|
274
427
|
revisionIdDelete,
|
|
275
428
|
revisionIdInsert,
|
|
276
|
-
commentMark
|
|
429
|
+
commentMark,
|
|
430
|
+
suggestionId,
|
|
431
|
+
initials
|
|
277
432
|
});
|
|
278
|
-
if (
|
|
433
|
+
if (producesTrackedChanges) appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
|
|
279
434
|
break;
|
|
280
435
|
}
|
|
281
436
|
case "commentOnRange":
|
|
282
437
|
if (commentMark) tr = tr.addMark(item.from, item.to, commentMark);
|
|
283
438
|
break;
|
|
284
439
|
case "formatRange":
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
440
|
+
if (producesTrackedChanges) {
|
|
441
|
+
const revisionId = revisionSeed++;
|
|
442
|
+
tr = applyTrackedInlineFormatting({
|
|
443
|
+
tr,
|
|
444
|
+
schema: view.state.schema,
|
|
445
|
+
doc: tr.doc,
|
|
446
|
+
from: item.from,
|
|
447
|
+
to: item.to,
|
|
448
|
+
formatting: item.operation.formatting,
|
|
449
|
+
revisionId,
|
|
450
|
+
author,
|
|
451
|
+
date,
|
|
452
|
+
initials,
|
|
453
|
+
suggestionId
|
|
454
|
+
});
|
|
455
|
+
appliedRevisionIds = [revisionId];
|
|
456
|
+
break;
|
|
290
457
|
}
|
|
458
|
+
tr = applyInlineFormatting({
|
|
459
|
+
tr,
|
|
460
|
+
schema: view.state.schema,
|
|
461
|
+
from: item.from,
|
|
462
|
+
to: item.to,
|
|
463
|
+
formatting: item.operation.formatting
|
|
464
|
+
});
|
|
291
465
|
break;
|
|
292
466
|
case "replaceBlock": {
|
|
293
467
|
const revisionIdDelete = revisionSeed++;
|
|
@@ -319,13 +493,15 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
319
493
|
date,
|
|
320
494
|
revisionIdDelete,
|
|
321
495
|
revisionIdInsert,
|
|
322
|
-
commentMark
|
|
496
|
+
commentMark,
|
|
497
|
+
suggestionId,
|
|
498
|
+
initials
|
|
323
499
|
});
|
|
324
500
|
tr = applyReplaceBlockStyleId({
|
|
325
501
|
item,
|
|
326
502
|
tr
|
|
327
503
|
});
|
|
328
|
-
if (
|
|
504
|
+
if (producesTrackedChanges) appliedRevisionIds = [revisionIdDelete, revisionIdInsert];
|
|
329
505
|
break;
|
|
330
506
|
}
|
|
331
507
|
case "insertAfterBlock":
|
|
@@ -338,18 +514,21 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
338
514
|
continue;
|
|
339
515
|
}
|
|
340
516
|
const marks = [];
|
|
341
|
-
|
|
517
|
+
let insertedBlockRevisionId = null;
|
|
518
|
+
if (producesTrackedChanges && insertionType) {
|
|
342
519
|
const revisionId = revisionSeed++;
|
|
520
|
+
insertedBlockRevisionId = revisionId;
|
|
343
521
|
marks.push(insertionType.create({
|
|
344
522
|
revisionId,
|
|
345
523
|
author,
|
|
346
|
-
date
|
|
524
|
+
date,
|
|
525
|
+
...trackedRevisionExtras
|
|
347
526
|
}));
|
|
348
527
|
appliedRevisionIds = [revisionId];
|
|
349
528
|
}
|
|
350
529
|
if (commentMark) marks.push(commentMark);
|
|
351
530
|
const content = item.insertText && item.insertText.length > 0 ? buildEmphasisInlineContent(view.state.schema, item.insertText, marks) : null;
|
|
352
|
-
const attrs = { ...item.operation.inheritFormatting === false ? {} :
|
|
531
|
+
const attrs = { ...item.operation.inheritFormatting === false ? {} : stripBlockIdentityAttrs(item.blockNode.attrs) };
|
|
353
532
|
if (item.operation.pageBreakBefore === true) attrs["pageBreakBefore"] = true;
|
|
354
533
|
if (item.operation.styleId !== void 0) {
|
|
355
534
|
attrs["styleId"] = item.operation.styleId;
|
|
@@ -362,6 +541,13 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
362
541
|
attrs["listStartOverride"] = null;
|
|
363
542
|
}
|
|
364
543
|
}
|
|
544
|
+
if (isSuggested && suggestionId !== null && insertedBlockRevisionId !== null) attrs["_suggestedInsert"] = {
|
|
545
|
+
suggestionId,
|
|
546
|
+
revisionId: insertedBlockRevisionId,
|
|
547
|
+
author,
|
|
548
|
+
date,
|
|
549
|
+
...initials ? { initials } : {}
|
|
550
|
+
};
|
|
365
551
|
const node = item.blockNode.type.create(attrs, content);
|
|
366
552
|
tr = tr.insert(item.from, node);
|
|
367
553
|
break;
|
|
@@ -374,20 +560,236 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
374
560
|
});
|
|
375
561
|
continue;
|
|
376
562
|
}
|
|
377
|
-
const
|
|
563
|
+
const signatureTable = buildSignatureTableNode({
|
|
378
564
|
schema: view.state.schema,
|
|
379
565
|
parties: item.operation.parties
|
|
380
566
|
});
|
|
381
|
-
if (!
|
|
567
|
+
if (!signatureTable) {
|
|
382
568
|
skipped.push({
|
|
383
569
|
id: item.operation.id,
|
|
384
570
|
reason: "unsupportedBlock"
|
|
385
571
|
});
|
|
386
572
|
continue;
|
|
387
573
|
}
|
|
574
|
+
let node = signatureTable;
|
|
575
|
+
if (isSuggested && suggestionId !== null) {
|
|
576
|
+
const revisionId = revisionSeed++;
|
|
577
|
+
node = signatureTable.type.create({
|
|
578
|
+
...signatureTable.attrs,
|
|
579
|
+
_suggestedInsert: {
|
|
580
|
+
suggestionId,
|
|
581
|
+
revisionId,
|
|
582
|
+
author,
|
|
583
|
+
date,
|
|
584
|
+
...initials ? { initials } : {}
|
|
585
|
+
}
|
|
586
|
+
}, signatureTable.content, signatureTable.marks);
|
|
587
|
+
appliedRevisionIds = [revisionId];
|
|
588
|
+
}
|
|
388
589
|
tr = tr.insert(item.from, node);
|
|
389
590
|
break;
|
|
390
591
|
}
|
|
592
|
+
case "insertTableRow": {
|
|
593
|
+
const insertion = item.tableRowInsertion;
|
|
594
|
+
if (!insertion) {
|
|
595
|
+
skipped.push({
|
|
596
|
+
id: item.operation.id,
|
|
597
|
+
reason: "unsupportedBlock"
|
|
598
|
+
});
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
const revision = structuralRevision;
|
|
602
|
+
const result = applyTableRowInsertion({
|
|
603
|
+
tr,
|
|
604
|
+
insertion,
|
|
605
|
+
cellTexts: item.operation.cellTexts,
|
|
606
|
+
revision
|
|
607
|
+
});
|
|
608
|
+
if (result.type === "unsupported") {
|
|
609
|
+
skipped.push({
|
|
610
|
+
id: item.operation.id,
|
|
611
|
+
reason: "unsupportedBlock"
|
|
612
|
+
});
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
tr = result.transaction;
|
|
616
|
+
if (result.revisionId !== null) {
|
|
617
|
+
revisionSeed++;
|
|
618
|
+
appliedRevisionIds = [result.revisionId];
|
|
619
|
+
}
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
case "insertTableColumn": {
|
|
623
|
+
const insertion = item.tableColumnInsertion;
|
|
624
|
+
if (!insertion) {
|
|
625
|
+
skipped.push({
|
|
626
|
+
id: item.operation.id,
|
|
627
|
+
reason: "unsupportedBlock"
|
|
628
|
+
});
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
const revision = structuralRevision;
|
|
632
|
+
const result = applyTableColumnInsertion({
|
|
633
|
+
tr,
|
|
634
|
+
insertion,
|
|
635
|
+
cellTexts: item.operation.cellTexts,
|
|
636
|
+
revision
|
|
637
|
+
});
|
|
638
|
+
if (result.type === "unsupported") {
|
|
639
|
+
skipped.push({
|
|
640
|
+
id: item.operation.id,
|
|
641
|
+
reason: "unsupportedBlock"
|
|
642
|
+
});
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
tr = result.transaction;
|
|
646
|
+
if (result.revisionId !== null) {
|
|
647
|
+
revisionSeed++;
|
|
648
|
+
appliedRevisionIds = [result.revisionId];
|
|
649
|
+
}
|
|
650
|
+
const columnKey = getTableColumnCoordinateKey(insertion);
|
|
651
|
+
insertedColumnCounts.set(columnKey, (insertedColumnCounts.get(columnKey) ?? 0) + 1);
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case "deleteTableColumn": {
|
|
655
|
+
const deletion = item.tableColumnDeletion;
|
|
656
|
+
if (!deletion) {
|
|
657
|
+
skipped.push({
|
|
658
|
+
id: item.operation.id,
|
|
659
|
+
reason: "unsupportedBlock"
|
|
660
|
+
});
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
const columnKey = getTableColumnCoordinateKey(deletion);
|
|
664
|
+
const revision = structuralRevision;
|
|
665
|
+
const result = applyTableColumnDeletion({
|
|
666
|
+
tr,
|
|
667
|
+
deletion,
|
|
668
|
+
insertedColumnCount: insertedColumnCounts.get(columnKey) ?? 0,
|
|
669
|
+
revision
|
|
670
|
+
});
|
|
671
|
+
if (result.type === "unsupported") {
|
|
672
|
+
skipped.push({
|
|
673
|
+
id: item.operation.id,
|
|
674
|
+
reason: "unsupportedBlock"
|
|
675
|
+
});
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
tr = result.transaction;
|
|
679
|
+
if (result.revisionId !== null) {
|
|
680
|
+
revisionSeed++;
|
|
681
|
+
appliedRevisionIds = [result.revisionId];
|
|
682
|
+
}
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
case "mergeTableCells": {
|
|
686
|
+
const merge = item.tableCellMerge;
|
|
687
|
+
const tablePosition = merge ? tr.mapping.map(merge.tablePosition, 1) : null;
|
|
688
|
+
const table = tablePosition === null ? null : tr.doc.nodeAt(tablePosition);
|
|
689
|
+
if (!merge || tablePosition === null || !table || table.type.spec["tableRole"] !== "table") {
|
|
690
|
+
skipped.push({
|
|
691
|
+
id: item.operation.id,
|
|
692
|
+
reason: "unsupportedBlock"
|
|
693
|
+
});
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
const revisionId = mode === "tracked-changes" ? revisionSeed : null;
|
|
697
|
+
const nextTr = revisionId === null ? mergeTableRectangle({
|
|
698
|
+
tr,
|
|
699
|
+
tablePosition,
|
|
700
|
+
table,
|
|
701
|
+
rectangle: merge.rectangle
|
|
702
|
+
}) : mergeTrackedVerticalTableCells({
|
|
703
|
+
tr,
|
|
704
|
+
tablePosition,
|
|
705
|
+
table,
|
|
706
|
+
rectangle: merge.rectangle,
|
|
707
|
+
revisionId,
|
|
708
|
+
author,
|
|
709
|
+
date
|
|
710
|
+
});
|
|
711
|
+
if (!nextTr) {
|
|
712
|
+
skipped.push({
|
|
713
|
+
id: item.operation.id,
|
|
714
|
+
reason: "unsupportedBlock"
|
|
715
|
+
});
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
tr = nextTr;
|
|
719
|
+
if (revisionId !== null) {
|
|
720
|
+
revisionSeed++;
|
|
721
|
+
appliedRevisionIds = [revisionId];
|
|
722
|
+
}
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
case "splitTableCell": {
|
|
726
|
+
const split = item.tableCellSplit;
|
|
727
|
+
const tablePosition = split ? tr.mapping.map(split.tablePosition, 1) : null;
|
|
728
|
+
const table = tablePosition === null ? null : tr.doc.nodeAt(tablePosition);
|
|
729
|
+
if (!split || tablePosition === null || !table || table.type.spec["tableRole"] !== "table") {
|
|
730
|
+
skipped.push({
|
|
731
|
+
id: item.operation.id,
|
|
732
|
+
reason: "unsupportedBlock"
|
|
733
|
+
});
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
const revisionId = mode === "tracked-changes" ? revisionSeed : null;
|
|
737
|
+
const nextTr = revisionId === null ? splitTableRectangle({
|
|
738
|
+
tr,
|
|
739
|
+
tablePosition,
|
|
740
|
+
table,
|
|
741
|
+
rectangle: split.rectangle
|
|
742
|
+
}) : splitTrackedVerticalTableCell({
|
|
743
|
+
tr,
|
|
744
|
+
tablePosition,
|
|
745
|
+
table,
|
|
746
|
+
rectangle: split.rectangle,
|
|
747
|
+
revisionId,
|
|
748
|
+
author,
|
|
749
|
+
date
|
|
750
|
+
});
|
|
751
|
+
if (!nextTr) {
|
|
752
|
+
skipped.push({
|
|
753
|
+
id: item.operation.id,
|
|
754
|
+
reason: "unsupportedBlock"
|
|
755
|
+
});
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
tr = nextTr;
|
|
759
|
+
if (revisionId !== null) {
|
|
760
|
+
revisionSeed++;
|
|
761
|
+
appliedRevisionIds = [revisionId];
|
|
762
|
+
}
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
case "deleteTableRow": {
|
|
766
|
+
const deletion = item.tableRowDeletion;
|
|
767
|
+
if (!deletion) {
|
|
768
|
+
skipped.push({
|
|
769
|
+
id: item.operation.id,
|
|
770
|
+
reason: "unsupportedBlock"
|
|
771
|
+
});
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const result = applyTableRowDeletion({
|
|
775
|
+
tr,
|
|
776
|
+
deletion,
|
|
777
|
+
revision: structuralRevision
|
|
778
|
+
});
|
|
779
|
+
if (result.type === "unsupported") {
|
|
780
|
+
skipped.push({
|
|
781
|
+
id: item.operation.id,
|
|
782
|
+
reason: "unsupportedBlock"
|
|
783
|
+
});
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
tr = result.transaction;
|
|
787
|
+
if (result.revisionId !== null) {
|
|
788
|
+
revisionSeed++;
|
|
789
|
+
appliedRevisionIds = [result.revisionId];
|
|
790
|
+
}
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
391
793
|
case "deleteBlock":
|
|
392
794
|
if (mode === "direct") {
|
|
393
795
|
tr = tr.delete(item.blockFrom, item.blockTo);
|
|
@@ -398,7 +800,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
398
800
|
tr = tr.addMark(item.from, item.to, deletionType.create({
|
|
399
801
|
revisionId,
|
|
400
802
|
author,
|
|
401
|
-
date
|
|
803
|
+
date,
|
|
804
|
+
...trackedRevisionExtras
|
|
402
805
|
}));
|
|
403
806
|
appliedRevisionIds = [revisionId];
|
|
404
807
|
}
|
|
@@ -422,7 +825,8 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
422
825
|
...appliedRevisionIds !== void 0 && appliedRevisionIds[0] !== void 0 && {
|
|
423
826
|
revisionId: appliedRevisionIds[0],
|
|
424
827
|
revisionIds: appliedRevisionIds
|
|
425
|
-
}
|
|
828
|
+
},
|
|
829
|
+
...suggestionId !== null && { suggestionId }
|
|
426
830
|
});
|
|
427
831
|
}
|
|
428
832
|
if (tr.docChanged) view.dispatch(tr);
|
|
@@ -452,7 +856,7 @@ const previewFolioAIEditOperations = (options) => {
|
|
|
452
856
|
skipped: result.skipped
|
|
453
857
|
};
|
|
454
858
|
};
|
|
455
|
-
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark }) => {
|
|
859
|
+
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials }) => {
|
|
456
860
|
let nextTr = tr;
|
|
457
861
|
const replacement = stripInlineEmphasisMarkers((() => {
|
|
458
862
|
if (item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") return item.operation.replace;
|
|
@@ -470,15 +874,24 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
470
874
|
}
|
|
471
875
|
const insertionType = nextTr.doc.type.schema.marks["insertion"];
|
|
472
876
|
const deletionType = nextTr.doc.type.schema.marks["deletion"];
|
|
877
|
+
const suggestionAttrs = suggestionId === null ? {} : {
|
|
878
|
+
provenance: "suggested",
|
|
879
|
+
suggestionId
|
|
880
|
+
};
|
|
881
|
+
const initialsAttr = initials ? { initials } : {};
|
|
473
882
|
const delAttrs = {
|
|
474
883
|
revisionId: revisionIdDelete,
|
|
475
884
|
author,
|
|
476
|
-
date
|
|
885
|
+
date,
|
|
886
|
+
...initialsAttr,
|
|
887
|
+
...suggestionAttrs
|
|
477
888
|
};
|
|
478
889
|
const insAttrs = {
|
|
479
890
|
revisionId: revisionIdInsert,
|
|
480
891
|
author,
|
|
481
|
-
date
|
|
892
|
+
date,
|
|
893
|
+
...initialsAttr,
|
|
894
|
+
...suggestionAttrs
|
|
482
895
|
};
|
|
483
896
|
const cleanBlock = item.blockNode.content.size === item.blockNode.textContent.length ? buildCleanBlockText(item.blockNode, item.blockFrom) : null;
|
|
484
897
|
let sourceText = null;
|
|
@@ -561,8 +974,7 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
561
974
|
}
|
|
562
975
|
return nextTr;
|
|
563
976
|
};
|
|
564
|
-
const
|
|
565
|
-
const blockId = operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange" ? operation.range.blockId : operation.blockId;
|
|
977
|
+
const resolveStableBlock = ({ snapshot, blockId, liveBlocks, liveBlocksByParaId }) => {
|
|
566
978
|
const anchor = snapshot.anchors[blockId];
|
|
567
979
|
if (!anchor) return {
|
|
568
980
|
type: "skip",
|
|
@@ -588,20 +1000,37 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
588
1000
|
type: "skip",
|
|
589
1001
|
reason: "changedBlock"
|
|
590
1002
|
};
|
|
591
|
-
const
|
|
592
|
-
const blockFrom = live.from;
|
|
593
|
-
const blockTo = live.to;
|
|
594
|
-
const cleanBlock = buildCleanBlockText(blockNode, blockFrom);
|
|
1003
|
+
const cleanBlock = buildCleanBlockText(live.node, live.from);
|
|
595
1004
|
const currentText = cleanBlock.text;
|
|
596
1005
|
const currentTextHash = hashFolioAIBlockText(normalizeFolioAIBlockText(currentText));
|
|
597
|
-
if (operation.precondition !== void 0 && currentTextHash !== operation.precondition.blockTextHash) return {
|
|
598
|
-
type: "skip",
|
|
599
|
-
reason: "preconditionFailed"
|
|
600
|
-
};
|
|
601
1006
|
if (currentTextHash !== anchor.textHash) return {
|
|
602
1007
|
type: "skip",
|
|
603
1008
|
reason: "changedBlock"
|
|
604
1009
|
};
|
|
1010
|
+
return {
|
|
1011
|
+
type: "resolved",
|
|
1012
|
+
blockNode: live.node,
|
|
1013
|
+
blockFrom: live.from,
|
|
1014
|
+
blockTo: live.to,
|
|
1015
|
+
cleanBlock,
|
|
1016
|
+
currentText,
|
|
1017
|
+
currentTextHash
|
|
1018
|
+
};
|
|
1019
|
+
};
|
|
1020
|
+
const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId, doc }) => {
|
|
1021
|
+
const blockId = operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange" ? operation.range.blockId : operation.blockId;
|
|
1022
|
+
const primaryBlock = resolveStableBlock({
|
|
1023
|
+
snapshot,
|
|
1024
|
+
blockId,
|
|
1025
|
+
liveBlocks,
|
|
1026
|
+
liveBlocksByParaId
|
|
1027
|
+
});
|
|
1028
|
+
if (primaryBlock.type === "skip") return primaryBlock;
|
|
1029
|
+
const { blockNode, blockFrom, blockTo, cleanBlock, currentText, currentTextHash } = primaryBlock;
|
|
1030
|
+
if (operation.precondition !== void 0 && currentTextHash !== operation.precondition.blockTextHash) return {
|
|
1031
|
+
type: "skip",
|
|
1032
|
+
reason: "preconditionFailed"
|
|
1033
|
+
};
|
|
605
1034
|
if (operation.type === "replaceRange" || operation.type === "commentOnRange" || operation.type === "formatRange") {
|
|
606
1035
|
const { startOffset, endOffset, selectedTextHash } = operation.range;
|
|
607
1036
|
const from = cleanBlock.offsets[startOffset];
|
|
@@ -676,6 +1105,207 @@ const resolveOperation = ({ snapshot, operation, liveBlocks, liveBlocksByParaId,
|
|
|
676
1105
|
}
|
|
677
1106
|
};
|
|
678
1107
|
}
|
|
1108
|
+
if (operation.type === "insertTableRow") {
|
|
1109
|
+
const insertion = findTableRowInsertion({
|
|
1110
|
+
doc,
|
|
1111
|
+
blockFrom,
|
|
1112
|
+
position: operation.position ?? "after"
|
|
1113
|
+
});
|
|
1114
|
+
if (!insertion || (operation.cellTexts?.length ?? 0) > insertion.cells.length) return {
|
|
1115
|
+
type: "skip",
|
|
1116
|
+
reason: "unsupportedBlock"
|
|
1117
|
+
};
|
|
1118
|
+
return {
|
|
1119
|
+
type: "resolved",
|
|
1120
|
+
operation: {
|
|
1121
|
+
operation,
|
|
1122
|
+
from: insertion.rowPosition,
|
|
1123
|
+
to: insertion.rowPosition,
|
|
1124
|
+
blockFrom,
|
|
1125
|
+
blockTo,
|
|
1126
|
+
blockNode,
|
|
1127
|
+
tableRowInsertion: insertion
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
if (operation.type === "deleteTableRow") {
|
|
1132
|
+
const target = findEnclosingTableRow(doc, blockFrom);
|
|
1133
|
+
if (!target) return {
|
|
1134
|
+
type: "skip",
|
|
1135
|
+
reason: "unsupportedBlock"
|
|
1136
|
+
};
|
|
1137
|
+
return {
|
|
1138
|
+
type: "resolved",
|
|
1139
|
+
operation: {
|
|
1140
|
+
operation,
|
|
1141
|
+
from: target.rowPosition,
|
|
1142
|
+
to: target.rowPosition,
|
|
1143
|
+
blockFrom,
|
|
1144
|
+
blockTo,
|
|
1145
|
+
blockNode,
|
|
1146
|
+
tableRowDeletion: {
|
|
1147
|
+
tableStart: target.tableStart,
|
|
1148
|
+
tablePosition: target.tablePosition,
|
|
1149
|
+
rowIndex: target.rowIndex,
|
|
1150
|
+
rowPosition: target.rowPosition
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
if (operation.type === "insertTableColumn") {
|
|
1156
|
+
const insertion = findTableColumnInsertion({
|
|
1157
|
+
doc,
|
|
1158
|
+
blockFrom,
|
|
1159
|
+
position: operation.position ?? "after"
|
|
1160
|
+
});
|
|
1161
|
+
if (!insertion) return {
|
|
1162
|
+
type: "skip",
|
|
1163
|
+
reason: "unsupportedBlock"
|
|
1164
|
+
};
|
|
1165
|
+
const { boundaryPosition, ...tableColumnInsertion } = insertion;
|
|
1166
|
+
return {
|
|
1167
|
+
type: "resolved",
|
|
1168
|
+
operation: {
|
|
1169
|
+
operation,
|
|
1170
|
+
from: boundaryPosition,
|
|
1171
|
+
to: boundaryPosition,
|
|
1172
|
+
blockFrom,
|
|
1173
|
+
blockTo,
|
|
1174
|
+
blockNode,
|
|
1175
|
+
tableColumnInsertion
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
if (operation.type === "deleteTableColumn") {
|
|
1180
|
+
const target = findEnclosingTableCell(doc, blockFrom);
|
|
1181
|
+
if (!target) return {
|
|
1182
|
+
type: "skip",
|
|
1183
|
+
reason: "unsupportedBlock"
|
|
1184
|
+
};
|
|
1185
|
+
return {
|
|
1186
|
+
type: "resolved",
|
|
1187
|
+
operation: {
|
|
1188
|
+
operation,
|
|
1189
|
+
from: target.cellPosition,
|
|
1190
|
+
to: target.cellPosition,
|
|
1191
|
+
blockFrom,
|
|
1192
|
+
blockTo,
|
|
1193
|
+
blockNode,
|
|
1194
|
+
tableColumnDeletion: {
|
|
1195
|
+
tablePosition: target.tablePosition,
|
|
1196
|
+
columnIndex: target.leftColumnIndex
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
if (operation.type === "mergeTableCells") {
|
|
1202
|
+
const startCell = findEnclosingTableCell(doc, blockFrom);
|
|
1203
|
+
if (!startCell) return {
|
|
1204
|
+
type: "skip",
|
|
1205
|
+
reason: "unsupportedBlock"
|
|
1206
|
+
};
|
|
1207
|
+
const table = doc.nodeAt(startCell.tablePosition);
|
|
1208
|
+
if (!table || table.type.spec["tableRole"] !== "table") return {
|
|
1209
|
+
type: "skip",
|
|
1210
|
+
reason: "unsupportedBlock"
|
|
1211
|
+
};
|
|
1212
|
+
const map = TableMap.get(table);
|
|
1213
|
+
let rectangle;
|
|
1214
|
+
let endCellPosition;
|
|
1215
|
+
let endCellEndPosition;
|
|
1216
|
+
if (operation.rowCount !== void 0) {
|
|
1217
|
+
rectangle = {
|
|
1218
|
+
left: startCell.leftColumnIndex,
|
|
1219
|
+
top: startCell.topRowIndex,
|
|
1220
|
+
right: startCell.rightColumnIndex,
|
|
1221
|
+
bottom: startCell.topRowIndex + operation.rowCount
|
|
1222
|
+
};
|
|
1223
|
+
if (rectangle.right - rectangle.left !== 1 || rectangle.bottom > map.height || operation.rowCount < 2) return {
|
|
1224
|
+
type: "skip",
|
|
1225
|
+
reason: "unsupportedBlock"
|
|
1226
|
+
};
|
|
1227
|
+
const endRelativePosition = map.map[(rectangle.bottom - 1) * map.width + rectangle.left];
|
|
1228
|
+
const endCell = endRelativePosition === void 0 ? null : table.nodeAt(endRelativePosition);
|
|
1229
|
+
if (!endCell || endRelativePosition === void 0) return {
|
|
1230
|
+
type: "skip",
|
|
1231
|
+
reason: "unsupportedBlock"
|
|
1232
|
+
};
|
|
1233
|
+
endCellPosition = startCell.tablePosition + 1 + endRelativePosition;
|
|
1234
|
+
endCellEndPosition = endCellPosition + endCell.nodeSize;
|
|
1235
|
+
} else {
|
|
1236
|
+
const endTarget = resolveStableBlock({
|
|
1237
|
+
snapshot,
|
|
1238
|
+
blockId: operation.endBlockId,
|
|
1239
|
+
liveBlocks,
|
|
1240
|
+
liveBlocksByParaId
|
|
1241
|
+
});
|
|
1242
|
+
if (endTarget.type === "skip") return endTarget;
|
|
1243
|
+
const endCell = findEnclosingTableCell(doc, endTarget.blockFrom);
|
|
1244
|
+
if (!endCell || startCell.tablePosition !== endCell.tablePosition) return {
|
|
1245
|
+
type: "skip",
|
|
1246
|
+
reason: "unsupportedBlock"
|
|
1247
|
+
};
|
|
1248
|
+
rectangle = {
|
|
1249
|
+
left: Math.min(startCell.leftColumnIndex, endCell.leftColumnIndex),
|
|
1250
|
+
top: Math.min(startCell.topRowIndex, endCell.topRowIndex),
|
|
1251
|
+
right: Math.max(startCell.rightColumnIndex, endCell.rightColumnIndex),
|
|
1252
|
+
bottom: Math.max(startCell.bottomRowIndex, endCell.bottomRowIndex)
|
|
1253
|
+
};
|
|
1254
|
+
endCellPosition = endCell.cellPosition;
|
|
1255
|
+
endCellEndPosition = endCell.cellEndPosition;
|
|
1256
|
+
}
|
|
1257
|
+
if (rectangle.right - rectangle.left === 1 && rectangle.bottom - rectangle.top === 1 || tableRectangleCutsMergedCell(map, rectangle)) return {
|
|
1258
|
+
type: "skip",
|
|
1259
|
+
reason: rectangle.right - rectangle.left === 1 && rectangle.bottom - rectangle.top === 1 ? "noopOperation" : "unsupportedBlock"
|
|
1260
|
+
};
|
|
1261
|
+
return {
|
|
1262
|
+
type: "resolved",
|
|
1263
|
+
operation: {
|
|
1264
|
+
operation,
|
|
1265
|
+
from: Math.min(startCell.cellPosition, endCellPosition),
|
|
1266
|
+
to: Math.max(startCell.cellEndPosition, endCellEndPosition),
|
|
1267
|
+
blockFrom,
|
|
1268
|
+
blockTo,
|
|
1269
|
+
blockNode,
|
|
1270
|
+
tableCellMerge: {
|
|
1271
|
+
tablePosition: startCell.tablePosition,
|
|
1272
|
+
rectangle
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
if (operation.type === "splitTableCell") {
|
|
1278
|
+
const cell = findEnclosingTableCell(doc, blockFrom);
|
|
1279
|
+
if (!cell) return {
|
|
1280
|
+
type: "skip",
|
|
1281
|
+
reason: "unsupportedBlock"
|
|
1282
|
+
};
|
|
1283
|
+
const rectangle = {
|
|
1284
|
+
left: cell.leftColumnIndex,
|
|
1285
|
+
top: cell.topRowIndex,
|
|
1286
|
+
right: cell.rightColumnIndex,
|
|
1287
|
+
bottom: cell.bottomRowIndex
|
|
1288
|
+
};
|
|
1289
|
+
if (rectangle.right - rectangle.left === 1 && rectangle.bottom - rectangle.top === 1) return {
|
|
1290
|
+
type: "skip",
|
|
1291
|
+
reason: "noopOperation"
|
|
1292
|
+
};
|
|
1293
|
+
return {
|
|
1294
|
+
type: "resolved",
|
|
1295
|
+
operation: {
|
|
1296
|
+
operation,
|
|
1297
|
+
from: cell.cellPosition,
|
|
1298
|
+
to: cell.cellEndPosition,
|
|
1299
|
+
blockFrom,
|
|
1300
|
+
blockTo,
|
|
1301
|
+
blockNode,
|
|
1302
|
+
tableCellSplit: {
|
|
1303
|
+
tablePosition: cell.tablePosition,
|
|
1304
|
+
rectangle
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
679
1309
|
if (operation.type === "deleteBlock" || operation.type === "replaceBlock") {
|
|
680
1310
|
const range = getTextRangeFromCleanBlock(cleanBlock);
|
|
681
1311
|
if (!range) {
|