@stll/folio-core 0.47.7 → 0.48.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 (160) hide show
  1. package/dist/controller/fontReadiness.js +3 -3
  2. package/dist/controller/hyphenationReadiness.d.ts +19 -0
  3. package/dist/controller/hyphenationReadiness.js +47 -0
  4. package/dist/controller/layoutPipeline.d.ts +3 -0
  5. package/dist/controller/layoutPipeline.js +59 -17
  6. package/dist/display-list/build/buildDisplayList.js +39 -12
  7. package/dist/display-list/build/pageFurniture.d.ts +7 -1
  8. package/dist/display-list/build/pageFurniture.js +21 -8
  9. package/dist/display-list/build/paragraphPrimitives.js +5 -4
  10. package/dist/display-list/build/tablePrimitives.js +10 -6
  11. package/dist/docx/attributeRemainder.js +10 -2
  12. package/dist/docx/blockContentParser.d.ts +2 -0
  13. package/dist/docx/blockContentParser.js +77 -62
  14. package/dist/docx/commentParser.d.ts +2 -1
  15. package/dist/docx/commentParser.js +57 -45
  16. package/dist/docx/containerChildren.d.ts +21 -8
  17. package/dist/docx/containerChildren.js +39 -9
  18. package/dist/docx/documentParser.d.ts +5 -1
  19. package/dist/docx/documentParser.js +8 -5
  20. package/dist/docx/documentSectionFacts.d.ts +22 -0
  21. package/dist/docx/documentSectionFacts.js +43 -0
  22. package/dist/docx/fieldParser.d.ts +8 -1
  23. package/dist/docx/fieldParser.js +47 -3
  24. package/dist/docx/fontTableParser.js +34 -27
  25. package/dist/docx/footnoteParser.d.ts +3 -2
  26. package/dist/docx/footnoteParser.js +15 -12
  27. package/dist/docx/groupDrawingParser.d.ts +2 -1
  28. package/dist/docx/groupDrawingParser.js +3 -6
  29. package/dist/docx/headerFooterParser.d.ts +7 -2
  30. package/dist/docx/headerFooterParser.js +15 -5
  31. package/dist/docx/hyperlinkParser.d.ts +48 -36
  32. package/dist/docx/hyperlinkParser.js +97 -82
  33. package/dist/docx/numberingParser.js +108 -84
  34. package/dist/docx/paragraphParser.d.ts +5 -1
  35. package/dist/docx/paragraphParser.js +381 -362
  36. package/dist/docx/paragraphProperties.js +123 -109
  37. package/dist/docx/paragraphPropertySource.d.ts +6 -1
  38. package/dist/docx/paragraphPropertySource.js +9 -1
  39. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +17 -6
  41. package/dist/docx/parser.d.ts +7 -1
  42. package/dist/docx/parser.js +33 -16
  43. package/dist/docx/previewBudget.d.ts +55 -33
  44. package/dist/docx/previewBudget.js +59 -65
  45. package/dist/docx/rezip.js +41 -58
  46. package/dist/docx/runParser.d.ts +4 -1
  47. package/dist/docx/runParser.js +172 -184
  48. package/dist/docx/sdtProperties.js +87 -84
  49. package/dist/docx/sectionParser.js +169 -160
  50. package/dist/docx/serializer/paragraphSerializer.js +42 -38
  51. package/dist/docx/serializer/settingsSerializer.js +19 -1
  52. package/dist/docx/settingsParser.js +8 -0
  53. package/dist/docx/streamingXmlParser.d.ts +6 -2
  54. package/dist/docx/streamingXmlParser.js +12 -7
  55. package/dist/docx/styleParser.js +34 -8
  56. package/dist/docx/tableParser.d.ts +21 -12
  57. package/dist/docx/tableParser.js +518 -441
  58. package/dist/docx/textBoxParser.d.ts +8 -3
  59. package/dist/docx/textBoxParser.js +3 -3
  60. package/dist/docx/verbatimCapture.d.ts +10 -8
  61. package/dist/docx/verbatimCapture.js +45 -2
  62. package/dist/docx/vmlImageParser.d.ts +15 -13
  63. package/dist/docx/vmlImageParser.js +75 -60
  64. package/dist/docx/vmlPreview.d.ts +3 -3
  65. package/dist/docx/vmlPreview.js +3 -4
  66. package/dist/docx/xmlNamespaceContext.d.ts +9 -0
  67. package/dist/docx/xmlNamespaceContext.js +54 -0
  68. package/dist/docx/xmlParser.d.ts +6 -3
  69. package/dist/docx/xmlParser.js +11 -41
  70. package/dist/docx/xmlResourceLimits.d.ts +2 -1
  71. package/dist/docx/xmlResourceLimits.js +4 -1
  72. package/dist/fonts/headlessMeasure.js +0 -0
  73. package/dist/headless-layout.d.ts +11 -2
  74. package/dist/headless-layout.js +153 -107
  75. package/dist/internal/paragraphFormattingSerialization.js +3 -3
  76. package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
  77. package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
  78. package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
  79. package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
  80. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
  81. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
  82. package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
  83. package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
  84. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
  85. package/dist/layout-bridge/convert/toFlowBlocks.js +258 -35
  86. package/dist/layout-bridge/engine/hitTest.js +5 -3
  87. package/dist/layout-bridge/engine/selectionRects.js +5 -3
  88. package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
  89. package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
  90. package/dist/layout-engine/index.d.ts +2 -2
  91. package/dist/layout-engine/index.js +70 -11
  92. package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
  93. package/dist/layout-engine/justificationCompatibility.js +9 -5
  94. package/dist/layout-engine/keep-together.d.ts +11 -5
  95. package/dist/layout-engine/keep-together.js +39 -16
  96. package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
  97. package/dist/layout-engine/layoutInstrumentation.js +8 -1
  98. package/dist/layout-engine/measure/advanceComposition.js +4 -7
  99. package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
  100. package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
  101. package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
  102. package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
  103. package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
  104. package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
  105. package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
  106. package/dist/layout-engine/measure/lineBreaks.js +10 -3
  107. package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
  108. package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
  109. package/dist/layout-engine/measure/measureBlocks.js +42 -8
  110. package/dist/layout-engine/measure/measureContainer.js +5 -5
  111. package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
  112. package/dist/layout-engine/measure/measureHelpers.js +21 -5
  113. package/dist/layout-engine/measure/measureParagraph.js +109 -30
  114. package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
  115. package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
  116. package/dist/layout-engine/measure/tabCalculator.js +2 -1
  117. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  118. package/dist/layout-engine/measure/tableCellFloating.js +51 -19
  119. package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
  120. package/dist/layout-engine/measure/tableCellGrid.js +45 -5
  121. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
  122. package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
  123. package/dist/layout-engine/noteAreaFlow.js +83 -0
  124. package/dist/layout-engine/paginator.d.ts +10 -1
  125. package/dist/layout-engine/paginator.js +13 -1
  126. package/dist/layout-engine/tableRowBreak.js +3 -1
  127. package/dist/layout-engine/types.d.ts +89 -3
  128. package/dist/layout-engine/types.js +96 -1
  129. package/dist/layout-painter/index.d.ts +3 -0
  130. package/dist/layout-painter/renderPage.js +32 -83
  131. package/dist/layout-painter/renderParagraph.d.ts +6 -1
  132. package/dist/layout-painter/renderParagraph.js +34 -22
  133. package/dist/layout-painter/renderTable.js +26 -13
  134. package/dist/prosemirror/attrs/index.d.ts +9 -9
  135. package/dist/prosemirror/attrs/index.js +43 -11
  136. package/dist/prosemirror/conversion/fromProseDoc.js +8 -1
  137. package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
  138. package/dist/prosemirror/conversion/markInterner.js +90 -0
  139. package/dist/prosemirror/conversion/toProseDoc.js +77 -41
  140. package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
  141. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  142. package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
  143. package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
  144. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +2 -1
  145. package/dist/prosemirror/listMarker.d.ts +1 -0
  146. package/dist/prosemirror/listMarker.js +2 -0
  147. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  148. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  149. package/dist/prosemirror/schema/nodes.d.ts +13 -1
  150. package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
  151. package/dist/prosemirror/textBoxHostParagraph.js +21 -0
  152. package/dist/prosemirror/validation.js +91 -80
  153. package/dist/utils/createDocument.js +10 -1
  154. package/dist/utils/scriptSegments.d.ts +33 -4
  155. package/dist/utils/scriptSegments.js +39 -6
  156. package/dist/utils/textFormattingMerge.d.ts +17 -3
  157. package/dist/utils/textFormattingMerge.js +18 -12
  158. package/dist/utils/trailingText.d.ts +22 -0
  159. package/dist/utils/trailingText.js +32 -0
  160. package/package.json +2 -2
