@stll/folio-core 0.44.0 → 0.46.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 (263) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.js +2 -2
  2. package/dist/ai-edits/apply.js +59 -19
  3. package/dist/ai-edits/headless.js +2 -1
  4. package/dist/compare/compare.js +2 -5
  5. package/dist/compare/content-alignment.js +78 -53
  6. package/dist/compare/inline-atoms.js +34 -20
  7. package/dist/compare/scenario.js +2 -5
  8. package/dist/compare/types.d.ts +9 -1
  9. package/dist/compare/types.js +12 -1
  10. package/dist/compat/eigenpal.d.ts +2 -1
  11. package/dist/compat/eigenpal.js +2 -1
  12. package/dist/content-controls/checkboxDisplay.d.ts +11 -0
  13. package/dist/content-controls/checkboxDisplay.js +40 -0
  14. package/dist/content-controls/findContentControls.js +3 -1
  15. package/dist/content-controls/mutateContentControls.js +21 -4
  16. package/dist/controller/contentControlWidgetController.d.ts +1 -0
  17. package/dist/controller/contentControlWidgetController.js +3 -0
  18. package/dist/controller/hiddenEditorApi.js +3 -1
  19. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  20. package/dist/controller/hiddenEditorManager.js +6 -3
  21. package/dist/display-list/build/buildDisplayList.js +20 -1
  22. package/dist/display-list/build/imagePrimitives.d.ts +1 -1
  23. package/dist/display-list/build/imagePrimitives.js +3 -1
  24. package/dist/display-list/build/paragraphPrimitives.js +21 -2
  25. package/dist/display-list/dom/renderDisplayListToDom.js +18 -10
  26. package/dist/display-list/types.d.ts +17 -0
  27. package/dist/document-operations.js +14 -3
  28. package/dist/docx/attributeRemainder.d.ts +43 -0
  29. package/dist/docx/attributeRemainder.js +65 -0
  30. package/dist/docx/blockContentParser.d.ts +1 -1
  31. package/dist/docx/blockContentParser.js +87 -63
  32. package/dist/docx/blockPlainText.d.ts +3 -4
  33. package/dist/docx/blockPlainText.js +1 -0
  34. package/dist/docx/bookmarkPlacement.js +2 -0
  35. package/dist/docx/borderParser.js +5 -5
  36. package/dist/docx/commentAnchorIndex.d.ts +30 -0
  37. package/dist/docx/commentAnchorIndex.js +50 -0
  38. package/dist/docx/commentParser.d.ts +1 -1
  39. package/dist/docx/commentParser.js +78 -50
  40. package/dist/docx/commentRangeIntegrity.d.ts +16 -1
  41. package/dist/docx/commentRangeIntegrity.js +42 -1
  42. package/dist/docx/commentRangeJoin.d.ts +9 -0
  43. package/dist/docx/commentRangeJoin.js +33 -0
  44. package/dist/docx/commentReferenceCompletion.d.ts +9 -0
  45. package/dist/docx/commentReferenceCompletion.js +47 -0
  46. package/dist/docx/commentReferenceNormalization.js +1 -0
  47. package/dist/docx/commentReplyMarkers.js +11 -19
  48. package/dist/docx/commentThreadKey.d.ts +18 -0
  49. package/dist/docx/commentThreadKey.js +22 -0
  50. package/dist/docx/compatibility.js +1 -0
  51. package/dist/docx/containerChildren.d.ts +111 -0
  52. package/dist/docx/containerChildren.gen.d.ts +28 -0
  53. package/dist/docx/containerChildren.gen.js +246 -0
  54. package/dist/docx/containerChildren.js +103 -0
  55. package/dist/docx/diagramPreview.js +87 -27
  56. package/dist/docx/documentParser.d.ts +1 -1
  57. package/dist/docx/documentParser.js +3 -3
  58. package/dist/docx/ensureParaIds.js +17 -7
  59. package/dist/docx/fieldParser.d.ts +1 -1
  60. package/dist/docx/fieldParser.js +4 -4
  61. package/dist/docx/fieldState.d.ts +26 -0
  62. package/dist/docx/fieldState.js +53 -0
  63. package/dist/docx/footnoteParser.d.ts +1 -1
  64. package/dist/docx/graphicFrameLocks.d.ts +16 -4
  65. package/dist/docx/graphicFrameLocks.js +19 -6
  66. package/dist/docx/groupDrawingParser.js +3 -3
  67. package/dist/docx/headerFooterParser.js +1 -1
  68. package/dist/docx/headerFooterReferenceNormalization.js +1 -0
  69. package/dist/docx/hyperlinkParser.d.ts +27 -8
  70. package/dist/docx/hyperlinkParser.js +78 -20
  71. package/dist/docx/imageParser.d.ts +9 -1
  72. package/dist/docx/imageParser.js +91 -20
  73. package/dist/docx/imageRawXml.d.ts +14 -1
  74. package/dist/docx/imageRawXml.js +30 -6
  75. package/dist/docx/inlineWrapperContent.d.ts +85 -0
  76. package/dist/docx/inlineWrapperContent.js +84 -0
  77. package/dist/docx/mathToMathml.js +12 -14
  78. package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
  79. package/dist/docx/nonVisualDrawingProps.js +46 -0
  80. package/dist/docx/normalizeBaseDirection.js +10 -1
  81. package/dist/docx/paraIdAttribute.d.ts +21 -0
  82. package/dist/docx/paraIdAttribute.js +64 -0
  83. package/dist/docx/paragraphParser.d.ts +1 -1
  84. package/dist/docx/paragraphParser.js +277 -168
  85. package/dist/docx/paragraphPropertySource.js +5 -1
  86. package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
  87. package/dist/docx/paragraphTextBoxEnrichment.js +9 -52
  88. package/dist/docx/paragraphTraversal.js +7 -4
  89. package/dist/docx/parseWarningMessage.js +2 -1
  90. package/dist/docx/parser.js +2 -2
  91. package/dist/docx/preservedRunContent.d.ts +32 -0
  92. package/dist/docx/preservedRunContent.js +86 -0
  93. package/dist/docx/previewBudget.d.ts +64 -0
  94. package/dist/docx/previewBudget.js +88 -0
  95. package/dist/docx/renderedPageBreakNormalization.js +2 -4
  96. package/dist/docx/revisionIdNormalization.js +17 -5
  97. package/dist/docx/rezip.js +91 -36
  98. package/dist/docx/runParser.d.ts +1 -1
  99. package/dist/docx/runParser.js +16 -12
  100. package/dist/docx/sdtPropertiesPatch.js +24 -18
  101. package/dist/docx/sectionParser.js +8 -1
  102. package/dist/docx/sectionReferenceHistory.js +2 -2
  103. package/dist/docx/selectiveSave.js +6 -6
  104. package/dist/docx/selectiveXmlPatch.d.ts +46 -2
  105. package/dist/docx/selectiveXmlPatch.js +86 -39
  106. package/dist/docx/serializer/blockSdtSerializer.js +38 -26
  107. package/dist/docx/serializer/borderSerializer.d.ts +1 -1
  108. package/dist/docx/serializer/borderSerializer.js +15 -14
  109. package/dist/docx/serializer/commentSerializer.d.ts +41 -16
  110. package/dist/docx/serializer/commentSerializer.js +87 -92
  111. package/dist/docx/serializer/documentSerializer.js +8 -11
  112. package/dist/docx/serializer/fontTableSerializer.js +6 -6
  113. package/dist/docx/serializer/headerFooterSerializer.js +12 -14
  114. package/dist/docx/serializer/markupRangeAttributes.js +2 -2
  115. package/dist/docx/serializer/noteSerializer.js +8 -8
  116. package/dist/docx/serializer/numberingSerializer.js +7 -6
  117. package/dist/docx/serializer/paragraphSerializer.js +93 -72
  118. package/dist/docx/serializer/partNamespaces.js +2 -2
  119. package/dist/docx/serializer/runSerializer.js +73 -44
  120. package/dist/docx/serializer/sectionPropertiesSerializer.js +19 -14
  121. package/dist/docx/serializer/settingsSerializer.js +4 -3
  122. package/dist/docx/serializer/stylesSerializer.js +6 -6
  123. package/dist/docx/serializer/tableSerializer.js +28 -17
  124. package/dist/docx/serializer/textFormattingSerializer.js +29 -28
  125. package/dist/docx/serializer/themeSerializer.js +6 -6
  126. package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
  127. package/dist/docx/serializer/xmlUtils.d.ts +1 -2
  128. package/dist/docx/serializer/xmlUtils.js +1 -13
  129. package/dist/docx/server/boundedArchive.d.ts +12 -0
  130. package/dist/docx/server/boundedArchive.js +20 -1
  131. package/dist/docx/server/createBilingualDocument.js +3 -1
  132. package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
  133. package/dist/docx/server/materializeYjsDocx.js +9 -1
  134. package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
  135. package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
  136. package/dist/docx/server/validateDocxConformance.js +22 -1
  137. package/dist/docx/shapeParser.js +12 -10
  138. package/dist/docx/tableParser.d.ts +1 -1
  139. package/dist/docx/tableParser.js +206 -85
  140. package/dist/docx/textBoxParser.d.ts +25 -2
  141. package/dist/docx/textBoxParser.js +59 -4
  142. package/dist/docx/unzip.d.ts +23 -0
  143. package/dist/docx/unzip.js +37 -26
  144. package/dist/docx/verbatimCapture.js +1 -1
  145. package/dist/docx/vmlImageParser.d.ts +17 -1
  146. package/dist/docx/vmlImageParser.js +20 -3
  147. package/dist/docx/vmlPreview.d.ts +1 -3
  148. package/dist/docx/vmlPreview.js +2 -30
  149. package/dist/docx/xmlEncoding.d.ts +5 -0
  150. package/dist/docx/xmlEncoding.js +12 -0
  151. package/dist/docx/xmlParser.d.ts +26 -2
  152. package/dist/docx/xmlParser.js +70 -27
  153. package/dist/docx/xmlResourceLimits.d.ts +89 -9
  154. package/dist/docx/xmlResourceLimits.js +105 -24
  155. package/dist/headless-layout.js +10 -1
  156. package/dist/index.d.ts +2 -1
  157. package/dist/index.js +2 -1
  158. package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
  159. package/dist/internal/paragraphFormattingSerialization.js +3 -2
  160. package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
  161. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  162. package/dist/layout-engine/measure/tableCellFloating.js +2 -0
  163. package/dist/layout-engine/types.d.ts +23 -1
  164. package/dist/layout-painter/renderImage.d.ts +5 -1
  165. package/dist/layout-painter/renderImage.js +14 -6
  166. package/dist/layout-painter/renderPage.d.ts +2 -0
  167. package/dist/layout-painter/renderPage.js +4 -0
  168. package/dist/layout-painter/renderParagraph.d.ts +13 -1
  169. package/dist/layout-painter/renderParagraph.js +26 -6
  170. package/dist/managers/autoSaveCodec.d.ts +9 -1
  171. package/dist/managers/autoSaveCodec.js +14 -12
  172. package/dist/markdown/images.js +1 -4
  173. package/dist/markdown/renderBlock.js +1 -0
  174. package/dist/markdown/renderRuns.d.ts +1 -1
  175. package/dist/markdown/renderRuns.js +28 -5
  176. package/dist/markdown/renderTable.js +22 -5
  177. package/dist/markdown/trailers.js +4 -1
  178. package/dist/pdf/images.js +142 -6
  179. package/dist/prosemirror/attrs/index.d.ts +14 -3
  180. package/dist/prosemirror/attrs/index.js +224 -2
  181. package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
  182. package/dist/prosemirror/authoredTransformAttrs.js +64 -0
  183. package/dist/prosemirror/commands/contentControls.js +10 -2
  184. package/dist/prosemirror/commands/image.d.ts +9 -2
  185. package/dist/prosemirror/commands/image.js +43 -28
  186. package/dist/prosemirror/commands/pageBreak.js +1 -1
  187. package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
  188. package/dist/prosemirror/commentReferenceAttrs.js +33 -0
  189. package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
  190. package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
  191. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  192. package/dist/prosemirror/conversion/fromProseDoc.js +338 -99
  193. package/dist/prosemirror/conversion/index.d.ts +2 -2
  194. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
  195. package/dist/prosemirror/conversion/toProseDoc.js +551 -327
  196. package/dist/prosemirror/extensions/StarterKit.js +8 -58
  197. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  198. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
  199. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
  200. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
  201. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
  202. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
  203. package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
  204. package/dist/prosemirror/extensions/markRegistry.js +75 -0
  205. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
  206. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
  207. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
  208. package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
  209. package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
  210. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
  211. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
  212. package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
  213. package/dist/prosemirror/extensions/nodes/ImageExtension.js +20 -0
  214. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
  215. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
  216. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
  217. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
  218. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +10 -2
  219. package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
  220. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +13 -5
  221. package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
  222. package/dist/prosemirror/inlineWrapperStack.js +75 -0
  223. package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
  224. package/dist/prosemirror/pageBreakRunProjection.js +19 -9
  225. package/dist/prosemirror/paragraphFormattingProvenance.d.ts +6 -3
  226. package/dist/prosemirror/paragraphFormattingProvenance.js +12 -3
  227. package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
  228. package/dist/prosemirror/replacedAnnotations.js +165 -0
  229. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
  230. package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
  231. package/dist/prosemirror/schema/index.d.ts +3 -3
  232. package/dist/prosemirror/schema/marks.d.ts +25 -1
  233. package/dist/prosemirror/schema/nodes.d.ts +139 -3
  234. package/dist/prosemirror/schema/nodes.js +16 -1
  235. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
  236. package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
  237. package/dist/prosemirror/validation.js +16 -2
  238. package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
  239. package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
  240. package/dist/prosemirror/zeroWidthAnchors.js +2 -0
  241. package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
  242. package/dist/render-dom/commentAnchorAttributes.js +54 -0
  243. package/dist/server.d.ts +3 -1
  244. package/dist/server.js +3 -1
  245. package/dist/types/content.d.ts +2 -2
  246. package/dist/utils/base64.d.ts +36 -0
  247. package/dist/utils/base64.js +40 -0
  248. package/dist/utils/clipboard.d.ts +9 -1
  249. package/dist/utils/clipboard.js +29 -2
  250. package/dist/utils/findReplace.js +1 -1
  251. package/dist/utils/imageLuminance.d.ts +15 -0
  252. package/dist/utils/imageLuminance.js +32 -0
  253. package/dist/utils/mergeDocumentContent.js +7 -17
  254. package/dist/utils/replaceText.js +1 -0
  255. package/dist/utils/units.d.ts +10 -1
  256. package/dist/utils/units.js +12 -1
  257. package/dist/utils/urlSecurity.d.ts +8 -2
  258. package/dist/utils/urlSecurity.js +21 -3
  259. package/package.json +2 -2
  260. package/dist/docx/blockRangeMarkers.d.ts +0 -36
  261. package/dist/docx/blockRangeMarkers.js +0 -59
  262. package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
  263. package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
