@stll/folio-core 0.10.0 → 0.12.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 (217) hide show
  1. package/dist/ai-edits/apply.js +602 -50
  2. package/dist/ai-edits/block-identity.d.ts +4 -0
  3. package/dist/ai-edits/block-identity.js +9 -0
  4. package/dist/ai-edits/blockRange.d.ts +28 -1
  5. package/dist/ai-edits/blockRange.js +67 -1
  6. package/dist/ai-edits/headless.d.ts +4 -4
  7. package/dist/ai-edits/headless.js +9 -9
  8. package/dist/ai-edits/index.d.ts +2 -2
  9. package/dist/ai-edits/index.js +2 -2
  10. package/dist/ai-edits/read.d.ts +7 -7
  11. package/dist/ai-edits/read.js +110 -3
  12. package/dist/ai-edits/table-cell-mutations.d.ts +48 -0
  13. package/dist/ai-edits/table-cell-mutations.js +237 -0
  14. package/dist/ai-edits/table-mutation-plan.d.ts +38 -0
  15. package/dist/ai-edits/table-mutation-plan.js +90 -0
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +100 -0
  17. package/dist/ai-edits/table-row-column-mutations.js +300 -0
  18. package/dist/ai-edits/table-targets.d.ts +32 -0
  19. package/dist/ai-edits/table-targets.js +70 -0
  20. package/dist/ai-edits/types.d.ts +34 -0
  21. package/dist/compat/eigenpal.js +1 -1
  22. package/dist/content-controls/mutateContentControls.js +1 -1
  23. package/dist/controller/activeEditorStory.d.ts +36 -0
  24. package/dist/controller/activeEditorStory.js +30 -0
  25. package/dist/controller/collaborationModules.d.ts +7 -0
  26. package/dist/controller/collaborationModules.js +15 -0
  27. package/dist/controller/contentControlWidgetController.d.ts +55 -0
  28. package/dist/controller/contentControlWidgetController.js +141 -0
  29. package/dist/controller/folioEditor.d.ts +1 -1
  30. package/dist/controller/fontReadiness.js +2 -0
  31. package/dist/controller/headerFooterEditorManager.d.ts +41 -0
  32. package/dist/controller/headerFooterEditorManager.js +182 -0
  33. package/dist/controller/hiddenEditorApi.js +2 -2
  34. package/dist/controller/hiddenEditorManager.d.ts +13 -2
  35. package/dist/controller/hiddenEditorManager.js +32 -11
  36. package/dist/controller/layoutPipeline.d.ts +2 -0
  37. package/dist/controller/layoutPipeline.js +16 -1
  38. package/dist/controller/noteEditorManager.d.ts +36 -0
  39. package/dist/controller/noteEditorManager.js +210 -0
  40. package/dist/document-operations.d.ts +35 -3
  41. package/dist/document-operations.js +161 -3
  42. package/dist/docx/blockContentParser.js +2 -2
  43. package/dist/docx/commentParser.js +6 -1
  44. package/dist/docx/conformance.js +8 -1
  45. package/dist/docx/documentGrid.d.ts +8 -0
  46. package/dist/docx/documentGrid.js +8 -0
  47. package/dist/docx/drawingUtils.js +1 -1
  48. package/dist/docx/ensureParaIds.js +1 -1
  49. package/dist/docx/fieldParser.js +1 -1
  50. package/dist/docx/notePropertiesParser.js +1 -1
  51. package/dist/docx/numberingParser.js +1 -1
  52. package/dist/docx/paragraphParser.js +63 -58
  53. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  54. package/dist/docx/paragraphTextBoxEnrichment.js +67 -13
  55. package/dist/docx/paragraphTraversal.js +1 -1
  56. package/dist/docx/parser.js +3 -3
  57. package/dist/docx/rezip.js +1 -1
  58. package/dist/docx/runParser.js +4 -2
  59. package/dist/docx/sdtProperties.js +1 -1
  60. package/dist/docx/sectionParser.js +1 -1
  61. package/dist/docx/selectiveSave.js +1 -1
  62. package/dist/docx/serializer/commentSerializer.js +1 -2
  63. package/dist/docx/serializer/documentSerializer.js +2 -2
  64. package/dist/docx/serializer/headerFooterSerializer.js +2 -2
  65. package/dist/docx/serializer/noteSerializer.js +2 -2
  66. package/dist/docx/serializer/paragraphSerializer.js +7 -1
  67. package/dist/docx/serializer/runSerializer.js +4 -2
  68. package/dist/docx/serializer/stylesSerializer.js +1 -1
  69. package/dist/docx/serializer/tableSerializer.d.ts +5 -4
  70. package/dist/docx/serializer/tableSerializer.js +17 -13
  71. package/dist/docx/server/applyDocxXmlPatchProposal.d.ts +87 -0
  72. package/dist/docx/server/applyDocxXmlPatchProposal.js +204 -0
  73. package/dist/docx/server/boundedArchive.d.ts +14 -3
  74. package/dist/docx/server/boundedArchive.js +56 -11
  75. package/dist/docx/server/evaluateDocxXmlPatchProposal.d.ts +85 -0
  76. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +361 -0
  77. package/dist/docx/server/inspectDocxPackage.d.ts +70 -0
  78. package/dist/docx/server/inspectDocxPackage.js +217 -0
  79. package/dist/docx/server/validateDocxConformance.d.ts +41 -0
  80. package/dist/docx/server/validateDocxConformance.js +336 -0
  81. package/dist/docx/settingsParser.js +11 -1
  82. package/dist/docx/shapeParser.js +1 -1
  83. package/dist/docx/styleParser.js +4 -2
  84. package/dist/docx/tableParser.js +18 -7
  85. package/dist/docx/textBoxParser.d.ts +3 -3
  86. package/dist/docx/textBoxParser.js +16 -12
  87. package/dist/docx/xmlSafety.d.ts +5 -0
  88. package/dist/docx/xmlSafety.js +9 -0
  89. package/dist/fields/resolveFieldValues.js +1 -3
  90. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  91. package/dist/i18n/messages/catalogs.gen.js +17 -0
  92. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  93. package/dist/index.js +1 -1
  94. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  95. package/dist/layout-bridge/convert/footnoteLayout.js +47 -40
  96. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  97. package/dist/layout-bridge/convert/headerFooterLayout.js +3 -0
  98. package/dist/layout-bridge/convert/templatePreviewFlow.js +3 -3
  99. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +4 -2
  100. package/dist/layout-bridge/convert/toFlowBlocks.js +136 -20
  101. package/dist/layout-bridge/dom/noteStoryDom.d.ts +6 -0
  102. package/dist/layout-bridge/dom/noteStoryDom.js +17 -0
  103. package/dist/layout-bridge/engine/clickToPosition.js +2 -2
  104. package/dist/layout-engine/index.d.ts +2 -2
  105. package/dist/layout-engine/index.js +43 -28
  106. package/dist/layout-engine/justificationCompatibility.d.ts +6 -0
  107. package/dist/layout-engine/justificationCompatibility.js +8 -0
  108. package/dist/layout-engine/keep-together.js +11 -1
  109. package/dist/layout-engine/measure/cache.js +21 -5
  110. package/dist/layout-engine/measure/lineBreakProvider.js +53 -34
  111. package/dist/layout-engine/measure/measureBlocks.js +6 -3
  112. package/dist/layout-engine/measure/measureContainer.js +1 -1
  113. package/dist/layout-engine/measure/measureHelpers.d.ts +1 -1
  114. package/dist/layout-engine/measure/measureHelpers.js +3 -3
  115. package/dist/layout-engine/measure/measureParagraph.js +69 -25
  116. package/dist/layout-engine/measure/measureProvider.js +4 -3
  117. package/dist/layout-engine/measure/tableCellFlow.js +5 -2
  118. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +9 -7
  119. package/dist/layout-engine/measure/textBoxParagraphLayout.js +22 -6
  120. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +3 -1
  121. package/dist/layout-engine/measure/textMeasurementPolicy.js +7 -1
  122. package/dist/layout-engine/paginator.js +5 -1
  123. package/dist/layout-engine/paragraphSpacing.d.ts +28 -4
  124. package/dist/layout-engine/paragraphSpacing.js +107 -13
  125. package/dist/layout-engine/renderedBreakReconciliation.js +1 -1
  126. package/dist/layout-engine/types.d.ts +26 -13
  127. package/dist/layout-painter/registry/modules/textBox.js +5 -1
  128. package/dist/layout-painter/renderPage.d.ts +5 -2
  129. package/dist/layout-painter/renderPage.js +56 -12
  130. package/dist/layout-painter/renderParagraph.js +22 -6
  131. package/dist/layout-painter/renderTable.d.ts +5 -1
  132. package/dist/layout-painter/renderTable.js +5 -2
  133. package/dist/layout-painter/renderTextBox.d.ts +2 -1
  134. package/dist/layout-painter/renderTextBox.js +25 -14
  135. package/dist/managers/DocumentLoaderManager.js +1 -1
  136. package/dist/managers/FindReplaceManager.d.ts +8 -0
  137. package/dist/managers/FindReplaceManager.js +18 -0
  138. package/dist/managers/TableSelectionManager.d.ts +6 -0
  139. package/dist/managers/TableSelectionManager.js +14 -0
  140. package/dist/model.js +1 -1
  141. package/dist/prosemirror/attrs/index.d.ts +4 -2
  142. package/dist/prosemirror/attrs/index.js +154 -18
  143. package/dist/prosemirror/commands/comments.d.ts +3 -3
  144. package/dist/prosemirror/commands/comments.js +248 -12
  145. package/dist/prosemirror/commands/contentControls.js +1 -1
  146. package/dist/prosemirror/commands/index.js +1 -1
  147. package/dist/prosemirror/commands/pageBreak.js +1 -1
  148. package/dist/prosemirror/commands/propertyChangeScope.d.ts +2 -2
  149. package/dist/prosemirror/commands/propertyChangeScope.js +3 -2
  150. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +9 -0
  151. package/dist/prosemirror/commands/tableCellMergeResolution.js +181 -0
  152. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -1
  153. package/dist/prosemirror/conversion/fromProseDoc.js +286 -61
  154. package/dist/prosemirror/conversion/sdtAttrs.d.ts +9 -0
  155. package/dist/prosemirror/conversion/sdtAttrs.js +49 -0
  156. package/dist/prosemirror/conversion/toProseDoc.d.ts +2 -1
  157. package/dist/prosemirror/conversion/toProseDoc.js +347 -98
  158. package/dist/prosemirror/extensions/ExtensionManager.js +1 -1
  159. package/dist/prosemirror/extensions/StarterKit.js +6 -1
  160. package/dist/prosemirror/extensions/core/ParagraphExtension.js +4 -1
  161. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +1 -0
  162. package/dist/prosemirror/extensions/features/ListExtension.js +1 -1
  163. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +10 -1
  164. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +93 -2
  165. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +2 -1
  166. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +6 -2
  167. package/dist/prosemirror/extensions/marks/CharacterSpacingExtension.js +1 -1
  168. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.d.ts +9 -1
  169. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +24 -1
  170. package/dist/prosemirror/extensions/nodes/FieldExtension.js +1 -1
  171. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -0
  172. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +22 -0
  173. package/dist/prosemirror/extensions/nodes/TableExtension.js +9 -3
  174. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +13 -0
  175. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +41 -0
  176. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +5 -2
  177. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +4 -1
  178. package/dist/prosemirror/findReplaceSelection.d.ts +6 -1
  179. package/dist/prosemirror/findReplaceSelection.js +57 -13
  180. package/dist/prosemirror/plugins/contentControlWidgets.d.ts +34 -8
  181. package/dist/prosemirror/plugins/contentControlWidgets.js +125 -91
  182. package/dist/prosemirror/plugins/selectionTracker.js +1 -0
  183. package/dist/prosemirror/plugins/suggestionMode.js +1 -1
  184. package/dist/prosemirror/schema/index.d.ts +2 -2
  185. package/dist/prosemirror/schema/marks.d.ts +5 -1
  186. package/dist/prosemirror/schema/nodes.d.ts +64 -7
  187. package/dist/prosemirror/selectionState.js +1 -0
  188. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  189. package/dist/prosemirror/tableCellMergeRevision.d.ts +9 -0
  190. package/dist/prosemirror/tableCellMergeRevision.js +22 -0
  191. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +9 -0
  192. package/dist/prosemirror/textBoxAnchorAttrs.js +33 -0
  193. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +1 -1
  194. package/dist/prosemirror/utils/extractTrackedChanges.js +62 -14
  195. package/dist/prosemirror/validation.js +9 -1
  196. package/dist/render-dom/BodySelectionOverlay.d.ts +31 -0
  197. package/dist/render-dom/BodySelectionOverlay.js +76 -0
  198. package/dist/render-dom/HeaderFooterSelectionOverlay.d.ts +28 -0
  199. package/dist/render-dom/HeaderFooterSelectionOverlay.js +121 -0
  200. package/dist/render-dom/RemoteSelectionOverlay.d.ts +30 -0
  201. package/dist/render-dom/RemoteSelectionOverlay.js +67 -0
  202. package/dist/render-dom/RenderedDomContext.d.ts +40 -0
  203. package/dist/render-dom/RenderedDomContext.js +126 -0
  204. package/dist/render-dom/resolveSidebarItemPositions.d.ts +33 -0
  205. package/dist/render-dom/resolveSidebarItemPositions.js +45 -0
  206. package/dist/server.d.ts +6 -2
  207. package/dist/server.js +5 -1
  208. package/dist/utils/findReplace.d.ts +32 -0
  209. package/dist/utils/findReplace.js +118 -0
  210. package/dist/utils/fontFamilyMerge.js +9 -11
  211. package/dist/utils/fontLoader.js +3 -2
  212. package/dist/utils/fontResolver.js +22 -4
  213. package/dist/utils/fontWeights.d.ts +1 -1
  214. package/dist/utils/fontWeights.js +1 -1
  215. package/dist/utils/headerFooter.js +24 -1
  216. package/dist/utils/paragraphFormattingMerge.js +1 -0
  217. package/package.json +1 -1
