@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
|
@@ -7,7 +7,8 @@ import { panic } from "better-result";
|
|
|
7
7
|
* The block walk is split by structural container before any textual
|
|
8
8
|
* alignment runs. Body blocks are compared with body blocks; tables are
|
|
9
9
|
* aligned table by table, then row by row and cell by cell. All quadratic
|
|
10
|
-
*
|
|
10
|
+
* structural candidate alignment in one caller-owned work session shares
|
|
11
|
+
* one hard budget; block exact-text anchors are gap-local and non-quadratic.
|
|
11
12
|
*/
|
|
12
13
|
const MAX_CONTENT_ALIGNMENT_LCS_CELLS = 4e6;
|
|
13
14
|
const MAX_CONTENT_STRUCTURE_TOKEN_LOOKUPS = 4e6;
|
|
@@ -28,6 +29,13 @@ const claimFolioContentAlignmentCells = (baseCount, revisedCount, workSession) =
|
|
|
28
29
|
workSession.remainingLcsCells -= baseCount * revisedCount;
|
|
29
30
|
return true;
|
|
30
31
|
};
|
|
32
|
+
const NON_WHITESPACE = /\S/u;
|
|
33
|
+
const PARAGRAPH_MARK_KIND_FAMILY = {
|
|
34
|
+
heading: "paragraphMark",
|
|
35
|
+
listItem: "paragraphMark",
|
|
36
|
+
paragraph: "paragraphMark"
|
|
37
|
+
};
|
|
38
|
+
const blockKindsCanPair = (baseKind, revisedKind) => baseKind === revisedKind || Object.hasOwn(PARAGRAPH_MARK_KIND_FAMILY, baseKind) && Object.hasOwn(PARAGRAPH_MARK_KIND_FAMILY, revisedKind);
|
|
31
39
|
const folioContentIdStability = (block) => block.idStability ?? "stable";
|
|
32
40
|
/**
|
|
33
41
|
* Longest strictly increasing subsequence by revised index.
|
|
@@ -91,20 +99,75 @@ const longestIncreasingFolioContentPairs = (pairs) => {
|
|
|
91
99
|
}
|
|
92
100
|
return ordered.toReversed();
|
|
93
101
|
};
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
const containerPathKeyOf = ({ containerPath }) => containerPath === void 0 || containerPath.length === 0 ? null : JSON.stringify(containerPath.map(({ kind, id }) => [kind, id]));
|
|
103
|
+
const blockPairingTableFactsOf = ({ table }) => {
|
|
104
|
+
if (!table) return { type: "body" };
|
|
105
|
+
return {
|
|
106
|
+
type: "table",
|
|
107
|
+
outerTableIndex: table.outerTableIndex,
|
|
108
|
+
tableIndex: table.tableIndex,
|
|
109
|
+
rowIndex: table.rowIndex,
|
|
110
|
+
cellIndex: table.cellIndex
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
const prepareAlignmentBlocks = ({ baseBlocks, revisedBlocks, idStability }) => {
|
|
114
|
+
const ROOT_CONTAINER_PATH_TOKEN = 0;
|
|
115
|
+
const containerPathTokens = /* @__PURE__ */ new Map();
|
|
116
|
+
let nextContainerPathToken = 1;
|
|
117
|
+
const containerPathTokenOf = (block) => {
|
|
118
|
+
const key = containerPathKeyOf(block);
|
|
119
|
+
if (key === null) return ROOT_CONTAINER_PATH_TOKEN;
|
|
120
|
+
const existing = containerPathTokens.get(key);
|
|
121
|
+
if (existing !== void 0) return existing;
|
|
122
|
+
const token = nextContainerPathToken++;
|
|
123
|
+
containerPathTokens.set(key, token);
|
|
124
|
+
return token;
|
|
125
|
+
};
|
|
126
|
+
const prepare = (blocks) => blocks.map((block, index) => ({
|
|
127
|
+
block,
|
|
128
|
+
index,
|
|
129
|
+
pairingFacts: {
|
|
130
|
+
containerPathToken: containerPathTokenOf(block),
|
|
131
|
+
id: block.id,
|
|
132
|
+
idStability: idStability(block),
|
|
133
|
+
kind: block.kind,
|
|
134
|
+
table: blockPairingTableFactsOf(block)
|
|
135
|
+
}
|
|
136
|
+
}));
|
|
137
|
+
return {
|
|
138
|
+
base: prepare(baseBlocks),
|
|
139
|
+
revised: prepare(revisedBlocks)
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
/** Every alignment pass shares this complete structural pairing policy. */
|
|
143
|
+
const blocksCanPair = ({ base, revised, stableIdMismatch, structuralScope }) => {
|
|
144
|
+
if (!blockKindsCanPair(base.kind, revised.kind) || base.containerPathToken !== revised.containerPathToken) return false;
|
|
145
|
+
if (base.table.type !== revised.table.type) return false;
|
|
146
|
+
if (structuralScope === "document" && base.table.type === "table" && revised.table.type === "table" && (base.table.outerTableIndex !== revised.table.outerTableIndex || base.table.tableIndex !== revised.table.tableIndex || base.table.rowIndex !== revised.table.rowIndex || base.table.cellIndex !== revised.table.cellIndex)) return false;
|
|
147
|
+
return !(stableIdMismatch === "separate" && base.idStability === "stable" && revised.idStability === "stable" && base.id !== revised.id);
|
|
148
|
+
};
|
|
149
|
+
const uniqueStableBlocks = (blocks) => {
|
|
150
|
+
const blocksById = /* @__PURE__ */ new Map();
|
|
151
|
+
for (const block of blocks) {
|
|
152
|
+
if (block.pairingFacts.idStability !== "stable") continue;
|
|
153
|
+
blocksById.set(block.pairingFacts.id, blocksById.has(block.pairingFacts.id) ? null : block);
|
|
154
|
+
}
|
|
155
|
+
return blocksById;
|
|
156
|
+
};
|
|
157
|
+
/** Stable identity is authoritative only when it is unique on both sides. */
|
|
158
|
+
const pairByStableId = ({ base, revised, canPair }) => {
|
|
159
|
+
const baseBlockById = uniqueStableBlocks(base);
|
|
160
|
+
const revisedBlockById = uniqueStableBlocks(revised);
|
|
99
161
|
const candidates = [];
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
162
|
+
for (const baseBlock of base) {
|
|
163
|
+
if (baseBlockById.get(baseBlock.pairingFacts.id) !== baseBlock) continue;
|
|
164
|
+
const revisedBlock = revisedBlockById.get(baseBlock.pairingFacts.id);
|
|
165
|
+
if (!revisedBlock) continue;
|
|
166
|
+
if (canPair(baseBlock, revisedBlock)) candidates.push({
|
|
167
|
+
baseIndex: baseBlock.index,
|
|
168
|
+
revisedIndex: revisedBlock.index
|
|
106
169
|
});
|
|
107
|
-
}
|
|
170
|
+
}
|
|
108
171
|
return longestIncreasingFolioContentPairs(candidates);
|
|
109
172
|
};
|
|
110
173
|
/**
|
|
@@ -116,180 +179,191 @@ const pairByStableId = (base, revised, idStability) => {
|
|
|
116
179
|
* already bounded by stable/exact anchors, so it cannot turn a positional id
|
|
117
180
|
* into a move or pull a block across an established correspondence.
|
|
118
181
|
*/
|
|
119
|
-
const pairByResidualIdContinuity = ({ baseBlocks, revisedBlocks, baseFrom, baseTo, revisedFrom, revisedTo,
|
|
120
|
-
const
|
|
121
|
-
const
|
|
182
|
+
const pairByResidualIdContinuity = ({ baseBlocks, revisedBlocks, baseFrom, baseTo, revisedFrom, revisedTo, canPair }) => {
|
|
183
|
+
const uniqueBlocksById = ({ blocks, from, to }) => {
|
|
184
|
+
const blocksById = /* @__PURE__ */ new Map();
|
|
122
185
|
for (let index = from; index < to; index++) {
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
186
|
+
const block = blocks[index];
|
|
187
|
+
if (!block) continue;
|
|
188
|
+
blocksById.set(block.pairingFacts.id, blocksById.has(block.pairingFacts.id) ? null : block);
|
|
126
189
|
}
|
|
127
|
-
return
|
|
190
|
+
return blocksById;
|
|
128
191
|
};
|
|
129
|
-
const
|
|
192
|
+
const baseBlocksById = uniqueBlocksById({
|
|
130
193
|
blocks: baseBlocks,
|
|
131
194
|
from: baseFrom,
|
|
132
195
|
to: baseTo
|
|
133
196
|
});
|
|
134
|
-
const
|
|
197
|
+
const revisedBlocksById = uniqueBlocksById({
|
|
135
198
|
blocks: revisedBlocks,
|
|
136
199
|
from: revisedFrom,
|
|
137
200
|
to: revisedTo
|
|
138
201
|
});
|
|
139
202
|
const candidates = [];
|
|
140
|
-
for (const [id,
|
|
141
|
-
const
|
|
142
|
-
if (baseIndex === null || revisedIndex === void 0 || revisedIndex === null) continue;
|
|
143
|
-
const baseBlock = baseBlocks[baseIndex];
|
|
144
|
-
const revisedBlock = revisedBlocks[revisedIndex];
|
|
203
|
+
for (const [id, baseBlock] of baseBlocksById) {
|
|
204
|
+
const revisedBlock = revisedBlocksById.get(id);
|
|
145
205
|
if (!baseBlock || !revisedBlock) continue;
|
|
146
|
-
if (idStability
|
|
206
|
+
if (baseBlock.pairingFacts.idStability === revisedBlock.pairingFacts.idStability || !canPair(baseBlock, revisedBlock)) continue;
|
|
147
207
|
candidates.push({
|
|
148
|
-
baseIndex,
|
|
149
|
-
revisedIndex
|
|
208
|
+
baseIndex: baseBlock.index,
|
|
209
|
+
revisedIndex: revisedBlock.index
|
|
150
210
|
});
|
|
151
211
|
}
|
|
152
212
|
return longestIncreasingFolioContentPairs(candidates);
|
|
153
213
|
};
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return key;
|
|
167
|
-
};
|
|
168
|
-
const baseTextKeys = base.map(({ block }) => internText(block.text));
|
|
169
|
-
const revisedTextKeys = revised.map(({ block }) => internText(block.text));
|
|
170
|
-
const entriesCanPair = (baseIndex, revisedIndex) => {
|
|
171
|
-
const baseBlock = base[baseIndex]?.block;
|
|
172
|
-
const revisedBlock = revised[revisedIndex]?.block;
|
|
173
|
-
if (!baseBlock || !revisedBlock || baseTextKeys[baseIndex] !== revisedTextKeys[revisedIndex]) return false;
|
|
174
|
-
return stableIdMismatch === "pair" || !(idStability(baseBlock) === "stable" && idStability(revisedBlock) === "stable" && baseBlock.id !== revisedBlock.id);
|
|
214
|
+
/** Exact anchors are gap-local; repeated or blank text is not identity evidence. */
|
|
215
|
+
const pairByUniqueExactText = ({ base, revised, baseFrom, baseTo, revisedFrom, revisedTo, canPair }) => {
|
|
216
|
+
const uniqueBlocks = (blocks, from, to) => {
|
|
217
|
+
const blocksByText = /* @__PURE__ */ new Map();
|
|
218
|
+
for (let index = from; index < to; index++) {
|
|
219
|
+
const block = blocks[index];
|
|
220
|
+
if (!block) continue;
|
|
221
|
+
const text = block.block.text;
|
|
222
|
+
if (!NON_WHITESPACE.test(text)) continue;
|
|
223
|
+
blocksByText.set(text, blocksByText.has(text) ? null : block);
|
|
224
|
+
}
|
|
225
|
+
return blocksByText;
|
|
175
226
|
};
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
227
|
+
const baseBlocksByText = uniqueBlocks(base, baseFrom, baseTo);
|
|
228
|
+
const revisedBlocksByText = uniqueBlocks(revised, revisedFrom, revisedTo);
|
|
229
|
+
const candidates = [];
|
|
230
|
+
for (const [text, baseBlock] of baseBlocksByText) {
|
|
231
|
+
const revisedBlock = revisedBlocksByText.get(text);
|
|
232
|
+
if (!baseBlock || !revisedBlock) continue;
|
|
233
|
+
if (canPair(baseBlock, revisedBlock)) candidates.push({
|
|
234
|
+
baseIndex: baseBlock.index,
|
|
235
|
+
revisedIndex: revisedBlock.index
|
|
236
|
+
});
|
|
182
237
|
}
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
baseIndex += 1;
|
|
196
|
-
revisedIndex += 1;
|
|
197
|
-
continue;
|
|
238
|
+
const orderedCandidates = candidates.toSorted((left, right) => left.baseIndex - right.baseIndex || left.revisedIndex - right.revisedIndex);
|
|
239
|
+
if (orderedCandidates.length === 0) return [];
|
|
240
|
+
const tailRevisedIndexes = [];
|
|
241
|
+
const tailCandidateIndexes = [];
|
|
242
|
+
const predecessors = new Int32Array(orderedCandidates.length).fill(-1);
|
|
243
|
+
orderedCandidates.forEach((candidate, candidateIndex) => {
|
|
244
|
+
let lower = 0;
|
|
245
|
+
let upper = tailRevisedIndexes.length;
|
|
246
|
+
while (lower < upper) {
|
|
247
|
+
const middle = lower + Math.floor((upper - lower) / 2);
|
|
248
|
+
if ((tailRevisedIndexes[middle] ?? Number.POSITIVE_INFINITY) < candidate.revisedIndex) lower = middle + 1;
|
|
249
|
+
else upper = middle;
|
|
198
250
|
}
|
|
199
|
-
|
|
200
|
-
|
|
251
|
+
predecessors[candidateIndex] = lower === 0 ? -1 : tailCandidateIndexes[lower - 1] ?? -1;
|
|
252
|
+
tailRevisedIndexes[lower] = candidate.revisedIndex;
|
|
253
|
+
tailCandidateIndexes[lower] = candidateIndex;
|
|
254
|
+
});
|
|
255
|
+
const pairs = [];
|
|
256
|
+
for (let candidateIndex = tailCandidateIndexes.at(-1) ?? -1; candidateIndex !== -1; candidateIndex = predecessors[candidateIndex] ?? -1) {
|
|
257
|
+
const candidate = orderedCandidates[candidateIndex];
|
|
258
|
+
if (candidate) pairs.push(candidate);
|
|
259
|
+
}
|
|
260
|
+
return pairs.toReversed();
|
|
261
|
+
};
|
|
262
|
+
const pairsInAnchorGaps = ({ baseLength, revisedLength, anchors, pairGap }) => {
|
|
263
|
+
const pairs = [];
|
|
264
|
+
let baseFrom = 0;
|
|
265
|
+
let revisedFrom = 0;
|
|
266
|
+
for (const anchor of [...anchors, {
|
|
267
|
+
baseIndex: baseLength,
|
|
268
|
+
revisedIndex: revisedLength
|
|
269
|
+
}]) {
|
|
270
|
+
pairs.push(...pairGap(baseFrom, anchor.baseIndex, revisedFrom, anchor.revisedIndex));
|
|
271
|
+
baseFrom = anchor.baseIndex + 1;
|
|
272
|
+
revisedFrom = anchor.revisedIndex + 1;
|
|
201
273
|
}
|
|
202
274
|
return pairs;
|
|
203
275
|
};
|
|
204
|
-
const
|
|
205
|
-
const workSession = options.workSession ?? createFolioContentAlignmentWorkSession();
|
|
276
|
+
const alignFolioContentBlocksInScope = (baseBlocks, revisedBlocks, options) => {
|
|
206
277
|
const stableIdMismatch = options.stableIdMismatch ?? "separate";
|
|
207
278
|
const idStability = options.idStability ?? folioContentIdStability;
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
const
|
|
220
|
-
|
|
279
|
+
const prepared = prepareAlignmentBlocks({
|
|
280
|
+
baseBlocks,
|
|
281
|
+
revisedBlocks,
|
|
282
|
+
idStability
|
|
283
|
+
});
|
|
284
|
+
const canPair = (baseBlock, revisedBlock) => blocksCanPair({
|
|
285
|
+
base: baseBlock.pairingFacts,
|
|
286
|
+
revised: revisedBlock.pairingFacts,
|
|
287
|
+
stableIdMismatch,
|
|
288
|
+
structuralScope: options.structuralScope
|
|
289
|
+
});
|
|
290
|
+
const stableIdAnchors = pairByStableId({
|
|
291
|
+
base: prepared.base,
|
|
292
|
+
revised: prepared.revised,
|
|
293
|
+
canPair
|
|
294
|
+
});
|
|
295
|
+
const exactTextAnchors = pairsInAnchorGaps({
|
|
296
|
+
baseLength: prepared.base.length,
|
|
297
|
+
revisedLength: prepared.revised.length,
|
|
298
|
+
anchors: stableIdAnchors,
|
|
299
|
+
pairGap: (baseFrom, baseTo, revisedFrom, revisedTo) => pairByUniqueExactText({
|
|
300
|
+
base: prepared.base,
|
|
301
|
+
revised: prepared.revised,
|
|
302
|
+
baseFrom,
|
|
303
|
+
baseTo,
|
|
304
|
+
revisedFrom,
|
|
305
|
+
revisedTo,
|
|
306
|
+
canPair
|
|
307
|
+
})
|
|
308
|
+
});
|
|
309
|
+
const exactAndStableAnchors = [...stableIdAnchors, ...exactTextAnchors].toSorted((left, right) => left.baseIndex - right.baseIndex || left.revisedIndex - right.revisedIndex);
|
|
310
|
+
const continuityAnchors = pairsInAnchorGaps({
|
|
311
|
+
baseLength: prepared.base.length,
|
|
312
|
+
revisedLength: prepared.revised.length,
|
|
313
|
+
anchors: exactAndStableAnchors,
|
|
314
|
+
pairGap: (baseFrom, baseTo, revisedFrom, revisedTo) => pairByResidualIdContinuity({
|
|
315
|
+
baseBlocks: prepared.base,
|
|
316
|
+
revisedBlocks: prepared.revised,
|
|
317
|
+
baseFrom,
|
|
318
|
+
baseTo,
|
|
319
|
+
revisedFrom,
|
|
320
|
+
revisedTo,
|
|
321
|
+
canPair
|
|
322
|
+
})
|
|
323
|
+
});
|
|
324
|
+
const anchors = [...exactAndStableAnchors, ...continuityAnchors].toSorted((left, right) => left.baseIndex - right.baseIndex || left.revisedIndex - right.revisedIndex);
|
|
221
325
|
const events = [];
|
|
222
326
|
const emitPositionalGap = (baseFrom, baseTo, revisedFrom, revisedTo) => {
|
|
223
327
|
const pairedCount = Math.min(baseTo - baseFrom, revisedTo - revisedFrom);
|
|
224
328
|
for (let offset = 0; offset < pairedCount; offset++) {
|
|
225
|
-
const baseBlock =
|
|
226
|
-
const revisedBlock =
|
|
227
|
-
if (baseBlock && revisedBlock) if (
|
|
329
|
+
const baseBlock = prepared.base[baseFrom + offset];
|
|
330
|
+
const revisedBlock = prepared.revised[revisedFrom + offset];
|
|
331
|
+
if (baseBlock && revisedBlock) if (!canPair(baseBlock, revisedBlock)) {
|
|
228
332
|
events.push({
|
|
229
333
|
type: "baseOnly",
|
|
230
|
-
block: baseBlock
|
|
334
|
+
block: baseBlock.block
|
|
231
335
|
});
|
|
232
336
|
events.push({
|
|
233
337
|
type: "revisedOnly",
|
|
234
|
-
block: revisedBlock
|
|
338
|
+
block: revisedBlock.block
|
|
235
339
|
});
|
|
236
340
|
} else events.push({
|
|
237
341
|
type: "pair",
|
|
238
|
-
baseBlock,
|
|
239
|
-
revisedBlock
|
|
342
|
+
baseBlock: baseBlock.block,
|
|
343
|
+
revisedBlock: revisedBlock.block
|
|
240
344
|
});
|
|
241
345
|
}
|
|
242
346
|
for (let index = baseFrom + pairedCount; index < baseTo; index++) {
|
|
243
|
-
const block =
|
|
347
|
+
const block = prepared.base[index]?.block;
|
|
244
348
|
if (block) events.push({
|
|
245
349
|
type: "baseOnly",
|
|
246
350
|
block
|
|
247
351
|
});
|
|
248
352
|
}
|
|
249
353
|
for (let index = revisedFrom + pairedCount; index < revisedTo; index++) {
|
|
250
|
-
const block =
|
|
354
|
+
const block = prepared.revised[index]?.block;
|
|
251
355
|
if (block) events.push({
|
|
252
356
|
type: "revisedOnly",
|
|
253
357
|
block
|
|
254
358
|
});
|
|
255
359
|
}
|
|
256
360
|
};
|
|
257
|
-
const emitGap = (baseFrom, baseTo, revisedFrom, revisedTo) => {
|
|
258
|
-
if (baseFrom === baseTo || revisedFrom === revisedTo) {
|
|
259
|
-
emitPositionalGap(baseFrom, baseTo, revisedFrom, revisedTo);
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const continuityPairs = pairByResidualIdContinuity({
|
|
263
|
-
baseBlocks,
|
|
264
|
-
revisedBlocks,
|
|
265
|
-
baseFrom,
|
|
266
|
-
baseTo,
|
|
267
|
-
revisedFrom,
|
|
268
|
-
revisedTo,
|
|
269
|
-
idStability
|
|
270
|
-
});
|
|
271
|
-
let baseCursor = baseFrom;
|
|
272
|
-
let revisedCursor = revisedFrom;
|
|
273
|
-
for (const pair of continuityPairs) {
|
|
274
|
-
emitPositionalGap(baseCursor, pair.baseIndex, revisedCursor, pair.revisedIndex);
|
|
275
|
-
const baseBlock = baseBlocks[pair.baseIndex];
|
|
276
|
-
const revisedBlock = revisedBlocks[pair.revisedIndex];
|
|
277
|
-
if (baseBlock && revisedBlock) events.push({
|
|
278
|
-
type: "pair",
|
|
279
|
-
baseBlock,
|
|
280
|
-
revisedBlock
|
|
281
|
-
});
|
|
282
|
-
baseCursor = pair.baseIndex + 1;
|
|
283
|
-
revisedCursor = pair.revisedIndex + 1;
|
|
284
|
-
}
|
|
285
|
-
emitPositionalGap(baseCursor, baseTo, revisedCursor, revisedTo);
|
|
286
|
-
};
|
|
287
361
|
let baseCursor = 0;
|
|
288
362
|
let revisedCursor = 0;
|
|
289
363
|
for (const anchor of anchors) {
|
|
290
|
-
|
|
291
|
-
const baseBlock =
|
|
292
|
-
const revisedBlock =
|
|
364
|
+
emitPositionalGap(baseCursor, anchor.baseIndex, revisedCursor, anchor.revisedIndex);
|
|
365
|
+
const baseBlock = prepared.base[anchor.baseIndex]?.block;
|
|
366
|
+
const revisedBlock = prepared.revised[anchor.revisedIndex]?.block;
|
|
293
367
|
if (baseBlock && revisedBlock) events.push({
|
|
294
368
|
type: "pair",
|
|
295
369
|
baseBlock,
|
|
@@ -298,9 +372,13 @@ const alignFolioContentBlocks = (baseBlocks, revisedBlocks, options = {}) => {
|
|
|
298
372
|
baseCursor = anchor.baseIndex + 1;
|
|
299
373
|
revisedCursor = anchor.revisedIndex + 1;
|
|
300
374
|
}
|
|
301
|
-
|
|
375
|
+
emitPositionalGap(baseCursor, prepared.base.length, revisedCursor, prepared.revised.length);
|
|
302
376
|
return events;
|
|
303
377
|
};
|
|
378
|
+
const alignFolioContentBlocks = (baseBlocks, revisedBlocks, options = {}) => alignFolioContentBlocksInScope(baseBlocks, revisedBlocks, {
|
|
379
|
+
...options,
|
|
380
|
+
structuralScope: "document"
|
|
381
|
+
});
|
|
304
382
|
const BODY_MOVE_BUCKET = 0;
|
|
305
383
|
const scopedAlignmentSteps = (events, context, bucketForBlock) => {
|
|
306
384
|
const steps = [];
|
|
@@ -321,7 +399,6 @@ const scopedAlignmentSteps = (events, context, bucketForBlock) => {
|
|
|
321
399
|
}
|
|
322
400
|
return steps;
|
|
323
401
|
};
|
|
324
|
-
const containerPathKeyOf = (block) => block.containerPath === void 0 || block.containerPath.length === 0 ? null : JSON.stringify(block.containerPath.map(({ kind, id }) => [kind, id]));
|
|
325
402
|
const contentBlocksShareContainerPath = (left, right) => containerPathKeyOf(left) === containerPathKeyOf(right);
|
|
326
403
|
const splitSegments = (blocks) => {
|
|
327
404
|
const segments = [];
|
|
@@ -633,7 +710,7 @@ const ALIGNMENT_DIRECTION = {
|
|
|
633
710
|
baseOnly: 2,
|
|
634
711
|
revisedOnly: 3
|
|
635
712
|
};
|
|
636
|
-
const alignProfiledContentSequence = ({ base, revised, workSession, canPair = () => true, pairSoleStructuralSlot = false, primaryEvidence = "stable", similarityFactor = () => 1 }) => {
|
|
713
|
+
const alignProfiledContentSequence = ({ base, revised, workSession, canPair = () => true, pairSoleStructuralSlot = false, soleShiftedPairResiduePolicy = "conservative", primaryEvidence = "stable", similarityFactor = () => 1 }) => {
|
|
637
714
|
if (base.length === 0 || revised.length === 0) return unpairedContentSequence(base.map(({ item }) => item), revised.map(({ item }) => item));
|
|
638
715
|
if ((base.length !== 1 || revised.length !== 1) && !claimFolioContentAlignmentCells(base.length, revised.length, workSession)) return unpairedContentSequence(base.map(({ item }) => item), revised.map(({ item }) => item));
|
|
639
716
|
const stablePairs = stableContentSequencePairs(base, revised);
|
|
@@ -805,7 +882,7 @@ const alignProfiledContentSequence = ({ base, revised, workSession, canPair = ()
|
|
|
805
882
|
});
|
|
806
883
|
hasRevisedOnly = true;
|
|
807
884
|
}
|
|
808
|
-
if (pairCount === 1 && solePairIsShifted && hasBaseOnly && hasRevisedOnly) return aligned.flatMap((entry) => entry.type === "pair" ? [{
|
|
885
|
+
if ((soleShiftedPairResiduePolicy === "conservative" || base.length === revised.length) && pairCount === 1 && solePairIsShifted && hasBaseOnly && hasRevisedOnly) return aligned.flatMap((entry) => entry.type === "pair" ? [{
|
|
809
886
|
type: "baseOnly",
|
|
810
887
|
item: entry.base
|
|
811
888
|
}, {
|
|
@@ -839,10 +916,11 @@ const alignRowCells = ({ baseRow, revisedRow, workSession, moveScopeContext, sta
|
|
|
839
916
|
for (const cellIndex of cellIndexes) {
|
|
840
917
|
const baseBlocks = baseCells.get(cellIndex) ?? [];
|
|
841
918
|
const revisedBlocks = revisedCells.get(cellIndex) ?? [];
|
|
842
|
-
const aligned =
|
|
919
|
+
const aligned = alignFolioContentBlocksInScope(baseBlocks, revisedBlocks, {
|
|
843
920
|
workSession,
|
|
844
921
|
idStability,
|
|
845
|
-
stableIdMismatch
|
|
922
|
+
stableIdMismatch,
|
|
923
|
+
structuralScope: "pairedTableCell"
|
|
846
924
|
}).flatMap((event) => {
|
|
847
925
|
if (event.type === "pair" && !contentBlocksShareContainerPath(event.baseBlock, event.revisedBlock)) return [{
|
|
848
926
|
type: "baseOnly",
|
|
@@ -876,6 +954,7 @@ const pairTableRows = ({ baseRows, revisedRows, workSession, idStability }) => {
|
|
|
876
954
|
revised: revisedRows.map(profile),
|
|
877
955
|
workSession,
|
|
878
956
|
pairSoleStructuralSlot: true,
|
|
957
|
+
soleShiftedPairResiduePolicy: "row-count-evidence",
|
|
879
958
|
primaryEvidence: "exact",
|
|
880
959
|
similarityFactor: (base, revised) => base.profile.physicalCellCount === revised.profile.physicalCellCount ? 1 : .5
|
|
881
960
|
}).map((alignment) => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
//#region src/compare/content-types.d.ts
|
|
2
2
|
/** How callers expect a block identifier to behave across document revisions. */
|
|
3
3
|
type FolioContentIdStability = "stable" | "positional";
|
|
4
|
+
/** Text-bearing block kinds that share one editable paragraph-mark structure. */
|
|
5
|
+
type FolioContentParagraphKind = "heading" | "listItem" | "paragraph";
|
|
4
6
|
/** One enclosing structural container, ordered outermost to innermost in a block path. */
|
|
5
7
|
type FolioContentContainerPathEntry = {
|
|
6
8
|
kind: string;
|
|
@@ -17,7 +19,24 @@ type FolioContentInlineFormatting = Partial<Record<FolioContentInlineBooleanProp
|
|
|
17
19
|
* An inline-formatting mutation: `false` is an explicit off value, while
|
|
18
20
|
* `null` removes the direct property so its inherited value becomes effective.
|
|
19
21
|
*/
|
|
20
|
-
type FolioContentInlineFormattingPatch =
|
|
22
|
+
type FolioContentInlineFormattingPatch = { [Property in keyof FolioContentInlineFormatting]?: FolioContentInlineFormatting[Property] | null; };
|
|
23
|
+
/** Presentation differences for one UTF-16 range in a text-aligned block pair. */
|
|
24
|
+
type FolioContentFormatRange = {
|
|
25
|
+
startOffset: number;
|
|
26
|
+
endOffset: number;
|
|
27
|
+
formatting: FolioContentInlineFormattingPatch;
|
|
28
|
+
};
|
|
29
|
+
/** A trusted inline projection or an explicit reason no ranges can be used. */
|
|
30
|
+
type FolioContentInlineComparisonResult = {
|
|
31
|
+
status: "compared";
|
|
32
|
+
segments: readonly FolioContentFormatRange[];
|
|
33
|
+
} | {
|
|
34
|
+
status: "unalignable";
|
|
35
|
+
side: "base" | "revised" | "both";
|
|
36
|
+
} | {
|
|
37
|
+
status: "budget-exceeded";
|
|
38
|
+
maximum: number;
|
|
39
|
+
};
|
|
21
40
|
type FolioContentRun = {
|
|
22
41
|
text: string;
|
|
23
42
|
bold?: boolean;
|
|
@@ -59,6 +78,18 @@ type FolioContentTableLocation = {
|
|
|
59
78
|
type FolioContentParagraphAlignment = "left" | "center" | "right" | "both" | "distribute" | "mediumKashida" | "highKashida" | "lowKashida" | "thaiDistribute";
|
|
60
79
|
/** Line-height interpretation understood by the neutral comparison model. */
|
|
61
80
|
type FolioContentLineSpacingRule = "auto" | "exact" | "atLeast";
|
|
81
|
+
/** The complete modeled attribute set of direct paragraph indentation. */
|
|
82
|
+
type FolioContentParagraphIndentation = {
|
|
83
|
+
indentLeft?: number;
|
|
84
|
+
indentRight?: number;
|
|
85
|
+
indentFirstLine?: number;
|
|
86
|
+
hangingIndent?: boolean;
|
|
87
|
+
};
|
|
88
|
+
/** A concrete numbering instance and its zero-based level. */
|
|
89
|
+
type FolioContentListReference = {
|
|
90
|
+
numId: number;
|
|
91
|
+
level: number;
|
|
92
|
+
};
|
|
62
93
|
/**
|
|
63
94
|
* The complete modeled attribute set of direct paragraph spacing. Optional
|
|
64
95
|
* fields distinguish an absent attribute from an explicit zero or false value.
|
|
@@ -85,8 +116,11 @@ type FolioContentBlock<Kind extends string = string> = {
|
|
|
85
116
|
directAlignment?: FolioContentParagraphAlignment;
|
|
86
117
|
/** Direct paragraph spacing; absent when every spacing value is inherited. */
|
|
87
118
|
directSpacing?: FolioContentParagraphSpacing;
|
|
119
|
+
/** Direct paragraph indentation; absent when every indentation value is inherited. */
|
|
120
|
+
directIndentation?: FolioContentParagraphIndentation;
|
|
88
121
|
/** Zero-based list indent level when the block carries numbering. */
|
|
89
122
|
listLevel?: number;
|
|
123
|
+
listReference?: FolioContentListReference;
|
|
90
124
|
previewRuns?: readonly FolioContentRun[];
|
|
91
125
|
table?: FolioContentTableLocation;
|
|
92
126
|
/** Structural ancestry, ordered from the outermost to the innermost container. */
|
|
@@ -97,4 +131,4 @@ type FolioContentSnapshot<Block extends FolioContentBlock = FolioContentBlock> =
|
|
|
97
131
|
blocks: readonly Block[];
|
|
98
132
|
};
|
|
99
133
|
//#endregion
|
|
100
|
-
export { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation };
|
|
134
|
+
export { FolioContentBlock, FolioContentContainerPathEntry, FolioContentFormatRange, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineComparisonResult, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentListReference, FolioContentParagraphAlignment, FolioContentParagraphIndentation, FolioContentParagraphKind, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FolioContentBlock, FolioContentIdStability,
|
|
1
|
+
import { FolioContentBlock, FolioContentFormatRange, FolioContentIdStability, FolioContentListReference, FolioContentParagraphIndentation, FolioContentParagraphSpacing, FolioContentSnapshot } from "./content-types.js";
|
|
2
2
|
import { WordDiffGranularity, createWordDiffSession } from "../ai-edits/word-diff.js";
|
|
3
3
|
import { FolioContentAlignmentStep, FolioContentAlignmentWorkSession } from "./content-alignment.js";
|
|
4
4
|
import { Result } from "better-result";
|
|
@@ -34,8 +34,15 @@ declare class FolioContentComparisonLimitError extends FolioContentComparisonLim
|
|
|
34
34
|
blockIndex?: number;
|
|
35
35
|
field?: string;
|
|
36
36
|
}> {}
|
|
37
|
+
declare const FolioContentInlinePresentationProjectionError_base: import("better-result").TaggedErrorClass<"FolioContentInlinePresentationProjectionError">;
|
|
38
|
+
declare class FolioContentInlinePresentationProjectionError extends FolioContentInlinePresentationProjectionError_base<{
|
|
39
|
+
message: string;
|
|
40
|
+
side: "base" | "revised" | "both";
|
|
41
|
+
baseBlockId: string;
|
|
42
|
+
revisedBlockId: string;
|
|
43
|
+
}> {}
|
|
37
44
|
/** Errors returned by {@link compareContent}. */
|
|
38
|
-
type FolioContentComparisonError = InvalidFolioContentComparisonError | FolioContentComparisonLimitError;
|
|
45
|
+
type FolioContentComparisonError = InvalidFolioContentComparisonError | FolioContentComparisonLimitError | FolioContentInlinePresentationProjectionError;
|
|
39
46
|
/** One Folio word-diff segment with JavaScript-slice-compatible offsets. */
|
|
40
47
|
type FolioContentTextSegment = {
|
|
41
48
|
type: "equal" | "del" | "ins";
|
|
@@ -49,14 +56,10 @@ type FolioContentTextSegment = {
|
|
|
49
56
|
type FolioContentParagraphFormattingPatch = {
|
|
50
57
|
styleId?: string | null;
|
|
51
58
|
listLevel?: number | null;
|
|
59
|
+
listReference?: FolioContentListReference | null;
|
|
52
60
|
alignment?: FolioContentBlock["directAlignment"] | null;
|
|
53
61
|
spacing?: FolioContentParagraphSpacing | null;
|
|
54
|
-
|
|
55
|
-
/** Presentation differences for one text-aligned block pair. */
|
|
56
|
-
type FolioContentFormatRange = {
|
|
57
|
-
startOffset: number;
|
|
58
|
-
endOffset: number;
|
|
59
|
-
formatting: FolioContentInlineFormattingPatch;
|
|
62
|
+
indentation?: FolioContentParagraphIndentation | null;
|
|
60
63
|
};
|
|
61
64
|
/** Presentation differences for one text-aligned block pair. */
|
|
62
65
|
type FolioContentFormattingChange = {
|
|
@@ -202,8 +205,8 @@ type CompareAlignedContentOptions<Block extends FolioContentBlock> = {
|
|
|
202
205
|
maxChanges: number;
|
|
203
206
|
idStability?: (block: Block) => FolioContentIdStability;
|
|
204
207
|
};
|
|
205
|
-
declare const compareAlignedFolioContent: <Block extends FolioContentBlock>({ baseBlocks, revisedBlocks, steps, workSession, maxChanges, idStability }: CompareAlignedContentOptions<Block>) => Result<FolioContentComparison<Block>, FolioContentComparisonLimitError>;
|
|
208
|
+
declare const compareAlignedFolioContent: <Block extends FolioContentBlock>({ baseBlocks, revisedBlocks, steps, workSession, maxChanges, idStability }: CompareAlignedContentOptions<Block>) => Result<FolioContentComparison<Block>, FolioContentComparisonLimitError | FolioContentInlinePresentationProjectionError>;
|
|
206
209
|
/** Compare two representation-neutral ordered content snapshots. */
|
|
207
210
|
declare const compareContent: <Block extends FolioContentBlock = FolioContentBlock>(options: CompareContentOptions<Block>) => Result<FolioContentComparison<Block>, FolioContentComparisonError>;
|
|
208
211
|
//#endregion
|
|
209
|
-
export { CompareContentOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FolioContentBlockProperty, FolioContentComparison, FolioContentComparisonError, FolioContentComparisonEvent, FolioContentComparisonLimit, FolioContentComparisonLimitError, FolioContentComparisonWorkSession, FolioContentFormatRange, FolioContentFormattingChange, FolioContentParagraphFormattingPatch, FolioContentStructuralChange, FolioContentTextSegment, InvalidFolioContentComparisonError, changedFolioContentParagraphFormatting, compareAlignedFolioContent, compareContent, createContentComparisonWorkSession, detectFolioContentMoves, detectFolioContentParagraphMarkPlans };
|
|
212
|
+
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 };
|