@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
@@ -1,5 +1,6 @@
1
1
  import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
2
2
  import { linkProseParagraphPropertySource, recreateProseNodeWithParagraphPropertySource } from "../../docx/paragraphPropertySource.js";
3
+ import { buildPageBreakRunSourceDescendantIndex } from "../../internal/pageBreakRunSourceDescendantIndex.js";
3
4
  import { createStyleEngine } from "../../style-engine/styleEngine.js";
4
5
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
5
6
  import { mergeParagraphFormatting, mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
@@ -9,16 +10,22 @@ import { emuToPixels } from "../../utils/units.js";
9
10
  import { setAutospacingBaseValue } from "../autospacingBase.js";
10
11
  import { buildRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
11
12
  import { stampNumberedRefFieldBaselines } from "../numberedRefFields.js";
13
+ import { pageBreakRunParagraphProjectionDispositionForFeatures } from "../pageBreakRunProjection.js";
12
14
  import { directionFromBidi } from "../paragraphDirection.js";
15
+ import { lineSpacingProvenanceFromSpacing } from "../paragraphSpacing.js";
16
+ import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, stripParagraphMarkFormattingForBodyRuns, stripParagraphMarkOnlyFormatting, suppressParagraphMarkFormatting } from "../runStyleFormatting.js";
13
17
  import { schema } from "../schema/index.js";
18
+ import { COMPLEX_SCRIPT_RUN_PROPERTY_KEYS } from "../schema/marks.js";
14
19
  import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
20
+ import { canCarryTrackedRunMark, trackedRunInlineAtomDisposition } from "../trackedRunInlineAtoms.js";
15
21
  import { assertValidProseMirrorDocument } from "../validation.js";
16
22
  import { resolveEffectiveTableCellFormatting } from "./effectiveTableCellFormatting.js";
17
- import { marksToTextFormatting } from "./fromProseDoc.js";
18
23
  import { shadingToRunShadingAttrs } from "./runShadingMark.js";
19
24
  import { sdtAttrsFromProperties } from "./sdtAttrs.js";
20
- import { panic } from "better-result";
25
+ import { TaggedError, panic } from "better-result";
21
26
  //#region src/prosemirror/conversion/toProseDoc.ts
27
+ /** DOCX content that cannot be preserved by the editable ProseMirror model. */
28
+ var UnsupportedDocxToProseMirrorConversionError = class extends TaggedError("UnsupportedDocxToProseMirrorConversionError") {};
22
29
  const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
23
30
  /**
24
31
  * Build a `nextTextBoxGroupId()` generator salted with a random per-load
@@ -106,36 +113,16 @@ function toProseDoc(document, options) {
106
113
  theme,
107
114
  nextTextBoxGroupId,
108
115
  nextHyperlinkInstanceIndex: createHyperlinkInstanceIndexAllocator(),
109
- pairedBookmarkIds: collectPairedBookmarkIds(paragraphs)
116
+ pairedBookmarkIds: collectPairedBookmarkIds(paragraphs),
117
+ pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(paragraphs)
110
118
  };
111
119
  const convertBodyBlocks = (blocks) => {
112
120
  const out = [];
113
- for (const block of blocks) if (block.type === "paragraph") {
114
- const pbPos = paragraphPageBreakPosition(block);
115
- if (pbPos === "before") out.push(schema.node("pageBreak"));
116
- const converted = convertParagraphWithTextBoxes(block, styleResolver, {
117
- textBoxGroupId: nextTextBoxGroupId(),
118
- context: conversionContext
119
- });
120
- const firstConverted = converted.at(0);
121
- if (pbPos === "before" && converted.length === 1 && firstConverted?.type.name === "paragraph" && firstConverted.content.size === 0 && document.package.settings?.splitPageBreakAndParagraphMark !== true) {
122
- const paragraph = firstConverted;
123
- converted[0] = recreateProseNodeWithParagraphPropertySource(paragraph, { attrs: {
124
- ...paragraph.attrs,
125
- _pageBreakCarrier: true
126
- } });
127
- }
128
- if (pbPos === "after") {
129
- const paragraphIndex = converted.findIndex((node) => node.type.name === "paragraph");
130
- const trailingParagraph = paragraphIndex >= 0 ? converted.at(paragraphIndex) : void 0;
131
- if (trailingParagraph) converted[paragraphIndex] = recreateProseNodeWithParagraphPropertySource(trailingParagraph, { attrs: {
132
- ...trailingParagraph.attrs,
133
- _trailingPageBreak: true
134
- } });
135
- }
136
- out.push(...converted);
137
- if (pbPos === "after") out.push(schema.node("pageBreak"));
138
- } else if (block.type === "table") out.push(convertTable(block, styleResolver, conversionContext));
121
+ for (const block of blocks) if (block.type === "paragraph") out.push(...convertParagraphWithTextBoxes(block, styleResolver, {
122
+ textBoxGroupId: nextTextBoxGroupId(),
123
+ context: conversionContext
124
+ }));
125
+ else if (block.type === "table") out.push(convertTable(block, styleResolver, conversionContext));
139
126
  else out.push(convertBlockSdt(block, convertBodyBlocks));
140
127
  return out;
141
128
  };
@@ -187,8 +174,17 @@ function convertBlockSdt(blockSdt, convertBlocks) {
187
174
  * Resolves style-based text formatting and passes it to runs so that
188
175
  * paragraph styles (like Heading1) apply their font size, color, etc.
189
176
  */
190
- function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex, pairedBookmarkIds, activeCommentIds, extraRunFormatting, tableParagraphOverlay, textBoxAnchors) {
191
- const attrs = paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay);
177
+ function convertParagraph(paragraph, styleResolver, context, activeCommentIds, extraRunFormatting, tableParagraphOverlay, textBoxAnchors) {
178
+ const { nextHyperlinkInstanceIndex, pairedBookmarkIds, pageBreakRunSourceDescendants } = context;
179
+ let pageBreakRunOwnerId = 0;
180
+ const nextPageBreakRunOwnerId = () => pageBreakRunOwnerId++;
181
+ const { attrs, effectiveFrame } = paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay);
182
+ assertParagraphPageBreakCanBeProjected({
183
+ paragraph,
184
+ attrs,
185
+ effectiveFrame,
186
+ sourceDescendants: pageBreakRunSourceDescendants
187
+ });
192
188
  const isTocParagraph = attrs._tableOfContentsLevel !== void 0;
193
189
  const inlineNodes = [];
194
190
  let inlineOffset = 0;
@@ -216,7 +212,7 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
216
212
  }
217
213
  const paragraphRunFormatting = resolveRunFormattingWithoutDefaults(paragraph.formatting?.runProperties, styleResolver);
218
214
  let inheritableParagraphRunFormatting;
219
- if (paragraphRunFormatting && !isTocParagraph) inheritableParagraphRunFormatting = stripParagraphMarkFormattingForBodyRuns(paragraphRunFormatting);
215
+ if (paragraphRunFormatting && !isTocParagraph && paragraph.formatting?.styleId === void 0) inheritableParagraphRunFormatting = stripParagraphMarkFormattingForBodyRuns(paragraphRunFormatting);
220
216
  const ordinaryStyleFormatting = paragraph.formatting?.styleId === void 0 ? mergeTextFormatting(styleRunFormatting, extraRunFormatting) : mergeTextFormatting(extraRunFormatting, styleRunFormatting);