@@ -0,0 +1,4 @@
1
+ //#region src/ai-edits/block-identity.d.ts
2
+ declare const stripBlockIdentityAttrs: (attrs: Record<string, unknown>) => Record<string, unknown>;
3
+ //#endregion
4
+ export { stripBlockIdentityAttrs };
@@ -0,0 +1,9 @@
1
+ //#region src/ai-edits/block-identity.ts
2
+ const IDENTITY_BLOCK_ATTRS = /* @__PURE__ */ new Set(["paraId", "textId"]);
3
+ const stripBlockIdentityAttrs = (attrs) => {
4
+ const next = {};
5
+ for (const [key, value] of Object.entries(attrs)) next[key] = IDENTITY_BLOCK_ATTRS.has(key) ? null : value;
6
+ return next;
7
+ };
8
+ //#endregion
9
+ export { stripBlockIdentityAttrs };
@@ -27,6 +27,33 @@ declare const resolveFolioAITextRange: ({
27
27
  doc,
28
28
  snapshot
29
29
  }: ResolveFolioAITextRangeOptions) => DocPositionRange | null;
30
+ type ResolvePassageRangeOptions = {
31
+ blockId: string;
32
+ text: string;
33
+ doc: Node;
34
+ snapshot?: FolioAIEditSnapshot | null | undefined;
35
+ };
36
+ /**
37
+ * Resolve a free-text passage inside a block to a `{ from, to }` PM range.
38
+ *
39
+ * First resolves the block the same way {@link resolveFolioAIBlockRange} does
40
+ * (live paraId lookup, then snapshot/seq fallback). Then matches `text` against
41
+ * the block's post-tracked-changes clean text (so the offsets it finds still
42
+ * map back to live PM positions on a doc with pending redlines) and maps the
43
+ * match back through the clean-text `offsets`.
44
+ *
45
+ * Matching normalizes whitespace runs in the needle to `\s+` (typography inserts
46
+ * non-breaking / narrow spaces that the caller's quoted text won't reproduce
47
+ * exactly) and tries case-sensitive first, then case-insensitive; the first
48
+ * match wins. Returns null when the block does not resolve, is not a textblock,
49
+ * the needle is empty, or the text does not occur in the block.
50
+ */
51
+ declare const resolvePassageRange: ({
52
+ blockId,
53
+ text,
54
+ doc,
55
+ snapshot
56
+ }: ResolvePassageRangeOptions) => DocPositionRange | null;
30
57
  /**
31
58
  * Clamp a `{from, to}` pair so both endpoints fit inside a document of
32
59
  * `docSize` (in PM content positions). Block-boundary snapshots and stale
@@ -41,4 +68,4 @@ declare const resolveFolioAITextRange: ({
41
68
  */
