@stll/folio-core 0.35.0 → 0.36.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 (80) hide show
  1. package/dist/ai-edits/apply.js +370 -118
  2. package/dist/ai-edits/headless.js +16 -10
  3. package/dist/ai-edits/index.d.ts +2 -2
  4. package/dist/ai-edits/index.js +2 -2
  5. package/dist/ai-edits/snapshot.js +6 -1
  6. package/dist/ai-edits/types.d.ts +19 -6
  7. package/dist/ai-edits/word-diff.d.ts +18 -3
  8. package/dist/ai-edits/word-diff.js +556 -67
  9. package/dist/compare/compare.js +7 -16
  10. package/dist/compare/plan.js +8 -5
  11. package/dist/compare/verification.d.ts +8 -9
  12. package/dist/compare/verification.js +51 -46
  13. package/dist/compat/eigenpal.d.ts +2 -2
  14. package/dist/compat/eigenpal.js +2 -2
  15. package/dist/controller/headerFooterEditorManager.js +5 -5
  16. package/dist/controller/hiddenEditorApi.js +1 -1
  17. package/dist/controller/noteEditorManager.js +2 -2
  18. package/dist/document-operations.d.ts +5 -3
  19. package/dist/document-operations.js +32 -5
  20. package/dist/docx/commentParser.js +2 -4
  21. package/dist/docx/commentRangeIntegrity.js +2 -4
  22. package/dist/docx/documentClone.d.ts +2 -0
  23. package/dist/docx/documentClone.js +2 -0
  24. package/dist/docx/headerFooterParser.js +2 -4
  25. package/dist/docx/normalizeBaseDirection.js +5 -7
  26. package/dist/docx/paragraphParser.js +27 -29
  27. package/dist/docx/paragraphPropertySource.d.ts +49 -0
  28. package/dist/docx/paragraphPropertySource.js +113 -0
  29. package/dist/docx/runConsolidator.js +3 -4
  30. package/dist/docx/runParser.js +2 -15
  31. package/dist/docx/sectionParser.js +1 -15
  32. package/dist/docx/serializer/paragraphSerializer.js +287 -30
  33. package/dist/docx/serializer/runSerializer.js +2 -10
  34. package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -10
  35. package/dist/docx/serializer/tableSerializer.js +4 -23
  36. package/dist/docx/serializer/trackedChangeAttributes.d.ts +9 -0
  37. package/dist/docx/serializer/trackedChangeAttributes.js +21 -0
  38. package/dist/docx/server/createBilingualDocument.js +8 -10
  39. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +22 -13
  40. package/dist/docx/server/validateDocxConformance.js +17 -13
  41. package/dist/docx/tableParser.js +1 -37
  42. package/dist/docx/trackedChangeInfo.d.ts +11 -0
  43. package/dist/docx/trackedChangeInfo.js +38 -0
  44. package/dist/docx/verbatimCapture.d.ts +20 -9
  45. package/dist/docx/verbatimCapture.js +208 -8
  46. package/dist/docx/xmlParser.d.ts +3 -1
  47. package/dist/docx/xmlParser.js +6 -1
  48. package/dist/docx/xmlSafety.d.ts +1 -1
  49. package/dist/docx/xmlSafety.js +157 -0
  50. package/dist/index.d.ts +2 -2
  51. package/dist/index.js +2 -2
  52. package/dist/layout-bridge/convert/headerFooterLayout.js +4 -4
  53. package/dist/layout-bridge/convert/toFlowBlocks.js +12 -7
  54. package/dist/markdown/renderBlock.js +7 -10
  55. package/dist/prosemirror/attrs/index.js +15 -0
  56. package/dist/prosemirror/commands/comments.js +365 -32
  57. package/dist/prosemirror/commands/pageBreak.js +1 -1
  58. package/dist/prosemirror/commands/propertyChangeScope.d.ts +26 -4
  59. package/dist/prosemirror/commands/propertyChangeScope.js +65 -3
  60. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -4
  61. package/dist/prosemirror/conversion/fromProseDoc.js +130 -18
  62. package/dist/prosemirror/conversion/toProseDoc.js +27 -12
  63. package/dist/prosemirror/extensions/core/ParagraphExtension.js +48 -4
  64. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -1
  65. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +20 -7
  66. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  67. package/dist/prosemirror/paragraphAlignment.d.ts +12 -0
  68. package/dist/prosemirror/paragraphAlignment.js +13 -0
  69. package/dist/prosemirror/plugins/suggestionMode.js +1 -1
  70. package/dist/prosemirror/schema/marks.d.ts +2 -0
  71. package/dist/prosemirror/schema/nodes.d.ts +12 -1
  72. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  73. package/dist/redline.js +4 -1
  74. package/dist/server.d.ts +2 -2
  75. package/dist/server.js +2 -2
  76. package/dist/utils/mergeDocumentContent.js +3 -3
  77. package/dist/utils/replaceText.js +2 -1
  78. package/dist/version-comparison.js +6 -4
  79. package/dist/watermark/index.js +2 -4
  80. package/package.json +7 -2
@@ -1,7 +1,52 @@
1
+ import { expectParagraphAttrs } from "../attrs/index.js";
1
2
  import { setAutospacingBaseValue } from "../autospacingBase.js";
3
+ import { directParagraphAlignment } from "../paragraphAlignment.js";
2
4
  import { directionFromBidi } from "../paragraphDirection.js";
3
5
  //#region src/prosemirror/commands/propertyChangeScope.ts
