@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
package/es/headless.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getNodeIdentityPolicyRevision, registerNodeIdentityPolicy, subscribeNodeIdentityPolicy } from "./plugins/common/node/node-identity-policy.js";
|
|
2
|
+
import { $clearNodeId, $copyNodeProperties, $ensureUniqueNodeIds, $findNodeById, $getActiveNodeIdKeys, $getNodeById, $getNodeId, $normalizeNodeIds, $setNodeId, isValidContentNodeId, migrateSerializedNodeIds } from "./plugins/common/node/node-id.js";
|
|
3
|
+
import { INodeIdentityService } from "./plugins/common/service/i-node-identity-service.js";
|
|
4
|
+
import { LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_MODIFY_WITH_RESULTS_COMMAND, LITEXML_REMOVE_COMMAND } from "./plugins/litexml/command/symbols.js";
|
|
2
5
|
import { createHeadlessEditor as createHeadlessEditor$1 } from "@lexical/headless";
|
|
3
6
|
import { createEmptyHistoryState, registerHistory } from "@lexical/history";
|
|
4
7
|
import { $computeTableMapSkipCellCheck, $createTableCellNode, $createTableNodeWithDimensions, $createTableRowNode, $createTableSelection, $deleteTableColumnAtSelection, $deleteTableRowAtSelection, $findTableNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $isSimpleTable, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, TableCellNode, TableNode, TableNode as TableNode$1, TableRowNode, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive } from "@lexical/table";
|
|
5
8
|
import { get, merge, template, templateSettings } from "es-toolkit/compat";
|
|
6
9
|
import EventEmitter from "eventemitter3";
|
|
7
|
-
import { $applyNodeReplacement, $caretFromPoint, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createRangeSelection, $createTextNode, $getCaretRange, $getCharacterOffsets, $getChildCaret, $getNearestNodeFromDOMNode, $getNodeByKey, $getPreviousSelection, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isNodeSelection, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isTextNode, $isTextPointCaret, $nodesOfType, $normalizeSelection__EXPERIMENTAL, $parseSerializedNode, $setSelection, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, DecoratorNode, ElementNode, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, IS_BOLD, IS_CODE, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, ParagraphNode, REDO_COMMAND, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SELECT_ALL_COMMAND, TEXT_TYPE_TO_FORMAT, TextNode, UNDO_COMMAND, createCommand, createEditor, getNearestEditorFromDOMNode, isHTMLElement, isModifierMatch,
|
|
10
|
+
import { $applyNodeReplacement, $caretFromPoint, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createRangeSelection, $createTextNode, $getCaretRange, $getCharacterOffsets, $getChildCaret, $getNearestNodeFromDOMNode, $getNodeByKey, $getPreviousSelection, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isNodeSelection, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isTextNode, $isTextPointCaret, $nodesOfType, $normalizeSelection__EXPERIMENTAL, $parseSerializedNode, $setSelection, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, DecoratorNode, ElementNode, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, IS_BOLD, IS_CODE, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, ParagraphNode, REDO_COMMAND, RootNode, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, SELECT_ALL_COMMAND, TEXT_TYPE_TO_FORMAT, TextNode, UNDO_COMMAND, createCommand, createEditor, getNearestEditorFromDOMNode, isHTMLElement, isModifierMatch, setDOMUnmanaged } from "lexical";
|
|
8
11
|
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode, HeadingNode, QuoteNode, registerRichText } from "@lexical/rich-text";
|
|
9
12
|
import createDebug from "debug";
|
|
10
13
|
import { $filter, $findMatchingParent, $getNearestBlockElementAncestorOrThrow, $getNearestNodeOfType, $insertNodeToNearestRoot, $wrapNodeInElement, CAN_USE_DOM, IS_APPLE as isApple, addClassNamesToElement, calculateZoomLevel, isHTMLAnchorElement, isHTMLElement as isHTMLElement$1, mergeRegister } from "@lexical/utils";
|
|
@@ -250,7 +253,7 @@ function $isCursorInQuote(selection) {
|
|
|
250
253
|
function exportNodeToJSON$1(node) {
|
|
251
254
|
const serializedNode = node.exportJSON();
|
|
252
255
|
const nodeClass = node.constructor;
|
|
253
|
-
serializedNode.id = node
|
|
256
|
+
serializedNode.id = $getNodeId(node);
|
|
254
257
|
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.`);
|
|
255
258
|
if ($isElementNode(node)) {
|
|
256
259
|
const serializedChildren = serializedNode.children;
|
|
@@ -593,7 +596,7 @@ const isHotkeyMatch = (event, hotkey) => {
|
|
|
593
596
|
};
|
|
594
597
|
//#endregion
|
|
595
598
|
//#region src/utils/hotkey/registerHotkey.ts
|
|
596
|
-
const logger$
|
|
599
|
+
const logger$10 = createDebugLogger("hotkey");
|
|
597
600
|
const registerHotkey = (hotkey, callback, options = {}) => {
|
|
598
601
|
return (e) => {
|
|
599
602
|
if (!isHotkeyMatch(e, hotkey.keys)) return false;
|
|
@@ -605,7 +608,7 @@ const registerHotkey = (hotkey, callback, options = {}) => {
|
|
|
605
608
|
...keys,
|
|
606
609
|
scopes: hotkey.scopes
|
|
607
610
|
});
|
|
608
|
-
logger$
|
|
611
|
+
logger$10.debug(`⌨️ Hotkey matched: ${hotkey.id} [${hotkey.keys}]`, hotkey);
|
|
609
612
|
return true;
|
|
610
613
|
};
|
|
611
614
|
};
|
|
@@ -1492,7 +1495,7 @@ function registerCommands(editor) {
|
|
|
1492
1495
|
}
|
|
1493
1496
|
//#endregion
|
|
1494
1497
|
//#region src/plugins/common/node/cursor.ts
|
|
1495
|
-
const logger$
|
|
1498
|
+
const logger$9 = createDebugLogger("common", "cursor");
|
|
1496
1499
|
var CardLikeElementNode = class extends ElementNode {
|
|
1497
1500
|
isCardLike() {
|
|
1498
1501
|
return true;
|
|
@@ -1531,7 +1534,7 @@ function registerCursorNode(editor) {
|
|
|
1531
1534
|
const needAddCursor = [];
|
|
1532
1535
|
for (const [kClass, nodeMaps] of mutatedNodes) if (DecoratorNode.prototype.isPrototypeOf(kClass.prototype)) for (const [key, mutation] of nodeMaps) {
|
|
1533
1536
|
const node = $getNodeByKey(key);
|
|
1534
|
-
logger$
|
|
1537
|
+
logger$9.debug("🎭 DecoratorNode mutated:", node?.getType(), mutation, node);
|
|
1535
1538
|
if (mutation === "created" && node?.isInline() && node.getNextSibling() === null) needAddCursor.push(node);
|
|
1536
1539
|
}
|
|
1537
1540
|
if (needAddCursor.length > 0) editor.update(() => {
|
|
@@ -1633,7 +1636,7 @@ function registerCursorNode(editor) {
|
|
|
1633
1636
|
$setSelection(sel);
|
|
1634
1637
|
return true;
|
|
1635
1638
|
} catch (error) {
|
|
1636
|
-
logger$
|
|
1639
|
+
logger$9.error("❌ Cursor selection error:", error);
|
|
1637
1640
|
}
|
|
1638
1641
|
else if ($isCursorNode(focusNode)) try {
|
|
1639
1642
|
const { key: anchorKey, offset: anchorOffset, type: anchorType } = selection.anchor;
|
|
@@ -1644,7 +1647,7 @@ function registerCursorNode(editor) {
|
|
|
1644
1647
|
$setSelection(sel);
|
|
1645
1648
|
return true;
|
|
1646
1649
|
} catch (error) {
|
|
1647
|
-
logger$
|
|
1650
|
+
logger$9.error("❌ Cursor navigation error:", error);
|
|
1648
1651
|
}
|
|
1649
1652
|
return false;
|
|
1650
1653
|
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ARROW_RIGHT_COMMAND, (event) => {
|
|
@@ -1675,6 +1678,174 @@ function registerCursorNode(editor) {
|
|
|
1675
1678
|
}, COMMAND_PRIORITY_HIGH));
|
|
1676
1679
|
}
|
|
1677
1680
|
//#endregion
|
|
1681
|
+
//#region src/plugins/common/service/node-identity-service.ts
|
|
1682
|
+
const logger$8 = createDebugLogger("service", "node-identity");
|
|
1683
|
+
/** Implementation owned by CommonPlugin; external callers use INodeIdentityService. */
|
|
1684
|
+
var NodeIdentityService = class {
|
|
1685
|
+
constructor() {
|
|
1686
|
+
this.editor = null;
|
|
1687
|
+
this.indexes = /* @__PURE__ */ new WeakMap();
|
|
1688
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
1689
|
+
this.bindingToken = null;
|
|
1690
|
+
this.unregisterBinding = null;
|
|
1691
|
+
}
|
|
1692
|
+
bindEditor(editor) {
|
|
1693
|
+
this.unregisterBinding?.();
|
|
1694
|
+
const bindingToken = {};
|
|
1695
|
+
this.bindingToken = bindingToken;
|
|
1696
|
+
this.indexes = /* @__PURE__ */ new WeakMap();
|
|
1697
|
+
this.editor = editor;
|
|
1698
|
+
const unregisterUpdate = editor.registerUpdateListener(({ editorState, prevEditorState }) => {
|
|
1699
|
+
if (editorState !== prevEditorState) this.notify();
|
|
1700
|
+
});
|
|
1701
|
+
const unregisterPolicy = subscribeNodeIdentityPolicy(editor, () => this.notify());
|
|
1702
|
+
let active = true;
|
|
1703
|
+
const unregister = () => {
|
|
1704
|
+
if (!active) return;
|
|
1705
|
+
active = false;
|
|
1706
|
+
unregisterUpdate();
|
|
1707
|
+
unregisterPolicy();
|
|
1708
|
+
};
|
|
1709
|
+
this.unregisterBinding = unregister;
|
|
1710
|
+
return () => {
|
|
1711
|
+
unregister();
|
|
1712
|
+
if (this.bindingToken !== bindingToken) return;
|
|
1713
|
+
this.bindingToken = null;
|
|
1714
|
+
this.unregisterBinding = null;
|
|
1715
|
+
this.editor = null;
|
|
1716
|
+
this.indexes = /* @__PURE__ */ new WeakMap();
|
|
1717
|
+
this.listeners.clear();
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
getNodeById(nodeId) {
|
|
1721
|
+
if (typeof nodeId !== "string" || !nodeId.trim()) return null;
|
|
1722
|
+
const editor = this.editor;
|
|
1723
|
+
if (!editor) return null;
|
|
1724
|
+
const editorState = editor.getEditorState();
|
|
1725
|
+
const revision = getNodeIdentityPolicyRevision(editor);
|
|
1726
|
+
let index = this.indexes.get(editorState);
|
|
1727
|
+
if (!index || index.revision !== revision) {
|
|
1728
|
+
index = {
|
|
1729
|
+
keys: editorState.read(() => $getActiveNodeIdKeys(), { editor }),
|
|
1730
|
+
revision
|
|
1731
|
+
};
|
|
1732
|
+
this.indexes.set(editorState, index);
|
|
1733
|
+
}
|
|
1734
|
+
const key = index.keys.get(nodeId);
|
|
1735
|
+
if (!key) return null;
|
|
1736
|
+
return editorState.read(() => {
|
|
1737
|
+
const node = $getNodeByKey(key);
|
|
1738
|
+
const id = node && $getNodeId(node);
|
|
1739
|
+
return node && id === nodeId ? Object.freeze({
|
|
1740
|
+
id,
|
|
1741
|
+
textContent: node.getTextContent(),
|
|
1742
|
+
type: node.getType()
|
|
1743
|
+
}) : null;
|
|
1744
|
+
}, { editor });
|
|
1745
|
+
}
|
|
1746
|
+
subscribe(listener) {
|
|
1747
|
+
this.listeners.add(listener);
|
|
1748
|
+
return () => this.listeners.delete(listener);
|
|
1749
|
+
}
|
|
1750
|
+
notify() {
|
|
1751
|
+
const listeners = Array.from(this.listeners);
|
|
1752
|
+
for (const listener of listeners) try {
|
|
1753
|
+
listener();
|
|
1754
|
+
} catch (error) {
|
|
1755
|
+
logger$8.error("Node identity subscriber failed:", error);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
//#endregion
|
|
1760
|
+
//#region src/plugins/litexml/node/identity-policy.ts
|
|
1761
|
+
const afterRepresentation = Object.freeze({ childIndex: 1 });
|
|
1762
|
+
/** Shared active-view rule for live Lexical nodes and serialized preflight trees. */
|
|
1763
|
+
function getLiteXmlIdentityProjection(node) {
|
|
1764
|
+
if (node.type === "root" || node.type === "diff-content") return "container";
|
|
1765
|
+
if (node.type === "diff") {
|
|
1766
|
+
if (node.diffType === "remove" || node.diffType === "listItemRemove") return "hidden";
|
|
1767
|
+
if (node.diffType === "modify" || node.diffType === "listItemModify") return afterRepresentation;
|
|
1768
|
+
return "container";
|
|
1769
|
+
}
|
|
1770
|
+
if ((node.type === "table-cell-diff" || node.type === "table-row-diff") && node.diffType === "remove") return "hidden";
|
|
1771
|
+
if (node.type === "listitem" && node.firstChildType === "diff" && node.firstChildDiffType === "listItemRemove") return "hidden";
|
|
1772
|
+
return "content";
|
|
1773
|
+
}
|
|
1774
|
+
/** Review containers and the document root are physical structure, not addressable content. */
|
|
1775
|
+
function isLiteXmlIdentityNode(type) {
|
|
1776
|
+
return type !== "root" && type !== "diff" && type !== "diff-content";
|
|
1777
|
+
}
|
|
1778
|
+
function getDiffType(node) {
|
|
1779
|
+
if (!node) return void 0;
|
|
1780
|
+
if ("diffType" in node && typeof node.diffType === "string") return node.diffType;
|
|
1781
|
+
if ("getDiffType" in node && typeof node.getDiffType === "function") return node.getDiffType();
|
|
1782
|
+
}
|
|
1783
|
+
function getLiveProjection(node) {
|
|
1784
|
+
const type = node.getType();
|
|
1785
|
+
if (type === "root" || type === "diff-content") return "container";
|
|
1786
|
+
if (type !== "diff" && type !== "table-cell-diff" && type !== "table-row-diff" && type !== "listitem") return "content";
|
|
1787
|
+
const firstChild = $isElementNode(node) ? node.getFirstChild() : null;
|
|
1788
|
+
return getLiteXmlIdentityProjection({
|
|
1789
|
+
diffType: getDiffType(node),
|
|
1790
|
+
firstChildDiffType: getDiffType(firstChild),
|
|
1791
|
+
firstChildType: firstChild?.getType(),
|
|
1792
|
+
type
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
function getTableReviewSide(node) {
|
|
1796
|
+
const type = node.getType();
|
|
1797
|
+
if (type !== "table-cell-diff" && type !== "table-row-diff") return null;
|
|
1798
|
+
if (!("getChangeId" in node) || typeof node.getChangeId !== "function") return null;
|
|
1799
|
+
const changeId = node.getChangeId();
|
|
1800
|
+
const side = getDiffType(node);
|
|
1801
|
+
return changeId && side ? {
|
|
1802
|
+
group: `${type}:${changeId}`,
|
|
1803
|
+
side
|
|
1804
|
+
} : null;
|
|
1805
|
+
}
|
|
1806
|
+
function getReviewSide(node) {
|
|
1807
|
+
const selfSide = getTableReviewSide(node);
|
|
1808
|
+
if (selfSide) return selfSide;
|
|
1809
|
+
let branch = node;
|
|
1810
|
+
for (let parent = node.getParent(); parent; branch = parent, parent = parent.getParent()) {
|
|
1811
|
+
const parentDiffType = getDiffType(parent);
|
|
1812
|
+
if (parent.getType() === "diff") {
|
|
1813
|
+
if (parentDiffType === "modify") {
|
|
1814
|
+
if (branch.getType() === "diff-content" && "side" in branch) return {
|
|
1815
|
+
group: `diff:${parent.getKey()}`,
|
|
1816
|
+
side: String(branch.side)
|
|
1817
|
+
};
|
|
1818
|
+
const branchIndex = parent.getChildren().indexOf(branch);
|
|
1819
|
+
if (branchIndex >= 0 && branchIndex < 2) return {
|
|
1820
|
+
group: `diff:${parent.getKey()}`,
|
|
1821
|
+
side: branchIndex === 0 ? "before" : "after"
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
if (parentDiffType === "listItemModify") {
|
|
1825
|
+
const branchIndex = parent.getChildren().indexOf(branch);
|
|
1826
|
+
if (branchIndex >= 0 && branchIndex < 2) return {
|
|
1827
|
+
group: `diff:${parent.getKey()}`,
|
|
1828
|
+
side: branchIndex === 0 ? "before" : "after"
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
const tableSide = getTableReviewSide(parent);
|
|
1833
|
+
if (tableSide) return tableSide;
|
|
1834
|
+
}
|
|
1835
|
+
return null;
|
|
1836
|
+
}
|
|
1837
|
+
const liteXmlIdentityPolicy = {
|
|
1838
|
+
canShareId(left, right) {
|
|
1839
|
+
const leftSide = getReviewSide(left);
|
|
1840
|
+
const rightSide = getReviewSide(right);
|
|
1841
|
+
return Boolean(leftSide && rightSide && leftSide.group === rightSide.group && leftSide.side !== rightSide.side);
|
|
1842
|
+
},
|
|
1843
|
+
isIdentityNode(node) {
|
|
1844
|
+
return isLiteXmlIdentityNode(node.getType());
|
|
1845
|
+
},
|
|
1846
|
+
project: getLiveProjection
|
|
1847
|
+
};
|
|
1848
|
+
//#endregion
|
|
1678
1849
|
//#region src/plugins/litexml/diff-validation.ts
|
|
1679
1850
|
const ACTIONABLE_DIFF_TYPES = /* @__PURE__ */ new Set([
|
|
1680
1851
|
"add",
|
|
@@ -1686,6 +1857,11 @@ const ACTIONABLE_DIFF_TYPES = /* @__PURE__ */ new Set([
|
|
|
1686
1857
|
]);
|
|
1687
1858
|
const isActionableDiff = (node) => node?.type === "diff" && ACTIONABLE_DIFF_TYPES.has(node.diffType || "");
|
|
1688
1859
|
const isElementNode = (node) => Array.isArray(node.children);
|
|
1860
|
+
const getNodeId = (node) => {
|
|
1861
|
+
if (typeof node.id === "string" && node.id.length > 0) return node.id;
|
|
1862
|
+
const properties = node.$?.properties;
|
|
1863
|
+
return typeof properties?.nodeId === "string" ? properties.nodeId : void 0;
|
|
1864
|
+
};
|
|
1689
1865
|
const cloneNode = (node) => structuredClone(node);
|
|
1690
1866
|
function hasActionableDiffDescendant(node) {
|
|
1691
1867
|
return (node.children || []).some((child) => isActionableDiff(child) || hasActionableDiffDescendant(child));
|
|
@@ -1711,10 +1887,10 @@ function collectIllegalNestedDiffPaths(root) {
|
|
|
1711
1887
|
}
|
|
1712
1888
|
if (!isElementNode(node)) return;
|
|
1713
1889
|
node.children?.forEach((child) => {
|
|
1714
|
-
visit(child, [...ancestors, node], `${path}/${child
|
|
1890
|
+
visit(child, [...ancestors, node], `${path}/${getNodeId(child) || child.type || "node"}`);
|
|
1715
1891
|
});
|
|
1716
1892
|
};
|
|
1717
|
-
visit(root, [], root
|
|
1893
|
+
visit(root, [], getNodeId(root) || root.type || "root");
|
|
1718
1894
|
return violations;
|
|
1719
1895
|
}
|
|
1720
1896
|
function findNewIllegalDiffPaths(previous, projected) {
|
|
@@ -1731,25 +1907,52 @@ function findNewIllegalDiffPaths(previous, projected) {
|
|
|
1731
1907
|
});
|
|
1732
1908
|
return newPaths;
|
|
1733
1909
|
}
|
|
1734
|
-
function
|
|
1735
|
-
|
|
1910
|
+
function findActiveNodeLocation(root, id, ancestors = []) {
|
|
1911
|
+
const projection = getLiteXmlIdentityProjection({
|
|
1912
|
+
diffType: root.diffType,
|
|
1913
|
+
firstChildDiffType: root.children?.[0]?.diffType,
|
|
1914
|
+
firstChildType: root.children?.[0]?.type,
|
|
1915
|
+
type: root.type
|
|
1916
|
+
});
|
|
1917
|
+
if (projection === "hidden") return null;
|
|
1918
|
+
if (projection === "content" && getNodeId(root) === id) {
|
|
1919
|
+
const parent = ancestors.at(-1) || null;
|
|
1920
|
+
return {
|
|
1921
|
+
ancestors,
|
|
1922
|
+
index: parent ? (parent.children || []).indexOf(root) : -1,
|
|
1923
|
+
node: root,
|
|
1924
|
+
parent
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
const activeChildren = typeof projection === "object" ? root.children?.[projection.childIndex] ? [root.children[projection.childIndex]] : [] : root.children || [];
|
|
1928
|
+
for (const child of activeChildren) {
|
|
1929
|
+
const nested = findActiveNodeLocation(child, id, [...ancestors, root]);
|
|
1930
|
+
if (nested) return nested;
|
|
1931
|
+
}
|
|
1932
|
+
return null;
|
|
1933
|
+
}
|
|
1934
|
+
function findNodeLocationByNode(root, target, ancestors = []) {
|
|
1935
|
+
if (root === target) return {
|
|
1736
1936
|
ancestors,
|
|
1737
1937
|
index: -1,
|
|
1738
1938
|
node: root,
|
|
1739
1939
|
parent: null
|
|
1740
1940
|
};
|
|
1741
1941
|
for (const [index, child] of (root.children || []).entries()) {
|
|
1742
|
-
if (child
|
|
1942
|
+
if (child === target) return {
|
|
1743
1943
|
ancestors: [...ancestors, root],
|
|
1744
1944
|
index,
|
|
1745
1945
|
node: child,
|
|
1746
1946
|
parent: root
|
|
1747
1947
|
};
|
|
1748
|
-
const nested =
|
|
1948
|
+
const nested = findNodeLocationByNode(child, target, [...ancestors, root]);
|
|
1749
1949
|
if (nested) return nested;
|
|
1750
1950
|
}
|
|
1751
1951
|
return null;
|
|
1752
1952
|
}
|
|
1953
|
+
function hasActiveLiteXmlNodeId(root, id) {
|
|
1954
|
+
return findActiveNodeLocation(root, id) !== null;
|
|
1955
|
+
}
|
|
1753
1956
|
function replaceNode(location, replacements) {
|
|
1754
1957
|
if (!location.parent || location.index < 0) return;
|
|
1755
1958
|
location.parent.children?.splice(location.index, 1, ...replacements);
|
|
@@ -1769,8 +1972,9 @@ function createDiffContent(side, children) {
|
|
|
1769
1972
|
};
|
|
1770
1973
|
}
|
|
1771
1974
|
function projectModifyTarget(root, incoming) {
|
|
1772
|
-
|
|
1773
|
-
|
|
1975
|
+
const incomingId = getNodeId(incoming);
|
|
1976
|
+
if (!incomingId) return;
|
|
1977
|
+
const location = findActiveNodeLocation(root, incomingId);
|
|
1774
1978
|
if (!location) return;
|
|
1775
1979
|
const target = location.node;
|
|
1776
1980
|
const next = cloneNode(incoming);
|
|
@@ -1804,10 +2008,30 @@ function projectModifyTarget(root, incoming) {
|
|
|
1804
2008
|
replaceNode(location, [next]);
|
|
1805
2009
|
return;
|
|
1806
2010
|
}
|
|
2011
|
+
if (target.type === "listitem") {
|
|
2012
|
+
const existingDiff = (target.children || []).find((child) => child.type === "diff");
|
|
2013
|
+
if (existingDiff?.diffType === "listItemModify") {
|
|
2014
|
+
const after = existingDiff.children?.[1];
|
|
2015
|
+
if (after) after.children = cloneNode(next.children || []);
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
if (existingDiff?.diffType === "listItemAdd") {
|
|
2019
|
+
existingDiff.children = cloneNode(next.children || []);
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
target.children = [createDiff("listItemModify", [{
|
|
2023
|
+
children: cloneNode(target.children || []),
|
|
2024
|
+
type: "paragraph"
|
|
2025
|
+
}, {
|
|
2026
|
+
children: cloneNode(next.children || []),
|
|
2027
|
+
type: "paragraph"
|
|
2028
|
+
}])];
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
1807
2031
|
replaceNode(location, [createDiff("modify", [cloneNode(target), next])]);
|
|
1808
2032
|
}
|
|
1809
2033
|
function projectRemoveTarget(root, id) {
|
|
1810
|
-
const location =
|
|
2034
|
+
const location = findActiveNodeLocation(root, id);
|
|
1811
2035
|
if (!location) return;
|
|
1812
2036
|
if (location.ancestors.some(isActionableDiff)) {
|
|
1813
2037
|
replaceNode(location, []);
|
|
@@ -1834,13 +2058,13 @@ function projectRemoveTarget(root, id) {
|
|
|
1834
2058
|
}
|
|
1835
2059
|
function projectInsert(root, operation, readXml) {
|
|
1836
2060
|
const referenceId = "beforeId" in operation ? operation.beforeId : operation.afterId;
|
|
1837
|
-
const reference = referenceId === "root" ? null :
|
|
2061
|
+
const reference = referenceId === "root" ? null : findActiveNodeLocation(root, referenceId)?.node || null;
|
|
1838
2062
|
const location = referenceId === "root" ? {
|
|
1839
2063
|
ancestors: [root],
|
|
1840
2064
|
index: "beforeId" in operation ? 0 : (root.children || []).length - 1,
|
|
1841
2065
|
node: root.children?.[0] || root,
|
|
1842
2066
|
parent: root
|
|
1843
|
-
} :
|
|
2067
|
+
} : findActiveNodeLocation(root, referenceId);
|
|
1844
2068
|
if (!location) return;
|
|
1845
2069
|
const incoming = (readXml(operation.litexml).root?.children || []).map(cloneNode);
|
|
1846
2070
|
if (incoming.length === 0 || !location.parent) return;
|
|
@@ -1879,12 +2103,12 @@ function projectInsert(root, operation, readXml) {
|
|
|
1879
2103
|
].includes(node.type || ""));
|
|
1880
2104
|
if (!actionableAncestor && blockAncestor && hasActionableDiffDescendant(blockAncestor)) {
|
|
1881
2105
|
const wrapped = createDiff("modify", [cloneNode(blockAncestor), cloneNode(blockAncestor)]);
|
|
1882
|
-
const blockLocation =
|
|
2106
|
+
const blockLocation = findNodeLocationByNode(root, blockAncestor);
|
|
1883
2107
|
if (blockLocation) replaceNode(blockLocation, [wrapped]);
|
|
1884
2108
|
return;
|
|
1885
2109
|
}
|
|
1886
2110
|
if (actionableAncestor && !isInlineReference) {
|
|
1887
|
-
const originLocation =
|
|
2111
|
+
const originLocation = findNodeLocationByNode(root, actionableAncestor);
|
|
1888
2112
|
if (originLocation?.parent && originLocation.index >= 0) {
|
|
1889
2113
|
const originInsertAt = "beforeId" in operation ? originLocation.index : originLocation.index + 1;
|
|
1890
2114
|
originLocation.parent.children?.splice(originInsertAt, 0, ...incoming.map((node) => createDiff("add", [node])));
|
|
@@ -2148,27 +2372,21 @@ function $isTableCellDiffNode(node) {
|
|
|
2148
2372
|
}
|
|
2149
2373
|
//#endregion
|
|
2150
2374
|
//#region src/plugins/litexml/utils/index.ts
|
|
2151
|
-
|
|
2152
|
-
if (typeof id !== "number" && typeof id !== "string") return null;
|
|
2153
|
-
const numericId = Number(id);
|
|
2154
|
-
return Number.isInteger(numericId) && numericId >= 0 ? numericId : null;
|
|
2155
|
-
};
|
|
2156
|
-
function $parseSerializedNodeImpl(serializedNode, editor, keepId = false, state = null) {
|
|
2375
|
+
function $parseSerializedNodeImpl(serializedNode, editor) {
|
|
2157
2376
|
const type = serializedNode.type;
|
|
2158
2377
|
const registeredNode = editor._nodes.get(type);
|
|
2159
2378
|
if (registeredNode === void 0) throw new Error(`parseEditorState: type "${type}" not found`);
|
|
2160
2379
|
const nodeClass = registeredNode.klass;
|
|
2161
2380
|
if (serializedNode.type !== nodeClass.getType()) throw new Error(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
|
|
2162
|
-
if (keepId) {
|
|
2163
|
-
const id = getNumericId$1(serializedNode.id);
|
|
2164
|
-
if (id !== null) resetRandomKey(id);
|
|
2165
|
-
}
|
|
2166
2381
|
const node = nodeClass.importJSON(serializedNode);
|
|
2382
|
+
const nodeId = isValidContentNodeId(serializedNode.id) ? serializedNode.id : $getNodeId(node);
|
|
2383
|
+
if (isValidContentNodeId(nodeId)) $setNodeId(node, nodeId);
|
|
2384
|
+
else if (nodeId) $clearNodeId(node);
|
|
2167
2385
|
const children = serializedNode.children;
|
|
2168
2386
|
if ($isElementNode(node) && Array.isArray(children)) {
|
|
2169
2387
|
const childNodes = [];
|
|
2170
2388
|
for (const serializedJSONChildNode of children) {
|
|
2171
|
-
const childNode = $parseSerializedNodeImpl(serializedJSONChildNode, editor
|
|
2389
|
+
const childNode = $parseSerializedNodeImpl(serializedJSONChildNode, editor);
|
|
2172
2390
|
childNodes.push(childNode);
|
|
2173
2391
|
}
|
|
2174
2392
|
node.append(...childNodes);
|
|
@@ -2178,6 +2396,7 @@ function $parseSerializedNodeImpl(serializedNode, editor, keepId = false, state
|
|
|
2178
2396
|
function exportNodeToJSON(node) {
|
|
2179
2397
|
const serializedNode = node.exportJSON();
|
|
2180
2398
|
const nodeClass = node.constructor;
|
|
2399
|
+
serializedNode.id = $getNodeId(node);
|
|
2181
2400
|
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.`);
|
|
2182
2401
|
if ($isElementNode(node)) {
|
|
2183
2402
|
const serializedChildren = serializedNode.children;
|
|
@@ -2193,16 +2412,6 @@ function exportNodeToJSON(node) {
|
|
|
2193
2412
|
function $cloneNode(node, editor) {
|
|
2194
2413
|
return $parseSerializedNodeImpl(exportNodeToJSON(node), editor);
|
|
2195
2414
|
}
|
|
2196
|
-
const maxId = 1679616;
|
|
2197
|
-
const startId = 1e6;
|
|
2198
|
-
const step = 7211;
|
|
2199
|
-
const modInverse = 1394051;
|
|
2200
|
-
function idToChar(id) {
|
|
2201
|
-
return ((Number(id) * step + startId) % maxId).toString(36).padStart(4, "0");
|
|
2202
|
-
}
|
|
2203
|
-
function charToId(char) {
|
|
2204
|
-
return String((parseInt(char, 36) - startId + maxId) * modInverse % maxId);
|
|
2205
|
-
}
|
|
2206
2415
|
//#endregion
|
|
2207
2416
|
//#region src/plugins/litexml/table-cell-diff.ts
|
|
2208
2417
|
function copyCellStructure(source, target) {
|
|
@@ -2212,6 +2421,7 @@ function copyCellStructure(source, target) {
|
|
|
2212
2421
|
}
|
|
2213
2422
|
function $createTableCellDiffFromCell(editor, cell, diffType, changeId) {
|
|
2214
2423
|
const diffCell = $createTableCellDiffNode(diffType, changeId, cell.getHeaderStyles(), cell.getColSpan(), cell.getWidth());
|
|
2424
|
+
$copyNodeProperties(cell, diffCell);
|
|
2215
2425
|
copyCellStructure(cell, diffCell);
|
|
2216
2426
|
const diff = $createDiffNode(diffType);
|
|
2217
2427
|
diff.append(...cell.getChildren().map((child) => $cloneNode(child, editor)));
|
|
@@ -2220,6 +2430,7 @@ function $createTableCellDiffFromCell(editor, cell, diffType, changeId) {
|
|
|
2220
2430
|
}
|
|
2221
2431
|
function $createPlainTableCellFromDiff(editor, cell) {
|
|
2222
2432
|
const plainCell = $createTableCellNode(cell.getHeaderStyles(), cell.getColSpan(), cell.getWidth());
|
|
2433
|
+
$copyNodeProperties(cell, plainCell);
|
|
2223
2434
|
copyCellStructure(cell, plainCell);
|
|
2224
2435
|
const diff = cell.getFirstChild();
|
|
2225
2436
|
if (diff instanceof DiffNode) plainCell.append(...diff.getChildren().map((child) => $cloneNode(child, editor)));
|
|
@@ -2434,11 +2645,13 @@ function $areTableRowStructuresCompatible(previousRow, nextRow) {
|
|
|
2434
2645
|
function $createTableRowDiffFromRow(editor, row, diffType, changeId) {
|
|
2435
2646
|
const source = $cloneNode(row, editor);
|
|
2436
2647
|
const diffRow = $createTableRowDiffNode(diffType, changeId, row.getHeight());
|
|
2648
|
+
$copyNodeProperties(row, diffRow);
|
|
2437
2649
|
diffRow.append(...source.getChildren());
|
|
2438
2650
|
return diffRow;
|
|
2439
2651
|
}
|
|
2440
2652
|
function $createPlainTableRowFromDiff(editor, row) {
|
|
2441
2653
|
const plainRow = $createTableRowNode(row.getHeight());
|
|
2654
|
+
$copyNodeProperties(row, plainRow);
|
|
2442
2655
|
plainRow.append(...row.getChildren().map((child) => $cloneNode(child, editor)));
|
|
2443
2656
|
return plainRow;
|
|
2444
2657
|
}
|
|
@@ -2506,9 +2719,9 @@ function toArrayXml(litexml) {
|
|
|
2506
2719
|
function hasNewIllegalDiffs(previous, projected) {
|
|
2507
2720
|
return findNewIllegalDiffPaths(previous, projected);
|
|
2508
2721
|
}
|
|
2509
|
-
function projectOperation(dataSource, document, operation) {
|
|
2722
|
+
function projectOperation(dataSource, document, operation, readXml = (xml) => dataSource.readLiteXMLToInode(xml)) {
|
|
2510
2723
|
try {
|
|
2511
|
-
const projected = projectLiteXmlOperation(document, operation,
|
|
2724
|
+
const projected = projectLiteXmlOperation(document, operation, readXml);
|
|
2512
2725
|
const newIllegalDiffs = hasNewIllegalDiffs(document, projected);
|
|
2513
2726
|
if (newIllegalDiffs.length > 0) {
|
|
2514
2727
|
logger$7.warn("⚠️ Skipping operation with illegal nested diff", newIllegalDiffs);
|
|
@@ -2521,19 +2734,163 @@ function projectOperation(dataSource, document, operation) {
|
|
|
2521
2734
|
}
|
|
2522
2735
|
}
|
|
2523
2736
|
function toProjectionOperation(operation) {
|
|
2524
|
-
if (operation.action === "remove") return {
|
|
2525
|
-
...operation,
|
|
2526
|
-
id: charToId(operation.id)
|
|
2527
|
-
};
|
|
2528
|
-
if (operation.action === "insert") return {
|
|
2529
|
-
...operation,
|
|
2530
|
-
..."beforeId" in operation ? { beforeId: operation.beforeId === "root" ? "root" : charToId(operation.beforeId) } : { afterId: operation.afterId === "root" ? "root" : charToId(operation.afterId) }
|
|
2531
|
-
};
|
|
2532
2737
|
return operation;
|
|
2533
2738
|
}
|
|
2739
|
+
function getSerializedNodeId(node) {
|
|
2740
|
+
if (typeof node?.id === "string" && node.id.length > 0) return node.id;
|
|
2741
|
+
const nodeId = node?.$?.properties?.nodeId;
|
|
2742
|
+
return typeof nodeId === "string" && nodeId.length > 0 ? nodeId : void 0;
|
|
2743
|
+
}
|
|
2744
|
+
function getActiveSerializedDocument() {
|
|
2745
|
+
return { root: exportNodeToJSON$1($getRoot()) };
|
|
2746
|
+
}
|
|
2747
|
+
/** Resolve every target before staging any part of one modify operation. */
|
|
2748
|
+
function prepareModify(editor, dataSource, xmls) {
|
|
2749
|
+
const pairs = [];
|
|
2750
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
2751
|
+
const targetIds = /* @__PURE__ */ new Set();
|
|
2752
|
+
for (const xml of xmls) {
|
|
2753
|
+
const inode = dataSource.readLiteXMLToInode(xml);
|
|
2754
|
+
parsed.set(xml, inode);
|
|
2755
|
+
const children = inode.root?.children;
|
|
2756
|
+
if (!children?.length) throw new Error("Modify operation contains no LiteXML nodes.");
|
|
2757
|
+
for (const child of children) {
|
|
2758
|
+
const nodeId = getSerializedNodeId(child);
|
|
2759
|
+
if (!nodeId) throw new Error("Modify operation root is missing its public node id.");
|
|
2760
|
+
if (targetIds.has(nodeId)) throw new Error(`Node id "${nodeId}" is targeted more than once.`);
|
|
2761
|
+
targetIds.add(nodeId);
|
|
2762
|
+
const oldNode = $findNodeById(nodeId);
|
|
2763
|
+
if (!oldNode) throw new Error(`Node id "${nodeId}" was not found.`);
|
|
2764
|
+
const newNode = $parseSerializedNodeImpl(child, editor);
|
|
2765
|
+
if (oldNode.isInline() !== newNode.isInline()) throw new Error(`Node id "${nodeId}" cannot change between inline and block structure.`);
|
|
2766
|
+
const parent = oldNode.getParent();
|
|
2767
|
+
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.`);
|
|
2768
|
+
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.`);
|
|
2769
|
+
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.`);
|
|
2770
|
+
pairs.push({
|
|
2771
|
+
newNode,
|
|
2772
|
+
oldNode
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
const targetKeys = new Set(pairs.map(({ oldNode }) => oldNode.getKey()));
|
|
2777
|
+
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.");
|
|
2778
|
+
return {
|
|
2779
|
+
pairs,
|
|
2780
|
+
readXml: (xml) => parsed.get(xml)
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
function getBatchTargetError(document, operation, dataSource) {
|
|
2784
|
+
if (operation.action === "remove") return hasActiveLiteXmlNodeId(document.root, operation.id) ? void 0 : `Node id "${operation.id}" was not found.`;
|
|
2785
|
+
if (operation.action === "insert") {
|
|
2786
|
+
try {
|
|
2787
|
+
if (!dataSource.readLiteXMLToInode(operation.litexml).root.children?.length) return "Insert operation contains no LiteXML nodes.";
|
|
2788
|
+
} catch (error) {
|
|
2789
|
+
return error instanceof Error ? error.message : String(error);
|
|
2790
|
+
}
|
|
2791
|
+
const anchorId = "beforeId" in operation ? operation.beforeId : operation.afterId;
|
|
2792
|
+
if (anchorId === "root") return (document.root.children?.length || 0) > 0 ? void 0 : "The document has no node to use as an insertion anchor.";
|
|
2793
|
+
return hasActiveLiteXmlNodeId(document.root, anchorId) ? void 0 : `Insertion anchor "${anchorId}" was not found.`;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
function applyLiteXMLBatch(editor, dataSource, operations) {
|
|
2797
|
+
let projectedDocument = getActiveSerializedDocument();
|
|
2798
|
+
const afterAnchors = /* @__PURE__ */ new Map();
|
|
2799
|
+
const results = [];
|
|
2800
|
+
operations.forEach((operation, index) => {
|
|
2801
|
+
const targetError = getBatchTargetError(projectedDocument, operation, dataSource);
|
|
2802
|
+
if (targetError) {
|
|
2803
|
+
results.push({
|
|
2804
|
+
action: operation.action,
|
|
2805
|
+
index,
|
|
2806
|
+
reason: targetError,
|
|
2807
|
+
status: "failed"
|
|
2808
|
+
});
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
let preparedModify;
|
|
2812
|
+
if (operation.action === "modify") try {
|
|
2813
|
+
preparedModify = prepareModify(editor, dataSource, toArrayXml(operation.litexml));
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
results.push({
|
|
2816
|
+
action: operation.action,
|
|
2817
|
+
index,
|
|
2818
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
2819
|
+
status: "failed"
|
|
2820
|
+
});
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
if (!projectOperation(dataSource, projectedDocument, toProjectionOperation(operation), preparedModify?.readXml)) {
|
|
2824
|
+
results.push({
|
|
2825
|
+
action: operation.action,
|
|
2826
|
+
index,
|
|
2827
|
+
reason: "The operation would create an invalid nested review diff.",
|
|
2828
|
+
status: "failed"
|
|
2829
|
+
});
|
|
2830
|
+
return;
|
|
2831
|
+
}
|
|
2832
|
+
try {
|
|
2833
|
+
let applied = false;
|
|
2834
|
+
switch (operation.action) {
|
|
2835
|
+
case "modify":
|
|
2836
|
+
applied = applyPreparedModify(editor, preparedModify);
|
|
2837
|
+
break;
|
|
2838
|
+
case "remove":
|
|
2839
|
+
applied = handleRemove(editor, operation.id, true);
|
|
2840
|
+
break;
|
|
2841
|
+
case "insert": {
|
|
2842
|
+
const cursorIds = "afterId" in operation ? afterAnchors.get(operation.afterId) : void 0;
|
|
2843
|
+
let override;
|
|
2844
|
+
if (cursorIds) for (let cursorIndex = cursorIds.length - 1; cursorIndex >= 0; cursorIndex--) {
|
|
2845
|
+
const cursor = $findNodeById(cursorIds[cursorIndex]);
|
|
2846
|
+
if (cursor) {
|
|
2847
|
+
override = cursor;
|
|
2848
|
+
break;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
const insertedNodeId = handleInsert(editor, {
|
|
2852
|
+
...operation,
|
|
2853
|
+
delay: true
|
|
2854
|
+
}, dataSource, override);
|
|
2855
|
+
applied = insertedNodeId !== null;
|
|
2856
|
+
if ("afterId" in operation && insertedNodeId) {
|
|
2857
|
+
const nextCursors = afterAnchors.get(operation.afterId) || [];
|
|
2858
|
+
nextCursors.push(insertedNodeId);
|
|
2859
|
+
afterAnchors.set(operation.afterId, nextCursors);
|
|
2860
|
+
}
|
|
2861
|
+
break;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
if (!applied) {
|
|
2865
|
+
results.push({
|
|
2866
|
+
action: operation.action,
|
|
2867
|
+
index,
|
|
2868
|
+
reason: "The editor could not apply the operation.",
|
|
2869
|
+
status: "failed"
|
|
2870
|
+
});
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
projectedDocument = getActiveSerializedDocument();
|
|
2874
|
+
results.push({
|
|
2875
|
+
action: operation.action,
|
|
2876
|
+
index,
|
|
2877
|
+
status: "applied"
|
|
2878
|
+
});
|
|
2879
|
+
} catch (error) {
|
|
2880
|
+
results.push({
|
|
2881
|
+
action: operation.action,
|
|
2882
|
+
index,
|
|
2883
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
2884
|
+
status: "failed"
|
|
2885
|
+
});
|
|
2886
|
+
}
|
|
2887
|
+
});
|
|
2888
|
+
return results;
|
|
2889
|
+
}
|
|
2534
2890
|
function tryParseChild(child, editor) {
|
|
2535
2891
|
try {
|
|
2536
|
-
const
|
|
2892
|
+
const nodeId = typeof child.id === "string" ? child.id : child.$?.properties?.nodeId;
|
|
2893
|
+
const oldNode = typeof nodeId === "string" ? $findNodeById(nodeId) : null;
|
|
2537
2894
|
return {
|
|
2538
2895
|
newNode: $parseSerializedNodeImpl(child, editor),
|
|
2539
2896
|
oldNode
|
|
@@ -2550,14 +2907,14 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
2550
2907
|
if ($isTableRowNode(oldNode) || $isTableRowNode(newNode)) {
|
|
2551
2908
|
if (!$isTableRowNode(oldNode) || !$isTableRowNode(newNode) || !$isTableNode(oldNode.getParent()) || !$areTableRowStructuresCompatible(oldNode, newNode)) {
|
|
2552
2909
|
logger$7.error(`❌ Invalid table row modification for row ${oldNode.getKey()}.`);
|
|
2553
|
-
return;
|
|
2910
|
+
return false;
|
|
2554
2911
|
}
|
|
2555
2912
|
const changeId = `${oldNode.getKey()}:${newNode.getKey()}`;
|
|
2556
2913
|
const removeRow = $createTableRowDiffFromRow(editor, oldNode, "remove", changeId);
|
|
2557
2914
|
const addRow = $createTableRowDiffFromRow(editor, newNode, "add", changeId);
|
|
2558
2915
|
oldNode.replace(removeRow, false);
|
|
2559
2916
|
removeRow.insertAfter(addRow);
|
|
2560
|
-
return;
|
|
2917
|
+
return true;
|
|
2561
2918
|
}
|
|
2562
2919
|
if ($isTableCellNode(oldNode) && $isTableCellNode(newNode)) {
|
|
2563
2920
|
const existingDiff = oldNode.getChildren().find($isDiffNode);
|
|
@@ -2567,7 +2924,7 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
2567
2924
|
newNode.getChildren().forEach((child) => {
|
|
2568
2925
|
existingDiff.append($cloneNode(child, editor));
|
|
2569
2926
|
});
|
|
2570
|
-
return;
|
|
2927
|
+
return true;
|
|
2571
2928
|
}
|
|
2572
2929
|
const after = existingDiff.getChildren().find((child) => $isDiffContentNode(child) && child.side === "after");
|
|
2573
2930
|
if (existingDiff.diffType === "modify" && after && $isDiffContentNode(after)) {
|
|
@@ -2575,7 +2932,7 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
2575
2932
|
newNode.getChildren().forEach((child) => {
|
|
2576
2933
|
after.append($cloneNode(child, editor));
|
|
2577
2934
|
});
|
|
2578
|
-
return;
|
|
2935
|
+
return true;
|
|
2579
2936
|
}
|
|
2580
2937
|
}
|
|
2581
2938
|
const before = $createDiffContentNode("before");
|
|
@@ -2586,27 +2943,42 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
2586
2943
|
diffNode.append(before, after);
|
|
2587
2944
|
oldNode.clear();
|
|
2588
2945
|
oldNode.append(diffNode);
|
|
2589
|
-
return;
|
|
2946
|
+
return true;
|
|
2590
2947
|
}
|
|
2591
2948
|
const oldBlock = $closest(oldNode, (node) => node.isInline() === false);
|
|
2592
2949
|
if (!oldBlock) throw new Error("Old block node not found for diffing.");
|
|
2593
2950
|
if ($closest(oldNode, (node) => node.getType() === DiffNode.getType())) {
|
|
2594
2951
|
oldNode.replace(newNode, false);
|
|
2595
|
-
return;
|
|
2952
|
+
return true;
|
|
2596
2953
|
}
|
|
2597
2954
|
if ($isListItemNode(oldNode) && $isListItemNode(newNode)) {
|
|
2955
|
+
const existingListDiff = oldNode.getChildren().find($isDiffNode);
|
|
2956
|
+
if (existingListDiff?.diffType === "listItemModify") {
|
|
2957
|
+
const after = existingListDiff.getChildAtIndex(1);
|
|
2958
|
+
if ($isElementNode(after)) {
|
|
2959
|
+
after.clear();
|
|
2960
|
+
newNode.getChildren().forEach((child) => after.append($cloneNode(child, editor)));
|
|
2961
|
+
return true;
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
if (existingListDiff?.diffType === "listItemAdd") {
|
|
2965
|
+
existingListDiff.clear();
|
|
2966
|
+
newNode.getChildren().forEach((child) => existingListDiff.append($cloneNode(child, editor)));
|
|
2967
|
+
return true;
|
|
2968
|
+
}
|
|
2598
2969
|
const before = $createParagraphNode();
|
|
2599
2970
|
const after = $createParagraphNode();
|
|
2600
2971
|
oldNode.getChildren().forEach((child) => before.append(child));
|
|
2601
2972
|
newNode.getChildren().forEach((child) => after.append(child));
|
|
2602
2973
|
oldNode.clear();
|
|
2603
2974
|
oldNode.append($createDiffNode("listItemModify").append(before, after));
|
|
2604
|
-
return;
|
|
2975
|
+
return true;
|
|
2605
2976
|
}
|
|
2606
2977
|
if (oldNode === oldBlock) {
|
|
2607
2978
|
const diffNode = $createDiffNode("modify");
|
|
2608
2979
|
oldNode.replace(diffNode, false);
|
|
2609
2980
|
diffNode.append($createDiffContentNode("before").append(oldBlock), $createDiffContentNode("after").append(newNode));
|
|
2981
|
+
return true;
|
|
2610
2982
|
} else {
|
|
2611
2983
|
if (!modifyBlockNodes.has(oldBlock.getKey())) {
|
|
2612
2984
|
modifyBlockNodes.add(oldBlock.getKey());
|
|
@@ -2615,6 +2987,7 @@ function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNode
|
|
|
2615
2987
|
diffNodeMap.set(oldBlock.getKey(), diffNode);
|
|
2616
2988
|
}
|
|
2617
2989
|
oldNode.replace(newNode, false);
|
|
2990
|
+
return true;
|
|
2618
2991
|
}
|
|
2619
2992
|
}
|
|
2620
2993
|
function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
|
|
@@ -2635,6 +3008,16 @@ function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
|
|
|
2635
3008
|
newDiffNode.append(p);
|
|
2636
3009
|
blockNode.append(newDiffNode);
|
|
2637
3010
|
continue;
|
|
3011
|
+
} else if ($isTableCellNode(blockNode) && $isTableRowNode(blockNode.getParent()) && $isTableCellNode(diffNode.getFirstChild())) {
|
|
3012
|
+
const beforeCell = diffNode.getFirstChild();
|
|
3013
|
+
if (!$isTableCellNode(beforeCell)) continue;
|
|
3014
|
+
const before = $createDiffContentNode("before");
|
|
3015
|
+
const after = $createDiffContentNode("after");
|
|
3016
|
+
beforeCell.getChildren().forEach((child) => before.append(child));
|
|
3017
|
+
blockNode.getChildren().forEach((child) => after.append($cloneNode(child, editor)));
|
|
3018
|
+
blockNode.clear();
|
|
3019
|
+
blockNode.append($createDiffNode("modify").append(before, after));
|
|
3020
|
+
continue;
|
|
2638
3021
|
} else {
|
|
2639
3022
|
diffNode.append($cloneNode(blockNode, editor));
|
|
2640
3023
|
blockNode.replace(diffNode, false);
|
|
@@ -2665,6 +3048,18 @@ function wrapBlockModify(oldBlock, editor, changeFn) {
|
|
|
2665
3048
|
oldBlock.append(diffNode);
|
|
2666
3049
|
return;
|
|
2667
3050
|
}
|
|
3051
|
+
if ($isTableCellNode(oldBlock) && !$isTableCellDiffNode(oldBlock) && $isTableRowNode(oldBlock.getParent())) {
|
|
3052
|
+
const before = $createDiffContentNode("before");
|
|
3053
|
+
oldBlock.getChildren().forEach((child) => before.append($cloneNode(child, editor)));
|
|
3054
|
+
changeFn();
|
|
3055
|
+
const newBlock = $getNodeByKey(oldBlock.getKey());
|
|
3056
|
+
if (!$isTableCellNode(newBlock)) throw new Error("Updated table cell node not found for modify wrapper.");
|
|
3057
|
+
const after = $createDiffContentNode("after");
|
|
3058
|
+
newBlock.getChildren().forEach((child) => after.append($cloneNode(child, editor)));
|
|
3059
|
+
newBlock.clear();
|
|
3060
|
+
newBlock.append($createDiffNode("modify").append(before, after));
|
|
3061
|
+
return;
|
|
3062
|
+
}
|
|
2668
3063
|
const diffNode = $createDiffNode("modify");
|
|
2669
3064
|
diffNode.append($cloneNode(oldBlock, editor));
|
|
2670
3065
|
changeFn();
|
|
@@ -2674,344 +3069,330 @@ function wrapBlockModify(oldBlock, editor, changeFn) {
|
|
|
2674
3069
|
newBlock.replace(diffNode, false);
|
|
2675
3070
|
}
|
|
2676
3071
|
function registerLiteXMLCommand(editor, dataSource) {
|
|
2677
|
-
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
let projectedDocument = getKernelFromEditor(editor).getDocument("json");
|
|
2684
|
-
const safePayload = resultPayload.filter((item) => {
|
|
2685
|
-
const nextProjection = projectOperation(dataSource, projectedDocument, toProjectionOperation(item));
|
|
2686
|
-
if (!nextProjection) return false;
|
|
2687
|
-
projectedDocument = nextProjection;
|
|
2688
|
-
return true;
|
|
2689
|
-
});
|
|
2690
|
-
try {
|
|
2691
|
-
safePayload.forEach((item) => {
|
|
2692
|
-
const { action } = item;
|
|
2693
|
-
switch (action) {
|
|
2694
|
-
case "modify": {
|
|
2695
|
-
const { litexml } = item;
|
|
2696
|
-
handleModify(editor, dataSource, toArrayXml(litexml), true);
|
|
2697
|
-
break;
|
|
2698
|
-
}
|
|
2699
|
-
case "remove": {
|
|
2700
|
-
const { id } = item;
|
|
2701
|
-
handleRemove(editor, charToId(id), true);
|
|
2702
|
-
break;
|
|
2703
|
-
}
|
|
2704
|
-
case "insert":
|
|
2705
|
-
handleInsert(editor, {
|
|
2706
|
-
...item,
|
|
2707
|
-
delay: true
|
|
2708
|
-
}, dataSource);
|
|
2709
|
-
break;
|
|
2710
|
-
default: logger$7.warn(`⚠️ Unknown action type: ${action}`);
|
|
2711
|
-
}
|
|
2712
|
-
});
|
|
2713
|
-
return false;
|
|
2714
|
-
} catch (error) {
|
|
2715
|
-
logger$7.error("❌ Error processing LITEXML_MODIFY_COMMAND:", error);
|
|
2716
|
-
return false;
|
|
2717
|
-
}
|
|
3072
|
+
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (operations) => {
|
|
3073
|
+
applyLiteXMLBatch(editor, dataSource, operations);
|
|
3074
|
+
return false;
|
|
3075
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_MODIFY_WITH_RESULTS_COMMAND, ({ operations, onResults }) => {
|
|
3076
|
+
onResults(applyLiteXMLBatch(editor, dataSource, operations));
|
|
3077
|
+
return true;
|
|
2718
3078
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_APPLY_COMMAND, (payload) => {
|
|
2719
3079
|
const { litexml, delay } = payload;
|
|
2720
3080
|
const arrayXml = toArrayXml(litexml);
|
|
2721
3081
|
if (!delay) {
|
|
2722
|
-
handleModify(editor, dataSource, arrayXml
|
|
3082
|
+
handleModify(editor, dataSource, arrayXml);
|
|
2723
3083
|
return false;
|
|
2724
3084
|
}
|
|
2725
3085
|
const operation = {
|
|
2726
3086
|
action: "modify",
|
|
2727
3087
|
litexml
|
|
2728
3088
|
};
|
|
2729
|
-
|
|
3089
|
+
const document = getActiveSerializedDocument();
|
|
3090
|
+
try {
|
|
3091
|
+
const prepared = prepareModify(editor, dataSource, arrayXml);
|
|
3092
|
+
if (projectOperation(dataSource, document, toProjectionOperation(operation), prepared.readXml)) applyPreparedModify(editor, prepared);
|
|
3093
|
+
} catch (error) {
|
|
3094
|
+
logger$7.error("❌ Failed to apply LiteXML modification:", error);
|
|
3095
|
+
}
|
|
2730
3096
|
return false;
|
|
2731
3097
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_REMOVE_COMMAND, (payload) => {
|
|
2732
3098
|
const { id, delay } = payload;
|
|
2733
|
-
const key = charToId(id);
|
|
2734
3099
|
if (!delay) {
|
|
2735
|
-
handleRemove(editor,
|
|
3100
|
+
handleRemove(editor, id, delay);
|
|
2736
3101
|
return false;
|
|
2737
3102
|
}
|
|
2738
3103
|
const operation = {
|
|
2739
3104
|
action: "remove",
|
|
2740
3105
|
id
|
|
2741
3106
|
};
|
|
2742
|
-
if (projectOperation(dataSource,
|
|
3107
|
+
if (projectOperation(dataSource, getActiveSerializedDocument(), toProjectionOperation(operation))) handleRemove(editor, id, delay);
|
|
2743
3108
|
return false;
|
|
2744
3109
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_INSERT_COMMAND, (payload) => {
|
|
2745
3110
|
if (!payload.delay) {
|
|
2746
3111
|
handleInsert(editor, payload, dataSource);
|
|
2747
3112
|
return false;
|
|
2748
3113
|
}
|
|
2749
|
-
if (projectOperation(dataSource,
|
|
3114
|
+
if (projectOperation(dataSource, getActiveSerializedDocument(), toProjectionOperation({
|
|
2750
3115
|
action: "insert",
|
|
2751
3116
|
...payload
|
|
2752
3117
|
}))) handleInsert(editor, payload, dataSource);
|
|
2753
3118
|
return false;
|
|
2754
3119
|
}, COMMAND_PRIORITY_EDITOR));
|
|
2755
3120
|
}
|
|
2756
|
-
function
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
} else $insertNodes([newNode]);
|
|
3121
|
+
function applyPreparedModify(editor, prepared) {
|
|
3122
|
+
const modifyBlockNodes = /* @__PURE__ */ new Set();
|
|
3123
|
+
const diffNodeMap = /* @__PURE__ */ new Map();
|
|
3124
|
+
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.`);
|
|
3125
|
+
finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor);
|
|
3126
|
+
return prepared.pairs.length > 0;
|
|
3127
|
+
}
|
|
3128
|
+
function handleModify(editor, dataSource, arrayXml) {
|
|
3129
|
+
let applied = false;
|
|
3130
|
+
arrayXml.forEach((xml) => {
|
|
3131
|
+
const inode = dataSource.readLiteXMLToInode(xml);
|
|
3132
|
+
let prevNode = null;
|
|
3133
|
+
inode.root.children.forEach((child) => {
|
|
3134
|
+
try {
|
|
3135
|
+
const { oldNode, newNode } = tryParseChild(child, editor);
|
|
3136
|
+
if (oldNode && newNode) {
|
|
3137
|
+
prevNode = oldNode.replace(newNode, false);
|
|
3138
|
+
applied = true;
|
|
3139
|
+
} else if (newNode) {
|
|
3140
|
+
if (prevNode) {
|
|
3141
|
+
if (!newNode.isInline()) {
|
|
3142
|
+
const prevBlock = $closest(prevNode, (node) => node.isInline() === false);
|
|
3143
|
+
if (prevBlock) prevNode = prevBlock.insertAfter(newNode);
|
|
3144
|
+
else {
|
|
3145
|
+
$insertNodes([newNode]);
|
|
3146
|
+
prevNode = newNode;
|
|
3147
|
+
}
|
|
3148
|
+
applied = true;
|
|
3149
|
+
} else {
|
|
3150
|
+
prevNode = prevNode.insertAfter(newNode);
|
|
3151
|
+
applied = true;
|
|
3152
|
+
}
|
|
3153
|
+
} else {
|
|
3154
|
+
$insertNodes([newNode]);
|
|
3155
|
+
applied = true;
|
|
2792
3156
|
}
|
|
2793
|
-
} catch (error) {
|
|
2794
|
-
logger$7.error("❌ Error replacing node:", error);
|
|
2795
3157
|
}
|
|
2796
|
-
})
|
|
3158
|
+
} catch (error) {
|
|
3159
|
+
logger$7.error("❌ Error replacing node:", error);
|
|
3160
|
+
}
|
|
2797
3161
|
});
|
|
2798
3162
|
});
|
|
3163
|
+
return applied;
|
|
2799
3164
|
}
|
|
2800
|
-
function handleRemove(editor,
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
if ($isTableCellNode(node)) {
|
|
2806
|
-
const table = $getTableForCell(node);
|
|
2807
|
-
const columnIndex = $getTableCellColumnIndex(node);
|
|
2808
|
-
const span = node.getColSpan();
|
|
2809
|
-
node.remove();
|
|
2810
|
-
if (table && columnIndex >= 0) $shrinkTableWidthsAfterCellRemoval(table, columnIndex, span);
|
|
2811
|
-
return;
|
|
2812
|
-
}
|
|
2813
|
-
node.remove();
|
|
2814
|
-
return;
|
|
2815
|
-
}
|
|
3165
|
+
function handleRemove(editor, nodeId, delay) {
|
|
3166
|
+
let applied = false;
|
|
3167
|
+
const node = $findNodeById(nodeId);
|
|
3168
|
+
if (!node) return false;
|
|
3169
|
+
if (!delay) {
|
|
2816
3170
|
if ($isTableCellNode(node)) {
|
|
2817
3171
|
const table = $getTableForCell(node);
|
|
2818
3172
|
const columnIndex = $getTableCellColumnIndex(node);
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
node.replace($createTableCellDiffFromCell(editor, node, "remove", changeId), false);
|
|
2825
|
-
return;
|
|
3173
|
+
const span = node.getColSpan();
|
|
3174
|
+
node.remove();
|
|
3175
|
+
applied = true;
|
|
3176
|
+
if (table && columnIndex >= 0) $shrinkTableWidthsAfterCellRemoval(table, columnIndex, span);
|
|
3177
|
+
return true;
|
|
2826
3178
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
3179
|
+
node.remove();
|
|
3180
|
+
applied = true;
|
|
3181
|
+
return true;
|
|
3182
|
+
}
|
|
3183
|
+
if ($isTableCellNode(node)) {
|
|
3184
|
+
const table = $getTableForCell(node);
|
|
3185
|
+
const columnIndex = $getTableCellColumnIndex(node);
|
|
3186
|
+
if (!table || columnIndex < 0) {
|
|
3187
|
+
logger$7.error(`❌ Table cell ${node.getKey()} is not attached to a valid table row.`);
|
|
3188
|
+
return false;
|
|
2830
3189
|
}
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
3190
|
+
const changeId = `${table.getKey()}:column:${columnIndex}`;
|
|
3191
|
+
node.replace($createTableCellDiffFromCell(editor, node, "remove", changeId), false);
|
|
3192
|
+
applied = true;
|
|
3193
|
+
return true;
|
|
3194
|
+
}
|
|
3195
|
+
if ($isTableRowNode(node) && $isTableNode(node.getParent())) {
|
|
3196
|
+
node.replace($createTableRowDiffFromRow(editor, node, "remove"), false);
|
|
3197
|
+
applied = true;
|
|
3198
|
+
return true;
|
|
3199
|
+
}
|
|
3200
|
+
if (node.isInline() === false) {
|
|
3201
|
+
const originDiffNode = $closest(node, (node) => node.getType() === DiffNode.getType());
|
|
3202
|
+
if (originDiffNode) {
|
|
3203
|
+
switch (originDiffNode.diffType) {
|
|
3204
|
+
case "add":
|
|
3205
|
+
originDiffNode.remove();
|
|
3206
|
+
applied = true;
|
|
3207
|
+
return true;
|
|
3208
|
+
case "modify": {
|
|
3209
|
+
const children = originDiffNode.getChildren();
|
|
3210
|
+
const newDiff = $createDiffNode("remove");
|
|
3211
|
+
const before = children[0];
|
|
3212
|
+
newDiff.append(...$isDiffContentNode(before) ? before.getChildren() : [before]);
|
|
3213
|
+
originDiffNode.replace(newDiff, false);
|
|
3214
|
+
applied = true;
|
|
3215
|
+
return true;
|
|
3216
|
+
}
|
|
3217
|
+
case "listItemModify": {
|
|
3218
|
+
const children = originDiffNode.getChildren();
|
|
3219
|
+
originDiffNode.replace(children[0], false).selectEnd();
|
|
3220
|
+
applied = true;
|
|
3221
|
+
return true;
|
|
2851
3222
|
}
|
|
2852
|
-
return;
|
|
2853
|
-
}
|
|
2854
|
-
if ($isListItemNode(node)) {
|
|
2855
|
-
const diffNode = $createDiffNode("listItemRemove");
|
|
2856
|
-
node.getChildren().forEach((child) => {
|
|
2857
|
-
diffNode.append($cloneNode(child, editor));
|
|
2858
|
-
});
|
|
2859
|
-
node.clear();
|
|
2860
|
-
node.append(diffNode);
|
|
2861
|
-
} else {
|
|
2862
|
-
const diffNode = $createDiffNode("remove");
|
|
2863
|
-
diffNode.append($cloneNode(node, editor));
|
|
2864
|
-
node.replace(diffNode, false);
|
|
2865
|
-
}
|
|
2866
|
-
} else {
|
|
2867
|
-
const oldBlock = $closest(node, (node) => node.isInline() === false);
|
|
2868
|
-
if (!oldBlock) throw new Error("Old block node not found for removal.");
|
|
2869
|
-
if ($closest(node, (node) => node.getType() === DiffNode.getType())) {
|
|
2870
|
-
node.remove();
|
|
2871
|
-
return;
|
|
2872
3223
|
}
|
|
2873
|
-
|
|
2874
|
-
|
|
3224
|
+
return false;
|
|
3225
|
+
}
|
|
3226
|
+
if ($isListItemNode(node)) {
|
|
3227
|
+
const diffNode = $createDiffNode("listItemRemove");
|
|
3228
|
+
node.getChildren().forEach((child) => {
|
|
3229
|
+
diffNode.append($cloneNode(child, editor));
|
|
2875
3230
|
});
|
|
3231
|
+
node.clear();
|
|
3232
|
+
node.append(diffNode);
|
|
3233
|
+
applied = true;
|
|
3234
|
+
} else {
|
|
3235
|
+
const diffNode = $createDiffNode("remove");
|
|
3236
|
+
diffNode.append($cloneNode(node, editor));
|
|
3237
|
+
node.replace(diffNode, false);
|
|
3238
|
+
applied = true;
|
|
2876
3239
|
}
|
|
2877
|
-
}
|
|
3240
|
+
} else {
|
|
3241
|
+
const oldBlock = $closest(node, (node) => node.isInline() === false);
|
|
3242
|
+
if (!oldBlock) throw new Error("Old block node not found for removal.");
|
|
3243
|
+
if ($closest(node, (node) => node.getType() === DiffNode.getType())) {
|
|
3244
|
+
node.remove();
|
|
3245
|
+
applied = true;
|
|
3246
|
+
return true;
|
|
3247
|
+
}
|
|
3248
|
+
wrapBlockModify(oldBlock, editor, () => {
|
|
3249
|
+
node.remove();
|
|
3250
|
+
});
|
|
3251
|
+
applied = true;
|
|
3252
|
+
}
|
|
3253
|
+
return applied;
|
|
2878
3254
|
}
|
|
2879
|
-
function handleInsert(editor, payload, dataSource) {
|
|
3255
|
+
function handleInsert(editor, payload, dataSource, referenceNodeOverride) {
|
|
2880
3256
|
const { litexml, delay } = payload;
|
|
2881
3257
|
const isBefore = "beforeId" in payload;
|
|
2882
3258
|
const inode = dataSource.readLiteXMLToInode(litexml);
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
3259
|
+
let insertedNodeId = null;
|
|
3260
|
+
try {
|
|
3261
|
+
let referenceNode = referenceNodeOverride || null;
|
|
3262
|
+
if (!referenceNode) {
|
|
2886
3263
|
if (isBefore) {
|
|
2887
3264
|
if (payload.beforeId === "root") referenceNode = $getRoot().getFirstChild();
|
|
2888
|
-
else referenceNode = $
|
|
3265
|
+
else referenceNode = $findNodeById(payload.beforeId);
|
|
2889
3266
|
} else if (payload.afterId === "root") referenceNode = $getRoot().getLastChild();
|
|
2890
|
-
else referenceNode = $
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
const referenceIndex = $getTableCellColumnIndex(cellReference);
|
|
2903
|
-
if (!table || referenceIndex < 0) {
|
|
2904
|
-
logger$7.error("❌ Table cell insertion requires a valid table parent.");
|
|
2905
|
-
return;
|
|
2906
|
-
}
|
|
2907
|
-
const rowWidthBefore = $getLogicalRowWidth(cellReference);
|
|
2908
|
-
const insertionIndex = isBefore ? referenceIndex : referenceIndex + cellReference.getColSpan();
|
|
2909
|
-
$updateTableWidthsForCellInsertion(table, rowWidthBefore, insertionIndex, newNodes.reduce((total, node) => total + ($isTableCellNode(node) ? node.getColSpan() : 0), 0));
|
|
2910
|
-
let spanOffset = 0;
|
|
2911
|
-
const cells = newNodes.map((node) => {
|
|
2912
|
-
const result = delay ? $createTableCellDiffFromCell(editor, node, "add", `${table.getKey()}:column:${insertionIndex + spanOffset}`) : node;
|
|
2913
|
-
spanOffset += node.getColSpan();
|
|
2914
|
-
return result;
|
|
2915
|
-
});
|
|
2916
|
-
if (isBefore) cells.reverse().forEach((cell) => {
|
|
2917
|
-
referenceNode = referenceNode.insertBefore(cell);
|
|
2918
|
-
});
|
|
2919
|
-
else cells.forEach((cell) => {
|
|
2920
|
-
referenceNode = referenceNode.insertAfter(cell);
|
|
2921
|
-
});
|
|
2922
|
-
return;
|
|
3267
|
+
else referenceNode = $findNodeById(payload.afterId);
|
|
3268
|
+
}
|
|
3269
|
+
if (!referenceNode) throw new Error("Reference node not found for insertion.");
|
|
3270
|
+
const newNodes = inode.root.children.map((child) => $parseSerializedNodeImpl(child, editor));
|
|
3271
|
+
$ensureUniqueNodeIds(newNodes);
|
|
3272
|
+
insertedNodeId = newNodes.length ? $getNodeId(newNodes.at(-1)) || null : null;
|
|
3273
|
+
const referencesTableCell = $isTableCellNode(referenceNode);
|
|
3274
|
+
const insertsOnlyTableCells = newNodes.length > 0 && newNodes.every($isTableCellNode);
|
|
3275
|
+
if (referencesTableCell || insertsOnlyTableCells) {
|
|
3276
|
+
if (!referencesTableCell || !insertsOnlyTableCells) {
|
|
3277
|
+
logger$7.error("❌ Table cells can only be inserted next to another cell in the same row.");
|
|
3278
|
+
return null;
|
|
2923
3279
|
}
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
});
|
|
2931
|
-
return;
|
|
3280
|
+
const cellReference = referenceNode;
|
|
3281
|
+
const table = $getTableForCell(cellReference);
|
|
3282
|
+
const referenceIndex = $getTableCellColumnIndex(cellReference);
|
|
3283
|
+
if (!table || referenceIndex < 0) {
|
|
3284
|
+
logger$7.error("❌ Table cell insertion requires a valid table parent.");
|
|
3285
|
+
return null;
|
|
2932
3286
|
}
|
|
2933
|
-
const
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
3287
|
+
const rowWidthBefore = $getLogicalRowWidth(cellReference);
|
|
3288
|
+
const insertionIndex = isBefore ? referenceIndex : referenceIndex + cellReference.getColSpan();
|
|
3289
|
+
$updateTableWidthsForCellInsertion(table, rowWidthBefore, insertionIndex, newNodes.reduce((total, node) => total + ($isTableCellNode(node) ? node.getColSpan() : 0), 0));
|
|
3290
|
+
let spanOffset = 0;
|
|
3291
|
+
const cells = newNodes.map((node) => {
|
|
3292
|
+
const result = delay ? $createTableCellDiffFromCell(editor, node, "add", `${table.getKey()}:column:${insertionIndex + spanOffset}`) : node;
|
|
3293
|
+
spanOffset += node.getColSpan();
|
|
3294
|
+
return result;
|
|
3295
|
+
});
|
|
3296
|
+
if (isBefore) cells.reverse().forEach((cell) => {
|
|
3297
|
+
referenceNode = referenceNode.insertBefore(cell);
|
|
3298
|
+
});
|
|
3299
|
+
else cells.forEach((cell) => {
|
|
3300
|
+
referenceNode = referenceNode.insertAfter(cell);
|
|
3301
|
+
});
|
|
3302
|
+
return insertedNodeId;
|
|
3303
|
+
}
|
|
3304
|
+
if (!delay) {
|
|
3305
|
+
if (isBefore) newNodes.reverse().forEach((node) => {
|
|
3306
|
+
referenceNode = referenceNode.insertBefore(node);
|
|
3307
|
+
});
|
|
3308
|
+
else newNodes.forEach((node) => {
|
|
3309
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
3310
|
+
});
|
|
3311
|
+
return insertedNodeId;
|
|
3312
|
+
}
|
|
3313
|
+
const referencesTableRow = $isTableRowNode(referenceNode);
|
|
3314
|
+
const insertsOnlyTableRows = newNodes.every($isTableRowNode);
|
|
3315
|
+
if (referencesTableRow || insertsOnlyTableRows) {
|
|
3316
|
+
if (!referencesTableRow || !insertsOnlyTableRows || !$isTableNode(referenceNode.getParent())) {
|
|
3317
|
+
logger$7.error("❌ Table rows can only be inserted next to another row in the same table.");
|
|
3318
|
+
return null;
|
|
2951
3319
|
}
|
|
2952
|
-
if (isBefore) {
|
|
2953
|
-
if (
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
|
|
2967
|
-
});
|
|
2968
|
-
} else {
|
|
2969
|
-
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
2970
|
-
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
2971
|
-
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
2972
|
-
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
2973
|
-
});
|
|
2974
|
-
else wrapBlockModify(refBlock, editor, () => {
|
|
2975
|
-
newNodes.forEach((node) => {
|
|
2976
|
-
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
2977
|
-
});
|
|
2978
|
-
});
|
|
2979
|
-
}
|
|
2980
|
-
} else if (referenceNode.isInline() === false) {
|
|
3320
|
+
if (isBefore) newNodes.reverse().forEach((node) => {
|
|
3321
|
+
if (!$isTableRowNode(node)) return;
|
|
3322
|
+
const diffRow = $createTableRowDiffFromRow(editor, node, "add");
|
|
3323
|
+
referenceNode = referenceNode.insertBefore(diffRow);
|
|
3324
|
+
});
|
|
3325
|
+
else newNodes.forEach((node) => {
|
|
3326
|
+
if (!$isTableRowNode(node)) return;
|
|
3327
|
+
const diffRow = $createTableRowDiffFromRow(editor, node, "add");
|
|
3328
|
+
referenceNode = referenceNode.insertAfter(diffRow);
|
|
3329
|
+
});
|
|
3330
|
+
return insertedNodeId;
|
|
3331
|
+
}
|
|
3332
|
+
if (isBefore) {
|
|
3333
|
+
if (referenceNode.isInline() === false) {
|
|
2981
3334
|
const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
|
|
2982
3335
|
if (originDiffNode) referenceNode = originDiffNode;
|
|
2983
|
-
newNodes.
|
|
2984
|
-
if (
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
diffNode.append(child);
|
|
2989
|
-
});
|
|
2990
|
-
node.append(diffNode);
|
|
2991
|
-
referenceNode = referenceNode.insertAfter(node);
|
|
2992
|
-
} else {
|
|
2993
|
-
const diffNode = $createDiffNode("add");
|
|
2994
|
-
diffNode.append(node);
|
|
2995
|
-
referenceNode = referenceNode.insertAfter(diffNode);
|
|
2996
|
-
}
|
|
3336
|
+
newNodes.map((node) => {
|
|
3337
|
+
if ($isListItemNode(node)) {
|
|
3338
|
+
const diffNode = $createDiffNode("listItemAdd");
|
|
3339
|
+
node.getChildren().forEach((child) => diffNode.append(child));
|
|
3340
|
+
return node.append(diffNode);
|
|
2997
3341
|
}
|
|
3342
|
+
const diffNode = $createDiffNode("add");
|
|
3343
|
+
diffNode.append(node);
|
|
3344
|
+
return diffNode;
|
|
3345
|
+
}).reverse().forEach((diffNode) => {
|
|
3346
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
|
|
2998
3347
|
});
|
|
2999
3348
|
} else {
|
|
3000
3349
|
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
3001
3350
|
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
3002
3351
|
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
3003
|
-
if (referenceNode) referenceNode = referenceNode.
|
|
3352
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
3004
3353
|
});
|
|
3005
3354
|
else wrapBlockModify(refBlock, editor, () => {
|
|
3006
3355
|
newNodes.forEach((node) => {
|
|
3007
|
-
if (referenceNode) referenceNode = referenceNode.
|
|
3356
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
3008
3357
|
});
|
|
3009
3358
|
});
|
|
3010
3359
|
}
|
|
3011
|
-
}
|
|
3012
|
-
|
|
3360
|
+
} else if (referenceNode.isInline() === false) {
|
|
3361
|
+
const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
|
|
3362
|
+
if (originDiffNode) referenceNode = originDiffNode;
|
|
3363
|
+
newNodes.forEach((node) => {
|
|
3364
|
+
if (referenceNode) {
|
|
3365
|
+
if ($isListItemNode(node)) {
|
|
3366
|
+
const diffNode = $createDiffNode("listItemAdd");
|
|
3367
|
+
node.getChildren().forEach((child) => {
|
|
3368
|
+
diffNode.append(child);
|
|
3369
|
+
});
|
|
3370
|
+
node.append(diffNode);
|
|
3371
|
+
referenceNode = referenceNode.insertAfter(node);
|
|
3372
|
+
} else {
|
|
3373
|
+
const diffNode = $createDiffNode("add");
|
|
3374
|
+
diffNode.append(node);
|
|
3375
|
+
referenceNode = referenceNode.insertAfter(diffNode);
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
});
|
|
3379
|
+
} else {
|
|
3380
|
+
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
3381
|
+
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
3382
|
+
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
3383
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
3384
|
+
});
|
|
3385
|
+
else wrapBlockModify(refBlock, editor, () => {
|
|
3386
|
+
newNodes.forEach((node) => {
|
|
3387
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
3388
|
+
});
|
|
3389
|
+
});
|
|
3013
3390
|
}
|
|
3014
|
-
})
|
|
3391
|
+
} catch (error) {
|
|
3392
|
+
logger$7.error("❌ Error inserting node:", error);
|
|
3393
|
+
insertedNodeId = null;
|
|
3394
|
+
}
|
|
3395
|
+
return insertedNodeId;
|
|
3015
3396
|
}
|
|
3016
3397
|
//#endregion
|
|
3017
3398
|
//#region src/plugins/litexml/command/diffCommand.ts
|
|
@@ -3294,13 +3675,8 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3294
3675
|
read(editor, data) {
|
|
3295
3676
|
try {
|
|
3296
3677
|
const inode = this.readLiteXMLToInode(data);
|
|
3297
|
-
const newState = editor.parseEditorState(
|
|
3298
|
-
|
|
3299
|
-
const root = $parseSerializedNodeImpl(inode.root, editor, true, state);
|
|
3300
|
-
state._nodeMap.set(root.getKey(), root);
|
|
3301
|
-
} catch (error) {
|
|
3302
|
-
console.error(error);
|
|
3303
|
-
}
|
|
3678
|
+
const newState = editor.parseEditorState(inode, () => {
|
|
3679
|
+
$normalizeNodeIds($getRoot());
|
|
3304
3680
|
});
|
|
3305
3681
|
editor.setEditorState(newState);
|
|
3306
3682
|
} catch (error) {
|
|
@@ -3366,6 +3742,22 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3366
3742
|
processXMLElement(xmlElement, parentNode) {
|
|
3367
3743
|
const tagName = xmlElement.tagName.toLowerCase();
|
|
3368
3744
|
const customReaders = this.litexmlService.getXMLReaders();
|
|
3745
|
+
const publicId = xmlElement.getAttribute("id") || void 0;
|
|
3746
|
+
const attachPublicId = (node, id = publicId) => {
|
|
3747
|
+
if (!node || !id) return;
|
|
3748
|
+
node.id = id;
|
|
3749
|
+
node.$ = {
|
|
3750
|
+
...node.$,
|
|
3751
|
+
properties: {
|
|
3752
|
+
...node.$?.properties,
|
|
3753
|
+
nodeId: id
|
|
3754
|
+
}
|
|
3755
|
+
};
|
|
3756
|
+
};
|
|
3757
|
+
const append = (node) => {
|
|
3758
|
+
attachPublicId(node);
|
|
3759
|
+
INodeHelper.appendChild(parentNode, node);
|
|
3760
|
+
};
|
|
3369
3761
|
if (customReaders[tagName]) {
|
|
3370
3762
|
const readerOrReaders = customReaders[tagName];
|
|
3371
3763
|
const readers = Array.isArray(readerOrReaders) ? readerOrReaders : [readerOrReaders];
|
|
@@ -3375,16 +3767,9 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3375
3767
|
const result = reader(xmlElement, children);
|
|
3376
3768
|
if (result !== false) {
|
|
3377
3769
|
if (Array.isArray(result)) {
|
|
3378
|
-
if (result.length > 0)
|
|
3379
|
-
const attrId = xmlElement.getAttribute("id");
|
|
3380
|
-
result[0].id = attrId ? charToId(attrId) : void 0;
|
|
3381
|
-
}
|
|
3770
|
+
if (result.length > 0) attachPublicId(result[0]);
|
|
3382
3771
|
INodeHelper.appendChild(parentNode, ...result);
|
|
3383
|
-
} else if (result)
|
|
3384
|
-
const attrId = xmlElement.getAttribute("id");
|
|
3385
|
-
result.id = attrId ? charToId(attrId) : void 0;
|
|
3386
|
-
INodeHelper.appendChild(parentNode, result);
|
|
3387
|
-
}
|
|
3772
|
+
} else if (result) append(result);
|
|
3388
3773
|
return;
|
|
3389
3774
|
}
|
|
3390
3775
|
}
|
|
@@ -3394,7 +3779,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3394
3779
|
case "paragraph": {
|
|
3395
3780
|
const paragraph = INodeHelper.createParagraph();
|
|
3396
3781
|
this.processXMLChildren(xmlElement, paragraph);
|
|
3397
|
-
|
|
3782
|
+
append(paragraph);
|
|
3398
3783
|
break;
|
|
3399
3784
|
}
|
|
3400
3785
|
case "h1":
|
|
@@ -3409,7 +3794,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3409
3794
|
tag: `h${level}`
|
|
3410
3795
|
});
|
|
3411
3796
|
this.processXMLChildren(xmlElement, heading);
|
|
3412
|
-
|
|
3797
|
+
append(heading);
|
|
3413
3798
|
break;
|
|
3414
3799
|
}
|
|
3415
3800
|
case "ul":
|
|
@@ -3421,6 +3806,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3421
3806
|
children: [],
|
|
3422
3807
|
value: 1
|
|
3423
3808
|
});
|
|
3809
|
+
attachPublicId(listItem, child.getAttribute("id") || void 0);
|
|
3424
3810
|
this.processXMLChildren(child, listItem);
|
|
3425
3811
|
INodeHelper.appendChild(parentNode, listItem);
|
|
3426
3812
|
});
|
|
@@ -3428,20 +3814,15 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3428
3814
|
case "blockquote": {
|
|
3429
3815
|
const quote = INodeHelper.createElementNode("quote", { children: [] });
|
|
3430
3816
|
this.processXMLChildren(xmlElement, quote);
|
|
3431
|
-
|
|
3817
|
+
append(quote);
|
|
3432
3818
|
break;
|
|
3433
3819
|
}
|
|
3434
|
-
case "code":
|
|
3435
|
-
|
|
3436
|
-
INodeHelper.appendChild(parentNode, codeNode);
|
|
3820
|
+
case "code":
|
|
3821
|
+
append(INodeHelper.createElementNode("codeInline", { children: [INodeHelper.createTextNode(xmlElement.textContent || "")] }));
|
|
3437
3822
|
break;
|
|
3438
|
-
}
|
|
3439
3823
|
case "text": {
|
|
3440
3824
|
const textContent = xmlElement.textContent || "";
|
|
3441
|
-
if (textContent)
|
|
3442
|
-
const textNode = INodeHelper.createTextNode(textContent);
|
|
3443
|
-
INodeHelper.appendChild(parentNode, textNode);
|
|
3444
|
-
}
|
|
3825
|
+
if (textContent) append(INodeHelper.createTextNode(textContent));
|
|
3445
3826
|
break;
|
|
3446
3827
|
}
|
|
3447
3828
|
default: this.processXMLChildren(xmlElement, parentNode);
|
|
@@ -3492,10 +3873,12 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3492
3873
|
lines.push(...handled.lines);
|
|
3493
3874
|
return;
|
|
3494
3875
|
}
|
|
3495
|
-
const
|
|
3496
|
-
id:
|
|
3876
|
+
const attributes = {
|
|
3877
|
+
id: $getNodeId(node),
|
|
3497
3878
|
...handled.attributes
|
|
3498
|
-
}
|
|
3879
|
+
};
|
|
3880
|
+
attributes.id = $getNodeId(node);
|
|
3881
|
+
const attrs = this.buildXMLAttributes(attributes);
|
|
3499
3882
|
const openTag = `${indentStr}<${handled.tagName}${attrs}>`;
|
|
3500
3883
|
const closeTag = `</${handled.tagName}>`;
|
|
3501
3884
|
if (handled.textContent) lines.push(`${openTag}${handled.textContent}${closeTag}`);
|
|
@@ -3527,7 +3910,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
3527
3910
|
* Escape XML special characters
|
|
3528
3911
|
*/
|
|
3529
3912
|
escapeXML(text) {
|
|
3530
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
3913
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'").replaceAll(" ", "	").replaceAll("\n", " ").replaceAll("\r", " ");
|
|
3531
3914
|
}
|
|
3532
3915
|
};
|
|
3533
3916
|
//#endregion
|
|
@@ -3900,6 +4283,74 @@ const SPICAL_TEXT = "";
|
|
|
3900
4283
|
const getLineNumber = (content, charIndex) => {
|
|
3901
4284
|
return content.slice(0, Math.max(0, charIndex)).split("\n").length;
|
|
3902
4285
|
};
|
|
4286
|
+
function getNodeReferenceForKey(editor, key) {
|
|
4287
|
+
return editor.getEditorState().read(() => {
|
|
4288
|
+
const node = $getNodeByKey(key);
|
|
4289
|
+
if (!node) return null;
|
|
4290
|
+
if ($isRootNode(node)) return { kind: "root" };
|
|
4291
|
+
const id = $getNodeId(node);
|
|
4292
|
+
return id ? {
|
|
4293
|
+
id,
|
|
4294
|
+
kind: "node"
|
|
4295
|
+
} : null;
|
|
4296
|
+
});
|
|
4297
|
+
}
|
|
4298
|
+
function getNodeKeyForReference(editor, reference, offset) {
|
|
4299
|
+
return editor.getEditorState().read(() => {
|
|
4300
|
+
if (reference.kind === "node") {
|
|
4301
|
+
const node = $findNodeById(reference.id);
|
|
4302
|
+
return node ? {
|
|
4303
|
+
key: node.getKey(),
|
|
4304
|
+
offset
|
|
4305
|
+
} : void 0;
|
|
4306
|
+
}
|
|
4307
|
+
const root = $getRoot();
|
|
4308
|
+
const findBoundaryText = (node, first) => {
|
|
4309
|
+
if ($isTextNode(node)) return node;
|
|
4310
|
+
if (!$isElementNode(node)) return null;
|
|
4311
|
+
const children = node.getChildren();
|
|
4312
|
+
const orderedChildren = first ? children : [...children].reverse();
|
|
4313
|
+
for (const child of orderedChildren) {
|
|
4314
|
+
const textNode = findBoundaryText(child, first);
|
|
4315
|
+
if (textNode) return textNode;
|
|
4316
|
+
}
|
|
4317
|
+
return null;
|
|
4318
|
+
};
|
|
4319
|
+
if (offset === 0) {
|
|
4320
|
+
const firstText = findBoundaryText(root, true);
|
|
4321
|
+
if (firstText) return {
|
|
4322
|
+
key: firstText.getKey(),
|
|
4323
|
+
offset: 0
|
|
4324
|
+
};
|
|
4325
|
+
}
|
|
4326
|
+
if (offset === root.getChildrenSize()) {
|
|
4327
|
+
const lastText = findBoundaryText(root, false);
|
|
4328
|
+
if (lastText) return {
|
|
4329
|
+
key: lastText.getKey(),
|
|
4330
|
+
offset: lastText.getTextContentSize()
|
|
4331
|
+
};
|
|
4332
|
+
}
|
|
4333
|
+
return {
|
|
4334
|
+
key: root.getKey(),
|
|
4335
|
+
offset
|
|
4336
|
+
};
|
|
4337
|
+
}, { editor });
|
|
4338
|
+
}
|
|
4339
|
+
function mapSelectionToEditor(selection, sourceEditor, targetEditor) {
|
|
4340
|
+
const startReference = getNodeReferenceForKey(sourceEditor, selection.startNodeId);
|
|
4341
|
+
const endReference = getNodeReferenceForKey(sourceEditor, selection.endNodeId);
|
|
4342
|
+
if (!startReference || !endReference) return null;
|
|
4343
|
+
const startPoint = getNodeKeyForReference(targetEditor, startReference, selection.startOffset);
|
|
4344
|
+
const endPoint = getNodeKeyForReference(targetEditor, endReference, selection.endOffset);
|
|
4345
|
+
if (!startPoint || !endPoint) return null;
|
|
4346
|
+
return {
|
|
4347
|
+
...selection,
|
|
4348
|
+
endNodeId: endPoint.key,
|
|
4349
|
+
endOffset: endPoint.offset,
|
|
4350
|
+
startNodeId: startPoint.key,
|
|
4351
|
+
startOffset: startPoint.offset
|
|
4352
|
+
};
|
|
4353
|
+
}
|
|
3903
4354
|
function registerMarkdownCommand(editor, kernel, service) {
|
|
3904
4355
|
return mergeRegister(editor.registerCommand(INSERT_MARKDOWN_COMMAND, (payload) => {
|
|
3905
4356
|
const { markdown } = payload;
|
|
@@ -3922,9 +4373,12 @@ function registerMarkdownCommand(editor, kernel, service) {
|
|
|
3922
4373
|
return false;
|
|
3923
4374
|
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(GET_MARKDOWN_SELECTION_COMMAND, (payload) => {
|
|
3924
4375
|
const newEditor = kernel.cloneNodeEditor();
|
|
3925
|
-
const
|
|
3926
|
-
|
|
3927
|
-
|
|
4376
|
+
const sourceEditor = kernel.getLexicalEditor();
|
|
4377
|
+
const selection = kernel.getSelection();
|
|
4378
|
+
const targetEditor = newEditor.getLexicalEditor();
|
|
4379
|
+
const mappedSelection = sourceEditor && targetEditor && selection ? mapSelectionToEditor(selection, sourceEditor, targetEditor) : null;
|
|
4380
|
+
if (mappedSelection && targetEditor) {
|
|
4381
|
+
newEditor.setSelection(mappedSelection);
|
|
3928
4382
|
newEditor.getLexicalEditor()?.update(() => {
|
|
3929
4383
|
const sel = $getSelection();
|
|
3930
4384
|
if (!sel) return;
|
|
@@ -4021,7 +4475,10 @@ var MarkdownDataSource = class extends DataSource {
|
|
|
4021
4475
|
const inode = { root: parseMarkdownToLexical(data, this.markdownService.markdownReaders) };
|
|
4022
4476
|
this.getService?.(INodeService)?.processNodeTree(inode);
|
|
4023
4477
|
logger$5.debug("Parsed Lexical State:", inode);
|
|
4024
|
-
editor.
|
|
4478
|
+
const editorState = editor.parseEditorState(inode, () => {
|
|
4479
|
+
$normalizeNodeIds($getRoot());
|
|
4480
|
+
});
|
|
4481
|
+
editor.setEditorState(editorState);
|
|
4025
4482
|
}
|
|
4026
4483
|
write(editor, options) {
|
|
4027
4484
|
if (options?.selection) return editor.getEditorState().read(() => {
|
|
@@ -4901,6 +5358,7 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
4901
5358
|
if (config?.enabled !== false) kernel.registerDataSource(this.datasource);
|
|
4902
5359
|
}
|
|
4903
5360
|
onInit(editor) {
|
|
5361
|
+
this.register(registerNodeIdentityPolicy(editor, liteXmlIdentityPolicy));
|
|
4904
5362
|
this.register(registerLiteXMLCommand(editor, this.datasource));
|
|
4905
5363
|
this.register(registerLiteXMLDiffCommand(editor));
|
|
4906
5364
|
this.register(registerLegacyTableCellDiffNormalization(editor));
|
|
@@ -4940,6 +5398,7 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
4940
5398
|
diffNode.getChildren().forEach((child) => nodeToXML(child, lines, indent));
|
|
4941
5399
|
break;
|
|
4942
5400
|
case "remove": break;
|
|
5401
|
+
case "listItemRemove": break;
|
|
4943
5402
|
case "listItemModify": diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
|
|
4944
5403
|
nodeToXML(child, lines, indent);
|
|
4945
5404
|
});
|
|
@@ -4992,6 +5451,7 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
4992
5451
|
diffNode.getChildren().forEach((child) => ctx.processChild(ctx, child));
|
|
4993
5452
|
break;
|
|
4994
5453
|
case "remove": break;
|
|
5454
|
+
case "listItemRemove": break;
|
|
4995
5455
|
case "listItemModify": diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
|
|
4996
5456
|
ctx.processChild(ctx, child);
|
|
4997
5457
|
});
|
|
@@ -5004,9 +5464,32 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
5004
5464
|
//#region src/plugins/common/data-source/json-data-source.ts
|
|
5005
5465
|
var JSONDataSource = class extends DataSource {
|
|
5006
5466
|
read(editor, data, options = {}) {
|
|
5007
|
-
let
|
|
5008
|
-
if (typeof data === "string")
|
|
5009
|
-
else
|
|
5467
|
+
let inputData;
|
|
5468
|
+
if (typeof data === "string") inputData = JSON.parse(data);
|
|
5469
|
+
else inputData = data;
|
|
5470
|
+
const dataObj = structuredClone(inputData);
|
|
5471
|
+
if (!(options.keepId ?? dataObj.keepId ?? false)) {
|
|
5472
|
+
const stripNodeIds = (node) => {
|
|
5473
|
+
delete node.id;
|
|
5474
|
+
const state = node.$;
|
|
5475
|
+
if (state && typeof state === "object" && state.properties) {
|
|
5476
|
+
const properties = { ...state.properties };
|
|
5477
|
+
delete properties.nodeId;
|
|
5478
|
+
if (Object.keys(properties).length > 0) node.$ = {
|
|
5479
|
+
...state,
|
|
5480
|
+
properties
|
|
5481
|
+
};
|
|
5482
|
+
else {
|
|
5483
|
+
const nextState = { ...state };
|
|
5484
|
+
delete nextState.properties;
|
|
5485
|
+
if (Object.keys(nextState).length > 0) node.$ = nextState;
|
|
5486
|
+
else delete node.$;
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
if (Array.isArray(node.children)) node.children.forEach((child) => stripNodeIds(child));
|
|
5490
|
+
};
|
|
5491
|
+
stripNodeIds(dataObj.root);
|
|
5492
|
+
} else migrateSerializedNodeIds(dataObj.root);
|
|
5010
5493
|
const process = (node) => {
|
|
5011
5494
|
for (let i = 0; i < node.children.length; i++) {
|
|
5012
5495
|
const child = node.children[i];
|
|
@@ -5028,24 +5511,10 @@ var JSONDataSource = class extends DataSource {
|
|
|
5028
5511
|
}
|
|
5029
5512
|
};
|
|
5030
5513
|
process(dataObj.root);
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
let maxId = -1;
|
|
5036
|
-
Array.from(state._nodeMap.keys()).forEach((key) => {
|
|
5037
|
-
if (key === "root") return;
|
|
5038
|
-
const numericKey = Number(key);
|
|
5039
|
-
if (Number.isInteger(numericKey) && numericKey >= 0) maxId = Math.max(maxId, numericKey);
|
|
5040
|
-
});
|
|
5041
|
-
resetRandomKey(maxId + 1);
|
|
5042
|
-
state._nodeMap.set(root.getKey(), root);
|
|
5043
|
-
} catch (error) {
|
|
5044
|
-
console.error(error);
|
|
5045
|
-
}
|
|
5046
|
-
});
|
|
5047
|
-
editor.setEditorState(state);
|
|
5048
|
-
} else editor.setEditorState(editor.parseEditorState({ root: dataObj.root }));
|
|
5514
|
+
const editorState = editor.parseEditorState({ root: dataObj.root }, () => {
|
|
5515
|
+
$normalizeNodeIds($getRoot());
|
|
5516
|
+
});
|
|
5517
|
+
editor.setEditorState(editorState);
|
|
5049
5518
|
}
|
|
5050
5519
|
write(editor, options) {
|
|
5051
5520
|
if (options?.selection) return editor.getEditorState().read(() => {
|
|
@@ -5139,7 +5608,10 @@ var TextDataSource = class extends DataSource {
|
|
|
5139
5608
|
}
|
|
5140
5609
|
INodeHelper.appendChild(rootNode, paragraph);
|
|
5141
5610
|
});
|
|
5142
|
-
editor.
|
|
5611
|
+
const editorState = editor.parseEditorState({ root: rootNode }, () => {
|
|
5612
|
+
$normalizeNodeIds($getRoot());
|
|
5613
|
+
});
|
|
5614
|
+
editor.setEditorState(editorState);
|
|
5143
5615
|
}
|
|
5144
5616
|
write(editor, options) {
|
|
5145
5617
|
if (options?.selection) return editor.getEditorState().read(() => {
|
|
@@ -5954,6 +6426,7 @@ const CommonPlugin = class extends KernelPlugin {
|
|
|
5954
6426
|
super();
|
|
5955
6427
|
this.kernel = kernel;
|
|
5956
6428
|
this.config = config;
|
|
6429
|
+
this.identityService = new NodeIdentityService();
|
|
5957
6430
|
this.formats = {
|
|
5958
6431
|
bold: true,
|
|
5959
6432
|
header: true,
|
|
@@ -5963,6 +6436,7 @@ const CommonPlugin = class extends KernelPlugin {
|
|
|
5963
6436
|
subscript: false,
|
|
5964
6437
|
superscript: false
|
|
5965
6438
|
};
|
|
6439
|
+
kernel.registerService(INodeIdentityService, this.identityService);
|
|
5966
6440
|
const markdownOption = config.markdownOption ?? true;
|
|
5967
6441
|
if (typeof markdownOption === "object") {
|
|
5968
6442
|
this.formats.bold = markdownOption.bold ?? true;
|
|
@@ -6103,6 +6577,15 @@ const CommonPlugin = class extends KernelPlugin {
|
|
|
6103
6577
|
});
|
|
6104
6578
|
markdownService.registerMarkdownWriter("text", (ctx, node) => {
|
|
6105
6579
|
if (!$isTextNode(node)) return;
|
|
6580
|
+
let parentNode = node.getParent();
|
|
6581
|
+
let isInTableCell = false;
|
|
6582
|
+
while (parentNode) {
|
|
6583
|
+
if (parentNode.getType() === "tablecell" || parentNode.getType() === "table-cell-diff") {
|
|
6584
|
+
isInTableCell = true;
|
|
6585
|
+
break;
|
|
6586
|
+
}
|
|
6587
|
+
parentNode = parentNode.getParent();
|
|
6588
|
+
}
|
|
6106
6589
|
const isBold = formats.bold && node.hasFormat("bold");
|
|
6107
6590
|
const isItalic = formats.italic && node.hasFormat("italic");
|
|
6108
6591
|
const isUnderline = node.hasFormat("underline");
|
|
@@ -6121,7 +6604,8 @@ const CommonPlugin = class extends KernelPlugin {
|
|
|
6121
6604
|
if (res) tailSpace = res[0];
|
|
6122
6605
|
const append = textContent.trimEnd();
|
|
6123
6606
|
const lastChar = append.at(-1);
|
|
6124
|
-
|
|
6607
|
+
const markdownText = isInTableCell ? append.replaceAll("\\", "\\\\").replaceAll("|", "\\|") : append;
|
|
6608
|
+
ctx.appendLine(markdownText);
|
|
6125
6609
|
if (isSubscript) ctx.appendLine("~");
|
|
6126
6610
|
if (isSuperscript) ctx.appendLine("^");
|
|
6127
6611
|
if (isUnderline) ctx.appendLine("</ins>");
|
|
@@ -6137,6 +6621,16 @@ const CommonPlugin = class extends KernelPlugin {
|
|
|
6137
6621
|
registerMDReader(markdownService);
|
|
6138
6622
|
}
|
|
6139
6623
|
onInit(editor) {
|
|
6624
|
+
this.register(this.identityService.bindEditor(editor));
|
|
6625
|
+
this.register(editor.registerNodeTransform(RootNode, $normalizeNodeIds));
|
|
6626
|
+
this.register(editor.registerCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, ({ nodes }) => {
|
|
6627
|
+
const clearCopiedIds = (node) => {
|
|
6628
|
+
$clearNodeId(node);
|
|
6629
|
+
if ($isElementNode(node)) node.getChildren().forEach(clearCopiedIds);
|
|
6630
|
+
};
|
|
6631
|
+
nodes.forEach(clearCopiedIds);
|
|
6632
|
+
return false;
|
|
6633
|
+
}, COMMAND_PRIORITY_CRITICAL));
|
|
6140
6634
|
this.register(this.kernel.registerHighCommand(PASTE_COMMAND, (event) => {
|
|
6141
6635
|
if (!(event instanceof ClipboardEvent)) return false;
|
|
6142
6636
|
const clipboardData = event.clipboardData;
|
|
@@ -9971,7 +10465,11 @@ const ListPlugin = class extends KernelPlugin {
|
|
|
9971
10465
|
return false;
|
|
9972
10466
|
});
|
|
9973
10467
|
litexmlService.registerXMLWriter(ListItemNode.getType(), (node, ctx) => {
|
|
9974
|
-
if ($isListItemNode(node))
|
|
10468
|
+
if ($isListItemNode(node)) {
|
|
10469
|
+
const child = node.getFirstChild();
|
|
10470
|
+
if (child?.getType() === "diff" && "diffType" in child && child.diffType === "listItemRemove") return { lines: [] };
|
|
10471
|
+
return ctx.createXmlNode("li");
|
|
10472
|
+
}
|
|
9975
10473
|
return false;
|
|
9976
10474
|
});
|
|
9977
10475
|
litexmlService.registerXMLReader("ol", (xmlNode, children) => {
|
|
@@ -10045,6 +10543,8 @@ const ListPlugin = class extends KernelPlugin {
|
|
|
10045
10543
|
markdownService.registerMarkdownWriter(ListItemNode.getType(), (ctx, node) => {
|
|
10046
10544
|
const parent = node.getParent();
|
|
10047
10545
|
if ($isListItemNode(node) && $isListNode(parent)) {
|
|
10546
|
+
const child = node.getFirstChild();
|
|
10547
|
+
if (child?.getType() === "diff" && "diffType" in child && child.diffType === "listItemRemove") return true;
|
|
10048
10548
|
if ($isListNode(node.getFirstChild())) return;
|
|
10049
10549
|
const level = getLevel(parent);
|
|
10050
10550
|
const prefix = " ".repeat(level);
|
|
@@ -10347,14 +10847,14 @@ const MathPlugin = class extends KernelPlugin {
|
|
|
10347
10847
|
if (!litexmlService) return;
|
|
10348
10848
|
litexmlService.registerXMLWriter(MathInlineNode.getType(), (node, ctx) => {
|
|
10349
10849
|
if (node instanceof MathInlineNode) {
|
|
10350
|
-
const attributes = { code: node.
|
|
10850
|
+
const attributes = { code: node.code };
|
|
10351
10851
|
return ctx.createXmlNode("math", attributes);
|
|
10352
10852
|
}
|
|
10353
10853
|
return false;
|
|
10354
10854
|
});
|
|
10355
10855
|
litexmlService.registerXMLWriter(MathBlockNode.getType(), (node, ctx) => {
|
|
10356
10856
|
if (node instanceof MathBlockNode) {
|
|
10357
|
-
const attributes = { code: node.
|
|
10857
|
+
const attributes = { code: node.code };
|
|
10358
10858
|
return ctx.createXmlNode("mathBlock", attributes);
|
|
10359
10859
|
}
|
|
10360
10860
|
return false;
|
|
@@ -11822,24 +12322,10 @@ const extractMediaFromEditorState = (state) => {
|
|
|
11822
12322
|
};
|
|
11823
12323
|
//#endregion
|
|
11824
12324
|
//#region src/headless/index.ts
|
|
11825
|
-
const
|
|
11826
|
-
if (typeof id !== "number" && typeof id !== "string") return null;
|
|
11827
|
-
const numericId = Number(id);
|
|
11828
|
-
return Number.isInteger(numericId) && numericId >= 0 ? numericId : null;
|
|
11829
|
-
};
|
|
11830
|
-
const findMaxSerializedId = (node) => {
|
|
11831
|
-
if (!node || typeof node !== "object") return -1;
|
|
11832
|
-
const record = node;
|
|
11833
|
-
const ownMax = getNumericId(record.id) ?? -1;
|
|
11834
|
-
if (!Array.isArray(record.children)) return ownMax;
|
|
11835
|
-
return record.children.reduce((maxId, child) => Math.max(maxId, findMaxSerializedId(child)), ownMax);
|
|
11836
|
-
};
|
|
11837
|
-
const createSerializedId = (context) => String(context.nextId++);
|
|
11838
|
-
const createCodeChildrenFromLegacyCode = (code, context) => code.split("\n").flatMap((text, index, array) => {
|
|
12325
|
+
const createCodeChildrenFromLegacyCode = (code) => code.split("\n").flatMap((text, index, array) => {
|
|
11839
12326
|
const textNode = {
|
|
11840
12327
|
detail: 0,
|
|
11841
12328
|
format: 0,
|
|
11842
|
-
id: createSerializedId(context),
|
|
11843
12329
|
mode: "normal",
|
|
11844
12330
|
style: "",
|
|
11845
12331
|
text,
|
|
@@ -11848,18 +12334,17 @@ const createCodeChildrenFromLegacyCode = (code, context) => code.split("\n").fla
|
|
|
11848
12334
|
};
|
|
11849
12335
|
if (index === array.length - 1) return textNode;
|
|
11850
12336
|
return [textNode, {
|
|
11851
|
-
id: createSerializedId(context),
|
|
11852
12337
|
type: "linebreak",
|
|
11853
12338
|
version: 1
|
|
11854
12339
|
}];
|
|
11855
12340
|
});
|
|
11856
|
-
const normalizeLegacyEditorDataNode = (node
|
|
12341
|
+
const normalizeLegacyEditorDataNode = (node) => {
|
|
11857
12342
|
if (!node || typeof node !== "object") return node;
|
|
11858
12343
|
const record = node;
|
|
11859
|
-
const children = Array.isArray(record.children) ? record.children.map((child) => normalizeLegacyEditorDataNode(child
|
|
12344
|
+
const children = Array.isArray(record.children) ? record.children.map((child) => normalizeLegacyEditorDataNode(child)) : record.children;
|
|
11860
12345
|
if (record.type === "code" && typeof record.code === "string" && !Array.isArray(children)) return {
|
|
11861
12346
|
...record,
|
|
11862
|
-
children: createCodeChildrenFromLegacyCode(record.code
|
|
12347
|
+
children: createCodeChildrenFromLegacyCode(record.code),
|
|
11863
12348
|
direction: record.direction ?? "ltr",
|
|
11864
12349
|
format: record.format ?? "",
|
|
11865
12350
|
indent: record.indent ?? 0,
|
|
@@ -11874,10 +12359,9 @@ const normalizeLegacyEditorDataNode = (node, context) => {
|
|
|
11874
12359
|
};
|
|
11875
12360
|
const normalizeLegacyEditorData = (editorData) => {
|
|
11876
12361
|
const data = typeof editorData === "string" ? JSON.parse(editorData) : editorData;
|
|
11877
|
-
const context = { nextId: findMaxSerializedId(data.root) + 1 };
|
|
11878
12362
|
return {
|
|
11879
12363
|
...data,
|
|
11880
|
-
root: normalizeLegacyEditorDataNode(data.root
|
|
12364
|
+
root: normalizeLegacyEditorDataNode(data.root)
|
|
11881
12365
|
};
|
|
11882
12366
|
};
|
|
11883
12367
|
const extractSerializedCodeText = (children) => children.map((child) => {
|
|
@@ -11960,6 +12444,17 @@ var HeadlessEditor = class {
|
|
|
11960
12444
|
await moment();
|
|
11961
12445
|
return this;
|
|
11962
12446
|
}
|
|
12447
|
+
async applyLiteXMLBatchWithResults(operations) {
|
|
12448
|
+
let results;
|
|
12449
|
+
if (!this.kernel.dispatchCommand(LITEXML_MODIFY_WITH_RESULTS_COMMAND, {
|
|
12450
|
+
onResults: (operationResults) => {
|
|
12451
|
+
results = operationResults;
|
|
12452
|
+
},
|
|
12453
|
+
operations
|
|
12454
|
+
}) || !results) throw new Error("LiteXML batch result command is not registered.");
|
|
12455
|
+
await moment();
|
|
12456
|
+
return results;
|
|
12457
|
+
}
|
|
11963
12458
|
export(options = {}) {
|
|
11964
12459
|
const snapshot = {
|
|
11965
12460
|
editorData: preserveSerializedCodeTextInEditorData(this.kernel.getDocument("json")),
|
|
@@ -12002,4 +12497,4 @@ function createHeadlessEditor(options) {
|
|
|
12002
12497
|
return new HeadlessEditor(options);
|
|
12003
12498
|
}
|
|
12004
12499
|
//#endregion
|
|
12005
|
-
export { DEFAULT_HEADLESS_EDITOR_PLUGINS, HeadlessEditor, createHeadlessEditor, extractMediaFromEditorState };
|
|
12500
|
+
export { $getNodeById, $getNodeId, DEFAULT_HEADLESS_EDITOR_PLUGINS, HeadlessEditor, INodeIdentityService, createHeadlessEditor, extractMediaFromEditorState };
|