42
69
  declare function clampRangeToDocSize(docSize: number, range: DocPositionRange): DocPositionRange;
43
70
  //#endregion
44
- export { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange };
71
+ export { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -38,6 +38,72 @@ const resolveFolioAITextRange = ({ range, doc, snapshot }) => {
38
38
  });
39
39
  };
40
40
  /**
41
+ * Resolve a free-text passage inside a block to a `{ from, to }` PM range.
42
+ *
43
+ * First resolves the block the same way {@link resolveFolioAIBlockRange} does
44
+ * (live paraId lookup, then snapshot/seq fallback). Then matches `text` against
45
+ * the block's post-tracked-changes clean text (so the offsets it finds still
46
+ * map back to live PM positions on a doc with pending redlines) and maps the
47
+ * match back through the clean-text `offsets`.
48
+ *
49
+ * Matching normalizes whitespace runs in the needle to `\s+` (typography inserts
50
+ * non-breaking / narrow spaces that the caller's quoted text won't reproduce
51
+ * exactly) and tries case-sensitive first, then case-insensitive; the first
52
+ * match wins. Returns null when the block does not resolve, is not a textblock,
53
+ * the needle is empty, or the text does not occur in the block.
54
+ */
55
+ const resolvePassageRange = ({ blockId, text, doc, snapshot }) => {
56
+ const blockRange = resolveFolioAIBlockRange({
57
+ blockId,
58
+ doc,
59
+ snapshot
60
+ });
61
+ if (blockRange === null) return null;
62
+ const blockNode = doc.nodeAt(blockRange.from);
63
+ if (!blockNode?.isTextblock) return null;
64
+ const needleSource = buildPassageNeedleSource(text);
65
+ if (needleSource === null) return null;
66
+ const cleanBlock = buildCleanBlockText(blockNode, blockRange.from);
67
+ const match = matchPassage(cleanBlock.text, needleSource);
68
+ if (match === null) return null;
69
+ const from = cleanBlock.offsets[match.start];
70
+ const to = cleanBlock.offsets[match.end];
71
+ if (from === void 0 || to === void 0) return null;
72
+ return clampRangeToDocSize(doc.content.size, {
73
+ from,
74
+ to
75
+ });
76
+ };
77
+ /**
78
+ * Turn a caller-supplied passage into a regex source that tolerates whitespace
79
+ * variance. The needle is trimmed first (quoted passages routinely carry
80
+ * accidental leading/trailing whitespace that the block text won't reproduce),
81
+ * then regex metacharacters are escaped so the literal text matches, then
82
+ * whitespace runs collapse to `\s+` (mirrors the anonymization matcher).
83
+ * Returns null for empty / whitespace-only input so an empty needle can never
84
+ * match at offset 0 and paint a zero-width highlight.
85
+ */
86
+ const buildPassageNeedleSource = (text) => {
87
+ const trimmed = text.trim();
88
+ if (trimmed.length === 0) return null;
89
+ return trimmed.replaceAll(/[\\^$.*+?()[\]{}|]/gu, "\\$&").replaceAll(/\s+/gu, "\\s+");
90
+ };
91
+ /**
92
+ * Case-sensitive match first, then case-insensitive fallback. The first hit in
93
+ * each pass wins; `index` and length are clean-text offsets the caller maps back
94
+ * through `offsets`.
95
+ */
96
+ const matchPassage = (haystack, needleSource) => {
97
+ for (const flags of ["u", "iu"]) {
98
+ const match = new RegExp(needleSource, flags).exec(haystack);
99
+ if (match !== null) return {
100
+ start: match.index,
101
+ end: match.index + match[0].length
102
+ };
103
+ }
104
+ return null;
105
+ };
106
+ /**
41
107
  * Resolve a `seq-NNNN` fallback id by document position.
42
108
  *
43
109
  * A sequential id is only minted for a paragraph the source DOCX left
@@ -76,4 +142,4 @@ function clampRangeToDocSize(docSize, range) {
76
142
  };
77
143
  }
78
144
  //#endregion
79
- export { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange };
145
+ export { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -222,10 +222,10 @@ declare class FolioDocxReviewer {
222
222
  /** Read one discovered story; returns null when its handle is no longer present. */
