@stll/folio-core 0.52.0 → 0.54.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 (389) 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 +1779 -141
  4. package/dist/ai-edits/batch-claims.d.ts +145 -0
  5. package/dist/ai-edits/batch-claims.js +275 -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 +91 -7
  14. package/dist/ai-edits/headless.js +520 -51
  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 +6 -4
  26. package/dist/ai-edits/read.js +113 -50
  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/revisionStretches.d.ts +17 -0
  30. package/dist/ai-edits/revisionStretches.js +80 -0
  31. package/dist/ai-edits/snapshot.d.ts +36 -3
  32. package/dist/ai-edits/snapshot.js +180 -25
  33. package/dist/ai-edits/table-cell-mutations.d.ts +4 -2
  34. package/dist/ai-edits/table-cell-mutations.js +6 -2
  35. package/dist/ai-edits/table-geometry.d.ts +1 -1
  36. package/dist/ai-edits/table-row-column-mutations.d.ts +44 -17
  37. package/dist/ai-edits/table-row-column-mutations.js +86 -34
  38. package/dist/ai-edits/types.d.ts +155 -16
  39. package/dist/compare/compare.d.ts +1 -1
  40. package/dist/compare/compare.js +65 -16
  41. package/dist/compare/content-alignment.d.ts +5 -1
  42. package/dist/compare/content-alignment.js +52 -34
  43. package/dist/compare/content-types.d.ts +7 -0
  44. package/dist/compare/content.d.ts +4 -1
  45. package/dist/compare/content.js +6 -1
  46. package/dist/compare/formatting.js +2 -1
  47. package/dist/compare/inline-atoms.d.ts +21 -4
  48. package/dist/compare/inline-atoms.js +138 -16
  49. package/dist/compare/inline-provenance.js +1 -1
  50. package/dist/compare/plan.js +10 -5
  51. package/dist/compare/scenario.js +14 -3
  52. package/dist/compare/section-boundary-properties.js +1 -1
  53. package/dist/compare/types.d.ts +4 -2
  54. package/dist/compare/types.js +2 -1
  55. package/dist/compare/verification.d.ts +1 -1
  56. package/dist/compare/verification.js +15 -3
  57. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  58. package/dist/controller/committedLayoutDiff.js +71 -0
  59. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  60. package/dist/controller/folioEditor.d.ts +1 -1
  61. package/dist/controller/fontReadiness.d.ts +70 -3
  62. package/dist/controller/fontReadiness.js +100 -10
  63. package/dist/controller/headerFooterEditorManager.js +6 -1
  64. package/dist/controller/layoutPipeline.d.ts +12 -10
  65. package/dist/controller/layoutPipeline.js +65 -11
  66. package/dist/controller/layoutRunOptions.d.ts +24 -0
  67. package/dist/controller/layoutRunOptions.js +17 -0
  68. package/dist/controller/layoutScheduler.d.ts +19 -14
  69. package/dist/controller/layoutScheduler.js +15 -12
  70. package/dist/controller/layoutSession.d.ts +26 -2
  71. package/dist/controller/layoutSession.js +1 -1
  72. package/dist/controller/noteEditorManager.js +8 -3
  73. package/dist/document-operations.d.ts +11 -2
  74. package/dist/document-operations.js +230 -21
  75. package/dist/docx/altChunk.d.ts +8 -0
  76. package/dist/docx/altChunk.js +17 -0
  77. package/dist/docx/archiveInflation.d.ts +127 -0
  78. package/dist/docx/archiveInflation.js +181 -0
  79. package/dist/docx/attributeRemainder.d.ts +1 -1
  80. package/dist/docx/blockContentParser.d.ts +64 -1
  81. package/dist/docx/blockContentParser.js +17 -4
  82. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  83. package/dist/docx/blockCustomXmlShell.js +14 -0
  84. package/dist/docx/blockPlainText.js +7 -0
  85. package/dist/docx/commentAnchorIndex.js +1 -1
  86. package/dist/docx/commentRangeIntegrity.js +8 -3
  87. package/dist/docx/commentReferenceNormalization.js +1 -1
  88. package/dist/docx/commentReplyMarkers.js +20 -1
  89. package/dist/docx/compatibility.d.ts +1 -1
  90. package/dist/docx/compatibility.js +34 -13
  91. package/dist/docx/containerChildren.gen.d.ts +1 -1
  92. package/dist/docx/containerChildren.gen.js +1 -0
  93. package/dist/docx/documentParser.js +12 -12
  94. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  95. package/dist/docx/drawingIdNormalization.js +6 -1
  96. package/dist/docx/ensureParaIds.js +119 -97
  97. package/dist/docx/footnoteParser.js +8 -4
  98. package/dist/docx/headerFooterParser.js +3 -3
  99. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  100. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  101. package/dist/docx/hyperlinkParser.d.ts +1 -1
  102. package/dist/docx/index.d.ts +1 -1
  103. package/dist/docx/listNumberingInstances.d.ts +59 -0
  104. package/dist/docx/listNumberingInstances.js +249 -0
  105. package/dist/docx/metadataPrivacy.js +24 -1
  106. package/dist/docx/normalizeBaseDirection.js +2 -1
  107. package/dist/docx/noteReferenceMark.d.ts +15 -0
  108. package/dist/docx/noteReferenceMark.js +48 -0
  109. package/dist/docx/opaqueCarrier.d.ts +7 -0
  110. package/dist/docx/opaqueCarrier.js +38 -0
  111. package/dist/docx/packageParts.js +4 -1
  112. package/dist/docx/paragraphParser.js +96 -27
  113. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  114. package/dist/docx/paragraphPropertySource.js +10 -2
  115. package/dist/docx/paragraphTraversal.js +1 -0
  116. package/dist/docx/parseWarningMessage.js +4 -1
  117. package/dist/docx/parser.js +175 -22
  118. package/dist/docx/rezip.js +31 -7
  119. package/dist/docx/sdtProperties.js +1 -1
  120. package/dist/docx/sdtPropertiesPatch.js +3 -0
  121. package/dist/docx/selectiveSave.js +16 -9
  122. package/dist/docx/selectiveXmlPatch.d.ts +3 -1
  123. package/dist/docx/selectiveXmlPatch.js +186 -39
  124. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  125. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  126. package/dist/docx/serializer/documentSerializer.js +2 -0
  127. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  128. package/dist/docx/serializer/noteSerializer.js +3 -1
  129. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  130. package/dist/docx/serializer/tableSerializer.js +25 -38
  131. package/dist/docx/server/boundedArchive.d.ts +8 -13
  132. package/dist/docx/server/boundedArchive.js +70 -59
  133. package/dist/docx/server/createBilingualDocument.js +3 -3
  134. package/dist/docx/server/extractDocxText.d.ts +8 -2
  135. package/dist/docx/server/extractDocxText.js +2 -2
  136. package/dist/docx/server/validateDocxConformance.d.ts +1 -1
  137. package/dist/docx/server/validateDocxConformance.js +1 -0
  138. package/dist/docx/tableParser.d.ts +157 -1
  139. package/dist/docx/tableParser.js +89 -10
  140. package/dist/docx/textBoxParser.js +1 -1
  141. package/dist/docx/unzip.d.ts +20 -2
  142. package/dist/docx/unzip.js +136 -25
  143. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  144. package/dist/docx/wordprocessingPrefixes.js +286 -0
  145. package/dist/fonts/embeddedFonts.js +1 -1
  146. package/dist/i18n/messages/catalogs.gen.d.ts +170 -34
  147. package/dist/i18n/messages/catalogs.gen.js +187 -51
  148. package/dist/i18n/messages/messages.gen.d.ts +10 -2
  149. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  150. package/dist/internal/compare/inline-presentation.js +11 -10
  151. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  152. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  153. package/dist/internal/indexedPositionMap.d.ts +11 -0
  154. package/dist/internal/indexedPositionMap.js +42 -0
  155. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  156. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  157. package/dist/internal/revisionResolutionEdits.js +19 -0
  158. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  159. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  160. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  161. package/dist/internal/revisionResolutionStep.js +282 -0
  162. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  163. package/dist/internal/revisionResolutionTracking.js +124 -0
  164. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  165. package/dist/internal/wholeStoryRevisionResolution.js +600 -0
  166. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  167. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  168. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  169. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  170. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  171. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  172. package/dist/layout-engine/measure/cache.d.ts +18 -1
  173. package/dist/layout-engine/measure/cache.js +31 -1
  174. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  175. package/dist/layout-engine/measure/measureContainer.js +7 -47
  176. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  177. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  178. package/dist/layout-engine/measure/measureParagraph.js +2 -2
  179. package/dist/layout-engine/measure/measureWorker.js +2 -3
  180. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  181. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  182. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
  183. package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
  184. package/dist/layout-engine/types.d.ts +6 -0
  185. package/dist/layout-painter/renderPage.js +1 -0
  186. package/dist/layout-painter/renderParagraph.js +1 -1
  187. package/dist/managers/ContextMenuManager.js +5 -2
  188. package/dist/managers/types.d.ts +3 -0
  189. package/dist/markdown/escape.d.ts +2 -2
  190. package/dist/markdown/escape.js +4 -5
  191. package/dist/markdown/fromMarkdown.js +2 -1
  192. package/dist/markdown/internals.d.ts +11 -1
  193. package/dist/markdown/internals.js +24 -3
  194. package/dist/markdown/renderBlock.js +121 -25
  195. package/dist/markdown/renderParagraph.d.ts +13 -1
  196. package/dist/markdown/renderParagraph.js +85 -38
  197. package/dist/markdown/renderRuns.d.ts +10 -1
  198. package/dist/markdown/renderRuns.js +323 -69
  199. package/dist/markdown/renderTable.js +24 -12
  200. package/dist/markdown/trailers.js +2 -2
  201. package/dist/markdown/types.d.ts +29 -7
  202. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  203. package/dist/paged-layout/incrementalMeasure.js +1 -9
  204. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  205. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  206. package/dist/panel-layout.d.ts +83 -0
  207. package/dist/panel-layout.js +96 -0
  208. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  209. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  210. package/dist/prosemirror/attrs/index.d.ts +6 -2
  211. package/dist/prosemirror/attrs/index.js +172 -1
  212. package/dist/prosemirror/clearRunColor.d.ts +6 -0
  213. package/dist/prosemirror/clearRunColor.js +46 -0
  214. package/dist/prosemirror/commands/comments.d.ts +7 -8
  215. package/dist/prosemirror/commands/comments.js +281 -212
  216. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  217. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  218. package/dist/prosemirror/commands/formatPainter.js +1 -1
  219. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  220. package/dist/prosemirror/commands/hyperlink.js +2 -1
  221. package/dist/prosemirror/commands/index.d.ts +2 -2
  222. package/dist/prosemirror/commands/index.js +2 -2
  223. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  224. package/dist/prosemirror/commands/paragraph.js +9 -1
  225. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  226. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  227. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  228. package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
  229. package/dist/prosemirror/commands/propertyChangeScope.js +24 -3
  230. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  231. package/dist/prosemirror/commands/resolveAllTableChanges.js +620 -0
  232. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  233. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  234. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  235. package/dist/prosemirror/commands/resolveParagraphProperties.js +73 -0
  236. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  237. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +15 -2
  238. package/dist/prosemirror/commands/tableCellMergeResolution.js +23 -7
  239. package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
  240. package/dist/prosemirror/containerFinalParagraph.js +7 -6
  241. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  242. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  243. package/dist/prosemirror/conversion/fromProseDoc.js +177 -605
  244. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -11
  245. package/dist/prosemirror/conversion/toProseDoc.js +70 -154
  246. package/dist/prosemirror/documentSchema.d.ts +8 -0
  247. package/dist/prosemirror/documentSchema.js +18 -0
  248. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  249. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  250. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  251. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  252. package/dist/prosemirror/extensions/StarterKit.js +5 -1
  253. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  254. package/dist/prosemirror/extensions/core/ParagraphExtension.js +116 -63
  255. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +4 -1
  256. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
  257. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
  258. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
  259. package/dist/prosemirror/extensions/features/ListExtension.js +167 -128
  260. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  261. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  262. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  263. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  264. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  265. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  266. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
  267. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  268. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +124 -0
  269. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +33 -11
  270. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
  271. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  272. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  273. package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
  274. package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
  275. package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
  276. package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
  277. package/dist/prosemirror/extensions/marks/markUtils.d.ts +15 -8
  278. package/dist/prosemirror/extensions/marks/markUtils.js +25 -10
  279. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +12 -0
  280. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +46 -0
  281. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  282. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  283. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
  284. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  285. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  286. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
  287. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  288. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  289. package/dist/prosemirror/extensions/nodes/TableExtension.js +121 -226
  290. package/dist/prosemirror/extensions/types.d.ts +11 -1
  291. package/dist/prosemirror/findReplaceSelection.js +1 -1
  292. package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
  293. package/dist/prosemirror/hyperlinkRemoval.js +129 -0
  294. package/dist/prosemirror/index.d.ts +5 -3
  295. package/dist/prosemirror/index.js +5 -3
  296. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  297. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  298. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  299. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  300. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  301. package/dist/prosemirror/listInstanceReferences.js +72 -0
  302. package/dist/prosemirror/listLabels.d.ts +24 -0
  303. package/dist/prosemirror/listLabels.js +111 -0
  304. package/dist/prosemirror/listNumbering.d.ts +92 -0
  305. package/dist/prosemirror/listNumbering.js +330 -0
  306. package/dist/prosemirror/listRendering.d.ts +27 -0
  307. package/dist/prosemirror/listRendering.js +27 -0
  308. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  309. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  310. package/dist/prosemirror/markupViewNotes.d.ts +8 -0
  311. package/dist/prosemirror/markupViewNotes.js +49 -0
  312. package/dist/prosemirror/markupViewProjection.d.ts +97 -0
  313. package/dist/prosemirror/markupViewProjection.js +88 -0
  314. package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
  315. package/dist/prosemirror/noteReferenceReview.js +332 -0
  316. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  317. package/dist/prosemirror/numberingAttr.js +9 -1
  318. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  319. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  320. package/dist/prosemirror/paragraphIndentation.js +32 -7
  321. package/dist/prosemirror/paragraphMarkJoin.d.ts +21 -0
  322. package/dist/prosemirror/paragraphMarkJoin.js +26 -0
  323. package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
  324. package/dist/prosemirror/paragraphPropertyCarry.js +158 -0
  325. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  326. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  327. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  328. package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
  329. package/dist/prosemirror/plugins/documentStyleState.js +32 -0
  330. package/dist/prosemirror/plugins/documentStyles.d.ts +3 -13
  331. package/dist/prosemirror/plugins/documentStyles.js +67 -17
  332. package/dist/prosemirror/plugins/index.d.ts +2 -1
  333. package/dist/prosemirror/plugins/index.js +2 -1
  334. package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
  335. package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
  336. package/dist/prosemirror/plugins/suggestionMode.d.ts +27 -3
  337. package/dist/prosemirror/plugins/suggestionMode.js +490 -63
  338. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  339. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  340. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  341. package/dist/prosemirror/positionSweep.d.ts +24 -0
  342. package/dist/prosemirror/positionSweep.js +222 -0
  343. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +23 -3
  344. package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
  345. package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
  346. package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
  347. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  348. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  349. package/dist/prosemirror/replacedAnnotations.js +170 -1
  350. package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
  351. package/dist/prosemirror/runFormattingFromMarks.js +567 -0
  352. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  353. package/dist/prosemirror/runFormattingReconciliation.js +10 -9
  354. package/dist/prosemirror/schema/index.d.ts +2 -2
  355. package/dist/prosemirror/schema/index.js +3 -0
  356. package/dist/prosemirror/schema/nodes.d.ts +44 -11
  357. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  358. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  359. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  360. package/dist/prosemirror/storyListNumbering.js +102 -0
  361. package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
  362. package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
  363. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
  364. package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
  365. package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
  366. package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
  367. package/dist/prosemirror/tableCellPaste.d.ts +74 -0
  368. package/dist/prosemirror/tableCellPaste.js +525 -0
  369. package/dist/prosemirror/tableGridMutation.d.ts +87 -2
  370. package/dist/prosemirror/tableGridMutation.js +255 -2
  371. package/dist/prosemirror/tableRunIn.d.ts +51 -0
  372. package/dist/prosemirror/tableRunIn.js +165 -0
  373. package/dist/prosemirror/textInput.js +11 -2
  374. package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
  375. package/dist/prosemirror/trackedRevisionPath.js +32 -0
  376. package/dist/prosemirror/validation.js +5 -1
  377. package/dist/redline.js +93 -9
  378. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  379. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  380. package/dist/server.d.ts +2 -2
  381. package/dist/utils/findReplace.js +2 -2
  382. package/dist/utils/mergeDocumentContent.js +1 -1
  383. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  384. package/dist/utils/noteReferenceLabels.js +32 -0
  385. package/dist/utils/replaceText.js +1 -1
  386. package/dist/version-comparison.d.ts +1 -0
  387. package/dist/version-comparison.js +1 -0
  388. package/package.json +6 -5
  389. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -3,6 +3,7 @@ import { matchInlineProvenance } from "../compare/inline-provenance.js";