@@ -1,4 +1,6 @@
1
+ import { rangedCommentIds } from "../../docx/commentAnchorIndex.js";
1
2
  import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
3
+ import { isInlineSdtContent, isTrackedChangeWrapperChild } from "../../docx/inlineWrapperContent.js";
2
4
  import { isNumberingReference } from "../../docx/numberingReference.js";
3
5
  import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
4
6
  import { isBaselineVertAlign } from "../../docx/runParser.js";
@@ -10,9 +12,12 @@ import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js"
10
12
  import { mergeParagraphFormatting, mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
11
13
  import { tableOfContentsStyleLevel } from "../../utils/tableOfContentsStyle.js";
12
14
  import { mergeTextFormatting } from "../../utils/textFormattingMerge.js";
13
- import { emuToPixels } from "../../utils/units.js";
15
+ import { emuToPixels, emuToStrokePixels } from "../../utils/units.js";
16
+ import { expectInlineWrapperMarkAttrs } from "../attrs/index.js";
17
+ import { authoredTransformAttrs } from "../authoredTransformAttrs.js";
14
18
  import { setAutospacingBaseValue } from "../autospacingBase.js";
15
19
  import { textFormattingToMarks } from "../extensions/marks/markUtils.js";
20
+ import { inlineWrapperLayer } from "../inlineWrapperStack.js";
16
21
  import { listRenderingAttrPatch } from "../listRenderingAttrs.js";
17
22
  import { stampNumberedRefFieldBaselines } from "../numberedRefFields.js";
18
23
  import { pageBreakRunParagraphProjectionDispositionForFeatures } from "../pageBreakRunProjection.js";
@@ -21,17 +26,23 @@ import { styleResolvedParagraphFormatting } from "../paragraphFormattingProvenan
21
26
  import { lineSpacingProvenanceFromSpacing } from "../paragraphSpacing.js";
22
27
  import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, resolveParagraphBodyRunFormatting, stripParagraphMarkFormattingForBodyRuns, stripParagraphMarkOnlyFormatting, suppressParagraphMarkFormatting } from "../runStyleFormatting.js";
23
28
  import { schema } from "../schema/index.js";
29
+ import { PRESERVED_XML_LEVELS } from "../schema/nodes.js";
24
30
  import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
25
31
  import { canCarryTrackedRunMark, trackedRunInlineAtomDisposition } from "../trackedRunInlineAtoms.js";
26
32
  import { assertValidProseMirrorDocument } from "../validation.js";
27
33
  import { resolveEffectiveTableCellFormatting } from "./effectiveTableCellFormatting.js";
28
34
  import { sdtAttrsFromProperties } from "./sdtAttrs.js";
29
- import { TaggedError, panic } from "better-result";
30
- import { DRAWING_RAW_XML_MODES } from "@stll/docx-core/model";
35
+ import { panic } from "better-result";
36
+ import { DRAWING_RAW_XML_MODES, PARSE_WARNING_CODES } from "@stll/docx-core/model";
31
37
  //#region src/prosemirror/conversion/toProseDoc.ts
32
- /** DOCX content that cannot be preserved by the editable ProseMirror model. */
33
- var UnsupportedDocxToProseMirrorConversionError = class extends TaggedError("UnsupportedDocxToProseMirrorConversionError") {};
34
38
  const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
39
+ const noPageBreakProjectionWarning = () => {};
40
+ const pageBreakProjectionWarn = (warn) => warn === void 0 ? noPageBreakProjectionWarning : (detail) => {
41
+ warn({
42
+ code: PARSE_WARNING_CODES.pageBreakProjectionApproximated,
43
+ detail
44
+ });
45
+ };
35
46
  /**
36
47
  * Build a `nextTextBoxGroupId()` generator salted with a random per-load
37
48
  * nonce, so minted text-box anchor ids (`<salt>:<group>:<index>`) are unique
@@ -89,6 +100,7 @@ const collectPairedBookmarkIds = (blocks) => {
89
100
  case "run":
90
101
  visitRun(child);
91
102
  break;
103
+ case "preservedInline": break;
92
104
  default: panic(`Unsupported hyperlink child: ${JSON.stringify(child)}`);
93
105
  }
94
106
  };
@@ -108,7 +120,7 @@ const collectPairedBookmarkIds = (blocks) => {
108
120
  return;
109
121
  case "simpleField":
110
122
  for (const child of content.content) if (child.type === "hyperlink") visitHyperlink(child);
111
- else visitRun(child);
123
+ else if (child.type === "run") visitRun(child);
112
124
  return;
113
125
  case "complexField":
114
126
  for (const run of [...content.fieldCode ?? [], ...content.fieldResult]) visitRun(run);
@@ -118,7 +130,7 @@ const collectPairedBookmarkIds = (blocks) => {
118
130
  case "deletion":
119
131
  case "moveFrom":
120
132
  case "moveTo":
121
- case "bidiWrapper":
133
+ case "inlineWrapper":
122
134
  for (const child of content.content) visitParagraphContent(child);
123
135
  return;
124
136
  case "commentRangeStart":
@@ -128,7 +140,8 @@ const collectPairedBookmarkIds = (blocks) => {
128
140
  case "moveFromRangeEnd":
129
141
  case "moveToRangeStart":
130
142
  case "moveToRangeEnd":
131
- case "mathEquation": return;
143
+ case "mathEquation":
144
+ case "preservedInline": return;
132
145
  default: panic(`Unsupported paragraph content: ${JSON.stringify(content)}`);
133
146
  }
134
147
  };
@@ -143,6 +156,7 @@ const collectPairedBookmarkIds = (blocks) => {
143
156
  case "blockSdt":
144
157
  visitBlocks(block.content);
145
158
  break;
159
+ case "preservedBlock": break;
146
160
  default: panic(`Unsupported block content: ${JSON.stringify(block)}`);
147
161
  }
148
162
  };
@@ -166,7 +180,10 @@ function toProseDoc(document, options) {
166
180
  nextTextBoxGroupId,
167
181
  nextHyperlinkInstanceIndex: createHyperlinkInstanceIndexAllocator(),
168
182
  pairedBookmarkIds: collectPairedBookmarkIds(paragraphs),
169
- pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(paragraphs)
183
+ pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(paragraphs),
184
+ storyRangedCommentIds: rangedCommentIds(paragraphs),
185
+ openCommentIds: /* @__PURE__ */ new Set(),
186
+ warnPageBreakProjection: pageBreakProjectionWarn(options?.warn)
170
187
  };
