@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
@@ -0,0 +1,185 @@
1
+ import { readFootnoteRefMarkAttrs } from "./attrs/index.js";
2
+ import { Result, TaggedError, panic } from "better-result";
3
+ import { Fragment, Mark, Slice } from "prosemirror-model";
4
+ import { Plugin } from "prosemirror-state";
5
+ import { isHistoryTransaction } from "prosemirror-history";
6
+ //#region src/prosemirror/noteReferenceOccurrences.ts
7
+ var NoteReferenceEditRefusal = class extends TaggedError("NoteReferenceEditRefusal") {};
8
+ var NoteReferenceReplayDefect = class extends TaggedError("NoteReferenceReplayDefect") {};
9
+ const NOTE_OCCURRENCE_REPLAY_META = "noteReferenceOccurrenceReplay";
10
+ const markNoteReferenceReplay = (transaction) => {
11
+ transaction.setMeta(NOTE_OCCURRENCE_REPLAY_META, true);
12
+ };
13
+ const NOTE_OCCURRENCE_REPORT_META = "noteReferenceOccurrenceReport";
14
+ const noteReferenceTransactionIssue = (transaction) => {
15
+ const issue = transaction.getMeta(NOTE_OCCURRENCE_REPORT_META);
16
+ return issue instanceof NoteReferenceEditRefusal || issue instanceof NoteReferenceReplayDefect ? issue : void 0;
17
+ };
18
+ const mintNoteReferenceOccurrenceId = () => crypto.randomUUID();
19
+ /** Identity establishes boundaries; text only validates the already bounded unit. */
20
+ const readOccurrences = (paragraph) => {
21
+ const referenceType = paragraph.type.schema.marks["footnoteRef"];
22
+ if (!referenceType || !paragraph.rangeHasMark(0, paragraph.content.size, referenceType)) return Result.ok([]);
23
+ const occurrences = [];
24
+ const seen = /* @__PURE__ */ new Set();
25
+ let previous;
26
+ let issue;
27
+ paragraph.forEach((node, from) => {
28
+ if (issue !== void 0) return;
29
+ const reference = node.marks.find((mark) => mark.type.name === "footnoteRef");
30
+ if (!reference) {
31
+ previous = void 0;
32
+ return;
33
+ }
34
+ const attrs = readFootnoteRefMarkAttrs(reference);
35
+ if (!attrs.ok || !node.isText) {
36
+ issue = "Note references require attributed text occurrences.";
37
+ return;
38
+ }
39
+ const id = attrs.value.occurrenceId;
40
+ if (previous?.id === id) {
41
+ if (!Mark.sameSet(previous.node.marks, node.marks)) {
42
+ issue = "A note reference cannot have different formatting or revision owners within one occurrence.";
43
+ return;
44
+ }
45
+ previous.text += node.text ?? "";
46
+ previous.to = from + node.nodeSize;
47
+ return;
48
+ }
49
+ if (seen.has(id)) {
50
+ issue = "A note-reference occurrence must remain contiguous.";
51
+ return;
52
+ }
53
+ seen.add(id);
54
+ previous = {
55
+ id,
56
+ node,
57
+ text: node.text ?? "",
58
+ from,
59
+ to: from + node.nodeSize
60
+ };
61
+ occurrences.push(previous);
62
+ });
63
+ for (const occurrence of occurrences) {
64
+ const reference = occurrence.node.marks.find((mark) => mark.type.name === "footnoteRef");
65
+ if (!reference) return panic("Missing note-reference mark in an occurrence.");
66
+ const attrs = readFootnoteRefMarkAttrs(reference);
67
+ if (!attrs.ok) return panic("Invalid attributed note-reference occurrence.");
68
+ if (occurrence.text !== String(attrs.value.id)) issue ??= "Edit the whole note reference; its rendered label is indivisible.";
69
+ }
70
+ return issue === void 0 ? Result.ok(occurrences) : Result.err(new NoteReferenceEditRefusal({ message: issue }));
71
+ };
72
+ /** Saving asserts the edit-owner invariant and emits each occurrence once. */
73
+ const coalesceNoteReferenceOccurrences = (paragraph) => {
74
+ if (saveOccurrenceValidator?.cachedIds(paragraph)?.length === 0) return paragraph;
75
+ const referenceType = paragraph.type.schema.marks["footnoteRef"];
76
+ if (!referenceType || !paragraph.rangeHasMark(0, paragraph.content.size, referenceType)) return paragraph;
77
+ const result = readOccurrences(paragraph);
78
+ if (result.isErr()) return panic(result.error.message);
79
+ if (result.value.every(({ node, from, to }) => node.nodeSize === to - from)) return paragraph;
80
+ const byStart = new Map(result.value.map((occurrence) => [occurrence.from, occurrence]));
81
+ const nodes = [];
82
+ let through = -1;
83
+ paragraph.forEach((node, from) => {
84
+ if (from < through) return;
85
+ const occurrence = byStart.get(from);
86
+ if (occurrence) {
87
+ nodes.push(node.type.schema.text(occurrence.text, node.marks));
88
+ through = occurrence.to;
89
+ } else nodes.push(node);
90
+ });
91
+ return paragraph.copy(Fragment.fromArray(nodes));
92
+ };
93
+ const createOccurrenceValidator = () => {
94
+ const cache = /* @__PURE__ */ new WeakMap();
95
+ const validate = (node) => {
96
+ const cached = cache.get(node);
97
+ if (cached) return cached;
98
+ const ids = /* @__PURE__ */ new Set();
99
+ const include = (id) => {
100
+ if (ids.has(id)) return new NoteReferenceEditRefusal({ message: "Separate note references require distinct occurrence identities." });
101
+ ids.add(id);
102
+ };
103
+ if (node.inlineContent) {
104
+ const own = readOccurrences(node);
105
+ if (own.isErr()) return Result.err(own.error);
106
+ for (const occurrence of own.value) {
107
+ const issue = include(occurrence.id);
108
+ if (issue) return Result.err(issue);
109
+ }
110
+ }
111
+ for (let index = 0; index < node.childCount; index += 1) {
112
+ const child = node.child(index);
113
+ if (child.isLeaf) continue;
114
+ const nested = validate(child);
115
+ if (nested.isErr()) return nested;
116
+ for (const id of nested.value) {
117
+ const issue = include(id);
118
+ if (issue) return Result.err(issue);
119
+ }
120
+ }
121
+ const result = Result.ok([...ids]);
122
+ cache.set(node, result);
123
+ return result;
124
+ };
125
+ return {
126
+ validate,
127
+ cachedIds: (node) => {
128
+ const facts = cache.get(node);
129
+ return facts?.isOk() ? facts.value : void 0;
130
+ },
131
+ validateSave: (doc) => {
132
+ const facts = cache.get(doc);
133
+ if (facts?.isOk() && facts.value.length === 0) return facts;
134
+ cache.delete(doc);
135
+ return validate(doc);
136
+ }
137
+ };
138
+ };
139
+ let saveOccurrenceValidator;
140
+ const assertNoteReferenceOccurrences = (doc) => {
141
+ const result = (saveOccurrenceValidator ??= createOccurrenceValidator()).validateSave(doc);
142
+ if (result.isErr()) panic(result.error.message);
143
+ };
144
+ /** Production edit boundary: refuse before committing an unsaveable occurrence. */
145
+ const noteReferenceOccurrencePlugin = () => {
146
+ const { validate } = createOccurrenceValidator();
147
+ return new Plugin({
148
+ filterTransaction(transaction) {
149
+ if (!transaction.docChanged) return true;
150
+ const result = validate(transaction.doc);
151
+ if (result.isOk()) return true;
152
+ const replay = isHistoryTransaction(transaction) || transaction.getMeta(NOTE_OCCURRENCE_REPLAY_META) === true;
153
+ transaction.setMeta(NOTE_OCCURRENCE_REPORT_META, replay ? new NoteReferenceReplayDefect({ message: result.error.message }) : result.error);
154
+ return replay;
155
+ },
156
+ props: { transformPasted(slice) {
157
+ const identities = /* @__PURE__ */ new Map();
158
+ const remint = (fragment) => {
159
+ const nodes = [];
160
+ fragment.forEach((node) => {
161
+ const marks = node.marks.map((mark) => {
162
+ if (mark.type.name !== "footnoteRef") return mark;
163
+ const attrs = readFootnoteRefMarkAttrs(mark);
164
+ if (!attrs.ok) return mark;
165
+ const id = attrs.value.occurrenceId;
166
+ let replacement = identities.get(id);
167
+ if (replacement === void 0) {
168
+ replacement = mintNoteReferenceOccurrenceId();
169
+ identities.set(id, replacement);
170
+ }
171
+ return mark.type.create({
172
+ ...mark.attrs,
173
+ occurrenceId: replacement
174
+ });
175
+ });
176
+ nodes.push(node.copy(remint(node.content)).mark(marks));
177
+ });
178
+ return Fragment.fromArray(nodes);
179
+ };
180
+ return new Slice(remint(slice.content), slice.openStart, slice.openEnd);
181
+ } }
182
+ });
183
+ };
184
+ //#endregion
185
+ export { NoteReferenceEditRefusal, NoteReferenceReplayDefect, assertNoteReferenceOccurrences, coalesceNoteReferenceOccurrences, markNoteReferenceReplay, mintNoteReferenceOccurrenceId, noteReferenceOccurrencePlugin, noteReferenceTransactionIssue };
@@ -2,6 +2,7 @@ import { withoutLostCommentThreads } from "../ai-edits/comment-lifecycle.js";
2
2
  import { visitCommentMarkers } from "../docx/commentAnchorIndex.js";