@@ -14,6 +14,7 @@ import { panic } from "better-result";
14
14
  import { DRAWING_RAW_XML_MODES, THEME_COLORS, isOoxmlSymbolCharacter } from "@stll/docx-core/model";
15
15
  import { isSafePreservedChildXml, isWithinPreservedMarkupBudget } from "@stll/docx-core/schema";
16
16
  //#region src/prosemirror/attrs/index.ts
17
+ const RUN_FORMATTING_VALUE_PROPERTY_SET = new Set(RUN_FORMATTING_VALUE_PROPERTIES);
17
18
  const PRESERVED_XML_LEVEL_VALUES = new Set(Object.values(PRESERVED_XML_LEVELS));
18
19
  const IMAGE_DISPLAY_MODES = [
19
20
  "inline",
@@ -149,15 +150,12 @@ const SECTION_VERTICAL_ALIGNMENTS = [
149
150
  "bottom"
150
151
  ];
151
152
  const OMITTED_GRID_SLOT_VALUES = ["before", "after"];
152
- const paragraphAttrsCache = /* @__PURE__ */ new WeakMap();
153
153
  const hardBreakAttrsCache = /* @__PURE__ */ new WeakMap();
154
154
  const pageBreakRunAttrsCache = /* @__PURE__ */ new WeakMap();
155
155
  const tabAttrsCache = /* @__PURE__ */ new WeakMap();
156
156
  const symbolAttrsCache = /* @__PURE__ */ new WeakMap();
157
157
  const preservedXmlAttrsCache = /* @__PURE__ */ new WeakMap();
158
158
  const preservedBlockAttrsCache = /* @__PURE__ */ new WeakMap();
159
- const tableAttrsCache = /* @__PURE__ */ new WeakMap();
160
- const tableRowAttrsCache = /* @__PURE__ */ new WeakMap();
161
159
  const tableCellAttrsCache = /* @__PURE__ */ new WeakMap();
162
160
  const imageAttrsCache = /* @__PURE__ */ new WeakMap();
163
161
  const fieldAttrsCache = /* @__PURE__ */ new WeakMap();
@@ -185,7 +183,36 @@ const runIdentityAttrsCache = /* @__PURE__ */ new WeakMap();
185
183
  const inlineWrapperAttrsCache = /* @__PURE__ */ new WeakMap();
186
184
  const runFormattingOverrideAttrsCache = /* @__PURE__ */ new WeakMap();
187
185
  const hyperlinkAttrsCache = /* @__PURE__ */ new WeakMap();
188
- const readParagraphAttrs = (node) => {
186
+ /**
187
+ * Read a node's attrs once per node. Nodes are immutable, and one conversion
188
+ * reads each paragraph, table and row several times (node validation, the
189
+ * bookmark walk, numbering). The cached result is frozen, value and issues
190
+ * included, so no caller can change what the next one reads.
191
+ */
192
+ const memoizeNodeAttrsReader = (read) => {
193
+ const results = /* @__PURE__ */ new WeakMap();
194
+ return (node) => {
195
+ const cached = results.get(node);
196
+ if (cached !== void 0) return cached;
197
+ const result = freezeAttrsResult(read(node));
198
+ results.set(node, result);
199
+ return result;
200
+ };
201
+ };
202
+ const freezeAttrsResult = (result) => {
203
+ if (!result.ok) {
204
+ const issues = result.issues.map((issue) => Object.freeze({ ...issue }));
205
+ return Object.freeze({
206
+ ok: false,
207
+ issues: Object.freeze(issues)
208
+ });
209
+ }
210
+ return Object.freeze({
211
+ ok: true,
212
+ value: Object.freeze(result.value)
213
+ });
214
+ };
215
+ const readParagraphAttrsUncached = (node) => {
189
216
  const attrs = attrsRecord(node.attrs);
190
217
  const issues = [];
191
218
  expectNodeType(node, "paragraph", issues);
@@ -244,6 +271,7 @@ const readParagraphAttrs = (node) => {
244
271
  });
245
272
  optionalStringArray(attrs, "listLevelNumFmts", "paragraph.attrs.listLevelNumFmts", issues);
246
273
  optionalNumberArray(attrs, "listLevelStarts", "paragraph.attrs.listLevelStarts", issues);
274
+ optionalTabStops(attrs, "listLevelTabs", "paragraph.attrs.listLevelTabs", issues);
247
275
  optionalNumber(attrs, "listAbstractNumId", "paragraph.attrs.listAbstractNumId", issues);
248
276
  optionalBorderMap(attrs, "borders", "paragraph.attrs.borders", issues, [
249
277
  "top",
@@ -271,7 +299,8 @@ const readParagraphAttrs = (node) => {
271
299
  optionalPreservedAttributes(attrs, "_preservedAttributes", "paragraph.attrs._preservedAttributes", issues);
272
300
  return attrsResult(attrs, issues);
273
301
  };
274
- const expectParagraphAttrs = (node) => expectCachedNodeAttrs(node, paragraphAttrsCache, readParagraphAttrs, "paragraph attrs");
302
+ const readParagraphAttrs = memoizeNodeAttrsReader(readParagraphAttrsUncached);
303
+ const expectParagraphAttrs = (node) => expectAttrs(readParagraphAttrs(node), "paragraph attrs");
275
304
  const readHardBreakAttrs = (node) => {
276
305
  const attrs = attrsRecord(node.attrs);
277
306
  const issues = [];
@@ -348,7 +377,7 @@ const readPreservedBlockAttrs = (node) => {
348
377
  return attrsResult(attrs, issues);
349
378
  };
350
379
  const expectPreservedBlockAttrs = (node) => expectCachedNodeAttrs(node, preservedBlockAttrsCache, readPreservedBlockAttrs, "preservedBlock attrs");
351
- const readTableAttrs = (node) => {
380
+ const readTableAttrsUncached = (node) => {
352
381
  const attrs = attrsRecord(node.attrs);
353
382
  const issues = [];
354
383
  expectNodeType(node, "table", issues);
@@ -375,8 +404,9 @@ const readTableAttrs = (node) => {
375
404
  optionalTablePreservedMarkup(attrs, "_preserved", "table.attrs._preserved", issues);
376
405
  return attrsResult(attrs, issues);
377
406
  };
378
- const expectTableAttrs = (node) => expectCachedNodeAttrs(node, tableAttrsCache, readTableAttrs, "table attrs");
379
- const readTableRowAttrs = (node) => {
407
+ const readTableAttrs = memoizeNodeAttrsReader(readTableAttrsUncached);
408
+ const expectTableAttrs = (node) => expectAttrs(readTableAttrs(node), "table attrs");
409
+ const readTableRowAttrsUncached = (node) => {
380
410
  const attrs = attrsRecord(node.attrs);
381
411
  const issues = [];
382
412
  expectNodeType(node, "tableRow", issues);
@@ -399,7 +429,8 @@ const readTableRowAttrs = (node) => {
399
429
  optionalTablePreservedMarkup(attrs, "_preserved", "tableRow.attrs._preserved", issues);
400
430
  return attrsResult(attrs, issues);
401
431
  };
402
- const expectTableRowAttrs = (node) => expectCachedNodeAttrs(node, tableRowAttrsCache, readTableRowAttrs, "table row attrs");
432
+ const readTableRowAttrs = memoizeNodeAttrsReader(readTableRowAttrsUncached);
433
+ const expectTableRowAttrs = (node) => expectAttrs(readTableRowAttrs(node), "table row attrs");
403
434
  const readTableCellAttrs = (node) => {
404
435
  const attrs = attrsRecord(node.attrs);
405
436
  const issues = [];
@@ -656,6 +687,7 @@ const readTextBoxAttrs = (node) => {
656
687
  optionalTextBoxTrackedChange(attrs, issues);
657
688
  optionalTextBoxInlineSdts(attrs, issues);
658
689
  optionalPreservedAttributes(attrs, "_preservedAttributes", "textBox.attrs._preservedAttributes", issues);
690
+ optionalRecord(attrs, "_docxHostParagraph", "textBox.attrs._docxHostParagraph", issues);
659
691
  return attrsResult(attrs, issues);
660
692
  };
661
693
  const expectTextBoxAttrs = (node) => expectCachedNodeAttrs(node, textBoxAttrsCache, readTextBoxAttrs, "text box attrs");
@@ -1042,8 +1074,7 @@ const readRunFormattingOverrideMarkAttrs = (mark) => {
1042
1074
  optionalTextFormatting(attrs, "_authoredValues", "runFormattingOverride.attrs._authoredValues", issues);
1043
1075
  const authoredValues = attrs["_authoredValues"];
1044
1076
  if (isRecord(authoredValues)) {
1045
- const allowed = new Set(RUN_FORMATTING_VALUE_PROPERTIES);
1046
- for (const property of Object.keys(authoredValues)) if (!allowed.has(property)) issues.push({
1077
+ for (const property of Object.keys(authoredValues)) if (!RUN_FORMATTING_VALUE_PROPERTY_SET.has(property)) issues.push({
1047
1078
  path: `runFormattingOverride.attrs._authoredValues.${property}`,
1048
1079
  message: "Expected a non-boolean authored formatting property."
1049
1080
  });
@@ -2199,6 +2230,7 @@ const validateParagraphFormatting = (value, path, issues) => {
2199
2230
  ]);
2200
2231
  optionalOneOfArray(value, "listLevelNumFmts", `${path}.listLevelNumFmts`, issues, COUNTER_FORMAT_VALUES);
2201
2232
  optionalNumberArray(value, "listLevelStarts", `${path}.listLevelStarts`, issues);
2233
+ optionalTabStops(value, "listLevelTabs", `${path}.listLevelTabs`, issues);
2202
2234
  optionalAutospacingBase(value, `${path}._autospacingBase`, issues);
2203
2235
  for (const key of ["numPr", "numPrFromStyle"]) optionalParagraphNumbering(value, key, `${path}.${key}`, issues);
2204
2236
  const direction = value["direction"];
@@ -42,6 +42,7 @@ import { schema } from "../schema/index.js";
42
42
  import { PRESERVED_XML_LEVELS } from "../schema/nodes.js";
43
43
  import { parseShapeGeometryAdjustments } from "../shapeGeometryAdjustments.js";
44
44
  import { expectTextBoxAnchorAttrs } from "../textBoxAnchorAttrs.js";
45
+ import { textBoxHostParagraph } from "../textBoxHostParagraph.js";
45
46
  import { assertValidProseMirrorDocument } from "../validation.js";
46
47
  import { hasSinkChildren } from "./preservedSinkCarriers.js";
47
48
  import { runShadingAttrsToShading, shadingToRunShadingAttrs } from "./runShadingMark.js";
@@ -678,7 +679,7 @@ function isStillSyntheticFiller(blocks) {
678
679
  }
679
680
  function appendTextBoxBlock(blocks, node, options) {
680
681
  const attrs = expectTextBoxAttrs(node);
681
- const paragraph = convertPMTextBox(node, options.styleResolver);
682
+ let paragraph = convertPMTextBox(node, options.styleResolver);
682
683
  const previousBlock = blocks.at(-1);
683
684
  if (attrs._docxPlacement === "inlineWithPrevious" && previousBlock?.type === "paragraph") {
684
685
  appendPageBreaks(previousBlock, options.pendingPageBreaks);
@@ -696,6 +697,12 @@ function appendTextBoxBlock(blocks, node, options) {
696
697
  if (!mergeTextBoxIntoTrailingInlineSdts(options.previousStandaloneTextBox.paragraph, paragraph, attrs._docxInlineSdts ?? [])) options.previousStandaloneTextBox.paragraph.content.push(...paragraph.content);
697
698
  return options.previousStandaloneTextBox;
698
699
  }
700
+ const host = attrs._docxPlacement === "standalone" ? textBoxHostParagraph(node) : void 0;
701
+ if (host) {
702
+ const hostParagraph = convertPMParagraph(host, void 0, void 0, options.styleResolver);
703
+ hostParagraph.content = paragraph.content;
704
+ paragraph = hostParagraph;
705
+ }
699
706
  if (attrs._docxPlacement === "standalone" && attrs._preservedAttributes?.length) paragraph.preservedAttributes = attrs._preservedAttributes;
700
707
  prependPageBreaks(paragraph, options.pendingPageBreaks);
701
708
  blocks.push(paragraph);
@@ -0,0 +1,21 @@
1
+ import { MarkFactory } from "../extensions/marks/markUtils.js";
2
+ import { Schema } from "prosemirror-model";
3
+ //#region src/prosemirror/conversion/markInterner.d.ts
4
+ /**
5
+ * Build marks that share one instance per distinct (type, attrs) value.
6
+ *
7
+ * A document repeats a small set of run formattings across thousands of runs,
8
+ * so one conversion builds each distinct mark once. Marks are immutable, and
9
+ * ProseMirror compares them by value, so sharing an instance changes nothing a
10
+ * caller can observe except the saved allocation. Create one interner per
11
+ * conversion; it holds every mark it built until it is dropped.
12
+ *
13
+ * The native JSON serialization is only a lookup key: it collapses values the
14
+ * mark would keep apart (`-0`, `NaN`, nested `undefined`), so a hit is shared
15
+ * only after an exact comparison, and a mismatch builds an unshared mark. A
16
+ * mark holding `NaN` is never shared: ProseMirror does not find two of them
17
+ * equal, and a shared instance would.
18
+ */
19
+ declare const createMarkInterner: (schema: Schema) => MarkFactory;
20
+ //#endregion
21
+ export { createMarkInterner };
@@ -0,0 +1,90 @@
1
+ import { panic } from "better-result";
2
+ //#region src/prosemirror/conversion/markInterner.ts
3
+ /**
4
+ * Leaves ProseMirror compares with `===`, minus `-0`/`0`: sharing a mark makes
5
+ * `Mark.eq` take its identity shortcut, so two leaves may share only when
6
+ * ProseMirror would call them equal anyway. `NaN` never equals itself there.
7
+ */
8
+ const sameLeaf = (left, right) => left === right && Object.is(left, right);
9
+ /**
10
+ * Whether two attr values are interchangeable: `sameLeaf` on leaves, and the
11
+ * same own keys in the same order, so `-0`/`0`, `NaN` and `undefined`/absent
12
+ * stay apart. Output follows the shared value's key order, which is why order
13
+ * counts.
14
+ */
15
+ const sameAttrValue = (left, right) => {
16
+ if (sameLeaf(left, right)) return true;
17
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return false;
18
+ if (Object.getPrototypeOf(left) !== Object.getPrototypeOf(right)) return false;
19
+ if (Array.isArray(left)) {
20
+ if (!Array.isArray(right) || left.length !== right.length) return false;
21
+ for (let index = 0; index < left.length; index += 1) if (index in left !== index in right || !sameAttrValue(left[index], right[index])) return false;
22
+ return true;
23
+ }
24
+ const leftKeys = Object.keys(left);
25
+ const rightKeys = Object.keys(right);
26
+ return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && sameAttrValue(Reflect.get(left, key), Reflect.get(right, key)));
27
+ };
28
+ const internedMark = (type, attrs) => {
29
+ const mark = type.create(attrs);
30
+ const nonDefault = /* @__PURE__ */ new Set();
31
+ for (const [name, spec] of Object.entries(type.spec.attrs ?? {})) if (!sameLeaf(mark.attrs[name], spec.default)) nonDefault.add(name);
32
+ return {
33
+ mark,
34
+ nonDefault
35
+ };
36
+ };
37
+ /**
38
+ * Whether `create(attrs)` would compute attrs interchangeable with the
39
+ * interned mark's. `create` fills every declared attr `attrs` leaves
40
+ * `undefined` from its default, so the given attrs must match each value they
41
+ * set and set every attr where the mark holds something else.
42
+ */
43
+ const matchesInterned = (type, attrs, { mark, nonDefault }) => {
44
+ const specs = type.spec.attrs ?? {};
45
+ let covered = 0;
46
+ for (const name in attrs) {
47
+ const given = attrs[name];
48
+ if (given === void 0 || !Object.hasOwn(specs, name)) continue;
49
+ if (!sameAttrValue(given, mark.attrs[name])) return false;
50
+ if (nonDefault.has(name)) covered += 1;
51
+ }
52
+ return covered === nonDefault.size;
53
+ };
54
+ /**
55
+ * Build marks that share one instance per distinct (type, attrs) value.
56
+ *
57
+ * A document repeats a small set of run formattings across thousands of runs,
58
+ * so one conversion builds each distinct mark once. Marks are immutable, and
59
+ * ProseMirror compares them by value, so sharing an instance changes nothing a
60
+ * caller can observe except the saved allocation. Create one interner per
61
+ * conversion; it holds every mark it built until it is dropped.
62
+ *
63
+ * The native JSON serialization is only a lookup key: it collapses values the
64
+ * mark would keep apart (`-0`, `NaN`, nested `undefined`), so a hit is shared
65
+ * only after an exact comparison, and a mismatch builds an unshared mark. A
66
+ * mark holding `NaN` is never shared: ProseMirror does not find two of them
67
+ * equal, and a shared instance would.
68
+ */
69
+ const createMarkInterner = (schema) => {
70
+ const marksByType = /* @__PURE__ */ new Map();
71
+ return (typeName, attrs) => {
72
+ const type = schema.marks[typeName];
73
+ if (type === void 0) panic(`Unknown mark type ${typeName}.`);
74
+ let marks = marksByType.get(type);
75
+ if (marks === void 0) {
76
+ marks = /* @__PURE__ */ new Map();
77
+ marksByType.set(type, marks);
78
+ }
79
+ const key = JSON.stringify(attrs ?? null);
80
+ const cached = marks.get(key);
81
+ if (cached === void 0) {
82
+ const interned = internedMark(type, attrs);
83
+ marks.set(key, interned);
84
+ return interned.mark;
85
+ }
86
+ return matchesInterned(type, attrs, cached) ? cached.mark : type.create(attrs);
87
+ };
88
+ };
89
+ //#endregion
90
+ export { createMarkInterner };
@@ -2,7 +2,7 @@ import { rangedCommentIds } from "../../docx/commentAnchorIndex.js";
2
2
  import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
3
3
  import { isInlineSdtContent, isTrackedChangeWrapperChild } from "../../docx/inlineWrapperContent.js";
4
4
  import { mergeParagraphNumbering as mergeParagraphNumbering$1, paragraphNumberingReferenceId as paragraphNumberingReferenceId$1 } from "../../docx/numberingReference.js";
5
- import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
5
+ import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
6
6
  import { isBaselineVertAlign } from "../../docx/runParser.js";
7
7
  import { resolveTableLook } from "../../docx/tableLook.js";
8
8
  import { isCellMergeContinuation } from "../../docx/tableParser.js";
@@ -41,6 +41,7 @@ import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
41
41
  import { canCarryTrackedRunMark, trackedRunInlineAtomDisposition } from "../trackedRunInlineAtoms.js";
42
42
  import { assertValidProseMirrorDocument } from "../validation.js";
43
43
  import { resolveEffectiveTableCellFormatting } from "./effectiveTableCellFormatting.js";
44
+ import { createMarkInterner } from "./markInterner.js";
44
45
  import { hasSinkChildren } from "./preservedSinkCarriers.js";
45
46
  import { sdtAttrsFromProperties } from "./sdtAttrs.js";
46
47
  import { panic } from "better-result";
@@ -227,7 +228,8 @@ function toProseDoc(document, options) {
227
228
  pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(paragraphs),
228
229
  storyRangedCommentIds: rangedCommentIds(paragraphs),
229
230
  openCommentIds: /* @__PURE__ */ new Set(),
230
- warnPageBreakProjection: pageBreakProjectionWarn(options?.warn)
231
+ warnPageBreakProjection: pageBreakProjectionWarn(options?.warn),
232
+ createMark: createMarkInterner(schema)
231
233
  };
232
234
  const convertBodyBlocks = (blocks) => {
233
235
  const out = [];
@@ -259,10 +261,12 @@ function toProseDoc(document, options) {
259
261
  if (nodes.length === 0) nodes.push(schema.node("paragraph", {}, []));
260
262
  const finalSectionStart = document.package.document.sections?.at(-1)?.properties.sectionStart ?? null;
261
263
  const adjustLineHeightInTable = document.package.settings?.adjustLineHeightInTable === true;
264
+ const doNotUseIndentAsNumberingTabStop = document.package.settings?.doNotUseIndentAsNumberingTabStop === true;
262
265
  const pmDoc = stampNumberedRefFieldBaselines(schema.node("doc", {
263
266
  [PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR]: getDocumentParagraphPropertySourceContract(document) ?? null,
264
267
  _finalSectionStart: finalSectionStart,
265
- _adjustLineHeightInTable: adjustLineHeightInTable
268
+ _adjustLineHeightInTable: adjustLineHeightInTable,
269
+ _doNotUseIndentAsNumberingTabStop: doNotUseIndentAsNumberingTabStop
266
270
  }, nodes));
267
271
  assertValidProseMirrorDocument(pmDoc, "Document conversion produced an invalid ProseMirror document");
268
272
  return pmDoc;
@@ -355,7 +359,10 @@ function convertBlockSdt(blockSdt, convertBlocks) {
355
359
  function convertParagraph(paragraph, styleResolver, context, extraRunFormatting, tableParagraphOverlay, textBoxAnchors) {
356
360
  const { nextHyperlinkInstanceIndex, pairedBookmarkIds, pageBreakRunSourceDescendants, storyRangedCommentIds, openCommentIds } = context;
357
361
  let runIdentityId = 0;
358
- const nextRunIdentityId = () => runIdentityId++;
362
+ const runScope = {
363
+ nextRunIdentityId: () => runIdentityId++,
364
+ createMark: context.createMark
365
+ };
359
366
  const { attrs, effectiveFrame } = paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay);
360
367
  reportParagraphPageBreakProjection({
361
368
  paragraph,
@@ -452,7 +459,7 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
452
459
  historical: getHistoricalRunFormatting
453
460
  };
454
461
  const emitTrackedChange = (change, markType, moveKind) => {
455
- emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextRunIdentityId, trackedRunFormattingResolvers, styleResolver, moveKind, textBoxAnchors, wrapperStack));
462
+ emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, runScope, trackedRunFormattingResolvers, styleResolver, moveKind, textBoxAnchors, wrapperStack));
456
463
  };
457
464
  const stacked = withInlineWrapperStacks(paragraph.content);
458
465
  const emptyRanges = planEmptyRanges(stacked.map((item) => item.content));
@@ -476,7 +483,7 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
476
483
  emitInlineNode(schema.node("commentReference", { commentId: content.id }));
477
484
  break;
478
485
  case "run":
479
- emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), nextRunIdentityId, styleResolver, textBoxAnchors));
486
+ emitInlineNodes(convertRun(content, getInheritedRunFormatting(content.formatting), runScope, styleResolver, textBoxAnchors));
480
487
  break;
481
488
  case "hyperlink": {
482
489
  const linkNodes = convertHyperlink(content, {
@@ -484,7 +491,7 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
484
491
  styleResolver,
485
492
  hyperlinkIndex: nextHyperlinkInstanceIndex(),
486
493
  textBoxAnchors,
487
- nextRunIdentityId
494
+ runScope
488
495
  });
489
496
  if (linkNodes.length === 0) {
490
497
  const emptyWrapperStacks = emptyInlineWrapperStacks(content.children);
@@ -511,12 +518,12 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
511
518
  getInheritedRunFormatting,
512
519
  styleResolver,
513
520
  nextHyperlinkInstanceIndex,
514
- nextRunIdentityId,
521
+ runScope,
515
522
  textBoxAnchors
516
523
  }));
517
524
  break;
518
525
  case "inlineSdt":
519
- emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, nextRunIdentityId, getInheritedRunFormatting, trackedRunFormattingResolvers, styleResolver, textBoxAnchors, stack));
526
+ emitInlineNode(convertInlineSdt(content, nextHyperlinkInstanceIndex, runScope, getInheritedRunFormatting, trackedRunFormattingResolvers, styleResolver, textBoxAnchors, stack));
520
527
  break;
521
528
  case "insertion":
522
529
  case "moveTo":
@@ -630,13 +637,13 @@ const withTrackedRunMark = (node, mark, ancestor) => {
630
637
  * Convert tracked change (insertion or deletion) content to PM nodes with
631
638
  * an insertion/deletion mark applied.
632
639
  */
633
- function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, nextRunIdentityId, runFormattingResolvers, styleResolver, moveKind = null, textBoxAnchors, wrappedBy = []) {
640
+ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, runScope, runFormattingResolvers, styleResolver, moveKind = null, textBoxAnchors, wrappedBy = []) {
634
641
  const nodes = [];
635
642
  const getTrackedRunFormatting = markType === "deletion" ? runFormattingResolvers.historical : runFormattingResolvers.current;
636
643
  for (const { content: item, stack } of withInlineWrapperStacks(change.content)) {
637
644
  if (!isTrackedChangeWrapperChild(item)) continue;
638
645
  const itemNodes = [];
639
- if (item.type === "run") itemNodes.push(...convertRun(item, getTrackedRunFormatting(item.formatting), nextRunIdentityId, styleResolver, textBoxAnchors));
646
+ if (item.type === "run") itemNodes.push(...convertRun(item, getTrackedRunFormatting(item.formatting), runScope, styleResolver, textBoxAnchors));
640
647
  else if (item.type === "hyperlink") {
641
648
  const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
642
649
  itemNodes.push(...convertHyperlink(item, {
@@ -644,14 +651,14 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, next
644
651
  styleResolver,
645
652
  hyperlinkIndex: currentHyperlinkIndex,
646
653
  textBoxAnchors,
647
- nextRunIdentityId
654
+ runScope
648
655
  }));
649
656
  } else if (item.type === "simpleField" || item.type === "complexField") {
650
657
  const fieldNode = convertField(item, {
651
658
  getInheritedRunFormatting: getTrackedRunFormatting,
652
659
  styleResolver,
653
660
  nextHyperlinkInstanceIndex,
654
- nextRunIdentityId,
661
+ runScope,
655
662
  textBoxAnchors
656
663
  });
657
664
  if (fieldNode) itemNodes.push(fieldNode);
@@ -661,9 +668,9 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, next
661
668
  } else if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") {
662
669
  const nestedMarkType = item.type === "insertion" || item.type === "moveTo" ? "insertion" : "deletion";
663
670
  const nestedMoveKind = item.type === "moveFrom" || item.type === "moveTo" ? item.type : null;
664
- itemNodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, nextRunIdentityId, runFormattingResolvers, styleResolver, nestedMoveKind, textBoxAnchors, [...wrappedBy, ...stack]));
671
+ itemNodes.push(...convertTrackedChange(item, nestedMarkType, nextHyperlinkInstanceIndex, runScope, runFormattingResolvers, styleResolver, nestedMoveKind, textBoxAnchors, [...wrappedBy, ...stack]));
665
672
  } else if (item.type === "inlineSdt") {
666
- const sdtNode = convertInlineSdt(item, nextHyperlinkInstanceIndex, nextRunIdentityId, getTrackedRunFormatting, runFormattingResolvers, styleResolver, textBoxAnchors, [...wrappedBy, ...stack]);
673
+ const sdtNode = convertInlineSdt(item, nextHyperlinkInstanceIndex, runScope, getTrackedRunFormatting, runFormattingResolvers, styleResolver, textBoxAnchors, [...wrappedBy, ...stack]);
667
674
  if (sdtNode) itemNodes.push(sdtNode);
668
675
  } else if (item.type === "bookmarkStart") itemNodes.push(schema.node("bookmarkBoundary", {
669
676
  type: "start",
@@ -1080,8 +1087,7 @@ function convertTable(table, styleResolver, context) {
1080
1087
  const tableStyleId = table.formatting?.styleId;
1081
1088
  const look = resolveTableLook(table.formatting?.look);
1082
1089
  const tableStyle = tableStyleId ? styleResolver?.getStyle(tableStyleId) : void 0;
1083
- const defaultTableStyle = styleResolver?.getDefaultTableStyle();
1084
- const fallbackTableStyle = tableStyleId ? void 0 : defaultTableStyle;
1090
+ const fallbackTableStyle = tableStyle ? void 0 : styleResolver?.getDefaultTableStyle();
1085
1091
  const conditionalTableStyleId = tableStyle?.styleId ?? fallbackTableStyle?.styleId;
1086
1092
  const resolvedTableBorders = table.formatting?.borders ?? tableStyle?.tblPr?.borders ?? fallbackTableStyle?.tblPr?.borders;
1087
1093
  const fallbackTableIndent = fallbackTableStyle?.tblPr?.indent;
@@ -1466,7 +1472,8 @@ function standaloneTableCellToProseMirror(cell, nodeType) {
1466
1472
  pageBreakRunSourceDescendants,
1467
1473
  storyRangedCommentIds: rangedCommentIds(cell.content),
1468
1474
  openCommentIds: /* @__PURE__ */ new Set(),
1469
- warnPageBreakProjection: noPageBreakProjectionWarning
1475
+ warnPageBreakProjection: noPageBreakProjectionWarning,
1476
+ createMark: createMarkInterner(schema)
1470
1477
  },
1471
1478
  isHeader: nodeType === "tableHeader",
1472
1479
  gridWidthPercent: void 0,
@@ -1479,7 +1486,7 @@ function standaloneTableCellToProseMirror(cell, nodeType) {
1479
1486
  defaultCellMargins: void 0
1480
1487
  });
1481
1488
  }
1482
- function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyperlinkInstanceIndex, nextRunIdentityId, textBoxAnchors }) {
1489
+ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyperlinkInstanceIndex, runScope, textBoxAnchors }) {
1483
1490
  let displayText = "";
1484
1491
  let fieldFormatting;
1485
1492
  let fieldPropertyChanges;
@@ -1490,7 +1497,7 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1490
1497
  fieldFormatting ??= run.formatting;
1491
1498
  fieldPropertyChanges ??= run.propertyChanges;
1492
1499
  if (!hasStructuredSourceContent) return;
1493
- into.push(...convertRun(run, getInheritedRunFormatting(run.formatting, field.fieldType), nextRunIdentityId, styleResolver, textBoxAnchors));
1500
+ into.push(...convertRun(run, getInheritedRunFormatting(run.formatting, field.fieldType), runScope, styleResolver, textBoxAnchors));
1494
1501
  };
1495
1502
  if (field.type === "simpleField") for (const { content, stack } of withInlineWrapperStacks(field.content)) {
1496
1503
  const itemNodes = [];
@@ -1512,7 +1519,7 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1512
1519
  styleResolver,
1513
1520
  hyperlinkIndex: nextHyperlinkInstanceIndex(),
1514
1521
  textBoxAnchors,
1515
- nextRunIdentityId
1522
+ runScope
1516
1523
  }));
1517
1524
  break;
1518
1525
  default: break;
@@ -1522,7 +1529,12 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1522
1529
  else for (const run of field.fieldResult) appendRun(run, inlineNodes);
1523
1530
  if (!fieldFormatting && field.type === "complexField" && field.fieldResult.length === 0) fieldFormatting = field.formatting;
1524
1531
  const inheritedFormatting = getInheritedRunFormatting(fieldFormatting, field.fieldType);
1525
- const { marks } = buildRunMarks(fieldFormatting, inheritedFormatting, styleResolver);
1532
+ const { marks } = buildRunMarks({
1533
+ runFormatting: fieldFormatting,
1534
+ inherited: inheritedFormatting,
1535
+ styleResolver,
1536
+ createMark: runScope.createMark
1537
+ });
1526
1538
  const hasConvertedHyperlinkContent = inlineNodes.some((node) => node.marks.some((mark) => mark.type.name === "hyperlink"));
1527
1539
  const hasConvertedPageBreakContent = inlineNodes.some((node) => node.type.name === "pageBreakRun");
1528
1540
  const hasConvertedPreservedContent = inlineNodes.some((node) => node.type.name === "preservedXml");
@@ -1602,7 +1614,7 @@ function convertMathEquation(math) {
1602
1614
  * its own leaves, so nothing outside can mark them afterwards: the caller's
1603
1615
  * stack has to reach them here, under the wrappers written inside the control.
1604
1616
  */
1605
- function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextRunIdentityId, getInheritedRunFormatting, trackedRunFormattingResolvers, styleResolver, textBoxAnchors, wrappedBy = []) {
1617
+ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, runScope, getInheritedRunFormatting, trackedRunFormattingResolvers, styleResolver, textBoxAnchors, wrappedBy = []) {
1606
1618
  const props = sdt.properties;
1607
1619
  const inlineNodes = [];
1608
1620
  const stacked = withInlineWrapperStacks(sdt.content, wrappedBy);
@@ -1618,7 +1630,7 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextRunIdentityId, ge
1618
1630
  if (emptyRanges.closedAt.has(index)) continue;
1619
1631
  switch (content.type) {
1620
1632
  case "run":
1621
- itemNodes.push(...convertRun(content, getInheritedRunFormatting(content.formatting), nextRunIdentityId, styleResolver, textBoxAnchors));
1633
+ itemNodes.push(...convertRun(content, getInheritedRunFormatting(content.formatting), runScope, styleResolver, textBoxAnchors));
1622
1634
  break;
1623
1635
  case "hyperlink": {
1624
1636
  const currentHyperlinkIndex = nextHyperlinkInstanceIndex();
@@ -1627,7 +1639,7 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextRunIdentityId, ge
1627
1639
  styleResolver,
1628
1640
  hyperlinkIndex: currentHyperlinkIndex,
1629
1641
  textBoxAnchors,
1630
- nextRunIdentityId
1642
+ runScope
1631
1643
  }));
1632
1644
  break;
1633
1645
  }
@@ -1637,14 +1649,14 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextRunIdentityId, ge
1637
1649
  getInheritedRunFormatting,
1638
1650
  styleResolver,
1639
1651
  nextHyperlinkInstanceIndex,
1640
- nextRunIdentityId,
1652
+ runScope,
1641
1653
  textBoxAnchors
1642
1654
  });
1643
1655
  if (fieldNode) itemNodes.push(fieldNode);
1644
1656
  break;
1645
1657
  }
