@lobehub/editor 1.5.1 → 1.5.2

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.
@@ -37,6 +37,15 @@ export declare class Kernel extends EventEmitter implements IEditorKernel {
37
37
  getSelectionDocument(type: string): unknown | null;
38
38
  registerDecorator(name: string, decorator: (_node: DecoratorNode<any>, _editor: LexicalEditor) => any): this;
39
39
  getDecorator(name: string): ((_node: DecoratorNode<any>, _editor: LexicalEditor) => any) | undefined;
40
+ /**
41
+ * Unregister a decorator
42
+ * @param name Decorator name
43
+ */
44
+ unregisterDecorator(name: string): boolean;
45
+ /**
46
+ * Get all registered decorator names
47
+ */
48
+ getRegisteredDecorators(): string[];
40
49
  /**
41
50
  * Support registering target data source
42
51
  * @param dataSource Data source
@@ -25,6 +25,7 @@ import { get, merge, template, templateSettings } from 'lodash-es';
25
25
  import defaultLocale from "../locale";
26
26
  import { $isRootTextContentEmpty } from "../plugins/common/utils";
27
27
  import { registerEvent } from "./event";
28
+ import { KernelPlugin } from "./plugin";
28
29
  import { createEmptyEditorState } from "./utils";
29
30
  templateSettings.interpolate = /{{([\S\s]+?)}}/g;
30
31
  export var Kernel = /*#__PURE__*/function (_EventEmitter) {
@@ -118,6 +119,9 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
118
119
  this.pluginsInstances = [];
119
120
  // Clear services to support hot reload
120
121
  this.serviceMap.clear();
122
+ // Clear decorators to prevent memory leaks
123
+ this.decorators = {};
124
+ console.debug('[Editor] Cleared all decorators during destroy');
121
125
  }
122
126
  }, {
123
127
  key: "getRootElement",
@@ -224,7 +228,28 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
224
228
  }, {
225
229
  key: "registerDecorator",
226
230
  value: function registerDecorator(name, decorator) {
231
+ if (this.decorators[name]) {
232
+ if (this.hotReloadMode) {
233
+ // In hot reload mode, allow decorator override with warning
234
+ console.warn("[Hot Reload] Overriding decorator \"".concat(name, "\""));
235
+ this.decorators[name] = decorator;
236
+ return this;
237
+ } else {
238
+ // Check if it's the same decorator function
239
+ var existingDecorator = this.decorators[name];
240
+ if (existingDecorator === decorator) {
241
+ // Same decorator function, no need to re-register
242
+ console.warn("[Editor] Decorator \"".concat(name, "\" is already registered with the same function"));
243
+ return this;
244
+ }
245
+
246
+ // Different decorator function in production mode
247
+ console.error("[Editor] Attempting to register duplicate decorator \"".concat(name, "\". Enable hot reload mode if this is intended."));
248
+ throw new Error("Decorator with name \"".concat(name, "\" is already registered."));
249
+ }
250
+ }
227
251
  this.decorators[name] = decorator;
252
+ console.debug("[Editor] Registered decorator: ".concat(name));
228
253
  return this;
229
254
  }
230
255
  }, {
@@ -233,6 +258,31 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
233
258
  return this.decorators[name];
234
259
  }
235
260
 
