@kerebron/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 (167) hide show
  1. package/esm/CoreEditor.d.ts +40 -0
  2. package/esm/CoreEditor.d.ts.map +1 -0
  3. package/esm/CoreEditor.js +224 -0
  4. package/esm/CoreEditor.js.map +1 -0
  5. package/esm/DummyEditorView.d.ts +60 -0
  6. package/esm/DummyEditorView.d.ts.map +1 -0
  7. package/esm/DummyEditorView.js +243 -0
  8. package/esm/DummyEditorView.js.map +1 -0
  9. package/esm/Extension.d.ts +31 -0
  10. package/esm/Extension.d.ts.map +1 -0
  11. package/esm/Extension.js +35 -0
  12. package/esm/Extension.js.map +1 -0
  13. package/esm/ExtensionManager.d.ts +32 -0
  14. package/esm/ExtensionManager.d.ts.map +1 -0
  15. package/esm/ExtensionManager.js +223 -0
  16. package/esm/ExtensionManager.js.map +1 -0
  17. package/esm/Mark.d.ts +23 -0
  18. package/esm/Mark.d.ts.map +1 -0
  19. package/esm/Mark.js +28 -0
  20. package/esm/Mark.js.map +1 -0
  21. package/esm/Node.d.ts +32 -0
  22. package/esm/Node.d.ts.map +1 -0
  23. package/esm/Node.js +37 -0
  24. package/esm/Node.js.map +1 -0
  25. package/esm/commands/CommandManager.d.ts +24 -0
  26. package/esm/commands/CommandManager.d.ts.map +1 -0
  27. package/esm/commands/CommandManager.js +101 -0
  28. package/esm/commands/CommandManager.js.map +1 -0
  29. package/esm/commands/baseCommandFactories.d.ts +3 -0
  30. package/esm/commands/baseCommandFactories.d.ts.map +1 -0
  31. package/esm/commands/baseCommandFactories.js +862 -0
  32. package/esm/commands/baseCommandFactories.js.map +1 -0
  33. package/esm/commands/createChainableState.d.ts +3 -0
  34. package/esm/commands/createChainableState.d.ts.map +1 -0
  35. package/esm/commands/createChainableState.js +30 -0
  36. package/esm/commands/createChainableState.js.map +1 -0
  37. package/esm/commands/keyCommandFactories.d.ts +3 -0
  38. package/esm/commands/keyCommandFactories.d.ts.map +1 -0
  39. package/esm/commands/keyCommandFactories.js +11 -0
  40. package/esm/commands/keyCommandFactories.js.map +1 -0
  41. package/esm/commands/mod.d.ts +7 -0
  42. package/esm/commands/mod.d.ts.map +1 -0
  43. package/esm/commands/mod.js +82 -0
  44. package/esm/commands/mod.js.map +1 -0
  45. package/esm/commands/replaceCommandFactories.d.ts +3 -0
  46. package/esm/commands/replaceCommandFactories.d.ts.map +1 -0
  47. package/esm/commands/replaceCommandFactories.js +95 -0
  48. package/esm/commands/replaceCommandFactories.js.map +1 -0
  49. package/esm/commands/types.d.ts +22 -0
  50. package/esm/commands/types.d.ts.map +1 -0
  51. package/esm/commands/types.js +2 -0
  52. package/esm/commands/types.js.map +1 -0
  53. package/esm/mod.d.ts +9 -0
  54. package/esm/mod.d.ts.map +1 -0
  55. package/esm/mod.js +9 -0
  56. package/esm/mod.js.map +1 -0
  57. package/esm/nodeToTreeString.d.ts +10 -0
  58. package/esm/nodeToTreeString.d.ts.map +1 -0
  59. package/esm/nodeToTreeString.js +75 -0
  60. package/esm/nodeToTreeString.js.map +1 -0
  61. package/esm/package.json +3 -0
  62. package/esm/plugins/TrackSelecionPlugin.d.ts +6 -0
  63. package/esm/plugins/TrackSelecionPlugin.d.ts.map +1 -0
  64. package/esm/plugins/TrackSelecionPlugin.js +25 -0
  65. package/esm/plugins/TrackSelecionPlugin.js.map +1 -0
  66. package/esm/plugins/input-rules/InputRulesPlugin.d.ts +25 -0
  67. package/esm/plugins/input-rules/InputRulesPlugin.d.ts.map +1 -0
  68. package/esm/plugins/input-rules/InputRulesPlugin.js +169 -0
  69. package/esm/plugins/input-rules/InputRulesPlugin.js.map +1 -0
  70. package/esm/plugins/input-rules/mod.d.ts +3 -0
  71. package/esm/plugins/input-rules/mod.d.ts.map +1 -0
  72. package/esm/plugins/input-rules/mod.js +3 -0
  73. package/esm/plugins/input-rules/mod.js.map +1 -0
  74. package/esm/plugins/input-rules/rulebuilders.d.ts +6 -0
  75. package/esm/plugins/input-rules/rulebuilders.d.ts.map +1 -0
  76. package/esm/plugins/input-rules/rulebuilders.js +65 -0
  77. package/esm/plugins/input-rules/rulebuilders.js.map +1 -0
  78. package/esm/plugins/keymap/keymap.d.ts +12 -0
  79. package/esm/plugins/keymap/keymap.d.ts.map +1 -0
  80. package/esm/plugins/keymap/keymap.js +126 -0
  81. package/esm/plugins/keymap/keymap.js.map +1 -0
  82. package/esm/plugins/keymap/mod.d.ts +2 -0
  83. package/esm/plugins/keymap/mod.d.ts.map +1 -0
  84. package/esm/plugins/keymap/mod.js +2 -0
  85. package/esm/plugins/keymap/mod.js.map +1 -0
  86. package/esm/plugins/keymap/w3c-keyname.d.ts +4 -0
  87. package/esm/plugins/keymap/w3c-keyname.d.ts.map +1 -0
  88. package/esm/plugins/keymap/w3c-keyname.js +125 -0
  89. package/esm/plugins/keymap/w3c-keyname.js.map +1 -0
  90. package/esm/search/mod.d.ts +3 -0
  91. package/esm/search/mod.d.ts.map +1 -0
  92. package/esm/search/mod.js +3 -0
  93. package/esm/search/mod.js.map +1 -0
  94. package/esm/search/query.d.ts +45 -0
  95. package/esm/search/query.d.ts.map +1 -0
  96. package/esm/search/query.js +335 -0
  97. package/esm/search/query.js.map +1 -0
  98. package/esm/search/search.d.ts +32 -0
  99. package/esm/search/search.d.ts.map +1 -0
  100. package/esm/search/search.js +200 -0
  101. package/esm/search/search.js.map +1 -0
  102. package/esm/types.d.ts +59 -0
  103. package/esm/types.d.ts.map +1 -0
  104. package/esm/types.js +2 -0
  105. package/esm/types.js.map +1 -0
  106. package/esm/ui.d.ts +15 -0
  107. package/esm/ui.d.ts.map +1 -0
  108. package/esm/ui.js +17 -0
  109. package/esm/ui.js.map +1 -0
  110. package/esm/utilities/SmartOutput.d.ts +41 -0
  111. package/esm/utilities/SmartOutput.d.ts.map +1 -0
  112. package/esm/utilities/SmartOutput.js +202 -0
  113. package/esm/utilities/SmartOutput.js.map +1 -0
  114. package/esm/utilities/createNodeFromContent.d.ts +9 -0
  115. package/esm/utilities/createNodeFromContent.d.ts.map +1 -0
  116. package/esm/utilities/createNodeFromContent.js +33 -0
  117. package/esm/utilities/createNodeFromContent.js.map +1 -0
  118. package/esm/utilities/getHtmlAttributes.d.ts +9 -0
  119. package/esm/utilities/getHtmlAttributes.d.ts.map +1 -0
  120. package/esm/utilities/getHtmlAttributes.js +48 -0
  121. package/esm/utilities/getHtmlAttributes.js.map +1 -0
  122. package/esm/utilities/getShadowRoot.d.ts +2 -0
  123. package/esm/utilities/getShadowRoot.d.ts.map +1 -0
  124. package/esm/utilities/getShadowRoot.js +17 -0
  125. package/esm/utilities/getShadowRoot.js.map +1 -0
  126. package/esm/utilities/mod.d.ts +6 -0
  127. package/esm/utilities/mod.d.ts.map +1 -0
  128. package/esm/utilities/mod.js +6 -0
  129. package/esm/utilities/mod.js.map +1 -0
  130. package/esm/utilities/toRawTextResult.d.ts +3 -0
  131. package/esm/utilities/toRawTextResult.d.ts.map +1 -0
  132. package/esm/utilities/toRawTextResult.js +22 -0
  133. package/esm/utilities/toRawTextResult.js.map +1 -0
  134. package/package.json +5 -2
  135. package/src/CoreEditor.ts +277 -0
  136. package/src/DummyEditorView.ts +403 -0
  137. package/src/Extension.ts +63 -0
  138. package/src/ExtensionManager.ts +328 -0
  139. package/src/Mark.ts +47 -0
  140. package/src/Node.ts +66 -0
  141. package/src/commands/CommandManager.ts +145 -0
  142. package/src/commands/baseCommandFactories.ts +1103 -0
  143. package/src/commands/createChainableState.ts +36 -0
  144. package/src/commands/keyCommandFactories.ts +26 -0
  145. package/src/commands/mod.ts +104 -0
  146. package/src/commands/replaceCommandFactories.ts +129 -0
  147. package/src/commands/types.ts +30 -0
  148. package/src/mod.ts +8 -0
  149. package/src/nodeToTreeString.ts +100 -0
  150. package/src/plugins/TrackSelecionPlugin.ts +27 -0
  151. package/src/plugins/input-rules/InputRulesPlugin.ts +242 -0
  152. package/src/plugins/input-rules/mod.ts +2 -0
  153. package/src/plugins/input-rules/rulebuilders.ts +88 -0
  154. package/src/plugins/keymap/keymap.ts +117 -0
  155. package/src/plugins/keymap/mod.ts +1 -0
  156. package/src/plugins/keymap/w3c-keyname.ts +123 -0
  157. package/src/search/mod.ts +2 -0
  158. package/src/search/query.ts +412 -0
  159. package/src/search/search.ts +284 -0
  160. package/src/types.ts +71 -0
  161. package/src/ui.ts +35 -0
  162. package/src/utilities/SmartOutput.ts +284 -0
  163. package/src/utilities/createNodeFromContent.ts +66 -0
  164. package/src/utilities/getHtmlAttributes.ts +68 -0
  165. package/src/utilities/getShadowRoot.ts +18 -0
  166. package/src/utilities/mod.ts +5 -0
  167. package/src/utilities/toRawTextResult.ts +27 -0
