@stll/folio-core 0.36.0 → 0.37.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.
Files changed (200) hide show
  1. package/README.md +59 -1
  2. package/dist/ai-edits/apply.js +545 -296
  3. package/dist/ai-edits/block-identity.d.ts +4 -1
  4. package/dist/ai-edits/block-identity.js +4 -1
  5. package/dist/ai-edits/blockRange.js +14 -14
  6. package/dist/ai-edits/clean-text.d.ts +40 -1
  7. package/dist/ai-edits/clean-text.js +52 -2
  8. package/dist/ai-edits/headless.d.ts +3 -1
  9. package/dist/ai-edits/headless.js +133 -71
  10. package/dist/ai-edits/index.d.ts +3 -3
  11. package/dist/ai-edits/index.js +2 -2
  12. package/dist/ai-edits/read.js +38 -20
  13. package/dist/ai-edits/snapshot.d.ts +14 -6
  14. package/dist/ai-edits/snapshot.js +99 -19
  15. package/dist/ai-edits/table-cell-mutations.js +1 -1
  16. package/dist/ai-edits/table-row-column-mutations.js +7 -0
  17. package/dist/ai-edits/table-template.d.ts +11 -1
  18. package/dist/ai-edits/table-template.js +49 -17
  19. package/dist/ai-edits/types.d.ts +45 -57
  20. package/dist/ai-suggestions/conflict.js +1 -4
  21. package/dist/ai-suggestions/text-positions.d.ts +8 -0
  22. package/dist/ai-suggestions/text-positions.js +27 -5
  23. package/dist/compare/__fixtures__/body-sequence.js +3 -4
  24. package/dist/compare/column-alignment.d.ts +13 -13
  25. package/dist/compare/column-alignment.js +78 -39
  26. package/dist/compare/compare.d.ts +4 -3
  27. package/dist/compare/compare.js +41 -5
  28. package/dist/compare/content-alignment.d.ts +95 -0
  29. package/dist/compare/content-alignment.js +1450 -0
  30. package/dist/compare/content-types.d.ts +100 -0
  31. package/dist/compare/content-types.js +0 -0
  32. package/dist/compare/content.d.ts +209 -0
  33. package/dist/compare/content.js +885 -0
  34. package/dist/compare/formatting.d.ts +5 -5
  35. package/dist/compare/formatting.js +6 -4
  36. package/dist/compare/plan.d.ts +6 -1
  37. package/dist/compare/plan.js +169 -567
  38. package/dist/compare/scenario.d.ts +2 -2
  39. package/dist/compare/types.d.ts +3 -3
  40. package/dist/compare/verification.d.ts +2 -2
  41. package/dist/compare/verification.js +25 -8
  42. package/dist/compat/eigenpal.d.ts +5 -3
  43. package/dist/compat/eigenpal.js +3 -2
  44. package/dist/controller/headerFooterEditorManager.js +14 -6
  45. package/dist/controller/hiddenEditorManager.d.ts +7 -2
  46. package/dist/controller/hiddenEditorManager.js +15 -6
  47. package/dist/controller/layoutPipeline.js +5 -3
  48. package/dist/controller/noteEditorManager.js +12 -7
  49. package/dist/display-list/build/headerFooterPrimitives.js +1 -0
  50. package/dist/display-list/build/storyPrimitives.d.ts +5 -2
  51. package/dist/display-list/build/storyPrimitives.js +60 -3
  52. package/dist/display-list/build/tablePrimitives.js +30 -0
  53. package/dist/display-list/primitives.d.ts +1 -1
  54. package/dist/document-operations.d.ts +6 -4
  55. package/dist/document-operations.js +77 -12
  56. package/dist/docx/blockContentParser.js +6 -27
  57. package/dist/docx/ensureParaIds.js +9 -4
  58. package/dist/docx/numberingParser.d.ts +3 -1
  59. package/dist/docx/numberingParser.js +4 -1
  60. package/dist/docx/paragraphParser.js +7 -6
  61. package/dist/docx/paragraphTraversal.js +30 -34
  62. package/dist/docx/rezip.d.ts +0 -8
  63. package/dist/docx/rezip.js +41 -16
  64. package/dist/docx/runParser.js +4 -2
  65. package/dist/docx/serializer/paragraphSerializer.js +2 -0
  66. package/dist/docx/serializer/runSerializer.js +5 -3
  67. package/dist/docx/serializer/trackedChangeAttributes.d.ts +3 -1
  68. package/dist/docx/serializer/trackedChangeAttributes.js +11 -1
  69. package/dist/docx/server/build.d.ts +1 -1
  70. package/dist/headless-layout.js +5 -4
  71. package/dist/index.d.ts +5 -3
  72. package/dist/index.js +3 -2
  73. package/dist/internal/headlessRevisionResolution.d.ts +23 -0
  74. package/dist/internal/headlessRevisionResolution.js +155 -0
  75. package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
  76. package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
  77. package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
  78. package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
  79. package/dist/internal/pageBreakRunPartition.d.ts +26 -0
  80. package/dist/internal/pageBreakRunPartition.js +35 -0
  81. package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
  82. package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
  83. package/dist/internal/sectionEndpointResolution.d.ts +32 -0
  84. package/dist/internal/sectionEndpointResolution.js +38 -0
  85. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  86. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
  87. package/dist/layout-bridge/convert/headerFooterLayout.js +48 -5
  88. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
  89. package/dist/layout-bridge/convert/toFlowBlocks.js +379 -75
  90. package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
  91. package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
  92. package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
  93. package/dist/layout-bridge/dom/textStreamDom.js +75 -0
  94. package/dist/layout-bridge/headerFooterLayout.js +16 -30
  95. package/dist/layout-engine/index.js +12 -3
  96. package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
  97. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
  98. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  99. package/dist/layout-engine/types.d.ts +7 -0
  100. package/dist/layout-painter/imageLayout.d.ts +1 -1
  101. package/dist/layout-painter/renderTable.js +20 -0
  102. package/dist/model.d.ts +1 -1
  103. package/dist/paged-layout/rangeProjection.js +6 -9
  104. package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
  105. package/dist/paged-layout/transactionDirtyRange.js +23 -1
  106. package/dist/prosemirror/attrs/index.d.ts +7 -3
  107. package/dist/prosemirror/attrs/index.js +113 -7
  108. package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
  109. package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
  110. package/dist/prosemirror/commands/comments.d.ts +10 -1
  111. package/dist/prosemirror/commands/comments.js +190 -59
  112. package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
  113. package/dist/prosemirror/commands/formatPainter.js +319 -63
  114. package/dist/prosemirror/commands/index.d.ts +2 -2
  115. package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
  116. package/dist/prosemirror/commands/pageBreak.js +18 -36
  117. package/dist/prosemirror/commands/propertyChangeScope.d.ts +7 -5
  118. package/dist/prosemirror/commands/propertyChangeScope.js +27 -18
  119. package/dist/prosemirror/conversion/fromProseDoc.d.ts +7 -2
  120. package/dist/prosemirror/conversion/fromProseDoc.js +809 -209
  121. package/dist/prosemirror/conversion/runShadingMark.js +2 -2
  122. package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
  123. package/dist/prosemirror/conversion/toProseDoc.js +416 -283
  124. package/dist/prosemirror/extensions/StarterKit.js +4 -0
  125. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -8
  126. package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
  127. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -1
  128. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
  129. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
  130. package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
  131. package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
  132. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
  133. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
  134. package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
  135. package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
  136. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
  137. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
  138. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
  139. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
  140. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
  141. package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
  142. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
  143. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  144. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
  145. package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
  146. package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
  147. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
  148. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
  149. package/dist/prosemirror/findReplaceSelection.js +8 -3
  150. package/dist/prosemirror/index.d.ts +3 -3
  151. package/dist/prosemirror/listMarker.d.ts +26 -1
  152. package/dist/prosemirror/listMarker.js +108 -14
  153. package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
  154. package/dist/prosemirror/pageBreakRunProjection.js +47 -0
  155. package/dist/prosemirror/paraText.js +6 -0
  156. package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
  157. package/dist/prosemirror/paragraphSpacing.js +125 -0
  158. package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
  159. package/dist/prosemirror/plugins/documentNumbering.js +18 -0
  160. package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
  161. package/dist/prosemirror/plugins/pmTextScan.js +14 -4
  162. package/dist/prosemirror/plugins/suggestionMode.js +8 -10
  163. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
  164. package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
  165. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
  166. package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
  167. package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
  168. package/dist/prosemirror/runFormattingProvenance.js +48 -0
  169. package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
  170. package/dist/prosemirror/runFormattingReconciliation.js +54 -0
  171. package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
  172. package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
  173. package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
  174. package/dist/prosemirror/runStyleFormatting.js +105 -0
  175. package/dist/prosemirror/schema/index.d.ts +3 -3
  176. package/dist/prosemirror/schema/marks.d.ts +82 -9
  177. package/dist/prosemirror/schema/marks.js +73 -1
  178. package/dist/prosemirror/schema/nodes.d.ts +18 -5
  179. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
  180. package/dist/prosemirror/styles/resolvedStyleAttrs.js +44 -23
  181. package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
  182. package/dist/prosemirror/styles/styleResolver.js +3 -3
  183. package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
  184. package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
  185. package/dist/prosemirror/tableGridMutation.d.ts +20 -0
  186. package/dist/prosemirror/tableGridMutation.js +30 -0
  187. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
  188. package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
  189. package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
  190. package/dist/prosemirror/validation.js +23 -4
  191. package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
  192. package/dist/render-dom/RenderedDomContext.js +17 -34
  193. package/dist/server.d.ts +4 -4
  194. package/dist/server.js +2 -2
  195. package/dist/style-engine/styleEngine.d.ts +1 -1
  196. package/dist/utils/clipboard.js +1 -1
  197. package/dist/utils/findReplace.js +38 -21
  198. package/dist/version-comparison.d.ts +38 -44
  199. package/dist/version-comparison.js +270 -401
  200. package/package.json +3 -2
