@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
@@ -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";
@@ -17,21 +18,26 @@ import { pluginsForHeadlessRevisionResolution } from "../internal/headlessRevisi
17
18
  import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
18
19
  import { captureSectionReferenceInventory, resolvedSectionReferenceLosses, withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
19
20
  import { expectCharacterStyleMarkAttrs } from "../prosemirror/attrs/index.js";
20
- import { acceptAIEditRevision, rejectAIEditRevision, resolveAllChangesInHeadlessState } from "../prosemirror/commands/comments.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";
27
29
  import { createDocumentNumberingPlugin, withDocumentNumbering } from "../prosemirror/plugins/documentNumbering.js";
28
30
  import { createDocumentStylesPlugin, getDocumentStyleResolver, withDocumentStyles } from "../prosemirror/plugins/documentStyles.js";
29
31
  import { schema, singletonManager } from "../prosemirror/schema/index.js";
32
+ import { sectionPropertiesOf } from "../prosemirror/sectionCarrier.js";
33
+ import { storyListNumbering } from "../prosemirror/storyListNumbering.js";
30
34
  import { canonicalJson } from "../utils/canonicalJson.js";
31
35
  import { deterministicHexId } from "../utils/hexId.js";
32
- import { buildAnnotatedBlockText } from "./clean-text.js";
36
+ import { buildAnnotatedBlockTextWithNoteReferences } from "./clean-text.js";
37
+ import { anchoredCommentIdsInBlocks, anchoredCommentIdsInProseDoc, withoutLostCommentThreads } from "./comment-lifecycle.js";
38
+ import { FolioPendingSuggestionRegistry, createPendingSuggestionSourceSnapshot, withoutPendingSuggestionCommentMarks } from "./pending-suggestions.js";
33
39
  import { getCommentAnchorsFromDoc, getTrackedChangeStatsFromDoc, getTrackedChangesFromDoc, getTrackedChangesFromSnapshot } from "./read.js";
34
- import { createFolioAIEditSnapshotWithStyleResolver, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
40
+ import { collectNoteReferenceLabels, createFolioAIEditSnapshotWithStyleResolver, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, isFolioAIContentBlock, normalizeFolioAIBlockText, sourceDocumentOf } from "./snapshot.js";
35
41
  import { matchTableGeometry } from "./table-geometry.js";
36
42
  import { tableTemplateCanCrossPackageLosslessly } from "./table-template.js";
37
43
  import { TaggedError, panic } from "better-result";
@@ -248,6 +254,15 @@ const comparisonAccessByReviewer = /* @__PURE__ */ new WeakMap();
248
254
  const MAIN_STORY = Object.freeze({ type: "main" });
249
255
  const headerFooterStoryKey = ({ type, relationshipId }) => `${type}:${relationshipId}`;
250
256
  const noteStoryKey = ({ type, noteId }) => `${type}:${noteId}`;
257
+ /**
258
+ * A part's roles as a reader label: `first`, or, in a document with several
259
+ * sections, `first (sections 1, 3), default (section 2)`.
260
+ */
261
+ const describePlacements = (placements, sectionCount) => {
262
+ const sectionsByRole = /* @__PURE__ */ new Map();
263
+ for (const { type, section } of placements) sectionsByRole.set(type, [...sectionsByRole.get(type) ?? [], section]);
264
+ return [...sectionsByRole].map(([type, sections]) => sectionCount > 1 ? `${type} (${sections.length > 1 ? "sections" : "section"} ${sections.join(", ")})` : type).join(", ");
265
+ };
251
266
  const secondaryStoryKey = (story) => story.type === "header" || story.type === "footer" ? headerFooterStoryKey(story) : noteStoryKey(story);
252
267
  const editableStoryKey = (story) => story.type === "main" ? "main" : secondaryStoryKey(story);
253
268
  const isFolioReviewedView = (value) => FOLIO_REVIEWED_VIEWS.some((view) => view === value);
@@ -256,6 +271,18 @@ const resolveReviewedState = (state, view) => {
256
271
  if (view === "current-markup") return state;
257
272
  return resolveAllChangesInHeadlessState(state, view === "original" ? "reject" : "accept");
258
273
  };
274
+ /**
275
+ * Reject suggestions before resolving all ordinary revisions. A suggested
276
+ * whole-paragraph or table insert is flagged on the node; its flag must stay
277
+ * intact until that suggestion is explicitly accepted or rejected.
278
+ */
279
+ const rejectSuggestions = (state) => {
280
+ let settled = state;
281
+ rejectAllSuggestions()(state, (transaction) => {
282
+ settled = state.apply(transaction);
283
+ });
284
+ return settled;
285
+ };
259
286
  const createHeadlessPlugins = (styles, numbering) => [
260
287
  ...pluginsForHeadlessRevisionResolution(singletonManager.getPlugins()),
261
288
  createDocumentStylesPlugin(styles),
@@ -266,7 +293,9 @@ const formatStorySnapshotForLLM = (snapshot, annotated) => {
266
293
  const blocks = snapshot.blocks.filter(isFolioAIContentBlock);
267
294
  if (!annotated) return blocks.map(formatBlockForLLM).join("\n");
268
295
  const nodeByStart = /* @__PURE__ */ new Map();
269
- sourceDocumentOf(snapshot).descendants((node, pos) => {
296
+ const sourceDocument = sourceDocumentOf(snapshot);
297
+ const noteReferences = collectNoteReferenceLabels(sourceDocument);
298
+ sourceDocument.descendants((node, pos) => {
270
299
  if (!node.isTextblock) return true;
271
300
  nodeByStart.set(pos, node);
272
301
  return false;
@@ -276,18 +305,22 @@ const formatStorySnapshotForLLM = (snapshot, annotated) => {
276
305
  return blocks.map((block) => {
277
306
  const from = startById.get(block.id);
278
307
  const node = from === void 0 ? void 0 : nodeByStart.get(from);
279
- const text = node ? buildAnnotatedBlockText(node) : block.text;
308
+ const text = node ? buildAnnotatedBlockTextWithNoteReferences(node, noteReferences) : block.text;
280
309
  return formatBlockLine(block, text);
281
310
  }).join("\n");
282
311
  };
283
312
  /**
284
313
  * 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.
314
+ * for headings, then the number a numbered heading shows, and the list marker
315
+ * for list items, so a model can copy the block id straight back into an
316
+ * operation and match the numbers the document shows.
287
317
  */
288
318
  const formatBlockLine = (block, text) => {
289
319
  const label = `[${block.id}]`;
290
- if (block.kind === "heading") return `${label} (h${headingLevel(block)}) ${text}`;
320
+ if (block.kind === "heading") {
321
+ const number = block.displayLabel !== void 0 && block.displayLabel !== block.styleId ? `${block.displayLabel} ` : "";
322
+ return `${label} (h${headingLevel(block)}) ${number}${text}`;
323
+ }
291
324
  if (block.kind === "listItem") return `${label} ${block.displayLabel ?? "•"} ${text}`;
292
325
  return `${label} ${text}`;
293
326
  };
@@ -340,7 +373,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
340
373
  resolvedStoryExpectations = /* @__PURE__ */ new Map();
341
374
  createdComments = [];
342
375
  usedCommentIds;
376
+ /** Comments the body anchored when it was loaded, read as the editor projects it. */
377
+ loadedMainCommentIds;
343
378
  documentOperationUndoEntries = [];
379
+ pendingSuggestions = new FolioPendingSuggestionRegistry();
344
380
  /**
345
381
  * Resolved-state overrides recorded by {@link resolveComment}, keyed by
346
382
  * comment id. Applied on read ({@link getComments}) and on write
@@ -356,6 +392,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
356
392
  this.state = args.state;
357
393
  this.author = args.author;
358
394
  this.usedCommentIds = new Set((args.baseDocument.package.document.comments ?? []).map(({ id }) => id));
395
+ this.loadedMainCommentIds = anchoredCommentIdsInProseDoc(args.state.doc);
359
396
  comparisonAccessByReviewer.set(this, Object.freeze({
360
397
  stageTerminalTableReviewCarrier: (target) => this.stageTerminalTableReviewCarrier(target),
361
398
  stageTargetStyles: (source, snapshots, importedHeaderFooterSnapshots) => this.stageTargetStyles(source, snapshots, importedHeaderFooterSnapshots),
@@ -609,6 +646,51 @@ var FolioDocxReviewer = class FolioDocxReviewer {
609
646
  snapshot() {
610
647
  return createStateSnapshot(this.state);
611
648
  }
649
+ /** Export staged AI operations for storage by the host, outside the DOCX. */
650
+ exportPendingSuggestions() {
651
+ const active = new Set(getSuggestions(this.state).map(({ suggestionId }) => suggestionId));
652
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) active.add(suggestionId);
653
+ return this.pendingSuggestions.exportPendingSuggestions({
654
+ activeSuggestionIds: active,
655
+ snapshotForStory: (story, commentIds) => {
656
+ const state = this.getEditableStoryState(story);
657
+ return state ? createPendingSuggestionSourceSnapshot(state, commentIds) : null;
658
+ }
659
+ });
660
+ }
661
+ /** Validate saved proposals against this document before restaging them. */
662
+ loadPendingSuggestions(records) {
663
+ return this.pendingSuggestions.loadPendingSuggestions({
664
+ records,
665
+ snapshotForStory: (story) => this.snapshotStory(story),
666
+ sourceSnapshotForStory: (story, commentIds) => {
667
+ const state = this.getEditableStoryState(story);
668
+ return state ? createPendingSuggestionSourceSnapshot(state, commentIds) : null;
669
+ },
670
+ apply: (record, snapshot) => {
671
+ const result = this.applyDocumentOperationsInternal({
672
+ story: record.story,
673
+ batch: {
674
+ version: 1,
675
+ operations: [record.operation],
676
+ mode: "suggested"
677
+ },
678
+ snapshot,
679
+ author: record.author,
680
+ createUndoEntry: false
681
+ });
682
+ return {
683
+ applied: result.applied,
684
+ skipped: result.skipped
685
+ };
686
+ },
687
+ activeSuggestionIds: () => {
688
+ const active = new Set(getSuggestions(this.state).map(({ suggestionId }) => suggestionId));
689
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) active.add(suggestionId);
690
+ return active;
691
+ }
692
+ });
693
+ }
612
694
  /**
613
695
  * The package's numbering, flattened to what a reader sees: one entry per
614
696
  * numbering instance and level, with the format and level text that produce
@@ -621,7 +703,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
621
703
  * packages can be compared entry by entry.
622
704
  */
623
705
  readNumberingDefinitions() {
624
- return numberingLevelsOf(this.baseDocument.package.numbering).toSorted((left, right) => left.numId - right.numId || left.level - right.level);
706
+ return numberingLevelsOf(completeNumberingForDoc(this.baseDocument.package.numbering, this.state.doc)).toSorted((left, right) => left.numId - right.numId || left.level - right.level);
625
707
  }
626
708
  /** Return parsed package metadata without exposing the mutable document model. */
627
709
  getDocumentProperties() {
@@ -810,7 +892,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
810
892
  });
811
893
  }
812
894
  /** Apply a versioned operation batch to one editable document story. */
813
- applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground }) {
895
+ applyDocumentOperationsToStory({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, undefinedStyles }) {
814
896
  return this.applyDocumentOperationsInternal({
815
897
  story,
816
898
  batch,
@@ -819,11 +901,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
819
901
  ...wordDiff !== void 0 && { wordDiff },
820
902
  ...tableTemplates !== void 0 && { tableTemplates },
821
903
  ...replacementBackground !== void 0 && { replacementBackground },
904
+ ...undefinedStyles !== void 0 && { undefinedStyles },
822
905
  createUndoEntry: true
823
906
  });
824
907
  }
825
- applyDocumentOperationsInternal({ story, batch, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, createUndoEntry }) {
908
+ applyDocumentOperationsInternal({ story, batch, author, snapshot, revisionStamp, wordDiff, tableTemplates, replacementBackground, undefinedStyles, createUndoEntry }) {
826
909
  const beforeState = this.requireEditableStoryState(story);
910
+ const operationAuthor = author ?? this.author;
911
+ const sourceSnapshot = snapshot ?? createStateSnapshot(beforeState);
827
912
  const createdCommentsLengthBefore = this.createdComments.length;
828
913
  const view = {
829
914
  state: beforeState,
@@ -833,19 +918,20 @@ var FolioDocxReviewer = class FolioDocxReviewer {
833
918
  };
834
919
  const result = applyFolioDocumentOperations({
835
920
  view,
836
- snapshot: snapshot ?? createStateSnapshot(beforeState),
921
+ snapshot: sourceSnapshot,
837
922
  batch,
838
923
  story: story.type === "main" ? "main" : story,
839
- author: this.author,
924
+ author: operationAuthor,
840
925
  ...revisionStamp !== void 0 && { revisionStamp },
841
926
  ...wordDiff !== void 0 && { wordDiff },
842
927
  ...tableTemplates !== void 0 && { tableTemplates },
843
928
  ...replacementBackground !== void 0 && { replacementBackground },
929
+ ...undefinedStyles !== void 0 && { undefinedStyles },
844
930
  createCommentId: (text) => {
845
931
  const comment = createReviewerComment({
846
932
  id: this.nextCommentId(),
847
933
  text,
848
- author: this.author,
934
+ author: operationAuthor,
849
935
  date: revisionStamp?.date
850
936
  });
851
937
  this.createdComments.push(comment);
@@ -856,7 +942,23 @@ var FolioDocxReviewer = class FolioDocxReviewer {
856
942
  id: `headless-${String(undoHandleCursor++)}`
857
943
  }) }
858
944
  });
945
+ const appliedCommentIds = new Set(result.applied.flatMap(({ commentId }) => commentId === void 0 ? [] : [commentId]));
946
+ const createdByBatch = this.createdComments.splice(createdCommentsLengthBefore);
947
+ this.createdComments.push(...createdByBatch.filter((comment) => appliedCommentIds.has(comment.id)));
859
948
  this.setEditableStoryState(story, view.state);
949
+ if (batch.mode === "suggested") {
950
+ const activeSuggestionIds = new Set(getSuggestions(beforeState).map(({ suggestionId }) => suggestionId));
951
+ for (const { suggestionId } of getSuggestions(this.state)) activeSuggestionIds.add(suggestionId);
952
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) activeSuggestionIds.add(suggestionId);
953
+ this.pendingSuggestions.recordApplied({
954
+ snapshot: sourceSnapshot,
955
+ story,
956
+ operations: batch.operations,
957
+ applied: result.applied,
958
+ author: operationAuthor,
959
+ activeSuggestionIds
960
+ });
961
+ }
860
962
  if (result.undoHandle !== null) this.documentOperationUndoEntries.push({
861
963
  undoHandle: result.undoHandle,
862
964
  story,
@@ -926,10 +1028,15 @@ var FolioDocxReviewer = class FolioDocxReviewer {
926
1028
  * one per non-empty part: `[header default] …`, `[footer default] …`,
927
1029
  * `[footnote #N] …`, `[endnote #N] …`. Lines reflect in-memory edits. Empty
928
1030
  * parts and separator notes are omitted.
1031
+ *
1032
+ * A header or footer is labelled with the role its section references give
1033
+ * it (`default`, `first`, `even`); in a document with several sections, also
1034
+ * with the sections that show it: `[header first (sections 1, 3)] …`.
929
1035
  */
930
1036
  getNotesAsText() {
931
1037
  const pkg = this.baseDocument.package;
932
1038
  const lines = [];
1039
+ const placements = this.headerFooterPlacements();
933
1040
  const pushHeaderFooter = (map, label) => {
934
1041
  if (!map) return;
935
1042
  for (const [relationshipId, hf] of map) {
@@ -938,7 +1045,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
938
1045
  relationshipId
939
1046
  };
940
1047
  const text = this.getHeaderFooterStoryText(handle, hf);
941
- if (text.length > 0) lines.push(`[${label} ${hf.hdrFtrType}] ${text}`);
1048
+ if (text.length > 0) {
1049
+ const roles = placements.byPart.get(`${label}:${relationshipId}`);
1050
+ lines.push(`[${label} ${roles ? describePlacements(roles, placements.sectionCount) : hf.hdrFtrType}] ${text}`);
1051
+ }
942
1052
  }
943
1053
  };
944
1054
  pushHeaderFooter(pkg.headers, "header");
@@ -1031,7 +1141,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1031
1141
  * (nested tracked changes, hyperlinks) is out of scope.
1032
1142
  */
1033
1143
  getComments(filter) {
1034
- const definitions = this.withResolvedOverrides([...this.baseDocument.package.document.comments ?? [], ...this.createdComments]);
1144
+ const definitions = this.withResolvedOverrides(this.keptCommentThreads([...this.baseDocument.package.document.comments ?? [], ...this.createdComments], this.state, this.secondaryStoryStates.values()));
1035
1145
  if (definitions.length === 0) return [];
1036
1146
  const anchors = this.commentAnchors();
1037
1147
  const repliesByParent = /* @__PURE__ */ new Map();
@@ -1054,6 +1164,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1054
1164
  text: commentPlainText(comment),
1055
1165
  anchoredText: anchor?.text ?? "",
1056
1166
  blockId: anchor?.blockId ?? null,
1167
+ story: anchor?.story ?? null,
1057
1168
  replies: (repliesByParent.get(comment.id) ?? []).map((reply) => ({
1058
1169
  id: reply.id,
1059
1170
  author: reply.author,
@@ -1071,7 +1182,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1071
1182
  * {@link getComments} or its id; the reply threads under that comment's root
1072
1183
  * (Word threads are flat). On {@link toBuffer} the reply is written as a real
1073
1184
  * Word reply — linked via `commentsExtended.xml` and given its own
1074
- * `commentRange` markers + reference anchored on the parent's range. Returns
1185
+ * `commentRange` markers + reference anchored on the parent's range. The
1186
+ * live document carries the same anchor. Returns
1075
1187
  * the created reply, or `null` when the target comment is absent.
1076
1188
  */
1077
1189
  replyTo(target, input) {
@@ -1085,6 +1197,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1085
1197
  if (!reply) return null;
1086
1198
  this.createdComments.push(reply);
1087
1199
  this.usedCommentIds.add(reply.id);
1200
+ this.anchorReplyInEditor({
1201
+ parentId: reply.parentId ?? parentId,
1202
+ replyId: reply.id
1203
+ });
1088
1204
  return {
1089
1205
  id: reply.id,
1090
1206
  author: reply.author,
@@ -1092,6 +1208,31 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1092
1208
  text: input.text
1093
1209
  };
1094
1210
  }
1211
+ anchorReplyInEditor({ parentId, replyId }) {
1212
+ for (const story of this.listStoryHandlesInternal()) {
1213
+ const state = this.getEditableStoryState(story);
1214
+ if (!state) continue;
1215
+ const commentMark = state.schema.marks["comment"];
1216
+ const referenceNode = state.schema.nodes["commentReference"];
1217
+ if (!commentMark || !referenceNode) continue;
1218
+ let from = null;
1219
+ let to = 0;
1220
+ let referenceAt = null;
1221
+ state.doc.descendants((node, pos) => {
1222
+ if (node.type === referenceNode && node.attrs["commentId"] === parentId) referenceAt ??= pos + node.nodeSize;
1223
+ if (node.isInline && node.marks.some((mark) => mark.type === commentMark && mark.attrs["commentId"] === parentId)) {
1224
+ from ??= pos;
1225
+ to = pos + node.nodeSize;
1226
+ }
1227
+ });
1228
+ if (referenceAt === null || from === null || from >= to) continue;
1229
+ let transaction = state.tr;
1230
+ transaction = transaction.addMark(from, to, commentMark.create({ commentId: replyId }));
1231
+ transaction = transaction.insert(referenceAt, referenceNode.create({ commentId: replyId }));
1232
+ this.setEditableStoryState(story, state.apply(transaction));
1233
+ return;
1234
+ }
1235
+ }
1095
1236
  /**
1096
1237
  * Mark a comment thread resolved, or reopen a previously resolved one. Pass
1097
1238
  * the id from {@link getComments} / {@link FolioReviewComment.id}. Applies
@@ -1111,6 +1252,39 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1111
1252
  this.resolvedOverrides.set(target.id, resolved);
1112
1253
  return true;
1113
1254
  }
1255
+ /**
1256
+ * Where each header and footer part is shown, as the live section
1257
+ * references state it: its role (`w:type`) and the 1-based section, in
1258
+ * section order. A part's own `hdrFtrType` holds one role, and one part can
1259
+ * serve several sections, in different roles.
1260
+ */
1261
+ headerFooterPlacements() {
1262
+ const records = [];
1263
+ this.state.doc.descendants((node) => {
1264
+ if (node.type.name !== "paragraph") return true;
1265
+ const record = sectionPropertiesOf(node);
1266
+ if (record && !records.includes(record)) records.push(record);
1267
+ return false;
1268
+ });
1269
+ const final = this.currentFinalSectionProperties();
1270
+ if (final) records.push(final);
1271
+ const byPart = /* @__PURE__ */ new Map();
1272
+ for (const [index, record] of records.entries()) {
1273
+ const place = (part, type, rId) => {
1274
+ const key = `${part}:${rId}`;
1275
+ byPart.set(key, [...byPart.get(key) ?? [], {
1276
+ type,
1277
+ section: index + 1
1278
+ }]);
1279
+ };
1280
+ for (const { type, rId } of record.headerReferences ?? []) place("header", type, rId);
1281
+ for (const { type, rId } of record.footerReferences ?? []) place("footer", type, rId);
1282
+ }
1283
+ return {
1284
+ byPart,
1285
+ sectionCount: records.length
1286
+ };
1287
+ }
1114
1288
  currentFinalSectionProperties() {
1115
1289
  return this.finalSectionPropertiesOverride ?? this.baseDocument.package.document.finalSectionProperties;
1116
1290
  }
@@ -1203,6 +1377,8 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1203
1377
  }
1204
1378
  acceptChangeInternal(target) {
1205
1379
  const id = revisionIdOf(target);
1380
+ const suggestionId = suggestionIdOfRevision(this.state, id);
1381
+ if (suggestionId !== null) this.runCommand(acceptSuggestion(suggestionId, { author: this.author }));
1206
1382
  const bodyChanged = this.runCommand(acceptAIEditRevision(id));
1207
1383
  const sectionChanged = this.resolveFinalSectionProperties("accept", id) > 0;
1208
1384
  return bodyChanged || sectionChanged;
@@ -1216,13 +1392,40 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1216
1392
  }
1217
1393
  rejectChangeInternal(target) {
1218
1394
  const id = revisionIdOf(target);
1395
+ const suggestionId = suggestionIdOfRevision(this.state, id);
1396
+ if (suggestionId !== null && this.runCommand(rejectSuggestion(suggestionId))) return true;
1219
1397
  const bodyChanged = this.runCommand(rejectAIEditRevision(id));
1220
1398
  const sectionChanged = this.resolveFinalSectionProperties("reject", id) > 0;
1221
1399
  return bodyChanged || sectionChanged;
1222
1400
  }
1401
+ /** Convert one pending suggestion into an ordinary tracked change. */
1402
+ acceptSuggestion(suggestionId, options) {
1403
+ return this.resolveWithSectionReferenceHistory(() => {
1404
+ let accepted = false;
1405
+ for (const handle of this.listStoryHandlesInternal()) {
1406
+ const state = this.getEditableStoryState(handle);
1407
+ if (!state || !getSuggestions(state).some((entry) => entry.suggestionId === suggestionId)) continue;
1408
+ accepted = this.runStoryCommand(acceptSuggestion(suggestionId, { author: options?.author ?? this.author }), handle) || accepted;
1409
+ }
1410
+ return accepted;
1411
+ });
1412
+ }
1413
+ /** Remove one pending suggestion without applying it. */
1414
+ rejectSuggestion(suggestionId) {
1415
+ return this.resolveWithSectionReferenceHistory(() => {
1416
+ let rejected = false;
1417
+ for (const handle of this.listStoryHandlesInternal()) {
1418
+ const state = this.getEditableStoryState(handle);
1419
+ if (!state || !getSuggestions(state).some((entry) => entry.suggestionId === suggestionId)) continue;
1420
+ rejected = this.runStoryCommand(rejectSuggestion(suggestionId), handle) || rejected;
1421
+ }
1422
+ return rejected;
1423
+ });
1424
+ }
1223
1425
  /**
1224
- * Accept every tracked change in the package. Returns the number of changes
1225
- * present before the sweep.
1426
+ * Accept every ordinary tracked change in the package. Pending suggestions
1427
+ * remain staged until {@link acceptSuggestion} is called. Returns the number
1428
+ * of ordinary changes present before the sweep.
1226
1429
  *
1227
1430
  * Every story, not just the body: a revision in a header or a footnote is
1228
1431
  * one a reviewer meant to resolve, and leaving it behind means an accepted
@@ -1271,17 +1474,89 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1271
1474
  }
1272
1475
  resolveEveryStory(mode) {
1273
1476
  let count = 0;
1274
- for (const { handle } of this.listStories()) {
1477
+ const resolvedStories = [];
1478
+ const refreshes = [];
1479
+ for (const handle of this.listStoryHandlesInternal()) {
1275
1480
  const state = this.getEditableStoryState(handle);
1276
1481
  if (!state) continue;
1277
- count += getTrackedChangesFromDoc(state.doc).length;
1278
- this.setEditableStoryState(handle, resolveAllChangesInHeadlessState(state, mode));
1482
+ const ordinaryCount = getTrackedChangesFromDoc(state.doc).filter(({ id }) => mode === "reject" || suggestionIdOfRevision(state, id) === null).length;
1483
+ count += ordinaryCount;
1484
+ const withSuggestions = mode === "accept" && ordinaryCount > 0 && getSuggestions(state).length > 0 ? this.acceptOrdinaryChangesWithSuggestions(state, handle) : null;
1485
+ if (withSuggestions) refreshes.push(withSuggestions.refresh);
1486
+ let resolvedState = state;
1487
+ if (mode === "reject") resolvedState = resolveAllChangesInHeadlessState(rejectSuggestions(state), mode);
1488
+ else if (ordinaryCount > 0) resolvedState = withSuggestions?.state ?? resolveAllChangesInHeadlessState(state, mode);
1489
+ resolvedStories.push({
1490
+ handle,
1491
+ state: resolvedState
1492
+ });
1493
+ }
1494
+ for (const { handle, state } of resolvedStories) this.setEditableStoryState(handle, state);
1495
+ if (refreshes.length > 0) {
1496
+ const activeSuggestionIds = new Set(getSuggestions(this.state).map(({ suggestionId }) => suggestionId));
1497
+ for (const entry of this.secondaryStoryStates.values()) for (const { suggestionId } of getSuggestions(entry.state)) activeSuggestionIds.add(suggestionId);
1498
+ for (const refresh of refreshes) refresh(activeSuggestionIds);
1279
1499
  }
1280
1500
  return count;
1281
1501
  }
1502
+ acceptOrdinaryChangesWithSuggestions(state, story) {
1503
+ const originalSuggestions = getSuggestions(state);
1504
+ const records = this.exportPendingSuggestions().filter((record) => editableStoryKey(record.story) === editableStoryKey(story));
1505
+ const recordedIds = new Set(records.map(({ suggestionId }) => suggestionId));
1506
+ if (originalSuggestions.some(({ suggestionId }) => !recordedIds.has(suggestionId))) panic("A pending suggestion has no operation to restage after bulk acceptance");
1507
+ let clean = rejectSuggestions(state);
1508
+ const restagedCommentIds = new Set(records.flatMap(({ commentId }) => commentId === void 0 ? [] : [commentId]));
1509
+ clean = withoutPendingSuggestionCommentMarks(clean, restagedCommentIds);
1510
+ let resolved = resolveAllChangesInHeadlessState(clean, "accept");
1511
+ const restaged = [];
1512
+ for (const record of records) {
1513
+ const snapshot = createStateSnapshot(resolved);
1514
+ const view = {
1515
+ state: resolved,
1516
+ dispatch: (transaction) => {
1517
+ view.state = view.state.apply(transaction);
1518
+ }
1519
+ };
1520
+ const applied = applyFolioDocumentOperations({
1521
+ view,
1522
+ snapshot,
1523
+ batch: {
1524
+ version: 1,
1525
+ mode: "suggested",
1526
+ operations: [record.operation]
1527
+ },
1528
+ story: story.type === "main" ? "main" : story,
1529
+ author: record.author,
1530
+ createCommentId: () => {
1531
+ if (record.commentId === void 0) panic("A staged comment suggestion has no comment id", { suggestionId: record.suggestionId });
1532
+ return record.commentId;
1533
+ }
1534
+ }).applied.find(({ id }) => id === record.operation.id);
1535
+ if (!applied) continue;
1536
+ restaged.push({
1537
+ snapshot,
1538
+ operation: record.operation,
1539
+ applied,
1540
+ author: record.author
1541
+ });
1542
+ resolved = view.state;
1543
+ }
1544
+ const restagedIds = new Set(getSuggestions(resolved).map(({ suggestionId }) => suggestionId));
1545
+ if (restaged.some(({ applied }) => !restagedIds.has(applied.suggestionId ?? applied.id))) panic("Bulk acceptance lost a pending suggestion");
1546
+ return {
1547
+ state: resolved,
1548
+ refresh: (activeSuggestionIds) => this.pendingSuggestions.replaceRestagedStory({
1549
+ story,
1550
+ restaged,
1551
+ activeSuggestionIds
1552
+ })
1553
+ };
1554
+ }
1282
1555
  /** The current document model with edits merged back in. */
1283
1556
  toDocument() {
1284
- return this.documentFromStateSnapshot(this.captureReviewerState());
1557
+ const document = this.documentFromStateSnapshot(this.captureReviewerState());
1558
+ applyReplyThreadMarkers(document);
1559
+ return document;
1285
1560
  }
1286
1561
  captureReviewerState() {
1287
1562
  const secondaryStoryStates = [];
@@ -1326,11 +1601,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1326
1601
  document.package.footers.delete(handle.relationshipId);
1327
1602
  }
1328
1603
  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);
1604
+ const definitions = [...document.package.document.comments ?? [], ...snapshot.createdComments];
1605
+ const kept = this.keptCommentThreads(definitions, snapshot.mainState, snapshot.secondaryStoryStates);
1606
+ if (kept.length !== definitions.length || snapshot.createdComments.length > 0 || snapshot.resolvedOverrides.size > 0) document.package.document.comments = this.withResolvedOverrides(kept, snapshot.resolvedOverrides);
1330
1607
  return document;
1331
1608
  }
1332
1609
  captureSaveSnapshot() {
1333
1610
  const snapshot = this.captureReviewerState();
1611
+ const pendingCommentIds = new Set(this.exportPendingSuggestions().flatMap(({ commentId }) => commentId === void 0 ? [] : [commentId]));
1334
1612
  const changedParaIds = new Set(getChangedParagraphIds(snapshot.mainState));
1335
1613
  let structuralChange = hasStructuralChanges(snapshot.mainState) || snapshot.finalSectionPropertiesOverride !== void 0 || snapshot.importedStyles !== void 0 || snapshot.importedHeaders.size > 0 || snapshot.importedFooters.size > 0;
1336
1614
  let untrackedChanges = hasUntrackedChanges(snapshot.mainState);
@@ -1357,7 +1635,15 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1357
1635
  reason: "untrackedChange"
1358
1636
  };
1359
1637
  return {
1360
- document: this.documentFromStateSnapshot(snapshot),
1638
+ document: this.documentFromStateSnapshot({
1639
+ ...snapshot,
1640
+ mainState: withoutPendingSuggestionCommentMarks(rejectSuggestions(snapshot.mainState), pendingCommentIds),
1641
+ secondaryStoryStates: snapshot.secondaryStoryStates.map((entry) => ({
1642
+ ...entry,
1643
+ state: withoutPendingSuggestionCommentMarks(rejectSuggestions(entry.state), pendingCommentIds)
1644
+ })),
1645
+ createdComments: snapshot.createdComments.filter(({ id }) => !pendingCommentIds.has(id))
1646
+ }),
1361
1647
  path,
1362
1648
  changedNoteParaIds,
1363
1649
  sectionReferenceRemovals: snapshot.sectionReferenceRemovals,
@@ -1469,16 +1755,11 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1469
1755
  const key = secondaryStoryKey(story);
1470
1756
  const existing = this.secondaryStoryStates.get(key);
1471
1757
  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);
1758
+ const doc = this.secondaryStoryDoc(story);
1759
+ if (!doc) return null;
1479
1760
  const state = ensureBaseDirectionInState(EditorState.create({
1480
1761
  schema,
1481
- doc: ensureDeterministicParaIdsInDoc(storyDoc),
1762
+ doc,
1482
1763
  plugins: createHeadlessPlugins(this.baseDocument.package.styles, this.baseDocument.package.numbering)
1483
1764
  }));
1484
1765
  this.secondaryStoryStates.set(key, {
@@ -1488,6 +1769,25 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1488
1769
  });
1489
1770
  return state;
1490
1771
  }
1772
+ /**
1773
+ * A secondary story as its editable state holds it: the loaded state's
1774
+ * document once an operation has loaded it, otherwise the conversion loading
1775
+ * performs — with the same deterministic block ids — without retaining a
1776
+ * state for a story nothing edits.
1777
+ */
1778
+ readSecondaryStoryDoc(story) {
1779
+ return this.secondaryStoryStates.get(secondaryStoryKey(story))?.state.doc ?? this.secondaryStoryDoc(story);
1780
+ }
1781
+ secondaryStoryDoc(story) {
1782
+ const source = story.type === "header" || story.type === "footer" ? this.getHeaderFooterStory(story) : this.getNoteStory(story);
1783
+ if (!source) return null;
1784
+ const conversionOptions = {
1785
+ ...this.baseDocument.package.styles !== void 0 && { styles: this.baseDocument.package.styles },
1786
+ ...this.baseDocument.package.theme !== void 0 && { theme: this.baseDocument.package.theme }
1787
+ };
1788
+ const storyDoc = story.type === "header" || story.type === "footer" ? headerFooterToProseDoc(source.content, conversionOptions) : footnoteToProseDoc(source.content, conversionOptions);
1789
+ return ensureDeterministicParaIdsInDoc(storyDoc);
1790
+ }
1491
1791
  requireEditableStoryState(story) {
1492
1792
  const state = this.getEditableStoryState(story);
1493
1793
  if (state) return state;
@@ -1650,12 +1950,14 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1650
1950
  let endnotes;
1651
1951
  for (const entry of secondaryStoryStates) {
1652
1952
  if (entry.state === entry.initialState) continue;
1953
+ const lists = storyListNumbering(entry.state, document.package.numbering);
1954
+ if (lists.numbering) document.package.numbering = lists.numbering;
1653
1955
  if (entry.handle.type === "header" || entry.handle.type === "footer") {
1654
1956
  const source = entry.handle.type === "header" ? document.package.headers?.get(entry.handle.relationshipId) : document.package.footers?.get(entry.handle.relationshipId);
1655
1957
  if (!source) continue;
1656
1958
  const edited = {
1657
1959
  ...source,
1658
- content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
1960
+ content: proseDocToBlocks(lists.doc, source.content, document.package.styles)
1659
1961
  };
1660
1962
  if (entry.handle.type === "header") {
1661
1963
  headers ??= new Map(document.package.headers);
@@ -1672,7 +1974,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1672
1974
  if (!source) continue;
1673
1975
  const edited = {
1674
1976
  ...source,
1675
- content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
1977
+ content: proseDocToBlocks(lists.doc, source.content, document.package.styles)
1676
1978
  };
1677
1979
  footnotes ??= [...document.package.footnotes ?? []];
1678
1980
  const index = footnotes.findIndex((note) => note.id === noteId);
@@ -1684,7 +1986,7 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1684
1986
  if (!source) continue;
1685
1987
  const edited = {
1686
1988
  ...source,
1687
- content: proseDocToBlocks(entry.state.doc, source.content, document.package.styles)
1989
+ content: proseDocToBlocks(lists.doc, source.content, document.package.styles)
1688
1990
  };
1689
1991
  endnotes ??= [...document.package.endnotes ?? []];
1690
1992
  const index = endnotes.findIndex((note) => note.id === noteId);
@@ -1706,13 +2008,51 @@ var FolioDocxReviewer = class FolioDocxReviewer {
1706
2008
  };
1707
2009
  });
1708
2010
  }
1709
- /** Map each anchored comment id to its anchored text and containing block id. */
2011
+ /**
2012
+ * `comments` without the threads an edit left anchored nowhere: anchored in
2013
+ * a story when it was loaded (or created anchored by this reviewer), and
2014
+ * anchored in no story now. See `comment-lifecycle.ts`.
2015
+ */
2016
+ keptCommentThreads(comments, mainState, secondaryStoryStates) {
2017
+ const anchoredBefore = new Set(this.loadedMainCommentIds);
2018
+ for (const { id } of this.createdComments) anchoredBefore.add(id);
2019
+ const anchoredNow = anchoredCommentIdsInProseDoc(mainState.doc);
2020
+ const loaded = /* @__PURE__ */ new Map();
2021
+ for (const entry of secondaryStoryStates) loaded.set(secondaryStoryKey(entry.handle), entry);
2022
+ const addAll = (target, ids) => {
2023
+ for (const id of ids) target.add(id);
2024
+ };
2025
+ for (const handle of this.listStoryHandlesInternal()) {
2026
+ if (handle.type === "main") continue;
2027
+ const entry = loaded.get(secondaryStoryKey(handle));
2028
+ if (entry) {
2029
+ addAll(anchoredBefore, anchoredCommentIdsInProseDoc(entry.initialState.doc));
2030
+ addAll(anchoredNow, anchoredCommentIdsInProseDoc(entry.state.doc));
2031
+ continue;
2032
+ }
2033
+ addAll(anchoredNow, anchoredCommentIdsInBlocks((handle.type === "header" || handle.type === "footer" ? this.getHeaderFooterStory(handle) : this.getNoteStory(handle))?.content ?? []));
2034
+ }
2035
+ return withoutLostCommentThreads(comments, {
2036
+ anchoredBefore,
2037
+ anchoredNow
2038
+ });
2039
+ }
2040
+ /**
2041
+ * Map each anchored comment id to its anchored text, its containing block id
2042
+ * and the story holding it. A comment can be anchored in any story, so every
2043
+ * story is read, not only the body.
2044
+ */
1710
2045
  commentAnchors() {
1711
2046
  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
- });
2047
+ for (const story of this.listStoryHandlesInternal()) {
2048
+ const doc = story.type === "main" ? this.state.doc : this.readSecondaryStoryDoc(story);
2049
+ if (!doc) continue;
2050
+ for (const anchor of getCommentAnchorsFromDoc(doc)) if (!anchors.has(anchor.commentId)) anchors.set(anchor.commentId, {
2051
+ text: anchor.quote,
2052
+ blockId: anchor.blockId,
2053
+ story
2054
+ });
2055
+ }
1716
2056
  return anchors;
1717
2057
  }
1718
2058
  /** Allocate outside every parsed or newly-created comment and reply id. */