@stll/folio-core 0.56.0 → 0.57.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 (142) hide show
  1. package/dist/ai-edits/apply.js +45 -14
  2. package/dist/ai-edits/batch-claims.d.ts +3 -1
  3. package/dist/ai-edits/batch-claims.js +25 -1
  4. package/dist/ai-edits/table-row-column-mutations.js +17 -3
  5. package/dist/ai-suggestions/apply.js +20 -1
  6. package/dist/compare/inline-atoms.js +40 -16
  7. package/dist/compare/inline-provenance.js +12 -5
  8. package/dist/compat/eigenpal.d.ts +2 -1
  9. package/dist/compat/eigenpal.js +2 -1
  10. package/dist/controller/canonicalClipboard.js +7 -1
  11. package/dist/controller/canonicalComments.d.ts +41 -0
  12. package/dist/controller/canonicalComments.js +152 -0
  13. package/dist/controller/canonicalComposition.d.ts +4 -0
  14. package/dist/controller/canonicalComposition.js +43 -11
  15. package/dist/controller/canonicalInlineProjection.d.ts +6 -1
  16. package/dist/controller/canonicalInlineProjection.js +86 -3
  17. package/dist/controller/canonicalInput.js +10 -1
  18. package/dist/controller/canonicalInputTimer.d.ts +10 -1
  19. package/dist/controller/canonicalInputTimer.js +24 -1
  20. package/dist/controller/canonicalPublicOperations.d.ts +2 -2
  21. package/dist/controller/canonicalPublicOperations.js +107 -15
  22. package/dist/controller/canonicalSelection.d.ts +18 -0
  23. package/dist/controller/canonicalSelection.js +32 -0
  24. package/dist/controller/canonicalSession.d.ts +3 -0
  25. package/dist/controller/canonicalSession.js +58 -24
  26. package/dist/controller/canonicalStoryEditor.d.ts +2 -0
  27. package/dist/controller/canonicalStoryEditor.js +20 -0
  28. package/dist/controller/folioEditor.js +13 -0
  29. package/dist/controller/headerFooterEditorManager.d.ts +1 -1
  30. package/dist/controller/headerFooterEditorManager.js +25 -11
  31. package/dist/controller/hiddenEditorApi.d.ts +13 -2
  32. package/dist/controller/hiddenEditorApi.js +4 -0
  33. package/dist/controller/hiddenEditorManager.d.ts +3 -1
  34. package/dist/controller/hiddenEditorManager.js +166 -13
  35. package/dist/controller/noteEditorManager.d.ts +1 -1
  36. package/dist/controller/noteEditorManager.js +28 -13
  37. package/dist/docx/builtInStyles.js +13 -6
  38. package/dist/docx/containerChildren.gen.d.ts +3 -1
  39. package/dist/docx/containerChildren.gen.js +33 -1
  40. package/dist/docx/fieldParser.js +14 -2
  41. package/dist/docx/hyperlinkParser.d.ts +1 -1
  42. package/dist/docx/hyperlinkParser.js +20 -1
  43. package/dist/docx/inlineWrapperContent.d.ts +4 -4
  44. package/dist/docx/inlineWrapperContent.js +4 -4
  45. package/dist/docx/linkedSdtPreservation.d.ts +10 -0
  46. package/dist/docx/linkedSdtPreservation.js +37 -0
  47. package/dist/docx/paragraphParser.js +88 -67
  48. package/dist/docx/paragraphPropertySource.d.ts +22 -1
  49. package/dist/docx/paragraphPropertySource.js +107 -1
  50. package/dist/docx/preservedRunContent.js +2 -1
  51. package/dist/docx/relsParser.js +26 -2
  52. package/dist/docx/rezip.js +9 -6
  53. package/dist/docx/selectiveSave.js +10 -8
  54. package/dist/docx/serializer/commentSerializer.d.ts +4 -1
  55. package/dist/docx/serializer/commentSerializer.js +7 -3
  56. package/dist/docx/serializer/paragraphSerializer.js +207 -94
  57. package/dist/docx/server/materializeYjsDocx.js +14 -6
  58. package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
  59. package/dist/i18n/messages/ar.gen.js +4 -0
  60. package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
  61. package/dist/i18n/messages/catalogs.gen.js +68 -0
  62. package/dist/i18n/messages/cs.gen.js +4 -0
  63. package/dist/i18n/messages/de.gen.js +4 -0
  64. package/dist/i18n/messages/en.gen.js +4 -0
  65. package/dist/i18n/messages/es.gen.js +4 -0
  66. package/dist/i18n/messages/et.gen.js +4 -0
  67. package/dist/i18n/messages/fr.gen.js +4 -0
  68. package/dist/i18n/messages/he.gen.js +4 -0
  69. package/dist/i18n/messages/hi.gen.js +4 -0
  70. package/dist/i18n/messages/hu.gen.js +4 -0
  71. package/dist/i18n/messages/lt.gen.js +4 -0
  72. package/dist/i18n/messages/lv.gen.js +4 -0
  73. package/dist/i18n/messages/messages.gen.d.ts +4 -0
  74. package/dist/i18n/messages/pl.gen.js +4 -0
  75. package/dist/i18n/messages/pt-BR.gen.js +4 -0
  76. package/dist/i18n/messages/sk.gen.js +4 -0
  77. package/dist/i18n/messages/tr.gen.js +4 -0
  78. package/dist/i18n/messages/zh-CN.gen.js +4 -0
  79. package/dist/index.d.ts +2 -1
  80. package/dist/index.js +2 -1
  81. package/dist/internal/revisionResolutionInline.js +25 -3
  82. package/dist/markdown/renderRuns.js +1 -11
  83. package/dist/panel-layout.d.ts +11 -9
  84. package/dist/panel-layout.js +13 -9
  85. package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
  86. package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
  87. package/dist/prosemirror/attrs/index.js +5 -0
  88. package/dist/prosemirror/commands/comments.js +60 -35
  89. package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
  90. package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
  91. package/dist/prosemirror/commentOps.js +15 -1
  92. package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
  93. package/dist/prosemirror/conversion/toProseDoc.js +119 -18
  94. package/dist/prosemirror/extensions/core/ParagraphExtension.js +22 -5
  95. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
  96. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
  97. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +22 -14
  98. package/dist/prosemirror/extensions/marks/markUtils.js +8 -7
  99. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
  100. package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
  101. package/dist/prosemirror/fieldRepresentation.js +18 -0
  102. package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
  103. package/dist/prosemirror/hyperlinkRemoval.js +28 -9
  104. package/dist/prosemirror/listNumbering.js +11 -5
  105. package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
  106. package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
  107. package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
  108. package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
  109. package/dist/prosemirror/noteReferenceReview.js +14 -1
  110. package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
  111. package/dist/prosemirror/plugins/documentStyles.js +17 -10
  112. package/dist/prosemirror/plugins/suggestionMode.js +47 -49
  113. package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
  114. package/dist/prosemirror/preservedXmlReview.js +35 -0
  115. package/dist/prosemirror/runFormattingFromMarks.js +2 -2
  116. package/dist/prosemirror/runFormattingReconciliation.js +5 -4
  117. package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
  118. package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
  119. package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
  120. package/dist/prosemirror/runStyleFormatting.js +3 -1
  121. package/dist/prosemirror/schema/marks.d.ts +1 -0
  122. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  123. package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
  124. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
  125. package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
  126. package/dist/prosemirror/textInput.d.ts +5 -1
  127. package/dist/prosemirror/textInput.js +3 -3
  128. package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
  129. package/dist/prosemirror/trackedRevisionPath.js +39 -1
  130. package/dist/prosemirror/validation.js +3 -5
  131. package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
  132. package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
  133. package/dist/types/canonicalCapabilities.d.ts +6 -20
  134. package/dist/types/canonicalCapabilities.js +6 -20
  135. package/dist/types/canonicalComments.d.ts +51 -0
  136. package/dist/types/canonicalComments.js +0 -0
  137. package/dist/types/canonicalSections.d.ts +12 -0
  138. package/dist/types/canonicalSections.js +0 -0
  139. package/dist/utils/headingCollector.js +45 -4
  140. package/dist/utils/outlineDepth.d.ts +9 -0
  141. package/dist/utils/outlineDepth.js +9 -0
  142. package/package.json +2 -2
