@lobehub/editor
Version:
A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.
454 lines (450 loc) • 18.6 kB
JavaScript
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
/* eslint-disable @typescript-eslint/no-use-before-define */
import { mergeRegister } from '@lexical/utils';
import { $createParagraphNode, $getNodeByKey, $insertNodes, $isElementNode, COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical';
import { $closest } from "../../../editor-kernel";
import { createDebugLogger } from "../../../utils/debug";
import { $createDiffNode, DiffNode } from "../node/DiffNode";
import { $cloneNode, $parseSerializedNodeImpl, charToId } from "../utils";
var logger = createDebugLogger('plugin', 'litexml');
// Helpers to reduce duplication and improve readability
function toArrayXml(litexml) {
return Array.isArray(litexml) ? litexml : [litexml];
}
function tryParseChild(child, editor) {
try {
var oldNode = $getNodeByKey(child.id);
var newNode = $parseSerializedNodeImpl(child, editor);
return {
newNode: newNode,
oldNode: oldNode
};
} catch (error) {
logger.error('❌ Error parsing child node:', error);
return {
newNode: null,
oldNode: null
};
}
}
function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor) {
var oldBlock = $closest(oldNode, function (node) {
return node.isInline() === false;
});
if (!oldBlock) {
throw new Error('Old block node not found for diffing.');
}
var originDiffNode = $closest(oldNode, function (node) {
return node.getType() === DiffNode.getType();
});
if (originDiffNode) {
oldNode.replace(newNode, false);
return;
}
if (oldNode === oldBlock) {
var diffNode = $createDiffNode('modify');
diffNode.append($cloneNode(oldBlock, editor), newNode);
oldNode.replace(diffNode, false);
} else {
if (!modifyBlockNodes.has(oldBlock.getKey())) {
modifyBlockNodes.add(oldBlock.getKey());
var _diffNode = $createDiffNode('modify');
_diffNode.append($cloneNode(oldBlock, editor));
diffNodeMap.set(oldBlock.getKey(), _diffNode);
}
oldNode.replace(newNode, false);
}
}
function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
var _iterator = _createForOfIteratorHelper(modifyBlockNodes),
_step;
try {
var _loop = function _loop() {
var blockNodeKey = _step.value;
var blockNode = $getNodeByKey(blockNodeKey);
var diffNode = diffNodeMap.get(blockNodeKey);
if (diffNode && blockNode) {
// 如果是列表项,可能需要特殊处理
if (blockNode.getType() === 'listitem' && $isElementNode(blockNode)) {
var newDiffNode = $createDiffNode('listItemModify');
var firstChild = diffNode.getFirstChild();
if (firstChild && $isElementNode(firstChild)) {
newDiffNode.append(firstChild);
}
var children = blockNode.getChildren();
var p = $createParagraphNode();
children.forEach(function (child) {
child.remove();
p.append(child);
});
newDiffNode.append(p);
blockNode.append(newDiffNode);
return 1; // continue
} else {
diffNode.append($cloneNode(blockNode, editor));
blockNode.replace(diffNode, false);
}
}
};
for (_iterator.s(); !(_step = _iterator.n()).done;) {
if (_loop()) continue;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
/**
* Wrap a block-level change with a `modify` diff: clone the old block, run the
* provided changeFn (which should mutate nodes inside the block), then clone
* the new block and replace it with the diff node. Useful for inline->block
* transitions where we want to show a modify diff.
*/
function wrapBlockModify(oldBlock, editor, changeFn) {
var diffNode = $createDiffNode('modify');
diffNode.append($cloneNode(oldBlock, editor));
changeFn();
var newBlock = $getNodeByKey(oldBlock.getKey());
if (!newBlock) {
throw new Error('New block node not found for modify wrapper.');
}
diffNode.append($cloneNode(newBlock, editor));
newBlock.replace(diffNode, false);
}
export var LITEXML_MODIFY_COMMAND = createCommand('LITEXML_MODIFY_COMMAND');
export var LITEXML_APPLY_COMMAND = createCommand('LITEXML_APPLY_COMMAND');
export var LITEXML_REMOVE_COMMAND = createCommand('LITEXML_REMOVE_COMMAND');
export var LITEXML_INSERT_COMMAND = createCommand('LITEXML_INSERT_COMMAND');
export function registerLiteXMLCommand(editor, dataSource) {
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, function (payload) {
var resultPayload = payload.reduce(function (acc, cur) {
if (cur.action === 'insert') {
acc.unshift(cur);
} else {
acc.push(cur);
}
return acc;
}, []);
try {
resultPayload.forEach(function (item) {
var action = item.action;
switch (action) {
case 'modify':
{
var litexml = item.litexml;
var arrayXml = toArrayXml(litexml);
// handle modfy action
handleModify(editor, dataSource, arrayXml, true);
break;
}
case 'remove':
{
var id = item.id;
var key = charToId(id);
// handle remove action
handleRemove(editor, key, true);
break;
}
case 'insert':
{
handleInsert(editor, _objectSpread(_objectSpread({}, item), {}, {
delay: true
}), dataSource);
break;
}
default:
{
logger.warn("\u26A0\uFE0F Unknown action type: ".concat(action));
}
}
});
return false;
} catch (error) {
logger.error('❌ Error processing LITEXML_MODIFY_COMMAND:', error);
return false;
}
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_APPLY_COMMAND, function (payload) {
var litexml = payload.litexml,
delay = payload.delay;
var arrayXml = toArrayXml(litexml);
handleModify(editor, dataSource, arrayXml, delay);
return false;
}, COMMAND_PRIORITY_EDITOR // Priority
), editor.registerCommand(LITEXML_REMOVE_COMMAND, function (payload) {
var id = payload.id,
delay = payload.delay;
var key = charToId(id);
handleRemove(editor, key, delay);
return false;
}, COMMAND_PRIORITY_EDITOR // Priority
), editor.registerCommand(LITEXML_INSERT_COMMAND, function (payload) {
handleInsert(editor, payload, dataSource);
return false;
}, COMMAND_PRIORITY_EDITOR // Priority
));
}
function handleModify(editor, dataSource, arrayXml, delay) {
if (delay) {
editor.update(function () {
var modifyBlockNodes = new Set();
var diffNodeMap = new Map();
arrayXml.forEach(function (xml) {
var inode = dataSource.readLiteXMLToInode(xml);
inode.root.children.forEach(function (child) {
try {
var _tryParseChild = tryParseChild(child, editor),
oldNode = _tryParseChild.oldNode,
newNode = _tryParseChild.newNode;
if (oldNode && newNode) {
handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor);
} else {
logger.warn("\u26A0\uFE0F Node with key ".concat(child.id, " not found for diffing."));
}
} catch (error) {
logger.error('❌ Error replacing node:', error);
}
});
});
// replace modified block nodes with diff nodes
finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor);
});
} else {
editor.update(function () {
arrayXml.forEach(function (xml) {
var inode = dataSource.readLiteXMLToInode(xml);
var prevNode = null;
inode.root.children.forEach(function (child) {
try {
var _tryParseChild2 = tryParseChild(child, editor),
oldNode = _tryParseChild2.oldNode,
newNode = _tryParseChild2.newNode;
if (oldNode && newNode) {
prevNode = oldNode.replace(newNode, false);
} else if (newNode) {
if (prevNode) {
if (!newNode.isInline()) {
var prevBlock = $closest(prevNode, function (node) {
return node.isInline() === false;
});
if (prevBlock) {
prevNode = prevBlock.insertAfter(newNode);
} else {
$insertNodes([newNode]);
prevNode = newNode;
}
} else {
prevNode = prevNode.insertAfter(newNode);
}
} else {
$insertNodes([newNode]);
}
}
} catch (error) {
logger.error('❌ Error replacing node:', error);
}
});
});
});
}
}
function handleRemove(editor, key, delay) {
editor.update(function () {
var node = $getNodeByKey(key);
if (!node) return;
if (!delay) {
node.remove();
return;
}
// delay removal: show a diff
if (node.isInline() === false) {
var originDiffNode = $closest(node, function (node) {
return node.getType() === DiffNode.getType();
});
if (originDiffNode) {
switch (originDiffNode.diffType) {
case 'add':
{
originDiffNode.remove();
return;
}
case 'modify':
{
var children = originDiffNode.getChildren();
var newDiff = $createDiffNode('remove');
newDiff.append(children[0]);
originDiffNode.replace(newDiff, false);
return;
}
case 'listItemModify':
{
var _children = originDiffNode.getChildren();
originDiffNode.replace(_children[0], false).selectEnd();
return;
}
case 'remove':
case 'unchanged':
{
// do nothing special
break;
}
}
return;
}
var diffNode = $createDiffNode('remove');
diffNode.append($cloneNode(node, editor));
node.replace(diffNode, false);
} else {
var oldBlock = $closest(node, function (node) {
return node.isInline() === false;
});
if (!oldBlock) {
throw new Error('Old block node not found for removal.');
}
var _originDiffNode = $closest(node, function (node) {
return node.getType() === DiffNode.getType();
});
if (_originDiffNode) {
node.remove();
return;
}
// wrap changes inside a modify diff
wrapBlockModify(oldBlock, editor, function () {
node.remove();
});
}
});
}
function handleInsert(editor, payload, dataSource) {
var litexml = payload.litexml,
delay = payload.delay;
var isBefore = ('beforeId' in payload);
var inode = dataSource.readLiteXMLToInode(litexml);
editor.update(function () {
try {
var referenceNode = null;
if (isBefore) {
referenceNode = $getNodeByKey(charToId(payload.beforeId));
} else {
referenceNode = $getNodeByKey(charToId(payload.afterId));
}
if (!referenceNode) {
throw new Error('Reference node not found for insertion.');
}
var newNodes = inode.root.children.map(function (child) {
return $parseSerializedNodeImpl(child, editor);
});
if (!delay) {
if (isBefore) {
referenceNode = referenceNode.insertBefore(newNodes);
} else {
newNodes.forEach(function (node) {
if (referenceNode) {
referenceNode = referenceNode.insertAfter(node);
}
});
}
return;
}
// delay insertion: show diffs or wrap block modifications
if (isBefore) {
if (referenceNode.isInline() === false) {
var originDiffNode = $closest(referenceNode, function (node) {
return node.getType() === DiffNode.getType();
});
if (originDiffNode) {
referenceNode = originDiffNode;
}
var diffNodes = newNodes.map(function (node) {
var diffNode = $createDiffNode('add');
diffNode.append(node);
return diffNode;
});
diffNodes.forEach(function (diffNode) {
if (referenceNode) {
referenceNode = referenceNode.insertBefore(diffNode);
}
});
} else {
var refBlock = $closest(referenceNode, function (node) {
return node.isInline() === false;
});
if (!refBlock) {
throw new Error('Reference block node not found for insertion.');
}
var _originDiffNode2 = $closest(referenceNode, function (node) {
return node.getType() === DiffNode.getType();
});
if (_originDiffNode2) {
// 可能是 modify / add,那么直接修改就好了
newNodes.forEach(function (node) {
if (referenceNode) {
referenceNode = referenceNode.insertBefore(node);
}
});
} else {
wrapBlockModify(refBlock, editor, function () {
newNodes.forEach(function (node) {
if (referenceNode) {
referenceNode = referenceNode.insertBefore(node);
}
});
});
}
}
} else {
if (referenceNode.isInline() === false) {
var _originDiffNode3 = $closest(referenceNode, function (node) {
return node.getType() === DiffNode.getType();
});
if (_originDiffNode3) {
referenceNode = _originDiffNode3;
}
newNodes.forEach(function (node) {
if (referenceNode) {
var diffNode = $createDiffNode('add');
diffNode.append(node);
referenceNode = referenceNode.insertAfter(diffNode);
}
});
} else {
var _refBlock = $closest(referenceNode, function (node) {
return node.isInline() === false;
});
if (!_refBlock) {
throw new Error('Reference block node not found for insertion.');
}
var _originDiffNode4 = $closest(referenceNode, function (node) {
return node.getType() === DiffNode.getType();
});
if (_originDiffNode4) {
// 可能是 modify / add,那么直接修改就好了
newNodes.forEach(function (node) {
if (referenceNode) {
referenceNode = referenceNode.insertAfter(node);
}
});
} else {
wrapBlockModify(_refBlock, editor, function () {
newNodes.forEach(function (node) {
if (referenceNode) {
referenceNode = referenceNode.insertAfter(node);
}
});
});
}
}
}
} catch (error) {
logger.error('❌ Error inserting node:', error);
}
});
}