@stll/folio-core 0.54.2 → 0.56.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 (267) hide show
  1. package/dist/ai-edits/apply.js +185 -33
  2. package/dist/ai-edits/headless.js +3 -2
  3. package/dist/ai-edits/types.d.ts +7 -1
  4. package/dist/compare/compare.js +1 -0
  5. package/dist/compare/content-types.d.ts +3 -15
  6. package/dist/compare/reproducible-package.d.ts +0 -17
  7. package/dist/compare/reproducible-package.js +12 -18
  8. package/dist/controller/canonicalClipboard.d.ts +23 -0
  9. package/dist/controller/canonicalClipboard.js +476 -0
  10. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  11. package/dist/controller/canonicalClipboardResources.js +382 -0
  12. package/dist/controller/canonicalComposition.d.ts +53 -0
  13. package/dist/controller/canonicalComposition.js +296 -0
  14. package/dist/controller/canonicalInlineProjection.d.ts +113 -0
  15. package/dist/controller/canonicalInlineProjection.js +393 -0
  16. package/dist/controller/canonicalInput.d.ts +54 -0
  17. package/dist/controller/canonicalInput.js +377 -0
  18. package/dist/controller/canonicalInputTimer.d.ts +7 -0
  19. package/dist/controller/canonicalInputTimer.js +11 -0
  20. package/dist/controller/canonicalOperations.d.ts +22 -0
  21. package/dist/controller/canonicalOperations.js +85 -0
  22. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  23. package/dist/controller/canonicalPublicOperations.js +556 -0
  24. package/dist/controller/canonicalReview.d.ts +12 -0
  25. package/dist/controller/canonicalReview.js +19 -0
  26. package/dist/controller/canonicalSession.d.ts +167 -0
  27. package/dist/controller/canonicalSession.js +1186 -0
  28. package/dist/controller/canonicalStoryEditor.d.ts +36 -0
  29. package/dist/controller/canonicalStoryEditor.js +87 -0
  30. package/dist/controller/canonicalStructure.d.ts +16 -0
  31. package/dist/controller/canonicalStructure.js +322 -0
  32. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  33. package/dist/controller/canonicalTocSelection.js +43 -0
  34. package/dist/controller/folioEditor.d.ts +3 -10
  35. package/dist/controller/folioEditor.js +14 -4
  36. package/dist/controller/headerFooterEditorManager.d.ts +5 -0
  37. package/dist/controller/headerFooterEditorManager.js +72 -3
  38. package/dist/controller/hiddenEditorApi.d.ts +45 -0
  39. package/dist/controller/hiddenEditorApi.js +29 -8
  40. package/dist/controller/hiddenEditorManager.d.ts +13 -2
  41. package/dist/controller/hiddenEditorManager.js +639 -31
  42. package/dist/controller/layoutPipeline.d.ts +1 -1
  43. package/dist/controller/layoutPipeline.js +2 -0
  44. package/dist/controller/noteEditorManager.d.ts +5 -0
  45. package/dist/controller/noteEditorManager.js +114 -15
  46. package/dist/document-operations.d.ts +8 -1
  47. package/dist/document-operations.js +6 -4
  48. package/dist/docx/attributeRemainder.js +1 -0
  49. package/dist/docx/blockPlainText.d.ts +1 -1
  50. package/dist/docx/blockPlainText.js +11 -3
  51. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  52. package/dist/docx/canonicalResourceSave.js +27 -0
  53. package/dist/docx/canonicalSave.d.ts +29 -0
  54. package/dist/docx/canonicalSave.js +101 -0
  55. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  56. package/dist/docx/canonicalSessionInput.js +27 -0
  57. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  58. package/dist/docx/canonicalStoryRepack.js +74 -0
  59. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  60. package/dist/docx/contentTypeRegistry.js +64 -0
  61. package/dist/docx/documentParser.d.ts +1 -13
  62. package/dist/docx/documentParser.js +1 -47
  63. package/dist/docx/ensureParaIds.d.ts +7 -1
  64. package/dist/docx/ensureParaIds.js +173 -215
  65. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  66. package/dist/docx/foldedListNumberFields.js +267 -0
  67. package/dist/docx/footnoteParser.d.ts +1 -11
  68. package/dist/docx/footnoteParser.js +1 -16
  69. package/dist/docx/headerFooterParser.d.ts +1 -9
  70. package/dist/docx/headerFooterParser.js +1 -12
  71. package/dist/docx/headerFooterVerbatim.d.ts +39 -1
  72. package/dist/docx/headerFooterVerbatim.js +173 -2
  73. package/dist/docx/imageParser.d.ts +13 -1
  74. package/dist/docx/imageParser.js +20 -11
  75. package/dist/docx/metadataPrivacy.js +6 -1
  76. package/dist/docx/noteIds.d.ts +5 -0
  77. package/dist/docx/noteIds.js +6 -2
  78. package/dist/docx/noteReferenceMark.d.ts +3 -1
  79. package/dist/docx/noteReferenceMark.js +15 -14
  80. package/dist/docx/numberingParser.d.ts +3 -1
  81. package/dist/docx/numberingParser.js +17 -3
  82. package/dist/docx/packageParts.d.ts +3 -1
  83. package/dist/docx/packageParts.js +74 -19
  84. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  85. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  86. package/dist/docx/paragraphParser.js +101 -53
  87. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  88. package/dist/docx/paragraphPropertySource.js +77 -22
  89. package/dist/docx/parser.js +3 -1
  90. package/dist/docx/removeHeaderFooterParts.js +17 -6
  91. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  92. package/dist/docx/reviewHistoryNamespace.js +4 -0
  93. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  94. package/dist/docx/reviewResolutionProvenance.js +153 -0
  95. package/dist/docx/rezip.d.ts +54 -6
  96. package/dist/docx/rezip.js +835 -257
  97. package/dist/docx/runConsolidator.d.ts +2 -58
  98. package/dist/docx/runConsolidator.js +3 -175
  99. package/dist/docx/runParser.d.ts +2 -1
  100. package/dist/docx/runParser.js +156 -116
  101. package/dist/docx/saveDiagnostics.d.ts +22 -0
  102. package/dist/docx/saveDiagnostics.js +0 -0
  103. package/dist/docx/selectiveSave.d.ts +3 -1
  104. package/dist/docx/selectiveSave.js +36 -4
  105. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  106. package/dist/docx/selectiveXmlPatch.js +81 -7
  107. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  108. package/dist/docx/serializer/documentSerializer.js +29 -10
  109. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  110. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  111. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  112. package/dist/docx/serializer/noteSerializer.js +24 -8
  113. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  114. package/dist/docx/serializer/partNamespaces.js +2 -1
  115. package/dist/docx/serializer/runSerializer.js +3 -1
  116. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  117. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  118. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  119. package/dist/docx/settingsParser.d.ts +1 -2
  120. package/dist/docx/settingsParser.js +5 -6
  121. package/dist/docx/storyBlockReplay.d.ts +31 -0
  122. package/dist/docx/storyBlockReplay.js +471 -0
  123. package/dist/docx/storyPlainText.d.ts +20 -0
  124. package/dist/docx/storyPlainText.js +23 -0
  125. package/dist/docx/streamingXmlParser.d.ts +22 -1
  126. package/dist/docx/streamingXmlParser.js +49 -3
  127. package/dist/docx/structuralXmlPatch.js +18 -23
  128. package/dist/docx/styleParser.d.ts +5 -1
  129. package/dist/docx/styleParser.js +1 -1
  130. package/dist/docx/tableParser.d.ts +1 -8
  131. package/dist/docx/tableParser.js +1 -28
  132. package/dist/docx/textBoxParser.d.ts +1 -5
  133. package/dist/docx/textBoxParser.js +1 -20
  134. package/dist/docx/unzip.js +6 -1
  135. package/dist/docx/verbatimCapture.d.ts +3 -1
  136. package/dist/docx/verbatimCapture.js +49 -6
  137. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  138. package/dist/i18n/messages/ar.gen.js +656 -0
  139. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  140. package/dist/i18n/messages/cs.gen.js +656 -0
  141. package/dist/i18n/messages/de.gen.d.ts +5 -0
  142. package/dist/i18n/messages/de.gen.js +656 -0
  143. package/dist/i18n/messages/en.gen.d.ts +5 -0
  144. package/dist/i18n/messages/en.gen.js +656 -0
  145. package/dist/i18n/messages/es.gen.d.ts +5 -0
  146. package/dist/i18n/messages/es.gen.js +656 -0
  147. package/dist/i18n/messages/et.gen.d.ts +5 -0
  148. package/dist/i18n/messages/et.gen.js +656 -0
  149. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  150. package/dist/i18n/messages/fr.gen.js +656 -0
  151. package/dist/i18n/messages/he.gen.d.ts +5 -0
  152. package/dist/i18n/messages/he.gen.js +656 -0
  153. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  154. package/dist/i18n/messages/hi.gen.js +656 -0
  155. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  156. package/dist/i18n/messages/hu.gen.js +656 -0
  157. package/dist/i18n/messages/locales.d.ts +5 -0
  158. package/dist/i18n/messages/locales.js +22 -0
  159. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  160. package/dist/i18n/messages/lt.gen.js +656 -0
  161. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  162. package/dist/i18n/messages/lv.gen.js +656 -0
  163. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  164. package/dist/i18n/messages/pl.gen.js +656 -0
  165. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  166. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  167. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  168. package/dist/i18n/messages/sk.gen.js +656 -0
  169. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  170. package/dist/i18n/messages/tr.gen.js +656 -0
  171. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  172. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  173. package/dist/i18n/messages.d.ts +2 -3
  174. package/dist/i18n/messages.js +1 -19
  175. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  176. package/dist/internal/acceptedBlockProjection.js +45 -0
  177. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  178. package/dist/internal/noteMarkerAttrs.js +16 -0
  179. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  180. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  181. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  182. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  183. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  184. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  185. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  186. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  187. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  188. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  189. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  190. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  191. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  192. package/dist/managers/DocumentLoaderManager.js +9 -1
  193. package/dist/markdown/escape.d.ts +3 -1
  194. package/dist/markdown/escape.js +9 -2
  195. package/dist/markdown/renderBlock.js +20 -121
  196. package/dist/markdown/renderParagraph.js +13 -15
  197. package/dist/markdown/renderRuns.d.ts +7 -1
  198. package/dist/markdown/renderRuns.js +35 -15
  199. package/dist/markdown/renderTable.js +2 -0
  200. package/dist/markdown/types.d.ts +3 -9
  201. package/dist/prosemirror/attrs/index.d.ts +2 -12
  202. package/dist/prosemirror/attrs/index.js +22 -48
  203. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  204. package/dist/prosemirror/canonicalCommands.d.ts +121 -0
  205. package/dist/prosemirror/canonicalCommands.js +35 -0
  206. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  207. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  208. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  209. package/dist/prosemirror/clipboardIntent.js +9 -0
  210. package/dist/prosemirror/commands/pageBreak.js +15 -1
  211. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  212. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  213. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  214. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  215. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  216. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  217. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  218. package/dist/prosemirror/conversion/toProseDoc.d.ts +11 -4
  219. package/dist/prosemirror/conversion/toProseDoc.js +55 -14
  220. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  221. package/dist/prosemirror/executeEditorCommand.js +26 -0
  222. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  223. package/dist/prosemirror/extensions/core/ParagraphExtension.js +88 -48
  224. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  225. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  226. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  227. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +56 -28
  228. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  229. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  230. package/dist/prosemirror/extensions/marks/markUtils.js +72 -8
  231. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  232. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  233. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  234. package/dist/prosemirror/insertOperations.js +2 -1
  235. package/dist/prosemirror/listMarker.d.ts +1 -0
  236. package/dist/prosemirror/listMarker.js +16 -7
  237. package/dist/prosemirror/listNumbering.js +26 -5
  238. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  239. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  240. package/dist/prosemirror/markupViewNotes.js +3 -2
  241. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  242. package/dist/prosemirror/noteReferenceReview.js +5 -3
  243. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  244. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  245. package/dist/prosemirror/plugins/suggestionMode.js +57 -7
  246. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  247. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  248. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  249. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  250. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  251. package/dist/prosemirror/schema/marks.d.ts +2 -0
  252. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  253. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  254. package/dist/prosemirror/textInput.js +5 -1
  255. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  256. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  257. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  258. package/dist/prosemirror/validation.js +4 -0
  259. package/dist/types/canonicalCapabilities.d.ts +215 -0
  260. package/dist/types/canonicalCapabilities.js +212 -0
  261. package/dist/types/canonicalSave.d.ts +11 -0
  262. package/dist/types/canonicalSave.js +0 -0
  263. package/dist/types/content.d.ts +2 -2
  264. package/dist/types/docxSerialization.d.ts +14 -0
  265. package/dist/types/docxSerialization.js +7 -0
  266. package/dist/utils/createDocument.js +2 -2
  267. package/package.json +16 -4
