@stll/folio-core 0.52.0 → 0.53.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 (307) hide show
  1. package/README.md +23 -0
  2. package/dist/ai-edits/apply.d.ts +26 -1
  3. package/dist/ai-edits/apply.js +1519 -105
  4. package/dist/ai-edits/batch-claims.d.ts +130 -0
  5. package/dist/ai-edits/batch-claims.js +244 -0
  6. package/dist/ai-edits/blockRange.js +9 -3
  7. package/dist/ai-edits/character-boundaries.d.ts +43 -0
  8. package/dist/ai-edits/character-boundaries.js +95 -0
  9. package/dist/ai-edits/clean-text.d.ts +33 -3
  10. package/dist/ai-edits/clean-text.js +52 -6
  11. package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
  12. package/dist/ai-edits/comment-lifecycle.js +52 -0
  13. package/dist/ai-edits/headless.d.ts +71 -7
  14. package/dist/ai-edits/headless.js +381 -41
  15. package/dist/ai-edits/index.d.ts +2 -1
  16. package/dist/ai-edits/index.js +2 -1
  17. package/dist/ai-edits/minimal-replacement.d.ts +21 -6
  18. package/dist/ai-edits/minimal-replacement.js +134 -15
  19. package/dist/ai-edits/newListNumbering.d.ts +23 -0
  20. package/dist/ai-edits/newListNumbering.js +94 -0
  21. package/dist/ai-edits/note-references.d.ts +96 -0
  22. package/dist/ai-edits/note-references.js +139 -0
  23. package/dist/ai-edits/pending-suggestions.d.ts +79 -0
  24. package/dist/ai-edits/pending-suggestions.js +306 -0
  25. package/dist/ai-edits/read.d.ts +1 -1
  26. package/dist/ai-edits/read.js +56 -43
  27. package/dist/ai-edits/result-validation.d.ts +29 -0
  28. package/dist/ai-edits/result-validation.js +164 -0
  29. package/dist/ai-edits/snapshot.d.ts +36 -3
  30. package/dist/ai-edits/snapshot.js +177 -23
  31. package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
  32. package/dist/ai-edits/table-cell-mutations.js +4 -1
  33. package/dist/ai-edits/table-geometry.d.ts +1 -1
  34. package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
  35. package/dist/ai-edits/table-row-column-mutations.js +61 -26
  36. package/dist/ai-edits/types.d.ts +155 -16
  37. package/dist/compare/compare.d.ts +1 -1
  38. package/dist/compare/compare.js +65 -16
  39. package/dist/compare/content-alignment.js +13 -2
  40. package/dist/compare/content-types.d.ts +7 -0
  41. package/dist/compare/content.d.ts +4 -1
  42. package/dist/compare/content.js +6 -1
  43. package/dist/compare/formatting.js +2 -1
  44. package/dist/compare/inline-atoms.d.ts +21 -4
  45. package/dist/compare/inline-atoms.js +138 -16
  46. package/dist/compare/plan.js +10 -5
  47. package/dist/compare/scenario.js +14 -3
  48. package/dist/compare/section-boundary-properties.js +1 -1
  49. package/dist/compare/types.d.ts +4 -2
  50. package/dist/compare/types.js +2 -1
  51. package/dist/compare/verification.d.ts +1 -1
  52. package/dist/compare/verification.js +15 -3
  53. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  54. package/dist/controller/committedLayoutDiff.js +71 -0
  55. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  56. package/dist/controller/folioEditor.d.ts +1 -1
  57. package/dist/controller/fontReadiness.d.ts +70 -3
  58. package/dist/controller/fontReadiness.js +100 -10
  59. package/dist/controller/headerFooterEditorManager.js +6 -1
  60. package/dist/controller/layoutPipeline.d.ts +12 -10
  61. package/dist/controller/layoutPipeline.js +62 -9
  62. package/dist/controller/layoutRunOptions.d.ts +24 -0
  63. package/dist/controller/layoutRunOptions.js +17 -0
  64. package/dist/controller/layoutScheduler.d.ts +19 -14
  65. package/dist/controller/layoutScheduler.js +15 -12
  66. package/dist/controller/layoutSession.d.ts +26 -2
  67. package/dist/controller/layoutSession.js +1 -1
  68. package/dist/controller/noteEditorManager.js +8 -3
  69. package/dist/document-operations.d.ts +11 -2
  70. package/dist/document-operations.js +230 -21
  71. package/dist/docx/altChunk.d.ts +8 -0
  72. package/dist/docx/altChunk.js +17 -0
  73. package/dist/docx/attributeRemainder.d.ts +1 -1
  74. package/dist/docx/blockContentParser.d.ts +64 -1
  75. package/dist/docx/blockContentParser.js +17 -4
  76. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  77. package/dist/docx/blockCustomXmlShell.js +14 -0
  78. package/dist/docx/blockPlainText.js +7 -0
  79. package/dist/docx/commentAnchorIndex.js +1 -1
  80. package/dist/docx/commentRangeIntegrity.js +8 -3
  81. package/dist/docx/commentReferenceNormalization.js +1 -1
  82. package/dist/docx/commentReplyMarkers.js +20 -1
  83. package/dist/docx/compatibility.d.ts +1 -1
  84. package/dist/docx/compatibility.js +34 -13
  85. package/dist/docx/containerChildren.gen.d.ts +1 -1
  86. package/dist/docx/containerChildren.gen.js +1 -0
  87. package/dist/docx/documentParser.js +12 -12
  88. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  89. package/dist/docx/drawingIdNormalization.js +6 -1
  90. package/dist/docx/ensureParaIds.js +119 -97
  91. package/dist/docx/footnoteParser.js +8 -4
  92. package/dist/docx/headerFooterParser.js +3 -3
  93. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  94. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  95. package/dist/docx/hyperlinkParser.d.ts +1 -1
  96. package/dist/docx/index.d.ts +1 -1
  97. package/dist/docx/listNumberingInstances.d.ts +59 -0
  98. package/dist/docx/listNumberingInstances.js +249 -0
  99. package/dist/docx/normalizeBaseDirection.js +2 -1
  100. package/dist/docx/opaqueCarrier.d.ts +7 -0
  101. package/dist/docx/opaqueCarrier.js +38 -0
  102. package/dist/docx/packageParts.js +4 -1
  103. package/dist/docx/paragraphParser.js +91 -24
  104. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  105. package/dist/docx/paragraphPropertySource.js +7 -1
  106. package/dist/docx/paragraphTraversal.js +1 -0
  107. package/dist/docx/parseWarningMessage.js +4 -1
  108. package/dist/docx/parser.js +173 -20
  109. package/dist/docx/rezip.js +27 -5
  110. package/dist/docx/sdtProperties.js +1 -1
  111. package/dist/docx/sdtPropertiesPatch.js +3 -0
  112. package/dist/docx/selectiveSave.js +16 -9
  113. package/dist/docx/selectiveXmlPatch.js +106 -37
  114. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  115. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  116. package/dist/docx/serializer/documentSerializer.js +2 -0
  117. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  118. package/dist/docx/serializer/noteSerializer.js +3 -1
  119. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  120. package/dist/docx/serializer/tableSerializer.js +25 -38
  121. package/dist/docx/server/createBilingualDocument.js +3 -3
  122. package/dist/docx/tableParser.d.ts +157 -1
  123. package/dist/docx/tableParser.js +89 -10
  124. package/dist/docx/textBoxParser.js +1 -1
  125. package/dist/docx/unzip.d.ts +2 -1
  126. package/dist/docx/unzip.js +1 -1
  127. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  128. package/dist/docx/wordprocessingPrefixes.js +286 -0
  129. package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
  130. package/dist/i18n/messages/catalogs.gen.js +153 -17
  131. package/dist/i18n/messages/messages.gen.d.ts +8 -0
  132. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  133. package/dist/internal/compare/inline-presentation.js +11 -10
  134. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  135. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  136. package/dist/internal/indexedPositionMap.d.ts +11 -0
  137. package/dist/internal/indexedPositionMap.js +42 -0
  138. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  139. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  140. package/dist/internal/revisionResolutionEdits.js +19 -0
  141. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  142. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  143. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  144. package/dist/internal/revisionResolutionStep.js +282 -0
  145. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  146. package/dist/internal/revisionResolutionTracking.js +124 -0
  147. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  148. package/dist/internal/wholeStoryRevisionResolution.js +530 -0
  149. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  150. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  151. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  152. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  153. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  154. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  155. package/dist/layout-engine/measure/cache.d.ts +18 -1
  156. package/dist/layout-engine/measure/cache.js +31 -1
  157. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  158. package/dist/layout-engine/measure/measureContainer.js +7 -47
  159. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  160. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  161. package/dist/layout-engine/measure/measureWorker.js +2 -3
  162. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  163. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  164. package/dist/layout-engine/types.d.ts +6 -0
  165. package/dist/layout-painter/renderPage.js +1 -0
  166. package/dist/layout-painter/renderParagraph.js +1 -1
  167. package/dist/managers/ContextMenuManager.js +5 -2
  168. package/dist/managers/types.d.ts +3 -0
  169. package/dist/markdown/fromMarkdown.js +2 -1
  170. package/dist/markdown/internals.d.ts +11 -1
  171. package/dist/markdown/internals.js +24 -3
  172. package/dist/markdown/renderBlock.js +39 -9
  173. package/dist/markdown/renderParagraph.d.ts +13 -1
  174. package/dist/markdown/renderParagraph.js +85 -38
  175. package/dist/markdown/renderRuns.js +3 -10
  176. package/dist/markdown/renderTable.js +24 -12
  177. package/dist/markdown/trailers.js +2 -2
  178. package/dist/markdown/types.d.ts +29 -7
  179. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  180. package/dist/paged-layout/incrementalMeasure.js +1 -9
  181. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  182. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  183. package/dist/panel-layout.d.ts +83 -0
  184. package/dist/panel-layout.js +96 -0
  185. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  186. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  187. package/dist/prosemirror/attrs/index.d.ts +6 -2
  188. package/dist/prosemirror/attrs/index.js +172 -1
  189. package/dist/prosemirror/commands/comments.d.ts +7 -8
  190. package/dist/prosemirror/commands/comments.js +241 -207
  191. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  192. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  193. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  194. package/dist/prosemirror/commands/index.d.ts +2 -2
  195. package/dist/prosemirror/commands/index.js +2 -2
  196. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  197. package/dist/prosemirror/commands/paragraph.js +9 -1
  198. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  199. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  200. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  201. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  202. package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
  203. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  204. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  205. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  206. package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
  207. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  208. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
  209. package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
  210. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  211. package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
  212. package/dist/prosemirror/conversion/toProseDoc.js +39 -4
  213. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  214. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  215. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  216. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  217. package/dist/prosemirror/extensions/StarterKit.js +3 -1
  218. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  219. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
  220. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
  221. package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
  222. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  223. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  224. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  225. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  226. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  227. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  228. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  229. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
  230. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
  231. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  232. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  233. package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
  234. package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
  235. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
  236. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
  237. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  238. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  239. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  240. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  241. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  242. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  243. package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
  244. package/dist/prosemirror/extensions/types.d.ts +4 -1
  245. package/dist/prosemirror/findReplaceSelection.js +1 -1
  246. package/dist/prosemirror/index.d.ts +3 -2
  247. package/dist/prosemirror/index.js +3 -2
  248. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  249. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  250. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  251. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  252. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  253. package/dist/prosemirror/listInstanceReferences.js +72 -0
  254. package/dist/prosemirror/listLabels.d.ts +24 -0
  255. package/dist/prosemirror/listLabels.js +111 -0
  256. package/dist/prosemirror/listNumbering.d.ts +110 -0
  257. package/dist/prosemirror/listNumbering.js +345 -0
  258. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  259. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  260. package/dist/prosemirror/markupViewProjection.d.ts +95 -0
  261. package/dist/prosemirror/markupViewProjection.js +83 -0
  262. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  263. package/dist/prosemirror/numberingAttr.js +9 -1
  264. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  265. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  266. package/dist/prosemirror/paragraphIndentation.js +29 -7
  267. package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
  268. package/dist/prosemirror/paragraphMarkJoin.js +24 -0
  269. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  270. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  271. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  272. package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
  273. package/dist/prosemirror/plugins/documentStyles.js +14 -1
  274. package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
  275. package/dist/prosemirror/plugins/suggestionMode.js +116 -52
  276. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  277. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  278. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  279. package/dist/prosemirror/positionSweep.d.ts +24 -0
  280. package/dist/prosemirror/positionSweep.js +222 -0
  281. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
  282. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  283. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  284. package/dist/prosemirror/replacedAnnotations.js +170 -1
  285. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  286. package/dist/prosemirror/schema/index.d.ts +2 -2
  287. package/dist/prosemirror/schema/index.js +3 -0
  288. package/dist/prosemirror/schema/nodes.d.ts +42 -11
  289. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  290. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  291. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  292. package/dist/prosemirror/storyListNumbering.js +102 -0
  293. package/dist/prosemirror/tableGridMutation.d.ts +48 -2
  294. package/dist/prosemirror/tableGridMutation.js +106 -1
  295. package/dist/prosemirror/validation.js +5 -1
  296. package/dist/redline.js +93 -9
  297. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  298. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  299. package/dist/utils/findReplace.js +2 -2
  300. package/dist/utils/mergeDocumentContent.js +1 -1
  301. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  302. package/dist/utils/noteReferenceLabels.js +32 -0
  303. package/dist/utils/replaceText.js +1 -1
  304. package/dist/version-comparison.d.ts +1 -0
  305. package/dist/version-comparison.js +1 -0
  306. package/package.json +3 -2
  307. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -0,0 +1,79 @@