@@ -0,0 +1,63 @@
1
+ import type { Plugin } from 'prosemirror-state';
2
+ import type { Node, Schema, SchemaSpec } from 'prosemirror-model';
3
+
4
+ import { type CoreEditor } from './CoreEditor.js';
5
+ import type { InputRule } from './plugins/input-rules/InputRulesPlugin.js';
6
+ import { CommandFactories, CommandShortcuts } from './commands/mod.js';
7
+
8
+ export interface ExtensionConfig {
9
+ // @ts-ignore - this is a dynamic key
10
+ [key: string]: any;
11
+
12
+ requires: Array<Extension | string>;
13
+ }
14
+
15
+ export interface Converter {
16
+ fromDoc(document: Node): Promise<Uint8Array>;
17
+ toDoc(content: Uint8Array): Promise<Node>;
18
+ }
19
+
20
+ export abstract class Extension {
21
+ readonly type = 'extension';
22
+ abstract name: string;
23
+ protected editor!: CoreEditor;
24
+
25
+ readonly conflicts?: Array<string>;
26
+
27
+ public constructor(protected config: Partial<ExtensionConfig> = {}) {
28
+ }
29
+
30
+ setEditor(editor: CoreEditor) {
31
+ this.editor = editor;
32
+ }
33
+
34
+ getEditor() {
35
+ return this.editor;
36
+ }
37
+
38
+ created() {
39
+ }
40
+
41
+ getInputRules(): InputRule[] {
42
+ return [];
43
+ }
44
+
45
+ getProseMirrorPlugins(): Plugin[] {
46
+ return [];
47
+ }
48
+
49
+ getCommandFactories(editor: CoreEditor): Partial<CommandFactories> {
50
+ return {};
51
+ }
52
+
53
+ getKeyboardShortcuts(editor: CoreEditor): Partial<CommandShortcuts> {
54
+ return {};
55
+ }
56
+
57
+ getConverters(editor: CoreEditor, schema: Schema): Record<string, Converter> {
58
+ return {};
59
+ }
60
+
61
+ setupSpec(spec: SchemaSpec) {
62
+ }
63
+ }
@@ -0,0 +1,328 @@
1
+ import { MarkSpec, NodeSpec, Schema } from 'prosemirror-model';
2
+ import { Plugin } from 'prosemirror-state';
3
+ import { NodeViewConstructor } from 'prosemirror-view';
4
+
5
+ import { Converter, Extension } from './Extension.js';
6
+ import { AnyExtension, AnyExtensionOrReq } from './types.js';
7
+ import { CoreEditor } from './CoreEditor.js';
8
+ import { Mark } from './Mark.js';
9
+ import { Node } from './Node.js';
10
+ import {
11
+ InputRule,
12
+ InputRulesPlugin,
13
+ } from './plugins/input-rules/InputRulesPlugin.js';
14
+ import { KeymapPlugin } from './plugins/keymap/keymap.js';
15
+ import { CommandShortcuts, firstCommand } from './commands/mod.js';
16
+ import { type Command } from 'prosemirror-state';
17
+ import { addAttributesToSchema } from './utilities/getHtmlAttributes.js';
18
+ import { type CommandManager } from './commands/CommandManager.js';
19
+ import { TrackSelecionPlugin } from './plugins/TrackSelecionPlugin.js';
20
+
21
+ export function findDuplicates(items: any[]): any[] {
22
+ const filtered = items.filter((el, index) => items.indexOf(el) !== index);
23
+
24
+ return Array.from(new Set(filtered));
25
+ }
26
+
27
+ export function splitExtensions(extensions: Iterable<AnyExtension>) {
28
+ const baseExtensions = Array.from(extensions).filter((extension) =>
29
+ extension.type === 'extension'
30
+ ) as Extension[];
31
+ const nodeExtensions = Array.from(extensions).filter((extension) =>
32
+ extension.type === 'node'
33
+ ) as Node[];
34
+ const markExtensions = Array.from(extensions).filter((extension) =>
35
+ extension.type === 'mark'
36
+ ) as Mark[];
37
+
38
+ return {
39
+ baseExtensions,
40
+ nodeExtensions,
41
+ markExtensions,
42
+ };
43
+ }
44
+
45
+ export class ExtensionManager {
46
+ public readonly schema: Schema;
47
+
48
+ public readonly extensions: Set<AnyExtension> = new Set();
49
+ readonly plugins: Plugin[] = [];
50
+ readonly nodeViews: Record<string, NodeViewConstructor> = {};
51
+
52
+ public converters: Record<string, Converter> = {};
53
+
54
+ private debug = true;
55
+
56
+ constructor(
57
+ extensions: AnyExtensionOrReq[],
58
+ private editor: CoreEditor,
59
+ private commandManager: CommandManager,
60
+ ) {
61
+ this.setupExtensions(new Set(extensions));
62
+ this.schema = this.getSchemaByResolvedExtensions(editor);
63
+
64
+ const event = new CustomEvent('schema:ready', {
65
+ detail: {
66
+ editor,
67
+ schema: this.schema,
68
+ },
69
+ });
70
+ editor.dispatchEvent(event);
71
+ }
72
+
73
+ getExtension<T extends Extension>(name: string): T | undefined {
74
+ const { nodeExtensions, markExtensions, baseExtensions } = splitExtensions(
75
+ this.extensions,
76
+ );
77
+
78
+ for (const extension of baseExtensions) {
79
+ if (extension.name === name) {
80
+ return <T> extension;
81
+ }
82
+ }
83
+ }
84
+
85
+ private initPlugins() {
86
+ const inputRules: InputRule[] = [];
87
+ const keyBindings: Map<string, Command> = new Map();
88
+
89
+ const mergeShortcuts = (
90
+ toInsert: Partial<CommandShortcuts>,
91
+ extName: string,
92
+ ) => {
93
+ for (const key in toInsert) {
94
+ if (!toInsert[key]) {
95
+ continue;
96
+ }
97
+
98
+ const commandFactory =
99
+ this.commandManager.commandFactories[toInsert[key]];
100
+ if (!commandFactory) {
101
+ console.warn(`No command constructor: ${toInsert[key]}`);
102
+ continue;
103
+ }
104
+ const command = commandFactory();
105
+
106
+ const keyBinding = keyBindings.get(key);
107
+ if (keyBinding) {
108
+ keyBindings.set(key, firstCommand(command, keyBinding));
109
+ } else {
110
+ keyBindings.set(key, command);
111
+ }
112
+ }
113
+ };
114
+
115
+ let converters = {};
116
+
117
+ for (const extension of this.extensions) {
118
+ extension.setEditor(this.editor);
119
+
120
+ if (extension.type === 'node') {
121
+ const nodeType = this.schema.nodes[extension.name];
122
+ inputRules.push(...extension.getInputRules(nodeType));
123
+ this.plugins.push(
124
+ ...extension.getProseMirrorPlugins(),
125
+ );
126
+ this.commandManager.mergeCommandFactories(
127
+ extension.getCommandFactories(this.editor, nodeType),
128
+ extension.name,
129
+ );
130
+ mergeShortcuts(
131
+ extension.getKeyboardShortcuts(this.editor),
132
+ extension.name,
133
+ );
134
+ converters = {
135
+ ...converters,
136
+ ...extension.getConverters(this.editor, this.schema),
137
+ };
138
+ const nodeView = extension.getNodeView(this.editor);
139
+ if (nodeView) {
140
+ this.nodeViews[extension.name] = nodeView;
141
+ }
142
+ }
143
+ if (extension.type === 'mark') {
144
+ const markType = this.schema.marks[extension.name];
145
+ inputRules.push(...extension.getInputRules(markType));
146
+ this.commandManager.mergeCommandFactories(
147
+ extension.getCommandFactories(this.editor, markType),
148
+ extension.name,
149
+ );
150
+ mergeShortcuts(
151
+ extension.getKeyboardShortcuts(this.editor),
152
+ extension.name,
153
+ );
154
+ }
155
+ if (extension.type === 'extension') {
156
+ this.plugins.push(
157
+ ...extension.getProseMirrorPlugins(),
158
+ );
159
+ this.commandManager.mergeCommandFactories(
160
+ extension.getCommandFactories(this.editor),
161
+ extension.name,
162
+ );
163
+ mergeShortcuts(
164
+ extension.getKeyboardShortcuts(this.editor),
165
+ extension.name,
166
+ );
167
+ converters = {
168
+ ...converters,
169
+ ...extension.getConverters(this.editor, this.schema),
170
+ };
171
+ }
172
+ }
173
+
174
+ if (this.debug) {
175
+ for (const key in keyBindings) {
176
+ const keyBinding = keyBindings.get(key);
177
+ if (!keyBinding) {
178
+ continue;
179
+ }
180
+
181
+ const wrapperCommand: Command = (state, dispatch, view) => {
182
+ console.debug(`Key: ${key}`);
183
+ return true;
184
+ };
185
+ keyBindings.set(
186
+ key,
187
+ firstCommand(wrapperCommand, keyBinding),
188
+ );
189
+ }
190
+ }
191
+
192
+ this.converters = converters;
193
+
194
+ this.plugins.push(new InputRulesPlugin(inputRules));
195
+ this.plugins.push(new KeymapPlugin(Object.fromEntries(keyBindings)));
196
+ this.plugins.push(new TrackSelecionPlugin(this.editor));
197
+ }
198
+
199
+ private setupExtensions(extensions: Set<AnyExtensionOrReq>) {
200
+ const allExtensions = new Map<string, AnyExtensionOrReq>();
201
+
202
+ const createMap = (extensions: Set<AnyExtensionOrReq>) => {
203
+ for (const extension of extensions) {
204
+ if ('name' in extension) {
205
+ allExtensions.set(extension.name, extension);
206
+ }
207
+ if ('requires' in extension) {
208
+ const childExtensions = Array.from(extension.requires).filter((e) =>
209
+ typeof e !== 'string'
210
+ );
211
+ createMap(new Set(childExtensions));
212
+ }
213
+ }
214
+ };
215
+
216
+ createMap(extensions);
217
+
218
+ const initialized: Set<string> = new Set();
219
+
220
+ const initializeExtension = (extension: AnyExtension) => {
221
+ console.info(`Initialize ${extension.type} ${extension.name}`);
222
+ this.extensions.add(extension);
223
+ };
224
+
225
+ function recursiveInitializeExtension(extension: AnyExtensionOrReq) {
226
+ if ('name' in extension && initialized.has(extension.name)) {
227
+ return;
228
+ }
229
+
230
+ if ('requires' in extension) {
231
+ const requires = extension.requires || [];
232
+ const requireNames = requires.map((
233
+ e: string | AnyExtensionOrReq,
234
+ ): string => typeof e === 'string' ? e : ('name' in e ? e.name : ''));
235
+
236
+ for (const require of requireNames) {
237
+ if (!initialized.has(require)) {
238
+ const requiredExtension = allExtensions.get(require);
239
+ if (!requiredExtension) {
240
+ throw new Error(
241
+ `Required extension for (${
242
+ 'name' in extension ? extension.name : extension
243
+ }) not found: ${require}`,
244
+ );
245
+ }
246
+ recursiveInitializeExtension(requiredExtension);
247
+ }
248
+ }
249
+ }
250
+
251
+ if ('name' in extension) {
252
+ initializeExtension(extension);
253
+
254
+ initialized.add(extension.name);
255
+ allExtensions.delete(extension.name);
256
+ }
257
+ }
258
+
259
+ for (const extension of allExtensions.values()) {
260
+ recursiveInitializeExtension(extension);
261
+ }
262
+
263
+ if (allExtensions.size > 0) {
264
+ throw new Error(
265
+ 'Not all extensions initialized: ' +
266
+ Array.from(allExtensions.keys()).join(', '),
267
+ );
268
+ }
269
+ }
270
+
271
+ getSchemaByResolvedExtensions(editor: CoreEditor): Schema {
272
+ const { nodeExtensions, markExtensions, baseExtensions } = splitExtensions(
273
+ this.extensions,
274
+ );
275
+
276
+ for (const extension of baseExtensions) {
277
+ if (Array.isArray(extension.conflicts)) {
278
+ for (const name of extension.conflicts) {
279
+ if (this.getExtension(name)) {
280
+ throw new Error(`Extension conflict: ${extension.name} vs ${name}`);
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ const nodes: { [name: string]: NodeSpec } = {};
287
+ for (const extension of nodeExtensions) {
288
+ nodes[extension.name] = extension.getNodeSpec();
289
+ addAttributesToSchema(nodes[extension.name], extension);
290
+ }
291
+
292
+ const marks: { [name: string]: MarkSpec } = {};
293
+ for (const extension of markExtensions) {
294
+ marks[extension.name] = extension.getMarkSpec();
295
+ addAttributesToSchema(marks[extension.name], extension);
296
+ }
297
+
298
+ const spec = {
299
+ topNode: this.editor.config.topNode || 'doc',
300
+ nodes,
301
+ marks,
302
+ };
303
+
304
+ for (const extension of baseExtensions) {
305
+ if ('setupSpec' in baseExtensions) {
306
+ extension.setupSpec(spec);
307
+ }
308
+ }
309
+
310
+ const event = new CustomEvent('schema:spec', {
311
+ detail: {
312
+ editor,
313
+ spec,
314
+ },
315
+ });
316
+ editor.dispatchEvent(event);
317
+
318
+ return new Schema(spec);
319
+ }
320
+
321
+ created() {
322
+ this.initPlugins();
323
+
324
+ for (const extension of this.extensions) {
325
+ extension.created();
326
+ }
327
+ }
328
+ }
package/src/Mark.ts ADDED
@@ -0,0 +1,47 @@
1
+ import type { MarkSpec, MarkType } from 'prosemirror-model';
2
+
3
+ import type { InputRule } from './plugins/input-rules/InputRulesPlugin.js';
4
+ import type { CoreEditor } from './CoreEditor.js';
5
+ import type { CommandFactories, CommandShortcuts } from './commands/mod.js';
6
+ import { Attribute } from './types.js';
7
+
8
+ export interface MarkConfig {
9
+ // @ts-ignore - this is a dynamic key
10
+ [key: string]: any;
11
+ }
12
+
13
+ export abstract class Mark {
14
+ readonly type = 'mark';
15
+ name: string = 'node';
16
+ protected editor!: CoreEditor;
17
+
18
+ public readonly attributes: Record<string, Attribute<any>> = {};
19
+
20
+ public constructor(protected config: Partial<MarkConfig> = {}) {}
21
+
22
+ setEditor(editor: CoreEditor) {
23
+ this.editor = editor;
24
+ }
25
+
26
+ created() {
27
+ }
28
+
29
+ getMarkSpec(): MarkSpec {
30
+ throw new Error('MarkSpec not defined: ' + this.name);
31
+ }
32
+
33
+ getInputRules(type: MarkType): InputRule[] {
34
+ return [];
35
+ }
36
+
37
+ getCommandFactories(
38
+ editor: CoreEditor,
39
+ type: MarkType,
40
+ ): Partial<CommandFactories> {
41
+ return {};
42
+ }
43
+
44
+ getKeyboardShortcuts(editor: CoreEditor): Partial<CommandShortcuts> {
45
+ return {};
46
+ }
47
+ }
package/src/Node.ts ADDED
@@ -0,0 +1,66 @@
1
+ import type { NodeSpec, NodeType, Schema } from 'prosemirror-model';
2
+ import type { NodeViewConstructor } from 'prosemirror-view';
3
+ import type { Plugin } from 'prosemirror-state';
4
+
5
+ import type { InputRule } from './plugins/input-rules/InputRulesPlugin.js';
6
+ import type { CoreEditor } from './CoreEditor.js';
7
+ import type { Command, CommandShortcuts } from './commands/mod.js';
8
+ import type { Converter } from './Extension.js';
9
+ import { Attribute } from './types.js';
10
+
11
+ export interface NodeConfig {
12
+ // @ts-ignore - this is a dynamic key
13
+ [key: string]: any;
14
+ }
15
+
16
+ export interface CommandFactories {
17
+ [key: string]: () => Command;
18
+ }
19
+
20
+ export abstract class Node {
21
+ readonly type = 'node';
22
+ name: string = 'node';
23
+ protected editor!: CoreEditor;
24
+
25
+ public readonly attributes: Record<string, Attribute<any>> = {};
26
+
27
+ public constructor(protected config: Partial<NodeConfig> = {}) {}
28
+
29
+ setEditor(editor: CoreEditor) {
30
+ this.editor = editor;
31
+ }
32
+
33
+ created() {
34
+ }
35
+
36
+ getNodeSpec(): NodeSpec {
37
+ throw new Error('NodeSpec not defined: ' + this.name);
38
+ }
39
+
40
+ getInputRules(type: NodeType): InputRule[] {
41
+ return [];
42
+ }
43
+
44
+ getProseMirrorPlugins(): Plugin[] {
45
+ return [];
46
+ }
47
+
48
+ getCommandFactories(
49
+ editor: CoreEditor,
50
+ type: NodeType,
51
+ ): Partial<CommandFactories> {
52
+ return {};
53
+ }
54
+
55
+ getKeyboardShortcuts(editor: CoreEditor): Partial<CommandShortcuts> {
56
+ return {};
57
+ }
58
+
59
+ getNodeView(editor: CoreEditor): NodeViewConstructor | undefined {
60
+ return undefined;
61
+ }
62
+
63
+ getConverters(editor: CoreEditor, schema: Schema): Record<string, Converter> {
64
+ return {};
65
+ }
66
+ }
@@ -0,0 +1,145 @@
1
+ import { EditorView } from 'prosemirror-view';
2
+ import type { EditorState, Transaction } from 'prosemirror-state';
3
+
4
+ import type { CoreEditor } from '../CoreEditor.js';
5
+ import { createChainableState } from './createChainableState.js';
6
+ import type {
7
+ ChainedCommands,
8
+ Command,
9
+ CommandFactories,
10
+ CommandFactory,
11
+ } from './types.js';
12
+
13
+ import { baseCommandFactories } from './baseCommandFactories.js';
14
+ import { keyCommandFactories } from './keyCommandFactories.js';
15
+ import { replaceCommandFactories } from './replaceCommandFactories.js';
16
+
17
+ type CommandRunner = (...args: any[]) => boolean;
18
+
19
+ export class CommandManager {
20
+ public readonly commandFactories: { [key: string]: CommandFactory } = {};
21
+ public readonly run: { [key: string]: CommandRunner } = {};
22
+
23
+ private debug = true;
24
+
25
+ constructor(
26
+ private editor: CoreEditor,
27
+ ) {
28
+ this.mergeCommandFactories(baseCommandFactories, 'baseCommand');
29
+ this.mergeCommandFactories(keyCommandFactories, 'key');
30
+ this.mergeCommandFactories(replaceCommandFactories, 'replace');
31
+ }
32
+
33
+ public mergeCommandFactories(
34
+ toInsert: Partial<CommandFactories>,
35
+ extName: string,
36
+ ) {
37
+ for (const key in toInsert) {
38
+ const commandFactory = toInsert[key];
39
+ if (!commandFactory) {
40
+ continue;
41
+ }
42
+ this.overwriteCommandFactory(extName, key, commandFactory);
43
+ }
44
+ }
45
+
46
+ private overwriteCommandFactory(
47
+ extName: string,
48
+ name: string,
49
+ commandFactory: CommandFactory,
50
+ ) {
51
+ if (this.debug) {
52
+ const wrappedFactory = (...args: unknown[]) => {
53
+ const realCommand = commandFactory(...args);
54
+
55
+ const command: Command = (state, dispatch, view) => {
56
+ if (dispatch) {
57
+ console.debug(`Command: ${extName}.${name}`);
58
+ }
59
+ return realCommand(state, dispatch, view);
60
+ };
61
+
62
+ return command;
63
+ };
64
+
65
+ this.commandFactories[name] = wrappedFactory;
66
+ } else {
67
+ this.commandFactories[name] = commandFactory;
68
+ }
69
+
70
+ this.run[name] = (...args) => {
71
+ const factory = this.commandFactories[name];
72
+ const command: Command = factory(...args);
73
+ const state = this.editor.state;
74
+ const view = this.editor.view;
75
+ if (view instanceof EditorView) {
76
+ return command(state, view.dispatch, view);
77
+ } else {
78
+ return command(state, view.dispatch);
79
+ }
80
+ };
81
+ }
82
+
83
+ get state(): EditorState {
84
+ return this.editor.state;
85
+ }
86
+
87
+ get chain(): () => ChainedCommands {
88
+ return () => this.createChain();
89
+ }
90
+
91
+ get can(): () => ChainedCommands {
92
+ return () => this.createCan();
93
+ }
94
+
95
+ public createChain(
96
+ startTr?: Transaction,
97
+ shouldDispatch = true,
98
+ ): ChainedCommands {
99
+ const { commandFactories, editor, state } = this;
100
+ const { view } = editor;
101
+ const callbacks: boolean[] = [];
102
+ const hasStartTransaction = !!startTr;
103
+ const tr = startTr || state.tr;
104
+
105
+ const chainedState = createChainableState(tr, state);
106
+ const fakeDispatch = () => undefined;
107
+
108
+ const chain = {
109
+ ...Object.fromEntries(
110
+ Object.entries(commandFactories).map(([name, commandFactory]) => {
111
+ const chainedCommand = (...args: never[]) => {
112
+ const command = commandFactory(...args);
113
+ const callback = command(
114
+ chainedState,
115
+ shouldDispatch ? fakeDispatch : undefined,
116
+ );
117
+ callbacks.push(callback);
118
+ return chain;
119
+ };
120
+ return [name, chainedCommand];
121
+ }),
122
+ ),
123
+ run: () => {
124
+ if (
125
+ !hasStartTransaction &&
126
+ shouldDispatch &&
127
+ !tr.getMeta('preventDispatch')
128
+ ) {
129
+ view.dispatch(tr);
130
+ }
131
+
132
+ return callbacks.every((callback) => callback === true);
133
+ },
134
+ chain: () => {
135
+ return this.createChain(tr, shouldDispatch);
136
+ },
137
+ } as unknown as ChainedCommands;
138
+
139
+ return chain;
140
+ }
141
+
142
+ public createCan(startTr?: Transaction): ChainedCommands {
143
+ return this.createChain(startTr, false);
144
+ }
145
+ }