@stll/folio-core 0.35.1 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/README.md +59 -1
  2. package/dist/ai-edits/apply.js +878 -377
  3. package/dist/ai-edits/block-identity.d.ts +4 -1
  4. package/dist/ai-edits/block-identity.js +4 -1
  5. package/dist/ai-edits/blockRange.js +14 -14
  6. package/dist/ai-edits/clean-text.d.ts +40 -1
  7. package/dist/ai-edits/clean-text.js +52 -2
  8. package/dist/ai-edits/headless.d.ts +3 -1
  9. package/dist/ai-edits/headless.js +141 -73
  10. package/dist/ai-edits/index.d.ts +3 -3
  11. package/dist/ai-edits/index.js +2 -2
  12. package/dist/ai-edits/read.js +38 -20
  13. package/dist/ai-edits/snapshot.d.ts +14 -6
  14. package/dist/ai-edits/snapshot.js +105 -20
  15. package/dist/ai-edits/table-cell-mutations.js +1 -1
  16. package/dist/ai-edits/table-row-column-mutations.js +7 -0
  17. package/dist/ai-edits/table-template.d.ts +11 -1
  18. package/dist/ai-edits/table-template.js +49 -17
  19. package/dist/ai-edits/types.d.ts +62 -61
  20. package/dist/ai-edits/word-diff.d.ts +18 -3
  21. package/dist/ai-edits/word-diff.js +556 -67
  22. package/dist/ai-suggestions/conflict.js +1 -4
  23. package/dist/ai-suggestions/text-positions.d.ts +8 -0
  24. package/dist/ai-suggestions/text-positions.js +27 -5
  25. package/dist/compare/__fixtures__/body-sequence.js +3 -4
  26. package/dist/compare/column-alignment.d.ts +13 -13
  27. package/dist/compare/column-alignment.js +78 -39
  28. package/dist/compare/compare.d.ts +4 -3
  29. package/dist/compare/compare.js +48 -21
  30. package/dist/compare/content-alignment.d.ts +95 -0
  31. package/dist/compare/content-alignment.js +1450 -0
  32. package/dist/compare/content-types.d.ts +100 -0
  33. package/dist/compare/content-types.js +0 -0
  34. package/dist/compare/content.d.ts +209 -0
  35. package/dist/compare/content.js +885 -0
  36. package/dist/compare/formatting.d.ts +5 -5
  37. package/dist/compare/formatting.js +6 -4
  38. package/dist/compare/plan.d.ts +6 -1
  39. package/dist/compare/plan.js +171 -566
  40. package/dist/compare/scenario.d.ts +2 -2
  41. package/dist/compare/types.d.ts +3 -3
  42. package/dist/compare/verification.d.ts +8 -9
  43. package/dist/compare/verification.js +72 -50
  44. package/dist/compat/eigenpal.d.ts +5 -3
  45. package/dist/compat/eigenpal.js +3 -2
  46. package/dist/controller/headerFooterEditorManager.js +18 -10
  47. package/dist/controller/hiddenEditorApi.js +1 -1
  48. package/dist/controller/hiddenEditorManager.d.ts +7 -2
  49. package/dist/controller/hiddenEditorManager.js +15 -6
  50. package/dist/controller/layoutPipeline.js +5 -3
  51. package/dist/controller/noteEditorManager.js +12 -7
  52. package/dist/display-list/build/headerFooterPrimitives.js +1 -0
  53. package/dist/display-list/build/storyPrimitives.d.ts +5 -2
  54. package/dist/display-list/build/storyPrimitives.js +60 -3
  55. package/dist/display-list/build/tablePrimitives.js +30 -0
  56. package/dist/display-list/primitives.d.ts +1 -1
  57. package/dist/document-operations.d.ts +8 -4
  58. package/dist/document-operations.js +104 -12
  59. package/dist/docx/blockContentParser.js +6 -27
  60. package/dist/docx/commentParser.js +2 -4
  61. package/dist/docx/commentRangeIntegrity.js +2 -4
  62. package/dist/docx/documentClone.d.ts +2 -0
  63. package/dist/docx/documentClone.js +2 -0
  64. package/dist/docx/ensureParaIds.js +9 -4
  65. package/dist/docx/headerFooterParser.js +2 -4
  66. package/dist/docx/normalizeBaseDirection.js +5 -7
  67. package/dist/docx/numberingParser.d.ts +3 -1
  68. package/dist/docx/numberingParser.js +4 -1
  69. package/dist/docx/paragraphParser.js +34 -35
  70. package/dist/docx/paragraphPropertySource.d.ts +49 -0
  71. package/dist/docx/paragraphPropertySource.js +113 -0
  72. package/dist/docx/paragraphTraversal.js +30 -34
  73. package/dist/docx/rezip.d.ts +0 -8
  74. package/dist/docx/rezip.js +41 -16
  75. package/dist/docx/runConsolidator.js +2 -4
  76. package/dist/docx/runParser.js +6 -17
  77. package/dist/docx/sectionParser.js +1 -15
  78. package/dist/docx/serializer/paragraphSerializer.js +289 -30
  79. package/dist/docx/serializer/runSerializer.js +6 -12
  80. package/dist/docx/serializer/sectionPropertiesSerializer.js +2 -10
  81. package/dist/docx/serializer/tableSerializer.js +4 -23
  82. package/dist/docx/serializer/trackedChangeAttributes.d.ts +11 -0
  83. package/dist/docx/serializer/trackedChangeAttributes.js +31 -0
  84. package/dist/docx/server/build.d.ts +1 -1
  85. package/dist/docx/server/createBilingualDocument.js +8 -10
  86. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +22 -13
  87. package/dist/docx/server/validateDocxConformance.js +17 -13
  88. package/dist/docx/tableParser.js +1 -37
  89. package/dist/docx/trackedChangeInfo.d.ts +11 -0
  90. package/dist/docx/trackedChangeInfo.js +38 -0
  91. package/dist/docx/verbatimCapture.d.ts +20 -9
  92. package/dist/docx/verbatimCapture.js +208 -8
  93. package/dist/docx/xmlParser.d.ts +3 -1
  94. package/dist/docx/xmlParser.js +6 -1
  95. package/dist/docx/xmlSafety.d.ts +1 -1
  96. package/dist/docx/xmlSafety.js +157 -0
  97. package/dist/headless-layout.js +5 -4
  98. package/dist/index.d.ts +5 -3
  99. package/dist/index.js +3 -2
  100. package/dist/internal/headlessRevisionResolution.d.ts +23 -0
  101. package/dist/internal/headlessRevisionResolution.js +155 -0
  102. package/dist/internal/headlessRevisionResolutionGuard.d.ts +12 -0
  103. package/dist/internal/headlessRevisionResolutionGuard.js +28 -0
  104. package/dist/internal/pageBreakRunDescendantIndex.d.ts +9 -0
  105. package/dist/internal/pageBreakRunDescendantIndex.js +27 -0
  106. package/dist/internal/pageBreakRunPartition.d.ts +26 -0
  107. package/dist/internal/pageBreakRunPartition.js +35 -0
  108. package/dist/internal/pageBreakRunSourceDescendantIndex.d.ts +15 -0
  109. package/dist/internal/pageBreakRunSourceDescendantIndex.js +126 -0
  110. package/dist/internal/sectionEndpointResolution.d.ts +32 -0
  111. package/dist/internal/sectionEndpointResolution.js +38 -0
  112. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  113. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +16 -4
  114. package/dist/layout-bridge/convert/headerFooterLayout.js +52 -9
  115. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -0
  116. package/dist/layout-bridge/convert/toFlowBlocks.js +391 -82
  117. package/dist/layout-bridge/dom/clickToPositionDom.d.ts +8 -1
  118. package/dist/layout-bridge/dom/clickToPositionDom.js +114 -40
  119. package/dist/layout-bridge/dom/textStreamDom.d.ts +24 -0
  120. package/dist/layout-bridge/dom/textStreamDom.js +75 -0
  121. package/dist/layout-bridge/headerFooterLayout.js +16 -30
  122. package/dist/layout-engine/index.js +12 -3
  123. package/dist/layout-engine/measure/tableFragmentBorderGeometry.d.ts +17 -0
  124. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +24 -0
  125. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  126. package/dist/layout-engine/types.d.ts +7 -0
  127. package/dist/layout-painter/imageLayout.d.ts +1 -1
  128. package/dist/layout-painter/renderTable.js +20 -0
  129. package/dist/markdown/renderBlock.js +7 -10
  130. package/dist/model.d.ts +1 -1
  131. package/dist/paged-layout/rangeProjection.js +6 -9
  132. package/dist/paged-layout/transactionDirtyRange.d.ts +3 -1
  133. package/dist/paged-layout/transactionDirtyRange.js +23 -1
  134. package/dist/prosemirror/attrs/index.d.ts +7 -3
  135. package/dist/prosemirror/attrs/index.js +128 -7
  136. package/dist/prosemirror/bookmarkBoundaryIntegrity.d.ts +9 -0
  137. package/dist/prosemirror/bookmarkBoundaryIntegrity.js +22 -0
  138. package/dist/prosemirror/commands/comments.d.ts +10 -1
  139. package/dist/prosemirror/commands/comments.js +551 -87
  140. package/dist/prosemirror/commands/formatPainter.d.ts +45 -10
  141. package/dist/prosemirror/commands/formatPainter.js +319 -63
  142. package/dist/prosemirror/commands/index.d.ts +2 -2
  143. package/dist/prosemirror/commands/pageBreak.d.ts +5 -2
  144. package/dist/prosemirror/commands/pageBreak.js +18 -36
  145. package/dist/prosemirror/commands/propertyChangeScope.d.ts +27 -3
  146. package/dist/prosemirror/commands/propertyChangeScope.js +85 -14
  147. package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -5
  148. package/dist/prosemirror/conversion/fromProseDoc.js +935 -223
  149. package/dist/prosemirror/conversion/runShadingMark.js +2 -2
  150. package/dist/prosemirror/conversion/toProseDoc.d.ts +26 -1
  151. package/dist/prosemirror/conversion/toProseDoc.js +439 -291
  152. package/dist/prosemirror/extensions/StarterKit.js +4 -0
  153. package/dist/prosemirror/extensions/core/ParagraphExtension.js +54 -6
  154. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +7 -1
  155. package/dist/prosemirror/extensions/features/ListExtension.js +95 -123
  156. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +19 -6
  157. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +27 -1
  158. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +134 -30
  159. package/dist/prosemirror/extensions/features/pasteCleanup.js +9 -15
  160. package/dist/prosemirror/extensions/marks/BoldExtension.js +4 -3
  161. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.d.ts +4 -7
  162. package/dist/prosemirror/extensions/marks/CharacterStyleExtension.js +5 -11
  163. package/dist/prosemirror/extensions/marks/FontSizeExtension.js +3 -3
  164. package/dist/prosemirror/extensions/marks/ItalicExtension.js +4 -3
  165. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.d.ts +13 -0
  166. package/dist/prosemirror/extensions/marks/PageBreakRunOwnerExtension.js +39 -0
  167. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +7 -1
  168. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +67 -21
  169. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  170. package/dist/prosemirror/extensions/marks/markUtils.d.ts +8 -1
  171. package/dist/prosemirror/extensions/marks/markUtils.js +88 -2
  172. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +44 -3
  173. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  174. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +15 -4
  175. package/dist/prosemirror/extensions/nodes/PageBreakExtension.d.ts +6 -0
  176. package/dist/prosemirror/extensions/nodes/PageBreakExtension.js +6 -0
  177. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.d.ts +6 -0
  178. package/dist/prosemirror/extensions/nodes/PageBreakRunExtension.js +36 -0
  179. package/dist/prosemirror/findReplaceSelection.js +8 -3
  180. package/dist/prosemirror/index.d.ts +3 -3
  181. package/dist/prosemirror/listMarker.d.ts +26 -1
  182. package/dist/prosemirror/listMarker.js +108 -14
  183. package/dist/prosemirror/pageBreakRunProjection.d.ts +28 -0
  184. package/dist/prosemirror/pageBreakRunProjection.js +47 -0
  185. package/dist/prosemirror/paraText.js +6 -0
  186. package/dist/prosemirror/paragraphAlignment.d.ts +12 -0
  187. package/dist/prosemirror/paragraphAlignment.js +13 -0
  188. package/dist/prosemirror/paragraphSpacing.d.ts +29 -0
  189. package/dist/prosemirror/paragraphSpacing.js +125 -0
  190. package/dist/prosemirror/plugins/documentNumbering.d.ts +8 -0
  191. package/dist/prosemirror/plugins/documentNumbering.js +18 -0
  192. package/dist/prosemirror/plugins/pmTextScan.d.ts +3 -2
  193. package/dist/prosemirror/plugins/pmTextScan.js +14 -4
  194. package/dist/prosemirror/plugins/suggestionMode.js +9 -11
  195. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +16 -0
  196. package/dist/prosemirror/rebaseParagraphRunFormatting.js +61 -0
  197. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +64 -0
  198. package/dist/prosemirror/runFormattingInlineCarriers.js +121 -0
  199. package/dist/prosemirror/runFormattingProvenance.d.ts +12 -0
  200. package/dist/prosemirror/runFormattingProvenance.js +48 -0
  201. package/dist/prosemirror/runFormattingReconciliation.d.ts +25 -0
  202. package/dist/prosemirror/runFormattingReconciliation.js +54 -0
  203. package/dist/prosemirror/runPropertyChangeResolution.d.ts +18 -0
  204. package/dist/prosemirror/runPropertyChangeResolution.js +18 -0
  205. package/dist/prosemirror/runStyleFormatting.d.ts +50 -0
  206. package/dist/prosemirror/runStyleFormatting.js +105 -0
  207. package/dist/prosemirror/schema/index.d.ts +3 -3
  208. package/dist/prosemirror/schema/marks.d.ts +84 -9
  209. package/dist/prosemirror/schema/marks.js +73 -1
  210. package/dist/prosemirror/schema/nodes.d.ts +30 -6
  211. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +14 -2
  212. package/dist/prosemirror/styles/resolvedStyleAttrs.js +45 -23
  213. package/dist/prosemirror/styles/styleResolver.d.ts +10 -13
  214. package/dist/prosemirror/styles/styleResolver.js +3 -3
  215. package/dist/prosemirror/styles/styleToggleCascade.d.ts +8 -1
  216. package/dist/prosemirror/styles/styleToggleCascade.js +24 -1
  217. package/dist/prosemirror/tableGridMutation.d.ts +20 -0
  218. package/dist/prosemirror/tableGridMutation.js +30 -0
  219. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +31 -0
  220. package/dist/prosemirror/trackedRunInlineAtoms.js +39 -0
  221. package/dist/prosemirror/utils/visualLineNavigation.js +27 -31
  222. package/dist/prosemirror/validation.js +23 -4
  223. package/dist/redline.js +4 -1
  224. package/dist/render-dom/HeaderFooterSelectionOverlay.js +20 -20
  225. package/dist/render-dom/RenderedDomContext.js +17 -34
  226. package/dist/server.d.ts +4 -4
  227. package/dist/server.js +2 -2
  228. package/dist/style-engine/styleEngine.d.ts +1 -1
  229. package/dist/utils/clipboard.js +1 -1
  230. package/dist/utils/findReplace.js +38 -21
  231. package/dist/utils/mergeDocumentContent.js +3 -3
  232. package/dist/utils/replaceText.js +2 -1
  233. package/dist/version-comparison.d.ts +38 -44
  234. package/dist/version-comparison.js +270 -399
  235. package/dist/watermark/index.js +2 -4
  236. package/package.json +8 -2