3
3
  import { isSeparatorEndnote, isSeparatorFootnote } from "../docx/footnoteParser.js";
4
4
  import { pluginsForHeadlessRevisionResolution } from "../internal/headlessRevisionResolutionGuard.js";
5
+ import { addTrackedDeletionMark } from "./addTrackedDeletionMark.js";
5
6
  import { expectFootnoteRefMarkAttrs, expectParagraphAttrs } from "./attrs/index.js";
6
7
  import { rejectAIEditRevision } from "./commands/comments.js";
7
8
  import { proseDocToBlocks } from "./conversion/fromProseDoc.js";
@@ -13,6 +14,12 @@ import { schema, singletonManager } from "./schema/index.js";
13
14
  import { Transform } from "prosemirror-transform";
14
15
  import { EditorState } from "prosemirror-state";
15
16
  //#region src/prosemirror/noteReferenceReview.ts
17
+ /**
18
+ * A note goes with its reference. Deleting a note's reference while tracking
19
+ * changes deletes the note's text too, so rejecting that deletion must give
20
+ * the note its text back, whichever surface resolves it: the body editor
21
+ * resolves the reference, and this resolves the note's story to match.
22
+ */
16
23
  const noteKey = (kind, id) => `${kind}:${id}`;
17
24
  /**
18
25
  * The notes a story's text refers to, and whether each reference is pending
@@ -155,7 +162,13 @@ const deleteNoteWithReference = (transform, reference) => {
155
162
  kind: "del",
156
163
  info: markInfo
157
164
  });
158
- if (node.isInline && !deletion.type.isInSet(node.marks)) transform.addMark(position, position + node.nodeSize, deletion);
165
+ if (node.isInline && !deletion.type.isInSet(node.marks)) addTrackedDeletionMark({
166
+ insertionPolicy: "preserve-pending",
167
+ tr: transform,
168
+ from: position,
169
+ to: position + node.nodeSize,
170
+ mark: deletion
171
+ });
159
172
  return !node.isInline;
160
173
  });
161
174
  return transform;
@@ -4,6 +4,7 @@ import { NumberingMap } from "../docx/numberingParser.js";
4
4
  import { Node } from "prosemirror-model";
5
5
  import { Transaction } from "prosemirror-state";
6
6
  //#region src/prosemirror/paragraphPropertyCarry.d.ts
7
+ type RecordedFormatting = NonNullable<ParagraphPropertyChangeAttrs["previousFormatting"]>;
7
8
  type CarryParagraphPropertiesOptions = {
8
9
  tr: Transaction;
9
10
  /** The paragraph that takes the properties. */
