@stll/folio-core 0.45.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 (207) hide show
  1. package/dist/ai-edits/apply.js +59 -19
  2. package/dist/ai-edits/headless.js +1 -1
  3. package/dist/compare/compare.js +2 -5
  4. package/dist/compare/scenario.js +2 -5
  5. package/dist/compare/types.d.ts +9 -1
  6. package/dist/compare/types.js +12 -1
  7. package/dist/compat/eigenpal.d.ts +2 -1
  8. package/dist/compat/eigenpal.js +2 -1
  9. package/dist/content-controls/checkboxDisplay.d.ts +11 -0
  10. package/dist/content-controls/checkboxDisplay.js +40 -0
  11. package/dist/content-controls/findContentControls.js +3 -1
  12. package/dist/content-controls/mutateContentControls.js +17 -2
  13. package/dist/controller/contentControlWidgetController.d.ts +1 -0
  14. package/dist/controller/contentControlWidgetController.js +3 -0
  15. package/dist/controller/hiddenEditorApi.js +3 -1
  16. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  17. package/dist/controller/hiddenEditorManager.js +6 -3
  18. package/dist/display-list/build/buildDisplayList.js +20 -1
  19. package/dist/display-list/build/imagePrimitives.d.ts +1 -1
  20. package/dist/display-list/build/imagePrimitives.js +3 -1
  21. package/dist/display-list/build/paragraphPrimitives.js +21 -2
  22. package/dist/display-list/dom/renderDisplayListToDom.js +10 -2
  23. package/dist/display-list/types.d.ts +17 -0
  24. package/dist/docx/attributeRemainder.d.ts +43 -0
  25. package/dist/docx/attributeRemainder.js +65 -0
  26. package/dist/docx/blockContentParser.d.ts +1 -1
  27. package/dist/docx/blockContentParser.js +87 -63
  28. package/dist/docx/blockPlainText.d.ts +3 -4
  29. package/dist/docx/blockPlainText.js +1 -0
  30. package/dist/docx/bookmarkPlacement.js +2 -0
  31. package/dist/docx/commentAnchorIndex.d.ts +30 -0
  32. package/dist/docx/commentAnchorIndex.js +50 -0
  33. package/dist/docx/commentParser.d.ts +1 -1
  34. package/dist/docx/commentParser.js +54 -37
  35. package/dist/docx/commentRangeIntegrity.d.ts +16 -1
  36. package/dist/docx/commentRangeIntegrity.js +42 -1
  37. package/dist/docx/commentRangeJoin.d.ts +9 -0
  38. package/dist/docx/commentRangeJoin.js +33 -0
  39. package/dist/docx/commentReferenceCompletion.d.ts +9 -0
  40. package/dist/docx/commentReferenceCompletion.js +47 -0
  41. package/dist/docx/commentReferenceNormalization.js +1 -0
  42. package/dist/docx/commentReplyMarkers.js +11 -19
  43. package/dist/docx/compatibility.js +1 -0
  44. package/dist/docx/containerChildren.d.ts +111 -0
  45. package/dist/docx/containerChildren.gen.d.ts +28 -0
  46. package/dist/docx/containerChildren.gen.js +246 -0
  47. package/dist/docx/containerChildren.js +103 -0
  48. package/dist/docx/documentParser.d.ts +1 -1
  49. package/dist/docx/documentParser.js +3 -3
  50. package/dist/docx/ensureParaIds.js +17 -7
  51. package/dist/docx/fieldParser.d.ts +1 -1
  52. package/dist/docx/fieldParser.js +4 -4
  53. package/dist/docx/fieldState.d.ts +26 -0
  54. package/dist/docx/fieldState.js +53 -0
  55. package/dist/docx/footnoteParser.d.ts +1 -1
  56. package/dist/docx/graphicFrameLocks.d.ts +16 -4
  57. package/dist/docx/graphicFrameLocks.js +19 -6
  58. package/dist/docx/headerFooterParser.js +1 -1
  59. package/dist/docx/headerFooterReferenceNormalization.js +1 -0
  60. package/dist/docx/hyperlinkParser.d.ts +27 -8
  61. package/dist/docx/hyperlinkParser.js +76 -18
  62. package/dist/docx/imageParser.js +33 -8
  63. package/dist/docx/inlineWrapperContent.d.ts +85 -0
  64. package/dist/docx/inlineWrapperContent.js +84 -0
  65. package/dist/docx/normalizeBaseDirection.js +10 -1
  66. package/dist/docx/paraIdAttribute.d.ts +21 -0
  67. package/dist/docx/paraIdAttribute.js +64 -0
  68. package/dist/docx/paragraphParser.d.ts +1 -1
  69. package/dist/docx/paragraphParser.js +277 -168
  70. package/dist/docx/paragraphPropertySource.js +5 -1
  71. package/dist/docx/paragraphTextBoxEnrichment.d.ts +1 -1
  72. package/dist/docx/paragraphTextBoxEnrichment.js +6 -52
  73. package/dist/docx/paragraphTraversal.js +7 -4
  74. package/dist/docx/parseWarningMessage.js +2 -1
  75. package/dist/docx/preservedRunContent.d.ts +32 -0
  76. package/dist/docx/preservedRunContent.js +86 -0
  77. package/dist/docx/renderedPageBreakNormalization.js +2 -4
  78. package/dist/docx/rezip.js +74 -18
  79. package/dist/docx/runParser.d.ts +1 -1
  80. package/dist/docx/runParser.js +16 -12
  81. package/dist/docx/sectionParser.js +8 -1
  82. package/dist/docx/selectiveXmlPatch.d.ts +46 -2
  83. package/dist/docx/selectiveXmlPatch.js +86 -39
  84. package/dist/docx/serializer/borderSerializer.js +2 -2
  85. package/dist/docx/serializer/commentSerializer.js +5 -7
  86. package/dist/docx/serializer/documentSerializer.js +8 -11
  87. package/dist/docx/serializer/headerFooterSerializer.js +7 -9
  88. package/dist/docx/serializer/noteSerializer.js +8 -8
  89. package/dist/docx/serializer/paragraphSerializer.js +76 -56
  90. package/dist/docx/serializer/runSerializer.js +25 -16
  91. package/dist/docx/serializer/sectionPropertiesSerializer.js +8 -4
  92. package/dist/docx/serializer/tableSerializer.js +18 -8
  93. package/dist/docx/server/createBilingualDocument.js +3 -1
  94. package/dist/docx/server/materializeYjsDocx.d.ts +1 -1
  95. package/dist/docx/server/materializeYjsDocx.js +9 -1
  96. package/dist/docx/server/migrateYjsAttrSchema.d.ts +55 -0
  97. package/dist/docx/server/migrateYjsAttrSchema.js +95 -0
  98. package/dist/docx/shapeParser.js +5 -5
  99. package/dist/docx/tableParser.d.ts +1 -1
  100. package/dist/docx/tableParser.js +206 -85
  101. package/dist/docx/textBoxParser.d.ts +25 -2
  102. package/dist/docx/textBoxParser.js +52 -2
  103. package/dist/docx/unzip.js +5 -4
  104. package/dist/docx/vmlImageParser.d.ts +17 -1
  105. package/dist/docx/vmlImageParser.js +17 -1
  106. package/dist/docx/xmlEncoding.d.ts +5 -0
  107. package/dist/docx/xmlEncoding.js +12 -0
  108. package/dist/docx/xmlParser.d.ts +10 -1
  109. package/dist/docx/xmlParser.js +14 -1
  110. package/dist/headless-layout.js +10 -1
  111. package/dist/index.d.ts +2 -1
  112. package/dist/index.js +2 -1
  113. package/dist/internal/pageBreakRunSourceDescendantIndex.js +5 -2
  114. package/dist/layout-bridge/convert/toFlowBlocks.js +102 -12
  115. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  116. package/dist/layout-engine/measure/tableCellFloating.js +2 -0
  117. package/dist/layout-engine/types.d.ts +23 -1
  118. package/dist/layout-painter/renderImage.d.ts +5 -1
  119. package/dist/layout-painter/renderImage.js +10 -3
  120. package/dist/layout-painter/renderPage.d.ts +2 -0
  121. package/dist/layout-painter/renderPage.js +4 -0
  122. package/dist/layout-painter/renderParagraph.d.ts +13 -1
  123. package/dist/layout-painter/renderParagraph.js +22 -3
  124. package/dist/managers/autoSaveCodec.d.ts +9 -1
  125. package/dist/managers/autoSaveCodec.js +12 -4
  126. package/dist/markdown/renderBlock.js +1 -0
  127. package/dist/markdown/renderRuns.d.ts +1 -1
  128. package/dist/markdown/renderRuns.js +28 -5
  129. package/dist/markdown/renderTable.js +22 -5
  130. package/dist/markdown/trailers.js +4 -1
  131. package/dist/pdf/images.js +142 -6
  132. package/dist/prosemirror/attrs/index.d.ts +14 -3
  133. package/dist/prosemirror/attrs/index.js +155 -2
  134. package/dist/prosemirror/authoredTransformAttrs.d.ts +28 -0
  135. package/dist/prosemirror/authoredTransformAttrs.js +64 -0
  136. package/dist/prosemirror/commands/contentControls.js +10 -2
  137. package/dist/prosemirror/commands/image.d.ts +9 -2
  138. package/dist/prosemirror/commands/image.js +42 -28
  139. package/dist/prosemirror/commands/pageBreak.js +1 -1
  140. package/dist/prosemirror/commentReferenceAttrs.d.ts +8 -0
  141. package/dist/prosemirror/commentReferenceAttrs.js +33 -0
  142. package/dist/prosemirror/commentReferenceIntegrity.d.ts +47 -0
  143. package/dist/prosemirror/commentReferenceIntegrity.js +43 -0
  144. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  145. package/dist/prosemirror/conversion/fromProseDoc.js +269 -70
  146. package/dist/prosemirror/conversion/index.d.ts +2 -2
  147. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -9
  148. package/dist/prosemirror/conversion/toProseDoc.js +468 -308
  149. package/dist/prosemirror/extensions/StarterKit.js +8 -58
  150. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  151. package/dist/prosemirror/extensions/core/ParagraphExtension.js +2 -1
  152. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +45 -27
  153. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -1
  154. package/dist/prosemirror/extensions/features/ImagePasteExtension.js +5 -3
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -0
  156. package/dist/prosemirror/extensions/markRegistry.d.ts +41 -0
  157. package/dist/prosemirror/extensions/markRegistry.js +75 -0
  158. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.d.ts +18 -0
  159. package/dist/prosemirror/extensions/marks/InlineWrapperExtension.js +63 -0
  160. package/dist/prosemirror/extensions/marks/markUtils.d.ts +2 -1
  161. package/dist/prosemirror/extensions/marks/markUtils.js +47 -4
  162. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.d.ts +11 -0
  163. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +87 -0
  164. package/dist/prosemirror/extensions/nodes/FieldExtension.js +9 -7
  165. package/dist/prosemirror/extensions/nodes/ImageExtension.js +16 -0
  166. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.d.ts +32 -0
  167. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +67 -0
  168. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.d.ts +16 -0
  169. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +60 -0
  170. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +3 -0
  171. package/dist/prosemirror/extensions/nodes/TableExtension.js +3 -2
  172. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +5 -1
  173. package/dist/prosemirror/inlineWrapperStack.d.ts +39 -0
  174. package/dist/prosemirror/inlineWrapperStack.js +75 -0
  175. package/dist/prosemirror/pageBreakRunProjection.d.ts +17 -7
  176. package/dist/prosemirror/pageBreakRunProjection.js +19 -9
  177. package/dist/prosemirror/replacedAnnotations.d.ts +59 -0
  178. package/dist/prosemirror/replacedAnnotations.js +165 -0
  179. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +3 -1
  180. package/dist/prosemirror/runFormattingInlineCarriers.js +5 -1
  181. package/dist/prosemirror/schema/index.d.ts +3 -3
  182. package/dist/prosemirror/schema/marks.d.ts +25 -1
  183. package/dist/prosemirror/schema/nodes.d.ts +90 -3
  184. package/dist/prosemirror/schema/nodes.js +16 -1
  185. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +2 -0
  186. package/dist/prosemirror/trackedRunInlineAtoms.js +2 -0
  187. package/dist/prosemirror/validation.js +16 -2
  188. package/dist/prosemirror/yjsDocumentMetadata.d.ts +73 -0
  189. package/dist/prosemirror/yjsDocumentMetadata.js +171 -0
  190. package/dist/prosemirror/zeroWidthAnchors.js +2 -0
  191. package/dist/render-dom/commentAnchorAttributes.d.ts +37 -0
  192. package/dist/render-dom/commentAnchorAttributes.js +54 -0
  193. package/dist/server.d.ts +3 -1
  194. package/dist/server.js +3 -1
  195. package/dist/types/content.d.ts +2 -2
  196. package/dist/utils/clipboard.d.ts +9 -1
  197. package/dist/utils/clipboard.js +27 -1
  198. package/dist/utils/findReplace.js +1 -1
  199. package/dist/utils/imageLuminance.d.ts +15 -0
  200. package/dist/utils/imageLuminance.js +32 -0
  201. package/dist/utils/mergeDocumentContent.js +7 -17
  202. package/dist/utils/replaceText.js +1 -0
  203. package/package.json +2 -2
  204. package/dist/docx/blockRangeMarkers.d.ts +0 -36
  205. package/dist/docx/blockRangeMarkers.js +0 -59
  206. package/dist/prosemirror/yjsParagraphSourceContract.d.ts +0 -9
  207. package/dist/prosemirror/yjsParagraphSourceContract.js +0 -26