1646
1658
  case "inlineSdt": {
1647
- const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, nextRunIdentityId, getInheritedRunFormatting, trackedRunFormattingResolvers, styleResolver, textBoxAnchors, stack);
1659
+ const nestedSdt = convertInlineSdt(content, nextHyperlinkInstanceIndex, runScope, getInheritedRunFormatting, trackedRunFormattingResolvers, styleResolver, textBoxAnchors, stack);
1648
1660
  if (nestedSdt) itemNodes.push(nestedSdt);
1649
1661
  break;
1650
1662
  }
@@ -1652,7 +1664,7 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextRunIdentityId, ge
1652
1664
  case "deletion":
1653
1665
  case "moveTo":
1654
1666
  case "moveFrom":
1655
- itemNodes.push(...convertTrackedChange(content, content.type === "insertion" || content.type === "moveTo" ? "insertion" : "deletion", nextHyperlinkInstanceIndex, nextRunIdentityId, trackedRunFormattingResolvers, styleResolver, content.type === "moveTo" || content.type === "moveFrom" ? content.type : null, textBoxAnchors));
1667
+ itemNodes.push(...convertTrackedChange(content, content.type === "insertion" || content.type === "moveTo" ? "insertion" : "deletion", nextHyperlinkInstanceIndex, runScope, trackedRunFormattingResolvers, styleResolver, content.type === "moveTo" || content.type === "moveFrom" ? content.type : null, textBoxAnchors));
1656
1668
  break;
