@stll/folio-core 0.36.0 → 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 (200) hide show
  1. package/README.md +59 -1
  2. package/dist/ai-edits/apply.js +545 -296
  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 +133 -71
  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 +99 -19
  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 +45 -57
  20. package/dist/ai-suggestions/conflict.js +1 -4
  21. package/dist/ai-suggestions/text-positions.d.ts +8 -0
  22. package/dist/ai-suggestions/text-positions.js +27 -5
  23. package/dist/compare/__fixtures__/body-sequence.js +3 -4
  24. package/dist/compare/column-alignment.d.ts +13 -13
  25. package/dist/compare/column-alignment.js +78 -39
  26. package/dist/compare/compare.d.ts +4 -3
  27. package/dist/compare/compare.js +41 -5
  28. package/dist/compare/content-alignment.d.ts +95 -0
  29. package/dist/compare/content-alignment.js +1450 -0
  30. package/dist/compare/content-types.d.ts +100 -0
  31. package/dist/compare/content-types.js +0 -0
  32. package/dist/compare/content.d.ts +209 -0
  33. package/dist/compare/content.js +885 -0
  34. package/dist/compare/formatting.d.ts +5 -5
  35. package/dist/compare/formatting.js +6 -4
  36. package/dist/compare/plan.d.ts +6 -1
  37. package/dist/compare/plan.js +169 -567
  38. package/dist/compare/scenario.d.ts +2 -2
  39. package/dist/compare/types.d.ts +3 -3
  40. package/dist/compare/verification.d.ts +2 -2
  41. package/dist/compare/verification.js +25 -8
  42. package/dist/compat/eigenpal.d.ts +5 -3
  43. package/dist/compat/eigenpal.js +3 -2
  44. package/dist/controller/headerFooterEditorManager.js +14 -6
  45. package/dist/controller/hiddenEditorManager.d.ts +7 -2
  46. package/dist/controller/hiddenEditorManager.js +15 -6
  47. package/dist/controller/layoutPipeline.js +5 -3
  48. package/dist/controller/noteEditorManager.js +12 -7
  49. package/dist/display-list/build/headerFooterPrimitives.js +1 -0
  50. package/dist/display-list/build/storyPrimitives.d.ts +5 -2
  51. package/dist/display-list/build/storyPrimitives.js +60 -3
  52. package/dist/display-list/build/tablePrimitives.js +30 -0
  53. package/dist/display-list/primitives.d.ts +1 -1
  54. package/dist/document-operations.d.ts +6 -4
  55. package/dist/document-operations.js +77 -12
  56. package/dist/docx/blockContentParser.js +6 -27
  57. package/dist/docx/ensureParaIds.js +9 -4
  58. package/dist/docx/numberingParser.d.ts +3 -1
  59. package/dist/docx/numberingParser.js +4 -1
  60. package/dist/docx/paragraphParser.js +7 -6
  61. package/dist/docx/paragraphTraversal.js +30 -34
  62. package/dist/docx/rezip.d.ts +0 -8
  63. package/dist/docx/rezip.js +41 -16
  64. package/dist/docx/runParser.js +4 -2
  65. package/dist/docx/serializer/paragraphSerializer.js +2 -0
  66. package/dist/docx/serializer/runSerializer.js +5 -3
  67. package/dist/docx/serializer/trackedChangeAttributes.d.ts +3 -1
  68. package/dist/docx/serializer/trackedChangeAttributes.js +11 -1
  69. package/dist/docx/server/build.d.ts +1 -1
  70. package/dist/headless-layout.js +5 -4
  71. package/dist/index.d.ts +5 -3
  72. package/dist/index.js +3 -2
  73. package/dist/internal/headlessRevisionResolution.d.ts +23 -0
  74. package/dist/internal/headlessRevisionResolution.js +155 -0
  75. package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
  76. package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
  77. package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
  78. package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
  79. package/dist/internal/pageBreakRunPartition.d.ts +26 -0
  80. package/dist/internal/pageBreakRunPartition.js +35 -0
  81. package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
  82. package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
  83. package/dist/internal/sectionEndpointResolution.d.ts +32 -0
  84. package/dist/internal/sectionEndpointResolution.js +38 -0
  85. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  86. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
  87. package/dist/layout-bridge/convert/headerFooterLayout.js +48 -5
  88. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
  89. package/dist/layout-bridge/convert/toFlowBlocks.js +379 -75
  90. package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
  91. package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
  92. package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
  93. package/dist/layout-bridge/dom/textStreamDom.js +75 -0
  94. package/dist/layout-bridge/headerFooterLayout.js +16 -30
  95. package/dist/layout-engine/index.js +12 -3
  96. package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
  97. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
  98. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  99. package/dist/layout-engine/types.d.ts +7 -0
  100. package/dist/layout-painter/imageLayout.d.ts +1 -1
  101. package/dist/layout-painter/renderTable.js +20 -0
  102. package/dist/model.d.ts +1 -1
  103. package/dist/paged-layout/rangeProjection.js +6 -9
  104. package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
  105. package/dist/paged-layout/transactionDirtyRange.js +23 -1
  106. package/dist/prosemirror/attrs/index.d.ts +7 -3
  107. package/dist/prosemirror/attrs/index.js +113 -7
  108. package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
  109. package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
  110. package/dist/prosemirror/commands/comments.d.ts +10 -1
  111. package/dist/prosemirror/commands/comments.js +190 -59
  112. package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
  113. package/dist/prosemirror/commands/formatPainter.js +319 -63
  114. package/dist/prosemirror/commands/index.d.ts +2 -2
  115. package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
  116. package/dist/prosemirror/commands/pageBreak.js +18 -36
  117. package/dist/prosemirror/commands/propertyChangeScope.d.ts +7 -5
  118. package/dist/prosemirror/commands/propertyChangeScope.js +27 -18
  119. package/dist/prosemirror/conversion/fromProseDoc.d.ts +7 -2
  120. package/dist/prosemirror/conversion/fromProseDoc.js +809 -209
  121. package/dist/prosemirror/conversion/runShadingMark.js +2 -2
  122. package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
  123. package/dist/prosemirror/conversion/toProseDoc.js +416 -283
  124. package/dist/prosemirror/extensions/StarterKit.js +4 -0
  125. package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -8
  126. package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
  127. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +1 -1
  128. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
  129. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
  130. package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
  131. package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
  132. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
  133. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
  134. package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
  135. package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
  136. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
  137. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
  138. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
  139. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
  140. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
  141. package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
  142. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
  143. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  144. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
  145. package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
  146. package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
  147. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
  148. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
  149. package/dist/prosemirror/findReplaceSelection.js +8 -3
  150. package/dist/prosemirror/index.d.ts +3 -3
  151. package/dist/prosemirror/listMarker.d.ts +26 -1
  152. package/dist/prosemirror/listMarker.js +108 -14
  153. package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
  154. package/dist/prosemirror/pageBreakRunProjection.js +47 -0
  155. package/dist/prosemirror/paraText.js +6 -0
  156. package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
  157. package/dist/prosemirror/paragraphSpacing.js +125 -0
  158. package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
  159. package/dist/prosemirror/plugins/documentNumbering.js +18 -0
  160. package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
  161. package/dist/prosemirror/plugins/pmTextScan.js +14 -4
  162. package/dist/prosemirror/plugins/suggestionMode.js +8 -10
  163. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
  164. package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
  165. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
  166. package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
  167. package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
  168. package/dist/prosemirror/runFormattingProvenance.js +48 -0
  169. package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
  170. package/dist/prosemirror/runFormattingReconciliation.js +54 -0
  171. package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
  172. package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
  173. package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
  174. package/dist/prosemirror/runStyleFormatting.js +105 -0
  175. package/dist/prosemirror/schema/index.d.ts +3 -3
  176. package/dist/prosemirror/schema/marks.d.ts +82 -9
  177. package/dist/prosemirror/schema/marks.js +73 -1
  178. package/dist/prosemirror/schema/nodes.d.ts +18 -5
  179. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
  180. package/dist/prosemirror/styles/resolvedStyleAttrs.js +44 -23
  181. package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
  182. package/dist/prosemirror/styles/styleResolver.js +3 -3
  183. package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
  184. package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
  185. package/dist/prosemirror/tableGridMutation.d.ts +20 -0
  186. package/dist/prosemirror/tableGridMutation.js +30 -0
  187. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
  188. package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
  189. package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
  190. package/dist/prosemirror/validation.js +23 -4
  191. package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
  192. package/dist/render-dom/RenderedDomContext.js +17 -34
  193. package/dist/server.d.ts +4 -4
  194. package/dist/server.js +2 -2
  195. package/dist/style-engine/styleEngine.d.ts +1 -1
  196. package/dist/utils/clipboard.js +1 -1
  197. package/dist/utils/findReplace.js +38 -21
  198. package/dist/version-comparison.d.ts +38 -44
  199. package/dist/version-comparison.js +270 -401
  200. package/package.json +3 -2