@@ -1,62 +1,6 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { formattingEquals, isTextOnlyRun, runsMergeable } from "@stll/docx-core/ops";
2
3
  //#region src/docx/runConsolidator.d.ts
3
- /**
4
- * Every `TextFormatting` field, named so the comparison below is total.
5
- *
6
- * A field this comparison forgets is a field one of two merged runs loses,
7
- * silently and at parse time. Naming them turns a field added to the model
8
- * without a comparison into a compile error rather than a fidelity defect.
9
- */
10
- type ComparedTextFormattingField = "bold" | "boldCs" | "italic" | "italicCs" | "underline" | "strike" | "doubleStrike" | "vertAlign" | "smallCaps" | "allCaps" | "hidden" | "noProof" | "color" | "highlight" | "shading" | "fontSize" | "fontSizeCs" | "fontFamily" | "language" | "spacing" | "position" | "scale" | "kerning" | "effect" | "emphasisMark" | "emboss" | "imprint" | "outline" | "shadow" | "rtl" | "cs" | "styleId" | "preserved";
11
- type ComparedTextFormatting = document_d_exports.ExhaustiveFields<document_d_exports.TextFormatting, ComparedTextFormattingField>;
12
- /**
13
- * Check if two TextFormatting objects are equivalent
14
- *
15
- * Uses deep comparison of all properties to determine if runs
16
- * can be merged without losing formatting information.
17
- */
18
- declare function formattingEquals(a: ComparedTextFormatting | undefined, b: ComparedTextFormatting | undefined): boolean;
19
- /**
20
- * Check if a run contains only text content
21
- * (runs with special content like images, fields, etc. should not be merged)
22
- */
23
- declare function isTextOnlyRun(run: document_d_exports.Run): boolean;
24
- /**
25
- * Check if a run can be merged with another run
26
- * Runs with breaks, tabs, images, fields, etc. act as merge boundaries
27
- */
28
- declare function canMergeRun(run: document_d_exports.Run): boolean;
29
- /**
30
- * Every `Run` field, named by what a merge does with it.
31
- *
32
- * The merged run is the survivor spread whole, so a field added to the model
33
- * is carried over from one side by default. Naming each one here forces the
34
- * next field to state whether that default is right before it can compile.
35
- */
36
- type MergeDecidedRunField =
37
- /** Identical for every run. */
38
- "type" |
39
- /** Concatenated; see {@link mergeRunContent}. */
40
- "content" |
41
- /** Must be equal, and the survivor keeps it. */
42
- "formatting" |
43
- /** Must be equal, and the survivor keeps it. */
44
- "preservedAttributes" |
45
- /** Refuses the merge outright; see {@link canMergeRun}. */
46
- "propertyChanges";
47
- type MergeDecidedRun = document_d_exports.ExhaustiveFields<document_d_exports.Run, MergeDecidedRunField>;
48
- /**
49
- * May two adjacent runs become one?
50
- *
51
- * Every consolidation site asks this one question, because a merge that any
52
- * one site decides differently is a merge the next parse undoes. A run holds
53
- * three records a merged run can hold only one of: its typed formatting, the
54
- * attributes `w:r` carried that the model has no field for, and the `w:rPr`
55
- * children no reader took a value from. Merging two runs that disagree on any
56
- * of them discards the loser's copy, which is how both runs' `w:rsid*` used to
57
- * vanish at parse time — before the editor, before any gate could see it.
58
- */
59
- declare function runsMergeable(a: MergeDecidedRun, b: MergeDecidedRun): boolean;
60
4
  /**
61
5
  * Consolidate an array of runs by merging consecutive runs with identical formatting
62
6
  *
@@ -93,4 +37,4 @@ declare function getConsolidationStats(originalParagraphs: document_d_exports.Pa
93
37
  reductionPercentage: number;
94
38
  };
95
39
  //#endregion
96
- export { canMergeRun, consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
40
+ export { consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
@@ -1,180 +1,8 @@
1
1
  import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
2
2
  import { runHoldsPayload } from "./runPayload.js";
3
+ import { formattingEquals, isTextOnlyRun, mergeRunContent, runsMergeable, runsMergeable as runsMergeable$1 } from "@stll/docx-core/ops";
3
4
  //#region src/docx/runConsolidator.ts
4
5
  /**
5
- * Check if two TextFormatting objects are equivalent
6
- *
7
- * Uses deep comparison of all properties to determine if runs
8
- * can be merged without losing formatting information.
9
- */
10
- function formattingEquals(a, b) {
11
- if (!a && !b) return true;
12
- if (!a || !b) return false;
13
- if (a.bold !== b.bold) return false;
14
- if (a.boldCs !== b.boldCs) return false;
15
- if (a.italic !== b.italic) return false;
16
- if (a.italicCs !== b.italicCs) return false;
17
- if (a.strike !== b.strike) return false;
18
- if (a.doubleStrike !== b.doubleStrike) return false;
19
- if (a.smallCaps !== b.smallCaps) return false;
20
- if (a.allCaps !== b.allCaps) return false;
21
- if (a.hidden !== b.hidden) return false;
22
- if (a.noProof !== b.noProof) return false;
23
- if (a.emboss !== b.emboss) return false;
24
- if (a.imprint !== b.imprint) return false;
25
- if (a.outline !== b.outline) return false;
26
- if (a.shadow !== b.shadow) return false;
27
- if (a.rtl !== b.rtl) return false;
28
- if (a.cs !== b.cs) return false;
29
- if (a.fontSize !== b.fontSize) return false;
30
- if (a.fontSizeCs !== b.fontSizeCs) return false;
31
- if (a.spacing !== b.spacing) return false;
32
- if (a.position !== b.position) return false;
33
- if (a.scale !== b.scale) return false;
34
- if (a.kerning !== b.kerning) return false;
35
- if (a.vertAlign !== b.vertAlign) return false;
36
- if (a.highlight !== b.highlight) return false;
37
- if (a.effect !== b.effect) return false;
38
- if (a.emphasisMark !== b.emphasisMark) return false;
39
- if (a.styleId !== b.styleId) return false;
40
- if (!underlineEquals(a.underline, b.underline)) return false;
41
- if (!colorEquals(a.color, b.color)) return false;
42
- if (!shadingEquals(a.shading, b.shading)) return false;
43
- if (!fontFamilyEquals(a.fontFamily, b.fontFamily)) return false;
44
- if (!languageEquals(a.language, b.language)) return false;
45
- if (!preservedMarkupEquals(a.preserved, b.preserved)) return false;
46
- return true;
47
- }
48
- /**
49
- * Compare two verbatim sinks position by position.
50
- *
51
- * The sink is ordered — by `index`, then by source order within an index — and
52
- * the order is what puts the markup back between the same modelled siblings,
53
- * so equality is by sequence, not by set.
54
- */
55
- function preservedMarkupEquals(a, b) {
56
- const left = a?.children ?? [];
57
- const right = b?.children ?? [];
58
- if (left.length !== right.length) return false;
59
- return left.every(({ index, xml }, position) => {
60
- const other = right[position];
61
- return other !== void 0 && other.index === index && other.xml === xml;
62
- });
63
- }
64
- /**
65
- * Compare two attribute remainders as sets.
66
- *
67
- * An element cannot carry one expanded name twice, so (namespace, name) orders
68
- * a remainder totally and the sorted keys compare exactly. Order is a set
69
- * comparison rather than a sequence one because attribute order in XML says
70
- * nothing: two runs that spelled the same attributes in a different order
71
- * carry the same remainder and may still become one run.
72
- */
73
- function preservedAttributesEqual(a, b) {
74
- const left = canonicalRemainderKeys(a);
75
- const right = canonicalRemainderKeys(b);
76
- return left.length === right.length && left.every((key, index) => key === right[index]);
77
- }
78
- /** `\u0000` cannot appear in an XML name, namespace URI or attribute value. */
79
- const REMAINDER_KEY_SEPARATOR = "\0";
80
- const canonicalRemainderKeys = (attributes) => (attributes ?? []).map(({ namespace, name, value }) => [
81
- namespace ?? "",
82
- name,
83
- value
84
- ].join(REMAINDER_KEY_SEPARATOR)).sort();
85
- function languageEquals(a, b) {
86
- return a?.val === b?.val && a?.eastAsia === b?.eastAsia && a?.bidi === b?.bidi;
87
- }
88
- /**
89
- * Compare underline settings
90
- */
91
- function underlineEquals(a, b) {
92
- if (!a && !b) return true;
93
- if (!a || !b) return false;
94
- if (a.style !== b.style) return false;
95
- return colorEquals(a.color, b.color);
96
- }
97
- /**
98
- * Compare color values
99
- */
100
- function colorEquals(a, b) {
101
- if (!a && !b) return true;
102
- if (!a || !b) return false;
103
- return a.rgb === b.rgb && a.auto === b.auto && a.themeColor === b.themeColor && a.themeTint === b.themeTint && a.themeShade === b.themeShade;
104
- }
105
- /**
106
- * Compare shading properties
107
- */
108
- function shadingEquals(a, b) {
109
- if (!a && !b) return true;
110
- if (!a || !b) return false;
111
- if (a.pattern !== b.pattern) return false;
112
- if (!colorEquals(a.color, b.color)) return false;
113
- if (!colorEquals(a.fill, b.fill)) return false;
114
- return true;
115
- }
116
- /**
117
- * Compare font family settings
118
- */
119
- function fontFamilyEquals(a, b) {
120
- if (!a && !b) return true;
121
- if (!a || !b) return false;
122
- return a.ascii === b.ascii && a.hAnsi === b.hAnsi && a.eastAsia === b.eastAsia && a.cs === b.cs && a.asciiTheme === b.asciiTheme && a.hAnsiTheme === b.hAnsiTheme && a.eastAsiaTheme === b.eastAsiaTheme && a.csTheme === b.csTheme;
123
- }
124
- /**
125
- * Check if a run contains only text content
126
- * (runs with special content like images, fields, etc. should not be merged)
127
- */
128
- function isTextOnlyRun(run) {
129
- return run.content.every((c) => c.type === "text" || c.type === "softHyphen" || c.type === "noBreakHyphen");
130
- }
131
- /**
132
- * Check if run content can be merged (simple text types)
133
- */
134
- function isMergeableContent(content) {
135
- return content.type === "text" || content.type === "softHyphen" || content.type === "noBreakHyphen";
136
- }
137
- /**
138
- * Check if a run can be merged with another run
139
- * Runs with breaks, tabs, images, fields, etc. act as merge boundaries
140
- */
141
- function canMergeRun(run) {
142
- if (run.propertyChanges && run.propertyChanges.length > 0) return false;
143
- if (!runHoldsPayload(run)) return false;
144
- return run.content.every(isMergeableContent);
145
- }
146
- /**
147
- * May two adjacent runs become one?
148
- *
149
- * Every consolidation site asks this one question, because a merge that any
150
- * one site decides differently is a merge the next parse undoes. A run holds
151
- * three records a merged run can hold only one of: its typed formatting, the
152
- * attributes `w:r` carried that the model has no field for, and the `w:rPr`
153
- * children no reader took a value from. Merging two runs that disagree on any
154
- * of them discards the loser's copy, which is how both runs' `w:rsid*` used to
155
- * vanish at parse time — before the editor, before any gate could see it.
156
- */
157
- function runsMergeable(a, b) {
158
- return canMergeRun(a) && canMergeRun(b) && formattingEquals(a.formatting, b.formatting) && preservedAttributesEqual(a.preservedAttributes, b.preservedAttributes);
159
- }
160
- /**
161
- * Merge the content of two runs into a single content array
162
- */
163
- function mergeRunContent(content1, content2) {
164
- const result = [];
165
- for (const c of content1) result.push(c);
166
- const lastText = result.at(-1);
167
- const firstText = content2[0];
168
- if (lastText?.type === "text" && firstText?.type === "text") {
169
- result[result.length - 1] = {
170
- type: "text",
171
- text: lastText.text + firstText.text
172
- };
173
- for (let i = 1; i < content2.length; i++) result.push(content2[i]);
174
- } else for (const c of content2) result.push(c);
175
- return result;
176
- }
177
- /**
178
6
  * Consolidate an array of runs by merging consecutive runs with identical formatting
179
7
  *
180
8
  * @param runs - Array of runs to consolidate
@@ -200,7 +28,7 @@ function consolidateRuns(runs) {
200
28
  };
201
29
  continue;
202
30
  }
203
- if (runsMergeable(current, run)) current = {
31
+ if (runsMergeable$1(current, run)) current = {
204
32
  ...current,
205
33
  content: mergeRunContent(current.content, run.content)
206
34
  };
@@ -276,4 +104,4 @@ function getConsolidationStats(originalParagraphs, consolidatedParagraphs) {
276
104
  };
277
105
  }
278
106
  //#endregion
279
- export { canMergeRun, consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
107
+ export { consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
@@ -60,7 +60,8 @@ declare const isBaselineVertAlign: (vertAlign: document_d_exports.TextFormatting
60
60
  * it beat are not kept.
61
61
  *
62
62
  * An element that states nothing at all still yields a record: `undefined` here
63
- * means the owner carried no `w:rPr`, never that the one it carried was empty.
63
+ * means the owner carried no `w:rPr`, or a paragraph-mark carrier contained
64
+ * only metadata owned by the paragraph. An authored empty set remains {}.
64
65
  */
