@lobehub/editor 4.23.1 → 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.
- package/es/editor-kernel/index.js +1 -0
- package/es/editor-kernel/kernel.js +47 -40
- package/es/editor-kernel/react/useLexicalNodeSelection.js +4 -2
- package/es/headless.js +201 -175
- package/es/index.js +5 -2
- package/es/plugins/auto-complete/plugin/index.js +4 -2
- package/es/plugins/block/react/ReactBlockPlugin.js +5 -4
- package/es/plugins/codeblock/command/index.js +8 -6
- package/es/plugins/codeblock/plugin/index.js +4 -2
- package/es/plugins/codeblock/react/ReactCodeblockPlugin.js +2 -1
- package/es/plugins/codemirror-block/command/index.js +8 -6
- package/es/plugins/common/data-source/json-data-source.js +5 -4
- package/es/plugins/common/plugin/index.js +1 -3
- package/es/plugins/common/plugin/paste-handler.js +2 -1
- package/es/plugins/hr/command/index.js +2 -1
- package/es/plugins/image/react/components/BrokenImage.js +2 -1
- package/es/plugins/image/react/components/Image.js +18 -15
- package/es/plugins/image/react/components/ImageEditPopover.js +42 -41
- package/es/plugins/image/react/components/ResizeHandle.js +1 -3
- package/es/plugins/link/node/LinkNode.js +4 -2
- package/es/plugins/link/plugin/registry.js +17 -12
- package/es/plugins/link/react/components/LinkEdit.js +9 -8
- package/es/plugins/list/plugin/index.js +1 -4
- package/es/plugins/list/utils/index.js +4 -2
- package/es/plugins/litexml/command/index.js +69 -62
- package/es/plugins/litexml/node/DiffNode.js +14 -10
- package/es/plugins/litexml/plugin/index.js +6 -12
- package/es/plugins/markdown/data-source/markdown-data-source.js +5 -4
- package/es/plugins/markdown/plugin/index.js +2 -1
- package/es/plugins/markdown/service/transformers.js +4 -2
- package/es/plugins/markdown/utils/index.js +10 -8
- package/es/plugins/math/command/index.js +4 -2
- package/es/plugins/math/react/components/MathInline.js +14 -13
- package/es/plugins/slash/plugin/index.js +13 -4
- package/es/plugins/slash/react/components/DefaultSlashMenu.js +2 -1
- package/es/plugins/slash/react/components/menuLoading.js +4 -0
- package/es/plugins/slash/service/i-slash-service.d.ts +6 -1
- package/es/plugins/slash/service/i-slash-service.js +16 -2
- package/es/plugins/slash/utils/utils.js +1 -1
- package/es/plugins/table/react/TableActionMenu/ActionMenu.js +69 -67
- package/es/plugins/table/react/TableColController.js +29 -26
- package/es/plugins/table/react/TableHoverActions/index.js +0 -2
- package/es/plugins/table/react/TableRowController.js +29 -26
- package/es/plugins/table/utils/index.js +4 -2
- package/es/react/Editor/Editor.js +24 -23
- package/es/react/hooks/useEditorState/index.js +28 -21
- package/es/react/hooks/useEditorState/utils.js +2 -1
- package/es/renderer/engine/render-builtin-node.js +26 -18
- package/es/renderer/engine/render-tree.js +4 -1
- package/es/renderer/renderers/mermaid.js +2 -1
- 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))
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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))
|
|
52
|
-
|
|
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
|
-
|
|
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))
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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)
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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
|
-
$
|
|
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:
|
|
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":
|
|
100
|
-
fallback
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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)
|
|
71
|
-
|
|
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
|
|
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))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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)
|
|
65
|
-
editor.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
|
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)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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)
|
|
260
|
-
|
|
261
|
-
|
|
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)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (referenceNode)
|
|
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
|
-
|
|
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)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
}
|