1657
1669
  case "mathEquation": {
1658
1670
  const mathNode = convertMathEquation(content);
@@ -1697,11 +1709,16 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, nextRunIdentityId, ge
1697
1709
  * @param run - The run to convert
1698
1710
  * @param resolvedStyleFormatting - Paragraph formatting and its explicit style-toggle state
1699
1711
  */
1700
- function convertRun(run, resolvedStyleFormatting, nextRunIdentityId, styleResolver, textBoxAnchors) {
1712
+ function convertRun(run, resolvedStyleFormatting, runScope, styleResolver, textBoxAnchors) {
1701
1713
  const nodes = [];
1702
- const { marks, mergedFormatting } = buildRunMarks(run.formatting, resolvedStyleFormatting, styleResolver);
1714
+ const { marks, mergedFormatting } = buildRunMarks({
1715
+ runFormatting: run.formatting,
1716
+ inherited: resolvedStyleFormatting,
1717
+ styleResolver,
1718
+ createMark: runScope.createMark
1719
+ });
1703
1720
  if (run.propertyChanges && run.propertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...run.propertyChanges] }));
1704
- const identity = runIdentityMark(run, nextRunIdentityId);
1721
+ const identity = runIdentityMark(run, runScope.nextRunIdentityId);
1705
1722
  if (identity) marks.push(identity);