171
188
  const convertBodyBlocks = (blocks) => {
172
189
  const out = [];
@@ -183,6 +200,9 @@ function toProseDoc(document, options) {
183
200
  case "blockSdt":
184
201
  out.push(convertBlockSdt(block, convertBodyBlocks));
185
202
  break;
203
+ case "preservedBlock":
204
+ out.push(convertPreservedBlock(block));
205
+ break;
186
206
  default: panic(`Unsupported block content: ${JSON.stringify(block)}`);
187
207
  }
188
208
  return out;
@@ -200,6 +220,30 @@ function toProseDoc(document, options) {
200
220
  return pmDoc;
201
221
  }
202
222
  /**
223
+ * Carry a block folio does not model into the editor as a zero-width node.
224
+ *
225
+ * The node's place in the document is the whole of its position, so it needs
226
+ * no index and nothing has to keep one honest as the blocks around it change.
227
+ */
228
+ function convertPreservedBlock(block) {
229
+ return schema.node("preservedBlock", { xml: block.xml });
230
+ }
231
+ /**
232
+ * Carry an inline child folio does not model into the editor as an opaque atom.
233
+ *
234
+ * The same atom the run level uses, tagged with the level it came from: the
235
+ * markup is a paragraph child and the save path must not put it back inside a
236
+ * `w:r`. It carries whatever marks surround it, so a capture inside a
237
+ * `w:ins` keeps the insertion and is accepted or rejected with it.
238
+ */
239
+ function preservedInlineNode(content) {
240
+ return schema.node("preservedXml", {
241
+ xml: content.xml,
242
+ text: content.text,
243
+ level: PRESERVED_XML_LEVELS.inline
244
+ });
245
+ }
246
+ /**
203
247
  * Convert a `BlockSdt` model node into a `blockSdt` PM node, recursively
204
248
  * converting its children with the caller-supplied block converter. Pass
205
249
  * `rawPropertiesXml` / `rawEndPropertiesXml` through as attrs so the
@@ -236,26 +280,27 @@ function convertBlockSdt(blockSdt, convertBlocks) {
236
280
  * Resolves style-based text formatting and passes it to runs so that
237
281
  * paragraph styles (like Heading1) apply their font size, color, etc.
238
282
  */
239
- function convertParagraph(paragraph, styleResolver, context, activeCommentIds, extraRunFormatting, tableParagraphOverlay, textBoxAnchors) {
240
- const { nextHyperlinkInstanceIndex, pairedBookmarkIds, pageBreakRunSourceDescendants } = context;
283
+ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting, tableParagraphOverlay, textBoxAnchors) {
284
+ const { nextHyperlinkInstanceIndex, pairedBookmarkIds, pageBreakRunSourceDescendants, storyRangedCommentIds, openCommentIds } = context;
241
285
  let pageBreakRunOwnerId = 0;
242
286
  const nextPageBreakRunOwnerId = () => pageBreakRunOwnerId++;
243
287
  const { attrs, effectiveFrame } = paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay);
244
- assertParagraphPageBreakCanBeProjected({
288
+ reportParagraphPageBreakProjection({
245
289
  paragraph,
246
290
  attrs,
247
291
  effectiveFrame,
248
- sourceDescendants: pageBreakRunSourceDescendants
292
+ sourceDescendants: pageBreakRunSourceDescendants,
293
+ warn: context.warnPageBreakProjection
249
294
  });
250
295
  const isTocParagraph = attrs._tableOfContentsLevel !== void 0;
251
296
  const inlineNodes = [];
252
297
  let inlineOffset = 0;
253
298
  let bookmarksArr;
254
299
  let emptyHyperlinks;
255
- const commentIds = activeCommentIds ?? /* @__PURE__ */ new Set();
300
+ let wrapperStack = [];
256
301
  const emitInlineNodes = (nodes) => {
257
302
  if (nodes.length === 0) return;
258
- const markedNodes = applyCommentMarks(nodes, commentIds);
303
+ const markedNodes = applyCommentMarks(withInlineWrapperMark(nodes, wrapperStack), openCommentIds);
259
304
  inlineNodes.push(...markedNodes);
260
305
  for (const node of markedNodes) inlineOffset += node.nodeSize;
261
306
  };
@@ -321,97 +366,104 @@ function convertParagraph(paragraph, styleResolver, context, activeCommentIds, e
321
366
  };
322
367
  };
323
368
  const emitTrackedChange = (change, markType, moveKind) => {
324
- emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind, textBoxAnchors));
369
+ emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind, textBoxAnchors, wrapperStack));
325
370
  };
