@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,4 +1,5 @@
1
1
  import { expectFootnoteRefMarkAttrs } from "../../attrs/index.js";
2
+ import { mintNoteReferenceOccurrenceId, noteReferenceOccurrencePlugin } from "../../noteReferenceOccurrences.js";
2
3
  import { suggestRangeDeletion, suggestionModeKey } from "../../plugins/suggestionMode.js";
3
4
  import { createMarkExtension } from "../create.js";
4
5
  import { expandNoteReferenceDeletionRange, noteReferenceRanges } from "./noteReferenceDeletion.js";
@@ -9,7 +10,9 @@ import { panic } from "better-result";
9
10
  *
10
11
  * Provides footnoteRef mark + insert/delete commands for footnotes and endnotes.
11
12
  */
13
+ const NOTE_REFERENCE_PARSE_PRIORITY = 60;
12
14
  const noteRefAttrsFromDom = (dom, noteType, vertAlign) => ({
15
+ occurrenceId: dom.dataset["noteOccurrence"] ?? mintNoteReferenceOccurrenceId(),
13
16
  id: dom.dataset["id"] ?? "",
14
17
  noteType: dom.dataset["noteType"] ?? noteType,
15
18
  ...vertAlign ? { vertAlign } : {}
@@ -21,6 +24,7 @@ const FootnoteRefExtension = createMarkExtension({
21
24
  inclusive: false,
22
25
  attrs: {
23
26
  id: {},
27
+ occurrenceId: {},
24
28
  noteType: { default: "footnote" },
25
29
  vertAlign: { default: null },
26
30
  customMarkFollows: { default: null }
@@ -28,18 +32,22 @@ const FootnoteRefExtension = createMarkExtension({
28
32
  parseDOM: [
29
33
  {
30
34
  tag: "sup.docx-footnote-ref",
35
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
31
36
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "footnote", "superscript")
32
37
  },
33
38
  {
34
39
  tag: "sup.docx-endnote-ref",
40
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
35
41
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "endnote", "superscript")
36
42
  },
37
43
  {
38
44
  tag: "span.docx-footnote-ref",
45
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
39
46
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "footnote", "baseline")
40
47
  },
41
48
  {
42
49
  tag: "span.docx-endnote-ref",
50
+ priority: NOTE_REFERENCE_PARSE_PRIORITY,
43
51
  getAttrs: (dom) => noteRefAttrsFromDom(dom, "endnote", "baseline")
44
52
  }
45
53
  ],
@@ -53,6 +61,7 @@ const FootnoteRefExtension = createMarkExtension({
53
61
  {
54
62
  class: `docx-${noteType}-ref ${isSuperscript ? "docx-note-ref-superscript" : "docx-note-ref-baseline"}`,
55
63
  "data-id": id,
64
+ "data-note-occurrence": attrs.occurrenceId,
56
65
  "data-note-type": noteType
57
66
  },
58
67
  0
@@ -76,6 +85,7 @@ const FootnoteRefExtension = createMarkExtension({
76
85
  if (!footnoteRefType) panic("Missing mark type: footnoteRef");
77
86
  const mark = footnoteRefType.create({
78
87
  id: String(id),
88
+ occurrenceId: mintNoteReferenceOccurrenceId(),
79
89
  noteType,
80
90
  vertAlign: "superscript"
81
91
  });
@@ -100,6 +110,7 @@ const FootnoteRefExtension = createMarkExtension({
100
110
  return true;
101
111
  };
102
112
  return {
113
+ plugins: [noteReferenceOccurrencePlugin()],
103
114
  keyboardShortcuts: {
104
115
  Backspace: deleteWholeNoteReference("backward"),
105
116
  Delete: deleteWholeNoteReference("forward")
@@ -1,6 +1,10 @@
1
+ import { BUILT_IN_STYLE_NAME } from "../../../docx/builtInStyles.js";
1
2
  import { anchorTargetAttrs, normalizeUserUrl, sanitizeExternalUrl } from "../../../utils/urlSecurity.js";
2
3
  import { expectHyperlinkMarkAttrs } from "../../attrs/index.js";
3
- import { removeHyperlinkInRange } from "../../hyperlinkRemoval.js";
4
+ import { withCanonicalCommand } from "../../canonicalCommands.js";
5
+ import { canonicalSelectionRange } from "../../canonicalSelectionRange.js";
6
+ import { assertHyperlinkRemovalRange, removeHyperlinkInRange } from "../../hyperlinkRemoval.js";
7
+ import { getDocumentBuiltInStyles } from "../../plugins/documentStyleState.js";
4
8
  import { createMarkExtension } from "../create.js";
5
9
  import { isMarkActive } from "./markUtils.js";
6
10
  import { panic } from "better-result";
@@ -121,7 +125,7 @@ const HyperlinkExtension = createMarkExtension({
121
125
  onSchemaReady(ctx) {
122
126
  if (!ctx.schema.marks["hyperlink"]) panic("Missing mark type: hyperlink");
123
127
  const documentHyperlinkType = (state) => state.schema.marks["hyperlink"] ?? panic("Missing mark type: hyperlink");
124
- const setHyperlink = (href, tooltip) => (state, dispatch) => {
128
+ const setHyperlink = (href, tooltip) => withCanonicalCommand((state, dispatch) => {
125
129
  const hlType = documentHyperlinkType(state);
126
130
  const { from, to, empty } = state.selection;
127
131
  if (empty) return false;
@@ -136,34 +140,60 @@ const HyperlinkExtension = createMarkExtension({
136
140
  dispatch(tr.scrollIntoView());
137
141
  }
138
142
  return true;
143
+ }, (state) => state.selection.empty ? [] : [{
144
+ type: "setHyperlink",
145
+ ...canonicalSelectionRange(state),
146
+ href: normalizeHyperlinkInput(href),
147
+ ...tooltip ? { tooltip } : {}
148
+ }]);
149
+ const removalRange = (state) => {
150
+ const { empty, $from } = state.selection;
151
+ const { from, to } = canonicalSelectionRange(state);
152
+ assertHyperlinkRemovalRange({
153
+ doc: state.doc,
154
+ from,
155
+ to
156
+ });
157
+ if (!empty) return {
158
+ from,
159
+ to
160
+ };
161
+ const linkMark = $from.marks().find((mark) => mark.type === documentHyperlinkType(state));
162
+ if (!linkMark) return void 0;
163
+ let contiguous;
164
+ let selected;
165
+ $from.parent.forEach((node, offset) => {
166
+ if (!node.isInline || !node.marks.some((mark) => mark.eq(linkMark))) {
167
+ contiguous = void 0;
168
+ return;
169
+ }
170
+ const start = $from.start() + offset;
171
+ const end = start + node.nodeSize;
172
+ if (contiguous?.to === start) contiguous.to = end;
173
+ else contiguous = {
174
+ from: start,
175
+ to: end
176
+ };
177
+ if (start <= from && from <= end) selected = contiguous;
178
+ });
179
+ return selected;
139
180
  };
140
- const removeHyperlink = (state, dispatch) => {
141
- const hlType = documentHyperlinkType(state);
142
- const { from, to, empty } = state.selection;
143
- if (empty) {
144
- const $pos = state.selection.$from;
145
- if (!$pos.marks().find((m) => m.type === hlType)) return false;
146
- let start = $pos.pos;
147
- let end = $pos.pos;
148
- $pos.parent.forEach((node, offset) => {
149
- if (node.isText) {
150
- const nodeStart = $pos.start() + offset;
151
- const nodeEnd = nodeStart + node.nodeSize;
152
- if (nodeStart <= $pos.pos && $pos.pos <= nodeEnd) {
153
- if (node.marks.some((m) => m.type === hlType)) {
154
- start = Math.min(start, nodeStart);
155
- end = Math.max(end, nodeEnd);
156
- }
157
- }
158
- }
159
- });
160
- if (dispatch) dispatch(removeHyperlinkInRange(state, state.tr, start, end).scrollIntoView());
161
- return true;
162
- }
163
- if (dispatch) dispatch(removeHyperlinkInRange(state, state.tr, from, to).scrollIntoView());
181
+ const removeHyperlink = withCanonicalCommand((state, dispatch) => {
182
+ const range = removalRange(state);
183
+ if (!range) return false;
184
+ if (dispatch) dispatch(removeHyperlinkInRange(state, state.tr, range.from, range.to).scrollIntoView());
164
185
  return true;
165
- };
166
- const insertHyperlink = (text, href, tooltip) => (state, dispatch) => {
186
+ }, (state) => {
187
+ const range = removalRange(state);
188
+ if (!range) return [];
189
+ const hyperlinkStyleId = getDocumentBuiltInStyles(state).styleIdForBuiltInName(BUILT_IN_STYLE_NAME.hyperlink);
190
+ return [{
191
+ type: "removeHyperlink",
192
+ ...range,
193
+ ...hyperlinkStyleId === void 0 ? {} : { hyperlinkStyleId }
194
+ }];
195
+ });
196
+ const insertHyperlink = (text, href, tooltip) => withCanonicalCommand((state, dispatch) => {
167
197
  const hlType = documentHyperlinkType(state);
168
198
  if (dispatch) {
169
199
  const mark = hlType.create({
@@ -174,7 +204,13 @@ const HyperlinkExtension = createMarkExtension({
174
204
  dispatch(state.tr.replaceSelectionWith(textNode, false).scrollIntoView());
175
205
  }
176
206
  return true;
177
- };
207
+ }, (state) => [{
208
+ type: "insertHyperlink",
209
+ ...canonicalSelectionRange(state),
210
+ text,
211
+ href: normalizeHyperlinkInput(href),
212
+ ...tooltip ? { tooltip } : {}
213
+ }]);
178
214
  return { commands: {
179
215
  setHyperlink,
180
216
  removeHyperlink: () => removeHyperlink,
@@ -1,8 +1,9 @@
1
1
  import { FONT_THEME_VALUES } from "../../../types/documentEnumValues.js";
2
2
  import { mergeFontFamily } from "../../../utils/fontFamilyMerge.js";
3
3
  import { normalizeHorizontalScalePercent } from "../../../utils/horizontalScale.js";
4
- import { expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectEmphasisMarkAttrs, expectFontFamilyMarkAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectStrikeMarkAttrs, expectTextEffectMarkAttrs } from "../../attrs/index.js";
4
+ import { expectCharacterSpacingMarkAttrs, expectCharacterStyleMarkAttrs, expectEmphasisMarkAttrs, expectFontFamilyMarkAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectStrikeMarkAttrs, expectTextEffectMarkAttrs } from "../../attrs/index.js";
5
5
  import { canonicalRunFormatting, withCanonicalCommand } from "../../canonicalCommands.js";
6
+ import { canonicalSelectionRange } from "../../canonicalSelectionRange.js";
6
7
  import { runShadingAttrsToShading, shadingToRunShadingAttrs } from "../../conversion/runShadingMark.js";
7
8
  import { documentMarkType } from "../../documentSchema.js";
8
9
  import { selectRunFormattingCarrierRepresentations } from "../../runFormattingInlineCarriers.js";
@@ -146,14 +147,15 @@ function saveStoredMarksToParagraph(state, tr, marks) {
146
147
  const paragraph = $from.parent;
147
148
  if (paragraph.type.name !== "paragraph") return tr;
148
149
  if (paragraph.textContent.length > 0) return tr;
149
- if (marks.length === 0) return tr.setNodeMarkup($from.before(), void 0, {
150
- ...paragraph.attrs,
151
- defaultTextFormatting: null
152
- });
153
- const defaultTextFormatting = marksToTextFormatting(marks);
150
+ const attrs = expectParagraphAttrs(paragraph);
151
+ const defaultTextFormatting = marks.length === 0 ? null : marksToTextFormatting(marks);
154
152
  return tr.setNodeMarkup($from.before(), void 0, {
155
153
  ...paragraph.attrs,
156
- defaultTextFormatting
154
+ defaultTextFormatting,
155
+ _originalFormatting: {
156
+ ...attrs._originalFormatting,
157
+ runProperties: defaultTextFormatting ?? void 0
158
+ }
157
159
  });
158
160
  }
159
161
  function dispatchStoredMarks(state, dispatch, marks) {
@@ -188,7 +190,8 @@ function createMarkWithMergedAttrs(markType, currentMark, nextAttrs) {
188
190
  }
189
191
  const canonicalSetMarkFormatting = ({ state, type, attrs }) => {
190
192
  const markType = documentMarkType(state, type);
191
- const { from, to, empty } = state.selection;
193
+ const { empty } = state.selection;
194
+ const { from, to } = canonicalSelectionRange(state);
192
195
  if (empty) return canonicalRunFormatting(state, marksToTextFormatting([createMarkWithMergedAttrs(markType, markType.isInSet(state.storedMarks ?? state.selection.$from.marks()), attrs)]));
193
196
  return selectRunFormattingCarrierRepresentations({
194
197
  doc: state.doc,
@@ -1,3 +1,4 @@
1
+ import { expectFootnoteRefMarkAttrs } from "../../attrs/index.js";
1
2
  //#region src/prosemirror/extensions/marks/noteReferenceDeletion.ts
2
3
  /** Keep the text carrying one OOXML note reference together during deletion. */
3
4
  const expandNoteReferenceDeletionRange = (doc, from, to) => {
@@ -7,6 +8,8 @@ const expandNoteReferenceDeletionRange = (doc, from, to) => {
7
8
  doc.nodesBetween(from, to, (node, pos) => {
8
9
  const reference = node.marks.find((mark) => mark.type.name === "footnoteRef");
9
10
  if (!node.isText || !reference) return;
11
+ const { occurrenceId } = expectFootnoteRefMarkAttrs(reference);
12
+ const sameOccurrence = (sibling) => sibling.isText && sibling.marks.some((mark) => mark.type.name === "footnoteRef" && expectFootnoteRefMarkAttrs(mark).occurrenceId === occurrenceId);
10
13
  intersectsReference = true;
11
14
  const $pos = doc.resolve(pos);
12
15
  const parent = $pos.parent;
@@ -15,12 +18,12 @@ const expandNoteReferenceDeletionRange = (doc, from, to) => {
15
18
  let referenceTo = pos + node.nodeSize;
16
19
  for (let left = index - 1; left >= 0; left--) {
17
20
  const sibling = parent.child(left);
18
- if (!sibling.isText || !sibling.marks.some((mark) => mark.eq(reference))) break;
21
+ if (!sameOccurrence(sibling)) break;
19
22
  referenceFrom -= sibling.nodeSize;
20
23
  }
21
24
  for (let right = index + 1; right < parent.childCount; right++) {
22
25
  const sibling = parent.child(right);
23
- if (!sibling.isText || !sibling.marks.some((mark) => mark.eq(reference))) break;
26
+ if (!sameOccurrence(sibling)) break;
24
27
  referenceTo += sibling.nodeSize;
25
28
  }
26
29
  expandedFrom = Math.min(expandedFrom, referenceFrom);
@@ -0,0 +1,8 @@
1
+ import { Node } from "prosemirror-model";
2
+ //#region src/prosemirror/fieldRepresentation.d.ts
3
+ /** Current children alone decide whether a field needs editable structural content. */
4
+ declare const fieldRequiresStructuredContent: (children: readonly Node[]) => boolean;
5
+ /** Resolution returns text-only fields to the same atom representation as initial conversion. */
6
+ declare const canonicalFieldNode: (node: Node) => Node;
7
+ //#endregion
8
+ export { canonicalFieldNode, fieldRequiresStructuredContent };
@@ -0,0 +1,18 @@
1
+ import "./extensions/marks/InlineWrapperExtension.js";
2
+ //#region src/prosemirror/fieldRepresentation.ts
3
+ /** Current children alone decide whether a field needs editable structural content. */
4
+ const fieldRequiresStructuredContent = (children) => children.some((child) => !child.isText || child.marks.some((mark) => mark.type.name === "hyperlink" || mark.type.name === "inlineWrapper" || mark.type.name === "insertion" || mark.type.name === "deletion"));
5
+ /** Resolution returns text-only fields to the same atom representation as initial conversion. */
6
+ const canonicalFieldNode = (node) => {
7
+ if (node.type.name !== "structuredField") return node;
8
+ const attrs = {
9
+ ...node.attrs,
10
+ displayText: node.textContent
11
+ };
12
+ if (fieldRequiresStructuredContent(node.content.content)) return node.attrs["displayText"] === node.textContent ? node : node.type.create(attrs, node.content, node.marks);
13
+ let marks = node.marks;
14
+ for (const mark of node.firstChild?.marks ?? []) marks = mark.addToSet(marks);
15
+ return node.type.schema.node("field", attrs, void 0, marks);
16
+ };
17
+ //#endregion
18
+ export { canonicalFieldNode, fieldRequiresStructuredContent };
@@ -1,9 +1,21 @@
1
+ import { Node } from "prosemirror-model";
1
2
  import { EditorState, Transaction } from "prosemirror-state";
2
3
  //#region src/prosemirror/hyperlinkRemoval.d.ts
4
+ declare const HyperlinkRemovalRefusal_base: import("better-result").TaggedErrorClass<"HyperlinkRemovalRefusal">;
5
+ declare class HyperlinkRemovalRefusal extends HyperlinkRemovalRefusal_base<{
6
+ message: string;
7
+ }> {}
8
+ type HyperlinkRemovalRangeOptions = {
9
+ doc: Node;
10
+ from: number;
11
+ to: number;
12
+ };
13
+ /** Canonical inline elements have no addressable interior gaps. */
14
+ declare const assertHyperlinkRemovalRange: ({ doc, from, to }: HyperlinkRemovalRangeOptions) => void;
3
15
  /**
4
16
  * Remove the hyperlinks from [`from`, `to`) of `tr.doc`: directly, or, with
5
17
  * suggestions on, as a tracked replacement of the linked text by plain text.
6
18
  */
7
19
  declare const removeHyperlinkInRange: (state: EditorState, tr: Transaction, from: number, to: number) => Transaction;
8
20
  //#endregion
9
- export { removeHyperlinkInRange };
21
+ export { HyperlinkRemovalRefusal, assertHyperlinkRemovalRange, removeHyperlinkInRange };
@@ -3,19 +3,33 @@ import { getDocumentBuiltInStyles, getDocumentStyleResolver } from "./plugins/do
3
3
  import { isSuggestionModeActive, makeRevisionInfo, suggestRangeDeletion } from "./plugins/suggestionMode.js";
4
4
  import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "./runFormattingReconciliation.js";
5
5
  import { paragraphRunStyleContextAt } from "./runStyleFormatting.js";
6
+ import { TaggedError } from "better-result";
6
7
  //#region src/prosemirror/hyperlinkRemoval.ts
7
- /** Contiguous stretches of linked, not yet deleted, text in [`from`, `to`) of `doc`. */
8
+ var HyperlinkRemovalRefusal = class extends TaggedError("HyperlinkRemovalRefusal") {};
9
+ const partialInlineRefusal = () => new HyperlinkRemovalRefusal({ message: "Hyperlink removal cannot split an indivisible inline element." });
10
+ /** Canonical inline elements have no addressable interior gaps. */
11
+ const assertHyperlinkRemovalRange = ({ doc, from, to }) => {
12
+ for (const position of [from, to]) {
13
+ const resolved = doc.resolve(position);
14
+ for (let depth = 1; depth <= resolved.depth; depth += 1) {
15
+ const node = resolved.node(depth);
16
+ if (node.isInline && node.marks.some(({ type }) => type.name === "hyperlink")) throw partialInlineRefusal();
17
+ }
18
+ }
19
+ };
20
+ /** Contiguous stretches of linked, not yet deleted, inline content in [`from`, `to`) of `doc`. */
8
21
  const linkedRanges = (doc, from, to) => {
9
22
  const ranges = [];
10
23
  doc.nodesBetween(from, to, (node, position) => {
11
- if (!node.isText || !node.marks.some(({ type }) => type.name === "hyperlink") || node.marks.some(({ type }) => type.name === "deletion")) return true;
24
+ if (!node.isInline || !node.marks.some(({ type }) => type.name === "hyperlink") || node.marks.some(({ type }) => type.name === "deletion")) return true;
12
25
  const start = Math.max(position, from);
13
26
  const end = Math.min(position + node.nodeSize, to);
14
27
  if (start >= end) return true;
28
+ if (!node.isText && (start !== position || end !== position + node.nodeSize)) throw partialInlineRefusal();
15
29
  const piece = {
16
30
  from: start,
17
31
  to: end,
18
- node: node.cut(start - position, end - position)
32
+ node: node.isText ? node.cut(start - position, end - position) : node
19
33
  };
20
34
  const last = ranges.at(-1);
21
35
  if (last && last.to === start) {
@@ -26,7 +40,7 @@ const linkedRanges = (doc, from, to) => {
26
40
  to: end,
27
41
  pieces: [piece]
28
42
  });
29
- return true;
43
+ return false;
30
44
  });
31
45
  return ranges;
32
46
  };
@@ -60,8 +74,8 @@ const withoutHyperlinkStyle = (state, doc, piece) => {
60
74
  previousFormatting
61
75
  };
62
76
  };
63
- /** Replace the marks of the text at [`from`, `to`) of `tr.doc` with `marks`. */
64
- const setTextMarks = (tr, from, to, current, marks) => {
77
+ /** Replace the marks of the inline content at [`from`, `to`) of `tr.doc` with `marks`. */
78
+ const setInlineMarks = (tr, from, to, current, marks) => {
65
79
  for (const mark of current) if (!mark.isInSet(marks)) tr.removeMark(from, to, mark);
66
80
  for (const mark of marks) if (!mark.isInSet(current)) tr.addMark(from, to, mark);
67
81
  };
@@ -70,6 +84,11 @@ const setTextMarks = (tr, from, to, current, marks) => {
70
84
  * suggestions on, as a tracked replacement of the linked text by plain text.
71
85
  */
72
86
  const removeHyperlinkInRange = (state, tr, from, to) => {
87
+ assertHyperlinkRemovalRange({
88
+ doc: tr.doc,
89
+ from,
90
+ to
91
+ });
73
92
  const { schema } = state;
74
93
  const hyperlinkType = schema.marks["hyperlink"];
75
94
  if (!hyperlinkType) return tr;
@@ -108,7 +127,7 @@ const removeHyperlinkInRange = (state, tr, from, to) => {
108
127
  ...withoutHyperlinkStyle(state, tr.doc, piece)
109
128
  }));
110
129
  if (!insertion || !deletionType) {
111
- for (const { piece, marks } of unlinked) setTextMarks(tr, piece.from, piece.to, piece.node.marks, marks.filter(({ type }) => type !== hyperlinkType));
130
+ for (const { piece, marks } of unlinked) setInlineMarks(tr, piece.from, piece.to, piece.node.marks, marks.filter(({ type }) => type !== hyperlinkType));
112
131
  continue;
113
132
  }
114
133
  const plain = unlinked.map(({ piece, marks, previousFormatting }) => {
@@ -119,11 +138,11 @@ const removeHyperlinkInRange = (state, tr, from, to) => {
119
138
  tr.insert(range.to, plain);
120
139
  for (const { piece, marks, previousFormatting } of unlinked.toReversed()) {
121
140
  const change = previousFormatting && !hasPendingPropertyChange(piece.node.marks) ? propertyChange(previousFormatting) : null;
122
- setTextMarks(tr, piece.from, piece.to, piece.node.marks, change ? change.addToSet(marks) : marks);
141
+ setInlineMarks(tr, piece.from, piece.to, piece.node.marks, change ? change.addToSet(marks) : marks);
123
142
  }
124
143
  suggestRangeDeletion(state, tr, range.from, range.to);
125
144
  }
126
145
  return tr;
127
146
  };
128
147
  //#endregion
129
- export { removeHyperlinkInRange };
148
+ export { HyperlinkRemovalRefusal, assertHyperlinkRemovalRange, removeHyperlinkInRange };
@@ -2,6 +2,7 @@ import { insertImageFromFile } from "./commands/image.js";
2
2
  import { insertPageBreak } from "./commands/pageBreak.js";
3
3
  import { generateTableOfContents } from "./commands/paragraph.js";
4
4
  import { insertTable } from "./commands/table.js";
5
+ import { executeEditorCommand } from "./executeEditorCommand.js";
5
6
  //#region src/prosemirror/insertOperations.ts
6
7
  /** Insert a `rows × columns` table at the current selection. */
7
8
  function insertTableInView(view, rows, columns) {
@@ -18,7 +19,7 @@ function insertPageBreakInView(view) {
18
19
  * {@link GenerateTableOfContentsOptions}.
19
20
  */
20
21
  function insertTableOfContentsInView(view, options) {
21
- return generateTableOfContents(options)(view.state, view.dispatch);
22
+ return executeEditorCommand(view, generateTableOfContents(options));
22
23
  }
23
24
  //#endregion
24
25
  export { insertImageFromFile, insertPageBreakInView, insertTableInView, insertTableOfContentsInView };
@@ -10,7 +10,7 @@ import { directParagraphIndentation } from "./paragraphIndentation.js";
10
10
  import { getDocumentNumbering } from "./plugins/documentNumbering.js";
11
11
  import { getDocumentStyleResolver } from "./plugins/documentStyleState.js";
12
12
  import { SUGGESTION_META, makeRevisionInfo } from "./plugins/suggestionMode.js";
13
- import { listAttrsFromNumbering, listLevelIndentAttrPatch } from "./styles/resolvedStyleAttrs.js";
13
+ import { listAttrsFromNumbering, listIndentationProvenancePatch } from "./styles/resolvedStyleAttrs.js";
14
14
  //#region src/prosemirror/listNumbering.ts
15
15
  /**
16
16
  * Which numbering instance a list command puts a paragraph in, and the
@@ -199,10 +199,16 @@ const listItemAttrs = (attrs, { numId, ilvl }, numbering) => ({
199
199
  numId,
200
200
  ilvl
201
201
  }, numbering),
202
- ...listLevelIndentAttrPatch(directParagraphIndentation(attrs), {
203
- numId,
204
- ilvl
205
- }, numbering),
202
+ ...listIndentationProvenancePatch({
203
+ direct: directParagraphIndentation(attrs),
204
+ styleFormatting: attrs._styleResolvedFormatting,
205
+ numberingSource: "paragraph",
206
+ numPr: {
207
+ numId,
208
+ ilvl
209
+ },
210
+ numbering
211
+ }),
206
212
  listImplicitChildLevelAdvances: attrs["listImplicitChildLevelAdvances"] ?? null,
207
213
  listFoldedMarkerSuffix: attrs["listFoldedMarkerSuffix"] ?? void 0,
208
214
  listMarkerSecondSlotOffsetTwips: attrs["listMarkerSecondSlotOffsetTwips"] ?? null
@@ -0,0 +1,5 @@
1
+ //#region src/prosemirror/noteReferenceOccurrenceId.d.ts
2
+ /** Occurrence identities are opaque, nonblank attribution tokens. */
3
+ declare const isNoteReferenceOccurrenceId: (value: unknown) => value is string;
4
+ //#endregion
5
+ export { isNoteReferenceOccurrenceId };
@@ -0,0 +1,5 @@
1
+ //#region src/prosemirror/noteReferenceOccurrenceId.ts
2
+ /** Occurrence identities are opaque, nonblank attribution tokens. */
3
+ const isNoteReferenceOccurrenceId = (value) => typeof value === "string" && value.trim().length > 0;
4
+ //#endregion
5
+ export { isNoteReferenceOccurrenceId };
@@ -0,0 +1,21 @@
1
+ import { Node } from "prosemirror-model";
2
+ import { Plugin, Transaction } from "prosemirror-state";
3
+ //#region src/prosemirror/noteReferenceOccurrences.d.ts
4
+ declare const NoteReferenceEditRefusal_base: import("better-result").TaggedErrorClass<"NoteReferenceEditRefusal">;
5
+ declare class NoteReferenceEditRefusal extends NoteReferenceEditRefusal_base<{
6
+ message: string;
7
+ }> {}
8
+ declare const NoteReferenceReplayDefect_base: import("better-result").TaggedErrorClass<"NoteReferenceReplayDefect">;
9
+ declare class NoteReferenceReplayDefect extends NoteReferenceReplayDefect_base<{
10
+ message: string;
11
+ }> {}
12
+ declare const markNoteReferenceReplay: (transaction: Transaction) => void;
13
+ declare const noteReferenceTransactionIssue: (transaction: Transaction) => NoteReferenceEditRefusal | NoteReferenceReplayDefect | undefined;
14
+ declare const mintNoteReferenceOccurrenceId: () => string;
15
+ /** Saving asserts the edit-owner invariant and emits each occurrence once. */
16
+ declare const coalesceNoteReferenceOccurrences: (paragraph: Node) => Node;
17
+ declare const assertNoteReferenceOccurrences: (doc: Node) => void;
18
+ /** Production edit boundary: refuse before committing an unsaveable occurrence. */
19
+ declare const noteReferenceOccurrencePlugin: () => Plugin<any>;
20
+ //#endregion
21
+ export { NoteReferenceEditRefusal, NoteReferenceReplayDefect, assertNoteReferenceOccurrences, coalesceNoteReferenceOccurrences, markNoteReferenceReplay, mintNoteReferenceOccurrenceId, noteReferenceOccurrencePlugin, noteReferenceTransactionIssue };