65
66
  declare function parseRunProperties(rPr: XmlElement | null, theme: document_d_exports.Theme | null, owner: RunPropertyOwner): document_d_exports.TextFormatting | undefined;
66
67
  /**
@@ -9,6 +9,7 @@ import { imageRawXmlFingerprint } from "./imageRawXml.js";
9
9
  import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, TextEffectSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
10
10
  import { preserveRunChild } from "./preservedRunContent.js";
11
11
  import { standalonePreviewLedger } from "./previewBudget.js";
12
+ import { parseBoundaryJoins } from "./reviewResolutionProvenance.js";
12
13
  import { parseShading } from "./shadingParser.js";
13
14
  import { captureShapeAlternateContent } from "./shapeAlternateContent.js";
14
15
  import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
@@ -22,6 +23,25 @@ import { WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, findAllDee
22
23
  import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
23
24
  //#region src/docx/runParser.ts
24
25
  /**
26
+ * Run Parser - Parse text runs (w:r) with complete formatting
27
+ *
28
+ * A run is a contiguous region of text with the same character formatting.
29
+ * Runs can contain:
30
+ * - Text (w:t)
31
+ * - Tabs (w:tab)
32
+ * - Line breaks (w:br)
33
+ * - Symbols (w:sym)
34
+ * - Footnote/endnote references
35
+ * - Field characters
36
+ * - Drawings/images (w:drawing)
37
+ * - And more...
38
+ *
39
+ * OOXML Reference:
40
+ * - Run: w:r
41
+ * - Run properties: w:rPr
42
+ * - Text content: w:t
43
+ */
44
+ /**
25
45
  * Sanity cap on `w:lang` `@w:val`/`@w:eastAsia`/`@w:bidi` tag length. BCP-47
26
46
  * tags top out well under this; a hostile/corrupt tag here would drive the
27
47
  * hyphenation dictionary lookup and segmenter cache keying with an
@@ -81,6 +101,11 @@ const OWNED_BY_A_SIBLING_RECORD = {
81
101
  reader: "runParser#parseRun"
82
102
  }) },
83
103
  paragraphMark: {
104
+ specVanish: ownedElsewhere({
105
+ container: "run-properties",
106
+ child: "specVanish",
107
+ reader: "paragraphProperties#parseParagraphProperties"
108
+ }),
84
109
  ins: ownedElsewhere({
85
110
  container: "run-properties",
86
111
  child: "ins",
@@ -354,7 +379,6 @@ const RUN_PROPERTY_HANDLERS = {
354
379
  },
355
380
  paragraphMark: {
356
381
  ...RUN_PROPERTY_BASE_HANDLERS,
357
- specVanish: readLastOccurrenceOnly(() => void 0),
358
382
  ...OWNED_BY_A_SIBLING_RECORD.paragraphMark
359
383
  },
360
384
  standalone: {
@@ -386,7 +410,8 @@ const RUN_PROPERTY_HANDLERS = {
386
410
  * it beat are not kept.
387
411
  *
388
412
  * An element that states nothing at all still yields a record: `undefined` here
389
- * means the owner carried no `w:rPr`, never that the one it carried was empty.
413
+ * means the owner carried no `w:rPr`, or a paragraph-mark carrier contained
414
+ * only metadata owned by the paragraph. An authored empty set remains {}.
390
415
  */
