@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
@@ -1,5 +1,6 @@
1
1
  import { createTableCellFlowState, placeTableCellBlock } from "../../layout-engine/measure/tableCellFlow.js";
2
2
  import { buildTableCellGrid, buildTableCellPlacements, getSourceCellAt } from "../../layout-engine/measure/tableCellGrid.js";
3
+ import { tableFragmentBottomBorders } from "../../layout-engine/measure/tableFragmentBorderGeometry.js";
3
4
  import { resolveTableCellPadding } from "../../layout-engine/types.js";
4
5
  import { HIT_REGION_KINDS } from "../primitives.js";
5
6
  import { parseDisplayColor } from "./colors.js";
@@ -332,6 +333,28 @@ const paintTableBody = ({ composer, block, measure, xPx, yPx, fromRow, toRow, he
332
333
  cursorYPx += rowMeasure.height;
333
334
  }
334
335
  };
336
+ const paintFragmentBottomBorders = ({ composer, fragment, block, measure, context }) => {
337
+ if (fragment.continuesOnNext !== true || fragment.bottomClip === void 0) return;
338
+ const primitives = [];
339
+ for (const { left, width, border } of tableFragmentBottomBorders({
340
+ fragment,
341
+ block,
342
+ measure
343
+ })) {
344
+ const stroke = strokeFor(border, context, "table fragment bottom border");
345
+ if (!stroke) continue;
346
+ const yPx = fragment.y + fragment.height - stroke.thicknessPx / 2;
347
+ primitives.push({
348
+ kind: "line",
349
+ x1Px: fragment.x + left,
350
+ y1Px: yPx,
351
+ x2Px: fragment.x + left + width,
352
+ y2Px: yPx,
353
+ stroke
354
+ });
355
+ }
356
+ composer.push(primitives);
357
+ };
335
358
  /** A whole, unpaginated table: what a nested table inside a cell or box is. */
336
359
  const paintTableBlock = ({ composer, block, measure, xPx, yPx, context, paintTextBox }) => paintTableBody({
337
360
  composer,
@@ -404,6 +427,13 @@ const paintTableFragment = ({ composer, fragment, block, measure, context, paint
404
427
  children: [...clipped.primitives()],
405
428
  regions: [...clipped.regions()]
406
429
  }]);
430
+ paintFragmentBottomBorders({
431
+ composer,
432
+ fragment,
433
+ block,
434
+ measure,
435
+ context
436
+ });
407
437
  };
408
438
  //#endregion
409
439
  export { paintTableBlock, paintTableFragment };
@@ -15,7 +15,7 @@ import { hasComplexScript, isComplexScriptCodePoint } from "../utils/scriptSegme
15
15
  * advances rightward from its origin whatever the paragraph does.
16
16
  */
17
17
  declare const glyphCellOffsetsPx: (run: DisplayGlyphRun) => readonly number[];
18
- declare const DISPLAY_PRIMITIVE_KINDS: ("glyphRun" | "rect" | "line" | "image" | "clipGroup" | "rotateGroup" | "opacityGroup")[];
18
+ declare const DISPLAY_PRIMITIVE_KINDS: ("image" | "line" | "rect" | "glyphRun" | "clipGroup" | "rotateGroup" | "opacityGroup")[];
19
19
  /**
20
20
  * Dash geometry of each stroke pattern, as multiples of the stroke thickness.
21
21
  *
@@ -5,6 +5,8 @@ import { FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions } from "./ai-
5
5
  declare const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION: 1;
6
6
  /** Direct paragraph-alignment values accepted by the operation contract. */
7
7
  declare const FOLIO_PARAGRAPH_ALIGNMENT_VALUES: readonly ("left" | "center" | "right" | "both" | "distribute" | "mediumKashida" | "highKashida" | "lowKashida" | "thaiDistribute")[];
8
+ /** `w:spacing/@w:lineRule` values accepted by the operation contract. */
9
+ declare const FOLIO_LINE_SPACING_RULE_VALUES: readonly ("auto" | "exact" | "atLeast")[];
8
10
  declare const FOLIO_DOCUMENT_OPERATION_TYPES: readonly ["replaceInBlock", "replaceRange", "commentOnRange", "formatRange", "insertAfterBlock", "insertBeforeBlock", "replaceBlock", "deleteBlock", "splitBlock", "mergeBlockWithNext", "setBlockParagraphProperties", "insertTable", "deleteTable", "commentOnBlock", "insertSignatureTable", "insertTableRow", "deleteTableRow", "insertTableColumn", "deleteTableColumn", "mergeTableCells", "splitTableCell"];
9
11
  declare const FOLIO_DOCUMENT_OPERATION_MODES: readonly ["direct", "tracked-changes", "suggested"];
10
12
  declare const FOLIO_DOCUMENT_OPERATION_STORIES: readonly ["main", "header", "footer", "footnote", "endnote"];
