@kerebron/editor 0.4.31 → 0.5.1

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 (39) hide show
  1. package/README.md +3 -5
  2. package/esm/CoreEditor.d.ts +25 -7
  3. package/esm/CoreEditor.d.ts.map +1 -1
  4. package/esm/CoreEditor.js +93 -33
  5. package/esm/CoreEditor.js.map +1 -1
  6. package/esm/ExtensionManager.d.ts +6 -9
  7. package/esm/ExtensionManager.d.ts.map +1 -1
  8. package/esm/ExtensionManager.js +44 -50
  9. package/esm/ExtensionManager.js.map +1 -1
  10. package/esm/Node.d.ts +1 -1
  11. package/esm/Node.d.ts.map +1 -1
  12. package/esm/commands/CommandManager.d.ts +1 -2
  13. package/esm/commands/CommandManager.d.ts.map +1 -1
  14. package/esm/commands/CommandManager.js +2 -4
  15. package/esm/commands/CommandManager.js.map +1 -1
  16. package/esm/commands/baseCommandFactories.d.ts.map +1 -1
  17. package/esm/commands/baseCommandFactories.js +5 -3
  18. package/esm/commands/baseCommandFactories.js.map +1 -1
  19. package/esm/plugins/input-rules/InputRulesPlugin.d.ts +5 -4
  20. package/esm/plugins/input-rules/InputRulesPlugin.d.ts.map +1 -1
  21. package/esm/plugins/input-rules/InputRulesPlugin.js +88 -17
  22. package/esm/plugins/input-rules/InputRulesPlugin.js.map +1 -1
  23. package/esm/plugins/input-rules/rulebuilders.d.ts.map +1 -1
  24. package/esm/plugins/input-rules/rulebuilders.js +16 -9
  25. package/esm/plugins/input-rules/rulebuilders.js.map +1 -1
  26. package/esm/types.d.ts +4 -12
  27. package/esm/types.d.ts.map +1 -1
  28. package/esm/utilities/createNodeFromContent.js +1 -1
  29. package/esm/utilities/createNodeFromContent.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/CoreEditor.ts +144 -42
  32. package/src/ExtensionManager.ts +57 -60
  33. package/src/Node.ts +1 -1
  34. package/src/commands/CommandManager.ts +2 -5
  35. package/src/commands/baseCommandFactories.ts +6 -3
  36. package/src/plugins/input-rules/InputRulesPlugin.ts +126 -15
  37. package/src/plugins/input-rules/rulebuilders.ts +27 -10
  38. package/src/types.ts +4 -12
  39. package/src/utilities/createNodeFromContent.ts +2 -2
