@stll/folio-core 0.36.0 → 0.37.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 (200) hide show
  1. package/README.md +59 -1
  2. package/dist/ai-edits/apply.js +545 -296
  3. package/dist/ai-edits/block-identity.d.ts +4 -1
  4. package/dist/ai-edits/block-identity.js +4 -1
  5. package/dist/ai-edits/blockRange.js +14 -14
  6. package/dist/ai-edits/clean-text.d.ts +40 -1
  7. package/dist/ai-edits/clean-text.js +52 -2
  8. package/dist/ai-edits/headless.d.ts +3 -1
  9. package/dist/ai-edits/headless.js +133 -71
  10. package/dist/ai-edits/index.d.ts +3 -3
  11. package/dist/ai-edits/index.js +2 -2
  12. package/dist/ai-edits/read.js +38 -20
  13. package/dist/ai-edits/snapshot.d.ts +14 -6
  14. package/dist/ai-edits/snapshot.js +99 -19
  15. package/dist/ai-edits/table-cell-mutations.js +1 -1
  16. package/dist/ai-edits/table-row-column-mutations.js +7 -0
  17. package/dist/ai-edits/table-template.d.ts +11 -1
  18. package/dist/ai-edits/table-template.js +49 -17
  19. package/dist/ai-edits/types.d.ts +45 -57
  20. package/dist/ai-suggestions/conflict.js +1 -4
  21. package/dist/ai-suggestions/text-positions.d.ts +8 -0
  22. package/dist/ai-suggestions/text-positions.js +27 -5
  23. package/dist/compare/__fixtures__/body-sequence.js +3 -4
  24. package/dist/compare/column-alignment.d.ts +13 -13
  25. package/dist/compare/column-alignment.js +78 -39
  26. package/dist/compare/compare.d.ts +4 -3
  27. package/dist/compare/compare.js +41 -5
  28. package/dist/compare/content-alignment.d.ts +95 -0
  29. package/dist/compare/content-alignment.js +1450 -0
  30. package/dist/compare/content-types.d.ts +100 -0
  31. package/dist/compare/content-types.js +0 -0
  32. package/dist/compare/content.d.ts +209 -0
  33. package/dist/compare/content.js +885 -0
  34. package/dist/compare/formatting.d.ts +5 -5
  35. package/dist/compare/formatting.js +6 -4
  36. package/dist/compare/plan.d.ts +6 -1
  37. package/dist/compare/plan.js +169 -567
  38. package/dist/compare/scenario.d.ts +2 -2
  39. package/dist/compare/types.d.ts +3 -3
  40. package/dist/compare/verification.d.ts +2 -2
  41. package/dist/compare/verification.js +25 -8
  42. package/dist/compat/eigenpal.d.ts +5 -3
  43. package/dist/compat/eigenpal.js +3 -2
  44. package/dist/controller/headerFooterEditorManager.js +14 -6
  45. package/dist/controller/hiddenEditorManager.d.ts +7 -2
  46. package/dist/controller/hiddenEditorManager.js +15 -6
  47. package/dist/controller/layoutPipeline.js +5 -3
  48. package/dist/controller/noteEditorManager.js +12 -7
  49. package/dist/display-list/build/headerFooterPrimitives.js +1 -0
  50. package/dist/display-list/build/storyPrimitives.d.ts +5 -2
  51. package/dist/display-list/build/storyPrimitives.js +60 -3
  52. package/dist/display-list/build/tablePrimitives.js +30 -0
  53. package/dist/display-list/primitives.d.ts +1 -1
  54. package/dist/document-operations.d.ts +6 -4
  55. package/dist/document-operations.js +77 -12
  56. package/dist/docx/blockContentParser.js +6 -27
  57. package/dist/docx/ensureParaIds.js +9 -4
  58. package/dist/docx/numberingParser.d.ts +3 -1
  59. package/dist/docx/numberingParser.js +4 -1
  60. package/dist/docx/paragraphParser.js +7 -6
  61. package/dist/docx/paragraphTraversal.js +30 -34
  62. package/dist/docx/rezip.d.ts +0 -8
  63. package/dist/docx/rezip.js +41 -16
  64. package/dist/docx/runParser.js +4 -2
  65. package/dist/docx/serializer/paragraphSerializer.js +2 -0
  66. package/dist/docx/serializer/runSerializer.js +5 -3
  67. package/dist/docx/serializer/trackedChangeAttributes.d.ts +3 -1
  68. package/dist/docx/serializer/trackedChangeAttributes.js +11 -1
  69. package/dist/docx/server/build.d.ts +1 -1
  70. package/dist/headless-layout.js +5 -4
  71. package/dist/index.d.ts +5 -3
  72. package/dist/index.js +3 -2
  73. package/dist/internal/headlessRevisionResolution.d.ts +23 -0
  74. package/dist/internal/headlessRevisionResolution.js +155 -0
  75. package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
  76. package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
  77. package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
  78. package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
  79. package/dist/internal/pageBreakRunPartition.d.ts +26 -0
  80. package/dist/internal/pageBreakRunPartition.js +35 -0
  81. package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
  82. package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
  83. package/dist/internal/sectionEndpointResolution.d.ts +32 -0
  84. package/dist/internal/sectionEndpointResolution.js +38 -0
  85. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  86. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
  87. package/dist/layout-bridge/convert/headerFooterLayout.js +48 -5
  88. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
  89. package/dist/layout-bridge/convert/toFlowBlocks.js +379 -75
  90. package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
  91. package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
  92. package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
  93. package/dist/layout-bridge/dom/textStreamDom.js +75 -0
  94. package/dist/layout-bridge/headerFooterLayout.js +16 -30
  95. package/dist/layout-engine/index.js +12 -3
  96. package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
  97. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
  98. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  99. package/dist/layout-engine/types.d.ts +7 -0
  100. package/dist/layout-painter/imageLayout.d.ts +1 -1
  101. package/dist/layout-painter/renderTable.js +20 -0
  102. package/dist/model.d.ts +1 -1
  103. package/dist/paged-layout/rangeProjection.js +6 -9
  104. package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
  105. package/dist/paged-layout/transactionDirtyRange.js +23 -1
  106. package/dist/prosemirror/attrs/index.d.ts +7 -3
  107. package/dist/prosemirror/attrs/index.js +113 -7
  108. package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
  109. package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
  110. package/dist/prosemirror/commands/comments.d.ts +10 -1
  111. package/dist/prosemirror/commands/comments.js +190 -59
  112. package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
  113. package/dist/prosemirror/commands/formatPainter.js +319 -63
  114. package/dist/prosemirror/commands/index.d.ts +2 -2
  115. package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
  116. package/dist/prosemirror/commands/pageBreak.js +18 -36
  117. package/dist/prosemirror/commands/propertyChangeScope.d.ts +7 -5
  118. package/dist/prosemirror/commands/propertyChangeScope.js +27 -18
  119. package/dist/prosemirror/conversion/fromProseDoc.d.ts +7 -2
  120. package/dist/prosemirror/conversion/fromProseDoc.js +809 -209
  121. package/dist/prosemirror/conversion/runShadingMark.js +2 -2
  122. package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
  123. package/dist/prosemirror/conversion/toProseDoc.js +416 -283
  124. package/dist/prosemirror/extensions/StarterKit.js +4 -0
  125. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -8
  126. package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
  127. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -1
  128. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
  129. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
  130. package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
  131. package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
  132. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
  133. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
  134. package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
  135. package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
  136. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
  137. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
  138. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
  139. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
  140. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
  141. package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
  142. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
  143. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  144. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
  145. package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
  146. package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
  147. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
  148. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
  149. package/dist/prosemirror/findReplaceSelection.js +8 -3
  150. package/dist/prosemirror/index.d.ts +3 -3
  151. package/dist/prosemirror/listMarker.d.ts +26 -1
  152. package/dist/prosemirror/listMarker.js +108 -14
  153. package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
  154. package/dist/prosemirror/pageBreakRunProjection.js +47 -0
  155. package/dist/prosemirror/paraText.js +6 -0
  156. package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
  157. package/dist/prosemirror/paragraphSpacing.js +125 -0
  158. package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
  159. package/dist/prosemirror/plugins/documentNumbering.js +18 -0
  160. package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
  161. package/dist/prosemirror/plugins/pmTextScan.js +14 -4
  162. package/dist/prosemirror/plugins/suggestionMode.js +8 -10
  163. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
  164. package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
  165. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
  166. package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
  167. package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
  168. package/dist/prosemirror/runFormattingProvenance.js +48 -0
  169. package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
  170. package/dist/prosemirror/runFormattingReconciliation.js +54 -0
  171. package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
  172. package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
  173. package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
  174. package/dist/prosemirror/runStyleFormatting.js +105 -0
  175. package/dist/prosemirror/schema/index.d.ts +3 -3
  176. package/dist/prosemirror/schema/marks.d.ts +82 -9
  177. package/dist/prosemirror/schema/marks.js +73 -1
  178. package/dist/prosemirror/schema/nodes.d.ts +18 -5
  179. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
  180. package/dist/prosemirror/styles/resolvedStyleAttrs.js +44 -23
  181. package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
  182. package/dist/prosemirror/styles/styleResolver.js +3 -3
  183. package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
  184. package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
  185. package/dist/prosemirror/tableGridMutation.d.ts +20 -0
  186. package/dist/prosemirror/tableGridMutation.js +30 -0
  187. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
  188. package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
  189. package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
  190. package/dist/prosemirror/validation.js +23 -4
  191. package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
  192. package/dist/render-dom/RenderedDomContext.js +17 -34
  193. package/dist/server.d.ts +4 -4
  194. package/dist/server.js +2 -2
  195. package/dist/style-engine/styleEngine.d.ts +1 -1
  196. package/dist/utils/clipboard.js +1 -1
  197. package/dist/utils/findReplace.js +38 -21
  198. package/dist/version-comparison.d.ts +38 -44
  199. package/dist/version-comparison.js +270 -401
  200. package/package.json +3 -2