1
+ import { FolioAIEditAppliedOperation, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditSnapshot } from "./types.js";
2
+ import { FolioDocumentStoryHandle } from "./headless.js";
3
+ import { EditorState } from "prosemirror-state";
4
+ //#region src/ai-edits/pending-suggestions.d.ts
5
+ declare const FOLIO_PENDING_SUGGESTION_VERSION: 1;
6
+ /** A host-owned, JSON-serializable proposal. It is never written into the DOCX. */
7
+ type FolioPendingSuggestionRecord = {
8
+ version: typeof FOLIO_PENDING_SUGGESTION_VERSION;
9
+ suggestionId: string;
10
+ operation: FolioAIEditOperation;
11
+ story: FolioDocumentStoryHandle;
12
+ anchor: {
13
+ blockId: string;
14
+ paraId: string | null;
15
+ originalTextHash: string;
16
+ selectedTextHash?: string;
17
+ startOffset?: number;
18
+ endOffset?: number;
19
+ };
20
+ author: string;
21
+ provenance: "suggested";
22
+ sourceDocumentFingerprint: string;
23
+ commentId?: number;
24
+ };
25
+ type FolioPendingSuggestionStaleReason = "missingAnchor" | "ambiguousAnchor" | "textChanged" | "unsupportedVersion" | "documentChanged" | "invalidRecord" | "applyFailed";
26
+ type FolioPendingSuggestionLoadResult = {
27
+ status: "restaged";
28
+ suggestionId: string;
29
+ } | {
30
+ status: "stale";
31
+ suggestionId: string | null;
32
+ reason: FolioPendingSuggestionStaleReason;
33
+ };
34
+ type RecordAppliedOptions = {
35
+ snapshot: FolioAIEditSnapshot;
36
+ story: FolioDocumentStoryHandle;
37
+ operations: readonly FolioAIEditOperation[];
38
+ applied: readonly FolioAIEditAppliedOperation[];
39
+ author: string;
40
+ activeSuggestionIds: ReadonlySet<string>;
41
+ };
42
+ type ReplaceRestagedStoryOptions = {
43
+ story: FolioDocumentStoryHandle;
44
+ restaged: readonly {
45
+ snapshot: FolioAIEditSnapshot;
46
+ operation: FolioAIEditOperation;
47
+ applied: FolioAIEditAppliedOperation;
48
+ author: string;
49
+ }[];
50
+ activeSuggestionIds: ReadonlySet<string>;
51
+ };
52
+ type LoadPendingSuggestionsOptions = {
53
+ records: readonly unknown[];
54
+ snapshotForStory: (story: FolioDocumentStoryHandle) => FolioAIEditSnapshot | null;
55
+ sourceSnapshotForStory: (story: FolioDocumentStoryHandle, commentIds: ReadonlySet<number>) => FolioAIEditSnapshot | null;
56
+ apply: (record: FolioPendingSuggestionRecord, snapshot: FolioAIEditSnapshot) => FolioAIEditApplyResult;
57
+ activeSuggestionIds: () => ReadonlySet<string>;
58
+ };
59
+ type ExportPendingSuggestionsOptions = {
60
+ activeSuggestionIds: ReadonlySet<string>;
61
+ snapshotForStory: (story: FolioDocumentStoryHandle, commentIds: ReadonlySet<number>) => FolioAIEditSnapshot | null;
62
+ };
63
+ /** Drop only comments attached to proposals that have not been accepted. */
64
+ declare const withoutPendingSuggestionCommentMarks: (state: EditorState, commentIds: ReadonlySet<number>) => EditorState;
65
+ /** The saved package's story, with all pending proposals removed. */
66
+ declare const createPendingSuggestionSourceSnapshot: (state: EditorState, commentIds: ReadonlySet<number>) => FolioAIEditSnapshot;
67
+ /** Tracks only operations that successfully staged in `suggested` mode. */
68
+ declare class FolioPendingSuggestionRegistry {
69
+ private readonly records;
70
+ private readonly sourceFingerprints;
71
+ clear(): void;
72
+ recordApplied({ snapshot, story, operations, applied, author, activeSuggestionIds }: RecordAppliedOptions): void;
73
+ /** Rebase records after ordinary revisions change their source story. */
74
+ replaceRestagedStory({ story, restaged, activeSuggestionIds }: ReplaceRestagedStoryOptions): void;
75
+ exportPendingSuggestions({ activeSuggestionIds, snapshotForStory }: ExportPendingSuggestionsOptions): FolioPendingSuggestionRecord[];
76
+ loadPendingSuggestions({ records, snapshotForStory, sourceSnapshotForStory, apply, activeSuggestionIds }: LoadPendingSuggestionsOptions): FolioPendingSuggestionLoadResult[];
77
+ }
78
+ //#endregion
79
+ export { FOLIO_PENDING_SUGGESTION_VERSION, FolioPendingSuggestionLoadResult, FolioPendingSuggestionRecord, FolioPendingSuggestionRegistry, FolioPendingSuggestionStaleReason, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks };
@@ -0,0 +1,306 @@
1
+ import { parseFolioDocumentOperationBatch } from "../document-operations.js";
2
+ import { rejectAllSuggestions } from "../prosemirror/commands/comments.js";
3
+ import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyles.js";
4
+ import { getFolioParaIdFromBlockId } from "../types/block-id.js";
5
+ import { canonicalJson } from "../utils/canonicalJson.js";
6
+ import { createFolioAIEditSnapshotWithStyleResolver, hashFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
7
+ //#region src/ai-edits/pending-suggestions.ts
8
+ const FOLIO_PENDING_SUGGESTION_VERSION = 1;
9
+ /** Drop only comments attached to proposals that have not been accepted. */
10
+ const withoutPendingSuggestionCommentMarks = (state, commentIds) => {
11
+ if (commentIds.size === 0) return state;
12
+ let transaction = state.tr;
13
+ state.doc.descendants((node, position) => {
14
+ for (const mark of node.marks) if (mark.type.name === "comment" && commentIds.has(mark.attrs["commentId"])) transaction = transaction.removeMark(position, position + node.nodeSize, mark);
15
+ });
16
+ return transaction.docChanged ? state.apply(transaction) : state;
17
+ };
18
+ /** The saved package's story, with all pending proposals removed. */
19
+ const createPendingSuggestionSourceSnapshot = (state, commentIds) => {
20
+ let source = state;
21
+ rejectAllSuggestions()(state, (transaction) => {
22
+ source = state.apply(transaction);
23
+ });
24
+ source = withoutPendingSuggestionCommentMarks(source, commentIds);
25
+ return createFolioAIEditSnapshotWithStyleResolver(source.doc, getDocumentStyleResolver(source));
26
+ };
27
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
28
+ const operationBlockId = (operation) => {
29
+ switch (operation.type) {
30
+ case "replaceRange":
31
+ case "commentOnRange":
32
+ case "formatRange": return operation.range.blockId;
33
+ default: return operation.blockId;
34
+ }
35
+ };
36
+ const storyFromUnknown = (value) => {
37
+ if (!isObject(value)) return null;
38
+ switch (value["type"]) {
39
+ case "main": return { type: "main" };
40
+ case "header":
41
+ case "footer": return typeof value["relationshipId"] === "string" ? {
42
+ type: value["type"],
43
+ relationshipId: value["relationshipId"]
44
+ } : null;
45
+ case "footnote":
46
+ case "endnote": return typeof value["noteId"] === "number" && Number.isSafeInteger(value["noteId"]) ? {
47
+ type: value["type"],
48
+ noteId: value["noteId"]
49
+ } : null;
50
+ default: return null;
51
+ }
52
+ };
53
+ const fingerprintOf = (snapshot, story) => hashFolioAIBlockText(canonicalJson([story, snapshot.blocks.map((block) => [block, snapshot.anchors[block.id]?.structuralBoundaryHash])]));
54
+ const paraIdCountsOf = (snapshot) => {
55
+ const counts = /* @__PURE__ */ new Map();
56
+ sourceDocumentOf(snapshot).descendants((node) => {
57
+ const paraId = node.attrs["paraId"];
58
+ if (node.type.name === "paragraph" && typeof paraId === "string") counts.set(paraId, (counts.get(paraId) ?? 0) + 1);
59
+ });
60
+ return counts;
61
+ };
62
+ const recordKey = (record) => canonicalJson([
63
+ record.story,
64
+ record.suggestionId,
65
+ record.operation.id
66
+ ]);
67
+ const parseRecord = (value) => {
68
+ if (!isObject(value) || !isObject(value["anchor"])) return null;
69
+ const story = storyFromUnknown(value["story"]);
70
+ const anchor = value["anchor"];
71
+ if (story === null || typeof value["suggestionId"] !== "string" || typeof value["author"] !== "string" || value["provenance"] !== "suggested" || typeof value["sourceDocumentFingerprint"] !== "string" || value["commentId"] !== void 0 && (typeof value["commentId"] !== "number" || !Number.isSafeInteger(value["commentId"])) || typeof anchor["blockId"] !== "string" || anchor["paraId"] !== null && typeof anchor["paraId"] !== "string" || typeof anchor["originalTextHash"] !== "string") return null;
72
+ let operation;
73
+ try {
74
+ const parsed = parseFolioDocumentOperationBatch({
75
+ version: 1,
76
+ operations: [value["operation"]],
77
+ mode: "suggested"
78
+ }).operations.at(0);
79
+ if (!parsed) return null;
80
+ operation = parsed;
81
+ } catch {
82
+ return null;
83
+ }
84
+ if (operationBlockId(operation) !== anchor["blockId"] || (operation.suggestionId ?? operation.id) !== value["suggestionId"] || getFolioParaIdFromBlockId(anchor["blockId"]) !== anchor["paraId"]) return null;
85
+ if (operation.type === "replaceRange" || operation.type === "formatRange") {
86
+ if (anchor["selectedTextHash"] !== operation.range.selectedTextHash || anchor["startOffset"] !== operation.range.startOffset || anchor["endOffset"] !== operation.range.endOffset) return null;
87
+ }
88
+ return {
89
+ version: 1,
90
+ suggestionId: value["suggestionId"],
91
+ operation,
92
+ story,
93
+ anchor: {
94
+ blockId: anchor["blockId"],
95
+ paraId: anchor["paraId"],
96
+ originalTextHash: anchor["originalTextHash"],
97
+ ...typeof anchor["selectedTextHash"] === "string" && { selectedTextHash: anchor["selectedTextHash"] },
98
+ ...typeof anchor["startOffset"] === "number" && { startOffset: anchor["startOffset"] },
99
+ ...typeof anchor["endOffset"] === "number" && { endOffset: anchor["endOffset"] }
100
+ },
101
+ author: value["author"],
102
+ provenance: "suggested",
103
+ sourceDocumentFingerprint: value["sourceDocumentFingerprint"],
104
+ ...typeof value["commentId"] === "number" && Number.isSafeInteger(value["commentId"]) && { commentId: value["commentId"] }
105
+ };
106
+ };
107
+ /** Tracks only operations that successfully staged in `suggested` mode. */
108
+ var FolioPendingSuggestionRegistry = class {
109
+ records = /* @__PURE__ */ new Map();
110
+ sourceFingerprints = /* @__PURE__ */ new Map();
111
+ clear() {
112
+ this.records.clear();
113
+ this.sourceFingerprints.clear();
114
+ }
115
+ recordApplied({ snapshot, story, operations, applied, author, activeSuggestionIds }) {
116
+ if (applied.length === 0) return;
117
+ const storyKey = canonicalJson(story);
118
+ for (const [key, record] of this.records) if (!activeSuggestionIds.has(record.suggestionId)) this.records.delete(key);
119
+ if (![...this.records.values()].some((record) => canonicalJson(record.story) === storyKey)) this.sourceFingerprints.delete(storyKey);
120
+ const fingerprint = this.sourceFingerprints.get(storyKey) ?? fingerprintOf(snapshot, story);
121
+ this.sourceFingerprints.set(storyKey, fingerprint);
122
+ const appliedById = new Map(applied.map((entry) => [entry.id, entry]));
123
+ for (const operation of operations) {
124
+ const outcome = appliedById.get(operation.id);
125
+ if (!outcome?.suggestionId) continue;
126
+ const blockId = operationBlockId(operation);
127
+ const anchor = snapshot.anchors[blockId];
128
+ if (!anchor) continue;
129
+ const range = operation.type === "replaceRange" || operation.type === "formatRange" ? operation.range : null;
130
+ const record = {
131
+ version: 1,
132
+ suggestionId: outcome.suggestionId,
133
+ operation: structuredClone(operation),
134
+ story: structuredClone(story),
135
+ anchor: {
136
+ blockId,
137
+ paraId: getFolioParaIdFromBlockId(blockId),
138
+ originalTextHash: anchor.textHash,
139
+ ...range !== null && {
140
+ selectedTextHash: range.selectedTextHash,
141
+ startOffset: range.startOffset,
142
+ endOffset: range.endOffset
143
+ }
144
+ },
145
+ author,
146
+ provenance: "suggested",
147
+ sourceDocumentFingerprint: fingerprint,
148
+ ...outcome.commentId !== void 0 && { commentId: outcome.commentId }
149
+ };
150
+ this.records.set(recordKey(record), record);
151
+ }
152
+ }
153
+ /** Rebase records after ordinary revisions change their source story. */
154
+ replaceRestagedStory({ story, restaged, activeSuggestionIds }) {
155
+ if (restaged.length === 0) return;
156
+ const storyKey = canonicalJson(story);
157
+ for (const [key, record] of this.records) if (canonicalJson(record.story) === storyKey) this.records.delete(key);
158
+ const first = restaged.at(0);
159
+ if (!first) return;
160
+ this.sourceFingerprints.set(storyKey, fingerprintOf(first.snapshot, story));
161
+ for (const { snapshot, operation, applied, author } of restaged) this.recordApplied({
162
+ snapshot,
163
+ story,
164
+ operations: [operation],
165
+ applied: [applied],
166
+ author,
167
+ activeSuggestionIds
168
+ });
169
+ }
170
+ exportPendingSuggestions({ activeSuggestionIds, snapshotForStory }) {
171
+ const pending = [];
172
+ const fingerprints = /* @__PURE__ */ new Map();
173
+ const commentIdsByStory = /* @__PURE__ */ new Map();
174
+ for (const record of this.records.values()) {
175
+ if (!activeSuggestionIds.has(record.suggestionId) || record.commentId === void 0) continue;
176
+ const storyKey = canonicalJson(record.story);
177
+ const commentIds = commentIdsByStory.get(storyKey) ?? /* @__PURE__ */ new Set();
178
+ commentIds.add(record.commentId);
179
+ commentIdsByStory.set(storyKey, commentIds);
180
+ }
181
+ for (const [key, record] of this.records) if (activeSuggestionIds.has(record.suggestionId)) {
182
+ const storyKey = canonicalJson(record.story);
183
+ let fingerprint = fingerprints.get(storyKey);
184
+ if (fingerprint === void 0) {
185
+ const snapshot = snapshotForStory(record.story, commentIdsByStory.get(storyKey) ?? /* @__PURE__ */ new Set());
186
+ if (!snapshot) continue;
187
+ fingerprint = fingerprintOf(snapshot, record.story);
188
+ fingerprints.set(storyKey, fingerprint);
189
+ }
190
+ const current = {
191
+ ...record,
192
+ sourceDocumentFingerprint: fingerprint
193
+ };
194
+ this.records.set(key, current);
195
+ pending.push(structuredClone(current));
196
+ } else this.records.delete(key);
197
+ for (const [storyKey, fingerprint] of fingerprints) this.sourceFingerprints.set(storyKey, fingerprint);
198
+ if (pending.length === 0) this.sourceFingerprints.clear();
199
+ return pending;
200
+ }
201
+ loadPendingSuggestions({ records, snapshotForStory, sourceSnapshotForStory, apply, activeSuggestionIds }) {
202
+ const baselines = /* @__PURE__ */ new Map();
203
+ const changedStories = /* @__PURE__ */ new Set();
204
+ return records.map((value) => {
205
+ const suggestionId = isObject(value) && typeof value["suggestionId"] === "string" ? value["suggestionId"] : null;
206
+ if (!isObject(value) || value["version"] !== 1) return {
207
+ status: "stale",
208
+ suggestionId,
209
+ reason: "unsupportedVersion"
210
+ };
211
+ const record = parseRecord(value);
212
+ if (!record) return {
213
+ status: "stale",
214
+ suggestionId,
215
+ reason: "invalidRecord"
216
+ };
217
+ const key = recordKey(record);
218
+ const existing = this.records.get(key);
219
+ if (existing && activeSuggestionIds().has(record.suggestionId)) {
220
+ const { commentId: _existingCommentId, ...existingProposal } = existing;
221
+ const { commentId: _loadedCommentId, ...loadedProposal } = record;
222
+ return canonicalJson(existingProposal) === canonicalJson(loadedProposal) ? {
223
+ status: "restaged",
224
+ suggestionId: record.suggestionId
225
+ } : {
226
+ status: "stale",
227
+ suggestionId,
228
+ reason: "invalidRecord"
229
+ };
230
+ }
231
+ const storyKey = canonicalJson(record.story);
232
+ if (!baselines.has(storyKey)) {
233
+ const snapshot = snapshotForStory(record.story);
234
+ const active = activeSuggestionIds();
235
+ const commentIds = /* @__PURE__ */ new Set();
236
+ for (const existingRecord of this.records.values()) if (canonicalJson(existingRecord.story) === storyKey && active.has(existingRecord.suggestionId) && existingRecord.commentId !== void 0) commentIds.add(existingRecord.commentId);
237
+ const source = sourceSnapshotForStory(record.story, commentIds);
238
+ baselines.set(storyKey, snapshot && source ? {
239
+ snapshot,
240
+ fingerprint: fingerprintOf(source, record.story)
241
+ } : null);
242
+ }
243
+ const baseline = baselines.get(storyKey);
244
+ if (!baseline || record.anchor.paraId === null) return {
245
+ status: "stale",
246
+ suggestionId,
247
+ reason: "missingAnchor"
248
+ };
249
+ const snapshot = changedStories.has(storyKey) ? snapshotForStory(record.story) : baseline.snapshot;
250
+ if (!snapshot) return {
251
+ status: "stale",
252
+ suggestionId,
253
+ reason: "missingAnchor"
254
+ };
255
+ const multiplicity = paraIdCountsOf(snapshot).get(record.anchor.paraId) ?? 0;
256
+ if (multiplicity === 0 || !snapshot.anchors[record.anchor.paraId]) return {
257
+ status: "stale",
258
+ suggestionId,
259
+ reason: "missingAnchor"
260
+ };
261
+ if (multiplicity > 1) return {
262
+ status: "stale",
263
+ suggestionId,
264
+ reason: "ambiguousAnchor"
265
+ };
266
+ const anchor = snapshot.anchors[record.anchor.paraId];
267
+ if (!anchor || anchor.textHash !== record.anchor.originalTextHash) return {
268
+ status: "stale",
269
+ suggestionId,
270
+ reason: "textChanged"
271
+ };
272
+ const { startOffset, endOffset, selectedTextHash } = record.anchor;
273
+ if (startOffset !== void 0 && endOffset !== void 0 && selectedTextHash !== void 0 && hashFolioAIBlockText(anchor.text.slice(startOffset, endOffset)) !== selectedTextHash) return {
274
+ status: "stale",
275
+ suggestionId,
276
+ reason: "textChanged"
277
+ };
278
+ if (baseline.fingerprint !== record.sourceDocumentFingerprint) return {
279
+ status: "stale",
280
+ suggestionId,
281
+ reason: "documentChanged"
282
+ };
283
+ const applied = apply(record, snapshot).applied.find((entry) => entry.id === record.operation.id);
284
+ if (!applied) return {
285
+ status: "stale",
286
+ suggestionId,
287
+ reason: "applyFailed"
288
+ };
289
+ this.sourceFingerprints.set(storyKey, record.sourceDocumentFingerprint);
290
+ if (!this.records.has(key)) {
291
+ const { commentId: _oldCommentId, ...rest } = record;
292
+ this.records.set(key, structuredClone({
293
+ ...rest,
294
+ ...applied.commentId !== void 0 && { commentId: applied.commentId }
295
+ }));
296
+ }
297
+ changedStories.add(storyKey);
298
+ return {
299
+ status: "restaged",
300
+ suggestionId: record.suggestionId
301
+ };
302
+ });
303
+ }
304
+ };
305
+ //#endregion
306
+ export { FOLIO_PENDING_SUGGESTION_VERSION, FolioPendingSuggestionRegistry, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks };
@@ -78,7 +78,7 @@ declare const getTrackedChangeStatsFromDoc: (doc: Node) => {
78
78
  /**
79
79
  * The comment anchors present in the body, read from the `comment` mark the
80
80
  * editor renders. Each entry carries the anchored text and containing block id;
81
- * runs of one comment id within the body fold into a single anchor.
81
+ * ranges for one comment id within the body fold into a single anchor.
82
82
  */
83
83
  declare const getCommentAnchorsFromDoc: (doc: Node) => FolioCommentAnchor[];
84
84
  //#endregion
@@ -1,8 +1,9 @@
1
- import { expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
1
+ import { expectRunPropertyChangeMarkAttrs, expectTrackedChangeMarkAttrs } from "../prosemirror/attrs/index.js";
2
2
  import { getFolioNodeRevisionCarriers } from "../prosemirror/revisionCarriers.js";
3
3
  import { expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition } from "../prosemirror/runFormattingInlineCarriers.js";
4
4
  import { getTableCellMergeChange } from "../prosemirror/tableCellMergeRevision.js";
5
- import { createFolioAIEditSnapshot, sourceDocumentOf } from "./snapshot.js";
5
+ import { readerTextOf } from "./note-references.js";
6
+ import { collectNoteReferenceLabels, createFolioAIEditSnapshot, sourceDocumentOf } from "./snapshot.js";
6
7
  //#region src/ai-edits/read.ts
7
8
  /**
8
9
  * Runtime census of every tracked-change discriminator the reviewer exposes.
@@ -50,12 +51,6 @@ const firstBlockIdWithin = ({ node, nodePos, blockStarts }) => {
50
51
  });
51
52
  return blockId;
52
53
  };
53
- /**
54
- * Whether a run's revision inside a marked row is that row's own. Folio writes
55
- * both halves under one revision id; Word mints a fresh `w:id` per element, so
56
- * an author + timestamp match counts too. A third party's edit inside the row
57
- * matches neither and keeps its own entry.
58
- */
59
54
  const belongsToRowRevision = (scope, revision) => revision.id === scope.change.id || revision.author === scope.change.author && revision.date === scope.change.date;
60
55
  /** Shared revision interpreter; a null block map omits unrelated block-id projection. */
61
56
  const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
@@ -65,6 +60,7 @@ const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
65
60
  const structuralChangeCellPositions = /* @__PURE__ */ new Map();
66
61
  const rowRevisionScopes = [];
67
62
  const derivedFormattingRepresentationPositions = /* @__PURE__ */ new Set();
63
+ const noteReferences = collectNoteReferenceLabels(doc);
68
64
  let currentBlockId = null;
69
65
  doc.descendants((node, pos) => {
70
66
  while (pos >= (rowRevisionScopes.at(-1)?.end ?? Number.POSITIVE_INFINITY)) rowRevisionScopes.pop();
@@ -207,7 +203,7 @@ const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
207
203
  }
208
204
  }
209
205
  }
210
- const text = node.text ?? "";
206
+ const text = readerTextOf(node, noteReferences);
211
207
  for (const mark of node.marks) {
212
208
  if (mark.type.name === "runPropertyChange") continue;
213
209
  if (typeof mark.attrs["revisionId"] !== "number") continue;
@@ -215,28 +211,39 @@ const getTrackedChangesFromProjectedDoc = (doc, blockStarts) => {
215
211
  if (mark.type === insertionType) kind = "insertion";
216
212
  else if (mark.type === deletionType) kind = "deletion";
217
213
  else continue;
218
- const revisionId = mark.attrs["revisionId"];
219
214
  const author = mark.attrs["author"];
220
215
  const date = mark.attrs["date"];
221
- const revision = {
222
- id: revisionId,
223
- author: typeof author === "string" ? author : "",
224
- date: typeof date === "string" ? date : null
225
- };
226
- const rowScope = rowRevisionScopes.at(-1);
227
- if (rowScope?.kind === kind && belongsToRowRevision(rowScope, revision)) continue;
228
- const key = `${currentBlockId ?? ""}:${kind}:${revisionId}`;
229
- const existing = grouped.get(key);
230
- if (existing) {
231
- existing.text += text;
232
- continue;
216
+ const layers = [...(expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors ?? []).map((ancestor) => ({
217
+ kind: ancestor.type === "insertion" || ancestor.type === "moveTo" ? "insertion" : "deletion",
218
+ revision: {
219
+ id: ancestor.revisionId,
220
+ author: ancestor.author,
221
+ date: ancestor.date ?? null
222
+ }
223
+ })), {
224
+ kind,
225
+ revision: {
226
+ id: mark.attrs["revisionId"],
227
+ author: typeof author === "string" ? author : "",
228
+ date: typeof date === "string" ? date : null
229
+ }
230
+ }];
231
+ for (const { kind: layerKind, revision } of layers) {
232
+ const rowScope = rowRevisionScopes.at(-1);
233
+ if (rowScope?.kind === layerKind && belongsToRowRevision(rowScope, revision)) continue;
234
+ const key = `${currentBlockId ?? ""}:${layerKind}:${revision.id}`;
235
+ const existing = grouped.get(key);
236
+ if (existing) {
237
+ existing.text += text;
238
+ continue;
239
+ }
240
+ grouped.set(key, {
241
+ ...revision,
242
+ type: layerKind,
243
+ text,
244
+ blockId: currentBlockId
245
+ });
233
246
  }
234
- grouped.set(key, {
235
- ...revision,
236
- type: kind,
237
- text,
238
- blockId: currentBlockId
239
- });
240
247
  }
241
248
  });
242
249
  return [...grouped.values()];
@@ -266,38 +273,44 @@ const getTrackedChangeStatsFromDoc = (doc) => {
266
273
  /**
267
274
  * The comment anchors present in the body, read from the `comment` mark the
268
275
  * editor renders. Each entry carries the anchored text and containing block id;
269
- * runs of one comment id within the body fold into a single anchor.
276
+ * ranges for one comment id within the body fold into a single anchor.
270
277
  */
271
278
  const getCommentAnchorsFromDoc = (doc) => {
272
279
  const commentType = doc.type.schema.marks["comment"];
273
280
  if (!commentType) return [];
274
281
  const blockStarts = blockStartIdsFromDoc(doc);
275
- const anchors = /* @__PURE__ */ new Map();
282
+ const ranges = /* @__PURE__ */ new Map();
283
+ const noteReferences = collectNoteReferenceLabels(doc);
276
284
  let currentBlockId = null;
285
+ let bodyText = "";
277
286
  doc.descendants((node, pos) => {
278
287
  if (node.isTextblock) {
279
288
  currentBlockId = blockStarts.get(pos) ?? null;
280
289
  return true;
281
290
  }
282
291
  if (!node.isInline || node.text === void 0) return;
283
- const text = node.text;
292
+ const text = readerTextOf(node, noteReferences);
293
+ const end = bodyText.length + text.length;
284
294
  for (const mark of node.marks) {
285
295
  if (mark.type !== commentType || typeof mark.attrs["commentId"] !== "number") continue;
286
296
  const commentId = mark.attrs["commentId"];
287
- const existing = anchors.get(commentId);
288
- if (!existing) {
289
- anchors.set(commentId, {
290
- commentId,
291
- blockId: currentBlockId,
292
- quote: text
293
- });
294
- continue;
295
- }
296
- existing.quote += text;
297
- existing.blockId ??= currentBlockId;
297
+ const range = ranges.get(commentId);
298
+ if (range) {
299
+ range.end = end;
300
+ range.blockId ??= currentBlockId;
301
+ } else ranges.set(commentId, {
302
+ blockId: currentBlockId,
303
+ start: bodyText.length,
304
+ end
305
+ });
298
306
  }
307
+ bodyText += text;
299
308
  });
300
- return [...anchors.values()];
309
+ return [...ranges].map(([commentId, { blockId, start, end }]) => ({
310
+ commentId,
311
+ blockId,
312
+ quote: bodyText.slice(start, end)
313
+ }));
301
314
  };
302
315
  //#endregion
303
316
  export { FOLIO_REVIEW_CHANGE_KINDS, getCommentAnchorsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot };
@@ -0,0 +1,29 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { Node } from "prosemirror-model";
3
+ import { ValidateDocumentModelIssue } from "@stll/docx-core";
4
+ //#region src/ai-edits/result-validation.d.ts
5
+ /** What the story's package knows that the story itself does not. */
6
+ type FolioOperationResultValidationContext = {
7
+ /**
8
+ * The document's numbering definitions; `null` when the story states none,
9
+ * `undefined` when it cannot say (no numbering plugin), which leaves
10
+ * numbering references unchecked.
11
+ */
12
+ numbering: document_d_exports.NumberingDefinitions | null | undefined;
13
+ /** Comment ids the batch allocated; the host holds the comments themselves. */
14
+ createdCommentIds: readonly number[];
15
+ /**
16
+ * The author the host stamps on those comments, when the batch names one;
17
+ * the host writes the batch's author, so a blank one fails there too.
18
+ */
19
+ commentAuthor?: string;
20
+ };
21
+ /**
22
+ * The save-validator errors the batch introduced: errors in the changed window
23
+ * of `after` that the same window of `before` did not already report. Empty
24
+ * when the result is as saveable as the story it started from.
25
+ */
26
+ declare const findIntroducedModelErrors: (before: Node, after: Node, context: FolioOperationResultValidationContext) => ValidateDocumentModelIssue[];
27
+ declare const describeModelError: ({ path, message }: ValidateDocumentModelIssue) => string;
28
+ //#endregion
29
+ export { FolioOperationResultValidationContext, describeModelError, findIntroducedModelErrors };