@stll/folio-core 0.55.0 → 0.57.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 (225) hide show
  1. package/dist/ai-edits/apply.js +214 -52
  2. package/dist/ai-edits/batch-claims.d.ts +3 -1
  3. package/dist/ai-edits/batch-claims.js +25 -1
  4. package/dist/ai-edits/headless.js +2 -2
  5. package/dist/ai-edits/table-row-column-mutations.js +17 -3
  6. package/dist/ai-edits/types.d.ts +7 -1
  7. package/dist/ai-suggestions/apply.js +20 -1
  8. package/dist/compare/compare.js +1 -0
  9. package/dist/compare/inline-atoms.js +40 -16
  10. package/dist/compare/inline-provenance.js +12 -5
  11. package/dist/compare/reproducible-package.d.ts +0 -17
  12. package/dist/compare/reproducible-package.js +12 -18
  13. package/dist/compat/eigenpal.d.ts +2 -1
  14. package/dist/compat/eigenpal.js +2 -1
  15. package/dist/controller/canonicalClipboard.d.ts +23 -0
  16. package/dist/controller/canonicalClipboard.js +482 -0
  17. package/dist/controller/canonicalClipboardResources.d.ts +21 -0
  18. package/dist/controller/canonicalClipboardResources.js +382 -0
  19. package/dist/controller/canonicalComments.d.ts +41 -0
  20. package/dist/controller/canonicalComments.js +152 -0
  21. package/dist/controller/canonicalComposition.d.ts +24 -3
  22. package/dist/controller/canonicalComposition.js +208 -48
  23. package/dist/controller/canonicalInlineProjection.d.ts +118 -0
  24. package/dist/controller/canonicalInlineProjection.js +476 -0
  25. package/dist/controller/canonicalInput.d.ts +13 -4
  26. package/dist/controller/canonicalInput.js +62 -14
  27. package/dist/controller/canonicalInputTimer.d.ts +16 -0
  28. package/dist/controller/canonicalInputTimer.js +34 -0
  29. package/dist/controller/canonicalPublicOperations.d.ts +48 -0
  30. package/dist/controller/canonicalPublicOperations.js +648 -0
  31. package/dist/controller/canonicalSelection.d.ts +18 -0
  32. package/dist/controller/canonicalSelection.js +32 -0
  33. package/dist/controller/canonicalSession.d.ts +27 -6
  34. package/dist/controller/canonicalSession.js +298 -116
  35. package/dist/controller/canonicalStoryEditor.d.ts +7 -3
  36. package/dist/controller/canonicalStoryEditor.js +27 -2
  37. package/dist/controller/canonicalStructure.js +57 -4
  38. package/dist/controller/canonicalTocSelection.d.ts +11 -0
  39. package/dist/controller/canonicalTocSelection.js +43 -0
  40. package/dist/controller/folioEditor.d.ts +2 -9
  41. package/dist/controller/folioEditor.js +18 -4
  42. package/dist/controller/headerFooterEditorManager.d.ts +2 -1
  43. package/dist/controller/headerFooterEditorManager.js +26 -11
  44. package/dist/controller/hiddenEditorApi.d.ts +24 -3
  45. package/dist/controller/hiddenEditorApi.js +8 -0
  46. package/dist/controller/hiddenEditorManager.d.ts +5 -1
  47. package/dist/controller/hiddenEditorManager.js +378 -43
  48. package/dist/controller/noteEditorManager.d.ts +2 -1
  49. package/dist/controller/noteEditorManager.js +29 -13
  50. package/dist/document-operations.d.ts +8 -1
  51. package/dist/document-operations.js +6 -4
  52. package/dist/docx/blockPlainText.d.ts +1 -1
  53. package/dist/docx/blockPlainText.js +11 -3
  54. package/dist/docx/builtInStyles.js +13 -6
  55. package/dist/docx/canonicalResourceSave.d.ts +16 -0
  56. package/dist/docx/canonicalResourceSave.js +27 -0
  57. package/dist/docx/canonicalSave.d.ts +29 -0
  58. package/dist/docx/canonicalSave.js +101 -0
  59. package/dist/docx/canonicalStoryRepack.d.ts +1 -1
  60. package/dist/docx/canonicalStoryRepack.js +38 -5
  61. package/dist/docx/containerChildren.gen.d.ts +3 -1
  62. package/dist/docx/containerChildren.gen.js +33 -1
  63. package/dist/docx/documentParser.d.ts +1 -13
  64. package/dist/docx/documentParser.js +1 -47
  65. package/dist/docx/ensureParaIds.js +10 -4
  66. package/dist/docx/fieldParser.js +14 -2
  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 +26 -1
  72. package/dist/docx/headerFooterVerbatim.js +81 -3
  73. package/dist/docx/hyperlinkParser.d.ts +1 -1
  74. package/dist/docx/hyperlinkParser.js +20 -1
  75. package/dist/docx/imageParser.d.ts +13 -1
  76. package/dist/docx/imageParser.js +20 -11
  77. package/dist/docx/inlineWrapperContent.d.ts +4 -4
  78. package/dist/docx/inlineWrapperContent.js +4 -4
  79. package/dist/docx/linkedSdtPreservation.d.ts +10 -0
  80. package/dist/docx/linkedSdtPreservation.js +37 -0
  81. package/dist/docx/metadataPrivacy.js +6 -1
  82. package/dist/docx/packageParts.js +9 -3
  83. package/dist/docx/paragraphParser.js +88 -67
  84. package/dist/docx/paragraphPropertySource.d.ts +22 -1
  85. package/dist/docx/paragraphPropertySource.js +109 -2
  86. package/dist/docx/parser.js +2 -1
  87. package/dist/docx/preservedRunContent.js +2 -1
  88. package/dist/docx/relsParser.js +26 -2
  89. package/dist/docx/removeHeaderFooterParts.js +17 -6
  90. package/dist/docx/rezip.d.ts +49 -5
  91. package/dist/docx/rezip.js +754 -235
  92. package/dist/docx/saveDiagnostics.d.ts +11 -0
  93. package/dist/docx/selectiveSave.d.ts +2 -1
  94. package/dist/docx/selectiveSave.js +42 -11
  95. package/dist/docx/selectiveXmlPatch.js +7 -5
  96. package/dist/docx/serializer/commentSerializer.d.ts +4 -1
  97. package/dist/docx/serializer/commentSerializer.js +7 -3
  98. package/dist/docx/serializer/documentSerializer.d.ts +9 -2
  99. package/dist/docx/serializer/documentSerializer.js +29 -10
  100. package/dist/docx/serializer/paragraphSerializer.js +207 -94
  101. package/dist/docx/server/applyDocxXmlPatchProposal.js +14 -9
  102. package/dist/docx/server/materializeYjsDocx.js +14 -6
  103. package/dist/docx/server/migrateYjsAttrSchema.js +17 -5
  104. package/dist/docx/storyBlockReplay.d.ts +21 -1
  105. package/dist/docx/storyBlockReplay.js +113 -9
  106. package/dist/docx/storyPlainText.d.ts +20 -0
  107. package/dist/docx/storyPlainText.js +23 -0
  108. package/dist/docx/structuralXmlPatch.js +10 -22
  109. package/dist/docx/styleParser.d.ts +5 -1
  110. package/dist/docx/styleParser.js +1 -1
  111. package/dist/docx/tableParser.d.ts +1 -8
  112. package/dist/docx/tableParser.js +1 -28
  113. package/dist/docx/textBoxParser.d.ts +1 -5
  114. package/dist/docx/textBoxParser.js +1 -20
  115. package/dist/docx/unzip.js +6 -1
  116. package/dist/i18n/messages/ar.gen.js +4 -0
  117. package/dist/i18n/messages/catalogs.gen.d.ts +68 -0
  118. package/dist/i18n/messages/catalogs.gen.js +68 -0
  119. package/dist/i18n/messages/cs.gen.js +4 -0
  120. package/dist/i18n/messages/de.gen.js +4 -0
  121. package/dist/i18n/messages/en.gen.js +4 -0
  122. package/dist/i18n/messages/es.gen.js +4 -0
  123. package/dist/i18n/messages/et.gen.js +4 -0
  124. package/dist/i18n/messages/fr.gen.js +4 -0
  125. package/dist/i18n/messages/he.gen.js +4 -0
  126. package/dist/i18n/messages/hi.gen.js +4 -0
  127. package/dist/i18n/messages/hu.gen.js +4 -0
  128. package/dist/i18n/messages/lt.gen.js +4 -0
  129. package/dist/i18n/messages/lv.gen.js +4 -0
  130. package/dist/i18n/messages/messages.gen.d.ts +4 -0
  131. package/dist/i18n/messages/pl.gen.js +4 -0
  132. package/dist/i18n/messages/pt-BR.gen.js +4 -0
  133. package/dist/i18n/messages/sk.gen.js +4 -0
  134. package/dist/i18n/messages/tr.gen.js +4 -0
  135. package/dist/i18n/messages/zh-CN.gen.js +4 -0
  136. package/dist/index.d.ts +2 -1
  137. package/dist/index.js +2 -1
  138. package/dist/internal/acceptedBlockProjection.d.ts +12 -0
  139. package/dist/internal/acceptedBlockProjection.js +45 -0
  140. package/dist/internal/revisionResolutionInline.js +25 -3
  141. package/dist/managers/DocumentLoaderManager.js +2 -1
  142. package/dist/markdown/escape.d.ts +3 -1
  143. package/dist/markdown/escape.js +9 -2
  144. package/dist/markdown/renderBlock.js +5 -120
  145. package/dist/markdown/renderParagraph.js +4 -2
  146. package/dist/markdown/renderRuns.d.ts +7 -1
  147. package/dist/markdown/renderRuns.js +33 -24
  148. package/dist/panel-layout.d.ts +11 -9
  149. package/dist/panel-layout.js +13 -9
  150. package/dist/prosemirror/addTrackedDeletionMark.d.ts +18 -0
  151. package/dist/prosemirror/addTrackedDeletionMark.js +87 -0
  152. package/dist/prosemirror/attrs/index.js +5 -0
  153. package/dist/prosemirror/canonicalCommands.d.ts +28 -4
  154. package/dist/prosemirror/canonicalCommands.js +11 -4
  155. package/dist/prosemirror/canonicalSelectionRange.d.ts +9 -0
  156. package/dist/prosemirror/canonicalSelectionRange.js +15 -0
  157. package/dist/prosemirror/clipboardIntent.d.ts +7 -0
  158. package/dist/prosemirror/clipboardIntent.js +9 -0
  159. package/dist/prosemirror/commands/comments.js +60 -35
  160. package/dist/prosemirror/commands/pageBreak.js +2 -2
  161. package/dist/prosemirror/commands/pastePlainText.js +2 -1
  162. package/dist/prosemirror/commands/propertyChangeScope.js +3 -6
  163. package/dist/prosemirror/commands/resolveParagraphProperties.js +22 -13
  164. package/dist/prosemirror/commentOps.js +15 -1
  165. package/dist/prosemirror/conversion/fromProseDoc.js +22 -3
  166. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +8 -0
  167. package/dist/prosemirror/conversion/hyphenTextCarriers.js +8 -0
  168. package/dist/prosemirror/conversion/toProseDoc.d.ts +7 -1
  169. package/dist/prosemirror/conversion/toProseDoc.js +124 -22
  170. package/dist/prosemirror/extensions/core/ParagraphExtension.js +61 -35
  171. package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +74 -25
  172. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +11 -0
  173. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +65 -29
  174. package/dist/prosemirror/extensions/marks/markUtils.js +11 -8
  175. package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +5 -2
  176. package/dist/prosemirror/fieldRepresentation.d.ts +8 -0
  177. package/dist/prosemirror/fieldRepresentation.js +18 -0
  178. package/dist/prosemirror/hyperlinkRemoval.d.ts +13 -1
  179. package/dist/prosemirror/hyperlinkRemoval.js +28 -9
  180. package/dist/prosemirror/insertOperations.js +2 -1
  181. package/dist/prosemirror/listNumbering.js +11 -5
  182. package/dist/prosemirror/noteReferenceOccurrenceId.d.ts +5 -0
  183. package/dist/prosemirror/noteReferenceOccurrenceId.js +5 -0
  184. package/dist/prosemirror/noteReferenceOccurrences.d.ts +21 -0
  185. package/dist/prosemirror/noteReferenceOccurrences.js +185 -0
  186. package/dist/prosemirror/noteReferenceReview.js +14 -1
  187. package/dist/prosemirror/paragraphPropertyCarry.d.ts +7 -1
  188. package/dist/prosemirror/paragraphPropertyCarry.js +8 -3
  189. package/dist/prosemirror/plugins/documentStyles.d.ts +4 -1
  190. package/dist/prosemirror/plugins/documentStyles.js +17 -10
  191. package/dist/prosemirror/plugins/suggestionMode.js +48 -48
  192. package/dist/prosemirror/preservedXmlReview.d.ts +6 -0
  193. package/dist/prosemirror/preservedXmlReview.js +35 -0
  194. package/dist/prosemirror/runFormattingFromMarks.js +2 -2
  195. package/dist/prosemirror/runFormattingReconciliation.js +5 -4
  196. package/dist/prosemirror/runPropertyChangeResolution.d.ts +8 -1
  197. package/dist/prosemirror/runPropertyChangeResolution.js +24 -2
  198. package/dist/prosemirror/runStyleFormatting.d.ts +3 -1
  199. package/dist/prosemirror/runStyleFormatting.js +3 -1
  200. package/dist/prosemirror/schema/marks.d.ts +1 -0
  201. package/dist/prosemirror/schema/nodes.d.ts +2 -0
  202. package/dist/prosemirror/styles/paragraphStyleCascade.js +4 -1
  203. package/dist/prosemirror/styles/resolvedStyleAttrs.d.ts +15 -15
  204. package/dist/prosemirror/styles/resolvedStyleAttrs.js +69 -28
  205. package/dist/prosemirror/textInput.d.ts +5 -1
  206. package/dist/prosemirror/textInput.js +3 -3
  207. package/dist/prosemirror/trackedRevisionPath.d.ts +5 -1
  208. package/dist/prosemirror/trackedRevisionPath.js +39 -1
  209. package/dist/prosemirror/validation.js +3 -5
  210. package/dist/prosemirror/yjsDocumentMetadata.d.ts +9 -3
  211. package/dist/prosemirror/yjsDocumentMetadata.js +37 -8
  212. package/dist/types/canonicalCapabilities.d.ts +201 -0
  213. package/dist/types/canonicalCapabilities.js +198 -0
  214. package/dist/types/canonicalComments.d.ts +51 -0
  215. package/dist/types/canonicalComments.js +0 -0
  216. package/dist/types/canonicalSave.d.ts +11 -0
  217. package/dist/types/canonicalSave.js +0 -0
  218. package/dist/types/canonicalSections.d.ts +12 -0
  219. package/dist/types/canonicalSections.js +0 -0
  220. package/dist/types/docxSerialization.d.ts +14 -0
  221. package/dist/types/docxSerialization.js +7 -0
  222. package/dist/utils/headingCollector.js +45 -4
  223. package/dist/utils/outlineDepth.d.ts +9 -0
  224. package/dist/utils/outlineDepth.js +9 -0
  225. package/package.json +4 -4