@@ -4,7 +4,7 @@ import { normalizeFoldedListNumbers } from "../../docx/foldedListNumberFields.js
4
4
  import { EDITED_PREVIEW_FINGERPRINT, imageRawXmlFingerprint } from "../../docx/imageRawXml.js";
5
5
  import { isInlineSdtContent, isSimpleFieldContent } from "../../docx/inlineWrapperContent.js";
6
6
  import { paragraphNumberingReferenceId as paragraphNumberingReferenceId$1, sameStatedParagraphNumbering as sameStatedParagraphNumbering$1 } from "../../docx/numberingReference.js";
7
- import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, ParagraphPropertySourceValidationError, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, linkParagraphPropertySourceCandidate, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings } from "../../docx/paragraphPropertySource.js";
7
+ import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, ParagraphPropertySourceValidationError, assignParagraphIndentationProjection, copyDocumentParagraphPropertySourceContract, copyDocumentParagraphPropertySources, copyParagraphPropertyCapture, copyParagraphPropertySource, decodeTableCellParagraphSourcePayload, getDocumentParagraphPropertySourceContract, getParagraphPropertySource, getParagraphPropertySourceCandidate, getParagraphPropertySourceToken, getParagraphPropertySourceTransferId, getProseDocumentParagraphPropertySourceContract, getProseParagraphPropertySourceToken, inspectParagraphSourceIdentity, isParagraphPropertySourceToken, linkParagraphPropertySourceCandidate, paragraphFormattingWithAuthoredIndentation, paragraphPropertySourceTokenMatchesContract, recreateProseNodeWithParagraphPropertySource, restoreTableCellsWithParagraphPropertySources, visitDocumentStoryParagraphs, visitTableCellParagraphPropertySourceBindings } from "../../docx/paragraphPropertySource.js";
8
8
  import { BLOCK_TREE_DESCENT, visitBlockTreeRecords, visitDocxParagraphs } from "../../docx/paragraphTraversal.js";
9
9
  import { refingerprintShapeAlternateContent } from "../../docx/shapeAlternateContent.js";
10
10
  import { DATE_UTC_ATTRIBUTE } from "../../docx/trackedChangeInfo.js";
@@ -33,9 +33,11 @@ import { inlineWrapperMember, inlineWrapperStackKey } from "../inlineWrapperStac
33
33
  import { completeNumberingForDoc } from "../listInstanceReferences.js";
34
34
  import { hasListRendering, listRenderingFromAttrs as listRenderingFromAttrs$1 } from "../listRenderingAttrs.js";
35
35
  import { expectMoveRangeBoundaryAttrs } from "../moveRangeBoundaryAttrs.js";
36
+ import { assertNoteReferenceOccurrences, coalesceNoteReferenceOccurrences } from "../noteReferenceOccurrences.js";
36
37
  import { resolveNumberedRefFields } from "../numberedRefFields.js";
37
38
  import { directParagraphAlignment } from "../paragraphAlignment.js";
38
39
  import { directionToAuthoredBidi } from "../paragraphDirection.js";
40
+ import { paragraphIndentationFromFormatting } from "../paragraphIndentation.js";
39
41
  import { directParagraphSpacing } from "../paragraphSpacing.js";
40
42
  import { expectRangeAnchorAttrs } from "../rangeAnchorAttrs.js";
41
43
  import { marksToTextFormatting, sameFormattingValue } from "../runFormattingFromMarks.js";