@@ -30,6 +30,7 @@ import { HighlightExtension } from "./marks/HighlightExtension.js";
30
30
  import { HyperlinkExtension } from "./marks/HyperlinkExtension.js";
31
31
  import { ItalicExtension } from "./marks/ItalicExtension.js";
32
32
  import { LanguageExtension } from "./marks/LanguageExtension.js";
33
+ import { PageBreakRunOwnerExtension } from "./marks/PageBreakRunOwnerExtension.js";
33
34
  import { RtlExtension } from "./marks/RtlExtension.js";
34
35
  import { RunFormattingOverrideExtension } from "./marks/RunFormattingOverrideExtension.js";
35
36
  import { RunShadingExtension } from "./marks/RunShadingExtension.js";
@@ -50,6 +51,7 @@ import { HorizontalRuleExtension } from "./nodes/HorizontalRuleExtension.js";
50
51
  import { ImageExtension } from "./nodes/ImageExtension.js";
51
52
  import { MathExtension } from "./nodes/MathExtension.js";
52
53
  import { PageBreakExtension } from "./nodes/PageBreakExtension.js";
54
+ import { PageBreakRunExtension } from "./nodes/PageBreakRunExtension.js";
53
55
  import { RenderedPageBreakExtension } from "./nodes/RenderedPageBreakExtension.js";
