@stll/folio-core 0.36.0 → 0.37.1

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 +689 -367
  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 +60 -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 +201 -569
  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 +8 -6
  55. package/dist/document-operations.js +121 -26
  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 +176 -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 +167 -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,12 +1,12 @@
1
- import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAISignatureParty, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./types.js";
1
+ import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIBlockStructuralBoundary, FolioAIBlockTableLocation, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditNormalization, FolioAIEditNormalizationCode, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineBooleanProperty, FolioAIInlineFormatting, FolioAIInlineFormattingPatch, FolioAIParagraphSpacing, FolioAISignatureParty, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./types.js";
2
2
  import { WORD_DIFF_GRANULARITIES, WordDiffGranularity, WordDiffNormalization, WordDiffOptions, WordDiffSegment, diffWordSegments } from "./word-diff.js";
3
3
  import { FolioAIEditApplyOutcome, FolioAIEditView, FolioRevisionStamp, FolioWordDiffOptions, applyFolioAIEditOperations } from "./apply.js";
4
4
  import { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange } from "./blockRange.js";
5
5
  import { buildAnnotatedBlockText } from "./clean-text.js";
6
- import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
6
+ import { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationResultBase, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
7
7
  import { FolioCommentAnchor, FolioReviewChange, FolioReviewChangeKind, getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
8
8
  import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
9
9
  import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsToStoryOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.js";
10
10
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
11
11
  import { getFolioParaIdFromBlockId } from "../types/block-id.js";
12
- export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineFormatting, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsToStoryOptions, type FolioCommentAnchor, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioEditableDocumentStoryHandle, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeKind, type FolioReviewedStory, type FolioReviewedView, type FolioRevisionStamp, type FolioWordDiffOptions, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
12
+ export { type ApplyFolioDocumentOperationsOptions, type DocPositionRange, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIEditView, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsToStoryOptions, type FolioCommentAnchor, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioEditableDocumentStoryHandle, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeKind, type FolioReviewedStory, type FolioReviewedView, type FolioRevisionStamp, type FolioWordDiffOptions, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -1,4 +1,4 @@
1
- import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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 } from "../document-operations.js";
1
+ import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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 } from "../document-operations.js";
2
2
  import { getFolioParaIdFromBlockId } from "../types/block-id.js";
3
3
  import { applyFolioAIEditOperations } from "./apply.js";
4
4
  import { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange } from "./blockRange.js";
@@ -8,4 +8,4 @@ import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
8
8
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
9
9
  import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlockText, isFolioAIContentBlock, normalizeFolioAIBlockText } from "./snapshot.js";
10
10
  import { WORD_DIFF_GRANULARITIES, diffWordSegments } from "./word-diff.js";