@@ -25,6 +25,7 @@ type PluginState = {
25
25
  export class InputRule {
26
26
  /// @internal
27
27
  handler: (
28
+ tr: Transaction,
28
29
  state: EditorState,
29
30
  match: RegExpMatchArray,
30
31
  start: number,
@@ -52,10 +53,11 @@ export class InputRule {
52
53
  /// rule's effect, or null to indicate the input was not handled.
53
54
  constructor(
54
55
  /// @internal
55
- readonly match: RegExp,
56
+ readonly regex: RegExp,
56
57
  handler:
57
58
  | string
58
59
  | ((
60
+ tr: Transaction,
59
61
  state: EditorState,
60
62
  match: RegExpMatchArray,
61
63
  start: number,
@@ -72,7 +74,7 @@ export class InputRule {
72
74
  inCode?: boolean | 'only';
73
75
  } = {},
74
76
  ) {
75
- this.match = match;
77
+ this.regex = regex;
76
78
  this.handler = typeof handler == 'string'
77
79
  ? stringHandler(handler)
78
80
  : handler;
@@ -83,11 +85,16 @@ export class InputRule {
83
85
 
84
86
  function stringHandler(string: string) {
85
87
  return function (
88
+ tr: Transaction,
86
89
  state: EditorState,
87
90
  match: RegExpMatchArray,
88
91
  start: number,
89
92
  end: number,
90
93
  ) {
94
+ if (!tr) {
95
+ tr = state.tr;
96
+ }
97
+
91
98
  let insert = string;
92
99
  if (match[1]) {
93
100
  const offset = match[0].lastIndexOf(match[1]);
@@ -99,7 +106,7 @@ function stringHandler(string: string) {
99
106
  start = end;
100
107
  }
101
108
  }
102
- return state.tr.insertText(insert, start, end);
109
+ return tr.insertText(insert, start, end);
103
110
  };
104
111
  }
105
112
 
@@ -124,7 +131,7 @@ export class InputRulesPlugin extends Plugin<PluginState> {
124
131
 
125
132
  props: {
126
133
  handleTextInput(view, from, to, text) {
127
- const cmd = runInputRules(from, to, text, rules);
134
+ const cmd = runInputRulesRange(from, to, text);
128
135
  const dispatch = (tr: Transaction) => {
129
136
  view.dispatch(tr);
130
137
  };
@@ -136,28 +143,115 @@ export class InputRulesPlugin extends Plugin<PluginState> {
136
143
  setTimeout(() => {
137
144
  const { $cursor } = view.state.selection as TextSelection;
138
145
  if ($cursor) {
139
- const cmd = runInputRules($cursor.pos, $cursor.pos, '', rules);
146
+ const cmd = runInputRulesRange($cursor.pos, $cursor.pos, '');
140
147
  const dispatch = (tr: Transaction) => {
141
148
  view.dispatch(tr);
142
149
  };
143
150
  return cmd(view.state, dispatch, view);
144
- // run(view, $cursor.pos, $cursor.pos, '', rules, this);
151
+ // run(view, $cursor.pos, $cursor.pos, '', this);
145
152
  }
146
153
  });
147
154
  },
148
155
  },
149
156
  },
150
157
 
158
+ rules,
151
159
  isInputRules: true,
152
160
  });
153
161
  }
154
162
  }
155
163
 
156
- export const runInputRules: CommandFactory = (
164
+ export const runInputRulesTexts: CommandFactory = () => {
165
+ const cmd: Command = (
166
+ state: EditorState,
167
+ dispatch?: (tr: Transaction) => void,
168
+ view?: EditorView,
169
+ ) => {
170
+ const plugins = state.plugins;
171
+ const plugin: InputRulesPlugin | undefined = plugins.find((plugin) =>
172
+ (plugin.spec as any).isInputRules
173
+ );
174
+ if (!plugin) {
175
+ return false;
176
+ }
177
+
178
+ const rules: readonly InputRule[] = plugin.spec.rules;
179
+
180
+ if (view?.composing) return false;
181
+
182
+ let doc = state.doc;
183
+ let tr = state.tr;
184
+
185
+ for (let i = 0; i < rules.length; i++) {
186
+ const rule = rules[i];
187
+
188
+ const textNodePositions: { pos: number; node: ProseMirrorNode }[] = [];
189
+ doc.descendants((node, pos) => {
190
+ if (node.isText) {
191
+ textNodePositions.push({ pos, node });
192
+ }
193
+ });
194
+
195
+ if (textNodePositions.length === 0) {
196
+ return false; // Nothing to do
197
+ }
198
+
199
+ // Process from the end of the document to the start to avoid position invalidation
200
+ for (let i = textNodePositions.length - 1; i >= 0; i--) {
201
+ const { pos, node } = textNodePositions[i];
202
+ if (!node.isText || !node.text) continue;
203
+
204
+ let text = node.text;
205
+
206
+ // throw new Error('aaaaaaaaaaa');
207
+
208
+ if (node.type.spec.code) {
209
+ if (!rule.inCode) continue;
210
+ } else if (rule.inCode === 'only') {
211
+ continue;
212
+ }
213
+ const match = rule.regex.exec(text);
214
+ if (!match) {
215
+ continue;
216
+ }
217
+
218
+ const from = pos;
219
+ const to = pos + node.nodeSize;
220
+
221
+ let subTr = rule.handler(
222
+ tr,
223
+ state,
224
+ match,
225
+ from - (match[0].length - text.length),
226
+ to,
227
+ );
228
+ if (!subTr) continue;
229
+
230
+ tr = subTr;
231
+ doc = tr.doc;
232
+
233
+ if (rule.undoable) {
234
+ tr.setMeta(plugin, { transform: tr, from, to, text });
235
+ }
236
+ }
237
+ }
238
+
239
+ if (dispatch && tr.docChanged) {
240
+ dispatch(tr);
241
+ }
242
+
243
+ return true;
244
+ };
245
+
246
+ cmd.displayName = 'runInputRulesTexts';
247
+
248
+ return cmd;
249
+ };
250
+
251
+ export const runInputRulesRange: CommandFactory = (
157
252
  from: number,
158
253
  to: number,
159
254
  text: string,
160
- rules: readonly InputRule[],
161
255
  ) => {
162
256
  const cmd: Command = (
163
257
  state: EditorState,
@@ -172,6 +266,8 @@ export const runInputRules: CommandFactory = (
172
266
  return false;
173
267
  }
174
268
 
269
+ const rules: readonly InputRule[] = plugin.spec.rules;
270
+
175
271
  if (view?.composing) return false;
176
272
  const $from = state.doc.resolve(from);
177
273
  const textBefore = $from.parent.textBetween(
@@ -180,6 +276,7 @@ export const runInputRules: CommandFactory = (
180
276
  null,
181
277
  '\ufffc',
182
278
  ) + text;
279
+ let tr = state.tr;
183
280
  for (let i = 0; i < rules.length; i++) {
184
281
  const rule = rules[i];
185
282
  if ($from.parent.type.spec.code) {
@@ -187,19 +284,33 @@ export const runInputRules: CommandFactory = (
187
284
  } else if (rule.inCode === 'only') {
188
285
  continue;
189
286
  }
190
- const match = rule.match.exec(textBefore);
191
- const tr = match &&
192
- rule.handler(state, match, from - (match[0].length - text.length), to);
193
- if (!tr) continue;
287
+ const match = rule.regex.exec(textBefore);
288
+ if (!match) {
289
+ continue;
290
+ }
291
+
292
+ const subTr = rule.handler(
293
+ tr,
294
+ state,
295
+ match,
296
+ from - (match[0].length - text.length),
297
+ to,
298
+ );
299
+ if (!subTr) continue;
300
+
301
+ tr = subTr;
194
302
  if (rule.undoable) {
195
303
  tr.setMeta(plugin, { transform: tr, from, to, text });
196
304
  }
197
- view?.dispatch(tr);
198
- return true;
199
305
  }
306
+
307
+ if (dispatch) {
308
+ dispatch(tr);
309
+ }
310
+
200
311
  return false;
201
312
  };
202
- cmd.displayName = 'runInputRules';
313
+ cmd.displayName = 'runInputRulesRange';
203
314
 
204
315
  return cmd;
205
316
  };
@@ -1,5 +1,12 @@
1
- import { canJoin, findWrapping } from 'prosemirror-transform';
2
- import { Attrs, Fragment, Node, NodeType, Slice } from 'prosemirror-model';
1
+ import { canJoin, findWrapping, replaceStep } from 'prosemirror-transform';
2
+ import {
3
+ Attrs,
4
+ Fragment,
5
+ Node,
6
+ NodeType,
7
+ ResolvedPos,
8
+ Slice,
9
+ } from 'prosemirror-model';
3
10
 
4
11
  import { InputRule } from './InputRulesPlugin.js';
5
12
 
@@ -24,9 +31,12 @@ export function wrappingInputRule(
24
31
  getAttrs: Attrs | null | ((matches: RegExpMatchArray) => Attrs | null) = null,
25
32
  joinPredicate?: (match: RegExpMatchArray, node: Node) => boolean,
26
33
  ) {
27
- return new InputRule(regexp, (state, match, start, end) => {
34
+ return new InputRule(regexp, (tr, state, match, start, end) => {
35
+ if (!tr) {
36
+ tr = state.tr;
37
+ }
28
38
  const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
29
- const tr = state.tr.delete(start, end);
39
+ tr = tr.delete(start, end);
30
40
  const $start = tr.doc.resolve(start);
31
41
  const range = $start.blockRange();
32
42
  const wrapping = range && findWrapping(range, nodeType, attrs);
@@ -54,7 +64,10 @@ export function textblockTypeInputRule(
54
64
  nodeType: NodeType,
55
65
  getAttrs: Attrs | null | ((match: RegExpMatchArray) => Attrs | null) = null,
56
66
  ) {
57
- return new InputRule(regexp, (state, match, start, end) => {
67
+ return new InputRule(regexp, (tr, state, match, start, end) => {
68
+ if (!tr) {
69
+ tr = state.tr;
70
+ }
58
71
  const $start = state.doc.resolve(start);
59
72
  const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
60
73
  if (
@@ -64,7 +77,7 @@ export function textblockTypeInputRule(
64
77
  nodeType,
65
78
  )
66
79
  ) return null;
67
- return state.tr
80
+ return tr
68
81
  .delete(start, end)
69
82
  .setBlockType(start, start, nodeType, attrs);
70
83
  });
@@ -75,14 +88,18 @@ export function replaceInlineNode(
75
88
  nodeType: NodeType,
76
89
  getAttrs: Attrs | null | ((matches: RegExpMatchArray) => Attrs | null) = null,
77
90
  ) {
78
- return new InputRule(regexp, (state, match, start, end) => {
91
+ return new InputRule(regexp, (tr, state, match, start, end) => {
92
+ if (!tr) {
93
+ tr = state.tr;
94
+ }
95
+
79
96
  const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
97
+
80
98
  const node = nodeType.createAndFill(attrs);
81
- const slice = new Slice(Fragment.from(node), 0, 0);
82
99
 
83
- const tr = state.tr;
84
100
  const from = tr.mapping.map(start);
85
101
  const to = tr.mapping.map(end);
86
- return tr.replace(from, to, slice);
102
+
103
+ return tr.replaceWith(from, to, Fragment.from(node));
87
104
  });
88
105
  }
package/src/types.ts CHANGED
@@ -3,6 +3,10 @@ import type { Extension } from './Extension.js';
3
3
  import type { Mark } from './Mark.js';
4
4
  import type { Node } from './Node.js';
5
5
 
6
+ export interface EditorKit {
7
+ getExtensions(): AnyExtensionOrReq[];
8
+ }
9
+
6
10
  export type AnyExtension = Extension | Node | Mark;
7
11
  export type AnyExtensionOrReq = AnyExtension | {
8
12
  requires: Array<AnyExtensionOrReq | string>;
@@ -10,18 +14,6 @@ export type AnyExtensionOrReq = AnyExtension | {
10
14
 
11
15
  export type Content = JSONContent | JSONContent[] | null;
12
16
 
13
- export interface EditorConfig {
14
- element: HTMLElement;
15
- content: Content;
16
- parseOptions: ParseOptions;
17
- extensions: AnyExtensionOrReq[];
18
- cdnUrl?: string;
19
- uri?: string;
20
- languageID?: string;
21
- topNode?: string;
22
- readOnly?: boolean;
23
- }
24
-
25
17
  export type JSONContent = {
26
18
  type?: string;
27
19
  attrs?: Record<string, any>;
@@ -19,9 +19,9 @@ export function createNodeFromObject(
19
19
  }
20
20
 
21
21
  return node;
22
- } catch (error) {
22
+ } catch (error: any) {
23
23
  if (options?.errorOnInvalidContent) {
24
- throw new Error('Invalid JSON content', {
24
+ throw new Error('Invalid JSON content: ' + error.message, {
25
25
  cause: error as Error,
26
26
  });
27
27
  }