@@ -3,29 +3,33 @@ import { copyParagraphPropertySource, getParagraphPropertySource, getParagraphPr
3
3
  import { visitDocxParagraphs } from "../../docx/paragraphTraversal.js";
4
4
  import { ShapeOutlineStyleSchema, narrowEnum } from "../../docx/parserEnums.js";
5
5
  import { DATE_UTC_ATTRIBUTE } from "../../docx/trackedChangeInfo.js";
6
+ import { createStyleEngine } from "../../style-engine/styleEngine.js";
6
7
  import { OUTLINE_STYLE_CSS_ALIASES, normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
7
8
  import { canonicalJson } from "../../utils/canonicalJson.js";
8
9
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
10
+ import { mergeTextFormatting } from "../../utils/textFormattingMerge.js";
9
11
  import { pixelsToEmu } from "../../utils/units.js";
10
- import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../attrs/index.js";
12
+ import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectPageBreakRunAttrs, expectPageBreakRunOwnerMarkAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../attrs/index.js";
11
13
  import { autospacingMatchesBase, hasAutospacingBaseSide } from "../autospacingBase.js";
12
14
  import { expectBookmarkBoundaryAttrs } from "../bookmarkBoundaryAttrs.js";
13
15
  import { paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, removeParagraphPropertyChanges } from "../commands/propertyChangeScope.js";
14
- import { applyRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
16
+ import { applyRunFormattingOverrideAttrs, buildRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
15
17
  import { resolveNumberedRefFields } from "../numberedRefFields.js";
16
18
  import { directParagraphAlignment } from "../paragraphAlignment.js";
17
19
  import { directionToBidi } from "../paragraphDirection.js";
20
+ import { directParagraphSpacing } from "../paragraphSpacing.js";
18
21
  import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
22
+ import { authoredRunFormattingFromAttrs, hasAuthoredRunFormattingProvenance } from "../runFormattingProvenance.js";
23
+ import { paragraphFormattingForRun, paragraphRunStyleContext, resolveEffectiveRunStyleFormatting, suppressParagraphMarkFormatting } from "../runStyleFormatting.js";
19
24
  import { parseShapeGeometryAdjustments } from "../shapeGeometryAdjustments.js";
20
- import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
21
25
  import { expectTextBoxAnchorAttrs } from "../textBoxAnchorAttrs.js";
22
26
  import { assertValidProseMirrorDocument } from "../validation.js";
23
- import { runShadingAttrsToShading } from "./runShadingMark.js";
27
+ import { runShadingAttrsToShading, shadingToRunShadingAttrs } from "./runShadingMark.js";
24
28
  import { decodeSdtListItems, sdtPropertiesFromAttrs, sdtPropertiesMatchAttrs } from "./sdtAttrs.js";
25
29
  import { textFormattingToMarks } from "./toProseDoc.js";
26
30
  import { panic } from "better-result";
27
- import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
28
31
  import { Fragment } from "prosemirror-model";
32
+ import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
29
33
  //#region src/prosemirror/conversion/fromProseDoc.ts
30
34
  /**
31
35
  * ProseMirror to Document Conversion
@@ -139,7 +143,7 @@ const restoreLinkedParagraphPropertySources = (content) => {
139
143
  /** Convert a ProseMirror document to the document model. */
140
144
  function fromProseDoc(pmDoc, baseDocument) {
141
145
  assertValidProseMirrorDocument(pmDoc, "Cannot convert invalid ProseMirror document to DOCX model");
142
- const blocks = extractBlocks(pmDoc);
146
+ const blocks = extractBlocks(pmDoc, "resolve", baseDocument?.package.styles ? createStyleEngine(baseDocument.package.styles) : null);
143
147
  const linkedSources = restoreLinkedParagraphPropertySources(blocks);
144
148
  if (baseDocument) restoreParagraphPropertySources(blocks, baseDocument.package.document.content, linkedSources.targets, linkedSources.sources);
145
149
  const documentBody = { content: blocks };
@@ -166,15 +170,40 @@ const hasSuggestedInsertion = (marks) => marks.some((mark) => mark.type.name ===
166
170
  * Returns the input array unchanged when there is nothing to strip so callers
167
171
  * can skip rebuilding the node.
168
172
  */
169
- function stripSuggestedInlineMarks(marks) {
173
+ function stripSuggestedInlineMarks(marks, { baseParagraphFormatting, inheritedFormatting, paragraphMarkFormatting, paragraphMarkPrecedesStyle, styleResolver }) {
170
174
  const hasSuggestedDeletion = marks.some((mark) => mark.type.name === "deletion" && isSuggestedMark(mark));
171
175
  const suggestedRunPropertyChange = marks.find((mark) => mark.type.name === "runPropertyChange" && isSuggestedMark(mark));
172
176
  if (!hasSuggestedDeletion && !suggestedRunPropertyChange) return marks;
173
177
  let next = marks.filter((mark) => !((mark.type.name === "deletion" || mark.type.name === "runPropertyChange") && isSuggestedMark(mark)));
174
178
  if (suggestedRunPropertyChange) {
175
179
  const previousFormatting = expectRunPropertyChangeMarkAttrs(suggestedRunPropertyChange).changes.at(0)?.previousFormatting;
180
+ const characterStyleMark = marks.find(({ type }) => type.name === "characterStyle");
181
+ const characterStyleAttrs = characterStyleMark ? expectCharacterStyleMarkAttrs(characterStyleMark) : void 0;
182
+ const preservedCharacterStyleAttrs = previousFormatting?.styleId !== void 0 && characterStyleAttrs?.styleId === previousFormatting.styleId ? characterStyleAttrs : void 0;
183
+ const paragraphFormatting = paragraphFormattingForRun({
184
+ marks,
185
+ context: {
186
+ baseParagraphFormatting,
187
+ paragraphFormatting: inheritedFormatting,
188
+ paragraphMarkFormatting,
189
+ paragraphMarkPrecedesStyle
190
+ },
191
+ ...previousFormatting !== void 0 ? { directFormatting: previousFormatting } : {}
192
+ });
193
+ const effectivePreviousFormatting = mergeTextFormatting(preservedCharacterStyleAttrs ? resolveEffectiveRunStyleFormatting({
194
+ marks,
195
+ paragraphFormatting,
196
+ styleResolver
197
+ }) : paragraphFormatting, previousFormatting);
176
198
  next = next.filter((mark) => !RUN_FORMATTING_MARK_NAMES.has(mark.type.name));
177
- for (const restored of textFormattingToMarks(previousFormatting)) next = restored.addToSet(next);
199
+ for (const restored of textFormattingToMarks(effectivePreviousFormatting, {
200
+ overrideFormatting: previousFormatting,
201
+ directFormatting: previousFormatting
202
+ })) next = restored.addToSet(next);
203
+ if (previousFormatting?.styleId) {
204
+ const characterStyle = suggestedRunPropertyChange.type.schema.marks["characterStyle"];
205
+ if (characterStyle) next = characterStyle.create(preservedCharacterStyleAttrs ?? { styleId: previousFormatting.styleId }).addToSet(next);
206
+ }
178
207
  }
179
208
  return next;
180
209
  }
@@ -248,18 +277,32 @@ function isSuggestedInsertedNode(node) {
248
277
  * Surviving block nodes are rebuilt with their suggested delete/merge markers
249
278
  * cleared and their children stripped.
250
279
  */
251
- function mapSuggestionStrippedNode(node) {
280
+ function mapSuggestionStrippedNode(node, formattingContext = {
281
+ baseParagraphFormatting: void 0,
282
+ inheritedFormatting: void 0,
283
+ paragraphMarkFormatting: void 0,
284
+ paragraphMarkPrecedesStyle: false,
285
+ styleResolver: null
286
+ }) {
252
287
  if (node.isInline) {
253
288
  if (hasSuggestedInsertion(node.marks)) return null;
254
- const marks = stripSuggestedInlineMarks(node.marks);
289
+ const marks = stripSuggestedInlineMarks(node.marks, formattingContext);
255
290
  return marks === node.marks ? node : node.mark(marks);
256
291
  }
257
292
  if (isSuggestedInsertedNode(node)) return null;
258
293
  const nextAttrs = stripSuggestedNodeAttrs(node);
259
294
  const children = [];
260
295
  let changed = nextAttrs !== null;
296
+ const paragraphStyleContext = node.type.name === "paragraph" ? paragraphRunStyleContext(node, formattingContext.styleResolver) : void 0;
297
+ const childFormattingContext = paragraphStyleContext ? {
298
+ baseParagraphFormatting: paragraphStyleContext.baseParagraphFormatting,
299
+ inheritedFormatting: paragraphStyleContext.paragraphFormatting,
300
+ paragraphMarkFormatting: paragraphStyleContext.paragraphMarkFormatting,
301
+ paragraphMarkPrecedesStyle: paragraphStyleContext.paragraphMarkPrecedesStyle,
302
+ styleResolver: formattingContext.styleResolver
303
+ } : formattingContext;
261
304
  node.forEach((child) => {
262
- const mapped = mapSuggestionStrippedNode(child);
305
+ const mapped = mapSuggestionStrippedNode(child, childFormattingContext);
263
306
  if (mapped === null) {
264
307
  changed = true;
265
308
  return;
@@ -279,8 +322,14 @@ function mapSuggestionStrippedNode(node) {
279
322
  * serializes as though the suggestion never happened. See the call site in
280
323
  * {@link extractBlocks} for why this is the sole serialization boundary.
281
324
  */
282
- function stripSuggestedProvenance(doc) {
283
- return mapSuggestionStrippedNode(doc) ?? doc;
325
+ function stripSuggestedProvenance(doc, styleResolver) {
326
+ return mapSuggestionStrippedNode(doc, {
327
+ baseParagraphFormatting: void 0,
328
+ inheritedFormatting: void 0,
329
+ paragraphMarkFormatting: void 0,
330
+ paragraphMarkPrecedesStyle: false,
331
+ styleResolver
332
+ }) ?? doc;
284
333
  }
285
334
  function materializeNumberedRefValues(doc) {
286
335
  const results = resolveNumberedRefFields(doc);
@@ -306,8 +355,8 @@ function materializeNumberedRefValues(doc) {
306
355
  };
307
356
  return visit(doc);
308
357
  }
309
- function extractBlocks(inputDoc, refResolution = "resolve") {
310
- const strippedDoc = stripSuggestedProvenance(inputDoc);
358
+ function extractBlocks(inputDoc, refResolution = "resolve", styleResolver = null) {
359
+ const strippedDoc = stripSuggestedProvenance(inputDoc, styleResolver);
311
360
  const pmDoc = refResolution === "resolve" ? materializeNumberedRefValues(strippedDoc) : strippedDoc;
312
361
  const blocks = [];
313
362
  const textBoxAnchorMarkers = /* @__PURE__ */ new Map();
@@ -332,25 +381,26 @@ function extractBlocks(inputDoc, refResolution = "resolve") {
332
381
  return;
333
382
  }
334
383
  if (node.type.name === "paragraph") {
335
- const paragraph = convertPMParagraph(node, documentCounts, textBoxAnchorMarkers);
384
+ const paragraph = convertPMParagraph(node, documentCounts, textBoxAnchorMarkers, styleResolver);
336
385
  prependPageBreaks(paragraph, pendingPageBreaks);
337
386
  pendingPageBreaks = 0;
338
387
  blocks.push(paragraph);
339
388
  previousStandaloneTextBox = null;
340
389
  } else if (node.type.name === "table") {
341
390
  if (pendingPageBreaks > 0 && !appendPendingPageBreaksToPreviousParagraph()) flushPendingPageBreaks();
342
- blocks.push(convertPMTable(node, documentCounts));
391
+ blocks.push(convertPMTable(node, documentCounts, styleResolver));
343
392
  previousStandaloneTextBox = null;
344
393
  } else if (node.type.name === "textBox") {
345
394
  previousStandaloneTextBox = appendTextBoxBlock(blocks, node, {
346
395
  pendingPageBreaks,
347
396
  previousStandaloneTextBox,
348
- textBoxAnchorMarkers
397
+ textBoxAnchorMarkers,
398
+ styleResolver
349
399
  });
350
400
  pendingPageBreaks = 0;
351
401
  } else if (node.type.name === "blockSdt") {
352
402
  if (pendingPageBreaks > 0 && !appendPendingPageBreaksToPreviousParagraph()) flushPendingPageBreaks();
353
- blocks.push(convertPMBlockSdt(node));
403
+ blocks.push(convertPMBlockSdt(node, styleResolver));
354
404
  previousStandaloneTextBox = null;
355
405
  }
356
406
  });
@@ -358,7 +408,7 @@ function extractBlocks(inputDoc, refResolution = "resolve") {
358
408
  removeUnresolvedTextBoxAnchors(blocks, textBoxAnchorMarkers);
359
409
  return blocks;
360
410
  }
361
- function convertPMBlockSdt(node) {
411
+ function convertPMBlockSdt(node, styleResolver) {
362
412
  const attrs = expectBlockSdtAttrs(node);
363
413
  const properties = { sdtType: attrs.sdtType };
364
414
  if (attrs.alias) properties.alias = attrs.alias;
@@ -376,7 +426,7 @@ function convertPMBlockSdt(node) {
376
426
  if (attrs.rawEndPropertiesXml) properties.rawEndPropertiesXml = attrs.rawEndPropertiesXml;
377
427
  if (attrs.rawSdtChildrenBeforeContent) properties.rawSdtChildrenBeforeContent = attrs.rawSdtChildrenBeforeContent;
378
428
  if (attrs.rawSdtChildrenAfterContent) properties.rawSdtChildrenAfterContent = attrs.rawSdtChildrenAfterContent;
379
- const extracted = extractBlocks(node.type.schema.node("doc", null, node.content), "inherit");
429
+ const extracted = extractBlocks(node.type.schema.node("doc", null, node.content), "inherit", styleResolver);
380
430
  return {
381
431
  type: "blockSdt",
382
432
  properties,
@@ -396,7 +446,7 @@ function isStillSyntheticFiller(blocks) {
396
446
  }
397
447
  function appendTextBoxBlock(blocks, node, options) {
398
448
  const attrs = expectTextBoxAttrs(node);
399
- const paragraph = convertPMTextBox(node);
449
+ const paragraph = convertPMTextBox(node, options.styleResolver);
400
450
  const previousBlock = blocks.at(-1);
401
451
  if (attrs._docxPlacement === "inlineWithPrevious" && previousBlock?.type === "paragraph") {
402
452
  appendPageBreaks(previousBlock, options.pendingPageBreaks);
@@ -536,6 +586,7 @@ function listRenderingFromAttrs(attrs) {
536
586
  if (!(attrs.listMarker != null || attrs.listIsBullet || attrs.listNumFmt != null)) return;
537
587
  return {
538
588
  marker: attrs.listMarker ?? "",
589
+ ...attrs.listMarkerTemplate != null && { markerTemplate: attrs.listMarkerTemplate },
539
590
  level: attrs.numPr?.ilvl ?? 0,
540
591
  numId,
541
592
  isBullet: attrs.listIsBullet ?? false,
@@ -586,9 +637,16 @@ function appendPageBreaks(paragraph, count) {
586
637
  /**
587
638
  * Convert a ProseMirror paragraph node to our Paragraph type
588
639
  */
589
- function convertPMParagraph(node, documentCounts, textBoxAnchorMarkers) {
640
+ function convertPMParagraph(node, documentCounts, textBoxAnchorMarkers, styleResolver = null) {
590
641
  const attrs = expectParagraphAttrs(node);
591
- let content = extractParagraphContent(node, documentCounts, attrs._emptyHyperlinks ?? void 0, textBoxAnchorMarkers, attrs.renderedPageBreakBefore === true);
642
+ const paragraphStyleContext = paragraphRunStyleContext(node, styleResolver);
643
+ let content = extractParagraphContent(node, documentCounts, attrs._emptyHyperlinks ?? void 0, textBoxAnchorMarkers, attrs.renderedPageBreakBefore === true, {
644
+ baseParagraphFormatting: paragraphStyleContext.baseParagraphFormatting,
645
+ inheritedFormatting: paragraphStyleContext.paragraphFormatting,
646
+ paragraphMarkFormatting: paragraphStyleContext.paragraphMarkFormatting,
647
+ paragraphMarkPrecedesStyle: paragraphStyleContext.paragraphMarkPrecedesStyle,
648
+ styleResolver
649
+ });
592
650
  const bookmarks = attrs.bookmarks;
593
651
  if (bookmarks && bookmarks.length > 0) {
594
652
  const starts = bookmarks.map((b) => ({
@@ -665,6 +723,7 @@ function assignBooleanToggle(result, attrs, orig, key) {
665
723
  }
666
724
  function paragraphAttrsToFormatting(attrs) {
667
725
  const directAlignment = directParagraphAlignment(attrs);
726
+ const directSpacing = directParagraphSpacing(attrs);
668
727
  const spaceBefore = Reflect.get(attrs, "spaceBefore");
669
728
  const spaceAfter = Reflect.get(attrs, "spaceAfter");
670
729
  const beforeHasAutospacingBase = hasAutospacingBaseSide(attrs._autospacingBase, "before");
@@ -673,9 +732,12 @@ function paragraphAttrsToFormatting(attrs) {
673
732
  const afterOriginalAutospacing = attrs._originalFormatting?.afterAutospacing === true;
674
733
  const beforeAutospacingEdited = beforeHasAutospacingBase ? !autospacingMatchesBase(attrs._autospacingBase, "before", spaceBefore) : beforeOriginalAutospacing && attrs._autospacingBase == null;
675
734
  const afterAutospacingEdited = afterHasAutospacingBase ? !autospacingMatchesBase(attrs._autospacingBase, "after", spaceAfter) : afterOriginalAutospacing && attrs._autospacingBase == null;
676
- const shouldSerializeSpaceBefore = typeof spaceBefore === "number" && (!beforeHasAutospacingBase || beforeAutospacingEdited);
677
- const shouldSerializeSpaceAfter = typeof spaceAfter === "number" && (!afterHasAutospacingBase || afterAutospacingEdited);
678
- const hasDirectLineSpacing = attrs.lineSpacingExplicit === true;
735
+ const beforeIsInherited = attrs.spacingFromDocDefaults?.before === true || attrs.spacingFromImplicitDefaultStyle?.before === true;
736
+ const afterIsInherited = attrs.spacingFromDocDefaults?.after === true || attrs.spacingFromImplicitDefaultStyle?.after === true;
737
+ const shouldSerializeSpaceBefore = typeof spaceBefore === "number" && (attrs.spacingExplicit?.before === true || beforeAutospacingEdited || !beforeIsInherited && !beforeHasAutospacingBase);
738
+ const shouldSerializeSpaceAfter = typeof spaceAfter === "number" && (attrs.spacingExplicit?.after === true || afterAutospacingEdited || !afterIsInherited && !afterHasAutospacingBase);
739
+ const hasDirectLineSpacing = directSpacing?.lineSpacing !== void 0;
740
+ const hasDirectLineSpacingRule = directSpacing?.lineSpacingRule !== void 0;
679
741
  if (attrs._originalFormatting) {
680
742
  const orig = attrs._originalFormatting;
681
743
  const result = { ...orig };
@@ -689,15 +751,16 @@ function paragraphAttrsToFormatting(attrs) {
689
751
  if (typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
690
752
  else delete result.spaceAfter;
691
753
  }
692
- if (attrs.spacingExplicit?.before && typeof spaceBefore === "number") result.spaceBefore = spaceBefore;
693
- if (attrs.spacingExplicit?.after && typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
694
- const originalHasDirectLineSpacing = orig.lineSpacing !== void 0 || orig.lineSpacingRule !== void 0;
695
- if (hasDirectLineSpacing || originalHasDirectLineSpacing) {
696
- if (typeof attrs.lineSpacing === "number") result.lineSpacing = attrs.lineSpacing;
697
- else Reflect.deleteProperty(result, "lineSpacing");
698
- if (attrs.lineSpacingRule) result.lineSpacingRule = attrs.lineSpacingRule;
699
- else Reflect.deleteProperty(result, "lineSpacingRule");
700
- }
754
+ if (orig.spaceBefore !== void 0 || attrs.spacingExplicit?.before) if (typeof spaceBefore === "number") result.spaceBefore = spaceBefore;
755
+ else Reflect.deleteProperty(result, "spaceBefore");
756
+ if (orig.spaceAfter !== void 0 || attrs.spacingExplicit?.after) if (typeof spaceAfter === "number") result.spaceAfter = spaceAfter;
757
+ else Reflect.deleteProperty(result, "spaceAfter");
758
+ const originalHasDirectLineSpacing = orig.lineSpacing !== void 0;
759
+ if (hasDirectLineSpacing || originalHasDirectLineSpacing) if (typeof attrs.lineSpacing === "number") result.lineSpacing = attrs.lineSpacing;
760
+ else Reflect.deleteProperty(result, "lineSpacing");
761
+ const originalHasDirectLineSpacingRule = orig.lineSpacingRule !== void 0;
762
+ if (hasDirectLineSpacingRule || originalHasDirectLineSpacingRule) if (attrs.lineSpacingRule) result.lineSpacingRule = attrs.lineSpacingRule;
763
+ else Reflect.deleteProperty(result, "lineSpacingRule");
701
764
  if (directAlignment === void 0) Reflect.deleteProperty(result, "alignment");
702
765
  else result.alignment = directAlignment;
703
766
  if (isStyleSourcedNumPr(attrs)) {
@@ -725,7 +788,7 @@ function paragraphAttrsToFormatting(attrs) {
725
788
  }
726
789
  const outlineLevel = Reflect.get(attrs, "outlineLevel");
727
790
  const bidi = directionToBidi(attrs.direction);
728
- if (!(directAlignment !== void 0 || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || hasDirectLineSpacing || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
791
+ if (!(directAlignment !== void 0 || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || hasDirectLineSpacing || hasDirectLineSpacingRule || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
729
792
  const f = {};
730
793
  if (directAlignment !== void 0) f.alignment = directAlignment;
731
794
  if (shouldSerializeSpaceBefore) f.spaceBefore = spaceBefore;
@@ -733,7 +796,7 @@ function paragraphAttrsToFormatting(attrs) {
733
796
  if (shouldSerializeSpaceAfter) f.spaceAfter = spaceAfter;
734
797
  if (afterAutospacingEdited) f.afterAutospacing = false;
735
798
  if (hasDirectLineSpacing && typeof attrs.lineSpacing === "number") f.lineSpacing = attrs.lineSpacing;
736
- if (hasDirectLineSpacing && attrs.lineSpacingRule) f.lineSpacingRule = attrs.lineSpacingRule;
799
+ if (hasDirectLineSpacingRule && attrs.lineSpacingRule) f.lineSpacingRule = attrs.lineSpacingRule;
737
800
  if (attrs.snapToGrid != null) f.snapToGrid = attrs.snapToGrid;
738
801
  if (attrs.spacingExplicit) f.spacingExplicit = attrs.spacingExplicit;
739
802
  if (attrs.indentLeft) f.indentLeft = attrs.indentLeft;
@@ -780,7 +843,14 @@ function createTrackedRunWrapper(type, info, child) {
780
843
  }
781
844
  function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false, inheritedFormattingOverride) {
782
845
  const content = [];
783
- const inheritedFormatting = inheritedFormattingOverride ?? (paragraph.type.name === "paragraph" ? expectParagraphAttrs(paragraph).defaultTextFormatting ?? void 0 : void 0);
846
+ const paragraphStyleContext = paragraph.type.name === "paragraph" ? paragraphRunStyleContext(paragraph) : void 0;
847
+ const formattingContext = inheritedFormattingOverride ?? {
848
+ baseParagraphFormatting: paragraphStyleContext?.baseParagraphFormatting,
849
+ inheritedFormatting: paragraphStyleContext?.paragraphFormatting,
850
+ paragraphMarkFormatting: paragraphStyleContext?.paragraphMarkFormatting,
851
+ paragraphMarkPrecedesStyle: paragraphStyleContext?.paragraphMarkPrecedesStyle ?? false,
852
+ styleResolver: null
853
+ };
784
854
  const sortedEmptyHyperlinks = (emptyHyperlinks ?? []).map((attrs, order) => ({
785
855
  attrs,
786
856
  order
@@ -792,6 +862,7 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
792
862
  let currentHyperlink = null;
793
863
  let currentHyperlinkKey = null;
794
864
  let currentTrackedChange;
865
+ const sourceRunOwners = /* @__PURE__ */ new WeakMap();
795
866
  const openedComments = /* @__PURE__ */ new Set();
796
867
  const commentLastOffset = /* @__PURE__ */ new Map();
797
868
  paragraph.forEach((node, offset) => {
@@ -809,6 +880,24 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
809
880
  currentHyperlinkKey = null;
810
881
  }
811
882
  };
883
+ const appendDirectRun = (node, run, coalescePlainText = false) => {
884
+ const marksKey = getMarksKey(node.marks);
885
+ const ownerId = pageBreakRunOwnerId(node);
886
+ const currentOwnerId = currentRun ? sourceRunOwners.get(currentRun) : void 0;
887
+ const currentRunIsPlainText = currentRun?.content.every((runContent) => runContent.type === "text");
888
+ const joinsOwnedSourceRun = ownerId !== void 0 && ownerId === currentOwnerId && currentRun !== null && runsShareProperties(currentRun, run);
889
+ const joinsOrdinaryText = coalescePlainText && ownerId === void 0 && currentOwnerId === void 0 && currentRunIsPlainText === true && currentMarksKey === marksKey;
890
+ if (currentRun && (joinsOwnedSourceRun || joinsOrdinaryText)) {
891
+ if (joinsOrdinaryText) {
892
+ for (const runContent of run.content) if (runContent.type === "text") appendTextToRun(currentRun, runContent.text);
893
+ } else currentRun.content.push(...run.content);
894
+ return;
895
+ }
896
+ if (currentRun) content.push(currentRun);
897
+ currentRun = run;
898
+ currentMarksKey = marksKey;
899
+ rememberSourceRunOwner(run, node, sourceRunOwners);
900
+ };
812
901
  const syncCommentRanges = (node, offset) => {
813
902
  const nodeCommentIds = getCommentMarkIds(node.marks);
814
903
  const toClose = [];
@@ -938,18 +1027,19 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
938
1027
  if (currentTrackedChange.type !== "hyperlink") panic("A tracked hyperlink lost its serialization parent");
939
1028
  if (node.type.name === "bookmarkBoundary") {
940
1029
  addNodeToHyperlink({
1030
+ ...formattingContext,
941
1031
  hyperlink: currentTrackedChange.hyperlink,
942
- inheritedFormatting,
943
- node
1032
+ node,
1033
+ sourceRunOwners
944
1034
  });
945
1035
  return;
946
1036
  }
947
1037
  const run = createTrackedChangeRun({
948
- inheritedFormatting,
1038
+ ...formattingContext,
949
1039
  marks: otherMarks,
950
1040
  node
951
1041
  });
952
- if (run) currentTrackedChange.hyperlink.children.push(run);
1042
+ if (run) appendRunToHyperlink(currentTrackedChange.hyperlink, run, node, sourceRunOwners);
953
1043
  return;
954
1044
  }
955
1045
  if (!currentTrackedChange || currentTrackedChange.type !== "direct" || currentTrackedChange.key !== trackedChangeKey) {
@@ -978,22 +1068,32 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
978
1068
  }
979
1069
  if (node.type.name === "field" || node.type.name === "structuredField") {
980
1070
  currentTrackedChange.wrapper.content.push(createFieldFromNode(node, {
1071
+ ...formattingContext,
981
1072
  marks: otherMarks,
982
1073
  textBoxAnchorMarkers
983
1074
  }));
984
1075
  return;
985
1076
  }
986
1077
  const run = createTrackedChangeRun({
987
- inheritedFormatting,
1078
+ ...formattingContext,
988
1079
  marks: otherMarks,
989
1080
  node
990
1081
  });
991
- if (run) currentTrackedChange.wrapper.content.push(run);
1082
+ if (run) appendRunToTrackedWrapper(currentTrackedChange.wrapper, run, node, sourceRunOwners);
1083
+ return;
1084
+ }
1085
+ const ownedRun = linkMark || pageBreakRunOwnerId(node) === void 0 ? null : createTrackedChangeRun({
1086
+ ...formattingContext,
1087
+ marks: node.marks,
1088
+ node
1089
+ });
1090
+ if (ownedRun) {
1091
+ appendDirectRun(node, ownedRun, node.isText);
992
1092
  return;
993
1093
  }
994
1094
  if (noteRefMark && !linkMark) {
995
1095
  flushCurrentInline();
996
- content.push(createNoteReferenceRun(noteRefMark, node.marks));
1096
+ content.push(createNoteReferenceRun(noteRefMark, node.marks, formattingContext));
997
1097
  return;
998
1098
  }
999
1099
  if (linkMark) {
@@ -1004,9 +1104,10 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
1004
1104
  currentHyperlinkKey = linkKey;
1005
1105
  }
1006
1106
  addNodeToHyperlink({
1107
+ ...formattingContext,
1007
1108
  hyperlink: currentHyperlink,
1008
- inheritedFormatting,
1009
- node
1109
+ node,
1110
+ sourceRunOwners
1010
1111
  });
1011
1112
  return;
1012
1113
  }
@@ -1025,24 +1126,24 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
1025
1126
  type: "bookmarkEnd",
1026
1127
  id: attrs.id
1027
1128
  });
1028
- } else if (node.isText) {
1029
- const marksKey = getMarksKey(node.marks);
1030
- if (currentRun && currentMarksKey === marksKey) appendTextToRun(currentRun, node.text || "");
1031
- else {
1032
- if (currentRun) content.push(currentRun);
1033
- currentRun = createRunFromText({
1034
- inheritedFormatting,
1035
- marks: node.marks,
1036
- text: node.text || ""
1037
- });
1038
- currentMarksKey = marksKey;
1039
- }
1129
+ } else if (node.isText) appendDirectRun(node, createRunFromText({
1130
+ ...formattingContext,
1131
+ marks: node.marks,
1132
+ text: node.text || ""
1133
+ }), true);
1134
+ else if (node.type.name === "pageBreakRun") {
1135
+ const { clear } = expectPageBreakRunAttrs(node);
1136
+ appendDirectRun(node, createPageBreakCarrierRun({
1137
+ ...formattingContext,
1138
+ clear,
1139
+ marks: node.marks
1140
+ }));
1040
1141
  } else if (node.type.name === "symbol") {
1041
1142
  flushCurrentInline();
1042
- content.push(createSymbolRun(node, node.marks));
1143
+ content.push(createSymbolRun(node, node.marks, formattingContext));
1043
1144
  } else if (node.type.name === "hardBreak") {
1044
1145
  flushCurrentInline();
1045
- content.push(createBreakRun(readHardBreakType(node)));
1146
+ content.push(createBreakRun(expectHardBreakAttrs(node), node.marks, formattingContext));
1046
1147
  } else if (node.type.name === "image") {
1047
1148
  flushCurrentInline();
1048
1149
  content.push(createImageRun(node));
@@ -1051,19 +1152,20 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
1051
1152
  content.push(createShapeRun(node));
1052
1153
  } else if (node.type.name === "tab") {
1053
1154
  flushCurrentInline();
1054
- content.push(createTabRun(node));
1155
+ content.push(createTabRun(node, node.marks, formattingContext));
1055
1156
  } else if (node.type.name === "renderedPageBreak") {
1056
1157
  flushCurrentInline();
1057
1158
  content.push(createRenderedPageBreakRun());
1058
1159
  } else if (node.type.name === "field" || node.type.name === "structuredField") {
1059
1160
  flushCurrentInline();
1060
1161
  content.push(createFieldFromNode(node, {
1162
+ ...formattingContext,
1061
1163
  marks: node.marks,
1062
1164
  textBoxAnchorMarkers
1063
1165
  }));
1064
1166
  } else if (node.type.name === "sdt") {
1065
1167
  flushCurrentInline();
1066
- content.push(createInlineSdtFromNode(node, textBoxAnchorMarkers, inheritedFormatting));
1168
+ content.push(createInlineSdtFromNode(node, textBoxAnchorMarkers, formattingContext));
1067
1169
  } else if (node.type.name === "math") {
1068
1170
  flushCurrentInline();
1069
1171
  content.push(createMathFromNode(node));
@@ -1099,12 +1201,20 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
1099
1201
  });
1100
1202
  return content;
1101
1203
  }
1102
- function createTrackedChangeRun({ inheritedFormatting, marks, node }) {
1204
+ function createTrackedChangeRun({ baseParagraphFormatting, inheritedFormatting, marks, node, paragraphMarkFormatting, paragraphMarkPrecedesStyle, styleResolver }) {
1205
+ const formattingContext = {
1206
+ baseParagraphFormatting,
1207
+ inheritedFormatting,
1208
+ paragraphMarkFormatting,
1209
+ paragraphMarkPrecedesStyle,
1210
+ styleResolver
1211
+ };
1103
1212
  const noteRefMark = marks.find((mark) => mark.type.name === "footnoteRef");
1104
- if (noteRefMark) return createNoteReferenceRun(noteRefMark, marks);
1105
- if (node.isText) {
1106
- const formatting = marksToTextFormatting(marks, { inheritedFormatting });
1107
- const run = {
1213
+ let run = null;
1214
+ if (noteRefMark) run = createNoteReferenceRun(noteRefMark, marks, formattingContext);
1215
+ else if (node.isText) {
1216
+ const formatting = marksToTextFormatting(marks, formattingContext);
1217
+ run = {
1108
1218
  type: "run",
1109
1219
  content: node.text ? [{
1110
1220
  type: "text",
@@ -1113,15 +1223,26 @@ function createTrackedChangeRun({ inheritedFormatting, marks, node }) {
1113
1223
  ...Object.keys(formatting).length > 0 ? { formatting } : {}
1114
1224
  };
1115
1225
  restoreRunPropertyChanges(run, marks);
1116
- return run;
1117
- }
1118
- if (node.type.name === "symbol") return createSymbolRun(node, marks);
1119
- if (node.type.name === "hardBreak") return createBreakRun(readHardBreakType(node));
1120
- if (node.type.name === "image") return createImageRun(node);
1121
- if (node.type.name === "shape") return createShapeRun(node);
1122
- if (node.type.name === "tab") return createTabRun(node);
1123
- if (node.type.name === "renderedPageBreak") return createRenderedPageBreakRun();
1124
- return null;
1226
+ } else if (node.type.name === "symbol") run = createSymbolRun(node, marks, formattingContext);
1227
+ else if (node.type.name === "hardBreak") run = createBreakRun(expectHardBreakAttrs(node), marks, formattingContext);
1228
+ else if (node.type.name === "pageBreakRun") {
1229
+ const { clear } = expectPageBreakRunAttrs(node);
1230
+ run = createPageBreakCarrierRun({
1231
+ ...formattingContext,
1232
+ clear,
1233
+ marks
1234
+ });
1235
+ } else if (node.type.name === "image") run = createImageRun(node);
1236
+ else if (node.type.name === "shape") run = createShapeRun(node);
1237
+ else if (node.type.name === "tab") run = createTabRun(node, marks, formattingContext);
1238
+ else if (node.type.name === "renderedPageBreak") run = createRenderedPageBreakRun();
1239
+ if (!run || pageBreakRunOwnerId(node) === void 0) return run;
1240
+ if (node.type.name === "image" || node.type.name === "shape" || node.type.name === "renderedPageBreak") {
1241
+ const formatting = getRunFormattingFromMarks(marks, formattingContext);
1242
+ if (formatting) run.formatting = formatting;
1243
+ restoreRunPropertyChanges(run, marks);
1244
+ }
1245
+ return run;
1125
1246
  }
1126
1247
  function createEmptyHyperlink(attrs) {
1127
1248
  const hyperlink = {
@@ -1208,7 +1329,7 @@ function createHyperlink(linkMark) {
1208
1329
  if (attrs.rId) hyperlink.rId = attrs.rId;
1209
1330
  return hyperlink;
1210
1331
  }
1211
- function addNodeToHyperlink({ hyperlink, inheritedFormatting, node }) {
1332
+ function addNodeToHyperlink({ baseParagraphFormatting, hyperlink, inheritedFormatting, node, paragraphMarkFormatting, paragraphMarkPrecedesStyle, sourceRunOwners, styleResolver }) {
1212
1333
  if (node.type.name === "bookmarkBoundary") {
1213
1334
  const attrs = expectBookmarkBoundaryAttrs(node);
1214
1335
  if (attrs.type === "start") hyperlink.children.push({
@@ -1224,31 +1345,84 @@ function addNodeToHyperlink({ hyperlink, inheritedFormatting, node }) {
1224
1345
  });
1225
1346
  return;
1226
1347
  }
1348
+ const formattingContext = {
1349
+ baseParagraphFormatting,
1350
+ inheritedFormatting,
1351
+ paragraphMarkFormatting,
1352
+ paragraphMarkPrecedesStyle,
1353
+ styleResolver
1354
+ };
1355
+ const nonLinkMarks = node.marks.filter((mark) => mark.type.name !== "hyperlink");
1356
+ const ownedRun = pageBreakRunOwnerId(node) === void 0 ? null : createTrackedChangeRun({
1357
+ ...formattingContext,
1358
+ marks: nonLinkMarks,
1359
+ node
1360
+ });
1361
+ if (ownedRun) {
1362
+ appendRunToHyperlink(hyperlink, ownedRun, node, sourceRunOwners);
1363
+ return;
1364
+ }
1227
1365
  const noteRefMark = node.marks.find((m) => m.type.name === "footnoteRef");
1228
1366
  if (noteRefMark) {
1229
- hyperlink.children.push(createNoteReferenceRun(noteRefMark, node.marks));
1367
+ hyperlink.children.push(createNoteReferenceRun(noteRefMark, node.marks, {
1368
+ baseParagraphFormatting,
1369
+ inheritedFormatting,
1370
+ paragraphMarkFormatting,
1371
+ paragraphMarkPrecedesStyle,
1372
+ styleResolver
1373
+ }));
1230
1374
  return;
1231
1375
  }
1232
- const nonLinkMarks = node.marks.filter((m) => m.type.name !== "hyperlink");
1233
1376
  if (node.isText && node.text) {
1234
1377
  const run = createRunFromText({
1378
+ baseParagraphFormatting,
1235
1379
  inheritedFormatting,
1236
1380
  marks: nonLinkMarks,
1381
+ paragraphMarkFormatting,
1382
+ paragraphMarkPrecedesStyle,
1383
+ styleResolver,
1237
1384
  text: node.text
1238
1385
  });
1239
1386
  hyperlink.children.push(run);
1240
1387
  return;
1241
1388
  }
1242
1389
  if (node.type.name === "symbol") {
1243
- hyperlink.children.push(createSymbolRun(node, nonLinkMarks));
1390
+ hyperlink.children.push(createSymbolRun(node, nonLinkMarks, {
1391
+ baseParagraphFormatting,
1392
+ inheritedFormatting,
1393
+ paragraphMarkFormatting,
1394
+ paragraphMarkPrecedesStyle,
1395
+ styleResolver
1396
+ }));
1244
1397
  return;
1245
1398
  }
1246
1399
  if (node.type.name === "hardBreak") {
1247
- hyperlink.children.push(createBreakRun(readHardBreakType(node), nonLinkMarks));
1400
+ hyperlink.children.push(createBreakRun(expectHardBreakAttrs(node), nonLinkMarks, {
1401
+ baseParagraphFormatting,
1402
+ inheritedFormatting,
1403
+ paragraphMarkFormatting,
1404
+ paragraphMarkPrecedesStyle,
1405
+ styleResolver
1406
+ }));
1407
+ return;
1408
+ }
1409
+ if (node.type.name === "pageBreakRun") {
1410
+ const { clear } = expectPageBreakRunAttrs(node);
1411
+ hyperlink.children.push(createPageBreakCarrierRun({
1412
+ ...formattingContext,
1413
+ clear,
1414
+ marks: nonLinkMarks
1415
+ }));
1248
1416
  return;
1249
1417
  }
1250
1418
  if (node.type.name === "tab") {
1251
- hyperlink.children.push(createTabRun(node, nonLinkMarks));
1419
+ hyperlink.children.push(createTabRun(node, nonLinkMarks, {
1420
+ baseParagraphFormatting,
1421
+ inheritedFormatting,
1422
+ paragraphMarkFormatting,
1423
+ paragraphMarkPrecedesStyle,
1424
+ styleResolver
1425
+ }));
1252
1426
  return;
1253
1427
  }
1254
1428
  if (node.type.name === "renderedPageBreak") {
@@ -1266,7 +1440,7 @@ function getNoteReferenceVertAlign(noteAttrs, marks) {
1266
1440
  if (marks.some((mark) => mark.type.name === "superscript")) return "superscript";
1267
1441
  if (noteAttrs.vertAlign === "baseline" || noteAttrs.vertAlign === "superscript") return noteAttrs.vertAlign;
1268
1442
  }
1269
- function createNoteReferenceRun(noteRefMark, marks) {
1443
+ function createNoteReferenceRun(noteRefMark, marks, formattingContext) {
1270
1444
  const noteAttrs = expectFootnoteRefMarkAttrs(noteRefMark);
1271
1445
  const run = {
1272
1446
  type: "run",
@@ -1275,12 +1449,24 @@ function createNoteReferenceRun(noteRefMark, marks) {
1275
1449
  id: typeof noteAttrs.id === "string" ? Number.parseInt(noteAttrs.id, 10) || 0 : noteAttrs.id
1276
1450
  }]
1277
1451
  };
1452
+ const formatting = getAtomRunFormattingFromMarks(marks, formattingContext);
1278
1453
  const vertAlign = getNoteReferenceVertAlign(noteAttrs, marks);
1279
- if (vertAlign) run.formatting = { vertAlign };
1454
+ if (formatting) run.formatting = formatting;
1455
+ if (vertAlign && formatting?.vertAlign === void 0 && formatting?.styleId === void 0) run.formatting = {
1456
+ ...formatting,
1457
+ vertAlign
1458
+ };
1459
+ restoreRunPropertyChanges(run, marks);
1280
1460
  return run;
1281
1461
  }
1282
- function createRunFromText({ inheritedFormatting, marks, text }) {
1283
- const formatting = getRunFormattingFromMarks(marks, { inheritedFormatting });
1462
+ function createRunFromText({ baseParagraphFormatting, inheritedFormatting, marks, paragraphMarkFormatting, paragraphMarkPrecedesStyle, styleResolver, text }) {
1463
+ const formatting = getRunFormattingFromMarks(marks, {
1464
+ baseParagraphFormatting,
1465
+ inheritedFormatting,
1466
+ paragraphMarkFormatting,
1467
+ paragraphMarkPrecedesStyle,
1468
+ styleResolver
1469
+ });
1284
1470
  const run = {
1285
1471
  type: "run",
1286
1472
  content: [{
@@ -1292,7 +1478,7 @@ function createRunFromText({ inheritedFormatting, marks, text }) {
1292
1478
  restoreRunPropertyChanges(run, marks);
1293
1479
  return run;
1294
1480
  }
1295
- function createSymbolRun(node, marks) {
1481
+ function createSymbolRun(node, marks, formattingContext) {
1296
1482
  const { font, char } = expectSymbolAttrs(node);
1297
1483
  const run = {
1298
1484
  type: "run",
@@ -1302,7 +1488,7 @@ function createSymbolRun(node, marks) {
1302
1488
  char
1303
1489
  }]
1304
1490
  };
1305
- const formatting = getRunFormattingFromMarks(marks);
1491
+ const formatting = getAtomRunFormattingFromMarks(marks, formattingContext);
1306
1492
  if (formatting) run.formatting = formatting;
1307
1493
  restoreRunPropertyChanges(run, marks);
1308
1494
  return run;
@@ -1315,10 +1501,14 @@ function restoreRunPropertyChanges(run, marks) {
1315
1501
  run.propertyChanges = [...changes];
1316
1502
  }
1317
1503
  function getRunFormattingFromMarks(marks, options) {
1318
- if (!marks || marks.length === 0) return;
1504
+ if (!marks || marks.length === 0 && !options) return;
1319
1505
  const formatting = marksToTextFormatting(marks, options);
1320
1506
  return Object.keys(formatting).length > 0 ? formatting : void 0;
1321
1507
  }
1508
+ function getAtomRunFormattingFromMarks(marks, options) {
1509
+ if (!marks?.some(({ type }) => RUN_FORMATTING_MARK_NAMES.has(type.name))) return;
1510
+ return getRunFormattingFromMarks(marks, options);
1511
+ }
1322
1512
  /**
1323
1513
  * Append text to an existing run
1324
1514
  */
@@ -1330,19 +1520,81 @@ function appendTextToRun(run, text) {
1330
1520
  text
1331
1521
  });
1332
1522
  }
1523
+ function runsShareProperties(left, right) {
1524
+ return canonicalJson({
1525
+ formatting: left.formatting,
1526
+ propertyChanges: left.propertyChanges
1527
+ }) === canonicalJson({
1528
+ formatting: right.formatting,
1529
+ propertyChanges: right.propertyChanges
1530
+ });
1531
+ }
1532
+ function pageBreakRunOwnerId(node) {
1533
+ const mark = node.marks.find(({ type }) => type.name === "pageBreakRunOwner");
1534
+ return mark ? expectPageBreakRunOwnerMarkAttrs(mark).id : void 0;
1535
+ }
1536
+ function canJoinOwnedRuns(previous, run, node, sourceRunOwners) {
1537
+ if (!previous || !runsShareProperties(previous, run)) return false;
1538
+ const ownerId = pageBreakRunOwnerId(node);
1539
+ return ownerId !== void 0 && sourceRunOwners.get(previous) === ownerId;
1540
+ }
1541
+ function rememberSourceRunOwner(run, node, sourceRunOwners) {
1542
+ const ownerId = pageBreakRunOwnerId(node);
1543
+ if (ownerId !== void 0) sourceRunOwners.set(run, ownerId);
1544
+ }
1545
+ function appendRunToTrackedWrapper(wrapper, run, node, sourceRunOwners) {
1546
+ const previous = wrapper.content.at(-1);
1547
+ if (previous?.type === "run" && canJoinOwnedRuns(previous, run, node, sourceRunOwners)) {
1548
+ previous.content.push(...run.content);
1549
+ return;
1550
+ }
1551
+ wrapper.content.push(run);
1552
+ rememberSourceRunOwner(run, node, sourceRunOwners);
1553
+ }
1554
+ function appendRunToHyperlink(hyperlink, run, node, sourceRunOwners) {
1555
+ const previous = hyperlink.children.at(-1);
1556
+ if (previous?.type === "run" && canJoinOwnedRuns(previous, run, node, sourceRunOwners)) {
1557
+ previous.content.push(...run.content);
1558
+ return;
1559
+ }
1560
+ hyperlink.children.push(run);
1561
+ rememberSourceRunOwner(run, node, sourceRunOwners);
1562
+ }
1563
+ function createPageBreakCarrierRun({ baseParagraphFormatting, clear, inheritedFormatting, marks, paragraphMarkFormatting, paragraphMarkPrecedesStyle, styleResolver }) {
1564
+ const run = {
1565
+ type: "run",
1566
+ content: [{
1567
+ type: "break",
1568
+ breakType: "page",
1569
+ ...clear !== void 0 ? { clear } : {}
1570
+ }]
1571
+ };
1572
+ const formatting = getAtomRunFormattingFromMarks(marks, {
1573
+ baseParagraphFormatting,
1574
+ inheritedFormatting,
1575
+ paragraphMarkFormatting,
1576
+ paragraphMarkPrecedesStyle,
1577
+ styleResolver
1578
+ });
1579
+ if (formatting) run.formatting = formatting;
1580
+ if (marks) restoreRunPropertyChanges(run, marks);
1581
+ return run;
1582
+ }
1333
1583
  /**
1334
1584
  * Create a Run containing a line break
1335
1585
  */
1336
- function createBreakRun(breakType = "textWrapping", marks) {
1586
+ function createBreakRun(attrs, marks, formattingContext) {
1337
1587
  const run = {
1338
1588
  type: "run",
1339
1589
  content: [{
1340
1590
  type: "break",
1341
- breakType
1591
+ ...attrs.breakType !== void 0 ? { breakType: attrs.breakType } : {},
1592
+ ...attrs.clear !== void 0 ? { clear: attrs.clear } : {}
1342
1593
  }]
1343
1594
  };
1344
- const formatting = getRunFormattingFromMarks(marks);
1595
+ const formatting = getAtomRunFormattingFromMarks(marks, formattingContext);
1345
1596
  if (formatting) run.formatting = formatting;
1597
+ if (marks) restoreRunPropertyChanges(run, marks);
1346
1598
  return run;
1347
1599
  }
1348
1600
  function createRenderedPageBreakRun() {
@@ -1351,13 +1603,10 @@ function createRenderedPageBreakRun() {
1351
1603
  content: [{ type: "renderedPageBreak" }]
1352
1604
  };
1353
1605
  }
1354
- function readHardBreakType(node) {
1355
- return expectHardBreakAttrs(node).breakType ?? "textWrapping";
1356
- }
1357
1606
  /**
1358
1607
  * Create a Run containing a tab
1359
1608
  */
1360
- function createTabRun(node, marks) {
1609
+ function createTabRun(node, marks, formattingContext) {
1361
1610
  const { positional } = expectTabAttrs(node);
1362
1611
  const run = {
1363
1612
  type: "run",
@@ -1366,15 +1615,32 @@ function createTabRun(node, marks) {
1366
1615
  ...positional ? { positional } : {}
1367
1616
  }]
1368
1617
  };
1369
- const formatting = getRunFormattingFromMarks(marks);
1618
+ const formatting = getAtomRunFormattingFromMarks(marks, formattingContext);
1370
1619
  if (formatting) run.formatting = formatting;
1620
+ if (marks) restoreRunPropertyChanges(run, marks);
1371
1621
  return run;
1372
1622
  }
1373
- function createFieldFromNode(node, { marks, textBoxAnchorMarkers }) {
1623
+ function createFieldFromNode(node, { baseParagraphFormatting, inheritedFormatting, marks, paragraphMarkFormatting, paragraphMarkPrecedesStyle, styleResolver, textBoxAnchorMarkers }) {
1374
1624
  const attrs = expectFieldAttrs(node);
1375
- const formatting = marks && marks.length > 0 ? marksToTextFormatting(marks) : void 0;
1625
+ const formatting = marks && marks.length > 0 ? marksToTextFormatting(marks, {
1626
+ baseParagraphFormatting,
1627
+ inheritedFormatting,
1628
+ paragraphMarkFormatting,
1629
+ paragraphMarkPrecedesStyle,
1630
+ styleResolver
1631
+ }) : void 0;
1632
+ const extractedContent = extractParagraphContent(node, void 0, void 0, textBoxAnchorMarkers, false, {
1633
+ baseParagraphFormatting,
1634
+ inheritedFormatting,
1635
+ paragraphMarkFormatting,
1636
+ paragraphMarkPrecedesStyle: paragraphMarkPrecedesStyle ?? false,
1637
+ styleResolver: styleResolver ?? null
1638
+ }).filter((content) => content.type === "run" || content.type === "hyperlink");
1639
+ const hasExplicitPageBreak = extractedContent.some((content) => {
1640
+ return (content.type === "run" ? [content] : content.children).some((run) => run.type === "run" && run.content.some((runContent) => runContent.type === "break" && runContent.breakType === "page"));
1641
+ });
1376
1642
  let displayText = attrs.displayText ?? "";
1377
- if (!displayText) switch (attrs.fieldType) {
1643
+ if (!displayText && !hasExplicitPageBreak) switch (attrs.fieldType) {
1378
1644
  case "PAGE":
1379
1645
  displayText = "1";
1380
1646
  break;
@@ -1393,7 +1659,7 @@ function createFieldFromNode(node, { marks, textBoxAnchorMarkers }) {
1393
1659
  }],
1394
1660
  ...formatting && Object.keys(formatting).length > 0 ? { formatting } : {}
1395
1661
  };
1396
- const extractedContent = extractParagraphContent(node, void 0, void 0, textBoxAnchorMarkers).filter((content) => content.type === "run" || content.type === "hyperlink");
1662
+ if (marks && node.type.name === "field") restoreRunPropertyChanges(displayRun, marks);
1397
1663
  const fieldContent = extractedContent.length > 0 ? synchronizeFieldDisplayText(extractedContent, displayText, displayRun) : [];
1398
1664
  if (attrs.fieldKind === "complex") {
1399
1665
  const complex = {
@@ -1466,11 +1732,11 @@ function createMathFromNode(node) {
1466
1732
  /**
1467
1733
  * Create an InlineSdt from a PM sdt node
1468
1734
  */
1469
- function createInlineSdtFromNode(node, textBoxAnchorMarkers, inheritedFormatting) {
1735
+ function createInlineSdtFromNode(node, textBoxAnchorMarkers, formattingContext) {
1470
1736
  return {
1471
1737
  type: "inlineSdt",
1472
1738
  properties: sdtPropertiesFromAttrs(expectSdtAttrs(node)),
1473
- content: extractParagraphContent(node, void 0, void 0, textBoxAnchorMarkers, false, inheritedFormatting).filter((c) => c.type === "run" || c.type === "hyperlink" || c.type === "simpleField" || c.type === "complexField" || c.type === "inlineSdt" || c.type === "insertion" || c.type === "deletion" || c.type === "moveFrom" || c.type === "moveTo" || c.type === "mathEquation")
1739
+ content: extractParagraphContent(node, void 0, void 0, textBoxAnchorMarkers, false, formattingContext).filter((c) => c.type === "run" || c.type === "hyperlink" || c.type === "simpleField" || c.type === "complexField" || c.type === "inlineSdt" || c.type === "insertion" || c.type === "deletion" || c.type === "moveFrom" || c.type === "moveTo" || c.type === "mathEquation")
1474
1740
  };
1475
1741
  }
1476
1742
  /**
@@ -1618,20 +1884,380 @@ function createShapeRun(node) {
1618
1884
  }]
1619
1885
  };
1620
1886
  }
1887
+ const RUN_FORMATTING_VISUAL_GROUPS = {
1888
+ bold: "bold",
1889
+ boldCs: null,
1890
+ italic: "italic",
1891
+ italicCs: null,
1892
+ underline: "underline",
1893
+ strike: "strike",
1894
+ doubleStrike: "strike",
1895
+ vertAlign: "vertAlign",
1896
+ smallCaps: "smallCaps",
1897
+ allCaps: "allCaps",
1898
+ hidden: "hidden",
1899
+ color: "color",
1900
+ highlight: "highlight",
1901
+ shading: "shading",
1902
+ fontSize: "fontSize",
1903
+ fontSizeCs: null,
1904
+ fontFamily: "fontFamily",
1905
+ language: "language",
1906
+ spacing: "characterSpacing",
1907
+ position: "characterSpacing",
1908
+ scale: "characterSpacing",
1909
+ kerning: "characterSpacing",
1910
+ effect: "effect",
1911
+ emphasisMark: "emphasisMark",
1912
+ emboss: "emboss",
1913
+ imprint: "imprint",
1914
+ outline: "outline",
1915
+ shadow: "shadow",
1916
+ rtl: "rtl",
1917
+ cs: null,
1918
+ styleId: null
1919
+ };
1920
+ const RUN_FORMATTING_FAST_PATH_DISPOSITION = {
1921
+ bold: "visual",
1922
+ boldCs: "structural",
1923
+ italic: "visual",
1924
+ italicCs: "structural",
1925
+ underline: "visual",
1926
+ strike: "visual",
1927
+ doubleStrike: "visual",
1928
+ vertAlign: "visual",
1929
+ smallCaps: "visual",
1930
+ allCaps: "visual",
1931
+ hidden: "visual",
1932
+ color: "visual",
1933
+ highlight: "visual",
1934
+ shading: "visual",
1935
+ fontSize: "visual",
1936
+ fontSizeCs: "structural",
1937
+ fontFamily: "visual",
1938
+ language: "visual",
1939
+ spacing: "visual",
1940
+ position: "visual",
1941
+ scale: "visual",
1942
+ kerning: "visual",
1943
+ effect: "visual",
1944
+ emphasisMark: "visual",
1945
+ emboss: "visual",
1946
+ imprint: "visual",
1947
+ outline: "visual",
1948
+ shadow: "visual",
1949
+ rtl: "visual",
1950
+ cs: "structural",
1951
+ styleId: "character-style"
1952
+ };
1953
+ const hasOnlyCarrierlessVisualFormatting = (formatting) => {
1954
+ for (const property of Object.keys(formatting)) if (RUN_FORMATTING_FAST_PATH_DISPOSITION[property] !== "visual") return false;
1955
+ return true;
1956
+ };
1957
+ const visibleUnderline = (formatting) => {
1958
+ const underline = formatting?.underline;
1959
+ return underline?.style === "none" ? void 0 : underline;
1960
+ };
1961
+ const visibleStrike = (formatting) => {
1962
+ if (formatting?.doubleStrike === true) return "double";
1963
+ return formatting?.strike === true ? "single" : void 0;
1964
+ };
1965
+ const visibleColor = (formatting) => formatting?.color?.auto === true ? void 0 : formatting?.color;
1966
+ const visibleValue = (value) => value === "none" ? void 0 : value;
1967
+ /**
1968
+ * Compare the exact presentation encoded by PM run marks without allocating the
1969
+ * intermediate maps used by reconciliation. Non-visual authored state is
1970
+ * deliberately absent here: the carrierless fast path below excludes every
1971
+ * structural carrier before calling this predicate.
1972
+ */
1973
+ const sameVisualFormatting = (left, right) => {
1974
+ if (left?.bold === true !== (right?.bold === true) || left?.italic === true !== (right?.italic === true) || !sameFormattingValue(visibleUnderline(left), visibleUnderline(right)) || visibleStrike(left) !== visibleStrike(right) || !sameFormattingValue(visibleColor(left), visibleColor(right)) || visibleValue(left?.highlight) !== visibleValue(right?.highlight) || (left?.fontSize || void 0) !== (right?.fontSize || void 0) || !sameFormattingValue(left?.fontFamily, right?.fontFamily) || !sameFormattingValue(left?.language, right?.language) || (left?.vertAlign === "superscript" || left?.vertAlign === "subscript" ? left.vertAlign : void 0) !== (right?.vertAlign === "superscript" || right?.vertAlign === "subscript" ? right.vertAlign : void 0) || left?.allCaps === true !== (right?.allCaps === true) || left?.smallCaps === true !== (right?.smallCaps === true) || left?.hidden === true !== (right?.hidden === true) || left?.emboss === true !== (right?.emboss === true) || left?.imprint === true !== (right?.imprint === true) || left?.shadow === true !== (right?.shadow === true) || left?.outline === true !== (right?.outline === true) || left?.rtl === true !== (right?.rtl === true) || (typeof left?.spacing === "number" ? left.spacing : null) !== (typeof right?.spacing === "number" ? right.spacing : null) || (typeof left?.position === "number" ? left.position : null) !== (typeof right?.position === "number" ? right.position : null) || (normalizeHorizontalScalePercent(left?.scale) ?? null) !== (normalizeHorizontalScalePercent(right?.scale) ?? null) || (typeof left?.kerning === "number" ? left.kerning : null) !== (typeof right?.kerning === "number" ? right.kerning : null) || visibleValue(left?.effect) !== visibleValue(right?.effect) || visibleValue(left?.emphasisMark) !== visibleValue(right?.emphasisMark)) return false;
1975
+ return sameFormattingValue(shadingToRunShadingAttrs(left?.shading), shadingToRunShadingAttrs(right?.shading));
1976
+ };
1977
+ const visualFormattingGroups = (formatting) => {
1978
+ const groups = /* @__PURE__ */ new Map();
1979
+ if (!formatting) return groups;
1980
+ if (formatting.bold) groups.set("bold", true);
1981
+ if (formatting.italic) groups.set("italic", true);
1982
+ if (formatting.underline && formatting.underline.style !== "none") groups.set("underline", {
1983
+ style: formatting.underline.style || "single",
1984
+ ...formatting.underline.color ? { color: formatting.underline.color } : {}
1985
+ });
1986
+ if (formatting.strike || formatting.doubleStrike) groups.set("strike", formatting.doubleStrike ? "double" : "single");
1987
+ if (formatting.color && !formatting.color.auto) {
1988
+ const { rgb, themeColor, themeTint, themeShade } = formatting.color;
1989
+ groups.set("color", {
1990
+ ...rgb ? { rgb } : {},
1991
+ ...themeColor ? { themeColor } : {},
1992
+ ...themeTint ? { themeTint } : {},
1993
+ ...themeShade ? { themeShade } : {}
1994
+ });
1995
+ }
1996
+ if (formatting.highlight && formatting.highlight !== "none") groups.set("highlight", formatting.highlight);
1997
+ const shadingAttrs = shadingToRunShadingAttrs(formatting.shading);
1998
+ if (shadingAttrs) groups.set("shading", runShadingAttrsToShading(shadingAttrs));
1999
+ if (formatting.fontSize) groups.set("fontSize", formatting.fontSize);
2000
+ if (formatting.fontFamily) {
2001
+ const { ascii, hAnsi, eastAsia, cs, hint, asciiTheme, hAnsiTheme, eastAsiaTheme, csTheme } = formatting.fontFamily;
2002
+ groups.set("fontFamily", {
2003
+ ...ascii ? { ascii } : {},
2004
+ ...hAnsi ? { hAnsi } : {},
2005
+ ...eastAsia ? { eastAsia } : {},
2006
+ ...cs ? { cs } : {},
2007
+ ...hint ? { hint } : {},
2008
+ ...asciiTheme ? { asciiTheme } : {},
2009
+ ...hAnsiTheme ? { hAnsiTheme } : {},
2010
+ ...eastAsiaTheme ? { eastAsiaTheme } : {},
2011
+ ...csTheme ? { csTheme } : {}
2012
+ });
2013
+ }
2014
+ if (formatting.language) {
2015
+ const { val, eastAsia, bidi } = formatting.language;
2016
+ groups.set("language", {
2017
+ ...val ? { val } : {},
2018
+ ...eastAsia ? { eastAsia } : {},
2019
+ ...bidi ? { bidi } : {}
2020
+ });
2021
+ }
2022
+ if (formatting.vertAlign === "superscript" || formatting.vertAlign === "subscript") groups.set("vertAlign", formatting.vertAlign);
2023
+ for (const [property, group] of [
2024
+ ["allCaps", "allCaps"],
2025
+ ["smallCaps", "smallCaps"],
2026
+ ["hidden", "hidden"],
2027
+ ["emboss", "emboss"],
2028
+ ["imprint", "imprint"],
2029
+ ["shadow", "shadow"],
2030
+ ["outline", "outline"],
2031
+ ["rtl", "rtl"]
2032
+ ]) if (formatting[property]) groups.set(group, true);
2033
+ const spacing = typeof formatting.spacing === "number" ? formatting.spacing : null;
2034
+ const position = typeof formatting.position === "number" ? formatting.position : null;
2035
+ const scale = normalizeHorizontalScalePercent(formatting.scale) ?? null;
2036
+ const kerning = typeof formatting.kerning === "number" ? formatting.kerning : null;
2037
+ if (spacing !== null || position !== null || scale !== null || kerning !== null) groups.set("characterSpacing", {
2038
+ spacing,
2039
+ position,
2040
+ scale,
2041
+ kerning
2042
+ });
2043
+ if (formatting.effect && formatting.effect !== "none") groups.set("effect", formatting.effect);
2044
+ if (formatting.emphasisMark && formatting.emphasisMark !== "none") groups.set("emphasisMark", formatting.emphasisMark);
2045
+ return groups;
2046
+ };
2047
+ const expectedRunFormattingOverrideAttrs = (authoredFormatting) => {
2048
+ const attrs = buildRunFormattingOverrideAttrs(authoredFormatting, {
2049
+ type: "authored-baseline",
2050
+ formatting: authoredFormatting
2051
+ });
2052
+ const hasDirectFormatting = Object.keys(authoredFormatting).some((property) => property !== "styleId");
2053
+ if (!attrs && !hasDirectFormatting) return;
2054
+ const expected = { ...attrs };
2055
+ const directFontProperties = [
2056
+ "color",
2057
+ "fontFamily",
2058
+ "fontSize"
2059
+ ].filter((property) => authoredFormatting[property] !== void 0);
2060
+ if (directFontProperties.length > 0) expected.directFontProperties = directFontProperties;
2061
+ const complexScriptPropertyAbsences = [
2062
+ ["bold", "boldCs"],
2063
+ ["italic", "italicCs"],
2064
+ ["fontSize", "fontSizeCs"]
2065
+ ].filter(([ordinary, complex]) => authoredFormatting[ordinary] !== void 0 && authoredFormatting[complex] === void 0).map(([, complex]) => complex);
2066
+ if (complexScriptPropertyAbsences.length > 0) expected.complexScriptPropertyAbsences = complexScriptPropertyAbsences;
2067
+ return Object.keys(expected).length > 0 ? expected : void 0;
2068
+ };
2069
+ const changedVisualFormattingGroups = (observedFormatting, expectedFormatting) => {
2070
+ const actual = visualFormattingGroups(observedFormatting);
2071
+ const expected = visualFormattingGroups(expectedFormatting);
2072
+ const changed = /* @__PURE__ */ new Set();
2073
+ for (const group of /* @__PURE__ */ new Set([...actual.keys(), ...expected.keys()])) {
2074
+ const actualValue = actual.get(group);
2075
+ const expectedValue = expected.get(group);
2076
+ if (!sameFormattingValue(actualValue, expectedValue)) changed.add(group);
2077
+ }
2078
+ return changed;
2079
+ };
2080
+ const VISUAL_BOOLEAN_FORMATTING_PROPERTIES = /* @__PURE__ */ new Set([
2081
+ "allCaps",
2082
+ "bold",
2083
+ "doubleStrike",
2084
+ "emboss",
2085
+ "hidden",
2086
+ "imprint",
2087
+ "italic",
2088
+ "outline",
2089
+ "rtl",
2090
+ "shadow",
2091
+ "smallCaps",
2092
+ "strike"
2093
+ ]);
2094
+ const fontFamilyDifference = (actual, inherited) => {
2095
+ const difference = {};
2096
+ for (const property of [
2097
+ "ascii",
2098
+ "hAnsi",
2099
+ "eastAsia",
2100
+ "cs",
2101
+ "hint",
2102
+ "asciiTheme",
2103
+ "hAnsiTheme",
2104
+ "eastAsiaTheme",
2105
+ "csTheme"
2106
+ ]) {
2107
+ const value = actual[property];
2108
+ if (value !== void 0 && value !== inherited?.[property]) Reflect.set(difference, property, value);
2109
+ }
2110
+ return Object.keys(difference).length > 0 ? difference : void 0;
2111
+ };
2112
+ const DIRECT_OVERRIDE_FORMATTING_PROPERTIES = [
2113
+ "allCaps",
2114
+ "bold",
2115
+ "boldCs",
2116
+ "cs",
2117
+ "doubleStrike",
2118
+ "emboss",
2119
+ "fontSizeCs",
2120
+ "hidden",
2121
+ "imprint",
2122
+ "italic",
2123
+ "italicCs",
2124
+ "outline",
2125
+ "rtl",
2126
+ "shadow",
2127
+ "smallCaps",
2128
+ "strike",
2129
+ "underline"
2130
+ ];
2131
+ const POSITIVE_OVERRIDE_PROPERTIES_REPRESENTED_BY_VISUAL_MARKS = /* @__PURE__ */ new Set([
2132
+ "allCaps",
2133
+ "emboss",
2134
+ "hidden",
2135
+ "imprint",
2136
+ "outline",
2137
+ "rtl",
2138
+ "shadow",
2139
+ "smallCaps",
2140
+ "strike"
2141
+ ]);
2142
+ const setFormattingFromOverrideSignal = (formatting, property, value) => {
2143
+ Reflect.deleteProperty(formatting, property);
2144
+ if (value === void 0) return;
2145
+ if (property === "underline") {
2146
+ formatting.underline = { style: "none" };
2147
+ return;
2148
+ }
2149
+ Reflect.set(formatting, property, value);
2150
+ };
2151
+ const sameFormattingValue = (left, right) => {
2152
+ if (left === right) return true;
2153
+ if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
2154
+ if (Array.isArray(left) || Array.isArray(right)) {
2155
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
2156
+ return left.every((value, index) => sameFormattingValue(value, right[index]));
2157
+ }
2158
+ const leftKeys = Object.keys(left);
2159
+ const rightKeys = Object.keys(right);
2160
+ if (leftKeys.length !== rightKeys.length) return false;
2161
+ return leftKeys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && sameFormattingValue(Reflect.get(left, key), Reflect.get(right, key)));
2162
+ };
2163
+ const reconcileOverrideSignals = (formatting, current, expected, inheritedFormatting, observedFormatting) => {
2164
+ for (const property of DIRECT_OVERRIDE_FORMATTING_PROPERTIES) {
2165
+ if (sameFormattingValue(current?.[property], expected?.[property])) continue;
2166
+ if (current?.[property] === void 0 && expected?.[property] === true && POSITIVE_OVERRIDE_PROPERTIES_REPRESENTED_BY_VISUAL_MARKS.has(property)) continue;
2167
+ if (current && hasAuthoredRunFormattingProvenance(current) && expected?.[property] === void 0 && sameFormattingValue(current[property], inheritedFormatting?.[property])) continue;
2168
+ setFormattingFromOverrideSignal(formatting, property, current?.[property]);
2169
+ }
2170
+ for (const property of [
2171
+ "color",
2172
+ "fontFamily",
2173
+ "fontSize"
2174
+ ]) {
2175
+ const currentIsDirect = current?.directFontProperties?.includes(property) === true;
2176
+ if (currentIsDirect === (expected?.directFontProperties?.includes(property) === true)) continue;
2177
+ if (!currentIsDirect) {
2178
+ Reflect.deleteProperty(formatting, property);
2179
+ continue;
2180
+ }
2181
+ const observed = observedFormatting[property];
2182
+ if (observed !== void 0) Reflect.set(formatting, property, observed);
2183
+ }
2184
+ for (const property of [
2185
+ "boldCs",
2186
+ "italicCs",
2187
+ "fontSizeCs"
2188
+ ]) {
2189
+ const currentIsAbsent = current?.complexScriptPropertyAbsences?.includes(property) === true;
2190
+ if (currentIsAbsent !== (expected?.complexScriptPropertyAbsences?.includes(property) === true) && currentIsAbsent) Reflect.deleteProperty(formatting, property);
2191
+ }
2192
+ };
2193
+ /**
2194
+ * Reconcile source-authored provenance with the current visual mark set at the
2195
+ * one save boundary shared by editor commands, raw transactions and compare.
2196
+ * Unchanged groups keep exact source state (including equal direct values and
2197
+ * otherwise invisible sentinels); changed groups derive the smallest direct
2198
+ * override needed relative to the current style cascade.
2199
+ */
2200
+ const reconcileAuthoredFormatting = ({ authoredFormatting, carrierlessContext, currentOverrideAttrs, inheritedFormatting, observedFormatting }) => {
2201
+ const hasAuthoredFormatting = Object.keys(authoredFormatting).length > 0;
2202
+ if (carrierlessContext === "ordinary" && currentOverrideAttrs === void 0 && !hasAuthoredFormatting && hasOnlyCarrierlessVisualFormatting(observedFormatting)) {
2203
+ if (sameVisualFormatting(observedFormatting, inheritedFormatting)) return authoredFormatting;
2204
+ if (observedFormatting.fontFamily === void 0 && sameVisualFormatting(inheritedFormatting, void 0)) return observedFormatting;
2205
+ }
2206
+ const expectedFormatting = hasAuthoredFormatting ? mergeTextFormatting(inheritedFormatting, authoredFormatting) : inheritedFormatting;
2207
+ const changedGroups = changedVisualFormattingGroups(observedFormatting, expectedFormatting);
2208
+ const reconciled = { ...authoredFormatting };
2209
+ for (const property of Object.keys(RUN_FORMATTING_VISUAL_GROUPS)) {
2210
+ const group = RUN_FORMATTING_VISUAL_GROUPS[property];
2211
+ if (group === null || !changedGroups.has(group)) continue;
2212
+ Reflect.deleteProperty(reconciled, property);
2213
+ if (VISUAL_BOOLEAN_FORMATTING_PROPERTIES.has(property)) {
2214
+ const observed = observedFormatting[property] === true;
2215
+ if (observed !== (inheritedFormatting?.[property] === true)) Reflect.set(reconciled, property, observed);
2216
+ continue;
2217
+ }
2218
+ const observed = observedFormatting[property];
2219
+ if (observed === void 0) continue;
2220
+ if (property === "fontFamily") {
2221
+ const difference = fontFamilyDifference(observed, inheritedFormatting?.fontFamily);
2222
+ if (difference) reconciled.fontFamily = difference;
2223
+ continue;
2224
+ }
2225
+ if (!sameFormattingValue(observed, inheritedFormatting?.[property])) Reflect.set(reconciled, property, observed);
2226
+ }
2227
+ if (currentOverrideAttrs !== void 0) reconcileOverrideSignals(reconciled, currentOverrideAttrs, expectedRunFormattingOverrideAttrs(authoredFormatting), inheritedFormatting, observedFormatting);
2228
+ return reconciled;
2229
+ };
2230
+ const formattingAuthorshipCost = (formatting) => {
2231
+ let cost = 0;
2232
+ const visit = (value) => {
2233
+ if (value === void 0) return;
2234
+ if (value === null || typeof value !== "object") {
2235
+ cost++;
2236
+ return;
2237
+ }
2238
+ const entries = Object.entries(value);
2239
+ if (entries.length === 0) {
2240
+ cost++;
2241
+ return;
2242
+ }
2243
+ for (const [, nested] of entries) visit(nested);
2244
+ };
2245
+ for (const [property, value] of Object.entries(formatting)) if (property !== "styleId") visit(value);
2246
+ return cost;
2247
+ };
1621
2248
  function marksToTextFormatting(marks, options) {
1622
2249
  const formatting = {};
1623
2250
  let directOverrideFormatting;
1624
2251
  let directFontProperties;
1625
- let characterStyleRPr;
2252
+ let characterStyleId;
1626
2253
  let runFormattingOverrideMark;
2254
+ let currentOverrideAttrs;
1627
2255
  for (const mark of marks) switch (mark.type.name) {
1628
2256
  case "bold":
1629
2257
  formatting.bold = true;
1630
- formatting.boldCs = true;
1631
2258
  break;
1632
2259
  case "italic":
1633
2260
  formatting.italic = true;
1634
- formatting.italicCs = true;
1635
2261
  break;
1636
2262
  case "underline": {
1637
2263
  const attrs = expectUnderlineMarkAttrs(mark);
@@ -1660,12 +2286,9 @@ function marksToTextFormatting(marks, options) {
1660
2286
  case "runShading":
1661
2287
  formatting.shading = runShadingAttrsToShading(expectRunShadingMarkAttrs(mark));
1662
2288
  break;
1663
- case "fontSize": {
1664
- const attrs = expectFontSizeMarkAttrs(mark);
1665
- formatting.fontSize = attrs.size;
1666
- formatting.fontSizeCs = attrs.size;
2289
+ case "fontSize":
2290
+ formatting.fontSize = expectFontSizeMarkAttrs(mark).size;
1667
2291
  break;
1668
- }
1669
2292
  case "fontFamily": {
1670
2293
  const attrs = expectFontFamilyMarkAttrs(mark);
1671
2294
  const ff = {};
@@ -1673,8 +2296,7 @@ function marksToTextFormatting(marks, options) {
1673
2296
  if (attrs.hAnsi) ff.hAnsi = attrs.hAnsi;
1674
2297
  if (attrs.eastAsia) ff.eastAsia = attrs.eastAsia;
1675
2298
  if (attrs.hint) ff.hint = attrs.hint;
1676
- const csVal = attrs.cs || attrs.ascii;
1677
- if (csVal) ff.cs = csVal;
2299
+ if (attrs.cs) ff.cs = attrs.cs;
1678
2300
  if (attrs.asciiTheme) ff.asciiTheme = attrs.asciiTheme;
1679
2301
  if (attrs.hAnsiTheme) ff.hAnsiTheme = attrs.hAnsiTheme;
1680
2302
  if (attrs.eastAsiaTheme) ff.eastAsiaTheme = attrs.eastAsiaTheme;
@@ -1742,100 +2364,77 @@ function marksToTextFormatting(marks, options) {
1742
2364
  case "characterStyle": {
1743
2365
  const attrs = expectCharacterStyleMarkAttrs(mark);
1744
2366
  formatting.styleId = attrs.styleId;
1745
- characterStyleRPr = attrs._styleRPr;
2367
+ characterStyleId = attrs.styleId;
1746
2368
  break;
1747
2369
  }
1748
2370
  default: break;
1749
2371
  }
2372
+ let authoredFormatting = {};
1750
2373
  if (runFormattingOverrideMark) {
1751
2374
  const overrideAttrs = expectRunFormattingOverrideMarkAttrs(runFormattingOverrideMark);
2375
+ currentOverrideAttrs = overrideAttrs;
1752
2376
  directFontProperties = overrideAttrs.directFontProperties;
1753
- applyRunFormattingOverrideAttrs(formatting, overrideAttrs);
1754
2377
  directOverrideFormatting = {};
1755
2378
  applyRunFormattingOverrideAttrs(directOverrideFormatting, overrideAttrs);
1756
2379
  for (const property of directFontProperties ?? []) {
1757
2380
  const value = formatting[property];
1758
2381
  if (value !== void 0) Reflect.set(directOverrideFormatting, property, value);
1759
2382
  }
2383
+ authoredFormatting = authoredRunFormattingFromAttrs(overrideAttrs) ?? directOverrideFormatting;
1760
2384
  }
1761
- if (characterStyleRPr) return subtractCharacterStyleFormatting({
1762
- directOverrideFormatting,
1763
- formatting,
1764
- inheritedFormatting: options?.inheritedFormatting,
1765
- styleRPr: characterStyleRPr
1766
- });
1767
- for (const property of [
1768
- "fontFamily",
1769
- "fontSize",
1770
- "color"
1771
- ]) {
1772
- if (directFontProperties?.includes(property)) continue;
1773
- const value = formatting[property];
1774
- const inheritedValue = options?.inheritedFormatting?.[property];
1775
- const matchesInherited = property === "fontFamily" ? sameFontFamily(formatting.fontFamily, options?.inheritedFormatting?.fontFamily) : JSON.stringify(value) === JSON.stringify(inheritedValue);
1776
- if (inheritedValue !== void 0 && matchesInherited) Reflect.deleteProperty(formatting, property);
1777
- }
1778
- return formatting;
1779
- }
1780
- const sameFontFamily = (left, right) => [
1781
- "ascii",
1782
- "hAnsi",
1783
- "eastAsia",
1784
- "hint",
1785
- "asciiTheme",
1786
- "hAnsiTheme",
1787
- "eastAsiaTheme",
1788
- "csTheme"
1789
- ].every((property) => left?.[property] === right?.[property]) && (left?.cs ?? left?.ascii) === (right?.cs ?? right?.ascii);
1790
- /**
1791
- * Negatable boolean run-property keys whose serializer emits an explicit
1792
- * `w:val="0"` override when the value is `false` (see `serializeTextFormatting`
1793
- * in `runSerializer.ts`). Each entry pairs a base key with the complex-script
1794
- * mirror that `marksToTextFormatting` sets alongside it (the bold/italic marks
1795
- * set `*Cs` too), so a negative override disables both. Keys without a complex
1796
- * mirror use `null`.
1797
- */
1798
- const NEGATABLE_STYLE_KEYS = [
1799
- ["bold", "boldCs"],
1800
- ["italic", "italicCs"],
1801
- ["strike", null],
1802
- ["doubleStrike", null],
1803
- ["allCaps", null],
1804
- ["smallCaps", null],
1805
- ["hidden", null],
1806
- ["emboss", null],
1807
- ["imprint", null],
1808
- ["shadow", null],
1809
- ["outline", null],
1810
- ["rtl", null]
1811
- ];
1812
- function subtractCharacterStyleFormatting({ directOverrideFormatting, formatting, inheritedFormatting, styleRPr }) {
1813
- const result = {};
1814
- const effectiveStyleFormatting = cascadeStyleTextFormatting([{
1815
- formatting: inheritedFormatting,
1816
- type: "direct"
1817
- }, {
1818
- formatting: styleRPr,
1819
- type: "style"
1820
- }]).formatting;
1821
- for (const key of Object.keys(formatting)) {
1822
- const value = formatting[key];
1823
- if (value === void 0) continue;
1824
- if (directOverrideFormatting?.[key] !== void 0) {
1825
- result[key] = value;
1826
- continue;
1827
- }
1828
- const styleValue = key === "styleId" ? void 0 : effectiveStyleFormatting?.[key];
1829
- if ((key === "boldCs" || key === "italicCs") && styleValue === false && value === true) continue;
1830
- if (styleValue !== void 0 && JSON.stringify(value) === JSON.stringify(styleValue)) continue;
1831
- result[key] = value;
2385
+ if (characterStyleId !== void 0 && !options?.styleResolver) {
2386
+ const conservativeFormatting = mergeTextFormatting(formatting, directOverrideFormatting) ?? {};
2387
+ for (const property of currentOverrideAttrs?.complexScriptPropertyAbsences ?? []) Reflect.deleteProperty(conservativeFormatting, property);
2388
+ return {
2389
+ ...conservativeFormatting,
2390
+ styleId: characterStyleId
2391
+ };
1832
2392
  }
1833
- for (const [key, csKey] of NEGATABLE_STYLE_KEYS) {
1834
- if (effectiveStyleFormatting?.[key] !== true || formatting[key] !== void 0) continue;
1835
- result[key] = false;
1836
- if (csKey !== null) result[csKey] = false;
2393
+ const inheritedFormatting = resolveEffectiveRunStyleFormatting({
2394
+ marks,
2395
+ paragraphFormatting: paragraphFormattingForRun({
2396
+ context: {
2397
+ baseParagraphFormatting: options?.baseParagraphFormatting,
2398
+ paragraphFormatting: options?.inheritedFormatting,
2399
+ paragraphMarkFormatting: options?.paragraphMarkFormatting,
2400
+ paragraphMarkPrecedesStyle: options?.paragraphMarkPrecedesStyle ?? false
2401
+ },
2402
+ directFormatting: authoredFormatting,
2403
+ marks
2404
+ }),
2405
+ ...options?.styleResolver !== void 0 ? { styleResolver: options.styleResolver } : {}
2406
+ });
2407
+ let reconciled = reconcileAuthoredFormatting({
2408
+ authoredFormatting,
2409
+ carrierlessContext: options?.paragraphMarkFormatting === void 0 ? "ordinary" : "paragraph-mark",
2410
+ currentOverrideAttrs,
2411
+ inheritedFormatting,
2412
+ observedFormatting: formatting
2413
+ });
2414
+ if (runFormattingOverrideMark === void 0 && characterStyleId === void 0 && options?.paragraphMarkFormatting !== void 0) {
2415
+ const suppressedInheritedFormatting = resolveEffectiveRunStyleFormatting({
2416
+ marks,
2417
+ paragraphFormatting: suppressParagraphMarkFormatting({
2418
+ baseFormatting: options.baseParagraphFormatting,
2419
+ directFormatting: authoredFormatting,
2420
+ paragraphMarkFormatting: options.paragraphMarkFormatting,
2421
+ paragraphMarkPrecedesStyle: options.paragraphMarkPrecedesStyle ?? false
2422
+ }),
2423
+ ...options.styleResolver !== void 0 ? { styleResolver: options.styleResolver } : {}
2424
+ });
2425
+ const suppressed = reconcileAuthoredFormatting({
2426
+ authoredFormatting,
2427
+ carrierlessContext: "paragraph-mark",
2428
+ currentOverrideAttrs,
2429
+ inheritedFormatting: suppressedInheritedFormatting,
2430
+ observedFormatting: formatting
2431
+ });
2432
+ if (formattingAuthorshipCost(suppressed) < formattingAuthorshipCost(reconciled)) reconciled = suppressed;
1837
2433
  }
1838
- return result;
2434
+ return {
2435
+ ...reconciled,
2436
+ ...characterStyleId !== void 0 ? { styleId: characterStyleId } : {}
2437
+ };
1839
2438
  }
1840
2439
  /**
1841
2440
  * Convert a ProseMirror table node to our Table type
@@ -1857,9 +2456,9 @@ function inferTableBorders(rows) {
1857
2456
  }
1858
2457
  }
1859
2458
  }
1860
- function convertPMTable(node, documentCounts) {
2459
+ function convertPMTable(node, documentCounts, styleResolver = null) {
1861
2460
  const attrs = expectTableAttrs(node);
1862
- const rows = convertPMTableRows(node, documentCounts);
2461
+ const rows = convertPMTableRows(node, documentCounts, styleResolver);
1863
2462
  const formatting = tableAttrsToFormatting(attrs) || void 0;
1864
2463
  if (!formatting?.borders) {
1865
2464
  const inferredBorders = inferTableBorders(rows);
@@ -1892,11 +2491,11 @@ function convertPMTable(node, documentCounts) {
1892
2491
  function restoreTablePropertyChanges(table, attrs) {
1893
2492
  if (Array.isArray(attrs.tblPrChange) && attrs.tblPrChange.length > 0) table.propertyChanges = [...attrs.tblPrChange];
1894
2493
  }
1895
- function convertPMTableRows(node, documentCounts) {
2494
+ function convertPMTableRows(node, documentCounts, styleResolver = null) {
1896
2495
  const rows = [];
1897
2496
  const activeVerticalMerges = /* @__PURE__ */ new Map();
1898
2497
  node.forEach((rowNode) => {
1899
- if (rowNode.type.name === "tableRow") rows.push(convertPMTableRow(rowNode, documentCounts, activeVerticalMerges));
2498
+ if (rowNode.type.name === "tableRow") rows.push(convertPMTableRow(rowNode, documentCounts, activeVerticalMerges, styleResolver));
1900
2499
  });
1901
2500
  return rows;
1902
2501
  }
@@ -1935,7 +2534,7 @@ function buildCellMarginsFromAttrs(m) {
1935
2534
  * inherited value becomes a direct override that outranks the style it came
1936
2535
  * from on reload. The resolved companion is what tells the two apart.
1937
2536
  */
1938
- const sameResolvedValue = (live, resolved) => resolved !== void 0 && resolved !== null && canonicalJson(live) === canonicalJson(resolved);
2537
+ const sameResolvedValue = (live, resolved) => resolved !== void 0 && resolved !== null && sameFormattingValue(live, resolved);
1939
2538
  function tableAttrsToFormatting(attrs) {
1940
2539
  if (attrs._originalFormatting) {
1941
2540
  const orig = attrs._originalFormatting;
@@ -1984,7 +2583,7 @@ function tableAttrsToFormatting(attrs) {
1984
2583
  /**
1985
2584
  * Convert a ProseMirror table row node to our TableRow type
1986
2585
  */
1987
- function convertPMTableRow(node, documentCounts, activeVerticalMerges) {
2586
+ function convertPMTableRow(node, documentCounts, activeVerticalMerges, styleResolver = null) {
1988
2587
  const attrs = expectTableRowAttrs(node);
1989
2588
  const cells = [];
1990
2589
  let gridColumn = 0;
@@ -2005,7 +2604,7 @@ function convertPMTableRow(node, documentCounts, activeVerticalMerges) {
2005
2604
  if (cellNode.type.name === "tableCell" || cellNode.type.name === "tableHeader") {
2006
2605
  const cellAttrs = expectTableCellAttrs(cellNode);
2007
2606
  const colspan = Math.max(cellAttrs.colspan, 1);
2008
- cells.push(convertPMTableCell(cellNode, documentCounts));
2607
+ cells.push(convertPMTableCell(cellNode, documentCounts, styleResolver));
2009
2608
  if (cellAttrs.rowspan > 1) {
2010
2609
  const continuationCells = cellAttrs._docxVMergeContinuationCells;
2011
2610
  activeVerticalMerges?.set(gridColumn, {
@@ -2099,22 +2698,23 @@ function tableRowAttrsToFormatting(attrs) {
2099
2698
  /**
2100
2699
  * Convert a ProseMirror table cell node to our TableCell type
2101
2700
  */
2102
- function convertPMTableCell(node, documentCounts) {
2701
+ function convertPMTableCell(node, documentCounts, styleResolver = null) {
2103
2702
  const attrs = expectTableCellAttrs(node);
2104
2703
  const content = [];
2105
2704
  const textBoxAnchorMarkers = /* @__PURE__ */ new Map();
2106
2705
  let previousStandaloneTextBox = null;
2107
2706
  node.forEach((contentNode) => {
2108
2707
  if (contentNode.type.name === "paragraph") {
2109
- content.push(convertPMParagraph(contentNode, documentCounts, textBoxAnchorMarkers));
2708
+ content.push(convertPMParagraph(contentNode, documentCounts, textBoxAnchorMarkers, styleResolver));
2110
2709
  previousStandaloneTextBox = null;
2111
2710
  } else if (contentNode.type.name === "table") {
2112
- content.push(convertPMTable(contentNode, documentCounts));
2711
+ content.push(convertPMTable(contentNode, documentCounts, styleResolver));
2113
2712
  previousStandaloneTextBox = null;
2114
2713
  } else if (contentNode.type.name === "textBox") previousStandaloneTextBox = appendTextBoxBlock(content, contentNode, {
2115
2714
  pendingPageBreaks: 0,
2116
2715
  previousStandaloneTextBox,
2117
- textBoxAnchorMarkers
2716
+ textBoxAnchorMarkers,
2717
+ styleResolver
2118
2718
  });
2119
2719
  });
2120
2720
  removeUnresolvedTextBoxAnchors(content, textBoxAnchorMarkers);
@@ -2195,13 +2795,13 @@ function tableCellAttrsToFormatting(attrs) {
2195
2795
  * Convert a ProseMirror textBox node back to a Paragraph wrapping a ShapeContent run.
2196
2796
  * The text box content becomes a Shape with textBody.
2197
2797
  */
2198
- function convertPMTextBox(node) {
2798
+ function convertPMTextBox(node, styleResolver = null) {
2199
2799
  const attrs = expectTextBoxAttrs(node);
2200
2800
  const verticalAlign = normalizeShapeTextAnchor(attrs.verticalAlign);
2201
2801
  const childBlocks = [];
2202
2802
  node.forEach((child) => {
2203
- if (child.type.name === "paragraph") childBlocks.push(convertPMParagraph(child));
2204
- else if (child.type.name === "table") childBlocks.push(convertPMTable(child));
2803
+ if (child.type.name === "paragraph") childBlocks.push(convertPMParagraph(child, void 0, void 0, styleResolver));
2804
+ else if (child.type.name === "table") childBlocks.push(convertPMTable(child, void 0, styleResolver));
2205
2805
  });
2206
2806
  const shape = {
2207
2807
  type: "shape",
@@ -2297,8 +2897,8 @@ function updateDocumentContent(originalDocument, pmDoc) {
2297
2897
  * Used for converting edited header/footer PM content back to the document
2298
2898
  * model.
2299
2899
  */
2300
- function proseDocToBlocks(pmDoc, baseContent) {
2301
- const blocks = extractBlocks(pmDoc);
2900
+ function proseDocToBlocks(pmDoc, baseContent, styles) {
2901
+ const blocks = extractBlocks(pmDoc, "resolve", styles ? createStyleEngine(styles) : null);
2302
2902
  const linkedSources = restoreLinkedParagraphPropertySources(blocks);
2303
2903
  if (baseContent) restoreParagraphPropertySources(blocks, baseContent, linkedSources.targets, linkedSources.sources);
2304
2904
  return blocks;