@stll/folio-core 0.43.0 → 0.45.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 (237) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.js +2 -2
  2. package/dist/ai-edits/headless.js +7 -5
  3. package/dist/ai-edits/index.d.ts +2 -2
  4. package/dist/ai-edits/index.js +2 -2
  5. package/dist/ai-edits/snapshot.js +13 -9
  6. package/dist/compare/content-alignment.js +94 -54
  7. package/dist/compare/inline-atoms.js +34 -20
  8. package/dist/compare/style-resources.js +6 -0
  9. package/dist/content-controls/mutateContentControls.js +4 -2
  10. package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
  11. package/dist/document-operations.js +14 -3
  12. package/dist/docx/appVersionNormalization.d.ts +0 -18
  13. package/dist/docx/blockContentParser.js +8 -0
  14. package/dist/docx/blockRangeMarkers.d.ts +36 -0
  15. package/dist/docx/blockRangeMarkers.js +59 -0
  16. package/dist/docx/bookmarkParser.d.ts +2 -20
  17. package/dist/docx/bookmarkParser.js +6 -30
  18. package/dist/docx/borderParser.d.ts +13 -0
  19. package/dist/docx/borderParser.js +71 -0
  20. package/dist/docx/builtInStyles.d.ts +165 -0
  21. package/dist/docx/builtInStyles.js +239 -0
  22. package/dist/docx/commentIdNormalization.d.ts +3 -1
  23. package/dist/docx/commentIdNormalization.js +18 -1
  24. package/dist/docx/commentParser.d.ts +2 -1
  25. package/dist/docx/commentParser.js +80 -42
  26. package/dist/docx/commentReferenceNormalization.d.ts +4 -1
  27. package/dist/docx/commentReferenceNormalization.js +23 -14
  28. package/dist/docx/commentThreadKey.d.ts +18 -0
  29. package/dist/docx/commentThreadKey.js +22 -0
  30. package/dist/docx/danglingRelationshipReferences.d.ts +15 -0
  31. package/dist/docx/danglingRelationshipReferences.js +30 -0
  32. package/dist/docx/defaultParagraphStyle.d.ts +18 -1
  33. package/dist/docx/defaultParagraphStyle.js +23 -1
  34. package/dist/docx/diagramPreview.js +87 -27
  35. package/dist/docx/documentParser.d.ts +2 -1
  36. package/dist/docx/documentParser.js +2 -2
  37. package/dist/docx/drawingUtils.d.ts +8 -1
  38. package/dist/docx/drawingUtils.js +12 -3
  39. package/dist/docx/fieldParser.js +3 -5
  40. package/dist/docx/footnoteParser.d.ts +3 -2
  41. package/dist/docx/footnoteParser.js +19 -4
  42. package/dist/docx/groupDrawingParser.js +4 -4
  43. package/dist/docx/headerFooterRefParser.d.ts +4 -3
  44. package/dist/docx/headerFooterRefParser.js +42 -12
  45. package/dist/docx/headerFooterReferenceNormalization.d.ts +4 -1
  46. package/dist/docx/headerFooterReferenceNormalization.js +5 -1
  47. package/dist/docx/hyperlinkParser.js +13 -17
  48. package/dist/docx/imageParser.d.ts +10 -2
  49. package/dist/docx/imageParser.js +80 -30
  50. package/dist/docx/imageRawXml.d.ts +14 -1
  51. package/dist/docx/imageRawXml.js +35 -11
  52. package/dist/docx/markupRangeMarker.d.ts +15 -0
  53. package/dist/docx/markupRangeMarker.js +44 -0
  54. package/dist/docx/mathToMathml.js +12 -14
  55. package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
  56. package/dist/docx/nonVisualDrawingProps.js +46 -0
  57. package/dist/docx/noteReferenceStyles.d.ts +29 -0
  58. package/dist/docx/noteReferenceStyles.js +70 -0
  59. package/dist/docx/numberingReferenceNormalization.d.ts +4 -1
  60. package/dist/docx/numberingReferenceNormalization.js +20 -1
  61. package/dist/docx/paraIdRangeNormalization.d.ts +0 -19
  62. package/dist/docx/paragraphParser.js +66 -99
  63. package/dist/docx/paragraphPropertySource.js +1 -0
  64. package/dist/docx/paragraphTextBoxEnrichment.js +3 -0
  65. package/dist/docx/paragraphTraversal.d.ts +37 -1
  66. package/dist/docx/paragraphTraversal.js +84 -1
  67. package/dist/docx/parseContext.d.ts +37 -0
  68. package/dist/docx/parseContext.js +67 -0
  69. package/dist/docx/parseWarningMessage.d.ts +6 -0
  70. package/dist/docx/parseWarningMessage.js +44 -0
  71. package/dist/docx/parser.js +83 -29
  72. package/dist/docx/previewBudget.d.ts +64 -0
  73. package/dist/docx/previewBudget.js +88 -0
  74. package/dist/docx/relsParser.d.ts +28 -11
  75. package/dist/docx/relsParser.js +26 -13
  76. package/dist/docx/revisionIdNormalization.js +96 -10
  77. package/dist/docx/rezip.js +80 -40
  78. package/dist/docx/runConsolidator.js +1 -2
  79. package/dist/docx/runParser.d.ts +8 -1
  80. package/dist/docx/runParser.js +30 -48
  81. package/dist/docx/sdtPropertiesPatch.js +24 -18
  82. package/dist/docx/sectionParser.d.ts +2 -1
  83. package/dist/docx/sectionParser.js +21 -65
  84. package/dist/docx/sectionReferenceHistory.js +2 -2
  85. package/dist/docx/selectiveSave.js +6 -6
  86. package/dist/docx/serializer/blockSdtSerializer.js +38 -26
  87. package/dist/docx/serializer/borderSerializer.d.ts +2 -3
  88. package/dist/docx/serializer/borderSerializer.js +13 -12
  89. package/dist/docx/serializer/commentSerializer.d.ts +41 -16
  90. package/dist/docx/serializer/commentSerializer.js +82 -72
  91. package/dist/docx/serializer/documentSerializer.d.ts +1 -5
  92. package/dist/docx/serializer/documentSerializer.js +6 -16
  93. package/dist/docx/serializer/fontTableSerializer.js +6 -6
  94. package/dist/docx/serializer/headerFooterSerializer.js +10 -5
  95. package/dist/docx/serializer/markupRangeAttributes.d.ts +8 -0
  96. package/dist/docx/serializer/markupRangeAttributes.js +24 -0
  97. package/dist/docx/serializer/noteSerializer.js +5 -0
  98. package/dist/docx/serializer/numberingSerializer.js +7 -6
  99. package/dist/docx/serializer/paragraphSerializer.d.ts +1 -5
  100. package/dist/docx/serializer/paragraphSerializer.js +47 -52
  101. package/dist/docx/serializer/partNamespaces.js +2 -2
  102. package/dist/docx/serializer/runSerializer.js +57 -31
  103. package/dist/docx/serializer/sectionPropertiesSerializer.js +11 -10
  104. package/dist/docx/serializer/settingsSerializer.js +4 -3
  105. package/dist/docx/serializer/stylesSerializer.js +6 -6
  106. package/dist/docx/serializer/tableSerializer.js +37 -21
  107. package/dist/docx/serializer/textFormattingSerializer.d.ts +2 -3
  108. package/dist/docx/serializer/textFormattingSerializer.js +29 -28
  109. package/dist/docx/serializer/themeSerializer.js +6 -6
  110. package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
  111. package/dist/docx/serializer/xmlUtils.d.ts +1 -2
  112. package/dist/docx/serializer/xmlUtils.js +1 -13
  113. package/dist/docx/server/boundedArchive.d.ts +12 -0
  114. package/dist/docx/server/boundedArchive.js +20 -1
  115. package/dist/docx/server/build.js +8 -1
  116. package/dist/docx/server/createBilingualDocument.js +10 -18
  117. package/dist/docx/server/extractDocxText.js +3 -4
  118. package/dist/docx/server/validateDocxConformance.js +22 -1
  119. package/dist/docx/shadingParser.d.ts +6 -0
  120. package/dist/docx/shadingParser.js +32 -0
  121. package/dist/docx/shapeParser.js +10 -8
  122. package/dist/docx/styleParser.js +13 -87
  123. package/dist/docx/styleReferenceResolution.d.ts +36 -0
  124. package/dist/docx/styleReferenceResolution.js +51 -0
  125. package/dist/docx/tableLook.d.ts +57 -0
  126. package/dist/docx/tableLook.js +63 -0
  127. package/dist/docx/tableParser.d.ts +7 -9
  128. package/dist/docx/tableParser.js +64 -110
  129. package/dist/docx/textBoxParser.js +11 -6
  130. package/dist/docx/trackedMoveRangeNormalization.d.ts +3 -1
  131. package/dist/docx/trackedMoveRangeNormalization.js +11 -21
  132. package/dist/docx/transitionalSpelling.d.ts +13 -2
  133. package/dist/docx/transitionalSpelling.js +23 -1
  134. package/dist/docx/unzip.d.ts +23 -0
  135. package/dist/docx/unzip.js +32 -22
  136. package/dist/docx/verbatimCapture.js +5 -12
  137. package/dist/docx/vmlImageParser.js +5 -4
  138. package/dist/docx/vmlPreview.d.ts +1 -3
  139. package/dist/docx/vmlPreview.js +2 -30
  140. package/dist/docx/watermarkParser.js +2 -2
  141. package/dist/docx/xmlParser.d.ts +38 -33
  142. package/dist/docx/xmlParser.js +92 -47
  143. package/dist/docx/xmlResourceLimits.d.ts +89 -9
  144. package/dist/docx/xmlResourceLimits.js +105 -24
  145. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  146. package/dist/internal/paragraphFormattingSerialization.d.ts +2 -3
  147. package/dist/internal/paragraphFormattingSerialization.js +29 -8
  148. package/dist/layout-bridge/convert/footnoteLayout.js +2 -7
  149. package/dist/layout-engine/index.d.ts +2 -2
  150. package/dist/layout-engine/index.js +2 -2
  151. package/dist/layout-engine/measure/measureBlocks.js +1 -6
  152. package/dist/layout-engine/types.d.ts +8 -2
  153. package/dist/layout-engine/types.js +35 -2
  154. package/dist/layout-painter/renderImage.js +4 -3
  155. package/dist/layout-painter/renderParagraph.js +4 -3
  156. package/dist/managers/autoSaveCodec.js +2 -8
  157. package/dist/markdown/images.js +1 -4
  158. package/dist/markdown/index.js +1 -1
  159. package/dist/markdown/internals.d.ts +6 -1
  160. package/dist/markdown/internals.js +14 -1
  161. package/dist/markdown/renderBlock.js +35 -21
  162. package/dist/markdown/renderParagraph.js +14 -5
  163. package/dist/markdown/renderRuns.js +4 -3
  164. package/dist/markdown/renderTable.js +4 -3
  165. package/dist/markdown/trailers.js +41 -7
  166. package/dist/markdown/types.d.ts +3 -7
  167. package/dist/prosemirror/attrs/index.js +71 -5
  168. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +11 -1
  169. package/dist/prosemirror/bookmarkBoundaryAttrs.js +18 -3
  170. package/dist/prosemirror/commands/image.js +1 -0
  171. package/dist/prosemirror/commands/index.d.ts +3 -3
  172. package/dist/prosemirror/commands/index.js +2 -2
  173. package/dist/prosemirror/commands/paragraph.d.ts +3 -3
  174. package/dist/prosemirror/commands/paragraph.js +2 -2
  175. package/dist/prosemirror/commentIdAllocator.js +2 -7
  176. package/dist/prosemirror/conversion/fromProseDoc.js +197 -68
  177. package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -14
  178. package/dist/prosemirror/conversion/toProseDoc.js +458 -335
  179. package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +14 -1
  180. package/dist/prosemirror/extensions/core/ParagraphExtension.js +11 -6
  181. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -3
  182. package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
  183. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +6 -2
  184. package/dist/prosemirror/extensions/features/pastedHeadingStyles.d.ts +7 -0
  185. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +74 -0
  186. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
  187. package/dist/prosemirror/extensions/marks/markUtils.d.ts +11 -3
  188. package/dist/prosemirror/extensions/marks/markUtils.js +98 -19
  189. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +7 -3
  190. package/dist/prosemirror/extensions/nodes/ImageExtension.js +6 -1
  191. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -2
  192. package/dist/prosemirror/extensions/nodes/TableExtension.js +15 -1
  193. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +8 -4
  194. package/dist/prosemirror/extensions/types.d.ts +2 -2
  195. package/dist/prosemirror/index.d.ts +3 -3
  196. package/dist/prosemirror/index.js +3 -3
  197. package/dist/prosemirror/insertOperations.d.ts +9 -2
  198. package/dist/prosemirror/insertOperations.js +9 -4
  199. package/dist/prosemirror/paragraphFormattingProvenance.d.ts +162 -0
  200. package/dist/prosemirror/paragraphFormattingProvenance.js +115 -0
  201. package/dist/prosemirror/plugins/documentStyles.d.ts +9 -1
  202. package/dist/prosemirror/plugins/documentStyles.js +11 -1
  203. package/dist/prosemirror/plugins/index.d.ts +2 -2
  204. package/dist/prosemirror/plugins/index.js +2 -2
  205. package/dist/prosemirror/plugins/revisionIds.d.ts +11 -2
  206. package/dist/prosemirror/plugins/revisionIds.js +21 -6
  207. package/dist/prosemirror/runFormattingReconciliation.js +3 -2
  208. package/dist/prosemirror/runStyleFormatting.d.ts +1 -1
  209. package/dist/prosemirror/schema/nodes.d.ts +81 -1
  210. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  211. package/dist/prosemirror/styles/styleResolver.d.ts +9 -0
  212. package/dist/prosemirror/styles/styleResolver.js +12 -0
  213. package/dist/style-engine/styleEngine.d.ts +3 -0
  214. package/dist/style-engine/styleEngine.js +3 -0
  215. package/dist/style-sets/extract.js +1 -23
  216. package/dist/style-sets/stellaStyle.js +46 -39
  217. package/dist/style-sets/styleSetNormalization.d.ts +19 -0
  218. package/dist/style-sets/styleSetNormalization.js +99 -0
  219. package/dist/types/content.d.ts +2 -2
  220. package/dist/utils/base64.d.ts +36 -0
  221. package/dist/utils/base64.js +40 -0
  222. package/dist/utils/clipboard.js +2 -1
  223. package/dist/utils/createDocument.js +145 -20
  224. package/dist/utils/headingCollector.d.ts +8 -5
  225. package/dist/utils/headingCollector.js +23 -25
  226. package/dist/utils/tableOfContentsStyle.js +9 -2
  227. package/dist/utils/units.d.ts +10 -1
  228. package/dist/utils/units.js +12 -1
  229. package/dist/utils/urlSecurity.d.ts +8 -2
  230. package/dist/utils/urlSecurity.js +21 -3
  231. package/package.json +2 -2
  232. package/dist/docx/textWhitespace.d.ts +0 -4
  233. package/dist/docx/textWhitespace.js +0 -4
  234. package/dist/layout-bridge/engine/tableWidthUtils.d.ts +0 -6
  235. package/dist/layout-bridge/engine/tableWidthUtils.js +0 -25
  236. package/dist/markdown/headings.d.ts +0 -13
  237. package/dist/markdown/headings.js +0 -20