@@ -1,22 +1,30 @@
1
1
  import { isValidHexId } from "../utils/hexId.js";
2
+ import { attributeRemainder } from "./attributeRemainder.js";
2
3
  import { parseBookmarkEnd as parseBookmarkEnd$1, parseBookmarkStart as parseBookmarkStart$1 } from "./bookmarkParser.js";
3
4
  import { parseBorderSpec } from "./borderParser.js";
5
+ import { CAPTURE, DROPPED_WITH_ITS_WRAPPER, OWNED_ELSEWHERE, dispatchChildren, transitionalNamespaceOf, withPreservedChildren } from "./containerChildren.js";
4
6
  import { parseFieldType } from "./fieldParser.js";
5
- import { parseHyperlink as parseHyperlink$1, parseHyperlinkChild } from "./hyperlinkParser.js";
7
+ import { fieldStateOf, parseFieldState } from "./fieldState.js";
8
+ import { hyperlinkChildHandlers, parseHyperlink as parseHyperlink$1 } from "./hyperlinkParser.js";
9
+ import { isInlineSdtContent, isTrackedChangeWrapperChild } from "./inlineWrapperContent.js";
6
10
  import { parseMarkupRangeMarker, parseMoveBookmarkMarker } from "./markupRangeMarker.js";
7
11
  import { markerFormattingFromLevel, numberingLevelHasMarkerSlot } from "./numberingParser.js";
