@lobehub/editor 4.23.0 → 4.23.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.
Files changed (52) hide show
  1. package/es/editor-kernel/index.js +1 -0
  2. package/es/editor-kernel/kernel.js +47 -40
  3. package/es/editor-kernel/react/useLexicalNodeSelection.js +4 -2
  4. package/es/headless.js +201 -175
  5. package/es/index.js +5 -2
  6. package/es/plugins/auto-complete/plugin/index.js +4 -2
  7. package/es/plugins/block/react/ReactBlockPlugin.js +5 -4
  8. package/es/plugins/codeblock/command/index.js +8 -6
  9. package/es/plugins/codeblock/plugin/index.js +4 -2
  10. package/es/plugins/codeblock/react/ReactCodeblockPlugin.js +2 -1
  11. package/es/plugins/codemirror-block/command/index.js +8 -6
  12. package/es/plugins/common/data-source/json-data-source.js +5 -4
  13. package/es/plugins/common/plugin/index.js +1 -3
  14. package/es/plugins/common/plugin/paste-handler.js +2 -1
  15. package/es/plugins/hr/command/index.js +2 -1
  16. package/es/plugins/image/react/components/BrokenImage.js +2 -1
  17. package/es/plugins/image/react/components/Image.js +18 -15
  18. package/es/plugins/image/react/components/ImageEditPopover.js +42 -41
  19. package/es/plugins/image/react/components/ResizeHandle.js +1 -3
  20. package/es/plugins/link/node/LinkNode.js +4 -2
  21. package/es/plugins/link/plugin/registry.js +17 -12
  22. package/es/plugins/link/react/components/LinkEdit.js +9 -8
  23. package/es/plugins/list/plugin/index.js +1 -4
  24. package/es/plugins/list/utils/index.js +4 -2
  25. package/es/plugins/litexml/command/index.js +69 -62
  26. package/es/plugins/litexml/node/DiffNode.js +14 -10
  27. package/es/plugins/litexml/plugin/index.js +6 -12
  28. package/es/plugins/markdown/data-source/markdown-data-source.js +5 -4
  29. package/es/plugins/markdown/plugin/index.js +2 -1
  30. package/es/plugins/markdown/service/transformers.js +4 -2
  31. package/es/plugins/markdown/utils/index.js +10 -8
  32. package/es/plugins/math/command/index.js +4 -2
  33. package/es/plugins/math/react/components/MathInline.js +14 -13
  34. package/es/plugins/math/react/style.js +2 -2
  35. package/es/plugins/slash/plugin/index.js +13 -4
  36. package/es/plugins/slash/react/components/DefaultSlashMenu.js +2 -1
  37. package/es/plugins/slash/react/components/menuLoading.js +4 -0
  38. package/es/plugins/slash/service/i-slash-service.d.ts +6 -1
  39. package/es/plugins/slash/service/i-slash-service.js +16 -2
  40. package/es/plugins/slash/utils/utils.js +1 -1
  41. package/es/plugins/table/react/TableActionMenu/ActionMenu.js +69 -67
  42. package/es/plugins/table/react/TableColController.js +29 -26
  43. package/es/plugins/table/react/TableHoverActions/index.js +0 -2
  44. package/es/plugins/table/react/TableRowController.js +29 -26
  45. package/es/plugins/table/utils/index.js +4 -2
  46. package/es/react/Editor/Editor.js +24 -23
  47. package/es/react/hooks/useEditorState/index.js +28 -21
  48. package/es/react/hooks/useEditorState/utils.js +2 -1
  49. package/es/renderer/engine/render-builtin-node.js +26 -18
  50. package/es/renderer/engine/render-tree.js +4 -1
  51. package/es/renderer/renderers/mermaid.js +2 -1
  52. package/package.json +1 -1