@@ -95,8 +97,8 @@ declare const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE: Readonly<{
95
97
  readonly replaceRange: readonly ["id", "type", "range", "severity", "area", "precondition", "suggestionId", "replace", "comment"];
96
98
  readonly commentOnRange: readonly ["id", "type", "range", "severity", "area", "precondition", "comment"];
97
99
  readonly formatRange: readonly ["id", "type", "range", "severity", "area", "precondition", "suggestionId", "formatting"];
98
- readonly insertAfterBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
99
- readonly insertBeforeBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
100
+ readonly insertAfterBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
101
+ readonly insertBeforeBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "inheritFormatting", "alignment", "spacing", "listLevel", "moveId", "pageBreakBefore", "styleId", "comment"];
100
102
  readonly replaceBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "text", "preserveFormatting", "styleId", "comment"];
101
103
  readonly deleteBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "moveId", "comment"];
102
104
  readonly splitBlock: readonly ["id", "type", "blockId", "severity", "area", "precondition", "suggestionId", "offset", "separator"];
@@ -119,7 +121,7 @@ declare const parseFolioDocumentOperationBatch: (value: unknown) => FolioDocumen
119
121
  * review queue instead of applying it; the core apply path never produces it.
120
122
  */
121
123
  type FolioDocumentOperationStatus = "committed" | "previewed" | "rejected" | "queued";