261
+ /**
262
+ * Unregister a decorator
263
+ * @param name Decorator name
264
+ */
265
+ }, {
266
+ key: "unregisterDecorator",
267
+ value: function unregisterDecorator(name) {
268
+ if (this.decorators[name]) {
269
+ delete this.decorators[name];
270
+ console.debug("[Editor] Unregistered decorator: ".concat(name));
271
+ return true;
272
+ }
273
+ console.warn("[Editor] Decorator \"".concat(name, "\" not found for unregistration"));
274
+ return false;
275
+ }
276
+
277
+ /**
278
+ * Get all registered decorator names
279
+ */
280
+ }, {
281
+ key: "getRegisteredDecorators",
282
+ value: function getRegisteredDecorators() {
283
+ return Object.keys(this.decorators);
284
+ }
285
+
236
286
  /**
237
287
  * Support registering target data source
238
288
  * @param dataSource Data source
@@ -270,6 +320,23 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
270
320
  });
271
321
  if (instanceIndex !== -1) {
272
322
  var oldInstance = this.pluginsInstances[instanceIndex];
323
+ // Clean up decorators registered by the old plugin instance
324
+ if (oldInstance instanceof KernelPlugin) {
325
+ var decoratorNames = oldInstance.getRegisteredDecorators();
326
+ var _iterator2 = _createForOfIteratorHelper(decoratorNames),
327
+ _step2;
328
+ try {
329
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
330
+ var decoratorName = _step2.value;
331
+ this.unregisterDecorator(decoratorName);
332
+ console.debug("[Hot Reload] Cleaned up decorator \"".concat(decoratorName, "\" from old plugin instance"));
333
+ }
334
+ } catch (err) {
335
+ _iterator2.e(err);
336
+ } finally {
337
+ _iterator2.f();
338
+ }
339
+ }
273
340
  if (oldInstance.destroy) {
274
341
  oldInstance.destroy();
275
342
  }
@@ -297,11 +364,11 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
297
364
  }, {
298
365
  key: "registerPlugins",
299
366
  value: function registerPlugins(plugins) {
300
- var _iterator2 = _createForOfIteratorHelper(plugins),
301
- _step2;
367
+ var _iterator3 = _createForOfIteratorHelper(plugins),
368
+ _step3;
302
369
  try {
303
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
304
- var plugin = _step2.value;
370
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
371
+ var plugin = _step3.value;
305
372
  if (Array.isArray(plugin)) {
306
373
  this.registerPlugin(plugin[0], plugin[1]);
307
374
  } else {
@@ -309,9 +376,9 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
309
376
  }
310
377
  }
311
378
  } catch (err) {
312
- _iterator2.e(err);
379
+ _iterator3.e(err);
313
380
  } finally {
314
- _iterator2.f();
381
+ _iterator3.f();
315
382
  }
316
383
  return this;
317
384
  }
@@ -471,19 +538,19 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
471
538
  var listenerInPriorityOrder = _this4._commands.get(command);
472
539
  if (listenerInPriorityOrder !== undefined) {
473
540
  var listenersSet = listenerInPriorityOrder[i];
474
- var _iterator3 = _createForOfIteratorHelper(listenersSet),
475
- _step3;
541
+ var _iterator4 = _createForOfIteratorHelper(listenersSet),
542
+ _step4;
476
543
  try {
477
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
478
- var _listener = _step3.value;
544
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
545
+ var _listener = _step4.value;
479
546
  if (_listener(payload, lexicalEditor)) {
480
547
  return true;
481
548
  }
482
549
  }
483
550
  } catch (err) {
484
- _iterator3.e(err);
551
+ _iterator4.e(err);
485
552
  } finally {
486
- _iterator3.f();
553
+ _iterator4.f();
487
554
  }
488
555
  }
489
556
  }
@@ -1,7 +1,21 @@
1
1
  import EventEmitter from 'eventemitter3';
2
+ import type { IEditorKernel } from "../types/kernel";
2
3
  export declare abstract class KernelPlugin extends EventEmitter {
3
4
  protected clears: Array<() => void>;
5
+ protected registeredDecorators: Set<string>;
4
6
  protected register(clear: () => void): void;
5
7
  protected registerClears(...clears: Array<() => void>): void;
8
+ /**
9
+ * Register a decorator and track it for cleanup
10
+ */
11
+ protected registerDecorator(kernel: IEditorKernel, name: string, decorator: (node: any, editor: any) => any): void;
12
+ /**
13
+ * Unregister a specific decorator
14
+ */
15
+ protected unregisterDecorator(kernel: IEditorKernel, name: string): boolean;
16
+ /**
17
+ * Get all decorator names registered by this plugin
18
+ */
19
+ getRegisteredDecorators(): string[];
6
20
  destroy(): void;
7
21
  }
@@ -24,6 +24,7 @@ export var KernelPlugin = /*#__PURE__*/function (_EventEmitter) {
24
24
  }
25
25
  _this = _super.call.apply(_super, [this].concat(args));
26
26
  _defineProperty(_assertThisInitialized(_this), "clears", []);
27
+ _defineProperty(_assertThisInitialized(_this), "registeredDecorators", new Set());
27
28
  return _this;
28
29
  }
29
30
  _createClass(KernelPlugin, [{
@@ -42,12 +43,47 @@ export var KernelPlugin = /*#__PURE__*/function (_EventEmitter) {
42
43
  return _this2.register(clear);
43
44
  });
44
45
  }
46
+
47
+ /**
48
+ * Register a decorator and track it for cleanup
49
+ */
50
+ }, {
51
+ key: "registerDecorator",
52
+ value: function registerDecorator(kernel, name, decorator) {
53
+ kernel.registerDecorator(name, decorator);
54
+ this.registeredDecorators.add(name);
55
+ }
56
+
57
+ /**
58
+ * Unregister a specific decorator
59
+ */
60
+ }, {
61
+ key: "unregisterDecorator",
62
+ value: function unregisterDecorator(kernel, name) {
63
+ var result = kernel.unregisterDecorator(name);
64
+ if (result) {
65
+ this.registeredDecorators.delete(name);
66
+ }
67
+ return result;
68
+ }
69
+
70
+ /**
71
+ * Get all decorator names registered by this plugin
72
+ */
73
+ }, {
74
+ key: "getRegisteredDecorators",
75
+ value: function getRegisteredDecorators() {
76
+ return Array.from(this.registeredDecorators);
77
+ }
45
78
  }, {
46
79
  key: "destroy",
47
80
  value: function destroy() {
48
81
  this.clears.forEach(function (clear) {
49
82
  return clear();
50
83
  });
84
+ // Note: Decorators will be cleaned up when kernel.destroy() is called
85
+ // Individual decorator cleanup should be handled by the kernel itself
86
+ this.registeredDecorators.clear();
51
87
  }
52
88
  }]);
