@stll/folio-core 0.27.1 → 0.28.1

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 (33) hide show
  1. package/dist/docx/server/extractDocxText.js +108 -33
  2. package/dist/docx/server/materializeYjsDocx.d.ts +31 -0
  3. package/dist/docx/server/materializeYjsDocx.js +61 -0
  4. package/dist/docx/styleParser.js +15 -14
  5. package/dist/layout-bridge/convert/toFlowBlocks.js +74 -23
  6. package/dist/layout-engine/footnoteColumnReflow.d.ts +15 -0
  7. package/dist/layout-engine/footnoteColumnReflow.js +74 -0
  8. package/dist/layout-engine/index.d.ts +3 -5
  9. package/dist/layout-engine/index.js +36 -15
  10. package/dist/layout-engine/measure/cache.d.ts +1 -1
  11. package/dist/layout-engine/measure/cache.js +11 -2
  12. package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
  13. package/dist/layout-engine/measure/listMarkerWidth.js +6 -4
  14. package/dist/layout-engine/paginator.d.ts +6 -0
  15. package/dist/layout-engine/paginator.js +30 -11
  16. package/dist/layout-engine/types.d.ts +11 -6
  17. package/dist/prosemirror/attrs/index.js +3 -2
  18. package/dist/prosemirror/commands/formatPainter.js +45 -1
  19. package/dist/prosemirror/conversion/fromProseDoc.d.ts +4 -1
  20. package/dist/prosemirror/conversion/fromProseDoc.js +59 -44
  21. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -1
  22. package/dist/prosemirror/conversion/toProseDoc.js +156 -33
  23. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +22 -22
  24. package/dist/prosemirror/schema/marks.d.ts +3 -1
  25. package/dist/prosemirror/styles/styleResolver.d.ts +0 -1
  26. package/dist/prosemirror/styles/styleResolver.js +22 -29
  27. package/dist/prosemirror/styles/styleToggleCascade.d.ts +37 -0
  28. package/dist/prosemirror/styles/styleToggleCascade.js +52 -0
  29. package/dist/server.d.ts +2 -1
  30. package/dist/server.js +2 -1
  31. package/dist/utils/textFormattingMerge.d.ts +9 -1
  32. package/dist/utils/textFormattingMerge.js +32 -1
  33. package/package.json +2 -2
@@ -8,6 +8,7 @@ import { applyRunFormattingOverrideAttrs } from "../extensions/marks/RunFormatti
8
8
  import { directionToBidi } from "../paragraphDirection.js";
9
9
  import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
10
10
  import { parseShapeGeometryAdjustments } from "../shapeGeometryAdjustments.js";
11
+ import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
11
12
  import { expectTextBoxAnchorAttrs } from "../textBoxAnchorAttrs.js";
12
13
  import { assertValidProseMirrorDocument } from "../validation.js";
13
14
  import { runShadingAttrsToShading } from "./runShadingMark.js";
@@ -647,6 +648,7 @@ function paragraphAttrsToFormatting(attrs) {
647
648
  */
648
649
  function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false) {
649
650
  const content = [];
651
+ const inheritedFormatting = paragraph.type.name === "paragraph" ? expectParagraphAttrs(paragraph).defaultTextFormatting ?? void 0 : void 0;
650
652
  const sortedEmptyHyperlinks = (emptyHyperlinks ?? []).map((attrs, order) => ({
651
653
  attrs,
652
654
  order
@@ -763,7 +765,12 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
763
765
  const changeMark = insertionMark ?? deletionMark;
764
766
  if (!changeMark) return;
765
767
  const changeAttrs = expectTrackedChangeMarkAttrs(changeMark);
766
- const run = createTrackedChangeRun(node, node.marks.filter((m) => m.type.name !== "insertion" && m.type.name !== "deletion"));
768
+ const otherMarks = node.marks.filter((m) => m.type.name !== "insertion" && m.type.name !== "deletion");
769
+ const run = createTrackedChangeRun({
770
+ inheritedFormatting,
771
+ marks: otherMarks,
772
+ node
773
+ });
767
774
  if (!run) return;
768
775
  const trackedContent = linkMark ? {
769
776
  ...createHyperlink(linkMark),
@@ -809,7 +816,11 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
809
816
  currentHyperlink = createHyperlink(linkMark);
810
817
  currentHyperlinkKey = linkKey;
811
818
  }
812
- addNodeToHyperlink(currentHyperlink, node);
819
+ addNodeToHyperlink({
820
+ hyperlink: currentHyperlink,
821
+ inheritedFormatting,
822
+ node
823
+ });
813
824
  return;
814
825
  }
815
826
  if (currentHyperlink) flushCurrentInline();
@@ -818,7 +829,11 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
818
829
  if (currentRun && currentMarksKey === marksKey) appendTextToRun(currentRun, node.text || "");
819
830
  else {
820
831
  if (currentRun) content.push(currentRun);
821
- currentRun = createRunFromText(node.text || "", node.marks);
832
+ currentRun = createRunFromText({
833
+ inheritedFormatting,
834
+ marks: node.marks,
835
+ text: node.text || ""
836
+ });
822
837
  currentMarksKey = marksKey;
823
838
  }
824
839
  } else if (node.type.name === "symbol") {
@@ -880,11 +895,11 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
880
895
  });
881
896
  return content;
882
897
  }
