@stll/folio-core 0.44.0 → 0.46.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/__fixtures__/paragraphs.js +2 -2
- package/dist/ai-edits/apply.js +59 -19
- package/dist/ai-edits/headless.js +2 -1
- package/dist/compare/compare.js +2 -5
- package/dist/compare/content-alignment.js +78 -53
- package/dist/compare/inline-atoms.js +34 -20
- package/dist/compare/scenario.js +2 -5
- package/dist/compare/types.d.ts +9 -1
- package/dist/compare/types.js +12 -1
- package/dist/compat/eigenpal.d.ts +2 -1
- package/dist/compat/eigenpal.js +2 -1
- package/dist/content-controls/checkboxDisplay.d.ts +11 -0
- package/dist/content-controls/checkboxDisplay.js +40 -0
- package/dist/content-controls/findContentControls.js +3 -1
- package/dist/content-controls/mutateContentControls.js +21 -4
- package/dist/controller/contentControlWidgetController.d.ts +1 -0
- package/dist/controller/contentControlWidgetController.js +3 -0
- package/dist/controller/hiddenEditorApi.js +3 -1
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +6 -3
- package/dist/display-list/build/buildDisplayList.js +20 -1
- package/dist/display-list/build/imagePrimitives.d.ts +1 -1
- package/dist/display-list/build/imagePrimitives.js +3 -1
- package/dist/display-list/build/paragraphPrimitives.js +21 -2
- package/dist/display-list/dom/renderDisplayListToDom.js +18 -10
- package/dist/display-list/types.d.ts +17 -0
- package/dist/document-operations.js +14 -3
- package/dist/docx/attributeRemainder.d.ts +43 -0
- package/dist/docx/attributeRemainder.js +65 -0
- package/dist/docx/blockContentParser.d.ts +1 -1
- package/dist/docx/blockContentParser.js +87 -63
- package/dist/docx/blockPlainText.d.ts +3 -4
- package/dist/docx/blockPlainText.js +1 -0
- package/dist/docx/bookmarkPlacement.js +2 -0
- package/dist/docx/borderParser.js +5 -5
- package/dist/docx/commentAnchorIndex.d.ts +30 -0
- package/dist/docx/commentAnchorIndex.js +50 -0
- package/dist/docx/commentParser.d.ts +1 -1
- package/dist/docx/commentParser.js +78 -50
- package/dist/docx/commentRangeIntegrity.d.ts +16 -1
- package/dist/docx/commentRangeIntegrity.js +42 -1
- package/dist/docx/commentRangeJoin.d.ts +9 -0
- package/dist/docx/commentRangeJoin.js +33 -0
- package/dist/docx/commentReferenceCompletion.d.ts +9 -0
- package/dist/docx/commentReferenceCompletion.js +47 -0
- package/dist/docx/commentReferenceNormalization.js +1 -0
- package/dist/docx/commentReplyMarkers.js +11 -19
- package/dist/docx/commentThreadKey.d.ts +18 -0
- package/dist/docx/commentThreadKey.js +22 -0
- package/dist/docx/compatibility.js +1 -0
- package/dist/docx/containerChildren.d.ts +111 -0
- package/dist/docx/containerChildren.gen.d.ts +28 -0
- package/dist/docx/containerChildren.gen.js +246 -0
- package/dist/docx/containerChildren.js +103 -0
- package/dist/docx/diagramPreview.js +87 -27
- package/dist/docx/documentParser.d.ts +1 -1
- package/dist/docx/documentParser.js +3 -3
- package/dist/docx/ensureParaIds.js +17 -7
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +4 -4
- package/dist/docx/fieldState.d.ts +26 -0
- package/dist/docx/fieldState.js +53 -0
- package/dist/docx/footnoteParser.d.ts +1 -1
- package/dist/docx/graphicFrameLocks.d.ts +16 -4
- package/dist/docx/graphicFrameLocks.js +19 -6
- package/dist/docx/groupDrawingParser.js +3 -3
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/headerFooterReferenceNormalization.js +1 -0
- package/dist/docx/hyperlinkParser.d.ts +27 -8
- package/dist/docx/hyperlinkParser.js +78 -20
- package/dist/docx/imageParser.d.ts +9 -1
- package/dist/docx/imageParser.js +91 -20
- package/dist/docx/imageRawXml.d.ts +14 -1
- package/dist/docx/imageRawXml.js +30 -6
- package/dist/docx/inlineWrapperContent.d.ts +85 -0
- package/dist/docx/inlineWrapperContent.js +84 -0
- package/dist/docx/mathToMathml.js +12 -14
- package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
- package/dist/docx/nonVisualDrawingProps.js +46 -0
- package/dist/docx/normalizeBaseDirection.js +10 -1
- package/dist/docx/paraIdAttribute.d.ts +21 -0
- package/dist/docx/paraIdAttribute.js +64 -0
- package/dist/docx/paragraphParser.d.ts +1 -1
- package/dist/docx/paragraphParser.js +277 -168
- package/dist/docx/paragraphPropertySource.js +5 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +9 -52
- package/dist/docx/paragraphTraversal.js +7 -4
- package/dist/docx/parseWarningMessage.js +2 -1
- package/dist/docx/parser.js +2 -2
- package/dist/docx/preservedRunContent.d.ts +32 -0
- package/dist/docx/preservedRunContent.js +86 -0
- package/dist/docx/previewBudget.d.ts +64 -0
- package/dist/docx/previewBudget.js +88 -0
- package/dist/docx/renderedPageBreakNormalization.js +2 -4
- package/dist/docx/revisionIdNormalization.js +17 -5
- package/dist/docx/rezip.js +91 -36
- package/dist/docx/runParser.d.ts +1 -1
- package/dist/docx/runParser.js +16 -12
- package/dist/docx/sdtPropertiesPatch.js +24 -18
- package/dist/docx/sectionParser.js +8 -1
- package/dist/docx/sectionReferenceHistory.js +2 -2
- package/dist/docx/selectiveSave.js +6 -6
- package/dist/docx/selectiveXmlPatch.d.ts +46 -2
- package/dist/docx/selectiveXmlPatch.js +86 -39
- package/dist/docx/serializer/blockSdtSerializer.js +38 -26
- package/dist/docx/serializer/borderSerializer.d.ts +1 -1
- package/dist/docx/serializer/borderSerializer.js +15 -14
- package/dist/docx/serializer/commentSerializer.d.ts +41 -16
- package/dist/docx/serializer/commentSerializer.js +87 -92
- package/dist/docx/serializer/documentSerializer.js +8 -11
- package/dist/docx/serializer/fontTableSerializer.js +6 -6
- package/dist/docx/serializer/headerFooterSerializer.js +12 -14
- package/dist/docx/serializer/markupRangeAttributes.js +2 -2
- package/dist/docx/serializer/noteSerializer.js +8 -8
- package/dist/docx/serializer/numberingSerializer.js +7 -6
- package/dist/docx/serializer/paragraphSerializer.js +93 -72
- package/dist/docx/serializer/partNamespaces.js +2 -2
- package/dist/docx/serializer/runSerializer.js +73 -44
- package/dist/docx/serializer/sectionPropertiesSerializer.js +19 -14
- package/dist/docx/serializer/settingsSerializer.js +4 -3
- package/dist/docx/serializer/stylesSerializer.js +6 -6
- package/dist/docx/serializer/tableSerializer.js +28 -17
- package/dist/docx/serializer/textFormattingSerializer.js +29 -28
- package/dist/docx/serializer/themeSerializer.js +6 -6
- package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
- package/dist/docx/serializer/xmlUtils.d.ts +1 -2
- package/dist/docx/serializer/xmlUtils.js +1 -13
- package/dist/docx/server/boundedArchive.d.ts +12 -0
- package/dist/docx/server/boundedArchive.js +20 -1
- package/dist/docx/server/createBilingualDocument.js +3 -1
- package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
- package/dist/docx/server/materializeYjsDocx.js +9 -1
- package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
- package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
- package/dist/docx/server/validateDocxConformance.js +22 -1
- package/dist/docx/shapeParser.js +12 -10
- package/dist/docx/tableParser.d.ts +1 -1
- package/dist/docx/tableParser.js +206 -85
- package/dist/docx/textBoxParser.d.ts +25 -2
- package/dist/docx/textBoxParser.js +59 -4
- package/dist/docx/unzip.d.ts +23 -0
- package/dist/docx/unzip.js +37 -26
- package/dist/docx/verbatimCapture.js +1 -1
- package/dist/docx/vmlImageParser.d.ts +17 -1
- package/dist/docx/vmlImageParser.js +20 -3
- package/dist/docx/vmlPreview.d.ts +1 -3
- package/dist/docx/vmlPreview.js +2 -30
- package/dist/docx/xmlEncoding.d.ts +5 -0
- package/dist/docx/xmlEncoding.js +12 -0
- package/dist/docx/xmlParser.d.ts +26 -2
- package/dist/docx/xmlParser.js +70 -27
- package/dist/docx/xmlResourceLimits.d.ts +89 -9
- package/dist/docx/xmlResourceLimits.js +105 -24
- package/dist/headless-layout.js +10 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
- package/dist/internal/paragraphFormattingSerialization.js +3 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +2 -0
- package/dist/layout-engine/types.d.ts +23 -1
- package/dist/layout-painter/renderImage.d.ts +5 -1
- package/dist/layout-painter/renderImage.js +14 -6
- package/dist/layout-painter/renderPage.d.ts +2 -0
- package/dist/layout-painter/renderPage.js +4 -0
- package/dist/layout-painter/renderParagraph.d.ts +13 -1
- package/dist/layout-painter/renderParagraph.js +26 -6
- package/dist/managers/autoSaveCodec.d.ts +9 -1
- package/dist/managers/autoSaveCodec.js +14 -12
- package/dist/markdown/images.js +1 -4
- package/dist/markdown/renderBlock.js +1 -0
- package/dist/markdown/renderRuns.d.ts +1 -1
- package/dist/markdown/renderRuns.js +28 -5
- package/dist/markdown/renderTable.js +22 -5
- package/dist/markdown/trailers.js +4 -1
- package/dist/pdf/images.js +142 -6
- package/dist/prosemirror/attrs/index.d.ts +14 -3
- package/dist/prosemirror/attrs/index.js +224 -2
- package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
- package/dist/prosemirror/authoredTransformAttrs.js +64 -0
- package/dist/prosemirror/commands/contentControls.js +10 -2
- package/dist/prosemirror/commands/image.d.ts +9 -2
- package/dist/prosemirror/commands/image.js +43 -28
- package/dist/prosemirror/commands/pageBreak.js +1 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
- package/dist/prosemirror/commentReferenceAttrs.js +33 -0
- package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
- package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
- package/dist/prosemirror/conversion/fromProseDoc.js +338 -99
- package/dist/prosemirror/conversion/index.d.ts +2 -2
- package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
- package/dist/prosemirror/conversion/toProseDoc.js +551 -327
- package/dist/prosemirror/extensions/StarterKit.js +8 -58
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
- package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
- package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
- package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
- package/dist/prosemirror/extensions/markRegistry.js +75 -0
- package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
- package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +20 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
- package/dist/prosemirror/extensions/nodes/ShapeExtension.js +10 -2
- package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +13 -5
- package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
- package/dist/prosemirror/inlineWrapperStack.js +75 -0
- package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
- package/dist/prosemirror/pageBreakRunProjection.js +19 -9
- package/dist/prosemirror/paragraphFormattingProvenance.d.ts +6 -3
- package/dist/prosemirror/paragraphFormattingProvenance.js +12 -3
- package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
- package/dist/prosemirror/replacedAnnotations.js +165 -0
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
- package/dist/prosemirror/schema/index.d.ts +3 -3
- package/dist/prosemirror/schema/marks.d.ts +25 -1
- package/dist/prosemirror/schema/nodes.d.ts +139 -3
- package/dist/prosemirror/schema/nodes.js +16 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
- package/dist/prosemirror/validation.js +16 -2
- package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
- package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
- package/dist/prosemirror/zeroWidthAnchors.js +2 -0
- package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
- package/dist/render-dom/commentAnchorAttributes.js +54 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +3 -1
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/base64.d.ts +36 -0
- package/dist/utils/base64.js +40 -0
- package/dist/utils/clipboard.d.ts +9 -1
- package/dist/utils/clipboard.js +29 -2
- package/dist/utils/findReplace.js +1 -1
- package/dist/utils/imageLuminance.d.ts +15 -0
- package/dist/utils/imageLuminance.js +32 -0
- package/dist/utils/mergeDocumentContent.js +7 -17
- package/dist/utils/replaceText.js +1 -0
- package/dist/utils/units.d.ts +10 -1
- package/dist/utils/units.js +12 -1
- package/dist/utils/urlSecurity.d.ts +8 -2
- package/dist/utils/urlSecurity.js +21 -3
- package/package.json +2 -2
- package/dist/docx/blockRangeMarkers.d.ts +0 -36
- package/dist/docx/blockRangeMarkers.js +0 -59
- package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
- package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { escapeXmlText } from "@stll/docx-core";
|
|
1
2
|
import JSZip from "jszip";
|
|
2
3
|
//#region src/ai-edits/__fixtures__/paragraphs.ts
|
|
3
4
|
/**
|
|
@@ -13,8 +14,7 @@ const ZIP_ENTRY_OPTIONS = {
|
|
|
13
14
|
date: new Date(Date.UTC(2e3, 0, 1)),
|
|
14
15
|
createFolders: false
|
|
15
16
|
};
|
|
16
|
-
const
|
|
17
|
-
const paragraphXml = (text) => `<w:p><w:r><w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r></w:p>`;
|
|
17
|
+
const paragraphXml = (text) => `<w:p><w:r><w:t xml:space="preserve">${escapeXmlText(text)}</w:t></w:r></w:p>`;
|
|
18
18
|
const buildParagraphsDocx = async (paragraphs, { inTableCell } = {}) => {
|
|
19
19
|
const paragraphsXml = paragraphs.map(paragraphXml).join("");
|
|
20
20
|
const body = inTableCell ? `<w:tbl><w:tblPr><w:tblW w:w="0" w:type="auto"/></w:tblPr><w:tblGrid><w:gridCol w:w="9360"/></w:tblGrid><w:tr><w:tc><w:tcPr><w:tcW w:w="9360" w:type="dxa"/></w:tcPr>${paragraphsXml}</w:tc></w:tr></w:tbl>` + paragraphXml("After the table.") : paragraphsXml;
|
package/dist/ai-edits/apply.js
CHANGED
|
@@ -10,6 +10,7 @@ import { directParagraphIndentation, paragraphIndentationAttrPatch, paragraphInd
|
|
|
10
10
|
import { directParagraphSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing } from "../prosemirror/paragraphSpacing.js";
|
|
11
11
|
import { getDocumentNumbering } from "../prosemirror/plugins/documentNumbering.js";
|
|
12
12
|
import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyles.js";
|
|
13
|
+
import { addCarriedMarks, annotatedReplacement, hasReplacedAnnotations, inheritedReplacementMarks, surveyReplacedAnnotations } from "../prosemirror/replacedAnnotations.js";
|
|
13
14
|
import { applyMarksToRunFormattingRepresentation, runFormattingInlineControlNodeName, selectRunFormattingCarrierRepresentations } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
14
15
|
import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
|
|
15
16
|
import { paragraphRunStyleContext, paragraphRunStyleContextAt } from "../prosemirror/runStyleFormatting.js";
|
|
@@ -949,6 +950,14 @@ const buildEmphasisInlineContent = (schema, text, baseMarks) => {
|
|
|
949
950
|
}
|
|
950
951
|
return nodes.length > 0 ? nodes : [schema.text(text, [...baseMarks])];
|
|
951
952
|
};
|
|
953
|
+
/**
|
|
954
|
+
* Inline content for a block the applier rebuilds whole, carrying over the
|
|
955
|
+
* annotations the block's old content held (see `replacedAnnotations`).
|
|
956
|
+
*/
|
|
957
|
+
const rebuiltBlockContent = ({ doc, blockFrom, blockTo, content }) => annotatedReplacement({
|
|
958
|
+
annotations: surveyReplacedAnnotations(doc, blockFrom + 1, blockTo - 1),
|
|
959
|
+
content: Fragment.fromArray([...content])
|
|
960
|
+
}).fragment;
|
|
952
961
|
const isBatchableParagraphInsertion = (item, mode) => {
|
|
953
962
|
if (item.operation.type !== "insertAfterBlock" && item.operation.type !== "insertBeforeBlock") return false;
|
|
954
963
|
if (item.commentText !== void 0) return false;
|
|
@@ -1608,7 +1617,12 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1608
1617
|
const replacement = item.operation.text;
|
|
1609
1618
|
const paragraphType = view.state.schema.nodes["paragraph"];
|
|
1610
1619
|
if (paragraphType) {
|
|
1611
|
-
const node = paragraphType.create(null,
|
|
1620
|
+
const node = paragraphType.create(null, rebuiltBlockContent({
|
|
1621
|
+
doc: tr.doc,
|
|
1622
|
+
blockFrom: item.blockFrom,
|
|
1623
|
+
blockTo: item.blockTo,
|
|
1624
|
+
content: replacement.length === 0 ? [] : buildEmphasisInlineContent(view.state.schema, replacement, commentMark ? [commentMark] : [])
|
|
1625
|
+
}));
|
|
1612
1626
|
tr = tr.replaceWith(item.blockFrom, item.blockTo, node);
|
|
1613
1627
|
tr = applyReplaceBlockStyleId({
|
|
1614
1628
|
item,
|
|
@@ -1619,7 +1633,12 @@ const applyFolioAIEditOperationsInternal = ({ view, snapshot, operations, mode =
|
|
|
1619
1633
|
}
|
|
1620
1634
|
}
|
|
1621
1635
|
if (mode === "direct" && hasInlineEmphasis(item.operation.text)) {
|
|
1622
|
-
const node = item.blockNode.type.create({ ...item.blockNode.attrs },
|
|
1636
|
+
const node = item.blockNode.type.create({ ...item.blockNode.attrs }, rebuiltBlockContent({
|
|
1637
|
+
doc: tr.doc,
|
|
1638
|
+
blockFrom: item.blockFrom,
|
|
1639
|
+
blockTo: item.blockTo,
|
|
1640
|
+
content: buildEmphasisInlineContent(view.state.schema, item.operation.text, commentMark ? [commentMark] : [])
|
|
1641
|
+
}));
|
|
1623
1642
|
tr = tr.replaceWith(item.blockFrom, item.blockTo, node);
|
|
1624
1643
|
tr = applyReplaceBlockStyleId({
|
|
1625
1644
|
item,
|
|
@@ -2342,18 +2361,28 @@ const cleanTextInlineNodes = ({ schema, text, marks = [] }) => {
|
|
|
2342
2361
|
if (trailing.length > 0) nodes.push(schema.text(trailing, marks));
|
|
2343
2362
|
return nodes;
|
|
2344
2363
|
};
|
|
2345
|
-
const insertCleanText = ({ tr, from, to, text }) => {
|
|
2346
|
-
|
|
2364
|
+
const insertCleanText = ({ tr, from, to, text, standsInFor }) => {
|
|
2365
|
+
const annotations = surveyReplacedAnnotations(tr.doc, from, to);
|
|
2366
|
+
const replaced = standsInFor ? surveyReplacedAnnotations(tr.doc, standsInFor.from, standsInFor.to).carried : [];
|
|
2367
|
+
if (!hasReplacedAnnotations(annotations) && replaced.length === 0 && !hasCleanTextControls(text)) return {
|
|
2347
2368
|
transaction: tr.insertText(text, from, to),
|
|
2369
|
+
start: from,
|
|
2348
2370
|
end: from + text.length
|
|
2349
2371
|
};
|
|
2350
|
-
const
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2372
|
+
const schema = tr.doc.type.schema;
|
|
2373
|
+
const { fragment, leadingSize, contentSize } = annotatedReplacement({
|
|
2374
|
+
annotations,
|
|
2375
|
+
content: Fragment.fromArray(cleanTextInlineNodes({
|
|
2376
|
+
schema,
|
|
2377
|
+
text,
|
|
2378
|
+
marks: addCarriedMarks(inheritedReplacementMarks(tr.doc, from, to), replaced)
|
|
2379
|
+
}))
|
|
2380
|
+
});
|
|
2381
|
+
const start = from + leadingSize;
|
|
2354
2382
|
return {
|
|
2355
|
-
transaction: tr.replaceWith(from, to,
|
|
2356
|
-
|
|
2383
|
+
transaction: tr.replaceWith(from, to, fragment),
|
|
2384
|
+
start,
|
|
2385
|
+
end: start + contentSize
|
|
2357
2386
|
};
|
|
2358
2387
|
};
|
|
2359
2388
|
const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete, revisionIdInsert, commentMark, suggestionId = null, initials, diffText }) => {
|
|
@@ -2365,8 +2394,11 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
2365
2394
|
})());
|
|
2366
2395
|
if (mode === "direct") {
|
|
2367
2396
|
if ((item.operation.type === "replaceInBlock" || item.operation.type === "replaceRange") && hasInlineEmphasis(item.operation.replace)) {
|
|
2368
|
-
const
|
|
2369
|
-
|
|
2397
|
+
const { fragment } = annotatedReplacement({
|
|
2398
|
+
annotations: surveyReplacedAnnotations(nextTr.doc, item.from, item.to),
|
|
2399
|
+
content: Fragment.fromArray(buildEmphasisInlineContent(nextTr.doc.type.schema, item.operation.replace, commentMark ? [commentMark] : []))
|
|
2400
|
+
});
|
|
2401
|
+
return nextTr.replaceWith(item.from, item.to, fragment);
|
|
2370
2402
|
}
|
|
2371
2403
|
const inserted = insertCleanText({
|
|
2372
2404
|
tr: nextTr,
|
|
@@ -2375,7 +2407,7 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
2375
2407
|
text: replacement
|
|
2376
2408
|
});
|
|
2377
2409
|
nextTr = inserted.transaction;
|
|
2378
|
-
if (commentMark && replacement.length > 0) nextTr = nextTr.addMark(
|
|
2410
|
+
if (commentMark && replacement.length > 0) nextTr = nextTr.addMark(inserted.start, inserted.end, commentMark);
|
|
2379
2411
|
return nextTr;
|
|
2380
2412
|
}
|
|
2381
2413
|
const insertionType = nextTr.doc.type.schema.marks["insertion"];
|
|
@@ -2465,11 +2497,15 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
2465
2497
|
tr: nextTr,
|
|
2466
2498
|
from: step.at,
|
|
2467
2499
|
to: step.at,
|
|
2468
|
-
text: step.text
|
|
2500
|
+
text: step.text,
|
|
2501
|
+
standsInFor: {
|
|
2502
|
+
from: item.from,
|
|
2503
|
+
to: item.to
|
|
2504
|
+
}
|
|
2469
2505
|
});
|
|
2470
2506
|
nextTr = inserted.transaction;
|
|
2471
|
-
nextTr = nextTr.addMark(
|
|
2472
|
-
if (commentMark) nextTr = nextTr.addMark(
|
|
2507
|
+
nextTr = nextTr.addMark(inserted.start, inserted.end, insertionType.create(insAttrs));
|
|
2508
|
+
if (commentMark) nextTr = nextTr.addMark(inserted.start, inserted.end, commentMark);
|
|
2473
2509
|
}
|
|
2474
2510
|
}
|
|
2475
2511
|
return nextTr;
|
|
@@ -2480,11 +2516,15 @@ const applyTextReplacement = ({ tr, item, mode, author, date, revisionIdDelete,
|
|
|
2480
2516
|
tr: nextTr,
|
|
2481
2517
|
from: item.to,
|
|
2482
2518
|
to: item.to,
|
|
2483
|
-
text: replacement
|
|
2519
|
+
text: replacement,
|
|
2520
|
+
standsInFor: {
|
|
2521
|
+
from: item.from,
|
|
2522
|
+
to: item.to
|
|
2523
|
+
}
|
|
2484
2524
|
});
|
|
2485
2525
|
nextTr = inserted.transaction;
|
|
2486
|
-
nextTr = nextTr.addMark(
|
|
2487
|
-
if (commentMark) nextTr = nextTr.addMark(
|
|
2526
|
+
nextTr = nextTr.addMark(inserted.start, inserted.end, insertionType.create(insAttrs));
|
|
2527
|
+
if (commentMark) nextTr = nextTr.addMark(inserted.start, inserted.end, commentMark);
|
|
2488
2528
|
}
|
|
2489
2529
|
if (item.to > item.from && deletionType) {
|
|
2490
2530
|
nextTr = nextTr.addMark(item.from, item.to, deletionType.create(delAttrs));
|
|
@@ -212,6 +212,7 @@ const sameReferencedNumberingLevels = ({ current, target, numId, levelsByNumId }
|
|
|
212
212
|
for (const level of levelsByNumId.get(numId) ?? []) {
|
|
213
213
|
const currentLevel = currentNumbering.getLevel(numId, level);
|
|
214
214
|
const targetLevel = targetNumbering.getLevel(numId, level);
|
|
215
|
+
if (currentLevel === null && targetLevel === null) continue;
|
|
215
216
|
if (!currentLevel || !targetLevel || canonicalJson(currentLevel) !== canonicalJson(targetLevel)) return false;
|
|
216
217
|
}
|
|
217
218
|
return true;
|
|
@@ -1543,7 +1544,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
|
|
|
1543
1544
|
* same states, so the text now describes exactly what a save would write.
|
|
1544
1545
|
*/
|
|
1545
1546
|
storyBlocks(state, source) {
|
|
1546
|
-
return state ? proseDocToBlocks(state.doc, source.content, this.baseDocument.package.styles
|
|
1547
|
+
return state ? proseDocToBlocks(state.doc, source.content, this.baseDocument.package.styles) : source.content;
|
|
1547
1548
|
}
|
|
1548
1549
|
getHeaderFooterStoryText(story, source) {
|
|
1549
1550
|
const state = this.secondaryStoryStates.get(headerFooterStoryKey(story))?.state;
|
package/dist/compare/compare.js
CHANGED
|
@@ -15,7 +15,7 @@ import { sameAuthoredInlineProvenance } from "./inline-provenance.js";
|
|
|
15
15
|
import { planStoryCompare } from "./plan.js";
|
|
16
16
|
import { withFixedPackageDates } from "./reproducible-package.js";
|
|
17
17
|
import { compareSectionBoundaryProperties } from "./section-boundary-properties.js";
|
|
18
|
-
import { CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError,
|
|
18
|
+
import { CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, InvalidCompareDocxOptionsError, compareDocxSerializeError } from "./types.js";
|
|
19
19
|
import { classifyGeometryMismatch, classifyProjectionMismatch, revisedFinalParagraphMarks } from "./verification.js";
|
|
20
20
|
import { Result, panic } from "better-result";
|
|
21
21
|
//#region src/compare/compare.ts
|
|
@@ -903,10 +903,7 @@ const serializeComparison = async ({ baseBuffer, baseCarriedRevisions, reviewer,
|
|
|
903
903
|
}));
|
|
904
904
|
return await Result.tryPromise({
|
|
905
905
|
try: async () => await withFixedPackageDates(await reviewer.toBuffer(), packageDate),
|
|
906
|
-
catch: (cause) =>
|
|
907
|
-
message: "The compared document could not be serialized.",
|
|
908
|
-
cause
|
|
909
|
-
})
|
|
906
|
+
catch: (cause) => compareDocxSerializeError("The compared document could not be serialized", cause)
|
|
910
907
|
});
|
|
911
908
|
};
|
|
912
909
|
/**
|
|
@@ -455,6 +455,27 @@ const MAX_CONTENT_STRUCTURE_PROFILE_TEXT_CODE_UNITS = 16384;
|
|
|
455
455
|
const MAX_CONTENT_STRUCTURE_PROFILE_TOKENS = 64;
|
|
456
456
|
const MAX_CONTENT_STRUCTURE_PROFILE_KIND_CODE_UNITS = 256;
|
|
457
457
|
const CONTENT_STRUCTURE_ANCHOR_MINIMUM_WORD_COUNT = 3;
|
|
458
|
+
/** Four independent 32-bit lanes, so a digest collision needs four at once. */
|
|
459
|
+
const CONTENT_DIGEST_SEEDS = [
|
|
460
|
+
2166136261,
|
|
461
|
+
2654435769,
|
|
462
|
+
2246822507,
|
|
463
|
+
3266489909
|
|
464
|
+
];
|
|
465
|
+
const CONTENT_DIGEST_PRIMES = [
|
|
466
|
+
16777619,
|
|
467
|
+
1540483477,
|
|
468
|
+
668265263,
|
|
469
|
+
374761393
|
|
470
|
+
];
|
|
471
|
+
const foldContentDigest = (lanes, text) => {
|
|
472
|
+
for (let index = 0; index < text.length; index++) {
|
|
473
|
+
const unit = text.charCodeAt(index);
|
|
474
|
+
for (let lane = 0; lane < lanes.length; lane++) lanes[lane] = Math.imul((lanes[lane] ?? 0) ^ unit, CONTENT_DIGEST_PRIMES[lane] ?? 1);
|
|
475
|
+
}
|
|
476
|
+
for (let lane = 0; lane < lanes.length; lane++) lanes[lane] = Math.imul((lanes[lane] ?? 0) ^ text.length, CONTENT_DIGEST_PRIMES[lane] ?? 1);
|
|
477
|
+
};
|
|
478
|
+
const renderContentDigest = (lanes, blockCount) => `${String(blockCount)}:${[...lanes].map((lane) => (lane >>> 0).toString(36)).join(":")}`;
|
|
458
479
|
const createContentStructureProfile = ({ blocks, blockStructure, idStability, physicalCellCount, retainContainerIdentity = false }) => {
|
|
459
480
|
let containerIdentity = null;
|
|
460
481
|
if (retainContainerIdentity) if (blocks.length > MAX_CONTENT_STRUCTURE_PROFILE_BLOCKS) containerIdentity = { status: "unavailable" };
|
|
@@ -481,39 +502,34 @@ const createContentStructureProfile = ({ blocks, blockStructure, idStability, ph
|
|
|
481
502
|
if (idStability(block) === "stable") stableIds.push(block.id);
|
|
482
503
|
}
|
|
483
504
|
}
|
|
484
|
-
|
|
485
|
-
|
|
505
|
+
const lanes = Int32Array.from(CONTENT_DIGEST_SEEDS);
|
|
506
|
+
for (const block of blocks) foldContentDigest(lanes, JSON.stringify([
|
|
507
|
+
...blockStructure(block),
|
|
508
|
+
block.kind,
|
|
509
|
+
block.text
|
|
510
|
+
]));
|
|
511
|
+
const identity = {
|
|
486
512
|
blockIds,
|
|
487
513
|
containerIdentity,
|
|
488
|
-
|
|
514
|
+
contentDigest: renderContentDigest(lanes, blocks.length),
|
|
489
515
|
physicalCellCount: physicalCellCount ?? null,
|
|
490
|
-
stableIds
|
|
491
|
-
|
|
492
|
-
|
|
516
|
+
stableIds
|
|
517
|
+
};
|
|
518
|
+
const overCap = {
|
|
519
|
+
...identity,
|
|
520
|
+
anchors: { status: "skipped-over-cap" },
|
|
521
|
+
tokens: { status: "skipped-over-cap" }
|
|
493
522
|
};
|
|
523
|
+
if (blocks.length > MAX_CONTENT_STRUCTURE_PROFILE_BLOCKS) return overCap;
|
|
494
524
|
let textCodeUnits = 0;
|
|
495
525
|
for (const block of blocks) {
|
|
496
|
-
if (block.kind.length > MAX_CONTENT_STRUCTURE_PROFILE_KIND_CODE_UNITS || block.text.length > MAX_CONTENT_STRUCTURE_PROFILE_TEXT_CODE_UNITS - textCodeUnits) return
|
|
497
|
-
anchorTexts: [],
|
|
498
|
-
blockIds,
|
|
499
|
-
containerIdentity,
|
|
500
|
-
exactSignature: null,
|
|
501
|
-
physicalCellCount: physicalCellCount ?? null,
|
|
502
|
-
stableIds,
|
|
503
|
-
tokenCounts: null,
|
|
504
|
-
tokenCount: 0
|
|
505
|
-
};
|
|
526
|
+
if (block.kind.length > MAX_CONTENT_STRUCTURE_PROFILE_KIND_CODE_UNITS || block.text.length > MAX_CONTENT_STRUCTURE_PROFILE_TEXT_CODE_UNITS - textCodeUnits) return overCap;
|
|
506
527
|
textCodeUnits += block.text.length;
|
|
507
528
|
}
|
|
508
529
|
const tokenCounts = /* @__PURE__ */ new Map();
|
|
509
530
|
const anchorTexts = /* @__PURE__ */ new Set();
|
|
510
531
|
let tokenCount = 0;
|
|
511
532
|
let tokensComplete = true;
|
|
512
|
-
const signature = blocks.map((block) => [
|
|
513
|
-
...blockStructure(block),
|
|
514
|
-
block.kind,
|
|
515
|
-
block.text
|
|
516
|
-
]);
|
|
517
533
|
for (const block of blocks) {
|
|
518
534
|
let blockWordCount = 0;
|
|
519
535
|
for (const match of block.text.matchAll(/\S+/gu)) {
|
|
@@ -530,24 +546,31 @@ const createContentStructureProfile = ({ blocks, blockStructure, idStability, ph
|
|
|
530
546
|
if (!tokensComplete) break;
|
|
531
547
|
}
|
|
532
548
|
return {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
549
|
+
...identity,
|
|
550
|
+
anchors: {
|
|
551
|
+
status: "computed",
|
|
552
|
+
texts: [...anchorTexts]
|
|
553
|
+
},
|
|
554
|
+
tokens: tokensComplete ? {
|
|
555
|
+
status: "computed",
|
|
556
|
+
counts: tokenCounts,
|
|
557
|
+
total: tokenCount
|
|
558
|
+
} : { status: "skipped-over-cap" }
|
|
541
559
|
};
|
|
542
560
|
};
|
|
561
|
+
const UNKNOWN_CONTENT_STRUCTURE_SIMILARITY = { status: "unknown" };
|
|
543
562
|
const contentStructureProfileSimilarity = (base, revised, workSession) => {
|
|
544
|
-
if (
|
|
545
|
-
|
|
546
|
-
|
|
563
|
+
if (base.tokens.status === "skipped-over-cap" || revised.tokens.status === "skipped-over-cap") return UNKNOWN_CONTENT_STRUCTURE_SIMILARITY;
|
|
564
|
+
if (base.tokens.total === 0 || revised.tokens.total === 0) return UNKNOWN_CONTENT_STRUCTURE_SIMILARITY;
|
|
565
|
+
const [tokens, counterparts] = base.tokens.counts.size <= revised.tokens.counts.size ? [base.tokens.counts, revised.tokens.counts] : [revised.tokens.counts, base.tokens.counts];
|
|
566
|
+
if (tokens.size > workSession.remainingStructuralTokenLookups) return UNKNOWN_CONTENT_STRUCTURE_SIMILARITY;
|
|
547
567
|
workSession.remainingStructuralTokenLookups -= tokens.size;
|
|
548
568
|
let shared = 0;
|
|
549
569
|
for (const [token, count] of tokens) shared += Math.min(count, counterparts.get(token) ?? 0);
|
|
550
|
-
return
|
|
570
|
+
return {
|
|
571
|
+
status: "measured",
|
|
572
|
+
value: 2 * shared / (base.tokens.total + revised.tokens.total)
|
|
573
|
+
};
|
|
551
574
|
};
|
|
552
575
|
const tableStructureProfile = (blocks, idStability) => {
|
|
553
576
|
const tableOrdinalByIndex = /* @__PURE__ */ new Map();
|
|
@@ -634,7 +657,6 @@ const uniqueExactContentSequencePairs = ({ baseKeys, revisedKeys }) => {
|
|
|
634
657
|
const uniqueIndexes = (keys) => {
|
|
635
658
|
const indexes = /* @__PURE__ */ new Map();
|
|
636
659
|
keys.forEach((key, index) => {
|
|
637
|
-
if (key === -1) return;
|
|
638
660
|
indexes.set(key, indexes.has(key) ? null : index);
|
|
639
661
|
});
|
|
640
662
|
return indexes;
|
|
@@ -731,16 +753,15 @@ const alignProfiledContentSequence = ({ base, revised, workSession, canPair = ()
|
|
|
731
753
|
}
|
|
732
754
|
const exactSignatureKeys = /* @__PURE__ */ new Map();
|
|
733
755
|
let nextExactSignatureKey = 0;
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
const existing = exactSignatureKeys.get(signature);
|
|
756
|
+
const internContentDigest = (digest) => {
|
|
757
|
+
const existing = exactSignatureKeys.get(digest);
|
|
737
758
|
if (existing !== void 0) return existing;
|
|
738
759
|
const key = nextExactSignatureKey++;
|
|
739
|
-
exactSignatureKeys.set(
|
|
760
|
+
exactSignatureKeys.set(digest, key);
|
|
740
761
|
return key;
|
|
741
762
|
};
|
|
742
|
-
const baseExactSignatureKeys = base.map(({ profile }) =>
|
|
743
|
-
const revisedExactSignatureKeys = revised.map(({ profile }) =>
|
|
763
|
+
const baseExactSignatureKeys = base.map(({ profile }) => internContentDigest(profile.contentDigest));
|
|
764
|
+
const revisedExactSignatureKeys = revised.map(({ profile }) => internContentDigest(profile.contentDigest));
|
|
744
765
|
let persistedPairs = /* @__PURE__ */ new Set();
|
|
745
766
|
if (base.some(({ profile }) => profile.containerIdentity !== null) || revised.some(({ profile }) => profile.containerIdentity !== null)) {
|
|
746
767
|
const exactPairs = uniqueExactContentSequencePairs({
|
|
@@ -781,16 +802,20 @@ const alignProfiledContentSequence = ({ base, revised, workSession, canPair = ()
|
|
|
781
802
|
if (!revisedItem) continue;
|
|
782
803
|
if (!canPair(baseItem, revisedItem)) continue;
|
|
783
804
|
const pairIndex = baseIndex * revised.length + revisedIndex;
|
|
784
|
-
const exact = baseExactSignatureKeys[baseIndex]
|
|
805
|
+
const exact = baseExactSignatureKeys[baseIndex] === revisedExactSignatureKeys[revisedIndex];
|
|
785
806
|
const stable = stablePairs.has(pairIndex);
|
|
786
|
-
const profileSimilarity = exact ?
|
|
787
|
-
|
|
807
|
+
const profileSimilarity = exact ? {
|
|
808
|
+
status: "measured",
|
|
809
|
+
value: 1
|
|
810
|
+
} : contentStructureProfileSimilarity(baseItem.profile, revisedItem.profile, workSession);
|
|
811
|
+
const similar = profileSimilarity.status === "measured" ? Math.max(0, Math.min(1, profileSimilarity.value * similarityFactor(baseItem, revisedItem))) : 0;
|
|
812
|
+
const similarEnough = profileSimilarity.status === "measured" && similar >= CONTENT_STRUCTURE_PAIR_SIMILARITY;
|
|
788
813
|
const stableAtSamePosition = stable && baseIndex === revisedIndex;
|
|
789
814
|
const persistedAtSamePosition = baseIndex === revisedIndex && provenanceTransitionAtSamePosition[baseIndex] === 1;
|
|
790
815
|
const shiftedPersisted = baseIndex !== revisedIndex && persistedPairs.has(pairIndex);
|
|
791
816
|
const persisted = persistedAtSamePosition || shiftedPersisted;
|
|
792
817
|
const soleStructuralSlot = pairSoleStructuralSlot && base.length === 1 && revised.length === 1;
|
|
793
|
-
if (!exact && !stableAtSamePosition && !persisted && !soleStructuralSlot &&
|
|
818
|
+
if (!exact && !stableAtSamePosition && !persisted && !soleStructuralSlot && !similarEnough) continue;
|
|
794
819
|
pairScores[pairIndex] = (stable ? stableWeight : 0) + (exact ? exactWeight : 0) + (shiftedPersisted && !stable && !exact ? secondaryWeight : 0) + (persistedAtSamePosition ? continuityBonus : 0) + Math.round(similar * CONTENT_STRUCTURE_SIMILARITY_SCALE) + (soleStructuralSlot ? 1 : 0);
|
|
795
820
|
}
|
|
796
821
|
}
|
|
@@ -1124,11 +1149,10 @@ const contentStructureEvidenceIndexes = (base, revised) => {
|
|
|
1124
1149
|
let nextExactKey = 0;
|
|
1125
1150
|
const internExactSignatures = (items) => {
|
|
1126
1151
|
for (const { profile } of items) {
|
|
1127
|
-
|
|
1128
|
-
let key = exactKeys.get(profile.exactSignature);
|
|
1152
|
+
let key = exactKeys.get(profile.contentDigest);
|
|
1129
1153
|
if (key === void 0) {
|
|
1130
1154
|
key = nextExactKey++;
|
|
1131
|
-
exactKeys.set(profile.
|
|
1155
|
+
exactKeys.set(profile.contentDigest, key);
|
|
1132
1156
|
}
|
|
1133
1157
|
exactKeyByProfile.set(profile, key);
|
|
1134
1158
|
}
|
|
@@ -1152,7 +1176,7 @@ const contentStructureEvidenceIndexes = (base, revised) => {
|
|
|
1152
1176
|
if (indexes.at(-1) !== itemIndex) indexes.push(itemIndex);
|
|
1153
1177
|
} else stableIndexes.set(id, [itemIndex]);
|
|
1154
1178
|
}
|
|
1155
|
-
for (const text of profile.
|
|
1179
|
+
for (const text of profile.anchors.status === "computed" ? profile.anchors.texts : []) {
|
|
1156
1180
|
const indexes = anchorTextIndexes.get(text);
|
|
1157
1181
|
if (indexes) {
|
|
1158
1182
|
if (indexes.at(-1) !== itemIndex) indexes.push(itemIndex);
|
|
@@ -1190,12 +1214,13 @@ const sortedIndexesLeaveRange = (indexes, start, end) => {
|
|
|
1190
1214
|
const profileHasEvidenceOutsideRange = (profile, opposite, start, end) => {
|
|
1191
1215
|
const exactKey = opposite.exactKeyByProfile.get(profile);
|
|
1192
1216
|
if (exactKey !== void 0 && sortedIndexesLeaveRange(opposite.exactIndexes.get(exactKey), start, end)) return true;
|
|
1193
|
-
if (profile.
|
|
1217
|
+
if (profile.anchors.status === "computed" && profile.anchors.texts.some((text) => sortedIndexesLeaveRange(opposite.anchorTextIndexes.get(text), start, end))) return true;
|
|
1194
1218
|
return profile.stableIds.some((id) => sortedIndexesLeaveRange(opposite.stableIndexes.get(id), start, end));
|
|
1195
1219
|
};
|
|
1196
1220
|
const profilesShareContentAnchor = (base, revised) => {
|
|
1197
|
-
|
|
1198
|
-
|
|
1221
|
+
if (base.anchors.status === "skipped-over-cap" || revised.anchors.status === "skipped-over-cap") return "unknown";
|
|
1222
|
+
const baseTexts = new Set(base.anchors.texts);
|
|
1223
|
+
return revised.anchors.texts.some((text) => baseTexts.has(text)) ? "shares" : "disjoint";
|
|
1199
1224
|
};
|
|
1200
1225
|
const isBodyDocumentSegment = (segment) => segment.kind === "body";
|
|
1201
1226
|
const profileBodySegments = (segments, idStability) => segments.flatMap((segment) => isBodyDocumentSegment(segment) ? [{
|
|
@@ -1288,13 +1313,13 @@ const pairTableSegmentsInGaps = ({ base, revised, baseGaps, revisedGaps, trusted
|
|
|
1288
1313
|
const revisedEnd = revisedStart + revisedItems.length;
|
|
1289
1314
|
const baseHasExternalEvidence = new Set(baseItems.filter(({ profile }) => profileHasEvidenceOutsideRange(profile, evidence.revised, revisedStart, revisedEnd)).map(({ item }) => item));
|
|
1290
1315
|
const revisedHasExternalEvidence = new Set(revisedItems.filter(({ profile }) => profileHasEvidenceOutsideRange(profile, evidence.base, baseStart, baseEnd)).map(({ item }) => item));
|
|
1291
|
-
const
|
|
1316
|
+
const solePairAnchorOverlap = baseItems.length === 1 && revisedItems.length === 1 ? profilesShareContentAnchor(baseItems.at(0)?.profile ?? panic("A sole base table has no profile"), revisedItems.at(0)?.profile ?? panic("A sole revised table has no profile")) : "disjoint";
|
|
1292
1317
|
const alignments = alignProfiledContentSequence({
|
|
1293
1318
|
base: baseItems,
|
|
1294
1319
|
revised: revisedItems,
|
|
1295
1320
|
workSession,
|
|
1296
1321
|
canPair: (baseItem, revisedItem) => !baseHasExternalEvidence.has(baseItem.item) && !revisedHasExternalEvidence.has(revisedItem.item),
|
|
1297
|
-
pairSoleStructuralSlot: trustedBodyPairCount > 0 ||
|
|
1322
|
+
pairSoleStructuralSlot: trustedBodyPairCount > 0 || solePairAnchorOverlap === "shares"
|
|
1298
1323
|
});
|
|
1299
1324
|
for (const alignment of alignments) {
|
|
1300
1325
|
if (alignment.type !== "pair") continue;
|
|
@@ -29,6 +29,15 @@ const textBlocksOf = (doc) => {
|
|
|
29
29
|
});
|
|
30
30
|
return blocks;
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Clean-text offset nearest a document position.
|
|
34
|
+
*
|
|
35
|
+
* `buildCleanBlockText` returns one offset per character plus one for
|
|
36
|
+
* end-of-block, so there is always an offset to land on. The result was typed
|
|
37
|
+
* `number | null` and never was: the caller's refusal branch could not run,
|
|
38
|
+
* while an empty `offsets` would have produced `-1` rather than the `null` the
|
|
39
|
+
* type promised.
|
|
40
|
+
*/
|
|
32
41
|
const offsetAt = ({ offsets, position }) => {
|
|
33
42
|
let low = 0;
|
|
34
43
|
let high = offsets.length;
|
|
@@ -40,16 +49,33 @@ const offsetAt = ({ offsets, position }) => {
|
|
|
40
49
|
}
|
|
41
50
|
return Math.min(low, offsets.length - 1);
|
|
42
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* Attributes a conversion mints for itself rather than reads from the package.
|
|
54
|
+
*
|
|
55
|
+
* `toProseDoc` salts text-box anchor ids with a per-load nonce so a pasted span
|
|
56
|
+
* cannot hijack a real anchor. A comparison reads two independent conversions,
|
|
57
|
+
* so a minted handle differs on every run, and a key holding one makes a
|
|
58
|
+
* document differ from itself. Only document facts belong in a key.
|
|
59
|
+
*
|
|
60
|
+
* The guard against a node type gaining a minted attribute without being added
|
|
61
|
+
* here is not this list but `compareIdentity.property.test.ts`, which compares
|
|
62
|
+
* generated packages carrying each atom with themselves.
|
|
63
|
+
*/
|
|
64
|
+
const CONVERSION_LOCAL_ATTRS = { textBoxAnchor: ["anchorId"] };
|
|
65
|
+
const documentFactAttrs = (node) => {
|
|
66
|
+
const local = CONVERSION_LOCAL_ATTRS[node.type.name];
|
|
67
|
+
if (!local) return node.attrs;
|
|
68
|
+
return Object.fromEntries(Object.entries(node.attrs).filter(([name]) => !local.includes(name)));
|
|
69
|
+
};
|
|
43
70
|
const atomKey = (node) => canonicalJson({
|
|
44
71
|
type: node.type.name,
|
|
45
|
-
attrs: node
|
|
72
|
+
attrs: documentFactAttrs(node),
|
|
46
73
|
content: node.content.toJSON()
|
|
47
74
|
});
|
|
48
75
|
const atomBlockOf = ({ node, from }) => {
|
|
49
76
|
const clean = buildCleanBlockText(node, from, { fieldResults: "omitted" });
|
|
50
77
|
const supported = [];
|
|
51
78
|
const unsupportedTopology = [];
|
|
52
|
-
let unalignable = false;
|
|
53
79
|
node.descendants((child, relativePosition) => {
|
|
54
80
|
if (child.isText) return false;
|
|
55
81
|
if (!child.isInline || !child.isAtom) return true;
|
|
@@ -58,18 +84,14 @@ const atomBlockOf = ({ node, from }) => {
|
|
|
58
84
|
offsets: clean.offsets,
|
|
59
85
|
position
|
|
60
86
|
});
|
|
61
|
-
if (offset === null) {
|
|
62
|
-
unalignable = true;
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
87
|
if (!isSupportedAtom(child)) {
|
|
66
88
|
if (child.type.name === "renderedPageBreak") return false;
|
|
67
|
-
unsupportedTopology.push(`${offset}:${child.type.name}:${canonicalJson(child
|
|
89
|
+
unsupportedTopology.push(`${offset}:${child.type.name}:${canonicalJson(documentFactAttrs(child))}`);
|
|
68
90
|
return false;
|
|
69
91
|
}
|
|
70
92
|
const prepared = prepareTargetInlineAtom(child);
|
|
71
93
|
if (!prepared) {
|
|
72
|
-
unsupportedTopology.push(`${offset}:${child.type.name}:${canonicalJson(child
|
|
94
|
+
unsupportedTopology.push(`${offset}:${child.type.name}:${canonicalJson(documentFactAttrs(child))}`);
|
|
73
95
|
return false;
|
|
74
96
|
}
|
|
75
97
|
supported.push({
|
|
@@ -80,7 +102,7 @@ const atomBlockOf = ({ node, from }) => {
|
|
|
80
102
|
});
|
|
81
103
|
return false;
|
|
82
104
|
});
|
|
83
|
-
return
|
|
105
|
+
return {
|
|
84
106
|
node,
|
|
85
107
|
from,
|
|
86
108
|
cleanText: clean.text,
|
|
@@ -108,15 +130,7 @@ const targetBlockIdLookup = (snapshot) => {
|
|
|
108
130
|
return owner?.id;
|
|
109
131
|
};
|
|
110
132
|
};
|
|
111
|
-
const atomBlocksOf = (doc) =>
|
|
112
|
-
const blocks = [];
|
|
113
|
-
for (const block of textBlocksOf(doc)) {
|
|
114
|
-
const atoms = atomBlockOf(block);
|
|
115
|
-
if (!atoms) return null;
|
|
116
|
-
blocks.push(atoms);
|
|
117
|
-
}
|
|
118
|
-
return blocks;
|
|
119
|
-
};
|
|
133
|
+
const atomBlocksOf = (doc) => textBlocksOf(doc).map(atomBlockOf);
|
|
120
134
|
const sameBlockTopology = (left, right) => left.node.type === right.node.type && left.cleanText === right.cleanText && canonicalJson(left.unsupportedTopology) === canonicalJson(right.unsupportedTopology);
|
|
121
135
|
const matchingAtomGroup = ({ live, target, liveStart, targetStart }) => {
|
|
122
136
|
if (live.length === target.length && live.every((atom, index) => atom.key === target[index]?.key)) return live.map((_, index) => [liveStart + index, targetStart + index]);
|
|
@@ -260,7 +274,7 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
|
|
|
260
274
|
let sourceBlocks;
|
|
261
275
|
const liveBlocks = atomBlocksOf(reviewed.state.doc);
|
|
262
276
|
const targetBlocks = atomBlocksOf(targetDocument);
|
|
263
|
-
if (
|
|
277
|
+
if (liveBlocks.length !== targetBlocks.length) return { status: "unalignable" };
|
|
264
278
|
const actions = [];
|
|
265
279
|
for (const [index, live] of liveBlocks.entries()) {
|
|
266
280
|
const target = targetBlocks[index];
|
|
@@ -366,7 +380,7 @@ const sameInlineAtoms = (leftDocument, rightDocument) => {
|
|
|
366
380
|
if (!hasSupportedAtom(leftDocument) && !hasSupportedAtom(rightDocument)) return true;
|
|
367
381
|
const leftBlocks = atomBlocksOf(leftDocument);
|
|
368
382
|
const rightBlocks = atomBlocksOf(rightDocument);
|
|
369
|
-
if (
|
|
383
|
+
if (leftBlocks.length !== rightBlocks.length) return false;
|
|
370
384
|
return leftBlocks.every((left, index) => {
|
|
371
385
|
const right = rightBlocks[index];
|
|
372
386
|
if (left.supported.length === 0 && right?.supported.length === 0) return true;
|
package/dist/compare/scenario.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FolioDocxReviewer } from "../ai-edits/headless.js";
|
|
2
2
|
import { createFolioAITextRangeHandle } from "../ai-edits/snapshot.js";
|
|
3
|
-
import { CompareDocxParseError,
|
|
3
|
+
import { CompareDocxParseError, compareDocxSerializeError } from "./types.js";
|
|
4
4
|
import { Result, panic } from "better-result";
|
|
5
5
|
//#region src/compare/scenario.ts
|
|
6
6
|
/**
|
|
@@ -252,10 +252,7 @@ const applyEditScript = async (base, script) => {
|
|
|
252
252
|
}
|
|
253
253
|
const serialized = await Result.tryPromise({
|
|
254
254
|
try: async () => await reviewer.toBuffer(),
|
|
255
|
-
catch: (cause) =>
|
|
256
|
-
message: "The edit script's target document could not be serialized.",
|
|
257
|
-
cause
|
|
258
|
-
})
|
|
255
|
+
catch: (cause) => compareDocxSerializeError("The edit script's target document could not be serialized", cause)
|
|
259
256
|
});
|
|
260
257
|
return serialized.isErr() ? Result.err(serialized.error) : Result.ok({
|
|
261
258
|
buffer: serialized.value,
|
package/dist/compare/types.d.ts
CHANGED
|
@@ -316,6 +316,14 @@ declare class CompareDocxSerializeError extends CompareDocxSerializeError_base<{
|
|
|
316
316
|
message: string;
|
|
317
317
|
cause: unknown;
|
|
318
318
|
}> {}
|
|
319
|
+
/**
|
|
320
|
+
* A serialize failure whose message names the reason.
|
|
321
|
+
*
|
|
322
|
+
* `cause` is structured context a caller can branch on, but a log line, a
|
|
323
|
+
* report and the corpus census read the message, and a constant one collapses
|
|
324
|
+
* every distinct save failure into a single undiagnosable row.
|
|
325
|
+
*/
|
|
326
|
+
declare const compareDocxSerializeError: (context: string, cause: unknown) => CompareDocxSerializeError;
|
|
319
327
|
declare const CompareDocxFinalParagraphMarkError_base: import("better-result").TaggedErrorClass<"CompareDocxFinalParagraphMarkError">;
|
|
320
328
|
/**
|
|
321
329
|
* A container's final paragraph mark carries a revision, so the package would
|
|
@@ -335,4 +343,4 @@ declare class CompareDocxFinalParagraphMarkError extends CompareDocxFinalParagra
|
|
|
335
343
|
}> {}
|
|
336
344
|
type CompareDocxError = CompareDocxApplyError | CompareDocxFinalParagraphMarkError | CompareDocxOperationLimitError | CompareDocxParseError | CompareDocxRoundTripError | CompareDocxSerializeError | InvalidCompareDocxOptionsError;
|
|
337
345
|
//#endregion
|
|
338
|
-
export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, CompareChange, CompareChangeLocation, CompareCompatibility, CompareDocxApplyError, CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, CompareFolioRequirement, CompareResult, CompareRevisionFormat, CompareUnsupportedPart, CompareUnsupportedReason, InvalidCompareDocxOptionsError };
|
|
346
|
+
export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, CompareChange, CompareChangeLocation, CompareCompatibility, CompareDocxApplyError, CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, CompareFolioRequirement, CompareResult, CompareRevisionFormat, CompareUnsupportedPart, CompareUnsupportedReason, InvalidCompareDocxOptionsError, compareDocxSerializeError };
|
package/dist/compare/types.js
CHANGED
|
@@ -31,6 +31,17 @@ var CompareDocxRoundTripError = class extends TaggedError("CompareDocxRoundTripE
|
|
|
31
31
|
var CompareDocxOperationLimitError = class extends TaggedError("CompareDocxOperationLimitError") {};
|
|
32
32
|
var CompareDocxSerializeError = class extends TaggedError("CompareDocxSerializeError") {};
|
|
33
33
|
/**
|
|
34
|
+
* A serialize failure whose message names the reason.
|
|
35
|
+
*
|
|
36
|
+
* `cause` is structured context a caller can branch on, but a log line, a
|
|
37
|
+
* report and the corpus census read the message, and a constant one collapses
|
|
38
|
+
* every distinct save failure into a single undiagnosable row.
|
|
39
|
+
*/
|
|
40
|
+
const compareDocxSerializeError = (context, cause) => new CompareDocxSerializeError({
|
|
41
|
+
message: `${context}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
42
|
+
cause
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
34
45
|
* A container's final paragraph mark carries a revision, so the package would
|
|
35
46
|
* not open, or would open carrying one no reader can resolve.
|
|
36
47
|
*
|
|
@@ -43,4 +54,4 @@ var CompareDocxSerializeError = class extends TaggedError("CompareDocxSerializeE
|
|
|
43
54
|
*/
|
|
44
55
|
var CompareDocxFinalParagraphMarkError = class extends TaggedError("CompareDocxFinalParagraphMarkError") {};
|
|
45
56
|
//#endregion
|
|
46
|
-
export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, InvalidCompareDocxOptionsError };
|
|
57
|
+
export { COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, CompareDocxApplyError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, InvalidCompareDocxOptionsError, compareDocxSerializeError };
|