@@ -15,12 +15,14 @@ function registerCodeCommand(editor) {
15
15
  CustomShikiTokenizer.defaultLanguage = payload.lang;
16
16
  const codeNode = editor.getEditorState().read(() => {
17
17
  const selection = $getSelection();
18
- if ($isRangeSelection(selection)) if (selection.isCollapsed()) return $findMatchingParent(selection.anchor.getNode(), $isCodeNode);
19
- else {
20
- const anchor = $findMatchingParent(selection.anchor.getNode(), $isCodeNode);
21
- const focus = $findMatchingParent(selection.focus.getNode(), $isCodeNode);
22
- if (anchor && focus && anchor === focus) return anchor;
23
- return null;
18
+ if ($isRangeSelection(selection)) {
19
+ if (selection.isCollapsed()) return $findMatchingParent(selection.anchor.getNode(), $isCodeNode);
20
+ else {
21
+ const anchor = $findMatchingParent(selection.anchor.getNode(), $isCodeNode);
22
+ const focus = $findMatchingParent(selection.focus.getNode(), $isCodeNode);
23
+ if (anchor && focus && anchor === focus) return anchor;
24
+ return null;
25
+ }
24
26
  }
25
27
  return false;
26
28
  });
@@ -48,8 +48,10 @@ const CodeblockPlugin = class extends KernelPlugin {
48
48
  if (this.config?.shikiTheme) CustomShikiTokenizer.defaultTheme = this.config?.shikiTheme;
49
49
  }
50
50
  onInit(editor) {
51
- if (!isHeadlessEditor(editor)) if (this.config?.shikiTheme) this.register(registerCodeHighlighting(editor, CustomShikiTokenizer));
52
- else this.register(registerCodeHighlighting(editor));
51
+ if (!isHeadlessEditor(editor)) {
52
+ if (this.config?.shikiTheme) this.register(registerCodeHighlighting(editor, CustomShikiTokenizer));
53
+ else this.register(registerCodeHighlighting(editor));
54
+ }
53
55
  this.register(registerCodeCommand(editor));
54
56
  if (!isHeadlessEditor(editor)) this.register(editor.registerCommand(PASTE_COMMAND, (event) => {
55
57
  if (!(event instanceof ClipboardEvent)) return false;
@@ -8,6 +8,7 @@ import { cx } from "antd-style";
8
8
  //#region src/plugins/codeblock/react/ReactCodeblockPlugin.tsx
9
9
  const ReactCodeblockPlugin = ({ theme, shikiTheme }) => {
10
10
  const [editor] = useLexicalComposerContext();
11
+ const codeStyle = cx(styles.code);
11
12
  useLayoutEffect(() => {
12
13
  editor.registerPlugin(MarkdownPlugin);
13
14
  editor.registerPlugin(CodeblockPlugin, {
@@ -15,7 +16,7 @@ const ReactCodeblockPlugin = ({ theme, shikiTheme }) => {
15
16
  theme: theme || { code: styles.code }
16
17
  });
17
18
  }, [
18
- cx(styles.code),
19
+ codeStyle,
19
20
  editor,
20
21
  shikiTheme,
21
22
  theme
@@ -19,12 +19,14 @@ function registerCodeMirrorCommand(editor) {
19
19
  }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(UPDATE_CODEBLOCK_LANG, (payload) => {
20
20
  const codeMirrorNode = editor.getEditorState().read(() => {
21
21
  const selection = $getSelection();
22
- if ($isRangeSelection(selection)) if (selection.isCollapsed()) return $findMatchingParent(selection.anchor.getNode(), $isCodeMirrorNode);
23
- else {
24
- const anchor = $findMatchingParent(selection.anchor.getNode(), $isCodeMirrorNode);
25
- const focus = $findMatchingParent(selection.focus.getNode(), $isCodeMirrorNode);
26
- if (anchor && focus && anchor === focus) return anchor;
27
- return null;
22
+ if ($isRangeSelection(selection)) {
23
+ if (selection.isCollapsed()) return $findMatchingParent(selection.anchor.getNode(), $isCodeMirrorNode);
24
+ else {
25
+ const anchor = $findMatchingParent(selection.anchor.getNode(), $isCodeMirrorNode);
26
+ const focus = $findMatchingParent(selection.focus.getNode(), $isCodeMirrorNode);
27
+ if (anchor && focus && anchor === focus) return anchor;
28
+ return null;
29
+ }
28
30
  }
29
31
  return false;
30
32
  });
@@ -97,10 +97,11 @@ var JSONDataSource = class extends DataSource {
97
97
  ...node.exportJSON(),
98
98
  $key: node.getKey()
99
99
  };
100
- if (node === firstNode) if (node === lastNode) {
101
- if (anchor.type !== "element" || focus.type !== "element" || focus.offset === anchor.offset) sNode.text = anchorOffset < focusOffset ? sNode.text.slice(anchorOffset, focusOffset) : sNode.text.slice(focusOffset, anchorOffset);
102
- } else sNode.text = isBefore ? sNode.text.slice(anchorOffset) : sNode.text.slice(focusOffset);
103
- else if (node === lastNode) sNode.text = isBefore ? sNode.text.slice(0, focusOffset) : sNode.text.slice(0, anchorOffset);
100
+ if (node === firstNode) {
101
+ if (node === lastNode) {
102
+ if (anchor.type !== "element" || focus.type !== "element" || focus.offset === anchor.offset) sNode.text = anchorOffset < focusOffset ? sNode.text.slice(anchorOffset, focusOffset) : sNode.text.slice(focusOffset, anchorOffset);
103
+ } else sNode.text = isBefore ? sNode.text.slice(anchorOffset) : sNode.text.slice(focusOffset);
104
+ } else if (node === lastNode) sNode.text = isBefore ? sNode.text.slice(0, focusOffset) : sNode.text.slice(0, anchorOffset);
104
105
  let hasPush = false;
105
106
  for (let i = lastElement.length - 1; i >= 0; i--) if ($getNodeByKey(lastElement[i].$key)?.isParentOf(node)) {
106
107
  lastElement[i].children.push(sNode);
@@ -175,9 +175,7 @@ const CommonPlugin = class extends KernelPlugin {
175
175
  case "h6":
176
176
  ctx.wrap("###### ", "\n\n");
177
177
  break;
178
- default:
179
- ctx.wrap("", "\n\n");
180
- break;
178
+ default: ctx.wrap("", "\n\n");
181
179
  }
182
180
  });
183
181
  markdownService.registerMarkdownWriter("text", (ctx, node) => {
@@ -23,7 +23,8 @@ const handleVSCodePaste = ({ clipboardData, config, editor, event }) => {
23
23
  if (!text) return "next";
24
24
  event.preventDefault();
25
25
  editor.update(() => {
26
- if (!$isRangeSelection($getSelection())) return;
26
+ const selection = $getSelection();
27
+ if (!$isRangeSelection(selection)) return;
27
28
  const codeNode = $createCodeMirrorNode(language, text);
28
29
  $insertNodes([codeNode]);
29
30
  const nodeSelection = $createNodeSelection();
@@ -5,7 +5,8 @@ const INSERT_HORIZONTAL_RULE_COMMAND = createCommand("INSERT_HORIZONTAL_RULE_COM
5
5
  function registerHorizontalRuleCommand(editor) {
6
6
  return editor.registerCommand(INSERT_HORIZONTAL_RULE_COMMAND, () => {
7
7
  editor.update(() => {
8
- $insertNodes([$createHorizontalRuleNode()]);
8
+ const hrNode = $createHorizontalRuleNode();
9
+ $insertNodes([hrNode]);
9
10
  });
10
11
  return true;
11
12
  }, COMMAND_PRIORITY_EDITOR);
@@ -4,8 +4,9 @@ import { styles } from "./style.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/plugins/image/react/components/BrokenImage.tsx
6
6
  const BrokenImage = () => {
7
+ const t = useTranslation();
7
8
  return /* @__PURE__ */ jsx("img", {
8
- alt: useTranslation()("image.broken"),
9
+ alt: t("image.broken"),
9
10
  className: styles.brokenImage,
10
11
  draggable: "false",
11
12
  src: imageBroken
@@ -96,26 +96,29 @@ const Image = memo(({ node, className, showScaleInfo = false, handleUpload, onPi
96
96
  switch (node.status) {
97
97
  case "error": return /* @__PURE__ */ jsx(BrokenImage, {});
98
98
  case "uploaded":
99
- case "loading": return /* @__PURE__ */ jsx(Suspense, {
100
- fallback: lastLoadedSrcRef.current ? /* @__PURE__ */ jsx("img", {
99
+ case "loading": {
100
+ const fallback = lastLoadedSrcRef.current ? /* @__PURE__ */ jsx("img", {
101
101
  alt: node.altText,
102
102
  className: cx(styles.lazyImage, className || void 0),
103
103
  draggable: "false",
104
104
  src: lastLoadedSrcRef.current,
105
105
  style: { width: "100%" }
106
- }) : null,
107
- children: /* @__PURE__ */ jsx(LazyImage, {
108
- className,
109
- newWidth,
110
- node,
111
- onLoad: (loadedSize) => {
112
- lastLoadedSrcRef.current = node.src;
113
- originalSizeRef.current.width = loadedSize.width;
114
- originalSizeRef.current.height = loadedSize.height;
115
- setSize(loadedSize);
116
- }
117
- })
118
- });
106
+ }) : null;
107
+ return /* @__PURE__ */ jsx(Suspense, {
108
+ fallback,
109
+ children: /* @__PURE__ */ jsx(LazyImage, {
110
+ className,
111
+ newWidth,
112
+ node,
113
+ onLoad: (loadedSize) => {
114
+ lastLoadedSrcRef.current = node.src;
115
+ originalSizeRef.current.width = loadedSize.width;
116
+ originalSizeRef.current.height = loadedSize.height;
117
+ setSize(loadedSize);
118
+ }
119
+ })
120
+ });
121
+ }
119
122
  default: return null;
120
123
  }
121
124
  }, [
@@ -103,49 +103,50 @@ const ImageEditPopover = memo(({ children, node, handleUpload, onPickFile }) =>
103
103
  handleClose,
104
104
  node
105
105
  ]);
106
- return /* @__PURE__ */ jsx(Popover, {
107
- arrow: false,
108
- content: /* @__PURE__ */ jsxs(Flexbox, {
106
+ const content = /* @__PURE__ */ jsxs(Flexbox, {
107
+ gap: 8,
108
+ style: { width: 320 },
109
+ children: [/* @__PURE__ */ jsx(Input, {
110
+ onChange: (e) => setUrl(e.target.value),
111
+ onKeyDown: (e) => {
112
+ if (e.key === "Enter") handleUrlSubmit();
113
+ if (e.key === "Escape") handleClose();
114
+ },
115
+ placeholder: "https://...",
116
+ prefix: /* @__PURE__ */ jsx(Icon, {
117
+ color: cssVar.colorTextDescription,
118
+ icon: LinkIcon
119
+ }),
120
+ value: url
121
+ }), /* @__PURE__ */ jsxs(Flexbox, {
109
122
  gap: 8,
110
- style: { width: 320 },
111
- children: [/* @__PURE__ */ jsx(Input, {
112
- onChange: (e) => setUrl(e.target.value),
113
- onKeyDown: (e) => {
114
- if (e.key === "Enter") handleUrlSubmit();
115
- if (e.key === "Escape") handleClose();
116
- },
117
- placeholder: "https://...",
118
- prefix: /* @__PURE__ */ jsx(Icon, {
119
- color: cssVar.colorTextDescription,
120
- icon: LinkIcon
121
- }),
122
- value: url
123
- }), /* @__PURE__ */ jsxs(Flexbox, {
124
- gap: 8,
125
- horizontal: true,
126
- justify: "flex-end",
127
- children: [handleUpload && /* @__PURE__ */ jsxs(Fragment, { children: [!onPickFile && /* @__PURE__ */ jsx("input", {
128
- accept: "image/*",
129
- onChange: handleFileChange,
130
- ref: fileInputRef,
131
- style: { display: "none" },
132
- type: "file"
133
- }), /* @__PURE__ */ jsx(Button, {
134
- icon: /* @__PURE__ */ jsx(Icon, { icon: UploadIcon }),
135
- loading: uploading,
136
- onClick: handlePickFile,
137
- size: "small",
138
- type: "text",
139
- children: t("image.replace")
140
- })] }), /* @__PURE__ */ jsx(Button, {
141
- onClick: handleUrlSubmit,
142
- size: "small",
143
- type: "text",
144
- variant: "filled",
145
- children: t("confirm")
146
- })]
123
+ horizontal: true,
124
+ justify: "flex-end",
125
+ children: [handleUpload && /* @__PURE__ */ jsxs(Fragment, { children: [!onPickFile && /* @__PURE__ */ jsx("input", {
126
+ accept: "image/*",
127
+ onChange: handleFileChange,
128
+ ref: fileInputRef,
129
+ style: { display: "none" },
130
+ type: "file"
131
+ }), /* @__PURE__ */ jsx(Button, {
132
+ icon: /* @__PURE__ */ jsx(Icon, { icon: UploadIcon }),
133
+ loading: uploading,
134
+ onClick: handlePickFile,
135
+ size: "small",
136
+ type: "text",
137
+ children: t("image.replace")
138
+ })] }), /* @__PURE__ */ jsx(Button, {
139
+ onClick: handleUrlSubmit,
140
+ size: "small",
141
+ type: "text",
142
+ variant: "filled",
143
+ children: t("confirm")
147
144
  })]
148
- }),
145
+ })]
146
+ });
147
+ return /* @__PURE__ */ jsx(Popover, {
148
+ arrow: false,
149
+ content,
149
150
  disabled: !editable || node.status !== "uploaded",
150
151
  onOpenChange: handleOpenChange,
151
152
  open,
@@ -29,9 +29,7 @@ const ResizeHandle = ({ imageRef, isBlock, onResize, onResizeEnd, onResizeStart,
29
29
  case "left":
30
30
  deltaX = isBlock ? centerX - e.clientX : -deltaX;
31
31
  break;
32
- case "right":
33
- if (isBlock) deltaX = e.clientX - centerX;
34
- break;
32
+ case "right": if (isBlock) deltaX = e.clientX - centerX;
35
33
  }
36
34
  lastDeltaX = deltaX;
37
35
  lastDeltaY = deltaY;
@@ -67,8 +67,10 @@ var LinkNode = class LinkNode extends ElementNode {
67
67
  ]) {
68
68
  const key = `__${attr}`;
69
69
  const value = this[key];
70
- if (!prevNode || prevNode[key] !== value) if (value) anchor[attr] = value;
71
- else anchor.removeAttribute(attr);
70
+ if (!prevNode || prevNode[key] !== value) {
71
+ if (value) anchor[attr] = value;
72
+ else anchor.removeAttribute(attr);
73
+ }
72
74
  }
73
75
  }
74
76
  }
@@ -13,7 +13,8 @@ function registerLinkCommands(editor, kernel, options) {
13
13
  if (!selection) return;
14
14
  if ($isRangeSelection(selection)) editor.getEditorState().read(() => {
15
15
  const node = getSelectedNode(selection);
16
- const isLink = $isLinkNode(node.getParent()) || $isLinkNode(node);
16
+ const parent = node.getParent();
17
+ const isLink = $isLinkNode(parent) || $isLinkNode(node);
17
18
  state.isLink = isLink;
18
19
  });
19
20
  else state.isLink = false;
@@ -47,17 +48,21 @@ function registerLinkCommands(editor, kernel, options) {
47
48
  let expandTo = null;
48
49
  editor.getEditorState().read(() => {
49
50
  const selection = $getSelection();
50
- if ($isRangeSelection(selection)) if (!selection.isCollapsed()) {
51
- const maybeUrl = formatUrl(selection.getTextContent().trim());
52
- if (validateUrl?.(maybeUrl)) nextUrl = maybeUrl;
53
- } else {
54
- const found = extractUrlFromText(selection.anchor.getNode().getTextContent());
55
- if (found && validateUrl?.(formatUrl(found.url))) {
56
- nextUrl = formatUrl(found.url);
57
- expandTo = {
58
- index: found.index,
59
- length: found.length
60
- };
51
+ if ($isRangeSelection(selection)) {
52
+ if (!selection.isCollapsed()) {
53
+ const text = selection.getTextContent().trim();
54
+ const maybeUrl = formatUrl(text);
55
+ if (validateUrl?.(maybeUrl)) nextUrl = maybeUrl;
56
+ } else {
57
+ const lineText = selection.anchor.getNode().getTextContent();
58
+ const found = extractUrlFromText(lineText);
59
+ if (found && validateUrl?.(formatUrl(found.url))) {
60
+ nextUrl = formatUrl(found.url);
61
+ expandTo = {
62
+ index: found.index,
63
+ length: found.length
64
+ };
65
+ }
61
66
  }
62
67
  }
63
68
  });
@@ -61,14 +61,15 @@ const LinkEdit = ({ editor }) => {
61
61
  switch (event.key) {
62
62
  case "Enter":
63
63
  event.preventDefault();
64
- if (event.currentTarget === textInputDOM) if (editor.getEditorState().read(() => linkNode.getTextContent()) !== textInputDOM.value) {
65
- editor.dispatchCommand(UPDATE_LINK_TEXT_COMMAND, {
66
- key: linkNode.getKey(),
67
- text: textInputDOM.value
68
- });
69
- inputDOM.focus();
70
- } else inputDOM.focus();
71
- else if (event.currentTarget === inputDOM) handleSubmit();
64
+ if (event.currentTarget === textInputDOM) {
65
+ if (editor.getEditorState().read(() => linkNode.getTextContent()) !== textInputDOM.value) {
66
+ editor.dispatchCommand(UPDATE_LINK_TEXT_COMMAND, {
67
+ key: linkNode.getKey(),
68
+ text: textInputDOM.value
69
+ });
70
+ inputDOM.focus();
71
+ } else inputDOM.focus();
72
+ } else if (event.currentTarget === inputDOM) handleSubmit();
72
73
  return;
73
74
  case "Tab":
74
75
  event.preventDefault();
@@ -141,10 +141,7 @@ const ListPlugin = class extends KernelPlugin {
141
141
  case "number":
142
142
  ctx.wrap(`${prefix}${node.getValue()}. `, "\n");
143
143
  break;
144
- case "check":
145
- ctx.wrap(`${prefix}- [${node.getChecked() ? "x" : " "}] `, "\n");
146
- break;
147
- default: break;
144
+ case "check": ctx.wrap(`${prefix}- [${node.getChecked() ? "x" : " "}] `, "\n");
148
145
  }
149
146
  }
150
147
  });
@@ -36,14 +36,16 @@ const listReplace = (listType) => {
36
36
  };
37
37
  };
38
38
  function $indentOverTab(selection) {
39
- if ($filter(selection.getNodes(), (node) => {
39
+ const nodes = selection.getNodes();
40
+ if ($filter(nodes, (node) => {
40
41
  if ($isBlockElementNode(node) && node.canIndent()) return node;
41
42
  return null;
42
43
  }).length > 0) return true;
43
44
  const anchor = selection.anchor;
44
45
  const focus = selection.focus;
45
46
  const first = focus.isBefore(anchor) ? focus : anchor;
46
- const firstBlock = $getNearestBlockElementAncestorOrThrow(first.getNode());
47
+ const firstNode = first.getNode();
48
+ const firstBlock = $getNearestBlockElementAncestorOrThrow(firstNode);
47
49
  if (firstBlock.canIndent()) {
48
50
  const firstBlockKey = firstBlock.getKey();
49
51
  let selectionAtStart = $createRangeSelection();
@@ -53,22 +53,24 @@ function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
53
53
  for (const blockNodeKey of modifyBlockNodes) {
54
54
  const blockNode = $getNodeByKey(blockNodeKey);
55
55
  const diffNode = diffNodeMap.get(blockNodeKey);
56
- if (diffNode && blockNode) if (blockNode.getType() === "listitem" && $isElementNode(blockNode)) {
57
- const newDiffNode = $createDiffNode("listItemModify");
58
- const firstChild = diffNode.getFirstChild();
59
- if (firstChild && $isElementNode(firstChild)) newDiffNode.append(firstChild);
60
- const children = blockNode.getChildren();
61
- const p = $createParagraphNode();
62
- children.forEach((child) => {
63
- child.remove();
64
- p.append(child);
65
- });
66
- newDiffNode.append(p);
67
- blockNode.append(newDiffNode);
68
- continue;
69
- } else {
70
- diffNode.append($cloneNode(blockNode, editor));
71
- blockNode.replace(diffNode, false);
56
+ if (diffNode && blockNode) {
57
+ if (blockNode.getType() === "listitem" && $isElementNode(blockNode)) {
58
+ const newDiffNode = $createDiffNode("listItemModify");
59
+ const firstChild = diffNode.getFirstChild();
60
+ if (firstChild && $isElementNode(firstChild)) newDiffNode.append(firstChild);
61
+ const children = blockNode.getChildren();
62
+ const p = $createParagraphNode();
63
+ children.forEach((child) => {
64
+ child.remove();
65
+ p.append(child);
66
+ });
67
+ newDiffNode.append(p);
68
+ blockNode.append(newDiffNode);
69
+ continue;
70
+ } else {
71
+ diffNode.append($cloneNode(blockNode, editor));
72
+ blockNode.replace(diffNode, false);
73
+ }
72
74
  }
73
75
  }
74
76
  }
@@ -176,15 +178,18 @@ function handleModify(editor, dataSource, arrayXml, delay) {
176
178
  try {
177
179
  const { oldNode, newNode } = tryParseChild(child, editor);
178
180
  if (oldNode && newNode) prevNode = oldNode.replace(newNode, false);
179
- else if (newNode) if (prevNode) if (!newNode.isInline()) {
180
- const prevBlock = $closest(prevNode, (node) => node.isInline() === false);
181
- if (prevBlock) prevNode = prevBlock.insertAfter(newNode);
182
- else {
183
- $insertNodes([newNode]);
184
- prevNode = newNode;
185
- }
186
- } else prevNode = prevNode.insertAfter(newNode);
187
- else $insertNodes([newNode]);
181
+ else if (newNode) {
182
+ if (prevNode) {
183
+ if (!newNode.isInline()) {
184
+ const prevBlock = $closest(prevNode, (node) => node.isInline() === false);
185
+ if (prevBlock) prevNode = prevBlock.insertAfter(newNode);
186
+ else {
187
+ $insertNodes([newNode]);
188
+ prevNode = newNode;
189
+ }
190
+ } else prevNode = prevNode.insertAfter(newNode);
191
+ } else $insertNodes([newNode]);
192
+ }
188
193
  } catch (error) {
189
194
  logger.error("❌ Error replacing node:", error);
190
195
  }
@@ -219,8 +224,6 @@ function handleRemove(editor, key, delay) {
219
224
  originDiffNode.replace(children[0], false).selectEnd();
220
225
  return;
221
226
  }
222
- case "remove":
223
- case "unchanged": break;
224
227
  }
225
228
  return;
226
229
  }
@@ -256,9 +259,10 @@ function handleInsert(editor, payload, dataSource) {
256
259
  editor.update(() => {
257
260
  try {
258
261
  let referenceNode = null;
259
- if (isBefore) if (payload.beforeId === "root") referenceNode = $getRoot().getFirstChild();
260
- else referenceNode = $getNodeByKey(charToId(payload.beforeId));
261
- else if (payload.afterId === "root") referenceNode = $getRoot().getLastChild();
262
+ if (isBefore) {
263
+ if (payload.beforeId === "root") referenceNode = $getRoot().getFirstChild();
264
+ else referenceNode = $getNodeByKey(charToId(payload.beforeId));
265
+ } else if (payload.afterId === "root") referenceNode = $getRoot().getLastChild();
262
266
  else referenceNode = $getNodeByKey(charToId(payload.afterId));
263
267
  if (!referenceNode) throw new Error("Reference node not found for insertion.");
264
268
  const newNodes = inode.root.children.map((child) => $parseSerializedNodeImpl(child, editor));
@@ -271,43 +275,46 @@ function handleInsert(editor, payload, dataSource) {
271
275
  });
272
276
  return;
273
277
  }
274
- if (isBefore) if (referenceNode.isInline() === false) {
275
- const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
276
- if (originDiffNode) referenceNode = originDiffNode;
277
- newNodes.map((node) => {
278
- const diffNode = $createDiffNode("add");
279
- diffNode.append(node);
280
- return diffNode;
281
- }).reverse().forEach((diffNode) => {
282
- if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
283
- });
284
- } else {
285
- const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
286
- if (!refBlock) throw new Error("Reference block node not found for insertion.");
287
- if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
288
- if (referenceNode) referenceNode = referenceNode.insertBefore(node);
289
- });
290
- else wrapBlockModify(refBlock, editor, () => {
291
- newNodes.forEach((node) => {
278
+ if (isBefore) {
279
+ if (referenceNode.isInline() === false) {
280
+ const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
281
+ if (originDiffNode) referenceNode = originDiffNode;
282
+ newNodes.map((node) => {
283
+ const diffNode = $createDiffNode("add");
284
+ diffNode.append(node);
285
+ return diffNode;
286
+ }).reverse().forEach((diffNode) => {
287
+ if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
288
+ });
289
+ } else {
290
+ const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
291
+ if (!refBlock) throw new Error("Reference block node not found for insertion.");
292
+ if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
292
293
  if (referenceNode) referenceNode = referenceNode.insertBefore(node);
293
294
  });
294
- });
295
- }
296
- else if (referenceNode.isInline() === false) {
295
+ else wrapBlockModify(refBlock, editor, () => {
296
+ newNodes.forEach((node) => {
297
+ if (referenceNode) referenceNode = referenceNode.insertBefore(node);
298
+ });
299
+ });
300
+ }
301
+ } else if (referenceNode.isInline() === false) {
297
302
  const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
298
303
  if (originDiffNode) referenceNode = originDiffNode;
299
304
  newNodes.forEach((node) => {
300
- if (referenceNode) if ($isListItemNode(node)) {
301
- const diffNode = $createDiffNode("listItemAdd");
302
- node.getChildren().forEach((child) => {
303
- diffNode.append(child);
304
- });
305
- node.append(diffNode);
306
- referenceNode = referenceNode.insertAfter(node);
307
- } else {
308
- const diffNode = $createDiffNode("add");
309
- diffNode.append(node);
310
- referenceNode = referenceNode.insertAfter(diffNode);
305
+ if (referenceNode) {
306
+ if ($isListItemNode(node)) {
307
+ const diffNode = $createDiffNode("listItemAdd");
308
+ node.getChildren().forEach((child) => {
309
+ diffNode.append(child);
310
+ });
311
+ node.append(diffNode);
312
+ referenceNode = referenceNode.insertAfter(node);
313
+ } else {
314
+ const diffNode = $createDiffNode("add");
315
+ diffNode.append(node);
316
+ referenceNode = referenceNode.insertAfter(diffNode);
317
+ }
311
318
  }
312
319
  });
313
320
  } else {
@@ -57,11 +57,13 @@ var DiffNode = class DiffNode extends CardLikeElementNode {
57
57
  content.className = "content";
58
58
  el.append(content);
59
59
  const decorator = getKernelFromEditor(editor)?.getDecorator("diff") || null;
60
- if (decorator) if (typeof decorator === "function") reconcileDecorator(editor, this.getKey(), decorator(this, editor));
61
- else reconcileDecorator(editor, this.getKey(), {
62
- queryDOM: decorator.queryDOM,
63
- render: decorator.render(this, editor)
64
- });
60
+ if (decorator) {
61
+ if (typeof decorator === "function") reconcileDecorator(editor, this.getKey(), decorator(this, editor));
62
+ else reconcileDecorator(editor, this.getKey(), {
63
+ queryDOM: decorator.queryDOM,
64
+ render: decorator.render(this, editor)
65
+ });
66
+ }
65
67
  return el;
66
68
  }
67
69
  updateDOM(_prevNode, _dom, _config) {
@@ -70,11 +72,13 @@ var DiffNode = class DiffNode extends CardLikeElementNode {
70
72
  const editor = kernel?.getLexicalEditor();
71
73
  if (editor) {
72
74
  const decorator = kernel?.getDecorator("diff") || null;
73
- if (decorator) if (typeof decorator === "function") reconcileDecorator(editor, this.getKey(), decorator(this, editor));
74
- else reconcileDecorator(editor, this.getKey(), {
75
- queryDOM: decorator.queryDOM,
76
- render: decorator.render(this, editor)
77
- });
75
+ if (decorator) {
76
+ if (typeof decorator === "function") reconcileDecorator(editor, this.getKey(), decorator(this, editor));
77
+ else reconcileDecorator(editor, this.getKey(), {
78
+ queryDOM: decorator.queryDOM,
79
+ render: decorator.render(this, editor)
80
+ });
81
+ }
78
82
  }
79
83
  return false;
80
84
  }