@lobehub/editor 1.22.1 → 1.23.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.
@@ -36,6 +36,7 @@ export declare class Kernel extends EventEmitter implements IEditorKernel {
36
36
  destroy(): void;
37
37
  getRootElement(): HTMLElement | null;
38
38
  setRootElement(dom: HTMLElement): LexicalEditor;
39
+ initNodeEditor(): LexicalEditor;
39
40
  setDocument(type: string, content: any): void;
40
41
  focus(): void;
41
42
  blur(): void;
@@ -191,6 +191,50 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
191
191
  this.emit('initialized', editor);
192
192
  return this.editor;
193
193
  }
194
+ }, {
195
+ key: "initNodeEditor",
196
+ value: function initNodeEditor() {
197
+ var _this3 = this;
198
+ if (this.editor) {
199
+ return this.editor;
200
+ }
201
+ // Initialize plugins if not already done
202
+ if (this.pluginsInstances.length === 0) {
203
+ this.logger.info("\uD83D\uDD0C Initializing ".concat(this.plugins.length, " plugins"));
204
+ var _iterator2 = _createForOfIteratorHelper(this.plugins),
205
+ _step2;
206
+ try {
207
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
208
+ var plugin = _step2.value;
209
+ var instance = new plugin(this, plugin.__config);
210
+ this.pluginsInstances.push(instance);
211
+ }
212
+ } catch (err) {
213
+ _iterator2.e(err);
214
+ } finally {
215
+ _iterator2.f();
216
+ }
217
+ }
218
+ this.logger.info("\uD83D\uDCDD Creating editor with ".concat(this.nodes.length, " nodes"));
219
+ var editor = this.editor = createEditor({
220
+ // @ts-expect-error Inject into lexical editor instance
221
+ __kernel: this,
222
+ namespace: 'lobehub',
223
+ nodes: this.nodes,
224
+ onError: function onError(error) {
225
+ _this3.logger.error('❌ Lexical editor error:', error);
226
+ _this3.emit('error', error);
227
+ },
228
+ theme: this.themes
229
+ });
230
+ this.pluginsInstances.forEach(function (plugin) {
231
+ var _plugin$onInit2;
232
+ (_plugin$onInit2 = plugin.onInit) === null || _plugin$onInit2 === void 0 || _plugin$onInit2.call(plugin, editor);
233
+ });
234
+ this.logger.info("\u2705 Editor ready with ".concat(this.pluginsInstances.length, " plugins"));
235
+ this.emit('initialized', editor);
236
+ return editor || null;
237
+ }
194
238
  }, {
195
239
  key: "setDocument",
196
240
  value: function setDocument(type, content) {
@@ -346,18 +390,18 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
346
390
  // Clean up decorators registered by the old plugin instance
347
391
  if (oldInstance instanceof KernelPlugin) {
348
392
  var decoratorNames = oldInstance.getRegisteredDecorators();
349
- var _iterator2 = _createForOfIteratorHelper(decoratorNames),
350
- _step2;
393
+ var _iterator3 = _createForOfIteratorHelper(decoratorNames),
394
+ _step3;
351
395
  try {
352
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
353
- var decoratorName = _step2.value;
396
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
397
+ var decoratorName = _step3.value;
354
398
  this.unregisterDecorator(decoratorName);
355
399
  this.logger.debug("\uD83E\uDDE8 Cleanup: decorator \"".concat(decoratorName, "\""));
356
400
  }
357
401
  } catch (err) {
358
- _iterator2.e(err);
402
+ _iterator3.e(err);
359
403
  } finally {
360
- _iterator2.f();
404
+ _iterator3.f();
361
405
  }
362
406
  }
363
407
  if (oldInstance.destroy) {
@@ -388,11 +432,11 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
388
432
  }, {
389
433
  key: "registerPlugins",
390
434
  value: function registerPlugins(plugins) {
391
- var _iterator3 = _createForOfIteratorHelper(plugins),
392
- _step3;
435
+ var _iterator4 = _createForOfIteratorHelper(plugins),
436
+ _step4;
393
437
  try {
394
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
395
- var plugin = _step3.value;
438
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
439
+ var plugin = _step4.value;
396
440
  if (Array.isArray(plugin)) {
397
441
  this.registerPlugin(plugin[0], plugin[1]);
398
442
  } else {
@@ -400,9 +444,9 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
400
444
  }
401
445
  }
402
446
  } catch (err) {
403
- _iterator3.e(err);
447
+ _iterator4.e(err);
404
448
  } finally {
405
- _iterator3.f();
449
+ _iterator4.f();
406
450
  }
407
451
  this.logger.debug("\uD83D\uDD0C Registered ".concat(plugins.length, " plugins"));
408
452
  return this;
@@ -539,12 +583,12 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
539
583
  }, {
540
584
  key: "isEmpty",
541
585
  get: function get() {
542
- var _this3 = this;
586
+ var _this4 = this;
543
587
  if (!this.editor) {
544
588
  return true;
545
589
  }
546
590
  return this.editor.getEditorState().read(function () {
547
- return $isRootTextContentEmpty(_this3.editor.isComposing(), true);
591
+ return $isRootTextContentEmpty(_this4.editor.isComposing(), true);
548
592
  });
549
593
  }
550
594
  }, {
@@ -587,7 +631,7 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
587
631
  }, {
588
632
  key: "registerHighCommand",
589
633
  value: function registerHighCommand(command, listener, priority) {
590
- var _this4 = this;
634
+ var _this5 = this;
591
635
  var lexicalEditor = this.editor;
592
636
  if (!lexicalEditor) {
593
637
  throw new Error('Editor is not initialized.');
@@ -598,22 +642,22 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
598
642
  commandsMap.set(command, [new Set(), new Set(), new Set(), new Set(), new Set()]);
599
643
  this._commandsClean.set(command, lexicalEditor.registerCommand(command, function (payload) {
600
644
  for (var i = 4; i >= 0; i--) {
601
- var listenerInPriorityOrder = _this4._commands.get(command);
645
+ var listenerInPriorityOrder = _this5._commands.get(command);
602
646
  if (listenerInPriorityOrder !== undefined) {
603
647
  var listenersSet = listenerInPriorityOrder[i];
604
- var _iterator4 = _createForOfIteratorHelper(listenersSet),
605
- _step4;
648
+ var _iterator5 = _createForOfIteratorHelper(listenersSet),
649
+ _step5;
606
650
  try {
607
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
608
- var _listener = _step4.value;
651
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
652
+ var _listener = _step5.value;
609
653
  if (_listener(payload, lexicalEditor)) {
610
654
  return true;
611
655
  }
612
656
  }
613
657
  } catch (err) {
614
- _iterator4.e(err);
658
+ _iterator5.e(err);
615
659
  } finally {
616
- _iterator4.f();
660
+ _iterator5.f();
617
661
  }
618
662
  }
619
663
  }
@@ -635,9 +679,9 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
635
679
  if (listenersInPriorityOrder.every(function (listenersSet) {
636
680
  return listenersSet.size === 0;
637
681
  })) {
638
- var _this4$_commandsClean;
682
+ var _this5$_commandsClean;
639
683
  commandsMap.delete(command);
640
- (_this4$_commandsClean = _this4._commandsClean.get(command)) === null || _this4$_commandsClean === void 0 || _this4$_commandsClean();
684
+ (_this5$_commandsClean = _this5._commandsClean.get(command)) === null || _this5$_commandsClean === void 0 || _this5$_commandsClean();
641
685
  }
642
686
  };
643
687
  }
@@ -24,6 +24,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
24
24
  import { registerDragonSupport } from '@lexical/dragon';
25
25
  import { registerHistory } from '@lexical/history';
26
26
  import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode, HeadingNode, QuoteNode, registerRichText } from '@lexical/rich-text';
27
+ import { CAN_USE_DOM } from '@lexical/utils';
27
28
  import { $createLineBreakNode, $createParagraphNode, $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_HIGH, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND } from 'lexical';
28
29
  import { KernelPlugin } from "../../../editor-kernel/plugin";
29
30
  import { IMarkdownShortCutService } from "../../markdown/service/shortcut";
@@ -322,7 +323,7 @@ export var CommonPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
322
323
  key: "onInit",
323
324
  value: function onInit(editor) {
324
325
  var _this$config2;
325
- this.registerClears(registerRichText(editor), registerDragonSupport(editor), registerHistory(editor, this.kernel.getHistoryState(), 300), registerHeaderBackspace(editor), registerRichKeydown(editor, this.kernel, {
326
+ this.registerClears(registerRichText(editor), CAN_USE_DOM ? registerDragonSupport(editor) : function () {}, registerHistory(editor, this.kernel.getHistoryState(), 300), registerHeaderBackspace(editor), registerRichKeydown(editor, this.kernel, {
326
327
  enableHotkey: (_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : _this$config2.enableHotkey
327
328
  }), registerCommands(editor), registerBreakLineClick(editor), registerCursorNode(editor), registerLastElement(editor),
328
329
  // Convert soft line breaks (Shift+Enter) to hard line breaks (paragraph breaks)
@@ -1,6 +1,7 @@
1
1
  import { type CSSProperties, type ReactNode } from 'react';
2
2
  export interface PlaceholderProps {
3
3
  children: ReactNode;
4
+ lineEmptyPlaceholder?: string;
4
5
  style?: CSSProperties;
5
6
  }
6
7
  declare const Placeholder: import("react").NamedExoticComponent<PlaceholderProps>;
@@ -5,7 +5,8 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
7
  import { mergeRegister } from '@lexical/utils';
8
- import { memo, useState } from 'react';
8
+ import { $getNodeByKey, $getSelection, $isBlockElementNode, $isRangeSelection } from 'lexical';
9
+ import { memo, useRef, useState } from 'react';
9
10
  import { useLexicalEditor } from "../../../../editor-kernel/react";
10
11
  import { $canShowPlaceholderCurry } from "../../utils";
11
12
  import { useStyles } from "./style";
@@ -14,9 +15,17 @@ function canShowPlaceholderFromCurrentEditorState(editor) {
14
15
  var currentCanShowPlaceholder = editor.getEditorState().read($canShowPlaceholderCurry(editor.isComposing()));
15
16
  return currentCanShowPlaceholder;
16
17
  }
18
+
19
+ // 判断 DOM 是否只有一个 br 子元素
20
+ function hasOnlyBrChild(element) {
21
+ var children = element.childNodes;
22
+ return children.length === 1 && children[0].nodeType === Node.ELEMENT_NODE && children[0].tagName.toLowerCase() === 'br';
23
+ }
17
24
  var Placeholder = /*#__PURE__*/memo(function (_ref) {
18
25
  var children = _ref.children,
19
- style = _ref.style;
26
+ style = _ref.style,
27
+ lineEmptyPlaceholder = _ref.lineEmptyPlaceholder;
28
+ var currentPlaceHolderRef = useRef(null);
20
29
  var _useState = useState(function () {
21
30
  return false;
22
31
  }),
@@ -32,10 +41,38 @@ var Placeholder = /*#__PURE__*/memo(function (_ref) {
32
41
  function resetCanShowPlaceholder() {
33
42
  var currentCanShowPlaceholder = canShowPlaceholderFromCurrentEditorState(editor);
34
43
  setCanShowPlaceholder(currentCanShowPlaceholder);
44
+ return currentCanShowPlaceholder;
35
45
  }
36
46
  resetCanShowPlaceholder();
37
47
  return mergeRegister(editor.registerUpdateListener(function () {
38
- resetCanShowPlaceholder();
48
+ var show = resetCanShowPlaceholder();
49
+ if (!show && lineEmptyPlaceholder) {
50
+ editor.read(function () {
51
+ var sel = $getSelection();
52
+ if ($isRangeSelection(sel) && sel.isCollapsed()) {
53
+ var anchor = sel.anchor;
54
+ var node = $getNodeByKey(anchor.key);
55
+ while (node && !$isBlockElementNode(node)) {
56
+ node = node.getParent();
57
+ }
58
+ if (node) {
59
+ var dom = editor.getElementByKey(node.getKey());
60
+ if (dom && hasOnlyBrChild(dom)) {
61
+ if (currentPlaceHolderRef.current && currentPlaceHolderRef.current !== dom) {
62
+ currentPlaceHolderRef.current.dataset.placeholder = '';
63
+ }
64
+ currentPlaceHolderRef.current = dom;
65
+ dom.dataset.placeholder = lineEmptyPlaceholder;
66
+ return;
67
+ }
68
+ }
69
+ if (currentPlaceHolderRef.current) {
70
+ currentPlaceHolderRef.current.dataset.placeholder = '';
71
+ currentPlaceHolderRef.current = null;
72
+ }
73
+ }
74
+ });
75
+ }
39
76
  }), editor.registerEditableListener(function () {
40
77
  resetCanShowPlaceholder();
41
78
  }));
@@ -82,7 +82,8 @@ var ReactPlainText = /*#__PURE__*/memo(function (_ref) {
82
82
  _Children$only$props = _Children$only.props,
83
83
  type = _Children$only$props.type,
84
84
  content = _Children$only$props.content,
85
- placeholder = _Children$only$props.placeholder;
85
+ placeholder = _Children$only$props.placeholder,
86
+ lineEmptyPlaceholder = _Children$only$props.lineEmptyPlaceholder;
86
87
  useLayoutEffect(function () {
87
88
  editor.registerPlugin(MarkdownPlugin, {
88
89
  enablePasteMarkdown: enablePasteMarkdown
@@ -202,6 +203,7 @@ var ReactPlainText = /*#__PURE__*/memo(function (_ref) {
202
203
  outline: 'none'
203
204
  }
204
205
  }), /*#__PURE__*/_jsx(Placeholder, {
206
+ lineEmptyPlaceholder: lineEmptyPlaceholder,
205
207
  style: style,
206
208
  children: placeholder
207
209
  }), decorators]
@@ -28,7 +28,7 @@ export var useStyles = createStyles(function (_ref2, _ref3) {
28
28
  marginMultiple = _ref3$marginMultiple === void 0 ? 2 : _ref3$marginMultiple,
29
29
  _ref3$lineHeight = _ref3.lineHeight,
30
30
  lineHeight = _ref3$lineHeight === void 0 ? 1.8 : _ref3$lineHeight;
31
- var __root = css(_templateObject11 || (_templateObject11 = _taggedTemplateLiteral(["\n --lobe-markdown-font-size: ", "px;\n --lobe-markdown-header-multiple: ", ";\n --lobe-markdown-margin-multiple: ", ";\n --lobe-markdown-line-height: ", ";\n --lobe-markdown-border-radius: ", ";\n --lobe-markdown-border-color: ", ";\n\n position: relative;\n\n display: flex;\n flex-direction: column;\n\n width: 100%;\n max-width: 100%;\n height: 100%;\n\n font-size: var(--lobe-markdown-font-size);\n line-height: var(--lobe-markdown-line-height);\n word-break: break-word;\n\n @keyframes cursor-blink {\n to {\n visibility: hidden;\n }\n }\n\n [data-lexical-cursor='true'] {\n pointer-events: none;\n position: absolute;\n display: block;\n\n &::after {\n content: '';\n\n position: absolute;\n inset-block-start: -2px;\n\n display: block;\n\n width: 20px;\n border-block-start: 1px solid ", ";\n\n animation: cursor-blink 1.1s steps(2, start) infinite;\n }\n }\n "])), fontSize, headerMultiple, marginMultiple, lineHeight, token.borderRadiusLG, token.colorFillQuaternary, token.colorText);
31
+ var __root = css(_templateObject11 || (_templateObject11 = _taggedTemplateLiteral(["\n --lobe-markdown-font-size: ", "px;\n --lobe-markdown-header-multiple: ", ";\n --lobe-markdown-margin-multiple: ", ";\n --lobe-markdown-line-height: ", ";\n --lobe-markdown-border-radius: ", ";\n --lobe-markdown-border-color: ", ";\n\n position: relative;\n\n display: flex;\n flex-direction: column;\n\n width: 100%;\n max-width: 100%;\n height: 100%;\n\n font-size: var(--lobe-markdown-font-size);\n line-height: var(--lobe-markdown-line-height);\n word-break: break-word;\n\n @keyframes cursor-blink {\n to {\n visibility: hidden;\n }\n }\n\n [data-placeholder] {\n position: relative;\n }\n\n [data-placeholder]::after {\n pointer-events: none;\n content: attr(data-placeholder);\n user-select: none;\n user-select: none;\n user-select: none;\n\n position: absolute;\n inset-block-start: 50%;\n transform: translateY(-50%);\n\n padding-inline-start: 2px;\n\n color: ", ";\n white-space: nowrap;\n }\n\n [data-lexical-cursor='true'] {\n pointer-events: none;\n position: absolute;\n display: block;\n\n &::after {\n content: '';\n\n position: absolute;\n inset-block-start: -2px;\n\n display: block;\n\n width: 20px;\n border-block-start: 1px solid ", ";\n\n animation: cursor-blink 1.1s steps(2, start) infinite;\n }\n }\n "])), fontSize, headerMultiple, marginMultiple, lineHeight, token.borderRadiusLG, token.colorFillQuaternary, token.colorTextDescription, token.colorText);
32
32
  var header = css(_templateObject12 || (_templateObject12 = _taggedTemplateLiteral(["\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n margin-block: max(\n calc(var(--lobe-markdown-header-multiple) * var(--lobe-markdown-margin-multiple) * 0.4em),\n var(--lobe-markdown-font-size)\n );\n font-weight: bold;\n line-height: 1.25;\n }\n\n h1 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 1.5 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h2 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + var(--lobe-markdown-header-multiple))\n );\n }\n\n h3 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 0.5 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h4 {\n font-size: calc(\n var(--lobe-markdown-font-size) * (1 + 0.25 * var(--lobe-markdown-header-multiple))\n );\n }\n\n h5,\n h6 {\n font-size: calc(var(--lobe-markdown-font-size) * 1);\n }\n "])));
33
33
  var p = css(_templateObject13 || (_templateObject13 = _taggedTemplateLiteral(["\n p {\n margin-block: 4px;\n line-height: var(--lobe-markdown-line-height);\n letter-spacing: 0.02em;\n\n &:not(:first-child) {\n margin-block-start: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n }\n\n &:not(:last-child) {\n margin-block-end: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n }\n }\n "])));
34
34
  var blockquote = css(_templateObject14 || (_templateObject14 = _taggedTemplateLiteral(["\n .editor_quote {\n margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);\n margin-inline: 0;\n padding-block: 0;\n padding-inline: 1em;\n border-inline-start: solid 4px ", ";\n\n color: ", ";\n }\n "])), token.colorBorder, token.colorTextSecondary);
@@ -3,6 +3,7 @@ import type { CommonPluginOptions } from "..";
3
3
  import type { IEditor } from "../../../types";
4
4
  export interface ReactEditorContentProps {
5
5
  content: any;
6
+ lineEmptyPlaceholder?: string;
6
7
  placeholder?: ReactNode;
7
8
  type: string;
8
9
  }
@@ -32,6 +32,7 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
32
32
  onInit = _ref.onInit,
33
33
  onChange = _ref.onChange,
34
34
  placeholder = _ref.placeholder,
35
+ lineEmptyPlaceholder = _ref.lineEmptyPlaceholder,
35
36
  _ref$plugins = _ref.plugins,
36
37
  plugins = _ref$plugins === void 0 ? [] : _ref$plugins,
37
38
  _ref$slashOption = _ref.slashOption,
@@ -113,6 +114,7 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
113
114
  variant: variant,
114
115
  children: /*#__PURE__*/_jsx(ReactEditorContent, {
115
116
  content: content,
117
+ lineEmptyPlaceholder: lineEmptyPlaceholder,
116
118
  placeholder: placeholder,
117
119
  type: type
118
120
  })
@@ -80,6 +80,10 @@ export interface IEditor {
80
80
  * Get editor theme
81
81
  */
82
82
  getTheme(): Record<string, string | Record<string, string>>;
83
+ /**
84
+ * Get node editor instance
85
+ */
86
+ initNodeEditor(): LexicalEditor | null;
83
87
  /**
84
88
  * Check if editor content is empty
85
89
  * @returns true if editor content is empty, false otherwise
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
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",