1706
1723
  for (const content of run.content) {
1707
1724
  const contentNodes = convertRunContent(content, marks, mergedFormatting, textBoxAnchors, run.formatting);
@@ -2015,12 +2032,13 @@ const canReconstructAuthoredRunFormatting = ({ directFormatting, effectiveFormat
2015
2032
  }
2016
2033
  return true;
2017
2034
  };
2018
- function buildRunMarks(runFormatting, inherited, styleResolver) {
2035
+ function buildRunMarks({ runFormatting, inherited, styleResolver, createMark }) {
2019
2036
  if (runFormatting === void 0 && inherited.implicitCharacterStyleApplied === true && inherited.paragraphMarkOverrides === void 0) return {
2020
2037
  marks: textFormattingToMarks(inherited.formatting, schema, {
2021
2038
  overrideFormatting: void 0,
2022
2039
  directFormatting: void 0,
2023
- authoredCarrier: "reconstruct"
2040
+ authoredCarrier: "reconstruct",
2041
+ createMark
2024
2042
  }),
2025
2043
  mergedFormatting: inherited.formatting
2026
2044
  };
@@ -2064,9 +2082,10 @@ function buildRunMarks(runFormatting, inherited, styleResolver) {
2064
2082
  const marks = textFormattingToMarks(mergedFormatting, schema, {
2065
2083
  overrideFormatting,
2066
2084
  directFormatting: runFormatting,
2067
- authoredCarrier
2085
+ authoredCarrier,
2086
+ createMark
2068
2087
  });
2069
- if (styleId) marks.push(schema.mark("characterStyle", { styleId }));
2088
+ if (styleId) marks.push(createMark("characterStyle", { styleId }));
2070
2089
  return {
2071
2090
  marks,
2072
2091
  mergedFormatting
@@ -2334,7 +2353,7 @@ function convertImage({ image, rawXml, rawXmlMode, rawImageFingerprint, runForma
2334
2353
  _docxRunFormatting: runFormatting
2335
2354
  });
2336
2355
  }
2337
- function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver, hyperlinkIndex, textBoxAnchors, nextRunIdentityId }) {
2356
+ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver, hyperlinkIndex, textBoxAnchors, runScope }) {
2338
2357
  const nodes = [];
2339
2358
  const href = hyperlink.href || (hyperlink.anchor ? `#${hyperlink.anchor}` : "");
2340
2359
  const linkMark = schema.mark("hyperlink", {
@@ -2371,9 +2390,14 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
2371
2390
  break;
2372
2391
  case "run": {
2373
2392
  const inheritedFormatting = getInheritedRunFormatting(child.formatting);
2374
- const { marks: runMarks, mergedFormatting } = buildRunMarks(child.formatting, inheritedFormatting, styleResolver);
2393
+ const { marks: runMarks, mergedFormatting } = buildRunMarks({
2394
+ runFormatting: child.formatting,
2395
+ inherited: inheritedFormatting,
2396
+ styleResolver,
2397
+ createMark: runScope.createMark
2398
+ });
2375
2399
  if (child.propertyChanges && child.propertyChanges.length > 0) runMarks.push(schema.mark("runPropertyChange", { changes: [...child.propertyChanges] }));
2376
- const identity = runIdentityMark(child, nextRunIdentityId);
2400
+ const identity = runIdentityMark(child, runScope.nextRunIdentityId);
2377
2401
  if (identity) runMarks.push(identity);
2378
2402
  const allMarks = [...runMarks, linkMark];
2379
2403
  for (const content of child.content) childNodes.push(...convertRunContent(content, allMarks, mergedFormatting, textBoxAnchors, child.formatting));
@@ -2518,10 +2542,20 @@ function convertParagraphWithTextBoxes(block, styleResolver, { textBoxGroupId, c
2518
2542
  context,
2519
2543
  trackedChange,
2520
2544
  inlineSdts,
2521
- ...standalone && index === 0 && block.preservedAttributes ? { hostPreservedAttributes: block.preservedAttributes } : {}
2545
+ ...standalone && index === 0 && block.preservedAttributes ? { hostPreservedAttributes: block.preservedAttributes } : {},
2546
+ ...standalone && index === 0 ? { hostParagraph: hostParagraphAttrs(pmParagraph.attrs) } : {}
2522
2547
  }));
2523
2548
  return nodes;
2524
2549
  }
2550
+ /**
2551
+ * The host paragraph's attributes without its identity. The node may be
2552
+ * copied, and a copy must not make a second claim on the same `w:p`; the
2553
+ * attribute remainder travels in the node's own `_preservedAttributes`.
2554
+ */
2555
+ function hostParagraphAttrs(attrs) {
2556
+ const { paraId: _paraId, textId: _textId, idStability: _idStability, _preservedAttributes, ...host } = proseParagraphAttrsWithoutPropertySource(attrs);
2557
+ return host;
2558
+ }
2525
2559
  function hasContentBesidesTextBoxAnchors(paragraph) {
2526
2560
  let hasContent = false;
2527
2561
  paragraph.descendants((node) => {
@@ -2785,7 +2819,8 @@ function convertTextBox(textBox, styleResolver, options) {
2785
2819
  _docxTextBodyContentState: textBox.content.length === 0 ? { type: "source-empty" } : { type: "authored" },
2786
2820
  _docxTrackedChange: options.trackedChange,
2787
2821
  _docxInlineSdts: options.inlineSdts.length > 0 ? options.inlineSdts : void 0,
2788
- _preservedAttributes: options.hostPreservedAttributes
2822
+ _preservedAttributes: options.hostPreservedAttributes,
2823
+ _docxHostParagraph: options.hostParagraph
2789
2824
  }, contentNodes);
2790
2825
  }
2791
2826
  /**
@@ -2809,7 +2844,8 @@ function headerFooterToProseDoc(content, options) {
2809
2844
  pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(content),
2810
2845
  storyRangedCommentIds: rangedCommentIds(content),
2811
2846
  openCommentIds: /* @__PURE__ */ new Set(),
2812
- warnPageBreakProjection: pageBreakProjectionWarn(options?.warn)
2847
+ warnPageBreakProjection: pageBreakProjectionWarn(options?.warn),
2848
+ createMark: createMarkInterner(schema)
2813
2849
  };
2814
2850
  const convertBlocks = (blocks) => {
2815
2851
  const out = [];
@@ -11,7 +11,8 @@ const DocExtension = createNodeExtension({
11
11
  attrs: {
12
12
  [PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR]: { default: null },
13
13
  _finalSectionStart: { default: null },
14
- _adjustLineHeightInTable: { default: false }
14
+ _adjustLineHeightInTable: { default: false },
15
+ _doNotUseIndentAsNumberingTabStop: { default: false }
15
16
  },
16
17
  content: "(paragraph | horizontalRule | pageBreak | table | textBox | blockSdt | preservedBlock | blockBookmarkBoundary)+"
17
18
  }