@stll/folio-core 0.35.1 → 0.37.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 (236) hide show
  1. package/README.md +59 -1
  2. package/dist/ai-edits/apply.js +878 -377
  3. package/dist/ai-edits/block-identity.d.ts +4 -1
  4. package/dist/ai-edits/block-identity.js +4 -1
  5. package/dist/ai-edits/blockRange.js +14 -14
  6. package/dist/ai-edits/clean-text.d.ts +40 -1
  7. package/dist/ai-edits/clean-text.js +52 -2
  8. package/dist/ai-edits/headless.d.ts +3 -1
  9. package/dist/ai-edits/headless.js +141 -73
  10. package/dist/ai-edits/index.d.ts +3 -3
  11. package/dist/ai-edits/index.js +2 -2
  12. package/dist/ai-edits/read.js +38 -20
  13. package/dist/ai-edits/snapshot.d.ts +14 -6
  14. package/dist/ai-edits/snapshot.js +105 -20
  15. package/dist/ai-edits/table-cell-mutations.js +1 -1
  16. package/dist/ai-edits/table-row-column-mutations.js +7 -0
  17. package/dist/ai-edits/table-template.d.ts +11 -1
  18. package/dist/ai-edits/table-template.js +49 -17
  19. package/dist/ai-edits/types.d.ts +62 -61
  20. package/dist/ai-edits/word-diff.d.ts +18 -3
  21. package/dist/ai-edits/word-diff.js +556 -67
  22. package/dist/ai-suggestions/conflict.js +1 -4
  23. package/dist/ai-suggestions/text-positions.d.ts +8 -0
  24. package/dist/ai-suggestions/text-positions.js +27 -5
  25. package/dist/compare/__fixtures__/body-sequence.js +3 -4
  26. package/dist/compare/column-alignment.d.ts +13 -13
  27. package/dist/compare/column-alignment.js +78 -39
  28. package/dist/compare/compare.d.ts +4 -3
  29. package/dist/compare/compare.js +48 -21
  30. package/dist/compare/content-alignment.d.ts +95 -0
  31. package/dist/compare/content-alignment.js +1450 -0
  32. package/dist/compare/content-types.d.ts +100 -0
  33. package/dist/compare/content-types.js +0 -0
  34. package/dist/compare/content.d.ts +209 -0
  35. package/dist/compare/content.js +885 -0
  36. package/dist/compare/formatting.d.ts +5 -5
  37. package/dist/compare/formatting.js +6 -4
  38. package/dist/compare/plan.d.ts +6 -1
  39. package/dist/compare/plan.js +171 -566
  40. package/dist/compare/scenario.d.ts +2 -2
  41. package/dist/compare/types.d.ts +3 -3
  42. package/dist/compare/verification.d.ts +8 -9
  43. package/dist/compare/verification.js +72 -50
  44. package/dist/compat/eigenpal.d.ts +5 -3
  45. package/dist/compat/eigenpal.js +3 -2
  46. package/dist/controller/headerFooterEditorManager.js +18 -10
  47. package/dist/controller/hiddenEditorApi.js +1 -1
  48. package/dist/controller/hiddenEditorManager.d.ts +7 -2
  49. package/dist/controller/hiddenEditorManager.js +15 -6
  50. package/dist/controller/layoutPipeline.js +5 -3
  51. package/dist/controller/noteEditorManager.js +12 -7
  52. package/dist/display-list/build/headerFooterPrimitives.js +1 -0
  53. package/dist/display-list/build/storyPrimitives.d.ts +5 -2
  54. package/dist/display-list/build/storyPrimitives.js +60 -3
  55. package/dist/display-list/build/tablePrimitives.js +30 -0
  56. package/dist/display-list/primitives.d.ts +1 -1
  57. package/dist/document-operations.d.ts +8 -4
  58. package/dist/document-operations.js +104 -12
  59. package/dist/docx/blockContentParser.js +6 -27
  60. package/dist/docx/commentParser.js +2 -4
  61. package/dist/docx/commentRangeIntegrity.js +2 -4
  62. package/dist/docx/documentClone.d.ts +2 -0
  63. package/dist/docx/documentClone.js +2 -0
  64. package/dist/docx/ensureParaIds.js +9 -4
  65. package/dist/docx/headerFooterParser.js +2 -4
  66. package/dist/docx/normalizeBaseDirection.js +5 -7
  67. package/dist/docx/numberingParser.d.ts +3 -1
  68. package/dist/docx/numberingParser.js +4 -1
  69. package/dist/docx/paragraphParser.js +34 -35
  70. package/dist/docx/paragraphPropertySource.d.ts +49 -0
  71. package/dist/docx/paragraphPropertySource.js +113 -0
  72. package/dist/docx/paragraphTraversal.js +30 -34
  73. package/dist/docx/rezip.d.ts +0 -8
  74. package/dist/docx/rezip.js +41 -16
  75. package/dist/docx/runConsolidator.js +2 -4
  76. package/dist/docx/runParser.js +6 -17
  77. package/dist/docx/sectionParser.js +1 -15
  78. package/dist/docx/serializer/paragraphSerializer.js +289 -30
  79. package/dist/docx/serializer/runSerializer.js +6 -12
  80. package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -10
  81. package/dist/docx/serializer/tableSerializer.js +4 -23
  82. package/dist/docx/serializer/trackedChangeAttributes.d.ts +11 -0
  83. package/dist/docx/serializer/trackedChangeAttributes.js +31 -0
  84. package/dist/docx/server/build.d.ts +1 -1
  85. package/dist/docx/server/createBilingualDocument.js +8 -10
  86. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +22 -13
  87. package/dist/docx/server/validateDocxConformance.js +17 -13
  88. package/dist/docx/tableParser.js +1 -37
  89. package/dist/docx/trackedChangeInfo.d.ts +11 -0
  90. package/dist/docx/trackedChangeInfo.js +38 -0
  91. package/dist/docx/verbatimCapture.d.ts +20 -9
  92. package/dist/docx/verbatimCapture.js +208 -8
  93. package/dist/docx/xmlParser.d.ts +3 -1
  94. package/dist/docx/xmlParser.js +6 -1
  95. package/dist/docx/xmlSafety.d.ts +1 -1
  96. package/dist/docx/xmlSafety.js +157 -0
  97. package/dist/headless-layout.js +5 -4
  98. package/dist/index.d.ts +5 -3
  99. package/dist/index.js +3 -2
  100. package/dist/internal/headlessRevisionResolution.d.ts +23 -0
  101. package/dist/internal/headlessRevisionResolution.js +155 -0
  102. package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
  103. package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
  104. package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
  105. package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
  106. package/dist/internal/pageBreakRunPartition.d.ts +26 -0
  107. package/dist/internal/pageBreakRunPartition.js +35 -0
  108. package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
  109. package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
  110. package/dist/internal/sectionEndpointResolution.d.ts +32 -0
  111. package/dist/internal/sectionEndpointResolution.js +38 -0
  112. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  113. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
  114. package/dist/layout-bridge/convert/headerFooterLayout.js +52 -9
  115. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
  116. package/dist/layout-bridge/convert/toFlowBlocks.js +391 -82
  117. package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
  118. package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
  119. package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
  120. package/dist/layout-bridge/dom/textStreamDom.js +75 -0
  121. package/dist/layout-bridge/headerFooterLayout.js +16 -30
  122. package/dist/layout-engine/index.js +12 -3
  123. package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
  124. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
  125. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  126. package/dist/layout-engine/types.d.ts +7 -0
  127. package/dist/layout-painter/imageLayout.d.ts +1 -1
  128. package/dist/layout-painter/renderTable.js +20 -0
  129. package/dist/markdown/renderBlock.js +7 -10
  130. package/dist/model.d.ts +1 -1
  131. package/dist/paged-layout/rangeProjection.js +6 -9
  132. package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
  133. package/dist/paged-layout/transactionDirtyRange.js +23 -1
  134. package/dist/prosemirror/attrs/index.d.ts +7 -3
  135. package/dist/prosemirror/attrs/index.js +128 -7
  136. package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
  137. package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
  138. package/dist/prosemirror/commands/comments.d.ts +10 -1
  139. package/dist/prosemirror/commands/comments.js +551 -87
  140. package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
  141. package/dist/prosemirror/commands/formatPainter.js +319 -63
  142. package/dist/prosemirror/commands/index.d.ts +2 -2
  143. package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
  144. package/dist/prosemirror/commands/pageBreak.js +18 -36
  145. package/dist/prosemirror/commands/propertyChangeScope.d.ts +27 -3
  146. package/dist/prosemirror/commands/propertyChangeScope.js +85 -14
  147. package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -5
  148. package/dist/prosemirror/conversion/fromProseDoc.js +935 -223
  149. package/dist/prosemirror/conversion/runShadingMark.js +2 -2
  150. package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
  151. package/dist/prosemirror/conversion/toProseDoc.js +439 -291
  152. package/dist/prosemirror/extensions/StarterKit.js +4 -0
  153. package/dist/prosemirror/extensions/core/ParagraphExtension.js +54 -6
  154. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -1
  155. package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
  156. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +19 -6
  157. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
  158. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
  159. package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
  160. package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
  161. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
  162. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
  163. package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
  164. package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
  165. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
  166. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
  167. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
  168. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
  169. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  170. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
  171. package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
  172. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
  173. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  174. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
  175. package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
  176. package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
  177. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
  178. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
  179. package/dist/prosemirror/findReplaceSelection.js +8 -3
  180. package/dist/prosemirror/index.d.ts +3 -3
  181. package/dist/prosemirror/listMarker.d.ts +26 -1
  182. package/dist/prosemirror/listMarker.js +108 -14
  183. package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
  184. package/dist/prosemirror/pageBreakRunProjection.js +47 -0
  185. package/dist/prosemirror/paraText.js +6 -0
  186. package/dist/prosemirror/paragraphAlignment.d.ts +12 -0
  187. package/dist/prosemirror/paragraphAlignment.js +13 -0
  188. package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
  189. package/dist/prosemirror/paragraphSpacing.js +125 -0
  190. package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
  191. package/dist/prosemirror/plugins/documentNumbering.js +18 -0
  192. package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
  193. package/dist/prosemirror/plugins/pmTextScan.js +14 -4
  194. package/dist/prosemirror/plugins/suggestionMode.js +9 -11
  195. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
  196. package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
  197. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
  198. package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
  199. package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
  200. package/dist/prosemirror/runFormattingProvenance.js +48 -0
  201. package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
  202. package/dist/prosemirror/runFormattingReconciliation.js +54 -0
  203. package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
  204. package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
  205. package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
  206. package/dist/prosemirror/runStyleFormatting.js +105 -0
  207. package/dist/prosemirror/schema/index.d.ts +3 -3
  208. package/dist/prosemirror/schema/marks.d.ts +84 -9
  209. package/dist/prosemirror/schema/marks.js +73 -1
  210. package/dist/prosemirror/schema/nodes.d.ts +30 -6
  211. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
  212. package/dist/prosemirror/styles/resolvedStyleAttrs.js +45 -23
  213. package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
  214. package/dist/prosemirror/styles/styleResolver.js +3 -3
  215. package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
  216. package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
  217. package/dist/prosemirror/tableGridMutation.d.ts +20 -0
  218. package/dist/prosemirror/tableGridMutation.js +30 -0
  219. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
  220. package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
  221. package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
  222. package/dist/prosemirror/validation.js +23 -4
  223. package/dist/redline.js +4 -1
  224. package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
  225. package/dist/render-dom/RenderedDomContext.js +17 -34
  226. package/dist/server.d.ts +4 -4
  227. package/dist/server.js +2 -2
  228. package/dist/style-engine/styleEngine.d.ts +1 -1
  229. package/dist/utils/clipboard.js +1 -1
  230. package/dist/utils/findReplace.js +38 -21
  231. package/dist/utils/mergeDocumentContent.js +3 -3
  232. package/dist/utils/replaceText.js +2 -1
  233. package/dist/version-comparison.d.ts +38 -44
  234. package/dist/version-comparison.js +270 -399
  235. package/dist/watermark/index.js +2 -4
  236. package/package.json +8 -2
