@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
@@ -26,10 +26,11 @@ import { serializeFontTableXml } from "./serializer/fontTableSerializer.js";
26
26
  import { serializeHeaderFooter } from "./serializer/headerFooterSerializer.js";
27
27
  import { serializeEndnotes, serializeFootnotes, serializeNewEndnotesPart, serializeNewFootnotesPart } from "./serializer/noteSerializer.js";
28
28
  import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
29
- import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
29
+ import { readRootNamespaceBindings, serializePartElement } from "./serializer/partNamespaces.js";
30
30
  import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
31
31
  import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerializer.js";
32
32
  import { serializeThemeXml } from "./serializer/themeSerializer.js";
33
+ import { hasCanonicalWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
33
34
  import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
34
35
  import { TaggedError, panic } from "better-result";
35
36
  import { escapeXmlAttribute, escapeXmlText, validateDocxPackage } from "@stll/docx-core";
@@ -363,7 +364,7 @@ function collectNewImages(blocks) {
363
364
  for (const sub of item.content) if (sub.type === "run") collectFromRun(sub);
364
365
  }
365
366
  } else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) images.push(...collectNewImages(cell.content));
366
- else if (block.type === "blockSdt") images.push(...collectNewImages(block.content));
367
+ else if (block.type === "blockSdt" || block.type === "blockCustomXml") images.push(...collectNewImages(block.content));
367
368
  return images;
368
369
  }
369
370
  /** Map MIME type to file extension (inverse of getContentTypeForExtension) */
@@ -484,7 +485,7 @@ function collectHyperlinksWithoutRId(blocks) {
484
485
  };
485
486
  for (const block of blocks) if (block.type === "paragraph") collectInlineHyperlinks(block.content);
486
487
  else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) hyperlinks.push(...collectHyperlinksWithoutRId(cell.content));
487
- else if (block.type === "blockSdt") hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
488
+ else if (block.type === "blockSdt" || block.type === "blockCustomXml") hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
488
489
  return hyperlinks;
489
490
  }
490
491
  /** The selective save boundary must use the same resource census as full repack. */
@@ -1338,7 +1339,8 @@ async function serializeHeadersFootersToZip(doc, zip, compressionLevel) {
1338
1339
  }
