@kerebron/extension-basic-editor 0.4.27 → 0.4.29

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 (242) hide show
  1. package/esm/ExtensionBaseKeymap.d.ts +7 -0
  2. package/esm/ExtensionBaseKeymap.d.ts.map +1 -0
  3. package/esm/ExtensionBaseKeymap.js +56 -0
  4. package/esm/ExtensionBaseKeymap.js.map +1 -0
  5. package/esm/ExtensionBasicCodeEditor.d.ts +11 -0
  6. package/esm/ExtensionBasicCodeEditor.d.ts.map +1 -0
  7. package/esm/ExtensionBasicCodeEditor.js +62 -0
  8. package/esm/ExtensionBasicCodeEditor.js.map +1 -0
  9. package/esm/ExtensionBasicEditor.d.ts +49 -0
  10. package/esm/ExtensionBasicEditor.d.ts.map +1 -0
  11. package/esm/ExtensionBasicEditor.js +95 -0
  12. package/esm/ExtensionBasicEditor.js.map +1 -0
  13. package/esm/ExtensionDropcursor.d.ts +19 -0
  14. package/esm/ExtensionDropcursor.d.ts.map +1 -0
  15. package/esm/ExtensionDropcursor.js +187 -0
  16. package/esm/ExtensionDropcursor.js.map +1 -0
  17. package/esm/ExtensionGapcursor.d.ts +32 -0
  18. package/esm/ExtensionGapcursor.d.ts.map +1 -0
  19. package/esm/ExtensionGapcursor.js +250 -0
  20. package/esm/ExtensionGapcursor.js.map +1 -0
  21. package/esm/ExtensionHistory.d.ts +14 -0
  22. package/esm/ExtensionHistory.d.ts.map +1 -0
  23. package/esm/ExtensionHistory.js +35 -0
  24. package/esm/ExtensionHistory.js.map +1 -0
  25. package/esm/ExtensionHtml.d.ts +15 -0
  26. package/esm/ExtensionHtml.d.ts.map +1 -0
  27. package/esm/ExtensionHtml.js +100 -0
  28. package/esm/ExtensionHtml.js.map +1 -0
  29. package/esm/ExtensionMediaUpload.d.ts +24 -0
  30. package/esm/ExtensionMediaUpload.d.ts.map +1 -0
  31. package/esm/ExtensionMediaUpload.js +168 -0
  32. package/esm/ExtensionMediaUpload.js.map +1 -0
  33. package/esm/ExtensionSelection.d.ts +11 -0
  34. package/esm/ExtensionSelection.d.ts.map +1 -0
  35. package/esm/ExtensionSelection.js +230 -0
  36. package/esm/ExtensionSelection.js.map +1 -0
  37. package/esm/ExtensionTextAlign.d.ts +11 -0
  38. package/esm/ExtensionTextAlign.d.ts.map +1 -0
  39. package/esm/ExtensionTextAlign.js +40 -0
  40. package/esm/ExtensionTextAlign.js.map +1 -0
  41. package/esm/MarkBookmark.d.ts +8 -0
  42. package/esm/MarkBookmark.d.ts.map +1 -0
  43. package/esm/MarkBookmark.js +17 -0
  44. package/esm/MarkBookmark.js.map +1 -0
  45. package/esm/MarkChange.d.ts +8 -0
  46. package/esm/MarkChange.d.ts.map +1 -0
  47. package/esm/MarkChange.js +14 -0
  48. package/esm/MarkChange.js.map +1 -0
  49. package/esm/MarkCode.d.ts +11 -0
  50. package/esm/MarkCode.d.ts.map +1 -0
  51. package/esm/MarkCode.js +24 -0
  52. package/esm/MarkCode.js.map +1 -0
  53. package/esm/MarkHighlight.d.ts +8 -0
  54. package/esm/MarkHighlight.d.ts.map +1 -0
  55. package/esm/MarkHighlight.js +36 -0
  56. package/esm/MarkHighlight.js.map +1 -0
  57. package/esm/MarkItalic.d.ts +11 -0
  58. package/esm/MarkItalic.d.ts.map +1 -0
  59. package/esm/MarkItalic.js +30 -0
  60. package/esm/MarkItalic.js.map +1 -0
  61. package/esm/MarkLink.d.ts +8 -0
  62. package/esm/MarkLink.d.ts.map +1 -0
  63. package/esm/MarkLink.js +30 -0
  64. package/esm/MarkLink.js.map +1 -0
  65. package/esm/MarkStrike.d.ts +11 -0
  66. package/esm/MarkStrike.d.ts.map +1 -0
  67. package/esm/MarkStrike.js +27 -0
  68. package/esm/MarkStrike.js.map +1 -0
  69. package/esm/MarkStrong.d.ts +11 -0
  70. package/esm/MarkStrong.d.ts.map +1 -0
  71. package/esm/MarkStrong.js +39 -0
  72. package/esm/MarkStrong.js.map +1 -0
  73. package/esm/MarkSubscript.d.ts +11 -0
  74. package/esm/MarkSubscript.d.ts.map +1 -0
  75. package/esm/MarkSubscript.js +31 -0
  76. package/esm/MarkSubscript.js.map +1 -0
  77. package/esm/MarkSuperscript.d.ts +11 -0
  78. package/esm/MarkSuperscript.d.ts.map +1 -0
  79. package/esm/MarkSuperscript.js +31 -0
  80. package/esm/MarkSuperscript.js.map +1 -0
  81. package/esm/MarkTextColor.d.ts +8 -0
  82. package/esm/MarkTextColor.d.ts.map +1 -0
  83. package/esm/MarkTextColor.js +28 -0
  84. package/esm/MarkTextColor.js.map +1 -0
  85. package/esm/MarkUnderline.d.ts +11 -0
  86. package/esm/MarkUnderline.d.ts.map +1 -0
  87. package/esm/MarkUnderline.js +34 -0
  88. package/esm/MarkUnderline.js.map +1 -0
  89. package/esm/NodeAside.d.ts +8 -0
  90. package/esm/NodeAside.d.ts.map +1 -0
  91. package/esm/NodeAside.js +17 -0
  92. package/esm/NodeAside.js.map +1 -0
  93. package/esm/NodeBlockquote.d.ts +13 -0
  94. package/esm/NodeBlockquote.d.ts.map +1 -0
  95. package/esm/NodeBlockquote.js +35 -0
  96. package/esm/NodeBlockquote.js.map +1 -0
  97. package/esm/NodeBookmark.d.ts +8 -0
  98. package/esm/NodeBookmark.d.ts.map +1 -0
  99. package/esm/NodeBookmark.js +20 -0
  100. package/esm/NodeBookmark.js.map +1 -0
  101. package/esm/NodeBulletList.d.ts +13 -0
  102. package/esm/NodeBulletList.d.ts.map +1 -0
  103. package/esm/NodeBulletList.js +35 -0
  104. package/esm/NodeBulletList.js.map +1 -0
  105. package/esm/NodeCodeBlock.d.ts +9 -0
  106. package/esm/NodeCodeBlock.d.ts.map +1 -0
  107. package/esm/NodeCodeBlock.js +51 -0
  108. package/esm/NodeCodeBlock.js.map +1 -0
  109. package/esm/NodeDefinitionDesc.d.ts +8 -0
  110. package/esm/NodeDefinitionDesc.d.ts.map +1 -0
  111. package/esm/NodeDefinitionDesc.js +17 -0
  112. package/esm/NodeDefinitionDesc.js.map +1 -0
  113. package/esm/NodeDefinitionList.d.ts +13 -0
  114. package/esm/NodeDefinitionList.d.ts.map +1 -0
  115. package/esm/NodeDefinitionList.js +29 -0
  116. package/esm/NodeDefinitionList.js.map +1 -0
  117. package/esm/NodeDefinitionTerm.d.ts +8 -0
  118. package/esm/NodeDefinitionTerm.d.ts.map +1 -0
  119. package/esm/NodeDefinitionTerm.js +17 -0
  120. package/esm/NodeDefinitionTerm.js.map +1 -0
  121. package/esm/NodeDocument.d.ts +7 -0
  122. package/esm/NodeDocument.d.ts.map +1 -0
  123. package/esm/NodeDocument.js +20 -0
  124. package/esm/NodeDocument.js.map +1 -0
  125. package/esm/NodeDocumentCode.d.ts +7 -0
  126. package/esm/NodeDocumentCode.d.ts.map +1 -0
  127. package/esm/NodeDocumentCode.js +30 -0
  128. package/esm/NodeDocumentCode.js.map +1 -0
  129. package/esm/NodeFrontmatter.d.ts +8 -0
  130. package/esm/NodeFrontmatter.d.ts.map +1 -0
  131. package/esm/NodeFrontmatter.js +17 -0
  132. package/esm/NodeFrontmatter.js.map +1 -0
  133. package/esm/NodeHardBreak.d.ts +14 -0
  134. package/esm/NodeHardBreak.d.ts.map +1 -0
  135. package/esm/NodeHardBreak.js +68 -0
  136. package/esm/NodeHardBreak.js.map +1 -0
  137. package/esm/NodeHeading.d.ts +13 -0
  138. package/esm/NodeHeading.d.ts.map +1 -0
  139. package/esm/NodeHeading.js +50 -0
  140. package/esm/NodeHeading.js.map +1 -0
  141. package/esm/NodeHorizontalRule.d.ts +11 -0
  142. package/esm/NodeHorizontalRule.d.ts.map +1 -0
  143. package/esm/NodeHorizontalRule.js +30 -0
  144. package/esm/NodeHorizontalRule.js.map +1 -0
  145. package/esm/NodeImage.d.ts +8 -0
  146. package/esm/NodeImage.d.ts.map +1 -0
  147. package/esm/NodeImage.js +34 -0
  148. package/esm/NodeImage.js.map +1 -0
  149. package/esm/NodeInlineShortCode.d.ts +11 -0
  150. package/esm/NodeInlineShortCode.d.ts.map +1 -0
  151. package/esm/NodeInlineShortCode.js +40 -0
  152. package/esm/NodeInlineShortCode.js.map +1 -0
  153. package/esm/NodeListItem.d.ts +14 -0
  154. package/esm/NodeListItem.d.ts.map +1 -0
  155. package/esm/NodeListItem.js +202 -0
  156. package/esm/NodeListItem.js.map +1 -0
  157. package/esm/NodeMath.d.ts +8 -0
  158. package/esm/NodeMath.d.ts.map +1 -0
  159. package/esm/NodeMath.js +100 -0
  160. package/esm/NodeMath.js.map +1 -0
  161. package/esm/NodeOrderedList.d.ts +23 -0
  162. package/esm/NodeOrderedList.d.ts.map +1 -0
  163. package/esm/NodeOrderedList.js +53 -0
  164. package/esm/NodeOrderedList.js.map +1 -0
  165. package/esm/NodeParagraph.d.ts +11 -0
  166. package/esm/NodeParagraph.d.ts.map +1 -0
  167. package/esm/NodeParagraph.js +45 -0
  168. package/esm/NodeParagraph.js.map +1 -0
  169. package/esm/NodeTaskItem.d.ts +24 -0
  170. package/esm/NodeTaskItem.d.ts.map +1 -0
  171. package/esm/NodeTaskItem.js +147 -0
  172. package/esm/NodeTaskItem.js.map +1 -0
  173. package/esm/NodeTaskList.d.ts +11 -0
  174. package/esm/NodeTaskList.d.ts.map +1 -0
  175. package/esm/NodeTaskList.js +26 -0
  176. package/esm/NodeTaskList.js.map +1 -0
  177. package/esm/NodeText.d.ts +7 -0
  178. package/esm/NodeText.d.ts.map +1 -0
  179. package/esm/NodeText.js +10 -0
  180. package/esm/NodeText.js.map +1 -0
  181. package/esm/NodeVideo.d.ts +8 -0
  182. package/esm/NodeVideo.d.ts.map +1 -0
  183. package/esm/NodeVideo.js +46 -0
  184. package/esm/NodeVideo.js.map +1 -0
  185. package/esm/package.json +3 -0
  186. package/esm/remote-selection/ExtensionRemoteSelection.d.ts +24 -0
  187. package/esm/remote-selection/ExtensionRemoteSelection.d.ts.map +1 -0
  188. package/esm/remote-selection/ExtensionRemoteSelection.js +23 -0
  189. package/esm/remote-selection/ExtensionRemoteSelection.js.map +1 -0
  190. package/esm/remote-selection/remoteSelectionPlugin.d.ts +25 -0
  191. package/esm/remote-selection/remoteSelectionPlugin.d.ts.map +1 -0
  192. package/esm/remote-selection/remoteSelectionPlugin.js +97 -0
  193. package/esm/remote-selection/remoteSelectionPlugin.js.map +1 -0
  194. package/package.json +6 -3
  195. package/src/ExtensionBaseKeymap.ts +64 -0
  196. package/src/ExtensionBasicCodeEditor.ts +82 -0
  197. package/src/ExtensionBasicEditor.ts +97 -0
  198. package/src/ExtensionDropcursor.ts +221 -0
  199. package/src/ExtensionGapcursor.ts +278 -0
  200. package/src/ExtensionHistory.ts +48 -0
  201. package/src/ExtensionHtml.ts +158 -0
  202. package/src/ExtensionMediaUpload.ts +258 -0
  203. package/src/ExtensionSelection.ts +379 -0
  204. package/src/ExtensionTextAlign.ts +50 -0
  205. package/src/MarkBookmark.ts +20 -0
  206. package/src/MarkChange.ts +17 -0
  207. package/src/MarkCode.ts +35 -0
  208. package/src/MarkHighlight.ts +38 -0
  209. package/src/MarkItalic.ts +41 -0
  210. package/src/MarkLink.ts +32 -0
  211. package/src/MarkStrike.ts +38 -0
  212. package/src/MarkStrong.ts +52 -0
  213. package/src/MarkSubscript.ts +42 -0
  214. package/src/MarkSuperscript.ts +42 -0
  215. package/src/MarkTextColor.ts +29 -0
  216. package/src/MarkUnderline.ts +47 -0
  217. package/src/NodeAside.ts +19 -0
  218. package/src/NodeBlockquote.ts +51 -0
  219. package/src/NodeBookmark.ts +23 -0
  220. package/src/NodeBulletList.ts +51 -0
  221. package/src/NodeCodeBlock.ts +60 -0
  222. package/src/NodeDefinitionDesc.ts +19 -0
  223. package/src/NodeDefinitionList.ts +46 -0
  224. package/src/NodeDefinitionTerm.ts +19 -0
  225. package/src/NodeDocument.ts +22 -0
  226. package/src/NodeDocumentCode.ts +33 -0
  227. package/src/NodeFrontmatter.ts +19 -0
  228. package/src/NodeHardBreak.ts +92 -0
  229. package/src/NodeHeading.ts +76 -0
  230. package/src/NodeHorizontalRule.ts +43 -0
  231. package/src/NodeImage.ts +36 -0
  232. package/src/NodeInlineShortCode.ts +55 -0
  233. package/src/NodeListItem.ts +320 -0
  234. package/src/NodeMath.ts +109 -0
  235. package/src/NodeOrderedList.ts +79 -0
  236. package/src/NodeParagraph.ts +60 -0
  237. package/src/NodeTaskItem.ts +190 -0
  238. package/src/NodeTaskList.ts +38 -0
  239. package/src/NodeText.ts +12 -0
  240. package/src/NodeVideo.ts +44 -0
  241. package/src/remote-selection/ExtensionRemoteSelection.ts +45 -0
  242. package/src/remote-selection/remoteSelectionPlugin.ts +157 -0