223
223
  readStory(handle: FolioDocumentStoryHandle): FolioDocumentStory | null;
224
224
  /**
225
- * The tracked changes (insertions and deletions) present in the body, read
226
- * from the same `insertion` / `deletion` marks the editor renders. Each
227
- * carries the revision id {@link acceptChange} / {@link rejectChange} resolve
228
- * against. Runs of one revision within a block fold into a single entry.
225
+ * The tracked changes present in the body, including inline and structural
226
+ * revisions. Each carries the revision id {@link acceptChange} /
227
+ * {@link rejectChange} resolve against. Related revision sites fold into one
228
+ * entry where the document model identifies them as one authored change.
229
229
  */
230
230
  getChanges(filter?: FolioReviewChangeFilter): FolioReviewChange[];
231
231
  /**
@@ -1,19 +1,19 @@
1
+ import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
1
2
  import { buildAnnotatedBlockText } from "./clean-text.js";
2
3
  import { createFolioAIEditSnapshot, normalizeFolioAIBlockText } from "./snapshot.js";
4
+ import { getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
5
+ import { deterministicHexId } from "../utils/hexId.js";
6
+ import { schema, singletonManager } from "../prosemirror/schema/index.js";
7
+ import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
3
8
  import { applyFolioDocumentOperations } from "../document-operations.js";
4
9
  import { getEndnoteText, getFootnoteText, isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
5
10
  import { getHeaderFooterText } from "../docx/headerFooterParser.js";
6
11
  import { parseDocx } from "../docx/parser.js";
7
- import { deterministicHexId } from "../utils/hexId.js";
8
12
  import { repackDocx } from "../docx/rezip.js";
9
13
  import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
10
14
  import { createReply } from "../docx/replyToComment.js";
11
15
  import { attemptSelectiveSave } from "../docx/selectiveSave.js";
12
16
  import { acceptAIEditRevision, acceptAllChanges, rejectAIEditRevision, rejectAllChanges } from "../prosemirror/commands/comments.js";
13
- import { proseDocToBlocks, updateDocumentContent } from "../prosemirror/conversion/fromProseDoc.js";
14
- import { getChangedParagraphIds, hasStructuralChanges, hasUntrackedChanges, ignoreTrackedChanges } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
15
- import { schema, singletonManager } from "../prosemirror/schema/index.js";
16
- import { footnoteToProseDoc, headerFooterToProseDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
17
17
  import { TaggedError } from "better-result";
18
18
  import { EditorState } from "prosemirror-state";
19
19
  //#region src/ai-edits/headless.ts
@@ -522,10 +522,10 @@ var FolioDocxReviewer = class FolioDocxReviewer {
522
522
  return stories.find(({ handle: candidate }) => (candidate.type === "footnote" || candidate.type === "endnote") && candidate.type === handle.type && candidate.noteId === handle.noteId) ?? null;
523
523
  }
524
524
  /**
525
- * The tracked changes (insertions and deletions) present in the body, read
526
- * from the same `insertion` / `deletion` marks the editor renders. Each
527
- * carries the revision id {@link acceptChange} / {@link rejectChange} resolve
528
- * against. Runs of one revision within a block fold into a single entry.
525
+ * The tracked changes present in the body, including inline and structural
526
+ * revisions. Each carries the revision id {@link acceptChange} /
527
+ * {@link rejectChange} resolve against. Related revision sites fold into one
528
+ * entry where the document model identifies them as one authored change.
529
529
  */
