@stll/folio-core 0.32.2 → 0.33.1

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 (227) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.d.ts +17 -0
  2. package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
  3. package/dist/ai-edits/apply.d.ts +49 -3
  4. package/dist/ai-edits/apply.js +652 -86
  5. package/dist/ai-edits/blockRange.d.ts +7 -3
  6. package/dist/ai-edits/blockRange.js +14 -6
  7. package/dist/ai-edits/headless.d.ts +39 -5
  8. package/dist/ai-edits/headless.js +76 -17
  9. package/dist/ai-edits/index.d.ts +6 -6
  10. package/dist/ai-edits/index.js +3 -3
  11. package/dist/ai-edits/read.d.ts +4 -0
  12. package/dist/ai-edits/read.js +30 -6
  13. package/dist/ai-edits/scoped-reading.js +4 -3
  14. package/dist/ai-edits/snapshot.d.ts +42 -2
  15. package/dist/ai-edits/snapshot.js +127 -62
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
  17. package/dist/ai-edits/table-row-column-mutations.js +98 -6
  18. package/dist/ai-edits/table-targets.d.ts +12 -3
  19. package/dist/ai-edits/table-targets.js +15 -5
  20. package/dist/ai-edits/types.d.ts +219 -15
  21. package/dist/ai-edits/word-diff.d.ts +54 -11
  22. package/dist/ai-edits/word-diff.js +221 -61
  23. package/dist/compare/__fixtures__/body-sequence.d.ts +56 -0
  24. package/dist/compare/__fixtures__/body-sequence.js +136 -0
  25. package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
  26. package/dist/compare/__fixtures__/nested-table.js +50 -0
  27. package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
  28. package/dist/compare/__fixtures__/numbered-list.js +85 -0
  29. package/dist/compare/column-alignment.d.ts +27 -0
  30. package/dist/compare/column-alignment.js +162 -0
  31. package/dist/compare/compare.d.ts +107 -0
  32. package/dist/compare/compare.js +412 -0
  33. package/dist/compare/formatting.d.ts +29 -0
  34. package/dist/compare/formatting.js +84 -0
  35. package/dist/compare/plan.d.ts +22 -0
  36. package/dist/compare/plan.js +1141 -0
  37. package/dist/compare/reproducible-package.d.ts +21 -0
  38. package/dist/compare/reproducible-package.js +47 -0
  39. package/dist/compare/scenario.d.ts +77 -0
  40. package/dist/compare/scenario.js +263 -0
  41. package/dist/compare/types.d.ts +297 -0
  42. package/dist/compare/types.js +42 -0
  43. package/dist/compare/verification.d.ts +83 -0
  44. package/dist/compare/verification.js +189 -0
  45. package/dist/compat/eigenpal.d.ts +9 -6
  46. package/dist/compat/eigenpal.js +6 -3
  47. package/dist/controller/layoutPipeline.d.ts +13 -0
  48. package/dist/controller/layoutPipeline.js +15 -1
  49. package/dist/display-list/build/buildContext.d.ts +39 -0
  50. package/dist/display-list/build/buildContext.js +31 -0
  51. package/dist/display-list/build/buildDisplayList.d.ts +79 -0
  52. package/dist/display-list/build/buildDisplayList.js +283 -0
  53. package/dist/display-list/build/colors.d.ts +20 -0
  54. package/dist/display-list/build/colors.js +136 -0
  55. package/dist/display-list/build/floatingImages.d.ts +24 -0
  56. package/dist/display-list/build/floatingImages.js +41 -0
  57. package/dist/display-list/build/fontTable.d.ts +31 -0
  58. package/dist/display-list/build/fontTable.js +97 -0
  59. package/dist/display-list/build/furniture.d.ts +86 -0
  60. package/dist/display-list/build/furniture.js +98 -0
  61. package/dist/display-list/build/glyphs.d.ts +55 -0
  62. package/dist/display-list/build/glyphs.js +103 -0
  63. package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
  64. package/dist/display-list/build/headerFooterPrimitives.js +40 -0
  65. package/dist/display-list/build/imagePrimitives.d.ts +35 -0
  66. package/dist/display-list/build/imagePrimitives.js +264 -0
  67. package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
  68. package/dist/display-list/build/pageBorderPrimitives.js +111 -0
  69. package/dist/display-list/build/pageFurniture.d.ts +35 -0
  70. package/dist/display-list/build/pageFurniture.js +141 -0
  71. package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
  72. package/dist/display-list/build/paragraphPrimitives.js +819 -0
  73. package/dist/display-list/build/regions.d.ts +46 -0
  74. package/dist/display-list/build/regions.js +56 -0
  75. package/dist/display-list/build/storyPrimitives.d.ts +29 -0
  76. package/dist/display-list/build/storyPrimitives.js +279 -0
  77. package/dist/display-list/build/strokes.d.ts +19 -0
  78. package/dist/display-list/build/strokes.js +78 -0
  79. package/dist/display-list/build/tablePrimitives.d.ts +38 -0
  80. package/dist/display-list/build/tablePrimitives.js +409 -0
  81. package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
  82. package/dist/display-list/build/textBoxPrimitives.js +129 -0
  83. package/dist/display-list/build/textDecorations.d.ts +29 -0
  84. package/dist/display-list/build/textDecorations.js +29 -0
  85. package/dist/display-list/build/unsupported.d.ts +57 -0
  86. package/dist/display-list/build/unsupported.js +0 -0
  87. package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
  88. package/dist/display-list/build/watermarkPrimitives.js +128 -0
  89. package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
  90. package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
  91. package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
  92. package/dist/display-list/editor/displayListPagePainter.js +63 -0
  93. package/dist/display-list/editor/pageRenderer.d.ts +24 -0
  94. package/dist/display-list/editor/pageRenderer.js +36 -0
  95. package/dist/display-list/primitives.d.ts +95 -0
  96. package/dist/display-list/primitives.js +127 -0
  97. package/dist/display-list/types.d.ts +472 -0
  98. package/dist/display-list/types.js +0 -0
  99. package/dist/document-operations.d.ts +39 -8
  100. package/dist/document-operations.js +169 -16
  101. package/dist/document-stories.d.ts +16 -0
  102. package/dist/document-stories.js +48 -4
  103. package/dist/docx/hyperlinkParser.d.ts +9 -1
  104. package/dist/docx/hyperlinkParser.js +19 -13
  105. package/dist/docx/packageParts.d.ts +35 -0
  106. package/dist/docx/packageParts.js +120 -0
  107. package/dist/docx/paraIdRangeNormalization.d.ts +40 -0
  108. package/dist/docx/paraIdRangeNormalization.js +64 -0
  109. package/dist/docx/paragraphParser.js +101 -26
  110. package/dist/docx/paragraphTraversal.js +1 -1
  111. package/dist/docx/renderedPageBreakNormalization.js +3 -1
  112. package/dist/docx/revisionIdNormalization.d.ts +27 -0
  113. package/dist/docx/revisionIdNormalization.js +121 -0
  114. package/dist/docx/rezip.d.ts +6 -0
  115. package/dist/docx/rezip.js +51 -19
  116. package/dist/docx/selectiveSave.js +2 -2
  117. package/dist/docx/serializer/commentSerializer.js +6 -1
  118. package/dist/docx/serializer/paragraphSerializer.js +73 -20
  119. package/dist/docx/serializer/runSerializer.js +11 -10
  120. package/dist/docx/serializer/tableSerializer.js +26 -10
  121. package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
  122. package/dist/docx/server/createBilingualDocx.js +3 -2
  123. package/dist/docx/streamingXmlParser.d.ts +13 -1
  124. package/dist/docx/streamingXmlParser.js +49 -11
  125. package/dist/docx/unzip.d.ts +1 -2
  126. package/dist/docx/unzip.js +25 -6
  127. package/dist/docx/xmlParser.d.ts +3 -1
  128. package/dist/docx/xmlParser.js +33 -27
  129. package/dist/export-pdf.d.ts +43 -0
  130. package/dist/export-pdf.js +98 -0
  131. package/dist/fonts/headlessMeasure.d.ts +76 -0
  132. package/dist/fonts/headlessMeasure.js +0 -0
  133. package/dist/fonts/sfnt/parse.d.ts +51 -0
  134. package/dist/fonts/sfnt/parse.js +525 -0
  135. package/dist/fonts/sfnt/subset.d.ts +20 -0
  136. package/dist/fonts/sfnt/subset.js +350 -0
  137. package/dist/fonts/sfnt/tables.d.ts +88 -0
  138. package/dist/fonts/sfnt/tables.js +131 -0
  139. package/dist/fonts/sfnt/woff.d.ts +14 -0
  140. package/dist/fonts/sfnt/woff.js +163 -0
  141. package/dist/generated/text_shaper.js +324 -0
  142. package/dist/generated/text_shaper_bg.wasm +0 -0
  143. package/dist/headless-layout.d.ts +75 -0
  144. package/dist/headless-layout.js +350 -0
  145. package/dist/index.d.ts +9 -6
  146. package/dist/index.js +6 -3
  147. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  148. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  149. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  150. package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
  151. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
  152. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
  153. package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
  154. package/dist/layout-engine/measure/advanceComposition.js +148 -0
  155. package/dist/layout-engine/measure/cache.d.ts +2 -0
  156. package/dist/layout-engine/measure/measureContainer.js +13 -0
  157. package/dist/layout-engine/measure/measureParagraph.js +1 -2
  158. package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
  159. package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
  160. package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
  161. package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
  162. package/dist/layout-engine/tableIndentCompatibility.js +15 -0
  163. package/dist/layout-engine/types.d.ts +8 -0
  164. package/dist/layout-painter/renderPage.d.ts +29 -1
  165. package/dist/layout-painter/renderPage.js +47 -19
  166. package/dist/layout-painter/renderParagraph.d.ts +21 -2
  167. package/dist/layout-painter/renderParagraph.js +1 -1
  168. package/dist/layout-painter/renderTable.js +37 -4
  169. package/dist/managers/editorShortcuts.d.ts +33 -1
  170. package/dist/managers/editorShortcuts.js +20 -1
  171. package/dist/markdown/renderRuns.js +7 -2
  172. package/dist/markdown/renderTable.js +5 -1
  173. package/dist/model.d.ts +3 -3
  174. package/dist/model.js +2 -2
  175. package/dist/pdf/contentStream.d.ts +73 -0
  176. package/dist/pdf/contentStream.js +143 -0
  177. package/dist/pdf/fonts.d.ts +96 -0
  178. package/dist/pdf/fonts.js +593 -0
  179. package/dist/pdf/images.d.ts +28 -0
  180. package/dist/pdf/images.js +439 -0
  181. package/dist/pdf/objects.d.ts +93 -0
  182. package/dist/pdf/objects.js +237 -0
  183. package/dist/pdf/pageSpace.d.ts +43 -0
  184. package/dist/pdf/pageSpace.js +57 -0
  185. package/dist/pdf/paint.d.ts +32 -0
  186. package/dist/pdf/paint.js +442 -0
  187. package/dist/pdf/writePdf.d.ts +56 -0
  188. package/dist/pdf/writePdf.js +345 -0
  189. package/dist/prosemirror/commands/comments.js +96 -6
  190. package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
  191. package/dist/prosemirror/conversion/toProseDoc.js +41 -4
  192. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
  193. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
  194. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
  195. package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
  196. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
  197. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
  198. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
  199. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
  200. package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
  201. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
  202. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
  203. package/dist/prosemirror/plugins/suggestionMode.js +1 -0
  204. package/dist/prosemirror/revisionCarriers.js +19 -2
  205. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
  206. package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
  207. package/dist/prosemirror/validation.js +55 -27
  208. package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
  209. package/dist/prosemirror/zeroWidthAnchors.js +23 -0
  210. package/dist/redline.js +12 -79
  211. package/dist/server.d.ts +3 -3
  212. package/dist/server.js +2 -2
  213. package/dist/shaping/placeRun.d.ts +51 -0
  214. package/dist/shaping/placeRun.js +84 -0
  215. package/dist/shaping/shaper.d.ts +92 -0
  216. package/dist/shaping/shaper.js +102 -0
  217. package/dist/types/block-id.d.ts +18 -2
  218. package/dist/types/block-id.js +49 -5
  219. package/dist/types/content.d.ts +2 -2
  220. package/dist/utils/clipboard.js +4 -3
  221. package/dist/utils/fontResolver.js +183 -0
  222. package/dist/utils/htmlComments.d.ts +17 -0
  223. package/dist/utils/htmlComments.js +22 -0
  224. package/dist/utils/scriptSegments.d.ts +12 -1
  225. package/dist/utils/scriptSegments.js +17 -1
  226. package/dist/version-comparison.d.ts +1 -1
  227. package/package.json +7 -3