54
56
  import { SdtExtension } from "./nodes/SdtExtension.js";
55
57
  import { ShapeExtension } from "./nodes/ShapeExtension.js";
@@ -107,6 +109,7 @@ function createStarterKit(options = {}) {
107
109
  add("textEffect", TextEffectExtension());
108
110
  add("runFormattingOverride", RunFormattingOverrideExtension());
109
111
  add("characterStyle", CharacterStyleExtension());
112
+ add("pageBreakRunOwner", PageBreakRunOwnerExtension());
110
113
  add("comment", CommentExtension());
111
114
  add("insertion", InsertionExtension());
112
115
  add("deletion", DeletionExtension());
@@ -125,6 +128,7 @@ function createStarterKit(options = {}) {
125
128
  add("gapCursor", GapCursorExtension());
126
129
  add("horizontalRule", HorizontalRuleExtension());
127
130
  add("pageBreak", PageBreakExtension());
131
+ add("pageBreakRun", PageBreakRunExtension());
128
132
  add("renderedPageBreak", RenderedPageBreakExtension());
129
133
  add("field", FieldExtension());
130
134
  add("field", StructuredFieldExtension({ getInternalClipboardToken }));
@@ -6,6 +6,7 @@ import { expectParagraphAttrs } from "../../attrs/index.js";
6
6
  import { autospacingMatchesBase } from "../../autospacingBase.js";
7
7
  import { directParagraphAlignment } from "../../paragraphAlignment.js";
8
8
  import { directionIsRtl } from "../../paragraphDirection.js";
9
+ import { withDirectParagraphSpacing } from "../../paragraphSpacing.js";
9
10
  import { listAttrsFromResolvedStyle, paragraphAttrsFromResolvedStyle } from "../../styles/resolvedStyleAttrs.js";
10
11
  import { createNodeExtension } from "../create.js";
11
12
  import { Fragment } from "prosemirror-model";
@@ -165,7 +166,7 @@ function extractParagraphAttrsFromStyle(element) {
165
166
  if (spacing) {
166
167
  attrs.lineSpacing = spacing.lineSpacing;
167
168
  attrs.lineSpacingRule = spacing.lineSpacingRule;
168
- attrs.lineSpacingExplicit = true;
169
+ attrs.lineSpacingExplicit = "both";
169
170
  }
170
171
  }
171
172
  if (style.marginTop) {
@@ -183,6 +184,7 @@ const paragraphNodeSpec = {
183
184
  group: "block",
184
185
  attrs: {
185
186
  paraId: { default: null },
187
+ idStability: { default: void 0 },
186
188
  textId: { default: null },
187
189
  alignment: { default: null },
188
190
  alignmentFromStyle: { default: void 0 },
@@ -208,6 +210,7 @@ const paragraphNodeSpec = {
208
210
  listIsBullet: { default: null },
209
211
  listIsLegal: { default: null },
210
212
  listMarker: { default: null },
213
+ listMarkerTemplate: { default: void 0 },
211
214
  listMarkerHidden: { default: null },
212
215
  listMarkerFormatting: { default: null },
213
216
  listMarkerAlignment: { default: null },
@@ -417,7 +420,7 @@ function makeSetLineSpacing(value, rule = "auto") {
417
420
  return (state, dispatch) => setParagraphAttrsCmd({
418
421
  lineSpacing: value,
419
422
  lineSpacingRule: rule,
420
- lineSpacingExplicit: true
423
+ lineSpacingExplicit: "both"
421
424
  })(state, dispatch);
422
425
  }
423
426
  function makeIncreaseIndent(amount = 720) {
@@ -512,12 +515,13 @@ function makeApplyStyle(schema) {
512
515
  styleId,
513
516
  ...resolvedAttrs.styleName ? { styleName: resolvedAttrs.styleName } : {}
514
517
  }));
515
- const originalFormatting = expectParagraphAttrs(node)._originalFormatting;
516
- if (originalFormatting?.alignment !== void 0) {
517
- const formattingWithoutDirectAlignment = { ...originalFormatting };
518
- Reflect.deleteProperty(formattingWithoutDirectAlignment, "alignment");
519
- newAttrs["_originalFormatting"] = Object.keys(formattingWithoutDirectAlignment).length > 0 ? formattingWithoutDirectAlignment : null;
520
- }
518
+ const originalFormatting = {
519
+ ...expectParagraphAttrs(node)._originalFormatting,
520
+ styleId
521
+ };
522
+ Reflect.deleteProperty(originalFormatting, "alignment");
523
+ newAttrs["_originalFormatting"] = withDirectParagraphSpacing(originalFormatting, void 0);
524
+ newAttrs["spacingExplicit"] = null;
521
525
  const listAttrs = listAttrsFromResolvedStyle(resolvedAttrs, resolvedAttrs.numbering);
522
526
  if (listAttrs) Object.assign(newAttrs, listAttrs);
523
527
  }
@@ -1,10 +1,20 @@
1
1
  import { expectParagraphAttrs } from "../../attrs/index.js";
2
- import { PPR_CHANGE_SCOPED_ATTR_KEYS } from "../../commands/propertyChangeScope.js";
2
+ import { PPR_CHANGE_SCOPED_ATTR_KEYS, hasSerializableParagraphPropertyChange } from "../../commands/propertyChangeScope.js";
3
+ import { CLEARED_LIST_RENDERING_ATTRS, LIST_RENDERING_ATTR_KEYS } from "../../listMarker.js";
4
+ import { getDocumentNumbering } from "../../plugins/documentNumbering.js";
3
5
  import { SUGGESTION_META, makeRevisionInfo } from "../../plugins/suggestionMode.js";
6
+ import { listLevelAttrPatch } from "../../styles/resolvedStyleAttrs.js";
4
7
  import { createExtension } from "../create.js";
5
8
  import { goToNextCell, goToPrevCell } from "../nodes/TableExtension.js";
6
9
  import { Priority } from "../types.js";
10
+ import { panic } from "better-result";
7
11
  //#region src/prosemirror/extensions/features/ListExtension.ts
12
+ /**
13
+ * List Extension — list commands + keymaps
14
+ *
15
+ * No schema contribution — lists use paragraph attrs (numPr).
16
+ * Provides: toggle bullet/number, indent/outdent, enter/backspace handling.
17
+ */
8
18
  function chainCommands(...commands) {
9
19
  return (state, dispatch, view) => {
10
20
  for (const cmd of commands) if (cmd(state, dispatch, view)) return true;
@@ -25,65 +35,73 @@ function appendParagraphPropertyChange(attrs, existing, previousFormatting, rev)
25
35
  }]
26
36
  };
27
37
  }
28
- const LIST_FORMATTING_ATTRS = [
29
- "numPr",
30
- "listIsBullet",
31
- "listIsLegal",
32
- "listNumFmt",
33
- "listMarker",
34
- "listMarkerHidden",
35
- "listMarkerFormatting",
36
- "listMarkerAlignment",
37
- "listMarkerSuffix",
38
- "listLevelNumFmts",
39
- "listLevelStarts",
40
- "listAbstractNumId",
41
- "listStartOverride"
42
- ];
43
38
  function getPreviousListFormatting(attrs) {
44
39
  const previousFormatting = {};
45
40
  for (const key of PPR_CHANGE_SCOPED_ATTR_KEYS) {
46
41
  const value = attrs[key];
47
42
  if (value != null) previousFormatting[key] = value;
48
43
  }
49
- for (const key of LIST_FORMATTING_ATTRS) previousFormatting[key] = attrs[key] ?? null;
44
+ previousFormatting["numPr"] = attrs["numPr"] ?? null;
45
+ for (const key of LIST_RENDERING_ATTR_KEYS) previousFormatting[key] = attrs[key] ?? null;
50
46
  return previousFormatting;
51
47
  }
48
+ function clearListAttrs(attrs) {
49
+ const styleNumPr = attrs.numPrFromStyle;
50
+ const numPr = styleNumPr?.numId !== void 0 && styleNumPr.numId !== 0 ? {
51
+ numId: 0,
52
+ ilvl: attrs.numPr?.ilvl ?? styleNumPr.ilvl ?? 0
53
+ } : null;
54
+ return {
55
+ ...attrs,
56
+ numPr,
57
+ ...CLEARED_LIST_RENDERING_ATTRS
58
+ };
59
+ }
60
+ function hasActiveListNumbering(attrs) {
61
+ return attrs.numPr?.numId !== void 0 && attrs.numPr.numId !== 0;
62
+ }
52
63
  function toggleList(numId) {
53
64
  return (state, dispatch) => {
54
65
  const { $from, $to } = state.selection;
55
66
  const paragraph = $from.parent;
56
67
  if (paragraph.type.name !== "paragraph") return false;
57
68
  const isInSameList = paragraph.attrs["numPr"]?.numId === numId;
69
+ const rev = makeRevisionInfo(state);
70
+ if (rev) {
71
+ let hasPendingChange = false;
72
+ state.doc.nodesBetween($from.pos, $to.pos, (node) => {
73
+ if (node.type.name === "paragraph" && hasSerializableParagraphPropertyChange(expectParagraphAttrs(node)._propertyChanges)) {
74
+ hasPendingChange = true;
75
+ return false;
76
+ }
77
+ });
78
+ if (hasPendingChange) return false;
79
+ }
58
80
  if (!dispatch) return true;
59
81
  let tr = state.tr;
60
82
  const seen = /* @__PURE__ */ new Set();
61
- const rev = makeRevisionInfo(state);
62
83
  state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
63
84
  if (node.type.name === "paragraph" && !seen.has(pos)) {
64
85
  seen.add(pos);
65
86
  let nextAttrs;
66
- if (isInSameList) nextAttrs = {
67
- ...node.attrs,
68
- numPr: null,
69
- listIsBullet: null,
70
- listNumFmt: null,
71
- listMarker: null
72
- };
87
+ if (isInSameList) nextAttrs = clearListAttrs(expectParagraphAttrs(node));
73
88
  else {
74
89
  const isBullet = numId === 1;
75
90
  nextAttrs = {
76
91
  ...node.attrs,
92
+ ...CLEARED_LIST_RENDERING_ATTRS,
77
93
  numPr: {
78
94
  numId,
79
95
  ilvl: node.attrs["numPr"]?.ilvl || 0
80
96
  },
81
97
  listIsBullet: isBullet,
82
- listNumFmt: isBullet ? null : "decimal",
83
- listMarker: null
98
+ listNumFmt: isBullet ? null : "decimal"
84
99
  };
85
100
  }
86
- if (rev) nextAttrs = appendParagraphPropertyChange(nextAttrs, expectParagraphAttrs(node)._propertyChanges, getPreviousListFormatting(node.attrs), rev);
101
+ if (rev) {
102
+ const existing = expectParagraphAttrs(node)._propertyChanges;
103
+ nextAttrs = appendParagraphPropertyChange(nextAttrs, existing, getPreviousListFormatting(node.attrs), rev);
104
+ }
87
105
  tr = tr.setNodeMarkup(pos, void 0, nextAttrs);
88
106
  }
89
107
  });
@@ -94,55 +112,45 @@ function toggleList(numId) {
94
112
  }
95
113
  const toggleBulletList = (state, dispatch) => toggleList(1)(state, dispatch);
96
114
  const toggleNumberedList = (state, dispatch) => toggleList(2)(state, dispatch);
115
+ const attrsForListLevel = (state, attrs, level) => {
116
+ if (!hasActiveListNumbering(attrs)) panic("Cannot change the level of a list without a numbering id");
117
+ return {
118
+ ...attrs,
119
+ ...listLevelAttrPatch(attrs, {
120
+ numId: attrs.numPr.numId,
121
+ ilvl: level
122
+ }, getDocumentNumbering(state))
123
+ };
124
+ };
97
125
  const increaseListLevel = (state, dispatch) => {
98
126
  const { $from } = state.selection;
99
127
  const paragraph = $from.parent;
100
128
  if (paragraph.type.name !== "paragraph") return false;
101
- if (!paragraph.attrs["numPr"]) return false;
102
- const currentLevel = paragraph.attrs["numPr"].ilvl || 0;
129
+ const attrs = expectParagraphAttrs(paragraph);
130
+ if (!hasActiveListNumbering(attrs)) return false;
131
+ const currentLevel = attrs.numPr.ilvl || 0;
103
132
  if (currentLevel >= 8) return false;
104
133
  if (!dispatch) return true;
105
134
  const paragraphPos = $from.before($from.depth);
106
- dispatch(state.tr.setNodeMarkup(paragraphPos, void 0, {
107
- ...paragraph.attrs,
108
- numPr: {
109
- ...paragraph.attrs["numPr"],
110
- ilvl: currentLevel + 1
111
- },
112
- indentLeft: null,
113
- indentFirstLine: null,
114
- hangingIndent: null
115
- }).scrollIntoView());
135
+ dispatch(state.tr.setNodeMarkup(paragraphPos, void 0, { ...attrsForListLevel(state, attrs, currentLevel + 1) }).scrollIntoView());
116
136
  return true;
117
137
  };
118
138
  const decreaseListLevel = (state, dispatch) => {
119
139
  const { $from } = state.selection;
120
140
  const paragraph = $from.parent;
121
141
  if (paragraph.type.name !== "paragraph") return false;
122
- if (!paragraph.attrs["numPr"]) return false;
123
- const currentLevel = paragraph.attrs["numPr"].ilvl || 0;
142
+ const attrs = expectParagraphAttrs(paragraph);
143
+ if (!hasActiveListNumbering(attrs)) return false;
144
+ const currentLevel = attrs.numPr.ilvl || 0;
124
145
  if (!dispatch) return true;
125
146
  const paragraphPos = $from.before($from.depth);
126
147
  if (currentLevel <= 0) dispatch(state.tr.setNodeMarkup(paragraphPos, void 0, {
127
- ...paragraph.attrs,
128
- numPr: null,
129
- listIsBullet: null,
130
- listNumFmt: null,
131
- listMarker: null,
132
- indentLeft: null,
133
- indentFirstLine: null,
134
- hangingIndent: null
135
- }).scrollIntoView());
136
- else dispatch(state.tr.setNodeMarkup(paragraphPos, void 0, {
137
- ...paragraph.attrs,
138
- numPr: {
139
- ...paragraph.attrs["numPr"],
140
- ilvl: currentLevel - 1
141
- },
148
+ ...clearListAttrs(attrs),
142
149
  indentLeft: null,
143
150
  indentFirstLine: null,
144
151
  hangingIndent: null
145
152
  }).scrollIntoView());
153
+ else dispatch(state.tr.setNodeMarkup(paragraphPos, void 0, { ...attrsForListLevel(state, attrs, currentLevel - 1) }).scrollIntoView());
146
154
  return true;
147
155
  };
148
156
  const removeList = (state, dispatch) => {
@@ -151,15 +159,9 @@ const removeList = (state, dispatch) => {
151
159
  let tr = state.tr;
152
160
  const seen = /* @__PURE__ */ new Set();
153
161
  state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
154
- if (node.type.name === "paragraph" && node.attrs["numPr"] && !seen.has(pos)) {
162
+ if (node.type.name === "paragraph" && hasActiveListNumbering(expectParagraphAttrs(node)) && !seen.has(pos)) {
155
163
  seen.add(pos);
156
- tr = tr.setNodeMarkup(pos, void 0, {
157
- ...node.attrs,
158
- numPr: null,
159
- listIsBullet: null,
160
- listNumFmt: null,
161
- listMarker: null
162
- });
164
+ tr = tr.setNodeMarkup(pos, void 0, clearListAttrs(expectParagraphAttrs(node)));
163
165
  }
164
166
  });
165
167
  dispatch(tr.scrollIntoView());
@@ -169,16 +171,17 @@ function isInList(state) {
169
171
  const { $from } = state.selection;
170
172
  const paragraph = $from.parent;
171
173
  if (paragraph.type.name !== "paragraph") return false;
172
- return !!paragraph.attrs["numPr"]?.numId;
174
+ return hasActiveListNumbering(expectParagraphAttrs(paragraph));
173
175
  }
174
176
  function getListInfo(state) {
175
177
  const { $from } = state.selection;
176
178
  const paragraph = $from.parent;
177
179
  if (paragraph.type.name !== "paragraph") return null;
178
- if (!paragraph.attrs["numPr"]?.numId) return null;
180
+ const attrs = expectParagraphAttrs(paragraph);
181
+ if (!hasActiveListNumbering(attrs)) return null;
179
182
  return {
180
- numId: paragraph.attrs["numPr"].numId,
181
- ilvl: paragraph.attrs["numPr"].ilvl || 0
183
+ numId: attrs.numPr.numId,
184
+ ilvl: attrs.numPr.ilvl || 0
182
185
  };
183
186
  }
184
187
  function exitListOnEmptyEnter() {
@@ -187,15 +190,10 @@ function exitListOnEmptyEnter() {
187
190
  if (!empty) return false;
188
191
  const paragraph = $from.parent;
189
192
  if (paragraph.type.name !== "paragraph") return false;
190
- if (!paragraph.attrs["numPr"]) return false;
193
+ const attrs = expectParagraphAttrs(paragraph);
194
+ if (!hasActiveListNumbering(attrs)) return false;
191
195
  if (paragraph.textContent.length > 0) return false;
192
- if (dispatch) dispatch(state.tr.setNodeMarkup($from.before(), void 0, {
193
- ...paragraph.attrs,
194
- numPr: null,
195
- listIsBullet: null,
196
- listNumFmt: null,
197
- listMarker: null
198
- }));
196
+ if (dispatch) dispatch(state.tr.setNodeMarkup($from.before(), void 0, clearListAttrs(attrs)));
199
197
  return true;
200
198
  };
201
199
  }
@@ -205,7 +203,7 @@ function splitListItem() {
205
203
  if (!empty) return false;
206
204
  const paragraph = $from.parent;
207
205
  if (paragraph.type.name !== "paragraph") return false;
208
- if (!paragraph.attrs["numPr"]) return false;
206
+ if (!hasActiveListNumbering(expectParagraphAttrs(paragraph))) return false;
209
207
  if (dispatch) {
210
208
  const { tr } = state;
211
209
  const pos = $from.pos;
@@ -225,14 +223,9 @@ function backspaceExitList() {
225
223
  if ($from.parentOffset !== 0) return false;
226
224
  const paragraph = $from.parent;
227
225
  if (paragraph.type.name !== "paragraph") return false;
228
- if (!paragraph.attrs["numPr"]) return false;
229
- if (dispatch) dispatch(state.tr.setNodeMarkup($from.before(), void 0, {
230
- ...paragraph.attrs,
231
- numPr: null,
232
- listIsBullet: null,
233
- listNumFmt: null,
234
- listMarker: null
235
- }));
226
+ const attrs = expectParagraphAttrs(paragraph);
227
+ if (!hasActiveListNumbering(attrs)) return false;
228
+ if (dispatch) dispatch(state.tr.setNodeMarkup($from.before(), void 0, clearListAttrs(attrs)));
236
229
  return true;
237
230
  };
238
231
  }
@@ -241,29 +234,19 @@ function increaseListIndent() {
241
234
  const { $from, $to } = state.selection;
242
235
  const positions = [];
243
236
  state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
244
- if (node.type.name === "paragraph" && node.attrs["numPr"]) {
245
- if ((node.attrs["numPr"].ilvl ?? 0) < 8) positions.push({
237
+ if (node.type.name === "paragraph") {
238
+ const attrs = expectParagraphAttrs(node);
239
+ if (!hasActiveListNumbering(attrs)) return;
240
+ if ((attrs.numPr?.ilvl ?? 0) < 8) positions.push({
246
241
  pos,
247
- attrs: node.attrs
242
+ attrs
248
243
  });
249
244
  }
250
245
  });
251
246
  if (positions.length === 0) return false;
252
247
  if (dispatch) {
253
248
  let tr = state.tr;
254
- for (const { pos, attrs } of positions) {
255
- const numPr = attrs["numPr"];
256
- tr = tr.setNodeMarkup(pos, void 0, {
257
- ...attrs,
258
- numPr: {
259
- ...numPr,
260
- ilvl: (numPr.ilvl ?? 0) + 1
261
- },
262
- indentLeft: null,
263
- indentFirstLine: null,
264
- hangingIndent: null
265
- });
266
- }
249
+ for (const { pos, attrs } of positions) tr = tr.setNodeMarkup(pos, void 0, attrsForListLevel(state, attrs, (attrs.numPr?.ilvl ?? 0) + 1));
267
250
  dispatch(tr);
268
251
  }
269
252
  return true;
@@ -274,37 +257,26 @@ function decreaseListIndent() {
274
257
  const { $from, $to } = state.selection;
275
258
  const positions = [];
276
259
  state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
277
- if (node.type.name === "paragraph" && node.attrs["numPr"]) positions.push({
278
- pos,
279
- attrs: node.attrs
280
- });
260
+ if (node.type.name === "paragraph") {
261
+ const attrs = expectParagraphAttrs(node);
262
+ if (hasActiveListNumbering(attrs)) positions.push({
263
+ pos,
264
+ attrs
265
+ });
266
+ }
281
267
  });
282
268
  if (positions.length === 0) return false;
283
269
  if (dispatch) {
284
270
  let tr = state.tr;
285
271
  for (const { pos, attrs } of positions) {
286
- const numPr = attrs["numPr"];
287
- const currentLevel = numPr.ilvl ?? 0;
272
+ const currentLevel = attrs.numPr?.ilvl ?? 0;
288
273
  if (currentLevel <= 0) tr = tr.setNodeMarkup(pos, void 0, {
289
- ...attrs,
290
- numPr: null,
291
- listIsBullet: null,
292
- listNumFmt: null,
293
- listMarker: null,
294
- indentLeft: null,
295
- indentFirstLine: null,
296
- hangingIndent: null
297
- });
298
- else tr = tr.setNodeMarkup(pos, void 0, {
299
- ...attrs,
300
- numPr: {
301
- ...numPr,
302
- ilvl: currentLevel - 1
303
- },
274
+ ...clearListAttrs(attrs),
304
275
  indentLeft: null,
305
276
  indentFirstLine: null,
306
277
  hangingIndent: null
307
278
  });
279
+ else tr = tr.setNodeMarkup(pos, void 0, { ...attrsForListLevel(state, attrs, currentLevel - 1) });
308
280
  }
309
281
  dispatch(tr);
310
282
  }
@@ -2,8 +2,8 @@ import { recreateProseNodeWithParagraphPropertySource, setProseParagraphMarkupWi
2
2
  import { deterministicHexId, generateHexId } from "../../../utils/hexId.js";
3
3
  import { createExtension } from "../create.js";
4
4
  import { ignoreTrackedChanges } from "./ParagraphChangeTrackerExtension.js";
5
- import { Plugin, PluginKey } from "prosemirror-state";
6
5
  import { Fragment } from "prosemirror-model";
6
+ import { Plugin, PluginKey } from "prosemirror-state";
7
7
  //#region src/prosemirror/extensions/features/ParaIdAllocatorExtension.ts
8
8
  /**
9
9
  * ParaIdAllocator — assigns a stable `w14:paraId` to every paragraph.
@@ -1,5 +1,7 @@
1
1
  import { Extension } from "../types.js";
2
+ import { RemovedSectionReference, TrackedSectionEndpointRemoval } from "../../../internal/sectionEndpointResolution.js";
2
3
  import { EditorState, PluginKey, Transaction } from "prosemirror-state";
4
+ import { Node } from "prosemirror-model";
3
5
  //#region src/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts
4
6
  declare const paragraphChangeTrackerKey: PluginKey<ParagraphChangeTrackerState>;
5
7
  type ParagraphChangeTrackerState = {
@@ -11,6 +13,12 @@ type ParagraphChangeTrackerState = {
11
13
  hasUntrackedChanges: boolean;
12
14
  /** Cached paragraph count to avoid full doc traversal on every transaction */
13
15
  paragraphCount: number;
16
+ /** Cached section-endpoint count used to invalidate stale save authorization. */
17
+ sectionEndpointCount: number;
18
+ /** Exact endpoint owners and properties used to invalidate count-neutral changes. */
19
+ sectionEndpointFingerprint: string;
20
+ /** Exact tracked-resolution transition that may reduce the saved section count. */
21
+ sectionEndpointRemoval: TrackedSectionEndpointRemoval | null;
14
22
  };
15
23
  /**
16
24
  * Get the change tracker state from an EditorState
@@ -28,12 +36,30 @@ declare function hasStructuralChanges(state: EditorState): boolean;
28
36
  * Check if any changes affected paragraphs without paraId
29
37
  */
30
38
  declare function hasUntrackedChanges(state: EditorState): boolean;
39
+ /** Exact section-count transition authorized by tracked paragraph-mark resolution. */
40
+ declare function getTrackedSectionEndpointRemoval(state: EditorState): TrackedSectionEndpointRemoval | null;
41
+ type MarkTrackedSectionEndpointRemovalOptions = {
42
+ sourceDoc: Node;
43
+ removedEndpointCount: number;
44
+ removedReferences: readonly RemovedSectionReference[];
45
+ };
46
+ /** Record only a complete, internally consistent endpoint-removal transition. */
47
+ declare function markTrackedSectionEndpointRemoval(tr: Transaction, { sourceDoc, removedEndpointCount, removedReferences }: MarkTrackedSectionEndpointRemovalOptions): Transaction;
31
48
  /**
32
49
  * Create a transaction that clears the change tracker
33
50
  */
34
51
  declare function clearTrackedChanges(state: EditorState): Transaction;
35
52
  declare function ignoreTrackedChanges(tr: Transaction): Transaction;
36
53
  declare function markStructuralChange(tr: Transaction): Transaction;
54
+ type MarkChangedParagraphRangesOptions = {
55
+ ranges: readonly {
56
+ from: number;
57
+ to: number;
58
+ }[];
59
+ mappingFrom: number;
60
+ };
61
+ /** Record mark-only paragraph changes whose replacement step has a granular map. */
62
+ declare function markChangedParagraphRanges(tr: Transaction, batch: MarkChangedParagraphRangesOptions): Transaction;
37
63
  declare const ParagraphChangeTrackerExtension: (options?: Partial<{}> | undefined) => Extension;
38
64
  //#endregion
39
- export { ParagraphChangeTrackerExtension, ParagraphChangeTrackerState, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, markStructuralChange, paragraphChangeTrackerKey };
65
+ export { ParagraphChangeTrackerExtension, ParagraphChangeTrackerState, clearTrackedChanges, getChangeTrackerState, getChangedParagraphIds, getTrackedSectionEndpointRemoval, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges, markChangedParagraphRanges, markStructuralChange, markTrackedSectionEndpointRemoval, paragraphChangeTrackerKey };