@@ -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, 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_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, 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_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,4 +1,9 @@
1
- import { expectRunFormattingOverrideMarkAttrs } from "../prosemirror/attrs/index.js";
1
+ import { expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs } from "../prosemirror/attrs/index.js";
2
+ import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
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";
2
7
  import { deriveBlankBlockId, deriveBlockId } from "../types/block-id.js";
3
8
  import { buildCleanBlockText } from "./clean-text.js";
4
9
  import { panic } from "better-result";
@@ -34,10 +39,10 @@ const isFolioAIContentBlock = ({ text }) => normalizeFolioAIBlockText(text).leng
34
39
  * container-edge rule then applies and the addition cannot be rejected
35
40
  * cleanly.
36
41
  *
37
- * A body always ends with a paragraph — a table may not be the last child of a
38
- * body or a cell, so a package that ends in a table carries a trailing, often
39
- * empty, paragraph after it so this is only `null` for a story with no
40
- * 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.
41
46
  */
42
47
  const trailingBodyBlockId = ({ blocks }) => {
43
48
  for (let index = blocks.length - 1; index >= 0; index--) {
@@ -51,6 +56,24 @@ const hashFolioAIBlockText = (text) => {
51
56
  for (const character of text) hash = (hash * 33 + (character.codePointAt(0) ?? 0)) % 2147483647;
52
57
  return `h${hash.toString(36)}`;
53
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));
54
77
  const createFolioAITextRangeHandle = ({ blockId, text, startOffset, endOffset }) => {
55
78
  if (blockId.length === 0 || !Number.isInteger(startOffset) || !Number.isInteger(endOffset) || startOffset < 0 || endOffset <= startOffset || endOffset > text.length) return null;
56
79
  return {
@@ -126,7 +149,7 @@ const getTableLocation = ({ path, blockIndex, tableIndexByStart }) => {
126
149
  };
127
150
  }
128
151
  };
129
- const createFolioAIEditSnapshot = (doc) => {
152
+ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
130
153
  const draftBlocks = [];
131
154
  const hashCounts = /* @__PURE__ */ new Map();
132
155
  const usedBlockIds = /* @__PURE__ */ new Set();
@@ -147,12 +170,16 @@ const createFolioAIEditSnapshot = (doc) => {
147
170
  });
148
171
  return true;
149
172
  }