8
12
  import { isNumberingReference } from "./numberingReference.js";
13
+ import { paraIdAttribute, textIdAttribute } from "./paraIdAttribute.js";
9
14
  import { paraIdInRange } from "./paraIdRangeNormalization.js";
10
15
  import { assignParagraphPropertySource } from "./paragraphPropertySource.js";
11
16
  import { FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, TabLeaderSchema, TabStopAlignmentSchema, narrowEnum } from "./parserEnums.js";
17
+ import { preserveInlineChild, preservedInlineCapture } from "./preservedRunContent.js";
12
18
  import { consolidateParagraphContent } from "./runConsolidator.js";
13
19
  import { parseRun, parseRunProperties } from "./runParser.js";
14
20
  import { parseSdtProperties } from "./sdtProperties.js";
15
21
  import { parseSectionProperties } from "./sectionParser.js";
16
22
  import { parseShading } from "./shadingParser.js";
23
+ import { scanRunForTextBoxDrawings } from "./textBoxParser.js";
17
24
  import { parsePropertyChangeInfo, parseTrackedChangeInfo } from "./trackedChangeInfo.js";
18
25
  import { captureVerbatimXml } from "./verbatimCapture.js";
19
- import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildByNamespaceUri, findChildren, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, selectAlternateContentBranch } from "./xmlParser.js";
26
+ import { isVmlPictParsedByRunParser } from "./vmlImageParser.js";
27
+ import { WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, findChild, findChildByNamespaceUri, findChildren, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffAttribute, selectAlternateContentBranch } from "./xmlParser.js";
20
28
  import { panic } from "better-result";
21
29
  import { BIDI_CONTROLS, PARAGRAPH_MARK_CHANGE_KINDS, REVIEW_CARRIERS } from "@stll/docx-core/model";
22
30
  //#region src/docx/paragraphParser.ts
@@ -480,12 +488,6 @@ function parseParagraphMarkChange(pPr) {
480
488
  };
481
489
  }
482
490
  }
