@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.
Files changed (48) hide show
  1. package/README.md +86 -0
  2. package/es/editor-kernel/index.d.ts +2 -2
  3. package/es/editor-kernel/index.js +2 -2
  4. package/es/headless/index.d.ts +5 -2
  5. package/es/headless/index.js +20 -22
  6. package/es/headless.d.ts +6 -507
  7. package/es/headless.js +906 -411
  8. package/es/i-node-identity-service-BBC_bg97.d.ts +522 -0
  9. package/es/index.d.ts +4 -2
  10. package/es/index.js +5 -2
  11. package/es/plugins/block/react/ReactBlockPlugin.js +0 -3
  12. package/es/plugins/common/data-source/json-data-source.js +33 -25
  13. package/es/plugins/common/data-source/text-data-source.js +6 -1
  14. package/es/plugins/common/index.d.ts +3 -1
  15. package/es/plugins/common/node/node-id.d.ts +7 -0
  16. package/es/plugins/common/node/node-id.js +484 -0
  17. package/es/plugins/common/node/node-identity-policy.d.ts +26 -0
  18. package/es/plugins/common/node/node-identity-policy.js +68 -0
  19. package/es/plugins/common/plugin/index.js +28 -2
  20. package/es/plugins/common/service/i-node-identity-service.d.ts +19 -0
  21. package/es/plugins/common/service/i-node-identity-service.js +4 -0
  22. package/es/plugins/common/service/node-identity-service.js +87 -0
  23. package/es/plugins/common/utils/index.js +3 -1
  24. package/es/plugins/list/plugin/index.js +7 -1
  25. package/es/plugins/litexml/command/index.d.ts +2 -2
  26. package/es/plugins/litexml/command/index.js +464 -292
  27. package/es/plugins/litexml/command/symbols.d.ts +18 -7
  28. package/es/plugins/litexml/command/symbols.js +2 -1
  29. package/es/plugins/litexml/data-source/litexml-data-source.js +35 -32
  30. package/es/plugins/litexml/diff-validation.js +68 -14
  31. package/es/plugins/litexml/index.d.ts +2 -2
  32. package/es/plugins/litexml/node/identity-policy.js +91 -0
  33. package/es/plugins/litexml/plugin/index.js +6 -0
  34. package/es/plugins/litexml/table-cell-diff.js +4 -0
  35. package/es/plugins/litexml/table-row-diff.js +4 -0
  36. package/es/plugins/litexml/utils/index.js +10 -23
  37. package/es/plugins/markdown/command/index.js +77 -4
  38. package/es/plugins/markdown/data-source/markdown-data-source.js +6 -1
  39. package/es/plugins/math/plugin/index.js +2 -2
  40. package/es/plugins/slash/service/i-slash-service.d.ts +2 -2
  41. package/es/plugins/yjs/plugin/index.js +5 -3
  42. package/es/plugins/yjs/plugin/utils/editor-state.js +8 -1
  43. package/es/plugins/yjs/plugin/utils/node-identity.js +106 -0
  44. package/es/plugins/yjs/service/index.js +38 -4
  45. package/es/{symbols-Do7jD4Tt.d.ts → symbols-Dyr9yyS0.d.ts} +18 -7
  46. package/package.json +4 -4
  47. package/scripts/postinstall-lexical-patch.cjs +0 -21
  48. package/patches/lexical@0.42.0.patch +0 -82
@@ -22,21 +22,32 @@ declare enum DiffAction {
22
22
  Reject = 0,
23
23
  Accept = 1
24
24
  }
