@stll/folio-core 0.47.7 → 0.49.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 (213) hide show
  1. package/dist/ai-edits/apply.js +565 -121
  2. package/dist/ai-edits/minimal-replacement.d.ts +64 -0
  3. package/dist/ai-edits/minimal-replacement.js +282 -0
  4. package/dist/controller/fontReadiness.js +3 -3
  5. package/dist/controller/hyphenationReadiness.d.ts +19 -0
  6. package/dist/controller/hyphenationReadiness.js +47 -0
  7. package/dist/controller/layoutPipeline.d.ts +3 -0
  8. package/dist/controller/layoutPipeline.js +70 -18
  9. package/dist/display-list/build/buildDisplayList.js +39 -12
  10. package/dist/display-list/build/pageFurniture.d.ts +7 -1
  11. package/dist/display-list/build/pageFurniture.js +21 -8
  12. package/dist/display-list/build/paragraphPrimitives.js +68 -11
  13. package/dist/display-list/build/tablePrimitives.js +10 -6
  14. package/dist/display-list/build/textBoxPrimitives.js +1 -1
  15. package/dist/display-list/build/unsupported.d.ts +2 -2
  16. package/dist/display-list/build/unsupported.js +0 -0
  17. package/dist/display-list/dom/renderDisplayListToDom.js +0 -1
  18. package/dist/docx/attributeRemainder.js +10 -2
  19. package/dist/docx/blockContentParser.d.ts +2 -0
  20. package/dist/docx/blockContentParser.js +79 -64
  21. package/dist/docx/bulletMarkers.d.ts +18 -2
  22. package/dist/docx/bulletMarkers.js +27 -2
  23. package/dist/docx/commentParser.d.ts +2 -1
  24. package/dist/docx/commentParser.js +57 -45
  25. package/dist/docx/containerChildren.d.ts +21 -8
  26. package/dist/docx/containerChildren.js +39 -9
  27. package/dist/docx/documentParser.d.ts +5 -1
  28. package/dist/docx/documentParser.js +8 -5
  29. package/dist/docx/documentSectionFacts.d.ts +22 -0
  30. package/dist/docx/documentSectionFacts.js +43 -0
  31. package/dist/docx/drawingGroupChildren.d.ts +60 -0
  32. package/dist/docx/drawingGroupChildren.js +176 -0
  33. package/dist/docx/drawingUtils.js +3 -0
  34. package/dist/docx/fieldParser.d.ts +8 -1
  35. package/dist/docx/fieldParser.js +47 -3
  36. package/dist/docx/fontTableParser.js +34 -27
  37. package/dist/docx/footnoteParser.d.ts +3 -2
  38. package/dist/docx/footnoteParser.js +15 -12
  39. package/dist/docx/groupDrawingParser.d.ts +9 -2
  40. package/dist/docx/groupDrawingParser.js +125 -37
  41. package/dist/docx/headerFooterParser.d.ts +7 -2
  42. package/dist/docx/headerFooterParser.js +15 -5
  43. package/dist/docx/hyperlinkParser.d.ts +48 -36
  44. package/dist/docx/hyperlinkParser.js +97 -82
  45. package/dist/docx/numberingParser.js +108 -84
  46. package/dist/docx/packageParts.d.ts +45 -1
  47. package/dist/docx/packageParts.js +93 -2
  48. package/dist/docx/paraIdAttribute.d.ts +3 -2
  49. package/dist/docx/paragraphParser.d.ts +5 -1
  50. package/dist/docx/paragraphParser.js +468 -365
  51. package/dist/docx/paragraphProperties.js +123 -109
  52. package/dist/docx/paragraphPropertySource.d.ts +6 -1
  53. package/dist/docx/paragraphPropertySource.js +9 -1
  54. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  55. package/dist/docx/paragraphTextBoxEnrichment.js +143 -10
  56. package/dist/docx/parser.d.ts +7 -1
  57. package/dist/docx/parser.js +33 -16
  58. package/dist/docx/previewBudget.d.ts +55 -33
  59. package/dist/docx/previewBudget.js +59 -65
  60. package/dist/docx/rezip.js +41 -58
  61. package/dist/docx/runParser.d.ts +4 -1
  62. package/dist/docx/runParser.js +182 -184
  63. package/dist/docx/sdtProperties.js +87 -84
  64. package/dist/docx/sectionParser.js +169 -160
  65. package/dist/docx/selectiveSaveFlags.d.ts +10 -3
  66. package/dist/docx/selectiveSaveFlags.js +1 -1
  67. package/dist/docx/selectiveXmlPatch.d.ts +56 -34
  68. package/dist/docx/selectiveXmlPatch.js +222 -115
  69. package/dist/docx/serializer/groupTextBoxWriteBack.d.ts +12 -0
  70. package/dist/docx/serializer/groupTextBoxWriteBack.js +101 -0
  71. package/dist/docx/serializer/paragraphSerializer.js +47 -42
  72. package/dist/docx/serializer/runSerializer.d.ts +5 -1
  73. package/dist/docx/serializer/runSerializer.js +4 -3
  74. package/dist/docx/serializer/settingsSerializer.js +19 -1
  75. package/dist/docx/settingsParser.js +8 -0
  76. package/dist/docx/shapeAlternateContent.d.ts +25 -0
  77. package/dist/docx/shapeAlternateContent.js +52 -0
  78. package/dist/docx/streamingXmlParser.d.ts +6 -2
  79. package/dist/docx/streamingXmlParser.js +12 -7
  80. package/dist/docx/styleParser.js +34 -8
  81. package/dist/docx/tableParser.d.ts +21 -12
  82. package/dist/docx/tableParser.js +518 -441
  83. package/dist/docx/textBoxParser.d.ts +18 -5
  84. package/dist/docx/textBoxParser.js +12 -6
  85. package/dist/docx/verbatimCapture.d.ts +10 -8
  86. package/dist/docx/verbatimCapture.js +45 -2
  87. package/dist/docx/vmlImageParser.d.ts +15 -13
  88. package/dist/docx/vmlImageParser.js +75 -60
  89. package/dist/docx/vmlPreview.d.ts +3 -3
  90. package/dist/docx/vmlPreview.js +3 -4
  91. package/dist/docx/xmlNamespaceContext.d.ts +9 -0
  92. package/dist/docx/xmlNamespaceContext.js +54 -0
  93. package/dist/docx/xmlParser.d.ts +6 -3
  94. package/dist/docx/xmlParser.js +11 -41
  95. package/dist/docx/xmlResourceLimits.d.ts +2 -1
  96. package/dist/docx/xmlResourceLimits.js +4 -1
  97. package/dist/fonts/headlessMeasure.js +0 -0
  98. package/dist/headless-layout.d.ts +11 -2
  99. package/dist/headless-layout.js +153 -107
  100. package/dist/internal/headlessRevisionResolution.js +50 -13
  101. package/dist/internal/paragraphFormattingSerialization.js +3 -3
  102. package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
  103. package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
  104. package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
  105. package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
  106. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
  107. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
  108. package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
  109. package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
  110. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
  111. package/dist/layout-bridge/convert/toFlowBlocks.js +284 -39
  112. package/dist/layout-bridge/engine/hitTest.js +5 -3
  113. package/dist/layout-bridge/engine/selectionRects.js +5 -3
  114. package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
  115. package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
  116. package/dist/layout-engine/index.d.ts +2 -2
  117. package/dist/layout-engine/index.js +102 -14
  118. package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
  119. package/dist/layout-engine/justificationCompatibility.js +9 -5
  120. package/dist/layout-engine/keep-together.d.ts +15 -6
  121. package/dist/layout-engine/keep-together.js +43 -16
  122. package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
  123. package/dist/layout-engine/layoutInstrumentation.js +8 -1
  124. package/dist/layout-engine/measure/advanceComposition.js +50 -7
  125. package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
  126. package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
  127. package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
  128. package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
  129. package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
  130. package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
  131. package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
  132. package/dist/layout-engine/measure/lineBreaks.js +10 -3
  133. package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
  134. package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
  135. package/dist/layout-engine/measure/measureBlocks.js +46 -10
  136. package/dist/layout-engine/measure/measureContainer.js +51 -8
  137. package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
  138. package/dist/layout-engine/measure/measureHelpers.js +34 -8
  139. package/dist/layout-engine/measure/measureParagraph.js +143 -101
  140. package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
  141. package/dist/layout-engine/measure/smallCapsCasing.d.ts +56 -0
  142. package/dist/layout-engine/measure/smallCapsCasing.js +88 -0
  143. package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
  144. package/dist/layout-engine/measure/tabCalculator.js +2 -1
  145. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  146. package/dist/layout-engine/measure/tableCellFloating.js +51 -19
  147. package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
  148. package/dist/layout-engine/measure/tableCellGrid.js +45 -5
  149. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
  150. package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
  151. package/dist/layout-engine/noteAreaFlow.js +83 -0
  152. package/dist/layout-engine/paginator.d.ts +12 -1
  153. package/dist/layout-engine/paginator.js +18 -2
  154. package/dist/layout-engine/paragraphSpacing.d.ts +4 -2
  155. package/dist/layout-engine/paragraphSpacing.js +2 -1
  156. package/dist/layout-engine/tableRowBreak.d.ts +8 -1
  157. package/dist/layout-engine/tableRowBreak.js +25 -2
  158. package/dist/layout-engine/types.d.ts +108 -3
  159. package/dist/layout-engine/types.js +96 -1
  160. package/dist/layout-painter/index.d.ts +3 -0
  161. package/dist/layout-painter/renderPage.js +32 -83
  162. package/dist/layout-painter/renderParagraph.d.ts +6 -1
  163. package/dist/layout-painter/renderParagraph.js +75 -26
  164. package/dist/layout-painter/renderTable.js +26 -13
  165. package/dist/layout-painter/renderTextBox.d.ts +12 -2
  166. package/dist/layout-painter/renderTextBox.js +26 -2
  167. package/dist/prosemirror/alternateContentAttrs.d.ts +9 -0
  168. package/dist/prosemirror/alternateContentAttrs.js +35 -0
  169. package/dist/prosemirror/attrs/index.d.ts +9 -9
  170. package/dist/prosemirror/attrs/index.js +133 -11
  171. package/dist/prosemirror/commands/comments.js +51 -41
  172. package/dist/prosemirror/contentControlRevisions.d.ts +43 -0
  173. package/dist/prosemirror/contentControlRevisions.js +84 -0
  174. package/dist/prosemirror/conversion/fromProseDoc.js +54 -23
  175. package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
  176. package/dist/prosemirror/conversion/markInterner.js +90 -0
  177. package/dist/prosemirror/conversion/toProseDoc.js +117 -58
  178. package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
  179. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  180. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.d.ts +7 -7
  181. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -7
  182. package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
  183. package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
  184. package/dist/prosemirror/extensions/nodes/FieldExtension.js +6 -3
  185. package/dist/prosemirror/extensions/nodes/SdtExtension.js +9 -2
  186. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +1 -0
  187. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +5 -1
  188. package/dist/prosemirror/listMarker.d.ts +1 -0
  189. package/dist/prosemirror/listMarker.js +4 -2
  190. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  191. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  192. package/dist/prosemirror/paragraphDirection.d.ts +29 -2
  193. package/dist/prosemirror/paragraphDirection.js +21 -2
  194. package/dist/prosemirror/plugins/suggestionMode.js +37 -8
  195. package/dist/prosemirror/rejoinRunCarriers.d.ts +27 -0
  196. package/dist/prosemirror/rejoinRunCarriers.js +66 -0
  197. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +10 -0
  198. package/dist/prosemirror/runIdentityAcrossRevisions.js +90 -0
  199. package/dist/prosemirror/schema/nodes.d.ts +50 -6
  200. package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
  201. package/dist/prosemirror/textBoxHostParagraph.js +21 -0
  202. package/dist/prosemirror/validation.js +91 -80
  203. package/dist/types/content.d.ts +2 -2
  204. package/dist/utils/createDocument.js +10 -1
  205. package/dist/utils/scriptSegments.d.ts +33 -4
  206. package/dist/utils/scriptSegments.js +39 -6
  207. package/dist/utils/textFormattingMerge.d.ts +17 -3
  208. package/dist/utils/textFormattingMerge.js +18 -12
  209. package/dist/utils/trailingText.d.ts +22 -0
  210. package/dist/utils/trailingText.js +32 -0
  211. package/package.json +2 -2
  212. package/dist/layout-engine/justifiedLineFit.d.ts +0 -7
  213. package/dist/layout-engine/justifiedLineFit.js +0 -6