1339
1340
  async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel, changedNoteParaIds }) {
1340
1341
  const footnotes = doc.package.footnotes ?? [];
1341
- if (footnotes.length > 0) if (findNotePartEntry(originalZip, "word/footnotes.xml")) await patchNotePartIntoZip({
1342
+ const originalFootnotes = doc.package.footnotes === void 0 ? null : findNotePartEntry(originalZip, "word/footnotes.xml");
1343
+ if (footnotes.length > 0 || originalFootnotes) if (originalFootnotes) await patchNotePartIntoZip({
1342
1344
  conventionalLowerPath: "word/footnotes.xml",
1343
1345
  currentXml: serializeFootnotes(footnotes),
1344
1346
  replacementXml: serializeNewFootnotesPart(footnotes),
@@ -1358,7 +1360,8 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1358
1360
  compressionLevel
1359
1361
  });
1360
1362
  const endnotes = doc.package.endnotes ?? [];
1361
- if (endnotes.length > 0) if (findNotePartEntry(originalZip, "word/endnotes.xml")) await patchNotePartIntoZip({
1363
+ const originalEndnotes = doc.package.endnotes === void 0 ? null : findNotePartEntry(originalZip, "word/endnotes.xml");
1364
+ if (endnotes.length > 0 || originalEndnotes) if (originalEndnotes) await patchNotePartIntoZip({
1362
1365
  conventionalLowerPath: "word/endnotes.xml",
1363
1366
  currentXml: serializeEndnotes(endnotes),
1364
1367
  replacementXml: serializeNewEndnotesPart(endnotes),
@@ -1444,6 +1447,25 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
1444
1447
  const STYLES_PART_PATH = "word/styles.xml";
1445
1448
  const STYLES_CLOSE_ROOT = "</w:styles>";
1446
1449
  /**
1450
+ * One serialized element with the namespace declarations its own prefixes
1451
+ * need, for splicing into a part whose root binds them differently (or not
1452
+ * at all). A prefix no table knows throws rather than being written unbound.
1453
+ */
1454
+ const selfBoundFragment = (fragment, partPath) => {
1455
+ const openEnd = fragment.indexOf(">");
1456
+ const selfClosing = fragment[openEnd - 1] === "/";
1457
+ const rootName = /^<(?<name>[^\s/>]+)/u.exec(fragment)?.groups?.["name"];
1458
+ if (openEnd === -1 || rootName === void 0) throw new DocxPackageFidelityError(`Cannot splice a malformed element into ${partPath}`);
1459
+ return serializePartElement({
1460
+ partPath,
1461
+ rootName,
1462
+ rootAttributes: fragment.slice(rootName.length + 1, selfClosing ? openEnd - 1 : openEnd).trim(),
1463
+ baselinePrefixes: [],
1464
+ sourceBindings: void 0,
1465
+ body: selfClosing ? "" : fragment.slice(openEnd + 1, fragment.lastIndexOf("</"))
1466
+ });
1467
+ };
1468
+ /**
1447
1469
  * The style table a package folio creates starts from: `docDefaults` and
1448
1470
  * `Normal`, and nothing else. A document that carries its own style table
1449
1471
  * replaces this part wholesale.
@@ -1532,7 +1554,8 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
1532
1554
  if (!styles || styles.styles.length === 0) return;
1533
1555
  const file = findNotePartEntry(originalZip, STYLES_PART_PATH);
1534
1556
  const originalXml = file ? await file.async("text") : null;
1535
- const rootClose = originalXml?.lastIndexOf(STYLES_CLOSE_ROOT) ?? -1;
1557
+ const rootName = originalXml === null ? void 0 : /<(?![?!])(?<name>[^\s/>]+)/u.exec(originalXml)?.groups?.["name"];
1558
+ const rootClose = originalXml !== null && rootName !== void 0 && getLocalName(rootName) === "styles" ? originalXml.lastIndexOf(`</${rootName}>`) : -1;
1536
1559
  if (file === null || originalXml === null || rootClose < 0) {
1537
1560
  await materializeNewNotePart({
1538
1561
  contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
@@ -1552,7 +1575,8 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
1552
1575
  added.push(style);
1553
1576
  }
1554
1577
  if (added.length === 0) return;
1555
- const patched = originalXml.slice(0, rootClose) + added.map(serializeStyle).join("") + originalXml.slice(rootClose);
1578
+ const canonical = hasCanonicalWordprocessingPrefixes(originalXml);
1579
+ const patched = originalXml.slice(0, rootClose) + added.map((style) => canonical ? serializeStyle(style) : selfBoundFragment(serializeStyle(style), file.name)).join("") + originalXml.slice(rootClose);
1556
1580
  newZip.file(file.name, patched, {
1557
1581
  compression: "DEFLATE",
1558
1582
  compressionOptions: { level: compressionLevel }
@@ -6,7 +6,7 @@ import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByLocalName, getAt
6
6
  //#region src/docx/sdtProperties.ts
7
7
  function parseListItems(el) {
8
8
  const items = [];
9
- for (const child of el.elements ?? []) if (child.type === "element" && (child.name === "w:listItem" || child.name?.endsWith(":listItem"))) {
9
+ for (const child of el.elements ?? []) if (child.type === "element" && getLocalName(child.name ?? "") === "listItem") {
10
10
  const displayText = getAttributeAnyPrefix(child, "displayText");
11
11
  const value = getAttributeAnyPrefix(child, "value");
12
12
  if (displayText === null && value === null) continue;
@@ -1,3 +1,4 @@
1
+ import { OOXML_NAMESPACE_SCOPE, findChild, getAttribute, parseXml } from "./xmlParser.js";
1
2
  import { escapeXmlAttribute } from "@stll/docx-core";
2
3
  //#region src/docx/sdtPropertiesPatch.ts
3
4
  /**
@@ -66,6 +67,8 @@ const withDateState = (xml, props) => {
66
67
  const fullDate = props.dateValueISO;
67
68
  const format = props.dateFormat;
68
69
  if (fullDate === void 0 && format === void 0) return xml;
70
+ const capturedDate = parseXml(xml, OOXML_NAMESPACE_SCOPE).elements?.at(0);
71
+ if ((fullDate === void 0 || getAttribute(capturedDate, "w", "fullDate") === fullDate) && (format === void 0 || getAttribute(findChild(capturedDate, "w", "dateFormat"), "w", "val") === format)) return xml;
69
72
  const attributes = (prefix, matched) => fullDate === void 0 ? matched : `${withoutAttr(matched, "fullDate")} ${prefix}:fullDate="${escapeXmlAttribute(fullDate)}"`;
70
73
  const formatChild = (prefix) => format === void 0 ? "" : `<${prefix}:dateFormat ${prefix}:val="${escapeXmlAttribute(format)}"/>`;
71
74
  const opened = /<(?<prefix>\w+):date\b(?<attrs>[^>]*)>(?<inner>[\s\S]*?)<\/\w+:date>/iu;
@@ -126,12 +126,15 @@ async function ensureCommentsExtendedPackaging(zip, updates) {
126
126
  * (`w:nsid`/`w:tmpl`, custom formats, level sub-elements), so only the changed
127
127
  * definitions are spliced by id; every other definition stays byte-exact.
128
128
  *
129
- * Any absent numbering part, unchanged numbering, or unsplittable edit leaves
130
- * the original part untouched — the file is simply not added to `updates`.
129
+ * Unchanged numbering leaves the original part untouched. Numbering the
130
+ * splice cannot write (a package without a numbering part, or an edit it
131
+ * cannot split) answers `false`: the paragraphs may reference those
132
+ * definitions, so the caller falls back to a full repack rather than save a
133
+ * package whose `w:numPr` names an instance it does not define.
131
134
  */
132
135
  async function patchNumberingPart(zip, doc, updates) {
133
136
  const numbering = doc.package.numbering;
134
- if (!numbering || numbering.abstractNums.length === 0 && numbering.nums.length === 0) return;
137
+ if (!numbering || numbering.abstractNums.length === 0 && numbering.nums.length === 0) return true;
135
138
  const conventionalLowerPath = "word/numbering.xml";
136
139
  let file = zip.file(conventionalLowerPath);
137
140
  if (!file) {
@@ -140,15 +143,19 @@ async function patchNumberingPart(zip, doc, updates) {
140
143
  break;
141
144
  }
142
145
  }
143
- if (!file) return;
146
+ if (!file) return false;
144
147
  const originalXml = await file.async("text");
148
+ const baselineXml = serializeNumberingXml(parseNumbering(originalXml).definitions);
149
+ const currentXml = serializeNumberingXml(numbering);
150
+ if (currentXml === baselineXml) return true;
145
151
  const patched = patchNumberingDefinitions({
146
152
  originalXml,
147
- baselineXml: serializeNumberingXml(parseNumbering(originalXml).definitions),
148
- currentXml: serializeNumberingXml(numbering)
153
+ baselineXml,
154
+ currentXml
149
155
  });
150
- if (patched === null || patched === originalXml) return;
151
- updates.set(file.name, patched);
156
+ if (patched === null) return false;
157
+ if (patched !== originalXml) updates.set(file.name, patched);
158
+ return true;
152
159
  }
153
160
  /**
154
161
  * Attempt a selective save — patch only changed paragraphs in document.xml.
@@ -250,7 +257,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
250
257
  }
251
258
  }
252
259
  if (!await patchCommentsExtended(zip, commentPlan, updates)) return null;
253
- await patchNumberingPart(zip, doc, updates);
260
+ if (!await patchNumberingPart(zip, doc, updates)) return null;
254
261
  for (const [path, xml] of await collectHeaderFooterUpdates(doc, zip)) updates.set(path, xml);
255
262
  await queueSettingsUpdates(zip, updates);
256
263
  const corePropsFile = zip.file("docProps/core.xml");
@@ -165,7 +165,9 @@ type XmlSplice = {
165
165
  * the rest of the part byte-for-byte, so it can write half a comment range:
166
166
  * invalid OOXML that anchors the comment to nothing. Answers null when it
167
167
  * would, leaving the caller to rewrite a wider region — ultimately the whole
168
- * part from the model, which is balanced with itself.
168
+ * part from the model, which is balanced with itself. Overlapping source
169
+ * regions are refused too: replacing an inner region moves the outer region's
170
+ * end, so its original offsets cannot be applied to the resulting string.
169
171
  */
170
172
  declare const spliceXml: (xml: string, splices: readonly XmlSplice[]) => string | null;
171
173
  type NoteElementName = "footnote" | "endnote";
@@ -1,6 +1,7 @@
1
1
  import { patchBreaksCommentRangeBalance } from "./commentRangeIntegrity.js";
2
2
  import { resolveParagraphIdentities } from "./paraIdAttribute.js";
3
3
  import { captureVerbatimXml } from "./verbatimCapture.js";
4
+ import { PARAGRAPH_SCAN_NAMES, hasCanonicalWordprocessingPrefixes, matchCloseTag, matchOpenTag, resolveSelectiveScanPrefixes, splicesAsCanonical } from "./wordprocessingPrefixes.js";
4
5
  import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findAttributeByNamespaceUri, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
5
6
  //#region src/docx/selectiveXmlPatch.ts
6
7
  /**
@@ -108,29 +109,6 @@ function buildParagraphOffsetIndex(xml) {
108
109
  for (const [id, range] of ranges) if (seenCount.get(id) === 1) index.set(id, range);
109
110
  return index;
110
111
  }
111
- /** The containers {@link scanParagraphs} tracks, by the literal tag the part writes. */
112
- const TRACKED_CONTAINERS = [
113
- {
114
- name: "mc:Fallback",
115
- key: "fallback"
116
- },
117
- {
118
- name: "mc:AlternateContent",
119
- key: "alternateContent"
120
- },
121
- {
122
- name: "w:txbxContent",
123
- key: "textBox"
124
- },
125
- {
126
- name: "w:tc",
127
- key: "tableCell"
128
- }
129
- ].map(({ name, key }) => ({
130
- key,
131
- open: `<${name}`,
132
- close: `</${name}>`
133
- }));
134
112
  /**
135
113
  * Every `<w:p>` element of `xml`, in the document order of its opening tags,
136
114
  * with the `w14:paraId` that tag carries and the containers around it.
@@ -145,6 +123,8 @@ const TRACKED_CONTAINERS = [
145
123
  * no splice can be built from it.
146
124
  */
147
125
  function scanParagraphs(xml) {
126
+ const resolution = resolveSelectiveScanPrefixes(xml);
127
+ const mcPrefixes = resolution.type === "resolved" ? resolution.prefixes.mc : ["mc"];
148
128
  const paragraphs = [];
149
129
  const open = [];
150
130
  const depth = {
@@ -153,6 +133,28 @@ function scanParagraphs(xml) {
153
133
  textBox: 0,
154
134
  tableCell: 0
155
135
  };
136
+ const containers = [
137
+ {
138
+ key: "fallback",
139
+ prefixes: mcPrefixes,
140
+ name: "Fallback"
141
+ },
142
+ {
143
+ key: "alternateContent",
144
+ prefixes: mcPrefixes,
145
+ name: "AlternateContent"
146
+ },
147
+ {
148
+ key: "textBox",
149
+ prefixes: ["w"],
150
+ name: "txbxContent"
151
+ },
152
+ {
153
+ key: "tableCell",
154
+ prefixes: ["w"],
155
+ name: "tc"
156
+ }
157
+ ];
156
158
  let pos = 0;
157
159
  scan: while (pos < xml.length) {
158
160
  const tagStart = xml.indexOf("<", pos);
@@ -165,13 +167,14 @@ function scanParagraphs(xml) {
165
167
  pos = end;
166
168
  continue;
167
169
  }
168
- for (const { key, open: openLiteral, close } of TRACKED_CONTAINERS) {
169
- if (xml.startsWith(close, tagStart)) {
170
+ for (const { key, prefixes, name } of containers) {
171
+ const closeLength = matchCloseTag(xml, tagStart, prefixes, name);
172
+ if (closeLength !== -1) {
170
173
  depth[key] = Math.max(0, depth[key] - 1);
171
- pos = tagStart + close.length;
174
+ pos = tagStart + closeLength;
172
175
  continue scan;
173
176
  }
174
- if (xml.startsWith(openLiteral, tagStart) && isXmlNameBoundary(xml[tagStart + openLiteral.length])) {
177
+ if (matchOpenTag(xml, tagStart, prefixes, name) !== -1) {
175
178
  const tagEnd = xml.indexOf(">", tagStart);
176
179
  if (tagEnd === -1) break scan;
177
180
  if (xml[tagEnd - 1] !== "/") depth[key] += 1;
@@ -333,10 +336,9 @@ const nearestSharedParaId = (own, other, paragraph, step) => {
333
336
  * - an id-less paragraph whose story's ordinals do not line up.
334
337
  */
335
338
  const routeChangedParagraphs = (originalXml, serializedXml, changedIds) => {
336
- const rootName = rootElementName(originalXml);
337
- if (rootName !== void 0 && !rootName.startsWith("w:")) return {
339
+ if (!splicesAsCanonical(originalXml, PARAGRAPH_SCAN_NAMES)) return {
338
340
  type: "refused",
339
- reason: `non-canonical-wordprocessingml-prefix: ${rootName}`
341
+ reason: `non-canonical-wordprocessingml-prefix: ${rootElementName(originalXml) ?? "unknown"}`
340
342
  };
341
343
  const original = addressableParagraphs(originalXml);
342
344
  const serialized = addressableParagraphs(serializedXml);
@@ -423,9 +425,18 @@ const routeChangedParagraphs = (originalXml, serializedXml, changedIds) => {
423
425
  newXml: authored ? newXml : withoutMintedIds(newXml, sourceXml)
424
426
  });
425
427
  }
428
+ const candidate = spliceXml(originalXml, splices);
429
+ if (candidate === null) return {
430
+ type: "refused",
431
+ reason: "unsafe-paragraph-splices"
432
+ };
433
+ if (!splicesAsCanonical(candidate, PARAGRAPH_SCAN_NAMES)) return {
434
+ type: "refused",
435
+ reason: "replacement-namespace-conflict"
436
+ };
426
437
  return {
427
438
  type: "routed",
428
- splices
439
+ xml: candidate
429
440
  };
430
441
  };
431
442
  /**
@@ -459,7 +470,7 @@ function validatePatchSafety(originalXml, serializedXml, changedIds) {
459
470
  function buildPatchedDocumentXml(originalXml, serializedXml, changedIds) {
460
471
  if (changedIds.size === 0) return originalXml;
461
472
  const routing = routeChangedParagraphs(originalXml, serializedXml, changedIds);
462
- return routing.type === "refused" ? null : spliceXml(originalXml, routing.splices);
473
+ return routing.type === "refused" ? null : routing.xml;
463
474
  }
464
475
  /**
465
476
  * Build a patched note part (word/footnotes.xml / word/endnotes.xml) by
@@ -489,11 +500,18 @@ function buildPatchedNoteXml(originalXml, serializedXml, changedIds) {
489
500
  * the rest of the part byte-for-byte, so it can write half a comment range:
490
501
  * invalid OOXML that anchors the comment to nothing. Answers null when it
491
502
  * would, leaving the caller to rewrite a wider region — ultimately the whole
492
- * part from the model, which is balanced with itself.
503
+ * part from the model, which is balanced with itself. Overlapping source
504
+ * regions are refused too: replacing an inner region moves the outer region's
505
+ * end, so its original offsets cannot be applied to the resulting string.
493
506
  */
494
507
  const spliceXml = (xml, splices) => {
495
508
  let result = xml;
496
- for (const { start, end, newXml } of [...splices].toSorted((a, b) => b.start - a.start)) result = result.slice(0, start) + newXml + result.slice(end);
509
+ let unpatchedEnd = xml.length;
510
+ for (const { start, end, newXml } of [...splices].toSorted((a, b) => b.start - a.start)) {
511
+ if (end > unpatchedEnd) return null;
512
+ result = result.slice(0, start) + newXml + result.slice(end);
513
+ unpatchedEnd = start;
514
+ }
497
515
  return patchBreaksCommentRangeBalance(xml, result) ? null : result;
498
516
  };
499
517
  /**
@@ -647,6 +665,40 @@ const paragraphRanges = (xml, wordPrefix) => {
647
665
  }
648
666
  return ranges;
649
667
  };
668
+ const OPENING_PARA_ID = /^<[^>]*?\sw14:paraId="(?<id>[^"]*)"/u;
669
+ const openingParaId = (paragraph) => OPENING_PARA_ID.exec(paragraph)?.groups?.["id"];
670
+ /** A note's XML outside its paragraphs, one entry per gap between them. */
671
+ const noteGaps = (note, ranges) => {
672
+ const gaps = [];
673
+ let cursor = 0;
674
+ for (const range of ranges) {
675
+ gaps.push(note.slice(cursor, range.start));
676
+ cursor = range.end;
677
+ }
678
+ gaps.push(note.slice(cursor));
679
+ return gaps;
680
+ };
681
+ /**
682
+ * Whether a note's block structure differs between two serializations of it:
683
+ * a paragraph added, removed or replaced by another, or anything written
684
+ * between paragraphs changed — a table, a row, a cell's properties, a
685
+ * row-level revision. Paragraph-by-paragraph splicing pairs paragraphs by
686
+ * ordinal, so it is only faithful when neither happened; a note whose shape
687
+ * changed is rewritten whole. A paragraph the baseline has no id for pairs
688
+ * with any id, because that id was minted after reading, not changed.
689
+ */
690
+ const noteShapeChanged = (baseline, current) => {
691
+ const baselineRanges = paragraphRanges(baseline.note, baseline.prefix);
692
+ const currentRanges = paragraphRanges(current.note, current.prefix);
693
+ if (baselineRanges.length !== currentRanges.length) return true;
694
+ const currentGaps = noteGaps(current.note, currentRanges);
695
+ if (noteGaps(baseline.note, baselineRanges).some((gap, index) => gap !== currentGaps[index])) return true;
696
+ return baselineRanges.some((range, index) => {
697
+ const baselineId = openingParaId(baseline.note.slice(range.start, range.end));
698
+ const currentRange = currentRanges[index];
699
+ return baselineId !== void 0 && baselineId !== openingParaId(current.note.slice(currentRange.start, currentRange.end));
700
+ });
701
+ };
650
702
  const collectChangedNoteParaIds = (baselineXml, currentXml) => {
651
703
  const changed = /* @__PURE__ */ new Set();
652
704
  const baselineIds = collectParaIds(baselineXml);
@@ -681,6 +733,7 @@ const collectChangedNoteParaIds = (baselineXml, currentXml) => {
681
733
  */
682
734
  function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, replacementXml, elementName, changedParaIds }) {
683
735
  const currentElements = collectNoteElementSyntax(serializedXml, elementName);
736
+ const baselineElements = collectNoteElementSyntax(baselineXml, elementName);
684
737
  const originalElements = collectNoteElementSyntax(originalXml, elementName);
685
738
  const replacementElements = collectNoteElementSyntax(replacementXml, elementName);
686
739
  const replacementXmlnsDeclarations = collectXmlnsFromOpeningTag(replacementXml);
@@ -690,9 +743,25 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
690
743
  const serializedParaIds = collectParaIds(serializedXml);
691
744
  const effectiveChangedParaIds = changedParaIds ?? collectChangedNoteParaIds(baselineXml, serializedXml);
692
745
  const unroutedChangedParaIds = new Set([...effectiveChangedParaIds].filter((paraId) => serializedParaIds.has(paraId)));
746
+ /** Notes the model has and the part does not: new notes, appended to the part. */
747
+ const addedNotes = [];
693
748
  for (const [id, currentSyntaxEntries] of currentElements) {
694
749
  const originalSyntaxEntries = originalElements.get(id);
695
750
  const replacementSyntaxEntries = replacementElements.get(id);
751
+ if (originalSyntaxEntries === void 0) {
752
+ const replacementSyntax = currentSyntaxEntries.length === 1 && replacementSyntaxEntries?.length === 1 ? replacementSyntaxEntries[0] : void 0;
753
+ const replacementNote = replacementSyntax ? extractNoteElement(replacementXml, replacementSyntax, id) : null;
754
+ if (!replacementSyntax || !replacementNote) return {
755
+ type: "refused",
756
+ reason: "unroutable-paragraph"
757
+ };
758
+ for (const paraId of collectParaIds(replacementNote).keys()) unroutedChangedParaIds.delete(paraId);
759
+ addedNotes.push({
760
+ note: replacementNote,
761
+ syntax: replacementSyntax
762
+ });
763
+ continue;
764
+ }
696
765
  if (currentSyntaxEntries.length !== 1 || originalSyntaxEntries?.length !== 1 || replacementSyntaxEntries?.length !== 1) return {
697
766
  type: "refused",
698
767
  reason: "unroutable-paragraph"
@@ -716,7 +785,17 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
716
785
  const originalParagraphs = paragraphRanges(originalNote, originalSyntax.elementPrefix);
717
786
  const replacementParagraphs = paragraphRanges(replacementNote, replacementSyntax.elementPrefix);
718
787
  const noteChangedParaIds = [...collectParaIds(currentNote).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
719
- if (noteChangedParaIds.length === 0) continue;
788
+ const baselineSyntaxEntries = baselineElements.get(id);
789
+ const baselineSyntax = baselineSyntaxEntries?.length === 1 ? baselineSyntaxEntries[0] : void 0;
790
+ const baselineNote = baselineSyntax ? extractNoteElement(baselineXml, baselineSyntax, id) : null;
791
+ const reshaped = baselineSyntax !== void 0 && baselineNote !== null && baselineNote !== currentNote && noteShapeChanged({
792
+ note: baselineNote,
793
+ prefix: baselineSyntax.elementPrefix
794
+ }, {
795
+ note: currentNote,
796
+ prefix: currentSyntax.elementPrefix
797
+ });
798
+ if (noteChangedParaIds.length === 0 && !reshaped) continue;
720
799
  const wholeNoteSplice = {
721
800
  start: originalOffsets.start,
722
801
  end: originalOffsets.end,
@@ -727,7 +806,7 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
727
806
  })
728
807
  };
729
808
  noteSplices.push(wholeNoteSplice);
730
- if (currentParagraphs.length !== originalParagraphs.length || currentParagraphs.length !== replacementParagraphs.length) {
809
+ if (reshaped || currentParagraphs.length !== originalParagraphs.length || currentParagraphs.length !== replacementParagraphs.length) {
731
810
  for (const paraId of noteChangedParaIds) unroutedChangedParaIds.delete(paraId);
732
811
  paragraphSplices.push(wholeNoteSplice);
733
812
  continue;
@@ -754,10 +833,36 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
754
833
  });
755
834
  }
756
835
  }
836
+ for (const [id, baselineSyntaxEntries] of baselineElements) {
837
+ if (currentElements.has(id) || baselineSyntaxEntries.length !== 1) continue;
838
+ const originalSyntaxEntries = originalElements.get(id);
839
+ const originalSyntax = originalSyntaxEntries?.length === 1 ? originalSyntaxEntries[0] : void 0;
840
+ const originalOffsets = originalSyntax && findNoteElement(originalXml, originalSyntax, id);
841
+ if (!originalOffsets) return {
842
+ type: "refused",
843
+ reason: "unroutable-paragraph"
844
+ };
845
+ const removal = {
846
+ start: originalOffsets.start,
847
+ end: originalOffsets.end,
848
+ newXml: ""
849
+ };
850
+ paragraphSplices.push(removal);
851
+ noteSplices.push(removal);
852
+ }
757
853
  if (unroutedChangedParaIds.size > 0) return {
758
854
  type: "refused",
759
855
  reason: "unroutable-paragraph"
760
856
  };
857
+ if (addedNotes.length > 0) {
858
+ const appended = appendNotesSplice(originalXml, originalElements, addedNotes, { sourceXmlnsDeclarations: replacementXmlnsDeclarations });
859
+ if (!appended) return {
860
+ type: "refused",
861
+ reason: "unroutable-paragraph"
862
+ };
863
+ paragraphSplices.push(appended);
864
+ noteSplices.push(appended);
865
+ }
761
866
  const patched = spliceXml(originalXml, paragraphSplices);
762
867
  if (patched !== null) return {
763
868
  type: "patched",
@@ -773,6 +878,31 @@ function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, repl
773
878
  };
774
879
  }
775
880
  /**
881
+ * Insert new notes after the part's last note, spelled with that note's
882
+ * prefixes. Null when the part holds no note to follow.
883
+ */
884
+ const appendNotesSplice = (originalXml, originalElements, addedNotes, { sourceXmlnsDeclarations }) => {
885
+ let last = null;
886
+ for (const [id, entries] of originalElements) for (const syntax of entries) {
887
+ const offsets = findNoteElement(originalXml, syntax, id);
888
+ if (offsets && (!last || offsets.end > last.end)) last = {
889
+ end: offsets.end,
890
+ syntax
891
+ };
892
+ }
893
+ if (!last) return null;
894
+ const target = last.syntax;
895
+ return {
896
+ start: last.end,
897
+ end: last.end,
898
+ newXml: addedNotes.map(({ note, syntax }) => rewriteWordprocessingPrefixes(note, {
899
+ source: syntax,
900
+ target,
901
+ sourceXmlnsDeclarations
902
+ })).join("")
903
+ };
904
+ };
905
+ /**
776
906
  * The full range of the first `<openLiteral …>…</closeTag>` element, or null.
777
907
  * Used to locate an unkeyed sub-element (a level's `mc:AlternateContent`).
778
908
  */
@@ -892,8 +1022,24 @@ function originalCustomNumFmtFormat(numFmtElement) {
892
1022
  */
893
1023
  function collectXmlnsFromOpeningTag(elementXml) {
894
1024
  const out = {};
895
- const tagEnd = elementXml.indexOf(">");
896
- const openTag = tagEnd === -1 ? elementXml : elementXml.slice(0, tagEnd);
1025
+ let tagStart = elementXml.indexOf("<");
1026
+ while (tagStart !== -1 && (elementXml.startsWith("<?", tagStart) || elementXml.startsWith("<!--", tagStart))) {
1027
+ const endMarker = elementXml.startsWith("<!--", tagStart) ? "-->" : "?>";
1028
+ const end = elementXml.indexOf(endMarker, tagStart);
1029
+ if (end === -1) return out;
1030
+ tagStart = elementXml.indexOf("<", end + endMarker.length);
1031
+ }
1032
+ if (tagStart === -1) return out;
1033
+ let tagEnd = tagStart + 1;
1034
+ let quote = null;
1035
+ while (tagEnd < elementXml.length) {
1036
+ const character = elementXml[tagEnd];
1037
+ if (character === quote) quote = null;
1038
+ else if (quote === null && (character === "\"" || character === "'")) quote = character;
1039
+ else if (quote === null && character === ">") break;
1040
+ tagEnd += 1;
1041
+ }
1042
+ const openTag = elementXml.slice(tagStart, tagEnd);
897
1043
  const pattern = /\s(?<name>xmlns(?::[\w.-]+)?)="(?<uri>[^"]*)"/gu;
898
1044
  let match;
899
1045
  while ((match = pattern.exec(openTag)) !== null) out[match.groups["name"]] = match.groups["uri"];
@@ -1056,9 +1202,10 @@ const patchNumberingByNamespace = ({ original, currentXml, changed, added }) =>
1056
1202
  const patchNumberingDefinitions = ({ originalXml, baselineXml, currentXml }) => {
1057
1203
  const changed = collectChangedNumberingDefs(baselineXml, currentXml);
1058
1204
  const added = collectAddedNumberingDefs(baselineXml, currentXml);
1205
+ if (changed.abstractNums.size === 0 && changed.nums.size === 0 && added.abstractNums.size === 0 && added.nums.size === 0) return originalXml;
1059
1206
  const original = parseXmlDocument(originalXml);
1060
1207
  if (!original || getLocalName(original.name) !== "numbering" || !WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(original) ?? "")) return null;
1061
- if (getNamespacePrefix(original.name ?? "") !== "w") return patchNumberingByNamespace({
1208
+ if (getNamespacePrefix(original.name ?? "") !== "w" || !hasCanonicalWordprocessingPrefixes(originalXml)) return patchNumberingByNamespace({
1062
1209
  original,
1063
1210
  currentXml,
1064
1211
  changed,
@@ -0,0 +1,5 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+ //#region src/docx/serializer/blockCustomXmlSerializer.d.ts
3
+ declare const serializeBlockCustomXml: (block: document_d_exports.BlockCustomXml, serializeChild: (child: document_d_exports.BlockContent) => string) => string;
4
+ //#endregion
5
+ export { serializeBlockCustomXml };
@@ -0,0 +1,4 @@
1
+ //#region src/docx/serializer/blockCustomXmlSerializer.ts
2
+ const serializeBlockCustomXml = (block, serializeChild) => block.openingXml + block.content.map(serializeChild).join("") + block.closingXml;
3
+ //#endregion
4
+ export { serializeBlockCustomXml };
@@ -1,3 +1,4 @@
1
+ import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
1
2
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
2
3
  import { serializeBookmarkMarker } from "./markupRangeAttributes.js";
3
4
  import { serializeParagraph } from "./paragraphSerializer.js";
@@ -45,6 +46,7 @@ function serializeBlockContent(block) {
45
46
  case "paragraph": return serializeParagraph(block);
46
47
  case "table": return serializeTable(block, serializeParagraph);
47
48
  case "blockSdt": return serializeBlockSdt(block, serializeBlockContent);
49
+ case "blockCustomXml": return serializeBlockCustomXml(block, serializeBlockContent);
48
50
  case "preservedBlock": return block.xml;
49
51
  case "bookmarkStart":
50
52
  case "bookmarkEnd": return serializeBookmarkMarker(block);
@@ -2,6 +2,7 @@ import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../he
2
2
  import { isEmptyParagraph } from "../paragraphParser.js";
3
3
  import { captureVerbatimXml } from "../verbatimCapture.js";
4
4
  import { getLocalName, parseXmlDocument } from "../xmlParser.js";
5
+ import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
5
6
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
6
7
  import { serializeBookmarkMarker } from "./markupRangeAttributes.js";
7
8
  import { serializeParagraph } from "./paragraphSerializer.js";
@@ -41,6 +42,7 @@ function serializeBlock(block) {
41
42
  case "paragraph": return serializeParagraph(block);
42
43
  case "table": return serializeTable(block, serializeParagraph);
43
44
  case "blockSdt": return serializeBlockSdt(block, serializeBlock);
45
+ case "blockCustomXml": return serializeBlockCustomXml(block, serializeBlock);
44
46
  case "preservedBlock": return block.xml;
45
47
  case "bookmarkStart":
46
48
  case "bookmarkEnd": return serializeBookmarkMarker(block);
@@ -1,3 +1,4 @@
1
+ import { serializeBlockCustomXml } from "./blockCustomXmlSerializer.js";
1
2
  import { serializeBlockSdt } from "./blockSdtSerializer.js";
2
3
  import { serializeBookmarkMarker } from "./markupRangeAttributes.js";
3
4
  import { serializeParagraph } from "./paragraphSerializer.js";
@@ -43,6 +44,7 @@ function serializeBlock(block) {
43
44
  case "paragraph": return serializeParagraph(block);
44
45
  case "table": return serializeTable(block, serializeParagraph);
45
46
  case "blockSdt": return serializeBlockSdt(block, serializeBlock);
47
+ case "blockCustomXml": return serializeBlockCustomXml(block, serializeBlock);
46
48
  case "preservedBlock": return block.xml;
47
49
  case "bookmarkStart":
48
50
  case "bookmarkEnd": return serializeBookmarkMarker(block);
@@ -65,7 +67,7 @@ function serializeRequiredNoteSeparators(elementName) {
65
67
  return `<w:${elementName} w:type="separator" w:id="-1">${NOTE_SEPARATOR_BODY}<w:r><w:separator/></w:r></w:p></w:${elementName}><w:${elementName} w:type="continuationSeparator" w:id="0">${NOTE_SEPARATOR_BODY}<w:r><w:continuationSeparator/></w:r></w:p></w:${elementName}>`;
66
68
  }
67
69
  function insertNoteReferenceMark(xml, elementName) {
68
- if (xml.includes(`<w:${elementName}Ref`)) return xml;
70
+ if (new RegExp(`<(?:[\\w.-]+:)?${elementName}Ref[\\s/>]`, "u").test(xml)) return xml;
69
71
  const paragraphOpen = /<w:p(?=[\s>])[^>]*>/u.exec(xml);
70
72
  if (!paragraphOpen) return `${`<w:p><w:r><w:rPr><w:rStyle w:val="${elementName === "footnote" ? "FootnoteReference" : "EndnoteReference"}"/></w:rPr><w:${elementName}Ref/></w:r></w:p>`}${xml}`;
71
73
  const paragraphStart = paragraphOpen.index + paragraphOpen[0].length;