@stll/folio-core 0.52.0 → 0.54.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 (389) hide show
  1. package/README.md +23 -0
  2. package/dist/ai-edits/apply.d.ts +26 -1
  3. package/dist/ai-edits/apply.js +1779 -141
  4. package/dist/ai-edits/batch-claims.d.ts +145 -0
  5. package/dist/ai-edits/batch-claims.js +275 -0
  6. package/dist/ai-edits/blockRange.js +9 -3
  7. package/dist/ai-edits/character-boundaries.d.ts +43 -0
  8. package/dist/ai-edits/character-boundaries.js +95 -0
  9. package/dist/ai-edits/clean-text.d.ts +33 -3
  10. package/dist/ai-edits/clean-text.js +52 -6
  11. package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
  12. package/dist/ai-edits/comment-lifecycle.js +52 -0
  13. package/dist/ai-edits/headless.d.ts +91 -7
  14. package/dist/ai-edits/headless.js +520 -51
  15. package/dist/ai-edits/index.d.ts +2 -1
  16. package/dist/ai-edits/index.js +2 -1
  17. package/dist/ai-edits/minimal-replacement.d.ts +21 -6
  18. package/dist/ai-edits/minimal-replacement.js +134 -15
  19. package/dist/ai-edits/newListNumbering.d.ts +23 -0
  20. package/dist/ai-edits/newListNumbering.js +94 -0
  21. package/dist/ai-edits/note-references.d.ts +96 -0
  22. package/dist/ai-edits/note-references.js +139 -0
  23. package/dist/ai-edits/pending-suggestions.d.ts +79 -0
  24. package/dist/ai-edits/pending-suggestions.js +306 -0
  25. package/dist/ai-edits/read.d.ts +6 -4
  26. package/dist/ai-edits/read.js +113 -50
  27. package/dist/ai-edits/result-validation.d.ts +29 -0
  28. package/dist/ai-edits/result-validation.js +164 -0
  29. package/dist/ai-edits/revisionStretches.d.ts +17 -0
  30. package/dist/ai-edits/revisionStretches.js +80 -0
  31. package/dist/ai-edits/snapshot.d.ts +36 -3
  32. package/dist/ai-edits/snapshot.js +180 -25
  33. package/dist/ai-edits/table-cell-mutations.d.ts +4 -2
  34. package/dist/ai-edits/table-cell-mutations.js +6 -2
  35. package/dist/ai-edits/table-geometry.d.ts +1 -1
  36. package/dist/ai-edits/table-row-column-mutations.d.ts +44 -17
  37. package/dist/ai-edits/table-row-column-mutations.js +86 -34
  38. package/dist/ai-edits/types.d.ts +155 -16
  39. package/dist/compare/compare.d.ts +1 -1
  40. package/dist/compare/compare.js +65 -16
  41. package/dist/compare/content-alignment.d.ts +5 -1
  42. package/dist/compare/content-alignment.js +52 -34
  43. package/dist/compare/content-types.d.ts +7 -0
  44. package/dist/compare/content.d.ts +4 -1
  45. package/dist/compare/content.js +6 -1
  46. package/dist/compare/formatting.js +2 -1
  47. package/dist/compare/inline-atoms.d.ts +21 -4
  48. package/dist/compare/inline-atoms.js +138 -16
  49. package/dist/compare/inline-provenance.js +1 -1
  50. package/dist/compare/plan.js +10 -5
  51. package/dist/compare/scenario.js +14 -3
  52. package/dist/compare/section-boundary-properties.js +1 -1
  53. package/dist/compare/types.d.ts +4 -2
  54. package/dist/compare/types.js +2 -1
  55. package/dist/compare/verification.d.ts +1 -1
  56. package/dist/compare/verification.js +15 -3
  57. package/dist/controller/committedLayoutDiff.d.ts +25 -0
  58. package/dist/controller/committedLayoutDiff.js +71 -0
  59. package/dist/controller/contentControlWidgetController.d.ts +1 -1
  60. package/dist/controller/folioEditor.d.ts +1 -1
  61. package/dist/controller/fontReadiness.d.ts +70 -3
  62. package/dist/controller/fontReadiness.js +100 -10
  63. package/dist/controller/headerFooterEditorManager.js +6 -1
  64. package/dist/controller/layoutPipeline.d.ts +12 -10
  65. package/dist/controller/layoutPipeline.js +65 -11
  66. package/dist/controller/layoutRunOptions.d.ts +24 -0
  67. package/dist/controller/layoutRunOptions.js +17 -0
  68. package/dist/controller/layoutScheduler.d.ts +19 -14
  69. package/dist/controller/layoutScheduler.js +15 -12
  70. package/dist/controller/layoutSession.d.ts +26 -2
  71. package/dist/controller/layoutSession.js +1 -1
  72. package/dist/controller/noteEditorManager.js +8 -3
  73. package/dist/document-operations.d.ts +11 -2
  74. package/dist/document-operations.js +230 -21
  75. package/dist/docx/altChunk.d.ts +8 -0
  76. package/dist/docx/altChunk.js +17 -0
  77. package/dist/docx/archiveInflation.d.ts +127 -0
  78. package/dist/docx/archiveInflation.js +181 -0
  79. package/dist/docx/attributeRemainder.d.ts +1 -1
  80. package/dist/docx/blockContentParser.d.ts +64 -1
  81. package/dist/docx/blockContentParser.js +17 -4
  82. package/dist/docx/blockCustomXmlShell.d.ts +7 -0
  83. package/dist/docx/blockCustomXmlShell.js +14 -0
  84. package/dist/docx/blockPlainText.js +7 -0
  85. package/dist/docx/commentAnchorIndex.js +1 -1
  86. package/dist/docx/commentRangeIntegrity.js +8 -3
  87. package/dist/docx/commentReferenceNormalization.js +1 -1
  88. package/dist/docx/commentReplyMarkers.js +20 -1
  89. package/dist/docx/compatibility.d.ts +1 -1
  90. package/dist/docx/compatibility.js +34 -13
  91. package/dist/docx/containerChildren.gen.d.ts +1 -1
  92. package/dist/docx/containerChildren.gen.js +1 -0
  93. package/dist/docx/documentParser.js +12 -12
  94. package/dist/docx/drawingIdNormalization.d.ts +3 -1
  95. package/dist/docx/drawingIdNormalization.js +6 -1
  96. package/dist/docx/ensureParaIds.js +119 -97
  97. package/dist/docx/footnoteParser.js +8 -4
  98. package/dist/docx/headerFooterParser.js +3 -3
  99. package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
  100. package/dist/docx/headerFooterReferenceNormalization.js +52 -27
  101. package/dist/docx/hyperlinkParser.d.ts +1 -1
  102. package/dist/docx/index.d.ts +1 -1
  103. package/dist/docx/listNumberingInstances.d.ts +59 -0
  104. package/dist/docx/listNumberingInstances.js +249 -0
  105. package/dist/docx/metadataPrivacy.js +24 -1
  106. package/dist/docx/normalizeBaseDirection.js +2 -1
  107. package/dist/docx/noteReferenceMark.d.ts +15 -0
  108. package/dist/docx/noteReferenceMark.js +48 -0
  109. package/dist/docx/opaqueCarrier.d.ts +7 -0
  110. package/dist/docx/opaqueCarrier.js +38 -0
  111. package/dist/docx/packageParts.js +4 -1
  112. package/dist/docx/paragraphParser.js +96 -27
  113. package/dist/docx/paragraphPropertySource.d.ts +4 -2
  114. package/dist/docx/paragraphPropertySource.js +10 -2
  115. package/dist/docx/paragraphTraversal.js +1 -0
  116. package/dist/docx/parseWarningMessage.js +4 -1
  117. package/dist/docx/parser.js +175 -22
  118. package/dist/docx/rezip.js +31 -7
  119. package/dist/docx/sdtProperties.js +1 -1
  120. package/dist/docx/sdtPropertiesPatch.js +3 -0
  121. package/dist/docx/selectiveSave.js +16 -9
  122. package/dist/docx/selectiveXmlPatch.d.ts +3 -1
  123. package/dist/docx/selectiveXmlPatch.js +186 -39
  124. package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
  125. package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
  126. package/dist/docx/serializer/documentSerializer.js +2 -0
  127. package/dist/docx/serializer/headerFooterSerializer.js +2 -0
  128. package/dist/docx/serializer/noteSerializer.js +3 -1
  129. package/dist/docx/serializer/paragraphSerializer.js +33 -9
  130. package/dist/docx/serializer/tableSerializer.js +25 -38
  131. package/dist/docx/server/boundedArchive.d.ts +8 -13
  132. package/dist/docx/server/boundedArchive.js +70 -59
  133. package/dist/docx/server/createBilingualDocument.js +3 -3
  134. package/dist/docx/server/extractDocxText.d.ts +8 -2
  135. package/dist/docx/server/extractDocxText.js +2 -2
  136. package/dist/docx/server/validateDocxConformance.d.ts +1 -1
  137. package/dist/docx/server/validateDocxConformance.js +1 -0
  138. package/dist/docx/tableParser.d.ts +157 -1
  139. package/dist/docx/tableParser.js +89 -10
  140. package/dist/docx/textBoxParser.js +1 -1
  141. package/dist/docx/unzip.d.ts +20 -2
  142. package/dist/docx/unzip.js +136 -25
  143. package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
  144. package/dist/docx/wordprocessingPrefixes.js +286 -0
  145. package/dist/fonts/embeddedFonts.js +1 -1
  146. package/dist/i18n/messages/catalogs.gen.d.ts +170 -34
  147. package/dist/i18n/messages/catalogs.gen.js +187 -51
  148. package/dist/i18n/messages/messages.gen.d.ts +10 -2
  149. package/dist/internal/compare/inline-presentation.d.ts +2 -1
  150. package/dist/internal/compare/inline-presentation.js +11 -10
  151. package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
  152. package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
  153. package/dist/internal/indexedPositionMap.d.ts +11 -0
  154. package/dist/internal/indexedPositionMap.js +42 -0
  155. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  156. package/dist/internal/revisionResolutionEdits.d.ts +13 -0
  157. package/dist/internal/revisionResolutionEdits.js +19 -0
  158. package/dist/internal/revisionResolutionInline.d.ts +24 -0
  159. package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
  160. package/dist/internal/revisionResolutionStep.d.ts +34 -0
  161. package/dist/internal/revisionResolutionStep.js +282 -0
  162. package/dist/internal/revisionResolutionTracking.d.ts +11 -0
  163. package/dist/internal/revisionResolutionTracking.js +124 -0
  164. package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
  165. package/dist/internal/wholeStoryRevisionResolution.js +600 -0
  166. package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
  167. package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
  168. package/dist/layout-bridge/convert/tableConversion.js +1 -1
  169. package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
  170. package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
  171. package/dist/layout-engine/layoutInstrumentation.js +4 -1
  172. package/dist/layout-engine/measure/cache.d.ts +18 -1
  173. package/dist/layout-engine/measure/cache.js +31 -1
  174. package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
  175. package/dist/layout-engine/measure/measureContainer.js +7 -47
  176. package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
  177. package/dist/layout-engine/measure/measureHelpers.js +22 -2
  178. package/dist/layout-engine/measure/measureParagraph.js +2 -2
  179. package/dist/layout-engine/measure/measureWorker.js +2 -3
  180. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
  181. package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
  182. package/dist/layout-engine/measure/paragraphMeasureShared.d.ts +12 -1
  183. package/dist/layout-engine/measure/paragraphMeasureShared.js +14 -1
  184. package/dist/layout-engine/types.d.ts +6 -0
  185. package/dist/layout-painter/renderPage.js +1 -0
  186. package/dist/layout-painter/renderParagraph.js +1 -1
  187. package/dist/managers/ContextMenuManager.js +5 -2
  188. package/dist/managers/types.d.ts +3 -0
  189. package/dist/markdown/escape.d.ts +2 -2
  190. package/dist/markdown/escape.js +4 -5
  191. package/dist/markdown/fromMarkdown.js +2 -1
  192. package/dist/markdown/internals.d.ts +11 -1
  193. package/dist/markdown/internals.js +24 -3
  194. package/dist/markdown/renderBlock.js +121 -25
  195. package/dist/markdown/renderParagraph.d.ts +13 -1
  196. package/dist/markdown/renderParagraph.js +85 -38
  197. package/dist/markdown/renderRuns.d.ts +10 -1
  198. package/dist/markdown/renderRuns.js +323 -69
  199. package/dist/markdown/renderTable.js +24 -12
  200. package/dist/markdown/trailers.js +2 -2
  201. package/dist/markdown/types.d.ts +29 -7
  202. package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
  203. package/dist/paged-layout/incrementalMeasure.js +1 -9
  204. package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
  205. package/dist/paged-layout/transactionDirtyRange.js +29 -41
  206. package/dist/panel-layout.d.ts +83 -0
  207. package/dist/panel-layout.js +96 -0
  208. package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
  209. package/dist/prosemirror/anchoredTextBoxes.js +156 -0
  210. package/dist/prosemirror/attrs/index.d.ts +6 -2
  211. package/dist/prosemirror/attrs/index.js +172 -1
  212. package/dist/prosemirror/clearRunColor.d.ts +6 -0
  213. package/dist/prosemirror/clearRunColor.js +46 -0
  214. package/dist/prosemirror/commands/comments.d.ts +7 -8
  215. package/dist/prosemirror/commands/comments.js +281 -212
  216. package/dist/prosemirror/commands/contentControls.d.ts +1 -1
  217. package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
  218. package/dist/prosemirror/commands/formatPainter.js +1 -1
  219. package/dist/prosemirror/commands/formatting.d.ts +1 -1
  220. package/dist/prosemirror/commands/hyperlink.js +2 -1
  221. package/dist/prosemirror/commands/index.d.ts +2 -2
  222. package/dist/prosemirror/commands/index.js +2 -2
  223. package/dist/prosemirror/commands/paragraph.d.ts +7 -1
  224. package/dist/prosemirror/commands/paragraph.js +9 -1
  225. package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
  226. package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
  227. package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
  228. package/dist/prosemirror/commands/propertyChangeScope.d.ts +3 -2
  229. package/dist/prosemirror/commands/propertyChangeScope.js +24 -3
  230. package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
  231. package/dist/prosemirror/commands/resolveAllTableChanges.js +620 -0
  232. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
  233. package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
  234. package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
  235. package/dist/prosemirror/commands/resolveParagraphProperties.js +73 -0
  236. package/dist/prosemirror/commands/sectionBreak.js +2 -1
  237. package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +15 -2
  238. package/dist/prosemirror/commands/tableCellMergeResolution.js +23 -7
  239. package/dist/prosemirror/containerFinalParagraph.d.ts +11 -5
  240. package/dist/prosemirror/containerFinalParagraph.js +7 -6
  241. package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
  242. package/dist/prosemirror/conversion/fromProseDoc.d.ts +2 -13
  243. package/dist/prosemirror/conversion/fromProseDoc.js +177 -605
  244. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -11
  245. package/dist/prosemirror/conversion/toProseDoc.js +70 -154
  246. package/dist/prosemirror/documentSchema.d.ts +8 -0
  247. package/dist/prosemirror/documentSchema.js +18 -0
  248. package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
  249. package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
  250. package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
  251. package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
  252. package/dist/prosemirror/extensions/StarterKit.js +5 -1
  253. package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
  254. package/dist/prosemirror/extensions/core/ParagraphExtension.js +116 -63
  255. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +4 -1
  256. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +24 -3
  257. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.d.ts +20 -0
  258. package/dist/prosemirror/extensions/features/JoinedRunStyleExtension.js +43 -0
  259. package/dist/prosemirror/extensions/features/ListExtension.js +167 -128
  260. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
  261. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
  262. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
  263. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
  264. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
  265. package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
  266. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +1 -1
  267. package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
  268. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +124 -0
  269. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +33 -11
  270. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +8 -4
  271. package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
  272. package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
  273. package/dist/prosemirror/extensions/marks/StrikeExtension.js +2 -2
  274. package/dist/prosemirror/extensions/marks/SubscriptExtension.js +2 -2
  275. package/dist/prosemirror/extensions/marks/SuperscriptExtension.js +2 -2
  276. package/dist/prosemirror/extensions/marks/TextColorExtension.js +4 -3
  277. package/dist/prosemirror/extensions/marks/markUtils.d.ts +15 -8
  278. package/dist/prosemirror/extensions/marks/markUtils.js +25 -10
  279. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +12 -0
  280. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +46 -0
  281. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
  282. package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
  283. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +2 -2
  284. package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
  285. package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
  286. package/dist/prosemirror/extensions/nodes/HardBreakExtension.js +5 -3
  287. package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
  288. package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
  289. package/dist/prosemirror/extensions/nodes/TableExtension.js +121 -226
  290. package/dist/prosemirror/extensions/types.d.ts +11 -1
  291. package/dist/prosemirror/findReplaceSelection.js +1 -1
  292. package/dist/prosemirror/hyperlinkRemoval.d.ts +9 -0
  293. package/dist/prosemirror/hyperlinkRemoval.js +129 -0
  294. package/dist/prosemirror/index.d.ts +5 -3
  295. package/dist/prosemirror/index.js +5 -3
  296. package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
  297. package/dist/prosemirror/indexedNodeLookup.js +103 -0
  298. package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
  299. package/dist/prosemirror/listAutoformatMarkers.js +98 -0
  300. package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
  301. package/dist/prosemirror/listInstanceReferences.js +72 -0
  302. package/dist/prosemirror/listLabels.d.ts +24 -0
  303. package/dist/prosemirror/listLabels.js +111 -0
  304. package/dist/prosemirror/listNumbering.d.ts +92 -0
  305. package/dist/prosemirror/listNumbering.js +330 -0
  306. package/dist/prosemirror/listRendering.d.ts +27 -0
  307. package/dist/prosemirror/listRendering.js +27 -0
  308. package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
  309. package/dist/prosemirror/listRenderingAttrs.js +8 -1
  310. package/dist/prosemirror/markupViewNotes.d.ts +8 -0
  311. package/dist/prosemirror/markupViewNotes.js +49 -0
  312. package/dist/prosemirror/markupViewProjection.d.ts +97 -0
  313. package/dist/prosemirror/markupViewProjection.js +88 -0
  314. package/dist/prosemirror/noteReferenceReview.d.ts +77 -0
  315. package/dist/prosemirror/noteReferenceReview.js +332 -0
  316. package/dist/prosemirror/numberingAttr.d.ts +9 -1
  317. package/dist/prosemirror/numberingAttr.js +9 -1
  318. package/dist/prosemirror/outlineLevelAttr.js +1 -1
  319. package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
  320. package/dist/prosemirror/paragraphIndentation.js +32 -7
  321. package/dist/prosemirror/paragraphMarkJoin.d.ts +21 -0
  322. package/dist/prosemirror/paragraphMarkJoin.js +26 -0
  323. package/dist/prosemirror/paragraphPropertyCarry.d.ts +64 -0
  324. package/dist/prosemirror/paragraphPropertyCarry.js +158 -0
  325. package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
  326. package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
  327. package/dist/prosemirror/plugins/documentNumbering.js +97 -11
  328. package/dist/prosemirror/plugins/documentStyleState.d.ts +26 -0
  329. package/dist/prosemirror/plugins/documentStyleState.js +32 -0
  330. package/dist/prosemirror/plugins/documentStyles.d.ts +3 -13
  331. package/dist/prosemirror/plugins/documentStyles.js +67 -17
  332. package/dist/prosemirror/plugins/index.d.ts +2 -1
  333. package/dist/prosemirror/plugins/index.js +2 -1
  334. package/dist/prosemirror/plugins/paragraphStyleResolution.d.ts +15 -0
  335. package/dist/prosemirror/plugins/paragraphStyleResolution.js +230 -0
  336. package/dist/prosemirror/plugins/suggestionMode.d.ts +27 -3
  337. package/dist/prosemirror/plugins/suggestionMode.js +490 -63
  338. package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
  339. package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
  340. package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
  341. package/dist/prosemirror/positionSweep.d.ts +24 -0
  342. package/dist/prosemirror/positionSweep.js +222 -0
  343. package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +23 -3
  344. package/dist/prosemirror/rebaseParagraphRunFormatting.js +39 -13
  345. package/dist/prosemirror/rebaseParagraphRuns.d.ts +36 -0
  346. package/dist/prosemirror/rebaseParagraphRuns.js +99 -0
  347. package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
  348. package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
  349. package/dist/prosemirror/replacedAnnotations.js +170 -1
  350. package/dist/prosemirror/runFormattingFromMarks.d.ts +18 -0
  351. package/dist/prosemirror/runFormattingFromMarks.js +567 -0
  352. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
  353. package/dist/prosemirror/runFormattingReconciliation.js +10 -9
  354. package/dist/prosemirror/schema/index.d.ts +2 -2
  355. package/dist/prosemirror/schema/index.js +3 -0
  356. package/dist/prosemirror/schema/nodes.d.ts +44 -11
  357. package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
  358. package/dist/prosemirror/sectionMarkEdits.js +59 -0
  359. package/dist/prosemirror/storyListNumbering.d.ts +17 -0
  360. package/dist/prosemirror/storyListNumbering.js +102 -0
  361. package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +59 -0
  362. package/dist/prosemirror/styles/paragraphStyleCascade.js +145 -0
  363. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +21 -2
  364. package/dist/prosemirror/styles/resolvedStyleAttrs.js +38 -1
  365. package/dist/prosemirror/styles/tableStyleRegions.d.ts +31 -0
  366. package/dist/prosemirror/styles/tableStyleRegions.js +55 -0
  367. package/dist/prosemirror/tableCellPaste.d.ts +74 -0
  368. package/dist/prosemirror/tableCellPaste.js +525 -0
  369. package/dist/prosemirror/tableGridMutation.d.ts +87 -2
  370. package/dist/prosemirror/tableGridMutation.js +255 -2
  371. package/dist/prosemirror/tableRunIn.d.ts +51 -0
  372. package/dist/prosemirror/tableRunIn.js +165 -0
  373. package/dist/prosemirror/textInput.js +11 -2
  374. package/dist/prosemirror/trackedRevisionPath.d.ts +17 -0
  375. package/dist/prosemirror/trackedRevisionPath.js +32 -0
  376. package/dist/prosemirror/validation.js +5 -1
  377. package/dist/redline.js +93 -9
  378. package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
  379. package/dist/render-dom/BodySelectionOverlay.js +3 -2
  380. package/dist/server.d.ts +2 -2
  381. package/dist/utils/findReplace.js +2 -2
  382. package/dist/utils/mergeDocumentContent.js +1 -1
  383. package/dist/utils/noteReferenceLabels.d.ts +36 -0
  384. package/dist/utils/noteReferenceLabels.js +32 -0
  385. package/dist/utils/replaceText.js +1 -1
  386. package/dist/version-comparison.d.ts +1 -0
  387. package/dist/version-comparison.js +1 -0
  388. package/package.json +6 -5
  389. package/dist/internal/headlessRevisionResolution.d.ts +0 -23