483
- function isTrackedChangeWrapperChild(content) {
484
- return content.type === "run" || content.type === "hyperlink" || content.type === "bookmarkStart" || content.type === "bookmarkEnd" || content.type === "simpleField" || content.type === "complexField" || content.type === "mathEquation" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo";
485
- }
486
- function isInlineSdtContent(content) {
487
- return content.type === "run" || content.type === "hyperlink" || content.type === "simpleField" || content.type === "complexField" || content.type === "inlineSdt" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo" || content.type === "mathEquation";
488
- }
489
491
  function pushTrackedChangeWrapper({ contents, type, info, content, preserveEmpty = false }) {
490
492
  if (content.length === 0 && !preserveEmpty) return;
491
493
  if (type === "insertion") {
@@ -590,38 +592,43 @@ function pushInlineSdtSegments({ contents, properties, parsedContent }) {
590
592
  function parseHyperlink(node, rels, styles, theme, media, rootXmlns = {}) {
591
593
  return parseHyperlink$1(node, rels, styles, theme, media, rootXmlns);
592
594
  }
593
- /** The revision wrapper a `w:hyperlink` child is, when it is one. */
594
- const hyperlinkRevisionWrapperType = (node) => {
595
- switch (getLocalName(node.name)) {
596
- case "ins": return "insertion";
597
- case "del": return "deletion";
598
- case "moveFrom": return "moveFrom";
599
- case "moveTo": return "moveTo";
600
- default: return;
601
- }
602
- };
595
+ /**
596
+ * The four `CT_RunTrackChange` wrappers a `w:hyperlink` may hold.
597
+ *
598
+ * What to *do* with each is the dispatcher's handler map; this set only
599
+ * answers whether the link needs segmenting at all, which decides between one
600
+ * link and a sequence of links and revisions.
601
+ */
602
+ const HYPERLINK_REVISION_WRAPPERS = /* @__PURE__ */ new Set([
603
+ "del",
604
+ "ins",
605
+ "moveFrom",
606
+ "moveTo"
607
+ ]);
603
608
  const OMML_NAMESPACE = "http://schemas.openxmlformats.org/officeDocument/2006/math";
604
609
  /**
605
- * A bare OMML element as an inline equation, or nothing when the child is not one.
610
+ * An OMML element as an equation, or nothing when the child is not one.
606
611
  *
607
- * `m:oMath` and `m:oMathPara` have branches of their own. This is for the rest
608
- * of `m:EG_OMathMathElements` — `m:f`, `m:acc`, `m:rad` and their siblings —
609
- * which the schema admits wherever `m:oMath` is admitted. They carry no
610
- * structure the editable model holds, so they travel as the markup they
611
- * arrived as, exactly like the equations that do have a wrapper.
612
+ * `m:oMathPara` is the display form and `m:oMath` the inline one. The rest of
613
+ * `m:EG_OMathMathElements` — `m:f`, `m:acc`, `m:rad` and their siblings — the
614
+ * schema admits wherever `m:oMath` is admitted, so `<w:ins><m:f/></w:ins>` is
615
+ * a tracked insertion of a fraction with no `m:oMath` around it. None of them
616
+ * carries structure the editable model holds, so all travel as the markup
617
+ * they arrived as.
612
618
  */
613
619
  const mathContentOf = (child) => {
614
- if (getNamespaceUri(child) !== OMML_NAMESPACE) return;
620
+ const namespace = getNamespaceUri(child);
621
+ if (namespace === void 0 || transitionalNamespaceOf(namespace) !== OMML_NAMESPACE) return;
615
622
  const equation = {
616
623
  type: "mathEquation",
617
- display: "inline",
624
+ display: getLocalName(child.name) === "oMathPara" ? "block" : "inline",
618
625
  ommlXml: captureVerbatimXml(child)
619
626
  };
620
627
  const plainText = extractMathText(child);
621
628
  if (plainText) equation.plainText = plainText;
622
629
  return equation;
623
630
  };
624
- const isHyperlinkChildContent = (content) => content.type === "run" || content.type === "bookmarkStart" || content.type === "bookmarkEnd";
631
+ const isHyperlinkChildContent = (content) => content.type === "run" || content.type === "bookmarkStart" || content.type === "bookmarkEnd" || content.type === "preservedInline";
625
632
  /**
626
633
  * A `w:hyperlink` as paragraph content, with any revision wrapper it holds
627
634
  * hoisted around it.
@@ -632,31 +639,16 @@ const isHyperlinkChildContent = (content) => content.type === "run" || content.t
632
639
  * inverse of what the serializer writes, so a package survives the round trip.
633
640
  */
634
641
  function parseHyperlinkParagraphContents(node, rels, styles, theme, media, rootXmlns) {
635
- const children = getChildElements(node);
636
- if (!children.some((child) => hyperlinkRevisionWrapperType(child) !== void 0)) return [parseHyperlink(node, rels, styles, theme, media, rootXmlns)];
642
+ if (!getChildElements(node).some((child) => HYPERLINK_REVISION_WRAPPERS.has(getLocalName(child.name)))) return [parseHyperlink(node, rels, styles, theme, media, rootXmlns)];
637
643
  const inScopeXmlns = mergeXmlnsDeclarations(rootXmlns, node);
638
644
  const shell = parseHyperlink(node, rels, styles, theme, media, rootXmlns);
639
645
  const linkOver = (linkChildren) => ({
640
646
  ...shell,
641
647
  children: [...linkChildren]
642
648
  });
643
- const contents = [];
644
- let plain = [];
645
- const flushPlain = () => {
646
- if (plain.length > 0) {
647
- contents.push(linkOver(plain));
648
- plain = [];
649
- }
650
- };
651
- for (const child of children) {
652
- const wrapperType = hyperlinkRevisionWrapperType(child);
653
- if (wrapperType === void 0) {
654
- const parsed = parseHyperlinkChild(child, styles, theme, rels, media, inScopeXmlns);
655
- if (parsed) plain.push(parsed);
656
- continue;
657
- }
658
- flushPlain();
659
- const wrapped = parseParagraphContents(child, styles, theme, null, rels, media, wrapperType === "deletion" || wrapperType === "moveFrom" ? "deletion" : "default", inScopeXmlns);
649
+ const items = [];
650
+ const hoistRevision = (wrapper) => (child) => {
651
+ const wrapped = parseParagraphContents(child, styles, theme, null, rels, media, wrapper === "deletion" || wrapper === "moveFrom" ? "deletion" : "default", inScopeXmlns);
660
652
  const content = [];
661
653
  let linked = [];
662
654
  const flushLinked = () => {
@@ -674,11 +666,53 @@ function parseHyperlinkParagraphContents(node, rels, styles, theme, media, rootX
674
666
  if (isTrackedChangeWrapperChild(item)) content.push(item);
675
667
  }
676
668
  flushLinked();
669
+ items.push({
670
+ type: "hoistedRevision",
671
+ wrapper,
672
+ info: parseTrackedChangeInfo(child),
673
+ content
674
+ });
675
+ };
676
+ const preserved = dispatchChildren({
677
+ element: node,
678
+ container: "w:hyperlink",
679
+ modelledCount: () => items.length,
680
+ handlers: {
681
+ ...hyperlinkChildHandlers({
682
+ push: (child) => {
683
+ items.push(child);
684
+ },
685
+ styles,
686
+ theme,
687
+ rels,
688
+ media,
689
+ inScopeXmlns
690
+ }),
691
+ ins: hoistRevision("insertion"),
692
+ del: hoistRevision("deletion"),
693
+ moveFrom: hoistRevision("moveFrom"),
694
+ moveTo: hoistRevision("moveTo")
695
+ }
696
+ });
697
+ const contents = [];
698
+ let plain = [];
699
+ const flushPlain = () => {
700
+ if (plain.length > 0) {
701
+ contents.push(linkOver(plain));
702
+ plain = [];
703
+ }
704
+ };
705
+ for (const item of withPreservedChildren(items, preserved, preservedInlineCapture)) {
706
+ if (item.type !== "hoistedRevision") {
707
+ plain.push(item);
708
+ continue;
709
+ }
710
+ flushPlain();
677
711
  pushTrackedChangeWrapper({
678
712
  contents,
679
- type: wrapperType,
680
- info: parseTrackedChangeInfo(child),
681
- content,
713
+ type: item.wrapper,
714
+ info: item.info,
715
+ content: item.content,
682
716
  preserveEmpty: true
683
717
  });
684
718
  }
@@ -708,22 +742,69 @@ function parseSimpleField(node, styles, theme, rels, media, rootXmlns = {}) {
708
742
  type: "simpleField",
709
743
  instruction,
710
744
  fieldType: parseFieldType(instruction),
711
- content: []
745
+ content: [],
746
+ ...parseFieldState(node)
712
747
  };
713
- if (parseOnOffAttribute(node, "w", "fldLock") === true) field.fldLock = true;
714
- if (parseOnOffAttribute(node, "w", "dirty") === true) field.dirty = true;
715
748
  const inScopeXmlns = mergeXmlnsDeclarations(rootXmlns, node);
716
- const children = getChildElements(node);
717
- for (const child of children) {
718
- const localName = getLocalName(child.name);
719
- if (localName === "r") field.content.push(parseRun(child, styles, theme, rels, media, inScopeXmlns));
720
- else if (localName === "hyperlink") field.content.push(parseHyperlink(child, rels, styles, theme, media, inScopeXmlns));
721
- }
749
+ const content = [];
750
+ field.content = withPreservedChildren(content, dispatchChildren({
751
+ element: node,
752
+ container: "w:fldSimple",
753
+ modelledCount: () => content.length,
754
+ handlers: {
755
+ r: (child) => {
756
+ content.push(parseRun(child, styles, theme, rels, media, inScopeXmlns));
757
+ },
758
+ hyperlink: (child) => {
759
+ content.push(parseHyperlink(child, rels, styles, theme, media, inScopeXmlns));
760
+ },
761
+ customXml: (child) => {
762
+ content.push(preserveInlineChild(child));
763
+ },
764
+ smartTag: (child) => {
765
+ content.push(preserveInlineChild(child));
766
+ },
767
+ bdo: CAPTURE,
768
+ bookmarkEnd: CAPTURE,
769
+ bookmarkStart: CAPTURE,
770
+ commentRangeEnd: CAPTURE,
771
+ commentRangeStart: CAPTURE,
772
+ customXmlDelRangeEnd: CAPTURE,
773
+ customXmlDelRangeStart: CAPTURE,
774
+ customXmlInsRangeEnd: CAPTURE,
775
+ customXmlInsRangeStart: CAPTURE,
776
+ customXmlMoveFromRangeEnd: CAPTURE,
777
+ customXmlMoveFromRangeStart: CAPTURE,
778
+ customXmlMoveToRangeEnd: CAPTURE,
779
+ customXmlMoveToRangeStart: CAPTURE,
780
+ del: CAPTURE,
781
+ dir: CAPTURE,
782
+ fldData: CAPTURE,
783
+ fldSimple: CAPTURE,
784
+ ins: CAPTURE,
785
+ moveFrom: CAPTURE,
786
+ moveFromRangeEnd: CAPTURE,
787
+ moveFromRangeStart: CAPTURE,
788
+ moveTo: CAPTURE,
789
+ moveToRangeEnd: CAPTURE,
790
+ moveToRangeStart: CAPTURE,
791
+ permEnd: CAPTURE,
792
+ permStart: CAPTURE,
793
+ proofErr: CAPTURE,
794
+ sdt: CAPTURE,
795
+ subDoc: CAPTURE
796
+ }
797
+ }), preservedInlineCapture);
722
798
  return field;
723
799
  }
724
- function hasRunPayloadElement(runElement) {
725
- return getChildElements(runElement).some((child) => !matchesName(child, "w", "rPr"));
726
- }
800
+ const hasRunPayload = ({ run, runElement, rels, media }) => {
801
+ if (run.content.length > 0) return true;
802
+ const { textBoxDrawings, vmlTextBoxes } = scanRunForTextBoxDrawings({
803
+ xmlRun: runElement,
804
+ claimedByRunParser: (pictElement) => isVmlPictParsedByRunParser(pictElement, rels, media)
805
+ });
806
+ return textBoxDrawings.length > 0 || vmlTextBoxes.length > 0;
807
+ };
727
808
  const LEGACY_FORM_CHECKBOX_GLYPHS = {
728
809
  checked: "☒",
729
810
  unchecked: "☐"
@@ -772,27 +853,45 @@ function isLegacyFormCheckboxInstruction(instruction) {
772
853
  */
773
854
  function parseParagraphContents(paraElement, styles, theme, _numbering, rels, media, trackedContext = "default", rootXmlns = {}) {
774
855
  const contents = [];
775
- const children = getChildElements(paraElement);
776
856
  const inScopeXmlns = mergeXmlnsDeclarations(rootXmlns, paraElement);
777
857
  let inComplexField = false;
778
858
  let complexFieldInstr = "";
779
859
  let complexFieldCodeRuns = [];
780
860
  let complexFieldResultRuns = [];
781
861
  let afterSeparator = false;
782
- let complexFieldLock = false;
783
- let complexFieldDirty = false;
862
+ let complexFieldState = {};
784
863
  let complexFieldFallbackDisplay;
785
864
  let complexFieldFormatting;
786
- for (const child of children) {
787
- const localName = getLocalName(child.name);
788
- switch (localName) {
789
- case "r": {
865
+ const parseBidiWrapper = (child, control) => {
866
+ const wrapper = {
867
+ type: "inlineWrapper",
868
+ kind: "bidi",
869
+ control,
870
+ content: parseParagraphContents(child, styles, theme, null, rels, media, trackedContext, inScopeXmlns)
871
+ };
872
+ const direction = getAttribute(child, "w", "val");
873
+ if (direction === "ltr" || direction === "rtl") wrapper.direction = direction;
874
+ return wrapper;
875
+ };
876
+ const preserved = dispatchChildren({
877
+ element: paraElement,
878
+ container: "run-level-content",
879
+ modelledCount: () => contents.length,
880
+ undeclared: { AlternateContent: (child) => {
881
+ const selectedBranch = selectAlternateContentBranch(child);
882
+ if (selectedBranch) contents.push(...parseParagraphContents(selectedBranch, styles, theme, null, rels, media, trackedContext, mergeXmlnsDeclarations(inScopeXmlns, child)));
883
+ } },
884
+ undeclaredNamespaces: { [OMML_NAMESPACE]: (child) => {
885
+ const equation = mathContentOf(child);
886
+ if (equation !== void 0) contents.push(equation);
887
+ } },
888
+ handlers: {
889
+ r: (child) => {
790
890
  const runElement = trackedContext === "deletion" ? normalizeDeletionContentElement(child) : child;
791
891
  const run = parseRun(runElement, styles, theme, rels, media, inScopeXmlns);
792
892
  const commentReferenceId = getCommentReferenceId(runElement);
793
893
  let hasFieldBegin = false;
794
- let beginFldLock = false;
795
- let beginDirty = false;
894
+ let beginFieldState = {};
796
895
  const beginFallbackDisplay = getLegacyFormCheckboxDisplay(runElement);
797
896
  let hasFieldSeparate = false;
798
897
  let hasFieldEnd = false;
@@ -800,8 +899,7 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
800
899
  let instrText = "";
801
900
  for (const content of run.content) if (content.type === "fieldChar") if (content.charType === "begin") {
802
901
  hasFieldBegin = true;
803
- beginFldLock = content.fldLock === true;
804
- beginDirty = content.dirty === true;
902
+ beginFieldState = fieldStateOf(content);
805
903
  } else if (content.charType === "separate") hasFieldSeparate = true;
806
904
  else {
807
905
  hasFieldEnd = true;
@@ -818,8 +916,7 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
818
916
  complexFieldInstr = "";
819
917
  complexFieldCodeRuns = [];
820
918
  complexFieldResultRuns = [];
821
- complexFieldLock = beginFldLock;
822
- complexFieldDirty = beginDirty;
919
+ complexFieldState = beginFieldState;
823
920
  complexFieldFallbackDisplay = beginFallbackDisplay;
824
921
  complexFieldFormatting = run.formatting;
825
922
  }
@@ -858,10 +955,9 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
858
955
  instruction: complexFieldInstr,
859
956
  fieldType: parseFieldType(complexFieldInstr),
860
957
  fieldCode: complexFieldCodeRuns,
861
- fieldResult: resultRuns
958
+ fieldResult: resultRuns,
959
+ ...complexFieldState
862
960
  };
863
- if (complexFieldLock) complexField.fldLock = true;
864
- if (complexFieldDirty) complexField.dirty = true;
865
961
  if (complexFieldFormatting) complexField.formatting = complexFieldFormatting;
866
962
  contents.push(complexField);
867
963
  if (commentReferenceId !== null) contents.push({
@@ -876,153 +972,146 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
876
972
  type: "commentReference",
877
973
  id: commentReferenceId
878
974
  });
879
- } else if (run.content.length > 0 || hasRunPayloadElement(runElement)) contents.push(run);
880
- break;
881
- }
882
- case "AlternateContent": {
883
- const selectedBranch = selectAlternateContentBranch(child);
884
- if (selectedBranch) contents.push(...parseParagraphContents(selectedBranch, styles, theme, null, rels, media, trackedContext, mergeXmlnsDeclarations(inScopeXmlns, child)));
885
- break;
886
- }
887
- case "hyperlink":
975
+ } else if (hasRunPayload({
976
+ run,
977
+ runElement,
978
+ rels,
979
+ media
980
+ })) contents.push(run);
981
+ },
982
+ hyperlink: (child) => {
888
983
  contents.push(...parseHyperlinkParagraphContents(child, rels, styles, theme, media, inScopeXmlns));
889
- break;
890
- case "bookmarkStart":
984
+ },
985
+ bookmarkStart: (child) => {
891
986
  contents.push(parseBookmarkStart(child));
892
- break;
893
- case "bookmarkEnd":
987
+ },
988
+ bookmarkEnd: (child) => {
894
989
  contents.push(parseBookmarkEnd(child));
895
- break;
896
- case "fldSimple":
990
+ },
991
+ fldSimple: (child) => {
897
992
  contents.push(parseSimpleField(child, styles, theme, rels, media, inScopeXmlns));
898
- break;
899
- case "pPr": break;
900
- case "proofErr":
901
- case "permStart":
902
- case "permEnd":
903
- case "customXml": break;
904
- case "sdt": {
993
+ },
994
+ pPr: OWNED_ELSEWHERE,
995
+ customXml: (child) => {
996
+ contents.push(preserveInlineChild(child));
997
+ },
998
+ proofErr: CAPTURE,
999
+ permStart: CAPTURE,
1000
+ permEnd: CAPTURE,
1001
+ subDoc: CAPTURE,
1002
+ customXmlDelRangeEnd: CAPTURE,
1003
+ customXmlDelRangeStart: CAPTURE,
1004
+ customXmlInsRangeEnd: CAPTURE,
1005
+ customXmlInsRangeStart: CAPTURE,
1006
+ customXmlMoveFromRangeEnd: CAPTURE,
1007
+ customXmlMoveFromRangeStart: CAPTURE,
1008
+ customXmlMoveToRangeEnd: CAPTURE,
1009
+ customXmlMoveToRangeStart: CAPTURE,
1010
+ smartTagPr: DROPPED_WITH_ITS_WRAPPER,
1011
+ sdt: (child) => {
905
1012
  const sdtPr = findChild(child, "w", "sdtPr");
906
1013
  const sdtEndPr = findChild(child, "w", "sdtEndPr");
907
1014
  const sdtContentEl = findChild(child, "w", "sdtContent");
908
1015
  if (sdtContentEl) {
909
1016
  const sdtParsed = parseParagraphContents(sdtContentEl, styles, theme, null, rels, media, trackedContext, mergeXmlnsDeclarations(inScopeXmlns, child));
1017
+ const properties = parseSdtProperties(sdtPr, sdtEndPr);
910
1018
  pushInlineSdtSegments({
911
1019
  contents,
912
- properties: parseSdtProperties(sdtPr, sdtEndPr),
1020
+ properties,
913
1021
  parsedContent: sdtParsed
914
1022
  });
915
1023
  }
916
- break;
917
- }
918
- case "ins":
1024
+ },
1025
+ ins: (child) => {
1026
+ const insInfo = parseTrackedChangeInfo(child);
1027
+ const insContent = parseParagraphContents(child, styles, theme, null, rels, media, "default", inScopeXmlns);
919
1028
  pushTrackedChangeSegments({
920
1029
  contents,
921
1030
  type: "insertion",
922
- info: parseTrackedChangeInfo(child),
923
- parsedContent: parseParagraphContents(child, styles, theme, null, rels, media, "default", inScopeXmlns)
1031
+ info: insInfo,
1032
+ parsedContent: insContent
924
1033
  });
925
- break;
926
- case "del":
1034
+ },
1035
+ del: (child) => {
1036
+ const delInfo = parseTrackedChangeInfo(child);
1037
+ const delContent = parseParagraphContents(child, styles, theme, null, rels, media, "deletion", inScopeXmlns);
927
1038
  pushTrackedChangeSegments({
928
1039
  contents,
929
1040
  type: "deletion",
930
- info: parseTrackedChangeInfo(child),
931
- parsedContent: parseParagraphContents(child, styles, theme, null, rels, media, "deletion", inScopeXmlns)
1041
+ info: delInfo,
1042
+ parsedContent: delContent
932
1043
  });
933
- break;
934
- case "moveFrom":
1044
+ },
1045
+ moveFrom: (child) => {
1046
+ const moveFromInfo = parseTrackedChangeInfo(child);
1047
+ const moveFromContent = parseParagraphContents(child, styles, theme, null, rels, media, "deletion", inScopeXmlns);
935
1048
  pushTrackedChangeSegments({
936
1049
  contents,
937
1050
  type: "moveFrom",
938
- info: parseTrackedChangeInfo(child),
939
- parsedContent: parseParagraphContents(child, styles, theme, null, rels, media, "deletion", inScopeXmlns)
1051
+ info: moveFromInfo,
1052
+ parsedContent: moveFromContent
940
1053
  });
941
- break;
942
- case "moveTo":
1054
+ },
1055
+ moveTo: (child) => {
1056
+ const moveToInfo = parseTrackedChangeInfo(child);
1057
+ const moveToContent = parseParagraphContents(child, styles, theme, null, rels, media, "default", inScopeXmlns);
943
1058
  pushTrackedChangeSegments({
944
1059
  contents,
945
1060
  type: "moveTo",
946
- info: parseTrackedChangeInfo(child),
947
- parsedContent: parseParagraphContents(child, styles, theme, null, rels, media, "default", inScopeXmlns)
1061
+ info: moveToInfo,
1062
+ parsedContent: moveToContent
948
1063
  });
949
- break;
950
- case "smartTag": {
1064
+ },
1065
+ smartTag: (child) => {
951
1066
  const inner = parseParagraphContents(child, styles, theme, null, rels, media, trackedContext, mergeXmlnsDeclarations(inScopeXmlns, child));
952
1067
  contents.push(...inner);
953
- break;
954
- }
955
- case "moveFromRangeStart":
1068
+ },
1069
+ moveFromRangeStart: (child) => {
956
1070
  contents.push({
957
1071
  type: "moveFromRangeStart",
958
1072
  ...parseMoveBookmarkMarker(child)
959
1073
  });
960
- break;
961
- case "moveFromRangeEnd":
1074
+ },
1075
+ moveFromRangeEnd: (child) => {
962
1076
  contents.push({
963
1077
  type: "moveFromRangeEnd",
964
1078
  ...parseMarkupRangeMarker(child)
965
1079
  });
966
- break;
967
- case "moveToRangeStart":
1080
+ },
1081
+ moveToRangeStart: (child) => {
968
1082
  contents.push({
969
1083
  type: "moveToRangeStart",
970
1084
  ...parseMoveBookmarkMarker(child)
971
1085
  });
972
- break;
973
- case "moveToRangeEnd":
1086
+ },
1087
+ moveToRangeEnd: (child) => {
974
1088
  contents.push({
975
1089
  type: "moveToRangeEnd",
976
1090
  ...parseMarkupRangeMarker(child)
977
1091
  });
978
- break;
979
- case "commentRangeStart":
1092
+ },
1093
+ commentRangeStart: (child) => {
980
1094
  contents.push({
981
1095
  type: "commentRangeStart",
982
1096
  ...parseMarkupRangeMarker(child)
983
1097
  });
984
- break;
985
- case "commentRangeEnd":
1098
+ },
1099
+ commentRangeEnd: (child) => {
986
1100
  contents.push({
987
1101
  type: "commentRangeEnd",
988
1102
  ...parseMarkupRangeMarker(child)
989
1103
  });
990
- break;
991
- case "bdo":
992
- case "dir": {
993
- const direction = getAttribute(child, "w", "val");
994
- const wrapper = {
995
- type: "bidiWrapper",
996
- control: localName === "bdo" ? BIDI_CONTROLS.override : BIDI_CONTROLS.embedding,
997
- content: parseParagraphContents(child, styles, theme, null, rels, media, trackedContext, inScopeXmlns)
998
- };
999
- if (direction === "ltr" || direction === "rtl") wrapper.direction = direction;
1000
- contents.push(wrapper);
1001
- break;
1002
- }
1003
- case "oMath":
1004
- case "oMathPara": {
1005
- const isBlock = localName === "oMathPara";
1006
- const ommlXml = captureVerbatimXml(child);
1007
- const plainText = extractMathText(child);
1008
- const mathEq = {
1009
- type: "mathEquation",
1010
- display: isBlock ? "block" : "inline",
1011
- ommlXml
1012
- };
1013
- if (plainText) mathEq.plainText = plainText;
1014
- contents.push(mathEq);
1015
- break;
1016
- }
1017
- default: {
1018
- const mathElement = mathContentOf(child);
1019
- if (mathElement !== void 0) contents.push(mathElement);
1020
- break;
1104
+ },
1105
+ bdo: (child) => {
1106
+ contents.push(parseBidiWrapper(child, BIDI_CONTROLS.override));
1107
+ },
1108
+ dir: (child) => {
1109
+ contents.push(parseBidiWrapper(child, BIDI_CONTROLS.embedding));
1021
1110
  }
1022
1111
  }
1023
- }
1112
+ });
1024
1113
  if (inComplexField && afterSeparator) contents.push(...complexFieldResultRuns);
1025
- return contents;
1114
+ return withPreservedChildren(contents, preserved, preservedInlineCapture);
1026
1115
  }
1027
1116
  function getCommentReferenceId(runElement) {
1028
1117
  const commentReference = findChild(runElement, "w", "commentReference");
@@ -1031,6 +1120,18 @@ function getCommentReferenceId(runElement) {
1031
1120
  return Number.isFinite(id) ? id : null;
1032
1121
  }
1033
1122
  /**
1123
+ * Attribute local names `w:p` has a model field for.
1124
+ *
1125
+ * `w14:paraId` and `w14:textId` are read below and written by
1126
+ * `serializeParagraph`; `folio:reviewCarrier` is folio's own. Everything else
1127
+ * the element carried is the attribute remainder.
1128
+ */
1129
+ const PARAGRAPH_ATTRIBUTES = /* @__PURE__ */ new Set([
1130
+ "paraId",
1131
+ "textId",
1132
+ "reviewCarrier"
1133
+ ]);
1134
+ /**
1034
1135
  * Parse a paragraph element (w:p)
1035
1136
  *
1036
1137
  * @param node - The w:p XML element
@@ -1047,10 +1148,15 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1047
1148
  type: "paragraph",
1048
1149
  content: []
1049
1150
  };
1050
- const paraId = getAttribute(node, "w14", "paraId") ?? getAttribute(node, "w", "paraId");
1151
+ const paraId = paraIdAttribute(node);
1051
1152
  if (paraId && isValidHexId(paraId)) paragraph.paraId = paraIdInRange(paraId);
1052
- const textId = getAttribute(node, "w14", "textId") ?? getAttribute(node, "w", "textId");
1153
+ const textId = textIdAttribute(node);
1053
1154
  if (textId && isValidHexId(textId)) paragraph.textId = paraIdInRange(textId);
1155
+ const remainder = attributeRemainder({
1156
+ element: node,
1157
+ modelled: PARAGRAPH_ATTRIBUTES
1158
+ });
1159
+ if (remainder) paragraph.preservedAttributes = remainder;
1054
1160
  if (getAttributeByNamespaceUri(node, FOLIO_REVIEW_HISTORY_NAMESPACES, "reviewCarrier") === REVIEW_CARRIERS.TERMINAL_TABLE) paragraph.reviewCarrier = REVIEW_CARRIERS.TERMINAL_TABLE;
1055
1161
  if (!options?.inHeaderFooter && paragraphStartsWithRenderedPageBreak(node)) paragraph.renderedPageBreakBefore = true;
1056
1162
  const pPr = findChild(node, "w", "pPr");
@@ -1205,6 +1311,7 @@ const getRunContentText = (content) => {
1205
1311
  case "break": return content.breakType === "page" ? "\f" : "\n";
1206
1312
  case "noBreakHyphen": return "‑";
1207
1313
  case "softHyphen": return "­";
1314
+ case "preservedXml": return content.text;
1208
1315
  case "drawing":
1209
1316
  case "endnoteRef":
1210
1317
  case "fieldChar":
@@ -1225,6 +1332,7 @@ const getHyperlinkText = (hyperlink) => hyperlink.children.map((child) => {
1225
1332
  case "run": return getRunText(child);
1226
1333
  case "bookmarkStart":
1227
1334
  case "bookmarkEnd": return "";
1335
+ case "preservedInline": return child.text;
1228
1336
  default: return panic(`Unsupported hyperlink child in plain-text extraction: ${JSON.stringify(child)}`);
1229
1337
  }
1230
1338
  }).join("");
@@ -1237,10 +1345,11 @@ const getParagraphContentText = (content) => {
1237
1345
  case "inlineSdt": return content.content.map(getParagraphContentText).join("");
1238
1346
  case "insertion":
1239
1347
  case "moveTo":
1240
- case "bidiWrapper": return content.content.map(getParagraphContentText).join("");
1348
+ case "inlineWrapper": return content.content.map(getParagraphContentText).join("");
1241
1349
  case "deletion":
1242
1350
  case "moveFrom": return "";
1243
1351
  case "mathEquation": return content.plainText ?? "";
1352
+ case "preservedInline": return content.text;
1244
1353
  case "bookmarkEnd":
1245
1354
  case "bookmarkStart":
1246
1355
  case "commentRangeEnd":