@@ -50,9 +50,13 @@ declare const resolvePassageRange: ({ blockId, text, doc, snapshot }: ResolvePas
50
50
  * server's `seq-NNNN`: the direct anchor lookup misses, and a
51
51
  * paraId-based live lookup can't match either (no node carries a
52
52
  * `seq-` paraId). The seq number is the block's 1-based position in
53
- * the same non-empty-block walk the server extractor and
54
- * `createFolioAIEditSnapshot` share, so it indexes the snapshot's
55
- * ordered `blocks` directly.
53
+ * the non-empty-block walk the server extractor and
54
+ * `createFolioAIEditSnapshot` share.
55
+ *
56
+ * That walk is not the snapshot's `blocks` array: `blocks` also holds
57
+ * the document's blank paragraphs, which the seq sequence does not
58
+ * count. So the position is counted here over the blocks that carry
59
+ * text, which is what the number means.
56
60
  */
57
61
  declare const resolveSequentialBlockAnchor: (blockId: string, snapshot: FolioAIEditSnapshot) => FolioAIBlockAnchor | undefined;
58
62
  /**
@@ -1,7 +1,7 @@
1
1
  import { findParagraphByParaId } from "../prosemirror/utils/findParagraphByParaId.js";
2
2
  import { getFolioParaIdFromBlockId, getSequentialFolioBlockIdIndex } from "../types/block-id.js";
3
3
  import { buildCleanBlockText } from "./clean-text.js";
4
- import { createFolioAIEditSnapshot, hashFolioAIBlockText } from "./snapshot.js";
4
+ import { createFolioAIEditSnapshot, hashFolioAIBlockText, isFolioAIContentBlock } from "./snapshot.js";
5
5
  //#region src/ai-edits/blockRange.ts
6
6
  const resolveFolioAIBlockRange = ({ blockId, doc, snapshot }) => {
7
7
  const paraId = getFolioParaIdFromBlockId(blockId);
@@ -113,15 +113,23 @@ const matchPassage = (haystack, needleSource) => {
113
113
  * server's `seq-NNNN`: the direct anchor lookup misses, and a
114
114
  * paraId-based live lookup can't match either (no node carries a
115
115
  * `seq-` paraId). The seq number is the block's 1-based position in
116
- * the same non-empty-block walk the server extractor and
117
- * `createFolioAIEditSnapshot` share, so it indexes the snapshot's
118
- * ordered `blocks` directly.
116
+ * the non-empty-block walk the server extractor and
117
+ * `createFolioAIEditSnapshot` share.
118
+ *
119
+ * That walk is not the snapshot's `blocks` array: `blocks` also holds
120
+ * the document's blank paragraphs, which the seq sequence does not
121
+ * count. So the position is counted here over the blocks that carry
122
+ * text, which is what the number means.
119
123
  */