883
- function createTrackedChangeRun(node, marks) {
898
+ function createTrackedChangeRun({ inheritedFormatting, marks, node }) {
884
899
  const noteRefMark = marks.find((mark) => mark.type.name === "footnoteRef");
885
900
  if (noteRefMark) return createNoteReferenceRun(noteRefMark, marks);
886
901
  if (node.isText) {
887
- const formatting = marksToTextFormatting(marks);
902
+ const formatting = marksToTextFormatting(marks, { inheritedFormatting });
888
903
  const run = {
889
904
  type: "run",
890
905
  content: node.text ? [{
@@ -989,10 +1004,7 @@ function createHyperlink(linkMark) {
989
1004
  if (attrs.rId) hyperlink.rId = attrs.rId;
990
1005
  return hyperlink;
991
1006
  }
992
- /**
993
- * Add a node to a hyperlink
994
- */
995
- function addNodeToHyperlink(hyperlink, node) {
1007
+ function addNodeToHyperlink({ hyperlink, inheritedFormatting, node }) {
996
1008
  const noteRefMark = node.marks.find((m) => m.type.name === "footnoteRef");
997
1009
  if (noteRefMark) {
998
1010
  hyperlink.children.push(createNoteReferenceRun(noteRefMark, node.marks));
@@ -1000,7 +1012,11 @@ function addNodeToHyperlink(hyperlink, node) {
1000
1012
  }
1001
1013
  const nonLinkMarks = node.marks.filter((m) => m.type.name !== "hyperlink");
1002
1014
  if (node.isText && node.text) {
1003
- const run = createRunFromText(node.text, nonLinkMarks);
1015
+ const run = createRunFromText({
1016
+ inheritedFormatting,
1017
+ marks: nonLinkMarks,
1018
+ text: node.text
1019
+ });
1004
1020
  hyperlink.children.push(run);
1005
1021
  return;
1006
1022
  }
@@ -1044,11 +1060,8 @@ function createNoteReferenceRun(noteRefMark, marks) {
1044
1060
  if (vertAlign) run.formatting = { vertAlign };
1045
1061
  return run;
1046
1062
  }
1047
- /**
1048
- * Create a Run from text and marks
1049
- */
1050
- function createRunFromText(text, marks) {
1051
- const formatting = getRunFormattingFromMarks(marks);
1063
+ function createRunFromText({ inheritedFormatting, marks, text }) {
1064
+ const formatting = getRunFormattingFromMarks(marks, { inheritedFormatting });
1052
1065
  const run = {
1053
1066
  type: "run",
1054
1067
  content: [{
@@ -1082,9 +1095,9 @@ function restoreRunPropertyChanges(run, marks) {
1082
1095
  if (changes.length === 0) return;
1083
1096
  run.propertyChanges = [...changes];
1084
1097
  }
1085
- function getRunFormattingFromMarks(marks) {
1098
+ function getRunFormattingFromMarks(marks, options) {
1086
1099
  if (!marks || marks.length === 0) return;
1087
- const formatting = marksToTextFormatting(marks);
1100
+ const formatting = marksToTextFormatting(marks, options);
1088
1101
  return Object.keys(formatting).length > 0 ? formatting : void 0;
1089
1102
  }
1090
1103
  /**
@@ -1354,11 +1367,9 @@ function createShapeRun(node) {
1354
1367
  }]
1355
1368
  };
1356
1369
  }
1357
- /**
1358
- * Convert ProseMirror marks to TextFormatting
1359
- */
1360
- function marksToTextFormatting(marks) {
1370
+ function marksToTextFormatting(marks, options) {
1361
1371
  const formatting = {};
1372
+ let directOverrideFormatting;
1362
1373
  let characterStyleRPr;
1363
1374
  let runFormattingOverrideMark;
1364
1375
  for (const mark of marks) switch (mark.type.name) {
@@ -1483,8 +1494,18 @@ function marksToTextFormatting(marks) {
1483
1494
  }
1484
1495
  default: break;
1485
1496
  }
1486
- if (runFormattingOverrideMark) applyRunFormattingOverrideAttrs(formatting, expectRunFormattingOverrideMarkAttrs(runFormattingOverrideMark));
1487
- if (characterStyleRPr) return subtractCharacterStyleFormatting(formatting, characterStyleRPr);
1497
+ if (runFormattingOverrideMark) {
1498
+ const overrideAttrs = expectRunFormattingOverrideMarkAttrs(runFormattingOverrideMark);
1499
+ applyRunFormattingOverrideAttrs(formatting, overrideAttrs);
1500
+ directOverrideFormatting = {};
1501
+ applyRunFormattingOverrideAttrs(directOverrideFormatting, overrideAttrs);
1502
+ }
1503
+ if (characterStyleRPr) return subtractCharacterStyleFormatting({
1504
+ directOverrideFormatting,
1505
+ formatting,
1506
+ inheritedFormatting: options?.inheritedFormatting,
1507
+ styleRPr: characterStyleRPr
1508
+ });
1488
1509
  return formatting;
1489
1510
  }
1490
1511
  /**
@@ -1509,35 +1530,29 @@ const NEGATABLE_STYLE_KEYS = [
1509
1530
  ["outline", null],
1510
1531
  ["rtl", null]
1511
1532
  ];
1512
- /**
1513
- * Drop run formatting values that the run's character style already provides
1514
- * (the `w:rStyle` reference re-imposes them on load), so a styled run
1515
- * serializes back to a style reference instead of baked direct formatting.
1516
- *
1517
- * `styleRPr` is the load-time snapshot from the characterStyle mark, captured
1518
- * in the same normal form this module produces from marks, so value equality
1519
- * is a faithful "came from the style and is unchanged" check. Values that
1520
- * differ (user edits, direct overrides from the source document) stay as
1521
- * direct formatting, which wins over the style per the OOXML cascade.
1522
- *
1523
- * When the user *removes* a boolean property the style supplied (e.g. toggling
1524
- * off bold on text whose `w:rStyle` is bold), that key is no longer present in
1525
- * `formatting`, so the subtraction loop above never sees it. Without a counter,
1526
- * the run keeps the style reference and Word re-applies the removed formatting
1527
- * on load. The second pass emits an explicit negative override (`false`) for
1528
- * each negatable boolean the style set to `true` but the run no longer carries.
1529
- */
1530
- function subtractCharacterStyleFormatting(formatting, styleRPr) {
1533
+ function subtractCharacterStyleFormatting({ directOverrideFormatting, formatting, inheritedFormatting, styleRPr }) {
1531
1534
  const result = {};
1535
+ const effectiveStyleFormatting = cascadeStyleTextFormatting([{
1536
+ formatting: inheritedFormatting,
1537
+ type: "direct"
1538
+ }, {
1539
+ formatting: styleRPr,
1540
+ type: "style"
1541
+ }]).formatting;
1532
1542
  for (const key of Object.keys(formatting)) {
1533
1543
  const value = formatting[key];
1534
1544
  if (value === void 0) continue;
1535
- const styleValue = key === "styleId" ? void 0 : styleRPr[key];
1545
+ if (directOverrideFormatting?.[key] !== void 0) {
1546
+ result[key] = value;
1547
+ continue;
1548
+ }
1549
+ const styleValue = key === "styleId" ? void 0 : effectiveStyleFormatting?.[key];
1550
+ if ((key === "boldCs" || key === "italicCs") && styleValue === false && value === true) continue;
1536
1551
  if (styleValue !== void 0 && JSON.stringify(value) === JSON.stringify(styleValue)) continue;
1537
1552
  result[key] = value;
1538
1553
  }
1539
1554
  for (const [key, csKey] of NEGATABLE_STYLE_KEYS) {
1540
- if (styleRPr[key] !== true || formatting[key] !== void 0) continue;
1555
+ if (effectiveStyleFormatting?.[key] !== true || formatting[key] !== void 0) continue;
1541
1556
  result[key] = false;
1542
1557
  if (csKey !== null) result[csKey] = false;
1543
1558
  }
@@ -22,7 +22,10 @@ declare function standaloneTableCellToProseMirror(cell: document_d_exports.Table
22
22
  /**
23
23
  * Convert TextFormatting to ProseMirror marks
24
24
  */
25
- declare function textFormattingToMarks(formatting: document_d_exports.TextFormatting | undefined): ReturnType<typeof schema.mark>[];
25
+ type TextFormattingToMarksOptions = {
26
+ overrideFormatting: document_d_exports.TextFormatting | undefined;
27
+ };
28
+ declare function textFormattingToMarks(formatting: document_d_exports.TextFormatting | undefined, options?: TextFormattingToMarksOptions): ReturnType<typeof schema.mark>[];
26
29
  /**
27
30
  * Convert HeaderFooter content (array of Paragraph/Table blocks) to a ProseMirror document.
28
31
  * Used for editing headers/footers in their own ProseMirror editor and for
@@ -8,6 +8,7 @@ import { setAutospacingBaseValue } from "../autospacingBase.js";
8
8
  import { buildRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
9
9
  import { directionFromBidi } from "../paragraphDirection.js";
10
10
  import { schema } from "../schema/index.js";
11
+ import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
11
12
  import { assertValidProseMirrorDocument } from "../validation.js";
12
13
  import { resolveEffectiveTableCellFormatting } from "./effectiveTableCellFormatting.js";
13
14
  import { marksToTextFormatting } from "./fromProseDoc.js";
@@ -144,22 +145,66 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
144
145
  emitInlineNodes([node]);
145
146
  };
146
147
  let styleRunFormatting;
148
+ let paragraphStyleRunFormatting;
147
149
  let paragraphStyleFontFamily;
148
150
  if (styleResolver) {
149
151
  styleRunFormatting = styleResolver.resolveParagraphStyle(paragraph.formatting?.styleId).runFormatting;
152
+ const paragraphStyle = paragraph.formatting?.styleId ? styleResolver.getStyle(paragraph.formatting.styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
153
+ paragraphStyleRunFormatting = paragraphStyle?.type === "paragraph" ? paragraphStyle.rPr : void 0;
150
154
  paragraphStyleFontFamily = resolveParagraphStyleFontFamily(paragraph.formatting?.styleId, styleResolver);
151
155
  }
152
156
  const paragraphRunFormatting = resolveRunFormattingWithoutDefaults(paragraph.formatting?.runProperties, styleResolver);
153
157
  let inheritableParagraphRunFormatting;
154
158
  if (paragraphRunFormatting && !isTocParagraph) inheritableParagraphRunFormatting = stripParagraphMarkFormattingForBodyRuns(paragraphRunFormatting);
155
- let baseRunFormatting = mergeTextFormatting(styleRunFormatting, extraRunFormatting);
159
+ const orderedToggleFormatting = cascadeStyleTextFormatting([
160
+ {
161
+ formatting: styleResolver?.getDocDefaults()?.rPr,
162
+ type: "defaults"
163
+ },
164
+ {
165
+ formatting: extraRunFormatting,
166
+ type: "style"
167
+ },
168
+ {
169
+ formatting: paragraphStyleRunFormatting,
170
+ type: "style"
171
+ }
172
+ ], { ordinaryFormatting: mergeTextFormatting(styleRunFormatting, extraRunFormatting) });
173
+ let baseRunFormatting = orderedToggleFormatting.formatting;
156
174
  if (paragraphStyleFontFamily) baseRunFormatting = mergeTextFormatting(baseRunFormatting, { fontFamily: paragraphStyleFontFamily });
157
175
  const paragraphMarkPrecedesStyle = paragraph.formatting?.styleId !== void 0;
158
- const defaultRunFormatting = paragraphMarkPrecedesStyle ? mergeTextFormatting(inheritableParagraphRunFormatting, baseRunFormatting) : mergeTextFormatting(baseRunFormatting, inheritableParagraphRunFormatting);
176
+ const ordinaryDefaultRunFormatting = paragraphMarkPrecedesStyle ? mergeTextFormatting(inheritableParagraphRunFormatting, baseRunFormatting) : mergeTextFormatting(baseRunFormatting, inheritableParagraphRunFormatting);
177
+ const defaultToggleCascade = paragraphMarkPrecedesStyle ? cascadeStyleTextFormatting([{
178
+ cascade: orderedToggleFormatting,
179
+ type: "carried"
180
+ }], { ordinaryFormatting: ordinaryDefaultRunFormatting }) : cascadeStyleTextFormatting([{
181
+ cascade: orderedToggleFormatting,
182
+ type: "carried"
183
+ }, {
184
+ formatting: inheritableParagraphRunFormatting,
185
+ type: "direct"
186
+ }], { ordinaryFormatting: ordinaryDefaultRunFormatting });
187
+ const defaultRunFormatting = defaultToggleCascade.formatting;
159
188
  const getInheritedRunFormatting = (formatting, fieldType) => {
160
- if (fieldType === "TOC") return hasDirectRunFormatting(formatting) ? suppressParagraphMarkFormatting(baseRunFormatting, void 0, formatting) : baseRunFormatting;
161
- if (!(hasDirectRunFormatting(formatting) || formatting?.styleId !== void 0)) return defaultRunFormatting;
162
- return suppressParagraphMarkFormatting(baseRunFormatting, inheritableParagraphRunFormatting, formatting, paragraphMarkPrecedesStyle);
189
+ if (fieldType === "TOC") return {
190
+ formatting: hasDirectRunFormatting(formatting) ? suppressParagraphMarkFormatting(baseRunFormatting, void 0, formatting) : baseRunFormatting,
191
+ toggleCascade: orderedToggleFormatting
192
+ };
193
+ if (!(hasDirectRunFormatting(formatting) || formatting?.styleId !== void 0)) return {
194
+ formatting: defaultRunFormatting,
195
+ toggleCascade: defaultToggleCascade
196
+ };
197
+ const suppressedFormatting = suppressParagraphMarkFormatting(baseRunFormatting, inheritableParagraphRunFormatting, formatting, paragraphMarkPrecedesStyle);
198
+ const paragraphMarkOverrides = getParagraphMarkSuppressionOverrides({
199
+ directFormatting: formatting,
200
+ paragraphMarkFormatting: inheritableParagraphRunFormatting,
201
+ suppressedFormatting
202
+ });
203
+ return {
204
+ formatting: suppressedFormatting,
205
+ ...paragraphMarkOverrides ? { paragraphMarkOverrides } : {},
206
+ toggleCascade: orderedToggleFormatting
207
+ };
163
208
  };
164
209
  const emitTrackedChange = (change, markType, moveKind) => {
165
210
  emitInlineNodes(convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, getInheritedRunFormatting, styleResolver, moveKind, textBoxAnchors));
@@ -499,27 +544,37 @@ function stripParagraphMarkFormattingForBodyRuns(formatting) {
499
544
  const { fontFamily: _fontFamily, ...bodyRunFormatting } = paragraphMarkFormatting;
500
545
  return Object.keys(bodyRunFormatting).length > 0 ? bodyRunFormatting : void 0;
501
546
  }
547
+ const PARAGRAPH_MARK_BOOLEAN_KEYS = [
548
+ "bold",
549
+ "italic",
550
+ "strike",
551
+ "doubleStrike",
552
+ "allCaps",
553
+ "smallCaps",
554
+ "hidden",
555
+ "emboss",
556
+ "imprint",
557
+ "shadow",
558
+ "outline",
559
+ "rtl"
560
+ ];
502
561
  function suppressParagraphMarkFormatting(base, paragraphMark, direct, paragraphMarkPrecedesStyle = false) {
503
562
  if (!paragraphMark) return base;
504
563
  const result = (paragraphMarkPrecedesStyle ? mergeTextFormatting(paragraphMark, base) : mergeTextFormatting(base, paragraphMark)) ?? {};
505
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "bold");
506
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "italic");
507
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "strike");
508
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "doubleStrike");
509
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "allCaps");
510
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "smallCaps");
511
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "hidden");
512
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "emboss");
513
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "imprint");
514
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "shadow");
515
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "outline");
516
- suppressBooleanParagraphMark(result, base, paragraphMark, direct, "rtl");
564
+ for (const key of PARAGRAPH_MARK_BOOLEAN_KEYS) suppressBooleanParagraphMark(result, base, paragraphMark, direct, key);
517
565
  if (paragraphMark.fontSize !== void 0 && direct?.fontSize === void 0 && base?.fontSize !== void 0) result.fontSize = base.fontSize;
518
566
  if (paragraphMark.fontSizeCs !== void 0 && direct?.fontSizeCs === void 0 && base?.fontSizeCs !== void 0) result.fontSizeCs = base.fontSizeCs;
519
567
  if (paragraphMark.underline !== void 0 && direct?.underline === void 0) result.underline = { style: "none" };
520
568
  if (paragraphMark.spacing !== void 0 && direct?.spacing === void 0) result.spacing = 0;
521
569
  return Object.keys(result).length > 0 ? result : void 0;
522
570
  }
571
+ function getParagraphMarkSuppressionOverrides({ directFormatting, paragraphMarkFormatting, suppressedFormatting }) {
572
+ if (!paragraphMarkFormatting) return;
573
+ const overrides = {};
574
+ for (const key of PARAGRAPH_MARK_BOOLEAN_KEYS) if (paragraphMarkFormatting[key] !== void 0 && directFormatting?.[key] === void 0 && suppressedFormatting?.[key] === false) overrides[key] = false;
575
+ if (paragraphMarkFormatting.underline !== void 0 && directFormatting?.underline === void 0 && suppressedFormatting?.underline?.style === "none") overrides.underline = { style: "none" };
576
+ return Object.keys(overrides).length > 0 ? overrides : void 0;
577
+ }
523
578
  function suppressBooleanParagraphMark(result, base, paragraphMark, direct, key) {
524
579
  if (paragraphMark[key] === void 0 || direct?.[key] !== void 0) return;
525
580
  result[key] = base?.[key] ?? false;
@@ -536,15 +591,45 @@ function resolveTextFormatting(formatting, styleResolver) {
536
591
  */
537
592
  function resolveRunFormattingWithoutDefaults(formatting, styleResolver) {
538
593
  if (!formatting || !styleResolver) return formatting;
539
- return mergeTextFormatting(formatting.styleId ? styleResolver.getRunStyleOwnProperties(formatting.styleId) : void 0, formatting);
594
+ return cascadeStyleTextFormatting([{
595
+ formatting: formatting.styleId ? styleResolver.getRunStyleOwnProperties(formatting.styleId) : void 0,
596
+ type: "style"
597
+ }, {
598
+ formatting,
599
+ type: "direct"
600
+ }]).formatting;
540
601
  }
541
602
  function resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolver, options = {}) {
542
603
  const style = styleId ? styleResolver.getStyle(styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
543
604
  const paragraphStyleRpr = style?.type === "paragraph" ? style.rPr : void 0;
544
605
  const rawParagraphMarkRpr = options.includeParagraphMarkRunProperties === false ? void 0 : formatting?.runProperties;
545
606
  const paragraphRunProperties = rawParagraphMarkRpr ? stripParagraphMarkOnlyFormatting(resolveRunFormattingWithoutDefaults(rawParagraphMarkRpr, styleResolver) ?? {}) : void 0;
546
- const bodyRunDefaults = mergeTextFormatting(mergeTextFormatting(styleResolver.getDocDefaults()?.rPr, styleResolver.getDefaultCharacterStyle()?.rPr), paragraphStyleRpr);
547
- return styleId === void 0 ? mergeTextFormatting(bodyRunDefaults, paragraphRunProperties) : mergeTextFormatting(paragraphRunProperties, bodyRunDefaults);
607
+ const orderedBodyToggleFormatting = cascadeStyleTextFormatting([
608
+ {
609
+ formatting: styleResolver.getDocDefaults()?.rPr,
610
+ type: "defaults"
611
+ },
612
+ {
613
+ formatting: paragraphStyleRpr,
614
+ type: "style"
615
+ },
616
+ {
617
+ formatting: styleResolver.getDefaultCharacterStyle()?.rPr,
618
+ type: "style"
619
+ }
620
+ ], { ordinaryFormatting: mergeTextFormatting(mergeTextFormatting(styleResolver.getDocDefaults()?.rPr, styleResolver.getDefaultCharacterStyle()?.rPr), paragraphStyleRpr) });
621
+ const bodyRunDefaults = orderedBodyToggleFormatting.formatting;
622
+ if (styleId !== void 0) return cascadeStyleTextFormatting([{
623
+ cascade: orderedBodyToggleFormatting,
624
+ type: "carried"
625
+ }], { ordinaryFormatting: mergeTextFormatting(paragraphRunProperties, bodyRunDefaults) }).formatting;
626
+ return cascadeStyleTextFormatting([{
627
+ cascade: orderedBodyToggleFormatting,
628
+ type: "carried"
629
+ }, {
630
+ formatting: paragraphRunProperties,
631
+ type: "direct"
632
+ }], { ordinaryFormatting: mergeTextFormatting(bodyRunDefaults, paragraphRunProperties) }).formatting;
548
633
  }
549
634
  function calculateRowSpans(table) {
550
635
  const result = /* @__PURE__ */ new Map();
@@ -1012,11 +1097,11 @@ function convertInlineSdt(sdt, nextHyperlinkInstanceIndex, getInheritedRunFormat
1012
1097
  * Convert a Run to ProseMirror text nodes with marks
1013
1098
  *
1014
1099
  * @param run - The run to convert
1015
- * @param styleFormatting - Text formatting from the paragraph's style (e.g., Heading1's font size/color)
1100
+ * @param resolvedStyleFormatting - Paragraph formatting and its explicit style-toggle state
1016
1101
  */
1017
- function convertRun(run, styleFormatting, styleResolver, textBoxAnchors) {
1102
+ function convertRun(run, resolvedStyleFormatting, styleResolver, textBoxAnchors) {
1018
1103
  const nodes = [];
1019
- const { marks, mergedFormatting } = buildRunMarks(run.formatting, styleFormatting, styleResolver);
1104
+ const { marks, mergedFormatting } = buildRunMarks(run.formatting, resolvedStyleFormatting, styleResolver);
1020
1105
  if (run.propertyChanges && run.propertyChanges.length > 0) marks.push(schema.mark("runPropertyChange", { changes: [...run.propertyChanges] }));
1021
1106
  for (const content of run.content) {
1022
1107
  const contentNodes = convertRunContent(content, marks, mergedFormatting, textBoxAnchors);
@@ -1024,13 +1109,34 @@ function convertRun(run, styleFormatting, styleResolver, textBoxAnchors) {
1024
1109
  }
1025
1110
  return nodes;
1026
1111
  }
1027
- function buildRunMarks(runFormatting, inheritedFormatting, styleResolver) {
1112
+ function buildRunMarks(runFormatting, inherited, styleResolver) {
1028
1113
  const styleId = runFormatting?.styleId;
1029
- const runStyleFormatting = styleId ? styleResolver?.getRunStyleOwnProperties(styleId) : void 0;
1030
- const mergedFormatting = mergeTextFormatting(mergeTextFormatting(inheritedFormatting, runStyleFormatting), runFormatting);
1031
- const marks = textFormattingToMarks(mergedFormatting);
1114
+ const characterStyleFormatting = styleId ? styleResolver?.getRunStyleOwnProperties(styleId) : styleResolver?.getDefaultCharacterStyle()?.rPr;
1115
+ let ordinaryRunStyleFormatting = inherited.formatting ? { ...inherited.formatting } : {};
1116
+ if (styleId) ordinaryRunStyleFormatting = mergeTextFormatting(inherited.formatting, characterStyleFormatting) ?? {};
1117
+ const cascadedStyleFormatting = cascadeStyleTextFormatting([{
1118
+ cascade: inherited.toggleCascade,
1119
+ type: "carried"
1120
+ }, {
1121
+ formatting: characterStyleFormatting,
1122
+ type: "style"
1123
+ }], { ordinaryFormatting: ordinaryRunStyleFormatting });
1124
+ const runStyleFormatting = cascadedStyleFormatting.formatting;
1125
+ const mergedFormatting = cascadeStyleTextFormatting([{
1126
+ cascade: cascadedStyleFormatting,
1127
+ type: "carried"
1128
+ }, {
1129
+ formatting: runFormatting,
1130
+ type: "direct"
1131
+ }], { ordinaryFormatting: mergeTextFormatting(runStyleFormatting, runFormatting) }).formatting;
1132
+ const marks = textFormattingToMarks(mergedFormatting, { overrideFormatting: getRunFormattingOverrides({
1133
+ directFormatting: runFormatting,
1134
+ effectiveStyleFormatting: runStyleFormatting,
1135
+ hasCharacterStyle: styleId !== void 0,
1136
+ paragraphMarkOverrides: inherited.paragraphMarkOverrides
1137
+ }) });
1032
1138
  if (styleId) {
1033
- const styleRPr = runStyleFormatting ? marksToTextFormatting(textFormattingToMarks(runStyleFormatting)) : void 0;
1139
+ const styleRPr = characterStyleFormatting ? marksToTextFormatting(textFormattingToMarks(characterStyleFormatting)) : void 0;
1034
1140
  marks.push(schema.mark("characterStyle", {
1035
1141
  styleId,
1036
1142
  _styleRPr: styleRPr && Object.keys(styleRPr).length > 0 ? styleRPr : null
@@ -1041,6 +1147,26 @@ function buildRunMarks(runFormatting, inheritedFormatting, styleResolver) {
1041
1147
  mergedFormatting
1042
1148
  };
1043
1149
  }
1150
+ const ORDINARY_STYLE_TOGGLE_KEYS = [
1151
+ "bold",
1152
+ "italic",
1153
+ "strike",
1154
+ "allCaps",
1155
+ "smallCaps",
1156
+ "hidden",
1157
+ "emboss",
1158
+ "imprint",
1159
+ "shadow",
1160
+ "outline"
1161
+ ];
1162
+ function getRunFormattingOverrides({ directFormatting, effectiveStyleFormatting, hasCharacterStyle, paragraphMarkOverrides }) {
1163
+ const overrides = mergeTextFormatting(paragraphMarkOverrides, directFormatting);
1164
+ if (!overrides || !directFormatting) return overrides;
1165
+ for (const key of ORDINARY_STYLE_TOGGLE_KEYS) if (directFormatting[key] === true && (!hasCharacterStyle || effectiveStyleFormatting?.[key] !== true)) Reflect.deleteProperty(overrides, key);
1166
+ if (directFormatting.boldCs === true && directFormatting.boldCs === directFormatting.bold && (!hasCharacterStyle || effectiveStyleFormatting?.boldCs === void 0)) Reflect.deleteProperty(overrides, "boldCs");
1167
+ if (directFormatting.italicCs === true && directFormatting.italicCs === directFormatting.italic && (!hasCharacterStyle || effectiveStyleFormatting?.italicCs === void 0)) Reflect.deleteProperty(overrides, "italicCs");
1168
+ return overrides;
1169
+ }
1044
1170
  /**
1045
1171
  * Convert RunContent to ProseMirror nodes
1046
1172
  */
@@ -1220,13 +1346,10 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
1220
1346
  }
1221
1347
  return nodes;
1222
1348
  }
1223
- /**
1224
- * Convert TextFormatting to ProseMirror marks
1225
- */
1226
- function textFormattingToMarks(formatting) {
1349
+ function textFormattingToMarks(formatting, options) {
1227
1350
  if (!formatting) return [];
1228
1351
  const marks = [];
1229
- const overrideAttrs = buildRunFormattingOverrideAttrs(formatting);
1352
+ const overrideAttrs = buildRunFormattingOverrideAttrs(options ? options.overrideFormatting : formatting);
1230
1353
  if (overrideAttrs) marks.push(schema.mark("runFormattingOverride", overrideAttrs));
1231
1354
  if (formatting.bold) marks.push(schema.mark("bold"));
1232
1355
  if (formatting.italic) marks.push(schema.mark("italic"));
@@ -4,38 +4,38 @@ import { createMarkExtension } from "../create.js";
4
4
  function buildRunFormattingOverrideAttrs(formatting) {
5
5
  if (!formatting) return;
6
6
  const attrs = {};
7
- if (formatting.bold === false) attrs.bold = false;
8
- if (formatting.italic === false) attrs.italic = false;
7
+ if (formatting.bold !== void 0) attrs.bold = formatting.bold;
8
+ if (formatting.italic !== void 0) attrs.italic = formatting.italic;
9
9
  if (formatting.underline?.style === "none") attrs.underline = "none";
10
- if (formatting.strike === false) attrs.strike = false;
10
+ if (formatting.strike !== void 0) attrs.strike = formatting.strike;
11
11
  if (formatting.doubleStrike === false) attrs.doubleStrike = false;
12
- if (formatting.allCaps === false) attrs.allCaps = false;
13
- if (formatting.smallCaps === false) attrs.smallCaps = false;
14
- if (formatting.hidden === false) attrs.hidden = false;
15
- if (formatting.emboss === false) attrs.emboss = false;
16
- if (formatting.imprint === false) attrs.imprint = false;
17
- if (formatting.shadow === false) attrs.shadow = false;
18
- if (formatting.outline === false) attrs.outline = false;
12
+ if (formatting.allCaps !== void 0) attrs.allCaps = formatting.allCaps;
13
+ if (formatting.smallCaps !== void 0) attrs.smallCaps = formatting.smallCaps;
14
+ if (formatting.hidden !== void 0) attrs.hidden = formatting.hidden;
15
+ if (formatting.emboss !== void 0) attrs.emboss = formatting.emboss;
16
+ if (formatting.imprint !== void 0) attrs.imprint = formatting.imprint;
17
+ if (formatting.shadow !== void 0) attrs.shadow = formatting.shadow;
18
+ if (formatting.outline !== void 0) attrs.outline = formatting.outline;
19
19
  if (formatting.rtl === false) attrs.rtl = false;
20
- if (formatting.boldCs !== void 0 && formatting.boldCs !== formatting.bold) attrs.boldCs = formatting.boldCs;
21
- if (formatting.italicCs !== void 0 && formatting.italicCs !== formatting.italic) attrs.italicCs = formatting.italicCs;
20
+ if (formatting.boldCs !== void 0) attrs.boldCs = formatting.boldCs;
21
+ if (formatting.italicCs !== void 0) attrs.italicCs = formatting.italicCs;
22
22
  if (formatting.fontSizeCs !== void 0 && formatting.fontSizeCs !== formatting.fontSize) attrs.fontSizeCs = formatting.fontSizeCs;
23
23
  if (formatting.cs !== void 0) attrs.cs = formatting.cs;
24
24
  return Object.keys(attrs).length > 0 ? attrs : void 0;
25
25
  }
26
26
  function applyRunFormattingOverrideAttrs(formatting, attrs) {
27
- if (attrs.bold === false) formatting.bold = false;
28
- if (attrs.italic === false) formatting.italic = false;
27
+ if (attrs.bold !== void 0) formatting.bold = attrs.bold;
28
+ if (attrs.italic !== void 0) formatting.italic = attrs.italic;
29
29
  if (attrs.underline === "none") formatting.underline = { style: "none" };
30
- if (attrs.strike === false) formatting.strike = false;
30
+ if (attrs.strike !== void 0) formatting.strike = attrs.strike;
31
31
  if (attrs.doubleStrike === false) formatting.doubleStrike = false;
32
- if (attrs.allCaps === false) formatting.allCaps = false;
33
- if (attrs.smallCaps === false) formatting.smallCaps = false;
34
- if (attrs.hidden === false) formatting.hidden = false;
35
- if (attrs.emboss === false) formatting.emboss = false;
36
- if (attrs.imprint === false) formatting.imprint = false;
37
- if (attrs.shadow === false) formatting.shadow = false;
38
- if (attrs.outline === false) formatting.outline = false;
32
+ if (attrs.allCaps !== void 0) formatting.allCaps = attrs.allCaps;
33
+ if (attrs.smallCaps !== void 0) formatting.smallCaps = attrs.smallCaps;
34
+ if (attrs.hidden !== void 0) formatting.hidden = attrs.hidden;
35
+ if (attrs.emboss !== void 0) formatting.emboss = attrs.emboss;
36
+ if (attrs.imprint !== void 0) formatting.imprint = attrs.imprint;
37
+ if (attrs.shadow !== void 0) formatting.shadow = attrs.shadow;
38
+ if (attrs.outline !== void 0) formatting.outline = attrs.outline;
39
39
  if (attrs.rtl === false) formatting.rtl = false;
40
40
  if (attrs.boldCs !== void 0) formatting.boldCs = attrs.boldCs;
41
41
  if (attrs.italicCs !== void 0) formatting.italicCs = attrs.italicCs;
@@ -121,7 +121,9 @@ type RunPropertyChangeMarkAttrs = {
121
121
  provenance: TrackedChangeProvenance;
122
122
  suggestionId?: string;
123
123
  };
124
- type RunFormattingOverrideAttrs = { [K in keyof Pick<document_d_exports.TextFormatting, "bold" | "italic" | "strike" | "doubleStrike" | "allCaps" | "smallCaps" | "hidden" | "emboss" | "imprint" | "shadow" | "outline" | "rtl">]?: false; } & {
124
+ type RunFormattingOverrideAttrs = { [K in keyof Pick<document_d_exports.TextFormatting, "bold" | "italic" | "strike" | "allCaps" | "smallCaps" | "hidden" | "emboss" | "imprint" | "shadow" | "outline">]?: boolean; } & {
125
+ doubleStrike?: false;
126
+ rtl?: false;
125
127
  /** Independent complex-script weight (`w:bCs`). */
126
128
  boldCs?: boolean;
127
129
  /** Force complex-script formatting for the full run (`w:cs`). */
@@ -127,7 +127,6 @@ declare class StyleResolver {
127
127
  */
128
128
  hasStyle(styleId: string): boolean;
129
129
  private findDefaultStyle;
130
- private mergeStyleIntoResult;
131
130
  }
132
131
  /**
133
132
  * Create a style resolver from document's style definitions