@lobehub/editor 4.8.5 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/{ReactSlashPlugin-BZvB0g1k.js → ReactSlashPlugin-Dn5VWUzS.js} +352 -339
- package/es/headless.d.ts +531 -0
- package/es/headless.js +26178 -0
- package/es/{index-BfKEHYDe.d.ts → index-BXj6uAM3.d.ts} +4 -0
- package/es/index.d.ts +250 -185
- package/es/index.js +2077 -1849
- package/es/react.d.ts +1 -1
- package/es/react.js +5 -5
- package/es/renderer.js +6 -6
- package/es/{style-B7Vfm0cE.js → style-CcDKrOoJ.js} +143 -143
- package/es/{style-Tlcvrhqp.js → style-DAKdXzC4.js} +42 -1
- package/package.json +5 -1
package/es/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { $ as DOM_TEXT_TYPE, A as $createCodeMirrorNode, At as createDebugLogger, C as $createCodeNode$1, Ct as CONTROL_OR_META, Dt as KeyEnum, E as CodeNode$1, Et as HotkeyScopeEnum, F as $isCursorNode, Ft as init_debug, G as kernel_exports, Gt as INodeHelper, H as DataSource, I as CardLikeElementNode, It as prodSafeLogger, J as $getNearestNodeFromDOMNode, K as $closest, Kt as init_helper, M as CodeMirrorNode, Mt as debugLoggers, N as $createCursorNode, Nt as debug_exports, Ot as init_hotkey, P as $isCardLikeElementNode, Pt as devConsole, Q as DOM_ELEMENT_TYPE, R as cursorNodeSerialized, S as formatUrl, St as init_registerHotkey, Tt as HotkeyEnum, U as Kernel, V as resetRandomKey, W as init_kernel, X as DOM_DOCUMENT_FRAGMENT_TYPE, Y as $getNodeFromDOMNode, Yt as __toCommonJS, Z as DOM_DOCUMENT_TYPE, _ as AutoLinkNode, _t as unregisterEditorKernel, a as $createMathBlockNode, at as generateEditorId, b as LinkNode, bt as HOVER_COMMAND, c as MathBlockNode, ct as getNodeKeyFromDOMNode, dt as isDOMNode, et as EDITOR_THEME_KEY, f as LinkHighlightNode, ft as isDocumentFragment, g as $toggleLink, gt as registerEditorKernel, h as $isLinkNode, ht as reconcileDecorator, it as genServiceId, j as $isCodeMirrorNode, jt as debugLogger, kt as browserDebug, l as MathInlineNode, lt as getParentElement, m as $createLinkNode, mt as noop, nt as compareNodeOrder, o as $createMathInlineNode, ot as getKernelFromEditor, pt as moment, q as $closestNodeType, rt as createEmptyEditorState, s as $isMathNode, st as getKernelFromEditorConfig, tt as assert, u as $createLinkHighlightNode, ut as init_utils, v as HOVER_LINK_COMMAND, vt as KernelPlugin, wt as init_sys, x as TOGGLE_LINK_COMMAND, xt as getHotkeyById, y as HOVER_OUT_LINK_COMMAND, yt as init_plugin } from "./style-
|
|
2
|
-
import { A as
|
|
3
|
-
import { S as
|
|
1
|
+
import { $ as DOM_TEXT_TYPE, A as $createCodeMirrorNode, At as createDebugLogger, B as Editor, C as $createCodeNode$1, Ct as CONTROL_OR_META, Dt as KeyEnum, E as CodeNode$1, Et as HotkeyScopeEnum, F as $isCursorNode, Ft as init_debug, G as kernel_exports, Gt as INodeHelper, H as DataSource, I as CardLikeElementNode, It as prodSafeLogger, J as $getNearestNodeFromDOMNode, K as $closest, Kt as init_helper, M as CodeMirrorNode, Mt as debugLoggers, N as $createCursorNode, Nt as debug_exports, Ot as init_hotkey, P as $isCardLikeElementNode, Pt as devConsole, Q as DOM_ELEMENT_TYPE, R as cursorNodeSerialized, S as formatUrl, St as init_registerHotkey, Tt as HotkeyEnum, U as Kernel, V as resetRandomKey, W as init_kernel, X as DOM_DOCUMENT_FRAGMENT_TYPE, Y as $getNodeFromDOMNode, Yt as __toCommonJS, Z as DOM_DOCUMENT_TYPE, _ as AutoLinkNode, _t as unregisterEditorKernel, a as $createMathBlockNode, at as generateEditorId, b as LinkNode, bt as HOVER_COMMAND, c as MathBlockNode, ct as getNodeKeyFromDOMNode, dt as isDOMNode, et as EDITOR_THEME_KEY, f as LinkHighlightNode, ft as isDocumentFragment, g as $toggleLink, gt as registerEditorKernel, h as $isLinkNode, ht as reconcileDecorator, it as genServiceId, j as $isCodeMirrorNode, jt as debugLogger, kt as browserDebug, l as MathInlineNode, lt as getParentElement, m as $createLinkNode, mt as noop, nt as compareNodeOrder, o as $createMathInlineNode, ot as getKernelFromEditor, pt as moment, q as $closestNodeType, rt as createEmptyEditorState, s as $isMathNode, st as getKernelFromEditorConfig, tt as assert, u as $createLinkHighlightNode, ut as init_utils, v as HOVER_LINK_COMMAND, vt as KernelPlugin, wt as init_sys, x as TOGGLE_LINK_COMMAND, xt as getHotkeyById, y as HOVER_OUT_LINK_COMMAND, yt as init_plugin } from "./style-DAKdXzC4.js";
|
|
2
|
+
import { A as detectLanguage, B as INodePlugin, C as ReactPlainText, D as useTranslation, E as ReactMarkdownPlugin, F as GET_MARKDOWN_SELECTION_COMMAND, G as idToChar, H as $cloneNode, I as INSERT_MARKDOWN_COMMAND, J as useLexicalEditor, K as INSERT_HEADING_COMMAND, L as isPunctuationChar, M as MARKDOWN_READER_LEVEL_HIGH, N as MARKDOWN_READER_LEVEL_NORMAL, O as MarkdownPlugin, P as MARKDOWN_WRITER_LEVEL_MAX, R as ILitexmlService, S as registerCodeInlineCommand, T as CommonPlugin, U as $parseSerializedNodeImpl, V as INodeService, W as charToId, X as useLexicalComposerContext, Y as ReactEditor, Z as LexicalErrorBoundary, _ as UPDATE_CODEBLOCK_LANG, a as ReactMentionPlugin, b as registerCheckList, c as INSERT_LINK_HIGHLIGHT_COMMAND, d as getSelectedNode, f as sanitizeUrl, g as CodeblockPlugin, h as bundledLanguagesInfo, i as SlashPlugin, j as IMarkdownShortCutService, k as detectCodeLanguage, l as registerLinkHighlightCommand, m as useLexicalNodeSelection, n as ReactSlashOption, o as MentionPlugin, q as INSERT_QUOTE_COMMAND, r as SlashMenu, s as INSERT_MENTION_COMMAND, t as ReactSlashPlugin, u as extractUrlFromText, v as getCodeLanguageByInput, w as ReactEditorContent, x as INSERT_CODEINLINE_COMMAND, y as INSERT_CHECK_LIST_COMMAND, z as LitexmlService } from "./ReactSlashPlugin-Dn5VWUzS.js";
|
|
3
|
+
import { S as DiffNode, _ as PlaceholderNode, a as styles$16, b as HorizontalRuleNode, c as ImageNode, d as BlockImageNode, f as styles$15, g as PlaceholderBlockNode, h as FileNode, i as imageBroken, l as $createBlockImageNode, m as $isFileNode, n as styles$18, o as $createImageNode, p as $createFileNode, r as styles$17, s as $isImageNode, t as styles$19, u as $isBlockImageNode, v as $createHorizontalRuleNode, x as $createDiffNode, y as $isHorizontalRuleNode } from "./style-CcDKrOoJ.js";
|
|
4
4
|
import { $computeTableMapSkipCellCheck, $createTableNodeWithDimensions, $createTableSelection, $deleteTableColumnAtSelection, $deleteTableRowAtSelection, $findTableNode, $getElementForTableNode, $getNodeTriplet, $getTableAndElementByKey, $getTableCellNodeFromLexicalNode, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, $mergeCells, $unmergeCell, TableCellHeaderStates, TableCellNode, TableNode, TableNode as TableNode$1, TableRowNode, getDOMCellFromTarget, getTableElement, getTableObserverFromTableElement, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive } from "@lexical/table";
|
|
5
5
|
import { debounce } from "es-toolkit/compat";
|
|
6
6
|
import EventEmitter from "eventemitter3";
|
|
7
|
-
import { $createNodeSelection, $createParagraphNode, $createRangeSelection, $createTextNode, $getNearestNodeFromDOMNode as $getNearestNodeFromDOMNode$1, $getNodeByKey, $getPreviousSelection, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, $isDecoratorNode, $isElementNode, $isNodeSelection, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isTextNode, $nodesOfType, $normalizeSelection__EXPERIMENTAL, $setSelection, CLICK_COMMAND, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, DROP_COMMAND, HISTORY_MERGE_TAG, INDENT_CONTENT_COMMAND, INSERT_TAB_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, ParagraphNode, SELECTION_CHANGE_COMMAND, SKIP_SCROLL_INTO_VIEW_TAG, TextNode, createCommand, getDOMSelection, getDOMSelectionFromTarget, isHTMLElement, isModifierMatch } from "lexical";
|
|
7
|
+
import { $createNodeSelection, $createParagraphNode, $createRangeSelection, $createTextNode, $getNearestNodeFromDOMNode as $getNearestNodeFromDOMNode$1, $getNodeByKey, $getPreviousSelection, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, $isDecoratorNode, $isElementNode, $isNodeSelection, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isTextNode, $nodesOfType, $normalizeSelection__EXPERIMENTAL, $setSelection, CLICK_COMMAND, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, DROP_COMMAND, HISTORY_MERGE_TAG, INDENT_CONTENT_COMMAND, INSERT_TAB_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, ParagraphNode, SELECTION_CHANGE_COMMAND, SKIP_SCROLL_INTO_VIEW_TAG, TabNode, TextNode, createCommand, getDOMSelection, getDOMSelectionFromTarget, isHTMLElement, isModifierMatch } from "lexical";
|
|
8
8
|
import { DRAG_DROP_PASTE } from "@lexical/rich-text";
|
|
9
9
|
import { $filter, $findMatchingParent, $getNearestBlockElementAncestorOrThrow, $getNearestNodeOfType, $insertNodeToNearestRoot, $wrapNodeInElement, addClassNamesToElement, calculateZoomLevel, mergeRegister, removeClassNamesFromElement } from "@lexical/utils";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { Suspense, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
12
|
-
import { ActionIcon, ActionIconGroup, Block, Button, Center, Dropdown, Flexbox, Hotkey, Icon, Input, InputNumber, LOBE_THEME_APP_ID, MaterialFileTypeIcon, Popover, Select, Text, TextArea } from "@lobehub/ui";
|
|
13
|
-
import katex, { renderToString } from "katex";
|
|
14
|
-
import "@lexical/code-core";
|
|
15
|
-
import { createStaticStyles, cssVar, cx, useThemeMode } from "antd-style";
|
|
16
12
|
import { $createListItemNode, $createListNode, $isListItemNode, $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND as INSERT_ORDERED_LIST_COMMAND$1, INSERT_UNORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND as INSERT_UNORDERED_LIST_COMMAND$1, ListItemNode, ListNode, REMOVE_LIST_COMMAND, UPDATE_LIST_START_COMMAND, registerList, registerListStrictIndentTransform } from "@lexical/list";
|
|
17
13
|
import { DOMParser } from "@xmldom/xmldom";
|
|
14
|
+
import katex, { renderToString } from "katex";
|
|
15
|
+
import { $isCodeHighlightNode, $isCodeNode, CodeHighlightNode, CodeNode } from "@lexical/code-core";
|
|
16
|
+
import { ActionIcon, ActionIconGroup, Block, Button, Center, Dropdown, Flexbox, Hotkey, Icon, Input, InputNumber, LOBE_THEME_APP_ID, MaterialFileTypeIcon, Popover, Select, Text, TextArea } from "@lobehub/ui";
|
|
17
|
+
import { createStaticStyles, cssVar, cx, useThemeMode } from "antd-style";
|
|
18
18
|
import { BaselineIcon, Check, ChevronDown, ChevronRight, CopyIcon, EditIcon, ExternalLinkIcon, Grid2X2XIcon, LinkIcon, LoaderCircleIcon, MoreHorizontalIcon, PanelBottomCloseIcon, PanelLeftCloseIcon, PanelRightCloseIcon, PanelTopCloseIcon, PlusIcon, TableColumnsSplitIcon, TableRowsSplitIcon, UnlinkIcon, UploadIcon, X } from "lucide-react";
|
|
19
19
|
import { createPortal } from "react-dom";
|
|
20
20
|
import { Switch, message } from "antd";
|
|
@@ -41,1121 +41,813 @@ const LexicalPortalContainer = forwardRef(({ editor, node, children }, ref) => {
|
|
|
41
41
|
});
|
|
42
42
|
LexicalPortalContainer.displayName = "LexicalPortalContainer";
|
|
43
43
|
//#endregion
|
|
44
|
-
//#region src/plugins/
|
|
45
|
-
|
|
46
|
-
const [editor] = useLexicalComposerContext();
|
|
47
|
-
useLayoutEffect(() => {
|
|
48
|
-
editor.registerPlugin(INodePlugin);
|
|
49
|
-
}, [editor]);
|
|
50
|
-
return null;
|
|
51
|
-
};
|
|
52
|
-
ReactNodePlugin.displayName = "ReactNodePlugin";
|
|
53
|
-
//#endregion
|
|
54
|
-
//#region src/utils/url.ts
|
|
55
|
-
init_debug();
|
|
44
|
+
//#region src/plugins/litexml/command/index.ts
|
|
45
|
+
init_helper();
|
|
56
46
|
init_hotkey();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Validates if a string is a valid URL
|
|
64
|
-
* @param url - The URL to validate
|
|
65
|
-
* @returns true if the URL is valid, false otherwise
|
|
66
|
-
*/
|
|
67
|
-
function isValidUrl(url) {
|
|
68
|
-
if (!url || typeof url !== "string") return false;
|
|
69
|
-
const trimmed = url.trim();
|
|
70
|
-
if (URL_REGEX.test(trimmed)) return true;
|
|
71
|
-
try {
|
|
72
|
-
const urlObj = new URL(trimmed);
|
|
73
|
-
return !!urlObj.protocol && !!urlObj.hostname;
|
|
74
|
-
} catch {}
|
|
75
|
-
if (/^[\da-z][\w#%+./:=?@~-]*\.[\w#%+/:=?@~-]+$/i.test(trimmed)) return true;
|
|
76
|
-
return false;
|
|
47
|
+
init_plugin();
|
|
48
|
+
init_debug();
|
|
49
|
+
const logger$6 = createDebugLogger("plugin", "litexml");
|
|
50
|
+
function toArrayXml(litexml) {
|
|
51
|
+
return Array.isArray(litexml) ? litexml : [litexml];
|
|
77
52
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
53
|
+
function tryParseChild(child, editor) {
|
|
54
|
+
try {
|
|
55
|
+
const oldNode = $getNodeByKey(child.id);
|
|
56
|
+
return {
|
|
57
|
+
newNode: $parseSerializedNodeImpl(child, editor),
|
|
58
|
+
oldNode
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logger$6.error("❌ Error parsing child node:", error);
|
|
62
|
+
return {
|
|
63
|
+
newNode: null,
|
|
64
|
+
oldNode: null
|
|
65
|
+
};
|
|
66
|
+
}
|
|
88
67
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.pluginName = "AutoCompletePlugin";
|
|
68
|
+
function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor) {
|
|
69
|
+
const oldBlock = $closest(oldNode, (node) => node.isInline() === false);
|
|
70
|
+
if (!oldBlock) throw new Error("Old block node not found for diffing.");
|
|
71
|
+
if ($closest(oldNode, (node) => node.getType() === DiffNode.getType())) {
|
|
72
|
+
oldNode.replace(newNode, false);
|
|
73
|
+
return;
|
|
96
74
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
kernel.registerNodes([PlaceholderNode, PlaceholderBlockNode]);
|
|
110
|
-
if (config?.theme) kernel.registerThemes(config?.theme);
|
|
75
|
+
if (oldNode === oldBlock) {
|
|
76
|
+
const diffNode = $createDiffNode("modify");
|
|
77
|
+
diffNode.append($cloneNode(oldBlock, editor), newNode);
|
|
78
|
+
oldNode.replace(diffNode, false);
|
|
79
|
+
} else {
|
|
80
|
+
if (!modifyBlockNodes.has(oldBlock.getKey())) {
|
|
81
|
+
modifyBlockNodes.add(oldBlock.getKey());
|
|
82
|
+
const diffNode = $createDiffNode("modify");
|
|
83
|
+
diffNode.append($cloneNode(oldBlock, editor));
|
|
84
|
+
diffNodeMap.set(oldBlock.getKey(), diffNode);
|
|
85
|
+
}
|
|
86
|
+
oldNode.replace(newNode, false);
|
|
111
87
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
88
|
+
}
|
|
89
|
+
function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
|
|
90
|
+
for (const blockNodeKey of modifyBlockNodes) {
|
|
91
|
+
const blockNode = $getNodeByKey(blockNodeKey);
|
|
92
|
+
const diffNode = diffNodeMap.get(blockNodeKey);
|
|
93
|
+
if (diffNode && blockNode) if (blockNode.getType() === "listitem" && $isElementNode(blockNode)) {
|
|
94
|
+
const newDiffNode = $createDiffNode("listItemModify");
|
|
95
|
+
const firstChild = diffNode.getFirstChild();
|
|
96
|
+
if (firstChild && $isElementNode(firstChild)) newDiffNode.append(firstChild);
|
|
97
|
+
const children = blockNode.getChildren();
|
|
98
|
+
const p = $createParagraphNode();
|
|
99
|
+
children.forEach((child) => {
|
|
100
|
+
child.remove();
|
|
101
|
+
p.append(child);
|
|
102
|
+
});
|
|
103
|
+
newDiffNode.append(p);
|
|
104
|
+
blockNode.append(newDiffNode);
|
|
105
|
+
continue;
|
|
106
|
+
} else {
|
|
107
|
+
diffNode.append($cloneNode(blockNode, editor));
|
|
108
|
+
blockNode.replace(diffNode, false);
|
|
117
109
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Wrap a block-level change with a `modify` diff: clone the old block, run the
|
|
114
|
+
* provided changeFn (which should mutate nodes inside the block), then clone
|
|
115
|
+
* the new block and replace it with the diff node. Useful for inline->block
|
|
116
|
+
* transitions where we want to show a modify diff.
|
|
117
|
+
*/
|
|
118
|
+
function wrapBlockModify(oldBlock, editor, changeFn) {
|
|
119
|
+
if ($isListItemNode(oldBlock)) {
|
|
120
|
+
const diffNode = $createDiffNode("listItemModify");
|
|
121
|
+
const p = $createParagraphNode();
|
|
122
|
+
oldBlock.getChildren().forEach((child) => {
|
|
123
|
+
p.append($cloneNode(child, editor));
|
|
124
|
+
});
|
|
125
|
+
changeFn();
|
|
126
|
+
diffNode.append(p);
|
|
127
|
+
const pNew = $createParagraphNode();
|
|
128
|
+
oldBlock.getChildren().forEach((child) => {
|
|
129
|
+
pNew.append(child);
|
|
130
|
+
});
|
|
131
|
+
diffNode.append(pNew);
|
|
132
|
+
oldBlock.append(diffNode);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const diffNode = $createDiffNode("modify");
|
|
136
|
+
diffNode.append($cloneNode(oldBlock, editor));
|
|
137
|
+
changeFn();
|
|
138
|
+
const newBlock = $getNodeByKey(oldBlock.getKey());
|
|
139
|
+
if (!newBlock) throw new Error("New block node not found for modify wrapper.");
|
|
140
|
+
diffNode.append($cloneNode(newBlock, editor));
|
|
141
|
+
newBlock.replace(diffNode, false);
|
|
142
|
+
}
|
|
143
|
+
const LITEXML_MODIFY_COMMAND = createCommand("LITEXML_MODIFY_COMMAND");
|
|
144
|
+
const LITEXML_APPLY_COMMAND = createCommand("LITEXML_APPLY_COMMAND");
|
|
145
|
+
const LITEXML_REMOVE_COMMAND = createCommand("LITEXML_REMOVE_COMMAND");
|
|
146
|
+
const LITEXML_INSERT_COMMAND = createCommand("LITEXML_INSERT_COMMAND");
|
|
147
|
+
function registerLiteXMLCommand(editor, dataSource) {
|
|
148
|
+
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (payload) => {
|
|
149
|
+
const resultPayload = payload.reduce((acc, cur) => {
|
|
150
|
+
if (cur.action === "insert") acc.unshift(cur);
|
|
151
|
+
else acc.push(cur);
|
|
152
|
+
return acc;
|
|
153
|
+
}, []);
|
|
154
|
+
try {
|
|
155
|
+
resultPayload.forEach((item) => {
|
|
156
|
+
const { action } = item;
|
|
157
|
+
switch (action) {
|
|
158
|
+
case "modify": {
|
|
159
|
+
const { litexml } = item;
|
|
160
|
+
handleModify(editor, dataSource, toArrayXml(litexml), true);
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
case "remove": {
|
|
164
|
+
const { id } = item;
|
|
165
|
+
handleRemove(editor, charToId(id), true);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case "insert":
|
|
169
|
+
handleInsert(editor, {
|
|
170
|
+
...item,
|
|
171
|
+
delay: true
|
|
172
|
+
}, dataSource);
|
|
173
|
+
break;
|
|
174
|
+
default: logger$6.warn(`⚠️ Unknown action type: ${action}`);
|
|
139
175
|
}
|
|
140
176
|
});
|
|
141
|
-
}));
|
|
142
|
-
this.register(editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
143
|
-
if (this.currentSuggestion) {
|
|
144
|
-
event?.preventDefault();
|
|
145
|
-
this.applySuggestion(editor);
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
177
|
return false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (this.currentSuggestion) {
|
|
152
|
-
event?.preventDefault();
|
|
153
|
-
this.clearPlaceholderNodes(editor);
|
|
154
|
-
this.currentSuggestion = null;
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
logger$6.error("❌ Error processing LITEXML_MODIFY_COMMAND:", error);
|
|
157
180
|
return false;
|
|
158
|
-
}, COMMAND_PRIORITY_CRITICAL));
|
|
159
|
-
this.register(editor.registerTextContentListener(() => {
|
|
160
|
-
if (this.placeholderNodes.length > 0) this.clearPlaceholderNodes(editor);
|
|
161
|
-
}));
|
|
162
|
-
}
|
|
163
|
-
hasPositionChanged(currentPosition) {
|
|
164
|
-
if (!this.lastCursorPosition) return true;
|
|
165
|
-
return this.lastCursorPosition.key !== currentPosition.key || this.lastCursorPosition.offset !== currentPosition.offset || this.lastCursorPosition.type !== currentPosition.type;
|
|
166
|
-
}
|
|
167
|
-
clearTimer() {
|
|
168
|
-
this.abortController?.abort("use cancel");
|
|
169
|
-
if (this.cursorStableTimer) {
|
|
170
|
-
clearTimeout(this.cursorStableTimer);
|
|
171
|
-
this.cursorStableTimer = null;
|
|
172
181
|
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
editor
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}).then((result) => {
|
|
199
|
-
let currentSelection = null;
|
|
200
|
-
editor.getEditorState().read(() => {
|
|
201
|
-
currentSelection = $getSelection();
|
|
202
|
-
});
|
|
203
|
-
if (editor.isComposing()) {
|
|
204
|
-
this.clearPlaceholderNodes(editor);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (!currentSelection || !$isRangeSelection(currentSelection) || !currentSelection.isCollapsed()) {
|
|
208
|
-
this.clearPlaceholderNodes(editor);
|
|
209
|
-
return;
|
|
182
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_APPLY_COMMAND, (payload) => {
|
|
183
|
+
const { litexml, delay } = payload;
|
|
184
|
+
handleModify(editor, dataSource, toArrayXml(litexml), delay);
|
|
185
|
+
return false;
|
|
186
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_REMOVE_COMMAND, (payload) => {
|
|
187
|
+
const { id, delay } = payload;
|
|
188
|
+
handleRemove(editor, charToId(id), delay);
|
|
189
|
+
return false;
|
|
190
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_INSERT_COMMAND, (payload) => {
|
|
191
|
+
handleInsert(editor, payload, dataSource);
|
|
192
|
+
return false;
|
|
193
|
+
}, COMMAND_PRIORITY_EDITOR));
|
|
194
|
+
}
|
|
195
|
+
function handleModify(editor, dataSource, arrayXml, delay) {
|
|
196
|
+
if (delay) editor.update(() => {
|
|
197
|
+
const modifyBlockNodes = /* @__PURE__ */ new Set();
|
|
198
|
+
const diffNodeMap = /* @__PURE__ */ new Map();
|
|
199
|
+
arrayXml.forEach((xml) => {
|
|
200
|
+
dataSource.readLiteXMLToInode(xml).root.children.forEach((child) => {
|
|
201
|
+
try {
|
|
202
|
+
const { oldNode, newNode } = tryParseChild(child, editor);
|
|
203
|
+
if (oldNode && newNode) handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor);
|
|
204
|
+
else logger$6.warn(`⚠️ Node with key ${child.id} not found for diffing.`);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
logger$6.error("❌ Error replacing node:", error);
|
|
210
207
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor);
|
|
211
|
+
});
|
|
212
|
+
else editor.update(() => {
|
|
213
|
+
arrayXml.forEach((xml) => {
|
|
214
|
+
const inode = dataSource.readLiteXMLToInode(xml);
|
|
215
|
+
let prevNode = null;
|
|
216
|
+
inode.root.children.forEach((child) => {
|
|
217
|
+
try {
|
|
218
|
+
const { oldNode, newNode } = tryParseChild(child, editor);
|
|
219
|
+
if (oldNode && newNode) prevNode = oldNode.replace(newNode, false);
|
|
220
|
+
else if (newNode) if (prevNode) if (!newNode.isInline()) {
|
|
221
|
+
const prevBlock = $closest(prevNode, (node) => node.isInline() === false);
|
|
222
|
+
if (prevBlock) prevNode = prevBlock.insertAfter(newNode);
|
|
223
|
+
else {
|
|
224
|
+
$insertNodes([newNode]);
|
|
225
|
+
prevNode = newNode;
|
|
226
|
+
}
|
|
227
|
+
} else prevNode = prevNode.insertAfter(newNode);
|
|
228
|
+
else $insertNodes([newNode]);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
logger$6.error("❌ Error replacing node:", error);
|
|
219
231
|
}
|
|
220
|
-
if (result) {
|
|
221
|
-
this.currentSuggestion = result;
|
|
222
|
-
this.showPlaceholderNodes(editor, result);
|
|
223
|
-
this.logger.debug("🔍 Auto-complete triggered:", {
|
|
224
|
-
afterText: textRet.textAfter,
|
|
225
|
-
input: textRet.textBefore,
|
|
226
|
-
position: currentPosition,
|
|
227
|
-
result,
|
|
228
|
-
selectionType
|
|
229
|
-
});
|
|
230
|
-
} else this.clearPlaceholderNodes(editor);
|
|
231
232
|
});
|
|
232
233
|
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
while (paragraphNode && paragraphNode.isInline()) {
|
|
243
|
-
const parent = paragraphNode.getParent();
|
|
244
|
-
if (!parent) break;
|
|
245
|
-
paragraphNode = parent;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function handleRemove(editor, key, delay) {
|
|
237
|
+
editor.update(() => {
|
|
238
|
+
const node = $getNodeByKey(key);
|
|
239
|
+
if (!node) return;
|
|
240
|
+
if (!delay) {
|
|
241
|
+
node.remove();
|
|
242
|
+
return;
|
|
246
243
|
}
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
244
|
+
if (node.isInline() === false) {
|
|
245
|
+
const originDiffNode = $closest(node, (node) => node.getType() === DiffNode.getType());
|
|
246
|
+
if (originDiffNode) {
|
|
247
|
+
switch (originDiffNode.diffType) {
|
|
248
|
+
case "add":
|
|
249
|
+
originDiffNode.remove();
|
|
250
|
+
return;
|
|
251
|
+
case "modify": {
|
|
252
|
+
const children = originDiffNode.getChildren();
|
|
253
|
+
const newDiff = $createDiffNode("remove");
|
|
254
|
+
newDiff.append(children[0]);
|
|
255
|
+
originDiffNode.replace(newDiff, false);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
case "listItemModify": {
|
|
259
|
+
const children = originDiffNode.getChildren();
|
|
260
|
+
originDiffNode.replace(children[0], false).selectEnd();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
case "remove":
|
|
264
|
+
case "unchanged": break;
|
|
259
265
|
}
|
|
260
|
-
return
|
|
261
|
-
text: "",
|
|
262
|
-
textAfter: ""
|
|
263
|
-
};
|
|
266
|
+
return;
|
|
264
267
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
268
|
+
if ($isListItemNode(node)) {
|
|
269
|
+
const diffNode = $createDiffNode("listItemRemove");
|
|
270
|
+
node.getChildren().forEach((child) => {
|
|
271
|
+
diffNode.append($cloneNode(child, editor));
|
|
272
|
+
});
|
|
273
|
+
node.clear();
|
|
274
|
+
node.append(diffNode);
|
|
275
|
+
} else {
|
|
276
|
+
const diffNode = $createDiffNode("remove");
|
|
277
|
+
diffNode.append($cloneNode(node, editor));
|
|
278
|
+
node.replace(diffNode, false);
|
|
276
279
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const result = collectTextBeforeCursor(paragraphNode, anchorNode, anchorOffset);
|
|
283
|
-
return {
|
|
284
|
-
textAfter: result.textAfter,
|
|
285
|
-
textBefore: result.text
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
showPlaceholderNodes(editor, suggestion) {
|
|
289
|
-
editor.update(() => {
|
|
290
|
-
const selection = $getSelection();
|
|
291
|
-
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
292
|
-
this.logger.warn("⚠️ No valid selection for placeholder");
|
|
280
|
+
} else {
|
|
281
|
+
const oldBlock = $closest(node, (node) => node.isInline() === false);
|
|
282
|
+
if (!oldBlock) throw new Error("Old block node not found for removal.");
|
|
283
|
+
if ($closest(node, (node) => node.getType() === DiffNode.getType())) {
|
|
284
|
+
node.remove();
|
|
293
285
|
return;
|
|
294
286
|
}
|
|
295
|
-
|
|
296
|
-
|
|
287
|
+
wrapBlockModify(oldBlock, editor, () => {
|
|
288
|
+
node.remove();
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function handleInsert(editor, payload, dataSource) {
|
|
294
|
+
const { litexml, delay } = payload;
|
|
295
|
+
const isBefore = "beforeId" in payload;
|
|
296
|
+
const inode = dataSource.readLiteXMLToInode(litexml);
|
|
297
|
+
editor.update(() => {
|
|
298
|
+
try {
|
|
299
|
+
let referenceNode = null;
|
|
300
|
+
if (isBefore) if (payload.beforeId === "root") referenceNode = $getRoot().getFirstChild();
|
|
301
|
+
else referenceNode = $getNodeByKey(charToId(payload.beforeId));
|
|
302
|
+
else if (payload.afterId === "root") referenceNode = $getRoot().getLastChild();
|
|
303
|
+
else referenceNode = $getNodeByKey(charToId(payload.afterId));
|
|
304
|
+
if (!referenceNode) throw new Error("Reference node not found for insertion.");
|
|
305
|
+
const newNodes = inode.root.children.map((child) => $parseSerializedNodeImpl(child, editor));
|
|
306
|
+
if (!delay) {
|
|
307
|
+
if (isBefore) newNodes.reverse().forEach((node) => {
|
|
308
|
+
referenceNode = referenceNode.insertBefore(node);
|
|
309
|
+
});
|
|
310
|
+
else newNodes.forEach((node) => {
|
|
311
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
312
|
+
});
|
|
297
313
|
return;
|
|
298
314
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
$setSelection(clonedSelection);
|
|
337
|
-
node.getParent()?.remove();
|
|
338
|
-
return;
|
|
315
|
+
if (isBefore) if (referenceNode.isInline() === false) {
|
|
316
|
+
const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
|
|
317
|
+
if (originDiffNode) referenceNode = originDiffNode;
|
|
318
|
+
newNodes.map((node) => {
|
|
319
|
+
const diffNode = $createDiffNode("add");
|
|
320
|
+
diffNode.append(node);
|
|
321
|
+
return diffNode;
|
|
322
|
+
}).reverse().forEach((diffNode) => {
|
|
323
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
|
|
324
|
+
});
|
|
325
|
+
} else {
|
|
326
|
+
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
327
|
+
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
328
|
+
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
329
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
330
|
+
});
|
|
331
|
+
else wrapBlockModify(refBlock, editor, () => {
|
|
332
|
+
newNodes.forEach((node) => {
|
|
333
|
+
if (referenceNode) referenceNode = referenceNode.insertBefore(node);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
else if (referenceNode.isInline() === false) {
|
|
338
|
+
const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
|
|
339
|
+
if (originDiffNode) referenceNode = originDiffNode;
|
|
340
|
+
newNodes.forEach((node) => {
|
|
341
|
+
if (referenceNode) if ($isListItemNode(node)) {
|
|
342
|
+
const diffNode = $createDiffNode("listItemAdd");
|
|
343
|
+
node.getChildren().forEach((child) => {
|
|
344
|
+
diffNode.append(child);
|
|
345
|
+
});
|
|
346
|
+
node.append(diffNode);
|
|
347
|
+
referenceNode = referenceNode.insertAfter(node);
|
|
348
|
+
} else {
|
|
349
|
+
const diffNode = $createDiffNode("add");
|
|
350
|
+
diffNode.append(node);
|
|
351
|
+
referenceNode = referenceNode.insertAfter(diffNode);
|
|
339
352
|
}
|
|
340
|
-
|
|
341
|
-
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
356
|
+
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
357
|
+
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
358
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
359
|
+
});
|
|
360
|
+
else wrapBlockModify(refBlock, editor, () => {
|
|
361
|
+
newNodes.forEach((node) => {
|
|
362
|
+
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
logger$6.error("❌ Error inserting node:", error);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/plugins/litexml/command/diffCommand.ts
|
|
373
|
+
let DiffAction = /* @__PURE__ */ function(DiffAction) {
|
|
374
|
+
DiffAction[DiffAction["Reject"] = 0] = "Reject";
|
|
375
|
+
DiffAction[DiffAction["Accept"] = 1] = "Accept";
|
|
376
|
+
return DiffAction;
|
|
377
|
+
}({});
|
|
378
|
+
const LITEXML_DIFFNODE_COMMAND = createCommand("LITEXML_DIFFNODE_COMMAND");
|
|
379
|
+
const LITEXML_DIFFNODE_ALL_COMMAND = createCommand("LITEXML_DIFFNODE_ALL_COMMAND");
|
|
380
|
+
function doAction(node, action) {
|
|
381
|
+
if (node.diffType === "modify") {
|
|
382
|
+
const children = node.getChildren();
|
|
383
|
+
if (action === 1) node.replace(children[1], false).selectEnd();
|
|
384
|
+
else if (action === 0) node.replace(children[0], false).selectEnd();
|
|
385
|
+
}
|
|
386
|
+
if (node.diffType === "remove") {
|
|
387
|
+
if (action === 1) node.remove();
|
|
388
|
+
else if (action === 0) {
|
|
389
|
+
const children = node.getChildren();
|
|
390
|
+
node.replace(children[0], false).selectEnd();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (node.diffType === "add") {
|
|
394
|
+
if (action === 1) {
|
|
395
|
+
const children = node.getChildren();
|
|
396
|
+
node.replace(children[0], false).selectEnd();
|
|
397
|
+
} else if (action === 0) node.remove();
|
|
398
|
+
}
|
|
399
|
+
if (node.diffType === "listItemModify") {
|
|
400
|
+
const children = node.getChildren();
|
|
401
|
+
if (action === 1) {
|
|
402
|
+
const lastChild = children[1];
|
|
403
|
+
if (!$isElementNode(lastChild)) throw new Error("Expected element node as child of DiffNode");
|
|
404
|
+
const nodeChildrens = lastChild.getChildren();
|
|
405
|
+
for (let i = nodeChildrens.length - 1; i >= 0; i--) node.insertAfter(nodeChildrens[i]);
|
|
406
|
+
const parent = node.getParentOrThrow();
|
|
407
|
+
node.remove();
|
|
408
|
+
parent.selectEnd();
|
|
409
|
+
} else if (action === 0) {
|
|
410
|
+
const firstChild = children[0];
|
|
411
|
+
if (!$isElementNode(firstChild)) throw new Error("Expected element node as child of DiffNode");
|
|
412
|
+
const nodeChildrens = firstChild.getChildren();
|
|
413
|
+
for (let i = nodeChildrens.length - 1; i >= 0; i--) node.insertAfter(nodeChildrens[i]);
|
|
414
|
+
const parent = node.getParentOrThrow();
|
|
415
|
+
node.remove();
|
|
416
|
+
parent.selectEnd();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (node.diffType === "listItemRemove") {
|
|
420
|
+
if (action === 1) node.getParentOrThrow().remove();
|
|
421
|
+
else if (action === 0) {
|
|
422
|
+
node.getChildren().forEach((child) => {
|
|
423
|
+
node.getParentOrThrow().append(child);
|
|
342
424
|
});
|
|
343
|
-
|
|
425
|
+
node.getParentOrThrow().selectEnd();
|
|
426
|
+
node.remove();
|
|
427
|
+
}
|
|
344
428
|
}
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
429
|
+
if (node.diffType === "listItemAdd") {
|
|
430
|
+
if (action === 1) {
|
|
431
|
+
node.getChildren().forEach((child) => {
|
|
432
|
+
node.getParentOrThrow().append(child);
|
|
433
|
+
});
|
|
434
|
+
node.getParentOrThrow().selectEnd();
|
|
435
|
+
node.remove();
|
|
436
|
+
} else if (action === 0) node.remove();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function registerLiteXMLDiffCommand(editor) {
|
|
440
|
+
return mergeRegister(editor.registerCommand(LITEXML_DIFFNODE_COMMAND, (payload) => {
|
|
441
|
+
const { action, nodeKey } = payload;
|
|
442
|
+
const node = editor.getEditorState().read(() => {
|
|
443
|
+
return $getNodeByKey(nodeKey);
|
|
444
|
+
});
|
|
445
|
+
if (!node) return false;
|
|
348
446
|
editor.update(() => {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
447
|
+
doAction(node, action);
|
|
448
|
+
});
|
|
449
|
+
return false;
|
|
450
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_DIFFNODE_ALL_COMMAND, (payload) => {
|
|
451
|
+
const { action } = payload;
|
|
452
|
+
const nodes = editor.getEditorState().read(() => {
|
|
453
|
+
return Array.from(editor._editorState._nodeMap.values()).filter((n) => n instanceof DiffNode && !!n.getParent());
|
|
454
|
+
});
|
|
455
|
+
if (!nodes.length) return false;
|
|
456
|
+
editor.update(() => {
|
|
457
|
+
nodes.forEach((node) => {
|
|
458
|
+
doAction(node, action);
|
|
353
459
|
});
|
|
354
|
-
const nodes = this.markdownService.parseMarkdownToLexical(markdown);
|
|
355
|
-
this.markdownService.insertIRootNode(editor, nodes, selection);
|
|
356
|
-
this.clearPlaceholderNodes(editor);
|
|
357
|
-
this.currentSuggestion = null;
|
|
358
460
|
});
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
destroy() {
|
|
364
|
-
this.clearTimer();
|
|
365
|
-
this.placeholderNodes = [];
|
|
366
|
-
super.destroy();
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
//#endregion
|
|
370
|
-
//#region src/plugins/auto-complete/react/style.ts
|
|
371
|
-
const styles$14 = createStaticStyles(({ css }) => ({
|
|
372
|
-
placeholderBlock: css`
|
|
373
|
-
opacity: 0.4;
|
|
374
|
-
`,
|
|
375
|
-
placeholderInline: css`
|
|
376
|
-
opacity: 0.4;
|
|
377
|
-
`
|
|
378
|
-
}));
|
|
461
|
+
return false;
|
|
462
|
+
}, COMMAND_PRIORITY_EDITOR));
|
|
463
|
+
}
|
|
379
464
|
//#endregion
|
|
380
|
-
//#region src/plugins/
|
|
381
|
-
const
|
|
465
|
+
//#region src/plugins/inode/react/index.tsx
|
|
466
|
+
const ReactNodePlugin = () => {
|
|
382
467
|
const [editor] = useLexicalComposerContext();
|
|
383
468
|
useLayoutEffect(() => {
|
|
384
|
-
editor.registerPlugin(
|
|
385
|
-
|
|
386
|
-
onAutoComplete,
|
|
387
|
-
theme: {
|
|
388
|
-
placeholderBlock: cx(styles$14.placeholderBlock),
|
|
389
|
-
placeholderInline: cx(styles$14.placeholderInline)
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
}, []);
|
|
469
|
+
editor.registerPlugin(INodePlugin);
|
|
470
|
+
}, [editor]);
|
|
393
471
|
return null;
|
|
394
472
|
};
|
|
395
|
-
|
|
473
|
+
ReactNodePlugin.displayName = "ReactNodePlugin";
|
|
396
474
|
//#endregion
|
|
397
|
-
//#region src/plugins/litexml/
|
|
475
|
+
//#region src/plugins/litexml/data-source/litexml-data-source.ts
|
|
398
476
|
init_debug();
|
|
399
|
-
const logger$
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
function tryParseChild(child, editor) {
|
|
404
|
-
try {
|
|
405
|
-
const oldNode = $getNodeByKey(child.id);
|
|
406
|
-
return {
|
|
407
|
-
newNode: $parseSerializedNodeImpl(child, editor),
|
|
408
|
-
oldNode
|
|
409
|
-
};
|
|
410
|
-
} catch (error) {
|
|
411
|
-
logger$6.error("❌ Error parsing child node:", error);
|
|
477
|
+
const logger$5 = createDebugLogger("plugin", "litexml");
|
|
478
|
+
var IXmlWriterContext = class {
|
|
479
|
+
createXmlNode(tagName, attributes, textContent) {
|
|
412
480
|
return {
|
|
413
|
-
|
|
414
|
-
|
|
481
|
+
attributes: attributes || {},
|
|
482
|
+
children: [],
|
|
483
|
+
tagName,
|
|
484
|
+
textContent
|
|
415
485
|
};
|
|
416
486
|
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
487
|
+
};
|
|
488
|
+
/**
|
|
489
|
+
* LitexmlDataSource - Handles conversion between Lexical editor state and XML format
|
|
490
|
+
* Provides read (parse XML to Lexical) and write (export Lexical to XML) capabilities
|
|
491
|
+
*/
|
|
492
|
+
var LitexmlDataSource = class extends DataSource {
|
|
493
|
+
constructor(dataType = "litexml", getService, service) {
|
|
494
|
+
super(dataType);
|
|
495
|
+
this.dataType = dataType;
|
|
496
|
+
this.getService = getService;
|
|
497
|
+
this.ctx = new IXmlWriterContext();
|
|
498
|
+
this.litexmlService = service || new LitexmlService();
|
|
424
499
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const diffNode = $createDiffNode("modify");
|
|
433
|
-
diffNode.append($cloneNode(oldBlock, editor));
|
|
434
|
-
diffNodeMap.set(oldBlock.getKey(), diffNode);
|
|
435
|
-
}
|
|
436
|
-
oldNode.replace(newNode, false);
|
|
500
|
+
readLiteXMLToInode(litexml) {
|
|
501
|
+
if (typeof litexml !== "string") throw new Error("Invalid data type. Expected string, got " + typeof litexml);
|
|
502
|
+
const xml = this.parseXMLString(litexml);
|
|
503
|
+
const inode = this.xmlToLexical(xml);
|
|
504
|
+
this.getService?.(INodeService)?.processNodeTree(inode);
|
|
505
|
+
logger$5.debug("Parsed XML to Lexical State:", inode);
|
|
506
|
+
return inode;
|
|
437
507
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
508
|
+
/**
|
|
509
|
+
* Parse XML string and set it to the editor
|
|
510
|
+
* @param editor - The Lexical editor instance
|
|
511
|
+
* @param data - XML string to parse
|
|
512
|
+
*/
|
|
513
|
+
read(editor, data) {
|
|
514
|
+
try {
|
|
515
|
+
const inode = this.readLiteXMLToInode(data);
|
|
516
|
+
const newState = editor.parseEditorState({ root: INodeHelper.createRootNode() }, (state) => {
|
|
517
|
+
try {
|
|
518
|
+
const root = $parseSerializedNodeImpl(inode.root, editor, true, state);
|
|
519
|
+
state._nodeMap.set(root.getKey(), root);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.error(error);
|
|
522
|
+
}
|
|
452
523
|
});
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
diffNode.append($cloneNode(blockNode, editor));
|
|
458
|
-
blockNode.replace(diffNode, false);
|
|
524
|
+
editor.setEditorState(newState);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
logger$5.error("Failed to parse XML:", error);
|
|
527
|
+
throw error;
|
|
459
528
|
}
|
|
460
529
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
function wrapBlockModify(oldBlock, editor, changeFn) {
|
|
469
|
-
if ($isListItemNode(oldBlock)) {
|
|
470
|
-
const diffNode = $createDiffNode("listItemModify");
|
|
471
|
-
const p = $createParagraphNode();
|
|
472
|
-
oldBlock.getChildren().forEach((child) => {
|
|
473
|
-
p.append($cloneNode(child, editor));
|
|
474
|
-
});
|
|
475
|
-
changeFn();
|
|
476
|
-
diffNode.append(p);
|
|
477
|
-
const pNew = $createParagraphNode();
|
|
478
|
-
oldBlock.getChildren().forEach((child) => {
|
|
479
|
-
pNew.append(child);
|
|
480
|
-
});
|
|
481
|
-
diffNode.append(pNew);
|
|
482
|
-
oldBlock.append(diffNode);
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
const diffNode = $createDiffNode("modify");
|
|
486
|
-
diffNode.append($cloneNode(oldBlock, editor));
|
|
487
|
-
changeFn();
|
|
488
|
-
const newBlock = $getNodeByKey(oldBlock.getKey());
|
|
489
|
-
if (!newBlock) throw new Error("New block node not found for modify wrapper.");
|
|
490
|
-
diffNode.append($cloneNode(newBlock, editor));
|
|
491
|
-
newBlock.replace(diffNode, false);
|
|
492
|
-
}
|
|
493
|
-
const LITEXML_MODIFY_COMMAND = createCommand("LITEXML_MODIFY_COMMAND");
|
|
494
|
-
const LITEXML_APPLY_COMMAND = createCommand("LITEXML_APPLY_COMMAND");
|
|
495
|
-
const LITEXML_REMOVE_COMMAND = createCommand("LITEXML_REMOVE_COMMAND");
|
|
496
|
-
const LITEXML_INSERT_COMMAND = createCommand("LITEXML_INSERT_COMMAND");
|
|
497
|
-
function registerLiteXMLCommand(editor, dataSource) {
|
|
498
|
-
return mergeRegister(editor.registerCommand(LITEXML_MODIFY_COMMAND, (payload) => {
|
|
499
|
-
const resultPayload = payload.reduce((acc, cur) => {
|
|
500
|
-
if (cur.action === "insert") acc.unshift(cur);
|
|
501
|
-
else acc.push(cur);
|
|
502
|
-
return acc;
|
|
503
|
-
}, []);
|
|
530
|
+
/**
|
|
531
|
+
* Export editor content to XML format
|
|
532
|
+
* @param editor - The Lexical editor instance
|
|
533
|
+
* @param options - Write options (e.g., selection flag)
|
|
534
|
+
* @returns XML string representation of the editor content
|
|
535
|
+
*/
|
|
536
|
+
write(editor, options) {
|
|
504
537
|
try {
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
...item,
|
|
521
|
-
delay: true
|
|
522
|
-
}, dataSource);
|
|
523
|
-
break;
|
|
524
|
-
default: logger$6.warn(`⚠️ Unknown action type: ${action}`);
|
|
538
|
+
if (options?.selection) return editor.getEditorState().read(() => {
|
|
539
|
+
const selection = $getSelection();
|
|
540
|
+
if (!selection) return null;
|
|
541
|
+
if ($isRangeSelection(selection)) {
|
|
542
|
+
const selectedNodes = selection.getNodes();
|
|
543
|
+
const rootNode = INodeHelper.createRootNode();
|
|
544
|
+
if (selectedNodes.some((node) => node.isInline())) {
|
|
545
|
+
const p = INodeHelper.createParagraph();
|
|
546
|
+
INodeHelper.appendChild(p, ...selectedNodes.map((node) => node.exportJSON()));
|
|
547
|
+
INodeHelper.appendChild(rootNode, p);
|
|
548
|
+
} else INodeHelper.appendChild(rootNode, ...selectedNodes.map((node) => node.exportJSON()));
|
|
549
|
+
return editor.parseEditorState({ root: rootNode }).read(() => {
|
|
550
|
+
const lexicalRootNode = $getRoot();
|
|
551
|
+
return this.lexicalToXML(lexicalRootNode);
|
|
552
|
+
});
|
|
525
553
|
}
|
|
554
|
+
return null;
|
|
555
|
+
});
|
|
556
|
+
return editor.getEditorState().read(() => {
|
|
557
|
+
const rootNode = $getRoot();
|
|
558
|
+
return this.lexicalToXML(rootNode);
|
|
526
559
|
});
|
|
527
|
-
return false;
|
|
528
560
|
} catch (error) {
|
|
529
|
-
logger$
|
|
530
|
-
|
|
561
|
+
logger$5.error("Failed to export to XML:", error);
|
|
562
|
+
throw error;
|
|
531
563
|
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
return
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const { oldNode, newNode } = tryParseChild(child, editor);
|
|
569
|
-
if (oldNode && newNode) prevNode = oldNode.replace(newNode, false);
|
|
570
|
-
else if (newNode) if (prevNode) if (!newNode.isInline()) {
|
|
571
|
-
const prevBlock = $closest(prevNode, (node) => node.isInline() === false);
|
|
572
|
-
if (prevBlock) prevNode = prevBlock.insertAfter(newNode);
|
|
573
|
-
else {
|
|
574
|
-
$insertNodes([newNode]);
|
|
575
|
-
prevNode = newNode;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Parse XML string using browser's built-in parser
|
|
567
|
+
*/
|
|
568
|
+
parseXMLString(xmlString) {
|
|
569
|
+
const doc = new DOMParser().parseFromString(xmlString, "text/xml");
|
|
570
|
+
if (doc.getElementsByTagName("parsererror").length > 0) throw new Error("Invalid XML: " + xmlString);
|
|
571
|
+
return doc;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Convert XML document to Lexical node structure
|
|
575
|
+
*/
|
|
576
|
+
xmlToLexical(xml) {
|
|
577
|
+
const rootNode = INodeHelper.createRootNode();
|
|
578
|
+
const xmlRoot = xml.documentElement;
|
|
579
|
+
if (xmlRoot) this.processXMLElement(xmlRoot, rootNode);
|
|
580
|
+
return { root: rootNode };
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Recursively process XML elements and convert to Lexical nodes
|
|
584
|
+
*/
|
|
585
|
+
processXMLElement(xmlElement, parentNode) {
|
|
586
|
+
const tagName = xmlElement.tagName.toLowerCase();
|
|
587
|
+
const customReaders = this.litexmlService.getXMLReaders();
|
|
588
|
+
if (customReaders[tagName]) {
|
|
589
|
+
const readerOrReaders = customReaders[tagName];
|
|
590
|
+
const readers = Array.isArray(readerOrReaders) ? readerOrReaders : [readerOrReaders];
|
|
591
|
+
for (const reader of readers) {
|
|
592
|
+
const children = [];
|
|
593
|
+
this.processXMLChildren(xmlElement, { children });
|
|
594
|
+
const result = reader(xmlElement, children);
|
|
595
|
+
if (result !== false) {
|
|
596
|
+
if (Array.isArray(result)) {
|
|
597
|
+
if (result.length > 0) {
|
|
598
|
+
const attrId = xmlElement.getAttribute("id");
|
|
599
|
+
result[0].id = attrId ? charToId(attrId) : void 0;
|
|
576
600
|
}
|
|
577
|
-
|
|
578
|
-
else
|
|
579
|
-
|
|
580
|
-
|
|
601
|
+
INodeHelper.appendChild(parentNode, ...result);
|
|
602
|
+
} else if (result) {
|
|
603
|
+
const attrId = xmlElement.getAttribute("id");
|
|
604
|
+
result.id = attrId ? charToId(attrId) : void 0;
|
|
605
|
+
INodeHelper.appendChild(parentNode, result);
|
|
606
|
+
}
|
|
607
|
+
return;
|
|
581
608
|
}
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
function handleRemove(editor, key, delay) {
|
|
587
|
-
editor.update(() => {
|
|
588
|
-
const node = $getNodeByKey(key);
|
|
589
|
-
if (!node) return;
|
|
590
|
-
if (!delay) {
|
|
591
|
-
node.remove();
|
|
592
|
-
return;
|
|
609
|
+
}
|
|
593
610
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
case "modify": {
|
|
602
|
-
const children = originDiffNode.getChildren();
|
|
603
|
-
const newDiff = $createDiffNode("remove");
|
|
604
|
-
newDiff.append(children[0]);
|
|
605
|
-
originDiffNode.replace(newDiff, false);
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
case "listItemModify": {
|
|
609
|
-
const children = originDiffNode.getChildren();
|
|
610
|
-
originDiffNode.replace(children[0], false).selectEnd();
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
case "remove":
|
|
614
|
-
case "unchanged": break;
|
|
615
|
-
}
|
|
616
|
-
return;
|
|
611
|
+
switch (tagName) {
|
|
612
|
+
case "p":
|
|
613
|
+
case "paragraph": {
|
|
614
|
+
const paragraph = INodeHelper.createParagraph();
|
|
615
|
+
this.processXMLChildren(xmlElement, paragraph);
|
|
616
|
+
INodeHelper.appendChild(parentNode, paragraph);
|
|
617
|
+
break;
|
|
617
618
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
619
|
+
case "h1":
|
|
620
|
+
case "h2":
|
|
621
|
+
case "h3":
|
|
622
|
+
case "h4":
|
|
623
|
+
case "h5":
|
|
624
|
+
case "h6": {
|
|
625
|
+
const level = parseInt(tagName.charAt(1));
|
|
626
|
+
const heading = INodeHelper.createElementNode("heading", {
|
|
627
|
+
children: [],
|
|
628
|
+
tag: `h${level}`
|
|
622
629
|
});
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const diffNode = $createDiffNode("remove");
|
|
627
|
-
diffNode.append($cloneNode(node, editor));
|
|
628
|
-
node.replace(diffNode, false);
|
|
630
|
+
this.processXMLChildren(xmlElement, heading);
|
|
631
|
+
INodeHelper.appendChild(parentNode, heading);
|
|
632
|
+
break;
|
|
629
633
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
634
|
+
case "ul":
|
|
635
|
+
case "ol":
|
|
636
|
+
xmlElement.querySelectorAll(":scope > li").forEach((li) => {
|
|
637
|
+
const listItem = INodeHelper.createElementNode("listitem", {
|
|
638
|
+
children: [],
|
|
639
|
+
value: 1
|
|
640
|
+
});
|
|
641
|
+
this.processXMLChildren(li, listItem);
|
|
642
|
+
INodeHelper.appendChild(parentNode, listItem);
|
|
643
|
+
});
|
|
644
|
+
break;
|
|
645
|
+
case "blockquote": {
|
|
646
|
+
const quote = INodeHelper.createElementNode("quote", { children: [] });
|
|
647
|
+
this.processXMLChildren(xmlElement, quote);
|
|
648
|
+
INodeHelper.appendChild(parentNode, quote);
|
|
649
|
+
break;
|
|
636
650
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
651
|
+
case "code": {
|
|
652
|
+
const codeNode = INodeHelper.createElementNode("codeInline", { children: [INodeHelper.createTextNode(xmlElement.textContent || "")] });
|
|
653
|
+
INodeHelper.appendChild(parentNode, codeNode);
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
case "text": {
|
|
657
|
+
const textContent = xmlElement.textContent || "";
|
|
658
|
+
if (textContent) {
|
|
659
|
+
const textNode = INodeHelper.createTextNode(textContent);
|
|
660
|
+
INodeHelper.appendChild(parentNode, textNode);
|
|
661
|
+
}
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
default: this.processXMLChildren(xmlElement, parentNode);
|
|
640
665
|
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (!referenceNode) throw new Error("Reference node not found for insertion.");
|
|
655
|
-
const newNodes = inode.root.children.map((child) => $parseSerializedNodeImpl(child, editor));
|
|
656
|
-
if (!delay) {
|
|
657
|
-
if (isBefore) newNodes.reverse().forEach((node) => {
|
|
658
|
-
referenceNode = referenceNode.insertBefore(node);
|
|
659
|
-
});
|
|
660
|
-
else newNodes.forEach((node) => {
|
|
661
|
-
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
662
|
-
});
|
|
663
|
-
return;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Process XML element's children
|
|
669
|
+
*/
|
|
670
|
+
processXMLChildren(xmlElement, parentNode) {
|
|
671
|
+
Array.from(xmlElement.childNodes).forEach((child) => {
|
|
672
|
+
if (child.nodeType === 1) this.processXMLElement(child, parentNode);
|
|
673
|
+
else if (child.nodeType === 3) {
|
|
674
|
+
const text = child.textContent || "";
|
|
675
|
+
if (text.trim()) {
|
|
676
|
+
const textNode = INodeHelper.createTextNode(text);
|
|
677
|
+
INodeHelper.appendChild(parentNode, textNode);
|
|
678
|
+
}
|
|
664
679
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Convert Lexical node structure to XML string
|
|
684
|
+
*/
|
|
685
|
+
lexicalToXML(rootNode) {
|
|
686
|
+
const xmlLines = ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>"];
|
|
687
|
+
xmlLines.push("<root>");
|
|
688
|
+
rootNode.getChildren().forEach((child) => {
|
|
689
|
+
this.nodesToXML(child, xmlLines);
|
|
690
|
+
});
|
|
691
|
+
xmlLines.push("</root>");
|
|
692
|
+
return xmlLines.join("\n");
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Recursively convert Lexical nodes to XML elements
|
|
696
|
+
*/
|
|
697
|
+
nodesToXML(node, lines, indent = 1) {
|
|
698
|
+
const indentStr = " ".repeat(indent * 2);
|
|
699
|
+
const type = node.getType();
|
|
700
|
+
const customWriters = this.litexmlService.getXMLWriters();
|
|
701
|
+
const childLines = [];
|
|
702
|
+
if (customWriters[type]) {
|
|
703
|
+
const writerOrWriters = customWriters[type];
|
|
704
|
+
const writers = Array.isArray(writerOrWriters) ? writerOrWriters : [writerOrWriters];
|
|
705
|
+
for (const writer of writers) {
|
|
706
|
+
const handled = writer(node, this.ctx, indent, this.nodesToXML.bind(this));
|
|
707
|
+
if (handled) {
|
|
708
|
+
if ("lines" in handled) {
|
|
709
|
+
lines.push(...handled.lines);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
const attrs = this.buildXMLAttributes({
|
|
713
|
+
id: idToChar(node.getKey()),
|
|
714
|
+
...handled.attributes
|
|
684
715
|
});
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (originDiffNode) referenceNode = originDiffNode;
|
|
690
|
-
newNodes.forEach((node) => {
|
|
691
|
-
if (referenceNode) if ($isListItemNode(node)) {
|
|
692
|
-
const diffNode = $createDiffNode("listItemAdd");
|
|
716
|
+
const openTag = `${indentStr}<${handled.tagName}${attrs}>`;
|
|
717
|
+
const closeTag = `</${handled.tagName}>`;
|
|
718
|
+
if (handled.textContent) lines.push(`${openTag}${handled.textContent}${closeTag}`);
|
|
719
|
+
else if ($isElementNode(node)) {
|
|
693
720
|
node.getChildren().forEach((child) => {
|
|
694
|
-
|
|
721
|
+
this.nodesToXML(child, childLines, indent + 1);
|
|
695
722
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
diffNode.append(node);
|
|
701
|
-
referenceNode = referenceNode.insertAfter(diffNode);
|
|
702
|
-
}
|
|
703
|
-
});
|
|
704
|
-
} else {
|
|
705
|
-
const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
|
|
706
|
-
if (!refBlock) throw new Error("Reference block node not found for insertion.");
|
|
707
|
-
if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
|
|
708
|
-
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
709
|
-
});
|
|
710
|
-
else wrapBlockModify(refBlock, editor, () => {
|
|
711
|
-
newNodes.forEach((node) => {
|
|
712
|
-
if (referenceNode) referenceNode = referenceNode.insertAfter(node);
|
|
713
|
-
});
|
|
714
|
-
});
|
|
723
|
+
lines.push(openTag, ...childLines, `${indentStr}${closeTag}`);
|
|
724
|
+
} else lines.push(openTag, `${indentStr}${closeTag}`);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
715
727
|
}
|
|
716
|
-
} catch (error) {
|
|
717
|
-
logger$6.error("❌ Error inserting node:", error);
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
//#endregion
|
|
722
|
-
//#region src/plugins/litexml/command/diffCommand.ts
|
|
723
|
-
let DiffAction = /* @__PURE__ */ function(DiffAction) {
|
|
724
|
-
DiffAction[DiffAction["Reject"] = 0] = "Reject";
|
|
725
|
-
DiffAction[DiffAction["Accept"] = 1] = "Accept";
|
|
726
|
-
return DiffAction;
|
|
727
|
-
}({});
|
|
728
|
-
const LITEXML_DIFFNODE_COMMAND = createCommand("LITEXML_DIFFNODE_COMMAND");
|
|
729
|
-
const LITEXML_DIFFNODE_ALL_COMMAND = createCommand("LITEXML_DIFFNODE_ALL_COMMAND");
|
|
730
|
-
function doAction(node, action) {
|
|
731
|
-
if (node.diffType === "modify") {
|
|
732
|
-
const children = node.getChildren();
|
|
733
|
-
if (action === 1) node.replace(children[1], false).selectEnd();
|
|
734
|
-
else if (action === 0) node.replace(children[0], false).selectEnd();
|
|
735
|
-
}
|
|
736
|
-
if (node.diffType === "remove") {
|
|
737
|
-
if (action === 1) node.remove();
|
|
738
|
-
else if (action === 0) {
|
|
739
|
-
const children = node.getChildren();
|
|
740
|
-
node.replace(children[0], false).selectEnd();
|
|
741
728
|
}
|
|
729
|
+
if ($isElementNode(node)) node.getChildren().forEach((child) => {
|
|
730
|
+
this.nodesToXML(child, childLines, indent);
|
|
731
|
+
});
|
|
732
|
+
lines.push(...childLines);
|
|
742
733
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
734
|
+
/**
|
|
735
|
+
* Build XML attribute string from attributes object
|
|
736
|
+
*/
|
|
737
|
+
buildXMLAttributes(attributes) {
|
|
738
|
+
if (!attributes || typeof attributes !== "object") return "";
|
|
739
|
+
return Object.entries(attributes).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => {
|
|
740
|
+
return ` ${key}="${this.escapeXML(String(value))}"`;
|
|
741
|
+
}).join("");
|
|
748
742
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
const nodeChildrens = lastChild.getChildren();
|
|
755
|
-
for (let i = nodeChildrens.length - 1; i >= 0; i--) node.insertAfter(nodeChildrens[i]);
|
|
756
|
-
const parent = node.getParentOrThrow();
|
|
757
|
-
node.remove();
|
|
758
|
-
parent.selectEnd();
|
|
759
|
-
} else if (action === 0) {
|
|
760
|
-
const firstChild = children[0];
|
|
761
|
-
if (!$isElementNode(firstChild)) throw new Error("Expected element node as child of DiffNode");
|
|
762
|
-
const nodeChildrens = firstChild.getChildren();
|
|
763
|
-
for (let i = nodeChildrens.length - 1; i >= 0; i--) node.insertAfter(nodeChildrens[i]);
|
|
764
|
-
const parent = node.getParentOrThrow();
|
|
765
|
-
node.remove();
|
|
766
|
-
parent.selectEnd();
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
if (node.diffType === "listItemRemove") {
|
|
770
|
-
if (action === 1) node.getParentOrThrow().remove();
|
|
771
|
-
else if (action === 0) {
|
|
772
|
-
node.getChildren().forEach((child) => {
|
|
773
|
-
node.getParentOrThrow().append(child);
|
|
774
|
-
});
|
|
775
|
-
node.getParentOrThrow().selectEnd();
|
|
776
|
-
node.remove();
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
if (node.diffType === "listItemAdd") {
|
|
780
|
-
if (action === 1) {
|
|
781
|
-
node.getChildren().forEach((child) => {
|
|
782
|
-
node.getParentOrThrow().append(child);
|
|
783
|
-
});
|
|
784
|
-
node.getParentOrThrow().selectEnd();
|
|
785
|
-
node.remove();
|
|
786
|
-
} else if (action === 0) node.remove();
|
|
743
|
+
/**
|
|
744
|
+
* Escape XML special characters
|
|
745
|
+
*/
|
|
746
|
+
escapeXML(text) {
|
|
747
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
787
748
|
}
|
|
749
|
+
};
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region src/utils/url.ts
|
|
752
|
+
init_registerHotkey();
|
|
753
|
+
/**
|
|
754
|
+
* Shared URL validation utilities
|
|
755
|
+
*/
|
|
756
|
+
const URL_REGEX = /* @__PURE__ */ new RegExp(/^(?:(?:https?|ftp):\/\/)?(?:www\.)?[\dA-Za-z][\w#%+./:=?@~-]*[\w#%+/=@~-]$/);
|
|
757
|
+
/**
|
|
758
|
+
* Validates if a string is a valid URL
|
|
759
|
+
* @param url - The URL to validate
|
|
760
|
+
* @returns true if the URL is valid, false otherwise
|
|
761
|
+
*/
|
|
762
|
+
function isValidUrl(url) {
|
|
763
|
+
if (!url || typeof url !== "string") return false;
|
|
764
|
+
const trimmed = url.trim();
|
|
765
|
+
if (URL_REGEX.test(trimmed)) return true;
|
|
766
|
+
try {
|
|
767
|
+
const urlObj = new URL(trimmed);
|
|
768
|
+
return !!urlObj.protocol && !!urlObj.hostname;
|
|
769
|
+
} catch {}
|
|
770
|
+
if (/^[\da-z][\w#%+./:=?@~-]*\.[\w#%+/:=?@~-]+$/i.test(trimmed)) return true;
|
|
771
|
+
return false;
|
|
788
772
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
return false;
|
|
800
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_DIFFNODE_ALL_COMMAND, (payload) => {
|
|
801
|
-
const { action } = payload;
|
|
802
|
-
const nodes = editor.getEditorState().read(() => {
|
|
803
|
-
return Array.from(editor._editorState._nodeMap.values()).filter((n) => n instanceof DiffNode && !!n.getParent());
|
|
804
|
-
});
|
|
805
|
-
if (!nodes.length) return false;
|
|
806
|
-
editor.update(() => {
|
|
807
|
-
nodes.forEach((node) => {
|
|
808
|
-
doAction(node, action);
|
|
809
|
-
});
|
|
810
|
-
});
|
|
811
|
-
return false;
|
|
812
|
-
}, COMMAND_PRIORITY_EDITOR));
|
|
773
|
+
/**
|
|
774
|
+
* Checks if text is a pure URL (single URL without other text)
|
|
775
|
+
* @param text - The text to check
|
|
776
|
+
* @returns true if text is a pure URL, false otherwise
|
|
777
|
+
*/
|
|
778
|
+
function isPureUrl(text) {
|
|
779
|
+
if (!text || typeof text !== "string") return false;
|
|
780
|
+
const trimmed = text.trim();
|
|
781
|
+
if (trimmed.includes("\n")) return false;
|
|
782
|
+
return isValidUrl(trimmed);
|
|
813
783
|
}
|
|
814
784
|
//#endregion
|
|
815
|
-
//#region src/plugins/litexml/
|
|
816
|
-
init_helper();
|
|
817
|
-
init_debug();
|
|
818
|
-
const logger$5 = createDebugLogger("plugin", "litexml");
|
|
819
|
-
var IXmlWriterContext = class {
|
|
820
|
-
createXmlNode(tagName, attributes, textContent) {
|
|
821
|
-
return {
|
|
822
|
-
attributes: attributes || {},
|
|
823
|
-
children: [],
|
|
824
|
-
tagName,
|
|
825
|
-
textContent
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
};
|
|
785
|
+
//#region src/plugins/litexml/plugin/index.ts
|
|
829
786
|
/**
|
|
830
|
-
*
|
|
831
|
-
*
|
|
787
|
+
* LitexmlPlugin - A plugin that provides XML-based data source support
|
|
788
|
+
* Allows converting between Lexical editor state and XML format
|
|
832
789
|
*/
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
this.dataType = dataType;
|
|
837
|
-
this.getService = getService;
|
|
838
|
-
this.ctx = new IXmlWriterContext();
|
|
839
|
-
this.litexmlService = service || new LitexmlService();
|
|
790
|
+
const LitexmlPlugin = class extends KernelPlugin {
|
|
791
|
+
static {
|
|
792
|
+
this.pluginName = "LitexmlPlugin";
|
|
840
793
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
794
|
+
constructor(kernel, config) {
|
|
795
|
+
super();
|
|
796
|
+
this.kernel = kernel;
|
|
797
|
+
this.config = config;
|
|
798
|
+
kernel.registerThemes({ diffNode: config?.theme || "editor_diffNode" });
|
|
799
|
+
const litexmlService = new LitexmlService();
|
|
800
|
+
this.service = litexmlService;
|
|
801
|
+
kernel.registerService(ILitexmlService, litexmlService);
|
|
802
|
+
this.datasource = new LitexmlDataSource("litexml", (serviceId) => {
|
|
803
|
+
return kernel.requireService(serviceId);
|
|
804
|
+
}, litexmlService);
|
|
805
|
+
kernel.registerNodes([DiffNode]);
|
|
806
|
+
kernel.registerDecorator(DiffNode.getType(), {
|
|
807
|
+
queryDOM: (el) => el.querySelector(".toolbar"),
|
|
808
|
+
render: (node, editor) => {
|
|
809
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
if (config?.enabled !== false) kernel.registerDataSource(this.datasource);
|
|
848
813
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
state._nodeMap.set(root.getKey(), root);
|
|
861
|
-
} catch (error) {
|
|
862
|
-
console.error(error);
|
|
863
|
-
}
|
|
814
|
+
onInit(editor) {
|
|
815
|
+
this.register(registerLiteXMLCommand(editor, this.datasource));
|
|
816
|
+
this.register(registerLiteXMLDiffCommand(editor));
|
|
817
|
+
this.register(editor.registerNodeTransform(DiffNode, (node) => {
|
|
818
|
+
if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
819
|
+
}));
|
|
820
|
+
this.register(editor.registerUpdateListener(({ editorState, prevEditorState }) => {
|
|
821
|
+
if (editorState === prevEditorState) return;
|
|
822
|
+
editor.update(() => {
|
|
823
|
+
const diffNodes = $nodesOfType(DiffNode);
|
|
824
|
+
for (const node of diffNodes) if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
864
825
|
});
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
throw error;
|
|
869
|
-
}
|
|
826
|
+
}));
|
|
827
|
+
this.registerLiteXml();
|
|
828
|
+
this.registerMarkdown();
|
|
870
829
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const p = INodeHelper.createParagraph();
|
|
887
|
-
INodeHelper.appendChild(p, ...selectedNodes.map((node) => node.exportJSON()));
|
|
888
|
-
INodeHelper.appendChild(rootNode, p);
|
|
889
|
-
} else INodeHelper.appendChild(rootNode, ...selectedNodes.map((node) => node.exportJSON()));
|
|
890
|
-
return editor.parseEditorState({ root: rootNode }).read(() => {
|
|
891
|
-
const lexicalRootNode = $getRoot();
|
|
892
|
-
return this.lexicalToXML(lexicalRootNode);
|
|
830
|
+
registerLiteXml() {
|
|
831
|
+
this.service.registerXMLWriter(DiffNode.getType(), (node, ctx, indent, nodeToXML) => {
|
|
832
|
+
const diffNode = node;
|
|
833
|
+
const lines = [];
|
|
834
|
+
switch (diffNode.diffType) {
|
|
835
|
+
case "modify":
|
|
836
|
+
nodeToXML(diffNode.getChildAtIndex(1), lines, indent);
|
|
837
|
+
break;
|
|
838
|
+
case "add":
|
|
839
|
+
nodeToXML(diffNode.getChildAtIndex(0), lines, indent);
|
|
840
|
+
break;
|
|
841
|
+
case "remove": break;
|
|
842
|
+
case "listItemModify":
|
|
843
|
+
diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
|
|
844
|
+
nodeToXML(child, lines, indent);
|
|
893
845
|
});
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
897
|
-
return
|
|
898
|
-
|
|
899
|
-
return this.lexicalToXML(rootNode);
|
|
900
|
-
});
|
|
901
|
-
} catch (error) {
|
|
902
|
-
logger$5.error("Failed to export to XML:", error);
|
|
903
|
-
throw error;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
/**
|
|
907
|
-
* Parse XML string using browser's built-in parser
|
|
908
|
-
*/
|
|
909
|
-
parseXMLString(xmlString) {
|
|
910
|
-
const doc = new DOMParser().parseFromString(xmlString, "text/xml");
|
|
911
|
-
if (doc.getElementsByTagName("parsererror").length > 0) throw new Error("Invalid XML: " + xmlString);
|
|
912
|
-
return doc;
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Convert XML document to Lexical node structure
|
|
916
|
-
*/
|
|
917
|
-
xmlToLexical(xml) {
|
|
918
|
-
const rootNode = INodeHelper.createRootNode();
|
|
919
|
-
const xmlRoot = xml.documentElement;
|
|
920
|
-
if (xmlRoot) this.processXMLElement(xmlRoot, rootNode);
|
|
921
|
-
return { root: rootNode };
|
|
922
|
-
}
|
|
923
|
-
/**
|
|
924
|
-
* Recursively process XML elements and convert to Lexical nodes
|
|
925
|
-
*/
|
|
926
|
-
processXMLElement(xmlElement, parentNode) {
|
|
927
|
-
const tagName = xmlElement.tagName.toLowerCase();
|
|
928
|
-
const customReaders = this.litexmlService.getXMLReaders();
|
|
929
|
-
if (customReaders[tagName]) {
|
|
930
|
-
const readerOrReaders = customReaders[tagName];
|
|
931
|
-
const readers = Array.isArray(readerOrReaders) ? readerOrReaders : [readerOrReaders];
|
|
932
|
-
for (const reader of readers) {
|
|
933
|
-
const children = [];
|
|
934
|
-
this.processXMLChildren(xmlElement, { children });
|
|
935
|
-
const result = reader(xmlElement, children);
|
|
936
|
-
if (result !== false) {
|
|
937
|
-
if (Array.isArray(result)) {
|
|
938
|
-
if (result.length > 0) {
|
|
939
|
-
const attrId = xmlElement.getAttribute("id");
|
|
940
|
-
result[0].id = attrId ? charToId(attrId) : void 0;
|
|
941
|
-
}
|
|
942
|
-
INodeHelper.appendChild(parentNode, ...result);
|
|
943
|
-
} else if (result) {
|
|
944
|
-
const attrId = xmlElement.getAttribute("id");
|
|
945
|
-
result.id = attrId ? charToId(attrId) : void 0;
|
|
946
|
-
INodeHelper.appendChild(parentNode, result);
|
|
947
|
-
}
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
switch (tagName) {
|
|
953
|
-
case "p":
|
|
954
|
-
case "paragraph": {
|
|
955
|
-
const paragraph = INodeHelper.createParagraph();
|
|
956
|
-
this.processXMLChildren(xmlElement, paragraph);
|
|
957
|
-
INodeHelper.appendChild(parentNode, paragraph);
|
|
958
|
-
break;
|
|
959
|
-
}
|
|
960
|
-
case "h1":
|
|
961
|
-
case "h2":
|
|
962
|
-
case "h3":
|
|
963
|
-
case "h4":
|
|
964
|
-
case "h5":
|
|
965
|
-
case "h6": {
|
|
966
|
-
const level = parseInt(tagName.charAt(1));
|
|
967
|
-
const heading = INodeHelper.createElementNode("heading", {
|
|
968
|
-
children: [],
|
|
969
|
-
tag: `h${level}`
|
|
970
|
-
});
|
|
971
|
-
this.processXMLChildren(xmlElement, heading);
|
|
972
|
-
INodeHelper.appendChild(parentNode, heading);
|
|
973
|
-
break;
|
|
974
|
-
}
|
|
975
|
-
case "ul":
|
|
976
|
-
case "ol":
|
|
977
|
-
xmlElement.querySelectorAll(":scope > li").forEach((li) => {
|
|
978
|
-
const listItem = INodeHelper.createElementNode("listitem", {
|
|
979
|
-
children: [],
|
|
980
|
-
value: 1
|
|
981
|
-
});
|
|
982
|
-
this.processXMLChildren(li, listItem);
|
|
983
|
-
INodeHelper.appendChild(parentNode, listItem);
|
|
984
|
-
});
|
|
985
|
-
break;
|
|
986
|
-
case "blockquote": {
|
|
987
|
-
const quote = INodeHelper.createElementNode("quote", { children: [] });
|
|
988
|
-
this.processXMLChildren(xmlElement, quote);
|
|
989
|
-
INodeHelper.appendChild(parentNode, quote);
|
|
990
|
-
break;
|
|
991
|
-
}
|
|
992
|
-
case "code": {
|
|
993
|
-
const codeNode = INodeHelper.createElementNode("codeInline", { children: [INodeHelper.createTextNode(xmlElement.textContent || "")] });
|
|
994
|
-
INodeHelper.appendChild(parentNode, codeNode);
|
|
995
|
-
break;
|
|
996
|
-
}
|
|
997
|
-
case "text": {
|
|
998
|
-
const textContent = xmlElement.textContent || "";
|
|
999
|
-
if (textContent) {
|
|
1000
|
-
const textNode = INodeHelper.createTextNode(textContent);
|
|
1001
|
-
INodeHelper.appendChild(parentNode, textNode);
|
|
1002
|
-
}
|
|
1003
|
-
break;
|
|
1004
|
-
}
|
|
1005
|
-
default: this.processXMLChildren(xmlElement, parentNode);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
/**
|
|
1009
|
-
* Process XML element's children
|
|
1010
|
-
*/
|
|
1011
|
-
processXMLChildren(xmlElement, parentNode) {
|
|
1012
|
-
Array.from(xmlElement.childNodes).forEach((child) => {
|
|
1013
|
-
if (child.nodeType === 1) this.processXMLElement(child, parentNode);
|
|
1014
|
-
else if (child.nodeType === 3) {
|
|
1015
|
-
const text = child.textContent || "";
|
|
1016
|
-
if (text.trim()) {
|
|
1017
|
-
const textNode = INodeHelper.createTextNode(text);
|
|
1018
|
-
INodeHelper.appendChild(parentNode, textNode);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
/**
|
|
1024
|
-
* Convert Lexical node structure to XML string
|
|
1025
|
-
*/
|
|
1026
|
-
lexicalToXML(rootNode) {
|
|
1027
|
-
const xmlLines = ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>"];
|
|
1028
|
-
xmlLines.push("<root>");
|
|
1029
|
-
rootNode.getChildren().forEach((child) => {
|
|
1030
|
-
this.nodesToXML(child, xmlLines);
|
|
1031
|
-
});
|
|
1032
|
-
xmlLines.push("</root>");
|
|
1033
|
-
return xmlLines.join("\n");
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* Recursively convert Lexical nodes to XML elements
|
|
1037
|
-
*/
|
|
1038
|
-
nodesToXML(node, lines, indent = 1) {
|
|
1039
|
-
const indentStr = " ".repeat(indent * 2);
|
|
1040
|
-
const type = node.getType();
|
|
1041
|
-
const customWriters = this.litexmlService.getXMLWriters();
|
|
1042
|
-
const childLines = [];
|
|
1043
|
-
if (customWriters[type]) {
|
|
1044
|
-
const writerOrWriters = customWriters[type];
|
|
1045
|
-
const writers = Array.isArray(writerOrWriters) ? writerOrWriters : [writerOrWriters];
|
|
1046
|
-
for (const writer of writers) {
|
|
1047
|
-
const handled = writer(node, this.ctx, indent, this.nodesToXML.bind(this));
|
|
1048
|
-
if (handled) {
|
|
1049
|
-
if ("lines" in handled) {
|
|
1050
|
-
lines.push(...handled.lines);
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
const attrs = this.buildXMLAttributes({
|
|
1054
|
-
id: idToChar(node.getKey()),
|
|
1055
|
-
...handled.attributes
|
|
1056
|
-
});
|
|
1057
|
-
const openTag = `${indentStr}<${handled.tagName}${attrs}>`;
|
|
1058
|
-
const closeTag = `</${handled.tagName}>`;
|
|
1059
|
-
if (handled.textContent) lines.push(`${openTag}${handled.textContent}${closeTag}`);
|
|
1060
|
-
else if ($isElementNode(node)) {
|
|
1061
|
-
node.getChildren().forEach((child) => {
|
|
1062
|
-
this.nodesToXML(child, childLines, indent + 1);
|
|
1063
|
-
});
|
|
1064
|
-
lines.push(openTag, ...childLines, `${indentStr}${closeTag}`);
|
|
1065
|
-
} else lines.push(openTag, `${indentStr}${closeTag}`);
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
if ($isElementNode(node)) node.getChildren().forEach((child) => {
|
|
1071
|
-
this.nodesToXML(child, childLines, indent);
|
|
1072
|
-
});
|
|
1073
|
-
lines.push(...childLines);
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Build XML attribute string from attributes object
|
|
1077
|
-
*/
|
|
1078
|
-
buildXMLAttributes(attributes) {
|
|
1079
|
-
if (!attributes || typeof attributes !== "object") return "";
|
|
1080
|
-
return Object.entries(attributes).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => {
|
|
1081
|
-
return ` ${key}="${this.escapeXML(String(value))}"`;
|
|
1082
|
-
}).join("");
|
|
1083
|
-
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Escape XML special characters
|
|
1086
|
-
*/
|
|
1087
|
-
escapeXML(text) {
|
|
1088
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
1089
|
-
}
|
|
1090
|
-
};
|
|
1091
|
-
//#endregion
|
|
1092
|
-
//#region src/plugins/litexml/plugin/index.ts
|
|
1093
|
-
init_plugin();
|
|
1094
|
-
/**
|
|
1095
|
-
* LitexmlPlugin - A plugin that provides XML-based data source support
|
|
1096
|
-
* Allows converting between Lexical editor state and XML format
|
|
1097
|
-
*/
|
|
1098
|
-
const LitexmlPlugin = class extends KernelPlugin {
|
|
1099
|
-
static {
|
|
1100
|
-
this.pluginName = "LitexmlPlugin";
|
|
1101
|
-
}
|
|
1102
|
-
constructor(kernel, config) {
|
|
1103
|
-
super();
|
|
1104
|
-
this.kernel = kernel;
|
|
1105
|
-
this.config = config;
|
|
1106
|
-
kernel.registerThemes({ diffNode: config?.theme || "editor_diffNode" });
|
|
1107
|
-
const litexmlService = new LitexmlService();
|
|
1108
|
-
this.service = litexmlService;
|
|
1109
|
-
kernel.registerService(ILitexmlService, litexmlService);
|
|
1110
|
-
this.datasource = new LitexmlDataSource("litexml", (serviceId) => {
|
|
1111
|
-
return kernel.requireService(serviceId);
|
|
1112
|
-
}, litexmlService);
|
|
1113
|
-
kernel.registerNodes([DiffNode]);
|
|
1114
|
-
kernel.registerDecorator(DiffNode.getType(), {
|
|
1115
|
-
queryDOM: (el) => el.querySelector(".toolbar"),
|
|
1116
|
-
render: (node, editor) => {
|
|
1117
|
-
return config?.decorator ? config.decorator(node, editor) : null;
|
|
1118
|
-
}
|
|
1119
|
-
});
|
|
1120
|
-
if (config?.enabled !== false) kernel.registerDataSource(this.datasource);
|
|
1121
|
-
}
|
|
1122
|
-
onInit(editor) {
|
|
1123
|
-
this.register(registerLiteXMLCommand(editor, this.datasource));
|
|
1124
|
-
this.register(registerLiteXMLDiffCommand(editor));
|
|
1125
|
-
this.register(editor.registerNodeTransform(DiffNode, (node) => {
|
|
1126
|
-
if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
1127
|
-
}));
|
|
1128
|
-
this.register(editor.registerUpdateListener(({ editorState, prevEditorState }) => {
|
|
1129
|
-
if (editorState === prevEditorState) return;
|
|
1130
|
-
editor.update(() => {
|
|
1131
|
-
const diffNodes = $nodesOfType(DiffNode);
|
|
1132
|
-
for (const node of diffNodes) if (node.diffType === "modify" && node.getChildrenSize() === 1) node.setDiffType("remove");
|
|
1133
|
-
});
|
|
1134
|
-
}));
|
|
1135
|
-
this.registerLiteXml();
|
|
1136
|
-
this.registerMarkdown();
|
|
1137
|
-
}
|
|
1138
|
-
registerLiteXml() {
|
|
1139
|
-
this.service.registerXMLWriter(DiffNode.getType(), (node, ctx, indent, nodeToXML) => {
|
|
1140
|
-
const diffNode = node;
|
|
1141
|
-
const lines = [];
|
|
1142
|
-
switch (diffNode.diffType) {
|
|
1143
|
-
case "modify":
|
|
1144
|
-
nodeToXML(diffNode.getChildAtIndex(1), lines, indent);
|
|
1145
|
-
break;
|
|
1146
|
-
case "add":
|
|
1147
|
-
nodeToXML(diffNode.getChildAtIndex(0), lines, indent);
|
|
1148
|
-
break;
|
|
1149
|
-
case "remove": break;
|
|
1150
|
-
case "listItemModify":
|
|
1151
|
-
diffNode.getChildAtIndex(1).getChildren().forEach((child) => {
|
|
1152
|
-
nodeToXML(child, lines, indent);
|
|
1153
|
-
});
|
|
1154
|
-
break;
|
|
1155
|
-
default: break;
|
|
1156
|
-
}
|
|
1157
|
-
return { lines };
|
|
1158
|
-
});
|
|
846
|
+
break;
|
|
847
|
+
default: break;
|
|
848
|
+
}
|
|
849
|
+
return { lines };
|
|
850
|
+
});
|
|
1159
851
|
}
|
|
1160
852
|
registerMarkdown() {
|
|
1161
853
|
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(DiffNode.getType(), (ctx, node) => {
|
|
@@ -1181,7 +873,7 @@ const LitexmlPlugin = class extends KernelPlugin {
|
|
|
1181
873
|
};
|
|
1182
874
|
//#endregion
|
|
1183
875
|
//#region src/plugins/litexml/react/DiffNodeToolbar/style.ts
|
|
1184
|
-
const styles$
|
|
876
|
+
const styles$14 = createStaticStyles(({ css, cssVar }) => ({
|
|
1185
877
|
accept: css`
|
|
1186
878
|
color: ${cssVar.colorSuccess};
|
|
1187
879
|
`,
|
|
@@ -1210,7 +902,7 @@ const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
|
1210
902
|
editor,
|
|
1211
903
|
node,
|
|
1212
904
|
children: /* @__PURE__ */ jsxs(Block, {
|
|
1213
|
-
className: isDarkMode ? styles$
|
|
905
|
+
className: isDarkMode ? styles$14.toolbarDark : styles$14.toolbarLight,
|
|
1214
906
|
gap: 2,
|
|
1215
907
|
horizontal: true,
|
|
1216
908
|
padding: 2,
|
|
@@ -1218,7 +910,7 @@ const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
|
1218
910
|
variant: "outlined",
|
|
1219
911
|
children: [/* @__PURE__ */ jsx(ActionIcon, {
|
|
1220
912
|
"aria-label": "Reject change",
|
|
1221
|
-
className: styles$
|
|
913
|
+
className: styles$14.reject,
|
|
1222
914
|
danger: true,
|
|
1223
915
|
icon: X,
|
|
1224
916
|
onClick: () => {
|
|
@@ -1234,7 +926,7 @@ const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
|
1234
926
|
title: t("modifier.reject")
|
|
1235
927
|
}), /* @__PURE__ */ jsx(ActionIcon, {
|
|
1236
928
|
"aria-label": "Accept change",
|
|
1237
|
-
className: styles$
|
|
929
|
+
className: styles$14.accept,
|
|
1238
930
|
icon: Check,
|
|
1239
931
|
onClick: () => {
|
|
1240
932
|
editor.dispatchCommand(LITEXML_DIFFNODE_COMMAND, {
|
|
@@ -1254,7 +946,7 @@ const ReactDiffNodeToolbar = ({ editor, node }) => {
|
|
|
1254
946
|
ReactDiffNodeToolbar.displayName = "ReactDiffNodeToolbar";
|
|
1255
947
|
//#endregion
|
|
1256
948
|
//#region src/plugins/litexml/react/style.ts
|
|
1257
|
-
const styles$
|
|
949
|
+
const styles$13 = createStaticStyles(({ css, cssVar }) => css`
|
|
1258
950
|
position: relative;
|
|
1259
951
|
|
|
1260
952
|
.toolbar {
|
|
@@ -1385,7 +1077,7 @@ const ReactLiteXmlPlugin = () => {
|
|
|
1385
1077
|
editor,
|
|
1386
1078
|
node
|
|
1387
1079
|
}),
|
|
1388
|
-
theme: styles$
|
|
1080
|
+
theme: styles$13
|
|
1389
1081
|
});
|
|
1390
1082
|
}, [editor]);
|
|
1391
1083
|
return null;
|
|
@@ -1514,6 +1206,1132 @@ const CodePlugin = class extends KernelPlugin {
|
|
|
1514
1206
|
}
|
|
1515
1207
|
};
|
|
1516
1208
|
//#endregion
|
|
1209
|
+
//#region src/plugins/hr/command/index.ts
|
|
1210
|
+
const INSERT_HORIZONTAL_RULE_COMMAND = createCommand("INSERT_HORIZONTAL_RULE_COMMAND");
|
|
1211
|
+
function registerHorizontalRuleCommand(editor) {
|
|
1212
|
+
return editor.registerCommand(INSERT_HORIZONTAL_RULE_COMMAND, () => {
|
|
1213
|
+
editor.update(() => {
|
|
1214
|
+
$insertNodes([$createHorizontalRuleNode()]);
|
|
1215
|
+
});
|
|
1216
|
+
return true;
|
|
1217
|
+
}, COMMAND_PRIORITY_EDITOR);
|
|
1218
|
+
}
|
|
1219
|
+
//#endregion
|
|
1220
|
+
//#region src/plugins/hr/plugin/index.ts
|
|
1221
|
+
init_helper();
|
|
1222
|
+
init_plugin();
|
|
1223
|
+
const HRPlugin = class extends KernelPlugin {
|
|
1224
|
+
static {
|
|
1225
|
+
this.pluginName = "HRPlugin";
|
|
1226
|
+
}
|
|
1227
|
+
constructor(kernel, config) {
|
|
1228
|
+
super();
|
|
1229
|
+
this.kernel = kernel;
|
|
1230
|
+
kernel.registerNodes([HorizontalRuleNode]);
|
|
1231
|
+
kernel.registerThemes({ hr: config?.theme || "" });
|
|
1232
|
+
this.registerDecorator(kernel, "horizontalrule", (node, editor) => {
|
|
1233
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
onInit(editor) {
|
|
1237
|
+
this.register(registerHorizontalRuleCommand(editor));
|
|
1238
|
+
this.registerMarkdown();
|
|
1239
|
+
this.registerLiteXml();
|
|
1240
|
+
}
|
|
1241
|
+
registerLiteXml() {
|
|
1242
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
1243
|
+
if (!litexmlService) return;
|
|
1244
|
+
litexmlService.registerXMLWriter(HorizontalRuleNode.getType(), (node, ctx) => {
|
|
1245
|
+
if ($isHorizontalRuleNode(node)) return ctx.createXmlNode("hr", {});
|
|
1246
|
+
return false;
|
|
1247
|
+
});
|
|
1248
|
+
litexmlService.registerXMLReader("hr", () => {
|
|
1249
|
+
return INodeHelper.createElementNode(HorizontalRuleNode.getType(), {});
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
registerMarkdown() {
|
|
1253
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownShortCut({
|
|
1254
|
+
regExp: /^(---|\*\*\*|___)$/,
|
|
1255
|
+
replace: (parentNode, _1, _2, isImport) => {
|
|
1256
|
+
const line = $createHorizontalRuleNode();
|
|
1257
|
+
if (isImport || parentNode.getNextSibling()) parentNode.replace(line);
|
|
1258
|
+
else parentNode.insertBefore(line);
|
|
1259
|
+
line.selectNext();
|
|
1260
|
+
},
|
|
1261
|
+
trigger: "enter",
|
|
1262
|
+
type: "element"
|
|
1263
|
+
});
|
|
1264
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(HorizontalRuleNode.getType(), (ctx, node) => {
|
|
1265
|
+
if ($isHorizontalRuleNode(node)) ctx.appendLine("---\n\n");
|
|
1266
|
+
});
|
|
1267
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("thematicBreak", () => {
|
|
1268
|
+
return INodeHelper.createElementNode("horizontalrule", {});
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
//#endregion
|
|
1273
|
+
//#region src/utils/cx.ts
|
|
1274
|
+
const cx$1 = (...classNames) => classNames.filter(Boolean).join(" ");
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/plugins/list/utils/index.ts
|
|
1277
|
+
const LIST_INDENT_SIZE = 4;
|
|
1278
|
+
function getIndent(whitespaces) {
|
|
1279
|
+
const tabs = whitespaces.match(/\t/g);
|
|
1280
|
+
const spaces = whitespaces.match(/ /g);
|
|
1281
|
+
let indent = 0;
|
|
1282
|
+
if (tabs) indent += tabs.length;
|
|
1283
|
+
if (spaces) indent += Math.floor(spaces.length / LIST_INDENT_SIZE);
|
|
1284
|
+
return indent;
|
|
1285
|
+
}
|
|
1286
|
+
const listReplace = (listType) => {
|
|
1287
|
+
return (parentNode, children, match, isImport) => {
|
|
1288
|
+
const previousNode = parentNode.getPreviousSibling();
|
|
1289
|
+
const nextNode = parentNode.getNextSibling();
|
|
1290
|
+
const listItem = $createListItemNode(listType === "check" ? match[3] === "x" : void 0);
|
|
1291
|
+
if ($isListNode(nextNode) && nextNode.getListType() === listType) {
|
|
1292
|
+
const firstChild = nextNode.getFirstChild();
|
|
1293
|
+
if (firstChild !== null) firstChild.insertBefore(listItem);
|
|
1294
|
+
else nextNode.append(listItem);
|
|
1295
|
+
parentNode.remove();
|
|
1296
|
+
} else if ($isListNode(previousNode) && previousNode.getListType() === listType) {
|
|
1297
|
+
previousNode.append(listItem);
|
|
1298
|
+
parentNode.remove();
|
|
1299
|
+
} else {
|
|
1300
|
+
const list = $createListNode(listType, listType === "number" ? Number(match[2]) : void 0);
|
|
1301
|
+
list.append(listItem);
|
|
1302
|
+
parentNode.replace(list);
|
|
1303
|
+
}
|
|
1304
|
+
listItem.append(...children);
|
|
1305
|
+
if (!isImport) listItem.select(0, 0);
|
|
1306
|
+
const indent = getIndent(match[1]);
|
|
1307
|
+
if (indent) listItem.setIndent(indent);
|
|
1308
|
+
};
|
|
1309
|
+
};
|
|
1310
|
+
function $indentOverTab(selection) {
|
|
1311
|
+
if ($filter(selection.getNodes(), (node) => {
|
|
1312
|
+
if ($isBlockElementNode(node) && node.canIndent()) return node;
|
|
1313
|
+
return null;
|
|
1314
|
+
}).length > 0) return true;
|
|
1315
|
+
const anchor = selection.anchor;
|
|
1316
|
+
const focus = selection.focus;
|
|
1317
|
+
const first = focus.isBefore(anchor) ? focus : anchor;
|
|
1318
|
+
const firstBlock = $getNearestBlockElementAncestorOrThrow(first.getNode());
|
|
1319
|
+
if (firstBlock.canIndent()) {
|
|
1320
|
+
const firstBlockKey = firstBlock.getKey();
|
|
1321
|
+
let selectionAtStart = $createRangeSelection();
|
|
1322
|
+
selectionAtStart.anchor.set(firstBlockKey, 0, "element");
|
|
1323
|
+
selectionAtStart.focus.set(firstBlockKey, 0, "element");
|
|
1324
|
+
selectionAtStart = $normalizeSelection__EXPERIMENTAL(selectionAtStart);
|
|
1325
|
+
if (selectionAtStart.anchor.is(first)) return true;
|
|
1326
|
+
}
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
//#endregion
|
|
1330
|
+
//#region src/plugins/list/plugin/registry.ts
|
|
1331
|
+
init_hotkey();
|
|
1332
|
+
function registerListCommands(editor, kernel, options) {
|
|
1333
|
+
const { enableHotkey = true } = options || {};
|
|
1334
|
+
return mergeRegister(kernel.registerHotkey(HotkeyEnum.BulletList, () => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND$1, void 0), {
|
|
1335
|
+
enabled: enableHotkey,
|
|
1336
|
+
preventDefault: true,
|
|
1337
|
+
stopImmediatePropagation: true
|
|
1338
|
+
}), kernel.registerHotkey(HotkeyEnum.NumberList, () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND$1, void 0), {
|
|
1339
|
+
enabled: enableHotkey,
|
|
1340
|
+
preventDefault: true,
|
|
1341
|
+
stopImmediatePropagation: true
|
|
1342
|
+
}), editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
1343
|
+
const selection = $getSelection();
|
|
1344
|
+
if (!$isRangeSelection(selection)) return false;
|
|
1345
|
+
event.preventDefault();
|
|
1346
|
+
const command = $indentOverTab(selection) ? event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND : INSERT_TAB_COMMAND;
|
|
1347
|
+
return editor.dispatchCommand(command, void 0);
|
|
1348
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => {
|
|
1349
|
+
const selection = $getSelection();
|
|
1350
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) return false;
|
|
1351
|
+
const anchor = selection.anchor;
|
|
1352
|
+
if (anchor.offset !== 0) return false;
|
|
1353
|
+
const anchorNode = anchor.getNode();
|
|
1354
|
+
let listItemNode;
|
|
1355
|
+
if ($isListItemNode(anchorNode)) listItemNode = anchorNode;
|
|
1356
|
+
else if ($isTextNode(anchorNode)) {
|
|
1357
|
+
if (anchorNode.getPreviousSibling()) return false;
|
|
1358
|
+
const parent = anchorNode.getParentOrThrow();
|
|
1359
|
+
if (!$isListItemNode(parent)) return false;
|
|
1360
|
+
listItemNode = parent;
|
|
1361
|
+
}
|
|
1362
|
+
if (!listItemNode || !$isRootNode(listItemNode.getParent()?.getParent())) return false;
|
|
1363
|
+
const listNode = listItemNode.getParentOrThrow();
|
|
1364
|
+
queueMicrotask(() => {
|
|
1365
|
+
editor.update(() => {
|
|
1366
|
+
if (!listItemNode) return;
|
|
1367
|
+
let newlistNode;
|
|
1368
|
+
if (listItemNode.getPreviousSibling() === null) {
|
|
1369
|
+
listItemNode.replace($createParagraphNode(), true).select(0, 0);
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
let next = listItemNode.getNextSibling();
|
|
1373
|
+
if (next) newlistNode = $createListNode(listNode.getListType(), listItemNode.getValue());
|
|
1374
|
+
while (next && newlistNode) {
|
|
1375
|
+
next.remove();
|
|
1376
|
+
newlistNode.append(next);
|
|
1377
|
+
next = next.getNextSibling();
|
|
1378
|
+
}
|
|
1379
|
+
const p = listItemNode.replace($createParagraphNode(), true);
|
|
1380
|
+
p.remove();
|
|
1381
|
+
listNode.insertAfter(p);
|
|
1382
|
+
if (newlistNode) p.insertAfter(newlistNode);
|
|
1383
|
+
p.select(0, 0);
|
|
1384
|
+
});
|
|
1385
|
+
});
|
|
1386
|
+
event.stopImmediatePropagation();
|
|
1387
|
+
event.preventDefault();
|
|
1388
|
+
return true;
|
|
1389
|
+
}, COMMAND_PRIORITY_LOW));
|
|
1390
|
+
}
|
|
1391
|
+
//#endregion
|
|
1392
|
+
//#region src/plugins/list/plugin/index.ts
|
|
1393
|
+
init_helper();
|
|
1394
|
+
init_plugin();
|
|
1395
|
+
const ORDERED_LIST_REGEX = /^(\s*)(\d+)\.\s/;
|
|
1396
|
+
const UNORDERED_LIST_REGEX = /^(\s*)[*+-]\s/;
|
|
1397
|
+
const CHECK_LIST_REGEX = /^(\s*)(?:-\s)?\s?(\[(\s|x)?])\s/i;
|
|
1398
|
+
const ListPlugin = class extends KernelPlugin {
|
|
1399
|
+
static {
|
|
1400
|
+
this.pluginName = "ListPlugin";
|
|
1401
|
+
}
|
|
1402
|
+
constructor(kernel, config) {
|
|
1403
|
+
super();
|
|
1404
|
+
this.kernel = kernel;
|
|
1405
|
+
this.config = config;
|
|
1406
|
+
kernel.registerNodes([ListNode, ListItemNode]);
|
|
1407
|
+
kernel.registerThemes({ list: {
|
|
1408
|
+
checklist: "editor_listItemCheck",
|
|
1409
|
+
listitem: "editor_listItem",
|
|
1410
|
+
listitemChecked: "editor_listItemChecked",
|
|
1411
|
+
listitemUnchecked: "editor_listItemUnchecked",
|
|
1412
|
+
nested: { listitem: "editor_listItemNested" },
|
|
1413
|
+
ol: cx$1("editor_listOrdered", config?.theme),
|
|
1414
|
+
olDepth: [
|
|
1415
|
+
"editor_listOrdered dp1",
|
|
1416
|
+
"editor_listOrdered dp2",
|
|
1417
|
+
"editor_listOrdered dp3",
|
|
1418
|
+
"editor_listOrdered dp4",
|
|
1419
|
+
"editor_listOrdered dp5"
|
|
1420
|
+
],
|
|
1421
|
+
ul: cx$1("editor_listUnordered", config?.theme)
|
|
1422
|
+
} });
|
|
1423
|
+
}
|
|
1424
|
+
onInit(editor) {
|
|
1425
|
+
this.register(registerList(editor));
|
|
1426
|
+
this.register(registerCheckList(editor));
|
|
1427
|
+
this.register(registerListStrictIndentTransform(editor));
|
|
1428
|
+
this.register(registerListCommands(editor, this.kernel, { enableHotkey: this.config?.enableHotkey }));
|
|
1429
|
+
this.registerMarkdown();
|
|
1430
|
+
this.registerLiteXml();
|
|
1431
|
+
}
|
|
1432
|
+
registerLiteXml() {
|
|
1433
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
1434
|
+
if (!litexmlService) return;
|
|
1435
|
+
litexmlService.registerXMLWriter(ListNode.getType(), (node, ctx) => {
|
|
1436
|
+
if ($isListNode(node)) {
|
|
1437
|
+
const tagName = node.getListType() === "number" ? "ol" : "ul";
|
|
1438
|
+
const attributes = {};
|
|
1439
|
+
if (node.getListType() === "number" && node.getStart() !== 1) attributes.start = node.getStart().toString();
|
|
1440
|
+
return ctx.createXmlNode(tagName, attributes);
|
|
1441
|
+
}
|
|
1442
|
+
return false;
|
|
1443
|
+
});
|
|
1444
|
+
litexmlService.registerXMLWriter(ListItemNode.getType(), (node, ctx) => {
|
|
1445
|
+
if ($isListItemNode(node)) return ctx.createXmlNode("li");
|
|
1446
|
+
return false;
|
|
1447
|
+
});
|
|
1448
|
+
litexmlService.registerXMLReader("ol", (xmlNode, children) => {
|
|
1449
|
+
return INodeHelper.createElementNode("list", {
|
|
1450
|
+
children,
|
|
1451
|
+
direction: "ltr",
|
|
1452
|
+
format: "",
|
|
1453
|
+
indent: 0,
|
|
1454
|
+
listType: "number",
|
|
1455
|
+
start: xmlNode.getAttribute("start") ? parseInt(xmlNode.getAttribute("start"), 10) : 1,
|
|
1456
|
+
tag: "ol",
|
|
1457
|
+
version: 1
|
|
1458
|
+
});
|
|
1459
|
+
});
|
|
1460
|
+
litexmlService.registerXMLReader("ul", (xmlNode, children) => {
|
|
1461
|
+
return INodeHelper.createElementNode("list", {
|
|
1462
|
+
children,
|
|
1463
|
+
direction: "ltr",
|
|
1464
|
+
format: "",
|
|
1465
|
+
indent: 0,
|
|
1466
|
+
listType: "bullet",
|
|
1467
|
+
start: 1,
|
|
1468
|
+
tag: "ul",
|
|
1469
|
+
version: 1
|
|
1470
|
+
});
|
|
1471
|
+
});
|
|
1472
|
+
litexmlService.registerXMLReader("li", (xmlNode, children) => {
|
|
1473
|
+
return INodeHelper.createElementNode("listitem", {
|
|
1474
|
+
children,
|
|
1475
|
+
direction: "ltr",
|
|
1476
|
+
format: "",
|
|
1477
|
+
indent: 0,
|
|
1478
|
+
type: "listitem",
|
|
1479
|
+
version: 1
|
|
1480
|
+
});
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
registerMarkdown() {
|
|
1484
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
1485
|
+
if (!markdownService) return;
|
|
1486
|
+
markdownService.registerMarkdownShortCut({
|
|
1487
|
+
regExp: UNORDERED_LIST_REGEX,
|
|
1488
|
+
replace: listReplace("bullet"),
|
|
1489
|
+
type: "element"
|
|
1490
|
+
});
|
|
1491
|
+
markdownService.registerMarkdownShortCut({
|
|
1492
|
+
regExp: ORDERED_LIST_REGEX,
|
|
1493
|
+
replace: listReplace("number"),
|
|
1494
|
+
type: "element"
|
|
1495
|
+
});
|
|
1496
|
+
markdownService.registerMarkdownShortCut({
|
|
1497
|
+
regExp: CHECK_LIST_REGEX,
|
|
1498
|
+
replace: listReplace("check"),
|
|
1499
|
+
type: "element"
|
|
1500
|
+
});
|
|
1501
|
+
markdownService.registerMarkdownWriter(ListNode.getType(), (ctx, node) => {
|
|
1502
|
+
if ($isListNode(node)) ctx.wrap("", "\n");
|
|
1503
|
+
});
|
|
1504
|
+
const getLevel = (node) => {
|
|
1505
|
+
if (!node) return 0;
|
|
1506
|
+
if ($isRootNode(node.getParent())) return 0;
|
|
1507
|
+
const parent = node.getParent();
|
|
1508
|
+
if (!parent) return 0;
|
|
1509
|
+
return getLevel($getNearestNodeOfType(parent, ListNode)) + 1;
|
|
1510
|
+
};
|
|
1511
|
+
markdownService.registerMarkdownWriter(ListItemNode.getType(), (ctx, node) => {
|
|
1512
|
+
const parent = node.getParent();
|
|
1513
|
+
if ($isListItemNode(node) && $isListNode(parent)) {
|
|
1514
|
+
if ($isListNode(node.getFirstChild())) return;
|
|
1515
|
+
const level = getLevel(parent);
|
|
1516
|
+
const prefix = " ".repeat(level);
|
|
1517
|
+
switch (parent.getListType()) {
|
|
1518
|
+
case "bullet":
|
|
1519
|
+
ctx.wrap(prefix + "- ", "\n");
|
|
1520
|
+
break;
|
|
1521
|
+
case "number":
|
|
1522
|
+
ctx.wrap(`${prefix}${node.getValue()}. `, "\n");
|
|
1523
|
+
break;
|
|
1524
|
+
case "check":
|
|
1525
|
+
ctx.wrap(`${prefix}- [${node.getChecked() ? "x" : " "}] `, "\n");
|
|
1526
|
+
break;
|
|
1527
|
+
default: break;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
});
|
|
1531
|
+
markdownService.registerMarkdownReader("list", (node, children) => {
|
|
1532
|
+
const isCheck = node.children?.[0]?.type === "listItem" && typeof node.children?.[0]?.checked === "boolean";
|
|
1533
|
+
let start = node.start || 1;
|
|
1534
|
+
return INodeHelper.createElementNode("list", {
|
|
1535
|
+
children: children.map((v) => {
|
|
1536
|
+
if (v.type === "listitem") v.value = start++;
|
|
1537
|
+
return v;
|
|
1538
|
+
}),
|
|
1539
|
+
direction: "ltr",
|
|
1540
|
+
format: "",
|
|
1541
|
+
indent: 0,
|
|
1542
|
+
listType: isCheck ? "check" : node.ordered ? "number" : "bullet",
|
|
1543
|
+
start: node.start || 1,
|
|
1544
|
+
tag: node.ordered ? "ol" : "ul",
|
|
1545
|
+
version: 1
|
|
1546
|
+
});
|
|
1547
|
+
});
|
|
1548
|
+
markdownService.registerMarkdownReader("listItem", (node, children, index) => {
|
|
1549
|
+
return children.map((v) => {
|
|
1550
|
+
if (v.type === "paragraph") return INodeHelper.createElementNode("listitem", {
|
|
1551
|
+
checked: typeof node.checked === "boolean" ? node.checked : void 0,
|
|
1552
|
+
children: v.children,
|
|
1553
|
+
direction: "ltr",
|
|
1554
|
+
format: "",
|
|
1555
|
+
indent: 0,
|
|
1556
|
+
type: "listitem",
|
|
1557
|
+
value: index + 1,
|
|
1558
|
+
version: 1
|
|
1559
|
+
});
|
|
1560
|
+
else if (v.type === "list") return INodeHelper.createElementNode("listitem", {
|
|
1561
|
+
children: [v],
|
|
1562
|
+
direction: "ltr",
|
|
1563
|
+
format: "",
|
|
1564
|
+
indent: 0,
|
|
1565
|
+
type: "listitem",
|
|
1566
|
+
value: index + 1,
|
|
1567
|
+
version: 1
|
|
1568
|
+
});
|
|
1569
|
+
return v;
|
|
1570
|
+
});
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
//#endregion
|
|
1575
|
+
//#region src/plugins/table/command/index.ts
|
|
1576
|
+
const INSERT_TABLE_COMMAND = createCommand();
|
|
1577
|
+
const SELECT_TABLE_COMMAND = createCommand();
|
|
1578
|
+
function registerTableCommand(editor) {
|
|
1579
|
+
return mergeRegister(editor.registerCommand(INSERT_TABLE_COMMAND, ({ rows, columns, includeHeaders }) => {
|
|
1580
|
+
const selection = $getSelection() || $getPreviousSelection();
|
|
1581
|
+
if (!selection || !$isRangeSelection(selection)) return false;
|
|
1582
|
+
if ($findTableNode(selection.anchor.getNode())) return false;
|
|
1583
|
+
const anchorNode = selection.anchor.getNode();
|
|
1584
|
+
const tableNode = $createTableNodeWithDimensions(Number(rows), Number(columns), includeHeaders);
|
|
1585
|
+
if ($isElementNode(anchorNode) && anchorNode.isEmpty()) anchorNode.replace(tableNode);
|
|
1586
|
+
else $insertNodeToNearestRoot(tableNode);
|
|
1587
|
+
const firstDescendant = tableNode.getFirstDescendant();
|
|
1588
|
+
if ($isTextNode(firstDescendant)) firstDescendant.select();
|
|
1589
|
+
return true;
|
|
1590
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(SELECT_TABLE_COMMAND, ({ table, columnIndex, rowIndex }) => {
|
|
1591
|
+
editor.update(() => {
|
|
1592
|
+
const prevSelection = $getSelection();
|
|
1593
|
+
const tableNode = $getNodeByKey(table);
|
|
1594
|
+
if (!tableNode || !$isTableNode(tableNode)) return;
|
|
1595
|
+
const tableSelection = $isTableSelection(prevSelection) ? prevSelection : $createTableSelection();
|
|
1596
|
+
if (rowIndex !== void 0) {
|
|
1597
|
+
const firstRow = tableNode.getChildren()[rowIndex];
|
|
1598
|
+
if (!firstRow) return;
|
|
1599
|
+
const firstCell = firstRow.getFirstChild();
|
|
1600
|
+
const lastCell = firstRow.getLastChild();
|
|
1601
|
+
if (!firstCell || !lastCell) return;
|
|
1602
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
1603
|
+
$setSelection(tableSelection);
|
|
1604
|
+
} else if (columnIndex !== void 0) {
|
|
1605
|
+
const firstRow = tableNode.getFirstChild();
|
|
1606
|
+
const lastRow = tableNode.getLastChild();
|
|
1607
|
+
if (!firstRow || !lastRow) return;
|
|
1608
|
+
const firstCell = firstRow.getChildren()[columnIndex];
|
|
1609
|
+
const lastCell = lastRow.getChildren()[columnIndex];
|
|
1610
|
+
if (!firstCell || !lastCell) return;
|
|
1611
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
1612
|
+
$setSelection(tableSelection);
|
|
1613
|
+
} else {
|
|
1614
|
+
const firstRow = tableNode.getFirstChild();
|
|
1615
|
+
const lastRow = tableNode.getLastChild();
|
|
1616
|
+
if (!firstRow || !lastRow) return;
|
|
1617
|
+
const firstCell = firstRow.getFirstChild();
|
|
1618
|
+
const lastCell = lastRow.getLastChild();
|
|
1619
|
+
if (!firstCell || !lastCell) return;
|
|
1620
|
+
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
1621
|
+
$setSelection(tableSelection);
|
|
1622
|
+
}
|
|
1623
|
+
});
|
|
1624
|
+
return false;
|
|
1625
|
+
}, COMMAND_PRIORITY_EDITOR));
|
|
1626
|
+
}
|
|
1627
|
+
//#endregion
|
|
1628
|
+
//#region src/plugins/table/node/index.ts
|
|
1629
|
+
const OriginalCreateDOM = TableNode.prototype.createDOM;
|
|
1630
|
+
function patchTableNode() {
|
|
1631
|
+
Object.defineProperty(TableNode.prototype, "createDOM", {
|
|
1632
|
+
configurable: true,
|
|
1633
|
+
enumerable: false,
|
|
1634
|
+
value: function(config, editor) {
|
|
1635
|
+
const table = OriginalCreateDOM.call(this, config, editor);
|
|
1636
|
+
const controller = document.createElement("div");
|
|
1637
|
+
table.append(controller);
|
|
1638
|
+
return table;
|
|
1639
|
+
},
|
|
1640
|
+
writable: true
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
//#endregion
|
|
1644
|
+
//#region src/plugins/table/plugin/index.ts
|
|
1645
|
+
init_helper();
|
|
1646
|
+
init_plugin();
|
|
1647
|
+
const tableCellProcessor = (before, content, after) => {
|
|
1648
|
+
return before + content.replace(/\n+$/, "").replaceAll(/\n+/g, "<br />") + after;
|
|
1649
|
+
};
|
|
1650
|
+
function isHeadlessEditor(editor) {
|
|
1651
|
+
return editor._headless === true;
|
|
1652
|
+
}
|
|
1653
|
+
const TablePlugin = class extends KernelPlugin {
|
|
1654
|
+
static {
|
|
1655
|
+
this.pluginName = "TablePlugin";
|
|
1656
|
+
}
|
|
1657
|
+
constructor(kernel, options) {
|
|
1658
|
+
super();
|
|
1659
|
+
this.kernel = kernel;
|
|
1660
|
+
patchTableNode();
|
|
1661
|
+
kernel.registerNodes([
|
|
1662
|
+
TableNode$1,
|
|
1663
|
+
TableRowNode,
|
|
1664
|
+
TableCellNode
|
|
1665
|
+
]);
|
|
1666
|
+
kernel.registerThemes({
|
|
1667
|
+
table: "editor_table",
|
|
1668
|
+
tableCell: "editor_table_cell",
|
|
1669
|
+
tableCellHeader: "editor_table_cell_header",
|
|
1670
|
+
tableCellSelected: "editor_table_cell_selected",
|
|
1671
|
+
tableScrollableWrapper: cx$1("editor_table_scrollable_wrapper", options?.theme)
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
onInit(editor) {
|
|
1675
|
+
this.register(registerTableCellUnmergeTransform(editor));
|
|
1676
|
+
if (!isHeadlessEditor(editor)) {
|
|
1677
|
+
setScrollableTablesActive(editor, true);
|
|
1678
|
+
this.register(registerTablePlugin(editor));
|
|
1679
|
+
this.register(registerTableSelectionObserver(editor));
|
|
1680
|
+
this.register(registerTableCommand(editor));
|
|
1681
|
+
}
|
|
1682
|
+
this.registerMarkdown();
|
|
1683
|
+
this.registerLiteXml();
|
|
1684
|
+
}
|
|
1685
|
+
registerLiteXml() {
|
|
1686
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
1687
|
+
if (!litexmlService) return;
|
|
1688
|
+
litexmlService.registerXMLWriter(TableNode$1.getType(), (node, ctx) => {
|
|
1689
|
+
if ($isTableNode(node)) {
|
|
1690
|
+
const attributes = {};
|
|
1691
|
+
const colWidths = node.getColWidths();
|
|
1692
|
+
if (colWidths && colWidths.length > 0) attributes.colWidths = colWidths.join(",");
|
|
1693
|
+
return ctx.createXmlNode("table", attributes);
|
|
1694
|
+
}
|
|
1695
|
+
return false;
|
|
1696
|
+
});
|
|
1697
|
+
litexmlService.registerXMLWriter(TableRowNode.getType(), (node, ctx) => {
|
|
1698
|
+
if (node instanceof TableRowNode) return ctx.createXmlNode("tr", {});
|
|
1699
|
+
return false;
|
|
1700
|
+
});
|
|
1701
|
+
litexmlService.registerXMLWriter(TableCellNode.getType(), (node, ctx) => {
|
|
1702
|
+
if (node instanceof TableCellNode) {
|
|
1703
|
+
const attributes = {};
|
|
1704
|
+
if (node.getColSpan() > 1) attributes.colSpan = node.getColSpan().toString();
|
|
1705
|
+
if (node.getRowSpan() > 1) attributes.rowSpan = node.getRowSpan().toString();
|
|
1706
|
+
if (node.getBackgroundColor()) attributes.backgroundColor = node.getBackgroundColor();
|
|
1707
|
+
return ctx.createXmlNode("td", attributes);
|
|
1708
|
+
}
|
|
1709
|
+
return false;
|
|
1710
|
+
});
|
|
1711
|
+
litexmlService.registerXMLReader("table", (xmlNode, children) => {
|
|
1712
|
+
const colWidthsAttr = xmlNode.getAttribute("colWidths");
|
|
1713
|
+
const colWidths = colWidthsAttr ? colWidthsAttr.split(",").map((width) => parseInt(width, 10)) : [];
|
|
1714
|
+
let maxTdlen = 1;
|
|
1715
|
+
for (const child of children) if ((child.children?.length || -1) > maxTdlen) maxTdlen = child.children.length;
|
|
1716
|
+
return INodeHelper.createElementNode(TableNode$1.getType(), {
|
|
1717
|
+
children,
|
|
1718
|
+
colWidths: colWidths.length > 0 ? colWidths : new Array(maxTdlen).fill(750 / maxTdlen),
|
|
1719
|
+
direction: null,
|
|
1720
|
+
format: "",
|
|
1721
|
+
indent: 0,
|
|
1722
|
+
version: 1
|
|
1723
|
+
});
|
|
1724
|
+
});
|
|
1725
|
+
litexmlService.registerXMLReader("tr", (_xmlNode, children) => {
|
|
1726
|
+
return INodeHelper.createElementNode(TableRowNode.getType(), {
|
|
1727
|
+
children,
|
|
1728
|
+
direction: "ltr",
|
|
1729
|
+
format: "",
|
|
1730
|
+
height: 33,
|
|
1731
|
+
indent: 0,
|
|
1732
|
+
version: 1
|
|
1733
|
+
});
|
|
1734
|
+
});
|
|
1735
|
+
const tdReader = (xmlNode, children) => {
|
|
1736
|
+
return INodeHelper.createElementNode(TableCellNode.getType(), {
|
|
1737
|
+
backgroundColor: xmlNode.getAttribute("backgroundColor") || null,
|
|
1738
|
+
children,
|
|
1739
|
+
colSpan: xmlNode.getAttribute("colSpan") ? parseInt(xmlNode.getAttribute("colSpan"), 10) : 1,
|
|
1740
|
+
direction: "ltr",
|
|
1741
|
+
format: "",
|
|
1742
|
+
headerState: 0,
|
|
1743
|
+
indent: 0,
|
|
1744
|
+
rowSpan: xmlNode.getAttribute("rowSpan") ? parseInt(xmlNode.getAttribute("rowSpan"), 10) : 1,
|
|
1745
|
+
version: 1
|
|
1746
|
+
});
|
|
1747
|
+
};
|
|
1748
|
+
litexmlService.registerXMLReader("th", tdReader);
|
|
1749
|
+
litexmlService.registerXMLReader("td", tdReader);
|
|
1750
|
+
}
|
|
1751
|
+
registerMarkdown() {
|
|
1752
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
1753
|
+
if (!markdownService) return;
|
|
1754
|
+
markdownService.registerMarkdownWriter(TableNode$1.getType(), (ctx) => {
|
|
1755
|
+
ctx.wrap("", "\n");
|
|
1756
|
+
});
|
|
1757
|
+
markdownService.registerMarkdownWriter(TableRowNode.getType(), (ctx, node) => {
|
|
1758
|
+
const parent = node.getParent();
|
|
1759
|
+
if (!$isTableNode(parent)) return;
|
|
1760
|
+
if (!node.getPreviousSibling()) ctx.wrap("", `\n${parent.getColWidths()?.map(() => {
|
|
1761
|
+
return "|:--";
|
|
1762
|
+
}).join("")}|\n`);
|
|
1763
|
+
else ctx.wrap("", "\n");
|
|
1764
|
+
});
|
|
1765
|
+
markdownService.registerMarkdownWriter(TableCellNode.getType(), (ctx, node) => {
|
|
1766
|
+
ctx.addProcessor(tableCellProcessor);
|
|
1767
|
+
if (!node.getNextSibling()) ctx.wrap("|", "|");
|
|
1768
|
+
else ctx.wrap("|", "");
|
|
1769
|
+
});
|
|
1770
|
+
markdownService.registerMarkdownReader("table", (node, children) => {
|
|
1771
|
+
const colLen = node.children[0]?.children.length || 1;
|
|
1772
|
+
return INodeHelper.createElementNode("table", {
|
|
1773
|
+
children,
|
|
1774
|
+
colWidths: new Array(colLen).fill(750 / colLen),
|
|
1775
|
+
direction: null,
|
|
1776
|
+
format: "",
|
|
1777
|
+
indent: 0,
|
|
1778
|
+
version: 1
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
1781
|
+
markdownService.registerMarkdownReader("tableRow", (_node, children) => {
|
|
1782
|
+
return INodeHelper.createElementNode("tablerow", {
|
|
1783
|
+
children,
|
|
1784
|
+
direction: "ltr",
|
|
1785
|
+
format: "",
|
|
1786
|
+
height: 33,
|
|
1787
|
+
indent: 0,
|
|
1788
|
+
version: 1
|
|
1789
|
+
});
|
|
1790
|
+
});
|
|
1791
|
+
markdownService.registerMarkdownReader("tableCell", (_node, children) => {
|
|
1792
|
+
return INodeHelper.createElementNode("tablecell", {
|
|
1793
|
+
backgroundColor: null,
|
|
1794
|
+
children,
|
|
1795
|
+
colSpan: 1,
|
|
1796
|
+
direction: "ltr",
|
|
1797
|
+
format: "",
|
|
1798
|
+
headerState: 0,
|
|
1799
|
+
indent: 0,
|
|
1800
|
+
rowSpan: 1,
|
|
1801
|
+
version: 1
|
|
1802
|
+
});
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
//#endregion
|
|
1807
|
+
//#region src/headless/plugins/codeblock.ts
|
|
1808
|
+
init_helper();
|
|
1809
|
+
init_plugin();
|
|
1810
|
+
const createCodeChildren = (code) => code.split("\n").flatMap((text, index, array) => {
|
|
1811
|
+
const textNode = INodeHelper.createTextNode(text);
|
|
1812
|
+
textNode.type = "code-highlight";
|
|
1813
|
+
if (index === array.length - 1) return textNode;
|
|
1814
|
+
return [textNode, {
|
|
1815
|
+
type: "linebreak",
|
|
1816
|
+
version: 1
|
|
1817
|
+
}];
|
|
1818
|
+
}).flat();
|
|
1819
|
+
const HeadlessCodeblockPlugin = class extends KernelPlugin {
|
|
1820
|
+
static {
|
|
1821
|
+
this.pluginName = "HeadlessCodeblockPlugin";
|
|
1822
|
+
}
|
|
1823
|
+
constructor(kernel) {
|
|
1824
|
+
super();
|
|
1825
|
+
this.kernel = kernel;
|
|
1826
|
+
kernel.registerNodes([CodeNode, CodeHighlightNode]);
|
|
1827
|
+
kernel.registerThemes({ code: "editor-code" });
|
|
1828
|
+
}
|
|
1829
|
+
onInit() {
|
|
1830
|
+
this.registerMarkdown();
|
|
1831
|
+
this.registerLiteXml();
|
|
1832
|
+
}
|
|
1833
|
+
registerLiteXml() {
|
|
1834
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
1835
|
+
if (!litexmlService) return;
|
|
1836
|
+
litexmlService.registerXMLWriter(CodeNode.getType(), (node, ctx) => {
|
|
1837
|
+
const codeNode = node;
|
|
1838
|
+
return ctx.createXmlNode("code", { lang: codeNode.getLanguage() || "plaintext" }, codeNode.getTextContent());
|
|
1839
|
+
});
|
|
1840
|
+
litexmlService.registerXMLReader("code", (xmlElement, children) => {
|
|
1841
|
+
const text = children.map((value) => value.text || "").join("");
|
|
1842
|
+
return INodeHelper.createElementNode(CodeNode.getType(), {
|
|
1843
|
+
children: createCodeChildren(text),
|
|
1844
|
+
direction: "ltr",
|
|
1845
|
+
format: "",
|
|
1846
|
+
indent: 0,
|
|
1847
|
+
language: xmlElement.getAttribute("lang"),
|
|
1848
|
+
version: 1
|
|
1849
|
+
});
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
registerMarkdown() {
|
|
1853
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
1854
|
+
if (!markdownService) return;
|
|
1855
|
+
markdownService.registerMarkdownWriter(CodeNode.getType(), (ctx, node) => {
|
|
1856
|
+
if ($isCodeNode(node)) ctx.wrap("```" + (node.getLanguage() || "") + "\n", "\n```\n");
|
|
1857
|
+
});
|
|
1858
|
+
markdownService.registerMarkdownWriter(TabNode.getType(), (ctx) => {
|
|
1859
|
+
ctx.appendLine(" ");
|
|
1860
|
+
});
|
|
1861
|
+
markdownService.registerMarkdownWriter(CodeHighlightNode.getType(), (ctx, node) => {
|
|
1862
|
+
if ($isCodeHighlightNode(node)) ctx.appendLine(node.getTextContent());
|
|
1863
|
+
});
|
|
1864
|
+
markdownService.registerMarkdownWriter("linebreak", (ctx, node) => {
|
|
1865
|
+
if ($isCodeNode(node.getParent())) ctx.appendLine("\n");
|
|
1866
|
+
});
|
|
1867
|
+
markdownService.registerMarkdownReader("code", (node) => {
|
|
1868
|
+
const language = node.lang ? getCodeLanguageByInput(node.lang) : "plaintext";
|
|
1869
|
+
return INodeHelper.createElementNode("code", {
|
|
1870
|
+
children: createCodeChildren(node.value),
|
|
1871
|
+
direction: "ltr",
|
|
1872
|
+
format: "",
|
|
1873
|
+
indent: 0,
|
|
1874
|
+
language,
|
|
1875
|
+
version: 1
|
|
1876
|
+
});
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
//#endregion
|
|
1881
|
+
//#region src/headless/index.ts
|
|
1882
|
+
const getNumericId = (id) => {
|
|
1883
|
+
if (typeof id !== "number" && typeof id !== "string") return null;
|
|
1884
|
+
const numericId = Number(id);
|
|
1885
|
+
return Number.isInteger(numericId) && numericId >= 0 ? numericId : null;
|
|
1886
|
+
};
|
|
1887
|
+
const findMaxSerializedId = (node) => {
|
|
1888
|
+
if (!node || typeof node !== "object") return -1;
|
|
1889
|
+
const record = node;
|
|
1890
|
+
const ownMax = getNumericId(record.id) ?? -1;
|
|
1891
|
+
if (!Array.isArray(record.children)) return ownMax;
|
|
1892
|
+
return record.children.reduce((maxId, child) => Math.max(maxId, findMaxSerializedId(child)), ownMax);
|
|
1893
|
+
};
|
|
1894
|
+
const createSerializedId = (context) => String(context.nextId++);
|
|
1895
|
+
const createCodeChildrenFromLegacyCode = (code, context) => code.split("\n").flatMap((text, index, array) => {
|
|
1896
|
+
const textNode = {
|
|
1897
|
+
detail: 0,
|
|
1898
|
+
format: 0,
|
|
1899
|
+
id: createSerializedId(context),
|
|
1900
|
+
mode: "normal",
|
|
1901
|
+
style: "",
|
|
1902
|
+
text,
|
|
1903
|
+
type: "code-highlight",
|
|
1904
|
+
version: 1
|
|
1905
|
+
};
|
|
1906
|
+
if (index === array.length - 1) return textNode;
|
|
1907
|
+
return [textNode, {
|
|
1908
|
+
id: createSerializedId(context),
|
|
1909
|
+
type: "linebreak",
|
|
1910
|
+
version: 1
|
|
1911
|
+
}];
|
|
1912
|
+
});
|
|
1913
|
+
const normalizeLegacyEditorDataNode = (node, context) => {
|
|
1914
|
+
if (!node || typeof node !== "object") return node;
|
|
1915
|
+
const record = node;
|
|
1916
|
+
const children = Array.isArray(record.children) ? record.children.map((child) => normalizeLegacyEditorDataNode(child, context)) : record.children;
|
|
1917
|
+
if (record.type === "code" && typeof record.code === "string" && !Array.isArray(children)) return {
|
|
1918
|
+
...record,
|
|
1919
|
+
children: createCodeChildrenFromLegacyCode(record.code, context),
|
|
1920
|
+
direction: record.direction ?? "ltr",
|
|
1921
|
+
format: record.format ?? "",
|
|
1922
|
+
indent: record.indent ?? 0,
|
|
1923
|
+
language: record.language ?? "plaintext",
|
|
1924
|
+
theme: record.theme ?? record.codeTheme
|
|
1925
|
+
};
|
|
1926
|
+
if (Array.isArray(children)) return {
|
|
1927
|
+
...record,
|
|
1928
|
+
children
|
|
1929
|
+
};
|
|
1930
|
+
return record;
|
|
1931
|
+
};
|
|
1932
|
+
const normalizeLegacyEditorData = (editorData) => {
|
|
1933
|
+
const data = typeof editorData === "string" ? JSON.parse(editorData) : editorData;
|
|
1934
|
+
const context = { nextId: findMaxSerializedId(data.root) + 1 };
|
|
1935
|
+
return {
|
|
1936
|
+
...data,
|
|
1937
|
+
root: normalizeLegacyEditorDataNode(data.root, context)
|
|
1938
|
+
};
|
|
1939
|
+
};
|
|
1940
|
+
const DEFAULT_HEADLESS_EDITOR_PLUGINS = [
|
|
1941
|
+
[CommonPlugin, { enableHotkey: false }],
|
|
1942
|
+
MarkdownPlugin,
|
|
1943
|
+
CodePlugin,
|
|
1944
|
+
HeadlessCodeblockPlugin,
|
|
1945
|
+
HRPlugin,
|
|
1946
|
+
ListPlugin,
|
|
1947
|
+
TablePlugin,
|
|
1948
|
+
LitexmlPlugin
|
|
1949
|
+
];
|
|
1950
|
+
var HeadlessEditor = class {
|
|
1951
|
+
constructor(options = {}) {
|
|
1952
|
+
this.kernel = Editor.createEditor();
|
|
1953
|
+
const plugins = [...options.plugins ?? DEFAULT_HEADLESS_EDITOR_PLUGINS, ...options.additionalPlugins ?? []];
|
|
1954
|
+
this.kernel.registerPlugins(plugins);
|
|
1955
|
+
this.kernel.initHeadlessEditor();
|
|
1956
|
+
if (options.initialValue) this.hydrate(options.initialValue);
|
|
1957
|
+
}
|
|
1958
|
+
hydrate(input) {
|
|
1959
|
+
this.kernel.setDocument(input.type, input.content, input.options);
|
|
1960
|
+
return this;
|
|
1961
|
+
}
|
|
1962
|
+
hydrateEditorData(editorData, options) {
|
|
1963
|
+
this.kernel.setDocument("json", normalizeLegacyEditorData(editorData), options);
|
|
1964
|
+
return this;
|
|
1965
|
+
}
|
|
1966
|
+
hydrateLiteXML(litexml, options) {
|
|
1967
|
+
this.kernel.setDocument("litexml", litexml, options);
|
|
1968
|
+
return this;
|
|
1969
|
+
}
|
|
1970
|
+
hydrateMarkdown(markdown, options) {
|
|
1971
|
+
this.kernel.setDocument("markdown", markdown, options);
|
|
1972
|
+
return this;
|
|
1973
|
+
}
|
|
1974
|
+
async applyLiteXML(operation) {
|
|
1975
|
+
const operations = Array.isArray(operation) ? operation : [operation];
|
|
1976
|
+
for (const item of operations) this.applyLiteXMLOperation(item);
|
|
1977
|
+
await moment();
|
|
1978
|
+
return this;
|
|
1979
|
+
}
|
|
1980
|
+
async applyLiteXMLBatch(operations) {
|
|
1981
|
+
this.kernel.dispatchCommand(LITEXML_MODIFY_COMMAND, operations);
|
|
1982
|
+
await moment();
|
|
1983
|
+
return this;
|
|
1984
|
+
}
|
|
1985
|
+
export(options = {}) {
|
|
1986
|
+
const snapshot = {
|
|
1987
|
+
editorData: this.kernel.getDocument("json"),
|
|
1988
|
+
markdown: this.kernel.getDocument("markdown")
|
|
1989
|
+
};
|
|
1990
|
+
if (options.litexml) snapshot.litexml = this.kernel.getDocument("litexml");
|
|
1991
|
+
return snapshot;
|
|
1992
|
+
}
|
|
1993
|
+
exportState(options) {
|
|
1994
|
+
return this.export(options);
|
|
1995
|
+
}
|
|
1996
|
+
destroy() {
|
|
1997
|
+
this.kernel.destroy();
|
|
1998
|
+
}
|
|
1999
|
+
applyLiteXMLOperation(operation) {
|
|
2000
|
+
switch (operation.action) {
|
|
2001
|
+
case "apply":
|
|
2002
|
+
case "replace":
|
|
2003
|
+
this.kernel.dispatchCommand(LITEXML_APPLY_COMMAND, {
|
|
2004
|
+
delay: operation.delay,
|
|
2005
|
+
litexml: operation.litexml
|
|
2006
|
+
});
|
|
2007
|
+
return;
|
|
2008
|
+
case "batch":
|
|
2009
|
+
this.kernel.dispatchCommand(LITEXML_MODIFY_COMMAND, operation.operations);
|
|
2010
|
+
return;
|
|
2011
|
+
case "insert":
|
|
2012
|
+
this.kernel.dispatchCommand(LITEXML_INSERT_COMMAND, operation);
|
|
2013
|
+
return;
|
|
2014
|
+
case "remove":
|
|
2015
|
+
this.kernel.dispatchCommand(LITEXML_REMOVE_COMMAND, {
|
|
2016
|
+
delay: operation.delay,
|
|
2017
|
+
id: operation.id
|
|
2018
|
+
});
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
function createHeadlessEditor(options) {
|
|
2024
|
+
return new HeadlessEditor(options);
|
|
2025
|
+
}
|
|
2026
|
+
//#endregion
|
|
2027
|
+
//#region src/plugins/auto-complete/plugin/index.ts
|
|
2028
|
+
init_helper();
|
|
2029
|
+
init_plugin();
|
|
2030
|
+
init_debug();
|
|
2031
|
+
const AutoCompletePlugin = class extends KernelPlugin {
|
|
2032
|
+
static {
|
|
2033
|
+
this.pluginName = "AutoCompletePlugin";
|
|
2034
|
+
}
|
|
2035
|
+
constructor(kernel, config) {
|
|
2036
|
+
super();
|
|
2037
|
+
this.kernel = kernel;
|
|
2038
|
+
this.config = config;
|
|
2039
|
+
this.logger = createDebugLogger("plugin", "auto-complete");
|
|
2040
|
+
this.lastCursorPosition = null;
|
|
2041
|
+
this.cursorStableTimer = null;
|
|
2042
|
+
this.abortController = null;
|
|
2043
|
+
this.placeholderNodes = [];
|
|
2044
|
+
this.currentSuggestion = null;
|
|
2045
|
+
this.markdownService = null;
|
|
2046
|
+
this.delay = config?.delay ?? 1e3;
|
|
2047
|
+
kernel.registerNodes([PlaceholderNode, PlaceholderBlockNode]);
|
|
2048
|
+
if (config?.theme) kernel.registerThemes(config?.theme);
|
|
2049
|
+
}
|
|
2050
|
+
onInit(editor) {
|
|
2051
|
+
this.markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
2052
|
+
if (!this.markdownService) {
|
|
2053
|
+
this.logger.error("❌ MarkdownShortCutService is required for AutoCompletePlugin");
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
this.register(editor.registerUpdateListener(({ editorState }) => {
|
|
2057
|
+
editorState.read(() => {
|
|
2058
|
+
if (editor.isComposing()) return;
|
|
2059
|
+
const selection = $getSelection();
|
|
2060
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
2061
|
+
this.clearTimer();
|
|
2062
|
+
this.clearPlaceholderNodes(editor);
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
const currentPosition = {
|
|
2066
|
+
key: selection.anchor.key,
|
|
2067
|
+
offset: selection.anchor.offset,
|
|
2068
|
+
type: selection.anchor.type
|
|
2069
|
+
};
|
|
2070
|
+
if (this.hasPositionChanged(currentPosition)) {
|
|
2071
|
+
this.clearTimer();
|
|
2072
|
+
this.clearPlaceholderNodes(editor);
|
|
2073
|
+
this.abortController = new AbortController();
|
|
2074
|
+
this.cursorStableTimer = window.setTimeout(() => {
|
|
2075
|
+
this.handleCursorStable(editor, currentPosition);
|
|
2076
|
+
}, this.delay);
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
}));
|
|
2080
|
+
this.register(editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
2081
|
+
if (this.currentSuggestion) {
|
|
2082
|
+
event?.preventDefault();
|
|
2083
|
+
this.applySuggestion(editor);
|
|
2084
|
+
return true;
|
|
2085
|
+
}
|
|
2086
|
+
return false;
|
|
2087
|
+
}, COMMAND_PRIORITY_HIGH));
|
|
2088
|
+
this.register(editor.registerCommand(KEY_ESCAPE_COMMAND, (event) => {
|
|
2089
|
+
if (this.currentSuggestion) {
|
|
2090
|
+
event?.preventDefault();
|
|
2091
|
+
this.clearPlaceholderNodes(editor);
|
|
2092
|
+
this.currentSuggestion = null;
|
|
2093
|
+
return true;
|
|
2094
|
+
}
|
|
2095
|
+
return false;
|
|
2096
|
+
}, COMMAND_PRIORITY_CRITICAL));
|
|
2097
|
+
this.register(editor.registerTextContentListener(() => {
|
|
2098
|
+
if (this.placeholderNodes.length > 0) this.clearPlaceholderNodes(editor);
|
|
2099
|
+
}));
|
|
2100
|
+
}
|
|
2101
|
+
hasPositionChanged(currentPosition) {
|
|
2102
|
+
if (!this.lastCursorPosition) return true;
|
|
2103
|
+
return this.lastCursorPosition.key !== currentPosition.key || this.lastCursorPosition.offset !== currentPosition.offset || this.lastCursorPosition.type !== currentPosition.type;
|
|
2104
|
+
}
|
|
2105
|
+
clearTimer() {
|
|
2106
|
+
this.abortController?.abort("use cancel");
|
|
2107
|
+
if (this.cursorStableTimer) {
|
|
2108
|
+
clearTimeout(this.cursorStableTimer);
|
|
2109
|
+
this.cursorStableTimer = null;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
handleCursorStable(editor, position) {
|
|
2113
|
+
editor.getEditorState().read(() => {
|
|
2114
|
+
if (editor.isComposing()) return;
|
|
2115
|
+
if (!this.abortController || this.abortController.signal.aborted) return;
|
|
2116
|
+
const selection = $getSelection();
|
|
2117
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) return;
|
|
2118
|
+
const currentPosition = {
|
|
2119
|
+
key: selection.anchor.key,
|
|
2120
|
+
offset: selection.anchor.offset,
|
|
2121
|
+
type: selection.anchor.type
|
|
2122
|
+
};
|
|
2123
|
+
if (!this.isSamePosition(position, currentPosition)) return;
|
|
2124
|
+
if (this.lastCursorPosition && this.isSamePosition(position, this.lastCursorPosition)) return;
|
|
2125
|
+
this.lastCursorPosition = currentPosition;
|
|
2126
|
+
const anchorNode = selection.anchor.getNode();
|
|
2127
|
+
let selectionType = "unknown";
|
|
2128
|
+
const textRet = this.getTextBeforeCursor(selection);
|
|
2129
|
+
selectionType = anchorNode.getType();
|
|
2130
|
+
if (this.config?.onAutoComplete) this.config.onAutoComplete({
|
|
2131
|
+
abortSignal: this.abortController.signal,
|
|
2132
|
+
afterText: textRet.textAfter,
|
|
2133
|
+
editor: this.kernel,
|
|
2134
|
+
input: textRet.textBefore,
|
|
2135
|
+
selectionType
|
|
2136
|
+
}).then((result) => {
|
|
2137
|
+
let currentSelection = null;
|
|
2138
|
+
editor.getEditorState().read(() => {
|
|
2139
|
+
currentSelection = $getSelection();
|
|
2140
|
+
});
|
|
2141
|
+
if (editor.isComposing()) {
|
|
2142
|
+
this.clearPlaceholderNodes(editor);
|
|
2143
|
+
return;
|
|
2144
|
+
}
|
|
2145
|
+
if (!currentSelection || !$isRangeSelection(currentSelection) || !currentSelection.isCollapsed()) {
|
|
2146
|
+
this.clearPlaceholderNodes(editor);
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
const newPosition = {
|
|
2150
|
+
key: currentSelection.anchor.key,
|
|
2151
|
+
offset: currentSelection.anchor.offset,
|
|
2152
|
+
type: currentSelection.anchor.type
|
|
2153
|
+
};
|
|
2154
|
+
if (!this.isSamePosition(currentPosition, newPosition)) {
|
|
2155
|
+
this.clearPlaceholderNodes(editor);
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
if (result) {
|
|
2159
|
+
this.currentSuggestion = result;
|
|
2160
|
+
this.showPlaceholderNodes(editor, result);
|
|
2161
|
+
this.logger.debug("🔍 Auto-complete triggered:", {
|
|
2162
|
+
afterText: textRet.textAfter,
|
|
2163
|
+
input: textRet.textBefore,
|
|
2164
|
+
position: currentPosition,
|
|
2165
|
+
result,
|
|
2166
|
+
selectionType
|
|
2167
|
+
});
|
|
2168
|
+
} else this.clearPlaceholderNodes(editor);
|
|
2169
|
+
});
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
getTextBeforeCursor(selection) {
|
|
2173
|
+
const ret = {
|
|
2174
|
+
textAfter: "",
|
|
2175
|
+
textBefore: ""
|
|
2176
|
+
};
|
|
2177
|
+
const anchorNode = selection.anchor.getNode();
|
|
2178
|
+
const anchorOffset = selection.anchor.offset;
|
|
2179
|
+
let paragraphNode = anchorNode;
|
|
2180
|
+
while (paragraphNode && paragraphNode.isInline()) {
|
|
2181
|
+
const parent = paragraphNode.getParent();
|
|
2182
|
+
if (!parent) break;
|
|
2183
|
+
paragraphNode = parent;
|
|
2184
|
+
}
|
|
2185
|
+
if (!paragraphNode) return ret;
|
|
2186
|
+
this.logger.debug("🔍 Paragraph Node Type:", paragraphNode, anchorNode);
|
|
2187
|
+
let founded = false;
|
|
2188
|
+
const collectTextBeforeCursor = (node, targetNode, targetOffset) => {
|
|
2189
|
+
if (node === targetNode) {
|
|
2190
|
+
founded = true;
|
|
2191
|
+
if (node.getTextContent) {
|
|
2192
|
+
const nodeText = node.getTextContent();
|
|
2193
|
+
return {
|
|
2194
|
+
text: nodeText.slice(0, targetOffset),
|
|
2195
|
+
textAfter: nodeText.slice(targetOffset)
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
return {
|
|
2199
|
+
text: "",
|
|
2200
|
+
textAfter: ""
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
let collectedText = "";
|
|
2204
|
+
let collectedTextAfter = "";
|
|
2205
|
+
if (node.getTextContent && node.getType() === "text") if (founded) collectedTextAfter += node.getTextContent();
|
|
2206
|
+
else collectedText += node.getTextContent();
|
|
2207
|
+
if (node.getChildren) {
|
|
2208
|
+
const children = node.getChildren();
|
|
2209
|
+
for (const child of children) {
|
|
2210
|
+
const result = collectTextBeforeCursor(child, targetNode, targetOffset);
|
|
2211
|
+
collectedTextAfter += result.textAfter;
|
|
2212
|
+
collectedText += result.text;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
return {
|
|
2216
|
+
text: collectedText,
|
|
2217
|
+
textAfter: collectedTextAfter
|
|
2218
|
+
};
|
|
2219
|
+
};
|
|
2220
|
+
const result = collectTextBeforeCursor(paragraphNode, anchorNode, anchorOffset);
|
|
2221
|
+
return {
|
|
2222
|
+
textAfter: result.textAfter,
|
|
2223
|
+
textBefore: result.text
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
showPlaceholderNodes(editor, suggestion) {
|
|
2227
|
+
editor.update(() => {
|
|
2228
|
+
const selection = $getSelection();
|
|
2229
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
2230
|
+
this.logger.warn("⚠️ No valid selection for placeholder");
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
if (!this.markdownService) {
|
|
2234
|
+
this.logger.warn("⚠️ No valid markdown service for placeholder");
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
const nodes = this.markdownService.parseMarkdownToLexical(suggestion);
|
|
2238
|
+
if (nodes.children[0]) {
|
|
2239
|
+
const firstChild = nodes.children[0];
|
|
2240
|
+
firstChild.children = [{
|
|
2241
|
+
children: firstChild.children,
|
|
2242
|
+
type: "PlaceholderInline"
|
|
2243
|
+
}];
|
|
2244
|
+
}
|
|
2245
|
+
for (let i = 1; i < nodes.children.length; i++) {
|
|
2246
|
+
const child = nodes.children[i];
|
|
2247
|
+
nodes.children[i] = {
|
|
2248
|
+
children: [child],
|
|
2249
|
+
type: "PlaceholderBlock"
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
const markerNode = INodeHelper.createTextNode("");
|
|
2253
|
+
nodes.children.push({
|
|
2254
|
+
children: [markerNode],
|
|
2255
|
+
name: "",
|
|
2256
|
+
type: "PlaceholderInline"
|
|
2257
|
+
});
|
|
2258
|
+
const saveSel = selection.clone();
|
|
2259
|
+
this.markdownService.insertIRootNode(editor, nodes, selection);
|
|
2260
|
+
$setSelection(saveSel);
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
clearPlaceholderNodes(editor) {
|
|
2264
|
+
editor.update(() => {
|
|
2265
|
+
editor.getEditorState()._nodeMap.forEach((node) => {
|
|
2266
|
+
const selection = $getSelection();
|
|
2267
|
+
const clonedSelection = selection ? selection.clone() : null;
|
|
2268
|
+
if (node.isAttached() && ["PlaceholderBlock", "PlaceholderInline"].includes(node.getType())) {
|
|
2269
|
+
if (node.getType() === "PlaceholderInline" && node.getTextContent().includes("") && node.getPreviousSibling() === null) {
|
|
2270
|
+
while (node.getNextSibling()) {
|
|
2271
|
+
const next = node.getNextSibling();
|
|
2272
|
+
if (next) $insertNodes([next]);
|
|
2273
|
+
}
|
|
2274
|
+
$setSelection(clonedSelection);
|
|
2275
|
+
node.getParent()?.remove();
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
node.remove();
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
applySuggestion(editor) {
|
|
2284
|
+
if (!this.currentSuggestion) return;
|
|
2285
|
+
const markdown = this.currentSuggestion;
|
|
2286
|
+
editor.update(() => {
|
|
2287
|
+
const selection = $getSelection();
|
|
2288
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed() || !this.markdownService) return;
|
|
2289
|
+
editor.getEditorState()._nodeMap.forEach((node) => {
|
|
2290
|
+
if (node.isAttached() && ["PlaceholderBlock", "PlaceholderInline"].includes(node.getType())) node.remove();
|
|
2291
|
+
});
|
|
2292
|
+
const nodes = this.markdownService.parseMarkdownToLexical(markdown);
|
|
2293
|
+
this.markdownService.insertIRootNode(editor, nodes, selection);
|
|
2294
|
+
this.clearPlaceholderNodes(editor);
|
|
2295
|
+
this.currentSuggestion = null;
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
isSamePosition(pos1, pos2) {
|
|
2299
|
+
return pos1.key === pos2.key && pos1.offset === pos2.offset && pos1.type === pos2.type;
|
|
2300
|
+
}
|
|
2301
|
+
destroy() {
|
|
2302
|
+
this.clearTimer();
|
|
2303
|
+
this.placeholderNodes = [];
|
|
2304
|
+
super.destroy();
|
|
2305
|
+
}
|
|
2306
|
+
};
|
|
2307
|
+
//#endregion
|
|
2308
|
+
//#region src/plugins/auto-complete/react/style.ts
|
|
2309
|
+
const styles$12 = createStaticStyles(({ css }) => ({
|
|
2310
|
+
placeholderBlock: css`
|
|
2311
|
+
opacity: 0.4;
|
|
2312
|
+
`,
|
|
2313
|
+
placeholderInline: css`
|
|
2314
|
+
opacity: 0.4;
|
|
2315
|
+
`
|
|
2316
|
+
}));
|
|
2317
|
+
//#endregion
|
|
2318
|
+
//#region src/plugins/auto-complete/react/ReactAutoCompletePlugin.tsx
|
|
2319
|
+
const ReactAutoCompletePlugin = ({ onAutoComplete, delay }) => {
|
|
2320
|
+
const [editor] = useLexicalComposerContext();
|
|
2321
|
+
useLayoutEffect(() => {
|
|
2322
|
+
editor.registerPlugin(AutoCompletePlugin, {
|
|
2323
|
+
delay,
|
|
2324
|
+
onAutoComplete,
|
|
2325
|
+
theme: {
|
|
2326
|
+
placeholderBlock: cx(styles$12.placeholderBlock),
|
|
2327
|
+
placeholderInline: cx(styles$12.placeholderInline)
|
|
2328
|
+
}
|
|
2329
|
+
});
|
|
2330
|
+
}, []);
|
|
2331
|
+
return null;
|
|
2332
|
+
};
|
|
2333
|
+
ReactAutoCompletePlugin.displayName = "ReactAutoCompletePlugin";
|
|
2334
|
+
//#endregion
|
|
1517
2335
|
//#region src/plugins/code/react/style.ts
|
|
1518
2336
|
const styles$11 = createStaticStyles(({ css, cssVar }) => ({ codeInline: css`
|
|
1519
2337
|
display: inline;
|
|
@@ -3199,70 +4017,6 @@ const ReactFilePlugin = ({ className, locale, handleUpload, markdownWriter, them
|
|
|
3199
4017
|
};
|
|
3200
4018
|
ReactFilePlugin.displayName = "ReactFilePlugin";
|
|
3201
4019
|
//#endregion
|
|
3202
|
-
//#region src/plugins/hr/command/index.ts
|
|
3203
|
-
const INSERT_HORIZONTAL_RULE_COMMAND = createCommand("INSERT_HORIZONTAL_RULE_COMMAND");
|
|
3204
|
-
function registerHorizontalRuleCommand(editor) {
|
|
3205
|
-
return editor.registerCommand(INSERT_HORIZONTAL_RULE_COMMAND, () => {
|
|
3206
|
-
editor.update(() => {
|
|
3207
|
-
$insertNodes([$createHorizontalRuleNode()]);
|
|
3208
|
-
});
|
|
3209
|
-
return true;
|
|
3210
|
-
}, COMMAND_PRIORITY_EDITOR);
|
|
3211
|
-
}
|
|
3212
|
-
//#endregion
|
|
3213
|
-
//#region src/plugins/hr/plugin/index.ts
|
|
3214
|
-
init_helper();
|
|
3215
|
-
init_plugin();
|
|
3216
|
-
const HRPlugin = class extends KernelPlugin {
|
|
3217
|
-
static {
|
|
3218
|
-
this.pluginName = "HRPlugin";
|
|
3219
|
-
}
|
|
3220
|
-
constructor(kernel, config) {
|
|
3221
|
-
super();
|
|
3222
|
-
this.kernel = kernel;
|
|
3223
|
-
kernel.registerNodes([HorizontalRuleNode]);
|
|
3224
|
-
kernel.registerThemes({ hr: config?.theme || "" });
|
|
3225
|
-
this.registerDecorator(kernel, "horizontalrule", (node, editor) => {
|
|
3226
|
-
return config?.decorator ? config.decorator(node, editor) : null;
|
|
3227
|
-
});
|
|
3228
|
-
}
|
|
3229
|
-
onInit(editor) {
|
|
3230
|
-
this.register(registerHorizontalRuleCommand(editor));
|
|
3231
|
-
this.registerMarkdown();
|
|
3232
|
-
this.registerLiteXml();
|
|
3233
|
-
}
|
|
3234
|
-
registerLiteXml() {
|
|
3235
|
-
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
3236
|
-
if (!litexmlService) return;
|
|
3237
|
-
litexmlService.registerXMLWriter(HorizontalRuleNode.getType(), (node, ctx) => {
|
|
3238
|
-
if ($isHorizontalRuleNode(node)) return ctx.createXmlNode("hr", {});
|
|
3239
|
-
return false;
|
|
3240
|
-
});
|
|
3241
|
-
litexmlService.registerXMLReader("hr", () => {
|
|
3242
|
-
return INodeHelper.createElementNode(HorizontalRuleNode.getType(), {});
|
|
3243
|
-
});
|
|
3244
|
-
}
|
|
3245
|
-
registerMarkdown() {
|
|
3246
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownShortCut({
|
|
3247
|
-
regExp: /^(---|\*\*\*|___)$/,
|
|
3248
|
-
replace: (parentNode, _1, _2, isImport) => {
|
|
3249
|
-
const line = $createHorizontalRuleNode();
|
|
3250
|
-
if (isImport || parentNode.getNextSibling()) parentNode.replace(line);
|
|
3251
|
-
else parentNode.insertBefore(line);
|
|
3252
|
-
line.selectNext();
|
|
3253
|
-
},
|
|
3254
|
-
trigger: "enter",
|
|
3255
|
-
type: "element"
|
|
3256
|
-
});
|
|
3257
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(HorizontalRuleNode.getType(), (ctx, node) => {
|
|
3258
|
-
if ($isHorizontalRuleNode(node)) ctx.appendLine("---\n\n");
|
|
3259
|
-
});
|
|
3260
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("thematicBreak", () => {
|
|
3261
|
-
return INodeHelper.createElementNode("horizontalrule", {});
|
|
3262
|
-
});
|
|
3263
|
-
}
|
|
3264
|
-
};
|
|
3265
|
-
//#endregion
|
|
3266
4020
|
//#region src/plugins/hr/react/style.ts
|
|
3267
4021
|
const styles$7 = createStaticStyles(({ css, cssVar }) => css`
|
|
3268
4022
|
cursor: pointer;
|
|
@@ -4784,558 +5538,259 @@ const LinkToolbar = memo(({ editor, enable }) => {
|
|
|
4784
5538
|
if (!payload.event.target || divRef.current === null) return false;
|
|
4785
5539
|
clearTimeout(clearTimerRef.current);
|
|
4786
5540
|
setLinkNode(payload.linkNode);
|
|
4787
|
-
updatePosition({
|
|
4788
|
-
callback: () => {
|
|
4789
|
-
LinkRef.current = payload.event.target;
|
|
4790
|
-
},
|
|
4791
|
-
floating: divRef.current,
|
|
4792
|
-
offset: 4,
|
|
4793
|
-
placement: "top-start",
|
|
4794
|
-
reference: payload.event.target
|
|
4795
|
-
});
|
|
4796
|
-
return false;
|
|
4797
|
-
}, COMMAND_PRIORITY_NORMAL), editor.registerCommand(HOVER_OUT_LINK_COMMAND, () => {
|
|
4798
|
-
clearTimerRef.current = setTimeout(handleCancel, 300);
|
|
4799
|
-
return true;
|
|
4800
|
-
}, COMMAND_PRIORITY_NORMAL));
|
|
4801
|
-
}, [enable, editable]);
|
|
4802
|
-
return /* @__PURE__ */ jsx(ActionIconGroup, {
|
|
4803
|
-
className: styles$5.linkToolbar,
|
|
4804
|
-
items: [
|
|
4805
|
-
{
|
|
4806
|
-
icon: EditIcon,
|
|
4807
|
-
key: "edit",
|
|
4808
|
-
label: t("link.edit"),
|
|
4809
|
-
onClick: handleEdit
|
|
4810
|
-
},
|
|
4811
|
-
{
|
|
4812
|
-
icon: ExternalLinkIcon,
|
|
4813
|
-
key: "openLink",
|
|
4814
|
-
label: t("link.open"),
|
|
4815
|
-
onClick: handleOpenLink
|
|
4816
|
-
},
|
|
4817
|
-
{
|
|
4818
|
-
icon: UnlinkIcon,
|
|
4819
|
-
key: "unlink",
|
|
4820
|
-
label: t("link.unlink"),
|
|
4821
|
-
onClick: () => {
|
|
4822
|
-
handleRemove();
|
|
4823
|
-
handleCancel();
|
|
4824
|
-
}
|
|
4825
|
-
}
|
|
4826
|
-
],
|
|
4827
|
-
onMouseEnter: () => {
|
|
4828
|
-
clearTimeout(clearTimerRef.current);
|
|
4829
|
-
},
|
|
4830
|
-
onMouseLeave: () => {
|
|
4831
|
-
handleCancel();
|
|
4832
|
-
},
|
|
4833
|
-
ref: divRef,
|
|
4834
|
-
shadow: true,
|
|
4835
|
-
size: {
|
|
4836
|
-
blockSize: 32,
|
|
4837
|
-
size: 16
|
|
4838
|
-
},
|
|
4839
|
-
variant: "outlined"
|
|
4840
|
-
});
|
|
4841
|
-
});
|
|
4842
|
-
LinkToolbar.displayName = "LinkToolbar";
|
|
4843
|
-
//#endregion
|
|
4844
|
-
//#region src/plugins/link/react/ReactLinkPlugin.tsx
|
|
4845
|
-
const ReactLinkPlugin = ({ theme, enableHotkey = true, validateUrl, attributes }) => {
|
|
4846
|
-
const [enableToolbar, setEnableToolbar] = useState(false);
|
|
4847
|
-
const [editor] = useLexicalComposerContext();
|
|
4848
|
-
useLayoutEffect(() => {
|
|
4849
|
-
editor.registerPlugin(MarkdownPlugin);
|
|
4850
|
-
editor.registerPlugin(LinkPlugin, {
|
|
4851
|
-
attributes,
|
|
4852
|
-
enableHotkey,
|
|
4853
|
-
theme: theme || styles$5,
|
|
4854
|
-
validateUrl
|
|
4855
|
-
});
|
|
4856
|
-
}, [
|
|
4857
|
-
attributes,
|
|
4858
|
-
enableHotkey,
|
|
4859
|
-
styles$5,
|
|
4860
|
-
theme,
|
|
4861
|
-
validateUrl
|
|
4862
|
-
]);
|
|
4863
|
-
useLexicalEditor(() => {
|
|
4864
|
-
const linkService = editor.requireService(ILinkService);
|
|
4865
|
-
setEnableToolbar(linkService.enableLinkToolbar);
|
|
4866
|
-
const handleChange = () => {
|
|
4867
|
-
setEnableToolbar(linkService.enableLinkToolbar);
|
|
4868
|
-
};
|
|
4869
|
-
linkService.on("linkToolbarChange", handleChange);
|
|
4870
|
-
return () => {
|
|
4871
|
-
linkService.off("linkToolbarChange", handleChange);
|
|
4872
|
-
};
|
|
4873
|
-
}, []);
|
|
4874
|
-
return /* @__PURE__ */ jsxs(PortalAnchor, { children: [/* @__PURE__ */ jsx(LinkToolbar, {
|
|
4875
|
-
editor: editor.getLexicalEditor(),
|
|
4876
|
-
enable: enableToolbar
|
|
4877
|
-
}), /* @__PURE__ */ jsx(LinkEdit, { editor: editor.getLexicalEditor() })] });
|
|
4878
|
-
};
|
|
4879
|
-
ReactLinkPlugin.displayName = "ReactLinkPlugin";
|
|
4880
|
-
//#endregion
|
|
4881
|
-
//#region src/plugins/link-highlight/plugin/registry.ts
|
|
4882
|
-
init_hotkey();
|
|
4883
|
-
function registerLinkHighlight(editor, kernel, options) {
|
|
4884
|
-
const { enableHotkey = true } = options || {};
|
|
4885
|
-
return mergeRegister(editor.registerUpdateListener(({ mutatedNodes }) => {
|
|
4886
|
-
const keys = (mutatedNodes?.get(LinkHighlightNode))?.keys() || [];
|
|
4887
|
-
const needAddBefore = /* @__PURE__ */ new Set();
|
|
4888
|
-
editor.getEditorState().read(() => {
|
|
4889
|
-
for (const key of keys) {
|
|
4890
|
-
const node = $getNodeByKey(key);
|
|
4891
|
-
if (!node) return;
|
|
4892
|
-
if (node.getParent()?.getFirstChild() === node) needAddBefore.add(node);
|
|
4893
|
-
}
|
|
4894
|
-
});
|
|
4895
|
-
if (needAddBefore.size > 0) editor.update(() => {
|
|
4896
|
-
needAddBefore.forEach((node) => {
|
|
4897
|
-
if (!node.getPreviousSibling()) node.insertBefore($createCursorNode());
|
|
4898
|
-
});
|
|
4899
|
-
});
|
|
4900
|
-
}), kernel.registerHotkey(HotkeyEnum.Link, () => editor.dispatchCommand(INSERT_LINK_HIGHLIGHT_COMMAND, void 0), {
|
|
4901
|
-
enabled: enableHotkey,
|
|
4902
|
-
preventDefault: true,
|
|
4903
|
-
stopPropagation: true
|
|
4904
|
-
}));
|
|
4905
|
-
}
|
|
4906
|
-
//#endregion
|
|
4907
|
-
//#region src/plugins/link-highlight/plugin/index.ts
|
|
4908
|
-
init_helper();
|
|
4909
|
-
init_plugin();
|
|
4910
|
-
init_debug();
|
|
4911
|
-
const LinkHighlightPlugin = class extends KernelPlugin {
|
|
4912
|
-
static {
|
|
4913
|
-
this.pluginName = "LinkHighlightPlugin";
|
|
4914
|
-
}
|
|
4915
|
-
constructor(kernel, config) {
|
|
4916
|
-
super();
|
|
4917
|
-
this.kernel = kernel;
|
|
4918
|
-
this.config = config;
|
|
4919
|
-
this.logger = createDebugLogger("plugin", "link-highlight");
|
|
4920
|
-
kernel.registerNodes([LinkHighlightNode]);
|
|
4921
|
-
kernel.registerThemes({ linkHighlight: config?.theme || "editor-link-highlight" });
|
|
4922
|
-
this.urlRegex = config?.urlRegex || /^(?:https?:\/\/|mailto:|tel:)[^\s"<>[\\\]^`{|}]+|^www\.[^\s"<>[\\\]^`{|}]+/i;
|
|
4923
|
-
this.logger.debug("LinkHighlightPlugin initialized");
|
|
4924
|
-
}
|
|
4925
|
-
onInit(editor) {
|
|
4926
|
-
this.register(registerLinkHighlightCommand(editor));
|
|
4927
|
-
this.register(registerLinkHighlight(editor, this.kernel, { enableHotkey: this.config?.enableHotkey }));
|
|
4928
|
-
if (this.config?.enablePasteAutoHighlight !== false) this.register(editor.registerCommand(PASTE_COMMAND, (payload) => {
|
|
4929
|
-
const { clipboardData } = payload;
|
|
4930
|
-
if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === "text/plain") {
|
|
4931
|
-
const data = clipboardData.getData("text/plain").trim();
|
|
4932
|
-
if (this.urlRegex.test(data) && isValidUrl(data)) {
|
|
4933
|
-
payload.stopImmediatePropagation();
|
|
4934
|
-
payload.preventDefault();
|
|
4935
|
-
this.logger.debug("Auto-highlighting pasted URL:", data);
|
|
4936
|
-
editor.update(() => {
|
|
4937
|
-
const selection = $getSelection();
|
|
4938
|
-
if ($isRangeSelection(selection)) {
|
|
4939
|
-
const linkHighlightNode = $createLinkHighlightNode();
|
|
4940
|
-
const textNode = $createTextNode(data);
|
|
4941
|
-
linkHighlightNode.append(textNode);
|
|
4942
|
-
selection.insertNodes([linkHighlightNode]);
|
|
4943
|
-
}
|
|
4944
|
-
});
|
|
4945
|
-
return true;
|
|
4946
|
-
}
|
|
4947
|
-
}
|
|
5541
|
+
updatePosition({
|
|
5542
|
+
callback: () => {
|
|
5543
|
+
LinkRef.current = payload.event.target;
|
|
5544
|
+
},
|
|
5545
|
+
floating: divRef.current,
|
|
5546
|
+
offset: 4,
|
|
5547
|
+
placement: "top-start",
|
|
5548
|
+
reference: payload.event.target
|
|
5549
|
+
});
|
|
4948
5550
|
return false;
|
|
4949
|
-
}, COMMAND_PRIORITY_NORMAL))
|
|
4950
|
-
|
|
4951
|
-
if (!markdownService) return;
|
|
4952
|
-
markdownService.registerMarkdownWriter(LinkHighlightNode.getType(), (ctx, node) => {
|
|
4953
|
-
ctx.appendLine(node.getTextContent());
|
|
5551
|
+
}, COMMAND_PRIORITY_NORMAL), editor.registerCommand(HOVER_OUT_LINK_COMMAND, () => {
|
|
5552
|
+
clearTimerRef.current = setTimeout(handleCancel, 300);
|
|
4954
5553
|
return true;
|
|
4955
|
-
});
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
textNode.replace(linkHighlightNode);
|
|
5554
|
+
}, COMMAND_PRIORITY_NORMAL));
|
|
5555
|
+
}, [enable, editable]);
|
|
5556
|
+
return /* @__PURE__ */ jsx(ActionIconGroup, {
|
|
5557
|
+
className: styles$5.linkToolbar,
|
|
5558
|
+
items: [
|
|
5559
|
+
{
|
|
5560
|
+
icon: EditIcon,
|
|
5561
|
+
key: "edit",
|
|
5562
|
+
label: t("link.edit"),
|
|
5563
|
+
onClick: handleEdit
|
|
4966
5564
|
},
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
version: 1
|
|
4982
|
-
});
|
|
4983
|
-
}
|
|
4984
|
-
return false;
|
|
4985
|
-
}, 1);
|
|
4986
|
-
markdownService.registerMarkdownReader("link", (node, children) => {
|
|
4987
|
-
const url = node.url || "";
|
|
4988
|
-
if (!children || children.length === 0) return false;
|
|
4989
|
-
if (children.filter((child) => child.type === "text").map((child) => child.text || "").join("") === url) {
|
|
4990
|
-
this.logger.debug("Converting markdown auto-link to LinkHighlightNode:", url);
|
|
4991
|
-
return INodeHelper.createElementNode("linkHighlight", {
|
|
4992
|
-
children: [INodeHelper.createTextNode(url, {})],
|
|
4993
|
-
direction: "ltr",
|
|
4994
|
-
format: "",
|
|
4995
|
-
indent: 0,
|
|
4996
|
-
type: "linkHighlight",
|
|
4997
|
-
version: 1
|
|
4998
|
-
});
|
|
5565
|
+
{
|
|
5566
|
+
icon: ExternalLinkIcon,
|
|
5567
|
+
key: "openLink",
|
|
5568
|
+
label: t("link.open"),
|
|
5569
|
+
onClick: handleOpenLink
|
|
5570
|
+
},
|
|
5571
|
+
{
|
|
5572
|
+
icon: UnlinkIcon,
|
|
5573
|
+
key: "unlink",
|
|
5574
|
+
label: t("link.unlink"),
|
|
5575
|
+
onClick: () => {
|
|
5576
|
+
handleRemove();
|
|
5577
|
+
handleCancel();
|
|
5578
|
+
}
|
|
4999
5579
|
}
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
` };
|
|
5580
|
+
],
|
|
5581
|
+
onMouseEnter: () => {
|
|
5582
|
+
clearTimeout(clearTimerRef.current);
|
|
5583
|
+
},
|
|
5584
|
+
onMouseLeave: () => {
|
|
5585
|
+
handleCancel();
|
|
5586
|
+
},
|
|
5587
|
+
ref: divRef,
|
|
5588
|
+
shadow: true,
|
|
5589
|
+
size: {
|
|
5590
|
+
blockSize: 32,
|
|
5591
|
+
size: 16
|
|
5592
|
+
},
|
|
5593
|
+
variant: "outlined"
|
|
5594
|
+
});
|
|
5016
5595
|
});
|
|
5596
|
+
LinkToolbar.displayName = "LinkToolbar";
|
|
5017
5597
|
//#endregion
|
|
5018
|
-
//#region src/plugins/link
|
|
5019
|
-
const
|
|
5598
|
+
//#region src/plugins/link/react/ReactLinkPlugin.tsx
|
|
5599
|
+
const ReactLinkPlugin = ({ theme, enableHotkey = true, validateUrl, attributes }) => {
|
|
5600
|
+
const [enableToolbar, setEnableToolbar] = useState(false);
|
|
5020
5601
|
const [editor] = useLexicalComposerContext();
|
|
5021
5602
|
useLayoutEffect(() => {
|
|
5022
5603
|
editor.registerPlugin(MarkdownPlugin);
|
|
5023
|
-
editor.registerPlugin(
|
|
5604
|
+
editor.registerPlugin(LinkPlugin, {
|
|
5605
|
+
attributes,
|
|
5024
5606
|
enableHotkey,
|
|
5025
|
-
|
|
5026
|
-
|
|
5607
|
+
theme: theme || styles$5,
|
|
5608
|
+
validateUrl
|
|
5027
5609
|
});
|
|
5028
5610
|
}, [
|
|
5029
|
-
|
|
5030
|
-
cx,
|
|
5611
|
+
attributes,
|
|
5031
5612
|
enableHotkey,
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5613
|
+
styles$5,
|
|
5614
|
+
theme,
|
|
5615
|
+
validateUrl
|
|
5035
5616
|
]);
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
}
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
if (indent) listItem.setIndent(indent);
|
|
5072
|
-
};
|
|
5073
|
-
};
|
|
5074
|
-
function $indentOverTab(selection) {
|
|
5075
|
-
if ($filter(selection.getNodes(), (node) => {
|
|
5076
|
-
if ($isBlockElementNode(node) && node.canIndent()) return node;
|
|
5077
|
-
return null;
|
|
5078
|
-
}).length > 0) return true;
|
|
5079
|
-
const anchor = selection.anchor;
|
|
5080
|
-
const focus = selection.focus;
|
|
5081
|
-
const first = focus.isBefore(anchor) ? focus : anchor;
|
|
5082
|
-
const firstBlock = $getNearestBlockElementAncestorOrThrow(first.getNode());
|
|
5083
|
-
if (firstBlock.canIndent()) {
|
|
5084
|
-
const firstBlockKey = firstBlock.getKey();
|
|
5085
|
-
let selectionAtStart = $createRangeSelection();
|
|
5086
|
-
selectionAtStart.anchor.set(firstBlockKey, 0, "element");
|
|
5087
|
-
selectionAtStart.focus.set(firstBlockKey, 0, "element");
|
|
5088
|
-
selectionAtStart = $normalizeSelection__EXPERIMENTAL(selectionAtStart);
|
|
5089
|
-
if (selectionAtStart.anchor.is(first)) return true;
|
|
5090
|
-
}
|
|
5091
|
-
return false;
|
|
5092
|
-
}
|
|
5093
|
-
//#endregion
|
|
5094
|
-
//#region src/plugins/list/plugin/registry.ts
|
|
5095
|
-
init_hotkey();
|
|
5096
|
-
function registerListCommands(editor, kernel, options) {
|
|
5097
|
-
const { enableHotkey = true } = options || {};
|
|
5098
|
-
return mergeRegister(kernel.registerHotkey(HotkeyEnum.BulletList, () => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND$1, void 0), {
|
|
5099
|
-
enabled: enableHotkey,
|
|
5100
|
-
preventDefault: true,
|
|
5101
|
-
stopImmediatePropagation: true
|
|
5102
|
-
}), kernel.registerHotkey(HotkeyEnum.NumberList, () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND$1, void 0), {
|
|
5103
|
-
enabled: enableHotkey,
|
|
5104
|
-
preventDefault: true,
|
|
5105
|
-
stopImmediatePropagation: true
|
|
5106
|
-
}), editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
5107
|
-
const selection = $getSelection();
|
|
5108
|
-
if (!$isRangeSelection(selection)) return false;
|
|
5109
|
-
event.preventDefault();
|
|
5110
|
-
const command = $indentOverTab(selection) ? event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND : INSERT_TAB_COMMAND;
|
|
5111
|
-
return editor.dispatchCommand(command, void 0);
|
|
5112
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => {
|
|
5113
|
-
const selection = $getSelection();
|
|
5114
|
-
if (!$isRangeSelection(selection) || !selection.isCollapsed()) return false;
|
|
5115
|
-
const anchor = selection.anchor;
|
|
5116
|
-
if (anchor.offset !== 0) return false;
|
|
5117
|
-
const anchorNode = anchor.getNode();
|
|
5118
|
-
let listItemNode;
|
|
5119
|
-
if ($isListItemNode(anchorNode)) listItemNode = anchorNode;
|
|
5120
|
-
else if ($isTextNode(anchorNode)) {
|
|
5121
|
-
if (anchorNode.getPreviousSibling()) return false;
|
|
5122
|
-
const parent = anchorNode.getParentOrThrow();
|
|
5123
|
-
if (!$isListItemNode(parent)) return false;
|
|
5124
|
-
listItemNode = parent;
|
|
5125
|
-
}
|
|
5126
|
-
if (!listItemNode || !$isRootNode(listItemNode.getParent()?.getParent())) return false;
|
|
5127
|
-
const listNode = listItemNode.getParentOrThrow();
|
|
5128
|
-
queueMicrotask(() => {
|
|
5129
|
-
editor.update(() => {
|
|
5130
|
-
if (!listItemNode) return;
|
|
5131
|
-
let newlistNode;
|
|
5132
|
-
if (listItemNode.getPreviousSibling() === null) {
|
|
5133
|
-
listItemNode.replace($createParagraphNode(), true).select(0, 0);
|
|
5134
|
-
return;
|
|
5135
|
-
}
|
|
5136
|
-
let next = listItemNode.getNextSibling();
|
|
5137
|
-
if (next) newlistNode = $createListNode(listNode.getListType(), listItemNode.getValue());
|
|
5138
|
-
while (next && newlistNode) {
|
|
5139
|
-
next.remove();
|
|
5140
|
-
newlistNode.append(next);
|
|
5141
|
-
next = next.getNextSibling();
|
|
5142
|
-
}
|
|
5143
|
-
const p = listItemNode.replace($createParagraphNode(), true);
|
|
5144
|
-
p.remove();
|
|
5145
|
-
listNode.insertAfter(p);
|
|
5146
|
-
if (newlistNode) p.insertAfter(newlistNode);
|
|
5147
|
-
p.select(0, 0);
|
|
5617
|
+
useLexicalEditor(() => {
|
|
5618
|
+
const linkService = editor.requireService(ILinkService);
|
|
5619
|
+
setEnableToolbar(linkService.enableLinkToolbar);
|
|
5620
|
+
const handleChange = () => {
|
|
5621
|
+
setEnableToolbar(linkService.enableLinkToolbar);
|
|
5622
|
+
};
|
|
5623
|
+
linkService.on("linkToolbarChange", handleChange);
|
|
5624
|
+
return () => {
|
|
5625
|
+
linkService.off("linkToolbarChange", handleChange);
|
|
5626
|
+
};
|
|
5627
|
+
}, []);
|
|
5628
|
+
return /* @__PURE__ */ jsxs(PortalAnchor, { children: [/* @__PURE__ */ jsx(LinkToolbar, {
|
|
5629
|
+
editor: editor.getLexicalEditor(),
|
|
5630
|
+
enable: enableToolbar
|
|
5631
|
+
}), /* @__PURE__ */ jsx(LinkEdit, { editor: editor.getLexicalEditor() })] });
|
|
5632
|
+
};
|
|
5633
|
+
ReactLinkPlugin.displayName = "ReactLinkPlugin";
|
|
5634
|
+
//#endregion
|
|
5635
|
+
//#region src/plugins/link-highlight/plugin/registry.ts
|
|
5636
|
+
init_hotkey();
|
|
5637
|
+
function registerLinkHighlight(editor, kernel, options) {
|
|
5638
|
+
const { enableHotkey = true } = options || {};
|
|
5639
|
+
return mergeRegister(editor.registerUpdateListener(({ mutatedNodes }) => {
|
|
5640
|
+
const keys = (mutatedNodes?.get(LinkHighlightNode))?.keys() || [];
|
|
5641
|
+
const needAddBefore = /* @__PURE__ */ new Set();
|
|
5642
|
+
editor.getEditorState().read(() => {
|
|
5643
|
+
for (const key of keys) {
|
|
5644
|
+
const node = $getNodeByKey(key);
|
|
5645
|
+
if (!node) return;
|
|
5646
|
+
if (node.getParent()?.getFirstChild() === node) needAddBefore.add(node);
|
|
5647
|
+
}
|
|
5648
|
+
});
|
|
5649
|
+
if (needAddBefore.size > 0) editor.update(() => {
|
|
5650
|
+
needAddBefore.forEach((node) => {
|
|
5651
|
+
if (!node.getPreviousSibling()) node.insertBefore($createCursorNode());
|
|
5148
5652
|
});
|
|
5149
5653
|
});
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5654
|
+
}), kernel.registerHotkey(HotkeyEnum.Link, () => editor.dispatchCommand(INSERT_LINK_HIGHLIGHT_COMMAND, void 0), {
|
|
5655
|
+
enabled: enableHotkey,
|
|
5656
|
+
preventDefault: true,
|
|
5657
|
+
stopPropagation: true
|
|
5658
|
+
}));
|
|
5154
5659
|
}
|
|
5155
5660
|
//#endregion
|
|
5156
|
-
//#region src/plugins/
|
|
5661
|
+
//#region src/plugins/link-highlight/plugin/index.ts
|
|
5157
5662
|
init_helper();
|
|
5158
5663
|
init_plugin();
|
|
5159
|
-
|
|
5160
|
-
const
|
|
5161
|
-
const CHECK_LIST_REGEX = /^(\s*)(?:-\s)?\s?(\[(\s|x)?])\s/i;
|
|
5162
|
-
const ListPlugin = class extends KernelPlugin {
|
|
5664
|
+
init_debug();
|
|
5665
|
+
const LinkHighlightPlugin = class extends KernelPlugin {
|
|
5163
5666
|
static {
|
|
5164
|
-
this.pluginName = "
|
|
5667
|
+
this.pluginName = "LinkHighlightPlugin";
|
|
5165
5668
|
}
|
|
5166
5669
|
constructor(kernel, config) {
|
|
5167
5670
|
super();
|
|
5168
5671
|
this.kernel = kernel;
|
|
5169
5672
|
this.config = config;
|
|
5170
|
-
|
|
5171
|
-
kernel.
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
listitemUnchecked: "editor_listItemUnchecked",
|
|
5176
|
-
nested: { listitem: "editor_listItemNested" },
|
|
5177
|
-
ol: cx("editor_listOrdered", config?.theme),
|
|
5178
|
-
olDepth: [
|
|
5179
|
-
"editor_listOrdered dp1",
|
|
5180
|
-
"editor_listOrdered dp2",
|
|
5181
|
-
"editor_listOrdered dp3",
|
|
5182
|
-
"editor_listOrdered dp4",
|
|
5183
|
-
"editor_listOrdered dp5"
|
|
5184
|
-
],
|
|
5185
|
-
ul: cx("editor_listUnordered", config?.theme)
|
|
5186
|
-
} });
|
|
5673
|
+
this.logger = createDebugLogger("plugin", "link-highlight");
|
|
5674
|
+
kernel.registerNodes([LinkHighlightNode]);
|
|
5675
|
+
kernel.registerThemes({ linkHighlight: config?.theme || "editor-link-highlight" });
|
|
5676
|
+
this.urlRegex = config?.urlRegex || /^(?:https?:\/\/|mailto:|tel:)[^\s"<>[\\\]^`{|}]+|^www\.[^\s"<>[\\\]^`{|}]+/i;
|
|
5677
|
+
this.logger.debug("LinkHighlightPlugin initialized");
|
|
5187
5678
|
}
|
|
5188
5679
|
onInit(editor) {
|
|
5189
|
-
this.register(
|
|
5190
|
-
this.register(
|
|
5191
|
-
this.register(
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5680
|
+
this.register(registerLinkHighlightCommand(editor));
|
|
5681
|
+
this.register(registerLinkHighlight(editor, this.kernel, { enableHotkey: this.config?.enableHotkey }));
|
|
5682
|
+
if (this.config?.enablePasteAutoHighlight !== false) this.register(editor.registerCommand(PASTE_COMMAND, (payload) => {
|
|
5683
|
+
const { clipboardData } = payload;
|
|
5684
|
+
if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === "text/plain") {
|
|
5685
|
+
const data = clipboardData.getData("text/plain").trim();
|
|
5686
|
+
if (this.urlRegex.test(data) && isValidUrl(data)) {
|
|
5687
|
+
payload.stopImmediatePropagation();
|
|
5688
|
+
payload.preventDefault();
|
|
5689
|
+
this.logger.debug("Auto-highlighting pasted URL:", data);
|
|
5690
|
+
editor.update(() => {
|
|
5691
|
+
const selection = $getSelection();
|
|
5692
|
+
if ($isRangeSelection(selection)) {
|
|
5693
|
+
const linkHighlightNode = $createLinkHighlightNode();
|
|
5694
|
+
const textNode = $createTextNode(data);
|
|
5695
|
+
linkHighlightNode.append(textNode);
|
|
5696
|
+
selection.insertNodes([linkHighlightNode]);
|
|
5697
|
+
}
|
|
5698
|
+
});
|
|
5699
|
+
return true;
|
|
5700
|
+
}
|
|
5205
5701
|
}
|
|
5206
5702
|
return false;
|
|
5207
|
-
});
|
|
5208
|
-
litexmlService.registerXMLWriter(ListItemNode.getType(), (node, ctx) => {
|
|
5209
|
-
if ($isListItemNode(node)) return ctx.createXmlNode("li");
|
|
5210
|
-
return false;
|
|
5211
|
-
});
|
|
5212
|
-
litexmlService.registerXMLReader("ol", (xmlNode, children) => {
|
|
5213
|
-
return INodeHelper.createElementNode("list", {
|
|
5214
|
-
children,
|
|
5215
|
-
direction: "ltr",
|
|
5216
|
-
format: "",
|
|
5217
|
-
indent: 0,
|
|
5218
|
-
listType: "number",
|
|
5219
|
-
start: xmlNode.getAttribute("start") ? parseInt(xmlNode.getAttribute("start"), 10) : 1,
|
|
5220
|
-
tag: "ol",
|
|
5221
|
-
version: 1
|
|
5222
|
-
});
|
|
5223
|
-
});
|
|
5224
|
-
litexmlService.registerXMLReader("ul", (xmlNode, children) => {
|
|
5225
|
-
return INodeHelper.createElementNode("list", {
|
|
5226
|
-
children,
|
|
5227
|
-
direction: "ltr",
|
|
5228
|
-
format: "",
|
|
5229
|
-
indent: 0,
|
|
5230
|
-
listType: "bullet",
|
|
5231
|
-
start: 1,
|
|
5232
|
-
tag: "ul",
|
|
5233
|
-
version: 1
|
|
5234
|
-
});
|
|
5235
|
-
});
|
|
5236
|
-
litexmlService.registerXMLReader("li", (xmlNode, children) => {
|
|
5237
|
-
return INodeHelper.createElementNode("listitem", {
|
|
5238
|
-
children,
|
|
5239
|
-
direction: "ltr",
|
|
5240
|
-
format: "",
|
|
5241
|
-
indent: 0,
|
|
5242
|
-
type: "listitem",
|
|
5243
|
-
version: 1
|
|
5244
|
-
});
|
|
5245
|
-
});
|
|
5246
|
-
}
|
|
5247
|
-
registerMarkdown() {
|
|
5703
|
+
}, COMMAND_PRIORITY_NORMAL));
|
|
5248
5704
|
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
5249
5705
|
if (!markdownService) return;
|
|
5250
|
-
markdownService.
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
type: "element"
|
|
5254
|
-
});
|
|
5255
|
-
markdownService.registerMarkdownShortCut({
|
|
5256
|
-
regExp: ORDERED_LIST_REGEX,
|
|
5257
|
-
replace: listReplace("number"),
|
|
5258
|
-
type: "element"
|
|
5706
|
+
markdownService.registerMarkdownWriter(LinkHighlightNode.getType(), (ctx, node) => {
|
|
5707
|
+
ctx.appendLine(node.getTextContent());
|
|
5708
|
+
return true;
|
|
5259
5709
|
});
|
|
5260
5710
|
markdownService.registerMarkdownShortCut({
|
|
5261
|
-
regExp:
|
|
5262
|
-
replace:
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
return getLevel($getNearestNodeOfType(parent, ListNode)) + 1;
|
|
5274
|
-
};
|
|
5275
|
-
markdownService.registerMarkdownWriter(ListItemNode.getType(), (ctx, node) => {
|
|
5276
|
-
const parent = node.getParent();
|
|
5277
|
-
if ($isListItemNode(node) && $isListNode(parent)) {
|
|
5278
|
-
if ($isListNode(node.getFirstChild())) return;
|
|
5279
|
-
const level = getLevel(parent);
|
|
5280
|
-
const prefix = " ".repeat(level);
|
|
5281
|
-
switch (parent.getListType()) {
|
|
5282
|
-
case "bullet":
|
|
5283
|
-
ctx.wrap(prefix + "- ", "\n");
|
|
5284
|
-
break;
|
|
5285
|
-
case "number":
|
|
5286
|
-
ctx.wrap(`${prefix}${node.getValue()}. `, "\n");
|
|
5287
|
-
break;
|
|
5288
|
-
case "check":
|
|
5289
|
-
ctx.wrap(`${prefix}- [${node.getChecked() ? "x" : " "}] `, "\n");
|
|
5290
|
-
break;
|
|
5291
|
-
default: break;
|
|
5292
|
-
}
|
|
5293
|
-
}
|
|
5294
|
-
});
|
|
5295
|
-
markdownService.registerMarkdownReader("list", (node, children) => {
|
|
5296
|
-
const isCheck = node.children?.[0]?.type === "listItem" && typeof node.children?.[0]?.checked === "boolean";
|
|
5297
|
-
let start = node.start || 1;
|
|
5298
|
-
return INodeHelper.createElementNode("list", {
|
|
5299
|
-
children: children.map((v) => {
|
|
5300
|
-
if (v.type === "listitem") v.value = start++;
|
|
5301
|
-
return v;
|
|
5302
|
-
}),
|
|
5303
|
-
direction: "ltr",
|
|
5304
|
-
format: "",
|
|
5305
|
-
indent: 0,
|
|
5306
|
-
listType: isCheck ? "check" : node.ordered ? "number" : "bullet",
|
|
5307
|
-
start: node.start || 1,
|
|
5308
|
-
tag: node.ordered ? "ol" : "ul",
|
|
5309
|
-
version: 1
|
|
5310
|
-
});
|
|
5711
|
+
regExp: /<((?:https?:\/\/|mailto:|tel:|www\.)[^\s"<>[\\\]^`{|}]+)>\s?$/,
|
|
5712
|
+
replace: (textNode, match) => {
|
|
5713
|
+
const [, url] = match;
|
|
5714
|
+
if (!url || !isValidUrl(url)) return;
|
|
5715
|
+
this.logger.debug("Converting markdown auto-link to LinkHighlightNode:", url);
|
|
5716
|
+
const linkHighlightNode = $createLinkHighlightNode();
|
|
5717
|
+
const textNodeContent = $createTextNode(url);
|
|
5718
|
+
linkHighlightNode.append(textNodeContent);
|
|
5719
|
+
textNode.replace(linkHighlightNode);
|
|
5720
|
+
},
|
|
5721
|
+
trigger: ">",
|
|
5722
|
+
type: "text-match"
|
|
5311
5723
|
});
|
|
5312
|
-
markdownService.registerMarkdownReader("
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5724
|
+
markdownService.registerMarkdownReader("html", (node) => {
|
|
5725
|
+
const match = (node.value || "").match(/^<((?:https?:\/\/|mailto:|tel:|www\.)[^\s"<>[\\\]^`{|}]+)>$/);
|
|
5726
|
+
if (match) {
|
|
5727
|
+
const url = match[1].replaceAll(/[\u200B-\u200D\u2060\uFEFF]/g, "");
|
|
5728
|
+
this.logger.debug("Converting HTML auto-link to LinkHighlightNode:", url);
|
|
5729
|
+
return INodeHelper.createElementNode("linkHighlight", {
|
|
5730
|
+
children: [INodeHelper.createTextNode(url, {})],
|
|
5317
5731
|
direction: "ltr",
|
|
5318
5732
|
format: "",
|
|
5319
5733
|
indent: 0,
|
|
5320
|
-
type: "
|
|
5321
|
-
value: index + 1,
|
|
5734
|
+
type: "linkHighlight",
|
|
5322
5735
|
version: 1
|
|
5323
5736
|
});
|
|
5324
|
-
|
|
5325
|
-
|
|
5737
|
+
}
|
|
5738
|
+
return false;
|
|
5739
|
+
}, 1);
|
|
5740
|
+
markdownService.registerMarkdownReader("link", (node, children) => {
|
|
5741
|
+
const url = node.url || "";
|
|
5742
|
+
if (!children || children.length === 0) return false;
|
|
5743
|
+
if (children.filter((child) => child.type === "text").map((child) => child.text || "").join("") === url) {
|
|
5744
|
+
this.logger.debug("Converting markdown auto-link to LinkHighlightNode:", url);
|
|
5745
|
+
return INodeHelper.createElementNode("linkHighlight", {
|
|
5746
|
+
children: [INodeHelper.createTextNode(url, {})],
|
|
5326
5747
|
direction: "ltr",
|
|
5327
5748
|
format: "",
|
|
5328
5749
|
indent: 0,
|
|
5329
|
-
type: "
|
|
5330
|
-
value: index + 1,
|
|
5750
|
+
type: "linkHighlight",
|
|
5331
5751
|
version: 1
|
|
5332
5752
|
});
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
});
|
|
5753
|
+
}
|
|
5754
|
+
return false;
|
|
5755
|
+
}, 1);
|
|
5756
|
+
this.logger.debug("LinkHighlightPlugin initialized with markdown support");
|
|
5336
5757
|
}
|
|
5337
5758
|
};
|
|
5338
5759
|
//#endregion
|
|
5760
|
+
//#region src/plugins/link-highlight/react/style.ts
|
|
5761
|
+
const styles$4 = createStaticStyles(({ css }) => {
|
|
5762
|
+
return { linkHighlight: css`
|
|
5763
|
+
cursor: unset;
|
|
5764
|
+
|
|
5765
|
+
margin-block: 1em;
|
|
5766
|
+
margin-inline: 0;
|
|
5767
|
+
padding: 2px;
|
|
5768
|
+
border: none;
|
|
5769
|
+
` };
|
|
5770
|
+
});
|
|
5771
|
+
//#endregion
|
|
5772
|
+
//#region src/plugins/link-highlight/react/ReactLinkHighlightPlugin.tsx
|
|
5773
|
+
const ReactLinkHighlightPlugin = ({ className, enableHotkey = true, enablePasteAutoHighlight = true }) => {
|
|
5774
|
+
const [editor] = useLexicalComposerContext();
|
|
5775
|
+
useLayoutEffect(() => {
|
|
5776
|
+
editor.registerPlugin(MarkdownPlugin);
|
|
5777
|
+
editor.registerPlugin(LinkHighlightPlugin, {
|
|
5778
|
+
enableHotkey,
|
|
5779
|
+
enablePasteAutoHighlight,
|
|
5780
|
+
theme: cx(styles$4.linkHighlight, className)
|
|
5781
|
+
});
|
|
5782
|
+
}, [
|
|
5783
|
+
className,
|
|
5784
|
+
cx,
|
|
5785
|
+
enableHotkey,
|
|
5786
|
+
enablePasteAutoHighlight,
|
|
5787
|
+
editor,
|
|
5788
|
+
styles$4.linkHighlight
|
|
5789
|
+
]);
|
|
5790
|
+
return null;
|
|
5791
|
+
};
|
|
5792
|
+
ReactLinkHighlightPlugin.displayName = "ReactLinkHighlightPlugin";
|
|
5793
|
+
//#endregion
|
|
5339
5794
|
//#region src/plugins/list/react/ReactListPlugin.tsx
|
|
5340
5795
|
const ReactListPlugin = ({ enableHotkey = true }) => {
|
|
5341
5796
|
const [editor] = useLexicalComposerContext();
|
|
@@ -6037,233 +6492,6 @@ const ReactMathPlugin = ({ className, renderComp, theme }) => {
|
|
|
6037
6492
|
};
|
|
6038
6493
|
ReactMathPlugin.displayName = "ReactMathPlugin";
|
|
6039
6494
|
//#endregion
|
|
6040
|
-
//#region src/plugins/table/command/index.ts
|
|
6041
|
-
const INSERT_TABLE_COMMAND = createCommand();
|
|
6042
|
-
const SELECT_TABLE_COMMAND = createCommand();
|
|
6043
|
-
function registerTableCommand(editor) {
|
|
6044
|
-
return mergeRegister(editor.registerCommand(INSERT_TABLE_COMMAND, ({ rows, columns, includeHeaders }) => {
|
|
6045
|
-
const selection = $getSelection() || $getPreviousSelection();
|
|
6046
|
-
if (!selection || !$isRangeSelection(selection)) return false;
|
|
6047
|
-
if ($findTableNode(selection.anchor.getNode())) return false;
|
|
6048
|
-
const anchorNode = selection.anchor.getNode();
|
|
6049
|
-
const tableNode = $createTableNodeWithDimensions(Number(rows), Number(columns), includeHeaders);
|
|
6050
|
-
if ($isElementNode(anchorNode) && anchorNode.isEmpty()) anchorNode.replace(tableNode);
|
|
6051
|
-
else $insertNodeToNearestRoot(tableNode);
|
|
6052
|
-
const firstDescendant = tableNode.getFirstDescendant();
|
|
6053
|
-
if ($isTextNode(firstDescendant)) firstDescendant.select();
|
|
6054
|
-
return true;
|
|
6055
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(SELECT_TABLE_COMMAND, ({ table, columnIndex, rowIndex }) => {
|
|
6056
|
-
editor.update(() => {
|
|
6057
|
-
const prevSelection = $getSelection();
|
|
6058
|
-
const tableNode = $getNodeByKey(table);
|
|
6059
|
-
if (!tableNode || !$isTableNode(tableNode)) return;
|
|
6060
|
-
const tableSelection = $isTableSelection(prevSelection) ? prevSelection : $createTableSelection();
|
|
6061
|
-
if (rowIndex !== void 0) {
|
|
6062
|
-
const firstRow = tableNode.getChildren()[rowIndex];
|
|
6063
|
-
if (!firstRow) return;
|
|
6064
|
-
const firstCell = firstRow.getFirstChild();
|
|
6065
|
-
const lastCell = firstRow.getLastChild();
|
|
6066
|
-
if (!firstCell || !lastCell) return;
|
|
6067
|
-
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
6068
|
-
$setSelection(tableSelection);
|
|
6069
|
-
} else if (columnIndex !== void 0) {
|
|
6070
|
-
const firstRow = tableNode.getFirstChild();
|
|
6071
|
-
const lastRow = tableNode.getLastChild();
|
|
6072
|
-
if (!firstRow || !lastRow) return;
|
|
6073
|
-
const firstCell = firstRow.getChildren()[columnIndex];
|
|
6074
|
-
const lastCell = lastRow.getChildren()[columnIndex];
|
|
6075
|
-
if (!firstCell || !lastCell) return;
|
|
6076
|
-
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
6077
|
-
$setSelection(tableSelection);
|
|
6078
|
-
} else {
|
|
6079
|
-
const firstRow = tableNode.getFirstChild();
|
|
6080
|
-
const lastRow = tableNode.getLastChild();
|
|
6081
|
-
if (!firstRow || !lastRow) return;
|
|
6082
|
-
const firstCell = firstRow.getFirstChild();
|
|
6083
|
-
const lastCell = lastRow.getLastChild();
|
|
6084
|
-
if (!firstCell || !lastCell) return;
|
|
6085
|
-
tableSelection.set(table, firstCell.getKey(), lastCell.getKey());
|
|
6086
|
-
$setSelection(tableSelection);
|
|
6087
|
-
}
|
|
6088
|
-
});
|
|
6089
|
-
return false;
|
|
6090
|
-
}, COMMAND_PRIORITY_EDITOR));
|
|
6091
|
-
}
|
|
6092
|
-
//#endregion
|
|
6093
|
-
//#region src/plugins/table/node/index.ts
|
|
6094
|
-
const OriginalCreateDOM = TableNode.prototype.createDOM;
|
|
6095
|
-
function patchTableNode() {
|
|
6096
|
-
Object.defineProperty(TableNode.prototype, "createDOM", {
|
|
6097
|
-
configurable: true,
|
|
6098
|
-
enumerable: false,
|
|
6099
|
-
value: function(config, editor) {
|
|
6100
|
-
const table = OriginalCreateDOM.call(this, config, editor);
|
|
6101
|
-
const controller = document.createElement("div");
|
|
6102
|
-
table.append(controller);
|
|
6103
|
-
return table;
|
|
6104
|
-
},
|
|
6105
|
-
writable: true
|
|
6106
|
-
});
|
|
6107
|
-
}
|
|
6108
|
-
//#endregion
|
|
6109
|
-
//#region src/plugins/table/plugin/index.ts
|
|
6110
|
-
init_helper();
|
|
6111
|
-
init_plugin();
|
|
6112
|
-
const tableCellProcessor = (before, content, after) => {
|
|
6113
|
-
return before + content.replace(/\n+$/, "").replaceAll(/\n+/g, "<br />") + after;
|
|
6114
|
-
};
|
|
6115
|
-
const TablePlugin = class extends KernelPlugin {
|
|
6116
|
-
static {
|
|
6117
|
-
this.pluginName = "TablePlugin";
|
|
6118
|
-
}
|
|
6119
|
-
constructor(kernel, options) {
|
|
6120
|
-
super();
|
|
6121
|
-
this.kernel = kernel;
|
|
6122
|
-
patchTableNode();
|
|
6123
|
-
kernel.registerNodes([
|
|
6124
|
-
TableNode$1,
|
|
6125
|
-
TableRowNode,
|
|
6126
|
-
TableCellNode
|
|
6127
|
-
]);
|
|
6128
|
-
kernel.registerThemes({
|
|
6129
|
-
table: "editor_table",
|
|
6130
|
-
tableCell: "editor_table_cell",
|
|
6131
|
-
tableCellHeader: "editor_table_cell_header",
|
|
6132
|
-
tableCellSelected: "editor_table_cell_selected",
|
|
6133
|
-
tableScrollableWrapper: cx("editor_table_scrollable_wrapper", options?.theme)
|
|
6134
|
-
});
|
|
6135
|
-
}
|
|
6136
|
-
onInit(editor) {
|
|
6137
|
-
setScrollableTablesActive(editor, true);
|
|
6138
|
-
this.register(registerTablePlugin(editor));
|
|
6139
|
-
this.register(registerTableSelectionObserver(editor));
|
|
6140
|
-
this.register(registerTableCellUnmergeTransform(editor));
|
|
6141
|
-
this.register(registerTableCommand(editor));
|
|
6142
|
-
this.registerMarkdown();
|
|
6143
|
-
this.registerLiteXml();
|
|
6144
|
-
}
|
|
6145
|
-
registerLiteXml() {
|
|
6146
|
-
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
6147
|
-
if (!litexmlService) return;
|
|
6148
|
-
litexmlService.registerXMLWriter(TableNode$1.getType(), (node, ctx) => {
|
|
6149
|
-
if ($isTableNode(node)) {
|
|
6150
|
-
const attributes = {};
|
|
6151
|
-
const colWidths = node.getColWidths();
|
|
6152
|
-
if (colWidths && colWidths.length > 0) attributes.colWidths = colWidths.join(",");
|
|
6153
|
-
return ctx.createXmlNode("table", attributes);
|
|
6154
|
-
}
|
|
6155
|
-
return false;
|
|
6156
|
-
});
|
|
6157
|
-
litexmlService.registerXMLWriter(TableRowNode.getType(), (node, ctx) => {
|
|
6158
|
-
if (node instanceof TableRowNode) return ctx.createXmlNode("tr", {});
|
|
6159
|
-
return false;
|
|
6160
|
-
});
|
|
6161
|
-
litexmlService.registerXMLWriter(TableCellNode.getType(), (node, ctx) => {
|
|
6162
|
-
if (node instanceof TableCellNode) {
|
|
6163
|
-
const attributes = {};
|
|
6164
|
-
if (node.getColSpan() > 1) attributes.colSpan = node.getColSpan().toString();
|
|
6165
|
-
if (node.getRowSpan() > 1) attributes.rowSpan = node.getRowSpan().toString();
|
|
6166
|
-
if (node.getBackgroundColor()) attributes.backgroundColor = node.getBackgroundColor();
|
|
6167
|
-
return ctx.createXmlNode("td", attributes);
|
|
6168
|
-
}
|
|
6169
|
-
return false;
|
|
6170
|
-
});
|
|
6171
|
-
litexmlService.registerXMLReader("table", (xmlNode, children) => {
|
|
6172
|
-
const colWidthsAttr = xmlNode.getAttribute("colWidths");
|
|
6173
|
-
const colWidths = colWidthsAttr ? colWidthsAttr.split(",").map((width) => parseInt(width, 10)) : [];
|
|
6174
|
-
let maxTdlen = 1;
|
|
6175
|
-
for (const child of children) if ((child.children?.length || -1) > maxTdlen) maxTdlen = child.children.length;
|
|
6176
|
-
return INodeHelper.createElementNode(TableNode$1.getType(), {
|
|
6177
|
-
children,
|
|
6178
|
-
colWidths: colWidths.length > 0 ? colWidths : new Array(maxTdlen).fill(750 / maxTdlen),
|
|
6179
|
-
direction: null,
|
|
6180
|
-
format: "",
|
|
6181
|
-
indent: 0,
|
|
6182
|
-
version: 1
|
|
6183
|
-
});
|
|
6184
|
-
});
|
|
6185
|
-
litexmlService.registerXMLReader("tr", (_xmlNode, children) => {
|
|
6186
|
-
return INodeHelper.createElementNode(TableRowNode.getType(), {
|
|
6187
|
-
children,
|
|
6188
|
-
direction: "ltr",
|
|
6189
|
-
format: "",
|
|
6190
|
-
height: 33,
|
|
6191
|
-
indent: 0,
|
|
6192
|
-
version: 1
|
|
6193
|
-
});
|
|
6194
|
-
});
|
|
6195
|
-
const tdReader = (xmlNode, children) => {
|
|
6196
|
-
return INodeHelper.createElementNode(TableCellNode.getType(), {
|
|
6197
|
-
backgroundColor: xmlNode.getAttribute("backgroundColor") || null,
|
|
6198
|
-
children,
|
|
6199
|
-
colSpan: xmlNode.getAttribute("colSpan") ? parseInt(xmlNode.getAttribute("colSpan"), 10) : 1,
|
|
6200
|
-
direction: "ltr",
|
|
6201
|
-
format: "",
|
|
6202
|
-
headerState: 0,
|
|
6203
|
-
indent: 0,
|
|
6204
|
-
rowSpan: xmlNode.getAttribute("rowSpan") ? parseInt(xmlNode.getAttribute("rowSpan"), 10) : 1,
|
|
6205
|
-
version: 1
|
|
6206
|
-
});
|
|
6207
|
-
};
|
|
6208
|
-
litexmlService.registerXMLReader("th", tdReader);
|
|
6209
|
-
litexmlService.registerXMLReader("td", tdReader);
|
|
6210
|
-
}
|
|
6211
|
-
registerMarkdown() {
|
|
6212
|
-
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
6213
|
-
if (!markdownService) return;
|
|
6214
|
-
markdownService.registerMarkdownWriter(TableNode$1.getType(), (ctx) => {
|
|
6215
|
-
ctx.wrap("", "\n");
|
|
6216
|
-
});
|
|
6217
|
-
markdownService.registerMarkdownWriter(TableRowNode.getType(), (ctx, node) => {
|
|
6218
|
-
const parent = node.getParent();
|
|
6219
|
-
if (!$isTableNode(parent)) return;
|
|
6220
|
-
if (!node.getPreviousSibling()) ctx.wrap("", `\n${parent.getColWidths()?.map(() => {
|
|
6221
|
-
return "|:--";
|
|
6222
|
-
}).join("")}|\n`);
|
|
6223
|
-
else ctx.wrap("", "\n");
|
|
6224
|
-
});
|
|
6225
|
-
markdownService.registerMarkdownWriter(TableCellNode.getType(), (ctx, node) => {
|
|
6226
|
-
ctx.addProcessor(tableCellProcessor);
|
|
6227
|
-
if (!node.getNextSibling()) ctx.wrap("|", "|");
|
|
6228
|
-
else ctx.wrap("|", "");
|
|
6229
|
-
});
|
|
6230
|
-
markdownService.registerMarkdownReader("table", (node, children) => {
|
|
6231
|
-
const colLen = node.children[0]?.children.length || 1;
|
|
6232
|
-
return INodeHelper.createElementNode("table", {
|
|
6233
|
-
children,
|
|
6234
|
-
colWidths: new Array(colLen).fill(750 / colLen),
|
|
6235
|
-
direction: null,
|
|
6236
|
-
format: "",
|
|
6237
|
-
indent: 0,
|
|
6238
|
-
version: 1
|
|
6239
|
-
});
|
|
6240
|
-
});
|
|
6241
|
-
markdownService.registerMarkdownReader("tableRow", (_node, children) => {
|
|
6242
|
-
return INodeHelper.createElementNode("tablerow", {
|
|
6243
|
-
children,
|
|
6244
|
-
direction: "ltr",
|
|
6245
|
-
format: "",
|
|
6246
|
-
height: 33,
|
|
6247
|
-
indent: 0,
|
|
6248
|
-
version: 1
|
|
6249
|
-
});
|
|
6250
|
-
});
|
|
6251
|
-
markdownService.registerMarkdownReader("tableCell", (_node, children) => {
|
|
6252
|
-
return INodeHelper.createElementNode("tablecell", {
|
|
6253
|
-
backgroundColor: null,
|
|
6254
|
-
children,
|
|
6255
|
-
colSpan: 1,
|
|
6256
|
-
direction: "ltr",
|
|
6257
|
-
format: "",
|
|
6258
|
-
headerState: 0,
|
|
6259
|
-
indent: 0,
|
|
6260
|
-
rowSpan: 1,
|
|
6261
|
-
version: 1
|
|
6262
|
-
});
|
|
6263
|
-
});
|
|
6264
|
-
}
|
|
6265
|
-
};
|
|
6266
|
-
//#endregion
|
|
6267
6495
|
//#region src/plugins/table/utils/index.ts
|
|
6268
6496
|
init_utils();
|
|
6269
6497
|
function $forEachTableCell(grid, cb) {
|
|
@@ -7608,4 +7836,4 @@ function disableHotReload() {
|
|
|
7608
7836
|
}
|
|
7609
7837
|
}
|
|
7610
7838
|
//#endregion
|
|
7611
|
-
export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodemirrorPlugin, CommonPlugin, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, FilePlugin, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HotkeyEnum, HotkeyScopeEnum, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodeService, IUploadService, ImagePlugin, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkPlugin, ListPlugin, LitexmlDataSource, LitexmlPlugin, LitexmlService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MarkdownPlugin, MathPlugin, MentionPlugin, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactCodePlugin, ReactCodeblockPlugin, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactFilePlugin, ReactHRPlugin, ReactImagePlugin, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactListPlugin, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactNodePlugin, ReactPlainText, ReactSlashOption, ReactSlashPlugin, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SlashMenu, SlashPlugin, TablePlugin, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, VirtualBlockPlugin, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
|
|
7839
|
+
export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodemirrorPlugin, CommonPlugin, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, FilePlugin, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HeadlessEditor, HotkeyEnum, HotkeyScopeEnum, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodeService, IUploadService, ImagePlugin, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkPlugin, ListPlugin, LitexmlDataSource, LitexmlPlugin, LitexmlService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MarkdownPlugin, MathPlugin, MentionPlugin, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactCodePlugin, ReactCodeblockPlugin, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactFilePlugin, ReactHRPlugin, ReactImagePlugin, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactListPlugin, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactNodePlugin, ReactPlainText, ReactSlashOption, ReactSlashPlugin, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SlashMenu, SlashPlugin, TablePlugin, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, VirtualBlockPlugin, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
|