221
217
  const orderedToggleFormatting = cascadeStyleTextFormatting([
222
218
  {
@@ -234,53 +230,75 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
234
230
  ], { ordinaryFormatting: ordinaryStyleFormatting });
235
231
  let baseRunFormatting = orderedToggleFormatting.formatting;
236
232
  if (paragraphStyleFontFamily) baseRunFormatting = mergeTextFormatting(baseRunFormatting, { fontFamily: paragraphStyleFontFamily });
237
- const paragraphMarkPrecedesStyle = paragraph.formatting?.styleId !== void 0;
238
- const ordinaryDefaultRunFormatting = paragraphMarkPrecedesStyle ? mergeTextFormatting(inheritableParagraphRunFormatting, baseRunFormatting) : mergeTextFormatting(baseRunFormatting, inheritableParagraphRunFormatting);
239
- const defaultToggleCascade = paragraphMarkPrecedesStyle ? cascadeStyleTextFormatting([{
233
+ const defaultCharacterFormatting = styleResolver?.getDefaultCharacterStyle()?.rPr;
234
+ const ordinaryBaseWithDefaultCharacter = mergeTextFormatting(defaultCharacterFormatting, baseRunFormatting);
235
+ const defaultCharacterStyleCascade = cascadeStyleTextFormatting([{
240
236
  cascade: orderedToggleFormatting,
241
237
  type: "carried"
242
- }], { ordinaryFormatting: ordinaryDefaultRunFormatting }) : cascadeStyleTextFormatting([{
243
- cascade: orderedToggleFormatting,
238
+ }, {
239
+ formatting: defaultCharacterFormatting,
240
+ type: "style"
241
+ }], { ordinaryFormatting: ordinaryBaseWithDefaultCharacter });
242
+ const ordinaryDefaultRunFormatting = mergeTextFormatting(ordinaryBaseWithDefaultCharacter, inheritableParagraphRunFormatting);
243
+ const defaultToggleCascade = cascadeStyleTextFormatting([{
244
+ cascade: defaultCharacterStyleCascade,
244
245
  type: "carried"
245
246
  }, {
246
247
  formatting: inheritableParagraphRunFormatting,
247
248
  type: "direct"
248
249
  }], { ordinaryFormatting: ordinaryDefaultRunFormatting });
249
250
  const defaultRunFormatting = defaultToggleCascade.formatting;
251
+ if (extraRunFormatting !== void 0) if (defaultRunFormatting) attrs.defaultTextFormatting = defaultRunFormatting;
252
+ else delete attrs.defaultTextFormatting;
250
253
  const getInheritedRunFormatting = (formatting, fieldType) => {
254
+ const hasCharacterStyle = formatting?.styleId !== void 0;
255
+ const inheritedBaseFormatting = hasCharacterStyle ? baseRunFormatting : ordinaryBaseWithDefaultCharacter;
256
+ const inheritedToggleCascade = hasCharacterStyle ? orderedToggleFormatting : defaultCharacterStyleCascade;
251
257
  if (fieldType === "TOC") return {
252
- formatting: hasDirectRunFormatting(formatting) ? suppressParagraphMarkFormatting(baseRunFormatting, void 0, formatting) : baseRunFormatting,
253
- toggleCascade: orderedToggleFormatting
258
+ formatting: hasDirectRunFormatting(formatting) ? suppressParagraphMarkFormatting({
259
+ baseFormatting: inheritedBaseFormatting,
260
+ directFormatting: formatting,
261
+ paragraphMarkFormatting: void 0
262
+ }) : inheritedBaseFormatting,
263
+ ...!hasCharacterStyle ? { implicitCharacterStyleApplied: true } : {},
264
+ toggleCascade: inheritedToggleCascade
254
265
  };
255
266
  if (!(hasDirectRunFormatting(formatting) || formatting?.styleId !== void 0)) return {
256
267
  formatting: defaultRunFormatting,
268
+ implicitCharacterStyleApplied: true,
257
269
  toggleCascade: defaultToggleCascade
258
270
  };
259
- const suppressedFormatting = suppressParagraphMarkFormatting(baseRunFormatting, inheritableParagraphRunFormatting, formatting, paragraphMarkPrecedesStyle);
271
+ const suppressedFormatting = suppressParagraphMarkFormatting({
272
+ baseFormatting: inheritedBaseFormatting,
273
+ directFormatting: formatting,
274
+ paragraphMarkFormatting: inheritableParagraphRunFormatting
275
+ });
260
276
  const paragraphMarkOverrides = getParagraphMarkSuppressionOverrides({
261
277
  directFormatting: formatting,
262
278
  paragraphMarkFormatting: inheritableParagraphRunFormatting,
263
279
  suppressedFormatting
264
- });
280
+ }) ?? (inheritableParagraphRunFormatting ? {} : void 0);
265
281
  return {
266
282
  formatting: suppressedFormatting,
283
+ ...!hasCharacterStyle ? { implicitCharacterStyleApplied: true } : {},
267
284
  ...paragraphMarkOverrides ? { paragraphMarkOverrides } : {},
268
- toggleCascade: orderedToggleFormatting
285
+ toggleCascade: inheritedToggleCascade
269
286
  };
270
287
  };
271
288
  const emitTrackedChange = (change, markType, moveKind) => {
272
- emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, moveKind, textBoxAnchors));
289
+ emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind, textBoxAnchors));
273
290
  };
274
291
  for (const content of paragraph.content) if (content.type === "commentRangeStart") commentIds.add(content.id);
275
292
  else if (content.type === "commentRangeEnd") commentIds.delete(content.id);
276
293
  else if (content.type === "commentReference") anchorPointComment(inlineNodes, content.id);
277
- else if (content.type === "run") emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), styleResolver, textBoxAnchors));
294
+ else if (content.type === "run") emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
278
295
  else if (content.type === "hyperlink") {
279
296
  const linkNodes = convertHyperlink(content, {
280
297
  getInheritedRunFormatting,
281
298
  styleResolver,
282
299
  hyperlinkIndex: nextHyperlinkInstanceIndex(),
283
- textBoxAnchors
300
+ textBoxAnchors,
301
+ nextPageBreakRunOwnerId
284
302
  });
285
303
  if (linkNodes.length === 0) {
286
304
  emptyHyperlinks ??= [];
@@ -298,9 +316,10 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
298
316
  getInheritedRunFormatting,
299
317
  styleResolver,
300
318
  nextHyperlinkInstanceIndex,
319
+ nextPageBreakRunOwnerId,
301
320
  textBoxAnchors
302
321
  }));
303
- else if (content.type === "inlineSdt") emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, textBoxAnchors));
322
+ else if (content.type === "inlineSdt") emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors));
304
323
  else if (content.type === "insertion" || content.type === "moveTo") emitTrackedChange(content, "insertion", content.type === "moveTo" ? "moveTo" : null);
305
324
  else if (content.type === "deletion" || content.type === "moveFrom") emitTrackedChange(content, "deletion", content.type === "moveFrom" ? "moveFrom" : null);
306
325
  else if (content.type === "mathEquation") emitInlineNode(convertMathEquation(content));
@@ -366,29 +385,31 @@ function anchorPointComment(nodes, commentId) {
366
385
  * Convert tracked change (insertion or deletion) content to PM nodes with
367
386
  * an insertion/deletion mark applied.
368
387
  */
369
- function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, moveKind = null, textBoxAnchors) {
388
+ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind = null, textBoxAnchors) {
370
389
  const nodes = [];
371
- for (const item of change.content) if (item.type === "run") nodes.push(...convertRun(item, getInheritedRunFormatting(item.formatting), styleResolver, textBoxAnchors));
390
+ for (const item of change.content) if (item.type === "run") nodes.push(...convertRun(item, getInheritedRunFormatting(item.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
372
391
  else if (item.type === "hyperlink") {
373
392
  const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
374
393
  nodes.push(...convertHyperlink(item, {
375
394
  getInheritedRunFormatting,
376
395
  styleResolver,
377
396
  hyperlinkIndex: currentHyperlinkIndex,
378
- textBoxAnchors
397
+ textBoxAnchors,
398
+ nextPageBreakRunOwnerId
379
399
  }));
380
400
  } else if (item.type === "simpleField" || item.type === "complexField") {
381
401
  const fieldNode = convertField(item, {
382
402
  getInheritedRunFormatting,
383
403
  styleResolver,
384
404
  nextHyperlinkInstanceIndex,
405
+ nextPageBreakRunOwnerId,
385
406
  textBoxAnchors
386
407
  });
387
408
  if (fieldNode) nodes.push(fieldNode);
388
409
  } else if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") {
389
410
  const nestedMarkType = item.type === "insertion" || item.type === "moveTo" ? "insertion" : "deletion";
390
411
  const nestedMoveKind = item.type === "moveFrom" || item.type === "moveTo" ? item.type : null;
391
- nodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, nestedMoveKind, textBoxAnchors));
412
+ nodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, nestedMoveKind, textBoxAnchors));
392
413
  } else if (item.type === "bookmarkStart") nodes.push(schema.node("bookmarkBoundary", {
393
414
  type: "start",
394
415
  id: item.id,
@@ -411,22 +432,11 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, getI
411
432
  });
412
433
  return nodes.map((node) => {
413
434
  if (node.marks.some(({ type }) => type.name === "insertion" || type.name === "deletion")) return node;
414
- if (canCarryTrackedRunMark(node, mark.type)) return node.mark(mark.addToSet(node.marks));
435
+ if (trackedRunInlineAtomDisposition(node) === "outside-wrapper") panic(`Inline atom ${JSON.stringify(node.type.name)} cannot occur in a tracked-run wrapper`);
436
+ if (canCarryTrackedRunMark(node)) return node.mark(mark.addToSet(node.marks));
415
437
  return node;
416
438
  });
417
439
  }