326
- for (const content of withoutBidiWrappers(paragraph.content)) switch (content.type) {
327
- case "commentRangeStart":
328
- commentIds.add(content.id);
329
- break;
330
- case "commentRangeEnd":
331
- commentIds.delete(content.id);
332
- break;
333
- case "commentReference":
334
- anchorPointComment(inlineNodes, content.id);
335
- break;
336
- case "run":
337
- emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
338
- break;
339
- case "hyperlink": {
340
- const linkNodes = convertHyperlink(content, {
341
- getInheritedRunFormatting,
342
- styleResolver,
343
- hyperlinkIndex: nextHyperlinkInstanceIndex(),
344
- textBoxAnchors,
345
- nextPageBreakRunOwnerId
346
- });
347
- if (linkNodes.length === 0) {
348
- emptyHyperlinks ??= [];
349
- emptyHyperlinks.push({
350
- offset: inlineOffset,
351
- ...content.href !== void 0 ? { href: content.href } : {},
352
- ...content.anchor !== void 0 ? { anchor: content.anchor } : {},
353
- ...content.tooltip !== void 0 ? { tooltip: content.tooltip } : {},
354
- ...content.rId !== void 0 ? { rId: content.rId } : {}
371
+ for (const { content, stack } of withInlineWrapperStacks(paragraph.content)) {
372
+ wrapperStack = stack;
373
+ switch (content.type) {
374
+ case "commentRangeStart":
375
+ openCommentIds.add(content.id);
376
+ break;
377
+ case "commentRangeEnd":
378
+ openCommentIds.delete(content.id);
379
+ break;
380
+ case "commentReference":
381
+ if (!storyRangedCommentIds.has(content.id)) anchorPointComment(inlineNodes, content.id);
382
+ emitInlineNode(schema.node("commentReference", { commentId: content.id }));
383
+ break;
384
+ case "run":
385
+ emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
386
+ break;
387
+ case "hyperlink": {
388
+ const linkNodes = convertHyperlink(content, {
389
+ getInheritedRunFormatting,
390
+ styleResolver,
391
+ hyperlinkIndex: nextHyperlinkInstanceIndex(),
392
+ textBoxAnchors,
393
+ nextPageBreakRunOwnerId
355
394
  });
395
+ if (linkNodes.length === 0) {
396
+ emptyHyperlinks ??= [];
397
+ emptyHyperlinks.push({
398
+ offset: inlineOffset,
399
+ ...content.href !== void 0 ? { href: content.href } : {},
400
+ ...content.anchor !== void 0 ? { anchor: content.anchor } : {},
401
+ ...content.tooltip !== void 0 ? { tooltip: content.tooltip } : {},
402
+ ...content.rId !== void 0 ? { rId: content.rId } : {}
403
+ });
404
+ break;
405
+ }
406
+ emitInlineNodes(linkNodes);
356
407
  break;
357
408
  }
358
- emitInlineNodes(linkNodes);
359
- break;
360
- }
361
- case "simpleField":
362
- case "complexField":
363
- emitInlineNode(convertField(content, {
364
- getInheritedRunFormatting,
365
- styleResolver,
366
- nextHyperlinkInstanceIndex,
367
- nextPageBreakRunOwnerId,
368
- textBoxAnchors
369
- }));
370
- break;
371
- case "inlineSdt":
372
- emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors));
373
- break;
374
- case "insertion":
375
- case "moveTo":
376
- emitTrackedChange(content, "insertion", content.type === "moveTo" ? "moveTo" : null);
377
- break;
378
- case "deletion":
379
- case "moveFrom":
380
- emitTrackedChange(content, "deletion", content.type === "moveFrom" ? "moveFrom" : null);
381
- break;
382
- case "mathEquation":
383
- emitInlineNode(convertMathEquation(content));
384
- break;
385
- case "bookmarkStart":
386
- if (pairedBookmarkIds.has(content.id)) {
387
- emitInlineNode(schema.node("bookmarkBoundary", {
388
- type: "start",
409
+ case "simpleField":
410
+ case "complexField":
411
+ emitInlineNode(convertField(content, {
412
+ getInheritedRunFormatting,
413
+ styleResolver,
414
+ nextHyperlinkInstanceIndex,
415
+ nextPageBreakRunOwnerId,
416
+ textBoxAnchors
417
+ }));
418
+ break;
419
+ case "inlineSdt":
420
+ emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors, stack));
421
+ break;
422
+ case "insertion":
423
+ case "moveTo":
424
+ emitTrackedChange(content, "insertion", content.type === "moveTo" ? "moveTo" : null);
425
+ break;
426
+ case "deletion":
427
+ case "moveFrom":
428
+ emitTrackedChange(content, "deletion", content.type === "moveFrom" ? "moveFrom" : null);
429
+ break;
430
+ case "mathEquation":
431
+ emitInlineNode(convertMathEquation(content));
432
+ break;
433
+ case "bookmarkStart":
434
+ if (pairedBookmarkIds.has(content.id)) {
435
+ emitInlineNode(schema.node("bookmarkBoundary", {
436
+ type: "start",
437
+ id: content.id,
438
+ name: content.name,
439
+ colFirst: content.colFirst,
440
+ colLast: content.colLast,
441
+ displacedByCustomXml: content.displacedByCustomXml
442
+ }));
443
+ break;
444
+ }
445
+ if (!bookmarksArr) bookmarksArr = [];
446
+ bookmarksArr.push({
447
+ id: content.id,
448
+ name: content.name
449
+ });
450
+ break;
451
+ case "bookmarkEnd":
452
+ if (pairedBookmarkIds.has(content.id)) emitInlineNode(schema.node("bookmarkBoundary", {
453
+ type: "end",
389
454
  id: content.id,
390
- name: content.name,
391
- colFirst: content.colFirst,
392
- colLast: content.colLast,
393
455
  displacedByCustomXml: content.displacedByCustomXml
394
456
  }));
395
457
  break;
396
- }
397
- if (!bookmarksArr) bookmarksArr = [];
398
- bookmarksArr.push({
399
- id: content.id,
400
- name: content.name
401
- });
402
- break;
403
- case "bookmarkEnd":
404
- if (pairedBookmarkIds.has(content.id)) emitInlineNode(schema.node("bookmarkBoundary", {
405
- type: "end",
406
- id: content.id,
407
- displacedByCustomXml: content.displacedByCustomXml
408
- }));
409
- break;
410
- case "moveFromRangeStart":
411
- case "moveFromRangeEnd":
412
- case "moveToRangeStart":
413
- case "moveToRangeEnd": break;
414
- default: panic(`Unsupported paragraph content: ${JSON.stringify(content)}`);
458
+ case "moveFromRangeStart":
459
+ case "moveFromRangeEnd":
460
+ case "moveToRangeStart":
461
+ case "moveToRangeEnd": break;
462
+ case "preservedInline":
463
+ emitInlineNode(preservedInlineNode(content));
464
+ break;
465
+ default: panic(`Unsupported paragraph content: ${JSON.stringify(content)}`);
466
+ }
415
467
  }
416
468
  if (bookmarksArr) attrs.bookmarks = bookmarksArr;
417
469
  if (emptyHyperlinks) attrs._emptyHyperlinks = emptyHyperlinks;
@@ -449,48 +501,57 @@ function anchorPointComment(nodes, commentId) {
449
501
  * Convert tracked change (insertion or deletion) content to PM nodes with
450
502
  * an insertion/deletion mark applied.
451
503
  */
452
- function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind = null, textBoxAnchors) {
504
+ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, moveKind = null, textBoxAnchors, wrappedBy = []) {
453
505
  const nodes = [];
454
- for (const item of change.content) if (item.type === "run") nodes.push(...convertRun(item, getInheritedRunFormatting(item.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
455
- else if (item.type === "hyperlink") {
456
- const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
457
- nodes.push(...convertHyperlink(item, {
458
- getInheritedRunFormatting,
459
- styleResolver,
460
- hyperlinkIndex: currentHyperlinkIndex,
461
- textBoxAnchors,
462
- nextPageBreakRunOwnerId
506
+ for (const { content: item, stack } of withInlineWrapperStacks(change.content)) {
507
+ if (!isTrackedChangeWrapperChild(item)) continue;
508
+ const itemNodes = [];
509
+ if (item.type === "run") itemNodes.push(...convertRun(item, getInheritedRunFormatting(item.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
510
+ else if (item.type === "hyperlink") {
511
+ const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
512
+ itemNodes.push(...convertHyperlink(item, {
513
+ getInheritedRunFormatting,
514
+ styleResolver,
515
+ hyperlinkIndex: currentHyperlinkIndex,
516
+ textBoxAnchors,
517
+ nextPageBreakRunOwnerId
518
+ }));
519
+ } else if (item.type === "simpleField" || item.type === "complexField") {
520
+ const fieldNode = convertField(item, {
521
+ getInheritedRunFormatting,
522
+ styleResolver,
523
+ nextHyperlinkInstanceIndex,
524
+ nextPageBreakRunOwnerId,
525
+ textBoxAnchors
526
+ });
527
+ if (fieldNode) itemNodes.push(fieldNode);
528
+ } else if (item.type === "mathEquation") {
529
+ const mathNode = convertMathEquation(item);
530
+ if (mathNode) itemNodes.push(mathNode);
531
+ } else if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") {
532
+ const nestedMarkType = item.type === "insertion" || item.type === "moveTo" ? "insertion" : "deletion";
533
+ const nestedMoveKind = item.type === "moveFrom" || item.type === "moveTo" ? item.type : null;
534
+ itemNodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, nestedMoveKind, textBoxAnchors));
535
+ } else if (item.type === "inlineSdt") {
536
+ const sdtNode = convertInlineSdt(item, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors, [...wrappedBy, ...stack]);
537
+ if (sdtNode) itemNodes.push(sdtNode);
538
+ } else if (item.type === "bookmarkStart") itemNodes.push(schema.node("bookmarkBoundary", {
539
+ type: "start",
540
+ id: item.id,
541
+ name: item.name,
542
+ colFirst: item.colFirst,
543
+ colLast: item.colLast,
544
+ displacedByCustomXml: item.displacedByCustomXml
463
545
  }));
464
- } else if (item.type === "simpleField" || item.type === "complexField") {
465
- const fieldNode = convertField(item, {
466
- getInheritedRunFormatting,
467
- styleResolver,
468
- nextHyperlinkInstanceIndex,
469
- nextPageBreakRunOwnerId,
470
- textBoxAnchors
471
- });
472
- if (fieldNode) nodes.push(fieldNode);
473
- } else if (item.type === "mathEquation") {
474
- const mathNode = convertMathEquation(item);
475
- if (mathNode) nodes.push(mathNode);
476
- } else if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") {
477
- const nestedMarkType = item.type === "insertion" || item.type === "moveTo" ? "insertion" : "deletion";
478
- const nestedMoveKind = item.type === "moveFrom" || item.type === "moveTo" ? item.type : null;
479
- nodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, nestedMoveKind, textBoxAnchors));
480
- } else if (item.type === "bookmarkStart") nodes.push(schema.node("bookmarkBoundary", {
481
- type: "start",
482
- id: item.id,
483
- name: item.name,
484
- colFirst: item.colFirst,
485
- colLast: item.colLast,
486
- displacedByCustomXml: item.displacedByCustomXml
487
- }));
488
- else if (item.type === "bookmarkEnd") nodes.push(schema.node("bookmarkBoundary", {
489
- type: "end",
490
- id: item.id,
491
- displacedByCustomXml: item.displacedByCustomXml
492
- }));
493
- else panic(`Unsupported tracked-run content: ${JSON.stringify(item)}`);
546
+ else if (item.type === "bookmarkEnd") itemNodes.push(schema.node("bookmarkBoundary", {
547
+ type: "end",
548
+ id: item.id,
549
+ displacedByCustomXml: item.displacedByCustomXml
550
+ }));
551
+ else if (item.type === "preservedInline") itemNodes.push(preservedInlineNode(item));
552
+ else panic(`Unsupported tracked-run content: ${JSON.stringify(item)}`);
553
+ nodes.push(...withInlineWrapperMark(itemNodes, stack));
554
+ }
494
555
  const mark = schema.marks[markType].create({
495
556
  revisionId: change.info.id,
496
557
  author: change.info.author,
@@ -525,6 +586,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
525
586
  if (paragraph.listRendering) Object.assign(attrs, listRenderingAttrPatch(paragraph.listRendering));
526
587
  if (formatting) attrs._originalFormatting = formatting;
527
588
  if (paragraph.propertyChanges && paragraph.propertyChanges.length > 0) attrs._propertyChanges = [...paragraph.propertyChanges];
589
+ if (paragraph.preservedAttributes && paragraph.preservedAttributes.length > 0) attrs._preservedAttributes = paragraph.preservedAttributes;
528
590
  if (paragraph.pPrMark) attrs.pPrMark = paragraph.pPrMark;
529
591
  const set = (key, val) => {
530
592
  if (val !== void 0) attrs[key] = val;
@@ -784,6 +846,7 @@ function calculateRowSpans(table) {
784
846
  const { colIndex: cellColIndex, vMerge, isMergeContinuation, startRow, hasMeaningfulContent } = cellInfo;
785
847
  const key = `${rowIndex}-${cellColIndex}`;
786
848
  if (vMerge === "restart") {
849
+ closeVerticalMerge(activeMerges, result, cellColIndex);
787
850
  activeMerges.set(cellColIndex, rowIndex);
788
851
  result.set(key, {
789
852
  rowSpan: 1,
@@ -814,7 +877,7 @@ function calculateRowSpans(table) {
814
877
  skip: true
815
878
  });
816
879
  } else {
817
- activeMerges.delete(cellColIndex);
880
+ closeVerticalMerge(activeMerges, result, cellColIndex);
818
881
  result.set(key, {
819
882
  rowSpan: 1,
820
883
  skip: false
@@ -822,19 +885,34 @@ function calculateRowSpans(table) {
822
885
  }
823
886
  }
824
887
  }
888
+ clearActiveVerticalMerges(activeMerges, result);
825
889
  return result;
826
890
  }
891
+ /**
892
+ * End the vertical merge active in one column.
893
+ *
894
+ * A `w:vMerge="restart"` is carried through the editor by its cell's rowspan,
895
+ * which only exists once a continuation joins it. A merge that closes with a
896
+ * rowspan of 1 — a restart with no continuation, one interrupted by a plain
897
+ * cell, one whose continuation is in a row a revision removed — has nothing
898
+ * but this flag to say the cell was a merge origin, and dropping it changes
899
+ * the table's visible structure.
900
+ */
901
+ function closeVerticalMerge(activeMerges, result, colIndex) {
902
+ const startRow = activeMerges.get(colIndex);
903
+ if (startRow === void 0) return;
904
+ const restartCell = result.get(`${startRow}-${colIndex}`);
905
+ if (restartCell && restartCell.rowSpan === 1) restartCell.preserveVMergeRestart = true;
906
+ activeMerges.delete(colIndex);
907
+ }
827
908
  function clearActiveVerticalMerges(activeMerges, result) {
828
- for (const [colIndex, startRow] of activeMerges) {
829
- const restartCell = result.get(`${startRow}-${colIndex}`);
830
- if (restartCell) restartCell.preserveVMergeRestart = true;
831
- }
832
- activeMerges.clear();
909
+ for (const colIndex of [...activeMerges.keys()]) closeVerticalMerge(activeMerges, result, colIndex);
833
910
  }
834
911
  function tableCellHasMeaningfulContent(cell) {
835
912
  return cell.content.some(blockHasMeaningfulContent);
836
913
  }
837
914
  function blockHasMeaningfulContent(block) {
915
+ if (block.type === "preservedBlock") return true;
838
916
  if (block.type === "table") return block.rows.some((row) => row.cells.some((cell) => tableCellHasMeaningfulContent(cell)));
839
917
  return block.content.some(paragraphContentHasMeaningfulContent);
840
918
  }
@@ -846,12 +924,11 @@ function paragraphContentHasMeaningfulContent(content) {
846
924
  }
847
925
  function convertTable(table, styleResolver, context) {
848
926
  for (const row of table.rows) for (const cell of row.cells) {
849
- if (isCellMergeContinuation(cell) && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) throw new UnsupportedDocxToProseMirrorConversionError({
850
- message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS["table-cell"]} cannot be represented in the editor model`,
851
- owner: "table-cell",
852
- contentType: "break"
853
- });
854
- assertSourceContainerHasNoPageBreakRun(cell.content, "table-cell", context.pageBreakRunSourceDescendants);
927
+ if (isCellMergeContinuation(cell) && context.pageBreakRunSourceDescendants.containsPageBreakRun(cell.content)) {
928
+ context.warnPageBreakProjection("A page break inside a vertically merged table cell paginates with the merged row");
929
+ continue;
930
+ }
931
+ reportSourceContainerPageBreakRun(cell.content, "table-cell", context.pageBreakRunSourceDescendants, context.warnPageBreakProjection);
855
932
  }
856
933
  const rowSpanMap = calculateRowSpans(table);
857
934
  const columnWidths = table.columnWidths;
@@ -957,6 +1034,7 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
957
1034
  if (resolvedJustification) attrsWithoutStructuralChange._resolvedJustification = resolvedJustification;
958
1035
  if (row.formatting) attrsWithoutStructuralChange._originalFormatting = row.formatting;
959
1036
  if (row.propertyChanges && row.propertyChanges.length > 0) attrsWithoutStructuralChange.trPrChange = [...row.propertyChanges];
1037
+ if (row.preservedAttributes && row.preservedAttributes.length > 0) attrsWithoutStructuralChange._preservedAttributes = row.preservedAttributes;
960
1038
  let attrs = attrsWithoutStructuralChange;
961
1039
  const rowStructuralChange = row.structuralChange;
962
1040
  if (rowStructuralChange) {
@@ -1153,13 +1231,23 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1153
1231
  if (preserveVMergeRestart) attrs._preserveVMergeRestart = true;
1154
1232
  if (vMergeContinuationCells && vMergeContinuationCells.length > 0) attrs._docxVMergeContinuationCells = transportTableCellsWithParagraphPropertySources(vMergeContinuationCells);
1155
1233
  const contentNodes = [];
1156
- for (const content of cell.content) if (content.type === "paragraph") contentNodes.push(...convertParagraphWithTextBoxes(content, styleResolver, {
1157
- textBoxGroupId: context.nextTextBoxGroupId(),
1158
- context,
1159
- ...conditionalStyle?.rPr !== void 0 ? { extraRunFormatting: conditionalStyle.rPr } : {},
1160
- ...conditionalStyle?.pPr !== void 0 ? { tableParagraphOverlay: conditionalStyle.pPr } : {}
1161
- }));
1162
- else contentNodes.push(convertTable(content, styleResolver, context));
1234
+ for (const content of cell.content) switch (content.type) {
1235
+ case "paragraph":
1236
+ contentNodes.push(...convertParagraphWithTextBoxes(content, styleResolver, {
1237
+ textBoxGroupId: context.nextTextBoxGroupId(),
1238
+ context,
1239
+ ...conditionalStyle?.rPr !== void 0 ? { extraRunFormatting: conditionalStyle.rPr } : {},
1240
+ ...conditionalStyle?.pPr !== void 0 ? { tableParagraphOverlay: conditionalStyle.pPr } : {}
1241
+ }));
1242
+ break;
1243
+ case "table":
1244
+ contentNodes.push(convertTable(content, styleResolver, context));
1245
+ break;
1246
+ case "preservedBlock":
1247
+ contentNodes.push(convertPreservedBlock(content));
1248
+ break;
1249
+ default: panic(`Unsupported table cell content: ${JSON.stringify(content)}`);
1250
+ }
1163
1251
  if (contentNodes.length === 0) contentNodes.push(schema.node("paragraph", {}, []));
1164
1252
  const nodeType = isHeader ? "tableHeader" : "tableCell";
1165
1253
  return schema.node(nodeType, attrs, contentNodes);
@@ -1168,7 +1256,7 @@ function standaloneTableCellToProseMirror(cell, nodeType) {
1168
1256
  const nextTextBoxGroupId = createTextBoxGroupIdFactory();
1169
1257
  const nextHyperlinkInstanceIndex = createHyperlinkInstanceIndexAllocator();
1170
1258
  const pageBreakRunSourceDescendants = buildPageBreakRunSourceDescendantIndex(cell.content);
1171
- assertSourceContainerHasNoPageBreakRun(cell.content, "table-cell", pageBreakRunSourceDescendants);
1259
+ reportSourceContainerPageBreakRun(cell.content, "table-cell", pageBreakRunSourceDescendants, noPageBreakProjectionWarning);
1172
1260
  return convertTableCell({
1173
1261
  cell,
1174
1262
  styleResolver: null,
@@ -1177,7 +1265,10 @@ function standaloneTableCellToProseMirror(cell, nodeType) {
1177
1265
  nextTextBoxGroupId,
1178
1266
  nextHyperlinkInstanceIndex,
1179
1267
  pairedBookmarkIds: collectPairedBookmarkIds(cell.content),
1180
- pageBreakRunSourceDescendants
1268
+ pageBreakRunSourceDescendants,
1269
+ storyRangedCommentIds: rangedCommentIds(cell.content),
1270
+ openCommentIds: /* @__PURE__ */ new Set(),
1271
+ warnPageBreakProjection: noPageBreakProjectionWarning
1181
1272
  },
1182
1273
  isHeader: nodeType === "tableHeader",
1183
1274
  gridWidthPercent: void 0,
@@ -1195,15 +1286,7 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1195
1286
  let fieldFormatting;
1196
1287
  let fieldPropertyChanges;
1197
1288
  const inlineNodes = [];
1198
- const runHasPageBreak = (run) => run.content.some((content) => content.type === "break" && content.breakType === "page");
1199
- if (field.type === "complexField" && field.fieldCode.some(runHasPageBreak)) throw new UnsupportedDocxToProseMirrorConversionError({
1200
- message: "A complex-field instruction containing an explicit page break cannot be represented in the editor model",
1201
- owner: "complex-field-instruction",
1202
- contentType: "break"
1203
- });
1204
- 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);
1205
- if (hasPageBreakContent) assertPageBreakFieldResultIsRepresentable(field);
1206
- const hasStructuredSourceContent = hasPageBreakContent || field.type === "simpleField" && field.content.some((content) => content.type === "hyperlink");
1289
+ const hasStructuredSourceContent = fieldResultHasPageBreakContent(field) || field.type === "simpleField" && field.content.some((content) => content.type === "hyperlink" || content.type === "preservedInline");
1207
1290
  const appendRun = (run) => {
1208
1291
  for (const content of run.content) if (content.type === "text") displayText += content.text;
1209
1292
  fieldFormatting ??= run.formatting;
@@ -1216,6 +1299,10 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1216
1299
  appendRun(content);
1217
1300
  continue;
1218
1301
  }
1302
+ if (content.type === "preservedInline") {
1303
+ inlineNodes.push(preservedInlineNode(content));
1304
+ continue;
1305
+ }
1219
1306
  for (const child of content.children) if (child.type === "run") {
1220
1307
  for (const runContent of child.content) if (runContent.type === "text") displayText += runContent.text;
1221
1308
  fieldFormatting ??= child.formatting;
@@ -1234,36 +1321,64 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1234
1321
  const inheritedFormatting = getInheritedRunFormatting(fieldFormatting, field.fieldType);
1235
1322
  const { marks } = buildRunMarks(fieldFormatting, inheritedFormatting, styleResolver);
1236
1323
  const hasConvertedHyperlinkContent = inlineNodes.some((node) => node.marks.some((mark) => mark.type.name === "hyperlink"));
1237
- const createStructuredField = inlineNodes.some((node) => node.type.name === "pageBreakRun") || hasStructuredSourceContent && hasConvertedHyperlinkContent;
1324
+ const hasConvertedPageBreakContent = inlineNodes.some((node) => node.type.name === "pageBreakRun");
1325
+ const hasConvertedPreservedContent = inlineNodes.some((node) => node.type.name === "preservedXml");
1326
+ const createStructuredField = hasConvertedPageBreakContent || hasConvertedPreservedContent || hasStructuredSourceContent && hasConvertedHyperlinkContent;
1238
1327
  if (!createStructuredField && fieldPropertyChanges && fieldPropertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...fieldPropertyChanges] }));
1239
1328
  return schema.node(createStructuredField ? "structuredField" : "field", {
1240
1329
  fieldType: field.fieldType,
1241
1330
  instruction: field.instruction,
1242
1331
  displayText,
1243
1332
  fieldKind: field.type === "simpleField" ? "simple" : "complex",
1244
- fldLock: field.fldLock ?? false,
1245
- dirty: field.dirty ?? false
1333
+ fldLock: field.fldLock ?? null,
1334
+ dirty: field.dirty ?? null
1246
1335
  }, createStructuredField ? inlineNodes : void 0, marks);
1247
1336
  }
1248
1337
  /**
1249
- * Convert a MathEquation to a ProseMirror math node.
1338
+ * Lift the transparent wrappers out of an inline content list, remembering for
1339
+ * each item which wrappers it sat inside.
1340
+ *
1341
+ * The tree becomes one sequence because every inline loop below narrows by a
1342
+ * chain of `else if` rather than by exhaustion, and a wrapper left in it would
1343
+ * reach whichever branch happens to be last. What the wrapper said is not lost
1344
+ * with the tree: it rides the `inlineWrapper` mark on the leaves it held, and
1345
+ * ProseMirror maintains that range across edits instead of an index.
1346
+ *
1347
+ * It is applied to the content of whatever holds the wrapper, never around it:
1348
+ * a wrapper inside a revision or a content control is lifted inside that
1349
+ * wrapper, so its runs keep the revision mark or the control they were
1350
+ * authored under.
1250
1351
  */
1352
+ const withInlineWrapperStacks = (content, stack = []) => content.flatMap((item) => item.type === "inlineWrapper" ? withInlineWrapperStacks(item.content, [...stack, inlineWrapperLayer(item)]) : [{
1353
+ content: item,
1354
+ stack
1355
+ }]);
1251
1356
  /**
1252
- * Flatten `w:bdo`/`w:dir` out of an inline content list.
1357
+ * `nodes` with `stack` recorded outside whatever wrapper they already carry.
1253
1358
  *
1254
- * The editor has no projection for a bidirectional wrapper yet, and the
1255
- * alternative to flattening it is worse than losing the direction: every
1256
- * inline loop below narrows by a chain of `else if` rather than by
1257
- * exhaustion, so an unhandled member reaches whichever branch happens to be
1258
- * last and is read as that. Flattening keeps the wrapper's content and loses
1259
- * only its direction, which the survival census records as an
1260
- * editor-projection loss until the mark that carries it exists.
1359
+ * A revision converts its own content first, so a node that arrives here
1360
+ * already marked sat inside the item this stack wraps: the two stacks
1361
+ * concatenate, outermost first. Concatenating is what the schema forces — the
1362
+ * mark excludes itself, so a second one replaces the first, and the inner
1363
+ * wrapper would be the one lost.
1261
1364
  *
1262
- * The save path is unaffected: a document opened and saved without being
1263
- * edited replays its markup, and one that is edited keeps the wrapper because
1264
- * `fromProseDoc` rebuilds from the source paragraph.
1365
+ * A node that holds its own leaves, a content control, is marked here as the
1366
+ * one node it is; its leaves were marked with the whole enclosing stack when
1367
+ * it was built, because the painter reads the wrapper off the leaf.
1368
+ */
1369
+ const withInlineWrapperMark = (nodes, stack) => {
1370
+ const markType = schema.marks["inlineWrapper"];
1371
+ if (stack.length === 0 || !markType) return nodes;
1372
+ return nodes.map((node) => {
1373
+ if (!node.isText && (!node.isInline || !node.type.allowsMarkType(markType))) return node;
1374
+ const inner = node.marks.find((mark) => mark.type === markType);
1375
+ const layers = inner === void 0 ? stack : [...stack, ...expectInlineWrapperMarkAttrs(inner).stack];
1376
+ return node.mark(markType.create({ stack: layers }).addToSet(node.marks));
1377
+ });
1378
+ };
1379
+ /**
1380
+ * Convert a MathEquation to a ProseMirror math node.
1265
1381
  */
1266
- const withoutBidiWrappers = (content) => content.flatMap((item) => item.type === "bidiWrapper" ? withoutBidiWrappers(item.content) : [item]);
1267
1382
  function convertMathEquation(math) {
1268
1383
  return schema.node("math", {
1269
1384
  display: math.display,
@@ -1273,54 +1388,66 @@ function convertMathEquation(math) {
1273
1388
  }
1274
1389
  /**
1275
1390
  * Convert an InlineSdt to a ProseMirror sdt node with inline content.
1391
+ *
1392
+ * `wrappedBy` is the stack the control itself sits inside. The control holds
1393
+ * its own leaves, so nothing outside can mark them afterwards: the caller's
1394
+ * stack has to reach them here, under the wrappers written inside the control.
1276
1395
  */
1277
- function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors) {
1396
+ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors, wrappedBy = []) {
1278
1397
  const props = sdt.properties;
1279
1398
  const inlineNodes = [];
1280
- for (const content of sdt.content) switch (content.type) {
1281
- case "run":
1282
- inlineNodes.push(...convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
1283
- break;
1284
- case "hyperlink": {
1285
- const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
1286
- inlineNodes.push(...convertHyperlink(content, {
1287
- getInheritedRunFormatting,
1288
- styleResolver,
1289
- hyperlinkIndex: currentHyperlinkIndex,
1290
- textBoxAnchors,
1291
- nextPageBreakRunOwnerId
1292
- }));
1293
- break;
1294
- }
1295
- case "simpleField":
1296
- case "complexField": {
1297
- const fieldNode = convertField(content, {
1298
- getInheritedRunFormatting,
1299
- styleResolver,
1300
- nextHyperlinkInstanceIndex,
1301
- nextPageBreakRunOwnerId,
1302
- textBoxAnchors
1303
- });
1304
- if (fieldNode) inlineNodes.push(fieldNode);
1305
- break;
1306
- }
1307
- case "inlineSdt": {
1308
- const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors);
1309
- if (nestedSdt) inlineNodes.push(nestedSdt);
1310
- break;
1311
- }
1312
- case "insertion":
1313
- case "deletion":
1314
- case "moveTo":
1315
- case "moveFrom":
1316
- inlineNodes.push(...convertTrackedChange(content, content.type === "insertion" || content.type === "moveTo" ? "insertion" : "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, content.type === "moveTo" || content.type === "moveFrom" ? content.type : null, textBoxAnchors));
1317
- break;
1318
- case "mathEquation": {
1319
- const mathNode = convertMathEquation(content);
1320
- if (mathNode) inlineNodes.push(mathNode);
1321
- break;
1399
+ for (const { content, stack } of withInlineWrapperStacks(sdt.content, wrappedBy)) {
1400
+ if (!isInlineSdtContent(content)) continue;
1401
+ const itemNodes = [];
1402
+ switch (content.type) {
1403
+ case "run":
1404
+ itemNodes.push(...convertRun(content, getInheritedRunFormatting(content.formatting), nextPageBreakRunOwnerId, styleResolver, textBoxAnchors));
1405
+ break;
1406
+ case "hyperlink": {
1407
+ const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
1408
+ itemNodes.push(...convertHyperlink(content, {
1409
+ getInheritedRunFormatting,
1410
+ styleResolver,
1411
+ hyperlinkIndex: currentHyperlinkIndex,
1412
+ textBoxAnchors,
1413
+ nextPageBreakRunOwnerId
1414
+ }));
1415
+ break;
1416
+ }
1417
+ case "simpleField":
1418
+ case "complexField": {
1419
+ const fieldNode = convertField(content, {
1420
+ getInheritedRunFormatting,
1421
+ styleResolver,
1422
+ nextHyperlinkInstanceIndex,
1423
+ nextPageBreakRunOwnerId,
1424
+ textBoxAnchors
1425
+ });
1426
+ if (fieldNode) itemNodes.push(fieldNode);
1427
+ break;
1428
+ }
1429
+ case "inlineSdt": {
1430
+ const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, textBoxAnchors, stack);
1431
+ if (nestedSdt) itemNodes.push(nestedSdt);
1432
+ break;
1433
+ }
1434
+ case "insertion":
1435
+ case "deletion":
1436
+ case "moveTo":
1437
+ case "moveFrom":
1438
+ itemNodes.push(...convertTrackedChange(content, content.type === "insertion" || content.type === "moveTo" ? "insertion" : "deletion", nextHyperlinkInstanceIndex, nextPageBreakRunOwnerId, getInheritedRunFormatting, styleResolver, content.type === "moveTo" || content.type === "moveFrom" ? content.type : null, textBoxAnchors));
1439
+ break;
1440
+ case "mathEquation": {
1441
+ const mathNode = convertMathEquation(content);
1442
+ if (mathNode) itemNodes.push(mathNode);
1443
+ break;
1444
+ }
1445
+ case "preservedInline":
1446
+ itemNodes.push(preservedInlineNode(content));
1447
+ break;
1448
+ default: panic(`Unsupported inline SDT content: ${JSON.stringify(content)}`);
1322
1449
  }
1323
- default: panic(`Unsupported inline SDT content: ${JSON.stringify(content)}`);
1450
+ inlineNodes.push(...withInlineWrapperMark(itemNodes, stack));
1324
1451
  }
1325
1452
  return schema.node("sdt", sdtAttrsFromProperties(props), inlineNodes.length > 0 ? inlineNodes : void 0);
1326
1453
  }
@@ -1331,7 +1458,6 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextPageBreakRunOwner
1331
1458
  * @param resolvedStyleFormatting - Paragraph formatting and its explicit style-toggle state
1332
1459
  */
1333
1460
  function convertRun(run, resolvedStyleFormatting, nextPageBreakRunOwnerId, styleResolver, textBoxAnchors) {
1334
- assertPageBreakSourceRunIsRepresentable(run);
1335
1461
  const nodes = [];
1336
1462
  const { marks, mergedFormatting } = buildRunMarks(run.formatting, resolvedStyleFormatting, styleResolver);
1337
1463
  if (run.propertyChanges && run.propertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...run.propertyChanges] }));
@@ -1342,28 +1468,35 @@ function convertRun(run, resolvedStyleFormatting, nextPageBreakRunOwnerId, style
1342
1468
  }
1343
1469
  return nodes;
1344
1470
  }
1345
- function assertPageBreakSourceRunIsRepresentable(run) {
1346
- if (!run.content.some((content) => content.type === "break" && content.breakType === "page")) return;
1347
- assertRunContentIsRepresentableBesidePageBreak(run, "page-break-bearing-run");
1471
+ const runHasPageBreakContent = (run) => run.content.some((content) => content.type === "break" && content.breakType === "page");
1472
+ /**
1473
+ * Whether a field's result holds an explicit page break.
1474
+ *
1475
+ * `convertField` re-cuts the whole result into a structured field when it
1476
+ * does, so this answers both who gets re-cut and who gets reported. One
1477
+ * predicate for the two, because a reporter that disagreed with the converter
1478
+ * would name a loss nobody suffered.
1479
+ */
1480
+ const fieldResultHasPageBreakContent = (field) => field.type === "simpleField" ? field.content.some((content) => {
1481
+ switch (content.type) {
1482
+ case "run": return runHasPageBreakContent(content);
1483
+ case "hyperlink": return content.children.some((child) => child.type === "run" && runHasPageBreakContent(child));
1484
+ case "preservedInline": return false;
1485
+ default: panic(`Unsupported simple-field content: ${JSON.stringify(content)}`);
1486
+ }
1487
+ }) : field.fieldResult.some(runHasPageBreakContent);
1488
+ function reportPageBreakSourceRunContent(run, warn) {
1489
+ if (!runHasPageBreakContent(run)) return;
1490
+ reportRunContentBesidePageBreak(run, "page-break-bearing-run", warn);
1348
1491
  }
1349
1492
  const PAGE_BREAK_CONTAINER_DESCRIPTIONS = {
1350
- "table-cell": "A table cell containing an explicit page break",
1351
- "text-box": "A text box containing an explicit page break"
1352
- };
1353
- const PAGE_BREAK_PARAGRAPH_OWNERS = {
1354
- borders: "paragraph-borders",
1355
- frame: "paragraph-frame",
1356
- outline: "paragraph-outline",
1357
- textBoxAnchor: "paragraph-text-box-anchor"
1493
+ "table-cell": "An interior page break in a table cell does not paginate the cell; its row moves as a unit",
1494
+ "text-box": "A page break inside a text box does not paginate the text box"
1358
1495
  };
1359
- function assertSourceContainerHasNoPageBreakRun(content, owner, sourceDescendants) {
1496
+ function reportSourceContainerPageBreakRun(content, owner, sourceDescendants, warn) {
1360
1497
  if (!sourceDescendants.containsPageBreakRun(content)) return;
1361
1498
  if (owner === "table-cell" && hasSingleLeadingTableCellPageBreak(content, sourceDescendants)) return;
1362
- throw new UnsupportedDocxToProseMirrorConversionError({
1363
- message: `${PAGE_BREAK_CONTAINER_DESCRIPTIONS[owner]} cannot be represented in the editor model`,
1364
- owner,
1365
- contentType: "break"
1366
- });
1499
+ warn(PAGE_BREAK_CONTAINER_DESCRIPTIONS[owner]);
1367
1500
  }
1368
1501
  /**
1369
1502
  * A page break opening a cell advances the whole row during pagination.
@@ -1404,7 +1537,7 @@ const scanLeadingPageBreakContent = (content, scan) => {
1404
1537
  case "moveFrom":
1405
1538
  case "moveTo":
1406
1539
  case "inlineSdt":
1407
- case "bidiWrapper":
1540
+ case "inlineWrapper":
1408
1541
  for (const child of content.content) scanLeadingPageBreakContent(child, scan);
1409
1542
  return;
1410
1543
  case "simpleField":
@@ -1423,6 +1556,7 @@ const scanLeadingPageBreakContent = (content, scan) => {
1423
1556
  case "moveToRangeEnd": return;
1424
1557
  case "commentReference":
1425
1558
  case "mathEquation":
1559
+ case "preservedInline":
1426
1560
  if (scan.pageBreaks === 0) scan.contentBeforeBreak = true;
1427
1561
  return;
1428
1562
  default: return content;
@@ -1441,64 +1575,103 @@ const hasSingleLeadingParagraphPageBreak = (paragraph) => {
1441
1575
  const scan = scanParagraphPageBreaks(paragraph);
1442
1576
  return !scan.contentBeforeBreak && scan.pageBreaks === 1;
1443
1577
  };
1444
- function assertParagraphPageBreakCanBeProjected({ paragraph, attrs, effectiveFrame, sourceDescendants }) {
1578
+ function reportParagraphPageBreakProjection({ paragraph, attrs, effectiveFrame, sourceDescendants, warn }) {
1445
1579
  const sourceFeatures = sourceDescendants.paragraphFeatures(paragraph);
1446
1580
  if (!sourceFeatures.hasPageBreakRun) return;
1581
+ reportParagraphPageBreakRunContent(paragraph, warn);
1447
1582
  const disposition = pageBreakRunParagraphProjectionDispositionForFeatures({
1448
1583
  attrs,
1449
1584
  effectiveFrame,
1450
1585
  textBoxAnchorAfterPageBreak: sourceFeatures.hasTextBoxShape && !sourceFeatures.pageBreakSharesTextBoxShape && scanParagraphPageBreaks(paragraph).textBoxShapeAfterBreak
1451
1586
  });
1452
- if (disposition.status === "supported") return;
1587
+ if (disposition.status === "exact") return;
1453
1588
  if (disposition.reason !== "textBoxAnchor" && hasSingleLeadingParagraphPageBreak(paragraph)) return;
1454
- throw new UnsupportedDocxToProseMirrorConversionError({
1455
- message: disposition.message,
1456
- owner: PAGE_BREAK_PARAGRAPH_OWNERS[disposition.reason],
1457
- contentType: "break"
1458
- });
1589
+ warn(disposition.message);
1459
1590
  }
1460
- function assertPageBreakFieldResultIsRepresentable(field) {
1461
- if (field.type === "complexField") {
1462
- for (const run of field.fieldResult) assertRunContentIsRepresentableBesidePageBreak(run, "field-result");
1463
- return;
1464
- }
1465
- for (const content of field.content) {
1466
- if (content.type === "run") {
1467
- assertRunContentIsRepresentableBesidePageBreak(content, "field-result");
1468
- continue;
1591
+ /**
1592
+ * Report every run-level loss a paragraph's page breaks bring with them.
1593
+ *
1594
+ * One walk of the paragraph rather than a check at each `convertRun` and
1595
+ * `convertField`: the reporter is a conversion-wide fact, and a per-call-site
1596
+ * check would have to be threaded through every nested converter to reach it.
1597
+ */
1598
+ function reportParagraphPageBreakRunContent(paragraph, warn) {
1599
+ const visitField = (field) => {
1600
+ if (field.type === "complexField") {
1601
+ if (field.fieldCode.some(runHasPageBreakContent)) warn("A complex-field instruction holds an explicit page break");
1602
+ if (!fieldResultHasPageBreakContent(field)) return;
1603
+ for (const run of field.fieldResult) reportRunContentBesidePageBreak(run, "field-result", warn);
1604
+ return;
1469
1605
  }
1470
- for (const child of content.children) if (child.type === "run") assertRunContentIsRepresentableBesidePageBreak(child, "field-result");
1471
- }
1606
+ if (!fieldResultHasPageBreakContent(field)) return;
1607
+ for (const content of field.content) {
1608
+ if (content.type === "run") {
1609
+ reportRunContentBesidePageBreak(content, "field-result", warn);
1610
+ continue;
1611
+ }
1612
+ if (content.type !== "hyperlink") continue;
1613
+ for (const child of content.children) if (child.type === "run") reportRunContentBesidePageBreak(child, "field-result", warn);
1614
+ }
1615
+ };
1616
+ const visitContent = (content) => {
1617
+ switch (content.type) {
1618
+ case "run":
1619
+ reportPageBreakSourceRunContent(content, warn);
1620
+ return;
1621
+ case "hyperlink":
1622
+ for (const child of content.children) if (child.type === "run") reportPageBreakSourceRunContent(child, warn);
1623
+ return;
1624
+ case "simpleField":
1625
+ case "complexField":
1626
+ visitField(content);
1627
+ return;
1628
+ case "insertion":
1629
+ case "deletion":
1630
+ case "moveFrom":
1631
+ case "moveTo":
1632
+ case "inlineSdt":
1633
+ case "inlineWrapper":
1634
+ for (const child of content.content) visitContent(child);
1635
+ return;
1636
+ default: return;
1637
+ }
1638
+ };
1639
+ for (const content of paragraph.content) visitContent(content);
1472
1640
  }
1473
1641
  const PAGE_BREAK_OWNER_DESCRIPTIONS = {
1474
1642
  "field-result": "A field result with an explicit page break",
1475
1643
  "page-break-bearing-run": "A page-break-bearing run"
1476
1644
  };
1477
- const unsupportedPageBreakContent = (owner, contentType) => {
1478
- const description = contentType === "shape" ? "a text-box shape" : contentType;
1479
- throw new UnsupportedDocxToProseMirrorConversionError({
1480
- message: `${PAGE_BREAK_OWNER_DESCRIPTIONS[owner]} containing ${description} cannot be represented in the editor model`,
1481
- owner,
1482
- contentType
1483
- });
1484
- };
1485
- function assertRunContentIsRepresentableBesidePageBreak(run, owner) {
1645
+ /**
1646
+ * Record what a page-break-bearing run loses when the editor re-cuts it.
1647
+ *
1648
+ * The run is split into one node per inline atom and rebuilt from the
1649
+ * `pageBreakRunOwner` mark, and the kinds below have no node of their own:
1650
+ * a hyphen returns as its character, a field character or instruction leaves
1651
+ * no trace, and a text-box shape is hoisted to its own block. Every one of
1652
+ * those losses is what the same run suffers with no page break in it, so
1653
+ * refusing the document here declined to open a file folio otherwise reads.
1654
+ */
1655
+ function reportRunContentBesidePageBreak(run, owner, warn) {
1486
1656
  for (const content of run.content) switch (content.type) {
1487
1657
  case "break":
1488
1658
  case "drawing":
1489
1659
  case "endnoteRef":
1490
1660
  case "footnoteRef":
1661
+ case "preservedXml":
1491
1662
  case "renderedPageBreak":
1492
1663
  case "symbol":
1493
1664
  case "tab":
1494
1665
  case "text": continue;
1495
1666
  case "shape":
1496
- if (content.shape.textBody) unsupportedPageBreakContent(owner, content.type);
1667
+ if (content.shape.textBody) warn(`${PAGE_BREAK_OWNER_DESCRIPTIONS[owner]} also holds a text-box shape`);
1497
1668
  continue;
1498
1669
  case "fieldChar":
1499
1670
  case "instrText":
1500
1671
  case "noBreakHyphen":
1501
- case "softHyphen": return unsupportedPageBreakContent(owner, content.type);
1672
+ case "softHyphen":
1673
+ warn(`${PAGE_BREAK_OWNER_DESCRIPTIONS[owner]} also holds ${content.type}`);
1674
+ continue;
1502
1675
  default: panic(`Unsupported page-break-bearing run content: ${JSON.stringify(content)}`);
1503
1676
  }
1504
1677
  }
@@ -1746,6 +1919,11 @@ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredF
1746
1919
  }
1747
1920
  case "fieldChar":
1748
1921
  case "instrText": return [];
1922
+ case "preservedXml": return [schema.node("preservedXml", {
1923
+ xml: content.xml,
1924
+ text: content.text,
1925
+ level: PRESERVED_XML_LEVELS.run
1926
+ }).mark(marks)];
1749
1927
  case "noBreakHyphen": return [schema.text("‑", marks)];
1750
1928
  case "softHyphen": return [schema.text("­", marks)];
1751
1929
  case "symbol": return [schema.node("symbol", {
@@ -1758,6 +1936,18 @@ function withRunBoundaryMarks(node, marks) {
1758
1936
  if (!marks.some(({ type }) => type.name === "hyperlink" || type.name === "pageBreakRunOwner")) return node;
1759
1937
  return node.mark(marks);
1760
1938
  }
1939
+ /**
1940
+ * The authored-EMU carrier for a drawing, or nothing when the source authored
1941
+ * none of these values and there is accordingly nothing to carry.
1942
+ */
1943
+ const authoredEmuAttrs = (values) => Object.values(values).some((value) => value !== void 0) ? values : void 0;
1944
+ /** The wrap insets of a drawing, keyed by the pixel attribute each becomes. */
1945
+ const wrapDistanceEmu = (wrap) => ({
1946
+ distTop: wrap?.distT,
1947
+ distBottom: wrap?.distB,
1948
+ distLeft: wrap?.distL,
1949
+ distRight: wrap?.distR
1950
+ });
1761
1951
  function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runFormatting }) {
1762
1952
  const imageSize = image.size;
1763
1953
  const widthPx = imageSize?.width ? emuToPixels(imageSize.width) : void 0;
@@ -1781,14 +1971,7 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runForma
1781
1971
  else if (wrapType === "behind" || wrapType === "inFront") displayMode = "float";
1782
1972
  else if (cssFloat !== "none") displayMode = "float";
1783
1973
  else displayMode = "block";
1784
- let transform;
1785
- if (image.transform) {
1786
- const transforms = [];
1787
- if (image.transform.rotation) transforms.push(`rotate(${image.transform.rotation}deg)`);
1788
- if (image.transform.flipH) transforms.push("scaleX(-1)");
1789
- if (image.transform.flipV) transforms.push("scaleY(-1)");
1790
- if (transforms.length > 0) transform = transforms.join(" ");
1791
- }
1974
+ const transformAttrs = authoredTransformAttrs(image.transform);
1792
1975
  const distTop = image.wrap.distT != null ? emuToPixels(image.wrap.distT) : void 0;
1793
1976
  const distBottom = image.wrap.distB != null ? emuToPixels(image.wrap.distB) : void 0;
1794
1977
  const distLeft = image.wrap.distL != null ? emuToPixels(image.wrap.distL) : void 0;
@@ -1813,7 +1996,7 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runForma
1813
1996
  let borderColor;
1814
1997
  let borderStyle;
1815
1998
  if (image.outline && image.outline.width) {
1816
- borderWidth = Math.round(image.outline.width / 914400 * 96 * 100) / 100;
1999
+ borderWidth = emuToStrokePixels(image.outline.width);
1817
2000
  if (image.outline.color?.rgb) borderColor = `#${image.outline.color.rgb}`;
1818
2001
  borderStyle = image.outline.style ? {
1819
2002
  solid: "solid",
@@ -1840,8 +2023,10 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runForma
1840
2023
  wrapType,
1841
2024
  displayMode,
1842
2025
  cssFloat,
1843
- transform,
2026
+ ...transformAttrs,
1844
2027
  opacity: image.opacity,
2028
+ brightness: image.effects?.brightness,
2029
+ contrast: image.effects?.contrast,
1845
2030
  distTop,
1846
2031
  distBottom,
1847
2032
  distLeft,
@@ -1856,8 +2041,17 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runForma
1856
2041
  paddingLeft: image.padding?.left,
1857
2042
  position,
1858
2043
  layoutInCell: image.layoutInCell,
2044
+ decorative: image.decorative,
2045
+ hidden: image.hidden,
2046
+ docPrExtensions: image.docPrExtensions ? [...image.docPrExtensions] : void 0,
1859
2047
  frameLocks: image.frameLocks ? { ...image.frameLocks } : void 0,
1860
2048
  borderWidth,
2049
+ _docxAuthoredEmu: authoredEmuAttrs({
2050
+ width: imageSize?.width,
2051
+ height: imageSize?.height,
2052
+ borderWidth: image.outline?.width,
2053
+ ...wrapDistanceEmu(image.wrap)
2054
+ }),
1861
2055
  borderColor,
1862
2056
  borderStyle,
1863
2057
  wrapText,
@@ -1899,8 +2093,11 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
1899
2093
  }, void 0, [linkMark]));
1900
2094
  continue;
1901
2095
  }
2096
+ if (child.type === "preservedInline") {
2097
+ nodes.push(preservedInlineNode(child).mark([linkMark]));
2098
+ continue;
2099
+ }
1902
2100
  if (child.type === "run") {
1903
- assertPageBreakSourceRunIsRepresentable(child);
1904
2101
  const inheritedFormatting = getInheritedRunFormatting(child.formatting);
1905
2102
  const { marks: runMarks, mergedFormatting } = buildRunMarks(child.formatting, inheritedFormatting, styleResolver);
1906
2103
  if (child.propertyChanges && child.propertyChanges.length > 0) runMarks.push(schema.mark("runPropertyChange", { changes: [...child.propertyChanges] }));
@@ -1946,27 +2143,22 @@ function convertShape(shape, runFormatting) {
1946
2143
  let outlineColorValue;
1947
2144
  let outlineStyle = "none";
1948
2145
  let outlineCap;
2146
+ let outlineJoin;
1949
2147
  let outlineHeadEnd;
1950
2148
  let outlineTailEnd;
1951
2149
  if (shape.outline) {
1952
- if (shape.outline.width) outlineWidth = Math.round(shape.outline.width / 914400 * 96 * 100) / 100;
2150
+ if (shape.outline.width) outlineWidth = emuToStrokePixels(shape.outline.width);
1953
2151
  if (shape.outline.color) {
1954
2152
  outlineColorValue = shape.outline.color;
1955
2153
  outlineColor = resolveColorValueToHex(shape.outline.color);
1956
2154
  }
1957
- outlineStyle = shape.outline.style || "solid";
2155
+ outlineStyle = shape.outline.style;
1958
2156
  outlineCap = shape.outline.cap;
2157
+ outlineJoin = shape.outline.join;
1959
2158
  outlineHeadEnd = shape.outline.headEnd;
1960
2159
  outlineTailEnd = shape.outline.tailEnd;
1961
2160
  } else outlineWidth = 0;
1962
- let transform;
1963
- if (shape.transform) {
1964
- const transforms = [];
1965
- if (shape.transform.rotation) transforms.push(`rotate(${shape.transform.rotation}deg)`);
1966
- if (shape.transform.flipH) transforms.push("scaleX(-1)");
1967
- if (shape.transform.flipV) transforms.push("scaleY(-1)");
1968
- if (transforms.length > 0) transform = transforms.join(" ");
1969
- }
2161
+ const transformAttrs = authoredTransformAttrs(shape.transform);
1970
2162
  const wrapType = shape.wrap?.type ?? "inline";
1971
2163
  const displayMode = wrapType === "inline" ? "inline" : "float";
1972
2164
  let cssFloat = "none";
@@ -1990,8 +2182,17 @@ function convertShape(shape, runFormatting) {
1990
2182
  shapeType: shapeAttrs.shapeType ?? "rect",
1991
2183
  geometryAdjustments: shape.geometryAdjustments === void 0 ? void 0 : JSON.stringify(shape.geometryAdjustments),
1992
2184
  shapeId: shape.id,
2185
+ shapeName: shape.name,
2186
+ alt: shape.alt,
2187
+ title: shape.title,
1993
2188
  width: widthPx,
1994
2189
  height: heightPx,
2190
+ _docxAuthoredEmu: authoredEmuAttrs({
2191
+ width: shapeSize?.width,
2192
+ height: shapeSize?.height,
2193
+ outlineWidth: shape.outline?.width,
2194
+ ...wrapDistanceEmu(shape.wrap)
2195
+ }),
1995
2196
  fillColor,
1996
2197
  fillColorValue,
1997
2198
  fillType,
@@ -2003,9 +2204,10 @@ function convertShape(shape, runFormatting) {
2003
2204
  outlineColorValue,
2004
2205
  outlineStyle,
2005
2206
  outlineCap,
2207
+ outlineJoin,
2006
2208
  outlineHeadEnd,
2007
2209
  outlineTailEnd,
2008
- transform,
2210
+ ...transformAttrs,
2009
2211
  displayMode,
2010
2212
  cssFloat,
2011
2213
  wrapType,
@@ -2019,18 +2221,20 @@ function convertShape(shape, runFormatting) {
2019
2221
  }
2020
2222
  function convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId, context, extraRunFormatting, preserveEmptyWrapper, tableParagraphOverlay }) {
2021
2223
  const { textBoxes, textBoxAnchors } = extractTextBoxesFromParagraph(block, textBoxGroupId);
2022
- const pmParagraph = convertParagraph(block, styleResolver, context, void 0, extraRunFormatting, tableParagraphOverlay, textBoxAnchors);
2224
+ const pmParagraph = convertParagraph(block, styleResolver, context, extraRunFormatting, tableParagraphOverlay, textBoxAnchors);
2023
2225
  const nodes = [];
2024
2226
  const isEmptyAfterExtraction = textBoxes.length > 0 && !hasContentBesidesTextBoxAnchors(pmParagraph);
2025
2227
  const keepWrapperParagraph = isEmptyAfterExtraction && (preserveEmptyWrapper === true || hasParagraphBoundaryPayload(block, pmParagraph));
2026
2228
  if (!isEmptyAfterExtraction || keepWrapperParagraph) nodes.push(pmParagraph);
2027
- for (const { textBox, anchorId, trackedChange, inlineSdts } of textBoxes) nodes.push(convertTextBox(textBox, styleResolver, {
2028
- placement: isEmptyAfterExtraction && !keepWrapperParagraph ? "standalone" : "inlineWithPrevious",
2229
+ const standalone = isEmptyAfterExtraction && !keepWrapperParagraph;
2230
+ for (const [index, { textBox, anchorId, trackedChange, inlineSdts }] of textBoxes.entries()) nodes.push(convertTextBox(textBox, styleResolver, {
2231
+ placement: standalone ? "standalone" : "inlineWithPrevious",
2029
2232
  groupId: textBoxGroupId,
2030
2233
  anchorId,
2031
2234
  context,
2032
2235
  trackedChange,
2033
- inlineSdts
2236
+ inlineSdts,
2237
+ ...standalone && index === 0 && block.preservedAttributes ? { hostPreservedAttributes: block.preservedAttributes } : {}
2034
2238
  }));
2035
2239
  return nodes;
2036
2240
  }
@@ -2158,6 +2362,9 @@ function textBoxFromShape(shape, textBody) {
2158
2362
  content: textBody.content
2159
2363
  };
2160
2364
  if (shape.id) textBox.id = shape.id;
2365
+ if (shape.name !== void 0) textBox.name = shape.name;
2366
+ if (shape.alt !== void 0) textBox.alt = shape.alt;
2367
+ if (shape.title !== void 0) textBox.title = shape.title;
2161
2368
  if (shape.position) textBox.position = shape.position;
2162
2369
  if (shape.wrap) textBox.wrap = shape.wrap;
2163
2370
  if (shape.fill) textBox.fill = shape.fill;
@@ -2174,7 +2381,7 @@ function textBoxFromShape(shape, textBody) {
2174
2381
  * Convert a TextBox to a ProseMirror textBox node
2175
2382
  */
2176
2383
  function convertTextBox(textBox, styleResolver, options) {
2177
- assertSourceContainerHasNoPageBreakRun(textBox.content, "text-box", options.context.pageBreakRunSourceDescendants);
2384
+ reportSourceContainerPageBreakRun(textBox.content, "text-box", options.context.pageBreakRunSourceDescendants, options.context.warnPageBreakProjection);
2178
2385
  const textBoxSize = textBox.size;
2179
2386
  const widthPx = textBoxSize?.width ? emuToPixels(textBoxSize.width) : 200;
2180
2387
  const heightPx = textBoxSize?.height ? emuToPixels(textBoxSize.height) : void 0;
@@ -2184,32 +2391,29 @@ function convertTextBox(textBox, styleResolver, options) {
2184
2391
  let outlineColor;
2185
2392
  let outlineStyle;
2186
2393
  if (textBox.outline && textBox.outline.width) {
2187
- outlineWidth = Math.round(textBox.outline.width / 914400 * 96 * 100) / 100;
2394
+ outlineWidth = emuToStrokePixels(textBox.outline.width);
2188
2395
  if (textBox.outline.color?.rgb) outlineColor = `#${textBox.outline.color.rgb}`;
2189
2396
  outlineStyle = textBox.outline.style || "solid";
2190
2397
  }
2191
- let transform;
2192
- if (textBox.transform) {
2193
- const transforms = [];
2194
- if (textBox.transform.rotation) transforms.push(`rotate(${textBox.transform.rotation}deg)`);
2195
- if (textBox.transform.flipH) transforms.push("scaleX(-1)");
2196
- if (textBox.transform.flipV) transforms.push("scaleY(-1)");
2197
- if (transforms.length > 0) transform = transforms.join(" ");
2198
- }
2199
- const marginTop = textBox.margins?.top !== void 0 ? emuToPixels(textBox.margins.top) : 4;
2200
- const marginBottom = textBox.margins?.bottom !== void 0 ? emuToPixels(textBox.margins.bottom) : 4;
2201
- const marginLeft = textBox.margins?.left !== void 0 ? emuToPixels(textBox.margins.left) : 7;
2202
- const marginRight = textBox.margins?.right !== void 0 ? emuToPixels(textBox.margins.right) : 7;
2398
+ const transformAttrs = authoredTransformAttrs(textBox.transform);
2399
+ const marginTop = textBox.margins?.top !== void 0 ? emuToPixels(textBox.margins.top) : void 0;
2400
+ const marginBottom = textBox.margins?.bottom !== void 0 ? emuToPixels(textBox.margins.bottom) : void 0;
2401
+ const marginLeft = textBox.margins?.left !== void 0 ? emuToPixels(textBox.margins.left) : void 0;
2402
+ const marginRight = textBox.margins?.right !== void 0 ? emuToPixels(textBox.margins.right) : void 0;
2403
+ const textBoxContext = {
2404
+ ...options.context,
2405
+ openCommentIds: /* @__PURE__ */ new Set()
2406
+ };
2203
2407
  const contentNodes = [];
2204
2408
  for (const block of textBox.content) {
2205
2409
  if (block.type === "paragraph") {
2206
2410
  contentNodes.push(...convertParagraphWithTextBoxes(block, styleResolver, {
2207
- textBoxGroupId: options.context.nextTextBoxGroupId(),
2208
- context: options.context
2411
+ textBoxGroupId: textBoxContext.nextTextBoxGroupId(),
2412
+ context: textBoxContext
2209
2413
  }));
2210
2414
  continue;
2211
2415
  }
2212
- contentNodes.push(convertTable(block, styleResolver, options.context));
2416
+ contentNodes.push(convertTable(block, styleResolver, textBoxContext));
2213
2417
  }
2214
2418
  if (contentNodes.length === 0) contentNodes.push(schema.node("paragraph", {}, []));
2215
2419
  const wrapType = textBox.wrap?.type;
@@ -2243,23 +2447,36 @@ function convertTextBox(textBox, styleResolver, options) {
2243
2447
  else if (wrapType === "behind" || wrapType === "inFront") displayMode = "float";
2244
2448
  else if (cssFloat !== "none") displayMode = "float";
2245
2449
  else displayMode = "block";
2246
- const distTop = textBox.wrap?.distT ? emuToPixels(textBox.wrap.distT) : void 0;
2247
- const distBottom = textBox.wrap?.distB ? emuToPixels(textBox.wrap.distB) : void 0;
2248
- const distLeft = textBox.wrap?.distL ? emuToPixels(textBox.wrap.distL) : void 0;
2249
- const distRight = textBox.wrap?.distR ? emuToPixels(textBox.wrap.distR) : void 0;
2450
+ const distTop = textBox.wrap?.distT !== void 0 ? emuToPixels(textBox.wrap.distT) : void 0;
2451
+ const distBottom = textBox.wrap?.distB !== void 0 ? emuToPixels(textBox.wrap.distB) : void 0;
2452
+ const distLeft = textBox.wrap?.distL !== void 0 ? emuToPixels(textBox.wrap.distL) : void 0;
2453
+ const distRight = textBox.wrap?.distR !== void 0 ? emuToPixels(textBox.wrap.distR) : void 0;
2250
2454
  return schema.node("textBox", {
2251
2455
  width: widthPx,
2252
2456
  height: heightPx,
2457
+ _docxAuthoredEmu: authoredEmuAttrs({
2458
+ width: textBoxSize?.width,
2459
+ height: textBoxSize?.height,
2460
+ outlineWidth: textBox.outline?.width,
2461
+ marginTop: textBox.margins?.top,
2462
+ marginBottom: textBox.margins?.bottom,
2463
+ marginLeft: textBox.margins?.left,
2464
+ marginRight: textBox.margins?.right,
2465
+ ...wrapDistanceEmu(textBox.wrap)
2466
+ }),
2253
2467
  autoFit: textBox.autoFit,
2254
2468
  wordArt: textBox.wordArt,
2255
2469
  textWrap: textBox.textWrap,
2256
2470
  verticalAlign: textBox.verticalAlign,
2257
2471
  textBoxId: textBox.id,
2472
+ textBoxName: textBox.name,
2473
+ alt: textBox.alt,
2474
+ title: textBox.title,
2258
2475
  fillColor,
2259
2476
  outlineWidth,
2260
2477
  outlineColor,
2261
2478
  outlineStyle,
2262
- transform,
2479
+ ...transformAttrs,
2263
2480
  marginTop,
2264
2481
  marginBottom,
2265
2482
  marginLeft,
@@ -2278,7 +2495,8 @@ function convertTextBox(textBox, styleResolver, options) {
2278
2495
  _docxAnchorId: options.anchorId,
2279
2496
  _docxTextBodyContentState: textBox.content.length === 0 ? { type: "source-empty" } : { type: "authored" },
2280
2497
  _docxTrackedChange: options.trackedChange,
2281
- _docxInlineSdts: options.inlineSdts.length > 0 ? options.inlineSdts : void 0
2498
+ _docxInlineSdts: options.inlineSdts.length > 0 ? options.inlineSdts : void 0,
2499
+ _preservedAttributes: options.hostPreservedAttributes
2282
2500
  }, contentNodes);
2283
2501
  }
2284
2502
  /**
@@ -2299,7 +2517,10 @@ function headerFooterToProseDoc(content, options) {
2299
2517
  nextTextBoxGroupId,
2300
2518
  nextHyperlinkInstanceIndex: createHyperlinkInstanceIndexAllocator(),
2301
2519
  pairedBookmarkIds: collectPairedBookmarkIds(content),
2302
- pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(content)
2520
+ pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(content),
2521
+ storyRangedCommentIds: rangedCommentIds(content),
2522
+ openCommentIds: /* @__PURE__ */ new Set(),
2523
+ warnPageBreakProjection: pageBreakProjectionWarn(options?.warn)
2303
2524
  };
2304
2525
  const convertBlocks = (blocks) => {
2305
2526
  const out = [];
@@ -2328,6 +2549,9 @@ function headerFooterToProseDoc(content, options) {
2328
2549
  case "blockSdt":
2329
2550
  out.push(convertBlockSdt(block, convertBlocks));
2330
2551
  break;
2552
+ case "preservedBlock":
2553
+ out.push(convertPreservedBlock(block));
2554
+ break;
2331
2555
  default: panic(`Unsupported block content: ${JSON.stringify(block)}`);
2332
2556
  }
2333
2557
  return out;
@@ -2348,4 +2572,4 @@ function createEmptyDoc() {
2348
2572
  return schema.node("doc", null, [schema.node("paragraph", {}, [])]);
2349
2573
  }
2350
2574
  //#endregion
2351
- export { UnsupportedDocxToProseMirrorConversionError, createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, resolveParagraphDefaultTextFormatting, standaloneTableCellToProseMirror, toProseDoc };
2575
+ export { createEmptyDoc, footnoteToProseDoc, headerFooterToProseDoc, resolveParagraphDefaultTextFormatting, standaloneTableCellToProseMirror, toProseDoc };