53
89
  return KernelPlugin;
@@ -42,7 +42,7 @@ export var FilePlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
42
42
  if (config !== null && config !== void 0 && config.theme) {
43
43
  kernel.registerThemes(config === null || config === void 0 ? void 0 : config.theme);
44
44
  }
45
- kernel.registerDecorator(FileNode.getType(), function (node, editor) {
45
+ _this.registerDecorator(kernel, FileNode.getType(), function (node, editor) {
46
46
  return config !== null && config !== void 0 && config.decorator ? config.decorator(node, editor) : null;
47
47
  });
48
48
  (_kernel$requireServic = kernel.requireService(IMarkdownShortCutService)) === null || _kernel$requireServic === void 0 || _kernel$requireServic.registerMarkdownWriter(FileNode.getType(), function (ctx, node) {
@@ -34,7 +34,7 @@ export var HRPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
34
34
  kernel.registerThemes({
35
35
  hr: (config === null || config === void 0 ? void 0 : config.theme) || ''
36
36
  });
37
- kernel.registerDecorator('horizontalrule', function (node, editor) {
37
+ _this.registerDecorator(kernel, 'horizontalrule', function (node, editor) {
38
38
  return config !== null && config !== void 0 && config.decorator ? config.decorator(node, editor) : null;
39
39
  });
40
40
  (_kernel$requireServic = kernel.requireService(IMarkdownShortCutService)) === null || _kernel$requireServic === void 0 || _kernel$requireServic.registerMarkdownShortCut({
@@ -41,10 +41,10 @@ export var MathPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
41
41
  if (config !== null && config !== void 0 && config.theme) {
42
42
  kernel.registerThemes(config === null || config === void 0 ? void 0 : config.theme);
43
43
  }
44
- kernel.registerDecorator(MathInlineNode.getType(), function (node, editor) {
44
+ _this.registerDecorator(kernel, MathInlineNode.getType(), function (node, editor) {
45
45
  return config !== null && config !== void 0 && config.decorator ? config.decorator(node, editor) : null;
46
46
  });
47
- kernel.registerDecorator(MathBlockNode.getType(), function (node, editor) {
47
+ _this.registerDecorator(kernel, MathBlockNode.getType(), function (node, editor) {
48
48
  return config !== null && config !== void 0 && config.decorator ? config.decorator(node, editor) : null;
49
49
  });
50
50
  (_kernel$requireServic = kernel.requireService(IMarkdownShortCutService)) === null || _kernel$requireServic === void 0 || _kernel$requireServic.registerMarkdownShortCut({
@@ -36,7 +36,7 @@ export var MentionPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
36
36
  if (config !== null && config !== void 0 && config.theme) {
37
37
  kernel.registerThemes(config === null || config === void 0 ? void 0 : config.theme);
38
38
  }
39
- kernel.registerDecorator(MentionNode.getType(), function (node, editor) {
39
+ _this.registerDecorator(kernel, MentionNode.getType(), function (node, editor) {
40
40
  return config !== null && config !== void 0 && config.decorator ? config.decorator(node, editor) : null;
41
41
  });
42
42
  (_kernel$requireServic = kernel.requireService(IMarkdownShortCutService)) === null || _kernel$requireServic === void 0 || _kernel$requireServic.registerMarkdownWriter(MentionNode.getType(), function (ctx, node) {
@@ -171,6 +171,10 @@ export interface IEditorKernel extends IEditor {
171
171
  * @param name
172
172
  */
173
173
  getDecorator(name: string): ((_node: DecoratorNode<any>, _editor: LexicalEditor) => any) | undefined;
174
+ /**
175
+ * Get all registered decorator names
176
+ */
177
+ getRegisteredDecorators(): string[];
174
178
  /**
175
179
  * Check if hot reload mode is enabled
176
180
  */
@@ -213,6 +217,11 @@ export interface IEditorKernel extends IEditor {
213
217
  * @param enabled Whether to enable hot reload mode
214
218
  */
215
219
  setHotReloadMode(enabled: boolean): void;
220
+ /**
221
+ * Unregister editor node decorator
222
+ * @param name Decorator name
223
+ */
224
+ unregisterDecorator(name: string): boolean;
216
225
  }
217
226
  /**
218
227
  * Plugin interface
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",