@@ -161,6 +163,13 @@ const restoreParagraphPropertySource = (paragraph, baseParagraph) => {
161
163
  copyParagraphPropertySource(paragraph, baseParagraph);
162
164
  const baseFormatting = baseParagraph.formatting;
163
165
  if (!baseFormatting) return;
166
+ if (baseFormatting.indentLeft !== void 0 || baseFormatting.indentRight !== void 0 || baseFormatting.indentFirstLine !== void 0 || baseFormatting.hangingIndent !== void 0) {
167
+ const authored = paragraphFormattingWithAuthoredIndentation(baseParagraph);
168
+ if ((authored?.indentLeft !== baseFormatting.indentLeft || authored?.indentRight !== baseFormatting.indentRight || authored?.indentFirstLine !== baseFormatting.indentFirstLine || authored?.hangingIndent !== baseFormatting.hangingIndent) && canonicalJson(modelParagraphFormattingEmission(paragraph.formatting)) === canonicalJson(modelParagraphFormattingEmission(authored))) {
169
+ paragraph.formatting = { ...baseFormatting };
170
+ return;
171
+ }
172
+ }
164
173
  const { numPr, numPrFromStyle } = baseFormatting;
165
174
  if (!numPr || !numPrFromStyle || !isStyleSourcedParagraphNumbering(numPr, numPrFromStyle)) return;
166
175
  if (canonicalJson(modelParagraphFormattingEmission(paragraph.formatting)) !== canonicalJson(modelParagraphFormattingEmission(baseFormatting))) return;
@@ -263,6 +272,7 @@ const restoreLinkedParagraphPropertySources = (content) => {
263
272
  };
264
273
  /** Convert a ProseMirror document to the document model. */
265
274
  function fromProseDoc(pmDoc, baseDocument, { reuse = "none" } = {}) {
275
+ assertNoteReferenceOccurrences(pmDoc);
266
276
  switch (reuse) {
267
277
  case "none": break;
268
278
  case "matched": panic("fromProseDoc: reuse \"matched\" is not implemented");
@@ -970,6 +980,14 @@ function convertPMParagraph(node, documentCounts, textBoxAnchorMarkers, styleRes
970
980
  if (attrs.reviewCarrier) paragraph.reviewCarrier = attrs.reviewCarrier;
971
981
  const pFormatting = paragraphAttrsToFormatting(attrs);
972
982
  if (pFormatting) paragraph.formatting = pFormatting;
983
+ if (attrs.numPr?.kind === "reference") {
984
+ const inheritedIndentation = paragraphIndentationFromFormatting(attrs._resolvedFormatting);
985
+ if (inheritedIndentation) assignParagraphIndentationProjection({
986
+ paragraph,
987
+ authored: pFormatting,
988
+ inherited: inheritedIndentation
989
+ });
990
+ }
973
991
  const listRendering = listRenderingFromAttrs(attrs);
974
992
  if (listRendering) paragraph.listRendering = listRendering;
975
993
  normalizeFoldedListNumbers(paragraph);
@@ -1373,7 +1391,8 @@ const nestInlineWrapperGroups = (items, groups, hyperlinkIndices, revisionOuterW
1373
1391
  }
1374
1392
  return coalesced;
1375
1393
  };
1376
- function extractParagraphContent(paragraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false, inheritedFormattingOverride) {
1394
+ function extractParagraphContent(originalParagraph, _documentCounts, emptyHyperlinks, textBoxAnchorMarkers, skipLeadingRenderedPageBreak = false, inheritedFormattingOverride) {
1395
+ const paragraph = coalesceNoteReferenceOccurrences(originalParagraph);
1377
1396
  const content = [];
1378
1397
  const paragraphStyleContext = paragraph.type.name === "paragraph" ? paragraphRunStyleContext(paragraph) : void 0;
1379
1398
  const formattingContext = inheritedFormattingOverride ?? {
@@ -2463,7 +2482,7 @@ const synchronizeFieldDisplayText = (content, displayText, fallbackRun) => {
2463
2482
  visit(child);
2464
2483
  continue;
2465
2484
  }
2466
- if (child.type === "hyperlink" || child.type === "inlineWrapper") visitRunsIn(child.type === "hyperlink" ? child.children : child.content, visit);
2485
+ if (child.type === "hyperlink" || child.type === "inlineWrapper" || child.type === "insertion" || child.type === "deletion" || child.type === "moveFrom" || child.type === "moveTo") visitRunsIn(child.type === "hyperlink" ? child.children : child.content, visit);
2467
2486
  }
2468
2487
  };
2469
2488
  const visitRuns = (visit) => {
@@ -2,7 +2,7 @@ import { rangedCommentIds } from "../../docx/commentAnchorIndex.js";
2
2
  import { resolveColorValueToHex } from "../../docx/drawingUtils.js";
3
3
  import { isInlineSdtContent, isTrackedChangeWrapperChild } from "../../docx/inlineWrapperContent.js";
4
4
  import { computeListRendering, getCachedNumberingMap } from "../../docx/numberingParser.js";
5
- import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
5
+ import { PROSE_PARAGRAPH_SOURCE_CONTRACT_ATTR, createProseParagraphWithPropertySource, getDocumentParagraphPropertySourceContract, paragraphFormattingWithAuthoredIndentation, proseParagraphAttrsWithoutPropertySource, recreateProseNodeWithParagraphPropertySource, transportTableCellsWithParagraphPropertySources } from "../../docx/paragraphPropertySource.js";
6
6
  import { isBaselineVertAlign } from "../../docx/runParser.js";
7
7
  import { replayableShapeAlternateContent } from "../../docx/shapeAlternateContent.js";
8
8
  import { resolveTableLook } from "../../docx/tableLook.js";
@@ -25,11 +25,11 @@ import { setAutospacingBaseValue } from "../autospacingBase.js";
25
25
  import { withEnclosingRevision } from "../contentControlRevisions.js";
26
26
  import { emptyFieldRunPropertyChanges } from "../emptyFieldResultRuns.js";
27
27
  import { planEmptyRanges } from "../emptyRangeAnchor.js";
28
- import { INLINE_WRAPPER_MARK_NAME } from "../extensions/marks/InlineWrapperExtension.js";
29
28
  import { textFormattingToMarks } from "../extensions/marks/markUtils.js";
30
29
  import { MOVE_RANGE_BOUNDARY_NODE_NAME } from "../extensions/nodes/MoveRangeBoundaryExtension.js";
31
30
  import { RANGE_ANCHOR_NODE_NAME } from "../extensions/nodes/RangeAnchorExtension.js";
32
31
  import "../extensions/nodes/SdtExtension.js";
32
+ import { fieldRequiresStructuredContent } from "../fieldRepresentation.js";
33
33
  import { inlineWrapperLayer } from "../inlineWrapperStack.js";
34
34
  import { CLEARED_LIST_RENDERING_ATTRS } from "../listMarker.js";
35
35
  import { listRenderingAttrPatch } from "../listRenderingAttrs.js";
@@ -37,13 +37,15 @@ import { stampNumberedRefFieldBaselines } from "../numberedRefFields.js";
37
37
  import { paragraphNumberingAttr } from "../numberingAttr.js";
38
38
  import { pageBreakRunParagraphProjectionDispositionForFeatures } from "../pageBreakRunProjection.js";
39
39
  import { directionFromBidi } from "../paragraphDirection.js";
40
+ import { paragraphIndentationFromFormatting } from "../paragraphIndentation.js";
40
41
  import { lineSpacingProvenanceFromSpacing } from "../paragraphSpacing.js";
41
42
  import { RUN_IDENTITY_MARK_NAME, hasRunIdentityPayload, runIdentityAttrs } from "../runIdentity.js";
42
43
  import { continueRunIdentitiesAcrossRevisions } from "../runIdentityAcrossRevisions.js";
43
- import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, resolveParagraphBodyRunFormatting, stripParagraphMarkFormattingForBodyRuns, stripParagraphMarkOnlyFormatting, suppressParagraphMarkFormatting } from "../runStyleFormatting.js";
44
+ import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, isParagraphMarkSuppressionEligible, resolveParagraphBodyRunFormatting, stripParagraphMarkFormattingForBodyRuns, stripParagraphMarkOnlyFormatting, suppressParagraphMarkFormatting } from "../runStyleFormatting.js";
44
45
  import { schema } from "../schema/index.js";
45
46
  import { PRESERVED_XML_LEVELS } from "../schema/nodes.js";
46
47
  import { extractTableParagraphOverlay, paragraphStyleCascadeAttrs, resolveRunFormattingWithoutDefaults } from "../styles/paragraphStyleCascade.js";
48
+ import { listIndentationProvenancePatch } from "../styles/resolvedStyleAttrs.js";
47
49
  import { cascadeStyleTextFormatting } from "../styles/styleToggleCascade.js";
48
50
  import { tableCellStyleRegions, tableRowBand } from "../styles/tableStyleRegions.js";
49
51
  import { canCarryTrackedRunMark, trackedRunInlineAtomDisposition } from "../trackedRunInlineAtoms.js";
@@ -57,6 +59,19 @@ import { sdtAttrsFromProperties } from "./sdtAttrs.js";
57
59
  import { panic } from "better-result";
58
60
  import { DRAWING_RAW_XML_MODES, PARSE_WARNING_CODES, mergeParagraphNumbering } from "@stll/docx-core/model";
59
61
  //#region src/prosemirror/conversion/toProseDoc.ts
62
+ /**
63
+ * Document to ProseMirror Conversion
64
+ *
65
+ * Converts our Document type (from DOCX parsing) to a ProseMirror document.
66
+ * Preserves all formatting attributes for round-trip fidelity.
67
+ *
68
+ * Style Resolution:
69
+ * When styles are provided, paragraph properties are resolved from the style chain:
70
+ * - Document defaults (docDefaults)
71
+ * - Normal style (if no explicit styleId)
72
+ * - Style chain (basedOn inheritance)
73
+ * - Inline properties (highest priority)
74
+ */
60
75
  const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
61
76
  const noPageBreakProjectionWarning = () => {};
62
77
  const pageBreakProjectionWarn = (warn) => warn === void 0 ? noPageBreakProjectionWarning : (detail) => {
@@ -106,6 +121,11 @@ const createHyperlinkInstanceIndexAllocator = () => {
106
121
  let index = 0;
107
122
  return () => index++;
108
123
  };
124
+ /** Imported occurrences belong to the projection walk, not to the referenced note. */
125
+ const createNoteReferenceOccurrenceAllocator = () => {
126
+ let index = 0;
127
+ return () => `source-note:${index++}`;
128
+ };
109
129
  /**
110
130
  * Find bookmark pairs across every editable inline wrapper in a document story.
111
131
  * Each endpoint is converted at its own structural position, so a range may
@@ -232,6 +252,7 @@ function toProseDoc(document, options) {
232
252
  const theme = options?.theme ?? document.package.theme ?? null;
233
253
  const nextTextBoxGroupId = createTextBoxGroupIdFactory();
234
254
  const nextHyperlinkInstanceIndex = createHyperlinkInstanceIndexAllocator();
255
+ const nextNoteReferenceOccurrenceId = createNoteReferenceOccurrenceAllocator();
235
256
  const pairedBookmarkIds = collectPairedBookmarkIds(paragraphs);
236
257
  const conversionContext = {
237
258
  listRenderings: /* @__PURE__ */ new Map(),
@@ -239,6 +260,7 @@ function toProseDoc(document, options) {
239
260
  theme,
240
261
  nextTextBoxGroupId,
241
262
  nextHyperlinkInstanceIndex,
263
+ nextNoteReferenceOccurrenceId,
242
264
  pairedBookmarkIds,
243
265
  pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(paragraphs),
244
266
  storyRangedCommentIds: rangedCommentIds(paragraphs),
@@ -406,12 +428,26 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
406
428
  let runIdentityId = 0;
407
429
  const runScope = {
408
430
  nextRunIdentityId: () => runIdentityId++,
431
+ nextNoteReferenceOccurrenceId: context.nextNoteReferenceOccurrenceId,
409
432
  createMark: context.createMark
410
433
  };
434
+ const directFormatting = paragraphFormattingWithAuthoredIndentation(paragraph);
411
435
  const { attrs, effectiveFrame } = paragraphFormattingToAttrs(paragraph, styleResolver, tableParagraphOverlay);
436
+ if (directFormatting === void 0) Reflect.deleteProperty(attrs, "_originalFormatting");
437
+ else attrs._originalFormatting = directFormatting;
412
438
  const numPr = mergeParagraphNumbering(attrs.numPrFromStyle ?? void 0, attrs.numPr ?? void 0);
413
439
  if (numPr?.kind === "none") Object.assign(attrs, CLEARED_LIST_RENDERING_ATTRS);
414
440
  else if (context.numbering !== void 0 && numPr?.kind === "reference") {
441
+ Object.assign(attrs, listIndentationProvenancePatch({
442
+ direct: paragraphIndentationFromFormatting(directFormatting),
443
+ styleFormatting: attrs._styleResolvedFormatting,
444
+ numberingSource: attrs.numPrFromStyle == null ? "paragraph" : "style",
445
+ numPr: {
446
+ numId: numPr.numId,
447
+ ilvl: numPr.ilvl ?? 0
448
+ },
449
+ numbering: context.numbering
450
+ }));
415
451
  const { rendering, nextSlotOffset } = resolveListRendering(numPr, context);
416
452
  if (rendering !== null) {
417
453
  const cached = paragraph.listRendering;
@@ -476,7 +512,7 @@ function convertParagraph(paragraph, styleResolver, context, extraRunFormatting,
476
512
  ...!hasCharacterStyle ? { implicitCharacterStyleApplied: true } : {},
477
513
  toggleCascade: inheritedToggleCascade
478
514
  };
479
- if (!(hasDirectRunFormatting(formatting) || formatting?.styleId !== void 0)) return {
515
+ if (!isParagraphMarkSuppressionEligible(formatting)) return {
480
516
  formatting: defaultRunFormatting,
481
517
  implicitCharacterStyleApplied: true,
482
518
  toggleCascade: defaultToggleCascade
@@ -643,7 +679,7 @@ function applyCommentMarks(nodes, commentIds) {
643
679
  if (!commentMarkType) return nodes;
644
680
  const commentMarks = [...commentIds].toSorted((left, right) => left - right).map((commentId) => commentMarkType.create({ commentId }));
645
681
  return nodes.map((node) => {
646
- if (!node.isText && (!node.isInline || !node.type.allowsMarkType(commentMarkType))) return node;
682
+ if (!node.isInline) return node;
647
683
  let marks = node.marks;
648
684
  for (const commentMark of commentMarks) marks = commentMark.addToSet(marks);
649
685
  return node.mark(marks);
@@ -1290,6 +1326,7 @@ function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPer
1290
1326
  function standaloneTableCellToProseMirror(cell, nodeType) {
1291
1327
  const nextTextBoxGroupId = createTextBoxGroupIdFactory();
1292
1328
  const nextHyperlinkInstanceIndex = createHyperlinkInstanceIndexAllocator();
1329
+ const nextNoteReferenceOccurrenceId = createNoteReferenceOccurrenceAllocator();
1293
1330
  const pageBreakRunSourceDescendants = buildPageBreakRunSourceDescendantIndex(cell.content);
1294
1331
  reportSourceContainerPageBreakRun(cell.content, "table-cell", pageBreakRunSourceDescendants, noPageBreakProjectionWarning);
1295
1332
  return convertTableCell({
@@ -1301,6 +1338,7 @@ function standaloneTableCellToProseMirror(cell, nodeType) {
1301
1338
  numbering: void 0,
1302
1339
  nextTextBoxGroupId,
1303
1340
  nextHyperlinkInstanceIndex,
1341
+ nextNoteReferenceOccurrenceId,
1304
1342
  pairedBookmarkIds: collectPairedBookmarkIds(cell.content),
1305
1343
  pageBreakRunSourceDescendants,
1306
1344
  storyRangedCommentIds: rangedCommentIds(cell.content),
@@ -1324,14 +1362,56 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1324
1362
  let fieldFormatting;
1325
1363
  let fieldPropertyChanges;
1326
1364
  const inlineNodes = [];
1327
- const hasStructuredSourceContent = fieldResultHasPageBreakContent(field) || field.type === "simpleField" && field.content.some((content) => content.type === "hyperlink" || content.type === "preservedInline" || content.type === "inlineWrapper");
1365
+ const hasStructuredSourceContent = fieldResultHasPageBreakContent(field) || field.type === "simpleField" && field.content.some((content) => content.type === "hyperlink" || content.type === "preservedInline" || content.type === "inlineWrapper" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo");
1328
1366
  const appendRun = (run, into) => {
1329
1367
  for (const content of run.content) if (content.type === "text") displayText += content.text;
1330
1368
  fieldFormatting ??= run.formatting;
1331
1369
  fieldPropertyChanges ??= run.propertyChanges;
1332
- if (!hasStructuredSourceContent) return;
1370
+ if (field.type !== "simpleField" && !hasStructuredSourceContent) return;
1333
1371
  into.push(...convertRun(run, getInheritedRunFormatting(run.formatting, field.fieldType), runScope, styleResolver, textBoxAnchors));
1334
1372
  };
1373
+ const collectDisplay = (items) => {
1374
+ for (const item of items) switch (item.type) {
1375
+ case "run":
1376
+ for (const child of item.content) if (child.type === "text") displayText += child.text;
1377
+ fieldFormatting ??= item.formatting;
1378
+ fieldPropertyChanges ??= item.propertyChanges;
1379
+ break;
1380
+ case "hyperlink":
1381
+ collectDisplay(item.children);
1382
+ break;
1383
+ case "inlineWrapper":
1384
+ case "inlineSdt":
1385
+ case "insertion":
1386
+ case "deletion":
1387
+ case "moveFrom":
1388
+ case "moveTo":
1389
+ collectDisplay(item.content);
1390
+ break;
1391
+ case "simpleField":
1392
+ collectDisplay(item.content);
1393
+ break;
1394
+ case "complexField":
1395
+ collectDisplay(item.fieldResult);
1396
+ break;
1397
+ case "preservedInline":
1398
+ displayText += item.text;
1399
+ break;
1400
+ case "mathEquation":
1401
+ displayText += item.plainText ?? "";
1402
+ break;
1403
+ case "bookmarkStart":
1404
+ case "bookmarkEnd":
1405
+ case "commentRangeStart":
1406
+ case "commentRangeEnd":
1407
+ case "commentReference":
1408
+ case "moveFromRangeStart":
1409
+ case "moveFromRangeEnd":
1410
+ case "moveToRangeStart":
1411
+ case "moveToRangeEnd": break;
1412
+ default: panic(`Unsupported field display content: ${JSON.stringify(item)}`);
1413
+ }
1414
+ };
1335
1415
  if (field.type === "simpleField") for (const { content, stack } of withInlineWrapperStacks(field.content)) {
1336
1416
  const itemNodes = [];
1337
1417
  switch (content.type) {
@@ -1355,6 +1435,16 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1355
1435
  runScope
1356
1436
  }));
1357
1437
  break;
1438
+ case "insertion":
1439
+ case "deletion":
1440
+ case "moveFrom":
1441
+ case "moveTo":
1442
+ collectDisplay(content.content);
1443
+ itemNodes.push(...convertTrackedChange(content, content.type === "insertion" || content.type === "moveTo" ? "insertion" : "deletion", nextHyperlinkInstanceIndex, runScope, {
1444
+ current: getInheritedRunFormatting,
1445
+ historical: getInheritedRunFormatting
1446
+ }, styleResolver, content.type === "moveFrom" || content.type === "moveTo" ? content.type : null, textBoxAnchors));
1447
+ break;
1358
1448
  default: break;
1359
1449
  }
1360
1450
  inlineNodes.push(...withInlineWrapperMark(itemNodes, stack));
@@ -1368,11 +1458,7 @@ function convertField(field, { getInheritedRunFormatting, styleResolver, nextHyp
1368
1458
  styleResolver,
1369
1459
  createMark: runScope.createMark
1370
1460
  });
1371
- const hasConvertedHyperlinkContent = inlineNodes.some((node) => node.marks.some((mark) => mark.type.name === "hyperlink"));
1372
- const hasConvertedPageBreakContent = inlineNodes.some((node) => node.type.name === "pageBreakRun");
1373
- const hasConvertedPreservedContent = inlineNodes.some((node) => node.type.name === "preservedXml");
1374
- const hasConvertedWrapperContent = inlineNodes.some((node) => node.marks.some((mark) => mark.type.name === INLINE_WRAPPER_MARK_NAME));
1375
- const createStructuredField = hasConvertedPageBreakContent || hasConvertedPreservedContent || hasStructuredSourceContent && (hasConvertedHyperlinkContent || hasConvertedWrapperContent);
1461
+ const createStructuredField = fieldRequiresStructuredContent(inlineNodes);
1376
1462
  const resultRuns = field.type === "simpleField" ? field.content.flatMap((content) => content.type === "run" ? [content] : []) : field.fieldResult;
1377
1463
  const emptyResultRuns = displayText === "" && resultRuns.length > 0 && (field.type === "complexField" || field.content.length === resultRuns.length) && resultRuns.every((run) => run.content.every((item) => item.type === "text" && item.text === "")) ? resultRuns : void 0;
1378
1464
  const resultPropertyChanges = emptyResultRuns ? emptyFieldRunPropertyChanges(emptyResultRuns) : fieldPropertyChanges;
@@ -1564,7 +1650,14 @@ function convertRun(run, resolvedStyleFormatting, runScope, styleResolver, textB
1564
1650
  const identity = runIdentityMark(run, runScope.nextRunIdentityId);
1565
1651
  if (identity) marks.push(identity);
1566
1652
  for (const content of run.content) {
1567
- const contentNodes = convertRunContent(content, marks, mergedFormatting, textBoxAnchors, run.formatting);
1653
+ const contentNodes = convertRunContent({
1654
+ content,
1655
+ marks,
1656
+ formatting: mergedFormatting,
1657
+ textBoxAnchors,
1658
+ authoredFormatting: run.formatting,
1659
+ nextNoteReferenceOccurrenceId: runScope.nextNoteReferenceOccurrenceId
1660
+ });
1568
1661
  nodes.push(...contentNodes);
1569
1662
  }
1570
1663
  return nodes;
@@ -1970,10 +2063,7 @@ const noteReferenceVertAlign = (vertAlign) => {
1970
2063
  * run was authored with.
1971
2064
  */
1972
2065
  const carriedRunFormatting = (formatting) => formatting && Object.keys(formatting).length > 0 ? formatting : void 0;
1973
- /**
1974
- * Convert RunContent to ProseMirror nodes
1975
- */
1976
- function convertRunContent(content, marks, formatting, textBoxAnchors, authoredFormatting) {
2066
+ function convertRunContent({ content, marks, formatting, textBoxAnchors, authoredFormatting, nextNoteReferenceOccurrenceId }) {
1977
2067
  switch (content.type) {
1978
2068
  case "noteMarker": return [schema.node("noteMarker", { kind: content.kind }).mark(marks)];
1979
2069
  case "text":
@@ -2013,6 +2103,7 @@ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredF
2013
2103
  case "footnoteRef": {
2014
2104
  const footnoteMark = schema.mark("footnoteRef", {
2015
2105
  id: content.id.toString(),
2106
+ occurrenceId: nextNoteReferenceOccurrenceId(),
2016
2107
  noteType: "footnote",
2017
2108
  vertAlign: noteReferenceVertAlign(formatting?.vertAlign),
2018
2109
  customMarkFollows: content.customMarkFollows
@@ -2022,6 +2113,7 @@ function convertRunContent(content, marks, formatting, textBoxAnchors, authoredF
2022
2113
  case "endnoteRef": {
2023
2114
  const endnoteMark = schema.mark("footnoteRef", {
2024
2115
  id: content.id.toString(),
2116
+ occurrenceId: nextNoteReferenceOccurrenceId(),
2025
2117
  noteType: "endnote",
2026
2118
  vertAlign: noteReferenceVertAlign(formatting?.vertAlign),
2027
2119
  customMarkFollows: content.customMarkFollows
@@ -2251,7 +2343,14 @@ function convertHyperlink(hyperlink, { getInheritedRunFormatting, styleResolver,
2251
2343
  const identity = runIdentityMark(child, runScope.nextRunIdentityId);
2252
2344
  if (identity) runMarks.push(identity);
2253
2345
  const allMarks = [...runMarks, linkMark];
2254
- for (const content of child.content) childNodes.push(...convertRunContent(content, allMarks, mergedFormatting, textBoxAnchors, child.formatting));
2346
+ for (const content of child.content) childNodes.push(...convertRunContent({
2347
+ content,
2348
+ marks: allMarks,
2349
+ formatting: mergedFormatting,
2350
+ textBoxAnchors,
2351
+ authoredFormatting: child.formatting,
2352
+ nextNoteReferenceOccurrenceId: runScope.nextNoteReferenceOccurrenceId
2353
+ }));
2255
2354
  break;
2256
2355
  }
2257
2356
  default: break;
@@ -2695,6 +2794,7 @@ function headerFooterToProseDoc(content, options) {
2695
2794
  const theme = options?.theme ?? null;
2696
2795
  const nextTextBoxGroupId = createTextBoxGroupIdFactory();
2697
2796
  const nextHyperlinkInstanceIndex = createHyperlinkInstanceIndexAllocator();
2797
+ const nextNoteReferenceOccurrenceId = createNoteReferenceOccurrenceAllocator();
2698
2798
  const pairedBookmarkIds = collectPairedBookmarkIds(content);
2699
2799
  const conversionContext = {
2700
2800
  listRenderings: /* @__PURE__ */ new Map(),
@@ -2702,6 +2802,7 @@ function headerFooterToProseDoc(content, options) {
2702
2802
  numbering: options?.numbering === void 0 ? void 0 : getCachedNumberingMap(options.numbering),
2703
2803
  nextTextBoxGroupId,
2704
2804
  nextHyperlinkInstanceIndex,
2805
+ nextNoteReferenceOccurrenceId,
2705
2806
  pairedBookmarkIds,
2706
2807
  pageBreakRunSourceDescendants: buildPageBreakRunSourceDescendantIndex(content),
2707
2808
  storyRangedCommentIds: rangedCommentIds(content),
@@ -14,12 +14,14 @@ import { CLEARED_LIST_RENDERING_ATTRS } from "../../listMarker.js";
14
14
  import { directParagraphAlignment } from "../../paragraphAlignment.js";
15
15
  import { directionIsRtl } from "../../paragraphDirection.js";
16
16
  import { directParagraphIndentation } from "../../paragraphIndentation.js";
17
+ import { recordReplacedParagraphProperties } from "../../paragraphPropertyCarry.js";
17
18
  import { directParagraphSpacing, withDirectParagraphSpacing } from "../../paragraphSpacing.js";
18
19
  import { getDocumentNumbering } from "../../plugins/documentNumbering.js";
19
20
  import { getDocumentBuiltInStyles, getDocumentStyleResolver } from "../../plugins/documentStyleState.js";
21
+ import { SUGGESTION_META, makeRevisionInfo } from "../../plugins/suggestionMode.js";
20
22
  import { rebaseParagraphRuns } from "../../rebaseParagraphRuns.js";
21
23
  import { mintSectionProperties, parseSectionBreakType, sectionBreakTypeOf } from "../../sectionCarrier.js";
22
- import { listAttrsFromResolvedStyle, listLevelIndentAttrPatch, paragraphAttrsFromResolvedStyle } from "../../styles/resolvedStyleAttrs.js";
24
+ import { listAttrsFromResolvedStyle, listIndentationProvenancePatch, paragraphAttrsFromResolvedStyle } from "../../styles/resolvedStyleAttrs.js";
23
25
  import { createNodeExtension } from "../create.js";
24
26
  import { headingOutlineLevel, paragraphNumberingLevel, paragraphNumberingReferenceId } from "@stll/docx-core/model";
25
27
  import { Fragment } from "prosemirror-model";
@@ -263,6 +265,7 @@ const paragraphNodeSpec = {
263
265
  _emptyHyperlinks: { default: null },
264
266
  _originalFormatting: { default: null },
265
267
  _resolvedFormatting: { default: void 0 },
268
+ _styleResolvedFormatting: { default: void 0 },
266
269
  _autospacingBase: { default: null },
267
270
  _sectionProperties: { default: null },
268
271
  _propertyChanges: { default: null },
@@ -548,6 +551,7 @@ function makeApplyStyle() {
548
551
  const { $from, $to } = state.selection;
549
552
  if (!dispatch) return true;
550
553
  let tr = state.tr;
554
+ const revision = makeRevisionInfo(state);
551
555
  const seen = /* @__PURE__ */ new Set();
552
556
  state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
553
557
  if (node.type.name === "paragraph" && !seen.has(pos)) {
@@ -583,12 +587,24 @@ function makeApplyStyle() {
583
587
  numPr: null,
584
588
  numPrFromStyle: null
585
589
  });
586
- else if (directNumId !== void 0) Object.assign(newAttrs, listLevelIndentAttrPatch(restyledIndentation(styleAttrs, restyledOriginal), {
587
- numId: directNumId,
588
- ilvl: paragraphNumberingLevel(current.numPr ?? void 0) ?? 0
589
- }, resolvedAttrs.numbering ?? getDocumentNumbering(state)));
590
+ else if (directNumId !== void 0) Object.assign(newAttrs, listIndentationProvenancePatch({
591
+ direct: restyledIndentation(styleAttrs, restyledOriginal),
592
+ styleFormatting: resolvedAttrs.paragraphFormatting,
593
+ numberingSource: "paragraph",
594
+ numPr: {
595
+ numId: directNumId,
596
+ ilvl: paragraphNumberingLevel(current.numPr ?? void 0) ?? 0
597
+ },
598
+ numbering: resolvedAttrs.numbering ?? getDocumentNumbering(state)
599
+ }));
590
600
  }
591
601
  tr = tr.setNodeMarkup(pos, void 0, newAttrs);
602
+ if (revision) recordReplacedParagraphProperties({
603
+ tr,
604
+ position: pos,
605
+ replaced: node,
606
+ revision
607
+ });
592
608
  rebaseParagraphRuns({
593
609
  tr,
594
610
  position: pos,
@@ -598,6 +614,7 @@ function makeApplyStyle() {
598
614
  });
599
615
  }
600
616
  });
617
+ if (revision) tr.setMeta(SUGGESTION_META, true);
601
618
  dispatch(tr.scrollIntoView());
602
619
  return true;
603
620
  };
@@ -1,5 +1,5 @@
1
1
  import { mergeTextFormatting } from "../../../utils/textFormattingMerge.js";
2
- import { expectCharacterStyleMarkAttrs, expectRunFormattingOverrideMarkAttrs } from "../../attrs/index.js";
2
+ import { expectCharacterStyleMarkAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs } from "../../attrs/index.js";
3
3
  import { clearIndentOnBackspace } from "../../commands/clearParagraphIndent.js";
4
4
  import { keepSectionBreaksOnSurvivingMarks } from "../../commands/sectionBreak.js";
5
5
  import { getDocumentStyleResolver } from "../../plugins/documentStyleState.js";
@@ -23,27 +23,55 @@ function chainCommands(...commands) {
23
23
  return false;
24
24
  };
25
25
  }
26
- /**
27
- * Custom Enter handler: splits the block, inherits style-related attrs,
28
- * clears paragraph borders, and preserves run formatting on the new paragraph.
29
- *
30
- * splitBlock creates a new paragraph with default attrs (all null),
31
- * so we must manually copy style-related attrs from the source paragraph.
32
- * Word does NOT propagate paragraph borders (w:pBdr) on Enter.
33
- */
34
26
  const INHERITED_PARA_ATTRS = [
35
- "defaultTextFormatting",
36
- "styleId",
37
- "_tableOfContentsLevel",
38
- "lineSpacing",
39
- "lineSpacingRule",
40
- "snapToGrid",
41
- "spaceAfter",
42
- "spaceBefore",
43
- "spacingExplicit",
44
- "spacingFromDocDefaults",
45
- "spacingFromImplicitDefaultStyle",
46
- "contextualSpacing"
27
+ {
28
+ attr: "defaultTextFormatting",
29
+ formattingFields: ["runProperties"]
30
+ },
31
+ {
32
+ attr: "styleId",
33
+ formattingFields: ["styleId"]
34
+ },
35
+ {
36
+ attr: "_tableOfContentsLevel",
37
+ formattingFields: []
38
+ },
39
+ {
40
+ attr: "lineSpacing",
41
+ formattingFields: ["lineSpacing", "spacingPreservedAttributes"]
42
+ },
43
+ {
44
+ attr: "lineSpacingRule",
45
+ formattingFields: ["lineSpacingRule", "spacingPreservedAttributes"]
46
+ },
47
+ {
48
+ attr: "snapToGrid",
49
+ formattingFields: ["snapToGrid"]
50
+ },
51
+ {
52
+ attr: "spaceAfter",
53
+ formattingFields: ["spaceAfter", "spacingPreservedAttributes"]
54
+ },
55
+ {
56
+ attr: "spaceBefore",
57
+ formattingFields: ["spaceBefore", "spacingPreservedAttributes"]
58
+ },
59
+ {
60
+ attr: "spacingExplicit",
61
+ formattingFields: ["spacingExplicit"]
62
+ },
63
+ {
64
+ attr: "spacingFromDocDefaults",
65
+ formattingFields: []
66
+ },
67
+ {
68
+ attr: "spacingFromImplicitDefaultStyle",
69
+ formattingFields: []
70
+ },
71
+ {
72
+ attr: "contextualSpacing",
73
+ formattingFields: ["contextualSpacing"]
74
+ }
47
75
  ];
48
76
  /** Style formatting needed when the caret has no marks of its own. */
49
77
  const STYLE_MARK_NAMES = /* @__PURE__ */ new Set([
@@ -142,10 +170,31 @@ const splitBlockClearBorders = (state, dispatch, view) => {
142
170
  }
143
171
  const newAttrs = { ...newPara.attrs };
144
172
  let attrsChanged = false;
145
- if (sourcePara) for (const key of INHERITED_PARA_ATTRS) {
146
- const srcVal = sourcePara.attrs[key];
147
- if (srcVal !== null && newAttrs[key] === null) {
148
- newAttrs[key] = srcVal;
173
+ if (sourcePara) {
174
+ const sourceAttrs = expectParagraphAttrs(sourcePara);
175
+ const addedAttrs = expectParagraphAttrs(newPara);
176
+ const originalFormatting = { ...addedAttrs._originalFormatting };
177
+ const resolvedFormatting = { ...addedAttrs._resolvedFormatting };
178
+ for (const { attr, formattingFields } of INHERITED_PARA_ATTRS) {
179
+ const srcVal = sourceAttrs[attr];
180
+ if (srcVal !== null && newAttrs[attr] === null) {
181
+ newAttrs[attr] = srcVal;
182
+ attrsChanged = true;
183
+ }
184
+ if (newAttrs[attr] !== srcVal) continue;
185
+ for (const field of formattingFields) {
186
+ const original = sourceAttrs._originalFormatting?.[field];
187
+ if (original !== void 0) Reflect.set(originalFormatting, field, original);
188
+ const resolved = sourceAttrs._resolvedFormatting?.[field];
189
+ if (resolved !== void 0) Reflect.set(resolvedFormatting, field, resolved);
190
+ }
191
+ }
192
+ if (Object.keys(originalFormatting).length > 0) {
193
+ newAttrs["_originalFormatting"] = originalFormatting;
194
+ attrsChanged = true;
195
+ }
196
+ if (Object.keys(resolvedFormatting).length > 0) {
197
+ newAttrs["_resolvedFormatting"] = resolvedFormatting;
149
198
  attrsChanged = true;
150
199
  }
151
200
  }
@@ -1,4 +1,5 @@
1
1
  import { expectFootnoteRefMarkAttrs } from "../../attrs/index.js";
2
+ import { mintNoteReferenceOccurrenceId, noteReferenceOccurrencePlugin } from "../../noteReferenceOccurrences.js";
2
3
  import { suggestRangeDeletion, suggestionModeKey } from "../../plugins/suggestionMode.js";
3
4
  import { createMarkExtension } from "../create.js";
4
5
  import { expandNoteReferenceDeletionRange, noteReferenceRanges } from "./noteReferenceDeletion.js";
@@ -9,7 +10,9 @@ import { panic } from "better-result";
9
10
  *
10
11
  * Provides footnoteRef mark + insert/delete commands for footnotes and endnotes.
11
12
  */
13
+ const NOTE_REFERENCE_PARSE_PRIORITY = 60;
12
14
  const noteRefAttrsFromDom = (dom, noteType, vertAlign) => ({
15
+ occurrenceId: dom.dataset["noteOccurrence"] ?? mintNoteReferenceOccurrenceId(),
13
16
  id: dom.dataset["id"] ?? "",
14
17
  noteType: dom.dataset["noteType"] ?? noteType,
15
18
  ...vertAlign ? { vertAlign } : {}
@@ -21,6 +24,7 @@ const FootnoteRefExtension = createMarkExtension({
21
24
  inclusive: false,
22
25
  attrs: {
23
26
  id: {},
27
+ occurrenceId: {},
24
28
  noteType: { default: "footnote" },
25
29
  vertAlign: { default: null },
26
30
  customMarkFollows: { default: null }
@@ -28,18 +32,22 @@ const FootnoteRefExtension = createMarkExtension({
28
32
  parseDOM: [
29
33
  {
30
34
  tag: "sup.docx-footnote-ref",
35
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
31
36
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "footnote", "superscript")
32
37
  },
33
38
  {
34
39
  tag: "sup.docx-endnote-ref",
40
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
35
41
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "endnote", "superscript")
36
42
  },
37
43
  {
38
44
  tag: "span.docx-footnote-ref",
45
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
39
46
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "footnote", "baseline")
40
47
  },
41
48
  {
42
49
  tag: "span.docx-endnote-ref",
50
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
43
51
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "endnote", "baseline")
44
52
  }
45
53
  ],
@@ -53,6 +61,7 @@ const FootnoteRefExtension = createMarkExtension({
53
61
  {
54
62
  class: `docx-${noteType}-ref ${isSuperscript ? "docx-note-ref-superscript" : "docx-note-ref-baseline"}`,
55
63
  "data-id": id,
64
+ "data-note-occurrence": attrs.occurrenceId,
56
65
  "data-note-type": noteType
57
66
  },
58
67
  0
@@ -76,6 +85,7 @@ const FootnoteRefExtension = createMarkExtension({
76
85
  if (!footnoteRefType) panic("Missing mark type: footnoteRef");
77
86
  const mark = footnoteRefType.create({
78
87
  id: String(id),
88
+ occurrenceId: mintNoteReferenceOccurrenceId(),
79
89
  noteType,
80
90
  vertAlign: "superscript"
81
91
  });
@@ -100,6 +110,7 @@ const FootnoteRefExtension = createMarkExtension({
100
110
  return true;
101
111
  };
102
112
  return {
113
+ plugins: [noteReferenceOccurrencePlugin()],
103
114
  keyboardShortcuts: {
104
115
  Backspace: deleteWholeNoteReference("backward"),
105
116
  Delete: deleteWholeNoteReference("forward")