418
- function canCarryTrackedRunMark(node, markType) {
419
- if (node.isText) return true;
420
- if (!node.isInline) return false;
421
- if (node.type.name === "field" || node.type.name === "structuredField") return true;
422
- return node.type.allowsMarkType(markType) && (node.type.name === "image" || node.type.name === "shape" || node.type.name === "hardBreak" || node.type.name === "tab" || node.type.name === "symbol" || node.type.name === "bookmarkBoundary" || node.type.name === "textBoxAnchor");
423
- }
424
- /**
425
- * Convert ParagraphFormatting to ProseMirror paragraph attrs
426
- *
427
- * If a styleResolver is provided, resolves style-based formatting and merges
428
- * with inline formatting. Inline formatting takes precedence.
429
- */
430
440
  function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay) {
431
441
  const formatting = paragraph.formatting;
432
442
  const styleId = formatting?.styleId;
@@ -446,6 +456,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
446
456
  if (paragraph.listRendering?.isBullet) attrs.listIsBullet = paragraph.listRendering.isBullet;
447
457
  if (paragraph.listRendering?.isLegal) attrs.listIsLegal = paragraph.listRendering.isLegal;
448
458
  if (paragraph.listRendering?.marker) attrs.listMarker = paragraph.listRendering.marker;
459
+ if (paragraph.listRendering?.markerTemplate) attrs.listMarkerTemplate = paragraph.listRendering.markerTemplate;
449
460
  if (paragraph.listRendering?.markerHidden) attrs.listMarkerHidden = paragraph.listRendering.markerHidden;
450
461
  if (paragraph.listRendering?.markerFormatting) attrs.listMarkerFormatting = paragraph.listRendering.markerFormatting;
451
462
  if (paragraph.listRendering?.markerAlignment) attrs.listMarkerAlignment = paragraph.listRendering.markerAlignment;
@@ -475,15 +486,15 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
475
486
  set("spaceAfter", formatting?.spaceAfter ?? stylePpr?.spaceAfter);
476
487
  set("lineSpacing", formatting?.lineSpacing ?? stylePpr?.lineSpacing);
477
488
  set("lineSpacingRule", formatting?.lineSpacingRule ?? stylePpr?.lineSpacingRule);
478
- set("lineSpacingExplicit", formatting?.lineSpacing !== void 0 || formatting?.lineSpacingRule !== void 0 ? true : void 0);
489
+ set("lineSpacingExplicit", lineSpacingProvenanceFromSpacing(formatting));
479
490
  set("snapToGrid", formatting?.snapToGrid ?? stylePpr?.snapToGrid);
480
491
  set("spacingExplicit", formatting?.spacingExplicit);
481
492
  const paragraphStyle = styleId ? styleResolver.getStyle(styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
482
493
  const docDefaultSpacing = styleResolver.getDocDefaults()?.pPr;
483
- const spacingFromImplicitDefaultStyle = {};
484
- if (!styleId && formatting?.spaceBefore === void 0 && paragraphStyle?.pPr?.spaceBefore !== void 0) spacingFromImplicitDefaultStyle.before = true;
485
- if (!styleId && formatting?.spaceAfter === void 0 && paragraphStyle?.pPr?.spaceAfter !== void 0) spacingFromImplicitDefaultStyle.after = true;
486
- if (spacingFromImplicitDefaultStyle.before || spacingFromImplicitDefaultStyle.after) attrs.spacingFromImplicitDefaultStyle = spacingFromImplicitDefaultStyle;
494
+ const spacingFromStyle = {};
495
+ if (formatting?.spaceBefore === void 0 && stylePpr?.spaceBefore !== void 0) spacingFromStyle.before = true;
496
+ if (formatting?.spaceAfter === void 0 && stylePpr?.spaceAfter !== void 0) spacingFromStyle.after = true;
497
+ if (spacingFromStyle.before || spacingFromStyle.after) attrs.spacingFromImplicitDefaultStyle = spacingFromStyle;
487
498
  const spacingFromDocDefaults = {};
488
499
  if (formatting?.spaceBefore === void 0 && tableParagraphOverlay?.spaceBefore === void 0 && paragraphStyle?.pPr?.spaceBefore === void 0 && docDefaultSpacing?.spaceBefore !== void 0) spacingFromDocDefaults.before = true;
489
500
  if (formatting?.spaceAfter === void 0 && tableParagraphOverlay?.spaceAfter === void 0 && paragraphStyle?.pPr?.spaceAfter === void 0 && docDefaultSpacing?.spaceAfter !== void 0) spacingFromDocDefaults.after = true;
@@ -507,9 +518,12 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
507
518
  set("runInWithNext", formatting?.runInWithNext ?? stylePpr?.runInWithNext);
508
519
  set("outlineLevel", formatting?.outlineLevel ?? stylePpr?.outlineLevel);
509
520
  set("direction", directionFromBidi(formatting?.bidi ?? stylePpr?.bidi));
510
- set("defaultTextFormatting", resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolver, { includeParagraphMarkRunProperties: tableOfContentsLevel === void 0 }));
511
- if (!formatting?.numPr && stylePpr?.numPr && stylePpr.numPr.numId !== 0) {
512
- attrs.numPr = stylePpr.numPr;
521
+ set("defaultTextFormatting", resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolver, { includeParagraphMarkRunProperties: tableOfContentsLevel === void 0 && (styleId === void 0 || paragraph.content.length === 0) }));
522
+ if (stylePpr?.numPr && formatting?.numPr?.numId === void 0 && stylePpr.numPr.numId !== 0) {
523
+ attrs.numPr = {
524
+ ...stylePpr.numPr,
525
+ ...formatting?.numPr
526
+ };
513
527
  attrs.numPrFromStyle = stylePpr.numPr;
514
528
  }
515
529
  } else {
@@ -518,6 +532,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
518
532
  set("spaceAfter", formatting?.spaceAfter);
519
533
  set("lineSpacing", formatting?.lineSpacing);
520
534
  set("lineSpacingRule", formatting?.lineSpacingRule);
535
+ set("lineSpacingExplicit", lineSpacingProvenanceFromSpacing(formatting));
521
536
  set("snapToGrid", formatting?.snapToGrid);
522
537
  set("spacingExplicit", formatting?.spacingExplicit);
523
538
  set("indentLeft", formatting?.indentLeft);
@@ -553,15 +568,19 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
553
568
  if (afterAutospacing) setAutospacingBaseValue(base, "after", attrs.spaceAfter);
554
569
  attrs._autospacingBase = base;
555
570
  }
556
- return attrs;
571
+ return {
572
+ attrs,
573
+ effectiveFrame: formatting?.frame === void 0 ? stylePpr?.frame : {
574
+ ...stylePpr?.frame,
575
+ ...formatting.frame
576
+ }
577
+ };
557
578
  }
558
579
  /**
559
- * Pick the paragraph-spacing fields out of a table style's (or conditional
560
- * region's) `w:pPr` for use as the cell-paragraph cascade overlay. Narrower
561
- * than the full `ParagraphFormatting` bag — see
562
- * {@link TableCellParagraphSpacingOverlay}.
580
+ * Pick the modeled paragraph fields out of a table style's (or conditional
581
+ * region's) `w:pPr` for use as the cell-paragraph cascade overlay.
563
582
  */