@@ -17,6 +18,11 @@ type CarryParagraphPropertiesOptions = {
17
18
  /** Properties the paragraph keeps its own values of: ones its own batch set. */
18
19
  keep?: ReadonlySet<string>;
19
20
  };
21
+ type CarriedParagraphPropertiesOptions = Pick<CarryParagraphPropertiesOptions, "source" | "keep"> & {
22
+ target: Node;
23
+ };
24
+ /** The exact formatting transfer, shared with pre-mutation capacity checks. */
25
+ declare const carriedParagraphProperties: ({ source, target, keep }: CarriedParagraphPropertiesOptions) => RecordedFormatting;
20
26
  /**
21
27
  * Give a paragraph another one's properties: the pPr a property change
22
28
  * covers. Its identity, mark, mark run properties and section stay its own,
@@ -61,4 +67,4 @@ declare const recordReplacedParagraphProperties: ({ tr, position, replaced, revi
61
67
  */
62
68
  declare const propertiesSetInBatch: (node: Node, batchRevisionIds: ReadonlySet<number>) => ReadonlySet<string>;
63
69
  //#endregion
64
- export { carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
70
+ export { carriedParagraphProperties, carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
@@ -17,6 +17,8 @@ import { panic } from "better-result";
17
17
  * revision, so accepting reads as the direct edit and rejecting restores the
18
18
  * second's own.
19
19
  */
20
+ /** The exact formatting transfer, shared with pre-mutation capacity checks. */
21
+ const carriedParagraphProperties = ({ source, target, keep }) => Object.fromEntries([...Object.entries(paragraphPropertiesSnapshot(source)).filter(([key]) => !keep?.has(key)), ...Object.entries(paragraphPropertiesSnapshot(target)).filter(([key]) => keep?.has(key))]);
20
22
  /**
21
23
  * Give a paragraph another one's properties: the pPr a property change
22
24
  * covers. Its identity, mark, mark run properties and section stay its own,
@@ -32,8 +34,11 @@ const carryParagraphProperties = ({ tr, position, source, styleResolver, numberi
32
34
  tracked: false
33
35
  };
34
36
  const previousFormatting = paragraphPropertiesSnapshot(target);
35
- const own = previousFormatting;
36
- const formatting = Object.fromEntries([...Object.entries(paragraphPropertiesSnapshot(source)).filter(([key]) => !keep?.has(key)), ...Object.entries(own).filter(([key]) => keep?.has(key))]);
37
+ const formatting = carriedParagraphProperties({
38
+ source,
39
+ target,
40
+ ...keep === void 0 ? {} : { keep }
41
+ });
37
42
  if (JSON.stringify(previousFormatting) === JSON.stringify(formatting)) return {
38
43
  tr,
39
44
  changed: false,
@@ -152,4 +157,4 @@ const propertiesSetInBatch = (node, batchRevisionIds) => {
152
157
  return new Set([.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(now)])].filter((key) => JSON.stringify(before[key]) !== JSON.stringify(now[key])));
153
158
  };
154
159
  //#endregion
155
- export { carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
160
+ export { carriedParagraphProperties, carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
@@ -3,6 +3,9 @@ import { StyleResolver } from "../styles/styleResolver.js";
3
3
  import { documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver } from "./documentStyleState.js";
4
4
  import { EditorState, Plugin } from "prosemirror-state";
5
5
  //#region src/prosemirror/plugins/documentStyles.d.ts
6
+ type DocumentStylesInput = document_d_exports.StyleDefinitions | StyleResolver | null | undefined;
7
+ /** Canonical projections own formatting; expose the resolver without PM repair transactions. */
8
+ declare const createDocumentStyleContextPlugin: (styles: DocumentStylesInput) => Plugin;
6
9
  /**
7
10
  * Create the plugin holding a StyleResolver for the document's `styles` for
8
11
  * the lifetime of the EditorState. The resolver is fixed per document load;
@@ -14,4 +17,4 @@ declare function createDocumentStylesPlugin(styles: document_d_exports.StyleDefi
14
17
  /** Reconfigure so ProseMirror replaces the keyed plugin's resolver state. */
15
18
  declare const withDocumentStyles: (state: EditorState, styles: document_d_exports.StyleDefinitions | StyleResolver | null | undefined) => EditorState;
16
19
  //#endregion
17
- export { createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
20
+ export { createDocumentStyleContextPlugin, createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
@@ -76,6 +76,21 @@ const bareResolver = () => {
76
76
  bareStyleResolver ??= createStyleResolver(void 0);
77
77
  return bareStyleResolver;
78
78
  };
79
+ const documentStyleContextSpec = (styles) => {
80
+ let resolver;
81
+ if (styles instanceof StyleResolver) resolver = styles;
82
+ else if (styles) resolver = createStyleResolver(styles);
83
+ else resolver = null;
84
+ return {
85
+ key: documentStylesKey,
86
+ state: {
87
+ init: () => resolver,
88
+ apply: (_tr, value) => value
89
+ }
90
+ };
91
+ };
92
+ /** Canonical projections own formatting; expose the resolver without PM repair transactions. */
93
+ const createDocumentStyleContextPlugin = (styles) => new Plugin(documentStyleContextSpec(styles));
79
94
  /**
80
95
  * Create the plugin holding a StyleResolver for the document's `styles` for
81
96
  * the lifetime of the EditorState. The resolver is fixed per document load;
@@ -84,16 +99,8 @@ const bareResolver = () => {
84
99
  * have one.
85
100
  */
86
101
  function createDocumentStylesPlugin(styles) {
87
- let resolver;
88
- if (styles instanceof StyleResolver) resolver = styles;
89
- else if (styles) resolver = createStyleResolver(styles);
90
- else resolver = null;
91
102
  return new Plugin({
92
- key: documentStylesKey,
93
- state: {
94
- init: () => resolver,
95
- apply: (_tr, value) => value
96
- },
103
+ ...documentStyleContextSpec(styles),
97
104
  appendTransaction: (transactions, _oldState, newState) => {
98
105
  const styleResolver = documentStylesKey.getState(newState) ?? bareResolver();
99
106
  const tr = resolveEditedParagraphStyles(transactions, newState, styleResolver, () => getDocumentNumbering(newState)) ?? newState.tr;
@@ -109,4 +116,4 @@ const withDocumentStyles = (state, styles) => {
109
116
  return state.reconfigure({ plugins: retained }).reconfigure({ plugins: [...retained, createDocumentStylesPlugin(styles)] });
110
117
  };
111
118
  //#endregion
112
- export { createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
119
+ export { createDocumentStyleContextPlugin, createDocumentStylesPlugin, documentStylesKey, getDocumentBuiltInStyles, getDocumentStyleDefinitions, getDocumentStyleResolver, withDocumentStyles };
@@ -1,3 +1,4 @@
1
+ import { addTrackedDeletionMark } from "../addTrackedDeletionMark.js";
1
2
  import { expectParagraphAttrs, expectTrackedChangeMarkAttrs } from "../attrs/index.js";
2
3
  import { clearIndentOnBackspace } from "../commands/clearParagraphIndent.js";
3
4
  import { paragraphPropertiesSnapshot } from "../commands/propertyChangeScope.js";
@@ -117,20 +118,13 @@ function findAdjacentRevisionForRange(doc, from, to, markTypeName, author) {
117
118
  * Retract the current author's own inserted text.
118
119
  * Processes in reverse order to maintain position validity.
119
120
  */
120
- function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, pluginState, joinState) {
121
- const ranges = [];
121
+ function markRangeAsDeleted(tr, doc, from, to, deletionType, pluginState, joinState) {
122
+ let hasMarkableContent = false;
122
123
  doc.nodesBetween(from, to, (node, pos) => {
123
124
  if (!canCarryTrackedRunMark(node)) return;
124
- const start = Math.max(pos, from);
125
- const end = Math.min(pos + node.nodeSize, to);
126
- if (start >= end) return;
127
- const isOwnInsert = node.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
125
+ if (Math.max(pos, from) >= Math.min(pos + node.nodeSize, to)) return;
128
126
  if (node.marks.some((m) => m.type === deletionType)) return;
129
- ranges.push({
130
- from: start,
131
- to: end,
132
- isOwnInsert
133
- });
127
+ hasMarkableContent = true;
134
128
  });
135
129
  const delAttrs = findAdjacentRevisionForRange(doc, from, to, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
136
130
  let removedMark = false;
@@ -165,18 +159,20 @@ function markRangeAsDeleted(tr, doc, from, to, insertionType, deletionType, plug
165
159
  date: delAttrs.date
166
160
  });
167
161
  }
168
- if (ranges.length === 0) return;
162
+ if (!hasMarkableContent) return;
169
163
  encloseWholeControls({
170
164
  tr,
171
165
  from,
172
166
  to,
173
167
  revisionId: delAttrs.revisionId
174
168
  });
175
- for (let i = ranges.length - 1; i >= 0; i--) {
176
- const range = ranges[i];
177
- if (range.isOwnInsert) tr.delete(range.from, range.to);
178
- else tr.addMark(range.from, range.to, deletionType.create(delAttrs));
179
- }
169
+ addTrackedDeletionMark({
170
+ tr,
171
+ from,
172
+ to,
173
+ mark: deletionType.create(delAttrs),
174
+ insertionPolicy: "retract-own"
175
+ });
180
176
  }
181
177
  /** Preserve clipboard revisions inside the revision that inserted their content. */
182
178
  function enclosePastedRunRevisions({ tr, from, to, revision: insertion }) {
@@ -298,7 +294,7 @@ function handleSuggestionPaste(view, slice, pluginState) {
298
294
  const endsContainer = wholeBlocks && paragraphEndsItsContainer(doc.resolve($to.before()), $to.parent.type.name);
299
295
  const rotates = endsContainer && slice.content.firstChild?.type === $to.parent.type && slice.content.lastChild?.type === $to.parent.type;
300
296
  const tableFirst = endsContainer && !rotates;
301
- if (!tableFirst) markRangeAsDeleted(tr, doc, from, wholeBlocks && !endsContainer ? $to.after() : to, insertionType, deletionType, pluginState);
297
+ if (!tableFirst) markRangeAsDeleted(tr, doc, from, wholeBlocks && !endsContainer ? $to.after() : to, deletionType, pluginState);
302
298
  const carryAcross = () => {
303
299
  if (selectsAll || $from.parent.type.name !== "paragraph" || $from.parent === $to.parent || from <= $from.start()) return;
304
300
  const firstPos = tr.mapping.map($from.before());
@@ -332,7 +328,7 @@ function handleSuggestionPaste(view, slice, pluginState) {
332
328
  let insertTo = placed.length === 0 ? at : Math.max(...placed.map((range) => range.to));
333
329
  if (tableFirst) {
334
330
  const struck = tr.steps.length;
335
- markRangeAsDeleted(tr, tr.doc, tr.mapping.map(from), tr.mapping.map(to), insertionType, deletionType, pluginState);
331
+ markRangeAsDeleted(tr, tr.doc, tr.mapping.map(from), tr.mapping.map(to), deletionType, pluginState);
336
332
  const since = tr.mapping.slice(struck);
337
333
  insertFrom = since.map(insertFrom, -1);
338
334
  insertTo = since.map(insertTo, -1);
@@ -405,7 +401,7 @@ function handleSuggestionTableCellPaste(view, slice, pluginState) {
405
401
  if (!cell) return;
406
402
  const { from, to } = cellPasteRange(cell, cellPos, content);
407
403
  const mapFrom = tr.mapping.maps.length;
408
- markRangeAsDeleted(tr, tr.doc, from, to, insertionType, deletionType, pluginState);
404
+ markRangeAsDeleted(tr, tr.doc, from, to, deletionType, pluginState);
409
405
  const insertFrom = tr.mapping.slice(mapFrom).map(to);
410
406
  const sizeBefore = tr.doc.content.size;
411
407
  tr.replaceRange(insertFrom, insertFrom, content);
@@ -538,7 +534,7 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
538
534
  const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
539
535
  if (from !== to) {
540
536
  const deletionType = view.state.schema.marks["deletion"];
541
- if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, insertionType, deletionType, pluginState, view.state);
537
+ if (deletionType) markRangeAsDeleted(tr, view.state.doc, from, to, deletionType, pluginState, view.state);
542
538
  }
543
539
  const insertAt = tr.mapping.map(to);
544
540
  tr.insertText(text, insertAt, insertAt);
@@ -597,7 +593,7 @@ function suggestRangeDeletion(state, tr, from, to) {
597
593
  const insertionType = state.schema.marks["insertion"];
598
594
  const deletionType = state.schema.marks["deletion"];
599
595
  if (!pluginState?.active || !insertionType || !deletionType) return false;
600
- markRangeAsDeleted(tr, tr.doc, from, to, insertionType, deletionType, pluginState);
596
+ markRangeAsDeleted(tr, tr.doc, from, to, deletionType, pluginState);
601
597
  return true;
602
598
  }
603
599
  /**
@@ -661,21 +657,23 @@ function handleSuggestionEnter(view, pluginState) {
661
657
  view.dispatch(tr.scrollIntoView());
662
658
  return true;
663
659
  }
664
- /** Find the adjacent visible unit without consuming a bookmark range marker. */
660
+ /** Find the adjacent visible unit without consuming retained deletions or bookmark markers. */
665
661
  function caretDeleteTarget(state, direction) {
666
662
  const { $from } = state.selection;
667
663
  const backward = direction === "backward";
668
- let from = backward ? $from.pos - 1 : $from.pos;
669
- let to = backward ? $from.pos : $from.pos + 1;
670
- while (from >= $from.start() && to <= $from.end()) {
671
- const adjacent = state.doc.resolve(from).nodeAfter;
672
- if (adjacent?.type.name !== "bookmarkBoundary") return {
673
- type: "inlineUnit",
674
- from,
675
- to
676
- };
677
- from += backward ? -adjacent.nodeSize : adjacent.nodeSize;
678
- to += backward ? -adjacent.nodeSize : adjacent.nodeSize;
664
+ let cursor = $from.pos;
665
+ while (cursor >= $from.start() && cursor <= $from.end()) {
666
+ const adjacent = backward ? state.doc.resolve(cursor).nodeBefore : state.doc.resolve(cursor).nodeAfter;
667
+ if (!adjacent) return { type: "paragraphEdge" };
668
+ if (adjacent.type.name !== "bookmarkBoundary" && !adjacent.marks.some((mark) => mark.type === state.schema.marks["deletion"])) {
669
+ const size = adjacent.isAtom && !adjacent.isText ? adjacent.nodeSize : 1;
670
+ return {
671
+ type: "inlineUnit",
672
+ from: backward ? cursor - size : cursor,
673
+ to: backward ? cursor : cursor + size
674
+ };
675
+ }
676
+ cursor += backward ? -adjacent.nodeSize : adjacent.nodeSize;
679
677
  }
680
678
  return { type: "paragraphEdge" };
681
679
  }
@@ -793,7 +791,7 @@ function handleSuggestionDelete(state, dispatch, direction) {
793
791
  from: rangeFrom.pos,
794
792
  to: rangeTo.pos
795
793
  }).sort((a, b) => b.from - a.from);
796
- for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, insertionType, deletionType, pluginState, state);
794
+ for (const { from, to } of ranges) markRangeAsDeleted(tr, state.doc, from, to, deletionType, pluginState, state);
797
795
  if (state.selection instanceof CellSelection) tr.setSelection(state.selection.map(tr.doc, tr.mapping));
798
796
  else {
799
797
  const cursorPos = tr.mapping.map($to.pos);
@@ -821,17 +819,20 @@ function handleSuggestionDelete(state, dispatch, direction) {
821
819
  const rangeTo = noteRange?.to ?? deleteEnd;
822
820
  const nodeAfter = state.doc.resolve(rangeFrom).nodeAfter;
823
821
  if (!nodeAfter || !canCarryTrackedRunMark(nodeAfter)) return false;
824
- const hasOwnInsertion = nodeAfter.marks.some((m) => m.type === insertionType && m.attrs["author"] === pluginState.author);
825
- if (nodeAfter.marks.some((m) => m.type === deletionType)) {
826
- const newPos = isBackward ? rangeFrom : rangeTo;
827
- tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
828
- } else if (hasOwnInsertion) tr.delete(rangeFrom, rangeTo);
829
- else {
822
+ const mapFrom = tr.mapping.maps.length;
823
+ if (noteRange) markRangeAsDeleted(tr, state.doc, noteRange.from, noteRange.to, deletionType, pluginState);
824
+ else if (!nodeAfter.marks.some((m) => m.type === deletionType)) {
830
825
  const delAttrs = findAdjacentRevisionForRange(state.doc, rangeFrom, rangeTo, "deletion", pluginState.author) || makeMarkAttrs(pluginState);
831
- tr.addMark(rangeFrom, rangeTo, deletionType.create(delAttrs));
832
- const newPos = isBackward ? rangeFrom : rangeTo;
833
- tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
826
+ addTrackedDeletionMark({
827
+ insertionPolicy: "retract-own",
828
+ tr,
829
+ from: rangeFrom,
830
+ to: rangeTo,
831
+ mark: deletionType.create(delAttrs)
832
+ });
834
833
  }
834
+ const newPos = tr.mapping.slice(mapFrom).map(isBackward ? rangeFrom : rangeTo);
835
+ tr.setSelection(TextSelection.near(tr.doc.resolve(newPos)));
835
836
  dispatch(tr.scrollIntoView());
836
837
  return true;
837
838
  }
@@ -848,7 +849,7 @@ function markComposedAsInsertion({ view, from, pluginState, replaced, compositio
848
849
  if (compositionId !== null) tr.setMeta("composition", compositionId);
849
850
  if (replaced) {
850
851
  tr.replaceRange(from, from, replaced);
851
- markRangeAsDeleted(tr, tr.doc, from, tr.mapping.map(from, 1), insertionType, deletionType, pluginState);
852
+ markRangeAsDeleted(tr, tr.doc, from, tr.mapping.map(from, 1), deletionType, pluginState);
852
853
  const restoredEnd = tr.doc.resolve(tr.mapping.map(from, 1));
853
854
  const restoredStart = tr.doc.resolve(from);
854
855
  if (restoredStart.parent.type.name === "paragraph" && restoredEnd.parent.type.name === "paragraph" && restoredStart.parent !== restoredEnd.parent) {
@@ -918,14 +919,14 @@ function replacedInline(before, step) {
918
919
  * rejecting restores it and accepting removes it. The author's own
919
920
  * insertions are retracted instead.
920
921
  */
921
- function restoreReplacedInline({ tr, replaced, insertionType, deletionType, pluginState }) {
922
+ function restoreReplacedInline({ tr, replaced, deletionType, pluginState }) {
922
923
  for (const { at, content } of replaced) {
923
924
  const pos = tr.mapping.map(at, -1);
924
925
  const $pos = tr.doc.resolve(pos);
925
926
  const index = $pos.index();
926
927
  if (!$pos.parent.canReplace(index, index, content)) continue;
927
928
  tr.insert(pos, content);
928
- markRangeAsDeleted(tr, tr.doc, pos, pos + content.size, insertionType, deletionType, pluginState);
929
+ markRangeAsDeleted(tr, tr.doc, pos, pos + content.size, deletionType, pluginState);
929
930
  }
930
931
  }
931
932
  /**
@@ -1101,7 +1102,6 @@ function createSuggestionModePlugin(initialActive = false, author = "User") {
1101
1102
  if (deletionType) restoreReplacedInline({
1102
1103
  tr,
1103
1104
  replaced,
1104
- insertionType,
1105
1105
  deletionType,
1106
1106
  pluginState
1107
1107
  });
@@ -0,0 +1,6 @@
1
+ import { Mark, Node } from "prosemirror-model";
2
+ //#region src/prosemirror/preservedXmlReview.d.ts
3
+ /** A retained opaque deleted result becomes ordinary text when its last deletion resolves. */
4
+ declare const resolvedPreservedXmlAttrs: (node: Node, marks: readonly Mark[]) => import("prosemirror-model").Attrs;
5
+ //#endregion
6
+ export { resolvedPreservedXmlAttrs };
@@ -0,0 +1,35 @@
1
+ import { captureVerbatimXml } from "../docx/verbatimCapture.js";
2
+ import { OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, cloneElement, getChildElements, getLocalName, parseXml } from "../docx/xmlParser.js";
3
+ import { expectPreservedXmlAttrs, expectTrackedChangeMarkAttrs } from "./attrs/index.js";
4
+ //#region src/prosemirror/preservedXmlReview.ts
5
+ const restoreDeletedText = (node) => {
6
+ if (node.type !== "element") return node;
7
+ const name = getLocalName(node.name);
8
+ let target;
9
+ if (name === "delText") target = "t";
10
+ if (name === "delInstrText") target = "instrText";
11
+ const renamed = target !== void 0 && WORDPROCESSINGML_NAMESPACE_URIS.has(node.namespaceUri ?? "") ? node.name?.replace(/[^:]+$/u, target) : node.name;
12
+ const elements = node.elements?.map(restoreDeletedText);
13
+ if (renamed === node.name && elements?.every((child, index) => child === node.elements?.[index]) !== false) return node;
14
+ return cloneElement(node, {
15
+ ...renamed !== void 0 && renamed !== node.name ? { name: renamed } : {},
16
+ ...elements ? { elements } : {}
17
+ });
18
+ };
19
+ const hasPendingDeletion = (marks) => marks.some((mark) => {
20
+ if (mark.type.name === "deletion") return true;
21
+ if (mark.type.name !== "insertion") return false;
22
+ return expectTrackedChangeMarkAttrs(mark)._docxRevisionAncestors?.some((ancestor) => ancestor.type === "deletion" || ancestor.type === "moveFrom") ?? false;
23
+ });
24
+ /** A retained opaque deleted result becomes ordinary text when its last deletion resolves. */
25
+ const resolvedPreservedXmlAttrs = (node, marks) => {
26
+ if (node.type.name !== "preservedXml" || !hasPendingDeletion(node.marks) || hasPendingDeletion(marks)) return node.attrs;
27
+ const attrs = expectPreservedXmlAttrs(node);
28
+ const xml = getChildElements(parseXml(attrs.xml, OOXML_NAMESPACE_SCOPE)).map((element) => captureVerbatimXml(restoreDeletedText(element))).join("");
29
+ return xml === attrs.xml ? node.attrs : {
30
+ ...node.attrs,
31
+ xml
32
+ };
33
+ };
34
+ //#endregion
35
+ export { resolvedPreservedXmlAttrs };
@@ -4,7 +4,7 @@ import { expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectE
4
4
  import { runShadingAttrsToShading, shadingToRunShadingAttrs } from "./conversion/runShadingMark.js";
5
5
  import { applyRunFormattingOverrideAttrs, buildRunFormattingOverrideAttrs } from "./extensions/marks/RunFormattingOverrideExtension.js";
6
6
  import { authoredRunFormattingFromAttrs, hasAuthoredRunFormattingProvenance } from "./runFormattingProvenance.js";
7
- import { paragraphFormattingForRun, resolveEffectiveRunStyleFormatting, suppressParagraphMarkFormatting } from "./runStyleFormatting.js";
7
+ import { isParagraphMarkSuppressionEligible, paragraphFormattingForRun, resolveEffectiveRunStyleFormatting, suppressParagraphMarkFormatting } from "./runStyleFormatting.js";
8
8
  //#region src/prosemirror/runFormattingFromMarks.ts
9
9
  const RUN_FORMATTING_VISUAL_GROUPS = {
10
10
  bold: "bold",
@@ -570,7 +570,7 @@ function marksToTextFormatting(marks, options) {
570
570
  inheritedFormatting: suppressedInheritedFormatting,
571
571
  observedFormatting: formatting
572
572
  });
573
- if (formattingAuthorshipCost(suppressed) < formattingAuthorshipCost(reconciled)) reconciled = suppressed;
573
+ if (isParagraphMarkSuppressionEligible(suppressed) && formattingAuthorshipCost(suppressed) < formattingAuthorshipCost(reconciled)) reconciled = suppressed;
574
574
  }
575
575
  return {
576
576
  ...reconciled,
@@ -3,7 +3,7 @@ import { expectCharacterStyleMarkAttrs } from "./attrs/index.js";
3
3
  import { textFormattingToMarks } from "./extensions/marks/markUtils.js";
4
4
  import { marksToTextFormatting } from "./runFormattingFromMarks.js";
5
5
  import { RUN_FORMATTING_MARK_NAMES } from "./runFormattingMarkNames.js";
6
- import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, paragraphFormattingForRun, resolveEffectiveRunStyleFormatting } from "./runStyleFormatting.js";
6
+ import { getParagraphMarkSuppressionOverrides, hasDirectRunFormatting, isParagraphMarkSuppressionEligible, paragraphFormattingForRun, resolveEffectiveRunStyleFormatting } from "./runStyleFormatting.js";
7
7
  import { Mark } from "prosemirror-model";
8
8
  //#region src/prosemirror/runFormattingReconciliation.ts
9
9
  /** Read authored run properties without confusing inherited visuals for direct formatting. */
@@ -37,14 +37,15 @@ const reconcileRunFormattingMarks = ({ authoredFormatting, context, node, styleR
37
37
  paragraphFormatting,
38
38
  ...styleResolver !== void 0 ? { styleResolver } : {}
39
39
  }), authoredFormatting);
40
- const overrideFormatting = mergeTextFormatting(getParagraphMarkSuppressionOverrides({
40
+ const overrideFormatting = mergeTextFormatting(isParagraphMarkSuppressionEligible(authoredFormatting) ? getParagraphMarkSuppressionOverrides({
41
41
  directFormatting: authoredFormatting,
42
42
  paragraphMarkFormatting: context.paragraphMarkFormatting,
43
43
  suppressedFormatting: paragraphFormatting
44
- }), authoredFormatting);
44
+ }) : void 0, authoredFormatting);
45
45
  const formattingMarks = textFormattingToMarks(effectiveFormatting, node.type.schema, {
46
46
  overrideFormatting,
47
- directFormatting: authoredFormatting
47
+ directFormatting: authoredFormatting,
48
+ authoredCarrier: "preserve"
48
49
  });
49
50
  return Mark.setFrom([
50
51
  ...marks.filter(({ type }) => !RUN_FORMATTING_MARK_NAMES.has(type.name)),
@@ -14,5 +14,12 @@ type ReconstructRejectedRunFormattingMarksOptions = {
14
14
  * owner so their reject semantics cannot diverge at the bulk threshold.
15
15
  */
16
16
  declare const reconstructRejectedRunFormattingMarks: ({ node, paragraphContext, previousFormatting, styleResolver }: ReconstructRejectedRunFormattingMarksOptions) => readonly Mark[];
17
+ type RestoreHistoricalRunFormattingOptions = {
18
+ node: Node;
19
+ paragraphContext: ParagraphRunStyleContext;
20
+ styleResolver?: RunStyleResolver | null;
21
+ };
22
+ /** Restored deletions inherit the live paragraph cascade without authoring their historical visuals. */
23
+ declare const restoreHistoricalRunFormatting: ({ node, paragraphContext, styleResolver }: RestoreHistoricalRunFormattingOptions) => readonly Mark[];
17
24
  //#endregion
18
- export { reconstructRejectedRunFormattingMarks };
25
+ export { reconstructRejectedRunFormattingMarks, restoreHistoricalRunFormatting };