@@ -1,54 +1,92 @@
1
1
  import { splitsSurrogatePair } from "../ai-edits/character-boundaries.js";
2
+ import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
3
+ import { setCanonicalInputTimer } from "./canonicalInputTimer.js";
2
4
  import { CanonicalSessionError } from "./canonicalSession.js";
3
5
  import { Result } from "better-result";
6
+ import { EditorState, TextSelection } from "prosemirror-state";
4
7
  //#region src/controller/canonicalComposition.ts
5
- const COMPOSITION_FLUSH_DELAY_MS = 25;
6
8
  /** Native IME owns the provisional view; the captured canonical projection stays unchanged. */
7
9
  const createCanonicalComposition = (options) => {
8
10
  let state = { type: "committed" };
9
11
  let authorizedNativeState = null;
12
+ let completed = { type: "idle" };
10
13
  let timer;
11
14
  const clearTimer = () => {
12
15
  if (timer !== void 0) clearTimeout(timer);
13
16
  timer = void 0;
14
17
  };
18
+ const selectedText = (baseline, proposed) => {
19
+ const { from, to } = baseline.selection;
20
+ const size = proposed.doc.content.size - baseline.doc.content.size + to - from;
21
+ if (size < 0 || from + size > proposed.doc.content.size) return null;
22
+ const text = proposed.doc.textBetween(from, from + size, "", "");
23
+ return text.length === size ? text : null;
24
+ };
25
+ const selectedReplacement = (baseline, proposed) => {
26
+ const { from, to } = baseline.selection;
27
+ const text = selectedText(baseline, proposed);
28
+ if (text === null || !baseline.tr.insertText(text, from, to).doc.eq(proposed.doc)) return null;
29
+ return {
30
+ from,
31
+ to,
32
+ text,
33
+ semantic: "composition"
34
+ };
35
+ };
15
36
  const replacement = (baseline, proposed) => {
16
37
  const { $from, $to } = baseline.selection;
17
38
  if (!$from.parent.isTextblock || !$to.parent.isTextblock) return null;
18
- if (!$from.sameParent($to)) {
19
- const from = baseline.selection.from;
20
- const to = baseline.selection.to;
21
- const insertedSize = proposed.doc.content.size - (baseline.doc.content.size - (to - from));
22
- if (insertedSize < 0 || from + insertedSize > proposed.doc.content.size) return null;
23
- const text = proposed.doc.textBetween(from, from + insertedSize, "", "");
24
- if (text.length !== insertedSize) return null;
25
- if (!baseline.tr.insertText(text, from, to).doc.eq(proposed.doc)) return null;
26
- return {
27
- from,
28
- to,
29
- text,
39
+ const selected = selectedReplacement(baseline, proposed);
40
+ if (selected) {
41
+ if ($from.sameParent($to) && selected.text === baseline.doc.textBetween(selected.from, selected.to, "", "")) return {
42
+ from: selected.from,
43
+ to: selected.from,
44
+ text: "",
30
45
  semantic: "composition"
31
46
  };
47
+ return selected;
32
48
  }
49
+ if (!$from.sameParent($to)) return null;
33
50
  const start = $from.start();
34
51
  const next = proposed.doc.nodeAt(start - 1);
35
- if (!next || next.type !== $from.parent.type || next.content.size !== next.textContent.length) return null;
36
- const before = $from.parent.textContent;
37
- const after = next.textContent;
38
- let from = 0;
39
- while (from < before.length && from < after.length && before[from] === after[from]) from++;
40
- while (splitsSurrogatePair(before, from) || splitsSurrogatePair(after, from)) from--;
41
- let oldEnd = before.length;
42
- let newEnd = after.length;
52
+ if (!next || next.type !== $from.parent.type) return null;
53
+ const previous = $from.parent.content;
54
+ const changedFrom = previous.findDiffStart(next.content);
55
+ if (changedFrom === null) {
56
+ if (!baseline.doc.eq(proposed.doc)) return null;
57
+ return {
58
+ from: baseline.selection.from,
59
+ to: baseline.selection.from,
60
+ text: "",
61
+ semantic: "composition"
62
+ };
63
+ }
64
+ const changedEnd = previous.findDiffEnd(next.content);
65
+ if (changedEnd === null) return null;
66
+ let from = changedFrom;
67
+ let oldEnd = changedEnd.a;
68
+ let newEnd = changedEnd.b;
69
+ const overlap = from - Math.min(oldEnd, newEnd);
70
+ if (overlap > 0) {
71
+ oldEnd += overlap;
72
+ newEnd += overlap;
73
+ }
74
+ const before = previous.textBetween(0, previous.size, "", "");
75
+ const after = next.content.textBetween(0, next.content.size, "", "");
76
+ while (from < oldEnd && from < newEnd && before[from] === after[from]) from++;
43
77
  while (oldEnd > from && newEnd > from && before[oldEnd - 1] === after[newEnd - 1]) {
44
78
  oldEnd--;
45
79
  newEnd--;
46
80
  }
81
+ while (splitsSurrogatePair(before, from) || splitsSurrogatePair(after, from)) from--;
47
82
  while (splitsSurrogatePair(before, oldEnd) || splitsSurrogatePair(after, newEnd)) {
48
83
  oldEnd++;
49
84
  newEnd++;
50
85
  }
51
- const text = after.slice(from, newEnd);
86
+ const removed = previous.cut(from, oldEnd);
87
+ const inserted = next.content.cut(from, newEnd);
88
+ const text = inserted.textBetween(0, inserted.size, "", "");
89
+ if (removed.textBetween(0, removed.size, "", "").length !== removed.size || text.length !== inserted.size) return null;
52
90
  if (!baseline.tr.insertText(text, start + from, start + oldEnd).doc.eq(proposed.doc)) return null;
53
91
  return {
54
92
  from: start + from,
@@ -57,26 +95,62 @@ const createCanonicalComposition = (options) => {
57
95
  semantic: "composition"
58
96
  };
59
97
  };
98
+ const applyNative = (view, transaction) => Result.try({
99
+ try: () => view.state.applyTransaction(transaction),
100
+ catch: (cause) => new CanonicalSessionError({
101
+ gap: CANONICAL_GAP.dispatch,
102
+ reason: "refused",
103
+ message: `Composition projection failed: ${String(cause)}`
104
+ })
105
+ });
60
106
  const finish = (view, commit) => {
61
107
  clearTimer();
62
108
  authorizedNativeState = null;
63
109
  const pending = state;
64
- if (pending.type === "committed") return;
110
+ if (pending.type === "committed") return { type: "idle" };
65
111
  const input = commit && pending.type === "provisional" ? replacement(pending.baseline, view.state) : null;
112
+ const committedText = selectedText(pending.baseline, view.state);
113
+ const proposed = view.state;
114
+ const from = pending.baseline.selection.from;
115
+ completed = { type: "idle" };
66
116
  state = { type: "committed" };
67
- if (!view.isDestroyed) view.updateState(pending.baseline);
117
+ if (!view.isDestroyed) {
118
+ if (view.composing) view.dom.dispatchEvent(new Event("compositionend", { bubbles: true }));
119
+ view.updateState(pending.baseline);
120
+ }
68
121
  options.end();
69
- if (!commit || view.isDestroyed || pending.type === "refused") return;
70
- if (input?.from === input?.to && input?.text === "") return;
122
+ if (!commit || view.isDestroyed) return { type: "cancelled" };
123
+ if (pending.type === "refused") return {
124
+ type: "refused",
125
+ text: pending.text
126
+ };
71
127
  if (!input) {
72
128
  options.refuse("Composition changed unsupported content; the canonical document was restored.");
73
- return;
129
+ return {
130
+ type: "refused",
131
+ text: committedText
132
+ };
74
133
  }
75
- options.replace(input);
134
+ if (input.from !== input.to || input.text !== "") options.replace(input);
135
+ if (committedText !== null && !view.isDestroyed && view.state.doc.textContent === proposed.doc.textContent && from + committedText.length <= view.state.doc.content.size && view.state.doc.textBetween(from, from + committedText.length, "", "") === committedText) completed = {
136
+ type: "completed",
137
+ receipt: {
138
+ view,
139
+ state: view.state,
140
+ from,
141
+ to: from + committedText.length,
142
+ text: committedText,
143
+ marks: pending.baseline.storedMarks ?? pending.baseline.selection.$from.marksAcross(pending.baseline.selection.$to)
144
+ }
145
+ };
146
+ return {
147
+ type: "committed",
148
+ text: committedText
149
+ };
76
150
  };
77
151
  const schedule = (view) => {
78
152
  clearTimer();
79
- timer = setTimeout(() => finish(view, true), COMPOSITION_FLUSH_DELAY_MS);
153
+ timer = setCanonicalInputTimer(() => finish(view, true), 25);
80
154
  };
81
155
  return {
82
156
  get status() {
@@ -85,24 +159,72 @@ const createCanonicalComposition = (options) => {
85
159
  get active() {
86
160
  return state.type !== "committed";
87
161
  },
162
+ matchesCancellationRange: (view, range) => {
163
+ if (state.type === "committed" || state.view !== view) return false;
164
+ const text = selectedText(state.baseline, view.state);
165
+ if (text === null || text.length === 0) return false;
166
+ const from = state.baseline.selection.from;
167
+ if (range.from !== from || range.to !== from + text.length) return false;
168
+ const selection = view.dom.ownerDocument.getSelection();
169
+ if (!selection || selection.rangeCount !== 1) return false;
170
+ const selected = selection.getRangeAt(0);
171
+ if (!view.dom.contains(selected.startContainer) || !view.dom.contains(selected.endContainer)) return false;
172
+ return view.posAtDOM(selected.startContainer, selected.startOffset) === range.from && view.posAtDOM(selected.endContainer, selected.endOffset) === range.to;
173
+ },
88
174
  start: (view) => {
89
175
  if (state.type !== "committed") return;
90
176
  if (!options.begin()) return;
91
177
  state = {
92
178
  type: "provisional",
93
179
  baseline: view.state,
180
+ view,
94
181
  phase: "active"
95
182
  };
96
183
  },
97
184
  ended: (view) => {
98
185
  if (state.type === "committed") return;
99
- state = {
100
- type: state.type,
101
- baseline: state.baseline,
102
- phase: "ended"
103
- };
186
+ switch (state.type) {
187
+ case "provisional":
188
+ state = {
189
+ type: "provisional",
190
+ baseline: state.baseline,
191
+ view: state.view,
192
+ phase: "ended"
193
+ };
194
+ break;
195
+ case "refused":
196
+ state = {
197
+ type: "refused",
198
+ baseline: state.baseline,
199
+ view: state.view,
200
+ phase: "ended",
201
+ text: state.text
202
+ };
203
+ break;
204
+ default:
205
+ }
104
206
  schedule(view);
105
207
  },
208
+ forgetCompleted: () => {
209
+ completed = { type: "idle" };
210
+ },
211
+ get pendingFinal() {
212
+ return completed.type === "authorized";
213
+ },
214
+ authorizeLateFinal: (view, text) => {
215
+ if (completed.type === "idle") return false;
216
+ const { receipt } = completed;
217
+ if (view.isDestroyed || receipt.view !== view || receipt.state !== view.state || text === null) {
218
+ completed = { type: "idle" };
219
+ return false;
220
+ }
221
+ completed = {
222
+ type: "authorized",
223
+ receipt,
224
+ text
225
+ };
226
+ return true;
227
+ },
106
228
  authorizeNative: (view) => {
107
229
  authorizedNativeState = view.state;
108
230
  },
@@ -116,13 +238,53 @@ const createCanonicalComposition = (options) => {
116
238
  recover: (view) => finish(view, true),
117
239
  cancel: (view) => finish(view, false),
118
240
  reset: () => {
119
- clearTimer();
120
- authorizedNativeState = null;
121
- if (state.type !== "committed") options.end();
122
- state = { type: "committed" };
241
+ completed = { type: "idle" };
242
+ if (state.type !== "committed") finish(state.view, false);
243
+ else {
244
+ clearTimer();
245
+ authorizedNativeState = null;
246
+ }
123
247
  },
124
248
  accept: (view, transaction) => {
125
- if (state.type === "committed") return false;
249
+ if (state.type === "committed") {
250
+ const final = completed;
251
+ if (final.type !== "authorized" || final.receipt.view !== view) return false;
252
+ completed = { type: "idle" };
253
+ if (view.isDestroyed || final.receipt.state !== view.state) return false;
254
+ const applied = applyNative(view, transaction);
255
+ const baseline = EditorState.create({
256
+ schema: final.receipt.state.schema,
257
+ doc: final.receipt.state.doc,
258
+ selection: TextSelection.create(final.receipt.state.doc, final.receipt.from, final.receipt.to),
259
+ storedMarks: final.receipt.marks
260
+ });
261
+ const input = applied.isOk() ? replacement(baseline, applied.value.state) : null;
262
+ if (applied.isErr() || !input || input.from < final.receipt.from || input.to > final.receipt.to || selectedText(baseline, applied.value.state) !== final.text) {
263
+ view.updateState(final.receipt.state);
264
+ options.refuse(applied.isErr() ? applied.error.message : "Final composition changed unsupported content; the canonical document was restored.");
265
+ return true;
266
+ }
267
+ if (final.receipt.text !== final.text) options.replace({
268
+ from: final.receipt.from,
269
+ to: final.receipt.to,
270
+ text: final.text,
271
+ semantic: "composition",
272
+ compositionPhase: "correction"
273
+ });
274
+ if (!view.isDestroyed) view.updateState(view.state);
275
+ if (!view.isDestroyed && view.state.doc.textContent === applied.value.state.doc.textContent && final.receipt.from + final.text.length <= view.state.doc.content.size && view.state.doc.textBetween(final.receipt.from, final.receipt.from + final.text.length, "", "") === final.text) completed = {
276
+ type: "completed",
277
+ receipt: {
278
+ view,
279
+ state: view.state,
280
+ from: final.receipt.from,
281
+ to: final.receipt.from + final.text.length,
282
+ text: final.text,
283
+ marks: final.receipt.marks
284
+ }
285
+ };
286
+ return true;
287
+ }
126
288
  if (state.type === "refused") {
127
289
  view.updateState(state.baseline);
128
290
  return true;
@@ -132,19 +294,15 @@ const createCanonicalComposition = (options) => {
132
294
  return true;
133
295
  }
134
296
  authorizedNativeState = null;
135
- const applied = Result.try({
136
- try: () => view.state.applyTransaction(transaction),
137
- catch: (cause) => new CanonicalSessionError({
138
- reason: "refused",
139
- message: `Composition projection failed: ${String(cause)}`
140
- })
141
- });
297
+ const applied = applyNative(view, transaction);
142
298
  if (applied.isErr()) {
143
299
  view.updateState(state.baseline);
144
300
  state = {
145
301
  type: "refused",
146
302
  baseline: state.baseline,
147
- phase: state.phase
303
+ view: state.view,
304
+ phase: state.phase,
305
+ text: selectedText(state.baseline, transaction)
148
306
  };
149
307
  options.refuse(applied.error.message);
150
308
  return true;
@@ -154,7 +312,9 @@ const createCanonicalComposition = (options) => {
154
312
  state = {
155
313
  type: "refused",
156
314
  baseline: state.baseline,
157
- phase: state.phase
315
+ view: state.view,
316
+ phase: state.phase,
317
+ text: selectedText(state.baseline, applied.value.state)
158
318
  };
159
319
  options.refuse("Composition changed unsupported content; the canonical document was restored.");
160
320
  return true;
@@ -0,0 +1,118 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { Node } from "prosemirror-model";
3
+ import { Result } from "better-result";
4
+ //#region src/controller/canonicalInlineProjection.d.ts
5
+ declare const CANONICAL_SOURCE_INLINE_POLICIES: {
6
+ readonly run: "container";
7
+ readonly hyperlink: "container";
8
+ readonly insertion: "container";
9
+ readonly deletion: "container";
10
+ readonly moveFrom: "container";
11
+ readonly moveTo: "container";
12
+ readonly inlineWrapper: "container";
13
+ readonly inlineSdt: "control";
14
+ readonly simpleField: "leaf";
15
+ readonly complexField: "leaf";
16
+ readonly mathEquation: "leaf";
17
+ readonly preservedInline: "leaf";
18
+ readonly bookmarkStart: "leaf";
19
+ readonly bookmarkEnd: "leaf";
20
+ readonly commentRangeStart: "leaf";
21
+ readonly commentRangeEnd: "leaf";
22
+ readonly commentReference: "leaf";
23
+ readonly moveFromRangeStart: "leaf";
24
+ readonly moveFromRangeEnd: "leaf";
25
+ readonly moveToRangeStart: "leaf";
26
+ readonly moveToRangeEnd: "leaf";
27
+ readonly text: "text";
28
+ readonly tab: "leaf";
29
+ readonly break: "leaf";
30
+ readonly symbol: "leaf";
31
+ readonly footnoteRef: "leaf";
32
+ readonly endnoteRef: "leaf";
33
+ readonly noteMarker: "leaf";
34
+ readonly fieldChar: "rawField";
35
+ readonly instrText: "rawField";
36
+ readonly softHyphen: "leaf";
37
+ readonly noBreakHyphen: "leaf";
38
+ readonly renderedPageBreak: "layout";
39
+ readonly preservedXml: "leaf";
40
+ readonly drawing: "leaf";
41
+ readonly shape: "leaf";
42
+ };
43
+ declare const CANONICAL_NATIVE_INLINE_POLICIES: {
44
+ readonly text: "text";
45
+ readonly bookmarkBoundary: "markers";
46
+ readonly moveRangeBoundary: "markers";
47
+ readonly rangeAnchor: "markers";
48
+ readonly noteMarker: "noteMarker";
49
+ readonly preservedXml: "capture";
50
+ readonly field: "unit";
51
+ readonly structuredField: "unit";
52
+ readonly hardBreak: "unit";
53
+ readonly image: "unit";
54
+ readonly math: "unit";
55
+ readonly pageBreakRun: "unit";
56
+ readonly shape: "unit";
57
+ readonly symbol: "unit";
58
+ readonly tab: "unit";
59
+ readonly textBoxAnchor: "unit";
60
+ readonly commentReference: "unit";
61
+ readonly renderedPageBreak: "refused";
62
+ readonly sdt: "refused";
63
+ };
64
+ declare const InlineProjectionError_base: import("better-result").TaggedErrorClass<"InlineProjectionError">;
65
+ declare class InlineProjectionError extends InlineProjectionError_base<{
66
+ message: string;
67
+ }> {}
68
+ /** Every source variant makes a decision before conversion can discard its positions. */
69
+ declare const canonicalInlineSourceIssue: (items: readonly (document_d_exports.ParagraphContent | document_d_exports.RunContent)[], context?: "paragraph" | "fieldPayload") => string | null;
70
+ type NativeCell = {
71
+ type: "text";
72
+ text: string;
73
+ from: number;
74
+ to: number;
75
+ node: Node;
76
+ } | {
77
+ type: "unit";
78
+ from: number;
79
+ to: number;
80
+ node: Node;
81
+ } | {
82
+ type: "markedUnit";
83
+ sourceType: document_d_exports.NoteReferenceContent["type"];
84
+ label: string;
85
+ from: number;
86
+ to: number;
87
+ node: Node;
88
+ } | {
89
+ type: "zeroWidth";
90
+ sources: readonly (document_d_exports.ParagraphContent | document_d_exports.RunContent)[];
91
+ from: number;
92
+ to: number;
93
+ node: Node;
94
+ };
95
+ /** Native positions and deletion geometry read the same inline-cell classification. */
96
+ declare const canonicalNativeCells: (paragraph: Node) => Result<NativeCell[], InlineProjectionError>;
97
+ type CanonicalInlineGap = {
98
+ position: number;
99
+ zeroWidthBefore: number;
100
+ };
101
+ type InlineProjection = {
102
+ text: string;
103
+ boundaries: CanonicalInlineGap[][];
104
+ };
105
+ type CanonicalInlineProjectionArgs = {
106
+ source: document_d_exports.Paragraph;
107
+ paragraph: Node;
108
+ pairedBookmarkIds: ReadonlySet<number>;
109
+ commentContext?: {
110
+ rangedIds: ReadonlySet<number>;
111
+ pointIds: ReadonlySet<number>;
112
+ openIds: ReadonlySet<number>;
113
+ };
114
+ };
115
+ /** Bind rendered gaps to the operation owner's source leaf ordinals, including collapsed pairs. */
116
+ declare const projectCanonicalInline: ({ source, paragraph, pairedBookmarkIds, commentContext }: CanonicalInlineProjectionArgs) => Result<InlineProjection, InlineProjectionError>;
117
+ //#endregion
118
+ export { CANONICAL_NATIVE_INLINE_POLICIES, CANONICAL_SOURCE_INLINE_POLICIES, CanonicalInlineGap, canonicalInlineSourceIssue, canonicalNativeCells, projectCanonicalInline };