@stll/folio-core 0.37.4 → 0.37.5

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.
@@ -1,3 +1,4 @@
1
+ import { FolioContentParagraphKind } from "../compare/content-types.js";
1
2
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockStructuralBoundary, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineBooleanProperty, FolioAIInlineFormatting, FolioAIInlineFormattingPatch, FolioAIParagraphSpacing, FolioAISignatureParty, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./types.js";
2
3
  import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "./word-diff.js";
3
4
  import { FolioAIEditApplyOutcome, FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "./apply.js";
@@ -9,4 +10,4 @@ import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlo
9
10
  import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsToStoryOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
10
11
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
11
12
  import { getFolioParaIdFromBlockId } from "../types/block-id.js";
12
- export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsToStoryOptions, type FolioCommentAnchor, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioEditableDocumentStoryHandle, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeKind, type FolioReviewedStory, type FolioReviewedView, type FolioRevisionStamp, type FolioWordDiffOptions, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
13
+ export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsToStoryOptions, type FolioCommentAnchor, type FolioContentParagraphKind, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioEditableDocumentStoryHandle, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeKind, type FolioReviewedStory, type FolioReviewedView, type FolioRevisionStamp, type FolioWordDiffOptions, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
- import { FolioContentBlock, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation } from "../compare/content-types.js";
2
+ import { FolioContentBlock, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentParagraphKind, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation } from "../compare/content-types.js";
3
3
  //#region src/ai-edits/types.d.ts
4
- type FolioAIBlockKind = "heading" | "listItem" | "paragraph";
4
+ type FolioAIBlockKind = FolioContentParagraphKind;
5
5
  /** Boolean run properties supported by snapshots and range-formatting mutations. */
6
6
  type FolioAIInlineBooleanProperty = FolioContentInlineBooleanProperty;
7
7
  type FolioAIInlineFormatting = FolioContentInlineFormatting;
@@ -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
- * text alignment in one caller-owned work session shares one hard budget.
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 pairByStableId = (base, revised, idStability) => {
95
- const revisedIndexById = /* @__PURE__ */ new Map();
96
- revised.forEach((block, revisedIndex) => {
97
- if (idStability(block) === "stable") revisedIndexById.set(block.id, revisedIndex);
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
- base.forEach((block, baseIndex) => {
101
- if (idStability(block) !== "stable") return;
102
- const revisedIndex = revisedIndexById.get(block.id);
103
- if (revisedIndex !== void 0) candidates.push({
104
- baseIndex,
105
- revisedIndex
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, idStability }) => {
120
- const uniqueIndexesById = ({ blocks, from, to }) => {
121
- const indexes = /* @__PURE__ */ new Map();
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 id = blocks[index]?.id;
124
- if (id === void 0) continue;
125
- indexes.set(id, indexes.has(id) ? null : index);
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 indexes;
190
+ return blocksById;
128
191
  };
129
- const baseIndexes = uniqueIndexesById({
192
+ const baseBlocksById = uniqueBlocksById({
130
193
  blocks: baseBlocks,
131
194
  from: baseFrom,
132
195
  to: baseTo
133
196
  });
134
- const revisedIndexes = uniqueIndexesById({
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, baseIndex] of baseIndexes) {
141
- const revisedIndex = revisedIndexes.get(id);
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(baseBlock) === idStability(revisedBlock)) continue;
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
- const pairByExactText = (base, revised, workSession, idStability, stableIdMismatch) => {
155
- const baseCount = base.length;
156
- const revisedCount = revised.length;
157
- if (baseCount === 0 || revisedCount === 0) return [];
158
- if (!claimFolioContentAlignmentCells(baseCount, revisedCount, workSession)) return [];
159
- const textKeys = /* @__PURE__ */ new Map();
160
- let nextTextKey = 0;
161
- const internText = (text) => {
162
- const existing = textKeys.get(text);
163
- if (existing !== void 0) return existing;
164
- const key = nextTextKey++;
165
- textKeys.set(text, key);
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 stride = revisedCount + 1;
177
- const lengths = new Int32Array((baseCount + 1) * stride);
178
- for (let baseIndex = baseCount - 1; baseIndex >= 0; baseIndex--) {
179
- const rowOffset = baseIndex * stride;
180
- const nextRowOffset = (baseIndex + 1) * stride;
181
- for (let revisedIndex = revisedCount - 1; revisedIndex >= 0; revisedIndex--) lengths[rowOffset + revisedIndex] = entriesCanPair(baseIndex, revisedIndex) ? (lengths[nextRowOffset + revisedIndex + 1] ?? 0) + 1 : Math.max(lengths[nextRowOffset + revisedIndex] ?? 0, lengths[rowOffset + revisedIndex + 1] ?? 0);
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 pairs = [];
184
- let baseIndex = 0;
185
- let revisedIndex = 0;
186
- while (baseIndex < baseCount && revisedIndex < revisedCount) {
187
- const baseEntry = base[baseIndex];
188
- const revisedEntry = revised[revisedIndex];
189
- if (!baseEntry || !revisedEntry) break;
190
- if (entriesCanPair(baseIndex, revisedIndex)) {
191
- pairs.push({
192
- baseIndex: baseEntry.index,
193
- revisedIndex: revisedEntry.index
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
- if ((lengths[(baseIndex + 1) * stride + revisedIndex] ?? 0) >= (lengths[baseIndex * stride + revisedIndex + 1] ?? 0)) baseIndex += 1;
200
- else revisedIndex += 1;
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 alignFolioContentBlocks = (baseBlocks, revisedBlocks, options = {}) => {
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 stableIdAnchors = pairByStableId(baseBlocks, revisedBlocks, idStability);
209
- const usedBaseIndexes = new Set(stableIdAnchors.map(({ baseIndex }) => baseIndex));
210
- const usedRevisedIndexes = new Set(stableIdAnchors.map(({ revisedIndex }) => revisedIndex));
211
- const baseRemaining = baseBlocks.flatMap((block, index) => usedBaseIndexes.has(index) ? [] : [{
212
- block,
213
- index
214
- }]);
215
- const revisedRemaining = revisedBlocks.flatMap((block, index) => usedRevisedIndexes.has(index) ? [] : [{
216
- block,
217
- index
218
- }]);
219
- const exactTextAnchors = pairByExactText(baseRemaining, revisedRemaining, workSession, idStability, stableIdMismatch);
220
- const anchors = longestIncreasingFolioContentPairs([...stableIdAnchors, ...exactTextAnchors].toSorted((left, right) => left.baseIndex - right.baseIndex));
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 = baseBlocks[baseFrom + offset];
226
- const revisedBlock = revisedBlocks[revisedFrom + offset];
227
- if (baseBlock && revisedBlock) if (idStability(baseBlock) === "stable" && idStability(revisedBlock) === "stable" && baseBlock.id !== revisedBlock.id && stableIdMismatch === "separate") {
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 = baseBlocks[index];
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 = revisedBlocks[index];
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
- emitGap(baseCursor, anchor.baseIndex, revisedCursor, anchor.revisedIndex);
291
- const baseBlock = baseBlocks[anchor.baseIndex];
292
- const revisedBlock = revisedBlocks[anchor.revisedIndex];
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
- emitGap(baseCursor, baseBlocks.length, revisedCursor, revisedBlocks.length);
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 = [];
@@ -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 = alignFolioContentBlocks(baseBlocks, revisedBlocks, {
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",
@@ -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;
@@ -97,4 +99,4 @@ type FolioContentSnapshot<Block extends FolioContentBlock = FolioContentBlock> =
97
99
  blocks: readonly Block[];
98
100
  };
99
101
  //#endregion
100
- export { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation };
102
+ export { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphKind, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation };
@@ -1,5 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
- import { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation } from "../compare/content-types.js";
2
+ import { FolioContentBlock, FolioContentContainerPathEntry, FolioContentIdStability, FolioContentInlineBooleanProperty, FolioContentInlineFormatting, FolioContentInlineFormattingPatch, FolioContentLineSpacingRule, FolioContentParagraphAlignment, FolioContentParagraphKind, FolioContentParagraphSpacing, FolioContentRun, FolioContentSnapshot, FolioContentTableLocation } from "../compare/content-types.js";
3
3
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockStructuralBoundary, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineBooleanProperty, FolioAIInlineFormatting, FolioAIInlineFormattingPatch, FolioAIParagraphSpacing, FolioAISignatureParty } from "../ai-edits/types.js";
4
4
  import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "../ai-edits/word-diff.js";
5
5
  import { FolioAIEditApplyOutcome, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "../ai-edits/apply.js";
@@ -39,4 +39,4 @@ import { getGoogleFontsEnabled, setEmbeddedFontFamilyMap, setGoogleFontsEnabled
39
39
  import { DOCX_CONFORMANCE_CLASSES } from "../index.js";
40
40
  type Document = document_d_exports.Document;
41
41
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
42
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
42
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFormatRange, type CompareResult, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioWordDiffOptions, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setEmbeddedFontFamilyMap, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -48,18 +48,6 @@ declare function computeListRendering(numPr: {
48
48
  }, numbering: NumberingMap): (document_d_exports.ListRendering & {
49
49
  levelStarts: number[];
50
50
  }) | null;
51
- /**
52
- * Compare two `numPr` references by value — `numId` plus `ilvl` (a missing
53
- * `ilvl` is level 0). Used for the style-sourced-numbering provenance check;
54
- * avoids JSON.stringify equality, which is sensitive to key order.
55
- */
56
- declare function numPrEqual(a: {
57
- numId?: number;
58
- ilvl?: number;
59
- } | null | undefined, b: {
60
- numId?: number;
61
- ilvl?: number;
62
- } | null | undefined): boolean;
63
51
  /**
64
52
  * Render list marker text by replacing placeholders with formatted numbers
65
53
  *
@@ -83,4 +71,4 @@ declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
83
71
  /** Whether a numbering level reserves horizontal space for a visible marker. */
84
72
  declare const numberingLevelHasMarkerSlot: (level: Pick<document_d_exports.ListLevel, "numFmt">) => boolean;
85
73
  //#endregion
86
- export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
74
+ export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
@@ -560,15 +560,6 @@ function computeListRendering(numPr, numbering) {
560
560
  return rendering;
561
561
  }
562
562
  /**
563
- * Compare two `numPr` references by value — `numId` plus `ilvl` (a missing
564
- * `ilvl` is level 0). Used for the style-sourced-numbering provenance check;
565
- * avoids JSON.stringify equality, which is sensitive to key order.
566
- */
567
- function numPrEqual(a, b) {
568
- if (a == null || b == null) return a == null && b == null;
569
- return a.numId === b.numId && (a.ilvl ?? 0) === (b.ilvl ?? 0);
570
- }
571
- /**
572
563
  * Render list marker text by replacing placeholders with formatted numbers
573
564
  *
574
565
  * @param lvlText - The level text pattern (e.g., "%1.", "%1.%2")
@@ -613,4 +604,4 @@ function isBulletLevel(level) {
613
604
  /** Whether a numbering level reserves horizontal space for a visible marker. */
614
605
  const numberingLevelHasMarkerSlot = (level) => level.numFmt !== "none";
615
606
  //#endregion
616
- export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
607
+ export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
@@ -1,4 +1,3 @@
1
- import { canonicalJson } from "../utils/canonicalJson.js";
2
1
  import { isValidHexColor } from "../utils/colorResolver.js";
3
2
  import { isValidHexId } from "../utils/hexId.js";
4
3
  import { parseBookmarkEnd as parseBookmarkEnd$1, parseBookmarkStart as parseBookmarkStart$1 } from "./bookmarkParser.js";
@@ -1195,10 +1194,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1195
1194
  }
1196
1195
  }
1197
1196
  }
1198
- if (pPr) assignParagraphPropertySource(paragraph, {
1199
- xml: captureParagraphPropertySource(pPr),
1200
- formattingJson: canonicalJson(paragraph.formatting ?? {})
1201
- });
1197
+ if (pPr) assignParagraphPropertySource(paragraph, captureParagraphPropertySource(pPr));
1202
1198
  return paragraph;
1203
1199
  }
1204
1200
  /**