@@ -1,11 +1,25 @@
1
+ import { expectParagraphAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
2
+ import { paragraphPropertiesSnapshot } from "../commands/propertyChangeScope.js";
3
+ import { paragraphEndsItsContainer } from "../containerFinalParagraph.js";
1
4
  import { encloseWholeControls } from "../contentControlRevisions.js";
2
5
  import { splitBlockClearBorders } from "../extensions/features/BaseKeymapExtension.js";
6
+ import { JOINED_RUNS_RESTYLED_META } from "../extensions/features/JoinedRunStyleExtension.js";
7
+ import { expandNoteReferenceDeletionRange } from "../extensions/marks/noteReferenceDeletion.js";
8
+ import { carryParagraphProperties, paragraphLeftAfter, recordReplacedParagraphProperties } from "../paragraphPropertyCarry.js";
9
+ import { rebaseParagraphRuns } from "../rebaseParagraphRunFormatting.js";
10
+ import { RUN_FORMATTING_MARK_NAMES } from "../runFormattingMarkNames.js";
11
+ import { paragraphRunStyleContext } from "../runStyleFormatting.js";
12
+ import { cellPasteRange, pasteTableCells } from "../tableCellPaste.js";
3
13
  import { handleEditorBeforeInput } from "../textInput.js";
4
14
  import { canCarryTrackedRunMark } from "../trackedRunInlineAtoms.js";
15
+ import { getDocumentNumbering } from "./documentNumbering.js";
16
+ import { getDocumentStyleResolver } from "./documentStyleState.js";
5
17
  import { mintRevisionId, seedRevisionIdsFromDoc } from "./revisionIds.js";
6
- import { Mapping } from "prosemirror-transform";
7
- import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
18
+ import { Slice } from "prosemirror-model";
19
+ import { Mapping, ReplaceStep } from "prosemirror-transform";
20
+ import { AllSelection, Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
8
21
  import { isHistoryTransaction } from "prosemirror-history";
22
+ import { undoInputRule } from "prosemirror-inputrules";
9
23
  //#region src/prosemirror/plugins/suggestionMode.ts
10
24
  /**
11
25
  * Suggestion Mode Plugin
@@ -22,6 +36,12 @@ import { isHistoryTransaction } from "prosemirror-history";
22
36
  const suggestionModeKey = new PluginKey("suggestionMode");
23
37
  const SUGGESTION_META = "suggestionModeApplied";
24
38
  const SUGGESTION_BYPASS_META = "suggestionModeBypass";
39
+ /**
40
+ * The text a tracked text-input transaction typed. Rules that react to typing
41
+ * (list autoformat) read it, because this plugin claims text input before any
42
+ * input rule sees it.
43
+ */
44
+ const SUGGESTED_TEXT_INPUT_META = "suggestionModeTextInput";
25
45
  function makeMarkAttrs(pluginState) {
26
46
  return {
27
47
  revisionId: mintRevisionId(),
@@ -39,6 +59,27 @@ function makeRevisionInfo(state) {
39
59
  date: attrs.date
40
60
  };
41
61
  }
62
+ /**
63
+ * A removed paragraph mark leaves the paragraph after it once accepted. An
64
+ * edit that reads as the paragraph before it (Backspace or Delete at the
65
+ * break, a deletion running from inside one paragraph into another) gives the
66
+ * paragraph left that paragraph's properties now, under the mark's revision:
67
+ * accepting reads as the edit made directly, rejecting restores its own.
68
+ */
69
+ function carryIntoParagraphLeft(state, tr, source, survivorPos, info) {
70
+ carryParagraphProperties({
71
+ tr,
72
+ position: survivorPos,
73
+ source,
74
+ styleResolver: getDocumentStyleResolver(state),
75
+ numbering: getDocumentNumbering(state),
76
+ revision: {
77
+ id: info.id,
78
+ author: info.author ?? "",
79
+ date: info.date ?? ""
80
+ }
81
+ });
82
+ }
42
83
  function makeParagraphMarkInfo(pluginState) {
43
84
  const attrs = makeMarkAttrs(pluginState);
44
85
  return {
@@ -69,10 +110,11 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
69
110
  return findAdjacentRevision(doc, from, markTypeName, author) ?? findAdjacentRevision(doc, to, markTypeName, author);
70
111
  }
71
112
  /**
72
- * Walk a text range and either mark as deletion or retract own insertions.
113
+ * Walk a selected range and track its text, paragraph breaks, and table rows.
114
+ * Retract the current author's own inserted text.
73
115
  * Processes in reverse order to maintain position validity.
74
116
  */
75
- function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState) {
117
+ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState, joinState) {
76
118
  const ranges = [];
77
119
  doc.nodesBetween(from, to, (node, pos) => {
78
120
  if (!canCarryTrackedRunMark(node)) return;
@@ -87,12 +129,44 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
87
129
  isOwnInsert
88
130
  });
89
131
  });
90
- if (ranges.length === 0) return;
91
132
  const delAttrs = findAdjacentRevisionForRange(doc, from, to, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
133
+ let removedMark = false;
134
+ doc.nodesBetween(from, to, (node, pos) => {
135
+ if (node.type.name === "tableRow" && pos >= from && pos + node.nodeSize <= to && node.attrs["trIns"] == null && node.attrs["trDel"] == null) tr.setNodeAttribute(pos, "trDel", delAttrs);
136
+ if (node.type.name !== "paragraph") return;
137
+ const boundary = pos + node.nodeSize;
138
+ if (boundary > from && boundary <= to && node.attrs["pPrMark"] == null) {
139
+ tr.setNodeAttribute(pos, "pPrMark", {
140
+ kind: "del",
141
+ info: {
142
+ id: delAttrs.revisionId,
143
+ author: delAttrs.author,
144
+ date: delAttrs.date
145
+ }
146
+ });
147
+ removedMark = true;
148
+ }
149
+ });
150
+ const $first = doc.resolve(from);
151
+ const $last = doc.resolve(to);
152
+ if (joinState && removedMark && $first.parent.type.name === "paragraph" && $last.parent.type.name === "paragraph" && $first.parent !== $last.parent && from > $first.start()) {
153
+ const lastPos = doc === tr.doc ? $last.before() : tr.mapping.map($last.before());
154
+ const lastMark = tr.doc.nodeAt(lastPos)?.attrs["pPrMark"];
155
+ const survivorPos = lastMark?.kind === "del" || lastMark?.kind === "moveFrom" ? paragraphLeftAfter({
156
+ doc: tr.doc,
157
+ paragraphPos: lastPos
158
+ }) ?? lastPos : lastPos;
159
+ carryIntoParagraphLeft(joinState, tr, $first.parent, survivorPos, {
160
+ id: delAttrs.revisionId,
161
+ author: delAttrs.author,
162
+ date: delAttrs.date
163
+ });
164
+ }
165
+ if (ranges.length === 0) return;
92
166
  encloseWholeControls({
93
167
  tr,
94
- from: tr.mapping.map(from),
95
- to: tr.mapping.map(to, -1),
168
+ from,
169
+ to,
96
170
  revisionId: delAttrs.revisionId
97
171
  });
98
172
  for (let i = ranges.length - 1; i >= 0; i--) {
@@ -101,10 +175,26 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
101
175
  else tr.addMark(range.from, range.to, deletionType.create(delAttrs));
102
176
  }
103
177
  }
178
+ /** Preserve clipboard revisions inside the revision that inserted their content. */
179
+ function enclosePastedRunRevisions({ tr, from, to, revision: insertion }) {
180
+ tr.doc.nodesBetween(from, to, (node, pos) => {
181
+ if (!canCarryTrackedRunMark(node)) return;
182
+ const revision = node.marks.find(({ type }) => type.name === "insertion" || type.name === "deletion");
183
+ if (!revision) return;
184
+ const previous = expectTrackedChangeMarkAttrs(revision);
185
+ tr.addMark(Math.max(from, pos), Math.min(to, pos + node.nodeSize), revision.type.create({
186
+ ...revision.attrs,
187
+ _docxRevisionAncestors: [{
188
+ type: "insertion",
189
+ ...insertion,
190
+ outerWrapperCount: 0
191
+ }, ...previous._docxRevisionAncestors ?? []]
192
+ }));
193
+ });
194
+ }
104
195
  /**
105
- * Add the insertion mark to every run carrier in `[from, to)` that does not
106
- * already carry a tracked-change mark. Content already carrying a revision is
107
- * left untouched so another author's attribution is not overwritten.
196
+ * Track every inserted run, paragraph break, and table row in `[from, to)`.
197
+ * Content already carrying a revision keeps its attribution.
108
198
  */
109
199
  function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, attrs) {
110
200
  doc.nodesBetween(from, to, (node, pos) => {
@@ -115,6 +205,23 @@ function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, att
115
205
  if (start >= end) return;
116
206
  tr.addMark(start, end, insertionType.create(attrs));
117
207
  });
208
+ const $end = doc.resolve(to);
209
+ const endsBetweenBlocks = !$end.parent.isTextblock && $end.nodeBefore !== null && $end.nodeBefore.type.name !== "paragraph";
210
+ doc.nodesBetween(from, to, (node, pos) => {
211
+ if (node.type.name === "paragraph") {
212
+ const boundary = pos + node.nodeSize;
213
+ const keepsOwnMark = endsBetweenBlocks && boundary - 1 === from;
214
+ if (boundary > from && boundary < to && !keepsOwnMark && node.attrs["pPrMark"] == null) tr.setNodeAttribute(pos, "pPrMark", {
215
+ kind: "ins",
216
+ info: {
217
+ id: attrs.revisionId,
218
+ author: attrs.author,
219
+ date: attrs.date
220
+ }
221
+ });
222
+ }
223
+ if (node.type.name === "tableRow" && pos >= from && pos + node.nodeSize <= to && node.attrs["trIns"] == null && node.attrs["trDel"] == null) tr.setNodeAttribute(pos, "trIns", attrs);
224
+ });
118
225
  encloseWholeControls({
119
226
  tr,
120
227
  from,
@@ -126,32 +233,221 @@ function markRangeAsInserted(tr, doc, from, to, insertionType, deletionType, att
126
233
  * With track changes on, pasting over a non-empty text selection marks the
127
234
  * replaced text as a tracked deletion and the pasted slice as a tracked
128
235
  * insertion (so they read as one replacement), matching typing over a
129
- * selection. Returns false for a collapsed cursor or non-text selection so the
236
+ * selection. Select-all (an `AllSelection`) counts as a text selection over the
237
+ * whole document. Returns false for a collapsed cursor or another selection so the
130
238
  * default paste + the `appendTransaction` catch-all marks a plain insertion
131
239
  * (eigenpal/docx-editor#784).
132
240
  */
133
241
  function handleSuggestionPaste(view, slice, pluginState) {
134
242
  const { selection } = view.state;
135
- if (!(selection instanceof TextSelection) || selection.empty) return false;
243
+ const selectsAll = selection instanceof AllSelection;
244
+ if (!(selection instanceof TextSelection || selectsAll) || selection.empty) return false;
136
245
  const insertionType = view.state.schema.marks["insertion"];
137
246
  const deletionType = view.state.schema.marks["deletion"];
138
247
  if (!insertionType || !deletionType) return false;
139
- const { from, to } = selection;
248
+ if (selectsAll && (slice.openStart > 0 || slice.openEnd > 0)) slice = new Slice(view.state.tr.replaceSelection(slice).doc.content, 0, 0);
249
+ const { from, to } = selectsAll ? {
250
+ from: Selection.atStart(view.state.doc).from,
251
+ to: Selection.atEnd(view.state.doc).to
252
+ } : selection;
140
253
  const tr = view.state.tr;
141
254
  tr.setMeta(SUGGESTION_META, true);
142
- markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState);
143
- const insertFrom = tr.mapping.map(to);
144
- const sizeBefore = tr.doc.content.size;
145
- tr.replaceRange(insertFrom, insertFrom, slice);
146
- const insertTo = insertFrom + (tr.doc.content.size - sizeBefore);
147
- tr.removeMark(insertFrom, insertTo, deletionType);
148
- const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
255
+ const { doc } = view.state;
256
+ const $from = doc.resolve(from);
257
+ const $to = doc.resolve(to);
258
+ const closedBlocks = slice.openStart === 0 && slice.content.firstChild?.isBlock === true;
259
+ const beforeStruck = closedBlocks && $from.parent.isTextblock && $from.parentOffset === 0;
260
+ const wholeBlocks = beforeStruck && $to.parent.isTextblock && $to.parentOffset === $to.parent.content.size;
261
+ const endsContainer = wholeBlocks && paragraphEndsItsContainer(doc.resolve($to.before()), $to.parent.type.name);
262
+ const rotates = endsContainer && slice.content.firstChild?.type === $to.parent.type && slice.content.lastChild?.type === $to.parent.type;
263
+ const tableFirst = endsContainer && !rotates;
264
+ if (!tableFirst) markRangeAsDeleted(tr, doc, from, wholeBlocks && !endsContainer ? $to.after() : to, insertionType, deletionType, pluginState);
265
+ const carryAcross = () => {
266
+ if (selectsAll || $from.parent.type.name !== "paragraph" || $from.parent === $to.parent || from <= $from.start()) return;
267
+ const firstPos = tr.mapping.map($from.before());
268
+ const mark = tr.doc.nodeAt(firstPos)?.attrs["pPrMark"];
269
+ const survivorPos = paragraphLeftAfter({
270
+ doc: tr.doc,
271
+ paragraphPos: firstPos
272
+ });
273
+ if (mark?.kind === "del" && survivorPos !== null) carryIntoParagraphLeft(view.state, tr, $from.parent, survivorPos, mark.info);
274
+ };
275
+ const inlineSlice = slice.content.firstChild?.isInline === true;
276
+ if (inlineSlice) carryAcross();
277
+ let at = tr.mapping.map(to);
278
+ if (rotates) at = tr.mapping.map($to.after());
279
+ else if (tableFirst) at = $from.before();
280
+ else if (beforeStruck) at = tr.mapping.map(from, -1);
281
+ const firstStep = tr.steps.length;
282
+ tr.replaceRange(at, at, slice);
283
+ const placed = tr.steps.slice(firstStep).flatMap((step, offset) => {
284
+ const later = new Mapping(tr.mapping.maps.slice(firstStep + offset + 1));
285
+ const ranges = [];
286
+ step.getMap().forEach((_oldFrom, _oldTo, newFrom, newTo) => {
287
+ ranges.push({
288
+ from: later.map(newFrom, -1),
289
+ to: later.map(newTo, 1)
290
+ });
291
+ });
292
+ return ranges;
293
+ });
294
+ let insertFrom = placed.length === 0 ? at : Math.min(...placed.map((range) => range.from));
295
+ let insertTo = placed.length === 0 ? at : Math.max(...placed.map((range) => range.to));
296
+ if (tableFirst) {
297
+ const struck = tr.steps.length;
298
+ markRangeAsDeleted(tr, tr.doc, tr.mapping.map(from), tr.mapping.map(to), insertionType, deletionType, pluginState);
299
+ const since = tr.mapping.slice(struck);
300
+ insertFrom = since.map(insertFrom, -1);
301
+ insertTo = since.map(insertTo, -1);
302
+ }
303
+ const insertAttrs = findAdjacentRevision(doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
304
+ enclosePastedRunRevisions({
305
+ tr,
306
+ from: insertFrom,
307
+ to: insertTo,
308
+ revision: insertAttrs
309
+ });
149
310
  markRangeAsInserted(tr, tr.doc, insertFrom, insertTo, insertionType, deletionType, insertAttrs);
311
+ if (!inlineSlice) carryAcross();
312
+ if (!closedBlocks && $to.parent.type.name === "paragraph") {
313
+ const $end = tr.doc.resolve(tr.mapping.map(to));
314
+ if ($end.parent.type.name === "paragraph" && $end.before() !== tr.mapping.map($from.before())) recordReplacedParagraphProperties({
315
+ tr,
316
+ position: $end.before(),
317
+ replaced: $to.parent,
318
+ revision: {
319
+ id: insertAttrs.revisionId,
320
+ author: insertAttrs.author,
321
+ date: insertAttrs.date
322
+ }
323
+ });
324
+ }
325
+ const firstPasted = slice.content.firstChild;
326
+ if (selectsAll && !closedBlocks && firstPasted?.type.name === "paragraph") {
327
+ const $first = tr.doc.resolve(insertFrom);
328
+ if ($first.parent.type.name === "paragraph") carryParagraphProperties({
329
+ tr,
330
+ position: $first.before(),
331
+ source: firstPasted,
332
+ revision: {
333
+ id: insertAttrs.revisionId,
334
+ author: insertAttrs.author,
335
+ date: insertAttrs.date
336
+ },
337
+ styleResolver: getDocumentStyleResolver(view.state),
338
+ numbering: getDocumentNumbering(view.state)
339
+ });
340
+ }
341
+ if (rotates) {
342
+ const rotation = tr.steps.length;
343
+ const joined = rotateIntoFinalParagraph(view.state, tr, tr.mapping.map($to.before()), insertAttrs);
344
+ insertFrom = Math.min(tr.mapping.slice(rotation).map(insertFrom, -1), joined);
345
+ insertTo = tr.mapping.slice(rotation).map(insertTo);
346
+ }
150
347
  tr.setSelection(TextSelection.near(tr.doc.resolve(insertTo)));
151
348
  view.dispatch(tr.scrollIntoView());
152
349
  return true;
153
350
  }
154
351
  /**
352
+ * With track changes on, a paste into a table's cells (a cell selection, or a
353
+ * block of copied cells) replaces each cell's content the way a paste over a
354
+ * text selection does: the old content struck through, the pasted content
355
+ * after it as an insertion. When the pasted content is whole paragraphs, the
356
+ * last one ends the cell, so its paragraph mark is tracked as added too:
357
+ * rejecting then drops it and accepting joins the struck paragraphs into it.
358
+ */
359
+ function handleSuggestionTableCellPaste(view, slice, pluginState) {
360
+ const insertionType = view.state.schema.marks["insertion"];
361
+ const deletionType = view.state.schema.marks["deletion"];
362
+ if (!insertionType || !deletionType) return false;
363
+ const revision = makeMarkAttrs(pluginState);
364
+ return pasteTableCells(view.state, slice, (tr) => view.dispatch(tr.setMeta(SUGGESTION_META, true)), {
365
+ revision,
366
+ replaceCellContent: (tr, cellPos, content) => {
367
+ const cell = tr.doc.nodeAt(cellPos);
368
+ if (!cell) return;
369
+ const { from, to } = cellPasteRange(cell, cellPos, content);
370
+ const mapFrom = tr.mapping.maps.length;
371
+ markRangeAsDeleted(tr, tr.doc, from, to, insertionType, deletionType, pluginState);
372
+ const insertFrom = tr.mapping.slice(mapFrom).map(to);
373
+ const sizeBefore = tr.doc.content.size;
374
+ tr.replaceRange(insertFrom, insertFrom, content);
375
+ const insertTo = insertFrom + (tr.doc.content.size - sizeBefore);
376
+ enclosePastedRunRevisions({
377
+ tr,
378
+ from: insertFrom,
379
+ to: insertTo,
380
+ revision
381
+ });
382
+ markRangeAsInserted(tr, tr.doc, insertFrom, insertTo, insertionType, deletionType, revision);
383
+ tr.doc.nodesBetween(insertFrom, insertTo, (node, pos) => {
384
+ if (node.type.name === "paragraph" && pos >= insertFrom && pos + node.nodeSize <= insertTo && node.attrs["pPrMark"] == null) tr.setNodeAttribute(pos, "pPrMark", {
385
+ kind: "ins",
386
+ info: {
387
+ id: revision.revisionId,
388
+ author: revision.author,
389
+ date: revision.date
390
+ }
391
+ });
392
+ });
393
+ }
394
+ });
395
+ }
396
+ /**
397
+ * Pasted paragraphs replaced a container's last paragraph. They were placed
398
+ * after it, and the paragraph was struck but keeps its mark, as a container's
399
+ * last paragraph must. Join the first pasted paragraph onto it, so the struck
400
+ * text and the first paste share one paragraph whose break is the inserted one,
401
+ * and record the replaced paragraph's formatting on the paragraph that now ends
402
+ * the container: accepting leaves only the paste, and rejecting closes every
403
+ * pasted break back into one paragraph that returns to its old formatting.
404
+ * Returns the position of the joined paragraph.
405
+ */
406
+ function rotateIntoFinalParagraph(state, tr, replacedPos, attrs) {
407
+ const replaced = tr.doc.nodeAt(replacedPos);
408
+ const first = replaced ? tr.doc.nodeAt(replacedPos + replaced.nodeSize) : null;
409
+ if (!replaced || !first) return replacedPos;
410
+ const replacedAttrs = expectParagraphAttrs(replaced);
411
+ const previousFormatting = {
412
+ ...paragraphPropertiesSnapshot(replaced),
413
+ numPr: replacedAttrs.numPr ?? null
414
+ };
415
+ const { _sectionProperties: sectionProperties } = replaced.attrs;
416
+ tr.join(replacedPos + replaced.nodeSize);
417
+ tr.setNodeMarkup(replacedPos, void 0, first.attrs);
418
+ const styleResolver = getDocumentStyleResolver(state);
419
+ if (styleResolver && replaced.content.size > 0) rebaseParagraphRuns({
420
+ previousContext: paragraphRunStyleContext(replaced, styleResolver),
421
+ paragraphPosition: replacedPos,
422
+ range: {
423
+ from: 0,
424
+ to: replaced.content.size
425
+ },
426
+ styleResolver,
427
+ tr
428
+ });
429
+ const $joined = tr.doc.resolve(replacedPos);
430
+ const container = $joined.parent;
431
+ let finalPos = replacedPos;
432
+ for (let index = $joined.index(); index < container.childCount - 1; index += 1) finalPos += container.child(index).nodeSize;
433
+ const final = tr.doc.nodeAt(finalPos);
434
+ if (final) tr.setNodeMarkup(finalPos, void 0, {
435
+ ...final.attrs,
436
+ pPrMark: null,
437
+ ...sectionProperties == null ? {} : { _sectionProperties: sectionProperties },
438
+ _propertyChanges: [{
439
+ type: "paragraphPropertyChange",
440
+ info: {
441
+ id: attrs.revisionId,
442
+ author: attrs.author,
443
+ date: attrs.date
444
+ },
445
+ previousFormatting
446
+ }]
447
+ });
448
+ return replacedPos;
449
+ }
450
+ /**
155
451
  * Insert text as a tracked insertion, optionally marking replaced selection as deletion.
156
452
  */
157
453
  function applySuggestionInsert(view, from, to, text, pluginState) {
@@ -159,20 +455,79 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
159
455
  if (!insertionType) return false;
160
456
  const tr = view.state.tr;
161
457
  tr.setMeta(SUGGESTION_META, true);
458
+ tr.setMeta(SUGGESTED_TEXT_INPUT_META, text);
459
+ const selectsAll = view.state.selection instanceof AllSelection;
460
+ if (selectsAll) {
461
+ from = Math.max(from, Selection.atStart(view.state.doc).from);
462
+ to = Math.min(to, Selection.atEnd(view.state.doc).to);
463
+ }
464
+ const replacedFormatting = selectsAll ? (view.state.doc.resolve(from).nodeAfter?.marks ?? []).filter(({ type }) => RUN_FORMATTING_MARK_NAMES.has(type.name)) : null;
162
465
  const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
163
466
  if (from !== to) {
164
467
  const deletionType = view.state.schema.marks["deletion"];
165
- if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState);
468
+ if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState, view.state);
166
469
  }
167
470
  const insertAt = tr.mapping.map(to);
168
471
  tr.insertText(text, insertAt, insertAt);
169
472
  const deletionType = view.state.schema.marks["deletion"];
170
473
  if (deletionType) tr.removeMark(insertAt, insertAt + text.length, deletionType);
171
474
  tr.addMark(insertAt, insertAt + text.length, insertionType.create(insertAttrs));
475
+ if (replacedFormatting) {
476
+ for (const mark of tr.doc.resolve(insertAt).nodeAfter?.marks ?? []) if (RUN_FORMATTING_MARK_NAMES.has(mark.type.name)) tr.removeMark(insertAt, insertAt + text.length, mark.type);
477
+ for (const mark of replacedFormatting) tr.addMark(insertAt, insertAt + text.length, mark);
478
+ }
479
+ separateSplitStretch(tr, insertAt, insertAt + text.length, insertAttrs.revisionId);
480
+ if (view.state.selection instanceof AllSelection) tr.setSelection(TextSelection.create(tr.doc, insertAt + text.length));
172
481
  view.dispatch(tr.scrollIntoView());
173
482
  return true;
174
483
  }
175
484
  /**
485
+ * Text typed inside another revision leaves that revision in two stretches.
486
+ * A saved package writes each stretch as a wrapper of its own with its own
487
+ * `w:id`, so the stretch after the typed text gets a fresh id now: the editor
488
+ * lists, resolves and saves the same changes a reopened package does.
489
+ */
490
+ function separateSplitStretch(tr, from, to, typedId) {
491
+ const $from = tr.doc.resolve(from);
492
+ const $to = tr.doc.resolve(to);
493
+ const before = $from.nodeBefore;
494
+ if (!before || $to.parent !== $from.parent) return;
495
+ const parent = $to.parent;
496
+ const start = $to.start();
497
+ for (const mark of before.marks) {
498
+ if (mark.type.name !== "insertion" && mark.type.name !== "deletion") continue;
499
+ const revisionId = mark.attrs["revisionId"];
500
+ if (revisionId === typedId || typeof revisionId !== "number") continue;
501
+ let end = to;
502
+ let offset = to - start;
503
+ while (offset < parent.content.size) {
504
+ const child = parent.childAfter(offset).node;
505
+ if (!child?.marks.some((candidate) => candidate.eq(mark))) break;
506
+ offset += child.nodeSize;
507
+ end = start + offset;
508
+ }
509
+ if (end === to) continue;
510
+ tr.removeMark(to, end, mark);
511
+ tr.addMark(to, end, mark.type.create({
512
+ ...mark.attrs,
513
+ revisionId: mintRevisionId()
514
+ }));
515
+ }
516
+ }
517
+ /**
518
+ * Remove `from`..`to` of `tr.doc` as a suggestion: text the current author
519
+ * inserted is retracted, anything else is marked deleted. `false` when the
520
+ * editor is not suggesting.
521
+ */
522
+ function suggestRangeDeletion(state, tr, from, to) {
523
+ const pluginState = suggestionModeKey.getState(state);
524
+ const insertionType = state.schema.marks["insertion"];
525
+ const deletionType = state.schema.marks["deletion"];
526
+ if (!pluginState?.active || !insertionType || !deletionType) return false;
527
+ markRangeAsDeleted(tr, tr.doc, from, to, insertionType, deletionType, pluginState);
528
+ return true;
529
+ }
530
+ /**
176
531
  * Track-changes Enter: split the paragraph and stamp `pPrMark = 'ins'` on the
177
532
  * FIRST half of the split (the upper paragraph), per ECMA-376 §17.13.5. The
178
533
  * actual splitting reuses `splitBlockClearBorders` so border/style/stored-mark
@@ -198,6 +553,13 @@ function handleSuggestionEnter(view, pluginState) {
198
553
  info: makeParagraphMarkInfo(pluginState)
199
554
  };
200
555
  tr.setNodeAttribute(sourcePos, "pPrMark", markInfo);
556
+ const $caret = tr.selection.$from;
557
+ if ($caret.parent.type.name === "paragraph" && $caret.before() !== sourcePos) recordReplacedParagraphProperties({
558
+ tr,
559
+ position: $caret.before(),
560
+ replaced: $from.parent,
561
+ revision: markInfo.info
562
+ });
201
563
  }
202
564
  }
203
565
  view.dispatch(tr.scrollIntoView());
@@ -251,9 +613,22 @@ function applyPPrDel(view, targetParagraphPos, pluginState) {
251
613
  const tr = view.state.tr;
252
614
  tr.setMeta(SUGGESTION_META, true);
253
615
  const joinPos = targetParagraphPos + targetNode.nodeSize;
616
+ const joined = view.state.doc.nodeAt(joinPos);
254
617
  try {
255
618
  tr.join(joinPos);
256
619
  tr.setNodeAttribute(targetParagraphPos, "pPrMark", null);
620
+ const styleResolver = getDocumentStyleResolver(view.state);
621
+ if (styleResolver && joined?.type === targetNode.type && joined.content.size > 0) rebaseParagraphRuns({
622
+ previousContext: paragraphRunStyleContext(joined, styleResolver),
623
+ paragraphPosition: targetParagraphPos,
624
+ range: {
625
+ from: targetNode.content.size,
626
+ to: targetNode.content.size + joined.content.size
627
+ },
628
+ styleResolver,
629
+ tr
630
+ });
631
+ tr.setMeta(JOINED_RUNS_RESTYLED_META, true);
257
632
  view.dispatch(tr.scrollIntoView());
258
633
  } catch {
259
634
  return true;
@@ -268,6 +643,10 @@ function applyPPrDel(view, targetParagraphPos, pluginState) {
268
643
  info: makeParagraphMarkInfo(pluginState)
269
644
  };
270
645
  tr.setNodeAttribute(targetParagraphPos, "pPrMark", markInfo);
646
+ carryIntoParagraphLeft(view.state, tr, targetNode, paragraphLeftAfter({
647
+ doc: tr.doc,
648
+ paragraphPos: targetParagraphPos
649
+ }) ?? targetParagraphPos + targetNode.nodeSize, markInfo.info);
271
650
  view.dispatch(tr.scrollIntoView());
272
651
  return true;
273
652
  }
@@ -291,7 +670,11 @@ function handleSuggestionDelete(state, dispatch, direction) {
291
670
  const tr = state.tr;
292
671
  tr.setMeta(SUGGESTION_META, true);
293
672
  if (!empty) {
294
- markRangeAsDeleted(tr, state.doc, $from.pos, $to.pos, insertionType, deletionType, pluginState);
673
+ const ranges = state.selection.ranges.map(({ $from: rangeFrom, $to: rangeTo }) => expandNoteReferenceDeletionRange(state.doc, rangeFrom.pos, rangeTo.pos) ?? {
674
+ from: rangeFrom.pos,
675
+ to: rangeTo.pos
676
+ }).sort((a, b) => b.from - a.from);
677
+ for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, insertionType, deletionType, pluginState, state);
295
678
  const cursorPos = tr.mapping.map($to.pos);
296
679
  tr.setSelection(TextSelection.near(tr.doc.resolve(cursorPos)));
297
680
  dispatch(tr.scrollIntoView());
@@ -301,22 +684,27 @@ function handleSuggestionDelete(state, dispatch, direction) {
301
684
  const deletePos = isBackward ? $from.pos - 1 : $from.pos;
302
685
  const deleteEnd = isBackward ? $from.pos : $from.pos + 1;
303
686
  if (deletePos < 0 || deleteEnd > state.doc.content.size) return true;
304
- const nodeAfter = state.doc.resolve(deletePos).nodeAfter;
687
+ const noteRange = expandNoteReferenceDeletionRange(state.doc, deletePos, deleteEnd);
688
+ const rangeFrom = noteRange?.from ?? deletePos;
689
+ const rangeTo = noteRange?.to ?? deleteEnd;
690
+ const nodeAfter = state.doc.resolve(rangeFrom).nodeAfter;
305
691
  if (!nodeAfter || !canCarryTrackedRunMark(nodeAfter)) return false;
306
692
  const hasOwnInsertion = nodeAfter.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
307
693
  if (nodeAfter.marks.some((m) => m.type === deletionType)) {
308
- const newPos = isBackward ? deletePos : deleteEnd;
694
+ const newPos = isBackward ? rangeFrom : rangeTo;
309
695
  tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
310
- } else if (hasOwnInsertion) tr.delete(deletePos, deleteEnd);
696
+ } else if (hasOwnInsertion) tr.delete(rangeFrom, rangeTo);
311
697
  else {
312
- const delAttrs = findAdjacentRevisionForRange(state.doc, deletePos, deleteEnd, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
313
- tr.addMark(deletePos, deleteEnd, deletionType.create(delAttrs));
314
- const newPos = isBackward ? deletePos : deleteEnd;
698
+ const delAttrs = findAdjacentRevisionForRange(state.doc, rangeFrom, rangeTo, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
699
+ tr.addMark(rangeFrom, rangeTo, deletionType.create(delAttrs));
700
+ const newPos = isBackward ? rangeFrom : rangeTo;
315
701
  tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
316
702
  }
317
703
  dispatch(tr.scrollIntoView());
318
704
  return true;
319
705
  }
706
+ /** Apply the editor's selected-content deletion path for a host cut action. */
707
+ const deleteSelectionAsSuggestion = (state, dispatch) => state.selection.empty ? false : handleSuggestionDelete(state, dispatch, "forward");
320
708
  /**
321
709
  * Mark the text committed by an IME composition as a tracked insertion.
322
710
  *
@@ -329,28 +717,66 @@ function handleSuggestionDelete(state, dispatch, direction) {
329
717
  * committed range safely. `markRangeAsInserted` skips nodes that already carry a
330
718
  * tracked-change mark, so this is idempotent. eigenpal/docx-editor#938.
331
719
  */
332
- function markComposedAsInsertion(view, from, pluginState) {
720
+ function markComposedAsInsertion(view, from, pluginState, replaced) {
333
721
  const insertionType = view.state.schema.marks["insertion"];
334
722
  const deletionType = view.state.schema.marks["deletion"];
335
723
  if (!insertionType || !deletionType) return;
336
724
  const to = view.state.selection.to;
337
- if (to <= from) return;
725
+ if (to <= from && !replaced) return;
338
726
  const tr = view.state.tr;
339
727
  tr.setMeta(SUGGESTION_META, true);
340
- const markAttrs = findAdjacentRevisionForRange(view.state.doc, from, to, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
341
- markRangeAsInserted(tr, view.state.doc, from, to, insertionType, deletionType, markAttrs);
728
+ if (replaced) {
729
+ tr.insert(from, replaced.content);
730
+ markRangeAsDeleted(tr, tr.doc, from, from + replaced.content.size, insertionType, deletionType, pluginState);
731
+ }
732
+ const insertionFrom = tr.mapping.map(from, 1);
733
+ const insertionTo = tr.mapping.map(to, 1);
734
+ if (insertionTo <= insertionFrom) {
735
+ if (tr.steps.length > 0) view.dispatch(tr);
736
+ return;
737
+ }
738
+ const markAttrs = findAdjacentRevisionForRange(tr.doc, insertionFrom, insertionTo, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
739
+ markRangeAsInserted(tr, tr.doc, insertionFrom, insertionTo, insertionType, deletionType, markAttrs);
342
740
  if (tr.steps.length === 0) return;
343
- const caret = tr.mapping.map(to);
741
+ const caret = tr.mapping.map(to, 1);
344
742
  tr.setSelection(TextSelection.create(tr.doc, caret));
345
743
  view.dispatch(tr);
346
744
  }
347
745
  /**
746
+ * Paragraphs inserted into one (a paste at the caret) split it: its break ends
747
+ * the last part, which reads as the last inserted paragraph. Rejecting the
748
+ * inserted breaks leaves that part, so it records what the paragraph read as.
749
+ */
750
+ function recordSplitParagraph(tr, before, step, insertedFrom, insertedTo, attrs) {
751
+ if (!(step instanceof ReplaceStep) || !before) return;
752
+ const $at = before.resolve(step.from);
753
+ if ($at.parent.type.name !== "paragraph" || step.to > $at.after() || before.textBetween(step.from, step.to) !== "") return;
754
+ const split = $at.parent;
755
+ const $first = tr.doc.resolve(insertedFrom);
756
+ const $last = tr.doc.resolve(insertedTo);
757
+ let lastPos = null;
758
+ if ($last.parent.type.name === "paragraph") lastPos = $last.before();
759
+ else if ($last.nodeBefore?.type.name === "paragraph") lastPos = insertedTo - $last.nodeBefore.nodeSize;
760
+ if ($first.parent.type.name !== "paragraph" || lastPos === null || lastPos === $first.before()) return;
761
+ recordReplacedParagraphProperties({
762
+ tr,
763
+ position: lastPos,
764
+ replaced: split,
765
+ revision: {
766
+ id: attrs.revisionId,
767
+ author: attrs.author,
768
+ date: attrs.date
769
+ }
770
+ });
771
+ }
772
+ /**
348
773
  * Create the suggestion mode plugin.
349
774
  * When active, text edits become tracked changes.
350
775
  */
351
776
  function createSuggestionModePlugin(initialActive = false, author = "User") {
352
777
  let composing = false;
353
778
  let compositionFrom = null;
779
+ let compositionReplaced = null;
354
780
  return new Plugin({
355
781
  key: suggestionModeKey,
356
782
  state: {
@@ -372,32 +798,32 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
372
798
  },
373
799
  props: {
374
800
  handleDOMEvents: {
801
+ cut(view, event) {
802
+ if (!suggestionModeKey.getState(view.state)?.active || view.state.selection.empty) return false;
803
+ const data = event.clipboardData;
804
+ if (!data) return false;
805
+ const { dom, text } = view.serializeForClipboard(view.state.selection.content());
806
+ data.clearData();
807
+ data.setData("text/html", dom.innerHTML);
808
+ data.setData("text/plain", text);
809
+ event.preventDefault();
810
+ return handleSuggestionDelete(view.state, view.dispatch, "forward");
811
+ },
375
812
  compositionstart(view) {
376
- const pluginState = suggestionModeKey.getState(view.state);
377
- if (!pluginState?.active) return false;
813
+ if (!suggestionModeKey.getState(view.state)?.active) return false;
814
+ if (composing) return false;
378
815
  composing = true;
379
816
  const { from, to } = view.state.selection;
380
- if (from !== to) {
381
- const insertionType = view.state.schema.marks["insertion"];
382
- const deletionType = view.state.schema.marks["deletion"];
383
- if (insertionType && deletionType) {
384
- const tr = view.state.tr;
385
- tr.setMeta(SUGGESTION_META, true);
386
- markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState);
387
- const caret = tr.mapping.map(to);
388
- tr.setSelection(TextSelection.create(tr.doc, caret));
389
- view.dispatch(tr);
390
- compositionFrom = caret;
391
- return false;
392
- }
393
- }
394
817
  compositionFrom = from;
818
+ compositionReplaced = from === to ? null : view.state.doc.slice(from, to);
395
819
  return false;
396
820
  },
397
821
  compositionend(view) {
398
822
  const pluginState = suggestionModeKey.getState(view.state);
399
823
  const from = compositionFrom;
824
+ const replaced = compositionReplaced;
400
825
  compositionFrom = null;
826
+ compositionReplaced = null;
401
827
  if (!pluginState?.active || from == null) {
402
828
  composing = false;
403
829
  return false;
@@ -405,7 +831,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
405
831
  queueMicrotask(() => {
406
832
  try {
407
833
  const current = suggestionModeKey.getState(view.state);
408
- if (current?.active) markComposedAsInsertion(view, from, current);
834
+ if (current?.active) markComposedAsInsertion(view, from, current, replaced);
409
835
  } finally {
410
836
  composing = false;
411
837
  }
@@ -420,8 +846,15 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
420
846
  handleKeyDown(view, event) {
421
847
  const pluginState = suggestionModeKey.getState(view.state);
422
848
  if (!pluginState?.active) return false;
423
- if (event.key === "Enter") return handleSuggestionEnter(view, pluginState);
849
+ if (event.key === "Enter" && !event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {
850
+ const { $from, empty } = view.state.selection;
851
+ if (empty && $from.parent.type.name === "paragraph" && expectParagraphAttrs($from.parent).numPr?.kind === "reference") return false;
852
+ return handleSuggestionEnter(view, pluginState);
853
+ }
854
+ if (event.key === "Backspace" && undoInputRule(view.state, view.dispatch)) return true;
424
855
  if (event.key === "Backspace" || event.key === "Delete") {
856
+ const { $from, empty } = view.state.selection;
857
+ if (event.key === "Backspace" && empty && $from.parentOffset === 0 && $from.parent.type.name === "paragraph" && expectParagraphAttrs($from.parent).numPr?.kind === "reference") return false;
425
858
  const boundaryTarget = paragraphBoundaryTarget(view.state, event.key === "Backspace" ? "backward" : "forward");
426
859
  if (boundaryTarget !== null) return applyPPrDel(view, boundaryTarget, pluginState);
427
860
  const direction = event.key === "Backspace" ? "backward" : "forward";
@@ -438,7 +871,7 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
438
871
  handlePaste(view, _event, slice) {
439
872
  const pluginState = suggestionModeKey.getState(view.state);
440
873
  if (!pluginState?.active) return false;
441
- return handleSuggestionPaste(view, slice, pluginState);
874
+ return handleSuggestionTableCellPaste(view, slice, pluginState) || handleSuggestionPaste(view, slice, pluginState);
442
875
  }
443
876
  },
444
877
  appendTransaction(transactions, _oldState, newState) {
@@ -461,20 +894,14 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
461
894
  const newFrom = following.map(stepFrom, -1);
462
895
  const newTo = following.map(stepTo, 1);
463
896
  if (newTo > newFrom) {
464
- newState.doc.nodesBetween(newFrom, newTo, (node, pos) => {
465
- if (!canCarryTrackedRunMark(node)) return;
466
- if (!node.marks.some((m) => m.type === insertionType || deletionType && m.type === deletionType)) {
467
- const nodeStart = Math.max(pos, newFrom);
468
- const nodeEnd = Math.min(pos + node.nodeSize, newTo);
469
- tr.addMark(nodeStart, nodeEnd, insertionType.create(markAttrs));
470
- }
471
- });
472
- encloseWholeControls({
897
+ if (userTr.getMeta("paste")) enclosePastedRunRevisions({
473
898
  tr,
474
899
  from: newFrom,
475
900
  to: newTo,
476
- revisionId: markAttrs.revisionId
901
+ revision: markAttrs
477
902
  });
903
+ if (deletionType) markRangeAsInserted(tr, newState.doc, newFrom, newTo, insertionType, deletionType, markAttrs);
904
+ recordSplitParagraph(tr, userTr.docs[stepIndex], step, newFrom, newTo, markAttrs);
478
905
  }
479
906
  });
480
907
  }
@@ -509,4 +936,4 @@ function isSuggestionModeActive(state) {
509
936
  return suggestionModeKey.getState(state)?.active ?? false;
510
937
  }
511
938
  //#endregion
512
- export { SUGGESTION_BYPASS_META, SUGGESTION_META, createSuggestionModePlugin, handleSuggestionEnter, handleSuggestionPaste, isSuggestionModeActive, makeRevisionInfo, paragraphBoundaryTarget, setSuggestionMode, suggestionModeKey, toggleSuggestionMode };
939
+ export { SUGGESTED_TEXT_INPUT_META, SUGGESTION_BYPASS_META, SUGGESTION_META, createSuggestionModePlugin, deleteSelectionAsSuggestion, handleSuggestionEnter, handleSuggestionPaste, handleSuggestionTableCellPaste, isSuggestionModeActive, makeRevisionInfo, paragraphBoundaryTarget, setSuggestionMode, suggestRangeDeletion, suggestionModeKey, toggleSuggestionMode };