@@ -0,0 +1,47 @@
1
+ import { expectParagraphAttrs } from "./attrs/index.js";
2
+ //#region src/prosemirror/pageBreakRunProjection.ts
3
+ const PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES = {
4
+ borders: "A bordered paragraph containing an explicit page-break run cannot be projected",
5
+ frame: "A framed paragraph containing an explicit page-break run cannot be projected",
6
+ outline: "An outline paragraph containing an explicit page-break run cannot be projected",
7
+ textBoxAnchor: "A paragraph containing both an explicit page-break run and a text-box anchor cannot be projected"
8
+ };
9
+ /** Keep source-import and ProseMirror-layout ownership decisions on one predicate. */
10
+ const pageBreakRunParagraphProjectionDispositionForFeatures = ({ attrs, effectiveFrame, hasTextBoxAnchor }) => {
11
+ if (effectiveFrame !== void 0 && effectiveFrame.dropCap !== "drop" && effectiveFrame.dropCap !== "margin") return {
12
+ status: "unsupported",
13
+ reason: "frame",
14
+ message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.frame
15
+ };
16
+ if (attrs.outlineLevel !== void 0) return {
17
+ status: "unsupported",
18
+ reason: "outline",
19
+ message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.outline
20
+ };
21
+ if (attrs.borders !== void 0 && Object.values(attrs.borders).some((border) => border !== void 0)) return {
22
+ status: "unsupported",
23
+ reason: "borders",
24
+ message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.borders
25
+ };
26
+ if (hasTextBoxAnchor) return {
27
+ status: "unsupported",
28
+ reason: "textBoxAnchor",
29
+ message: PAGE_BREAK_RUN_PARAGRAPH_PROJECTION_MESSAGES.textBoxAnchor
30
+ };
31
+ return { status: "supported" };
32
+ };
33
+ const pageBreakRunParagraphProjectionDisposition = (paragraph) => {
34
+ const attrs = expectParagraphAttrs(paragraph);
35
+ let hasTextBoxAnchor = false;
36
+ paragraph.descendants((descendant) => {
37
+ hasTextBoxAnchor ||= descendant.type.name === "textBoxAnchor";
38
+ return !hasTextBoxAnchor;
39
+ });
40
+ return pageBreakRunParagraphProjectionDispositionForFeatures({
41
+ attrs,
42
+ effectiveFrame: attrs._originalFormatting?.frame,
43
+ hasTextBoxAnchor
44
+ });
45
+ };
46
+ //#endregion
47
+ export { pageBreakRunParagraphProjectionDisposition, pageBreakRunParagraphProjectionDispositionForFeatures };
@@ -53,7 +53,12 @@ function findTextInPmParagraph(doc, paragraphFrom, paragraphTo, searchText) {
53
53
  if (!searchText) return null;
54
54
  let fullText = "";
55
55
  const textPositions = [];
56
+ const structuralBoundaryOffsets = [];
56
57
  doc.nodesBetween(paragraphFrom, paragraphTo, (node, pos) => {
58
+ if (node.type.name === "pageBreakRun") {
59
+ structuralBoundaryOffsets.push(fullText.length);
60
+ return false;
61
+ }
57
62
  if (node.marks.some((m) => m.type.name === "insertion")) return false;
58
63
  if (node.isText && node.text) {
59
64
  textPositions.push({
@@ -81,6 +86,7 @@ function findTextInPmParagraph(doc, paragraphFrom, paragraphTo, searchText) {
81
86
  if (firstMatch === -1) return null;
82
87
  if (fullText.indexOf(searchText, firstMatch + 1) !== -1) return null;
83
88
  const matchEnd = firstMatch + searchText.length;
89
+ if (structuralBoundaryOffsets.some((offset) => offset > firstMatch && offset < matchEnd)) return null;
84
90
  let segmentStart = 0;
85
91
  for (const position of textPositions) {
86
92
  const segmentEnd = segmentStart + position.text.length;
@@ -0,0 +1,12 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { ParagraphAttrs } from "./schema/nodes.js";
3
+ //#region src/prosemirror/paragraphAlignment.d.ts
4
+ /**
5
+ * Read the authored paragraph-level `w:jc`, independently of the effective
6
+ * alignment used for layout. Imported and command-authored paragraphs carry
7
+ * explicit provenance; the final branch covers PM-created content whose
8
+ * effective value is observably different from its style.
9
+ */
10
+ declare const directParagraphAlignment: (attrs: ParagraphAttrs) => document_d_exports.ParagraphAlignment | undefined;
11
+ //#endregion
12
+ export { directParagraphAlignment };
@@ -0,0 +1,13 @@
1
+ //#region src/prosemirror/paragraphAlignment.ts
2
+ /**
3
+ * Read the authored paragraph-level `w:jc`, independently of the effective
4
+ * alignment used for layout. Imported and command-authored paragraphs carry
5
+ * explicit provenance; the final branch covers PM-created content whose
6
+ * effective value is observably different from its style.
7
+ */
8
+ const directParagraphAlignment = (attrs) => {
9
+ if (attrs._originalFormatting?.alignment != null) return attrs._originalFormatting.alignment;
10
+ if (attrs.alignment != null && attrs.alignment !== attrs.alignmentFromStyle) return attrs.alignment;
11
+ };
12
+ //#endregion
13
+ export { directParagraphAlignment };
@@ -0,0 +1,29 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { FolioAIParagraphSpacing } from "../ai-edits/types.js";
3
+ import { ParagraphAttrs } from "./schema/nodes.js";
4
+ //#region src/prosemirror/paragraphSpacing.d.ts
5
+ /** The complete modeled attribute set of one direct `w:pPr/w:spacing`. */
6
+ declare const DIRECT_PARAGRAPH_SPACING_KEYS: readonly ["spaceBefore", "spaceAfter", "lineSpacing", "lineSpacingRule", "beforeAutospacing", "afterAutospacing"];
7
+ type DirectParagraphSpacing = FolioAIParagraphSpacing;
8
+ type ExactLineSpacingProvenance = Exclude<NonNullable<ParagraphAttrs["lineSpacingExplicit"]>, boolean>;
9
+ declare const lineSpacingProvenanceFromSpacing: (spacing: DirectParagraphSpacing | null | undefined) => ExactLineSpacingProvenance | undefined;
10
+ /** Project only authored `w:spacing` attributes, preserving explicit zero and false. */
11
+ declare const paragraphSpacingFromFormatting: (formatting: DirectParagraphSpacing | null | undefined) => DirectParagraphSpacing | undefined;
12
+ /**
13
+ * Read direct spacing independently of the effective values used for layout.
14
+ * Imported and command-authored paragraphs keep their source in
15
+ * `_originalFormatting`; explicit PM attrs cover newly created content.
16
+ */
17
+ declare const directParagraphSpacing: (attrs: ParagraphAttrs) => DirectParagraphSpacing | undefined;
18
+ /** Structural equality for the canonical, fixed-order spacing projection. */
19
+ declare const paragraphSpacingEqual: (left: DirectParagraphSpacing | null | undefined, right: DirectParagraphSpacing | null | undefined) => boolean;
20
+ /** Replace the complete direct `w:spacing` cluster in canonical formatting. */
21
+ declare const withDirectParagraphSpacing: (formatting: document_d_exports.ParagraphFormatting | null | undefined, spacing: DirectParagraphSpacing | null | undefined) => document_d_exports.ParagraphFormatting | undefined;
22
+ type ParagraphSpacingAttrPatchOptions = {
23
+ direct: DirectParagraphSpacing | null | undefined;
24
+ inherited: DirectParagraphSpacing | null | undefined;
25
+ };
26
+ /** Project direct-over-inherited spacing into the attrs consumed by layout. */
27
+ declare const paragraphSpacingAttrPatch: ({ direct, inherited }: ParagraphSpacingAttrPatchOptions) => Record<string, unknown>;
28
+ //#endregion
29
+ export { DIRECT_PARAGRAPH_SPACING_KEYS, DirectParagraphSpacing, directParagraphSpacing, lineSpacingProvenanceFromSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing };
@@ -0,0 +1,125 @@
1
+ import { autospacingMatchesBase, hasAutospacingBaseSide, setAutospacingBaseValue } from "./autospacingBase.js";
2
+ //#region src/prosemirror/paragraphSpacing.ts
3
+ /** The complete modeled attribute set of one direct `w:pPr/w:spacing`. */
4
+ const DIRECT_PARAGRAPH_SPACING_KEYS = [
5
+ "spaceBefore",
6
+ "spaceAfter",
7
+ "lineSpacing",
8
+ "lineSpacingRule",
9
+ "beforeAutospacing",
10
+ "afterAutospacing"
11
+ ];
12
+ const lineSpacingProvenanceFromSpacing = (spacing) => {
13
+ const hasValue = spacing?.lineSpacing !== void 0;
14
+ const hasRule = spacing?.lineSpacingRule !== void 0;
15
+ if (hasValue && hasRule) return "both";
16
+ if (hasValue) return "value";
17
+ return hasRule ? "rule" : void 0;
18
+ };
19
+ /** Resolve exact provenance, including editor states written with the former boolean marker. */
20
+ const lineSpacingProvenance = (attrs) => {
21
+ if (typeof attrs.lineSpacingExplicit === "string") return attrs.lineSpacingExplicit;
22
+ const original = paragraphSpacingFromFormatting(attrs._originalFormatting);
23
+ const originalProvenance = lineSpacingProvenanceFromSpacing(original);
24
+ if (originalProvenance !== void 0 || attrs.lineSpacingExplicit !== true) return originalProvenance;
25
+ return lineSpacingProvenanceFromSpacing({
26
+ ...typeof attrs.lineSpacing === "number" ? { lineSpacing: attrs.lineSpacing } : {},
27
+ ...attrs.lineSpacingRule !== void 0 ? { lineSpacingRule: attrs.lineSpacingRule } : {}
28
+ });
29
+ };
30
+ /** Project only authored `w:spacing` attributes, preserving explicit zero and false. */
31
+ const paragraphSpacingFromFormatting = (formatting) => {
32
+ if (!formatting) return;
33
+ const spacing = {
34
+ ...formatting.spaceBefore !== void 0 ? { spaceBefore: formatting.spaceBefore } : {},
35
+ ...formatting.spaceAfter !== void 0 ? { spaceAfter: formatting.spaceAfter } : {},
36
+ ...formatting.lineSpacing !== void 0 ? { lineSpacing: formatting.lineSpacing } : {},
37
+ ...formatting.lineSpacingRule !== void 0 ? { lineSpacingRule: formatting.lineSpacingRule } : {},
38
+ ...formatting.beforeAutospacing !== void 0 ? { beforeAutospacing: formatting.beforeAutospacing } : {},
39
+ ...formatting.afterAutospacing !== void 0 ? { afterAutospacing: formatting.afterAutospacing } : {}
40
+ };
41
+ return Object.keys(spacing).length > 0 ? spacing : void 0;
42
+ };
43
+ /**
44
+ * Read direct spacing independently of the effective values used for layout.
45
+ * Imported and command-authored paragraphs keep their source in
46
+ * `_originalFormatting`; explicit PM attrs cover newly created content.
47
+ */
48
+ const directParagraphSpacing = (attrs) => {
49
+ const spacing = paragraphSpacingFromFormatting(attrs._originalFormatting) ?? {};
50
+ const lineProvenance = lineSpacingProvenance(attrs);
51
+ const spaceBefore = attrs.spaceBefore;
52
+ const spaceAfter = attrs.spaceAfter;
53
+ const beforeHasBase = hasAutospacingBaseSide(attrs._autospacingBase, "before");
54
+ const afterHasBase = hasAutospacingBaseSide(attrs._autospacingBase, "after");
55
+ const beforeAutospacingEdited = beforeHasBase ? !autospacingMatchesBase(attrs._autospacingBase, "before", spaceBefore) : spacing.beforeAutospacing === true && attrs._autospacingBase == null;
56
+ const afterAutospacingEdited = afterHasBase ? !autospacingMatchesBase(attrs._autospacingBase, "after", spaceAfter) : spacing.afterAutospacing === true && attrs._autospacingBase == null;
57
+ if (beforeAutospacingEdited) {
58
+ spacing.beforeAutospacing = false;
59
+ if (typeof spaceBefore === "number") spacing.spaceBefore = spaceBefore;
60
+ else Reflect.deleteProperty(spacing, "spaceBefore");
61
+ }
62
+ if (afterAutospacingEdited) {
63
+ spacing.afterAutospacing = false;
64
+ if (typeof spaceAfter === "number") spacing.spaceAfter = spaceAfter;
65
+ else Reflect.deleteProperty(spacing, "spaceAfter");
66
+ }
67
+ if (spacing.spaceBefore !== void 0 || attrs.spacingExplicit?.before === true) if (typeof spaceBefore === "number") spacing.spaceBefore = spaceBefore;
68
+ else Reflect.deleteProperty(spacing, "spaceBefore");
69
+ if (spacing.spaceAfter !== void 0 || attrs.spacingExplicit?.after === true) if (typeof spaceAfter === "number") spacing.spaceAfter = spaceAfter;
70
+ else Reflect.deleteProperty(spacing, "spaceAfter");
71
+ if (lineProvenance === "value" || lineProvenance === "both") if (typeof attrs.lineSpacing === "number") spacing.lineSpacing = attrs.lineSpacing;
72
+ else Reflect.deleteProperty(spacing, "lineSpacing");
73
+ else Reflect.deleteProperty(spacing, "lineSpacing");
74
+ if (lineProvenance === "rule" || lineProvenance === "both") if (attrs.lineSpacingRule !== void 0) spacing.lineSpacingRule = attrs.lineSpacingRule;
75
+ else Reflect.deleteProperty(spacing, "lineSpacingRule");
76
+ else Reflect.deleteProperty(spacing, "lineSpacingRule");
77
+ return Object.keys(spacing).length > 0 ? spacing : void 0;
78
+ };
79
+ /** Structural equality for the canonical, fixed-order spacing projection. */
80
+ const paragraphSpacingEqual = (left, right) => (left?.spaceBefore ?? null) === (right?.spaceBefore ?? null) && (left?.spaceAfter ?? null) === (right?.spaceAfter ?? null) && (left?.lineSpacing ?? null) === (right?.lineSpacing ?? null) && (left?.lineSpacingRule ?? null) === (right?.lineSpacingRule ?? null) && (left?.beforeAutospacing ?? null) === (right?.beforeAutospacing ?? null) && (left?.afterAutospacing ?? null) === (right?.afterAutospacing ?? null);
81
+ /** Replace the complete direct `w:spacing` cluster in canonical formatting. */
82
+ const withDirectParagraphSpacing = (formatting, spacing) => {
83
+ const result = { ...formatting };
84
+ for (const key of DIRECT_PARAGRAPH_SPACING_KEYS) Reflect.deleteProperty(result, key);
85
+ Reflect.deleteProperty(result, "spacingExplicit");
86
+ if (spacing) {
87
+ Object.assign(result, spacing);
88
+ const spacingExplicit = {
89
+ ...spacing.spaceBefore !== void 0 ? { before: true } : {},
90
+ ...spacing.spaceAfter !== void 0 ? { after: true } : {}
91
+ };
92
+ if (Object.keys(spacingExplicit).length > 0) result.spacingExplicit = spacingExplicit;
93
+ }
94
+ return Object.keys(result).length > 0 ? result : void 0;
95
+ };
96
+ /** Project direct-over-inherited spacing into the attrs consumed by layout. */
97
+ const paragraphSpacingAttrPatch = ({ direct, inherited }) => {
98
+ const effective = {
99
+ ...inherited,
100
+ ...direct
101
+ };
102
+ const spacingExplicit = {
103
+ ...direct?.spaceBefore !== void 0 ? { before: true } : {},
104
+ ...direct?.spaceAfter !== void 0 ? { after: true } : {}
105
+ };
106
+ const spacingFromStyle = {
107
+ ...direct?.spaceBefore === void 0 && inherited?.spaceBefore !== void 0 ? { before: true } : {},
108
+ ...direct?.spaceAfter === void 0 && inherited?.spaceAfter !== void 0 ? { after: true } : {}
109
+ };
110
+ const autospacingBase = {};
111
+ if (effective.beforeAutospacing === true) setAutospacingBaseValue(autospacingBase, "before", effective.spaceBefore);
112
+ if (effective.afterAutospacing === true) setAutospacingBaseValue(autospacingBase, "after", effective.spaceAfter);
113
+ return {
114
+ spaceBefore: effective.spaceBefore ?? null,
115
+ spaceAfter: effective.spaceAfter ?? null,
116
+ lineSpacing: effective.lineSpacing ?? null,
117
+ lineSpacingRule: effective.lineSpacingRule ?? null,
118
+ lineSpacingExplicit: lineSpacingProvenanceFromSpacing(direct) ?? null,
119
+ spacingExplicit: Object.keys(spacingExplicit).length > 0 ? spacingExplicit : null,
120
+ spacingFromImplicitDefaultStyle: Object.keys(spacingFromStyle).length > 0 ? spacingFromStyle : null,
121
+ _autospacingBase: Object.keys(autospacingBase).length > 0 ? autospacingBase : null
122
+ };
123
+ };
124
+ //#endregion
125
+ export { DIRECT_PARAGRAPH_SPACING_KEYS, directParagraphSpacing, lineSpacingProvenanceFromSpacing, paragraphSpacingAttrPatch, paragraphSpacingEqual, paragraphSpacingFromFormatting, withDirectParagraphSpacing };
@@ -0,0 +1,8 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+ import { NumberingMap } from "../../docx/numberingParser.js";
3
+ import { EditorState, Plugin } from "prosemirror-state";
4
+ //#region src/prosemirror/plugins/documentNumbering.d.ts
5
+ declare const createDocumentNumberingPlugin: (definitions: document_d_exports.NumberingDefinitions | null | undefined) => Plugin;
6
+ declare const getDocumentNumbering: (state: EditorState) => NumberingMap | null;
7
+ //#endregion
8
+ export { createDocumentNumberingPlugin, getDocumentNumbering };
@@ -0,0 +1,18 @@
1
+ import { getCachedNumberingMap } from "../../docx/numberingParser.js";
2
+ import { Plugin, PluginKey } from "prosemirror-state";
3
+ //#region src/prosemirror/plugins/documentNumbering.ts
4
+ /** Document-scoped numbering definitions for list-editing commands. */
5
+ const documentNumberingKey = new PluginKey("documentNumbering");
6
+ const createDocumentNumberingPlugin = (definitions) => {
7
+ const numbering = definitions ? getCachedNumberingMap(definitions) : null;
8
+ return new Plugin({
9
+ key: documentNumberingKey,
10
+ state: {
11
+ init: () => numbering,
12
+ apply: (_transaction, value) => value
13
+ }
14
+ });
15
+ };
16
+ const getDocumentNumbering = (state) => documentNumberingKey.getState(state) ?? null;
17
+ //#endregion
18
+ export { createDocumentNumberingPlugin, getDocumentNumbering };
@@ -9,8 +9,9 @@ type TextChunk = {
9
9
  };
10
10
  /**
11
11
  * Collect every block-level node's text content as an array of
12
- * chunks (one per PM text node), grouped per block. Join a block's
13
- * chunk texts to scan it as a single string.
12
+ * chunks (one per PM text node), grouped per uninterrupted scan segment.
13
+ * An explicit page-break run ends a segment: it is zero-width in the joined
14
+ * text, but a text-shaped overlay must not select across that structural atom.
14
15
  */
15
16
  declare const collectBlockChunks: (doc: Node) => TextChunk[][];
16
17
  /** Map a joined-string offset back to its PM doc position. */
@@ -1,15 +1,25 @@
1
1
  //#region src/prosemirror/plugins/pmTextScan.ts
2
2
  /**
3
3
  * Collect every block-level node's text content as an array of
4
- * chunks (one per PM text node), grouped per block. Join a block's
5
- * chunk texts to scan it as a single string.
4
+ * chunks (one per PM text node), grouped per uninterrupted scan segment.
5
+ * An explicit page-break run ends a segment: it is zero-width in the joined
6
+ * text, but a text-shaped overlay must not select across that structural atom.
6
7
  */
7
8
  const collectBlockChunks = (doc) => {
8
9
  const blocks = [];
9
10
  doc.descendants((node, pos) => {
10
11
  if (node.isTextblock) {
11
- const chunks = [];
12
+ let chunks = [];
13
+ const flushSegment = () => {
14
+ if (chunks.length === 0) return;
15
+ blocks.push(chunks);
16
+ chunks = [];
17
+ };
12
18
  node.descendants((child, offset) => {
19
+ if (child.type.name === "pageBreakRun") {
20
+ flushSegment();
21
+ return false;
22
+ }
13
23
  if (child.isText && child.text !== void 0) {
14
24
  const start = pos + 1 + offset;
15
25
  chunks.push({
@@ -30,7 +40,7 @@ const collectBlockChunks = (doc) => {
30
40
  }
31
41
  return true;
32
42
  });
33
- if (chunks.length > 0) blocks.push(chunks);
43
+ flushSegment();
34
44
  return false;
35
45
  }
36
46
  return true;
@@ -1,7 +1,8 @@
1
1
  import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
2
+ import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
2
3
  import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
3
- import { isHistoryTransaction } from "prosemirror-history";
4
4
  import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
5
+ import { isHistoryTransaction } from "prosemirror-history";
5
6
  //#region src/prosemirror/plugins/suggestionMode.ts
6
7
  /**
7
8
  * Suggestion Mode Plugin
@@ -71,7 +72,7 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
71
72
  function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState) {
72
73
  const ranges = [];
73
74
  doc.nodesBetween(from, to, (node, pos) => {
74
- if (!node.isText && !(node.isInline && node.type.allowsMarkType(deletionType))) return;
75
+ if (!canCarryTrackedRunMark(node)) return;
75
76
  const start = Math.max(pos, from);
76
77
  const end = Math.min(pos + node.nodeSize, to);
77
78
  if (start >= end) return;
@@ -92,16 +93,13 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
92
93
  }
93
94
  }
94
95
  /**
95
- * Add the insertion mark to every inline node in `[from, to)` that can carry
96
- * it and does not already carry a tracked-change mark. Mirrors the catch-all's
97
- * node-by-node walk: a leaf text node's own `markSet` is empty so
98
- * `allowsMarkType` is false, hence the explicit `isText` arm; content already
99
- * carrying an insertion/deletion is left untouched so another author's revision
100
- * isn't overwritten. eigenpal/docx-editor#784.
96
+ * Add the insertion mark to every run carrier in `[from, to)` that does not
97
+ * already carry a tracked-change mark. Content already carrying a revision is
98
+ * left untouched so another author's attribution is not overwritten.
101
99
  */
102
100
  function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, attrs) {
103
101
  doc.nodesBetween(from, to, (node, pos) => {
104
- if (!node.isText && !(node.isInline && node.type.allowsMarkType(insertionType))) return;
102
+ if (!canCarryTrackedRunMark(node)) return;
105
103
  if (node.marks.some((m) => m.type === insertionType || m.type === deletionType)) return;
106
104
  const start = Math.max(pos, from);
107
105
  const end = Math.min(pos + node.nodeSize, to);
@@ -289,7 +287,7 @@ function handleSuggestionDelete(state, dispatch, direction) {
289
287
  const deleteEnd = isBackward ? $from.pos : $from.pos + 1;
290
288
  if (deletePos < 0 || deleteEnd > state.doc.content.size) return true;
291
289
  const nodeAfter = state.doc.resolve(deletePos).nodeAfter;
292
- if (!nodeAfter || !(nodeAfter.isText || nodeAfter.isInline && nodeAfter.type.allowsMarkType(deletionType))) return false;
290
+ if (!nodeAfter || !canCarryTrackedRunMark(nodeAfter)) return false;
293
291
  const hasOwnInsertion = nodeAfter.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
294
292
  if (nodeAfter.marks.some((m) => m.type === deletionType)) {
295
293
  const newPos = isBackward ? deletePos : deleteEnd;
@@ -449,7 +447,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
449
447
  const deletionType = newState.schema.marks["deletion"];
450
448
  for (const step of userTr.steps) step.getMap().forEach((_oldFrom, _oldTo, newFrom, newTo) => {
451
449
  if (newTo > newFrom) newState.doc.nodesBetween(newFrom, newTo, (node, pos) => {
452
- if (!node.isText && !(node.isInline && node.type.allowsMarkType(insertionType))) return;
450
+ if (!canCarryTrackedRunMark(node)) return;
453
451
  if (!node.marks.some((m) => m.type === insertionType || deletionType && m.type === deletionType)) {
454
452
  const nodeStart = Math.max(pos, newFrom);
455
453
  const nodeEnd = Math.min(pos + node.nodeSize, newTo);
@@ -0,0 +1,16 @@
1
+ import { RunStyleResolver } from "./runStyleFormatting.js";
2
+ import { Transaction } from "prosemirror-state";
3
+ //#region src/prosemirror/rebaseParagraphRunFormatting.d.ts
4
+ type RebaseParagraphRunFormattingOptions = {
5
+ nextAttrs: Record<string, unknown>;
6
+ paragraphPosition: number;
7
+ styleResolver: RunStyleResolver;
8
+ tr: Transaction;
9
+ };
10
+ /**
11
+ * Change paragraph attrs and re-resolve inherited run marks in the new style
12
+ * context without turning the old rendered style into direct run formatting.
13
+ */
14
+ declare const setParagraphAttrsWithRebasedRunFormatting: ({ nextAttrs, paragraphPosition, styleResolver, tr }: RebaseParagraphRunFormattingOptions) => Transaction;
15
+ //#endregion
16
+ export { setParagraphAttrsWithRebasedRunFormatting };
@@ -0,0 +1,61 @@
1
+ import { expandRunFormattingCarrier } from "./runFormattingInlineCarriers.js";
2
+ import { RUN_FORMATTING_MARK_NAMES } from "./runFormattingMarkNames.js";
3
+ import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "./runFormattingReconciliation.js";
4
+ import { paragraphRunStyleContext } from "./runStyleFormatting.js";
5
+ import { panic } from "better-result";
6
+ import { Mark } from "prosemirror-model";
7
+ //#region src/prosemirror/rebaseParagraphRunFormatting.ts
8
+ /**
9
+ * Change paragraph attrs and re-resolve inherited run marks in the new style
10
+ * context without turning the old rendered style into direct run formatting.
11
+ */
12
+ const setParagraphAttrsWithRebasedRunFormatting = ({ nextAttrs, paragraphPosition, styleResolver, tr }) => {
13
+ const paragraph = tr.doc.nodeAt(paragraphPosition);
14
+ if (!paragraph || paragraph.type.name !== "paragraph") return panic("Cannot rebase run formatting outside a paragraph", {
15
+ nodeType: paragraph?.type.name,
16
+ paragraphPosition
17
+ });
18
+ const previousContext = paragraphRunStyleContext(paragraph, styleResolver);
19
+ const nextContext = paragraphRunStyleContext(paragraph.type.create(nextAttrs, paragraph.content, paragraph.marks), styleResolver);
20
+ const changes = [];
21
+ const collectRebasedRepresentation = ({ node, position }) => {
22
+ const nextMarks = reconcileRunFormattingMarks({
23
+ authoredFormatting: readAuthoredRunFormatting({
24
+ context: previousContext,
25
+ marks: node.marks,
26
+ styleResolver
27
+ }),
28
+ context: nextContext,
29
+ node,
30
+ styleResolver
31
+ });
32
+ if (Mark.sameSet(node.marks, nextMarks)) return;
33
+ changes.push({
34
+ attrs: node.attrs,
35
+ currentFormattingMarks: node.marks.filter(({ type }) => RUN_FORMATTING_MARK_NAMES.has(type.name)),
36
+ from: position,
37
+ isText: node.isText,
38
+ marks: nextMarks,
39
+ to: position + node.nodeSize
40
+ });
41
+ };
42
+ paragraph.descendants((node, relativePosition) => {
43
+ if (!node.isInline) return true;
44
+ const carrier = expandRunFormattingCarrier(node, paragraphPosition + 1 + relativePosition);
45
+ if (!carrier) return !node.isAtom;
46
+ for (const representation of carrier.representations) collectRebasedRepresentation(representation);
47
+ return false;
48
+ });
49
+ tr = tr.setNodeMarkup(paragraphPosition, void 0, nextAttrs);
50
+ for (const { attrs, currentFormattingMarks, from, isText, marks, to } of changes) {
51
+ if (!isText) {
52
+ tr = tr.setNodeMarkup(from, void 0, attrs, marks);
53
+ continue;
54
+ }
55
+ for (const mark of currentFormattingMarks) tr = tr.removeMark(from, to, mark.type);
56
+ for (const mark of marks) if (RUN_FORMATTING_MARK_NAMES.has(mark.type.name)) tr = tr.addMark(from, to, mark);
57
+ }
58
+ return tr;
59
+ };
60
+ //#endregion
61
+ export { setParagraphAttrsWithRebasedRunFormatting };
@@ -0,0 +1,64 @@
1
+ import { Node } from "prosemirror-model";
2
+ //#region src/prosemirror/runFormattingInlineCarriers.d.ts
3
+ type RunFormattingInlineAtomDisposition = "break-run" | "field-run" | "not-a-run" | "page-break-carrier" | "structured-field" | "symbol-run" | "tab-run" | "text-run";
4
+ /**
5
+ * How every inline atom maps to a serialized run-formatting carrier.
6
+ *
7
+ * This is intentionally separate from tracked wrapper ownership: drawings and
8
+ * bookmarks can sit inside an insertion/deletion wrapper, but their editor
9
+ * marks do not serialize as run properties. The runtime fixed-point test binds
10
+ * this map to the complete schema atom set.
11
+ */
12
+ declare const RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS: Readonly<{
13
+ readonly bookmarkBoundary: "not-a-run";
14
+ readonly field: "field-run";
15
+ readonly hardBreak: "break-run";
16
+ readonly image: "not-a-run";
17
+ readonly math: "not-a-run";
18
+ readonly pageBreakRun: "page-break-carrier";
19
+ readonly renderedPageBreak: "not-a-run";
20
+ readonly shape: "not-a-run";
21
+ readonly structuredField: "structured-field";
22
+ readonly symbol: "symbol-run";
23
+ readonly tab: "tab-run";
24
+ readonly text: "text-run";
25
+ readonly textBoxAnchor: "not-a-run";
26
+ }>;
27
+ declare const runFormattingInlineAtomDisposition: (node: Node) => RunFormattingInlineAtomDisposition | null;
28
+ type RunFormattingCarrierRepresentation = {
29
+ node: Node;
30
+ position: number;
31
+ role: "owner" | "serialized-result";
32
+ };
33
+ type RunFormattingCarrier = {
34
+ disposition: Exclude<RunFormattingInlineAtomDisposition, "not-a-run">;
35
+ node: Node;
36
+ position: number;
37
+ representations: readonly RunFormattingCarrierRepresentation[];
38
+ };
39
+ type SelectedRunFormattingCarrierRepresentation = RunFormattingCarrierRepresentation & {
40
+ carrierDisposition: RunFormattingCarrier["disposition"];
41
+ from: number;
42
+ to: number;
43
+ };
44
+ /**
45
+ * Expand one logical carrier into every editor node that materializes its run
46
+ * properties. A structured field is owned by its outer atom, while its result
47
+ * runs are derived representations needed by the package serializer.
48
+ */
49
+ declare const expandRunFormattingCarrier: (node: Node, position: number) => RunFormattingCarrier | null;
50
+ type SelectRunFormattingCarrierRepresentationsOptions = {
51
+ doc: Node;
52
+ from: number;
53
+ to: number;
54
+ };
55
+ /**
56
+ * Resolve a document range to the exact editor nodes that serialize run
57
+ * properties. The result is shared by every formatting mutation so preflight,
58
+ * direct edits, and tracked edits cannot disagree about ownership.
59
+ */
60
+ declare const selectRunFormattingCarrierRepresentations: ({ doc, from, to }: SelectRunFormattingCarrierRepresentationsOptions) => SelectedRunFormattingCarrierRepresentation[];
61
+ /** Deterministic visible text for one logical formatting revision carrier. */
62
+ declare const runFormattingCarrierReviewText: (carrier: RunFormattingCarrier) => string;
63
+ //#endregion
64
+ export { RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS, RunFormattingCarrier, RunFormattingCarrierRepresentation, RunFormattingInlineAtomDisposition, SelectedRunFormattingCarrierRepresentation, expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition, selectRunFormattingCarrierRepresentations };
@@ -0,0 +1,121 @@
1
+ import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
2
+ import { expectFieldAttrs, expectSymbolAttrs } from "./attrs/index.js";
3
+ import { panic } from "better-result";
4
+ //#region src/prosemirror/runFormattingInlineCarriers.ts
5
+ /**
6
+ * How every inline atom maps to a serialized run-formatting carrier.
7
+ *
8
+ * This is intentionally separate from tracked wrapper ownership: drawings and
9
+ * bookmarks can sit inside an insertion/deletion wrapper, but their editor
10
+ * marks do not serialize as run properties. The runtime fixed-point test binds
11
+ * this map to the complete schema atom set.
12
+ */
13
+ const RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS = Object.freeze({
14
+ bookmarkBoundary: "not-a-run",
15
+ field: "field-run",
16
+ hardBreak: "break-run",
17
+ image: "not-a-run",
18
+ math: "not-a-run",
19
+ pageBreakRun: "page-break-carrier",
20
+ renderedPageBreak: "not-a-run",
21
+ shape: "not-a-run",
22
+ structuredField: "structured-field",
23
+ symbol: "symbol-run",
24
+ tab: "tab-run",
25
+ text: "text-run",
26
+ textBoxAnchor: "not-a-run"
27
+ });
28
+ const dispositionsByName = new Map(Object.entries(RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS));
29
+ const runFormattingInlineAtomDisposition = (node) => {
30
+ if (!node.isInline || !node.isAtom) return null;
31
+ const disposition = dispositionsByName.get(node.type.name);
32
+ if (!disposition) return panic(`Inline atom ${JSON.stringify(node.type.name)} has no run-formatting disposition`);
33
+ return disposition;
34
+ };
35
+ /**
36
+ * Expand one logical carrier into every editor node that materializes its run
37
+ * properties. A structured field is owned by its outer atom, while its result
38
+ * runs are derived representations needed by the package serializer.
39
+ */
40
+ const expandRunFormattingCarrier = (node, position) => {
41
+ const disposition = runFormattingInlineAtomDisposition(node);
42
+ if (disposition === null || disposition === "not-a-run") return null;
43
+ if (disposition !== "structured-field") return {
44
+ disposition,
45
+ node,
46
+ position,
47
+ representations: [{
48
+ node,
49
+ position,
50
+ role: "owner"
51
+ }]
52
+ };
53
+ const representations = [{
54
+ node,
55
+ position,
56
+ role: "owner"
57
+ }];
58
+ node.descendants((child, relativePosition) => {
59
+ const childDisposition = runFormattingInlineAtomDisposition(child);
60
+ if (childDisposition === null) return true;
61
+ if (childDisposition === "structured-field") return panic("A structured field cannot contain another structured field");
62
+ if (childDisposition !== "not-a-run") representations.push({
63
+ node: child,
64
+ position: position + 1 + relativePosition,
65
+ role: "serialized-result"
66
+ });
67
+ return false;
68
+ });
69
+ return {
70
+ disposition,
71
+ node,
72
+ position,
73
+ representations
74
+ };
75
+ };
76
+ /**
77
+ * Resolve a document range to the exact editor nodes that serialize run
78
+ * properties. The result is shared by every formatting mutation so preflight,
79
+ * direct edits, and tracked edits cannot disagree about ownership.
80
+ */
81
+ const selectRunFormattingCarrierRepresentations = ({ doc, from, to }) => {
82
+ const selected = [];
83
+ doc.nodesBetween(from, to, (node, position) => {
84
+ if (!node.isInline) return true;
85
+ const carrier = expandRunFormattingCarrier(node, position);
86
+ if (!carrier) return !node.isAtom;
87
+ if (carrier.disposition === "page-break-carrier") return false;
88
+ for (const representation of carrier.representations) {
89
+ if (runFormattingInlineAtomDisposition(representation.node) === "page-break-carrier") continue;
90
+ const representationFrom = Math.max(from, representation.position);
91
+ const representationTo = Math.min(to, representation.position + representation.node.nodeSize);
92
+ if (representationFrom >= representationTo) continue;
93
+ if (carrier.disposition === "structured-field" && representation.role === "owner" && (representationFrom !== representation.position || representationTo !== representation.position + representation.node.nodeSize)) continue;
94
+ selected.push({
95
+ ...representation,
96
+ carrierDisposition: carrier.disposition,
97
+ from: representationFrom,
98
+ to: representationTo
99
+ });
100
+ }
101
+ return false;
102
+ });
103
+ return selected;
104
+ };
105
+ /** Deterministic visible text for one logical formatting revision carrier. */
106
+ const runFormattingCarrierReviewText = (carrier) => {
107
+ switch (carrier.disposition) {
108
+ case "text-run": return carrier.node.text ?? "";
109
+ case "tab-run": return " ";
110
+ case "break-run": return "\n";
111
+ case "page-break-carrier": return "";
112
+ case "symbol-run": {
113
+ const { char } = expectSymbolAttrs(carrier.node);
114
+ return decodeOoxmlSymbolCharacter(char) ?? "�";
115
+ }
116
+ case "field-run":
117
+ case "structured-field": return expectFieldAttrs(carrier.node).displayText ?? carrier.node.textContent;
118
+ }
119
+ };
120
+ //#endregion
121
+ export { RUN_FORMATTING_INLINE_ATOM_DISPOSITIONS, expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition, selectRunFormattingCarrierRepresentations };