@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
@@ -1,30 +1,33 @@
1
1
  import { recordHiddenEditorPhase, recordHiddenEditorStateCreate } from "../layout-engine/layoutInstrumentation.js";
2
2
  import { suppressHiddenEditorScrollToSelection } from "../paged-layout/hiddenEditorScroll.js";
3
3
  import { isReadOnlyEditKey } from "../paged-layout/readOnlyEditAttempt.js";
4
+ import { getCanonicalCommandIntents } from "../prosemirror/canonicalCommands.js";
5
+ import { registerClipboardIntentHandler } from "../prosemirror/clipboardIntent.js";
6
+ import { pasteWithoutFormatting } from "../prosemirror/commands/pastePlainText.js";
4
7
  import { createEmptyDoc, toProseDoc } from "../prosemirror/conversion/toProseDoc.js";
8
+ import { registerEditorCommandOwner } from "../prosemirror/executeEditorCommand.js";
5
9
  import { ensureBaseDirectionInState } from "../prosemirror/extensions/features/AutoBidiDetectionExtension.js";
6
10
  import { ensureParaIdsInDoc, ensureParaIdsInState } from "../prosemirror/extensions/features/ParaIdAllocatorExtension.js";
11
+ import { createParagraphChangeTrackerPlugin, markPackageChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
12
+ import { toggleMarkForAllScripts, toggleUnderlineMark } from "../prosemirror/extensions/marks/markUtils.js";
7
13
  import { createDocumentNumberingPlugin } from "../prosemirror/plugins/documentNumbering.js";
8
14
  import { createDocumentStylesPlugin } from "../prosemirror/plugins/documentStyles.js";
9
15
  import { schema } from "../prosemirror/schema/index.js";
10
16
  import { createTextInputPlugin } from "../prosemirror/textInput.js";
11
17
  import { applyAttrSchemaMigrations, proseDocumentParagraphSourceContract, readYjsAttrSchemaVersion, readYjsParagraphSourceContract, withParagraphSourceContract, writeYjsDocumentMetadata } from "../prosemirror/yjsDocumentMetadata.js";
18
+ import { CANONICAL_GAP, usesCanonicalSession } from "../types/canonicalCapabilities.js";
19
+ import { prepareCanonicalPaste } from "./canonicalClipboard.js";
20
+ import { createCanonicalInputBoundary } from "./canonicalInput.js";
21
+ import { CanonicalPublicOperations } from "./canonicalPublicOperations.js";
22
+ import { createCanonicalSession, publishCanonicalProjection } from "./canonicalSession.js";
23
+ import { prepareCanonicalAutoformat, prepareCanonicalCommands } from "./canonicalStructure.js";
12
24
  import { createHiddenEditorApi } from "./hiddenEditorApi.js";
13
- import { panic } from "better-result";
14
- import { EditorState } from "prosemirror-state";
25
+ import { Result, TaggedError, panic } from "better-result";
26
+ import { OP_STORIES } from "@stll/docx-core/ops";
27
+ import { EditorState, Plugin } from "prosemirror-state";
15
28
  import { EditorView } from "prosemirror-view";
16
29
  //#region src/controller/hiddenEditorManager.ts
17
- /**
18
- * Hidden-editor view lifecycle manager
19
- *
20
- * Framework-agnostic owner of the off-screen ProseMirror EditorView. Holds the
21
- * view plus the bookkeeping that decides when an incoming document is a truly
22
- * external change (vs. an internal edit echoed back through props), builds the
23
- * editor state and `editorProps`, and composes the slice-2a imperative API so
24
- * the API and the lifecycle share one view owner. The React adapter
25
- * (`HiddenProseMirror`) keeps the input refs and its effects (which decide
26
- * *when* to act) and drives this manager through the methods below.
27
- */
30
+ var CanonicalSessionRefusalError = class extends TaggedError("CanonicalSessionRefusalError") {};
28
31
  const normalizeSeedState = (state) => ensureBaseDirectionInState(ensureParaIdsInState(state));
29
32
  const normalizeLocalSeedState = (state) => ensureBaseDirectionInState(state);
30
33
  const isObjectRecord = (value) => typeof value === "object" && value !== null;
@@ -201,6 +204,318 @@ const createHiddenEditorClipboardHandlers = (deps) => ({
201
204
  });
202
205
  const createHiddenEditorManager = (deps) => {
203
206
  let view = null;
207
+ let releaseCommandOwner;
208
+ let editorSession = { type: "prosemirror" };
209
+ let modeOverride = null;
210
+ const syncCanonicalMode = () => {
211
+ if (editorSession.type !== "canonical") return;
212
+ editorSession.session.setMode(modeOverride ?? (deps.getEditingMode?.() === "suggesting" ? {
213
+ type: "suggesting",
214
+ author: deps.getSuggestionAuthor?.() ?? "User"
215
+ } : { type: "editing" }));
216
+ };
217
+ const refuse = (reason, gap = CANONICAL_GAP.dispatch) => {
218
+ const message = reason;
219
+ if (deps.onSessionRefusal) deps.onSessionRefusal(message, gap);
220
+ else throw new CanonicalSessionRefusalError({
221
+ gap,
222
+ message
223
+ });
224
+ };
225
+ const seedSession = (document) => {
226
+ if (!usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) {
227
+ editorSession = { type: "prosemirror" };
228
+ return true;
229
+ }
230
+ const documentIdentity = deps.getDocumentIdentity();
231
+ if (editorSession.type === "refused" && editorSession.documentIdentity === documentIdentity) return false;
232
+ if (deps.getCollaboration() || !document) {
233
+ const reason = deps.getCollaboration() ? "Canonical sessions do not support collaboration." : "Canonical sessions require a loaded Document.";
234
+ editorSession = {
235
+ type: "refused",
236
+ reason,
237
+ documentIdentity
238
+ };
239
+ refuse(reason, CANONICAL_GAP.collaboration);
240
+ return false;
241
+ }
242
+ const result = createCanonicalSession(document, deps.getStyles());
243
+ if (result.isErr()) {
244
+ editorSession = {
245
+ type: "refused",
246
+ reason: result.error.message,
247
+ documentIdentity
248
+ };
249
+ refuse(result.error.message, result.error.gap);
250
+ return false;
251
+ }
252
+ editorSession = {
253
+ type: "canonical",
254
+ session: result.value
255
+ };
256
+ modeOverride = null;
257
+ syncCanonicalMode();
258
+ return true;
259
+ };
260
+ const canonicalState = (session) => EditorState.create({
261
+ doc: session.projection.doc,
262
+ plugins: [
263
+ createParagraphChangeTrackerPlugin(),
264
+ createDocumentStylesPlugin(deps.getStyles() ?? session.document.package.styles),
265
+ createDocumentNumberingPlugin(session.document.package.numbering),
266
+ ...[...deps.getExtensionManager()?.getPlugins() ?? [], ...deps.getExternalPlugins()].flatMap(({ props }) => props.transformPastedHTML === void 0 && props.transformPasted === void 0 && props.transformCopied === void 0 && props.clipboardTextSerializer === void 0 ? [] : [new Plugin({ props: {
267
+ ...props.transformCopied === void 0 ? {} : { transformCopied: props.transformCopied },
268
+ ...props.clipboardTextSerializer === void 0 ? {} : { clipboardTextSerializer: props.clipboardTextSerializer },
269
+ ...props.transformPastedHTML === void 0 ? {} : { transformPastedHTML: props.transformPastedHTML },
270
+ ...props.transformPasted === void 0 ? {} : { transformPasted: props.transformPasted }
271
+ } })])
272
+ ]
273
+ });
274
+ const publishCommit = (commit) => {
275
+ if (!view || deps.getReadOnly() || editorSession.type !== "canonical") return false;
276
+ const session = editorSession.session;
277
+ const result = publishCanonicalProjection({
278
+ state: view.state,
279
+ commit,
280
+ session
281
+ });
282
+ if (result.isErr()) {
283
+ refuse(result.error.message, result.error.gap);
284
+ return false;
285
+ }
286
+ const staged = result.value;
287
+ view.updateState(staged.state);
288
+ deps.onTransaction({
289
+ transactions: staged.transactions,
290
+ newState: staged.state,
291
+ docChanged: true
292
+ });
293
+ deps.onSelectionChange(staged.state);
294
+ return true;
295
+ };
296
+ const history = (direction) => {
297
+ if (!view || deps.getReadOnly() || editorSession.type !== "canonical") return false;
298
+ const session = editorSession.session;
299
+ if (direction === "undo" ? !session.canUndo : !session.canRedo) return false;
300
+ const prepared = direction === "undo" ? session.prepareUndo(view.state) : session.prepareRedo(view.state);
301
+ if (prepared.isErr()) {
302
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
303
+ return false;
304
+ }
305
+ return publishCommit(prepared.value);
306
+ };
307
+ const executeCanonicalCommand = (command) => {
308
+ if (!view || editorSession.type !== "canonical") return void 0;
309
+ if (deps.getReadOnly()) return false;
310
+ if (command === pasteWithoutFormatting) return command(view.state, (transaction) => view?.dispatch(transaction), view);
311
+ if (editorSession.session.isComposing) {
312
+ refuse("Composition must finish before formatting.");
313
+ return false;
314
+ }
315
+ const intents = getCanonicalCommandIntents(command, view.state);
316
+ if (intents === void 0) return void 0;
317
+ syncCanonicalMode();
318
+ editorSession.session.breakUndoGroup();
319
+ if (intents.length > 0 && intents.every((intent) => intent.type === "formatRun" && intent.from === intent.to)) return command(view.state, (transaction) => {
320
+ if (view !== null) view.dispatch(view.state.tr.setStoredMarks(transaction.storedMarks));
321
+ }, view);
322
+ const prepared = prepareCanonicalCommands(editorSession.session, view.state, intents);
323
+ if (prepared.isErr()) {
324
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
325
+ return false;
326
+ }
327
+ return publishCommit(prepared.value);
328
+ };
329
+ let clipboardDrag = { type: "idle" };
330
+ let lastInputRule;
331
+ const canonicalStructuralInput = {
332
+ command: (name) => {
333
+ if (!view) return;
334
+ let command = deps.getExtensionManager()?.getCommand(name)?.();
335
+ if (command === void 0) {
336
+ const mark = view.state.schema.marks[{
337
+ toggleBold: "bold",
338
+ toggleItalic: "italic",
339
+ toggleUnderline: "underline"
340
+ }[name]];
341
+ if (mark !== void 0) command = name === "toggleUnderline" ? toggleUnderlineMark(mark) : toggleMarkForAllScripts(mark, name === "toggleBold" ? "bold" : "italic");
342
+ }
343
+ if (command === void 0) {
344
+ refuse("The formatting command is unavailable.", CANONICAL_GAP.commands);
345
+ return;
346
+ }
347
+ if (executeCanonicalCommand(command) === void 0) command(view.state, (transaction) => view?.dispatch(transaction), view);
348
+ },
349
+ structure: (type) => {
350
+ if (!view || deps.getReadOnly() || editorSession.type !== "canonical") return;
351
+ syncCanonicalMode();
352
+ const session = editorSession.session;
353
+ const state = view.state;
354
+ const selection = session.projection.selectionAt(state);
355
+ if (selection.isErr()) {
356
+ refuse(selection.error.message, selection.error.gap);
357
+ return;
358
+ }
359
+ let prepared;
360
+ switch (type) {
361
+ case "split":
362
+ prepared = (session.projection.paragraph(selection.value.head.blockId)?.source)?.formatting?.numPr?.kind === "reference" && state.selection.$from.parent.content.size === 0 ? prepareCanonicalCommands(session, state, [{ type: "removeList" }]) : session.prepareSplit(state);
363
+ break;
364
+ case "joinBackward": {
365
+ if (lastInputRule?.session === session && lastInputRule.version === session.version && lastInputRule.caret === state.selection.head) {
366
+ lastInputRule = void 0;
367
+ history("undo");
368
+ return;
369
+ }
370
+ const paragraph = session.projection.paragraph(selection.value.head.blockId)?.source;
371
+ if (paragraph?.formatting?.numPr?.kind === "reference") {
372
+ prepared = prepareCanonicalCommands(session, state, [{ type: "removeList" }]);
373
+ break;
374
+ }
375
+ if ((paragraph?.formatting?.indentLeft ?? 0) > 0 || (paragraph?.formatting?.indentFirstLine ?? 0) > 0) {
376
+ prepared = session.prepareIntent(state, {
377
+ type: "formatParagraph",
378
+ at: selection.value.head,
379
+ patch: {
380
+ indentLeft: null,
381
+ indentFirstLine: null,
382
+ hangingIndent: null
383
+ }
384
+ });
385
+ break;
386
+ }
387
+ prepared = session.prepareJoin(state, "backward");
388
+ break;
389
+ }
390
+ case "joinForward":
391
+ prepared = session.prepareJoin(state, "forward");
392
+ break;
393
+ case "indent":
394
+ case "outdent":
395
+ prepared = prepareCanonicalCommands(session, state, [{
396
+ type: "changeListLevel",
397
+ direction: type === "indent" ? "increase" : "decrease"
398
+ }]);
399
+ break;
400
+ case "tab":
401
+ case "lineBreak":
402
+ case "pageBreak": {
403
+ const from = session.projection.addressAt(state.selection.from);
404
+ const to = session.projection.addressAt(state.selection.to);
405
+ if (from.isErr()) {
406
+ refuse(from.error.message, from.error.gap);
407
+ return;
408
+ }
409
+ if (to.isErr()) {
410
+ refuse(to.error.message, to.error.gap);
411
+ return;
412
+ }
413
+ prepared = session.prepareIntent(state, {
414
+ type: "insertAtom",
415
+ from: from.value,
416
+ to: to.value,
417
+ atom: type === "tab" ? { type: "tab" } : {
418
+ type: "break",
419
+ breakType: type === "pageBreak" ? "page" : "textWrapping"
420
+ }
421
+ });
422
+ break;
423
+ }
424
+ default: return type;
425
+ }
426
+ if (prepared.isErr()) {
427
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
428
+ return;
429
+ }
430
+ publishCommit(prepared.value);
431
+ }
432
+ };
433
+ const canonicalInputLifecycle = {
434
+ breakUndoGroup: () => {
435
+ if (editorSession.type === "canonical") editorSession.session.breakUndoGroup();
436
+ },
437
+ beginComposition: () => {
438
+ if (deps.getReadOnly() || editorSession.type !== "canonical") return false;
439
+ const begun = editorSession.session.beginComposition();
440
+ if (begun.isErr()) {
441
+ refuse(begun.error.message, begun.error.gap);
442
+ return false;
443
+ }
444
+ return true;
445
+ },
446
+ endComposition: () => {
447
+ if (editorSession.type === "canonical") editorSession.session.endComposition();
448
+ }
449
+ };
450
+ const input = createCanonicalInputBoundary({
451
+ pastePlainText: (pmView) => {
452
+ if (deps.getReadOnly()) {
453
+ deps.onReadOnlyEditAttempt();
454
+ return;
455
+ }
456
+ pasteWithoutFormatting(pmView.state, (transaction) => pmView.dispatch(transaction), pmView);
457
+ },
458
+ cut: (pmView, event) => {
459
+ event.preventDefault();
460
+ if (deps.getReadOnly()) {
461
+ deps.onReadOnlyEditAttempt();
462
+ return true;
463
+ }
464
+ if (editorSession.type !== "canonical" || pmView.state.selection.empty) return true;
465
+ if (event.clipboardData === null) {
466
+ refuse("The clipboard cannot receive the cut content.");
467
+ return true;
468
+ }
469
+ syncCanonicalMode();
470
+ const session = editorSession.session;
471
+ const { from, to } = pmView.state.selection;
472
+ const prepared = session.prepareReplace(pmView.state, {
473
+ from,
474
+ to,
475
+ text: "",
476
+ semantic: "replacement"
477
+ });
478
+ if (prepared.isErr()) {
479
+ refuse(prepared.error.message, prepared.error.gap);
480
+ return true;
481
+ }
482
+ const copied = Result.try({
483
+ try: () => {
484
+ const serialized = pmView.serializeForClipboard(pmView.state.selection.content());
485
+ event.clipboardData?.setData("text/html", serialized.dom.innerHTML);
486
+ event.clipboardData?.setData("text/plain", serialized.text);
487
+ },
488
+ catch: () => new CanonicalSessionRefusalError({
489
+ gap: CANONICAL_GAP.dispatch,
490
+ message: "The clipboard could not receive the cut content."
491
+ })
492
+ });
493
+ if (copied.isErr()) refuse(copied.error.message, copied.error.gap);
494
+ else publishCommit(prepared.value);
495
+ return true;
496
+ },
497
+ ...canonicalInputLifecycle,
498
+ ...canonicalStructuralInput,
499
+ replace: (intent) => {
500
+ if (!view || deps.getReadOnly() || editorSession.type !== "canonical") return;
501
+ syncCanonicalMode();
502
+ const rule = prepareCanonicalAutoformat(editorSession.session, view.state, intent);
503
+ const prepared = rule ?? editorSession.session.prepareReplace(view.state, intent);
504
+ if (prepared.isErr()) {
505
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
506
+ return;
507
+ }
508
+ const session = editorSession.session;
509
+ if (publishCommit(prepared.value) && rule !== void 0 && view !== null) lastInputRule = {
510
+ session,
511
+ version: session.version,
512
+ caret: view.state.selection.head
513
+ };
514
+ },
515
+ undo: () => history("undo"),
516
+ redo: () => history("redo"),
517
+ refuse
518
+ });
204
519
  let isDestroying = false;
205
520
  let requested = false;
206
521
  let isInitialized = false;
@@ -212,31 +527,52 @@ const createHiddenEditorManager = (deps) => {
212
527
  if (!host) return;
213
528
  const collaboration = deps.getCollaboration();
214
529
  const collaborationModules = deps.getCollaborationModules();
215
- if (collaboration && !collaborationModules) return;
530
+ if (collaboration && !collaborationModules && !usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) return;
216
531
  const precomputedInitialState = deps.getPrecomputedInitialState();
217
532
  const document = deps.getDocument();
218
533
  const styles = deps.getStyles();
219
534
  const extensionManager = deps.getExtensionManager();
220
535
  const externalPlugins = deps.getExternalPlugins();
536
+ input.reset();
537
+ if (!seedSession(document)) return;
221
538
  const editorProps = {
222
- state: precomputedInitialState && !collaboration ? precomputedInitialState : createHiddenEditorState({
223
- document,
224
- styles,
225
- manager: extensionManager,
226
- externalPlugins,
227
- collaboration,
228
- collaborationModules,
229
- reason: "mount"
230
- }),
539
+ state: (() => {
540
+ if (editorSession.type === "canonical") return canonicalState(editorSession.session);
541
+ if (precomputedInitialState && !collaboration) return precomputedInitialState;
542
+ return createHiddenEditorState({
543
+ document,
544
+ styles,
545
+ manager: extensionManager,
546
+ externalPlugins,
547
+ collaboration,
548
+ collaborationModules,
549
+ reason: "mount"
550
+ });
551
+ })(),
231
552
  attributes: HIDDEN_EDITOR_ATTRIBUTES,
232
- editable: () => !deps.getReadOnly(),
553
+ editable: () => !deps.getReadOnly() && editorSession.type !== "refused",
233
554
  dispatchTransaction: (transaction) => {
234
555
  if (!view || isDestroying) return;
235
556
  if (deps.getReadOnly() && transaction.docChanged) {
236
557
  deps.onReadOnlyEditAttempt();
237
558
  return;
238
559
  }
560
+ if (editorSession.type === "refused") return;
561
+ if (editorSession.type === "canonical" && transaction.docChanged) {
562
+ if (input.acceptComposition(view, transaction)) return;
563
+ if (!input.commitNativeProposal(view, transaction)) input.refuseNativeMutation(view);
564
+ view.updateState(view.state);
565
+ return;
566
+ }
567
+ if (editorSession.type === "canonical" && transaction.selectionSet && !input.isComposing) {
568
+ lastInputRule = void 0;
569
+ editorSession.session.breakUndoGroup();
570
+ }
239
571
  const applied = view.state.applyTransaction(transaction);
572
+ if (editorSession.type === "canonical" && !applied.state.doc.eq(view.state.doc)) {
573
+ refuse("A plugin attempted an unclassified canonical document mutation.");
574
+ return;
575
+ }
240
576
  view.updateState(applied.state);
241
577
  const docChanged = applied.transactions.some((appliedTransaction) => appliedTransaction.docChanged);
242
578
  const selectionChanged = applied.transactions.some((appliedTransaction) => appliedTransaction.selectionSet || appliedTransaction.docChanged);
@@ -256,20 +592,107 @@ const createHiddenEditorManager = (deps) => {
256
592
  event.preventDefault();
257
593
  return true;
258
594
  }
595
+ if (editorSession.type === "canonical" && input.handleKeyDown(pmView, event)) return true;
259
596
  return deps.onKeyDown(pmView, event);
260
597
  },
598
+ handleTextInput: (pmView, from, to, text) => editorSession.type === "canonical" ? input.handleTextInput(pmView, from, to, text) : false,
599
+ handlePaste: (pmView, _event, slice) => {
600
+ if (editorSession.type !== "canonical") return false;
601
+ if (deps.getReadOnly()) {
602
+ deps.onReadOnlyEditAttempt();
603
+ return true;
604
+ }
605
+ syncCanonicalMode();
606
+ const prepared = prepareCanonicalPaste({
607
+ session: editorSession.session,
608
+ state: pmView.state,
609
+ slice
610
+ });
611
+ if (prepared.isErr()) {
612
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
613
+ } else publishCommit(prepared.value);
614
+ return true;
615
+ },
616
+ handleDrop: (pmView, event, slice, moved) => {
617
+ if (editorSession.type !== "canonical") return false;
618
+ if (deps.getReadOnly()) {
619
+ deps.onReadOnlyEditAttempt();
620
+ return true;
621
+ }
622
+ const target = pmView.posAtCoords({
623
+ left: event.clientX,
624
+ top: event.clientY
625
+ })?.pos;
626
+ if (target === void 0) {
627
+ refuse("The drop has no document destination.");
628
+ return true;
629
+ }
630
+ const captured = clipboardDrag;
631
+ clipboardDrag = { type: "idle" };
632
+ if (moved && (captured.type !== "dragging" || captured.session !== editorSession.session || captured.version !== editorSession.session.version)) {
633
+ refuse("The dragged content changed or has no captured source.");
634
+ return true;
635
+ }
636
+ const source = captured.type === "dragging" ? {
637
+ from: captured.from,
638
+ to: captured.to
639
+ } : pmView.state.selection;
640
+ if (moved && target >= source.from && target <= source.to) return true;
641
+ syncCanonicalMode();
642
+ const prepared = prepareCanonicalPaste({
643
+ session: editorSession.session,
644
+ state: pmView.state,
645
+ slice,
646
+ ...moved ? {
647
+ moveTarget: target,
648
+ moveSource: {
649
+ from: source.from,
650
+ to: source.to
651
+ }
652
+ } : { pasteTarget: target }
653
+ });
654
+ if (prepared.isErr()) {
655
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
656
+ } else publishCommit(prepared.value);
657
+ return true;
658
+ },
261
659
  handleScrollToSelection: suppressHiddenEditorScrollToSelection,
262
660
  handleDOMEvents: {
661
+ keydown: (pmView, event) => editorSession.type === "canonical" ? input.handleDOMEvents.keydown(pmView, event) : false,
263
662
  focus: () => false,
264
- blur: () => false,
663
+ blur: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.blur(pmView) : false,
265
664
  ...createHiddenEditorClipboardHandlers(deps),
665
+ compositionstart: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.compositionstart(pmView) : false,
666
+ compositionend: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.compositionend(pmView) : false,
667
+ input: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.input(pmView) : false,
668
+ paste: (pmView, event) => {
669
+ if (editorSession.type === "canonical" && !deps.getReadOnly()) return input.handleDOMEvents.paste(pmView, event);
670
+ return createHiddenEditorClipboardHandlers(deps).paste(pmView, event);
671
+ },
672
+ cut: (pmView, event) => editorSession.type === "canonical" ? input.handleDOMEvents.cut(pmView, event) : createHiddenEditorClipboardHandlers(deps).cut(pmView, event),
266
673
  beforeinput: (_view, event) => {
674
+ if (editorSession.type === "canonical" && !deps.getReadOnly()) return input.handleDOMEvents.beforeinput(_view, event);
267
675
  if (!deps.getReadOnly()) return false;
268
676
  deps.onReadOnlyEditAttempt();
269
677
  event.preventDefault();
270
678
  return true;
271
679
  },
680
+ dragstart: (pmView) => {
681
+ if (editorSession.type === "canonical") clipboardDrag = {
682
+ type: "dragging",
683
+ session: editorSession.session,
684
+ version: editorSession.session.version,
685
+ from: pmView.state.selection.from,
686
+ to: pmView.state.selection.to
687
+ };
688
+ return false;
689
+ },
690
+ dragend: () => {
691
+ clipboardDrag = { type: "idle" };
692
+ return false;
693
+ },
272
694
  drop: (_view, event) => {
695
+ if (editorSession.type === "canonical" && !deps.getReadOnly()) return input.handleDOMEvents.drop(_view, event);
273
696
  if (!deps.getReadOnly()) return false;
274
697
  deps.onReadOnlyEditAttempt();
275
698
  event.preventDefault();
@@ -279,6 +702,26 @@ const createHiddenEditorManager = (deps) => {
279
702
  };
280
703
  const viewStartedAt = performance.now();
281
704
  view = new EditorView(host, editorProps);
705
+ if (editorSession.type === "canonical") registerClipboardIntentHandler(view, (slice, plain) => {
706
+ if (!view || editorSession.type !== "canonical") return false;
707
+ if (deps.getReadOnly()) {
708
+ deps.onReadOnlyEditAttempt();
709
+ return false;
710
+ }
711
+ syncCanonicalMode();
712
+ const prepared = prepareCanonicalPaste({
713
+ session: editorSession.session,
714
+ state: view.state,
715
+ slice,
716
+ plain
717
+ });
718
+ if (prepared.isErr()) {
719
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
720
+ return false;
721
+ }
722
+ return publishCommit(prepared.value);
723
+ });
724
+ releaseCommandOwner = registerEditorCommandOwner(view, executeCanonicalCommand);
282
725
  recordHiddenEditorPhase("mount", "editor-view", performance.now() - viewStartedAt);
283
726
  syncHiddenEditorAccessibility(view, deps.getReadOnly());
284
727
  isInitialized = true;
@@ -298,8 +741,11 @@ const createHiddenEditorManager = (deps) => {
298
741
  if (view && !isDestroying) {
299
742
  isDestroying = true;
300
743
  deps.onEditorViewDestroy();
744
+ releaseCommandOwner?.();
745
+ releaseCommandOwner = void 0;
301
746
  view.destroy();
302
747
  view = null;
748
+ input.reset();
303
749
  isDestroying = false;
304
750
  }
305
751
  };
@@ -307,17 +753,23 @@ const createHiddenEditorManager = (deps) => {
307
753
  if (!view || isDestroying) return;
308
754
  const collaboration = deps.getCollaboration();
309
755
  const collaborationModules = deps.getCollaborationModules();
310
- if (collaboration && !collaborationModules) return;
756
+ if (collaboration && !collaborationModules && !usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) return;
311
757
  const document = deps.getDocument();
312
758
  const currentDocId = deps.getDocumentIdentity();
313
759
  const currentCollaborationFragment = collaboration?.yXmlFragment ?? null;
314
760
  const collaborationSourceChanged = currentCollaborationFragment !== lastCollaborationFragment;
315
- if (collaboration && !collaborationSourceChanged) return;
316
- if (isInitialized && currentDocId === lastDocumentId && !collaborationSourceChanged) return;
761
+ const sessionChanged = editorSession.type !== "prosemirror" !== usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting);
762
+ if (collaboration && !collaborationSourceChanged && !sessionChanged) return;
763
+ if (isInitialized && currentDocId === lastDocumentId && !collaborationSourceChanged && !sessionChanged) return;
764
+ input.reset();
765
+ if (!seedSession(document)) {
766
+ destroyView();
767
+ return;
768
+ }
317
769
  isInitialized = true;
318
770
  lastDocumentId = currentDocId;
319
771
  lastCollaborationFragment = currentCollaborationFragment;
320
- const newState = createHiddenEditorState({
772
+ const newState = editorSession.type === "canonical" ? canonicalState(editorSession.session) : createHiddenEditorState({
321
773
  document,
322
774
  styles: deps.getStyles(),
323
775
  manager: deps.getExtensionManager(),
@@ -336,6 +788,57 @@ const createHiddenEditorManager = (deps) => {
336
788
  if (!view) return;
337
789
  syncHiddenEditorAccessibility(view, deps.getReadOnly());
338
790
  };
791
+ const publishStoryCommit = (storyView, commit) => {
792
+ if (!view || editorSession.type !== "canonical") return false;
793
+ if (deps.getEditingMode?.() === "suggesting") {
794
+ refuse("Suggesting is unavailable in the experimental canonical session.", CANONICAL_GAP.suggesting);
795
+ return false;
796
+ }
797
+ const session = editorSession.session;
798
+ const bodyTransaction = view.state.tr;
799
+ if (!bodyTransaction.doc.eq(commit.bodyProjection.doc)) bodyTransaction.replaceWith(0, bodyTransaction.doc.content.size, commit.bodyProjection.doc.content);
800
+ markPackageChange(bodyTransaction);
801
+ bodyTransaction.setMeta("addToHistory", false);
802
+ const bodyStaged = Result.try(() => view?.state.applyTransaction(bodyTransaction));
803
+ if (bodyStaged.isErr() || !bodyStaged.value || !bodyStaged.value.transactions.includes(bodyTransaction) || !bodyStaged.value.state.doc.eq(commit.bodyProjection.doc) || bodyStaged.value.transactions.some((transaction) => transaction !== bodyTransaction && transaction.docChanged)) {
804
+ refuse("A plugin refused or changed the canonical body projection.");
805
+ return false;
806
+ }
807
+ const staged = publishCanonicalProjection({
808
+ state: storyView.state,
809
+ commit,
810
+ session
811
+ });
812
+ if (staged.isErr()) {
813
+ refuse(staged.error.message, staged.error.gap);
814
+ return false;
815
+ }
816
+ storyView.updateState(staged.value.state);
817
+ view.updateState(bodyStaged.value.state);
818
+ deps.onTransaction({
819
+ transactions: staged.value.transactions,
820
+ newState: view.state,
821
+ docChanged: true
822
+ });
823
+ deps.onSelectionChange(view.state);
824
+ return true;
825
+ };
826
+ let publicOperations = null;
827
+ const getPublicOperations = () => {
828
+ if (editorSession.type !== "canonical") return null;
829
+ const session = editorSession.session;
830
+ if (publicOperations?.session === session) return publicOperations.executor;
831
+ const executor = new CanonicalPublicOperations({
832
+ session,
833
+ getState: (story) => story === OP_STORIES.MAIN && !deps.getReadOnly() && !isDestroying ? view?.state ?? null : null,
834
+ publish: publishCommit
835
+ });
836
+ publicOperations = {
837
+ session,
838
+ executor
839
+ };
840
+ return executor;
841
+ };
339
842
  return {
340
843
  ensureView,
341
844
  retryViewCreation,
@@ -347,7 +850,112 @@ const createHiddenEditorManager = (deps) => {
347
850
  isInitialized: () => isInitialized,
348
851
  api: createHiddenEditorApi({
349
852
  getView: () => view,
350
- getDocumentContext: deps.getDocumentContext,
853
+ getDocumentContext: () => editorSession.type === "refused" ? null : deps.getDocumentContext(),
854
+ isCanonicalSaveCurrent: (version) => editorSession.type === "canonical" && !editorSession.session.isComposing && editorSession.session.version === version,
855
+ captureCanonicalSave: () => editorSession.type === "canonical" ? editorSession.session.captureSaveSnapshot() : null,
856
+ getCanonicalDocument: () => editorSession.type === "canonical" ? editorSession.session.document : null,
857
+ setCanonicalMode: (mode) => {
858
+ if (editorSession.type !== "canonical") return false;
859
+ modeOverride = mode;
860
+ syncCanonicalMode();
861
+ return true;
862
+ },
863
+ resolveCanonicalRevisions: (revisionIds, resolution) => {
864
+ if (!view || deps.getReadOnly() || editorSession.type !== "canonical") return false;
865
+ const prepared = editorSession.session.prepareResolve(view.state, {
866
+ revisionIds,
867
+ resolution
868
+ });
869
+ if (prepared.isErr()) {
870
+ if (prepared.error.reason !== "noChange") refuse(prepared.error.message, prepared.error.gap);
871
+ return false;
872
+ }
873
+ return publishCommit(prepared.value);
874
+ },
875
+ executeCanonicalCommand,
876
+ getCanonicalHistory: () => editorSession.type === "canonical" ? {
877
+ undo: () => history("undo"),
878
+ redo: () => history("redo"),
879
+ canUndo: () => editorSession.type === "canonical" && editorSession.session.canUndo,
880
+ canRedo: () => editorSession.type === "canonical" && editorSession.session.canRedo
881
+ } : null,
882
+ canonicalOperations: {
883
+ applyCanonicalDocumentOperations: (options) => {
884
+ ensureView();
885
+ return getPublicOperations()?.apply(options) ?? null;
886
+ },
887
+ undoCanonicalDocumentOperations: (handle) => getPublicOperations()?.undo(handle) ?? null,
888
+ updateCanonicalInputLifecycle: (action) => {
889
+ if (editorSession.type !== "canonical") return false;
890
+ switch (action) {
891
+ case "beginComposition": return canonicalInputLifecycle.beginComposition();
892
+ case "endComposition":
893
+ canonicalInputLifecycle.endComposition();
894
+ return true;
895
+ case "breakUndoGroup":
896
+ canonicalInputLifecycle.breakUndoGroup();
897
+ return true;
898
+ }
899
+ },
900
+ getCanonicalStorySelection: (story) => {
901
+ if (editorSession.type !== "canonical") return null;
902
+ const session = editorSession.session;
903
+ const selection = session.selection;
904
+ if (!selection) return null;
905
+ const projection = session.projectStory(story);
906
+ if (projection.isErr()) return null;
907
+ const anchor = projection.value.positionAt(selection.anchor);
908
+ const head = projection.value.positionAt(selection.head);
909
+ return anchor.isOk() && head.isOk() ? {
910
+ anchor: anchor.value,
911
+ head: head.value
912
+ } : null;
913
+ },
914
+ applyCanonicalStoryHistory: ({ view: storyView, story, direction }) => {
915
+ if (story === OP_STORIES.MAIN && storyView === view) return history(direction);
916
+ if (!view || editorSession.type !== "canonical" || deps.getReadOnly()) return false;
917
+ const session = editorSession.session;
918
+ if (direction === "undo" ? !session.canUndo : !session.canRedo) return false;
919
+ const prepared = direction === "undo" ? session.prepareUndo(storyView.state, story) : session.prepareRedo(storyView.state, story);
920
+ if (prepared.isErr()) {
921
+ refuse(prepared.error.message, prepared.error.gap);
922
+ return false;
923
+ }
924
+ return publishStoryCommit(storyView, prepared.value);
925
+ },
926
+ applyCanonicalOperations: (ops) => {
927
+ ensureView();
928
+ if (!view || editorSession.type !== "canonical" || deps.getReadOnly()) return false;
929
+ const prepared = editorSession.session.prepareOperations(view.state, ops);
930
+ if (prepared.isErr()) {
931
+ refuse(prepared.error.message, prepared.error.gap);
932
+ return false;
933
+ }
934
+ return publishCommit(prepared.value);
935
+ },
936
+ getCanonicalStoryProjection: (story) => {
937
+ if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting)) ensureView();
938
+ if (editorSession.type !== "canonical" || editorSession.session.isComposing) return null;
939
+ const result = editorSession.session.projectStory(story);
940
+ if (result.isErr()) {
941
+ refuse(result.error.message, result.error.gap);
942
+ return null;
943
+ }
944
+ return result.value.doc;
945
+ },
946
+ replaceCanonicalStoryText: ({ view: storyView, story, intent }) => {
947
+ if (!view || editorSession.type !== "canonical" || deps.getReadOnly()) return false;
948
+ const prepared = editorSession.session.prepareReplace(storyView.state, {
949
+ ...intent,
950
+ story
951
+ });
952
+ if (prepared.isErr()) {
953
+ refuse(prepared.error.message, prepared.error.gap);
954
+ return false;
955
+ }
956
+ return publishStoryCommit(storyView, prepared.value);
957
+ }
958
+ },
351
959
  isDestroying: () => isDestroying,
352
960
  ensureView,
353
961
  isViewRequested
@@ -355,4 +963,4 @@ const createHiddenEditorManager = (deps) => {
355
963
  };
356
964
  };
357
965
  //#endregion
358
- export { collectRemoteSelections, createHiddenEditorClipboardHandlers, createHiddenEditorManager, createHiddenEditorState };
966
+ export { CanonicalSessionRefusalError, collectRemoteSelections, createHiddenEditorClipboardHandlers, createHiddenEditorManager, createHiddenEditorState };