11
- export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
11
+ export { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, 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, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, WORD_DIFF_GRANULARITIES, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioAIContentBlock, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -1,5 +1,6 @@
1
1
  import { expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
2
2
  import { getFolioNodeRevisionCarriers } from "../prosemirror/revisionCarriers.js";
3
+ import { expandRunFormattingCarrier, runFormattingCarrierReviewText, runFormattingInlineAtomDisposition } from "../prosemirror/runFormattingInlineCarriers.js";
3
4
  import { getTableCellMergeChange } from "../prosemirror/tableCellMergeRevision.js";
4
5
  import { createFolioAIEditSnapshot } from "./snapshot.js";
5
6
  //#region src/ai-edits/read.ts
@@ -66,6 +67,7 @@ const getTrackedChangesFromDoc = (doc) => {
66
67
  const grouped = /* @__PURE__ */ new Map();
67
68
  const structuralChangeCellPositions = /* @__PURE__ */ new Map();
68
69
  const rowRevisionScopes = [];
70
+ const derivedFormattingRepresentationPositions = /* @__PURE__ */ new Set();
69
71
  let currentBlockId = null;
70
72
  doc.descendants((node, pos) => {
71
73
  while (pos >= (rowRevisionScopes.at(-1)?.end ?? Number.POSITIVE_INFINITY)) rowRevisionScopes.pop();
@@ -172,29 +174,45 @@ const getTrackedChangesFromDoc = (doc) => {
172
174
  currentBlockId = blockStarts.get(pos) ?? null;
173
175
  return true;
174
176
  }
175
- if (!node.isInline || node.text === void 0) return;
176
- const text = node.text;
177
- for (const mark of node.marks) {
178
- if (mark.type.name === "runPropertyChange") {
179
- const { changes } = expectRunPropertyChangeMarkAttrs(mark);
180
- for (const change of changes) {
181
- const key = `${currentBlockId ?? ""}:formatting:${String(change.info.id)}`;
182
- const existing = grouped.get(key);
183
- if (existing) {
184
- existing.text += text;
185
- continue;
177
+ if (!node.isInline) return;
178
+ if (!derivedFormattingRepresentationPositions.has(pos)) {
179
+ const disposition = runFormattingInlineAtomDisposition(node);
180
+ const formattingCarrier = disposition === "structured-field" || disposition !== null && disposition !== "not-a-run" && node.marks.some((mark) => mark.type.name === "runPropertyChange") ? expandRunFormattingCarrier(node, pos) : null;
181
+ if (formattingCarrier) {
182
+ let changesById;
183
+ for (const representation of formattingCarrier.representations) {
184
+ if (representation.role === "serialized-result") derivedFormattingRepresentationPositions.add(representation.position);
185
+ const changeMark = representation.node.marks.find((mark) => mark.type.name === "runPropertyChange");
186
+ if (!changeMark) continue;
187
+ for (const change of expectRunPropertyChangeMarkAttrs(changeMark).changes) {
188
+ changesById ??= /* @__PURE__ */ new Map();
189
+ if (!changesById.has(change.info.id)) changesById.set(change.info.id, change);
190
+ }
191
+ }
192
+ if (changesById) {
193
+ const text = runFormattingCarrierReviewText(formattingCarrier);
194
+ for (const change of changesById.values()) {
195
+ const key = `${currentBlockId ?? ""}:formatting:${String(change.info.id)}`;
196
+ const existing = grouped.get(key);
197
+ if (existing) {
198
+ existing.text += text;
199
+ continue;
200
+ }
201
+ grouped.set(key, {
202
+ id: change.info.id,
203
+ type: "formatting",
204
+ author: change.info.author,
205
+ date: change.info.date ?? null,
206
+ text,
207
+ blockId: currentBlockId
208
+ });
186
209
  }
187
- grouped.set(key, {
188
- id: change.info.id,
189
- type: "formatting",
190
- author: change.info.author,
191
- date: change.info.date ?? null,
192
- text,
193
- blockId: currentBlockId
194
- });
195
210
  }
196
- continue;
197
211
  }
212
+ }
213
+ const text = node.text ?? "";
214
+ for (const mark of node.marks) {
215
+ if (mark.type.name === "runPropertyChange") continue;
198
216
  if (typeof mark.attrs["revisionId"] !== "number") continue;
199
217
  let kind;
200
218
  if (mark.type === insertionType) kind = "insertion";
@@ -1,4 +1,6 @@
1
- import { FolioAIBlock, FolioAIEditSnapshot, FolioAITextRangeHandle } from "./types.js";
1
+ import { FolioAIBlock, FolioAIBlockStructuralBoundary, FolioAIEditSnapshot, FolioAITextRangeHandle } from "./types.js";
2
+ import { CleanBlockText } from "./clean-text.js";
3
+ import { RunStyleResolver } from "../prosemirror/runStyleFormatting.js";
2
4
  import { Node } from "prosemirror-model";
3
5
  //#region src/ai-edits/snapshot.d.ts
4
6
  /** @internal Numbering references collected during the snapshot's document walk. */
@@ -30,13 +32,17 @@ declare const isFolioAIContentBlock: ({ text }: Pick<FolioAIBlock, "text">) => b
30
32
  * container-edge rule then applies and the addition cannot be rejected
31
33
  * cleanly.
32
34
  *
33
- * A body always ends with a paragraph — a table may not be the last child of a
34
- * body or a cell, so a package that ends in a table carries a trailing, often
35
- * empty, paragraph after it so this is only `null` for a story with no
36
- * body-level paragraph at all, which is a malformed document.
35
+ * A body may validly end with a table immediately before its final section
36
+ * properties. Such a story has no body paragraph to anchor to, so this returns
37
+ * `null`; callers that can place a peer beside the table use its outer boundary
38
+ * instead.
37
39
  */
38
40
  declare const trailingBodyBlockId: ({ blocks }: FolioAIEditSnapshot) => string | null;
39
41
  declare const hashFolioAIBlockText: (text: string) => string;
42
+ /** Canonical public projection of the clean view's zero-width structure. */
43
+ declare const projectFolioAIBlockStructuralBoundaries: ({ structuralBoundaries }: Pick<CleanBlockText, "structuralBoundaries">) => readonly FolioAIBlockStructuralBoundary[];
44
+ /** Stable precondition fingerprint for a block's zero-width structure. */
45
+ declare const hashFolioAIBlockStructuralBoundaries: (cleanBlock: Pick<CleanBlockText, "structuralBoundaries">) => string;
40
46
  type CreateFolioAITextRangeHandleOptions = {
41
47
  blockId: string;
42
48
  text: string;
@@ -71,5 +77,7 @@ type FolioStoryTable = {
71
77
  */
72
78
  declare const folioStoryTables: (doc: Node) => FolioStoryTable[];
73
79
  declare const createFolioAIEditSnapshot: (doc: Node) => FolioAIEditSnapshot;
80
+ /** @internal Use for an EditorState that owns the document's style resolver. */
81
+ declare const createFolioAIEditSnapshotWithStyleResolver: (doc: Node, styleResolver: RunStyleResolver | null) => FolioAIEditSnapshot;
74
82
  //#endregion
75
- export { FolioStoryTable, createFolioAIEditSnapshot, createFolioAITextRangeHandle, folioStoryTables, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, trailingBodyBlockId };
83
+ export { FolioStoryTable, createFolioAIEditSnapshot, createFolioAIEditSnapshotWithStyleResolver, createFolioAITextRangeHandle, folioStoryTables, hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, projectFolioAIBlockStructuralBoundaries, trailingBodyBlockId };
@@ -1,5 +1,9 @@
1
1
  import { expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs } from "../prosemirror/attrs/index.js";
2
+ import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
2
3
  import { directParagraphAlignment } from "../prosemirror/paragraphAlignment.js";
4
+ import { directParagraphSpacing } from "../prosemirror/paragraphSpacing.js";
5
+ import { authoredRunFormattingFromAttrs } from "../prosemirror/runFormattingProvenance.js";
6
+ import { paragraphRunStyleContext } from "../prosemirror/runStyleFormatting.js";
3
7
  import { deriveBlankBlockId, deriveBlockId } from "../types/block-id.js";
4
8
  import { buildCleanBlockText } from "./clean-text.js";
5
9
  import { panic } from "better-result";
@@ -35,10 +39,10 @@ const isFolioAIContentBlock = ({ text }) => normalizeFolioAIBlockText(text).leng
35
39
  * container-edge rule then applies and the addition cannot be rejected
36
40
  * cleanly.
37
41
  *
38
- * A body always ends with a paragraph — a table may not be the last child of a
39
- * body or a cell, so a package that ends in a table carries a trailing, often
40
- * empty, paragraph after it so this is only `null` for a story with no
41
- * body-level paragraph at all, which is a malformed document.
42
+ * A body may validly end with a table immediately before its final section
43
+ * properties. Such a story has no body paragraph to anchor to, so this returns
44
+ * `null`; callers that can place a peer beside the table use its outer boundary
45
+ * instead.
42
46
  */
43
47
  const trailingBodyBlockId = ({ blocks }) => {
44
48
  for (let index = blocks.length - 1; index >= 0; index--) {
@@ -52,6 +56,24 @@ const hashFolioAIBlockText = (text) => {
52
56
  for (const character of text) hash = (hash * 33 + (character.codePointAt(0) ?? 0)) % 2147483647;
53
57
  return `h${hash.toString(36)}`;
54
58
  };
59
+ const EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARIES = Object.freeze([]);
60
+ const EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARY_HASH = hashFolioAIBlockText(JSON.stringify(EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARIES));
61
+ /** Canonical public projection of the clean view's zero-width structure. */
62
+ const projectFolioAIBlockStructuralBoundaries = ({ structuralBoundaries }) => {
63
+ let projected;
64
+ for (const { clear, offset, presentInCleanView } of structuralBoundaries) {
65
+ if (!presentInCleanView) continue;
66
+ (projected ??= []).push({
67
+ type: "pageBreak",
68
+ offset,
69
+ ...clear !== void 0 ? { clear } : {}
70
+ });
71
+ }
72
+ return projected ?? EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARIES;
73
+ };
74
+ const hashFolioAIBlockStructuralBoundaryProjection = (structuralBoundaries) => structuralBoundaries.length === 0 ? EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARY_HASH : hashFolioAIBlockText(JSON.stringify(structuralBoundaries));
75
+ /** Stable precondition fingerprint for a block's zero-width structure. */
76
+ const hashFolioAIBlockStructuralBoundaries = (cleanBlock) => hashFolioAIBlockStructuralBoundaryProjection(projectFolioAIBlockStructuralBoundaries(cleanBlock));
55
77
  const createFolioAITextRangeHandle = ({ blockId, text, startOffset, endOffset }) => {
56
78
  if (blockId.length === 0 || !Number.isInteger(startOffset) || !Number.isInteger(endOffset) || startOffset < 0 || endOffset <= startOffset || endOffset > text.length) return null;
57
79
  return {
@@ -127,7 +149,7 @@ const getTableLocation = ({ path, blockIndex, tableIndexByStart }) => {
127
149
  };
128
150
  }
129
151
  };
130
- const createFolioAIEditSnapshot = (doc) => {
152
+ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
131
153
  const draftBlocks = [];
132
154
  const hashCounts = /* @__PURE__ */ new Map();
133
155
  const usedBlockIds = /* @__PURE__ */ new Set();
@@ -148,12 +170,16 @@ const createFolioAIEditSnapshot = (doc) => {
148
170
  });
149
171
  return true;
150
172
  }
151
- const { text } = buildCleanBlockText(node, pos);
173
+ const cleanBlock = buildCleanBlockText(node, pos);
174
+ const { text } = cleanBlock;
175
+ const structuralBoundaries = projectFolioAIBlockStructuralBoundaries(cleanBlock);
176
+ const structuralBoundaryHash = hashFolioAIBlockStructuralBoundaryProjection(structuralBoundaries);
152
177
  const normalizedText = normalizeFolioAIBlockText(text);
153
178
  const textHash = hashFolioAIBlockText(normalizedText);
154
179
  hashCounts.set(textHash, (hashCounts.get(textHash) ?? 0) + 1);
155
180
  const paraIdAttr = node.attrs["paraId"];
156
181
  const paraId = typeof paraIdAttr === "string" && paraIdAttr.length > 0 ? paraIdAttr : null;
182
+ const idStability = node.attrs["idStability"] === "positional" ? "positional" : void 0;
157
183
  const isBlank = normalizedText.length === 0;
158
184
  let id;
159
185
  if (isBlank) {
@@ -180,7 +206,8 @@ const createFolioAIEditSnapshot = (doc) => {
180
206
  const numberingReferenceKey = getNumberingReferenceKey(node);
181
207
  if (numberingReferenceKey) numberingReferenceKeys.add(numberingReferenceKey);
182
208
  const directAlignment = getDirectAlignment(node);
183
- const previewRuns = getPreviewRuns(node);
209
+ const directSpacing = getDirectSpacing(node);
210
+ const previewRuns = getPreviewRuns(node, styleResolver);
184
211
  const table = getTableLocation({
185
212
  path,
186
213
  blockIndex: index,
@@ -191,12 +218,15 @@ const createFolioAIEditSnapshot = (doc) => {
191
218
  id,
192
219
  kind,
193
220
  text,
221
+ ...idStability !== void 0 && { idStability },
194
222
  ...headingLevel !== void 0 && { headingLevel },
195
223
  ...displayLabel !== void 0 && { displayLabel },
196
224
  ...styleId !== void 0 && { styleId },
197
225
  ...listLevel !== void 0 && { listLevel },
198
226
  ...directAlignment !== void 0 && { directAlignment },
227
+ ...directSpacing !== void 0 && { directSpacing },
199
228
  ...previewRuns !== void 0 && { previewRuns },
229
+ ...structuralBoundaries.length > 0 && { structuralBoundaries },
200
230
  ...table !== void 0 && { table }
201
231
  },
202
232
  anchor: {
@@ -205,7 +235,8 @@ const createFolioAIEditSnapshot = (doc) => {
205
235
  to: pos + node.nodeSize,
206
236
  text,
207
237
  normalizedText,
208
- textHash
238
+ textHash,
239
+ structuralBoundaryHash
209
240
  }
210
241
  });
211
242
  return true;
@@ -226,6 +257,9 @@ const createFolioAIEditSnapshot = (doc) => {
226
257
  numberingReferenceKeysBySnapshot.set(snapshot, [...numberingReferenceKeys]);
227
258
  return snapshot;
228
259
  };
260
+ const createFolioAIEditSnapshot = (doc) => createFolioAIEditSnapshotInternal(doc, null);
261
+ /** @internal Use for an EditorState that owns the document's style resolver. */
262
+ const createFolioAIEditSnapshotWithStyleResolver = (doc, styleResolver) => createFolioAIEditSnapshotInternal(doc, styleResolver);
229
263
  const getBlockKind = (node, headingLevel) => {
230
264
  const listMarker = node.attrs["listMarker"];
231
265
  const numPr = node.attrs["numPr"];
@@ -268,15 +302,34 @@ const getStyleId = (node) => {
268
302
  };
269
303
  /** Read only authored `w:jc`, never the effective alignment resolved from a style. */
270
304
  const getDirectAlignment = (node) => directParagraphAlignment(expectParagraphAttrs(node));
305
+ /** Read only authored `w:spacing`, never effective spacing resolved from a style. */
306
+ const getDirectSpacing = (node) => directParagraphSpacing(expectParagraphAttrs(node));
271
307
  const DELETION_MARK = "deletion";
272
- const getPreviewRuns = (node) => {
308
+ const HIDDEN_MARK = "hidden";
309
+ const RUN_FORMATTING_OVERRIDE_MARK = "runFormattingOverride";
310
+ const CHARACTER_STYLE_MARK = "characterStyle";
311
+ const getPreviewRuns = (node, styleResolver) => {
273
312
  const runs = [];
274
313
  const defaultStyle = getDefaultPreviewRunStyle(node);
314
+ let paragraphStyleContext;
275
315
  node.descendants((child) => {
276
316
  if (!child.isText || child.text === void 0) return true;
277
- if (child.marks.some((mark) => mark.type.name === DELETION_MARK)) return false;
317
+ if (child.marks.some((mark) => mark.type.name === DELETION_MARK || mark.type.name === HIDDEN_MARK)) return false;
278
318
  const style = getPreviewRunStyle(child.marks, defaultStyle);
279
- const directFormatting = getDirectPreviewRunStyle(child.marks, defaultStyle);
319
+ const hasAuthorshipCarrier = child.marks.some(({ type }) => type.name === RUN_FORMATTING_OVERRIDE_MARK || type.name === CHARACTER_STYLE_MARK);
320
+ let directFormatting;
321
+ if (!hasAuthorshipCarrier) directFormatting = getDirectPreviewRunStyleFromMarks(child.marks, defaultStyle);
322
+ else {
323
+ paragraphStyleContext ??= paragraphRunStyleContext(node, styleResolver);
324
+ const directTextFormatting = marksToTextFormatting(child.marks, {
325
+ baseParagraphFormatting: paragraphStyleContext.baseParagraphFormatting,
326
+ inheritedFormatting: paragraphStyleContext.paragraphFormatting,
327
+ paragraphMarkFormatting: paragraphStyleContext.paragraphMarkFormatting,
328
+ paragraphMarkPrecedesStyle: paragraphStyleContext.paragraphMarkPrecedesStyle,
329
+ styleResolver
330
+ });
331
+ directFormatting = getDirectPreviewRunStyle(child.marks, directTextFormatting, styleResolver);
332
+ }
280
333
  const previous = runs.at(-1);
281
334
  if (previous && samePreviewRunStyle(previous, style) && sameDirectFormatting(previous.directFormatting, directFormatting)) {
282
335
  previous.text += child.text;
@@ -334,23 +387,50 @@ const getPreviewRunStyle = (marks, defaultStyle) => {
334
387
  }
335
388
  default: break;
336
389
  }
390
+ const overrideMark = marks.find(({ type }) => type.name === RUN_FORMATTING_OVERRIDE_MARK);
391
+ if (!overrideMark) return style;
392
+ const overrides = expectRunFormattingOverrideMarkAttrs(overrideMark);
393
+ if (overrides.bold === false) delete style.bold;
394
+ if (overrides.italic === false) delete style.italic;
395
+ if (overrides.underline === "none") delete style.underline;
396
+ const hasDoubleStrike = marks.some((mark) => mark.type.name === "strike" && mark.attrs["double"] === true);
397
+ if (overrides.strike === false && !hasDoubleStrike) delete style.strike;
398
+ if (overrides.color === "auto") delete style.color;
337
399
  return style;
338
400
  };
339
- const getDirectPreviewRunStyle = (marks, inheritedStyle) => {
401
+ const getDirectPreviewRunStyleFromMarks = (marks, inheritedStyle) => {
340
402
  const markedStyle = getPreviewRunStyle(marks, {});
341
403
  const directStyle = {};
342
- const overrideMark = marks.find(({ type }) => type.name === "runFormattingOverride");
343
- const hasFormattingProvenance = overrideMark !== void 0 || marks.some(({ type }) => type.name === "characterStyle");
344
- const directFontProperties = overrideMark ? expectRunFormattingOverrideMarkAttrs(overrideMark).directFontProperties : void 0;
345
404
  for (const property of [
346
405
  "bold",
347
406
  "italic",
348
407
  "underline",
349
408
  "strike"
350
409
  ]) if (Boolean(markedStyle[property]) !== Boolean(inheritedStyle[property])) directStyle[property] = Boolean(markedStyle[property]);
351
- if (markedStyle.fontFamily !== void 0 && (hasFormattingProvenance ? directFontProperties?.includes("fontFamily") : markedStyle.fontFamily !== inheritedStyle.fontFamily)) directStyle.fontFamily = markedStyle.fontFamily;
352
- if (markedStyle.fontSizePt !== void 0 && (hasFormattingProvenance ? directFontProperties?.includes("fontSize") : markedStyle.fontSizePt !== inheritedStyle.fontSizePt)) directStyle.fontSizePt = markedStyle.fontSizePt;
353
- if (markedStyle.color !== void 0 && (hasFormattingProvenance ? directFontProperties?.includes("color") : markedStyle.color !== inheritedStyle.color)) directStyle.color = markedStyle.color;
410
+ if (markedStyle.fontFamily !== void 0 && markedStyle.fontFamily !== inheritedStyle.fontFamily) directStyle.fontFamily = markedStyle.fontFamily;
411
+ if (markedStyle.fontSizePt !== void 0 && markedStyle.fontSizePt !== inheritedStyle.fontSizePt) directStyle.fontSizePt = markedStyle.fontSizePt;
412
+ if (markedStyle.color !== void 0 && markedStyle.color !== inheritedStyle.color) directStyle.color = markedStyle.color;
413
+ return directStyle;
414
+ };
415
+ const getDirectPreviewRunStyle = (marks, formatting, styleResolver) => {
416
+ let directFormatting = formatting;
417
+ if (!styleResolver) {
418
+ const overrideMark = marks.find(({ type }) => type.name === RUN_FORMATTING_OVERRIDE_MARK);
419
+ const authoredFormatting = overrideMark ? authoredRunFormattingFromAttrs(expectRunFormattingOverrideMarkAttrs(overrideMark)) : void 0;
420
+ if (authoredFormatting !== void 0) directFormatting = authoredFormatting;
421
+ else if (marks.some(({ type }) => type.name === CHARACTER_STYLE_MARK)) directFormatting = {};
422
+ }
423
+ const directStyle = {};
424
+ for (const property of ["bold", "italic"]) if (directFormatting[property] !== void 0) directStyle[property] = directFormatting[property];
425
+ if (directFormatting.underline !== void 0) directStyle.underline = isUnderlineEnabled(directFormatting.underline);
426
+ if (directFormatting.strike !== void 0) directStyle.strike = directFormatting.strike;
427
+ else if (directFormatting.doubleStrike === true) directStyle.strike = true;
428
+ const fontFamily = directFormatting.fontFamily ? getFontFamilyFromAttrs(directFormatting.fontFamily) : void 0;
429
+ if (fontFamily !== void 0) directStyle.fontFamily = fontFamily;
430
+ const fontSizePt = getFontSizeTextFormatting(directFormatting).fontSizePt;
431
+ if (fontSizePt !== void 0) directStyle.fontSizePt = fontSizePt;
432
+ const color = directFormatting.color ? getColorFromAttrs(directFormatting.color) : void 0;
433
+ if (color !== void 0) directStyle.color = color;
354
434
  return directStyle;
355
435
  };
356
436
  const getBooleanTextFormatting = (formatting) => ({
@@ -399,4 +479,4 @@ const isEmptyPreviewRunStyle = ({ bold, italic, underline, strike, fontFamily, f
399
479
  const sameDirectFormatting = (left, right) => left === void 0 && isEmptyPreviewRunStyle(right) || left !== void 0 && left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strike === right.strike && left.fontFamily === right.fontFamily && left.fontSizePt === right.fontSizePt && left.color === right.color;
400
480
  const isUnstyledPreviewRun = ({ bold, italic, underline, strike, fontFamily, fontSizePt, color }) => bold === void 0 && italic === void 0 && underline === void 0 && strike === void 0 && fontFamily === void 0 && fontSizePt === void 0 && color === void 0;
401
481
  //#endregion
402
- export { createFolioAIEditSnapshot, createFolioAITextRangeHandle, folioStoryTables, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, trailingBodyBlockId };
482
+ export { createFolioAIEditSnapshot, createFolioAIEditSnapshotWithStyleResolver, createFolioAITextRangeHandle, folioStoryTables, hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, projectFolioAIBlockStructuralBoundaries, trailingBodyBlockId };
@@ -5,8 +5,8 @@ import { markStructuralChange } from "../prosemirror/extensions/features/Paragra
5
5
  import { tableRectanglesEqual } from "./table-mutation-plan.js";
6
6
  import { tableRectangleCutsMergedCell } from "./table-targets.js";
7
7
  import { Result } from "better-result";
8
- import { TableMap } from "prosemirror-tables";
9
8
  import { Fragment } from "prosemirror-model";
9
+ import { TableMap } from "prosemirror-tables";
10
10
  //#region src/ai-edits/table-cell-mutations.ts
11
11
  const mergeTableRectangle = ({ tr, tablePosition, table, rectangle }) => {
12
12
  const map = TableMap.get(table);
@@ -1,4 +1,5 @@
1
1
  import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
2
+ import { reconcileTableGridAfterColumnRemoval } from "../prosemirror/tableGridMutation.js";
2
3
  import { stripBlockIdentityAttrs } from "./block-identity.js";
3
4
  import { findEnclosingTableCell, findEnclosingTableRow } from "./table-targets.js";
4
5
  import { tableRowFromTemplate } from "./table-template.js";
@@ -189,6 +190,12 @@ const applyTableColumnDeletion = ({ tr, deletion, insertedColumnCount, revision
189
190
  right: columnIndex + 1,
190
191
  bottom: map.height
191
192
  }, columnIndex);
193
+ reconcileTableGridAfterColumnRemoval({
194
+ tr,
195
+ tablePosition,
196
+ previousTable: table,
197
+ removedColumn: columnIndex
198
+ });
192
199
  return applied(tr, null);
193
200
  };
194
201
  const applyTableRowDeletion = ({ tr, deletion, revision }) => {
@@ -3,6 +3,16 @@ import { Node, Schema } from "prosemirror-model";
3
3
  //#region src/ai-edits/table-template.d.ts
4
4
  /** The node an operation should place, by the id of the operation placing it. */
5
5
  type FolioTableTemplates = ReadonlyMap<string, Node>;
6
+ /**
7
+ * Whether a target table can cross into the base package without semantic
8
+ * content being stripped from its template.
9
+ *
10
+ * Paragraph identities are deliberately excluded: they are regenerated in
11
+ * the receiving document. Package-owned relationships, annotations,
12
+ * bookmarks and unresolved revision metadata are not portable and therefore
13
+ * make a replacement unsafe. Callers can retain their granular plan instead.
14
+ */
15
+ declare const tableTemplateCanCrossPackageLosslessly: (template: Node) => boolean;
6
16
  /**
7
17
  * A structural tracked change on a table row.
8
18
  *
@@ -57,4 +67,4 @@ type TableRowFromTemplateOptions = {
57
67
  */
58
68
  declare const tableRowFromTemplate: ({ template, columnCount }: TableRowFromTemplateOptions) => Node | null;
59
69
  //#endregion
60
- export { FolioTableTemplates, TableStructureRevision, tableFromTemplate, tableRowFromTemplate };
70
+ export { FolioTableTemplates, TableStructureRevision, tableFromTemplate, tableRowFromTemplate, tableTemplateCanCrossPackageLosslessly };
@@ -73,6 +73,42 @@ const CLEARED_ROW_ATTRS = [
73
73
  "trPrChange"
74
74
  ];
75
75
  const CLEARED_CELL_ATTRS = ["cellMarker", "tcPrChange"];
76
+ const hasAuthoredValue = (value) => {
77
+ if (value === null || value === void 0 || value === false || value === "") return false;
78
+ return !Array.isArray(value) || value.length > 0;
79
+ };
80
+ const clearedAttrsOf = (node) => {
81
+ switch (node.type.spec["tableRole"]) {
82
+ case "table": return CLEARED_TABLE_ATTRS;
83
+ case "row": return CLEARED_ROW_ATTRS;
84
+ case "cell":
85
+ case "header_cell": return CLEARED_CELL_ATTRS;
86
+ default: return node.isTextblock ? CLEARED_PARAGRAPH_ATTRS : [];
87
+ }
88
+ };
89
+ const losesAuthoredAttrs = (node) => clearedAttrsOf(node).some((name) => hasAuthoredValue(node.attrs[name]));
90
+ /**
91
+ * Whether a target table can cross into the base package without semantic
92
+ * content being stripped from its template.
93
+ *
94
+ * Paragraph identities are deliberately excluded: they are regenerated in
95
+ * the receiving document. Package-owned relationships, annotations,
96
+ * bookmarks and unresolved revision metadata are not portable and therefore
97
+ * make a replacement unsafe. Callers can retain their granular plan instead.
98
+ */
99
+ const tableTemplateCanCrossPackageLosslessly = (template) => {
100
+ let portable = true;
101
+ const inspect = (node) => {
102
+ if (PACKAGE_BOUND_NODE_NAMES.has(node.type.name) || node.marks.some(({ type }) => PACKAGE_BOUND_MARK_NAMES.has(type.name)) || losesAuthoredAttrs(node)) {
103
+ portable = false;
104
+ return false;
105
+ }
106
+ return true;
107
+ };
108
+ if (!inspect(template)) return false;
109
+ template.descendants(inspect);
110
+ return portable;
111
+ };
76
112
  const withoutAttrs = (attrs, cleared) => {
77
113
  const next = { ...attrs };
78
114
  for (const key of cleared) next[key] = null;
@@ -92,24 +128,20 @@ const insertionMarkOf = (schema, revision) => {
92
128
  });
93
129
  };
94
130
  const copiedAttrs = (node, context) => {
131
+ const sourceAttrs = node.isTextblock ? stripBlockIdentityAttrs(node.attrs) : node.attrs;
132
+ const attrs = withoutAttrs(sourceAttrs, clearedAttrsOf(node));
95
133
  switch (node.type.spec["tableRole"]) {
96
- case "table": return withoutAttrs(node.attrs, CLEARED_TABLE_ATTRS);
97
- case "row": {
98
- const attrs = withoutAttrs(node.attrs, CLEARED_ROW_ATTRS);
99
- return context.revision ? {
100
- ...attrs,
101
- trIns: context.revision
102
- } : attrs;
103
- }
134
+ case "table": return attrs;
135
+ case "row": return context.revision ? {
136
+ ...attrs,
137
+ trIns: context.revision
138
+ } : attrs;
104
139
  case "cell":
105
- case "header_cell": {
106
- const attrs = withoutAttrs(node.attrs, CLEARED_CELL_ATTRS);
107
- return context.clampRowSpan ? {
108
- ...attrs,
109
- rowspan: 1
110
- } : attrs;
111
- }
112
- default: return node.isTextblock ? withoutAttrs(stripBlockIdentityAttrs(node.attrs), CLEARED_PARAGRAPH_ATTRS) : node.attrs;
140
+ case "header_cell": return context.clampRowSpan ? {
141
+ ...attrs,
142
+ rowspan: 1
143
+ } : attrs;
144
+ default: return attrs;
113
145
  }
114
146
  };
115
147
  /**
@@ -173,4 +205,4 @@ const tableRowFromTemplate = ({ template, columnCount }) => {
173
205
  });
174
206
  };
175
207
  //#endregion
176
- export { tableFromTemplate, tableRowFromTemplate };
208
+ export { tableFromTemplate, tableRowFromTemplate, tableTemplateCanCrossPackageLosslessly };