120
124
  const resolveSequentialBlockAnchor = (blockId, snapshot) => {
121
125
  const index = getSequentialFolioBlockIdIndex(blockId);
122
126
  if (index === null) return;
123
- const block = snapshot.blocks.at(index - 1);
124
- return block ? snapshot.anchors[block.id] : void 0;
127
+ let remaining = index;
128
+ for (const block of snapshot.blocks) {
129
+ if (!isFolioAIContentBlock(block)) continue;
130
+ remaining -= 1;
131
+ if (remaining === 0) return snapshot.anchors[block.id];
132
+ }
125
133
  };
126
134
  /**
127
135
  * Clamp a `{from, to}` pair so both endpoints fit inside a document of
@@ -1,4 +1,5 @@
1
1
  import { FolioAIBlock, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
2
+ import { FolioRevisionStamp, FolioWordDiffOptions } from "./apply.js";
2
3
  import { document_d_exports } from "../types/document.js";
3
4
  import { FolioDocumentOperationBatch, FolioDocumentOperationResult, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult } from "../document-operations.js";
4
5
  import { FolioReviewChange, FolioReviewChangeKind } from "./read.js";
@@ -20,6 +21,10 @@ type FolioApplyOperationsOptions = {
20
21
  * `snapshot()` -> build ops -> `applyOperations()` on the same reviewer.
21
22
  */