4
6
  /**
7
+ * Remove selected entries from an oldest-to-newest paragraph-property chain.
8
+ * A retained entry immediately after a removed run inherits that run's
9
+ * earliest previous snapshot. Only a removed trailing run restores the live
10
+ * paragraph properties. This makes repeated scoped rejection independent of
11
+ * the order in which revisions are resolved.
12
+ */
13
+ const removeParagraphPropertyChanges = (changes, shouldRemove) => {
14
+ const remaining = [];
15
+ let removedPrevious;
16
+ let removedAny = false;
17
+ let removingRun = false;
18
+ for (const change of changes) {
19
+ if (shouldRemove(change)) {
20
+ if (!removingRun) {
21
+ removedPrevious = change.previousFormatting;
22
+ removingRun = true;
23
+ }
24
+ removedAny = true;
25
+ continue;
26
+ }
27
+ if (!removingRun) {
28
+ remaining.push(change);
29
+ continue;
30
+ }
31
+ const rebased = { ...change };
32
+ if (removedPrevious === void 0) Reflect.deleteProperty(rebased, "previousFormatting");
33
+ else rebased.previousFormatting = removedPrevious;
34
+ remaining.push(rebased);
35
+ removedPrevious = void 0;
36
+ removingRun = false;
37
+ }
38
+ if (!removedAny) return { type: "unchanged" };
39
+ if (!removingRun) return {
40
+ type: "keep-live",
41
+ remaining
42
+ };
43
+ return {
44
+ type: "restore-previous",
45
+ remaining,
46
+ previousFormatting: removedPrevious
47
+ };
48
+ };
49
+ /**
5
50
  * Paragraph attrs governed by a `w:pPrChange` — the attrs
6
51
  * `paragraphFormattingToAttrs` (conversion/toProseDoc.ts) can produce from a
7
52
  * parsed `CT_PPrBase` (ECMA-376 §17.13.5.29). Rejecting a pPrChange restores
@@ -17,7 +62,8 @@ import { directionFromBidi } from "../paragraphDirection.js";
17
62
  * `runInWithNext` (`w:specVanish` lives in that rPr)
18
63
  * - load-time style/numbering bookkeeping the command layer cannot recompute
19
64
  * without a style resolver: `numPrFromStyle`, the `list*` rendering attrs,
20
- * `spacingFromDocDefaults`, `spacingFromImplicitDefaultStyle`
65
+ * `alignmentFromStyle`, `spacingFromDocDefaults`,
66
+ * `spacingFromImplicitDefaultStyle`
21
67
  */
22
68
  const PPR_CHANGE_SCOPED_ATTR_KEYS = [
23
69
  "styleId",
@@ -47,6 +93,20 @@ const PPR_CHANGE_SCOPED_ATTR_KEYS = [
47
93
  "_autospacingBase"
48
94
  ];
49
95
  const PPR_CHANGE_SCOPED_ATTR_KEY_SET = new Set(PPR_CHANGE_SCOPED_ATTR_KEYS);
96
+ /** The in-scope paragraph properties as they stand, for a `w:pPrChange` record. */
97
+ const paragraphPropertiesSnapshot = (node) => {
98
+ const attrs = expectParagraphAttrs(node);
99
+ const snapshot = {};
100
+ for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) {
101
+ if (key === "alignment") continue;
102
+ const value = attrs[key];
103
+ if (key === "hangingIndent" && value === false && attrs._originalFormatting?.hangingIndent === void 0) continue;
104
+ if (value !== null && value !== void 0) snapshot[key] = value;
105
+ }
106
+ const directAlignment = directParagraphAlignment(attrs);
107
+ if (directAlignment !== void 0) snapshot["alignment"] = directAlignment;
108
+ return snapshot;
109
+ };
50
110
  /**
51
111
  * Parser-shaped `ParagraphFormatting` keys that must NOT merge through to
52
112
  * attrs verbatim: they either normalize to a differently named attr
@@ -108,10 +168,12 @@ const PPR_CHANGE_SCOPED_FORMATTING_KEYS = [
108
168
  * the scoped set (editor-created list suggestions snapshot list-rendering
109
169
  * bookkeeping attrs) merge through 1:1 so their pre-change values restore too.
110
170
  */
