@stll/folio-core 0.54.2 → 0.56.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 (267) hide show
  1. package/dist/ai-edits/apply.js +185 -33
  2. package/dist/ai-edits/headless.js +3 -2
  3. package/dist/ai-edits/types.d.ts +7 -1
  4. package/dist/compare/compare.js +1 -0
  5. package/dist/compare/content-types.d.ts +3 -15
  6. package/dist/compare/reproducible-package.d.ts +0 -17
  7. package/dist/compare/reproducible-package.js +12 -18
  8. package/dist/controller/canonicalClipboard.d.ts +23 -0
  9. package/dist/controller/canonicalClipboard.js +476 -0
  10. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  11. package/dist/controller/canonicalClipboardResources.js +382 -0
  12. package/dist/controller/canonicalComposition.d.ts +53 -0
  13. package/dist/controller/canonicalComposition.js +296 -0
  14. package/dist/controller/canonicalInlineProjection.d.ts +113 -0
  15. package/dist/controller/canonicalInlineProjection.js +393 -0
  16. package/dist/controller/canonicalInput.d.ts +54 -0
  17. package/dist/controller/canonicalInput.js +377 -0
  18. package/dist/controller/canonicalInputTimer.d.ts +7 -0
  19. package/dist/controller/canonicalInputTimer.js +11 -0
  20. package/dist/controller/canonicalOperations.d.ts +22 -0
  21. package/dist/controller/canonicalOperations.js +85 -0
  22. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  23. package/dist/controller/canonicalPublicOperations.js +556 -0
  24. package/dist/controller/canonicalReview.d.ts +12 -0
  25. package/dist/controller/canonicalReview.js +19 -0
  26. package/dist/controller/canonicalSession.d.ts +167 -0
  27. package/dist/controller/canonicalSession.js +1186 -0
  28. package/dist/controller/canonicalStoryEditor.d.ts +36 -0
  29. package/dist/controller/canonicalStoryEditor.js +87 -0
  30. package/dist/controller/canonicalStructure.d.ts +16 -0
  31. package/dist/controller/canonicalStructure.js +322 -0
  32. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  33. package/dist/controller/canonicalTocSelection.js +43 -0
  34. package/dist/controller/folioEditor.d.ts +3 -10
  35. package/dist/controller/folioEditor.js +14 -4
  36. package/dist/controller/headerFooterEditorManager.d.ts +5 -0
  37. package/dist/controller/headerFooterEditorManager.js +72 -3
  38. package/dist/controller/hiddenEditorApi.d.ts +45 -0
  39. package/dist/controller/hiddenEditorApi.js +29 -8
  40. package/dist/controller/hiddenEditorManager.d.ts +13 -2
  41. package/dist/controller/hiddenEditorManager.js +639 -31
  42. package/dist/controller/layoutPipeline.d.ts +1 -1
  43. package/dist/controller/layoutPipeline.js +2 -0
  44. package/dist/controller/noteEditorManager.d.ts +5 -0
  45. package/dist/controller/noteEditorManager.js +114 -15
  46. package/dist/document-operations.d.ts +8 -1
  47. package/dist/document-operations.js +6 -4
  48. package/dist/docx/attributeRemainder.js +1 -0
  49. package/dist/docx/blockPlainText.d.ts +1 -1
  50. package/dist/docx/blockPlainText.js +11 -3
  51. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  52. package/dist/docx/canonicalResourceSave.js +27 -0
  53. package/dist/docx/canonicalSave.d.ts +29 -0
  54. package/dist/docx/canonicalSave.js +101 -0
  55. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  56. package/dist/docx/canonicalSessionInput.js +27 -0
  57. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  58. package/dist/docx/canonicalStoryRepack.js +74 -0
  59. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  60. package/dist/docx/contentTypeRegistry.js +64 -0
  61. package/dist/docx/documentParser.d.ts +1 -13
  62. package/dist/docx/documentParser.js +1 -47
  63. package/dist/docx/ensureParaIds.d.ts +7 -1
  64. package/dist/docx/ensureParaIds.js +173 -215
  65. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  66. package/dist/docx/foldedListNumberFields.js +267 -0
  67. package/dist/docx/footnoteParser.d.ts +1 -11
  68. package/dist/docx/footnoteParser.js +1 -16
  69. package/dist/docx/headerFooterParser.d.ts +1 -9
  70. package/dist/docx/headerFooterParser.js +1 -12
  71. package/dist/docx/headerFooterVerbatim.d.ts +39 -1
  72. package/dist/docx/headerFooterVerbatim.js +173 -2
  73. package/dist/docx/imageParser.d.ts +13 -1
  74. package/dist/docx/imageParser.js +20 -11
  75. package/dist/docx/metadataPrivacy.js +6 -1
  76. package/dist/docx/noteIds.d.ts +5 -0
  77. package/dist/docx/noteIds.js +6 -2
  78. package/dist/docx/noteReferenceMark.d.ts +3 -1
  79. package/dist/docx/noteReferenceMark.js +15 -14
  80. package/dist/docx/numberingParser.d.ts +3 -1
  81. package/dist/docx/numberingParser.js +17 -3
  82. package/dist/docx/packageParts.d.ts +3 -1
  83. package/dist/docx/packageParts.js +74 -19
  84. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  85. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  86. package/dist/docx/paragraphParser.js +101 -53
  87. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  88. package/dist/docx/paragraphPropertySource.js +77 -22
  89. package/dist/docx/parser.js +3 -1
  90. package/dist/docx/removeHeaderFooterParts.js +17 -6
  91. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  92. package/dist/docx/reviewHistoryNamespace.js +4 -0
  93. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  94. package/dist/docx/reviewResolutionProvenance.js +153 -0
  95. package/dist/docx/rezip.d.ts +54 -6
  96. package/dist/docx/rezip.js +835 -257
  97. package/dist/docx/runConsolidator.d.ts +2 -58
  98. package/dist/docx/runConsolidator.js +3 -175
  99. package/dist/docx/runParser.d.ts +2 -1
  100. package/dist/docx/runParser.js +156 -116
  101. package/dist/docx/saveDiagnostics.d.ts +22 -0
  102. package/dist/docx/saveDiagnostics.js +0 -0
  103. package/dist/docx/selectiveSave.d.ts +3 -1
  104. package/dist/docx/selectiveSave.js +36 -4
  105. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  106. package/dist/docx/selectiveXmlPatch.js +81 -7
  107. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  108. package/dist/docx/serializer/documentSerializer.js +29 -10
  109. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  110. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  111. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  112. package/dist/docx/serializer/noteSerializer.js +24 -8
  113. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  114. package/dist/docx/serializer/partNamespaces.js +2 -1
  115. package/dist/docx/serializer/runSerializer.js +3 -1
  116. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  117. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  118. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  119. package/dist/docx/settingsParser.d.ts +1 -2
  120. package/dist/docx/settingsParser.js +5 -6
  121. package/dist/docx/storyBlockReplay.d.ts +31 -0
  122. package/dist/docx/storyBlockReplay.js +471 -0
  123. package/dist/docx/storyPlainText.d.ts +20 -0
  124. package/dist/docx/storyPlainText.js +23 -0
  125. package/dist/docx/streamingXmlParser.d.ts +22 -1
  126. package/dist/docx/streamingXmlParser.js +49 -3
  127. package/dist/docx/structuralXmlPatch.js +18 -23
  128. package/dist/docx/styleParser.d.ts +5 -1
  129. package/dist/docx/styleParser.js +1 -1
  130. package/dist/docx/tableParser.d.ts +1 -8
  131. package/dist/docx/tableParser.js +1 -28
  132. package/dist/docx/textBoxParser.d.ts +1 -5
  133. package/dist/docx/textBoxParser.js +1 -20
  134. package/dist/docx/unzip.js +6 -1
  135. package/dist/docx/verbatimCapture.d.ts +3 -1
  136. package/dist/docx/verbatimCapture.js +49 -6
  137. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  138. package/dist/i18n/messages/ar.gen.js +656 -0
  139. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  140. package/dist/i18n/messages/cs.gen.js +656 -0
  141. package/dist/i18n/messages/de.gen.d.ts +5 -0
  142. package/dist/i18n/messages/de.gen.js +656 -0
  143. package/dist/i18n/messages/en.gen.d.ts +5 -0
  144. package/dist/i18n/messages/en.gen.js +656 -0
  145. package/dist/i18n/messages/es.gen.d.ts +5 -0
  146. package/dist/i18n/messages/es.gen.js +656 -0
  147. package/dist/i18n/messages/et.gen.d.ts +5 -0
  148. package/dist/i18n/messages/et.gen.js +656 -0
  149. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  150. package/dist/i18n/messages/fr.gen.js +656 -0
  151. package/dist/i18n/messages/he.gen.d.ts +5 -0
  152. package/dist/i18n/messages/he.gen.js +656 -0
  153. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  154. package/dist/i18n/messages/hi.gen.js +656 -0
  155. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  156. package/dist/i18n/messages/hu.gen.js +656 -0
  157. package/dist/i18n/messages/locales.d.ts +5 -0
  158. package/dist/i18n/messages/locales.js +22 -0
  159. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  160. package/dist/i18n/messages/lt.gen.js +656 -0
  161. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  162. package/dist/i18n/messages/lv.gen.js +656 -0
  163. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  164. package/dist/i18n/messages/pl.gen.js +656 -0
  165. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  166. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  167. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  168. package/dist/i18n/messages/sk.gen.js +656 -0
  169. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  170. package/dist/i18n/messages/tr.gen.js +656 -0
  171. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  172. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  173. package/dist/i18n/messages.d.ts +2 -3
  174. package/dist/i18n/messages.js +1 -19
  175. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  176. package/dist/internal/acceptedBlockProjection.js +45 -0
  177. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  178. package/dist/internal/noteMarkerAttrs.js +16 -0
  179. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  180. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  181. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  182. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  183. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  184. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  185. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  186. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  187. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  188. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  189. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  190. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  191. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  192. package/dist/managers/DocumentLoaderManager.js +9 -1
  193. package/dist/markdown/escape.d.ts +3 -1
  194. package/dist/markdown/escape.js +9 -2
  195. package/dist/markdown/renderBlock.js +20 -121
  196. package/dist/markdown/renderParagraph.js +13 -15
  197. package/dist/markdown/renderRuns.d.ts +7 -1
  198. package/dist/markdown/renderRuns.js +35 -15
  199. package/dist/markdown/renderTable.js +2 -0
  200. package/dist/markdown/types.d.ts +3 -9
  201. package/dist/prosemirror/attrs/index.d.ts +2 -12
  202. package/dist/prosemirror/attrs/index.js +22 -48
  203. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  204. package/dist/prosemirror/canonicalCommands.d.ts +121 -0
  205. package/dist/prosemirror/canonicalCommands.js +35 -0
  206. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  207. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  208. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  209. package/dist/prosemirror/clipboardIntent.js +9 -0
  210. package/dist/prosemirror/commands/pageBreak.js +15 -1
  211. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  212. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  213. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  214. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  215. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  216. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  217. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  218. package/dist/prosemirror/conversion/toProseDoc.d.ts +11 -4
  219. package/dist/prosemirror/conversion/toProseDoc.js +55 -14
  220. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  221. package/dist/prosemirror/executeEditorCommand.js +26 -0
  222. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  223. package/dist/prosemirror/extensions/core/ParagraphExtension.js +88 -48
  224. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  225. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  226. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  227. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +56 -28
  228. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  229. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  230. package/dist/prosemirror/extensions/marks/markUtils.js +72 -8
  231. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  232. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  233. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  234. package/dist/prosemirror/insertOperations.js +2 -1
  235. package/dist/prosemirror/listMarker.d.ts +1 -0
  236. package/dist/prosemirror/listMarker.js +16 -7
  237. package/dist/prosemirror/listNumbering.js +26 -5
  238. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  239. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  240. package/dist/prosemirror/markupViewNotes.js +3 -2
  241. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  242. package/dist/prosemirror/noteReferenceReview.js +5 -3
  243. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  244. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  245. package/dist/prosemirror/plugins/suggestionMode.js +57 -7
  246. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  247. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  248. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  249. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  250. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  251. package/dist/prosemirror/schema/marks.d.ts +2 -0
  252. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  253. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  254. package/dist/prosemirror/textInput.js +5 -1
  255. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  256. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  257. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  258. package/dist/prosemirror/validation.js +4 -0
  259. package/dist/types/canonicalCapabilities.d.ts +215 -0
  260. package/dist/types/canonicalCapabilities.js +212 -0
  261. package/dist/types/canonicalSave.d.ts +11 -0
  262. package/dist/types/canonicalSave.js +0 -0
  263. package/dist/types/content.d.ts +2 -2
  264. package/dist/types/docxSerialization.d.ts +14 -0
  265. package/dist/types/docxSerialization.js +7 -0
  266. package/dist/utils/createDocument.js +2 -2
  267. package/package.json +16 -4
