@lobehub/editor 4.29.0 → 4.29.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/README.md +86 -0
- package/es/editor-kernel/index.d.ts +2 -2
- package/es/editor-kernel/index.js +2 -2
- package/es/headless/index.d.ts +5 -2
- package/es/headless/index.js +20 -22
- package/es/headless.d.ts +6 -507
- package/es/headless.js +906 -411
- package/es/i-node-identity-service-BBC_bg97.d.ts +522 -0
- package/es/index.d.ts +4 -2
- package/es/index.js +5 -2
- package/es/plugins/block/react/ReactBlockPlugin.js +0 -3
- package/es/plugins/common/data-source/json-data-source.js +33 -25
- package/es/plugins/common/data-source/text-data-source.js +6 -1
- package/es/plugins/common/index.d.ts +3 -1
- package/es/plugins/common/node/node-id.d.ts +7 -0
- package/es/plugins/common/node/node-id.js +484 -0
- package/es/plugins/common/node/node-identity-policy.d.ts +26 -0
- package/es/plugins/common/node/node-identity-policy.js +68 -0
- package/es/plugins/common/plugin/index.js +28 -2
- package/es/plugins/common/service/i-node-identity-service.d.ts +19 -0
- package/es/plugins/common/service/i-node-identity-service.js +4 -0
- package/es/plugins/common/service/node-identity-service.js +87 -0
- package/es/plugins/common/utils/index.js +3 -1
- package/es/plugins/list/plugin/index.js +7 -1
- package/es/plugins/litexml/command/index.d.ts +2 -2
- package/es/plugins/litexml/command/index.js +464 -292
- package/es/plugins/litexml/command/symbols.d.ts +18 -7
- package/es/plugins/litexml/command/symbols.js +2 -1
- package/es/plugins/litexml/data-source/litexml-data-source.js +35 -32
- package/es/plugins/litexml/diff-validation.js +68 -14
- package/es/plugins/litexml/index.d.ts +2 -2
- package/es/plugins/litexml/node/identity-policy.js +91 -0
- package/es/plugins/litexml/plugin/index.js +6 -0
- package/es/plugins/litexml/table-cell-diff.js +4 -0
- package/es/plugins/litexml/table-row-diff.js +4 -0
- package/es/plugins/litexml/utils/index.js +10 -23
- package/es/plugins/markdown/command/index.js +77 -4
- package/es/plugins/markdown/data-source/markdown-data-source.js +6 -1
- package/es/plugins/math/plugin/index.js +2 -2
- package/es/plugins/slash/service/i-slash-service.d.ts +2 -2
- package/es/plugins/yjs/plugin/index.js +5 -3
- package/es/plugins/yjs/plugin/utils/editor-state.js +8 -1
- package/es/plugins/yjs/plugin/utils/node-identity.js +106 -0
- package/es/plugins/yjs/service/index.js +38 -4
- package/es/{symbols-Do7jD4Tt.d.ts → symbols-Dyr9yyS0.d.ts} +18 -7
- package/package.json +4 -4
- package/scripts/postinstall-lexical-patch.cjs +0 -21
- package/patches/lexical@0.42.0.patch +0 -82
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { createDebugLogger, init_debug } from "../../../utils/debug.js";
|
|
2
|
-
import { $
|
|
3
|
-
import {
|
|
2
|
+
import { $ensureUniqueNodeIds, $findNodeById, $getNodeId, init_node_id } from "../../common/node/node-id.js";
|
|
3
|
+
import { exportNodeToJSON, init_utils } from "../../common/utils/index.js";
|
|
4
|
+
import { $closest, init_utils as init_utils$1 } from "../../../editor-kernel/utils.js";
|
|
5
|
+
import { findNewIllegalDiffPaths, hasActiveLiteXmlNodeId, projectLiteXmlOperation } from "../diff-validation.js";
|
|
4
6
|
import { $createDiffContentNode, $isDiffContentNode } from "../node/DiffContentNode.js";
|
|
5
7
|
import { $createDiffNode, $isDiffNode, DiffNode } from "../node/DiffNode.js";
|
|
6
8
|
import { $isTableCellDiffNode } from "../node/TableCellDiffNode.js";
|
|
7
|
-
import { $cloneNode, $parseSerializedNodeImpl
|
|
9
|
+
import { $cloneNode, $parseSerializedNodeImpl } from "../utils/index.js";
|
|
8
10
|
import { $createTableCellDiffFromCell, $getLogicalRowWidth, $getTableCellColumnIndex, $getTableForCell, $shrinkTableWidthsAfterCellRemoval, $updateTableWidthsForCellInsertion } from "../table-cell-diff.js";
|
|
9
11
|
import { $areTableRowStructuresCompatible, $createTableRowDiffFromRow } from "../table-row-diff.js";
|
|
10
|
-
import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./symbols.js";
|
|
12
|
+
import { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND } from "./symbols.js";
|
|
11
13
|
import { $isTableCellNode, $isTableNode, $isTableRowNode } from "@lexical/table";
|
|
12
14
|
import { $createParagraphNode, $getNodeByKey, $getRoot, $insertNodes, $isElementNode, COMMAND_PRIORITY_EDITOR } from "lexical";
|
|
13
15
|
import { mergeRegister } from "@lexical/utils";
|
|
14
|
-
import { $isListItemNode } from "@lexical/list";
|
|
16
|
+
import { $isListItemNode, $isListNode } from "@lexical/list";
|
|
15
17
|
//#region src/plugins/litexml/command/index.ts
|
|
18
|
+
init_utils$1();
|
|
19
|
+
init_node_id();
|
|
16
20
|
init_utils();
|
|
17
21
|
init_debug();
|
|
18
22
|
const logger = createDebugLogger("plugin", "litexml");
|
|
@@ -22,9 +26,9 @@ function toArrayXml(litexml) {
|
|
|
22
26
|
function hasNewIllegalDiffs(previous, projected) {
|
|
23
27
|
return findNewIllegalDiffPaths(previous, projected);
|
|
24
28
|
}
|
|
25
|
-
function projectOperation(dataSource, document, operation) {
|
|
29
|
+
function projectOperation(dataSource, document, operation, readXml = (xml) => dataSource.readLiteXMLToInode(xml)) {
|
|
26
30
|
try {
|
|
27
|
-
const projected = projectLiteXmlOperation(document, operation,
|
|
31
|
+
const projected = projectLiteXmlOperation(document, operation, readXml);
|
|
28
32
|
const newIllegalDiffs = hasNewIllegalDiffs(document, projected);
|
|
29
33
|
if (newIllegalDiffs.length > 0) {
|
|
30
34
|
logger.warn("⚠️ Skipping operation with illegal nested diff", newIllegalDiffs);
|
|
@@ -37,19 +41,163 @@ function projectOperation(dataSource, document, operation) {
|
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
function toProjectionOperation(operation) {
|
|
40
|
-
if (operation.action === "remove") return {
|
|
41
|
-
...operation,
|
|
42
|
-
id: charToId(operation.id)
|
|
43
|
-
};
|
|
44
|
-
if (operation.action === "insert") return {
|
|
45
|
-
...operation,
|
|
46
|
-
..."beforeId" in operation ? { beforeId: operation.beforeId === "root" ? "root" : charToId(operation.beforeId) } : { afterId: operation.afterId === "root" ? "root" : charToId(operation.afterId) }
|
|
47
|
-
};
|
|
48
44
|
return operation;
|
|
49
45
|
}
|
|
46
|
+
function getSerializedNodeId(node) {
|
|
47
|
+
if (typeof node?.id === "string" && node.id.length > 0) return node.id;
|
|
48
|
+
const nodeId = node?.$?.properties?.nodeId;
|
|
49
|
+
return typeof nodeId === "string" && nodeId.length > 0 ? nodeId : void 0;
|
|
50
|
+
}
|
|
51
|
+
function getActiveSerializedDocument() {
|
|
52
|
+
return { root: exportNodeToJSON($getRoot()) };
|
|
53
|
+
}
|
|
54
|
+
/** Resolve every target before staging any part of one modify operation. */
|
|
55
|
+
function prepareModify(editor, dataSource, xmls) {
|
|
56
|
+
const pairs = [];
|
|
57
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
58
|
+
const targetIds = /* @__PURE__ */ new Set();
|
|
59
|
+
for (const xml of xmls) {
|
|
60
|
+
const inode = dataSource.readLiteXMLToInode(xml);
|
|
61
|
+
parsed.set(xml, inode);
|
|
62
|
+
const children = inode.root?.children;
|
|
63
|
+
if (!children?.length) throw new Error("Modify operation contains no LiteXML nodes.");
|
|
64
|
+
for (const child of children) {
|
|
65
|
+
const nodeId = getSerializedNodeId(child);
|
|
66
|
+
if (!nodeId) throw new Error("Modify operation root is missing its public node id.");
|
|
67
|
+
if (targetIds.has(nodeId)) throw new Error(`Node id "${nodeId}" is targeted more than once.`);
|
|
68
|
+
targetIds.add(nodeId);
|
|
69
|
+
const oldNode = $findNodeById(nodeId);
|
|
70
|
+
if (!oldNode) throw new Error(`Node id "${nodeId}" was not found.`);
|
|
71
|
+
const newNode = $parseSerializedNodeImpl(child, editor);
|
|
72
|
+
if (oldNode.isInline() !== newNode.isInline()) throw new Error(`Node id "${nodeId}" cannot change between inline and block structure.`);
|
|
73
|
+
const parent = oldNode.getParent();
|
|
74
|
+
if (($isListNode(parent) || $isListItemNode(oldNode) || $isListItemNode(newNode)) && (!$isListNode(parent) || !$isListItemNode(oldNode) || !$isListItemNode(newNode))) throw new Error(`Node id "${nodeId}" has an incompatible list-item structure.`);
|
|
75
|
+
if ($isTableNode(parent) && (!$isTableRowNode(oldNode) || !$isTableRowNode(newNode)) || $isTableRowNode(parent) && (!$isTableCellNode(oldNode) || !$isTableCellNode(newNode)) || $isTableRowNode(newNode) && !$isTableNode(parent) || $isTableCellNode(newNode) && !$isTableRowNode(parent)) throw new Error(`Node id "${nodeId}" has an incompatible table structure.`);
|
|
76
|
+
if (($isTableRowNode(oldNode) || $isTableRowNode(newNode)) && (!$isTableRowNode(oldNode) || !$isTableRowNode(newNode) || !$isTableNode(parent) || !$areTableRowStructuresCompatible(oldNode, newNode))) throw new Error(`Node id "${nodeId}" has an incompatible table row structure.`);
|
|
77
|
+
pairs.push({
|
|
78
|
+
newNode,
|
|
79
|
+
oldNode
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const targetKeys = new Set(pairs.map(({ oldNode }) => oldNode.getKey()));
|
|
84
|
+
for (const { oldNode } of pairs) for (let parent = oldNode.getParent(); parent; parent = parent.getParent()) if (targetKeys.has(parent.getKey())) throw new Error("Modify operation targets overlapping ancestor and descendant nodes.");
|
|
85
|
+
return {
|
|
86
|
+
pairs,
|
|
87
|
+
readXml: (xml) => parsed.get(xml)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function getBatchTargetError(document, operation, dataSource) {
|
|
91
|
+
if (operation.action === "remove") return hasActiveLiteXmlNodeId(document.root, operation.id) ? void 0 : `Node id "${operation.id}" was not found.`;
|
|
92
|
+
if (operation.action === "insert") {
|
|
93
|
+
try {
|
|
94
|
+
if (!dataSource.readLiteXMLToInode(operation.litexml).root.children?.length) return "Insert operation contains no LiteXML nodes.";
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return error instanceof Error ? error.message : String(error);
|
|
97
|
+
}
|
|
98
|
+
const anchorId = "beforeId" in operation ? operation.beforeId : operation.afterId;
|
|
99
|
+
if (anchorId === "root") return (document.root.children?.length || 0) > 0 ? void 0 : "The document has no node to use as an insertion anchor.";
|
|
100
|
+
return hasActiveLiteXmlNodeId(document.root, anchorId) ? void 0 : `Insertion anchor "${anchorId}" was not found.`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function applyLiteXMLBatch(editor, dataSource, operations) {
|
|
104
|
+
let projectedDocument = getActiveSerializedDocument();
|
|
105
|
+
const afterAnchors = /* @__PURE__ */ new Map();
|
|
106
|
+
const results = [];
|
|
107
|
+
operations.forEach((operation, index) => {
|
|
108
|
+
const targetError = getBatchTargetError(projectedDocument, operation, dataSource);
|
|
109
|
+
if (targetError) {
|
|
110
|
+
results.push({
|
|
111
|
+
action: operation.action,
|
|
112
|
+
index,
|
|
113
|
+
reason: targetError,
|
|
114
|
+
status: "failed"
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let preparedModify;
|
|
119
|
+
if (operation.action === "modify") try {
|
|
120
|
+
preparedModify = prepareModify(editor, dataSource, toArrayXml(operation.litexml));
|
|
121
|
+
} catch (error) {
|
|
122
|
+
results.push({
|
|
123
|
+
action: operation.action,
|
|
124
|
+
index,
|
|
125
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
126
|
+
status: "failed"
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (!projectOperation(dataSource, projectedDocument, toProjectionOperation(operation), preparedModify?.readXml)) {
|
|
131
|
+
results.push({
|
|
132
|
+
action: operation.action,
|
|
133
|
+
index,
|
|
134
|
+
reason: "The operation would create an invalid nested review diff.",
|
|
135
|
+
status: "failed"
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
let applied = false;
|
|
141
|
+
switch (operation.action) {
|
|
142
|
+
case "modify":
|
|
143
|
+
applied = applyPreparedModify(editor, preparedModify);
|
|
144
|
+
break;
|
|
145
|
+
case "remove":
|
|
146
|
+
applied = handleRemove(editor, operation.id, true);
|
|
147
|
+
break;
|
|
148
|
+
case "insert": {
|
|
149
|
+
const cursorIds = "afterId" in operation ? afterAnchors.get(operation.afterId) : void 0;
|
|
150
|
+
let override;
|
|
151
|
+
if (cursorIds) for (let cursorIndex = cursorIds.length - 1; cursorIndex >= 0; cursorIndex--) {
|
|
152
|
+
const cursor = $findNodeById(cursorIds[cursorIndex]);
|
|
153
|
+
if (cursor) {
|
|
154
|
+
override = cursor;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const insertedNodeId = handleInsert(editor, {
|
|
159
|
+
...operation,
|
|
160
|
+
delay: true
|
|
161
|
+
}, dataSource, override);
|
|
162
|
+
applied = insertedNodeId !== null;
|
|
163
|
+
if ("afterId" in operation && insertedNodeId) {
|
|
164
|
+
const nextCursors = afterAnchors.get(operation.afterId) || [];
|
|
165
|
+
nextCursors.push(insertedNodeId);
|
|
166
|
+
afterAnchors.set(operation.afterId, nextCursors);
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (!applied) {
|
|
172
|
+
results.push({
|
|
173
|
+
action: operation.action,
|
|
174
|
+
index,
|
|
175
|
+
reason: "The editor could not apply the operation.",
|
|
176
|
+
status: "failed"
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
projectedDocument = getActiveSerializedDocument();
|
|
181
|
+
results.push({
|
|
182
|
+
action: operation.action,
|
|
183
|
+
index,
|
|
184
|
+
status: "applied"
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
results.push({
|
|
188
|
+
action: operation.action,
|
|
189
|
+
index,
|
|
190
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
191
|
+
status: "failed"
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
return results;
|
|
196
|
+
}
|
|
50
197
|
function tryParseChild(child, editor) {
|
|
51
198
|
try {
|
|
52
|
-
const
|
|
199
|
+
const nodeId = typeof child.id === "string" ? child.id : child.$?.properties?.nodeId;
|
|
200
|
+
const oldNode = typeof nodeId === "string" ? $findNodeById(nodeId) : null;
|
|
53
201
|
return {
|
|
54
202
|
newNode: $parseSerializedNodeImpl(child, editor),
|
|
55
203
|
oldNode
|
|
@@ -66,14 +214,14 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
66
214
|
if ($isTableRowNode(oldNode) || $isTableRowNode(newNode)) {
|
|
67
215
|
if (!$isTableRowNode(oldNode) || !$isTableRowNode(newNode) || !$isTableNode(oldNode.getParent()) || !$areTableRowStructuresCompatible(oldNode, newNode)) {
|
|
68
216
|
logger.error(`❌ Invalid table row modification for row ${oldNode.getKey()}.`);
|
|
69
|
-
return;
|
|
217
|
+
return false;
|
|
70
218
|
}
|
|
71
219
|
const changeId = `${oldNode.getKey()}:${newNode.getKey()}`;
|
|
72
220
|
const removeRow = $createTableRowDiffFromRow(editor, oldNode, "remove", changeId);
|
|
73
221
|
const addRow = $createTableRowDiffFromRow(editor, newNode, "add", changeId);
|
|
74
222
|
oldNode.replace(removeRow, false);
|
|
75
223
|
removeRow.insertAfter(addRow);
|
|
76
|
-
return;
|
|
224
|
+
return true;
|
|
77
225
|
}
|
|
78
226
|
if ($isTableCellNode(oldNode) && $isTableCellNode(newNode)) {
|
|
79
227
|
const existingDiff = oldNode.getChildren().find($isDiffNode);
|
|
@@ -83,7 +231,7 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
83
231
|
newNode.getChildren().forEach((child) => {
|
|
84
232
|
existingDiff.append($cloneNode(child, editor));
|
|
85
233
|
});
|
|
86
|
-
return;
|
|
234
|
+
return true;
|
|
87
235
|
}
|
|
88
236
|
const after = existingDiff.getChildren().find((child) => $isDiffContentNode(child) && child.side === "after");
|
|
89
237
|
if (existingDiff.diffType === "modify" && after && $isDiffContentNode(after)) {
|
|
@@ -91,7 +239,7 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
91
239
|
newNode.getChildren().forEach((child) => {
|
|
92
240
|
after.append($cloneNode(child, editor));
|
|
93
241
|
});
|
|
94
|
-
return;
|
|
242
|
+
return true;
|
|
95
243
|
}
|
|
96
244
|
}
|
|
97
245
|
const before = $createDiffContentNode("before");
|
|
@@ -102,27 +250,42 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
102
250
|
diffNode.append(before, after);
|
|
103
251
|
oldNode.clear();
|
|
104
252
|
oldNode.append(diffNode);
|
|
105
|
-
return;
|
|
253
|
+
return true;
|
|
106
254
|
}
|
|
107
255
|
const oldBlock = $closest(oldNode, (node) => node.isInline() === false);
|
|
108
256
|
if (!oldBlock) throw new Error("Old block node not found for diffing.");
|
|
109
257
|
if ($closest(oldNode, (node) => node.getType() === DiffNode.getType())) {
|
|
110
258
|
oldNode.replace(newNode, false);
|
|
111
|
-
return;
|
|
259
|
+
return true;
|
|
112
260
|
}
|
|
113
261
|
if ($isListItemNode(oldNode) && $isListItemNode(newNode)) {
|
|
262
|
+
const existingListDiff = oldNode.getChildren().find($isDiffNode);
|
|
263
|
+
if (existingListDiff?.diffType === "listItemModify") {
|
|
264
|
+
const after = existingListDiff.getChildAtIndex(1);
|
|
265
|
+
if ($isElementNode(after)) {
|
|
266
|
+
after.clear();
|
|
267
|
+
newNode.getChildren().forEach((child) => after.append($cloneNode(child, editor)));
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (existingListDiff?.diffType === "listItemAdd") {
|
|
272
|
+
existingListDiff.clear();
|
|
273
|
+
newNode.getChildren().forEach((child) => existingListDiff.append($cloneNode(child, editor)));
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
114
276
|
const before = $createParagraphNode();
|
|
115
277
|
const after = $createParagraphNode();
|
|
116
278
|
oldNode.getChildren().forEach((child) => before.append(child));
|
|
117
279
|
newNode.getChildren().forEach((child) => after.append(child));
|
|
118
280
|
oldNode.clear();
|
|
119
281
|
oldNode.append($createDiffNode("listItemModify").append(before, after));
|
|
120
|
-
return;
|
|
282
|
+
return true;
|
|
121
283
|
}
|
|
122
284
|
if (oldNode === oldBlock) {
|
|
123
285
|
const diffNode = $createDiffNode("modify");
|
|
124
286
|
oldNode.replace(diffNode, false);
|
|
125
287
|
diffNode.append($createDiffContentNode("before").append(oldBlock), $createDiffContentNode("after").append(newNode));
|
|
288
|
+
return true;
|
|
126
289
|
} else {
|
|
127
290
|
if (!modifyBlockNodes.has(oldBlock.getKey())) {
|
|
128
291
|
modifyBlockNodes.add(oldBlock.getKey());
|
|
@@ -131,6 +294,7 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
131
294
|
diffNodeMap.set(oldBlock.getKey(), diffNode);
|
|
132
295
|
}
|
|
133
296
|
oldNode.replace(newNode, false);
|
|
297
|
+
return true;
|
|
134
298
|
}
|
|
135
299
|
}
|
|
136
300
|
function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
|
|
@@ -151,6 +315,16 @@ function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
|
|
|
151
315
|
newDiffNode.append(p);
|
|
152
316
|
blockNode.append(newDiffNode);
|
|
153
317
|
continue;
|
|
318
|
+
} else if ($isTableCellNode(blockNode) && $isTableRowNode(blockNode.getParent()) && $isTableCellNode(diffNode.getFirstChild())) {
|
|
319
|
+
const beforeCell = diffNode.getFirstChild();
|
|
320
|
+
if (!$isTableCellNode(beforeCell)) continue;
|
|
321
|
+
const before = $createDiffContentNode("before");
|
|
322
|
+
const after = $createDiffContentNode("after");
|
|
323
|
+
beforeCell.getChildren().forEach((child) => before.append(child));
|
|
324
|
+
blockNode.getChildren().forEach((child) => after.append($cloneNode(child, editor)));
|
|
325
|
+
blockNode.clear();
|
|
326
|
+
blockNode.append($createDiffNode("modify").append(before, after));
|
|
327
|
+
continue;
|
|
154
328
|
} else {
|
|
155
329
|
diffNode.append($cloneNode(blockNode, editor));
|
|
156
330
|
blockNode.replace(diffNode, false);
|
|
@@ -181,6 +355,18 @@ function wrapBlockModify(oldBlock, editor, changeFn) {
|
|
|
181
355
|
oldBlock.append(diffNode);
|
|
182
356
|
return;
|
|
183
357
|
}
|
|
358
|
+
if ($isTableCellNode(oldBlock) && !$isTableCellDiffNode(oldBlock) && $isTableRowNode(oldBlock.getParent())) {
|
|
359
|
+
const before = $createDiffContentNode("before");
|
|
360
|
+
oldBlock.getChildren().forEach((child) => before.append($cloneNode(child, editor)));
|
|
361
|
+
changeFn();
|
|
362
|
+
const newBlock = $getNodeByKey(oldBlock.getKey());
|
|
363
|
+
if (!$isTableCellNode(newBlock)) throw new Error("Updated table cell node not found for modify wrapper.");
|
|
364
|
+
const after = $createDiffContentNode("after");
|
|
365
|
+
newBlock.getChildren().forEach((child) => after.append($cloneNode(child, editor)));
|
|
366
|
+
newBlock.clear();
|
|
367
|
+
newBlock.append($createDiffNode("modify").append(before, after));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
184
370
|
const diffNode = $createDiffNode("modify");
|
|
185
371
|
diffNode.append($cloneNode(oldBlock, editor));
|
|
186
372
|
changeFn();
|
|
@@ -190,345 +376,331 @@ function wrapBlockModify(oldBlock, editor, changeFn) {
|
|
|
190
376
|
newBlock.replace(diffNode, false);
|
|
191
377
|
}
|
|
192
378
|
function registerLiteXMLCommand(editor, dataSource) {
|
|
193
|
-
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
let projectedDocument = getKernelFromEditor(editor).getDocument("json");
|
|
200
|
-
const safePayload = resultPayload.filter((item) => {
|
|
201
|
-
const nextProjection = projectOperation(dataSource, projectedDocument, toProjectionOperation(item));
|
|
202
|
-
if (!nextProjection) return false;
|
|
203
|
-
projectedDocument = nextProjection;
|
|
204
|
-
return true;
|
|
205
|
-
});
|
|
206
|
-
try {
|
|
207
|
-
safePayload.forEach((item) => {
|
|
208
|
-
const { action } = item;
|
|
209
|
-
switch (action) {
|
|
210
|
-
case "modify": {
|
|
211
|
-
const { litexml } = item;
|
|
212
|
-
handleModify(editor, dataSource, toArrayXml(litexml), true);
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
case "remove": {
|
|
216
|
-
const { id } = item;
|
|
217
|
-
handleRemove(editor, charToId(id), true);
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
case "insert":
|
|
221
|
-
handleInsert(editor, {
|
|
222
|
-
...item,
|
|
223
|
-
delay: true
|
|
224
|
-
}, dataSource);
|
|
225
|
-
break;
|
|
226
|
-
default: logger.warn(`⚠️ Unknown action type: ${action}`);
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
return false;
|
|
230
|
-
} catch (error) {
|
|
231
|
-
logger.error("❌ Error processing LITEXML_MODIFY_COMMAND:", error);
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
379
|
+
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (operations) => {
|
|
380
|
+
applyLiteXMLBatch(editor, dataSource, operations);
|
|
381
|
+
return false;
|
|
382
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_MODIFY_WITH_RESULTS_COMMAND, ({ operations, onResults }) => {
|
|
383
|
+
onResults(applyLiteXMLBatch(editor, dataSource, operations));
|
|
384
|
+
return true;
|
|
234
385
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_APPLY_COMMAND, (payload) => {
|
|
235
386
|
const { litexml, delay } = payload;
|
|
236
387
|
const arrayXml = toArrayXml(litexml);
|
|
237
388
|
if (!delay) {
|
|
238
|
-
handleModify(editor, dataSource, arrayXml
|
|
389
|
+
handleModify(editor, dataSource, arrayXml);
|
|
239
390
|
return false;
|
|
240
391
|
}
|
|
241
392
|
const operation = {
|
|
242
393
|
action: "modify",
|
|
243
394
|
litexml
|
|
244
395
|
};
|
|
245
|
-
|
|
396
|
+
const document = getActiveSerializedDocument();
|
|
397
|
+
try {
|
|
398
|
+
const prepared = prepareModify(editor, dataSource, arrayXml);
|
|
399
|
+
if (projectOperation(dataSource, document, toProjectionOperation(operation), prepared.readXml)) applyPreparedModify(editor, prepared);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
logger.error("❌ Failed to apply LiteXML modification:", error);
|
|
402
|
+
}
|
|
246
403
|
return false;
|
|
247
404
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_REMOVE_COMMAND, (payload) => {
|
|
248
405
|
const { id, delay } = payload;
|
|
249
|
-
const key = charToId(id);
|
|
250
406
|
if (!delay) {
|
|
251
|
-
handleRemove(editor,
|
|
407
|
+
handleRemove(editor, id, delay);
|
|
252
408
|
return false;
|
|
253
409
|
}
|
|
254
410
|
const operation = {
|
|
255
411
|
action: "remove",
|
|
256
412
|
id
|
|
257
413
|
};
|
|
258
|
-
if (projectOperation(dataSource,
|
|
414
|
+
if (projectOperation(dataSource, getActiveSerializedDocument(), toProjectionOperation(operation))) handleRemove(editor, id, delay);
|
|
259
415
|
return false;
|
|
260
416
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_INSERT_COMMAND, (payload) => {
|
|
261
417
|
if (!payload.delay) {
|
|
262
418
|
handleInsert(editor, payload, dataSource);
|
|
263
419
|
return false;
|
|
264
420
|
}
|
|
265
|
-
if (projectOperation(dataSource,
|
|
421
|
+
if (projectOperation(dataSource, getActiveSerializedDocument(), toProjectionOperation({
|
|
266
422
|
action: "insert",
|
|
267
423
|
...payload
|
|
268
424
|
}))) handleInsert(editor, payload, dataSource);
|
|
269
425
|
return false;
|
|
270
426
|
}, COMMAND_PRIORITY_EDITOR));
|
|
271
427
|
}
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
} else $insertNodes([newNode]);
|
|
428
|
+
function applyPreparedModify(editor, prepared) {
|
|
429
|
+
const modifyBlockNodes = /* @__PURE__ */ new Set();
|
|
430
|
+
const diffNodeMap = /* @__PURE__ */ new Map();
|
|
431
|
+
for (const { oldNode, newNode } of prepared.pairs) if (!handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor)) throw new Error(`Node id "${$getNodeId(oldNode)}" could not be modified.`);
|
|
432
|
+
finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor);
|
|
433
|
+
return prepared.pairs.length > 0;
|
|
434
|
+
}
|
|
435
|
+
function handleModify(editor, dataSource, arrayXml) {
|
|
436
|
+
let applied = false;
|
|
437
|
+
arrayXml.forEach((xml) => {
|
|
438
|
+
const inode = dataSource.readLiteXMLToInode(xml);
|
|
439
|
+
let prevNode = null;
|
|
440
|
+
inode.root.children.forEach((child) => {
|
|
441
|
+
try {
|
|
442
|
+
const { oldNode, newNode } = tryParseChild(child, editor);
|
|
443
|
+
if (oldNode && newNode) {
|
|
444
|
+
prevNode = oldNode.replace(newNode, false);
|
|
445
|
+
applied = true;
|
|
446
|
+
} else if (newNode) {
|
|
447
|
+
if (prevNode) {
|
|
448
|
+
if (!newNode.isInline()) {
|
|
449
|
+
const prevBlock = $closest(prevNode, (node) => node.isInline() === false);
|
|
450
|
+
if (prevBlock) prevNode = prevBlock.insertAfter(newNode);
|
|
451
|
+
else {
|
|
452
|
+
$insertNodes([newNode]);
|
|
453
|
+
prevNode = newNode;
|
|
454
|
+
}
|
|
455
|
+
applied = true;
|
|
456
|
+
} else {
|
|
457
|
+
prevNode = prevNode.insertAfter(newNode);
|
|
458
|
+
applied = true;
|
|
459
|
+
}
|
|
460
|
+
} else {
|
|
461
|
+
$insertNodes([newNode]);
|
|
462
|
+
applied = true;
|
|
308
463
|
}
|
|
309
|
-
} catch (error) {
|
|
310
|
-
logger.error("❌ Error replacing node:", error);
|
|
311
464
|
}
|
|
312
|
-
})
|
|
465
|
+
} catch (error) {
|
|
466
|
+
logger.error("❌ Error replacing node:", error);
|
|
467
|
+
}
|
|
313
468
|
});
|
|
314
469
|
});
|
|
470
|
+
return applied;
|
|
315
471
|
}
|
|
316
|
-
function handleRemove(editor,
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if ($isTableCellNode(node)) {
|
|
322
|
-
const table = $getTableForCell(node);
|
|
323
|
-
const columnIndex = $getTableCellColumnIndex(node);
|
|
324
|
-
const span = node.getColSpan();
|
|
325
|
-
node.remove();
|
|
326
|
-
if (table && columnIndex >= 0) $shrinkTableWidthsAfterCellRemoval(table, columnIndex, span);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
node.remove();
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
472
|
+
function handleRemove(editor, nodeId, delay) {
|
|
473
|
+
let applied = false;
|
|
474
|
+
const node = $findNodeById(nodeId);
|
|
475
|
+
if (!node) return false;
|
|
476
|
+
if (!delay) {
|
|
332
477
|
if ($isTableCellNode(node)) {
|
|
333
478
|
const table = $getTableForCell(node);
|
|
334
479
|
const columnIndex = $getTableCellColumnIndex(node);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
node.replace($createTableCellDiffFromCell(editor, node, "remove", changeId), false);
|
|
341
|
-
return;
|
|
480
|
+
const span = node.getColSpan();
|
|
481
|
+
node.remove();
|
|
482
|
+
applied = true;
|
|
483
|
+
if (table && columnIndex >= 0) $shrinkTableWidthsAfterCellRemoval(table, columnIndex, span);
|
|
484
|
+
return true;
|
|
342
485
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
486
|
+
node.remove();
|
|
487
|
+
applied = true;
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
if ($isTableCellNode(node)) {
|
|
491
|
+
const table = $getTableForCell(node);
|
|
492
|
+
const columnIndex = $getTableCellColumnIndex(node);
|
|
493
|
+
if (!table || columnIndex < 0) {
|
|
494
|
+
logger.error(`❌ Table cell ${node.getKey()} is not attached to a valid table row.`);
|
|
495
|
+
return false;
|
|
346
496
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
497
|
+
const changeId = `${table.getKey()}:column:${columnIndex}`;
|
|
498
|
+
node.replace($createTableCellDiffFromCell(editor, node, "remove", changeId), false);
|
|
499
|
+
applied = true;
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
if ($isTableRowNode(node) && $isTableNode(node.getParent())) {
|
|
503
|
+
node.replace($createTableRowDiffFromRow(editor, node, "remove"), false);
|
|
504
|
+
applied = true;
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
if (node.isInline() === false) {
|
|
508
|
+
const originDiffNode = $closest(node, (node) => node.getType() === DiffNode.getType());
|
|
509
|
+
if (originDiffNode) {
|
|
510
|
+
switch (originDiffNode.diffType) {
|
|
511
|
+
case "add":
|
|
512
|
+
originDiffNode.remove();
|
|
513
|
+
applied = true;
|
|
514
|
+
return true;
|
|
515
|
+
case "modify": {
|
|
516
|
+
const children = originDiffNode.getChildren();
|
|
517
|
+
const newDiff = $createDiffNode("remove");
|
|
518
|
+
const before = children[0];
|
|
519
|
+
newDiff.append(...$isDiffContentNode(before) ? before.getChildren() : [before]);
|
|
520
|
+
originDiffNode.replace(newDiff, false);
|
|
521
|
+
applied = true;
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
case "listItemModify": {
|
|
525
|
+
const children = originDiffNode.getChildren();
|
|
526
|
+
originDiffNode.replace(children[0], false).selectEnd();
|
|
527
|
+
applied = true;
|
|
528
|
+
return true;
|
|
367
529
|
}
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
if ($isListItemNode(node)) {
|
|
371
|
-
const diffNode = $createDiffNode("listItemRemove");
|
|
372
|
-
node.getChildren().forEach((child) => {
|
|
373
|
-
diffNode.append($cloneNode(child, editor));
|
|
374
|
-
});
|
|
375
|
-
node.clear();
|
|
376
|
-
node.append(diffNode);
|
|
377
|
-
} else {
|
|
378
|
-
const diffNode = $createDiffNode("remove");
|
|
379
|
-
diffNode.append($cloneNode(node, editor));
|
|
380
|
-
node.replace(diffNode, false);
|
|
381
|
-
}
|
|
382
|
-
} else {
|
|
383
|
-
const oldBlock = $closest(node, (node) => node.isInline() === false);
|
|
384
|
-
if (!oldBlock) throw new Error("Old block node not found for removal.");
|
|
385
|
-
if ($closest(node, (node) => node.getType() === DiffNode.getType())) {
|
|
386
|
-
node.remove();
|
|
387
|
-
return;
|
|
388
530
|
}
|
|
389
|
-
|
|
390
|
-
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
if ($isListItemNode(node)) {
|
|
534
|
+
const diffNode = $createDiffNode("listItemRemove");
|
|
535
|
+
node.getChildren().forEach((child) => {
|
|
536
|
+
diffNode.append($cloneNode(child, editor));
|
|
391
537
|
});
|
|
538
|
+
node.clear();
|
|
539
|
+
node.append(diffNode);
|
|
540
|
+
applied = true;
|
|
541
|
+
} else {
|
|
542
|
+
const diffNode = $createDiffNode("remove");
|
|
543
|
+
diffNode.append($cloneNode(node, editor));
|
|
544
|
+
node.replace(diffNode, false);
|
|
545
|
+
applied = true;
|
|
392
546
|
}
|
|
393
|
-
}
|
|
547
|
+
} else {
|
|
548
|
+
const oldBlock = $closest(node, (node) => node.isInline() === false);
|
|
549
|
+
if (!oldBlock) throw new Error("Old block node not found for removal.");
|
|
550
|
+
if ($closest(node, (node) => node.getType() === DiffNode.getType())) {
|
|
551
|
+
node.remove();
|
|
552
|
+
applied = true;
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
wrapBlockModify(oldBlock, editor, () => {
|
|
556
|
+
node.remove();
|
|
557
|
+
});
|
|
558
|
+
applied = true;
|
|
559
|
+
}
|
|
560
|
+
return applied;
|
|
394
561
|
}
|
|
395
|
-
function handleInsert(editor, payload, dataSource) {
|
|
562
|
+
function handleInsert(editor, payload, dataSource, referenceNodeOverride) {
|
|
396
563
|
const { litexml, delay } = payload;
|
|
397
564
|
const isBefore = "beforeId" in payload;
|
|
398
565
|
const inode = dataSource.readLiteXMLToInode(litexml);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
566
|
+
let insertedNodeId = null;
|
|
567
|
+
try {
|
|
568
|
+
let referenceNode = referenceNodeOverride || null;
|
|
569
|
+
if (!referenceNode) {
|
|
402
570
|
if (isBefore) {
|
|
403
571
|
if (payload.beforeId === "root") referenceNode = $getRoot().getFirstChild();
|
|
404
|
-
else referenceNode = $
|
|
572
|
+
else referenceNode = $findNodeById(payload.beforeId);
|
|
405
573
|
} else if (payload.afterId === "root") referenceNode = $getRoot().getLastChild();
|
|
406
|
-
else referenceNode = $
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const referenceIndex = $getTableCellColumnIndex(cellReference);
|
|
419
|
-
if (!table || referenceIndex < 0) {
|
|
420
|
-
logger.error("❌ Table cell insertion requires a valid table parent.");
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
const rowWidthBefore = $getLogicalRowWidth(cellReference);
|
|
424
|
-
const insertionIndex = isBefore ? referenceIndex : referenceIndex + cellReference.getColSpan();
|
|
425
|
-
const insertedSpan = newNodes.reduce((total, node) => total + ($isTableCellNode(node) ? node.getColSpan() : 0), 0);
|
|
426
|
-
$updateTableWidthsForCellInsertion(table, rowWidthBefore, insertionIndex, insertedSpan);
|
|
427
|
-
let spanOffset = 0;
|
|
428
|
-
const cells = newNodes.map((node) => {
|
|
429
|
-
const result = delay ? $createTableCellDiffFromCell(editor, node, "add", `${table.getKey()}:column:${insertionIndex + spanOffset}`) : node;
|
|
430
|
-
spanOffset += node.getColSpan();
|
|
431
|
-
return result;
|
|
432
|
-
});
|
|
433
|
-
if (isBefore) cells.reverse().forEach((cell) => {
|
|
434
|
-
referenceNode = referenceNode.insertBefore(cell);
|
|
435
|
-
});
|
|
436
|
-
else cells.forEach((cell) => {
|
|
437
|
-
referenceNode = referenceNode.insertAfter(cell);
|
|
438
|
-
});
|
|
439
|
-
return;
|
|
574
|
+
else referenceNode = $findNodeById(payload.afterId);
|
|
575
|
+
}
|
|
576
|
+
if (!referenceNode) throw new Error("Reference node not found for insertion.");
|
|
577
|
+
const newNodes = inode.root.children.map((child) => $parseSerializedNodeImpl(child, editor));
|
|
578
|
+
$ensureUniqueNodeIds(newNodes);
|
|
579
|
+
insertedNodeId = newNodes.length ? $getNodeId(newNodes.at(-1)) || null : null;
|
|
580
|
+
const referencesTableCell = $isTableCellNode(referenceNode);
|
|
581
|
+
const insertsOnlyTableCells = newNodes.length > 0 && newNodes.every($isTableCellNode);
|
|
582
|
+
if (referencesTableCell || insertsOnlyTableCells) {
|
|
583
|
+
if (!referencesTableCell || !insertsOnlyTableCells) {
|
|
584
|
+
logger.error("❌ Table cells can only be inserted next to another cell in the same row.");
|
|
585
|
+
return null;
|
|
440
586
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
});
|
|
448
|
-
return;
|
|
587
|
+
const cellReference = referenceNode;
|
|
588
|
+
const table = $getTableForCell(cellReference);
|
|
589
|
+
const referenceIndex = $getTableCellColumnIndex(cellReference);
|
|
590
|
+
if (!table || referenceIndex < 0) {
|
|
591
|
+
logger.error("❌ Table cell insertion requires a valid table parent.");
|
|
592
|
+
return null;
|
|
449
593
|
}
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
594
|
+
const rowWidthBefore = $getLogicalRowWidth(cellReference);
|
|
595
|
+
const insertionIndex = isBefore ? referenceIndex : referenceIndex + cellReference.getColSpan();
|
|
596
|
+
const insertedSpan = newNodes.reduce((total, node) => total + ($isTableCellNode(node) ? node.getColSpan() : 0), 0);
|
|
597
|
+
$updateTableWidthsForCellInsertion(table, rowWidthBefore, insertionIndex, insertedSpan);
|
|
598
|
+
let spanOffset = 0;
|
|
599
|
+
const cells = newNodes.map((node) => {
|
|
600
|
+
const result = delay ? $createTableCellDiffFromCell(editor, node, "add", `${table.getKey()}:column:${insertionIndex + spanOffset}`) : node;
|
|
601
|
+
spanOffset += node.getColSpan();
|
|
602
|
+
return result;
|
|
603
|
+
});
|
|
604
|
+
if (isBefore) cells.reverse().forEach((cell) => {
|
|
605
|
+
referenceNode = referenceNode.insertBefore(cell);
|
|
606
|
+
});
|
|
607
|
+
else cells.forEach((cell) => {
|
|
608
|
+
referenceNode = referenceNode.insertAfter(cell);
|
|
609
|
+
});
|
|
610
|
+
return insertedNodeId;
|
|
611
|
+
}
|
|
612
|
+
if (!delay) {
|
|
613
|
+
if (isBefore) newNodes.reverse().forEach((node) => {
|
|
614
|
+
referenceNode = referenceNode.insertBefore(node);
|
|
615
|
+
});
|
|
616
|
+
else newNodes.forEach((node) => {
|
|
617
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
618
|
+
});
|
|
619
|
+
return insertedNodeId;
|
|
620
|
+
}
|
|
621
|
+
const referencesTableRow = $isTableRowNode(referenceNode);
|
|
622
|
+
const insertsOnlyTableRows = newNodes.every($isTableRowNode);
|
|
623
|
+
if (referencesTableRow || insertsOnlyTableRows) {
|
|
624
|
+
if (!referencesTableRow || !insertsOnlyTableRows || !$isTableNode(referenceNode.getParent())) {
|
|
625
|
+
logger.error("❌ Table rows can only be inserted next to another row in the same table.");
|
|
626
|
+
return null;
|
|
468
627
|
}
|
|
469
|
-
if (isBefore) {
|
|
470
|
-
if (
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
|
|
484
|
-
});
|
|
485
|
-
} else {
|
|
486
|
-
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
487
|
-
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
488
|
-
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
489
|
-
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
490
|
-
});
|
|
491
|
-
else wrapBlockModify(refBlock, editor, () => {
|
|
492
|
-
newNodes.forEach((node) => {
|
|
493
|
-
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
} else if (referenceNode.isInline() === false) {
|
|
628
|
+
if (isBefore) newNodes.reverse().forEach((node) => {
|
|
629
|
+
if (!$isTableRowNode(node)) return;
|
|
630
|
+
const diffRow = $createTableRowDiffFromRow(editor, node, "add");
|
|
631
|
+
referenceNode = referenceNode.insertBefore(diffRow);
|
|
632
|
+
});
|
|
633
|
+
else newNodes.forEach((node) => {
|
|
634
|
+
if (!$isTableRowNode(node)) return;
|
|
635
|
+
const diffRow = $createTableRowDiffFromRow(editor, node, "add");
|
|
636
|
+
referenceNode = referenceNode.insertAfter(diffRow);
|
|
637
|
+
});
|
|
638
|
+
return insertedNodeId;
|
|
639
|
+
}
|
|
640
|
+
if (isBefore) {
|
|
641
|
+
if (referenceNode.isInline() === false) {
|
|
498
642
|
const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
|
|
499
643
|
if (originDiffNode) referenceNode = originDiffNode;
|
|
500
|
-
newNodes.
|
|
501
|
-
if (
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
diffNode.append(child);
|
|
506
|
-
});
|
|
507
|
-
node.append(diffNode);
|
|
508
|
-
referenceNode = referenceNode.insertAfter(node);
|
|
509
|
-
} else {
|
|
510
|
-
const diffNode = $createDiffNode("add");
|
|
511
|
-
diffNode.append(node);
|
|
512
|
-
referenceNode = referenceNode.insertAfter(diffNode);
|
|
513
|
-
}
|
|
644
|
+
newNodes.map((node) => {
|
|
645
|
+
if ($isListItemNode(node)) {
|
|
646
|
+
const diffNode = $createDiffNode("listItemAdd");
|
|
647
|
+
node.getChildren().forEach((child) => diffNode.append(child));
|
|
648
|
+
return node.append(diffNode);
|
|
514
649
|
}
|
|
650
|
+
const diffNode = $createDiffNode("add");
|
|
651
|
+
diffNode.append(node);
|
|
652
|
+
return diffNode;
|
|
653
|
+
}).reverse().forEach((diffNode) => {
|
|
654
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
|
|
515
655
|
});
|
|
516
656
|
} else {
|
|
517
657
|
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
518
658
|
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
519
659
|
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
520
|
-
if (referenceNode) referenceNode = referenceNode.
|
|
660
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
521
661
|
});
|
|
522
662
|
else wrapBlockModify(refBlock, editor, () => {
|
|
523
663
|
newNodes.forEach((node) => {
|
|
524
|
-
if (referenceNode) referenceNode = referenceNode.
|
|
664
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
525
665
|
});
|
|
526
666
|
});
|
|
527
667
|
}
|
|
528
|
-
}
|
|
529
|
-
|
|
668
|
+
} else if (referenceNode.isInline() === false) {
|
|
669
|
+
const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
|
|
670
|
+
if (originDiffNode) referenceNode = originDiffNode;
|
|
671
|
+
newNodes.forEach((node) => {
|
|
672
|
+
if (referenceNode) {
|
|
673
|
+
if ($isListItemNode(node)) {
|
|
674
|
+
const diffNode = $createDiffNode("listItemAdd");
|
|
675
|
+
node.getChildren().forEach((child) => {
|
|
676
|
+
diffNode.append(child);
|
|
677
|
+
});
|
|
678
|
+
node.append(diffNode);
|
|
679
|
+
referenceNode = referenceNode.insertAfter(node);
|
|
680
|
+
} else {
|
|
681
|
+
const diffNode = $createDiffNode("add");
|
|
682
|
+
diffNode.append(node);
|
|
683
|
+
referenceNode = referenceNode.insertAfter(diffNode);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
} else {
|
|
688
|
+
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
689
|
+
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
690
|
+
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
691
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
692
|
+
});
|
|
693
|
+
else wrapBlockModify(refBlock, editor, () => {
|
|
694
|
+
newNodes.forEach((node) => {
|
|
695
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
696
|
+
});
|
|
697
|
+
});
|
|
530
698
|
}
|
|
531
|
-
})
|
|
699
|
+
} catch (error) {
|
|
700
|
+
logger.error("❌ Error inserting node:", error);
|
|
701
|
+
insertedNodeId = null;
|
|
702
|
+
}
|
|
703
|
+
return insertedNodeId;
|
|
532
704
|
}
|
|
533
705
|
//#endregion
|
|
534
|
-
export { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, registerLiteXMLCommand };
|
|
706
|
+
export { LITEXML_APPLY_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND, registerLiteXMLCommand };
|