25
- declare const LITEXML_MODIFY_COMMAND: import("lexical").LexicalCommand<({
26
- action: "insert";
25
+ type LiteXmlModifyOperation = {
26
+ action: 'insert';
27
27
  beforeId: string;
28
28
  litexml: string;
29
29
  } | {
30
- action: "insert";
30
+ action: 'insert';
31
31
  afterId: string;
32
32
  litexml: string;
33
33
  } | {
34
- action: "remove";
34
+ action: 'remove';
35
35
  id: string;
36
36
  } | {
37
- action: "modify";
37
+ action: 'modify';
38
38
  litexml: string | string[];
39
- })[]>;
39
+ };
40
+ interface LiteXmlOperationResult {
41
+ action: LiteXmlModifyOperation['action'];
42
+ index: number;
43
+ reason?: string;
44
+ status: 'applied' | 'failed';
45
+ }
46
+ declare const LITEXML_MODIFY_COMMAND: import("lexical").LexicalCommand<LiteXmlModifyOperation[]>;
47
+ declare const LITEXML_MODIFY_WITH_RESULTS_COMMAND: import("lexical").LexicalCommand<{
48
+ onResults: (results: LiteXmlOperationResult[]) => void;
49
+ operations: ReadonlyArray<LiteXmlModifyOperation>;
50
+ }>;
40
51
  declare const LITEXML_APPLY_COMMAND: import("lexical").LexicalCommand<{
41
52
  delay?: boolean;
42
53
  litexml: string | string[];
@@ -62,4 +73,4 @@ declare const LITEXML_DIFFNODE_ALL_COMMAND: import("lexical").LexicalCommand<{
62
73
  action: DiffAction;
63
74
  }>;
64
75
  //#endregion
65
- export { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND };
76
+ export { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND, LiteXmlModifyOperation, LiteXmlOperationResult };
@@ -25,10 +25,11 @@ let DiffAction = /* @__PURE__ */ function(DiffAction) {
25
25
  return DiffAction;
26
26
  }({});
27
27
  const LITEXML_MODIFY_COMMAND = createCommand("LITEXML_MODIFY_COMMAND");
28
+ const LITEXML_MODIFY_WITH_RESULTS_COMMAND = createCommand("LITEXML_MODIFY_WITH_RESULTS_COMMAND");
28
29
  const LITEXML_APPLY_COMMAND = createCommand("LITEXML_APPLY_COMMAND");
29
30
  const LITEXML_REMOVE_COMMAND = createCommand("LITEXML_REMOVE_COMMAND");
30
31
  const LITEXML_INSERT_COMMAND = createCommand("LITEXML_INSERT_COMMAND");
31
32
  const LITEXML_DIFFNODE_COMMAND = createCommand("LITEXML_DIFFNODE_COMMAND");
32
33
  const LITEXML_DIFFNODE_ALL_COMMAND = createCommand("LITEXML_DIFFNODE_ALL_COMMAND");
33
34
  //#endregion
34
- export { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND };
35
+ export { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND };
@@ -1,13 +1,14 @@
1
1
  import { createDebugLogger, init_debug } from "../../../utils/debug.js";
2
2
  import { INodeHelper, init_helper } from "../../../editor-kernel/inode/helper.js";
3
+ import { $getNodeId, $normalizeNodeIds, init_node_id } from "../../common/node/node-id.js";
3
4
  import DataSource from "../../../editor-kernel/data-source.js";
4
- import { $parseSerializedNodeImpl, charToId, idToChar } from "../utils/index.js";
5
5
  import { INodeService } from "../../inode/service/index.js";
6
6
  import { LitexmlService } from "../service/litexml-service.js";
7
7
  import { $getRoot, $getSelection, $isElementNode, $isRangeSelection } from "lexical";
8
8
  import { DOMParser } from "@xmldom/xmldom";
9
9
  //#region src/plugins/litexml/data-source/litexml-data-source.ts
10
10
  init_helper();
11
+ init_node_id();
11
12
  init_debug();
12
13
  const logger = createDebugLogger("plugin", "litexml");
13
14
  var IXmlWriterContext = class {
@@ -48,13 +49,8 @@ var LitexmlDataSource = class extends DataSource {
48
49
  read(editor, data) {
49
50
  try {
50
51
  const inode = this.readLiteXMLToInode(data);
51
- const newState = editor.parseEditorState({ root: INodeHelper.createRootNode() }, (state) => {
52
- try {
53
- const root = $parseSerializedNodeImpl(inode.root, editor, true, state);
54
- state._nodeMap.set(root.getKey(), root);
55
- } catch (error) {
56
- console.error(error);
57
- }
52
+ const newState = editor.parseEditorState(inode, () => {
53
+ $normalizeNodeIds($getRoot());
58
54
  });
59
55
  editor.setEditorState(newState);
60
56
  } catch (error) {
@@ -120,6 +116,22 @@ var LitexmlDataSource = class extends DataSource {
120
116
  processXMLElement(xmlElement, parentNode) {
121
117
  const tagName = xmlElement.tagName.toLowerCase();
122
118
  const customReaders = this.litexmlService.getXMLReaders();
119
+ const publicId = xmlElement.getAttribute("id") || void 0;
120
+ const attachPublicId = (node, id = publicId) => {
121
+ if (!node || !id) return;
122
+ node.id = id;
123
+ node.$ = {
124
+ ...node.$,
125
+ properties: {
126
+ ...node.$?.properties,
127
+ nodeId: id
128
+ }
129
+ };
130
+ };
131
+ const append = (node) => {
132
+ attachPublicId(node);
133
+ INodeHelper.appendChild(parentNode, node);
134
+ };
123
135
  if (customReaders[tagName]) {
124
136
  const readerOrReaders = customReaders[tagName];
125
137
  const readers = Array.isArray(readerOrReaders) ? readerOrReaders : [readerOrReaders];
@@ -129,16 +141,9 @@ var LitexmlDataSource = class extends DataSource {
129
141
  const result = reader(xmlElement, children);
130
142
  if (result !== false) {
131
143
  if (Array.isArray(result)) {
132
- if (result.length > 0) {
133
- const attrId = xmlElement.getAttribute("id");
134
- result[0].id = attrId ? charToId(attrId) : void 0;
135
- }
144
+ if (result.length > 0) attachPublicId(result[0]);
136
145
  INodeHelper.appendChild(parentNode, ...result);
137
- } else if (result) {
138
- const attrId = xmlElement.getAttribute("id");
139
- result.id = attrId ? charToId(attrId) : void 0;
140
- INodeHelper.appendChild(parentNode, result);
141
- }
146
+ } else if (result) append(result);
142
147
  return;
143
148
  }
144
149
  }
@@ -148,7 +153,7 @@ var LitexmlDataSource = class extends DataSource {
148
153
  case "paragraph": {
149
154
  const paragraph = INodeHelper.createParagraph();
150
155
  this.processXMLChildren(xmlElement, paragraph);
151
- INodeHelper.appendChild(parentNode, paragraph);
156
+ append(paragraph);
152
157
  break;
153
158
  }
154
159
  case "h1":
@@ -163,7 +168,7 @@ var LitexmlDataSource = class extends DataSource {
163
168
  tag: `h${level}`
164
169
  });
165
170
  this.processXMLChildren(xmlElement, heading);
166
- INodeHelper.appendChild(parentNode, heading);
171
+ append(heading);
167
172
  break;
168
173
  }
169
174
  case "ul":
@@ -175,6 +180,7 @@ var LitexmlDataSource = class extends DataSource {
175
180
  children: [],
176
181
  value: 1
177
182
  });
183
+ attachPublicId(listItem, child.getAttribute("id") || void 0);
178
184
  this.processXMLChildren(child, listItem);
179
185
  INodeHelper.appendChild(parentNode, listItem);
180
186
  });
@@ -182,20 +188,15 @@ var LitexmlDataSource = class extends DataSource {
182
188
  case "blockquote": {
183
189
  const quote = INodeHelper.createElementNode("quote", { children: [] });
184
190
  this.processXMLChildren(xmlElement, quote);
185
- INodeHelper.appendChild(parentNode, quote);
191
+ append(quote);
186
192
  break;
187
193
  }
188
- case "code": {
189
- const codeNode = INodeHelper.createElementNode("codeInline", { children: [INodeHelper.createTextNode(xmlElement.textContent || "")] });
190
- INodeHelper.appendChild(parentNode, codeNode);
194
+ case "code":
195
+ append(INodeHelper.createElementNode("codeInline", { children: [INodeHelper.createTextNode(xmlElement.textContent || "")] }));
191
196
  break;
192
- }
193
197
  case "text": {
194
198
  const textContent = xmlElement.textContent || "";
195
- if (textContent) {
196
- const textNode = INodeHelper.createTextNode(textContent);
197
- INodeHelper.appendChild(parentNode, textNode);
198
- }
199
+ if (textContent) append(INodeHelper.createTextNode(textContent));
199
200
  break;
200
201
  }
201
202
  default: this.processXMLChildren(xmlElement, parentNode);
@@ -246,10 +247,12 @@ var LitexmlDataSource = class extends DataSource {
246
247
  lines.push(...handled.lines);
247
248
  return;
248
249
  }
249
- const attrs = this.buildXMLAttributes({
250
- id: idToChar(node.getKey()),
250
+ const attributes = {
251
+ id: $getNodeId(node),
251
252
  ...handled.attributes
252
- });
253
+ };
254
+ attributes.id = $getNodeId(node);
255
+ const attrs = this.buildXMLAttributes(attributes);
253
256
  const openTag = `${indentStr}<${handled.tagName}${attrs}>`;
254
257
  const closeTag = `</${handled.tagName}>`;
255
258
  if (handled.textContent) lines.push(`${openTag}${handled.textContent}${closeTag}`);
@@ -281,7 +284,7 @@ var LitexmlDataSource = class extends DataSource {
281
284
  * Escape XML special characters
282
285
  */
283
286
  escapeXML(text) {
284
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
287
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll(" ", "&#9;").replaceAll("\n", "&#10;").replaceAll("\r", "&#13;");
285
288
  }
286
289
  };
287
290
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { getLiteXmlIdentityProjection } from "./node/identity-policy.js";
1
2
  //#region src/plugins/litexml/diff-validation.ts
2
3
  const ACTIONABLE_DIFF_TYPES = /* @__PURE__ */ new Set([
3
4
  "add",
@@ -9,6 +10,11 @@ const ACTIONABLE_DIFF_TYPES = /* @__PURE__ */ new Set([
9
10
  ]);
10
11
  const isActionableDiff = (node) => node?.type === "diff" && ACTIONABLE_DIFF_TYPES.has(node.diffType || "");
11
12
  const isElementNode = (node) => Array.isArray(node.children);
13
+ const getNodeId = (node) => {
14
+ if (typeof node.id === "string" && node.id.length > 0) return node.id;
15
+ const properties = node.$?.properties;
16
+ return typeof properties?.nodeId === "string" ? properties.nodeId : void 0;
17
+ };
12
18
  const cloneNode = (node) => structuredClone(node);
13
19
  function hasActionableDiffDescendant(node) {
14
20
  return (node.children || []).some((child) => isActionableDiff(child) || hasActionableDiffDescendant(child));
@@ -34,10 +40,10 @@ function collectIllegalNestedDiffPaths(root) {
34
40
  }
35
41
  if (!isElementNode(node)) return;
36
42
  node.children?.forEach((child) => {
37
- visit(child, [...ancestors, node], `${path}/${child.id || child.type || "node"}`);
43
+ visit(child, [...ancestors, node], `${path}/${getNodeId(child) || child.type || "node"}`);
38
44
  });
39
45
  };
40
- visit(root, [], root.id || root.type || "root");
46
+ visit(root, [], getNodeId(root) || root.type || "root");
41
47
  return violations;
42
48
  }
43
49
  function findNewIllegalDiffPaths(previous, projected) {
@@ -54,25 +60,52 @@ function findNewIllegalDiffPaths(previous, projected) {
54
60
  });
55
61
  return newPaths;
56
62
  }
57
- function findNodeLocation(root, id, ancestors = []) {
58
- if (root.id === id) return {
63
+ function findActiveNodeLocation(root, id, ancestors = []) {
64
+ const projection = getLiteXmlIdentityProjection({
65
+ diffType: root.diffType,
66
+ firstChildDiffType: root.children?.[0]?.diffType,
67
+ firstChildType: root.children?.[0]?.type,
68
+ type: root.type
69
+ });
70
+ if (projection === "hidden") return null;
71
+ if (projection === "content" && getNodeId(root) === id) {
72
+ const parent = ancestors.at(-1) || null;
73
+ return {
74
+ ancestors,
75
+ index: parent ? (parent.children || []).indexOf(root) : -1,
76
+ node: root,
77
+ parent
78
+ };
79
+ }
80
+ const activeChildren = typeof projection === "object" ? root.children?.[projection.childIndex] ? [root.children[projection.childIndex]] : [] : root.children || [];
81
+ for (const child of activeChildren) {
82
+ const nested = findActiveNodeLocation(child, id, [...ancestors, root]);
83
+ if (nested) return nested;
84
+ }
85
+ return null;
86
+ }
87
+ function findNodeLocationByNode(root, target, ancestors = []) {
88
+ if (root === target) return {
59
89
  ancestors,
60
90
  index: -1,
61
91
  node: root,
62
92
  parent: null
63
93
  };
64
94
  for (const [index, child] of (root.children || []).entries()) {
65
- if (child.id === id) return {
95
+ if (child === target) return {
66
96
  ancestors: [...ancestors, root],
67
97
  index,
68
98
  node: child,
69
99
  parent: root
70
100
  };
71
- const nested = findNodeLocation(child, id, [...ancestors, root]);
101
+ const nested = findNodeLocationByNode(child, target, [...ancestors, root]);
72
102
  if (nested) return nested;
73
103
  }
74
104
  return null;
75
105
  }
106
+ function hasActiveLiteXmlNodeId(root, id) {
107
+ return findActiveNodeLocation(root, id) !== null;
108
+ }
76
109
  function replaceNode(location, replacements) {
77
110
  if (!location.parent || location.index < 0) return;
78
111
  location.parent.children?.splice(location.index, 1, ...replacements);
@@ -92,8 +125,9 @@ function createDiffContent(side, children) {
92
125
  };
93
126
  }
94
127
  function projectModifyTarget(root, incoming) {
95
- if (!incoming.id) return;
96
- const location = findNodeLocation(root, incoming.id);
128
+ const incomingId = getNodeId(incoming);
129
+ if (!incomingId) return;
130
+ const location = findActiveNodeLocation(root, incomingId);
97
131
  if (!location) return;
98
132
  const target = location.node;
99
133
  const next = cloneNode(incoming);
@@ -127,10 +161,30 @@ function projectModifyTarget(root, incoming) {
127
161
  replaceNode(location, [next]);
128
162
  return;
129
163
  }
164
+ if (target.type === "listitem") {
165
+ const existingDiff = (target.children || []).find((child) => child.type === "diff");
166
+ if (existingDiff?.diffType === "listItemModify") {
167
+ const after = existingDiff.children?.[1];
168
+ if (after) after.children = cloneNode(next.children || []);
169
+ return;
170
+ }
171
+ if (existingDiff?.diffType === "listItemAdd") {
172
+ existingDiff.children = cloneNode(next.children || []);
173
+ return;
174
+ }
175
+ target.children = [createDiff("listItemModify", [{
176
+ children: cloneNode(target.children || []),
177
+ type: "paragraph"
178
+ }, {
179
+ children: cloneNode(next.children || []),
180
+ type: "paragraph"
181
+ }])];
182
+ return;
183
+ }
130
184
  replaceNode(location, [createDiff("modify", [cloneNode(target), next])]);
131
185
  }
132
186
  function projectRemoveTarget(root, id) {
133
- const location = findNodeLocation(root, id);
187
+ const location = findActiveNodeLocation(root, id);
134
188
  if (!location) return;
135
189
  if (location.ancestors.some(isActionableDiff)) {
136
190
  replaceNode(location, []);
@@ -157,13 +211,13 @@ function projectRemoveTarget(root, id) {
157
211
  }
158
212
  function projectInsert(root, operation, readXml) {
159
213
  const referenceId = "beforeId" in operation ? operation.beforeId : operation.afterId;
160
- const reference = referenceId === "root" ? null : findNodeLocation(root, referenceId)?.node || null;
214
+ const reference = referenceId === "root" ? null : findActiveNodeLocation(root, referenceId)?.node || null;
161
215
  const location = referenceId === "root" ? {
162
216
  ancestors: [root],
163
217
  index: "beforeId" in operation ? 0 : (root.children || []).length - 1,
164
218
  node: root.children?.[0] || root,
165
219
  parent: root
166
- } : findNodeLocation(root, referenceId);
220
+ } : findActiveNodeLocation(root, referenceId);
167
221
  if (!location) return;
168
222
  const incoming = (readXml(operation.litexml).root?.children || []).map(cloneNode);
169
223
  if (incoming.length === 0 || !location.parent) return;
@@ -202,12 +256,12 @@ function projectInsert(root, operation, readXml) {
202
256
  ].includes(node.type || ""));
203
257
  if (!actionableAncestor && blockAncestor && hasActionableDiffDescendant(blockAncestor)) {
204
258
  const wrapped = createDiff("modify", [cloneNode(blockAncestor), cloneNode(blockAncestor)]);
205
- const blockLocation = findNodeLocation(root, blockAncestor.id || "");
259
+ const blockLocation = findNodeLocationByNode(root, blockAncestor);
206
260
  if (blockLocation) replaceNode(blockLocation, [wrapped]);
207
261
  return;
208
262
  }
209
263
  if (actionableAncestor && !isInlineReference) {
210
- const originLocation = actionableAncestor.id ? findNodeLocation(root, actionableAncestor.id) : null;
264
+ const originLocation = findNodeLocationByNode(root, actionableAncestor);
211
265
  if (originLocation?.parent && originLocation.index >= 0) {
212
266
  const originInsertAt = "beforeId" in operation ? originLocation.index : originLocation.index + 1;
213
267
  originLocation.parent.children?.splice(originInsertAt, 0, ...incoming.map((node) => createDiff("add", [node])));
@@ -234,4 +288,4 @@ function projectLiteXmlOperation(document, operation, readXml) {
234
288
  return projected;
235
289
  }
236
290
  //#endregion
237
- export { collectIllegalNestedDiffPaths, findNewIllegalDiffPaths, hasActionableDiffDescendant, projectLiteXmlOperation };
291
+ export { collectIllegalNestedDiffPaths, findNewIllegalDiffPaths, hasActionableDiffDescendant, hasActiveLiteXmlNodeId, projectLiteXmlOperation };
@@ -1,9 +1,9 @@
1
1
  import { ILitexmlService, LitexmlService, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord } from "./service/litexml-service.js";
2
2
  import LitexmlDataSource from "./data-source/litexml-data-source.js";
3
- import { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND } from "./command/symbols.js";
3
+ import { DiffAction, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND, LiteXmlModifyOperation, LiteXmlOperationResult } from "./command/symbols.js";
4
4
  import "./command/index.js";
5
5
  import "./command/diffCommand.js";
6
6
  import { LitexmlPlugin, LitexmlPluginOptions } from "./plugin/index.js";
7
7
  import { ReactLiteXmlPlugin } from "./react/index.js";
8
8
  import { useHasDiffNode } from "./react/hooks/useHasDiffNode.js";
9
- export { DiffAction, ILitexmlService, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LitexmlDataSource, LitexmlPlugin, type LitexmlPluginOptions, LitexmlService, ReactLiteXmlPlugin, type XMLReaderFunc, type XMLReaderRecord, type XMLWriterFunc, type XMLWriterRecord, useHasDiffNode };
9
+ export { DiffAction, ILitexmlService, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND, type LiteXmlModifyOperation, LitexmlDataSource, LitexmlPlugin, type LitexmlPluginOptions, LitexmlService, ReactLiteXmlPlugin, type XMLReaderFunc, type XMLReaderRecord, type XMLWriterFunc, type XMLWriterRecord, useHasDiffNode };
@@ -0,0 +1,91 @@
1
+ import { $isElementNode } from "lexical";
2
+ //#region src/plugins/litexml/node/identity-policy.ts
3
+ const afterRepresentation = Object.freeze({ childIndex: 1 });
4
+ /** Shared active-view rule for live Lexical nodes and serialized preflight trees. */
5
+ function getLiteXmlIdentityProjection(node) {
6
+ if (node.type === "root" || node.type === "diff-content") return "container";
7
+ if (node.type === "diff") {
8
+ if (node.diffType === "remove" || node.diffType === "listItemRemove") return "hidden";
9
+ if (node.diffType === "modify" || node.diffType === "listItemModify") return afterRepresentation;
10
+ return "container";
11
+ }
12
+ if ((node.type === "table-cell-diff" || node.type === "table-row-diff") && node.diffType === "remove") return "hidden";
13
+ if (node.type === "listitem" && node.firstChildType === "diff" && node.firstChildDiffType === "listItemRemove") return "hidden";
14
+ return "content";
15
+ }
16
+ /** Review containers and the document root are physical structure, not addressable content. */
17
+ function isLiteXmlIdentityNode(type) {
18
+ return type !== "root" && type !== "diff" && type !== "diff-content";
19
+ }
20
+ function getDiffType(node) {
21
+ if (!node) return void 0;
22
+ if ("diffType" in node && typeof node.diffType === "string") return node.diffType;
23
+ if ("getDiffType" in node && typeof node.getDiffType === "function") return node.getDiffType();
24
+ }
25
+ function getLiveProjection(node) {
26
+ const type = node.getType();
27
+ if (type === "root" || type === "diff-content") return "container";
28
+ if (type !== "diff" && type !== "table-cell-diff" && type !== "table-row-diff" && type !== "listitem") return "content";
29
+ const firstChild = $isElementNode(node) ? node.getFirstChild() : null;
30
+ return getLiteXmlIdentityProjection({
31
+ diffType: getDiffType(node),
32
+ firstChildDiffType: getDiffType(firstChild),
33
+ firstChildType: firstChild?.getType(),
34
+ type
35
+ });
36
+ }
37
+ function getTableReviewSide(node) {
38
+ const type = node.getType();
39
+ if (type !== "table-cell-diff" && type !== "table-row-diff") return null;
40
+ if (!("getChangeId" in node) || typeof node.getChangeId !== "function") return null;
41
+ const changeId = node.getChangeId();
42
+ const side = getDiffType(node);
43
+ return changeId && side ? {
44
+ group: `${type}:${changeId}`,
45
+ side
46
+ } : null;
47
+ }
48
+ function getReviewSide(node) {
49
+ const selfSide = getTableReviewSide(node);
50
+ if (selfSide) return selfSide;
51
+ let branch = node;
52
+ for (let parent = node.getParent(); parent; branch = parent, parent = parent.getParent()) {
53
+ const parentDiffType = getDiffType(parent);
54
+ if (parent.getType() === "diff") {
55
+ if (parentDiffType === "modify") {
56
+ if (branch.getType() === "diff-content" && "side" in branch) return {
57
+ group: `diff:${parent.getKey()}`,
58
+ side: String(branch.side)
59
+ };
60
+ const branchIndex = parent.getChildren().indexOf(branch);
61
+ if (branchIndex >= 0 && branchIndex < 2) return {
62
+ group: `diff:${parent.getKey()}`,
63
+ side: branchIndex === 0 ? "before" : "after"
64
+ };
65
+ }
66
+ if (parentDiffType === "listItemModify") {
67
+ const branchIndex = parent.getChildren().indexOf(branch);
68
+ if (branchIndex >= 0 && branchIndex < 2) return {
69
+ group: `diff:${parent.getKey()}`,
70
+ side: branchIndex === 0 ? "before" : "after"
71
+ };
72
+ }
73
+ }
74
+ const tableSide = getTableReviewSide(parent);
75
+ if (tableSide) return tableSide;
76
+ }
77
+ return null;
78
+ }
79
+ const liteXmlIdentityPolicy = {
80
+ canShareId(left, right) {
81
+ const leftSide = getReviewSide(left);
82
+ const rightSide = getReviewSide(right);
83
+ return Boolean(leftSide && rightSide && leftSide.group === rightSide.group && leftSide.side !== rightSide.side);
84
+ },
85
+ isIdentityNode(node) {
86
+ return isLiteXmlIdentityNode(node.getType());
87
+ },
88
+ project: getLiveProjection
89
+ };
90
+ //#endregion
91
+ export { getLiteXmlIdentityProjection, isLiteXmlIdentityNode, liteXmlIdentityPolicy };
@@ -1,4 +1,6 @@
1
+ import { init_node_identity_policy, registerNodeIdentityPolicy } from "../../common/node/node-identity-policy.js";
1
2
  import { KernelPlugin, init_plugin } from "../../../editor-kernel/plugin.js";
3
+ import { liteXmlIdentityPolicy } from "../node/identity-policy.js";
2
4
  import { $isDiffContentNode, DiffContentNode } from "../node/DiffContentNode.js";
3
5
  import { DiffNode } from "../node/DiffNode.js";
4
6
  import { $isTableCellDiffNode, TableCellDiffNode } from "../node/TableCellDiffNode.js";
@@ -14,6 +16,7 @@ import { $isTableNode } from "@lexical/table";
14
16
  import { $nodesOfType, HISTORIC_TAG } from "lexical";
15
17
  //#region src/plugins/litexml/plugin/index.ts
16
18
  init_plugin();
19
+ init_node_identity_policy();
17
20
  /**
18
21
  * LitexmlPlugin - A plugin that provides XML-based data source support
19
22
  * Allows converting between Lexical editor state and XML format
@@ -52,6 +55,7 @@ const LitexmlPlugin = class extends KernelPlugin {
52
55
  if (config?.enabled !== false) kernel.registerDataSource(this.datasource);
53
56
  }
54
57
  onInit(editor) {
58
+ this.register(registerNodeIdentityPolicy(editor, liteXmlIdentityPolicy));
55
59
  this.register(registerLiteXMLCommand(editor, this.datasource));
56
60
  this.register(registerLiteXMLDiffCommand(editor));
57
61
  this.register(registerLegacyTableCellDiffNormalization(editor));
@@ -91,6 +95,7 @@ const LitexmlPlugin = class extends KernelPlugin {
91
95
  diffNode.getChildren().forEach((child) => nodeToXML(child, lines, indent));
92
96
  break;
93
97
  case "remove": break;
98
+ case "listItemRemove": break;
94
99
  case "listItemModify": diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
95
100
  nodeToXML(child, lines, indent);
96
101
  });
@@ -143,6 +148,7 @@ const LitexmlPlugin = class extends KernelPlugin {
143
148
  diffNode.getChildren().forEach((child) => ctx.processChild(ctx, child));
144
149
  break;
145
150
  case "remove": break;
151
+ case "listItemRemove": break;
146
152
  case "listItemModify": diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
147
153
  ctx.processChild(ctx, child);
148
154
  });
@@ -1,9 +1,11 @@
1
+ import { $copyNodeProperties, init_node_id } from "../common/node/node-id.js";
1
2
  import { $createDiffNode, DiffNode } from "./node/DiffNode.js";
2
3
  import { $createTableCellDiffNode, TableCellDiffNode } from "./node/TableCellDiffNode.js";
3
4
  import { $cloneNode } from "./utils/index.js";
4
5
  import { $createTableCellNode, $isTableCellNode, $isTableNode, $isTableRowNode, TableNode } from "@lexical/table";
5
6
  import { $nodesOfType } from "lexical";
6
7
  //#region src/plugins/litexml/table-cell-diff.ts
8
+ init_node_id();
7
9
  function copyCellStructure(source, target) {
8
10
  target.setRowSpan(source.getRowSpan());
9
11
  target.setBackgroundColor(source.getBackgroundColor());
@@ -11,6 +13,7 @@ function copyCellStructure(source, target) {
11
13
  }
12
14
  function $createTableCellDiffFromCell(editor, cell, diffType, changeId) {
13
15
  const diffCell = $createTableCellDiffNode(diffType, changeId, cell.getHeaderStyles(), cell.getColSpan(), cell.getWidth());
16
+ $copyNodeProperties(cell, diffCell);
14
17
  copyCellStructure(cell, diffCell);
15
18
  const diff = $createDiffNode(diffType);
16
19
  diff.append(...cell.getChildren().map((child) => $cloneNode(child, editor)));
@@ -19,6 +22,7 @@ function $createTableCellDiffFromCell(editor, cell, diffType, changeId) {
19
22
  }
20
23
  function $createPlainTableCellFromDiff(editor, cell) {
21
24
  const plainCell = $createTableCellNode(cell.getHeaderStyles(), cell.getColSpan(), cell.getWidth());
25
+ $copyNodeProperties(cell, plainCell);
22
26
  copyCellStructure(cell, plainCell);
23
27
  const diff = cell.getFirstChild();
24
28
  if (diff instanceof DiffNode) plainCell.append(...diff.getChildren().map((child) => $cloneNode(child, editor)));
@@ -1,8 +1,10 @@
1
+ import { $copyNodeProperties, init_node_id } from "../common/node/node-id.js";
1
2
  import { $cloneNode } from "./utils/index.js";
2
3
  import { $createTableRowDiffNode, TableRowDiffNode } from "./node/TableRowDiffNode.js";
3
4
  import { $createTableRowNode, $isTableCellNode, $isTableNode } from "@lexical/table";
4
5
  import { $nodesOfType } from "lexical";
5
6
  //#region src/plugins/litexml/table-row-diff.ts
7
+ init_node_id();
6
8
  function $areTableRowStructuresCompatible(previousRow, nextRow) {
7
9
  const previousCells = previousRow.getChildren();
8
10
  const nextCells = nextRow.getChildren();
@@ -15,11 +17,13 @@ function $areTableRowStructuresCompatible(previousRow, nextRow) {
15
17
  function $createTableRowDiffFromRow(editor, row, diffType, changeId) {
16
18
  const source = $cloneNode(row, editor);
17
19
  const diffRow = $createTableRowDiffNode(diffType, changeId, row.getHeight());
20
+ $copyNodeProperties(row, diffRow);
18
21
  diffRow.append(...source.getChildren());
19
22
  return diffRow;
20
23
  }
21
24
  function $createPlainTableRowFromDiff(editor, row) {
22
25
  const plainRow = $createTableRowNode(row.getHeight());
26
+ $copyNodeProperties(row, plainRow);
23
27
  plainRow.append(...row.getChildren().map((child) => $cloneNode(child, editor)));
24
28
  return plainRow;
25
29
  }
@@ -1,26 +1,22 @@
1
- import { $isElementNode, resetRandomKey } from "lexical";
1
+ import { $clearNodeId, $getNodeId, $setNodeId, init_node_id, isValidContentNodeId } from "../../common/node/node-id.js";
2
+ import { $isElementNode } from "lexical";
2
3
  //#region src/plugins/litexml/utils/index.ts
3
- const getNumericId = (id) => {
4
- if (typeof id !== "number" && typeof id !== "string") return null;
5
- const numericId = Number(id);
6
- return Number.isInteger(numericId) && numericId >= 0 ? numericId : null;
7
- };
8
- function $parseSerializedNodeImpl(serializedNode, editor, keepId = false, state = null) {
4
+ init_node_id();
5
+ function $parseSerializedNodeImpl(serializedNode, editor) {
9
6
  const type = serializedNode.type;
10
7
  const registeredNode = editor._nodes.get(type);
11
8
  if (registeredNode === void 0) throw new Error(`parseEditorState: type "${type}" not found`);
12
9
  const nodeClass = registeredNode.klass;
13
10
  if (serializedNode.type !== nodeClass.getType()) throw new Error(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
14
- if (keepId) {
15
- const id = getNumericId(serializedNode.id);
16
- if (id !== null) resetRandomKey(id);
17
- }
18
11
  const node = nodeClass.importJSON(serializedNode);
12
+ const nodeId = isValidContentNodeId(serializedNode.id) ? serializedNode.id : $getNodeId(node);
13
+ if (isValidContentNodeId(nodeId)) $setNodeId(node, nodeId);
14
+ else if (nodeId) $clearNodeId(node);
19
15
  const children = serializedNode.children;
20
16
  if ($isElementNode(node) && Array.isArray(children)) {
21
17
  const childNodes = [];
22
18
  for (const serializedJSONChildNode of children) {
23
- const childNode = $parseSerializedNodeImpl(serializedJSONChildNode, editor, keepId, state);
19
+ const childNode = $parseSerializedNodeImpl(serializedJSONChildNode, editor);
24
20
  childNodes.push(childNode);
25
21
  }
26
22
  node.append(...childNodes);
@@ -30,6 +26,7 @@ function $parseSerializedNodeImpl(serializedNode, editor, keepId = false, state
30
26
  function exportNodeToJSON(node) {
31
27
  const serializedNode = node.exportJSON();
32
28
  const nodeClass = node.constructor;
29
+ serializedNode.id = $getNodeId(node);
33
30
  if (serializedNode.type !== nodeClass.getType()) throw new Error(`LexicalNode: Node ${nodeClass.name} does not match the serialized type. Check if .exportJSON() is implemented and it is returning the correct type.`);
34
31
  if ($isElementNode(node)) {
35
32
  const serializedChildren = serializedNode.children;
@@ -45,15 +42,5 @@ function exportNodeToJSON(node) {
45
42
  function $cloneNode(node, editor) {
46
43
  return $parseSerializedNodeImpl(exportNodeToJSON(node), editor);
47
44
  }
48
- const maxId = 1679616;
49
- const startId = 1e6;
50
- const step = 7211;
51
- const modInverse = 1394051;
52
- function idToChar(id) {
53
- return ((Number(id) * step + startId) % maxId).toString(36).padStart(4, "0");
54
- }
55
- function charToId(char) {
56
- return String((parseInt(char, 36) - startId + maxId) * modInverse % maxId);
57
- }
58
45
  //#endregion
59
- export { $cloneNode, $parseSerializedNodeImpl, charToId, idToChar };
46
+ export { $cloneNode, $parseSerializedNodeImpl };