122
- type FolioDocumentOperationRecovery = "refreshDocument" | "narrowMatch" | "changeMode" | "changeTarget" | "removeOperation" | "inspectBatch" | "retryLater";
124
+ type FolioDocumentOperationRecovery = "refreshDocument" | "narrowMatch" | "changeMode" | "changeTarget" | "removeOperation" | "inspectBatch" | "resolveTrackedChange" | "retryLater";
123
125
  type FolioDocumentOperationIssue = {
124
126
  operationId: string;
125
127
  operationIndex: number;
@@ -280,4 +282,4 @@ type ApplyFolioDocumentOperationsOptions = {
280
282
  };
281
283
  declare const applyFolioDocumentOperations: ({ view, snapshot, batch, story, author, createCommentId, createUndoHandle, revisionStamp, wordDiff, tableTemplates }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
282
284
  //#endregion
283
- export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
285
+ export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationBatchPrecondition, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationQueuedOperation, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -1,10 +1,12 @@
1
1
  import { applyFolioAIEditOperations, previewFolioAIEditOperations } from "./ai-edits/apply.js";
2
- import { PARAGRAPH_ALIGNMENT_VALUES } from "./types/documentEnumValues.js";
2
+ import { LINE_SPACING_RULE_VALUES, PARAGRAPH_ALIGNMENT_VALUES } from "./types/documentEnumValues.js";
3
3
  import { TaggedError } from "better-result";
4
4
  //#region src/document-operations.ts
5
5
  const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION = 1;
6
6
  /** Direct paragraph-alignment values accepted by the operation contract. */
7
7
  const FOLIO_PARAGRAPH_ALIGNMENT_VALUES = Object.freeze([...PARAGRAPH_ALIGNMENT_VALUES]);
8
+ /** `w:spacing/@w:lineRule` values accepted by the operation contract. */
9
+ const FOLIO_LINE_SPACING_RULE_VALUES = Object.freeze([...LINE_SPACING_RULE_VALUES]);
8
10
  const FOLIO_DOCUMENT_OPERATION_TYPES = Object.freeze([
9
11
  "replaceInBlock",
10
12
  "replaceRange",
@@ -146,6 +148,12 @@ const readOptionalBoolean = (value, key, path) => {
146
148
  if (typeof candidate === "boolean") return candidate;
147
149
  return invalidBatch(`${path}.${key}`, "expected a boolean when provided");
148
150
  };
151
+ const readClearableBoolean = (value, key, path) => {
152
+ const candidate = value[key];
153
+ if (candidate === void 0 || candidate === null) return candidate;
154
+ if (typeof candidate === "boolean") return candidate;
155
+ return invalidBatch(`${path}.${key}`, "expected a boolean or null when provided");
156
+ };
149
157
  const readClearableNonEmptyString = (value, key, path) => {
150
158
  const candidate = value[key];
151
159
  if (candidate === void 0 || candidate === null) return candidate;
@@ -175,8 +183,8 @@ const readOptionalStringArray = (value, key, path) => {
175
183
  };
176
184
  const readNonNegativeInteger = (value, key, path) => {
177
185
  const candidate = value[key];
178
- if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 0) return candidate;
179
- return invalidBatch(`${path}.${key}`, "expected a non-negative integer");
186
+ if (typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0) return candidate;
187
+ return invalidBatch(`${path}.${key}`, "expected a non-negative safe integer");
180
188
  };
181
189
  /**
182
190
  * A non-negative integer that may also be cleared. `undefined` is "say
@@ -194,6 +202,46 @@ const readClearableParagraphAlignment = ({ value, key, path }) => {
194
202
  for (const alignment of FOLIO_PARAGRAPH_ALIGNMENT_VALUES) if (candidate === alignment) return alignment;
195
203
  return invalidBatch(`${path}.${key}`, `expected one of ${FOLIO_PARAGRAPH_ALIGNMENT_VALUES.join(", ")} or null when provided`);
196
204
  };
205
+ const PARAGRAPH_SPACING_KEYS = [
206
+ "spaceBefore",
207
+ "spaceAfter",
208
+ "lineSpacing",
209
+ "lineSpacingRule",
210
+ "beforeAutospacing",
211
+ "afterAutospacing"
212
+ ];
213
+ const isLineSpacingRule = (value) => FOLIO_LINE_SPACING_RULE_VALUES.some((rule) => value === rule);
214
+ /**
215
+ * Read one complete direct `w:spacing` cluster. Individual fields are
216
+ * optional so explicit zero and false survive; null removes the child.
217
+ */
218
+ const readClearableParagraphSpacing = ({ value, key, path }) => {
219
+ const candidate = value[key];
220
+ if (candidate === void 0 || candidate === null) return candidate;
221
+ const spacingPath = `${path}.${key}`;
222
+ if (!isPlainObject(candidate)) return invalidBatch(spacingPath, "expected an object or null when provided");
223
+ assertAllowedKeys(candidate, spacingPath, PARAGRAPH_SPACING_KEYS);
224
+ if (Object.keys(candidate).length === 0) return invalidBatch(spacingPath, "expected at least one spacing property");
225
+ const spacing = {};
226
+ for (const unsignedKey of ["spaceBefore", "spaceAfter"]) if (candidate[unsignedKey] !== void 0) spacing[unsignedKey] = readNonNegativeInteger(candidate, unsignedKey, spacingPath);
227
+ const lineSpacing = candidate["lineSpacing"];
228
+ if (lineSpacing !== void 0) {
229
+ if (typeof lineSpacing !== "number" || !Number.isSafeInteger(lineSpacing)) return invalidBatch(`${spacingPath}.lineSpacing`, "expected a safe signed integer when provided");
230
+ spacing.lineSpacing = lineSpacing;
231
+ }
232
+ const lineSpacingRule = candidate["lineSpacingRule"];
233
+ if (lineSpacingRule !== void 0) {
234
+ if (!isLineSpacingRule(lineSpacingRule)) return invalidBatch(`${spacingPath}.lineSpacingRule`, `expected one of ${FOLIO_LINE_SPACING_RULE_VALUES.join(", ")} when provided`);
235
+ spacing.lineSpacingRule = lineSpacingRule;
236
+ }
237
+ for (const booleanKey of ["beforeAutospacing", "afterAutospacing"]) {
238
+ const booleanValue = candidate[booleanKey];
239
+ if (booleanValue === void 0) continue;
240
+ if (typeof booleanValue !== "boolean") return invalidBatch(`${spacingPath}.${booleanKey}`, "expected a boolean when provided");
241
+ spacing[booleanKey] = booleanValue;
242
+ }
243
+ return spacing;
244
+ };
197
245
  /**
198
246
  * A rectangular grid of cell texts. Rectangular because a table whose rows
199
247
  * hold different cell counts is not a table any consumer can lay out, and the
@@ -221,7 +269,8 @@ const readParagraphProperties = (value, path) => {
221
269
  assertAllowedKeys(candidate, propertiesPath, [
222
270
  "styleId",
223
271
  "listLevel",
224
- "alignment"
272
+ "alignment",
273
+ "spacing"
225
274
  ]);
226
275
  const styleId = candidate["styleId"] === null ? null : readOptionalString(candidate, "styleId", propertiesPath);
227
276
  const listLevel = readClearableNonNegativeInteger(candidate, "listLevel", propertiesPath);
@@ -230,11 +279,17 @@ const readParagraphProperties = (value, path) => {
230
279
  key: "alignment",
231
280
  path: propertiesPath
232
281
  });
233
- if (styleId === void 0 && listLevel === void 0 && alignment === void 0) return invalidBatch(propertiesPath, "expected at least one property to set");
282
+ const spacing = readClearableParagraphSpacing({
283
+ value: candidate,
284
+ key: "spacing",
285
+ path: propertiesPath
286
+ });
287
+ if (styleId === void 0 && listLevel === void 0 && alignment === void 0 && spacing === void 0) return invalidBatch(propertiesPath, "expected at least one property to set");
234
288
  return {
235
289
  ...styleId !== void 0 && { styleId },
236
290
  ...listLevel !== void 0 && { listLevel },
237
- ...alignment !== void 0 && { alignment }
291
+ ...alignment !== void 0 && { alignment },
292
+ ...spacing !== void 0 && { spacing }
238
293
  };
239
294
  };
240
295
  const readTextRange = (value, path) => {
@@ -280,10 +335,10 @@ const readInlineFormatting = (value, path) => {
280
335
  "fontSizePt",
281
336
  "color"
282
337
  ]);
283
- const bold = readOptionalBoolean(candidate, "bold", formattingPath);
284
- const italic = readOptionalBoolean(candidate, "italic", formattingPath);
285
- const underline = readOptionalBoolean(candidate, "underline", formattingPath);
286
- const strike = readOptionalBoolean(candidate, "strike", formattingPath);
338
+ const bold = readClearableBoolean(candidate, "bold", formattingPath);
339
+ const italic = readClearableBoolean(candidate, "italic", formattingPath);
340
+ const underline = readClearableBoolean(candidate, "underline", formattingPath);
341
+ const strike = readClearableBoolean(candidate, "strike", formattingPath);
287
342
  const fontFamily = readClearableNonEmptyString(candidate, "fontFamily", formattingPath);
288
343
  const fontSizePt = readClearableFontSize(candidate, "fontSizePt", formattingPath);
289
344
  const color = readClearableRgbColor(candidate, "color", formattingPath);
@@ -374,6 +429,7 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
374
429
  "text",
375
430
  "inheritFormatting",
376
431
  "alignment",
432
+ "spacing",
377
433
  "listLevel",
378
434
  "moveId",
379
435
  "pageBreakBefore",
@@ -385,6 +441,7 @@ const FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE = Object.freeze({
385
441
  "text",
386
442
  "inheritFormatting",
387
443
  "alignment",
444
+ "spacing",
388
445
  "listLevel",
389
446
  "moveId",
390
447
  "pageBreakBefore",
@@ -571,6 +628,11 @@ const parseDocumentOperation = (value, index) => {
571
628
  key: "alignment",
572
629
  path
573
630
  });
631
+ const spacing = readClearableParagraphSpacing({
632
+ value,
633
+ key: "spacing",
634
+ path
635
+ });
574
636
  return {
575
637
  ...operationMeta,
576
638
  id,
@@ -579,6 +641,7 @@ const parseDocumentOperation = (value, index) => {
579
641
  text: readString(value, "text", path),
580
642
  ...inheritFormatting !== void 0 && { inheritFormatting },
581
643
  ...alignment !== void 0 && { alignment },
644
+ ...spacing !== void 0 && { spacing },
582
645
  ...listLevel !== void 0 && { listLevel },
583
646
  ...moveId !== void 0 && { moveId },
584
647
  ...pageBreakBefore !== void 0 && { pageBreakBefore },
@@ -762,6 +825,8 @@ const recoveryByReason = {
762
825
  preconditionFailed: "refreshDocument",
763
826
  staleRange: "refreshDocument",
764
827
  emptyOperation: "removeOperation",
828
+ pendingParagraphPropertyChange: "resolveTrackedChange",
829
+ pendingRunPropertyChange: "resolveTrackedChange",
765
830
  noopOperation: "removeOperation",
766
831
  documentVersionMismatch: "refreshDocument",
767
832
  documentNotEditable: "retryLater"
@@ -778,7 +843,7 @@ const getFolioDocumentOperationIssues = (operations, skipped) => {
778
843
  operationIndex,
779
844
  path: `$.operations[${operationIndex}]`,
780
845
  code: reason,
781
- retryable: reason !== "emptyOperation" && reason !== "noopOperation",
846
+ retryable: reason !== "emptyOperation" && reason !== "noopOperation" && reason !== "pendingParagraphPropertyChange" && reason !== "pendingRunPropertyChange",
782
847
  recovery: recoveryByReason[reason]
783
848
  };
784
849
  });
@@ -1008,4 +1073,4 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", a
1008
1073
  };
1009
1074
  };
1010
1075
  //#endregion
1011
- export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
1076
+ export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_BATCH_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_KEYS_BY_TYPE, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -9,7 +9,7 @@ import { parseTable } from "./tableParser.js";
9
9
  import { captureVerbatimXml } from "./verbatimCapture.js";
10
10
  import { findChild, getChildElements, getLocalName, mergeXmlnsDeclarations } from "./xmlParser.js";
11
11
  //#region src/docx/blockContentParser.ts
12
- const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList, siblingNumIdsByAbstractNumId }) => {
12
+ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
13
13
  const listRendering = paragraph.listRendering;
14
14
  if (!listRendering || !numbering) {
15
15
  previousList.abstractNumId = null;
@@ -26,26 +26,17 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
26
26
  }
27
27
  const firstEncounter = !listCounters.has(numId);
28
28
  if (firstEncounter) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
29
- const counters = listCounters.get(numId);
29
+ let counters = listCounters.get(numId);
30
30
  if (!counters) return;
31
31
  const abstractNumId = numbering.getAbstractNumId(numId);
32
- if (firstEncounter && abstractNumId !== null) {
33
- let siblingNumIds = siblingNumIdsByAbstractNumId.get(abstractNumId);
34
- if (!siblingNumIds) {
35
- siblingNumIds = /* @__PURE__ */ new Set();
36
- siblingNumIdsByAbstractNumId.set(abstractNumId, siblingNumIds);
37
- }
38
- siblingNumIds.add(numId);
39
- }
40
32
  const styleNumbering = paragraph.formatting?.numPrFromStyle;
41
- let resumedAbstractCounters;
42
33
  const resumesRestartedInstance = firstEncounter && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.numId !== null && previousList.numId !== numId && restartedNumIds.has(previousList.numId);
43
34
  const resumesStyleInstance = firstEncounter && !styleNumbering && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.fromStyle === true;
44
35
  if (abstractNumId !== null && (styleNumbering || resumesRestartedInstance || resumesStyleInstance)) {
45
36
  const latestAbstractCounters = abstractCounters.get(abstractNumId);
46
37
  if (latestAbstractCounters) {
47
- for (let i = 0; i < counters.length; i += 1) counters[i] = latestAbstractCounters[i] ?? NaN;
48
- resumedAbstractCounters = latestAbstractCounters;
38
+ counters = latestAbstractCounters;
39
+ listCounters.set(numId, counters);
49
40
  }
50
41
  }
51
42
  if (listRendering.startOverride !== void 0 || resumesRestartedInstance || previousList.numId === numId && restartedNumIds.has(numId)) restartedNumIds.add(numId);
@@ -64,17 +55,7 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
64
55
  const childCounter = counters[level + 1];
65
56
  counters[level + 1] = (childCounter === void 0 || Number.isNaN(childCounter) ? 0 : childCounter) + childAdvances;
66
57
  }
67
- if (abstractNumId !== null) {
68
- if (resumedAbstractCounters) {
69
- const siblingNumIds = siblingNumIdsByAbstractNumId.get(abstractNumId);
70
- for (const otherNumId of siblingNumIds ?? []) {
71
- const otherCounters = listCounters.get(otherNumId);
72
- if (otherNumId === numId || !otherCounters || !otherCounters.every((value, index) => Object.is(value, resumedAbstractCounters[index]))) continue;
73
- for (let i = 0; i < otherCounters.length; i += 1) otherCounters[i] = counters[i] ?? NaN;
74
- }
75
- }
76
- abstractCounters.set(abstractNumId, [...counters]);
77
- }
58
+ if (abstractNumId !== null) abstractCounters.set(abstractNumId, counters);
78
59
  previousList.abstractNumId = abstractNumId;
79
60
  previousList.fromStyle = Boolean(styleNumbering);
80
61
  previousList.numId = numId;
@@ -108,7 +89,6 @@ const parseBlockContent = (parent, styles, theme, numbering, rels, media, option
108
89
  fromStyle: false,
109
90
  numId: null
110
91
  },
111
- siblingNumIdsByAbstractNumId: /* @__PURE__ */ new Map(),
112
92
  options: {
113
93
  ...options,
114
94
  rootXmlns: mergeXmlnsDeclarations(options?.rootXmlns ?? {}, parent)
@@ -129,8 +109,7 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
129
109
  listCounters: state.listCounters,
130
110
  abstractCounters: state.abstractCounters,
131
111
  restartedNumIds: state.restartedNumIds,
132
- previousList: state.previousList,
133
- siblingNumIdsByAbstractNumId: state.siblingNumIdsByAbstractNumId
112
+ previousList: state.previousList
134
113
  });