@@ -0,0 +1,379 @@
1
+ import {
2
+ Fragment,
3
+ Node,
4
+ NodeType,
5
+ ResolvedPos,
6
+ Slice,
7
+ } from 'prosemirror-model';
8
+ import {
9
+ AllSelection,
10
+ EditorState,
11
+ TextSelection,
12
+ Transaction,
13
+ } from 'prosemirror-state';
14
+
15
+ import { type CoreEditor, Extension } from '@kerebron/editor';
16
+ import type {
17
+ CommandFactories,
18
+ CommandFactory,
19
+ } from '@kerebron/editor/commands';
20
+ import { createNodeFromObject } from '@kerebron/editor/utilities';
21
+ import { EditorView } from 'prosemirror-view';
22
+
23
+ function normalizeSiblings(fragment: Fragment, $context: ResolvedPos) {
24
+ if (fragment.childCount < 2) return fragment;
25
+ for (let d = $context.depth; d >= 0; d--) {
26
+ let parent = $context.node(d);
27
+ let match = parent.contentMatchAt($context.index(d));
28
+ let lastWrap: readonly NodeType[] | undefined;
29
+ let result: Node[] | null = [];
30
+ fragment.forEach((node) => {
31
+ if (!result) return;
32
+ let wrap = match.findWrapping(node.type);
33
+ let inLast;
34
+ if (!wrap) return result = null;
35
+ if (
36
+ inLast = result.length && lastWrap!.length &&
37
+ addToSibling(wrap, lastWrap!, node, result[result.length - 1], 0)
38
+ ) {
39
+ result[result.length - 1] = inLast;
40
+ } else {
41
+ if (result.length) {
42
+ result[result.length - 1] = closeRight(
43
+ result[result.length - 1],
44
+ lastWrap!.length,
45
+ );
46
+ }
47
+ let wrapped = withWrappers(node, wrap);
48
+ result.push(wrapped);
49
+ match = match.matchType(wrapped.type)!;
50
+ lastWrap = wrap;
51
+ }
52
+ });
53
+ if (result) return Fragment.from(result);
54
+ }
55
+ return fragment;
56
+ }
57
+
58
+ function withWrappers(node: Node, wrap: readonly NodeType[], from = 0) {
59
+ for (let i = wrap.length - 1; i >= from; i--) {
60
+ node = wrap[i].create(null, Fragment.from(node));
61
+ }
62
+ return node;
63
+ }
64
+
65
+ function addToSibling(
66
+ wrap: readonly NodeType[],
67
+ lastWrap: readonly NodeType[],
68
+ node: Node,
69
+ sibling: Node,
70
+ depth: number,
71
+ ): Node | undefined {
72
+ if (
73
+ depth < wrap.length && depth < lastWrap.length &&
74
+ wrap[depth] == lastWrap[depth]
75
+ ) {
76
+ let inner = addToSibling(
77
+ wrap,
78
+ lastWrap,
79
+ node,
80
+ sibling.lastChild!,
81
+ depth + 1,
82
+ );
83
+ if (inner) {
84
+ return sibling.copy(
85
+ sibling.content.replaceChild(sibling.childCount - 1, inner),
86
+ );
87
+ }
88
+ let match = sibling.contentMatchAt(sibling.childCount);
89
+ if (
90
+ match.matchType(depth == wrap.length - 1 ? node.type : wrap[depth + 1])
91
+ ) {
92
+ return sibling.copy(
93
+ sibling.content.append(
94
+ Fragment.from(withWrappers(node, wrap, depth + 1)),
95
+ ),
96
+ );
97
+ }
98
+ }
99
+ }
100
+
101
+ function closeRight(node: Node, depth: number) {
102
+ if (depth == 0) return node;
103
+ let fragment = node.content.replaceChild(
104
+ node.childCount - 1,
105
+ closeRight(node.lastChild!, depth - 1),
106
+ );
107
+ let fill = node.contentMatchAt(node.childCount).fillBefore(
108
+ Fragment.empty,
109
+ true,
110
+ )!;
111
+ return node.copy(fragment.append(fill));
112
+ }
113
+
114
+ function closeRange(
115
+ fragment: Fragment,
116
+ side: number,
117
+ from: number,
118
+ to: number,
119
+ depth: number,
120
+ openEnd: number,
121
+ ) {
122
+ let node = side < 0 ? fragment.firstChild! : fragment.lastChild!,
123
+ inner = node.content;
124
+ if (fragment.childCount > 1) openEnd = 0;
125
+ if (depth < to - 1) {
126
+ inner = closeRange(inner, side, from, to, depth + 1, openEnd);
127
+ }
128
+ if (depth >= from) {
129
+ inner = side < 0
130
+ ? node.contentMatchAt(0)!.fillBefore(inner, openEnd <= depth)!.append(
131
+ inner,
132
+ )
133
+ : inner.append(
134
+ node.contentMatchAt(node.childCount)!.fillBefore(Fragment.empty, true)!,
135
+ );
136
+ }
137
+ return fragment.replaceChild(
138
+ side < 0 ? 0 : fragment.childCount - 1,
139
+ node.copy(inner),
140
+ );
141
+ }
142
+
143
+ function closeSlice(slice: Slice, openStart: number, openEnd: number) {
144
+ if (openStart < slice.openStart) {
145
+ slice = new Slice(
146
+ closeRange(
147
+ slice.content,
148
+ -1,
149
+ openStart,
150
+ slice.openStart,
151
+ 0,
152
+ slice.openEnd,
153
+ ),
154
+ openStart,
155
+ slice.openEnd,
156
+ );
157
+ }
158
+ if (openEnd < slice.openEnd) {
159
+ slice = new Slice(
160
+ closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0),
161
+ slice.openStart,
162
+ openEnd,
163
+ );
164
+ }
165
+ return slice;
166
+ }
167
+
168
+ function sliceSingleNode(slice: Slice) {
169
+ return slice.openStart == 0 && slice.openEnd == 0 &&
170
+ slice.content.childCount == 1
171
+ ? slice.content.firstChild
172
+ : null;
173
+ }
174
+
175
+ function fixSlice(slice: Slice, $context: ResolvedPos): Slice {
176
+ slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
177
+ if (slice.openStart || slice.openEnd) {
178
+ let openStart = 0, openEnd = 0;
179
+ for (
180
+ let node = slice.content.firstChild;
181
+ openStart < slice.openStart && !node!.type.spec.isolating;
182
+ openStart++, node = node!.firstChild
183
+ // deno-lint-ignore no-empty
184
+ ) {}
185
+ for (
186
+ let node = slice.content.lastChild;
187
+ openEnd < slice.openEnd && !node!.type.spec.isolating;
188
+ openEnd++, node = node!.lastChild
189
+ // deno-lint-ignore no-empty
190
+ ) {}
191
+ slice = closeSlice(slice, openStart, openEnd);
192
+ }
193
+ return slice;
194
+ }
195
+
196
+ function sliceHasOnlyText(slice: Slice) {
197
+ return slice.content.content.every((node) => node.isInline);
198
+ }
199
+
200
+ const selectAll: CommandFactory = () => {
201
+ return function (
202
+ state: EditorState,
203
+ dispatch?: (tr: Transaction) => void,
204
+ view?: EditorView,
205
+ ) {
206
+ const tr = state.tr.setSelection(new AllSelection(state.doc));
207
+ if (view) {
208
+ view.dispatch(tr);
209
+ }
210
+
211
+ return true;
212
+ };
213
+ };
214
+
215
+ function textPositionsToResolvedPos(
216
+ textPosVec: number[],
217
+ doc: Node,
218
+ paraNum: number,
219
+ ): ResolvedPos[] {
220
+ const retVal = textPosVec.map((x) => -1);
221
+
222
+ let currentTextPos = 0;
223
+ let inParaRange = false;
224
+
225
+ function callback(
226
+ currentPos: number,
227
+ level: number,
228
+ idx: number,
229
+ textLen: number,
230
+ ) {
231
+ if (!inParaRange) {
232
+ return;
233
+ }
234
+
235
+ for (let i = 0; i < textPosVec.length; i++) {
236
+ const val = textPosVec[i];
237
+ if (val >= currentTextPos && val < currentTextPos + textLen) {
238
+ retVal[i] = currentPos + (val - currentTextPos);
239
+ }
240
+ }
241
+
242
+ currentTextPos += textLen;
243
+ }
244
+
245
+ function treeTraverse(
246
+ node: Node,
247
+ level = 0,
248
+ idx = 0,
249
+ currentPos = 0,
250
+ ) {
251
+ if (level === 1 && idx === paraNum) {
252
+ inParaRange = true;
253
+ }
254
+
255
+ let textLen = 0;
256
+ if (node.isText && node.text) {
257
+ textLen = node.text?.length;
258
+ } else if (node.isLeaf) {
259
+ textLen = 1;
260
+ }
261
+
262
+ if (textLen > 0) {
263
+ callback(currentPos, level, idx, textLen);
264
+ }
265
+
266
+ node.forEach((child, offset, childIndex) => {
267
+ treeTraverse(child, level + 1, childIndex, currentPos + offset + 1);
268
+ });
269
+ }
270
+
271
+ treeTraverse(doc);
272
+
273
+ if (inParaRange) {
274
+ for (let i = 0; i < textPosVec.length; i++) {
275
+ const val = textPosVec[i];
276
+ if (retVal[i] === -1) {
277
+ if (val < currentTextPos) {
278
+ retVal[i] = 1;
279
+ } else {
280
+ retVal[i] = doc.nodeSize - 1;
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ return retVal.map((x) => doc.resolve(x - 1));
287
+ }
288
+
289
+ const selectText: CommandFactory = (
290
+ textStart: number,
291
+ length: number,
292
+ paraNum = 0,
293
+ ) => {
294
+ return function (
295
+ state: EditorState,
296
+ dispatch?: (tr: Transaction) => void,
297
+ view?: EditorView,
298
+ ) {
299
+ const [$head, $anchor] = textPositionsToResolvedPos(
300
+ [textStart + length, textStart],
301
+ state.doc,
302
+ paraNum,
303
+ );
304
+
305
+ const tr = state.tr.setSelection(new TextSelection($anchor, $head));
306
+ if (view) {
307
+ view.dispatch(tr);
308
+ }
309
+
310
+ return true;
311
+ };
312
+ };
313
+
314
+ export class ExtensionSelection extends Extension {
315
+ name = 'selection';
316
+
317
+ extractSelection(): Node {
318
+ const state = this.editor.state;
319
+ const { from, to } = state.selection;
320
+ const slice = state.doc.slice(from, to);
321
+
322
+ if (sliceHasOnlyText(slice)) {
323
+ const para = state.schema.nodes.paragraph.create(null, slice.content);
324
+ return state.schema.topNodeType.createAndFill(null, [para])!;
325
+ }
326
+
327
+ return state.schema.topNodeType.createAndFill(null, slice.content)!;
328
+ }
329
+
330
+ replaceSelection(otherDoc: Node) {
331
+ const preferPlain = false;
332
+ const view = this.editor.view;
333
+ const state = this.editor.state;
334
+
335
+ let slice: Slice;
336
+
337
+ if (otherDoc.type?.name === 'doc') {
338
+ otherDoc = createNodeFromObject(otherDoc.toJSON(), this.editor.schema);
339
+ }
340
+ slice = new Slice(otherDoc.content, 1, 1);
341
+
342
+ const $context = state.selection.$from;
343
+
344
+ slice = fixSlice(slice, $context);
345
+
346
+ let singleNode = sliceSingleNode(slice);
347
+ let tr = singleNode
348
+ ? state.tr.replaceSelectionWith(singleNode, preferPlain)
349
+ : state.tr.replaceSelection(slice);
350
+ view.dispatch(tr.scrollIntoView());
351
+ }
352
+
353
+ appendSelection(otherDoc: Node) {
354
+ const view = this.editor.view;
355
+ const { state } = view;
356
+
357
+ let slice: Slice;
358
+
359
+ if (otherDoc.type?.name === 'doc') {
360
+ otherDoc = createNodeFromObject(otherDoc.toJSON(), this.editor.schema);
361
+ }
362
+ slice = new Slice(otherDoc.content, 1, 1);
363
+
364
+ const $context = view.state.selection.$from;
365
+
366
+ slice = fixSlice(slice, $context);
367
+
368
+ const tr = state.tr.insert(view.state.selection.to, slice.content);
369
+ view.dispatch(tr.scrollIntoView());
370
+ }
371
+
372
+ override getCommandFactories(editor: CoreEditor): Partial<CommandFactories> {
373
+ this.editor = editor;
374
+ return {
375
+ 'selectAll': () => selectAll(),
376
+ 'selectText': (...args) => selectText(...args),
377
+ };
378
+ }
379
+ }
@@ -0,0 +1,50 @@
1
+ import { type CoreEditor, Extension } from '@kerebron/editor';
2
+ import { type EditorState, type Transaction } from 'prosemirror-state';
3
+ import { type CommandFactories } from '@kerebron/editor/commands';
4
+
5
+ type TextAlign = 'left' | 'center' | 'right' | 'justify';
6
+
7
+ /**
8
+ * Extension that adds text alignment commands.
9
+ * Works by setting a 'textAlign' attribute on block nodes that support it.
10
+ */
11
+ export class ExtensionTextAlign extends Extension {
12
+ name = 'textAlign';
13
+
14
+ override getCommandFactories(editor: CoreEditor): Partial<CommandFactories> {
15
+ const setAlignment = (align: TextAlign) => {
16
+ return (state: EditorState, dispatch?: (tr: Transaction) => void) => {
17
+ const { selection, tr } = state;
18
+ const { from, to } = selection;
19
+
20
+ let changed = false;
21
+ state.doc.nodesBetween(from, to, (node, pos) => {
22
+ // Check if this node type has a textAlign attribute
23
+ if (
24
+ node.isBlock &&
25
+ node.type.spec.attrs &&
26
+ 'textAlign' in node.type.spec.attrs
27
+ ) {
28
+ tr.setNodeMarkup(pos, undefined, {
29
+ ...node.attrs,
30
+ textAlign: align,
31
+ });
32
+ changed = true;
33
+ }
34
+ });
35
+
36
+ if (changed && dispatch) {
37
+ dispatch(tr);
38
+ }
39
+ return changed;
40
+ };
41
+ };
42
+
43
+ return {
44
+ setTextAlignLeft: () => setAlignment('left'),
45
+ setTextAlignCenter: () => setAlignment('center'),
46
+ setTextAlignRight: () => setAlignment('right'),
47
+ setTextAlignJustify: () => setAlignment('justify'),
48
+ };
49
+ }
50
+ }
@@ -0,0 +1,20 @@
1
+ import { MarkSpec } from 'prosemirror-model';
2
+
3
+ import { Mark } from '@kerebron/editor';
4
+
5
+ export class MarkBookmark extends Mark {
6
+ override name = 'bookmark';
7
+ requires = ['doc'];
8
+
9
+ override getMarkSpec(): MarkSpec {
10
+ return {
11
+ attrs: {
12
+ id: {},
13
+ },
14
+ parseDOM: [],
15
+ toDOM(mark) {
16
+ return ['a', { id: mark.attrs.id }, 0];
17
+ },
18
+ };
19
+ }
20
+ }
@@ -0,0 +1,17 @@
1
+ import { MarkSpec } from 'prosemirror-model';
2
+
3
+ import { Mark } from '@kerebron/editor';
4
+
5
+ export class MarkChange extends Mark {
6
+ override name = 'change';
7
+ requires = ['doc'];
8
+
9
+ override getMarkSpec(): MarkSpec {
10
+ return {
11
+ parseDOM: [],
12
+ toDOM() {
13
+ return ['change', 0];
14
+ },
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,35 @@
1
+ import type { MarkSpec, MarkType } from 'prosemirror-model';
2
+ import { type CoreEditor, Mark } from '@kerebron/editor';
3
+ import {
4
+ type CommandFactories,
5
+ type CommandShortcuts,
6
+ } from '@kerebron/editor/commands';
7
+
8
+ export class MarkCode extends Mark {
9
+ override name = 'code';
10
+ requires = ['doc'];
11
+
12
+ override getMarkSpec(): MarkSpec {
13
+ return {
14
+ parseDOM: [{ tag: 'code' }],
15
+ toDOM() {
16
+ return ['code', 0];
17
+ },
18
+ };
19
+ }
20
+
21
+ override getCommandFactories(
22
+ editor: CoreEditor,
23
+ type: MarkType,
24
+ ): Partial<CommandFactories> {
25
+ return {
26
+ 'toggleCode': () => editor.commandFactories.toggleMark(type),
27
+ };
28
+ }
29
+
30
+ override getKeyboardShortcuts(): Partial<CommandShortcuts> {
31
+ return {
32
+ 'Mod-`': 'toggleCode',
33
+ };
34
+ }
35
+ }
@@ -0,0 +1,38 @@
1
+ import { type MarkSpec } from 'prosemirror-model';
2
+ import { Mark } from '@kerebron/editor';
3
+
4
+ /** Mark for text highlight (background color) */
5
+ export class MarkHighlight extends Mark {
6
+ override name = 'highlight';
7
+
8
+ override getMarkSpec(): MarkSpec {
9
+ return {
10
+ attrs: {
11
+ color: { default: null },
12
+ },
13
+ parseDOM: [
14
+ {
15
+ tag: 'mark',
16
+ getAttrs(dom: HTMLElement) {
17
+ const color = dom.style.backgroundColor;
18
+ return color ? { color } : { color: 'yellow' };
19
+ },
20
+ },
21
+ {
22
+ tag: 'span[style*="background-color"]',
23
+ getAttrs(dom: HTMLElement) {
24
+ const color = dom.style.backgroundColor;
25
+ return color ? { color } : false;
26
+ },
27
+ },
28
+ ],
29
+ toDOM(mark) {
30
+ const { color } = mark.attrs;
31
+ const bgColor = color || 'yellow';
32
+ return ['mark', {
33
+ style: `background-color: ${bgColor}; color: inherit;`,
34
+ }, 0];
35
+ },
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,41 @@
1
+ import { type CoreEditor, Mark } from '@kerebron/editor';
2
+ import {
3
+ type CommandFactories,
4
+ type CommandShortcuts,
5
+ } from '@kerebron/editor/commands';
6
+ import type { MarkSpec, MarkType } from 'prosemirror-model';
7
+
8
+ export class MarkItalic extends Mark {
9
+ override name = 'em';
10
+ requires = ['doc'];
11
+
12
+ override getMarkSpec(): MarkSpec {
13
+ return {
14
+ parseDOM: [
15
+ { tag: 'i' },
16
+ { tag: 'em' },
17
+ { style: 'font-assets=italic' },
18
+ { style: 'font-assets=normal', clearMark: (m) => m.type.name == 'em' },
19
+ ],
20
+ toDOM() {
21
+ return ['em', 0];
22
+ },
23
+ };
24
+ }
25
+
26
+ override getCommandFactories(
27
+ editor: CoreEditor,
28
+ type: MarkType,
29
+ ): Partial<CommandFactories> {
30
+ return {
31
+ 'toggleItalic': () => editor.commandFactories.toggleMark(type),
32
+ };
33
+ }
34
+
35
+ override getKeyboardShortcuts(): Partial<CommandShortcuts> {
36
+ return {
37
+ 'Mod-i': 'toggleItalic',
38
+ 'Mod-I': 'toggleItalic',
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,32 @@
1
+ import { Mark as PmMark, MarkSpec } from 'prosemirror-model';
2
+ import { Mark } from '@kerebron/editor';
3
+
4
+ export class MarkLink extends Mark {
5
+ override name = 'link';
6
+ requires = ['doc'];
7
+
8
+ override getMarkSpec(): MarkSpec {
9
+ return {
10
+ attrs: {
11
+ href: {},
12
+ title: { default: null },
13
+ },
14
+ inclusive: false,
15
+ parseDOM: [
16
+ {
17
+ tag: 'a[href]',
18
+ getAttrs(dom: HTMLElement) {
19
+ return {
20
+ href: dom.getAttribute('href'),
21
+ title: dom.getAttribute('title'),
22
+ };
23
+ },
24
+ },
25
+ ],
26
+ toDOM(node) {
27
+ const { href, title } = node.attrs;
28
+ return ['a', { href, title }, 0];
29
+ },
30
+ };
31
+ }
32
+ }
@@ -0,0 +1,38 @@
1
+ import { type CoreEditor, Mark } from '@kerebron/editor';
2
+ import type { MarkSpec, MarkType } from 'prosemirror-model';
3
+ import {
4
+ type CommandFactories,
5
+ type CommandShortcuts,
6
+ } from '@kerebron/editor/commands';
7
+
8
+ export class MarkStrike extends Mark {
9
+ override name = 'strike';
10
+ requires = ['doc'];
11
+
12
+ override getMarkSpec(): MarkSpec {
13
+ return {
14
+ parseDOM: [
15
+ { tag: 'strike' },
16
+ ],
17
+ toDOM() {
18
+ return ['strike', 0];
19
+ },
20
+ };
21
+ }
22
+
23
+ override getCommandFactories(
24
+ editor: CoreEditor,
25
+ type: MarkType,
26
+ ): Partial<CommandFactories> {
27
+ return {
28
+ 'toggleStrike': () => editor.commandFactories.toggleMark(type),
29
+ };
30
+ }
31
+
32
+ override getKeyboardShortcuts(): Partial<CommandShortcuts> {
33
+ return {
34
+ 'Mod-s': 'toggleStrike',
35
+ 'Mod-S': 'toggleStrike',
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,52 @@
1
+ import { type CoreEditor, Mark } from '@kerebron/editor';
2
+ import {
3
+ type CommandFactories,
4
+ type CommandShortcuts,
5
+ } from '@kerebron/editor/commands';
6
+ import { MarkSpec, type MarkType } from 'prosemirror-model';
7
+
8
+ export class MarkStrong extends Mark {
9
+ override name = 'strong';
10
+ requires = ['doc'];
11
+
12
+ override getMarkSpec(): MarkSpec {
13
+ return {
14
+ parseDOM: [
15
+ { tag: 'strong' },
16
+ // This works around a Google Docs misbehavior where
17
+ // pasted content will be inexplicably wrapped in `<b>`
18
+ // tags with a font-weight normal.
19
+ {
20
+ tag: 'b',
21
+ getAttrs: (node: HTMLElement) =>
22
+ node.style.fontWeight != 'normal' && null,
23
+ },
24
+ { style: 'font-weight=400', clearMark: (m) => m.type.name == 'strong' },
25
+ {
26
+ style: 'font-weight',
27
+ getAttrs: (value: string) =>
28
+ /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null,
29
+ },
30
+ ],
31
+ toDOM() {
32
+ return ['strong', 0];
33
+ },
34
+ };
35
+ }
36
+
37
+ override getCommandFactories(
38
+ editor: CoreEditor,
39
+ type: MarkType,
40
+ ): Partial<CommandFactories> {
41
+ return {
42
+ 'toggleStrong': () => editor.commandFactories.toggleMark(type),
43
+ };
44
+ }
45
+
46
+ override getKeyboardShortcuts(): Partial<CommandShortcuts> {
47
+ return {
48
+ 'Mod-b': 'toggleStrong',
49
+ 'Mod-B': 'toggleStrong',
50
+ };
51
+ }
52
+ }