391
416
  function parseRunProperties(rPr, theme, owner) {
392
417
  if (!rPr) return;
@@ -403,6 +428,8 @@ function parseRunProperties(rPr, theme, owner) {
403
428
  }
404
429
  });
405
430
  if (preserved) formatting.preserved = preserved;
431
+ const children = getChildElements(rPr);
432
+ if (owner === RUN_PROPERTY_OWNERS.paragraphMark && Object.keys(formatting).length === 0 && children.length > 0 && children.every((child) => WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(child) ?? "") && Object.hasOwn(OWNED_BY_A_SIBLING_RECORD.paragraphMark, getLocalName(child.name)))) return;
406
433
  return formatting;
407
434
  }
408
435
  function parseRunPropertyChanges(rPr, theme, currentFormatting) {
@@ -413,6 +440,8 @@ function parseRunPropertyChanges(rPr, theme, currentFormatting) {
413
440
  type: "runPropertyChange",
414
441
  info: parsePropertyChangeInfo(changeElement)
415
442
  };
443
+ const boundaryJoins = parseBoundaryJoins(changeElement);
444
+ if (boundaryJoins !== void 0) change.boundaryJoins = boundaryJoins;
416
445
  const previousFormatting = parseRunProperties(previousRPr, theme, RUN_PROPERTY_OWNERS.standalone);
417
446
  if (previousFormatting) change.previousFormatting = previousFormatting;
418
447
  if (currentFormatting) change.currentFormatting = currentFormatting;
@@ -602,130 +631,141 @@ function parseDrawingContent(element, rels, media, previews) {
602
631
  function parseRunContents(runElement, rels, media, previews, rootXmlns = {}) {
603
632
  const contents = [];
604
633
  const children = getChildElements(runElement);
605
- for (const child of children) switch (getLocalName(child.name)) {
606
- case "t":
607
- contents.push(parseTextContent(child));
608
- break;
609
- case "tab":
610
- contents.push(parseTabContent());
611
- break;
612
- case "ptab":
613
- contents.push(parsePositionalTabContent(child));
614
- break;
615
- case "br":
616
- contents.push(parseBreakContent(child));
617
- break;
618
- case "sym":
619
- contents.push(parseSymbolContent(child));
620
- break;
621
- case "footnoteReference":
622
- contents.push(parseFootnoteReference(child));
623
- break;
624
- case "endnoteReference":
625
- contents.push(parseEndnoteReference(child));
626
- break;
627
- case "fldChar":
628
- contents.push(parseFieldChar(child));
629
- break;
630
- case "instrText":
631
- contents.push(parseInstrText(child));
632
- break;
633
- case "softHyphen":
634
- contents.push({ type: "softHyphen" });
635
- break;
636
- case "noBreakHyphen":
637
- contents.push({ type: "noBreakHyphen" });
638
- break;
639
- case "drawing": {
640
- const drawing = parseDrawingContent(child, rels, media, previews);
641
- if (drawing) contents.push(drawing);
642
- break;
643
- }
644
- case "pict": {
645
- const vmlDrawing = parseVmlImageContent(child, rels, media, previews, rootXmlns);
646
- if (vmlDrawing) {
647
- contents.push(vmlDrawing);
634
+ for (const child of children) {
635
+ const localName = getLocalName(child.name);
636
+ switch (localName) {
637
+ case "t":
638
+ contents.push(parseTextContent(child));
639
+ break;
640
+ case "tab":
641
+ contents.push(parseTabContent());
642
+ break;
643
+ case "ptab":
644
+ contents.push(parsePositionalTabContent(child));
645
+ break;
646
+ case "br":
647
+ contents.push(parseBreakContent(child));
648
+ break;
649
+ case "sym":
650
+ contents.push(parseSymbolContent(child));
651
+ break;
652
+ case "footnoteReference":
653
+ contents.push(parseFootnoteReference(child));
654
+ break;
655
+ case "endnoteReference":
656
+ contents.push(parseEndnoteReference(child));
657
+ break;
658
+ case "footnoteRef":
659
+ case "endnoteRef":
660
+ if (WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(child) ?? "")) contents.push({
661
+ type: "noteMarker",
662
+ kind: localName === "footnoteRef" ? "footnote" : "endnote"
663
+ });
664
+ else contents.push(preserveRunChild(child));
665
+ break;
666
+ case "fldChar":
667
+ contents.push(parseFieldChar(child));
668
+ break;
669
+ case "instrText":
670
+ contents.push(parseInstrText(child));
671
+ break;
672
+ case "softHyphen":
673
+ contents.push({ type: "softHyphen" });
674
+ break;
675
+ case "noBreakHyphen":
676
+ contents.push({ type: "noBreakHyphen" });
677
+ break;
678
+ case "drawing": {
679
+ const drawing = parseDrawingContent(child, rels, media, previews);
680
+ if (drawing) contents.push(drawing);
648
681
  break;
649
682
  }
650
- if (shouldPreserveRawVmlPict(child)) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
651
- break;
652
- }
653
- case "object": {
654
- const objectPreview = parseVmlImageContent(child, rels, media, previews, rootXmlns);
655
- contents.push(objectPreview ?? preserveRunChild(child));
656
- break;
657
- }
658
- case "rPr": break;
659
- case "commentReference": break;
660
- case "lastRenderedPageBreak":
661
- contents.push({ type: "renderedPageBreak" });
662
- break;
663
- case "cr":
664
- contents.push({
665
- type: "break",
666
- sourceElement: "cr"
667
- });
668
- break;
669
- case "AlternateContent": {
670
- const alternateChildren = getChildElements(child);
671
- const choiceEl = alternateChildren.find((el) => getLocalName(el.name) === "Choice");
672
- const fallbackEl = alternateChildren.find((el) => getLocalName(el.name) === "Fallback");
673
- const contentsBeforeAlternate = contents.length;
674
- const choiceTextBoxDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)) : void 0;
675
- const groupedChoiceDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && findAllDeep(element, "wpg", "wgp").length > 0) : void 0;
676
- if (groupedChoiceDrawing) {
677
- const groupedDrawing = parseDrawingContent(groupedChoiceDrawing, rels, media, previews);
678
- if (groupedDrawing?.type === "drawing" && groupedDrawing.rawXmlMode === DRAWING_RAW_XML_MODES.PREVIEW_ONLY && groupedDrawing.image.src) {
679
- contents.push({
680
- ...groupedDrawing,
681
- rawXml: captureVerbatimXml(child)
682
- });
683
+ case "pict": {
684
+ const vmlDrawing = parseVmlImageContent(child, rels, media, previews, rootXmlns);
685
+ if (vmlDrawing) {
686
+ contents.push(vmlDrawing);
683
687
  break;
684
688
  }
689
+ if (shouldPreserveRawVmlPict(child)) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
690
+ break;
685
691
  }
686
- const fallbackPict = fallbackEl ? getChildElements(fallbackEl).find((el) => getLocalName(el.name) === "pict") : void 0;
687
- const fallbackVml = fallbackPict && !choiceTextBoxDrawing ? parseVmlImageContent(fallbackPict, rels, media, previews, rootXmlns) : null;
688
- if (fallbackVml?.image.src) {
689
- fallbackVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
690
- contents.push(fallbackVml);
692
+ case "object": {
693
+ const objectPreview = parseVmlImageContent(child, rels, media, previews, rootXmlns);
694
+ contents.push(objectPreview ?? preserveRunChild(child));
691
695
  break;
692
696
  }
693
- const targetEl = selectAlternateContentBranch(child);
694
- if (targetEl) for (const innerChild of getChildElements(targetEl)) {
695
- const innerName = getLocalName(innerChild.name);
696
- if (innerName === "drawing") {
697
- const innerDrawing = parseDrawingContent(innerChild, rels, media, previews);
698
- if (innerDrawing) {
699
- if (innerDrawing.type === "drawing" && (innerDrawing.rawXml !== void 0 || !innerDrawing.image.src)) innerDrawing.rawXml = captureVerbatimXml(child);
700
- if (innerDrawing.type === "shape") {
701
- const alternateContent = captureShapeAlternateContent({
702
- shape: innerDrawing.shape,
703
- alternateContent: child,
704
- branch: targetEl,
705
- drawing: innerChild
706
- });
707
- if (alternateContent) innerDrawing.alternateContent = alternateContent;
708
- }
709
- contents.push(innerDrawing);
710
- }
711
- } else if (innerName === "pict") {
712
- const innerVml = parseVmlImageContent(innerChild, rels, media, previews, rootXmlns);
713
- if (innerVml) {
714
- innerVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
715
- contents.push(innerVml);
697
+ case "rPr": break;
698
+ case "commentReference": break;
699
+ case "lastRenderedPageBreak":
700
+ contents.push({ type: "renderedPageBreak" });
701
+ break;
702
+ case "cr":
703
+ contents.push({
704
+ type: "break",
705
+ sourceElement: "cr"
706
+ });
707
+ break;
708
+ case "AlternateContent": {
709
+ const alternateChildren = getChildElements(child);
710
+ const choiceEl = alternateChildren.find((el) => getLocalName(el.name) === "Choice");
711
+ const fallbackEl = alternateChildren.find((el) => getLocalName(el.name) === "Fallback");
712
+ const contentsBeforeAlternate = contents.length;
713
+ const choiceTextBoxDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)) : void 0;
714
+ const groupedChoiceDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && findAllDeep(element, "wpg", "wgp").length > 0) : void 0;
715
+ if (groupedChoiceDrawing) {
716
+ const groupedDrawing = parseDrawingContent(groupedChoiceDrawing, rels, media, previews);
717
+ if (groupedDrawing?.type === "drawing" && groupedDrawing.rawXmlMode === DRAWING_RAW_XML_MODES.PREVIEW_ONLY && groupedDrawing.image.src) {
718
+ contents.push({
719
+ ...groupedDrawing,
720
+ rawXml: captureVerbatimXml(child)
721
+ });
722
+ break;
716
723
  }
717
- } else contents.push(...parseRunContents({
718
- ...targetEl,
719
- elements: [innerChild]
720
- }, rels, media, previews, rootXmlns));
724
+ }
725
+ const fallbackPict = fallbackEl ? getChildElements(fallbackEl).find((el) => getLocalName(el.name) === "pict") : void 0;
726
+ const fallbackVml = fallbackPict && !choiceTextBoxDrawing ? parseVmlImageContent(fallbackPict, rels, media, previews, rootXmlns) : null;
727
+ if (fallbackVml?.image.src) {
728
+ fallbackVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
729
+ contents.push(fallbackVml);
730
+ break;
731
+ }
732
+ const targetEl = selectAlternateContentBranch(child);
733
+ if (targetEl) for (const innerChild of getChildElements(targetEl)) {
734
+ const innerName = getLocalName(innerChild.name);
735
+ if (innerName === "drawing") {
736
+ const innerDrawing = parseDrawingContent(innerChild, rels, media, previews);
737
+ if (innerDrawing) {
738
+ if (innerDrawing.type === "drawing" && (innerDrawing.rawXml !== void 0 || !innerDrawing.image.src)) innerDrawing.rawXml = captureVerbatimXml(child);
739
+ if (innerDrawing.type === "shape") {
740
+ const alternateContent = captureShapeAlternateContent({
741
+ shape: innerDrawing.shape,
742
+ alternateContent: child,
743
+ branch: targetEl,
744
+ drawing: innerChild
745
+ });
746
+ if (alternateContent) innerDrawing.alternateContent = alternateContent;
747
+ }
748
+ contents.push(innerDrawing);
749
+ }
750
+ } else if (innerName === "pict") {
751
+ const innerVml = parseVmlImageContent(innerChild, rels, media, previews, rootXmlns);
752
+ if (innerVml) {
753
+ innerVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
754
+ contents.push(innerVml);
755
+ }
756
+ } else contents.push(...parseRunContents({
757
+ ...targetEl,
758
+ elements: [innerChild]
759
+ }, rels, media, previews, rootXmlns));
760
+ }
761
+ const hasTextBoxBranch = [choiceEl, fallbackEl].some((branch) => getChildElements(branch).some((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)));
762
+ if (contents.length === contentsBeforeAlternate && !hasTextBoxBranch) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
763
+ break;
721
764
  }
722
- const hasTextBoxBranch = [choiceEl, fallbackEl].some((branch) => getChildElements(branch).some((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)));
723
- if (contents.length === contentsBeforeAlternate && !hasTextBoxBranch) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
724
- break;
765
+ default:
766
+ contents.push(preserveRunChild(child));
767
+ break;
725
768
  }
726
- default:
727
- contents.push(preserveRunChild(child));
728
- break;
729
769
  }
730
770
  return contents;
731
771
  }
@@ -0,0 +1,22 @@
1
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
2
+ //#region src/docx/saveDiagnostics.d.ts
3
+ /** Save diagnostics describe fidelity fallbacks without including document content. */
4
+ type SaveDiagnostic = {
5
+ type: "sourceReplayMismatch";
6
+ part: string;
7
+ } | {
8
+ type: "selectiveSaveRefused";
9
+ part: string;
10
+ } | {
11
+ type: "sourceReplayUnavailable";
12
+ part: string;
13
+ } | {
14
+ type: "canonicalResourceReplacement";
15
+ gap: typeof CANONICAL_GAP.resourceReplacement;
16
+ part: string;
17
+ };
18
+ type SaveDiagnosticOptions = {
19
+ onDiagnostic?: ((diagnostic: SaveDiagnostic) => void) | undefined;
20
+ };
21
+ //#endregion
22
+ export { SaveDiagnostic, SaveDiagnosticOptions };
File without changes
@@ -1,6 +1,8 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { SaveDiagnosticOptions } from "./saveDiagnostics.js";
3
+ import { DocumentBodyAuthorityOptions } from "./serializer/documentSerializer.js";
2
4
  //#region src/docx/selectiveSave.d.ts
3
- type SelectiveSaveOptions = {
5
+ type SelectiveSaveOptions = SaveDiagnosticOptions & DocumentBodyAuthorityOptions & {
4
6
  /** Changed paragraph IDs to selectively patch */
5
7
  changedParaIds: Set<string>;
6
8
  /** Whether paragraph membership, order, or block structure changed. */