@@ -1,4 +1,7 @@
1
+ import { FolioAIBlock } from "./types.js";
1
2
  //#region src/ai-edits/block-identity.d.ts
3
+ /** Resolve explicit snapshot provenance, falling back to the legacy id-shape convention. */
4
+ declare const folioAIBlockIdStability: ({ id, idStability }: FolioAIBlock) => "stable" | "positional";
2
5
  declare const stripBlockIdentityAttrs: (attrs: Record<string, unknown>) => Record<string, unknown>;
3
6
  //#endregion
4
- export { stripBlockIdentityAttrs };
7
+ export { folioAIBlockIdStability, stripBlockIdentityAttrs };
@@ -1,9 +1,12 @@
1
+ import { getFolioParaIdFromBlockId } from "../types/block-id.js";
1
2
  //#region src/ai-edits/block-identity.ts
2
3
  const IDENTITY_BLOCK_ATTRS = /* @__PURE__ */ new Set(["paraId", "textId"]);
4
+ /** Resolve explicit snapshot provenance, falling back to the legacy id-shape convention. */
5
+ const folioAIBlockIdStability = ({ id, idStability }) => idStability ?? (getFolioParaIdFromBlockId(id) === null ? "positional" : "stable");
3
6
  const stripBlockIdentityAttrs = (attrs) => {
4
7
  const next = {};
5
8
  for (const [key, value] of Object.entries(attrs)) next[key] = IDENTITY_BLOCK_ATTRS.has(key) ? null : value;
6
9
  return next;
7
10
  };