@@ -1,4 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { ParseContext } from "./parseContext.js";
2
3
  import { XmlElement } from "./xmlParser.js";
3
4
  //#region src/docx/sectionParser.d.ts
4
5
  declare function getUnserializedSectionPropertyChildNames(props: document_d_exports.SectionProperties): readonly string[] | undefined;
@@ -8,7 +9,7 @@ declare function getUnserializedSectionPropertyChildNames(props: document_d_expo
8
9
  * @param sectPr - The w:sectPr element
9
10
  * @returns SectionProperties object
10
11
  */
11
- declare function parseSectionProperties(sectPr: XmlElement | null): document_d_exports.SectionProperties;
12
+ declare function parseSectionProperties(sectPr: XmlElement | null, context?: ParseContext): document_d_exports.SectionProperties;
12
13
  /**
13
14
  * Get page width in pixels (96 DPI)
14
15
  *
@@ -1,9 +1,10 @@
1
+ import { parseBorderSpec } from "./borderParser.js";
1
2
  import { parseFooterReference, parseHeaderReference } from "./headerFooterRefParser.js";
2
3
  import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
3
- import { BorderStyleSchema, NumberFormatSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
4
+ import { NumberFormatSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
4
5
  import { parseSectionReferenceHistory } from "./sectionReferenceHistory.js";
5
6
  import { parsePropertyChangeInfo } from "./trackedChangeInfo.js";
6
- import { findChild, findChildren, getAttribute, getChildElements, getLocalName, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
7
+ import { findChild, findChildren, getAttribute, getChildElements, getLocalName, parseBooleanElement, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
7
8
  //#region src/docx/sectionParser.ts
8
9
  /**
9
10
  * Sanity cap on `w:cols/@w:num`. Word's column picker tops out well below
@@ -49,49 +50,6 @@ function getUnserializedSectionPropertyChildNames(props) {
49
50
  return Array.isArray(descriptor?.value) ? descriptor.value : void 0;
50
51
  }
51
52
  /**
52
- * Parse a color element/attribute for page borders/background
53
- */
54
- function parseColorValue(colorStr, themeColor, themeTint, themeShade) {
55
- if (!colorStr && !themeColor) return;
56
- const color = {};
57
- if (colorStr && colorStr !== "auto") color.rgb = colorStr;
58
- else if (colorStr === "auto") color.auto = true;
59
- const validatedThemeColor = narrowEnum(themeColor, ThemeColorSlotSchema);
60
- if (validatedThemeColor) color.themeColor = validatedThemeColor;
61
- if (themeTint) color.themeTint = themeTint;
62
- if (themeShade) color.themeShade = themeShade;
63
- return Object.keys(color).length > 0 ? color : void 0;
64
- }
65
- /**
66
- * Parse a border element for page borders
67
- */
68
- function parseBorderSpec(element) {
69
- if (!element) return;
70
- const rawStyle = getAttribute(element, "w", "val") ?? "none";
71
- const border = { style: narrowEnum(rawStyle, BorderStyleSchema) ?? rawStyle };
72
- const sz = parseNumericAttribute(element, "w", "sz");
73
- if (sz !== void 0) border.size = sz;
74
- const space = parseNumericAttribute(element, "w", "space");
75
- if (space !== void 0) border.space = space;
76
- const color = parseColorValue(getAttribute(element, "w", "color"), getAttribute(element, "w", "themeColor"), getAttribute(element, "w", "themeTint"), getAttribute(element, "w", "themeShade"));
77
- if (color) border.color = color;
78
- const shadow = getAttribute(element, "w", "shadow");
79
- if (shadow === "1" || shadow === "true") border.shadow = true;
80
- const frame = getAttribute(element, "w", "frame");
81
- if (frame === "1" || frame === "true") border.frame = true;
82
- const artRelationshipId = getAttribute(element, "w", "id")?.trim();
83
- if (artRelationshipId) border.artRelationshipId = artRelationshipId;
84
- const topLeftArtRelationshipId = getAttribute(element, "w", "topLeft")?.trim();
85
- if (topLeftArtRelationshipId) border.topLeftArtRelationshipId = topLeftArtRelationshipId;
86
- const topRightArtRelationshipId = getAttribute(element, "w", "topRight")?.trim();
87
- if (topRightArtRelationshipId) border.topRightArtRelationshipId = topRightArtRelationshipId;
88
- const bottomLeftArtRelationshipId = getAttribute(element, "w", "bottomLeft")?.trim();
89
- if (bottomLeftArtRelationshipId) border.bottomLeftArtRelationshipId = bottomLeftArtRelationshipId;
90
- const bottomRightArtRelationshipId = getAttribute(element, "w", "bottomRight")?.trim();
91
- if (bottomRightArtRelationshipId) border.bottomRightArtRelationshipId = bottomRightArtRelationshipId;
92
- return border;
93
- }
94
- /**
95
53
  * Parse page orientation
96
54
  */
97
55
  function parseOrientation(orient) {
@@ -160,7 +118,7 @@ function parseLineNumberRestart(restart) {
160
118
  * @param sectPr - The w:sectPr element
161
119
  * @returns SectionProperties object
162
120
  */
163
- function parseSectionProperties(sectPr) {
121
+ function parseSectionProperties(sectPr, context) {
164
122
  const props = {};
165
123
  if (!sectPr) return props;
166
124
  const unhandledChildNames = getChildElements(sectPr).map((child) => getLocalName(child.name ?? "")).filter((name) => !serializedSectionPropertyChildNames.has(name));
@@ -199,11 +157,9 @@ function parseSectionProperties(sectPr) {
199
157
  if (num !== void 0) props.columnCount = Math.min(num, MAX_SECTION_COLUMNS);
200
158
  const space = parseNumericAttribute(cols, "w", "space");
201
159
  if (space !== void 0) props.columnSpace = space;
202
- const equalWidth = getAttribute(cols, "w", "equalWidth");
203
- if (equalWidth === "1" || equalWidth === "true") props.equalWidth = true;
204
- else if (equalWidth === "0" || equalWidth === "false") props.equalWidth = false;
205
- const sep = getAttribute(cols, "w", "sep");
206
- if (sep === "1" || sep === "true") props.separator = true;
160
+ const equalWidth = parseOnOffAttribute(cols, "w", "equalWidth");
161
+ if (equalWidth !== void 0) props.equalWidth = equalWidth;
162
+ if (parseOnOffAttribute(cols, "w", "sep") === true) props.separator = true;
207
163
  const colElements = findChildren(cols, "w", "col");
208
164
  if (colElements.length > 0) {
209
165
  props.columns = [];
@@ -235,15 +191,15 @@ function parseSectionProperties(sectPr) {
235
191
  else addUnhandledChildName("textDirection");
236
192
  }
237
193
  const bidi = findChild(sectPr, "w", "bidi");
238
- if (bidi) props.bidi = parseBooleanElement(bidi);
239
- const headerRefs = findChildren(sectPr, "w", "headerReference");
240
- if (headerRefs.length > 0) props.headerReferences = headerRefs.map((el) => parseHeaderReference(el));
241
- const footerRefs = findChildren(sectPr, "w", "footerReference");
242
- if (footerRefs.length > 0) props.footerReferences = footerRefs.map((el) => parseFooterReference(el));
194
+ if (bidi) props.bidi = parseBooleanElement(bidi, "w", context);
195
+ const headerRefs = findChildren(sectPr, "w", "headerReference").flatMap((el) => parseHeaderReference(el, context) ?? []);
196
+ if (headerRefs.length > 0) props.headerReferences = headerRefs;
197
+ const footerRefs = findChildren(sectPr, "w", "footerReference").flatMap((el) => parseFooterReference(el, context) ?? []);
198
+ if (footerRefs.length > 0) props.footerReferences = footerRefs;
243
199
  const titlePg = findChild(sectPr, "w", "titlePg");
244
- if (titlePg) props.titlePg = parseBooleanElement(titlePg);
200
+ if (titlePg) props.titlePg = parseBooleanElement(titlePg, "w", context);
245
201
  const evenAndOddHeaders = findChild(sectPr, "w", "evenAndOddHeaders");
246
- if (evenAndOddHeaders) props.evenAndOddHeaders = parseBooleanElement(evenAndOddHeaders);
202
+ if (evenAndOddHeaders) props.evenAndOddHeaders = parseBooleanElement(evenAndOddHeaders, "w", context);
247
203
  const lnNumType = findChild(sectPr, "w", "lnNumType");
248
204
  if (lnNumType) {
249
205
  props.lineNumbers = {};
@@ -272,13 +228,13 @@ function parseSectionProperties(sectPr) {
272
228
  const pgBorders = findChild(sectPr, "w", "pgBorders");
273
229
  if (pgBorders) {
274
230
  props.pageBorders = {};
275
- const topBorder = parseBorderSpec(findChild(pgBorders, "w", "top"));
231
+ const topBorder = parseBorderSpec(findChild(pgBorders, "w", "top"), context);
276
232
  if (topBorder) props.pageBorders.top = topBorder;
277
- const bottomBorder = parseBorderSpec(findChild(pgBorders, "w", "bottom"));
233
+ const bottomBorder = parseBorderSpec(findChild(pgBorders, "w", "bottom"), context);
278
234
  if (bottomBorder) props.pageBorders.bottom = bottomBorder;
279
- const leftBorder = parseBorderSpec(findChild(pgBorders, "w", "left"));
235
+ const leftBorder = parseBorderSpec(findChild(pgBorders, "w", "left"), context);
280
236
  if (leftBorder) props.pageBorders.left = leftBorder;
281
- const rightBorder = parseBorderSpec(findChild(pgBorders, "w", "right"));
237
+ const rightBorder = parseBorderSpec(findChild(pgBorders, "w", "right"), context);
282
238
  if (rightBorder) props.pageBorders.right = rightBorder;
283
239
  const display = getAttribute(pgBorders, "w", "display");
284
240
  if (display === "allPages" || display === "firstPage" || display === "notFirstPage") props.pageBorders.display = display;
@@ -315,11 +271,11 @@ function parseSectionProperties(sectPr) {
315
271
  if (Object.keys(enProps).length > 0) props.endnotePr = enProps;
316
272
  }
317
273
  const formProt = findChild(sectPr, "w", "formProt");
318
- if (formProt) props.formProtection = parseBooleanElement(formProt);
274
+ if (formProt) props.formProtection = parseBooleanElement(formProt, "w", context);
319
275
  const noEndnote = findChild(sectPr, "w", "noEndnote");
320
- if (noEndnote) props.noEndnote = parseBooleanElement(noEndnote);
276
+ if (noEndnote) props.noEndnote = parseBooleanElement(noEndnote, "w", context);
321
277
  const rtlGutter = findChild(sectPr, "w", "rtlGutter");
322
- if (rtlGutter) props.rtlGutter = parseBooleanElement(rtlGutter);
278
+ if (rtlGutter) props.rtlGutter = parseBooleanElement(rtlGutter, "w", context);
323
279
  const docGrid = findChild(sectPr, "w", "docGrid");
324
280
  if (docGrid) {
325
281
  props.docGrid = {};
@@ -1,7 +1,7 @@
1
1
  import { canonicalJson } from "../utils/canonicalJson.js";
2
- import { escapeXml } from "./serializer/xmlUtils.js";
3
2
  import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, findChildrenByNamespaceUri, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri } from "./xmlParser.js";
4
3
  import { TaggedError } from "better-result";
4
+ import { escapeXmlAttribute } from "@stll/docx-core";
5
5
  //#region src/docx/sectionReferenceHistory.ts
6
6
  const SECTION_REFERENCE_HISTORY_NAMESPACE = "urn:stella:folio:section-reference-history:1";
7
7
  const HISTORY_NAMESPACES = /* @__PURE__ */ new Set([SECTION_REFERENCE_HISTORY_NAMESPACE]);
@@ -41,7 +41,7 @@ const serializeSectionReferenceHistory = (references) => {
41
41
  if (new Set(values.map(({ type }) => type)).size !== values.length) return invalidHistory();
42
42
  return values.map(({ type, rId }) => {
43
43
  if (!rId || rId.trim() !== rId) return invalidHistory();
44
- return `<w:${kind}Reference w:type="${type}" r:id="${escapeXml(rId)}"/>`;
44
+ return `<w:${kind}Reference w:type="${type}" r:id="${escapeXmlAttribute(rId)}"/>`;
45
45
  }).join("");
46
46
  };
47
47
  const content = serializeReferences("header", references.headerReferences ?? []) + serializeReferences("footer", references.footerReferences ?? []);
@@ -8,7 +8,7 @@ import { RELATIONSHIP_TYPES } from "./relsParser.js";
8
8
  import { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_PART_LOWER, addCommentsExtendedOverride, addCommentsExtendedRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, hasUnmaterializedInlineResources, updateCoreProperties, withoutAttachedTemplate } from "./rezip.js";
9
9
  import "./selectiveSaveFlags.js";
10
10
  import { buildPatchedDocumentXml, buildPatchedNoteXml, collectParaIds, patchNumberingDefinitions } from "./selectiveXmlPatch.js";
11
- import { ensureThreadedCommentParaIds, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
11
+ import { planCommentParts, serializeComments, serializeCommentsExtended } from "./serializer/commentSerializer.js";
12
12
  import { serializeDocument } from "./serializer/documentSerializer.js";
13
13
  import { serializeEndnotes, serializeFootnotes } from "./serializer/noteSerializer.js";
14
14
  import { serializeNumberingXml } from "./serializer/numberingSerializer.js";
@@ -84,8 +84,8 @@ const commentsExtendedInfoEqual = (a, b) => {
84
84
  * - Threading changed: write the part and ensure its override + relationship,
85
85
  * bailing if the packaging files needed to wire it are absent.
86
86
  */
87
- async function patchCommentsExtended(zip, comments, updates) {
88
- const desiredXml = serializeCommentsExtended(comments);
87
+ async function patchCommentsExtended(zip, plan, updates) {
88
+ const desiredXml = serializeCommentsExtended(plan);
89
89
  const existing = findZipEntryCaseInsensitive(zip, COMMENTS_EXTENDED_PART_LOWER);
90
90
  if (!desiredXml) return existing === null;
91
91
  const baselineInfo = existing ? parseCommentsExtended(await existing.async("text")) : /* @__PURE__ */ new Map();
@@ -190,6 +190,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
190
190
  if (!validateFolioDocumentModel(doc).valid) return null;
191
191
  const comments = doc.package.document.comments ?? [];
192
192
  const hasComments = comments.length > 0;
193
+ const commentPlan = planCommentParts(comments);
193
194
  try {
194
195
  const zip = await (await import("jszip")).default.loadAsync(originalBuffer);
195
196
  for (const [path, file] of Object.entries(zip.files)) if (!file.dir && isUnsafePackagePath(path)) return null;
@@ -215,9 +216,8 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
215
216
  }
216
217
  const sourceCommentsFile = zip.file("word/comments.xml");
217
218
  if (hasComments || sourceCommentsFile) {
218
- ensureThreadedCommentParaIds(comments);
219
219
  const sourceBindings = sourceCommentsFile ? readRootNamespaceBindings(await sourceCommentsFile.async("text")) : void 0;
220
- updates.set("word/comments.xml", serializeComments(comments, sourceBindings));
220
+ updates.set("word/comments.xml", serializeComments(commentPlan, sourceBindings));
221
221
  }
222
222
  if (hasComments) {
223
223
  const ctFile = zip.file("[Content_Types].xml");
@@ -235,7 +235,7 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
235
235
  }
236
236
  }
237
237
  }
238
- if (!await patchCommentsExtended(zip, comments, updates)) return null;
238
+ if (!await patchCommentsExtended(zip, commentPlan, updates)) return null;
239
239
  await patchNumberingPart(zip, doc, updates);
240
240
  for (const [path, xml] of await collectHeaderFooterUpdates(doc, zip)) updates.set(path, xml);
241
241
  await queueSettingsUpdates(zip, updates);
@@ -1,24 +1,39 @@
1
1
  import { reconcileRawSdtPr } from "../sdtPropertiesPatch.js";
2
2
  import { isSingleWellFormedElement } from "./xmlUtils.js";
3
+ import { escapeXmlAttribute } from "@stll/docx-core";
3
4
  //#region src/docx/serializer/blockSdtSerializer.ts
4
- function escapeXmlAttr(value) {
5
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
6
- }
5
+ /**
6
+ * Block-level SDT serializer.
7
+ *
8
+ * Replays `<w:sdtPr>` (and `<w:sdtEndPr>` when present) verbatim from the
9
+ * `rawPropertiesXml` / `rawEndPropertiesXml` strings captured by the parser.
10
+ * That keeps OOXML element order intact (`CT_SdtPr` is an `xsd:sequence`,
11
+ * ECMA-376 §17.5.2) and round-trips unmodeled features — `w:dataBinding`,
12
+ * `w15:repeatingSection`, `@lastValue`, custom XML mappings — without us
13
+ * having to enumerate them.
14
+ *
15
+ * If a `BlockSdt` was constructed programmatically (no parsed snapshot to
16
+ * replay), fall back to a minimal projection from the modeled fields so the
17
+ * result is still a valid `<w:sdt>`.
18
+ *
19
+ * Sharing the helper between the document body and the header/footer
20
+ * serializers keeps body↔HF parity in one place.
21
+ */
7
22
  function serializeFallbackSdtPr(props) {
8
23
  const parts = [];
9
24
  if (props.id !== void 0) parts.push(`<w:id w:val="${props.id}"/>`);
10
- if (props.alias) parts.push(`<w:alias w:val="${escapeXmlAttr(props.alias)}"/>`);
11
- if (props.tag) parts.push(`<w:tag w:val="${escapeXmlAttr(props.tag)}"/>`);
25
+ if (props.alias) parts.push(`<w:alias w:val="${escapeXmlAttribute(props.alias)}"/>`);
26
+ if (props.tag) parts.push(`<w:tag w:val="${escapeXmlAttribute(props.tag)}"/>`);
12
27
  if (props.lock) parts.push(`<w:lock w:val="${props.lock}"/>`);
13
- if (props.placeholder) parts.push(`<w:placeholder><w:docPart w:val="${escapeXmlAttr(props.placeholder)}"/></w:placeholder>`);
28
+ if (props.placeholder) parts.push(`<w:placeholder><w:docPart w:val="${escapeXmlAttribute(props.placeholder)}"/></w:placeholder>`);
14
29
  if (props.showingPlaceholder) parts.push("<w:showingPlcHdr/>");
15
30
  switch (props.sdtType) {
16
31
  case "plainText":
17
32
  parts.push("<w:text/>");
18
33
  break;
19
34
  case "date": {
20
- const fullDateAttr = props.dateValueISO ? ` w:fullDate="${escapeXmlAttr(props.dateValueISO)}"` : "";
21
- const formatChild = props.dateFormat ? `<w:dateFormat w:val="${escapeXmlAttr(props.dateFormat)}"/>` : "";
35
+ const fullDateAttr = props.dateValueISO ? ` w:fullDate="${escapeXmlAttribute(props.dateValueISO)}"` : "";
36
+ const formatChild = props.dateFormat ? `<w:dateFormat w:val="${escapeXmlAttribute(props.dateFormat)}"/>` : "";
22
37
  if (fullDateAttr || formatChild) parts.push(`<w:date${fullDateAttr}>${formatChild}</w:date>`);
23
38
  else parts.push("<w:date/>");
24
39
  break;
@@ -26,7 +41,7 @@ function serializeFallbackSdtPr(props) {
26
41
  case "dropdown":
27
42
  case "comboBox": {
28
43
  const tag = props.sdtType === "dropdown" ? "w:dropDownList" : "w:comboBox";
29
- const items = (props.listItems ?? []).map((item) => `<w:listItem w:displayText="${escapeXmlAttr(item.displayText)}" w:value="${escapeXmlAttr(item.value)}"/>`).join("");
44
+ const items = (props.listItems ?? []).map((item) => `<w:listItem w:displayText="${escapeXmlAttribute(item.displayText)}" w:value="${escapeXmlAttribute(item.value)}"/>`).join("");
30
45
  parts.push(`<${tag}>${items}</${tag}>`);
31
46
  break;
32
47
  }
@@ -48,25 +63,22 @@ function serializeFallbackSdtPr(props) {
48
63
  }
49
64
  return `<w:sdtPr>${parts.join("")}</w:sdtPr>`;
50
65
  }
66
+ /**
67
+ * `properties.dropdownLastValue` is the only record of what was selected.
68
+ *
69
+ * The XSD default of `@w:lastValue` is the empty string, so "never selected",
70
+ * "cleared" and "selected" are three distinguishable states, and the body's
71
+ * display text is evidence for none of them: it is equally the placeholder of
72
+ * a dropdown nobody has touched, and a displayText shared by two list items
73
+ * picks the wrong sibling. The parser records the authored `@w:lastValue` and
74
+ * `setContentControlValue` records a pick; what neither wrote is no selection.
75
+ *
76
+ * `""` is a value a producer can author (`<w:listItem w:value=""/>`), so
77
+ * presence is the test, not truthiness.
78
+ */
51
79
  function extractDropdownLastValue(blockSdt) {
52
80
  if (blockSdt.properties.sdtType !== "dropdown" && blockSdt.properties.sdtType !== "comboBox") return;
53
- const modeled = blockSdt.properties.dropdownLastValue;
54
- if (modeled !== void 0) return modeled;
55
- if (blockSdt.properties.rawPropertiesXml !== void 0) return;
56
- const firstBlock = blockSdt.content[0];
57
- if (!firstBlock || firstBlock.type !== "paragraph") return;
58
- const parts = [];
59
- for (const child of firstBlock.content) if (child.type === "run") {
60
- for (const item of child.content) if (item.type === "text") parts.push(item.text);
61
- }
62
- const text = parts.join("");
63
- if (text.length === 0) return;
64
- const items = blockSdt.properties.listItems;
65
- if (items) {
66
- const match = items.find((item) => item.displayText === text);
67
- if (match) return match.value;
68
- }
69
- return text;
81
+ return blockSdt.properties.dropdownLastValue;
70
82
  }
71
83
  function extractDateFullDate(blockSdt) {
72
84
  if (blockSdt.properties.sdtType !== "date") return;
@@ -1,8 +1,7 @@
1
1
  import { document_d_exports } from "../../types/document.js";
2
2
  //#region src/docx/serializer/borderSerializer.d.ts
3
- type ExhaustiveFields<Source, Classified extends keyof Source> = Exclude<keyof Source, Classified> extends never ? Source : never;
4
3
  type ClassifiedBorderField = "style" | "color" | "size" | "space" | "shadow" | "frame" | "artRelationshipId" | "topLeftArtRelationshipId" | "topRightArtRelationshipId" | "bottomLeftArtRelationshipId" | "bottomRightArtRelationshipId";
5
- type ExhaustiveBorder = ExhaustiveFields<document_d_exports.BorderSpec, ClassifiedBorderField>;
4
+ type ExhaustiveBorder = document_d_exports.ExhaustiveFields<document_d_exports.BorderSpec, ClassifiedBorderField>;
6
5
  /**
7
6
  * Serialize a single border element (`<w:top .../>`, `<w:left .../>`, ...).
8
7
  *
@@ -19,7 +18,7 @@ type ExhaustiveBorder = ExhaustiveFields<document_d_exports.BorderSpec, Classifi
19
18
  *
20
19
  * `style` and the color values come straight from the parsed DOCX (the parser
21
20
  * casts `w:val`/`w:color` without validating the enum), so they are
22
- * untrusted and are `escapeXml`'d before re-entering XML attributes; for valid
21
+ * untrusted and are `escapeXmlAttribute`'d before re-entering XML attributes; for valid
23
22
  * documents these are enum/hex values, so escaping is a no-op.
24
23
  */
25
24
  declare function serializeBorder(input: ExhaustiveBorder | undefined, elementName: string): string;
@@ -1,4 +1,5 @@
1
- import { escapeXml, intAttr } from "./xmlUtils.js";
1
+ import { intAttr } from "./xmlUtils.js";
2
+ import { escapeXmlAttribute } from "@stll/docx-core";
2
3
  //#region src/docx/serializer/borderSerializer.ts
3
4
  /**
4
5
  * Serialize a single border element (`<w:top .../>`, `<w:left .../>`, ...).
@@ -16,30 +17,30 @@ import { escapeXml, intAttr } from "./xmlUtils.js";
16
17
  *
17
18
  * `style` and the color values come straight from the parsed DOCX (the parser
18
19
  * casts `w:val`/`w:color` without validating the enum), so they are
19
- * untrusted and are `escapeXml`'d before re-entering XML attributes; for valid
20
+ * untrusted and are `escapeXmlAttribute`'d before re-entering XML attributes; for valid
20
21
  * documents these are enum/hex values, so escaping is a no-op.
21
22
  */
22
23
  function serializeBorder(input, elementName) {
23
24
  if (!input) return "";
24
25
  const { style, color, size, space, shadow, frame, artRelationshipId, topLeftArtRelationshipId, topRightArtRelationshipId, bottomLeftArtRelationshipId, bottomRightArtRelationshipId } = input;
25
- const attrs = [`w:val="${escapeXml(style)}"`];
26
+ const attrs = [`w:val="${escapeXmlAttribute(style)}"`];
26
27
  if (size !== void 0) attrs.push(`w:sz="${intAttr(size)}"`);
27
28
  if (space !== void 0) attrs.push(`w:space="${intAttr(space)}"`);
28
29
  if (color) {
29
30
  const { auto, rgb, themeColor, themeTint, themeShade } = color;
30
31
  if (auto) attrs.push("w:color=\"auto\"");
31
- else if (rgb) attrs.push(`w:color="${escapeXml(rgb)}"`);
32
- if (themeColor) attrs.push(`w:themeColor="${escapeXml(themeColor)}"`);
33
- if (themeTint) attrs.push(`w:themeTint="${escapeXml(themeTint)}"`);
34
- if (themeShade) attrs.push(`w:themeShade="${escapeXml(themeShade)}"`);
32
+ else if (rgb) attrs.push(`w:color="${escapeXmlAttribute(rgb)}"`);
33
+ if (themeColor) attrs.push(`w:themeColor="${escapeXmlAttribute(themeColor)}"`);
34
+ if (themeTint) attrs.push(`w:themeTint="${escapeXmlAttribute(themeTint)}"`);
35
+ if (themeShade) attrs.push(`w:themeShade="${escapeXmlAttribute(themeShade)}"`);
35
36
  }
36
37
  if (shadow) attrs.push("w:shadow=\"true\"");
37
38
  if (frame) attrs.push("w:frame=\"true\"");
38
- if (artRelationshipId) attrs.push(`w:id="${escapeXml(artRelationshipId)}"`);
39
- if (topLeftArtRelationshipId) attrs.push(`w:topLeft="${escapeXml(topLeftArtRelationshipId)}"`);
40
- if (topRightArtRelationshipId) attrs.push(`w:topRight="${escapeXml(topRightArtRelationshipId)}"`);
41
- if (bottomLeftArtRelationshipId) attrs.push(`w:bottomLeft="${escapeXml(bottomLeftArtRelationshipId)}"`);
42
- if (bottomRightArtRelationshipId) attrs.push(`w:bottomRight="${escapeXml(bottomRightArtRelationshipId)}"`);
39
+ if (artRelationshipId) attrs.push(`r:id="${escapeXmlAttribute(artRelationshipId)}"`);
40
+ if (topLeftArtRelationshipId) attrs.push(`r:topLeft="${escapeXmlAttribute(topLeftArtRelationshipId)}"`);
41
+ if (topRightArtRelationshipId) attrs.push(`r:topRight="${escapeXmlAttribute(topRightArtRelationshipId)}"`);
42
+ if (bottomLeftArtRelationshipId) attrs.push(`r:bottomLeft="${escapeXmlAttribute(bottomLeftArtRelationshipId)}"`);
43
+ if (bottomRightArtRelationshipId) attrs.push(`r:bottomRight="${escapeXmlAttribute(bottomRightArtRelationshipId)}"`);
43
44
  return `<w:${elementName} ${attrs.join(" ")}/>`;
44
45
  }
45
46
  //#endregion
@@ -1,30 +1,55 @@
1
1
  import { Comment } from "../../types/content.js";
2
2
  //#region src/docx/serializer/commentSerializer.d.ts
3
3
  /**
4
- * Serialize comments array to comments.xml content. Returns a valid empty
5
- * `<w:comments/>` document for an empty array so callers can overwrite an
4
+ * What every comment part is written from: one ordered list of comment ids, and
5
+ * the facts each id carries.
6
+ *
7
+ * `word/comments.xml` and `word/commentsExtended.xml` describe the same
8
+ * comments and have to agree about which is which. They agreed by each walking
9
+ * the `Comment[]` they were handed and pairing entries up by position, so any
10
+ * disagreement about order — one part reordered, the other not — silently moved
11
+ * a comment's thread link, resolved state and paraId onto a different comment.
12
+ * Positions cannot disagree if neither part has one: both walk `order` and look
13
+ * every fact up by `w:id`, the only identity the package itself has.
14
+ */
15
+ type CommentPartPlan = {
16
+ /** The `w:id`s to write, in order, each exactly once. */
17
+ readonly order: readonly number[];
18
+ readonly byId: ReadonlyMap<number, Comment>;
19
+ /** The `w14:paraId` each comment is threaded by, minted where the model had none. */
20
+ readonly threadParaIdById: ReadonlyMap<number, string>;
21
+ /** The comments needing a `commentsExtended.xml` entry: replies, their parents, resolved comments. */
22
+ readonly threadedIds: ReadonlySet<number>;
23
+ };
24
+ /**
25
+ * Plan both comment parts from the model, in the model's own order.
26
+ *
27
+ * The order is the document's: the order `word/comments.xml` listed the
28
+ * comments in is the order it is written back in, so a save neither reshuffles
29
+ * a reviewer's threads nor hands the next parse a different `comments[]` than
30
+ * the one it read.
31
+ *
32
+ * Planning also mints the `w14:paraId` a thread needs when the model has none —
33
+ * a comment written in the editor has no Word-authored id, and without one the
34
+ * `commentsExtended.xml` link would be dropped. Minting is deterministic
35
+ * (comment id and text derived) so repeated saves mint the same id, and it
36
+ * happens here rather than at each call site because both parts must see it.
37
+ */
38
+ declare const planCommentParts: (comments: readonly Comment[]) => CommentPartPlan;
39
+ /**
40
+ * Serialize a comment plan to comments.xml content. Returns a valid empty
41
+ * `<w:comments/>` document for an empty plan so callers can overwrite an
6
42
  * existing `word/comments.xml` part when the editor has removed the last
7
43
  * comment — leaving the previous file in place would otherwise re-emit
8
44
  * the orphaned comment threads on every save.
9
45
  */
10
- declare function serializeComments(comments: Comment[], sourceBindings?: ReadonlyMap<string, string>): string;
11
- /**
12
- * Assign a deterministic `w14:paraId` to the LAST paragraph of every comment
13
- * that needs a commentsExtended entry (a reply, a reply's parent, or a resolved
14
- * comment) but has none. A document authored or loaded without comment paraIds
15
- * would otherwise have no stable key to thread through commentsExtended.xml, and
16
- * the thread link would be silently dropped. Word-authored ids are preserved;
17
- * only threaded, id-less paragraphs are filled. Deterministic (content- and
18
- * id-derived) so repeated saves mint the SAME id. MUST run before serializing
19
- * BOTH comments.xml and commentsExtended.xml so the two reference the same id.
20
- */
21
- declare function ensureThreadedCommentParaIds(comments: readonly Comment[]): void;
46
+ declare function serializeComments({ order, byId }: CommentPartPlan, sourceBindings?: ReadonlyMap<string, string>): string;
22
47
  /**
23
48
  * Serialize `commentsExtended.xml` (`w15:commentsEx`) for reply threading and
24
49
  * resolved state, or `null` when no comment needs an entry (see
25
50
  * {@link buildCommentExtendedEntries}). This is the part that makes Word render
26
51
  * a comment as a REPLY rather than a separate top-level thread.
27
52
  */
28
- declare function serializeCommentsExtended(comments: readonly Comment[]): string | null;
53
+ declare function serializeCommentsExtended(plan: CommentPartPlan): string | null;
29
54
  //#endregion
30
- export { ensureThreadedCommentParaIds, serializeComments, serializeCommentsExtended };
55
+ export { CommentPartPlan, planCommentParts, serializeComments, serializeCommentsExtended };