150
- 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);
151
177
  const normalizedText = normalizeFolioAIBlockText(text);
152
178
  const textHash = hashFolioAIBlockText(normalizedText);
153
179
  hashCounts.set(textHash, (hashCounts.get(textHash) ?? 0) + 1);
154
180
  const paraIdAttr = node.attrs["paraId"];
155
181
  const paraId = typeof paraIdAttr === "string" && paraIdAttr.length > 0 ? paraIdAttr : null;
182
+ const idStability = node.attrs["idStability"] === "positional" ? "positional" : void 0;
156
183
  const isBlank = normalizedText.length === 0;
157
184
  let id;
158
185
  if (isBlank) {
@@ -178,7 +205,9 @@ const createFolioAIEditSnapshot = (doc) => {
178
205
  const listLevel = getListLevel(node);
179
206
  const numberingReferenceKey = getNumberingReferenceKey(node);
180
207
  if (numberingReferenceKey) numberingReferenceKeys.add(numberingReferenceKey);
181
- const previewRuns = getPreviewRuns(node);
208
+ const directAlignment = getDirectAlignment(node);
209
+ const directSpacing = getDirectSpacing(node);
210
+ const previewRuns = getPreviewRuns(node, styleResolver);
182
211
  const table = getTableLocation({
183
212
  path,
184
213
  blockIndex: index,
@@ -189,11 +218,15 @@ const createFolioAIEditSnapshot = (doc) => {
189
218
  id,
190
219
  kind,
191
220
  text,
221
+ ...idStability !== void 0 && { idStability },
192
222
  ...headingLevel !== void 0 && { headingLevel },
193
223
  ...displayLabel !== void 0 && { displayLabel },
194
224
  ...styleId !== void 0 && { styleId },
195
225
  ...listLevel !== void 0 && { listLevel },
226
+ ...directAlignment !== void 0 && { directAlignment },
227
+ ...directSpacing !== void 0 && { directSpacing },
196
228
  ...previewRuns !== void 0 && { previewRuns },
229
+ ...structuralBoundaries.length > 0 && { structuralBoundaries },
197
230
  ...table !== void 0 && { table }
198
231
  },
199
232
  anchor: {
@@ -202,7 +235,8 @@ const createFolioAIEditSnapshot = (doc) => {
202
235
  to: pos + node.nodeSize,
203
236
  text,
204
237
  normalizedText,
205
- textHash
238
+ textHash,
239
+ structuralBoundaryHash
206
240
  }
207
241
  });
208
242
  return true;
@@ -223,6 +257,9 @@ const createFolioAIEditSnapshot = (doc) => {
223
257
  numberingReferenceKeysBySnapshot.set(snapshot, [...numberingReferenceKeys]);
224
258
  return snapshot;
225
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);
226
263
  const getBlockKind = (node, headingLevel) => {
227
264
  const listMarker = node.attrs["listMarker"];
228
265
  const numPr = node.attrs["numPr"];
@@ -263,15 +300,36 @@ const getStyleId = (node) => {
263
300
  const styleId = node.attrs["styleId"];
264
301
  return typeof styleId === "string" && styleId.length > 0 ? styleId : void 0;
265
302
  };
303
+ /** Read only authored `w:jc`, never the effective alignment resolved from a style. */
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));
266
307
  const DELETION_MARK = "deletion";
267
- 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) => {
268
312
  const runs = [];
269
313
  const defaultStyle = getDefaultPreviewRunStyle(node);
314
+ let paragraphStyleContext;
270
315
  node.descendants((child) => {
271
316
  if (!child.isText || child.text === void 0) return true;
272
- 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;
273
318
  const style = getPreviewRunStyle(child.marks, defaultStyle);
274
- 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
+ }
275
333
  const previous = runs.at(-1);
276
334
  if (previous && samePreviewRunStyle(previous, style) && sameDirectFormatting(previous.directFormatting, directFormatting)) {
277
335
  previous.text += child.text;
@@ -329,23 +387,50 @@ const getPreviewRunStyle = (marks, defaultStyle) => {
329
387
  }
330
388
  default: break;
331
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;
332
399
  return style;
333
400
  };
334
- const getDirectPreviewRunStyle = (marks, inheritedStyle) => {
401
+ const getDirectPreviewRunStyleFromMarks = (marks, inheritedStyle) => {
335
402
  const markedStyle = getPreviewRunStyle(marks, {});
336
403
  const directStyle = {};
337
- const overrideMark = marks.find(({ type }) => type.name === "runFormattingOverride");
338
- const hasFormattingProvenance = overrideMark !== void 0 || marks.some(({ type }) => type.name === "characterStyle");
339
- const directFontProperties = overrideMark ? expectRunFormattingOverrideMarkAttrs(overrideMark).directFontProperties : void 0;
340
404
  for (const property of [
341
405
  "bold",
342
406
  "italic",
343
407
  "underline",
344
408
  "strike"
345
409
  ]) if (Boolean(markedStyle[property]) !== Boolean(inheritedStyle[property])) directStyle[property] = Boolean(markedStyle[property]);
346
- if (markedStyle.fontFamily !== void 0 && (hasFormattingProvenance ? directFontProperties?.includes("fontFamily") : markedStyle.fontFamily !== inheritedStyle.fontFamily)) directStyle.fontFamily = markedStyle.fontFamily;
347
- if (markedStyle.fontSizePt !== void 0 && (hasFormattingProvenance ? directFontProperties?.includes("fontSize") : markedStyle.fontSizePt !== inheritedStyle.fontSizePt)) directStyle.fontSizePt = markedStyle.fontSizePt;
348
- 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;
349
434
  return directStyle;
350
435
  };
351
436
  const getBooleanTextFormatting = (formatting) => ({
@@ -394,4 +479,4 @@ const isEmptyPreviewRunStyle = ({ bold, italic, underline, strike, fontFamily, f
394
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;
395
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;
396
481
  //#endregion
397
- 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 };