22
23
  snapshot?: FolioAIEditSnapshot;
24
+ /** Omit to stamp revisions from the wall clock and the shared id cursor. */
25
+ revisionStamp?: FolioRevisionStamp;
26
+ /** Token size a replacement's redline is cut at. Word-level by default. */
27
+ wordDiff?: FolioWordDiffOptions;
23
28
  };
24
29
  /** Options for {@link FolioDocxReviewer.applyDocumentOperations}. */
25
30
  type FolioApplyDocumentOperationsOptions = Omit<FolioApplyOperationsOptions, "mode">;
@@ -52,6 +57,17 @@ type FolioDocumentStoryHandle = {
52
57
  noteId: number;
53
58
  };
54
59
  type FolioEditableDocumentStoryHandle = FolioDocumentStoryHandle;
60
+ /**
61
+ * One numbering level as a reader meets it: which list, which depth, and the
62
+ * format and template that produce the label.
63
+ */
64
+ type FolioNumberingLevel = {
65
+ numId: number;
66
+ level: number;
67
+ format: string;
68
+ levelText: string;
69
+ start?: number;
70
+ };
55
71
  type FolioDocumentStory = {
56
72
  handle: FolioDocumentStoryHandle;
57
73
  text: string;
@@ -169,6 +185,18 @@ declare class FolioDocxReviewer {
169
185
  * the anchor map the apply layer resolves operations against.
170
186
  */
171
187
  snapshot(): FolioAIEditSnapshot;
188
+ /**
189
+ * The package's numbering, flattened to what a reader sees: one entry per
190
+ * numbering instance and level, with the format and level text that produce
191
+ * its labels.
192
+ *
193
+ * Labels are rendered from these definitions rather than stored on the
194
+ * paragraphs, so nothing in a block projection changes when a list is
195
+ * renumbered — which is right for an insertion that renumbers the items
196
+ * below it, and wrong for a list whose FORMAT changed. Sorted so two
197
+ * packages can be compared entry by entry.
198
+ */
199
+ readNumberingDefinitions(): FolioNumberingLevel[];
172
200
  /** Return parsed package metadata without exposing the mutable document model. */
173
201
  getDocumentProperties(): Readonly<NonNullable<document_d_exports.Document["package"]["properties"]>> | null;
174
202
  /** Snapshot one editable story into stable, operation-ready blocks. */
@@ -191,7 +219,7 @@ declare class FolioDocxReviewer {
191
219
  */
192
220
  applyDocumentOperations(batch: FolioDocumentOperationBatch, options?: FolioApplyDocumentOperationsOptions): FolioDocumentOperationResult;
193
221
  /** Apply a versioned operation batch to one editable document story. */
194
- applyDocumentOperationsToStory({ story, batch, snapshot }: FolioApplyDocumentOperationsToStoryOptions): FolioDocumentOperationResult;
222
+ applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff }: FolioApplyDocumentOperationsToStoryOptions): FolioDocumentOperationResult;
195
223
  private applyDocumentOperationsInternal;
196
224
  /** Undo the latest unchanged document-operation batch and its created comments. */
197
225
  undoDocumentOperations(undoHandle: FolioDocumentOperationUndoHandle): FolioDocumentOperationUndoResult;
@@ -276,12 +304,17 @@ declare class FolioDocxReviewer {
276
304
  */
277
305
  rejectChange(target: FolioReviewChange | number): boolean;
278
306
  /**
279
- * Accept every tracked change in the body. Returns the number of changes
280
- * present before the sweep. Note-body changes are out of scope.
307
+ * Accept every tracked change in the package. Returns the number of changes
308
+ * present before the sweep.
309
+ *
310
+ * Every story, not just the body: a revision in a header or a footnote is
311
+ * one a reviewer meant to resolve, and leaving it behind means an accepted
312
+ * document still carries a redline nobody can see from the body.
281
313
  */
282
314
  acceptAll(): number;
283
- /** Reject every tracked change in the body. See {@link acceptAll}. */
315
+ /** Reject every tracked change in the package. See {@link acceptAll}. */
284
316
  rejectAll(): number;
317
+ private resolveEveryStory;
285
318
  /** The current document model with edits merged back in. */
286
319
  toDocument(): document_d_exports.Document;
287
320
  /**
@@ -320,6 +353,7 @@ declare class FolioDocxReviewer {
320
353
  * resulting state for {@link toBuffer}.
321
354
  */
322
355
  private runCommand;
356
+ private runStoryCommand;
323
357
  }
324
358
  /** Options for {@link applyFolioAIEditsToBuffer}. */
325
359
  type ApplyFolioAIEditsToBufferOptions = {
@@ -348,4 +382,4 @@ type ApplyFolioAIEditsToBufferResult = FolioAIEditApplyResult & {
348
382
  */
349
383
  declare const applyFolioAIEditsToBuffer: (buffer: ArrayBuffer, operations: FolioAIEditOperation[], options?: ApplyFolioAIEditsToBufferOptions) => Promise<ApplyFolioAIEditsToBufferResult>;
350
384
  //#endregion
351
- export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioGetContentAsTextOptions, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
385
+ export { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioGetContentAsTextOptions, FolioNumberingLevel, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, type FolioReviewChange, FolioReviewChangeFilter, type FolioReviewChangeKind, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, type FolioRevisionStamp, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView };
@@ -14,7 +14,7 @@ import { schema, singletonManager } from "../prosemirror/schema/index.js";
14
14
  import { deterministicHexId } from "../utils/hexId.js";
15
15
  import { buildAnnotatedBlockText } from "./clean-text.js";
16
16
  import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
17
- import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
17
+ import { createFolioAIEditSnapshot, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
18
18
  import { TaggedError } from "better-result";
19
19
  import { Fragment } from "prosemirror-model";
20
20
  import { EditorState } from "prosemirror-state";
@@ -154,12 +154,19 @@ const resolveReviewedState = (state, view) => {
154
154
  };
155
155
  const formatStoryStateForLLM = (state, annotated) => {
156
156
  const snapshot = createFolioAIEditSnapshot(state.doc);
157
- if (!annotated) return snapshot.blocks.map(formatBlockForLLM).join("\n");
157
+ const blocks = snapshot.blocks.filter(isFolioAIContentBlock);
158
+ if (!annotated) return blocks.map(formatBlockForLLM).join("\n");
159
+ const nodeByStart = /* @__PURE__ */ new Map();
160
+ state.doc.descendants((node, pos) => {
161
+ if (!node.isTextblock) return true;
162
+ nodeByStart.set(pos, node);
163
+ return false;
164
+ });
158
165
  const startById = /* @__PURE__ */ new Map();
159
166
  for (const anchor of Object.values(snapshot.anchors)) startById.set(anchor.id, anchor.from);
160
- return snapshot.blocks.map((block) => {
167
+ return blocks.map((block) => {
161
168
  const from = startById.get(block.id);
162
- const node = from === void 0 ? null : state.doc.nodeAt(from);
169
+ const node = from === void 0 ? void 0 : nodeByStart.get(from);
163
170
  const text = node ? buildAnnotatedBlockText(node) : block.text;
164
171
  return formatBlockLine(block, text);
165
172
  }).join("\n");
@@ -260,6 +267,38 @@ var FolioDocxReviewer = class FolioDocxReviewer {
260
267
  snapshot() {
261
268
  return createFolioAIEditSnapshot(this.state.doc);
262
269
  }
270
+ /**
271
+ * The package's numbering, flattened to what a reader sees: one entry per
272
+ * numbering instance and level, with the format and level text that produce
273
+ * its labels.
274
+ *
275
+ * Labels are rendered from these definitions rather than stored on the
276
+ * paragraphs, so nothing in a block projection changes when a list is
277
+ * renumbered — which is right for an insertion that renumbers the items
278
+ * below it, and wrong for a list whose FORMAT changed. Sorted so two
279
+ * packages can be compared entry by entry.
280
+ */
281
+ readNumberingDefinitions() {
282
+ const numbering = this.baseDocument.package.numbering;
283
+ if (!numbering) return [];
284
+ const levelsByAbstractId = new Map(numbering.abstractNums.map((abstractNum) => [abstractNum.abstractNumId, abstractNum.levels]));
285
+ const levels = [];
286
+ for (const instance of numbering.nums) {
287
+ const overrides = new Map((instance.levelOverrides ?? []).map((override) => [override.ilvl, override]));
288
+ for (const level of levelsByAbstractId.get(instance.abstractNumId) ?? []) {
289
+ const override = overrides.get(level.ilvl);
290
+ const resolved = override?.lvl ?? level;
291
+ levels.push({
292
+ numId: instance.numId,
293
+ level: resolved.ilvl,
294
+ format: resolved.numFmt,
295
+ levelText: resolved.lvlText,
296
+ ...(override?.startOverride ?? resolved.start) !== void 0 ? { start: override?.startOverride ?? resolved.start } : {}
297
+ });
298
+ }
299
+ }
300
+ return levels.toSorted((left, right) => left.numId - right.numId || left.level - right.level);
301
+ }
263
302
  /** Return parsed package metadata without exposing the mutable document model. */
264
303
  getDocumentProperties() {
265
304
  const properties = this.baseDocument.package.properties;
@@ -325,6 +364,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
325
364
  mode: options.mode ?? "tracked-changes"
326
365
  },
327
366
  ...options.snapshot !== void 0 && { snapshot: options.snapshot },
367
+ ...options.revisionStamp !== void 0 && { revisionStamp: options.revisionStamp },
328
368
  createUndoEntry: false
329
369
  });
330
370
  return {
@@ -343,19 +383,22 @@ var FolioDocxReviewer = class FolioDocxReviewer {
343
383
  story: MAIN_STORY,
344
384
  batch,
345
385
  ...options.snapshot !== void 0 && { snapshot: options.snapshot },
386
+ ...options.revisionStamp !== void 0 && { revisionStamp: options.revisionStamp },
346
387
  createUndoEntry: true
347
388
  });
348
389
  }
349
390
  /** Apply a versioned operation batch to one editable document story. */
350
- applyDocumentOperationsToStory({ story, batch, snapshot }) {
391
+ applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff }) {
351
392
  return this.applyDocumentOperationsInternal({
352
393
  story,
353
394
  batch,
354
395
  ...snapshot !== void 0 && { snapshot },
396
+ ...revisionStamp !== void 0 && { revisionStamp },
397
+ ...wordDiff !== void 0 && { wordDiff },
355
398
  createUndoEntry: true
356
399
  });
357
400
  }
358
- applyDocumentOperationsInternal({ story, batch, snapshot, createUndoEntry }) {
401
+ applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, createUndoEntry }) {
359
402
  const beforeState = this.requireEditableStoryState(story);
360
403
  const createdCommentsLengthBefore = this.createdComments.length;
361
404
  const view = {
@@ -370,6 +413,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
370
413
  batch,
371
414
  story: story.type === "main" ? "main" : story,
372
415
  author: this.author,
416
+ ...revisionStamp !== void 0 && { revisionStamp },
417
+ ...wordDiff !== void 0 && { wordDiff },
373
418
  createCommentId: (text) => {
374
419
  const comment = createReviewerComment(this.nextCommentId(), text, this.author);
375
420
  this.createdComments.push(comment);
@@ -658,18 +703,28 @@ var FolioDocxReviewer = class FolioDocxReviewer {
658
703
  return this.runCommand(rejectAIEditRevision(revisionIdOf(target)));
659
704
  }
660
705
  /**
661
- * Accept every tracked change in the body. Returns the number of changes
662
- * present before the sweep. Note-body changes are out of scope.
706
+ * Accept every tracked change in the package. Returns the number of changes
707
+ * present before the sweep.
708
+ *
709
+ * Every story, not just the body: a revision in a header or a footnote is
710
+ * one a reviewer meant to resolve, and leaving it behind means an accepted
711
+ * document still carries a redline nobody can see from the body.
663
712
  */
664
713
  acceptAll() {
665
- const count = this.getChanges().length;
666
- this.runCommand(acceptAllChanges());
667
- return count;
714
+ return this.resolveEveryStory(acceptAllChanges());
668
715
  }
669
- /** Reject every tracked change in the body. See {@link acceptAll}. */
716
+ /** Reject every tracked change in the package. See {@link acceptAll}. */
670
717
  rejectAll() {
671
- const count = this.getChanges().length;
672
- this.runCommand(rejectAllChanges());
718
+ return this.resolveEveryStory(rejectAllChanges());
719
+ }
720
+ resolveEveryStory(command) {
721
+ let count = 0;
722
+ for (const { handle } of this.listStories()) {
723
+ const state = this.getEditableStoryState(handle);
724
+ if (!state) continue;
725
+ count += getTrackedChangesFromDoc(state.doc).length;
726
+ this.runStoryCommand(command, handle);
727
+ }
673
728
  return count;
674
729
  }
675
730
  /** The current document model with edits merged back in. */
@@ -898,15 +953,19 @@ var FolioDocxReviewer = class FolioDocxReviewer {
898
953
  * resulting state for {@link toBuffer}.
899
954
  */
900
955
  runCommand(command) {
901
- this.resolvedStoryExpectations.delete("main");
956
+ return this.runStoryCommand(command, MAIN_STORY);
957
+ }
958
+ runStoryCommand(command, story) {
959
+ const state = this.getEditableStoryState(story);
960
+ if (!state) return false;
902
961
  const view = {
903
- state: this.state,
962
+ state,
904
963
  dispatch: (transaction) => {
905
964
  view.state = view.state.apply(transaction);
906
965
  }
907
966
  };
908
967
  const handled = command(view.state, view.dispatch);
909
- this.state = view.state;
968
+ this.setEditableStoryState(story, view.state);
910
969
  return handled;
911
970
  }
912
971
  };
@@ -1,12 +1,12 @@
1
- import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAISignatureParty, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./types.js";
2
- import { FolioAIEditView, applyFolioAIEditOperations } from "./apply.js";
1
+ import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAISignatureParty, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./types.js";
2
+ import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "./word-diff.js";
3
+ import { FolioAIEditApplyOutcome, FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "./apply.js";
3
4
  import { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange } from "./blockRange.js";
4
5
  import { buildAnnotatedBlockText } from "./clean-text.js";
5
- import { ApplyFolioDocumentOperationsOptions, 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, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
6
+ import { ApplyFolioDocumentOperationsOptions, 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, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
6
7
  import { FolioCommentAnchor, FolioReviewChange, FolioReviewChangeKind, getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
7
8
  import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsToStoryOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
8
- import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./snapshot.js";
9
+ import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
9
10
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
10
11
  import { getFolioParaIdFromBlockId } from "../types/block-id.js";
11
- import { WordDiffSegment, diffWordSegments } from "./word-diff.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_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineFormatting, 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 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, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
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_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, 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 FolioAIInlineFormatting, 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 };
@@ -6,6 +6,6 @@ import { buildAnnotatedBlockText } from "./clean-text.js";
6
6
  import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
7
7
  import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
8
8
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
9
- import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, normalizeFolioAIBlockText } from "./snapshot.js";
10
- import { diffWordSegments } from "./word-diff.js";
11
- export { 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_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
9
+ import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
10
+ import { WORD_DIFF_GRANULARITIES, diffWordSegments } from "./word-diff.js";
11
+ export { 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_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, 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 };
@@ -60,6 +60,10 @@ type FolioCommentAnchor = {
60
60
  * The tracked changes present in the body, read from inline marks and
61
61
  * structural node attributes. Runs of one inline revision within a block fold
62
62
  * into a single entry.
63
+ *
64
+ * A tracked row insertion or deletion marks the row AND every run in its
65
+ * cells, the way Word writes it. Both halves are one change, so the run marks
66
+ * fold into the row's entry rather than reporting a second time.
63
67
  */
64
68
  declare const getTrackedChangesFromDoc: (doc: Node) => FolioReviewChange[];
65
69
  /**
@@ -44,9 +44,20 @@ const firstBlockIdWithin = ({ node, nodePos, blockStarts }) => {
44
44
  return blockId;
45
45
  };
46
46
  /**
47
+ * Whether a run's revision inside a marked row is that row's own. Folio writes
48
+ * both halves under one revision id; Word mints a fresh `w:id` per element, so
49
+ * an author + timestamp match counts too. A third party's edit inside the row
50
+ * matches neither and keeps its own entry.
51
+ */
52
+ const belongsToRowRevision = (scope, revision) => revision.id === scope.change.id || revision.author === scope.change.author && revision.date === scope.change.date;
53
+ /**
47
54
  * The tracked changes present in the body, read from inline marks and
48
55
  * structural node attributes. Runs of one inline revision within a block fold
49
56
  * into a single entry.
57
+ *
58
+ * A tracked row insertion or deletion marks the row AND every run in its
59
+ * cells, the way Word writes it. Both halves are one change, so the run marks
60
+ * fold into the row's entry rather than reporting a second time.
50
61
  */
51
62
  const getTrackedChangesFromDoc = (doc) => {
52
63
  const insertionType = doc.type.schema.marks["insertion"];
@@ -54,8 +65,10 @@ const getTrackedChangesFromDoc = (doc) => {
54
65
  const blockStarts = blockStartIdsFromDoc(doc);
55
66
  const grouped = /* @__PURE__ */ new Map();
56
67
  const structuralChangeCellPositions = /* @__PURE__ */ new Map();
68
+ const rowRevisionScopes = [];
57
69
  let currentBlockId = null;
58
70
  doc.descendants((node, pos) => {
71
+ while (pos >= (rowRevisionScopes.at(-1)?.end ?? Number.POSITIVE_INFINITY)) rowRevisionScopes.pop();
59
72
  const nodeRevisionCarriers = getFolioNodeRevisionCarriers(node, pos);
60
73
  if (nodeRevisionCarriers.length > 0) {
61
74
  const blockId = node.isTextblock ? blockStarts.get(pos) ?? null : firstBlockIdWithin({
@@ -80,7 +93,7 @@ const getTrackedChangesFromDoc = (doc) => {
80
93
  const revisionId = marker.revisionId;
81
94
  const author = "author" in marker ? marker.author : void 0;
82
95
  const date = "date" in marker ? marker.date : void 0;
83
- grouped.set(`row:${kind}:${revisionId}:${String(pos)}`, {
96
+ const change = {
84
97
  id: revisionId,
85
98
  type: kind,
86
99
  author: typeof author === "string" ? author : "",
@@ -91,6 +104,12 @@ const getTrackedChangesFromDoc = (doc) => {
91
104
  nodePos: pos,
92
105
  blockStarts
93
106
  })
107
+ };
108
+ grouped.set(`row:${kind}:${revisionId}:${String(pos)}`, change);
109
+ rowRevisionScopes.push({
110
+ end: pos + node.nodeSize,
111
+ kind: kind === "rowInserted" ? "insertion" : "deletion",
112
+ change
94
113
  });
95
114
  }
96
115
  if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
@@ -182,19 +201,24 @@ const getTrackedChangesFromDoc = (doc) => {
182
201
  else if (mark.type === deletionType) kind = "deletion";
183
202
  else continue;
184
203
  const revisionId = mark.attrs["revisionId"];
204
+ const author = mark.attrs["author"];
205
+ const date = mark.attrs["date"];
206
+ const revision = {
207
+ id: revisionId,
208
+ author: typeof author === "string" ? author : "",
209
+ date: typeof date === "string" ? date : null
210
+ };
211
+ const rowScope = rowRevisionScopes.at(-1);
212
+ if (rowScope?.kind === kind && belongsToRowRevision(rowScope, revision)) continue;
185
213
  const key = `${currentBlockId ?? ""}:${kind}:${revisionId}`;
186
214
  const existing = grouped.get(key);
187
215
  if (existing) {
188
216
  existing.text += text;
189
217
  continue;
190
218
  }
191
- const author = mark.attrs["author"];
192
- const date = mark.attrs["date"];
193
219
  grouped.set(key, {
194
- id: revisionId,
220
+ ...revision,
195
221
  type: kind,
196
- author: typeof author === "string" ? author : "",
197
- date: typeof date === "string" ? date : null,
198
222
  text,
199
223
  blockId: currentBlockId
200
224
  });
@@ -1,3 +1,4 @@
1
+ import { isFolioAIContentBlock } from "./snapshot.js";
1
2
  //#region src/ai-edits/scoped-reading.ts
2
3
  const toSectionHandle = (snapshot, headingBlockId, headingLevel) => {
3
4
  const headingTextHash = snapshot.anchors[headingBlockId]?.textHash;
@@ -15,7 +16,7 @@ const getFolioDocumentOutline = (snapshot) => {
15
16
  const sections = [];
16
17
  const parentStack = [];
17
18
  for (const block of snapshot.blocks) {
18
- if (block.headingLevel === void 0) continue;
19
+ if (block.headingLevel === void 0 || !isFolioAIContentBlock(block)) continue;
19
20
  const handle = toSectionHandle(snapshot, block.id, block.headingLevel);
20
21
  if (handle === null) continue;
21
22
  while ((parentStack.at(-1)?.level ?? 0) >= block.headingLevel) parentStack.pop();
@@ -42,7 +43,7 @@ const readFolioDocumentSection = (snapshot, handle) => {
42
43
  let endIndex = snapshot.blocks.length;
43
44
  for (let index = startIndex + 1; index < snapshot.blocks.length; index++) {
44
45
  const block = snapshot.blocks.at(index);
45
- if (block?.headingLevel !== void 0 && block.headingLevel <= heading.level) {
46
+ if (block !== void 0 && block.headingLevel !== void 0 && block.headingLevel <= heading.level && isFolioAIContentBlock(block)) {
46
47
  endIndex = index;
47
48
  break;
48
49
  }
@@ -52,7 +53,7 @@ const readFolioDocumentSection = (snapshot, handle) => {
52
53
  section: {
53
54
  handle,
54
55
  heading,
55
- blocks: snapshot.blocks.slice(startIndex, endIndex)
56
+ blocks: snapshot.blocks.slice(startIndex, endIndex).filter(isFolioAIContentBlock)
56
57
  }
57
58
  };
58
59
  };
@@ -1,7 +1,39 @@
1
- import { FolioAIEditSnapshot, FolioAITextRangeHandle } from "./types.js";
1
+ import { FolioAIBlock, FolioAIEditSnapshot, FolioAITextRangeHandle } from "./types.js";
2
2
  import { Node } from "prosemirror-model";
3
3
  //#region src/ai-edits/snapshot.d.ts
4
4
  declare const normalizeFolioAIBlockText: (text: string) => string;
5
+ /**
6
+ * Whether a block carries text a reader would see.
7
+ *
8
+ * The snapshot holds every paragraph, blank ones included: an empty cell is
9
+ * part of a table's shape, an empty row is a row, and a comparison that cannot
10
+ * address them cannot describe what changed around them. A reading surface
11
+ * wants the opposite — a model shown a document should see its content, not a
12
+ * line per blank paragraph.
13
+ *
14
+ * So the two needs are split here rather than in the walk: the snapshot is
15
+ * complete, and every surface that reads it for a person or a model states
16
+ * that it wants content by calling this. One helper, so "what counts as
17
+ * content" has a single answer; five inline emptiness checks would drift.
18
+ */
19
+ declare const isFolioAIContentBlock: ({ text }: Pick<FolioAIBlock, "text">) => boolean;
20
+ /**
21
+ * The block that content appended to the end of a story hangs from: the last
22
+ * paragraph at body level.
23
+ *
24
+ * Not simply the last block. A story's last block is often inside a table, and
25
+ * a paragraph cannot be appended after one: the insertion escapes to the
26
+ * table's boundary, where the block it was anchored to is not adjacent to it
27
+ * and its paragraph mark ends a different container's paragraph. Neither
28
+ * container-edge rule then applies and the addition cannot be rejected
29
+ * cleanly.
30
+ *
31
+ * A body always ends with a paragraph — a table may not be the last child of a
32
+ * body or a cell, so a package that ends in a table carries a trailing, often
33
+ * empty, paragraph after it — so this is only `null` for a story with no
34
+ * body-level paragraph at all, which is a malformed document.
35
+ */
36
+ declare const trailingBodyBlockId: ({ blocks }: FolioAIEditSnapshot) => string | null;
5
37
  declare const hashFolioAIBlockText: (text: string) => string;
6
38
  type CreateFolioAITextRangeHandleOptions = {
7
39
  blockId: string;
@@ -10,6 +42,14 @@ type CreateFolioAITextRangeHandleOptions = {
10
42
  endOffset: number;
11
43
  };
12
44
  declare const createFolioAITextRangeHandle: ({ blockId, text, startOffset, endOffset }: CreateFolioAITextRangeHandleOptions) => FolioAITextRangeHandle | null;
45
+ /**
46
+ * True for a `tableRow` marked `hidden` (OOXML `w:trPr/w:hidden` — Word never
47
+ * renders the row). Nothing inside such a row may surface its text to the
48
+ * AI/agent snapshot: an attacker DOCX could otherwise smuggle prompt-injection
49
+ * instructions into a row that no human ever sees. The walk skips the row's
50
+ * whole subtree, so a table nested inside a hidden row is hidden with it.
51
+ */
52
+ declare const isHiddenTableRow: (node: Node) => boolean;
13
53
  declare const createFolioAIEditSnapshot: (doc: Node) => FolioAIEditSnapshot;
14
54
  //#endregion
15
- export { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, normalizeFolioAIBlockText };
55
+ export { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, trailingBodyBlockId };