3
3
  import { stageSectionBoundaryProperties } from "../compare/section-boundary-properties.js";
4
4
  import { importReferencedStyleDefinitions } from "../compare/style-resources.js";
5
5
  import { applyFolioDocumentOperations } from "../document-operations.js";
6
+ import { applyReplyThreadMarkers } from "../docx/commentReplyMarkers.js";
6
7
  import { rebindDrawingImageRelationship } from "../docx/drawingRelationships.js";
7
8
  import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
8
9
  import { getHeaderFooterText } from "../docx/headerFooterParser.js";
@@ -16,22 +17,30 @@ import { attemptSelectiveSave } from "../docx/selectiveSave.js";
16
17
  import { pluginsForHeadlessRevisionResolution } from "../internal/headlessRevisionResolutionGuard.js";
17
18
  import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
18
19
  import { captureSectionReferenceInventory, resolvedSectionReferenceLosses, withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
19
- import { expectCharacterStyleMarkAttrs } from "../prosemirror/attrs/index.js";
20
- import { acceptAIEditRevision, rejectAIEditRevision, resolveAllChangesInHeadlessState } from "../prosemirror/commands/comments.js";
20
+ import { expectCharacterStyleMarkAttrs, expectFootnoteRefMarkAttrs } from "../prosemirror/attrs/index.js";
21
+ import { acceptAIEditRevision, acceptSuggestion, getSuggestions, rejectAIEditRevision, rejectAllSuggestions, rejectSuggestion, resolveAllChangesInHeadlessState, suggestionIdOfRevision } from "../prosemirror/commands/comments.js";
21
22
  import { sectionRejectProperties } from "../prosemirror/commands/propertyChangeScope.js";
22
23
  import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
23
24
  import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
24
25
  import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
25
26
  import { getChangedParagraphIds, getTrackedSectionEndpointRemoval, hasStructuralChanges, hasUntrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
27
+ import { completeNumberingForDoc } from "../prosemirror/listInstanceReferences.js";
26
28
  import "../prosemirror/listMarker.js";
29
+ import { deleteNoteWithReference, noteDeletionRevisions, noteKeysReferencedIn, noteReferenceStates, noteReferencesRestored, referenceDeletions, referencedNotes } from "../prosemirror/noteReferenceReview.js";
27
30
  import { createDocumentNumberingPlugin, withDocumentNumbering } from "../prosemirror/plugins/documentNumbering.js";
28
- import { createDocumentStylesPlugin, getDocumentStyleResolver, withDocumentStyles } from "../prosemirror/plugins/documentStyles.js";
31
+ import { getDocumentStyleResolver } from "../prosemirror/plugins/documentStyleState.js";
32
+ import { createDocumentStylesPlugin, withDocumentStyles } from "../prosemirror/plugins/documentStyles.js";
29
33
  import { schema, singletonManager } from "../prosemirror/schema/index.js";
34
+ import { sectionPropertiesOf } from "../prosemirror/sectionCarrier.js";
35
+ import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
30
36
  import { canonicalJson } from "../utils/canonicalJson.js";
31
37
  import { deterministicHexId } from "../utils/hexId.js";
32
- import { buildAnnotatedBlockText } from "./clean-text.js";
33
- import { getCommentAnchorsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot } from "./read.js";
34
- import { createFolioAIEditSnapshotWithStyleResolver, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
38
+ import { buildAnnotatedBlockTextWithNoteReferences } from "./clean-text.js";
39
+ import { anchoredCommentIdsInBlocks, anchoredCommentIdsInProseDoc, withoutLostCommentThreads } from "./comment-lifecycle.js";
40
+ import { noteReferenceEditsAllowed } from "./note-references.js";
41
+ import { FolioPendingSuggestionRegistry, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks } from "./pending-suggestions.js";
42
+ import { getCommentAnchorsFromDoc, getTrackedChangeGroupIdsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot } from "./read.js";
43
+ import { collectNoteReferenceLabels, createFolioAIEditSnapshotWithStyleResolver, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
35
44
  import { matchTableGeometry } from "./table-geometry.js";
36
45
  import { tableTemplateCanCrossPackageLosslessly } from "./table-template.js";
37
46
  import { TaggedError, panic } from "better-result";
@@ -248,6 +257,37 @@ const comparisonAccessByReviewer = /* @__PURE__ */ new WeakMap();
248
257
  const MAIN_STORY = Object.freeze({ type: "main" });
249
258
  const headerFooterStoryKey = ({ type, relationshipId }) => `${type}:${relationshipId}`;
250
259
  const noteStoryKey = ({ type, noteId }) => `${type}:${noteId}`;
260
+ /** The note story a {@link noteStoryKey} key names. */
261
+ const noteStoryHandleOf = (key) => {
262
+ const [type, noteId] = key.split(":");
263
+ return (type === "footnote" || type === "endnote") && noteId !== void 0 ? {
264
+ type,
265
+ noteId: Number(noteId)
266
+ } : null;
267
+ };
268
+ /** The notes a story's text refers to, keyed as {@link noteStoryKey} keys them. */
269
+ const referencedNoteKeys = (doc) => {
270
+ const keys = /* @__PURE__ */ new Set();
271
+ doc.descendants((node) => {
272
+ if (!node.isText) return true;
273
+ for (const mark of node.marks) {
274
+ if (mark.type.name !== "footnoteRef") continue;
275
+ const { id, noteType } = expectFootnoteRefMarkAttrs(mark);
276
+ keys.add(`${noteType === "endnote" ? "endnote" : "footnote"}:${String(id)}`);
277
+ }
278
+ return false;
279
+ });
280
+ return keys;
281
+ };
282
+ /**
283
+ * A part's roles as a reader label: `first`, or, in a document with several
284
+ * sections, `first (sections 1, 3), default (section 2)`.
285
+ */
286
+ const describePlacements = (placements, sectionCount) => {
287
+ const sectionsByRole = /* @__PURE__ */ new Map();
288
+ for (const { type, section } of placements) sectionsByRole.set(type, [...sectionsByRole.get(type) ?? [], section]);
289
+ return [...sectionsByRole].map(([type, sections]) => sectionCount > 1 ? `${type} (${sections.length > 1 ? "sections" : "section"} ${sections.join(", ")})` : type).join(", ");
290
+ };
251
291
  const secondaryStoryKey = (story) => story.type === "header" || story.type === "footer" ? headerFooterStoryKey(story) : noteStoryKey(story);
252
292
  const editableStoryKey = (story) => story.type === "main" ? "main" : secondaryStoryKey(story);
253
293
  const isFolioReviewedView = (value) => FOLIO_REVIEWED_VIEWS.some((view) => view === value);
@@ -256,6 +296,18 @@ const resolveReviewedState = (state, view) => {
256
296
  if (view === "current-markup") return state;
257
297
  return resolveAllChangesInHeadlessState(state, view === "original" ? "reject" : "accept");
258
298
  };
299
+ /**
300
+ * Reject suggestions before resolving all ordinary revisions. A suggested
301
+ * whole-paragraph or table insert is flagged on the node; its flag must stay
302
+ * intact until that suggestion is explicitly accepted or rejected.
303
+ */
304
+ const rejectSuggestions = (state) => {
305
+ let settled = state;
306
+ rejectAllSuggestions()(state, (transaction) => {
307
+ settled = state.apply(transaction);
308
+ });
309
+ return settled;
310
+ };
259
311
  const createHeadlessPlugins = (styles, numbering) => [
260
312
  ...pluginsForHeadlessRevisionResolution(singletonManager.getPlugins()),
261
313
  createDocumentStylesPlugin(styles),
@@ -266,7 +318,9 @@ const formatStorySnapshotForLLM = (snapshot, annotated) => {
266
318
  const blocks = snapshot.blocks.filter(isFolioAIContentBlock);
267
319
  if (!annotated) return blocks.map(formatBlockForLLM).join("\n");
268
320
  const nodeByStart = /* @__PURE__ */ new Map();
269
- sourceDocumentOf(snapshot).descendants((node, pos) => {
321
+ const sourceDocument = sourceDocumentOf(snapshot);
322
+ const noteReferences = collectNoteReferenceLabels(sourceDocument);
323
+ sourceDocument.descendants((node, pos) => {
270
324
  if (!node.isTextblock) return true;
271
325
  nodeByStart.set(pos, node);
272
326
  return false;
@@ -276,18 +330,22 @@ const formatStorySnapshotForLLM = (snapshot, annotated) => {
276
330
  return blocks.map((block) => {
277
331
  const from = startById.get(block.id);
278
332
  const node = from === void 0 ? void 0 : nodeByStart.get(from);
279
- const text = node ? buildAnnotatedBlockText(node) : block.text;
333
+ const text = node ? buildAnnotatedBlockTextWithNoteReferences(node, noteReferences) : block.text;
280
334
  return formatBlockLine(block, text);
281
335
  }).join("\n");
282
336
  };
283
337
  /**
284
338
  * One LLM-ready line for a block: `[<blockId>] text`, with an `(h<level>)` tag
285
- * for headings and the list marker for list items, so a model can copy the
286
- * block id straight back into an operation.
339
+ * for headings, then the number a numbered heading shows, and the list marker
340
+ * for list items, so a model can copy the block id straight back into an
341
+ * operation and match the numbers the document shows.
287
342
  */
288
343
  const formatBlockLine = (block, text) => {
289
344
  const label = `[${block.id}]`;
290
- if (block.kind === "heading") return `${label} (h${headingLevel(block)}) ${text}`;
345
+ if (block.kind === "heading") {
346
+ const number = block.displayLabel !== void 0 && block.displayLabel !== block.styleId ? `${block.displayLabel} ` : "";
347
+ return `${label} (h${headingLevel(block)}) ${number}${text}`;
348
+ }
291
349
  if (block.kind === "listItem") return `${label} ${block.displayLabel ?? "•"} ${text}`;
292
350
  return `${label} ${text}`;
293
351
  };
@@ -332,6 +390,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
332
390
  state;
333
391
  secondaryStoryStates = /* @__PURE__ */ new Map();
334
392
  removedHeaderFooterStories = /* @__PURE__ */ new Map();
393
+ /** Notes whose reference a resolution removed: a note exists only through its reference. */
394
+ removedNoteStories = /* @__PURE__ */ new Map();
335
395
  sectionReferenceRemovals = [];
336
396
  importedStyles;
337
397
  importedMedia = /* @__PURE__ */ new Map();
@@ -340,7 +400,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
340
400
  resolvedStoryExpectations = /* @__PURE__ */ new Map();
341
401
  createdComments = [];
342
402
  usedCommentIds;
403
+ /** Comments the body anchored when it was loaded, read as the editor projects it. */
404
+ loadedMainCommentIds;
343
405
  documentOperationUndoEntries = [];
406
+ pendingSuggestions = new FolioPendingSuggestionRegistry();
344
407
  /**
345
408
  * Resolved-state overrides recorded by {@link resolveComment}, keyed by
346
409
  * comment id. Applied on read ({@link getComments}) and on write
@@ -356,6 +419,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
356
419
  this.state = args.state;
357
420
  this.author = args.author;
358
421
  this.usedCommentIds = new Set((args.baseDocument.package.document.comments ?? []).map(({ id }) => id));
422
+ this.loadedMainCommentIds = anchoredCommentIdsInProseDoc(args.state.doc);
359
423
  comparisonAccessByReviewer.set(this, Object.freeze({
360
424
  stageTerminalTableReviewCarrier: (target) => this.stageTerminalTableReviewCarrier(target),
361
425
  stageTargetStyles: (source, snapshots, importedHeaderFooterSnapshots) => this.stageTargetStyles(source, snapshots, importedHeaderFooterSnapshots),
@@ -609,6 +673,51 @@ var FolioDocxReviewer = class FolioDocxReviewer {
609
673
  snapshot() {
610
674
  return createStateSnapshot(this.state);
611
675
  }
676
+ /** Export staged AI operations for storage by the host, outside the DOCX. */
677
+ exportPendingSuggestions() {
678
+ const active = new Set(getSuggestions(this.state).map(({ suggestionId }) => suggestionId));
679
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) active.add(suggestionId);
680
+ return this.pendingSuggestions.exportPendingSuggestions({
681
+ activeSuggestionIds: active,
682
+ snapshotForStory: (story, commentIds) => {
683
+ const state = this.getEditableStoryState(story);
684
+ return state ? createPendingSuggestionSourceSnapshot(state, commentIds) : null;
685
+ }
686
+ });
687
+ }
688
+ /** Validate saved proposals against this document before restaging them. */
689
+ loadPendingSuggestions(records) {
690
+ return this.pendingSuggestions.loadPendingSuggestions({
691
+ records,
692
+ snapshotForStory: (story) => this.snapshotStory(story),
693
+ sourceSnapshotForStory: (story, commentIds) => {
694
+ const state = this.getEditableStoryState(story);
695
+ return state ? createPendingSuggestionSourceSnapshot(state, commentIds) : null;
696
+ },
697
+ apply: (record, snapshot) => {
698
+ const result = this.applyDocumentOperationsInternal({
699
+ story: record.story,
700
+ batch: {
701
+ version: 1,
702
+ operations: [record.operation],
703
+ mode: "suggested"
704
+ },
705
+ snapshot,
706
+ author: record.author,
707
+ createUndoEntry: false
708
+ });
709
+ return {
710
+ applied: result.applied,
711
+ skipped: result.skipped
712
+ };
713
+ },
714
+ activeSuggestionIds: () => {
715
+ const active = new Set(getSuggestions(this.state).map(({ suggestionId }) => suggestionId));
716
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) active.add(suggestionId);
717
+ return active;
718
+ }
719
+ });
720
+ }
612
721
  /**
613
722
  * The package's numbering, flattened to what a reader sees: one entry per
614
723
  * numbering instance and level, with the format and level text that produce
@@ -621,7 +730,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
621
730
  * packages can be compared entry by entry.
622
731
  */
623
732
  readNumberingDefinitions() {
624
- return numberingLevelsOf(this.baseDocument.package.numbering).toSorted((left, right) => left.numId - right.numId || left.level - right.level);
733
+ return numberingLevelsOf(completeNumberingForDoc(this.baseDocument.package.numbering, this.state.doc)).toSorted((left, right) => left.numId - right.numId || left.level - right.level);
625
734
  }
626
735
  /** Return parsed package metadata without exposing the mutable document model. */
627
736
  getDocumentProperties() {
@@ -810,7 +919,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
810
919
  });
811
920
  }
812
921
  /** Apply a versioned operation batch to one editable document story. */
813
- applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground }) {
922
+ applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, undefinedStyles }) {
814
923
  return this.applyDocumentOperationsInternal({
815
924
  story,
816
925
  batch,
@@ -819,11 +928,19 @@ var FolioDocxReviewer = class FolioDocxReviewer {
819
928
  ...wordDiff !== void 0 && { wordDiff },
820
929
  ...tableTemplates !== void 0 && { tableTemplates },
821
930
  ...replacementBackground !== void 0 && { replacementBackground },
931
+ ...undefinedStyles !== void 0 && { undefinedStyles },
822
932
  createUndoEntry: true
823
933
  });
824
934
  }
825
- applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, createUndoEntry }) {
935
+ /** Apply a batch; the notes whose references it deletes or removes follow them. */
936
+ applyDocumentOperationsInternal(options) {
937
+ if (noteReferenceEditsAllowed()) return this.applyDocumentOperationsToStoryState(options);
938
+ return this.withNotesFollowingReferences(() => this.applyDocumentOperationsToStoryState(options));
939
+ }
940
+ applyDocumentOperationsToStoryState({ story, batch, author, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, undefinedStyles, createUndoEntry }) {
826
941
  const beforeState = this.requireEditableStoryState(story);
942
+ const operationAuthor = author ?? this.author;
943
+ const sourceSnapshot = snapshot ?? createStateSnapshot(beforeState);
827
944
  const createdCommentsLengthBefore = this.createdComments.length;
828
945
  const view = {
829
946
  state: beforeState,
@@ -833,19 +950,20 @@ var FolioDocxReviewer = class FolioDocxReviewer {
833
950
  };
834
951
  const result = applyFolioDocumentOperations({
835
952
  view,
836
- snapshot: snapshot ?? createStateSnapshot(beforeState),
953
+ snapshot: sourceSnapshot,
837
954
  batch,
838
955
  story: story.type === "main" ? "main" : story,
839
- author: this.author,
956
+ author: operationAuthor,
840
957
  ...revisionStamp !== void 0 && { revisionStamp },
841
958
  ...wordDiff !== void 0 && { wordDiff },
842
959
  ...tableTemplates !== void 0 && { tableTemplates },
843
960
  ...replacementBackground !== void 0 && { replacementBackground },
961
+ ...undefinedStyles !== void 0 && { undefinedStyles },
844
962
  createCommentId: (text) => {
845
963
  const comment = createReviewerComment({
846
964
  id: this.nextCommentId(),
847
965
  text,
848
- author: this.author,
966
+ author: operationAuthor,
849
967
  date: revisionStamp?.date
850
968
  });
851
969
  this.createdComments.push(comment);
@@ -856,7 +974,23 @@ var FolioDocxReviewer = class FolioDocxReviewer {
856
974
  id: `headless-${String(undoHandleCursor++)}`
857
975
  }) }
858
976
  });
977
+ const appliedCommentIds = new Set(result.applied.flatMap(({ commentId }) => commentId === void 0 ? [] : [commentId]));
978
+ const createdByBatch = this.createdComments.splice(createdCommentsLengthBefore);
979
+ this.createdComments.push(...createdByBatch.filter((comment) => appliedCommentIds.has(comment.id)));
859
980
  this.setEditableStoryState(story, view.state);
981
+ if (batch.mode === "suggested") {
982
+ const activeSuggestionIds = new Set(getSuggestions(beforeState).map(({ suggestionId }) => suggestionId));
983
+ for (const { suggestionId } of getSuggestions(this.state)) activeSuggestionIds.add(suggestionId);
984
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) activeSuggestionIds.add(suggestionId);
985
+ this.pendingSuggestions.recordApplied({
986
+ snapshot: sourceSnapshot,
987
+ story,
988
+ operations: batch.operations,
989
+ applied: result.applied,
990
+ author: operationAuthor,
991
+ activeSuggestionIds
992
+ });
993
+ }
860
994
  if (result.undoHandle !== null) this.documentOperationUndoEntries.push({
861
995
  undoHandle: result.undoHandle,
862
996
  story,
@@ -891,7 +1025,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
891
1025
  undoHandle,
892
1026
  reason: "documentChanged"
893
1027
  };
894
- this.setEditableStoryState(entry.story, entry.beforeState);
1028
+ this.withNotesFollowingReferences(() => this.setEditableStoryState(entry.story, entry.beforeState));
895
1029
  this.createdComments.length = entry.createdCommentsLengthBefore;
896
1030
  this.documentOperationUndoEntries.pop();
897
1031
  return {
@@ -926,10 +1060,15 @@ var FolioDocxReviewer = class FolioDocxReviewer {
926
1060
  * one per non-empty part: `[header default] …`, `[footer default] …`,
927
1061
  * `[footnote #N] …`, `[endnote #N] …`. Lines reflect in-memory edits. Empty
928
1062
  * parts and separator notes are omitted.
1063
+ *
1064
+ * A header or footer is labelled with the role its section references give
1065
+ * it (`default`, `first`, `even`); in a document with several sections, also
1066
+ * with the sections that show it: `[header first (sections 1, 3)] …`.
929
1067
  */
930
1068
  getNotesAsText() {
931
1069
  const pkg = this.baseDocument.package;
932
1070
  const lines = [];
1071
+ const placements = this.headerFooterPlacements();
933
1072
  const pushHeaderFooter = (map, label) => {
934
1073
  if (!map) return;
935
1074
  for (const [relationshipId, hf] of map) {
@@ -938,7 +1077,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
938
1077
  relationshipId
939
1078
  };
940
1079
  const text = this.getHeaderFooterStoryText(handle, hf);
941
- if (text.length > 0) lines.push(`[${label} ${hf.hdrFtrType}] ${text}`);
1080
+ if (text.length > 0) {
1081
+ const roles = placements.byPart.get(`${label}:${relationshipId}`);
1082
+ lines.push(`[${label} ${roles ? describePlacements(roles, placements.sectionCount) : hf.hdrFtrType}] ${text}`);
1083
+ }
942
1084
  }
943
1085
  };
944
1086
  pushHeaderFooter(pkg.headers, "header");
@@ -990,7 +1132,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
990
1132
  type: "endnote",
991
1133
  noteId: endnote.id
992
1134
  });
993
- return handles.filter((handle) => handle.type !== "header" && handle.type !== "footer" || !this.removedHeaderFooterStories.has(headerFooterStoryKey(handle)));
1135
+ return handles.filter((handle) => {
1136
+ if (handle.type === "header" || handle.type === "footer") return !this.removedHeaderFooterStories.has(headerFooterStoryKey(handle));
1137
+ return handle.type !== "footnote" && handle.type !== "endnote" || !this.removedNoteStories.has(noteStoryKey(handle));
1138
+ });
994
1139
  }
995
1140
  /** Discover every readable document story through a typed, serializable handle. */
996
1141
  listStories() {
@@ -1031,7 +1176,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1031
1176
  * (nested tracked changes, hyperlinks) is out of scope.
1032
1177
  */
1033
1178
  getComments(filter) {
1034
- const definitions = this.withResolvedOverrides([...this.baseDocument.package.document.comments ?? [], ...this.createdComments]);
1179
+ const definitions = this.withResolvedOverrides(this.keptCommentThreads([...this.baseDocument.package.document.comments ?? [], ...this.createdComments], this.state, this.secondaryStoryStates.values()));
1035
1180
  if (definitions.length === 0) return [];
1036
1181
  const anchors = this.commentAnchors();
1037
1182
  const repliesByParent = /* @__PURE__ */ new Map();
@@ -1054,6 +1199,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1054
1199
  text: commentPlainText(comment),
1055
1200
  anchoredText: anchor?.text ?? "",
1056
1201
  blockId: anchor?.blockId ?? null,
1202
+ story: anchor?.story ?? null,
1057
1203
  replies: (repliesByParent.get(comment.id) ?? []).map((reply) => ({
1058
1204
  id: reply.id,
1059
1205
  author: reply.author,
@@ -1071,7 +1217,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1071
1217
  * {@link getComments} or its id; the reply threads under that comment's root
1072
1218
  * (Word threads are flat). On {@link toBuffer} the reply is written as a real
1073
1219
  * Word reply — linked via `commentsExtended.xml` and given its own
1074
- * `commentRange` markers + reference anchored on the parent's range. Returns
1220
+ * `commentRange` markers + reference anchored on the parent's range. The
1221
+ * live document carries the same anchor. Returns
1075
1222
  * the created reply, or `null` when the target comment is absent.
1076
1223
  */
1077
1224
  replyTo(target, input) {
@@ -1085,6 +1232,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1085
1232
  if (!reply) return null;
1086
1233
  this.createdComments.push(reply);
1087
1234
  this.usedCommentIds.add(reply.id);
1235
+ this.anchorReplyInEditor({
1236
+ parentId: reply.parentId ?? parentId,
1237
+ replyId: reply.id
1238
+ });
1088
1239
  return {
1089
1240
  id: reply.id,
1090
1241
  author: reply.author,
@@ -1092,6 +1243,31 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1092
1243
  text: input.text
1093
1244
  };
1094
1245
  }
1246
+ anchorReplyInEditor({ parentId, replyId }) {
1247
+ for (const story of this.listStoryHandlesInternal()) {
1248
+ const state = this.getEditableStoryState(story);
1249
+ if (!state) continue;
1250
+ const commentMark = state.schema.marks["comment"];
1251
+ const referenceNode = state.schema.nodes["commentReference"];
1252
+ if (!commentMark || !referenceNode) continue;
1253
+ let from = null;
1254
+ let to = 0;
1255
+ let referenceAt = null;
1256
+ state.doc.descendants((node, pos) => {
1257
+ if (node.type === referenceNode && node.attrs["commentId"] === parentId) referenceAt ??= pos + node.nodeSize;
1258
+ if (node.isInline && node.marks.some((mark) => mark.type === commentMark && mark.attrs["commentId"] === parentId)) {
1259
+ from ??= pos;
1260
+ to = pos + node.nodeSize;
1261
+ }
1262
+ });
1263
+ if (referenceAt === null || from === null || from >= to) continue;
1264
+ let transaction = state.tr;
1265
+ transaction = transaction.addMark(from, to, commentMark.create({ commentId: replyId }));
1266
+ transaction = transaction.insert(referenceAt, referenceNode.create({ commentId: replyId }));
1267
+ this.setEditableStoryState(story, state.apply(transaction));
1268
+ return;
1269
+ }
1270
+ }
1095
1271
  /**
1096
1272
  * Mark a comment thread resolved, or reopen a previously resolved one. Pass
1097
1273
  * the id from {@link getComments} / {@link FolioReviewComment.id}. Applies
@@ -1111,6 +1287,39 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1111
1287
  this.resolvedOverrides.set(target.id, resolved);
1112
1288
  return true;
1113
1289
  }
1290
+ /**
1291
+ * Where each header and footer part is shown, as the live section
1292
+ * references state it: its role (`w:type`) and the 1-based section, in
1293
+ * section order. A part's own `hdrFtrType` holds one role, and one part can
1294
+ * serve several sections, in different roles.
1295
+ */
1296
+ headerFooterPlacements() {
1297
+ const records = [];
1298
+ this.state.doc.descendants((node) => {
1299
+ if (node.type.name !== "paragraph") return true;
1300
+ const record = sectionPropertiesOf(node);
1301
+ if (record && !records.includes(record)) records.push(record);
1302
+ return false;
1303
+ });
1304
+ const final = this.currentFinalSectionProperties();
1305
+ if (final) records.push(final);
1306
+ const byPart = /* @__PURE__ */ new Map();
1307
+ for (const [index, record] of records.entries()) {
1308
+ const place = (part, type, rId) => {
1309
+ const key = `${part}:${rId}`;
1310
+ byPart.set(key, [...byPart.get(key) ?? [], {
1311
+ type,
1312
+ section: index + 1
1313
+ }]);
1314
+ };
1315
+ for (const { type, rId } of record.headerReferences ?? []) place("header", type, rId);
1316
+ for (const { type, rId } of record.footerReferences ?? []) place("footer", type, rId);
1317
+ }
1318
+ return {
1319
+ byPart,
1320
+ sectionCount: records.length
1321
+ };
1322
+ }
1114
1323
  currentFinalSectionProperties() {
1115
1324
  return this.finalSectionPropertiesOverride ?? this.baseDocument.package.document.finalSectionProperties;
1116
1325
  }
@@ -1199,11 +1408,15 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1199
1408
  * revision is no longer present (already resolved, or never existed).
1200
1409
  */
1201
1410
  acceptChange(target) {
1202
- return this.resolveWithSectionReferenceHistory(() => this.acceptChangeInternal(target));
1411
+ return this.resolveWithSectionReferenceHistory(() => this.withNotesFollowingReferences(() => this.acceptChangeInternal(target)));
1203
1412
  }
1204
1413
  acceptChangeInternal(target) {
1205
1414
  const id = revisionIdOf(target);
1206
- const bodyChanged = this.runCommand(acceptAIEditRevision(id));
1415
+ const ids = getTrackedChangeGroupIdsFromDoc(this.state.doc, id);
1416
+ const suggestionId = suggestionIdOfRevision(this.state, id);
1417
+ if (suggestionId !== null) this.runCommand(acceptSuggestion(suggestionId, { author: this.author }));
1418
+ let bodyChanged = false;
1419
+ for (const memberId of ids) bodyChanged = this.runCommand(acceptAIEditRevision(memberId)) || bodyChanged;
1207
1420
  const sectionChanged = this.resolveFinalSectionProperties("accept", id) > 0;
1208
1421
  return bodyChanged || sectionChanged;
1209
1422
  }
@@ -1212,17 +1425,46 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1212
1425
  * deletion's text is restored. See {@link acceptChange} for targeting.
1213
1426
  */
1214
1427
  rejectChange(target) {
1215
- return this.resolveWithSectionReferenceHistory(() => this.rejectChangeInternal(target));
1428
+ return this.resolveWithSectionReferenceHistory(() => this.withNotesFollowingReferences(() => this.rejectChangeInternal(target)));
1216
1429
  }
1217
1430
  rejectChangeInternal(target) {
1218
1431
  const id = revisionIdOf(target);
1219
- const bodyChanged = this.runCommand(rejectAIEditRevision(id));
1432
+ const ids = getTrackedChangeGroupIdsFromDoc(this.state.doc, id);
1433
+ const suggestionId = suggestionIdOfRevision(this.state, id);
1434
+ if (suggestionId !== null && this.runCommand(rejectSuggestion(suggestionId))) return true;
1435
+ let bodyChanged = false;
1436
+ for (const memberId of ids) bodyChanged = this.runCommand(rejectAIEditRevision(memberId)) || bodyChanged;
1220
1437
  const sectionChanged = this.resolveFinalSectionProperties("reject", id) > 0;
1221
1438
  return bodyChanged || sectionChanged;
1222
1439
  }
1440
+ /** Convert one pending suggestion into an ordinary tracked change. */
1441
+ acceptSuggestion(suggestionId, options) {
1442
+ return this.resolveWithSectionReferenceHistory(() => {
1443
+ let accepted = false;
1444
+ for (const handle of this.listStoryHandlesInternal()) {
1445
+ const state = this.getEditableStoryState(handle);
1446
+ if (!state || !getSuggestions(state).some((entry) => entry.suggestionId === suggestionId)) continue;
1447
+ accepted = this.runStoryCommand(acceptSuggestion(suggestionId, { author: options?.author ?? this.author }), handle) || accepted;
1448
+ }
1449
+ return accepted;
1450
+ });
1451
+ }
1452
+ /** Remove one pending suggestion without applying it. */
1453
+ rejectSuggestion(suggestionId) {
1454
+ return this.resolveWithSectionReferenceHistory(() => {
1455
+ let rejected = false;
1456
+ for (const handle of this.listStoryHandlesInternal()) {
1457
+ const state = this.getEditableStoryState(handle);
1458
+ if (!state || !getSuggestions(state).some((entry) => entry.suggestionId === suggestionId)) continue;
1459
+ rejected = this.runStoryCommand(rejectSuggestion(suggestionId), handle) || rejected;
1460
+ }
1461
+ return rejected;
1462
+ });
1463
+ }
1223
1464
  /**
1224
- * Accept every tracked change in the package. Returns the number of changes
1225
- * present before the sweep.
1465
+ * Accept every ordinary tracked change in the package. Pending suggestions
1466
+ * remain staged until {@link acceptSuggestion} is called. Returns the number
1467
+ * of ordinary changes present before the sweep.
1226
1468
  *
1227
1469
  * Every story, not just the body: a revision in a header or a footnote is
1228
1470
  * one a reviewer meant to resolve, and leaving it behind means an accepted
@@ -1270,18 +1512,161 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1270
1512
  return propertyChanges?.length ?? 0;
1271
1513
  }
1272
1514
  resolveEveryStory(mode) {
1515
+ return this.withNotesFollowingReferences(() => this.resolveEveryStoryOnce(mode));
1516
+ }
1517
+ /**
1518
+ * Change the body the way a note follows its reference: a note whose
1519
+ * reference goes, goes too, as a note no text refers to does; a note whose
1520
+ * reference an edit deletes as a tracked change has its text deleted with
1521
+ * it; a note whose reference's deletion is rejected gets back the text that
1522
+ * deletion took.
1523
+ */
1524
+ withNotesFollowingReferences(change) {
1525
+ const before = this.state.doc;
1526
+ const result = change();
1527
+ const after = this.state.doc;
1528
+ const referencedAfter = referencedNoteKeys(after);
1529
+ for (const key of referencedAfter) this.removedNoteStories.delete(key);
1530
+ const unreferenced = new Set([...referencedNoteKeys(before)].filter((key) => !referencedAfter.has(key)));
1531
+ const referencedElsewhere = unreferenced.size > 0 ? this.noteKeysReferencedOutsideBody(unreferenced) : /* @__PURE__ */ new Set();
1532
+ for (const key of unreferenced) {
1533
+ if (referencedElsewhere.has(key)) continue;
1534
+ const handle = noteStoryHandleOf(key);
1535
+ if (handle) this.removedNoteStories.set(noteStoryKey(handle), handle);
1536
+ }
1537
+ const rejectedDeletions = referenceDeletions(before);
1538
+ for (const key of noteReferencesRestored(before, after)) {
1539
+ const handle = noteStoryHandleOf(key);
1540
+ const state = handle ? this.getEditableStoryState(handle) : null;
1541
+ const reference = rejectedDeletions.get(key);
1542
+ const revisions = state && reference ? noteDeletionRevisions(state.doc, reference) : [];
1543
+ if (handle && revisions.length > 0) this.runStoryCommand(rejectAIEditRevision(revisions), handle);
1544
+ }
1545
+ this.deleteNotesWithTheirReferences(before, after);
1546
+ return result;
1547
+ }
1548
+ /** Delete the text of each note whose reference went from live to a tracked deletion. */
1549
+ deleteNotesWithTheirReferences(before, after) {
1550
+ const was = noteReferenceStates(before);
1551
+ const deletions = referenceDeletions(after);
1552
+ for (const [key, state] of noteReferenceStates(after)) {
1553
+ if (state !== "deleted" || was.get(key) !== "live") continue;
1554
+ const reference = deletions.get(key);
1555
+ if (!reference || reference.attrs["provenance"] === "suggested" || reference.attrs["moveKind"] != null) continue;
1556
+ const handle = noteStoryHandleOf(key);
1557
+ if (!handle || this.removedNoteStories.has(noteStoryKey(handle))) continue;
1558
+ this.runStoryCommand((storyState, dispatch) => {
1559
+ const transaction = deleteNoteWithReference(storyState.tr, reference);
1560
+ if (!transaction.docChanged) return false;
1561
+ dispatch?.(transaction);
1562
+ return true;
1563
+ }, handle);
1564
+ }
1565
+ }
1566
+ /**
1567
+ * The notes a story other than the body refers to (a header, a footer, a
1568
+ * comment, another note), leaving out references inside the `skipped` notes.
1569
+ */
1570
+ noteKeysReferencedOutsideBody(skipped) {
1571
+ const keys = new Set(noteKeysReferencedIn(this.baseDocument.package.document.comments ?? []));
1572
+ for (const handle of this.listStoryHandlesInternal()) {
1573
+ if (handle.type === "main") continue;
1574
+ let source;
1575
+ if (handle.type === "footnote" || handle.type === "endnote") {
1576
+ if (skipped.has(noteStoryKey(handle))) continue;
1577
+ source = this.getNoteStory(handle);
1578
+ } else source = this.getHeaderFooterStory(handle);
1579
+ const state = this.secondaryStoryStates.get(secondaryStoryKey(handle))?.state;
1580
+ const found = state ? referencedNoteKeys(state.doc) : noteKeysReferencedIn(source?.content ?? []);
1581
+ for (const key of found) keys.add(key);
1582
+ }
1583
+ return keys;
1584
+ }
1585
+ resolveEveryStoryOnce(mode) {
1273
1586
  let count = 0;
1274
- for (const { handle } of this.listStories()) {
1587
+ const resolvedStories = [];
1588
+ const refreshes = [];
1589
+ for (const handle of this.listStoryHandlesInternal()) {
1275
1590
  const state = this.getEditableStoryState(handle);
1276
1591
  if (!state) continue;
1277
- count += getTrackedChangesFromDoc(state.doc).length;
1278
- this.setEditableStoryState(handle, resolveAllChangesInHeadlessState(state, mode));
1592
+ const ordinaryCount = getTrackedChangesFromDoc(state.doc).filter(({ id }) => mode === "reject" || suggestionIdOfRevision(state, id) === null).length;
1593
+ count += ordinaryCount;
1594
+ const withSuggestions = mode === "accept" && ordinaryCount > 0 && getSuggestions(state).length > 0 ? this.acceptOrdinaryChangesWithSuggestions(state, handle) : null;
1595
+ if (withSuggestions) refreshes.push(withSuggestions.refresh);
1596
+ let resolvedState = state;
1597
+ if (mode === "reject") resolvedState = resolveAllChangesInHeadlessState(rejectSuggestions(state), mode);
1598
+ else if (ordinaryCount > 0) resolvedState = withSuggestions?.state ?? resolveAllChangesInHeadlessState(state, mode);
1599
+ resolvedStories.push({
1600
+ handle,
1601
+ state: resolvedState
1602
+ });
1603
+ }
1604
+ for (const { handle, state } of resolvedStories) this.setEditableStoryState(handle, state);
1605
+ if (refreshes.length > 0) {
1606
+ const activeSuggestionIds = new Set(getSuggestions(this.state).map(({ suggestionId }) => suggestionId));
1607
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) activeSuggestionIds.add(suggestionId);
1608
+ for (const refresh of refreshes) refresh(activeSuggestionIds);
1279
1609
  }
1280
1610
  return count;
1281
1611
  }
1612
+ acceptOrdinaryChangesWithSuggestions(state, story) {
1613
+ const originalSuggestions = getSuggestions(state);
1614
+ const records = this.exportPendingSuggestions().filter((record) => editableStoryKey(record.story) === editableStoryKey(story));
1615
+ const recordedIds = new Set(records.map(({ suggestionId }) => suggestionId));
1616
+ if (originalSuggestions.some(({ suggestionId }) => !recordedIds.has(suggestionId))) panic("A pending suggestion has no operation to restage after bulk acceptance");
1617
+ let clean = rejectSuggestions(state);
1618
+ const restagedCommentIds = new Set(records.flatMap(({ commentId }) => commentId === void 0 ? [] : [commentId]));
1619
+ clean = withoutPendingSuggestionCommentMarks(clean, restagedCommentIds);
1620
+ let resolved = resolveAllChangesInHeadlessState(clean, "accept");
1621
+ const restaged = [];
1622
+ for (const record of records) {
1623
+ const snapshot = createStateSnapshot(resolved);
1624
+ const view = {
1625
+ state: resolved,
1626
+ dispatch: (transaction) => {
1627
+ view.state = view.state.apply(transaction);
1628
+ }
1629
+ };
1630
+ const applied = applyFolioDocumentOperations({
1631
+ view,
1632
+ snapshot,
1633
+ batch: {
1634
+ version: 1,
1635
+ mode: "suggested",
1636
+ operations: [record.operation]
1637
+ },
1638
+ story: story.type === "main" ? "main" : story,
1639
+ author: record.author,
1640
+ createCommentId: () => {
1641
+ if (record.commentId === void 0) panic("A staged comment suggestion has no comment id", { suggestionId: record.suggestionId });
1642
+ return record.commentId;
1643
+ }
1644
+ }).applied.find(({ id }) => id === record.operation.id);
1645
+ if (!applied) continue;
1646
+ restaged.push({
1647
+ snapshot,
1648
+ operation: record.operation,
1649
+ applied,
1650
+ author: record.author
1651
+ });
1652
+ resolved = view.state;
1653
+ }
1654
+ const restagedIds = new Set(getSuggestions(resolved).map(({ suggestionId }) => suggestionId));
1655
+ if (restaged.some(({ applied }) => !restagedIds.has(applied.suggestionId ?? applied.id))) panic("Bulk acceptance lost a pending suggestion");
1656
+ return {
1657
+ state: resolved,
1658
+ refresh: (activeSuggestionIds) => this.pendingSuggestions.replaceRestagedStory({
1659
+ story,
1660
+ restaged,
1661
+ activeSuggestionIds
1662
+ })
1663
+ };
1664
+ }
1282
1665
  /** The current document model with edits merged back in. */
1283
1666
  toDocument() {
1284
- return this.documentFromStateSnapshot(this.captureReviewerState());
1667
+ const document = this.documentFromStateSnapshot(this.captureReviewerState());
1668
+ applyReplyThreadMarkers(document);
1669
+ return document;
1285
1670
  }
1286
1671
  captureReviewerState() {
1287
1672
  const secondaryStoryStates = [];
@@ -1296,6 +1681,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1296
1681
  secondaryStoryStates,
1297
1682
  sectionReferenceRemovals: [...this.sectionReferenceRemovals],
1298
1683
  removedHeaderFooterStories: [...this.removedHeaderFooterStories.values()],
1684
+ removedNoteStories: [...this.removedNoteStories.values()],
1299
1685
  importedStyles: this.importedStyles,
1300
1686
  importedMedia: new Map(this.importedMedia),
1301
1687
  importedHeaders: new Map(this.importedHeaders),
@@ -1326,13 +1712,30 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1326
1712
  document.package.footers.delete(handle.relationshipId);
1327
1713
  }
1328
1714
  this.mergeEditedSecondaryStories(document, snapshot.secondaryStoryStates);
1329
- if (snapshot.createdComments.length > 0 || snapshot.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides([...document.package.document.comments ?? [], ...snapshot.createdComments], snapshot.resolvedOverrides);
1715
+ if (snapshot.removedNoteStories.length > 0) {
1716
+ const candidates = new Set(snapshot.removedNoteStories.map(noteStoryKey));
1717
+ const referenced = referencedNotes(document, candidates);
1718
+ const removed = new Set([...candidates].filter((key) => !referenced.has(key)));
1719
+ const { footnotes, endnotes } = document.package;
1720
+ if (footnotes) document.package.footnotes = footnotes.filter((note) => !removed.has(noteStoryKey({
1721
+ type: "footnote",
1722
+ noteId: note.id
1723
+ })));
1724
+ if (endnotes) document.package.endnotes = endnotes.filter((note) => !removed.has(noteStoryKey({
1725
+ type: "endnote",
1726
+ noteId: note.id
1727
+ })));
1728
+ }
1729
+ const definitions = [...document.package.document.comments ?? [], ...snapshot.createdComments];
1730
+ const kept = this.keptCommentThreads(definitions, snapshot.mainState, snapshot.secondaryStoryStates);
1731
+ if (kept.length !== definitions.length || snapshot.createdComments.length > 0 || snapshot.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides(kept, snapshot.resolvedOverrides);
1330
1732
  return document;
1331
1733
  }
1332
1734
  captureSaveSnapshot() {
1333
1735
  const snapshot = this.captureReviewerState();
1736
+ const pendingCommentIds = new Set(this.exportPendingSuggestions().flatMap(({ commentId }) => commentId === void 0 ? [] : [commentId]));
1334
1737
  const changedParaIds = new Set(getChangedParagraphIds(snapshot.mainState));
1335
- let structuralChange = hasStructuralChanges(snapshot.mainState) || snapshot.finalSectionPropertiesOverride !== void 0 || snapshot.importedStyles !== void 0 || snapshot.importedHeaders.size > 0 || snapshot.importedFooters.size > 0;
1738
+ let structuralChange = hasStructuralChanges(snapshot.mainState) || snapshot.finalSectionPropertiesOverride !== void 0 || snapshot.importedStyles !== void 0 || snapshot.importedHeaders.size > 0 || snapshot.importedFooters.size > 0 || snapshot.removedNoteStories.length > 0;
1336
1739
  let untrackedChanges = hasUntrackedChanges(snapshot.mainState);
1337
1740
  const changedNoteParaIds = /* @__PURE__ */ new Set();
1338
1741
  for (const entry of snapshot.secondaryStoryStates) {
@@ -1357,7 +1760,15 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1357
1760
  reason: "untrackedChange"
1358
1761
  };
1359
1762
  return {
1360
- document: this.documentFromStateSnapshot(snapshot),
1763
+ document: this.documentFromStateSnapshot({
1764
+ ...snapshot,
1765
+ mainState: withoutPendingSuggestionCommentMarks(rejectSuggestions(snapshot.mainState), pendingCommentIds),
1766
+ secondaryStoryStates: snapshot.secondaryStoryStates.map((entry) => ({
1767
+ ...entry,
1768
+ state: withoutPendingSuggestionCommentMarks(rejectSuggestions(entry.state), pendingCommentIds)
1769
+ })),
1770
+ createdComments: snapshot.createdComments.filter(({ id }) => !pendingCommentIds.has(id))
1771
+ }),
1361
1772
  path,
1362
1773
  changedNoteParaIds,
1363
1774
  sectionReferenceRemovals: snapshot.sectionReferenceRemovals,
@@ -1469,16 +1880,11 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1469
1880
  const key = secondaryStoryKey(story);
1470
1881
  const existing = this.secondaryStoryStates.get(key);
1471
1882
  if (existing) return existing.state;
1472
- const source = story.type === "header" || story.type === "footer" ? this.getHeaderFooterStory(story) : this.getNoteStory(story);
1473
- if (!source) return null;
1474
- const conversionOptions = {
1475
- ...this.baseDocument.package.styles !== void 0 && { styles: this.baseDocument.package.styles },
1476
- ...this.baseDocument.package.theme !== void 0 && { theme: this.baseDocument.package.theme }
1477
- };
1478
- const storyDoc = story.type === "header" || story.type === "footer" ? headerFooterToProseDoc(source.content, conversionOptions) : footnoteToProseDoc(source.content, conversionOptions);
1883
+ const doc = this.secondaryStoryDoc(story);
1884
+ if (!doc) return null;
1479
1885
  const state = ensureBaseDirectionInState(EditorState.create({
1480
1886
  schema,
1481
- doc: ensureDeterministicParaIdsInDoc(storyDoc),
1887
+ doc,
1482
1888
  plugins: createHeadlessPlugins(this.baseDocument.package.styles, this.baseDocument.package.numbering)
1483
1889
  }));
1484
1890
  this.secondaryStoryStates.set(key, {
@@ -1488,6 +1894,25 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1488
1894
  });
1489
1895
  return state;
1490
1896
  }
1897
+ /**
1898
+ * A secondary story as its editable state holds it: the loaded state's
1899
+ * document once an operation has loaded it, otherwise the conversion loading
1900
+ * performs — with the same deterministic block ids — without retaining a
1901
+ * state for a story nothing edits.
1902
+ */
1903
+ readSecondaryStoryDoc(story) {
1904
+ return this.secondaryStoryStates.get(secondaryStoryKey(story))?.state.doc ?? this.secondaryStoryDoc(story);
1905
+ }
1906
+ secondaryStoryDoc(story) {
1907
+ const source = story.type === "header" || story.type === "footer" ? this.getHeaderFooterStory(story) : this.getNoteStory(story);
1908
+ if (!source) return null;
1909
+ const conversionOptions = {
1910
+ ...this.baseDocument.package.styles !== void 0 && { styles: this.baseDocument.package.styles },
1911
+ ...this.baseDocument.package.theme !== void 0 && { theme: this.baseDocument.package.theme }
1912
+ };
1913
+ const storyDoc = story.type === "header" || story.type === "footer" ? headerFooterToProseDoc(source.content, conversionOptions) : footnoteToProseDoc(source.content, conversionOptions);
1914
+ return ensureDeterministicParaIdsInDoc(storyDoc);
1915
+ }
1491
1916
  requireEditableStoryState(story) {
1492
1917
  const state = this.getEditableStoryState(story);
1493
1918
  if (state) return state;
@@ -1650,12 +2075,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1650
2075
  let endnotes;
1651
2076
  for (const entry of secondaryStoryStates) {
1652
2077
  if (entry.state === entry.initialState) continue;
2078
+ const lists = storyListNumbering(entry.state, document.package.numbering);
2079
+ if (lists.numbering) document.package.numbering = lists.numbering;
1653
2080
  if (entry.handle.type === "header" || entry.handle.type === "footer") {
1654
2081
  const source = entry.handle.type === "header" ? document.package.headers?.get(entry.handle.relationshipId) : document.package.footers?.get(entry.handle.relationshipId);
1655
2082
  if (!source) continue;
1656
2083
  const edited = {
1657
2084
  ...source,
1658
- content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
2085
+ content: proseDocToBlocks(lists.doc, source.content, document.package.styles)
1659
2086
  };
1660
2087
  if (entry.handle.type === "header") {
1661
2088
  headers ??= new Map(document.package.headers);
@@ -1672,7 +2099,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1672
2099
  if (!source) continue;
1673
2100
  const edited = {
1674
2101
  ...source,
1675
- content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
2102
+ content: proseDocToBlocks(lists.doc, source.content, document.package.styles)
1676
2103
  };
1677
2104
  footnotes ??= [...document.package.footnotes ?? []];
1678
2105
  const index = footnotes.findIndex((note) => note.id === noteId);
@@ -1684,7 +2111,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1684
2111
  if (!source) continue;
1685
2112
  const edited = {
1686
2113
  ...source,
1687
- content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
2114
+ content: proseDocToBlocks(lists.doc, source.content, document.package.styles)
1688
2115
  };
1689
2116
  endnotes ??= [...document.package.endnotes ?? []];
1690
2117
  const index = endnotes.findIndex((note) => note.id === noteId);
@@ -1706,13 +2133,55 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1706
2133
  };
1707
2134
  });
1708
2135
  }
1709
- /** Map each anchored comment id to its anchored text and containing block id. */
2136
+ /**
2137
+ * `comments` without the threads an edit left anchored nowhere: anchored in
2138
+ * a story when it was loaded (or created anchored by this reviewer), and
2139
+ * anchored in no story now. See `comment-lifecycle.ts`.
2140
+ */
2141
+ keptCommentThreads(comments, mainState, secondaryStoryStates) {
2142
+ const anchoredBefore = new Set(this.loadedMainCommentIds);
2143
+ for (const { id } of this.createdComments) anchoredBefore.add(id);
2144
+ const anchoredNow = anchoredCommentIdsInProseDoc(mainState.doc);
2145
+ const loaded = /* @__PURE__ */ new Map();
2146
+ for (const entry of secondaryStoryStates) loaded.set(secondaryStoryKey(entry.handle), entry);
2147
+ const addAll = (target, ids) => {
2148
+ for (const id of ids) target.add(id);
2149
+ };
2150
+ for (const handle of this.listStoryHandlesInternal()) {
2151
+ if (handle.type === "main") continue;
2152
+ const entry = loaded.get(secondaryStoryKey(handle));
2153
+ if (entry) {
2154
+ addAll(anchoredBefore, anchoredCommentIdsInProseDoc(entry.initialState.doc));
2155
+ addAll(anchoredNow, anchoredCommentIdsInProseDoc(entry.state.doc));
2156
+ continue;
2157
+ }
2158
+ addAll(anchoredNow, anchoredCommentIdsInBlocks((handle.type === "header" || handle.type === "footer" ? this.getHeaderFooterStory(handle) : this.getNoteStory(handle))?.content ?? []));
2159
+ }
2160
+ for (const handle of this.removedNoteStories.values()) {
2161
+ const entry = loaded.get(secondaryStoryKey(handle));
2162
+ addAll(anchoredBefore, entry ? anchoredCommentIdsInProseDoc(entry.initialState.doc) : anchoredCommentIdsInBlocks(this.getNoteStory(handle)?.content ?? []));
2163
+ }
2164
+ return withoutLostCommentThreads(comments, {
2165
+ anchoredBefore,
2166
+ anchoredNow
2167
+ });
2168
+ }
2169
+ /**
2170
+ * Map each anchored comment id to its anchored text, its containing block id
2171
+ * and the story holding it. A comment can be anchored in any story, so every
2172
+ * story is read, not only the body.
2173
+ */
1710
2174
  commentAnchors() {
1711
2175
  const anchors = /* @__PURE__ */ new Map();
1712
- for (const anchor of getCommentAnchorsFromDoc(this.state.doc)) anchors.set(anchor.commentId, {
1713
- text: anchor.quote,
1714
- blockId: anchor.blockId
1715
- });
2176
+ for (const story of this.listStoryHandlesInternal()) {
2177
+ const doc = story.type === "main" ? this.state.doc : this.readSecondaryStoryDoc(story);
2178
+ if (!doc) continue;
2179
+ for (const anchor of getCommentAnchorsFromDoc(doc)) if (!anchors.has(anchor.commentId)) anchors.set(anchor.commentId, {
2180
+ text: anchor.quote,
2181
+ blockId: anchor.blockId,
2182
+ story
2183
+ });
2184
+ }
1716
2185
  return anchors;
1717
2186
  }
1718
2187
  /** Allocate outside every parsed or newly-created comment and reply id. */