564
- function extractTableParagraphSpacingOverlay(pPr) {
583
+ function extractTableParagraphOverlay(pPr) {
565
584
  if (!pPr) return;
566
585
  const overlay = {};
567
586
  if (pPr.spaceBefore !== void 0) overlay.spaceBefore = pPr.spaceBefore;
@@ -569,6 +588,7 @@ function extractTableParagraphSpacingOverlay(pPr) {
569
588
  if (pPr.lineSpacing !== void 0) overlay.lineSpacing = pPr.lineSpacing;
570
589
  if (pPr.lineSpacingRule !== void 0) overlay.lineSpacingRule = pPr.lineSpacingRule;
571
590
  if (pPr.contextualSpacing !== void 0) overlay.contextualSpacing = pPr.contextualSpacing;
591
+ if (pPr.frame !== void 0) overlay.frame = pPr.frame;
572
592
  return Object.keys(overlay).length > 0 ? overlay : void 0;
573
593
  }
574
594
  /**
@@ -581,11 +601,11 @@ function resolveTableStyleConditional(styleResolver, tableStyleId, conditionType
581
601
  const conditional = style.tblStylePr.find((p) => p.type === conditionType);
582
602
  if (!conditional) return;
583
603
  const mergedRunProps = mergeTextFormatting(conditional.pPr?.runProperties ? resolveRunFormattingWithoutDefaults(conditional.pPr.runProperties, styleResolver) : void 0, conditional.rPr ? resolveRunFormattingWithoutDefaults(conditional.rPr, styleResolver) : void 0);
584
- const paragraphSpacingOverlay = extractTableParagraphSpacingOverlay(conditional.pPr);
604
+ const paragraphOverlay = extractTableParagraphOverlay(conditional.pPr);
585
605
  const result = {};
586
606
  if (conditional.tcPr) result.tcPr = conditional.tcPr;
587
607
  if (mergedRunProps) result.rPr = mergedRunProps;
588
- if (paragraphSpacingOverlay) result.pPr = paragraphSpacingOverlay;
608
+ if (paragraphOverlay) result.pPr = paragraphOverlay;
589
609
  return result;
590
610
  }
591
611
  function resolveTableBaseStyle(styleResolver, tableStyleId) {
@@ -593,11 +613,11 @@ function resolveTableBaseStyle(styleResolver, tableStyleId) {
593
613
  const style = styleResolver.getStyle(tableStyleId);
594
614
  if (!style) return;
595
615
  const mergedRunProps = mergeTextFormatting(style.pPr?.runProperties ? resolveRunFormattingWithoutDefaults(style.pPr.runProperties, styleResolver) : void 0, style.rPr ? resolveRunFormattingWithoutDefaults(style.rPr, styleResolver) : void 0);
596
- const paragraphSpacingOverlay = extractTableParagraphSpacingOverlay(style.pPr);
616
+ const paragraphOverlay = extractTableParagraphOverlay(style.pPr);
597
617
  const result = {};
598
618
  if (style.tcPr) result.tcPr = style.tcPr;
599
619
  if (mergedRunProps) result.rPr = mergedRunProps;
600
- if (paragraphSpacingOverlay) result.pPr = paragraphSpacingOverlay;
620
+ if (paragraphOverlay) result.pPr = paragraphOverlay;
601
621
  return result.tcPr || result.rPr || result.pPr ? result : void 0;
602
622
  }
603
623
  function mergeConditionalStyles(base, override) {
@@ -632,70 +652,11 @@ function mergeConditionalStyles(base, override) {
632
652
  if (mergedPPr) merged.pPr = mergedPPr;
633
653
  return merged;
634
654
  }
635
- function hasDirectRunFormatting(formatting) {
636
- if (!formatting) return false;
637
- for (const key in formatting) {
638
- if (!Object.hasOwn(formatting, key)) continue;
639
- const value = Reflect.get(formatting, key);
640
- if (key !== "styleId" && value !== void 0) return true;
641
- }
642
- return false;
643
- }
644
- function stripParagraphMarkOnlyFormatting(formatting) {
645
- const { allCaps: _ac, highlight: _h, shading: _s, smallCaps: _sc, vertAlign: _va, ...rest } = formatting;
646
- return Object.keys(rest).length > 0 ? rest : void 0;
647
- }
648
- function stripParagraphMarkFormattingForBodyRuns(formatting) {
649
- const paragraphMarkFormatting = stripParagraphMarkOnlyFormatting(formatting);
650
- if (!paragraphMarkFormatting) return;
651
- const { fontFamily: _fontFamily, ...bodyRunFormatting } = paragraphMarkFormatting;
652
- return Object.keys(bodyRunFormatting).length > 0 ? bodyRunFormatting : void 0;
653
- }
654
- const PARAGRAPH_MARK_BOOLEAN_KEYS = [
655
- "bold",
656
- "italic",
657
- "strike",
658
- "doubleStrike",
659
- "allCaps",
660
- "smallCaps",
661
- "hidden",
662
- "emboss",
663
- "imprint",
664
- "shadow",
665
- "outline",
666
- "rtl"
667
- ];
668
- function suppressParagraphMarkFormatting(base, paragraphMark, direct, paragraphMarkPrecedesStyle = false) {
669
- if (!paragraphMark) return base;
670
- const result = (paragraphMarkPrecedesStyle ? mergeTextFormatting(paragraphMark, base) : mergeTextFormatting(base, paragraphMark)) ?? {};
671
- for (const key of PARAGRAPH_MARK_BOOLEAN_KEYS) suppressBooleanParagraphMark(result, base, paragraphMark, direct, key);
672
- if (paragraphMark.fontSize !== void 0 && direct?.fontSize === void 0 && base?.fontSize !== void 0) result.fontSize = base.fontSize;
673
- if (paragraphMark.fontSizeCs !== void 0 && direct?.fontSizeCs === void 0 && base?.fontSizeCs !== void 0) result.fontSizeCs = base.fontSizeCs;
674
- if (paragraphMark.underline !== void 0 && direct?.underline === void 0) result.underline = { style: "none" };
675
- if (paragraphMark.spacing !== void 0 && direct?.spacing === void 0) result.spacing = 0;
676
- return Object.keys(result).length > 0 ? result : void 0;
677
- }
678
- function getParagraphMarkSuppressionOverrides({ directFormatting, paragraphMarkFormatting, suppressedFormatting }) {
679
- if (!paragraphMarkFormatting) return;
680
- const overrides = {};
681
- for (const key of PARAGRAPH_MARK_BOOLEAN_KEYS) if (paragraphMarkFormatting[key] !== void 0 && directFormatting?.[key] === void 0 && suppressedFormatting?.[key] === false) overrides[key] = false;
682
- if (paragraphMarkFormatting.underline !== void 0 && directFormatting?.underline === void 0 && suppressedFormatting?.underline?.style === "none") overrides.underline = { style: "none" };
683
- return Object.keys(overrides).length > 0 ? overrides : void 0;
684
- }
685
- function suppressBooleanParagraphMark(result, base, paragraphMark, direct, key) {
686
- if (paragraphMark[key] === void 0 || direct?.[key] !== void 0) return;
687
- result[key] = base?.[key] ?? false;
688
- }
689
655
  function resolveTextFormatting(formatting, styleResolver) {
690
656
  if (!formatting) return styleResolver?.resolveRunStyle(null);
691
657
  if (!styleResolver) return formatting;
692
658
  return mergeTextFormatting(styleResolver.resolveRunStyle(formatting.styleId), formatting);
693
659
  }
694
- /**
695
- * Resolve an embedded character-style reference without importing
696
- * `docDefaults`. The caller already has the paragraph cascade, including
697
- * document defaults, and will layer these own properties over it.
698
- */
699
660
  function resolveRunFormattingWithoutDefaults(formatting, styleResolver) {
700
661
  if (!formatting || !styleResolver) return formatting;
701
662
  return cascadeStyleTextFormatting([{
@@ -706,6 +667,7 @@ function resolveRunFormattingWithoutDefaults(formatting, styleResolver) {
706
667
  type: "direct"
707
668
  }]).formatting;
708
669
  }
670
+ /** @internal Recompute a paragraph's inherited run defaults from authored package state. */
709
671
  function resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolver, options = {}) {
710
672
  const style = styleId ? styleResolver.getStyle(styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
711
673
  const paragraphStyleRpr = style?.type === "paragraph" ? style.rPr : void 0;
@@ -726,10 +688,6 @@ function resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolve
726
688
  }
727
689
  ], { ordinaryFormatting: mergeTextFormatting(mergeTextFormatting(styleResolver.getDocDefaults()?.rPr, styleResolver.getDefaultCharacterStyle()?.rPr), paragraphStyleRpr) });
728
690
  const bodyRunDefaults = orderedBodyToggleFormatting.formatting;
729
- if (styleId !== void 0) return cascadeStyleTextFormatting([{
730
- cascade: orderedBodyToggleFormatting,
731
- type: "carried"
732
- }], { ordinaryFormatting: mergeTextFormatting(paragraphRunProperties, bodyRunDefaults) }).formatting;
733
691
  return cascadeStyleTextFormatting([{
734
692
  cascade: orderedBodyToggleFormatting,
735
693
  type: "carried"
@@ -831,6 +789,7 @@ function paragraphContentHasMeaningfulContent(content) {
831
789
  return true;
832
790
  }
833
791
  function convertTable(table, styleResolver, context) {
792
+ for (const row of table.rows) for (const cell of row.cells) assertSourceContainerHasNoPageBreakRun(cell.content, "table-cell", context.pageBreakRunSourceDescendants);
834
793
  const rowSpanMap = calculateRowSpans(table);
835
794
  const columnWidths = table.columnWidths;
836
795
  const totalWidth = columnWidths?.reduce((sum, w) => sum + w, 0) ?? 0;
@@ -967,10 +926,7 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
967
926
  if (effectiveCells.length === 0) {
968
927
  const fallback = {
969
928
  type: "tableCell",
970
- content: [{
971
- type: "paragraph",
972
- content: []
973
- }]
929
+ content: []
974
930
  };
975
931
  if (totalCols > 1) fallback.formatting = { gridSpan: totalCols };
976
932
  effectiveCells = [fallback];
@@ -1135,14 +1091,19 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1135
1091
  return schema.node(nodeType, attrs, contentNodes);
1136
1092
  }
1137
1093
  function standaloneTableCellToProseMirror(cell, nodeType) {
1094
+ const nextTextBoxGroupId = createTextBoxGroupIdFactory();
1095
+ const nextHyperlinkInstanceIndex = createHyperlinkInstanceIndexAllocator();
1096
+ const pageBreakRunSourceDescendants = buildPageBreakRunSourceDescendantIndex(cell.content);
1097
+ assertSourceContainerHasNoPageBreakRun(cell.content, "table-cell", pageBreakRunSourceDescendants);
1138
1098
  return convertTableCell({
1139
1099
  cell,
1140
1100
  styleResolver: null,
1141
1101
  context: {
1142
1102
  theme: null,
1143
- nextTextBoxGroupId: createTextBoxGroupIdFactory(),
1144
- nextHyperlinkInstanceIndex: createHyperlinkInstanceIndexAllocator(),
1145
- pairedBookmarkIds: collectPairedBookmarkIds(cell.content)
1103
+ nextTextBoxGroupId,
1104
+ nextHyperlinkInstanceIndex,
1105
+ pairedBookmarkIds: collectPairedBookmarkIds(cell.content),
1106
+ pageBreakRunSourceDescendants
1146
1107
  },
1147
1108
  isHeader: nodeType === "tableHeader",
1148
1109
  gridWidthPercent: void 0,
@@ -1155,16 +1116,26 @@ function standaloneTableCellToProseMirror(cell, nodeType) {
1155
1116
  defaultCellMargins: void 0
1156
1117
  });
1157
1118
  }
1158
- function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyperlinkInstanceIndex, textBoxAnchors }) {
1119
+ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, textBoxAnchors }) {
1159
1120
  let displayText = "";
1160
1121
  let fieldFormatting;
1122
+ let fieldPropertyChanges;
1161
1123
  const inlineNodes = [];
1162
- const hasStructuredSourceContent = field.type === "simpleField" && field.content.some((content) => content.type === "hyperlink");
1124
+ const runHasPageBreak = (run) => run.content.some((content) => content.type === "break" && content.breakType === "page");
1125
+ if (field.type === "complexField" && field.fieldCode.some(runHasPageBreak)) throw new UnsupportedDocxToProseMirrorConversionError({
1126
+ message: "A complex-field instruction containing an explicit page break cannot be represented in the editor model",
1127
+ owner: "complex-field-instruction",
1128
+ contentType: "break"
1129
+ });
1130
+ const hasPageBreakContent = field.type === "simpleField" ? field.content.some((content) => content.type === "run" ? runHasPageBreak(content) : content.children.some((child) => child.type === "run" && runHasPageBreak(child))) : field.fieldResult.some(runHasPageBreak);
1131
+ if (hasPageBreakContent) assertPageBreakFieldResultIsRepresentable(field);
1132
+ const hasStructuredSourceContent = hasPageBreakContent || field.type === "simpleField" && field.content.some((content) => content.type === "hyperlink");
1163
1133
  const appendRun = (run) => {
1164
1134
  for (const content of run.content) if (content.type === "text") displayText += content.text;
1165
1135
  fieldFormatting ??= run.formatting;
1136
+ fieldPropertyChanges ??= run.propertyChanges;
1166
1137
  if (!hasStructuredSourceContent) return;
1167
- inlineNodes.push(...convertRun(run, getInheritedRunFormatting(run.formatting, field.fieldType), styleResolver, textBoxAnchors));
1138
+ inlineNodes.push(...convertRun(run, getInheritedRunFormatting(run.formatting, field.fieldType), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
1168
1139
  };
1169
1140
  if (field.type === "simpleField") for (const content of field.content) {
1170
1141
  if (content.type === "run") {
@@ -1174,12 +1145,14 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1174
1145
  for (const child of content.children) if (child.type === "run") {
1175
1146
  for (const runContent of child.content) if (runContent.type === "text") displayText += runContent.text;
1176
1147
  fieldFormatting ??= child.formatting;
1148
+ fieldPropertyChanges ??= child.propertyChanges;
1177
1149
  }
1178
1150
  inlineNodes.push(...convertHyperlink(content, {
1179
1151
  getInheritedRunFormatting: (formatting) => getInheritedRunFormatting(formatting, field.fieldType),
1180
1152
  styleResolver,
1181
1153
  hyperlinkIndex: nextHyperlinkInstanceIndex(),
1182
- textBoxAnchors
1154
+ textBoxAnchors,
1155
+ nextPageBreakRunOwnerId
1183
1156
  }));
1184
1157
  }
1185
1158
  else for (const run of field.fieldResult) appendRun(run);
@@ -1187,7 +1160,8 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1187
1160
  const inheritedFormatting = getInheritedRunFormatting(fieldFormatting, field.fieldType);
1188
1161
  const { marks } = buildRunMarks(fieldFormatting, inheritedFormatting, styleResolver);
1189
1162
  const hasConvertedHyperlinkContent = inlineNodes.some((node) => node.marks.some((mark) => mark.type.name === "hyperlink"));
1190
- const createStructuredField = hasStructuredSourceContent && hasConvertedHyperlinkContent;
1163
+ const createStructuredField = inlineNodes.some((node) => node.type.name === "pageBreakRun") || hasStructuredSourceContent && hasConvertedHyperlinkContent;
1164
+ if (!createStructuredField && fieldPropertyChanges && fieldPropertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...fieldPropertyChanges] }));
1191
1165
  return schema.node(createStructuredField ? "structuredField" : "field", {
1192
1166
  fieldType: field.fieldType,
1193
1167
  instruction: field.instruction,
@@ -1210,18 +1184,19 @@ function convertMathEquation(math) {
1210
1184
  /**
1211
1185
  * Convert an InlineSdt to a ProseMirror sdt node with inline content.
1212
1186
  */
1213
- function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, textBoxAnchors) {
1187
+ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors) {
1214
1188
  const props = sdt.properties;
1215
1189
  const inlineNodes = [];
1216
1190
  for (const content of sdt.content) if (content.type === "run") {
1217
- const runNodes = convertRun(content, getInheritedRunFormatting(content.formatting), styleResolver, textBoxAnchors);
1191
+ const runNodes = convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors);
1218
1192
  inlineNodes.push(...runNodes);
1219
1193
  } else if (content.type === "hyperlink") {
1220
1194
  const linkNodes = convertHyperlink(content, {
1221
1195
  getInheritedRunFormatting,
1222
1196
  styleResolver,
1223
1197
  hyperlinkIndex: nextHyperlinkInstanceIndex(),
1224
- textBoxAnchors
1198
+ textBoxAnchors,
1199
+ nextPageBreakRunOwnerId
1225
1200
  });
1226
1201
  inlineNodes.push(...linkNodes);
1227
1202
  } else if (content.type === "simpleField" || content.type === "complexField") {
@@ -1229,16 +1204,17 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, getInheritedRunFormat
1229
1204
  getInheritedRunFormatting,
1230
1205
  styleResolver,
1231
1206
  nextHyperlinkInstanceIndex,
1207
+ nextPageBreakRunOwnerId,
1232
1208
  textBoxAnchors
1233
1209
  });
1234
1210
  if (fieldNode) inlineNodes.push(fieldNode);
1235
1211
  } else if (content.type === "inlineSdt") {
1236
- const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, textBoxAnchors);
1212
+ const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors);
1237
1213
  if (nestedSdt) inlineNodes.push(nestedSdt);
1238
- } else if (content.type === "insertion") inlineNodes.push(...convertTrackedChange(content, "insertion", nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, null, textBoxAnchors));
1239
- else if (content.type === "deletion") inlineNodes.push(...convertTrackedChange(content, "deletion", nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, null, textBoxAnchors));
1240
- else if (content.type === "moveTo") inlineNodes.push(...convertTrackedChange(content, "insertion", nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, "moveTo", textBoxAnchors));
1241
- else if (content.type === "moveFrom") inlineNodes.push(...convertTrackedChange(content, "deletion", nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, "moveFrom", textBoxAnchors));
1214
+ } else if (content.type === "insertion") inlineNodes.push(...convertTrackedChange(content, "insertion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, null, textBoxAnchors));
1215
+ else if (content.type === "deletion") inlineNodes.push(...convertTrackedChange(content, "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, null, textBoxAnchors));
1216
+ else if (content.type === "moveTo") inlineNodes.push(...convertTrackedChange(content, "insertion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, "moveTo", textBoxAnchors));
1217
+ else if (content.type === "moveFrom") inlineNodes.push(...convertTrackedChange(content, "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, "moveFrom", textBoxAnchors));
1242
1218
  else {
1243
1219
  const mathNode = convertMathEquation(content);
1244
1220
  if (mathNode) inlineNodes.push(mathNode);
@@ -1251,19 +1227,208 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, getInheritedRunFormat
1251
1227
  * @param run - The run to convert
1252
1228
  * @param resolvedStyleFormatting - Paragraph formatting and its explicit style-toggle state
1253
1229
  */
1254
- function convertRun(run, resolvedStyleFormatting, styleResolver, textBoxAnchors) {
1230
+ function convertRun(run, resolvedStyleFormatting, nextPageBreakRunOwnerId, styleResolver, textBoxAnchors) {
1231
+ assertPageBreakSourceRunIsRepresentable(run);
1255
1232
  const nodes = [];
1256
1233
  const { marks, mergedFormatting } = buildRunMarks(run.formatting, resolvedStyleFormatting, styleResolver);
1257
1234
  if (run.propertyChanges && run.propertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...run.propertyChanges] }));
1235
+ if (run.content.some((content) => content.type === "break" && content.breakType === "page")) marks.push(schema.mark("pageBreakRunOwner", { id: nextPageBreakRunOwnerId() }));
1258
1236
  for (const content of run.content) {
1259
1237
  const contentNodes = convertRunContent(content, marks, mergedFormatting, textBoxAnchors);
1260
1238
  nodes.push(...contentNodes);
1261
1239
  }
1262
1240
  return nodes;
1263
1241
  }
1242
+ function assertPageBreakSourceRunIsRepresentable(run) {
1243
+ if (!run.content.some((content) => content.type === "break" && content.breakType === "page")) return;
1244
+ assertRunContentIsRepresentableBesidePageBreak(run, "page-break-bearing-run");
1245
+ }
1246
+ const PAGE_BREAK_CONTAINER_DESCRIPTIONS = {
1247
+ "table-cell": "A table cell containing an explicit page break",
1248
+ "text-box": "A text box containing an explicit page break"
1249
+ };
1250
+ const PAGE_BREAK_PARAGRAPH_OWNERS = {
1251
+ borders: "paragraph-borders",
1252
+ frame: "paragraph-frame",
1253
+ outline: "paragraph-outline",
1254
+ textBoxAnchor: "paragraph-text-box-anchor"
1255
+ };
1256
+ function assertSourceContainerHasNoPageBreakRun(content, owner, sourceDescendants) {
1257
+ if (!sourceDescendants.containsPageBreakRun(content)) return;
1258
+ throw new UnsupportedDocxToProseMirrorConversionError({
1259
+ message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS[owner]} cannot be represented in the editor model`,
1260
+ owner,
1261
+ contentType: "break"
1262
+ });
1263
+ }
1264
+ function assertParagraphPageBreakCanBeProjected({ paragraph, attrs, effectiveFrame, sourceDescendants }) {
1265
+ const sourceFeatures = sourceDescendants.paragraphFeatures(paragraph);
1266
+ if (!sourceFeatures.hasPageBreakRun) return;
1267
+ const disposition = pageBreakRunParagraphProjectionDispositionForFeatures({
1268
+ attrs,
1269
+ effectiveFrame,
1270
+ hasTextBoxAnchor: sourceFeatures.hasTextBoxShape && !sourceFeatures.pageBreakSharesTextBoxShape
1271
+ });
1272
+ if (disposition.status === "supported") return;
1273
+ throw new UnsupportedDocxToProseMirrorConversionError({
1274
+ message: disposition.message,
1275
+ owner: PAGE_BREAK_PARAGRAPH_OWNERS[disposition.reason],
1276
+ contentType: "break"
1277
+ });
1278
+ }
1279
+ function assertPageBreakFieldResultIsRepresentable(field) {
1280
+ if (field.type === "complexField") {
1281
+ for (const run of field.fieldResult) assertRunContentIsRepresentableBesidePageBreak(run, "field-result");
1282
+ return;
1283
+ }
1284
+ for (const content of field.content) {
1285
+ if (content.type === "run") {
1286
+ assertRunContentIsRepresentableBesidePageBreak(content, "field-result");
1287
+ continue;
1288
+ }
1289
+ for (const child of content.children) if (child.type === "run") assertRunContentIsRepresentableBesidePageBreak(child, "field-result");
1290
+ }
1291
+ }
1292
+ const PAGE_BREAK_OWNER_DESCRIPTIONS = {
1293
+ "field-result": "A field result with an explicit page break",
1294
+ "page-break-bearing-run": "A page-break-bearing run"
1295
+ };
1296
+ const unsupportedPageBreakContent = (owner, contentType) => {
1297
+ const description = contentType === "shape" ? "a text-box shape" : contentType;
1298
+ throw new UnsupportedDocxToProseMirrorConversionError({
1299
+ message: `${PAGE_BREAK_OWNER_DESCRIPTIONS[owner]} containing ${description} cannot be represented in the editor model`,
1300
+ owner,
1301
+ contentType
1302
+ });
1303
+ };
1304
+ function assertRunContentIsRepresentableBesidePageBreak(run, owner) {
1305
+ for (const content of run.content) switch (content.type) {
1306
+ case "break":
1307
+ case "drawing":
1308
+ case "endnoteRef":
1309
+ case "footnoteRef":
1310
+ case "renderedPageBreak":
1311
+ case "symbol":
1312
+ case "tab":
1313
+ case "text": continue;
1314
+ case "shape":
1315
+ if (content.shape.textBody) unsupportedPageBreakContent(owner, content.type);
1316
+ continue;
1317
+ case "fieldChar":
1318
+ case "instrText":
1319
+ case "noBreakHyphen":
1320
+ case "softHyphen": return unsupportedPageBreakContent(owner, content.type);
1321
+ default: panic(`Unsupported page-break-bearing run content: ${JSON.stringify(content)}`);
1322
+ }
1323
+ }
1324
+ const RUN_FORMATTING_INFERENCE = {
1325
+ bold: "ordinary-toggle",
1326
+ boldCs: "structural",
1327
+ italic: "ordinary-toggle",
1328
+ italicCs: "structural",
1329
+ underline: "underline",
1330
+ strike: "visible-boolean",
1331
+ doubleStrike: "double-strike",
1332
+ vertAlign: "visible-value",
1333
+ smallCaps: "visible-boolean",
1334
+ allCaps: "visible-boolean",
1335
+ hidden: "visible-boolean",
1336
+ color: "color",
1337
+ highlight: "visible-value",
1338
+ shading: "preserve",
1339
+ fontSize: "font-size",
1340
+ fontSizeCs: "structural",
1341
+ fontFamily: "font-family",
1342
+ language: "nested-visible-value",
1343
+ spacing: "visible-value",
1344
+ position: "visible-value",
1345
+ scale: "scale",
1346
+ kerning: "visible-value",
1347
+ effect: "visible-value",
1348
+ emphasisMark: "visible-value",
1349
+ emboss: "visible-boolean",
1350
+ imprint: "visible-boolean",
1351
+ outline: "visible-boolean",
1352
+ shadow: "visible-boolean",
1353
+ rtl: "visible-boolean",
1354
+ cs: "structural",
1355
+ styleId: "structural"
1356
+ };
1357
+ const sameFormattingValue = (left, right) => {
1358
+ if (left === right) return true;
1359
+ if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
1360
+ if (Array.isArray(left) || Array.isArray(right)) {
1361
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
1362
+ return left.every((value, index) => sameFormattingValue(value, right.at(index)));
1363
+ }
1364
+ const leftEntries = Object.entries(left).filter(([, value]) => value !== void 0);
1365
+ const rightEntries = Object.entries(right).filter(([, value]) => value !== void 0);
1366
+ if (leftEntries.length !== rightEntries.length) return false;
1367
+ return leftEntries.every(([property, value]) => Object.hasOwn(right, property) && sameFormattingValue(value, Reflect.get(right, property)));
1368
+ };
1369
+ const isReconstructibleFontFamily = (directValue, effectiveValue) => {
1370
+ if (typeof directValue !== "object" || directValue === null || !sameFormattingValue(directValue, effectiveValue)) return false;
1371
+ const properties = Object.keys(directValue).filter((property) => Reflect.get(directValue, property) !== void 0);
1372
+ return properties.length === 2 && properties.includes("ascii") && properties.includes("hAnsi") && typeof Reflect.get(directValue, "ascii") === "string" && typeof Reflect.get(directValue, "hAnsi") === "string";
1373
+ };
1374
+ const canReconstructAuthoredRunFormatting = ({ directFormatting, effectiveFormatting, inheritedFormatting, paragraphMarkOverrides }) => {
1375
+ if (paragraphMarkOverrides !== void 0) return false;
1376
+ for (const property of Object.keys(directFormatting ?? {})) {
1377
+ const directValue = directFormatting?.[property];
1378
+ if (directValue === void 0 || property === "styleId") continue;
1379
+ const inheritedValue = inheritedFormatting?.[property];
1380
+ const effectiveValue = effectiveFormatting?.[property];
1381
+ switch (RUN_FORMATTING_INFERENCE[property]) {
1382
+ case "structural": continue;
1383
+ case "ordinary-toggle":
1384
+ if (directValue !== true || inheritedValue !== true) continue;
1385
+ return false;
1386
+ case "visible-boolean":
1387
+ if (directValue === false || directValue === true && inheritedValue !== true) continue;
1388
+ return false;
1389
+ case "font-size":
1390
+ if (typeof directValue === "number" && Number.isFinite(directValue) && directValue > 0) continue;
1391
+ return false;
1392
+ case "font-family":
1393
+ if (isReconstructibleFontFamily(directValue, effectiveValue)) continue;
1394
+ return false;
1395
+ case "color":
1396
+ if (typeof directValue === "object" && directValue !== null && Reflect.get(directValue, "auto") !== true && Reflect.get(directValue, "themeColor") === void 0 && sameFormattingValue(directValue, effectiveValue)) continue;
1397
+ return false;
1398
+ case "underline":
1399
+ if (typeof directValue === "object" && directValue !== null && Reflect.get(directValue, "style") !== "none" && Reflect.get(directValue, "color") === void 0 && sameFormattingValue(directValue, effectiveValue) && !sameFormattingValue(directValue, inheritedValue)) continue;
1400
+ return false;
1401
+ case "double-strike":
1402
+ if (directValue === false) continue;
1403
+ if (directValue === true && inheritedFormatting?.strike !== true && inheritedFormatting?.doubleStrike !== true) continue;
1404
+ return false;
1405
+ case "scale":
1406
+ if (normalizeHorizontalScalePercent(typeof directValue === "number" ? directValue : void 0) === directValue && directValue !== inheritedValue) continue;
1407
+ return false;
1408
+ case "nested-visible-value":
1409
+ if (sameFormattingValue(directValue, effectiveValue) && !sameFormattingValue(directValue, inheritedValue)) continue;
1410
+ return false;
1411
+ case "visible-value":
1412
+ if (directValue !== "none" && directValue !== "baseline" && sameFormattingValue(directValue, effectiveValue) && !sameFormattingValue(directValue, inheritedValue)) continue;
1413
+ return false;
1414
+ case "preserve": return false;
1415
+ }
1416
+ }
1417
+ return true;
1418
+ };
1264
1419
  function buildRunMarks(runFormatting, inherited, styleResolver) {
1420
+ if (runFormatting === void 0 && inherited.implicitCharacterStyleApplied === true && inherited.paragraphMarkOverrides === void 0) return {
1421
+ marks: textFormattingToMarks(inherited.formatting, {
1422
+ overrideFormatting: void 0,
1423
+ directFormatting: void 0,
1424
+ authoredCarrier: "reconstruct"
1425
+ }),
1426
+ mergedFormatting: inherited.formatting
1427
+ };
1265
1428
  const styleId = runFormatting?.styleId;
1266
- const characterStyleFormatting = styleId ? styleResolver?.getRunStyleOwnProperties(styleId) : styleResolver?.getDefaultCharacterStyle()?.rPr;
1429
+ let characterStyleFormatting;
1430
+ if (styleId) characterStyleFormatting = styleResolver?.getRunStyleOwnProperties(styleId);
1431
+ else if (!inherited.implicitCharacterStyleApplied) characterStyleFormatting = styleResolver?.getDefaultCharacterStyle()?.rPr;
1267
1432
  let ordinaryRunStyleFormatting = inherited.formatting ? { ...inherited.formatting } : {};
1268
1433
  if (styleId) ordinaryRunStyleFormatting = mergeTextFormatting(inherited.formatting, characterStyleFormatting) ?? {};
1269
1434
  const cascadedStyleFormatting = cascadeStyleTextFormatting([{
@@ -1281,20 +1446,28 @@ function buildRunMarks(runFormatting, inherited, styleResolver) {
1281
1446
  formatting: runFormatting,
1282
1447
  type: "direct"
1283
1448
  }], { ordinaryFormatting: mergeTextFormatting(runStyleFormatting, runFormatting) }).formatting;
1284
- const marks = textFormattingToMarks(mergedFormatting, { overrideFormatting: getRunFormattingOverrides({
1449
+ const authoredCarrier = canReconstructAuthoredRunFormatting({
1450
+ directFormatting: runFormatting,
1451
+ effectiveFormatting: mergedFormatting,
1452
+ inheritedFormatting: runStyleFormatting,
1453
+ paragraphMarkOverrides: inherited.paragraphMarkOverrides
1454
+ }) ? "reconstruct" : "preserve";
1455
+ const overrideFormatting = getRunFormattingOverrides({
1285
1456
  directFormatting: runFormatting,
1286
1457
  effectiveStyleFormatting: runStyleFormatting,
1287
1458
  hasCharacterStyle: styleId !== void 0,
1288
1459
  paragraphMarkOverrides: inherited.paragraphMarkOverrides
1289
- }) });
1290
- addDirectFontProvenance(marks, runFormatting);
1291
- if (styleId) {
1292
- const styleRPr = characterStyleFormatting ? marksToTextFormatting(textFormattingToMarks(characterStyleFormatting)) : void 0;
1293
- marks.push(schema.mark("characterStyle", {
1294
- styleId,
1295
- _styleRPr: styleRPr && Object.keys(styleRPr).length > 0 ? styleRPr : null
1296
- }));
1460
+ });
1461
+ if (authoredCarrier === "reconstruct") {
1462
+ if (overrideFormatting?.bold === true) delete overrideFormatting.bold;
1463
+ if (overrideFormatting?.italic === true) delete overrideFormatting.italic;
1297
1464
  }
1465
+ const marks = textFormattingToMarks(mergedFormatting, {
1466
+ overrideFormatting,
1467
+ directFormatting: runFormatting,
1468
+ authoredCarrier
1469
+ });
1470
+ if (styleId) marks.push(schema.mark("characterStyle", { styleId }));
1298
1471
  return {
1299
1472
  marks,
1300
1473
  mergedFormatting
@@ -1305,12 +1478,43 @@ const addDirectFontProvenance = (marks, directFormatting) => {
1305
1478
  if (directFormatting?.fontFamily !== void 0) directFontProperties.push("fontFamily");
1306
1479
  if (directFormatting?.fontSize !== void 0) directFontProperties.push("fontSize");
1307
1480
  if (directFormatting?.color !== void 0) directFontProperties.push("color");
1308
- if (!directFormatting || !Object.keys(directFormatting).some((property) => property !== "styleId")) return;
1481
+ if (directFontProperties.length === 0) return;
1482
+ const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
1483
+ const existing = index >= 0 ? marks.at(index) : void 0;
1484
+ const override = schema.mark("runFormattingOverride", {
1485
+ ...existing?.attrs,
1486
+ directFontProperties
1487
+ });
1488
+ if (index >= 0) {
1489
+ marks[index] = override;
1490
+ return;
1491
+ }
1492
+ marks.push(override);
1493
+ };
1494
+ const COMPLEX_SCRIPT_MIRRORS = [
1495
+ {
1496
+ ordinary: "bold",
1497
+ complex: "boldCs"
1498
+ },
1499
+ {
1500
+ ordinary: "italic",
1501
+ complex: "italicCs"
1502
+ },
1503
+ {
1504
+ ordinary: "fontSize",
1505
+ complex: "fontSizeCs"
1506
+ }
1507
+ ];
1508
+ const addComplexScriptAbsenceProvenance = (marks, directFormatting) => {
1509
+ const absent = COMPLEX_SCRIPT_MIRRORS.filter(({ ordinary, complex }) => directFormatting?.[ordinary] !== void 0 && directFormatting[complex] === void 0).map(({ complex }) => complex);
1510
+ if (absent.length === 0) return;
1309
1511
  const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
1310
1512
  const existing = index >= 0 ? marks.at(index) : void 0;
1513
+ const existingAbsences = new Set(existing?.attrs["complexScriptPropertyAbsences"] ?? []);
1514
+ for (const property of absent) existingAbsences.add(property);
1311
1515
  const override = schema.mark("runFormattingOverride", {
1312
1516
  ...existing?.attrs,
1313
- ...directFontProperties.length > 0 && { directFontProperties }
1517
+ complexScriptPropertyAbsences: COMPLEX_SCRIPT_RUN_PROPERTY_KEYS.filter((property) => existingAbsences.has(property))
1314
1518
  });
1315
1519
  if (index >= 0) {
1316
1520
  marks[index] = override;
@@ -1319,8 +1523,6 @@ const addDirectFontProvenance = (marks, directFormatting) => {
1319
1523
  marks.push(override);
1320
1524
  };
1321
1525
  const ORDINARY_STYLE_TOGGLE_KEYS = [
1322
- "bold",
1323
- "italic",
1324
1526
  "strike",
1325
1527
  "allCaps",
1326
1528
  "smallCaps",
@@ -1334,8 +1536,6 @@ function getRunFormattingOverrides({ directFormatting, effectiveStyleFormatting,
1334
1536
  const overrides = mergeTextFormatting(paragraphMarkOverrides, directFormatting);
1335
1537
  if (!overrides || !directFormatting) return overrides;
1336
1538
  for (const key of ORDINARY_STYLE_TOGGLE_KEYS) if (directFormatting[key] === true && (!hasCharacterStyle || effectiveStyleFormatting?.[key] !== true)) Reflect.deleteProperty(overrides, key);
1337
- if (directFormatting.boldCs === true && directFormatting.boldCs === directFormatting.bold && (!hasCharacterStyle || effectiveStyleFormatting?.boldCs === void 0)) Reflect.deleteProperty(overrides, "boldCs");
1338
- if (directFormatting.italicCs === true && directFormatting.italicCs === directFormatting.italic && (!hasCharacterStyle || effectiveStyleFormatting?.italicCs === void 0)) Reflect.deleteProperty(overrides, "italicCs");
1339
1539
  return overrides;
1340
1540
  }
1341
1541
  /**
@@ -1347,12 +1547,21 @@ function convertRunContent(content, marks, formatting, textBoxAnchors) {
1347
1547
  if (content.text) return [schema.text(content.text, marks)];
1348
1548
  return [];
1349
1549
  case "break":
1350
- if (content.breakType === "textWrapping" || !content.breakType) return [withHyperlinkBoundaryMarks(schema.node("hardBreak"), marks)];
1351
- if (content.breakType === "column") return [withHyperlinkBoundaryMarks(schema.node("hardBreak", { breakType: "column" }), marks)];
1352
- return [];
1550
+ if (content.breakType === "textWrapping" || !content.breakType) {
1551
+ const attrs = {
1552
+ ...content.breakType !== void 0 ? { breakType: content.breakType } : {},
1553
+ ...content.clear !== void 0 ? { clear: content.clear } : {}
1554
+ };
1555
+ return [schema.node("hardBreak", attrs).mark(marks)];
1556
+ }
1557
+ if (content.breakType === "column") return [schema.node("hardBreak", {
1558
+ breakType: "column",
1559
+ ...content.clear !== void 0 ? { clear: content.clear } : {}
1560
+ }).mark(marks)];
1561
+ return [schema.node("pageBreakRun", content.clear === void 0 ? void 0 : { clear: content.clear }).mark(marks)];
1353
1562
  case "renderedPageBreak": return [schema.node("renderedPageBreak").mark(marks)];
1354
1563
  case "tab": return [schema.node("tab", content.positional ? { positional: content.positional } : void 0).mark(marks)];
1355
- case "drawing": return [withHyperlinkBoundaryMarks(convertImage({
1564
+ case "drawing": return [withRunBoundaryMarks(convertImage({
1356
1565
  image: content.image,
1357
1566
  rawXml: content.rawXml,
1358
1567
  rawXmlMode: content.rawXmlMode
@@ -1363,7 +1572,7 @@ function convertRunContent(content, marks, formatting, textBoxAnchors) {
1363
1572
  const anchorId = textBoxAnchors?.get(shp);
1364
1573
  return anchorId ? [schema.node("textBoxAnchor", { anchorId }).mark(marks)] : [];
1365
1574
  }
1366
- return [withHyperlinkBoundaryMarks(convertShape(shp), marks)];
1575
+ return [withRunBoundaryMarks(convertShape(shp), marks)];
1367
1576
  }
1368
1577
  case "footnoteRef": {
1369
1578
  const footnoteMark = schema.mark("footnoteRef", {
@@ -1391,8 +1600,8 @@ function convertRunContent(content, marks, formatting, textBoxAnchors) {
1391
1600
  }).mark(marks)];
1392
1601
  }
1393
1602
  }
1394
- function withHyperlinkBoundaryMarks(node, marks) {
1395
- if (!marks.some((mark) => mark.type.name === "hyperlink")) return node;
1603
+ function withRunBoundaryMarks(node, marks) {
1604
+ if (!marks.some(({ type }) => type.name === "hyperlink" || type.name === "pageBreakRunOwner")) return node;
1396
1605
  return node.mark(marks);
1397
1606
  }
1398
1607
  function convertImage({ image, rawXml, rawXmlMode }) {
@@ -1500,7 +1709,7 @@ function convertImage({ image, rawXml, rawXmlMode }) {
1500
1709
  _docxObjectPreview: rawXml !== void 0 && /<(?:[A-Za-z_][\w.-]*:)?object(?:\s|>)/u.test(rawXml)
1501
1710
  });
1502
1711
  }
1503
- function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver, hyperlinkIndex, textBoxAnchors }) {
1712
+ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver, hyperlinkIndex, textBoxAnchors, nextPageBreakRunOwnerId }) {
1504
1713
  const nodes = [];
1505
1714
  const href = hyperlink.href || (hyperlink.anchor ? `#${hyperlink.anchor}` : "");
1506
1715
  const linkMark = schema.mark("hyperlink", {
@@ -1528,8 +1737,11 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
1528
1737
  continue;
1529
1738
  }
1530
1739
  if (child.type === "run") {
1740
+ assertPageBreakSourceRunIsRepresentable(child);
1531
1741
  const inheritedFormatting = getInheritedRunFormatting(child.formatting);
1532
1742
  const { marks: runMarks, mergedFormatting } = buildRunMarks(child.formatting, inheritedFormatting, styleResolver);
1743
+ if (child.propertyChanges && child.propertyChanges.length > 0) runMarks.push(schema.mark("runPropertyChange", { changes: [...child.propertyChanges] }));
1744
+ if (child.content.some((content) => content.type === "break" && content.breakType === "page")) runMarks.push(schema.mark("pageBreakRunOwner", { id: nextPageBreakRunOwnerId() }));
1533
1745
  const allMarks = [...runMarks, linkMark];
1534
1746
  for (const content of child.content) nodes.push(...convertRunContent(content, allMarks, mergedFormatting, textBoxAnchors));
1535
1747
  }
@@ -1539,7 +1751,14 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
1539
1751
  function textFormattingToMarks(formatting, options) {
1540
1752
  if (!formatting) return [];
1541
1753
  const marks = [];
1542
- const overrideAttrs = buildRunFormattingOverrideAttrs(options ? options.overrideFormatting : formatting);
1754
+ const overrideFormatting = options ? options.overrideFormatting : formatting;
1755
+ let overrideAttrs;
1756
+ if (options?.authoredCarrier === "reconstruct") overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting, { type: "structural-only" });
1757
+ else if (options) overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting, {
1758
+ type: "authored-baseline",
1759
+ formatting: options.directFormatting
1760
+ });
1761
+ else overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting);
1543
1762
  if (overrideAttrs) marks.push(schema.mark("runFormattingOverride", overrideAttrs));
1544
1763
  if (formatting.bold) marks.push(schema.mark("bold"));
1545
1764
  if (formatting.italic) marks.push(schema.mark("italic"));
@@ -1592,6 +1811,8 @@ function textFormattingToMarks(formatting, options) {
1592
1811
  if (formatting.outline) marks.push(schema.mark("textOutline"));
1593
1812
  if (formatting.rtl) marks.push(schema.mark("rtl"));
1594
1813
  if (formatting.effect && formatting.effect !== "none") marks.push(schema.mark("textEffect", { effect: formatting.effect }));
1814
+ addDirectFontProvenance(marks, options?.directFormatting);
1815
+ if (options?.authoredCarrier === "preserve") addComplexScriptAbsenceProvenance(marks, options.directFormatting);
1595
1816
  return marks;
1596
1817
  }
1597
1818
  /**
@@ -1701,7 +1922,7 @@ function convertShape(shape) {
1701
1922
  }
1702
1923
  function convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId, context, extraRunFormatting, preserveEmptyWrapper, tableParagraphOverlay }) {
1703
1924
  const { textBoxes, textBoxAnchors } = extractTextBoxesFromParagraph(block, textBoxGroupId);
1704
- const pmParagraph = convertParagraph(block, styleResolver, context.nextHyperlinkInstanceIndex, context.pairedBookmarkIds, void 0, extraRunFormatting, tableParagraphOverlay, textBoxAnchors);
1925
+ const pmParagraph = convertParagraph(block, styleResolver, context, void 0, extraRunFormatting, tableParagraphOverlay, textBoxAnchors);
1705
1926
  const nodes = [];
1706
1927
  const isEmptyAfterExtraction = textBoxes.length > 0 && !hasContentBesidesTextBoxAnchors(pmParagraph);
1707
1928
  const keepWrapperParagraph = isEmptyAfterExtraction && (preserveEmptyWrapper === true || hasParagraphBoundaryPayload(block, pmParagraph));
@@ -1854,6 +2075,7 @@ function textBoxFromShape(shape, textBody) {
1854
2075
  * Convert a TextBox to a ProseMirror textBox node
1855
2076
  */
1856
2077
  function convertTextBox(textBox, styleResolver, options) {
2078
+ assertSourceContainerHasNoPageBreakRun(textBox.content, "text-box", options.context.pageBreakRunSourceDescendants);
1857
2079
  const textBoxSize = textBox.size;
1858
2080
  const widthPx = textBoxSize?.width ? emuToPixels(textBoxSize.width) : 200;
1859
2081
  const heightPx = textBoxSize?.height ? emuToPixels(textBoxSize.height) : void 0;
@@ -1966,7 +2188,8 @@ function headerFooterToProseDoc(content, options) {
1966
2188
  theme,
1967
2189
  nextTextBoxGroupId,
1968
2190
  nextHyperlinkInstanceIndex: createHyperlinkInstanceIndexAllocator(),
1969
- pairedBookmarkIds: collectPairedBookmarkIds(content)
2191
+ pairedBookmarkIds: collectPairedBookmarkIds(content),
2192
+ pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(content)
1970
2193
  };
1971
2194
  const convertBlocks = (blocks) => {
1972
2195
  const out = [];
@@ -2000,100 +2223,10 @@ function footnoteToProseDoc(content, options) {
2000
2223
  return headerFooterToProseDoc(content, options);
2001
2224
  }
2002
2225
  /**
2003
- * Determine where a page break appears inside a paragraph.
2004
- *
2005
- * Per ECMA-376 §17.3.3.1, `<w:br w:type="page"/>` is always a forced break,
2006
- * including in a paragraph whose only content is the break itself. We previously
2007
- * searched only top-level runs and missed breaks nested in hyperlinks, tracked
2008
- * changes, or fields — so those paragraphs silently dropped their forced break
2009
- * and the following content collapsed onto the previous page (common on legal
2010
- * signature pages and exhibit covers).
2011
- * See eigenpal docx-editor #409 (break-only page break sub-fix).
2012
- *
2013
- * Returns:
2014
- * "before" — the break appears before any visible content; the paragraph's
2015
- * content belongs on the NEXT page.
2016
- * "after" — the break follows some visible content; the paragraph stays
2017
- * on the current page and the next block starts a new page.
2018
- * null — no page break found.
2019
- */
2020
- function paragraphPageBreakPosition(paragraph) {
2021
- if (!mayContainPageBreak(paragraph)) return null;
2022
- return findParagraphPageBreakPosition(paragraph);
2023
- }
2024
- function mayContainPageBreak(paragraph) {
2025
- for (const item of paragraph.content) {
2026
- if (item.type === "run") {
2027
- for (const content of item.content) if (content.type === "break" && content.breakType === "page") return true;
2028
- continue;
2029
- }
2030
- if (item.type === "hyperlink" || item.type === "simpleField" || item.type === "complexField" || item.type === "inlineSdt" || item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") return true;
2031
- if (item.type === "bookmarkStart" || item.type === "bookmarkEnd" || item.type === "commentRangeStart" || item.type === "commentRangeEnd" || item.type === "commentReference" || item.type === "moveFromRangeStart" || item.type === "moveFromRangeEnd" || item.type === "moveToRangeStart" || item.type === "moveToRangeEnd" || item.type === "mathEquation") continue;
2032
- }
2033
- return false;
2034
- }
2035
- function findParagraphPageBreakPosition(paragraph) {
2036
- const state = { seenVisibleContent: false };
2037
- function isPageBreak(content) {
2038
- return content.type === "break" && content.breakType === "page";
2039
- }
2040
- function isVisibleRunContent(content) {
2041
- return content.type === "text" && content.text.length > 0 || content.type === "tab" || content.type === "drawing" || content.type === "shape" || content.type === "symbol" || content.type === "fieldChar" || content.type === "instrText" || content.type === "footnoteRef" || content.type === "endnoteRef" || content.type === "noBreakHyphen" || content.type === "softHyphen";
2042
- }
2043
- function visitRun(run) {
2044
- for (const content of run.content) {
2045
- if (isPageBreak(content)) return true;
2046
- if (isVisibleRunContent(content)) state.seenVisibleContent = true;
2047
- }
2048
- return false;
2049
- }
2050
- function visitHyperlinkChildren(hyperlink) {
2051
- for (const child of hyperlink.children) if (child.type === "run" && visitRun(child)) return true;
2052
- return false;
2053
- }
2054
- function visitRunOrHyperlinkList(children) {
2055
- for (const child of children) {
2056
- if (child.type === "run" && visitRun(child)) return true;
2057
- if (child.type === "hyperlink" && visitHyperlinkChildren(child)) return true;
2058
- if (child.type === "simpleField") for (const fieldChild of child.content) {
2059
- if (fieldChild.type === "run" && visitRun(fieldChild)) return true;
2060
- if (fieldChild.type === "hyperlink" && visitHyperlinkChildren(fieldChild)) return true;
2061
- }
2062
- if (child.type === "complexField") {
2063
- for (const run of child.fieldCode) if (visitRun(run)) return true;
2064
- for (const run of child.fieldResult) if (visitRun(run)) return true;
2065
- }
2066
- if ((child.type === "insertion" || child.type === "deletion" || child.type === "moveFrom" || child.type === "moveTo") && visitRunOrHyperlinkList(child.content)) return true;
2067
- }
2068
- return false;
2069
- }
2070
- function visitItem(item) {
2071
- if (item.type === "run") return visitRun(item);
2072
- if (item.type === "hyperlink") return visitHyperlinkChildren(item);
2073
- if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") return visitRunOrHyperlinkList(item.content);
2074
- if (item.type === "simpleField") return visitRunOrHyperlinkList(item.content);
2075
- if (item.type === "complexField") {
2076
- for (const run of item.fieldResult) if (visitRun(run)) return true;
2077
- return false;
2078
- }
2079
- if (item.type === "inlineSdt") {
2080
- for (const child of item.content) if (visitItem(child)) return true;
2081
- return false;
2082
- }
2083
- if (item.type === "mathEquation") {
2084
- state.seenVisibleContent = true;
2085
- return false;
2086
- }
2087
- return false;
2088
- }
2089
- for (const item of paragraph.content) if (visitItem(item)) return state.seenVisibleContent ? "after" : "before";
2090
- return null;
2091
- }
2092
- /**
2093
2226
  * Create an empty ProseMirror document
2094
2227
  */
2095
2228
  function createEmptyDoc() {
2096
2229
  return schema.node("doc", null, [schema.node("paragraph", {}, [])]);
2097
2230
  }
2098
2231
  //#endregion
2099
- export { createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, standaloneTableCellToProseMirror, textFormattingToMarks, toProseDoc };
2232
+ export { UnsupportedDocxToProseMirrorConversionError, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, resolveParagraphDefaultTextFormatting, standaloneTableCellToProseMirror, textFormattingToMarks, toProseDoc };