111
- function paragraphRejectAttrPatch(previousFormatting) {
171
+ function paragraphRejectAttrPatch(previousFormatting, alignmentFromStyle) {
112
172
  const prev = previousFormatting ?? {};
113
173
  const patch = {};
114
174
  for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) patch[key] = Object.hasOwn(prev, key) ? prev[key] ?? null : null;
175
+ if (!Object.hasOwn(prev, "alignment")) patch["alignment"] = alignmentFromStyle ?? null;
176
+ patch["alignmentFromStyle"] = alignmentFromStyle;
115
177
  if (!Object.hasOwn(prev, "direction")) patch["direction"] = directionFromBidi(prev.bidi);
116
178
  if (!Object.hasOwn(prev, "_autospacingBase")) patch["_autospacingBase"] = autospacingBaseFromFormatting(prev);
117
179
  for (const [key, value] of Object.entries(prev)) {
@@ -241,4 +303,4 @@ function isRecord(value) {
241
303
  return typeof value === "object" && value !== null && !Array.isArray(value);
242
304
  }
243
305
  //#endregion
244
- export { PPR_CHANGE_SCOPED_ATTR_KEYS, paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, sectionRejectProperties, tableCellRejectAttrPatch, tableRejectAttrPatch, tableRowRejectAttrPatch };
306
+ export { PPR_CHANGE_SCOPED_ATTR_KEYS, paragraphPropertiesSnapshot, paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, removeParagraphPropertyChanges, sectionRejectProperties, tableCellRejectAttrPatch, tableRejectAttrPatch, tableRowRejectAttrPatch };
@@ -2,9 +2,7 @@ import { document_d_exports } from "../../types/document.js";
2
2
  import { TableAttrs, TableCellAttrs, TableRowAttrs } from "../schema/nodes.js";
3
3
  import { Mark, Node } from "prosemirror-model";
4
4
  //#region src/prosemirror/conversion/fromProseDoc.d.ts
5
- /**
6
- * Convert a ProseMirror document to our Document type
7
- */
5
+ /** Convert a ProseMirror document to the document model. */
8
6
  declare function fromProseDoc(pmDoc: Node, baseDocument?: document_d_exports.Document): document_d_exports.Document;
9
7
  /**
10
8
  * Convert ProseMirror marks to TextFormatting
@@ -32,6 +30,6 @@ declare function updateDocumentContent(originalDocument: document_d_exports.Docu
32
30
  * Used for converting edited header/footer PM content back to the document
33
31
  * model.
34
32
  */
35
- declare function proseDocToBlocks(pmDoc: Node): document_d_exports.BlockContent[];
33
+ declare function proseDocToBlocks(pmDoc: Node, baseContent?: document_d_exports.BlockContent[]): document_d_exports.BlockContent[];
36
34
  //#endregion
37
35
  export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, tableAttrsToFormatting, tableCellAttrsToFormatting, tableRowAttrsToFormatting, updateDocumentContent };
@@ -1,5 +1,8 @@
1
1
  import { numPrEqual } from "../../docx/numberingParser.js";
2
+ import { copyParagraphPropertySource, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceTransferId, linkParagraphPropertySourceCandidate, recreateProseNodeWithParagraphPropertySource } from "../../docx/paragraphPropertySource.js";
3
+ import { visitDocxParagraphs } from "../../docx/paragraphTraversal.js";
2
4
  import { ShapeOutlineStyleSchema, narrowEnum } from "../../docx/parserEnums.js";
5
+ import { DATE_UTC_ATTRIBUTE } from "../../docx/trackedChangeInfo.js";
3
6
  import { OUTLINE_STYLE_CSS_ALIASES, normalizeShapeTextAnchor } from "../../types/documentEnumValues.js";
4
7
  import { canonicalJson } from "../../utils/canonicalJson.js";
5
8
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
@@ -7,8 +10,10 @@ import { pixelsToEmu } from "../../utils/units.js";
7
10
  import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunPropertyChangeMarkAttrs, expectRunShadingMarkAttrs, expectSdtAttrs, expectShapeAttrs, expectStrikeMarkAttrs, expectSymbolAttrs, expectTabAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../attrs/index.js";
8
11
  import { autospacingMatchesBase, hasAutospacingBaseSide } from "../autospacingBase.js";
9
12
  import { expectBookmarkBoundaryAttrs } from "../bookmarkBoundaryAttrs.js";
13
+ import { paragraphRejectAttrPatch, paragraphRejectOriginalFormatting, removeParagraphPropertyChanges } from "../commands/propertyChangeScope.js";
10
14
  import { applyRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
11
15
  import { resolveNumberedRefFields } from "../numberedRefFields.js";
16
+ import { directParagraphAlignment } from "../paragraphAlignment.js";
12
17
  import { directionToBidi } from "../paragraphDirection.js";
13
18
  import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
14
19
  import { parseShapeGeometryAdjustments } from "../shapeGeometryAdjustments.js";
@@ -76,12 +81,68 @@ function textBoxWrapFromAttrs(attrs) {
76
81
  if (attrs.distRight !== void 0) wrap.distR = pixelsToEmu(attrs.distRight);
77
82
  return wrap;
78
83
  }
79
- /**
80
- * Convert a ProseMirror document to our Document type
81
- */
84
+ const assignUniqueParagraph = (paragraphs, paraId, paragraph) => {
85
+ const existing = paragraphs.get(paraId);
86
+ if (!paragraphs.has(paraId) || existing === paragraph) {
87
+ paragraphs.set(paraId, paragraph);
88
+ return;
89
+ }
90
+ paragraphs.set(paraId, null);
91
+ };
92
+ const uniqueParagraphsById = (content, includeTransferIds = false) => {
93
+ const paragraphs = /* @__PURE__ */ new Map();
94
+ visitDocxParagraphs({ documentBody: { content } }, (paragraph) => {
95
+ const { paraId } = paragraph;
96
+ if (paraId) assignUniqueParagraph(paragraphs, paraId, paragraph);
97
+ const transferId = includeTransferIds ? getParagraphPropertySourceTransferId(paragraph) : void 0;
98
+ if (transferId) assignUniqueParagraph(paragraphs, transferId, paragraph);
99
+ });
100
+ return paragraphs;
101
+ };
102
+ const restoreParagraphPropertySources = (content, baseContent, linkedTargets, linkedSources) => {
103
+ const baseParagraphs = uniqueParagraphsById(baseContent, true);
104
+ for (const [paraId, paragraph] of uniqueParagraphsById(content)) {
105
+ const baseParagraph = baseParagraphs.get(paraId);
106
+ if (!paragraph || !baseParagraph || linkedTargets.has(paragraph) || linkedSources.has(baseParagraph) || !getParagraphPropertySource(baseParagraph)) continue;
107
+ copyParagraphPropertySource(paragraph, baseParagraph);
108
+ const baseFormatting = baseParagraph.formatting;
109
+ if (!baseFormatting?.numPr || !baseFormatting.numPrFromStyle || !numPrEqual(baseFormatting.numPr, baseFormatting.numPrFromStyle)) continue;
110
+ const { numPr, numPrFromStyle, ...authoredFormatting } = baseFormatting;
111
+ if (canonicalJson(paragraph.formatting ?? {}) !== canonicalJson(authoredFormatting)) continue;
112
+ paragraph.formatting = {
113
+ ...paragraph.formatting,
114
+ numPr,
115
+ numPrFromStyle
116
+ };
117
+ }
118
+ };
119
+ const restoreLinkedParagraphPropertySources = (content) => {
120
+ const targetsBySource = /* @__PURE__ */ new Map();
121
+ const linkedTargets = /* @__PURE__ */ new Set();
122
+ visitDocxParagraphs({ documentBody: { content } }, (paragraph) => {
123
+ const source = getParagraphPropertySourceCandidate(paragraph);
124
+ if (!source) return;
125
+ linkedTargets.add(paragraph);
126
+ const targets = targetsBySource.get(source);
127
+ if (targets) targets.push(paragraph);
128
+ else targetsBySource.set(source, [paragraph]);
129
+ });
130
+ for (const [source, targets] of targetsBySource) if (targets.length === 1) {
131
+ const target = targets.at(0);
132
+ if (target) copyParagraphPropertySource(target, source);
133
+ }
134
+ return {
135
+ targets: linkedTargets,
136
+ sources: new Set(targetsBySource.keys())
137
+ };
138
+ };
139
+ /** Convert a ProseMirror document to the document model. */
82
140
  function fromProseDoc(pmDoc, baseDocument) {
83
141
  assertValidProseMirrorDocument(pmDoc, "Cannot convert invalid ProseMirror document to DOCX model");
84
- const documentBody = { content: extractBlocks(pmDoc) };
142
+ const blocks = extractBlocks(pmDoc);
143
+ const linkedSources = restoreLinkedParagraphPropertySources(blocks);
144
+ if (baseDocument) restoreParagraphPropertySources(blocks, baseDocument.package.document.content, linkedSources.targets, linkedSources.sources);
145
+ const documentBody = { content: blocks };
85
146
  if (baseDocument?.package.document.finalSectionProperties) documentBody.finalSectionProperties = baseDocument.package.document.finalSectionProperties;
86
147
  if (baseDocument?.package.document.sections) documentBody.sections = baseDocument.package.document.sections;
87
148
  if (baseDocument?.package.document.comments) documentBody.comments = baseDocument.package.document.comments;
@@ -131,6 +192,22 @@ function stripSuggestedInlineMarks(marks) {
131
192
  */
132
193
  function stripSuggestedNodeAttrs(node) {
133
194
  const name = node.type.name;
195
+ if (name === "paragraph") {
196
+ const propertyChanges = expectParagraphAttrs(node)._propertyChanges;
197
+ if (!Array.isArray(propertyChanges)) return null;
198
+ if (!propertyChanges.some(({ info }) => info.provenance === "suggested")) return null;
199
+ const removal = removeParagraphPropertyChanges(propertyChanges, ({ info }) => info.provenance === "suggested");
200
+ if (removal.type === "unchanged") return null;
201
+ const nextAttrs = {
202
+ ...node.attrs,
203
+ _propertyChanges: removal.remaining.length > 0 ? removal.remaining : null
204
+ };
205
+ if (removal.type === "restore-previous") {
206
+ Object.assign(nextAttrs, paragraphRejectAttrPatch(removal.previousFormatting));
207
+ nextAttrs["_originalFormatting"] = paragraphRejectOriginalFormatting(removal.previousFormatting, nextAttrs["_originalFormatting"]);
208
+ }
209
+ return nextAttrs;
210
+ }
134
211
  if (name === "tableRow") {
135
212
  const rowAttrs = expectTableRowAttrs(node);
136
213
  if (rowAttrs.trDel?.provenance === "suggested") return {
@@ -192,7 +269,10 @@ function mapSuggestionStrippedNode(node) {
192
269
  });
193
270
  if (!changed) return node;
194
271
  const content = Fragment.fromArray(children);
195
- return nextAttrs === null ? node.copy(content) : node.type.create(nextAttrs, content, node.marks);
272
+ return recreateProseNodeWithParagraphPropertySource(node, {
273
+ ...nextAttrs === null ? {} : { attrs: nextAttrs },
274
+ content
275
+ });
196
276
  }
197
277
  /**
198
278
  * Strip suggested (AI-proposed) tracked changes from a document so it
@@ -208,10 +288,10 @@ function materializeNumberedRefValues(doc) {
208
288
  const visit = (node) => {
209
289
  if (node.type.name === "field" || node.type.name === "structuredField") {
210
290
  const displayText = results.get(node);
211
- if (displayText !== void 0) return node.type.create({
291
+ if (displayText !== void 0) return recreateProseNodeWithParagraphPropertySource(node, { attrs: {
212
292
  ...node.attrs,
213
293
  displayText
214
- }, node.content, node.marks);
294
+ } });
215
295
  return node;
216
296
  }
217
297
  if (node.childCount === 0) return node;
@@ -222,7 +302,7 @@ function materializeNumberedRefValues(doc) {
222
302
  children.push(mappedChild);
223
303
  changed ||= mappedChild !== child;
224
304
  });
225
- return changed ? node.copy(Fragment.fromArray(children)) : node;
305
+ return changed ? recreateProseNodeWithParagraphPropertySource(node, { content: Fragment.fromArray(children) }) : node;
226
306
  };
227
307
  return visit(doc);
228
308
  }
@@ -541,12 +621,20 @@ function convertPMParagraph(node, documentCounts, textBoxAnchorMarkers) {
541
621
  else if (attrs.sectionBreakType) paragraph.sectionProperties = { sectionStart: attrs.sectionBreakType };
542
622
  if (attrs._propertyChanges && attrs._propertyChanges.length > 0) paragraph.propertyChanges = attrs._propertyChanges.map(propertyChangeFromAttrs);
543
623
  if (attrs.pPrMark) paragraph.pPrMark = attrs.pPrMark;
624
+ linkParagraphPropertySourceCandidate(paragraph, node);
544
625
  return paragraph;
545
626
  }
546
627
  const propertyChangeFromAttrs = (change) => {
547
- const { previousFormatting, currentFormatting, ...info } = change;
548
- if (previousFormatting === void 0) return currentFormatting === void 0 ? info : {
549
- ...info,
628
+ const { previousFormatting, currentFormatting, info, ...changeInfo } = change;
629
+ const serializedInfo = { ...info };
630
+ Reflect.deleteProperty(serializedInfo, "provenance");
631
+ Reflect.deleteProperty(serializedInfo, "suggestionId");
632
+ const normalizedChangeInfo = {
633
+ ...changeInfo,
634
+ info: serializedInfo
635
+ };
636
+ if (previousFormatting === void 0) return currentFormatting === void 0 ? normalizedChangeInfo : {
637
+ ...normalizedChangeInfo,
550
638
  currentFormatting
551
639
  };
552
640
  const { numPr, ...previousWithoutNumPr } = previousFormatting;
@@ -555,7 +643,7 @@ const propertyChangeFromAttrs = (change) => {
555
643
  numPr
556
644
  };
557
645
  return {
558
- ...info,
646
+ ...normalizedChangeInfo,
559
647
  previousFormatting: normalizedPrevious,
560
648
  ...currentFormatting !== void 0 && { currentFormatting }
561
649
  };
@@ -576,6 +664,7 @@ function assignBooleanToggle(result, attrs, orig, key) {
576
664
  else result[key] = value;
577
665
  }
578
666
  function paragraphAttrsToFormatting(attrs) {
667
+ const directAlignment = directParagraphAlignment(attrs);
579
668
  const spaceBefore = Reflect.get(attrs, "spaceBefore");
580
669
  const spaceAfter = Reflect.get(attrs, "spaceAfter");
581
670
  const beforeHasAutospacingBase = hasAutospacingBaseSide(attrs._autospacingBase, "before");
@@ -609,8 +698,8 @@ function paragraphAttrsToFormatting(attrs) {
609
698
  if (attrs.lineSpacingRule) result.lineSpacingRule = attrs.lineSpacingRule;
610
699
  else Reflect.deleteProperty(result, "lineSpacingRule");
611
700
  }
612
- if (attrs.alignment !== (orig.alignment ?? void 0)) if (attrs.alignment) result.alignment = attrs.alignment;
613
- else delete result.alignment;
701
+ if (directAlignment === void 0) Reflect.deleteProperty(result, "alignment");
702
+ else result.alignment = directAlignment;
614
703
  if (isStyleSourcedNumPr(attrs)) {
615
704
  delete result.numPr;
616
705
  delete result.numPrFromStyle;
@@ -636,9 +725,9 @@ function paragraphAttrsToFormatting(attrs) {
636
725
  }
637
726
  const outlineLevel = Reflect.get(attrs, "outlineLevel");
638
727
  const bidi = directionToBidi(attrs.direction);
639
- if (!(attrs.alignment || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || hasDirectLineSpacing || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
728
+ if (!(directAlignment !== void 0 || shouldSerializeSpaceBefore || shouldSerializeSpaceAfter || beforeAutospacingEdited || afterAutospacingEdited || hasDirectLineSpacing || attrs.snapToGrid != null || attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine || attrs.numPr || attrs.styleId || attrs.borders || attrs.shading || attrs.tabs || typeof outlineLevel === "number" || attrs.contextualSpacing || attrs.spacingExplicit || bidi != null || attrs.pageBreakBefore != null || attrs.widowControl != null || attrs.kinsoku != null || attrs.overflowPunctuation != null || attrs.suppressAutoHyphens != null)) return;
640
729
  const f = {};
641
- if (attrs.alignment) f.alignment = attrs.alignment;
730
+ if (directAlignment !== void 0) f.alignment = directAlignment;
642
731
  if (shouldSerializeSpaceBefore) f.spaceBefore = spaceBefore;
643
732
  if (beforeAutospacingEdited) f.beforeAutospacing = false;
644
733
  if (shouldSerializeSpaceAfter) f.spaceAfter = spaceAfter;
@@ -794,6 +883,10 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
794
883
  id: changeAttrs.revisionId,
795
884
  author: changeAttrs.author || "Unknown",
796
885
  ...changeAttrs.date ? { date: changeAttrs.date } : {},
886
+ ...changeAttrs.utcDate ? { utcDate: {
887
+ attribute: DATE_UTC_ATTRIBUTE,
888
+ value: changeAttrs.utcDate
889
+ } } : {},
797
890
  ...changeAttrs.initials ? { initials: changeAttrs.initials } : {}
798
891
  };
799
892
  if (insertionMark) content.push({
@@ -819,6 +912,10 @@ function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, te
819
912
  author: changeAttrs.author || "Unknown"
820
913
  };
821
914
  if (changeAttrs.date) info.date = changeAttrs.date;
915
+ if (changeAttrs.utcDate) info.utcDate = {
916
+ attribute: DATE_UTC_ATTRIBUTE,
917
+ value: changeAttrs.utcDate
918
+ };
822
919
  if (changeAttrs.initials) info.initials = changeAttrs.initials;
823
920
  let type;
824
921
  if (insertionMark) type = changeAttrs.moveKind === "moveTo" ? "moveTo" : "insertion";
@@ -1934,6 +2031,10 @@ function convertPMTableRow(node, documentCounts, activeVerticalMerges) {
1934
2031
  id: attrs.trIns.revisionId,
1935
2032
  author: attrs.trIns.author,
1936
2033
  ...attrs.trIns.date != null && { date: attrs.trIns.date },
2034
+ ...attrs.trIns.utcDate != null && { utcDate: {
2035
+ attribute: "w16du:dateUtc",
2036
+ value: attrs.trIns.utcDate
2037
+ } },
1937
2038
  ...attrs.trIns.initials != null && { initials: attrs.trIns.initials }
1938
2039
  }
1939
2040
  };
@@ -1943,6 +2044,10 @@ function convertPMTableRow(node, documentCounts, activeVerticalMerges) {
1943
2044
  id: attrs.trDel.revisionId,
1944
2045
  author: attrs.trDel.author,
1945
2046
  ...attrs.trDel.date != null && { date: attrs.trDel.date },
2047
+ ...attrs.trDel.utcDate != null && { utcDate: {
2048
+ attribute: "w16du:dateUtc",
2049
+ value: attrs.trDel.utcDate
2050
+ } },
1946
2051
  ...attrs.trDel.initials != null && { initials: attrs.trDel.initials }
1947
2052
  }
1948
2053
  };
@@ -2025,6 +2130,10 @@ function convertPMTableCell(node, documentCounts) {
2025
2130
  id: attrs.cellMarker.info.revisionId,
2026
2131
  author: attrs.cellMarker.info.author,
2027
2132
  ...attrs.cellMarker.info.date != null && { date: attrs.cellMarker.info.date },
2133
+ ...attrs.cellMarker.info.utcDate != null && { utcDate: {
2134
+ attribute: "w16du:dateUtc",
2135
+ value: attrs.cellMarker.info.utcDate
2136
+ } },
2028
2137
  ...attrs.cellMarker.info.initials != null && { initials: attrs.cellMarker.info.initials }
2029
2138
  };
2030
2139
  if (attrs.cellMarker.kind === "merge") cell.structuralChange = {
@@ -2188,8 +2297,11 @@ function updateDocumentContent(originalDocument, pmDoc) {
2188
2297
  * Used for converting edited header/footer PM content back to the document
2189
2298
  * model.
2190
2299
  */
2191
- function proseDocToBlocks(pmDoc) {
2192
- return extractBlocks(pmDoc);
2300
+ function proseDocToBlocks(pmDoc, baseContent) {
2301
+ const blocks = extractBlocks(pmDoc);
2302
+ const linkedSources = restoreLinkedParagraphPropertySources(blocks);
2303
+ if (baseContent) restoreParagraphPropertySources(blocks, baseContent, linkedSources.targets, linkedSources.sources);
2304
+ return blocks;
2193
2305
  }
2194
2306
  //#endregion
2195
2307
  export { fromProseDoc, marksToTextFormatting, proseDocToBlocks, standaloneTableCellFromProseMirror, tableAttrsToFormatting, tableCellAttrsToFormatting, tableRowAttrsToFormatting, updateDocumentContent };
@@ -1,4 +1,5 @@
1
1
  import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
2
+ import { linkProseParagraphPropertySource, recreateProseNodeWithParagraphPropertySource } from "../../docx/paragraphPropertySource.js";
2
3
  import { createStyleEngine } from "../../style-engine/styleEngine.js";
3
4
  import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
4
5
  import { mergeParagraphFormatting, mergeParagraphTabStops } from "../../utils/paragraphFormattingMerge.js";
@@ -119,18 +120,18 @@ function toProseDoc(document, options) {
119
120
  const firstConverted = converted.at(0);
120
121
  if (pbPos === "before" && converted.length === 1 && firstConverted?.type.name === "paragraph" && firstConverted.content.size === 0 && document.package.settings?.splitPageBreakAndParagraphMark !== true) {
121
122
  const paragraph = firstConverted;
122
- converted[0] = paragraph.type.create({
123
+ converted[0] = recreateProseNodeWithParagraphPropertySource(paragraph, { attrs: {
123
124
  ...paragraph.attrs,
124
125
  _pageBreakCarrier: true
125
- }, paragraph.content, paragraph.marks);
126
+ } });
126
127
  }
127
128
  if (pbPos === "after") {
128
129
  const paragraphIndex = converted.findIndex((node) => node.type.name === "paragraph");
129
130
  const trailingParagraph = paragraphIndex >= 0 ? converted.at(paragraphIndex) : void 0;
130
- if (trailingParagraph) converted[paragraphIndex] = trailingParagraph.type.create({
131
+ if (trailingParagraph) converted[paragraphIndex] = recreateProseNodeWithParagraphPropertySource(trailingParagraph, { attrs: {
131
132
  ...trailingParagraph.attrs,
132
133
  _trailingPageBreak: true
133
- }, trailingParagraph.content, trailingParagraph.marks);
134
+ } });
134
135
  }
135
136
  out.push(...converted);
136
137
  if (pbPos === "after") out.push(schema.node("pageBreak"));
@@ -323,7 +324,9 @@ function convertParagraph(paragraph, styleResolver, nextHyperlinkInstanceIndex,
323
324
  }
324
325
  if (bookmarksArr) attrs.bookmarks = bookmarksArr;
325
326
  if (emptyHyperlinks) attrs._emptyHyperlinks = emptyHyperlinks;
326
- return schema.node("paragraph", attrs, inlineNodes);
327
+ const proseParagraph = schema.node("paragraph", attrs, inlineNodes);
328
+ linkProseParagraphPropertySource(proseParagraph, paragraph);
329
+ return proseParagraph;
327
330
  }
328
331
  const resolveParagraphStyleFontFamily = (styleId, styleResolver) => {
329
332
  let style = styleId ? styleResolver.getStyle(styleId) : styleResolver.getDefaultParagraphStyle();
@@ -402,6 +405,7 @@ function convertTrackedChange(change, markType, nextHyperlinkInstanceIndex, getI
402
405
  revisionId: change.info.id,
403
406
  author: change.info.author,
404
407
  date: change.info.date ?? null,
408
+ utcDate: change.info.utcDate?.value ?? null,
405
409
  initials: change.info.initials ?? null,
406
410
  moveKind
407
411
  });
@@ -466,6 +470,7 @@ function paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOver
466
470
  if (styleResolver) {
467
471
  stylePpr = styleResolver.resolveParagraphStyleInTable(styleId, tableParagraphOverlay).paragraphFormatting;
468
472
  set("alignment", formatting?.alignment ?? stylePpr?.alignment);
473
+ set("alignmentFromStyle", stylePpr?.alignment);
469
474
  set("spaceBefore", formatting?.spaceBefore ?? stylePpr?.spaceBefore);
470
475
  set("spaceAfter", formatting?.spaceAfter ?? stylePpr?.spaceAfter);
471
476
  set("lineSpacing", formatting?.lineSpacing ?? stylePpr?.lineSpacing);
@@ -936,7 +941,9 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
936
941
  trIns: {
937
942
  revisionId: row.structuralChange.info.id,
938
943
  author: row.structuralChange.info.author,
939
- date: row.structuralChange.info.date ?? null
944
+ date: row.structuralChange.info.date ?? null,
945
+ ...row.structuralChange.info.utcDate ? { utcDate: row.structuralChange.info.utcDate.value } : {},
946
+ ...row.structuralChange.info.initials ? { initials: row.structuralChange.info.initials } : {}
940
947
  }
941
948
  };
942
949
  else if (row.structuralChange?.type === "tableRowDeletion") attrs = {
@@ -944,7 +951,9 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
944
951
  trDel: {
945
952
  revisionId: row.structuralChange.info.id,
946
953
  author: row.structuralChange.info.author,
947
- date: row.structuralChange.info.date ?? null
954
+ date: row.structuralChange.info.date ?? null,
955
+ ...row.structuralChange.info.utcDate ? { utcDate: row.structuralChange.info.utcDate.value } : {},
956
+ ...row.structuralChange.info.initials ? { initials: row.structuralChange.info.initials } : {}
948
957
  }
949
958
  };
950
959
  const numCells = row.cells.length;
@@ -1084,7 +1093,9 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1084
1093
  info: {
1085
1094
  revisionId: cell.structuralChange.info.id,
1086
1095
  author: cell.structuralChange.info.author,
1087
- date: cell.structuralChange.info.date ?? null
1096
+ date: cell.structuralChange.info.date ?? null,
1097
+ ...cell.structuralChange.info.utcDate ? { utcDate: cell.structuralChange.info.utcDate.value } : {},
1098
+ ...cell.structuralChange.info.initials ? { initials: cell.structuralChange.info.initials } : {}
1088
1099
  }
1089
1100
  };
1090
1101
  else if (cell.structuralChange?.type === "tableCellDeletion") attrs.cellMarker = {
@@ -1092,7 +1103,9 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1092
1103
  info: {
1093
1104
  revisionId: cell.structuralChange.info.id,
1094
1105
  author: cell.structuralChange.info.author,
1095
- date: cell.structuralChange.info.date ?? null
1106
+ date: cell.structuralChange.info.date ?? null,
1107
+ ...cell.structuralChange.info.utcDate ? { utcDate: cell.structuralChange.info.utcDate.value } : {},
1108
+ ...cell.structuralChange.info.initials ? { initials: cell.structuralChange.info.initials } : {}
1096
1109
  }
1097
1110
  };
1098
1111
  else if (cell.structuralChange?.type === "tableCellMerge") attrs.cellMarker = {
@@ -1100,7 +1113,9 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1100
1113
  info: {
1101
1114
  revisionId: cell.structuralChange.info.id,
1102
1115
  author: cell.structuralChange.info.author,
1103
- date: cell.structuralChange.info.date ?? null
1116
+ date: cell.structuralChange.info.date ?? null,
1117
+ ...cell.structuralChange.info.utcDate ? { utcDate: cell.structuralChange.info.utcDate.value } : {},
1118
+ ...cell.structuralChange.info.initials ? { initials: cell.structuralChange.info.initials } : {}
1104
1119
  },
1105
1120
  ...cell.structuralChange.verticalMerge !== void 0 ? { verticalMerge: cell.structuralChange.verticalMerge } : {},
1106
1121
  ...cell.structuralChange.verticalMergeOriginal !== void 0 ? { verticalMergeOriginal: cell.structuralChange.verticalMergeOriginal } : {}
@@ -1965,10 +1980,10 @@ function headerFooterToProseDoc(content, options) {
1965
1980
  if (isDetachedWatermarkHost) {
1966
1981
  const paragraphNodeIndex = paragraphNodes.findIndex(({ type }) => type.name === "paragraph");
1967
1982
  const paragraphNode = paragraphNodes[paragraphNodeIndex];
1968
- if (paragraphNode) paragraphNodes[paragraphNodeIndex] = paragraphNode.type.create({
1983
+ if (paragraphNode) paragraphNodes[paragraphNodeIndex] = recreateProseNodeWithParagraphPropertySource(paragraphNode, { attrs: {
1969
1984
  ...paragraphNode.attrs,
1970
1985
  _detachedWatermarkHost: true
1971
- }, paragraphNode.content, paragraphNode.marks);
1986
+ } });
1972
1987
  }
1973
1988
  out.push(...paragraphNodes);
1974
1989
  } else if (block.type === "table") out.push(convertTable(block, styleResolver, conversionContext));
@@ -1,8 +1,10 @@
1
+ import { PARAGRAPH_ALIGNMENT_VALUES } from "../../../types/documentEnumValues.js";
1
2
  import { paragraphToStyle } from "../../../utils/formatToStyle.js";
2
3
  import { collectHeadings } from "../../../utils/headingCollector.js";
3
4
  import { tableOfContentsStyleLevel } from "../../../utils/tableOfContentsStyle.js";
4
5
  import { expectParagraphAttrs } from "../../attrs/index.js";
5
6
  import { autospacingMatchesBase } from "../../autospacingBase.js";
7
+ import { directParagraphAlignment } from "../../paragraphAlignment.js";
6
8
  import { directionIsRtl } from "../../paragraphDirection.js";
7
9
  import { listAttrsFromResolvedStyle, paragraphAttrsFromResolvedStyle } from "../../styles/resolvedStyleAttrs.js";
8
10
  import { createNodeExtension } from "../create.js";
@@ -95,6 +97,7 @@ function cssTextAlignToAlignment(value) {
95
97
  default: return;
96
98
  }
97
99
  }
100
+ const parseParagraphAlignment = (value) => PARAGRAPH_ALIGNMENT_VALUES.find((alignment) => alignment === value);
98
101
  /**
99
102
  * Parse CSS line-height to twips.
100
103
  * - Unitless multiplier (e.g. "1.5"): 240 twips * multiplier (single=240)
@@ -182,6 +185,7 @@ const paragraphNodeSpec = {
182
185
  paraId: { default: null },
183
186
  textId: { default: null },
184
187
  alignment: { default: null },
188
+ alignmentFromStyle: { default: void 0 },
185
189
  kinsoku: { default: null },
186
190
  overflowPunctuation: { default: null },
187
191
  suppressAutoHyphens: { default: null },
@@ -249,23 +253,32 @@ const paragraphNodeSpec = {
249
253
  if (!(dom instanceof HTMLElement)) return false;
250
254
  const element = dom;
251
255
  const paraId = element.dataset["paraId"];
252
- const alignment = element.dataset["alignment"];
256
+ const alignment = parseParagraphAlignment(element.dataset["alignment"]);
257
+ const authoredAlignment = parseParagraphAlignment(element.dataset["directAlignment"]);
258
+ const alignmentFromStyle = parseParagraphAlignment(element.dataset["alignmentFromStyle"]);
253
259
  const styleId = element.dataset["styleId"];
254
260
  const tableOfContentsLevel = Number(element.dataset["tableOfContentsLevel"]);
255
261
  const sectionBreakType = element.dataset["sectionBreak"];
256
262
  const attrs = {
257
263
  ...paraId ? { paraId } : {},
258
264
  ...alignment ? { alignment } : {},
265
+ ...alignmentFromStyle ? { alignmentFromStyle } : {},
259
266
  ...styleId ? { styleId } : {},
260
267
  ...Number.isSafeInteger(tableOfContentsLevel) && tableOfContentsLevel > 0 ? { _tableOfContentsLevel: tableOfContentsLevel } : {},
261
268
  ...sectionBreakType ? { sectionBreakType } : {}
262
269
  };
263
270
  const styleAttrs = extractParagraphAttrsFromStyle(element);
264
271
  const mergedAlignment = attrs.alignment || styleAttrs.alignment;
272
+ const mergedDirectAlignment = authoredAlignment ?? (attrs.alignmentFromStyle === void 0 ? mergedAlignment : void 0);
273
+ const originalFormatting = {
274
+ ...styleId ? { styleId } : {},
275
+ ...mergedDirectAlignment ? { alignment: mergedDirectAlignment } : {}
276
+ };
265
277
  return {
266
278
  ...styleAttrs,
267
279
  ...attrs,
268
- ...mergedAlignment !== void 0 ? { alignment: mergedAlignment } : {}
280
+ ...mergedAlignment !== void 0 ? { alignment: mergedAlignment } : {},
281
+ ...Object.keys(originalFormatting).length > 0 ? { _originalFormatting: originalFormatting } : {}
269
282
  };
270
283
  }
271
284
  }, ...[
@@ -279,8 +292,10 @@ const paragraphNodeSpec = {
279
292
  tag,
280
293
  getAttrs(dom) {
281
294
  const level = Number.parseInt(tag.charAt(1), 10);
295
+ const styleAttrs = extractParagraphAttrsFromStyle(dom);
282
296
  return {
283
- ...extractParagraphAttrsFromStyle(dom),
297
+ ...styleAttrs,
298
+ ...styleAttrs.alignment ? { _originalFormatting: { alignment: styleAttrs.alignment } } : {},
284
299
  styleId: `Heading${level}`,
285
300
  outlineLevel: level - 1
286
301
  };
@@ -295,6 +310,9 @@ const paragraphNodeSpec = {
295
310
  if (listClass) domAttrs["class"] = listClass;
296
311
  if (attrs.paraId) domAttrs["data-para-id"] = attrs.paraId;
297
312
  if (attrs.alignment) domAttrs["data-alignment"] = attrs.alignment;
313
+ const directAlignment = directParagraphAlignment(attrs);
314
+ if (directAlignment) domAttrs["data-direct-alignment"] = directAlignment;
315
+ if (attrs.alignmentFromStyle) domAttrs["data-alignment-from-style"] = attrs.alignmentFromStyle;
298
316
  if (attrs.styleId) domAttrs["data-style-id"] = attrs.styleId;
299
317
  if (typeof attrs._tableOfContentsLevel === "number") domAttrs["data-table-of-contents-level"] = String(attrs._tableOfContentsLevel);
300
318
  if (attrs.listMarker) domAttrs["data-list-marker"] = attrs.listMarker;
@@ -373,7 +391,27 @@ function setParagraphAttrsCmd(attrs) {
373
391
  };
374
392
  }
375
393
  function makeSetAlignment(alignment) {
376
- return (state, dispatch) => setParagraphAttr("alignment", alignment)(state, dispatch);
394
+ return (state, dispatch) => {
395
+ const { $from, $to } = state.selection;
396
+ if (!dispatch) return true;
397
+ let tr = state.tr;
398
+ const seen = /* @__PURE__ */ new Set();
399
+ state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
400
+ if (node.type.name !== "paragraph" || seen.has(pos)) return;
401
+ seen.add(pos);
402
+ const attrs = expectParagraphAttrs(node);
403
+ tr = tr.setNodeMarkup(pos, void 0, {
404
+ ...node.attrs,
405
+ alignment,
406
+ _originalFormatting: {
407
+ ...attrs._originalFormatting,
408
+ alignment
409
+ }
410
+ });
411
+ });
412
+ dispatch(tr.scrollIntoView());
413
+ return true;
414
+ };
377
415
  }
378
416
  function makeSetLineSpacing(value, rule = "auto") {
379
417
  return (state, dispatch) => setParagraphAttrsCmd({
@@ -474,6 +512,12 @@ function makeApplyStyle(schema) {
474
512
  styleId,
475
513
  ...resolvedAttrs.styleName ? { styleName: resolvedAttrs.styleName } : {}
476
514
  }));
515
+ const originalFormatting = expectParagraphAttrs(node)._originalFormatting;
516
+ if (originalFormatting?.alignment !== void 0) {
517
+ const formattingWithoutDirectAlignment = { ...originalFormatting };
518
+ Reflect.deleteProperty(formattingWithoutDirectAlignment, "alignment");
519
+ newAttrs["_originalFormatting"] = Object.keys(formattingWithoutDirectAlignment).length > 0 ? formattingWithoutDirectAlignment : null;
520
+ }
477
521
  const listAttrs = listAttrsFromResolvedStyle(resolvedAttrs, resolvedAttrs.numbering);
478
522
  if (listAttrs) Object.assign(newAttrs, listAttrs);
479
523
  }
@@ -1,3 +1,4 @@
1
+ import { setProseParagraphMarkupWithPropertySource } from "../../../docx/paragraphPropertySource.js";
1
2
  import { getTransactionDirtyRange } from "../../../paged-layout/transactionDirtyRange.js";
2
3
  import { detectBaseDirection } from "../../../utils/baseDirection.js";
3
4
  import { directionIsAutoManaged } from "../../paragraphDirection.js";
@@ -62,7 +63,12 @@ const collectBidiUpdatesInRange = (doc, range) => {
62
63
  };
63
64
  const applyBidiUpdates = (state, updates) => {
64
65
  const tr = state.tr;
65
- for (const update of updates) tr.setNodeMarkup(update.pos, void 0, update.attrs);
66
+ for (const update of updates) setProseParagraphMarkupWithPropertySource({
67
+ attrs: update.attrs,
68
+ ownership: "preserve",
69
+ pos: update.pos,
70
+ transaction: tr
71
+ });
66
72
  ignoreTrackedChanges(tr);
67
73
  tr.setMeta(autoBidiDetectionKey, "applied");
68
74
  tr.setMeta("addToHistory", false);