@stll/folio-core 0.37.4 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-edits/apply.js +275 -88
- package/dist/ai-edits/clean-text.js +12 -1
- package/dist/ai-edits/headless.d.ts +110 -0
- package/dist/ai-edits/headless.js +569 -40
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/snapshot.d.ts +24 -1
- package/dist/ai-edits/snapshot.js +206 -9
- package/dist/ai-edits/table-geometry.d.ts +7 -7
- package/dist/ai-edits/table-geometry.js +43 -27
- package/dist/ai-edits/table-template.d.ts +2 -1
- package/dist/ai-edits/table-template.js +9 -3
- package/dist/ai-edits/types.d.ts +38 -16
- package/dist/compare/__fixtures__/body-sequence.d.ts +4 -0
- package/dist/compare/__fixtures__/body-sequence.js +5 -3
- package/dist/compare/compare.d.ts +26 -2
- package/dist/compare/compare.js +444 -16
- package/dist/compare/content-alignment.js +216 -137
- package/dist/compare/content-types.d.ts +36 -2
- package/dist/compare/content.d.ts +13 -10
- package/dist/compare/content.js +69 -23
- package/dist/compare/formatting.d.ts +4 -22
- package/dist/compare/formatting.js +8 -112
- package/dist/compare/inline-atom-resources.d.ts +6 -0
- package/dist/compare/inline-atom-resources.js +26 -0
- package/dist/compare/inline-atoms.d.ts +35 -0
- package/dist/compare/inline-atoms.js +383 -0
- package/dist/compare/inline-provenance.d.ts +36 -0
- package/dist/compare/inline-provenance.js +334 -0
- package/dist/compare/plan.d.ts +4 -1
- package/dist/compare/plan.js +32 -4
- package/dist/compare/scenario.d.ts +4 -6
- package/dist/compare/section-boundary-properties.d.ts +63 -0
- package/dist/compare/section-boundary-properties.js +235 -0
- package/dist/compare/style-resources.d.ts +30 -0
- package/dist/compare/style-resources.js +404 -0
- package/dist/compare/types.d.ts +50 -11
- package/dist/compare/types.js +3 -1
- package/dist/compare/verification.d.ts +3 -5
- package/dist/compare/verification.js +8 -39
- package/dist/compat/eigenpal.d.ts +4 -4
- package/dist/compat/eigenpal.js +3 -3
- package/dist/document-operations.d.ts +5 -3
- package/dist/document-operations.js +112 -5
- package/dist/docx/blockContentParser.js +14 -2
- package/dist/docx/drawingIdNormalization.js +43 -2
- package/dist/docx/drawingRelationships.d.ts +10 -0
- package/dist/docx/drawingRelationships.js +28 -0
- package/dist/docx/fieldParser.js +1 -1
- package/dist/docx/hyperlinkParser.js +3 -5
- package/dist/docx/imageRawXml.d.ts +8 -0
- package/dist/docx/imageRawXml.js +13 -0
- package/dist/docx/newImage.d.ts +2 -2
- package/dist/docx/newImage.js +3 -3
- package/dist/docx/numberingParser.d.ts +1 -13
- package/dist/docx/numberingParser.js +31 -37
- package/dist/docx/paragraphParser.js +12 -9
- package/dist/docx/paragraphPropertySource.d.ts +8 -5
- package/dist/docx/paragraphPropertySource.js +16 -4
- package/dist/docx/removeHeaderFooterParts.d.ts +14 -0
- package/dist/docx/removeHeaderFooterParts.js +87 -0
- package/dist/docx/rezip.d.ts +3 -1
- package/dist/docx/rezip.js +143 -41
- package/dist/docx/runParser.js +16 -5
- package/dist/docx/sectionParser.js +3 -0
- package/dist/docx/sectionReferenceHistory.d.ts +17 -0
- package/dist/docx/sectionReferenceHistory.js +59 -0
- package/dist/docx/selectiveSave.js +11 -31
- package/dist/docx/selectiveXmlPatch.d.ts +8 -1
- package/dist/docx/selectiveXmlPatch.js +62 -2
- package/dist/docx/serializer/borderSerializer.d.ts +4 -1
- package/dist/docx/serializer/borderSerializer.js +20 -18
- package/dist/docx/serializer/commentSerializer.js +1 -1
- package/dist/docx/serializer/numberingSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +13 -177
- package/dist/docx/serializer/partNamespaces.d.ts +1 -1
- package/dist/docx/serializer/partNamespaces.js +4 -0
- package/dist/docx/serializer/runSerializer.d.ts +1 -5
- package/dist/docx/serializer/runSerializer.js +4 -130
- package/dist/docx/serializer/sectionPropertiesSerializer.js +5 -2
- package/dist/docx/serializer/stylesSerializer.js +1 -1
- package/dist/docx/serializer/textFormattingSerializer.d.ts +17 -0
- package/dist/docx/serializer/textFormattingSerializer.js +144 -0
- package/dist/docx/tableParser.js +21 -11
- package/dist/docx/xmlParser.d.ts +10 -1
- package/dist/docx/xmlParser.js +23 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/internal/compare/inline-presentation.d.ts +20 -0
- package/dist/internal/compare/inline-presentation.js +222 -0
- package/dist/internal/paragraphFormattingSerialization.d.ts +25 -0
- package/dist/internal/paragraphFormattingSerialization.js +164 -0
- package/dist/internal/sectionReferenceResolution.d.ts +21 -0
- package/dist/internal/sectionReferenceResolution.js +72 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
- package/dist/markdown/renderRuns.js +1 -0
- package/dist/markdown/renderTable.js +1 -0
- package/dist/prosemirror/attrs/index.js +20 -1
- package/dist/prosemirror/commands/comments.d.ts +11 -1
- package/dist/prosemirror/commands/comments.js +68 -6
- package/dist/prosemirror/commands/propertyChangeScope.d.ts +9 -3
- package/dist/prosemirror/commands/propertyChangeScope.js +4 -10
- package/dist/prosemirror/conversion/fromProseDoc.js +22 -22
- package/dist/prosemirror/conversion/toProseDoc.js +16 -57
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -0
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +3 -0
- package/dist/prosemirror/imageCommit.js +7 -9
- package/dist/prosemirror/paragraphIndentation.d.ts +21 -0
- package/dist/prosemirror/paragraphIndentation.js +72 -0
- package/dist/prosemirror/plugins/documentNumbering.d.ts +3 -1
- package/dist/prosemirror/plugins/documentNumbering.js +7 -1
- package/dist/prosemirror/plugins/documentStyles.d.ts +3 -1
- package/dist/prosemirror/plugins/documentStyles.js +7 -1
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +3 -1
- package/dist/prosemirror/rebaseParagraphRunFormatting.js +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +15 -2
- package/dist/prosemirror/runFormattingInlineCarriers.js +33 -3
- package/dist/prosemirror/runStyleFormatting.d.ts +15 -2
- package/dist/prosemirror/runStyleFormatting.js +76 -2
- package/dist/prosemirror/schema/marks.d.ts +3 -1
- package/dist/prosemirror/schema/marks.js +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +3 -0
- package/dist/prosemirror/tableCellRevisionVisibility.d.ts +6 -0
- package/dist/prosemirror/tableCellRevisionVisibility.js +14 -0
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
- package/dist/redline.js +15 -2
- package/dist/server.d.ts +3 -2
- package/dist/server.js +2 -2
- package/dist/version-comparison.d.ts +2 -0
- package/dist/version-comparison.js +30 -29
- package/package.json +2 -2
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 { paragraphIndentationEqual } from "../prosemirror/paragraphIndentation.js";
|
|
2
3
|
import { alignFolioContentStructure, contentBlocksShareContainer, createFolioContentAlignmentWorkSession } from "./content-alignment.js";
|
|
3
4
|
import { inlineFormattingSegments } from "./formatting.js";
|
|
4
5
|
import { Result, TaggedError, panic } from "better-result";
|
|
@@ -40,6 +41,7 @@ const MAX_MOVE_PROFILE_TOKENS = 16384;
|
|
|
40
41
|
const MAX_MOVE_PROFILE_CODE_UNITS = 1048576;
|
|
41
42
|
var InvalidFolioContentComparisonError = class extends TaggedError("InvalidFolioContentComparisonError") {};
|
|
42
43
|
var FolioContentComparisonLimitError = class extends TaggedError("FolioContentComparisonLimitError") {};
|
|
44
|
+
var FolioContentInlinePresentationProjectionError = class extends TaggedError("FolioContentInlinePresentationProjectionError") {};
|
|
43
45
|
const createContentComparisonWorkSession = (granularity) => ({
|
|
44
46
|
alignment: createFolioContentAlignmentWorkSession(),
|
|
45
47
|
remainingMoveComparisons: MAX_MOVE_SIMILARITY_COMPARISONS,
|
|
@@ -416,9 +418,13 @@ const paragraphSpacingEqual = (base, revised) => base?.spaceBefore === revised?.
|
|
|
416
418
|
const changedFolioContentParagraphFormatting = (base, revised) => {
|
|
417
419
|
const patch = {};
|
|
418
420
|
if ((base.styleId ?? null) !== (revised.styleId ?? null)) patch.styleId = revised.styleId ?? null;
|
|
419
|
-
|
|
421
|
+
const listReferenceChanged = base.listReference?.numId !== revised.listReference?.numId;
|
|
422
|
+
const targetOmitsLevelOnChangedReference = listReferenceChanged && revised.listReference !== void 0 && revised.listLevel === void 0;
|
|
423
|
+
if (base.listLevel !== revised.listLevel || targetOmitsLevelOnChangedReference) patch.listLevel = revised.listLevel ?? null;
|
|
424
|
+
if (listReferenceChanged || base.listLevel !== revised.listLevel && revised.listLevel === void 0 && revised.listReference !== void 0) patch.listReference = revised.listReference ?? null;
|
|
420
425
|
if (base.directAlignment !== revised.directAlignment) patch.alignment = revised.directAlignment ?? null;
|
|
421
426
|
if (!paragraphSpacingEqual(base.directSpacing, revised.directSpacing)) patch.spacing = revised.directSpacing ?? null;
|
|
427
|
+
if (!paragraphIndentationEqual(base.directIndentation, revised.directIndentation)) patch.indentation = revised.directIndentation ?? null;
|
|
422
428
|
return Object.keys(patch).length > 0 ? patch : null;
|
|
423
429
|
};
|
|
424
430
|
const changedBlockProperties = (base, revised) => {
|
|
@@ -640,16 +646,37 @@ const structuralChangeForStep = (step, id) => {
|
|
|
640
646
|
};
|
|
641
647
|
const formattingChange = (base, revised, maxRanges) => {
|
|
642
648
|
const paragraph = changedFolioContentParagraphFormatting(base, revised);
|
|
643
|
-
const
|
|
649
|
+
const inlineComparison = base.text === revised.text ? inlineFormattingSegments({
|
|
644
650
|
baseBlock: base,
|
|
645
651
|
targetBlock: revised,
|
|
646
652
|
maxSegments: maxRanges
|
|
647
|
-
}) :
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
+
}) : {
|
|
654
|
+
status: "compared",
|
|
655
|
+
segments: []
|
|
656
|
+
};
|
|
657
|
+
switch (inlineComparison.status) {
|
|
658
|
+
case "compared": {
|
|
659
|
+
const { segments: ranges } = inlineComparison;
|
|
660
|
+
return {
|
|
661
|
+
status: "compared",
|
|
662
|
+
formatting: paragraph || ranges.length > 0 ? {
|
|
663
|
+
...paragraph && { paragraph },
|
|
664
|
+
ranges
|
|
665
|
+
} : null
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
case "budget-exceeded": return { status: "budget-exceeded" };
|
|
669
|
+
case "unalignable": return {
|
|
670
|
+
status: "unalignable",
|
|
671
|
+
error: new FolioContentInlinePresentationProjectionError({
|
|
672
|
+
message: "Inline formatting runs cannot be aligned to their block text.",
|
|
673
|
+
side: inlineComparison.side,
|
|
674
|
+
baseBlockId: base.id,
|
|
675
|
+
revisedBlockId: revised.id
|
|
676
|
+
})
|
|
677
|
+
};
|
|
678
|
+
default: return panic("Unhandled inline formatting comparison result", { result: inlineComparison });
|
|
679
|
+
}
|
|
653
680
|
};
|
|
654
681
|
const compareAlignedFolioContent = ({ baseBlocks, revisedBlocks, steps, workSession, maxChanges, idStability }) => {
|
|
655
682
|
const paragraphPlans = detectFolioContentParagraphMarkPlans(steps);
|
|
@@ -733,13 +760,21 @@ const compareAlignedFolioContent = ({ baseBlocks, revisedBlocks, steps, workSess
|
|
|
733
760
|
const base = step.baseBlock;
|
|
734
761
|
const revised = step.revisedBlock;
|
|
735
762
|
const properties = changedBlockProperties(base, revised);
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
763
|
+
const formattingResult = formattingChange(base, revised, remainingFormattingRanges);
|
|
764
|
+
let formatting;
|
|
765
|
+
switch (formattingResult.status) {
|
|
766
|
+
case "compared":
|
|
767
|
+
formatting = formattingResult.formatting;
|
|
768
|
+
break;
|
|
769
|
+
case "budget-exceeded": return Result.err(limitExceeded({
|
|
770
|
+
input: "result",
|
|
771
|
+
limit: "changes",
|
|
772
|
+
maximum: maxChanges,
|
|
773
|
+
actual: maxChanges + 1
|
|
774
|
+
}));
|
|
775
|
+
case "unalignable": return Result.err(formattingResult.error);
|
|
776
|
+
default: return panic("Unhandled formatting comparison result", { result: formattingResult });
|
|
777
|
+
}
|
|
743
778
|
remainingFormattingRanges -= formatting?.ranges.length ?? 0;
|
|
744
779
|
let event;
|
|
745
780
|
if (base.text !== revised.text || properties.length > 0) event = {
|
|
@@ -780,13 +815,24 @@ const compareAlignedFolioContent = ({ baseBlocks, revisedBlocks, steps, workSess
|
|
|
780
815
|
}
|
|
781
816
|
if (step.type === "revisedOnly") {
|
|
782
817
|
const move = moveByRevisedId.get(step.block.id);
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
818
|
+
const moveFormattingResult = move ? formattingChange(move.baseBlock, step.block, remainingFormattingRanges) : {
|
|
819
|
+
status: "compared",
|
|
820
|
+
formatting: null
|
|
821
|
+
};
|
|
822
|
+
let moveFormatting;
|
|
823
|
+
switch (moveFormattingResult.status) {
|
|
824
|
+
case "compared":
|
|
825
|
+
moveFormatting = moveFormattingResult.formatting;
|
|
826
|
+
break;
|
|
827
|
+
case "budget-exceeded": return Result.err(limitExceeded({
|
|
828
|
+
input: "result",
|
|
829
|
+
limit: "changes",
|
|
830
|
+
maximum: maxChanges,
|
|
831
|
+
actual: maxChanges + 1
|
|
832
|
+
}));
|
|
833
|
+
case "unalignable": return Result.err(moveFormattingResult.error);
|
|
834
|
+
default: return panic("Unhandled formatting comparison result", { result: moveFormattingResult });
|
|
835
|
+
}
|
|
790
836
|
remainingFormattingRanges -= moveFormatting?.ranges.length ?? 0;
|
|
791
837
|
const moveProperties = move ? changedBlockProperties(move.baseBlock, step.block) : [];
|
|
792
838
|
if (!addRelation(move ? {
|
|
@@ -882,4 +928,4 @@ const compareContent = (options) => {
|
|
|
882
928
|
});
|
|
883
929
|
};
|
|
884
930
|
//#endregion
|
|
885
|
-
export { FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentComparisonLimitError, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans };
|
|
931
|
+
export { FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentComparisonLimitError, FolioContentInlinePresentationProjectionError, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans };
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
import { FolioContentBlock,
|
|
1
|
+
import { FolioContentBlock, FolioContentInlineComparisonResult } from "./content-types.js";
|
|
2
2
|
//#region src/compare/formatting.d.ts
|
|
3
|
-
/** One run of characters whose supported inline formatting differs. */
|
|
4
|
-
type InlineFormattingSegment = {
|
|
5
|
-
/** Zero-based UTF-16 offset into the block's visible text. */
|
|
6
|
-
startOffset: number;
|
|
7
|
-
endOffset: number;
|
|
8
|
-
/** Differing properties set to the target value; null removes a direct property. */
|
|
9
|
-
formatting: FolioContentInlineFormattingPatch;
|
|
10
|
-
};
|
|
11
3
|
type InlineFormattingSegmentsOptions = {
|
|
12
4
|
baseBlock: FolioContentBlock;
|
|
13
5
|
targetBlock: FolioContentBlock;
|
|
14
|
-
/** Refuse (return `null`) rather than build more segments than this. */
|
|
15
6
|
maxSegments: number;
|
|
16
7
|
};
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
* `baseBlock`'s, for two blocks that carry the same text. Returns `null` only
|
|
20
|
-
* when the diff would exceed `maxSegments`.
|
|
21
|
-
*
|
|
22
|
-
* A block whose runs cannot be aligned to its text reports no segments rather
|
|
23
|
-
* than guessing: attributing formatting to the wrong characters is worse than
|
|
24
|
-
* missing a formatting-only change, and the caller has no offset it could
|
|
25
|
-
* trust instead.
|
|
26
|
-
*/
|
|
27
|
-
declare const inlineFormattingSegments: ({ baseBlock, targetBlock, maxSegments }: InlineFormattingSegmentsOptions) => InlineFormattingSegment[] | null;
|
|
8
|
+
/** Malformed run streams take precedence over the output budget. */
|
|
9
|
+
declare const inlineFormattingSegments: ({ baseBlock, targetBlock, maxSegments }: InlineFormattingSegmentsOptions) => FolioContentInlineComparisonResult;
|
|
28
10
|
//#endregion
|
|
29
|
-
export {
|
|
11
|
+
export { inlineFormattingSegments };
|
|
@@ -1,115 +1,11 @@
|
|
|
1
|
+
import { canonicalInlinePresentationSegments } from "../internal/compare/inline-presentation.js";
|
|
1
2
|
//#region src/compare/formatting.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
const changedNumberValue = (baseEffective, targetEffective, baseAuthored, targetAuthored) => {
|
|
11
|
-
if (baseAuthored !== targetAuthored) return targetAuthored ?? null;
|
|
12
|
-
return baseEffective === targetEffective ? void 0 : targetEffective ?? null;
|
|
13
|
-
};
|
|
14
|
-
const changedSupportedFormatting = (base, target) => {
|
|
15
|
-
const baseDirect = base.directFormatting ?? {};
|
|
16
|
-
const targetDirect = target.directFormatting ?? {};
|
|
17
|
-
const formatting = {};
|
|
18
|
-
const changedBoolean = (property) => {
|
|
19
|
-
if (baseDirect[property] !== targetDirect[property]) return targetDirect[property] ?? null;
|
|
20
|
-
return Boolean(base[property]) === Boolean(target[property]) ? void 0 : Boolean(target[property]);
|
|
21
|
-
};
|
|
22
|
-
for (const property of [
|
|
23
|
-
"bold",
|
|
24
|
-
"italic",
|
|
25
|
-
"underline",
|
|
26
|
-
"strike"
|
|
27
|
-
]) {
|
|
28
|
-
const changed = changedBoolean(property);
|
|
29
|
-
if (changed !== void 0) formatting[property] = changed;
|
|
30
|
-
}
|
|
31
|
-
const fontFamily = changedStringValue(base.fontFamily, target.fontFamily, baseDirect.fontFamily, targetDirect.fontFamily);
|
|
32
|
-
if (fontFamily !== void 0) formatting.fontFamily = fontFamily;
|
|
33
|
-
const fontSizePt = changedNumberValue(base.fontSizePt, target.fontSizePt, baseDirect.fontSizePt, targetDirect.fontSizePt);
|
|
34
|
-
if (fontSizePt !== void 0) formatting.fontSizePt = fontSizePt;
|
|
35
|
-
const color = changedStringValue(normalizeEffectiveInlineFormattingColor(base.color), normalizeEffectiveInlineFormattingColor(target.color), normalizeDirectInlineFormattingColor(baseDirect.color), normalizeDirectInlineFormattingColor(targetDirect.color));
|
|
36
|
-
if (color !== void 0) formatting.color = color;
|
|
37
|
-
return formatting;
|
|
38
|
-
};
|
|
39
|
-
const sameInlineFormatting = (left, right) => left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strike === right.strike && left.fontFamily === right.fontFamily && left.fontSizePt === right.fontSizePt && left.color === right.color;
|
|
40
|
-
const hasInlineFormatting = (formatting) => formatting.bold !== void 0 || formatting.italic !== void 0 || formatting.underline !== void 0 || formatting.strike !== void 0 || formatting.fontFamily !== void 0 || formatting.fontSizePt !== void 0 || formatting.color !== void 0;
|
|
41
|
-
/**
|
|
42
|
-
* A block's runs, or `null` when they cannot describe the block's text.
|
|
43
|
-
* Non-text inline content (a field, an image) leaves the concatenated run text
|
|
44
|
-
* shorter than the block text; attributing formatting by offset would then
|
|
45
|
-
* point at the wrong characters, so the caller must back off instead.
|
|
46
|
-
*/
|
|
47
|
-
const previewRunsForBlock = (block) => {
|
|
48
|
-
const runs = block.previewRuns ?? [{ text: block.text }];
|
|
49
|
-
return runs.map(({ text }) => text).join("") === block.text ? runs : null;
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* Segments where `targetBlock`'s supported run formatting differs from
|
|
53
|
-
* `baseBlock`'s, for two blocks that carry the same text. Returns `null` only
|
|
54
|
-
* when the diff would exceed `maxSegments`.
|
|
55
|
-
*
|
|
56
|
-
* A block whose runs cannot be aligned to its text reports no segments rather
|
|
57
|
-
* than guessing: attributing formatting to the wrong characters is worse than
|
|
58
|
-
* missing a formatting-only change, and the caller has no offset it could
|
|
59
|
-
* trust instead.
|
|
60
|
-
*/
|
|
61
|
-
const inlineFormattingSegments = ({ baseBlock, targetBlock, maxSegments }) => {
|
|
62
|
-
const baseRuns = previewRunsForBlock(baseBlock);
|
|
63
|
-
const targetRuns = previewRunsForBlock(targetBlock);
|
|
64
|
-
if (!baseRuns || !targetRuns || baseBlock.text.length === 0) return [];
|
|
65
|
-
const segments = [];
|
|
66
|
-
let baseRunIndex = 0;
|
|
67
|
-
let targetRunIndex = 0;
|
|
68
|
-
let baseRunOffset = 0;
|
|
69
|
-
let targetRunOffset = 0;
|
|
70
|
-
let textOffset = 0;
|
|
71
|
-
while (baseRunIndex < baseRuns.length && targetRunIndex < targetRuns.length) {
|
|
72
|
-
const baseRun = baseRuns[baseRunIndex];
|
|
73
|
-
const targetRun = targetRuns[targetRunIndex];
|
|
74
|
-
if (!baseRun || !targetRun) break;
|
|
75
|
-
const length = Math.min(baseRun.text.length - baseRunOffset, targetRun.text.length - targetRunOffset);
|
|
76
|
-
if (length <= 0) {
|
|
77
|
-
if (baseRunOffset >= baseRun.text.length) {
|
|
78
|
-
baseRunIndex++;
|
|
79
|
-
baseRunOffset = 0;
|
|
80
|
-
}
|
|
81
|
-
if (targetRunOffset >= targetRun.text.length) {
|
|
82
|
-
targetRunIndex++;
|
|
83
|
-
targetRunOffset = 0;
|
|
84
|
-
}
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
const formatting = changedSupportedFormatting(baseRun, targetRun);
|
|
88
|
-
if (hasInlineFormatting(formatting)) {
|
|
89
|
-
const previous = segments.at(-1);
|
|
90
|
-
if (previous && previous.endOffset === textOffset && sameInlineFormatting(previous.formatting, formatting)) previous.endOffset += length;
|
|
91
|
-
else {
|
|
92
|
-
if (segments.length >= maxSegments) return null;
|
|
93
|
-
segments.push({
|
|
94
|
-
startOffset: textOffset,
|
|
95
|
-
endOffset: textOffset + length,
|
|
96
|
-
formatting
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
textOffset += length;
|
|
101
|
-
baseRunOffset += length;
|
|
102
|
-
targetRunOffset += length;
|
|
103
|
-
if (baseRunOffset >= baseRun.text.length) {
|
|
104
|
-
baseRunIndex++;
|
|
105
|
-
baseRunOffset = 0;
|
|
106
|
-
}
|
|
107
|
-
if (targetRunOffset >= targetRun.text.length) {
|
|
108
|
-
targetRunIndex++;
|
|
109
|
-
targetRunOffset = 0;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return segments;
|
|
113
|
-
};
|
|
3
|
+
/** Character-aligned inline-formatting comparison of two text-equal blocks. */
|
|
4
|
+
/** Malformed run streams take precedence over the output budget. */
|
|
5
|
+
const inlineFormattingSegments = ({ baseBlock, targetBlock, maxSegments }) => canonicalInlinePresentationSegments({
|
|
6
|
+
baseBlock,
|
|
7
|
+
revisedBlock: targetBlock,
|
|
8
|
+
maxSegments
|
|
9
|
+
});
|
|
114
10
|
//#endregion
|
|
115
11
|
export { inlineFormattingSegments };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
//#region src/compare/inline-atom-resources.d.ts
|
|
3
|
+
/** Detach supported atoms from the source package; save allocates the receiving media relationship. */
|
|
4
|
+
declare const prepareTargetInlineAtom: (node: Node) => Node | null;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { prepareTargetInlineAtom };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { rebindDrawingImageRelationship } from "../docx/drawingRelationships.js";
|
|
2
|
+
import { expectImageAttrs } from "../prosemirror/attrs/index.js";
|
|
3
|
+
//#region src/compare/inline-atom-resources.ts
|
|
4
|
+
/** Detach supported atoms from the source package; save allocates the receiving media relationship. */
|
|
5
|
+
const prepareTargetInlineAtom = (node) => {
|
|
6
|
+
if (node.type.name === "field") return node;
|
|
7
|
+
if (node.type.name === "pageBreakRun") return node;
|
|
8
|
+
if (node.type.name !== "image") return null;
|
|
9
|
+
const attrs = expectImageAttrs(node);
|
|
10
|
+
if (!attrs.src.startsWith("data:") || attrs.hlinkRId || attrs.hlinkHref) return null;
|
|
11
|
+
const rId = "rId_img_compare";
|
|
12
|
+
const rawXml = attrs._docxRawXml;
|
|
13
|
+
const reboundXml = rawXml ? rebindDrawingImageRelationship({
|
|
14
|
+
xml: rawXml,
|
|
15
|
+
previousId: attrs.rId ?? "",
|
|
16
|
+
nextId: rId
|
|
17
|
+
}) : void 0;
|
|
18
|
+
if (reboundXml === null) return null;
|
|
19
|
+
return node.type.create({
|
|
20
|
+
...node.attrs,
|
|
21
|
+
rId,
|
|
22
|
+
...reboundXml !== void 0 && { _docxRawXml: reboundXml }
|
|
23
|
+
}, node.content, node.marks);
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { prepareTargetInlineAtom };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FolioAIEditSnapshot } from "../ai-edits/types.js";
|
|
2
|
+
import { FolioRevisionStamp } from "../ai-edits/apply.js";
|
|
3
|
+
import { EditorState, Transaction } from "prosemirror-state";
|
|
4
|
+
import { Node } from "prosemirror-model";
|
|
5
|
+
//#region src/compare/inline-atoms.d.ts
|
|
6
|
+
type MatchInlineAtomsOptions = {
|
|
7
|
+
state: EditorState;
|
|
8
|
+
targetSnapshot: FolioAIEditSnapshot;
|
|
9
|
+
revisionStamp: FolioRevisionStamp;
|
|
10
|
+
/** First id reserved for this comparison, before planned operations allocated ids. */
|
|
11
|
+
originalRevisionIdSeed: number;
|
|
12
|
+
author: string;
|
|
13
|
+
maxRanges: number;
|
|
14
|
+
};
|
|
15
|
+
type MatchInlineAtomsResult = {
|
|
16
|
+
status: "matched";
|
|
17
|
+
transaction: Transaction;
|
|
18
|
+
nextRevisionId: number;
|
|
19
|
+
changedTargetBlockIds: readonly string[];
|
|
20
|
+
rangeCount: number;
|
|
21
|
+
} | {
|
|
22
|
+
status: "unalignable";
|
|
23
|
+
} | {
|
|
24
|
+
status: "budget-exceeded";
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Restore field, image, and page-break atoms omitted by text-only comparison operations.
|
|
28
|
+
* Positions come from the legacy review resolver's mapping, never a guessed
|
|
29
|
+
* paragraph correspondence: paragraph-mark deletions can merge paragraphs.
|
|
30
|
+
*/
|
|
31
|
+
declare const matchInlineAtoms: ({ state, targetSnapshot, revisionStamp, originalRevisionIdSeed, author, maxRanges }: MatchInlineAtomsOptions) => MatchInlineAtomsResult;
|
|
32
|
+
/** Compare supported inline atom identity after accept or reject projection. */
|
|
33
|
+
declare const sameInlineAtoms: (leftDocument: Node, rightDocument: Node) => boolean;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { MatchInlineAtomsOptions, MatchInlineAtomsResult, matchInlineAtoms, sameInlineAtoms };
|