@stll/folio-core 0.51.0 → 0.53.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/README.md +23 -0
- package/dist/ai-edits/apply.d.ts +26 -1
- package/dist/ai-edits/apply.js +1519 -105
- package/dist/ai-edits/batch-claims.d.ts +130 -0
- package/dist/ai-edits/batch-claims.js +244 -0
- package/dist/ai-edits/blockRange.js +9 -3
- package/dist/ai-edits/character-boundaries.d.ts +43 -0
- package/dist/ai-edits/character-boundaries.js +95 -0
- package/dist/ai-edits/clean-text.d.ts +33 -3
- package/dist/ai-edits/clean-text.js +52 -6
- package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
- package/dist/ai-edits/comment-lifecycle.js +52 -0
- package/dist/ai-edits/headless.d.ts +71 -7
- package/dist/ai-edits/headless.js +381 -41
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/index.js +2 -1
- package/dist/ai-edits/minimal-replacement.d.ts +21 -6
- package/dist/ai-edits/minimal-replacement.js +134 -15
- package/dist/ai-edits/newListNumbering.d.ts +23 -0
- package/dist/ai-edits/newListNumbering.js +94 -0
- package/dist/ai-edits/note-references.d.ts +96 -0
- package/dist/ai-edits/note-references.js +139 -0
- package/dist/ai-edits/pending-suggestions.d.ts +79 -0
- package/dist/ai-edits/pending-suggestions.js +306 -0
- package/dist/ai-edits/read.d.ts +1 -1
- package/dist/ai-edits/read.js +56 -43
- package/dist/ai-edits/result-validation.d.ts +29 -0
- package/dist/ai-edits/result-validation.js +164 -0
- package/dist/ai-edits/snapshot.d.ts +36 -3
- package/dist/ai-edits/snapshot.js +177 -23
- package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
- package/dist/ai-edits/table-cell-mutations.js +4 -1
- package/dist/ai-edits/table-geometry.d.ts +1 -1
- package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
- package/dist/ai-edits/table-row-column-mutations.js +61 -26
- package/dist/ai-edits/types.d.ts +155 -16
- package/dist/compare/compare.d.ts +1 -1
- package/dist/compare/compare.js +65 -16
- package/dist/compare/content-alignment.js +13 -2
- package/dist/compare/content-types.d.ts +7 -0
- package/dist/compare/content.d.ts +4 -1
- package/dist/compare/content.js +6 -1
- package/dist/compare/formatting.js +2 -1
- package/dist/compare/inline-atoms.d.ts +21 -4
- package/dist/compare/inline-atoms.js +138 -16
- package/dist/compare/inline-provenance.js +18 -22
- package/dist/compare/plan.js +10 -5
- package/dist/compare/scenario.js +14 -3
- package/dist/compare/section-boundary-properties.js +14 -1
- package/dist/compare/types.d.ts +4 -2
- package/dist/compare/types.js +2 -1
- package/dist/compare/verification.d.ts +1 -1
- package/dist/compare/verification.js +15 -3
- package/dist/controller/committedLayoutDiff.d.ts +25 -0
- package/dist/controller/committedLayoutDiff.js +71 -0
- package/dist/controller/contentControlWidgetController.d.ts +1 -1
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/fontReadiness.d.ts +70 -3
- package/dist/controller/fontReadiness.js +100 -10
- package/dist/controller/headerFooterEditorManager.js +6 -1
- package/dist/controller/layoutPipeline.d.ts +12 -10
- package/dist/controller/layoutPipeline.js +62 -9
- package/dist/controller/layoutRunOptions.d.ts +24 -0
- package/dist/controller/layoutRunOptions.js +17 -0
- package/dist/controller/layoutScheduler.d.ts +19 -14
- package/dist/controller/layoutScheduler.js +15 -12
- package/dist/controller/layoutSession.d.ts +26 -2
- package/dist/controller/layoutSession.js +1 -1
- package/dist/controller/noteEditorManager.js +8 -3
- package/dist/document-operations.d.ts +11 -2
- package/dist/document-operations.js +230 -21
- package/dist/docx/altChunk.d.ts +8 -0
- package/dist/docx/altChunk.js +17 -0
- package/dist/docx/attributeRemainder.d.ts +1 -1
- package/dist/docx/blockContentParser.d.ts +64 -1
- package/dist/docx/blockContentParser.js +17 -4
- package/dist/docx/blockCustomXmlShell.d.ts +7 -0
- package/dist/docx/blockCustomXmlShell.js +14 -0
- package/dist/docx/blockPlainText.js +7 -0
- package/dist/docx/commentAnchorIndex.js +1 -1
- package/dist/docx/commentRangeIntegrity.js +8 -3
- package/dist/docx/commentReferenceNormalization.js +1 -1
- package/dist/docx/commentReplyMarkers.js +20 -1
- package/dist/docx/compatibility.d.ts +1 -1
- package/dist/docx/compatibility.js +34 -13
- package/dist/docx/containerChildren.gen.d.ts +1 -1
- package/dist/docx/containerChildren.gen.js +1 -0
- package/dist/docx/documentParser.js +12 -12
- package/dist/docx/drawingIdNormalization.d.ts +3 -1
- package/dist/docx/drawingIdNormalization.js +6 -1
- package/dist/docx/ensureParaIds.js +119 -97
- package/dist/docx/footnoteParser.js +8 -4
- package/dist/docx/headerFooterParser.js +3 -3
- package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
- package/dist/docx/headerFooterReferenceNormalization.js +52 -27
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/listNumberingInstances.d.ts +59 -0
- package/dist/docx/listNumberingInstances.js +249 -0
- package/dist/docx/normalizeBaseDirection.js +2 -1
- package/dist/docx/opaqueCarrier.d.ts +7 -0
- package/dist/docx/opaqueCarrier.js +38 -0
- package/dist/docx/packageParts.js +4 -1
- package/dist/docx/paragraphParser.js +91 -24
- package/dist/docx/paragraphPropertySource.d.ts +4 -2
- package/dist/docx/paragraphPropertySource.js +7 -1
- package/dist/docx/paragraphTraversal.js +1 -0
- package/dist/docx/parseWarningMessage.js +4 -1
- package/dist/docx/parser.js +173 -20
- package/dist/docx/rezip.js +27 -5
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sdtPropertiesPatch.js +3 -0
- package/dist/docx/selectiveSave.js +16 -9
- package/dist/docx/selectiveXmlPatch.js +106 -37
- package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
- package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
- package/dist/docx/serializer/documentSerializer.js +2 -0
- package/dist/docx/serializer/headerFooterSerializer.js +2 -0
- package/dist/docx/serializer/noteSerializer.js +3 -1
- package/dist/docx/serializer/paragraphSerializer.js +33 -9
- package/dist/docx/serializer/tableSerializer.js +25 -38
- package/dist/docx/server/createBilingualDocument.js +3 -3
- package/dist/docx/tableParser.d.ts +157 -1
- package/dist/docx/tableParser.js +89 -10
- package/dist/docx/textBoxParser.js +1 -1
- package/dist/docx/unzip.d.ts +2 -1
- package/dist/docx/unzip.js +1 -1
- package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
- package/dist/docx/wordprocessingPrefixes.js +286 -0
- package/dist/generated/text_shaper.js +26 -0
- package/dist/generated/text_shaper_bg.wasm +0 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
- package/dist/i18n/messages/catalogs.gen.js +153 -17
- package/dist/i18n/messages/messages.gen.d.ts +8 -0
- package/dist/internal/compare/inline-presentation.d.ts +2 -1
- package/dist/internal/compare/inline-presentation.js +11 -10
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
- package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
- package/dist/internal/indexedPositionMap.d.ts +11 -0
- package/dist/internal/indexedPositionMap.js +42 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/revisionResolutionEdits.d.ts +13 -0
- package/dist/internal/revisionResolutionEdits.js +19 -0
- package/dist/internal/revisionResolutionInline.d.ts +24 -0
- package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
- package/dist/internal/revisionResolutionStep.d.ts +34 -0
- package/dist/internal/revisionResolutionStep.js +282 -0
- package/dist/internal/revisionResolutionTracking.d.ts +11 -0
- package/dist/internal/revisionResolutionTracking.js +124 -0
- package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
- package/dist/internal/wholeStoryRevisionResolution.js +530 -0
- package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
- package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
- package/dist/layout-bridge/convert/tableConversion.js +1 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
- package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
- package/dist/layout-engine/layoutInstrumentation.js +4 -1
- package/dist/layout-engine/measure/cache.d.ts +18 -1
- package/dist/layout-engine/measure/cache.js +31 -1
- package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
- package/dist/layout-engine/measure/measureContainer.js +7 -47
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
- package/dist/layout-engine/measure/measureHelpers.js +22 -2
- package/dist/layout-engine/measure/measureWorker.js +2 -3
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
- package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
- package/dist/layout-engine/types.d.ts +6 -0
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/managers/ContextMenuManager.js +5 -2
- package/dist/managers/types.d.ts +3 -0
- package/dist/markdown/fromMarkdown.js +2 -1
- package/dist/markdown/internals.d.ts +11 -1
- package/dist/markdown/internals.js +24 -3
- package/dist/markdown/renderBlock.js +39 -9
- package/dist/markdown/renderParagraph.d.ts +13 -1
- package/dist/markdown/renderParagraph.js +85 -38
- package/dist/markdown/renderRuns.js +3 -10
- package/dist/markdown/renderTable.js +24 -12
- package/dist/markdown/trailers.js +2 -2
- package/dist/markdown/types.d.ts +29 -7
- package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
- package/dist/paged-layout/incrementalMeasure.js +1 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
- package/dist/paged-layout/transactionDirtyRange.js +29 -41
- package/dist/panel-layout.d.ts +83 -0
- package/dist/panel-layout.js +96 -0
- package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
- package/dist/prosemirror/anchoredTextBoxes.js +156 -0
- package/dist/prosemirror/attrs/index.d.ts +6 -2
- package/dist/prosemirror/attrs/index.js +172 -1
- package/dist/prosemirror/commands/comments.d.ts +7 -8
- package/dist/prosemirror/commands/comments.js +241 -207
- package/dist/prosemirror/commands/contentControls.d.ts +1 -1
- package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
- package/dist/prosemirror/commands/formatting.d.ts +1 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +7 -1
- package/dist/prosemirror/commands/paragraph.js +9 -1
- package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
- package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
- package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
- package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
- package/dist/prosemirror/commands/sectionBreak.js +2 -1
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
- package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
- package/dist/prosemirror/conversion/toProseDoc.js +39 -4
- package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
- package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
- package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
- package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
- package/dist/prosemirror/extensions/StarterKit.js +3 -1
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
- package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
- package/dist/prosemirror/extensions/types.d.ts +4 -1
- package/dist/prosemirror/findReplaceSelection.js +1 -1
- package/dist/prosemirror/index.d.ts +3 -2
- package/dist/prosemirror/index.js +3 -2
- package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
- package/dist/prosemirror/indexedNodeLookup.js +103 -0
- package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
- package/dist/prosemirror/listAutoformatMarkers.js +98 -0
- package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
- package/dist/prosemirror/listInstanceReferences.js +72 -0
- package/dist/prosemirror/listLabels.d.ts +24 -0
- package/dist/prosemirror/listLabels.js +111 -0
- package/dist/prosemirror/listNumbering.d.ts +110 -0
- package/dist/prosemirror/listNumbering.js +345 -0
- package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
- package/dist/prosemirror/listRenderingAttrs.js +8 -1
- package/dist/prosemirror/markupViewProjection.d.ts +95 -0
- package/dist/prosemirror/markupViewProjection.js +83 -0
- package/dist/prosemirror/numberingAttr.d.ts +9 -1
- package/dist/prosemirror/numberingAttr.js +9 -1
- package/dist/prosemirror/outlineLevelAttr.js +1 -1
- package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
- package/dist/prosemirror/paragraphIndentation.js +29 -7
- package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
- package/dist/prosemirror/paragraphMarkJoin.js +24 -0
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
- package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
- package/dist/prosemirror/plugins/documentNumbering.js +97 -11
- package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
- package/dist/prosemirror/plugins/documentStyles.js +14 -1
- package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
- package/dist/prosemirror/plugins/suggestionMode.js +116 -52
- package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
- package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
- package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
- package/dist/prosemirror/positionSweep.d.ts +24 -0
- package/dist/prosemirror/positionSweep.js +222 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
- package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
- package/dist/prosemirror/replacedAnnotations.js +170 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
- package/dist/prosemirror/runStyleFormatting.d.ts +7 -1
- package/dist/prosemirror/runStyleFormatting.js +13 -7
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/index.js +3 -0
- package/dist/prosemirror/schema/nodes.d.ts +42 -11
- package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
- package/dist/prosemirror/sectionMarkEdits.js +59 -0
- package/dist/prosemirror/storyListNumbering.d.ts +17 -0
- package/dist/prosemirror/storyListNumbering.js +102 -0
- package/dist/prosemirror/tableGridMutation.d.ts +48 -2
- package/dist/prosemirror/tableGridMutation.js +106 -1
- package/dist/prosemirror/validation.js +5 -1
- package/dist/redline.js +93 -9
- package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
- package/dist/render-dom/BodySelectionOverlay.js +3 -2
- package/dist/shaping/shaper.d.ts +47 -4
- package/dist/shaping/shaper.js +46 -15
- package/dist/text-shaping.d.ts +4 -0
- package/dist/text-shaping.js +4 -0
- package/dist/utils/findReplace.js +2 -2
- package/dist/utils/mergeDocumentContent.js +1 -1
- package/dist/utils/noteReferenceLabels.d.ts +36 -0
- package/dist/utils/noteReferenceLabels.js +32 -0
- package/dist/utils/replaceText.js +1 -1
- package/dist/version-comparison.d.ts +1 -0
- package/dist/version-comparison.js +1 -0
- package/package.json +8 -2
- package/dist/internal/headlessRevisionResolution.d.ts +0 -23
package/dist/compare/compare.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { FolioDocxReviewer, getFolioDocxComparisonAccess } from "../ai-edits/headless.js";
|
|
2
|
-
import {
|
|
2
|
+
import { withComparisonNoteReferenceEdits } from "../ai-edits/note-references.js";
|
|
3
|
+
import { alignedNoteReferenceLabels, detachFolioAIEditSnapshotExternalHyperlinks, numberingReferenceKeysOf, relabelFolioAIEditSnapshotNoteReferences, remapFolioAIEditSnapshotNumberingReferences, remapFolioAIEditSnapshotStyleReferences, sourceDocumentOf, storyTablesOf } from "../ai-edits/snapshot.js";
|
|
3
4
|
import { projectTableGeometry } from "../ai-edits/table-geometry.js";
|
|
4
5
|
import { tableTemplateCanCrossPackageLosslessly } from "../ai-edits/table-template.js";
|
|
5
6
|
import { createScopedWordDiffOptions } from "../ai-edits/word-diff.js";
|
|
6
7
|
import "../document-operations.js";
|
|
7
8
|
import { pairFolioDocumentStories } from "../document-stories.js";
|
|
9
|
+
import { isAltChunkMarkup } from "../docx/altChunk.js";
|
|
10
|
+
import { isOpaqueNestedRowMarkup, opaqueRevisionCarrierName } from "../docx/opaqueCarrier.js";
|
|
8
11
|
import { sectionReferenceHistory } from "../docx/sectionReferenceHistory.js";
|
|
9
12
|
import { sameCanonicalInlinePresentation } from "../internal/compare/inline-presentation.js";
|
|
10
13
|
import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
|
|
11
14
|
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
12
15
|
import { createContentComparisonWorkSession } from "./content.js";
|
|
13
|
-
import { sameInlineAtoms } from "./inline-atoms.js";
|
|
16
|
+
import { MISSING_NOTE_DETAIL, sameInlineAtoms } from "./inline-atoms.js";
|
|
14
17
|
import { sameAuthoredInlineProvenance } from "./inline-provenance.js";
|
|
15
18
|
import { planStoryCompare } from "./plan.js";
|
|
16
19
|
import { withFixedPackageDates } from "./reproducible-package.js";
|
|
@@ -67,6 +70,26 @@ const parseSide = async (buffer, side, author) => await Result.tryPromise({
|
|
|
67
70
|
cause
|
|
68
71
|
})
|
|
69
72
|
});
|
|
73
|
+
const snapshotContainsUnsupportedCarrier = (snapshot) => {
|
|
74
|
+
let found = false;
|
|
75
|
+
sourceDocumentOf(snapshot).descendants((node) => {
|
|
76
|
+
if (node.type.name === "preservedBlock") {
|
|
77
|
+
const xml = node.attrs["xml"];
|
|
78
|
+
if (typeof xml === "string" && (isAltChunkMarkup(xml) || opaqueRevisionCarrierName(xml) !== void 0 || isOpaqueNestedRowMarkup(xml))) found = true;
|
|
79
|
+
}
|
|
80
|
+
const preserved = node.attrs["_preserved"];
|
|
81
|
+
if (preserved !== null && typeof preserved === "object" && "children" in preserved) {
|
|
82
|
+
const children = preserved.children;
|
|
83
|
+
if (!Array.isArray(children)) return;
|
|
84
|
+
for (const child of children) {
|
|
85
|
+
if (child === null || typeof child !== "object" || !("xml" in child)) continue;
|
|
86
|
+
const xml = child.xml;
|
|
87
|
+
if (typeof xml === "string" && (opaqueRevisionCarrierName(xml) !== void 0 || isOpaqueNestedRowMarkup(xml))) found = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return found;
|
|
92
|
+
};
|
|
70
93
|
/** Verify formatting only where the plan claims a text-equal formatting change. */
|
|
71
94
|
const formattingRoundTripFailure = ({ invariant, story, changes, actualBlocks, expectedBlocks, expectedBlockId }) => {
|
|
72
95
|
const expectedIndexById = new Map(expectedBlocks.map(({ id }, index) => [id, index]));
|
|
@@ -286,6 +309,14 @@ const parseComparison = async (base, target, options) => {
|
|
|
286
309
|
});
|
|
287
310
|
continue;
|
|
288
311
|
}
|
|
312
|
+
if (snapshotContainsUnsupportedCarrier(baseSnapshot) || snapshotContainsUnsupportedCarrier(targetSnapshot)) {
|
|
313
|
+
unsupported.push({
|
|
314
|
+
reason: "unsupported-content",
|
|
315
|
+
baseStory,
|
|
316
|
+
targetStory
|
|
317
|
+
});
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
289
320
|
const rawBaseSnapshot = baseSnapshot;
|
|
290
321
|
if (revisionFormat === "folio-exact" && baseStory.type === "main" && getFolioDocxComparisonAccess(reviewer).stageTerminalTableReviewCarrier(sourceDocumentOf(targetSnapshot))) {
|
|
291
322
|
const staged = reviewer.snapshotStory(baseStory);
|
|
@@ -368,6 +399,8 @@ const parseComparison = async (base, target, options) => {
|
|
|
368
399
|
unsupported
|
|
369
400
|
});
|
|
370
401
|
};
|
|
402
|
+
/** The note-reference markers a story's plan is written in: the base's, then the target's new notes. */
|
|
403
|
+
const noteReferenceLabelsOf = (pair) => alignedNoteReferenceLabels(sourceDocumentOf(pair.baseSnapshot), sourceDocumentOf(pair.targetSnapshot));
|
|
371
404
|
const planCopiesNonPortableWholeTable = (pair, plan) => {
|
|
372
405
|
const targetTables = new Map(storyTablesOf(pair.targetSnapshot).map(({ index, node }) => [index, node]));
|
|
373
406
|
return plan.tableTemplates.some(({ targetRowIndex, targetTableIndex }) => {
|
|
@@ -389,10 +422,11 @@ const planComparison = ({ pairs }) => {
|
|
|
389
422
|
const remainingStructuralTokenLookups = workSession.alignment.remainingStructuralTokenLookups;
|
|
390
423
|
const remainingMoveComparisons = workSession.remainingMoveComparisons;
|
|
391
424
|
const remainingMoveTokenLookups = workSession.remainingMoveTokenLookups;
|
|
425
|
+
const planningTarget = relabelFolioAIEditSnapshotNoteReferences(pair.targetSnapshot, noteReferenceLabelsOf(pair));
|
|
392
426
|
let plan = planStoryCompare({
|
|
393
427
|
story: pair.baseStory,
|
|
394
428
|
baseSnapshot: pair.baseSnapshot,
|
|
395
|
-
targetSnapshot:
|
|
429
|
+
targetSnapshot: planningTarget,
|
|
396
430
|
maxOperations: remainingOperations,
|
|
397
431
|
wholeTableReplacement: "allow",
|
|
398
432
|
workSession
|
|
@@ -405,7 +439,7 @@ const planComparison = ({ pairs }) => {
|
|
|
405
439
|
plan = planStoryCompare({
|
|
406
440
|
story: pair.baseStory,
|
|
407
441
|
baseSnapshot: pair.baseSnapshot,
|
|
408
|
-
targetSnapshot:
|
|
442
|
+
targetSnapshot: planningTarget,
|
|
409
443
|
maxOperations: remainingOperations,
|
|
410
444
|
wholeTableReplacement: "avoid",
|
|
411
445
|
workSession
|
|
@@ -454,7 +488,15 @@ const COMPARE_SKIP_DISPOSITION = {
|
|
|
454
488
|
pendingRunPropertyChange: "unwritable",
|
|
455
489
|
noopOperation: "unwritable",
|
|
456
490
|
documentVersionMismatch: "fatal",
|
|
457
|
-
documentNotEditable: "fatal"
|
|
491
|
+
documentNotEditable: "fatal",
|
|
492
|
+
payloadDoesNotFit: "unwritable",
|
|
493
|
+
overlappingOperation: "fatal",
|
|
494
|
+
splitsCharacter: "unwritable",
|
|
495
|
+
protectedReference: "unwritable",
|
|
496
|
+
missingStyle: "fatal",
|
|
497
|
+
missingNumbering: "fatal",
|
|
498
|
+
invalidResult: "fatal",
|
|
499
|
+
pendingDeletion: "unwritable"
|
|
458
500
|
};
|
|
459
501
|
const getCompareSkipDisposition = (reason) => COMPARE_SKIP_DISPOSITION[reason];
|
|
460
502
|
/**
|
|
@@ -494,8 +536,10 @@ const resolveTableTemplates = (targetTables, requests) => {
|
|
|
494
536
|
* what you could not represent" and "give me nothing unless you can prove it"
|
|
495
537
|
* are both legitimate asks and only the caller knows which one it is making.
|
|
496
538
|
*/
|
|
497
|
-
const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat, terminalTableCarrierStaged, numberingChanges, targetNumbering, targetNumberingReferences, targetNumberingReferenceMap, finalSectionComparison, styleImportFailure, pairs }, planned) => {
|
|
539
|
+
const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat, terminalTableCarrierStaged, numberingChanges, targetNumbering, targetNumberingReferences, targetNumberingReferenceMap, finalSectionComparison, styleImportFailure, pairs, unsupported }, planned) => {
|
|
498
540
|
const relationshipIds = sectionRelationshipMap(pairs);
|
|
541
|
+
const baseNotes = /* @__PURE__ */ new Set();
|
|
542
|
+
for (const { baseStory } of [...pairs, ...unsupported]) if (baseStory?.type === "footnote" || baseStory?.type === "endnote") baseNotes.add(`${baseStory.type}:${String(baseStory.noteId)}`);
|
|
499
543
|
const plannedOperationCount = planned.reduce((count, { plan }) => count + plan.operations.length, 0);
|
|
500
544
|
const finalSectionOperationCount = finalSectionComparison.status === "stage" ? 1 : 0;
|
|
501
545
|
if (plannedOperationCount + finalSectionOperationCount > 1e4) return Result.err(new CompareDocxOperationLimitError({
|
|
@@ -560,13 +604,14 @@ const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat,
|
|
|
560
604
|
documentChanged ||= geometryChanged;
|
|
561
605
|
idSeed = afterGeometry;
|
|
562
606
|
if (plan.operations.length > 0) {
|
|
563
|
-
const
|
|
607
|
+
const operationRevisionStamp = {
|
|
608
|
+
date: revisionStamp.date,
|
|
609
|
+
idSeed
|
|
610
|
+
};
|
|
611
|
+
const { skipped, nextRevisionId } = withComparisonNoteReferenceEdits(() => reviewer.applyDocumentOperationsToStory({
|
|
564
612
|
story: pair.baseStory,
|
|
565
613
|
snapshot: pair.baseSnapshot,
|
|
566
|
-
revisionStamp:
|
|
567
|
-
date: revisionStamp.date,
|
|
568
|
-
idSeed
|
|
569
|
-
},
|
|
614
|
+
revisionStamp: operationRevisionStamp,
|
|
570
615
|
wordDiff,
|
|
571
616
|
batch: {
|
|
572
617
|
version: 1,
|
|
@@ -574,8 +619,9 @@ const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat,
|
|
|
574
619
|
operations: plan.operations
|
|
575
620
|
},
|
|
576
621
|
tableTemplates: resolveTableTemplates(targetTables, plan.tableTemplates),
|
|
577
|
-
replacementBackground: "keep"
|
|
578
|
-
|
|
622
|
+
replacementBackground: "keep",
|
|
623
|
+
undefinedStyles: "keep"
|
|
624
|
+
}));
|
|
579
625
|
if (nextRevisionId === void 0) panic("The applier did not report where it left the revision-id counter", { story: pair.baseStory });
|
|
580
626
|
idSeed = nextRevisionId;
|
|
581
627
|
documentChanged = true;
|
|
@@ -614,6 +660,8 @@ const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat,
|
|
|
614
660
|
const atoms = comparisonAccess.matchInlineAtoms({
|
|
615
661
|
story: pair.baseStory,
|
|
616
662
|
targetSnapshot: pair.targetSnapshot,
|
|
663
|
+
noteReferenceLabels: noteReferenceLabelsOf(pair),
|
|
664
|
+
baseNotes,
|
|
617
665
|
revisionStamp: {
|
|
618
666
|
date: revisionStamp.date,
|
|
619
667
|
idSeed
|
|
@@ -646,7 +694,7 @@ const applyComparison = ({ reviewer, revisionStamp, granularity, revisionFormat,
|
|
|
646
694
|
invariant: "accept-reproduces-target",
|
|
647
695
|
cause: "inline-structure",
|
|
648
696
|
story: pair.baseStory,
|
|
649
|
-
detail: "supported inline atoms could not be aligned to the revised content"
|
|
697
|
+
detail: atoms.detail ?? "supported inline atoms could not be aligned to the revised content"
|
|
650
698
|
});
|
|
651
699
|
break;
|
|
652
700
|
case "budget-exceeded": return Result.err(new CompareDocxOperationLimitError({
|
|
@@ -927,8 +975,9 @@ const compareDocx = async (base, target, options) => {
|
|
|
927
975
|
const applied = applyComparison(parsed.value, planned.value);
|
|
928
976
|
if (applied.isErr()) return Result.err(applied.error);
|
|
929
977
|
const { changes, verification } = applied.value;
|
|
930
|
-
|
|
931
|
-
|
|
978
|
+
const missingNote = verification.status === "unverified" ? verification.failures.find(({ detail }) => detail === MISSING_NOTE_DETAIL) : void 0;
|
|
979
|
+
if (verification.status === "unverified" && (missingNote !== void 0 || (options.onUnverified ?? "refuse") === "refuse")) {
|
|
980
|
+
const firstFailure = missingNote ?? verification.failures[0];
|
|
932
981
|
if (firstFailure === void 0) panic("An unverified comparison reported no failing invariant");
|
|
933
982
|
return Result.err(new CompareDocxRoundTripError({
|
|
934
983
|
message: `The generated tracked changes do not satisfy ${firstFailure.invariant}: ${firstFailure.detail}`,
|
|
@@ -1331,7 +1331,7 @@ const pairTableRows = ({ baseRows, revisedRows, workSession, idStability }) => {
|
|
|
1331
1331
|
item: row,
|
|
1332
1332
|
profile: rowStructureProfile(row, idStability)
|
|
1333
1333
|
});
|
|
1334
|
-
|
|
1334
|
+
const aligned = alignProfiledContentSequence({
|
|
1335
1335
|
base: baseRows.map(profile),
|
|
1336
1336
|
revised: revisedRows.map(profile),
|
|
1337
1337
|
workSession,
|
|
@@ -1339,7 +1339,18 @@ const pairTableRows = ({ baseRows, revisedRows, workSession, idStability }) => {
|
|
|
1339
1339
|
soleShiftedPairResiduePolicy: "row-count-evidence",
|
|
1340
1340
|
primaryEvidence: "exact",
|
|
1341
1341
|
similarityFactor: (base, revised) => base.profile.physicalCellCount === revised.profile.physicalCellCount ? 1 : .5
|
|
1342
|
-
})
|
|
1342
|
+
});
|
|
1343
|
+
const baseRow = baseRows.at(0);
|
|
1344
|
+
const revisedRow = revisedRows.at(0);
|
|
1345
|
+
if (baseRows.length === 1 && baseRow !== void 0 && revisedRow !== void 0 && !aligned.some(({ type }) => type === "pair")) return [{
|
|
1346
|
+
type: "pair",
|
|
1347
|
+
baseRow,
|
|
1348
|
+
revisedRow
|
|
1349
|
+
}, ...revisedRows.slice(1).map((row) => ({
|
|
1350
|
+
type: "revisedOnly",
|
|
1351
|
+
row
|
|
1352
|
+
}))];
|
|
1353
|
+
return aligned.map((alignment) => {
|
|
1343
1354
|
switch (alignment.type) {
|
|
1344
1355
|
case "pair": return {
|
|
1345
1356
|
type: "pair",
|
|
@@ -116,8 +116,15 @@ type FolioContentBlock<Kind extends string = string> = {
|
|
|
116
116
|
idStability?: FolioContentIdStability;
|
|
117
117
|
/** One-based heading depth when the block has outline semantics. */
|
|
118
118
|
headingLevel?: number;
|
|
119
|
+
/**
|
|
120
|
+
* The number or bullet the block shows beside its text (`2.1.`, `a)`, `•`),
|
|
121
|
+
* a numbered heading's included; an unnumbered heading's style id. Absent
|
|
122
|
+
* when the block shows neither, a marker its level hides included.
|
|
123
|
+
*/
|
|
119
124
|
displayLabel?: string;
|
|
120
125
|
styleId?: string;
|
|
126
|
+
/** Authored `w:outlineLvl`; absent when heading depth comes only from a style. */
|
|
127
|
+
directOutlineLevel?: document_d_exports.OutlineLevel;
|
|
121
128
|
/** Direct paragraph alignment; absent when alignment comes only from a style. */
|
|
122
129
|
directAlignment?: FolioContentParagraphAlignment;
|
|
123
130
|
/** Direct paragraph spacing; absent when every spacing value is inherited. */
|
|
@@ -55,12 +55,15 @@ type FolioContentTextSegment = {
|
|
|
55
55
|
/** Target-side paragraph properties that differ from the base block. */
|
|
56
56
|
type FolioContentParagraphFormattingPatch = {
|
|
57
57
|
styleId?: string | null;
|
|
58
|
+
outlineLevel?: FolioContentBlock["directOutlineLevel"] | null;
|
|
58
59
|
listLevel?: number | null;
|
|
59
60
|
listReference?: FolioContentListReference | null;
|
|
60
61
|
alignment?: FolioContentBlock["directAlignment"] | null;
|
|
61
62
|
spacing?: FolioContentParagraphSpacing | null;
|
|
62
63
|
indentation?: FolioContentParagraphIndentation | null;
|
|
63
64
|
};
|
|
65
|
+
/** Authored outline identity, including the distinction between heading and body text. */
|
|
66
|
+
declare const sameFolioContentOutlineLevel: (left: FolioContentBlock["directOutlineLevel"], right: FolioContentBlock["directOutlineLevel"]) => boolean;
|
|
64
67
|
/** Presentation differences for one text-aligned block pair. */
|
|
65
68
|
type FolioContentFormattingChange = {
|
|
66
69
|
paragraph?: FolioContentParagraphFormattingPatch;
|
|
@@ -210,4 +213,4 @@ declare const compareAlignedFolioContent: <Block extends FolioContentBlock>({ ba
|
|
|
210
213
|
/** Compare two representation-neutral ordered content snapshots. */
|
|
211
214
|
declare const compareContent: <Block extends FolioContentBlock = FolioContentBlock>(options: CompareContentOptions<Block>) => Result<FolioContentComparison<Block>, FolioContentComparisonError>;
|
|
212
215
|
//#endregion
|
|
213
|
-
export { CompareContentOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentBlockProperty, FolioContentComparison, FolioContentComparisonError, FolioContentComparisonEvent, FolioContentComparisonLimit, FolioContentComparisonLimitError, FolioContentComparisonWorkSession, type FolioContentFormatRange, FolioContentFormattingChange, FolioContentInlinePresentationProjectionError, FolioContentParagraphFormattingPatch, FolioContentStructuralChange, FolioContentTextSegment, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans };
|
|
216
|
+
export { CompareContentOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentBlockProperty, FolioContentComparison, FolioContentComparisonError, FolioContentComparisonEvent, FolioContentComparisonLimit, FolioContentComparisonLimitError, FolioContentComparisonWorkSession, type FolioContentFormatRange, FolioContentFormattingChange, FolioContentInlinePresentationProjectionError, FolioContentParagraphFormattingPatch, FolioContentStructuralChange, FolioContentTextSegment, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans, sameFolioContentOutlineLevel };
|
package/dist/compare/content.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WORD_DIFF_GRANULARITIES, createWordDiffSession } from "../ai-edits/word-diff.js";
|
|
2
|
+
import { outlineLevelFromAttrValue } from "../prosemirror/outlineLevelAttr.js";
|
|
2
3
|
import { paragraphIndentationEqual } from "../prosemirror/paragraphIndentation.js";
|
|
3
4
|
import { PARAGRAPH_ALIGNMENT_VALUES } from "../types/documentEnumValues.js";
|
|
4
5
|
import { alignFolioContentStructure, contentBlocksShareContainer, createFolioContentAlignmentWorkSession } from "./content-alignment.js";
|
|
@@ -43,6 +44,8 @@ const MAX_MOVE_PROFILE_CODE_UNITS = 1048576;
|
|
|
43
44
|
var InvalidFolioContentComparisonError = class extends TaggedError("InvalidFolioContentComparisonError") {};
|
|
44
45
|
var FolioContentComparisonLimitError = class extends TaggedError("FolioContentComparisonLimitError") {};
|
|
45
46
|
var FolioContentInlinePresentationProjectionError = class extends TaggedError("FolioContentInlinePresentationProjectionError") {};
|
|
47
|
+
/** Authored outline identity, including the distinction between heading and body text. */
|
|
48
|
+
const sameFolioContentOutlineLevel = (left, right) => left?.kind === right?.kind && (left?.kind !== "heading" || right?.kind === "heading" && left.level === right.level);
|
|
46
49
|
const createContentComparisonWorkSession = (granularity) => ({
|
|
47
50
|
alignment: createFolioContentAlignmentWorkSession(),
|
|
48
51
|
remainingMoveComparisons: MAX_MOVE_SIMILARITY_COMPARISONS,
|
|
@@ -99,6 +102,7 @@ const validateParagraphFormatting = (block, side, blockIndex) => {
|
|
|
99
102
|
if (block.headingLevel !== void 0 && (!isFiniteInteger(block.headingLevel) || block.headingLevel < 1)) return invalidInput(side, `blocks[${String(blockIndex)}].headingLevel`, "Heading levels must be positive integers.", blockIndex);
|
|
100
103
|
if (block.listLevel !== void 0 && (!isFiniteInteger(block.listLevel) || block.listLevel < 0)) return invalidInput(side, `blocks[${String(blockIndex)}].listLevel`, "List levels must be non-negative integers.", blockIndex);
|
|
101
104
|
if (block.directAlignment !== void 0 && !PARAGRAPH_ALIGNMENTS.has(block.directAlignment)) return invalidInput(side, `blocks[${String(blockIndex)}].directAlignment`, "Paragraph alignment is not recognized.", blockIndex);
|
|
105
|
+
if (block.directOutlineLevel !== void 0 && outlineLevelFromAttrValue(block.directOutlineLevel) === null) return invalidInput(side, `blocks[${String(blockIndex)}].directOutlineLevel`, "Direct outline level is not recognized.", blockIndex);
|
|
102
106
|
const spacing = block.directSpacing;
|
|
103
107
|
if (spacing === void 0) return null;
|
|
104
108
|
if (!isRecord(spacing)) return invalidInput(side, `blocks[${String(blockIndex)}].directSpacing`, "Direct paragraph spacing must be an object.", blockIndex);
|
|
@@ -409,6 +413,7 @@ const paragraphSpacingEqual = (base, revised) => base?.spaceBefore === revised?.
|
|
|
409
413
|
const changedFolioContentParagraphFormatting = (base, revised) => {
|
|
410
414
|
const patch = {};
|
|
411
415
|
if ((base.styleId ?? null) !== (revised.styleId ?? null)) patch.styleId = revised.styleId ?? null;
|
|
416
|
+
if (!sameFolioContentOutlineLevel(base.directOutlineLevel, revised.directOutlineLevel)) patch.outlineLevel = revised.directOutlineLevel ?? null;
|
|
412
417
|
const listReferenceChanged = base.listReference?.numId !== revised.listReference?.numId;
|
|
413
418
|
const targetOmitsLevelOnChangedReference = listReferenceChanged && revised.listReference !== void 0 && revised.listLevel === void 0;
|
|
414
419
|
if (base.listLevel !== revised.listLevel || targetOmitsLevelOnChangedReference) patch.listLevel = revised.listLevel ?? null;
|
|
@@ -950,4 +955,4 @@ const compareContent = (options) => {
|
|
|
950
955
|
});
|
|
951
956
|
};
|
|
952
957
|
//#endregion
|
|
953
|
-
export { FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans };
|
|
958
|
+
export { FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans, sameFolioContentOutlineLevel };
|
|
@@ -5,7 +5,8 @@ import { canonicalInlinePresentationSegments } from "../internal/compare/inline-
|
|
|
5
5
|
const inlineFormattingSegments = ({ baseBlock, targetBlock, maxSegments }) => canonicalInlinePresentationSegments({
|
|
6
6
|
baseBlock,
|
|
7
7
|
revisedBlock: targetBlock,
|
|
8
|
-
maxSegments
|
|
8
|
+
maxSegments,
|
|
9
|
+
presentationBasis: baseBlock.styleId === targetBlock.styleId ? "full" : "authored"
|
|
9
10
|
});
|
|
10
11
|
//#endregion
|
|
11
12
|
export { inlineFormattingSegments };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { FolioAIEditSnapshot } from "../ai-edits/types.js";
|
|
2
2
|
import { FolioRevisionStamp } from "../ai-edits/apply.js";
|
|
3
|
-
import {
|
|
3
|
+
import { NoteReferenceLabels } from "../ai-edits/note-references.js";
|
|
4
4
|
import { Node } from "prosemirror-model";
|
|
5
|
+
import { EditorState, Transaction } from "prosemirror-state";
|
|
5
6
|
//#region src/compare/inline-atoms.d.ts
|
|
6
7
|
type MatchInlineAtomsOptions = {
|
|
7
8
|
state: EditorState;
|
|
@@ -11,6 +12,17 @@ type MatchInlineAtomsOptions = {
|
|
|
11
12
|
originalRevisionIdSeed: number;
|
|
12
13
|
author: string;
|
|
13
14
|
maxRanges: number;
|
|
15
|
+
/**
|
|
16
|
+
* The markers the comparison's operations wrote note references as.
|
|
17
|
+
* Defaults to the target story's own numbering.
|
|
18
|
+
*/
|
|
19
|
+
noteReferenceLabels?: NoteReferenceLabels;
|
|
20
|
+
/**
|
|
21
|
+
* The notes the base package defines, as `footnote:<id>` / `endnote:<id>`.
|
|
22
|
+
* A reference the comparison writes may only name one of them. Defaults to
|
|
23
|
+
* the notes the story already references.
|
|
24
|
+
*/
|
|
25
|
+
baseNotes?: ReadonlySet<string>;
|
|
14
26
|
};
|
|
15
27
|
type MatchInlineAtomsResult = {
|
|
16
28
|
status: "matched";
|
|
@@ -20,16 +32,21 @@ type MatchInlineAtomsResult = {
|
|
|
20
32
|
rangeCount: number;
|
|
21
33
|
} | {
|
|
22
34
|
status: "unalignable";
|
|
35
|
+
/** Why, when the cause is more specific than an alignment failure. */
|
|
36
|
+
detail?: string;
|
|
23
37
|
} | {
|
|
24
38
|
status: "budget-exceeded";
|
|
25
39
|
};
|
|
40
|
+
/** A revised reference names a note the redline's package does not have. */
|
|
41
|
+
declare const MISSING_NOTE_DETAIL = "a note reference names a footnote or endnote the base document does not have";
|
|
26
42
|
/**
|
|
27
|
-
* Restore field, image, and page-break atoms omitted by text-only comparison
|
|
43
|
+
* Restore field, image, and page-break atoms omitted by text-only comparison
|
|
44
|
+
* operations, and the note references whose markers they wrote as text.
|
|
28
45
|
* Positions come from the legacy review resolver's mapping, never a guessed
|
|
29
46
|
* paragraph correspondence: paragraph-mark deletions can merge paragraphs.
|
|
30
47
|
*/
|
|
31
|
-
declare const matchInlineAtoms: ({ state, targetSnapshot, revisionStamp, originalRevisionIdSeed, author, maxRanges }: MatchInlineAtomsOptions) => MatchInlineAtomsResult;
|
|
48
|
+
declare const matchInlineAtoms: ({ state, targetSnapshot, revisionStamp, originalRevisionIdSeed, author, maxRanges, noteReferenceLabels, baseNotes }: MatchInlineAtomsOptions) => MatchInlineAtomsResult;
|
|
32
49
|
/** Compare supported inline atom identity after accept or reject projection. */
|
|
33
50
|
declare const sameInlineAtoms: (leftDocument: Node, rightDocument: Node) => boolean;
|
|
34
51
|
//#endregion
|
|
35
|
-
export { MatchInlineAtomsOptions, MatchInlineAtomsResult, matchInlineAtoms, sameInlineAtoms };
|
|
52
|
+
export { MISSING_NOTE_DETAIL, MatchInlineAtomsOptions, MatchInlineAtomsResult, matchInlineAtoms, sameInlineAtoms };
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { buildCleanBlockText } from "../ai-edits/clean-text.js";
|
|
2
|
-
import {
|
|
2
|
+
import { noteReferenceOf } from "../ai-edits/note-references.js";
|
|
3
|
+
import { collectNoteReferenceLabels, sourceDocumentOf } from "../ai-edits/snapshot.js";
|
|
4
|
+
import { drawingXmlWithoutIdentity } from "../docx/drawingIdNormalization.js";
|
|
3
5
|
import { resolveAllChangesInHeadlessStateWithMapping } from "../prosemirror/commands/comments.js";
|
|
4
6
|
import { runFormattingInlineAtomResultText } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
5
7
|
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
6
8
|
import { prepareTargetInlineAtom } from "./inline-atom-resources.js";
|
|
7
9
|
//#region src/compare/inline-atoms.ts
|
|
10
|
+
/** A revised reference names a note the redline's package does not have. */
|
|
11
|
+
const MISSING_NOTE_DETAIL = "a note reference names a footnote or endnote the base document does not have";
|
|
8
12
|
const actionRangeCount = (action) => action.kind === "replace-text" && action.textDisposition === "retained" || action.kind === "replace-atom" && action.atomDisposition === "retained" ? 2 : 1;
|
|
9
13
|
const MAX_INLINE_ATOM_LCS_CELLS = 16384;
|
|
10
14
|
const isSupportedAtom = (node) => node.isInline && node.isAtom && (node.type.name === "field" || node.type.name === "image" || node.type.name === "pageBreakRun");
|
|
@@ -65,6 +69,11 @@ const offsetAt = ({ offsets, position }) => {
|
|
|
65
69
|
*/
|
|
66
70
|
const CONVERSION_LOCAL_ATTRS = { textBoxAnchor: ["anchorId"] };
|
|
67
71
|
const documentFactAttrs = (node) => {
|
|
72
|
+
if (node.type.name === "field" || node.type.name === "structuredField") {
|
|
73
|
+
const equivalentFieldForms = node.type.name === "field" && (node.attrs["fieldKind"] === "simple" || node.attrs["fieldKind"] === "complex") && node.attrs["fieldResultIsFallback"] == null;
|
|
74
|
+
return Object.fromEntries(Object.entries(node.attrs).filter(([name]) => name !== "_docxFieldCode" && (!equivalentFieldForms || name !== "fieldKind")));
|
|
75
|
+
}
|
|
76
|
+
if (node.type.name === "image") return Object.fromEntries(Object.entries(node.attrs).filter(([name]) => name !== "docPrId").map(([name, value]) => [name, name === "_docxRawXml" && typeof value === "string" ? drawingXmlWithoutIdentity(value) : value]));
|
|
68
77
|
const local = CONVERSION_LOCAL_ATTRS[node.type.name];
|
|
69
78
|
if (!local) return node.attrs;
|
|
70
79
|
return Object.fromEntries(Object.entries(node.attrs).filter(([name]) => !local.includes(name)));
|
|
@@ -74,8 +83,22 @@ const atomKey = (node) => canonicalJson({
|
|
|
74
83
|
attrs: documentFactAttrs(node),
|
|
75
84
|
content: node.content.toJSON()
|
|
76
85
|
});
|
|
77
|
-
const atomBlockOf = ({ node, from }, fieldResults) => {
|
|
78
|
-
const clean = buildCleanBlockText(node, from, {
|
|
86
|
+
const atomBlockOf = ({ node, from }, fieldResults, noteReferences) => {
|
|
87
|
+
const clean = buildCleanBlockText(node, from, {
|
|
88
|
+
fieldResults,
|
|
89
|
+
noteReferences
|
|
90
|
+
});
|
|
91
|
+
const references = [];
|
|
92
|
+
for (const boundary of clean.structuralBoundaries) {
|
|
93
|
+
if (boundary.type !== "noteReference") continue;
|
|
94
|
+
references.push({
|
|
95
|
+
noteType: boundary.noteType,
|
|
96
|
+
noteId: boundary.noteId,
|
|
97
|
+
offset: boundary.offset,
|
|
98
|
+
length: boundary.length,
|
|
99
|
+
from: boundary.from
|
|
100
|
+
});
|
|
101
|
+
}
|
|
79
102
|
const supported = [];
|
|
80
103
|
const unsupportedTopology = [];
|
|
81
104
|
node.descendants((child, relativePosition) => {
|
|
@@ -116,7 +139,8 @@ const atomBlockOf = ({ node, from }, fieldResults) => {
|
|
|
116
139
|
cleanText: clean.text,
|
|
117
140
|
offsets: clean.offsets,
|
|
118
141
|
supported,
|
|
119
|
-
unsupportedTopology
|
|
142
|
+
unsupportedTopology,
|
|
143
|
+
noteReferences: references
|
|
120
144
|
};
|
|
121
145
|
};
|
|
122
146
|
const targetBlockIdLookup = (snapshot) => {
|
|
@@ -138,7 +162,44 @@ const targetBlockIdLookup = (snapshot) => {
|
|
|
138
162
|
return owner?.id;
|
|
139
163
|
};
|
|
140
164
|
};
|
|
141
|
-
const atomBlocksOf = (doc, fieldResults) => textBlocksOf(doc).map((block) => atomBlockOf(block, fieldResults));
|
|
165
|
+
const atomBlocksOf = (doc, fieldResults, noteReferences = "sourceText") => textBlocksOf(doc).map((block) => atomBlockOf(block, fieldResults, noteReferences));
|
|
166
|
+
const noteReferenceKey = ({ noteType, noteId }) => `${noteType}:${noteId}`;
|
|
167
|
+
/** Every note a story references, tracked-deleted references included. */
|
|
168
|
+
const referencedNotesOf = (doc) => {
|
|
169
|
+
const notes = /* @__PURE__ */ new Set();
|
|
170
|
+
doc.descendants((node) => {
|
|
171
|
+
const reference = noteReferenceOf(node);
|
|
172
|
+
if (reference) notes.add(noteReferenceKey(reference));
|
|
173
|
+
return !node.isText;
|
|
174
|
+
});
|
|
175
|
+
return notes;
|
|
176
|
+
};
|
|
177
|
+
const hasNoteReference = (doc) => {
|
|
178
|
+
let found = false;
|
|
179
|
+
doc.descendants((node) => {
|
|
180
|
+
if (found) return false;
|
|
181
|
+
if (noteReferenceOf(node)) found = true;
|
|
182
|
+
return !node.isText;
|
|
183
|
+
});
|
|
184
|
+
return found;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Whether this comparison inserted text that may stand for a note reference.
|
|
188
|
+
* Operations carry clean text, where a reference reads as its marker, so an
|
|
189
|
+
* operation that brings a reference writes the marker as text.
|
|
190
|
+
*/
|
|
191
|
+
const wroteNoteReferenceMarker = (doc, originalRevisionIdSeed) => {
|
|
192
|
+
let found = false;
|
|
193
|
+
doc.descendants((node) => {
|
|
194
|
+
if (found) return false;
|
|
195
|
+
if (!node.isText) return true;
|
|
196
|
+
if (!node.text?.includes("[^") || noteReferenceOf(node)) return false;
|
|
197
|
+
const revisionId = node.marks.find(({ type }) => type.name === "insertion")?.attrs["revisionId"];
|
|
198
|
+
if (typeof revisionId === "number" && revisionId >= originalRevisionIdSeed) found = true;
|
|
199
|
+
return false;
|
|
200
|
+
});
|
|
201
|
+
return found;
|
|
202
|
+
};
|
|
142
203
|
const sameBlockTopology = (left, right) => {
|
|
143
204
|
if (left.node.type !== right.node.type || left.cleanText !== right.cleanText) return false;
|
|
144
205
|
const supportedOffsets = /* @__PURE__ */ new Set([...left.supported.map(({ offset }) => offset), ...right.supported.map(({ offset }) => offset)]);
|
|
@@ -279,12 +340,15 @@ const sourceTextBlockIds = (source) => {
|
|
|
279
340
|
}
|
|
280
341
|
return blocks;
|
|
281
342
|
};
|
|
282
|
-
const sameParagraphSourcePosition = ({ sourceBlocks, reviewed, offset, fieldResults }) => {
|
|
343
|
+
const sameParagraphSourcePosition = ({ sourceBlocks, reviewed, offset, fieldResults, noteReferences }) => {
|
|
283
344
|
const paraId = reviewed.node.attrs["paraId"];
|
|
284
345
|
if (typeof paraId !== "string" || paraId.length === 0) return null;
|
|
285
346
|
const source = sourceBlocks.get(paraId);
|
|
286
347
|
if (!source || source.node.type !== reviewed.node.type) return null;
|
|
287
|
-
const clean = buildCleanBlockText(source.node, source.from, {
|
|
348
|
+
const clean = buildCleanBlockText(source.node, source.from, {
|
|
349
|
+
fieldResults,
|
|
350
|
+
noteReferences
|
|
351
|
+
});
|
|
288
352
|
return clean.text === reviewed.cleanText ? clean.offsets[offset] ?? null : null;
|
|
289
353
|
};
|
|
290
354
|
const textRangeDisposition = ({ doc, from, to, expectedText, originalRevisionIdSeed }) => {
|
|
@@ -332,14 +396,17 @@ const atomDisposition = ({ atom, originalRevisionIdSeed }) => {
|
|
|
332
396
|
return typeof revisionId === "number" && revisionId >= originalRevisionIdSeed ? "inserted" : null;
|
|
333
397
|
};
|
|
334
398
|
/**
|
|
335
|
-
* Restore field, image, and page-break atoms omitted by text-only comparison
|
|
399
|
+
* Restore field, image, and page-break atoms omitted by text-only comparison
|
|
400
|
+
* operations, and the note references whose markers they wrote as text.
|
|
336
401
|
* Positions come from the legacy review resolver's mapping, never a guessed
|
|
337
402
|
* paragraph correspondence: paragraph-mark deletions can merge paragraphs.
|
|
338
403
|
*/
|
|
339
|
-
const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisionIdSeed, author, maxRanges }) => {
|
|
404
|
+
const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisionIdSeed, author, maxRanges, noteReferenceLabels, baseNotes }) => {
|
|
340
405
|
if (!Number.isSafeInteger(maxRanges) || maxRanges < 0) return { status: "budget-exceeded" };
|
|
341
406
|
const targetDocument = sourceDocumentOf(targetSnapshot);
|
|
342
|
-
|
|
407
|
+
const restoresNoteReferences = wroteNoteReferenceMarker(state.doc, originalRevisionIdSeed) && hasNoteReference(targetDocument);
|
|
408
|
+
const noteReferences = restoresNoteReferences ? noteReferenceLabels ?? collectNoteReferenceLabels(targetDocument) : "sourceText";
|
|
409
|
+
if (!restoresNoteReferences && !hasSupportedAtom(state.doc) && !hasSupportedAtom(targetDocument)) return {
|
|
343
410
|
status: "matched",
|
|
344
411
|
transaction: state.tr,
|
|
345
412
|
nextRevisionId: revisionStamp.idSeed,
|
|
@@ -349,8 +416,8 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
|
|
|
349
416
|
const reviewed = resolveAllChangesInHeadlessStateWithMapping(state, "accept");
|
|
350
417
|
const targetBlockIdAt = targetBlockIdLookup(targetSnapshot);
|
|
351
418
|
let sourceBlocks;
|
|
352
|
-
const liveBlocks = atomBlocksOf(reviewed.state.doc, "text");
|
|
353
|
-
const targetBlocks = atomBlocksOf(targetDocument, "text");
|
|
419
|
+
const liveBlocks = atomBlocksOf(reviewed.state.doc, "text", noteReferences);
|
|
420
|
+
const targetBlocks = atomBlocksOf(targetDocument, "text", noteReferences);
|
|
354
421
|
if (liveBlocks.length !== targetBlocks.length) return { status: "unalignable" };
|
|
355
422
|
const actions = [];
|
|
356
423
|
let rangeCount = 0;
|
|
@@ -361,19 +428,63 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
|
|
|
361
428
|
actions.push(action);
|
|
362
429
|
return true;
|
|
363
430
|
};
|
|
431
|
+
let referencedNotes;
|
|
364
432
|
let omittedLiveBlocks;
|
|
365
433
|
let omittedTargetBlocks;
|
|
366
434
|
for (const [index, fullLive] of liveBlocks.entries()) {
|
|
367
435
|
const fullTarget = targetBlocks[index];
|
|
368
436
|
if (!fullTarget) return { status: "unalignable" };
|
|
437
|
+
if ((fullLive.noteReferences.length > 0 || fullTarget.noteReferences.length > 0) && fullLive.cleanText === fullTarget.cleanText) {
|
|
438
|
+
const kept = new Map(fullLive.noteReferences.map((reference) => [reference.offset, reference]));
|
|
439
|
+
for (const reference of fullTarget.noteReferences) {
|
|
440
|
+
const live = kept.get(reference.offset);
|
|
441
|
+
if (live) {
|
|
442
|
+
kept.delete(reference.offset);
|
|
443
|
+
if (noteReferenceKey(live) !== noteReferenceKey(reference) || live.length !== reference.length) return { status: "unalignable" };
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
referencedNotes ??= baseNotes ?? referencedNotesOf(state.doc);
|
|
447
|
+
if (!referencedNotes.has(noteReferenceKey(reference))) return {
|
|
448
|
+
status: "unalignable",
|
|
449
|
+
detail: MISSING_NOTE_DETAIL
|
|
450
|
+
};
|
|
451
|
+
const node = targetDocument.nodeAt(reference.from);
|
|
452
|
+
const reviewedFrom = fullLive.offsets[reference.offset];
|
|
453
|
+
const reviewedLast = fullLive.offsets[reference.offset + reference.length - 1];
|
|
454
|
+
if (!node || !noteReferenceOf(node) || reviewedFrom === void 0 || reviewedLast === void 0) return { status: "unalignable" };
|
|
455
|
+
const from = mappedSourcePosition({
|
|
456
|
+
mapping: reviewed.mapping,
|
|
457
|
+
position: reviewedFrom
|
|
458
|
+
});
|
|
459
|
+
const to = mappedSourcePosition({
|
|
460
|
+
mapping: reviewed.mapping,
|
|
461
|
+
position: reviewedLast + 1
|
|
462
|
+
});
|
|
463
|
+
if (from === null || to === null || textRangeDisposition({
|
|
464
|
+
doc: state.doc,
|
|
465
|
+
from,
|
|
466
|
+
to,
|
|
467
|
+
expectedText: fullTarget.cleanText.slice(reference.offset, reference.offset + reference.length),
|
|
468
|
+
originalRevisionIdSeed
|
|
469
|
+
}) !== "inserted") return { status: "unalignable" };
|
|
470
|
+
if (!planAction({
|
|
471
|
+
kind: "note-reference",
|
|
472
|
+
from,
|
|
473
|
+
to,
|
|
474
|
+
node,
|
|
475
|
+
targetBlockId: targetBlockIdAt(fullTarget.from)
|
|
476
|
+
})) return { status: "budget-exceeded" };
|
|
477
|
+
}
|
|
478
|
+
if (kept.size > 0) return { status: "unalignable" };
|
|
479
|
+
}
|
|
369
480
|
if (fullLive.supported.length === 0 && fullTarget.supported.length === 0) continue;
|
|
370
481
|
if (sameSupportedAtoms(fullLive, fullTarget)) continue;
|
|
371
482
|
let live = fullLive;
|
|
372
483
|
let target = fullTarget;
|
|
373
484
|
let fieldResults = "text";
|
|
374
485
|
if (!sameBlockTopology(live, target)) {
|
|
375
|
-
omittedLiveBlocks ??= atomBlocksOf(reviewed.state.doc, "omitted");
|
|
376
|
-
omittedTargetBlocks ??= atomBlocksOf(targetDocument, "omitted");
|
|
486
|
+
omittedLiveBlocks ??= atomBlocksOf(reviewed.state.doc, "omitted", noteReferences);
|
|
487
|
+
omittedTargetBlocks ??= atomBlocksOf(targetDocument, "omitted", noteReferences);
|
|
377
488
|
const omittedLive = omittedLiveBlocks[index];
|
|
378
489
|
const omittedTarget = omittedTargetBlocks[index];
|
|
379
490
|
if (!omittedLive || !omittedTarget || !sameBlockTopology(omittedLive, omittedTarget)) return { status: "unalignable" };
|
|
@@ -441,7 +552,8 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
|
|
|
441
552
|
sourceBlocks: sourceBlocks ??= sourceTextBlockIds(state.doc),
|
|
442
553
|
reviewed: live,
|
|
443
554
|
offset: atom.offset,
|
|
444
|
-
fieldResults
|
|
555
|
+
fieldResults,
|
|
556
|
+
noteReferences
|
|
445
557
|
});
|
|
446
558
|
if (from === null) return { status: "unalignable" };
|
|
447
559
|
const resultText = runFormattingInlineAtomResultText(atom.node);
|
|
@@ -520,6 +632,16 @@ const matchInlineAtoms = ({ state, targetSnapshot, revisionStamp, originalRevisi
|
|
|
520
632
|
if (action.targetBlockId) changedTargetBlockIds.add(action.targetBlockId);
|
|
521
633
|
continue;
|
|
522
634
|
}
|
|
635
|
+
if (action.kind === "note-reference") {
|
|
636
|
+
const from = transaction.mapping.map(action.from, 1);
|
|
637
|
+
const to = transaction.mapping.map(action.to, -1);
|
|
638
|
+
const written = transaction.doc.nodeAt(from);
|
|
639
|
+
const noteMark = action.node.marks.find(({ type }) => type.name === "footnoteRef");
|
|
640
|
+
if (from >= to || !written?.isText || !noteMark) return { status: "unalignable" };
|
|
641
|
+
transaction.replaceWith(from, to, state.schema.text(action.node.text ?? "", noteMark.addToSet(written.marks)));
|
|
642
|
+
if (action.targetBlockId) changedTargetBlockIds.add(action.targetBlockId);
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
523
645
|
if (action.kind === "replace-atom") {
|
|
524
646
|
const from = transaction.mapping.map(action.from, 1);
|
|
525
647
|
const to = transaction.mapping.map(action.to, -1);
|
|
@@ -581,4 +703,4 @@ const sameInlineAtoms = (leftDocument, rightDocument) => {
|
|
|
581
703
|
});
|
|
582
704
|
};
|
|
583
705
|
//#endregion
|
|
584
|
-
export { matchInlineAtoms, sameInlineAtoms };
|
|
706
|
+
export { MISSING_NOTE_DETAIL, matchInlineAtoms, sameInlineAtoms };
|