@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/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-Tlcvrhqp.js";
2
- import { A as idToChar, B as MARKDOWN_READER_LEVEL_NORMAL, C as ReactEditorContent, D as $cloneNode, E as LitexmlService, F as MarkdownPlugin, G as INSERT_MARKDOWN_COMMAND, H as INodePlugin, I as detectCodeLanguage, J as ReactEditor, K as isPunctuationChar, L as detectLanguage, M as INSERT_QUOTE_COMMAND, N as ReactMarkdownPlugin, O as $parseSerializedNodeImpl, P as useTranslation, R as IMarkdownShortCutService, S as ReactPlainText, T as ILitexmlService, U as INodeService, V as MARKDOWN_WRITER_LEVEL_MAX, W as GET_MARKDOWN_SELECTION_COMMAND, X as LexicalErrorBoundary, Y as useLexicalComposerContext, _ as bundledLanguagesInfo, a as ReactMentionPlugin, b as INSERT_CODEINLINE_COMMAND, c as INSERT_CHECK_LIST_COMMAND, d as registerLinkHighlightCommand, f as extractUrlFromText, g as useLexicalNodeSelection, i as SlashPlugin, j as INSERT_HEADING_COMMAND, k as charToId, l as registerCheckList, m as sanitizeUrl, n as ReactSlashOption, o as MentionPlugin, p as getSelectedNode, q as useLexicalEditor, r as SlashMenu, s as INSERT_MENTION_COMMAND, t as ReactSlashPlugin, u as INSERT_LINK_HIGHLIGHT_COMMAND, v as CodeblockPlugin, w as CommonPlugin, x as registerCodeInlineCommand, y as UPDATE_CODEBLOCK_LANG, z as MARKDOWN_READER_LEVEL_HIGH } from "./ReactSlashPlugin-BZvB0g1k.js";
3
- import { S as PlaceholderNode, _ as $isFileNode, a as styles$16, b as DiffNode, c as ImageNode, d as BlockImageNode, f as $createHorizontalRuleNode, g as $createFileNode, h as styles$15, i as imageBroken, l as $createBlockImageNode, m as HorizontalRuleNode, n as styles$18, o as $createImageNode, p as $isHorizontalRuleNode, r as styles$17, s as $isImageNode, t as styles$19, u as $isBlockImageNode, v as FileNode, x as PlaceholderBlockNode, y as $createDiffNode } from "./style-B7Vfm0cE.js";
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/inode/react/index.tsx
45
- const ReactNodePlugin = () => {
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
- init_registerHotkey();
58
- /**
59
- * Shared URL validation utilities
60
- */
61
- const URL_REGEX = /* @__PURE__ */ new RegExp(/^(?:(?:https?|ftp):\/\/)?(?:www\.)?[\dA-Za-z][\w#%+./:=?@~-]*[\w#%+/=@~-]$/);
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
- * Checks if text is a pure URL (single URL without other text)
80
- * @param text - The text to check
81
- * @returns true if text is a pure URL, false otherwise
82
- */
83
- function isPureUrl(text) {
84
- if (!text || typeof text !== "string") return false;
85
- const trimmed = text.trim();
86
- if (trimmed.includes("\n")) return false;
87
- return isValidUrl(trimmed);
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
- //#endregion
90
- //#region src/plugins/auto-complete/plugin/index.ts
91
- init_helper();
92
- init_plugin();
93
- const AutoCompletePlugin = class extends KernelPlugin {
94
- static {
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
- constructor(kernel, config) {
98
- super();
99
- this.kernel = kernel;
100
- this.config = config;
101
- this.logger = createDebugLogger("plugin", "auto-complete");
102
- this.lastCursorPosition = null;
103
- this.cursorStableTimer = null;
104
- this.abortController = null;
105
- this.placeholderNodes = [];
106
- this.currentSuggestion = null;
107
- this.markdownService = null;
108
- this.delay = config?.delay ?? 1e3;
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
- onInit(editor) {
113
- this.markdownService = this.kernel.requireService(IMarkdownShortCutService);
114
- if (!this.markdownService) {
115
- this.logger.error("❌ MarkdownShortCutService is required for AutoCompletePlugin");
116
- return;
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
- this.register(editor.registerUpdateListener(({ editorState }) => {
119
- editorState.read(() => {
120
- if (editor.isComposing()) return;
121
- const selection = $getSelection();
122
- if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
123
- this.clearTimer();
124
- this.clearPlaceholderNodes(editor);
125
- return;
126
- }
127
- const currentPosition = {
128
- key: selection.anchor.key,
129
- offset: selection.anchor.offset,
130
- type: selection.anchor.type
131
- };
132
- if (this.hasPositionChanged(currentPosition)) {
133
- this.clearTimer();
134
- this.clearPlaceholderNodes(editor);
135
- this.abortController = new AbortController();
136
- this.cursorStableTimer = window.setTimeout(() => {
137
- this.handleCursorStable(editor, currentPosition);
138
- }, this.delay);
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
- }, COMMAND_PRIORITY_HIGH));
150
- this.register(editor.registerCommand(KEY_ESCAPE_COMMAND, (event) => {
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
- handleCursorStable(editor, position) {
175
- editor.getEditorState().read(() => {
176
- if (editor.isComposing()) return;
177
- if (!this.abortController || this.abortController.signal.aborted) return;
178
- const selection = $getSelection();
179
- if (!$isRangeSelection(selection) || !selection.isCollapsed()) return;
180
- const currentPosition = {
181
- key: selection.anchor.key,
182
- offset: selection.anchor.offset,
183
- type: selection.anchor.type
184
- };
185
- if (!this.isSamePosition(position, currentPosition)) return;
186
- if (this.lastCursorPosition && this.isSamePosition(position, this.lastCursorPosition)) return;
187
- this.lastCursorPosition = currentPosition;
188
- const anchorNode = selection.anchor.getNode();
189
- let selectionType = "unknown";
190
- const textRet = this.getTextBeforeCursor(selection);
191
- selectionType = anchorNode.getType();
192
- if (this.config?.onAutoComplete) this.config.onAutoComplete({
193
- abortSignal: this.abortController.signal,
194
- afterText: textRet.textAfter,
195
- editor: this.kernel,
196
- input: textRet.textBefore,
197
- selectionType
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
- const newPosition = {
212
- key: currentSelection.anchor.key,
213
- offset: currentSelection.anchor.offset,
214
- type: currentSelection.anchor.type
215
- };
216
- if (!this.isSamePosition(currentPosition, newPosition)) {
217
- this.clearPlaceholderNodes(editor);
218
- return;
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
- getTextBeforeCursor(selection) {
235
- const ret = {
236
- textAfter: "",
237
- textBefore: ""
238
- };
239
- const anchorNode = selection.anchor.getNode();
240
- const anchorOffset = selection.anchor.offset;
241
- let paragraphNode = anchorNode;
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 (!paragraphNode) return ret;
248
- this.logger.debug("🔍 Paragraph Node Type:", paragraphNode, anchorNode);
249
- let founded = false;
250
- const collectTextBeforeCursor = (node, targetNode, targetOffset) => {
251
- if (node === targetNode) {
252
- founded = true;
253
- if (node.getTextContent) {
254
- const nodeText = node.getTextContent();
255
- return {
256
- text: nodeText.slice(0, targetOffset),
257
- textAfter: nodeText.slice(targetOffset)
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
- let collectedText = "";
266
- let collectedTextAfter = "";
267
- if (node.getTextContent && node.getType() === "text") if (founded) collectedTextAfter += node.getTextContent();
268
- else collectedText += node.getTextContent();
269
- if (node.getChildren) {
270
- const children = node.getChildren();
271
- for (const child of children) {
272
- const result = collectTextBeforeCursor(child, targetNode, targetOffset);
273
- collectedTextAfter += result.textAfter;
274
- collectedText += result.text;
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
- return {
278
- text: collectedText,
279
- textAfter: collectedTextAfter
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
- if (!this.markdownService) {
296
- this.logger.warn("⚠️ No valid markdown service for placeholder");
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
- const nodes = this.markdownService.parseMarkdownToLexical(suggestion);
300
- if (nodes.children[0]) {
301
- const firstChild = nodes.children[0];
302
- firstChild.children = [{
303
- children: firstChild.children,
304
- type: "PlaceholderInline"
305
- }];
306
- }
307
- for (let i = 1; i < nodes.children.length; i++) {
308
- const child = nodes.children[i];
309
- nodes.children[i] = {
310
- children: [child],
311
- type: "PlaceholderBlock"
312
- };
313
- }
314
- const markerNode = INodeHelper.createTextNode("​");
315
- nodes.children.push({
316
- children: [markerNode],
317
- name: "",
318
- type: "PlaceholderInline"
319
- });
320
- const saveSel = selection.clone();
321
- this.markdownService.insertIRootNode(editor, nodes, selection);
322
- $setSelection(saveSel);
323
- });
324
- }
325
- clearPlaceholderNodes(editor) {
326
- editor.update(() => {
327
- editor.getEditorState()._nodeMap.forEach((node) => {
328
- const selection = $getSelection();
329
- const clonedSelection = selection ? selection.clone() : null;
330
- if (node.isAttached() && ["PlaceholderBlock", "PlaceholderInline"].includes(node.getType())) {
331
- if (node.getType() === "PlaceholderInline" && node.getTextContent().includes("​") && node.getPreviousSibling() === null) {
332
- while (node.getNextSibling()) {
333
- const next = node.getNextSibling();
334
- if (next) $insertNodes([next]);
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
- node.remove();
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
- applySuggestion(editor) {
346
- if (!this.currentSuggestion) return;
347
- const markdown = this.currentSuggestion;
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
- const selection = $getSelection();
350
- if (!$isRangeSelection(selection) || !selection.isCollapsed() || !this.markdownService) return;
351
- editor.getEditorState()._nodeMap.forEach((node) => {
352
- if (node.isAttached() && ["PlaceholderBlock", "PlaceholderInline"].includes(node.getType())) node.remove();
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
- isSamePosition(pos1, pos2) {
361
- return pos1.key === pos2.key && pos1.offset === pos2.offset && pos1.type === pos2.type;
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/auto-complete/react/ReactAutoCompletePlugin.tsx
381
- const ReactAutoCompletePlugin = ({ onAutoComplete, delay }) => {
465
+ //#region src/plugins/inode/react/index.tsx
466
+ const ReactNodePlugin = () => {
382
467
  const [editor] = useLexicalComposerContext();
383
468
  useLayoutEffect(() => {
384
- editor.registerPlugin(AutoCompletePlugin, {
385
- delay,
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
- ReactAutoCompletePlugin.displayName = "ReactAutoCompletePlugin";
473
+ ReactNodePlugin.displayName = "ReactNodePlugin";
396
474
  //#endregion
397
- //#region src/plugins/litexml/command/index.ts
475
+ //#region src/plugins/litexml/data-source/litexml-data-source.ts
398
476
  init_debug();
399
- const logger$6 = createDebugLogger("plugin", "litexml");
400
- function toArrayXml(litexml) {
401
- return Array.isArray(litexml) ? litexml : [litexml];
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
- newNode: null,
414
- oldNode: null
481
+ attributes: attributes || {},
482
+ children: [],
483
+ tagName,
484
+ textContent
415
485
  };
416
486
  }
417
- }
418
- function handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor) {
419
- const oldBlock = $closest(oldNode, (node) => node.isInline() === false);
420
- if (!oldBlock) throw new Error("Old block node not found for diffing.");
421
- if ($closest(oldNode, (node) => node.getType() === DiffNode.getType())) {
422
- oldNode.replace(newNode, false);
423
- return;
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
- if (oldNode === oldBlock) {
426
- const diffNode = $createDiffNode("modify");
427
- diffNode.append($cloneNode(oldBlock, editor), newNode);
428
- oldNode.replace(diffNode, false);
429
- } else {
430
- if (!modifyBlockNodes.has(oldBlock.getKey())) {
431
- modifyBlockNodes.add(oldBlock.getKey());
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
- function finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor) {
440
- for (const blockNodeKey of modifyBlockNodes) {
441
- const blockNode = $getNodeByKey(blockNodeKey);
442
- const diffNode = diffNodeMap.get(blockNodeKey);
443
- if (diffNode && blockNode) if (blockNode.getType() === "listitem" && $isElementNode(blockNode)) {
444
- const newDiffNode = $createDiffNode("listItemModify");
445
- const firstChild = diffNode.getFirstChild();
446
- if (firstChild && $isElementNode(firstChild)) newDiffNode.append(firstChild);
447
- const children = blockNode.getChildren();
448
- const p = $createParagraphNode();
449
- children.forEach((child) => {
450
- child.remove();
451
- p.append(child);
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
- newDiffNode.append(p);
454
- blockNode.append(newDiffNode);
455
- continue;
456
- } else {
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
- * Wrap a block-level change with a `modify` diff: clone the old block, run the
464
- * provided changeFn (which should mutate nodes inside the block), then clone
465
- * the new block and replace it with the diff node. Useful for inline->block
466
- * transitions where we want to show a modify diff.
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
- resultPayload.forEach((item) => {
506
- const { action } = item;
507
- switch (action) {
508
- case "modify": {
509
- const { litexml } = item;
510
- handleModify(editor, dataSource, toArrayXml(litexml), true);
511
- break;
512
- }
513
- case "remove": {
514
- const { id } = item;
515
- handleRemove(editor, charToId(id), true);
516
- break;
517
- }
518
- case "insert":
519
- handleInsert(editor, {
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$6.error(" Error processing LITEXML_MODIFY_COMMAND:", error);
530
- return false;
561
+ logger$5.error("Failed to export to XML:", error);
562
+ throw error;
531
563
  }
532
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_APPLY_COMMAND, (payload) => {
533
- const { litexml, delay } = payload;
534
- handleModify(editor, dataSource, toArrayXml(litexml), delay);
535
- return false;
536
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_REMOVE_COMMAND, (payload) => {
537
- const { id, delay } = payload;
538
- handleRemove(editor, charToId(id), delay);
539
- return false;
540
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_INSERT_COMMAND, (payload) => {
541
- handleInsert(editor, payload, dataSource);
542
- return false;
543
- }, COMMAND_PRIORITY_EDITOR));
544
- }
545
- function handleModify(editor, dataSource, arrayXml, delay) {
546
- if (delay) editor.update(() => {
547
- const modifyBlockNodes = /* @__PURE__ */ new Set();
548
- const diffNodeMap = /* @__PURE__ */ new Map();
549
- arrayXml.forEach((xml) => {
550
- dataSource.readLiteXMLToInode(xml).root.children.forEach((child) => {
551
- try {
552
- const { oldNode, newNode } = tryParseChild(child, editor);
553
- if (oldNode && newNode) handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor);
554
- else logger$6.warn(`⚠️ Node with key ${child.id} not found for diffing.`);
555
- } catch (error) {
556
- logger$6.error("❌ Error replacing node:", error);
557
- }
558
- });
559
- });
560
- finalizeModifyBlocks(modifyBlockNodes, diffNodeMap, editor);
561
- });
562
- else editor.update(() => {
563
- arrayXml.forEach((xml) => {
564
- const inode = dataSource.readLiteXMLToInode(xml);
565
- let prevNode = null;
566
- inode.root.children.forEach((child) => {
567
- try {
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
- } else prevNode = prevNode.insertAfter(newNode);
578
- else $insertNodes([newNode]);
579
- } catch (error) {
580
- logger$6.error("❌ Error replacing node:", error);
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
- if (node.isInline() === false) {
595
- const originDiffNode = $closest(node, (node) => node.getType() === DiffNode.getType());
596
- if (originDiffNode) {
597
- switch (originDiffNode.diffType) {
598
- case "add":
599
- originDiffNode.remove();
600
- return;
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
- if ($isListItemNode(node)) {
619
- const diffNode = $createDiffNode("listItemRemove");
620
- node.getChildren().forEach((child) => {
621
- diffNode.append($cloneNode(child, editor));
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
- node.clear();
624
- node.append(diffNode);
625
- } else {
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
- } else {
631
- const oldBlock = $closest(node, (node) => node.isInline() === false);
632
- if (!oldBlock) throw new Error("Old block node not found for removal.");
633
- if ($closest(node, (node) => node.getType() === DiffNode.getType())) {
634
- node.remove();
635
- return;
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
- wrapBlockModify(oldBlock, editor, () => {
638
- node.remove();
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
- function handleInsert(editor, payload, dataSource) {
644
- const { litexml, delay } = payload;
645
- const isBefore = "beforeId" in payload;
646
- const inode = dataSource.readLiteXMLToInode(litexml);
647
- editor.update(() => {
648
- try {
649
- let referenceNode = null;
650
- if (isBefore) if (payload.beforeId === "root") referenceNode = $getRoot().getFirstChild();
651
- else referenceNode = $getNodeByKey(charToId(payload.beforeId));
652
- else if (payload.afterId === "root") referenceNode = $getRoot().getLastChild();
653
- else referenceNode = $getNodeByKey(charToId(payload.afterId));
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
- if (isBefore) if (referenceNode.isInline() === false) {
666
- const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
667
- if (originDiffNode) referenceNode = originDiffNode;
668
- newNodes.map((node) => {
669
- const diffNode = $createDiffNode("add");
670
- diffNode.append(node);
671
- return diffNode;
672
- }).reverse().forEach((diffNode) => {
673
- if (referenceNode) referenceNode = referenceNode.insertBefore(diffNode);
674
- });
675
- } else {
676
- const refBlock = $closest(referenceNode, (node) => node.isInline() === false);
677
- if (!refBlock) throw new Error("Reference block node not found for insertion.");
678
- if ($closest(referenceNode, (node) => node.getType() === DiffNode.getType())) newNodes.forEach((node) => {
679
- if (referenceNode) referenceNode = referenceNode.insertBefore(node);
680
- });
681
- else wrapBlockModify(refBlock, editor, () => {
682
- newNodes.forEach((node) => {
683
- if (referenceNode) referenceNode = referenceNode.insertBefore(node);
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
- else if (referenceNode.isInline() === false) {
688
- const originDiffNode = $closest(referenceNode, (node) => node.getType() === DiffNode.getType());
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
- diffNode.append(child);
721
+ this.nodesToXML(child, childLines, indent + 1);
695
722
  });
696
- node.append(diffNode);
697
- referenceNode = referenceNode.insertAfter(node);
698
- } else {
699
- const diffNode = $createDiffNode("add");
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
- if (node.diffType === "add") {
744
- if (action === 1) {
745
- const children = node.getChildren();
746
- node.replace(children[0], false).selectEnd();
747
- } else if (action === 0) node.remove();
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
- if (node.diffType === "listItemModify") {
750
- const children = node.getChildren();
751
- if (action === 1) {
752
- const lastChild = children[1];
753
- if (!$isElementNode(lastChild)) throw new Error("Expected element node as child of DiffNode");
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
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
- function registerLiteXMLDiffCommand(editor) {
790
- return mergeRegister(editor.registerCommand(LITEXML_DIFFNODE_COMMAND, (payload) => {
791
- const { action, nodeKey } = payload;
792
- const node = editor.getEditorState().read(() => {
793
- return $getNodeByKey(nodeKey);
794
- });
795
- if (!node) return false;
796
- editor.update(() => {
797
- doAction(node, action);
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/data-source/litexml-data-source.ts
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
- * LitexmlDataSource - Handles conversion between Lexical editor state and XML format
831
- * Provides read (parse XML to Lexical) and write (export Lexical to XML) capabilities
787
+ * LitexmlPlugin - A plugin that provides XML-based data source support
788
+ * Allows converting between Lexical editor state and XML format
832
789
  */
833
- var LitexmlDataSource = class extends DataSource {
834
- constructor(dataType = "litexml", getService, service) {
835
- super(dataType);
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
- readLiteXMLToInode(litexml) {
842
- if (typeof litexml !== "string") throw new Error("Invalid data type. Expected string, got " + typeof litexml);
843
- const xml = this.parseXMLString(litexml);
844
- const inode = this.xmlToLexical(xml);
845
- this.getService?.(INodeService)?.processNodeTree(inode);
846
- logger$5.debug("Parsed XML to Lexical State:", inode);
847
- return inode;
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
- * Parse XML string and set it to the editor
851
- * @param editor - The Lexical editor instance
852
- * @param data - XML string to parse
853
- */
854
- read(editor, data) {
855
- try {
856
- const inode = this.readLiteXMLToInode(data);
857
- const newState = editor.parseEditorState({ root: INodeHelper.createRootNode() }, (state) => {
858
- try {
859
- const root = $parseSerializedNodeImpl(inode.root, editor, true, state);
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
- editor.setEditorState(newState);
866
- } catch (error) {
867
- logger$5.error("Failed to parse XML:", error);
868
- throw error;
869
- }
826
+ }));
827
+ this.registerLiteXml();
828
+ this.registerMarkdown();
870
829
  }
871
- /**
872
- * Export editor content to XML format
873
- * @param editor - The Lexical editor instance
874
- * @param options - Write options (e.g., selection flag)
875
- * @returns XML string representation of the editor content
876
- */
877
- write(editor, options) {
878
- try {
879
- if (options?.selection) return editor.getEditorState().read(() => {
880
- const selection = $getSelection();
881
- if (!selection) return null;
882
- if ($isRangeSelection(selection)) {
883
- const selectedNodes = selection.getNodes();
884
- const rootNode = INodeHelper.createRootNode();
885
- if (selectedNodes.some((node) => node.isInline())) {
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
- return null;
896
- });
897
- return editor.getEditorState().read(() => {
898
- const rootNode = $getRoot();
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
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$13 = createStaticStyles(({ css, cssVar }) => ({
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$13.toolbarDark : styles$13.toolbarLight,
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$13.reject,
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$13.accept,
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$12 = createStaticStyles(({ css, cssVar }) => css`
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$12
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
- const markdownService = this.kernel.requireService(IMarkdownShortCutService);
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
- markdownService.registerMarkdownShortCut({
4957
- regExp: /<((?:https?:\/\/|mailto:|tel:|www\.)[^\s"<>[\\\]^`{|}]+)>\s?$/,
4958
- replace: (textNode, match) => {
4959
- const [, url] = match;
4960
- if (!url || !isValidUrl(url)) return;
4961
- this.logger.debug("Converting markdown auto-link to LinkHighlightNode:", url);
4962
- const linkHighlightNode = $createLinkHighlightNode();
4963
- const textNodeContent = $createTextNode(url);
4964
- linkHighlightNode.append(textNodeContent);
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
- trigger: ">",
4968
- type: "text-match"
4969
- });
4970
- markdownService.registerMarkdownReader("html", (node) => {
4971
- const match = (node.value || "").match(/^<((?:https?:\/\/|mailto:|tel:|www\.)[^\s"<>[\\\]^`{|}]+)>$/);
4972
- if (match) {
4973
- const url = match[1].replaceAll(/[\u200B-\u200D\u2060\uFEFF]/g, "");
4974
- this.logger.debug("Converting HTML auto-link to LinkHighlightNode:", url);
4975
- return INodeHelper.createElementNode("linkHighlight", {
4976
- children: [INodeHelper.createTextNode(url, {})],
4977
- direction: "ltr",
4978
- format: "",
4979
- indent: 0,
4980
- type: "linkHighlight",
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
- return false;
5001
- }, 1);
5002
- this.logger.debug("LinkHighlightPlugin initialized with markdown support");
5003
- }
5004
- };
5005
- //#endregion
5006
- //#region src/plugins/link-highlight/react/style.ts
5007
- const styles$4 = createStaticStyles(({ css }) => {
5008
- return { linkHighlight: css`
5009
- cursor: unset;
5010
-
5011
- margin-block: 1em;
5012
- margin-inline: 0;
5013
- padding: 2px;
5014
- border: none;
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-highlight/react/ReactLinkHighlightPlugin.tsx
5019
- const ReactLinkHighlightPlugin = ({ className, enableHotkey = true, enablePasteAutoHighlight = true }) => {
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(LinkHighlightPlugin, {
5604
+ editor.registerPlugin(LinkPlugin, {
5605
+ attributes,
5024
5606
  enableHotkey,
5025
- enablePasteAutoHighlight,
5026
- theme: cx(styles$4.linkHighlight, className)
5607
+ theme: theme || styles$5,
5608
+ validateUrl
5027
5609
  });
5028
5610
  }, [
5029
- className,
5030
- cx,
5611
+ attributes,
5031
5612
  enableHotkey,
5032
- enablePasteAutoHighlight,
5033
- editor,
5034
- styles$4.linkHighlight
5613
+ styles$5,
5614
+ theme,
5615
+ validateUrl
5035
5616
  ]);
5036
- return null;
5037
- };
5038
- ReactLinkHighlightPlugin.displayName = "ReactLinkHighlightPlugin";
5039
- //#endregion
5040
- //#region src/plugins/list/utils/index.ts
5041
- const LIST_INDENT_SIZE = 4;
5042
- function getIndent(whitespaces) {
5043
- const tabs = whitespaces.match(/\t/g);
5044
- const spaces = whitespaces.match(/ /g);
5045
- let indent = 0;
5046
- if (tabs) indent += tabs.length;
5047
- if (spaces) indent += Math.floor(spaces.length / LIST_INDENT_SIZE);
5048
- return indent;
5049
- }
5050
- const listReplace = (listType) => {
5051
- return (parentNode, children, match, isImport) => {
5052
- const previousNode = parentNode.getPreviousSibling();
5053
- const nextNode = parentNode.getNextSibling();
5054
- const listItem = $createListItemNode(listType === "check" ? match[3] === "x" : void 0);
5055
- if ($isListNode(nextNode) && nextNode.getListType() === listType) {
5056
- const firstChild = nextNode.getFirstChild();
5057
- if (firstChild !== null) firstChild.insertBefore(listItem);
5058
- else nextNode.append(listItem);
5059
- parentNode.remove();
5060
- } else if ($isListNode(previousNode) && previousNode.getListType() === listType) {
5061
- previousNode.append(listItem);
5062
- parentNode.remove();
5063
- } else {
5064
- const list = $createListNode(listType, listType === "number" ? Number(match[2]) : void 0);
5065
- list.append(listItem);
5066
- parentNode.replace(list);
5067
- }
5068
- listItem.append(...children);
5069
- if (!isImport) listItem.select(0, 0);
5070
- const indent = getIndent(match[1]);
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
- event.stopImmediatePropagation();
5151
- event.preventDefault();
5152
- return true;
5153
- }, COMMAND_PRIORITY_LOW));
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/list/plugin/index.ts
5661
+ //#region src/plugins/link-highlight/plugin/index.ts
5157
5662
  init_helper();
5158
5663
  init_plugin();
5159
- const ORDERED_LIST_REGEX = /^(\s*)(\d+)\.\s/;
5160
- const UNORDERED_LIST_REGEX = /^(\s*)[*+-]\s/;
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 = "ListPlugin";
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
- kernel.registerNodes([ListNode, ListItemNode]);
5171
- kernel.registerThemes({ list: {
5172
- checklist: "editor_listItemCheck",
5173
- listitem: "editor_listItem",
5174
- listitemChecked: "editor_listItemChecked",
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(registerList(editor));
5190
- this.register(registerCheckList(editor));
5191
- this.register(registerListStrictIndentTransform(editor));
5192
- this.register(registerListCommands(editor, this.kernel, { enableHotkey: this.config?.enableHotkey }));
5193
- this.registerMarkdown();
5194
- this.registerLiteXml();
5195
- }
5196
- registerLiteXml() {
5197
- const litexmlService = this.kernel.requireService(ILitexmlService);
5198
- if (!litexmlService) return;
5199
- litexmlService.registerXMLWriter(ListNode.getType(), (node, ctx) => {
5200
- if ($isListNode(node)) {
5201
- const tagName = node.getListType() === "number" ? "ol" : "ul";
5202
- const attributes = {};
5203
- if (node.getListType() === "number" && node.getStart() !== 1) attributes.start = node.getStart().toString();
5204
- return ctx.createXmlNode(tagName, attributes);
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.registerMarkdownShortCut({
5251
- regExp: UNORDERED_LIST_REGEX,
5252
- replace: listReplace("bullet"),
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: CHECK_LIST_REGEX,
5262
- replace: listReplace("check"),
5263
- type: "element"
5264
- });
5265
- markdownService.registerMarkdownWriter(ListNode.getType(), (ctx, node) => {
5266
- if ($isListNode(node)) ctx.wrap("", "\n");
5267
- });
5268
- const getLevel = (node) => {
5269
- if (!node) return 0;
5270
- if ($isRootNode(node.getParent())) return 0;
5271
- const parent = node.getParent();
5272
- if (!parent) return 0;
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("listItem", (node, children, index) => {
5313
- return children.map((v) => {
5314
- if (v.type === "paragraph") return INodeHelper.createElementNode("listitem", {
5315
- checked: typeof node.checked === "boolean" ? node.checked : void 0,
5316
- children: v.children,
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: "listitem",
5321
- value: index + 1,
5734
+ type: "linkHighlight",
5322
5735
  version: 1
5323
5736
  });
5324
- else if (v.type === "list") return INodeHelper.createElementNode("listitem", {
5325
- children: [v],
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: "listitem",
5330
- value: index + 1,
5750
+ type: "linkHighlight",
5331
5751
  version: 1
5332
5752
  });
5333
- return v;
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 };