530
530
  getChanges(filter) {
531
531
  const changes = getTrackedChangesFromDoc(this.state.doc);
@@ -1,6 +1,6 @@
1
1
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditAppliedOperation, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditReviewMeta, FolioAIEditSeverity, FolioAIEditSkipReason, FolioAIEditSkippedOperation, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAISignatureParty, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./types.js";
2
2
  import { FolioAIEditView, applyFolioAIEditOperations } from "./apply.js";
3
- import { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange } from "./blockRange.js";
3
+ import { DocPositionRange, clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange } from "./blockRange.js";
4
4
  import { buildAnnotatedBlockText } from "./clean-text.js";
5
5
  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, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "../document-operations.js";
6
6
  import { FolioCommentAnchor, FolioReviewChange, FolioReviewChangeKind, getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
@@ -9,4 +9,4 @@ import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlo
9
9
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
10
10
  import { getFolioParaIdFromBlockId } from "../types/block-id.js";
11
11
  import { WordDiffSegment, diffWordSegments } from "./word-diff.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 FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, 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 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, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange };
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 FolioAIComment, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyResult, 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 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, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, type WordDiffSegment, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -4,8 +4,8 @@ import { createFolioAIEditSnapshot, createFolioAITextRangeHandle, hashFolioAIBlo
4
4
  import { diffWordSegments } from "./word-diff.js";
5
5
  import { applyFolioAIEditOperations } from "./apply.js";
6
6
  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";
7
- import { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange } from "./blockRange.js";
7
+ import { clampRangeToDocSize, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange } from "./blockRange.js";
8
8
  import { getCommentAnchorsFromDoc, getTrackedChangesFromDoc } from "./read.js";
9
9
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./scoped-reading.js";
10
10
  import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, UnsupportedFolioReviewedViewError, isFolioResolvedReviewedView, isFolioReviewedView } from "./headless.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, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange };
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, applyFolioAIEditOperations, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, buildAnnotatedBlockText, clampRangeToDocSize, createFolioAIEditSnapshot, createFolioAITextRangeHandle, diffWordSegments, getCommentAnchorsFromDoc, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, getTrackedChangesFromDoc, hashFolioAIBlockText, isFolioDocumentOperationModeSupported, isFolioResolvedReviewedView, isFolioReviewedView, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, readFolioDocumentSection, resolveFolioAIBlockRange, resolveFolioAITextRange, resolvePassageRange };
@@ -1,18 +1,18 @@
1
1
  import { Node } from "prosemirror-model";
2
2
 
3
3
  //#region src/ai-edits/read.d.ts
4
- type FolioReviewChangeKind = "insertion" | "deletion";
5
- /** A tracked change (insertion or deletion) discovered in the document body. */
4
+ type FolioReviewChangeKind = "insertion" | "deletion" | "formatting" | "rowInserted" | "rowDeleted" | "cellInserted" | "cellDeleted" | "cellMerged";
5
+ /** A tracked change discovered in the document body. */
6
6
  type FolioReviewChange = {
7
7
  /**
8
8
  * The tracked-change revision id — the OOXML `w:id` carried on the
9
- * insertion / deletion mark. A replace produces two changes (a deletion side
9
+ * revision marker. A text replacement produces two changes (a deletion side
10
10
  * and an insertion side) with distinct ids.
11
11
  */
12
12
  id: number;
13
13
  type: FolioReviewChangeKind;
14
14
  author: string; /** ISO date the change was authored, or `null` when the source omitted it. */
15
- date: string | null; /** Inserted text for insertions, removed text for deletions. */
15
+ date: string | null; /** Affected text, including row or cell content for structural revisions. */
16
16
  text: string;
17
17
  /**
18
18
  * Stable id of the containing body block (Word `w14:paraId` or `seq-NNNN`).
@@ -28,9 +28,9 @@ type FolioCommentAnchor = {
28
28
  quote: string;
29
29
  };
30
30
  /**
31
- * The tracked changes (insertions and deletions) present in the body, read from
32
- * the `insertion` / `deletion` marks the editor renders. Runs of one revision
33
- * within a block fold into a single entry.
31
+ * The tracked changes present in the body, read from inline marks and
32
+ * structural node attributes. Runs of one inline revision within a block fold
33
+ * into a single entry.
34
34
  */
35
35
  declare const getTrackedChangesFromDoc: (doc: Node) => FolioReviewChange[];
36
36
  /**
@@ -1,4 +1,6 @@
1
+ import { expectRunPropertyChangeMarkAttrs } from "../prosemirror/attrs/index.js";
1
2
  import { createFolioAIEditSnapshot } from "./snapshot.js";
3
+ import { getTableCellMergeChange } from "../prosemirror/tableCellMergeRevision.js";
2
4
  //#region src/ai-edits/read.ts
3
5
  /** Map each snapshot block's start position to its stable id. */
4
6
  const blockStartIdsFromDoc = (doc) => {
@@ -6,18 +8,103 @@ const blockStartIdsFromDoc = (doc) => {
6
8
  for (const anchor of Object.values(createFolioAIEditSnapshot(doc).anchors)) starts.set(anchor.from, anchor.id);
7
9
  return starts;
8
10
  };
11
+ const firstBlockIdWithin = ({ node, nodePos, blockStarts }) => {
12
+ let blockId = null;
13
+ node.descendants((child, relativePos) => {
14
+ if (blockId !== null || !child.isTextblock) return blockId === null;
15
+ blockId = blockStarts.get(nodePos + 1 + relativePos) ?? null;
16
+ return false;
17
+ });
18
+ return blockId;
19
+ };
9
20
  /**
10
- * The tracked changes (insertions and deletions) present in the body, read from
11
- * the `insertion` / `deletion` marks the editor renders. Runs of one revision
12
- * within a block fold into a single entry.
21
+ * The tracked changes present in the body, read from inline marks and
22
+ * structural node attributes. Runs of one inline revision within a block fold
23
+ * into a single entry.
13
24
  */
14
25
  const getTrackedChangesFromDoc = (doc) => {
15
26
  const insertionType = doc.type.schema.marks["insertion"];
16
27
  const deletionType = doc.type.schema.marks["deletion"];
17
28
  const blockStarts = blockStartIdsFromDoc(doc);
18
29
  const grouped = /* @__PURE__ */ new Map();
30
+ const structuralChangeCellPositions = /* @__PURE__ */ new Map();
19
31
  let currentBlockId = null;
20
32
  doc.descendants((node, pos) => {
33
+ if (node.type.name === "tableRow") for (const [attrName, kind] of [["trIns", "rowInserted"], ["trDel", "rowDeleted"]]) {
34
+ const marker = node.attrs[attrName];
35
+ if (typeof marker !== "object" || marker === null || !("revisionId" in marker) || typeof marker.revisionId !== "number") continue;
36
+ const revisionId = marker.revisionId;
37
+ const author = "author" in marker ? marker.author : void 0;
38
+ const date = "date" in marker ? marker.date : void 0;
39
+ grouped.set(`row:${kind}:${revisionId}:${String(pos)}`, {
40
+ id: revisionId,
41
+ type: kind,
42
+ author: typeof author === "string" ? author : "",
43
+ date: typeof date === "string" ? date : null,
44
+ text: node.textContent,
45
+ blockId: firstBlockIdWithin({
46
+ node,
47
+ nodePos: pos,
48
+ blockStarts
49
+ })
50
+ });
51
+ }
52
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
53
+ const marker = node.attrs["cellMarker"];
54
+ if (typeof marker === "object" && marker !== null && "kind" in marker && (marker.kind === "ins" || marker.kind === "del" || marker.kind === "merge") && "info" in marker && typeof marker.info === "object" && marker.info !== null && "revisionId" in marker.info && typeof marker.info.revisionId === "number") {
55
+ const revisionId = marker.info.revisionId;
56
+ const author = "author" in marker.info ? marker.info.author : void 0;
57
+ const date = "date" in marker.info ? marker.info.date : void 0;
58
+ let kind = "cellMerged";
59
+ if (marker.kind === "ins") kind = "cellInserted";
60
+ else if (marker.kind === "del") kind = "cellDeleted";
61
+ const key = `cell:${kind}:${revisionId}`;
62
+ const text = node.textContent;
63
+ const blockId = firstBlockIdWithin({
64
+ node,
65
+ nodePos: pos,
66
+ blockStarts
67
+ });
68
+ const existing = grouped.get(key);
69
+ const cellPositions = structuralChangeCellPositions.get(key) ?? /* @__PURE__ */ new Set();
70
+ const shouldAppendText = existing !== void 0 && !cellPositions.has(pos);
71
+ grouped.set(key, {
72
+ id: revisionId,
73
+ type: kind,
74
+ author: typeof author === "string" ? author : "",
75
+ date: typeof date === "string" ? date : null,
76
+ text: shouldAppendText && existing.text.length > 0 && text.length > 0 ? `${existing.text}\n${text}` : existing?.text || text,
77
+ blockId: existing?.blockId ?? blockId
78
+ });
79
+ cellPositions.add(pos);
80
+ structuralChangeCellPositions.set(key, cellPositions);
81
+ }
82
+ const continuationCells = node.attrs["_docxVMergeContinuationCells"];
83
+ if (Array.isArray(continuationCells)) for (const continuationCell of continuationCells) {
84
+ const change = getTableCellMergeChange(continuationCell);
85
+ if (!change) continue;
86
+ const revisionId = change.info.id;
87
+ const key = `cell:cellMerged:${revisionId}`;
88
+ const existing = grouped.get(key);
89
+ const text = node.textContent;
90
+ const cellPositions = structuralChangeCellPositions.get(key) ?? /* @__PURE__ */ new Set();
91
+ const shouldAppendText = existing !== void 0 && !cellPositions.has(pos);
92
+ grouped.set(key, {
93
+ id: revisionId,
94
+ type: "cellMerged",
95
+ author: change.info.author,
96
+ date: change.info.date ?? null,
97
+ text: shouldAppendText && existing.text.length > 0 && text.length > 0 ? `${existing.text}\n${text}` : existing?.text || text,
98
+ blockId: existing?.blockId ?? firstBlockIdWithin({
99
+ node,
100
+ nodePos: pos,
101
+ blockStarts
102
+ })
103
+ });
104
+ cellPositions.add(pos);
105
+ structuralChangeCellPositions.set(key, cellPositions);
106
+ }
107
+ }
21
108
  if (node.isTextblock) {
22
109
  currentBlockId = blockStarts.get(pos) ?? null;
23
110
  return true;
@@ -25,6 +112,26 @@ const getTrackedChangesFromDoc = (doc) => {
25
112
  if (!node.isInline || node.text === void 0) return;
26
113
  const text = node.text;
27
114
  for (const mark of node.marks) {
115
+ if (mark.type.name === "runPropertyChange") {
116
+ const { changes } = expectRunPropertyChangeMarkAttrs(mark);
117
+ for (const change of changes) {
118
+ const key = `${currentBlockId ?? ""}:formatting:${String(change.info.id)}`;
119
+ const existing = grouped.get(key);
120
+ if (existing) {
121
+ existing.text += text;
122
+ continue;
123
+ }
124
+ grouped.set(key, {
125
+ id: change.info.id,
126
+ type: "formatting",
127
+ author: change.info.author,
128
+ date: change.info.date ?? null,
129
+ text,
130
+ blockId: currentBlockId
131
+ });
132
+ }
133
+ continue;
134
+ }
28
135
  if (typeof mark.attrs["revisionId"] !== "number") continue;
29
136
  let kind;
30
137
  if (mark.type === insertionType) kind = "insertion";
@@ -0,0 +1,48 @@
1
+ import { TableRectangle } from "./table-mutation-plan.js";
2
+ import { Transaction } from "prosemirror-state";
3
+ import { Node } from "prosemirror-model";
4
+
5
+ //#region src/ai-edits/table-cell-mutations.d.ts
6
+ type TableCellRectangleMutationOptions = {
7
+ tr: Transaction;
8
+ tablePosition: number;
9
+ table: Node;
10
+ rectangle: TableRectangle;
11
+ };
12
+ type TrackedTableCellRectangleMutationOptions = TableCellRectangleMutationOptions & {
13
+ revisionId: number;
14
+ author: string;
15
+ date: string;
16
+ };
17
+ declare const mergeTableRectangle: ({
18
+ tr,
19
+ tablePosition,
20
+ table,
21
+ rectangle
22
+ }: TableCellRectangleMutationOptions) => Transaction | null;
23
+ declare const mergeTrackedVerticalTableCells: ({
24
+ tr,
25
+ tablePosition,
26
+ table,
27
+ rectangle,
28
+ revisionId,
29
+ author,
30
+ date
31
+ }: TrackedTableCellRectangleMutationOptions) => Transaction | null;
32
+ declare const splitTableRectangle: ({
33
+ tr,
34
+ tablePosition,
35
+ table,
36
+ rectangle
37
+ }: TableCellRectangleMutationOptions) => Transaction | null;
38
+ declare const splitTrackedVerticalTableCell: ({
39
+ tr,
40
+ tablePosition,
41
+ table,
42
+ rectangle,
43
+ revisionId,
44
+ author,
45
+ date
46
+ }: TrackedTableCellRectangleMutationOptions) => Transaction | null;
47
+ //#endregion
48
+ export { mergeTableRectangle, mergeTrackedVerticalTableCells, splitTableRectangle, splitTrackedVerticalTableCell };