@@ -0,0 +1,556 @@
1
+ import { splitsGraphemeCluster, splitsSurrogatePair } from "../ai-edits/character-boundaries.js";
2
+ import { buildCleanBlockText, resolveCleanTextRange } from "../ai-edits/clean-text.js";
3
+ import { stripInlineEmphasisMarkers } from "../ai-edits/inline-emphasis.js";
4
+ import { collectNoteReferenceLabels, hashFolioAIBlockText } from "../ai-edits/snapshot.js";
5
+ import { wordDiffSessionFromOptions } from "../ai-edits/word-diff.js";
6
+ import { getFolioDocumentOperationIssues, getFolioDocumentOperationReceiptsForStory, parseFolioDocumentOperationBatch } from "../document-operations.js";
7
+ import { maxAnnotationIdInDoc } from "../prosemirror/plugins/revisionIds.js";
8
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
9
+ import { Result, TaggedError, panic } from "better-result";
10
+ import { Mark } from "prosemirror-model";
11
+ import { OP_STORIES, applyDocumentOps, compileEditorIntent, createEditorIntentIdAllocator, documentStories } from "@stll/docx-core/ops";
12
+ //#region src/controller/canonicalPublicOperations.ts
13
+ /** Public batches compile against the read projection; only the canonical journal publishes. */
14
+ /** A new public kind cannot bypass a canonical compiler/refusal decision. */
15
+ const CANONICAL_PUBLIC_OPERATION_DISPOSITIONS = {
16
+ replaceInBlock: "compile",
17
+ replaceRange: "compile",
18
+ replaceBlock: "compile",
19
+ splitBlock: "compile",
20
+ formatRange: "compile",
21
+ mergeBlockWithNext: "compile",
22
+ setBlockParagraphProperties: CANONICAL_GAP.publicUnsupportedInline,
23
+ insertAfterBlock: CANONICAL_GAP.publicUnsupportedInline,
24
+ insertBeforeBlock: CANONICAL_GAP.publicUnsupportedInline,
25
+ deleteBlock: CANONICAL_GAP.publicUnsupportedInline,
26
+ commentOnBlock: CANONICAL_GAP.publicComments,
27
+ commentOnRange: CANONICAL_GAP.publicComments,
28
+ insertTable: CANONICAL_GAP.publicTableProjection,
29
+ insertSignatureTable: CANONICAL_GAP.publicTableProjection,
30
+ deleteTable: CANONICAL_GAP.publicTableProjection,
31
+ insertTableRow: CANONICAL_GAP.publicTableProjection,
32
+ deleteTableRow: CANONICAL_GAP.publicTableProjection,
33
+ insertTableColumn: CANONICAL_GAP.publicTableProjection,
34
+ deleteTableColumn: CANONICAL_GAP.publicTableProjection,
35
+ mergeTableCells: CANONICAL_GAP.publicTableProjection,
36
+ splitTableCell: CANONICAL_GAP.publicTableProjection
37
+ };
38
+ const compilePublicIntents = ({ document, intents, mode, author, date, nextRevisionId, allocate }) => {
39
+ const ops = [];
40
+ const revisions = [];
41
+ for (const sourceIntent of intents) {
42
+ const ids = allocate(document, sourceIntent);
43
+ const intent = sourceIntent.type === "splitParagraph" ? {
44
+ type: "splitParagraph",
45
+ at: sourceIntent.at,
46
+ ...sourceIntent.to === void 0 ? {} : { to: sourceIntent.to },
47
+ newBlockId: ids.newBlockId
48
+ } : sourceIntent;
49
+ const firstRevisionId = nextRevisionId;
50
+ const compiled = compileEditorIntent(document, {
51
+ intent,
52
+ mode: mode === "direct" ? {
53
+ type: "editing",
54
+ newIds: ids.newIds
55
+ } : {
56
+ type: "suggesting",
57
+ revision: {
58
+ id: firstRevisionId,
59
+ author,
60
+ date
61
+ },
62
+ newIds: {
63
+ control: ids.newIds.control,
64
+ revision: ids.newIds.revision.map((_, index) => firstRevisionId + index + 1)
65
+ }
66
+ }
67
+ });
68
+ if (compiled.isErr()) return Result.err(compiled.error);
69
+ const applied = applyDocumentOps(document, compiled.value.ops);
70
+ if (applied.isErr()) return Result.err(applied.error);
71
+ document = applied.value.document;
72
+ ops.push(...compiled.value.ops);
73
+ revisions.push(...applied.value.revisions);
74
+ for (const id of applied.value.revisions) nextRevisionId = Math.max(nextRevisionId, id + 1);
75
+ }
76
+ return Result.ok({
77
+ document,
78
+ ops,
79
+ revisions,
80
+ nextRevisionId
81
+ });
82
+ };
83
+ var CanonicalPublicOperationResolutionError = class extends TaggedError("CanonicalPublicOperationResolutionError") {};
84
+ const canonicalPublicStory = (story) => {
85
+ if (story === "main") return story;
86
+ switch (story.type) {
87
+ case "header":
88
+ case "footer": return {
89
+ kind: story.type,
90
+ rId: story.relationshipId
91
+ };
92
+ case "footnote":
93
+ case "endnote": return {
94
+ kind: story.type,
95
+ id: story.noteId
96
+ };
97
+ default: return panic(`Unknown public operation story ${story}`);
98
+ }
99
+ };
100
+ const batchStatus = (batch, skippedCount) => {
101
+ if (batch.dryRun) return "previewed";
102
+ if (batch.atomic && skippedCount > 0) return "rejected";
103
+ return "committed";
104
+ };
105
+ /** Handles address isolated journal groups; no second document or PM history is retained. */
106
+ var CanonicalPublicOperations = class {
107
+ options;
108
+ handles = [];
109
+ nextHandle = 1;
110
+ handleNamespace = crypto.randomUUID();
111
+ constructor(options) {
112
+ this.options = options;
113
+ }
114
+ apply(options) {
115
+ const batch = parseFolioDocumentOperationBatch(options.batch);
116
+ const { session } = this.options;
117
+ const publicStory = options.story ?? "main";
118
+ const story = canonicalPublicStory(publicStory);
119
+ const state = this.options.getState(story);
120
+ const projected = session.projectStory(story);
121
+ const skipped = [];
122
+ const normalizations = [];
123
+ const refusals = /* @__PURE__ */ new Map();
124
+ const resolved = [];
125
+ const { diff } = wordDiffSessionFromOptions(options.wordDiff);
126
+ let nextRevisionId = options.revisionStamp?.idSeed ?? 1;
127
+ if (!session.isComposing && options.revisionStamp === void 0) for (const currentStory of documentStories(session.document)) {
128
+ const currentProjection = session.projectStory(currentStory);
129
+ if (currentProjection.isOk()) nextRevisionId = Math.max(nextRevisionId, maxAnnotationIdInDoc(currentProjection.value.doc) + 1);
130
+ }
131
+ const skip = (id, reason, message) => {
132
+ skipped.push({
133
+ id,
134
+ reason,
135
+ ...message === void 0 ? {} : { message }
136
+ });
137
+ };
138
+ const result = (applied, undoHandle) => {
139
+ const failures = new Map(skipped.map((item) => [item.id, item]));
140
+ const ordered = batch.operations.flatMap(({ id }) => {
141
+ const failure = failures.get(id);
142
+ if (!failure) return [];
143
+ const canonicalRefusal = refusals.get(id);
144
+ return [canonicalRefusal ? {
145
+ ...failure,
146
+ canonicalRefusal
147
+ } : failure];
148
+ });
149
+ return {
150
+ version: 1,
151
+ status: batchStatus(batch, ordered.length),
152
+ applied,
153
+ skipped: ordered,
154
+ issues: getFolioDocumentOperationIssues(batch.operations, ordered),
155
+ receipts: getFolioDocumentOperationReceiptsForStory({
156
+ operations: batch.operations,
157
+ applied,
158
+ story: publicStory
159
+ }),
160
+ undoHandle,
161
+ nextRevisionId,
162
+ ...normalizations.length === 0 ? {} : { normalizations }
163
+ };
164
+ };
165
+ for (const operation of batch.operations) {
166
+ if (story !== OP_STORIES.MAIN) {
167
+ refusals.set(operation.id, { gap: CANONICAL_GAP.publicSecondaryStories });
168
+ skip(operation.id, "unsupportedBlock", "Canonical public secondary-story routing is unavailable.");
169
+ continue;
170
+ }
171
+ if (!state || projected.isErr() || session.isComposing) {
172
+ skip(operation.id, "documentNotEditable");
173
+ continue;
174
+ }
175
+ if (batch.mode === "suggested") {
176
+ refusals.set(operation.id, { gap: CANONICAL_GAP.publicSuggestedMode });
177
+ skip(operation.id, "unsupportedMode", "Canonical pending suggestions are unavailable.");
178
+ continue;
179
+ }
180
+ if (options.tableTemplates !== void 0 || options.undefinedStyles === "keep") {
181
+ refusals.set(operation.id, { gap: CANONICAL_GAP.publicUnsupportedInline });
182
+ skip(operation.id, "unsupportedBlock", "This canonical compiler does not support the supplied execution options.");
183
+ continue;
184
+ }
185
+ const disposition = CANONICAL_PUBLIC_OPERATION_DISPOSITIONS[operation.type];
186
+ if (disposition !== "compile" || "comment" in operation && operation.comment !== void 0) {
187
+ refusals.set(operation.id, { gap: disposition === "compile" ? CANONICAL_GAP.publicComments : disposition });
188
+ skip(operation.id, "unsupportedBlock");
189
+ continue;
190
+ }
191
+ const compiled = this.resolve({
192
+ operation,
193
+ options,
194
+ state,
195
+ story,
196
+ diff
197
+ });
198
+ if (compiled.isErr()) {
199
+ if (compiled.error.reason === "unsupportedBlock") refusals.set(operation.id, { gap: CANONICAL_GAP.publicUnsupportedInline });
200
+ skip(operation.id, compiled.error.reason, compiled.error.message);
201
+ continue;
202
+ }
203
+ if (resolved.some(({ from, to }) => compiled.value.from < to && compiled.value.to > from || from === to && compiled.value.from <= from && compiled.value.to >= from || compiled.value.from === compiled.value.to && compiled.value.from >= from && compiled.value.from <= to)) {
204
+ skip(operation.id, "overlappingOperation");
205
+ continue;
206
+ }
207
+ resolved.push(compiled.value);
208
+ }
209
+ if (batch.atomic && skipped.length > 0) {
210
+ for (const { operation } of resolved) skip(operation.id, "atomicBatchRejected");
211
+ return result([], null);
212
+ }
213
+ if (!state || projected.isErr() || session.isComposing) return result([], null);
214
+ resolved.sort((left, right) => right.from - left.from);
215
+ let document = session.document;
216
+ const ops = [];
217
+ const appliedById = /* @__PURE__ */ new Map();
218
+ const allocate = createEditorIntentIdAllocator();
219
+ const date = options.revisionStamp?.date ?? (/* @__PURE__ */ new Date()).toISOString();
220
+ for (const { operation, intents, normalizations: adjustments } of resolved) {
221
+ const compiled = compilePublicIntents({
222
+ document,
223
+ intents,
224
+ allocate,
225
+ nextRevisionId,
226
+ mode: batch.mode === "direct" ? "direct" : "tracked-changes",
227
+ author: options.author ?? "AI",
228
+ date
229
+ });
230
+ if (compiled.isErr()) {
231
+ refusals.set(operation.id, { gap: CANONICAL_GAP.publicUnsupportedInline });
232
+ skip(operation.id, "unsupportedBlock", compiled.error.message);
233
+ continue;
234
+ }
235
+ if (compiled.value.ops.length === 0) {
236
+ skip(operation.id, "noopOperation");
237
+ continue;
238
+ }
239
+ normalizations.push(...adjustments);
240
+ document = compiled.value.document;
241
+ ops.push(...compiled.value.ops);
242
+ nextRevisionId = compiled.value.nextRevisionId;
243
+ const revisionIds = compiled.value.revisions;
244
+ const revisionId = revisionIds.at(0);
245
+ appliedById.set(operation.id, {
246
+ id: operation.id,
247
+ ...revisionId === void 0 ? {} : {
248
+ revisionId,
249
+ revisionIds
250
+ }
251
+ });
252
+ }
253
+ if (batch.atomic && skipped.length > 0) {
254
+ for (const { id } of appliedById.values()) skip(id, "atomicBatchRejected");
255
+ return result([], null);
256
+ }
257
+ if (ops.length === 0) return result([], null);
258
+ const selection = projected.value.selectionAt(state);
259
+ const prepared = selection.isErr() ? selection : session.prepareOps(state, ops, selection.value);
260
+ if (prepared.isErr()) {
261
+ for (const { id } of appliedById.values()) {
262
+ refusals.set(id, { gap: CANONICAL_GAP.publicUnsupportedInline });
263
+ skip(id, prepared.error.reason === "noChange" ? "noopOperation" : "unsupportedBlock", prepared.error.message);
264
+ }
265
+ return result([], null);
266
+ }
267
+ const applied = batch.operations.flatMap(({ id }) => {
268
+ const entry = appliedById.get(id);
269
+ return entry ? [batch.dryRun ? { id } : entry] : [];
270
+ });
271
+ if (batch.dryRun) return result(applied, null);
272
+ if (!this.options.publish(prepared.value)) {
273
+ for (const { id } of applied) skip(id, "documentNotEditable");
274
+ return result([], null);
275
+ }
276
+ const handle = {
277
+ type: "documentOperationUndo",
278
+ id: `canonical-${this.handleNamespace}-${String(this.nextHandle++)}`
279
+ };
280
+ this.handles.push({
281
+ handle,
282
+ version: session.version,
283
+ story
284
+ });
285
+ return result(applied, handle);
286
+ }
287
+ undo(handle) {
288
+ const index = this.handles.findIndex(({ handle: existing }) => existing.id === handle.id && existing.type === handle.type);
289
+ if (index < 0) return {
290
+ status: "rejected",
291
+ undoHandle: handle,
292
+ reason: "unknownHandle"
293
+ };
294
+ if (index !== this.handles.length - 1) return {
295
+ status: "rejected",
296
+ undoHandle: handle,
297
+ reason: "notLatest"
298
+ };
299
+ const entry = this.handles.at(-1);
300
+ const { session } = this.options;
301
+ if (!entry || entry.version !== session.version) return {
302
+ status: "rejected",
303
+ undoHandle: handle,
304
+ reason: "documentChanged"
305
+ };
306
+ const state = this.options.getState(entry.story);
307
+ if (!state) return {
308
+ status: "rejected",
309
+ undoHandle: handle,
310
+ reason: "documentChanged"
311
+ };
312
+ const prepared = session.prepareUndo(state, entry.story);
313
+ if (prepared.isErr() || !this.options.publish(prepared.value)) return {
314
+ status: "rejected",
315
+ undoHandle: handle,
316
+ reason: "documentChanged"
317
+ };
318
+ this.handles.pop();
319
+ const previous = this.handles.at(-1);
320
+ if (previous && previous.version === entry.version - 1) previous.version = session.version;
321
+ return {
322
+ status: "undone",
323
+ undoHandle: handle
324
+ };
325
+ }
326
+ resolve({ operation, options, state, story, diff }) {
327
+ const refusal = (reason, message) => Result.err(new CanonicalPublicOperationResolutionError({
328
+ reason,
329
+ message: message ?? `The operation was refused: ${reason}.`
330
+ }));
331
+ if ("comment" in operation) return refusal("unsupportedBlock", "Canonical comment operations are unavailable.");
332
+ switch (operation.type) {
333
+ case "replaceInBlock":
334
+ case "replaceRange":
335
+ case "replaceBlock":
336
+ case "splitBlock":
337
+ case "formatRange":
338
+ case "mergeBlockWithNext": break;
339
+ default: return refusal("unsupportedBlock", `Canonical public operation ${operation.type} is unavailable.`);
340
+ }
341
+ const blockId = operation.type === "replaceRange" || operation.type === "formatRange" ? operation.range.blockId : operation.blockId;
342
+ const anchor = Object.hasOwn(options.snapshot.anchors, blockId) ? options.snapshot.anchors[blockId] : void 0;
343
+ const projection = this.options.session.projectStory(story);
344
+ if (!anchor || projection.isErr()) return refusal("missingBlock");
345
+ const paragraph = projection.value.paragraph(blockId);
346
+ if (!paragraph) return refusal("missingBlock");
347
+ if (paragraph.source.pPrMark?.kind === "del" || paragraph.source.pPrMark?.kind === "moveFrom") return refusal("pendingDeletion");
348
+ const clean = buildCleanBlockText(paragraph.node, paragraph.start - 1, {
349
+ fieldResults: "text",
350
+ noteReferences: collectNoteReferenceLabels(state.doc)
351
+ });
352
+ const hash = hashFolioAIBlockText(clean.text);
353
+ if (operation.precondition && hash !== operation.precondition.blockTextHash) return refusal("preconditionFailed");
354
+ if (hash !== anchor.textHash) return refusal("changedBlock");
355
+ let start = 0;
356
+ let end = clean.text.length;
357
+ let text = "";
358
+ switch (operation.type) {
359
+ case "replaceInBlock":
360
+ if (operation.find === "") return refusal("emptyOperation");
361
+ start = clean.text.indexOf(operation.find);
362
+ if (start < 0) return refusal("missingFind");
363
+ if (clean.text.indexOf(operation.find, start + 1) >= 0) return refusal("ambiguousFind");
364
+ end = start + operation.find.length;
365
+ text = operation.replace;
366
+ break;
367
+ case "replaceRange":
368
+ case "formatRange":
369
+ start = operation.range.startOffset;
370
+ end = operation.range.endOffset;
371
+ if (hashFolioAIBlockText(clean.text.slice(start, end)) !== operation.range.selectedTextHash) return refusal("staleRange");
372
+ text = operation.type === "replaceRange" ? operation.replace : "";
373
+ break;
374
+ case "replaceBlock":
375
+ if (operation.styleId !== void 0 || operation.preserveFormatting === false) return refusal("unsupportedBlock", "Canonical replacement paragraph properties are unavailable.");
376
+ text = operation.text;
377
+ break;
378
+ case "splitBlock":
379
+ if (operation.firstParagraphProperties || operation.secondParagraphProperties) return refusal("unsupportedBlock", "Canonical split paragraph properties are unavailable.");
380
+ start = operation.offset;
381
+ end = start + (operation.separator?.length ?? 0);
382
+ if (start <= 0 || start >= clean.text.length || clean.text.slice(start, end) !== (operation.separator ?? "")) return refusal("staleRange");
383
+ break;
384
+ case "mergeBlockWithNext":
385
+ if (operation.mergedParagraphProperties || operation.separator) return refusal("unsupportedBlock", "Canonical public join properties and separators are unavailable.");
386
+ start = clean.text.length;
387
+ end = start;
388
+ break;
389
+ default: return operation;
390
+ }
391
+ if ([start, end].some((offset) => splitsSurrogatePair(clean.text, offset) || splitsGraphemeCluster(clean.text, offset))) return refusal("splitsCharacter");
392
+ if (clean.structuralBoundaries.some((boundary) => boundary.type === "noteReference" && start < boundary.offset + boundary.length && end > boundary.offset)) return refusal("protectedReference");
393
+ if (stripInlineEmphasisMarkers(text) !== text) return refusal("unsupportedBlock", "Canonical public emphasis replacements are unavailable.");
394
+ if (/\[\^(?:e)?\d+\]/u.test(text) || /[\t\r\n]/u.test(text)) return refusal("unsupportedBlock", "Canonical public replacements require plain text.");
395
+ const range = resolveCleanTextRange({
396
+ cleanBlock: clean,
397
+ startOffset: start,
398
+ endOffset: end
399
+ });
400
+ if (!range) return refusal("staleRange");
401
+ const from = projection.value.addressAt(range.from);
402
+ const to = projection.value.addressAt(range.to);
403
+ if (from.isErr() || to.isErr()) return refusal("unsupportedBlock");
404
+ if (operation.type !== "splitBlock" && operation.type !== "formatRange" && operation.type !== "mergeBlockWithNext" && clean.text.slice(start, end) === text) return refusal("noopOperation");
405
+ let intent;
406
+ switch (operation.type) {
407
+ case "splitBlock":
408
+ intent = {
409
+ type: "splitParagraph",
410
+ at: from.value,
411
+ to: to.value,
412
+ newBlockId: createEditorIntentIdAllocator()(this.options.session.document, {
413
+ type: "splitParagraph",
414
+ at: from.value,
415
+ to: to.value
416
+ }).newBlockId
417
+ };
418
+ break;
419
+ case "mergeBlockWithNext": {
420
+ const body = this.options.session.document.package.document.content;
421
+ if (story !== "main") return refusal("unsupportedBlock", "Canonical public joins currently require the main story.");
422
+ const next = body.at(body.findIndex((block) => block.type === "paragraph" && block.paraId === blockId) + 1);
423
+ if (next?.type !== "paragraph" || !next.paraId) return refusal("missingBlock");
424
+ intent = {
425
+ type: "joinParagraphs",
426
+ story,
427
+ blockId,
428
+ nextBlockId: next.paraId
429
+ };
430
+ break;
431
+ }
432
+ case "formatRange": {
433
+ const formatting = operation.formatting;
434
+ const patch = {
435
+ ...formatting.bold === void 0 ? {} : { bold: formatting.bold },
436
+ ...formatting.italic === void 0 ? {} : { italic: formatting.italic },
437
+ ...formatting.underline === void 0 ? {} : { underline: formatting.underline === null ? null : { style: formatting.underline ? "single" : "none" } },
438
+ ...formatting.strike === void 0 ? {} : {
439
+ strike: formatting.strike,
440
+ doubleStrike: null
441
+ },
442
+ ...formatting.fontSizePt === void 0 ? {} : { fontSize: formatting.fontSizePt === null ? null : formatting.fontSizePt * 2 },
443
+ ...formatting.fontFamily === void 0 ? {} : { fontFamily: formatting.fontFamily === null ? null : {
444
+ ascii: formatting.fontFamily,
445
+ hAnsi: formatting.fontFamily
446
+ } },
447
+ ...formatting.color === void 0 ? {} : { color: formatting.color === null ? null : { rgb: formatting.color.replace(/^#/u, "").toUpperCase() } }
448
+ };
449
+ intent = {
450
+ type: "formatRun",
451
+ from: from.value,
452
+ to: to.value,
453
+ patch
454
+ };
455
+ break;
456
+ }
457
+ case "replaceInBlock":
458
+ case "replaceRange":
459
+ case "replaceBlock":
460
+ intent = {
461
+ type: "replaceText",
462
+ from: from.value,
463
+ to: to.value,
464
+ text,
465
+ ...options.replacementBackground === "keep" ? {} : { runPropsPatch: {
466
+ highlight: null,
467
+ shading: null
468
+ } }
469
+ };
470
+ break;
471
+ default: return panic(`Unknown canonical public operation ${operation}`);
472
+ }
473
+ const intents = [];
474
+ const normalizations = [];
475
+ if (intent.type === "replaceText") {
476
+ let offset = start;
477
+ let pending = null;
478
+ const changes = [];
479
+ for (const segment of diff(clean.text.slice(start, end), text)) switch (segment.type) {
480
+ case "equal":
481
+ if (pending) changes.push(pending);
482
+ pending = null;
483
+ offset += segment.text.length;
484
+ break;
485
+ case "del":
486
+ pending ??= {
487
+ start: offset,
488
+ end: offset,
489
+ text: ""
490
+ };
491
+ offset += segment.text.length;
492
+ pending.end = offset;
493
+ break;
494
+ case "ins":
495
+ pending ??= {
496
+ start: offset,
497
+ end: offset,
498
+ text: ""
499
+ };
500
+ pending.text += segment.text;
501
+ break;
502
+ default: {
503
+ const unreachable = segment.type;
504
+ return panic(`Unknown replacement diff ${unreachable}`);
505
+ }
506
+ }
507
+ if (pending) changes.push(pending);
508
+ for (const change of changes.toReversed()) {
509
+ const gap = resolveCleanTextRange({
510
+ cleanBlock: clean,
511
+ startOffset: change.start,
512
+ endOffset: change.end
513
+ });
514
+ if (!gap) return refusal("unsupportedBlock");
515
+ if (change.text.length > 0 && gap.from < gap.to) {
516
+ let first = null;
517
+ let uniform = true;
518
+ paragraph.node.nodesBetween(gap.from - paragraph.start, gap.to - paragraph.start, (node) => {
519
+ if (!node.isText || node.marks.some((mark) => mark.type.name === "deletion")) return;
520
+ const marks = node.marks.filter((mark) => mark.type.name !== "insertion");
521
+ if (first === null) first = marks;
522
+ else if (!Mark.sameSet(first, marks)) uniform = false;
523
+ });
524
+ if (!uniform && normalizations.length === 0) normalizations.push({
525
+ id: operation.id,
526
+ code: "uniformReplacementFormatting"
527
+ });
528
+ }
529
+ const changeFrom = projection.value.addressAt(gap.from);
530
+ const changeTo = projection.value.addressAt(gap.to);
531
+ if (changeFrom.isErr() || changeTo.isErr()) return refusal("unsupportedBlock");
532
+ intents.push({
533
+ type: "replaceText",
534
+ from: changeFrom.value,
535
+ to: changeTo.value,
536
+ text: change.text,
537
+ ...intent.runPropsPatch === void 0 ? {} : { runPropsPatch: intent.runPropsPatch }
538
+ });
539
+ }
540
+ } else intents.push(intent);
541
+ const structural = operation.type === "splitBlock" || operation.type === "mergeBlockWithNext";
542
+ const nextParagraph = operation.type === "mergeBlockWithNext" && intent.type === "joinParagraphs" ? projection.value.paragraph(intent.nextBlockId) : void 0;
543
+ let claimedTo = range.to;
544
+ if (structural) claimedTo = paragraph.start + paragraph.node.content.size;
545
+ if (nextParagraph) claimedTo = nextParagraph.start + nextParagraph.node.content.size;
546
+ return Result.ok({
547
+ operation,
548
+ intents,
549
+ normalizations,
550
+ from: structural ? paragraph.start : range.from,
551
+ to: claimedTo
552
+ });
553
+ }
554
+ };
555
+ //#endregion
556
+ export { CANONICAL_PUBLIC_OPERATION_DISPOSITIONS, CanonicalPublicOperations };
@@ -0,0 +1,12 @@
1
+ import { FolioEditor } from "./folioEditor.js";
2
+ //#region src/controller/canonicalReview.d.ts
3
+ type ResolveCanonicalReviewRangeOptions = {
4
+ editor: Pick<FolioEditor, "getCanonicalDocument" | "getState" | "resolveCanonicalRevisions"> | null | undefined;
5
+ from: number;
6
+ to: number;
7
+ resolution: "accept" | "reject";
8
+ };
9
+ /** Null delegates to the default session's ProseMirror review commands. */
10
+ declare const resolveCanonicalReviewRange: ({ editor, from, to, resolution }: ResolveCanonicalReviewRangeOptions) => boolean | null;
11
+ //#endregion
12
+ export { resolveCanonicalReviewRange };
@@ -0,0 +1,19 @@
1
+ import { getTrackedChangeGroupIdsFromDoc, getTrackedChangesFromDoc } from "../ai-edits/read.js";
2
+ import { findAIEditRevisionRange } from "../prosemirror/commands/comments.js";
3
+ //#region src/controller/canonicalReview.ts
4
+ /** Null delegates to the default session's ProseMirror review commands. */
5
+ const resolveCanonicalReviewRange = ({ editor, from, to, resolution }) => {
6
+ if (!editor?.getCanonicalDocument()) return null;
7
+ const state = editor.getState();
8
+ if (!state) return false;
9
+ const ids = /* @__PURE__ */ new Set();
10
+ for (const change of getTrackedChangesFromDoc(state.doc)) {
11
+ const groupIds = getTrackedChangeGroupIdsFromDoc(state.doc, change.id);
12
+ const range = findAIEditRevisionRange(state, groupIds);
13
+ if (!range) continue;
14
+ if (from === to ? range.from <= from && range.to >= to : range.from < to && range.to > from) for (const id of groupIds) ids.add(id);
15
+ }
16
+ return ids.size > 0 && editor.resolveCanonicalRevisions([...ids], resolution);
17
+ };
18
+ //#endregion
19
+ export { resolveCanonicalReviewRange };