8
11
  //#endregion
9
- export { stripBlockIdentityAttrs };
12
+ export { folioAIBlockIdStability, stripBlockIdentityAttrs };
@@ -1,6 +1,6 @@
1
1
  import { findParagraphByParaId } from "../prosemirror/utils/findParagraphByParaId.js";
2
2
  import { getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex } from "../types/block-id.js";
3
- import { buildCleanBlockText } from "./clean-text.js";
3
+ import { buildCleanBlockText, resolveCleanTextRange } from "./clean-text.js";
4
4
  import { createFolioAIEditSnapshot, hashFolioAIBlockText, isFolioAIContentBlock } from "./snapshot.js";
5
5
  //#region src/ai-edits/blockRange.ts
6
6
  const resolveFolioAIBlockRange = ({ blockId, doc, snapshot }) => {
@@ -28,14 +28,14 @@ const resolveFolioAITextRange = ({ range, doc, snapshot }) => {
28
28
  const blockNode = doc.nodeAt(blockRange.from);
29
29
  if (!blockNode?.isTextblock) return null;
30
30
  const cleanBlock = buildCleanBlockText(blockNode, blockRange.from);
31
- const from = cleanBlock.offsets[range.startOffset];
32
- const to = cleanBlock.offsets[range.endOffset];
33
- if (from === void 0 || to === void 0) return null;
34
- if (hashFolioAIBlockText(cleanBlock.text.slice(range.startOffset, range.endOffset)) !== range.selectedTextHash) return null;
35
- return clampRangeToDocSize(doc.content.size, {
36
- from,
37
- to
31
+ const resolved = resolveCleanTextRange({
32
+ cleanBlock,
33
+ startOffset: range.startOffset,
34
+ endOffset: range.endOffset
38
35
  });
36
+ if (resolved === null) return null;
37
+ if (hashFolioAIBlockText(cleanBlock.text.slice(range.startOffset, range.endOffset)) !== range.selectedTextHash) return null;
38
+ return clampRangeToDocSize(doc.content.size, resolved);
39
39
  };
40
40
  /**
41
41
  * Resolve a free-text passage inside a block to a `{ from, to }` PM range.
@@ -66,13 +66,13 @@ const resolvePassageRange = ({ blockId, text, doc, snapshot }) => {
66
66
  const cleanBlock = buildCleanBlockText(blockNode, blockRange.from);
67
67
  const match = matchPassage(cleanBlock.text, needleSource);
68
68
  if (match === null) return null;
69
- const from = cleanBlock.offsets[match.start];
70
- const to = cleanBlock.offsets[match.end];
71
- if (from === void 0 || to === void 0) return null;
72
- return clampRangeToDocSize(doc.content.size, {
73
- from,
74
- to
69
+ const resolved = resolveCleanTextRange({
70
+ cleanBlock,
71
+ startOffset: match.start,
72
+ endOffset: match.end
75
73
  });
74
+ if (resolved === null) return null;
75
+ return clampRangeToDocSize(doc.content.size, resolved);
76
76
  };
77
77
  /**
78
78
  * Turn a caller-supplied passage into a regex source that tolerates whitespace
@@ -1,3 +1,4 @@
1
+ import { PageBreakRunAttrs } from "../prosemirror/schema/nodes.js";
1
2
  import { Node } from "prosemirror-model";
2
3
  //#region src/ai-edits/clean-text.d.ts
3
4
  /**
@@ -21,7 +22,45 @@ import { Node } from "prosemirror-model";
21
22
  type CleanBlockText = {
22
23
  text: string;
23
24
  offsets: number[];
25
+ /**
26
+ * Zero-width structural carriers projected at their clean-text boundary.
27
+ *
28
+ * A single clean offset cannot identify both sides of an atom: the PM
29
+ * position immediately after the preceding character is before the atom,
30
+ * while the next character starts after it. Callers that select text must
31
+ * resolve through {@link resolveCleanTextRange}; indexing `offsets`
32
+ * directly can accidentally absorb the carrier into an adjacent range.
33
+ */
34
+ structuralBoundaries: readonly CleanTextStructuralBoundary[];
24
35
  };
36
+ type CleanTextStructuralBoundary = {
37
+ type: "pageBreakRun";
38
+ /** Clean-text offset at which the zero-width carrier occurs. */
39
+ offset: number;
40
+ /** PM range owned by the carrier. */
41
+ from: number;
42
+ to: number;
43
+ /** Preserved even though `clear` does not alter page-break layout. */
44
+ clear?: PageBreakRunAttrs["clear"];
45
+ /** Whether the post-tracked-changes projection retains this carrier. */
46
+ presentInCleanView: boolean;
47
+ };
48
+ type ResolveCleanTextRangeOptions = {
49
+ cleanBlock: CleanBlockText;
50
+ startOffset: number;
51
+ endOffset: number;
52
+ };
53
+ /**
54
+ * Resolve clean-text offsets without selecting a zero-width structural atom.
55
+ *
56
+ * A range wholly on one side of a boundary is biased away from the atom. A
57
+ * range spanning both sides is unrepresentable as a generic text selection
58
+ * and returns `null`; a structural operation must own that mutation instead.
59
+ */
60
+ declare const resolveCleanTextRange: ({ cleanBlock, startOffset, endOffset }: ResolveCleanTextRangeOptions) => {
61
+ from: number;
62
+ to: number;
63
+ } | null;
25
64
  declare const buildCleanBlockText: (blockNode: Node, blockFrom: number) => CleanBlockText;
26
65
  /**
27
66
  * A "redline-aware" view of a textblock: the same left-to-right traversal as
@@ -44,4 +83,4 @@ declare const buildCleanBlockText: (blockNode: Node, blockFrom: number) => Clean
44
83
  */
45
84
  declare const buildAnnotatedBlockText: (blockNode: Node) => string;
46
85
  //#endregion
47
- export { CleanBlockText, buildAnnotatedBlockText, buildCleanBlockText };
86
+ export { CleanBlockText, CleanTextStructuralBoundary, buildAnnotatedBlockText, buildCleanBlockText, resolveCleanTextRange };
@@ -1,4 +1,39 @@
1
+ import { expectPageBreakRunAttrs } from "../prosemirror/attrs/index.js";
1
2
  //#region src/ai-edits/clean-text.ts
3
+ const EMPTY_CLEAN_TEXT_STRUCTURAL_BOUNDARIES = Object.freeze([]);
4
+ /**
5
+ * Resolve clean-text offsets without selecting a zero-width structural atom.
6
+ *
7
+ * A range wholly on one side of a boundary is biased away from the atom. A
8
+ * range spanning both sides is unrepresentable as a generic text selection
9
+ * and returns `null`; a structural operation must own that mutation instead.
10
+ */
11
+ const resolveCleanTextRange = ({ cleanBlock, startOffset, endOffset }) => {
12
+ if (!Number.isInteger(startOffset) || !Number.isInteger(endOffset) || startOffset < 0 || endOffset < startOffset || endOffset > cleanBlock.text.length) return null;
13
+ const baseFrom = cleanBlock.offsets[startOffset];
14
+ const baseTo = cleanBlock.offsets[endOffset];
15
+ if (baseFrom === void 0 || baseTo === void 0) return null;
16
+ const { structuralBoundaries } = cleanBlock;
17
+ if (structuralBoundaries.length === 0) return baseFrom <= baseTo ? {
18
+ from: baseFrom,
19
+ to: baseTo
20
+ } : null;
21
+ let from = baseFrom;
22
+ let to = baseTo;
23
+ for (const boundary of structuralBoundaries) {
24
+ if (boundary.offset > startOffset && boundary.offset < endOffset) return null;
25
+ if (boundary.offset === startOffset) from = Math.max(from, boundary.to);
26
+ if (startOffset !== endOffset && boundary.offset === endOffset) to = Math.min(to, boundary.from);
27
+ }
28
+ if (startOffset === endOffset) return {
29
+ from,
30
+ to: from
31
+ };
32
+ return from <= to ? {
33
+ from,
34
+ to
35
+ } : null;
36
+ };
2
37
  const DELETION_MARK = "deletion";
3
38
  const INSERTION_MARK = "insertion";
4
39
  const COMMENT_MARK = "comment";
@@ -6,8 +41,22 @@ const HIDDEN_MARK = "hidden";
6
41
  const buildCleanBlockText = (blockNode, blockFrom) => {
7
42
  let text = "";
8
43
  const offsets = [];
44
+ let structuralBoundaries;
9
45
  let lastEnd = blockFrom + 1;
10
46
  blockNode.descendants((node, pos) => {
47
+ if (node.type.name === "pageBreakRun") {
48
+ const from = blockFrom + 1 + pos;
49
+ const { clear } = expectPageBreakRunAttrs(node);
50
+ (structuralBoundaries ??= []).push({
51
+ type: "pageBreakRun",
52
+ offset: text.length,
53
+ from,
54
+ to: from + node.nodeSize,
55
+ ...clear !== void 0 ? { clear } : {},
56
+ presentInCleanView: !node.marks.some(({ type }) => type.name === DELETION_MARK)
57
+ });
58
+ return false;
59
+ }
11
60
  if (!node.isText || node.text === void 0) return true;
12
61
  if (node.marks.some((mark) => mark.type.name === DELETION_MARK || mark.type.name === HIDDEN_MARK)) return false;
13
62
  const startPos = blockFrom + 1 + pos;
@@ -19,7 +68,8 @@ const buildCleanBlockText = (blockNode, blockFrom) => {
19
68
  offsets.push(lastEnd);
20
69
  return {
21
70
  text,
22
- offsets
71
+ offsets,
72
+ structuralBoundaries: structuralBoundaries ?? EMPTY_CLEAN_TEXT_STRUCTURAL_BOUNDARIES
23
73
  };
24
74
  };
25
75
  /**
@@ -89,4 +139,4 @@ const renderAnnotatedSegment = ({ annotation, text }) => {
89
139
  return inner;
90
140
  };
91
141
  //#endregion
92
- export { buildAnnotatedBlockText, buildCleanBlockText };
142
+ export { buildAnnotatedBlockText, buildCleanBlockText, resolveCleanTextRange };
@@ -361,6 +361,9 @@ declare class FolioDocxReviewer {
361
361
  private resolveEveryStory;
362
362
  /** The current document model with edits merged back in. */
363
363
  toDocument(): document_d_exports.Document;
364
+ private captureReviewerState;
365
+ private documentFromStateSnapshot;
366
+ private captureSaveSnapshot;
364
367
  /**
365
368
  * Serialise the reviewed document to a new `.docx` buffer. Tries a selective
366
369
  * patch first (only changed paragraphs are rewritten; every other byte of the
@@ -369,7 +372,6 @@ declare class FolioDocxReviewer {
369
372
  */
370
373
  toBuffer(): Promise<ArrayBuffer>;
371
374
  private assertResolvedStoriesSerialized;
372
- private getChangedNoteParaIds;
373
375
  /**
374
376
  * Attempt the selective patch, treating a throw the same as a decline. Odd
375
377
  * source XML can make the paragraph diff throw rather than return `null`; a
@@ -6,21 +6,24 @@ import { parseDocx } from "../docx/parser.js";
6
6
  import { createReply } from "../docx/replyToComment.js";
7
7
  import { repackDocx } from "../docx/rezip.js";
8
8
  import { attemptSelectiveSave } from "../docx/selectiveSave.js";
9
- import { acceptAIEditRevision, acceptAllChanges, rejectAIEditRevision, rejectAllChanges } from "../prosemirror/commands/comments.js";
9
+ import { pluginsForHeadlessRevisionResolution } from "../internal/headlessRevisionResolutionGuard.js";
10
+ import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
11
+ import { acceptAIEditRevision, rejectAIEditRevision, resolveAllChangesInHeadlessState } from "../prosemirror/commands/comments.js";
10
12
  import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
11
13
  import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
12
14
  import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
13
- import { getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
14
- import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
15
+ import { getChangedParagraphIds, getTrackedSectionEndpointRemoval, hasStructuralChanges, hasUntrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
16
+ import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
17
+ import { createDocumentStylesPlugin, getDocumentStyleResolver } from "../prosemirror/plugins/documentStyles.js";
15
18
  import { schema, singletonManager } from "../prosemirror/schema/index.js";
16
19
  import { deterministicHexId } from "../utils/hexId.js";
17
20
  import { buildAnnotatedBlockText } from "./clean-text.js";
18
21
  import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
19
- import { createFolioAIEditSnapshot, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
22
+ import { createFolioAIEditSnapshotWithStyleResolver, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
20
23
  import { matchTableGeometry } from "./table-geometry.js";
21
24
  import { TaggedError } from "better-result";
22
- import { EditorState } from "prosemirror-state";
23
25
  import { Fragment } from "prosemirror-model";
26
+ import { EditorState } from "prosemirror-state";
24
27
  //#region src/ai-edits/headless.ts
25
28
  /**
26
29
  * Headless `.docx` review path: buffer -> apply AI edits -> buffer, with
@@ -113,7 +116,8 @@ const ensureDeterministicParaIdsInDoc = (doc) => {
113
116
  seen.add(paraId);
114
117
  next = recreateProseNodeWithParagraphPropertySource(child, { attrs: {
115
118
  ...child.attrs,
116
- paraId
119
+ paraId,
120
+ idStability: "positional"
117
121
  } });
118
122
  }
119
123
  const paraId = next.attrs["paraId"];
@@ -138,7 +142,24 @@ const FOLIO_REVIEWED_VIEWS = Object.freeze([
138
142
  const FOLIO_RESOLVED_REVIEWED_VIEWS = Object.freeze(["original", "final"]);
139
143
  var UnsupportedFolioReviewedViewError = class extends TaggedError("UnsupportedFolioReviewedViewError") {};
140
144
  var FolioDocumentStoryNotFoundError = class extends TaggedError("FolioDocumentStoryNotFoundError") {};
145
+ const FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES = Object.freeze({
146
+ storyMissing: "story-missing",
147
+ revisionMarkupRemains: "revision-markup-remains",
148
+ textProjection: "text-projection",
149
+ blockProjection: "block-projection"
150
+ });
141
151
  var FolioResolvedStorySerializationError = class extends TaggedError("FolioResolvedStorySerializationError") {};
152
+ /**
153
+ * `idStability` records how an id entered the current snapshot. A synthesized
154
+ * paraId is positional until save writes it into the package, then becomes an
155
+ * authored stable id when reopened. Compare the persisted block projection
156
+ * without changing that live snapshot identity contract.
157
+ */
158
+ const resolvedStoryBlockProjection = (block) => {
159
+ const persisted = { ...block };
160
+ delete persisted.idStability;
161
+ return persisted;
162
+ };
142
163
  const MAIN_STORY = Object.freeze({ type: "main" });
143
164
  const headerFooterStoryKey = ({ type, relationshipId }) => `${type}:${relationshipId}`;
144
165
  const noteStoryKey = ({ type, noteId }) => `${type}:${noteId}`;
@@ -146,19 +167,18 @@ const secondaryStoryKey = (story) => story.type === "header" || story.type === "
146
167
  const editableStoryKey = (story) => story.type === "main" ? "main" : secondaryStoryKey(story);
147
168
  const isFolioReviewedView = (value) => FOLIO_REVIEWED_VIEWS.some((view) => view === value);
148
169
  const isFolioResolvedReviewedView = (value) => FOLIO_RESOLVED_REVIEWED_VIEWS.some((view) => view === value);
149
- const applyCommandToState = (state, command) => {
150
- let nextState = state;
151
- command(state, (transaction) => {
152
- nextState = nextState.apply(transaction);
153
- });
154
- return nextState;
155
- };
156
170
  const resolveReviewedState = (state, view) => {
157
171
  if (view === "current-markup") return state;
158
- return applyCommandToState(state, view === "original" ? rejectAllChanges() : acceptAllChanges());
172
+ return resolveAllChangesInHeadlessState(state, view === "original" ? "reject" : "accept");
159
173
  };
174
+ const createHeadlessPlugins = (styles, numbering) => [
175
+ ...pluginsForHeadlessRevisionResolution(singletonManager.getPlugins()),
176
+ createDocumentStylesPlugin(styles),
177
+ createDocumentNumberingPlugin(numbering)
178
+ ];
179
+ const createStateSnapshot = (state) => createFolioAIEditSnapshotWithStyleResolver(state.doc, getDocumentStyleResolver(state));
160
180
  const formatStoryStateForLLM = (state, annotated) => {
161
- const snapshot = createFolioAIEditSnapshot(state.doc);
181
+ const snapshot = createStateSnapshot(state);
162
182
  const blocks = snapshot.blocks.filter(isFolioAIContentBlock);
163
183
  if (!annotated) return blocks.map(formatBlockForLLM).join("\n");
164
184
  const nodeByStart = /* @__PURE__ */ new Map();
@@ -252,7 +272,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
252
272
  preloadFonts: false,
253
273
  password: options.password
254
274
  });
255
- const plugins = [...singletonManager.getPlugins(), createDocumentStylesPlugin(baseDocument.package.styles)];
275
+ const plugins = createHeadlessPlugins(baseDocument.package.styles, baseDocument.package.numbering);
256
276
  const state = ensureBaseDirectionInState(EditorState.create({
257
277
  schema,
258
278
  doc: ensureDeterministicParaIdsInDoc(toProseDoc(baseDocument)),
@@ -270,7 +290,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
270
290
  * the anchor map the apply layer resolves operations against.
271
291
  */
272
292
  snapshot() {
273
- return createFolioAIEditSnapshot(this.state.doc);
293
+ return createStateSnapshot(this.state);
274
294
  }
275
295
  /**
276
296
  * The package's numbering, flattened to what a reader sees: one entry per
@@ -317,7 +337,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
317
337
  /** Snapshot one editable story into stable, operation-ready blocks. */
318
338
  snapshotStory(story) {
319
339
  const state = this.getEditableStoryState(story);
320
- return state ? createFolioAIEditSnapshot(state.doc) : null;
340
+ return state ? createStateSnapshot(state) : null;
321
341
  }
322
342
  /**
323
343
  * One story's tables, in the document order {@link snapshotStory} numbers
@@ -380,7 +400,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
380
400
  return {
381
401
  story,
382
402
  view,
383
- snapshot: createFolioAIEditSnapshot(state.doc),
403
+ snapshot: createStateSnapshot(state),
384
404
  text: formatStoryStateForLLM(state, view === "current-markup"),
385
405
  changes: getTrackedChangesFromDoc(state.doc)
386
406
  };
@@ -398,7 +418,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
398
418
  this.resolvedStoryExpectations.set(editableStoryKey(story), {
399
419
  story,
400
420
  text: formatStoryStateForLLM(resolvedState, false),
401
- blocks: createFolioAIEditSnapshot(resolvedState.doc).blocks
421
+ blocks: createStateSnapshot(resolvedState).blocks.map(resolvedStoryBlockProjection)
402
422
  });
403
423
  return true;
404
424
  }
@@ -464,7 +484,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
464
484
  };
465
485
  const result = applyFolioDocumentOperations({
466
486
  view,
467
- snapshot: snapshot ?? createFolioAIEditSnapshot(beforeState.doc),
487
+ snapshot: snapshot ?? createStateSnapshot(beforeState),
468
488
  batch,
469
489
  story: story.type === "main" ? "main" : story,
470
490
  author: this.author,
@@ -530,7 +550,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
530
550
  * feed straight back into {@link applyOperations} and accept / reject.
531
551
  */
532
552
  getContent() {
533
- return this.snapshot().blocks;
553
+ return [...this.snapshot().blocks];
534
554
  }
535
555
  /**
536
556
  * The body as LLM-ready plain text: one line per block, each prefixed with
@@ -767,29 +787,73 @@ var FolioDocxReviewer = class FolioDocxReviewer {
767
787
  * document still carries a redline nobody can see from the body.
768
788
  */
769
789
  acceptAll() {
770
- return this.resolveEveryStory(acceptAllChanges());
790
+ return this.resolveEveryStory("accept");
771
791
  }
772
792
  /** Reject every tracked change in the package. See {@link acceptAll}. */
773
793
  rejectAll() {
774
- return this.resolveEveryStory(rejectAllChanges());
794
+ return this.resolveEveryStory("reject");
775
795
  }
776
- resolveEveryStory(command) {
796
+ resolveEveryStory(mode) {
777
797
  let count = 0;
778
798
  for (const { handle } of this.listStories()) {
779
799
  const state = this.getEditableStoryState(handle);
780
800
  if (!state) continue;
781
801
  count += getTrackedChangesFromDoc(state.doc).length;
782
- this.runStoryCommand(command, handle);
802
+ this.setEditableStoryState(handle, resolveAllChangesInHeadlessState(state, mode));
783
803
  }
784
804
  return count;
785
805
  }
786
806
  /** The current document model with edits merged back in. */
787
807
  toDocument() {
788
- const document = updateDocumentContent(this.baseDocument, this.state.doc);
789
- this.mergeEditedSecondaryStories(document);
790
- if (this.createdComments.length > 0 || this.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides([...document.package.document.comments ?? [], ...this.createdComments]);
808
+ return this.documentFromStateSnapshot(this.captureReviewerState());
809
+ }
810
+ captureReviewerState() {
811
+ const secondaryStoryStates = [];
812
+ for (const { handle, initialState, state } of this.secondaryStoryStates.values()) secondaryStoryStates.push({
813
+ handle,
814
+ initialState,
815
+ state
816
+ });
817
+ return {
818
+ mainState: this.state,
819
+ secondaryStoryStates,
820
+ createdComments: [...this.createdComments],
821
+ resolvedOverrides: new Map(this.resolvedOverrides),
822
+ resolvedStoryExpectations: [...this.resolvedStoryExpectations.values()]
823
+ };
824
+ }
825
+ documentFromStateSnapshot(snapshot) {
826
+ const document = updateDocumentContent(this.baseDocument, snapshot.mainState.doc);
827
+ this.mergeEditedSecondaryStories(document, snapshot.secondaryStoryStates);
828
+ if (snapshot.createdComments.length > 0 || snapshot.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides([...document.package.document.comments ?? [], ...snapshot.createdComments], snapshot.resolvedOverrides);
791
829
  return document;
792
830
  }
831
+ captureSaveSnapshot() {
832
+ const snapshot = this.captureReviewerState();
833
+ const changedParaIds = new Set(getChangedParagraphIds(snapshot.mainState));
834
+ let structuralChange = hasStructuralChanges(snapshot.mainState);
835
+ let untrackedChanges = hasUntrackedChanges(snapshot.mainState);
836
+ const changedNoteParaIds = /* @__PURE__ */ new Set();
837
+ for (const entry of snapshot.secondaryStoryStates) {
838
+ if (entry.handle.type !== "footnote" && entry.handle.type !== "endnote") continue;
839
+ for (const paraId of getChangedParagraphIds(entry.state)) {
840
+ changedParaIds.add(paraId);
841
+ changedNoteParaIds.add(paraId);
842
+ }
843
+ structuralChange ||= hasStructuralChanges(entry.state);
844
+ untrackedChanges ||= hasUntrackedChanges(entry.state);
845
+ }
846
+ return {
847
+ document: this.documentFromStateSnapshot(snapshot),
848
+ path: structuralChange || untrackedChanges ? { type: "full-repack" } : {
849
+ type: "selective-first",
850
+ changedParaIds
851
+ },
852
+ changedNoteParaIds,
853
+ sectionEndpointRemoval: getTrackedSectionEndpointRemoval(snapshot.mainState),
854
+ resolvedStoryExpectations: snapshot.resolvedStoryExpectations
855
+ };
856
+ }
793
857
  /**
794
858
  * Serialise the reviewed document to a new `.docx` buffer. Tries a selective
795
859
  * patch first (only changed paragraphs are rewritten; every other byte of the
@@ -797,67 +861,65 @@ var FolioDocxReviewer = class FolioDocxReviewer {
797
861
  * structural edits — the same two-tier path the editor's save uses.
798
862
  */
799
863
  async toBuffer() {
800
- const document = this.toDocument();
801
- const selective = await this.trySelectiveSave(document);
864
+ const save = this.captureSaveSnapshot();
865
+ const selective = save.path.type === "selective-first" ? await this.trySelectiveSave(save.document, save.path.changedParaIds) : null;
802
866
  if (selective) {
803
- await this.assertResolvedStoriesSerialized(selective);
867
+ await this.assertResolvedStoriesSerialized(selective, save.resolvedStoryExpectations);
804
868
  return selective;
805
869
  }
806
- const buffer = await repackDocx({
807
- ...document,
870
+ const repackDocument = {
871
+ ...save.document,
808
872
  originalBuffer: this.originalBuffer
809
- }, { changedNoteParaIds: this.getChangedNoteParaIds() });
810
- await this.assertResolvedStoriesSerialized(buffer);
873
+ };
874
+ const buffer = save.sectionEndpointRemoval ? await withTrackedSectionEndpointRemoval({
875
+ document: repackDocument,
876
+ resolution: save.sectionEndpointRemoval,
877
+ repack: () => repackDocx(repackDocument, { changedNoteParaIds: save.changedNoteParaIds })
878
+ }) : await repackDocx(repackDocument, { changedNoteParaIds: save.changedNoteParaIds });
879
+ await this.assertResolvedStoriesSerialized(buffer, save.resolvedStoryExpectations);
811
880
  return buffer;
812
881
  }
813
- async assertResolvedStoriesSerialized(buffer) {
814
- if (this.resolvedStoryExpectations.size === 0) return;
882
+ async assertResolvedStoriesSerialized(buffer, expectations) {
883
+ if (expectations.length === 0) return;
815
884
  const reopened = await FolioDocxReviewer.fromBuffer(buffer);
816
- for (const { story, text, blocks } of this.resolvedStoryExpectations.values()) {
885
+ for (const { story, text, blocks } of expectations) {
817
886
  const serialized = reopened.readReviewedStory({
818
887
  story,
819
888
  view: "current-markup"
820
889
  });
821
890
  const serializedState = reopened.getEditableStoryState(story);
822
- if (serialized && serializedState && serialized.changes.length === 0 && formatStoryStateForLLM(serializedState, false) === text && JSON.stringify(createFolioAIEditSnapshot(serializedState.doc).blocks) === JSON.stringify(blocks)) continue;
891
+ const serializedText = serializedState ? formatStoryStateForLLM(serializedState, false) : null;
892
+ const serializedBlocks = serializedState ? createStateSnapshot(serializedState).blocks.map(resolvedStoryBlockProjection) : null;
893
+ const mismatches = [];
894
+ if (!serialized || !serializedState) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.storyMissing);
895
+ else {
896
+ if (serialized.changes.length > 0) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.revisionMarkupRemains);
897
+ if (serializedText !== text) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.textProjection);
898
+ if (JSON.stringify(serializedBlocks) !== JSON.stringify(blocks)) mismatches.push(FOLIO_RESOLVED_STORY_SERIALIZATION_MISMATCHES.blockProjection);
899
+ }
900
+ if (mismatches.length === 0) continue;
823
901
  throw new FolioResolvedStorySerializationError({
824
902
  message: "Resolved document story did not persist to the serialized DOCX.",
825
903
  story,
826
- expectedText: text,
827
- actualText: serializedState ? formatStoryStateForLLM(serializedState, false) : null,
904
+ mismatches,
905
+ expectedBlockCount: blocks.length,
906
+ actualBlockCount: serializedBlocks?.length ?? null,
828
907
  remainingChangeCount: serialized?.changes.length ?? null
829
908
  });
830
909
  }
831
910
  }
832
- getChangedNoteParaIds() {
833
- const changed = /* @__PURE__ */ new Set();
834
- for (const entry of this.secondaryStoryStates.values()) {
835
- if (entry.handle.type !== "footnote" && entry.handle.type !== "endnote") continue;
836
- for (const paraId of getChangedParagraphIds(entry.state)) changed.add(paraId);
837
- }
838
- return changed;
839
- }
840
911
  /**
841
912
  * Attempt the selective patch, treating a throw the same as a decline. Odd
842
913
  * source XML can make the paragraph diff throw rather than return `null`; a
843
914
  * full repack is the correct lossless fallback in both cases, so the fallback
844
915
  * is the graceful handling — no separate error surface is needed here.
845
916
  */
846
- async trySelectiveSave(document) {
847
- const changedParaIds = new Set(getChangedParagraphIds(this.state));
848
- let structuralChange = hasStructuralChanges(this.state);
849
- let untrackedChanges = hasUntrackedChanges(this.state);
850
- for (const entry of this.secondaryStoryStates.values()) {
851
- if (entry.handle.type !== "footnote" && entry.handle.type !== "endnote") continue;
852
- for (const paraId of getChangedParagraphIds(entry.state)) changedParaIds.add(paraId);
853
- structuralChange ||= hasStructuralChanges(entry.state);
854
- untrackedChanges ||= hasUntrackedChanges(entry.state);
855
- }
917
+ async trySelectiveSave(document, changedParaIds) {
856
918
  try {
857
919
  return await attemptSelectiveSave(document, this.originalBuffer, {
858
920
  changedParaIds,
859
- structuralChange,
860
- hasUntrackedChanges: untrackedChanges
921
+ structuralChange: false,
922
+ hasUntrackedChanges: false
861
923
  });
862
924
  } catch {
863
925
  return null;
@@ -878,7 +940,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
878
940
  const state = ensureBaseDirectionInState(EditorState.create({
879
941
  schema,
880
942
  doc: ensureDeterministicParaIdsInDoc(storyDoc),
881
- plugins: [...singletonManager.getPlugins(), createDocumentStylesPlugin(this.baseDocument.package.styles)]
943
+ plugins: createHeadlessPlugins(this.baseDocument.package.styles, this.baseDocument.package.numbering)
882
944
  }));
883
945
  this.secondaryStoryStates.set(key, {
884
946
  handle: story,
@@ -924,19 +986,19 @@ var FolioDocxReviewer = class FolioDocxReviewer {
924
986
  const sourceText = source.type === "footnote" ? getFootnoteText(source) : getEndnoteText(source);
925
987
  return normalizeFolioAIBlockText(state?.doc.textContent ?? sourceText);
926
988
  }
927
- mergeEditedSecondaryStories(document) {
989
+ mergeEditedSecondaryStories(document, secondaryStoryStates = this.secondaryStoryStates.values()) {
928
990
  let headers;
929
991
  let footers;
930
992
  let footnotes;
931
993
  let endnotes;
932
- for (const entry of this.secondaryStoryStates.values()) {
994
+ for (const entry of secondaryStoryStates) {
933
995
  if (entry.state === entry.initialState) continue;
934
996
  if (entry.handle.type === "header" || entry.handle.type === "footer") {
935
997
  const source = this.getHeaderFooterStory(entry.handle);
936
998
  if (!source) continue;
937
999
  const edited = {
938
1000
  ...source,
939
- content: proseDocToBlocks(entry.state.doc, source.content)
1001
+ content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
940
1002
  };
941
1003
  if (entry.handle.type === "header") {
942
1004
  headers ??= new Map(document.package.headers);
@@ -953,7 +1015,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
953
1015
  if (!source) continue;
954
1016
  const edited = {
955
1017
  ...source,
956
- content: proseDocToBlocks(entry.state.doc, source.content)
1018
+ content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
957
1019
  };
958
1020
  footnotes ??= [...document.package.footnotes ?? []];
959
1021
  const index = footnotes.findIndex((note) => note.id === noteId);
@@ -965,7 +1027,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
965
1027
  if (!source) continue;
966
1028
  const edited = {
967
1029
  ...source,
968
- content: proseDocToBlocks(entry.state.doc, source.content)
1030
+ content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
969
1031
  };
970
1032
  endnotes ??= [...document.package.endnotes ?? []];
971
1033
  const index = endnotes.findIndex((note) => note.id === noteId);
@@ -977,10 +1039,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
977
1039
  if (endnotes) document.package.endnotes = endnotes;
978
1040
  }
979
1041
  /** Apply any {@link resolveComment} overrides recorded for these comments. */
980
- withResolvedOverrides(comments) {
981
- if (this.resolvedOverrides.size === 0) return [...comments];
1042
+ withResolvedOverrides(comments, resolvedOverrides = this.resolvedOverrides) {
1043
+ if (resolvedOverrides.size === 0) return [...comments];
982
1044
  return comments.map((comment) => {
983
- const override = this.resolvedOverrides.get(comment.id);
1045
+ const override = resolvedOverrides.get(comment.id);
984
1046
  return override === void 0 ? comment : {
985
1047
  ...comment,
986
1048
  done: override