@@ -375,23 +375,16 @@ function getMarkAttr(state, markType, attr) {
375
375
  });
376
376
  return value;
377
377
  }
378
- const addDirectFontProvenance = (marks, schema, directFormatting) => {
378
+ const withDirectFontProvenance = (attrs, directFormatting) => {
379
379
  const directFontProperties = [];
380
380
  if (directFormatting?.fontFamily !== void 0) directFontProperties.push("fontFamily");
381
381
  if (directFormatting?.fontSize !== void 0) directFontProperties.push("fontSize");
382
382
  if (directFormatting?.color !== void 0) directFontProperties.push("color");
383
- if (directFontProperties.length === 0) return;
384
- const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
385
- const existing = index >= 0 ? marks.at(index) : void 0;
386
- const override = schema.mark("runFormattingOverride", {
387
- ...existing?.attrs,
383
+ if (directFontProperties.length === 0) return attrs;
384
+ return {
385
+ ...attrs,
388
386
  directFontProperties
389
- });
390
- if (index >= 0) {
391
- marks[index] = override;
392
- return;
393
- }
394
- marks.push(override);
387
+ };
395
388
  };
396
389
  const COMPLEX_SCRIPT_MIRRORS = [
397
390
  {
@@ -407,25 +400,19 @@ const COMPLEX_SCRIPT_MIRRORS = [
407
400
  complex: "fontSizeCs"
408
401
  }
409
402
  ];
410
- const addComplexScriptAbsenceProvenance = (marks, schema, directFormatting) => {
403
+ const withComplexScriptAbsenceProvenance = (attrs, directFormatting) => {
411
404
  const absent = COMPLEX_SCRIPT_MIRRORS.filter(({ ordinary, complex }) => directFormatting?.[ordinary] !== void 0 && directFormatting[complex] === void 0).map(({ complex }) => complex);
412
- if (absent.length === 0) return;
413
- const index = marks.findIndex(({ type }) => type.name === "runFormattingOverride");
414
- const existing = index >= 0 ? marks.at(index) : void 0;
415
- const existingAbsences = new Set(existing?.attrs["complexScriptPropertyAbsences"] ?? []);
405
+ if (absent.length === 0) return attrs;
406
+ const existingAbsences = new Set(attrs?.complexScriptPropertyAbsences ?? []);
416
407
  for (const property of absent) existingAbsences.add(property);
417
- const override = schema.mark("runFormattingOverride", {
418
- ...existing?.attrs,
408
+ return {
409
+ ...attrs,
419
410
  complexScriptPropertyAbsences: COMPLEX_SCRIPT_RUN_PROPERTY_KEYS.filter((property) => existingAbsences.has(property))
420
- });
421
- if (index >= 0) {
422
- marks[index] = override;
423
- return;
424
- }
425
- marks.push(override);
411
+ };
426
412
  };
427
413
  function textFormattingToMarks(formatting, schema, options) {
428
414
  if (!formatting) return [];
415
+ const createMark = options?.createMark ?? ((type, attrs) => schema.mark(type, attrs));
429
416
  const marks = [];
430
417
  const overrideFormatting = options ? options.overrideFormatting : formatting;
431
418
  let overrideAttrs;
@@ -435,25 +422,24 @@ function textFormattingToMarks(formatting, schema, options) {
435
422
  formatting: options.directFormatting
436
423
  });
437
424
  else overrideAttrs = buildRunFormattingOverrideAttrs(overrideFormatting);
438
- if (overrideAttrs) marks.push(schema.mark("runFormattingOverride", overrideAttrs));
439
- if (formatting.bold) marks.push(schema.mark("bold"));
440
- if (formatting.italic) marks.push(schema.mark("italic"));
441
- if (formatting.underline && formatting.underline.style !== "none") marks.push(schema.mark("underline", {
425
+ if (formatting.bold) marks.push(createMark("bold"));
426
+ if (formatting.italic) marks.push(createMark("italic"));
427
+ if (formatting.underline && formatting.underline.style !== "none") marks.push(createMark("underline", {
442
428
  style: formatting.underline.style,
443
429
  color: formatting.underline.color
444
430
  }));
445
- if (formatting.strike || formatting.doubleStrike) marks.push(schema.mark("strike", { double: formatting.doubleStrike || false }));
446
- if (formatting.color && !formatting.color.auto) marks.push(schema.mark("textColor", {
431
+ if (formatting.strike || formatting.doubleStrike) marks.push(createMark("strike", { double: formatting.doubleStrike || false }));
432
+ if (formatting.color && !formatting.color.auto) marks.push(createMark("textColor", {
447
433
  rgb: formatting.color.rgb,
448
434
  themeColor: formatting.color.themeColor,
449
435
  themeTint: formatting.color.themeTint,
450
436
  themeShade: formatting.color.themeShade
451
437
  }));
452
- if (formatting.highlight && formatting.highlight !== "none") marks.push(schema.mark("highlight", { color: formatting.highlight }));
438
+ if (formatting.highlight && formatting.highlight !== "none") marks.push(createMark("highlight", { color: formatting.highlight }));
453
439
  const runShadingMarkAttrs = shadingToRunShadingAttrs(formatting.shading);
454
- if (runShadingMarkAttrs) marks.push(schema.mark("runShading", runShadingMarkAttrs));
455
- if (formatting.fontSize) marks.push(schema.mark("fontSize", { size: formatting.fontSize }));
456
- if (formatting.fontFamily) marks.push(schema.mark("fontFamily", {
440
+ if (runShadingMarkAttrs) marks.push(createMark("runShading", runShadingMarkAttrs));
441
+ if (formatting.fontSize) marks.push(createMark("fontSize", { size: formatting.fontSize }));
442
+ if (formatting.fontFamily) marks.push(createMark("fontFamily", {
457
443
  ascii: formatting.fontFamily.ascii,
458
444
  hAnsi: formatting.fontFamily.hAnsi,
459
445
  eastAsia: formatting.fontFamily.eastAsia,
@@ -464,31 +450,33 @@ function textFormattingToMarks(formatting, schema, options) {
464
450
  eastAsiaTheme: formatting.fontFamily.eastAsiaTheme,
465
451
  csTheme: formatting.fontFamily.csTheme
466
452
  }));
467
- if (formatting.language) marks.push(schema.mark("language", formatting.language));
468
- if (formatting.vertAlign === "superscript") marks.push(schema.mark("superscript"));
469
- else if (formatting.vertAlign === "subscript") marks.push(schema.mark("subscript"));
470
- if (formatting.allCaps) marks.push(schema.mark("allCaps"));
471
- if (formatting.smallCaps) marks.push(schema.mark("smallCaps"));
453
+ if (formatting.language) marks.push(createMark("language", formatting.language));
454
+ if (formatting.vertAlign === "superscript") marks.push(createMark("superscript"));
455
+ else if (formatting.vertAlign === "subscript") marks.push(createMark("subscript"));
456
+ if (formatting.allCaps) marks.push(createMark("allCaps"));
457
+ if (formatting.smallCaps) marks.push(createMark("smallCaps"));
472
458
  const spacing = typeof formatting.spacing === "number" ? formatting.spacing : null;
473
459
  const position = typeof formatting.position === "number" ? formatting.position : null;
474
460
  const scale = normalizeHorizontalScalePercent(formatting.scale) ?? null;
475
461
  const kerning = typeof formatting.kerning === "number" ? formatting.kerning : null;
476
- if (spacing !== null || position !== null || scale !== null || kerning !== null) marks.push(schema.mark("characterSpacing", {
462
+ if (spacing !== null || position !== null || scale !== null || kerning !== null) marks.push(createMark("characterSpacing", {
477
463
  spacing,
478
464
  position,
479
465
  scale,
480
466
  kerning
481
467
  }));
482
- if (formatting.hidden === true) marks.push(schema.mark("hidden"));
483
- if (formatting.emboss) marks.push(schema.mark("emboss"));
484
- if (formatting.imprint) marks.push(schema.mark("imprint"));
485
- if (formatting.shadow) marks.push(schema.mark("textShadow"));
486
- if (formatting.emphasisMark && formatting.emphasisMark !== "none") marks.push(schema.mark("emphasisMark", { type: formatting.emphasisMark }));
487
- if (formatting.outline) marks.push(schema.mark("textOutline"));
488
- if (formatting.rtl) marks.push(schema.mark("rtl"));
489
- if (formatting.effect && formatting.effect !== "none") marks.push(schema.mark("textEffect", { effect: formatting.effect }));
490
- addDirectFontProvenance(marks, schema, options?.directFormatting);
491
- if (options?.authoredCarrier === "preserve") addComplexScriptAbsenceProvenance(marks, schema, options.directFormatting);
468
+ if (formatting.hidden === true) marks.push(createMark("hidden"));
469
+ if (formatting.emboss) marks.push(createMark("emboss"));
470
+ if (formatting.imprint) marks.push(createMark("imprint"));
471
+ if (formatting.shadow) marks.push(createMark("textShadow"));
472
+ if (formatting.emphasisMark && formatting.emphasisMark !== "none") marks.push(createMark("emphasisMark", { type: formatting.emphasisMark }));
473
+ if (formatting.outline) marks.push(createMark("textOutline"));
474
+ if (formatting.rtl) marks.push(createMark("rtl"));
475
+ if (formatting.effect && formatting.effect !== "none") marks.push(createMark("textEffect", { effect: formatting.effect }));
476
+ let provenanceAttrs = withDirectFontProvenance(overrideAttrs, options?.directFormatting);
477
+ if (options?.authoredCarrier === "preserve") provenanceAttrs = withComplexScriptAbsenceProvenance(provenanceAttrs, options.directFormatting);
478
+ if (overrideAttrs) marks.unshift(createMark("runFormattingOverride", provenanceAttrs));
479
+ else if (provenanceAttrs) marks.push(createMark("runFormattingOverride", provenanceAttrs));
492
480
  return marks;
493
481
  }
494
482
  /**
@@ -9,7 +9,8 @@ const createFieldAttrs = () => ({
9
9
  _numberedRefBaseline: { default: void 0 },
10
10
  fieldKind: { default: "simple" },
11
11
  fldLock: { default: null },
12
- dirty: { default: null }
12
+ dirty: { default: null },
13
+ fieldResultIsFallback: { default: null }
13
14
  });
14
15
  /** A `data-` flag a pasted field carried: absent states nothing. */
15
16
  const statedFlag = (value) => value === void 0 ? null : value === "true";
@@ -19,10 +20,11 @@ const readFieldDomAttrs = (dom) => ({
19
20
  displayText: dom.textContent ?? "",
20
21
  fieldKind: dom.dataset["fieldKind"] ?? "simple",
21
22
  fldLock: statedFlag(dom.dataset["fldLock"]),
22
- dirty: statedFlag(dom.dataset["dirty"])
23
+ dirty: statedFlag(dom.dataset["dirty"]),
24
+ fieldResultIsFallback: statedFlag(dom.dataset["fieldResultIsFallback"])
23
25
  });
24
26
  const getFieldDomAttrs = (node) => {
25
- const { fieldType, instruction, fieldKind, fldLock, dirty } = expectFieldAttrs(node);
27
+ const { fieldType, instruction, fieldKind, fldLock, dirty, fieldResultIsFallback } = expectFieldAttrs(node);
26
28
  return {
27
29
  class: `docx-field docx-field-${fieldType.toLowerCase()}`,
28
30
  "data-field-type": fieldType,
@@ -30,6 +32,7 @@ const getFieldDomAttrs = (node) => {
30
32
  "data-field-kind": fieldKind,
31
33
  ...fldLock === void 0 ? {} : { "data-fld-lock": String(fldLock) },
32
34
  ...dirty === void 0 ? {} : { "data-dirty": String(dirty) },
35
+ ...fieldResultIsFallback === void 0 ? {} : { "data-field-result-is-fallback": String(fieldResultIsFallback) },
33
36
  style: "outline: 1px solid var(--doc-field-outline, rgba(200,200,200,0.4)); padding: 0 1px; border-radius: 2px;"
34
37
  };
35
38
  };
@@ -54,7 +54,13 @@ const SdtExtension = createNodeExtension({
54
54
  * `w:sdtEndPr` as a record, so a control the editor rebuilds still
55
55
  * writes its end mark once the captured bytes are gone.
56
56
  */
57
- endProperties: { default: null }
57
+ endProperties: { default: null },
58
+ /**
59
+ * The revisions whose element encloses the control (`w:ins > w:sdt`),
60
+ * as opposed to revising the content inside it (`w:sdt > w:ins`). Both
61
+ * put the same marks on the leaves; see `contentControlRevisions.ts`.
62
+ */
63
+ _docxEnclosingRevisionIds: { default: null }
58
64
  },
59
65
  parseDOM: [{
60
66
  tag: "span.docx-sdt",
@@ -78,7 +84,8 @@ const SdtExtension = createNodeExtension({
78
84
  checked: onOffFromDataset(el.dataset["checked"]),
79
85
  _preserved: null,
80
86
  rawEndPropertiesXml: null,
81
- endProperties: null
87
+ endProperties: null,
88
+ _docxEnclosingRevisionIds: null
82
89
  };
83
90
  }
84
91
  }],
@@ -370,6 +370,7 @@ const ShapeExtension = createNodeExtension({
370
370
  shapeType: { default: "rect" },
371
371
  geometryAdjustments: { default: null },
372
372
  _docxRunFormatting: { default: null },
373
+ _docxAlternateContent: { default: null },
373
374
  shapeId: { default: null },
374
375
  shapeName: { default: null },
375
376
  alt: { default: null },
@@ -86,6 +86,7 @@ const TextBoxExtension = createNodeExtension({
86
86
  alt: { default: null },
87
87
  title: { default: null },
88
88
  fillColor: { default: null },
89
+ gradientFill: { default: null },
89
90
  outlineWidth: { default: null },
90
91
  outlineColor: { default: null },
91
92
  outlineStyle: { default: null },
@@ -111,13 +112,16 @@ const TextBoxExtension = createNodeExtension({
111
112
  wrapDistanceSlots: { default: null },
112
113
  wrapEffectExtentSlots: { default: null },
113
114
  wrapPolygon: { default: null },
115
+ _docxAlternateContent: { default: null },
114
116
  _docxPlacement: { default: null },
115
117
  _docxGroupId: { default: null },
116
118
  _docxAnchorId: { default: null },
119
+ _docxGroupChild: { default: null },
117
120
  _docxTextBodyContentState: { default: { type: "authored" } },
118
121
  _docxTrackedChange: { default: null },
119
122
  _docxInlineSdts: { default: null },
120
- _preservedAttributes: { default: null }
123
+ _preservedAttributes: { default: null },
124
+ _docxHostParagraph: { default: null }
121
125
  },
122
126
  parseDOM: [{
123
127
  tag: "div.docx-textbox",
@@ -58,6 +58,7 @@ declare const CLEARED_LIST_RENDERING_ATTRS: Readonly<{
58
58
  listMarkerSecondSlotOffsetTwips: null;
59
59
  listLevelNumFmts: null;
60
60
  listLevelStarts: null;
61
+ listLevelTabs: null;
61
62
  listAbstractNumId: null;
62
63
  listStartOverride: null;
63
64
  }>;
@@ -1,4 +1,4 @@
1
- import { convertBulletToUnicode } from "../docx/bulletMarkers.js";
1
+ import { bulletMarkerFontName, convertBulletToUnicode } from "../docx/bulletMarkers.js";
2
2
  import { paragraphNumberingLevel, paragraphNumberingReferenceId, sameStatedParagraphNumbering } from "../docx/numberingReference.js";
3
3
  import { formatOoxmlCounter } from "../docx/ooxmlCounterFormatter.js";
4
4
  import { isListNumPr } from "../layout-engine/types.js";
@@ -20,6 +20,7 @@ const CLEARED_LIST_RENDERING_ATTRS = Object.freeze({
20
20
  listMarkerSecondSlotOffsetTwips: null,
21
21
  listLevelNumFmts: null,
22
22
  listLevelStarts: null,
23
+ listLevelTabs: null,
23
24
  listAbstractNumId: null,
24
25
  listStartOverride: null
25
26
  });
@@ -84,6 +85,7 @@ function previousListAttrs(attrs) {
84
85
  ...previous.listMarkerSecondSlotOffsetTwips !== void 0 ? { listMarkerSecondSlotOffsetTwips: previous.listMarkerSecondSlotOffsetTwips } : {},
85
86
  ...previous.listLevelNumFmts !== void 0 ? { listLevelNumFmts: previous.listLevelNumFmts } : {},
86
87
  ...previous.listLevelStarts !== void 0 ? { listLevelStarts: previous.listLevelStarts } : {},
88
+ ...previous.listLevelTabs !== void 0 ? { listLevelTabs: previous.listLevelTabs } : {},
87
89
  ...previous.listAbstractNumId !== void 0 ? { listAbstractNumId: previous.listAbstractNumId } : {},
88
90
  ...previous.listStartOverride !== void 0 ? { listStartOverride: previous.listStartOverride } : {}
89
91
  };
@@ -203,7 +205,7 @@ function advanceListMarker(attrs, state) {
203
205
  fromStyle: false,
204
206
  numId: null
205
207
  };
206
- return convertBulletToUnicode(attrs.listMarker ?? markerTemplate ?? "");
208
+ return convertBulletToUnicode(attrs.listMarker ?? markerTemplate ?? "", bulletMarkerFontName(attrs.listMarkerFormatting));
207
209
  }
208
210
  const firstInstanceEncounter = !state.counters.has(numId);
209
211
  let counters = state.counters.get(numId) ?? Array.from({ length: 9 }, () => NaN);
@@ -14,6 +14,7 @@ declare const LIST_RENDERING_ATTR_BY_FIELD: {
14
14
  readonly markerAlignment: "listMarkerAlignment";
15
15
  readonly markerAllCaps: "listMarkerAllCaps";
16
16
  readonly markerSuffix: "listMarkerSuffix";
17
+ readonly levelTabs: "listLevelTabs";
17
18
  readonly levelNumFmts: "listLevelNumFmts";
18
19
  readonly levelStarts: "listLevelStarts";
19
20
  readonly abstractNumId: "listAbstractNumId";
@@ -20,6 +20,7 @@ const LIST_RENDERING_ATTR_BY_FIELD = {
20
20
  markerAlignment: "listMarkerAlignment",
21
21
  markerAllCaps: "listMarkerAllCaps",
22
22
  markerSuffix: "listMarkerSuffix",
23
+ levelTabs: "listLevelTabs",
23
24
  levelNumFmts: "listLevelNumFmts",
24
25
  levelStarts: "listLevelStarts",
25
26
  abstractNumId: "listAbstractNumId",
@@ -48,6 +49,7 @@ const listRenderingAttrPatch = (rendering) => {
48
49
  listMarkerAlignment: rendering.markerAlignment || void 0,
49
50
  listMarkerAllCaps: rendering.markerAllCaps || void 0,
50
51
  listMarkerSuffix: rendering.markerSuffix || void 0,
52
+ listLevelTabs: rendering.levelTabs?.length ? rendering.levelTabs : void 0,
51
53
  listLevelNumFmts: rendering.levelNumFmts || void 0,
52
54
  listLevelStarts: rendering.levelStarts || void 0,
53
55
  listAbstractNumId: rendering.abstractNumId,
@@ -71,6 +73,7 @@ const listRenderingFromAttrs = ({ attrs, numId }) => {
71
73
  markerAlignment: attrs.listMarkerAlignment ?? void 0,
72
74
  markerAllCaps: attrs.listMarkerAllCaps ?? void 0,
73
75
  markerSuffix: attrs.listMarkerSuffix ?? void 0,
76
+ levelTabs: attrs.listLevelTabs ?? void 0,
74
77
  levelNumFmts: attrs.listLevelNumFmts ?? void 0,
75
78
  levelStarts: attrs.listLevelStarts ?? void 0,
76
79
  abstractNumId: attrs.listAbstractNumId ?? void 0,
@@ -15,6 +15,17 @@
15
15
  * The persisted/serialized model keeps the flat OOXML tri-state
16
16
  * (`ParagraphFormatting.bidi: boolean | undefined`); `directionToBidi` /
17
17
  * `directionFromBidi` bridge the two at the conversion boundary.
18
+ *
19
+ * `source: "auto"` is a *rendering* decision only — it makes the layout
20
+ * painter and the editor's DOM shape the paragraph RTL, so newly typed or
21
+ * pasted directional text displays correctly before the user ever makes an
22
+ * explicit choice. It must never reach the saved package on its own: a
23
+ * paragraph that arrived without `w:bidi` and was never touched by the user
24
+ * must save without `w:bidi` too, no matter how many auto-detection passes
25
+ * ran over it on load. Only `source: "manual"` (an explicit user toggle, or
26
+ * a `w:bidi` the source document already carried) is authored content and
27
+ * belongs in `w:pPr`. Use `directionToBidi` for layout/rendering and
28
+ * `directionToAuthoredBidi` for anything that gets serialized.
18
29
  */
19
30
  type ParagraphDirection = {
20
31
  source: "auto";
@@ -29,8 +40,24 @@ declare const directionIsRtl: (direction: ParagraphDirection | null | undefined)
29
40
  * AutoBidiDetection may (re-)evaluate. A manual decision is left untouched.
30
41
  */
31
42
  declare const directionIsAutoManaged: (direction: ParagraphDirection | null | undefined) => boolean;
32
- /** Map a direction to the serialized OOXML `w:bidi` tri-state. */
43
+ /**
44
+ * Map a direction to the OOXML `w:bidi` tri-state for layout/rendering: an
45
+ * auto-detected direction lays out RTL exactly like a manual one. Do not use
46
+ * this to decide what gets written to the saved package — see
47
+ * `directionToAuthoredBidi`.
48
+ */
33
49
  declare const directionToBidi: (direction: ParagraphDirection | null | undefined) => boolean | undefined;
50
+ /**
51
+ * Map a direction to the OOXML `w:bidi` tri-state as *authored* content, for
52
+ * serialization. Only a manual decision (explicit user toggle, or a `w:bidi`
53
+ * the source document already carried) is ever written: `undefined` here
54
+ * means "the paragraph does not state its own direction," which keeps
55
+ * `w:pPr` free of a flag the user never asked for. An auto-detected
56
+ * direction (`source: "auto"`) resolves to `undefined` — same as undecided —
57
+ * regardless of which way detection currently leans, so it stays a view-only
58
+ * aid until the user makes it explicit.
59
+ */
60
+ declare const directionToAuthoredBidi: (direction: ParagraphDirection | null | undefined) => boolean | undefined;
34
61
  /**
35
62
  * Reconstruct a direction from a model `bidi` value (DOCX import / load): an
36
63
  * explicit `true`/`false` is a manual decision, absence is undecided.
@@ -39,4 +66,4 @@ declare const directionFromBidi: (bidi: boolean | null | undefined) => Paragraph
39
66
  /** Runtime guard for the PM attribute validator. */
40
67
  declare const isParagraphDirection: (value: unknown) => value is ParagraphDirection;
41
68
  //#endregion
42
- export { ParagraphDirection, directionFromBidi, directionIsAutoManaged, directionIsRtl, directionToBidi, isParagraphDirection };
69
+ export { ParagraphDirection, directionFromBidi, directionIsAutoManaged, directionIsRtl, directionToAuthoredBidi, directionToBidi, isParagraphDirection };
@@ -6,13 +6,32 @@ const directionIsRtl = (direction) => direction?.source === "auto" || direction?
6
6
  * AutoBidiDetection may (re-)evaluate. A manual decision is left untouched.
7
7
  */
8
8
  const directionIsAutoManaged = (direction) => direction == null || direction.source === "auto";
9
- /** Map a direction to the serialized OOXML `w:bidi` tri-state. */
9
+ /**
10
+ * Map a direction to the OOXML `w:bidi` tri-state for layout/rendering: an
11
+ * auto-detected direction lays out RTL exactly like a manual one. Do not use
12
+ * this to decide what gets written to the saved package — see
13
+ * `directionToAuthoredBidi`.
14
+ */
10
15
  const directionToBidi = (direction) => {
11
16
  if (direction == null) return;
12
17
  if (direction.source === "auto") return true;
13
18
  return direction.value === "rtl";
14
19
  };
15
20
  /**
21
+ * Map a direction to the OOXML `w:bidi` tri-state as *authored* content, for
22
+ * serialization. Only a manual decision (explicit user toggle, or a `w:bidi`
23
+ * the source document already carried) is ever written: `undefined` here
24
+ * means "the paragraph does not state its own direction," which keeps
25
+ * `w:pPr` free of a flag the user never asked for. An auto-detected
26
+ * direction (`source: "auto"`) resolves to `undefined` — same as undecided —
27
+ * regardless of which way detection currently leans, so it stays a view-only
28
+ * aid until the user makes it explicit.
29
+ */
30
+ const directionToAuthoredBidi = (direction) => {
31
+ if (direction == null || direction.source === "auto") return;
32
+ return direction.value === "rtl";
33
+ };
34
+ /**
16
35
  * Reconstruct a direction from a model `bidi` value (DOCX import / load): an
17
36
  * explicit `true`/`false` is a manual decision, absence is undecided.
18
37
  */
@@ -30,4 +49,4 @@ const isParagraphDirection = (value) => {
30
49
  return value.source === "manual" && "value" in value && (value.value === "rtl" || value.value === "ltr");
31
50
  };
32
51
  //#endregion
33
- export { directionFromBidi, directionIsAutoManaged, directionIsRtl, directionToBidi, isParagraphDirection };
52
+ export { directionFromBidi, directionIsAutoManaged, directionIsRtl, directionToAuthoredBidi, directionToBidi, isParagraphDirection };
@@ -1,7 +1,9 @@
1
+ import { encloseWholeControls } from "../contentControlRevisions.js";
1
2
  import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
2
3
  import { handleEditorBeforeInput } from "../textInput.js";
3
4
  import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
4
5
  import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
6
+ import { Mapping } from "prosemirror-transform";
5
7
  import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
6
8
  import { isHistoryTransaction } from "prosemirror-history";
7
9
  //#region src/prosemirror/plugins/suggestionMode.ts
@@ -87,6 +89,12 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
87
89
  });
88
90
  if (ranges.length === 0) return;
89
91
  const delAttrs = findAdjacentRevisionForRange(doc, from, to, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
92
+ encloseWholeControls({
93
+ tr,
94
+ from: tr.mapping.map(from),
95
+ to: tr.mapping.map(to, -1),
96
+ revisionId: delAttrs.revisionId
97
+ });
90
98
  for (let i = ranges.length - 1; i >= 0; i--) {
91
99
  const range = ranges[i];
92
100
  if (range.isOwnInsert) tr.delete(range.from, range.to);
@@ -107,6 +115,12 @@ function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, att
107
115
  if (start >= end) return;
108
116
  tr.addMark(start, end, insertionType.create(attrs));
109
117
  });
118
+ encloseWholeControls({
119
+ tr,
120
+ from,
121
+ to,
122
+ revisionId: attrs.revisionId
123
+ });
110
124
  }
111
125
  /**
112
126
  * With track changes on, pasting over a non-empty text selection marks the
@@ -439,16 +453,31 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
439
453
  const tr = newState.tr;
440
454
  tr.setMeta(SUGGESTION_META, true);
441
455
  const deletionType = newState.schema.marks["deletion"];
442
- for (const step of userTr.steps) step.getMap().forEach((_oldFrom, _oldTo, newFrom, newTo) => {
443
- if (newTo > newFrom) newState.doc.nodesBetween(newFrom, newTo, (node, pos) => {
444
- if (!canCarryTrackedRunMark(node)) return;
445
- if (!node.marks.some((m) => m.type === insertionType || deletionType && m.type === deletionType)) {
446
- const nodeStart = Math.max(pos, newFrom);
447
- const nodeEnd = Math.min(pos + node.nodeSize, newTo);
448
- tr.addMark(nodeStart, nodeEnd, insertionType.create(markAttrs));
456
+ const laterMaps = transactions.slice(transactions.indexOf(userTr) + 1).flatMap((transaction) => transaction.mapping.maps);
457
+ for (const [stepIndex, step] of userTr.steps.entries()) {
458
+ const stepMap = step.getMap();
459
+ const following = new Mapping([...userTr.mapping.maps.slice(stepIndex + 1), ...laterMaps]);
460
+ stepMap.forEach((_oldFrom, _oldTo, stepFrom, stepTo) => {
461
+ const newFrom = following.map(stepFrom, -1);
462
+ const newTo = following.map(stepTo, 1);
463
+ if (newTo > newFrom) {
464
+ newState.doc.nodesBetween(newFrom, newTo, (node, pos) => {
465
+ if (!canCarryTrackedRunMark(node)) return;
466
+ if (!node.marks.some((m) => m.type === insertionType || deletionType && m.type === deletionType)) {
467
+ const nodeStart = Math.max(pos, newFrom);
468
+ const nodeEnd = Math.min(pos + node.nodeSize, newTo);
469
+ tr.addMark(nodeStart, nodeEnd, insertionType.create(markAttrs));
470
+ }
471
+ });
472
+ encloseWholeControls({
473
+ tr,
474
+ from: newFrom,
475
+ to: newTo,
476
+ revisionId: markAttrs.revisionId
477
+ });
449
478
  }
450
479
  });
451
- });
480
+ }
452
481
  return tr.steps.length > 0 ? tr : null;
453
482
  }
454
483
  });
@@ -0,0 +1,27 @@
1
+ import { ParagraphRunStyleContext, RunStyleResolver } from "./runStyleFormatting.js";
2
+ import { Transaction } from "prosemirror-state";
3
+ import { Mark, Node } from "prosemirror-model";
4
+ //#region src/prosemirror/rejoinRunCarriers.d.ts
5
+ type ContinuedRunMarksOptions = {
6
+ left: Node;
7
+ right: Node;
8
+ /** The paragraph's run style context, read only when the marks differ in formatting alone. */
9
+ context: () => ParagraphRunStyleContext;
10
+ styleResolver: RunStyleResolver | null;
11
+ };
12
+ /**
13
+ * The marks `right` takes to continue `left`'s run, or `null` when the two
14
+ * already share their marks or write different runs: another mark differs,
15
+ * or the formatting they state does.
16
+ */
17
+ declare const continuedRunMarks: ({ left, right, context, styleResolver }: ContinuedRunMarksOptions) => readonly Mark[] | null;
18
+ type RejoinRunsAtOptions = {
19
+ tr: Transaction;
20
+ /** Positions in `tr.doc` where resolved content began or ended. */
21
+ boundaries: Iterable<number>;
22
+ styleResolver: RunStyleResolver | null;
23
+ };
24
+ /** Rejoin the runs meeting at each of `boundaries`, left to right. */
25
+ declare const rejoinRunsAt: ({ tr, boundaries, styleResolver }: RejoinRunsAtOptions) => void;
26
+ //#endregion
27
+ export { continuedRunMarks, rejoinRunsAt };
@@ -0,0 +1,66 @@
1
+ import { formattingEquals } from "../docx/runConsolidator.js";
2
+ import { RUN_FORMATTING_MARK_NAMES } from "./runFormattingMarkNames.js";
3
+ import { readAuthoredRunFormatting } from "./runFormattingReconciliation.js";
4
+ import { paragraphRunStyleContextAt } from "./runStyleFormatting.js";
5
+ import { Mark } from "prosemirror-model";
6
+ //#region src/prosemirror/rejoinRunCarriers.ts
7
+ /**
8
+ * Rejoin a run that resolving a revision left split.
9
+ *
10
+ * A revision splits the run it cuts into (`w:r`, `w:del > w:r`, `w:r`), and a
11
+ * run-property change rewrites the formatting marks of the piece it covers.
12
+ * Once the revision is resolved the pieces write the same `w:rPr`, but the
13
+ * editor can hold that formatting on different mark carriers (a restored
14
+ * `w:rPrChange` is rebuilt from its `w:rPr`), and leaves with different marks
15
+ * are written as different runs. At a resolved boundary, a leaf that writes
16
+ * the same run as the leaf before it takes that leaf's marks, so the two are
17
+ * one run again.
18
+ *
19
+ * Leaves the source wrote as separate runs keep them apart: a run identity
20
+ * (`runIdentity`) differs between them, and it is compared, not rewritten.
21
+ * The pieces of one run read back from a saved redline share an identity
22
+ * (`runIdentityAcrossRevisions.ts`).
23
+ */
24
+ const isFormattingMark = (mark) => RUN_FORMATTING_MARK_NAMES.has(mark.type.name);
25
+ /**
26
+ * The marks `right` takes to continue `left`'s run, or `null` when the two
27
+ * already share their marks or write different runs: another mark differs,
28
+ * or the formatting they state does.
29
+ */
30
+ const continuedRunMarks = ({ left, right, context, styleResolver }) => {
31
+ if (!left.isText || !right.isText || Mark.sameSet(left.marks, right.marks)) return null;
32
+ const otherMarks = (node) => node.marks.filter((mark) => !isFormattingMark(mark));
33
+ if (!Mark.sameSet(otherMarks(left), otherMarks(right))) return null;
34
+ const paragraphContext = context();
35
+ const stated = (node) => readAuthoredRunFormatting({
36
+ context: paragraphContext,
37
+ marks: node.marks,
38
+ styleResolver
39
+ });
40
+ return formattingEquals(stated(left), stated(right)) ? left.marks : null;
41
+ };
42
+ /** Rejoin the runs meeting at each of `boundaries`, left to right. */
43
+ const rejoinRunsAt = ({ tr, boundaries, styleResolver }) => {
44
+ for (const boundary of [...new Set(boundaries)].toSorted((left, right) => left - right)) {
45
+ if (boundary <= 0 || boundary >= tr.doc.content.size) continue;
46
+ const at = tr.doc.resolve(boundary);
47
+ const { nodeBefore: left, nodeAfter: right } = at;
48
+ if (!left || !right || !at.parent.inlineContent) continue;
49
+ const marks = continuedRunMarks({
50
+ left,
51
+ right,
52
+ context: () => paragraphRunStyleContextAt({
53
+ doc: tr.doc,
54
+ pos: boundary,
55
+ styleResolver
56
+ }),
57
+ styleResolver
58
+ });
59
+ if (marks === null) continue;
60
+ const to = boundary + right.nodeSize;
61
+ for (const mark of right.marks) if (!mark.isInSet(marks)) tr.removeMark(boundary, to, mark);
62
+ for (const mark of marks) if (!mark.isInSet(right.marks)) tr.addMark(boundary, to, mark);
63
+ }
64
+ };
65
+ //#endregion
66
+ export { continuedRunMarks, rejoinRunsAt };
@@ -0,0 +1,10 @@
1
+ import { Node } from "prosemirror-model";
2
+ //#region src/prosemirror/runIdentityAcrossRevisions.d.ts
3
+ /**
4
+ * `nodes`, one inline container's content, with each run a revision cut given
5
+ * the identity of the run it was cut from. A control is a container of its
6
+ * own. `nodes` itself comes back when nothing changes.
7
+ */
8
+ declare const continueRunIdentitiesAcrossRevisions: (nodes: readonly Node[]) => readonly Node[];
9
+ //#endregion
10
+ export { continueRunIdentitiesAcrossRevisions };