135
114
  content.push(paragraph);
136
115
  continue;
@@ -392,10 +392,15 @@ const ensureParaIdsInternal = async (docx, options) => {
392
392
  };
393
393
  const zip = await JSZip.loadAsync(docx);
394
394
  if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw createEnsureParaIdsError("Refusing to normalize a digitally signed package because rewriting OOXML invalidates its signatures. Warn the user and pass allowSignedPackageMutation only if invalidation is acceptable.");
395
- for (const [partPath, content] of updates) zip.file(partPath, content, {
396
- compression: "DEFLATE",
397
- compressionOptions: { level: 6 }
398
- });
395
+ for (const [partPath, content] of updates) {
396
+ const sourceEntry = zip.file(partPath);
397
+ if (sourceEntry === null) throw createEnsureParaIdsError(`Package part disappeared during normalization: ${partPath}`);
398
+ zip.file(partPath, content, {
399
+ compression: "DEFLATE",
400
+ compressionOptions: { level: 6 },
401
+ date: sourceEntry.date
402
+ });
403
+ }
399
404
  return {
400
405
  docx: await zip.generateAsync({
401
406
  type: "uint8array",
@@ -80,5 +80,7 @@ declare function getBulletCharacter(level: document_d_exports.ListLevel): string
80
80
  * Check if a list level is a bullet (not numbered)
81
81
  */
82
82
  declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
83
+ /** Whether a numbering level reserves horizontal space for a visible marker. */
84
+ declare const numberingLevelHasMarkerSlot: (level: Pick<document_d_exports.ListLevel, "numFmt">) => boolean;
83
85
  //#endregion
84
- export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
86
+ export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
@@ -542,6 +542,7 @@ function computeListRendering(numPr, numbering) {
542
542
  level: ilvl,
543
543
  numId,
544
544
  marker: level.lvlText,
545
+ markerTemplate: level.lvlText,
545
546
  isBullet: level.numFmt === "bullet",
546
547
  numFmt: level.isLgl ? "decimal" : level.numFmt,
547
548
  levelNumFmts,
@@ -609,5 +610,7 @@ function getBulletCharacter(level) {
609
610
  function isBulletLevel(level) {
610
611
  return level.numFmt === "bullet" || level.numFmt === "none";
611
612
  }
613
+ /** Whether a numbering level reserves horizontal space for a visible marker. */
614
+ const numberingLevelHasMarkerSlot = (level) => level.numFmt !== "none";
612
615
  //#endregion
613
- export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
616
+ export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, markerFormattingFromLevel, numPrEqual, numberingLevelHasMarkerSlot, padDecimal, parseNumbering, renderListMarker };
@@ -4,7 +4,7 @@ import { isValidHexId } from "../utils/hexId.js";
4
4
  import { parseBookmarkEnd as parseBookmarkEnd$1, parseBookmarkStart as parseBookmarkStart$1 } from "./bookmarkParser.js";
5
5
  import { parseFieldType } from "./fieldParser.js";
6
6
  import { parseHyperlink as parseHyperlink$1, parseHyperlinkChild } from "./hyperlinkParser.js";
7
- import { markerFormattingFromLevel } from "./numberingParser.js";
7
+ import { markerFormattingFromLevel, numberingLevelHasMarkerSlot } from "./numberingParser.js";
8
8
  import { paraIdInRange } from "./paraIdRangeNormalization.js";
9
9
  import { assignParagraphPropertySource } from "./paragraphPropertySource.js";
10
10
  import { BorderStyleSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, TabLeaderSchema, TabStopAlignmentSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
@@ -495,10 +495,10 @@ function normalizeDeletionContentElement(node) {
495
495
  let mappedName = node.name;
496
496
  if (localName === "delText") mappedName = replaceLocalName(node.name, "t");
497
497
  else if (localName === "delInstrText") mappedName = replaceLocalName(node.name, "instrText");
498
- const result = { ...node };
499
- if (mappedName !== void 0) result.name = mappedName;
500
- if (node.elements) result.elements = node.elements.map(normalizeDeletionContentElement);
501
- return result;
498
+ return cloneElement(node, {
499
+ ...mappedName !== void 0 ? { name: mappedName } : {},
500
+ ...node.elements ? { elements: node.elements.map(normalizeDeletionContentElement) } : {}
501
+ });
502
502
  }
503
503
  function parseParagraphPropertyChanges(pPr, theme, styles, currentFormatting) {
504
504
  if (!pPr) return;
@@ -1122,6 +1122,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1122
1122
  level: ilvl,
1123
1123
  numId,
1124
1124
  marker: level.lvlText,
1125
+ markerTemplate: level.lvlText,
1125
1126
  isBullet: level.numFmt === "bullet",
1126
1127
  levelNumFmts,
1127
1128
  levelStarts
@@ -1186,7 +1187,7 @@ function parseParagraph(node, styles, theme, numbering, rels = null, media = nul
1186
1187
  const directHanging = directInd ? parseNumericAttribute(directInd, "w", "hanging") : void 0;
1187
1188
  const hasDirectFirstLineOrHanging = directFirstLine !== void 0 && directFirstLine !== 0 || directHanging !== void 0 && directHanging !== 0;
1188
1189
  if (!hasDirectLeft && !chainInd.left && level.pPr.indentLeft !== void 0) paragraph.formatting.indentLeft = level.pPr.indentLeft;
1189
- if (!hasDirectFirstLineOrHanging && !chainInd.firstLine) {
1190
+ if (!hasDirectFirstLineOrHanging && !chainInd.firstLine && numberingLevelHasMarkerSlot(level)) {
1190
1191
  if (level.pPr.indentFirstLine !== void 0) paragraph.formatting.indentFirstLine = level.pPr.indentFirstLine;
1191
1192
  if (level.pPr.hangingIndent !== void 0) paragraph.formatting.hangingIndent = level.pPr.hangingIndent;
1192
1193
  }
@@ -1,41 +1,37 @@
1
1
  //#region src/docx/paragraphTraversal.ts
2
2
  /** Visit every run directly owned by a paragraph's inline-content tree. */
3
3
  const visitParagraphRuns = (paragraph, visit) => {
4
- const visitRun = (run) => {
5
- visit(run);
6
- };
7
- const visitHyperlink = (hyperlink) => {
8
- for (const child of hyperlink.children) if (child.type === "run") visitRun(child);
9
- };
10
- const visitInlineContent = (content) => {
11
- for (const child of content) {
12
- if (child.type === "run") {
13
- visitRun(child);
14
- continue;
15
- }
16
- if (child.type === "hyperlink") visitHyperlink(child);
17
- }
18
- };
19
4
  const visitParagraphContent = (content) => {
20
- if (content.type === "run") {
21
- visitRun(content);
22
- return;
23
- }
24
- if (content.type === "hyperlink") {
25
- visitHyperlink(content);
26
- return;
27
- }
28
- if (content.type === "simpleField" || content.type === "insertion" || content.type === "deletion" || content.type === "moveFrom" || content.type === "moveTo") {
29
- visitInlineContent(content.content);
30
- return;
31
- }
32
- if (content.type === "inlineSdt") {
33
- for (const child of content.content) visitParagraphContent(child);
34
- return;
35
- }
36
- if (content.type === "complexField") {
37
- for (const run of content.fieldCode) visitRun(run);
38
- for (const run of content.fieldResult) visitRun(run);
5
+ switch (content.type) {
6
+ case "run":
7
+ visit(content);
8
+ return;
9
+ case "hyperlink":
10
+ for (const child of content.children) visitParagraphContent(child);
11
+ return;
12
+ case "simpleField":
13
+ case "insertion":
14
+ case "deletion":
15
+ case "moveFrom":
16
+ case "moveTo":
17
+ case "inlineSdt":
18
+ for (const child of content.content) visitParagraphContent(child);
19
+ return;
20
+ case "complexField":
21
+ for (const run of content.fieldCode) visit(run);
22
+ for (const run of content.fieldResult) visit(run);
23
+ return;
24
+ case "bookmarkStart":
25
+ case "bookmarkEnd":
26
+ case "commentRangeStart":
27
+ case "commentRangeEnd":
28
+ case "commentReference":
29
+ case "moveFromRangeStart":
30
+ case "moveFromRangeEnd":
31
+ case "moveToRangeStart":
32
+ case "moveToRangeEnd":
33
+ case "mathEquation": return;
34
+ default: return content;
39
35
  }
40
36
  };
41
37
  for (const content of paragraph.content) visitParagraphContent(content);
@@ -28,14 +28,6 @@ type RepackOptions = {
28
28
  /** Changed note paragraphs that must be serialized even without source paraIds. */
29
29
  changedNoteParaIds?: ReadonlySet<string>;
30
30
  };
31
- /**
32
- * Repack a Document into a valid DOCX file
33
- *
34
- * @param doc - Document with modified content
35
- * @param options - Optional repack options
36
- * @returns Promise resolving to DOCX as ArrayBuffer
37
- * @throws {Error} if document has no original buffer for round-trip
38
- */
39
31
  declare function repackDocx(doc: document_d_exports.Document, options?: RepackOptions): Promise<ArrayBuffer>;
40
32
  /**
41
33
  * Repack a Document using raw content for more control
@@ -1,3 +1,4 @@
1
+ import { consumeTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
1
2
  import { isAllowedExternalWatermarkImageUrl } from "../watermark/index.js";
2
3
  import { normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
3
4
  import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
@@ -115,11 +116,34 @@ const extractHeaderFooterReferences = (xml) => {
115
116
  const hasParsedHeaderFooterPart = (doc, ref) => {
116
117
  return (ref.element === "headerReference" ? doc.package.headers : doc.package.footers)?.has(ref.rId) ?? false;
117
118
  };
118
- function assertDocumentPackageFidelity(originalDocumentXml, serializedDocumentXml, doc) {
119
+ const headerFooterReferenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
120
+ const incrementReferenceCount = (counts, key) => {
121
+ counts.set(key, (counts.get(key) ?? 0) + 1);
122
+ };
123
+ const consumeReferenceCount = (counts, key) => {
124
+ const count = counts.get(key) ?? 0;
125
+ if (count === 0) return false;
126
+ counts.set(key, count - 1);
127
+ return true;
128
+ };
129
+ function assertDocumentPackageFidelity(originalDocumentXml, serializedDocumentXml, doc, sectionEndpointRemoval) {
119
130
  const originalSectionCount = countDocumentSections(originalDocumentXml);
120
- if (countDocumentSections(serializedDocumentXml) < originalSectionCount) throw new DocxPackageFidelityError("Full DOCX repack would drop section properties. Use selective patching instead.");
121
- const serializedRefs = new Set(extractHeaderFooterReferences(serializedDocumentXml).map((ref) => `${ref.element}:${ref.type}:${ref.rId}`));
122
- if (extractHeaderFooterReferences(originalDocumentXml).filter((ref) => hasParsedHeaderFooterPart(doc, ref) && !serializedRefs.has(`${ref.element}:${ref.type}:${ref.rId}`)).length > 0) throw new DocxPackageFidelityError("Full DOCX repack would drop header/footer references. Use selective patching instead.");
131
+ const serializedSectionCount = countDocumentSections(serializedDocumentXml);
132
+ const trackedRemovedSectionCount = sectionEndpointRemoval ? sectionEndpointRemoval.sourceParagraphEndpointCount - sectionEndpointRemoval.expectedParagraphEndpointCount : 0;
133
+ const exactTrackedSectionRemoval = trackedRemovedSectionCount > 0 && originalSectionCount - trackedRemovedSectionCount === serializedSectionCount && serializedSectionCount < originalSectionCount;
134
+ if (serializedSectionCount < originalSectionCount && !exactTrackedSectionRemoval) throw new DocxPackageFidelityError("Full DOCX repack would drop section properties. Use selective patching instead.");
135
+ const serializedReferenceCounts = /* @__PURE__ */ new Map();
136
+ for (const reference of extractHeaderFooterReferences(serializedDocumentXml)) incrementReferenceCount(serializedReferenceCounts, headerFooterReferenceKey(reference));
137
+ const removedReferenceCounts = /* @__PURE__ */ new Map();
138
+ if (exactTrackedSectionRemoval && sectionEndpointRemoval) for (const { part, type, relationshipId } of sectionEndpointRemoval.removedReferences) incrementReferenceCount(removedReferenceCounts, `${part}Reference:${type}:${relationshipId}`);
139
+ const missingRefs = [];
140
+ for (const reference of extractHeaderFooterReferences(originalDocumentXml)) {
141
+ if (!hasParsedHeaderFooterPart(doc, reference)) continue;
142
+ const key = headerFooterReferenceKey(reference);
143
+ if (consumeReferenceCount(serializedReferenceCounts, key) || consumeReferenceCount(removedReferenceCounts, key)) continue;
144
+ missingRefs.push(reference);
145
+ }
146
+ if (missingRefs.length > 0) throw new DocxPackageFidelityError("Full DOCX repack would drop header/footer references. Use selective patching instead.");
123
147
  }
124
148
  async function serializeCommentsToZip(doc, zip, compressionLevel) {
125
149
  const comments = doc.package.document.comments ?? [];
@@ -488,7 +512,7 @@ const cloneDocxZip = (source) => {
488
512
  clone.files = { ...source.files };
489
513
  return clone;
490
514
  };
491
- const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds }) => {
515
+ const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval }) => {
492
516
  await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
493
517
  const parts = collectDocxParts(document, outputZip);
494
518
  await processNewImages(parts, outputZip, compressionLevel);
@@ -496,7 +520,7 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
496
520
  assertValidFolioDocumentModel(document, "Cannot repack invalid DOCX document model");
497
521
  applyReplyThreadMarkers(document);
498
522
  const documentXml = serializeDocument(document, originalDocumentXml === void 0 ? void 0 : readRootNamespaceBindings(originalDocumentXml));
499
- if (originalDocumentXml) assertDocumentPackageFidelity(originalDocumentXml, documentXml, document);
523
+ if (originalDocumentXml) assertDocumentPackageFidelity(originalDocumentXml, documentXml, document, sectionEndpointRemoval);
500
524
  outputZip.file("word/document.xml", documentXml, {
501
525
  compression: "DEFLATE",
502
526
  compressionOptions: { level: compressionLevel }
@@ -526,15 +550,7 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
526
550
  }
527
551
  return generateDocxZip(outputZip, compressionLevel);
528
552
  };
529
- /**
530
- * Repack a Document into a valid DOCX file
531
- *
532
- * @param doc - Document with modified content
533
- * @param options - Optional repack options
534
- * @returns Promise resolving to DOCX as ArrayBuffer
535
- * @throws {Error} if document has no original buffer for round-trip
536
- */
537
- async function repackDocx(doc, options = {}) {
553
+ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, sectionEndpointRemoval }) {
538
554
  if (!doc.originalBuffer) panic("Cannot repack document: no original buffer for round-trip. Use createDocx() for new documents.");
539
555
  const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
540
556
  const exportDocument = withoutOrphanCommentRanges(doc);
@@ -551,7 +567,16 @@ async function repackDocx(doc, options = {}) {
551
567
  compressionLevel,
552
568
  updateModifiedDate,
553
569
  ...modifiedBy !== void 0 ? { modifiedBy } : {},
554
- ...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {}
570
+ ...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {},
571
+ ...sectionEndpointRemoval !== void 0 ? { sectionEndpointRemoval } : {}
572
+ });
573
+ }
574
+ function repackDocx(doc, options = {}) {
575
+ const sectionEndpointRemoval = consumeTrackedSectionEndpointRemoval(doc);
576
+ return repackDocxWithSectionEndpointRemoval({
577
+ document: doc,
578
+ options,
579
+ ...sectionEndpointRemoval ? { sectionEndpointRemoval } : {}
555
580
  });
556
581
  }
557
582
  /**